371 lines
10 KiB
Go
371 lines
10 KiB
Go
// Package config defines cron-service runtime configuration.
|
||
package config
|
||
|
||
import (
|
||
"strings"
|
||
|
||
"hyapp/pkg/appcode"
|
||
"hyapp/pkg/configx"
|
||
"hyapp/pkg/logx"
|
||
)
|
||
|
||
const defaultAppCode = "lalu"
|
||
|
||
// Config describes cron-service startup and schedule settings.
|
||
type Config struct {
|
||
// ServiceName is written into logs and health metadata.
|
||
ServiceName string `yaml:"service_name"`
|
||
// NodeID identifies this scheduler instance in run history and task leases.
|
||
NodeID string `yaml:"node_id"`
|
||
// Environment is the runtime environment tag used by structured logs.
|
||
Environment string `yaml:"environment"`
|
||
// GRPCAddr exposes gRPC health and future internal management APIs.
|
||
GRPCAddr string `yaml:"grpc_addr"`
|
||
// HealthHTTPAddr exposes HTTP health for CLB and deploy scripts only.
|
||
HealthHTTPAddr string `yaml:"health_http_addr"`
|
||
// MySQLDSN stores cron run history and schedule-level leases only.
|
||
MySQLDSN string `yaml:"mysql_dsn"`
|
||
// UserServiceAddr is the owner endpoint for user/auth/host batch tasks.
|
||
UserServiceAddr string `yaml:"user_service_addr"`
|
||
// ActivityServiceAddr is the owner endpoint for activity/message batch tasks.
|
||
ActivityServiceAddr string `yaml:"activity_service_addr"`
|
||
// GameServiceAddr is the owner endpoint for game/order relay batch tasks.
|
||
GameServiceAddr string `yaml:"game_service_addr"`
|
||
// WalletServiceAddr is the owner endpoint for wallet/VIP/settlement tasks.
|
||
WalletServiceAddr string `yaml:"wallet_service_addr"`
|
||
// Tasks contains per-task schedule knobs. Disabled tasks are documented but not started.
|
||
Tasks map[string]TaskConfig `yaml:"tasks"`
|
||
// Log controls stdout structured logging.
|
||
Log logx.Config `yaml:"log"`
|
||
}
|
||
|
||
// TaskConfig keeps scheduler knobs as strings so YAML stays easy to read.
|
||
type TaskConfig struct {
|
||
// Enabled controls whether cron-service starts a loop for this task.
|
||
Enabled bool `yaml:"enabled"`
|
||
// Interval is the delay between successful schedule attempts.
|
||
Interval string `yaml:"interval"`
|
||
// Timeout bounds the owner service RPC call for one batch.
|
||
Timeout string `yaml:"timeout"`
|
||
// LockTTL is the owner service task claim lock budget.
|
||
LockTTL string `yaml:"lock_ttl"`
|
||
// BatchSize is passed to owner service batch RPCs.
|
||
BatchSize int `yaml:"batch_size"`
|
||
// AppCodes scopes the task by tenant; every app_code gets an independent run.
|
||
AppCodes []string `yaml:"app_codes"`
|
||
// PendingPublishMaxAge is used by mic_open_session_compensation for pending_publish sessions.
|
||
PendingPublishMaxAge string `yaml:"pending_publish_max_age"`
|
||
// PublishingSessionMaxAge is used by mic_open_session_compensation for publishing sessions.
|
||
PublishingSessionMaxAge string `yaml:"publishing_session_max_age"`
|
||
// OpenSessionMaxAge is used by room_open_session_compensation for open in-room sessions.
|
||
OpenSessionMaxAge string `yaml:"open_session_max_age"`
|
||
}
|
||
|
||
// Default returns local development defaults that follow the 13xxx port rule.
|
||
func Default() Config {
|
||
return Config{
|
||
ServiceName: "cron-service",
|
||
NodeID: "cron-local",
|
||
Environment: "local",
|
||
GRPCAddr: ":13007",
|
||
HealthHTTPAddr: ":13107",
|
||
MySQLDSN: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_cron?parseTime=true&charset=utf8mb4&loc=UTC",
|
||
UserServiceAddr: "127.0.0.1:13005",
|
||
ActivityServiceAddr: "127.0.0.1:13006",
|
||
GameServiceAddr: "127.0.0.1:13008",
|
||
WalletServiceAddr: "127.0.0.1:13004",
|
||
Tasks: defaultTasks(),
|
||
Log: logx.Config{
|
||
Level: "info",
|
||
Format: "json",
|
||
MaxPayloadBytes: 2048,
|
||
},
|
||
}
|
||
}
|
||
|
||
// Load reads a YAML file into Config and normalizes empty fields.
|
||
func Load(path string) (Config, error) {
|
||
cfg := Default()
|
||
if path == "" {
|
||
return cfg, nil
|
||
}
|
||
if err := configx.LoadYAML(path, &cfg); err != nil {
|
||
return Config{}, err
|
||
}
|
||
normalize(&cfg)
|
||
return cfg, nil
|
||
}
|
||
|
||
func normalize(cfg *Config) {
|
||
cfg.ServiceName = strings.TrimSpace(cfg.ServiceName)
|
||
if cfg.ServiceName == "" {
|
||
cfg.ServiceName = "cron-service"
|
||
}
|
||
cfg.NodeID = strings.TrimSpace(cfg.NodeID)
|
||
if cfg.NodeID == "" {
|
||
cfg.NodeID = cfg.ServiceName
|
||
}
|
||
cfg.Environment = strings.ToLower(strings.TrimSpace(cfg.Environment))
|
||
if cfg.Environment == "" {
|
||
cfg.Environment = "local"
|
||
}
|
||
cfg.GRPCAddr = strings.TrimSpace(cfg.GRPCAddr)
|
||
if cfg.GRPCAddr == "" {
|
||
cfg.GRPCAddr = ":13007"
|
||
}
|
||
cfg.HealthHTTPAddr = strings.TrimSpace(cfg.HealthHTTPAddr)
|
||
if cfg.HealthHTTPAddr == "" {
|
||
cfg.HealthHTTPAddr = ":13107"
|
||
}
|
||
cfg.GameServiceAddr = strings.TrimSpace(cfg.GameServiceAddr)
|
||
if cfg.GameServiceAddr == "" {
|
||
cfg.GameServiceAddr = "127.0.0.1:13008"
|
||
}
|
||
cfg.Tasks = normalizeTasks(cfg.Tasks)
|
||
}
|
||
|
||
func defaultTasks() map[string]TaskConfig {
|
||
return map[string]TaskConfig{
|
||
"login_ip_risk": {
|
||
Enabled: true,
|
||
Interval: "5s",
|
||
Timeout: "3s",
|
||
LockTTL: "30s",
|
||
BatchSize: 100,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"user_region_rebuild": {
|
||
Enabled: true,
|
||
Interval: "5s",
|
||
Timeout: "5s",
|
||
LockTTL: "30s",
|
||
BatchSize: 500,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"message_fanout": {
|
||
Enabled: true,
|
||
Interval: "1s",
|
||
Timeout: "10s",
|
||
LockTTL: "30s",
|
||
BatchSize: 500,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"vip_daily_coin_rebate": {
|
||
// scheduler 启动即执行,因此不能用 24h 间隔假装 UTC 0 点;每分钟触发一次,
|
||
// wallet owner 以 UTC task_day 的 completed run 快速 no-op,并在跨日后创建新批次。
|
||
Enabled: true,
|
||
Interval: "1m",
|
||
Timeout: "30s",
|
||
LockTTL: "1m",
|
||
BatchSize: 500,
|
||
AppCodes: []string{"lalu", "fami"},
|
||
},
|
||
"growth_level_reward": {
|
||
Enabled: true,
|
||
Interval: "5s",
|
||
Timeout: "10s",
|
||
LockTTL: "30s",
|
||
BatchSize: 100,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"temporary_growth_level": {
|
||
Enabled: true,
|
||
Interval: "5s",
|
||
Timeout: "15s",
|
||
LockTTL: "30s",
|
||
BatchSize: 100,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"achievement_reward": {
|
||
Enabled: true,
|
||
Interval: "5s",
|
||
Timeout: "10s",
|
||
LockTTL: "30s",
|
||
BatchSize: 100,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"weekly_star_settlement": {
|
||
Enabled: true,
|
||
Interval: "5s",
|
||
Timeout: "20s",
|
||
LockTTL: "1m",
|
||
BatchSize: 50,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"agency_opening_settlement": {
|
||
Enabled: true,
|
||
Interval: "5s",
|
||
Timeout: "20s",
|
||
LockTTL: "1m",
|
||
BatchSize: 50,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"cp_weekly_rank_settlement": {
|
||
Enabled: true,
|
||
Interval: "5m",
|
||
Timeout: "30s",
|
||
LockTTL: "5m",
|
||
BatchSize: 50,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"room_turnover_reward_settlement": {
|
||
Enabled: true,
|
||
Interval: "5m",
|
||
Timeout: "30s",
|
||
LockTTL: "5m",
|
||
BatchSize: 100,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"cp_intimacy_leaderboard": {
|
||
Enabled: true,
|
||
Interval: "5m",
|
||
Timeout: "20s",
|
||
LockTTL: "5m",
|
||
BatchSize: 10000,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"game_level_event_relay": {
|
||
Enabled: true,
|
||
Interval: "5s",
|
||
Timeout: "10s",
|
||
LockTTL: "30s",
|
||
BatchSize: 100,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"host_salary_daily_settlement": {
|
||
Enabled: true,
|
||
Interval: "24h",
|
||
Timeout: "30s",
|
||
LockTTL: "2m",
|
||
BatchSize: 200,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"host_salary_half_month_settlement": {
|
||
Enabled: true,
|
||
Interval: "24h",
|
||
Timeout: "30s",
|
||
LockTTL: "2m",
|
||
BatchSize: 200,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"host_salary_month_end": {
|
||
Enabled: true,
|
||
Interval: "24h",
|
||
Timeout: "60s",
|
||
LockTTL: "5m",
|
||
BatchSize: 200,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"mic_open_session_compensation": {
|
||
Enabled: true,
|
||
Interval: "60s",
|
||
Timeout: "10s",
|
||
LockTTL: "60s",
|
||
BatchSize: 100,
|
||
AppCodes: []string{defaultAppCode},
|
||
PendingPublishMaxAge: "2m",
|
||
PublishingSessionMaxAge: "12h",
|
||
},
|
||
"room_open_session_compensation": {
|
||
Enabled: true,
|
||
Interval: "60s",
|
||
Timeout: "10s",
|
||
LockTTL: "60s",
|
||
BatchSize: 200,
|
||
AppCodes: []string{defaultAppCode},
|
||
// stale worker 2 分钟兜底断线离房;24h 只兜 room-service 节点崩溃或 outbox 丢失的极端场景。
|
||
OpenSessionMaxAge: "24h",
|
||
},
|
||
"manager_user_block_expiry": {
|
||
Enabled: true,
|
||
Interval: "30s",
|
||
Timeout: "10s",
|
||
LockTTL: "30s",
|
||
BatchSize: 100,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
"admin_user_ban_expiry": {
|
||
Enabled: true,
|
||
Interval: "30s",
|
||
Timeout: "10s",
|
||
LockTTL: "30s",
|
||
BatchSize: 100,
|
||
AppCodes: []string{defaultAppCode},
|
||
},
|
||
}
|
||
}
|
||
|
||
func normalizeTasks(tasks map[string]TaskConfig) map[string]TaskConfig {
|
||
defaults := defaultTasks()
|
||
if tasks == nil {
|
||
tasks = map[string]TaskConfig{}
|
||
}
|
||
for name, def := range defaults {
|
||
current, ok := tasks[name]
|
||
if !ok {
|
||
tasks[name] = def
|
||
continue
|
||
}
|
||
tasks[name] = mergeTaskConfig(def, current)
|
||
}
|
||
for name, task := range tasks {
|
||
tasks[name] = mergeTaskConfig(TaskConfig{
|
||
Interval: "5s",
|
||
Timeout: "5s",
|
||
LockTTL: "30s",
|
||
BatchSize: 100,
|
||
AppCodes: []string{defaultAppCode},
|
||
}, task)
|
||
}
|
||
return tasks
|
||
}
|
||
|
||
func mergeTaskConfig(def TaskConfig, current TaskConfig) TaskConfig {
|
||
current.Interval = strings.TrimSpace(current.Interval)
|
||
if current.Interval == "" {
|
||
current.Interval = def.Interval
|
||
}
|
||
current.Timeout = strings.TrimSpace(current.Timeout)
|
||
if current.Timeout == "" {
|
||
current.Timeout = def.Timeout
|
||
}
|
||
current.LockTTL = strings.TrimSpace(current.LockTTL)
|
||
if current.LockTTL == "" {
|
||
current.LockTTL = def.LockTTL
|
||
}
|
||
current.PendingPublishMaxAge = strings.TrimSpace(current.PendingPublishMaxAge)
|
||
if current.PendingPublishMaxAge == "" {
|
||
current.PendingPublishMaxAge = def.PendingPublishMaxAge
|
||
}
|
||
current.PublishingSessionMaxAge = strings.TrimSpace(current.PublishingSessionMaxAge)
|
||
if current.PublishingSessionMaxAge == "" {
|
||
current.PublishingSessionMaxAge = def.PublishingSessionMaxAge
|
||
}
|
||
current.OpenSessionMaxAge = strings.TrimSpace(current.OpenSessionMaxAge)
|
||
if current.OpenSessionMaxAge == "" {
|
||
current.OpenSessionMaxAge = def.OpenSessionMaxAge
|
||
}
|
||
if current.BatchSize <= 0 {
|
||
current.BatchSize = def.BatchSize
|
||
}
|
||
current.AppCodes = normalizeAppCodes(current.AppCodes)
|
||
if len(current.AppCodes) == 0 {
|
||
current.AppCodes = def.AppCodes
|
||
}
|
||
return current
|
||
}
|
||
|
||
func normalizeAppCodes(values []string) []string {
|
||
seen := map[string]bool{}
|
||
result := make([]string, 0, len(values))
|
||
for _, value := range values {
|
||
code := appcode.Normalize(value)
|
||
if code == "" || seen[code] {
|
||
continue
|
||
}
|
||
seen[code] = true
|
||
result = append(result, code)
|
||
}
|
||
return result
|
||
}
|