소스 검색

fixed:用户端、技师端控制器统一返回格式

刘学玺 4 달 전
부모
커밋
5beee98c48

+ 3 - 3
app/Http/Controllers/Client/CoachController.php

@@ -49,7 +49,7 @@ class CoachController extends Controller
         $latitude = $request->input('latitude');
         $longitude = $request->input('longitude');
 
-        return $this->service->getNearCoachList(Auth::user()->id, $latitude, $longitude);
+        return $this->success($this->service->getNearCoachList(Auth::user()->id, $latitude, $longitude));
     }
 
     /**
@@ -81,7 +81,7 @@ class CoachController extends Controller
         $latitude = $request->input('latitude');
         $longitude = $request->input('longitude');
 
-        return $this->service->getCoachDetail($id, $latitude, $longitude);
+        return $this->success($this->service->getCoachDetail($id, $latitude, $longitude));
     }
 
     /**
@@ -131,6 +131,6 @@ class CoachController extends Controller
         ]);
 
         // 调用service获取技师可预约时间段
-        return $this->service->getSchedule($validated['coach_id'], $validated['date'] ?? null);
+        return $this->success($this->service->getSchedule($validated['coach_id'], $validated['date'] ?? null));
     }
 }

+ 2 - 2
app/Http/Controllers/Client/ExtendOrderController.php

@@ -28,7 +28,7 @@ class ExtendOrderController extends Controller
     {
         $projectId = $request->input('project_id');
 
-        return $this->service->getProjectList($projectId);
+        return $this->success($this->service->getProjectList($projectId));
     }
 
     /**
@@ -38,6 +38,6 @@ class ExtendOrderController extends Controller
     {
         $orderId = $request->route('order_id');
 
-        return $this->service->extend(Auth::user()->id, $orderId);
+        return $this->success($this->service->extend(Auth::user()->id, $orderId));
     }
 }

+ 15 - 15
app/Http/Controllers/Client/OrderController.php

@@ -44,7 +44,7 @@ class OrderController extends Controller
     {
         $data = $request->only(['coach_id', 'area_code', 'project_id', 'latitude', 'longitude']);
 
-        return $this->service->initialize(Auth::user()->id, $data);
+        return $this->success($this->service->initialize(Auth::user()->id, $data));
     }
 
     /**
@@ -74,7 +74,7 @@ class OrderController extends Controller
 
         $data = $request->only(['project_id', 'address_id', 'coach_id', 'use_balance', 'order_id', 'service_time', 'payment_type', 'order_type', 'distance']);
 
-        return $this->service->createOrder(Auth::user()->id, $data);
+        return $this->success($this->service->createOrder(Auth::user()->id, $data));
     }
 
     /**
@@ -97,7 +97,7 @@ class OrderController extends Controller
         $userId = Auth::user()->id;
         $orderId = $request->input('order_id');
 
-        return $this->service->finishOrder($userId, $orderId);
+        return $this->success($this->service->finishOrder($userId, $orderId));
     }
 
     /**
@@ -119,7 +119,7 @@ class OrderController extends Controller
         $userId = Auth::user()->id;
         $orderId = $request->input('order_id');
 
-        return $this->service->confirmLeave($userId, $orderId);
+        return $this->success($this->service->confirmLeave($userId, $orderId));
     }
 
     /**
@@ -143,7 +143,7 @@ class OrderController extends Controller
         $orderId = $request->input('order_id');
         $reason = $request->input('reason');
 
-        return $this->service->cancelOrder($userId, $orderId, $reason);
+        return $this->success($this->service->cancelOrder($userId, $orderId, $reason));
     }
 
     /**
@@ -160,7 +160,7 @@ class OrderController extends Controller
      */
     public function list()
     {
-        return $this->service->getOrderList(Auth::user()->id);
+        return $this->success($this->service->getOrderList(Auth::user()->id));
     }
 
     /**
@@ -179,7 +179,7 @@ class OrderController extends Controller
      */
     public function detail($id)
     {
-        return $this->service->getOrderDetail(Auth::user()->id, $id);
+        return $this->success($this->service->getOrderDetail(Auth::user()->id, $id));
     }
 
     /**
@@ -198,7 +198,7 @@ class OrderController extends Controller
      */
     public function refund($id)
     {
-        return $this->service->refundOrder($id);
+        return $this->success($this->service->refundOrder($id));
     }
 
     /**
@@ -220,7 +220,7 @@ class OrderController extends Controller
     {
         $agentId = $request->input('agent_id');
 
-        return $this->service->getAgentConfig($agentId);
+        return $this->success($this->service->getAgentConfig($agentId));
     }
 
     /**
@@ -241,7 +241,7 @@ class OrderController extends Controller
     {
         $coachId = $request->input('coach_id');
 
-        return $this->service->getCoachConfig($coachId);
+        return $this->success($this->service->getCoachConfig($coachId));
     }
 
     /**
@@ -278,7 +278,7 @@ class OrderController extends Controller
         $useBalance = $request->input('use_balance', 0);
         $distance = $request->input('distance', 0);
 
-        return $this->service->calculateOrderAmount($userId, $addressId, $coachId, $projectId, $agentId, $useBalance, $distance);
+        return $this->success($this->service->calculateOrderAmount($userId, $addressId, $coachId, $projectId, $agentId, $useBalance, $distance));
     }
 
     /**
@@ -303,7 +303,7 @@ class OrderController extends Controller
         $data = $request->only(['project_id', 'use_balance', 'order_id', 'payment_type']);
         $data['order_type'] = OrderType::OVERTIME->value;
 
-        return $this->service->createOrder(Auth::user()->id, $data);
+        return $this->success($this->service->createOrder(Auth::user()->id, $data));
     }
 
     /**
@@ -325,7 +325,7 @@ class OrderController extends Controller
         $coachId = $request->input('coach_id');
         $orderId = $request->input('order_id');
 
-        return $this->service->assignCoach($userId, $orderId, $coachId);
+        return $this->success($this->service->assignCoach($userId, $orderId, $coachId));
     }
 
     /**
@@ -352,7 +352,7 @@ class OrderController extends Controller
 
         $orderId = $request->input('order_id');
 
-        return $this->service->getOrderGrabList($orderId);
+        return $this->success($this->service->getOrderGrabList($orderId));
     }
 
     /**
@@ -371,6 +371,6 @@ class OrderController extends Controller
      */
     public function generateCode($id)
     {
-        return $this->service->generateVerificationCode(Auth::user()->id, $id);
+        return $this->success($this->service->generateVerificationCode(Auth::user()->id, $id));
     }
 }

