소스 검색

feat:技师端-拒单

刘学玺 4 달 전
부모
커밋
2339354a11
5개의 변경된 파일153개의 추가작업 그리고 2개의 파일을 삭제
  1. 10 2
      app/Enums/OrderRecordStatus.php
  2. 8 0
      app/Enums/OrderStatus.php
  3. 32 0
      app/Http/Controllers/Coach/OrderController.php
  4. 101 0
      app/Services/Coach/OrderService.php
  5. 2 0
      routes/api.php

+ 10 - 2
app/Enums/OrderRecordStatus.php

@@ -47,6 +47,11 @@ enum OrderRecordStatus: int
      */
     case CANCELLED = 8;
 
+    /**
+     * 记录状态:已拒单
+     */
+    case REJECTED = 9;
+
     /**
      * 获取状态的显示文本
      *
@@ -63,6 +68,7 @@ enum OrderRecordStatus: int
             self::COMPLETED => '服务完成',
             self::EVALUATED => '已评价',
             self::CANCELLED => '已取消',
+            self::REJECTED => '已拒单',
         };
     }
 
@@ -79,7 +85,7 @@ enum OrderRecordStatus: int
     /**
      * 检查当前状态是否与指定状态相同
      *
-     * @param self $status 要比较的状态
+     * @param  self  $status  要比较的状态
      * @return bool 如果状态相同返回 true,否则返回 false
      */
     public function is(self $status): bool
@@ -90,7 +96,7 @@ enum OrderRecordStatus: int
     /**
      * 根据整数值创建对应的状态枚举实例
      *
-     * @param int $value 状态值
+     * @param  int  $value  状态值
      * @return self|null 返回对应的状态枚举实例,如果值无效则返回 null
      */
     public static function fromValue(int $value): ?self
@@ -104,6 +110,7 @@ enum OrderRecordStatus: int
             self::COMPLETED->value => self::COMPLETED,
             self::EVALUATED->value => self::EVALUATED,
             self::CANCELLED->value => self::CANCELLED,
+            self::REJECTED->value => self::REJECTED,
             default => null
         };
     }
@@ -134,6 +141,7 @@ enum OrderRecordStatus: int
             self::COMPLETED->value => self::COMPLETED->label(),
             self::EVALUATED->value => self::EVALUATED->label(),
             self::CANCELLED->value => self::CANCELLED->label(),
+            self::REJECTED->value => self::REJECTED->label(),
         ];
     }
 }

+ 8 - 0
app/Enums/OrderStatus.php

@@ -82,6 +82,11 @@ enum OrderStatus: int
      */
     case COMMENTED = 15;
 
+    /**
+     * 订单状态:已拒单
+     */
+    case REJECTED = 16;
+
     /**
      * 获取状态的显示文本
      *
@@ -105,6 +110,7 @@ enum OrderStatus: int
             self::FINISHED => '服务结束',
             self::LEFT => '撤离',
             self::COMMENTED => '已评价',
+            self::REJECTED => '已拒单',
         };
     }
 
@@ -152,6 +158,7 @@ enum OrderStatus: int
             self::FINISHED->value => self::FINISHED,
             self::LEFT->value => self::LEFT,
             self::COMMENTED->value => self::COMMENTED,
+            self::REJECTED->value => self::REJECTED,
             default => null
         };
     }
@@ -188,6 +195,7 @@ enum OrderStatus: int
             self::FINISHED->value => self::FINISHED->label(),
             self::LEFT->value => self::LEFT->label(),
             self::COMMENTED->value => self::COMMENTED->label(),
+            self::REJECTED->value => self::REJECTED->label(),
         ];
     }
 }

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

@@ -157,4 +157,36 @@ class OrderController extends Controller
     {
         return $this->service->acceptOrder(Auth::user()->id, $order_id);
     }
+
+    /**
+     * [订单]拒单
+     *
+     * @description 技师拒绝已分配的订单
+     *
+     * @authenticated
+     *
+     * @urlParam order_id integer required 订单ID Example: 1
+     *
+     * @bodyParam reason string required 拒单原因(8-200个字符) Example: 距离太远,无法服务
+     *
+     * @response {
+     *   "message": "拒单成功",
+     *   "order_id": 1,
+     *   "order_no": "202403210001"
+     * }
+     * @response 400 {
+     *   "message": "订单状态异常,无法拒单"
+     * }
+     * @response 403 {
+     *   "message": "该订单未分配给您"
+     * }
+     */
+    public function rejectOrder(Request $request, int $order_id)
+    {
+        $data = $request->validate([
+            'reason' => 'required|string|min:8|max:200',
+        ]);
+
+        return $this->service->rejectOrder(Auth::user()->id, $order_id, $data['reason']);
+    }
 }

