Commit 56e8707e387736294cf431425e978fdc2575bd3a
1 parent
36b6fee0
【增加】Midjourney Proxy 回调通知
Showing
6 changed files
with
67 additions
and
40 deletions
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,7 +11,10 @@ import lombok.Data; | ||
| 11 | * @since 1.0 | 11 | * @since 1.0 |
| 12 | */ | 12 | */ |
| 13 | @Data | 13 | @Data |
| 14 | -public class MidjourneyNotifyVO { | 14 | +public class MidjourneyNotifyReqVO { |
| 15 | + | ||
| 16 | + @Schema(description = "job id") | ||
| 17 | + private String id; | ||
| 15 | 18 | ||
| 16 | @Schema(description = "任务类型") | 19 | @Schema(description = "任务类型") |
| 17 | private MidjourneyTaskActionEnum action; | 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,6 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.image; | ||
| 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; | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 6 | +import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO; | ||
| 6 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO; | 7 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO; |
| 7 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageListReqVO; | 8 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageListReqVO; |
| 8 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; | 9 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; |
| @@ -13,7 +14,6 @@ import io.swagger.v3.oas.annotations.Operation; | @@ -13,7 +14,6 @@ import io.swagger.v3.oas.annotations.Operation; | ||
| 13 | import io.swagger.v3.oas.annotations.Parameter; | 14 | import io.swagger.v3.oas.annotations.Parameter; |
| 14 | import io.swagger.v3.oas.annotations.tags.Tag; | 15 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 15 | import jakarta.annotation.Resource; | 16 | import jakarta.annotation.Resource; |
| 16 | -import jakarta.servlet.http.HttpServletRequest; | ||
| 17 | import lombok.extern.slf4j.Slf4j; | 17 | import lombok.extern.slf4j.Slf4j; |
| 18 | import org.springframework.validation.annotation.Validated; | 18 | import org.springframework.validation.annotation.Validated; |
| 19 | import org.springframework.web.bind.annotation.*; | 19 | import org.springframework.web.bind.annotation.*; |
| @@ -74,9 +74,9 @@ public class AiImageController { | @@ -74,9 +74,9 @@ public class AiImageController { | ||
| 74 | return success(aiImageService.deleteIdMy(id, getLoginUserId())); | 74 | return success(aiImageService.deleteIdMy(id, getLoginUserId())); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | - @Operation(summary = "删除【我的】绘画记录") | 77 | + @Operation(summary = "midjourney proxy - 回调通知") |
| 78 | @RequestMapping("/midjourney-notify") | 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,6 +28,9 @@ public class AiImageDO extends BaseDO { | ||
| 28 | @Schema(description = "用户编号") | 28 | @Schema(description = "用户编号") |
| 29 | private Long userId; | 29 | private Long userId; |
| 30 | 30 | ||
| 31 | + @Schema(description = "midjourney proxy 关联的 job id") | ||
| 32 | + private String jobId; | ||
| 33 | + | ||
| 31 | @Schema(description = "提示词") | 34 | @Schema(description = "提示词") |
| 32 | private String prompt; | 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,7 +4,6 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 4 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 4 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 5 | import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; | 5 | import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; |
| 6 | import org.apache.ibatis.annotations.Mapper; | 6 | import org.apache.ibatis.annotations.Mapper; |
| 7 | -import org.springframework.stereotype.Repository; | ||
| 8 | 7 | ||
| 9 | /** | 8 | /** |
| 10 | * AI 绘图 Mapper | 9 | * AI 绘图 Mapper |
| @@ -26,4 +25,14 @@ public interface AiImageMapper extends BaseMapperX<AiImageDO> { | @@ -26,4 +25,14 @@ public interface AiImageMapper extends BaseMapperX<AiImageDO> { | ||
| 26 | return; | 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 | package cn.iocoder.yudao.module.ai.service.image; | 1 | package cn.iocoder.yudao.module.ai.service.image; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | +import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO; | ||
| 4 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO; | 5 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO; |
| 5 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageListReqVO; | 6 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageListReqVO; |
| 6 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; | 7 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; |
| @@ -65,4 +66,12 @@ public interface AiImageService { | @@ -65,4 +66,12 @@ public interface AiImageService { | ||
| 65 | */ | 66 | */ |
| 66 | Boolean deleteIdMy(Long id, Long loginUserId); | 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 | package cn.iocoder.yudao.module.ai.service.image; | 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 | import cn.hutool.http.HttpUtil; | 5 | import cn.hutool.http.HttpUtil; |
| 4 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | 6 | import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; |
| 5 | import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageModelEnum; | 7 | import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageModelEnum; |
| @@ -14,9 +16,14 @@ import cn.iocoder.yudao.module.ai.ErrorCodeConstants; | @@ -14,9 +16,14 @@ import cn.iocoder.yudao.module.ai.ErrorCodeConstants; | ||
| 14 | import cn.iocoder.yudao.module.ai.client.MidjourneyProxyClient; | 16 | import cn.iocoder.yudao.module.ai.client.MidjourneyProxyClient; |
| 15 | import cn.iocoder.yudao.module.ai.client.enums.MidjourneyModelEnum; | 17 | import cn.iocoder.yudao.module.ai.client.enums.MidjourneyModelEnum; |
| 16 | import cn.iocoder.yudao.module.ai.client.enums.MidjourneySubmitCodeEnum; | 18 | import cn.iocoder.yudao.module.ai.client.enums.MidjourneySubmitCodeEnum; |
| 19 | +import cn.iocoder.yudao.module.ai.client.enums.MidjourneyTaskStatusEnum; | ||
| 17 | import cn.iocoder.yudao.module.ai.client.vo.MidjourneyImagineReqVO; | 20 | import cn.iocoder.yudao.module.ai.client.vo.MidjourneyImagineReqVO; |
| 21 | +import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO; | ||
| 18 | import cn.iocoder.yudao.module.ai.client.vo.MidjourneySubmitRespVO; | 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 | import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; | 27 | import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; |
| 21 | import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; | 28 | import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; |
| 22 | import cn.iocoder.yudao.module.ai.enums.AiImagePublicStatusEnum; | 29 | import cn.iocoder.yudao.module.ai.enums.AiImagePublicStatusEnum; |
| @@ -36,15 +43,9 @@ import org.springframework.scheduling.annotation.Async; | @@ -36,15 +43,9 @@ import org.springframework.scheduling.annotation.Async; | ||
| 36 | import org.springframework.stereotype.Service; | 43 | import org.springframework.stereotype.Service; |
| 37 | import org.springframework.transaction.annotation.Transactional; | 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 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 46 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 44 | 47 | ||
| 45 | 48 | ||
| 46 | -// TODO @fan:注释优化下哈 | ||
| 47 | - | ||
| 48 | /** | 49 | /** |
| 49 | * AI 绘画(接入 dall2/dall3、midjourney) | 50 | * AI 绘画(接入 dall2/dall3、midjourney) |
| 50 | * | 51 | * |
| @@ -56,9 +57,6 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU | @@ -56,9 +57,6 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU | ||
| 56 | @Slf4j | 57 | @Slf4j |
| 57 | public class AiImageServiceImpl implements AiImageService { | 58 | public class AiImageServiceImpl implements AiImageService { |
| 58 | 59 | ||
| 59 | - // TODO @fan:使用 @Resource 注入 | ||
| 60 | - | ||
| 61 | - // TODO @fan:imageMapper | ||
| 62 | @Resource | 60 | @Resource |
| 63 | private AiImageMapper imageMapper; | 61 | private AiImageMapper imageMapper; |
| 64 | @Resource | 62 | @Resource |
| @@ -173,19 +171,16 @@ public class AiImageServiceImpl implements AiImageService { | @@ -173,19 +171,16 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 173 | // 4、保存任务 id (状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误)) | 171 | // 4、保存任务 id (状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误)) |
| 174 | String updateStatus = null; | 172 | String updateStatus = null; |
| 175 | String errorMessage = null; | 173 | String errorMessage = null; |
| 176 | - Map<String, Object> drawResponse = new HashMap<>(); | ||
| 177 | 174 | ||
| 178 | if (!MidjourneySubmitCodeEnum.SUCCESS_CODES.contains(submitRespVO.getCode())) { | 175 | if (!MidjourneySubmitCodeEnum.SUCCESS_CODES.contains(submitRespVO.getCode())) { |
| 179 | updateStatus = AiImageStatusEnum.FAIL.getStatus(); | 176 | updateStatus = AiImageStatusEnum.FAIL.getStatus(); |
| 180 | errorMessage = submitRespVO.getDescription(); | 177 | errorMessage = submitRespVO.getDescription(); |
| 181 | - } else { | ||
| 182 | - drawResponse.put("jobId", submitRespVO.getResult()); | ||
| 183 | } | 178 | } |
| 184 | imageMapper.updateById(new AiImageDO() | 179 | imageMapper.updateById(new AiImageDO() |
| 185 | .setId(aiImageDO.getId()) | 180 | .setId(aiImageDO.getId()) |
| 186 | .setStatus(updateStatus) | 181 | .setStatus(updateStatus) |
| 187 | .setErrorMessage(errorMessage) | 182 | .setErrorMessage(errorMessage) |
| 188 | - .setDrawResponse(drawResponse) | 183 | + .setJobId(submitRespVO.getResult()) |
| 189 | ); | 184 | ); |
| 190 | return aiImageDO.getId(); | 185 | return aiImageDO.getId(); |
| 191 | } | 186 | } |
| @@ -228,28 +223,36 @@ public class AiImageServiceImpl implements AiImageService { | @@ -228,28 +223,36 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 228 | return imageMapper.deleteById(id) > 0; | 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 | private AiImageDO validateExists(Long id) { | 258 | private AiImageDO validateExists(Long id) { |