"""审计日志 Pydantic Schema""" from __future__ import annotations import uuid from datetime import datetime from pydantic import BaseModel class AuditLogResponse(BaseModel): """单条审计记录""" id: uuid.UUID user_id: str | None = None display_name: str | None = None role: str | None = None action: str action_label: str | None = None # 服务端补的中文标签,避免前端各处硬编码 module: str module_label: str | None = None target_type: str | None = None target_id: str | None = None target_name: str | None = None details: dict | None = None ip_address: str | None = None user_agent: str | None = None method: str | None = None url: str | None = None status_code: int | None = None error_message: str | None = None # 与结构化日志对账用:拿着它就能捞到对应的接口日志 request_id: str | None = None created_at: datetime model_config = {"from_attributes": True} class AuditLogListResponse(BaseModel): """审计日志分页列表""" items: list[AuditLogResponse] total: int class DailyUsageRow(BaseModel): """某个操作人在某一天的用量汇总(北京时间自然日)""" day: str # YYYY-MM-DD(北京时间) user_id: str | None = None display_name: str | None = None role: str | None = None login_count: int = 0 # 登录次数(当天成功登录) logout_count: int = 0 # 登出次数(当天成功登出) op_count: int = 0 # 操作次数(当天全部审计记录数) # ⚠️ 上线/下线时间取【当天首次/末次活动】,不是登录/登出时间: # token 有效期内(refresh 7 天)用户不会重新登录,按登录算会导致 # 「登录次数 0 但操作 35 次」这种自相矛盾。 first_active_at: datetime | None = None # 上线时间(当天首次活动) last_active_at: datetime | None = None # 下线时间(当天末次活动) class DailyUsageResponse(BaseModel): """日活 / 使用统计""" start_date: str end_date: str items: list[DailyUsageRow] total: int # 行数(= 天数 × 人数),不是审计记录数 class AuditOption(BaseModel): """筛选项(value/label 结构,直接喂给前端下拉)""" value: str label: str class AuditOptionsResponse(BaseModel): """筛选项集合""" modules: list[AuditOption] actions: list[AuditOption] # 导出可选的列(value=后端列 key,label=中文表头)。 # 由后端下发而非前端硬编码:列的中文名与取值口径都在后端, # 两端各写一份迟早会出现"导出的列和页面上的对不上"。 log_export_columns: list[AuditOption] = [] usage_export_columns: list[AuditOption] = []