Эх сурвалжийг харах

Merge remote-tracking branch 'origin/master-jdk21-ai' into master-jdk21-ai

cherishsince 10 сар өмнө
parent
commit
4c89342d5b
11 өөрчлөгдсөн 67 нэмэгдсэн , 48 устгасан
  1. 4 0
      yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiMusicStatusEnum.java
  2. 1 0
      yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiModelEnum.java
  3. 1 0
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/MusicController.java
  4. 2 1
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/SunoReqVO.java
  5. 10 3
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java
  6. 2 3
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/music/AiMusicMapper.java
  7. 10 5
      yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/MusicServiceImpl.java
  8. 2 1
      yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java
  9. 6 2
      yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/suno/SunoConfig.java
  10. 28 32
      yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/suno/api/SunoApi.java
  11. 1 1
      yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/suno/SunoTests.java

+ 4 - 0
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiMusicStatusEnum.java

@@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.ai.enums;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
+// TODO @xin:这个类,挪到 enums/music 包下;
+// TODO @xin:1)@author 这个是标准的 javadoc;2)@date 可以不要哈;3)可以加下枚举类的注释
 /**
  * @Author xiaoxin
  * @Date 2024/6/5
@@ -11,6 +13,8 @@ import lombok.Getter;
 @Getter
 public enum AiMusicStatusEnum {
 
+    // TODO @xin:是不是收敛成,只有 3 个:进行中,成功,失败;类似 AiImageStatusEnum
+
     SUBMITTED("submitted", "已提交"),
     QUEUED("queued", "排队中"),
     STREAMING("streaming", "进行中"),

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

@@ -50,6 +50,7 @@ public enum AiModelEnum {
     XING_HUO_3_0("星火大模型3.0", "generalv3", "/v3.1/chat"),
     XING_HUO_3_5("星火大模型3.5", "generalv3.5", "/v3.5/chat"),
 
+    // TODO @xin:// Suno;中间加个空格,会更清晰一点。一般来说,不同类型的单词之间,最好有空格。例如说,// 新增一个;再例如说;// 这是 1 个 create 逻辑
     //Suno
     SUNO_2( "SUNO-2", "chirp-v2-xxl-alpha",null),
     SUNO_3_0( "SUNO-3.0", "chirp-v3-0",null),

+ 1 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/MusicController.java

@@ -17,6 +17,7 @@ import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 
+// TODO @xin:AI 前缀;都要加下哈
 @Tag(name = "管理后台 - AI 音乐生成")
 @RestController
 @RequestMapping("/ai/music")

+ 2 - 1
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/SunoReqVO.java

@@ -4,12 +4,13 @@ import com.fasterxml.jackson.annotation.JsonInclude;
 import lombok.Data;
 
 @Data
-@JsonInclude(value = JsonInclude.Include.NON_NULL)
+@JsonInclude(value = JsonInclude.Include.NON_NULL) // TODO @xin:不用加这个哈
 public class SunoReqVO {
     /**
      * 用于生成音乐音频的提示
      */
     private String prompt;
+    // TODO @xin:Boolean,不使用基本类型。
     /**
      *  是否纯音乐
      */

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

