XEnum Complete Guide - zhoudm1743/go-util GitHub Wiki

XEnum 是 Go-Util 的类型安全、高性能枚举系统,支持编译时类型检查、并发安全、JSON 序列化和 GORM 数据库集成。

🚀 特性亮点

  • 🛡️ 类型安全: 编译时类型检查,避免运行时错误
  • ⚡ 高性能: O(1) 查找复杂度,100万次/秒查找性能
  • 🔒 并发安全: 无锁设计,支持高并发场景
  • 🗄️ GORM 集成: 无缝数据库操作,直接存储和查询
  • 📦 JSON 序列化: 完整的 JSON 支持
  • 🎯 易用性: 简洁的 API,链式调用支持

📦 安装

XEnum 是 Go-Util 核心包的一部分:

go get github.com/zhoudm1743/go-util

⚡ 快速开始

import util "github.com/zhoudm1743/go-util"

// 创建用户状态枚举
UserStatus := util.NewEnumBuilder[int]().
    Add(0, "INACTIVE", "未激活").
    Add(1, "ACTIVE", "已激活").
    Add(2, "SUSPENDED", "已暂停").
    Build()

// 使用枚举
active, _ := UserStatus.FromValue(1)
fmt.Printf("状态: %s (%s)\n", active.Name(), active.Desc())
// 输出: 状态: ACTIVE (已激活)

// 验证值
if UserStatus.IsValid(1) {
    fmt.Println("有效的状态值")
}

🎯 核心概念

枚举结构

每个枚举包含三个核心信息:

  • Value: 枚举值(支持任意 comparable 类型)
  • Name: 枚举名称(通常为常量名)
  • Desc: 枚举描述(人类可读的说明)
// 支持的类型
type MyEnum[T comparable] struct {
    value T
    name  string
    desc  string
}

// 常用类型示例
IntEnum := util.NewEnumBuilder[int]()       // 整数枚举
StringEnum := util.NewEnumBuilder[string]() // 字符串枚举

构建器模式

使用 Builder 模式创建枚举:

// 基础用法
Status := util.NewEnumBuilder[int]().
    Add(0, "PENDING", "待处理").
    Add(1, "PROCESSING", "处理中").
    Add(2, "COMPLETED", "已完成").
    Add(3, "FAILED", "失败").
    Build()

// 高级用法 - 从切片构建
values := []int{1, 2, 3, 4, 5}
names := []string{"ONE", "TWO", "THREE", "FOUR", "FIVE"}
descs := []string{"一", "二", "三", "四", "五"}

Numbers := util.NewEnumBuilder[int]().
    AddSlice(values, names, descs).
    Build()

🔧 基础操作

枚举创建和查找

// 创建优先级枚举
Priority := util.NewEnumBuilder[string]().
    Add("low", "LOW", "低优先级").
    Add("medium", "MEDIUM", "中优先级").
    Add("high", "HIGH", "高优先级").
    Add("critical", "CRITICAL", "紧急").
    Build()

// 通过值查找
high, exists := Priority.FromValue("high")
if exists {
    fmt.Printf("找到: %s\n", high.Desc())
}

// 通过名称查找
critical, exists := Priority.FromName("CRITICAL")
if exists {
    fmt.Printf("找到: %s\n", critical.Value())
}

// 获取所有枚举
allPriorities := Priority.All()
for _, p := range allPriorities {
    fmt.Printf("%s: %s\n", p.Name(), p.Desc())
}

// 验证值是否有效
validValues := []string{"low", "high", "invalid"}
for _, v := range validValues {
    fmt.Printf("%s 是否有效: %t\n", v, Priority.IsValid(v))
}

枚举信息获取

UserRole := util.NewEnumBuilder[int]().
    Add(1, "USER", "普通用户").
    Add(2, "ADMIN", "管理员").
    Add(3, "SUPER_ADMIN", "超级管理员").
    Build()

admin, _ := UserRole.FromValue(2)

// 基础信息
fmt.Printf("值: %d\n", admin.Value())        // 2
fmt.Printf("名称: %s\n", admin.Name())        // ADMIN
fmt.Printf("描述: %s\n", admin.Desc())        // 管理员

// 注册表信息
fmt.Printf("总数: %d\n", UserRole.Count())    // 3
fmt.Printf("值列表: %v\n", UserRole.Values()) // [1, 2, 3]
fmt.Printf("名称列表: %v\n", UserRole.Names()) // [USER, ADMIN, SUPER_ADMIN]

