根据参考文档修改一部分规范
This commit is contained in:
@@ -21,6 +21,8 @@ Python 是一门极其灵活的动态语言,但“灵活”在大型团队协
|
||||
...
|
||||
|
||||
* **常量(Constants):** 强制使用 SCREAMING\_SNAKE\_CASE(全大写加下划线),定义在模块顶部。
|
||||
* **布尔语义命名:** 布尔变量与布尔返回值应使用 `is_`、`has_`、`can_`、`allow_` 前缀,避免语义歧义。
|
||||
* **集合语义命名:** 列表、集合、查询结果变量应使用复数命名(如 `users`, `orders`, `permission_codes`)。
|
||||
* **私有与受保护属性:** \* 单下划线 \_private\_var:表示内部使用(软性约束,仅作提示)。
|
||||
* 双下划线 \_\_strict\_private:触发名称改写(Name Mangling),除非极特殊情况(如防止子类重写),**日常业务开发中不推荐使用**,以免增加调试难度。
|
||||
|
||||
@@ -90,6 +92,7 @@ class UserCreateRequest(BaseModel):
|
||||
|
||||
* **禁止吞噬异常:** 绝不允许出现 except Exception: pass 这种掩耳盗铃的代码。
|
||||
* **自定义异常层次:** 模块应当抛出特定业务领域的异常(如继承自 ValueError 的 OrderNotFoundError),而不是直接抛出裸露的 Exception。
|
||||
* **边界层错误策略:** 在 API、CLI、任务入口等边界层,应优先“抛异常并交由统一处理”,避免在底层函数中拼接错误响应字符串。
|
||||
|
||||
### **4\. 资源释放与安全**
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
* **严禁重复造轮子**:Django 和 DRF 拥有极其成熟的第三方开源生态。对于标准化的通用需求,**必须**优先集成成熟的第三方库。例如使用 djangorestframework-simplejwt 处理 Token,使用 django-filter 处理复杂查询过滤。
|
||||
* **代理层无状态**:若系统作为中间层代理外部 API(如大模型网关),代理视图禁止落库复杂的业务状态。
|
||||
* **认证逻辑收口**:对接外部系统的身份认证逻辑(如 LDAP、API Key)必须严格收拢在 DRF 的 Authentication 类或 Django 的中间件中,**绝对禁止**在业务 View 中直接读取 Request Headers 做条件分流。
|
||||
* **认证冲突拒绝**:当请求同时携带多种互斥认证凭证时,必须直接拒绝并返回统一错误,禁止按优先级做隐式降级兼容。
|
||||
|
||||
## **数据层规范 (Models & Serializers)**
|
||||
|
||||
@@ -28,11 +29,15 @@
|
||||
* **显式定义主键与审计**:不推荐使用庞大的公共抽象基类隐式继承。业务 Model 应当显式定义主键和必备的审计字段(如 created\_at, updated\_at),避免基类变更带来全局副作用。
|
||||
* **字段防空红线**:字符型字段(CharField, TextField)禁止设置 null=True,必须设为 default=''。
|
||||
* **外键明确声明**:使用 ForeignKey 关联时必须显式指定清晰的 related\_name。
|
||||
* **枚举字段规范**:枚举值优先使用 TextChoices 或 IntegerChoices,禁止散落硬编码字符串。
|
||||
* **字段文档化**:核心业务字段应补充 help_text,以保障文档可读性与维护一致性。
|
||||
* **索引基线**:高频过滤、排序与关联字段必须显式建索引,避免接口上线后暴露慢查询。
|
||||
* **物理删除优先**:默认优先使用物理删除。仅当业务明确要求审计追溯时引入逻辑删除(is\_deleted)。若启用逻辑删除,必须配合使用带条件的唯一约束以防止重复插入冲突。
|
||||
|
||||
### **序列化层红线 (Serializers)**
|
||||
|
||||
* **禁用全量暴露**:**严禁**在 Serializer 中使用 fields \= '\_\_all\_\_'。必须逐个显式列出对外暴露的字段,防止数据库新增敏感字段后发生意外泄露。
|
||||
* **参数集中声明**:字段级约束(如 read_only、required、write_only)优先通过 Meta.extra_kwargs 集中配置。
|
||||
* **严防 N+1 查询**:
|
||||
* 列表接口**严禁**使用包含深层嵌套关系的复杂 Serializer,必须为其单独定义轻量级的结构。
|
||||
* 若 Serializer 输出了外键字段,View 层的 QuerySet **必须**配合使用 select\_related 或 prefetch\_related。
|
||||
@@ -60,8 +65,24 @@ View 层仅作为路由调度、参数校验和权限决策的枢纽,绝不可
|
||||
### **路由与异常处理**
|
||||
|
||||
* **命名空间绝对隔离**:全局强制采用 NamespaceVersioning 机制。根路由必须携带命名空间,且内部的 urls.py 必须存在 app\_name。反向解析必须带版本号前缀。
|
||||
* **URL 风格统一**:内部 REST 接口默认使用 kebab-case 与复数资源命名;第三方透传代理接口可按上游路径 1:1 保持。
|
||||
* **异常抛出代替返回**:遇到业务校验错误,**严禁**在代码中手动 return Response({"error": "xxx"})。**必须**直接抛出异常(如 raise ValidationError),交由全局的 exception\_handler 统一格式化。
|
||||
* **统一响应壳**:严禁在 View 中手动构建类似 {"code": 0, "data": ...} 的字典,必须交由自定义的全局 Renderer 统一包装。
|
||||
* **状态码语义化**:HTTP 状态码必须通过 `rest_framework.status` 常量引用,禁止直接写数字魔法值。
|
||||
|
||||
## **分页与文档规范**
|
||||
|
||||
* **默认分页策略**:常规列表接口默认使用 LimitOffsetPagination;仅在瀑布流、时间游标等场景使用 CursorPagination。
|
||||
* **文档注解强制化**:所有自定义 Action 与非标准接口必须显式补充 `@extend_schema`(至少包含 summary、description、request、responses)。
|
||||
* **透传接口说明**:代理第三方接口时,文档必须标注哪些字段为“上游原样透传”,避免接口语义歧义。
|
||||
|
||||
## **查询性能与异步基线**
|
||||
|
||||
* **N+1 防线**:开发阶段必须启用 N+1 监测手段(插件或测试守卫),出现问题即阻断合并。
|
||||
* **批量操作优先**:批量写入与批量更新优先使用 `bulk_create` / `bulk_update`,禁止循环逐条写库。
|
||||
* **大结果集迭代**:处理大表时优先使用 `iterator()` 或分批读取,避免一次性加载导致内存峰值过高。
|
||||
* **异步边界清晰**:异步视图仅用于高并发 I/O 场景,CPU 密集计算必须下沉到任务队列或独立计算服务。
|
||||
* **耗时任务剥离**:邮件、报表、第三方调用等明显耗时逻辑应通过异步任务框架执行,避免阻塞同步请求。
|
||||
|
||||
## **权限管控与数据隔离 (Permissions)**
|
||||
|
||||
|
||||
550
ref/drfstd.md
Normal file
550
ref/drfstd.md
Normal file
@@ -0,0 +1,550 @@
|
||||
# **GTCO\_AI Django REST Framework(DRF) 后端开发规范指南**
|
||||
|
||||
**版本**: 2.16
|
||||
|
||||
**适用对象**: Python 后端开发团队
|
||||
|
||||
**目标**: 明确技术选型,统一代码风格,降低维护成本,保障系统高可扩展性。
|
||||
|
||||
**执行策略**: 本规范采用“限制优先”原则。默认不提供多实现方案,优先固定实现路径;未在本文明确允许的做法,一律视为不允许。
|
||||
|
||||
**文档定位**: 本文件是开发规范的唯一基准文档。若与其它开发文档冲突,以本文件为准。
|
||||
|
||||
**配套文档入口**: `docs/development/index.md`
|
||||
|
||||
**专项规范**:
|
||||
1. 文档管理:`docs/development/documentation_standards.md`
|
||||
2. Dify 代理:`docs/development/dify_proxy_standards.md`
|
||||
3. 代码审查:`docs/development/code_review_guidelines.md`
|
||||
|
||||
## **0.0 项目红线 (不可变约束)**
|
||||
|
||||
以下为当前仓库的强制架构约束,默认长期有效:
|
||||
|
||||
1. **版本唯一**:仅允许 `v1` 命名空间,必须通过 `NamespaceVersioning` 解析版本;禁止新增未命名空间路由。
|
||||
2. **认证唯一入口**:仅允许 `SmartHeaderAuthentication + GTCOAIJWTAuthentication` 组合;禁止在 View 层读取 `Authorization` / `X-API-Key` 做分流。
|
||||
3. **认证冲突硬拒绝**:同一请求同时携带 Bearer 与 `X-API-Key` 时必须拒绝,禁止降级兼容。
|
||||
4. **响应统一封装**:仅允许 `StandardResponseRenderer` 统一包装 `code/message/data`;禁止在业务 View 手动拼装统一响应壳。
|
||||
5. **异常统一出口**:仅允许 `gtcoai_exception_handler` 输出错误结构;禁止各模块自定义返回格式。
|
||||
6. **组织作用域强隔离**:凡组织级数据(user/externals)查询必须绑定 `request.user.current_organization`;禁止跨组织默认可见。
|
||||
7. **权限链路固定**:涉及外部资源访问时,必须遵循“系统动作策略 -> 组织角色/成员 -> 用户组授权”顺序,禁止跳步放行。
|
||||
8. **写操作服务化**:复杂写操作必须下沉到 Service 层;View 层仅做编排、参数校验、权限决策。
|
||||
9. **代理链路无状态**:Dify 透明代理禁止落库业务状态,禁止在代理层引入账号同步、审批等副作用逻辑。
|
||||
10. **变更必须可验证**:涉及权限、认证、代理路径改动时,必须补齐自动化测试并执行真实路由脚本验证。
|
||||
11. **门禁入口唯一**:合并前质量检查仅允许通过 `scripts/ci/quality_gate.sh` 执行,禁止自定义临时命令替代。
|
||||
|
||||
## **0\. 技术栈清单 (Tech Stack)**
|
||||
|
||||
本规范强制使用以下库与工具:
|
||||
|
||||
| 分类 | 选定库 | 用途 |
|
||||
| :---- | :---- | :---- |
|
||||
| **包管理** | uv | Rust 编写的 Python 包管理器,用于替代 pip/poetry |
|
||||
| **配置管理** | django-split-settings | 多环境配置拆分 |
|
||||
| **代码质量** | Ruff | 格式化、排序、检查三合一工具 |
|
||||
| **类型检查** | VS Code (Pylance) | 编辑器实时检查 (CI 不强制 Mypy) |
|
||||
| **异步视图** | adrf | DRF 的异步扩展 (Async Django REST Framework) |
|
||||
| **异步任务** | Celery | 后台任务队列 |
|
||||
| **消息/缓存** | Redis | Celery Broker / Backend 及 Django Cache |
|
||||
| **性能调试** | django-debug-toolbar | SQL 查询分析 (开发环境) |
|
||||
| **性能监测** | nplusone | N+1 问题自动检测 |
|
||||
| **日志系统** | Loguru | JSON 结构化日志 |
|
||||
| **认证授权** | drf-simplejwt | JWT 认证 |
|
||||
| **目录认证** | django-auth-ldap | LDAP 认证(生产环境) |
|
||||
| **API 文档** | drf-spectacular | OpenAPI 3.0 Schema 生成 |
|
||||
| **测试框架** | pytest-django | 测试运行器 |
|
||||
| **测试数据** | factory\_boy | 测试数据生成 |
|
||||
| **并行测试** | pytest-xdist | 多进程测试加速 |
|
||||
|
||||
## **1\. 工程化规范 (Engineering)**
|
||||
|
||||
### **1.1 依赖管理**
|
||||
|
||||
* **工具**: 统一使用 uv。
|
||||
* 常用命令: uv pip compile (锁定), uv sync (同步)。
|
||||
* **版本锁定**: 生产环境必须存在 uv.lock 文件。
|
||||
* **Python 版本**: 强制版本为 **Python 3.11 及以上**(与 `pyproject.toml` 保持一致)。
|
||||
|
||||
### **1.2 配置管理 (Settings)**
|
||||
|
||||
**禁止**将所有配置写在单个 settings.py 中。**必须**使用 django-split-settings。
|
||||
|
||||
**目录结构要求**:
|
||||
|
||||
|
||||
settings/
|
||||
\_\_init\_\_.py \# 入口逻辑
|
||||
base.py \# 通用配置
|
||||
components/ \# 模块化配置 (logging.py, drf.py, celery.py)
|
||||
environments/\# 环境差异化配置 (local.py, prod.py)
|
||||
|
||||
* **安全要求**: 密钥 (SECRET\_KEY, DB\_PASSWORD) **必须**从环境变量 (.env) 读取,**严禁**硬编码。
|
||||
|
||||
### **1.3 代码规范 (Linting)**
|
||||
|
||||
**工具**: 统一使用 **Ruff**。
|
||||
|
||||
* **配置要求**:
|
||||
* **格式化**: ruff format
|
||||
* **检查**: ruff check \--fix
|
||||
* **行长**: **120** (严禁使用默认的 88\)
|
||||
* **类型检查**:
|
||||
* 开发人员需确保 VS Code (Pylance) 无红色报错。
|
||||
* CI 流水线不运行 Mypy。
|
||||
|
||||
## **2\. 命名与风格 (Naming & Style)**
|
||||
|
||||
### **2.1 命名表**
|
||||
|
||||
| 对象 | 规范 | 示例 |
|
||||
| :---- | :---- | :---- |
|
||||
| **App** | 全小写,复数 | users, orders |
|
||||
| **Model** | PascalCase,单数 | User, ProductCategory |
|
||||
| **Field** | snake\_case | created\_at |
|
||||
| **Serializer** | Model \+ Serializer | UserSerializer |
|
||||
| **View** | Resource \+ Action | UserViewSet |
|
||||
| **URL** | kebab-case | /api/v1/user-profiles/ |
|
||||
|
||||
### **2.2 编码习惯**
|
||||
|
||||
* **布尔值**: 必须使用 is\_, has\_, allow\_ 前缀。
|
||||
* **集合变量**: 必须使用复数形式 (users, items)。
|
||||
|
||||
## **3\. 模型层规范 (Models)**
|
||||
|
||||
### **3.1 基础模型**
|
||||
|
||||
**严禁**使用公共抽象基类(如 BaseModel)进行隐式继承。所有业务 Model **必须**直接继承 django.db.models.Model,并**显式定义**所有需要的字段,包括主键和审计字段。
|
||||
|
||||
这种“各写各的”策略是为了保证每个 Model 的定义清晰、独立,便于后续的迁移和重构,避免基类变更带来的全局副作用。
|
||||
|
||||
class MyModel(models.Model):
|
||||
\# 1\. 主键:根据实际需求显式定义,强制使用 UUID 或 BigAutoField
|
||||
id \= models.BigAutoField(primary\_key=True)
|
||||
|
||||
\# 2\. 业务字段
|
||||
name \= models.CharField(max\_length=100, help\_text="名称")
|
||||
|
||||
\# 3\. 审计字段:必须显式书写,严禁省略
|
||||
created\_at \= models.DateTimeField(auto\_now\_add=True, help\_text="创建时间")
|
||||
updated\_at \= models.DateTimeField(auto\_now=True, help\_text="更新时间")
|
||||
|
||||
class Meta:
|
||||
verbose\_name \= "我的模型"
|
||||
verbose\_name\_plural \= verbose\_name
|
||||
|
||||
### **3.2 字段定义**
|
||||
|
||||
* **Choices**: 强制使用 TextChoices / IntegerChoices 枚举。
|
||||
* **Null/Blank**: 字符型字段禁止 null=True,必须设为 default=''。
|
||||
* **Help Text**: 所有字段必须填写 help\_text (用于生成文档)。
|
||||
* **Related Name**: 外键必须指定 related\_name。
|
||||
|
||||
### **3.3 数据库约束**
|
||||
|
||||
* **Code First**: 严禁手动直连数据库修改结构,一切变更必须通过 Migrations。
|
||||
* **索引要求**: 凡是涉及 filter, order\_by, ForeignKey 的字段,**必须**加索引。
|
||||
* **删除策略**: 默认优先使用物理删除;仅当业务明确要求“可恢复”或“审计追溯”时才引入软删除字段。
|
||||
* **唯一约束**: 若启用软删除,必须使用带条件的唯一约束 (Conditional Unique Constraint)。
|
||||
|
||||
class Meta:
|
||||
constraints \= \[
|
||||
models.UniqueConstraint(
|
||||
fields=\['username'\],
|
||||
name='unique\_active\_username',
|
||||
condition=models.Q(is\_deleted=False)
|
||||
)
|
||||
\]
|
||||
|
||||
### **3.4 外部服务凭证与账号模型规范 (Externals Credentials & Accounts)**
|
||||
|
||||
适用于第三方代理场景(如 Dify、OpenAI、内部网关等)。
|
||||
|
||||
* **用户-凭证关系**: 必须为一对多(一个用户可维护多个外部凭证)。
|
||||
* **凭证核心字段**: 至少包含 `service`、`credential_type`、`name`、`secret`、`is_active`。
|
||||
* **凭证命名**: `name` 必须用于业务可读标识(如“生产Dify主Key”)。
|
||||
* **唯一约束**: 同一用户下,`(service, credential_type, name)` 必须唯一。
|
||||
* **密钥存储**: 明文密钥字段必须加密存储(字段级加密或等效方案),禁止日志打印完整密钥。
|
||||
* **账号绑定**: 若用户直接绑定外部账号,必须与凭证分表管理。账号表用于身份与连接状态,凭证表用于请求鉴权材料。
|
||||
* **转发读取原则**: 代理请求阶段只按需求读取目标凭证,不在转发层混入账号同步、授权刷新等重逻辑。
|
||||
|
||||
## **4\. 序列化层规范 (Serializers)**
|
||||
|
||||
### **4.1 编写规范**
|
||||
|
||||
* **选型**: 统一使用 ModelSerializer。
|
||||
* **字段**: 禁止使用 fields \= '\_\_all\_\_',必须显式列出。
|
||||
* **参数**: 使用 Meta.extra\_kwargs 定义字段参数,保持代码简洁。
|
||||
|
||||
### **4.2 职责边界**
|
||||
|
||||
* **定位**: 仅负责数据结构转换和基础格式验证。
|
||||
* **禁止**: save/create/update 方法中**严禁**包含发送邮件、复杂计算、外部调用等业务逻辑。
|
||||
|
||||
### **4.3 性能规范**
|
||||
|
||||
* **列表接口**: 严禁在列表页使用深层嵌套的 Serializer,强制定义 SimpleSerializer。
|
||||
* **预加载**: 包含外键的 Serializer,View 层必须配合 select\_related / prefetch\_related。
|
||||
* **聚合计算**: **严禁**在 SerializerMethodField 中执行 SQL 聚合 (count/sum)。必须在 View 层使用 annotate 计算完成后传入。
|
||||
|
||||
## **5\. 视图与业务层规范 (Views & Logic)**
|
||||
|
||||
### **5.1 ViewSet 选型与分层**
|
||||
|
||||
**核心原则**: **精准控制与逻辑分层**。
|
||||
|
||||
* **选型**:
|
||||
* 标准 CRUD 强制使用 ModelViewSet。
|
||||
* 定制化业务场景强制选用 GenericViewSet 配合 Mixins 按需组合。
|
||||
* 严禁直接使用裸的 ViewSet。
|
||||
* APIView 仅允许用于“非标准资源形态接口”(如 Dify 透明代理、缓存刷新、健康检查);必须补充 `@extend_schema` 且在类注释中说明无法使用 ViewSet 的原因。
|
||||
* 权限控制边界:只读接口绝不暴露 CreateModelMixin 或 DestroyModelMixin。
|
||||
* **方法职责表**:
|
||||
|
||||
| 方法 | 核心职责 | 规范要求 |
|
||||
| :---- | :---- | :---- |
|
||||
| **get\_queryset** | 查数据 \+ 优化 | **必须**实现权限隔离 (如 filter(user=request.user)) 与预加载 (select\_related),杜绝 N+1。 |
|
||||
| **get\_serializer\_class** | 定格式 (读写分离) | **必须**区分场景:list 用极简版,create 用校验版,retrieve 用全量版。 |
|
||||
| **get\_permissions** | 定权限 (动作分离) | **必须**区分动作:如 create 允许匿名,destroy 必须管理员。 |
|
||||
| **perform\_create/update** | 写业务 (逻辑下沉) | 仅处理关联逻辑 (如绑定 request.user) 或调用 Service 层。 |
|
||||
|
||||
### **5.2 Service 层模式**
|
||||
|
||||
View 层仅作为路由与调度的纯粹枢纽,写操作及复杂查询必须封装到 Service 层。
|
||||
|
||||
**严禁**重写 create, update, destroy 等主方法来堆砌业务逻辑。
|
||||
|
||||
\# views.py
|
||||
def create(self, request, \*args, \*\*kwargs):
|
||||
\# ❌ 错误:不要重写 create 方法,破坏了 DRF 标准流
|
||||
...
|
||||
|
||||
def perform\_create(self, serializer):
|
||||
\# ✅ 正确:使用钩子调用 Service
|
||||
create\_order\_service(
|
||||
user=self.request.user,
|
||||
data=serializer.validated\_data
|
||||
)
|
||||
|
||||
### **5.3 返回与响应规范 (Return & Response)**
|
||||
|
||||
**规则 1: 标准 CRUD 不重写 Return**
|
||||
|
||||
对于标准的 create, list, retrieve 等方法,**严禁**重写其 return 逻辑。DRF 父类已处理好序列化和状态码。
|
||||
|
||||
**规则 2: 自定义 Action 规范**
|
||||
|
||||
对于 @action 装饰的方法:
|
||||
|
||||
* **状态码**: 必须导入 rest\_framework.status 使用常量,**严禁**使用魔术数字 (200, 404)。
|
||||
* **返回**: 必须显式返回 Response 对象。
|
||||
|
||||
from rest\_framework import status
|
||||
from rest\_framework.decorators import action
|
||||
|
||||
@action(detail=True, methods=\['post'\])
|
||||
def approve(self, request, pk=None):
|
||||
order \= self.get\_object()
|
||||
\# 业务逻辑...
|
||||
return Response(serializer.data, status=status.HTTP\_200\_OK)
|
||||
|
||||
**规则 3: 错误处理 (Raise, Don't Return)**
|
||||
|
||||
遇到业务错误,**严禁**手动 return Response(error)。
|
||||
|
||||
**必须**直接 raise Exception (如 ValidationError, PermissionDenied)。全局异常处理器会将异常统一格式化。
|
||||
|
||||
**规则 4: 统一响应包装**
|
||||
|
||||
**严禁**在每个 View 中手动构建 { "code": 0, "data": ... } 结构。
|
||||
|
||||
**必须**编写自定义 Renderer 对全局响应进行统一包装。View 层只返回纯粹的业务数据。
|
||||
|
||||
相关实现文件位置:apps/common/renderers.py。
|
||||
|
||||
### **5.4 认证分流规范 (Authentication Routing)**
|
||||
|
||||
* **单一职责**: 鉴权头分流必须在认证层处理,固定入口为 `apps.common.authentication.SmartHeaderAuthentication`,禁止在 View 层手动判断 Header。
|
||||
* **Header 约定**:
|
||||
* JWT: `Authorization: Bearer <token>`
|
||||
* API Key: `X-API-Key: <raw_api_key>`
|
||||
* **冲突请求**: 同时携带 JWT 与 API Key 的请求必须拒绝,固定抛出 `ValidationError`,禁止“优先 JWT”或“优先 API Key”兼容策略。
|
||||
* **错误一致性**: 认证失败响应必须统一由全局异常处理器格式化,禁止各认证类返回不同结构。
|
||||
* **日志可观测性**: 中间件应可区分 `jwt/api_key/anonymous` 三类请求,并记录认证失败(401/403)上下文。
|
||||
|
||||
### **5.5 LDAP 接入规范 (LDAP Integration)**
|
||||
|
||||
* **接入方式**: 通过 Django Authentication Backend 接入 LDAP,禁止在 View/Serializer 直接发 LDAP 请求。
|
||||
* **配置来源**: LDAP 地址、绑定 DN、密码、搜索过滤器必须来源于环境变量。
|
||||
* **登录模式**: 登录接口应支持 `local/ldap/auto` 模式切换,默认由配置控制优先级。
|
||||
* **身份落库**: LDAP 登录成功后,需将本地用户标记为 `auth_type=ldap`,并按配置同步基础属性。
|
||||
* **平台差异**: Windows 开发环境可不强制安装 `python-ldap`,但 Linux 生产环境必须验证 LDAP 依赖可用。
|
||||
|
||||
## **6\. 接口设计规范 (API Design)**
|
||||
|
||||
### **6.1 URL 路由**
|
||||
|
||||
#### **📌 命名空间与版本控制 (Namespace & Versioning)**
|
||||
|
||||
全局强制采用 DRF 的 NamespaceVersioning 机制。**DRF 仅通过读取路由的 namespace 属性来判断当前 API 版本**,若配置缺失,会导致 request.version 为空,进而被权限控制层直接拦截返回 **404 Not Found**。
|
||||
|
||||
**强制执行标准**:
|
||||
|
||||
1. **根路由 (urls.py)**:
|
||||
挂载业务路由时,include() 必须包含版本号命名空间(`namespace="v1"`)。当前项目固定结构如下:
|
||||
\# gtco_ai/urls.py
|
||||
|
||||
api\_v1\_patterns \= \[
|
||||
path("user/", include("apps.user.urls")),
|
||||
path("external/", include("apps.externals.urls")),
|
||||
\]
|
||||
|
||||
urlpatterns \= \[
|
||||
path("api/v1/", include((api\_v1\_patterns, "api"), namespace="v1")),
|
||||
\]
|
||||
|
||||
2. **子路由 (apps/xxx/urls.py)**:
|
||||
每个 APP 的路由文件必须定义 app\_name 变量,否则 Django 无法正确注册命名空间。
|
||||
\# apps/user/urls.py
|
||||
app\_name \= "user" \# ✅ 必须存在且在项目中唯一
|
||||
|
||||
urlpatterns \= \[
|
||||
\# ...
|
||||
\]
|
||||
|
||||
3. **反向解析 (Reverse)**:
|
||||
在代码中生成 URL 时,必须带上版本前缀。
|
||||
\# ✅ 正确
|
||||
reverse("v1:user:user-detail", args=\[user.id\])
|
||||
\# ❌ 错误
|
||||
reverse("user:user-detail", args=\[user.id\])
|
||||
|
||||
#### **📌 路径设计规范**
|
||||
|
||||
* **路由前缀**:所有 API 路由必须以 /api/{version}/ 开头(例如 /api/v1/)。
|
||||
* **末尾斜杠**:统一保留 URL 末尾斜杠 /,与 DRF DefaultRouter 默认行为保持一致。
|
||||
* **内部自研接口规范**:
|
||||
* **资源命名**:URL 路径**必须**使用 kebab-case(短横线连接)风格,例如 /api/v1/user-profiles/。资源集合统一使用名词复数形式。
|
||||
* **层级结构**:URL 层级不超过 3 层,禁止过深嵌套。标准结构:/api/v1/{resource}/{id}/{sub-resource}/。
|
||||
* **【特批豁免】第三方代理接口 (Proxy API) 规范**:
|
||||
* **背景**:当代理外部系统(如 Dify 知识库等)接口,且前端需复用官方 SDK 或完全对照第三方官方文档时适用。
|
||||
* **1:1 映射优先**:允许豁免单复数、命名风格和层级深度的限制,**必须保证字面路径的 1:1 映射**(例如:允许使用 snake\_case 的 child\_chunks,允许单数 document,允许嵌套深度超过 4 层的 /datasets/{id}/documents/{id}/segments/{id}/child\_chunks/)。
|
||||
* **架构底线约束**:即便进行字面映射,底层代码实现仍**必须**严格依赖 Django / DRF 的标准路由调度体系(利用 DefaultRouter 处理扁平结构,利用 path \+ as\_view() 显式分发深层嵌套)。
|
||||
|
||||
### **6.2 响应格式与状态码 (Response & Status Codes)**
|
||||
|
||||
**统一响应结构**:
|
||||
|
||||
{
|
||||
"code": 20000, // 业务状态码 (5位)
|
||||
"message": "success", // 提示信息
|
||||
"data": { ... } // 业务数据
|
||||
}
|
||||
|
||||
#### **📌 业务状态码设计原则 (A-BB-CC 5位高可扩展标准)**
|
||||
|
||||
随着业务的演进与微服务化,状态码需要具备极高的**可扩展性**与清晰的**域划分**。本系统严格执行 **A-BB-CC 5位数标准**:
|
||||
|
||||
* **A (1位)**: 错误级别 / HTTP 语义映射。
|
||||
* 2 \= 成功 (Success)
|
||||
* 4 \= 客户端错误 / 业务校验阻断 (Client Error)
|
||||
* 5 \= 服务端异常 / 第三方依赖崩溃 (Server Error)
|
||||
* **BB (2位)**: 业务模块 / 子域划分 (00\~99),最高支持 100 个核心模块。
|
||||
* 00 \= Common (全局基础)
|
||||
* 01 \= User / Auth (用户与认证)
|
||||
* 02 \= Dify (Dify 代理相关的各个模块集合,含知识库、工作流等)
|
||||
* **【扩展策略】**:如果未来某个超大型模块的错误类型极其复杂、超出了 99 种,**允许为该大模块分配相邻的多个 BB 号段**。
|
||||
* **CC (2位)**: 具体错误分类序号 (00\~99)。
|
||||
* 00 \= 该模块的“默认通用错误”或“成功”。
|
||||
* 01\~99 \= 具体的精细化业务异常。
|
||||
|
||||
#### **📌 状态码扩展与开发强制规约**
|
||||
|
||||
1. **禁止过度分配 (泛化 vs 特化)**:
|
||||
* **禁止**为每一个表单字段的“必填项校验”单独分配 Code。普通的输入参数异常统一使用 40000 (全局 Bad Request),并在 message 或 data 节点中动态告知前端具体的字段校验失败原因。
|
||||
* **分配底线**:只有当**前端代码需要利用此 Code 进行特殊的交互逻辑跳转时**,才予以分配。例如:40102 (账号被冻结) 前端检测到后需要弹出特殊申诉弹窗;40201 (Dify API Key未配置) 前端检测到后需要引导至设置页面。
|
||||
2. **代码物理隔离 (分治策略)**:
|
||||
* **禁止单文件膨胀**:禁止把所有业务 Code 全部堆在 apps/common/status\_code.py 中。
|
||||
* **规范要求**:apps/common/status\_code.py **仅存放 00 模块**的基础状态码。各个业务线独有的状态码,**必须存放于各自 App 的文件内**(例如 apps/dify/knowledge/status\_code.py)。
|
||||
|
||||
### **6.3 分页**
|
||||
|
||||
* **默认分页**: 统一采用 LimitOffsetPagination。
|
||||
* **大数据分页**: 当前仓库未批准引入 Cursor/Keyset 替代方案作为默认行为;所有常规列表接口必须先落地 LimitOffsetPagination。
|
||||
* **流式分页**: 对于瀑布流场景,统一使用 CursorPagination。
|
||||
|
||||
### **6.4 API 文档注解规范 (drf-spectacular / extend_schema)**
|
||||
|
||||
* **强制要求**: 所有对外接口(尤其是 `@action` 自定义动作和代理接口)必须显式使用 `@extend_schema`。
|
||||
* **最低字段**: `summary`、`description`、`request`、`responses` 必须完整声明。
|
||||
* **响应说明**: 对透传接口应使用 `OpenApiResponse` 明确说明“上游原样透传”,避免文档误导前端。
|
||||
* **禁止事项**: 禁止仅依赖自动推断导致关键请求字段(如 `method`、`route`、`path_params`)在文档中缺失。
|
||||
|
||||
## **7\. 性能与异步规范 (Performance & Async)**
|
||||
|
||||
### **7.1 数据库查询**
|
||||
|
||||
* **N+1 问题**: 开发环境必须安装 nplusone 插件,CI 检测到 N+1 报错即为不合格。
|
||||
* **批量操作**: 循环插入/更新必须使用 bulk\_create / bulk\_update。
|
||||
* **迭代**: 遍历大表必须使用 .iterator()。
|
||||
|
||||
### **7.2 异步视图**
|
||||
|
||||
* **场景限制**: 仅在 **高并发 I/O 密集型** (如聚合多个第三方 API) 场景下使用。
|
||||
* **实现方案**: 统一使用 adrf 库。
|
||||
* **禁止事项**: 严禁在 async def 中运行 CPU 密集型任务 (Pandas/图片处理)。严禁混用同步 ORM 方法。
|
||||
|
||||
### **7.3 异步任务**
|
||||
|
||||
* **选型**: 统一使用 Celery \+ Redis。
|
||||
* **剥离标准**: 耗时 \> 200ms 的逻辑 (邮件、报表、AI推理) 必须剥离到后台任务。
|
||||
* **事务安全**: 必须使用 transaction.on\_commit 触发任务。
|
||||
|
||||
### **7.4 缓存**
|
||||
|
||||
* **组件**: 统一使用 django-redis。
|
||||
* **策略**: 强制使用 Cache-Aside (旁路缓存) 策略。
|
||||
|
||||
## **8\. 日志与异常规范 (Logs)**
|
||||
|
||||
### **8.1 异常处理**
|
||||
|
||||
**配置**:
|
||||
|
||||
1. 必须在 settings.py 中配置 EXCEPTION\_HANDLER 指向 `apps.common.exceptions.gtcoai_exception_handler`。
|
||||
2. 异常处理器需将所有 DRF 异常 (APIException) 和 未知异常 (Exception) 统一转化为标准 JSON 响应结构。
|
||||
|
||||
### **8.2 日志配置**
|
||||
|
||||
**工具**: 统一使用 Loguru。
|
||||
|
||||
**配置要求**:
|
||||
|
||||
1. **拦截**: 必须通过 InterceptHandler 接管 Django 原生日志。
|
||||
2. **输出**:
|
||||
* 控制台: 彩色文本。
|
||||
* 文件: JSON 格式 (serialize=True),便于采集。
|
||||
|
||||
## **9\. 认证与权限规范 (Auth)**
|
||||
|
||||
### **9.1 认证架构 (Authentication Strategy)**
|
||||
|
||||
**核心方案**:采用 JWT (JSON Web Token) 作为无状态认证标准,库选型锁定 djangorestframework-simplejwt。
|
||||
|
||||
**单点登录 (SSO) 集成**:
|
||||
|
||||
**架构选型**:强制使用 django-allauth 处理 OAuth 协议握手,配合 dj-rest-auth 暴露 REST 接口。严禁手写 OAuth2 流程。
|
||||
|
||||
**交互流程**:采用 "Authorization Code" 模式。前端仅负责获取第三方(如 Google/GitHub)的 code,后端负责用 code 换取用户信息并颁发本系统的 JWT。严禁后端直接信任前端传来的第三方用户信息。
|
||||
|
||||
**账号融合**:强制实施“基于邮箱的自动关联”策略。无论用户是通过账号密码注册,还是通过 SSO 登录,只要邮箱一致,必须关联到同一个 User 实体,确保用户身份唯一性。
|
||||
|
||||
### **9.2 Token安全与管理 (Token Engineering)**
|
||||
|
||||
**载荷 (Payload) 规范**:
|
||||
|
||||
**最小化原则**:Token 内仅允许携带非敏感的身份标识(如 user\_id, role, username)。
|
||||
|
||||
**扩展方式**:必须通过继承 TokenObtainPairSerializer 并重写 get\_token 方法注入自定义声明。严禁修改库源码或在 View 层手动拼凑 Token。
|
||||
|
||||
**生命周期管理**:
|
||||
|
||||
双 Token 机制:强制启用 Access Token 与 Refresh Token 分离。
|
||||
|
||||
* Access Token 有效期:**固定 24 小时**(与 `settings/components/drf.py` 保持一致)。
|
||||
* Refresh Token 有效期:**固定 7 天**。
|
||||
|
||||
**黑名单机制**:生产环境必须启用 Blacklist 应用,确保用户注销或修改密码后,旧的 Refresh Token 立即失效。
|
||||
|
||||
### **9.3 权限分层治理 (Permission Governance)**
|
||||
|
||||
**默认策略**:实施 "Default Deny" (默认拒绝) 策略。全局配置 (DEFAULT\_PERMISSION\_CLASSES) 必须设为 IsAuthenticated,仅对特定公开接口(如注册、登录、健康检查)显式配置 AllowAny。
|
||||
|
||||
在 ViewSet 中,严禁使用静态的 permission\_classes 列表覆盖所有方法。
|
||||
|
||||
**动作级权限 (Action-Based)**:
|
||||
|
||||
**实现规范**:必须重写 get\_permissions() 方法,根据 self.action(如 create, list, destroy)动态分配权限(例如:所有人可注册,登录用户可查看,仅管理员可删除)。
|
||||
|
||||
**系统动作策略优先 (System Policy First)**:当接口涉及“资源 + 动作”权限时,必须先校验系统级资源动作策略(资源支持动作 + 系统禁用动作),再进入组织角色与成员授权判断。严禁仅依赖成员授权直接放行不受系统支持的动作。
|
||||
|
||||
**对象级权限 (Object-Level)**:
|
||||
|
||||
涉及“只能操作自己的数据”或“组内可见”的场景,严禁在 View 的业务逻辑中写 if user \== owner。
|
||||
|
||||
**实现规范**:必须自定义继承自 BasePermission 的权限类,并重写 has\_object\_permission 方法,将鉴权逻辑与业务逻辑物理隔离。
|
||||
|
||||
### **9.4 安全加固 (Security Hardening)**
|
||||
|
||||
**CSRF 防护**:所有 POST/PUT/PATCH/DELETE 请求必须包含 CSRF Token,全局配置 (CSRF\_COOKIE\_HTTPONLY) 设为 True。
|
||||
|
||||
**防爆破 (Throttling)**:所有认证相关接口(登录、注册、刷新 Token、SSO 回调)必须配置高强度的限流策略(如 AnonRateThrottle),防止暴力破解。
|
||||
|
||||
**错误模糊化**:认证失败时,统一返回“用户名或密码错误”或“认证失败”,严禁明确提示“用户不存在”或“密码错误”,防止用户信息泄露。
|
||||
|
||||
## **10\. 测试规范 (Testing)**
|
||||
|
||||
### **10.1 工具链**
|
||||
|
||||
* **Runner**: 统一使用 pytest \+ pytest-django。
|
||||
* **Data**: 统一使用 factory\_boy。
|
||||
* **Client**: 统一使用 APIClient (DRF)。
|
||||
* **Coverage**: 统一使用 pytest-cov。
|
||||
|
||||
### **10.2 编写规范**
|
||||
|
||||
* **Fixture**: 通用对象定义在 conftest.py。
|
||||
* **Mock**: 外部 API 调用**必须** Mock,禁止在测试中发起真实网络请求。
|
||||
* **真实链路验证例外**: 对 `apps/externals` 的代理/权限链路改动,允许并要求使用 `scripts/external/` 下脚本做“联调验证”,但该验证不替代 pytest。
|
||||
* **提交门禁**: 上述改动在合并前必须同时满足“脚本联调通过 + 相关 pytest 用例通过”。
|
||||
* **执行**: CI 环境需开启 pytest-xdist 并行执行。
|
||||
|
||||
### **10.3 当前项目固定测试命令 (提交前最低要求)**
|
||||
|
||||
1. 外部资源治理与代理相关改动:必须运行 `pytest apps/externals/tests.py`。
|
||||
2. 用户/组织/认证相关改动:必须运行 `pytest apps/user/tests.py`。
|
||||
3. 同时改动 `user + externals`:必须同时运行以上两组用例,禁止只跑局部单测后直接提交。
|
||||
|
||||
### **10.4 CI 固定执行链路 (禁止绕过)**
|
||||
|
||||
1. 本仓库唯一质量门禁脚本:`scripts/ci/quality_gate.sh`。
|
||||
2. 脚本执行顺序固定为:`ruff format --check` -> `ruff check` -> `pytest apps/user/tests.py` -> `pytest apps/externals/tests.py`。
|
||||
3. 当前阶段(Docker 开发流程)提交前必须本地执行该脚本;任一步失败不得提交。
|
||||
4. CI 启用后,流水线必须在构建前执行该脚本;任一步失败即阻断后续阶段。
|
||||
5. 禁止在本地或 CI 中通过 `|| true`、跳过测试参数、或删改步骤顺序绕过门禁。
|
||||
|
||||
### **10.5 提交审查规范**
|
||||
|
||||
代码审查流程与输出模板统一见:
|
||||
|
||||
`docs/development/code_review_guidelines.md`
|
||||
|
||||
## **11\. 文档管理规范 (Documentation)**
|
||||
|
||||
本章节拆分为独立文档,减少主规范长度并便于专项维护。
|
||||
|
||||
详见:`docs/development/documentation_standards.md`
|
||||
|
||||
最低强制要求:
|
||||
|
||||
1. 核心业务改动必须同步更新测试与文档。
|
||||
2. OpenAPI 注解必须完整(`@extend_schema`)。
|
||||
3. 文档冲突时以 `docs/development/standards.md` 为准。
|
||||
|
||||
## **12\. Dify 代理模块规范 (Dify Proxy Module)**
|
||||
|
||||
本章节拆分为独立文档,避免主规范过长。
|
||||
|
||||
详见:`docs/development/dify_proxy_standards.md`
|
||||
|
||||
最低强制要求:
|
||||
|
||||
1. 代理层无状态、无业务存储。
|
||||
2. 路由常量统一由 `interfaces.py` 维护,禁止硬编码 URL。
|
||||
3. 保持全异步链路并统一错误处理。
|
||||
|
||||
## **13\. 全局权限治理落地文档**
|
||||
|
||||
权限治理的可执行实施方案(角色矩阵、Scope 规范、分阶段改造、验收清单)见:
|
||||
|
||||
`docs/plans/permission_governance_plan.md`
|
||||
Reference in New Issue
Block a user