Browse Source

feat:技师端-出发

刘学玺 4 months ago
parent
commit
06960ed0a4

+ 8 - 0
app/Enums/OrderRecordStatus.php

@@ -52,6 +52,11 @@ enum OrderRecordStatus: int
      */
     case REJECTED = 9;
 
+    /**
+     * 记录状态:技师出发
+     */
+    case DEPARTED = 10;
+
     /**
      * 获取状态的显示文本
      *
@@ -69,6 +74,7 @@ enum OrderRecordStatus: int
             self::EVALUATED => '已评价',
             self::CANCELLED => '已取消',
             self::REJECTED => '已拒单',
+            self::DEPARTED => '技师出发',
         };
     }
 
@@ -111,6 +117,7 @@ enum OrderRecordStatus: int
             self::EVALUATED->value => self::EVALUATED,
             self::CANCELLED->value => self::CANCELLED,
             self::REJECTED->value => self::REJECTED,
+            self::DEPARTED->value => self::DEPARTED,
             default => null
         };
     }
@@ -142,6 +149,7 @@ enum OrderRecordStatus: int
             self::EVALUATED->value => self::EVALUATED->label(),
             self::CANCELLED->value => self::CANCELLED->label(),
             self::REJECTED->value => self::REJECTED->label(),
+            self::DEPARTED->value => self::DEPARTED->label(),
         ];
     }
 }

+ 1 - 0
app/Enums/TechnicianDepartureStatus.php

@@ -0,0 +1 @@
+ 

+ 54 - 0
app/Http/Controllers/Coach/OrderController.php

@@ -189,4 +189,58 @@ class OrderController extends Controller
 
         return $this->service->rejectOrder(Auth::user()->id, $order_id, $data['reason']);
     }
+
+    /**
+     * [订单]出发
+     *
+     * @description 技师确认出发前往服务地点
+     *
+     * @authenticated
+     *
+     * @urlParam order_id integer required 订单ID Example: 1
+     *
+     * @response {
+     *   "message": "已确认出发",
+     *   "order_id": 1,
+     *   "order_no": "202403210001",
+     *   "departed_at": "2024-03-21 10:00:00"
+     * }
+     * @response 400 {
+     *   "message": "订单状态异常,无法确认出发"
+     * }
+     * @response 403 {
+     *   "message": "该订单未分配给您"
+     * }
+     */
+    public function depart(int $order_id)
+    {
+        return $this->service->depart(Auth::user()->id, $order_id);
+    }
+
+    /**
+     * [订单]到达
+     *
+     * @description 技师确认已到达服务地点
+     *
+     * @authenticated
+     *
+     * @urlParam order_id integer required 订单ID Example: 1
+     *
+     * @response {
+     *   "message": "已确认到达",
+     *   "order_id": 1,
+     *   "order_no": "202403210001",
+     *   "arrived_at": "2024-03-21 10:30:00"
+     * }
+     * @response 400 {
+     *   "message": "订单状态异常,无法确认到达"
+     * }
+     * @response 403 {
+     *   "message": "该订单未分配给您"
+     * }
+     */
+    public function arrive(int $order_id)
+    {
+        return $this->service->arrive(Auth::user()->id, $order_id);
+    }
 }

+ 159 - 1
app/Services/Coach/OrderService.php

@@ -65,7 +65,12 @@ class OrderService
                 );
 
                 // 需要加载关联数据
-                $items = $paginator->items();
+                $items = collect($paginator->items())->map(function ($order) {
+                    $order->type_text = OrderType::from($order->type)->label();
+                    $order->state_text = OrderStatus::from($order->state)->label();
+
+                    return $order;
+                });
                 $query->with(['project']); // 加载项目信息以便返回project_name等字段
 
                 return [
@@ -568,4 +573,157 @@ class OrderService
             }
         });
     }
