From 8cf29598a58afeb24d8799d0e0c19fd06a2aff6d Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sun, 9 Aug 2026 13:04:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E7=89=88=E6=9C=AC=E5=B9=B6?= =?UTF-8?q?=E6=94=B9=E7=94=A8=E5=86=85=E9=83=A8=E8=B4=A6=E5=8F=B7=E5=AD=98?= =?UTF-8?q?=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/ck/CKManager.vue | 8 +-- internal/app/app.go | 8 ++- internal/app/app_accounts.go | 14 ++--- internal/app/app_export.go | 13 ++-- internal/app/app_products.go | 2 +- internal/config/config.go | 79 ++++++++++++++++++++--- internal/config/config_test.go | 80 ++++++++++++++++++++++++ internal/constants/constants.go | 15 +++-- internal/seckill/account_store.go | 47 +++++++++----- internal/seckill/account_store_test.go | 38 +++++++++++ internal/seckill/scheduler.go | 6 +- internal/types/types.go | 13 ++-- wails.json | 2 +- 13 files changed, 271 insertions(+), 54 deletions(-) create mode 100644 internal/config/config_test.go create mode 100644 internal/seckill/account_store_test.go diff --git a/frontend/src/components/ck/CKManager.vue b/frontend/src/components/ck/CKManager.vue index 34fef4b..b1b2c2a 100644 --- a/frontend/src/components/ck/CKManager.vue +++ b/frontend/src/components/ck/CKManager.vue @@ -21,7 +21,7 @@ const loading = ref(false) const verifyResult = ref(null) const organizeResult = ref(null) -// 账号文件管理状态 +// 内部账号库管理状态 const accountContent = ref('') const accountLoading = ref(false) const selectedAccountNames = ref([]) @@ -46,7 +46,7 @@ const importAccountText = async (content: string) => { try { const result = await ImportAccounts(content) if (!result.success) { - showToast('导入失败', result.error || '账号文件未更新', 'error') + showToast('导入失败', result.error || '内部账号库未更新', 'error') return } accountContent.value = '' @@ -115,7 +115,7 @@ const saveEdit = async () => { try { const result = await UpdateAccount(editingAccount.value.name, editName.value, editCookie.value) if (!result.success) { - showToast('保存失败', result.error || '账号文件未更新', 'error') + showToast('保存失败', result.error || '内部账号库未更新', 'error') return } await refreshManagedAccounts() @@ -139,7 +139,7 @@ const deleteManagedAccounts = async (names: string[]) => { try { const result = await DeleteAccounts(targetNames) if (!result.success) { - showToast('删除失败', result.error || '账号文件未更新', 'error') + showToast('删除失败', result.error || '内部账号库未更新', 'error') return } await refreshManagedAccounts() diff --git a/internal/app/app.go b/internal/app/app.go index cefd478..ad16f3f 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -42,7 +42,7 @@ type App struct { products []types.Product productsAt time.Time - // accountsMu 串行化账号文件修改,避免并发导入、编辑或删除互相覆盖。 + // accountsMu 串行化内部账号库修改,避免并发导入、编辑或删除互相覆盖。 accountsMu sync.Mutex closeLog func() @@ -123,8 +123,14 @@ func (a *App) Startup(ctx context.Context) { a.ctx = ctx a.paths = config.DefaultAppPaths() + configMigrated, configMigrationErr := config.MigrateLegacyFile(a.paths.ConfigFile, a.paths.LegacyConfigFile) a.logStore = logger.NewMemoryLogStore(800) a.closeLog = logger.InitLogging(a.logStore, a.paths.LogDir) + if configMigrationErr != nil { + logger.LogEvent("配置", "warn", "旧配置迁移失败: %s: %v", a.paths.LegacyConfigFile, configMigrationErr) + } else if configMigrated { + logger.LogEvent("配置", "success", "已迁移旧配置到应用内部目录: %s", a.paths.ConfigFile) + } // 加载配置并应用运行时设置 a.cfg = config.LoadConfig(a.paths.ConfigFile) diff --git a/internal/app/app_accounts.go b/internal/app/app_accounts.go index 192c295..94a1429 100644 --- a/internal/app/app_accounts.go +++ b/internal/app/app_accounts.go @@ -8,7 +8,7 @@ import ( "douyu_go/internal/types" ) -// ImportAccounts 将剪贴板中的账号行追加到账号文件。每行格式必须为 +// ImportAccounts 将剪贴板中的账号行追加到内部账号库。每行格式必须为 // 账号名----密码----Cookie,账号名不能与已有账号重复。 func (a *App) ImportAccounts(content string) types.AccountMutationResult { entries, err := parseAccountImport(content) @@ -22,7 +22,7 @@ func (a *App) ImportAccounts(content string) types.AccountMutationResult { accountFile := a.scheduler.AccountFilePath() lines, err := parseAccountFile(accountFile) if err != nil { - return types.AccountMutationResult{Error: "读取账号文件失败: " + err.Error()} + return types.AccountMutationResult{Error: "读取内部账号库失败: " + err.Error()} } existingNames := make(map[string]struct{}, len(lines)) @@ -46,7 +46,7 @@ func (a *App) ImportAccounts(content string) types.AccountMutationResult { return types.AccountMutationResult{Success: true, Count: len(entries)} } -// UpdateAccount 更新账号名或 Cookie。newCookie 留空时保留文件中的原 Cookie。 +// UpdateAccount 更新账号名或 Cookie。newCookie 留空时保留内部账号库中的原 Cookie。 func (a *App) UpdateAccount(originalName, newName, newCookie string) types.AccountMutationResult { originalName = strings.TrimSpace(originalName) newName = strings.TrimSpace(newName) @@ -64,7 +64,7 @@ func (a *App) UpdateAccount(originalName, newName, newCookie string) types.Accou accountFile := a.scheduler.AccountFilePath() lines, err := parseAccountFile(accountFile) if err != nil { - return types.AccountMutationResult{Error: "读取账号文件失败: " + err.Error()} + return types.AccountMutationResult{Error: "读取内部账号库失败: " + err.Error()} } targetIndex := -1 @@ -108,7 +108,7 @@ func (a *App) UpdateAccount(originalName, newName, newCookie string) types.Accou return types.AccountMutationResult{Success: true, Count: 1} } -// DeleteAccounts 从账号文件中删除指定账号,可一次删除多个账号。 +// DeleteAccounts 从内部账号库中删除指定账号,可一次删除多个账号。 func (a *App) DeleteAccounts(accountNames []string) types.AccountMutationResult { nameSet := make(map[string]struct{}, len(accountNames)) for _, name := range accountNames { @@ -126,7 +126,7 @@ func (a *App) DeleteAccounts(accountNames []string) types.AccountMutationResult accountFile := a.scheduler.AccountFilePath() lines, err := parseAccountFile(accountFile) if err != nil { - return types.AccountMutationResult{Error: "读取账号文件失败: " + err.Error()} + return types.AccountMutationResult{Error: "读取内部账号库失败: " + err.Error()} } kept := make([]accountLine, 0, len(lines)) deleted := 0 @@ -194,7 +194,7 @@ func (a *App) saveAccountLines(accountFile string, lines []accountLine) error { content += "\r\n" } if _, err := writeFileWithBackup(accountFile, []byte(content)); err != nil { - return fmt.Errorf("更新账号文件失败: %w", err) + return fmt.Errorf("更新内部账号库失败: %w", err) } a.scheduler.LoadAccounts() return nil diff --git a/internal/app/app_export.go b/internal/app/app_export.go index 8432dbf..3159f8c 100644 --- a/internal/app/app_export.go +++ b/internal/app/app_export.go @@ -53,7 +53,7 @@ func (a *App) ExportAccounts(accountNames []string, filename string) types.Expor accountFile := a.scheduler.AccountFilePath() lines, err := parseAccountFile(accountFile) if err != nil { - return types.ExportAccountsResult{Error: "读取账号文件失败: " + err.Error()} + return types.ExportAccountsResult{Error: "读取内部账号库失败: " + err.Error()} } nameSet := make(map[string]bool) @@ -79,7 +79,10 @@ func (a *App) ExportAccounts(accountNames []string, filename string) types.Expor exportPath := filepath.Join(filepath.Dir(accountFile), filename) content := strings.Join(matchedLines, "\n") + "\n" - if err := os.WriteFile(exportPath, []byte(content), 0644); err != nil { + if err := os.MkdirAll(filepath.Dir(exportPath), 0700); err != nil { + return types.ExportAccountsResult{Error: "创建导出目录失败: " + err.Error()} + } + if err := os.WriteFile(exportPath, []byte(content), 0600); err != nil { return types.ExportAccountsResult{Error: "写入文件失败: " + err.Error()} } @@ -137,7 +140,7 @@ func (a *App) OrganizeAccounts(commodityID, commodityName string, startTime, end accountFile := a.scheduler.AccountFilePath() lines, err := parseAccountFile(accountFile) if err != nil { - return types.OrganizeAccountsResult{Error: "读取账号文件失败: " + err.Error()} + return types.OrganizeAccountsResult{Error: "读取内部账号库失败: " + err.Error()} } missSet := make(map[string]bool) @@ -160,9 +163,9 @@ func (a *App) OrganizeAccounts(commodityID, commodityName string, startTime, end newContent := strings.Join(keepLines, "\r\n") + "\r\n" backupPath, err := writeFileWithBackup(accountFile, []byte(newContent)) if err != nil { - return types.OrganizeAccountsResult{Error: "更新账号文件失败: " + err.Error()} + return types.OrganizeAccountsResult{Error: "更新内部账号库失败: " + err.Error()} } - logger.LogEvent("整理", "success", "账号文件已安全更新,备份=%s", backupPath) + logger.LogEvent("整理", "success", "内部账号库已安全更新,备份=%s", backupPath) // 重新加载账号 a.scheduler.LoadAccounts() diff --git a/internal/app/app_products.go b/internal/app/app_products.go index 8c188af..41d7541 100644 --- a/internal/app/app_products.go +++ b/internal/app/app_products.go @@ -55,7 +55,7 @@ func (a *App) GetAccounts() []types.AccountView { return list } -// ReloadAccounts 重新加载账号文件。 +// ReloadAccounts 重新加载内部账号库。 func (a *App) ReloadAccounts() types.ReloadAccountsResult { a.scheduler.LoadAccounts() return types.ReloadAccountsResult{Success: true, Count: a.scheduler.AccountCount()} diff --git a/internal/config/config.go b/internal/config/config.go index 864a61f..041a4b6 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -15,15 +15,43 @@ import ( ) // DefaultAppPaths 收集运行时路径,支持环境变量覆盖。 -// 所有相对路径会基于当前工作目录解析为绝对路径,避免打包后工作目录不同导致路径错位。 +// 默认数据存放在当前用户的应用配置目录,而非工作目录,避免 macOS 应用包目录无写权限。 func DefaultAppPaths() types.AppPaths { - return types.AppPaths{ - ConfigFile: resolvePath(envOrDefault("DOUYU_CONFIG", constants.DefaultConfigFile)), - AccountFile: resolvePath(envOrDefault("DOUYU_USERS", constants.DefaultAccountFile)), - LogDir: resolvePath(envOrDefault("DOUYU_LOG_DIR", constants.DefaultLogDir)), - SuccessDir: resolvePath(envOrDefault("DOUYU_SUCCESS_DIR", constants.DefaultSuccessDir)), - FontFiles: imgpkg.CollectFontCandidates(), + dataDir := resolvePath(envOrDefault("DOUYU_DATA_DIR", defaultAppDataDir())) + configFile := filepath.Join(dataDir, constants.DefaultConfigFile) + accountFile := filepath.Join(dataDir, constants.DefaultAccountFile) + legacyConfigFile := resolvePath(constants.LegacyDefaultConfigFile) + legacyAccountFile := resolvePath(constants.LegacyDefaultAccountFile) + + if value := os.Getenv("DOUYU_CONFIG"); value != "" { + configFile = resolvePath(value) + legacyConfigFile = "" } + if value := os.Getenv("DOUYU_USERS"); value != "" { + accountFile = resolvePath(value) + legacyAccountFile = "" + } + + return types.AppPaths{ + DataDir: dataDir, + ConfigFile: configFile, + AccountFile: accountFile, + LegacyConfigFile: legacyConfigFile, + LegacyAccountFile: legacyAccountFile, + LogDir: resolvePath(envOrDefault("DOUYU_LOG_DIR", filepath.Join(dataDir, constants.DefaultLogDir))), + SuccessDir: resolvePath(envOrDefault("DOUYU_SUCCESS_DIR", filepath.Join(dataDir, constants.DefaultSuccessDir))), + FontFiles: imgpkg.CollectFontCandidates(), + } +} + +func defaultAppDataDir() string { + if dir, err := os.UserConfigDir(); err == nil && dir != "" { + return filepath.Join(dir, constants.DefaultAppDataDirName) + } + if home, err := os.UserHomeDir(); err == nil && home != "" { + return filepath.Join(home, "."+constants.DefaultAppDataDirName) + } + return "." + constants.DefaultAppDataDirName } // resolvePath 将相对路径解析为绝对路径。 @@ -44,6 +72,38 @@ func envOrDefault(key, fallback string) string { return fallback } +// MigrateLegacyFile 在目标文件不存在时复制旧版本数据,不删除旧文件。 +// 返回值表示是否完成了迁移。 +func MigrateLegacyFile(target, legacy string) (bool, error) { + if target == "" || legacy == "" || samePath(target, legacy) { + return false, nil + } + if _, err := os.Stat(target); err == nil { + return false, nil + } else if !errors.Is(err, os.ErrNotExist) { + return false, err + } + + content, err := os.ReadFile(legacy) + if errors.Is(err, os.ErrNotExist) { + return false, nil + } + if err != nil { + return false, err + } + if err := os.MkdirAll(filepath.Dir(target), 0700); err != nil { + return false, err + } + if err := os.WriteFile(target, content, 0600); err != nil { + return false, err + } + return true, nil +} + +func samePath(left, right string) bool { + return filepath.Clean(left) == filepath.Clean(right) +} + // LoadTraceConfig 读取 trace 开关;默认关闭,避免保存敏感调试数据。 func LoadTraceConfig() types.TraceConfig { return types.TraceConfig{ @@ -160,5 +220,8 @@ func SaveConfig(path string, cfg types.Config) error { if err != nil { return err } - return os.WriteFile(path, data, 0644) + if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { + return err + } + return os.WriteFile(path, data, 0600) } diff --git a/internal/config/config_test.go b/internal/config/config_test.go new file mode 100644 index 0000000..ea99090 --- /dev/null +++ b/internal/config/config_test.go @@ -0,0 +1,80 @@ +package config + +import ( + "os" + "path/filepath" + "testing" +) + +func TestMigrateLegacyFile(t *testing.T) { + dir := t.TempDir() + legacy := filepath.Join(dir, "legacy", "config.json") + target := filepath.Join(dir, "internal", "config.json") + content := []byte(`{"activity":"xiaodian"}`) + + if err := os.MkdirAll(filepath.Dir(legacy), 0700); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(legacy, content, 0600); err != nil { + t.Fatal(err) + } + + migrated, err := MigrateLegacyFile(target, legacy) + if err != nil { + t.Fatalf("迁移失败: %v", err) + } + if !migrated { + t.Fatal("应迁移旧文件") + } + got, err := os.ReadFile(target) + if err != nil { + t.Fatal(err) + } + if string(got) != string(content) { + t.Fatalf("迁移内容 = %q, want %q", got, content) + } + if _, err := os.Stat(legacy); err != nil { + t.Fatalf("旧文件不应被删除: %v", err) + } + + if err := os.WriteFile(legacy, []byte(`{"activity":"other"}`), 0600); err != nil { + t.Fatal(err) + } + migrated, err = MigrateLegacyFile(target, legacy) + if err != nil { + t.Fatalf("重复迁移失败: %v", err) + } + if migrated { + t.Fatal("目标已存在时不应覆盖迁移") + } + got, err = os.ReadFile(target) + if err != nil { + t.Fatal(err) + } + if string(got) != string(content) { + t.Fatalf("目标文件被意外覆盖: %q", got) + } +} + +func TestDefaultAppPathsUseInternalDataDir(t *testing.T) { + dir := t.TempDir() + t.Setenv("DOUYU_DATA_DIR", dir) + t.Setenv("DOUYU_CONFIG", "") + t.Setenv("DOUYU_USERS", "") + t.Setenv("DOUYU_LOG_DIR", "") + t.Setenv("DOUYU_SUCCESS_DIR", "") + + paths := DefaultAppPaths() + if paths.DataDir != dir { + t.Fatalf("DataDir = %q, want %q", paths.DataDir, dir) + } + if paths.ConfigFile != filepath.Join(dir, "config.json") { + t.Fatalf("ConfigFile = %q", paths.ConfigFile) + } + if paths.AccountFile != filepath.Join(dir, "accounts.txt") { + t.Fatalf("AccountFile = %q", paths.AccountFile) + } + if paths.LogDir != filepath.Join(dir, "logs") || paths.SuccessDir != filepath.Join(dir, "success_images") { + t.Fatalf("应用数据子目录异常: logs=%q success=%q", paths.LogDir, paths.SuccessDir) + } +} diff --git a/internal/constants/constants.go b/internal/constants/constants.go index 622414f..ec1e5d0 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -3,7 +3,7 @@ package constants import "time" // Version 应用版本号,与 wails.json version 同步。 -const Version = "0.1.5" +const Version = "0.1.6" // BuildTime 构建时间,由编译时 ldflags 注入。 var BuildTime = "unknown" @@ -15,10 +15,15 @@ const ( DefaultConcurrency = 150 ProductCacheTTL = 30 * time.Second - DefaultConfigFile = "config.json" - DefaultAccountFile = "data/user.txt" - DefaultLogDir = "logs" - DefaultSuccessDir = "success_images" + DefaultAppDataDirName = "douyu-go" + DefaultConfigFile = "config.json" + DefaultAccountFile = "accounts.txt" + DefaultLogDir = "logs" + DefaultSuccessDir = "success_images" + + // LegacyDefault* 是旧版本相对工作目录存储的位置,仅用于首次迁移。 + LegacyDefaultConfigFile = "config.json" + LegacyDefaultAccountFile = "data/user.txt" // DefaultTimeAPI 服务端时间校准接口。 // 斗鱼服务器时间与活动无关,统一用精英手册的 storedetail(能稳定返回 diff --git a/internal/seckill/account_store.go b/internal/seckill/account_store.go index 86b6dae..0f7c806 100644 --- a/internal/seckill/account_store.go +++ b/internal/seckill/account_store.go @@ -8,26 +8,32 @@ import ( "strings" "sync" + "douyu_go/internal/config" "douyu_go/internal/constants" "douyu_go/internal/logger" "douyu_go/internal/types" ) -// AccountStore 账号存储,负责从账号文件加载、查询账号信息。 +// AccountStore 账号存储,负责从应用内部账号库加载、查询账号信息。 // 账号文件格式:每行 `账号名----密码----Cookie`(密码段保留以兼容历史文件, // 但程序不读取也不使用密码,鉴权完全基于 Cookie)。 type AccountStore struct { - mu sync.RWMutex - accounts []types.Account - filePath string + mu sync.RWMutex + accounts []types.Account + filePath string + legacyFilePath string } -// NewAccountStore 创建账号存储,filePath 为账号文件路径。 -func NewAccountStore(filePath string) *AccountStore { +// NewAccountStore 创建账号存储。legacyFilePaths 仅在内部账号库首次创建时用于迁移旧文件。 +func NewAccountStore(filePath string, legacyFilePaths ...string) *AccountStore { if filePath == "" { filePath = constants.DefaultAccountFile } - return &AccountStore{filePath: filePath} + legacyFilePath := "" + if len(legacyFilePaths) > 0 { + legacyFilePath = legacyFilePaths[0] + } + return &AccountStore{filePath: filePath, legacyFilePath: legacyFilePath} } // FilePath 返回账号文件路径。 @@ -35,23 +41,33 @@ func (a *AccountStore) FilePath() string { return a.filePath } -// Load 从账号文件加载账号。 +// Load 从应用内部账号库加载账号。 func (a *AccountStore) Load() { path := a.filePath - // 文件不存在时自动创建目录和空文件,方便用户直接粘贴 Cookie + // 首次启动时迁移旧版本工作目录中的账号文件,保留原文件作为备份。 + if _, err := os.Stat(path); os.IsNotExist(err) { + migrated, migrateErr := config.MigrateLegacyFile(path, a.legacyFilePath) + if migrateErr != nil { + logger.LogEvent("账号", "warn", "旧账号文件迁移失败: %s: %v", a.legacyFilePath, migrateErr) + } else if migrated { + logger.LogEvent("账号", "success", "已迁移旧账号文件到应用内部目录: %s", path) + } + } + + // 应用内部账号库不存在时自动创建目录和空文件,方便用户直接在界面导入。 if _, err := os.Stat(path); os.IsNotExist(err) { if dir := filepath.Dir(path); dir != "" && dir != "." { - if mkErr := os.MkdirAll(dir, 0755); mkErr != nil { + if mkErr := os.MkdirAll(dir, 0700); mkErr != nil { logger.LogEvent("账号", "warn", "账号目录创建失败: %s: %v", dir, mkErr) } } // 使用 Windows 换行格式 \r\n hint := "# 格式: 账号名----密码----Cookie\r\n# 每行一个账号,粘贴后保存即可\r\n" - if wrErr := os.WriteFile(path, []byte(hint), 0644); wrErr != nil { + if wrErr := os.WriteFile(path, []byte(hint), 0600); wrErr != nil { logger.LogEvent("账号", "warn", "账号文件创建失败: %s: %v", path, wrErr) } else { - logger.LogEvent("账号", "info", "已创建空账号文件: %s,请粘贴 Cookie 后重新加载", path) + logger.LogEvent("账号", "info", "已创建空账号库: %s,请在 CK 管理中导入账号", path) } } @@ -69,13 +85,16 @@ func (a *AccountStore) Load() { normalized, converted := normalizeLineEndings(content) if converted { // 保存转换后的内容 - if err := os.WriteFile(path, normalized, 0644); err != nil { + if err := os.WriteFile(path, normalized, 0600); err != nil { logger.LogEvent("账号", "warn", "账号文件换行格式转换失败: %s: %v", path, err) } else { logger.LogEvent("账号", "info", "账号文件已自动转换为 Windows 换行格式: %s", path) } content = normalized } + if err := os.Chmod(path, 0600); err != nil { + logger.LogEvent("账号", "warn", "账号库权限收紧失败: %s: %v", path, err) + } var list []types.Account scanner := bufio.NewScanner(bytes.NewReader(content)) @@ -102,7 +121,7 @@ func (a *AccountStore) Load() { a.mu.Lock() a.accounts = list a.mu.Unlock() - logger.LogEvent("账号", "success", "账号加载完成: %d 个,来源=%s", len(list), path) + logger.LogEvent("账号", "success", "账号加载完成: %d 个,内部存储=%s", len(list), path) } // Count 返回当前账号数。 diff --git a/internal/seckill/account_store_test.go b/internal/seckill/account_store_test.go new file mode 100644 index 0000000..b94f10c --- /dev/null +++ b/internal/seckill/account_store_test.go @@ -0,0 +1,38 @@ +package seckill + +import ( + "os" + "path/filepath" + "testing" +) + +func TestAccountStoreMigratesLegacyFile(t *testing.T) { + dir := t.TempDir() + legacy := filepath.Join(dir, "data", "user.txt") + target := filepath.Join(dir, "app-data", "accounts.txt") + content := []byte("# 旧账号\r\nfoo----password----cookie-a\r\n") + + if err := os.MkdirAll(filepath.Dir(legacy), 0700); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(legacy, content, 0600); err != nil { + t.Fatal(err) + } + + store := NewAccountStore(target, legacy) + store.Load() + accounts := store.Snapshot() + if len(accounts) != 1 || accounts[0].Name != "foo" || accounts[0].Cookie != "cookie-a" { + t.Fatalf("迁移后的账号 = %#v", accounts) + } + if _, err := os.Stat(legacy); err != nil { + t.Fatalf("旧账号文件不应被删除: %v", err) + } + info, err := os.Stat(target) + if err != nil { + t.Fatal(err) + } + if info.Mode().Perm() != 0600 { + t.Fatalf("账号库权限 = %o, want 0600", info.Mode().Perm()) + } +} diff --git a/internal/seckill/scheduler.go b/internal/seckill/scheduler.go index 3798ac3..cea4b1d 100644 --- a/internal/seckill/scheduler.go +++ b/internal/seckill/scheduler.go @@ -129,7 +129,7 @@ func New(cli *client.DouyuClient, paths types.AppPaths, font *truetype.Font, con client: cli, paths: paths, font: font, - accounts: NewAccountStore(paths.AccountFile), + accounts: NewAccountStore(paths.AccountFile, paths.LegacyAccountFile), successImages: image.NewSuccessImageSaver( font, paths.SuccessDir, @@ -260,7 +260,7 @@ func normalizeSeckillMode(mode types.SeckillMode) types.SeckillMode { // ==================== 账号转发(AccountStore) ==================== -// LoadAccounts 从账号文件加载账号。 +// LoadAccounts 从应用内部账号库加载账号。 func (s *Scheduler) LoadAccounts() { s.accounts.Load() } @@ -270,7 +270,7 @@ func (s *Scheduler) AccountCount() int { return s.accounts.Count() } -// AccountFilePath 返回账号文件路径。 +// AccountFilePath 返回内部账号库路径。 func (s *Scheduler) AccountFilePath() string { return s.accounts.FilePath() } diff --git a/internal/types/types.go b/internal/types/types.go index 4ed39d1..8abbb9a 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -72,11 +72,14 @@ const ( // AppPaths 运行时路径。 type AppPaths struct { - ConfigFile string - AccountFile string - LogDir string - SuccessDir string - FontFiles []string + DataDir string + ConfigFile string + AccountFile string + LegacyConfigFile string + LegacyAccountFile string + LogDir string + SuccessDir string + FontFiles []string } // TraceConfig 追踪配置。 diff --git a/wails.json b/wails.json index 1b96eb4..e5ca03d 100644 --- a/wails.json +++ b/wails.json @@ -2,7 +2,7 @@ "$schema": "https://wails.io/schemas/config.v2.json", "name": "douyu_go", "outputfilename": "douyu_go", - "version": "0.1.5", + "version": "0.1.6", "frontend:dir": "frontend", "frontend:install": "npm install", "frontend:build": "npm run build",