2026-05-07 14:34:42 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from typing import Annotated
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
|
|
|
|
|
from app.api.deps import get_db
|
2026-05-30 15:46:51 +08:00
|
|
|
from app.schemas.auth import (
|
|
|
|
|
LoginRequest,
|
|
|
|
|
LoginResponse,
|
|
|
|
|
SessionFinishRequest,
|
|
|
|
|
SessionFinishResponse,
|
|
|
|
|
)
|
2026-05-11 05:18:16 +00:00
|
|
|
from app.schemas.common import ErrorResponse
|
2026-05-07 14:34:42 +08:00
|
|
|
from app.services.auth import AuthService
|
2026-05-30 15:46:51 +08:00
|
|
|
from app.services.user_session_metrics import UserSessionMetricService
|
2026-05-07 14:34:42 +08:00
|
|
|
|
|
|
|
|
router = APIRouter(prefix="/auth")
|
|
|
|
|
DbSession = Annotated[Session, Depends(get_db)]
|
|
|
|
|
|
|
|
|
|
|
2026-05-11 05:18:16 +00:00
|
|
|
@router.post(
|
|
|
|
|
"/login",
|
|
|
|
|
response_model=LoginResponse,
|
|
|
|
|
summary="用户登录",
|
|
|
|
|
description="支持管理员账号和员工账号登录,成功后返回前端会话所需的用户信息。",
|
|
|
|
|
responses={
|
|
|
|
|
status.HTTP_401_UNAUTHORIZED: {
|
|
|
|
|
"model": ErrorResponse,
|
|
|
|
|
"description": "账号或密码错误。",
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
)
|
2026-05-07 14:34:42 +08:00
|
|
|
def login(payload: LoginRequest, db: DbSession) -> LoginResponse:
|
|
|
|
|
try:
|
|
|
|
|
return AuthService(db).login(payload)
|
|
|
|
|
except ValueError as exc:
|
|
|
|
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(exc)) from exc
|
2026-05-30 15:46:51 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post(
|
|
|
|
|
"/sessions/{session_id}/finish",
|
|
|
|
|
response_model=SessionFinishResponse,
|
|
|
|
|
summary="结算用户在线会话",
|
|
|
|
|
)
|
|
|
|
|
def finish_session(
|
|
|
|
|
session_id: str,
|
|
|
|
|
payload: SessionFinishRequest,
|
|
|
|
|
db: DbSession,
|
|
|
|
|
) -> SessionFinishResponse:
|
|
|
|
|
session = UserSessionMetricService(db).finish_session(
|
|
|
|
|
session_id=session_id,
|
|
|
|
|
reason=payload.reason,
|
|
|
|
|
last_activity_at=payload.lastActivityAt,
|
|
|
|
|
activity_event_count=payload.activityEventCount,
|
|
|
|
|
event={"page_path": payload.pagePath},
|
|
|
|
|
)
|
|
|
|
|
if session is None:
|
|
|
|
|
return SessionFinishResponse(
|
|
|
|
|
detail="会话不存在或已被清理。",
|
|
|
|
|
sessionId=session_id,
|
|
|
|
|
durationMs=0,
|
|
|
|
|
)
|
|
|
|
|
return SessionFinishResponse(
|
|
|
|
|
sessionId=session.session_id,
|
|
|
|
|
durationMs=int(session.duration_ms or 0),
|
|
|
|
|
)
|