feat(assets): 素材库与画布 - 新增可交互通用文件夹

This commit is contained in:
echoD886 committed 2026-08-20 15:16:45 +08:00
1 parent 9ee71d4650
commit 935108edca
35 files changed
+2010 -155

No files matched your search

+109
View File
@@ -24,6 +24,8 @@ var ErrTextReplayQuotaExceeded = errors.New("text replay quota exceeded")
var ErrTextReplayClosed = errors.New("text replay task is closed")
var ErrProjectAssetFolderNotEmpty = errors.New("project asset folder is not empty")
type Repository struct {
db *gorm.DB
}
@@ -968,6 +970,9 @@ func (r *Repository) DeleteProject(userID string, id string) error {
if err := tx.Where("project_id = ?", id).Delete(&model.ProjectAssetLink{}).Error; err != nil {
return err
}
if err := tx.Where("project_id = ?", id).Delete(&model.ProjectAssetFolder{}).Error; err != nil {
return err
}
if err := tx.Where("project_id = ?", id).Delete(&model.ProjectAssetCandidate{}).Error; err != nil {
return err
}
@@ -1156,6 +1161,110 @@ func (r *Repository) ProjectAssets(userID string, projectID string) ([]model.Ass
return assets, err
}
func (r *Repository) ProjectAssetLinks(projectID string) ([]model.ProjectAssetLink, error) {
var links []model.ProjectAssetLink
err := r.db.Where("project_id = ?", projectID).Order("folder_id asc, position asc, created_at asc").Find(&links).Error
return links, err
}
func (r *Repository) ProjectAssetLink(projectID string, assetID string) (*model.ProjectAssetLink, error) {
var link model.ProjectAssetLink
if err := r.db.First(&link, "project_id = ? AND asset_id = ?", projectID, assetID).Error; err != nil {
return nil, err
}
return &link, nil
}
func (r *Repository) NextProjectAssetPosition(projectID string, folderID string) (int, error) {
var result struct{ Maximum int }
err := r.db.Model(&model.ProjectAssetLink{}).
Select("COALESCE(MAX(position), -1) AS maximum").
Where("project_id = ? AND folder_id = ?", projectID, folderID).
Scan(&result).Error
return result.Maximum + 1, err
}
func (r *Repository) MoveProjectAsset(projectID string, assetID string, folderID string, position int) error {
return r.db.Transaction(func(tx *gorm.DB) error {
result := tx.Model(&model.ProjectAssetLink{}).
Where("project_id = ? AND asset_id = ?", projectID, assetID).
Updates(map[string]any{"folder_id": folderID, "position": position})
if result.Error != nil {
return result.Error
}
if result.RowsAffected != 1 {
return gorm.ErrRecordNotFound
}
return tx.Model(&model.Project{}).Where("id = ?", projectID).
Updates(map[string]any{"revision": gorm.Expr("revision + 1"), "updated_at": time.Now()}).Error
})
}
func (r *Repository) ProjectAssetFolders(projectID string) ([]model.ProjectAssetFolder, error) {
var folders []model.ProjectAssetFolder
err := r.db.Where("project_id = ?", projectID).Order("parent_id asc, position asc, created_at asc").Find(&folders).Error
return folders, err
}
func (r *Repository) ProjectAssetFolder(projectID string, folderID string) (*model.ProjectAssetFolder, error) {
var folder model.ProjectAssetFolder
if err := r.db.First(&folder, "id = ? AND project_id = ?", folderID, projectID).Error; err != nil {
return nil, err
}
return &folder, nil
}
func (r *Repository) CreateProjectAssetFolder(folder *model.ProjectAssetFolder) error {
return r.db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(folder).Error; err != nil {
return err
}
return tx.Model(&model.Project{}).Where("id = ?", folder.ProjectID).
Updates(map[string]any{"revision": gorm.Expr("revision + 1"), "updated_at": folder.UpdatedAt}).Error
})
}
func (r *Repository) UpdateProjectAssetFolder(folder *model.ProjectAssetFolder) error {
return r.db.Transaction(func(tx *gorm.DB) error {
result := tx.Model(&model.ProjectAssetFolder{}).
Where("id = ? AND project_id = ?", folder.ID, folder.ProjectID).
Updates(map[string]any{"parent_id": folder.ParentID, "name": folder.Name, "name_key": folder.NameKey, "style": folder.Style, "position": folder.Position, "updated_at": folder.UpdatedAt})
if result.Error != nil {
return result.Error
}
if result.RowsAffected != 1 {
return gorm.ErrRecordNotFound
}
return tx.Model(&model.Project{}).Where("id = ?", folder.ProjectID).
Updates(map[string]any{"revision": gorm.Expr("revision + 1"), "updated_at": folder.UpdatedAt}).Error
})
}
func (r *Repository) DeleteProjectAssetFolder(projectID string, folderID string) error {
return r.db.Transaction(func(tx *gorm.DB) error {
var childCount int64
if err := tx.Model(&model.ProjectAssetFolder{}).Where("project_id = ? AND parent_id = ?", projectID, folderID).Count(&childCount).Error; err != nil {
return err
}
var assetCount int64
if err := tx.Model(&model.ProjectAssetLink{}).Where("project_id = ? AND folder_id = ?", projectID, folderID).Count(&assetCount).Error; err != nil {
return err
}
if childCount > 0 || assetCount > 0 {
return ErrProjectAssetFolderNotEmpty
}
result := tx.Delete(&model.ProjectAssetFolder{}, "id = ? AND project_id = ?", folderID, projectID)
if result.Error != nil {
return result.Error
}
if result.RowsAffected != 1 {
return gorm.ErrRecordNotFound
}
return tx.Model(&model.Project{}).Where("id = ?", projectID).
Updates(map[string]any{"revision": gorm.Expr("revision + 1"), "updated_at": time.Now()}).Error
})
}
// LinkProjectAsset 将首版本、素材领域字段、项目引用和修订号原子提交,避免产生半关联资产。
func (r *Repository) LinkProjectAsset(asset *model.Asset, version *model.AssetVersion, link *model.ProjectAssetLink) (bool, error) {
createdLink := false