chore: sync local project changes
This commit is contained in:
@@ -2,10 +2,10 @@
|
||||
|
||||
# Fonrey API 契约规范(API_CONTRACT)
|
||||
|
||||
**版本**: 1.0
|
||||
**版本**: 1.1
|
||||
**适用范围**: 全模块(account / permission / property / client / complex / org / setting)
|
||||
**关联总纲**: `TECH_STACK/TECH_STACK.md`
|
||||
**最后更新**: 2026-04-27
|
||||
**最后更新**: 2026-04-28
|
||||
|
||||
---
|
||||
|
||||
@@ -285,3 +285,183 @@ Fonrey 优先采用“预签名上传 + 回执提交(commit)”两段式。
|
||||
- [ ] 下载是否采用 job 流程并处理未就绪 409
|
||||
- [ ] 权限拒绝是否遵循 200/403/302(401) 三态
|
||||
- [ ] 测试是否覆盖契约关键路径
|
||||
- [ ] 所有视图是否附加 `@extend_schema`(或 `@extend_schema_view`)注解
|
||||
- [ ] 枚举字段是否通过 `OpenApiTypes` 或 `ChoiceField` 在 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/`) | 开发环境默认开启,生产环境按需 |
|
||||
|
||||
安装:
|
||||
```bash
|
||||
pip install drf-spectacular schemathesis
|
||||
```
|
||||
|
||||
`settings.py` 最低配置:
|
||||
```python
|
||||
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`:
|
||||
```python
|
||||
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`,最低包含:
|
||||
|
||||
```python
|
||||
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` 并指定 `choices`,`drf-spectacular` 会自动生成 `enum` 约束:
|
||||
|
||||
```python
|
||||
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`:
|
||||
|
||||
```python
|
||||
@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 与代码漂移:
|
||||
|
||||
```bash
|
||||
# 生成最新 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 路径覆盖,最低要求:
|
||||
|
||||
```bash
|
||||
# 对本地运行的 dev server 跑契约测试
|
||||
schemathesis run openapi.json \
|
||||
--base-url http://localhost:8000 \
|
||||
--auth-header "Authorization: Bearer $TEST_TOKEN" \
|
||||
--checks status_code_conformance response_schema_conformance \
|
||||
--tag property # 可按模块 tag 分批跑
|
||||
```
|
||||
|
||||
CI 集成示例(GitHub Actions):
|
||||
|
||||
```yaml
|
||||
- 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` 保持同步:
|
||||
|
||||
```python
|
||||
# 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.json`,Swagger UI 可访问 | `/api/docs/` 正常渲染,无 import 报错 |
|
||||
| **P1 注解补全** | 所有现有视图加 `@extend_schema`,枚举字段用 ChoiceField | `openapi.json` 无 `{}` 空 Schema;所有端点有 `summary` 和 `tags` |
|
||||
| **P2 契约测试** | schemathesis 纳入 CI,Positive 用例全绿 | CI status_code + response_schema 两项检查全 PASS |
|
||||
| **P3 持续守护** | openapi.json diff 纳入 PR Review;枚举值变更同步 enums.py | PR checklist 自动提醒 Schema 变更 |
|
||||
|
||||
Reference in New Issue
Block a user