# 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) ```go // ❌ 当前 if allocation == nil { return fmt.Errorf("分配记录不存在") } // ✅ 修复后 if allocation == nil { return errors.New(errors.CodeNotFound, "分配记录不存在") } ``` #### 系统依赖错误(5xx) ```go // ❌ 当前 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`: ```go // 额度相关 CodeInsufficientQuota = 40010 // 额度不足 CodeExceedLimit = 40011 // 超过限制 // 冲突相关 CodeConflict = 40900 // 资源冲突 ``` ### 执行策略 1. **按模块分批**:建议每完成 5 个文件提交一次 2. **优先级**:权限管理 > 套餐分配 > 卡设备 > 其他 3. **测试覆盖**:每个模块补充错误场景单元测试 4. **向后兼容**:保持错误消息中文描述 ## Impact ### Breaking Changes - 部分接口错误码从 500 调整为 4xx - 客户端需要处理新的错误码(如 `CodeInsufficientQuota`、`CodeExceedLimit`) ### Testing Requirements 每个模块补充错误场景测试: ```go 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 ### 编译检查 ```bash go build -o /tmp/test_api ./cmd/api go build -o /tmp/test_worker ./cmd/worker ``` ### 单元测试(分模块) ```bash # 套餐分配系统 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 小时