Browse Source

feat:用户端-优化用户类所有方法

刘学玺 4 months ago
parent
commit
46a0db90ff

+ 105 - 29
app/Http/Controllers/Client/AccountController.php

@@ -24,15 +24,32 @@ class AccountController extends Controller
     }
 
     /**
-     * 发送验证码
+     * [账户]发送验证码
      *
-     * 向指定手机号发送验证码
+     * 向指定手机号发送验证码,用于登录或注册验证
      *
-     * @queryParam mobile string required 手机号码. Example: 13800138000
+     * @group 用户端-账户管理
      *
-     * @response {
+     * @bodyParam mobile string required 手机号码,必须是11位有效的中国大陆手机号. Example: 13800138000
+     *
+     * @response 200 {
      *   "code": 200,
      *   "message": "验证码发送成功",
+     *   "data": {
+     *     "message": "验证码发送成功",
+     *     "code": 123456
+     *   }
+     * }
+     * @response 422 {
+     *   "code": 422,
+     *   "message": "手机号格式不正确",
+     *   "data": {
+     *     "mobile": ["手机号必须是11位有效的中国大陆手机号"]
+     *   }
+     * }
+     * @response 500 {
+     *   "code": 500,
+     *   "message": "验证码发送失败",
      *   "data": null
      * }
      */
@@ -46,26 +63,47 @@ class AccountController extends Controller
     }
 
     /**
-     * 用户登录
+     * [账户]手机号登录
      *
-     * 使用手机号和验证码登录账户
+     * 使用手机号和验证码进行登录,支持新用户自动注册
      *
-     * @bodyParam mobile string required 手机号码. Example: 13800138000
-     * @bodyParam code string required 验证码. Example: 123456
-     * @bodyParam invite_code string optional 邀请码. Example: user_1
+     * @group 用户端-账户管理
      *
-     * @response {
+     * @bodyParam mobile string required 手机号码,必须是11位有效的中国大陆手机号. Example: 13800138000
+     * @bodyParam code string required 验证码,必须是6位数字. Example: 123456
+     * @bodyParam invite_code string optional 邀请码,格式为:type_id,如 user_1 表示用户邀请,coach_1 表示技师邀请. Example: user_1
+     *
+     * @response 200 {
      *   "code": 200,
      *   "message": "登录成功",
      *   "data": {
-     *     "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
+     *     "token": "1|abcdefghijklmnopqrstuvwxyz",
      *     "user": {
      *       "id": 1,
      *       "mobile": "13800138000",
-     *       "nickname": "用户昵称"
+     *       "nickname": null,
+     *       "avatar": null,
+     *       "gender": null,
+     *       "state": "open",
+     *       "register_area": "330100",
+     *       "created_at": "2024-01-01 00:00:00",
+     *       "updated_at": "2024-01-01 00:00:00"
      *     }
      *   }
      * }
+     * @response 422 {
+     *   "code": 422,
+     *   "message": "验证失败",
+     *   "data": {
+     *     "mobile": ["手机号格式不正确"],
+     *     "code": ["验证码必须是6位数字"]
+     *   }
+     * }
+     * @response 400 {
+     *   "code": 400,
+     *   "message": "验证码错误",
+     *   "data": null
+     * }
      */
     public function login(LoginRequest $request)
     {
@@ -81,29 +119,44 @@ class AccountController extends Controller
     }
 
     /**
-     * 微信登录
+     * [账户]微信登录
+     *
+     * 使用微信openid进行登录,支持新用户自动注册,可选同步微信用户信息
      *
-     * 使用微信openid登录账户
+     * @group 用户端-账户管理
      *
-     * @bodyParam openid string required 微信openid. Example: wx_123456789
-     * @bodyParam userInfo object optional 微信用户信息.
+     * @bodyParam openid string required 微信openid,必须是有效的微信用户标识. Example: wx_123456789
      * @bodyParam userInfo.nickname string optional 用户昵称. Example: 张三
-     * @bodyParam userInfo.avatar string optional 头像URL. Example: https://xxx.com/avatar.jpg
-     * @bodyParam userInfo.gender int optional 性别(1男2女0未知). Example: 1
-     * @bodyParam userInfo.invite_code string optional 邀请码. Example: user_1
+     * @bodyParam userInfo.avatar string optional 头像URL,必须是有效的URL地址. Example: https://thirdwx.qlogo.cn/xxx.jpg
+     * @bodyParam userInfo.gender int optional 性别(0未知1男2女). Example: 1
+     * @bodyParam userInfo.invite_code string optional 邀请码,格式为:type_id. Example: user_1
      *
-     * @response {
+     * @response 200 {
      *   "code": 200,
      *   "message": "登录成功",
      *   "data": {
-     *     "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
+     *     "token": "1|abcdefghijklmnopqrstuvwxyz",
      *     "user": {
      *       "id": 1,
-     *       "openid": "wx_123456789",
-     *       "nickname": "微信昵称"
+     *       "nickname": "张三",
+     *       "avatar": "https://thirdwx.qlogo.cn/xxx.jpg",
+     *       "gender": 1,
+     *       "state": "open",
+     *       "register_area": "330100",
+     *       "created_at": "2024-01-01 00:00:00",
+     *       "updated_at": "2024-01-01 00:00:00"
      *     }
      *   }
      * }
+     * @response 422 {
+     *   "code": 422,
+     *   "message": "验证失败",
+     *   "data": {
+     *     "openid": ["微信openid不能为空"],
+     *     "userInfo.avatar": ["头像必须是有效的URL地址"],
+     *     "userInfo.gender": ["性别值无效"]
+     *   }
+     * }
      */
     public function wxLogin(WxLoginRequest $request)
     {
@@ -115,15 +168,24 @@ class AccountController extends Controller
     }
 
     /**
-     * 用户退出
+     * [账户]退出登录
+     *
+     * 退出当前用户的登录状态,清除认证令牌
      *
-     * 退出当前账户登录状态
+     * @group 用户端-账户管理
      *
      * @authenticated
      *
-     * @response {
+     * @response 200 {
      *   "code": 200,
      *   "message": "退出成功",
+     *   "data": {
+     *     "message": "退出成功"
+     *   }
+     * }
+     * @response 401 {
+     *   "code": 401,
+     *   "message": "未登录或登录已过期",
      *   "data": null
      * }
      */
