初始化

This commit is contained in:
Ran
2026-08-25 17:59:42 +08:00
commit 4b7380dd9b
408 changed files with 327400 additions and 0 deletions
+152
View File
@@ -0,0 +1,152 @@
// 创作媒体服务,封装媒体上传前的所有权检查、关联更新和旧媒体清理事务。
package service
import (
"strings"
"juhe-factory/api/internal/model"
"github.com/google/uuid"
"gorm.io/gorm"
)
// MediaUploadTarget 汇总上传前需要的业务对象及待替换媒体信息。
type MediaUploadTarget struct {
Project *model.CreativeProject
Episode *model.ProjectEpisode
Asset *model.ProjectAsset
OldMediaID *uuid.UUID
OldObjectKey string
}
// MediaObject 查询当前用户有权播放或下载的媒体对象键。
func (s *Creative) MediaObject(userID, mediaID uuid.UUID) (string, error) {
var objectKey string
err := s.DB.Table("media_assets").
Where("id=? AND owner_user_id=? AND deleted_at IS NULL", mediaID, userID).
Pluck("object_key", &objectKey).Error
if err != nil {
return "", err
}
if strings.TrimSpace(objectKey) == "" {
return "", gorm.ErrRecordNotFound
}
return objectKey, nil
}
// PrepareRedrawMediaUpload 校验重绘项目所有权,并返回指定媒体类型的替换信息。
func (s *Creative) PrepareRedrawMediaUpload(userID, projectID uuid.UUID, kind string) (MediaUploadTarget, error) {
var project model.CreativeProject
if err := s.DB.Where("id=? AND user_id=? AND project_type='video_redraw' AND deleted_at IS NULL", projectID, userID).Take(&project).Error; err != nil {
return MediaUploadTarget{}, err
}
oldMediaID := project.SourceVideoAssetID
if kind == "subtitle" {
oldMediaID = project.SubtitleAssetID
}
oldObjectKey, err := s.mediaObjectKey(oldMediaID)
if err != nil {
return MediaUploadTarget{}, err
}
return MediaUploadTarget{Project: &project, OldMediaID: oldMediaID, OldObjectKey: oldObjectKey}, nil
}
// PersistRedrawMediaUpload 在事务中保存新媒体、更新项目关联并删除旧媒体记录。
func (s *Creative) PersistRedrawMediaUpload(projectID uuid.UUID, kind, currentStatus string, media *model.MediaAsset, oldMediaID *uuid.UUID) error {
column, status := "source_video_asset_id", "uploaded"
if kind == "subtitle" {
column, status = "subtitle_asset_id", currentStatus
}
return s.DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(media).Error; err != nil {
return err
}
updates := map[string]any{column: media.ID, "redraw_status": status}
if err := tx.Table("creative_projects").Where("id=?", projectID).Updates(updates).Error; err != nil {
return err
}
return deleteReplacedMedia(tx, oldMediaID)
})
}
// PrepareEpisodeMediaUpload 校验剧集与项目所有权,并返回指定媒体类型的替换信息。
func (s *Creative) PrepareEpisodeMediaUpload(userID, projectID, episodeID uuid.UUID, kind string) (MediaUploadTarget, error) {
var episode model.ProjectEpisode
err := s.DB.Table("project_episodes e").Joins("JOIN creative_projects p ON p.id=e.project_id").
Where("e.id=? AND e.project_id=? AND e.deleted_at IS NULL AND p.user_id=? AND p.deleted_at IS NULL", episodeID, projectID, userID).
Select("e.*").First(&episode).Error
if err != nil {
return MediaUploadTarget{}, err
}
oldMediaID := episode.SourceVideoAssetID
if kind == "subtitle" {
oldMediaID = episode.SubtitleAssetID
}
oldObjectKey, err := s.mediaObjectKey(oldMediaID)
if err != nil {
return MediaUploadTarget{}, err
}
return MediaUploadTarget{Episode: &episode, OldMediaID: oldMediaID, OldObjectKey: oldObjectKey}, nil
}
// PersistEpisodeMediaUpload 在事务中保存新媒体、更新剧集关联并删除旧媒体记录。
func (s *Creative) PersistEpisodeMediaUpload(episodeID uuid.UUID, kind string, status string, media *model.MediaAsset, oldMediaID *uuid.UUID) error {
column := "source_video_asset_id"
if kind == "subtitle" {
column = "subtitle_asset_id"
}
return s.DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(media).Error; err != nil {
return err
}
if err := tx.Table("project_episodes").Where("id=?", episodeID).Updates(map[string]any{column: media.ID, "status": status}).Error; err != nil {
return err
}
return deleteReplacedMedia(tx, oldMediaID)
})
}
// PrepareAssetMediaUpload 校验普通创作项目中的资产所有权并返回资产信息。
func (s *Creative) PrepareAssetMediaUpload(userID, projectID, assetID uuid.UUID) (MediaUploadTarget, error) {
var asset model.ProjectAsset
err := s.DB.Table("project_assets a").Joins("JOIN creative_projects p ON p.id=a.project_id").
Where("a.id=? AND a.project_id=? AND a.deleted_at IS NULL AND p.user_id=? AND p.project_type<>'video_redraw' AND p.deleted_at IS NULL", assetID, projectID, userID).
Select("a.*").First(&asset).Error
if err != nil {
return MediaUploadTarget{}, err
}
return MediaUploadTarget{Asset: &asset}, nil
}
// AttachUploadedAssetAudio 在事务中保存上传音频并更新资产关联。
func (s *Creative) AttachUploadedAssetAudio(assetID uuid.UUID, media *model.MediaAsset) error {
return s.DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(media).Error; err != nil {
return err
}
return tx.Table("project_assets").Where("id=?", assetID).Update("audio_asset_id", media.ID).Error
})
}
// mediaObjectKey 查询待替换媒体的对象键;空媒体标识表示无需清理。
func (s *Creative) mediaObjectKey(mediaID *uuid.UUID) (string, error) {
if mediaID == nil {
return "", nil
}
var objectKey string
if err := s.DB.Table("media_assets").Where("id=?", *mediaID).Pluck("object_key", &objectKey).Error; err != nil {
return "", err
}
return objectKey, nil
}
// deleteReplacedMedia 删除旧媒体的渠道缓存和媒体记录,供上传事务复用。
func deleteReplacedMedia(tx *gorm.DB, mediaID *uuid.UUID) error {
if mediaID == nil {
return nil
}
if err := tx.Exec("DELETE FROM channel_asset_cache WHERE media_asset_id=?", *mediaID).Error; err != nil {
return err
}
return tx.Exec("DELETE FROM media_assets WHERE id=?", *mediaID).Error
}