You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
277 lines
9.3 KiB
277 lines
9.3 KiB
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | Niucloud-admin 企业快速开发的多应用管理平台
|
|
// +----------------------------------------------------------------------
|
|
// | 官方网址:https://www.niucloud.com
|
|
// +----------------------------------------------------------------------
|
|
// | niucloud团队 版权所有 开源版本可自由商用
|
|
// +----------------------------------------------------------------------
|
|
// | Author: Niucloud Team
|
|
// +----------------------------------------------------------------------
|
|
|
|
namespace app\job\transfer\schedule;
|
|
|
|
use app\model\staff\Staff;
|
|
use app\model\staff\StaffAttendance;
|
|
use core\base\BaseJob;
|
|
use think\facade\Log;
|
|
|
|
/**
|
|
* 定时处理员工考勤状态任务
|
|
* 自动生成考勤记录、处理迟到早退、计算工作时长等
|
|
*/
|
|
class StaffAttendanceJob extends BaseJob
|
|
{
|
|
/**
|
|
* 执行定时任务
|
|
* @param mixed ...$data 任务参数
|
|
* @return bool 处理结果
|
|
*/
|
|
public function doJob(...$data)
|
|
{
|
|
Log::write('开始执行员工考勤状态处理任务');
|
|
|
|
try {
|
|
$currentDate = date('Y-m-d');
|
|
$currentTime = date('H:i:s');
|
|
|
|
// 处理当日考勤记录生成
|
|
$generateResult = $this->generateDailyAttendance($currentDate);
|
|
|
|
// 处理迟到状态更新
|
|
$lateResult = $this->processLateArrivals($currentDate, $currentTime);
|
|
|
|
// 处理早退状态更新
|
|
$earlyResult = $this->processEarlyDepartures($currentDate, $currentTime);
|
|
|
|
// 处理缺勤状态更新
|
|
$absentResult = $this->processAbsences($currentDate);
|
|
|
|
// 计算工作时长
|
|
$workHoursResult = $this->calculateWorkHours($currentDate);
|
|
|
|
Log::write('员工考勤状态处理任务执行完成', [
|
|
'generated_records' => $generateResult['count'],
|
|
'late_arrivals' => $lateResult['count'],
|
|
'early_departures' => $earlyResult['count'],
|
|
'absences' => $absentResult['count'],
|
|
'calculated_hours' => $workHoursResult['count']
|
|
]);
|
|
|
|
return true;
|
|
|
|
} catch (\Exception $e) {
|
|
Log::write('员工考勤状态处理任务执行失败:' . $e->getMessage());
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 生成当日考勤记录
|
|
* @param string $date 日期
|
|
* @return array 处理结果
|
|
*/
|
|
protected function generateDailyAttendance($date)
|
|
{
|
|
// 获取所有在职员工
|
|
$activeStaff = Staff::where('status', 1)->select();
|
|
|
|
$count = 0;
|
|
foreach ($activeStaff as $staff) {
|
|
// 检查是否已有当日考勤记录
|
|
$exists = StaffAttendance::where('staff_id', $staff->id)
|
|
->where('attendance_date', $date)
|
|
->count();
|
|
|
|
if ($exists == 0) {
|
|
// 创建考勤记录
|
|
$attendance = new StaffAttendance();
|
|
$attendance->staff_id = $staff->id;
|
|
$attendance->attendance_date = $date;
|
|
$attendance->status = 'pending'; // 待考勤状态
|
|
$attendance->work_schedule_start = '09:00:00'; // 默认上班时间
|
|
$attendance->work_schedule_end = '18:00:00'; // 默认下班时间
|
|
$attendance->created_at = date('Y-m-d H:i:s');
|
|
$attendance->save();
|
|
|
|
$count++;
|
|
Log::write("为员工生成考勤记录:员工ID {$staff->id}, 日期: {$date}");
|
|
}
|
|
}
|
|
|
|
return ['count' => $count];
|
|
}
|
|
|
|
/**
|
|
* 处理迟到状态
|
|
* @param string $date 日期
|
|
* @param string $currentTime 当前时间
|
|
* @return array 处理结果
|
|
*/
|
|
protected function processLateArrivals($date, $currentTime)
|
|
{
|
|
// 查找今日还未签到但已过上班时间的员工
|
|
$lateThreshold = '09:30:00'; // 迟到阈值:9:30后算迟到
|
|
|
|
$lateAttendances = StaffAttendance::where('attendance_date', $date)
|
|
->where('check_in_time', null)
|
|
->where('work_schedule_start', '<', $currentTime)
|
|
->select();
|
|
|
|
$count = 0;
|
|
foreach ($lateAttendances as $attendance) {
|
|
if ($currentTime > $lateThreshold) {
|
|
$attendance->status = 'late';
|
|
$attendance->late_minutes = $this->calculateLateMinutes($attendance->work_schedule_start, $currentTime);
|
|
$attendance->save();
|
|
|
|
$count++;
|
|
Log::write("员工迟到:员工ID {$attendance->staff_id}, 迟到 {$attendance->late_minutes} 分钟");
|
|
}
|
|
}
|
|
|
|
return ['count' => $count];
|
|
}
|
|
|
|
/**
|
|
* 处理早退状态
|
|
* @param string $date 日期
|
|
* @param string $currentTime 当前时间
|
|
* @return array 处理结果
|
|
*/
|
|
protected function processEarlyDepartures($date, $currentTime)
|
|
{
|
|
// 查找今日已签到但提前签退的员工
|
|
$earlyAttendances = StaffAttendance::where('attendance_date', $date)
|
|
->where('check_in_time', '<>', null)
|
|
->where('check_out_time', '<>', null)
|
|
->where('check_out_time', '<', 'work_schedule_end')
|
|
->select();
|
|
|
|
$count = 0;
|
|
foreach ($earlyAttendances as $attendance) {
|
|
$attendance->status = 'early_departure';
|
|
$attendance->early_minutes = $this->calculateEarlyMinutes($attendance->check_out_time, $attendance->work_schedule_end);
|
|
$attendance->save();
|
|
|
|
$count++;
|
|
Log::write("员工早退:员工ID {$attendance->staff_id}, 早退 {$attendance->early_minutes} 分钟");
|
|
}
|
|
|
|
return ['count' => $count];
|
|
}
|
|
|
|
/**
|
|
* 处理缺勤状态
|
|
* @param string $date 日期
|
|
* @return array 处理结果
|
|
*/
|
|
protected function processAbsences($date)
|
|
{
|
|
$currentTime = date('H:i:s');
|
|
$absentThreshold = '18:30:00'; // 18:30后还未签到算缺勤
|
|
|
|
if ($currentTime < $absentThreshold) {
|
|
return ['count' => 0]; // 还没到判断缺勤的时间
|
|
}
|
|
|
|
// 查找今日全天未签到的员工
|
|
$absentAttendances = StaffAttendance::where('attendance_date', $date)
|
|
->where('check_in_time', null)
|
|
->where('status', '!=', 'leave') // 排除请假的员工
|
|
->select();
|
|
|
|
$count = 0;
|
|
foreach ($absentAttendances as $attendance) {
|
|
$attendance->status = 'absent';
|
|
$attendance->save();
|
|
|
|
$count++;
|
|
Log::write("员工缺勤:员工ID {$attendance->staff_id}, 日期: {$date}");
|
|
}
|
|
|
|
return ['count' => $count];
|
|
}
|
|
|
|
/**
|
|
* 计算工作时长
|
|
* @param string $date 日期
|
|
* @return array 处理结果
|
|
*/
|
|
protected function calculateWorkHours($date)
|
|
{
|
|
// 查找当日已完成签到签退的考勤记录
|
|
$completedAttendances = StaffAttendance::where('attendance_date', $date)
|
|
->where('check_in_time', '<>', null)
|
|
->where('check_out_time', '<>', null)
|
|
->where('work_hours', null) // 还未计算工作时长的
|
|
->select();
|
|
|
|
$count = 0;
|
|
foreach ($completedAttendances as $attendance) {
|
|
$workHours = $this->calculateHoursBetween($attendance->check_in_time, $attendance->check_out_time);
|
|
|
|
$attendance->work_hours = $workHours;
|
|
$attendance->save();
|
|
|
|
$count++;
|
|
Log::write("计算员工工作时长:员工ID {$attendance->staff_id}, 工作时长: {$workHours} 小时");
|
|
}
|
|
|
|
return ['count' => $count];
|
|
}
|
|
|
|
/**
|
|
* 计算迟到分钟数
|
|
* @param string $scheduleTime 计划时间
|
|
* @param string $actualTime 实际时间
|
|
* @return int 迟到分钟数
|
|
*/
|
|
protected function calculateLateMinutes($scheduleTime, $actualTime)
|
|
{
|
|
$schedule = strtotime($scheduleTime);
|
|
$actual = strtotime($actualTime);
|
|
|
|
if ($actual > $schedule) {
|
|
return intval(($actual - $schedule) / 60);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* 计算早退分钟数
|
|
* @param string $actualTime 实际时间
|
|
* @param string $scheduleTime 计划时间
|
|
* @return int 早退分钟数
|
|
*/
|
|
protected function calculateEarlyMinutes($actualTime, $scheduleTime)
|
|
{
|
|
$actual = strtotime($actualTime);
|
|
$schedule = strtotime($scheduleTime);
|
|
|
|
if ($schedule > $actual) {
|
|
return intval(($schedule - $actual) / 60);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* 计算两个时间之间的小时数
|
|
* @param string $startTime 开始时间
|
|
* @param string $endTime 结束时间
|
|
* @return float 小时数
|
|
*/
|
|
protected function calculateHoursBetween($startTime, $endTime)
|
|
{
|
|
$start = strtotime($startTime);
|
|
$end = strtotime($endTime);
|
|
|
|
if ($end > $start) {
|
|
return round(($end - $start) / 3600, 2);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|