Jelajahi Sumber

fixed:用户端-创建订单(添加订单交易号)

刘学玺 4 bulan lalu
induk
melakukan
09ddbcf1d4

+ 58 - 122
app/Services/Client/CoachService.php

@@ -6,11 +6,9 @@ use App\Enums\OrderStatus;
 use App\Enums\TechnicianAuthStatus;
 use App\Enums\TechnicianLocationType;
 use App\Enums\TechnicianStatus;
-use App\Enums\TechnicianWorkStatus;
 use App\Enums\UserStatus;
 use App\Models\CoachSchedule;
 use App\Models\CoachUser;
-use App\Models\MemberUser;
 use App\Models\Order;
 use Illuminate\Support\Carbon;
 use Illuminate\Support\Facades\Auth;
@@ -22,136 +20,67 @@ class CoachService
 {
     /**
      * 获取技师列表
-     *
-     * @param  int  $userId  用户ID
-     * @param  float  $latitude  纬度
-     * @param  float  $longitude  经度
-     * @return array ['items' => array, 'total' => int]
-     *
-     * @throws \Exception
      */
-    public function getNearCoachList(int $userId, float $latitude, float $longitude)
+    public function getNearCoachList($latitude, $longitude)
     {
-        try {
-            // 参数验证
-            abort_if(! is_numeric($latitude) || ! is_numeric($longitude), 400, '无效的经纬度坐标');
-            abort_if($latitude < -90 || $latitude > 90 || $longitude < -180 || $longitude > 180,
-                400, '经纬度超出有效范围');
+        $page = request()->get('page', 1);
+        $perPage = request()->get('per_page', 15);
+        // 获取当前用户
+        $user = Auth::user();
 
-            $page = max(1, (int) request()->get('page', 1));
-            $perPage = max(1, min(50, (int) request()->get('per_page', 15)));
+        Log::info('Current user and coordinates:', [
+            'user' => $user ? $user->id : null,
+            'latitude' => $latitude,
+            'longitude' => $longitude,
+        ]);
 
-            // 获取当前用户并检查状态
-            $user = MemberUser::select(['id', 'state'])->find($userId);
-            abort_if(! $user, 404, '用户不存在');
-            abort_if($user->state !== UserStatus::OPEN->value, 400, '用户状态异常');
+        // 检查用户状态
+        if (! $user) {
+            throw new \Exception('用户未登录');
+        }
 
-            Log::info('开始获取附近技师列表', [
-                'user_id' => $userId,
-                'latitude' => $latitude,
-                'longitude' => $longitude,
-                'page' => $page,
-                'per_page' => $perPage,
-            ]);
+        if ($user->state !== 'enable') {
+            throw new \Exception('用户状态异常');
+        }
 
-            // 使用缓存减少Redis查询压力
-            $cacheKey = "nearby_coaches:{$latitude}:{$longitude}";
-            $nearbyCoachIds = Cache::remember($cacheKey, now()->addMinutes(1), function () use ($longitude, $latitude) {
-                return Redis::georadius(
-                    'coach_locations',
-                    $longitude,
-                    $latitude,
-                    40,
-                    'km',
-                    ['WITHDIST', 'ASC']
-                ) ?: [];
-            });
-
-            if (empty($nearbyCoachIds)) {
-                Log::info('附近没有找到技师', compact('latitude', 'longitude'));
-
-                return ['items' => [], 'total' => 0];
-            }
+        // 使用 Redis 的 georadius 命令获取附近的技师 ID
+        $nearbyCoachIds = Redis::georadius('coach_locations', $longitude, $latitude, 40, 'km', ['WITHDIST']);
 
-            // 处理技师数据
-            $coachData = collect($nearbyCoachIds)->map(function ($item) {
-                [$id, $type] = explode('_', $item[0]);
-
-                return [
-                    'id' => (int) $id,
-                    'type' => $type,
-                    'distance' => round((float) $item[1], 2),
-                ];
-            })->unique('id')->values();
-
-            // 分页处理
-            $total = $coachData->count();
-            $offset = ($page - 1) * $perPage;
-            $paginatedCoachIds = $coachData->slice($offset, $perPage)
-                ->pluck('id')
-                ->toArray();
-
-            if (empty($paginatedCoachIds)) {
-                return ['items' => [], 'total' => 0];
-            }
+        $coachData = array_map(function ($item) {
+            [$id, $type] = explode('_', $item[0]);
 
-            // 查询数据库获取技师信息
-            $coaches = CoachUser::query()
-                ->whereIn('id', $paginatedCoachIds)
-                ->where('state', TechnicianStatus::ACTIVE->value)
-                ->whereHas('info', function ($query) {
-                    $query->where('state', TechnicianAuthStatus::PASSED->value);
-                })
-                ->whereHas('real', function ($query) {
-                    $query->where('state', TechnicianAuthStatus::PASSED->value);
-                })
-                ->whereHas('qual', function ($query) {
-                    $query->where('state', TechnicianAuthStatus::PASSED->value);
-                })
-                ->with(['info' => function ($query) {
-                    $query->select(['id', 'coach_id', 'nickname', 'avatar', 'gender', 'state']);
-                }])
-                ->get();
-
-            // 设置距离并格式化数据
-            $items = $coaches->map(function ($coach) use ($coachData) {
-                $distanceData = $coachData->firstWhere('id', $coach->id);
-
-                return [
-                    'id' => $coach->id,
-                    'nickname' => $coach->info->nickname ?? '',
-                    'avatar' => $coach->info->avatar ?? '',
-                    'gender' => $coach->info->gender ?? '',
-                    'state' => $coach->state,
-                    'state_text' => TechnicianStatus::fromValue($coach->state)->label(),
-                    'work_status' => $coach->work_status,
-                    'work_status_text' => TechnicianWorkStatus::fromValue($coach->work_status)->label(),
-                    'distance' => $distanceData ? $distanceData['distance'] : null,
-                ];
-            })->sortBy('distance')->values()->all();
-
-            Log::info('成功获取附近技师列表', [
-                'total' => $total,
-                'page' => $page,
-                'per_page' => $perPage,
-                'found' => count($items),
-            ]);
+            return ['id' => $id, 'type' => $type, 'distance' => $item[1]];
+        }, $nearbyCoachIds);
 
-            return [
-                'items' => $items,
-                'total' => $total,
-            ];
+        // 提取所有的id
+        $coachIds = array_unique(array_column($coachData, 'id'));
 
-        } catch (\Exception $e) {
-            Log::error('获取附近技师列表失败', [
-                'user_id' => $userId,
-                'latitude' => $latitude,
-                'longitude' => $longitude,
-                'error' => $e->getMessage(),
-                'trace' => $e->getTraceAsString(),
-            ]);
-            throw $e;
+        // 分页截取 coachIds
+        $paginatedCoachIds = array_slice($coachIds, ($page - 1) * $perPage, $perPage);
+
+        // 查询数据库获取技师信息
+        $coaches = CoachUser::query()
+            ->whereIn('id', $paginatedCoachIds)
+            ->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'])
+            ->paginate($perPage);
+
+        // 遍历技师并设置距离
+        foreach ($coaches as $coach) {
+            $coach->distance = round($coachData[array_search($coach->id, array_column($coachData, 'id'))]['distance'] ?? null, 2);
         }
+        // 按 distance 升序排序
+        $coaches = $coaches->sortBy('distance')->values();
+
+        return $coaches;
     }
 
     /**
@@ -340,6 +269,9 @@ class CoachService
             abort_if($targetDate->diffInDays(now()) > 30,
                 400, '只能查询未来30天内的时间段');
 
+            $cacheKey = "coach:timeslots:{$coachId}:{$date}";
+            Cache::forget($cacheKey);
+
             // 使用缓存减少数据库查询
             return Cache::remember(
                 "coach:timeslots:{$coachId}:{$date}",
@@ -391,8 +323,12 @@ class CoachService
      */
     private function getDayOrders(int $coachId, string $date): array
     {
+        $date = Carbon::parse($data['date'] ?? $date);
+        $startOfDay = $date->startOfDay()->format('Y-m-d H:i:s');
+        $endOfDay = $date->endOfDay()->format('Y-m-d H:i:s');
+
         return Order::where('coach_id', $coachId)
-            ->where('service_time', $date)
+            ->whereBetween('service_time', [$startOfDay, $endOfDay])
             ->whereIn('state', [
                 OrderStatus::ACCEPTED->value,
                 OrderStatus::DEPARTED->value,

+ 14 - 1
app/Services/Client/OrderService.php

@@ -25,6 +25,7 @@ use App\Models\SysConfig;
 use App\Models\User;
 use App\Models\WalletPaymentRecord;
 use App\Models\WalletRefundRecord;
+use Carbon\Carbon;
 use Exception;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\DB;
@@ -118,7 +119,7 @@ class OrderService
      */
     public function createOrder(int $userId, array $data): array
     {
-
+        // TODO:检测技师可服务时间段
         return DB::transaction(function () use ($userId, $data) {
             // 1. 参数校验
             $user = MemberUser::where('id', $userId)
@@ -185,6 +186,10 @@ class OrderService
                 $wallet = $user->wallet;
                 abort_if($wallet->available_balance < $amounts['balance_amount'], 400, '可用余额不足');
             }
+
+            // 项目服务时长(分钟)
+            $data['duration'] = $project->duration;
+
             // 7. 创建订单记录
             $order = $this->createOrderRecord($userId, $data, $orderType, $address, $data['payment_type'], (object) $amounts);
 
@@ -242,8 +247,14 @@ class OrderService
     // 提取方法:创建订单记录
     private function createOrderRecord($userId, $data, $orderType, $address, $payment_type, object $amounts): Order
     {
+        // 计算服务开始和结束时间
+        $serviceStartTime = Carbon::parse($data['service_time']);
+        $serviceEndTime = $serviceStartTime->copy()->addMinutes($data['duration']);
+
+        // TODO: 优化结构
         $order = new Order;
         $order->user_id = $userId;
+        $order->order_no = 'O'.Carbon::now()->format('YmdHis').rand(1000, 9999);
         $order->project_id = $data['project_id'];
         $order->coach_id = $data['coach_id'];
         $order->type = $orderType;
@@ -262,6 +273,8 @@ class OrderService
         $order->location = $address->location;
         $order->address = $address->address;
         $order->area_code = $address->area_code;
+        $order->service_start_time = $serviceStartTime;
+        $order->service_end_time = $serviceEndTime;
         $order->save();
 
         // 创建订单记录

+ 1 - 1
app/Services/Coach/AccountService.php

@@ -573,7 +573,7 @@ class AccountService
     /**
      * 清除可预约时间段缓存
      */
-    private function clearTimeSlotCache(int $coachId): void
+    public function clearTimeSlotCache(int $coachId): void
     {
         try {
             $pattern = "coach:timeslots:{$coachId}:*";

+ 32 - 3
app/Services/Coach/OrderService.php

@@ -17,6 +17,7 @@ use App\Models\Order;
 use App\Models\OrderGrabRecord;
 use App\Models\OrderRecord;
 use App\Services\SettingItemService;
+use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Log;
 use Illuminate\Support\Facades\Redis;
@@ -451,6 +452,9 @@ class OrderService
                 $order->state = OrderStatus::ACCEPTED->value;
                 $order->save();
 
+                // 清理技师相关缓存
+                $this->clearCoachCache($coach->id, $order->service_time);
+
                 // 记录日志
                 Log::info('技师接单成功', [
                     'user_id' => $userId,
@@ -459,9 +463,6 @@ class OrderService
                     'order_no' => $order->order_no,
                 ]);
 
-                // 清理技师相关缓存
-                app(AccountService::class)->clearTimeSlotCache($coach->id);
-
                 return [
                     'message' => '接单成功',
                     'order_id' => $orderId,
@@ -979,4 +980,32 @@ class OrderService
             // 不抛出异常,继续执行
         }
     }
+
+    /**
+     * 清理技师相关缓存
+     *
+     * @param  int  $coachId  技师ID
+     * @param  string|null  $date  服务日期
+     */
+    private function clearCoachCache(int $coachId, ?string $date = null): void
+    {
+        try {
+            // 清理技师时间段缓存
+            $date = $date ?: now()->toDateString();
+            $cacheKey = "coach:timeslots:{$coachId}:{$date}";
+            Cache::forget($cacheKey);
+
+            Log::info('成功清理技师缓存', [
+                'coach_id' => $coachId,
+                'date' => $date,
+            ]);
+        } catch (\Exception $e) {
+            Log::error('清理技师缓存失败', [
+                'coach_id' => $coachId,
+                'date' => $date,
+                'error' => $e->getMessage(),
+            ]);
+            // 缓存清理失败不影响主流程
+        }
+    }
 }