Commit 9d6b615f10cc644a8de61a68e74abcc69998d07c

Authored by YunaiV
1 parent 671c3016

【功能新增】AI:音乐管理 50%

Showing 19 changed files with 326 additions and 43 deletions
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java
@@ -29,13 +29,16 @@ public interface ErrorCodeConstants { @@ -29,13 +29,16 @@ public interface ErrorCodeConstants {
29 29
30 // ========== API 聊天消息 1-040-004-000 ========== 30 // ========== API 聊天消息 1-040-004-000 ==========
31 31
32 - ErrorCode AI_CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "消息不存在!");  
33 - ErrorCode AI_CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "Stream 对话异常!"); 32 + ErrorCode CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "消息不存在!");
  33 + ErrorCode CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "Stream 对话异常!");
34 34
35 // ========== API 绘画 1-040-005-000 ========== 35 // ========== API 绘画 1-040-005-000 ==========
36 36
37 - ErrorCode AI_IMAGE_NOT_EXISTS = new ErrorCode(1_022_005_000, "图片不存在!");  
38 - ErrorCode AI_IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_022_005_001, "Midjourney 提交失败!原因:{}");  
39 - ErrorCode AI_IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_022_005_002, "Midjourney 按钮 customId 不存在! {}"); 37 + ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_022_005_000, "图片不存在!");
  38 + ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_022_005_001, "Midjourney 提交失败!原因:{}");
  39 + ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_022_005_002, "Midjourney 按钮 customId 不存在! {}");
  40 +
  41 + // ========== API 音乐 1-040-006-000 ==========
  42 + ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_022_006_000, "音乐不存在!");
40 43
41 } 44 }
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicGenerateModeEnum.java
1 package cn.iocoder.yudao.module.ai.enums.music; 1 package cn.iocoder.yudao.module.ai.enums.music;
2 2
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
3 import lombok.AllArgsConstructor; 4 import lombok.AllArgsConstructor;
4 import lombok.Getter; 5 import lombok.Getter;
5 6
  7 +import java.util.Arrays;
  8 +
