|
@@ -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,
|