161 lines
5.7 KiB
Go
161 lines
5.7 KiB
Go
// 素材与画布项目的基础读写。
|
||
//
|
||
// 素材的彻底删除(含引用、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
|
||
})
|
||
}
|