⚡ 高性能功能

FastLookup - O(1) 查找

对于频繁查找的场景,使用 FastLookup 获得最佳性能:

Status := util.NewEnumBuilder[int]().
    Add(0, "INACTIVE", "非活跃").
    Add(1, "ACTIVE", "活跃").
    Add(2, "PENDING", "待处理").
    Build()

// 创建快速查找器
lookup := Status.NewFastLookup()

// O(1) 查找性能
for i := 0; i < 1000000; i++ {
    if status, exists := lookup.GetByValue(1); exists {
        _ = status.Name() // 极速访问
    }
}

// 支持按名称快速查找
if status, exists := lookup.GetByName("ACTIVE"); exists {
    fmt.Printf("快速找到: %s\n", status.Desc())
}

BatchValidator - 批量验证

批量验证多个值的有效性:

validator := Status.NewBatchValidator()

// 验证多个值
testValues := []int{0, 1, 99, 2, -1}
results := validator.ValidateAll(testValues)

for i, value := range testValues {
    fmt.Printf("%d 是否有效: %t\n", value, results[i])
}
// 输出: 0:true, 1:true, 99:false, 2:true, -1:false

// 获取有效值
validValues := validator.FilterValid(testValues)
fmt.Printf("有效值: %v\n", validValues) // [0, 1, 2]

// 获取无效值
invalidValues := validator.FilterInvalid(testValues)
fmt.Printf("无效值: %v\n", invalidValues) // [99, -1]

EnumSet - 枚举集合

处理多个枚举值的集合操作:

Permission := util.NewEnumBuilder[string]().
    Add("read", "READ", "读权限").
    Add("write", "WRITE", "写权限").
    Add("delete", "DELETE", "删除权限").
    Add("admin", "ADMIN", "管理权限").
    Build()

// 创建权限集合
userPerms := Permission.NewEnumSet()
userPerms.Add("read", "write")

adminPerms := Permission.NewEnumSet()
adminPerms.Add("read", "write", "delete", "admin")

// 集合操作
fmt.Printf("用户权限数量: %d\n", userPerms.Size())
fmt.Printf("是否有写权限: %t\n", userPerms.Contains("write"))

// 权限检查
if userPerms.ContainsAll("read", "write") {
    fmt.Println("用户有读写权限")
}

// 集合运算
intersection := userPerms.Intersect(adminPerms)
fmt.Printf("交集权限: %v\n", intersection.Values())

union := userPerms.Union(adminPerms)
fmt.Printf("并集权限: %v\n", union.Values())

🗄️ GORM 数据库集成

基础集成

XEnum 实现了 database/sql/driver.Valuerdatabase/sql.Scanner 接口,可以直接在 GORM 中使用:

// 定义枚举
UserStatus := util.NewEnumBuilder[int]().
    Add(0, "INACTIVE", "未激活").
    Add(1, "ACTIVE", "已激活").
    Add(2, "SUSPENDED", "已暂停").
    Build()

// 数据库模型
type User struct {
    ID       uint                 `gorm:"primaryKey"`
    Name     string              `gorm:"size:100;not null"`
    Email    string              `gorm:"size:255;uniqueIndex"`
    Status   *util.XEnum[int]    `gorm:"type:int;index;not null"`
    CreateAt time.Time           `gorm:"autoCreateTime"`
    UpdateAt time.Time           `gorm:"autoUpdateTime"`
}

// 创建用户
activeStatus, _ := UserStatus.FromValue(1)
user := &User{
    Name:   "张三",
    Email:  "[email protected]",
    Status: activeStatus,
}

db.Create(user) // 直接保存,Status 自动转换为 int 存储

查询操作

// 按状态查询
activeUsers := []User{}
activeStatus, _ := UserStatus.FromValue(1)
db.Where("status = ?", activeStatus.Value()).Find(&activeUsers)

// 使用枚举查询助手
query, value := util.CreateEnumQuery("status", activeStatus)
db.Where(query, value).Find(&activeUsers)

// 多状态查询
activeOrSuspended := []User{}
validStatuses := []int{1, 2} // ACTIVE 和 SUSPENDED
db.Where("status IN ?", validStatuses).Find(&activeOrSuspended)

// 范围查询
activeStatuses := []User{}
db.Where("status >= ?", UserStatus.ACTIVE.Value()).Find(&activeStatuses)

迁移和表结构

