Commit c29430e7542147b46a5c5be6dda050e80e9448ae

Authored by cherishsince
2 parents 7d0f5fdb c8a2c836

Merge branch 'master-jdk21-ai' of https://gitee.com/cherishsince/ruoyi-vue-pro into master-jdk21-ai

Showing 33 changed files with 290 additions and 341 deletions
yudao-dependencies/pom.xml
@@ -628,15 +628,6 @@ @@ -628,15 +628,6 @@
628 <artifactId>ureport2-console</artifactId> 628 <artifactId>ureport2-console</artifactId>
629 <version>${ureport2.version}</version> 629 <version>${ureport2.version}</version>
630 </dependency> 630 </dependency>
631 -  
632 - <!-- 添加ai模块 -->  
633 - <dependency>  
634 - <groupId>org.springframework.ai</groupId>  
635 - <artifactId>spring-ai-bom</artifactId>  
636 - <version>${spring-ai-bom.version}</version>  
637 - <type>pom</type>  
638 - <scope>import</scope>  
639 - </dependency>  
640 </dependencies> 631 </dependencies>
641 </dependencyManagement> 632 </dependencyManagement>
642 633
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java
@@ -11,6 +11,7 @@ public interface ErrorCodeConstants { @@ -11,6 +11,7 @@ public interface ErrorCodeConstants {
11 11
12 // ========== 模块 ai 错误码区间 [1-022-000-000 ~ 1-023-000-000) ========== 12 // ========== 模块 ai 错误码区间 [1-022-000-000 ~ 1-023-000-000) ==========
13 13
  14 + // TODO @fansili:1)类注释不太对;2)中英文之间,有个空格;例如说 AI 模型
14 ErrorCode AI_MODULE_NOT_SUPPORTED = new ErrorCode(1_022_000_000, "AI模型暂不支持!"); 15 ErrorCode AI_MODULE_NOT_SUPPORTED = new ErrorCode(1_022_000_000, "AI模型暂不支持!");
15 16
16 } 17 }
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiModelEnum.java deleted
1 -package cn.iocoder.yudao.module.ai.enums;  
2 -  
3 -import lombok.Getter;  
4 -  
5 -/**  
6 - * author: fansili  
7 - * time: 2024/3/4 12:36  
8 - */  
9 -@Getter  
10 -public enum AiModelEnum {  
11 -  
12 - OPEN_AI_GPT_3_5("gpt-3.5-turbo", "GPT3.5"),  
13 - OPEN_AI_GPT_4("gpt-4-turbo", "GPT4")  
14 -  
15 - ;  
16 -  
17 - AiModelEnum(String value, String message) {  
18 - this.value = value;  
19 - this.message = message;  
20 - }  
21 -  
22 - private String value;  
23 -  
24 - private String message;  
25 -}  
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/OpenAiModelEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.enums;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +// TODO done @fansili:1)类注释要加下;2)author 和 time 用 javadoc,@author 和 @since;3)@AllArgsConstructor 使用这个注解,去掉构造方法;4)value 改成 model 字段,然后注释都写下哈;5)message 改成 name,然后注释都写下哈
  7 +/**
  8 + * @author: fansili
  9 + * @time: 2024/3/4 12:36
  10 + */
  11 +@Getter
  12 +@AllArgsConstructor
  13 +public enum OpenAiModelEnum {
  14 +
  15 + /**
  16 + * open ai 3.5模型
  17 + */
  18 + OPEN_AI_GPT_3_5("gpt-3.5-turbo", "GPT3.5"),
  19 + /**
  20 + * open ai 4.0 收费模型
  21 + */
  22 + OPEN_AI_GPT_4("gpt-4-turbo", "GPT4")
  23 +
  24 + ;
  25 +
  26 + /**
  27 + * 模型 - 用于参数传递
  28 + */
  29 + private String model;
  30 + /**
  31 + * 模型名字 - 用于展示
  32 + */
  33 + private String name;
  34 +
  35 +}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/ChatController.java
