Files
nexus/Project/fonrey/TECH_STACK/API_CONTRACT.md
2026-05-02 16:21:46 +08:00

26 KiB
Raw Blame History

For AI assistants: Read this entire file before designing or implementing any API. Contract rules here are mandatory. Do not invent per-module variants unless explicitly allowed.

Fonrey API 契约规范API_CONTRACT

版本: 1.2
适用范围: 全模块account / permission / property / client / complex / org / setting+ 平台管理后台admin
关联总纲: TECH_STACK/TECH_STACK.md
最后更新: 2026-05-02


变更历史

日期 变更人 变更内容
2026-04-30 Atlas 补充"变更历史"章节(文档治理)
2026-05-02 Atlas 升至 v1.2:新增 §12 平台管理后台端点附录A-B-1适用范围扩展至 admin 模块

1. 文档定位与原则

本文件定义 Fonrey 全局 API 契约标准,解决跨模块接口风格漂移问题。模块技术方案中的 API 章节必须遵循本文件,不得各自定义冲突规则。

1.1 强制级别

  • MUST:必须遵守,违反视为缺陷
  • SHOULD:建议遵守,若不遵守需在模块文档注明原因
  • MAY:可选能力

1.2 适用接口类型

  • JSON API/api/**
  • HTMX 片段端点HTML response
  • 文件上传/下载端点

2. 请求 / 响应格式规范

2.1 请求体规范JSON API

  • Content-Type MUST 为 application/json
  • charset=utf-8 SHOULD 显式声明
  • 写操作POST/PUT/PATCHMUST 传业务 payload禁止空对象写入

推荐请求结构:

{
  "data": {
    "...": "业务字段"
  },
  "meta": {
    "request_id": "可选,客户端透传"
  }
}

兼容说明:历史端点已存在 filters/sort/pagination 平铺结构时可继续使用,但新接口 SHOULD 迁移到 data 容器。

2.2 成功响应规范JSON API

成功响应 MUST 使用统一 envelope

{
  "ok": true,
  "data": {},
  "meta": {
    "request_id": "uuid",
    "timestamp": "2026-04-27T16:30:00+08:00"
  }
}

说明:

  • ok MUST 为 true
  • data MUST 存在(可为空对象 {} 或空数组 []
  • meta SHOULD 包含 request_id 与服务端时间

2.3 失败响应规范JSON API

失败响应 MUST 使用统一 envelope

{
  "ok": false,
  "error": "权限不足",
  "code": "PROPERTY_PERMISSION_DENIED",
  "details": {},
  "meta": {
    "request_id": "uuid",
    "timestamp": "2026-04-27T16:30:00+08:00"
  }
}

说明:

  • error MUST 为面向用户/调用方可读消息
  • code MUST 为稳定机器可读码(大写下划线)
  • details MAY 提供字段级错误(如校验失败)

2.4 HTMX 响应规范

  • 成功:返回 HTML 片段;必要时通过 HX-Trigger 触发前端事件
  • 失败:
    • 状态码 MUST 正确4xx/5xx
    • SHOULD 在响应头返回 HX-Trigger,例如:
      • {"toast:error":"权限不足"}
      • {"toast:error":"请求失败,请重试"}

3. 错误码规范

3.1 命名规则

  • 错误码 MUST 为 UPPER_SNAKE_CASE
  • 推荐前缀:<MODULE>_(如 PROPERTY_ / CLIENT_ / ORG_

3.2 HTTP 状态码基线

HTTP 使用场景 示例 code
400 参数错误、业务前置条件不满足 PROPERTY_VALIDATION_ERROR
401 仅用于纯 API Token 鉴权失败(当前 Web 会话模式一般不用) AUTH_UNAUTHORIZED
403 已登录但无权限 *_PERMISSION_DENIED
404 资源不存在或不可见 *_NOT_FOUND
409 状态冲突、任务未就绪 *_STATE_CONFLICT / *_JOB_NOT_READY
422 字段级校验错误(可选) *_VALIDATION_FAILED
429 频控触发 RATE_LIMITED
500 未预期异常 INTERNAL_ERROR

3.3 稳定性要求

  • code MUST 可稳定依赖,不得频繁改名
  • 错误文案 error 可优化,但不应影响调用方流程判断

4. 分页规范

Fonrey 列表查询 MUST 使用 Keyset 分页;禁止 OFFSET 深分页。

4.1 请求格式

{
  "filters": {},
  "sort": {"field": "updated_at", "order": "desc"},
  "pagination": {"mode": "keyset", "cursor": null, "limit": 20}
}

4.2 响应格式

{
  "ok": true,
  "data": {
    "items": [],
    "next_cursor": "opaque_cursor_2"
  },
  "meta": {
    "pagination": {"mode": "keyset", "cursor": "opaque_cursor", "limit": 20}
  }
}

4.3 约束

  • limit MUST 有上限(建议 ≤ 100
  • cursor MUST 为不透明字符串,禁止暴露内部排序字段组合
  • 排序字段 MUST 来自白名单,防止 SQL 注入与慢查询

5. 搜索 / 筛选规范

5.1 推荐请求结构

{
  "filters": {
    "keyword": "保利",
    "status": ["active", "pending"],
    "district_id": "uuid"
  },
  "sort": {"field": "updated_at", "order": "desc"},
  "pagination": {"mode": "keyset", "cursor": null, "limit": 20}
}

5.2 语义规范

  • keyword:模糊检索词(服务端统一做 trim
  • 多选条件 MUST 使用数组(如 status: []
  • 空数组 [] 语义:不限制该条件
  • null 语义:由模块文档明确(默认建议等同“不传”)

5.3 安全与性能

  • 仅允许白名单字段参与筛选和排序
  • LIKE/全文检索字段 SHOULD 建立索引或搜索策略
  • 查询快照哈希(用于缓存/导出SHOULD 对 filters+sort+scope 进行规范化后计算

6. 上传规范

Fonrey 优先采用“预签名上传 + 回执提交commit”两段式。

6.1 标准流程

  1. 客户端请求 upload-token业务 API
  2. 客户端直传对象存储R2
  3. 客户端调用 commit API 回写元数据

6.2 合约要求

  • upload-token MUST 短时有效(建议 5~15 分钟)
  • commit MUST 幂等(建议支持 idempotency_key
  • 上传白名单与大小限制 MUST 在模块文档声明并在服务端校验
  • SHOULD 校验 content_typesize
  • MAY 增加 sha256 校验确保完整性

6.3 错误码建议

  • *_UPLOAD_TOKEN_EXPIRED (409/400)
  • *_UPLOAD_FILE_TOO_LARGE (400)
  • *_UPLOAD_FILE_TYPE_NOT_ALLOWED (400)
  • *_UPLOAD_COMMIT_CONFLICT (409)

7. 文件下载规范

下载统一采用“导出任务 + 状态查询 + download endpoint”。

7.1 标准流程

  1. 创建导出任务 POST /api/**/export/jobs/
  2. 轮询任务状态 GET /api/**/export/jobs/{job_id}/
  3. 下载结果 GET /api/**/export/jobs/{job_id}/download/

