Browse Source

feat:技师端-获取技师位置信息

刘学玺 4 months ago
parent
commit
97d5e7b641

+ 60 - 0
app/Http/Controllers/Coach/AccountController.php

@@ -2,12 +2,14 @@
 
 namespace App\Http\Controllers\Coach;
 
+use App\Enums\TechnicianLocationType;
 use App\Http\Controllers\Controller;
 use App\Http\Requests\Coach\SubmitBaseInfoRequest;
 use App\Http\Requests\Coach\SubmitQualificationRequest;
 use App\Http\Requests\Coach\SubmitRealNameRequest;
 use App\Services\Coach\AccountService;
 use App\Traits\ResponseTrait;
+use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Auth;
 
 /**
@@ -147,4 +149,62 @@ class AccountController extends Controller
     {
         return $this->success($this->service->getCoachInfo(Auth::user()));
     }
+
+    /**
+     * [账户]设置技师位置信息
+     *
+     * @description 设置技师的当前位置或常用位置
+     *
+     * @authenticated
+     *
+     * @bodyParam latitude float required 纬度 Example: 39.9042
+     * @bodyParam longitude float required 经度 Example: 116.4074
+     * @bodyParam type int 位置类型(1:当前位置 2:常用位置) Example: 2
+     *
+     * @response {
+     *   "message": "位置信息设置成功"
+     * }
+     */
+    public function setLocation(Request $request)
+    {
+        $validated = $request->validate([
+            'latitude' => 'required|numeric|between:-90,90',
+            'longitude' => 'required|numeric|between:-180,180',
+            'type' => 'sometimes|integer|in:1,2',
+        ]);
+
+        $result = $this->service->setLocation(
+            Auth::user()->coach->id,
+            $validated['latitude'],
+            $validated['longitude'],
+            $validated['type'] ?? TechnicianLocationType::COMMON->value
+        );
+
+        return $this->success(['message' => '位置信息设置成功']);
+    }
+
+    /**
+     * [账户]获取技师位置信息
+     *
+     * @description 获取技师的当前位置和常用位置信息
+     *
+     * @authenticated
+     *
+     * @response {
+     *   "data": {
+     *     "current": {
+     *       "address": "北京市朝阳区建国路93号万达广场"
+     *     },
+     *     "common": {
+     *       "address": "北京市海淀区中关村大街1号"
+     *     }
+     *   }
+     *idid
+     */
+    public function getLocation()
+    {
+        $result = $this->service->getLocation(Auth::user()->id);
+
+        return $this->success($result);
+    }
 }

+ 2 - 2
app/Services/Client/OrderService.php

@@ -1085,7 +1085,7 @@ class OrderService
                 $existsGrabSuccess = $order->grabRecords()
                     ->where('state', OrderGrabRecordStatus::SUCCEEDED->value)
                     ->exists();
-                abort_if($existsGrabSuccess, 400, '该订单已抢单成');
+                abort_if($existsGrabSuccess, 400, '该订单已抢单成');
 
                 // 4. 更新订单信息
                 $this->updateOrderForAssign($order, $coachId);
@@ -1094,7 +1094,7 @@ class OrderService
                 $this->createAssignRecord($order, $userId);
 
                 // 6. 处理支付
