validate([ 'username' => ['required', 'string'], 'password' => ['required', 'string'], ]); $user = User::with('venues:id,name')->where('username', $data['username'])->first(); if (! $user || ! $user->is_active || ! Hash::check($data['password'], $user->password)) { $this->recordLoginAudit( $request, null, 422, '登录失败(账号或密码错误)', ['username' => $data['username']] ); return response()->json(['message' => '账号或密码错误'], 422); } // 移动端核销登录:签发更长有效期的 token(Sanctum 仍会在 expires_at 到期后失效) $isH5Verify = $request->input('client') === 'h5_verify'; if ($isH5Verify && ! $user->isSuperAdmin()) { $this->recordLoginAudit( $request, $user, 403, '登录失败(核销端仅限超级管理员账号)', ['username' => $data['username'], 'client' => 'h5_verify'] ); return response()->json(['message' => '场馆管理员请使用活动专用核销链接与账号登录'], 403); } $expiresAt = $isH5Verify ? now()->addMonths(6) : null; $tokenName = $isH5Verify ? 'h5-verify' : 'admin-token'; $token = $user->createToken($tokenName, ['*'], $expiresAt)->plainTextToken; $this->recordLoginAudit( $request, $user, 200, '登录('.$user->username.')', [ 'username' => $data['username'], 'client' => $request->input('client'), ] ); return response()->json([ 'token' => $token, 'user' => [ 'id' => $user->id, 'username' => $user->username, 'name' => $user->name, 'role' => $user->role, 'venues' => $user->venues, 'full_admin_access' => $user->isSuperAdmin(), ], ]); } public function logout(Request $request): JsonResponse { $request->user()?->currentAccessToken()?->delete(); return response()->json(['message' => '已退出登录']); } /** * @param array $payload */ private function recordLoginAudit(Request $request, ?User $user, int $statusCode, string $operationSummary, array $payload = []): void { try { AuditLog::create([ 'user_id' => $user?->id, 'username' => $user?->username ?? (isset($payload['username']) ? (string) $payload['username'] : null), 'role' => $user?->role, 'method' => 'POST', 'path' => '/'.ltrim($request->path(), '/'), 'action' => 'POST '.$request->path(), 'operation_summary' => $operationSummary, 'status_code' => $statusCode, 'ip' => $request->ip(), 'user_agent' => substr((string) $request->userAgent(), 0, 500), 'request_payload' => $payload, ]); } catch (\Throwable) { } } }