7.2 合约要求

  • 任务未完成下载 MUST 返回 409 + *_EXPORT_JOB_NOT_READY
  • 下载链接 SHOULD 为一次性或短时有效 URL
  • 响应 SHOULD 设置 Content-Disposition(附件下载)
  • 文件名 SHOULD 带模块与日期,便于审计

8. 权限拒绝返回规范

8.1 JSON API

  • 未登录MUST 返回 302Web Session 场景)或 401(纯 API 场景)
  • 已登录无权限MUST 返回 403
  • 失败体 MUST 使用统一错误 envelopecode*_PERMISSION_DENIED

8.2 页面路由SSR/HTMX

  • 未登录302 跳转登录页
  • 已登录无权限403 页面(或 HTMX 403 片段)
  • HTMX 拒绝 SHOULD 触发 HX-Trigger toast 事件

8.3 测试强约束

每个受保护端点 MUST 覆盖三态:

  • 200有权限
  • 403已登录无权限
  • 302/401未登录视端点类型

9. 乐观锁Optimistic Locking规范

9.1 适用场景

propertiesclientscomplexes 三张高竞争表的更新操作(PUT/PATCHMUST 使用乐观锁并发控制,防止"后写覆盖先写"数据丢失。

9.2 请求规范

客户端发起更新时MUST 在请求体中携带当前资源版本号:

{
  "data": {
    "sale_price": 180,
    "version": 3
  }
}

兼容说明:当前 Fonrey 为内部 Web / Electron 客户端,采用请求体传递 version 字段,无需 If-Match Header避免 HTMX 额外配置复杂度)。未来若提供对外开放 REST API可补充支持 If-Match: <version> Header 形式。