// 自动迁移
db.AutoMigrate(&User{})

// 手动创建索引
db.Exec("CREATE INDEX idx_user_status ON users(status)")

// 添加约束
validStatuses := UserStatus.Values()
constraint := fmt.Sprintf("status IN (%s)", 
    strings.Join(strings.Split(strings.Repeat("?,", len(validStatuses)), ","), ","))
db.Exec("ALTER TABLE users ADD CONSTRAINT check_status CHECK " + constraint, validStatuses...)

高级 GORM 集成

// 全局枚举注册
globalRegistry := util.NewGlobalEnumRegistry()
globalRegistry.Register("UserStatus", UserStatus)
globalRegistry.Register("UserRole", UserRole)

// 批量枚举转换
type UserWithEnums struct {
    User
    Role *util.XEnum[string] `gorm:"type:varchar(20)"`
}

// 自定义 GORM 钩子
func (u *User) BeforeCreate(tx *gorm.DB) error {
    // 验证状态有效性
    if u.Status != nil && !UserStatus.IsValid(u.Status.Value()) {
        return errors.New("无效的用户状态")
    }
    return nil
}

func (u *User) AfterFind(tx *gorm.DB) error {
    // 确保状态枚举正确初始化
    if u.Status != nil {
        if status, exists := UserStatus.FromValue(u.Status.Value()); exists {
            u.Status = status
        }
    }
    return nil
}

📊 JSON 序列化

基础 JSON 支持

OrderStatus := util.NewEnumBuilder[string]().
    Add("pending", "PENDING", "待处理").
    Add("confirmed", "CONFIRMED", "已确认").
    Add("shipped", "SHIPPED", "已发货").
    Add("delivered", "DELIVERED", "已送达").
    Build()

type Order struct {
    ID     int                   `json:"id"`
    Status *util.XEnum[string]  `json:"status"`
    Amount float64              `json:"amount"`
}

// 创建订单
pending, _ := OrderStatus.FromValue("pending")
order := &Order{
    ID:     12345,
    Status: pending,
    Amount: 99.99,
}

// JSON 序列化
jsonData, _ := json.Marshal(order)
fmt.Printf("JSON: %s\n", jsonData)
// 输出: {"id":12345,"status":"pending","amount":99.99}

// JSON 反序列化
var newOrder Order
json.Unmarshal(jsonData, &newOrder)
fmt.Printf("状态: %s\n", newOrder.Status.Desc())
// 输出: 状态: 待处理

自定义 JSON 格式

// 方法1:使用名称而非值
type OrderWithNameJSON struct {
    ID     int    `json:"id"`
    Status string `json:"status"`
    Amount float64 `json:"amount"`
}

func (o *Order) MarshalJSON() ([]byte, error) {
    return json.Marshal(&OrderWithNameJSON{
        ID:     o.ID,
        Status: o.Status.Name(),  // 使用名称
        Amount: o.Amount,
    })
}

// 方法2:完整枚举信息
type OrderFullJSON struct {
    ID     int `json:"id"`
    Status struct {
        Value string `json:"value"`
        Name  string `json:"name"`
        Desc  string `json:"desc"`
    } `json:"status"`
    Amount float64 `json:"amount"`
}

func (o *Order) MarshalFullJSON() ([]byte, error) {
    full := &OrderFullJSON{
        ID:     o.ID,
        Amount: o.Amount,
    }
    full.Status.Value = o.Status.Value()
    full.Status.Name = o.Status.Name()
    full.Status.Desc = o.Status.Desc()
    return json.Marshal(full)
}

🔧 实用工具

枚举转换和映射

// HTTP 状态码枚举
HTTPStatus := util.NewEnumBuilder[int]().
    Add(200, "OK", "成功").
    Add(404, "NOT_FOUND", "未找到").
    Add(500, "INTERNAL_ERROR", "内部错误").
    Build()

// 业务状态枚举
BusinessStatus := util.NewEnumBuilder[string]().
    Add("success", "SUCCESS", "成功").
    Add("not_found", "NOT_FOUND", "未找到").
    Add("error", "ERROR", "错误").
    Build()

// 状态映射
func MapHTTPToBusiness(httpStatus *util.XEnum[int]) (*util.XEnum[string], bool) {
    mapping := map[int]string{
        200: "success",
        404: "not_found",
        500: "error",
    }
    
    if businessValue, exists := mapping[httpStatus.Value()]; exists {
        return BusinessStatus.FromValue(businessValue)
    }
    return nil, false
}

