All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m35s
19 KiB
19 KiB
工程约束
本文件只记录从当前代码、配置、迁移、构建或可复现运行结果证明的长期工程规则。业务行为属于 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。 - Owner:API 负责人
- 最后验证日期: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返回另一套结构。 - 机械检查/人工原因:审查新增 Handler;
rg "response\.(Success|Error)" internal/handler internal/routes;生成 OpenAPI 人工核对。 - 例外条件:第三方回调必须返回渠道要求的字面协议时可使用专用响应。
- Owner:API 负责人
- 最后验证日期: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 不足以证明完整。 - 例外条件:仅内部、不进入接口契约的结构。
- Owner:API 负责人
- 最后验证日期: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 使用 GORM,MUST NOT 新增
database/sql直接访问。 - 理由:统一连接、事务、Callback 数据范围和错误处理。
- 最小正例:通过
*gorm.DB或 Store 查询。 - 最小反例:业务 Service 新建
sql.DB。 - 机械检查/人工原因:检查变更新增 import:
git 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_main;MUST 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.sqlbasename 配对;当前根目录 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.go与cmd/gendocs/main.go的占位装配。 - 理由:运行路由与生成文档共享元数据,但两套 composition root 仍需一致。
- 最小正例:路由文件注册并在两处 docs Handlers 增加字段。
- 最小反例:只在 Fiber app 上挂载 Handler。
- 机械检查/人工原因:
go run cmd/gendocs/main.go;对比两处bootstrap.Handlers字段;再对照真实internal/bootstrap装配。 - 例外条件:健康/就绪路由在总入口内定义,但仍使用 RouteSpec。
- Owner:API 负责人
- 最后验证日期: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/jsonimport 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 或 map,MUST 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 单独证明行为。
- 例外条件:固定且有界的小型枚举集合。
- Owner:API 负责人
- 最后验证日期: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/wallet、agentrecharge等现有模式;变更时人工检查条件、版本和 RowsAffected。 - 例外条件:只读投影不适用。
- Owner:领域负责人
- 最后验证日期:2026-08-07
- 更新触发条件:并发写或状态机变化
ENG-OUTBOX-001
- 状态:生效
- 适用范围:需要提交后可靠副作用的用例
- 规则:业务事实与可靠异步副作用 MUST 在同一事务写 Outbox;MUST 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 Log,HTTP 请求写 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
- 更新触发条件:新增配置或依赖升级