实现七月迭代公共技术基础
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m20s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m20s
This commit is contained in:
183
internal/infrastructure/systemconfig/registry.go
Normal file
183
internal/infrastructure/systemconfig/registry.go
Normal file
@@ -0,0 +1,183 @@
|
||||
// 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()
|
||||
}
|
||||
Reference in New Issue
Block a user