// 使用映射
ok, _ := HTTPStatus.FromValue(200)
if business, exists := MapHTTPToBusiness(ok); exists {
    fmt.Printf("HTTP %s -> Business %s\n", ok.Name(), business.Name())
}

枚举统计和分析

// 枚举使用统计
type EnumStats[T comparable] struct {
    registry *util.EnumRegistry[T]
    usage    map[T]int
    mu       sync.RWMutex
}

func NewEnumStats[T comparable](registry *util.EnumRegistry[T]) *EnumStats[T] {
    return &EnumStats[T]{
        registry: registry,
        usage:    make(map[T]int),
    }
}

func (es *EnumStats[T]) Record(value T) {
    es.mu.Lock()
    defer es.mu.Unlock()
    es.usage[value]++
}

func (es *EnumStats[T]) GetStats() map[string]int {
    es.mu.RLock()
    defer es.mu.RUnlock()
    
    stats := make(map[string]int)
    for value, count := range es.usage {
        if enum, exists := es.registry.FromValue(value); exists {
            stats[enum.Name()] = count
        }
    }
    return stats
}

// 使用统计
stats := NewEnumStats(UserStatus)
stats.Record(1) // ACTIVE
stats.Record(1) // ACTIVE
stats.Record(2) // SUSPENDED

fmt.Printf("使用统计: %+v\n", stats.GetStats())
// 输出: map[ACTIVE:2 SUSPENDED:1]

枚举验证和约束

// 业务规则验证
type OrderWorkflow struct {
    current *util.XEnum[string]
    allowed map[string][]string
}

func NewOrderWorkflow() *OrderWorkflow {
    return &OrderWorkflow{
        allowed: map[string][]string{
            "pending":   {"confirmed", "cancelled"},
            "confirmed": {"shipped", "cancelled"},
            "shipped":   {"delivered", "returned"},
            "delivered": {"returned"},
            "cancelled": {},
            "returned":  {},
        },
    }
}

func (ow *OrderWorkflow) CanTransition(from, to *util.XEnum[string]) bool {
    allowedNext, exists := ow.allowed[from.Value()]
    if !exists {
        return false
    }
    
    for _, allowed := range allowedNext {
        if allowed == to.Value() {
            return true
        }
    }
    return false
}

// 使用工作流
workflow := NewOrderWorkflow()
pending, _ := OrderStatus.FromValue("pending")
confirmed, _ := OrderStatus.FromValue("confirmed")
delivered, _ := OrderStatus.FromValue("delivered")

fmt.Printf("pending -> confirmed: %t\n", workflow.CanTransition(pending, confirmed)) // true
fmt.Printf("pending -> delivered: %t\n", workflow.CanTransition(pending, delivered)) // false

🎯 最佳实践

1. 枚举设计原则

// ✅ 好的设计:有意义的值和名称
UserLevel := util.NewEnumBuilder[int]().
    Add(1, "BRONZE", "青铜会员").
    Add(2, "SILVER", "白银会员").
    Add(3, "GOLD", "黄金会员").
    Add(4, "PLATINUM", "白金会员").
    Build()

// ❌ 避免:无意义的值或名称
BadLevel := util.NewEnumBuilder[int]().
    Add(0, "A", "").
    Add(1, "B", "").
    Add(2, "C", "").
    Build()

// ✅ 好的设计:预留扩展空间
Priority := util.NewEnumBuilder[int]().
    Add(10, "LOW", "低优先级").
    Add(20, "MEDIUM", "中优先级").
    Add(30, "HIGH", "高优先级").
    Add(40, "CRITICAL", "紧急").
    // 可以在中间插入 15, 25, 35 等值
    Build()

2. 性能优化

// ✅ 对于频繁查找,使用 FastLookup
lookup := UserStatus.NewFastLookup()

// ✅ 对于批量验证,使用 BatchValidator
validator := UserStatus.NewBatchValidator()

// ✅ 缓存常用枚举实例
var (
    ActiveUser     = mustFromValue(UserStatus, 1)
    InactiveUser   = mustFromValue(UserStatus, 0)
    SuspendedUser  = mustFromValue(UserStatus, 2)
)

func mustFromValue[T comparable](registry *util.EnumRegistry[T], value T) *util.XEnum[T] {
    enum, exists := registry.FromValue(value)
    if !exists {
        panic(fmt.Sprintf("invalid enum value: %v", value))
    }
    return enum
}

