diff --git a/backend/cmd/main.go b/backend/cmd/main.go index 6b62521..4f2ff50 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -1050,7 +1050,7 @@ func handleProxyTest(w http.ResponseWriter, r *http.Request) { ipKey = "team_reg_proxy_test_ip" } - logger.Info(fmt.Sprintf("测试代理连接: %s", proxyURL), "", "proxy") + logger.Status(fmt.Sprintf("测试代理连接中: %s", proxyURL), "", "proxy") // 解析代理 URL proxyParsed, err := parseProxyURL(proxyURL) diff --git a/backend/internal/api/error_cleaner.go b/backend/internal/api/error_cleaner.go index 53e4777..6765167 100644 --- a/backend/internal/api/error_cleaner.go +++ b/backend/internal/api/error_cleaner.go @@ -80,7 +80,7 @@ func checkAndCleanErrors() { } // 执行清理 - logger.Info("开始定期清理错误账号...", "", "cleaner") + logger.Status("定期清理错误账号中...", "", "cleaner") errorAccounts, err := fetchAllErrorAccounts() if err != nil { diff --git a/backend/internal/api/owner_ban_check.go b/backend/internal/api/owner_ban_check.go index 0aba43c..488012a 100644 --- a/backend/internal/api/owner_ban_check.go +++ b/backend/internal/api/owner_ban_check.go @@ -309,7 +309,7 @@ func runBanCheckTask(owners []database.TeamOwner, concurrency int) { lastBanCheckTime = time.Now() }() - logger.Info(fmt.Sprintf("开始封禁检查: 共 %d 个母号, 并发数: %d", len(owners), concurrency), "", "ban-check") + logger.Status(fmt.Sprintf("封禁检查中: 共 %d 个母号, 并发数: %d", len(owners), concurrency), "", "ban-check") // 任务队列 taskChan := make(chan database.TeamOwner, len(owners)) diff --git a/backend/internal/api/s2a_clean.go b/backend/internal/api/s2a_clean.go index 6cf5ebc..61d5649 100644 --- a/backend/internal/api/s2a_clean.go +++ b/backend/internal/api/s2a_clean.go @@ -47,7 +47,7 @@ func HandleCleanErrorAccounts(w http.ResponseWriter, r *http.Request) { return } - logger.Info("开始清理错误账号...", "", "s2a") + logger.Status("清理错误账号中...", "", "s2a") // Step 1: 获取所有错误账号 errorAccounts, err := fetchAllErrorAccounts() @@ -68,7 +68,7 @@ func HandleCleanErrorAccounts(w http.ResponseWriter, r *http.Request) { return } - logger.Info(fmt.Sprintf("找到 %d 个错误账号,开始删除...", len(errorAccounts)), "", "s2a") + logger.Status(fmt.Sprintf("找到 %d 个错误账号,删除中...", len(errorAccounts)), "", "s2a") // Step 2: 逐条删除 success := 0 diff --git a/backend/internal/api/team_process.go b/backend/internal/api/team_process.go index 0c869ec..cb1f917 100644 --- a/backend/internal/api/team_process.go +++ b/backend/internal/api/team_process.go @@ -740,7 +740,7 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul memberStartTime := time.Now() memberLogPrefix := fmt.Sprintf("%s [成员 %d]", logPrefix, memberIdx+1) - logger.Info(fmt.Sprintf("%s 开始入库 | 邮箱: %s", memberLogPrefix, memberChild.Email), memberChild.Email, "team") + logger.Status(fmt.Sprintf("%s 入库中... | 邮箱: %s", memberLogPrefix, memberChild.Email), memberChild.Email, "team") var s2aSuccess bool var lastError string @@ -852,7 +852,7 @@ func processSingleTeam(idx int, req TeamProcessRequest) (result TeamProcessResul if req.IncludeOwner && teamProcessState.Running { ownerLogPrefix := fmt.Sprintf("%s [母号 ]", logPrefix) ownerStartTime := time.Now() - logger.Info(fmt.Sprintf("%s 开始母号入库...", ownerLogPrefix), owner.Email, "team") + logger.Status(fmt.Sprintf("%s 母号入库中...", ownerLogPrefix), owner.Email, "team") var ownerSuccess bool var lastError string diff --git a/backend/internal/api/upload.go b/backend/internal/api/upload.go index 50b3715..b855cee 100644 --- a/backend/internal/api/upload.go +++ b/backend/internal/api/upload.go @@ -83,9 +83,9 @@ func HandleUploadValidate(w http.ResponseWriter, r *http.Request) { } // 并发验证账号并获取 account_id(只保留 team 账号) - logger.Info(fmt.Sprintf("开始验证账号: 共 %d 个,只导入 plan 为 team 的账号", len(owners)), "", "upload") + logger.Status(fmt.Sprintf("验证账号中: 共 %d 个,只导入 plan 为 team 的账号", len(owners)), "", "upload") validOwners, teamCount, nonTeamCount, failCount := validateAndFetchAccountIDs(owners, 20) - logger.Info(fmt.Sprintf("验证完成: team=%d, 非team=%d, 失败=%d", teamCount, nonTeamCount, failCount), "", "upload") + logger.Success(fmt.Sprintf("验证完成: team=%d, 非team=%d, 失败=%d", teamCount, nonTeamCount, failCount), "", "upload") if len(validOwners) == 0 { Error(w, http.StatusBadRequest, fmt.Sprintf("没有有效的 team 账号(共 %d 个,非team: %d,失败: %d)", len(owners), nonTeamCount, failCount)) @@ -141,7 +141,7 @@ func HandleRefetchAccountIDs(w http.ResponseWriter, r *http.Request) { return } - logger.Info(fmt.Sprintf("开始重新获取 account_id: 共 %d 个", len(owners)), "", "upload") + logger.Status(fmt.Sprintf("重新获取 account_id 中: 共 %d 个", len(owners)), "", "upload") // 并发获取 account_id var wg sync.WaitGroup diff --git a/backend/internal/logger/logger.go b/backend/internal/logger/logger.go index f9d11ec..7295c98 100644 --- a/backend/internal/logger/logger.go +++ b/backend/internal/logger/logger.go @@ -25,6 +25,20 @@ var ( listMu sync.RWMutex ) +// 旋转动画字符 +var spinnerFrames = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"} +var spinnerIndex int +var spinnerMu sync.Mutex + +// getSpinnerChar 获取下一个旋转字符 +func getSpinnerChar() string { + spinnerMu.Lock() + defer spinnerMu.Unlock() + char := spinnerFrames[spinnerIndex%len(spinnerFrames)] + spinnerIndex++ + return char +} + // AddListener 添加日志监听器 func AddListener(id string) chan LogEntry { listMu.Lock() @@ -130,6 +144,10 @@ func log(level, message, email, module string) { case "warning": prefix = "WARN " color = colorYellow + case "status": + // 状态日志使用旋转动画字符 + prefix = getSpinnerChar() + " WAIT " + color = colorCyan } // 模块名固定宽度(8字符) @@ -184,6 +202,11 @@ func Warning(message, email, module string) { log("warning", message, email, module) } +// Status 记录状态日志(带旋转动画字符,用于等待中的操作) +func Status(message, email, module string) { + log("status", message, email, module) +} + // GetLogs 获取日志 func GetLogs(limit int) []LogEntry { logsMu.RLock() diff --git a/backend/internal/register/chatgpt.go b/backend/internal/register/chatgpt.go index 0568daf..8f51eec 100644 --- a/backend/internal/register/chatgpt.go +++ b/backend/internal/register/chatgpt.go @@ -301,7 +301,7 @@ func APIRegister(email, password, realName, birthdate, proxy string, logPrefix s fpInfo := reg.Client.GetFingerprintInfo() if retry == 0 { - logger.Info(fmt.Sprintf("%s 初始化会话... [%s]", logPrefix, fpInfo), email, "register") + logger.Status(fmt.Sprintf("%s 初始化会话... [%s]", logPrefix, fpInfo), email, "register") } else { logger.Warning(fmt.Sprintf("%s 403 重试 %d/3,换指纹 [%s]", logPrefix, retry, fpInfo), email, "register") } @@ -343,10 +343,9 @@ func APIRegister(email, password, realName, birthdate, proxy string, logPrefix s if err := reg.SendVerificationEmail(); err != nil { return nil, fmt.Errorf("发送验证邮件失败: %v", err) } - logger.Success(fmt.Sprintf("%s 已发送验证邮件", logPrefix), email, "register") - // 获取验证码 (带超时 90s) - logger.Info(fmt.Sprintf("%s 等待验证码...", logPrefix), email, "register") + // 获取验证码 (带超时 90s) - 合并日志,使用 Status 显示等待状态 + logger.Status(fmt.Sprintf("%s 验证邮箱中...", logPrefix), email, "register") otpCode, err := mail.GetVerificationCode(email, 90*time.Second) if err != nil { return nil, err