v0.1.0: 新增CK管理功能

功能:
- 新增CK管理页面,支持复验抢购结果和整理账号文件
- 复验功能:查询斗鱼真实兑换记录,统计已抢到/未抢到的账号
- 整理CK功能:分离已抢到的账号到新文件,原文件只保留未抢到的
- 导出文件名优化:使用中文商品名+日期时间格式(如:user_已抢到_雪国2号_20260626_150405.txt)
- 复用左侧商品选择,统一数据源

接口:
- VerifyAndExport: 复验并统计
- ExportAccountsWithProduct: 导出账号(自动命名)
- OrganizeAccounts: 整理账号文件

版本: 0.1.0
This commit is contained in:
yml2213
2026-06-26 12:40:41 +08:00
parent 96b5afb756
commit 7f5da30698
7 changed files with 854 additions and 18 deletions
+245
View File
@@ -1,9 +1,13 @@
package main
import (
"bufio"
"context"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
@@ -286,6 +290,247 @@ func (a *App) VerifyAllExchange() map[string]any {
return results
}
// VerifyAndExport 复验并导出结果。
func (a *App) VerifyAndExport(commodityID string) map[string]any {
accounts := a.scheduler.AccountSnapshot()
results := make(map[string]any)
hitAccounts := make([]string, 0)
missAccounts := make([]string, 0)
for i, account := range accounts {
if i > 0 {
time.Sleep(200 * time.Millisecond)
}
if account.Cookie == "" {
missAccounts = append(missAccounts, account.Name)
results[account.Name] = map[string]any{
"success": false,
"error": "Cookie 为空",
}
continue
}
records, err := a.client.ExchangeList(account.Cookie)
if err != nil {
missAccounts = append(missAccounts, account.Name)
results[account.Name] = map[string]any{
"success": false,
"error": "查询失败: " + err.Error(),
}
continue
}
// 检查是否抢到了指定商品
hit := false
for _, r := range records {
if r.CommodityID == commodityID {
hit = true
break
}
}
if hit {
hitAccounts = append(hitAccounts, account.Name)
results[account.Name] = map[string]any{
"success": true,
"hit": true,
}
} else {
missAccounts = append(missAccounts, account.Name)
results[account.Name] = map[string]any{
"success": true,
"hit": false,
}
}
}
return map[string]any{
"success": true,
"hitCount": len(hitAccounts),
"missCount": len(missAccounts),
"hitAccounts": hitAccounts,
"missAccounts": missAccounts,
"details": results,
}
}
// ExportAccounts 导出指定账号到文件。
func (a *App) ExportAccounts(accountNames []string, filename string) map[string]any {
if len(accountNames) == 0 {
return map[string]any{"success": false, "error": "没有要导出的账号"}
}
// 读取原账号文件
accountFile := a.scheduler.AccountFilePath()
lines, err := os.ReadFile(accountFile)
if err != nil {
return map[string]any{"success": false, "error": "读取账号文件失败: " + err.Error()}
}
// 解析账号行
nameSet := make(map[string]bool)
for _, name := range accountNames {
nameSet[name] = true
}
var matchedLines []string
scanner := bufio.NewScanner(strings.NewReader(string(lines)))
for scanner.Scan() {
line := scanner.Text()
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
continue
}
parts := strings.SplitN(trimmed, "----", 3)
if len(parts) >= 1 {
name := strings.TrimSpace(parts[0])
if nameSet[name] {
matchedLines = append(matchedLines, line)
}
}
}
if len(matchedLines) == 0 {
return map[string]any{"success": false, "error": "没有找到匹配的账号行"}
}
// 生成导出文件路径
exportPath := filepath.Join(filepath.Dir(accountFile), filename)
content := strings.Join(matchedLines, "\n") + "\n"
if err := os.WriteFile(exportPath, []byte(content), 0644); err != nil {
return map[string]any{"success": false, "error": "写入文件失败: " + err.Error()}
}
logger.LogEvent("导出", "success", "已导出 %d 个账号到: %s", len(matchedLines), exportPath)
return map[string]any{
"success": true,
"count": len(matchedLines),
"filePath": exportPath,
}
}
// ExportAccountsWithProduct 导出指定账号到文件,自动使用中文商品名和时间戳命名。
func (a *App) ExportAccountsWithProduct(accountNames []string, commodityID, commodityName string) map[string]any {
// 生成文件名: user_已抢到_商品中文名_20260626_150405.txt
now := time.Now()
timestamp := now.Format("20060102_150405")
// 清理商品名中的特殊字符
safeName := strings.ReplaceAll(commodityName, "/", "_")
safeName = strings.ReplaceAll(safeName, "\\", "_")
safeName = strings.ReplaceAll(safeName, ":", "_")
safeName = strings.ReplaceAll(safeName, "*", "_")
safeName = strings.ReplaceAll(safeName, "?", "_")
safeName = strings.ReplaceAll(safeName, "\"", "_")
safeName = strings.ReplaceAll(safeName, "<", "_")
safeName = strings.ReplaceAll(safeName, ">", "_")
safeName = strings.ReplaceAll(safeName, "|", "_")
filename := fmt.Sprintf("user_已抢到_%s_%s.txt", safeName, timestamp)
return a.ExportAccounts(accountNames, filename)
}
// OrganizeAccounts 整理账号,把已抢到指定商品的账号分离出来。
func (a *App) OrganizeAccounts(commodityID, commodityName string, startTime, endTime int64) map[string]any {
accounts := a.scheduler.AccountSnapshot()
hitAccounts := make([]string, 0)
missAccounts := make([]string, 0)
for i, account := range accounts {
if i > 0 {
time.Sleep(200 * time.Millisecond)
}
if account.Cookie == "" {
missAccounts = append(missAccounts, account.Name)
continue
}
records, err := a.client.ExchangeList(account.Cookie)
if err != nil {
missAccounts = append(missAccounts, account.Name)
continue
}
// 检查是否在指定时间段内抢到了指定商品
hit := false
for _, r := range records {
if r.CommodityID == commodityID {
// 检查时间范围
recordTime, _ := strconv.ParseInt(r.Time, 10, 64)
if startTime == 0 || (recordTime >= startTime && recordTime <= endTime) {
hit = true
break
}
}
}
if hit {
hitAccounts = append(hitAccounts, account.Name)
} else {
missAccounts = append(missAccounts, account.Name)
}
}
// 导出已抢到的账号
var exportResult map[string]any
if len(hitAccounts) > 0 {
exportResult = a.ExportAccountsWithProduct(hitAccounts, commodityID, commodityName)
}
// 更新原文件,只保留未抢到的
accountFile := a.scheduler.AccountFilePath()
lines, err := os.ReadFile(accountFile)
if err != nil {
return map[string]any{"success": false, "error": "读取账号文件失败: " + err.Error()}
}
missSet := make(map[string]bool)
for _, name := range missAccounts {
missSet[name] = true
}
var keepLines []string
scanner := bufio.NewScanner(strings.NewReader(string(lines)))
for scanner.Scan() {
line := scanner.Text()
trimmed := strings.TrimSpace(line)
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
keepLines = append(keepLines, line)
continue
}
parts := strings.SplitN(trimmed, "----", 3)
if len(parts) >= 1 {
name := strings.TrimSpace(parts[0])
if missSet[name] {
keepLines = append(keepLines, line)
}
}
}
// 写回原文件
newContent := strings.Join(keepLines, "\n") + "\n"
if err := os.WriteFile(accountFile, []byte(newContent), 0644); err != nil {
return map[string]any{"success": false, "error": "更新账号文件失败: " + err.Error()}
}
// 重新加载账号
a.scheduler.LoadAccounts()
logger.LogEvent("整理", "success", "整理完成: 已抢到=%d, 未抢到=%d", len(hitAccounts), len(missAccounts))
return map[string]any{
"success": true,
"hitCount": len(hitAccounts),
"missCount": len(missAccounts),
"hitAccounts": hitAccounts,
"missAccounts": missAccounts,
"exportResult": exportResult,
}
}
// GetVersion 返回应用版本号。
func (a *App) GetVersion() string {
return constants.Version