# 枚举与状态字段规范 **背景**:系统历史上存在 int/string 枚举混用、description 与常量定义脱节、响应字段不统一等问题,导致前端映射错误、接口文档失真。本规范统一所有枚举相关写法。 --- ## 1. 类型选择:int vs string ### 选择规则 | 场景 | 类型 | 理由 | |------|------|------| | **状态类**:表示生命周期阶段(待支付→已完成→已关闭) | `int` | 便于范围查询、数值比较、DB 索引优化 | | **布尔状态**:启用/禁用、激活/未激活 | `int` | 与全局常量统一,`0=禁用, 1=启用` | | **类型/方式类**:支付方式、订单类型、运营商、平台 | `string` | 语义更清晰,无需记忆数字含义 | | **标识符类**:平台类型(web/h5/all)、角色来源 | `string` | 约定字符串值,自文档化 | ### 判断依据 ``` 这个字段表示"现在处于哪个阶段"? → int 这个字段表示"它属于哪种类别/使用什么方式"? → string ``` ### ✅ 正确示例 ```go // 状态类 → int Status int `json:"status"` // 充值状态:1=待支付, 2=已支付... PaymentStatus int `json:"payment_status"` // 订单支付状态 // 类型/方式类 → string PaymentMethod string `json:"payment_method"` // "wechat" | "offline" OrderType string `json:"order_type"` // "single_card" | "device" Platform string `json:"platform"` // "web" | "h5" | "all" ``` ### ❌ 错误示例 ```go // ❌ 状态用 string(无法范围查询,数字对比失效) Status string `json:"status"` // "pending" | "completed" // ❌ 支付方式用 int(前端必须维护不直观的数字映射) PaymentMethod int `json:"payment_method"` // 1=微信, 2=线下 ``` --- ## 2. Int 状态值约定 ### 2.1 通用禁用/启用 **必须使用全局常量,禁止自定义**: ```go // pkg/constants/constants.go StatusDisabled = 0 // 禁用 StatusEnabled = 1 // 启用 ``` ✅ 正确: ```go Status int `gorm:"comment:状态(0-禁用 1-启用);default:1"` ``` ❌ 禁止(与全局常量语义相反): ```go // ❌ 不可以用 1=启用 2=禁用 这种方式 Status int `gorm:"comment:状态(1-启用 2-禁用)"` ``` ### 2.2 生命周期状态 从 **1** 开始递增(0 保留,避免与"未赋值/零值"混淆): ```go // 充值状态(标准示例) const ( RechargeStatusPending = 1 // 待支付 RechargeStatusPaid = 2 // 已支付 RechargeStatusCompleted = 3 // 已完成 RechargeStatusClosed = 4 // 已关闭 RechargeStatusRefunded = 5 // 已退款 ) ``` ### 2.3 特殊值 - 跳跃值(如 `99`)仅用于明确需要人工干预的异常状态,必须加注释说明原因 - 禁止在同一枚举中混用 0 起始和 1 起始 --- ## 3. String 枚举值约定 - 全小写字母 + 下划线 `snake_case`(禁止驼峰、禁止大写) - 必须在 `pkg/constants/` 定义常量,禁止硬编码字符串 - DTO 中必须加 `validate:"oneof=..."` 约束 ```go // pkg/constants/constants.go const ( PaymentMethodWechat = "wechat" // 微信支付 PaymentMethodOffline = "offline" // 线下转账 ) // DTO 中 PaymentMethod string `json:"payment_method" validate:"required,oneof=wechat offline" description:"支付方式 (wechat:微信支付, offline:线下转账)"` ``` ❌ 禁止: ```go // ❌ 硬编码字符串 if record.PaymentMethod == "wechat" { ... } // ❌ 大写或驼峰 const PaymentMethodWechat = "Wechat" const PaymentMethodWechat = "WECHAT" ``` --- ## 4. Constants 是唯一真相来源 **DTO 的 description 枚举列表必须与 `pkg/constants/` 定义完全一致。** ### 操作规程 1. 先在 `pkg/constants/` 定义(或查找已有)枚举常量及注释 2. 将常量的注释**原文抄写**到 DTO description ```go // 步骤1:constants.go 中定义 const ( RechargeStatusPending = 1 // 待支付 RechargeStatusPaid = 2 // 已支付 RechargeStatusCompleted = 3 // 已完成 RechargeStatusClosed = 4 // 已关闭 RechargeStatusRefunded = 5 // 已退款 ) // 步骤2:DTO description 从上面抄写,格式统一 Status int `json:"status" description:"状态 (1:待支付, 2:已支付, 3:已完成, 4:已关闭, 5:已退款)"` ``` ### 禁止行为 ```go // ❌ description 中的枚举值与 constants 不一致(本次 bug 根因) // constants: RechargeStatusCompleted=3(已完成) // dto 写的: 3:已取消 ← 完全错误! Status int `json:"status" description:"状态 (1:待支付, 2:已完成, 3:已取消)"` ``` --- ## 5. description 格式标准 **统一格式**:`字段含义 (值1:中文含义1, 值2:中文含义2, ...)` 规则: - 必须用**中文圆括号外**、**英文括号内** - 值和含义之间用**冒号** `:`,不用等号 `=` - 多个值之间用**逗号加空格** `, ` - 含义必须是**中文**,不可用英文 ```go // ✅ 统一格式 Status int `description:"状态 (1:待支付, 2:已支付, 3:已完成, 4:已关闭, 5:已退款)"` Platform string `description:"适用端口 (all:全部, web:Web后台, h5:H5端)"` UserType int `description:"用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)"` // ❌ 格式混乱 Status int `description:"状态 (0=禁用, 1=启用)"` // 用等号 Status int `description:"0=禁用 1=启用"` // 无括号无逗号 Status int `description:"状态:0待生效 1生效中 2已用完"` // 格式不统一 ``` --- ## 6. Response DTO 的状态字段 **所有 Response DTO 中的 int 状态字段,必须同时提供对应的 `xxx_name` 文字字段。** 这是防止前端映射错误的关键措施。 ```go // ✅ Response DTO 标准写法 type AgentRechargeResponse struct { Status int `json:"status" description:"状态 (1:待支付, 2:已支付, 3:已完成, 4:已关闭, 5:已退款)"` StatusName string `json:"status_name" description:"状态名称(中文)"` // ... } // Service 层 toResponse 函数中赋值 func statusName(status int) string { switch status { case constants.RechargeStatusPending: return "待支付" case constants.RechargeStatusPaid: return "已支付" case constants.RechargeStatusCompleted: return "已完成" case constants.RechargeStatusClosed: return "已关闭" case constants.RechargeStatusRefunded: return "已退款" default: return "未知" } } ``` **字段命名约定**: - `status` → `status_name` - `payment_status` → `payment_status_name` - `commission_status` → `commission_status_name` **例外**:Request DTO(查询过滤、创建请求)不需要 `_name` 字段。 --- ## 7. Code Review 检查项 ``` □ int/string 类型选择是否符合规则? □ 禁/启用是否用了 0=禁用, 1=启用?(禁止 1=启用, 2=禁用) □ string 枚举常量是否在 pkg/constants/ 定义? □ dto description 是否从 constants 注释原文抄写? □ description 格式是否统一(冒号、逗号、括号)? □ Response DTO 是否同时有 status int 和 status_name string? □ 是否有遗漏的枚举值没有在 description 中列出? ``` --- ## 8. 现存已知不一致(技术债,待修复) | 文件 | 问题 | 正确值 | |------|------|--------| | `internal/model/shop_package_allocation.go` | `1-启用 2-禁用` | 应改为 `0-禁用 1-启用` | | `internal/model/system.go` | `1-启用 2-禁用` | 需确认是否改 DB | | `internal/model/dto/agent_recharge_dto.go` | description `2:已完成, 3:已取消` | 应为 `2:已支付, 3:已完成, 4:已关闭, 5:已退款` | | `internal/model/dto/asset_wallet_dto.go` | 只有 `status_text` 无 `status int` | 需补充 `status int` | > ⚠️ 已有数据的状态起始值(如 `1-启用 2-禁用`)改变前必须做 DB 数据迁移,改动前先评估影响。