guard()->user(); $time = $request->filled('time') ? Carbon::createFromFormat('Y-m', $request->input('time')) : now(); $list = EmployeeSign::where('employee_id', $user->id)->filter(['month' => $time->format('Y-m')])->get(); $data = []; $start = $time->copy()->startOfMonth(); $end = $time->copy()->endOfMonth(); do { $info = $list->where(fn ($item) => $item->date->format('Y-m-d') == $start->format('Y-m-d'))->first(); array_push($data, [ 'date' => $start->format('Y-m-d'), 'sign_status' => $info ? $info->sign_status : null, 'first_time' => $info && $info->first_time ? $info->first_time->format('H:i') : '', 'last_time' => $info && $info->last_time ? $info->last_time->format('H:i') : '', ]); $start->addDay(); } while (! $end->isSameDay($start)); return $data; } public function info(Request $request, EmployeeSignService $service) { $user = $this->guard()->user(); $store = $user->store; if (! $store) { throw new RuntimeException('没有绑定门店'); } $date = now(); // 上班/下班 打卡, 当天是否打卡 $time = SignTime::Morning; if ( EmployeeSignLog::filter([ 'employee_id' => $user->id, 'date' => $date->format('Y-m-d'), ])->exists() ) { $time = SignTime::Afternoon; } // 根据定位的距离判断, 是否外勤 $maxDistance = (int) AdminSettingService::make()->arrayGet('sign', 'distance', 0); $data = [ 'time' => $time, 'enable' => false, 'type' => SignType::Normal, 'distance' => null, 'description' => '请打开手机定位, 并给予APP获取位置信息权限', 'maxDistance' => $maxDistance, ]; // 如果开启定位 if ($request->filled(['lon', 'lat'])) { $lon = $request->input('lon'); $lat = $request->input('lat'); // 切换坐标系 $locationType = $request->input('location_type', 'wgs84'); if ($locationType == 'wgs84') { $res = wgs84ToGcj02($lat, $lon); $lon = $res['lon']; $lat = $res['lat']; } // 计算距离 $distance = $service->haversineDistance( $lat, $lon, $store->lat, $store->lon, ); if ($maxDistance > 0 && $distance > $maxDistance) { $data = array_merge($data, [ 'enable' => true, 'type' => SignType::Outside, 'distance' => $distance, 'description' => '当前位置不在考勤范围内, 请选择外勤打卡', ]); } else { $data = array_merge($data, [ 'enable' => true, 'distance' => $distance, 'description' => '已进入考勤范围: '.$store->title, ]); } } return $data; } public function store(Request $request, EmployeeSignService $service) { $request->validate([ 'type' => ['required', Rule::enum(SignType::class)], 'time' => ['required', Rule::enum(SignTime::class)], 'position' => ['required'], 'position.lon' => ['required'], 'position.lat' => ['required'], ], [ 'type.required' => __('employee_sign_log.sign_type').'必填', 'time.required' => __('employee_sign_log.sign_time').'必填', 'position.required' => __('employee_sign_log.position').'必填', 'position.*.required' => __('employee_sign_log.position').'必填', ]); $user = $this->guard()->user(); $store = $user->store; if (! $store) { throw new RuntimeException('没有绑定门店'); } $time = SignTime::from($request->input('time')); $type = SignType::from($request->input('type')); // 根据定位的距离判断, 是否需要外勤打卡 $maxDistance = (int) AdminSettingService::make()->arrayGet('sign', 'distance'); if ($maxDistance > 0 && $type == SignType::Normal) { $lon = $request->input('position.lon'); $lat = $request->input('position.lat'); $locationType = $request->input('position.location_type', 'wgs84'); // 切换坐标系 if ($locationType == 'wgs84') { $res = wgs84ToGcj02($lat, $lon); $lon = $res['lon']; $lat = $res['lat']; } $distance = $service->haversineDistance( $lat, $lon, $store->lat, $store->lon, ); if ($distance > $maxDistance) { throw new RuntimeException('当前位置不在考勤范围内, 请选择外勤打卡'); } } try { DB::beginTransaction(); if (! $service->signDay($user, $time, now(), $request->only(['remarks', 'position', 'type']))) { throw new RuntimeException($service->getError()); } DB::commit(); return response('', Response::HTTP_OK); } catch (\Exception $e) { DB::rollBack(); throw new RuntimeException($e->getMessage()); } } }