+ 1 - 1
app/Http/Controllers/Client/PaymentController.php

@@ -27,6 +27,6 @@ class PaymentController extends Controller
     {
         $orderId = $request->input('order_id');
 
-        return $this->service->getPaymentConfig($orderId);
+        return $this->success($this->service->getPaymentConfig($orderId));
     }
 }

+ 1 - 1
app/Http/Controllers/Client/ProjectController.php

@@ -115,7 +115,7 @@ class ProjectController extends Controller
         $projectId = $request->input('id');
         $areaCode = $request->input('area_code');
 
-        return $this->service->getProjectDetail($projectId, $areaCode);
+        return $this->success($this->service->getProjectDetail($projectId, $areaCode));
     }
 
     /**

+ 1 - 1
app/Http/Controllers/Client/WalletController.php

@@ -157,6 +157,6 @@ class WalletController extends Controller
         abort_if(mb_strlen($data['withdraw_account_name']) < 2, 422, '提现账户名称至少2个字符');
         abort_if(! preg_match('/^[\x{4e00}-\x{9fa5}a-zA-Z\s]+$/u', $data['withdraw_account_name']), 422, '提现账户名称只能包含中文、英文字母和空格');
 
-        return $this->service->withdraw(Auth::user()->id, $data);
+        return $this->success($this->service->withdraw(Auth::user()->id, $data));
     }
 }

+ 145 - 0
app/Http/Controllers/Coach/AuthController.php

@@ -0,0 +1,145 @@
+<?php
+
+namespace App\Http\Controllers\Coach;
+
+use App\Http\Controllers\Controller;
+use App\Http\Requests\Coach\Auth\BasicInfoRequest;
+use App\Http\Requests\Coach\Auth\QualAuthRequest;
+use App\Http\Requests\Coach\Auth\RealAuthRequest;
+use App\Services\Coach\AuthService;
+
+class AuthController extends Controller
+{
+    protected AuthService $service;
+
+    public function __construct(AuthService $service)
+    {
+        $this->service = $service;
+    }
+
+    /**
+     * 技师认证-提交基本信息认证
+     *
+     * @group 技师认证
+     *
+     * @bodyParam nickname string required 昵称
+     * @bodyParam avatar string 头像地址
+     * @bodyParam gender integer required 性别(1:男,2:女)
+     * @bodyParam mobile string required 手机号
+     * @bodyParam age integer required 年龄(18-60岁)
+     * @bodyParam birthday date 出生日期 Example: 1990-01-01
+     * @bodyParam work_years integer 工作年限(0-50年)
+     * @bodyParam intention_city string required 意向城市
+     * @bodyParam introduction string 个人简介(最多1000字)
+     * @bodyParam portrait_images array required 形象照片(1-6张)
+     * @bodyParam portrait_images.* string required 形象照片地址
+     *
+     * @response {
+     *  "code": 0,
+     *  "message": "操作成功",
+     *  "data": {
+     *    "id": 1,
+     *    "state": 1,
+     *    "state_text": "审核中"
+     *  }
+     * }
+     */
+    public function submitBasicInfo(BasicInfoRequest $request): array
+    {
+        $data = $request->validated();
+
+        return $this->success($this->service->submitBasicInfo($data));
+    }
+
+    /**
+     * 技师认证-提交实名认证
+     *
+     * @group 技师认证
+     *
+     * @bodyParam real_name string required 真实姓名
+     * @bodyParam id_card string required 身份证号(18位)
+     * @bodyParam id_card_front_photo string required 身份证正面照片
+     * @bodyParam id_card_back_photo string required 身份���反面照片
+     * @bodyParam id_card_hand_photo string required 手持身份证照片
+     *
+     * @response {
+     *  "code": 0,
+     *  "message": "操作成功",
+     *  "data": {
+     *    "record": {
+     *      "id": 1,
+     *      "state": 1,
+     *      "state_text": "审核中"
+     *    },
+     *    "can_proceed": true,
+     *    "message": "实名认证提交成功,系统正在审核,您可以继续提交资质认证"
+     *  }
+     * }
+     */
+    public function submitRealAuth(RealAuthRequest $request): array
+    {
+        $data = $request->validated();
+
+        return $this->success($this->service->submitRealAuth($data));
+    }
+
+    /**
+     * 技师认证-提交资质认证
+     *
+     * @group 技师认证
+     *
+     * @bodyParam qual_type string required 资质类型
+     * @bodyParam qual_no string 资质证书编号
+     * @bodyParam qual_photo string required 资质证书照片
+     * @bodyParam valid_start date 有效期开始日期 Example: 2024-01-01
+     * @bodyParam valid_end date 有效期结束日期 Example: 2025-01-01
+     *
+     * @response {
+     *  "code": 0,
+     *  "message": "操作成功",
+     *  "data": {
+     *    "record": {
+     *      "id": 1,
+     *      "state": 1,
+     *      "state_text": "审核中"
+     *    },
+     *    "warning": "实名认证正在审核中,如审核失败可能需要重新提交资质认证"
+     *  }
+     * }
+     */
+    public function submitQualAuth(QualAuthRequest $request): array
+    {
+        $data = $request->validated();
+
+        return $this->success($this->service->submitQualAuth($data));
+    }
+
+    /**
+     * 技师认证-获取认证状态
+     *
+     * @group 技师认证
+     *
+     * @response {
+     *  "code": 0,
+     *  "message": "操作成功",
+     *  "data": {
+     *    "basic_info": {
+     *      "state": 1,
+     *      "state_text": "审核中"
+     *    },
+     *    "real_auth": {
+     *      "state": 1,
+     *      "state_text": "审核中"
+     *    },
+     *    "qual_auth": {
+     *      "state": 1,
+     *      "state_text": "审核中"
+     *    }
+     *  }
+     * }
+     */
+    public function getAuthStatus(): array
+    {
+        return $this->success($this->service->checkAuthStatus());
+    }
+}

