Files
ttstd-infinite-canvas/backend/internal/repository/canvas_mutation_receipts.go

48 lines
1.8 KiB
Go

package repository
import (
"errors"
"time"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"yingce/backend/internal/model"
)
// LockCanvasMutation serializes all operations for an existing canvas, including
// deletion. On SQLite this first write also avoids a deferred read transaction
// trying to upgrade its stale snapshot to a write lock.
func (r *Repository) LockCanvasMutation(userID, canvasID string) error {
return r.db.Model(&model.CanvasProject{}).Where("id = ? AND user_id = ?", canvasID, userID).
UpdateColumn("revision", gorm.Expr("revision")).Error
}
func (r *Repository) CanvasMutationReceiptForUser(userID, canvasID, mutationID string) (*model.CanvasMutationReceipt, error) {
var receipt model.CanvasMutationReceipt
err := r.db.First(&receipt, "user_id = ? AND canvas_id = ? AND mutation_id = ?", userID, canvasID, mutationID).Error
return &receipt, err
}
func (r *Repository) TryCreateCanvasMutationReceipt(receipt *model.CanvasMutationReceipt) (bool, error) {
if receipt.CreatedAt.IsZero() {
receipt.CreatedAt = time.Now().UTC()
}
result := r.db.Clauses(clause.OnConflict{Columns: []clause.Column{{Name: "user_id"}, {Name: "canvas_id"}, {Name: "mutation_id"}}, DoNothing: true}).Create(receipt)
return result.RowsAffected == 1, result.Error
}
func (r *Repository) FinalizeCanvasMutationReceipt(receipt *model.CanvasMutationReceipt) error {
if receipt.ResultJSON == "" {
return errors.New("canvas mutation receipt requires a committed result")
}
result := r.db.Model(&model.CanvasMutationReceipt{}).Where("id = ? AND payload_digest = ?", receipt.ID, receipt.PayloadDigest).
Updates(map[string]any{"result_json": receipt.ResultJSON, "revision": receipt.Revision})
if result.Error != nil {
return result.Error
}
if result.RowsAffected != 1 {
return errors.New("canvas mutation receipt was not finalized")
}
return nil
}