6 /** 9 /**
7 * AI 音乐状态的枚举 10 * AI 音乐状态的枚举
8 * 11 *
@@ -10,7 +13,7 @@ import lombok.Getter; @@ -10,7 +13,7 @@ import lombok.Getter;
10 */ 13 */
11 @AllArgsConstructor 14 @AllArgsConstructor
12 @Getter 15 @Getter
13 -public enum AiMusicGenerateModeEnum { 16 +public enum AiMusicGenerateModeEnum implements IntArrayValuable {
14 17
15 LYRIC(1, "歌词模式"), 18 LYRIC(1, "歌词模式"),
16 DESCRIPTION(2, "描述模式"); 19 DESCRIPTION(2, "描述模式");
@@ -24,4 +27,11 @@ public enum AiMusicGenerateModeEnum { @@ -24,4 +27,11 @@ public enum AiMusicGenerateModeEnum {
24 */ 27 */
25 private final String name; 28 private final String name;
26 29
  30 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiMusicGenerateModeEnum::getMode).toArray();
  31 +
  32 + @Override
  33 + public int[] array() {
  34 + return ARRAYS;
  35 + }
  36 +
27 } 37 }
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicStatusEnum.java
1 package cn.iocoder.yudao.module.ai.enums.music; 1 package cn.iocoder.yudao.module.ai.enums.music;
2 2
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
3 import lombok.AllArgsConstructor; 4 import lombok.AllArgsConstructor;
4 import lombok.Getter; 5 import lombok.Getter;
5 6
  7 +import java.util.Arrays;
  8 +
6 /** 9 /**
7 * AI 音乐状态的枚举 10 * AI 音乐状态的枚举
8 * 11 *
@@ -10,7 +13,7 @@ import lombok.Getter; @@ -10,7 +13,7 @@ import lombok.Getter;
10 */ 13 */
11 @AllArgsConstructor 14 @AllArgsConstructor
12 @Getter 15 @Getter
13 -public enum AiMusicStatusEnum { 16 +public enum AiMusicStatusEnum implements IntArrayValuable {
14 17
15 IN_PROGRESS(10, "进行中"), 18 IN_PROGRESS(10, "进行中"),
16 SUCCESS(20, "已完成"); 19 SUCCESS(20, "已完成");
@@ -25,4 +28,11 @@ public enum AiMusicStatusEnum { @@ -25,4 +28,11 @@ public enum AiMusicStatusEnum {
25 */ 28 */
26 private final String name; 29 private final String name;
27 30
  31 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiMusicStatusEnum::getStatus).toArray();
  32 +
  33 + @Override
  34 + public int[] array() {
  35 + return ARRAYS;
  36 + }
  37 +
28 } 38 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java
1 package cn.iocoder.yudao.module.ai.controller.admin.image; 1 package cn.iocoder.yudao.module.ai.controller.admin.image;
2 2
3 -import cn.hutool.core.collection.CollUtil;  
4 import cn.hutool.core.util.ObjUtil; 3 import cn.hutool.core.util.ObjUtil;
5 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; 4 import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
6 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
@@ -26,9 +25,7 @@ import org.springframework.security.access.prepost.PreAuthorize; @@ -26,9 +25,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
26 import org.springframework.validation.annotation.Validated; 25 import org.springframework.validation.annotation.Validated;
27 import org.springframework.web.bind.annotation.*; 26 import org.springframework.web.bind.annotation.*;
28 27
29 -import java.util.Collections;  
30 import java.util.List; 28 import java.util.List;
31 -import java.util.Objects;  
32 29
33 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 30 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
34 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 31 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@@ -42,15 +39,16 @@ public class AiImageController { @@ -42,15 +39,16 @@ public class AiImageController {
42 @Resource 39 @Resource
43 private AiImageService imageService; 40 private AiImageService imageService;
44 41
45 - @Operation(summary = "获取【我的】绘图分页")  
46 @GetMapping("/my-page") 42 @GetMapping("/my-page")
  43 + @Operation(summary = "获取【我的】绘图分页")
47 public CommonResult<PageResult<AiImageRespVO>> getImagePageMy(@Validated PageParam pageReqVO) { 44 public CommonResult<PageResult<AiImageRespVO>> getImagePageMy(@Validated PageParam pageReqVO) {
48 PageResult<AiImageDO> pageResult = imageService.getImagePageMy(getLoginUserId(), pageReqVO); 45 PageResult<AiImageDO> pageResult = imageService.getImagePageMy(getLoginUserId(), pageReqVO);
49 return success(BeanUtils.toBean(pageResult, AiImageRespVO.class)); 46 return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));
50 } 47 }
51 48
52 - @Operation(summary = "获取【我的】绘图记录")  
53 @GetMapping("/get-my") 49 @GetMapping("/get-my")
  50 + @Operation(summary = "获取【我的】绘图记录")
  51 + @Parameter(name = "id", required = true, description = "绘画编号", example = "1024")
54 public CommonResult<AiImageRespVO> getImageMy(@RequestParam("id") Long id) { 52 public CommonResult<AiImageRespVO> getImageMy(@RequestParam("id") Long id) {
55 AiImageDO image = imageService.getImage(id); 53 AiImageDO image = imageService.getImage(id);
56 if (image == null || ObjUtil.notEqual(getLoginUserId(), image.getUserId())) { 54 if (image == null || ObjUtil.notEqual(getLoginUserId(), image.getUserId())) {
@@ -59,17 +57,13 @@ public class AiImageController { @@ -59,17 +57,13 @@ public class AiImageController {
59 return success(BeanUtils.toBean(image, AiImageRespVO.class)); 57 return success(BeanUtils.toBean(image, AiImageRespVO.class));
60 } 58 }
61 59
62 - @Operation(summary = "获取【我的】绘图记录 - ids")  
63 @GetMapping("/get-my-ids") 60 @GetMapping("/get-my-ids")
  61 + @Operation(summary = "获取【我的】绘图记录列表")
  62 + @Parameter(name = "ids", required = true, description = "绘画编号数组", example = "1024,2048")
64 public CommonResult<List<AiImageRespVO>> getImageMyIds(@RequestParam("ids") List<Long> ids) { 63 public CommonResult<List<AiImageRespVO>> getImageMyIds(@RequestParam("ids") List<Long> ids) {
65 List<AiImageDO> imageList = imageService.getImageByIds(ids); 64 List<AiImageDO> imageList = imageService.getImageByIds(ids);
66 - if (CollUtil.isEmpty(imageList)) {  
67 - return success(Collections.emptyList());  
68 - }  
69 - List<AiImageDO> userImageList = imageList.stream()  
70 - .map(item -> ObjUtil.equal(getLoginUserId(), item.getUserId()) ? item : null)  
71 - .filter(Objects::nonNull).toList();  
72 - return success(BeanUtils.toBean(userImageList, AiImageRespVO.class)); 65 + imageList.removeIf(item -> !ObjUtil.equal(getLoginUserId(), item.getUserId()));
  66 + return success(BeanUtils.toBean(imageList, AiImageRespVO.class));
73 } 67 }
74 68
75 @Operation(summary = "生成图片") 69 @Operation(summary = "生成图片")
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePageReqVO.java
@@ -20,7 +20,7 @@ public class AiImagePageReqVO extends PageParam { @@ -20,7 +20,7 @@ public class AiImagePageReqVO extends PageParam {
20 @Schema(description = "用户编号", example = "28987") 20 @Schema(description = "用户编号", example = "28987")
21 private Long userId; 21 private Long userId;
22 22
23 - @Schema(description = "平台") 23 + @Schema(description = "平台", example = "OpenAI")
24 private String platform; 24 private String platform;
25 25
26 @Schema(description = "绘画状态", example = "1") 26 @Schema(description = "绘画状态", example = "1")
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiImageController.http renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.http
1 -### 生成音乐:Suno + 1 +### 生成音乐:Suno + 歌词模式
2 POST {{baseUrl}}/ai/music/generate 2 POST {{baseUrl}}/ai/music/generate
3 Content-Type: application/json 3 Content-Type: application/json
4 Authorization: {{token}} 4 Authorization: {{token}}
@@ -10,4 +10,17 @@ Authorization: {{token}} @@ -10,4 +10,17 @@ Authorization: {{token}}
10 "modelVersion": "chirp-v3.5", 10 "modelVersion": "chirp-v3.5",
11 "tags": ["Happy"], 11 "tags": ["Happy"],
12 "title": "Happy Song" 12 "title": "Happy Song"
  13 +}
  14 +
  15 +### 生成音乐:Suno + 描述模式
  16 +POST {{baseUrl}}/ai/music/generate
  17 +Content-Type: application/json
  18 +Authorization: {{token}}
  19 +
  20 +{
  21 + "platform": "Suno",
  22 + "generateMode": 2,
  23 + "prompt": "来一首快乐的歌曲",
  24 + "makeInstrumental": false,
  25 + "title": "Happy Song"
13 } 26 }
14 \ No newline at end of file 27 \ No newline at end of file
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.java
1 package cn.iocoder.yudao.module.ai.controller.admin.music; 1 package cn.iocoder.yudao.module.ai.controller.admin.music;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicRespVO;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdatePublicStatusReqVO;
4 import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO; 9 import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
  10 +import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
5 import cn.iocoder.yudao.module.ai.service.music.AiMusicService; 11 import cn.iocoder.yudao.module.ai.service.music.AiMusicService;
6 import io.swagger.v3.oas.annotations.Operation; 12 import io.swagger.v3.oas.annotations.Operation;
  13 +import io.swagger.v3.oas.annotations.Parameter;
7 import io.swagger.v3.oas.annotations.tags.Tag; 14 import io.swagger.v3.oas.annotations.tags.Tag;
8 import jakarta.annotation.Resource; 15 import jakarta.annotation.Resource;
9 import jakarta.validation.Valid; 16 import jakarta.validation.Valid;
10 -import org.springframework.web.bind.annotation.PostMapping;  
11 -import org.springframework.web.bind.annotation.RequestBody;  
12 -import org.springframework.web.bind.annotation.RequestMapping;  
13 -import org.springframework.web.bind.annotation.RestController; 17 +import org.springframework.security.access.prepost.PreAuthorize;
  18 +import org.springframework.web.bind.annotation.*;
14 19
15 import java.util.List; 20 import java.util.List;
16 21
@@ -31,4 +36,31 @@ public class AiMusicController { @@ -31,4 +36,31 @@ public class AiMusicController {
31 return success(musicService.generateMusic(getLoginUserId(), reqVO)); 36 return success(musicService.generateMusic(getLoginUserId(), reqVO));
32 } 37 }
33 38
  39 + // ================ 绘图管理 ================
  40 +
  41 + @GetMapping("/page")
  42 + @Operation(summary = "获得音乐分页")
  43 + @PreAuthorize("@ss.hasPermission('ai:music:query')")
  44 + public CommonResult<PageResult<AiMusicRespVO>> getMusicPage(@Valid AiMusicPageReqVO pageReqVO) {
  45 + PageResult<AiMusicDO> pageResult = musicService.getMusicPage(pageReqVO);
  46 + return success(BeanUtils.toBean(pageResult, AiMusicRespVO.class));
  47 + }
  48 +
  49 + @DeleteMapping("/delete")
  50 + @Operation(summary = "删除音乐")
  51 + @Parameter(name = "id", description = "编号", required = true)
  52 + @PreAuthorize("@ss.hasPermission('ai:music:delete')")
  53 + public CommonResult<Boolean> deleteMusic(@RequestParam("id") Long id) {
  54 + musicService.deleteMusic(id);
  55 + return success(true);
  56 + }
  57 +
  58 + @PutMapping("/update")
  59 + @Operation(summary = "更新音乐发布状态")
  60 + @PreAuthorize("@ss.hasPermission('ai:music:update')")
  61 + public CommonResult<Boolean> updateMusicPublicStatus(@Valid @RequestBody AiMusicUpdatePublicStatusReqVO updateReqVO) {
  62 + musicService.updateMusicPublicStatus(updateReqVO);
  63 + return success(true);
  64 + }
  65 +
34 } 66 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;
  6 +import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;
  7 +import io.swagger.v3.oas.annotations.media.Schema;
  8 +import lombok.Data;
  9 +import lombok.EqualsAndHashCode;
  10 +import lombok.ToString;
  11 +import org.springframework.format.annotation.DateTimeFormat;
  12 +
  13 +import java.time.LocalDateTime;
  14 +
  15 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  16 +
  17 +@Schema(description = "管理后台 - AI 音乐分页 Request VO")
  18 +@Data
  19 +@EqualsAndHashCode(callSuper = true)
  20 +@ToString(callSuper = true)
  21 +public class AiMusicPageReqVO extends PageParam {
  22 +
  23 + @Schema(description = "用户编号", example = "12212")
  24 + private Long userId;
  25 +
  26 + @Schema(description = "音乐名称", example = "夜空中最亮的星")
  27 + private String title;
  28 +
  29 + @Schema(description = "音乐状态", example = "20")
  30 + @InEnum(AiMusicStatusEnum.class)
  31 + private Integer status;
  32 +
  33 + @Schema(description = "生成模式", example = "1")
  34 + @InEnum(AiMusicGenerateModeEnum.class)
  35 + private Integer generateMode;
  36 +
  37 + @Schema(description = "是否发布", example = "true")
  38 + private Boolean publicStatus;
  39 +
  40 + @Schema(description = "创建时间")
  41 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  42 + private LocalDateTime[] createTime;
  43 +
  44 +}
0 \ No newline at end of file 45 \ 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/AiMusicRespVO.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 lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +
  9 +@Schema(description = "管理后台 - AI 音乐 Response VO")
  10 +@Data
  11 +public class AiMusicRespVO {
  12 +
  13 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
  14 + private Long id;
  15 +
  16 + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "12212")
  17 + private Long userId;
  18 +
  19 + @Schema(description = "音乐名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "夜空中最亮的星")
  20 + private String title;
  21 +
  22 + @Schema(description = "歌词", example = "oh~卖糕的")
  23 + private String lyric;
  24 +
  25 + @Schema(description = "图片地址", example = "https://www.iocoder.cn")
  26 + private String imageUrl;
  27 +
  28 + @Schema(description = "音频地址", example = "https://www.iocoder.cn")
  29 + private String audioUrl;
  30 +
  31 + @Schema(description = "视频地址", example = "https://www.iocoder.cn")
  32 + private String videoUrl;
  33 +
  34 + @Schema(description = "音乐状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
  35 + private Integer status;
  36 +
  37 + @Schema(description = "描述词", example = "一首轻快的歌曲")
  38 + private String gptDescriptionPrompt;
  39 +
  40 + @Schema(description = "提示词", example = "创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")
  41 + private String prompt;
  42 +
  43 + @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "Suno")
  44 + private String platform;
  45 +
  46 + @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "chirp-v3.5")
  47 + private String model;
  48 +
  49 + @Schema(description = "生成模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  50 + private Integer generateMode;
  51 +
  52 + @Schema(description = "音乐风格标签")
  53 + private List<String> tags;
  54 +
  55 + @Schema(description = "是否发布", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  56 + private Boolean publicStatus;
  57 +
  58 + @Schema(description = "任务编号", example = "11369")
  59 + private String taskId;
  60 +
  61 + @Schema(description = "错误信息")
  62 + private String errorMessage;
  63 +
  64 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  65 + private LocalDateTime createTime;
  66 +
  67 +}
0 \ No newline at end of file 68 \ 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/AiMusicUpdatePublicStatusReqVO.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.NotNull;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - AI 音乐修改发布状态 Request VO")
  8 +@Data
  9 +public class AiMusicUpdatePublicStatusReqVO {
  10 +
  11 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
  12 + private Long id;
  13 +
  14 + @Schema(description = "是否发布", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
  15 + @NotNull(message = "是否发布不能为空")
  16 + private Boolean publicStatus;
  17 +
  18 +}
0 \ No newline at end of file 19 \ 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/AiSunoGenerateReqVO.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.music.vo; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
2 2
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import jakarta.validation.constraints.NotBlank; 4 import jakarta.validation.constraints.NotBlank;
  5 +import jakarta.validation.constraints.NotEmpty;
5 import jakarta.validation.constraints.NotNull; 6 import jakarta.validation.constraints.NotNull;
6 import lombok.Data; 7 import lombok.Data;
7 8
@@ -15,6 +16,10 @@ public class AiSunoGenerateReqVO { @@ -15,6 +16,10 @@ public class AiSunoGenerateReqVO {
15 @NotBlank(message = "平台不能为空") 16 @NotBlank(message = "平台不能为空")
16 private String platform; // 参见 AiPlatformEnum 枚举 17 private String platform; // 参见 AiPlatformEnum 枚举
17 18
  19 + /**
  20 + * 1. 描述模式:描述词 + 是否纯音乐 + 模型 TODO @xin:目前貌似描述词没弄对?看着不是 prompt 字段(也可能我弄错了)。可以微信再沟通下哈
  21 + * 2. 歌词模式:歌词 + 音乐风格 + 标题 + 模型 TODO @xin:目前这块少传递了标题;
  22 + */
18 @Schema(description = "生成模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") 23 @Schema(description = "生成模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
19 @NotNull(message = "生成模式不能为空") 24 @NotNull(message = "生成模式不能为空")
20 private Integer generateMode; // 参见 AiMusicGenerateModeEnum 枚举 25 private Integer generateMode; // 参见 AiMusicGenerateModeEnum 枚举
@@ -26,7 +31,9 @@ public class AiSunoGenerateReqVO { @@ -26,7 +31,9 @@ public class AiSunoGenerateReqVO {
26 @Schema(description = "是否纯音乐", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "true") 31 @Schema(description = "是否纯音乐", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "true")
27 private Boolean makeInstrumental; 32 private Boolean makeInstrumental;
28 33
29 - @Schema(description = "模型版本", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "chirp-v3.5") 34 + // TODO @xin:看了下这个字段,发现最终还是 model 合适点;因为它其实是模型
  35 + @Schema(description = "模型版本", requiredMode = Schema.RequiredMode.REQUIRED, example = "chirp-v3.5")
  36 + @NotEmpty(message = "模型不能为空")
30 private String modelVersion; // 参见 AiModelEnum 枚举 37 private String modelVersion; // 参见 AiModelEnum 枚举
31 38
32 @Schema(description = "音乐风格", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "[\"pop\",\"jazz\",\"punk\"]") 39 @Schema(description = "音乐风格", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "[\"pop\",\"jazz\",\"punk\"]")
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java
@@ -99,6 +99,11 @@ public class AiMusicDO extends BaseDO { @@ -99,6 +99,11 @@ public class AiMusicDO extends BaseDO {
99 private List<String> tags; 99 private List<String> tags;
100 100
101 /** 101 /**
  102 + * 是否公开
  103 + */
  104 + private Boolean publicStatus;
  105 +
  106 + /**
102 * 任务编号 107 * 任务编号
103 */ 108 */
104 private String taskId; 109 private String taskId;
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/music/AiMusicMapper.java
1 package cn.iocoder.yudao.module.ai.dal.mysql.music; 1 package cn.iocoder.yudao.module.ai.dal.mysql.music;
2 2
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
4 import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO; 7 import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
5 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
6 9
@@ -18,4 +21,15 @@ public interface AiMusicMapper extends BaseMapperX&lt;AiMusicDO&gt; { @@ -18,4 +21,15 @@ public interface AiMusicMapper extends BaseMapperX&lt;AiMusicDO&gt; {
18 return selectList(AiMusicDO::getStatus, status); 21 return selectList(AiMusicDO::getStatus, status);
19 } 22 }
20 23
  24 + default PageResult<AiMusicDO> selectPage(AiMusicPageReqVO reqVO) {
  25 + return selectPage(reqVO, new LambdaQueryWrapperX<AiMusicDO>()
  26 + .eqIfPresent(AiMusicDO::getUserId, reqVO.getUserId())
  27 + .eqIfPresent(AiMusicDO::getTitle, reqVO.getTitle())
  28 + .eqIfPresent(AiMusicDO::getStatus, reqVO.getStatus())
  29 + .eqIfPresent(AiMusicDO::getGenerateMode, reqVO.getGenerateMode())
  30 + .betweenIfPresent(AiMusicDO::getCreateTime, reqVO.getCreateTime())
  31 + .eqIfPresent(AiMusicDO::getPublicStatus, reqVO.getPublicStatus())
  32 + .orderByDesc(AiMusicDO::getId));
  33 + }
  34 +
21 } 35 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
@@ -44,7 +44,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU @@ -44,7 +44,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
44 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error; 44 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
45 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 45 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
46 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 46 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
47 -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.AI_CHAT_MESSAGE_NOT_EXIST; 47 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_NOT_EXIST;
48 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; 48 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;
49 49
50 /** 50 /**
@@ -150,7 +150,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -150,7 +150,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
150 log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable); 150 log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 发生异常]", userId, sendReqVO, throwable);
151 chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage())); 151 chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId()).setContent(throwable.getMessage()));
152 }).onErrorResume(error -> { 152 }).onErrorResume(error -> {
153 - return Flux.just(error(ErrorCodeConstants.AI_CHAT_STREAM_ERROR)); 153 + return Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR));
154 }); 154 });
155 } 155 }
156 156
@@ -257,7 +257,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -257,7 +257,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
257 // 1. 校验消息存在 257 // 1. 校验消息存在
258 AiChatMessageDO message = chatMessageMapper.selectById(id); 258 AiChatMessageDO message = chatMessageMapper.selectById(id);
259 if (message == null || ObjUtil.notEqual(message.getUserId(), userId)) { 259 if (message == null || ObjUtil.notEqual(message.getUserId(), userId)) {
260 - throw exception(AI_CHAT_MESSAGE_NOT_EXIST); 260 + throw exception(CHAT_MESSAGE_NOT_EXIST);
261 } 261 }
262 // 2. 执行删除 262 // 2. 执行删除
263 chatMessageMapper.deleteById(id); 263 chatMessageMapper.deleteById(id);
@@ -268,7 +268,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -268,7 +268,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
268 // 1. 校验消息存在 268 // 1. 校验消息存在
269 List<AiChatMessageDO> messages = chatMessageMapper.selectListByConversationId(conversationId); 269 List<AiChatMessageDO> messages = chatMessageMapper.selectListByConversationId(conversationId);
270 if (CollUtil.isEmpty(messages) || ObjUtil.notEqual(messages.get(0).getUserId(), userId)) { 270 if (CollUtil.isEmpty(messages) || ObjUtil.notEqual(messages.get(0).getUserId(), userId)) {
271 - throw exception(AI_CHAT_MESSAGE_NOT_EXIST); 271 + throw exception(CHAT_MESSAGE_NOT_EXIST);
272 } 272 }
273 // 2. 执行删除 273 // 2. 执行删除
274 chatMessageMapper.deleteBatchIds(convertList(messages, AiChatMessageDO::getId)); 274 chatMessageMapper.deleteBatchIds(convertList(messages, AiChatMessageDO::getId));
@@ -279,7 +279,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { @@ -279,7 +279,7 @@ public class AiChatMessageServiceImpl implements AiChatMessageService {
279 // 1. 校验消息存在 279 // 1. 校验消息存在
280 AiChatMessageDO message = chatMessageMapper.selectById(id); 280 AiChatMessageDO message = chatMessageMapper.selectById(id);
281 if (message == null) { 281 if (message == null) {
282 - throw exception(AI_CHAT_MESSAGE_NOT_EXIST); 282 + throw exception(CHAT_MESSAGE_NOT_EXIST);
283 } 283 }
284 // 2. 执行删除 284 // 2. 执行删除
285 chatMessageMapper.deleteById(id); 285 chatMessageMapper.deleteById(id);
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
@@ -35,6 +35,7 @@ import org.springframework.scheduling.annotation.Async; @@ -35,6 +35,7 @@ import org.springframework.scheduling.annotation.Async;
35 import org.springframework.stereotype.Service; 35 import org.springframework.stereotype.Service;
36 import org.springframework.transaction.annotation.Transactional; 36 import org.springframework.transaction.annotation.Transactional;
37 37
  38 +import java.util.Collections;
38 import java.util.List; 39 import java.util.List;
39 import java.util.Map; 40 import java.util.Map;
40 41
@@ -76,6 +77,9 @@ public class AiImageServiceImpl implements AiImageService { @@ -76,6 +77,9 @@ public class AiImageServiceImpl implements AiImageService {
76 77
77 @Override 78 @Override
78 public List<AiImageDO> getImageByIds(List<Long> ids) { 79 public List<AiImageDO> getImageByIds(List<Long> ids) {
  80 + if (CollUtil.isEmpty(ids)) {
  81 + return Collections.emptyList();
  82 + }
79 return imageMapper.selectBatchIds(ids); 83 return imageMapper.selectBatchIds(ids);
80 } 84 }
81 85
@@ -135,7 +139,7 @@ public class AiImageServiceImpl implements AiImageService { @@ -135,7 +139,7 @@ public class AiImageServiceImpl implements AiImageService {
135 // 1. 校验是否存在 139 // 1. 校验是否存在
136 AiImageDO image = validateImageExists(id); 140 AiImageDO image = validateImageExists(id);
137 if (ObjUtil.notEqual(image.getUserId(), userId)) { 141 if (ObjUtil.notEqual(image.getUserId(), userId)) {
138 - throw exception(AI_IMAGE_NOT_EXISTS); 142 + throw exception(IMAGE_NOT_EXISTS);
139 } 143 }
140 // 2. 删除记录 144 // 2. 删除记录
141 imageMapper.deleteById(id); 145 imageMapper.deleteById(id);
@@ -165,7 +169,7 @@ public class AiImageServiceImpl implements AiImageService { @@ -165,7 +169,7 @@ public class AiImageServiceImpl implements AiImageService {
165 private AiImageDO validateImageExists(Long id) { 169 private AiImageDO validateImageExists(Long id) {
166 AiImageDO image = imageMapper.selectById(id); 170 AiImageDO image = imageMapper.selectById(id);
167 if (image == null) { 171 if (image == null) {
168 - throw exception(AI_IMAGE_NOT_EXISTS); 172 + throw exception(IMAGE_NOT_EXISTS);
169 } 173 }
170 return image; 174 return image;
171 } 175 }
@@ -191,7 +195,7 @@ public class AiImageServiceImpl implements AiImageService { @@ -191,7 +195,7 @@ public class AiImageServiceImpl implements AiImageService {
191 if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) { 195 if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) {
192 String description = imagineResponse.description().contains("quota_not_enough") ? 196 String description = imagineResponse.description().contains("quota_not_enough") ?
193 "账户余额不足" : imagineResponse.description(); 197 "账户余额不足" : imagineResponse.description();
194 - throw exception(AI_IMAGE_MIDJOURNEY_SUBMIT_FAIL, description); 198 + throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description);
195 } 199 }
196 200
197 // 4. 情况二【成功】:更新 taskId 和参数 201 // 4. 情况二【成功】:更新 taskId 和参数
@@ -271,13 +275,13 @@ public class AiImageServiceImpl implements AiImageService { @@ -271,13 +275,13 @@ public class AiImageServiceImpl implements AiImageService {
271 // 1.1 检查 image 275 // 1.1 检查 image
272 AiImageDO image = validateImageExists(reqVO.getId()); 276 AiImageDO image = validateImageExists(reqVO.getId());
273 if (ObjUtil.notEqual(userId, image.getUserId())) { 277 if (ObjUtil.notEqual(userId, image.getUserId())) {
274 - throw exception(AI_IMAGE_NOT_EXISTS); 278 + throw exception(IMAGE_NOT_EXISTS);
275 } 279 }
276 // 1.2 检查 customId 280 // 1.2 检查 customId
277 MidjourneyApi.Button button = CollUtil.findOne(image.getButtons(), 281 MidjourneyApi.Button button = CollUtil.findOne(image.getButtons(),
278 buttonX -> buttonX.customId().equals(reqVO.getCustomId())); 282 buttonX -> buttonX.customId().equals(reqVO.getCustomId()));
279 if (button == null) { 283 if (button == null) {
280 - throw exception(AI_IMAGE_CUSTOM_ID_NOT_EXISTS); 284 + throw exception(IMAGE_CUSTOM_ID_NOT_EXISTS);
281 } 285 }
282 286
283 // 2. 调用 Midjourney Proxy 提交任务 287 // 2. 调用 Midjourney Proxy 提交任务
@@ -286,7 +290,7 @@ public class AiImageServiceImpl implements AiImageService { @@ -286,7 +290,7 @@ public class AiImageServiceImpl implements AiImageService {
286 if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(actionResponse.code())) { 290 if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(actionResponse.code())) {
287 String description = actionResponse.description().contains("quota_not_enough") ? 291 String description = actionResponse.description().contains("quota_not_enough") ?
288 "账户余额不足" : actionResponse.description(); 292 "账户余额不足" : actionResponse.description();
289 - throw exception(AI_IMAGE_MIDJOURNEY_SUBMIT_FAIL, description); 293 + throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description);
290 } 294 }
291 295
292 // 3. 新增 image 记录 296 // 3. 新增 image 记录
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicService.java
1 package cn.iocoder.yudao.module.ai.service.music; 1 package cn.iocoder.yudao.module.ai.service.music;
2 2
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
  5 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdatePublicStatusReqVO;
3 import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO; 6 import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
  7 +import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
  8 +import jakarta.validation.Valid;
4 9
5 import java.util.List; 10 import java.util.List;
6 11
@@ -27,4 +32,26 @@ public interface AiMusicService { @@ -27,4 +32,26 @@ public interface AiMusicService {
27 */ 32 */
28 Integer syncMusic(); 33 Integer syncMusic();
29 34
  35 + /**
  36 + * 更新音乐发布状态
  37 + *
  38 + * @param updateReqVO 更新信息
  39 + */
  40 + void updateMusicPublicStatus(@Valid AiMusicUpdatePublicStatusReqVO updateReqVO);
  41 +
  42 + /**
  43 + * 删除AI 音乐
  44 + *
  45 + * @param id 编号
  46 + */
  47 + void deleteMusic(Long id);
  48 +
  49 + /**
  50 + * 获得音乐分页
  51 + *
  52 + * @param pageReqVO 分页查询
  53 + * @return 音乐分页
  54 + */
  55 + PageResult<AiMusicDO> getMusicPage(AiMusicPageReqVO pageReqVO);
  56 +
30 } 57 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java
@@ -4,6 +4,9 @@ import cn.hutool.core.collection.CollUtil; @@ -4,6 +4,9 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.text.StrPool; 4 import cn.hutool.core.text.StrPool;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
6 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; 6 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
  9 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdatePublicStatusReqVO;
7 import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO; 10 import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
8 import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO; 11 import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
9 import cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper; 12 import cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper;
@@ -15,8 +18,10 @@ import org.springframework.stereotype.Service; @@ -15,8 +18,10 @@ import org.springframework.stereotype.Service;
15 18
16 import java.util.*; 19 import java.util.*;
17 20
  21 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
18 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 22 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
19 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; 23 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  24 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MUSIC_NOT_EXISTS;
20 25
21 /** 26 /**
22 * AI 音乐 Service 实现类 27 * AI 音乐 Service 实现类
@@ -56,7 +61,7 @@ public class AiMusicServiceImpl implements AiMusicService { @@ -56,7 +61,7 @@ public class AiMusicServiceImpl implements AiMusicService {
56 return Collections.emptyList(); 61 return Collections.emptyList();
57 } 62 }
58 List<AiMusicDO> musicList = buildMusicDOList(musicDataList); 63 List<AiMusicDO> musicList = buildMusicDOList(musicDataList);
59 - musicList.forEach(music -> music.setUserId(userId).setPlatform(music.getPlatform()).setGenerateMode(reqVO.getGenerateMode())); 64 + musicList.forEach(music -> music.setUserId(userId).setPlatform(reqVO.getPlatform()).setGenerateMode(reqVO.getGenerateMode()));
60 musicMapper.insertBatch(musicList); 65 musicMapper.insertBatch(musicList);
61 return convertList(musicList, AiMusicDO::getId); 66 return convertList(musicList, AiMusicDO::getId);
62 } 67 }
@@ -92,12 +97,41 @@ public class AiMusicServiceImpl implements AiMusicService { @@ -92,12 +97,41 @@ public class AiMusicServiceImpl implements AiMusicService {
92 * @return AiMusicDO 集合 97 * @return AiMusicDO 集合
93 */ 98 */
94 private static List<AiMusicDO> buildMusicDOList(List<SunoApi.MusicData> musicList) { 99 private static List<AiMusicDO> buildMusicDOList(List<SunoApi.MusicData> musicList) {
  100 + // TODO @xin:成功的情况下,需要下载到自己的文件服务器。参考图片的处理
95 return convertList(musicList, musicData -> new AiMusicDO() 101 return convertList(musicList, musicData -> new AiMusicDO()
96 .setTaskId(musicData.id()).setModel(musicData.modelName()) 102 .setTaskId(musicData.id()).setModel(musicData.modelName())
97 .setPrompt(musicData.prompt()).setGptDescriptionPrompt(musicData.gptDescriptionPrompt()) 103 .setPrompt(musicData.prompt()).setGptDescriptionPrompt(musicData.gptDescriptionPrompt())
98 .setAudioUrl(musicData.audioUrl()).setVideoUrl(musicData.videoUrl()).setImageUrl(musicData.imageUrl()) 104 .setAudioUrl(musicData.audioUrl()).setVideoUrl(musicData.videoUrl()).setImageUrl(musicData.imageUrl())
99 .setTitle(musicData.title()).setLyric(musicData.lyric()).setTags(StrUtil.split(musicData.tags(), StrPool.COMMA)) 105 .setTitle(musicData.title()).setLyric(musicData.lyric()).setTags(StrUtil.split(musicData.tags(), StrPool.COMMA))
100 - .setStatus(Objects.equals("complete", musicData.status()) ? AiMusicStatusEnum.SUCCESS.getStatus() : AiMusicStatusEnum.IN_PROGRESS.getStatus())); 106 + .setStatus(Objects.equals("complete", musicData.status()) ?
  107 + AiMusicStatusEnum.SUCCESS.getStatus() : AiMusicStatusEnum.IN_PROGRESS.getStatus()));
  108 + }
  109 +
  110 + @Override
  111 + public void updateMusicPublicStatus(AiMusicUpdatePublicStatusReqVO updateReqVO) {
  112 + // 校验存在
  113 + validateMusicExists(updateReqVO.getId());
  114 + // 更新
  115 + musicMapper.updateBatch(new AiMusicDO().setPublicStatus(updateReqVO.getPublicStatus()));
  116 + }
  117 +
  118 + @Override
  119 + public void deleteMusic(Long id) {
  120 + // 校验存在
  121 + validateMusicExists(id);
  122 + // 删除
  123 + musicMapper.deleteById(id);
  124 + }
  125 +
  126 + private void validateMusicExists(Long id) {
  127 + if (musicMapper.selectById(id) == null) {
  128 + throw exception(MUSIC_NOT_EXISTS);
  129 + }
  130 + }
101 131
  132 + @Override
  133 + public PageResult<AiMusicDO> getMusicPage(AiMusicPageReqVO pageReqVO) {
  134 + return musicMapper.selectPage(pageReqVO);
102 } 135 }
  136 +
103 } 137 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/music/SunoTests.java
@@ -17,7 +17,8 @@ public class SunoTests { @@ -17,7 +17,8 @@ public class SunoTests {
17 17
18 @Before 18 @Before
19 public void setup() { 19 public void setup() {
20 - String url = "https://suno-om0w1cy6e-status2xxs-projects.vercel.app"; 20 +// String url = "https://suno-om0w1cy6e-status2xxs-projects.vercel.app";
  21 + String url = "http://127.0.0.1:3001";
21 this.sunoApi = new SunoApi(url); 22 this.sunoApi = new SunoApi(url);
22 } 23 }
23 24
@@ -53,5 +54,4 @@ public class SunoTests { @@ -53,5 +54,4 @@ public class SunoTests {
53 System.out.println(limitUsageData); 54 System.out.println(limitUsageData);
54 } 55 }
55 56
56 -  
57 } 57 }
yudao-server/src/main/resources/application.yaml
@@ -201,7 +201,8 @@ yudao.ai: @@ -201,7 +201,8 @@ yudao.ai:
201 notify-url: http://java.nat300.top/admin-api/ai/image/midjourney/notify 201 notify-url: http://java.nat300.top/admin-api/ai/image/midjourney/notify
202 suno: 202 suno:
203 enable: true 203 enable: true
204 - base-url: https://suno-om0w1cy6e-status2xxs-projects.vercel.app 204 +# base-url: https://suno-om0w1cy6e-status2xxs-projects.vercel.app
  205 + base-url: http://127.0.0.1:3001
205 206
206 --- #################### 芋道相关配置 #################### 207 --- #################### 芋道相关配置 ####################
207 208