generateVerifyCode(); // 将验证码存入缓存,设置5分钟过期时间 $this->storeVerifyCode($mobile, $code); // 调用短信服务发送验证码到用户手机 $this->sendSmsVerifyCode($mobile, $code); // 返回成功消息和验证码(用于测试环境) return [ 'message' => '验证码发送成功', 'code' => $code, ]; } /** * 用户登录 * 业务逻辑: * 1. 验证用户输入的验证码是否与缓存中的一致 * 2. 根据手机号查找用户,不存在则创建新用户 * 3. 新用户默认状态为开启,记录注册区域 * 4. 生成用户认证token * 5. 返回token和用户信息 * * @param string $mobile 手机号 * @param string $code 验证码 * @return array 包含token和用户信息 * * @throws \Exception 验证码错误时抛出异常 */ public function login(string $mobile, string $code, ?string $inviteCode = null): array { // 验证用户输入的验证码是否正确 $this->verifyCode($mobile, $code); // 根据手机号查找用户,不存在则创建新用户 $user = $this->findOrCreateUser($mobile); // 如果提供了邀请码,处理用户的邀请关系 if ($inviteCode) { $this->handleInviteRelation($user, $inviteCode); } // 为用户生成新的认证令牌 $token = $this->createAuthToken($user); // 返回令牌和用户信息 return [ 'token' => $token, 'user' => $user, ]; } /** * 微信登录 * 业务逻辑: * 1. 根据openid查找或创建社交账号记录 * 2. 检查社交账号是否已关联用户 * 3. 未关联则创建新用户并建立关联 * 4. 更新用户微信相关信息(昵称、头像、性别) * 5. 处理邀请关系(如果是新用户且有邀请码) * 6. 生成用户认证token * 7. 返回token和用户信息 * * @param string $openid 微信openid * @param array $userInfo 用户信息,包含 nickname, avatar, gender, invite_code(可选) * @return array 包含token和用户信息 * * @throws BusinessException 业务异常 */ public function wxLogin(string $openid, array $userInfo = []): array { // 使用事务确保数据一致性 return DB::transaction(function () use ($openid, $userInfo) { // 查找或创建微信社交账号记录 $socialAccount = $this->findOrCreateSocialAccount($openid); // 获取关联的用户,如果没有则创建新用户 $user = $socialAccount->user ?? $this->createUserFromWechat($userInfo); // 如果社交账号未关联用户,建立关联关系 if (! $socialAccount->user_id) { $this->linkSocialAccount($socialAccount, $user); } // 处理邀请关系(如果提供了邀请码) if (! empty($userInfo['invite_code'])) { $this->handleInviteRelation($user, $userInfo['invite_code']); } // 生成用户认证令牌 $token = $this->createAuthToken($user); // 返回令牌和刷新后的用户信息 return [ 'token' => $token, 'user' => $user->fresh(), // 重新获取用户信息,确保数据最新 'openid' => $openid, // 添加openid到返回数据中 ]; }); } /** * 用户退出 * 业务逻辑: * 1. 根据��户ID查找用户 * 2. 验证用户是否存在,不存在则中断请求 * 3. 删除用户所有token * 4. 返回退出成功消息 * * @param int $userId 用户ID * @return array 退出结果消息 */ public function logout(int $userId): array { // 查找并验证用户是否存在 $user = $this->findUser($userId); // 删除用户的所有认证令牌 $this->revokeTokens($user); // 返回退出成功的消息 return ['message' => '退出成功']; } /** * 用户注销 * 业务逻辑: * 1. 获取当前认证用户 * 2. 验证用户存在且状态为启用 * 3. 更新用户状态为禁用 * 4. 软删除用户记录 * 5. 删除用户所有token * 6. 返回注销成功消息 * * @return array 注销结果消息 * * @throws \Exception 用户状态异常时抛出异常 */ public function deleteAccount(): array { // 获取并验证当前登录用户 $user = $this->getCurrentUser(); // 验证用户是否可以被注销 $this->ensureUserCanBeDeleted($user); // 停用用户账号并软删除 $this->deactivateUser($user); // 删除用户的所有认证令牌 $this->revokeTokens($user); // 返回注销成功的消息 return ['message' => '账号已注销']; } /** * 生成验证码 * * 逻辑描述: * 1. 根据配置的验证码长度生成随机数 * 2. 确保生成的验证码位数固定 * * @return int 生成的验证码 */ private function generateVerifyCode(): int { // 计算最小值(例如: 6位数的最小值为100000) $min = 10 ** (self::VERIFY_CODE_LENGTH - 1); // 计算最大值(例如: 6位数的最大值为999999) $max = (10 ** self::VERIFY_CODE_LENGTH) - 1; // 生成指定范围内的随机数作为验证码 return mt_rand($min, $max); } /** * 保存验证码到缓存 * * 逻辑描述: * 1. 使用手机号作为缓存key * 2. 设置验证码的过期时间 * * @param string $mobile 手机号 * @param int $code 验证码 */ private function storeVerifyCode(string $mobile, int $code): void { // 构建缓存键名(verify_code:手机号) $key = self::VERIFY_CODE_PREFIX . $mobile; // 将验证码存入缓存,并设置过期时间 Cache::put($key, $code, self::VERIFY_CODE_EXPIRE); } /** * 发送验证码短信 * * 逻辑���述: * 1. 调用短信服务发送验证码 * * @param string $mobile 手机号 * @param int $code 验证码 * * @throws \Exception 短信发送失败时抛出异常 */ private function sendSmsVerifyCode(string $mobile, int $code): void { try { // 调用短信服务发送验证码短信 $this->smsService->sendVerifyCode($mobile, $code); } catch (\Exception $e) { // 记录发送失败的日志 Log::error('发送验证码失败', [ 'mobile' => $mobile, 'code' => $code, 'error' => $e->getMessage(), ]); // 重新抛出异常 throw $e; } } /** * 验证验证码 * * 逻辑描述: * 1. 从缓存中获取验证码 * 2. 比对验证码是否匹配 * 3. 验证失败则抛出异常 * * @param string $mobile 手机号 * @param string $code 验证码 * * @throws BusinessException 验证码错误时抛出异常 */ private function verifyCode(string $mobile, string $code): void { // 使用手机号和前缀构建缓存键,获取存储的验证码 $cacheCode = Cache::get(self::VERIFY_CODE_PREFIX . $mobile); // 验证码不存在或不匹配则抛出业务异常 if (! $cacheCode || $cacheCode != $code) { throw new BusinessException('验证码错误'); } } /** * 查找或创建用户 * * 逻辑描述: * 1. 根据手机号查找用户 * 2. 不存在则创建新用户 * 3. 设置用户状态和注册区域 * * @param string $mobile 手机号 */ private function findOrCreateUser(string $mobile): MemberUser { // 使用 firstOrCreate 方法查找或创建用户 return MemberUser::firstOrCreate( // 查询条件:根据手机号查找 ['mobile' => $mobile], // 创建数据:设置用户状态和注册区域 [ 'state' => UserStatus::OPEN->value, // 设置用户状态为开启 'register_area' => request()->header('area_code'), // 从请求头获取注册区域 ] ); } /** * 查找或创建社交账号 * * 逻辑描述: * 1. 先查找是否存在社交账号 * 2. 不存在则创建新记录 * * @param string $openid 微信openid */ private function findOrCreateSocialAccount(string $openid): MemberSocialAccount { // 先查找是否存在社交账号 $socialAccount = MemberSocialAccount::where([ 'platform' => 'WECHAT', 'social_id' => $openid, ])->first(); // 如果不存在,则创建新记录 if (!$socialAccount) { $socialAccount = MemberSocialAccount::create([ 'platform' => 'WECHAT', 'social_id' => $openid, ]); } return $socialAccount; } /** * 创建微信用户 * * 逻辑描述: * 1. 创建新用户记录 * 2. 设置用户基本信息 * 3. 设置微信相关信息 * * @param array $userInfo 微信用户信息 */ private function createUserFromWechat(array $userInfo): MemberUser { // 创建新的用户记录 return MemberUser::create([ 'state' => UserStatus::OPEN->value, // 设置用户状态为开启 'register_area' => request()->header('area_code') ?? '0000', // 从请求头获取注册区域 'nickname' => $userInfo['nickname'] ?? null, // 设置微信昵称,可选 'avatar' => $userInfo['avatar'] ?? null, // 设置微信头像,可选 'gender' => $userInfo['gender'] ?? 0, // 设置性别,可选 ]); } /** * 关联社交账号和用户 * * 逻辑描述: * 1. 更新社交账号用户ID * * @param \App\Models\MemberSocialAccount $account 社交账号 * @param \App\Models\MemberUser $user 用户 */ private function linkSocialAccount(MemberSocialAccount $account, MemberUser $user): void { // 更新社交账号记录,建立与用户的关联关系 $account->update([ 'user_id' => $user->id, // 设置关联的用户ID ]); } /** * 生成认证令牌 * * 逻辑描述: * 1. 为用户创建新的认证令牌 * * @param \App\Models\MemberUser $user 用户 * @return string 令牌字符串 */ private function createAuthToken(MemberUser $user): string { // 使用 Sanctum 为用户创建新的认证令牌 return $user->createToken('auth-token') // 创建名为 auth-token 的令牌 ->plainTextToken; // 返回令牌的明文字符串 } /** * 查找用户 * * 逻辑描述: * 1. 根据ID查找用户 * 2. 用户不存在则抛出异常 * * @param int $userId 用户ID * * @throws \Illuminate\Http\Exceptions\HttpResponseException 用户不存在时抛出异常 */ private function findUser(int $userId): MemberUser { // 根据用户ID查找用户记录 $user = MemberUser::find($userId); // 用户不存在则抛出404异常 abort_if(! $user, 404, '用户不存在'); // 返回找到的用户 return $user; } /** * 获取当前用户 * * 逻辑描述: * 1. 获取当前认证用户 * 2. 用户未登录则抛出异常 * * @throws BusinessException 用户未登录时抛出异常 */ private function getCurrentUser(): MemberUser { /** @var MemberUser $user */ // 从认证系统获取当前登录用户 $user = Auth::user(); // 用户未登录则抛出业务异常 if (! $user) { throw new BusinessException('用户未登录'); } // 返回当前用户 return $user; } /** * 确保用户可以被删除 * * 逻辑描述: * 1. 检查用户状态是否为开启状态 * 2. 状态异常则抛出业务异常 * * @param \App\Models\MemberUser $user 用户 * * @throws BusinessException 用户状态异常时抛出异常 */ private function ensureUserCanBeDeleted(MemberUser $user): void { // 检查用户状态是否为开启状态 if ($user->state !== UserStatus::OPEN->value) { // 状态异常则抛出业务异常 throw new BusinessException('用户状态异常'); } } /** * 停用用户 * * 逻辑描述: * 1. 更新用户状态为关闭状态 * 2. 保存用户状态更新 * 3. 软删除用户记录(不会真正删除数据) * * @param \App\Models\MemberUser $user 用户 */ private function deactivateUser(MemberUser $user): void { // 更新用户状态为关闭状态 $user->state = UserStatus::CLOSE->value; // 保存用户状态更新 $user->save(); // 软删除用户记录(不会真正删除数据) $user->delete(); } /** * 撤销用户令牌 * * 逻辑描述: * 1. 删除用户所有的认证令牌记录 * * @param \App\Models\MemberUser $user 用户 */ private function revokeTokens(MemberUser $user): void { // 删除用户所有的认证令牌记录 $user->tokens()->delete(); } /** * 处理邀请关系 * * 逻辑描述: * 1. 解析邀请码格式(格式为: type_id) * 2. 验证邀请码格式是否正确 * 3. 分解邀请码获取类型和ID * 4. 根据类型查找邀请人 * 5. 邀请人不存在则记录日���并返回 * 6. 检查用户是否已在营销团队中 * 7. 使用事务创建团队关系 * * @param \App\Models\MemberUser $user 用户 * @param string $inviteCode 邀请码 */ private function handleInviteRelation(MemberUser $user, string $inviteCode): void { try { // 解析邀请码格式(格式为: type_id) $parts = explode('_', $inviteCode); // 验证邀请码格式是否正确 if (count($parts) !== 2) { // 记录无效邀请码的警告日志 Log::warning('Invalid invite code format', ['invite_code' => $inviteCode]); return; } // 分解邀请码获取类型和ID [$type, $id] = $parts; // 根据类型查找邀请人 $inviter = match ($type) { 'user' => MemberUser::find($id), // 用户邀请 'coach' => CoachUser::find($id), // 技师邀请 default => null }; // 邀请人不存在则记录日志并返回 if (! $inviter) { Log::warning('Inviter not found', [ 'type' => $type, 'id' => $id, 'invite_code' => $inviteCode, ]); return; } // 检查用户是否已在营销团队中 $existingTeam = MarketDistTeam::where('user_id', $user->id)->exists(); if ($existingTeam) { // 记录用户已在团队中的信息日志 Log::info('User already in marketing team', ['user_id' => $user->id]); return; } // 使用事务创建团队关系 DB::transaction(function () use ($user, $inviter) { // 创建营销团队成员记录 MarketDistTeam::create([ 'user_id' => $user->id, // 被邀请用户ID 'owner_id' => $inviter->id, // 邀请人ID 'owner_type' => $inviter::class, // 邀请人类型(用户/技师) 'level' => 1, // 设置团队层级为1 'status' => 1, // 设置状态为有效 ]); }); } catch (\Exception $e) { // 记录处理邀请关系时的错误日志 Log::error('Failed to handle invite relation', [ 'user_id' => $user->id, 'invite_code' => $inviteCode, 'error' => $e->getMessage(), ]); } } /** * 绑定/修改手机号 * 业务逻辑: * 1. 验证用户输入的验证码是否正确 * 2. 检查新手机号是否已被其他用户使用 * 3. 更新用户手机号 * 4. 返回更新后的用户信息 * * @param string $mobile 新手机号 * @param string $code 验证码 * @return array 包含成功消息和更新后的用户信息 * * @throws BusinessException 手机号已被使用时抛出异常 */ public function bindMobile(string $mobile, string $code): array { // 使用事务确保数据一致性 return DB::transaction(function () use ($mobile, $code) { // 验证验证码 $this->verifyCode($mobile, $code); // 获取当前用户 $user = $this->getCurrentUser(); // 检查手机号是否已被其他用户使用 $existingUser = MemberUser::where('mobile', $mobile) ->where('id', '!=', $user->id) ->exists(); if ($existingUser) { throw new BusinessException('手机号已被其他用户使用', 409); } // 更新用户手机号 $user->mobile = $mobile; $user->save(); // 返回成功消息和更新后的用户信息 return [ 'message' => '手机号绑定成功', 'user' => $user->fresh(), ]; }); } }