Ver Fonte

fixed:优化订单和服务项目业务逻辑

刘学玺 há 4 meses atrás
pai
commit
accdf325db
2 ficheiros alterados com 269 adições e 130 exclusões
  1. 260 125
      app/Services/Client/OrderService.php
  2. 9 5
      app/Services/Client/ProjectService.php

+ 260 - 125
app/Services/Client/OrderService.php

@@ -5,9 +5,7 @@ namespace App\Services\Client;
 use App\Models\AgentConfig;
 use App\Models\AgentInfo;
 use App\Models\CoachConfig;
-use App\Models\CoachSchedule;
 use App\Models\CoachUser;
-use App\Models\Coupon;
 use App\Models\MemberUser;
 use App\Models\Order;
 use App\Models\OrderGrabRecord;
@@ -40,68 +38,66 @@ class OrderService
      */
     public function initialize(int $userId, array $data): array
     {
-        $user = MemberUser::find($userId);
+        try {
+            // 参数验证
+            abort_if(empty($data), 400, '订单初始化参数不能为空');
 
-        if (! $user) {
-            throw new Exception('用户不存在');
-        }
+            DB::beginTransaction();
+            try {
+                $user = MemberUser::find($userId);
+                abort_if(! $user || $user->state != 'enable', 400, '用户状态异常');
 
-        if ($user->state != 'enable') {
-            throw new Exception('用户状态异常');
-        }
+                // 查询用户钱包
+                $wallet = $user->wallet;
 
-        // 查询用户钱包
-        $wallet = $user->wallet;
-
-        // 查询默认地址
-        $address = $user->address;
-
-        $areaCode = $address ? $address->area_code : $data['area_code'];
-
-        // 查询技师数据
-        $coach = CoachUser::where('state', 'enable')
-            ->whereHas('info', function ($query) {
-                $query->where('state', 'approved');
-            })
-            ->whereHas('real', function ($query) {
-                $query->where('state', 'approved');
-            })
-            ->whereHas('qual', function ($query) {
-                $query->where('state', 'approved');
-            })
-            ->with(['info:id,nickname,avatar,gender'])
-            ->find($data['coach_id']);
-
-        if (! $coach) {
-            throw new Exception('技师不存在');
-        }
+                // 查询默认地址
+                $address = $user->address;
 
-        // 查询技师排班
-        // $schedule = CoachSchedule::where('coach_id', $coachId)
-        //     ->where('date', date('Y-m-d'))
-        //     ->first();
+                $areaCode = $address ? $address->area_code : $data['area_code'];
 
-        // // 查询用户优惠券
-        // $coupons = Coupon::where('user_id', $userId)
-        //     ->where('state', 'enable')
-        //     ->where('expire_time', '>', now())
-        //     ->get();
+                // 查询技师数据
+                $coach = $this->validateCoach($data['coach_id']);
 
-        // 获取项目详情
-        $project = $this->projectService->getProjectDetail($data['project_id'], $areaCode);
+                // 查询技师排班
+                // $schedule = CoachSchedule::where('coach_id', $coachId)
+                //     ->where('date', date('Y-m-d'))
+                //     ->first();
 
-        // 计算订单金额
-        $amounts = $this->calculateOrderAmount($userId, $address?->id, $data['coach_id'], $data['project_id'], $project?->agent_id);
+                // // 查询用户优惠券
+                // $coupons = Coupon::where('user_id', $userId)
+                //     ->where('state', 'enable')
+                //     ->where('expire_time', '>', now())
+                //     ->get();
 
-        return [
-            'wallet' => $wallet,
-            'coach' => $coach,
-            'project' => $project,
-            'address' => $address,
-            // 'schedule' => $schedule,
-            'amounts' => $amounts,
-            // 'coupons' => $coupons
-        ];
+                // 获取项目详情
+                $project = $this->projectService->getProjectDetail($data['project_id'], $areaCode);
+
+                // 计算订单金额
+                $amounts = $this->calculateOrderAmount($userId, $address?->id, $data['coach_id'], $data['project_id'], $project?->agent_id);
+
+                DB::commit();
+
+                return [
+                    'wallet' => $wallet,
+                    'coach' => $coach,
+                    'project' => $project,
+                    'address' => $address,
+                    // 'schedule' => $schedule,
+                    'amounts' => $amounts,
+                    // 'coupons' => $coupons
+                ];
+            } catch (\Exception $e) {
+                DB::rollBack();
+                throw $e;
+            }
+        } catch (\Exception $e) {
+            Log::error('订单初始化失败', [
+                'userId' => $userId,
+                'data' => $data,
+                'error' => $e->getMessage(),
+            ]);
+            throw $e;
+        }
     }
 
     /**
@@ -123,10 +119,25 @@ class OrderService
                 // 2. 创建订单
                 $orderType = isset($data['order_id']) ? 'add_time' : 'normal';
 
+                // 关键操作:验证必要参数
+                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不能为空');
+
                 if ($orderType == 'normal') {
+                    // 普通订单验证技师
                     $coach = $this->validateCoach($data['coach_id']);
                 } else {
+                    // 加钟订单验证
                     $originalOrder = $this->getOriginalOrder($user, $data['order_id']);
+
+                    // 关键操作:验证原订单技师状态
+                    $coach = $this->validateCoach($originalOrder->coach_id);
+
+                    // 关键操作:验证原订单状态
+                    abort_if(! in_array($originalOrder->state, ['service_ing', 'service_end']), 400, '原订单状态不允许加钟');
+
                     $data = $this->prepareAddTimeData($originalOrder, $data);
                 }
 
@@ -137,6 +148,15 @@ class OrderService
                 // 计算订单金额
                 $amounts = $this->calculateOrderAmount($userId, $address->id, $data['coach_id'], $data['project_id'], $project->agent_id, $data['use_balance'] ?? false);
 
+                // 关键操作:验证订单金额
+                abort_if($amounts['total_amount'] <= 0, 400, '订单金额异常');
+
+                // 关键操作:验证余额支付
+                if ($amounts['payment_type'] == 'balance') {
+                    $wallet = $user->wallet;
+                    abort_if($wallet->available_balance < $amounts['balance_amount'], 400, '可用余额不足');
+                }
+
                 // 创建订单记录
                 $order = $this->createOrderRecord($userId, $data, $orderType, $address, $amounts);
 
@@ -161,7 +181,7 @@ class OrderService
     }
 
     // 提取方法:验证技师
-    private function validateCoach($coachId): CoachUser
+    public function validateCoach(int $coachId): CoachUser
     {
         $coach = CoachUser::where('id', $coachId)
             ->where('state', 'enable')
@@ -263,53 +283,99 @@ class OrderService
     {
         return DB::transaction(function () use ($userId, $orderId) {
             try {
-                $user = MemberUser::where('id', $userId)->firstOrFail();
-                $order = $user->orders()->find($orderId);
+                // 1. 验证用户和订单
+                $order = $this->validateOrderForCancel($userId, $orderId);
 
-                if (! $order) {
-                    throw new Exception('订单不存在');
-                }
+                // 2. 处理退款
+                $this->handleCancelRefund($order);
 
-                // 判断订单状态
-                if ($order->state == 'wait_receive') { // 已接单
-                    // 扣除20%费用
-                    $deductAmount = ($order->payment_amount + $order->balance_amount - $order->traffic_amount) * 0.2;
-                    $this->handleRefund($user, $order, $deductAmount, false);
-                } elseif ($order->state == 'on_the_way') { // 已出发
-                    // 扣除50%费用并扣除路费
-                    $deductAmount = ($order->payment_amount + $order->balance_amount - $order->traffic_amount) * 0.5;
-                    $this->handleRefund($user, $order, $deductAmount, true);
-                } elseif ($order->state == 'wait_pay') {
-                    // 待支付状态直接取消,无需退款
-                } else {
-                    throw new Exception('当前订单状态不允许取消');
-                }
-
-                // 添加订单取消记录
-                OrderRecord::create([
-                    'order_id' => $orderId,
-                    'object_id' => $userId,
-                    'object_type' => MemberUser::class,
-                    'state' => 'cancel',
-                    'remark' => '用户取消订单',
-                ]);
-
-                // 修改订单状态
-                $order->state = 'cancel';
-                $order->save();
+                // 3. 完成订单取消
+                $this->completeCancel($order, $userId);
 
                 return ['message' => '订单已取消'];
+
             } catch (Exception $e) {
-                Log::error('取消订单失败:', [
-                    'message' => $e->getMessage(),
-                    'user_id' => $userId,
-                    'order_id' => $orderId,
-                ]);
+                $this->logCancelOrderError($e, $userId, $orderId);
                 throw $e;
             }
         });
     }
 
+    /**
+     * 验证订单取消条件
+     */
+    private function validateOrderForCancel(int $userId, int $orderId): Order
+    {
+        // 复用之前的用户验证逻辑
+        $user = MemberUser::where('id', $userId)
+            ->where('state', 'enable')
+            ->firstOrFail();
+
+        // 验证订单状态
+        $order = Order::where('user_id', $userId)
+            ->where('id', $orderId)
+            ->whereIn('state', ['wait_pay', 'wait_receive', 'on_the_way'])
+            ->lockForUpdate()
+            ->firstOrFail();
+
+        return $order;
+    }
+
+    /**
+     * 处理订单取消退款
+     */
+    private function handleCancelRefund(Order $order): void
+    {
+        $user = $order->user;
+
+        switch ($order->state) {
+            case 'wait_receive': // 已接单
+                // 扣除20%费用
+                $deductAmount = ($order->payment_amount + $order->balance_amount - $order->traffic_amount) * 0.2;
+                $this->handleRefund($user, $order, $deductAmount, false);
+                break;
+
+            case 'on_the_way': // 已出发
+                // 扣除50%费用并扣除路费
+                $deductAmount = ($order->payment_amount + $order->balance_amount - $order->traffic_amount) * 0.5;
+                $this->handleRefund($user, $order, $deductAmount, true);
+                break;
+
+            case 'wait_pay':
+                // 待支付状态直接取消,无需退款
+                break;
+
+            default:
+                abort(400, '当前订单状态不允许取消');
+        }
+    }
+
+    /**
+     * 完成订单取消
+     */
+    private function completeCancel(Order $order, int $userId): void
+    {
+        // 添加订单取消记录
+        OrderRecord::create([
+            'order_id' => $order->id,
+            'object_id' => $userId,
+            'object_type' => MemberUser::class,
+            'state' => 'cancel',
+            'remark' => '用户取消订单',
+        ]);
+
+        // 修改订单状态
+        $order->state = 'cancel';
+        $order->cancel_time = now(); // 添加取消时间
+        $order->save();
+
+        // 如果有技师,可能需要通知技师订单已取消
+        if ($order->coach_id) {
+            // TODO: 发送通知给技师
+            // event(new OrderCancelledEvent($order));
+        }
+    }
+
     /**
      * 处理退款
      */
