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('用户未登录'); } if ($user->state !== 'enable') { throw new \Exception('用户状态异常'); } // 使用 Redis 的 georadius 命令获取附近的技师 ID $nearbyCoachIds = Redis::georadius('coach_locations', $longitude, $latitude, 40, 'km', ['WITHDIST']); $coachData = array_map(function ($item) { [$id, $type] = explode('_', $item[0]); return ['id' => $id, 'type' => $type, 'distance' => $item[1]]; }, $nearbyCoachIds); // 提取所有的id $coachIds = array_unique(array_column($coachData, 'id')); // 分页截取 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; } /** * 获取技师详情 */ public function getCoachDetail($coachId, $latitude, $longitude) { // 检查Redis连接 try { $pingResult = Redis::connection()->ping(); Log::info('Redis connection test:', ['ping_result' => $pingResult]); } catch (\Exception $e) { Log::error('Redis connection error:', ['error' => $e->getMessage()]); throw new \Exception('Redis连接失败:'.$e->getMessage()); } // 检查Redis中的所有位置数据 $allLocations = Redis::zrange('coach_locations', 0, -1, 'WITHSCORES'); Log::info('All locations in Redis:', ['locations' => $allLocations]); // 获取当前用户 $user = Auth::user(); Log::info('Current user and coordinates:', [ 'user' => $user ? $user->id : null, 'latitude' => $latitude, 'longitude' => $longitude, 'coach_id' => $coachId, ]); // 检查用户状态 if (! $user) { throw new \Exception('用户未登录'); } if ($user->state !== 'enable') { throw new \Exception('用户状态异常'); } // 获取技师信息 $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($coachId); if (! $coach) { throw new \Exception('技师不存在'); } // 从 Redis 获取技师的 id_home 和 id_work 的经纬度 $homeLocation = Redis::geopos('coach_locations', $coachId.'_home'); $workLocation = Redis::geopos('coach_locations', $coachId.'_work'); // 检查输入的经纬度是否有效 if (! is_numeric($latitude) || ! is_numeric($longitude)) { Log::error('Invalid coordinates:', ['latitude' => $latitude, 'longitude' => $longitude]); throw new \Exception('无效的经纬度坐标'); } // 临时存储用户当前位置用于计算距离 $tempKey = 'user_temp_'.$user->id; Redis::geoadd('coach_locations', $longitude, $latitude, $tempKey); // 计算距离(单位:km) $distanceHome = null; $distanceWork = null; if ($homeLocation && ! empty($homeLocation[0])) { $distanceHome = Redis::geodist('coach_locations', $tempKey, $coachId.'_home', 'km'); Log::info('Home distance calculation:', [ 'from' => $tempKey, 'to' => $coachId.'_home', 'distance' => $distanceHome, 'home_location' => $homeLocation[0], ]); } if ($workLocation && ! empty($workLocation[0])) { $distanceWork = Redis::geodist('coach_locations', $tempKey, $coachId.'_work', 'km'); } // 删除临时位置点 Redis::zrem('coach_locations', $tempKey); // 选择最近的距离 $distances = array_filter([$distanceHome, $distanceWork]); $coach->distance = ! empty($distances) ? round(min($distances), 2) : null; return $coach; } /** * 设置技师位置信息 * * @param int $coachId 技师ID * @param float $latitude 纬度 * @param float $longitude 经度 * @param string $type 位置类型 (home|work) * @return bool * * @throws \Exception */ public function setCoachLocation($coachId, $latitude, $longitude, $type = 'home') { if (! is_numeric($latitude) || ! is_numeric($longitude)) { Log::error('Invalid coordinates in setCoachLocation:', [ 'coach_id' => $coachId, 'latitude' => $latitude, 'longitude' => $longitude, ]); throw new \Exception('无效的经纬度坐标'); } if (! in_array($type, ['home', 'work'])) { throw new \Exception('无效的位置类型,必须是 home 或 work'); } $key = $coachId.'_'.$type; $result = Redis::geoadd('coach_locations', $longitude, $latitude, $key); Log::info('Coach location set:', [ 'coach_id' => $coachId, 'type' => $type, 'key' => $key, 'latitude' => $latitude, 'longitude' => $longitude, 'result' => $result, ]); // 验证数据是否成功写入 $location = Redis::geopos('coach_locations', $key); Log::info('Verify location after set:', [ 'key' => $key, 'location' => $location, ]); return $result; } }