Commit 5c73e5e1f417205447bdc2ead080082a274b629f

Authored by 芋道源码
Committed by Gitee
2 parents b4eed07d 5ba38dbd

!5 【解决todo】AI:音乐接入

Merge pull request !5 from 小新/master-jdk21-ai
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicGenerateEnum.java
... ... @@ -12,9 +12,8 @@ import lombok.Getter;
12 12 @Getter
13 13 public enum AiMusicGenerateEnum {
14 14  
15   - // TODO @xin:用数字哈。项目目前枚举都是数字
16   - LYRIC("lyric", "歌词模式"),
17   - DESCRIPTION("description", "描述模式");
  15 + LYRIC("1", "歌词模式"),
  16 + DESCRIPTION("2", "描述模式");
18 17  
19 18 /**
20 19 * 模式
... ...
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicStatusEnum.java
... ... @@ -12,27 +12,18 @@ import lombok.Getter;
12 12 @Getter
13 13 public enum AiMusicStatusEnum {
14 14  
15   - // TODO @xin:用数字哈。项目目前枚举都是数字
16 15 // @xin 文档中无失败这个返回值
17   - STREAMING("streaming", "进行中"),
18   - COMPLETE("complete", "完成");
  16 + STREAMING("10", "进行中"),
  17 + COMPLETE("20", "完成");
19 18  
20 19 /**
21 20 * 状态
22 21 */
23 22 private final String status;
  23 +
24 24 /**
25 25 * 状态名
26 26 */
27 27 private final String name;
28 28  
29   - public static AiMusicStatusEnum valueOfStatus(String status) {
30   - for (AiMusicStatusEnum statusEnum : AiMusicStatusEnum.values()) {
31   - if (statusEnum.getStatus().equals(status)) {
32   - return statusEnum;
33   - }
34   - }
35   - throw new IllegalArgumentException("未知会话状态: " + status);
36   - }
37   -
38 29 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.music;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4   -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoReqVO;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
5 5 import cn.iocoder.yudao.module.ai.service.music.AiMusicService;
6 6 import io.swagger.v3.oas.annotations.Operation;
7 7 import io.swagger.v3.oas.annotations.tags.Tag;
  8 +import jakarta.annotation.Resource;
8 9 import jakarta.validation.Valid;
9   -import lombok.RequiredArgsConstructor;
10 10 import org.springframework.web.bind.annotation.PostMapping;
11 11 import org.springframework.web.bind.annotation.RequestBody;
12 12 import org.springframework.web.bind.annotation.RequestMapping;
... ... @@ -19,16 +19,15 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
19 19 @Tag(name = "管理后台 - AI 音乐")
20 20 @RestController
21 21 @RequestMapping("/ai/music")
22   -@RequiredArgsConstructor // TODO @xin:通过 @Resource 注入哈
23 22 public class AiMusicController {
24 23  
25   - // TODO @xin:变量不用有 ai 前缀
26   - private final AiMusicService aiMusicService;
  24 + @Resource
  25 + private AiMusicService musicService;
27 26  
28 27 @PostMapping("/generate")
29 28 @Operation(summary = "音乐生成")
30   - public CommonResult<List<Long>> generateMusic(@RequestBody @Valid SunoReqVO sunoReqVO) {
31   - return success(aiMusicService.generateMusic(sunoReqVO));
  29 + public CommonResult<List<Long>> generateMusic(@RequestBody @Valid AiSunoGenerateReqVO reqVO) {
  30 + return success(musicService.generateMusic(reqVO));
32 31 }
33 32  
34 33 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiSunoGenerateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotBlank;
  5 +import lombok.Data;
  6 +
  7 +import java.util.List;
  8 +
  9 +/**
  10 + * @author xiaoxin
  11 + */
  12 +@Schema(description = "管理后台 - 音乐生成 Request VO")
  13 +@Data
  14 +public class AiSunoGenerateReqVO {
  15 +
  16 + @Schema(description = "用于生成音乐音频的提示", example = "创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")
  17 + private String prompt;
  18 +
  19 + @Schema(description = "是否纯音乐", example = "true")
  20 + private Boolean makeInstrumental;
  21 +
  22 + @Schema(description = "模型版本, 默认 chirp-v3.5", example = "chirp-v3.5")
  23 + private String modelVersion;// 参见 AiModelEnum 枚举
  24 +
  25 + @Schema(description = "音乐风格", example = "[\"pop\",\"jazz\",\"punk\"]")
  26 + private List<String> tags;
  27 +
  28 + @Schema(description = "音乐/歌曲名称", example = "夜空中最亮的星")
  29 + private String title;
  30 +
  31 + @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "Suno")
  32 + @NotBlank(message = "平台不能为空")
  33 + private String platform;// 参见 AiPlatformEnum 枚举
  34 +
  35 + @Schema(description = "生成模式 1(歌词模式), 2(描述模式)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  36 + @NotBlank(message = "生成模式不能为空")
  37 + private String generateMode;// 参见 AiMusicGenerateEnum 枚举
  38 +
  39 +}
0 40 \ No newline at end of file
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/SunoReqVO.java deleted
1   -package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
2   -
3   -import io.swagger.v3.oas.annotations.media.Schema;
4   -import jakarta.validation.constraints.NotBlank;
5   -import lombok.Data;
6   -
7   -import java.util.List;
8   -
9   -// TODO @xin:1)ai 前缀;2)AiSunoGenerateReqVO,要有生成哈。3)swaggger 缺少的属性,也最好加下,类似 example,类上的 swagger 等
10   -/**
11   - * @author xiaoxin
12   - */
13   -@Data
14   -public class SunoReqVO {
15   -
16   - @Schema(description = "用于生成音乐音频的提示")
17   - private String prompt;
18   -
19   - @Schema(description = "是否纯音乐")
20   - private Boolean makeInstrumental;
21   -
22   - // TODO @xin:我们自己是不是用 modelVersion?还是什么梗精准,减少非必要的缩写
23   - @Schema(description = "模型版本")
24   - private String mv;
25   -
26   - @Schema(description = "音乐风格")
27   - private List<String> tags;
28   -
29   - @Schema(description = "音乐/歌曲名称")
30   - private String title;
31   -
32   - @Schema(description = "平台")
33   - @NotBlank(message = "平台不能为空")
34   - private String platform;
35   -
36   - @Schema(description = "生成模式 lyric(歌词模式), description(描述模式)")
37   - @NotBlank(message = "生成模式不能为空")
38   - private String generateMode;
39   -
40   -}
41 0 \ No newline at end of file
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java
1 1 package cn.iocoder.yudao.module.ai.dal.dataobject.music;
2 2  
3   -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
4 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
5 4 import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;
6 5 import com.baomidou.mybatisplus.annotation.IdType;
7 6 import com.baomidou.mybatisplus.annotation.TableField;
8 7 import com.baomidou.mybatisplus.annotation.TableId;
9 8 import com.baomidou.mybatisplus.annotation.TableName;
10   -import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
  9 +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
11 10 import lombok.Data;
12 11  
13 12 import java.util.List;
14 13  
15   -// TODO @xin:注释完善下
  14 +
16 15 /**
17   - * @Author xiaoxin
18   - * @Date 2024/6/5
  16 + * AI 音乐 DO
  17 + *
  18 + * @author xiaoxin
19 19 */
20 20 @TableName("ai_music")
21 21 @Data
... ... @@ -98,25 +98,11 @@ public class AiMusicDO extends BaseDO {
98 98 /**
99 99 * 音乐风格标签
100 100 */
101   - @TableField(typeHandler = AiMusicTagsHandler.class)
  101 + @TableField(typeHandler = JacksonTypeHandler.class)
102 102 private List<String> tags;
103 103  
104 104 /**
105 105 * 任务编号
106 106 */
107 107 private String taskId;
108   -
109   - // TODO @xin:用 @TableField(typeHandler = JacksonTypeHandler.class) 替代即可
110   - public static class AiMusicTagsHandler extends AbstractJsonTypeHandler<Object> {
111   -
112   - @Override
113   - protected Object parse(String json) {
114   - return JsonUtils.parseArray(json, String.class);
115   - }
116   -
117   - @Override
118   - protected String toJson(Object obj) {
119   - return JsonUtils.toJsonString(obj);
120   - }
121   - }
122 108 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/job/SunoJob.java
1 1 package cn.iocoder.yudao.module.ai.job;
2 2  
3   -import cn.hutool.core.collection.CollUtil;
4   -import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
5 3 import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
6   -import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
7   -import cn.iocoder.yudao.module.ai.service.music.AiMusicConvert;
8 4 import cn.iocoder.yudao.module.ai.service.music.AiMusicService;
9 5 import jakarta.annotation.Resource;
10 6 import lombok.extern.slf4j.Slf4j;
11 7 import org.springframework.stereotype.Component;
12 8  
13   -import java.util.ArrayList;
14   -import java.util.HashMap;
15   -import java.util.List;
16   -import java.util.Map;
17 9  
18   -// TODO @xin:不要直接叫这个名字哈,要有它的目的
19 10 /**
20   - * 处理 Suno Job
  11 + * 同步 Suno 任务状态以及回写对应的音乐信息 Job
  12 + *
21 13 * @author xiaoxin
22 14 */
23 15 @Component
... ... @@ -25,37 +17,12 @@ import java.util.Map;
25 17 public class SunoJob implements JobHandler {
26 18  
27 19 @Resource
28   - private SunoApi sunoApi;
29   - @Resource
30 20 private AiMusicService musicService;
31 21  
32 22 @Override
33 23 public String execute(String param) {
34   - // TODO @xin:可以考虑,整个逻辑都下沉到 Service 里,有点类似 AccessLogCleanJob
35   - List<AiMusicDO> unCompletedTask = musicService.getUnCompletedTask();
36   -
37   - if (CollUtil.isEmpty(unCompletedTask)) {
38   - log.info("Suno 无进行中任务需要更新!");
39   - return "Suno 无进行中任务需要更新!";
40   - }
41   -
42   -
43   - log.info("Suno 开始同步, 共 [{}] 个任务!", unCompletedTask.size());
44   - //GET 请求,为避免参数过长,分批次处理
45   - CollUtil.split(unCompletedTask, 4)
46   - .forEach(chunk -> {
47   - Map<String, Long> taskIdMap = CollUtil.toMap(chunk, new HashMap<>(), AiMusicDO::getTaskId, AiMusicDO::getId);
48   - List<SunoApi.MusicData> musicTaskList = sunoApi.getMusicList(new ArrayList<>(taskIdMap.keySet()));
49   - if (CollUtil.isNotEmpty(musicTaskList)) {
50   - List<AiMusicDO> aiMusicDOS = AiMusicConvert.convertFrom(musicTaskList);
51   - //回填id
52   - aiMusicDOS.forEach(aiMusicDO -> aiMusicDO.setId(taskIdMap.get(aiMusicDO.getTaskId())));
53   - musicService.updateBatch(aiMusicDOS);
54   - } else {
55   - log.warn("Suno 任务同步失败, 任务ID: [{}]", taskIdMap.keySet());
56   - }
57   - });
58   -
59   - return "Suno 同步 - ".concat(String.valueOf(unCompletedTask.size())).concat(" 个任务!");
  24 + Integer count = musicService.syncMusicTask();
  25 + log.info("[execute][Suno 同步任务数量 [{}] 个]", count);
  26 + return String.format("Suno 同步 - [%s] 个任务", count);
60 27 }
61 28 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicConvert.java deleted
1   -package cn.iocoder.yudao.module.ai.service.music;
2   -
3   -import cn.hutool.core.text.StrPool;
4   -import cn.hutool.core.util.StrUtil;
5   -import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
6   -import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
7   -
8   -import java.util.List;
9   -import java.util.stream.Collectors;
10   -
11   -/**
12   - * AI 音乐 Convert
13   - *
14   - * @author xiaoxin
15   - */
16   -public class AiMusicConvert {
17   -
18   - public static AiMusicDO convertFrom(SunoApi.MusicData musicData) {
19   - return new AiMusicDO()
20   - .setTaskId(musicData.id())
21   - .setPrompt(musicData.prompt())
22   - .setGptDescriptionPrompt(musicData.gptDescriptionPrompt())
23   - .setAudioUrl(musicData.audioUrl())
24   - .setVideoUrl(musicData.videoUrl())
25   - .setImageUrl(musicData.imageUrl())
26   - .setLyric(musicData.lyric())
27   - .setTitle(musicData.title())
28   - .setStatus(musicData.status())
29   - .setModel(musicData.modelName())
30   - .setTags(StrUtil.isNotBlank(musicData.tags()) ? List.of(musicData.tags().split(StrPool.COMMA)) : null);
31   - }
32   -
33   - // TODO @xin:一般情况下,不用 convert,直接逻辑里 convert 就好啦。
34   - public static List<AiMusicDO> convertFrom(List<SunoApi.MusicData> list) {
35   - // TODO @xin:可以使用 CollectionUtils.convertList 简洁一点
36   - return list.stream()
37   - .map(AiMusicConvert::convertFrom)
38   - .collect(Collectors.toList());
39   - }
40   -
41   -
42   -}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicService.java
1 1 package cn.iocoder.yudao.module.ai.service.music;
2 2  
3   -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoReqVO;
  3 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
4 4 import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
5 5  
6 6 import java.util.List;
... ... @@ -18,7 +18,7 @@ public interface AiMusicService {
18 18 * @param reqVO 请求参数
19 19 * @return 生成的音乐ID
20 20 */
21   - List<Long> generateMusic(SunoReqVO reqVO);
  21 + List<Long> generateMusic(AiSunoGenerateReqVO reqVO);
22 22  
23 23 /**
24 24 * 获取未完成状态的任务
... ... @@ -27,6 +27,20 @@ public interface AiMusicService {
27 27 */
28 28 List<AiMusicDO> getUnCompletedTask();
29 29  
30   - Boolean updateBatch(List<AiMusicDO> aiMusicDOList);
  30 +
  31 + /**
  32 + * 同步音乐任务
  33 + *
  34 + * @return 同步数量
  35 + */
  36 + Integer syncMusicTask();
  37 +
  38 + /**
  39 + * 批量更新音乐信息
  40 + *
  41 + * @param musicDOS 音乐信息
  42 + * @return 是否成功
  43 + */
  44 + Boolean updateBatch(List<AiMusicDO> musicDOS);
31 45  
32 46 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java
... ... @@ -2,8 +2,10 @@ package cn.iocoder.yudao.module.ai.service.music;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.text.StrPool;
  5 +import cn.hutool.core.util.StrUtil;
5 6 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
6   -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoReqVO;
  7 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
7 9 import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
8 10 import cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper;
9 11 import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateEnum;
... ... @@ -13,8 +15,7 @@ import jakarta.annotation.Resource;
13 15 import lombok.extern.slf4j.Slf4j;
14 16 import org.springframework.stereotype.Service;
15 17  
16   -import java.util.Collections;
17   -import java.util.List;
  18 +import java.util.*;
18 19 import java.util.stream.Collectors;
19 20  
20 21 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
... ... @@ -35,13 +36,22 @@ public class AiMusicServiceImpl implements AiMusicService {
35 36 private AiMusicMapper musicMapper;
36 37  
37 38 @Override
38   - public List<Long> generateMusic(SunoReqVO reqVO) {
39   - // TODO @xin:是不是可以 if else 调用对应的 API,然后 insertMusicData 逻辑
40   - AiMusicGenerateEnum generateEnum = AiMusicGenerateEnum.valueOfMode(reqVO.getGenerateMode());
41   - return switch (generateEnum) {
42   - case DESCRIPTION -> descriptionMode(reqVO);
43   - case LYRIC -> lyricMode(reqVO);
44   - };
  39 + public List<Long> generateMusic(AiSunoGenerateReqVO reqVO) {
  40 + List<SunoApi.MusicData> musicDataList;
  41 + if (Objects.equals(AiMusicGenerateEnum.LYRIC.getMode(), reqVO.getGenerateMode())) {
  42 + //歌词模式
  43 + SunoApi.MusicGenerateRequest sunoReq = new SunoApi.MusicGenerateRequest(reqVO.getPrompt(), reqVO.getModelVersion(), CollUtil.join(reqVO.getTags(), StrPool.COMMA), reqVO.getTitle());
  44 + musicDataList = sunoApi.customGenerate(sunoReq);
  45 + } else if (Objects.equals(AiMusicGenerateEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) {
  46 + //描述模式
  47 + SunoApi.MusicGenerateRequest sunoReq = new SunoApi.MusicGenerateRequest(reqVO.getPrompt(), reqVO.getModelVersion(), reqVO.getMakeInstrumental());
  48 + musicDataList = sunoApi.generate(sunoReq);
  49 + } else {
  50 + log.error("未知的生成模式:{}", reqVO.getGenerateMode());
  51 + throw new IllegalArgumentException("未知的生成模式");
  52 + }
  53 + // 2. 插入数据库
  54 + return insertMusicData(musicDataList, reqVO.getGenerateMode(), reqVO.getPlatform());
45 55 }
46 56  
47 57 @Override
... ... @@ -50,36 +60,33 @@ public class AiMusicServiceImpl implements AiMusicService {
50 60 }
51 61  
52 62 @Override
53   - public Boolean updateBatch(List<AiMusicDO> aiMusicDOList) {
54   - return musicMapper.updateBatch(aiMusicDOList);
55   - }
56   -
57   - /**
58   - * 描述模式生成音乐
59   - *
60   - * @param reqVO 请求参数
61   - * @return 生成的音乐ID集合
62   - */
63   - public List<Long> descriptionMode(SunoReqVO reqVO) {
64   - // 1. 异步生成
65   - SunoApi.MusicGenerateRequest sunoReq = new SunoApi.MusicGenerateRequest(reqVO.getPrompt(), reqVO.getMv(), reqVO.getMakeInstrumental());
66   - List<SunoApi.MusicData> musicDataList = sunoApi.generate(sunoReq);
67   - // 2. 插入数据库
68   - return insertMusicData(musicDataList, reqVO.getGenerateMode(), reqVO.getPlatform());
  63 + public Integer syncMusicTask() {
  64 + List<AiMusicDO> unCompletedTask = this.getUnCompletedTask();
  65 + if (CollUtil.isEmpty(unCompletedTask)) {
  66 + log.info("Suno 无进行中任务需要更新!");
  67 + return 0;
  68 + }
  69 + log.info("Suno 开始同步, 共 [{}] 个任务!", unCompletedTask.size());
  70 + //GET 请求,为避免参数过长,分批次处理
  71 + CollUtil.split(unCompletedTask, 4)
  72 + .forEach(chunk -> {
  73 + Map<String, Long> taskIdMap = CollUtil.toMap(chunk, new HashMap<>(), AiMusicDO::getTaskId, AiMusicDO::getId);
  74 + List<SunoApi.MusicData> musicTaskList = sunoApi.getMusicList(new ArrayList<>(taskIdMap.keySet()));
  75 + if (CollUtil.isNotEmpty(musicTaskList)) {
  76 + List<AiMusicDO> aiMusicDOS = buildMusicDOList(musicTaskList);
  77 + //回填id
  78 + aiMusicDOS.forEach(aiMusicDO -> aiMusicDO.setId(taskIdMap.get(aiMusicDO.getTaskId())));
  79 + this.updateBatch(aiMusicDOS);
  80 + } else {
  81 + log.warn("Suno 任务同步失败, 任务ID: [{}]", taskIdMap.keySet());
  82 + }
  83 + });
  84 + return unCompletedTask.size();
69 85 }
70 86  
71   - /**
72   - * 歌词模式生成音乐
73   - *
74   - * @param reqVO 请求参数
75   - * @return 生成的音乐ID集合
76   - */
77   - public List<Long> lyricMode(SunoReqVO reqVO) {
78   - // 1. 异步生成
79   - SunoApi.MusicGenerateRequest sunoReq = new SunoApi.MusicGenerateRequest(reqVO.getPrompt(), reqVO.getMv(), CollUtil.join(reqVO.getTags(), StrPool.COMMA), reqVO.getTitle());
80   - List<SunoApi.MusicData> musicDataList = sunoApi.customGenerate(sunoReq);
81   - // 2. 插入数据库
82   - return insertMusicData(musicDataList, reqVO.getGenerateMode(), reqVO.getPlatform());
  87 + @Override
  88 + public Boolean updateBatch(List<AiMusicDO> musicDOS) {
  89 + return musicMapper.updateBatch(musicDOS);
83 90 }
84 91  
85 92 /**
... ... @@ -92,7 +99,7 @@ public class AiMusicServiceImpl implements AiMusicService {
92 99 if (CollUtil.isEmpty(musicDataList)) {
93 100 return Collections.emptyList();
94 101 }
95   - List<AiMusicDO> aiMusicDOList = AiMusicConvert.convertFrom(musicDataList).stream()
  102 + List<AiMusicDO> aiMusicDOList = buildMusicDOList(musicDataList).stream()
96 103 .map(musicDO -> musicDO.setUserId(getLoginUserId())
97 104 .setGenerateMode(generateMode)
98 105 .setPlatform(platform))
... ... @@ -103,4 +110,24 @@ public class AiMusicServiceImpl implements AiMusicService {
103 110 .collect(Collectors.toList());
104 111 }
105 112  
  113 + /**
  114 + * 构建 AiMusicDO 集合
  115 + *
  116 + * @param musicTaskList suno 音乐任务列表
  117 + * @return AiMusicDO 集合
  118 + */
  119 + private static List<AiMusicDO> buildMusicDOList(List<SunoApi.MusicData> musicTaskList) {
  120 + return CollectionUtils.convertList(musicTaskList, musicData -> new AiMusicDO()
  121 + .setTaskId(musicData.id())
  122 + .setPrompt(musicData.prompt())
  123 + .setGptDescriptionPrompt(musicData.gptDescriptionPrompt())
  124 + .setAudioUrl(musicData.audioUrl())
  125 + .setVideoUrl(musicData.videoUrl())
  126 + .setImageUrl(musicData.imageUrl())
  127 + .setLyric(musicData.lyric())
  128 + .setTitle(musicData.title())
  129 + .setStatus(Objects.equals("complete", musicData.status()) ? AiMusicStatusEnum.COMPLETE.getStatus() : AiMusicStatusEnum.STREAMING.getStatus())
  130 + .setModel(musicData.modelName())
  131 + .setTags(StrUtil.isNotBlank(musicData.tags()) ? List.of(musicData.tags().split(StrPool.COMMA)) : null));
  132 + }
106 133 }
... ...