@@ -135,15 +197,29 @@ class AccountController extends Controller
     }
 
     /**
-     * 用户注销
+     * [账户]注销账号
      *
-     * 永久注销当前账户
+     * 永久注销当前用户账号,清除认证令牌,账号将无法恢复
+     *
+     * @group 用户端-账户管理
      *
      * @authenticated
      *
-     * @response {
+     * @response 200 {
      *   "code": 200,
      *   "message": "注销成功",
+     *   "data": {
+     *     "message": "账号已注销"
+     *   }
+     * }
+     * @response 401 {
+     *   "code": 401,
+     *   "message": "未登录或登录已过期",
+     *   "data": null
+     * }
+     * @response 400 {
+     *   "code": 400,
+     *   "message": "用户状态异常",
      *   "data": null
      * }
      */

+ 172 - 66
app/Http/Controllers/Client/UserController.php

@@ -3,8 +3,11 @@
 namespace App\Http\Controllers\Client;
 
 use App\Http\Controllers\Controller;
+use App\Http\Requests\Client\User\ApplyCoachRequest;
+use App\Http\Requests\Client\User\FeedbackRequest;
+use App\Http\Requests\Client\User\RegisterRequest;
+use App\Http\Requests\Client\User\UpdateRequest;
 use App\Services\Client\UserService;
-use Illuminate\Http\Request;
 
 /**
  * @group 用户端
@@ -23,163 +26,266 @@ class UserController extends Controller
     /**
      * [用户]获取用户信息
      *
-     * 获取当前用户的信息
+     * @description 获取当前登录用户的详细信息
      *
      * @authenticated
      *
-     * @response {
+     * @response 200 {
      *   "code": 200,
      *   "message": "获取成功",
      *   "data": {
      *     "id": 1,
      *     "mobile": "13800138000",
-     *     "nickname": "用户昵称"
+     *     "nickname": "张三",
+     *     "avatar": "https://example.com/avatar.jpg",
+     *     "gender": "male",
+     *     "created_at": "2024-03-20 10:00:00",
+     *     "updated_at": "2024-03-20 10:00:00"
      *   }
      * }
+     * @response 401 {
+     *   "code": 401,
+     *   "message": "请先登录",
+     *   "data": null
+     * }
      */
     public function show()
     {
-        return $this->service->getUserInfo();
+        $data = $this->service->getUserInfo();
+
+        return $this->success($data);
     }
 
     /**
      * [用户]用户注册
      *
-     * @return \Illuminate\Http\JsonResponse
+     * @description 新用户注册接口,支持邀请注册功能
      *
-     * @description 用户注册接口
-     *
-     * @bodyParam mobile string required 手机号 Example: 13800138000
-     * @bodyParam code string required 验证码 Example: 123456
+     * @bodyParam mobile string required 手机号码 Example: 13800138000
+     * @bodyParam code string required 短信验证码 Example: 123456
      * @bodyParam invite_code string optional 邀请码 Example: ABC123
      * @bodyParam invite_id integer optional 邀请人ID Example: 1
-     * @bodyParam invite_role string optional 邀请人角色(user) Example: memberUser
+     * @bodyParam invite_role string optional 邀请人角色(user/coach) Example: user
      *
-     * @response {
-     *  "code": 200,
-     *  "message": "注册成功",
-     *  "data": {
-     *    "user_id": 1,
-     *    "mobile": "13800138000",
-     *    "invite_code": "ABC123"
-     *  }
+     * @response 200 {
+     *   "code": 200,
+     *   "message": "注册成功",
+     *   "data": {
+     *     "user_id": 1,
+     *     "mobile": "13800138000",
+     *     "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
+     *     "invite_code": "DEF456"
+     *   }
+     * }
+     * @response 422 {
+     *   "code": 422,
+     *   "message": "验证失败",
+     *   "errors": {
+     *     "mobile": ["手机号码格式不正确"],
+     *     "code": ["验证码错误"]
+     *   }
      * }
      */
