All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m20s
184 lines
5.0 KiB
Go
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()
|
|
}
|