9.3 服务端执行规范

服务端执行 UPDATE 时 MUST 同时匹配 version,并将 version +1

UPDATE properties
SET sale_price = :sale_price,
    version    = version + 1,
    updated_at = NOW(),
    updated_by = :operator_id
WHERE id      = :id
  AND version = :client_version   -- 乐观锁匹配
  AND deleted_at IS NULL;
  • 若受影响行数 = 1:更新成功,返回 200
  • 若受影响行数 = 0:抛 ConflictError,返回 409 + code *_VERSION_CONFLICT

9.4 冲突响应规范

{
  "ok": false,
  "error": "已被他人修改,请刷新重试",
  "code": "PROPERTY_VERSION_CONFLICT",
  "details": {
    "field": "version",
    "your_version": 3,
    "hint": "请重新获取最新数据后再提交"
  },
  "meta": {
    "request_id": "uuid",
    "timestamp": "2026-04-28T10:00:00+08:00"
  }
}
  • HTTP 状态码 MUST 为 409
  • code 格式:<MODULE>_VERSION_CONFLICT(如 PROPERTY_VERSION_CONFLICT / CLIENT_VERSION_CONFLICT / COMPLEX_VERSION_CONFLICT
  • 前端 SHOULD 展示 Toast「已被他人修改,请刷新重试」,并自动触发资源重新加载

9.5 Check List

  • version 字段在 GET 响应中 MUST 返回(供后续 PUT/PATCH 携带)
  • 服务层 update 方法 MUST 校验受影响行数0 行时抛 ConflictError
  • 前端表单 MUST 在隐藏域中保存 version,随 PUT/PATCH 提交
  • HTMX 场景:冲突时后端 MUST 返回 HX-Trigger: {"toast:error":"已被他人修改,请刷新重试"}
  • 测试 MUST 覆盖:并发两次更新同版本,第二次 MUST 返回 409

10. 与模块文档的衔接规则

  • 各模块技术方案中的“四、API 设计原则”“六、关键 API 规范”“十二、错误码建议”必须引用本文件
  • 模块文档可补充模块特有 code 与字段,但不得与本规范冲突
  • 冲突时以本文件为准;若需例外,必须在模块文档显式记录 ADR 链接

10. 落地检查清单Review Checklist

  • 是否使用统一成功/失败 envelope
  • 错误码是否为稳定 UPPER_SNAKE_CASE
  • 列表接口是否全部 Keyset 分页
  • filters/sort 字段是否白名单化
  • 上传是否采用 token+commit 且具备幂等保障
  • 下载是否采用 job 流程并处理未就绪 409
  • 权限拒绝是否遵循 200/403/302(401) 三态
  • 测试是否覆盖契约关键路径
  • 所有视图是否附加 @extend_schema(或 @extend_schema_view)注解
  • 枚举字段是否通过 OpenApiTypesChoiceField 在 Schema 中完整暴露所有值
  • 生成的 openapi.json 是否已提交 / 与代码同步更新
  • schemathesis 契约测试是否纳入 CI至少覆盖 Positive 用例)

11. OpenAPI 落地规范(机器可读契约)

For AI assistants: §11 是实现层强约定。生成视图代码时必须同步写 @extend_schema生成测试代码时必须包含契约断言。