@@ -18,6 +18,8 @@ import java.util.stream.Collectors;
 @TableName("ai_music")
 @Data
 public class AiMusicDO extends BaseDO {
+
+    // TODO @xin:@Schema 只在 VO 里使用,这里还是使用标准的注释哈
     @TableId(type = IdType.AUTO)
     @Schema(description = "编号")
     private Long id;
@@ -40,6 +42,7 @@ public class AiMusicDO extends BaseDO {
     @Schema(description = "视频地址")
     private String videoUrl;
 
+    // TODO @xin:需要关联下对应的枚举
     @Schema(description = "音乐状态")
     private String status;
 
@@ -49,19 +52,24 @@ public class AiMusicDO extends BaseDO {
     @Schema(description = "提示词")
     private String prompt;
 
+    // TODO @xin:生成模式,需要记录下;歌词、描述
+
+    // TODO @xin:多存储一个平台,platform;考虑未来可能有别的音乐接口
     @Schema(description = "模型")
     private String model;
 
     @Schema(description = "错误信息")
     private String errorMessage;
 
+    // TODO @xin:tags 要不要使用 List<String>
+
     @Schema(description = "音乐风格标签")
     private String tags;
 
-    @Schema(description = "任务id")
+    @Schema(description = "任务编号")
     private String taskId;
 
-
+    // TODO @xin:转换不放在 DO 里面哈。
 
     public static AiMusicDO convertFrom(SunoApi.MusicData musicData) {
         return new AiMusicDO()
@@ -84,5 +92,4 @@ public class AiMusicDO extends BaseDO {
                 .collect(Collectors.toList());
     }
 
-
 }

+ 2 - 3
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/music/AiMusicMapper.java

@@ -5,10 +5,9 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
 import org.apache.ibatis.annotations.Mapper;
 
 /**
- * @Author xiaoxin
- * @Date 2024/6/5
+ * AI 音乐 Mapper
+ * @author  xiaoxin
  */
 @Mapper
 public interface AiMusicMapper extends BaseMapperX<AiMusicDO> {
-
 }

+ 10 - 5
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/MusicServiceImpl.java

@@ -31,26 +31,29 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti
 @Slf4j
 public class MusicServiceImpl implements MusicService {
 
+    // TODO @xin:使用 @Resource 注入,整个项目保持统一哈;
     private final SunoApi sunoApi;
     private final AiMusicMapper musicMapper;
 
     private final Queue<String> taskQueue = new ConcurrentLinkedQueue<>();
 
+    // TODO @xin:要不把 descriptionMode、lyricMode 合并,同一个 generateMusic 方法,然后根据传入的 mode 模式:歌词、描述来区分?
 
     @Override
     public List<Long> descriptionMode(SunoReqVO reqVO) {
-        SunoApi.SunoReq sunoReq = new SunoApi.SunoReq(reqVO.getPrompt(), reqVO.getMv(), reqVO.isMakeInstrumental());
-        //默认异步
+        // 1. 异步生成
+        SunoApi.SunoRequest sunoReq = new SunoApi.SunoRequest(reqVO.getPrompt(), reqVO.getMv(), reqVO.isMakeInstrumental());
         List<SunoApi.MusicData> musicDataList = sunoApi.generate(sunoReq);
+        // 2. 插入数据库
         return insertMusicData(musicDataList);
     }
 
-
     @Override
     public List<Long> lyricMode(SunoLyricModeVO reqVO) {
-        SunoApi.SunoReq sunoReq = new SunoApi.SunoReq(reqVO.getPrompt(), reqVO.getMv(), reqVO.getTags(), reqVO.getTitle());
-        //默认异步
+        // 1. 异步生成
+        SunoApi.SunoRequest sunoReq = new SunoApi.SunoRequest(reqVO.getPrompt(), reqVO.getMv(), reqVO.getTags(), reqVO.getTitle());
         List<SunoApi.MusicData> musicDataList = sunoApi.customGenerate(sunoReq);
+        // 2. 插入数据库
         return insertMusicData(musicDataList);
     }
 
@@ -64,6 +67,7 @@ public class MusicServiceImpl implements MusicService {
         if (CollUtil.isEmpty(musicDataList)) {
             return Collections.emptyList();
         }
+        // TODO @xin:建议使用 insertBatch 方法,批量插入
         return AiMusicDO.convertFrom(musicDataList).stream()
                 .peek(musicDO -> musicMapper.insert(musicDO.setUserId(getLoginUserId())))
                 .peek(e -> Optional.of(e.getTaskId()).ifPresent(taskQueue::add))
@@ -71,6 +75,7 @@ public class MusicServiceImpl implements MusicService {
                 .collect(Collectors.toList());
     }
 
+    // TODO @xin:这个,改成标准的 job 来实现哈。从数据库加载任务,然后执行。
     @Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS)
     @Transactional
     public void flushSunoTask() {

+ 2 - 1
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java

@@ -118,8 +118,9 @@ public class YudaoAiProperties {
     public static class SunoProperties {
 
         private boolean enable = false;
+
         /**
-         * suno-api 服务的基本地址
+         * API 服务的基本地址
          */
         private String baseUrl;
 

+ 6 - 2
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/suno/SunoConfig.java

@@ -4,16 +4,20 @@ import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+// TODO @xin:不需要这个类哈,直接 SunoApi 传入 baseUrl 参数即可
 /**
- * @Author xiaoxin
- * @Date 2024/5/29
+ * Suno 配置类
+ *
+ * @author  xiaoxin
  */
 @Data
 @NoArgsConstructor
 @AllArgsConstructor
 public class SunoConfig {
+
     /**
      * suno-api服务的基本路径
      */
     private String baseUrl;
+
 }

+ 28 - 32
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/suno/api/SunoApi.java

@@ -27,14 +27,15 @@ import java.util.function.Predicate;
 public class SunoApi {
 
     private final WebClient webClient;
+
     private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();
     private final Function<ClientResponse, Mono<? extends Throwable>> EXCEPTION_FUNCTION = response -> response.bodyToMono(String.class)
             .handle((respBody, sink) -> {
+                // TODO @xin:最好是 request、response 都有哈
                 log.error("【suno-api】调用失败!resp: 【{}】", respBody);
                 sink.error(new IllegalStateException("【suno-api】调用失败!"));
             });
 
-
     public SunoApi(SunoConfig config) {
         this.webClient = WebClient.builder()
                 .baseUrl(config.getBaseUrl())
@@ -42,50 +43,49 @@ public class SunoApi {
                 .build();
     }
 
-    public List<MusicData> generate(SunoApi.SunoReq sunReq) {
+    public List<MusicData> generate(SunoRequest request) {
         return this.webClient.post()
                 .uri("/api/generate")
-                .body(Mono.just(sunReq), SunoApi.SunoReq.class)
+                .body(Mono.just(request), SunoRequest.class)
                 .retrieve()
                 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION)
-                .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() {
-                })
+                .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { })
                 .block();
     }
 
-    public List<MusicData> customGenerate(SunoApi.SunoReq sunReq) {
+    public List<MusicData> customGenerate(SunoRequest request) {
         return this.webClient.post()
                 .uri("/api/custom_generate")
-                .body(Mono.just(sunReq), SunoApi.SunoReq.class)
+                .body(Mono.just(request), SunoRequest.class)
                 .retrieve()
                 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION)
-                .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() {
-                })
+                .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { })
                 .block();
     }
 
+    // TODO @xin: 是不是叫 chatCompletion
     public List<MusicData> doChatCompletion(String prompt) {
         return this.webClient.post()
                 .uri("/v1/chat/completions")
-                .body(Mono.just(new SunoReq(prompt)), SunoApi.SunoReq.class)
+                .body(Mono.just(new SunoRequest(prompt)), SunoRequest.class)
                 .retrieve()
                 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION)
-                .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() {
-                })
+                .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { })
                 .block();
     }
 
     public LyricsData generateLyrics(String prompt) {
         return this.webClient.post()
                 .uri("/api/generate_lyrics")
-                .body(Mono.just(new SunoReq(prompt)), SunoApi.SunoReq.class)
+                .body(Mono.just(new SunoRequest(prompt)), SunoRequest.class)
                 .retrieve()
                 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION)
                 .bodyToMono(LyricsData.class)
                 .block();
     }
 
-
+    // TODO @xin:应该传入 List<String> ids
+    // TODO @xin:方法名,建议使用 getMusicList
     public List<MusicData> selectById(String ids) {
         return this.webClient.get()
                 .uri(uriBuilder -> uriBuilder
@@ -94,12 +94,11 @@ public class SunoApi {
                         .build())
                 .retrieve()
                 .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION)
-                .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() {
-                })
+                .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { })
                 .block();
     }
 
