diff --git a/njzscloud-common/njzscloud-common-core/src/main/java/com/njzscloud/common/core/jackson/Jackson.java b/njzscloud-common/njzscloud-common-core/src/main/java/com/njzscloud/common/core/jackson/Jackson.java index dfd02b9..7cd2ee1 100644 --- a/njzscloud-common/njzscloud-common-core/src/main/java/com/njzscloud/common/core/jackson/Jackson.java +++ b/njzscloud-common/njzscloud-common-core/src/main/java/com/njzscloud/common/core/jackson/Jackson.java @@ -61,7 +61,7 @@ public class Jackson { try { return objectMapper.writeValueAsString(o); } catch (JsonProcessingException e) { - log.error("Jackson 序列化失败", e); + // log.error("Jackson 序列化失败", e); throw Exceptions.error(e, "Jackson 序列化失败"); } } @@ -76,7 +76,7 @@ public class Jackson { try { return objectMapper.writeValueAsBytes(o); } catch (JsonProcessingException e) { - log.error("Jackson 序列化失败", e); + // log.error("Jackson 序列化失败", e); throw Exceptions.error(e, "Jackson 序列化失败"); } } @@ -94,7 +94,7 @@ public class Jackson { try { return objectMapper.readValue(json, objectMapper.getTypeFactory().constructType(type)); } catch (JsonProcessingException e) { - log.error("Jackson 反序列化失败", e); + // log.error("Jackson 反序列化失败", e); throw Exceptions.error(e, "Jackson 反序列化失败"); } } @@ -103,7 +103,7 @@ public class Jackson { try { return objectMapper.readValue(json, objectMapper.getTypeFactory().constructType(type)); } catch (Exception e) { - log.error("Jackson 反序列化失败", e); + // log.error("Jackson 反序列化失败", e); throw Exceptions.error(e, "Jackson 反序列化失败"); } } @@ -112,7 +112,7 @@ public class Jackson { try { return objectMapper.readValue(json, objectMapper.getTypeFactory().constructType(type)); } catch (Exception e) { - log.error("Jackson 反序列化失败", e); + // log.error("Jackson 反序列化失败", e); throw Exceptions.error(e, "Jackson 反序列化失败"); } } @@ -128,7 +128,7 @@ public class Jackson { try { return xmlMapper.writeValueAsString(o); } catch (JsonProcessingException e) { - log.error("Jackson 序列化失败", e); + // log.error("Jackson 序列化失败", e); throw Exceptions.error(e, "Jackson 序列化失败"); } } @@ -143,7 +143,7 @@ public class Jackson { try { return xmlMapper.writeValueAsBytes(o); } catch (JsonProcessingException e) { - log.error("Jackson 序列化失败", e); + // log.error("Jackson 序列化失败", e); throw Exceptions.error(e, "Jackson 序列化失败"); } } @@ -152,7 +152,7 @@ public class Jackson { try { return xmlMapper.readValue(json, xmlMapper.getTypeFactory().constructType(type)); } catch (JsonProcessingException e) { - log.error("Jackson 反序列化失败", e); + // log.error("Jackson 反序列化失败", e); throw Exceptions.error(e, "Jackson 反序列化失败"); } } @@ -161,7 +161,7 @@ public class Jackson { try { return xmlMapper.readValue(json, xmlMapper.getTypeFactory().constructType(type)); } catch (Exception e) { - log.error("Jackson 反序列化失败", e); + // log.error("Jackson 反序列化失败", e); throw Exceptions.error(e, "Jackson 反序列化失败"); } } @@ -170,7 +170,7 @@ public class Jackson { try { return xmlMapper.readValue(json, xmlMapper.getTypeFactory().constructType(type)); } catch (Exception e) { - log.error("Jackson 反序列化失败", e); + // log.error("Jackson 反序列化失败", e); throw Exceptions.error(e, "Jackson 反序列化失败"); } } diff --git a/njzscloud-common/njzscloud-common-ws/pom.xml b/njzscloud-common/njzscloud-common-ws/pom.xml new file mode 100644 index 0000000..ae641b0 --- /dev/null +++ b/njzscloud-common/njzscloud-common-ws/pom.xml @@ -0,0 +1,38 @@ + + 4.0.0 + + com.njzscloud + njzscloud-common + 0.0.1 + + + njzscloud-common-ws + jar + + + UTF-8 + + + + + com.njzscloud + njzscloud-common-core + provided + + + com.njzscloud + njzscloud-common-security + provided + + + org.springframework.boot + spring-boot-starter-web + provided + + + org.springframework + spring-websocket + + + diff --git a/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/config/WebSocketAutoConfiguration.java b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/config/WebSocketAutoConfiguration.java new file mode 100644 index 0000000..29870d3 --- /dev/null +++ b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/config/WebSocketAutoConfiguration.java @@ -0,0 +1,65 @@ +package com.njzscloud.common.ws.config; + +import com.njzscloud.common.ws.support.ImpartialWebSocketHandler; +import com.njzscloud.common.ws.support.TokenHandshakeInterceptor; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.unit.DataSize; +import org.springframework.web.socket.config.annotation.EnableWebSocket; +import org.springframework.web.socket.config.annotation.WebSocketConfigurer; +import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; +import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean; + +import java.time.Duration; + +@Configuration +@EnableWebSocket +@RequiredArgsConstructor +@EnableConfigurationProperties(WebsocketProperties.class) +public class WebSocketAutoConfiguration implements WebSocketConfigurer { + + private final WebsocketProperties websocketProperties; + + // websocket-heartbeat-js + + @Override + public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { + String path = websocketProperties.getPath(); + registry.addHandler(new ImpartialWebSocketHandler(), path == null || path.isEmpty() ? "/fdx" : path) + .addInterceptors(new TokenHandshakeInterceptor()) + .setAllowedOrigins("*"); + } + + @Bean + public ServletServerContainerFactoryBean createWebSocketContainer() { + Duration asyncSendTimeout = websocketProperties.getAsyncSendTimeout(); + Duration maxSessionIdleTimeout = websocketProperties.getMaxSessionIdleTimeout(); + DataSize maxTextMessageBufferSize = websocketProperties.getMaxTextMessageBufferSize(); + DataSize maxBinaryMessageBufferSize = websocketProperties.getMaxBinaryMessageBufferSize(); + ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); + + if (asyncSendTimeout != null) { + long seconds = asyncSendTimeout.getSeconds(); + container.setAsyncSendTimeout(seconds * 1000); + } + + if (maxSessionIdleTimeout != null) { + long seconds = maxSessionIdleTimeout.getSeconds(); + container.setMaxSessionIdleTimeout(seconds * 1000); + } + + if (maxTextMessageBufferSize != null) { + long size = maxTextMessageBufferSize.toBytes(); + container.setMaxTextMessageBufferSize((int) size); + } + + if (maxBinaryMessageBufferSize != null) { + long size = maxBinaryMessageBufferSize.toBytes(); + container.setMaxBinaryMessageBufferSize((int) size); + } + + return container; + } +} diff --git a/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/config/WebsocketProperties.java b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/config/WebsocketProperties.java new file mode 100644 index 0000000..3f2c0c1 --- /dev/null +++ b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/config/WebsocketProperties.java @@ -0,0 +1,41 @@ +package com.njzscloud.common.ws.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.util.unit.DataSize; + +import java.time.Duration; + +/** + * websocket 配置 + */ +@Getter +@Setter +@ConfigurationProperties("ws") +public class WebsocketProperties { + /** + * websocket 请求地址, 默认 /fdx + */ + private String path = "/fdx"; + + /** + * 发送超时时间 + */ + private Duration asyncSendTimeout; + + /** + * 会话超时时间, 默认 15min + */ + private Duration maxSessionIdleTimeout = Duration.ofMinutes(15); + + /** + * 文本消息缓存大小, 默认 2MB + */ + private DataSize maxTextMessageBufferSize = DataSize.ofMegabytes(2); + + /** + * 二进制消息缓存大小, 默认 2MB + */ + private DataSize maxBinaryMessageBufferSize = DataSize.ofMegabytes(2); +} diff --git a/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/Constants.java b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/Constants.java new file mode 100644 index 0000000..adb1b09 --- /dev/null +++ b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/Constants.java @@ -0,0 +1,22 @@ +package com.njzscloud.common.ws.support; + +/** + * 常量 + */ +public class Constants { + /** + * TOKEN 属性 + */ + public static final String SESSION_ATTRIBUTE_TOKEN = "token"; + + /** + * 客户端 Id 属性 + */ + public static final String SESSION_ATTRIBUTE_CID = "cid"; + + /** + * 用户 Id 属性 + */ + public static final String SESSION_ATTRIBUTE_UID = "uid"; + +} diff --git a/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/ImpartialWebSocketHandler.java b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/ImpartialWebSocketHandler.java new file mode 100644 index 0000000..efa0a82 --- /dev/null +++ b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/ImpartialWebSocketHandler.java @@ -0,0 +1,104 @@ +package com.njzscloud.common.ws.support; + +import com.njzscloud.common.core.ex.ExceptionMsg; +import com.njzscloud.common.core.utils.R; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.socket.*; +import org.springframework.web.socket.handler.AbstractWebSocketHandler; + +import java.util.HashMap; +import java.util.Map; + + +// TODO websocket 消息处理器, 消息转发 + +/** + * websocket 处理器 + * 不支持分片消息 + * 支持文本、二进制信息 + */ +@Slf4j +public class ImpartialWebSocketHandler extends AbstractWebSocketHandler { + + @Override + public void afterConnectionEstablished(WebSocketSession session) throws Exception { + Map attributes = session.getAttributes(); + String token = (String) attributes.get(Constants.SESSION_ATTRIBUTE_TOKEN); + String cid = (String) attributes.get(Constants.SESSION_ATTRIBUTE_CID); + String uid = (String) attributes.get(Constants.SESSION_ATTRIBUTE_UID); + + Websocket.optLink(false, it -> { + Wsdoor wsdoor = new Wsdoor(uid, cid, token, session); + Map map = it.get(uid); + if (map == null) { + map = new HashMap<>(); + map.put(cid, wsdoor); + it.put(uid, map); + return; + } + map.put(cid, wsdoor); + }); + + Websocket.send(uid, cid, new WsMsg().setEvent("connected") + .setData(R.success(cid, "连接成功")) + ); + } + + @Override + protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { + Map attributes = session.getAttributes(); + String cid = (String) attributes.get(Constants.SESSION_ATTRIBUTE_CID); + String uid = (String) attributes.get(Constants.SESSION_ATTRIBUTE_UID); + String payload = message.getPayload(); + WsMsg wsMsg = WsMsg.of(payload); + if (wsMsg == null) { + Websocket.send(uid, cid, new WsMsg().setEvent("error") + .setData(R.failed(ExceptionMsg.CLI_ERR_MSG, "消息格式错误")) + ); + return; + } + Websocket.dispatch(wsMsg + .setUid(uid) + .setCid(cid) + ); + } + + @Override + protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception { + Map attributes = session.getAttributes(); + String token = (String) attributes.get(Constants.SESSION_ATTRIBUTE_TOKEN); + String cid = (String) attributes.get(Constants.SESSION_ATTRIBUTE_CID); + String uid = (String) attributes.get(Constants.SESSION_ATTRIBUTE_UID); + BinaryMessage binaryMessage = new BinaryMessage(message.getPayload().array()); + session.sendMessage(binaryMessage); + + log.info("二进制消息: {}", message.getPayloadLength()); + } + + @Override + protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception { + log.info("Pong..."); + } + + @Override + public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { + Map attributes = session.getAttributes(); + String token = (String) attributes.get(Constants.SESSION_ATTRIBUTE_TOKEN); + String cid = (String) attributes.get(Constants.SESSION_ATTRIBUTE_CID); + String uid = (String) attributes.get(Constants.SESSION_ATTRIBUTE_UID); + log.error("连接失败: {}", cid, exception); + Websocket.optLink(false, uid, it -> it.remove(cid)); + } + + @Override + public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { + Map attributes = session.getAttributes(); + String token = (String) attributes.get(Constants.SESSION_ATTRIBUTE_TOKEN); + String cid = (String) attributes.get(Constants.SESSION_ATTRIBUTE_CID); + String uid = (String) attributes.get(Constants.SESSION_ATTRIBUTE_UID); + log.info("连接关闭: {} {}", cid, closeStatus); + + Websocket.optLink(false, uid, it -> it.remove(cid)); + + } +} diff --git a/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/TokenHandshakeInterceptor.java b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/TokenHandshakeInterceptor.java new file mode 100644 index 0000000..501eab3 --- /dev/null +++ b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/TokenHandshakeInterceptor.java @@ -0,0 +1,73 @@ +package com.njzscloud.common.ws.support; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.njzscloud.common.security.support.UserDetail; +import com.njzscloud.common.security.util.SecurityUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.http.server.ServletServerHttpResponse; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.server.HandshakeInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Map; + +// TODO TOKEN 处理器 + +/** + * websocket Token 拦截器 + * 子协议格式: Auth [token],[cid], 如: Auth aimsn,niosdawq + * token: 用户登录凭证; cid: 客户端号, 推荐使用 token Id + */ +@Slf4j +public class TokenHandshakeInterceptor implements HandshakeInterceptor { + private static final String SEC_WEB_SOCKET_PROTOCOL = "Sec-WebSocket-Protocol"; + // private static final Pattern AUTH_PATTERN = Pattern.compile("^Auth (?[a-zA-Z0-9-.:_~+/]+=*),(?[a-zA-Z0-9-.:_~+/]+=*)$", Pattern.CASE_INSENSITIVE); + private static final String Authorization = "authorization"; + private static final String Cid = "cid"; + + @Override + public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse serverHttpResponse, WebSocketHandler wsHandler, Map attributes) { + try { + HttpServletRequest req = ((ServletServerHttpRequest) request).getServletRequest(); + String authorization = req.getParameter(Authorization); + // String cid = req.getParameter(Cid); + if (StrUtil.isBlank(authorization)/* || StrUtil.isBlank(cid) */) return false; + + String uid = this.resolve(authorization); + if (StrUtil.isBlank(uid)) return false; + String cid = IdUtil.fastSimpleUUID(); + attributes.put(Constants.SESSION_ATTRIBUTE_TOKEN, authorization); + attributes.put(Constants.SESSION_ATTRIBUTE_CID, cid); + attributes.put(Constants.SESSION_ATTRIBUTE_UID, uid); + return true; + } catch (Exception e) { + log.error("[Websocket 拦截器] 数据解析失败", e); + return false; + } + } + + @Override + public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { + try { + HttpServletRequest httpRequest = ((ServletServerHttpRequest) request).getServletRequest(); + HttpServletResponse httpResponse = ((ServletServerHttpResponse) response).getServletResponse(); + String token = httpRequest.getHeader(SEC_WEB_SOCKET_PROTOCOL); + if (StrUtil.isNotEmpty(token)) { + httpResponse.addHeader(SEC_WEB_SOCKET_PROTOCOL, token); + } + } catch (ClassCastException e) { + log.error("[Websocket 拦截器] 类型转换失败: {}、{}", request.getClass(), response.getClass(), e); + } + } + + private String resolve(String token) { + UserDetail userDetail = SecurityUtil.parseToken(token); + if (userDetail == null) return null; + return userDetail.getUserId().toString(); + } +} diff --git a/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/WSListener.java b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/WSListener.java new file mode 100644 index 0000000..86b5c3b --- /dev/null +++ b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/WSListener.java @@ -0,0 +1,10 @@ +package com.njzscloud.common.ws.support; + +import java.lang.annotation.*; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface WSListener { + String value(); +} diff --git a/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/Websocket.java b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/Websocket.java new file mode 100644 index 0000000..e8c430a --- /dev/null +++ b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/Websocket.java @@ -0,0 +1,176 @@ +package com.njzscloud.common.ws.support; + +import lombok.extern.slf4j.Slf4j; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Consumer; + +/** + * websocket 工具 + */ +@Slf4j +public class Websocket { + /** + * 连接缓存 + * 用户Id:客户端Id:Wsdoor + */ + static final Map> LINK_CACHE = new HashMap<>(); + static final Map> LISTENERS = new ConcurrentHashMap<>(); + + static final ReentrantReadWriteLock LINK_CACHE_LOCK = new ReentrantReadWriteLock(); + + static final ReentrantReadWriteLock.ReadLock LINK_CACHE_READ_LOCK = LINK_CACHE_LOCK.readLock(); + + static final ReentrantReadWriteLock.WriteLock LINK_CACHE_WRITE_LOCK = LINK_CACHE_LOCK.writeLock(); + + public static boolean online(String uid, String cid) { + Websocket.LINK_CACHE_READ_LOCK.lock(); + try { + return LINK_CACHE.containsKey(uid) && LINK_CACHE.get(uid).containsKey(cid); + } finally { + Websocket.LINK_CACHE_READ_LOCK.unlock(); + } + } + + /** + * 操作 websocket 连接 + * readonly 为 true 时, 使用读锁, 否则使用写锁 + * + * @param readonly 是否只读 + * @param consumer 操作方法 + */ + static void optLink(boolean readonly, Consumer>> consumer) { + if (readonly) { + try { + Websocket.LINK_CACHE_READ_LOCK.lock(); + consumer.accept(LINK_CACHE); + } finally { + Websocket.LINK_CACHE_READ_LOCK.unlock(); + } + } else { + try { + Websocket.LINK_CACHE_WRITE_LOCK.lock(); + consumer.accept(LINK_CACHE); + } finally { + Websocket.LINK_CACHE_WRITE_LOCK.unlock(); + } + } + } + + /** + * 操作 websocket 连接 + * readonly 为 true 时, 使用读锁, 否则使用写锁 + * + * @param readonly 是否只读 + * @param uid 用户 Id + * @param consumer 操作方法 + */ + static void optLink(boolean readonly, String uid, Consumer> consumer) { + if (readonly) { + try { + Websocket.LINK_CACHE_READ_LOCK.lock(); + Map map = LINK_CACHE.get(uid); + consumer.accept(map); + } finally { + Websocket.LINK_CACHE_READ_LOCK.unlock(); + } + } else { + try { + Websocket.LINK_CACHE_WRITE_LOCK.lock(); + Map map = LINK_CACHE.get(uid); + consumer.accept(map); + } finally { + Websocket.LINK_CACHE_WRITE_LOCK.unlock(); + } + } + } + + /** + * 操作 websocket 连接 + * readonly 为 true 时, 使用读锁, 否则使用写锁 + * + * @param readonly 是否只读 + * @param uid 用户 Id + * @param cid 客户端 Id + * @param consumer 操作方法 + */ + static void optLink(boolean readonly, String uid, String cid, Consumer consumer) { + if (readonly) { + try { + Websocket.LINK_CACHE_READ_LOCK.lock(); + Map map = LINK_CACHE.get(uid); + Wsdoor user = null; + if (map != null) user = map.get(cid); + consumer.accept(user); + } finally { + Websocket.LINK_CACHE_READ_LOCK.unlock(); + } + } else { + try { + Websocket.LINK_CACHE_WRITE_LOCK.lock(); + Map map = LINK_CACHE.get(uid); + Wsdoor user = null; + if (map != null) user = map.get(cid); + consumer.accept(user); + } finally { + Websocket.LINK_CACHE_WRITE_LOCK.unlock(); + } + } + } + + public static void addListener(String event, Consumer consumer) { + LISTENERS.put(event, consumer); + } + + public static void removeListener(String event, Consumer consumer) { + LISTENERS.remove(event, consumer); + } + + public static void dispatch(WsMsg wsMsg) { + if (wsMsg == null) return; + Consumer consumer = LISTENERS.get(wsMsg.event); + if (consumer != null) { + try { + consumer.accept(wsMsg); + } catch (Exception e) { + log.error("WebSocket 消息处理失败, 事件: {}, 数据: {}", wsMsg.event, wsMsg, e); + } + } + } + + /** + * 发送文本数据 + * + * @param uid 用户 Id + * @param cid 客户端 Id + * @param wsMsg 消息 + */ + public static void send(String uid, String cid, WsMsg wsMsg) { + optLink(true, uid, cid, it -> { + if (it != null) it.send(wsMsg); + }); + } + + /** + * 发送文本数据 + * + * @param uid 用户 Id + * @param wsMsg 消息 + */ + public static void send(String uid, WsMsg wsMsg) { + optLink(true, uid, it -> { + if (it == null) return; + + Collection wsdoors = it.values(); + + if (wsdoors.isEmpty()) return; + + for (Wsdoor wsdoor : wsdoors) wsdoor.send(wsMsg); + + }); + } +} diff --git a/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/WsMsg.java b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/WsMsg.java new file mode 100644 index 0000000..b47b963 --- /dev/null +++ b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/WsMsg.java @@ -0,0 +1,56 @@ +package com.njzscloud.common.ws.support; + + +import com.njzscloud.common.core.jackson.Jackson; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import lombok.extern.slf4j.Slf4j; + +/** + * websocket 消息内容 + */ +@Slf4j +@Getter +@Setter +@Accessors(chain = true) +public class WsMsg { + + /** + * 事件 + */ + public String event; + + /** + * 客户端 Id + */ + public String cid; + + /** + * 用户 Id + */ + public String uid; + + /** + * 数据内容 + */ + public Object data; + + public static WsMsg of(String json) { + try { + return Jackson.toBean(json, WsMsg.class); + } catch (Exception e) { + log.error("WebSocket 消息反序列化失败, 数据: {}", json, e); + return null; + } + } + + @Override + public String toString() { + return Jackson.toJsonStr(this); + } + + public T extractData(Class clazz) { + return Jackson.toBean(Jackson.toJsonStr(data), clazz); + } +} diff --git a/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/Wsdoor.java b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/Wsdoor.java new file mode 100644 index 0000000..2b3beda --- /dev/null +++ b/njzscloud-common/njzscloud-common-ws/src/main/java/com/njzscloud/common/ws/support/Wsdoor.java @@ -0,0 +1,49 @@ +package com.njzscloud.common.ws.support; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; + +/** + * websocket 会话描述 + */ +@Slf4j +@RequiredArgsConstructor +public class Wsdoor { + + /** + * 用户 Id + */ + public final String uid; + + /** + * 客户端 Id + */ + public final String cid; + + /** + * TOKEN + */ + public final String token; + + /** + * 连接会话 + */ + public final WebSocketSession session; + + /** + * 发送文本数据 + * + * @param wsMsg 消息 + */ + public boolean send(WsMsg wsMsg) { + try { + session.sendMessage(new TextMessage(wsMsg.toString())); + return true; + } catch (Exception e) { + log.error("消息发送失败", e); + } + return false; + } +} diff --git a/njzscloud-common/njzscloud-common-ws/src/main/resources/META-INF/spring.factories b/njzscloud-common/njzscloud-common-ws/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..9e894b6 --- /dev/null +++ b/njzscloud-common/njzscloud-common-ws/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration = \ + com.njzscloud.common.ws.config.WebSocketAutoConfiguration diff --git a/njzscloud-common/pom.xml b/njzscloud-common/pom.xml index 777e53e..971173a 100644 --- a/njzscloud-common/pom.xml +++ b/njzscloud-common/pom.xml @@ -30,6 +30,7 @@ njzscloud-common-http njzscloud-common-wechat njzscloud-common-localizer + njzscloud-common-ws diff --git a/njzscloud-svr/pom.xml b/njzscloud-svr/pom.xml index 3b3d71f..ace608a 100644 --- a/njzscloud-svr/pom.xml +++ b/njzscloud-svr/pom.xml @@ -45,6 +45,10 @@ com.njzscloud njzscloud-common-sn + + com.njzscloud + njzscloud-common-ws + 待审核、2-->已审核 + */ + private String auditType; private Long stationId; private CheckStatus checkStatus; diff --git a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/service/OrderInfoService.java b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/service/OrderInfoService.java index 94de06c..89fe1dd 100644 --- a/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/service/OrderInfoService.java +++ b/njzscloud-svr/src/main/java/com/njzscloud/supervisory/order/service/OrderInfoService.java @@ -452,6 +452,22 @@ public class OrderInfoService extends ServiceImpl - it.in(CollUtil.isNotEmpty(areaList), "b.area", areaList) - .or(CollUtil.isNotEmpty(cityList), it1 -> it1.in("b.area", cityList)) - .or(CollUtil.isNotEmpty(cityAreaList), it2 -> it2.in("b.area", cityAreaList))) - ; + String auditType = orderPagingSearchParam.getAuditType(); + if ("1".equals(auditType)) { + ew.eq("a.order_status", OrderStatus.YiJieDan) + .and(it -> + it.in(CollUtil.isNotEmpty(areaList), "b.area", areaList).in("a.audit_status", AuditStatus.QuDaiShenHe, AuditStatus.ShiDaiShenHe) + .or(CollUtil.isNotEmpty(cityList), it1 -> it1.in("b.area", cityList).eq("a.audit_status", AuditStatus.ShiDaiShenHe)) + .or(CollUtil.isNotEmpty(cityAreaList), it2 -> it2.in("b.area", cityAreaList).eq("a.audit_status", AuditStatus.DaiShenHe)) + ); + } else if ("2".equals(auditType)) { + ew + .in("a.order_status", OrderStatus.YiJieDan, OrderStatus.QingYunZhong, OrderStatus.YiJinChang, OrderStatus.YiChuChang, OrderStatus.YiWanCheng) + .and(it -> + it.in(CollUtil.isNotEmpty(areaList), "b.area", areaList).eq("a.audit_status", AuditStatus.ShiDaiShenHe) + .or(CollUtil.isNotEmpty(cityList), it1 -> it1.in("b.area", cityList).in("a.audit_status", AuditStatus.TongGuo, AuditStatus.BoHui)) + .or(CollUtil.isNotEmpty(cityAreaList), it2 -> it2.in("b.area", cityAreaList).in("a.audit_status", AuditStatus.TongGuo, AuditStatus.BoHui)) + ); + } else { + ew + .in("a.order_status", OrderStatus.YiJieDan, OrderStatus.QingYunZhong, OrderStatus.YiJinChang, OrderStatus.YiChuChang, OrderStatus.YiWanCheng) + .and(it -> + it.in(CollUtil.isNotEmpty(areaList), "b.area", areaList) + .or(CollUtil.isNotEmpty(cityList), it1 -> it1.in("b.area", cityList)) + .or(CollUtil.isNotEmpty(cityAreaList), it2 -> it2.in("b.area", cityAreaList))) + ; + } } return PageResult.of(baseMapper.paging(page, ew)); @@ -1138,12 +1172,13 @@ public class OrderInfoService extends ServiceImpl list = baseMapper.exportList(ew); List> downList = new ArrayList<>(); int i = 1; - for (OrderExportResult result: list) { + for (OrderExportResult result : list) { Map map = new LinkedHashMap<>(); map.put("序号", i); map.put("日期", result.getTransTime()); @@ -1161,7 +1196,7 @@ public class OrderInfoService extends ServiceImplnjzscloud-common-core 0.0.1 + + com.njzscloud + njzscloud-common-ws + 0.0.1 + com.njzscloud njzscloud-common-sichen diff --git a/z-doc/websocket-tester.html b/z-doc/websocket-tester.html new file mode 100644 index 0000000..6a0a0ef --- /dev/null +++ b/z-doc/websocket-tester.html @@ -0,0 +1,548 @@ + + + + + + WebSocket测试工具 + + + + + + + WebSocket 测试工具 + 简单高效地测试WebSocket连接和通信 + + + + + + WebSocket 地址 + + 未连接 + + + + TOKEN + + + + + 连接 + + + 断开 + + + + + 连接信息 + 未连接到服务器 + + + + + + + + 清除消息 + + + + + + + 连接到WebSocket服务器后,消息将显示在这里 + + + + + + + 发送 + + + + + + + + +
简单高效地测试WebSocket连接和通信
连接到WebSocket服务器后,消息将显示在这里