Browse Source

fixed:技师端-优化-排班设置

刘学玺 3 months ago
parent
commit
b5911c43c9

+ 6 - 42
app/Http/Controllers/Coach/AccountController.php

@@ -11,6 +11,7 @@ use Illuminate\Support\Facades\Auth;
 use App\Enums\TechnicianLocationType;
 use App\Services\Coach\AccountService;
 use App\Http\Requests\Coach\SetLocationRequest;
+use App\Http\Requests\Coach\SetScheduleRequest;
 use App\Http\Requests\Coach\SendVerifyCodeRequest;
 use App\Http\Requests\Coach\SubmitBaseInfoRequest;
 use App\Http\Requests\Coach\SubmitRealNameRequest;
@@ -288,7 +289,7 @@ class AccountController extends Controller
      *
      * @bodyParam time_ranges array required 时间段数组
      * @bodyParam time_ranges[].start_time string required 开始时间(HH:mm格式) Example: "09:00"
-     * @bodyParam time_ranges[].end_time string required 结束时间(HH:mm格式) Example: "12:00"
+     * @bodyParam time_ranges[].end_time string required 结束时间(HH:mm格式) Example: "24:00"
      *
      * @response {
      *   "status": true,
@@ -299,53 +300,16 @@ class AccountController extends Controller
      *       {
      *         "start_time": "09:00",
      *         "end_time": "12:00"
-     *       },
-     *       {
-     *         "start_time": "14:00",
-     *         "end_time": "18:00"
      *       }
-     *     ]
+     *     ],
+     *     "updated_at": "2024-03-21 10:00:00"
      *   }
      * }
-     * @response 400 {
-     *   "message": "时间段格式错误"
-     * }
-     * @response 400 {
-     *   "message": "时间格式错误,应为HH:mm格式"
-     * }
-     * @response 400 {
-     *   "message": "结束时间必须大于开始时间"
-     * }
-     * @response 400 {
-     *   "message": "时间段之间不能重叠"
-     * }
      */
-    public function setSchedule(Request $request)
+    public function setSchedule(SetScheduleRequest $request)
     {
-        $validated = $request->validate([
-            'time_ranges' => 'required|array|min:1',
-            'time_ranges.*.start_time' => [
-                'required',
-                'string',
-                'regex:/^([01][0-9]|2[0-3]):[0-5][0-9]$/',
-            ],
-            'time_ranges.*.end_time' => [
-                'required',
-                'string',
-                'regex:/^([01][0-9]|2[0-3]):[0-5][0-9]$/',
-            ],
-        ], [
-            'time_ranges.required' => '必须设置时间段',
-            'time_ranges.array' => '时间段必须是数组格式',
-            'time_ranges.min' => '至少设置一个时间段',
-            'time_ranges.*.start_time.required' => '开始时间不能为空',
-            'time_ranges.*.start_time.regex' => '开始时间格式错误,应为HH:mm格式',
-            'time_ranges.*.end_time.required' => '结束时间不能为空',
-            'time_ranges.*.end_time.regex' => '结束时间格式错误,应为HH:mm格式',
-        ]);
-
         return $this->success(
-            $this->service->setSchedule(Auth::user()->id, $validated['time_ranges'])
+            $this->service->setSchedule(Auth::user()->coach, $request->validated()['time_ranges'])
         );
     }
 

+ 55 - 0
app/Http/Requests/Coach/SetScheduleRequest.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace App\Http\Requests\Coach;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+/**
+ * 技师排班设置请求验证
+ */
+class SetScheduleRequest extends FormRequest
+{
+    /**
+     * 判断用户是否有权限进行此请求
+     */
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    /**
+     * 获取验证规则
+     */
+    public function rules(): array
+    {
+        return [
+            'time_ranges' => 'required|array|min:1',
+            'time_ranges.*.start_time' => [
+                'required',
+                'string',
+                'regex:/^([01][0-9]|2[0-3]):[0-5][0-9]$/',  // 开始时间保持 00:00-23:59
+            ],
+            'time_ranges.*.end_time' => [
+                'required',
+                'string',
+                'regex:/^((?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$/',  // 结束时间允许 24:00
+            ],
+        ];
+    }
+
+    /**
+     * 获取验证错误消息
+     */
+    public function messages(): array
+    {
+        return [
+            'time_ranges.required' => '必须设置时间段',
+            'time_ranges.array' => '时间段必须是数组格式',
+            'time_ranges.min' => '至少设置一个时间段',
+            'time_ranges.*.start_time.required' => '开始时间不能为空',
+            'time_ranges.*.start_time.regex' => '开始时间格式错误,应为00:00-23:59格式',
+            'time_ranges.*.end_time.required' => '结束时间不能为空',
+            'time_ranges.*.end_time.regex' => '结束时间格式错误,应为00:00-24:00格式',
+        ];
+    }
+}

