$coachId, 'service_time' => $serviceTime, 'duration' => $duration ]); // 验证基本参数 Log::info('开始验证基本参数'); $this->validateServiceTimeParams($coachId, $serviceTime, $duration); Log::info('基本参数验证通过'); Log::info('服务时间验证通过'); return true; } catch (\Exception $e) { Log::error('验证服务时间失败', [ 'coach_id' => $coachId, 'service_time' => $serviceTime, 'duration' => $duration, 'error_message' => $e->getMessage(), 'error_code' => $e->getCode(), 'error_file' => $e->getFile(), 'error_line' => $e->getLine(), 'error_trace' => $e->getTraceAsString() ]); throw $e; } } /** * 验证服务时间参数 * * @param int $coachId 技师ID * @param string $serviceTime 服务时间(格式:Y-m-d H:i:s) * @param int $duration 服务时长(分钟) * * @throws \Exception 验证失败时抛出异常 */ private function validateServiceTimeParams(int $coachId, string $serviceTime, int $duration): void { Log::info('开始验证技师状态', ['coach_id' => $coachId]); // 验证技师状态 $coach = CoachUser::query() ->with(['info']) ->where('id', $coachId) ->where('state', TechnicianStatus::ACTIVE->value) ->first(); abort_if(! $coach, 400, '技师不存在或未激活'); Log::info('技师状态验证通过', ['coach' => $coach->toArray()]); // 验证技师认证状态 Log::info('开始验证技师认证状态'); abort_if( ! $coach->info || $coach->info->state !== TechnicianAuthStatus::PASSED->value, 400, '技师未通过认证' ); Log::info('技师认证状态验证通过'); // 验证服务时间基本参数 Log::info('开始验证服务时间基本参数', ['service_time' => $serviceTime]); $this->validateBasicServiceTime($serviceTime); Log::info('服务时间基本参数验证通过'); // 验证技师工作时间 Log::info('开始验证技师工作时间'); $workSchedule = $this->getCoachWorkSchedule($coachId); Log::info('获取到技师排班信息', ['work_schedule' => $workSchedule]); $serviceDateTime = Carbon::parse($serviceTime); $serviceEndTime = $serviceDateTime->copy()->addMinutes($duration); Log::info('计算服务时间', [ 'service_start' => $serviceDateTime->format('Y-m-d H:i:s'), 'service_end' => $serviceEndTime->format('Y-m-d H:i:s'), 'duration' => $duration ]); $this->validateWorkingHours($serviceDateTime, $serviceEndTime, $workSchedule); Log::info('技师工作时间验证通过'); // 验证时间冲突 Log::info('开始验证时间冲突'); $this->checkTimeConflicts($coachId, $serviceDateTime, $serviceEndTime); Log::info('时间冲突验证通过'); } /** * 验证服务时间基本参数 * * 业务规则: * 1. 服务时间不能早于当前时间 * 2. 最多只能提前7天预约 * * @param string $serviceTime 服务时间 * * @throws \Exception 验证失败时抛出异常 */ private function validateBasicServiceTime(string $serviceTime): void { $serviceDateTime = Carbon::parse($serviceTime); // 验证是否过期 abort_if( $serviceDateTime->isPast(), 400, '服务时间不能早于当前时间' ); // 验证预约时间范围(最多提前7天) $maxAdvanceDays = config('business.max_advance_days', 7); abort_if( $serviceDateTime->diffInDays(now()) > $maxAdvanceDays, 400, "最多只能提前{$maxAdvanceDays}天预约" ); } /** * 获取技师工作时间安排 * * 业务规则: * 1. 优先使用技师的自定义排班 * 2. 如果没有排班,使用默认时间(09:00-21:00) * 3. 默认每天都工作 * * @param int $coachId 技师ID * @return array 工作时间安排 */ private function getCoachWorkSchedule(int $coachId): array { Log::info('开始获取技师排班信息', ['coach_id' => $coachId]); $schedule = CoachSchedule::where('coach_id', $coachId) ->where('state', 1) ->first(); Log::info('查询技师排班记录', [ 'coach_id' => $coachId, 'has_schedule' => !is_null($schedule), 'schedule' => $schedule ? $schedule->toArray() : null ]); if (! $schedule) { $defaultSchedule = [ 'work_days' => range(1, 7), 'work_hours' => [ [ 'start' => '09:00', 'end' => '21:00', ] ], 'rest_dates' => [], ]; Log::info('未找到排班记录,使用默认排班', ['default_schedule' => $defaultSchedule]); return $defaultSchedule; } $timeRanges = is_string($schedule->time_ranges) ? json_decode($schedule->time_ranges, true) : $schedule->time_ranges; Log::info('解析排班时间范围', [ 'raw_time_ranges' => $schedule->time_ranges, 'parsed_time_ranges' => $timeRanges ]); // 从排班表中获取工作日 $workDays = []; $workHours = []; foreach ($timeRanges as $index => $range) { Log::info('处理时间范围', [ 'index' => $index, 'range' => $range ]); $workHours[] = [ 'start' => $range['start_time'], 'end' => $range['end_time'], ]; Log::info('更新工作时间', [ 'current_work_hours' => $workHours ]); } $workDays = array_unique($workDays); Log::info('处理工作日', [ 'initial_work_days' => $workDays ]); // 如果没有设置工作日,则默认每天都工作 if (empty($workDays)) { $workDays = range(1, 7); Log::info('未设置工作日,使用默认每天工作', [ 'default_work_days' => $workDays ]); } $result = [ 'work_days' => $workDays, 'work_hours' => $workHours, ]; Log::info('获取技师排班信息完成', [ 'final_schedule' => $result ]); return $result; } /** * 验证是否在工作时间内 */ private function validateWorkingHours(Carbon $serviceDateTime, Carbon $serviceEndTime, array $workSchedule): void { $isInSchedule = false; foreach ($workSchedule['work_hours'] as $range) { $rangeStart = Carbon::parse($serviceDateTime->format('Y-m-d') . ' ' . $range['start']); $rangeEnd = Carbon::parse($serviceDateTime->format('Y-m-d') . ' ' . $range['end']); if ( $serviceDateTime->between($rangeStart, $rangeEnd) ) { $isInSchedule = true; break; } } abort_if(!$isInSchedule, 400, '所选时间不在技师服务时间范围内'); } /** * 检查订单时间冲突 */ private function checkTimeConflicts(int $coachId, Carbon $serviceDateTime, Carbon $serviceEndTime): void { // 检查是否与其他订单时间重叠 $conflictingOrders = Order::where('coach_id', $coachId) ->where(function ($query) use ($serviceDateTime, $serviceEndTime) { $query->where(function ($q) use ($serviceDateTime, $serviceEndTime) { $q->where('service_start_time', '<=', $serviceEndTime->format('Y-m-d H:i:s')) ->where('service_end_time', '>=', $serviceDateTime->format('Y-m-d H:i:s')); }); }) ->whereIn('state', [ OrderStatus::ACCEPTED->value, OrderStatus::DEPARTED->value, OrderStatus::ARRIVED->value, OrderStatus::SERVICING->value, ]) ->exists(); abort_if($conflictingOrders, 400, '该时间段技师已有其他预约'); } }