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

161 lines
5.7 KiB
Go
Raw 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.
// 素材与画布项目的基础读写。
//
// 素材的彻底删除(含引用、Outbox)在 resource_reference.go 的 DeleteAssetsAndResources。
package repository
import (
"time"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"yingce/backend/internal/model"
)
func (r *Repository) Assets(userID string) ([]model.Asset, error) {
var assets []model.Asset
err := r.db.Order("updated_at desc").Find(&assets, "user_id = ?", userID).Error
return assets, err
}
func (r *Repository) AssetSummaries(userID string) ([]model.Asset, error) {
var assets []model.Asset
err := r.db.Select("id", "folder_id", "kind", "category", "status", "primary_version_id", "title", "created_at", "updated_at").Order("updated_at desc").Find(&assets, "user_id = ?", userID).Error
return assets, err
}
func (r *Repository) AssetForUser(userID string, id string) (*model.Asset, error) {
var asset model.Asset
if err := r.db.First(&asset, "id = ? AND user_id = ?", id, userID).Error; err != nil {
return nil, err
}
return &asset, nil
}
func (r *Repository) AssetsForUserIDs(userID string, ids []string) ([]model.Asset, error) {
if len(ids) == 0 {
return nil, nil
}
var assets []model.Asset
err := r.db.Find(&assets, "user_id = ? AND id IN ?", userID, ids).Error
return assets, err
}
func (r *Repository) UpsertAsset(asset *model.Asset) error {
result := r.db.Model(&model.Asset{}).
Where("id = ? AND user_id = ?", asset.ID, asset.UserID).
Updates(map[string]any{"folder_id": asset.FolderID, "kind": asset.Kind, "category": asset.Category, "status": asset.Status, "primary_version_id": asset.PrimaryVersionID, "title": asset.Title, "payload_json": asset.PayloadJSON, "updated_at": asset.UpdatedAt})
if result.Error != nil || result.RowsAffected > 0 {
return result.Error
}
return r.db.Create(asset).Error
}
func (r *Repository) DeleteAsset(userID string, id string) error {
return r.DeleteAssetAndResources(userID, id, nil, nil, false)
}
func (r *Repository) FindExpiredArchivedAssets(cutoff time.Time, limit int) ([]model.Asset, error) {
var assets []model.Asset
if limit <= 0 {
limit = 100
}
err := r.db.Where("status = ? AND updated_at <= ?", model.AssetVersionStatusArchived, cutoff).
Order("updated_at asc, id asc").
Limit(limit).
Find(&assets).Error
return assets, err
}
func (r *Repository) ReplaceAssets(userID string, assets []model.Asset) error {
return r.db.Transaction(func(tx *gorm.DB) error {
if err := tx.Delete(&model.Asset{}, "user_id = ?", userID).Error; err != nil {
return err
}
if len(assets) == 0 {
return nil
}
return tx.Create(&assets).Error
})
}
func (r *Repository) CanvasProjects(userID string) ([]model.CanvasProject, error) {
var projects []model.CanvasProject
err := r.db.Order("updated_at desc").Find(&projects, "user_id = ?", userID).Error
return projects, err
}
func (r *Repository) CanvasProjectSummaries(userID string) ([]model.CanvasProject, error) {
var projects []model.CanvasProject
err := r.db.Select("id", "title", "revision", "created_at", "updated_at").Order("updated_at desc").Find(&projects, "user_id = ?", userID).Error
return projects, err
}
func (r *Repository) CanvasProjectForUser(userID string, id string) (*model.CanvasProject, error) {
var project model.CanvasProject
if err := r.db.First(&project, "id = ? AND user_id = ?", id, userID).Error; err != nil {
return nil, err
}
return &project, nil
}
func (r *Repository) UpsertCanvasProject(project *model.CanvasProject) error {
expected := project.Revision
if expected < 0 {
return ErrCanvasRevisionConflict
}
if expected == 0 {
created := *project
created.Revision = 1
result := r.db.Clauses(clause.OnConflict{DoNothing: true}).Create(&created)
if result.Error != nil {
return result.Error
}
if result.RowsAffected != 1 {
return ErrCanvasRevisionConflict
}
project.Revision = 1
return nil
}
// The revision predicate and increment must be in the same SQL statement.
// A missing row is a conflict, never an invitation to recreate a deleted canvas.
result := r.db.Model(&model.CanvasProject{}).
Where("id = ? AND user_id = ? AND revision = ?", project.ID, project.UserID, expected).
Updates(map[string]any{"project_id": project.ProjectID, "title": project.Title, "payload_json": project.PayloadJSON, "updated_at": project.UpdatedAt, "revision": expected + 1})
if result.Error != nil {
return result.Error
}
if result.RowsAffected != 1 {
return ErrCanvasRevisionConflict
}
project.Revision = expected + 1
return nil
}
func (r *Repository) DeleteCanvasProject(userID string, id string) error {
return r.db.Transaction(func(tx *gorm.DB) error {
// Serialize deletion with saves before reading the history IDs to remove.
if err := tx.Model(&model.CanvasProject{}).Where("user_id = ? AND id = ?", userID, id).UpdateColumn("revision", gorm.Expr("revision")).Error; err != nil {
return err
}
var snapshotIDs []string
if err := tx.Model(&model.CanvasSnapshot{}).Where("user_id = ? AND canvas_id = ?", userID, id).Pluck("id", &snapshotIDs).Error; err != nil {
return err
}
if err := deleteCanvasSnapshots(tx, snapshotIDs); err != nil {
return err
}
if err := tx.Where("user_id = ? AND project_id = ?", userID, id).Delete(&model.CanvasShare{}).Error; err != nil {
return err
}
if err := tx.Where("canvas_id = ?", id).Delete(&model.CanvasUnitLink{}).Error; err != nil {
return err
}
// 任务和会话是审计记录,不随独立画布实体保留归属 ID,避免删除后继续挂住画布上下文。
if err := tx.Model(&model.Task{}).Where("user_id = ? AND project_id = ?", userID, id).Update("project_id", "").Error; err != nil {
return err
}
return tx.Delete(&model.CanvasProject{}, "id = ? AND user_id = ?", id, userID).Error
})
}