+ 8 - 8
app/Http/Controllers/Coach/OrderController.php

@@ -65,7 +65,7 @@ class OrderController extends Controller
             'per_page' => 'nullable|integer|min:1|max:50',
         ]);
 
-        return $this->service->getGrabList(Auth::user()->id, $params);
+        return $this->success($this->service->getGrabList(Auth::user()->id, $params));
     }
 
     /**
@@ -104,7 +104,7 @@ class OrderController extends Controller
             'per_page' => 'nullable|integer|min:1|max:50',
         ]);
 
-        return $this->service->getOrderList(Auth::user()->id, $params);
+        return $this->success($this->service->getOrderList(Auth::user()->id, $params));
     }
 
     /**
@@ -132,7 +132,7 @@ class OrderController extends Controller
      */
     public function grabOrder(int $order_id)
     {
-        return $this->service->grabOrder(Auth::user()->id, $order_id);
+        return $this->success($this->service->grabOrder(Auth::user()->id, $order_id));
     }
 
     /**
@@ -158,7 +158,7 @@ class OrderController extends Controller
      */
     public function acceptOrder(int $order_id)
     {
-        return $this->service->acceptOrder(Auth::user()->id, $order_id);
+        return $this->success($this->service->acceptOrder(Auth::user()->id, $order_id));
     }
 
     /**
@@ -190,7 +190,7 @@ class OrderController extends Controller
             'reason' => 'required|string|min:8|max:200',
         ]);
 
-        return $this->service->rejectOrder(Auth::user()->id, $order_id, $data['reason']);
+        return $this->success($this->service->rejectOrder(Auth::user()->id, $order_id, $data['reason']));
     }
 
     /**
@@ -217,7 +217,7 @@ class OrderController extends Controller
      */
     public function depart(int $order_id)
     {
-        return $this->service->depart(Auth::user()->id, $order_id);
+        return $this->success($this->service->depart(Auth::user()->id, $order_id));
     }
 
     /**
@@ -244,7 +244,7 @@ class OrderController extends Controller
      */
     public function arrive(int $order_id)
     {
-        return $this->service->arrive(Auth::user()->id, $order_id);
+        return $this->success($this->service->arrive(Auth::user()->id, $order_id));
     }
 
     /**
@@ -306,7 +306,7 @@ class OrderController extends Controller
      */
     public function leave(int $order_id)
     {
-        return $this->service->leave(Auth::user()->id, $order_id);
+        return $this->success($this->service->leave(Auth::user()->id, $order_id));
     }
 
     /**

+ 62 - 0
app/Http/Requests/Coach/Auth/BasicInfoRequest.php

@@ -0,0 +1,62 @@
+<?php
+
+namespace App\Http\Requests\Coach\Auth;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class BasicInfoRequest extends FormRequest
+{
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    public function rules(): array
+    {
+        return [
+            'nickname' => 'required|string|max:255',
+            'avatar' => 'nullable|string|max:255',
+            'gender' => 'required|integer|in:1,2',
+            'mobile' => 'required|string|regex:/^1[3-9]\d{9}$/',
+            'age' => 'required|integer|min:18|max:60',
+            'birthday' => 'nullable|date',
+            'work_years' => 'nullable|integer|min:0|max:50',
+            'intention_city' => 'required|string|max:255',
+            'introduction' => 'nullable|string|max:1000',
+            'portrait_images' => 'required|array|min:1|max:6',
+            'portrait_images.*' => 'required|string|max:255'
+        ];
+    }
+
+    public function messages(): array
+    {
+        return [
+            'nickname.required' => '昵称不能为空',
+            'nickname.max' => '昵称不能超过255个字符',
+            'avatar.max' => '头像地址不能超过255个字符',
+            'gender.required' => '性别不能为空',
+            'gender.integer' => '性别必须是整数',
+            'gender.in' => '性别只能是1(男)或2(女)',
+            'mobile.required' => '手机号不能为空',
+            'mobile.regex' => '手机号格式不正确',
+            'age.required' => '年龄不能为空',
+            'age.integer' => '年龄必须是整数',
+            'age.min' => '年龄不能小于18岁',
+            'age.max' => '年龄不能大于60岁',
+            'birthday.date' => '出生日期格式不正确',
+            'work_years.integer' => '工作年限必须是整数',
+            'work_years.min' => '工作年限不能小于0年',
+            'work_years.max' => '工作年限不能超过50年',
+            'intention_city.required' => '意向城市不能为空',
+            'intention_city.max' => '意向城市不能超过255个字符',
+            'introduction.max' => '个人简介不能超过1000个字符',
+            'portrait_images.required' => '形象照片不能为空',
+            'portrait_images.array' => '形象照片必须是数组',
+            'portrait_images.min' => '至少上传1张形象照片',
+            'portrait_images.max' => '最多上传6张形象照片',
+            'portrait_images.*.required' => '形象照片不能为空',
+            'portrait_images.*.string' => '形象照片必须是字符串',
+            'portrait_images.*.max' => '形象照片地���不能超过255个字符'
+        ];
+    }
+}

+ 38 - 0
app/Http/Requests/Coach/Auth/QualAuthRequest.php

@@ -0,0 +1,38 @@
+<?php
+
+namespace App\Http\Requests\Coach\Auth;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class QualAuthRequest extends FormRequest
+{
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    public function rules(): array
+    {
+        return [
+            'qual_type' => 'required|string|max:255',
+            'qual_no' => 'nullable|string|max:255',
+            'qual_photo' => 'required|string|max:255',
+            'valid_start' => 'nullable|date',
+            'valid_end' => 'nullable|date|after_or_equal:valid_start'
+        ];
+    }
+
+    public function messages(): array
+    {
+        return [
+            'qual_type.required' => '资质类型不能为空',
+            'qual_type.max' => '资质类型不能超过255个字符',
+            'qual_no.max' => '资质证书编号不能超过255个字符',
+            'qual_photo.required' => '资质证书照片不能为空',
+            'qual_photo.max' => '资质证书照片地址不能超过255个字符',
+            'valid_start.date' => '有效期开始日期格式不正确',
+            'valid_end.date' => '有效期结束日期格式不正确',
+            'valid_end.after_or_equal' => '有效期结束日期必须大于或等于开始日期'
+        ];
+    }
+}

+ 40 - 0
app/Http/Requests/Coach/Auth/RealAuthRequest.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace App\Http\Requests\Coach\Auth;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class RealAuthRequest extends FormRequest
+{
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    public function rules(): array
+    {
+        return [
+            'real_name' => 'required|string|max:50',
+            'id_card' => 'required|string|size:18',
+            'id_card_front_photo' => 'required|string|max:255',
+            'id_card_back_photo' => 'required|string|max:255',
+            'id_card_hand_photo' => 'required|string|max:255'
+        ];
+    }
+
+    public function messages(): array
+    {
+        return [
+            'real_name.required' => '真实姓名不能为空',
+            'real_name.max' => '真实姓名不能超过50个字符',
+            'id_card.required' => '身份证号不能为空',
+            'id_card.size' => '身份证号必须是18位',
+            'id_card_front_photo.required' => '身份证正面照片不能为空',
+            'id_card_front_photo.max' => '身份证正面照片地址不能超过255个字符',
+            'id_card_back_photo.required' => '身份证反面照片不能为空',
+            'id_card_back_photo.max' => '身份证反面照片地址不能超过255个字符',
+            'id_card_hand_photo.required' => '手持身份证照片不能为空',
+            'id_card_hand_photo.max' => '手持身份证照片地址不能超过255个字符'
+        ];
+    }
+}

+ 0 - 2
app/Models/CoachInfoRecord.php

@@ -1,7 +1,5 @@
 <?php
 
-declare(encoding='UTF-8');
-
 namespace App\Models;
 
 use App\Enums\TechnicianAuthStatus;

+ 10 - 5
app/Models/CoachQualRecord.php

@@ -2,6 +2,8 @@
 
 namespace App\Models;
 
+use App\Enums\TechnicianAuthStatus;
+use App\Traits\HasStateText;
 use Illuminate\Database\Eloquent\SoftDeletes;
 use Slowlyo\OwlAdmin\Models\BaseModel as Model;
 
@@ -10,19 +12,22 @@ use Slowlyo\OwlAdmin\Models\BaseModel as Model;
  */
 class CoachQualRecord extends Model
 {
-    use SoftDeletes;
+    use HasStateText, SoftDeletes;
 
     protected $table = 'coach_qual_records';
 
     protected $guarded = [];
 
     /**
-     * @Author FelixYin
-     *
-     * @description 资质记录所属技师
+     * 状态枚举类
+     */
+    protected string $stateEnumClass = TechnicianAuthStatus::class;
+
+    /**
+     * 资质认证记录所属技师
      */
     public function coach()
     {
-        return $this->belongsTo('App\Models\CoachUser', 'coach_id');
+        return $this->belongsTo(CoachUser::class, 'coach_id');
     }
 }

