All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m45s
主要变更: 1. OpenAPI 文档契约对齐 - 统一错误响应字段名为 msg(非 message) - 规范 envelope 响应结构(code, msg, data, timestamp) - 个人客户路由纳入文档体系(使用 Register 机制) - 新增 BuildDocHandlers() 统一管理 handler 构造 - 确保文档生成的幂等性 2. Service 层错误处理统一 - 全面替换 fmt.Errorf 为 errors.New/Wrap - 统一错误码使用规范 - Handler 层参数校验不泄露底层细节 - 新增错误码验证集成测试 3. 代码质量提升 - 删除未使用的 Task handler 和路由 - 新增代码规范检查脚本(check-service-errors.sh) - 新增注释路径一致性检查(check-comment-paths.sh) - 更新 API 文档生成指南 4. OpenSpec 归档 - 归档 openapi-contract-alignment 变更(63 tasks) - 归档 service-error-unify-core 变更 - 归档 service-error-unify-support 变更 - 归档 code-cleanup-docs-update 变更 - 归档 handler-validation-security 变更 - 同步 delta specs 到主规范文件 影响范围: - pkg/openapi: 新增 handlers.go,优化 generator.go - internal/service/*: 48 个 service 文件错误处理统一 - internal/handler/admin: 优化参数校验错误提示 - internal/routes: 个人客户路由改造,删除 task 路由 - scripts: 新增 3 个代码检查脚本 - docs: 更新 OpenAPI 文档(15750+ 行) - openspec/specs: 同步 3 个主规范文件 破坏性变更:无 向后兼容:是
6.3 KiB
6.3 KiB
Change: Service 层错误语义统一 - 支持模块
Why
完成剩余支持模块的错误语义统一,实现全局一致性。支持模块包括套餐分配、权限管理、卡与设备管理、邮件同步等功能。
前置依赖:
- 提案 1(核心业务模块)已完成
- 错误处理规范已明确
影响范围:
- 套餐分配系统(4 个文件,50 处)
- 权限与账号管理(3 个文件,49 处)
- 卡与设备管理(2 个文件,29 处)
- 其他支持服务(5 个文件,26 处)
总计:14 个文件,约 154 处 fmt.Errorf 待替换
What Changes
修改清单
套餐分配系统
-
shop_package_allocation/service.go(17 处)- 分配记录不存在 →
CodeNotFound - 分配额度不足 →
CodeInsufficientQuota - 数据库错误 →
Wrap(CodeInternalError, err)
- 分配记录不存在 →
-
shop_series_allocation/service.go(24 处)- 系列分配记录不存在 →
CodeNotFound - 分配冲突 →
CodeConflict - 数据库错误 →
Wrap(CodeInternalError, err)
- 系列分配记录不存在 →
-
shop_package_batch_allocation/service.go(6 处)- 批量分配失败 →
Wrap(CodeInternalError, err)
- 批量分配失败 →
-
shop_package_batch_pricing/service.go(3 处)- 批量定价失败 →
Wrap(CodeInternalError, err)
- 批量定价失败 →
权限与账号管理
-
account/service.go(24 处)- 账号不存在 →
CodeNotFound - 用户名重复 →
CodeDuplicate - 密码错误 →
CodeInvalidPassword - 状态不允许 →
CodeInvalidStatus - 数据库错误 →
Wrap(CodeInternalError, err)
- 账号不存在 →
-
role/service.go(15 处)- 角色不存在 →
CodeNotFound - 角色已存在 →
CodeDuplicate - 角色被使用无法删除 →
CodeForbidden - 数据库错误 →
Wrap(CodeInternalError, err)
- 角色不存在 →
-
permission/service.go(10 处)- 权限不存在 →
CodeNotFound - 权限冲突 →
CodeConflict - 数据库错误 →
Wrap(CodeInternalError, err)
- 权限不存在 →
卡与设备管理
-
enterprise_card/service.go(9 处)- 卡不存在 →
CodeNotFound - 卡状态不允许 →
CodeInvalidStatus - 数据库错误 →
Wrap(CodeInternalError, err)
- 卡不存在 →
-
enterprise_device/service.go(20 处)- 设备不存在 →
CodeNotFound - 设备状态不允许 →
CodeInvalidStatus - 设备绑定卡数量超限 →
CodeExceedLimit - 数据库错误 →
Wrap(CodeInternalError, err)
- 设备不存在 →
其他支持服务
-
carrier/service.go(9 处)- 运营商不存在 →
CodeNotFound - 数据库错误 →
Wrap(CodeInternalError, err)
- 运营商不存在 →
-
shop_commission/service.go(7 处)- 分佣设置不存在 →
CodeNotFound - 数据库错误 →
Wrap(CodeInternalError, err)
- 分佣设置不存在 →
-
commission_withdrawal_setting/service.go(4 处)- 提现设置不存在 →
CodeNotFound - 数据库错误 →
Wrap(CodeInternalError, err)
- 提现设置不存在 →
-
email/service.go(6 处)- 邮件服务未配置 →
CodeServiceUnavailable - 邮件发送失败 →
Wrap(CodeInternalError, err)
- 邮件服务未配置 →
-
sync/service.go(4 处)- 同步任务失败 →
Wrap(CodeInternalError, err)
- 同步任务失败 →
错误处理统一规则(同提案 1)
业务校验错误(4xx)
// ❌ 当前
if allocation == nil {
return fmt.Errorf("分配记录不存在")
}
// ✅ 修复后
if allocation == nil {
return errors.New(errors.CodeNotFound, "分配记录不存在")
}
系统依赖错误(5xx)
// ❌ 当前
if err := s.store.Account.Create(ctx, account); err != nil {
return fmt.Errorf("创建账号失败: %w", err)
}
// ✅ 修复后
if err := s.store.Account.Create(ctx, account); err != nil {
return errors.Wrap(errors.CodeInternalError, err, "创建账号失败")
}
Decisions
新增错误码
如果需要新增错误码,添加到 pkg/errors/codes.go:
// 额度相关
CodeInsufficientQuota = 40010 // 额度不足
CodeExceedLimit = 40011 // 超过限制
// 冲突相关
CodeConflict = 40900 // 资源冲突
执行策略
- 按模块分批:建议每完成 5 个文件提交一次
- 优先级:权限管理 > 套餐分配 > 卡设备 > 其他
- 测试覆盖:每个模块补充错误场景单元测试
- 向后兼容:保持错误消息中文描述
Impact
Breaking Changes
- 部分接口错误码从 500 调整为 4xx
- 客户端需要处理新的错误码(如
CodeInsufficientQuota、CodeExceedLimit)
Testing Requirements
每个模块补充错误场景测试:
func TestService_ErrorHandling(t *testing.T) {
t.Run("分配记录不存在返回 404", func(t *testing.T) {
err := service.GetAllocation(ctx, 99999)
assert.Error(t, err)
assert.Equal(t, errors.CodeNotFound, errors.GetCode(err))
})
t.Run("额度不足返回 400", func(t *testing.T) {
err := service.AllocatePackage(ctx, hugeAmount)
assert.Error(t, err)
assert.Equal(t, errors.CodeInsufficientQuota, errors.GetCode(err))
})
}
Affected Specs
- UPDATE:
openspec/specs/error-handling/spec.md- 补充新增错误码定义
- 添加支持模块错误处理示例
Verification Checklist
编译检查
go build -o /tmp/test_api ./cmd/api
go build -o /tmp/test_worker ./cmd/worker
单元测试(分模块)
# 套餐分配系统
source .env.local && go test -v ./internal/service/shop_package_allocation/...
source .env.local && go test -v ./internal/service/shop_series_allocation/...
# 权限与账号
source .env.local && go test -v ./internal/service/account/...
source .env.local && go test -v ./internal/service/role/...
source .env.local && go test -v ./internal/service/permission/...
# 卡与设备
source .env.local && go test -v ./internal/service/enterprise_card/...
source .env.local && go test -v ./internal/service/enterprise_device/...
错误码验证
手动测试关键接口:
- ✅ 分配记录不存在返回 404
- ✅ 额度不足返回 400
- ✅ 角色被使用无法删除返回 403
- ✅ 设备绑定卡数超限返回 400
- ✅ 数据库错误返回 500
Estimated Effort
| 模块 | 文件数 | 错误点数 | 预估时间 |
|---|---|---|---|
| 套餐分配系统 | 4 | 50 | 1.5h |
| 权限与账号 | 3 | 49 | 1.5h |
| 卡与设备 | 2 | 29 | 1h |
| 其他支持服务 | 5 | 26 | 1h |
| 测试验证 | - | - | 1h |
总计:约 6 小时