根目录原 7 个 app*.go 平铺,现收进 internal/app/ 子包: - package main → package app - NewApp → New(导出) - startup/shutdown → Startup/Shutdown(导出,供 main.go 引用) - main.go 改为薄入口,仅导入 internal/app 并装配 Wails 前端同步更新(Wails 绑定路径 go/main → go/app): - window.go.main.App → window.go.app.App(10 处) - env.d.ts 类型声明路径 wailsjs/go/main/App → wailsjs/go/app/App 根目录 Go 文件从 7 个减至 1 个(main.go),元文件更清晰
154 lines
4.0 KiB
Go
154 lines
4.0 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/golang/freetype/truetype"
|
|
|
|
"douyu_go/internal/activity"
|
|
"douyu_go/internal/client"
|
|
"douyu_go/internal/config"
|
|
"douyu_go/internal/constants"
|
|
imgpkg "douyu_go/internal/image"
|
|
"douyu_go/internal/logger"
|
|
"douyu_go/internal/seckill"
|
|
"douyu_go/internal/trace"
|
|
"douyu_go/internal/types"
|
|
)
|
|
|
|
// App 应用主结构体,所有导出方法可被前端通过 Wails 绑定调用。
|
|
// 绑定方法按领域拆分到 app_*.go 文件。
|
|
type App struct {
|
|
ctx context.Context
|
|
|
|
clientMu sync.RWMutex
|
|
client *client.DouyuClient
|
|
scheduler *seckill.Scheduler
|
|
logStore *logger.MemoryLogStore
|
|
trace *trace.Recorder
|
|
paths types.AppPaths
|
|
font *truetype.Font
|
|
|
|
cfgMu sync.RWMutex
|
|
cfg types.Config
|
|
|
|
productsMu sync.RWMutex
|
|
products []types.Product
|
|
productsAt time.Time
|
|
|
|
closeLog func()
|
|
closeTrace func()
|
|
}
|
|
|
|
// getClient 返回当前 client 指针快照(读锁保护)。
|
|
func (a *App) getClient() *client.DouyuClient {
|
|
a.clientMu.RLock()
|
|
defer a.clientMu.RUnlock()
|
|
return a.client
|
|
}
|
|
|
|
// getCfg 返回当前配置的深拷贝(读锁保护)。
|
|
func (a *App) getCfg() types.Config {
|
|
a.cfgMu.RLock()
|
|
defer a.cfgMu.RUnlock()
|
|
return cloneConfig(a.cfg)
|
|
}
|
|
|
|
// setClient 替换 client(写锁保护)。
|
|
func (a *App) setClient(c *client.DouyuClient) {
|
|
a.clientMu.Lock()
|
|
a.client = c
|
|
a.clientMu.Unlock()
|
|
}
|
|
|
|
// setCfg 替换配置(写锁保护)。
|
|
func (a *App) setCfg(c types.Config) {
|
|
a.cfgMu.Lock()
|
|
a.cfg = c
|
|
a.cfgMu.Unlock()
|
|
}
|
|
|
|
// cloneConfig 深拷贝配置,避免 RetryStages 切片共享引用。
|
|
func cloneConfig(c types.Config) types.Config {
|
|
cp := c
|
|
if c.RetryStages != nil {
|
|
cp.RetryStages = make([]types.RetryStage, len(c.RetryStages))
|
|
copy(cp.RetryStages, c.RetryStages)
|
|
}
|
|
return cp
|
|
}
|
|
|
|
// New 创建 App 实例。
|
|
func New() *App {
|
|
return &App{}
|
|
}
|
|
|
|
// Startup Wails 应用启动时调用。
|
|
func (a *App) Startup(ctx context.Context) {
|
|
a.ctx = ctx
|
|
|
|
a.paths = config.DefaultAppPaths()
|
|
a.logStore = logger.NewMemoryLogStore(800)
|
|
a.closeLog = logger.InitLogging(a.logStore, a.paths.LogDir)
|
|
|
|
// 加载配置并应用运行时设置
|
|
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
|
|
|
|
// 解析当前活动:配置中的 activity ID 决定使用哪套接口参数
|
|
act := activity.Get(a.cfg.Activity)
|
|
if act == nil {
|
|
act = activity.Default()
|
|
logger.LogEvent("活动", "warn", "配置中的活动 %q 未识别,已回退到默认活动 %s", a.cfg.Activity, act.DisplayName)
|
|
}
|
|
a.client = client.New(act, a.cfg.TimeAPIURL, a.trace)
|
|
a.font = imgpkg.LoadFont(a.paths.FontFiles)
|
|
a.scheduler = seckill.New(a.client, a.paths, a.font, a.cfg.Concurrency, a.cfg.Attempts, a.cfg.RetryStages, a.cfg.SeckillMode)
|
|
a.scheduler.LoadAccounts()
|
|
|
|
if err := os.MkdirAll(a.paths.SuccessDir, 0755); err != nil {
|
|
logger.LogEvent("图片", "warn", "成功图片目录创建失败,图片保存功能可能不可用: %v", err)
|
|
}
|
|
|
|
if sample, err := a.client.ServerTimeSample(); err == nil {
|
|
logger.SetDouyuOffset(sample.Offset)
|
|
logger.LogEvent("时间", "success", "服务端时间获取成功: %s", sample.EstimatedServerNow().In(constants.CST).Format("2006-01-02 15:04:05.000"))
|
|
} else {
|
|
logger.LogEvent("时间", "error", "服务端时间获取失败: %v", err)
|
|
}
|
|
|
|
logger.LogEvent("系统", "success", "应用启动完成 | 账号: %d", a.scheduler.AccountCount())
|
|
}
|
|
|
|
// Shutdown Wails 应用关闭时调用。
|
|
func (a *App) Shutdown(ctx context.Context) {
|
|
if a.scheduler != nil {
|
|
a.scheduler.Close()
|
|
}
|
|
if a.closeTrace != nil {
|
|
a.closeTrace()
|
|
}
|
|
if a.closeLog != nil {
|
|
a.closeLog()
|
|
}
|
|
}
|
|
|
|
// GetVersion 返回应用版本号。
|
|
func (a *App) GetVersion() string {
|
|
if constants.BuildTime != "unknown" {
|
|
return constants.Version + " (" + constants.BuildTime + ")"
|
|
}
|
|
return constants.Version
|
|
}
|