+
+    /**
+     * 技师出发
+     *
+     * @param  int  $userId  技师用户ID
+     * @param  int  $orderId  订单ID
+     */
+    public function depart(int $userId, int $orderId): array
+    {
+        try {
+            return DB::transaction(function () use ($userId, $orderId) {
+
+                // 获取技师信息
+                $user = MemberUser::with(['coach'])->findOrFail($userId);
+
+                // 获取订单信息
+                $order = Order::query()->where('id', $orderId)->lockForUpdate()->first();
+
+                // 检查订单是否存在
+                abort_if(! $order, 404, '订单不存在');
+
+                // 检查是否是该技师的订单
+                abort_if($order->coach_id !== $user->coach->id, 403, '无权操作此订单');
+
+                // 检查订单状态是否为已分配技师
+                abort_if($order->status !== OrderRecordStatus::ASSIGNED->value, 400, '订单状态不正确');
+
+                // 更新订单状态为技师出发
+                $order->status = OrderStatus::DEPARTED->value;
+                $order->save();
+
+                // 记录订单状态变更日志
+                OrderRecord::create([
+                    'order_id' => $orderId,
+                    'status' => OrderRecordStatus::DEPARTED->value,
+                    'operator_id' => $user->coach->id,
+                    'operator_type' => CoachUser::class,
+                    'remark' => '技师已出发',
+                ]);
+
+                // 发送通知给用户
+                // TODO: 发送通知
+                // event(new TechnicianDepartedEvent($order));
+
+                return [
+                    'status' => true,
+                    'message' => '操作成功',
+                    'data' => [
+                        'order_id' => $orderId,
+                        'status' => $order->status,
+                        'created_at' => $order->created_at,
+                    ],
+                ];
+            });
+        } catch (\Exception $e) {
+            \Log::error('技师出发失败', [
+                'user_id' => $userId,
+                'order_id' => $orderId,
+                'error' => $e->getMessage(),
+            ]);
+            throw $e;
+        }
+    }
+
+    /**
+     * 技师到达
+     *
+     * @param  int  $userId  技师用户ID
+     * @param  int  $orderId  订单ID
+     */
+    public function arrive(int $userId, int $orderId): array
+    {
+        return DB::transaction(function () use ($userId, $orderId) {
+            try {
+                // 获取技师信息
+                $user = MemberUser::with(['coach'])->findOrFail($userId);
+                $coach = $user->coach;
+                abort_if(! $coach, 404, '技师信息不存在');
+
+                // 获取订单信息
+                $order = Order::query()->where('id', $orderId)->lockForUpdate()->first();
+                abort_if(! $order, 404, '订单不存在');
+
+                // 检查是否是该技师的订单
+                abort_if($order->coach_id !== $coach->id, 403, '无权操作此订单');
+                dd(in_array($order->status, [
+                    OrderStatus::DEPARTED->value,
+                ]));
+                // 检查订单状态
+                abort_if(! in_array($order->status, [
+                    OrderStatus::DEPARTED->value,
+                ]), 400, '订单状态不正确');
+
+                $now = now();
+
+                // 更新订单状态为技师到达
+                $order->status = OrderRecordStatus::ARRIVED->value;
+                $order->save();
+
+                // 记录订单状态变更日志
+                OrderRecord::create([
+                    'order_id' => $orderId,
+                    'status' => OrderRecordStatus::ARRIVED->value,
+                    'operator_id' => $coach->id,
+                    'operator_type' => CoachUser::class,
+                    'remark' => '技师已到达',
+                ]);
+
+                // 更新技师当前位置到Redis GEO
+                try {
+                    Redis::geoadd(
+                        'coach_locations',
+                        $order->longitude,
+                        $order->latitude,
+                        $coach->id.'_'.TechnicianLocationType::CURRENT->value
+                    );
+                } catch (\Exception $e) {
+                    Log::error('更新技师位置失败', [
+                        'coach_id' => $coach->id,
+                        'order_id' => $orderId,
+                        'error' => $e->getMessage(),
+                    ]);
+                }
+
+                // TODO: 发送通知给用户
+                // event(new TechnicianArrivedEvent($order));
+
+                Log::info('技师到达成功', [
+                    'coach_id' => $coach->id,
+                    'order_id' => $orderId,
+                    'arrived_at' => $now,
+                ]);
+
+                return [
+                    'status' => true,
+                    'message' => '操作成功',
+                    'data' => [
+                        'order_id' => $orderId,
+                        'status' => $order->status,
+                        'arrived_at' => $now,
+                    ],
+                ];
+            } catch (\Exception $e) {
+                Log::error('技师到达失败', [
+                    'user_id' => $userId,
+                    'order_id' => $orderId,
+                    'error' => $e->getMessage(),
+                    'trace' => $e->getTraceAsString(),
+                ]);
+                throw $e;
+            }
+        });
+    }
 }

+ 10 - 1
routes/api.php

@@ -23,7 +23,7 @@ Route::get('/enums', [EnumController::class, 'getEnumData']);
 // 客户端路由组
 Route::prefix('client')->group(function () {
 
-    // 无需认证的开路由
+    // 无需认证的开路由
     Route::prefix('account')->group(function () {
         // 发验证码
         Route::post('send-code', [AccountController::class, 'sendVerifyCode']);
@@ -148,12 +148,21 @@ Route::middleware(['auth:sanctum', 'verified'])->prefix('coach')->group(function
 
     // 订单相关路由
     Route::prefix('orders')->group(function () {
+        // 获取技师订单列表
         Route::get('/', [CoachOrderController::class, 'getOrderList']);
+        // 获取可抢单列表
         Route::get('/grab', [CoachOrderController::class, 'getGrabList']);
+        // 抢单
         Route::post('/grab/{order_id}', [CoachOrderController::class, 'grabOrder']);
+        // 接受订单
         Route::post('/accept/{order_id}', [CoachOrderController::class, 'acceptOrder']);
+        // 拒绝订单
         Route::post('/reject/{order_id}', [CoachOrderController::class, 'rejectOrder'])
             ->middleware(['throttle:6,1']); // 限制拒单频率
+        // 技师出发
+        Route::post('/depart/{order_id}', [CoachOrderController::class, 'depart']);
+        // 技师到达
+        Route::post('/arrive/{order_id}', [CoachOrderController::class, 'arrive']);
     });
 
     // 项目相关路由