feat: Add owner account management feature with a new frontend list component and backend API/database integration.

This commit is contained in:
2026-01-30 13:23:29 +08:00
parent 3430b57cbf
commit 23901c9d14
4 changed files with 144 additions and 1 deletions

View File

@@ -104,6 +104,7 @@ func startServer(cfg *config.Config) {
mux.HandleFunc("/api/db/owners", api.CORS(handleGetOwners))
mux.HandleFunc("/api/db/owners/stats", api.CORS(handleGetOwnerStats))
mux.HandleFunc("/api/db/owners/clear", api.CORS(handleClearOwners))
mux.HandleFunc("/api/db/owners/refetch-account-ids", api.CORS(api.HandleRefetchAccountIDs))
mux.HandleFunc("/api/upload/validate", api.CORS(api.HandleUploadValidate))
// 注册测试 API

View File

@@ -128,6 +128,84 @@ func HandleUploadValidate(w http.ResponseWriter, r *http.Request) {
})
}
// HandleRefetchAccountIDs 重新获取缺少 account_id 的母号
func HandleRefetchAccountIDs(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
Error(w, http.StatusMethodNotAllowed, "仅支持 POST")
return
}
if database.Instance == nil {
Error(w, http.StatusInternalServerError, "数据库未初始化")
return
}
// 获取所有缺少 account_id 的 owners
owners, err := database.Instance.GetOwnersWithoutAccountID()
if err != nil {
Error(w, http.StatusInternalServerError, fmt.Sprintf("查询数据库失败: %v", err))
return
}
if len(owners) == 0 {
Success(w, map[string]interface{}{
"message": "所有母号都已有 account_id",
"total": 0,
"success": 0,
"fail": 0,
})
return
}
logger.Info(fmt.Sprintf("开始重新获取 account_id: 共 %d 个", len(owners)), "", "upload")
// 并发获取 account_id
var wg sync.WaitGroup
sem := make(chan struct{}, 20) // 20 并发
var mu sync.Mutex
successCount := 0
failCount := 0
for _, owner := range owners {
wg.Add(1)
sem <- struct{}{}
go func(o database.TeamOwner) {
defer wg.Done()
defer func() { <-sem }()
accountID, err := fetchAccountID(o.Token)
mu.Lock()
defer mu.Unlock()
if err != nil {
failCount++
logger.Warning(fmt.Sprintf("获取 account_id 失败 (%s): %v", o.Email, err), "", "upload")
} else {
// 更新数据库
if updateErr := database.Instance.UpdateOwnerAccountID(o.ID, accountID); updateErr != nil {
failCount++
logger.Error(fmt.Sprintf("更新 account_id 失败 (%s): %v", o.Email, updateErr), "", "upload")
} else {
successCount++
logger.Info(fmt.Sprintf("获取 account_id 成功: %s -> %s", o.Email, accountID), "", "upload")
}
}
}(owner)
}
wg.Wait()
logger.Info(fmt.Sprintf("重新获取 account_id 完成: 成功=%d, 失败=%d", successCount, failCount), "", "upload")
Success(w, map[string]interface{}{
"message": "重新获取 account_id 完成",
"total": len(owners),
"success": successCount,
"fail": failCount,
})
}
// fetchAccountIDsConcurrent 并发获取 account_id
func fetchAccountIDsConcurrent(owners []database.TeamOwner, concurrency int) {
var wg sync.WaitGroup

View File

@@ -237,6 +237,36 @@ func (d *DB) ClearTeamOwners() error {
return err
}
// GetOwnersWithoutAccountID 获取缺少 account_id 的 owners
func (d *DB) GetOwnersWithoutAccountID() ([]TeamOwner, error) {
rows, err := d.db.Query(`
SELECT id, email, password, token, account_id, status, created_at
FROM team_owners WHERE account_id = '' OR account_id IS NULL
ORDER BY created_at DESC
`)
if err != nil {
return nil, err
}
defer rows.Close()
var owners []TeamOwner
for rows.Next() {
var owner TeamOwner
err := rows.Scan(&owner.ID, &owner.Email, &owner.Password, &owner.Token, &owner.AccountID, &owner.Status, &owner.CreatedAt)
if err != nil {
continue
}
owners = append(owners, owner)
}
return owners, nil
}
// UpdateOwnerAccountID 更新 owner 的 account_id
func (d *DB) UpdateOwnerAccountID(id int64, accountID string) error {
_, err := d.db.Exec("UPDATE team_owners SET account_id = ? WHERE id = ?", accountID, id)
return err
}
// GetOwnerStats 获取统计
func (d *DB) GetOwnerStats() map[string]int {
stats := map[string]int{