如何用PHP项目实现考勤报表?

wen java案例 3

本文目录导读:

如何用PHP项目实现考勤报表?

  1. 整体架构设计
  2. 核心逻辑实现(PHP代码)
  3. 数据库查询优化(SQL)
  4. 前端展示与导出
  5. 实际项目中的难点与建议
  6. 总结实现流程

这是一个相对复杂但很常见的需求,要用PHP实现一个考勤报表系统,核心在于数据采集规则计算数据展示三个环节。

以下是一个从设计思路到具体实现的分步指南,包含核心代码片段。

整体架构设计

  1. 数据源:通常是打卡机(硬件)生成的文本/log文件,或者通过API获取,最常见的是存入MySQL数据库。
  2. 核心表设计
    • 考勤记录表(attendance_log):存储原始打卡数据。
      • id, user_id, clock_in(上班打卡), clock_out(下班打卡), date(日期)
    • 用户/员工表(users)id, name, department, work_start_time(上班时间), work_end_time(下班时间)
    • 考勤汇总表(attendance_summary):存储计算后的结果(可选,用于报表加速)。
      • id, user_id, date, status(正常/迟到/早退/旷工), work_hours, late_minutes, early_minutes
    • 假期/异常表(attendance_exception):请假、出差、外勤记录。

核心逻辑实现(PHP代码)

数据导入与预处理

假设数据来自打卡机文件或表单提交,你需要将其插入attendance_log

<?php
// 假设接收到来自打卡机的数据
function importAttendanceData($userId, $timestamp) {
    $date = date('Y-m-d', $timestamp);
    $time = date('H:i:s', $timestamp);
    // 1. 判断是上班打卡还是下班打卡(根据时间或设备号)
    // 这里简化处理:每天最早的一条是上班,最晚的一条是下班
    $existing = getTodayLog($userId, $date);
    if (empty($existing)) {
        // 第一条记录视为上班
        saveLog($userId, $date, $time, null);
    } else {
        // 更新下班时间为当前时间(如果当前时间更晚)
        if ($time > $existing['clock_in']) {
            updateLogClockOut($userId, $date, $time);
        }
    }
}
// 伪代码: 连接数据库
function saveLog($userId, $date, $clockIn, $clockOut) {
    $pdo = new PDO('mysql:host=localhost;dbname=attendance', 'root', '');
    $sql = "INSERT INTO attendance_log (user_id, date, clock_in, clock_out) VALUES (?, ?, ?, ?)";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$userId, $date, $clockIn, $clockOut]);
}
?>

考勤规则计算引擎

这是最复杂的一步,你需要根据公司的考勤制度写逻辑。

<?php
function calculateAttendance($userId, $date) {
    // 1. 获取员工信息及当天的打卡记录
    $user = getUser($userId);
    $log = getAttendanceLog($userId, $date);
    $holiday = isHoliday($date); // 检查是否是周末或法定假日
    $result = [];
    $result['date'] = $date;
    $result['user_name'] = $user['name'];
    // 2. 判断是否请假
    if (hasLeave($userId, $date)) {
        $result['status'] = '请假';
        $result['work_hours'] = 0;
        return $result;
    }
    // 3. 判断是否无打卡(旷工或休息)
    if (empty($log['clock_in']) || empty($log['clock_out'])) {
        if ($holiday) {
            $result['status'] = '休息';
        } else {
            $result['status'] = '缺卡/旷工';
        }
        $result['work_hours'] = 0;
        return $result;
    }
    // 4. 计算迟到 (假设上班时间为 09:00)
    $clockInTime = strtotime($log['clock_in']);
    $workStartTime = strtotime($user['work_start_time']); // 09:00:00
    $lateMinutes = 0;
    if ($clockInTime > $workStartTime) {
        // 可以设置宽限期,比如9:05前不算迟到
        $gracePeriod = 10 * 60; // 10分钟
        if ($clockInTime - $workStartTime > $gracePeriod) {
            $lateMinutes = round(($clockInTime - $workStartTime) / 60);
        }
    }
    // 5. 计算早退 (假设下班时间为 18:00)
    $clockOutTime = strtotime($log['clock_out']);
    $workEndTime = strtotime($user['work_end_time']); // 18:00:00
    $earlyMinutes = 0;
    if ($clockOutTime < $workEndTime) {
        $earlyMinutes = round(($workEndTime - $clockOutTime) / 60);
    }
    // 6. 计算实际工作时长(扣除午休时间,比如12:00-13:00)
    $workHours = ($clockOutTime - $clockInTime) / 3600; // 小时
    // 扣除午休1小时(如果跨越了中午)
    if (strtotime($log['clock_in']) < strtotime('12:00:00') && strtotime($log['clock_out']) > strtotime('13:00:00')) {
        $workHours -= 1;
    }
    // 7. 判定最终状态
    if ($lateMinutes > 0 && $earlyMinutes > 0) {
        $result['status'] = '迟到且早退';
    } elseif ($lateMinutes > 0) {
        $result['status'] = '迟到';
    } elseif ($earlyMinutes > 0) {
        $result['status'] = '早退';
    } elseif ($workHours >= 8) { // 假设标准8小时
        $result['status'] = '正常';
    } else {
        $result['status'] = '工时不足';
    }
    $result['clock_in'] = $log['clock_in'];
    $result['clock_out'] = $log['clock_out'];
    $result['late_minutes'] = $lateMinutes;
    $result['early_minutes'] = $earlyMinutes;
    $result['work_hours'] = round($workHours, 2);
    // 8. 存储计算结果到汇总表(可选)
    saveSummary($userId, $date, $result);
    return $result;
}
?>