11.1 工具链MUST

角色 工具 说明
Schema 生成 drf-spectacular 唯一授权的 OpenAPI 生成库;禁止 drf-yasg
Schema 文件 openapi.json(根目录) 每次 CI 必须重新生成并 diff 检查
契约测试 schemathesis 基于生成 Schema 做 Positive + Negative 测试
文档 UI Swagger UI/api/docs/ 开发环境默认开启,生产环境按需

安装:

pip install drf-spectacular schemathesis

settings.py 最低配置:

INSTALLED_APPS += ["drf_spectacular"]

SPECTACULAR_SETTINGS = {
    "TITLE": "Fonrey API",
    "VERSION": "1.1.0",
    "SERVE_INCLUDE_SCHEMA": False,
    "SCHEMA_PATH_PREFIX": r"/api/",
    # 枚举值直接展开(不折叠成 $ref便于 AI agent 直接读值
    "ENUM_GENERATE_CHOICE_DESCRIPTION": True,
    "COMPONENT_SPLIT_REQUEST": True,
}

urls.py

from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView

urlpatterns += [
    path("api/schema/", SpectacularAPIView.as_view(), name="schema"),
    path("api/docs/",   SpectacularSwaggerView.as_view(url_name="schema"), name="swagger-ui"),
]

11.2 视图注解规范MUST

每个 APIView / ViewSet 动作 MUST 携带 @extend_schema,最低包含:

from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiExample
from drf_spectacular.types import OpenApiTypes

@extend_schema(
    summary="获取房源详情",           # 简短操作名(中文 OK
    tags=["property"],              # 模块 tag与路由前缀一致
    responses={200: PropertyDetailSerializer},
    # 失败响应也要声明,给 AI agent 提供完整错误路径
    responses={
        200: PropertyDetailSerializer,
        403: OpenApiTypes.OBJECT,   # 统一 error envelope
        404: OpenApiTypes.OBJECT,
    },
)
def retrieve(self, request, pk=None):
    ...

枚举字段 MUST 在 Serializer 中使用 ChoiceField 并指定 choicesdrf-spectacular 会自动生成 enum 约束:

from ENUMS import PropertyType  # 取自项目枚举常量

class PropertySerializer(serializers.Serializer):
    property_type = serializers.ChoiceField(
        choices=PropertyType.choices,
        help_text="房源类型(详见 DATA_MODEL/ENUMS.md § property.property_type"
    )

分页/筛选端点额外声明 parameters

@extend_schema(
    parameters=[
        OpenApiParameter("cursor",   OpenApiTypes.STR,  description="Keyset 游标,首页传 null"),
        OpenApiParameter("limit",    OpenApiTypes.INT,  description="每页条数,最大 100"),
        OpenApiParameter("status",   OpenApiTypes.STR,  description="状态筛选,多选用逗号分隔"),
    ]
)

11.3 Schema 文件管理MUST

CI pipeline MUST 包含以下步骤,防止 Schema 与代码漂移:

# 生成最新 Schema
python manage.py spectacular --color --file openapi.json

# diff 检查(有变更时 CI 提醒,但不阻断 — 由开发者 review 后提交)
git diff --exit-code openapi.json || echo "⚠️  openapi.json has changed — please review and commit"
  • openapi.json MUST 纳入版本控制(不加入 .gitignore
  • 合并 PR 时若 openapi.json 有非预期变更MUST 作为 Review 阻断项

11.4 契约测试规范MUST

使用 schemathesis 对每个模块做 Positive 路径覆盖,最低要求:

# 对本地运行的 dev server 跑契约测试
schemathesis run openapi.json \
  --base-url http://localhost:8000 \
  --auth-header "Authorization: Bearer *** \
  --checks status_code_conformance response_schema_conformance \
  --tag property   # 可按模块 tag 分批跑

CI 集成示例GitHub Actions

- name: Contract Tests
  run: |
    python manage.py spectacular --file openapi.json
    schemathesis run openapi.json \
      --base-url http://localhost:8000 \
      --checks status_code_conformance response_schema_conformance \
      --exitfirst   # 首个失败即停止

AI Agent 验收词Acceptance Criteria:实现任意 API 端点后,须能通过以下验证:

GIVEN  openapi.json 已重新生成
WHEN   schemathesis 对该端点执行 Positive 测试
THEN   status_code_conformance PASS响应码与 Schema 声明一致)
  AND  response_schema_conformance PASS响应体结构与 Serializer 一致)
  AND  所有枚举字段值落在 ENUMS.md 定义的合法值集合内

