feat: Implement core backend infrastructure including configuration management, SQLite database with team owners and app settings, and initial owner-related APIs and frontend components.
This commit is contained in:
@@ -10,13 +10,14 @@ import (
|
||||
|
||||
// TeamOwner 账号结构
|
||||
type TeamOwner struct {
|
||||
ID int64 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
AccountID string `json:"account_id"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ID int64 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
AccountID string `json:"account_id"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
LastCheckedAt *time.Time `json:"last_checked_at,omitempty"`
|
||||
}
|
||||
|
||||
// DB 数据库管理器
|
||||
@@ -40,10 +41,31 @@ func Init(dbPath string) error {
|
||||
return fmt.Errorf("创建表失败: %w", err)
|
||||
}
|
||||
|
||||
// 执行数据库迁移
|
||||
if err := Instance.migrate(); err != nil {
|
||||
fmt.Printf("[数据库] 迁移警告: %v\n", err)
|
||||
}
|
||||
|
||||
fmt.Printf("[数据库] SQLite 已连接: %s\n", dbPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// migrate 数据库迁移
|
||||
func (d *DB) migrate() error {
|
||||
// 添加 last_checked_at 列(如果不存在)
|
||||
_, err := d.db.Exec(`ALTER TABLE team_owners ADD COLUMN last_checked_at DATETIME`)
|
||||
if err != nil && !isColumnExistsError(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isColumnExistsError 判断是否是列已存在的错误
|
||||
func isColumnExistsError(err error) bool {
|
||||
return err != nil && (err.Error() == "duplicate column name: last_checked_at" ||
|
||||
err.Error() == "table team_owners already has a column named last_checked_at")
|
||||
}
|
||||
|
||||
// createTables 创建表
|
||||
func (d *DB) createTables() error {
|
||||
_, err := d.db.Exec(`
|
||||
@@ -54,9 +76,10 @@ func (d *DB) createTables() error {
|
||||
token TEXT,
|
||||
account_id TEXT NOT NULL,
|
||||
status TEXT DEFAULT 'valid',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
last_checked_at DATETIME
|
||||
);
|
||||
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_team_owners_email ON team_owners(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_team_owners_status ON team_owners(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_team_owners_account_id ON team_owners(account_id);
|
||||
@@ -173,7 +196,7 @@ func (d *DB) AddTeamOwners(owners []TeamOwner) (int, error) {
|
||||
|
||||
// GetTeamOwners 获取列表
|
||||
func (d *DB) GetTeamOwners(status string, limit, offset int) ([]TeamOwner, int, error) {
|
||||
query := "SELECT id, email, password, token, account_id, status, created_at FROM team_owners WHERE 1=1"
|
||||
query := "SELECT id, email, password, token, account_id, status, created_at, last_checked_at FROM team_owners WHERE 1=1"
|
||||
countQuery := "SELECT COUNT(*) FROM team_owners WHERE 1=1"
|
||||
args := []interface{}{}
|
||||
|
||||
@@ -202,10 +225,14 @@ func (d *DB) GetTeamOwners(status string, limit, offset int) ([]TeamOwner, int,
|
||||
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)
|
||||
var lastCheckedAt sql.NullTime
|
||||
err := rows.Scan(&owner.ID, &owner.Email, &owner.Password, &owner.Token, &owner.AccountID, &owner.Status, &owner.CreatedAt, &lastCheckedAt)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if lastCheckedAt.Valid {
|
||||
owner.LastCheckedAt = &lastCheckedAt.Time
|
||||
}
|
||||
owners = append(owners, owner)
|
||||
}
|
||||
|
||||
@@ -215,7 +242,7 @@ func (d *DB) GetTeamOwners(status string, limit, offset int) ([]TeamOwner, int,
|
||||
// GetPendingOwners 获取待处理(排除已使用和处理中的)
|
||||
func (d *DB) GetPendingOwners() ([]TeamOwner, error) {
|
||||
rows, err := d.db.Query(`
|
||||
SELECT id, email, password, token, account_id, status, created_at
|
||||
SELECT id, email, password, token, account_id, status, created_at, last_checked_at
|
||||
FROM team_owners WHERE status = 'valid'
|
||||
ORDER BY created_at ASC
|
||||
`)
|
||||
@@ -227,15 +254,62 @@ func (d *DB) GetPendingOwners() ([]TeamOwner, error) {
|
||||
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)
|
||||
var lastCheckedAt sql.NullTime
|
||||
err := rows.Scan(&owner.ID, &owner.Email, &owner.Password, &owner.Token, &owner.AccountID, &owner.Status, &owner.CreatedAt, &lastCheckedAt)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if lastCheckedAt.Valid {
|
||||
owner.LastCheckedAt = &lastCheckedAt.Time
|
||||
}
|
||||
owners = append(owners, owner)
|
||||
}
|
||||
return owners, nil
|
||||
}
|
||||
|
||||
// GetOwnersForBanCheck 获取需要检查封禁状态的母号
|
||||
func (d *DB) GetOwnersForBanCheck(checkIntervalHours int) ([]TeamOwner, error) {
|
||||
// 获取 valid 状态且 超过检查间隔 或 从未检查过 的母号
|
||||
rows, err := d.db.Query(`
|
||||
SELECT id, email, password, token, account_id, status, created_at, last_checked_at
|
||||
FROM team_owners
|
||||
WHERE status = 'valid'
|
||||
AND (last_checked_at IS NULL OR last_checked_at < datetime('now', ?))
|
||||
ORDER BY last_checked_at ASC NULLS FIRST, created_at ASC
|
||||
`, fmt.Sprintf("-%d hours", checkIntervalHours))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var owners []TeamOwner
|
||||
for rows.Next() {
|
||||
var owner TeamOwner
|
||||
var lastCheckedAt sql.NullTime
|
||||
err := rows.Scan(&owner.ID, &owner.Email, &owner.Password, &owner.Token, &owner.AccountID, &owner.Status, &owner.CreatedAt, &lastCheckedAt)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if lastCheckedAt.Valid {
|
||||
owner.LastCheckedAt = &lastCheckedAt.Time
|
||||
}
|
||||
owners = append(owners, owner)
|
||||
}
|
||||
return owners, nil
|
||||
}
|
||||
|
||||
// UpdateOwnerLastCheckedAt 更新母号的最后检查时间
|
||||
func (d *DB) UpdateOwnerLastCheckedAt(id int64) error {
|
||||
_, err := d.db.Exec("UPDATE team_owners SET last_checked_at = CURRENT_TIMESTAMP WHERE id = ?", id)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateOwnerLastCheckedAtByEmail 通过邮箱更新母号的最后检查时间
|
||||
func (d *DB) UpdateOwnerLastCheckedAtByEmail(email string) error {
|
||||
_, err := d.db.Exec("UPDATE team_owners SET last_checked_at = CURRENT_TIMESTAMP WHERE email = ?", email)
|
||||
return err
|
||||
}
|
||||
|
||||
// MarkOwnerAsUsed 标记 Owner 为已使用
|
||||
func (d *DB) MarkOwnerAsUsed(email string) error {
|
||||
_, err := d.db.Exec("UPDATE team_owners SET status = 'used' WHERE email = ?", email)
|
||||
@@ -290,7 +364,7 @@ func (d *DB) ClearUsedOwners() (int64, error) {
|
||||
// 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
|
||||
SELECT id, email, password, token, account_id, status, created_at, last_checked_at
|
||||
FROM team_owners WHERE account_id = '' OR account_id IS NULL
|
||||
ORDER BY created_at DESC
|
||||
`)
|
||||
@@ -302,10 +376,14 @@ func (d *DB) GetOwnersWithoutAccountID() ([]TeamOwner, error) {
|
||||
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)
|
||||
var lastCheckedAt sql.NullTime
|
||||
err := rows.Scan(&owner.ID, &owner.Email, &owner.Password, &owner.Token, &owner.AccountID, &owner.Status, &owner.CreatedAt, &lastCheckedAt)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if lastCheckedAt.Valid {
|
||||
owner.LastCheckedAt = &lastCheckedAt.Time
|
||||
}
|
||||
owners = append(owners, owner)
|
||||
}
|
||||
return owners, nil
|
||||
|
||||
Reference in New Issue
Block a user