You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

478 lines
11 KiB

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