11.5 枚举值契约一致性MUST

ENUMS.md 是枚举的单一事实来源Source of Truth。项目 MUST 维护一个 enums.py(或按模块拆分),与 ENUMS.md 保持同步:

# fonrey/core/enums.py — 机器可读枚举常量,与 ENUMS.md 严格对齐
from django.db import models

class PropertyType(models.TextChoices):
    RESIDENTIAL = "residential", "住宅"
    COMMERCIAL  = "commercial",  "商业"
    OFFICE      = "office",      "办公"
    INDUSTRIAL  = "industrial",  "工业"
    LAND        = "land",        "土地"
    OTHER       = "other",       "其他"

AI agent 实现时验证词:

GIVEN  enums.py 中某枚举类的所有 value
WHEN   与 ENUMS.md 对应域的值列表对比
THEN   两侧完全一致(无多余值,无缺失值,大小写相同)

11.6 分阶段落地路线(参考)

阶段 目标 完成标志
P0 接入 安装 drf-spectacular生成首版 openapi.jsonSwagger UI 可访问 /api/docs/ 正常渲染,无 import 报错
P1 注解补全 所有现有视图加 @extend_schema,枚举字段用 ChoiceField openapi.json{} 空 Schema所有端点有 summarytags
P2 契约测试 schemathesis 纳入 CIPositive 用例全绿 CI status_code + response_schema 两项检查全 PASS
P3 持续守护 openapi.json diff 纳入 PR Review枚举值变更同步 enums.py PR checklist 自动提醒 Schema 变更

12. 平台管理后台端点附录A-B-1 回应)

背景:审核报告 A-B-1 指出 API_CONTRACT 缺少 /admin/... 命名空间端点规范,导致 Backend 实现缺乏契约约束。本章补齐平台管理后台(admin.fonrey.com)所有端点清单,作为机器可读约束附录。

适用规则:本章端点遵循第 210 章全局规范;以下特例优先级高于全局规范:

  • 认证Django SessionadminSessionId Cookie+ CSRFX-CSRFToken+ 必要时 MFA Step-up不使用 JWT / Bearer Token
  • 响应类型:GET 页面视图返回 text/htmlPOST/PATCH 写操作返回 JSON 或 HTMX partial在端点备注中明示
  • 版本控制:/admin/... 无版本号前缀;破坏性变更通过 Django View Mixin 灰度。
  • 非法权限访问:统一返回 403(不暴露资源是否存在)。

12.1 认证端点

端点 方法 鉴权 请求 成功响应 失败响应 备注
/admin/login/ GET 公开 200 HTML登录表单 已登录时 302 → /admin/
/admin/login/ POST 公开 username, password 302 → /admin/mfa/verify/ 200 HTML表单 + 错误) 失败计数写 Redis5 次锁 15 min
/admin/login/mfa/verify/ GET session 中有 pre_mfa_admin_id 200 HTMLTOTP 输入框) 302 → /admin/login/ 超时 5 min 未提交则清 pre_mfa
/admin/login/mfa/verify/ POST session 中有 pre_mfa_admin_id otp_code 302 → /admin/ 200 HTML错误/ 429OTP 失败 ≥3 成功后写 admin_sessions,清 pre_mfa_admin_id
/admin/login/mfa/step-up/ POST AdminSession otp_code 200 JSON {"ok": true} 400 JSON {"error": "invalid_otp"} session.mfa_confirmed_at + Redis pub:mfa:stepup:*
/admin/logout/ POST AdminSession 302 → /admin/login/ admin_sessionsflush session

