Files
junhong_cmp_fiber/pkg/errors/codes.go
huang 409a68d60b
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m45s
feat: OpenAPI 契约对齐与框架优化
主要变更:
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 个主规范文件

破坏性变更:无
向后兼容:是
2026-01-30 11:40:36 +08:00

343 lines
14 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package errors
import "fmt"
// 错误码定义
const (
// 成功
CodeSuccess = 0
// 客户端错误 (1000-1999) -> 4xx HTTP 状态码
CodeInvalidParam = 1001 // 参数验证失败
CodeMissingToken = 1002 // 缺失认证令牌
CodeInvalidToken = 1003 // 无效或过期的令牌
CodeUnauthorized = 1004 // 未授权
CodeForbidden = 1005 // 禁止访问
CodeNotFound = 1006 // 资源未找到
CodeConflict = 1007 // 资源冲突
CodeTooManyRequests = 1008 // 请求过多
CodeRequestTooLarge = 1009 // 请求体过大
// RBAC 相关错误 (1010-1099)
CodeAccountNotFound = 1010 // 账号不存在
CodeAccountDisabled = 1011 // 账号已禁用
CodeAccountDeleted = 1012 // 账号已删除
CodeUsernameExists = 1013 // 用户名已存在
CodePhoneExists = 1014 // 手机号已存在
CodeInvalidPassword = 1015 // 密码格式不正确
CodePasswordTooWeak = 1016 // 密码强度不足
CodeParentIDRequired = 1017 // 非 root 用户必须提供上级账号
CodeInvalidParentID = 1018 // 上级账号不存在或无效
CodeCannotModifyParent = 1019 // 禁止修改上级账号
CodeCannotModifyUserType = 1020 // 禁止修改用户类型
CodeRoleNotFound = 1021 // 角色不存在
CodeRoleNameExists = 1022 // 角色名称已存在
CodePermissionNotFound = 1023 // 权限不存在
CodePermCodeExists = 1024 // 权限编码已存在
CodeInvalidPermCode = 1025 // 权限编码格式不正确
CodeRoleAlreadyAssigned = 1026 // 角色已分配
CodePermAlreadyAssigned = 1027 // 权限已分配
// 认证相关错误 (1040-1049)
CodeInvalidCredentials = 1040 // 用户名或密码错误
CodeAccountLocked = 1041 // 账号已锁定
CodePasswordExpired = 1042 // 密码已过期
CodeInvalidOldPassword = 1043 // 旧密码错误
// 组织相关错误 (1030-1049)
CodeShopNotFound = 1030 // 店铺不存在
CodeShopCodeExists = 1031 // 店铺编号已存在
CodeShopLevelExceeded = 1032 // 店铺层级超过最大值
CodeEnterpriseNotFound = 1033 // 企业不存在
CodeEnterpriseCodeExists = 1034 // 企业编号已存在
CodeCustomerNotFound = 1035 // 个人客户不存在
CodeCustomerPhoneExists = 1036 // 个人客户手机号已存在
// 财务相关错误 (1050-1069)
CodeInvalidStatus = 1050 // 状态不允许此操作
CodeInsufficientBalance = 1051 // 余额不足
CodeWithdrawalNotFound = 1052 // 提现申请不存在
CodeWalletNotFound = 1053 // 钱包不存在
CodeInsufficientQuota = 1054 // 额度不足
CodeExceedLimit = 1055 // 超过限制
// IoT 卡相关错误 (1070-1089)
CodeIotCardNotFound = 1070 // IoT 卡不存在
CodeIotCardBoundToDevice = 1071 // IoT 卡已绑定设备
CodeIotCardStatusNotAllowed = 1072 // 卡状态不允许此操作
CodeAssetAllocationRecordNotFound = 1073 // 分配记录不存在
CodeNotDirectSubordinate = 1074 // 非直属下级店铺
CodeCannotAllocateToSelf = 1075 // 不能分配给自己
CodeCannotRecallFromSelf = 1076 // 不能从自己回收
CodeCardAlreadyAuthorized = 1077 // 卡已授权给该企业
CodeCardNotAuthorized = 1078 // 卡未授权给该企业
CodeCannotAuthorizeOthersCard = 1079 // 不能授权非自己的卡
CodeCannotRevokeOthersAuthorization = 1080 // 不能回收非自己创建的授权
CodeCannotAuthorizeBoundCard = 1081 // 不能授权已绑定设备的卡
CodeCannotAuthorizeToOthersEnterprise = 1082 // 不能授权给非自己的企业
CodeDeviceAlreadyAuthorized = 1083 // 设备已授权给该企业
CodeDeviceNotAuthorized = 1084 // 设备未授权给该企业
CodeDeviceAuthorizedToOther = 1085 // 设备已授权给其他企业
CodeCannotAuthorizeOthersDevice = 1086 // 不能授权非自己的设备
// 对象存储相关错误 (1090-1099)
CodeStorageNotConfigured = 1090 // 对象存储服务未配置
CodeStorageUploadFailed = 1091 // 文件上传失败
CodeStorageDownloadFailed = 1092 // 文件下载失败
CodeStorageFileNotFound = 1093 // 文件不存在
CodeStorageInvalidPurpose = 1094 // 不支持的文件用途
CodeStorageInvalidFileType = 1095 // 不支持的文件类型
// 运营商相关错误 (1100-1109)
CodeCarrierNotFound = 1100 // 运营商不存在
CodeCarrierCodeExists = 1101 // 运营商编码已存在
// 服务端错误 (2000-2999) -> 5xx HTTP 状态码
CodeInternalError = 2001 // 内部服务器错误
CodeDatabaseError = 2002 // 数据库错误
CodeRedisError = 2003 // Redis 错误
CodeServiceUnavailable = 2004 // 服务不可用
CodeTimeout = 2005 // 请求超时
CodeTaskQueueError = 2006 // 任务队列错误
)
// allErrorCodes 所有已注册的错误码
// 新增错误码时必须同时在此列表中注册
var allErrorCodes = []int{
CodeSuccess,
CodeInvalidParam,
CodeMissingToken,
CodeInvalidToken,
CodeUnauthorized,
CodeForbidden,
CodeNotFound,
CodeConflict,
CodeTooManyRequests,
CodeRequestTooLarge,
CodeAccountNotFound,
CodeAccountDisabled,
CodeAccountDeleted,
CodeUsernameExists,
CodePhoneExists,
CodeInvalidPassword,
CodePasswordTooWeak,
CodeParentIDRequired,
CodeInvalidParentID,
CodeCannotModifyParent,
CodeCannotModifyUserType,
CodeRoleNotFound,
CodeRoleNameExists,
CodePermissionNotFound,
CodePermCodeExists,
CodeInvalidPermCode,
CodeRoleAlreadyAssigned,
CodePermAlreadyAssigned,
CodeShopNotFound,
CodeShopCodeExists,
CodeShopLevelExceeded,
CodeEnterpriseNotFound,
CodeEnterpriseCodeExists,
CodeCustomerNotFound,
CodeCustomerPhoneExists,
CodeInvalidCredentials,
CodeAccountLocked,
CodePasswordExpired,
CodeInvalidOldPassword,
CodeInvalidStatus,
CodeInsufficientBalance,
CodeWithdrawalNotFound,
CodeWalletNotFound,
CodeInsufficientQuota,
CodeExceedLimit,
CodeIotCardNotFound,
CodeIotCardBoundToDevice,
CodeIotCardStatusNotAllowed,
CodeAssetAllocationRecordNotFound,
CodeNotDirectSubordinate,
CodeCannotAllocateToSelf,
CodeCannotRecallFromSelf,
CodeCardAlreadyAuthorized,
CodeCardNotAuthorized,
CodeCannotAuthorizeOthersCard,
CodeCannotRevokeOthersAuthorization,
CodeCannotAuthorizeBoundCard,
CodeCannotAuthorizeToOthersEnterprise,
CodeDeviceAlreadyAuthorized,
CodeDeviceNotAuthorized,
CodeDeviceAuthorizedToOther,
CodeCannotAuthorizeOthersDevice,
CodeStorageNotConfigured,
CodeStorageUploadFailed,
CodeStorageDownloadFailed,
CodeStorageFileNotFound,
CodeStorageInvalidPurpose,
CodeStorageInvalidFileType,
CodeCarrierNotFound,
CodeCarrierCodeExists,
CodeInternalError,
CodeDatabaseError,
CodeRedisError,
CodeServiceUnavailable,
CodeTimeout,
CodeTaskQueueError,
}
func init() {
for _, code := range allErrorCodes {
if _, ok := errorMessages[code]; !ok {
panic(fmt.Sprintf("错误码 %d 缺少映射消息,请在 errorMessages 中添加", code))
}
}
}
// errorMessages 错误消息映射表(中文)
var errorMessages = map[int]string{
CodeSuccess: "成功",
CodeInvalidParam: "参数验证失败",
CodeMissingToken: "缺失认证令牌",
CodeInvalidToken: "无效或过期的令牌",
CodeUnauthorized: "未授权访问",
CodeForbidden: "禁止访问",
CodeNotFound: "资源未找到",
CodeConflict: "资源冲突",
CodeTooManyRequests: "请求过多,请稍后重试",
CodeRequestTooLarge: "请求体过大",
CodeAccountNotFound: "账号不存在",
CodeAccountDisabled: "账号已禁用",
CodeAccountDeleted: "账号已删除",
CodeUsernameExists: "用户名已存在",
CodePhoneExists: "手机号已存在",
CodeInvalidPassword: "密码格式不正确",
CodePasswordTooWeak: "密码强度不足",
CodeParentIDRequired: "非 root 用户必须提供上级账号",
CodeInvalidParentID: "上级账号不存在或无效",
CodeCannotModifyParent: "禁止修改上级账号",
CodeCannotModifyUserType: "禁止修改用户类型",
CodeRoleNotFound: "角色不存在",
CodeRoleNameExists: "角色名称已存在",
CodePermissionNotFound: "权限不存在",
CodePermCodeExists: "权限编码已存在",
CodeInvalidPermCode: "权限编码格式不正确(应为 module:action 格式)",
CodeRoleAlreadyAssigned: "角色已分配",
CodePermAlreadyAssigned: "权限已分配",
CodeShopNotFound: "店铺不存在",
CodeShopCodeExists: "店铺编号已存在",
CodeShopLevelExceeded: "店铺层级不能超过 7 级",
CodeEnterpriseNotFound: "企业不存在",
CodeEnterpriseCodeExists: "企业编号已存在",
CodeCustomerNotFound: "个人客户不存在",
CodeCustomerPhoneExists: "个人客户手机号已存在",
CodeInvalidStatus: "状态不允许此操作",
CodeInsufficientBalance: "余额不足",
CodeWithdrawalNotFound: "提现申请不存在",
CodeWalletNotFound: "钱包不存在",
CodeInsufficientQuota: "额度不足",
CodeExceedLimit: "超过限制",
CodeIotCardNotFound: "IoT 卡不存在",
CodeIotCardBoundToDevice: "IoT 卡已绑定设备,不能单独操作",
CodeIotCardStatusNotAllowed: "卡状态不允许此操作",
CodeAssetAllocationRecordNotFound: "分配记录不存在",
CodeNotDirectSubordinate: "只能操作直属下级店铺",
CodeCannotAllocateToSelf: "不能分配给自己",
CodeCannotRecallFromSelf: "不能从自己回收",
CodeCardAlreadyAuthorized: "卡已授权给该企业",
CodeCardNotAuthorized: "卡未授权给该企业",
CodeCannotAuthorizeOthersCard: "不能授权非自己的卡",
CodeCannotRevokeOthersAuthorization: "不能回收非自己创建的授权",
CodeCannotAuthorizeBoundCard: "不能授权已绑定设备的卡",
CodeCannotAuthorizeToOthersEnterprise: "不能授权给非自己的企业",
CodeDeviceAlreadyAuthorized: "设备已授权给该企业",
CodeDeviceNotAuthorized: "设备未授权给该企业",
CodeDeviceAuthorizedToOther: "设备已授权给其他企业",
CodeCannotAuthorizeOthersDevice: "不能授权非自己的设备",
CodeStorageNotConfigured: "对象存储服务未配置",
CodeStorageUploadFailed: "文件上传失败",
CodeStorageDownloadFailed: "文件下载失败",
CodeStorageFileNotFound: "文件不存在",
CodeStorageInvalidPurpose: "不支持的文件用途",
CodeStorageInvalidFileType: "不支持的文件类型",
CodeCarrierNotFound: "运营商不存在",
CodeCarrierCodeExists: "运营商编码已存在",
CodeInvalidCredentials: "用户名或密码错误",
CodeAccountLocked: "账号已锁定",
CodePasswordExpired: "密码已过期",
CodeInvalidOldPassword: "旧密码错误",
CodeInternalError: "内部服务器错误",
CodeDatabaseError: "数据库错误",
CodeRedisError: "缓存服务错误",
CodeServiceUnavailable: "服务暂时不可用",
CodeTimeout: "请求超时",
CodeTaskQueueError: "任务队列错误",
}
// GetMessage 获取错误码对应的消息
// lang 参数暂时保留以便未来支持多语言,目前仅支持中文
func GetMessage(code int, lang string) string {
if msg, ok := errorMessages[code]; ok {
return msg
}
// 未定义的错误码返回默认消息
if code >= 2000 && code < 3000 {
return "内部服务器错误"
}
return "请求处理失败"
}
// GetHTTPStatus 将错误码映射为 HTTP 状态码
func GetHTTPStatus(code int) int {
switch code {
case CodeSuccess:
return 200 // OK
case CodeInvalidParam, CodeRequestTooLarge:
return 400 // Bad Request
case CodeMissingToken, CodeInvalidToken, CodeUnauthorized:
return 401 // Unauthorized
case CodeForbidden:
return 403 // Forbidden
case CodeNotFound:
return 404 // Not Found
case CodeConflict,
CodeUsernameExists,
CodePhoneExists,
CodeRoleNameExists,
CodePermCodeExists,
CodeShopCodeExists,
CodeEnterpriseCodeExists,
CodeCustomerPhoneExists,
CodeCarrierCodeExists:
return 409 // Conflict
case CodeTooManyRequests:
return 429 // Too Many Requests
case CodeServiceUnavailable:
return 503 // Service Unavailable
case CodeTimeout:
return 504 // Gateway Timeout
default:
// 服务端错误2000-2999默认映射为 500
if code >= 2000 && code < 3000 {
return 500 // Internal Server Error
}
// 客户端错误1000-1999默认映射为 400
if code >= 1000 && code < 2000 {
return 400 // Bad Request
}
// 其他未知错误默认为 500
return 500 // Internal Server Error
}
}
// GetLogLevel 将错误码映射为日志级别
// 返回值: "warn" (客户端错误), "error" (服务端错误), "info" (成功)
func GetLogLevel(code int) string {
if code == 0 {
return "info" // 成功
}
if code >= 2000 && code < 3000 {
return "error" // 服务端错误
}
if code >= 1000 && code < 2000 {
return "warn" // 客户端错误
}
return "error" // 默认为错误级别
}