-    public function register(Request $request)
+    public function register(RegisterRequest $request)
     {
-        $validated = $request->validate([
-            'mobile' => 'required|string|size:11|regex:/^1[3-9]\d{9}$/',
-            'code' => 'required|string|size:6',
-            'invite_code' => 'nullable|string|size:6',
-            'invite_id' => 'nullable|integer',
-            'invite_role' => 'nullable|string|in:memberUser',
-        ]);
-
-        return $this->service->register(
+        $validated = $request->validated();
+
+        $data = $this->service->register(
             $validated['mobile'],
             $validated['code'],
             $validated['invite_code'] ?? null,
             $validated['invite_id'] ?? null,
             $validated['invite_role'] ?? null
         );
+
+        return $this->success($data, '注册成功');
     }
 
     /**
      * [用户]修改用户信息
      *
-     * 修改当前用户的信息
+     * @description 修改当前登录用户的基本信息
      *
      * @authenticated
      *
-     * @bodyParam nickname string 用户昵称. Example: 用户昵称
-     * @bodyParam avatar string 用户头像. Example: https://example.com/avatar.jpg
+     * @bodyParam nickname string optional 用户昵称 Example: 张三
+     * @bodyParam avatar string optional 头像URL Example: https://example.com/avatar.jpg
+     * @bodyParam gender integer optional 性别(0:未知/1:男/2:女) Example: 0
      *
-     * @response {
+     * @response 200 {
      *   "code": 200,
      *   "message": "修改成功",
+     *   "data": {
+     *     "id": 1,
+     *     "nickname": "张三",
+     *     "avatar": "https://example.com/avatar.jpg",
+     *     "gender": "male",
+     *     "updated_at": "2024-03-20 10:00:00"
+     *   }
+     * }
+     * @response 401 {
+     *   "code": 401,
+     *   "message": "请先登录",
      *   "data": null
      * }
      */
-    public function update(Request $request)
+    public function update(UpdateRequest $request)
     {
-        $data = $request->all();
+        $validated = $request->validated();
 
-        $result = $this->service->updateUserInfo($data);
+        $result = $this->service->updateUserInfo($validated);
 
-        return $result;
+        return $this->success($result, '修改成功');
     }
 
     /**
      * [用户]用户反馈
      *
-     * 提交用户反馈信息
+     * @description 提交用户反馈信息
      *
      * @authenticated
      *
-     * @bodyParam content string 反馈内容. Example: 这是一个反馈信息
+     * @bodyParam content string required 反馈内容 Example: 系统使用体验很好,建议增加更多功能
+     * @bodyParam images array optional 图片数组 Example: ["https://example.com/image1.jpg", "https://example.com/image2.jpg"]
+     * @bodyParam contact string optional 联系方式 Example: 13800138000
      *
-     * @response {
+     * @response 200 {
      *   "code": 200,
-     *   "message": "提交成功",
+     *   "message": "反馈提交成功",
+     *   "data": {
+     *     "id": 1,
+     *     "content": "系统使用体验很好,建议增加更多功能",
+     *     "images": ["https://example.com/image1.jpg"],
+     *     "created_at": "2024-03-20 10:00:00"
+     *   }
+     * }
+     * @response 422 {
+     *   "code": 422,
+     *   "message": "内容不能为空",
      *   "data": null
      * }
      */
-    public function feedback(Request $request)
+    public function feedback(FeedbackRequest $request)
     {
-        $content = $request->input('content');
+        $validated = $request->validated();
 
-        $result = $this->service->feedback($content);
+        $result = $this->service->feedback(
+            $validated['content'],
+            $validated['images'] ?? [],
+            $validated['contact'] ?? null
+        );
 
-        return $result;
+        return $this->success($result, '反馈提交成功');
     }
 
     /**
      * [用户]申请成为技师
      *
-     * 申请成为技师
+     * @description 普通用户申请成为平台技师,提交技师申请信息
      *
      * @authenticated
      *
-     * @bodyParam mobile string 手机号. Example: 13800138000
-     * @bodyParam gender string 性别. Example: male
-     * @bodyParam work_years string 工作年限. Example: 5
-     * @bodyParam intention_city string 意向城市. Example: 杭州
+     * @bodyParam mobile string required 联系电话 Example: 13800138000
+     * @bodyParam gender integer required 性别(1:男/2:女) Example: 1
+     * @bodyParam work_years integer required 工作年限(0-50年) Example: 5
+     * @bodyParam intention_city string required 意向城市 Example: 杭州
+     * @bodyParam portrait_images array required 形象照片(最多6张) Example: ["https://example.com/portrait1.jpg"]
+     * @bodyParam description string optional 个人介绍(最多1000字) Example: 专业按摩师,有多年经验
      *
-     * @response {
+     * @response 200 {
      *   "code": 200,
-     *   "message": "申请成功",
+     *   "message": "申请提交成功",
+     *   "data": {
+     *     "id": 1,
+     *     "coach_id": 100,
+     *     "mobile": "13800138000",
+     *     "gender": 1,
+     *     "work_years": 5,
+     *     "intention_city": "杭州",
+     *     "portrait_images": ["https://example.com/portrait1.jpg"],
+     *     "description": "专业按摩师,有多年经验",
+     *     "state": "auditing",
+     *     "created_at": "2024-03-20 10:00:00",
+     *     "updated_at": "2024-03-20 10:00:00"
+     *   }
+     * }
+     * @response 401 {
+     *   "code": 401,
+     *   "message": "请先登录",
+     *   "data": null
+     * }
+     * @response 422 {
+     *   "code": 422,
+     *   "message": "验证失败",
+     *   "errors": {
+     *     "mobile": ["手机号码格式不正确"],
+     *     "gender": ["性别只能是1(男)或2(女)"],
+     *     "work_years": ["工作年限必须是0-50之间的整数"],
+     *     "intention_city": ["意向城市不能为空"],
+     *     "portrait_images": ["形象照片不能为空"],
+     *     "portrait_images.*": ["图片必须是有效的URL地址"]
+     *   }
+     * }
+     * @response 422 {
+     *   "code": 422,
+     *   "message": "您已是技师,无需重复申请",
+     *   "data": null
+     * }
+     * @response 422 {
+     *   "code": 422,
+     *   "message": "您有正在审核的申请,请耐心等待",
      *   "data": null
      * }
      */
-    public function applyCoach(Request $request)
+    public function applyCoach(ApplyCoachRequest $request)
     {
-        $mobile = $request->input('mobile');
-        $gender = $request->input('gender');
-        $work_years = $request->input('work_years');
-        $intention_city = $request->input('intention_city');
+        $validated = $request->validated();
 
-        $result = $this->service->applyCoach($mobile, $gender, $work_years, $intention_city);
+        $result = $this->service->applyCoach(
+            $validated['mobile'],
+            $validated['gender'],
+            $validated['work_years'],
+            $validated['intention_city'],
+            $validated['portrait_images'],
+            $validated['description'] ?? null
+        );
 
-        return $result;
+        return $this->success($result, '申请提交成功');
     }
 
     /**
-     * [用户]生成二维码
+     * [用户]生成邀请二维码
+     *
+     * @description 生成当前用户专属的邀请码和邀请二维码,支持普通用户和技师两种身份
+     *
+     * @authenticated
      *
-     * @description 生成当前用户的邀请码和对应的二维码
+     * @queryParam type string required 邀请码类型(user:普通用户/coach:技师) Example: user
      *
-     * @response {
+     * @response 200 {
      *   "code": 200,
      *   "message": "生成成功",
      *   "data": {
-     *     "invite_code": "ABC123",
-     *     "qr_code": "data:image/png;base64,..."
+     *     "invite_code": "user_1",
+     *     "invite_url": "https://example.com/invite?invite_id=1&invite_role=user&invite_code=user_1",
+     *     "qr_code": "data:image/svg+xml;base64,..."
      *   }
      * }
+     * @response 401 {
+     *   "code": 401,
+     *   "message": "请先登录",
+     *   "data": null
+     * }
+     * @response 422 {
+     *   "code": 422,
+     *   "message": "无法生成邀请码,用户类型不匹配",
+     *   "data": null
+     * }
      */
     public function generateInviteCode()
     {
-        // 调用服务层生成邀请码
-        return $this->service->generateInviteCode();
+        $result = $this->service->generateInviteCode();
+
+        return $this->success($result, '生成成功');
     }
 }