12.2 租户管理端点

端点 方法 鉴权 请求要点 成功响应 失败响应 备注
/admin/tenants/ GET AdminSession任意角色 ?status=, ?q=, ?page_cursor= 200 HTML租户列表页 Keyset 分页;禁止 OFFSET
/admin/tenants/create/ GET AdminSessionSUPER/OPS 200 HTML创建表单
/admin/tenants/create/ POST AdminSessionSUPER/OPS+ CSRF tenant_name, contact_phone, contact_email, license_user_limit, paid_until 302 → /admin/tenants/<id>/ 200 HTML表单 + 错误)/ 409tenant_code 冲突) tenantsstatus='provisioning')后入队 provision_tenant Saga
/admin/tenants/<uuid:id>/ GET AdminSession 200 HTML详情页 404
/admin/tenants/<uuid:id>/edit/ GET AdminSessionSUPER/OPS 200 HTML编辑表单 404
/admin/tenants/<uuid:id>/edit/ POST AdminSessionSUPER/OPS+ CSRF 可编辑字段name / contact_* / license_user_limit / paid_until 302 → 详情页 200 HTML表单 + 错误) platform_audit_logsUPDATE_TENANT_INFO
/admin/tenants/<uuid:id>/suspend/ POST AdminSessionSUPER+ CSRF + MFA Step-up suspended_reason, suspended_until(可选) 200 JSON {"ok": true} + HTMX partial 400 / 409已暂停 suspended_reason 枚举见 ENUMS.md;写审计
/admin/tenants/<uuid:id>/resume/ POST AdminSessionSUPER+ CSRF + MFA Step-up 200 JSON {"ok": true} + HTMX partial 409非 suspended 状态) 写审计
/admin/tenants/<uuid:id>/cancel/ POST AdminSessionSUPER+ CSRF + MFA Step-up reason 200 JSON {"ok": true} 409非 active 状态) 进入 pending_cancellation;冷静期 30 天
/admin/tenants/<uuid:id>/hard-delete/ POST AdminSessionSUPER+ CSRF + MFA Step-up5 min 内)+ 双人复核 token confirm_tenant_name, reviewer_token 200 JSON {"ok": true} 400名称不符/ 403token 无效)/ 409非 pending_cancellation + 30d 状态) 入队 hard_delete_tenant;写审计 HARD_DELETE_TENANT
/admin/tenants/<uuid:id>/update-license/ POST AdminSessionSUPER/OPS+ CSRF paid_until, license_user_limit 200 JSON {"ok": true} 400参数非法 写审计 UPDATE_LICENSE / UPDATE_LICENSE_USER_LIMIT
/admin/tenants/<uuid:id>/export/ POST AdminSessionSUPER/OPS+ CSRF export_formatcsv/json 202 JSON {"task_id": "..."} 入队 run_export;写 export_tasksstatus='pending'
/admin/tenants/<uuid:id>/export/<uuid:task_id>/status/ GET AdminSession 200 JSON {"status": "pending"|"in_progress"|"success"|"failed", "download_url": null|"..."} 404 HTMX 轮询端点;终态时移除 hx-trigger

12.3 平台管理员账号端点

端点 方法 鉴权 请求要点 成功响应 备注
/admin/admins/ GET AdminSessionSUPER 200 HTML管理员列表
/admin/admins/create/ POST AdminSessionSUPER+ CSRF username, email, role 302 → 列表页 写审计 CREATE_PLATFORM_ADMIN
/admin/admins/<uuid:id>/edit/ POST AdminSessionSUPER+ CSRF role, is_active 302 → 列表页 写审计 UPDATE_PLATFORM_ADMIN_ROLE
/admin/admins/<uuid:id>/deactivate/ POST AdminSessionSUPER+ CSRF + MFA Step-up 302 → 列表页 清对应 admin_sessions;写审计 DEACTIVATE_PLATFORM_ADMIN
/admin/admins/<uuid:id>/reset-password/ POST AdminSessionSUPER+ CSRF 200 JSON {"ok": true} 发初始密码邮件;写审计

