Files
ai_site/platform/internal/dbsync/rollback.go
whm 76cdcd760e feat: ship loose-offline dbsync (validate, agent push, LWW audit)
Add UUID/FK channel checks, agent whitelist/push APIs, bindings, super-admin LWW audit with rollback, reconcile rate limits, and sync docs. Default customers stay opt-in; company conflict UI is removed.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-31 17:54:14 +08:00

80 lines
2.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package dbsync
import (
"context"
"fmt"
"strings"
"time"
"github.com/google/uuid"
)
// RollbackLwwOverride 超管按落败快照回滚线上 A 单行。
// 仅 outcome=applied_source 可回滚:把 loser_payload 写回 remote无快照则按 pk 删除。
func RollbackLwwOverride(ctx context.Context, store *FileStore, overrideID string) (*LwwOverride, error) {
if store == nil {
return nil, fmt.Errorf("store is nil")
}
o, err := store.GetLwwOverride(overrideID)
if err != nil {
return nil, err
}
if o.Outcome != OutcomeApplied {
return nil, fmt.Errorf("仅「源端覆盖」记录可回滚(当前 outcome=%s", o.Outcome)
}
if strings.TrimSpace(o.ChannelID) == "" {
return nil, fmt.Errorf("override missing channel_id")
}
ch, err := store.GetChannel(o.ChannelID)
if err != nil {
return nil, fmt.Errorf("channel: %w", err)
}
table := strings.TrimSpace(o.Table)
pkCol := "id"
if ch.PKColumns != nil && strings.TrimSpace(ch.PKColumns[table]) != "" {
pkCol = ch.PKColumns[table]
}
db, err := Open(ch.Remote.Driver, ch.Remote.DSN)
if err != nil {
return nil, fmt.Errorf("open remote: %w", err)
}
defer db.Close()
if err := EnsureMeta(ctx, db, ch.Remote.Driver); err != nil {
return nil, err
}
before := SnapshotTargetRow(ctx, db, ch.Remote.Driver, table, pkCol, o.RowPK)
ver := time.Now().UnixNano()
loser := strings.TrimSpace(o.LoserPayload)
op := "upsert"
payload := loser
if loser == "" || loser == "{}" {
op = "delete"
payload = fmt.Sprintf(`{%q:%q}`, pkCol, o.RowPK)
}
if err := ApplyChange(ctx, db, ch.Remote.Driver, table, pkCol, op, payload, ver); err != nil {
return nil, fmt.Errorf("apply rollback: %w", err)
}
rec := LwwOverride{
ID: uuid.NewString(),
TenantID: o.TenantID,
ChannelID: o.ChannelID,
Table: o.Table,
RowPK: o.RowPK,
Op: op,
Entry: EntryRollback,
Policy: "manual_rollback",
Outcome: OutcomeRolled,
LoserPayload: before,
WinnerPayload: payload,
TargetVer: o.SourceVer,
SourceVer: ver,
CreatedAt: time.Now().UTC(),
}
if err := store.AddLwwOverride(rec); err != nil {
return nil, err
}
return &rec, nil
}