shenrongliang
2025-04-08 97bfa45190dd80a2b94e2712fe3a683c0fd2bfa0
Merge remote-tracking branch 'origin/master'
已修改4个文件
已添加3个文件
223 ■■■■ 文件已修改
easegen-front/src/views/chooseTemplate/index.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
easegen-front/src/views/myCourse/index.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
media_task_queue.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/manager/MediaTaskManager.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/model/MediaTask.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceImpl.java 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceUtil.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
easegen-front/src/views/chooseTemplate/index.vue
@@ -218,10 +218,11 @@
                  <Delete />
                </el-icon>
              </Vue3DraggableResizable>
              <div style="width: 160%;height: 100%;position: absolute;top: 0;left: -30%">
                <Vue3DraggableResizable
                  v-if="selectPPT.showDigitalHuman"
                  :parent="true"
                  :parent="false"
                  :lockAspectRatio="true"
                  :minW="350"
                  :initW="PPTpositon.w"
                  :initH="PPTpositon.h"
                  @drag-move="onDragMove"
@@ -258,8 +259,6 @@
                    <Delete />
                  </el-icon>
                </Vue3DraggableResizable>
              </div>
            </div>
          </div>
          <el-card
@@ -1437,6 +1436,7 @@
          thumbnail = item.pictureUrl
          pageNum++
        }
        console.log(item)
        const innerPictureCom = item.innerPicture
        console.log('innerPictureCom:', JSON.stringify(innerPictureCom))