报表生成与展示

思路:按月查询,对每天的数据应用上述规则,然后汇总。

<?php
// 生成月度考勤报表
function generateMonthlyReport($userId, $yearMonth) {
    $startDate = $yearMonth . '-01';
    $endDate = date('Y-m-t', strtotime($startDate));
    $report = [];
    $totalLate = 0;
    $totalEarly = 0;
    $totalWorkHours = 0;
    $abnormalDays = 0;
    $currentDate = $startDate;
    while ($currentDate <= $endDate) {
        // 计算每一天的考勤
        $dailyResult = calculateAttendance($userId, $currentDate);
        // 汇总数据
        if ($dailyResult['status'] !== '休息' && $dailyResult['status'] !== '请假') {
            $totalLate += $dailyResult['late_minutes'];
            $totalEarly += $dailyResult['early_minutes'];
            $totalWorkHours += $dailyResult['work_hours'];
        }
        if ($dailyResult['status'] !== '正常' && $dailyResult['status'] !== '休息') {
            $abnormalDays++;
        }
        $report['detail'][] = $dailyResult;
        $currentDate = date('Y-m-d', strtotime($currentDate . ' +1 day'));
    }
    // 报表汇总
    $report['summary'] = [
        'total_work_days' => count(array_filter($report['detail'], fn($d) => $d['status'] !== '休息')),
        'total_late_minutes' => $totalLate,
        'total_early_minutes' => $totalEarly,
        'total_work_hours' => round($totalWorkHours, 2),
        'abnormal_days' => $abnormalDays,
        'late_days' => count(array_filter($report['detail'], fn($d) => $d['status'] === '迟到'))
    ];
    return $report;
}
// 在页面上展示
$reportData = generateMonthlyReport(1, '2024-01');
?>

数据库查询优化(SQL)

如果你需要直接在数据库层面做简单报表(不做复杂规则),可以使用SQL:

-- 按天汇总每个人的考勤情况(假设上下班时间固定为09:00和18:00)
SELECT 
    u.name,
    a.date,
    a.clock_in,
    a.clock_out,
    CASE 
        WHEN a.clock_in > '09:00:00' THEN '迟到'
        WHEN a.clock_out < '18:00:00' THEN '早退'
        WHEN a.clock_in IS NULL THEN '缺勤'
        ELSE '正常'
    END AS status,
    TIMESTAMPDIFF(HOUR, a.clock_in, a.clock_out) - 1 AS work_hours -- 扣除午休1小时
FROM 
    attendance_log a
JOIN 
    users u ON a.user_id = u.id
WHERE 
    a.date BETWEEN '2024-01-01' AND '2024-01-31'
ORDER BY 
    a.date, u.name;

前端展示与导出

  1. HTML表格:使用Bootstrap生成漂亮表格。
  2. 导出Excel:最常用的方案是使用 PhpSpreadsheet 库。
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
// 将 $reportData 导出为Excel
function exportToExcel($reportData, $filename) {
    $spreadsheet = new Spreadsheet();
    $sheet = $spreadsheet->getActiveSheet();
    // 设置表头
    $sheet->setCellValue('A1', '日期');
    $sheet->setCellValue('B1', '姓名');
    $sheet->setCellValue('C1', '上班打卡');
    $sheet->setCellValue('D1', '下班打卡');
    // ... 其他列
    // 填充数据
    $row = 2;
    foreach ($reportData['detail'] as $item) {
        $sheet->setCellValue('A'.$row, $item['date']);
        $sheet->setCellValue('B'.$row, $item['user_name']);
        $sheet->setCellValue('C'.$row, $item['clock_in']);
        $sheet->setCellValue('D'.$row, $item['clock_out']);
        $row++;
    }
    // 生成文件
    $writer = new Xlsx($spreadsheet);
    header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    header('Content-Disposition: attachment; filename="'.$filename.'.xlsx"');
    $writer->save('php://output');
}

实际项目中的难点与建议

  1. 复杂排班:不是所有人都是9-6,需要支持 排班表,每个人每天可能不同班次。
    • 方案:建立 shifts 表(早班、晚班、中班),user_schedule 表每天关联一个班次。
  2. 跨天打卡:比如晚班20:00打卡,第二天早8:00下班。
    • 方案attendance_log 应该记录 clock_in_timeclock_out_time 为完整时间戳(如 2024-01-01 20:00:00),不要只存时间。
  3. 实时性:如果报表需要实时生成,规则计算会很慢。
    • 方案:使用 定时任务(Cron Job) 每天凌晨计算前一天的所有数据,存储到汇总表,报表直接从汇总表读取。
  4. 性能:上万人的公司,每天遍历31天逐行计算会很慢。
    • 方案:尽量用SQL窗口函数或存储过程处理;PHP端使用批处理缓存

总结实现流程

  1. 定义数据模型:用户、打卡记录、排班、假期。
  2. 接收打卡数据:API或文件导入。
  3. 运行计算脚本:每晚通过Cron执行 php calculate_attendance.php
  4. 提供查询接口:根据日期、部门、员工查询 attendance_summary 表。
  5. 导出报表:使用PhpSpreadsheet导出Excel。

如果你需要具体的某个环节(如Excel导出、复杂排班逻辑、SQL优化)的详细代码,可以进一步描述你的需求。

抱歉,评论功能暂时关闭!