package app import ( "douyu_go/internal/activity" "douyu_go/internal/client" "douyu_go/internal/config" "douyu_go/internal/constants" "douyu_go/internal/logger" "douyu_go/internal/types" ) // GetActivities 返回所有已注册的手册活动,供前端下拉选择。 func (a *App) GetActivities() []types.ActivityInfo { list := activity.List() out := make([]types.ActivityInfo, 0, len(list)) for _, act := range list { out = append(out, types.ActivityInfo{ID: act.ID, DisplayName: act.DisplayName}) } return out } // GetCurrentActivity 返回当前生效的活动。 func (a *App) GetCurrentActivity() types.ActivityInfo { cli := a.getClient() act := cli.Activity return types.ActivityInfo{ID: act.ID, DisplayName: act.DisplayName} } // SwitchActivity 切换当前活动并持久化到配置;切换后重建客户端与时间基准。 func (a *App) SwitchActivity(id string) types.SwitchActivityResult { act := activity.Get(id) if act == nil { return types.SwitchActivityResult{Error: "未知活动: " + id} } cfg := a.getCfg() cfg.Activity = act.ID // 仅当用户未自定义时间校准接口时才回退默认值,避免切换活动静默覆盖用户在设置里填的 timeApiUrl。 if cfg.TimeAPIURL == "" { cfg.TimeAPIURL = constants.DefaultTimeAPI } if err := config.SaveConfig(a.paths.ConfigFile, cfg); err != nil { logger.LogEvent("活动", "error", "切换活动配置保存失败: %v", err) return types.SwitchActivityResult{Error: "配置保存失败: " + err.Error()} } a.setCfg(cfg) newClient := client.New(act, cfg.TimeAPIURL, a.getTrace()) a.setClient(newClient) if !a.scheduler.UpdateClient(newClient) { logger.LogEvent("活动", "warn", "抢购任务正在运行,调度器客户端将在下次抢购时生效") } if sample, err := newClient.ServerTimeSample(); err == nil { logger.SetDouyuOffset(sample.Offset) } logger.LogEvent("活动", "success", "已切换到活动: %s", act.DisplayName) return types.SwitchActivityResult{Success: true, Activity: act.ID, DisplayName: act.DisplayName} } // GetSettings 返回当前配置。 func (a *App) GetSettings() types.Config { return a.getCfg() } // SaveSettings 保存配置并运行时生效。 func (a *App) SaveSettings(cfg types.Config) types.SimpleResult { oldCfg := a.getCfg() if cfg.Activity == "" { cfg.Activity = oldCfg.Activity } act := activity.Get(cfg.Activity) if act == nil { unknownActivity := cfg.Activity act = activity.Default() cfg.Activity = act.ID logger.LogEvent("配置", "warn", "保存设置时活动 %q 未识别,已回退到默认活动 %s", unknownActivity, act.DisplayName) } if cfg.TimeAPIURL == "" { cfg.TimeAPIURL = constants.DefaultTimeAPI } if cfg.TraceMaxBody < 0 { cfg.TraceMaxBody = 0 } if cfg.XiaodianPageSize <= 0 || cfg.XiaodianPageSize > 200 { cfg.XiaodianPageSize = 200 } // 校验 if cfg.Concurrency <= 0 { cfg.Concurrency = constants.DefaultConcurrency } if cfg.Attempts <= 0 { cfg.Attempts = constants.DefaultAttempts } if cfg.LogLevel == "" { cfg.LogLevel = "info" } // 写入文件 if err := config.SaveConfig(a.paths.ConfigFile, cfg); err != nil { logger.LogEvent("配置", "error", "配置保存失败: %v", err) return types.SimpleResult{} } traceChanged := cfg.TraceEnabled != oldCfg.TraceEnabled || cfg.TraceMaxBody != oldCfg.TraceMaxBody clientChanged := traceChanged || cfg.TimeAPIURL != oldCfg.TimeAPIURL || cfg.Activity != oldCfg.Activity if traceChanged { a.replaceTrace(cfg) } // 运行时生效 a.setCfg(cfg) logger.SetMinLevel(cfg.LogLevel) a.scheduler.UpdateSettings(cfg.Concurrency, cfg.Attempts, cfg.RetryStages, cfg.SeckillMode) if clientChanged { newClient := client.New(act, cfg.TimeAPIURL, a.getTrace()) a.setClient(newClient) if !a.scheduler.UpdateClient(newClient) { logger.LogEvent("配置", "warn", "抢购任务正在运行,客户端配置将在下次抢购时完整生效") } if sample, err := newClient.ServerTimeSample(); err == nil { logger.SetDouyuOffset(sample.Offset) } else { logger.LogEvent("时间", "warn", "保存配置后服务端时间校准失败: %v", err) } } logger.LogEvent("配置", "success", "配置已保存并生效: 日志等级=%s 并发=%d 重试=%d 重试阶段=%d 模式=%d trace=%v", cfg.LogLevel, cfg.Concurrency, cfg.Attempts, len(cfg.RetryStages), cfg.SeckillMode, cfg.TraceEnabled) return types.SimpleResult{Success: true} }