فهرست منبع

fixed:用户端->技师-排班

刘学玺 4 ماه پیش
والد
کامیت
1c688598f2
2فایلهای تغییر یافته به همراه134 افزوده شده و 48 حذف شده
  1. 1 5
      app/Services/Client/CoachService.php
  2. 133 43
      app/Services/Client/Traits/ValidatesServiceTime.php

+ 1 - 5
app/Services/Client/CoachService.php

@@ -449,11 +449,7 @@ class CoachService
     }
 
     /**
-     * 检查是否有时间冲突
-     *
-     * @param  int  $coachId  技师ID
-     * @param  string  $serviceTime  服务时间
-     * @return bool 是否在可服务时间段中
+     * 验证技师服务时间是否在可用时间段内
      */
     public function validateServiceTimeWithinCoachAvailability(int $coachId, string $serviceTime): bool
     {

+ 133 - 43
app/Services/Client/Traits/ValidatesServiceTime.php

@@ -10,32 +10,89 @@ use App\Models\CoachUser;
 use App\Models\Order;
 use Carbon\Carbon;
 
+/**
+ * 服务时间验证 Trait
+ *
+ * 业务规则:
+ * 1. 技师状态验证:
+ *    - 技师必须存在且状态为激活
+ *    - 技师必须通过认证
+ *
+ * 2. 服务时间验证:
+ *    - 服务时间不能早于当前时间
+ *    - 最多只能提前7天预约
+ *    - 必须在技师的工作时间内
+ *    - 不能与其他订单时间冲突
+ *
+ * 3. 工作时间规则:
+ *    - 默认工作时间为 09:00-21:00
+ *    - 可以通过排班设置自定义工作时间
+ *    - 支持设置特殊休息日
+ *
+ * 4. 订单冲突规则:
+ *    - 检查已支付、已接单、服务中的订单
+ *    - 考虑服务时长(默认2小时)
+ *    - 开始时间不能在其他订单的服务时间段内
+ *    - 结束时间不能在其他订单的服务时间段内
+ */
 trait ValidatesServiceTime
 {
+    /**
+     * 验证服务时间参数
+     *
+     * @param  int  $coachId  技师ID
+     * @param  string  $serviceTime  服务时间(格式:Y-m-d H:i:s)
+     *
+     * @throws \Exception 验证失败时抛出异常
+     */
     private function validateServiceTimeParams(int $coachId, string $serviceTime): void
     {
         // 验证技师状态
-        $coach = CoachUser::where('id', $coachId)
+        $coach = CoachUser::query()
+            ->with(['info'])
+            ->where('id', $coachId)
             ->where('state', TechnicianStatus::ACTIVE->value)
-            ->where('auth_state', TechnicianAuthStatus::PASSED->value)
-            ->firstOrFail();
+            ->first();
+
+        abort_if(! $coach, 400, '技师不存在或未激活');
+
+        // 验证技师认证状态
+        abort_if(! $coach->info || $coach->info->state !== TechnicianAuthStatus::PASSED->value,
+            400, '技师未通过认证');
+
+        // 验证服务时间基本参数
+        $this->validateBasicServiceTime($serviceTime);
+
+        // 验证技师工作时间
+        $workSchedule = $this->getCoachWorkSchedule($coachId);
+        $this->validateWorkingHours($serviceTime, $workSchedule);
 
-        // 验证时间格式
+        // 验证时间冲突
+        $this->checkTimeConflicts($coachId, $serviceTime);
+    }
+
+    /**
+     * 验证服务时间基本参数
+     *
+     * 业务规则:
+     * 1. 服务时间不能早于当前时间
+     * 2. 最多只能提前7天预约
+     *
+     * @param  string  $serviceTime  服务时间
+     *
+     * @throws \Exception 验证失败时抛出异常
+     */
+    private function validateBasicServiceTime(string $serviceTime): void
+    {
         $serviceDateTime = Carbon::parse($serviceTime);
+
+        // 验证是否过期
         abort_if(
             $serviceDateTime->isPast(),
             400,
             '服务时间不能早于当前时间'
         );
 
-        // 验证预约提前时间(至少提前2小时)
-        $minAdvanceHours = config('business.min_advance_hours', 2);
-        abort_if(
-            $serviceDateTime->diffInHours(now()) < $minAdvanceHours,
-            400,
-            "需要至少提前{$minAdvanceHours}小时预约"
-        );
-
         // 验证预约时间范围(最多提前7天)
         $maxAdvanceDays = config('business.max_advance_days', 7);
         abort_if(
@@ -45,13 +102,23 @@ trait ValidatesServiceTime
         );
     }
 
+    /**
+     * 获取技师工作时间安排
+     *
+     * 业务规则:
+     * 1. 优先使用技师的自定义排班
+     * 2. 如果没有排班,使用默认时间(09:00-21:00)
+     * 3. 默认每天都工作
+     *
+     * @param  int  $coachId  技师ID
+     * @return array 工作时间安排
+     */
     private function getCoachWorkSchedule(int $coachId): array
     {
         $schedule = CoachSchedule::where('coach_id', $coachId)
             ->where('state', 1)
             ->first();
-
-        if (! $schedule) {
+        if (! $schedule || empty($schedule->time_ranges)) {
             return [
                 'work_days' => range(1, 7),
                 'work_hours' => [
@@ -62,40 +129,62 @@ trait ValidatesServiceTime
             ];
         }
 
+        $timeRanges = json_decode($schedule->time_ranges, true);
+
         return [
-            'work_days' => json_decode($schedule->work_days, true),
+            'work_days' => range(1, 7), // 默认每天都工作
             'work_hours' => [
-                'start' => $schedule->work_start_time,
-                'end' => $schedule->work_end_time,
+                'start' => $timeRanges[0]['start_time'],
+                'end' => end($timeRanges)['end_time'],
             ],
-            'rest_dates' => json_decode($schedule->rest_dates, true) ?? [],
+            'rest_dates' => [],
         ];
     }
 
+    /**
+     * 验证工作时间
+     *
+     * 业务规则:
+     * 1. 检查是否为工作日
+     * 2. 检查是否为特殊休息日
+     * 3. 检查是否在工作时间范围内
+     *
+     * @param  string  $serviceTime  服务时间
+     * @param  array  $workSchedule  工作时间安排
+     *
+     * @throws \Exception 验证失败时抛出异常
+     */
     private function validateWorkingHours(string $serviceTime, array $workSchedule): void
     {
         $serviceDateTime = Carbon::parse($serviceTime);
 
         // 检查工作日
         $dayOfWeek = $serviceDateTime->dayOfWeek;
+        $workDays = $workSchedule['work_days'] ?? range(1, 7); // 如果未设置,默认每天都工作
         abort_if(
-            ! in_array($dayOfWeek, $workSchedule['work_days']),
+            ! in_array($dayOfWeek, $workDays),
             400,
             '该时间不在技师工作日内'
         );
 
         // 检查特殊休息日
         $dateStr = $serviceDateTime->format('Y-m-d');
+        $restDates = $workSchedule['rest_dates'] ?? []; // 如果未设置,默认没有休息日
         abort_if(
-            in_array($dateStr, $workSchedule['rest_dates']),
+            in_array($dateStr, $restDates),
             400,
             '技师该日期休息'
         );
 
         // 检查工作时间
         $timeStr = $serviceDateTime->format('H:i');
-        $startTime = Carbon::parse($workSchedule['work_hours']['start']);
-        $endTime = Carbon::parse($workSchedule['work_hours']['end']);
+        $workHours = $workSchedule['work_hours'] ?? [
+            'start' => '09:00',
+            'end' => '21:00',
+        ]; // 如果未设置,使用默认工作时间
+
+        $startTime = Carbon::parse($workHours['start']);
+        $endTime = Carbon::parse($workHours['end']);
 
         abort_if(
             $timeStr < $startTime->format('H:i') || $timeStr > $endTime->format('H:i'),
@@ -108,40 +197,41 @@ trait ValidatesServiceTime
         );
     }
 
+    /**
+     * 检查时间冲突
+     *
+     * 业务规则:
+     * 1. 检查已支付、已接单、服务中的订单
+     * 2. 考虑服务时长(默认2小时)
+     * 3. 新订单的服务时间段不能与其他订单重叠
+     *
+     * @param  int  $coachId  技师ID
+     * @param  string  $serviceTime  服务时间
+     *
+     * @throws \Exception 验证失败时抛出异常
+     */
     private function checkTimeConflicts(int $coachId, string $serviceTime): void
     {
         $serviceDateTime = Carbon::parse($serviceTime);
 
-        // 获取服务时长(默认2小时)
+        // 获取服务时长配置(默认2小时)
         $serviceDuration = config('business.default_service_duration', 120);
 
-        // 计算服务结束时间
+        // 计算本次服务结束时间
         $serviceEndTime = $serviceDateTime->copy()->addMinutes($serviceDuration);
 
-        // 检查是否有其他订单在这个时间段
-        $conflictOrder = Order::where('coach_id', $coachId)
-            ->whereIn('state', [
+        // 检查是否与其他订单时间冲突
+        $conflictingOrder = Order::query()
+            ->where('coach_id', $coachId)
+            ->where('service_time', '<=', $serviceEndTime)
+            ->where('service_end_time', '>=', $serviceDateTime)
+            ->whereIn('status', [
                 OrderStatus::PAID->value,
                 OrderStatus::ACCEPTED->value,
                 OrderStatus::SERVING->value,
             ])
-            ->where(function ($query) use ($serviceDateTime, $serviceEndTime) {
-                $query->where(function ($q) use ($serviceDateTime) {
-                    // 新订单开始时间在其他订单的服务时间段内
-                    $q->where('service_time', '<=', $serviceDateTime)
-                        ->where('service_end_time', '>', $serviceDateTime);
-                })->orWhere(function ($q) use ($serviceEndTime) {
-                    // 新订单结束时间在其他订单的服务时间段内
-                    $q->where('service_time', '<', $serviceEndTime)
-                        ->where('service_end_time', '>=', $serviceEndTime);
-                });
-            })
             ->first();
 
-        abort_if(
-            $conflictOrder,
-            400,
-            '该时间段技师已有其他订单'
-        );
+        abort_if($conflictingOrder, 400, '该时间段已被预约');
     }
 }