12.4 IP 白名单端点

端点 方法 鉴权 请求要点 成功响应 备注
/admin/ip-whitelist/ GET AdminSessionSUPER 200 HTML白名单列表
/admin/ip-whitelist/add/ POST AdminSessionSUPER+ CSRF cidr, label, expires_at(可选) 302 → 列表页 清 Redis pub:ipwl:active
/admin/ip-whitelist/<uuid:id>/toggle/ POST AdminSessionSUPER+ CSRF is_active 200 JSON {"ok": true} 清 Redis pub:ipwl:active
/admin/ip-whitelist/<uuid:id>/delete/ POST AdminSessionSUPER+ CSRF + MFA Step-up 302 → 列表页 清 Redis pub:ipwl:active;写审计

12.5 客户端版本发布端点

端点 方法 鉴权 请求要点 成功响应 备注
/admin/releases/ GET AdminSession 200 HTML版本列表
/admin/api/client-releases/ POST AdminSessionSUPER+ CSRF + MFA Step-up version, platform, arch, release_type, artifact(文件上传) 201 JSON版本对象 校验 SemVer入队 checksum + scan
/admin/api/client-releases/<uuid:pk>/ PATCH AdminSessionSUPER+ CSRF status, release_notes, force_update, min_required_version 200 JSON更新后对象 状态机约束见 平台管理后台技术方案.md §5.2
/admin/api/client-releases/<uuid:pk>/rollback/ POST AdminSessionSUPER+ CSRF + MFA Step-up 200 JSON {"ok": true} 原子切换 published写审计 ROLLBACK_CLIENT_RELEASE

12.6 备份与恢复端点

端点 方法 鉴权 请求要点 成功响应 备注
/admin/backups/ GET AdminSessionSUPER/OPS 200 HTML备份列表
/admin/backups/trigger/ POST AdminSessionSUPER+ CSRF backup_typefull/incremental 202 JSON {"task_id": "..."} 入队 run_backup
/admin/backups/<uuid:id>/status/ GET AdminSession 200 JSON {"status": "...", "size_bytes": ...} HTMX 轮询端点
/admin/backups/<uuid:id>/restore/ POST AdminSessionSUPER+ CSRF + MFA Step-up5 min 内)+ 双人复核 token confirm_text, reviewer_token 202 JSON {"task_id": "..."} 高危操作;入队 run_restore;写审计 RESTORE_BACKUP

12.7 平台审计日志端点

端点 方法 鉴权 请求要点 成功响应 备注
/admin/audit-logs/ GET AdminSessionSUPER/OPS ?action_type=, ?operator_id=, ?target_id=, ?from=, ?to=, ?page_cursor= 200 HTML审计列表 Keyset 分页;只读,无写操作
/admin/audit-logs/<uuid:id>/ GET AdminSessionSUPER/OPS 200 HTML详情

12.8 仪表盘端点(统计只读)

端点 方法 鉴权 说明
/admin/dashboard/ GET AdminSession任意角色 总览页(读 Redis 缓存)
/admin/dashboard/stats/ GET AdminSession HTMX 局刷统计块;读 pub:dashboard:stats 缓存
/admin/dashboard/version-distribution/ GET AdminSession 版本分布图;转发至 /api/release/v1/metrics/version-distribution/

12.9 端点完整性守护规则

本节端点清单必须与 平台管理后台技术方案.md §4 保持同步。新增端点必须同时更新本表和技术方案;删除/重命名端点必须先创建 ADR格式 ADR-YYYYMMDD-XXX,本文件"变更历史"行追加记录。

AI agent 实现时验证词:

GIVEN  本附录第 12.112.8 节的端点清单
WHEN   开发者实现或修改任意 /admin/... 端点
THEN   端点鉴权方式、请求字段、成功状态码、失败状态码与本附录完全一致;
       任何缺失端点视为 A-B-1 级 Blocker