本文目录导读:

这是一个相对复杂但很常见的需求,要用PHP实现一个考勤报表系统,核心在于数据采集、规则计算和数据展示三个环节。
以下是一个从设计思路到具体实现的分步指南,包含核心代码片段。
整体架构设计
- 数据源:通常是打卡机(硬件)生成的文本/log文件,或者通过API获取,最常见的是存入MySQL数据库。
- 核心表设计:
- 考勤记录表(
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;
前端展示与导出
- HTML表格:使用Bootstrap生成漂亮表格。
- 导出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');
}
实际项目中的难点与建议
- 复杂排班:不是所有人都是9-6,需要支持 排班表,每个人每天可能不同班次。
- 方案:建立
shifts表(早班、晚班、中班),user_schedule表每天关联一个班次。
- 方案:建立
- 跨天打卡:比如晚班20:00打卡,第二天早8:00下班。
- 方案:
attendance_log应该记录clock_in_time和clock_out_time为完整时间戳(如2024-01-01 20:00:00),不要只存时间。
- 方案:
- 实时性:如果报表需要实时生成,规则计算会很慢。
- 方案:使用 定时任务(Cron Job) 每天凌晨计算前一天的所有数据,存储到汇总表,报表直接从汇总表读取。
- 性能:上万人的公司,每天遍历31天逐行计算会很慢。
- 方案:尽量用SQL窗口函数或存储过程处理;PHP端使用批处理和缓存。
总结实现流程
- 定义数据模型:用户、打卡记录、排班、假期。
- 接收打卡数据:API或文件导入。
- 运行计算脚本:每晚通过Cron执行
php calculate_attendance.php。 - 提供查询接口:根据日期、部门、员工查询
attendance_summary表。 - 导出报表:使用PhpSpreadsheet导出Excel。
如果你需要具体的某个环节(如Excel导出、复杂排班逻辑、SQL优化)的详细代码,可以进一步描述你的需求。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播