+ 101 - 0
app/Services/Coach/OrderService.php

@@ -3,15 +3,18 @@
 namespace App\Services\Coach;
 
 use App\Enums\OrderGrabRecordStatus;
+use App\Enums\OrderRecordStatus;
 use App\Enums\OrderStatus;
 use App\Enums\OrderType;
 use App\Enums\ProjectStatus;
 use App\Enums\TechnicianAuthStatus;
 use App\Enums\TechnicianLocationType;
 use App\Enums\TechnicianStatus;
+use App\Models\CoachUser;
 use App\Models\MemberUser;
 use App\Models\Order;
 use App\Models\OrderGrabRecord;
+use App\Models\OrderRecord;
 use App\Services\SettingItemService;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Log;
@@ -467,4 +470,102 @@ class OrderService
             }
         });
     }
+
+    /**
+     * 技师拒单
+     *
+     * @param  int  $userId  用户ID
+     * @param  int  $orderId  订单ID
+     * @param  string  $reason  拒单原因
+     */
+    public function rejectOrder(int $userId, int $orderId, string $reason): array
+    {
+        return DB::transaction(function () use ($userId, $orderId, $reason) {
+            try {
+                // 获取技师信息(优化关联加载)
+                $user = MemberUser::with([
+                    'coach',
+                    'coach.info',
+                    'coach.real',
+                    'coach.qual',
+                ])->findOrFail($userId);
+
+                // 验证技师信息
+                [$coach, $location] = $this->validateCoach($user);
+
+                // 获取订单信息并加锁
+                $order = Order::lockForUpdate()->findOrFail($orderId);
+
+                // 验证订单状态(修正状态判断)
+                abort_if(! in_array($order->state, [
+                    OrderStatus::ASSIGNED->value,
+                    OrderStatus::PAID->value,
+                ]), 400, '订单状态异常,无法拒单');
+
+                // 验证订单是否分配给该技师
+                abort_if($order->coach_id !== $coach->id, 403, '该订单未分配给您');
+
+                // 检查拒单次数限制
+                $rejectCount = OrderRecord::where('object_id', $coach->id)
+                    ->where('object_type', CoachUser::class)
+                    ->where('state', OrderRecordStatus::REJECTED->value)
+                    ->whereDate('created_at', today())
+                    ->count();
+
+                // 更新订单状态
+                $order->update([
+                    'state' => OrderStatus::REJECTED->value,
+                ]);
+
+                // 创建订单记录
+                OrderRecord::create([
+                    'order_id' => $order->id,
+                    'object_id' => $coach->id,
+                    'object_type' => CoachUser::class,
+                    'state' => OrderRecordStatus::REJECTED->value,
+                    'remark' => $reason,
+                ]);
+
+                // 发送消息通知
+                try {
+                    // event(new OrderRejectedEvent($order, $coach, $reason));
+                } catch (\Exception $e) {
+                    Log::error('发送拒单通知失败', [
+                        'order_id' => $orderId,
+                        'coach_id' => $coach->id,
+                        'error' => $e->getMessage(),
+                    ]);
+                }
+
+                // 记录日志
+                Log::info('技师拒单成功', [
+                    'user_id' => $userId,
+                    'coach_id' => $coach->id,
+                    'order_id' => $orderId,
+                    'order_no' => $order->order_no,
+                    'reason' => $reason,
+                    'reject_count' => $rejectCount + 1,
+                ]);
+
+                return [
+                    'message' => '拒单成功',
+                    'order_id' => $orderId,
+                    'order_no' => $order->order_no,
+                    'reject_count' => $rejectCount + 1,
+                    'max_reject_count' => 5,
+                ];
+
+            } catch (\Exception $e) {
+                Log::error('技师拒单失败', [
+                    'user_id' => $userId,
+                    'order_id' => $orderId,
+                    'reason' => $reason,
+                    'error' => $e->getMessage(),
+                    'file' => $e->getFile(),
+                    'line' => $e->getLine(),
+                ]);
+                throw $e;
+            }
+        });
+    }
 }

+ 2 - 0
routes/api.php

@@ -152,6 +152,8 @@ Route::middleware(['auth:sanctum', 'verified'])->prefix('coach')->group(function
         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']); // 限制拒单频率
     });
 
     // 项目相关路由