# Supply API - Claude Code 项目规范 ## 项目概述 Supply API 是一个基于 Go 的微服务,提供供应链管理功能,包括账户管理、套餐管理、结算服务、收益服务和审计日志。 ## 技术栈 - **语言**: Go 1.21+ - **数据库**: PostgreSQL 15+ - **缓存**: Redis - **框架**: 标准库 + 自定义中间件 - **测试**: Go testing + testify --- ## 代码规范 ### 1. 命名规范 #### 1.1 字段命名统一 **关键经验**: 跨模块字段必须保持命名一致,否则会导致类型转换错误。 | 规范 | 示例 | 说明 | |------|------|------| | IP来源字段 | `SourceIP` | 统一使用 `SourceIP`,禁止使用 `ClientIP` | | 追踪ID字段 | `TraceID` | W3C Trace Context 标准 | | 请求ID字段 | `RequestID` | HTTP 请求追踪 | | 幂等键字段 | `IdempotencyKey` | 统一命名 | #### 1.2 结构体命名 ``` // ✅ 正确 type AuditEvent struct { SourceIP string `json:"source_ip"` } // ❌ 错误 - 与其他模块不一致 type AuditEvent struct { ClientIP string `json:"client_ip"` } ``` ### 2. 接口设计 #### 2.1 Store 接口必须包含版本控制 **关键经验**: 乐观锁是防止并发更新导致数据不一致的标准做法。 ```go // ✅ 正确 - 包含 expectedVersion 参数 type SettlementStore interface { Update(ctx context.Context, s *Settlement, expectedVersion int) error } // ❌ 错误 - 缺少版本控制 type SettlementStore interface { Update(ctx context.Context, s *Settlement) error } ``` #### 2.2 领域服务接口与实现分离 ```go // 领域层定义接口 type SettlementStore interface { Create(ctx context.Context, s *Settlement) error GetByID(ctx context.Context, supplierID, id int64) (*Settlement, error) Update(ctx context.Context, s *Settlement, expectedVersion int) error List(ctx context.Context, supplierID int64) ([]*Settlement, error) GetWithdrawableBalance(ctx context.Context, supplierID int64) (float64, error) } ``` ### 3. 中间件设计 #### 3.1 Logging 中间件必须使用结构化日志 **关键经验**: 标准库 `log` 无法满足生产环境可观测性需求。 ```go // ✅ 正确 - 使用结构化日志接口 func Logging(next http.Handler, logger logging.Logger) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fields := map[string]interface{}{ "method": r.Method, "path": r.URL.Path, } logger.Info("HTTP request", fields) next.ServeHTTP(w, r) }) } ``` #### 3.2 Tracing 中间件解析 W3C Trace Context ```go // W3C Trace Context 标准 traceparent header 格式 // traceparent: 00-{trace-id}-{span-id}-{trace-flags} func ParseTraceParent(traceParent string) (*TraceContext, error) { // 格式: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01 // 长度: 55 字符 } ``` ### 4. 健康检查设计 #### 4.1 统一使用 HealthHandler **关键经验**: 避免重复实现导致的维护负担和不一致。 ```go // ✅ 正确 - 使用统一的 HealthHandler healthHandler := httpapi.NewHealthHandlerWithDefaults(dbHealthCheck, redisHealthCheck) mux.HandleFunc("/actuator/health", healthHandler.ServeHealth) // ❌ 错误 - inline handler 导致代码重复 mux.HandleFunc("/actuator/health", handleHealthCheck(db, redisCache)) ``` #### 4.2 健康检查端点路径 | 路径 | 说明 | |------|------| | `/actuator/health` | 综合健康检查 | | `/actuator/health/live` | 存活探针 | | `/actuator/health/ready` | 就绪探针 | ### 5. 审计日志设计 #### 5.1 事件字段规范 ```go type Event struct { EventID string `json:"event_id,omitempty"` TenantID int64 `json:"tenant_id"` ObjectType string `json:"object_type"` // e.g., "supply_settlement" ObjectID int64 `json:"object_id"` Action string `json:"action"` // e.g., "withdraw", "cancel" BeforeState map[string]any `json:"before_state,omitempty"` AfterState map[string]any `json:"after_state,omitempty"` RequestID string `json:"request_id,omitempty"` ResultCode string `json:"result_code"` // e.g., "OK", "SUP_SET_4001" SourceIP string `json:"source_ip,omitempty"` CreatedAt time.Time `json:"created_at"` } ``` #### 5.2 敏感信息脱敏 审计日志必须脱敏处理: - 手机号、邮箱 - 身份证号 - 银行账号 - 密码和密钥 ### 6. 错误处理 #### 6.1 错误码格式 ``` {SOURCE}_{CATEGORY}_{CODE} 例如: SUP_SET_4001 - SUP: 来源系统 - SET: 业务类别 (settlement) - 4001: 具体错误码 ``` #### 6.2 错误信息不泄露内部细节 ```go // ✅ 正确 - 用户友好错误信息 return errors.New("SUP_SET_4001: withdraw amount exceeds available balance") // ❌ 错误 - 泄露内部实现 return errors.New("database connection failed: connection refused") ``` ### 7. 数据库设计 #### 7.1 乐观锁实现 ```sql -- PostgreSQL 乐观锁 UPDATE settlements SET status = $1, version = version + 1, updated_at = NOW() WHERE id = $2 AND version = $3 RETURNING id; -- 如果返回 0 行,说明版本冲突 ``` #### 7.2 悲观锁实现 ```sql -- 扣减配额时使用悲观锁 UPDATE supply_packages SET available_quota = available_quota - $1, sold_quota = sold_quota + $1 WHERE id = $2 AND user_id = $3 AND available_quota >= $1 RETURNING id; ``` --- ## 测试规范 ### 1. 测试文件命名 ``` {package}_test.go // 标准测试(默认) {package}_integration_test.go // 集成测试(需数据库) {package}_e2e_test.go // 端到端测试 ``` ### 2. Mock 接口而非具体实现 ```go // ✅ 正确 - Mock 接口 type mockSettlementStore struct { settlements map[int64]*Settlement } func (m *mockSettlementStore) GetByID(ctx context.Context, supplierID, id int64) (*Settlement, error) { if s, ok := m.settlements[id]; ok && s.SupplierID == supplierID { return s, nil } return nil, errors.New("not found") } // ❌ 错误 - Mock 具体类型 type mockRepo struct { repo *repository.SettlementRepository } ``` ### 3. Mock 审计存储签名(关键) 审计存储接口方法签名为: ```go type AuditStore interface { Emit(ctx context.Context, event audit.Event) error // 注意是 audit.Event 不是 interface{} Query(ctx context.Context, filter audit.EventFilter) ([]audit.Event, error) QueryWithTotal(ctx context.Context, filter audit.EventFilter) ([]audit.Event, int64, error) GetByID(ctx context.Context, eventID string) (audit.Event, error) } ``` ### 4. 测试覆盖率要求 | 模块 | 最低覆盖率 | 说明 | |------|-----------|------| | domain | 70% | 领域模型、状态机、业务规则 | | audit/service | 80% | 审计服务、告警服务 | | audit/handler | 75% | HTTP 处理器 | | audit/model | 80% | 数据模型、验证 | | audit/sanitizer | 80% | 敏感信息脱敏 | | middleware | 80% | 认证、限流、幂等 | | security | 80% | 安全相关 | | iam | 70% | 身份认证授权 | ### 5. 测试运行命令 ```bash # 快速测试(跳过集成测试) go test -short ./... # 包含集成测试 go test -tags=integration ./... # 详细输出 go test -v -cover ./internal/domain/... # 检查覆盖率 go test -cover ./... ``` ### 6. 测试命名规范 ``` Test{Service}_{Method}_{Scenario} 示例: - TestAccountService_Create_Success - TestAccountService_Create_InvalidInput - TestPackageService_Publish_ExpiredPackage - TestSettlementService_Withdraw_ExceedsBalance ``` --- ## Git 提交规范 ### 1. 提交信息格式 ``` {type}: {subject} {body} {footer} ``` ### 2. Type 类型 - `fix`: 缺陷修复 - `feat`: 新功能 - `docs`: 文档更新 - `refactor`: 重构 - `test`: 测试相关 - `chore`: 构建/工具变更 ### 3. 示例 ``` fix: 修复结算更新时的乐观锁冲突 问题:并发更新结算状态时可能导致数据覆盖 解决:添加 expectedVersion 参数实现乐观锁 Fixes: SUP-1234 ``` --- ## 依赖管理 ### 1. 使用 Go Modules ```bash go mod init lijiaoqiao/supply-api go mod tidy ``` ### 2. 禁止依赖未验证的包 评估标准: - 维护状态 - 下载量 - 安全漏洞历史 --- ## 配置管理 ### 1. 环境配置分离 ``` config/ ├── config.dev.yaml ├── config.staging.yaml └── config.prod.yaml ``` ### 2. 敏感配置通过环境变量 ```yaml database: password: ${DB_PASSWORD} ``` --- ## 常见问题与解决方案 ### Q1: 如何处理跨模块命名不一致? **A**: 建立字段命名标准文档,所有模块遵循 W3C 和行业通用命名。 ### Q2: 何时使用乐观锁 vs 悲观锁? **A**: - 乐观锁:读多写少,低冲突场景 - 悲观锁:高并发写,财务类敏感操作 ### Q3: 如何避免中间件代码重复? **A**: 使用统一的 Handler 模式,集中管理公共逻辑。 ### Q4: 审计日志的性能影响如何控制? **A**: 采样策略 + 异步写入 + 批量处理。 --- ## 测试验证规范(2026-04-09) ### 1. 数据库连接配置 ```bash # Unix socket 连接(推荐开发环境) export SUPPLY_API_DB_HOST="/var/run/postgresql" export SUPPLY_API_DB_USER="long" export SUPPLY_API_DB_PASSWORD="" export SUPPLY_API_DB_NAME="supply_test" # TCP 连接 export SUPPLY_API_DB_HOST="localhost" export SUPPLY_API_DB_PORT="5432" ``` ### 2. 测试运行命令 ```bash # 单元测试(跳过集成测试) go test -short ./... # 集成测试(需真实数据库) go test -tags=integration ./... # 性能基准测试 go test -tags=slow -bench=. -benchmem ./internal/benchmark/... # 完整测试(含集成) go test -tags=integration,benchmark ./... # E2E 测试 go test -tags=e2e ./e2e/... # 覆盖率报告 go test -cover ./... ``` ### 3. 服务启动 ```bash # 使用配置文件 /tmp/supply-api -env=dev # 服务运行端口 # http://localhost:18082 ``` ### 4. 健康检查端点 | 端点 | 方法 | 说明 | |------|------|------| | `/actuator/health` | GET | 综合健康检查 | | `/actuator/health/live` | GET | 存活探针 | | `/actuator/health/ready` | GET | 就绪探针 | ### 5. 性能基准(参考值) | 操作 | 性能 | |------|------| | AccountService_Create | ~680 ns/op | | AccountService_Verify | ~3.6 ns/op | | PackageService_CreateDraft | ~510 ns/op | | SettlementService_Withdraw | ~630 ns/op | | LoggingMiddleware | ~1.8 μs/op | | TracingMiddleware | ~1.9 μs/op | ### 6. 测试覆盖率目标 | 模块 | 当前覆盖率 | 目标覆盖率 | |------|-----------|-----------| | audit/events | 97.6% | 95%+ | | audit/model | 93.8% | 90%+ | | audit/service | 83.0% | 80%+ | | audit/sanitizer | 84.3% | 80%+ | | security | 88.8% | 80%+ | | domain | 61.2% | 70%+ | | middleware | 53.9% | 70%+ | --- ## 文件结构 ``` supply-api/ ├── cmd/supply-api/ # 主程序入口 ├── internal/ │ ├── audit/ # 审计日志模块 │ │ ├── model/ # 审计事件模型 │ │ ├── service/ # 审计服务 │ │ ├── handler/ # HTTP 处理器 │ │ ├── repository/ # 数据库仓储 │ │ ├── sanitizer/ # 敏感信息脱敏 │ │ └── events/ # 事件定义 │ ├── iam/ # IAM 模块 │ ├── domain/ # 领域模型 │ ├── middleware/ # HTTP 中间件 │ ├── repository/ # 通用数据仓储 │ ├── cache/ # Redis 缓存 │ ├── config/ # 配置管理 │ └── pkg/ # 公共包 ├── sql/postgresql/ # 数据库 DDL 脚本 ├── e2e/ # E2E 测试 ├── docs/ # 设计文档 └── deploy/ # 部署配置 ``` --- ## 常见问题与解决方案 ### Q1: 如何处理跨模块命名不一致? **A**: 建立字段命名标准文档,所有模块遵循 W3C 和行业通用命名。 ### Q2: 何时使用乐观锁 vs 悲观锁? **A**: - 乐观锁:读多写少,低冲突场景 - 悲观锁:高并发写,财务类敏感操作 ### Q3: 如何避免中间件代码重复? **A**: 使用统一的 Handler 模式,集中管理公共逻辑。 ### Q4: 审计日志的性能影响如何控制? **A**: 采样策略 + 异步写入 + 批量处理。 ### Q5: E2E 测试编译失败如何处理? **A**: 检查未使用的导入和变量,确保 `ctx` 变量被正确使用或声明为 `_`。 ### Q6: 基准测试无法运行? **A**: 基准测试需要 `-tags=slow` 标记,且 `testing.Short()` 返回 false 时才运行。