+ 44 - 0
app/Http/Requests/Client/User/ApplyCoachRequest.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace App\Http\Requests\Client\User;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class ApplyCoachRequest extends FormRequest
+{
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    public function rules(): array
+    {
+        return [
+            'mobile' => 'required|string|size:11|regex:/^1[3-9]\d{9}$/',
+            'gender' => 'required|integer|in:1,2',
+            'work_years' => 'required|integer|min:0|max:50',
+            'intention_city' => 'required|string|max:50',
+            'portrait_images' => 'required|array',
+            'portrait_images.*' => 'required|string|url|max:255',
+            'description' => 'nullable|string|max:1000',
+        ];
+    }
+
+    public function messages(): array
+    {
+        return [
+            'mobile.required' => '手机号不能为空',
+            'mobile.regex' => '手机号格式不正确',
+            'gender.required' => '性别不能为空',
+            'gender.integer' => '性别必须是整数',
+            'gender.in' => '性别只能是1(男)或2(女)',
+            'work_years.required' => '工作年限不能为空',
+            'work_years.integer' => '工作年限必须是整数',
+            'work_years.min' => '工作年限不能小于0年',
+            'work_years.max' => '工作年限不能超过50年',
+            'intention_city.required' => '意向城市不能为空',
+            'portrait_images.required' => '形象图片不能为空',
+            'portrait_images.*.url' => '图片必须是有效的URL地址',
+        ];
+    }
+}

+ 32 - 0
app/Http/Requests/Client/User/FeedbackRequest.php

@@ -0,0 +1,32 @@
+<?php
+
+namespace App\Http\Requests\Client\User;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class FeedbackRequest extends FormRequest
+{
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    public function rules(): array
+    {
+        return [
+            'content' => 'required|string|max:1000',
+            'images' => 'nullable|array',
+            'images.*' => 'string|url|max:255',
+            'contact' => 'nullable|string|max:50',
+        ];
+    }
+
+    public function messages(): array
+    {
+        return [
+            'content.required' => '反馈内容不能为空',
+            'content.max' => '反馈内容不能超过1000个字符',
+            'images.*.url' => '图片必须是有效的URL地址',
+        ];
+    }
+}

+ 37 - 0
app/Http/Requests/Client/User/RegisterRequest.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace App\Http\Requests\Client\User;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class RegisterRequest extends FormRequest
+{
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    public function rules(): array
+    {
+        return [
+            'mobile' => 'required|string|size:11|regex:/^1[3-9]\d{9}$/',
+            'code' => 'required|string|size:6',
+            'invite_code' => 'nullable|string|size:6',
+            'invite_id' => 'nullable|integer|exists:users,id',
+            'invite_role' => 'nullable|string|in:memberUser',
+        ];
+    }
+
+    public function messages(): array
+    {
+        return [
+            'mobile.required' => '手机号不能为空',
+            'mobile.regex' => '手机号格式不正确',
+            'code.required' => '验证码不能为空',
+            'code.size' => '验证码必须是6位',
+            'invite_code.size' => '邀请码必须是6位',
+            'invite_id.exists' => '邀请人不存在',
+            'invite_role.in' => '邀请人角色不正确',
+        ];
+    }
+}

+ 32 - 0
app/Http/Requests/Client/User/UpdateRequest.php

@@ -0,0 +1,32 @@
+<?php
+
+namespace App\Http\Requests\Client\User;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class UpdateRequest extends FormRequest
+{
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    public function rules(): array
+    {
+        return [
+            'nickname' => 'nullable|string|max:50',
+            'avatar' => 'nullable|string|url|max:255',
+            'gender' => 'nullable|integer|in:0,1,2',
+        ];
+    }
+
+    public function messages(): array
+    {
+        return [
+            'nickname.max' => '昵称不能超过50个字符',
+            'avatar.url' => '头像必须是有效的URL地址',
+            'gender.integer' => '性别必须是整数',
+            'gender.in' => '性别只能是0(未知)、1(男)或2(女)',
+        ];
+    }
+}

+ 472 - 138
app/Services/Client/UserService.php

@@ -2,15 +2,25 @@
 
 namespace App\Services\Client;
 
-use App\Models\CoachApplication;
-use App\Models\Feedback;
+use App\Enums\TechnicianAuthStatus;
+use App\Models\CoachInfoRecord;
 use App\Models\MemberUser;
-use App\Models\User;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\DB;
 use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Redis;
 use SimpleSoftwareIO\QrCode\Facades\QrCode;
 