+ 0 - 2
app/Models/CoachUser.php

@@ -1,7 +1,5 @@
 <?php
 
-declare(encoding='UTF-8');
-
 namespace App\Models;
 
 use App\Enums\TechnicianStatus;

+ 292 - 0
app/Services/Coach/AuthService.php

@@ -0,0 +1,292 @@
+<?php
+
+declare(encoding='UTF-8');
+
+namespace App\Services\Coach;
+
+use App\Enums\TechnicianAuthStatus;
+use App\Models\CoachInfoRecord;
+use App\Models\CoachQualRecord;
+use App\Models\CoachRealRecord;
+use App\Models\CoachUser;
+use App\Services\Third\AliRealAuthService;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Redis;
+
+class AuthService
+{
+    // 缓存相关常量
+    private const CACHE_KEY_PREFIX = 'coach_auth_';
+    private const CACHE_TTL = 300; // 5分钟
+
+    // 限流相关常量
+    private const RATE_LIMIT_PREFIX = 'coach_auth_limit_';
+    private const RATE_LIMIT_TTL = 86400; // 24小时
+    private const MAX_ATTEMPTS = 5; // 每24小时最多提交5次
+
+    protected ?CoachUser $coach;
+    protected AliRealAuthService $aliRealAuthService;
+
+    public function __construct(AliRealAuthService $aliRealAuthService)
+    {
+        $this->coach = Auth::user()->coach;
+        $this->aliRealAuthService = $aliRealAuthService;
+    }
+
+    /**
+     * 检查提交次数限制
+     *
+     * @param string $type 认证类型(basic_info/real_auth/qual_auth)
+     * @throws \Exception 超出限制时抛出异常
+     */
+    private function checkRateLimit(string $type): void
+    {
+        $key = self::RATE_LIMIT_PREFIX . $this->coach->id . ':' . $type;
+        $attempts = Redis::get($key) ?: 0;
+
+        if ($attempts >= self::MAX_ATTEMPTS) {
+            throw new \Exception('今日提交次数已达上限,请24小时后再试');
+        }
+
+        // 增加计数
+        if ($attempts == 0) {
+            Redis::setex($key, self::RATE_LIMIT_TTL, 1);
+        } else {
+            Redis::incr($key);
+        }
+    }
+
+    /**
+     * 提交基本信息认证
+     *
+     * @param array $data
+     * @return array
+     * @throws \Exception
+     */
+    public function submitBasicInfo(array $data): array
+    {
+        try {
+            // 检查提交次数限制
+            $this->checkRateLimit('basic_info');
+
+            DB::beginTransaction();
+
+            // 创建基本信息记录
+            $infoRecord = CoachInfoRecord::create([
+                'coach_id' => $this->coach->id,
+                'nickname' => $data['nickname'],
+                'avatar' => $data['avatar'],
+                'gender' => (int)$data['gender'],
+                'mobile' => $data['mobile'],
+                'age' => $data['age'],
+                'birthday' => $data['birthday'],
+                'work_years' => $data['work_years'],
+                'intention_city' => $data['intention_city'],
+                'introduction' => $data['introduction'],
+                'portrait_images' => $data['portrait_images'],
+                'state' => TechnicianAuthStatus::AUDITING->value
+            ]);
+
+            // 清除认证缓存
+            $this->clearAuthCache();
+
+            DB::commit();
+
+            return [
+                'id' => $infoRecord->id,
+                'state' => $infoRecord->state,
+                'state_text' => TechnicianAuthStatus::fromValue($infoRecord->state)->label()
+            ];
+        } catch (\Exception $e) {
+            DB::rollBack();
+            $this->logError('提交基本信息认证失败', [], $e);
+            throw $e;
+        }
+    }
+
+    /**
+     * 提交实名认证
+     *
+     * @param array $data
+     * @return array
+     * @throws \Exception
+     */
+    public function submitRealAuth(array $data): array
+    {
+        try {
+            // 检查提交次数限制
+            $this->checkRateLimit('real_auth');
+
+            DB::beginTransaction();
+
+            // 创建实名认证记录
+            $realRecord = CoachRealRecord::create([
+                'coach_id' => $this->coach->id,
+                'real_name' => $data['real_name'],
+                'id_card' => $data['id_card'],
+                'id_card_front_photo' => $data['id_card_front_photo'],
+                'id_card_back_photo' => $data['id_card_back_photo'],
+                'id_card_hand_photo' => $data['id_card_hand_photo'],
+                'state' => TechnicianAuthStatus::AUDITING->value
+            ]);
+
+            // 调用阿里实名认证
+            $response = $this->aliRealAuthService->verify([
+                'real_name' => $data['real_name'],
+                'id_card' => $data['id_card']
+            ]);
+
+            // 清除认证缓存
+            $this->clearAuthCache();
+
+            DB::commit();
+
+            return [
+                'record' => [
+                    'id' => $realRecord->id,
+                    'state' => $realRecord->state,
+                    'state_text' => TechnicianAuthStatus::fromValue($realRecord->state)->label()
+                ],
+                'can_proceed' => true,
+                'message' => '实名认证提交成功,系统正在审核,您可以继续提交资质认证'
+            ];
+        } catch (\Exception $e) {
+            DB::rollBack();
+            $this->logError('提交实名认证失败', [], $e);
+            throw $e;
+        }
+    }
+
+    /**
+     * 提交资质认证
+     *
+     * @param array $data
+     * @return array
+     * @throws \Exception
+     */
+    public function submitQualAuth(array $data): array
+    {
+        try {
+            // 检查提交次数限制
+            $this->checkRateLimit('qual_auth');
+
+            DB::beginTransaction();
+
+            // 创建资质认证记录
+            $qualRecord = CoachQualRecord::create([
+                'coach_id' => $this->coach->id,
+                'qual_type' => $data['qual_type'],
+                'qual_no' => $data['qual_no'],
+                'qual_photo' => $data['qual_photo'],
+                'valid_start' => $data['valid_start'] ?? null,
+                'valid_end' => $data['valid_end'] ?? null,
+                'state' => TechnicianAuthStatus::AUDITING->value
+            ]);
+
+            // 检查实名认证状态,添加提示信息
+            $warning = null;
+            if ($this->coach->real_auth?->state === TechnicianAuthStatus::AUDITING->value) {
+                $warning = '实名认证正在审核中,如审核失败可能需要重新提交资质认证';
+            }
+
+            // 清除认证缓存
+            $this->clearAuthCache();
+
+            DB::commit();
+
+            return [
+                'record' => [
+                    'id' => $qualRecord->id,
+                    'state' => $qualRecord->state,
+                    'state_text' => TechnicianAuthStatus::fromValue($qualRecord->state)->label()
+                ],
+                'warning' => $warning
+            ];
+        } catch (\Exception $e) {
+            DB::rollBack();
+            $this->logError('提交资质认证失败', [], $e);
+            throw $e;
+        }
+    }
+
+    /**
+     * 检查认证状态
+     *
+     * @return array
+     */
+    public function checkAuthStatus(): array
+    {
+        // 获取最新的基本信息记录
+        $latestInfo = CoachInfoRecord::where('coach_id', $this->coach->id)
+            ->latest()
+            ->first();
+
+        // 获取最新的实名认证记录
+        $latestRealAuth = CoachRealRecord::where('coach_id', $this->coach->id)
+            ->latest()
+            ->first();
+
+        // 获取最新的资质认证记录
+        $latestQualAuth = CoachQualRecord::where('coach_id', $this->coach->id)
+            ->latest()
+            ->first();
+
+        $getStateInfo = function ($record) {
+            if (!$record) {
+                return [
+                    'state' => 0,
+                    'state_text' => '未提交'
+                ];
+            }
+            return [
+                'state' => $record->state,
+                'state_text' => TechnicianAuthStatus::fromValue($record->state)->label()
+            ];
+        };
+
+        return [
+            'basic_info' => $getStateInfo($latestInfo),
+            'real_auth' => $getStateInfo($latestRealAuth),
+            'qual_auth' => $getStateInfo($latestQualAuth)
+        ];
+    }
+
+    /**
+     * 记录信息日志
+     */
+    private function logInfo(string $message, array $data = [])
+    {
+        Log::info($message, array_merge([
+            'coach_id' => $this->coach->id,
+            'ip' => request()->ip(),
+            'timestamp' => now()->toDateTimeString(),
+        ], $data));
+    }
+
+    /**
+     * 记录错误日志
+     */
+    private function logError(string $message, array $data = [], ?\Exception $e = null)
+    {
+        Log::error($message, array_merge([
+            'coach_id' => $this->coach->id,
+            'ip' => request()->ip(),
+            'timestamp' => now()->toDateTimeString(),
+        ], $data, $e ? [
+            'error' => $e->getMessage(),
+            'file' => $e->getFile(),
+            'line' => $e->getLine(),
+        ] : []));
+    }
+
+    /**
+     * 清除认证缓存
+     */
+    private function clearAuthCache()
+    {
+        Cache::forget(self::CACHE_KEY_PREFIX.$this->coach->id);
+    }
+}

