Просмотр исходного кода

Merge branch 'master' of ssh://gogs.yinbin.ink:30004/didong/owl-admin

Yin Bin 4 месяцев назад
Родитель
Сommit
e1c848d068

+ 1 - 1
app/Http/Controllers/Client/CoachController.php

@@ -58,7 +58,7 @@ class CoachController extends Controller
      *
      * @authenticated
      *
-     * @urlParam id int required 技师ID. Example: 1
+     * @urlParam id int required 技师ID. Example: 6
      *
      * @queryParam latitude float 纬度. Example: 34.0522
      * @queryParam longitude float 经度. Example: -118.2437

+ 34 - 4
app/Http/Controllers/Client/OrderController.php

@@ -31,6 +31,8 @@ class OrderController extends Controller
      * @bodyParam coach_id int required 技师ID. Example: 6
      * @bodyParam area_code string required 区划代码. Example: 370602
      * @bodyParam project_id int required 项目ID. Example: 1
+     * @bodyParam latitude int 纬度. Example: 37.4219983
+     * @bodyParam longitude int 经度. Example: 122.1347344
      *
      * @response {
      *   "status": "success",
@@ -39,7 +41,7 @@ class OrderController extends Controller
      */
     public function initialize(Request $request)
     {
-        $data = $request->only(['coach_id', 'area_code', 'project_id']);
+        $data = $request->only(['coach_id', 'area_code', 'project_id', 'latitude', 'longitude']);
 
         return $this->service->initialize(Auth::user()->id, $data);
     }
@@ -161,7 +163,7 @@ class OrderController extends Controller
      *
      * @authenticated
      *
-     * @urlParam id required 订单ID. Example: 1
+     * @urlParam id required 订单ID. Example: 6
      *
      * @response {
      *   "status": "success",
@@ -281,7 +283,8 @@ class OrderController extends Controller
      *
      * @bodyParam project_id int required 项目ID. Example: 1
      * @bodyParam use_balance boolean 使用余额. Example: false
-     * @bodyParam order_id int 订单ID. Example: 1
+     * @bodyParam order_id int 订单ID. Example: 15
+     * @bodyParam payment_type string 支付类型. Example: balance
      *
      * @response {
      *   "status": "success",
@@ -290,7 +293,7 @@ class OrderController extends Controller
      */
     public function addTime(Request $request)
     {
-        $data = $request->only(['project_id', 'use_balance', 'order_id']);
+        $data = $request->only(['project_id', 'use_balance', 'order_id', 'payment_type']);
 
         return $this->service->createOrder(Auth::user()->id, $data);
     }
@@ -316,4 +319,31 @@ class OrderController extends Controller
 
         return $this->service->assignCoach($userId, $orderId, $coachId);
     }
+
+    /**
+     * [订单]获取抢单列表
+     *
+     * 获取抢单列表
+     *
+     * @queryParam order_id int required 订单ID. Example: 7
+     *
+     * @response {
+     *  "data": [
+     *    {
+     *      "id": 1,
+     *      "coach_id": 1,
+     *      "nickname": "技师昵称",
+     *      "avatar": "头像地址",
+     *      "created_at": "2024-03-21 10:00:00"
+     *    }
+     *  ]
+     * }
+     */
+    public function getOrderGrabList(Request $request)
+    {
+
+        $orderId = $request->input('order_id');
+
+        return $this->service->getOrderGrabList($orderId);
+    }
 }

+ 5 - 5
app/Http/Controllers/Client/ProjectController.php

@@ -77,8 +77,7 @@ class ProjectController extends Controller
      *
      * @authenticated
      *
-     * @urlParam id integer required 项目ID. Example: 1
-     *
+     * @queryParam id integer required 项目ID. Example: 1
      * @queryParam area_code string required 区域代码. Example: 330100
      *
      * @response {
@@ -111,11 +110,12 @@ class ProjectController extends Controller
      *   "message": "该区域暂无代理商"
      * }
      */
-    public function detail(Request $request, $id)
+    public function detail(Request $request)
     {
+        $projectId = $request->input('id');
         $areaCode = $request->input('area_code');
 
-        return $this->service->getProjectDetail($id, $areaCode);
+        return $this->service->getProjectDetail($projectId, $areaCode);
     }
 
     /**
@@ -125,7 +125,7 @@ class ProjectController extends Controller
      *
      * @authenticated
      *
-     * @queryParam coach_id integer required 技师ID. Example: 1
+     * @queryParam coach_id integer required 技师ID. Example: 6
      * @queryParam area_code string required 区域代码. Example: 330100
      * @queryParam project_cate_id integer 项目分类ID. Example: 1
      *

+ 63 - 0
app/Http/Controllers/Coach/OrderController

@@ -0,0 +1,63 @@
+<?php
+
+namespace App\Http\Controllers\Client;
+
+use App\Http\Controllers\Controller;
+use App\Services\Client\OrderService;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+
+/**
+ * @group 技师端
+ *
+ * 订单相关的API接口
+ */
+class OrderController extends Controller
+{
+    // protected OrderService $service;
+
+    // public function __construct(OrderService $service)
+    // {
+    //     $this->service = $service;
+    // }
+
+    /**
+     * [订单]获取可抢订单列表
+     *
+     * 获取可抢订单列表
+     *
+     * @authenticated
+     *
+     * @queryParam area_code string required 区划代码. Example: 370602
+     * @queryParam page int 页码. Example: 1
+     * @queryParam per_page int 每页数量. Example: 10
+     *
+     * @response {
+     *   "data": [
+     *     {
+     *       "id": 1,
+     *       "order_no": "202403210001",
+     *       "project_name": "项目名称",
+     *       "project_duration": 60,
+     *       "project_price": "188.00",
+     *       "address": "山东省烟台市芝罘区",
+     *       "distance": 2.5,
+     *       "service_time": "2024-03-21 10:00:00",
+     *       "created_at": "2024-03-21 09:30:00"
+     *     }
+     *   ],
+     *   "meta": {
+     *     "total": 100,
+     *     "per_page": 10,
+     *     "current_page": 1,
+     *     "last_page": 10
+     *   }
+     * }
+     */
+    public function getGrabList(Request $request)
+    {
+        $params = $request->only(['area_code', 'page', 'per_page']);
+
+        return $this->service->getGrabList(Auth::user()->id, $params);
+    }
+}

+ 81 - 29
app/Services/Client/OrderService.php

@@ -2,6 +2,7 @@
 
 namespace App\Services\Client;
 
+use App\Enums\ProjectStatus;
 use App\Models\AgentConfig;
 use App\Models\AgentInfo;
 use App\Models\CoachConfig;
@@ -66,7 +67,6 @@ class OrderService
 
                 // 查询技师数据
                 $coach = $this->validateCoach($data['coach_id']);
-
                 // 获取项目详情
                 $project = $this->projectService->getProjectDetail($data['project_id'], $areaCode);
                 abort_if(! $project, 400, '项目不存在');
@@ -77,7 +77,10 @@ class OrderService
                     $address?->id ?? 0,
                     $data['coach_id'],
                     $data['project_id'],
-                    $project->agent_id
+                    $project->agent_id,
+                    false,
+                    $data['latitude'],
+                    $data['longitude']
                 );
 
                 return [
@@ -111,7 +114,7 @@ class OrderService
                 ->firstOrFail();
 
             $project = Project::where('id', $data['project_id'])
-                ->where('state', 'enable')
+                ->where('state', ProjectStatus::OPEN->value())
                 ->firstOrFail();
 
             // 2. 订单类型判断
@@ -119,25 +122,27 @@ class OrderService
 
             // 关键操作:验证必要参数
             abort_if(empty($data['project_id']), 400, '项目ID不能为空');
-            abort_if(empty($data['service_time']), 400, '服务时间不能为空');
             abort_if($orderType == 'normal' && empty($data['coach_id']), 400, '技师ID不能为空');
             abort_if($orderType == 'normal' && empty($data['address_id']), 400, '地址ID不能为空');
 
-            // 3. 验证地址
-            $address = $user->addresses()
-                ->where('id', $data['address_id'])
-                ->firstOrFail();
-
+            // 3. 验证技师
             // 4. 根据订单类型处理
             if ($orderType == 'normal') {
-                $coach = $this->validateCoach($data['coach_id']);
+                $this->validateCoach($data['coach_id']);
             } else {
                 $originalOrder = $this->getOriginalOrder($user, $data['order_id']);
-                $coach = $this->validateCoach($originalOrder->coach_id);
+
+                $data['address_id'] = $originalOrder->address_id;
+
+                $this->validateCoach($originalOrder->coach_id);
                 abort_if(! in_array($originalOrder->state, ['service_ing', 'service_end']), 400, '原订单状态不允许加钟');
                 $data = $this->prepareAddTimeData($originalOrder, $data);
             }
 
+            $address = $user->addresses()
+                ->where('id', $data['address_id'])
+                ->firstOrFail();
+
             // 5. 计算订单金额
             $amounts = $this->calculateOrderAmount(
                 $userId,
@@ -150,11 +155,11 @@ class OrderService
 
             // 6. 验证金额和余额
             abort_if($amounts['total_amount'] <= 0, 400, '订单金额异常');
+
             if ($data['payment_type'] == 'balance') {
                 $wallet = $user->wallet;
                 abort_if($wallet->available_balance < $amounts['balance_amount'], 400, '可用余额不足');
             }
-
             // 7. 创建订单记录
             $order = $this->createOrderRecord($userId, $data, $orderType, $address, $data['payment_type'], (object) $amounts);
 
@@ -175,9 +180,9 @@ class OrderService
     {
         $coach = CoachUser::where('id', $coachId)
             ->where('state', 'enable')
-            ->whereHas('info', fn($q) => $q->where('state', 'approved'))
-            ->whereHas('qual', fn($q) => $q->where('state', 'approved'))
-            ->whereHas('real', fn($q) => $q->where('state', 'approved'))
+            ->whereHas('info', fn ($q) => $q->where('state', 'approved'))
+            ->whereHas('qual', fn ($q) => $q->where('state', 'approved'))
+            ->whereHas('real', fn ($q) => $q->where('state', 'approved'))
             ->firstOrFail();
 
         return $coach;
@@ -730,10 +735,10 @@ class OrderService
         try {
             // 1. 校验技师
             $coach = CoachUser::where('state', 'enable')
-                ->whereHas('info', fn($q) => $q->where('state', 'approved'))
-                ->whereHas('real', fn($q) => $q->where('state', 'approved'))
-                ->whereHas('qual', fn($q) => $q->where('state', 'approved'))
-                ->with(['projects' => fn($q) => $q->where('project_id', $projectId)])
+                ->whereHas('info', fn ($q) => $q->where('state', 'approved'))
+                ->whereHas('real', fn ($q) => $q->where('state', 'approved'))
+                ->whereHas('qual', fn ($q) => $q->where('state', 'approved'))
+                ->with(['projects' => fn ($q) => $q->where('project_id', $projectId)])
                 ->find($coachId);
 
             abort_if(! $coach, 404, '技师不存在或状态异常');
@@ -761,7 +766,7 @@ class OrderService
                 ? bcmul($fee, '2', 2)
                 : $fee;
         } catch (Exception $e) {
-            Log::error(__CLASS__ . '->' . __FUNCTION__ . '计算路费失败:', [
+            Log::error(__CLASS__.'->'.__FUNCTION__.'计算路费失败:', [
                 'message' => $e->getMessage(),
                 'coach_id' => $coachId,
                 'project_id' => $projectId,
@@ -820,6 +825,8 @@ class OrderService
      * @param  int  $agentId  代理商ID
      * @param  bool  $useBalance  是否使用余额
      * @param  float  $distance  距离
+     * @param  int  $lat  纬度
+     * @param  int  $lng  经度
      *
      * @throws Exception
      */
@@ -830,9 +837,12 @@ class OrderService
         int $projectId,
         ?int $agentId = null,
         bool $useBalance = false,
-        float $distance = 0
+        float $distance = 0,
+        int $lat = 0,
+        int $lng = 0
     ): array {
         try {
+
             // 1. 参数校验
             $user = MemberUser::find($userId);
             abort_if(! $user || $user->state != 'enable', 404, '用户不存在或状态异常');
@@ -841,26 +851,27 @@ class OrderService
             $coach = $this->validateCoach($coachId);
 
             abort_if(! $coach, 404, '技师不存在或状态异常');
-
             $coachProject = $coach->projects()
                 ->where('state', 'enable')
                 ->where('project_id', $projectId)
                 ->first();
 
             abort_if(! $coachProject, 404, '技师项目不存在');
-
             // 3. 查询基础项目
             $project = Project::where('id', $projectId)
-                ->where('state', 'enable')
+                ->where('state', ProjectStatus::OPEN->value())
                 ->first();
 
             abort_if(! $project, 404, '项目不存在或状态异常');
 
             // 4. 计算距离
-            if ($distance <= 0) {
-                $address = $user->addresses()->findOrFail($addressId);
+            if (floatval($distance) <= 0) {
+
+                $address = $user->addresses()->find($addressId) ?? ['latitude' => $lat, 'longitude' => $lng];
+
                 $coachService = app(CoachService::class);
-                $coachDetail = $coachService->getCoachDetail($coachId, $address->latitude, $address->longitude);
+
+                $coachDetail = $coachService->getCoachDetail($coachId, $address['latitude'], $address['longitude']);
                 $distance = $coachDetail['distance'] ?? 0;
             }
 
@@ -904,7 +915,7 @@ class OrderService
                 'delivery_fee' => $deliveryFee,
             ];
         } catch (Exception $e) {
-            Log::error(__CLASS__ . '->' . __FUNCTION__ . '计算订单金额失败:', [
+            Log::error(__CLASS__.'->'.__FUNCTION__.'计算订单金额失败:', [
                 'message' => $e->getMessage(),
                 'user_id' => $userId,
                 'project_id' => $projectId,
@@ -977,6 +988,47 @@ class OrderService
         return [$balanceAmount, $payAmount];
     }
 
+    /**
+     * 获取订单抢单池列表
+     *
+     * @param  int  $orderId  订单ID
+     * @return array 抢单池列表
+     */
+    public function getOrderGrabList(int $orderId): array
+    {
+
+        try {
+            // 查询订单信息
+            $order = Order::where('id', $orderId)
+                ->whereIn('state', ['wait_pay', 'wait_service'])
+                ->firstOrFail();
+            // 查询抢单池列表
+            $grabList = $order->grabRecords()->with(['coach.info'])->get();
+
+            // 格式化返回数据
+            $result = [];
+            foreach ($grabList as $grab) {
+                $coach = $grab->coach;
+                $result[] = [
+                    'id' => $grab->id,
+                    'coach_id' => $coach->id,
+                    'nickname' => $coach->info->nickname,
+                    'avatar' => $coach->info->avatar,
+                    'created_at' => $grab->created_at->format('Y-m-d H:i:s'),
+                ];
+            }
+
+            return $result;
+
+        } catch (\Exception $e) {
+            Log::error('获取订单抢单池列表失败', [
+                'error' => $e->getMessage(),
+                'order_id' => $orderId,
+            ]);
+            throw $e;
+        }
+    }
+
     /**
      * 指定技师
      */
@@ -1044,7 +1096,7 @@ class OrderService
         WalletPaymentRecord::create([
             'order_id' => $order->id,
             'wallet_id' => $wallet->id,
-            'payment_no' => 'balance_' . $order->id,
+            'payment_no' => 'balance_'.$order->id,
             'payment_method' => 'balance',
             'total_amount' => $order->balance_amount,
             'actual_amount' => 0,

+ 2 - 1
app/Services/Client/ProjectService.php

@@ -90,8 +90,9 @@ class ProjectService
      */
     public function getProjectDetail($projectId, $areaCode)
     {
+
         // 查询系统项目
-        $project = Project::where('state', 'enable')->find($projectId);
+        $project = Project::where('state', ProjectStatus::OPEN->value())->find($projectId);
         abort_if(! $project, 404, '项目不存在');
 
         // 根据区域代码获取代理商

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

@@ -0,0 +1,18 @@
+<?php
+
+namespace App\Services\Client;
+
+class OrderService
+{
+    // protected AgentService $agentService;
+
+    // protected ProjectService $projectService;
+
+    // public function __construct(
+    //     AgentService $agentService,
+    //     ProjectService $projectService
+    // ) {
+    //     $this->agentService = $agentService;
+    //     $this->projectService = $projectService;
+    // }
+}

+ 2 - 1
routes/api.php

@@ -60,7 +60,7 @@ Route::middleware('auth:sanctum')->group(function () {
     // 项目相关
     Route::prefix('project')->group(function () {
         Route::get('/', [ProjectController::class, 'index']); // 获取项目列表
-        Route::get('/{id}/detail', [ProjectController::class, 'detail']); // 获取项目详情
+        Route::get('/detail', [ProjectController::class, 'detail']); // 获取项目详情
         Route::get('/coach-list', [ProjectController::class, 'coachProjectList']); // 获取技师开通的项目列表
     });
 
@@ -100,6 +100,7 @@ Route::middleware('auth:sanctum')->group(function () {
         // Route::post('get-agent-config', [OrderController::class, 'getAgentConfig']);
         // Route::post('get-coach-config', [OrderController::class, 'getCoachConfig']);
         // Route::post('calculate-delivery-fee', [OrderController::class, 'calculateDeliveryFee']);
+        Route::get('grab-list', [OrderController::class, 'getOrderGrabList']);
     });
 
     // 钱包相关