多平台账号批量注册系统,支持 OpenAI / Grok / Kiro / Gemini 平台的全自动注册。 Features: - 用户名/密码注册登录(首个用户自动管理员) - JWT 认证 + bcrypt 密码加密 - 积分系统与多用户管理 - HuggingFace Space 弹性 Worker 节点管理 - Cloudflare Worker 负载均衡 - 实时日志推送(SSE/WebSocket) - 管理后台(用户管理、系统设置、代理池、卡池) Tech Stack: Go 1.25 / Gin / GORM / PostgreSQL / Vue 3 / Vite / TailwindCSS
194 lines
4.3 KiB
Go
194 lines
4.3 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
"github.com/xiaolajiaoyyds/regplatformm/internal/model"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// CardPool 虚拟卡池(线程安全轮询,健康验证)
|
|
type CardPool struct {
|
|
mu sync.RWMutex
|
|
cards []model.CardPoolEntry
|
|
index uint64
|
|
db *gorm.DB
|
|
}
|
|
|
|
// NewCardPool 创建卡池
|
|
func NewCardPool(db *gorm.DB) *CardPool {
|
|
p := &CardPool{db: db}
|
|
p.Reload()
|
|
return p
|
|
}
|
|
|
|
// Reload 从 DB 加载有效卡到内存
|
|
func (p *CardPool) Reload() {
|
|
if p.db == nil {
|
|
return
|
|
}
|
|
var entries []model.CardPoolEntry
|
|
if err := p.db.Where("is_valid = ? AND fail_count < ?", true, 5).
|
|
Order("use_count ASC, created_at ASC").Find(&entries).Error; err != nil {
|
|
log.Warn().Err(err).Msg("卡池加载失败")
|
|
return
|
|
}
|
|
|
|
p.mu.Lock()
|
|
p.cards = entries
|
|
atomic.StoreUint64(&p.index, 0)
|
|
p.mu.Unlock()
|
|
|
|
log.Info().Int("count", len(entries)).Msg("卡池已加载")
|
|
}
|
|
|
|
// GetNext 轮询获取下一张有效卡
|
|
func (p *CardPool) GetNext() *model.CardPoolEntry {
|
|
p.mu.RLock()
|
|
n := len(p.cards)
|
|
if n == 0 {
|
|
p.mu.RUnlock()
|
|
return nil
|
|
}
|
|
idx := atomic.AddUint64(&p.index, 1)
|
|
card := p.cards[idx%uint64(n)]
|
|
p.mu.RUnlock()
|
|
return &card
|
|
}
|
|
|
|
// Count 返回可用卡数量
|
|
func (p *CardPool) Count() int {
|
|
p.mu.RLock()
|
|
defer p.mu.RUnlock()
|
|
return len(p.cards)
|
|
}
|
|
|
|
// MarkUsed 标记卡已使用
|
|
func (p *CardPool) MarkUsed(id uint) {
|
|
if p.db == nil {
|
|
return
|
|
}
|
|
now := time.Now()
|
|
p.db.Model(&model.CardPoolEntry{}).Where("id = ?", id).Updates(map[string]interface{}{
|
|
"use_count": gorm.Expr("use_count + 1"),
|
|
"last_used_at": now,
|
|
})
|
|
}
|
|
|
|
// MarkFailed 标记卡使用失败
|
|
func (p *CardPool) MarkFailed(id uint) {
|
|
if p.db == nil {
|
|
return
|
|
}
|
|
p.db.Model(&model.CardPoolEntry{}).Where("id = ?", id).Updates(map[string]interface{}{
|
|
"fail_count": gorm.Expr("fail_count + 1"),
|
|
})
|
|
// 失败次数过多自动标记无效
|
|
p.db.Model(&model.CardPoolEntry{}).Where("id = ? AND fail_count >= ?", id, 5).
|
|
Update("is_valid", false)
|
|
p.Reload()
|
|
}
|
|
|
|
// PoolStats 返回卡池统计
|
|
func (p *CardPool) PoolStats() map[string]interface{} {
|
|
if p.db == nil {
|
|
return map[string]interface{}{"total": 0, "valid": 0, "invalid": 0}
|
|
}
|
|
var total, validCount int64
|
|
p.db.Model(&model.CardPoolEntry{}).Count(&total)
|
|
p.db.Model(&model.CardPoolEntry{}).Where("is_valid = ?", true).Count(&validCount)
|
|
return map[string]interface{}{
|
|
"total": total,
|
|
"valid": validCount,
|
|
"invalid": total - validCount,
|
|
"active": p.Count(),
|
|
}
|
|
}
|
|
|
|
// StartValidator 启动后台定期验证协程(每 30 分钟验证一次)
|
|
func (p *CardPool) StartValidator(ctx context.Context) {
|
|
go func() {
|
|
ticker := time.NewTicker(30 * time.Minute)
|
|
defer ticker.Stop()
|
|
log.Info().Msg("卡池定时验证已启动")
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
log.Info().Msg("卡池定时验证已停止")
|
|
return
|
|
case <-ticker.C:
|
|
p.validateExpired()
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// validateExpired 标记已过期的卡为无效
|
|
func (p *CardPool) validateExpired() {
|
|
if p.db == nil {
|
|
return
|
|
}
|
|
now := time.Now()
|
|
currentMonth := int(now.Month())
|
|
currentYear := now.Year()
|
|
|
|
result := p.db.Model(&model.CardPoolEntry{}).
|
|
Where("is_valid = ? AND (exp_year < ? OR (exp_year = ? AND exp_month < ?))",
|
|
true, currentYear, currentYear, currentMonth).
|
|
Update("is_valid", false)
|
|
if result.RowsAffected > 0 {
|
|
log.Info().Int64("expired", result.RowsAffected).Msg("已标记过期卡为无效")
|
|
p.Reload()
|
|
}
|
|
}
|
|
|
|
// ValidateAll 验证所有卡(过期检查),返回 (总数, 有效, 无效)
|
|
func (p *CardPool) ValidateAll() (total, valid, invalid int) {
|
|
if p.db == nil {
|
|
return 0, 0, 0
|
|
}
|
|
p.validateExpired()
|
|
|
|
var entries []model.CardPoolEntry
|
|
if err := p.db.Find(&entries).Error; err != nil {
|
|
return 0, 0, 0
|
|
}
|
|
total = len(entries)
|
|
for _, e := range entries {
|
|
if e.IsValid {
|
|
valid++
|
|
} else {
|
|
invalid++
|
|
}
|
|
}
|
|
p.Reload()
|
|
return total, valid, invalid
|
|
}
|
|
|
|
// ValidateByIDs 验证指定 ID 的卡
|
|
func (p *CardPool) ValidateByIDs(ids []uint) (total, valid, invalid int) {
|
|
if p.db == nil || len(ids) == 0 {
|
|
return 0, 0, 0
|
|
}
|
|
p.validateExpired()
|
|
|
|
var entries []model.CardPoolEntry
|
|
if err := p.db.Where("id IN ?", ids).Find(&entries).Error; err != nil {
|
|
return 0, 0, 0
|
|
}
|
|
total = len(entries)
|
|
for _, e := range entries {
|
|
if e.IsValid {
|
|
valid++
|
|
} else {
|
|
invalid++
|
|
}
|
|
}
|
|
p.Reload()
|
|
return total, valid, invalid
|
|
}
|