-
+    // TODO @xin:方法名,建议使用 getLimitUsage
     public LimitData selectLimit() {
         return this.webClient.get()
                 .uri("/api/get_limit")
@@ -109,7 +108,7 @@ public class SunoApi {
                 .block();
     }
 
-
+    // TODO @xin:可以改成 MusicGenerateRequest
     /**
      * 根据提示生成音频
      *
@@ -117,12 +116,12 @@ public class SunoApi {
      * @param tags             音乐风格
      * @param title            音乐名称
      * @param mv               模型
-     * @param waitAudio        false表示后台模式,仅返回音频任务信息,需要调用get API获取详细的音频信息。
-     *                         true表示同步模式,API最多等待100s,音频生成完毕后直接返回音频链接等信息,建议在GPT等agent中使用。
+     * @param waitAudio        false 表示后台模式,仅返回音频任务信息,需要调用 get API 获取详细的音频信息。
+     *                         true 表示同步模式,API 最多等待 100s,音频生成完毕后直接返回音频链接等信息,建议在 GPT  agent 中使用。
      * @param makeInstrumental 指示音乐音频是否为定制,如果为 true,则从歌词生成,否则从提示生成
      */
     @JsonInclude(value = JsonInclude.Include.NON_NULL)
-    public record SunoReq(
+    public record SunoRequest(
             String prompt,
             String tags,
             String title,
@@ -130,23 +129,23 @@ public class SunoApi {
             @JsonProperty("wait_audio") boolean waitAudio,
             @JsonProperty("make_instrumental") boolean makeInstrumental
     ) {
-        public SunoReq(String prompt) {
+
+        public SunoRequest(String prompt) {
             this(prompt, null, null, null, false, false);
         }
 
-        public SunoReq(String prompt, String mv, boolean makeInstrumental) {
+        public SunoRequest(String prompt, String mv, boolean makeInstrumental) {
             this(prompt, null, null, mv, false, makeInstrumental);
         }
 
-
-        public SunoReq(String prompt, String mv, String tags, String title) {
+        public SunoRequest(String prompt, String mv, String tags, String title) {
             this(prompt, tags, title, mv, false, false);
         }
-    }
 
+    }
 
     /**
-     * SunoAPI 响应的音频数据
+     * Suno API 响应的音频数据
      *
      * @param id                   音乐数据的 ID
      * @param title                音乐音频的标题
@@ -179,9 +178,8 @@ public class SunoApi {
     ) {
     }
 
-
     /**
-     * SunoAPI 响应的歌词数据。
+     * Suno API 响应的歌词数据。
      *
      * @param text   歌词
      * @param title  标题
@@ -194,9 +192,8 @@ public class SunoApi {
     ) {
     }
 
-
     /**
-     * SunoAPI 响应的限额数据,目前每日免费50
+     * Suno API 响应的限额数据,目前每日免费50
      */
     public record LimitData(
             @JsonProperty("credits_left") Long creditsLeft,
@@ -206,5 +203,4 @@ public class SunoApi {
     ) {
     }
 
-
 }

+ 1 - 1
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/suno/SunoTests.java

@@ -29,7 +29,7 @@ public class SunoTests {
 
     @Test
     public void generate() {
-        List<SunoApi.MusicData> generate = sunoApi.generate(new SunoApi.SunoReq("创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。"));
+        List<SunoApi.MusicData> generate = sunoApi.generate(new SunoApi.SunoRequest("创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。"));
         System.out.println(generate);
     }