@@ -342,12 +408,15 @@ class OrderService
         }
     }
 
-    // 提取方法:创建退款记录
+    /**
+     * 创建退款记录
+     */
     private function createRefundRecords($user, $order, $amount, $type = 'balance'): void
     {
         $refundMethod = $type;
         $remark = $type == 'balance' ? '订单取消退还余额' : '订单取消退还支付金额';
 
+        // 创建退款记录
         $refundRecord = $user->wallet->refundRecords()->create([
             'refund_method' => $refundMethod,
             'total_refund_amount' => $order->payment_amount + $order->balance_amount,
@@ -358,6 +427,7 @@ class OrderService
             'order_id' => $order->id,
         ]);
 
+        // 创建交易记录
         $user->wallet->transRecords()->create([
             'amount' => $amount,
             'owner_type' => get_class($refundRecord),
@@ -373,11 +443,26 @@ class OrderService
             'state' => 'success',
         ]);
 
+        // 更新钱包余额
         $user->wallet->increment('total_balance', $amount);
         $user->wallet->increment('available_balance', $amount);
         $user->wallet->save();
     }
 
+    /**
+     * 记录订单取消错误日志
+     */
+    private function logCancelOrderError(Exception $e, int $userId, int $orderId): void
+    {
+        // 复用之前的日志记录方法
+        Log::error('取消订单失败:', [
+            'message' => $e->getMessage(),
+            'user_id' => $userId,
+            'order_id' => $orderId,
+            'trace' => $e->getTraceAsString(),
+        ]);
+    }
+
     /**
      * 结束订单
      */
