Commit 5579620140c7036c35bf270b2ca40ba98a7de7b7

Authored by cherishsince
1 parent 708d66e8

【优化】聊天 event stream 改为 flex 返回更加的优雅

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/Utf8SseEmitter.java deleted
1 -package cn.iocoder.yudao.module.ai.controller;  
2 -  
3 -import org.springframework.http.HttpHeaders;  
4 -import org.springframework.http.MediaType;  
5 -import org.springframework.http.server.ServerHttpResponse;  
6 -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;  
7 -  
8 -import java.nio.charset.StandardCharsets;  
9 -  
10 -/**  
11 - * 解决中文乱码  
12 - *  
13 - * @author fansili  
14 - * @time 2024/4/14 15:13  
15 - * @since 1.0  
16 - */  
17 -public class Utf8SseEmitter extends SseEmitter {  
18 -  
19 - @Override  
20 - protected void extendResponse(ServerHttpResponse outputMessage) {  
21 - super.extendResponse(outputMessage);  
22 -  
23 - HttpHeaders headers = outputMessage.getHeaders();  
24 - headers.setContentType(new MediaType(MediaType.TEXT_EVENT_STREAM, StandardCharsets.UTF_8));  
25 - }  
26 -}  
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java
1 package cn.iocoder.yudao.module.ai.controller.admin.chat; 1 package cn.iocoder.yudao.module.ai.controller.admin.chat;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 -import cn.iocoder.yudao.module.ai.controller.Utf8SseEmitter;  
5 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; 4 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
6 -import cn.iocoder.yudao.module.ai.service.AiChatService;  
7 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; 5 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
  6 +import cn.iocoder.yudao.module.ai.service.AiChatService;
