// Package systemconfig 实现受控系统配置注册、缓存和持久化 Adapter。 package systemconfig import ( "context" stderrors "errors" "strconv" "strings" "sync" "time" "github.com/bytedance/sonic" "github.com/redis/go-redis/v9" "github.com/break/junhong_cmp_fiber/pkg/constants" ) // Definition 是业务模块拥有的配置 Key 注册定义。 type Definition struct { Key string Module string ValueType string DefaultValue string Description string Readonly bool Sensitive bool Control string EnumValues []string Min *int64 Max *int64 } // Registry 保存可写配置的代码权威定义。 type Registry struct { mu sync.RWMutex definitions map[string]Definition lastValidated map[string]string } // NewRegistry 创建空注册表;具体业务 Key 由所属模块注册。 func NewRegistry() *Registry { return &Registry{definitions: map[string]Definition{}, lastValidated: map[string]string{}} } // Register 注册一个业务模块拥有的配置 Key。 func (r *Registry) Register(definition Definition) error { if !validKey(definition.Key) || strings.TrimSpace(definition.Module) == "" || strings.TrimSpace(definition.Description) == "" { return stderrors.New("系统配置注册信息不完整") } if err := ValidateValue(definition, definition.DefaultValue); err != nil { return err } r.mu.Lock() defer r.mu.Unlock() if existing, exists := r.definitions[definition.Key]; exists { if existing.ValueType != definition.ValueType { return stderrors.New("系统配置 Key 存在类型冲突") } return stderrors.New("系统配置 Key 重复注册") } r.definitions[definition.Key] = definition r.lastValidated[definition.Key] = definition.DefaultValue return nil } // Get 查询已注册定义。 func (r *Registry) Get(key string) (Definition, bool) { r.mu.RLock() defer r.mu.RUnlock() definition, exists := r.definitions[key] return definition, exists } // List 返回注册定义快照。 func (r *Registry) List() []Definition { r.mu.RLock() defer r.mu.RUnlock() items := make([]Definition, 0, len(r.definitions)) for _, definition := range r.definitions { items = append(items, definition) } return items } // Remember 保存最近一次通过注册校验的值。 func (r *Registry) Remember(key, value string) { r.mu.Lock() defer r.mu.Unlock() r.lastValidated[key] = value } // LastValidatedOrDefault 返回最近验证值或代码安全默认值。 func (r *Registry) LastValidatedOrDefault(definition Definition) string { r.mu.RLock() defer r.mu.RUnlock() if value, exists := r.lastValidated[definition.Key]; exists { return value } return definition.DefaultValue } // ValidateValue 按注册类型、枚举和值域校验字符串化配置值。 func ValidateValue(definition Definition, value string) error { switch definition.ValueType { case constants.SystemConfigTypeString: case constants.SystemConfigTypeInt: parsed, err := strconv.ParseInt(value, 10, 64) if err != nil { return stderrors.New("系统配置值必须是整数") } if definition.Min != nil && parsed < *definition.Min { return stderrors.New("系统配置值小于允许范围") } if definition.Max != nil && parsed > *definition.Max { return stderrors.New("系统配置值大于允许范围") } case constants.SystemConfigTypeBool: if value != "true" && value != "false" { return stderrors.New("系统配置值必须是 true 或 false") } case constants.SystemConfigTypeJSON: var parsed any if sonic.Unmarshal([]byte(value), &parsed) != nil { return stderrors.New("系统配置值必须是合法 JSON") } default: return stderrors.New("系统配置类型不受支持") } if len(definition.EnumValues) > 0 { for _, allowed := range definition.EnumValues { if value == allowed { return nil } } return stderrors.New("系统配置值不在允许枚举中") } return nil } func validKey(key string) bool { parts := strings.Split(key, ".") if len(parts) < 3 { return false } for _, part := range parts { if strings.TrimSpace(part) == "" { return false } } return true } // Cache 是系统配置使用的最小缓存边界。 type Cache interface { Get(ctx context.Context, key string) (string, error) Set(ctx context.Context, key, value string, ttl time.Duration) error Delete(ctx context.Context, key string) error } // RedisCache 使用 Redis 实现系统配置短期缓存。 type RedisCache struct { client *redis.Client } // NewRedisCache 创建系统配置 Redis Adapter。 func NewRedisCache(client *redis.Client) *RedisCache { return &RedisCache{client: client} } // Get 读取缓存值。 func (c *RedisCache) Get(ctx context.Context, key string) (string, error) { return c.client.Get(ctx, key).Result() } // Set 回填缓存值。 func (c *RedisCache) Set(ctx context.Context, key, value string, ttl time.Duration) error { return c.client.Set(ctx, key, value, ttl).Err() } // Delete 失效单 Key 缓存。 func (c *RedisCache) Delete(ctx context.Context, key string) error { return c.client.Del(ctx, key).Err() }