marketDistTeamService = $marketDistTeamService; } /** * 获取当前用户信息 * * 业务逻辑: * 1. 通过 Auth 门面获取当前登录用户信息 * 2. 返回用户基本信息 * * @return \Illuminate\Http\JsonResponse 返回用户信息 * * @throws \Exception 获取用户信息失败时抛出异常 */ public function getUserInfo(): MemberUser { try { /** @var MemberUser $user */ // 通过 Auth 门面获取当前登录用户信息 $user = Auth::user(); return $user; } catch (\Exception $e) { // 记录错误日志 Log::error('获取用户信息失败', ['error' => $e->getMessage()]); throw $e; } } /** * 用户注册 * * 业务逻辑: * 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 返回注册结果 * * @throws \Exception 注册过程中出现错误时抛出异常 */ public function register(string $mobile, string $code, ?string $invite_code = null, ?int $invite_id = null, ?string $invite_role = null): array { try { DB::beginTransaction(); // 检查手机号是否已注册 abort_if(MemberUser::where('mobile', $mobile)->exists(), 422, '该手机号已注册'); // 验证短信验证码 abort_if(! $this->verifySmsCode($mobile, $code), 422, '验证码错误或已过期'); // 创建用户 $user = MemberUser::create([ 'mobile' => $mobile, 'password' => bcrypt(substr($mobile, -6)), // 默认密码为手机号后6位 'nickname' => substr_replace($mobile, '****', 3, 4), // 默认昵称为手机号(中间4位隐藏) ]); // 处理邀请关系 if ($invite_code && $invite_role && $invite_id) { $this->marketDistTeamService->createInviteRelation($user, $invite_code, $invite_id, $invite_role); } DB::commit(); return [ 'user_id' => $user->id, 'mobile' => $mobile, 'invite_code' => $user->invite_code, ]; } catch (\Exception $e) { DB::rollBack(); 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 { $cacheKey = "sms_code:{$mobile}"; $cacheCode = Redis::get($cacheKey); if (! $cacheCode || $cacheCode !== $code) { return false; } Redis::del($cacheKey); // 验证成功后删除验证码 return true; } catch (\Exception $e) { Log::error('验证码验证失败', ['error' => $e->getMessage(), 'mobile' => $mobile]); return false; } } /** * 更新当前用户信息 * * 业务逻辑: * 1. 获取当前登录用户 * 2. 更新用户基本信息 * * @param array $data 待更新的用户数据 * @return \Illuminate\Http\JsonResponse 返回更新结果 * * @throws \Exception 更新失败时抛出异常 */ public function updateUserInfo(array $data): MemberUser { try { DB::beginTransaction(); // 更新用户信息 $user = Auth::user(); $user->update($data); DB::commit(); return $user->fresh(); } catch (\Exception $e) { DB::rollBack(); Log::error('更新用户信息失败', ['error' => $e->getMessage(), 'data' => $data]); throw $e; } } /** * 提交用户反馈 * * 业务逻辑: * 1. 创建用户反馈记录 * 2. 保存反馈内容和图片 * 3. 记录用户联系方式(如果提供) * * @param string $content 反馈内容 * @param array $images 反馈图片数组 * @param string|null $contact 联系方式 * @return \Illuminate\Http\JsonResponse 返回提交结果 * * @throws \Exception 提交反馈失败时抛出异常 */ public function feedback(string $content, array $images = [], ?string $contact = null): void //Feedback { try { DB::beginTransaction(); // 创建反馈记录 // $feedback = Feedback::create([ // 'user_id' => Auth::user()->id, // 'content' => $content, // 'images' => $images, // 'contact' => $contact, // ]); DB::commit(); // return $feedback; } catch (\Exception $e) { DB::rollBack(); Log::error('提交反馈失败', ['error' => $e->getMessage(), 'content' => $content]); throw $e; } } /** * 申请成为技师 * * 业务逻辑: * 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, int $gender, string $work_years, string $intention_city, array $portrait_images, ?string $description = null ): CoachInfoRecord { try { DB::beginTransaction(); // 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) { 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(), ]); } /** * 生成用户邀请码 * * 业务逻辑: * 1. 获取当前用户信息 * 2. 根据用户类型生成邀请码 * 3. 生成包含邀请参数的链接 * 4. 生成邀请二维码 * 5. 记录邀请码生成日志 * * @param string $type 邀请码类型(user/coach) * @return array 返回邀请码和二维码信息 * * @throws \Exception 生成邀请码失败时抛出异常 */ public function generateInviteCode(string $type = 'user'): array { try { /** @var MemberUser $user */ // 1. 获取当前用户 $user = Auth::user(); // 2. 根据类型获取邀请人ID和角色 $inviteInfo = $this->getInviteInfo($user, $type); if (! $inviteInfo) { throw new \Exception('无法生成邀请码,用户类型不匹配'); } // 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, ]); return [ 'invite_code' => $inviteCode, 'invite_url' => $inviteUrl, 'qr_code' => $qrCode, ]; } catch (\Exception $e) { Log::error('生成邀请码失败', [ 'error' => $e->getMessage(), 'user_id' => Auth::id(), 'type' => $type, ]); throw $e; } } /** * 获取邀请人信息 * * 业务逻辑: * 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; } } /** * 根据类型生成邀请码 * * 业务逻辑: * 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); } /** * 生成邀请链接 * * 业务逻辑: * 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); } }