+ 18 - 0
app/Models/CoachSchedule.php

@@ -15,4 +15,22 @@ class CoachSchedule extends Model
     protected $table = 'coach_schedules';
 
     protected $guarded = [];
+
+    /**
+     * 应该被转换为原生类型的属性
+     *
+     * @var array
+     */
+    protected $casts = [
+        'time_ranges' => 'array',  // 将 time_ranges 字段转换为数组
+    ];
+
+    /**
+     * @Author FelixYin
+     * @description 排班所属技师
+     */
+    public function coach()
+    {
+        return $this->belongsTo(CoachUser::class, 'coach_id', 'id');
+    }
 }

+ 73 - 118
app/Services/Coach/AccountService.php

@@ -732,64 +732,39 @@ class AccountService
     /**
      * 设置技师排班时间(每天通用)
      *
-     * @param  int  $userId  技师用户ID
-     * @param  array  $timeRanges  时间段数组 格式: [
-     *                             ['start_time' => '09:00', 'end_time' => '12:00'],
-     *                             ['start_time' => '14:00', 'end_time' => '18:00']
-     *                             ]
-     *
-     * @throws \Exception
+     * @param CoachUser $coach 技师对象
+     * @param array $timeRanges 时间段数组
+     * @return array 返回设置结果
+     * @throws \Exception 当设置失败时抛出异常
      */
