Commit 44e44dc4bb1431a293afd2d3cd9c57fccb65258e

Authored by cherishsince
1 parent 5044a581

增加注释

yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjNotifyCode.java
@@ -8,35 +8,7 @@ public final class MjNotifyCode { @@ -8,35 +8,7 @@ public final class MjNotifyCode {
8 * 成功. 8 * 成功.
9 */ 9 */
10 public static final int SUCCESS = 1; 10 public static final int SUCCESS = 1;
11 - /**  
12 - * 数据未找到.  
13 - */  
14 - public static final int NOT_FOUND = 3;  
15 - /**  
16 - * 校验错误.  
17 - */  
18 - public static final int VALIDATION_ERROR = 4;  
19 - /**  
20 - * 系统异常.  
21 - */  
22 - public static final int FAILURE = 9;  
23 11
24 - /**  
25 - * 已存在.  
26 - */  
27 - public static final int EXISTED = 21;  
28 - /**  
29 - * 排队中.  
30 - */  
31 - public static final int IN_QUEUE = 22;  
32 - /**  
33 - * 队列已满.  
34 - */  
35 - public static final int QUEUE_REJECTED = 23;  
36 - /**  
37 - * prompt包含敏感词.  
38 - */  
39 - public static final int BANNED_PROMPT = 24;  
40 12
41 13
42 } 14 }
43 \ No newline at end of file 15 \ No newline at end of file
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/MjWebSocketStarter.java
@@ -7,9 +7,11 @@ import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig; @@ -7,9 +7,11 @@ import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig;
7 import cn.iocoder.yudao.framework.ai.midjourney.constants.MjNotifyCode; 7 import cn.iocoder.yudao.framework.ai.midjourney.constants.MjNotifyCode;
8 import cn.iocoder.yudao.framework.ai.midjourney.webSocket.handler.MjWebSocketHandler; 8 import cn.iocoder.yudao.framework.ai.midjourney.webSocket.handler.MjWebSocketHandler;
9 import cn.iocoder.yudao.framework.ai.midjourney.webSocket.listener.MjMessageListener; 9 import cn.iocoder.yudao.framework.ai.midjourney.webSocket.listener.MjMessageListener;
  10 +import lombok.Getter;
10 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
11 import org.apache.tomcat.websocket.Constants; 12 import org.apache.tomcat.websocket.Constants;
12 import org.jetbrains.annotations.NotNull; 13 import org.jetbrains.annotations.NotNull;
  14 +import org.springframework.util.concurrent.ListenableFuture;
