✨博文作者:烟雨孤舟
💖 喜欢的可以 点赞 收藏 关注哦~~
✍️ 作者简介: 一个热爱大数据的学习者
✍️ 笔记简介:作为大数据爱好者,以下是个人总结的学习笔记,如有错误,请多多指教!
📘 编程规约
命名风格
1.【强制】代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
反例:*name / name / name / name* / name / name
2.【强制】所有编程相关的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
说明:正确的英文拼写和语法可以让阅读者易于理解,避免歧义。
正例:alibaba / cainiao/ aliyun/ youku / hangzhou 等国际通用的名称,可视同英文。
反例:DaZhePromotion [打折] / getPingfenByName() [评分]
3.【强制】代码和注释中都要避免使用任何语言的种族歧视性词语。
正例:日本人 / 印度人 / blockList / allowList / secondary
反例:RIBENGUIZI / Asan / blackList / whiteList / slave
4.【强制】类名使用 UpperCamelCase 大波峰风格,但以下情形例外:DO / BO / DTO / VO / AO / PO / UID等。
正例:ForceCode / UserDO / HtmlDTO / XmlService / TcpUdpDeal / TaPromotion 反例:forcecode / UserDo / HTMLDto / XMLService / TCPUDPDeal / TAPromotion
- 【强制】方法名、参数名、成员变量、局部变量都统一使用小波峰风格。
正例:localValue / getHttpMessage() / inputUserId
6.【强制】常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
正例:MAX_STOCK_COUNT / CACHE_EXPIRED_TIME 反例:MAX_count
- 【强制】杜绝完全不规范的缩写,避免望文不知义。
反例:AbstractClass"缩写"成 AbsClass;此类随意缩写严重降低了代码的可阅读性
- 【推荐】任何自定义编程元素在命名时,使用尽量完整的单词组合来表达。
9.【推荐】在常量与变量的命名时,表示类型的名词放在词尾,以提升辨识度。
正例:startTime / workQueue / nameList / TERMINATED_THREAD_COUNT 反例:startedAt / QueueOfWork / listName / COUNT_TERMINATED_THREAD
10.【强制】对外暴露的 API 接口必须以资源建模为核心进行设计,URL 表达的是资源本身而非动作,接口行为必须通过 HTTP 方法体现,禁止将 URL 当作函数名或行为描述。
- URL 必须仅使用小写字母,禁止大小写混用。
- URL 表示资源本身,禁止在路径中出现任何动词。
- 集合资源必须统一使用复数形式,禁止单复数混用。
- 资源的操作语义必须通过 HTTP 方法表达,禁止通过路径表达行为。
- URL 仅用于描述资源层级与从属关系,不得表达业务动作。
- JSON 对外字段命名必须与调用方约定保持一致,推荐使用 camelCase。
- 后端内部字段命名与对外字段不一致时,必须通过 Serializer 或映射层完成转换,禁止直接暴露内部字段结构。
- RESTful API 必须通过标准 HTTP 方法对资源执行操作,形成明确、稳定的语义映射关系:
| HTTP 方法 | 语义 | CRUD |
|---|---|---|
| GET | 获取资源 | Read |
| POST | 创建资源 | Create |
| PUT | 更新完整资源 | Update |
| DELETE | 删除资源 | Delete |
11.【强制】禁止使用语言保留字;禁止"同字段多写法"(如 user_id/userId/uid 混用)。
12.【强制】后端内部命名遵循 PEP8,具体可参考下表。
| 类型 | 格式 | 示例 | 说明 |
|---|---|---|---|
| 文件名 | snake_case | user_service.py | 与模块功能一致 |
| 包名 | 全小写 | user | 不使用大写 |
| 类名 | PascalCase | UserLoginDTO | 类 / DTO / VO |
| 函数名 | snake_case | create_order() | Python 生态统一 |
| 方法参数 | snake_case | user_id | Python 内部统一 |
| 变量名 | snake_case | client_ip | 语义清晰 |
| 工具函数 | snake_case | generate_token() | 通用能力 |
| 常量 | UPPER_SNAKE_CASE | MAX_RETRY_COUNT | 全局或模块级 |
| JSON 字段(对外) | camelCase | createTime | 与前端 / 第三方统一 |
| 数据库表名 | snake_case | user_account | 避免 SQL 冲突 |
| 数据库字段 | snake_case | created_at | ORM 友好 |
| 配置项 / 环境变量 | UPPER_SNAKE_CASE | DEBUG_MODE | 系统级配置 |
| URL / API 路由 | kebab-case | /api/user/login-info | REST 风格 |
注释规约
-
【强制】注释必须准确、简洁,描述代码的设计意图、业务目的和行为逻辑。
-
【强制】对关键业务逻辑、复杂算法、异常流程、接口参数均需添加明确注释。
-
【强制】修改代码时必须同步更新注释,避免出现"注释与代码不一致"的情况。
-
【强制】所有公共方法、核心逻辑、对外接口均需编写完整 Docstring。
-
【强制】注释需保持中性、专业,禁止出现个人情绪、无意义备注。
-
【强制】注释一律使用中文或英文保持一致,不允许中英文混用造成理解障碍;
-
【强制】业务逻辑注释必须说明:为什么这样实现(Why),而非仅说明"做了什么(What)"。
-
【强制】对于关键业务流程(支付、回调、审批、事务程等)必须添加分段注释,流程注释必须体现业务行为,而非技术行为。
-
【强制】Model 层仅允许注释字段含义,不允许描述业务逻辑。
-
【强制】TODO:只能作为检查备注不能用于项目注释
-
【强制】标准Docstring格式如下:
- 功能说明(必须)
- 业务背景 / 实现意图说明(建议)
- 参数说明(必须)
- 返回值说明(必须)
- 异常说明(必须)
- 是否涉及权限、事务、加密等安全操作(视情况)
类注释模板:
class XxxView:
""
模块名称(XxxView)
方法概览:
funcA(request) 功能描述A
funcB(request) 功能描述B
funcC(request) 功能描述C
说明:
控制器层负责解析请求与调用业务服务,不包含业务逻辑。
""方法注释模板:
@staticmethod
def funcA(request):
""
功能:功能简述,例如获取首页卡片列表。
参数:
request:请求对象。
返回:
JsonResponse:遵循统一响应格式。
行为说明:
- 解析参数
- 获取用户上下文(如 companyId)
- 调用 Service
- 返回结果
""
分层架构
1.【强制】Controller/View 层(接口层)
只允许:接收请求、加载装饰器、鉴权/权限、调用 DTO 校验、调用 Service、统一响应返回
禁止:写业务流程、跨表编排、复杂循环查询、直接写事务、直接调第三方
2.【强制】DTO/Serializer 层(参数与数据结构层)
只允许:字段定义、类型/范围校验、默认值、格式转换(时间/枚举/映射)
禁止:查库、写库、发请求、做业务判断(如"余额是否足够"属于业务)
说明:
- 禁止在 Controller 或 Service 中重复校验字段;
- 所有接口的输入参数与输出结构必须通过 DTO(Data Transfer Object)进行封装;
- 不允许在 DTO 编写业务逻辑,禁止进行数据库查询或状态判断;
- DTO 的职责是"结构定义 + 参数校验",保持轻量与可复用;
- 输出字段必须使用独立的 Serializer,以避免返回数据库模型原始结构;
- 所有外部输入必须通过 DTO 校验后再进入 Service 层;
- 禁止在 Controller 层直接使用 request.data,必须通过DTO的validated_data 获取参数。
3.【强制】Service 层(业务编排层)
负责:业务流程、事务、状态流转、幂等、外部调用封装、聚合查询
禁止:依赖 request/response;禁止返回 ORM 实例给外层;禁止把校验散到多处
说明:
- 方法一定使用
@staticmethod - 权限校验、认证、trace_id 注入等由装饰器与中间件统一处理
- 对于多表写入、支付回调、审批流程、财务操作,必须使用事务
- 统一返回标准响应结构,包括 code、message、data
- 函数传参超过 6 个业务参数,就必须结构化 / keyword-only
- 所有请求参数必须通过 DTO(serializers)验证,不得直接引用 request.data
4.【强制】Model 层(数据模型层)
只允许:字段、索引、基础 Manager、轻量便捷方法
禁止:流程型业务、外部接口调用、跨域编排
说明:
- Model 层仅定义结构信息与必要的字段约束
- 禁止在 Model 写业务逻辑、外部请求逻辑、流程控制逻辑
- 所有业务处理必须位于 Service 层
- 模型应具备可维护性、易扩展性,字段命名必须语义明确
- 字符串字段必须显式声明 max_length
- 布尔字段必须指定默认值
- 时间字段必须使用 auto_now / auto_now_add 或统一维护逻辑
- 必须添加必要索引(db_index=True)
- company_id(多租户隔离字段)
- 外键字段(如 user_id、order_id)
- 状态类字段(status)
- 高频查询字段(phone、username、open_id 等)
5.【推荐】项目结构搭建各层应用分离,具体可参考以下代码。
# (以 Django 项目为例)
project/
├── apps/
│ ├── user/
│ │ ├── models/
│ │ │ └── user.py # Model 层(实体定义)
│ │ ├── services/
│ │ │ └── user_service.py # Service 层(业务逻辑)
│ │ ├── views/
│ │ │ └── user_view.py # Controller 层(接口控制器)
│ │ ├── serializers/
│ │ │ └── user_dto.py # DTO/VO(序列化、数据校验)
│ │ ├── urls.py # 路由配置
│ │ └── constants.py # 模块内常量/枚举
│ │
│ ├── order/
│ │ ├── models/
│ │ ├── services/
│ │ ├── views/
│ │ ├── serializers/
│ │ ├── urls.py
│ │ └── constants.py
│ │
│ └── ...(其他业务模块)
│
├── utils/ # 工具函数(如加密、日期处理、日志)
│ ├── crypto_sm.py
│ ├── logging_utils.py
│ └── sensitive_filter.py
│
├── middleware/ # 中间件(trace_id、权限、日志)
│ └── request_tracing.py
│
├── config/ # 配置文件(日志配置、环境配置)
│ └── logging_conf.py
│
├── common/ # 公共基类(BaseModel、BaseService)
│ ├── base_model.py
│ └── base_service.py
│
├── tests/ # 单元测试
│ └── test_user_service.py
│
└── manage.py
代码规范
1.【强制】单个函数不超过 80 行,单个函数职责单一。
2.【推荐】复杂条件判断需拆分为语义函数,复杂流程必须使用状态机或流程表表达。
- Service / DAO(Repository)层方法命名规约
1)基本规则
- 【强制】统一使用 snake_case。
- 【强制】方法名必须体现:动作 + 领域对象 + 可选限定条件。
- 【强制】Service 方法表达业务意图;Repository/DAO 方法表达数据访问意图。
- 【强制】】禁止 query_* 作为前缀:在 Django 语境下"query"与 QuerySet/ORM 概念混淆,且语义不如 get/list/find 清晰。
2)推荐前缀(按"返回类型/动作"划分)
单个对象(返回对象或 None)
- 【推荐】get_*:按唯一键/主键获取
- 【推荐】find_*:按条件查找一个(强调"可能找不到")
- 【可选】require_*:必须存在,否则抛业务异常(适合 Service 层)
示例:
- get_user_by_id(user_id)
- find_active_contract_by_user(user_id)
- require_company(company_id)(不存在直接抛 BusinessException)
多个对象(返回 list / QuerySet / 分页结果)
- 【推荐】list_*:返回列表/分页(强调"多条")
- 【推荐】search_*:带关键词/多条件检索
- 【推荐】paginate_*:明确返回分页结构(page/page_size/total/items)
示例:
- list_users_by_company(company_id)
- search_jobs(keyword, city_code)
- paginate_orders(company_id, page, page_size)
统计 / 聚合(返回数值/聚合结构)
- 【推荐】】count_*:数量统计
- 【推荐】sum_* / avg_* / stats_*:金额/均值/综合统计
示例:
- count_pending_orders(company_id)
- sum_paid_amount(company_id, month)
- stats_finance_dashboard(company_id, date_range)
写入(创建/保存)
- 【强制】区分"创建"和"更新"语义,避免一个 save_* 既创建又更新导致幂等/审计混乱
- 【推荐】create_*:创建新对象
- 【推荐】bulk_create_*:批量创建
- 【推荐】upsert_*:明确"存在则更新,不存在则创建"(仅在确实需要且有唯一约束时)
示例:
- create_user(payload)
- bulk_create_images(report_id, images)
- upsert_payment_binding(user_id, platform_id, payload)
更新(修改)
- 【推荐】update_*:更新对象(最好说明依据和范围)
- 【推荐】patch_*:部分更新(字段集合较小、可选字段多)
- 【推荐】set_*:仅设置单一状态/字段(更语义化)
示例:
- update_user_profile(user_id, payload)
- patch_contract(contract_id, payload)
- set_order_status(order_id, status)
删除(物理删除 / 软删除)
- 【强制】必须区分 hard delete 与 soft delete
- 【推荐】soft_delete_*:逻辑删除(设置 is_deleted=True)
- 【推荐】delete_*:物理删除(仅限极少数场景)
- 【推荐】restore_*:恢复软删除数据
示例:
- soft_delete_user(user_id)
- delete_temp_file(file_id)
- restore_payment_binding(binding_id)
- 领域模型命名规约(Django 版)
1)持久化对象(Model / Entity)
- 【强制】Django ORM 实体类:Xxx(不加 DO 后缀)
- 【推荐】数据库表名在 Meta.db_table 定义(snake_case)
示例:
- class UserAccount(models.Model): ...
- class AccidentReport(models.Model): ...
2)数据传输对象(DTO)
- 【强制】入参校验用 XxxDTO(Serializer / Pydantic 均可)
- 【推荐】DTO 只做结构定义/校验/格式转换,不查库不写业务
示例:
- class UserLoginDTO(serializers.Serializer): ...
- class ProtocolIssueDTO(serializers.Serializer): ...
3)展示对象(VO)
- 【强制】对外响应结构用 XxxVO
- 【推荐】VO 与 DTO 分离,避免"入参字段"混进"出参字段"
示例:
- class UserInfoVO(serializers.Serializer): ...
4)业务对象(BO / Command / Query)
当 Service 传参复杂、需要结构化时,推荐引入更清晰的语义对象:
- 【推荐】XxxCommand:表示一次业务动作的输入(更贴近 DDD)
- 【推荐】XxxQuery:表示一次查询意图的输入
- 【推荐】XxxBO:Service 内部的业务中间结构(不对外)
示例:
- ProtocolIssueCommand
- OrderSearchQuery
5)统称命名
- 【强制】禁止出现 xxxPOJO(Python 里同理:禁止 xxxObject/xxxData 这种无语义后缀)
- 【推荐】统一用:Model / DTO / VO / Command / Query / BO 这些可读且可治理的类型名
日期时间
1.【强制】日期格式中必须严格区分大小写,M 表示月份、m 表示分钟,H 表示 24 小时制小时,h 表示 12 小时制小时,禁止因格式符号误用导致日期解析或逻辑错误。
2.【强制】禁止在程序中将一年固定写死为 365 天,所有跨年、跨月的时间计算必须以真实公历规则为准。
3.【强制】涉及闰年场景时,必须正确处理 2 月 29 日的存在性,禁止假设任意日期在"加一年"后仍然合法。
4.【强制】日期计算不得通过简单的天数加减模拟自然年或自然月,必须使用标准日期时间能力完成。
5.【强制】 系统内时间获取与存储语义必须保持一致,禁止在业务逻辑中混用字符串时间与时间对象进行比较或运算。
异步与并发规约
1.【强制】所有可能被重复调用的接口或函数(如支付回调、状态变更、外部通知)必须具备幂等性设计,禁止假设请求只会执行一次。
2.【强制】幂等性必须通过业务唯一标识 + 数据库约束或锁机制保证,禁止仅依赖内存状态或临时变量判断。
3.【强制】涉及并发写操作(如余额扣减、状态流转、资源占用)的逻辑,必须使用明确的并发控制手段(数据库唯一约束、行级锁、乐观锁或分布式锁)。
4.【强制】Web 请求线程内禁止使用 while 循环、阻塞等待或手动重试逻辑,所有重试与补偿必须交由异步任务或延迟队列处理。
5.【强制】禁止在 Django 请求线程中直接创建线程、线程池或协程池,所有并发执行必须通过统一的异步任务体系实现。
6.【强制】多表写入或状态级联变更场景下,必须使用事务保证原子性,禁止依赖调用顺序或概率正确性。
7.【强制】回调接口在完成验签与幂等校验之前,禁止执行业务逻辑。
8.【强制】并发控制逻辑必须显式可见,禁止在函数内部隐式加锁或隐藏副作用。
9.【强制】请求线程对异步任务的唯一职责是一次性投递任务,投递成功后必须立即返回响应,禁止在请求线程中等待任务执行结果或感知任务成功与否。
10.【强制】所有异步任务必须具备可追踪性,必须能通过唯一任务标识(如 task_id、biz_id、order_id)查询任务的创建时间、当前状态、执行次数及最近一次失败原因。
11.【强制】异步任务必须定义明确的重试策略,包括最大重试次数、重试间隔及最终失败后的处理方式,禁止无限重试或无边界循环重试。
12.【强制】超过最大重试次数的异步任务必须进入失败终态,并触发告警或人工介入机制,禁止失败任务长期处于"无状态"或"不可见"状态。
13.【强制】异步任务中的事务必须独立管理,不得依赖 Web 请求线程中的事务上下文,禁止假设请求事务在异步执行阶段仍然有效。
14.【强制】任何可能产生并发冲突的业务逻辑,必须优先通过数据库层约束或原子操作解决,并发安全不得仅依赖应用层判断。
15.【强制】分布式锁仅可作为并发控制的补充手段,禁止作为默认并发方案;使用分布式锁时必须设置超时机制,防止死锁。
16.【强制】异步任务与并发处理过程中产生的异常必须被完整捕获并记录,异常日志必须包含业务标识与任务标识,不得因异常导致 Worker 进程崩溃。
17.【强制】任何异步任务或并发处理逻辑,若无法被监控、无法告警、无法追溯执行结果,一律视为不合规设计。
幂等与补偿规范
1.【强制】所有来自外部系统的推送、回调或通知接口,必须假设会被重复调用、乱序调用或延迟调用,禁止假设"只回调一次"。
2.【强制】所有回调与推送接口必须具备幂等性设计,同一业务标识(如 out_trade_no、biz_id、order_id、notify_id)的回调不得导致业务状态被重复变更。
3.【强制】幂等性必须通过业务唯一标识 + 数据库唯一约束或状态校验实现,禁止仅依赖内存变量、缓存标志或进程状态判断。
4.【强制】回调接口在完成验签、来源校验和幂等校验之前,禁止执行业务逻辑,禁止写库、改状态或触发下游流程。
5.【强制】回调接口必须支持重复回调安全处理,已处理完成的回调再次到达时,必须快速返回成功响应,不得抛出异常或再次执行业务逻辑。
6.【强制】回调接口的响应必须遵循"成功即确认"原则,禁止因内部非关键异常返回失败响应,导致外部系统持续重试。
7.【强制】任何回调处理失败,不得在回调接口中进行同步重试或阻塞等待,失败处理必须交由异步补偿机制完成。
8.【强制】推送或回调的请求与响应必须记录完整的业务日志,包括业务标识、回调来源、验签结果、处理结果及失败原因,日志中不得包含敏感明文信息。
9.【强制】所有对外推送行为必须具备失败补偿能力,推送失败后必须进入异步重试或补偿流程,禁止"一次推送失败即丢失"。
10.【强制】推送补偿必须具备明确的重试策略,包括最大重试次数、重试间隔及最终失败后的处理方式,禁止无限重试。
11.【强制】超过最大重试次数的推送或回调补偿任务,必须进入失败终态,并触发告警或人工处理流程,不得长期处于不可见状态。
12.【强制】推送与回调的幂等状态必须持久化存储,禁止仅依赖缓存或临时表,以防止服务重启或故障导致幂等失效。
13.【强制】涉及资金、订单、合同、生效状态等关键业务的回调处理,必须具备明确的状态机约束,禁止状态回退或非法状态跳转。
14.【强制】推送、回调及其补偿逻辑必须具备可追踪性,系统必须能够通过业务标识查询每一次推送、回调与补偿的执行记录。
15.【强制】任何无法确认是否处理成功、无法判断当前状态、无法进行补偿的推送或回调设计,一律视为不合规实现。
控制语句
1.【强制】业务判断条件必须语义清晰,禁止在 if、while 等控制语句中直接书写复杂业务规则。
2.【强制】当条件判断包含多个业务含义时,必须拆分为语义化布尔函数,禁止通过逻辑运算符堆叠表达业务规则。
3.【【强制】控制语句嵌套层级不得超过 2 层,超过时必须通过函数拆分或提前返回(early return)降低复杂度。
4.【强制】禁止使用 if / elif / else 链条实现复杂业务流程或状态流转,必须使用状态映射表或状态机模型。
5.【强制】条件分支必须穷尽所有合法状态,禁止依赖默认 else 吞掉异常或未知分支。
6.【强制】禁止在循环中编写与循环无关的业务逻辑,循环体内只允许处理当前迭代对象。
7.【强制】禁止在循环中执行不受控的数据库查询或外部接口调用,必须提前批量获取或使用缓存。
8.【强制】异常控制不得依赖 if 判断替代,错误场景必须通过异常机制显式中断流程。
9.【强制】使用控制语句隐式改变核心业务状态(如在条件判断中完成写操作)。
前后端规约
1.【强制】前后端交互的 API,需要明确协议、域名、路径、请求方法、请求内容、状态码、响 应体。 说明:
1)协议:生产环境必须使用 HTTPS。
2)路径:每一个 API 需对应一个路径,表示 API 具体的请求地址:
a) 代表一种资源,只能为名词,推荐使用复数,不能为动词,请求方法已经表达动作意义。
b) URL 路径不能使用大写,单词如果需要分隔,统一使用下划线。
c) 路径禁止携带表示请求内容类型的后缀,比如".json",".xml",通过 accept 头表达即可。
3)请求方法:对具体操作的定义,常见的请求方法如下:
a) GET:从服务器取出资源。
b) POST:在服务器新建一个资源。
c) PUT:在服务器更新资源。
d) DELETE:从服务器删除资源。
4)请求内容:URL 带的参数必须无敏感信息或符合安全要求;body 里带参数时必须设置Content-Type。
5)响应体:响应体 body 可放置多种数据类型,由 Content-Type 头来确定。
- 【强制】前后端数据列表相关的接口返回,如果为空,则返回空数组[]或空集合{}。
说明:此条约定有利于数据层面上的协作更加高效,减少前端很多琐碎的 null 判断。
- 【强制】服务端发生错误时,返回给前端的响应信息必须包含 HTTP 状态码,errorCode、
errorMessage、用户提示信息四个部分。
说明:四个部分的涉众对象分别是浏览器、前端开发、错误排查人员、用户。其中输出给用户的提示信息。
要求:简短清晰、提示友好,引导用户进行下一步操作或解释错误原因,提示信息可以包括错误
原因、上下文环境、推荐操作等。errorMessage:简要描述后端出错原因,便于错误排查人员快速定位问题,注意不要包含敏感数据信息。
正例:常见的 HTTP 状态码如下:
1) 200 OK: 表明该请求被成功地完成,所请求的资源发送到客户端。
2) 401 Unauthorized: 请求要求身份验证,常见对于需要登录而用户未登录的情况。
3) 403 Forbidden:服务器拒绝请求,常见于机密信息或复制其它登录用户链接访问服务器的情况。
4) 404 Not Found: 服务器无法取得所请求的网页,请求资源不存在。
5) 500 Internal Server Error: 服务器内部错误。
- 【强制】在前后端交互的 JSON 格式数据中,所有的 key 必须为小写字母开始 lowerCamelCase 风格,符合英文表达习惯,且表意完整。
正例:errorCode / errorMessage / assetStatus / menuList / orderList / configFlag
反例:ERRORCODE / ERROR_CODE / error_message / error-message / errormessage
- 【强制】errorMessage 是前后端错误追踪机制的体现,可以在前端输出到 type="hidden"
文字类控件中,或者用户端的日志中,帮助我们快速地定位出问题。
6.【强制】HTTP 请求通过 URL 传递参数时,不能超过 2048 字节。
说明:不同浏览器对于 URL 的最大长度限制略有不同,并且对超出最大长度的处理逻辑也有差
异,2048字节是取所有浏览器的最小值。
反例:某业务将退货的商品 id 列表放在 URL 中作为参数传递,当一次退货商品数量过多时,URL 参数超长,传递到后端的参数被截断,导致部分商品未能正确退货。
7.【强制】HTTP 请求通过 body 传递内容时,必须控制长度,超出最大长度后,后端解析会出错。
说明:nginx 默认限制是 1MB,tomcat 默认限制为 2MB,当确实有业务需要传较大内容时,可以通过调大服务器端的限制。
8.【强制】在翻页场景中,用户输入参数的小于 1,则前端返回第一页参数给后端;后端发现用户输入的参数大于总页数,直接返回最后一页。
9.【强制】服务器内部重定向必须使用 forward;外部重定向地址必须使用 URL 统一代理模块生成,否则会因线上采用 HTTPS 协议而导致浏览器提示"不安全",并且还会带来 URL 维护不一致的问。
10.【推荐】服务器返回信息必须被标记是否可以缓存,如果缓存,客户端可能会重用之前的请求结果。
说明:缓存有利于减少交互次数,减少交互的平均延迟。 正例:http 1.1 中,s-maxage 告诉服务器进行缓存,时间单位为秒,用法如下, response.setHeader("Cache-Control", "s-maxage=" + cacheSeconds);
11.【推荐】服务端返回的数据,使用 JSON 格式而非 XML。
说明:尽管 HTTP 支持使用不同的输出格式,例如纯文本,JSON,CSV,XML,RSS 甚至 HTML。如果我们使用的面向用户的服务,应该选择 JSON 作为通信中使用的标准数据交换格式,包括请求和响应。此外,application/JSON 是一种通用的 MIME 类型,具有实用、精简、易读的特点。
12.【推荐】前后端的时间格式统一为"yyyy-MM-dd HH:mm:ss",统一为 GMT。
13.【参考】在接口路径中不要加入版本号,版本控制在 HTTP 头信息中体现,有利于向前兼容。
说明:用户在低版本与高版本之间反复切换时,会导致迁移复杂度升高,存在数据错乱风险。
其他
1.【推荐】及时清理不再使用的代码段或配置信息。
说明:对于垃圾代码或过时配置,坚决清理干净,避免程序过度臃肿,代码冗余。
正例:对于暂时被注释掉,后续可能恢复使用的代码片断,在注释代码中todo
📘 函数规约
参数传递
1.【强制】函数业务参数超过 6 个:必须结构化(DTO/dataclass)或 keyword-only 参数。
说明: keyword-only 参数(强制)
-
*之后的参数只能使用关键字传递
-
禁止位置参数调用
-
防止参数错位、误传
@staticmethod
def protocol_issuance_inner(*,user_id: int,agent_id: int,company_id: int,
template_id: int,start_month: str,end_month: str,check_exist: bool = True):说明: 参数结构化: 当参数存在语义分组时,必须结构化。@dataclass(frozen=True)
class ProtocolActor:
user_id: int
agent_id: int
company_id: int
creator: str@staticmethod
def protocol_issuance_inner(*,actor: ProtocolActor,template_id: int,start_month: str,end_month: str):
函数接口与参数边界
1.【强制】参数在系统内必须遵循单向收敛规则:form_data → DTO / dataclass → params → 明确函数参数,任何参数一旦进入 Service / Domain 层,必须具备确定语义、确定结构、确定类型。
2.【强制】form_data 仅用于 Controller / API 接口层,表示原始外部输入数据。
说明:form_data 来源于 HTTP 请求(JSON / form / query),不可信、不稳定、不可直接参与业务逻辑。
正例:
def issue_protocol(request):
form_data = request.data
dto = ProtocolIssueDTO(data=form_data)
dto.is_valid(raise_exception=True)
ProtocolService.issue_protocol(params=dto.to_params())
反例:
def issue_protocol(request):
ProtocolService.issue_protocol(params=request.data) # ❌ 原始数据直传
3.【强制】**kwargs 仅允许用于 外部接口封装 / 适配层,禁止进入 Service / Domain 层。
允许场景:
- HTTP / RPC 请求参数透传
- 第三方接口参数适配(字段不稳定或随版本变化)
- 通用封装层(如 request / push / callback 适配器)
说明:**kwargs 的本质是"不确定参数集合",禁止用于承载确定业务语义。
正例(第三方接口封装):
class AlipayClient:
@staticmethod
def call_api(method: str, **kwargs):
payload = build_payload(method, kwargs)
return send_request(payload)
反例(业务函数使用 kwargs):
def issue_protocol(**kwargs): # ❌ 业务语义不可见
...
4.【强制】params 表示 已校验、已结构化、业务语义确定的参数集合。
说明:params 不是"随便起的名字",而是系统内部统一的"业务参数载体"。
使用前提:
- 来源必须是 DTO / dataclass / TypedDict / 明确结构体之一,禁止使用裸 dict
- 已完成校验、类型转换、字段映射
- 不包含任何原始外部输入数据
- 仅用于 Service → Service 或 Service → Repository / Domain 调用
正例:
@staticmethod
def issue_protocol(*, params: ProtocolIssueParams):
user_id = params.user_id
company_id = params.company_id
反例:
def issue_protocol(params: dict): # ❌ 裸 dict
...
5.【强制】Service / Domain 层函数禁止直接接收request、form_data、**kwargs、原始 dict 作为业务参数。
说明:所有外部输入参数必须在 Controller 层完成解析与校验,并转换为 DTO / dataclass / 明确的函数参数后再传入业务层。
6.【强制】内部业务函数参数必须命名为 params 或显式语义对象,禁止使用 kwargs 作为内部参数名。
说明:params 表示结构化、已校验、语义确定的参数集合,与 kwargs 的"不确定性"语义必须严格区分。
7.【强制】HTTP 表单或请求体数据在 Controller 层统一命名为 form_data,禁止直接向下层透传。
说明:form_data 仅表示"原始请求数据",不得进入 Service 层;必须先完成字段映射、类型转换与校验。
8.【强制】form_data 与业务参数必须完成职责切割:
- form_data:外部输入、原始数据、可能不可信;
- params / DTO:内部使用、语义稳定、类型安全。
9.【推荐】当参数存在明显语义分组、生命周期或权限边界时,优先使用 dataclass(frozen=True) 或 DTO 进行结构化建模。
说明:结构化参数优先级高于 params,用于表达领域概念而非调用便利性。
| 名称 | 使用层级 | 含义 |
|---|---|---|
| form_data | Controller | 原始请求数据,未绑定业务语义 |
| params | Service / Domain | 已校验、结构明确的业务参数 |
| DTO / dataclass | 业务层 | 领域语义对象,强类型、可审计 |
| kwargs | 接口 / 适配层 | 不稳定参数集合,仅用于透传 |
📘 MySQL 数据库
建表规约
1.【强制】表达"是 / 否"语义的字段必须使用 is_xxx 命名,类型为 tinyint unsigned,取值范围固定为 0 / 1。
说明:任何字段如不允许出现负数,必须使用 unsigned;数据库层不允许使用 boolean 类型。
正例:表达逻辑删除的字段名is_deleted,1 表示删除,0 表示未删除。
2.【强制】表名、字段名必须使用小写字母与下划线,禁止大小写混用,禁止数字开头,禁止使用连续下划线。
说明:MySQL 在Windows 下不区分大小写,但在Linux 下默认是区分大小写。因此,数据库名、表名、字段名,都不允许出现任何大写字母,避免节外生枝。
正例:aliyun_admin,rdc_config,level3_name
反例:AliyunAdmin,rdcConfig,level_3_name
3.【强制】表名不使用复数名词。
说明:表名应该仅仅表示表里面的实体内容,不应该表示实体数量。
4.【强制】禁用保留字,如desc、range、match、delayed 等,请参考MySQL 官方保留字。
5.【强制】主键索引名为pk_字段名;唯一索引名为uk_字段名;普通索引名则为idx_字段名。
说明:pk_ 即primary key;uk_ 即 unique key;idx_ 即index 的简称。
6.【强制】小数类型为decimal,禁止使用float 和double。
说明:在存储的时候,float 和 double 都存在精度损失的问题,很可能在比较值的时候,得到不正确的结果。如果存储的数据范围超过decimal的范围,建议将数据拆成整数和小数并分开存储。
7.【强制】如果存储的字符串长度固定或接近固定时使用 char,变长字符串使用 varchar。
8.【强制】varchar 是可变长字符串,不预先分配存储空间,长度不要超过5000,超过该长度必须使用 text,并与主表拆分,避免影响索引与查询性能。
9.【强制】表必备三字段:id, create_at, update_at。
说明:字段命名必须与 Django ORM 生态保持一致,禁止使用 create_time / update_time 混用。
10.【强制】所有表与字段必须显式指定字符集为 utf8mb4,禁止使用 MySQL 默认字符集
11.【强制】存储引擎统一使用 InnoDB,禁止使用 MyISAM。
12.【推荐】表的命名最好是遵循"业务名称_表的作用"。 正例:orce_project / trade_config
13.【推荐】库名与应用名称尽量一致。
14.【推荐】如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。
15..【推荐】字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循:
1) 不是频繁修改的字段。
2) 不是唯一索引的字段。
3) 不是varchar 超长字段,更不能是text 字段。
正例:各业务线经常冗余存储商品名称,避免查询时需要调用IC 服务获取。
16.【推荐】单表行数超过500 万行或者单表容量超过2GB,才推荐进行分库分表。
说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
17.【参考】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。
正例:无符号值可以避免误存负数,且扩大了表示范围。
| 对象 | 年龄区间 | 类型 | 字节 | 表示范围 |
|---|---|---|---|---|
| 人 | 150 岁之内 | tinyint unsigned | 1 | 无符号值:0 到255 |
| 龟 | 数百岁 | smallint unsigned | 2 | 无符号值:0 到65535 |
| 恐龙化石 | 数千万年 | int unsigned | 4 | 无符号值:0 到约43 亿 |
| 太阳 | 约50 亿年 | bigint unsigned | 8 | 无符号值:0 到约10 的19 次方 |
索引规约
1.【强制】业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。
说明:不要以为唯一索引影响了insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
2.【强制】超过三个表禁止join。需要join 的字段,数据类型保持绝对一致;多表关联查询时,保证被关联的字段需要有索引。
说明:即使双表join 也要注意表索引、SQL 性能。
3.【强制】在varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度。
说明:索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为20 的索引,区分度会高达90%以上,可以使用count(distinct left(列名, 索引长度))/count(*)的区分度来确定。
4.【强制】禁止左模糊或全模糊查询(%xxx / %xxx%),全文搜索必须交由搜索引擎解决。
说明:索引文件具有B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。
5.【推荐】如果有order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现file_sort 的情况,影响查询性能。
正例:where a=? and b=? order by c; 索引:a_b_c
反例:索引如果存在范围查询,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引a_b 无法排序。
6.【推荐】利用覆盖索引来进行查询操作,避免回表。
说明:如果一本书需要知道第11 章是什么标题,会翻开第11 章对应的那一页吗?目录浏览一下就好,这个目录就是起到覆盖索引的作用。
正例:能够建立索引的种类分为主键索引、唯一索引、普通索引三种,而覆盖索引只是一种查询的一种效果,用explain 的结果,extra 列会出现:using index。
7.【推荐】利用延迟关联或者子查询优化超多分页场景。
说明:MySQL 并不是跳过offset 行,而是取offset+N 行,然后返回放弃前offset 行,返回N 行,那当offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行SQL改写。
正例:先快速定位需要获取的id 段,然后再关联:SELECT t1.* FROM 表1 as t1, (select id from 表1 where 条件 LIMIT 100000,20 ) as t2 where t1.id=t2.id
8.【推荐】SQL 性能优化的目标:至少要达到 range 级别,要求是ref 级别,如果可以是consts 最好。
说明:
1) consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
2) ref 指的是使用普通的索引(normal index)。
3) range 对索引进行范围检索。
反例:explain 表的结果,type=index,索引物理文件全扫描,速度非常慢,这个index 级别比较range还低,与全表扫描是小巫见大巫。
9.【推荐】建组合索引的时候,区分度最高的在最左边。
正例:如果where a=? and b=?,a 列的几乎接近于唯一值,那么只需要单建idx_a 索引即可。 说明:存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。如:where c>? and d=? 那么即使c 的区分度更高,也必须把d 放在索引的最前列,即建立组合索引idx_d_c。
10.【推荐】防止因字段类型不同造成的隐式转换,导致索引失效。
11.【参考】创建索引时避免有如下极端误解:
1) 索引宁滥勿缺。认为一个查询就需要建一个索引。
2) 吝啬索引的创建。认为索引会消耗空间、严重拖慢记录的更新以及行的新增速度。
3) 抵制惟一索引。认为惟一索引一律需要在应用层通过"先查后插"方式解决。
SQL 语句
1.【强制】统计行数必须使用 count(*),禁止使用 count(列) 或 count(常量)。
说明:count(*)会统计值为NULL 的行,而count(列名)不会统计此列为NULL 值的行。
2.【强制】count(distinct col) 计算该列除NULL 之外的不重复行数,注意 count(distinct col1, col2) 如果其中一列全为NULL,那么即使另一列有不同的值,也返回为0。
3.【强制】当某一列的值全是NULL 时,count(col)的返回结果为0,但sum(col)的返回结果为 NULL,因此使用sum()时需注意NPE 问题。
正例:可以使用如下方式来避免sum 的NPE 问题:SELECT IFNULL(SUM(column), 0) FROM table;
4.【强制】判断 NULL 统一使用 IS NULL / IS NOT NULL,禁止使用 = NUL。
说明:NULL 与任何值的直接比较都为NULL。
5.【强制】代码中写分页查询逻辑时,若count 为0 应直接返回,避免执行后面的分页语句。
6.【强制】不得使用外键与级联,一切外键概念必须在应用层解决。
说明:(概念解释)学生表中的student_id 是主键,那么成绩表中的student_id 则为外键。如果更新学生表中的student_id,同时触发成绩表中的student_id 更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
7.【强制】禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。
8.【强制】数据订正(特别是删除或修改记录操作)时,要先select,避免出现误删除,确认无误才能执行更新语句。
9.【强制】对于数据库中表记录的查询和变更,只要涉及多个表,都需要在列名前加表的别名(或表名)进行限定。
说明:对多表进行查询记录、更新记录、删除记录时,如果对操作列没有限定表的别名(或表名),并且操作列在多个表中存在时,就会抛异常。
正例:select t1.name from table_first as t1 , table_second as t2 where t1.id=t2.id;
反例:在某业务中,由于多表关联查询语句没有加表的别名(或表名)的限制,正常运行两年后,最近在某个表中增加一个同名字段,在预发布环境做数据库变更后,线上查询语句出现出1052 异常:Column 'name' in field list is ambiguous。
10.【推荐】SQL 语句中表的别名前加as,并且以t1、t2、t3、...的顺序依次命名。
11.【推荐】in 操作能避免则避免,若实在避免不了,需要仔细评估in 后边的集合元素数量,控制在1000 个之内。
12.【参考】因国际化需要,所有的字符存储与表示,均采用utf8 字符集,那么字符计数方法需 要注意。
说明:
SELECT LENGTH("轻松工作"); 返回为12
SELECT CHARACTER_LENGTH("轻松工作"); 返回为4
如果需要存储表情,那么选择utf8mb4 来进行存储,注意它与utf8 编码的区别。
13.【参考】TRUNCATETABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但TRUNCATE无事务且不触发trigger,有可能造成事故,故不建议在开发代码中使用此语句。
说明:TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。
ORM 映射
1.【强制】在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。
说明:
1)增加查询分析器解析成本。
2)增减字段容易与resultMap 配置不一致。
3)无用字段增加网络消耗,尤其是text 类型的字段。
2.【强制】数据库字段与业务字段不一致时,必须通过 Django Model 或 Serializer 映射,禁止直接暴露数据库字段。
3.【强制】sql.xml 配置参数使用:#{},#param# 不要使用${} 此种方式容易出现SQL 注入。
4.【强制】更新数据表记录时,必须同时更新记录对应的update_time 字段值为当前时间。
5.【参考】@Transactional 事务不要滥用。事务会影响数据库的QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。
ORM 使用
1.【强制】ORM 优先、Python 兜底:凡是可以在数据库层完成的操作,禁止在 Python 中实现,包括去重、过滤、聚合与排序。
2.【强制】去重必须使用 distinct(),禁止使用 set();条件过滤必须使用 filter(),禁止通过 for 循环实现筛选逻辑。
3.【强制】QuerySet 仅作为 SQL 描述 使用,不视为实际数据,禁止将 QuerySet 当作 list 使用。
4.【强制】QuerySet 只有在 list()、first()、exists()、get() 等显式落地操作时才允许进入 Python 逻辑。
5.【强制】只要仍需继续 ORM 查询链路(如 __in、子查询、继续 filter / exclude),禁止提前 list() 落地。
6.【强制】一旦进入 Python 层业务逻辑处理,必须立即显式落地 QuerySet(list() / first()),禁止隐式执行。
7.【强制】禁止对 QuerySet 进行布尔判断(如 if qs: / if not qs:),存在性判断必须使用 exists(),逻辑判断必须基于已落地数据。
8.【强制】ORM 使用顺序必须遵循:
QuerySet(懒加载) → values / values_list(字段裁剪) → list / first(数据落地)。
9.【强制】禁止跨数据库、跨连接直接进行 ORM 关联查询,涉及多库数据必须拆分查询并在业务层显式处理。
| ORM | 用什么 |
|---|---|
| 继续 ORM 查询链路 | QuerySet |
| 只判断是否存在 | .exists() |
| 取一条(可能没有) | .first() |
| 取一条(必须有) | .get() |
| 只要一个字段列表 | .values_list(flat=True) |
| 要 dict 结构 | .values() |
| 要 Python 判断 / 返回前端 | list() |
正例:只判断是否存在
agentIdQuery = (
AgentRiderInfo.objects
.filter(riderId=riderId)
.values_list('agentId', flat=True)
.distinct()
)
groupIdQuery = (
ImdadaGroupAgent.objects
.filter(agentId__in=agentIdQuery, is_deleted=False)
.values_list('groupId', flat=True)
.distinct()
)
agentList = list(
CompanyInfo.objects
.filter(id__in=groupIdQuery, is_deleted=False)
.values(code=F('id'), name=F('companyName'))
)
反例:过早 list 导致 DB 失去优化空间
agentIds = list(AgentRiderInfo.objects.filter(riderId=riderId).values_list('agentId', flat=True))
groupIds = ImdadaGroupAgent.objects.filter(agentId__in=agentIds) # 可以,但不如上面
📘 异常日志
错误码
1.【强制】错误码的制定原则:快速溯源、沟通标准化。
说明: 错误码想得过于完美和复杂,就像康熙字典中的生僻字一样,用词似乎精准,但是字典不容易随身携带并且简单易懂。
正例:错误码回答的问题是谁的错?错在哪?
1)错误码必须能够快速知晓错误来源,可快速判断是谁的问题。
2)错误码必须能够进行清晰地比对(代码中容易equals)。
3)错误码有利于团队快速对错误原因达到一致认知。
2.【强制】错误码不体现版本号和错误等级信息。
说明:错误码以不断追加的方式进行兼容。错误等级由日志和错误码本身的释义来决定。
3.【强制】全部正常,但不得不填充错误码时返回五个零:00000。
4.【强制】错误码为字符串类型,共5 位,分成两个部分:错误产生来源+四位数字编号。
说明:错误产生来源分为A/B/C,A 表示错误来源于用户,比如参数错误,用户安装版本过低,用户支付超时等问题;B 表示错误来源于当前系统,往往是业务逻辑出错,或程序健壮性差等问题;C 表示错误来源于第三方服务,比如CDN 服务出错,消息投递超时等问题;四位数字编号从0001 到9999,大类之间的步长间距预留100。
5.【强制】编号不与公司业务架构,更不与组织架构挂钩,以先到先得的原则在统一平台上进行, 审批生效,编号即被永久固定。
6.【强制】错误码使用者避免随意定义新的错误码。
说明:尽可能在原有错误码附表中找到语义相同或者相近的错误码在代码中使用即可。
7.【强制】错误码不能直接输出给用户作为提示信息使用。
说明:堆栈(stack_trace)、错误信息(error_message)、错误码(error_code)、提示信息(user_tip)是一个有效关联并互相转义的和谐整体,但是请勿互相越俎代庖。
8.【推荐】错误码之外的业务独特信息由error_message 来承载,而不是让错误码本身涵盖过多具体业务属性。
9.【推荐】在获取第三方服务错误码时,向上抛出允许本系统转义,由C 转为B,并且在错误信 息上带上原有的第三方错误码。
10.【参考】错误码分为一级宏观错误码、二级宏观错误码、三级宏观错误码。
说明:在无法更加具体确定的错误场景中,可以直接使用一级宏观错误码,分别是:A0001(用户端错误)、B0001(系统执行出错)、C0001(调用第三方服务出错)。
正例:调用第三方服务出错是一级,中间件错误是二级,消息服务出错是三级。
11.【参考】错误码的后三位编号与HTTP 状态码没有任何关系。
12.【参考】错误码有利于不同文化背景的开发者进行交流与代码协作。
说明:英文单词形式的错误码不利于非英语母语国家之间的开发者互相协作。
13.【参考】错误码使用纯数字来进行错误码编排不利于感性记忆和分类。
说明:数字是一个整体,每位数字的地位和含义是相同的。
反例:一个五位数字12345,第1 位是错误等级,第2 位是错误来源,345是编号,人的大脑不会主动地拆开并分辨每位数字的不同含义。
日志规约
1.【强制】所有应用日志文件 至少保留 15 天,以覆盖"按周发生"的异常与隐性问题。
2.【强制】涉及网络运行状态、网络安全事件、个人敏感信息操作等日志,留存时间不得少于6个月,并必须进行多机或异地备份,以满足合规与审计要求。
3.【【强制】当天日志文件统一命名为:{app_name}.log,并存放于:
/home/admin/{app_name}/logs/ 目录下。
4.【强制】历史日志必须按日期滚动,命名格式为:{logname}.log.{yyyy-MM-dd}。
5.【强制】应用扩展日志(如统计日志、监控日志、访问日志等)必须采用以下命名规则:
{app_name}{log_type}{log_name}.log,其中 log_type 用于区分日志类别(如 stats / monitor / access)。
6.【强制】日志文件名必须能够直接体现所属应用、日志类型及用途,禁止使用无语义文件名。
7.【强制】错误日志与业务日志应物理分离,便于问题排查与监控分析。
8.【强制】日志输出时,禁止使用字符串拼接,必须使用日志框架的占位符方式输出变量。
9.【强制】在输出 DEBUG / TRACE 级别日志前,必须显式进行日志级别判断,避免不必要的性能损耗。
10.【强制】必须避免重复打印同一日志内容,日志配置中必须关闭无效继承(如 Python logging 中的重复传播配置)。
11.【强制】生产环境禁止使用 print()、sys.stdout、sys.stderr 或 exception.print_stack_trace() 等方式输出日志或异常堆栈。
12.【强制】异常日志必须同时包含:
- 业务上下文信息(案发现场)
- 异常堆栈信息
13.【强制】异常日志必须通过统一日志组件输出,禁止仅记录异常消息而丢失堆栈。
14.【强制】日志打印时,禁止直接使用 JSON 工具将复杂对象序列化为字符串。
15.【强制】日志中仅允许打印必要的业务字段或对象的安全 str() 表达,禁止因日志打印影响业务流程。
16.【强制】生产环境默认关闭 DEBUG 日志,仅在必要场景下短期开启,并在问题定位完成后及时关闭。
17.【强制】用户输入参数错误、可预期的业务异常,优先使用 WARNING 级别日志,避免滥用 ERROR 级别导致误报警。
18.【强制】日志错误信息优先使用英文描述;如英文不足以清晰表达业务含义,可辅以中文说明,避免歧义。
异常分类
1.【推荐】系统需统一异常类型,并由全局异常处理器(GlobalExceptionHandler)统一生成响应。
2.【强制】系统必须统一异常类型,并由全局异常处理器集中处理与生成响应。
3.【强制】异常响应必须根据异常类型返回标准化错误结构与明确的 HTTP 状态码。
4.【强制】异常日志必须可审计、可追溯,并与请求日志通过 trace_id 关联。
5.【强制】禁止在业务代码中自行拼装异常响应或绕过全局异常处理器。
| 类型 | 说明 | 典型场景 |
|---|---|---|
| ValidationException | 参数校验失败 | DTO 校验不通过、缺少必要字段 |
| BusinessException | 业务逻辑异常 | 用户不存在、状态冲突、额度不足 |
| PermissionException | 权限认证失败 | RBAC 权限不足、公司隔离校验失败 |
| SystemException | 系统内部错误 | 数据库超时、Redis 异常、第三方接口超时 |
异常处理
1.【强制】所有异常必须被显式捕获并处理,禁止异常无日志直接抛出或被吞掉。
2.【强制】捕获异常后必须调用统一的异常记录函数进行错误上报与审计,禁止直接打印异常信息。
3.【强制】异常记录必须包含明确的业务定位信息(location)与必要的上下文信息(detail),禁止只记录异常对象本身。
4.【强制】异常处理逻辑中禁止继续执行业务流程,必须通过抛出业务异常或返回错误结果显式中断。
5.【强制】使用空捕获或无意义捕获,如 except Exception: pass。
6.【强制】在异常处理中直接向外返回原始异常信息或堆栈信息。
7.【强制】在异常处理中记录任何敏感明文数据(如身份证号、银行卡号、密钥、token 等)。
- 异常记录规范
【强制】异常记录函数调用必须遵循统一格式:
-
location:类名.方法名(),用于快速定位异常来源;
-
detail:关键业务参数 + 异常摘要信息,禁止包含敏感数据。
except Exception as e:
sendError(f'RecruitApi.queryAlipayLaborCardUrlV3()', f"outJobId:{str(outJobId)},异常:{str(e)}")
请求日志
1.【推荐】每个请求必须记录以下字段(由 RequestTracingMiddleware 自动注入):
2.【强制】每个请求必须记录完整的请求日志,并由统一中间件自动注入日志上下文。
3.【强制】请求日志必须包含以下字段:trace_id、脱敏后的 user_id、company_id、请求方法、请求路径、脱敏后的请求参数、脱敏后的响应结果以及请求耗时(毫秒)。
4.【强制】请求日志必须可用于链路追踪、性能分析、问题复现和前后端问题归因。
5.【强制】请求日志中禁止记录任何敏感明文信息。
安全日志
1.【强制】以下安全相关行为必须记录到独立的安全日志(security.log),禁止写入普通应用日志:
- 多次登录失败;
- 越权访问;
- 解密失败或验签失败;
- 重复回调、恶意回调、签名不一致;
- 权限或角色变更、公司切换等敏感操作。
2.【强制】安全日志必须包含 trace_id、脱敏后的 user_id、请求来源 IP 以及明确的安全事件标识。
3.【禁止】安全日志中记录任何敏感明文数据。
示例(安全日志结构):
{
"time": "2025-01-16 10:23:10",
"level": "WARNING",
"trace_id": "fa93bdcd",
"event": "login_fail",
"user": "138****8000",
"reason": "密码错误次数过多",
"ip": "127.0.0.1"
}
日志要求
1.【强制】所有日志必须使用统一封装的日志组件输出,禁止使用 print()、临时调试语句或裸 logger。
2.【强制】日志级别必须遵循以下顺序并正确使用:
DEBUG < INFO < WARNING < ERROR < CRITICAL。
3.【强制】所有日志内容必须包含:时间戳、模块名、trace_id 以及清晰的摘要信息。
4.【强制】所有日志输出必须经过统一的脱敏过滤处理。
📘 接口响应规约
统一响应结构
1.【强制】所有接口必须返回统一结构:
{
"code": 200,
"message": "success",
"data": {}
}
2.【强制】任何接口不得返回非 JSON 结构(文件下载除外)。
3.【强制】错误响应必须由全局异常处理器生成,禁止在业务代码中自行拼装错误返回。
路由规范
1.【强制】接口必须遵循 REST 语义,HTTP 方法与行为必须保持一致。
2.【强制】GET 请求不得包含 body;POST 不得用于简单查询。
3.【强制】所有有业务副作用的接口必须明确标识为非幂等。
| 场景 | HTTP 方法 | 示例路径 | 是否幂等 | 说明 |
|---|---|---|---|---|
| 获取列表 | GET | /api/users?page=1&pageSize=10 | ✅ | 无副作用,支持分页与简单筛选 |
| 获取详情 | GET | /api/users/{id} | ✅ | 查询单个资源 |
| 创建资源 | POST | /api/users | ❌ | 新增数据,返回主键或完整对象 |
| 更新资源(全量) | PUT | /api/users/{id} | ❌ | 覆盖更新对象 |
| 更新资源(部分) | PATCH | /api/users/{id} | ❌ | 更新部分字段 |
| 删除资源 | DELETE | /api/users/{id} | ❌ | 删除单个对象,建议逻辑删除 |
| 批量删除 | DELETE / POST | /api/users/batch_delete | ❌ | DELETE 不支持 body 时可使用 POST |
| 条件查询 | POST | /api/users/query | ✅ | 参数复杂或包含数组时使用 |
| 导出数据 | POST | /api/users/export | ❌ | 生成文件/任务,不可 GET |
| 导入数据 | POST | /api/users/import | ❌ | 上传文件,可能含文件流 |
| 文件上传 | POST | /api/upload | ❌ | 需 Content-Type: multipart/form-data |
| 状态切换 | PUT / PATCH | /api/users/{id}/status | ❌ | 改变状态,如启用/禁用 |
| 批量操作 | POST | /api/orders/batch_approve | ❌ | 批量审批、导入、审核等操作 |
| 登录 | POST | /api/login | ❌ | 创建会话/签发 Token |
| 登出 | POST / DELETE | /api/logout | ❌ | 销毁 Token 或 Session |
| 刷新 Token | POST | /api/token/refresh | ❌ | 续签认证信息 |
| 上传签名/图片 | POST | /api/files/upload | ❌ | 附带文件流 |
| 通用验证 | POST | /api/verify/code | ✅ | 验证验证码、短信、签名等 |
| 审批/确认操作 | POST | /api/finance/approve | ❌ | 含业务副作用,必须写入日志 |
| 调用第三方接口 | POST | /api/alipay/notify | ✅ | 回调接口或转发中间层 |
| 异步任务创建 | POST | /api/tasks | ❌ | 创建后台任务 |
| 异步任务查询 | GET | /api/tasks/{id} | ✅ | 查询任务进度 |
| 通知回调(Webhook) | POST | /api/notify/... | ✅ | 外部系统回调接入 |
| 系统心跳 | GET | /api/health | ✅ | 系统健康检查,无副作用 |
| 参数校验或模拟运行 | POST | /api/.../validate | ✅ | 仅做校验,不写库 |
高争议场景规约
1.【强制】分页查询默认使用 GET;当查询条件复杂或包含数组结构时,必须使用 POST /query,且保持幂等。
2.【强制】导出操作不得使用 GET 直接生成大文件;必须通过 POST 创建任务,再通过任务接口查询或下载结果。
3.【强制】批量操作统一使用 POST,请求体必须显式包含操作对象与行为类型。
4.【强制】状态变更接口必须使用独立路径(如 /status),禁止与普通更新混用。
5.【强制】逻辑删除接口语义仍使用 DELETE,但底层实现必须为软删除。
6.【强制】文件上传接口必须使用 multipart/form-data,并返回可审计的文件标识信息。
7.【强制】第三方回调接口必须跳过登录鉴权,但必须执行验签、幂等校验与完整日志记录。
8.【强制】异步任务必须提供"创建 + 查询"接口对,禁止只创建不提供查询能力。
标准状态码
| 状态码 | 含义 | 使用场景 |
|---|---|---|
| 200 | OK | 成功 |
| 201 | Created | 资源创建成功 |
| 204 | No Content | 删除成功 |
| 400 | Bad Request | 参数错误 |
| 401 | Unauthorized | 未登录 |
| 403 | Forbidden | 权限不足 |
| 404 | Not Found | 资源不存在 |
| 409 | Conflict | 状态冲突 / 唯一约束 |
| 422 | Unprocessable Entity | 业务条件不满足 |
| 500 | Internal Error | 系统异常 |
📘 安全与权限规约
通信与加密规范
1.【强制】所有系统间通信及涉及敏感数据的接口必须进行加密传输,禁止任何形式的明文交互。
2.【强制】涉及隐私、财务、身份信息的接口必须启用加密机制,禁止以"内部接口"为由绕过加密。
3.【强制】所有加解密、签名与验签操作必须使用统一安全模块(如 utils.crypto_sm),禁止各系统自行实现算法逻辑。
4.【强制】加密密钥必须存储在安全配置中心或 KMS 中,禁止硬编码、禁止入库、禁止入日志。
5.【强制】密钥必须支持版本管理与轮换机制,过期或泄露密钥必须立即废止。
6.【强制】所有验签失败、解密失败事件必须记录安全日志,不得向客户端暴露具体失败原因。
7.【禁止】在任何日志中打印密钥、明文数据、密文原文或解密前内容。
8.【强制】系统数据加密采用AES算法,接口加密算法统一采用以下国密体系:
| 类型 | 算法 | 用途 |
|---|---|---|
| 对称加密 | SM4 | 报文加密、敏感数据存储 |
| 非对称加密 | SM2 | 签名、密钥加密 |
| 摘要算法 | SM3 | 数据完整性校验 |
权限与访问控制规范
1.【强制】系统权限模型必须采用 多租户 RBAC + 公司级隔离 设计,禁止单一维度授权。
2.【强制】权限必须通过角色进行分配,禁止直接向用户授予权限。
3.【强制】所有权限校验必须基于 company_id 维度进行,确保不同公司之间数据与权限完全隔离。
4.【强制】数据访问必须校验当前用户所属公司与目标数据公司一致,否则返回 HTTP 403 并记录安全日志。
5.【禁止】跨公司、跨租户访问任何资源,除非在系统配置中显式授权。
6.【强制】隶属于用户个人的页面或者功能必须进行权限控制校验。
说明:防止没有做水平权限校验就可随意访问、修改、删除别人的数据,比如查看他人的私信内容。
7.【强制】用户敏感数据禁止直接展示,必须对展示数据进行脱敏。
说明:中国大陆个人手机号码显示:139****1219,隐藏中间4 位,防止隐私泄露。
8.【强制】用户输入的SQL 参数严格使用参数绑定或者METADATA 字段值限定,防止SQL 注入, 禁止字符串拼接SQL 访问数据库。
反例:某系统签名大量被恶意修改,即是因为对于危险字符 # --没有进行转义,导致数据库更新时,where后边的信息被注释掉,对全库进行更新。
9.【强制】用户请求传入的任何参数必须做有效性验证。
说明:忽略参数校验可能导致:
- page size 过大导致内存溢出
- 恶意order by 导致数据库慢查询
- 缓存击穿
- SSRF
- 任意重定向
- SQL 注入,Shell 注入,反序列化注入
- 正则输入源串拒绝服务ReDoS
10.【强制】禁止向HTML 页面输出未经安全过滤或未正确转义的用户数据。
11.【强制】表单、AJAX 提交必须执行CSRF 安全验证。
说明:CSRF(Cross-site request forgery)跨站请求伪造是一类常见编程漏洞。对于存在CSRF 漏洞的应用/网站,攻击者可以事先构造好URL,只要受害者用户一访问,后台便在用户不知情的情况下对数据库中用户参数进行相应修改。
12.【强制】URL 外部重定向传入的目标地址必须执行白名单过滤。
13.【强制】在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重放的机 制,如数量限制、疲劳度控制、验证码校验,避免被滥刷而导致资损。
说明:如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其它用户,并造成短信平台资源浪费。
14.【强制】发贴、评论、发送即时消息等用户生成内容的场景必须实现防刷、文本内容违禁词过 滤等风控策略。
接口访问与认证控制
1.【强制】所有接口必须声明权限标识(permission_code),并通过统一装饰器或中间件进行校验。
2.【强制】所有管理端接口必须启用身份认证与权限校验机制(Session / JWT / OAuth2 + RBAC)。
3.【强制】接口身份识别必须通过 Token 或签名机制完成,禁止通过明文参数传递用户标识。
4.【强制】第三方回调接口必须跳过登录校验,但必须执行验签、幂等校验及完整日志记录。
5.【强制】在业务代码中隐式跳过权限校验或自行实现鉴权逻辑。
高风险操作安全控制
1.【强制】以下高风险操作必须启用二次确认或动态安全校验机制:
- 数据删除操作;
- 批量导出、下载;
- 财务审批、支付相关操作;
- 用户权限与角色变更;
- 系统配置及密钥管理。
2.【强制】高风险操作必须记录可审计的安全日志,包含操作者、操作对象、结果及来源 IP。
数据级访问控制
1.【强制】所有查询操作必须自动附带数据归属过滤条件(如 company_id、部门或用户维度)。
2.【强制】模型层必须定义 company_id 作为多租户隔离字段,ORM 查询不得绕过该约束。
3.【强制】敏感数据(财务、身份)必须进行脱敏展示,导出操作必须单独进行权限校验与审计。
日志与审计要求
1.【强制】所有权限校验、权限拒绝、权限变更行为必须记录安全审计日志。
2.【强制】审计日志必须包含:操作者、角色、公司 ID、请求路径、操作行为、时间戳及来源 IP。
3.【强制】系统必须支持权限定期复核与权限使用审计,确保权限配置长期可控。
📘 单元测试规约
1.【强制】好的单元测试必须遵守AIR 原则。
说明:单元测试在线上运行时,感觉像空气(AIR)一样感觉不到,但在测试质量的保障上,却是非常关键的。好的单元测试宏观上来说,具有自动化、独立性、可重复执行的特点。
- A:Automatic(自动化)
- I:Independent(独立性)
- R:Repeatable(可重复)
2.【强制】任何需要人工查看输出结果(如使用 print())才能判断正确性的测试,均视为不合格单元测试。单元测试的正确性必须通过断言进行验证。
3.【强制】必须保持单元测试的独立性。测试用例之间不得互相调用,不得依赖执行先后顺序,也不得共享状态。任何依赖其他测试执行结果作为输入的测试用例,均视为不合规。
4.【强制】单元测试必须具备可重复执行能力,不得受到外界环境影响。单元测试通常会在持续集成环境中频繁执行,若依赖网络、第三方服务或中间件,将导致测试不稳定并破坏 CI 体系。对于存在外部依赖的代码,必须在设计阶段通过依赖注入方式解耦,在测试时使用内存实现或 Mock / Stub 进行替代。
5.【强制】单元测试粒度必须足够小,以便精确定位问题。测试粒度最多为类级,推荐以方法级为主。单元测试不负责验证跨类、跨模块或跨系统交互逻辑,该职责属于集成测试或系统测试。
6.【强制】核心业务、核心应用及核心模块的新增或修改代码,必须确保单元测试通过后方可合入主干分支。若新增代码导致原有单元测试失败,必须同步修复。
7.【强制】单元测试代码必须与业务代码隔离存放,不得写在业务代码目录中。Django 项目中,测试代码应统一放置在 tests/ 或 app/tests/ 目录下,并由测试框架自动扫描执行。
8.【强制】单元测试的基本目标为:整体语句覆盖率不低于 70%;核心模块的语句覆盖率与分支覆盖率应达到 100%。可复用度高的 Service 层与核心业务逻辑应重点覆盖。
9.【【强制】单元测试设计应遵循 BCDE 原则,即覆盖边界条件、验证正确输入的正常行为、结合业务设计进行测试,并对非法输入和异常流程进行验证。
10.【强制】涉及数据库查询、更新或删除的单元测试,不得假设数据库中存在特定数据,也不得通过人工方式直接操作数据库准备测试数据。测试数据必须通过程序方式构造,并符合业务规则。
11.【强制】数据库相关的单元测试应具备自动回滚机制,或对测试产生的数据进行明确标识,以避免对数据库环境造成污染。
12.【推荐】对于难以测试的代码,应在合适时机进行必要的重构以提升可测试性,避免为了满足测试要求而编写不规范或取巧的测试代码。
13.【推荐】在设计评审阶段,开发人员应与测试人员共同确认单元测试范围,单元测试应尽量覆盖业务用例(UC)。
14.【推荐】单元测试应作为质量保障手段,在项目提测前完成,不建议在系统上线后补写核心单元测试用例。
15.【参考】为提升代码可测试性,业务代码应避免构造函数中包含复杂逻辑、过多全局变量或静态方法、过多外部依赖以及过深的条件分支。复杂条件逻辑应通过卫语句、策略模式或状态模式进行重构。
16.【参考】不得对单元测试存在错误认知,包括认为单元测试仅属于测试人员职责、认为单元测试代码是多余的、认为单元测试无需维护,或认为单元测试与线上故障无关。
17.【强制】项目必须编写单元测试,整体测试覆盖率不得低于 80%,核心业务模块必须具备完整的测试用例。
18.【强制】测试文件必须以 test_*.py 命名,并与被测试模块保持清晰的目录映射关系。
19.【强制】单元测试必须覆盖核心业务逻辑、关键分支条件及异常路径,禁止仅覆盖成功路径。
20.【强制】所有接口入参必须通过 Serializer 或自定义校验器进行校验,后端不得仅依赖前端参数校验。
21.【强制】单元测试中必须覆盖参数校验失败场景,确保非法输入能够被正确拦截。
22.【强制】测试返回的错误信息不得包含异常堆栈、系统路径或内部实现细节。
23.【强制】单元测试不得依赖真实外部系统(如第三方接口、支付通道),必须使用 Mock 或 Stub 进行隔离。
24.【强制】单元测试不得依赖执行顺序或共享状态,所有测试用例必须可独立运行。