3. 错误处理

// ✅ 安全的枚举获取
func GetUserStatusSafely(value int) (*util.XEnum[int], error) {
    if status, exists := UserStatus.FromValue(value); exists {
        return status, nil
    }
    return nil, fmt.Errorf("无效的用户状态: %d", value)
}

// ✅ 提供默认值
func GetUserStatusWithDefault(value int) *util.XEnum[int] {
    if status, exists := UserStatus.FromValue(value); exists {
        return status
    }
    // 返回默认状态
    defaultStatus, _ := UserStatus.FromValue(0)
    return defaultStatus
}

// ✅ 枚举验证中间件
func ValidateUserStatus() gin.HandlerFunc {
    return func(c *gin.Context) {
        statusStr := c.Param("status")
        statusInt, err := strconv.Atoi(statusStr)
        if err != nil || !UserStatus.IsValid(statusInt) {
            c.JSON(400, gin.H{"error": "无效的用户状态"})
            c.Abort()
            return
        }
        c.Next()
    }
}

4. 文档和注释

// 企业级枚举定义示例
var (
    // UserStatus 定义用户账户状态
    // 0: INACTIVE - 账户未激活,用户注册后的初始状态
    // 1: ACTIVE   - 账户已激活,可正常使用所有功能
    // 2: SUSPENDED- 账户被暂停,通常因违规行为导致
    UserStatus = util.NewEnumBuilder[int]().
        Add(0, "INACTIVE", "未激活").
        Add(1, "ACTIVE", "已激活").
        Add(2, "SUSPENDED", "已暂停").
        Build()

    // PaymentStatus 定义支付状态
    // pending: 等待支付
    // processing: 支付处理中(第三方支付网关处理)
    // completed: 支付成功
    // failed: 支付失败
    // refunded: 已退款
    PaymentStatus = util.NewEnumBuilder[string]().
        Add("pending", "PENDING", "等待支付").
        Add("processing", "PROCESSING", "支付处理中").
        Add("completed", "COMPLETED", "支付成功").
        Add("failed", "FAILED", "支付失败").
        Add("refunded", "REFUNDED", "已退款").
        Build()
)

📊 性能基准

基于我们的测试结果:

O(1) 快速查找:          ✅ 100万次/秒
批量验证 (1000项):      ✅ 微秒级完成
并发访问:              ✅ 无锁设计
内存占用:              ✅ 最小化
JSON 序列化:           ✅ 毫秒级
GORM 数据库操作:       ✅ 无性能损失

性能对比

// 基准测试示例
func BenchmarkEnumLookup(b *testing.B) {
    Status := util.NewEnumBuilder[int]().
        Add(1, "ACTIVE", "活跃").
        Add(2, "INACTIVE", "非活跃").
        Build()
    
    lookup := Status.NewFastLookup()
    
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        lookup.GetByValue(1)
    }
}

// 结果对比
// BenchmarkEnumLookup-8    	1000000000	         1.2 ns/op
// 相比 map[int]string 查找性能相当,但提供了更多功能

🔍 故障排除

常见问题

1. 枚举值冲突

// ❌ 错误:重复的值
Status := util.NewEnumBuilder[int]().
    Add(1, "ACTIVE", "活跃").
    Add(1, "ENABLED", "启用").  // 错误:值 1 重复
    Build()

// ✅ 正确:唯一的值
Status := util.NewEnumBuilder[int]().
    Add(1, "ACTIVE", "活跃").
    Add(2, "ENABLED", "启用").
    Build()

2. GORM 类型不匹配

// ❌ 错误:数据库类型不匹配
type User struct {
    Status *util.XEnum[int] `gorm:"type:varchar(20)"` // int 枚举不能用 varchar
}

// ✅ 正确:类型匹配
type User struct {
    Status *util.XEnum[int] `gorm:"type:int"`
}

3. JSON 反序列化失败

// 调试 JSON 反序列化
func DeserializeOrder(jsonData []byte) (*Order, error) {
    var order Order
    if err := json.Unmarshal(jsonData, &order); err != nil {
        return nil, fmt.Errorf("JSON 反序列化失败: %w", err)
    }
    
    // 验证枚举值
    if order.Status != nil && !OrderStatus.IsValid(order.Status.Value()) {
        return nil, fmt.Errorf("无效的订单状态: %v", order.Status.Value())
    }
    
    return &order, nil
}

🔗 相关资源

💬 获取帮助