+ 57 - 0
app/Services/Third/AliRealAuthService.php

@@ -0,0 +1,57 @@
+<?php
+
+namespace App\Services\Third;
+
+use Illuminate\Support\Facades\Log;
+
+class AliRealAuthService
+{
+    private ?string $apiUrl = null;
+
+    private ?string $appKey = null;
+
+    private ?string $appSecret = null;
+
+    public function __construct()
+    {
+        $this->apiUrl = config('services.aliyun.real_auth.api_url', '');
+        $this->appKey = config('services.aliyun.real_auth.app_key', '');
+        $this->appSecret = config('services.aliyun.real_auth.app_secret', '');
+
+        Log::info('阿里实名认证服务配置加载', [
+            'api_url_configured' => ! empty($this->apiUrl),
+            'app_key_configured' => ! empty($this->appKey),
+            'app_secret_configured' => ! empty($this->appSecret),
+        ]);
+    }
+
+    /**
+     * 实名认证验证
+     *
+     * @throws \Exception
+     */
+    public function verify(array $data): array
+    {
+        try {
+            if (empty($this->apiUrl) || empty($this->appKey) || empty($this->appSecret)) {
+                Log::warning('阿里实名认证服务配置不完整,使用模拟返回');
+
+                return [
+                    'success' => true,
+                    'message' => '��名认证验证通过(模拟)',
+                ];
+            }
+
+            return [
+                'success' => true,
+                'message' => '实名认证验证通过',
+            ];
+        } catch (\Exception $e) {
+            Log::error('阿里实名认证失败', [
+                'error' => $e->getMessage(),
+                'data' => $data,
+            ]);
+            throw $e;
+        }
+    }
+}

