Files
junhong_cmp_fiber/internal/infrastructure/systemconfig/registry.go
break 17782d5f8e
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m20s
实现七月迭代公共技术基础
2026-07-23 17:52:48 +09:00

184 lines
5.0 KiB
Go

// 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()
}