@@ -385,41 +470,95 @@ class OrderService
     {
         return DB::transaction(function () use ($userId, $orderId) {
             try {
-                // 关键操作:参数校验
-                $order = Order::where('user_id', $userId)
-                    ->where('id', $orderId)
-                    ->where('state', 'service_ing') // 订单状态必须是服务中
-                    ->firstOrFail();
+                // 1. 验证用户和订单
+                $order = $this->validateOrderForFinish($userId, $orderId);
 
-                if (! $order) {
-                    throw new Exception('订单不能结束');
-                }
+                // 2. 验证技师状态
+                $this->validateCoach($order->coach_id);
 
-                // 关键操作:创建订单历史记录
-                OrderRecord::create([
-                    'order_id' => $orderId,
-                    'object_id' => $userId,
-                    'object_type' => MemberUser::class,
-                    'state' => 'finish',
-                    'remark' => '服务完成',
-                ]);
+                // 3. 验证服务时长
+                $this->validateServiceDuration($order);
 
-                // 关键操作:修改订单状态为服务结束
-                $order->state = 'service_end';
-                $order->save();
+                // 4. 完成订单
+                $this->completeOrder($order, $userId);
 
                 return ['message' => '订单已完成'];
+
             } catch (Exception $e) {
-                Log::error('结束订单失败:', [
-                    'message' => $e->getMessage(),
-                    'user_id' => $userId,
-                    'order_id' => $orderId,
-                ]);
+                $this->logFinishOrderError($e, $userId, $orderId);
                 throw $e;
             }
         });
     }
 
