浏览代码

【代码评审】AI:音乐接入

YunaiV 11 月之前
父节点
当前提交
ec1376f4cb

+ 1 - 0
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicGenerateEnum.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.enums.music;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
+// TODO @xiaoxin:这个也叫 AiMusicGenerateModeEnum 吧。虽然长,但是和项目统一一点。
 /**
  * AI 音乐状态的枚举
  *

+ 1 - 1
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicStatusEnum.java

@@ -12,7 +12,7 @@ import lombok.Getter;
 @Getter
 public enum AiMusicStatusEnum {
 
-    // @xin 文档中无失败这个返回值
+    // @xin 文档中无失败这个返回值 TODO @xin:用 Integer 哈。另外个枚举类也是
     STREAMING("10", "进行中"),
     COMPLETE("20", "完成");
 

+ 5 - 7
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiSunoGenerateReqVO.java

@@ -6,13 +6,11 @@ import lombok.Data;
 
 import java.util.List;
 
-/**
- * @author xiaoxin
- */
 @Schema(description = "管理后台 - 音乐生成 Request VO")
 @Data
 public class AiSunoGenerateReqVO {
 
+    // TODO @xin:每个参数,必要的是否必填校验
     @Schema(description = "用于生成音乐音频的提示", example = "创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")
     private String prompt;
 
@@ -20,7 +18,7 @@ public class AiSunoGenerateReqVO {
     private Boolean makeInstrumental;
 
     @Schema(description = "模型版本, 默认 chirp-v3.5", example = "chirp-v3.5")
-    private String modelVersion;// 参见 AiModelEnum 枚举
+    private String modelVersion; // 参见 AiModelEnum 枚举
 
     @Schema(description = "音乐风格", example = "[\"pop\",\"jazz\",\"punk\"]")
     private List<String> tags;
@@ -30,10 +28,10 @@ public class AiSunoGenerateReqVO {
 
     @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "Suno")
     @NotBlank(message = "平台不能为空")
-    private String platform;// 参见 AiPlatformEnum 枚举
+    private String platform; // 参见 AiPlatformEnum 枚举
 
-    @Schema(description = "生成模式 1(歌词模式), 2(描述模式)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    @Schema(description = "生成模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
     @NotBlank(message = "生成模式不能为空")
-    private String generateMode;// 参见 AiMusicGenerateEnum 枚举
+    private String generateMode; // 参见 AiMusicGenerateEnum 枚举
 
 }

+ 8 - 7
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java

@@ -68,7 +68,6 @@ public class AiImageDO extends BaseDO {
      */
     private Integer height;
 
-    // TODO @fan:这种就注释绘画状态,然后枚举类关联下就好啦
     /**
      * 生成状态
      *
@@ -76,6 +75,11 @@ public class AiImageDO extends BaseDO {
      */
     private Integer status;
 
+    /**
+     * 绘画错误信息
+     */
+    private String errorMessage;
+
     /**
      * 图片地址
      */
@@ -101,15 +105,12 @@ public class AiImageDO extends BaseDO {
     private List<MidjourneyApi.Button> buttons;
 
     /**
-     * midjourney proxy 关联的 task id
+     * 任务编号
+     *
+     * 1. midjourney proxy:关联的 task id
      */
     private String taskId;
 
-    /**
-     * 绘画错误信息
-     */
-    private String errorMessage;
-
     public static class ButtonTypeHandler extends AbstractJsonTypeHandler<Object> {
 
         @Override

+ 3 - 1
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java

@@ -11,7 +11,6 @@ import lombok.Data;
 
 import java.util.List;
 
-
 /**
  * AI 音乐 DO
  *
@@ -29,6 +28,8 @@ public class AiMusicDO extends BaseDO {
 
     /**
      * 用户编号
+     *
+     * 关联 AdminUserDO 的 userId 字段
      */
     private Long userId;
 
@@ -105,4 +106,5 @@ public class AiMusicDO extends BaseDO {
      * 任务编号
      */
     private String taskId;
+
 }

+ 1 - 1
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/job/image/MidjourneySyncJob.java → yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/job/image/AiMidjourneySyncJob.java

@@ -13,7 +13,7 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @Slf4j
-public class MidjourneySyncJob implements JobHandler {
+public class AiMidjourneySyncJob implements JobHandler {
 
     @Resource
     private AiImageService imageService;

+ 6 - 5
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/job/SunoJob.java → yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/job/sun/AiSunoSyncJob.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.ai.job;
+package cn.iocoder.yudao.module.ai.job.sun;
 
 import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
 import cn.iocoder.yudao.module.ai.service.music.AiMusicService;
@@ -14,15 +14,16 @@ import org.springframework.stereotype.Component;
  */
 @Component
 @Slf4j
-public class SunoJob implements JobHandler {
+public class AiSunoSyncJob implements JobHandler {
 
     @Resource
     private AiMusicService musicService;
 
     @Override
     public String execute(String param) {
-        Integer count = musicService.syncMusicTask();
-        log.info("[execute][Suno 同步任务数量 [{}] 个]", count);
-        return String.format("Suno 同步 -  [%s]任务", count);
+        Integer count = musicService.syncMusic();
+        log.info("[execute][同步 Suno ({}) 个]", count);
+        return String.format("同步 Suno %s 个", count);
     }
+
 }

+ 1 - 2
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicService.java

@@ -27,13 +27,12 @@ public interface AiMusicService {
      */
     List<AiMusicDO> getUnCompletedTask();
 
-
     /**
      * 同步音乐任务
      *
      * @return 同步数量
      */
-    Integer syncMusicTask();
+    Integer syncMusic();
 
     /**
      * 批量更新音乐信息

+ 34 - 20
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java

@@ -39,51 +39,61 @@ public class AiMusicServiceImpl implements AiMusicService {
     public List<Long> generateMusic(AiSunoGenerateReqVO reqVO) {
         List<SunoApi.MusicData> musicDataList;
         if (Objects.equals(AiMusicGenerateEnum.LYRIC.getMode(), reqVO.getGenerateMode())) {
-            //歌词模式
-            SunoApi.MusicGenerateRequest sunoReq = new SunoApi.MusicGenerateRequest(reqVO.getPrompt(), reqVO.getModelVersion(), CollUtil.join(reqVO.getTags(), StrPool.COMMA), reqVO.getTitle());
+            // 1.1 歌词模式
+            SunoApi.MusicGenerateRequest sunoReq = new SunoApi.MusicGenerateRequest(
+                    reqVO.getPrompt(), reqVO.getModelVersion(), CollUtil.join(reqVO.getTags(), StrPool.COMMA), reqVO.getTitle());
             musicDataList = sunoApi.customGenerate(sunoReq);
         } else if (Objects.equals(AiMusicGenerateEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) {
-            //描述模式
-            SunoApi.MusicGenerateRequest sunoReq = new SunoApi.MusicGenerateRequest(reqVO.getPrompt(), reqVO.getModelVersion(), reqVO.getMakeInstrumental());
+            // 1.2 描述模式
+            SunoApi.MusicGenerateRequest sunoReq = new SunoApi.MusicGenerateRequest(
+                    reqVO.getPrompt(), reqVO.getModelVersion(), reqVO.getMakeInstrumental());
             musicDataList = sunoApi.generate(sunoReq);
         } else {
+            // TODO @xin:不用 log error,直接抛异常,吧 reqVO 呆进去,有全局处理的哈
             log.error("未知的生成模式:{}", reqVO.getGenerateMode());
             throw new IllegalArgumentException("未知的生成模式");
         }
+
         // 2. 插入数据库
+        // TODO @xin:因为 insertMusicData 复用的比较少,所以不用愁单独的方法,直接写在这里就好啦
         return insertMusicData(musicDataList, reqVO.getGenerateMode(), reqVO.getPlatform());
     }
 
+    // TODO @xin:1)service 里面,不要直接查询 db;2)不要用 ne,用 STREAMING 哈
     @Override
     public List<AiMusicDO> getUnCompletedTask() {
         return musicMapper.selectList(new LambdaQueryWrapper<AiMusicDO>().ne(AiMusicDO::getStatus, AiMusicStatusEnum.COMPLETE.getStatus()));
     }
 
     @Override
-    public Integer syncMusicTask() {
+    public Integer syncMusic() {
         List<AiMusicDO> unCompletedTask = this.getUnCompletedTask();
         if (CollUtil.isEmpty(unCompletedTask)) {
+            // TODO @xin:这里不用打,反正 Job 也打了
             log.info("Suno 无进行中任务需要更新!");
             return 0;
         }
-        log.info("Suno 开始同步, 共 [{}] 个任务!", unCompletedTask.size());
-        //GET 请求,为避免参数过长,分批次处理
-        CollUtil.split(unCompletedTask, 4)
-                .forEach(chunk -> {
-                    Map<String, Long> taskIdMap = CollUtil.toMap(chunk, new HashMap<>(), AiMusicDO::getTaskId, AiMusicDO::getId);
-                    List<SunoApi.MusicData> musicTaskList = sunoApi.getMusicList(new ArrayList<>(taskIdMap.keySet()));
-                    if (CollUtil.isNotEmpty(musicTaskList)) {
-                        List<AiMusicDO> aiMusicDOS = buildMusicDOList(musicTaskList);
-                        //回填id
-                        aiMusicDOS.forEach(aiMusicDO -> aiMusicDO.setId(taskIdMap.get(aiMusicDO.getTaskId())));
-                        this.updateBatch(aiMusicDOS);
-                    } else {
-                        log.warn("Suno 任务同步失败, 任务ID: [{}]", taskIdMap.keySet());
-                    }
-                });
+        log.info("[syncMusic][Suno 开始同步, 共 ({}) 个任务]", unCompletedTask.size());
+        // GET 请求,为避免参数过长,分批次处理
+        // TODO @xin:建议批量更大一些。
+        CollUtil.split(unCompletedTask, 4).forEach(chunk -> {
+            // TODO @xin:可以使用 CollectionUtils 里的 map 转换
+            Map<String, Long> taskIdMap = CollUtil.toMap(chunk, new HashMap<>(), AiMusicDO::getTaskId, AiMusicDO::getId);
+            List<SunoApi.MusicData> musicTaskList = sunoApi.getMusicList(new ArrayList<>(taskIdMap.keySet()));
+            // TODO @xin:查询不到,直接 return;这样真正逻辑的 85 - 87 就不用多一层括号
+            if (CollUtil.isNotEmpty(musicTaskList)) {
+                List<AiMusicDO> aiMusicDOS = buildMusicDOList(musicTaskList);
+                //回填id
+                aiMusicDOS.forEach(aiMusicDO -> aiMusicDO.setId(taskIdMap.get(aiMusicDO.getTaskId())));
+                this.updateBatch(aiMusicDOS);
+            } else {
+                log.warn("Suno 任务同步失败, 任务ID: [{}]", taskIdMap.keySet());
+            }
+        });
         return unCompletedTask.size();
     }
 
+    // TODO @xin:这个方法,看着不用啦
     @Override
     public Boolean updateBatch(List<AiMusicDO> musicDOS) {
         return musicMapper.updateBatch(musicDOS);
@@ -105,6 +115,7 @@ public class AiMusicServiceImpl implements AiMusicService {
                         .setPlatform(platform))
                 .toList();
         musicMapper.insertBatch(aiMusicDOList);
+        // TODO @xin:用 CollectionUtils 简化操作
         return aiMusicDOList.stream()
                 .map(AiMusicDO::getId)
                 .collect(Collectors.toList());
@@ -117,6 +128,7 @@ public class AiMusicServiceImpl implements AiMusicService {
      * @return AiMusicDO 集合
      */
     private static List<AiMusicDO> buildMusicDOList(List<SunoApi.MusicData> musicTaskList) {
+        // TODO @xin:想通的变量,放在同一行,避免过长。
         return CollectionUtils.convertList(musicTaskList, musicData -> new AiMusicDO()
                 .setTaskId(musicData.id())
                 .setPrompt(musicData.prompt())
@@ -128,6 +140,8 @@ public class AiMusicServiceImpl implements AiMusicService {
                 .setTitle(musicData.title())
                 .setStatus(Objects.equals("complete", musicData.status()) ? AiMusicStatusEnum.COMPLETE.getStatus() : AiMusicStatusEnum.STREAMING.getStatus())
                 .setModel(musicData.modelName())
+                // TODO @xin:可以用 hutool 的 StrUtil 的 split 之类的
                 .setTags(StrUtil.isNotBlank(musicData.tags()) ? List.of(musicData.tags().split(StrPool.COMMA)) : null));
     }
+
 }