ValidatesServiceTime.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. <?php
  2. namespace App\Services\Client\Traits;
  3. use App\Enums\OrderStatus;
  4. use App\Enums\TechnicianAuthStatus;
  5. use App\Enums\TechnicianStatus;
  6. use App\Models\CoachSchedule;
  7. use App\Models\CoachUser;
  8. use App\Models\Order;
  9. use Carbon\Carbon;
  10. trait ValidatesServiceTime
  11. {
  12. private function validateServiceTimeParams(int $coachId, string $serviceTime): void
  13. {
  14. // 验证技师状态
  15. $coach = CoachUser::where('id', $coachId)
  16. ->where('state', TechnicianStatus::ACTIVE->value)
  17. ->where('auth_state', TechnicianAuthStatus::PASSED->value)
  18. ->firstOrFail();
  19. // 验证时间格式
  20. $serviceDateTime = Carbon::parse($serviceTime);
  21. abort_if(
  22. $serviceDateTime->isPast(),
  23. 400,
  24. '服务时间不能早于当前时间'
  25. );
  26. // 验证预约提前时间(至少提前2小时)
  27. $minAdvanceHours = config('business.min_advance_hours', 2);
  28. abort_if(
  29. $serviceDateTime->diffInHours(now()) < $minAdvanceHours,
  30. 400,
  31. "需要至少提前{$minAdvanceHours}小时预约"
  32. );
  33. // 验证预约时间范围(最多提前7天)
  34. $maxAdvanceDays = config('business.max_advance_days', 7);
  35. abort_if(
  36. $serviceDateTime->diffInDays(now()) > $maxAdvanceDays,
  37. 400,
  38. "最多只能提前{$maxAdvanceDays}天预约"
  39. );
  40. }
  41. private function getCoachWorkSchedule(int $coachId): array
  42. {
  43. $schedule = CoachSchedule::where('coach_id', $coachId)
  44. ->where('state', 1)
  45. ->first();
  46. if (! $schedule) {
  47. return [
  48. 'work_days' => range(1, 7),
  49. 'work_hours' => [
  50. 'start' => '09:00',
  51. 'end' => '21:00',
  52. ],
  53. 'rest_dates' => [],
  54. ];
  55. }
  56. return [
  57. 'work_days' => json_decode($schedule->work_days, true),
  58. 'work_hours' => [
  59. 'start' => $schedule->work_start_time,
  60. 'end' => $schedule->work_end_time,
  61. ],
  62. 'rest_dates' => json_decode($schedule->rest_dates, true) ?? [],
  63. ];
  64. }
  65. private function validateWorkingHours(string $serviceTime, array $workSchedule): void
  66. {
  67. $serviceDateTime = Carbon::parse($serviceTime);
  68. // 检查工作日
  69. $dayOfWeek = $serviceDateTime->dayOfWeek;
  70. abort_if(
  71. ! in_array($dayOfWeek, $workSchedule['work_days']),
  72. 400,
  73. '该时间不在技师工作日内'
  74. );
  75. // 检查特殊休息日
  76. $dateStr = $serviceDateTime->format('Y-m-d');
  77. abort_if(
  78. in_array($dateStr, $workSchedule['rest_dates']),
  79. 400,
  80. '技师该日期休息'
  81. );
  82. // 检查工作时间
  83. $timeStr = $serviceDateTime->format('H:i');
  84. $startTime = Carbon::parse($workSchedule['work_hours']['start']);
  85. $endTime = Carbon::parse($workSchedule['work_hours']['end']);
  86. abort_if(
  87. $timeStr < $startTime->format('H:i') || $timeStr > $endTime->format('H:i'),
  88. 400,
  89. sprintf(
  90. '服务时间需在%s-%s之间',
  91. $startTime->format('H:i'),
  92. $endTime->format('H:i')
  93. )
  94. );
  95. }
  96. private function checkTimeConflicts(int $coachId, string $serviceTime): void
  97. {
  98. $serviceDateTime = Carbon::parse($serviceTime);
  99. // 获取服务时长(默认2小时)
  100. $serviceDuration = config('business.default_service_duration', 120);
  101. // 计算服务结束时间
  102. $serviceEndTime = $serviceDateTime->copy()->addMinutes($serviceDuration);
  103. // 检查是否有其他订单在这个时间段
  104. $conflictOrder = Order::where('coach_id', $coachId)
  105. ->whereIn('state', [
  106. OrderStatus::PAID->value,
  107. OrderStatus::ACCEPTED->value,
  108. OrderStatus::SERVING->value,
  109. ])
  110. ->where(function ($query) use ($serviceDateTime, $serviceEndTime) {
  111. $query->where(function ($q) use ($serviceDateTime) {
  112. // 新订单开始时间在其他订单的服务时间段内
  113. $q->where('service_time', '<=', $serviceDateTime)
  114. ->where('service_end_time', '>', $serviceDateTime);
  115. })->orWhere(function ($q) use ($serviceEndTime) {
  116. // 新订单结束时间在其他订单的服务时间段内
  117. $q->where('service_time', '<', $serviceEndTime)
  118. ->where('service_end_time', '>=', $serviceEndTime);
  119. });
  120. })
  121. ->first();
  122. abort_if(
  123. $conflictOrder,
  124. 400,
  125. '该时间段技师已有其他订单'
  126. );
  127. }
  128. }