Commit 330fd52b3e2ffc2f6045b736b05ddd2f5762864a
1 parent
24479dac
【添加】midjourney 增加 imagine 接口
Showing
3 changed files
with
79 additions
and
10 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/AiImageController.java
| 1 | 1 | package cn.iocoder.yudao.module.ai.controller; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 3 | 4 | import cn.iocoder.yudao.module.ai.service.AiImageService; |
| 4 | 5 | import cn.iocoder.yudao.module.ai.vo.AiImageDallDrawingReq; |
| 6 | +import cn.iocoder.yudao.module.ai.vo.AiImageMidjourneyReq; | |
| 7 | +import cn.iocoder.yudao.module.ai.vo.AiImageMidjourneyRes; | |
| 5 | 8 | import io.swagger.v3.oas.annotations.Operation; |
| 6 | 9 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 7 | 10 | import lombok.AllArgsConstructor; |
| 8 | 11 | import lombok.extern.slf4j.Slf4j; |
| 9 | 12 | import org.springframework.validation.annotation.Validated; |
| 10 | -import org.springframework.web.bind.annotation.GetMapping; | |
| 11 | -import org.springframework.web.bind.annotation.ModelAttribute; | |
| 13 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 14 | +import org.springframework.web.bind.annotation.RequestBody; | |
| 12 | 15 | import org.springframework.web.bind.annotation.RequestMapping; |
| 13 | 16 | import org.springframework.web.bind.annotation.RestController; |
| 14 | 17 | import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; |
| ... | ... | @@ -30,10 +33,16 @@ public class AiImageController { |
| 30 | 33 | private final AiImageService aiImageService; |
| 31 | 34 | |
| 32 | 35 | @Operation(summary = "dall2/dall3绘画", description = "openAi dall3是付费的!") |
| 33 | - @GetMapping("/dallDrawing") | |
| 34 | - public SseEmitter dallDrawing(@Validated @ModelAttribute AiImageDallDrawingReq req) { | |
| 36 | + @PostMapping("/dallDrawing") | |
| 37 | + public SseEmitter dallDrawing(@Validated @RequestBody AiImageDallDrawingReq req) { | |
| 35 | 38 | Utf8SseEmitter sseEmitter = new Utf8SseEmitter(); |
| 36 | 39 | aiImageService.dallDrawing(req, sseEmitter); |
| 37 | 40 | return sseEmitter; |
| 38 | 41 | } |
| 42 | + | |
| 43 | + @Operation(summary = "midjourney", description = "midjourney图片绘画流程:1、提交任务 2、获取完成的任务 3、选择对应功能 4、获取最终结果") | |
| 44 | + @PostMapping("/midjourney") | |
| 45 | + public CommonResult<AiImageMidjourneyRes> midjourney(@Validated @RequestBody AiImageMidjourneyReq req) { | |
| 46 | + return CommonResult.success(aiImageService.midjourney(req)); | |
| 47 | + } | |
| 39 | 48 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiImageService.java
| ... | ... | @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.ai.service; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.ai.controller.Utf8SseEmitter; |
| 4 | 4 | import cn.iocoder.yudao.module.ai.vo.AiImageDallDrawingReq; |
| 5 | +import cn.iocoder.yudao.module.ai.vo.AiImageMidjourneyReq; | |
| 6 | +import cn.iocoder.yudao.module.ai.vo.AiImageMidjourneyRes; | |
| 5 | 7 | |
| 6 | 8 | /** |
| 7 | 9 | * ai 作图 |
| ... | ... | @@ -19,4 +21,12 @@ public interface AiImageService { |
| 19 | 21 | * @param sseEmitter |
| 20 | 22 | */ |
| 21 | 23 | void dallDrawing(AiImageDallDrawingReq req, Utf8SseEmitter sseEmitter); |
| 24 | + | |
| 25 | + /** | |
| 26 | + * midjourney 图片生成 | |
| 27 | + * | |
| 28 | + * @param req | |
| 29 | + * @return | |
| 30 | + */ | |
| 31 | + AiImageMidjourneyRes midjourney(AiImageMidjourneyReq req); | |
| 22 | 32 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiImageServiceImpl.java
| ... | ... | @@ -8,17 +8,26 @@ import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageClient; |
| 8 | 8 | import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageModelEnum; |
| 9 | 9 | import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageOptions; |
| 10 | 10 | import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageStyleEnum; |
| 11 | +import cn.iocoder.yudao.framework.ai.midjourney.api.MidjourneyInteractionsApi; | |
| 12 | +import cn.iocoder.yudao.framework.ai.midjourney.webSocket.MidjourneyWebSocketStarter; | |
| 13 | +import cn.iocoder.yudao.framework.ai.midjourney.webSocket.WssNotify; | |
| 14 | +import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; | |
| 11 | 15 | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| 16 | +import cn.iocoder.yudao.module.ai.ErrorCodeConstants; | |
| 12 | 17 | import cn.iocoder.yudao.module.ai.controller.Utf8SseEmitter; |
| 13 | 18 | import cn.iocoder.yudao.module.ai.dal.dataobject.AiImageDO; |
| 14 | 19 | import cn.iocoder.yudao.module.ai.enums.AiChatDrawingStatusEnum; |
| 15 | 20 | import cn.iocoder.yudao.module.ai.mapper.AiImageMapper; |
| 16 | 21 | import cn.iocoder.yudao.module.ai.service.AiImageService; |
| 17 | 22 | import cn.iocoder.yudao.module.ai.vo.AiImageDallDrawingReq; |
| 23 | +import cn.iocoder.yudao.module.ai.vo.AiImageMidjourneyReq; | |
| 24 | +import cn.iocoder.yudao.module.ai.vo.AiImageMidjourneyRes; | |
| 25 | +import jakarta.annotation.PostConstruct; | |
| 18 | 26 | import lombok.AllArgsConstructor; |
| 19 | 27 | import lombok.extern.slf4j.Slf4j; |
| 20 | 28 | import org.springframework.http.MediaType; |
| 21 | 29 | import org.springframework.stereotype.Service; |
| 30 | +import org.springframework.transaction.annotation.Transactional; | |
| 22 | 31 | |
| 23 | 32 | import java.io.IOException; |
| 24 | 33 | |
| ... | ... | @@ -36,6 +45,24 @@ public class AiImageServiceImpl implements AiImageService { |
| 36 | 45 | |
| 37 | 46 | private final AiImageMapper aiImageMapper; |
| 38 | 47 | private final OpenAiImageClient openAiImageClient; |
| 48 | + private final MidjourneyWebSocketStarter midjourneyWebSocketStarter; | |
| 49 | + private final MidjourneyInteractionsApi midjourneyInteractionsApi; | |
| 50 | + | |
| 51 | + @PostConstruct | |
| 52 | + public void startMidjourney() { | |
| 53 | + log.info("midjourney web socket starter..."); | |
| 54 | + midjourneyWebSocketStarter.start(new WssNotify() { | |
| 55 | + @Override | |
| 56 | + public void notify(int code, String message) { | |
| 57 | + log.info("code: {}, message: {}", code, message); | |
| 58 | + if (message.contains("Authentication failed")) { | |
| 59 | + // TODO 芋艿,这里看怎么处理,token无效的时候会认证失败! | |
| 60 | + // 认证失败 | |
| 61 | + log.error("midjourney socket 认证失败,检查token是否失效!"); | |
| 62 | + } | |
| 63 | + } | |
| 64 | + }); | |
| 65 | + } | |
| 39 | 66 | |
| 40 | 67 | @Override |
| 41 | 68 | public void dallDrawing(AiImageDallDrawingReq req, Utf8SseEmitter sseEmitter) { |
| ... | ... | @@ -55,15 +82,33 @@ public class AiImageServiceImpl implements AiImageService { |
| 55 | 82 | // 发送信息 |
| 56 | 83 | sendSseEmitter(sseEmitter, imageGeneration); |
| 57 | 84 | // 保存数据库 |
| 58 | - doSave(req, imageGeneration.getOutput().getUrl(), AiChatDrawingStatusEnum.COMPLETE, null); | |
| 85 | + doSave(req.getPrompt(), req.getSize(), req.getModal(), | |
| 86 | + imageGeneration.getOutput().getUrl(), AiChatDrawingStatusEnum.COMPLETE, null); | |
| 59 | 87 | } catch (AiException aiException) { |
| 60 | 88 | // 保存数据库 |
| 61 | - doSave(req, null, AiChatDrawingStatusEnum.FAIL, aiException.getMessage()); | |
| 89 | + doSave(req.getPrompt(), req.getSize(), req.getModal(), | |
| 90 | + null, AiChatDrawingStatusEnum.FAIL, aiException.getMessage()); | |
| 62 | 91 | // 发送错误信息 |
| 63 | 92 | sendSseEmitter(sseEmitter, aiException.getMessage()); |
| 64 | 93 | } |
| 65 | 94 | } |
| 66 | 95 | |
| 96 | + @Override | |
| 97 | + @Transactional(rollbackFor = Exception.class) | |
| 98 | + public AiImageMidjourneyRes midjourney(AiImageMidjourneyReq req) { | |
| 99 | + // 保存数据库 | |
| 100 | + doSave(req.getPrompt(), null, "midjoureny", | |
| 101 | + null, AiChatDrawingStatusEnum.SUBMIT, null); | |
| 102 | + // 提交 midjourney 任务 | |
| 103 | + Boolean imagine = midjourneyInteractionsApi.imagine(req.getPrompt()); | |
| 104 | + if (!imagine) { | |
| 105 | + throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MIDJOURNEY_IMAGINE_FAIL); | |
| 106 | + } | |
| 107 | + // | |
| 108 | + | |
| 109 | + return null; | |
| 110 | + } | |
| 111 | + | |
| 67 | 112 | private static void sendSseEmitter(Utf8SseEmitter sseEmitter, Object object) { |
| 68 | 113 | try { |
| 69 | 114 | sseEmitter.send(object, MediaType.APPLICATION_JSON); |
| ... | ... | @@ -75,14 +120,19 @@ public class AiImageServiceImpl implements AiImageService { |
| 75 | 120 | } |
| 76 | 121 | } |
| 77 | 122 | |
| 78 | - private void doSave(AiImageDallDrawingReq req, String imageUrl, AiChatDrawingStatusEnum drawingStatusEnum, String drawingError) { | |
| 123 | + private void doSave(String prompt, | |
| 124 | + String size, | |
| 125 | + String model, | |
| 126 | + String imageUrl, | |
| 127 | + AiChatDrawingStatusEnum drawingStatusEnum, | |
| 128 | + String drawingError) { | |
| 79 | 129 | // 保存数据库 |
| 80 | 130 | Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); |
| 81 | 131 | AiImageDO aiImageDO = new AiImageDO(); |
| 82 | 132 | aiImageDO.setId(null); |
| 83 | - aiImageDO.setPrompt(req.getPrompt()); | |
| 84 | - aiImageDO.setSize(req.getSize()); | |
| 85 | - aiImageDO.setModal(req.getModal()); | |
| 133 | + aiImageDO.setPrompt(prompt); | |
| 134 | + aiImageDO.setSize(size); | |
| 135 | + aiImageDO.setModal(model); | |
| 86 | 136 | aiImageDO.setUserId(loginUserId); |
| 87 | 137 | aiImageDO.setDrawingImageUrl(imageUrl); |
| 88 | 138 | aiImageDO.setDrawingStatus(drawingStatusEnum.getStatus()); | ... | ... |