8 import io.swagger.v3.oas.annotations.Operation; 7 import io.swagger.v3.oas.annotations.Operation;
9 import io.swagger.v3.oas.annotations.Parameter; 8 import io.swagger.v3.oas.annotations.Parameter;
10 import io.swagger.v3.oas.annotations.tags.Tag; 9 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -13,7 +12,7 @@ import lombok.extern.slf4j.Slf4j; @@ -13,7 +12,7 @@ import lombok.extern.slf4j.Slf4j;
13 import org.springframework.http.MediaType; 12 import org.springframework.http.MediaType;
14 import org.springframework.validation.annotation.Validated; 13 import org.springframework.validation.annotation.Validated;
15 import org.springframework.web.bind.annotation.*; 14 import org.springframework.web.bind.annotation.*;
16 -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; 15 +import reactor.core.publisher.Flux;
17 16
18 import java.util.List; 17 import java.util.List;
19 18
@@ -39,10 +38,8 @@ public class AiChatMessageController { @@ -39,10 +38,8 @@ public class AiChatMessageController {
39 // TODO @fan:要不要使用 Flux 来返回;可以使用 Flux<AiChatMessageRespVO> 38 // TODO @fan:要不要使用 Flux 来返回;可以使用 Flux<AiChatMessageRespVO>
40 @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快") 39 @Operation(summary = "发送消息(流式)", description = "流式返回,响应较快")
41 @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) 40 @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
42 - public SseEmitter sendMessageStream(@Validated @RequestBody AiChatMessageSendReqVO sendReqVO) {  
43 - Utf8SseEmitter sseEmitter = new Utf8SseEmitter();  
44 - chatService.chatStream(sendReqVO, sseEmitter);  
45 - return sseEmitter; 41 + public Flux<AiChatMessageRespVO> sendMessageStream(@Validated @RequestBody AiChatMessageSendReqVO sendReqVO) {
  42 + return chatService.chatStream(sendReqVO);
46 } 43 }
47 44
48 @Operation(summary = "获得指定会话的消息列表") 45 @Operation(summary = "获得指定会话的消息列表")
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java
1 package cn.iocoder.yudao.module.ai.controller.admin.image; 1 package cn.iocoder.yudao.module.ai.controller.admin.image;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 -import cn.iocoder.yudao.module.ai.controller.Utf8SseEmitter;  
5 -import cn.iocoder.yudao.module.ai.service.AiImageService;  
6 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallDrawingReq; 4 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallDrawingReq;
7 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyReq; 5 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyReq;
  6 +import cn.iocoder.yudao.module.ai.service.AiImageService;
8 import io.swagger.v3.oas.annotations.Operation; 7 import io.swagger.v3.oas.annotations.Operation;
9 import io.swagger.v3.oas.annotations.tags.Tag; 8 import io.swagger.v3.oas.annotations.tags.Tag;
10 import lombok.AllArgsConstructor; 9 import lombok.AllArgsConstructor;
@@ -14,7 +13,6 @@ import org.springframework.web.bind.annotation.PostMapping; @@ -14,7 +13,6 @@ import org.springframework.web.bind.annotation.PostMapping;
14 import org.springframework.web.bind.annotation.RequestBody; 13 import org.springframework.web.bind.annotation.RequestBody;
15 import org.springframework.web.bind.annotation.RequestMapping; 14 import org.springframework.web.bind.annotation.RequestMapping;
16 import org.springframework.web.bind.annotation.RestController; 15 import org.springframework.web.bind.annotation.RestController;
17 -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;  
18 16
19 // TODO @芋艿:整理接口定义 17 // TODO @芋艿:整理接口定义
20 /** 18 /**
@@ -35,10 +33,11 @@ public class AiImageController { @@ -35,10 +33,11 @@ public class AiImageController {
35 33
36 @Operation(summary = "dall2/dall3绘画", description = "openAi dall3是付费的!") 34 @Operation(summary = "dall2/dall3绘画", description = "openAi dall3是付费的!")
37 @PostMapping("/dallDrawing") 35 @PostMapping("/dallDrawing")
38 - public SseEmitter dallDrawing(@Validated @RequestBody AiImageDallDrawingReq req) {  
39 - Utf8SseEmitter sseEmitter = new Utf8SseEmitter();  
40 - aiImageService.dallDrawing(req, sseEmitter);  
41 - return sseEmitter; 36 + public void dallDrawing(@Validated @RequestBody AiImageDallDrawingReq req) {
  37 +// Utf8SseEmitter sseEmitter = new Utf8SseEmitter();
  38 +// aiImageService.dallDrawing(req, sseEmitter);
  39 +// return sseEmitter;
  40 +
42 } 41 }
43 42
44 @Operation(summary = "midjourney", description = "midjourney图片绘画流程:1、提交任务 2、获取完成的任务 3、选择对应功能 4、获取最终结果") 43 @Operation(summary = "midjourney", description = "midjourney图片绘画流程:1、提交任务 2、获取完成的任务 3、选择对应功能 4、获取最终结果")
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatService.java
1 package cn.iocoder.yudao.module.ai.service; 1 package cn.iocoder.yudao.module.ai.service;
2 2
3 -import cn.iocoder.yudao.module.ai.controller.Utf8SseEmitter;  
4 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; 3 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
5 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; 4 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
  5 +import reactor.core.publisher.Flux;
6 6
7 import java.util.List; 7 import java.util.List;
8 8
@@ -26,11 +26,10 @@ public interface AiChatService { @@ -26,11 +26,10 @@ public interface AiChatService {
26 /** 26 /**
27 * chat stream 27 * chat stream
28 * 28 *
29 - * @param req  
30 - * @param sseEmitter 29 + * @param sendReqVO
31 * @return 30 * @return
32 */ 31 */
33 - void chatStream(AiChatMessageSendReqVO req, Utf8SseEmitter sseEmitter); 32 + Flux<AiChatMessageRespVO> chatStream(AiChatMessageSendReqVO sendReqVO);
34 33
35 /** 34 /**
36 * 获取 - 获取对话 message list 35 * 获取 - 获取对话 message list
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiImageService.java
1 package cn.iocoder.yudao.module.ai.service; 1 package cn.iocoder.yudao.module.ai.service;
2 2
3 -import cn.iocoder.yudao.module.ai.controller.Utf8SseEmitter;  
4 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallDrawingReq; 3 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallDrawingReq;
5 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyReq; 4 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyReq;
6 5
@@ -17,9 +16,8 @@ public interface AiImageService { @@ -17,9 +16,8 @@ public interface AiImageService {
17 * ai绘画 - dall2/dall3 绘画 16 * ai绘画 - dall2/dall3 绘画
18 * 17 *
19 * @param req 18 * @param req
20 - * @param sseEmitter  
21 */ 19 */
22 - void dallDrawing(AiImageDallDrawingReq req, Utf8SseEmitter sseEmitter); 20 + void dallDrawing(AiImageDallDrawingReq req);
23 21
24 /** 22 /**
25 * midjourney 图片生成 23 * midjourney 图片生成
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java
@@ -9,7 +9,6 @@ import cn.iocoder.yudao.framework.ai.chat.messages.MessageType; @@ -9,7 +9,6 @@ import cn.iocoder.yudao.framework.ai.chat.messages.MessageType;
9 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; 9 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
10 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; 10 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
11 import cn.iocoder.yudao.module.ai.config.AiChatClientFactory; 11 import cn.iocoder.yudao.module.ai.config.AiChatClientFactory;
12 -import cn.iocoder.yudao.module.ai.controller.Utf8SseEmitter;  
13 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO; 12 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
14 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; 13 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
15 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; 14 import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
@@ -25,13 +24,12 @@ import cn.iocoder.yudao.module.ai.service.AiChatRoleService; @@ -25,13 +24,12 @@ import cn.iocoder.yudao.module.ai.service.AiChatRoleService;
25 import cn.iocoder.yudao.module.ai.service.AiChatService; 24 import cn.iocoder.yudao.module.ai.service.AiChatService;
26 import lombok.AllArgsConstructor; 25 import lombok.AllArgsConstructor;
27 import lombok.extern.slf4j.Slf4j; 26 import lombok.extern.slf4j.Slf4j;
28 -import org.springframework.http.MediaType;  
29 import org.springframework.stereotype.Service; 27 import org.springframework.stereotype.Service;
30 import org.springframework.transaction.annotation.Transactional; 28 import org.springframework.transaction.annotation.Transactional;
31 import reactor.core.publisher.Flux; 29 import reactor.core.publisher.Flux;
32 30
33 -import java.io.IOException;  
34 import java.util.List; 31 import java.util.List;
  32 +import java.util.concurrent.atomic.AtomicInteger;
35 import java.util.function.Consumer; 33 import java.util.function.Consumer;
36 34
37 /** 35 /**
@@ -76,6 +74,7 @@ public class AiChatServiceImpl implements AiChatService { @@ -76,6 +74,7 @@ public class AiChatServiceImpl implements AiChatService {
76 chatModal.getModel(), chatModal.getId(), req.getContent(), 74 chatModal.getModel(), chatModal.getId(), req.getContent(),
77 null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts()); 75 null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
78 String content = null; 76 String content = null;
  77 + int tokens = 0;
79 try { 78 try {
80 // 创建 chat 需要的 Prompt 79 // 创建 chat 需要的 Prompt
81 Prompt prompt = new Prompt(req.getContent()); 80 Prompt prompt = new Prompt(req.getContent());
@@ -87,6 +86,7 @@ public class AiChatServiceImpl implements AiChatService { @@ -87,6 +86,7 @@ public class AiChatServiceImpl implements AiChatService {
87 ChatClient chatClient = aiChatClientFactory.getChatClient(platformEnum); 86 ChatClient chatClient = aiChatClientFactory.getChatClient(platformEnum);
88 ChatResponse call = chatClient.call(prompt); 87 ChatResponse call = chatClient.call(prompt);
89 content = call.getResult().getOutput().getContent(); 88 content = call.getResult().getOutput().getContent();
  89 + tokens = call.getResults().size();
90 // 更新 conversation 90 // 更新 conversation
91 } catch (Exception e) { 91 } catch (Exception e) {
92 content = ExceptionUtil.getMessage(e); 92 content = ExceptionUtil.getMessage(e);
@@ -94,7 +94,7 @@ public class AiChatServiceImpl implements AiChatService { @@ -94,7 +94,7 @@ public class AiChatServiceImpl implements AiChatService {
94 // 保存 chat message 94 // 保存 chat message
95 insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(), 95 insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(),
96 chatModal.getModel(), chatModal.getId(), content, 96 chatModal.getModel(), chatModal.getId(), content,
97 - null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts()); 97 + tokens, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
98 } 98 }
99 return new AiChatMessageRespVO().setContent(content); 99 return new AiChatMessageRespVO().setContent(content);
100 } 100 }
@@ -123,8 +123,7 @@ public class AiChatServiceImpl implements AiChatService { @@ -123,8 +123,7 @@ public class AiChatServiceImpl implements AiChatService {
123 return insertChatMessageDO; 123 return insertChatMessageDO;
124 } 124 }
125 125
126 - @Override  
127 - public void chatStream(AiChatMessageSendReqVO req, Utf8SseEmitter sseEmitter) { 126 + public Flux<AiChatMessageRespVO> chatStream(AiChatMessageSendReqVO req) {
128 Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); 127 Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
129 // 查询对话 128 // 查询对话
130 AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId()); 129 AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId());
@@ -145,46 +144,42 @@ public class AiChatServiceImpl implements AiChatService { @@ -145,46 +144,42 @@ public class AiChatServiceImpl implements AiChatService {
145 // req.setTopP(req.getTopP()); 144 // req.setTopP(req.getTopP());
146 // req.setTemperature(req.getTemperature()); 145 // req.setTemperature(req.getTemperature());
147 // 保存 chat message 146 // 保存 chat message
148 - // 保存 chat message  
149 insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(), 147 insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(),
150 chatModal.getModel(), chatModal.getId(), req.getContent(), 148 chatModal.getModel(), chatModal.getId(), req.getContent(),
151 null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts()); 149 null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
152 -  
153 // 获取 client 类型 150 // 获取 client 类型
154 AiPlatformEnum platformEnum = AiPlatformEnum.valueOfPlatform(chatModal.getPlatform()); 151 AiPlatformEnum platformEnum = AiPlatformEnum.valueOfPlatform(chatModal.getPlatform());
155 StreamingChatClient streamingChatClient = aiChatClientFactory.getStreamingChatClient(platformEnum); 152 StreamingChatClient streamingChatClient = aiChatClientFactory.getStreamingChatClient(platformEnum);
156 Flux<ChatResponse> streamResponse = streamingChatClient.stream(prompt); 153 Flux<ChatResponse> streamResponse = streamingChatClient.stream(prompt);
157 - 154 + // 转换 flex AiChatMessageRespVO
158 StringBuffer contentBuffer = new StringBuffer(); 155 StringBuffer contentBuffer = new StringBuffer();
159 - streamResponse.subscribe(  
160 - new Consumer<ChatResponse>() {  
161 - @Override  
162 - public void accept(ChatResponse chatResponse) {  
163 - String content = chatResponse.getResults().get(0).getOutput().getContent();  
164 - try {  
165 - contentBuffer.append(content);  
166 - sseEmitter.send(new AiChatMessageRespVO().setContent(content), MediaType.APPLICATION_JSON);  
167 - } catch (IOException e) {  
168 - log.error("发送异常{}", ExceptionUtil.getMessage(e));  
169 - // 如果不是因为关闭而抛出异常,则重新连接  
170 - sseEmitter.completeWithError(e);  
171 - }  
172 - }  
173 - },  
174 - error -> {  
175 - //  
176 - log.error("subscribe错误 {}", ExceptionUtil.getMessage(error));  
177 - },  
178 - () -> {  
179 - log.info("发送完成!");  
180 - sseEmitter.complete();  
181 - // 保存 chat message  
182 - insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(),  
183 - chatModal.getModel(), chatModal.getId(), contentBuffer.toString(),  
184 - null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());  
185 - 156 + AtomicInteger tokens = new AtomicInteger(0);
  157 + return streamResponse.map(res -> {
  158 + AiChatMessageRespVO aiChatMessageRespVO = new AiChatMessageRespVO();
  159 + aiChatMessageRespVO.setContent(res.getResult().getOutput().getContent());
  160 + contentBuffer.append(res.getResult().getOutput().getContent());
  161 + tokens.incrementAndGet();
  162 + return aiChatMessageRespVO;
186 } 163 }
187 - ); 164 + ).doOnComplete(new Runnable() {
  165 + @Override
  166 + public void run() {
  167 + log.info("发送完成!");
  168 + // 保存 chat message
  169 + insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(),
  170 + chatModal.getModel(), chatModal.getId(), contentBuffer.toString(),
  171 + tokens.get(), conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
  172 + }
  173 + }).doOnError(new Consumer<Throwable>() {
  174 + @Override
  175 + public void accept(Throwable throwable) {
  176 + log.error("发送错误 {}!", throwable.getMessage());
  177 + // 保存 chat message
  178 + insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(),
  179 + chatModal.getModel(), chatModal.getId(), throwable.getMessage(),
  180 + tokens.get(), conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts());
  181 + }
  182 + });
188 } 183 }
189 184
190 @Override 185 @Override
@@ -194,7 +189,7 @@ public class AiChatServiceImpl implements AiChatService { @@ -194,7 +189,7 @@ public class AiChatServiceImpl implements AiChatService {
194 // 获取对话所有 message 189 // 获取对话所有 message
195 List<AiChatMessageDO> aiChatMessageDOList = aiChatMessageMapper.selectByConversationId(conversationId); 190 List<AiChatMessageDO> aiChatMessageDOList = aiChatMessageMapper.selectByConversationId(conversationId);
196 // 转换 AiChatMessageRespVO 191 // 转换 AiChatMessageRespVO
197 - return AiChatMessageConvert.INSTANCE.convertAiChatMessageRespVOList(aiChatMessageDOList); 192 + return AiChatMessageConvert.INSTANCE.convertAiChatMessageRespVOList(aiChatMessageDOList);
198 } 193 }
199 194
200 @Override 195 @Override
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiImageServiceImpl.java
@@ -5,8 +5,8 @@ import cn.iocoder.yudao.framework.ai.image.ImageGeneration; @@ -5,8 +5,8 @@ import cn.iocoder.yudao.framework.ai.image.ImageGeneration;
5 import cn.iocoder.yudao.framework.ai.image.ImagePrompt; 5 import cn.iocoder.yudao.framework.ai.image.ImagePrompt;
6 import cn.iocoder.yudao.framework.ai.image.ImageResponse; 6 import cn.iocoder.yudao.framework.ai.image.ImageResponse;
7 import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageClient; 7 import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageClient;
8 -import cn.iocoder.yudao.framework.ai.imageopenai.enums.OpenAiImageModelEnum;  
9 import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageOptions; 8 import cn.iocoder.yudao.framework.ai.imageopenai.OpenAiImageOptions;
  9 +import cn.iocoder.yudao.framework.ai.imageopenai.enums.OpenAiImageModelEnum;
10 import cn.iocoder.yudao.framework.ai.imageopenai.enums.OpenAiImageStyleEnum; 10 import cn.iocoder.yudao.framework.ai.imageopenai.enums.OpenAiImageStyleEnum;
11 import cn.iocoder.yudao.framework.ai.midjourney.api.MidjourneyInteractionsApi; 11 import cn.iocoder.yudao.framework.ai.midjourney.api.MidjourneyInteractionsApi;
12 import cn.iocoder.yudao.framework.ai.midjourney.webSocket.MidjourneyWebSocketStarter; 12 import cn.iocoder.yudao.framework.ai.midjourney.webSocket.MidjourneyWebSocketStarter;
@@ -14,22 +14,18 @@ import cn.iocoder.yudao.framework.ai.midjourney.webSocket.WssNotify; @@ -14,22 +14,18 @@ import cn.iocoder.yudao.framework.ai.midjourney.webSocket.WssNotify;
14 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; 14 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
15 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; 15 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
16 import cn.iocoder.yudao.module.ai.ErrorCodeConstants; 16 import cn.iocoder.yudao.module.ai.ErrorCodeConstants;
17 -import cn.iocoder.yudao.module.ai.controller.Utf8SseEmitter; 17 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallDrawingReq;
  18 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyReq;
18 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; 19 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
19 -import cn.iocoder.yudao.module.ai.enums.AiChatDrawingStatusEnum;  
20 import cn.iocoder.yudao.module.ai.dal.mysql.AiImageMapper; 20 import cn.iocoder.yudao.module.ai.dal.mysql.AiImageMapper;
  21 +import cn.iocoder.yudao.module.ai.enums.AiChatDrawingStatusEnum;
21 import cn.iocoder.yudao.module.ai.service.AiImageService; 22 import cn.iocoder.yudao.module.ai.service.AiImageService;
22 -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallDrawingReq;  
23 -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyReq;  
24 import jakarta.annotation.PostConstruct; 23 import jakarta.annotation.PostConstruct;
25 import lombok.AllArgsConstructor; 24 import lombok.AllArgsConstructor;
26 import lombok.extern.slf4j.Slf4j; 25 import lombok.extern.slf4j.Slf4j;
27 -import org.springframework.http.MediaType;  
28 import org.springframework.stereotype.Service; 26 import org.springframework.stereotype.Service;
29 import org.springframework.transaction.annotation.Transactional; 27 import org.springframework.transaction.annotation.Transactional;
30 28
31 -import java.io.IOException;  
32 -  
33 /** 29 /**
34 * ai 作图 30 * ai 作图
35 * 31 *
@@ -64,7 +60,7 @@ public class AiImageServiceImpl implements AiImageService { @@ -64,7 +60,7 @@ public class AiImageServiceImpl implements AiImageService {
64 } 60 }
65 61
66 @Override 62 @Override
67 - public void dallDrawing(AiImageDallDrawingReq req, Utf8SseEmitter sseEmitter) { 63 + public void dallDrawing(AiImageDallDrawingReq req) {
68 // 获取 model 64 // 获取 model
69 OpenAiImageModelEnum openAiImageModelEnum = OpenAiImageModelEnum.valueOfModel(req.getModal()); 65 OpenAiImageModelEnum openAiImageModelEnum = OpenAiImageModelEnum.valueOfModel(req.getModal());
70 OpenAiImageStyleEnum openAiImageStyleEnum = OpenAiImageStyleEnum.valueOfStyle(req.getStyle()); 66 OpenAiImageStyleEnum openAiImageStyleEnum = OpenAiImageStyleEnum.valueOfStyle(req.getStyle());
@@ -79,7 +75,7 @@ public class AiImageServiceImpl implements AiImageService { @@ -79,7 +75,7 @@ public class AiImageServiceImpl implements AiImageService {
79 // 发送 75 // 发送
80 ImageGeneration imageGeneration = imageResponse.getResult(); 76 ImageGeneration imageGeneration = imageResponse.getResult();
81 // 发送信息 77 // 发送信息
82 - sendSseEmitter(sseEmitter, imageGeneration); 78 +// sendSseEmitter(sseEmitter, imageGeneration);
83 // 保存数据库 79 // 保存数据库
84 doSave(req.getPrompt(), req.getSize(), req.getModal(), 80 doSave(req.getPrompt(), req.getSize(), req.getModal(),
85 imageGeneration.getOutput().getUrl(), AiChatDrawingStatusEnum.COMPLETE, null); 81 imageGeneration.getOutput().getUrl(), AiChatDrawingStatusEnum.COMPLETE, null);
@@ -88,7 +84,7 @@ public class AiImageServiceImpl implements AiImageService { @@ -88,7 +84,7 @@ public class AiImageServiceImpl implements AiImageService {
88 doSave(req.getPrompt(), req.getSize(), req.getModal(), 84 doSave(req.getPrompt(), req.getSize(), req.getModal(),
89 null, AiChatDrawingStatusEnum.FAIL, aiException.getMessage()); 85 null, AiChatDrawingStatusEnum.FAIL, aiException.getMessage());
90 // 发送错误信息 86 // 发送错误信息
91 - sendSseEmitter(sseEmitter, aiException.getMessage()); 87 +// sendSseEmitter(sseEmitter, aiException.getMessage());
92 } 88 }
93 } 89 }
94 90
@@ -105,16 +101,16 @@ public class AiImageServiceImpl implements AiImageService { @@ -105,16 +101,16 @@ public class AiImageServiceImpl implements AiImageService {
105 } 101 }
106 } 102 }
107 103
108 - private static void sendSseEmitter(Utf8SseEmitter sseEmitter, Object object) {  
109 - try {  
110 - sseEmitter.send(object, MediaType.APPLICATION_JSON);  
111 - } catch (IOException e) {  
112 - throw new RuntimeException(e);  
113 - } finally {  
114 - // 发送 complete  
115 - sseEmitter.complete();  
116 - }  
117 - } 104 +// private static void sendSseEmitter(Utf8SseEmitter sseEmitter, Object object) {
  105 +// try {
  106 +// sseEmitter.send(object, MediaType.APPLICATION_JSON);
  107 +// } catch (IOException e) {
  108 +// throw new RuntimeException(e);
  109 +// } finally {
  110 +// // 发送 complete
  111 +// sseEmitter.complete();
  112 +// }
  113 +// }
118 114
119 private AiImageDO doSave(String prompt, 115 private AiImageDO doSave(String prompt,
120 String size, 116 String size,
yudao-server/src/main/resources/application-local.yaml
@@ -2,7 +2,6 @@ server: @@ -2,7 +2,6 @@ server:
2 port: 48080 2 port: 48080
3 3
4 --- #################### 数据库相关配置 #################### 4 --- #################### 数据库相关配置 ####################
5 -  
6 spring: 5 spring:
7 # 数据源配置项 6 # 数据源配置项
8 autoconfigure: 7 autoconfigure:
@@ -79,7 +78,12 @@ spring: @@ -79,7 +78,12 @@ spring:
79 port: 6379 # 端口 78 port: 6379 # 端口
80 database: 0 # 数据库索引 79 database: 0 # 数据库索引
81 # password: dev # 密码,建议生产环境开启 80 # password: dev # 密码,建议生产环境开启
82 - 81 +server:
  82 + servlet:
  83 + encoding:
  84 + enabled: true
  85 + charset: UTF-8
  86 + force: true
83 --- #################### 定时任务相关配置 #################### 87 --- #################### 定时任务相关配置 ####################
84 88
85 # Quartz 配置项,对应 QuartzProperties 配置类 89 # Quartz 配置项,对应 QuartzProperties 配置类