request->post('guest_key', ''))); if (!preg_match('/^[a-f0-9]{64}$/', $guestKey)) { return $this->error('游客标识无效,请刷新页面重试', 422); } // Only persist a one-way fingerprint; the random browser key remains the credential. $fingerprint = hash('sha256', $guestKey); $username = 'guest_' . substr($fingerprint, 0, 32); $user = User::where('username', $username)->find(); if (!$user) { $roleId = Role::where('slug', 'user')->value('id'); $membershipId = MembershipLevel::where('slug', 'free')->value('id') ?: 1; try { $user = User::create([ 'username' => $username, 'email' => $username . '@guest.local', 'password_hash' => password_hash(bin2hex(random_bytes(32)), PASSWORD_BCRYPT), 'nickname' => '访客', 'role' => 'user', 'role_id' => $roleId ?: null, 'membership_level_id' => $membershipId, 'status' => 'active', ]); } catch (\Throwable $exception) { // Two tabs may initialize the same browser guest at the same time. $user = User::where('username', $username)->find(); if (!$user) { throw $exception; } } } if ($user->status !== 'active') { return $this->error('游客访问暂不可用', 403); } $user->save(['last_login_at' => date('Y-m-d H:i:s')]); $token = JwtService::generateToken(['user_id' => $user->id]); return $this->success([ 'token' => $token, 'user' => $this->formatUser($user->id), ], '已进入游客模式'); } public function register() { $allow = SettingsService::get('allow_register', true); if ($allow !== true && $allow !== 'true') { return $this->error('当前不允许注册'); } $input = $this->request->post(); $username = trim($input['username'] ?? ''); $email = trim($input['email'] ?? ''); $password = $input['password'] ?? ''; $invitationCode = strtoupper(preg_replace('/\s+/', '', trim((string) ($input['invitation_code'] ?? '')))); if (strlen($username) < 3 || strlen($username) > 50) { return $this->error('用户名长度需 3-50 个字符'); } if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { return $this->error('邮箱格式不正确'); } if (strlen($password) < 6) { return $this->error('密码至少 6 位'); } if ($invitationCode === '') { return $this->error('请输入邀请码', 422); } if (User::where('username', $username)->whereOr('email', $email)->find()) { return $this->error('用户名或邮箱已存在'); } $defaultRoleId = Role::where('slug', 'user')->value('id'); $membershipId = MembershipLevel::where('slug', 'free')->value('id') ?: 1; $user = Db::transaction(function () use ( $invitationCode, $username, $email, $password, $defaultRoleId, $membershipId ) { $invitation = InvitationCode::where('code', $invitationCode)->lock(true)->find(); if (!$invitation) { $this->abortRegistration('邀请码不存在', 422); } if ($invitation->status !== 'active') { $this->abortRegistration($invitation->status === 'used' ? '邀请码已被使用' : '邀请码已作废', 422); } if ($invitation->expires_at && strtotime((string) $invitation->expires_at) <= time()) { $this->abortRegistration('邀请码已过期', 422); } if (User::where('username', $username)->whereOr('email', $email)->find()) { $this->abortRegistration('用户名或邮箱已存在', 422); } $user = User::create([ 'username' => $username, 'email' => $email, 'password_hash' => password_hash($password, PASSWORD_BCRYPT), 'nickname' => $username, 'role' => 'user', 'role_id' => $defaultRoleId ?: null, 'department_id' => $invitation->department_id ?: null, 'membership_level_id' => $membershipId, 'status' => 'active', ]); $invitation->save([ 'status' => 'used', 'used_by' => $user->id, 'used_at' => date('Y-m-d H:i:s'), ]); return $user; }); $token = JwtService::generateToken(['user_id' => $user->id]); return $this->success([ 'token' => $token, 'user' => $this->formatUser($user->id), ], '注册成功'); } public function login() { $input = $this->request->post(); $account = trim($input['account'] ?? ''); $password = $input['password'] ?? ''; if (!$account || !$password) { return $this->error('请输入账号和密码'); } $user = User::where(function ($query) use ($account) { $query->where('username', $account)->whereOr('email', $account); })->where('status', 'active')->find(); if (!$user || !password_verify($password, $user->password_hash)) { return $this->error('账号或密码错误', 401); } $user->save(['last_login_at' => date('Y-m-d H:i:s')]); $token = JwtService::generateToken(['user_id' => $user->id]); return $this->success([ 'token' => $token, 'user' => $this->formatUser($user->id), ], '登录成功'); } public function me() { return $this->success($this->authUser()); } public function updateProfile() { $user = $this->authUser(); $nickname = trim($this->request->put('nickname', '')); if ($nickname) { User::where('id', $user['id'])->update(['nickname' => $nickname]); } return $this->success($this->formatUser($user['id'])); } public function logout() { return $this->success(null, '已退出'); } private function formatUser(int $userId): array { return UserContextService::formatPublicUser($userId); } private function abortRegistration(string $message, int $httpCode): never { throw new HttpResponseException(json([ 'code' => 1, 'message' => $message, 'data' => null, ], $httpCode)); } }