Skip to content

API 约定 ​

所有 system / business 接口共用同一套强制约定,偏离请先讨论。

完整 API 参考:Apidog。本文聚焦约定与关键端点速查。

路由前缀 ​

前缀用途
/api/v1/auth认证(公开)
/api/v1/route路由(前端拉常量路由 / 用户路由)
/api/v1/system-manage/*系统模块(用户 / 角色 / 菜单 / API / 字典)
/api/v1/business/<module>/*业务模块(autodiscover 自动挂载)

响应格式 ​

任何成功响应一律:

json
{ "code": "0000", "msg": "OK", "data": { ... } }

字段命名一律 camelCase——SchemaBase 的 alias_generator=to_camel_case 自动处理;Model.to_dict() 同样输出 camelCase。禁止返回裸 dict,禁止手工拼 snake_case。

详细的码段 / 前端映射见 响应码。

路径与方法约定 ​

操作方法 + 路径Body / Params
列表 / 搜索POST /resources/searchBody 继承 PageQueryBase
单条查询GET /resources/{id}—
创建POST /resourcesBody: XxxCreate
更新PATCH /resources/{id}Body: XxxUpdate
单条删除DELETE /resources/{id}—
批量删除DELETE /resourcesBody: {ids: [...]}(CommonIds)
子资源查询GET /resources/{id}/sub—
派生查询GET /resources/tree、GET /resources/options—
实例动作POST /resources/{id}/action-name视情况带 Body
集合动作POST /resources/batch-offline、POST /resources/refreshBody

约束:

  • 不要带尾斜杠(/users ✅ ,/users/ ❌)
  • 多词路径用 kebab-case(/batch-offline、/constant-routes、/user-routes)
  • 资源名一律 复数(/users、/roles、/warehouses)
  • "搜索"统一用 POST /resources/search 而不是 GET ?...=...——支持复杂查询体(数组、嵌套)

字段命名 ​

  • 请求 body / query 一律 camelCase(Pydantic validate_by_name=True 兼容 snake_case,但前端始终发 camelCase)
  • 响应 data 一律 camelCase——使用 schema.model_dump(by_alias=True) 或 model.to_dict()

分页约定 ​

请求体继承 PageQueryBase:

python
from app.utils import PageQueryBase

class WarehouseSearch(PageQueryBase):
    name: str | None = None

固定字段:

字段默认说明
current1页码(≥ 1)
size10每页数量(1–1000)
orderBynull排序字段列表,- 前缀降序,例 ["-createdAt", "id"]

响应体:

json
{
  "code": "0000", "msg": "OK",
  "data": {
    "records": [...],
    "total": 42,
    "current": 1,
    "size": 10
  }
}

ID 一律 sqid ​

所有对外暴露的资源 ID 是 sqid 字符串(如 Yc7vN3kE),不是自增 int。Pydantic schema 中用 SqidId,路径参数用 SqidPath:

python
from app.utils import SqidId, SqidPath, SchemaBase

class WarehouseUpdate(SchemaBase):
    parent_id: SqidId | None = None        # body 字段

@router.get("/warehouses/{item_id}")
async def get_dept(item_id: SqidPath):     # 路径参数
    ...

SqidId 在校验时把 sqid 解码成 int,序列化时再编码回 sqid。SqidPath 只解码不参与序列化输出。详见 Sqids 资源 ID。

CRUDRouter — 不要手写样板路由 ​

新增资源路由的默认方式是 CRUDRouter,6 条标准 REST 一行声明:

python
from app.utils import CRUDRouter, SearchFieldConfig, require_buttons

dept_crud = CRUDRouter(
    prefix="/warehouses",
    controller=warehouse_controller,
    create_schema=WarehouseCreate,
    update_schema=WarehouseUpdate,
    list_schema=WarehouseSearch,
    search_fields=SearchFieldConfig(
        contains_fields=["name", "code"],
        exact_fields=["status"],
    ),
    summary_prefix="仓库",
    soft_delete=True,
    tree_endpoint=True,
    action_dependencies={
        "create": [require_buttons("B_INVENTORY_WAREHOUSE_CREATE")],
        "update": [require_buttons("B_INVENTORY_WAREHOUSE_EDIT")],
        "delete": [require_buttons("B_INVENTORY_WAREHOUSE_DELETE")],
        "batch_delete": [require_buttons("B_INVENTORY_WAREHOUSE_DELETE")],
    },
)
router = dept_crud.router

需要自定义某条路由时用 @crud.override("list"),不要在 router 上重新声明同路径——会被 _OrderedRouter 排序后遮蔽。完整 API 见 CRUDRouter。

鉴权依赖 ​

依赖用途
DependAuth仅校验 JWT 与用户存在,不做权限检查(验证码、模拟登录、用户信息接口用)
DependPermission在 DependAuth 之上,按 (method, path) 比对 role.apis(业务接口默认挂在 router 上)
require_buttons("B_X", ...)任一通过即可,缺失返回 2203
require_buttons(..., require_all=True)全部通过,缺失返回 2202
require_roles("R_X", ...)同上,针对角色,码 2204 / 2205

R_SUPER 自动跳过所有权限校验。详见 认证与权限。

关键端点速查 ​

认证(/api/v1/auth,公开) ​

方法路径说明
POST/login用户名密码登录
POST/captcha发送手机验证码
POST/code-login验证码登录
POST/register注册(默认角色 R_USER)
POST/refresh-token刷新 access token
GET/user-info当前用户信息 + 角色 + 按钮(DependAuth)
PATCH/password修改密码(DependAuth,会递增 token 版本)
POST/impersonate/{user_id}超级管理员模拟登录(DependPermission + 超管校验)

路由(/api/v1/route) ​

方法路径说明
GET/constant-routes公共路由(登录页 / 错误页等,从 Redis)
GET/user-routes当前用户可见的菜单树(DependAuth)
GET/exists?name=xxx校验路由名是否存在(DependAuth)

系统管理(/api/v1/system-manage,全部 DependPermission) ​

每个资源都遵循上节标准 6 路由:

资源前缀备注
用户/userscreate / update 走 @override 注入密码哈希 + 角色关联
角色/roles含 GET /roles/{id}/menus、PATCH /roles/{id}/menus 等子资源
菜单/menus含 GET /menus/tree、GET /menus/pages
API/apis只读(仅 list / get / tree / tags),由启动时 refresh_api_list() 全量对账维护
字典/dictionaries含 GET /dictionaries/{type}/options(带 5 分钟 Redis 缓存)

业务模块(/api/v1/business/<name>) ​

按模块自治,业务模块路由由各模块的 api/ 与初始化数据共同维护。

响应封装 ​

类用途典型场景
Success(data=...)单条 / 无分页get / create / update
SuccessExtra(data={"records": [...]}, total, current, size)分页list / search
Fail(code=Code.X, msg="...")业务失败规则不通过
Custom(code, status_code, msg, data, **kwargs)任意极少数自定义 status_code 场景

OpenAPI 文档模型

Success 等是 JSONResponse 子类,FastAPI 默认无法在 Swagger UI 展示真实结构。需要为 OpenAPI 生成准确响应模型时,在路由上加 response_model=ResponseModel[UserOut] 或 PageResponseModel[UserOut]。详见 Schema 基类。

路由内顺序:静态路径优先 ​

CRUDRouter 内部使用 _OrderedRouter,每次 add_api_route 后自动把不含 {...} 的路径排到前面,避免 GET /resources/{id} 遮蔽 GET /resources/tree。不要绕过它(例如直接操作 router.routes.append(...))。

基于 MIT 协议发布