package config import ( "errors" "strings" "hyapp/pkg/configx" "hyapp/pkg/logx" ) // Config describes statistics-service dependencies and MQ subscriptions. type Config struct { ServiceName string `yaml:"service_name"` NodeID string `yaml:"node_id"` Environment string `yaml:"environment"` HealthHTTPAddr string `yaml:"health_http_addr"` QueryHTTPAddr string `yaml:"query_http_addr"` MySQLDSN string `yaml:"mysql_dsn"` ActivityMySQLDSN string `yaml:"activity_mysql_dsn"` RocketMQ RocketMQConfig `yaml:"rocketmq"` Log logx.Config `yaml:"log"` } type RocketMQConfig struct { Enabled bool `yaml:"enabled"` NameServers []string `yaml:"name_servers"` NameServerDomain string `yaml:"name_server_domain"` AccessKey string `yaml:"access_key"` SecretKey string `yaml:"secret_key"` SecurityToken string `yaml:"security_token"` Namespace string `yaml:"namespace"` VIPChannel bool `yaml:"vip_channel"` WalletOutbox ConsumerMQConfig `yaml:"wallet_outbox"` RoomOutbox ConsumerMQConfig `yaml:"room_outbox"` UserOutbox ConsumerMQConfig `yaml:"user_outbox"` GameOutbox ConsumerMQConfig `yaml:"game_outbox"` } type ConsumerMQConfig struct { Enabled bool `yaml:"enabled"` Topic string `yaml:"topic"` ConsumerGroup string `yaml:"consumer_group"` ConsumerMaxReconsumeTimes int32 `yaml:"consumer_max_reconsume_times"` } func Default() Config { return Config{ ServiceName: "statistics-service", NodeID: "statistics-local", Environment: "local", HealthHTTPAddr: ":13110", QueryHTTPAddr: ":13010", MySQLDSN: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_statistics?parseTime=true&charset=utf8mb4&loc=UTC&multiStatements=true", ActivityMySQLDSN: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_activity?parseTime=true&charset=utf8mb4&loc=UTC", RocketMQ: defaultRocketMQConfig(), Log: logx.Config{ Level: "info", Format: "json", MaxPayloadBytes: 2048, }, } } func Load(path string) (Config, error) { cfg := Default() if path != "" { if err := configx.LoadYAML(path, &cfg); err != nil { return Config{}, err } } cfg.ServiceName = strings.TrimSpace(cfg.ServiceName) if cfg.ServiceName == "" { cfg.ServiceName = "statistics-service" } cfg.NodeID = strings.TrimSpace(cfg.NodeID) if cfg.NodeID == "" { cfg.NodeID = cfg.ServiceName } cfg.Environment = strings.TrimSpace(cfg.Environment) if cfg.Environment == "" { cfg.Environment = "local" } cfg.HealthHTTPAddr = strings.TrimSpace(cfg.HealthHTTPAddr) if cfg.HealthHTTPAddr == "" { cfg.HealthHTTPAddr = ":13110" } cfg.QueryHTTPAddr = strings.TrimSpace(cfg.QueryHTTPAddr) if cfg.QueryHTTPAddr == "" { cfg.QueryHTTPAddr = ":13010" } cfg.MySQLDSN = strings.TrimSpace(cfg.MySQLDSN) cfg.ActivityMySQLDSN = strings.TrimSpace(cfg.ActivityMySQLDSN) cfg.RocketMQ = normalizeRocketMQConfig(cfg.RocketMQ) if cfg.RocketMQ.Enabled { if len(cfg.RocketMQ.NameServers) == 0 && cfg.RocketMQ.NameServerDomain == "" { return Config{}, errors.New("rocketmq name_servers or name_server_domain is required") } if (cfg.RocketMQ.AccessKey == "") != (cfg.RocketMQ.SecretKey == "") { return Config{}, errors.New("rocketmq access_key and secret_key must be configured together") } } return cfg, nil } func defaultRocketMQConfig() RocketMQConfig { return RocketMQConfig{ Enabled: false, WalletOutbox: ConsumerMQConfig{Enabled: false, Topic: "hyapp_wallet_outbox", ConsumerGroup: "hyapp-statistics-wallet-outbox", ConsumerMaxReconsumeTimes: 16}, RoomOutbox: ConsumerMQConfig{Enabled: false, Topic: "hyapp_room_outbox", ConsumerGroup: "hyapp-statistics-room-outbox", ConsumerMaxReconsumeTimes: 16}, UserOutbox: ConsumerMQConfig{Enabled: false, Topic: "hyapp_user_outbox", ConsumerGroup: "hyapp-statistics-user-outbox", ConsumerMaxReconsumeTimes: 16}, GameOutbox: ConsumerMQConfig{Enabled: false, Topic: "hyapp_game_outbox", ConsumerGroup: "hyapp-statistics-game-outbox", ConsumerMaxReconsumeTimes: 16}, } } func normalizeRocketMQConfig(cfg RocketMQConfig) RocketMQConfig { defaults := defaultRocketMQConfig() cfg.NameServers = normalizeStringSlice(cfg.NameServers) cfg.NameServerDomain = strings.TrimSpace(cfg.NameServerDomain) cfg.AccessKey = strings.TrimSpace(cfg.AccessKey) cfg.SecretKey = strings.TrimSpace(cfg.SecretKey) cfg.SecurityToken = strings.TrimSpace(cfg.SecurityToken) cfg.Namespace = strings.TrimSpace(cfg.Namespace) cfg.WalletOutbox = normalizeConsumer(cfg.WalletOutbox, defaults.WalletOutbox) cfg.RoomOutbox = normalizeConsumer(cfg.RoomOutbox, defaults.RoomOutbox) cfg.UserOutbox = normalizeConsumer(cfg.UserOutbox, defaults.UserOutbox) cfg.GameOutbox = normalizeConsumer(cfg.GameOutbox, defaults.GameOutbox) if cfg.WalletOutbox.Enabled || cfg.RoomOutbox.Enabled || cfg.UserOutbox.Enabled || cfg.GameOutbox.Enabled { cfg.Enabled = true } return cfg } func normalizeConsumer(cfg ConsumerMQConfig, defaults ConsumerMQConfig) ConsumerMQConfig { if cfg.Topic = strings.TrimSpace(cfg.Topic); cfg.Topic == "" { cfg.Topic = defaults.Topic } if cfg.ConsumerGroup = strings.TrimSpace(cfg.ConsumerGroup); cfg.ConsumerGroup == "" { cfg.ConsumerGroup = defaults.ConsumerGroup } if cfg.ConsumerMaxReconsumeTimes <= 0 { cfg.ConsumerMaxReconsumeTimes = defaults.ConsumerMaxReconsumeTimes } return cfg } func normalizeStringSlice(values []string) []string { normalized := make([]string, 0, len(values)) for _, value := range values { if value = strings.TrimSpace(value); value != "" { normalized = append(normalized, value) } } return normalized }