package service import ( "context" "encoding/json" "fmt" "os" "path/filepath" "runtime" "sync" "time" "market-data-service/api" "market-data-service/pkg/config" ) // ConfigService 配置管理服务接口 type ConfigService interface { // GetConfigList 获取配置列表 GetConfigList(ctx context.Context, req *api.ConfigListRequest) (*api.ConfigListData, error) // UpdateConfig 更新配置 UpdateConfig(ctx context.Context, req *api.ConfigUpdateRequest) (*api.ConfigUpdateData, error) // ReloadConfig 热加载配置 ReloadConfig(ctx context.Context, req *api.ReloadRequest) (*api.ReloadData, error) // GetSystemStatus 获取系统状态 GetSystemStatus(ctx context.Context) (*api.SystemStatusData, error) // GetCurrentConfig 获取当前配置(内部使用) GetCurrentConfig() *config.Config } // ConfigServiceImpl 配置服务实现 type ConfigServiceImpl struct { configPath string config *config.Config mu sync.RWMutex // 配置变更回调 callbacks map[api.ConfigType][]func() cbMu sync.RWMutex // 启动时间 startTime time.Time // 配置版本 version string } // NewConfigService 创建配置服务 func NewConfigService(configPath string) (ConfigService, error) { cfg, err := config.Load(configPath) if err != nil { // 如果加载失败,使用默认配置 cfg = getDefaultConfig() } return &ConfigServiceImpl{ configPath: configPath, config: cfg, callbacks: make(map[api.ConfigType][]func()), startTime: time.Now(), version: "1.0.0", }, nil } // GetConfigList 获取配置列表 func (s *ConfigServiceImpl) GetConfigList(ctx context.Context, req *api.ConfigListRequest) (*api.ConfigListData, error) { s.mu.RLock() defer s.mu.RUnlock() sections := []api.ConfigSection{} // 服务器配置 if req.Type == "" || req.Type == api.ConfigTypeServer { sections = append(sections, api.ConfigSection{ Name: "服务器配置", Type: api.ConfigTypeServer, Description: "HTTP服务器相关配置", Items: []api.ConfigItem{ { Key: "port", Value: s.config.Server.Port, Type: "int", Description: "服务端口", Editable: true, Required: true, }, { Key: "mode", Value: s.config.Server.Mode, Type: "string", Description: "运行模式: debug/release", Editable: true, Required: true, }, { Key: "api_key", Value: s.config.Server.APIKey, Type: "string", Description: "API认证密钥", Editable: true, Required: true, }, }, }) } // 数据库配置 if req.Type == "" || req.Type == api.ConfigTypeDatabase { sections = append(sections, api.ConfigSection{ Name: "数据库配置", Type: api.ConfigTypeDatabase, Description: "PostgreSQL数据库连接配置", Items: []api.ConfigItem{ { Key: "host", Value: s.config.Database.Host, Type: "string", Description: "数据库主机地址", Editable: true, Required: true, }, { Key: "port", Value: s.config.Database.Port, Type: "int", Description: "数据库端口", Editable: true, Required: true, }, { Key: "user", Value: s.config.Database.User, Type: "string", Description: "数据库用户名", Editable: true, Required: true, }, { Key: "password", Value: "********", Type: "password", Description: "数据库密码", Editable: true, Required: true, }, { Key: "database", Value: s.config.Database.Database, Type: "string", Description: "数据库名", Editable: true, Required: true, }, }, }) } // Redis配置 if req.Type == "" || req.Type == api.ConfigTypeRedis { sections = append(sections, api.ConfigSection{ Name: "Redis配置", Type: api.ConfigTypeRedis, Description: "Redis缓存配置", Items: []api.ConfigItem{ { Key: "host", Value: s.config.Redis.Host, Type: "string", Description: "Redis主机地址", Editable: true, Required: false, }, { Key: "port", Value: s.config.Redis.Port, Type: "int", Description: "Redis端口", Editable: true, Required: false, }, { Key: "password", Value: "********", Type: "password", Description: "Redis密码", Editable: true, Required: false, }, { Key: "db", Value: s.config.Redis.DB, Type: "int", Description: "Redis数据库编号", Editable: true, Required: false, }, }, }) } // 数据源配置 if req.Type == "" || req.Type == api.ConfigTypeSource { sections = append(sections, api.ConfigSection{ Name: "数据源配置", Type: api.ConfigTypeSource, Description: "股票和期货数据源配置", Items: []api.ConfigItem{ { Key: "stock_active", Value: s.config.Sources.Stock.Active, Type: "string", Description: "股票数据源适配器", Editable: true, Required: true, }, { Key: "futures_active", Value: s.config.Sources.Futures.Active, Type: "string", Description: "期货数据源适配器", Editable: true, Required: true, }, }, }) } return &api.ConfigListData{ Sections: sections, Version: s.version, Updated: time.Now(), }, nil } // UpdateConfig 更新配置 func (s *ConfigServiceImpl) UpdateConfig(ctx context.Context, req *api.ConfigUpdateRequest) (*api.ConfigUpdateData, error) { s.mu.Lock() defer s.mu.Unlock() needRestart := false switch req.Type { case api.ConfigTypeServer: if port, ok := req.Items["port"]; ok { s.config.Server.Port = int(port.(float64)) needRestart = true } if mode, ok := req.Items["mode"]; ok { s.config.Server.Mode = mode.(string) } if apiKey, ok := req.Items["api_key"]; ok { s.config.Server.APIKey = apiKey.(string) } case api.ConfigTypeDatabase: if host, ok := req.Items["host"]; ok { s.config.Database.Host = host.(string) needRestart = true } if port, ok := req.Items["port"]; ok { s.config.Database.Port = int(port.(float64)) needRestart = true } if user, ok := req.Items["user"]; ok { s.config.Database.User = user.(string) needRestart = true } if password, ok := req.Items["password"]; ok && password.(string) != "********" { s.config.Database.Password = password.(string) needRestart = true } if database, ok := req.Items["database"]; ok { s.config.Database.Database = database.(string) needRestart = true } case api.ConfigTypeSource: if stockActive, ok := req.Items["stock_active"]; ok { s.config.Sources.Stock.Active = stockActive.(string) } if futuresActive, ok := req.Items["futures_active"]; ok { s.config.Sources.Futures.Active = futuresActive.(string) } } // 保存到文件 if err := s.saveConfig(); err != nil { return &api.ConfigUpdateData{ Success: false, Message: fmt.Sprintf("配置保存失败: %v", err), }, nil } // 触发回调 s.triggerCallbacks(req.Type) message := "配置更新成功" if needRestart { message += ",部分配置需要重启服务后生效" } return &api.ConfigUpdateData{ Success: true, NeedRestart: needRestart, Message: message, }, nil } // ReloadConfig 热加载配置 func (s *ConfigServiceImpl) ReloadConfig(ctx context.Context, req *api.ReloadRequest) (*api.ReloadData, error) { s.mu.Lock() defer s.mu.Unlock() // 从文件重新加载 cfg, err := config.Load(s.configPath) if err != nil { return &api.ReloadData{ Success: false, Message: fmt.Sprintf("加载配置失败: %v", err), }, nil } // 根据类型选择性更新 if req.ConfigType == "" { s.config = cfg } else { switch req.ConfigType { case api.ConfigTypeServer: s.config.Server = cfg.Server case api.ConfigTypeDatabase: s.config.Database = cfg.Database case api.ConfigTypeRedis: s.config.Redis = cfg.Redis case api.ConfigTypeSource: s.config.Sources = cfg.Sources } } // 触发回调 s.triggerCallbacks(req.ConfigType) return &api.ReloadData{ Success: true, Message: "配置热加载成功", }, nil } // GetSystemStatus 获取系统状态 func (s *ConfigServiceImpl) GetSystemStatus(ctx context.Context) (*api.SystemStatusData, error) { var m runtime.MemStats runtime.ReadMemStats(&m) uptime := time.Since(s.startTime) uptimeStr := formatDuration(uptime) return &api.SystemStatusData{ Status: "running", Version: s.version, StartTime: s.startTime, Uptime: uptimeStr, GoVersion: runtime.Version(), MemoryUsage: api.MemoryInfo{ Alloc: m.Alloc, TotalAlloc: m.TotalAlloc, Sys: m.Sys, NumGC: m.NumGC, }, Goroutines: runtime.NumGoroutine(), }, nil } // GetCurrentConfig 获取当前配置 func (s *ConfigServiceImpl) GetCurrentConfig() *config.Config { s.mu.RLock() defer s.mu.RUnlock() return s.config } // RegisterCallback 注册配置变更回调 func (s *ConfigServiceImpl) RegisterCallback(configType api.ConfigType, callback func()) { s.cbMu.Lock() defer s.cbMu.Unlock() s.callbacks[configType] = append(s.callbacks[configType], callback) } // triggerCallbacks 触发回调 func (s *ConfigServiceImpl) triggerCallbacks(configType api.ConfigType) { s.cbMu.RLock() defer s.cbMu.RUnlock() // 触发特定类型的回调 if cbs, ok := s.callbacks[configType]; ok { for _, cb := range cbs { go cb() } } // 触发通用回调 if cbs, ok := s.callbacks[""]; ok { for _, cb := range cbs { go cb() } } } // saveConfig 保存配置到文件 func (s *ConfigServiceImpl) saveConfig() error { if s.configPath == "" { return nil } // 确保目录存在 dir := filepath.Dir(s.configPath) if err := os.MkdirAll(dir, 0755); err != nil { return err } // 序列化为JSON data, err := json.MarshalIndent(s.config, "", " ") if err != nil { return err } return os.WriteFile(s.configPath, data, 0644) } // getDefaultConfig 获取默认配置 func getDefaultConfig() *config.Config { return &config.Config{ Server: config.ServerConfig{ Port: 8080, Mode: "debug", APIKey: "default-api-key", }, Database: config.DatabaseConfig{ Host: "localhost", Port: 5432, User: "user", Password: "password", Database: "marketdata", }, Redis: config.RedisConfig{ Host: "localhost", Port: 6379, Password: "", DB: 0, }, Sources: config.SourcesConfig{ Stock: config.SourceConfig{ Active: "tushare", }, Futures: config.SourceConfig{ Active: "tushare", }, }, } } // formatDuration 格式化持续时间 func formatDuration(d time.Duration) string { days := int(d.Hours()) / 24 hours := int(d.Hours()) % 24 minutes := int(d.Minutes()) % 60 if days > 0 { return fmt.Sprintf("%d天%d小时%d分钟", days, hours, minutes) } if hours > 0 { return fmt.Sprintf("%d小时%d分钟", hours, minutes) } return fmt.Sprintf("%d分钟", minutes) }