package main import ( "bufio" "os" "strings" "time" ) // loadAccounts 从账号文件加载账号,格式为“账号----密码----Cookie”。 func (s *appState) loadAccounts() { path := s.paths.AccountFile if path == "" { path = defaultAccountFile } f, err := os.Open(path) if err != nil { logEvent("账号", "error", "读取账号文件失败: %s: %v", path, err) s.accountsMu.Lock() s.accounts = nil s.accountsMu.Unlock() return } defer f.Close() var list []Account scanner := bufio.NewScanner(f) scanner.Buffer(make([]byte, 1024), 1024*1024) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } parts := strings.SplitN(line, "----", 3) if len(parts) != 3 { logEvent("账号", "warn", "账号行格式错误,已跳过: %s", line) continue } list = append(list, Account{ Name: strings.TrimSpace(parts[0]), Password: strings.TrimSpace(parts[1]), Cookie: strings.TrimSpace(parts[2]), }) } if err := scanner.Err(); err != nil { logEvent("账号", "error", "读取账号文件中断: %v", err) } s.accountsMu.Lock() s.accounts = list s.accountsMu.Unlock() logEvent("账号", "success", "账号加载完成: %d 个,来源=%s", len(list), path) } // accountCount 返回当前账号数。 func (s *appState) accountCount() int { s.accountsMu.RLock() defer s.accountsMu.RUnlock() return len(s.accounts) } // accountSnapshot 复制一份账号列表,避免执行任务时长时间持锁。 func (s *appState) accountSnapshot() []Account { s.accountsMu.RLock() defer s.accountsMu.RUnlock() list := make([]Account, len(s.accounts)) copy(list, s.accounts) return list } // accountCookieByName 按账号名查找真实 Cookie,仅在后端内部使用。 func (s *appState) accountCookieByName(name string) (string, bool) { s.accountsMu.RLock() defer s.accountsMu.RUnlock() for _, account := range s.accounts { if account.Name == name { return account.Cookie, true } } return "", false } // productSnapshot 复制一份商品列表和对应的刷新时间。 func (s *appState) productSnapshot() ([]Product, time.Time) { s.productsMu.RLock() defer s.productsMu.RUnlock() list := make([]Product, len(s.products)) copy(list, s.products) return list, s.productsAt } // setProducts 替换商品缓存,并记录这批库存数据的刷新时间。 func (s *appState) setProducts(products []Product, fetchedAt time.Time) { s.productsMu.Lock() defer s.productsMu.Unlock() s.products = products s.productsAt = fetchedAt } // productPayload 根据当前缓存生成前端商品响应。 func (s *appState) productPayload(source string) ProductPayload { products, fetchedAt := s.productSnapshot() payload := ProductPayload{ Success: true, Source: source, Products: products, Count: len(products), } if !fetchedAt.IsZero() { age := time.Since(fetchedAt) if age < 0 { age = 0 } payload.FetchedAt = fetchedAt.In(cst).Format("2006-01-02 15:04:05.000") payload.AgeSecond = int(age.Seconds()) payload.Stale = age > productCacheTTL } else { payload.Stale = true } return payload } // clearResults 清空抢购结果。 func (s *appState) clearResults() { s.resultsMu.Lock() defer s.resultsMu.Unlock() s.results = make(map[string]string) } // setResult 写入单次兑换结果。 func (s *appState) setResult(key, value string) { s.resultsMu.Lock() defer s.resultsMu.Unlock() s.results[key] = value } // resultsSnapshot 复制当前结果。 func (s *appState) resultsSnapshot() map[string]string { s.resultsMu.RLock() defer s.resultsMu.RUnlock() out := make(map[string]string, len(s.results)) for k, v := range s.results { out[k] = v } return out }