Commit 44e44dc4bb1431a293afd2d3cd9c57fccb65258e
1 parent
5044a581
增加注释
Showing
3 changed files
with
113 additions
and
53 deletions
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); |