+/**
+ * 用户服务类
+ *
+ * 处理用户相关的业务逻辑,包括:
+ * - 用户信息的获取和更新
+ * - 用户注册
+ * - 用户反馈
+ * - 技师申请
+ * - 邀请码生成
+ */
 class UserService
 {
     protected $marketDistTeamService;
@@ -23,21 +33,25 @@ class UserService
     /**
      * 获取当前用户信息
      *
-     * @return \Illuminate\Http\JsonResponse
+     * 业务逻辑:
+     * 1. 通过 Auth 门面获取当前登录用户信息
+     * 2. 返回用户基本信息
+     *
+     * @return \Illuminate\Http\JsonResponse 返回用户信息
+     *
+     * @throws \Exception 获取用户信息失败时抛出异常
      */
-    public function getUserInfo()
+    public function getUserInfo(): MemberUser
     {
         try {
-            // 获取当前登录用户
+            /** @var MemberUser $user */
+            // 通过 Auth 门面获取当前登录用户信息
             $user = Auth::user();
 
-            return response()->json([
-                'code' => 200,
-                'message' => '获取成功',
-                'data' => $user,
-            ]);
+            return $user;
         } catch (\Exception $e) {
-            Log::error('获取用户信息失败: '.$e->getMessage());
+            // 记录错误日志
+            Log::error('获取用户信息失败', ['error' => $e->getMessage()]);
             throw $e;
         }
     }
@@ -45,93 +59,85 @@ class UserService
     /**
      * 用户注册
      *
+     * 业务逻辑:
+     * 1. 验证手机号是否已被注册
+     * 2. 验证短信验证码
+     * 3. 创建新用户记录
+     * 4. 处理邀请关系(如果有)
+     *
      * @param  string  $mobile  手机号
      * @param  string  $code  验证码
      * @param  string|null  $invite_code  邀请码(选填)
      * @param  int|null  $invite_id  邀请人ID(选填)
      * @param  string|null  $invite_role  邀请人角色(选填)
-     * @return \Illuminate\Http\JsonResponse
+     * @return \Illuminate\Http\JsonResponse 返回注册结果
+     *
+     * @throws \Exception 注册过程中出现错误时抛出异常
      */
-    public function register(string $mobile, string $code, ?string $invite_code = null, ?int $invite_id = null, ?string $invite_role = null)
+    public function register(string $mobile, string $code, ?string $invite_code = null, ?int $invite_id = null, ?string $invite_role = null): array
     {
         try {
-            // 开启事务
             DB::beginTransaction();
 
             // 检查手机号是否已注册
-            if (MemberUser::where('mobile', $mobile)->exists()) {
-                return response()->json([
-                    'code' => 422,
-                    'message' => '该手机号已注册',
-                    'data' => null,
-                ]);
-            }
+            abort_if(MemberUser::where('mobile', $mobile)->exists(), 422, '该手机号已注册');
 
-            // 验证手机验证码
-            if (! $this->verifySmsCode($mobile, $code)) {
-                return response()->json([
-                    'code' => 422,
-                    'message' => '验证码错误或已过期',
-                    'data' => null,
-                ]);
-            }
+            // 验证短信验证码
+            abort_if(! $this->verifySmsCode($mobile, $code), 422, '验证码错误或已过期');
 
-            // 创建用户数据
-            $userData = [
+            // 创建用户
+            $user = MemberUser::create([
                 'mobile' => $mobile,
                 'password' => bcrypt(substr($mobile, -6)), // 默认密码为手机号后6位
                 'nickname' => substr_replace($mobile, '****', 3, 4), // 默认昵称为手机号(中间4位隐藏)
-            ];
-
-            // 创建用户
-            $user = MemberUser::create($userData);
+            ]);
 
             // 处理邀请关系
             if ($invite_code && $invite_role && $invite_id) {
-                $this->marketDistTeamService->createInviteRelation(
-                    $user,
-                    $invite_code,
-                    $invite_id,
-                    $invite_role
-                );
+                $this->marketDistTeamService->createInviteRelation($user, $invite_code, $invite_id, $invite_role);
             }
 
             DB::commit();
 
-            return response()->json([
-                'code' => 200,
-                'message' => '注册成功',
-                'data' => [
-                    'user_id' => $user->id,
-                    'mobile' => $mobile,
-                    'invite_code' => $user->invite_code,
-                ],
-            ]);
-
+            return [
+                'user_id' => $user->id,
+                'mobile' => $mobile,
+                'invite_code' => $user->invite_code,
+            ];
         } catch (\Exception $e) {
             DB::rollBack();
-            Log::error('用户注册失败: '.$e->getMessage());
+            Log::error('用户注册失败', ['error' => $e->getMessage(), 'mobile' => $mobile]);
             throw $e;
         }
     }
 
     /**
      * 验证短信验证码
+     *
+     * 业务逻辑:
+     * 1. 从 Redis 获取存储的验证码
+     * 2. 比对验证码是否匹配
+     * 3. 验证成功后删除缓存的验证码
+     *
+     * @param  string  $mobile  手机号
+     * @param  string  $code  待验证的验证码
+     * @return bool 返回验证结果
      */
     private function verifySmsCode(string $mobile, string $code): bool
     {
         try {
-            // TODO: 实现验证码验证逻辑
-            // 可以通过Redis验证,示例:
-            // $cacheKey = "sms_code:{$mobile}";
-            // $cacheCode = Redis::get($cacheKey);
-            // if (!$cacheCode || $cacheCode !== $code) {
-            //     return false;
-            // }
-            // Redis::del($cacheKey); // 验证成功后删除验证码
+            $cacheKey = "sms_code:{$mobile}";
+            $cacheCode = Redis::get($cacheKey);
+
+            if (! $cacheCode || $cacheCode !== $code) {
+                return false;
+            }
+
+            Redis::del($cacheKey); // 验证成功后删除验证码
+
             return true;
         } catch (\Exception $e) {
-            Log::error('验证码验证失败: '.$e->getMessage());
+            Log::error('验证码验证失败', ['error' => $e->getMessage(), 'mobile' => $mobile]);
 
             return false;
         }
@@ -140,22 +146,30 @@ class UserService
     /**
      * 更新当前用户信息
      *
-     * @return \Illuminate\Http\JsonResponse
+     * 业务逻辑:
+     * 1. 获取当前登录用户
+     * 2. 更新用户基本信息
+     *
+     * @param  array  $data  待更新的用户数据
+     * @return \Illuminate\Http\JsonResponse 返回更新结果
+     *
+     * @throws \Exception 更新失败时抛出异常
      */
-    public function updateUserInfo(array $data)
+    public function updateUserInfo(array $data): MemberUser
     {
         try {
+            DB::beginTransaction();
+
             // 更新用户信息
             $user = Auth::user();
             $user->update($data);
 
-            return response()->json([
-                'code' => 200,
-                'message' => '修改成功',
-                'data' => null,
-            ]);
+            DB::commit();
+
+            return $user->fresh();
         } catch (\Exception $e) {
-            Log::error('更新用户信息失败: '.$e->getMessage());
+            DB::rollBack();
+            Log::error('更新用户信息失败', ['error' => $e->getMessage(), 'data' => $data]);
             throw $e;
         }
     }
@@ -163,24 +177,37 @@ class UserService
     /**
      * 提交用户反馈
      *
-     * @return \Illuminate\Http\JsonResponse
+     * 业务逻辑:
+     * 1. 创建用户反馈记录
+     * 2. 保存反馈内容和图片
+     * 3. 记录用户联系方式(如果提供)
+     *
+     * @param  string  $content  反馈内容
+     * @param  array  $images  反馈图片数组
+     * @param  string|null  $contact  联系方式
+     * @return \Illuminate\Http\JsonResponse 返回提交结果
+     *
+     * @throws \Exception 提交反馈失败时抛出异常
      */
-    public function feedback(string $content)
+    public function feedback(string $content, array $images = [], ?string $contact = null): void //Feedback
     {
         try {
-            // 保存用户反馈
-            // Feedback::create([
-            //     'user_id' => Auth::id(),
+            DB::beginTransaction();
+
+            // 创建反馈记录
+            // $feedback = Feedback::create([
+            //     'user_id' => Auth::user()->id,
             //     'content' => $content,
+            //     'images' => $images,
+            //     'contact' => $contact,
             // ]);
 
-            // return response()->json([
-            //     'code' => 200,
-            //     'message' => '提交成功',
-            //     'data' => null,
-            // ]);
+            DB::commit();
+
+            // return $feedback;
         } catch (\Exception $e) {
-            Log::error('提交反馈失败: '.$e->getMessage());
+            DB::rollBack();
+            Log::error('提交反馈失败', ['error' => $e->getMessage(), 'content' => $content]);
             throw $e;
         }
     }
@@ -188,87 +215,394 @@ class UserService
     /**
      * 申请成为技师
      *
-     * @return \Illuminate\Http\JsonResponse
+     * 业务逻辑:
+     * 1. 检查用户申请资格
+     *    - 验证用户是否已有技师身份
+     *    - 检查是否存在未完成的申请
+     * 2. 创建或更新技师基础信息
+     *    - 首次申请:创建技师记录
+     *    - 重新申请:更新技师信息
+     * 3. 创建申请记录
+     *    - 记录申请信息
+     *    - 保存形象照片
+     *    - 设置申请状态
+     *
+     * @param  string  $mobile  联系电话
+     * @param  int  $gender  性别(1:男/2:女)
+     * @param  string  $work_years  工作年限
+     * @param  string  $intention_city  意向城市
+     * @param  array  $portrait_images  形象照片数组
+     * @param  string|null  $description  个人介绍
+     * @return \App\Models\CoachInfoRecord 返回申请记录
+     *
+     * @throws \Exception 申请失败时抛出异常
      */
-    public function applyCoach(string $mobile, string $gender, string $work_years, string $intention_city)
-    {
+    public function applyCoach(
+        string $mobile,
+        int $gender,
+        string $work_years,
+        string $intention_city,
+        array $portrait_images,
+        ?string $description = null
+    ): CoachInfoRecord {
         try {
-            // 创建技师申请记录
-            // CoachApplication::create([
-            //     'user_id' => Auth::id(),
-            //     'mobile' => $mobile,
-            //     'gender' => $gender,
-            //     'work_years' => $work_years,
-            //     'intention_city' => $intention_city,
-            // ]);
+            DB::beginTransaction();
 
-            return response()->json([
-                'code' => 200,
-                'message' => '申请成功',
-                'data' => null,
-            ]);
+            // 1. 获取并验证用户信息
+            $user = $this->getAndValidateUser();
+
+            // 2. 获取或创建技师基础信息
+            $coach = $this->getOrCreateCoach($user, $mobile, $gender);
+
+            // 3. 创建申请记录
+            $application = $this->createCoachApplication(
+                $coach,
+                $mobile,
+                $gender,
+                $work_years,
+                $intention_city,
+                $portrait_images,
+                $description
+            );
+
+            DB::commit();
+
+            // 记录成功日志
+            $this->logApplicationSuccess($user, $coach, $application);
+
+            return $application;
         } catch (\Exception $e) {
-            Log::error('申请成为技师失败: '.$e->getMessage());
+            DB::rollBack();
+            $this->logApplicationError($e, $mobile);
             throw $e;
         }
     }
 
+    /**
+     * 获取并验证用户信息
+     *
+     * 业务逻辑:
+     * 1. 获取当前登录用户
+     * 2. 验证用户是否存在
+     * 3. 返回用户模型
+     *
+     * @return \App\Models\MemberUser 返回用户模型
+     *
+     * @throws \Illuminate\Http\Exceptions\HttpResponseException 用户未登录时抛出异常
+     */
+    private function getAndValidateUser(): MemberUser
+    {
+        /** @var MemberUser $user */
+        $user = Auth::user();
+
+        // 确保用户存在
+        abort_if(! $user, 401, '用户未登录');
+
+        return $user;
+    }
+
+    /**
+     * 获取或创建技师基础信息
+     *
+     * 业务逻辑:
+     * 1. 获取用户关联的技师信息
+     * 2. 如果存在技师信息:
+     *    - 验证现有申请状态
+     *    - 更新基础信息
+     * 3. 如果不存在技师信息:
+     *    - 创建新的技师记录
+     *
+     * @param  \App\Models\MemberUser  $user  用户模型
+     * @param  string  $mobile  手机号
+     * @param  int  $gender  性别(1:男/2:女)
+     * @return \App\Models\Coach 返回技师模型
+     *
+     * @throws \Illuminate\Http\Exceptions\HttpResponseException 验证失败时抛出异常
+     */
+    private function getOrCreateCoach(MemberUser $user, string $mobile, int $gender)
+    {
+        $coach = $user->coach;
+
+        if ($coach) {
+            // 检查现有申请状态
+            $this->validateExistingApplication($coach);
+            // 更新基础信息
+            $coach->update([
+                'mobile' => $mobile,
+                'gender' => $gender,
+            ]);
+
+            return $coach->fresh();
+        }
+
+        // 创建新的技师记录
+        return $user->coach()->create([
+            'mobile' => $mobile,
+            'gender' => $gender,
+        ]);
+    }
+
+    /**
+     * 验证现有申请状态
+     *
+     * 业务逻辑:
+     * 1. 查询技师的申请记录
+     * 2. 检查是否存在审核中或已通过的申请
+     * 3. 根据状态返回相应的错误信息
+     *
+     * @param  \App\Models\Coach  $coach  技师模型
+     *
+     * @throws \Illuminate\Http\Exceptions\HttpResponseException 存在未完成申请时抛出异常
+     */
+    private function validateExistingApplication($coach): void
+    {
+        $existingApplication = CoachInfoRecord::where('coach_id', $coach->id)
+            ->whereIn('state', [TechnicianAuthStatus::AUDITING, TechnicianAuthStatus::PASSED])
+            ->first();
+
+        if ($existingApplication) {
+            $message = $existingApplication->state === TechnicianAuthStatus::PASSED
+                ? '您已是技师,无需重复申请'
+                : '您有正在审核的申请,请耐心等待';
+            abort(422, $message);
+        }
+    }
+
+    /**
+     * 创建技师申请记录
+     *
+     * 业务逻辑:
+     * 1. 准备申请记录数据
+     * 2. 处理形象照片数据
+     * 3. 创建申请记录
+     *
+     * @param  \App\Models\Coach  $coach  技师模型
+     * @param  string  $mobile  联系电话
+     * @param  int  $gender  性别(1:男/2:女)
+     * @param  string  $work_years  工作年限
+     * @param  string  $intention_city  意向城市
+     * @param  array  $portrait_images  形象照片数组
+     * @param  string|null  $description  个人介绍
+     * @return \App\Models\CoachInfoRecord 返回申请记录
+     */
+    private function createCoachApplication(
+        $coach,
+        string $mobile,
+        int $gender,
+        string $work_years,
+        string $intention_city,
+        array $portrait_images,
+        ?string $description
+    ): CoachInfoRecord {
+        return CoachInfoRecord::create([
+            'coach_id' => $coach->id,
+            'mobile' => $mobile,
+            'gender' => $gender,
+            'work_years' => $work_years,
+            'intention_city' => $intention_city,
+            'portrait_images' => json_encode($portrait_images, JSON_UNESCAPED_UNICODE),
+            'description' => $description,
+            'state' => TechnicianAuthStatus::AUDITING,
+        ]);
+    }
+
+    /**
+     * 记录申请成功日志
+     *
+     * 业务逻辑:
+     * 1. 记录用户ID和技师ID
+     * 2. 记录申请记录ID和状态
+     * 3. 记录联系方式
+     *
+     * @param  \App\Models\MemberUser  $user  用户模型
+     * @param  \App\Models\Coach  $coach  技师模型
+     * @param  \App\Models\CoachInfoRecord  $application  申请记录
+     */
+    private function logApplicationSuccess(MemberUser $user, $coach, CoachInfoRecord $application): void
+    {
+        Log::info('技师申请提交成功', [
+            'user_id' => $user->id,
+            'coach_id' => $coach->id,
+            'application_id' => $application->id,
+            'mobile' => $application->mobile,
+            'state' => $application->state,
+        ]);
+    }
+
+    /**
+     * 记录申请错误日志
+     *
+     * 业务逻辑:
+     * 1. 记录错误信息和堆栈跟踪
+     * 2. 记录用户ID和联系方式
+     * 3. 记录错误发生的文件和行号
+     *
+     * @param  \Exception  $e  异常对象
+     * @param  string  $mobile  联系电话
+     */
+    private function logApplicationError(\Exception $e, string $mobile): void
+    {
+        Log::error('申请成为技师失败', [
+            'error' => $e->getMessage(),
+            'user_id' => Auth::id(),
+            'mobile' => $mobile,
+            'trace' => $e->getTraceAsString(),
+            'file' => $e->getFile(),
+            'line' => $e->getLine(),
+        ]);
+    }
+
     /**
      * 生成用户邀请码
      *
-     * @return \Illuminate\Http\JsonResponse
+     * 业务逻辑:
+     * 1. 获取当前用户信息
+     * 2. 根据用户类型生成邀请码
+     * 3. 生成包含邀请参数的链接
+     * 4. 生成邀请二维码
+     * 5. 记录邀请码生成日志
+     *
+     * @param  string  $type  邀请码类型(user/coach)
+     * @return array 返回邀请码和二维码信息
+     *
+     * @throws \Exception 生成邀请码失败时抛出异常
      */
-    public function generateInviteCode()
+    public function generateInviteCode(string $type = 'user'): array
     {
         try {
-            // 获取当前用户
+            /** @var MemberUser $user */
+
+            // 1. 获取当前用户
             $user = Auth::user();
 
-            // 生成邀请码
-            $inviteCode = strtoupper(substr(md5($user->id), 0, 6));
+            // 2. 根据类型获取邀请人ID和角色
+            $inviteInfo = $this->getInviteInfo($user, $type);
+            if (! $inviteInfo) {
+                throw new \Exception('无法生成邀请码,用户类型不匹配');
+            }
 
-            // 生成带参数的网页链接
-            $qrContent = config('app.url').'/invite?'.http_build_query([
-                'invite_id' => $user->id,
-                'invite_role' => 'user',
+            // 3. 生成邀请码
+            $inviteCode = $this->generateInviteCodeByType(
+                $type,
+                $inviteInfo['id']
+            );
+
+            // 4. 生成邀请链接和二维码
+            $inviteUrl = $this->generateInviteUrl($inviteCode);
+            $qrCode = $this->generateQrCode($inviteUrl);
+
+            // 5. 记录日志
+            Log::info('生成邀请码', [
+                'user_id' => $user->id,
+                'type' => $type,
+                'invite_id' => $inviteInfo['id'],
                 'invite_code' => $inviteCode,
+                'invite_url' => $inviteUrl,
             ]);
 
-            // 使用QrCode库生成SVG格式的二维码
-            $qrImage = QrCode::format('svg')
-                ->size(200)
-                ->margin(2)
-                ->encoding('UTF-8')
-                ->generate($qrContent);
-
-            // 将SVG转为base64
-            $qrBase64 = base64_encode($qrImage);
-
-            // 记录生成日志
-            Log::info('用户生成邀请码:', [
-                'invite_id' => $user->id,
-                'invite_role' => 'user',
+            return [
                 'invite_code' => $inviteCode,
-                'invite_url' => $qrContent,  // 记录生成的邀请链接
+                'invite_url' => $inviteUrl,
+                'qr_code' => $qrCode,
+            ];
+        } catch (\Exception $e) {
+            Log::error('生成邀请码失败', [
+                'error' => $e->getMessage(),
+                'user_id' => Auth::id(),
+                'type' => $type,
             ]);
+            throw $e;
+        }
+    }
 
-            return response()->json([
-                'code' => 200,
-                'message' => '生成成功',
-                'data' => [
-                    'invite_code' => $inviteCode,
-                    'invite_url' => $qrContent,  // 返回邀请链接
-                    'qr_code' => 'data:image/svg+xml;base64,'.$qrBase64,
-                ],
-            ]);
+    /**
+     * 获取邀请人信息
+     *
+     * 业务逻辑:
+     * 1. 根据类型判断邀请人身份
+     * 2. 用户类型:直接返回用户信息
+     * 3. 技师类型:
+     *    - 验证技师状态
+     *    - 返回技师信息
+     *
+     * @param  \App\Models\MemberUser  $user  当前用户
+     * @param  string  $type  邀请类型(user/coach)
+     * @return array|null 返回邀请人信息,无效类型返回null
+     */
+    private function getInviteInfo(MemberUser $user, string $type): ?array
+    {
+        switch ($type) {
+            case 'user':
+                return [
+                    'id' => $user->id,
+                    'role' => 'user',
+                ];
+            case 'coach':
+                $coach = $user->coach;
+                if ($coach && $coach->state === TechnicianAuthStatus::PASSED) {
+                    return [
+                        'id' => $coach->id,
+                        'role' => 'coach',
+                    ];
+                }
+
+                return null;
+            default:
+                return null;
+        }
+    }
 
-        } catch (\Exception $e) {
-            Log::error('生成邀请码失败: '.$e->getMessage());
+    /**
+     * 根据类型生成邀请码
+     *
+     * 业务逻辑:
+     * 1. 组合类型和ID
+     * 2. 生成格式化的邀请码
+     *
+     * @param  string  $type  邀请类型(user/coach)
+     * @param  int  $id  邀请人ID
+     * @return string 返回格式为 "type_id" 的邀请码
+     */
+    private function generateInviteCodeByType(string $type, int $id): string
+    {
+        return sprintf('%s_%d', $type, $id);
+    }
 
-            return response()->json([
-                'code' => 500,
-                'msg' => '生成二维码失败,请稍后再试。',
-            ]);
-        }
+    /**
+     * 生成邀请链接
+     *
+     * 业务逻辑:
+     * 1. 获取应用URL
+     * 2. 添加邀请码参数
+     * 3. 生成完整的邀请链接
+     *
+     * @param  string  $inviteCode  邀请码
+     * @return string 返回完整的邀请链接
+     */
+    private function generateInviteUrl(string $inviteCode): string
+    {
+        return config('app.url').'/invite?'.http_build_query(['invite_code' => $inviteCode]);
+    }
+
+    /**
+     * 生成二维码
+     *
+     * 业务逻辑:
+     * 1. 使用 QrCode 库生成 SVG 格式二维码
+     * 2. 设置二维码大小和边距
+     * 3. 转换为 base64 编码
+     *
+     * @param  string  $content  二维码内容
+     * @return string 返回 base64 编码的二维码图片
+     */
+    private function generateQrCode(string $content): string
+    {
+        $qrImage = QrCode::format('svg')
+            ->size(200)
+            ->margin(2)
+            ->encoding('UTF-8')
+            ->generate($content);
+
+        return 'data:image/svg+xml;base64,'.base64_encode($qrImage);
     }
 }