|
@@ -0,0 +1,147 @@
|
|
|
|
+<?php
|
|
|
|
+
|
|
|
|
+namespace App\Services\Client\Traits;
|
|
|
|
+
|
|
|
|
+use App\Enums\OrderStatus;
|
|
|
|
+use App\Enums\TechnicianAuthStatus;
|
|
|
|
+use App\Enums\TechnicianStatus;
|
|
|
|
+use App\Models\CoachSchedule;
|
|
|
|
+use App\Models\CoachUser;
|
|
|
|
+use App\Models\Order;
|
|
|
|
+use Carbon\Carbon;
|
|
|
|
+
|
|
|
|
+trait ValidatesServiceTime
|
|
|
|
+{
|
|
|
|
+ private function validateServiceTimeParams(int $coachId, string $serviceTime): void
|
|
|
|
+ {
|
|
|
|
+ // 验证技师状态
|
|
|
|
+ $coach = CoachUser::where('id', $coachId)
|
|
|
|
+ ->where('state', TechnicianStatus::ACTIVE->value)
|
|
|
|
+ ->where('auth_state', TechnicianAuthStatus::PASSED->value)
|
|
|
|
+ ->firstOrFail();
|
|
|
|
+
|
|
|
|
+ // 验证时间格式
|
|
|
|
+ $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(
|
|
|
|
+ $serviceDateTime->diffInDays(now()) > $maxAdvanceDays,
|
|
|
|
+ 400,
|
|
|
|
+ "最多只能提前{$maxAdvanceDays}天预约"
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private function getCoachWorkSchedule(int $coachId): array
|
|
|
|
+ {
|
|
|
|
+ $schedule = CoachSchedule::where('coach_id', $coachId)
|
|
|
|
+ ->where('state', 1)
|
|
|
|
+ ->first();
|
|
|
|
+
|
|
|
|
+ if (! $schedule) {
|
|
|
|
+ return [
|
|
|
|
+ 'work_days' => range(1, 7),
|
|
|
|
+ 'work_hours' => [
|
|
|
|
+ 'start' => '09:00',
|
|
|
|
+ 'end' => '21:00',
|
|
|
|
+ ],
|
|
|
|
+ 'rest_dates' => [],
|
|
|
|
+ ];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return [
|
|
|
|
+ 'work_days' => json_decode($schedule->work_days, true),
|
|
|
|
+ 'work_hours' => [
|
|
|
|
+ 'start' => $schedule->work_start_time,
|
|
|
|
+ 'end' => $schedule->work_end_time,
|
|
|
|
+ ],
|
|
|
|
+ 'rest_dates' => json_decode($schedule->rest_dates, true) ?? [],
|
|
|
|
+ ];
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private function validateWorkingHours(string $serviceTime, array $workSchedule): void
|
|
|
|
+ {
|
|
|
|
+ $serviceDateTime = Carbon::parse($serviceTime);
|
|
|
|
+
|
|
|
|
+ // 检查工作日
|
|
|
|
+ $dayOfWeek = $serviceDateTime->dayOfWeek;
|
|
|
|
+ abort_if(
|
|
|
|
+ ! in_array($dayOfWeek, $workSchedule['work_days']),
|
|
|
|
+ 400,
|
|
|
|
+ '该时间不在技师工作日内'
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ // 检查特殊休息日
|
|
|
|
+ $dateStr = $serviceDateTime->format('Y-m-d');
|
|
|
|
+ abort_if(
|
|
|
|
+ in_array($dateStr, $workSchedule['rest_dates']),
|
|
|
|
+ 400,
|
|
|
|
+ '技师该日期休息'
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ // 检查工作时间
|
|
|
|
+ $timeStr = $serviceDateTime->format('H:i');
|
|
|
|
+ $startTime = Carbon::parse($workSchedule['work_hours']['start']);
|
|
|
|
+ $endTime = Carbon::parse($workSchedule['work_hours']['end']);
|
|
|
|
+
|
|
|
|
+ abort_if(
|
|
|
|
+ $timeStr < $startTime->format('H:i') || $timeStr > $endTime->format('H:i'),
|
|
|
|
+ 400,
|
|
|
|
+ sprintf(
|
|
|
|
+ '服务时间需在%s-%s之间',
|
|
|
|
+ $startTime->format('H:i'),
|
|
|
|
+ $endTime->format('H:i')
|
|
|
|
+ )
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private function checkTimeConflicts(int $coachId, string $serviceTime): void
|
|
|
|
+ {
|
|
|
|
+ $serviceDateTime = Carbon::parse($serviceTime);
|
|
|
|
+
|
|
|
|
+ // 获取服务时长(默认2小时)
|
|
|
|
+ $serviceDuration = config('business.default_service_duration', 120);
|
|
|
|
+
|
|
|
|
+ // 计算服务结束时间
|
|
|
|
+ $serviceEndTime = $serviceDateTime->copy()->addMinutes($serviceDuration);
|
|
|
|
+
|
|
|
|
+ // 检查是否有其他订单在这个时间段
|
|
|
|
+ $conflictOrder = Order::where('coach_id', $coachId)
|
|
|
|
+ ->whereIn('state', [
|
|
|
|
+ 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,
|
|
|
|
+ '该时间段技师已有其他订单'
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+}
|