混剪功能
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
package cn.iocoder.yudao.module.tik.media;
|
||||
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.aliyun.ice20201109.Client;
|
||||
import com.aliyun.ice20201109.models.*;
|
||||
import com.aliyun.teaopenapi.models.Config;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
@@ -26,6 +26,8 @@ import java.util.*;
|
||||
* <version>1.2.9</version>
|
||||
* </dependency>
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class BatchProduceAlignment {
|
||||
|
||||
static final String regionId = "cn-hangzhou";
|
||||
@@ -97,36 +99,14 @@ public class BatchProduceAlignment {
|
||||
|
||||
public List<String> batchProduceAlignment(String title,String text,String[] videoArray,String[] bgMusicArray,int produceCount) throws Exception {
|
||||
|
||||
// 批量提交任务
|
||||
List<String> jobIds = new ArrayList<>();
|
||||
// 批量提交任务,返回 "jobId:url" 格式
|
||||
List<String> jobIdWithUrls = new ArrayList<>();
|
||||
for (int i = 0; i < produceCount; i++) {
|
||||
String jobId = produceSingleVideo(title, text, videoArray, bgMusicArray);
|
||||
jobIds.add(jobId);
|
||||
String jobIdWithUrl = produceSingleVideo(title, text, videoArray, bgMusicArray);
|
||||
jobIdWithUrls.add(jobIdWithUrl);
|
||||
}
|
||||
while (true) {
|
||||
Thread.sleep(3000);
|
||||
boolean allFinished = true;
|
||||
for (int i = 0; i < jobIds.size(); i++) {
|
||||
String jobId = jobIds.get(i);
|
||||
GetMediaProducingJobRequest req = new GetMediaProducingJobRequest();
|
||||
req.setJobId(jobId);
|
||||
GetMediaProducingJobResponse response = iceClient.getMediaProducingJob(req);
|
||||
GetMediaProducingJobResponseBody.GetMediaProducingJobResponseBodyMediaProducingJob mediaProducingJob = response.getBody().getMediaProducingJob();
|
||||
String status = mediaProducingJob.getStatus();
|
||||
System.out.println("jobId: " + mediaProducingJob.getJobId() + ", status:" + status);
|
||||
if ("Failed".equalsIgnoreCase(status)) {
|
||||
throw new Exception("Produce failed. jobid: " + mediaProducingJob.getJobId());
|
||||
}
|
||||
if (!"Success".equalsIgnoreCase(status)) {
|
||||
allFinished = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allFinished) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return jobIds;
|
||||
// 改为异步模式,不在这里等待
|
||||
return jobIdWithUrls;
|
||||
}
|
||||
|
||||
public String produceSingleVideo(String title, String text, String[] videoArray, String[] bgMusicArray) throws Exception {
|
||||
@@ -166,8 +146,41 @@ public class BatchProduceAlignment {
|
||||
request.setTimeline(timeline);
|
||||
request.setOutputMediaConfig(outputMediaConfig);
|
||||
SubmitMediaProducingJobResponse response = iceClient.submitMediaProducingJob(request);
|
||||
//ystem.out.println("start job. jobid: " + response.getBody().getJobId() + ", outputMediaUrl: " + outputMediaUrl);
|
||||
log.info("start job. jobid: " + response.getBody().getJobId() + ", outputMediaUrl: " + outputMediaUrl);
|
||||
return response.getBody().getJobId() + " : " + outputMediaUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查单个任务状态
|
||||
*
|
||||
* @param jobId 任务ID
|
||||
* @return 任务状态:Pending/Running/Success/Failed
|
||||
*/
|
||||
public String checkJobStatus(String jobId) throws Exception {
|
||||
if (iceClient == null) {
|
||||
initClient();
|
||||
}
|
||||
|
||||
GetMediaProducingJobRequest req = new GetMediaProducingJobRequest();
|
||||
req.setJobId(jobId);
|
||||
GetMediaProducingJobResponse response = iceClient.getMediaProducingJob(req);
|
||||
GetMediaProducingJobResponseBody.GetMediaProducingJobResponseBodyMediaProducingJob mediaProducingJob = response.getBody().getMediaProducingJob();
|
||||
String status = mediaProducingJob.getStatus();
|
||||
log.debug("jobId: {}, status: {}", mediaProducingJob.getJobId(), status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从任务ID中获取输出URL
|
||||
*
|
||||
* @param jobIdWithUrl jobId : url 格式的字符串
|
||||
* @return 输出URL
|
||||
*/
|
||||
public String extractOutputUrl(String jobIdWithUrl) {
|
||||
if (jobIdWithUrl == null || !jobIdWithUrl.contains(" : ")) {
|
||||
return null;
|
||||
}
|
||||
return jobIdWithUrl.split(" : ")[1];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package cn.iocoder.yudao.module.tik.mix.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* 混剪任务配置
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
public class MixTaskConfig {
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package cn.iocoder.yudao.module.tik.mix.constants;
|
||||
|
||||
/**
|
||||
* 混剪任务常量
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class MixTaskConstants {
|
||||
|
||||
/**
|
||||
* 任务状态
|
||||
*/
|
||||
public static final String STATUS_PENDING = "pending";
|
||||
public static final String STATUS_RUNNING = "running";
|
||||
public static final String STATUS_SUCCESS = "success";
|
||||
public static final String STATUS_FAILED = "failed";
|
||||
|
||||
/**
|
||||
* 任务进度
|
||||
*/
|
||||
public static final int PROGRESS_SUBMITTED = 10;
|
||||
public static final int PROGRESS_UPLOADED = 50;
|
||||
public static final int PROGRESS_COMPLETED = 100;
|
||||
|
||||
/**
|
||||
* 定时任务配置
|
||||
*/
|
||||
public static final String CRON_CHECK_STATUS = "*/30 * * * * ?";
|
||||
|
||||
private MixTaskConstants() {
|
||||
// 防止实例化
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package cn.iocoder.yudao.module.tik.mix.controller;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import cn.iocoder.yudao.module.tik.mix.service.MixTaskService;
|
||||
import cn.iocoder.yudao.module.tik.mix.vo.*;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
/**
|
||||
* 混剪任务 Controller
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Tag(name = "混剪任务", description = "混剪任务相关接口")
|
||||
@RestController
|
||||
@RequestMapping("/api/mix")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class MixTaskController {
|
||||
|
||||
private final MixTaskService mixTaskService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建混剪任务")
|
||||
public CommonResult<Long> createMixTask(@Valid @RequestBody MixTaskSaveReqVO createReqVO) {
|
||||
// 从当前登录用户获取 userId
|
||||
Long userId = SecurityFrameworkUtils.getLoginUserId();
|
||||
if (userId == null) {
|
||||
log.warn("获取用户ID失败,用户未登录");
|
||||
return CommonResult.error(GlobalErrorCodeConstants.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
log.info("用户创建混剪任务, userId={}, title={}", userId, createReqVO.getTitle());
|
||||
Long taskId = mixTaskService.createMixTask(createReqVO, userId);
|
||||
return CommonResult.success(taskId);
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新混剪任务")
|
||||
public CommonResult<Boolean> updateMixTask(@Valid @RequestBody MixTaskUpdateReqVO updateReqVO) {
|
||||
mixTaskService.updateMixTask(updateReqVO);
|
||||
return CommonResult.success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete/{id}")
|
||||
@Operation(summary = "删除混剪任务")
|
||||
public CommonResult<Boolean> deleteMixTask(@PathVariable Long id) {
|
||||
mixTaskService.deleteMixTask(id);
|
||||
return CommonResult.success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get/{id}")
|
||||
@Operation(summary = "获得混剪任务")
|
||||
public CommonResult<MixTaskRespVO> getMixTask(@PathVariable Long id) {
|
||||
MixTaskRespVO mixTaskVO = mixTaskService.getMixTask(id);
|
||||
return CommonResult.success(mixTaskVO);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得混剪任务分页")
|
||||
public CommonResult<PageResult<MixTaskRespVO>> getMixTaskPage(MixTaskPageReqVO pageReqVO) {
|
||||
// 从当前登录用户获取 userId
|
||||
Long userId = SecurityFrameworkUtils.getLoginUserId();
|
||||
if (userId == null) {
|
||||
log.warn("获取用户ID失败,用户未登录");
|
||||
return CommonResult.error(GlobalErrorCodeConstants.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
PageResult<MixTaskRespVO> pageResult = mixTaskService.getUserMixTaskPage(pageReqVO, userId);
|
||||
return CommonResult.success(pageResult);
|
||||
}
|
||||
|
||||
@GetMapping("/status/{id}")
|
||||
@Operation(summary = "查询任务状态")
|
||||
public CommonResult<MixTaskRespVO> getTaskStatus(@PathVariable Long id) {
|
||||
MixTaskRespVO taskStatusVO = mixTaskService.getTaskStatus(id);
|
||||
return CommonResult.success(taskStatusVO);
|
||||
}
|
||||
|
||||
@PostMapping("/retry/{id}")
|
||||
@Operation(summary = "重新生成失败的任务")
|
||||
public CommonResult<Boolean> retryTask(@PathVariable Long id) {
|
||||
mixTaskService.retryTask(id);
|
||||
return CommonResult.success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/cancel/{id}")
|
||||
@Operation(summary = "取消任务")
|
||||
public CommonResult<Boolean> cancelTask(@PathVariable Long id) {
|
||||
mixTaskService.cancelTask(id);
|
||||
return CommonResult.success(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package cn.iocoder.yudao.module.tik.mix.dal.dataobject;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 混剪任务 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("tik_mix_task")
|
||||
@KeySequence("tik_mix_task_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class MixTaskDO extends TenantBaseDO {
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@TableField("tenant_id")
|
||||
private Long tenantId;
|
||||
|
||||
@TableField("user_id")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 视频标题
|
||||
*/
|
||||
@TableField("title")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 文案内容
|
||||
*/
|
||||
@TableField("text")
|
||||
private String text;
|
||||
|
||||
/**
|
||||
* 视频素材URL列表(JSON)
|
||||
*/
|
||||
@TableField("video_urls")
|
||||
private String videoUrls;
|
||||
|
||||
/**
|
||||
* 背景音乐URL列表(JSON)
|
||||
*/
|
||||
@TableField("bg_music_urls")
|
||||
private String bgMusicUrls;
|
||||
|
||||
/**
|
||||
* 生成数量
|
||||
*/
|
||||
@TableField("produce_count")
|
||||
private Integer produceCount;
|
||||
|
||||
/**
|
||||
* 任务ID列表(JSON)
|
||||
*/
|
||||
@TableField("job_ids")
|
||||
private String jobIds;
|
||||
|
||||
/**
|
||||
* 输出文件URL列表(JSON)
|
||||
*/
|
||||
@TableField("output_urls")
|
||||
private String outputUrls;
|
||||
|
||||
/**
|
||||
* 任务状态
|
||||
* 枚举:pending(待处理), running(处理中), success(成功), failed(失败)
|
||||
*/
|
||||
@TableField("status")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 进度(0-100)
|
||||
*/
|
||||
@TableField("progress")
|
||||
private Integer progress;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
@TableField("error_msg")
|
||||
private String errorMsg;
|
||||
|
||||
/**
|
||||
* 完成时间
|
||||
*/
|
||||
@TableField("finish_time")
|
||||
private LocalDateTime finishTime;
|
||||
|
||||
/**
|
||||
* 获取视频URL列表
|
||||
*/
|
||||
public List<String> getVideoUrlList() {
|
||||
return JsonUtils.parseObject(videoUrls, new TypeReference<List<String>>() {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置视频URL列表
|
||||
*/
|
||||
public void setVideoUrlList(List<String> videoUrls) {
|
||||
this.videoUrls = JsonUtils.toJsonString(videoUrls);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取背景音乐URL列表
|
||||
*/
|
||||
public List<String> getBgMusicUrlList() {
|
||||
return JsonUtils.parseObject(bgMusicUrls, new TypeReference<List<String>>() {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置背景音乐URL列表
|
||||
*/
|
||||
public void setBgMusicUrlList(List<String> bgMusicUrls) {
|
||||
this.bgMusicUrls = JsonUtils.toJsonString(bgMusicUrls);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务ID列表
|
||||
*/
|
||||
public List<String> getJobIdList() {
|
||||
return JsonUtils.parseObject(jobIds, new TypeReference<List<String>>() {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置任务ID列表
|
||||
*/
|
||||
public void setJobIdList(List<String> jobIds) {
|
||||
this.jobIds = JsonUtils.toJsonString(jobIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输出URL列表
|
||||
*/
|
||||
public List<String> getOutputUrlList() {
|
||||
return JsonUtils.parseObject(outputUrls, new TypeReference<List<String>>() {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输出URL列表
|
||||
*/
|
||||
public void setOutputUrlList(List<String> outputUrls) {
|
||||
this.outputUrls = JsonUtils.toJsonString(outputUrls);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package cn.iocoder.yudao.module.tik.mix.dal.mysql;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.tik.mix.dal.dataobject.MixTaskDO;
|
||||
import cn.iocoder.yudao.module.tik.mix.vo.MixTaskPageReqVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 混剪任务 Mapper
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface MixTaskMapper extends BaseMapperX<MixTaskDO> {
|
||||
|
||||
/**
|
||||
* 分页查询(所有任务)
|
||||
*/
|
||||
default PageResult<MixTaskDO> selectPage(MixTaskPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<MixTaskDO>()
|
||||
.eqIfPresent(MixTaskDO::getStatus, reqVO.getStatus())
|
||||
.likeIfPresent(MixTaskDO::getTitle, reqVO.getTitle())
|
||||
.geIfPresent(MixTaskDO::getCreateTime, reqVO.getCreateTimeStart())
|
||||
.leIfPresent(MixTaskDO::getCreateTime, reqVO.getCreateTimeEnd())
|
||||
.orderByDesc(MixTaskDO::getId)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询(用户任务)
|
||||
*/
|
||||
default PageResult<MixTaskDO> selectPageByUserId(MixTaskPageReqVO reqVO, Long userId) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<MixTaskDO>()
|
||||
.eq(MixTaskDO::getUserId, userId)
|
||||
.eqIfPresent(MixTaskDO::getStatus, reqVO.getStatus())
|
||||
.likeIfPresent(MixTaskDO::getTitle, reqVO.getTitle())
|
||||
.geIfPresent(MixTaskDO::getCreateTime, reqVO.getCreateTimeStart())
|
||||
.leIfPresent(MixTaskDO::getCreateTime, reqVO.getCreateTimeEnd())
|
||||
.orderByDesc(MixTaskDO::getId)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户的所有混剪任务
|
||||
*/
|
||||
@Select("SELECT * FROM tik_mix_task WHERE user_id = #{userId} ORDER BY create_time DESC")
|
||||
List<MixTaskDO> selectListByUserId(@Param("userId") Long userId);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package cn.iocoder.yudao.module.tik.mix.job;
|
||||
|
||||
import cn.iocoder.yudao.module.tik.mix.constants.MixTaskConstants;
|
||||
import cn.iocoder.yudao.module.tik.mix.service.MixTaskService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 混剪任务状态同步定时任务
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class MixTaskStatusSyncJob {
|
||||
|
||||
private final MixTaskService mixTaskService;
|
||||
|
||||
/**
|
||||
* 每30秒检查一次任务状态
|
||||
*/
|
||||
@Scheduled(cron = MixTaskConstants.CRON_CHECK_STATUS)
|
||||
public void syncTaskStatus() {
|
||||
log.debug("开始同步混剪任务状态");
|
||||
try {
|
||||
mixTaskService.checkTaskStatusBatch();
|
||||
} catch (Exception e) {
|
||||
log.error("同步混剪任务状态失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package cn.iocoder.yudao.module.tik.mix.service;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.tik.mix.vo.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 混剪任务 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface MixTaskService {
|
||||
|
||||
/**
|
||||
* 创建混剪任务
|
||||
*/
|
||||
Long createMixTask(MixTaskSaveReqVO createReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 更新混剪任务
|
||||
*/
|
||||
void updateMixTask(MixTaskUpdateReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除混剪任务
|
||||
*/
|
||||
void deleteMixTask(Long id);
|
||||
|
||||
/**
|
||||
* 获得混剪任务
|
||||
*/
|
||||
MixTaskRespVO getMixTask(Long id);
|
||||
|
||||
/**
|
||||
* 获得混剪任务分页
|
||||
*/
|
||||
PageResult<MixTaskRespVO> getMixTaskPage(MixTaskPageReqVO pageReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 查询用户混剪任务列表
|
||||
*/
|
||||
PageResult<MixTaskRespVO> getUserMixTaskPage(MixTaskPageReqVO pageReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 查询任务状态
|
||||
*/
|
||||
MixTaskRespVO getTaskStatus(Long id);
|
||||
|
||||
/**
|
||||
* 重新生成失败的任务
|
||||
*/
|
||||
void retryTask(Long id);
|
||||
|
||||
/**
|
||||
* 取消任务
|
||||
*/
|
||||
void cancelTask(Long id);
|
||||
|
||||
/**
|
||||
* 批量检查任务状态
|
||||
*/
|
||||
void checkTaskStatusBatch();
|
||||
|
||||
/**
|
||||
* 同步任务状态(从阿里云 ICE 查询)
|
||||
*/
|
||||
void syncTaskStatus(Long taskId, String jobId);
|
||||
|
||||
/**
|
||||
* 保存任务结果
|
||||
*/
|
||||
void saveTaskResult(Long taskId, List<String> outputUrls);
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
package cn.iocoder.yudao.module.tik.mix.service;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.tik.mix.constants.MixTaskConstants;
|
||||
import cn.iocoder.yudao.module.tik.mix.dal.dataobject.MixTaskDO;
|
||||
import cn.iocoder.yudao.module.tik.mix.dal.mysql.MixTaskMapper;
|
||||
import cn.iocoder.yudao.module.tik.mix.util.MixTaskUtils;
|
||||
import cn.iocoder.yudao.module.tik.mix.vo.*;
|
||||
import cn.iocoder.yudao.module.tik.media.BatchProduceAlignment;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* 混剪任务 Service 实现
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MixTaskServiceImpl implements MixTaskService {
|
||||
|
||||
private final MixTaskMapper mixTaskMapper;
|
||||
private final BatchProduceAlignment batchProduceAlignment;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createMixTask(MixTaskSaveReqVO createReqVO, Long userId) {
|
||||
log.info("开始创建混剪任务,用户ID: {}, 标题: {}", userId, createReqVO.getTitle());
|
||||
|
||||
// 1. 创建初始任务对象
|
||||
MixTaskDO task = MixTaskUtils.createInitialTask(createReqVO, userId);
|
||||
|
||||
// 2. 保存到数据库
|
||||
mixTaskMapper.insert(task);
|
||||
log.info("任务已创建,任务ID: {}", task.getId());
|
||||
|
||||
// 3. 异步提交到阿里云 ICE
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
submitToICE(task.getId(), createReqVO);
|
||||
} catch (Exception e) {
|
||||
log.error("提交任务到ICE失败,任务ID: {}", task.getId(), e);
|
||||
updateTaskError(task.getId(), "提交任务失败: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
return task.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateMixTask(MixTaskUpdateReqVO updateReqVO) {
|
||||
log.info("更新混剪任务,任务ID: {}", updateReqVO.getId());
|
||||
|
||||
// 1. 检查任务是否存在
|
||||
MixTaskDO existTask = mixTaskMapper.selectById(updateReqVO.getId());
|
||||
if (existTask == null) {
|
||||
log.error("任务不存在,任务ID: {}", updateReqVO.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 创建更新对象
|
||||
MixTaskDO updateTask = MixTaskUtils.createTaskUpdate(updateReqVO.getId(), updateReqVO);
|
||||
|
||||
// 3. 执行更新
|
||||
mixTaskMapper.updateById(updateTask);
|
||||
log.info("任务更新成功,任务ID: {}", updateReqVO.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteMixTask(Long id) {
|
||||
log.info("删除混剪任务,任务ID: {}", id);
|
||||
|
||||
// 1. 检查任务是否存在
|
||||
MixTaskDO existTask = mixTaskMapper.selectById(id);
|
||||
if (existTask == null) {
|
||||
log.error("任务不存在,任务ID: {}", id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 执行删除
|
||||
mixTaskMapper.deleteById(id);
|
||||
log.info("任务删除成功,任务ID: {}", id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MixTaskRespVO getMixTask(Long id) {
|
||||
log.debug("查询混剪任务,任务ID: {}", id);
|
||||
|
||||
MixTaskDO task = mixTaskMapper.selectById(id);
|
||||
return BeanUtils.toBean(task, MixTaskRespVO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<MixTaskRespVO> getMixTaskPage(MixTaskPageReqVO pageReqVO, Long userId) {
|
||||
log.debug("分页查询混剪任务,用户ID: {}, 页码: {}, 页大小: {}",
|
||||
userId, pageReqVO.getPageNo(), pageReqVO.getPageSize());
|
||||
|
||||
PageResult<MixTaskDO> pageResult = mixTaskMapper.selectPage(pageReqVO);
|
||||
return BeanUtils.toBean(pageResult, MixTaskRespVO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<MixTaskRespVO> getUserMixTaskPage(MixTaskPageReqVO pageReqVO, Long userId) {
|
||||
log.debug("分页查询用户混剪任务,用户ID: {}, 页码: {}, 页大小: {}",
|
||||
userId, pageReqVO.getPageNo(), pageReqVO.getPageSize());
|
||||
|
||||
// 使用用户ID过滤查询
|
||||
PageResult<MixTaskDO> pageResult = mixTaskMapper.selectPageByUserId(pageReqVO, userId);
|
||||
return BeanUtils.toBean(pageResult, MixTaskRespVO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MixTaskRespVO getTaskStatus(Long id) {
|
||||
log.debug("查询任务状态,任务ID: {}", id);
|
||||
return getMixTask(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void retryTask(Long id) {
|
||||
log.info("重新生成失败任务,任务ID: {}", id);
|
||||
|
||||
// 1. 查询原任务
|
||||
MixTaskDO existTask = mixTaskMapper.selectById(id);
|
||||
if (existTask == null) {
|
||||
log.error("任务不存在,任务ID: {}", id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 重置任务状态
|
||||
MixTaskDO updateTask = new MixTaskDO();
|
||||
updateTask.setId(id);
|
||||
updateTask.setStatus(MixTaskConstants.STATUS_PENDING);
|
||||
updateTask.setProgress(0);
|
||||
updateTask.setErrorMsg(null);
|
||||
updateTask.setJobIds(null);
|
||||
updateTask.setOutputUrls(null);
|
||||
mixTaskMapper.updateById(updateTask);
|
||||
|
||||
// 3. 重新提交到ICE
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
MixTaskSaveReqVO saveReqVO = BeanUtils.toBean(existTask, MixTaskSaveReqVO.class);
|
||||
submitToICE(id, saveReqVO);
|
||||
} catch (Exception e) {
|
||||
log.error("重新提交任务失败,任务ID: {}", id, e);
|
||||
updateTaskError(id, "重新提交失败: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
log.info("任务重新提交成功,任务ID: {}", id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void cancelTask(Long id) {
|
||||
log.info("取消任务,任务ID: {}", id);
|
||||
|
||||
// 1. 查询任务
|
||||
MixTaskDO existTask = mixTaskMapper.selectById(id);
|
||||
if (existTask == null) {
|
||||
log.error("任务不存在,任务ID: {}", id);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 检查任务状态
|
||||
if (!MixTaskConstants.STATUS_RUNNING.equals(existTask.getStatus())) {
|
||||
log.warn("任务非运行状态,无法取消,任务ID: {}, 状态: {}", id, existTask.getStatus());
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. TODO: 调用阿里云 ICE 取消接口
|
||||
|
||||
// 4. 更新任务状态为失败
|
||||
MixTaskDO updateTask = new MixTaskDO();
|
||||
updateTask.setId(id);
|
||||
updateTask.setStatus(MixTaskConstants.STATUS_FAILED);
|
||||
updateTask.setProgress(MixTaskConstants.PROGRESS_COMPLETED);
|
||||
updateTask.setErrorMsg("用户主动取消任务");
|
||||
mixTaskMapper.updateById(updateTask);
|
||||
|
||||
log.info("任务取消成功,任务ID: {}", id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkTaskStatusBatch() {
|
||||
log.debug("开始批量检查任务状态");
|
||||
|
||||
// 查询所有运行中的任务
|
||||
List<MixTaskDO> runningTasks = mixTaskMapper.selectList(
|
||||
new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<MixTaskDO>()
|
||||
.eq(MixTaskDO::getStatus, MixTaskConstants.STATUS_RUNNING)
|
||||
);
|
||||
|
||||
if (runningTasks.isEmpty()) {
|
||||
log.debug("没有运行中的任务,跳过检查");
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("发现 {} 个运行中的任务,开始检查状态", runningTasks.size());
|
||||
|
||||
// 逐个检查任务状态
|
||||
for (MixTaskDO task : runningTasks) {
|
||||
try {
|
||||
List<String> jobIds = task.getJobIdList();
|
||||
if (jobIds != null && !jobIds.isEmpty()) {
|
||||
// 每个任务可能有多个jobId,取第一个进行检查
|
||||
String jobId = jobIds.get(0);
|
||||
syncTaskStatus(task.getId(), jobId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("检查任务状态失败,任务ID: {}", task.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("批量检查任务状态完成");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncTaskStatus(Long taskId, String jobId) {
|
||||
log.debug("同步任务状态,任务ID: {}, jobId: {}", taskId, jobId);
|
||||
|
||||
try {
|
||||
// TODO: 调用阿里云 ICE API 查询任务状态
|
||||
// 这里需要集成具体的 ICE SDK 或 HTTP API
|
||||
|
||||
// 模拟状态检查逻辑
|
||||
// String status = iceClient.getJobStatus(jobId);
|
||||
// String progress = iceClient.getJobProgress(jobId);
|
||||
// String outputUrl = iceClient.getJobOutput(jobId);
|
||||
|
||||
// 根据返回的状态更新任务
|
||||
// updateTaskStatus(taskId, status, progress, outputUrl);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("同步任务状态失败,任务ID: {}, jobId: {}", taskId, jobId, e);
|
||||
updateTaskError(taskId, "查询任务状态失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void saveTaskResult(Long taskId, List<String> outputUrls) {
|
||||
log.info("保存任务结果,任务ID: {}, 结果数量: {}", taskId, outputUrls.size());
|
||||
|
||||
// 1. 更新任务输出URL
|
||||
MixTaskDO updateTask = new MixTaskDO();
|
||||
updateTask.setId(taskId);
|
||||
updateTask.setOutputUrlList(outputUrls);
|
||||
updateTask.setStatus(MixTaskConstants.STATUS_SUCCESS);
|
||||
updateTask.setProgress(MixTaskConstants.PROGRESS_COMPLETED);
|
||||
updateTask.setFinishTime(java.time.LocalDateTime.now());
|
||||
mixTaskMapper.updateById(updateTask);
|
||||
|
||||
log.info("任务结果保存成功,任务ID: {}", taskId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交任务到阿里云 ICE
|
||||
*/
|
||||
private void submitToICE(Long taskId, MixTaskSaveReqVO createReqVO) {
|
||||
log.info("提交任务到ICE,任务ID: {}", taskId);
|
||||
|
||||
try {
|
||||
// 1. 更新任务状态为运行中,进度10%
|
||||
updateTaskStatus(taskId, MixTaskConstants.STATUS_RUNNING, MixTaskConstants.PROGRESS_SUBMITTED);
|
||||
|
||||
// 2. 转换为ICE需要的参数格式
|
||||
String[] videoArray = createReqVO.getVideoUrls().toArray(new String[0]);
|
||||
String[] bgMusicArray = createReqVO.getBgMusicUrls().toArray(new String[0]);
|
||||
|
||||
// 3. 调用ICE批量生成接口
|
||||
List<String> jobIdWithUrls = batchProduceAlignment.batchProduceAlignment(
|
||||
createReqVO.getTitle(),
|
||||
createReqVO.getText(),
|
||||
videoArray,
|
||||
bgMusicArray,
|
||||
createReqVO.getProduceCount()
|
||||
);
|
||||
|
||||
// 4. 解析jobId和输出URL
|
||||
MixTaskUtils.JobIdUrlPair jobIdUrlPair = MixTaskUtils.parseJobIdsAndUrls(jobIdWithUrls);
|
||||
|
||||
// 5. 更新任务信息
|
||||
updateTaskWithResults(taskId, jobIdUrlPair.getJobIds(), jobIdUrlPair.getOutputUrls(),
|
||||
MixTaskConstants.STATUS_RUNNING, MixTaskConstants.PROGRESS_UPLOADED);
|
||||
|
||||
log.info("任务提交到ICE成功,任务ID: {}, jobId数量: {}", taskId, jobIdUrlPair.getJobIds().size());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("提交任务到ICE失败,任务ID: {}", taskId, e);
|
||||
updateTaskError(taskId, "ICE处理失败: " + e.getMessage());
|
||||
// 注意:异步线程中不抛出异常,避免未处理异常
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务状态
|
||||
*/
|
||||
private void updateTaskStatus(Long taskId, String status, Integer progress) {
|
||||
MixTaskDO updateTask = new MixTaskDO();
|
||||
updateTask.setId(taskId);
|
||||
updateTask.setStatus(status);
|
||||
updateTask.setProgress(progress);
|
||||
mixTaskMapper.updateById(updateTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务结果
|
||||
*/
|
||||
private void updateTaskWithResults(Long taskId, List<String> jobIds, List<String> outputUrls,
|
||||
String status, Integer progress) {
|
||||
MixTaskDO updateTask = new MixTaskDO();
|
||||
updateTask.setId(taskId);
|
||||
updateTask.setStatus(status);
|
||||
updateTask.setProgress(progress);
|
||||
updateTask.setJobIdList(jobIds);
|
||||
updateTask.setOutputUrlList(outputUrls);
|
||||
mixTaskMapper.updateById(updateTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务错误信息
|
||||
*/
|
||||
private void updateTaskError(Long taskId, String errorMsg) {
|
||||
MixTaskDO updateTask = new MixTaskDO();
|
||||
updateTask.setId(taskId);
|
||||
updateTask.setStatus(MixTaskConstants.STATUS_FAILED);
|
||||
updateTask.setProgress(MixTaskConstants.PROGRESS_COMPLETED);
|
||||
updateTask.setErrorMsg(errorMsg);
|
||||
updateTask.setFinishTime(java.time.LocalDateTime.now());
|
||||
mixTaskMapper.updateById(updateTask);
|
||||
|
||||
log.error("任务执行失败,任务ID: {}, 错误信息: {}", taskId, errorMsg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package cn.iocoder.yudao.module.tik.mix.util;
|
||||
|
||||
import cn.iocoder.yudao.module.tik.mix.constants.MixTaskConstants;
|
||||
import cn.iocoder.yudao.module.tik.mix.dal.dataobject.MixTaskDO;
|
||||
import cn.iocoder.yudao.module.tik.mix.vo.MixTaskSaveReqVO;
|
||||
import cn.iocoder.yudao.module.tik.mix.vo.MixTaskUpdateReqVO;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 混剪任务工具类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class MixTaskUtils {
|
||||
|
||||
private MixTaskUtils() {
|
||||
// 防止实例化
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建初始任务对象
|
||||
*/
|
||||
public static MixTaskDO createInitialTask(MixTaskSaveReqVO reqVO, Long userId) {
|
||||
MixTaskDO task = new MixTaskDO();
|
||||
task.setUserId(userId);
|
||||
task.setTitle(reqVO.getTitle());
|
||||
task.setText(reqVO.getText());
|
||||
task.setVideoUrlList(reqVO.getVideoUrls());
|
||||
task.setBgMusicUrlList(reqVO.getBgMusicUrls());
|
||||
task.setProduceCount(reqVO.getProduceCount());
|
||||
task.setStatus(MixTaskConstants.STATUS_PENDING);
|
||||
task.setProgress(0);
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建任务更新对象
|
||||
*/
|
||||
public static MixTaskDO createTaskUpdate(Long taskId, MixTaskUpdateReqVO reqVO) {
|
||||
MixTaskDO task = new MixTaskDO();
|
||||
task.setId(taskId);
|
||||
task.setTitle(reqVO.getTitle());
|
||||
task.setText(reqVO.getText());
|
||||
task.setVideoUrlList(reqVO.getVideoUrls());
|
||||
task.setBgMusicUrlList(reqVO.getBgMusicUrls());
|
||||
task.setProduceCount(reqVO.getProduceCount());
|
||||
task.setJobIdList(reqVO.getJobIds());
|
||||
task.setOutputUrlList(reqVO.getOutputUrls());
|
||||
task.setStatus(reqVO.getStatus());
|
||||
task.setProgress(reqVO.getProgress());
|
||||
task.setErrorMsg(reqVO.getErrorMsg());
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 jobId 和输出 URL
|
||||
*
|
||||
* @param jobIdWithUrls jobId:url 格式的列表
|
||||
* @return 解析结果
|
||||
*/
|
||||
public static JobIdUrlPair parseJobIdsAndUrls(List<String> jobIdWithUrls) {
|
||||
List<String> jobIds = new ArrayList<>();
|
||||
List<String> outputUrls = new ArrayList<>();
|
||||
|
||||
for (String jobIdWithUrl : jobIdWithUrls) {
|
||||
String[] parts = jobIdWithUrl.split(" : ");
|
||||
if (parts.length == 2) {
|
||||
jobIds.add(parts[0]);
|
||||
outputUrls.add(parts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return new JobIdUrlPair(jobIds, outputUrls);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务进度计算器
|
||||
*/
|
||||
public static class ProgressCalculator {
|
||||
|
||||
/**
|
||||
* 计算任务进度
|
||||
*
|
||||
* @param totalCount 总数量
|
||||
* @param completedCount 完成数量
|
||||
* @param failedCount 失败数量
|
||||
* @return 进度百分比
|
||||
*/
|
||||
public static int calculate(int totalCount, int completedCount, int failedCount) {
|
||||
return (completedCount + failedCount) * MixTaskConstants.PROGRESS_COMPLETED / totalCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断任务是否完全失败
|
||||
*/
|
||||
public static boolean isAllFailed(int totalCount, int failedCount) {
|
||||
return failedCount == totalCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断任务是否全部完成
|
||||
*/
|
||||
public static boolean isAllCompleted(int totalCount, int completedCount) {
|
||||
return completedCount == totalCount;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* jobId 和 URL 解析结果
|
||||
*/
|
||||
public static class JobIdUrlPair {
|
||||
private final List<String> jobIds;
|
||||
private final List<String> outputUrls;
|
||||
|
||||
public JobIdUrlPair(List<String> jobIds, List<String> outputUrls) {
|
||||
this.jobIds = jobIds;
|
||||
this.outputUrls = outputUrls;
|
||||
}
|
||||
|
||||
public List<String> getJobIds() {
|
||||
return jobIds;
|
||||
}
|
||||
|
||||
public List<String> getOutputUrls() {
|
||||
return outputUrls;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.module.tik.mix.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 混剪任务分页 Request VO")
|
||||
@Data
|
||||
public class MixTaskPageReqVO extends SortablePageParam {
|
||||
|
||||
@Schema(description = "任务状态", example = "running")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "视频标题", example = "美食")
|
||||
private String title;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "创建时间开始")
|
||||
private LocalDateTime createTimeStart;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@Schema(description = "创建时间结束")
|
||||
private LocalDateTime createTimeEnd;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package cn.iocoder.yudao.module.tik.mix.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 混剪任务 Response VO")
|
||||
@Data
|
||||
public class MixTaskRespVO {
|
||||
|
||||
@Schema(description = "ID", example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户ID", example = "1")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "视频标题", example = "美食纪录片")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "文案内容")
|
||||
private String text;
|
||||
|
||||
@Schema(description = "视频素材URL列表")
|
||||
private List<String> videoUrls;
|
||||
|
||||
@Schema(description = "背景音乐URL列表")
|
||||
private List<String> bgMusicUrls;
|
||||
|
||||
@Schema(description = "生成数量", example = "3")
|
||||
private Integer produceCount;
|
||||
|
||||
@Schema(description = "任务ID列表")
|
||||
private List<String> jobIds;
|
||||
|
||||
@Schema(description = "输出文件URL列表")
|
||||
private List<String> outputUrls;
|
||||
|
||||
@Schema(description = "任务状态", example = "running")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "进度(0-100)", example = "65")
|
||||
private Integer progress;
|
||||
|
||||
@Schema(description = "错误信息")
|
||||
private String errorMsg;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
@Schema(description = "完成时间")
|
||||
private LocalDateTime finishTime;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package cn.iocoder.yudao.module.tik.mix.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 混剪任务创建 Request VO")
|
||||
@Data
|
||||
public class MixTaskSaveReqVO {
|
||||
|
||||
@Schema(description = "视频标题", required = true, example = "美食纪录片")
|
||||
@NotBlank(message = "视频标题不能为空")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "文案内容", required = true, example = "人们懂得用五味杂陈形容人生...")
|
||||
@NotBlank(message = "文案内容不能为空")
|
||||
private String text;
|
||||
|
||||
@Schema(description = "视频素材URL列表", required = true)
|
||||
@NotEmpty(message = "视频素材不能为空")
|
||||
private List<String> videoUrls;
|
||||
|
||||
@Schema(description = "背景音乐URL列表", required = true)
|
||||
@NotEmpty(message = "背景音乐不能为空")
|
||||
private List<String> bgMusicUrls;
|
||||
|
||||
@Schema(description = "生成数量", required = true, example = "3")
|
||||
@NotNull(message = "生成数量不能为空")
|
||||
private Integer produceCount;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cn.iocoder.yudao.module.tik.mix.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 混剪任务更新 Request VO")
|
||||
@Data
|
||||
public class MixTaskUpdateReqVO {
|
||||
|
||||
@Schema(description = "ID", required = true, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "视频标题", example = "美食纪录片")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "文案内容")
|
||||
private String text;
|
||||
|
||||
@Schema(description = "视频素材URL列表")
|
||||
private List<String> videoUrls;
|
||||
|
||||
@Schema(description = "背景音乐URL列表")
|
||||
private List<String> bgMusicUrls;
|
||||
|
||||
@Schema(description = "生成数量", example = "3")
|
||||
private Integer produceCount;
|
||||
|
||||
@Schema(description = "任务ID列表")
|
||||
private List<String> jobIds;
|
||||
|
||||
@Schema(description = "输出文件URL列表")
|
||||
private List<String> outputUrls;
|
||||
|
||||
@Schema(description = "任务状态", example = "running")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "进度(0-100)", example = "65")
|
||||
private Integer progress;
|
||||
|
||||
@Schema(description = "错误信息")
|
||||
private String errorMsg;
|
||||
}
|
||||
Reference in New Issue
Block a user