-                if ($order->payment_type == 'balance') {
+                if ($order->payment_type === PaymentMethod::BALANCE->value) {
                     $this->handleBalancePaymentForAssign($order, $userId, $coachId);
                 }
 

+ 112 - 0
app/Services/Coach/AccountService.php

@@ -3,9 +3,12 @@
 namespace App\Services\Coach;
 
 use App\Enums\TechnicianAuthStatus;
+use App\Enums\TechnicianLocationType;
+use App\Models\MemberUser;
 use Illuminate\Support\Facades\Cache;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Redis;
 
 class AccountService
 {
@@ -297,4 +300,113 @@ class AccountService
     {
         Cache::forget(self::CACHE_KEY_PREFIX.$coachId);
     }
+
+    /**
+     * 设置定位信息
+     *
+     * @param  int  $coachId  技师ID
+     * @param  float  $latitude  纬度
+     * @param  float  $longitude  经度
+     * @param  int  $type  位置类型 (current:1|common:2)
+     * @return bool
+     *
+     * @throws \Exception
+     */
+    public function setLocation($coachId, $latitude, $longitude, $type = TechnicianLocationType::COMMON->value)
+    {
+        DB::beginTransaction();
+        try {
+            // 验证经纬度参数
+            if (! is_numeric($latitude) || ! is_numeric($longitude)) {
+                throw new \Exception('无效的经纬度坐标');
+            }
+
+            // 验证位置类型
+            if (! in_array($type, [TechnicianLocationType::CURRENT->value, TechnicianLocationType::COMMON->value])) {
+                throw new \Exception('无效的位置类型');
+            }
+
+            // 生成Redis键
+            $key = $coachId.'_'.$type;
+
+            // 将位置信息写入Redis
+            $result = Redis::geoadd('coach_locations', $longitude, $latitude, $key);
+
+            // 同时写入数据库保存历史记录
+            DB::table('coach_locations')->updateOrInsert(
+                ['coach_id' => $coachId, 'type' => $type],
+                [
+                    'latitude' => $latitude,
+                    'longitude' => $longitude,
+                    'updated_at' => now(),
+                ]
+            );
+
+            DB::commit();
+
+            Log::info('技师位置信息设置成功', [
+                'coach_id' => $coachId,
+                'type' => $type,
+                'latitude' => $latitude,
+                'longitude' => $longitude,
+            ]);
+
+            return $result;
+
+        } catch (\Exception $e) {
+            DB::rollBack();
+            Log::error('技师位置信息设置异常', [
+                'coach_id' => $coachId,
+                'latitude' => $latitude,
+                'longitude' => $longitude,
+                'type' => $type,
+                'error' => $e->getMessage(),
+                'file' => $e->getFile(),
+                'line' => $e->getLine(),
+            ]);
+            throw $e;
+        }
+    }
+
+    /**
+     * 获取技师位置信息
+     *
+     * @param  int  $userId  用户ID
+     * @return array 位置信息
+     */
+    public function getLocation($userId)
+    {
+        try {
+            // 改进:直接使用 coach 模型
+            $user = MemberUser::find($userId);
+            abort_if(! $user, 404, '用户不存在');
+            abort_if(! $user->coach, 404, '技师信息不存在');
+
+            // 获取常用位置信息
+            $location = $user->coach->locations()
+                ->where('type', TechnicianLocationType::COMMON->value)
+                ->first();
+
+            $result = [
+                'address' => $location ? $location->location : null,
+            ];
+
+            // 记录日志
+            Log::info('获取技师常用位置信息成功', [
+                'coach_id' => $user->coach->id,
+                'location' => $result,
+            ]);
+
+            return $result;
+
+        } catch (\Exception $e) {
+            Log::error('获取技师常用位置信息异常', [
+                'coach_id' => $user->coach->id ?? null,
+                'error' => $e->getMessage(),
+                'file' => $e->getFile(),
+                'line' => $e->getLine(),
+            ]);
+            throw $e;
+        }
+    }
 }

+ 7 - 1
routes/api.php

@@ -127,7 +127,7 @@ Route::prefix('client')->group(function () {
 });
 
 // 技师端路由组
-Route::prefix('coach')->middleware(['auth:sanctum'])->group(function () {
+Route::middleware(['auth:sanctum', 'verified'])->prefix('coach')->group(function () {
     // 账户相关路由组
     Route::prefix('account')->group(function () {
         Route::post('base-info', [App\Http\Controllers\Coach\AccountController::class, 'submitBaseInfo'])
@@ -137,6 +137,9 @@ Route::prefix('coach')->middleware(['auth:sanctum'])->group(function () {
         Route::post('real-name', [App\Http\Controllers\Coach\AccountController::class, 'submitRealName'])
             ->middleware('throttle:3,1');  // 实名认证限制更严格
         Route::get('info', [App\Http\Controllers\Coach\AccountController::class, 'info']);
+        // 设置位置信息
+        Route::post('location', [App\Http\Controllers\Coach\AccountController::class, 'setLocation'])
+            ->name('coach.account.location');
     });
 
     // 订单相关路由
@@ -145,4 +148,7 @@ Route::prefix('coach')->middleware(['auth:sanctum'])->group(function () {
         Route::get('/grab', [CoachOrderController::class, 'getGrabList']);
         Route::post('/grab/{order_id}', [CoachOrderController::class, 'grabOrder']);
     });
+
+    // 获取技师位置信息
+    Route::get('location', [\App\Http\Controllers\Coach\AccountController::class, 'getLocation']);
 });