package config import ( "flag" "fmt" "log" "os" "strconv" "time" ) // DatabaseConfig 数据库配置 type DatabaseConfig struct { Host, Password, DBName, SSLMode, TimeZone string Port int User string Schema string } // RedisConfig Redis 配置(与项目其他服务一致:host/port/db/password) type RedisConfig struct { Host string Port int DB int Password string } // URL 构造 redis:// URL(pkg/redis 客户端使用) func (r *RedisConfig) URL() string { auth := "" if r.Password != "" { auth = r.Password + "@" } return fmt.Sprintf("redis://%s%s:%d/%d", auth, r.Host, r.Port, r.DB) } // RefreshIntervals 物化视图/预聚表刷新间隔 type RefreshIntervals struct { DailyUserIncome time.Duration DailyExhibitionRevenue time.Duration DailyLikeIncome time.Duration AssetLevelDistribution time.Duration WeeklyUserIncome time.Duration UpcomingLevelUps time.Duration } // ChannelConfig 事件 channel 配置 type ChannelConfig struct { EventChannelCapacity int EventWorkerCount int EventBatchSize int EventBatchInterval time.Duration } // PartitionConfig 分区管理配置 type PartitionConfig struct { RetentionDays int PreCreateDays int } // ExtensionConfig 预留扩展开关 type ExtensionConfig struct { EnableOLAPDualWrite bool EnableRealtimeChannel bool EnableSDKEndpoint bool EnableSampling bool } var ( DBConfig = &DatabaseConfig{ Schema: "statistic", } RedisCfg = &RedisConfig{ Host: "localhost", Port: 6379, DB: 0, Password: "", } RefreshCfg = &RefreshIntervals{ DailyUserIncome: 5 * time.Minute, DailyExhibitionRevenue: 5 * time.Minute, DailyLikeIncome: 5 * time.Minute, AssetLevelDistribution: 15 * time.Minute, WeeklyUserIncome: 5 * time.Minute, UpcomingLevelUps: 15 * time.Minute, } ChannelCfg = &ChannelConfig{ EventChannelCapacity: 1000, EventWorkerCount: 1, EventBatchSize: 100, EventBatchInterval: 1 * time.Second, } PartitionCfg = &PartitionConfig{ RetentionDays: 30, PreCreateDays: 7, } ExtCfg = &ExtensionConfig{ // 全部默认 false(本期不启用) EnableOLAPDualWrite: false, EnableRealtimeChannel: false, EnableSDKEndpoint: false, EnableSampling: false, } ) func getEnv(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func getEnvInt(key string, fallback int) int { if v := os.Getenv(key); v != "" { if n, err := strconv.Atoi(v); err == nil { return n } } return fallback } func getEnvDuration(key string, fallback time.Duration) time.Duration { if v := os.Getenv(key); v != "" { if d, err := time.ParseDuration(v); err == nil { return d } } return fallback } func getEnvBool(key string, fallback bool) bool { if v := os.Getenv(key); v != "" { if b, err := strconv.ParseBool(v); err == nil { return b } } return fallback } // InitConfig 初始化所有配置(flag + 环境变量) func InitConfig() { flag.StringVar(&DBConfig.Host, "db-host", getEnv("STATISTIC_DB_HOST", "localhost"), "数据库主机") flag.IntVar(&DBConfig.Port, "db-port", getEnvInt("STATISTIC_DB_PORT", 5432), "数据库端口") flag.StringVar(&DBConfig.User, "db-user", getEnv("STATISTIC_DB_USER", "postgres"), "数据库用户") flag.StringVar(&DBConfig.Password, "db-password", getEnv("STATISTIC_DB_PASSWORD", ""), "数据库密码") flag.StringVar(&DBConfig.DBName, "db-name", getEnv("STATISTIC_DB_NAME", "topfans"), "数据库名") flag.StringVar(&DBConfig.SSLMode, "db-sslmode", "disable", "数据库 SSL 模式") flag.StringVar(&DBConfig.Schema, "db-schema", getEnv("STATISTIC_DB_SCHEMA", "statistic"), "数据库 schema") flag.StringVar(&RedisCfg.Host, "redis-host", getEnv("STATISTIC_REDIS_HOST", "localhost"), "Redis 主机") flag.IntVar(&RedisCfg.Port, "redis-port", getEnvInt("STATISTIC_REDIS_PORT", 6379), "Redis 端口") flag.IntVar(&RedisCfg.DB, "redis-db", getEnvInt("STATISTIC_REDIS_DB", 0), "Redis DB") flag.StringVar(&RedisCfg.Password, "redis-password", getEnv("STATISTIC_REDIS_PASSWORD", ""), "Redis 密码") flag.IntVar(&ChannelCfg.EventChannelCapacity, "event-channel-capacity", getEnvInt("STATISTIC_EVENT_CHANNEL_CAPACITY", 1000), "事件 channel 容量") flag.IntVar(&ChannelCfg.EventBatchSize, "event-batch-size", getEnvInt("STATISTIC_EVENT_BATCH_SIZE", 100), "事件批量大小") flag.DurationVar(&ChannelCfg.EventBatchInterval, "event-batch-interval", getEnvDuration("STATISTIC_EVENT_BATCH_INTERVAL", time.Second), "事件批量间隔") flag.IntVar(&PartitionCfg.RetentionDays, "partition-retention-days", getEnvInt("STATISTIC_PARTITION_RETENTION_DAYS", 30), "分区保留天数") flag.IntVar(&PartitionCfg.PreCreateDays, "partition-precreate-days", getEnvInt("STATISTIC_PARTITION_PRECREATE_DAYS", 7), "分区预创建天数") flag.BoolVar(&ExtCfg.EnableOLAPDualWrite, "enable-olap", getEnvBool("STATISTIC_ENABLE_OLAP_DUAL_WRITE", false), "启用 OLAP 双写") flag.BoolVar(&ExtCfg.EnableRealtimeChannel, "enable-realtime", getEnvBool("STATISTIC_ENABLE_REALTIME_CHANNEL", false), "启用实时通道") flag.BoolVar(&ExtCfg.EnableSDKEndpoint, "enable-sdk", getEnvBool("STATISTIC_ENABLE_SDK_ENDPOINT", false), "启用 SDK 端点") flag.BoolVar(&ExtCfg.EnableSampling, "enable-sampling", getEnvBool("STATISTIC_ENABLE_SAMPLING", false), "启用采样") flag.Parse() log.Println("statisticService 配置初始化完成") log.Printf(" 数据库: %s:%d/%s (schema=%s)", DBConfig.Host, DBConfig.Port, DBConfig.DBName, DBConfig.Schema) log.Printf(" Redis: %s", RedisCfg.URL()) log.Printf(" 事件 channel 容量: %d, 批量: %d/%v", ChannelCfg.EventChannelCapacity, ChannelCfg.EventBatchSize, ChannelCfg.EventBatchInterval) log.Printf(" 分区保留: %d 天, 预创建: %d 天", PartitionCfg.RetentionDays, PartitionCfg.PreCreateDays) log.Printf(" 扩展开关: OLAP=%v Realtime=%v SDK=%v Sampling=%v", ExtCfg.EnableOLAPDualWrite, ExtCfg.EnableRealtimeChannel, ExtCfg.EnableSDKEndpoint, ExtCfg.EnableSampling) }