Files
junhong_cmp_fiber/docs/engineering/工程约束.md
break e134552ec5
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m35s
更新
2026-08-13 12:31:47 +08:00

19 KiB
Raw Blame History

工程约束

本文件只记录从当前代码、配置、迁移、构建或可复现运行结果证明的长期工程规则。业务行为属于 openspec/specs/,第三方协议属于 docs/integrations/。自动化测试当前为 N/A用户决策

ENG-ARCH-001

  • 状态:生效
  • 适用范围internal/domain/**
  • 规则Domain MUST NOT import Fiber、GORM、Redis、Asynq 或具体第三方 SDK。
  • 理由:领域不变量必须能脱离传输和基础设施独立演化。
  • 最小正例Domain 只依赖标准库、领域类型和 Port。
  • 最小反例Domain 直接调用 GORM 或 Redis。
  • 机械检查/人工原因! grep -RIlE 'gofiber|gorm.io|go-redis|hibiken/asynq' internal/domain --include='*.go',当前通过。
  • 例外条件:既有兼容行为仍须先通过独立 Change 才能调整边界。
  • Owner:架构负责人
  • 最后验证日期2026-08-07
  • 更新触发条件Domain 依赖或分层策略变化

ENG-ARCH-002

  • 状态:生效
  • 适用范围:新增或修改完整用例
  • 规则:复杂写 MUST 使用 Application→Domain→Port简单写 MUST 使用 Application 事务脚本;读取 MUST 使用 Query 或既有只读 Service且不得修改状态。
  • 理由:按用例选择最小边界,避免半迁移和形式化 DDD。
  • 最小正例:订单资金状态机收口 Domain列表查询直接 DTO 投影。
  • 最小反例:只为单表 CRUD 创建聚合,或 Query 内更新状态。
  • 机械检查/人工原因:暂为人工:从 Route 追踪到用例和持久化,确认一个完整用例只有一个规则归属。
  • 例外条件:未触碰的旧 internal/service 保持现状。
  • Owner:架构负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:新增用例或触碰旧复杂写

ENG-ERR-001

  • 状态:生效
  • 适用范围:到 HTTP 边界的错误链
  • 规则:到 Handler/HTTP 边界前 MUST 转换为 pkg/errors 稳定错误Handler 参数校验 MUST NOT 拼接或返回底层 err.Error()
  • 理由:全局 ErrorHandler 只能安全映射稳定错误,底层文本可能泄密。
  • 最小正例errors.Wrap(code, err) 后返回给全局 ErrorHandler。
  • 最小反例return c.JSON(...err.Error())
  • 机械检查/人工原因:审查变更中的 Handler 返回点;运行 go build ./cmd/api。全仓 fmt.Errorf 只作候选,不能直接判错。
  • 例外条件:内部不可见、不会越过接口边界的诊断错误允许 fmt.Errorf
  • OwnerAPI 负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:错误系统或 ErrorHandler 变化

ENG-RESP-001

  • 状态:生效
  • 适用范围HTTP Handler
  • 规则:成功响应 MUST 使用 pkg/response;错误 MUST 返回给 internal/middleware.ErrorHandler,不得自造响应壳。
  • 理由:统一 {code,msg,data,timestamp} 和 HTTP 映射。
  • 最小正例return response.Success(c, data)
  • 最小反例Handler 直接 c.JSON 返回另一套结构。
  • 机械检查/人工原因:审查新增 Handlerrg "response\.(Success|Error)" internal/handler internal/routes;生成 OpenAPI 人工核对。
  • 例外条件:第三方回调必须返回渠道要求的字面协议时可使用专用响应。
  • OwnerAPI 负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:响应协议或第三方回调契约变化

ENG-DTO-001

  • 状态:生效
  • 适用范围:进入 OpenAPI 的请求/响应 DTO
  • 规则:公开字段 MUST 有中文 description枚举描述 MUST 与 pkg/constants 当前值一致;状态响应按现有契约提供名称字段。
  • 理由:前端和生成文档依赖字段与枚举精确一致。
  • 最小正例:从 constants 原文复制枚举值并生成 OpenAPI 核对。
  • 最小反例:凭记忆写 description 或遗漏状态名称。
  • 机械检查/人工原因go run cmd/gendocs/main.go 后核对 OpenAPI暂为人工比对枚举与 constants单纯 grep 不足以证明完整。
  • 例外条件:仅内部、不进入接口契约的结构。
  • OwnerAPI 负责人
  • 最后验证日期2026-08-07
  • 更新触发条件DTO、枚举或 OpenAPI 生成变化

ENG-MODEL-001

  • 状态:生效
  • 适用范围:新增/修改 internal/model 与当前根迁移
  • 规则:关联 MUST 保存 ID 并显式查询MUST NOT 新增 GORM foreignKey/hasMany/belongsTo 标签或数据库外键。
  • 理由:当前隔离与渐进迁移依赖显式关联。
  • 最小正例:保存 ShopID,由 Store 显式查询。
  • 最小反例:新增关联切片和 foreignKey 标签。
  • 机械检查/人工原因:只检查变更新增行:git diff -- internal/model migrations | grep -E "^\+.*(foreignKey|hasMany|belongsTo|REFERENCES)",期望空。
  • 例外条件migrations/archive 的历史外键不作为新增违规。
  • Owner:数据负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:模型或关联策略变化

ENG-DB-001

  • 状态:生效
  • 适用范围:生产 Go 数据访问
  • 规则:生产数据访问 MUST 使用 GORMMUST NOT 新增 database/sql 直接访问。
  • 理由统一连接、事务、Callback 数据范围和错误处理。
  • 最小正例:通过 *gorm.DB 或 Store 查询。
  • 最小反例:业务 Service 新建 sql.DB
  • 机械检查/人工原因:检查变更新增 importgit diff -U0 -- "*.go" | grep -E "^\+.*database/sql",期望空。
  • 例外条件:第三方库内部实现不受本规则约束。
  • Owner:数据负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:数据访问基础设施变化

ENG-DB-002

  • 状态:生效
  • 适用范围Agent 对项目数据库的诊断、核对与只读查询。
  • 规则MUST 使用 dbhub MCP测试/本地库使用 mcp__dbhub__execute_sql_main,正式库使用 mcp__dbhub__execute_sql_pro_mainMUST NOT 通过 psql、连接串、环境变量或其他命令行客户端直连数据库。
  • 理由dbhub 提供受控只读访问,避免命令历史、环境凭证和目标库选择漂移。
  • 最小正例:调用 mcp__dbhub__execute_sql_main 查询订单与佣金记录。
  • 最小反例source .env && psql ...
  • 机械检查/人工原因:审查 Agent 执行记录中的数据库访问工具;仓库业务 Go 代码不受本条约束,仍遵守 ENG-DB-001。
  • 例外条件维护者明确提供的、需执行写入或迁移的人工操作按生产运行说明执行Agent 不代执行。
  • Owner:基础设施负责人
  • 最后验证日期2026-08-13
  • 更新触发条件dbhub MCP 名称、访问范围或数据库运维边界变化

ENG-MIG-001

  • 状态:生效
  • 适用范围migrations/ 当前根目录
  • 规则:新迁移 MUST 使用新顺序号并提供 .up.sql/.down.sql 配对MUST NOT 修改已发布迁移。
  • 理由:保证 Schema 可重放和回滚。
  • 最小正例:新增同号 up/down 文件并在隔离库执行。
  • 最小反例:改旧迁移或只提供 up。
  • 机械检查/人工原因:按 migrations/*.up.sql*.down.sql basename 配对;当前根目录 89/89。隔离库使用 scripts/migrate.sh 执行 up/down/up。
  • 例外条件:不可逆数据清理须在 Change 说明恢复方式archive 历史资产不要求补配。
  • Owner:数据负责人
  • 最后验证日期2026-08-07
  • 更新触发条件Schema 或迁移工具变化

ENG-ROUTE-001

  • 状态:生效
  • 适用范围:新增或修改 Handler/路由
  • 规则:路由 MUST 经 internal/routes.Register 注册 RouteSpec新增 Handler MUST 同步 cmd/api/docs.gocmd/gendocs/main.go 的占位装配。
  • 理由:运行路由与生成文档共享元数据,但两套 composition root 仍需一致。
  • 最小正例:路由文件注册并在两处 docs Handlers 增加字段。
  • 最小反例:只在 Fiber app 上挂载 Handler。
  • 机械检查/人工原因go run cmd/gendocs/main.go;对比两处 bootstrap.Handlers 字段;再对照真实 internal/bootstrap 装配。
  • 例外条件:健康/就绪路由在总入口内定义,但仍使用 RouteSpec。
  • OwnerAPI 负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:新增 Handler 或文档生成方式变化

ENG-COMMENT-001

  • 状态:生效
  • 适用范围Go 生产代码
  • 规则:导出符号 MUST 有中文文档注释复杂逻辑说明原因Handler 注释 MUST 包含 HTTP 方法和路径。
  • 理由:保持 godoc、评审和真实路由可核对。
  • 最小正例:导出 Handler 注释写职责及 POST /api/...
  • 最小反例:注释复述赋值或路径过时。
  • 机械检查/人工原因go vet 只做基础检查;中文、原因和路径由变更 diff 人工核对真实路由。
  • 例外条件:显而易见且少于 15 行的未导出函数可无注释。
  • Owner:后端负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:新增导出 API 或路由变化

ENG-JSON-001

  • 状态:生效
  • 适用范围:新增 JSON 编解码代码
  • 规则:新增业务 JSON MUST 优先 sonic新增 encoding/json import MUST 记录兼容原因。
  • 理由Fiber 已配置 sonic但现有 SDK/OpenAPI/协议有标准库兼容需求。
  • 最小正例:业务载荷使用 sonic.Marshal
  • 最小反例:无理由新增 encoding/json
  • 机械检查/人工原因:只审查 diff 中新增 import当前仓库已有多处兼容使用不能以全仓 grep 直接失败。
  • 例外条件:标准库接口、第三方 SDK 或协议兼容明确要求。
  • Owner:后端负责人
  • 最后验证日期2026-08-07
  • 更新触发条件JSON 库或兼容协议变化

ENG-QUEUE-001

  • 状态:生效
  • 适用范围queueClient.EnqueueTask 调用
  • 规则payload MUST 是 struct 或 mapMUST NOT 传预序列化 []byte
  • 理由:封装会统一校验并 sonic.Marshal字节切片会二次编码。
  • 最小正例EnqueueTask(ctx, typ, Payload{ID:id})
  • 最小反例Marshal 后把 []byte 交给 EnqueueTask。
  • 机械检查/人工原因:核对全部 EnqueueTask 第三实参及 pkg/queue/client.go 的 ValidatePayload运行 go build ./cmd/worker
  • 例外条件:直接调用 asynq.NewTask 时由调用方序列化。
  • Owner:异步任务负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:队列封装或载荷协议变化

ENG-PAGE-001

  • 状态:生效
  • 适用范围:列表 API
  • 规则:列表 MUST 分页并执行各接口当前上限;默认 20/最大 100 仅适用于已明确采用该契约的列表。
  • 理由:避免无界查询,同时不把不同接口的既有分页强行合并。
  • 最小正例Handler/Query 对缺省值与上限显式归一化。
  • 最小反例:只写 OpenAPI tag不在运行时限制。
  • 机械检查/人工原因:从 DTO→Handler→Query 逐链核对 Limit暂为人工不以 tag 单独证明行为。
  • 例外条件:固定且有界的小型枚举集合。
  • OwnerAPI 负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:分页协议或查询性能变化

ENG-STATE-001

  • 状态:生效
  • 适用范围:新增或变更状态/类型字段
  • 规则:新增生命周期状态 SHOULD 使用 int类型/方式 SHOULD 使用 string启停新增语义使用 0=禁用、1=启用。既有不一致 MUST 按 As-Is 保留,未经 Change 不得统一。
  • 理由:避免继续扩大状态语义漂移,同时保护兼容行为。
  • 最小正例:新 Status int新 PaymentMethod string。
  • 最小反例:基线任务顺手改既有状态值。
  • 机械检查/人工原因:比对 constants、DTO、模型与迁移既有例外必须在 Spec 记录。
  • 例外条件:第三方原始字段在 Adapter 边界转换;既有兼容状态保留。
  • Owner:领域负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:新增状态机或兼容映射

ENG-AUTHZ-001

  • 状态:生效
  • 适用范围:资源读取与写入
  • 规则:资源操作 MUST 在业务边界校验店铺/企业/个人客户数据范围MUST NOT 只依赖路由角色中间件。无权限与不存在不得形成可枚举差异。
  • 理由:路由只做粗粒度身份,资源归属随数据变化。
  • 最小正例Application/Service 在读写前调用当前数据范围检查。
  • 最小反例:知道资源 ID 即可更新。
  • 机械检查/人工原因:从 Handler 追踪 Application/Service 的资源校验及 GORM Callback暂为人工抽查权限失败响应。
  • 例外条件:公开健康、回调等不以资源归属认证,但必须执行自身信任校验。
  • Owner:安全负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:新增资源入口或数据范围变化

ENG-CONC-001

  • 状态:生效
  • 适用范围:状态机、余额、库存与并发领取
  • 规则:状态流转 MUST 使用 expected-status 条件更新;余额 MUST 使用 version/锁定策略MUST NOT 读后无条件写关键事实。
  • 理由:防止重复处理、负余额和并发覆盖。
  • 最小正例WHERE status=expected 并检查 RowsAffected。
  • 最小反例:先读状态再无条件 Updates。
  • 机械检查/人工原因:核对 application/walletagentrecharge 等现有模式;变更时人工检查条件、版本和 RowsAffected。
  • 例外条件:只读投影不适用。
  • Owner:领域负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:并发写或状态机变化

ENG-OUTBOX-001

  • 状态:生效
  • 适用范围:需要提交后可靠副作用的用例
  • 规则:业务事实与可靠异步副作用 MUST 在同一事务写 OutboxMUST NOT 用裸 goroutine 代替可靠投递;消费者 MUST 容忍重复。
  • 理由:进程崩溃和至少一次投递会造成丢失或重复。
  • 最小正例:事务写业务事实与 Outbox消费者条件更新。
  • 最小反例:提交后启动 goroutine 调第三方。
  • 机械检查/人工原因:追踪 producer→Outbox→Relay→consumer检查事件唯一键、租约和终态条件。
  • 例外条件:允许丢失的低价值遥测可不用 Outbox但须明确说明。
  • Owner:可靠性负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:新增外部副作用或事件

ENG-TX-001

  • 状态:生效
  • 适用范围:资金、状态与成功审计
  • 规则:资金/关键状态事实和要求成功必达的审计 MUST 在同一 GORM 事务;事务内 MUST NOT 持有不可回滚的长外部 I/O。
  • 理由:保证事实与审计一致,并控制锁时间。
  • 最小正例:事务写事实和 Audit Writer提交后由 Outbox 外发。
  • 最小反例:事务中等待第三方网络后再提交。
  • 机械检查/人工原因:逐用例人工核对 Transaction 闭包、Audit Writer 和外部调用位置。
  • 例外条件:业务回滚后的 failed/denied 审计使用独立短事务。
  • Owner:架构与审计负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:高风险写或外部调用变化

ENG-AUDIT-001

  • 状态:机械检查阻塞
  • 适用范围:状态变更、资金、权限、关键配置、敏感读取与关键拒绝
  • 规则:用例 MUST 明确 Audit Event、Domain Ledger、Integration Log、Outbox 的使用决定或 N/A 理由;四类事实不得互相替代。
  • 理由Access Log 不能证明业务事实Integration Log 不能替代资金流水。
  • 最小正例:状态成功写 Audit外部尝试写 Integration Log。
  • 最小反例:只记录 access.log 后宣称已审计。
  • 机械检查/人工原因:当前 cmd/audit-coverage 写入已删除 .scratch 路径,不能作为通过门禁;修复前逐调用链人工核对并在仓库外记录证据。
  • 例外条件:低风险用例可登记 N/A 理由。
  • Owner:审计负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:审计 CLI 输出路径修复或新增高风险用例

ENG-LOG-001

  • 状态:生效
  • 适用范围:运行日志、审计、资金事实和外部交互
  • 规则Access Log、Audit Event、Domain Ledger、Integration Log 与 Outbox MUST 分责;日志 MUST 使用结构化 Zap 且不得记录密钥或完整敏感体。
  • 理由:不同保留期、查询维度与一致性要求不可混用。
  • 最小正例:外部失败写脱敏 Integration LogHTTP 请求写 Access Log。
  • 最小反例:把完整凭证写 app.log。
  • 机械检查/人工原因:人工核对 Logger 字段、Sanitizer 与事实存储;本地按 request_id/correlation_id 查询日志。
  • 例外条件:第三方要求回显的非敏感渠道码可保留。
  • Owner:审计与安全负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:日志字段、保留策略或外部集成变化

ENG-SECRET-001

  • 状态:生效
  • 适用范围:配置、代码、文档、日志和示例
  • 规则密钥、Token、证书和真实凭证 MUST 由生效配置/环境注入MUST NOT 新增到仓库或日志;示例只用占位符。
  • 理由:仓库历史和日志传播范围不可控。
  • 最小正例:文档使用 <TOKEN>,运行环境注入 Secret。
  • 最小反例:提交可用 AppSecret。
  • 机械检查/人工原因:对 git diff 做敏感模式人工扫描;核对 pkg/config 绑定和日志字段。当前历史凭证属于待决策存量,不得复制。
  • 例外条件:明确不可用的假值可用于文档占位;当前无测试夹具。
  • Owner:安全负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:配置字段、凭证或集成变化

ENG-CONFIG-001

  • 状态:生效
  • 适用范围:运行配置和依赖版本
  • 规则:依赖版本 MUST 以 go.mod 为准;运行参数 MUST 经 pkg/config 加载/校验;业务代码 MUST NOT 散落读取环境变量。
  • 理由:保证默认值、环境覆盖和启动校验只有一个入口。
  • 最小正例:在 Config 增字段并由 loader 绑定。
  • 最小反例:业务 Service 直接 os.Getenv
  • 机械检查/人工原因:检查变更新增 os.Getenv;运行 go build 并核对 pkg/config/config.go 校验。
  • 例外条件:启动脚本读取环境变量用于组装进程配置。
  • Owner:基础设施负责人
  • 最后验证日期2026-08-07
  • 更新触发条件:新增配置或依赖升级