package repository import ( "context" "database/sql" "errors" "github.com/Wei-Shaw/sub2api/internal/service" ) // soraAccountRepository 实现 service.SoraAccountRepository 接口。 // 使用原生 SQL 操作 sora_accounts 表,因为该表不在 Ent ORM 管理范围内。 // // 设计说明: // - sora_accounts 表是独立迁移创建的,不通过 Ent Schema 管理 // - 使用 ON CONFLICT (account_id) DO UPDATE 实现 Upsert 语义 // - 与 accounts 主表通过外键关联,ON DELETE CASCADE 确保级联删除 type soraAccountRepository struct { sql *sql.DB } // NewSoraAccountRepository 创建 Sora 账号扩展表仓储实例 func NewSoraAccountRepository(sqlDB *sql.DB) service.SoraAccountRepository { return &soraAccountRepository{sql: sqlDB} } // Upsert 创建或更新 Sora 账号扩展信息 // 使用 PostgreSQL ON CONFLICT ... DO UPDATE 实现原子性 upsert func (r *soraAccountRepository) Upsert(ctx context.Context, accountID int64, updates map[string]any) error { accessToken, accessOK := updates["access_token"].(string) refreshToken, refreshOK := updates["refresh_token"].(string) sessionToken, sessionOK := updates["session_token"].(string) if !accessOK || accessToken == "" || !refreshOK || refreshToken == "" { if !sessionOK { return errors.New("缺少 access_token/refresh_token,且未提供可更新字段") } result, err := r.sql.ExecContext(ctx, ` UPDATE sora_accounts SET session_token = CASE WHEN $2 = '' THEN session_token ELSE $2 END, updated_at = NOW() WHERE account_id = $1 `, accountID, sessionToken) if err != nil { return err } rows, err := result.RowsAffected() if err != nil { return err } if rows == 0 { return errors.New("sora_accounts 记录不存在,无法仅更新 session_token") } return nil } _, err := r.sql.ExecContext(ctx, ` INSERT INTO sora_accounts (account_id, access_token, refresh_token, session_token, created_at, updated_at) VALUES ($1, $2, $3, $4, NOW(), NOW()) ON CONFLICT (account_id) DO UPDATE SET access_token = EXCLUDED.access_token, refresh_token = EXCLUDED.refresh_token, session_token = CASE WHEN EXCLUDED.session_token = '' THEN sora_accounts.session_token ELSE EXCLUDED.session_token END, updated_at = NOW() `, accountID, accessToken, refreshToken, sessionToken) return err } // GetByAccountID 根据账号 ID 获取 Sora 扩展信息 func (r *soraAccountRepository) GetByAccountID(ctx context.Context, accountID int64) (*service.SoraAccount, error) { rows, err := r.sql.QueryContext(ctx, ` SELECT account_id, access_token, refresh_token, COALESCE(session_token, '') FROM sora_accounts WHERE account_id = $1 `, accountID) if err != nil { return nil, err } defer func() { _ = rows.Close() }() if !rows.Next() { return nil, nil // 记录不存在 } var sa service.SoraAccount if err := rows.Scan(&sa.AccountID, &sa.AccessToken, &sa.RefreshToken, &sa.SessionToken); err != nil { return nil, err } return &sa, nil } // Delete 删除 Sora 账号扩展信息 func (r *soraAccountRepository) Delete(ctx context.Context, accountID int64) error { _, err := r.sql.ExecContext(ctx, ` DELETE FROM sora_accounts WHERE account_id = $1 `, accountID) return err }