-    public function setSchedule(int $userId, array $timeRanges): array
+    public function setSchedule(CoachUser $coach, array $timeRanges): array
     {
-        return DB::transaction(function () use ($userId, $timeRanges) {
-            try {
-                // 获取技师信息
-                $user = MemberUser::with(['coach'])->findOrFail($userId);
-                $coach = $user->coach;
-                abort_if(! $coach, 404, '技师信息不存在');
-
-                // 验证并排序时间段
-                $sortedRanges = $this->validateAndSortTimeRanges($timeRanges);
-
-                // 创建或更新排班记录
-                $schedule = CoachSchedule::updateOrCreate(
-                    [
-                        'coach_id' => $coach->id,
-                    ],
-                    [
-                        'time_ranges' => json_encode($sortedRanges),
-                        'state' => 1,
-                    ]
-                );
+        return DB::transaction(function () use ($coach, $timeRanges) {
+            // 验证并排序时间段
+            $sortedRanges = $this->validateAndSortTimeRanges($timeRanges);
+
+            // 创建或更新排班记录
+            $schedule = CoachSchedule::updateOrCreate(
+                ['coach_id' => $coach->id],
+                [
+                    'time_ranges' => $sortedRanges,  // 直接传入数组,模型会自动处理JSON转换
+                    'state' => 1,
+                ]
+            );
 
-                // 更新Redis缓存
-                $this->updateScheduleCache($coach->id, $sortedRanges);
+            // 更新Redis缓存
+            $this->updateScheduleCache($coach->id, $sortedRanges);
 
-                // 记录日志
-                Log::info('技师排班设置成功', [
+            // 返回设置结果
+            return [
+                'status' => true,
+                'message' => '排班设置成功',
+                'data' => [
                     'coach_id' => $coach->id,
                     'time_ranges' => $sortedRanges,
-                ]);
-
-                return [
-                    'status' => true,
-                    'message' => '排班设置成功',
-                    'data' => [
-                        'coach_id' => $coach->id,
-                        'time_ranges' => $sortedRanges,
-                        'updated_at' => $schedule->updated_at->toDateTimeString(),
-                    ],
-                ];
-            } catch (\Exception $e) {
-                Log::error('技师排班设置败', [
-                    'user_id' => $userId,
-                    'time_ranges' => $timeRanges,
-                    'error' => $e->getMessage(),
-                    'trace' => $e->getTraceAsString(),
-                ]);
-                throw $e;
-            }
+                    'updated_at' => $schedule->updated_at->toDateTimeString(),
+                ],
+            ];
         });
     }
 
@@ -804,17 +779,23 @@ class AccountService
         // 验证每个时间段格式并转换为分钟数进行比较
         $ranges = collect($timeRanges)->map(function ($range) {
             abort_if(
-                ! isset($range['start_time'], $range['end_time']),
+                !isset($range['start_time'], $range['end_time']),
                 400,
                 '时间段格式错误'
             );
 
-            // 验证时间格式
+            // 验证时间格式,限制在 00:00-24:00
             foreach (['start_time', 'end_time'] as $field) {
+                // 先检查是否为 24:00
+                if ($range[$field] === '24:00') {
+                    continue;
+                }
+
+                // 其他时间格式验证 (00:00-23:59)
                 abort_if(
-                    ! preg_match('/^([01][0-9]|2[0-3]):[0-5][0-9]$/', $range[$field]),
+                    !preg_match('/^((?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$/', $range[$field]),
                     400,
-                    '时间格式错,应为HH:mm格式'
+                    '时间格式错误,应为00:00-24:00格式'
                 );
             }
 
@@ -866,9 +847,12 @@ class AccountService
      */
     private function timeToMinutes(string $time): int
     {
-        [$hours, $minutes] = explode(':', $time);
+        if ($time === '24:00') {
+            return 24 * 60;  // 特殊处理 24:00
+        }
 
-        return (int) $hours * 60 + (int) $minutes;
+        [$hours, $minutes] = explode(':', $time);
+        return (int)$hours * 60 + (int)$minutes;
     }
 
     /**
@@ -876,24 +860,17 @@ class AccountService
      */
     private function updateScheduleCache(int $coachId, array $timeRanges): void
     {
-        try {
-            $cacheKey = "coach:schedule:{$coachId}";
-            $cacheData = [
-                'updated_at' => now()->toDateTimeString(),
-                'time_ranges' => $timeRanges,
-            ];
+        $cacheKey = "coach:schedule:{$coachId}";
+        $cacheData = [
+            'time_ranges' => $timeRanges,
+            'updated_at' => now()->toDateTimeString(),
+        ];
 
-            Redis::setex($cacheKey, 86400, json_encode($cacheData));
+        // 使用 Redis 存储,24小时过期
+        Redis::setex($cacheKey, 86400, json_encode($cacheData));
 
-            // 清除相关的可预约时间段缓存
-            $this->clearTimeSlotCache($coachId);
-        } catch (\Exception $e) {
-            Log::error('更新排班缓存失败', [
-                'coach_id' => $coachId,
-                'error' => $e->getMessage(),
-            ]);
-            // 缓存更新失败不影响主流程
-        }
+        // 清除相关的可预约时间段缓存
+        $this->clearTimeSlotCache($coachId);
     }
 
     /**
@@ -901,17 +878,10 @@ class AccountService
      */
     public function clearTimeSlotCache(int $coachId): void
     {
-        try {
-            $pattern = "coach:timeslots:{$coachId}:*";
-            $keys = Redis::keys($pattern);
-            if (! empty($keys)) {
-                Redis::del($keys);
-            }
-        } catch (\Exception $e) {
-            Log::error('清除可预约时间段缓存失败', [
-                'coach_id' => $coachId,
-                'error' => $e->getMessage(),
-            ]);
+        $pattern = "coach:timeslots:{$coachId}:*";
+        $keys = Redis::keys($pattern);
+        if (! empty($keys)) {
+            Redis::del($keys);
         }
     }
 
@@ -1053,48 +1023,33 @@ class AccountService
     /**
      * 获取技师排班信息
      *
-     * 业务流程:
-     * 1. 从缓存获取数据,不存在则从数据库读取
-     * 2. 解析排班时间段
-     * 3. 缓存结果数据
-     *
      * @param CoachUser $coach 技师对象
-     * @return array 返回排班信息,包含:
-     *        - time_ranges: array 工作时间段列表
-     *            - start_time: string 开始时间
-     *            - end_time: string 结束时间
-     * @throws \Exception 当获取失败时抛出异常
+     * @return array 返回排班信息
      */
     public function getSchedule(CoachUser $coach): array
     {
         $cacheKey = "coach:schedule:{$coach->id}";
 
-        return (array) Cache::remember($cacheKey, 86400, function () use ($coach) {
-            // 从数据库获取有效的排班记录
-            $schedule = CoachSchedule::where('coach_id', $coach->id)
-                ->where('state', 1)  // 有效状态
-                ->first();
+        // 尝试从 Redis 获取缓存
+        $cached = Redis::get($cacheKey);
+        if ($cached) {
+            return json_decode($cached, true);
+        }
 
-            // 构建并返回数据
-            return [
-                'time_ranges' => $this->parseTimeRanges($schedule),  // 解析时间段
-            ];
-        });
-    }
+        // 缓存不存在,从数据库获取
+        $schedule = CoachSchedule::where('coach_id', $coach->id)
+            ->where('state', 1)
+            ->first();
 
-    /**
-     * 解析排班时间段
-     *
-     * @param CoachSchedule|null $schedule 排班记录
-     * @return array 时间段列表
-     */
-    private function parseTimeRanges(?CoachSchedule $schedule): array
-    {
-        if (!$schedule) {
-            return [];
-        }
+        $data = [
+            'time_ranges' => $schedule ? $schedule->time_ranges : [],
+            'updated_at' => $schedule ? $schedule->updated_at->toDateTimeString() : now()->toDateTimeString(),
+        ];
+
+        // 写入 Redis 缓存
+        Redis::setex($cacheKey, 86400, json_encode($data));
 
-        return json_decode($schedule->time_ranges, true) ?: [];
+        return $data;
     }
 
     /**