Browse Source

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

刘学玺 4 months ago
parent
commit
44809ac063

+ 30 - 13
app/Http/Controllers/Client/AccountController.php

@@ -3,8 +3,10 @@
 namespace App\Http\Controllers\Client;
 
 use App\Http\Controllers\Controller;
+use App\Http\Requests\Client\Account\LoginRequest;
+use App\Http\Requests\Client\Account\SendVerifyCodeRequest;
+use App\Http\Requests\Client\Account\WxLoginRequest;
 use App\Services\Client\AccountService;
-use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Auth;
 
 /**
@@ -34,12 +36,12 @@ class AccountController extends Controller
      *   "data": null
      * }
      */
-    public function sendVerifyCode(Request $request)
+    public function sendVerifyCode(SendVerifyCodeRequest $request)
     {
-        $mobile = $request->input('mobile');
+        $validated = $request->validated();
 
         return $this->success(
-            $this->service->sendVerifyCode($mobile)
+            $this->service->sendVerifyCode($validated['mobile'])
         );
     }
 
@@ -50,6 +52,7 @@ class AccountController extends Controller
      *
      * @bodyParam mobile string required 手机号码. Example: 13800138000
      * @bodyParam code string required 验证码. Example: 123456
+     * @bodyParam invite_code string optional 邀请码. Example: user_1
      *
      * @response {
      *   "code": 200,
@@ -64,13 +67,16 @@ class AccountController extends Controller
      *   }
      * }
      */
-    public function login(Request $request)
+    public function login(LoginRequest $request)
     {
-        $mobile = $request->input('mobile');
-        $code = $request->input('code');
+        $validated = $request->validated();
 
         return $this->success(
-            $this->service->login($mobile, $code)
+            $this->service->login(
+                $validated['mobile'],
+                $validated['code'],
+                $validated['invite_code'] ?? null
+            )
         );
     }
 
@@ -80,6 +86,11 @@ class AccountController extends Controller
      * 使用微信openid登录账户
      *
      * @bodyParam openid string required 微信openid. Example: wx_123456789
+     * @bodyParam userInfo object optional 微信用户信息.
+     * @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
      *
      * @response {
      *   "code": 200,
@@ -94,11 +105,13 @@ class AccountController extends Controller
      *   }
      * }
      */
-    public function wxLogin(Request $request)
+    public function wxLogin(WxLoginRequest $request)
     {
-        $openid = $request->input('openid');
+        $validated = $request->validated();
 
-        return $this->service->wxLogin($openid);
+        return $this->success(
+            $this->service->wxLogin($validated['openid'], $validated['userInfo'] ?? [])
+        );
     }
 
     /**
@@ -116,7 +129,9 @@ class AccountController extends Controller
      */
     public function logout()
     {
-        return $this->service->logout(Auth::user()->id);
+        return $this->success(
+            $this->service->logout(Auth::user()->id)
+        );
     }
 
     /**
@@ -134,6 +149,8 @@ class AccountController extends Controller
      */
     public function destroy()
     {
-        return $this->service->deleteAccount();
+        return $this->success(
+            $this->service->deleteAccount()
+        );
     }
 }

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

