feat: 添加媒体资源处理的异步任务及相关逻辑

This commit is contained in:
2025-12-23 12:04:30 +08:00
parent 7c2b937352
commit 1dba706022
8 changed files with 317 additions and 13 deletions

View File

@@ -12,9 +12,11 @@ import (
"quyun/v2/app/errorx"
tenant_dto "quyun/v2/app/http/tenant/dto"
jobs_args "quyun/v2/app/jobs/args"
"quyun/v2/app/requests"
"quyun/v2/database/models"
"quyun/v2/pkg/consts"
provider_job "quyun/v2/providers/job"
pkgerrors "github.com/pkg/errors"
"github.com/samber/lo"
@@ -29,7 +31,30 @@ import (
// mediaAsset 提供媒体资源上传初始化等能力(上传/处理链路会在后续里程碑补齐)。
//
// @provider
type mediaAsset struct{}
type mediaAsset struct {
job *provider_job.Job
}
func IsMediaAssetProcessJobNonRetryableError(err error) bool {
var appErr *errorx.AppError
if !errors.As(err, &appErr) {
return false
}
switch appErr.Code {
case errorx.CodeInvalidParameter,
errorx.CodeRecordNotFound,
errorx.CodeStatusConflict,
errorx.CodePreconditionFailed,
errorx.CodePermissionDenied:
return true
default:
return false
}
}
func (s *mediaAsset) enqueueMediaAssetProcessJob(args jobs_args.MediaAssetProcessJob) error {
return s.job.Add(args)
}
func mediaAssetTransitionAllowed(from, to consts.MediaAssetStatus) bool {
switch from {
@@ -218,7 +243,11 @@ func (s *mediaAsset) AdminUploadComplete(
"asset_id": assetID,
}).Info("services.media_asset.admin.upload_complete")
var out models.MediaAsset
var (
out models.MediaAsset
needEnqueue bool
enqueueArgs jobs_args.MediaAssetProcessJob
)
err := models.Q.Transaction(func(tx *models.Query) error {
tbl, query := tx.MediaAsset.QueryContext(ctx)
@@ -241,11 +270,16 @@ func (s *mediaAsset) AdminUploadComplete(
return errorx.ErrPreconditionFailed.WithMsg("media asset deleted")
}
// 幂等:重复 upload_complete 时返回现态。
// 幂等:重复 upload_complete 时返回现态;但只要处于 processing就允许再次触发入队用于“上次入队失败”的补偿重试
switch m.Status {
case consts.MediaAssetStatusProcessing, consts.MediaAssetStatusReady, consts.MediaAssetStatusFailed:
case consts.MediaAssetStatusReady, consts.MediaAssetStatusFailed:
out = *m
return nil
case consts.MediaAssetStatusProcessing:
out = *m
needEnqueue = true
enqueueArgs = jobs_args.MediaAssetProcessJob{TenantID: tenantID, AssetID: assetID}
return nil
case consts.MediaAssetStatusUploaded:
// allowed
default:
@@ -294,19 +328,32 @@ func (s *mediaAsset) AdminUploadComplete(
m.UpdatedAt = now
out = *m
// 触发异步处理(当前为 stub后续接入队列/任务系统时在此处落任务并保持幂等。
logrus.WithFields(logrus.Fields{
"tenant_id": tenantID,
"user_id": operatorUserID,
"asset_id": assetID,
"status": m.Status,
}).Info("services.media_asset.process.triggered")
needEnqueue = true
enqueueArgs = jobs_args.MediaAssetProcessJob{TenantID: tenantID, AssetID: assetID}
return nil
})
if err != nil {
return nil, err
}
if needEnqueue {
// 注意River 的唯一约束会将重复入队“软跳过”,因此这里允许多次触发以补偿偶发入队失败。
if err := s.enqueueMediaAssetProcessJob(enqueueArgs); err != nil {
logrus.WithFields(logrus.Fields{
"tenant_id": tenantID,
"user_id": operatorUserID,
"asset_id": assetID,
}).WithError(err).Warn("services.media_asset.process.enqueue_failed")
return nil, err
}
logrus.WithFields(logrus.Fields{
"tenant_id": tenantID,
"user_id": operatorUserID,
"asset_id": assetID,
}).Info("services.media_asset.process.enqueued")
}
return &out, nil
}