+    /**
+     * 验证订单完成条件
+     */
+    private function validateOrderForFinish(int $userId, int $orderId): Order
+    {
+        // 验证用户状态
+        $user = MemberUser::where('id', $userId)
+            ->where('state', 'enable')
+            ->firstOrFail();
+
+        // 验证订单状态
+        $order = Order::where('user_id', $userId)
+            ->where('id', $orderId)
+            ->where('state', 'service_ing')
+            ->lockForUpdate()
+            ->firstOrFail();
+
+        return $order;
+    }
+
+    /**
+     * 验证服务时长
+     */
+    private function validateServiceDuration(Order $order): void
+    {
+        // 计算服务时长
+        $serviceStartTime = $order->service_start_time ?? $order->created_at;
+        $serviceDuration = now()->diffInMinutes($serviceStartTime);
+
+        // 获取项目要求的最短服务时长
+        $minDuration = $order->project->duration ?? 0;
+
+        abort_if($serviceDuration < $minDuration, 400, "服务时长不足{$minDuration}分钟");
+    }
+
+    /**
+     * 完成订单
+     */
+    private function completeOrder(Order $order, int $userId): void
+    {
+        // 1. 创建订单记录
+        OrderRecord::create([
+            'order_id' => $order->id,
+            'object_id' => $userId,
+            'object_type' => MemberUser::class,
+            'state' => 'finish',
+            'remark' => '服务完成',
+        ]);
+
+        // 2. 更新订单状态
+        $order->state = 'service_end';
+        $order->finish_time = now();
+        $order->save();
+    }
+
+    /**
+     * 记录订单完成错误日志
+     */
+    private function logFinishOrderError(Exception $e, int $userId, int $orderId): void
+    {
+        Log::error('结束订单失败:', [
+            'message' => $e->getMessage(),
+            'user_id' => $userId,
+            'order_id' => $orderId,
+            'trace' => $e->getTraceAsString(),
+        ]);
+    }
+
     /**
      * 确认技师离开
      */
@@ -450,7 +589,7 @@ class OrderService
                 $order->state = 'leave';
                 $order->save();
 
-                return ['message' => '已确技师离开'];
+                return ['message' => '已确技师离开'];
             } catch (Exception $e) {
                 Log::error('确认技师离开失败:', [
                     'message' => $e->getMessage(),
@@ -699,11 +838,7 @@ class OrderService
             abort_if(! $user || $user->state != 'enable', 404, '用户不存在或状态异常');
 
             // 2. 查询技师项目
-            $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'))
-                ->find($coachId);
+            $coach = $this->validateCoach($coachId);
 
             abort_if(! $coach, 404, '技师不存在或状态异常');
 

+ 9 - 5
app/Services/Client/ProjectService.php

@@ -17,14 +17,18 @@ class ProjectService
         // 根据区域代码获取代理商
         $agent = $this->findAvailableAgent($areaCode);
 
+        // 获取项目分类
+        $projectCate = ProjectCate::find($projectCateId);
         // 获取项目列表
         if ($agent) {
-            dd($agent->cates());
-            $projects = Project::where('state', 'enable')
-                ->where('agent_id', $agent->id)
-                ->paginate(10);
+            $agentCate = $agent->cates()->find($projectCate->id);
+            if ($type == 'normal') {
+                $projects = $agentCate->projects()->where('state', 'enable')->whereHas('basicInfo', fn ($q) => $q->where('type', 'normal'))->paginate(10);
+            } else {
+                $projects = $agentCate->projects()->where('state', 'enable')->paginate(10);
+            }
         } else {
-            $projectCate = ProjectCate::find($projectCateId);
+
             if ($type == 'normal') {
                 $projects = $projectCate?->services()->where('type', 'normal')->get();
             } else {