Commit 56e8707e387736294cf431425e978fdc2575bd3a

Authored by cherishsince
1 parent 36b6fee0

【增加】Midjourney Proxy 回调通知

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/client/vo/MidjourneyNotifyVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/client/vo/MidjourneyNotifyReqVO.java
... ... @@ -11,7 +11,10 @@ import lombok.Data;
11 11 * @since 1.0
12 12 */
13 13 @Data
14   -public class MidjourneyNotifyVO {
  14 +public class MidjourneyNotifyReqVO {
  15 +
  16 + @Schema(description = "job id")
  17 + private String id;
15 18  
16 19 @Schema(description = "任务类型")
17 20 private MidjourneyTaskActionEnum action;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.image;
3 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO;
6 7 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO;
7 8 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageListReqVO;
8 9 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO;
... ... @@ -13,7 +14,6 @@ import io.swagger.v3.oas.annotations.Operation;
13 14 import io.swagger.v3.oas.annotations.Parameter;
14 15 import io.swagger.v3.oas.annotations.tags.Tag;
15 16 import jakarta.annotation.Resource;
16   -import jakarta.servlet.http.HttpServletRequest;
17 17 import lombok.extern.slf4j.Slf4j;
18 18 import org.springframework.validation.annotation.Validated;
19 19 import org.springframework.web.bind.annotation.*;
... ... @@ -74,9 +74,9 @@ public class AiImageController {
74 74 return success(aiImageService.deleteIdMy(id, getLoginUserId()));
75 75 }
76 76  
77   - @Operation(summary = "删除【我的】绘画记录")
  77 + @Operation(summary = "midjourney proxy - 回调通知")
78 78 @RequestMapping("/midjourney-notify")
79   - public CommonResult<Boolean> midjourneyNotify(HttpServletRequest request) {
80   - return success(true);
  79 + public CommonResult<Boolean> midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO) {
  80 + return success(aiImageService.midjourneyNotify(getLoginUserId(), notifyReqVO));
81 81 }
82 82 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java
... ... @@ -28,6 +28,9 @@ public class AiImageDO extends BaseDO {
28 28 @Schema(description = "用户编号")
29 29 private Long userId;
30 30  
  31 + @Schema(description = "midjourney proxy 关联的 job id")
  32 + private String jobId;
  33 +
31 34 @Schema(description = "提示词")
32 35 private String prompt;
33 36  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java
... ... @@ -4,7 +4,6 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 4 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
5 5 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
6 6 import org.apache.ibatis.annotations.Mapper;
7   -import org.springframework.stereotype.Repository;
8 7  
9 8 /**
10 9 * AI 绘图 Mapper
... ... @@ -26,4 +25,14 @@ public interface AiImageMapper extends BaseMapperX&lt;AiImageDO&gt; {
26 25 return;
27 26 }
28 27  
  28 + /**
  29 + * 查询 - 根据 job id
  30 + *
  31 + * @param id
  32 + * @return
  33 + */
  34 + default AiImageDO selectByJobId(String id) {
  35 + return this.selectOne(new LambdaQueryWrapperX<AiImageDO>().eq(AiImageDO::getJobId, id));
  36 + }
  37 +
29 38 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java
1 1 package cn.iocoder.yudao.module.ai.service.image;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO;
4 5 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO;
5 6 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageListReqVO;
6 7 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO;
... ... @@ -65,4 +66,12 @@ public interface AiImageService {
65 66 */
66 67 Boolean deleteIdMy(Long id, Long loginUserId);
67 68  
  69 + /**
  70 + * midjourney proxy - 回调通知
  71 + *
  72 + * @param loginUserId
  73 + * @param notifyReqVO
  74 + * @return
  75 + */
  76 + Boolean midjourneyNotify(Long loginUserId, MidjourneyNotifyReqVO notifyReqVO);
68 77 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
1 1 package cn.iocoder.yudao.module.ai.service.image;
2 2  
  3 +import cn.hutool.core.bean.BeanUtil;
  4 +import cn.hutool.core.util.StrUtil;
3 5 import cn.hutool.http.HttpUtil;
4 6 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
5 7 import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageModelEnum;
... ... @@ -14,9 +16,14 @@ import cn.iocoder.yudao.module.ai.ErrorCodeConstants;
14 16 import cn.iocoder.yudao.module.ai.client.MidjourneyProxyClient;
15 17 import cn.iocoder.yudao.module.ai.client.enums.MidjourneyModelEnum;
16 18 import cn.iocoder.yudao.module.ai.client.enums.MidjourneySubmitCodeEnum;
  19 +import cn.iocoder.yudao.module.ai.client.enums.MidjourneyTaskStatusEnum;
17 20 import cn.iocoder.yudao.module.ai.client.vo.MidjourneyImagineReqVO;
  21 +import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO;
18 22 import cn.iocoder.yudao.module.ai.client.vo.MidjourneySubmitRespVO;
19   -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.*;
  23 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO;
  24 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageListReqVO;
  25 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO;
  26 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyOperateReqVO;
20 27 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
21 28 import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper;
22 29 import cn.iocoder.yudao.module.ai.enums.AiImagePublicStatusEnum;
... ... @@ -36,15 +43,9 @@ import org.springframework.scheduling.annotation.Async;
36 43 import org.springframework.stereotype.Service;
37 44 import org.springframework.transaction.annotation.Transactional;
38 45  
39   -import java.util.HashMap;
40   -import java.util.List;
41   -import java.util.Map;
42   -
43 46 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
44 47  
45 48  
46   -// TODO @fan:注释优化下哈
47   -
48 49 /**
49 50 * AI 绘画(接入 dall2/dall3、midjourney)
50 51 *
... ... @@ -56,9 +57,6 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
56 57 @Slf4j
57 58 public class AiImageServiceImpl implements AiImageService {
58 59  
59   - // TODO @fan:使用 @Resource 注入
60   -
61   - // TODO @fan:imageMapper
62 60 @Resource
63 61 private AiImageMapper imageMapper;
64 62 @Resource
... ... @@ -173,19 +171,16 @@ public class AiImageServiceImpl implements AiImageService {
173 171 // 4、保存任务 id (状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误))
174 172 String updateStatus = null;
175 173 String errorMessage = null;
176   - Map<String, Object> drawResponse = new HashMap<>();
177 174  
178 175 if (!MidjourneySubmitCodeEnum.SUCCESS_CODES.contains(submitRespVO.getCode())) {
179 176 updateStatus = AiImageStatusEnum.FAIL.getStatus();
180 177 errorMessage = submitRespVO.getDescription();
181   - } else {
182   - drawResponse.put("jobId", submitRespVO.getResult());
183 178 }
184 179 imageMapper.updateById(new AiImageDO()
185 180 .setId(aiImageDO.getId())
186 181 .setStatus(updateStatus)
187 182 .setErrorMessage(errorMessage)
188   - .setDrawResponse(drawResponse)
  183 + .setJobId(submitRespVO.getResult())
189 184 );
190 185 return aiImageDO.getId();
191 186 }
... ... @@ -228,28 +223,36 @@ public class AiImageServiceImpl implements AiImageService {
228 223 return imageMapper.deleteById(id) > 0;
229 224 }
230 225  
231   - private void validateMessageId(String mjMessageId, String messageId) {
232   - if (!mjMessageId.equals(messageId)) {
233   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MIDJOURNEY_MESSAGE_ID_INCORRECT);
  226 + @Override
  227 + public Boolean midjourneyNotify(Long loginUserId, MidjourneyNotifyReqVO notifyReqVO) {
  228 + // 1、根据 job id 查询关联的 image
  229 + AiImageDO image = imageMapper.selectByJobId(notifyReqVO.getId());
  230 + if (image == null) {
  231 + log.warn("midjourneyNotify 回调的 jobId 不存在! jobId: {}", notifyReqVO.getId());
  232 + return false;
234 233 }
235   - }
236   -
237   - private AiImageMidjourneyOperationsVO validateMidjourneyOperationsExists(List<AiImageMidjourneyOperationsVO> midjourneyOperations, String operateId) {
238   - for (AiImageMidjourneyOperationsVO midjourneyOperation : midjourneyOperations) {
239   - if (midjourneyOperation.getCustom_id().equals(operateId)) {
240   - return midjourneyOperation;
241   - }
  234 + //
  235 + String imageStatus = null;
  236 + if (MidjourneyTaskStatusEnum.SUCCESS == notifyReqVO.getStatus()) {
  237 + imageStatus = AiImageStatusEnum.COMPLETE.getStatus();
  238 + } else if (MidjourneyTaskStatusEnum.FAILURE == notifyReqVO.getStatus()) {
  239 + imageStatus = AiImageStatusEnum.FAIL.getStatus();
242 240 }
243   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MIDJOURNEY_OPERATION_NOT_EXISTS);
244   - }
245   -
246   -
247   - private List<AiImageMidjourneyOperationsVO> getMidjourneyOperations(AiImageDO aiImageDO) {
248   -// if (StrUtil.isBlank(aiImageDO.getMjOperations())) {
249   -// return Collections.emptyList();
250   -// }
251   -// return JsonUtils.parseArray(aiImageDO.getMjOperations(), AiImageMidjourneyOperationsVO.class);
252   - return null;
  241 + // 2、上传图片
  242 + String filePath = null;
  243 + if (!StrUtil.isBlank(notifyReqVO.getImageUrl())) {
  244 + filePath = fileApi.createFile(HttpUtil.downloadBytes(notifyReqVO.getImageUrl()));
  245 + }
  246 + // 2、更新 image 状态
  247 + imageMapper.updateById(
  248 + new AiImageDO()
  249 + .setId(image.getId())
  250 + .setStatus(imageStatus)
  251 + .setPicUrl(filePath)
  252 + .setOriginalPicUrl(notifyReqVO.getImageUrl())
  253 + .setDrawResponse(BeanUtil.beanToMap(notifyReqVO))
  254 + );
  255 + return true;
253 256 }
254 257  
255 258 private AiImageDO validateExists(Long id) {
... ...