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 628 <artifactId>ureport2-console</artifactId>
629 629 <version>${ureport2.version}</version>
630 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 631 </dependencies>
641 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 11  
12 12 // ========== 模块 ai 错误码区间 [1-022-000-000 ~ 1-023-000-000) ==========
13 13  
  14 + // TODO @fansili:1)类注释不太对;2)中英文之间,有个空格;例如说 AI 模型
14 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 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 5 import cn.iocoder.yudao.module.ai.ErrorCodeConstants;
6 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 8 import io.swagger.v3.oas.annotations.Operation;
9 9 import io.swagger.v3.oas.annotations.tags.Tag;
10 10 import jakarta.servlet.http.HttpServletResponse;
... ... @@ -13,7 +13,6 @@ import org.springframework.ai.chat.ChatClient;
13 13 import org.springframework.ai.chat.ChatResponse;
14 14 import org.springframework.ai.chat.prompt.Prompt;
15 15 import org.springframework.ai.openai.OpenAiChatClient;
16   -import org.springframework.ai.openai.api.OpenAiApi;
17 16 import org.springframework.beans.factory.annotation.Autowired;
18 17 import org.springframework.context.ApplicationContext;
19 18 import org.springframework.validation.annotation.Validated;
... ... @@ -23,15 +22,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
23 22 import org.springframework.web.bind.annotation.RestController;
24 23 import reactor.core.publisher.Flux;
25 24  
26   -import java.util.Scanner;
27 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 28 @Tag(name = "AI模块")
36 29 @RestController
37 30 @RequestMapping("/ai-api")
... ... @@ -47,7 +40,7 @@ public class ChatController {
47 40 ChatClient chatClient = getChatClient(reqVO.getAiModel());
48 41 String res;
49 42 try {
50   - res = chatClient.call(reqVO.getInputText());
  43 + res = chatClient.call(reqVO.getPrompt());
51 44 } catch (Exception e) {
52 45 res = e.getMessage();
53 46 }
... ... @@ -58,33 +51,14 @@ public class ChatController {
58 51 @Operation(summary = "对话聊天chatStream", description = "简单的ai聊天")
59 52 public CommonResult chatStream(HttpServletResponse response, @RequestBody @Validated AiChatReqVO reqVO) throws InterruptedException {
60 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 55 chatResponse.subscribe(new Consumer<ChatResponse>() {
63 56 @Override
64 57 public void accept(ChatResponse chatResponse) {
65 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 67 * @param aiModelEnum
94 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 72 return applicationContext.getBean(OpenAiChatClient.class);
99 73 }
100 74 // AI模型暂不支持
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/vo/AiChatReqVO.java
1 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 4 import io.swagger.v3.oas.annotations.media.Schema;
5 5 import jakarta.validation.constraints.NotNull;
6 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 9 @Data
  10 +@Schema(description = "用户 App - 上传文件 Request VO")
16 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 17 @Schema(description = "AI模型", requiredMode = Schema.RequiredMode.REQUIRED)
23 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 3 # openAI https://openai.com/
4 4 spring.ai.openai.api-key=${OPEN_AI_KEY}
... ...
yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
... ... @@ -149,6 +149,10 @@
149 149 <!-- </exclusion>-->
150 150 <!-- </exclusions>-->
151 151 </dependency>
  152 + <dependency>
  153 + <groupId>cn.iocoder.boot</groupId>
  154 + <artifactId>yudao-common</artifactId>
  155 + </dependency>
152 156 </dependencies>
153 157  
154 158 </project>
155 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 import cn.iocoder.yudao.framework.ai.chat.messages.MessageType;
7 7 import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions;
8 8 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
  9 +import cn.iocoder.yudao.framework.ai.chatqianwen.api.QianWenApi;
9 10 import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException;
10 11 import com.aliyun.broadscope.bailian.sdk.models.*;
11 12 import lombok.extern.slf4j.Slf4j;
... ... @@ -29,7 +30,7 @@ import java.util.stream.Collectors;
29 30 * time: 2024/3/13 21:06
30 31 */
31 32 @Slf4j
32   -public class QianWenChatClient implements ChatClient, StreamingChatClient {
  33 +public class QianWenChatClient implements ChatClient, StreamingChatClient {
33 34  
34 35 private QianWenApi qianWenApi;
35 36  
... ... @@ -44,6 +45,7 @@ public class QianWenChatClient implements ChatClient, StreamingChatClient {
44 45 this.qianWenOptions = qianWenOptions;
45 46 }
46 47  
  48 + // TODO @fansili:看看咋公用出来,允许传入类似异常之类的参数;
47 49 public final RetryTemplate retryTemplate = RetryTemplate.builder()
48 50 // 最大重试次数 10
49 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 3 import com.aliyun.broadscope.bailian.sdk.AccessTokenClient;
4 4 import com.aliyun.broadscope.bailian.sdk.ApplicationClient;
... ... @@ -9,6 +9,7 @@ import org.springframework.http.HttpStatusCode;
9 9 import org.springframework.http.ResponseEntity;
10 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 3 import cn.hutool.json.JSONUtil;
4 4 import cn.iocoder.yudao.framework.ai.imageopenai.api.OpenAiImageRequest;
5 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 7 import io.netty.channel.ChannelOption;
8 8 import lombok.extern.slf4j.Slf4j;
9 9 import org.apache.http.HttpEntity;
... ... @@ -14,7 +14,6 @@ import org.apache.http.impl.client.CloseableHttpClient;
14 14 import org.apache.http.impl.client.HttpClients;
15 15 import org.apache.http.util.EntityUtils;
16 16 import org.springframework.http.client.reactive.ReactorClientHttpConnector;
17   -import org.springframework.web.reactive.function.BodyInserters;
18 17 import org.springframework.web.reactive.function.client.WebClient;
19 18 import reactor.netty.http.client.HttpClient;
20 19  
... ... @@ -55,7 +54,7 @@ public class OpenAiImageApi {
55 54 httpPost.setURI(URI.create(DEFAULT_BASE_URL.concat("/v1/images/generations")));
56 55 httpPost.setHeader("Content-Type", "application/json");
57 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 59 CloseableHttpResponse response= null;
61 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 1 package cn.iocoder.yudao.framework.ai.midjourney;
2 2  
  3 +import cn.iocoder.yudao.framework.ai.midjourney.constants.MidjourneyGennerateStatusEnum;
3 4 import lombok.Data;
4 5 import lombok.experimental.Accessors;
5 6  
... ... @@ -7,7 +8,7 @@ import java.util.List;
7 8  
8 9 @Data
9 10 @Accessors(chain = true)
10   -public class MjMessage {
  11 +public class MidjourneyMessage {
11 12  
12 13 /**
13 14 * id是一个重要的字段,在同时生成多个的时候,可以区分生成信息
... ... @@ -41,7 +42,7 @@ public class MjMessage {
41 42 * 1、等待
42 43 * 2、进行中
43 44 * 3、完成
44   - * {@link cn.iocoder.yudao.framework.ai.midjourney.constants.MjGennerateStatusEnum}
  45 + * {@link MidjourneyGennerateStatusEnum}
45 46 */
46 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 3 import cn.hutool.core.util.StrUtil;
5 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 10 import com.alibaba.fastjson.JSON;
13 11 import com.alibaba.fastjson.JSONObject;
14 12 import com.google.common.collect.Lists;
15   -import com.google.common.collect.Maps;
16 13 import lombok.extern.slf4j.Slf4j;
17   -import org.jetbrains.annotations.NotNull;
18 14 import org.springframework.core.io.FileSystemResource;
19 15 import org.springframework.http.*;
20 16 import org.springframework.util.LinkedMultiValueMap;
... ... @@ -24,6 +20,7 @@ import org.springframework.web.client.RestTemplate;
24 20 import java.io.IOException;
25 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 28 * time: 2024/4/3 17:36
32 29 */
33 30 @Slf4j
34   -public class MjInteractions {
35   -
  31 +public class MidjourneyInteractionsApi extends MidjourneyInteractions {
36 32  
37 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 38 this.url = midjourneyConfig.getServerUrl().concat(midjourneyConfig.getApiInteractions());
46 39 }
47 40  
... ... @@ -49,45 +42,38 @@ public class MjInteractions {
49 42 // 获取请求模板
50 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 46 requestParams.put("prompt", prompt);
58 47 // 解析 template 参数占位符
59   - String requestBody = MjUtil.parseTemplate(requestTemplate, requestParams);
  48 + String requestBody = MidjourneyUtil.parseTemplate(requestTemplate, requestParams);
60 49 // 获取 header
61   - HttpHeaders httpHeaders = getHttpHeaders();
  50 + HttpHeaders httpHeaders = getHeadersOfAppJson();
62 51 // 发送请求
63 52 HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, httpHeaders);
64 53 String res = restTemplate.postForObject(url, requestEntity, String.class);
65 54 // 这个 res 只要不返回值,就是成功!
66   - boolean isSuccess = StrUtil.isBlank(res);
67   - if (isSuccess) {
  55 + // TODO @fansili:可以直接 if (StrUtil.isBlank(res))
  56 + if (StrUtil.isBlank(res)) {
68 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 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 71 requestParams.put("custom_id", reRoll.getCustomId());
86 72 requestParams.put("message_id", reRoll.getMessageId());
87 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 78 HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, httpHeaders);
93 79 String res = restTemplate.postForObject(url, requestEntity, String.class);
... ... @@ -100,12 +86,13 @@ public class MjInteractions {
100 86 return isSuccess;
101 87 }
102 88  
103   -
104   - public UploadAttachmentsRes uploadAttachments(Attachments attachments) {
  89 + // TODO @fansili:搞成私有方法,可能会好点;
  90 + public UploadAttachmentsRes uploadAttachments(AttachmentsReq attachments) {
105 91 // file
106 92 JSONObject fileObj = new JSONObject();
107 93 fileObj.put("id", "0");
108 94 fileObj.put("filename", attachments.getFileSystemResource().getFilename());
  95 + // TODO @fansili:这块用 lombok 哪个异常处理,简化下代码;
109 96 try {
110 97 fileObj.put("file_size", attachments.getFileSystemResource().contentLength());
111 98 } catch (IOException e) {
... ... @@ -115,12 +102,7 @@ public class MjInteractions {
115 102 MultiValueMap<String, Object> multipartRequest = new LinkedMultiValueMap<>();
116 103 multipartRequest.put("files", Lists.newArrayList(fileObj));
117 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 106 // 创建HttpEntity对象,包含表单数据和头部信息
125 107 HttpEntity<MultiValueMap<String, Object>> multiValueMapHttpEntity = new HttpEntity<>(multipartRequest, httpHeaders);
126 108 // 发送POST请求并接收响应
... ... @@ -128,38 +110,26 @@ public class MjInteractions {
128 110 String response = restTemplate.postForObject(midjourneyConfig.getServerUrl().concat(uri), multiValueMapHttpEntity, String.class);
129 111 UploadAttachmentsRes uploadAttachmentsRes = JSON.parseObject(response, UploadAttachmentsRes.class);
130 112  
131   -
132 113 //
133 114 // 上传文件
134 115 String uploadUrl = uploadAttachmentsRes.getAttachments().getFirst().getUploadUrl();
135   - String uploadAttachmentsUrl = midjourneyConfig.getApiAttachmentsUpload().concat(uploadUrl);
136 116 httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
137 117 HttpEntity<FileSystemResource> fileSystemResourceHttpEntity = new HttpEntity<>(attachments.getFileSystemResource(), httpHeaders);
138 118 ResponseEntity<String> exchange = restTemplate.exchange(uploadUrl, HttpMethod.PUT, fileSystemResourceHttpEntity, String.class);
139 119 String uploadRes = exchange.getBody();
140   -
141 120 return uploadAttachmentsRes;
142 121 }
143 122  
144   - public Boolean describe(Describe describe) {
  123 + public Boolean describe(DescribeReq describe) {
145 124 // 获取请求模板
146 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 128 requestParams.put("file_name", describe.getFileName());
154 129 requestParams.put("final_file_name", describe.getFinalFileName());
155 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 134 MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
165 135 formData.add("payload_json", requestBody);
... ... @@ -175,14 +145,4 @@ public class MjInteractions {
175 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 3 import lombok.Data;
4 4 import lombok.experimental.Accessors;
... ... @@ -12,7 +12,7 @@ import org.springframework.core.io.FileSystemResource;
12 12 */
13 13 @Data
14 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 3 import lombok.Data;
4 4 import lombok.experimental.Accessors;
5 5  
6   -import java.io.File;
7   -
8 6 /**
9 7 * describe
10 8 *
... ... @@ -13,7 +11,7 @@ import java.io.File;
13 11 */
14 12 @Data
15 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 3 import lombok.Data;
4 4 import lombok.experimental.Accessors;
... ... @@ -9,7 +9,7 @@ import lombok.experimental.Accessors;
9 9 */
10 10 @Data
11 11 @Accessors(chain = true)
12   -public class ReRoll {
  12 +public class ReRollReq {
13 13  
14 14 /**
15 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 3 import lombok.Data;
4 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 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 1 package cn.iocoder.yudao.framework.ai.midjourney.constants;
2 2  
  3 +import lombok.AllArgsConstructor;
3 4 import lombok.Getter;
4 5  
  6 +// TODO done @fansili:1)Mj 缩写,还是搞成全称。。虽然长一点,但是感觉会相对清晰一些哈;2)lombok 相关的注解,可以用用哈;3)value 改 status;
5 7 /**
6 8 * mj 生成状态
7 9 *
... ... @@ -9,8 +11,8 @@ import lombok.Getter;
9 11 * time: 2024/4/6 21:07
10 12 */
11 13 @Getter
12   -public enum MjGennerateStatusEnum {
13   -
  14 +@AllArgsConstructor
  15 +public enum MidjourneyGennerateStatusEnum {
14 16  
15 17 WAITING("waiting", "等待..."),
16 18 IN_PROGRESS("in_progress", "进行中"),
... ... @@ -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 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 6 * MJ 命令
7 7 */
8 8 @Getter
9   -public enum MjInteractionsEnum {
  9 +public enum MidjourneyInteractionsEnum {
10 10  
11 11 IMAGINE("imagine", "生成图片"),
12 12 DESCRIBE("describe", "生成描述"),
... ... @@ -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 21 this.value =value;
22 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 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 15 */
16 16 DELETE;
17 17  
18   - public static MjMessageTypeEnum of(String type) {
  18 + public static MidjourneyMessageTypeEnum of(String type) {
19 19 return switch (type) {
20 20 case "MESSAGE_CREATE" -> CREATE;
21 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 3 import lombok.experimental.UtilityClass;
4 4  
5 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 1 package cn.iocoder.yudao.framework.ai.midjourney.util;
2 2  
3 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 6 import java.util.Map;
7 7 import java.util.regex.Matcher;
... ... @@ -13,7 +13,7 @@ import java.util.regex.Pattern;
13 13 * author: fansili
14 14 * time: 2024/4/6 19:00
15 15 */
16   -public class MjUtil {
  16 +public class MidjourneyUtil {
17 17 /**
18 18 * content正则匹配prompt和进度.
19 19 */
... ... @@ -26,12 +26,12 @@ public class MjUtil {
26 26 * @param content
27 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 32 // "**南极应该是什么样子? --v 6.0 --style raw** - <@972721304891453450> (32%) (fast, stealth)",
33 33 // "**南极应该是什么样子? --v 6.0 --style raw** - <@972721304891453450> (fast, stealth)"
34   - MjMessage.Content mjContent = new MjMessage.Content();
  34 + MidjourneyMessage.Content mjContent = new MidjourneyMessage.Content();
35 35 if (CharSequenceUtil.isBlank(content)) {
36 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 4 import cn.hutool.core.text.CharSequenceUtil;
5 5 import cn.hutool.core.thread.ThreadUtil;
6 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 10 import lombok.Getter;
11 11 import lombok.extern.slf4j.Slf4j;
12 12 import org.apache.tomcat.websocket.Constants;
... ... @@ -20,10 +20,10 @@ import org.springframework.web.socket.client.standard.StandardWebSocketClient;
20 20  
21 21 import java.io.IOException;
22 22 import java.net.URI;
23   -import java.util.concurrent.TimeoutException;
24 23  
  24 +// TODO @fansili:mj 这块 websocket 有点小复杂,虽然代码量 400 多行;感觉可以考虑,有没第三方 sdk,通过它透明接入 mj
25 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 35 /**
36 36 * mj 监听(所有message 都会 callback到这里)
37 37 */
38   - private final MjMessageListener userMessageListener;
  38 + private final MidjourneyMessageListener userMessageListener;
39 39 /**
40 40 * wss 服务器
41 41 */
... ... @@ -57,10 +57,10 @@ public class MjWebSocketStarter implements WebSocketStarter {
57 57 */
58 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 64 this.wssServer = wssServer;
65 65 this.resumeWss = resumeWss;
66 66 this.midjourneyConfig = midjourneyConfig;
... ... @@ -82,7 +82,7 @@ public class MjWebSocketStarter implements WebSocketStarter {
82 82 headers.add("Sec-Websocket-Extensions", "permessage-deflate; client_max_window_bits");
83 83 headers.add("User-Agent", this.midjourneyConfig.getUserAage());
84 84 // 创建 mjHeader
85   - MjWebSocketHandler mjWebSocketHandler = new MjWebSocketHandler(
  85 + MidjourneyWebSocketHandler mjWebSocketHandler = new MidjourneyWebSocketHandler(
86 86 this.midjourneyConfig, this.userMessageListener, this::onSocketSuccess, this::onSocketFailure);
87 87 //
88 88 String gatewayUrl;
... ... @@ -104,12 +104,12 @@ public class MjWebSocketStarter implements WebSocketStarter {
104 104 socketSessionFuture.addCallback(new ListenableFutureCallback<>() {
105 105 @Override
106 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 110 @Override
111 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 117 private void onSocketSuccess(String sessionId, Object sequence, String resumeGatewayUrl) {
118 118 this.resumeData = new ResumeData(sessionId, sequence, resumeGatewayUrl);
119 119 this.running = true;
120   - notifyWssLock(MjNotifyCode.SUCCESS, "");
  120 + notifyWssLock(MidjourneyNotifyCode.SUCCESS, "");
121 121 }
122 122  
123 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 8 import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig;
9 9 import cn.iocoder.yudao.framework.ai.midjourney.webSocket.FailureCallback;
10 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 12 import lombok.Setter;
13 13 import lombok.extern.slf4j.Slf4j;
14 14 import net.dv8tion.jda.api.utils.data.DataArray;
... ... @@ -29,7 +29,7 @@ import java.util.concurrent.ScheduledExecutorService;
29 29 import java.util.concurrent.TimeUnit;
30 30  
31 31 @Slf4j
32   -public class MjWebSocketHandler implements WebSocketHandler {
  32 +public class MidjourneyWebSocketHandler implements WebSocketHandler {
33 33 /**
34 34 * close 错误码:重连
35 35 */
... ... @@ -49,7 +49,7 @@ public class MjWebSocketHandler implements WebSocketHandler {
49 49 /**
50 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 85 */
86 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 92 this.midjourneyConfig = account;
93 93 this.userMessageListener = userMessageListener;
94 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 6 import cn.hutool.core.util.StrUtil;
7 7 import cn.hutool.json.JSONUtil;
8 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 14 import com.alibaba.fastjson.JSON;
15 15 import lombok.extern.slf4j.Slf4j;
16 16 import net.dv8tion.jda.api.utils.data.DataObject;
... ... @@ -18,17 +18,17 @@ import net.dv8tion.jda.api.utils.data.DataObject;
18 18 import java.util.List;
19 19  
20 20 @Slf4j
21   -public class MjMessageListener {
  21 +public class MidjourneyMessageListener {
22 22  
23 23 private MidjourneyConfig midjourneyConfig;
24 24  
25   - public MjMessageListener(MidjourneyConfig midjourneyConfig) {
  25 + public MidjourneyMessageListener(MidjourneyConfig midjourneyConfig) {
26 26 this.midjourneyConfig = midjourneyConfig;
27 27 }
28 28  
29 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 32 return;
33 33 }
34 34 DataObject data = raw.getObject("d");
... ... @@ -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 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 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 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 55 mjMessage.setAttachments(attachments);
56 56 }
57 57 // 转换状态
... ... @@ -61,18 +61,18 @@ public class MjMessageListener {
61 61 System.err.println(JSONUtil.toJsonPrettyStr(mjMessage));
62 62 }
63 63  
64   - private void convertGenerateStatus(MjMessage mjMessage) {
  64 + private void convertGenerateStatus(MidjourneyMessage mjMessage) {
65 65 if (mjMessage.getType() == 20 && mjMessage.getContent().getStatus().contains("Waiting")) {
66   - mjMessage.setGenerateStatus(MjGennerateStatusEnum.WAITING.getValue());
  66 + mjMessage.setGenerateStatus(MidjourneyGennerateStatusEnum.WAITING.getStatus());
67 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 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 76 if (!CharSequenceUtil.equals(channelId, midjourneyConfig.getChannelId())) {
77 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 2 * author: fansili
3 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 15 package cn.iocoder.yudao.framework.ai;
6 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 1 package cn.iocoder.yudao.framework.ai.chat;
2 2  
3 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 5 import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenChatClient;
6 6 import cn.iocoder.yudao.framework.ai.chatqianwen.QianWenOptions;
7   -import com.aliyun.broadscope.bailian.sdk.models.CompletionsRequest;
8 7 import org.junit.Before;
9 8 import org.junit.Test;
10 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 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 9 import com.alibaba.fastjson.JSON;
11 10 import org.junit.Before;
12 11 import org.junit.Test;
... ... @@ -23,7 +22,7 @@ import java.util.Map;
23 22 * author: fansili
24 23 * time: 2024/4/4 18:59
25 24 */
26   -public class MjInteractionsTests {
  25 +public class MidjourneyInteractionsTests {
27 26  
28 27 private MidjourneyConfig midjourneyConfig;
29 28 @Before
... ... @@ -39,24 +38,24 @@ public class MjInteractionsTests {
39 38  
40 39 @Test
41 40 public void mjImageTest() {
42   - MjInteractions mjImagineInteractions = new MjInteractions(midjourneyConfig);
  41 + MidjourneyInteractionsApi mjImagineInteractions = new MidjourneyInteractionsApi(midjourneyConfig);
43 42 mjImagineInteractions.imagine("童话里应该是什么样子?");
44 43 }
45 44  
46 45  
47 46 @Test
48 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 50 .setMessageId("1226165117448753243")
52 51 .setCustomId("MJ::JOB::upsample::3::2aeefbef-43e2-4057-bcf1-43b5f39ab6f7"));
53 52 }
54 53  
55 54 @Test
56 55 public void uploadAttachmentsTest() {
57   - MjInteractions mjImagineInteractions = new MjInteractions(midjourneyConfig);
  56 + MidjourneyInteractionsApi mjImagineInteractions = new MidjourneyInteractionsApi(midjourneyConfig);
58 57 UploadAttachmentsRes res = mjImagineInteractions.uploadAttachments(
59   - new Attachments().setFileSystemResource(
  58 + new AttachmentsReq().setFileSystemResource(
60 59 new FileSystemResource(new File("/Users/fansili/Downloads/DSC01402.JPG")))
61 60 );
62 61 System.err.println(JSON.toJSONString(res));
... ... @@ -64,8 +63,8 @@ public class MjInteractionsTests {
64 63  
65 64 @Test
66 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 68 .setFileName("DSC01402.JPG")
70 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 4 import org.junit.Test;
5 5  
6 6 /**
... ... @@ -9,14 +9,14 @@ import org.junit.Test;
9 9 * author: fansili
10 10 * time: 2024/4/6 21:57
11 11 */
12   -public class MjUtilTests {
  12 +public class MidjourneyUtilTests {
13 13  
14 14 @Test
15 15 public void parseContentTest() {
16 16 String content1 = "**南极应该是什么样子? --v 6.0 --style raw** - <@972721304891453450> (32%) (fast, stealth)";
17 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 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 6 import org.junit.Before;
8 7 import org.junit.Test;
9 8  
... ... @@ -17,7 +16,7 @@ import java.util.Scanner;
17 16 * author: fansili
18 17 * time: 2024/4/3 16:40
19 18 */
20   -public class MjWebSocketTests {
  19 +public class MidjourneyWebSocketTests {
21 20  
22 21 private MidjourneyConfig midjourneyConfig;
23 22  
... ... @@ -35,8 +34,8 @@ public class MjWebSocketTests {
35 34 @Test
36 35 public void startSocketTest() {
37 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 40 try {
42 41 webSocketStarter.start();
... ...