88 lines
2.0 KiB
Go
88 lines
2.0 KiB
Go
package license
|
||
|
||
import (
|
||
"context"
|
||
"database/sql"
|
||
"fmt"
|
||
"strings"
|
||
)
|
||
|
||
// ConsumedMirror 第二持久化(如 Postgres):leases 目录被删后仍能识别已用过的 id。
|
||
type ConsumedMirror interface {
|
||
Load(ctx context.Context) (activeID string, ids []string, err error)
|
||
Save(ctx context.Context, activeID string, ids []string) error
|
||
}
|
||
|
||
type pgMirror struct {
|
||
db *sql.DB
|
||
}
|
||
|
||
func NewPGMirror(db *sql.DB) ConsumedMirror {
|
||
if db == nil {
|
||
return nil
|
||
}
|
||
return &pgMirror{db: db}
|
||
}
|
||
|
||
func (p *pgMirror) Load(ctx context.Context) (string, []string, error) {
|
||
rows, err := p.db.QueryContext(ctx, `
|
||
SELECT lease_id, active FROM platform_meta.license_consumed`)
|
||
if err != nil {
|
||
return "", nil, err
|
||
}
|
||
defer rows.Close()
|
||
var active string
|
||
var ids []string
|
||
for rows.Next() {
|
||
var id string
|
||
var isActive bool
|
||
if err := rows.Scan(&id, &isActive); err != nil {
|
||
return "", nil, err
|
||
}
|
||
ids = append(ids, id)
|
||
if isActive {
|
||
active = id
|
||
}
|
||
}
|
||
return active, ids, rows.Err()
|
||
}
|
||
|
||
func (p *pgMirror) Save(ctx context.Context, activeID string, ids []string) error {
|
||
tx, err := p.db.BeginTx(ctx, nil)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer func() { _ = tx.Rollback() }()
|
||
if _, err := tx.ExecContext(ctx, `DELETE FROM platform_meta.license_consumed`); err != nil {
|
||
return err
|
||
}
|
||
for _, id := range ids {
|
||
id = trimID(id)
|
||
if id == "" {
|
||
continue
|
||
}
|
||
_, err := tx.ExecContext(ctx, `
|
||
INSERT INTO platform_meta.license_consumed(lease_id, active, updated_at)
|
||
VALUES ($1, $2, now())
|
||
ON CONFLICT (lease_id) DO UPDATE SET active=EXCLUDED.active, updated_at=now()`,
|
||
id, id == activeID)
|
||
if err != nil {
|
||
return fmt.Errorf("license_consumed upsert: %w", err)
|
||
}
|
||
}
|
||
if activeID != "" {
|
||
_, err := tx.ExecContext(ctx, `
|
||
INSERT INTO platform_meta.license_consumed(lease_id, active, updated_at)
|
||
VALUES ($1, true, now())
|
||
ON CONFLICT (lease_id) DO UPDATE SET active=true, updated_at=now()`, activeID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return tx.Commit()
|
||
}
|
||
|
||
func trimID(s string) string {
|
||
return strings.TrimSpace(s)
|
||
}
|