diff --git a/.gitignore b/.gitignore index e8b0755..185a360 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ logs/ success_images/ build/ +config.json # Wails 自动生成的前端绑定 frontend/wailsjs/ diff --git a/app.go b/app.go index cdd0c25..689a71b 100644 --- a/app.go +++ b/app.go @@ -29,6 +29,7 @@ type App struct { trace *trace.Recorder paths types.AppPaths font *truetype.Font + cfg types.Config productsMu sync.RWMutex products []types.Product @@ -51,14 +52,21 @@ func (a *App) startup(ctx context.Context) { a.logStore = logger.NewMemoryLogStore(800) a.closeLog = logger.InitLogging(a.logStore, a.paths.LogDir) - tr, closeTrace := trace.InitTrace(a.paths.LogDir, config.LoadTraceConfig()) + // 加载配置并应用运行时设置 + a.cfg = config.LoadConfig(a.paths.ConfigFile) + logger.SetMinLevel(a.cfg.LogLevel) + + traceCfg := types.TraceConfig{ + Enabled: a.cfg.TraceEnabled, + MaxBodyBytes: a.cfg.TraceMaxBody, + } + tr, closeTrace := trace.InitTrace(a.paths.LogDir, traceCfg) a.trace = tr a.closeTrace = closeTrace - cfg := config.LoadConfig(a.paths.ConfigFile) - a.client = client.New(cfg.TimeAPIURL, a.trace) + a.client = client.New(a.cfg.TimeAPIURL, a.trace) a.font = imgpkg.LoadFont(a.paths.FontFiles) - a.scheduler = seckill.New(a.client, a.paths, a.font) + a.scheduler = seckill.New(a.client, a.paths, a.font, a.cfg.Concurrency, a.cfg.Attempts) a.scheduler.LoadAccounts() if err := os.MkdirAll(a.paths.SuccessDir, 0755); err != nil { @@ -202,6 +210,45 @@ func (a *App) QueryScore(accountName string) map[string]any { return map[string]any{"score": score} } +// GetVersion 返回应用版本号。 +func (a *App) GetVersion() string { + return constants.Version +} + +// GetSettings 返回当前配置。 +func (a *App) GetSettings() types.Config { + return a.cfg +} + +// SaveSettings 保存配置并运行时生效。 +func (a *App) SaveSettings(cfg types.Config) map[string]any { + // 校验 + 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 map[string]any{"success": false, "error": err.Error()} + } + + // 运行时生效 + a.cfg = cfg + logger.SetMinLevel(cfg.LogLevel) + a.scheduler.UpdateSettings(cfg.Concurrency, cfg.Attempts) + + logger.LogEvent("配置", "success", "配置已保存并生效: 日志等级=%s 并发=%d 重试=%d trace=%v", + cfg.LogLevel, cfg.Concurrency, cfg.Attempts, cfg.TraceEnabled) + return map[string]any{"success": true} +} + // ==================== 内部辅助方法 ==================== func (a *App) setProducts(products []types.Product, fetchedAt time.Time) { diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..6e11e0b --- /dev/null +++ b/build.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# 构建带版本号的产物 +# 用法: ./build.sh # 构建当前平台 +# GOOS=windows ./build.sh # 构建 Windows +# GOOS=linux ./build.sh # 构建 Linux +set -e + +VERSION=$(grep '"version"' wails.json | head -1 | sed 's/.*"version": *"//;s/".*//') +echo "版本: v${VERSION}" + +if [ "$GOOS" = "windows" ]; then + wails build -platform windows/amd64 -o "douyu_go_v${VERSION}.exe" +elif [ "$GOOS" = "linux" ]; then + wails build -platform linux/amd64 -o "douyu_go_v${VERSION}" +else + wails build +fi + +echo "=== 构建产物 ===" +ls -lh build/bin/ diff --git a/frontend/index.html b/frontend/index.html index f17ad66..82a933b 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -170,6 +170,7 @@ .log-e{color:var(--danger)} .log-i{color:var(--accent)} .log-w{color:var(--warning)} + .log-d{color:var(--muted-2)} .log-result{background:var(--panel-2);padding:5px 9px;margin:4px 0;border-radius:var(--radius-sm);word-break:break-all;border:1px solid var(--border)} .log-cat{display:inline-block;min-width:34px;text-align:center;margin-right:6px;padding:0 5px;border:1px solid var(--border);border-radius:5px;color:var(--muted);font-size:11px;line-height:1.4;background:var(--panel)} @@ -184,6 +185,35 @@ .toast-warn{border-left-color:var(--warning)}.toast-warn .toast-title{color:var(--warning)} .toast-error{border-left-color:var(--danger)}.toast-error .toast-title{color:var(--danger)} + /* ========== 设置弹窗 ========== */ + .modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:9000;display:none;align-items:center;justify-content:center} + .modal-overlay.show{display:flex} + .modal{background:var(--panel);border:1px solid var(--border);border-radius:12px;width:460px;max-width:92vw;max-height:85vh;overflow-y:auto;box-shadow:var(--shadow)} + .modal-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid var(--border)} + .modal-header h3{font-size:16px;color:var(--text-strong);margin:0} + .modal-close{width:30px;height:30px;border:0;border-radius:6px;background:transparent;color:var(--muted);cursor:pointer;font-size:18px;display:flex;align-items:center;justify-content:center} + .modal-close:hover{background:var(--panel-2);color:var(--text-strong)} + .modal-body{padding:20px} + .form-group{margin-bottom:16px} + .form-group:last-child{margin-bottom:0} + .form-label{display:block;font-size:13px;font-weight:700;color:var(--muted);margin-bottom:6px} + .form-hint{font-size:12px;color:var(--muted-2);margin-top:4px} + .form-row{display:flex;gap:12px} + .form-row .form-group{flex:1} + .form-input{width:100%;height:36px;padding:0 10px;background:var(--bg);color:var(--text-strong);border:1px solid var(--border);border-radius:var(--radius-sm);font-size:13px} + .form-input:focus{outline:0;border-color:var(--accent);box-shadow:var(--focus)} + .form-select{width:100%;height:36px;padding:0 10px;background:var(--bg);color:var(--text-strong);border:1px solid var(--border);border-radius:var(--radius-sm);font-size:13px;cursor:pointer} + .form-select:focus{outline:0;border-color:var(--accent)} + .toggle-row{display:flex;align-items:center;justify-content:space-between;padding:10px 0} + .toggle-label{font-size:13px;color:var(--text-strong)} + .toggle{position:relative;width:42px;height:24px;cursor:pointer} + .toggle input{opacity:0;width:0;height:0} + .toggle-slider{position:absolute;inset:0;background:var(--border-strong);border-radius:999px;transition:.2s} + .toggle-slider::before{content:"";position:absolute;left:3px;top:3px;width:18px;height:18px;background:#fff;border-radius:50%;transition:.2s} + .toggle input:checked+.toggle-slider{background:var(--accent)} + .toggle input:checked+.toggle-slider::before{transform:translateX(18px)} + .modal-footer{padding:12px 20px;border-top:1px solid var(--border);display:flex;justify-content:flex-end;gap:8px} + @media (max-width: 900px){ body{display:block;overflow:auto;height:auto;min-height:100vh} .left{width:100%;min-width:0;height:42vh;border-right:0;border-bottom:1px solid var(--border)} @@ -202,6 +232,58 @@
+ + +