Browse Source

fixed:用户端-获取附近技师

刘学玺 4 months ago
parent
commit
e621092cac
2 changed files with 123 additions and 51 deletions
  1. 2 1
      app/Http/Controllers/Client/CoachController.php
  2. 121 50
      app/Services/Client/CoachService.php

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

@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Client;
 use App\Http\Controllers\Controller;
 use App\Services\Client\CoachService;
 use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
 
 /**
  * @group 用户端
@@ -48,7 +49,7 @@ class CoachController extends Controller
         $latitude = $request->input('latitude');
         $longitude = $request->input('longitude');
 
-        return $this->service->getNearCoachList($latitude, $longitude);
+        return $this->service->getNearCoachList(Auth::user()->id, $latitude, $longitude);
     }
 
     /**

+ 121 - 50
app/Services/Client/CoachService.php

@@ -6,9 +6,11 @@ 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;
@@ -20,67 +22,136 @@ class CoachService
 {
     /**
      * 获取技师列表
+     *
+     * @param  int  $userId  用户ID
+     * @param  float  $latitude  纬度
+     * @param  float  $longitude  经度
+     * @return array ['items' => array, 'total' => int]
+     *
+     * @throws \Exception
      */
-    public function getNearCoachList($latitude, $longitude)
+    public function getNearCoachList(int $userId, float $latitude, float $longitude)
     {
-        $page = request()->get('page', 1);
-        $perPage = request()->get('per_page', 15);
-        // 获取当前用户
-        $user = Auth::user();
-
-        Log::info('Current user and coordinates:', [
-            'user' => $user ? $user->id : null,
-            'latitude' => $latitude,
-            'longitude' => $longitude,
-        ]);
-
-        // 检查用户状态
-        if (! $user) {
-            throw new \Exception('用户未登录');
-        }
+        try {
+            // 参数验证
+            abort_if(! is_numeric($latitude) || ! is_numeric($longitude), 400, '无效的经纬度坐标');
+            abort_if($latitude < -90 || $latitude > 90 || $longitude < -180 || $longitude > 180,
+                400, '经纬度超出有效范围');
 
-        if ($user->state !== 'enable') {
-            throw new \Exception('用户状态异常');
-        }
+            $page = max(1, (int) request()->get('page', 1));
+            $perPage = max(1, min(50, (int) request()->get('per_page', 15)));
 
-        // 使用 Redis 的 georadius 命令获取附近的技师 ID
-        $nearbyCoachIds = Redis::georadius('coach_locations', $longitude, $latitude, 40, 'km', ['WITHDIST']);
+            // 获取当前用户并检查状态
+            $user = MemberUser::select(['id', 'state'])->find($userId);
+            abort_if(! $user, 404, '用户不存在');
+            abort_if($user->state !== UserStatus::OPEN->value, 400, '用户状态异常');
 
-        $coachData = array_map(function ($item) {
-            [$id, $type] = explode('_', $item[0]);
+            Log::info('开始获取附近技师列表', [
+                'user_id' => $userId,
+                'latitude' => $latitude,
+                'longitude' => $longitude,
+                'page' => $page,
+                'per_page' => $perPage,
+            ]);
 
-            return ['id' => $id, 'type' => $type, 'distance' => $item[1]];
-        }, $nearbyCoachIds);
+            // 使用缓存减少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];
+            }
 
-        // 提取所有的id
-        $coachIds = array_unique(array_column($coachData, 'id'));
+            // 处理技师数据
+            $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];
+            }
 
-        // 分页截取 coachIds
-        $paginatedCoachIds = array_slice($coachIds, ($page - 1) * $perPage, $perPage);
+            // 查询数据库获取技师信息
+            $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),
+            ]);
 
-        // 查询数据库获取技师信息
-        $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);
+            return [
+                'items' => $items,
+                'total' => $total,
+            ];
 
-        // 遍历技师并设置距离
-        foreach ($coaches as $coach) {
-            $coach->distance = round($coachData[array_search($coach->id, array_column($coachData, 'id'))]['distance'] ?? null, 2);
+        } catch (\Exception $e) {
+            Log::error('获取附近技师列表失败', [
+                'user_id' => $userId,
+                'latitude' => $latitude,
+                'longitude' => $longitude,
+                'error' => $e->getMessage(),
+                'trace' => $e->getTraceAsString(),
+            ]);
+            throw $e;
         }
-        // 按 distance 升序排序
-        $coaches = $coaches->sortBy('distance')->values();
-
-        return $coaches;
     }
 
     /**