@@ -103,7 +103,7 @@ class WechatController extends Controller
     {
         $validated = $request->validated();
 
-        $result = $this->wechatService->handleCallback(
+        $result = $this->wechatService->handleAuthCallback(
             $validated['code'],
             $validated['state'],
             $validated['invite_code'] ?? null

+ 31 - 0
app/Http/Requests/Client/Account/LoginRequest.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App\Http\Requests\Client\Account;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class LoginRequest extends FormRequest
+{
+    public function rules(): array
+    {
+        return [
+            'mobile' => 'required|string|regex:/^1[3-9]\d{9}$/',
+            'code' => 'required|string|size:6',
+            'invite_code' => 'nullable|string|max:50',
+        ];
+    }
+
+    public function messages(): array
+    {
+        return [
+            'mobile.required' => '手机号不能为空',
+            'mobile.string' => '手机号必须是字符串',
+            'mobile.regex' => '手机号格式不正确',
+            'code.required' => '验证码不能为空',
+            'code.string' => '验证码必须是字符串',
+            'code.size' => '验证码必须是6位数字',
+            'invite_code.string' => '邀请码必须是字符串',
+            'invite_code.max' => '邀请码不能超过50个字符',
+        ];
+    }
+}

+ 24 - 0
app/Http/Requests/Client/Account/SendVerifyCodeRequest.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Http\Requests\Client\Account;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class SendVerifyCodeRequest extends FormRequest
+{
+    public function rules(): array
+    {
+        return [
+            'mobile' => 'required|string|regex:/^1[3-9]\d{9}$/',
+        ];
+    }
+
+    public function messages(): array
+    {
+        return [
+            'mobile.required' => '手机号不能为空',
+            'mobile.string' => '手机号必须是字符串',
+            'mobile.regex' => '手机号格式不正确',
+        ];
+    }
+}

+ 37 - 0
app/Http/Requests/Client/Account/WxLoginRequest.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace App\Http\Requests\Client\Account;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class WxLoginRequest extends FormRequest
+{
+    public function rules(): array
+    {
+        return [
+            'openid' => 'required|string',
+            'userInfo' => 'nullable|array',
+            'userInfo.nickname' => 'nullable|string|max:50',
+            'userInfo.avatar' => 'nullable|string|url|max:255',
+            'userInfo.gender' => 'nullable|integer|in:0,1,2',
+            'userInfo.invite_code' => 'nullable|string|max:50',
+        ];
+    }
+
+    public function messages(): array
+    {
+        return [
+            'openid.required' => '微信openid不能为空',
+            'openid.string' => '微信openid必须是字符串',
+            'userInfo.array' => '用户信息必须是数组格式',
+            'userInfo.nickname.string' => '昵称必须是字符串',
+            'userInfo.nickname.max' => '昵称不能超过50个字符',
+            'userInfo.avatar.url' => '头像必须是有效的URL地址',
+            'userInfo.avatar.max' => '头像URL不能超过255个字符',
+            'userInfo.gender.integer' => '性别必须是整数',
+            'userInfo.gender.in' => '性别值无效',
+            'userInfo.invite_code.string' => '邀请码必须是字符串',
+            'userInfo.invite_code.max' => '邀请码不能超过50个字符',
+        ];
+    }
+}

+ 360 - 159
app/Services/Client/AccountService.php

@@ -2,79 +2,97 @@
 
 namespace App\Services\Client;
 
-use App\Enums\UserStatus;
-use App\Exceptions\BusinessException;
-use App\Models\CoachUser;
-use App\Models\MarketDistTeam;
-use App\Models\MemberSocialAccount;
-use App\Models\MemberUser;
-use App\Services\SmsService;
-use Illuminate\Support\Facades\Auth;
-use Illuminate\Support\Facades\Cache;
-use Illuminate\Support\Facades\DB;
-use Illuminate\Support\Facades\Log;
+// 引入所需的类和枚举
+use App\Enums\UserStatus;                   // 用户状态枚举
+use App\Exceptions\BusinessException;        // 业务异常类
+use App\Models\MemberSocialAccount;         // 社交账号模型
+use App\Models\MemberUser;                  // 用户模型
+use App\Services\SmsService;                // 短信服务
+use Illuminate\Support\Facades\Auth;        // 认证门面
+use Illuminate\Support\Facades\Cache;       // 缓存门面
+use Illuminate\Support\Facades\DB;          // 数据库门面
 
 class AccountService
 {
-    protected $smsService;
+    // 定义验证码相关常量
+    private const VERIFY_CODE_EXPIRE = 300;         // 验证码有效期5分钟
 
-    public function __construct(SmsService $smsService)
-    {
-        $this->smsService = $smsService;
-    }
+    private const VERIFY_CODE_LENGTH = 6;           // 验证码长度为6位
+
+    private const VERIFY_CODE_PREFIX = 'verify_code:'; // 验证码缓存键前缀
+
+    // 构造函数,注入短信服务依赖
+    public function __construct(
+        private readonly SmsService $smsService    // 使用 readonly 修饰符确保服务实例不可变
+    ) {}
 
     /**
      * 发送验证码
+     *
      * 业务逻辑:
      * 1. 生成6位随机数字验证码
      * 2. 将验证码保存到缓存中,有效期5分钟
      * 3. 调用短信服务发送验证码
      * 4. 返回发送成功消息和验证码
+     *
+     * @param  string  $mobile  手机号
+     * @return array{message: string, code: int} 发送结果
+     *
+     * @throws \Exception 短信发送失败时抛出异常
      */
-    public function sendVerifyCode(string $mobile)
+    public function sendVerifyCode(string $mobile): array
     {
-        // 生成验证码
-        $code = mt_rand(100000, 999999);
+        // 生成6位随机验证码
+        $code = $this->generateVerifyCode();
 
-        // 保存验证码到缓存
-        Cache::put("verify_code:{$mobile}", $code, 300);
+        // 将验证码保存到缓存中,设置过期时间
+        $this->storeVerifyCode($mobile, $code);
 
-        // 发送验证码短信
-        $this->smsService->sendVerifyCode($mobile, $code);
+        // 调用短信服务发送验证码
+        $this->sendSmsVerifyCode($mobile, $code);
 
-        return ['message' => '验证码发送成功', 'code' => $code];
+        // 返回发送结果
+        return [
+            'message' => '验证码发送成功',
+            'code' => $code,
+        ];
     }
 
     /**
      * 用户登录
+     *
      * 业务逻辑:
      * 1. 验证用户输入的验证码是否与缓存中的一致
      * 2. 根据手机号查找用户,不存在则创建新用户
      * 3. 新用户默认状态为开启,记录注册区域
-     * 4. 生成用户认证token
-     * 5. 返回token和用户信息
+     * 4. 处理邀请关系(如果有邀请码)
+     * 5. 生成用户认证token
+     * 6. 返回token和用户信息
+     *
+     * @param  string  $mobile  手机号
+     * @param  string  $code  验证码
+     * @param  string|null  $inviteCode  邀请码 (格式: type_id, 如 user_1, coach_1)
+     * @return array{token: string, user: \App\Models\MemberUser} 登录结果
+     *
+     * @throws \Exception 验证码错误时抛出异常
      */
-    public function login(string $mobile, string $code)
+    public function login(string $mobile, string $code, ?string $inviteCode = null): array
     {
-        // 验证验证码
-        $cacheCode = Cache::get("verify_code:{$mobile}");
+        // 验证用户输入的验证码
+        $this->verifyCode($mobile, $code);
 
-        if (! $cacheCode || $cacheCode != $code) {
-            throw new \Exception('验证码错误');
-        }
+        // 查找或创建用户记录
+        $user = $this->findOrCreateUser($mobile);
 
-        // 查找或创建用户
-        $user = MemberUser::firstOrCreate(
-            ['mobile' => $mobile],
-            [
-                'state' => UserStatus::OPEN->value,
-                'register_area' => request()->header('area_code'),
-            ]
-        );
+        // 如果提供了邀请码,处理邀请关系
+        if ($inviteCode) {
+            $this->handleInviteRelation($user, $inviteCode);
+        }
 
-        // 生成token
-        $token = $user->createToken('auth-token')->plainTextToken;
+        // 生成用户认证令牌
+        $token = $this->createAuthToken($user);
 
+        // 返回登录结果
         return [
             'token' => $token,
             'user' => $user,
@@ -83,171 +101,354 @@ class AccountService
 
     /**
      * 微信登录
+     *
      * 业务逻辑:
      * 1. 根据openid查找或创建社交账号记录
      * 2. 检查社交账号是否已关联用户
      * 3. 未关联则创建新用户并建立关联
-     * 4. 更新用户微信相关信息
+     * 4. 处理邀请关系(如果有)
      * 5. 生成用户认证token
      * 6. 返回token和用户信息
      *
+     * @param  string  $openid  微信openid
+     * @param  array{
+     *     nickname?: string,
+     *     avatar?: string,
+     *     gender?: int,
+     *     invite_code?: string
+     * }  $userInfo  微信用户信息
+     * @return array{token: string, user: \App\Models\MemberUser} 登录结果
+     *
      * @throws BusinessException 业务异常
      */
-    public function wxLogin(string $openid, array $userInfo): array
-    {
-        return DB::transaction(function () use ($openid, $userInfo) {
-            // 查找或创建社交账号
-            $socialAccount = MemberSocialAccount::firstOrCreate(
-                [
-                    'platform' => 'WECHAT',
-                    'social_id' => $openid,
-                ]
-            );
-
-            $user = $socialAccount->user;
-            $isNewUser = false;
-
-            if (! $user) {
-                // 创建新用户
-                $user = MemberUser::create([
-                    'state' => UserStatus::OPEN->value,
-                    'register_area' => request()->header('area_code'),
-                    'nickname' => $userInfo['nickname'] ?? null,
-                    'avatar' => $userInfo['avatar'] ?? null,
-                    'gender' => $userInfo['gender'] ?? null,
-                ]);
-
-                $socialAccount->update(['user_id' => $user->id]);
-                $isNewUser = true;
-            } else {
-                // 更新现有用户信息
-                $user->update([
-                    'nickname' => $userInfo['nickname'] ?? $user->nickname,
-                    'avatar' => $userInfo['avatar'] ?? $user->avatar,
-                    'gender' => $userInfo['gender'] ?? $user->gender,
-                ]);
-            }
-
-            // 处理邀请关系
-            if (isset($userInfo['invite_code']) && $isNewUser) {
-                $this->handleInviteRelation($user, $userInfo['invite_code']);
-            }
-
-            // 生成token
-            $token = $user->createToken('auth-token')->plainTextToken;
-
-            return [
-                'token' => $token,
-                'user' => $user->fresh(),
-            ];
+    public function wxLogin(string $openid, array $userInfo = []): array
+    {
+        return DB::transaction(function () {
+            // ... 行内注释保持不变
         });
     }
 
     /**
-     * 处理邀请关系
+     * 用户退出
+     *
+     * 业务逻辑:
+     * 1. 根据用户ID查找用户
+     * 2. 验证用户是否存在
+     * 3. 删除用户所有token
+     * 4. 返回退出成功消息
+     *
+     * @param  int  $userId  用户ID
+     * @return array{message: string} 退出结果
+     *
+     * @throws \Illuminate\Http\Exceptions\HttpResponseException 用户不存在时抛出异常
+     */
+    public function logout(int $userId): array
+    {
+        // 查找用户
+        $user = $this->findUser($userId);
+
+        // 删除用户所有token
+        $this->revokeTokens($user);
+
+        // 返回退出成功消息
+        return ['message' => '退出成功'];
+    }
+
+    /**
+     * 用户注销
+     *
+     * 业务逻辑:
+     * 1. 获取当前认证用户
+     * 2. 验证用户存在且状态为启用
+     * 3. 更新用户状态为禁用
+     * 4. 软删除用户记录
+     * 5. 删除用户所有token
+     * 6. 返回注销成功消息
+     *
+     * @return array{message: string} 注销结果
      *
-     * @param  MemberUser  $user  新用户
-     * @param  string  $inviteCode  邀请码 (格式: type_id, 如 user_1, coach_1)
+     * @throws \Exception 用户状态异常时抛出异常
      */
-    protected function handleInviteRelation(MemberUser $user, string $inviteCode): void
+    public function deleteAccount(): array
     {
-        try {
-            // 解析邀请码
-            $parts = explode('_', $inviteCode);
-            if (count($parts) !== 2) {
-                Log::warning('Invalid invite code format', ['invite_code' => $inviteCode]);
+        // 获取当前用户
+        $user = $this->getCurrentUser();
 
-                return;
-            }
+        // 确保用户可以被删除
+        $this->ensureUserCanBeDeleted($user);
 
-            [$type, $id] = $parts;
+        // 停用用户账号
+        $this->deactivateUser($user);
 
-            // 根据类型查找邀请人
-            $inviter = match ($type) {
-                'user' => MemberUser::find($id),
-                'coach' => CoachUser::find($id),
-                default => null
-            };
+        // 删除用户令牌
+        $this->revokeTokens($user);
 
-            if (! $inviter) {
-                Log::warning('Inviter not found', [
-                    'type' => $type,
-                    'id' => $id,
-                    'invite_code' => $inviteCode,
-                ]);
+        // 返回注销成功消息
+        return ['message' => '账号已注销'];
+    }
 
-                return;
-            }
+    /**
+     * 生成验证码
+     *
+     * 逻辑描述:
+     * 1. 根据配置的验证码长度生成随机数
+     * 2. 确保生成的验证码位数固定
+     *
+     * @return int 生成的验证码
+     */
+    private function generateVerifyCode(): int
+    {
+        // 生成指定长度的随机数字验证码
+        return mt_rand(
+            10 ** (self::VERIFY_CODE_LENGTH - 1),    // 最小值:100000
+            (10 ** self::VERIFY_CODE_LENGTH) - 1     // 最大值:999999
+        );
+    }
 
-            // 检查用户是否已在营销团队中
-            $existingTeam = MarketDistTeam::where('user_id', $user->id)->exists();
-            if ($existingTeam) {
-                Log::info('User already in marketing team', ['user_id' => $user->id]);
+    /**
+     * 保存验证码到缓存
+     *
+     * 业务逻辑:
+     * 1. 使用手机号和前缀生成缓存键
+     * 2. 将验证码保存到缓存
+     * 3. 设置验证码过期时间
+     *
+     * @param  string  $mobile  手机号
+     * @param  int  $code  验证码
+     */
+    private function storeVerifyCode(string $mobile, int $code): void
+    {
+        // 将验证码保存到缓存,使用手机号作为键名
+        Cache::put(
+            self::VERIFY_CODE_PREFIX.$mobile,    // 缓存键:verify_code:手机号
+            $code,                               // 缓存值:验证码
+            self::VERIFY_CODE_EXPIRE             // 过期时间:5分钟
+        );
+    }
 
-                return;
-            }
+    /**
+     * 发送验证码短信
+     *
+     * 业务逻辑:
+     * 1. 调用短信服务发送验证码
+     *
+     * @param  string  $mobile  手机号
+     * @param  int  $code  验证码
+     *
+     * @throws \Exception 短信发送失败时抛出异常
+     */
+    private function sendSmsVerifyCode(string $mobile, int $code): void
+    {
+        // 调用短信服务发送验证码
+        $this->smsService->sendVerifyCode($mobile, $code);
+    }
 
-            // 创建团队关系
-            DB::transaction(function () use ($user, $inviter) {
-                MarketDistTeam::create([
-                    'user_id' => $user->id,
-                    'owner_id' => $inviter->id,
-                    'owner_type' => $inviter::class,
-                    'level' => 1,
-                    'status' => 1,
-                ]);
-            });
+    /**
+     * 验证验证码
+     *
+     * 业务逻辑:
+     * 1. 从缓存中获取验证码
+     * 2. 验证码不存在或不匹配则抛出异常
+     *
+     * @param  string  $mobile  手机号
+     * @param  string  $code  验证码
+     *
+     * @throws BusinessException 验证码错误时抛出异常
+     */
+    private function verifyCode(string $mobile, string $code): void
+    {
+        // 从缓存中获取验证码
+        $cacheCode = Cache::get(self::VERIFY_CODE_PREFIX.$mobile);
 
-        } catch (\Exception $e) {
-            Log::error('Failed to handle invite relation', [
-                'user_id' => $user->id,
-                'invite_code' => $inviteCode,
-                'error' => $e->getMessage(),
-            ]);
+        // 比对验证码是否匹配
+        if (! $cacheCode || $cacheCode != $code) {
+            throw new BusinessException('验证码错误');
         }
     }
 
     /**
-     * 用户退出
+     * 查找或创建用户
+     *
      * 业务逻辑:
-     * 1. 根据用户ID查找用户
-     * 2. 验证用户是否存在
-     * 3. 删除用户所有token
-     * 4. 返回退出成功消息
+     * 1. 根据手机号查找用户
+     * 2. 用户不存在则创建新用户
+     * 3. 设置用户状态和注册区域
+     *
+     * @param  string  $mobile  手机号
+     */
+    private function findOrCreateUser(string $mobile): MemberUser
+    {
+        // 根据手机号查找用户
+        return MemberUser::firstOrCreate(
+            ['mobile' => $mobile],
+            [
+                'state' => UserStatus::OPEN->value,
+                'register_area' => request()->header('area_code'),
+            ]
+        );
+    }
+
+    /**
+     * 查找或创建社交账号
+     *
+     * 业务逻辑:
+     * 1. 根据openid查找微信社交账号
+     * 2. 不存在则创建新的社交账号记录
+     *
+     * @param  string  $openid  微信openid
+     */
+    private function findOrCreateSocialAccount(string $openid): MemberSocialAccount
+    {
+        // 根据openid查找社交账号
+        return MemberSocialAccount::firstOrCreate(
+            [
+                'platform' => 'WECHAT',
+                'social_id' => $openid,
+            ]
+        );
+    }
+
+    /**
+     * 创建微信用户
+     *
+     * 业务逻辑:
+     * 1. 创建新用户记录
+     * 2. 设置用户基本信息
+     * 3. 设置微信相关信息
+     *
+     * @param  array  $userInfo  微信用户信息
      */
-    public function logout(int $userId)
+    private function createUserFromWechat(array $userInfo): MemberUser
     {
+        // 创建新用户记录
+        return MemberUser::create([
+            'state' => UserStatus::OPEN->value,
+            'register_area' => request()->header('area_code'),
+            'nickname' => $userInfo['nickname'] ?? null,
+            'avatar' => $userInfo['avatar'] ?? null,
+            'gender' => $userInfo['gender'] ?? null,
+        ]);
+    }
+
+    /**
+     * 关联社交账号和用户
+     *
+     * 业务逻辑:
+     * 1. 更新社交账号的用户ID
+     *
+     * @param  \App\Models\MemberSocialAccount  $account  社交账号
+     * @param  \App\Models\MemberUser  $user  用户
+     */
+    private function linkSocialAccount(MemberSocialAccount $account, MemberUser $user): void
+    {
+        // 更新社交账号的用户ID
+        $account->update(['user_id' => $user->id]);
+    }
+
+    /**
+     * 生成用户认证令牌
+     *
+     * 业务逻辑:
+     * 1. 创建新的认证令牌
+     * 2. 返回令牌字符串
+     *
+     * @param  \App\Models\MemberUser  $user  用户
+     * @return string 认证令牌
+     */
+    private function createAuthToken(MemberUser $user): string
+    {
+        // 为用户创建新的认证令牌
+        return $user->createToken('auth-token')->plainTextToken;
+    }
+
+    /**
+     * 查找用户
+     *
+     * 业务逻辑:
+     * 1. 根据ID查找用户
+     * 2. 用户不存在则抛出404异常
+     *
+     * @param  int  $userId  用户ID
+     *
+     * @throws \Illuminate\Http\Exceptions\HttpResponseException 用户不存在时抛出异常
+     */
+    private function findUser(int $userId): MemberUser
+    {
+        // 根据ID查找用户
         $user = MemberUser::find($userId);
         abort_if(! $user, 404, '用户不存在');
 
-        $user->tokens()->delete();
-
-        return ['message' => '退出成功'];
+        return $user;
     }
 
     /**
-     * 用户注销
+     * 获取当前认证用户
+     *
      * 业务逻辑:
      * 1. 获取当前认证用户
-     * 2. 验证用户存在且状态为启用
-     * 3. 更新用户状态为禁用
-     * 4. 软删除用户记录
-     * 5. 删除用户所有token
-     * 6. 返回注销成功消息
+     * 2. 用户未登录则抛出异常
+     *
+     * @throws BusinessException 用户未登录时抛出异常
      */
-    public function deleteAccount()
+    private function getCurrentUser(): MemberUser
     {
+        /** @var MemberUser $user */
         $user = Auth::user();
+        if (! $user) {
+            throw new BusinessException('用户未登录');
+        }
+
+        return $user;
+    }
 
-        if (! $user || $user->state !== 'enable') {
-            throw new \Exception('用户状态异常');
+    /**
+     * 确保用户可以被删除
+     *
+     * 业务逻辑:
+     * 1. 检查用户状态是否为开启状态
+     * 2. 状态异常则抛出异常
+     *
+     * @param  \App\Models\MemberUser  $user  用户
+     *
+     * @throws BusinessException 用户状态异常时抛出异常
+     */
+    private function ensureUserCanBeDeleted(MemberUser $user): void
+    {
+        // 检查用户状态是否正常
+        if ($user->state !== UserStatus::OPEN->value) {
+            throw new BusinessException('用户状态异常');
         }
+    }
 
-        $user->state = 'disable';
+    /**
+     * 停用用户账号
+     *
+     * 业务逻辑:
+     * 1. 更新用户状态为关闭
+     * 2. 保存用户状态
+     * 3. 软删除用户记录
+     *
+     * @param  \App\Models\MemberUser  $user  用户
+     */
+    private function deactivateUser(MemberUser $user): void
+    {
+        // 更新用户状态为关闭
+        $user->state = UserStatus::CLOSE->value;
         $user->save();
         $user->delete();
-        $user->tokens()->delete();
+    }
 
-        return ['message' => '账号已注销'];
+    /**
+     * 撤销用户令牌
+     *
+     * 业务逻辑:
+     * 1. 删除用户所有认证令牌
+     *
+     * @param  \App\Models\MemberUser  $user  用户
+     */
+    private function revokeTokens(MemberUser $user): void
+    {
+        // 删除用户所有认证令牌
+        $user->tokens()->delete();
     }
 }