Commit 1c9c9790cd0feec6154637c20fa1bcb20d692dfb

Authored by YunaiV
1 parent 89d07934

【代码重构】AI:“聊天模型”重构为“模型”,支持 type 模型类型

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java
@@ -13,9 +13,9 @@ public class AiMidjourneyImagineReqVO { @@ -13,9 +13,9 @@ public class AiMidjourneyImagineReqVO {
13 @NotEmpty(message = "提示词不能为空!") 13 @NotEmpty(message = "提示词不能为空!")
14 private String prompt; 14 private String prompt;
15 15
16 - @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "midjourney")  
17 - @NotEmpty(message = "模型不能为空")  
18 - private String model; // 参考 MidjourneyApi.ModelEnum 16 + @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  17 + @NotNull(message = "模型编号不能为空")
  18 + private Long modelId;
19 19
20 @Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 20 @Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
21 @NotNull(message = "图片宽度不能为空") 21 @NotNull(message = "图片宽度不能为空")
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.service.image; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.service.image;
3 import cn.hutool.core.bean.BeanUtil; 3 import cn.hutool.core.bean.BeanUtil;
4 import cn.hutool.core.codec.Base64; 4 import cn.hutool.core.codec.Base64;
5 import cn.hutool.core.collection.CollUtil; 5 import cn.hutool.core.collection.CollUtil;
  6 +import cn.hutool.core.lang.Assert;
6 import cn.hutool.core.map.MapUtil; 7 import cn.hutool.core.map.MapUtil;
7 import cn.hutool.core.util.ObjUtil; 8 import cn.hutool.core.util.ObjUtil;
8 import cn.hutool.core.util.StrUtil; 9 import cn.hutool.core.util.StrUtil;
@@ -217,52 +218,56 @@ public class AiImageServiceImpl implements AiImageService { @@ -217,52 +218,56 @@ public class AiImageServiceImpl implements AiImageService {
217 218
218 @Override 219 @Override
219 @Transactional(rollbackFor = Exception.class) 220 @Transactional(rollbackFor = Exception.class)
220 - public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO reqVO) {  
221 - MidjourneyApi midjourneyApi = modelService.getMidjourneyApi();  
222 - // 1. 保存数据库  
223 - AiImageDO image = BeanUtils.toBean(reqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false) 221 + public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO drawReqVO) {
  222 + // 1. 校验模型
  223 + AiModelDO model = modelService.validateModel(drawReqVO.getModelId());
  224 + Assert.equals(model.getPlatform(), AiPlatformEnum.MIDJOURNEY.getPlatform(), "平台不匹配");
  225 + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(model.getId());
  226 +
  227 + // 2. 保存数据库
  228 + AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)
224 .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()) 229 .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus())
225 - .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()); 230 + .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()).setModelId(model.getId()).setModel(model.getName());
226 imageMapper.insert(image); 231 imageMapper.insert(image);
227 232
228 - // 2. 调用 Midjourney Proxy 提交任务  
229 - List<String> base64Array = StrUtil.isBlank(reqVO.getReferImageUrl()) ? null :  
230 - Collections.singletonList("data:image/jpeg;base64,".concat(Base64.encode(HttpUtil.downloadBytes(reqVO.getReferImageUrl())))); 233 + // 3. 调用 Midjourney Proxy 提交任务
  234 + List<String> base64Array = StrUtil.isBlank(drawReqVO.getReferImageUrl()) ? null :
  235 + Collections.singletonList("data:image/jpeg;base64,".concat(Base64.encode(HttpUtil.downloadBytes(drawReqVO.getReferImageUrl()))));
231 MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest( 236 MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest(
232 - base64Array, reqVO.getPrompt(),null,  
233 - MidjourneyApi.ImagineRequest.buildState(reqVO.getWidth(),  
234 - reqVO.getHeight(), reqVO.getVersion(), reqVO.getModel())); 237 + base64Array, drawReqVO.getPrompt(),null,
  238 + MidjourneyApi.ImagineRequest.buildState(drawReqVO.getWidth(),
  239 + drawReqVO.getHeight(), drawReqVO.getVersion(), model.getModel()));
