Files
dygou_go/internal/app/app_verify.go
T
yml2213 2c88edb741 重构: 应用代码移入 internal/app 包,根目录只留入口
根目录原 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),元文件更清晰
2026-07-29 13:27:08 +08:00

226 lines
6.0 KiB
Go

package app
import (
"fmt"
"strconv"
"strings"
"sync"
"time"
"douyu_go/internal/logger"
"douyu_go/internal/types"
)
const exchangeLookupConcurrency = 8
type exchangeLookupResult struct {
Account types.Account
Records []types.ExchangeRecord
Err error
EmptyCookie bool
}
// QueryScore 查询单个账号的积分。
func (a *App) QueryScore(accountName string) types.ScoreResult {
if accountName == "" {
return types.ScoreResult{Score: "缺少账号名"}
}
cookie, ok := a.scheduler.AccountCookieByName(accountName)
if !ok || cookie == "" {
return types.ScoreResult{Score: fmt.Sprintf("账号不存在或 Cookie 为空: %s", accountName)}
}
score, err := a.getClient().Score(cookie)
if err != nil {
return types.ScoreResult{Score: "查询失败: " + err.Error()}
}
return types.ScoreResult{Score: strconv.Itoa(score)}
}
// VerifyExchange 查询兑换记录,用于复验抢购结果。
func (a *App) VerifyExchange(accountName string) types.ExchangeRecordsResult {
if accountName == "" {
return types.ExchangeRecordsResult{Error: "缺少账号名"}
}
cookie, ok := a.scheduler.AccountCookieByName(accountName)
if !ok || cookie == "" {
return types.ExchangeRecordsResult{Error: fmt.Sprintf("账号不存在或 Cookie 为空: %s", accountName)}
}
records, err := a.getClient().ExchangeList(cookie)
if err != nil {
return types.ExchangeRecordsResult{Error: "查询失败: " + err.Error()}
}
matched := filterExchangeRecords(records)
return types.ExchangeRecordsResult{
Success: true,
Records: matched,
Count: len(matched),
}
}
// VerifyAllExchange 批量查询兑换记录,使用有限并发减少等待时间。
func (a *App) VerifyAllExchange() types.VerifyAllExchangeResult {
accounts := a.scheduler.AccountSnapshot()
results := make(map[string]types.AccountExchangeResult)
lookups := a.lookupExchangeRecords("复验", accounts)
for _, lookup := range lookups {
if lookup.EmptyCookie {
results[lookup.Account.Name] = types.AccountExchangeResult{Error: "Cookie 为空"}
continue
}
if lookup.Err != nil {
results[lookup.Account.Name] = types.AccountExchangeResult{Error: "查询失败: " + lookup.Err.Error()}
continue
}
matched := filterExchangeRecords(lookup.Records)
results[lookup.Account.Name] = types.AccountExchangeResult{
Success: true,
Records: matched,
Count: len(matched),
}
}
return types.VerifyAllExchangeResult{Results: results}
}
// VerifyAndExport 复验并导出结果。
func (a *App) VerifyAndExport(commodityID string) types.VerifyAndExportResult {
accounts := a.scheduler.AccountSnapshot()
results := make(map[string]types.AccountHitResult)
hitAccounts := make([]string, 0)
missAccounts := make([]string, 0)
lookups := a.lookupExchangeRecords("复验", accounts)
for _, lookup := range lookups {
if lookup.EmptyCookie {
missAccounts = append(missAccounts, lookup.Account.Name)
results[lookup.Account.Name] = types.AccountHitResult{Error: "Cookie 为空"}
continue
}
if lookup.Err != nil {
missAccounts = append(missAccounts, lookup.Account.Name)
results[lookup.Account.Name] = types.AccountHitResult{Error: "查询失败: " + lookup.Err.Error()}
continue
}
// 检查是否抢到了指定商品
hit := hasCommodityRecord(lookup.Records, commodityID, 0, 0)
if hit {
hitAccounts = append(hitAccounts, lookup.Account.Name)
results[lookup.Account.Name] = types.AccountHitResult{Success: true, Hit: true}
} else {
missAccounts = append(missAccounts, lookup.Account.Name)
results[lookup.Account.Name] = types.AccountHitResult{Success: true}
}
}
return types.VerifyAndExportResult{
Success: true,
HitCount: len(hitAccounts),
MissCount: len(missAccounts),
HitAccounts: hitAccounts,
MissAccounts: missAccounts,
Details: results,
}
}
func (a *App) lookupExchangeRecords(category string, accounts []types.Account) []exchangeLookupResult {
results := make([]exchangeLookupResult, len(accounts))
if len(accounts) == 0 {
return results
}
workerCount := exchangeLookupConcurrency
if len(accounts) < workerCount {
workerCount = len(accounts)
}
start := time.Now()
logger.LogEvent(category, "info", "兑换记录批量查询开始: 账号=%d 并发=%d", len(accounts), workerCount)
jobs := make(chan int)
var wg sync.WaitGroup
for worker := 0; worker < workerCount; worker++ {
wg.Add(1)
go func() {
defer wg.Done()
for idx := range jobs {
account := accounts[idx]
lookup := exchangeLookupResult{Account: account}
if strings.TrimSpace(account.Cookie) == "" {
lookup.EmptyCookie = true
results[idx] = lookup
continue
}
records, err := a.getClient().ExchangeList(account.Cookie)
lookup.Records = records
lookup.Err = err
results[idx] = lookup
}
}()
}
for idx := range accounts {
jobs <- idx
}
close(jobs)
wg.Wait()
errors := 0
empty := 0
for _, result := range results {
if result.EmptyCookie {
empty++
}
if result.Err != nil {
errors++
}
}
level := "success"
if errors > 0 {
level = "warn"
}
logger.LogEvent(category, level, "兑换记录批量查询完成: 账号=%d 空CK=%d 失败=%d 耗时=%s",
len(accounts), empty, errors, time.Since(start).Round(time.Millisecond))
return results
}
func filterExchangeRecords(records []types.ExchangeRecord) []types.ExchangeRecord {
matched := make([]types.ExchangeRecord, 0, len(records))
for _, r := range records {
if r.CommodityID != "" {
matched = append(matched, r)
}
}
return matched
}
func hasCommodityRecord(records []types.ExchangeRecord, commodityID string, startTime, endTime int64) bool {
if commodityID == "" {
return false
}
for _, r := range records {
if r.CommodityID != commodityID {
continue
}
if startTime == 0 && endTime == 0 {
return true
}
recordTime, err := strconv.ParseInt(r.Time, 10, 64)
if err != nil {
continue
}
if startTime > 0 && recordTime < startTime {
continue
}
if endTime > 0 && recordTime > endTime {
continue
}
return true
}
return false
}