@@ -1458,7 +1458,7 @@
            color: '#ffffff',
            pptRemark: item.pptRemark
          },
          hasPerson: item.showDigitalHuman==true? 1 : 2,
          components: [
            {
              ...cloneDeep(digitalHumanComponents), // æ·±æ‹·è´
easegen-front/src/views/myCourse/index.vue
@@ -60,11 +60,11 @@
        width="120"
        :formatter="dateFormatter"
      />
      <el-table-column :label="t('myCourse.progress')" align="center" prop="progress">
        <template #default="scope">
          <el-progress :percentage="getProgress(scope.row.status, scope.row.progress)" />
        </template>
      </el-table-column>
<!--      <el-table-column :label="t('myCourse.progress')" align="center" prop="progress">-->
<!--        <template #default="scope">-->
<!--          <el-progress :percentage="getProgress(scope.row.status, scope.row.progress)" />-->
<!--        </template>-->
<!--      </el-table-column>-->
      <el-table-column :label="t('myCourse.SynthesisTime')" align="center">
        <template #default="scope">
          {{ calculateDuration(scope.row.createTime, scope.row.finishTime) }}
media_task_queue.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
{"current":null,"queue":[]}
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/manager/MediaTaskManager.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,144 @@
package cn.iocoder.yudao.module.digitalcourse.manager;
import cn.iocoder.yudao.module.digitalcourse.model.MediaTask;
import cn.iocoder.yudao.module.digitalcourse.service.coursemedia.CourseMediaServiceUtil;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
/**
 * @author kanglujie
 * @date 2025-04-07 14:36:00
 */
@Component
public class MediaTaskManager {
    private final BlockingQueue<MediaTask> queue = new LinkedBlockingQueue<>();
    private final ExecutorService executor = Executors.newSingleThreadExecutor();
    private final ObjectMapper mapper = new ObjectMapper()
            .registerModule(new JavaTimeModule())
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    private final Path persistPath = Paths.get("media_task_queue.json");
    private final Path persistTempPath = Paths.get("media_task_queue_temp.json");
    private final Object lock = new Object(); // é”å¯¹è±¡
    private volatile MediaTask currentTask = null;
    @Resource
    private CourseMediaServiceUtil courseMediaServiceUtil;
    @PostConstruct
    public void init() throws IOException {
        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
        loadQueueFromDisk();
        startWorker();
    }
    public int submitTask(MediaTask task) throws IOException {
        synchronized (lock) {
            queue.add(task);
            persistQueue(); // ç«‹å³æŒä¹…化
            return queue.size();
        }
    }
    public int getQueuePosition(Long taskId) {
        int pos = 1;
        if (currentTask != null && currentTask.getId().equals(taskId)) return 0;
        for (MediaTask task : queue) {
            if (task.getId().equals(taskId)) return pos;
            pos++;
        }
        return -1;
    }
    private void startWorker() {
        executor.submit(() -> {
            while (true) {
                try {
                    currentTask = queue.take(); // å¼€å§‹å¤„理
                    persistQueue();
                    System.out.println("开始合成:" + currentTask.getId());
                    courseMediaServiceUtil.remoteMegerMedia(currentTask.getReq());
                    synchronized (lock) {
                        currentTask = null;
                        persistQueue(); // æ‰§è¡Œå®Œå†æŒä¹…化
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    // å¦‚果异常了,重新入队
                    if (currentTask != null) {
                        queue.add(currentTask);
                        currentTask = null;
                    }
                    persistQueue();
                }
            }
        });
    }
    private void persistQueue() {
        synchronized (lock) {
            try {
                QueueState state = new QueueState();
                state.current = currentTask;
                state.queue = new ArrayList<>(queue);
                mapper.writeValue(persistTempPath.toFile(), state);
                Files.move(persistTempPath, persistPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    private void loadQueueFromDisk() {
        synchronized (lock) {
            if (Files.exists(persistPath)) {
                try {
                    QueueState state = mapper.readValue(persistPath.toFile(), QueueState.class);
                    if (state.current != null) {
                        queue.add(state.current);
                    }
                    if (state.queue != null) {
                        queue.addAll(state.queue);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    @PreDestroy
    public void shutdown() {
        executor.shutdownNow();
        persistQueue();
    }
    // å†…部类用于 JSON æ˜ å°„
    public static class QueueState {
        public MediaTask current;
        public List<MediaTask> queue;
    }
}
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/model/MediaTask.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.digitalcourse.model;
/**
 * @author kanglujie
 * @date 2025-04-07 14:35:03
 */
import cn.iocoder.yudao.module.digitalcourse.controller.admin.coursemedia.vo.CourseMediaMegerVO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MediaTask {
    private Long id;
    private CourseMediaMegerVO req;
    private LocalDateTime submitTime;
}
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceImpl.java
@@ -1,38 +1,28 @@
package cn.iocoder.yudao.module.digitalcourse.service.coursemedia;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.digitalcourse.controller.admin.coursemedia.vo.CourseMediaMegerVO;
import cn.iocoder.yudao.module.digitalcourse.controller.admin.coursemedia.vo.CourseMediaPageReqVO;
import cn.iocoder.yudao.module.digitalcourse.controller.admin.coursemedia.vo.CourseMediaSaveReqVO;
import cn.iocoder.yudao.module.digitalcourse.controller.admin.courses.vo.AppCoursesUpdateReqVO;
import cn.iocoder.yudao.module.digitalcourse.dal.dataobject.coursemedia.CourseMediaDO;
import cn.iocoder.yudao.module.digitalcourse.dal.mysql.coursemedia.CourseMediaMapper;
import cn.iocoder.yudao.module.digitalcourse.manager.MediaTaskManager;
import cn.iocoder.yudao.module.digitalcourse.model.MediaTask;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import java.math.BigInteger;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.module.digitalcourse.enums.ErrorCodeConstants.COURSE_MEDIA_NOT_EXISTS;
@@ -115,10 +105,17 @@
        }
        updateReqVO.setCourseMediaId(courseMediaDO.getId());
        //异步调用数字人视频渲染接口,开始合并
        courseMediaServiceUtil.remoteMegerMedia(updateReqVO);
        return CommonResult.success(true);
        MediaTask task = new MediaTask(id, updateReqVO, LocalDateTime.now());
        int pos = 0;
        try {
            pos = mediaTaskManager.submitTask(task);
        } catch (IOException e) {
            throw new RuntimeException(e);
    }
        return CommonResult.success("合成视频提交成功,您排在第 " + (pos+1) + " ä¸ª");
    }
    @Resource
    private MediaTaskManager mediaTaskManager;
    @Override
    public CommonResult reMegerMedia(CourseMediaMegerVO updateReqVO) {
        Long id = updateReqVO.getId();
yudao-module-digitalcourse/yudao-module-digitalcourse-biz/src/main/java/cn/iocoder/yudao/module/digitalcourse/service/coursemedia/CourseMediaServiceUtil.java
@@ -73,9 +73,11 @@
     * @param updateReqVO
     * @return
     */
    @Async
    public void remoteMegerMedia(CourseMediaMegerVO updateReqVO) {
        FileUtil.clean("D:/heygem_data/face2face/temp/");
        CourseMediaDO courseMediaDO = courseMediaMapper.selectById(updateReqVO.getCourseMediaId());
        courseMediaDO.setStatus(1);
        courseMediaMapper.updateById(courseMediaDO);
        List<AppCourseScenesMegerReqVO> scenes = updateReqVO.getScenes();
        //获取数字人素材(声音、视频)
        String entityId = null;
@@ -295,7 +297,7 @@
        byte[] bytes = FileUtil.readBytes(FileUtil.file("D:/heygem_data/face2face/temp/"+"111111.mp4"));
        String file = fileApi.createFile(bytes);
        // å¦‚果成功,更新状态为1(成功)
        courseMediaDO.setStatus(1);
        courseMediaDO.setStatus(2);
        courseMediaDO.setPreviewUrl(file);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date();