Files
FastapiAdmin/backend/app/core/base_schema.py
T
zhangtao 64bd82ee75 refactor: 完成项目代码结构优化与权限清理
主要变更:
1. 重构健康检查模块,迁移目录结构并合并重复代码
2. 清理路由标签冗余的"系统管理/平台管理"前缀,简化标签名称
3. 重构工作流相关目录与API,统一命名为flow和nodes
4. 重命名缓存删除接口为clear,同步前后端路由与权限
5. 简化接口参数描述,统一批量状态修改的请求体格式
6. 重构动态路由加载逻辑,优化初始化流程
7. 清理废弃的self_service模块,整合租户自助服务到租户API
8. 重构发票管理接口,统一路由前缀与权限控制
9. 优化代码生成模板,补全参数注解与描述
10: 修复日志删除接口的参数解析逻辑
2026-07-02 00:10:02 +08:00

154 lines
5.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from datetime import datetime
from typing import TYPE_CHECKING, TypeVar
from pydantic import BaseModel, ConfigDict, Field, model_validator
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.validator import DateTimeStr
if TYPE_CHECKING:
from app.api.v1.module_system.user.model import UserModel
UserT = TypeVar("UserT")
class CommonSchema(BaseModel):
"""通用信息模型"""
model_config = ConfigDict(from_attributes=True)
id: int = Field(description="编号ID")
name: str = Field(description="名称")
class BaseSchema(BaseModel):
"""通用输出模型,包含基础字段和审计字段"""
model_config = ConfigDict(from_attributes=True)
id: int | None = Field(default=None, description="主键ID")
uuid: str | None = Field(default=None, description="UUID")
created_time: DateTimeStr | None = Field(default=None, description="创建时间")
updated_time: DateTimeStr | None = Field(default=None, description="更新时间")
is_deleted: bool = Field(default=False, description="是否已删除")
deleted_time: DateTimeStr | None = Field(default=None, description="删除时间")
class UserBySchema(BaseModel):
"""通用创建模型,包含基础字段和审计字段"""
model_config = ConfigDict(from_attributes=True)
created_id: int | None = Field(default=None, description="创建人ID")
created_by: CommonSchema | None = Field(default=None, description="创建人信息")
updated_id: int | None = Field(default=None, description="更新人ID")
updated_by: CommonSchema | None = Field(default=None, description="更新人信息")
deleted_id: int | None = Field(default=None, description="删除人ID")
deleted_by: CommonSchema | None = Field(default=None, description="删除人信息")
class TenantBySchema(BaseModel):
"""租户嵌套出参(不再使用扁平 tenant_id / tenant_name / tenant_code)"""
model_config = ConfigDict(from_attributes=True)
tenant_id: int | None = Field(default=None, description="租户ID")
tenant_by: CommonSchema | None = Field(default=None, description="租户信息")
class BatchSetAvailable(BaseModel):
"""批量设置可用状态的请求模型"""
ids: list[int] = Field(default_factory=list, description="ID列表")
status: int = Field(default=0, ge=0, le=1, description="是否可用")
class UploadResponseSchema(BaseModel):
"""上传响应模型"""
model_config = ConfigDict(from_attributes=True)
file_path: str | None = Field(default=None, description="新文件映射路径")
file_name: str | None = Field(default=None, description="新文件名称")
origin_name: str | None = Field(default=None, description="原文件名称")
file_url: str | None = Field(default=None, description="新文件访问地址")
class DownloadFileSchema(BaseModel):
"""下载文件模型"""
file_path: str = Field(..., description="新文件映射路径")
file_name: str = Field(..., description="新文件名称")
class AuthSchema(BaseModel):
"""权限认证模型
``user`` 字段运行时为 ``Any``(避免与 SQLAlchemy 懒加载冲突,也避免
循环依赖)。通过 ``get_user()`` 方法获得 IDE 类型推断。
"""
model_config = ConfigDict(arbitrary_types_allowed=True)
user: UserT | None = Field(default=None, description="用户信息(UserModel 实例)", exclude=True)
check_data_scope: bool = Field(default=True, description="是否检查数据权限")
db: AsyncSession | None = Field(default=None, description="数据库会话", exclude=True)
tenant_id: int | None = Field(default=None, description="租户ID,用于用户认证前查询")
def get_user(self) -> "UserModel | None":
"""类型化的用户访问方法。
业务方 ``auth.get_user()`` 由 IDE 推断为 ``UserModel | None``,
享受自动补全。``auth.user`` 仍可用但无类型补全。
"""
return self.user # type: ignore[return-value]
class JWTPayloadSchema(BaseModel):
"""JWT载荷模型"""
sub: str = Field(..., description="用户登录信息")
is_refresh: bool = Field(default=False, description="是否刷新token")
exp: datetime | int = Field(..., description="过期时间")
@model_validator(mode="after")
def validate_fields(self):
if not self.sub or len(self.sub.strip()) == 0:
raise ValueError("会话编号不能为空")
return self
class JWTOutSchema(BaseModel):
"""JWT响应模型"""
model_config = ConfigDict(from_attributes=True)
access_token: str = Field(..., min_length=1, description="访问token")
refresh_token: str = Field(..., min_length=1, description="刷新token")
token_type: str = Field(default="Bearer", description="token类型")
expires_in: int = Field(..., gt=0, description="过期时间(秒)")
class RefreshTokenPayloadSchema(BaseModel):
"""刷新Token载荷模型"""
refresh_token: str = Field(..., min_length=1, description="刷新token")
class LogoutPayloadSchema(BaseModel):
"""退出登录载荷模型"""
token: str = Field(..., min_length=1, description="token")
class PageResultSchema[T](BaseModel):
"""分页查询结果模型"""
model_config = ConfigDict(from_attributes=True)
page_no: int | None = Field(default=None, ge=1, description="页码,默认为1")
page_size: int | None = Field(default=None, ge=1, description="页面大小,默认为10")
total: int = Field(default=0, ge=0, description="总记录数")
has_next: bool | None = Field(default=False, description="是否有下一页")
items: list[T] = Field(default_factory=list, description="分页后的数据列表")