delete(); // 删除补卡申请 EmployeeSignRepair::whereIn('employee_id', $ids)->delete(); } /** * 整理昨天的打卡流水, 生成对应的打卡记录 */ public function signResult(?Carbon $date = null) { $date = $date ?: now()->subDay(); $start = $date->copy()->startOfDay(); $end = $date->copy()->endOfDay(); // 打卡日志 $list = EmployeeSignLog::whereBetween('time', [$start, $end])->get(); // 休息的员工 $restEmployeeIds = EmployeeRest::whereBetWeen('date', [$start, $end])->pluck('employee_id'); // 需要打卡的员工 $employees = Employee::where('store_id', '>', 0)->where('is_sign', 1)->whereNotIn('id', $restEmployeeIds)->get(); foreach ($employees as $employee) { $logs = $list->where('employee_id', $employee->id); // 状态: 两个打卡=正常, 一次打卡 = 缺卡, 两次未打=旷工 $status = 0; // 外勤打卡-事由 $remarks = null; // 上班打卡 $firstTime = null; if ($item = $logs->where('sign_time', SignTime::Morning)->sortBy('time')->first()) { $firstTime = $item->time; $status++; if ($item->sign_type == SignType::Outside) { $remarks = $item->remarks; } } // 下班打卡 $lastTime = null; if ($item = $logs->where('sign_time', SignTime::Afternoon)->sortByDesc('time')->first()) { $lastTime = $item->time; $status++; if ($item->sign_type == SignType::Outside) { $remarks = $item->remarks; } } // 打卡类型 $type = SignType::None; if ($status > 0) { $type = $logs->where('sign_type', SignType::Outside)->count() > 0 ? SignType::Outside : SignType::Normal; } $attributes = [ 'sign_type' => $type, 'first_time' => $firstTime, 'last_time' => $lastTime, 'sign_status' => match ($status) { 0 => SignStatus::Absent, 1 => SignStatus::Lose, 2 => SignStatus::Normal, }, 'remarks' => $remarks, ]; $employee->signs()->updateOrCreate([ 'date' => $date, 'store_id' => $employee->store_id, ], $attributes); } } /** * 打卡 * * @param Employee $user 用户 * @param SignTime $time 上班/下班 打卡 * @param mixed $date 打卡时间 * @param array $options {type: 正常/外勤 打卡, remarks: 备注, position: 位置} * @return bool */ public function signDay(Employee $user, SignTime $time, $date = '', array $options = []) { $date = $date ?: now(); $log = EmployeeSignLog::create([ 'store_id' => $user->store_id, 'employee_id' => $user->id, 'sign_time' => $time, 'time' => $date, 'sign_type' => data_get($options, 'type'), 'remarks' => data_get($options, 'remarks'), 'position' => data_get($options, 'position'), 'is_repair' => data_get($options, 'is_repair') ? 1 : 0, 'outside_remarks' => data_get($options, 'outside_remarks'), 'repair_id' => data_get($options, 'repair_id'), ]); // 更新打卡情况 $sign = EmployeeSign::firstOrCreate([ 'date' => $date->format('Y-m-d'), 'store_id' => $user->store_id, 'employee_id' => $user->id, ]); $sign->sign_type = $log->sign_type; if ($time == SignTime::Morning) { $sign->first_time = $log->time; } elseif ($time == SignTime::Afternoon) { $sign->last_time = $log->time; } $sign->sign_status = SignStatus::Lose; if ($sign->first_time && $sign->last_time) { $sign->sign_status = SignStatus::Normal; } $sign->remarks = $log->remarks; $sign->save(); return true; } /** * 判断员工某天是否休息 */ public function hasRest(Employee $user, $date = '') { $date = $date ?: now(); return EmployeeRest::where('employee_id', $user->id)->where('date', $date)->exists(); } /** * 计算两点之间的距离(米) */ public function haversineDistance($lat1, $lon1, $lat2, $lon2) { // 地球半径(千米) $R = 6371; // 将角度转换为弧度 $lat1 = deg2rad($lat1); $lon1 = deg2rad($lon1); $lat2 = deg2rad($lat2); $lon2 = deg2rad($lon2); // 经纬度差值 $dlon = $lon2 - $lon1; $dlat = $lat2 - $lat1; // Calculate the Haversine formula $a = pow(sin($dlat / 2), 2) + cos($lat1) * cos($lat2) * pow(sin($dlon / 2), 2); $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); $distance = $R * $c; return floor($distance * 1000); } }