Commit 1c9c9790cd0feec6154637c20fa1bcb20d692dfb
1 parent
89d07934
【代码重构】AI:“聊天模型”重构为“模型”,支持 type 模型类型
Showing
4 changed files
with
33 additions
and
27 deletions
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 对象 |