|
@@ -2,8 +2,10 @@
|
|
|
|
|
|
namespace App\Services\Coach;
|
|
|
|
|
|
+use App\Enums\OrderStatus;
|
|
|
use App\Enums\TechnicianAuthStatus;
|
|
|
use App\Enums\TechnicianLocationType;
|
|
|
+use App\Enums\TechnicianWorkStatus;
|
|
|
use App\Models\CoachSchedule;
|
|
|
use App\Models\MemberUser;
|
|
|
use Illuminate\Support\Facades\Cache;
|
|
@@ -465,7 +467,7 @@ class AccountService
|
|
|
];
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
- Log::error('技师排班设置失败', [
|
|
|
+ Log::error('技师排班设置���败', [
|
|
|
'user_id' => $userId,
|
|
|
'time_ranges' => $timeRanges,
|
|
|
'error' => $e->getMessage(),
|
|
@@ -492,7 +494,7 @@ class AccountService
|
|
|
// 验证时间格式
|
|
|
foreach (['start_time', 'end_time'] as $field) {
|
|
|
abort_if(! preg_match('/^([01][0-9]|2[0-3]):[0-5][0-9]$/', $range[$field]),
|
|
|
- 400, '时间格式错误,应为HH:mm格式');
|
|
|
+ 400, '时间格式错,应为HH:mm格式');
|
|
|
}
|
|
|
|
|
|
// 转换为分钟数便于比较
|
|
@@ -586,4 +588,208 @@ class AccountService
|
|
|
]);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更改技师工作状态
|
|
|
+ *
|
|
|
+ * @param int $userId 用户ID
|
|
|
+ * @param int $status 状态(1:休息中 2:工作中)
|
|
|
+ */
|
|
|
+ public function updateWorkStatus(int $userId, int $status): array
|
|
|
+ {
|
|
|
+ DB::beginTransaction();
|
|
|
+ try {
|
|
|
+ // 获取技师信息
|
|
|
+ $user = MemberUser::with(['coach', 'coach.infoRecords', 'coach.qualRecords', 'coach.realRecords'])
|
|
|
+ ->findOrFail($userId);
|
|
|
+ $coach = $user->coach;
|
|
|
+ abort_if(! $coach, 404, '技师信息不存在');
|
|
|
+
|
|
|
+ // 验证状态值
|
|
|
+ abort_if(! in_array($status, [1, 2]), 400, '无效的状态值');
|
|
|
+
|
|
|
+ // 验证技师认证状态
|
|
|
+ $this->validateCoachStatus($coach);
|
|
|
+
|
|
|
+ // 获取当前时间是否在排班时间内
|
|
|
+ $isInSchedule = $this->checkScheduleTime($coach->id);
|
|
|
+
|
|
|
+ $currentStatus = $coach->work_status;
|
|
|
+ $newStatus = $status;
|
|
|
+
|
|
|
+ // 如果要切换到休息状态
|
|
|
+ if ($status === 1) {
|
|
|
+ // 验证当前状态是否允许切换到休息
|
|
|
+ $this->validateRestStatus($currentStatus);
|
|
|
+ $newStatus = TechnicianWorkStatus::REST->value;
|
|
|
+ }
|
|
|
+ // 如果要切换到工作状态
|
|
|
+ elseif ($status === 2) {
|
|
|
+ // 验证是否在排班时间内
|
|
|
+ abort_if(! $isInSchedule, 422, '当前时间不在排班时间内,无法切换到工作状态');
|
|
|
+
|
|
|
+ // 检查是否有进行中的订单
|
|
|
+ $hasActiveOrder = $coach->orders()
|
|
|
+ ->whereIn('state', [
|
|
|
+ OrderStatus::ACCEPTED->value, // 已接单
|
|
|
+ OrderStatus::DEPARTED->value, // 已出发
|
|
|
+ OrderStatus::ARRIVED->value, // 已到达
|
|
|
+ OrderStatus::SERVING->value, // 服务中
|
|
|
+ ])
|
|
|
+ ->exists();
|
|
|
+
|
|
|
+ // 根据是否有进行中订单决定状态
|
|
|
+ $newStatus = $hasActiveOrder ?
|
|
|
+ TechnicianWorkStatus::BUSY->value :
|
|
|
+ TechnicianWorkStatus::FREE->value;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果状态没有变化,则不需要更新
|
|
|
+ if ($currentStatus === $newStatus) {
|
|
|
+ DB::rollBack();
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'status' => true,
|
|
|
+ 'message' => '状态未发生变化',
|
|
|
+ 'data' => [
|
|
|
+ 'work_status' => $newStatus,
|
|
|
+ 'work_status_text' => TechnicianWorkStatus::fromValue($newStatus)->label(),
|
|
|
+ 'updated_at' => now()->toDateTimeString(),
|
|
|
+ ],
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新状态
|
|
|
+ $coach->work_status = $newStatus;
|
|
|
+ $coach->save();
|
|
|
+
|
|
|
+ // 更新Redis缓存
|
|
|
+ $this->updateWorkStatusCache($coach->id, $newStatus);
|
|
|
+
|
|
|
+ DB::commit();
|
|
|
+
|
|
|
+ // 记录日志
|
|
|
+ Log::info('技师工作状态更新成功', [
|
|
|
+ 'coach_id' => $coach->id,
|
|
|
+ 'old_status' => $currentStatus,
|
|
|
+ 'new_status' => $newStatus,
|
|
|
+ 'updated_at' => now()->toDateTimeString(),
|
|
|
+ 'in_schedule' => $isInSchedule,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'status' => true,
|
|
|
+ 'message' => '状态更新成功',
|
|
|
+ 'data' => [
|
|
|
+ 'work_status' => $newStatus,
|
|
|
+ 'work_status_text' => TechnicianWorkStatus::fromValue($newStatus)->label(),
|
|
|
+ 'updated_at' => now()->toDateTimeString(),
|
|
|
+ ],
|
|
|
+ ];
|
|
|
+
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ DB::rollBack();
|
|
|
+ Log::error('技师工作状态更新失败', [
|
|
|
+ 'user_id' => $userId,
|
|
|
+ 'status' => $status,
|
|
|
+ 'error' => $e->getMessage(),
|
|
|
+ 'trace' => $e->getTraceAsString(),
|
|
|
+ ]);
|
|
|
+ throw $e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证技师认证状态
|
|
|
+ */
|
|
|
+ private function validateCoachStatus($coach): void
|
|
|
+ {
|
|
|
+ // 验证基本信息认证
|
|
|
+ $baseInfo = $coach->info;
|
|
|
+ abort_if(! $baseInfo || $baseInfo->state !== TechnicianAuthStatus::PASSED->value,
|
|
|
+ 422, '基本信息未认证通过');
|
|
|
+
|
|
|
+ // 验证资质认证
|
|
|
+ $qualification = $coach->qual;
|
|
|
+ abort_if(! $qualification || $qualification->state !== TechnicianAuthStatus::PASSED->value,
|
|
|
+ 422, '资质信息未认证通过');
|
|
|
+
|
|
|
+ // 验证实名认证
|
|
|
+ $realName = $coach->real;
|
|
|
+ abort_if(! $realName || $realName->state !== TechnicianAuthStatus::PASSED->value,
|
|
|
+ 422, '实名信息未认证通过');
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 验证是否可以切换到休息状态
|
|
|
+ */
|
|
|
+ private function validateRestStatus(int $currentStatus): void
|
|
|
+ {
|
|
|
+ // 只有在空闲或忙碌状态下才能更改为休息状态
|
|
|
+ abort_if(! in_array($currentStatus, [
|
|
|
+ TechnicianWorkStatus::FREE->value,
|
|
|
+ TechnicianWorkStatus::BUSY->value,
|
|
|
+ ]), 422, '当前状态不能更改为休息状态');
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查当前时间是否在排班时间内
|
|
|
+ */
|
|
|
+ private function checkScheduleTime(int $coachId): bool
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ $schedule = CoachSchedule::where('coach_id', $coachId)
|
|
|
+ ->where('state', 1)
|
|
|
+ ->first();
|
|
|
+
|
|
|
+ if (! $schedule) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ $timeRanges = json_decode($schedule->time_ranges, true);
|
|
|
+ if (empty($timeRanges)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ $currentTime = now()->format('H:i');
|
|
|
+
|
|
|
+ foreach ($timeRanges as $range) {
|
|
|
+ if ($currentTime >= $range['start_time'] && $currentTime <= $range['end_time']) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ Log::error('检查排班时间异常', [
|
|
|
+ 'coach_id' => $coachId,
|
|
|
+ 'error' => $e->getMessage(),
|
|
|
+ ]);
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新工作状态缓存
|
|
|
+ */
|
|
|
+ private function updateWorkStatusCache(int $coachId, int $status): void
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ $cacheKey = "coach:work_status:{$coachId}";
|
|
|
+ $cacheData = [
|
|
|
+ 'status' => $status,
|
|
|
+ 'updated_at' => now()->toDateTimeString(),
|
|
|
+ ];
|
|
|
+
|
|
|
+ Redis::setex($cacheKey, 86400, json_encode($cacheData));
|
|
|
+
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ Log::error('更新工作状态缓存失败', [
|
|
|
+ 'coach_id' => $coachId,
|
|
|
+ 'error' => $e->getMessage(),
|
|
|
+ ]);
|
|
|
+ // 缓存更新失败不影响主流程
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|