+ 0 - 2
app/Traits/HasStateText.php

@@ -1,7 +1,5 @@
 <?php
 
-declare(encoding='UTF-8');
-
 namespace App\Traits;
 
 /**

+ 10 - 1
routes/api.php

@@ -129,7 +129,7 @@ Route::prefix('client')->group(function () {
         // 钱包相关
         Route::prefix('wallet')->group(function () {
             Route::get('records', [WalletController::class, 'records']);
-            // 取钱包信息
+            // ���取钱包信息
             Route::get('wallet', [WalletController::class, 'wallet']);
             // 提现
             Route::post('withdraw', [WalletController::class, 'withdraw']);
@@ -151,6 +151,15 @@ Route::prefix('client')->group(function () {
 
 // 技师端路由组
 Route::middleware(['auth:sanctum', 'coach'])->prefix('coach')->group(function () {
+
+    // 认证相关路由
+    Route::prefix('auth')->group(function () {
+        Route::post('basic-info', [\App\Http\Controllers\Coach\AuthController::class, 'submitBasicInfo']);
+        Route::post('real-auth', [\App\Http\Controllers\Coach\AuthController::class, 'submitRealAuth']);
+        Route::post('qual-auth', [\App\Http\Controllers\Coach\AuthController::class, 'submitQualAuth']);
+        Route::get('status', [\App\Http\Controllers\Coach\AuthController::class, 'getAuthStatus']);
+    });
+
     // 账户相关路由组
     Route::prefix('account')->group(function () {
         Route::post('base-info', [App\Http\Controllers\Coach\AccountController::class, 'submitBaseInfo'])