feat: Introduce MediaAssetVariant for better asset management

- Added MediaAssetVariant enum with values 'main' and 'preview'.
- Updated media asset service logic to utilize MediaAssetVariant for variant handling.
- Refactored database models and queries to include variant and source_asset_id fields.
- Enhanced validation for asset variants in upload and processing functions.
- Updated Swagger documentation to reflect new variant structure and descriptions.
- Implemented necessary database migrations to support the new variant constraints.
This commit is contained in:
2025-12-22 19:27:31 +08:00
parent d04e2ee693
commit 2cc823d3a8
14 changed files with 439 additions and 171 deletions

View File

@@ -34,13 +34,13 @@ type ContentDetailResult struct {
HasAccess bool
}
func requiredMediaAssetVariantForRole(role consts.ContentAssetRole) string {
func requiredMediaAssetVariantForRole(role consts.ContentAssetRole) consts.MediaAssetVariant {
switch role {
case consts.ContentAssetRolePreview:
return mediaAssetVariantPreview
return consts.MediaAssetVariantPreview
default:
// main/cover 一律要求 main 产物,避免误把 preview 绑定成正片/封面。
return mediaAssetVariantMain
return consts.MediaAssetVariantMain
}
}
@@ -226,23 +226,9 @@ func (s *content) AttachAsset(ctx context.Context, tenantID, userID, contentID,
}
// C2 规则preview 必须绑定独立产物media_assets.variant=previewmain/cover 必须为 main。
var assetRow struct {
Variant string `gorm:"column:variant"`
SourceAssetID *int64 `gorm:"column:source_asset_id"`
}
if err := _db.WithContext(ctx).
Table(models.TableNameMediaAsset).
Select("variant, source_asset_id").
Where("tenant_id = ? AND id = ? AND deleted_at IS NULL", tenantID, assetID).
Take(&assetRow).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errorx.ErrRecordNotFound.WithMsg("media asset not found")
}
return nil, err
}
variant := assetRow.Variant
variant := asset.Variant
if variant == "" {
variant = mediaAssetVariantMain
variant = consts.MediaAssetVariantMain
}
requiredVariant := requiredMediaAssetVariantForRole(role)
if variant != requiredVariant {
@@ -250,31 +236,29 @@ func (s *content) AttachAsset(ctx context.Context, tenantID, userID, contentID,
}
// 关联规则preview 产物必须声明来源 mainmain/cover 不允许带来源。
if role == consts.ContentAssetRolePreview {
if assetRow.SourceAssetID == nil || *assetRow.SourceAssetID <= 0 {
if asset.SourceAssetID <= 0 {
return nil, errorx.ErrPreconditionFailed.WithMsg("preview asset must have source_asset_id")
}
var srcRow struct {
Variant string `gorm:"column:variant"`
}
if err := _db.WithContext(ctx).
Table(models.TableNameMediaAsset).
Select("variant").
Where("tenant_id = ? AND id = ? AND deleted_at IS NULL", tenantID, *assetRow.SourceAssetID).
Take(&srcRow).Error; err != nil {
src, err := queryAsset.Where(
tblAsset.TenantID.Eq(tenantID),
tblAsset.ID.Eq(asset.SourceAssetID),
tblAsset.DeletedAt.IsNull(),
).First()
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errorx.ErrRecordNotFound.WithMsg("preview source asset not found")
}
return nil, err
}
srcVariant := srcRow.Variant
srcVariant := src.Variant
if srcVariant == "" {
srcVariant = mediaAssetVariantMain
srcVariant = consts.MediaAssetVariantMain
}
if srcVariant != mediaAssetVariantMain {
if srcVariant != consts.MediaAssetVariantMain {
return nil, errorx.ErrPreconditionFailed.WithMsg("preview source asset must be main variant")
}
} else {
if assetRow.SourceAssetID != nil && *assetRow.SourceAssetID > 0 {
if asset.SourceAssetID > 0 {
return nil, errorx.ErrPreconditionFailed.WithMsg("main/cover asset must not have source_asset_id")
}
}