feat: Implement batch team owner processing with dedicated upload, configuration, and monitoring pages and backend services.
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -17,15 +18,18 @@ import (
|
||||
"codex-pool/internal/register"
|
||||
)
|
||||
|
||||
// TeamOwner 团队母号信息
|
||||
type TeamOwner struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Token string `json:"token"`
|
||||
AccountID string `json:"account_id"` // 已存储的 account_id,如有则直接使用
|
||||
}
|
||||
|
||||
// TeamProcessRequest 团队处理请求
|
||||
type TeamProcessRequest struct {
|
||||
// Owner 账号列表
|
||||
Owners []struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Token string `json:"token"`
|
||||
AccountID string `json:"account_id"` // 已存储的 account_id,如有则直接使用
|
||||
} `json:"owners"`
|
||||
Owners []TeamOwner `json:"owners"`
|
||||
// 配置
|
||||
MembersPerTeam int `json:"members_per_team"` // 每个 Team 的成员数
|
||||
ConcurrentTeams int `json:"concurrent_teams"` // 并发 Team 数量
|
||||
@@ -33,6 +37,7 @@ type TeamProcessRequest struct {
|
||||
Headless bool `json:"headless"` // 是否无头模式
|
||||
Proxy string `json:"proxy"` // 代理设置
|
||||
IncludeOwner bool `json:"include_owner"` // 母号也入库到 S2A
|
||||
ProcessCount int `json:"process_count"` // 处理数量,0表示全部
|
||||
}
|
||||
|
||||
// TeamProcessResult 团队处理结果
|
||||
@@ -91,12 +96,7 @@ func HandleTeamProcess(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
// 转换为请求格式(包含已存储的 account_id)
|
||||
for _, o := range pendingOwners {
|
||||
req.Owners = append(req.Owners, struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Token string `json:"token"`
|
||||
AccountID string `json:"account_id"`
|
||||
}{
|
||||
req.Owners = append(req.Owners, TeamOwner{
|
||||
Email: o.Email,
|
||||
Password: o.Password,
|
||||
Token: o.Token,
|
||||
@@ -106,6 +106,12 @@ func HandleTeamProcess(w http.ResponseWriter, r *http.Request) {
|
||||
logger.Info(fmt.Sprintf("从数据库加载 %d 个待处理母号", len(req.Owners)), "", "team")
|
||||
}
|
||||
|
||||
// 根据 ProcessCount 限制处理数量
|
||||
if req.ProcessCount > 0 && req.ProcessCount < len(req.Owners) {
|
||||
req.Owners = req.Owners[:req.ProcessCount]
|
||||
logger.Info(fmt.Sprintf("限制处理数量: %d 个母号", req.ProcessCount), "", "team")
|
||||
}
|
||||
|
||||
if req.MembersPerTeam <= 0 {
|
||||
req.MembersPerTeam = 4
|
||||
}
|
||||
@@ -330,7 +336,28 @@ func processSingleTeam(idx int, req TeamProcessRequest) TeamProcessResult {
|
||||
}
|
||||
result.TeamID = teamID
|
||||
|
||||
// Step 2: 并发注册成员
|
||||
// Step 2: 测试邀请功能(检测 Team 是否被封禁)
|
||||
testEmail := mail.GenerateEmail()
|
||||
if err := inviter.SendInvites([]string{testEmail}); err != nil {
|
||||
// 邀请失败,可能是 Team 被封禁
|
||||
errStr := err.Error()
|
||||
if strings.Contains(errStr, "403") || strings.Contains(errStr, "forbidden") ||
|
||||
strings.Contains(errStr, "banned") || strings.Contains(errStr, "suspended") ||
|
||||
strings.Contains(errStr, "deactivated") {
|
||||
// Team 被封禁,标记为 invalid
|
||||
logger.Error(fmt.Sprintf("%s Team 被封禁,标记为无效: %v", logPrefix, err), owner.Email, "team")
|
||||
if database.Instance != nil {
|
||||
database.Instance.MarkOwnerAsInvalid(owner.Email)
|
||||
}
|
||||
result.Errors = append(result.Errors, "Team 被封禁")
|
||||
result.DurationMs = time.Since(startTime).Milliseconds()
|
||||
return result
|
||||
}
|
||||
// 其他邀请错误,继续尝试
|
||||
logger.Warning(fmt.Sprintf("%s 首次邀请失败,继续尝试: %v", logPrefix, err), owner.Email, "team")
|
||||
}
|
||||
|
||||
// Step 3: 并发注册成员
|
||||
// 每个成员:邀请 → 注册,失败重试1次
|
||||
// Team 有4次额外补救机会
|
||||
type MemberAccount struct {
|
||||
|
||||
Reference in New Issue
Block a user