@@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.module.ai.ErrorCodeConstants; 5 import cn.iocoder.yudao.module.ai.ErrorCodeConstants;
6 import cn.iocoder.yudao.module.ai.controller.admin.vo.AiChatReqVO; 6 import cn.iocoder.yudao.module.ai.controller.admin.vo.AiChatReqVO;
7 -import cn.iocoder.yudao.module.ai.enums.AiModelEnum; 7 +import cn.iocoder.yudao.module.ai.enums.OpenAiModelEnum;
8 import io.swagger.v3.oas.annotations.Operation; 8 import io.swagger.v3.oas.annotations.Operation;
9 import io.swagger.v3.oas.annotations.tags.Tag; 9 import io.swagger.v3.oas.annotations.tags.Tag;
10 import jakarta.servlet.http.HttpServletResponse; 10 import jakarta.servlet.http.HttpServletResponse;
@@ -13,7 +13,6 @@ import org.springframework.ai.chat.ChatClient; @@ -13,7 +13,6 @@ import org.springframework.ai.chat.ChatClient;
13 import org.springframework.ai.chat.ChatResponse; 13 import org.springframework.ai.chat.ChatResponse;
14 import org.springframework.ai.chat.prompt.Prompt; 14 import org.springframework.ai.chat.prompt.Prompt;
15 import org.springframework.ai.openai.OpenAiChatClient; 15 import org.springframework.ai.openai.OpenAiChatClient;
16 -import org.springframework.ai.openai.api.OpenAiApi;  
17 import org.springframework.beans.factory.annotation.Autowired; 16 import org.springframework.beans.factory.annotation.Autowired;
18 import org.springframework.context.ApplicationContext; 17 import org.springframework.context.ApplicationContext;
19 import org.springframework.validation.annotation.Validated; 18 import org.springframework.validation.annotation.Validated;
@@ -23,15 +22,9 @@ import org.springframework.web.bind.annotation.RequestMapping; @@ -23,15 +22,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
23 import org.springframework.web.bind.annotation.RestController; 22 import org.springframework.web.bind.annotation.RestController;
24 import reactor.core.publisher.Flux; 23 import reactor.core.publisher.Flux;
25 24
26 -import java.util.Scanner;  
27 import java.util.function.Consumer; 25 import java.util.function.Consumer;
28 26
29 -/**  
30 - * AI模块  
31 - *  
32 - * author: fansili  
33 - * time: 2024/3/3 20:28  
34 - */ 27 +// TODO done @fansili:有了 swagger 注释,就不用类注释了
35 @Tag(name = "AI模块") 28 @Tag(name = "AI模块")
36 @RestController 29 @RestController
37 @RequestMapping("/ai-api") 30 @RequestMapping("/ai-api")
@@ -47,7 +40,7 @@ public class ChatController { @@ -47,7 +40,7 @@ public class ChatController {
47 ChatClient chatClient = getChatClient(reqVO.getAiModel()); 40 ChatClient chatClient = getChatClient(reqVO.getAiModel());
48 String res; 41 String res;
49 try { 42 try {
50 - res = chatClient.call(reqVO.getInputText()); 43 + res = chatClient.call(reqVO.getPrompt());
51 } catch (Exception e) { 44 } catch (Exception e) {
52 res = e.getMessage(); 45 res = e.getMessage();
53 } 46 }
@@ -58,33 +51,14 @@ public class ChatController { @@ -58,33 +51,14 @@ public class ChatController {
58 @Operation(summary = "对话聊天chatStream", description = "简单的ai聊天") 51 @Operation(summary = "对话聊天chatStream", description = "简单的ai聊天")
59 public CommonResult chatStream(HttpServletResponse response, @RequestBody @Validated AiChatReqVO reqVO) throws InterruptedException { 52 public CommonResult chatStream(HttpServletResponse response, @RequestBody @Validated AiChatReqVO reqVO) throws InterruptedException {
60 OpenAiChatClient chatClient = applicationContext.getBean(OpenAiChatClient.class); 53 OpenAiChatClient chatClient = applicationContext.getBean(OpenAiChatClient.class);
61 - Flux<ChatResponse> chatResponse = chatClient.stream(new Prompt(reqVO.getInputText())); 54 + Flux<ChatResponse> chatResponse = chatClient.stream(new Prompt(reqVO.getPrompt()));
62 chatResponse.subscribe(new Consumer<ChatResponse>() { 55 chatResponse.subscribe(new Consumer<ChatResponse>() {
63 @Override 56 @Override
64 public void accept(ChatResponse chatResponse) { 57 public void accept(ChatResponse chatResponse) {
65 System.err.println(chatResponse.getResults().get(0).getOutput().getContent()); 58 System.err.println(chatResponse.getResults().get(0).getOutput().getContent());
66 } 59 }
67 }); 60 });
68 - return CommonResult.success("1");  
69 - }  
70 -  
71 - public static void main(String[] args) {  
72 - OpenAiChatClient openAiChatClient = new OpenAiChatClient(new OpenAiApi("openkey"));  
73 - Flux<ChatResponse> responseFlux = openAiChatClient.stream(new Prompt("最好的编程语言!"));  
74 - long now = System.currentTimeMillis();  
75 - responseFlux.subscribe(new Consumer<ChatResponse>() {  
76 - @Override  
77 - public void accept(ChatResponse chatResponse) {  
78 - if (chatResponse.getResults().get(0).getOutput() == null) {  
79 - return;  
80 - }  
81 - System.err.println(chatResponse.getResults().get(0).getOutput().getContent());  
82 - }  
83 - });  
84 -  
85 - // 阻止退出  
86 - Scanner scanner = new Scanner(System.in);  
87 - scanner.nextLine(); 61 + return CommonResult.success(null);
88 } 62 }
89 63
90 /** 64 /**
@@ -93,8 +67,8 @@ public class ChatController { @@ -93,8 +67,8 @@ public class ChatController {
93 * @param aiModelEnum 67 * @param aiModelEnum
94 * @return 68 * @return
95 */ 69 */
96 - private ChatClient getChatClient(AiModelEnum aiModelEnum) {  
97 - if (AiModelEnum.OPEN_AI_GPT_3_5 == aiModelEnum) { 70 + private ChatClient getChatClient(OpenAiModelEnum aiModelEnum) {
  71 + if (OpenAiModelEnum.OPEN_AI_GPT_3_5 == aiModelEnum) {
98 return applicationContext.getBean(OpenAiChatClient.class); 72 return applicationContext.getBean(OpenAiChatClient.class);
99 } 73 }
100 // AI模型暂不支持 74 // AI模型暂不支持
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/vo/AiChatReqVO.java
1 package cn.iocoder.yudao.module.ai.controller.admin.vo; 1 package cn.iocoder.yudao.module.ai.controller.admin.vo;
2 2
3 -import cn.iocoder.yudao.module.ai.enums.AiModelEnum; 3 +import cn.iocoder.yudao.module.ai.enums.OpenAiModelEnum;
4 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
5 import jakarta.validation.constraints.NotNull; 5 import jakarta.validation.constraints.NotNull;
6 import lombok.Data; 6 import lombok.Data;
7 7
8 -/**  
9 - * ai 聊天 req  
10 - *  
11 - * author: fansili  
12 - * time: 2024/3/4 12:33  
13 - */  
14 -@Schema(description = "用户 App - 上传文件 Request VO") 8 +// TODO done @fansili 1)swagger 注释不太对;2)有了 swagger 注释,就不用类注释了
15 @Data 9 @Data
  10 +@Schema(description = "用户 App - 上传文件 Request VO")
16 public class AiChatReqVO { 11 public class AiChatReqVO {
17 12
18 - @Schema(description = "输入内容", requiredMode = Schema.RequiredMode.REQUIRED)  
19 - @NotNull(message = "输入内容不能为空")  
20 - private String inputText; 13 + @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED)
  14 + @NotNull(message = "提示词不能为空!")
  15 + private String prompt;
21 16
22 @Schema(description = "AI模型", requiredMode = Schema.RequiredMode.REQUIRED) 17 @Schema(description = "AI模型", requiredMode = Schema.RequiredMode.REQUIRED)
23 @NotNull(message = "AI模型不能为空") 18 @NotNull(message = "AI模型不能为空")
24 - private AiModelEnum aiModel; 19 + private OpenAiModelEnum aiModel;
25 20
26 } 21 }
yudao-module-ai/yudao-module-ai-biz/src/main/resources/application.properties
1 -# open ai 1 +# open ai TODO @fansili??????????????
2 2
3 # openAI https://openai.com/ 3 # openAI https://openai.com/
4 spring.ai.openai.api-key=${OPEN_AI_KEY} 4 spring.ai.openai.api-key=${OPEN_AI_KEY}
yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
@@ -149,6 +149,10 @@ @@ -149,6 +149,10 @@
149 <!-- </exclusion>--> 149 <!-- </exclusion>-->
150 <!-- </exclusions>--> 150 <!-- </exclusions>-->
151 </dependency> 151 </dependency>
  152 + <dependency>
  153 + <groupId>cn.iocoder.boot</groupId>
  154 + <artifactId>yudao-common</artifactId>
  155 + </dependency>
152 </dependencies> 156 </dependencies>
153 157
154 </project> 158 </project>
155 \ No newline at end of file 159 \ No newline at end of file
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/QianWenChatClient.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.ai.chat.*; @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.ai.chat.*;
6 import cn.iocoder.yudao.framework.ai.chat.messages.MessageType; 6 import cn.iocoder.yudao.framework.ai.chat.messages.MessageType;
7 import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions; 7 import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions;
8 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; 8 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
  9 +import cn.iocoder.yudao.framework.ai.chatqianwen.api.QianWenApi;
9 import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException; 10 import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException;
10 import com.aliyun.broadscope.bailian.sdk.models.*; 11 import com.aliyun.broadscope.bailian.sdk.models.*;
11 import lombok.extern.slf4j.Slf4j; 12 import lombok.extern.slf4j.Slf4j;
@@ -29,7 +30,7 @@ import java.util.stream.Collectors; @@ -29,7 +30,7 @@ import java.util.stream.Collectors;
29 * time: 2024/3/13 21:06 30 * time: 2024/3/13 21:06
30 */ 31 */
31 @Slf4j 32 @Slf4j
32 -public class QianWenChatClient implements ChatClient, StreamingChatClient { 33 +public class QianWenChatClient implements ChatClient, StreamingChatClient {
33 34
34 private QianWenApi qianWenApi; 35 private QianWenApi qianWenApi;
35 36
@@ -44,6 +45,7 @@ public class QianWenChatClient implements ChatClient, StreamingChatClient { @@ -44,6 +45,7 @@ public class QianWenChatClient implements ChatClient, StreamingChatClient {
44 this.qianWenOptions = qianWenOptions; 45 this.qianWenOptions = qianWenOptions;
45 } 46 }
46 47
  48 + // TODO @fansili:看看咋公用出来,允许传入类似异常之类的参数;
47 public final RetryTemplate retryTemplate = RetryTemplate.builder() 49 public final RetryTemplate retryTemplate = RetryTemplate.builder()
48 // 最大重试次数 10 50 // 最大重试次数 10
49 .maxAttempts(10) 51 .maxAttempts(10)
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/QianWenApi.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatqianwen/api/QianWenApi.java
1 -package cn.iocoder.yudao.framework.ai.chatqianwen; 1 +package cn.iocoder.yudao.framework.ai.chatqianwen.api;
2 2
3 import com.aliyun.broadscope.bailian.sdk.AccessTokenClient; 3 import com.aliyun.broadscope.bailian.sdk.AccessTokenClient;
4 import com.aliyun.broadscope.bailian.sdk.ApplicationClient; 4 import com.aliyun.broadscope.bailian.sdk.ApplicationClient;
@@ -9,6 +9,7 @@ import org.springframework.http.HttpStatusCode; @@ -9,6 +9,7 @@ import org.springframework.http.HttpStatusCode;
9 import org.springframework.http.ResponseEntity; 9 import org.springframework.http.ResponseEntity;
10 import reactor.core.publisher.Flux; 10 import reactor.core.publisher.Flux;
11 11
  12 +// TODO done @fansili:是不是挪到 api 包里?按照 spring ai 的结构;根目录只放 client 和 options
12 /** 13 /**
13 * 阿里 通义千问 14 * 阿里 通义千问
14 * 15 *
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/imageopenai/OpenAiImageApi.java
@@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.ai.imageopenai; @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.ai.imageopenai;
3 import cn.hutool.json.JSONUtil; 3 import cn.hutool.json.JSONUtil;
4 import cn.iocoder.yudao.framework.ai.imageopenai.api.OpenAiImageRequest; 4 import cn.iocoder.yudao.framework.ai.imageopenai.api.OpenAiImageRequest;
5 import cn.iocoder.yudao.framework.ai.imageopenai.api.OpenAiImageResponse; 5 import cn.iocoder.yudao.framework.ai.imageopenai.api.OpenAiImageResponse;
6 -import cn.iocoder.yudao.framework.ai.util.JacksonUtil; 6 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
7 import io.netty.channel.ChannelOption; 7 import io.netty.channel.ChannelOption;
8 import lombok.extern.slf4j.Slf4j; 8 import lombok.extern.slf4j.Slf4j;
9 import org.apache.http.HttpEntity; 9 import org.apache.http.HttpEntity;
@@ -14,7 +14,6 @@ import org.apache.http.impl.client.CloseableHttpClient; @@ -14,7 +14,6 @@ import org.apache.http.impl.client.CloseableHttpClient;
14 import org.apache.http.impl.client.HttpClients; 14 import org.apache.http.impl.client.HttpClients;
15 import org.apache.http.util.EntityUtils; 15 import org.apache.http.util.EntityUtils;
16 import org.springframework.http.client.reactive.ReactorClientHttpConnector; 16 import org.springframework.http.client.reactive.ReactorClientHttpConnector;
17 -import org.springframework.web.reactive.function.BodyInserters;  
18 import org.springframework.web.reactive.function.client.WebClient; 17 import org.springframework.web.reactive.function.client.WebClient;
19 import reactor.netty.http.client.HttpClient; 18 import reactor.netty.http.client.HttpClient;
20 19
@@ -55,7 +54,7 @@ public class OpenAiImageApi { @@ -55,7 +54,7 @@ public class OpenAiImageApi {
55 httpPost.setURI(URI.create(DEFAULT_BASE_URL.concat("/v1/images/generations"))); 54 httpPost.setURI(URI.create(DEFAULT_BASE_URL.concat("/v1/images/generations")));
56 httpPost.setHeader("Content-Type", "application/json"); 55 httpPost.setHeader("Content-Type", "application/json");
57 httpPost.setHeader("Authorization", "Bearer " + apiKey); 56 httpPost.setHeader("Authorization", "Bearer " + apiKey);
58 - httpPost.setEntity(new StringEntity(JacksonUtil.toJson(request), "UTF-8")); 57 + httpPost.setEntity(new StringEntity(JsonUtils.toJsonString(request), "UTF-8"));
59 58
60 CloseableHttpResponse response= null; 59 CloseableHttpResponse response= null;
61 try { 60 try {
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/MjMessage.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/MidjourneyMessage.java
1 package cn.iocoder.yudao.framework.ai.midjourney; 1 package cn.iocoder.yudao.framework.ai.midjourney;
2 2
  3 +import cn.iocoder.yudao.framework.ai.midjourney.constants.MidjourneyGennerateStatusEnum;
3 import lombok.Data; 4 import lombok.Data;
4 import lombok.experimental.Accessors; 5 import lombok.experimental.Accessors;
5 6
@@ -7,7 +8,7 @@ import java.util.List; @@ -7,7 +8,7 @@ import java.util.List;
7 8
8 @Data 9 @Data
9 @Accessors(chain = true) 10 @Accessors(chain = true)
10 -public class MjMessage { 11 +public class MidjourneyMessage {
11 12
12 /** 13 /**
13 * id是一个重要的字段,在同时生成多个的时候,可以区分生成信息 14 * id是一个重要的字段,在同时生成多个的时候,可以区分生成信息
@@ -41,7 +42,7 @@ public class MjMessage { @@ -41,7 +42,7 @@ public class MjMessage {
41 * 1、等待 42 * 1、等待
42 * 2、进行中 43 * 2、进行中
43 * 3、完成 44 * 3、完成
44 - * {@link cn.iocoder.yudao.framework.ai.midjourney.constants.MjGennerateStatusEnum} 45 + * {@link MidjourneyGennerateStatusEnum}
45 */ 46 */
46 private String generateStatus; 47 private String generateStatus;
47 48
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/api/MidjourneyInteractions.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.midjourney.api;
  2 +
  3 +import cn.hutool.core.util.IdUtil;
  4 +import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig;
  5 +import cn.iocoder.yudao.framework.ai.midjourney.constants.MidjourneyConstants;
  6 +import com.google.common.collect.Maps;
  7 +import lombok.extern.slf4j.Slf4j;
  8 +import org.springframework.http.HttpHeaders;
  9 +import org.springframework.http.MediaType;
  10 +
  11 +import java.util.HashMap;
  12 +
  13 +// TODO @fansili:按照 spring ai 的封装习惯,这个类是不是 MidjourneyApi
  14 +
  15 +/**
  16 + * 图片生成
  17 + *
  18 + * author: fansili
  19 + * time: 2024/4/3 17:36
  20 + */
  21 +@Slf4j
  22 +public abstract class MidjourneyInteractions {
  23 +
  24 + // TODO done @fansili:静态变量,放在最前面哈;
  25 + /**
  26 + * header - referer 头信息
  27 + */
  28 + private static final String HEADER_REFERER = "https://discord.com/channels/%s/%s";
  29 + /**
  30 + * mj配置文件
  31 + */
  32 + protected final MidjourneyConfig midjourneyConfig;
  33 +
  34 + protected MidjourneyInteractions(MidjourneyConfig midjourneyConfig) {
  35 + this.midjourneyConfig = midjourneyConfig;
  36 + }
  37 +
  38 + /**
  39 + * 获取headers - application json
  40 + *
  41 + * @return
  42 + */
  43 + protected HttpHeaders getHeadersOfAppJson() {
  44 + // 设置header值
  45 + HttpHeaders httpHeaders = new HttpHeaders();
  46 + httpHeaders.setContentType(MediaType.APPLICATION_JSON);
  47 + httpHeaders.set("Authorization", midjourneyConfig.getToken());
  48 + httpHeaders.set("User-Agent", midjourneyConfig.getUserAage());
  49 + httpHeaders.set("Cookie", MidjourneyConstants.HTTP_COOKIE);
  50 + httpHeaders.set("Referer", String.format(HEADER_REFERER, midjourneyConfig.getGuildId(), midjourneyConfig.getChannelId()));
  51 + return httpHeaders;
  52 + }
  53 +
  54 + /**
  55 + * 获取headers - http form data
  56 + *
  57 + * @return
  58 + */
  59 + protected HttpHeaders getHeadersOfFormData() {
  60 + // 设置header值
  61 + HttpHeaders httpHeaders = new HttpHeaders();
  62 + httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
  63 + httpHeaders.set("Authorization", midjourneyConfig.getToken());
  64 + httpHeaders.set("User-Agent", midjourneyConfig.getUserAage());
  65 + httpHeaders.set("Cookie", MidjourneyConstants.HTTP_COOKIE);
  66 + httpHeaders.set("Referer", String.format(HEADER_REFERER, midjourneyConfig.getGuildId(), midjourneyConfig.getChannelId()));
  67 + return httpHeaders;
  68 + }
  69 +
  70 + /**
  71 + * 获取 - 默认参数
  72 + * @return
  73 + */
  74 + protected HashMap<String, String> getDefaultParams() {
  75 + HashMap<String, String> requestParams = Maps.newHashMap();
  76 + // TODO @fansili:感觉参数的组装,可以搞成一个公用的方法;就是 config + 入参的感觉;
  77 + requestParams.put("guild_id", midjourneyConfig.getGuildId());
  78 + requestParams.put("channel_id", midjourneyConfig.getChannelId());
  79 + requestParams.put("session_id", midjourneyConfig.getSessionId());
  80 + requestParams.put("nonce", String.valueOf(IdUtil.getSnowflakeNextId())); // TODO @fansili:建议用 uuid 之类的;nextId 跨进程未必合适哈;
  81 + return requestParams;
  82 + }
  83 +}
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/interactions/MjInteractions.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/api/MidjourneyInteractionsApi.java
1 -package cn.iocoder.yudao.framework.ai.midjourney.interactions; 1 +package cn.iocoder.yudao.framework.ai.midjourney.api;
2 2
3 -import cn.hutool.core.util.IdUtil;  
4 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig; 4 import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig;
6 -import cn.iocoder.yudao.framework.ai.midjourney.constants.MjConstants;  
7 -import cn.iocoder.yudao.framework.ai.midjourney.util.MjUtil;  
8 -import cn.iocoder.yudao.framework.ai.midjourney.vo.Attachments;  
9 -import cn.iocoder.yudao.framework.ai.midjourney.vo.Describe;  
10 -import cn.iocoder.yudao.framework.ai.midjourney.vo.ReRoll;  
11 -import cn.iocoder.yudao.framework.ai.midjourney.vo.UploadAttachmentsRes; 5 +import cn.iocoder.yudao.framework.ai.midjourney.util.MidjourneyUtil;
  6 +import cn.iocoder.yudao.framework.ai.midjourney.api.req.AttachmentsReq;
  7 +import cn.iocoder.yudao.framework.ai.midjourney.api.req.DescribeReq;
  8 +import cn.iocoder.yudao.framework.ai.midjourney.api.req.ReRollReq;
  9 +import cn.iocoder.yudao.framework.ai.midjourney.api.res.UploadAttachmentsRes;
12 import com.alibaba.fastjson.JSON; 10 import com.alibaba.fastjson.JSON;
13 import com.alibaba.fastjson.JSONObject; 11 import com.alibaba.fastjson.JSONObject;
14 import com.google.common.collect.Lists; 12 import com.google.common.collect.Lists;
15 -import com.google.common.collect.Maps;  
16 import lombok.extern.slf4j.Slf4j; 13 import lombok.extern.slf4j.Slf4j;
17 -import org.jetbrains.annotations.NotNull;  
18 import org.springframework.core.io.FileSystemResource; 14 import org.springframework.core.io.FileSystemResource;
19 import org.springframework.http.*; 15 import org.springframework.http.*;
20 import org.springframework.util.LinkedMultiValueMap; 16 import org.springframework.util.LinkedMultiValueMap;
@@ -24,6 +20,7 @@ import org.springframework.web.client.RestTemplate; @@ -24,6 +20,7 @@ import org.springframework.web.client.RestTemplate;
24 import java.io.IOException; 20 import java.io.IOException;
25 import java.util.HashMap; 21 import java.util.HashMap;
26 22
  23 +// TODO @fansili:按照 spring ai 的封装习惯,这个类是不是 MidjourneyApi
27 /** 24 /**
28 * 图片生成 25 * 图片生成
29 * 26 *
@@ -31,17 +28,13 @@ import java.util.HashMap; @@ -31,17 +28,13 @@ import java.util.HashMap;
31 * time: 2024/4/3 17:36 28 * time: 2024/4/3 17:36
32 */ 29 */
33 @Slf4j 30 @Slf4j
34 -public class MjInteractions {  
35 - 31 +public class MidjourneyInteractionsApi extends MidjourneyInteractions {
36 32
37 private final String url; 33 private final String url;
38 - private final MidjourneyConfig midjourneyConfig;  
39 - private final RestTemplate restTemplate = new RestTemplate();  
40 - private static final String HEADER_REFERER = "https://discord.com/channels/%s/%s";  
41 - 34 + private final RestTemplate restTemplate = new RestTemplate(); // TODO @fansili:优先级低:后续搞到统一的管理
42 35
43 - public MjInteractions(MidjourneyConfig midjourneyConfig) {  
44 - this.midjourneyConfig = midjourneyConfig; 36 + public MidjourneyInteractionsApi(MidjourneyConfig midjourneyConfig) {
  37 + super(midjourneyConfig);
45 this.url = midjourneyConfig.getServerUrl().concat(midjourneyConfig.getApiInteractions()); 38 this.url = midjourneyConfig.getServerUrl().concat(midjourneyConfig.getApiInteractions());
46 } 39 }
47 40
@@ -49,45 +42,38 @@ public class MjInteractions { @@ -49,45 +42,38 @@ public class MjInteractions {
49 // 获取请求模板 42 // 获取请求模板
50 String requestTemplate = midjourneyConfig.getRequestTemplates().get("imagine"); 43 String requestTemplate = midjourneyConfig.getRequestTemplates().get("imagine");
51 // 设置参数 44 // 设置参数
52 - HashMap<String, String> requestParams = Maps.newHashMap();  
53 - requestParams.put("guild_id", midjourneyConfig.getGuildId());  
54 - requestParams.put("channel_id", midjourneyConfig.getChannelId());  
55 - requestParams.put("session_id", midjourneyConfig.getSessionId());  
56 - requestParams.put("nonce", String.valueOf(IdUtil.getSnowflakeNextId())); 45 + HashMap<String, String> requestParams = getDefaultParams();
57 requestParams.put("prompt", prompt); 46 requestParams.put("prompt", prompt);
58 // 解析 template 参数占位符 47 // 解析 template 参数占位符
59 - String requestBody = MjUtil.parseTemplate(requestTemplate, requestParams); 48 + String requestBody = MidjourneyUtil.parseTemplate(requestTemplate, requestParams);
60 // 获取 header 49 // 获取 header
61 - HttpHeaders httpHeaders = getHttpHeaders(); 50 + HttpHeaders httpHeaders = getHeadersOfAppJson();
62 // 发送请求 51 // 发送请求
63 HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, httpHeaders); 52 HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, httpHeaders);
64 String res = restTemplate.postForObject(url, requestEntity, String.class); 53 String res = restTemplate.postForObject(url, requestEntity, String.class);
65 // 这个 res 只要不返回值,就是成功! 54 // 这个 res 只要不返回值,就是成功!
66 - boolean isSuccess = StrUtil.isBlank(res);  
67 - if (isSuccess) { 55 + // TODO @fansili:可以直接 if (StrUtil.isBlank(res))
  56 + if (StrUtil.isBlank(res)) {
68 return true; 57 return true;
  58 + } else {
  59 + log.error("请求失败! 请求参数:{} 返回结果! {}", requestBody, res);
  60 + return false;
69 } 61 }
70 - log.error("请求失败! 请求参数:{} 返回结果! {}", requestBody, res);  
71 - return isSuccess;  
72 } 62 }
  63 + // TODO done @fansili:方法和方法之间,空一行哈;
73 64
74 65
75 -  
76 - public Boolean reRoll(ReRoll reRoll) { 66 + public Boolean reRoll(ReRollReq reRoll) {
77 // 获取请求模板 67 // 获取请求模板
78 String requestTemplate = midjourneyConfig.getRequestTemplates().get("reroll"); 68 String requestTemplate = midjourneyConfig.getRequestTemplates().get("reroll");
79 // 设置参数 69 // 设置参数
80 - HashMap<String, String> requestParams = Maps.newHashMap();  
81 - requestParams.put("guild_id", midjourneyConfig.getGuildId());  
82 - requestParams.put("channel_id", midjourneyConfig.getChannelId());  
83 - requestParams.put("session_id", midjourneyConfig.getSessionId());  
84 - requestParams.put("nonce", String.valueOf(IdUtil.getSnowflakeNextId())); 70 + HashMap<String, String> requestParams = getDefaultParams();
85 requestParams.put("custom_id", reRoll.getCustomId()); 71 requestParams.put("custom_id", reRoll.getCustomId());
86 requestParams.put("message_id", reRoll.getMessageId()); 72 requestParams.put("message_id", reRoll.getMessageId());
87 // 获取 header 73 // 获取 header
88 - HttpHeaders httpHeaders = getHttpHeaders(); 74 + HttpHeaders httpHeaders = getHeadersOfAppJson();
89 // 设置参数 75 // 设置参数
90 - String requestBody = MjUtil.parseTemplate(requestTemplate, requestParams); 76 + String requestBody = MidjourneyUtil.parseTemplate(requestTemplate, requestParams);
91 // 发送请求 77 // 发送请求
92 HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, httpHeaders); 78 HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, httpHeaders);
93 String res = restTemplate.postForObject(url, requestEntity, String.class); 79 String res = restTemplate.postForObject(url, requestEntity, String.class);
@@ -100,12 +86,13 @@ public class MjInteractions { @@ -100,12 +86,13 @@ public class MjInteractions {
100 return isSuccess; 86 return isSuccess;
101 } 87 }
102 88
103 -  
104 - public UploadAttachmentsRes uploadAttachments(Attachments attachments) { 89 + // TODO @fansili:搞成私有方法,可能会好点;
  90 + public UploadAttachmentsRes uploadAttachments(AttachmentsReq attachments) {
105 // file 91 // file
106 JSONObject fileObj = new JSONObject(); 92 JSONObject fileObj = new JSONObject();
107 fileObj.put("id", "0"); 93 fileObj.put("id", "0");
108 fileObj.put("filename", attachments.getFileSystemResource().getFilename()); 94 fileObj.put("filename", attachments.getFileSystemResource().getFilename());
  95 + // TODO @fansili:这块用 lombok 哪个异常处理,简化下代码;
109 try { 96 try {
110 fileObj.put("file_size", attachments.getFileSystemResource().contentLength()); 97 fileObj.put("file_size", attachments.getFileSystemResource().contentLength());
111 } catch (IOException e) { 98 } catch (IOException e) {
@@ -115,12 +102,7 @@ public class MjInteractions { @@ -115,12 +102,7 @@ public class MjInteractions {
115 MultiValueMap<String, Object> multipartRequest = new LinkedMultiValueMap<>(); 102 MultiValueMap<String, Object> multipartRequest = new LinkedMultiValueMap<>();
116 multipartRequest.put("files", Lists.newArrayList(fileObj)); 103 multipartRequest.put("files", Lists.newArrayList(fileObj));
117 // 设置header值 104 // 设置header值
118 - HttpHeaders httpHeaders = new HttpHeaders();  
119 - httpHeaders.setContentType(MediaType.APPLICATION_JSON);  
120 - httpHeaders.set("Authorization", midjourneyConfig.getToken());  
121 - httpHeaders.set("User-Agent", midjourneyConfig.getUserAage());  
122 - httpHeaders.set("Cookie", MjConstants.HTTP_COOKIE);  
123 - httpHeaders.set("Referer", String.format(HEADER_REFERER, midjourneyConfig.getGuildId(), midjourneyConfig.getChannelId())); 105 + HttpHeaders httpHeaders = getHeadersOfAppJson();
124 // 创建HttpEntity对象,包含表单数据和头部信息 106 // 创建HttpEntity对象,包含表单数据和头部信息
125 HttpEntity<MultiValueMap<String, Object>> multiValueMapHttpEntity = new HttpEntity<>(multipartRequest, httpHeaders); 107 HttpEntity<MultiValueMap<String, Object>> multiValueMapHttpEntity = new HttpEntity<>(multipartRequest, httpHeaders);
126 // 发送POST请求并接收响应 108 // 发送POST请求并接收响应
@@ -128,38 +110,26 @@ public class MjInteractions { @@ -128,38 +110,26 @@ public class MjInteractions {
128 String response = restTemplate.postForObject(midjourneyConfig.getServerUrl().concat(uri), multiValueMapHttpEntity, String.class); 110 String response = restTemplate.postForObject(midjourneyConfig.getServerUrl().concat(uri), multiValueMapHttpEntity, String.class);
129 UploadAttachmentsRes uploadAttachmentsRes = JSON.parseObject(response, UploadAttachmentsRes.class); 111 UploadAttachmentsRes uploadAttachmentsRes = JSON.parseObject(response, UploadAttachmentsRes.class);
130 112
131 -  
132 // 113 //
133 // 上传文件 114 // 上传文件
134 String uploadUrl = uploadAttachmentsRes.getAttachments().getFirst().getUploadUrl(); 115 String uploadUrl = uploadAttachmentsRes.getAttachments().getFirst().getUploadUrl();
135 - String uploadAttachmentsUrl = midjourneyConfig.getApiAttachmentsUpload().concat(uploadUrl);  
136 httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA); 116 httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
137 HttpEntity<FileSystemResource> fileSystemResourceHttpEntity = new HttpEntity<>(attachments.getFileSystemResource(), httpHeaders); 117 HttpEntity<FileSystemResource> fileSystemResourceHttpEntity = new HttpEntity<>(attachments.getFileSystemResource(), httpHeaders);
138 ResponseEntity<String> exchange = restTemplate.exchange(uploadUrl, HttpMethod.PUT, fileSystemResourceHttpEntity, String.class); 118 ResponseEntity<String> exchange = restTemplate.exchange(uploadUrl, HttpMethod.PUT, fileSystemResourceHttpEntity, String.class);
139 String uploadRes = exchange.getBody(); 119 String uploadRes = exchange.getBody();
140 -  
141 return uploadAttachmentsRes; 120 return uploadAttachmentsRes;
142 } 121 }
143 122
144 - public Boolean describe(Describe describe) { 123 + public Boolean describe(DescribeReq describe) {
145 // 获取请求模板 124 // 获取请求模板
146 String requestTemplate = midjourneyConfig.getRequestTemplates().get("describe"); 125 String requestTemplate = midjourneyConfig.getRequestTemplates().get("describe");
147 // 设置参数 126 // 设置参数
148 - HashMap<String, String> requestParams = Maps.newHashMap();  
149 - requestParams.put("guild_id", midjourneyConfig.getGuildId());  
150 - requestParams.put("channel_id", midjourneyConfig.getChannelId());  
151 - requestParams.put("session_id", midjourneyConfig.getSessionId());  
152 - requestParams.put("nonce", String.valueOf(IdUtil.getSnowflakeNextId())); 127 + HashMap<String, String> requestParams = getDefaultParams();
153 requestParams.put("file_name", describe.getFileName()); 128 requestParams.put("file_name", describe.getFileName());
154 requestParams.put("final_file_name", describe.getFinalFileName()); 129 requestParams.put("final_file_name", describe.getFinalFileName());
155 // 设置 header 130 // 设置 header
156 - HttpHeaders httpHeaders = new HttpHeaders();  
157 - httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA); // 设置内容类型为JSON  
158 - httpHeaders.set("Authorization", midjourneyConfig.getToken());  
159 - httpHeaders.set("User-Agent", midjourneyConfig.getUserAage());  
160 - httpHeaders.set("Cookie", MjConstants.HTTP_COOKIE);  
161 - httpHeaders.set("Referer", String.format(HEADER_REFERER, midjourneyConfig.getGuildId(), midjourneyConfig.getChannelId()));  
162 - String requestBody = MjUtil.parseTemplate(requestTemplate, requestParams); 131 + HttpHeaders httpHeaders = getHeadersOfFormData();
  132 + String requestBody = MidjourneyUtil.parseTemplate(requestTemplate, requestParams);
163 // 创建表单数据 133 // 创建表单数据
164 MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); 134 MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
165 formData.add("payload_json", requestBody); 135 formData.add("payload_json", requestBody);
@@ -175,14 +145,4 @@ public class MjInteractions { @@ -175,14 +145,4 @@ public class MjInteractions {
175 return isSuccess; 145 return isSuccess;
176 } 146 }
177 147
178 - @NotNull  
179 - private HttpHeaders getHttpHeaders() {  
180 - HttpHeaders httpHeaders = new HttpHeaders();  
181 - httpHeaders.setContentType(MediaType.APPLICATION_JSON); // 设置内容类型为JSON  
182 - httpHeaders.set("Authorization", midjourneyConfig.getToken());  
183 - httpHeaders.set("User-Agent", midjourneyConfig.getUserAage());  
184 - httpHeaders.set("Cookie", MjConstants.HTTP_COOKIE);  
185 - httpHeaders.set("Referer", String.format(HEADER_REFERER, midjourneyConfig.getGuildId(), midjourneyConfig.getChannelId()));  
186 - return httpHeaders;  
187 - }  
188 } 148 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/vo/Attachments.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/api/req/AttachmentsReq.java
1 -package cn.iocoder.yudao.framework.ai.midjourney.vo; 1 +package cn.iocoder.yudao.framework.ai.midjourney.api.req;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 import lombok.experimental.Accessors; 4 import lombok.experimental.Accessors;
@@ -12,7 +12,7 @@ import org.springframework.core.io.FileSystemResource; @@ -12,7 +12,7 @@ import org.springframework.core.io.FileSystemResource;
12 */ 12 */
13 @Data 13 @Data
14 @Accessors(chain = true) 14 @Accessors(chain = true)
15 -public class Attachments { 15 +public class AttachmentsReq {
16 16
17 /** 17 /**
18 * 创建文件系统资源对象 18 * 创建文件系统资源对象
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/vo/Describe.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/api/req/DescribeReq.java
1 -package cn.iocoder.yudao.framework.ai.midjourney.vo; 1 +package cn.iocoder.yudao.framework.ai.midjourney.api.req;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 import lombok.experimental.Accessors; 4 import lombok.experimental.Accessors;
5 5
6 -import java.io.File;  
7 -  
8 /** 6 /**
9 * describe 7 * describe
10 * 8 *
@@ -13,7 +11,7 @@ import java.io.File; @@ -13,7 +11,7 @@ import java.io.File;
13 */ 11 */
14 @Data 12 @Data
15 @Accessors(chain = true) 13 @Accessors(chain = true)
16 -public class Describe { 14 +public class DescribeReq {
17 15
18 /** 16 /**
19 * 文件名字 17 * 文件名字
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/vo/ReRoll.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/api/req/ReRollReq.java
1 -package cn.iocoder.yudao.framework.ai.midjourney.vo; 1 +package cn.iocoder.yudao.framework.ai.midjourney.api.req;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 import lombok.experimental.Accessors; 4 import lombok.experimental.Accessors;
@@ -9,7 +9,7 @@ import lombok.experimental.Accessors; @@ -9,7 +9,7 @@ import lombok.experimental.Accessors;
9 */ 9 */
10 @Data 10 @Data
11 @Accessors(chain = true) 11 @Accessors(chain = true)
12 -public class ReRoll { 12 +public class ReRollReq {
13 13
14 /** 14 /**
15 * socket 消息里面收到的 messageId 15 * socket 消息里面收到的 messageId
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/vo/UploadAttachmentsRes.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/api/res/UploadAttachmentsRes.java
1 -package cn.iocoder.yudao.framework.ai.midjourney.vo; 1 +package cn.iocoder.yudao.framework.ai.midjourney.api.res;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 import lombok.experimental.Accessors; 4 import lombok.experimental.Accessors;
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjConstants.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MidjourneyConstants.java
1 package cn.iocoder.yudao.framework.ai.midjourney.constants; 1 package cn.iocoder.yudao.framework.ai.midjourney.constants;
2 2
3 -public final class MjConstants { 3 +public final class MidjourneyConstants {
4 4
5 /** 5 /**
6 * 消息 - 编号 6 * 消息 - 编号
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjGennerateStatusEnum.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MidjourneyGennerateStatusEnum.java
1 package cn.iocoder.yudao.framework.ai.midjourney.constants; 1 package cn.iocoder.yudao.framework.ai.midjourney.constants;
2 2
  3 +import lombok.AllArgsConstructor;
3 import lombok.Getter; 4 import lombok.Getter;
4 5
  6 +// TODO done @fansili:1)Mj 缩写,还是搞成全称。。虽然长一点,但是感觉会相对清晰一些哈;2)lombok 相关的注解,可以用用哈;3)value 改 status;
5 /** 7 /**
6 * mj 生成状态 8 * mj 生成状态
7 * 9 *
@@ -9,8 +11,8 @@ import lombok.Getter; @@ -9,8 +11,8 @@ import lombok.Getter;
9 * time: 2024/4/6 21:07 11 * time: 2024/4/6 21:07
10 */ 12 */
11 @Getter 13 @Getter
12 -public enum MjGennerateStatusEnum {  
13 - 14 +@AllArgsConstructor
  15 +public enum MidjourneyGennerateStatusEnum {
14 16
15 WAITING("waiting", "等待..."), 17 WAITING("waiting", "等待..."),
16 IN_PROGRESS("in_progress", "进行中"), 18 IN_PROGRESS("in_progress", "进行中"),
@@ -18,12 +20,12 @@ public enum MjGennerateStatusEnum { @@ -18,12 +20,12 @@ public enum MjGennerateStatusEnum {
18 20
19 ; 21 ;
20 22
21 - MjGennerateStatusEnum(String value, String message) {  
22 - this.value = value;  
23 - this.message = message;  
24 - }  
25 -  
26 - private String value;  
27 - 23 + /**
  24 + * 状态
  25 + */
  26 + private String status;
  27 + /**
  28 + * 状态信息
  29 + */
28 private String message; 30 private String message;
29 } 31 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjInteractionsEnum.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MidjourneyInteractionsEnum.java
@@ -6,7 +6,7 @@ import lombok.Getter; @@ -6,7 +6,7 @@ import lombok.Getter;
6 * MJ 命令 6 * MJ 命令
7 */ 7 */
8 @Getter 8 @Getter
9 -public enum MjInteractionsEnum { 9 +public enum MidjourneyInteractionsEnum {
10 10
11 IMAGINE("imagine", "生成图片"), 11 IMAGINE("imagine", "生成图片"),
12 DESCRIBE("describe", "生成描述"), 12 DESCRIBE("describe", "生成描述"),
@@ -17,7 +17,7 @@ public enum MjInteractionsEnum { @@ -17,7 +17,7 @@ public enum MjInteractionsEnum {
17 17
18 ; 18 ;
19 19
20 - MjInteractionsEnum(String value, String message) { 20 + MidjourneyInteractionsEnum(String value, String message) {
21 this.value =value; 21 this.value =value;
22 this.message =message; 22 this.message =message;
23 } 23 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjMessageTypeEnum.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MidjourneyMessageTypeEnum.java
1 package cn.iocoder.yudao.framework.ai.midjourney.constants; 1 package cn.iocoder.yudao.framework.ai.midjourney.constants;
2 2
3 3
4 -public enum MjMessageTypeEnum { 4 +public enum MidjourneyMessageTypeEnum {
5 /** 5 /**
6 * 创建. 6 * 创建.
7 */ 7 */
@@ -15,7 +15,7 @@ public enum MjMessageTypeEnum { @@ -15,7 +15,7 @@ public enum MjMessageTypeEnum {
15 */ 15 */
16 DELETE; 16 DELETE;
17 17
18 - public static MjMessageTypeEnum of(String type) { 18 + public static MidjourneyMessageTypeEnum of(String type) {
19 return switch (type) { 19 return switch (type) {
20 case "MESSAGE_CREATE" -> CREATE; 20 case "MESSAGE_CREATE" -> CREATE;
21 case "MESSAGE_UPDATE" -> UPDATE; 21 case "MESSAGE_UPDATE" -> UPDATE;
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjNotifyCode.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MidjourneyNotifyCode.java
@@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.ai.midjourney.constants; @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.ai.midjourney.constants;
3 import lombok.experimental.UtilityClass; 3 import lombok.experimental.UtilityClass;
4 4
5 @UtilityClass 5 @UtilityClass
6 -public final class MjNotifyCode { 6 +public final class MidjourneyNotifyCode {
7 /** 7 /**
8 * 成功. 8 * 成功.
9 */ 9 */
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/util/MjUtil.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/util/MidjourneyUtil.java
1 package cn.iocoder.yudao.framework.ai.midjourney.util; 1 package cn.iocoder.yudao.framework.ai.midjourney.util;
2 2
3 import cn.hutool.core.text.CharSequenceUtil; 3 import cn.hutool.core.text.CharSequenceUtil;
4 -import cn.iocoder.yudao.framework.ai.midjourney.MjMessage; 4 +import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyMessage;
5 5
6 import java.util.Map; 6 import java.util.Map;
7 import java.util.regex.Matcher; 7 import java.util.regex.Matcher;
@@ -13,7 +13,7 @@ import java.util.regex.Pattern; @@ -13,7 +13,7 @@ import java.util.regex.Pattern;
13 * author: fansili 13 * author: fansili
14 * time: 2024/4/6 19:00 14 * time: 2024/4/6 19:00
15 */ 15 */
16 -public class MjUtil { 16 +public class MidjourneyUtil {
17 /** 17 /**
18 * content正则匹配prompt和进度. 18 * content正则匹配prompt和进度.
19 */ 19 */
@@ -26,12 +26,12 @@ public class MjUtil { @@ -26,12 +26,12 @@ public class MjUtil {
26 * @param content 26 * @param content
27 * @return 27 * @return
28 */ 28 */
29 - public static MjMessage.Content parseContent(String content) { 29 + public static MidjourneyMessage.Content parseContent(String content) {
30 // 有三种格式。 30 // 有三种格式。
31 // 南极应该是什么样子? 31 // 南极应该是什么样子?
32 // "**南极应该是什么样子? --v 6.0 --style raw** - <@972721304891453450> (32%) (fast, stealth)", 32 // "**南极应该是什么样子? --v 6.0 --style raw** - <@972721304891453450> (32%) (fast, stealth)",
33 // "**南极应该是什么样子? --v 6.0 --style raw** - <@972721304891453450> (fast, stealth)" 33 // "**南极应该是什么样子? --v 6.0 --style raw** - <@972721304891453450> (fast, stealth)"
34 - MjMessage.Content mjContent = new MjMessage.Content(); 34 + MidjourneyMessage.Content mjContent = new MidjourneyMessage.Content();
35 if (CharSequenceUtil.isBlank(content)) { 35 if (CharSequenceUtil.isBlank(content)) {
36 return null; 36 return null;
37 } 37 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/MjWebSocketStarter.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/MidjourneyWebSocketStarter.java
@@ -4,9 +4,9 @@ package cn.iocoder.yudao.framework.ai.midjourney.webSocket; @@ -4,9 +4,9 @@ package cn.iocoder.yudao.framework.ai.midjourney.webSocket;
4 import cn.hutool.core.text.CharSequenceUtil; 4 import cn.hutool.core.text.CharSequenceUtil;
5 import cn.hutool.core.thread.ThreadUtil; 5 import cn.hutool.core.thread.ThreadUtil;
6 import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig; 6 import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig;
7 -import cn.iocoder.yudao.framework.ai.midjourney.constants.MjNotifyCode;  
8 -import cn.iocoder.yudao.framework.ai.midjourney.webSocket.handler.MjWebSocketHandler;  
9 -import cn.iocoder.yudao.framework.ai.midjourney.webSocket.listener.MjMessageListener; 7 +import cn.iocoder.yudao.framework.ai.midjourney.constants.MidjourneyNotifyCode;
  8 +import cn.iocoder.yudao.framework.ai.midjourney.webSocket.handler.MidjourneyWebSocketHandler;
  9 +import cn.iocoder.yudao.framework.ai.midjourney.webSocket.listener.MidjourneyMessageListener;
10 import lombok.Getter; 10 import lombok.Getter;
11 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
12 import org.apache.tomcat.websocket.Constants; 12 import org.apache.tomcat.websocket.Constants;
@@ -20,10 +20,10 @@ import org.springframework.web.socket.client.standard.StandardWebSocketClient; @@ -20,10 +20,10 @@ import org.springframework.web.socket.client.standard.StandardWebSocketClient;
20 20
21 import java.io.IOException; 21 import java.io.IOException;
22 import java.net.URI; 22 import java.net.URI;
23 -import java.util.concurrent.TimeoutException;  
24 23
  24 +// TODO @fansili:mj 这块 websocket 有点小复杂,虽然代码量 400 多行;感觉可以考虑,有没第三方 sdk,通过它透明接入 mj
25 @Slf4j 25 @Slf4j
26 -public class MjWebSocketStarter implements WebSocketStarter { 26 +public class MidjourneyWebSocketStarter implements WebSocketStarter {
27 /** 27 /**
28 * 链接重试次数 28 * 链接重试次数
29 */ 29 */
@@ -35,7 +35,7 @@ public class MjWebSocketStarter implements WebSocketStarter { @@ -35,7 +35,7 @@ public class MjWebSocketStarter implements WebSocketStarter {
35 /** 35 /**
36 * mj 监听(所有message 都会 callback到这里) 36 * mj 监听(所有message 都会 callback到这里)
37 */ 37 */
38 - private final MjMessageListener userMessageListener; 38 + private final MidjourneyMessageListener userMessageListener;
39 /** 39 /**
40 * wss 服务器 40 * wss 服务器
41 */ 41 */
@@ -57,10 +57,10 @@ public class MjWebSocketStarter implements WebSocketStarter { @@ -57,10 +57,10 @@ public class MjWebSocketStarter implements WebSocketStarter {
57 */ 57 */
58 private WebSocketSession webSocketSession = null; 58 private WebSocketSession webSocketSession = null;
59 59
60 - public MjWebSocketStarter(String wssServer,  
61 - String resumeWss,  
62 - MidjourneyConfig midjourneyConfig,  
63 - MjMessageListener userMessageListener) { 60 + public MidjourneyWebSocketStarter(String wssServer,
  61 + String resumeWss,
  62 + MidjourneyConfig midjourneyConfig,
  63 + MidjourneyMessageListener userMessageListener) {
64 this.wssServer = wssServer; 64 this.wssServer = wssServer;
65 this.resumeWss = resumeWss; 65 this.resumeWss = resumeWss;
66 this.midjourneyConfig = midjourneyConfig; 66 this.midjourneyConfig = midjourneyConfig;
@@ -82,7 +82,7 @@ public class MjWebSocketStarter implements WebSocketStarter { @@ -82,7 +82,7 @@ public class MjWebSocketStarter implements WebSocketStarter {
82 headers.add("Sec-Websocket-Extensions", "permessage-deflate; client_max_window_bits"); 82 headers.add("Sec-Websocket-Extensions", "permessage-deflate; client_max_window_bits");
83 headers.add("User-Agent", this.midjourneyConfig.getUserAage()); 83 headers.add("User-Agent", this.midjourneyConfig.getUserAage());
84 // 创建 mjHeader 84 // 创建 mjHeader
85 - MjWebSocketHandler mjWebSocketHandler = new MjWebSocketHandler( 85 + MidjourneyWebSocketHandler mjWebSocketHandler = new MidjourneyWebSocketHandler(
86 this.midjourneyConfig, this.userMessageListener, this::onSocketSuccess, this::onSocketFailure); 86 this.midjourneyConfig, this.userMessageListener, this::onSocketSuccess, this::onSocketFailure);
87 // 87 //
88 String gatewayUrl; 88 String gatewayUrl;
@@ -104,12 +104,12 @@ public class MjWebSocketStarter implements WebSocketStarter { @@ -104,12 +104,12 @@ public class MjWebSocketStarter implements WebSocketStarter {
104 socketSessionFuture.addCallback(new ListenableFutureCallback<>() { 104 socketSessionFuture.addCallback(new ListenableFutureCallback<>() {
105 @Override 105 @Override
106 public void onFailure(@NotNull Throwable e) { 106 public void onFailure(@NotNull Throwable e) {
107 - onSocketFailure(MjWebSocketHandler.CLOSE_CODE_EXCEPTION, e.getMessage()); 107 + onSocketFailure(MidjourneyWebSocketHandler.CLOSE_CODE_EXCEPTION, e.getMessage());
108 } 108 }
109 109
110 @Override 110 @Override
111 public void onSuccess(WebSocketSession session) { 111 public void onSuccess(WebSocketSession session) {
112 - MjWebSocketStarter.this.webSocketSession = session; 112 + MidjourneyWebSocketStarter.this.webSocketSession = session;
113 } 113 }
114 }); 114 });
115 } 115 }
@@ -117,7 +117,7 @@ public class MjWebSocketStarter implements WebSocketStarter { @@ -117,7 +117,7 @@ public class MjWebSocketStarter implements WebSocketStarter {
117 private void onSocketSuccess(String sessionId, Object sequence, String resumeGatewayUrl) { 117 private void onSocketSuccess(String sessionId, Object sequence, String resumeGatewayUrl) {
118 this.resumeData = new ResumeData(sessionId, sequence, resumeGatewayUrl); 118 this.resumeData = new ResumeData(sessionId, sequence, resumeGatewayUrl);
119 this.running = true; 119 this.running = true;
120 - notifyWssLock(MjNotifyCode.SUCCESS, ""); 120 + notifyWssLock(MidjourneyNotifyCode.SUCCESS, "");
121 } 121 }
122 122
123 private void onSocketFailure(int code, String reason) { 123 private void onSocketFailure(int code, String reason) {
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/handler/MjWebSocketHandler.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/handler/MidjourneyWebSocketHandler.java
@@ -8,7 +8,7 @@ import cn.hutool.http.useragent.UserAgentUtil; @@ -8,7 +8,7 @@ import cn.hutool.http.useragent.UserAgentUtil;
8 import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig; 8 import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig;
9 import cn.iocoder.yudao.framework.ai.midjourney.webSocket.FailureCallback; 9 import cn.iocoder.yudao.framework.ai.midjourney.webSocket.FailureCallback;
10 import cn.iocoder.yudao.framework.ai.midjourney.webSocket.SuccessCallback; 10 import cn.iocoder.yudao.framework.ai.midjourney.webSocket.SuccessCallback;
11 -import cn.iocoder.yudao.framework.ai.midjourney.webSocket.listener.MjMessageListener; 11 +import cn.iocoder.yudao.framework.ai.midjourney.webSocket.listener.MidjourneyMessageListener;
12 import lombok.Setter; 12 import lombok.Setter;
13 import lombok.extern.slf4j.Slf4j; 13 import lombok.extern.slf4j.Slf4j;
14 import net.dv8tion.jda.api.utils.data.DataArray; 14 import net.dv8tion.jda.api.utils.data.DataArray;
@@ -29,7 +29,7 @@ import java.util.concurrent.ScheduledExecutorService; @@ -29,7 +29,7 @@ import java.util.concurrent.ScheduledExecutorService;
29 import java.util.concurrent.TimeUnit; 29 import java.util.concurrent.TimeUnit;
30 30
31 @Slf4j 31 @Slf4j
32 -public class MjWebSocketHandler implements WebSocketHandler { 32 +public class MidjourneyWebSocketHandler implements WebSocketHandler {
33 /** 33 /**
34 * close 错误码:重连 34 * close 错误码:重连
35 */ 35 */
@@ -49,7 +49,7 @@ public class MjWebSocketHandler implements WebSocketHandler { @@ -49,7 +49,7 @@ public class MjWebSocketHandler implements WebSocketHandler {
49 /** 49 /**
50 * mj 消息监听 50 * mj 消息监听
51 */ 51 */
52 - private final MjMessageListener userMessageListener; 52 + private final MidjourneyMessageListener userMessageListener;
53 /** 53 /**
54 * 成功回调 54 * 成功回调
55 */ 55 */
@@ -85,10 +85,10 @@ public class MjWebSocketHandler implements WebSocketHandler { @@ -85,10 +85,10 @@ public class MjWebSocketHandler implements WebSocketHandler {
85 */ 85 */
86 private final Decompressor decompressor = new ZlibDecompressor(2048); 86 private final Decompressor decompressor = new ZlibDecompressor(2048);
87 87
88 - public MjWebSocketHandler(MidjourneyConfig account,  
89 - MjMessageListener userMessageListener,  
90 - SuccessCallback successCallback,  
91 - FailureCallback failureCallback) { 88 + public MidjourneyWebSocketHandler(MidjourneyConfig account,
  89 + MidjourneyMessageListener userMessageListener,
  90 + SuccessCallback successCallback,
  91 + FailureCallback failureCallback) {
92 this.midjourneyConfig = account; 92 this.midjourneyConfig = account;
93 this.userMessageListener = userMessageListener; 93 this.userMessageListener = userMessageListener;
94 this.successCallback = successCallback; 94 this.successCallback = successCallback;
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/listener/MjMessageListener.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/listener/MidjourneyMessageListener.java
@@ -6,11 +6,11 @@ import cn.hutool.core.text.CharSequenceUtil; @@ -6,11 +6,11 @@ import cn.hutool.core.text.CharSequenceUtil;
6 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
7 import cn.hutool.json.JSONUtil; 7 import cn.hutool.json.JSONUtil;
8 import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig; 8 import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig;
9 -import cn.iocoder.yudao.framework.ai.midjourney.MjMessage;  
10 -import cn.iocoder.yudao.framework.ai.midjourney.constants.MjConstants;  
11 -import cn.iocoder.yudao.framework.ai.midjourney.constants.MjGennerateStatusEnum;  
12 -import cn.iocoder.yudao.framework.ai.midjourney.constants.MjMessageTypeEnum;  
13 -import cn.iocoder.yudao.framework.ai.midjourney.util.MjUtil; 9 +import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyMessage;
  10 +import cn.iocoder.yudao.framework.ai.midjourney.constants.MidjourneyConstants;
  11 +import cn.iocoder.yudao.framework.ai.midjourney.constants.MidjourneyGennerateStatusEnum;
  12 +import cn.iocoder.yudao.framework.ai.midjourney.constants.MidjourneyMessageTypeEnum;
  13 +import cn.iocoder.yudao.framework.ai.midjourney.util.MidjourneyUtil;
14 import com.alibaba.fastjson.JSON; 14 import com.alibaba.fastjson.JSON;
15 import lombok.extern.slf4j.Slf4j; 15 import lombok.extern.slf4j.Slf4j;
16 import net.dv8tion.jda.api.utils.data.DataObject; 16 import net.dv8tion.jda.api.utils.data.DataObject;
@@ -18,17 +18,17 @@ import net.dv8tion.jda.api.utils.data.DataObject; @@ -18,17 +18,17 @@ import net.dv8tion.jda.api.utils.data.DataObject;
18 import java.util.List; 18 import java.util.List;
19 19
20 @Slf4j 20 @Slf4j
21 -public class MjMessageListener { 21 +public class MidjourneyMessageListener {
22 22
23 private MidjourneyConfig midjourneyConfig; 23 private MidjourneyConfig midjourneyConfig;
24 24
25 - public MjMessageListener(MidjourneyConfig midjourneyConfig) { 25 + public MidjourneyMessageListener(MidjourneyConfig midjourneyConfig) {
26 this.midjourneyConfig = midjourneyConfig; 26 this.midjourneyConfig = midjourneyConfig;
27 } 27 }
28 28
29 public void onMessage(DataObject raw) { 29 public void onMessage(DataObject raw) {
30 - MjMessageTypeEnum messageType = MjMessageTypeEnum.of(raw.getString("t"));  
31 - if (messageType == null || MjMessageTypeEnum.DELETE == messageType) { 30 + MidjourneyMessageTypeEnum messageType = MidjourneyMessageTypeEnum.of(raw.getString("t"));
  31 + if (messageType == null || MidjourneyMessageTypeEnum.DELETE == messageType) {
32 return; 32 return;
33 } 33 }
34 DataObject data = raw.getObject("d"); 34 DataObject data = raw.getObject("d");
@@ -37,21 +37,21 @@ public class MjMessageListener { @@ -37,21 +37,21 @@ public class MjMessageListener {
37 } 37 }
38 38
39 // 转换几个重要的信息 39 // 转换几个重要的信息
40 - MjMessage mjMessage = new MjMessage();  
41 - mjMessage.setId(data.getString(MjConstants.MSG_ID));  
42 - mjMessage.setType(data.getInt(MjConstants.MSG_TYPE)); 40 + MidjourneyMessage mjMessage = new MidjourneyMessage();
  41 + mjMessage.setId(data.getString(MidjourneyConstants.MSG_ID));
  42 + mjMessage.setType(data.getInt(MidjourneyConstants.MSG_TYPE));
43 mjMessage.setRawData(StrUtil.str(raw.toJson(), "UTF-8")); 43 mjMessage.setRawData(StrUtil.str(raw.toJson(), "UTF-8"));
44 - mjMessage.setContent(MjUtil.parseContent(data.getString(MjConstants.MSG_CONTENT))); 44 + mjMessage.setContent(MidjourneyUtil.parseContent(data.getString(MidjourneyConstants.MSG_CONTENT)));
45 // 转换 components 45 // 转换 components
46 - if (!data.getArray(MjConstants.MSG_COMPONENTS).isEmpty()) {  
47 - String componentsJson = StrUtil.str(data.getArray(MjConstants.MSG_COMPONENTS).toJson(), "UTF-8");  
48 - List<MjMessage.ComponentType> components = JSON.parseArray(componentsJson, MjMessage.ComponentType.class); 46 + if (!data.getArray(MidjourneyConstants.MSG_COMPONENTS).isEmpty()) {
  47 + String componentsJson = StrUtil.str(data.getArray(MidjourneyConstants.MSG_COMPONENTS).toJson(), "UTF-8");
  48 + List<MidjourneyMessage.ComponentType> components = JSON.parseArray(componentsJson, MidjourneyMessage.ComponentType.class);
49 mjMessage.setComponents(components); 49 mjMessage.setComponents(components);
50 } 50 }
51 // 转换附件 51 // 转换附件
52 - if (!data.getArray(MjConstants.MSG_ATTACHMENTS).isEmpty()) {  
53 - String attachmentsJson = StrUtil.str(data.getArray(MjConstants.MSG_ATTACHMENTS).toJson(), "UTF-8");  
54 - List<MjMessage.Attachment> attachments = JSON.parseArray(attachmentsJson, MjMessage.Attachment.class); 52 + if (!data.getArray(MidjourneyConstants.MSG_ATTACHMENTS).isEmpty()) {
  53 + String attachmentsJson = StrUtil.str(data.getArray(MidjourneyConstants.MSG_ATTACHMENTS).toJson(), "UTF-8");
  54 + List<MidjourneyMessage.Attachment> attachments = JSON.parseArray(attachmentsJson, MidjourneyMessage.Attachment.class);
55 mjMessage.setAttachments(attachments); 55 mjMessage.setAttachments(attachments);
56 } 56 }
57 // 转换状态 57 // 转换状态
@@ -61,18 +61,18 @@ public class MjMessageListener { @@ -61,18 +61,18 @@ public class MjMessageListener {
61 System.err.println(JSONUtil.toJsonPrettyStr(mjMessage)); 61 System.err.println(JSONUtil.toJsonPrettyStr(mjMessage));
62 } 62 }
63 63
64 - private void convertGenerateStatus(MjMessage mjMessage) { 64 + private void convertGenerateStatus(MidjourneyMessage mjMessage) {
65 if (mjMessage.getType() == 20 && mjMessage.getContent().getStatus().contains("Waiting")) { 65 if (mjMessage.getType() == 20 && mjMessage.getContent().getStatus().contains("Waiting")) {
66 - mjMessage.setGenerateStatus(MjGennerateStatusEnum.WAITING.getValue()); 66 + mjMessage.setGenerateStatus(MidjourneyGennerateStatusEnum.WAITING.getStatus());
67 } else if (mjMessage.getType() == 20 && !StrUtil.isBlank(mjMessage.getContent().getProgress())) { 67 } else if (mjMessage.getType() == 20 && !StrUtil.isBlank(mjMessage.getContent().getProgress())) {
68 - mjMessage.setGenerateStatus(MjGennerateStatusEnum.IN_PROGRESS.getValue()); 68 + mjMessage.setGenerateStatus(MidjourneyGennerateStatusEnum.IN_PROGRESS.getStatus());
69 } else if (mjMessage.getType() == 0 && !CollUtil.isEmpty(mjMessage.getComponents())) { 69 } else if (mjMessage.getType() == 0 && !CollUtil.isEmpty(mjMessage.getComponents())) {
70 - mjMessage.setGenerateStatus(MjGennerateStatusEnum.COMPLETED.getValue()); 70 + mjMessage.setGenerateStatus(MidjourneyGennerateStatusEnum.COMPLETED.getStatus());
71 } 71 }
72 } 72 }
73 73
74 - private boolean ignoreAndLogMessage(DataObject data, MjMessageTypeEnum messageType) {  
75 - String channelId = data.getString(MjConstants.MSG_CHANNEL_ID); 74 + private boolean ignoreAndLogMessage(DataObject data, MidjourneyMessageTypeEnum messageType) {
  75 + String channelId = data.getString(MidjourneyConstants.MSG_CHANNEL_ID);
76 if (!CharSequenceUtil.equals(channelId, midjourneyConfig.getChannelId())) { 76 if (!CharSequenceUtil.equals(channelId, midjourneyConfig.getChannelId())) {
77 return true; 77 return true;
78 } 78 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/package-info.java
1 /** 1 /**
2 * author: fansili 2 * author: fansili
3 * time: 2024/3/12 20:29 3 * time: 2024/3/12 20:29
  4 + *
  5 + * TODO @fansili:包的想法,需要重点看看
  6 + *
  7 + * 1. org.springframework.ai:包括 chat、image、model、parser、util 部分
  8 + *
  9 + * 2. yudao.framework.models
  10 + * \qianwen 通义千问
  11 + * \yiyan 文心一言
  12 + * \xinghuo 星火
  13 + * \midjourney
4 */ 14 */
5 package cn.iocoder.yudao.framework.ai; 15 package cn.iocoder.yudao.framework.ai;
6 \ No newline at end of file 16 \ No newline at end of file
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/util/JacksonUtil.java deleted
1 -package cn.iocoder.yudao.framework.ai.util;  
2 -  
3 -import com.fasterxml.jackson.annotation.JsonInclude;  
4 -import com.fasterxml.jackson.core.JsonProcessingException;  
5 -import com.fasterxml.jackson.databind.ObjectMapper;  
6 -import com.fasterxml.jackson.databind.SerializationFeature;  
7 -import com.fasterxml.jackson.databind.module.SimpleModule;  
8 -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;  
9 -  
10 -import java.io.IOException;  
11 -  
12 -/**  
13 - * Jackson工具类  
14 - *  
15 - * author: fansili  
16 - * time: 2024/3/17 10:13  
17 - */  
18 -public class JacksonUtil {  
19 -  
20 - private static final ObjectMapper objectMapper = new ObjectMapper();  
21 -  
22 - /**  
23 - * 初始化 ObjectMapper 以美化输出(即格式化JSON内容)  
24 - */  
25 - static {  
26 - // 美化输出(缩进)  
27 - objectMapper.enable(SerializationFeature.INDENT_OUTPUT);  
28 - // 忽略值为 null 的属性  
29 - objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);  
30 - // 配置一个模块来将 Long 类型转换为 String 类型  
31 - SimpleModule module = new SimpleModule();  
32 - module.addSerializer(Long.class, ToStringSerializer.instance);  
33 - objectMapper.registerModule(module);  
34 - }  
35 -  
36 - /**  
37 - * 将对象转换为 JSON 字符串  
38 - *  
39 - * @param obj 需要序列化的Java对象  
40 - * @return 序列化后的 JSON 字符串  
41 - * @throws JsonProcessingException 当 JSON 序列化过程中出现错误时抛出异常  
42 - */  
43 - public static String toJson(Object obj) {  
44 - try {  
45 - return objectMapper.writeValueAsString(obj);  
46 - } catch (JsonProcessingException e) {  
47 - throw new RuntimeException(e);  
48 - }  
49 - }  
50 -  
51 - /**  
52 - * 将 JSON 字符串反序列化为指定类型的对象  
53 - *  
54 - * @param json JSON 字符串  
55 - * @param clazz 目标类型 Class 对象  
56 - * @param <T> 泛型类型参数  
57 - * @return 反序列化后的 Java 对象  
58 - * @throws IOException 当 JSON 解析过程中出现错误时抛出异常  
59 - */  
60 - public static <T> T fromJson(String json, Class<T> clazz) {  
61 - try {  
62 - return objectMapper.readValue(json, clazz);  
63 - } catch (JsonProcessingException e) {  
64 - throw new RuntimeException(e);  
65 - }  
66 - }  
67 -  
68 - /**  
69 - * 将对象转换为格式化的 JSON 字符串(已启用 INDENT_OUTPUT 功能,所以所有方法都会返回格式化后的 JSON)  
70 - *  
71 - * @param obj 需要序列化的Java对象  
72 - * @return 格式化后的 JSON 字符串  
73 - * @throws JsonProcessingException 当 JSON 序列化过程中出现错误时抛出异常  
74 - */  
75 - public static String toFormattedJson(Object obj) {  
76 - // 已在类初始化时设置了 SerializationFeature.INDENT_OUTPUT,此处无需额外操作  
77 - return toJson(obj);  
78 - }  
79 -}  
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/QianWenChatClientTests.java
1 package cn.iocoder.yudao.framework.ai.chat; 1 package cn.iocoder.yudao.framework.ai.chat;
2 2
3 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; 3 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
4 -import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenApi; 4 +import cn.iocoder.yudao.framework.ai.chatqianwen.api.QianWenApi;
5 import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenChatClient; 5 import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenChatClient;
6 import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenOptions; 6 import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenOptions;
7 -import com.aliyun.broadscope.bailian.sdk.models.CompletionsRequest;  
8 import org.junit.Before; 7 import org.junit.Before;
9 import org.junit.Test; 8 import org.junit.Test;
10 import reactor.core.publisher.Flux; 9 import reactor.core.publisher.Flux;
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/mj/MjInteractionsTests.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/midjourney/MidjourneyInteractionsTests.java
1 -package cn.iocoder.yudao.framework.ai.mj; 1 +package cn.iocoder.yudao.framework.ai.midjourney;
2 2
3 import cn.hutool.core.io.FileUtil; 3 import cn.hutool.core.io.FileUtil;
4 -import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig;  
5 -import cn.iocoder.yudao.framework.ai.midjourney.interactions.MjInteractions;  
6 -import cn.iocoder.yudao.framework.ai.midjourney.vo.Attachments;  
7 -import cn.iocoder.yudao.framework.ai.midjourney.vo.Describe;  
8 -import cn.iocoder.yudao.framework.ai.midjourney.vo.ReRoll;  
9 -import cn.iocoder.yudao.framework.ai.midjourney.vo.UploadAttachmentsRes; 4 +import cn.iocoder.yudao.framework.ai.midjourney.api.MidjourneyInteractionsApi;
  5 +import cn.iocoder.yudao.framework.ai.midjourney.api.req.AttachmentsReq;
  6 +import cn.iocoder.yudao.framework.ai.midjourney.api.req.DescribeReq;
  7 +import cn.iocoder.yudao.framework.ai.midjourney.api.req.ReRollReq;
  8 +import cn.iocoder.yudao.framework.ai.midjourney.api.res.UploadAttachmentsRes;
10 import com.alibaba.fastjson.JSON; 9 import com.alibaba.fastjson.JSON;
11 import org.junit.Before; 10 import org.junit.Before;
12 import org.junit.Test; 11 import org.junit.Test;
@@ -23,7 +22,7 @@ import java.util.Map; @@ -23,7 +22,7 @@ import java.util.Map;
23 * author: fansili 22 * author: fansili
24 * time: 2024/4/4 18:59 23 * time: 2024/4/4 18:59
25 */ 24 */
26 -public class MjInteractionsTests { 25 +public class MidjourneyInteractionsTests {
27 26
28 private MidjourneyConfig midjourneyConfig; 27 private MidjourneyConfig midjourneyConfig;
29 @Before 28 @Before
@@ -39,24 +38,24 @@ public class MjInteractionsTests { @@ -39,24 +38,24 @@ public class MjInteractionsTests {
39 38
40 @Test 39 @Test
41 public void mjImageTest() { 40 public void mjImageTest() {
42 - MjInteractions mjImagineInteractions = new MjInteractions(midjourneyConfig); 41 + MidjourneyInteractionsApi mjImagineInteractions = new MidjourneyInteractionsApi(midjourneyConfig);
43 mjImagineInteractions.imagine("童话里应该是什么样子?"); 42 mjImagineInteractions.imagine("童话里应该是什么样子?");
44 } 43 }
45 44
46 45
47 @Test 46 @Test
48 public void reRollTest() { 47 public void reRollTest() {
49 - MjInteractions mjImagineInteractions = new MjInteractions(midjourneyConfig);  
50 - mjImagineInteractions.reRoll(new ReRoll() 48 + MidjourneyInteractionsApi mjImagineInteractions = new MidjourneyInteractionsApi(midjourneyConfig);
  49 + mjImagineInteractions.reRoll(new ReRollReq()
51 .setMessageId("1226165117448753243") 50 .setMessageId("1226165117448753243")
52 .setCustomId("MJ::JOB::upsample::3::2aeefbef-43e2-4057-bcf1-43b5f39ab6f7")); 51 .setCustomId("MJ::JOB::upsample::3::2aeefbef-43e2-4057-bcf1-43b5f39ab6f7"));
53 } 52 }
54 53
55 @Test 54 @Test
56 public void uploadAttachmentsTest() { 55 public void uploadAttachmentsTest() {
57 - MjInteractions mjImagineInteractions = new MjInteractions(midjourneyConfig); 56 + MidjourneyInteractionsApi mjImagineInteractions = new MidjourneyInteractionsApi(midjourneyConfig);
58 UploadAttachmentsRes res = mjImagineInteractions.uploadAttachments( 57 UploadAttachmentsRes res = mjImagineInteractions.uploadAttachments(
59 - new Attachments().setFileSystemResource( 58 + new AttachmentsReq().setFileSystemResource(
60 new FileSystemResource(new File("/Users/fansili/Downloads/DSC01402.JPG"))) 59 new FileSystemResource(new File("/Users/fansili/Downloads/DSC01402.JPG")))
61 ); 60 );
62 System.err.println(JSON.toJSONString(res)); 61 System.err.println(JSON.toJSONString(res));
@@ -64,8 +63,8 @@ public class MjInteractionsTests { @@ -64,8 +63,8 @@ public class MjInteractionsTests {
64 63
65 @Test 64 @Test
66 public void describeTest() { 65 public void describeTest() {
67 - MjInteractions mjImagineInteractions = new MjInteractions(midjourneyConfig);  
68 - mjImagineInteractions.describe(new Describe() 66 + MidjourneyInteractionsApi mjImagineInteractions = new MidjourneyInteractionsApi(midjourneyConfig);
  67 + mjImagineInteractions.describe(new DescribeReq()
69 .setFileName("DSC01402.JPG") 68 .setFileName("DSC01402.JPG")
70 .setFinalFileName("16826931-2873-45ec-8cfb-0ad81f1a075f/DSC01402.JPG") 69 .setFinalFileName("16826931-2873-45ec-8cfb-0ad81f1a075f/DSC01402.JPG")
71 ); 70 );
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/mj/MjUtilTests.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/midjourney/MidjourneyUtilTests.java
1 -package cn.iocoder.yudao.framework.ai.mj; 1 +package cn.iocoder.yudao.framework.ai.midjourney;
2 2
3 -import cn.iocoder.yudao.framework.ai.midjourney.util.MjUtil; 3 +import cn.iocoder.yudao.framework.ai.midjourney.util.MidjourneyUtil;
4 import org.junit.Test; 4 import org.junit.Test;
5 5
6 /** 6 /**
@@ -9,14 +9,14 @@ import org.junit.Test; @@ -9,14 +9,14 @@ import org.junit.Test;
9 * author: fansili 9 * author: fansili
10 * time: 2024/4/6 21:57 10 * time: 2024/4/6 21:57
11 */ 11 */
12 -public class MjUtilTests { 12 +public class MidjourneyUtilTests {
13 13
14 @Test 14 @Test
15 public void parseContentTest() { 15 public void parseContentTest() {
16 String content1 = "**南极应该是什么样子? --v 6.0 --style raw** - <@972721304891453450> (32%) (fast, stealth)"; 16 String content1 = "**南极应该是什么样子? --v 6.0 --style raw** - <@972721304891453450> (32%) (fast, stealth)";
17 String content2 = "**南极应该是什么样子? --v 6.0 --style raw** - <@972721304891453450> (fast, stealth)"; 17 String content2 = "**南极应该是什么样子? --v 6.0 --style raw** - <@972721304891453450> (fast, stealth)";
18 18
19 - System.err.println(MjUtil.parseContent(content1));  
20 - System.err.println(MjUtil.parseContent(content2)); 19 + System.err.println(MidjourneyUtil.parseContent(content1));
  20 + System.err.println(MidjourneyUtil.parseContent(content2));
21 } 21 }
22 } 22 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/mj/MjWebSocketTests.java renamed to yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/midjourney/MidjourneyWebSocketTests.java
1 -package cn.iocoder.yudao.framework.ai.mj; 1 +package cn.iocoder.yudao.framework.ai.midjourney;
2 2
3 import cn.hutool.core.io.FileUtil; 3 import cn.hutool.core.io.FileUtil;
4 -import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig;  
5 -import cn.iocoder.yudao.framework.ai.midjourney.webSocket.listener.MjMessageListener;  
6 -import cn.iocoder.yudao.framework.ai.midjourney.webSocket.MjWebSocketStarter; 4 +import cn.iocoder.yudao.framework.ai.midjourney.webSocket.listener.MidjourneyMessageListener;
  5 +import cn.iocoder.yudao.framework.ai.midjourney.webSocket.MidjourneyWebSocketStarter;
7 import org.junit.Before; 6 import org.junit.Before;
8 import org.junit.Test; 7 import org.junit.Test;
9 8
@@ -17,7 +16,7 @@ import java.util.Scanner; @@ -17,7 +16,7 @@ import java.util.Scanner;
17 * author: fansili 16 * author: fansili
18 * time: 2024/4/3 16:40 17 * time: 2024/4/3 16:40
19 */ 18 */
20 -public class MjWebSocketTests { 19 +public class MidjourneyWebSocketTests {
21 20
22 private MidjourneyConfig midjourneyConfig; 21 private MidjourneyConfig midjourneyConfig;
23 22
@@ -35,8 +34,8 @@ public class MjWebSocketTests { @@ -35,8 +34,8 @@ public class MjWebSocketTests {
35 @Test 34 @Test
36 public void startSocketTest() { 35 public void startSocketTest() {
37 String wssUrl = "wss://gateway.discord.gg"; 36 String wssUrl = "wss://gateway.discord.gg";
38 - var messageListener = new MjMessageListener(midjourneyConfig);  
39 - var webSocketStarter = new MjWebSocketStarter(wssUrl, null, midjourneyConfig, messageListener); 37 + var messageListener = new MidjourneyMessageListener(midjourneyConfig);
  38 + var webSocketStarter = new MidjourneyWebSocketStarter(wssUrl, null, midjourneyConfig, messageListener);
40 39
41 try { 40 try {
42 webSocketStarter.start(); 41 webSocketStarter.start();