235 MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest); 240 MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest);
236 241
237 - // 3. 情况一【失败】:抛出业务异常 242 + // 4.1 情况一【失败】:抛出业务异常
238 if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) { 243 if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) {
239 String description = imagineResponse.description().contains("quota_not_enough") ? 244 String description = imagineResponse.description().contains("quota_not_enough") ?
240 "账户余额不足" : imagineResponse.description(); 245 "账户余额不足" : imagineResponse.description();
241 throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description); 246 throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description);
242 } 247 }
243 248
244 - // 4. 情况二【成功】:更新 taskId 和参数 249 + // 4.2 情况二【成功】:更新 taskId 和参数
245 imageMapper.updateById(new AiImageDO().setId(image.getId()) 250 imageMapper.updateById(new AiImageDO().setId(image.getId())
246 - .setTaskId(imagineResponse.result()).setOptions(BeanUtil.beanToMap(reqVO))); 251 + .setTaskId(imagineResponse.result()).setOptions(BeanUtil.beanToMap(drawReqVO)));
247 return image.getId(); 252 return image.getId();
248 } 253 }
249 254
250 @Override 255 @Override
251 public Integer midjourneySync() { 256 public Integer midjourneySync() {
252 - MidjourneyApi midjourneyApi = modelService.getMidjourneyApi();  
253 // 1.1 获取 Midjourney 平台,状态在 “进行中” 的 image 257 // 1.1 获取 Midjourney 平台,状态在 “进行中” 的 image
254 - List<AiImageDO> imageList = imageMapper.selectListByStatusAndPlatform( 258 + List<AiImageDO> images = imageMapper.selectListByStatusAndPlatform(
255 AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform()); 259 AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform());
256 - if (CollUtil.isEmpty(imageList)) { 260 + if (CollUtil.isEmpty(images)) {
257 return 0; 261 return 0;
258 } 262 }
259 // 1.2 调用 Midjourney Proxy 获取任务进展 263 // 1.2 调用 Midjourney Proxy 获取任务进展
260 - List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(convertSet(imageList, AiImageDO::getTaskId)); 264 + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(images.get(0).getModelId());
  265 + List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(convertSet(images, AiImageDO::getTaskId));
261 Map<String, MidjourneyApi.Notify> taskMap = convertMap(taskList, MidjourneyApi.Notify::id); 266 Map<String, MidjourneyApi.Notify> taskMap = convertMap(taskList, MidjourneyApi.Notify::id);
262 267
263 // 2. 逐个处理,更新进展 268 // 2. 逐个处理,更新进展
264 int count = 0; 269 int count = 0;
265 - for (AiImageDO image : imageList) { 270 + for (AiImageDO image : images) {
266 MidjourneyApi.Notify notify = taskMap.get(image.getTaskId()); 271 MidjourneyApi.Notify notify = taskMap.get(image.getTaskId());
267 if (notify == null) { 272 if (notify == null) {
268 log.error("[midjourneySync][image({}) 查询不到进展]", image); 273 log.error("[midjourneySync][image({}) 查询不到进展]", image);
@@ -320,12 +325,12 @@ public class AiImageServiceImpl implements AiImageService { @@ -320,12 +325,12 @@ public class AiImageServiceImpl implements AiImageService {
320 325
321 @Override 326 @Override
322 public Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO) { 327 public Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO) {
323 - MidjourneyApi midjourneyApi = modelService.getMidjourneyApi();  
324 // 1.1 检查 image 328 // 1.1 检查 image
325 AiImageDO image = validateImageExists(reqVO.getId()); 329 AiImageDO image = validateImageExists(reqVO.getId());
326 if (ObjUtil.notEqual(userId, image.getUserId())) { 330 if (ObjUtil.notEqual(userId, image.getUserId())) {
327 throw exception(IMAGE_NOT_EXISTS); 331 throw exception(IMAGE_NOT_EXISTS);
328 } 332 }
  333 + MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(image.getModelId());
329 // 1.2 检查 customId 334 // 1.2 检查 customId
330 MidjourneyApi.Button button = CollUtil.findOne(image.getButtons(), 335 MidjourneyApi.Button button = CollUtil.findOne(image.getButtons(),
331 buttonX -> buttonX.customId().equals(reqVO.getCustomId())); 336 buttonX -> buttonX.customId().equals(reqVO.getCustomId()));
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java
@@ -137,9 +137,9 @@ public class AiChatModelServiceImpl implements AiModelService { @@ -137,9 +137,9 @@ public class AiChatModelServiceImpl implements AiModelService {
137 } 137 }
138 138
139 @Override 139 @Override
140 - public MidjourneyApi getMidjourneyApi() {  
141 - AiApiKeyDO apiKey = apiKeyService.getRequiredDefaultApiKey(  
142 - AiPlatformEnum.MIDJOURNEY.getPlatform(), CommonStatusEnum.ENABLE.getStatus()); 140 + public MidjourneyApi getMidjourneyApi(Long id) {
  141 + AiModelDO model = validateModel(id);
  142 + AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
143 return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl()); 143 return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl());
144 } 144 }
145 145
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java
@@ -109,9 +109,10 @@ public interface AiModelService { @@ -109,9 +109,10 @@ public interface AiModelService {
109 /** 109 /**
110 * 获得 MidjourneyApi 对象 110 * 获得 MidjourneyApi 对象
111 * 111 *
  112 + * @param id 编号
112 * @return MidjourneyApi 对象 113 * @return MidjourneyApi 对象
113 */ 114 */
114 - MidjourneyApi getMidjourneyApi(); 115 + MidjourneyApi getMidjourneyApi(Long id);
115 116
116 /** 117 /**
117 * 获得 SunoApi 对象 118 * 获得 SunoApi 对象