13 import org.springframework.util.concurrent.ListenableFutureCallback; 15 import org.springframework.util.concurrent.ListenableFutureCallback;
14 import org.springframework.web.socket.CloseStatus; 16 import org.springframework.web.socket.CloseStatus;
15 import org.springframework.web.socket.WebSocketHttpHeaders; 17 import org.springframework.web.socket.WebSocketHttpHeaders;
@@ -22,19 +24,43 @@ import java.util.concurrent.TimeoutException; @@ -22,19 +24,43 @@ import java.util.concurrent.TimeoutException;
22 24
23 @Slf4j 25 @Slf4j
24 public class MjWebSocketStarter implements WebSocketStarter { 26 public class MjWebSocketStarter implements WebSocketStarter {
  27 + /**
  28 + * 链接重试次数
  29 + */
25 private static final int CONNECT_RETRY_LIMIT = 5; 30 private static final int CONNECT_RETRY_LIMIT = 5;
26 - 31 + /**
  32 + * mj 配置文件
  33 + */
27 private final MidjourneyConfig midjourneyConfig; 34 private final MidjourneyConfig midjourneyConfig;
  35 + /**
  36 + * mj 监听(所有message 都会 callback到这里)
  37 + */
28 private final MjMessageListener userMessageListener; 38 private final MjMessageListener userMessageListener;
  39 + /**
  40 + * wss 服务器
  41 + */
29 private final String wssServer; 42 private final String wssServer;
  43 + /**
  44 + *
  45 + */
30 private final String resumeWss; 46 private final String resumeWss;
31 - 47 + /**
  48 + *
  49 + */
  50 + private ResumeData resumeData = null;
  51 + /**
  52 + * 是否运行成功
  53 + */
32 private boolean running = false; 54 private boolean running = false;
33 - 55 + /**
  56 + * 链接成功的 session
  57 + */
34 private WebSocketSession webSocketSession = null; 58 private WebSocketSession webSocketSession = null;
35 - private ResumeData resumeData = null;  
36 59
37 - public MjWebSocketStarter(String wssServer, String resumeWss, MidjourneyConfig midjourneyConfig, MjMessageListener userMessageListener) { 60 + public MjWebSocketStarter(String wssServer,
  61 + String resumeWss,
  62 + MidjourneyConfig midjourneyConfig,
  63 + MjMessageListener userMessageListener) {
38 this.wssServer = wssServer; 64 this.wssServer = wssServer;
39 this.resumeWss = resumeWss; 65 this.resumeWss = resumeWss;
40 this.midjourneyConfig = midjourneyConfig; 66 this.midjourneyConfig = midjourneyConfig;
@@ -42,11 +68,12 @@ public class MjWebSocketStarter implements WebSocketStarter { @@ -42,11 +68,12 @@ public class MjWebSocketStarter implements WebSocketStarter {
42 } 68 }
43 69
44 @Override 70 @Override
45 - public void start() throws Exception { 71 + public void start() {
46 start(false); 72 start(false);
47 } 73 }
48 74
49 private void start(boolean reconnect) { 75 private void start(boolean reconnect) {
  76 + // 设置header
50 WebSocketHttpHeaders headers = new WebSocketHttpHeaders(); 77 WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
51 headers.add("Accept-Encoding", "gzip, deflate, br"); 78 headers.add("Accept-Encoding", "gzip, deflate, br");
52 headers.add("Accept-Language", "zh-CN,zh;q=0.9"); 79 headers.add("Accept-Language", "zh-CN,zh;q=0.9");
@@ -54,19 +81,26 @@ public class MjWebSocketStarter implements WebSocketStarter { @@ -54,19 +81,26 @@ public class MjWebSocketStarter implements WebSocketStarter {
54 headers.add("Pragma", "no-cache"); 81 headers.add("Pragma", "no-cache");
55 headers.add("Sec-Websocket-Extensions", "permessage-deflate; client_max_window_bits"); 82 headers.add("Sec-Websocket-Extensions", "permessage-deflate; client_max_window_bits");
56 headers.add("User-Agent", this.midjourneyConfig.getUserAage()); 83 headers.add("User-Agent", this.midjourneyConfig.getUserAage());
57 - var handler = new MjWebSocketHandler(this.midjourneyConfig, this.userMessageListener, this::onSocketSuccess, this::onSocketFailure); 84 + // 创建 mjHeader
  85 + MjWebSocketHandler mjWebSocketHandler = new MjWebSocketHandler(
  86 + this.midjourneyConfig, this.userMessageListener, this::onSocketSuccess, this::onSocketFailure);
  87 + //
58 String gatewayUrl; 88 String gatewayUrl;
59 if (reconnect) { 89 if (reconnect) {
60 - gatewayUrl = getGatewayServer(this.resumeData.resumeGatewayUrl()) + "/?encoding=json&v=9&compress=zlib-stream";  
61 - handler.setSessionId(this.resumeData.sessionId());  
62 - handler.setSequence(this.resumeData.sequence());  
63 - handler.setResumeGatewayUrl(this.resumeData.resumeGatewayUrl()); 90 + gatewayUrl = getGatewayServer(this.resumeData.getResumeGatewayUrl()) + "/?encoding=json&v=9&compress=zlib-stream";
  91 + mjWebSocketHandler.setSessionId(this.resumeData.getSessionId());
  92 + mjWebSocketHandler.setSequence(this.resumeData.getSequence());
  93 + mjWebSocketHandler.setResumeGatewayUrl(this.resumeData.getResumeGatewayUrl());
64 } else { 94 } else {
65 gatewayUrl = getGatewayServer(null) + "/?encoding=json&v=9&compress=zlib-stream"; 95 gatewayUrl = getGatewayServer(null) + "/?encoding=json&v=9&compress=zlib-stream";
66 } 96 }
67 - var webSocketClient = new StandardWebSocketClient(); 97 + // 创建 StandardWebSocketClient
  98 + StandardWebSocketClient webSocketClient = new StandardWebSocketClient();
  99 + // 设置 io timeout 时间
68 webSocketClient.getUserProperties().put(Constants.IO_TIMEOUT_MS_PROPERTY, "10000"); 100 webSocketClient.getUserProperties().put(Constants.IO_TIMEOUT_MS_PROPERTY, "10000");
69 - var socketSessionFuture = webSocketClient.doHandshake(handler, headers, URI.create(gatewayUrl)); 101 + //
  102 + ListenableFuture<WebSocketSession> socketSessionFuture = webSocketClient.doHandshake(mjWebSocketHandler, headers, URI.create(gatewayUrl));
  103 + // 添加 callback 进行回调
70 socketSessionFuture.addCallback(new ListenableFutureCallback<>() { 104 socketSessionFuture.addCallback(new ListenableFutureCallback<>() {
71 @Override 105 @Override
72 public void onFailure(@NotNull Throwable e) { 106 public void onFailure(@NotNull Throwable e) {
@@ -87,14 +121,18 @@ public class MjWebSocketStarter implements WebSocketStarter { @@ -87,14 +121,18 @@ public class MjWebSocketStarter implements WebSocketStarter {
87 } 121 }
88 122
89 private void onSocketFailure(int code, String reason) { 123 private void onSocketFailure(int code, String reason) {
  124 + // 1001异常可以忽略
90 if (code == 1001) { 125 if (code == 1001) {
91 return; 126 return;
92 } 127 }
  128 + // 关闭 socket
93 closeSocketSessionWhenIsOpen(); 129 closeSocketSessionWhenIsOpen();
  130 + // 没有运行通知
94 if (!this.running) { 131 if (!this.running) {
95 notifyWssLock(code, reason); 132 notifyWssLock(code, reason);
96 return; 133 return;
97 } 134 }
  135 + // 已经运行先设置为false,发起
98 this.running = false; 136 this.running = false;
99 if (code >= 4000) { 137 if (code >= 4000) {
100 log.warn("[wss-{}] Can't reconnect! Account disabled. Closed by {}({}).", this.midjourneyConfig.getChannelId(), code, reason); 138 log.warn("[wss-{}] Can't reconnect! Account disabled. Closed by {}({}).", this.midjourneyConfig.getChannelId(), code, reason);
@@ -107,36 +145,34 @@ public class MjWebSocketStarter implements WebSocketStarter { @@ -107,36 +145,34 @@ public class MjWebSocketStarter implements WebSocketStarter {
107 } 145 }
108 } 146 }
109 147
  148 + /**
  149 + * 重连
  150 + */
110 private void tryReconnect() { 151 private void tryReconnect() {
111 try { 152 try {
112 tryStart(true); 153 tryStart(true);
113 } catch (Exception e) { 154 } catch (Exception e) {
114 - if (e instanceof TimeoutException) {  
115 - closeSocketSessionWhenIsOpen();  
116 - }  
117 - log.warn("[wss-{}] Reconnect fail: {}, Try new connection...", this.midjourneyConfig.getChannelId(), e.getMessage()); 155 + log.warn("[wss-{}] Reconnect fail: {}, Try new connection...", this.midjourneyConfig.getChannelId(), e.getMessage());
118 ThreadUtil.sleep(1000); 156 ThreadUtil.sleep(1000);
119 tryNewConnect(); 157 tryNewConnect();
120 } 158 }
121 } 159 }
122 160
123 private void tryNewConnect() { 161 private void tryNewConnect() {
  162 + // 链接重试次数5
124 for (int i = 1; i <= CONNECT_RETRY_LIMIT; i++) { 163 for (int i = 1; i <= CONNECT_RETRY_LIMIT; i++) {
125 try { 164 try {
126 tryStart(false); 165 tryStart(false);
127 return; 166 return;
128 } catch (Exception e) { 167 } catch (Exception e) {
129 - if (e instanceof TimeoutException) {  
130 - closeSocketSessionWhenIsOpen();  
131 - }  
132 - log.warn("[wss-{}] New connect fail ({}): {}", this.midjourneyConfig.getChannelId(), i, e.getMessage()); 168 + log.warn("[wss-{}] New connect fail ({}): {}", this.midjourneyConfig.getChannelId(), i, e.getMessage());
133 ThreadUtil.sleep(5000); 169 ThreadUtil.sleep(5000);
134 } 170 }
135 } 171 }
136 log.error("[wss-{}] Account disabled", this.midjourneyConfig.getChannelId()); 172 log.error("[wss-{}] Account disabled", this.midjourneyConfig.getChannelId());
137 } 173 }
138 174
139 - public void tryStart(boolean reconnect) throws Exception { 175 + public void tryStart(boolean reconnect) {
140 start(reconnect); 176 start(reconnect);
141 } 177 }
142 178
@@ -144,6 +180,9 @@ public class MjWebSocketStarter implements WebSocketStarter { @@ -144,6 +180,9 @@ public class MjWebSocketStarter implements WebSocketStarter {
144 System.err.println("notifyWssLock: " + code + " - " + reason); 180 System.err.println("notifyWssLock: " + code + " - " + reason);
145 } 181 }
146 182
  183 + /**
  184 + * 关闭 socket session
  185 + */
147 private void closeSocketSessionWhenIsOpen() { 186 private void closeSocketSessionWhenIsOpen() {
148 try { 187 try {
149 if (this.webSocketSession != null && this.webSocketSession.isOpen()) { 188 if (this.webSocketSession != null && this.webSocketSession.isOpen()) {
@@ -161,6 +200,20 @@ public class MjWebSocketStarter implements WebSocketStarter { @@ -161,6 +200,20 @@ public class MjWebSocketStarter implements WebSocketStarter {
161 return this.wssServer; 200 return this.wssServer;
162 } 201 }
163 202
164 - public record ResumeData(String sessionId, Object sequence, String resumeGatewayUrl) { 203 + @Getter
  204 + public static class ResumeData {
  205 +
  206 + public ResumeData(String sessionId, Object sequence, String resumeGatewayUrl) {
  207 + this.sessionId = sessionId;
  208 + this.sequence = sequence;
  209 + this.resumeGatewayUrl = resumeGatewayUrl;
  210 + }
  211 +
  212 + /**
  213 + * socket session
  214 + */
  215 + private final String sessionId;
  216 + private final Object sequence;
  217 + private final String resumeGatewayUrl;
165 } 218 }
166 } 219 }
167 \ No newline at end of file 220 \ No newline at end of file
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/handler/MjWebSocketHandler.java
@@ -30,16 +30,41 @@ import java.util.concurrent.TimeUnit; @@ -30,16 +30,41 @@ import java.util.concurrent.TimeUnit;
30 30
31 @Slf4j 31 @Slf4j
32 public class MjWebSocketHandler implements WebSocketHandler { 32 public class MjWebSocketHandler implements WebSocketHandler {
  33 + /**
  34 + * close 错误码:重连
  35 + */
33 public static final int CLOSE_CODE_RECONNECT = 2001; 36 public static final int CLOSE_CODE_RECONNECT = 2001;
  37 + /**
  38 + * close 错误码:无效、作废
  39 + */
34 public static final int CLOSE_CODE_INVALIDATE = 1009; 40 public static final int CLOSE_CODE_INVALIDATE = 1009;
  41 + /**
  42 + * close 错误码:异常
  43 + */
35 public static final int CLOSE_CODE_EXCEPTION = 1011; 44 public static final int CLOSE_CODE_EXCEPTION = 1011;
36 - 45 + /**
  46 + * mj配置文件
  47 + */
37 private final MidjourneyConfig midjourneyConfig; 48 private final MidjourneyConfig midjourneyConfig;
  49 + /**
  50 + * mj 消息监听
  51 + */
38 private final MjMessageListener userMessageListener; 52 private final MjMessageListener userMessageListener;
  53 + /**
  54 + * 成功回调
  55 + */
39 private final SuccessCallback successCallback; 56 private final SuccessCallback successCallback;
  57 + /**
  58 + * 失败回调
  59 + */
40 private final FailureCallback failureCallback; 60 private final FailureCallback failureCallback;
41 - 61 + /**
  62 + * 心跳执行器
  63 + */
42 private final ScheduledExecutorService heartExecutor; 64 private final ScheduledExecutorService heartExecutor;
  65 + /**
  66 + * auth数据
  67 + */
43 private final DataObject authData; 68 private final DataObject authData;
44 69
45 @Setter 70 @Setter
@@ -55,6 +80,9 @@ public class MjWebSocketHandler implements WebSocketHandler { @@ -55,6 +80,9 @@ public class MjWebSocketHandler implements WebSocketHandler {
55 private Future<?> heartbeatInterval; 80 private Future<?> heartbeatInterval;
56 private Future<?> heartbeatTimeout; 81 private Future<?> heartbeatTimeout;
57 82
  83 + /**
  84 + * 处理 message 消息的 Decompressor
  85 + */
58 private final Decompressor decompressor = new ZlibDecompressor(2048); 86 private final Decompressor decompressor = new ZlibDecompressor(2048);
59 87
60 public MjWebSocketHandler(MidjourneyConfig account, 88 public MjWebSocketHandler(MidjourneyConfig account,
@@ -77,11 +105,13 @@ public class MjWebSocketHandler implements WebSocketHandler { @@ -77,11 +105,13 @@ public class MjWebSocketHandler implements WebSocketHandler {
77 @Override 105 @Override
78 public void handleTransportError(@NotNull WebSocketSession session, @NotNull Throwable e) throws Exception { 106 public void handleTransportError(@NotNull WebSocketSession session, @NotNull Throwable e) throws Exception {
79 log.error("[wss-{}] Transport error", this.midjourneyConfig.getChannelId(), e); 107 log.error("[wss-{}] Transport error", this.midjourneyConfig.getChannelId(), e);
  108 + // 通知链接异常
80 onFailure(CLOSE_CODE_EXCEPTION, "transport error"); 109 onFailure(CLOSE_CODE_EXCEPTION, "transport error");
81 } 110 }
82 111
83 @Override 112 @Override
84 public void afterConnectionClosed(@NotNull WebSocketSession session, @NotNull CloseStatus closeStatus) throws Exception { 113 public void afterConnectionClosed(@NotNull WebSocketSession session, @NotNull CloseStatus closeStatus) throws Exception {
  114 + // 链接关闭
85 onFailure(closeStatus.getCode(), closeStatus.getReason()); 115 onFailure(closeStatus.getCode(), closeStatus.getReason());
86 } 116 }
87 117
@@ -92,13 +122,18 @@ public class MjWebSocketHandler implements WebSocketHandler { @@ -92,13 +122,18 @@ public class MjWebSocketHandler implements WebSocketHandler {
92 122
93 @Override 123 @Override
94 public void handleMessage(@NotNull WebSocketSession session, WebSocketMessage<?> message) throws Exception { 124 public void handleMessage(@NotNull WebSocketSession session, WebSocketMessage<?> message) throws Exception {
  125 + // 获取 message 消息
95 ByteBuffer buffer = (ByteBuffer) message.getPayload(); 126 ByteBuffer buffer = (ByteBuffer) message.getPayload();
  127 + // 解析 message
96 byte[] decompressed = decompressor.decompress(buffer.array()); 128 byte[] decompressed = decompressor.decompress(buffer.array());
97 if (decompressed == null) { 129 if (decompressed == null) {
98 return; 130 return;
99 } 131 }
  132 + // 转换 json
100 String json = new String(decompressed, StandardCharsets.UTF_8); 133 String json = new String(decompressed, StandardCharsets.UTF_8);
  134 + // 转换 jda 自带的 dataObject(和json object 差不多)
101 DataObject data = DataObject.fromJson(json); 135 DataObject data = DataObject.fromJson(json);
  136 + // 获取消息类型
102 int opCode = data.getInt("op"); 137 int opCode = data.getInt("op");
103 switch (opCode) { 138 switch (opCode) {
104 case WebSocketCode.HEARTBEAT -> handleHeartbeat(session); 139 case WebSocketCode.HEARTBEAT -> handleHeartbeat(session);