From 2c62939aeb8c55e351511853a37335f9d24d28a2 Mon Sep 17 00:00:00 2001 From: caoyizhong <1270296080> Date: Fri, 1 Dec 2023 18:59:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0web=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/constant/ModuleNameConstant.java | 4 + .../entity/AftersalesExchangeEntity.java | 17 +++ blade-service/logpm-aftersales/pom.xml | 11 ++ .../aftersales/AftersalesApplication.java | 12 ++ .../aftersales/config/WebSocketConfig.java | 38 +++++++ .../launcher/OpcSessionHandler.java | 78 +++++++++++++ .../aftersales/resp/NoticeWebsocketResp.java | 20 ++++ .../aftersales/service/WebSocketServer.java | 105 ++++++++++++++++++ .../impl/AftersalesExchangeServiceImpl.java | 12 +- 9 files changed, 295 insertions(+), 2 deletions(-) create mode 100644 blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/config/WebSocketConfig.java create mode 100644 blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/launcher/OpcSessionHandler.java create mode 100644 blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/resp/NoticeWebsocketResp.java create mode 100644 blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/service/WebSocketServer.java diff --git a/blade-biz-common/src/main/java/org/springblade/common/constant/ModuleNameConstant.java b/blade-biz-common/src/main/java/org/springblade/common/constant/ModuleNameConstant.java index 95746ccec..153720380 100644 --- a/blade-biz-common/src/main/java/org/springblade/common/constant/ModuleNameConstant.java +++ b/blade-biz-common/src/main/java/org/springblade/common/constant/ModuleNameConstant.java @@ -46,6 +46,10 @@ public interface ModuleNameConstant { * 仓库服务名称 */ String APPLICATION_WAREHOUSE_NAME = "logpm-warehouse"+DEVAUTH; + /** + * 仓库服务名称 + */ + String APPLICATION_WEBSTOCKET_NAME = "logpm-webstocket"+DEVAUTH; /** * 配送签收服务名称 diff --git a/blade-service-api/logpm-aftersales-api/src/main/java/com/logpm/aftersales/entity/AftersalesExchangeEntity.java b/blade-service-api/logpm-aftersales-api/src/main/java/com/logpm/aftersales/entity/AftersalesExchangeEntity.java index 1bc9169c1..2e9dd1da4 100644 --- a/blade-service-api/logpm-aftersales-api/src/main/java/com/logpm/aftersales/entity/AftersalesExchangeEntity.java +++ b/blade-service-api/logpm-aftersales-api/src/main/java/com/logpm/aftersales/entity/AftersalesExchangeEntity.java @@ -92,4 +92,21 @@ public class AftersalesExchangeEntity extends TenantEntity { @ApiModelProperty(value = "仓库ID") private String warehouseId; + + @Override + public String toString() { + return "AftersalesExchangeEntity{" + + "reserve1='" + reserve1 + '\'' + + ", reserve2='" + reserve2 + '\'' + + ", reserve3='" + reserve3 + '\'' + + ", reserve4='" + reserve4 + '\'' + + ", reserve5='" + reserve5 + '\'' + + ", workOrderId='" + workOrderId + '\'' + + ", businessName='" + businessName + '\'' + + ", businessId='" + businessId + '\'' + + ", content='" + content + '\'' + + ", annex='" + annex + '\'' + + ", warehouseId='" + warehouseId + '\'' + + '}'; + } } diff --git a/blade-service/logpm-aftersales/pom.xml b/blade-service/logpm-aftersales/pom.xml index a934b97bb..b093409c6 100644 --- a/blade-service/logpm-aftersales/pom.xml +++ b/blade-service/logpm-aftersales/pom.xml @@ -16,6 +16,17 @@ jar + + + org.springframework.boot + spring-boot-starter-websocket + + + spring-boot-starter-tomcat + org.springframework.boot + + + org.springblade blade-core-boot diff --git a/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/AftersalesApplication.java b/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/AftersalesApplication.java index b0db4c432..dac63b988 100644 --- a/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/AftersalesApplication.java +++ b/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/AftersalesApplication.java @@ -19,6 +19,9 @@ package com.logpm.aftersales; import org.springblade.common.constant.ModuleNameConstant; import org.springblade.core.cloud.client.BladeCloudApplication; import org.springblade.core.launch.BladeApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.web.socket.config.annotation.EnableWebSocket; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * Demo启动器 @@ -26,11 +29,20 @@ import org.springblade.core.launch.BladeApplication; * @author Chill */ @BladeCloudApplication +//@EnableWebSocket public class AftersalesApplication { public static void main(String[] args) { BladeApplication.run(ModuleNameConstant.APPLICATION_AFTERSALES_NAME, AftersalesApplication.class, args); } + /** + * 如果直接使用springboot的内置容器,而不是使用独立的servlet容器,就要注入ServerEndpointExporter,外部容器则不需要。 + */ +/* @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + }*/ + } diff --git a/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/config/WebSocketConfig.java b/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/config/WebSocketConfig.java new file mode 100644 index 000000000..ce3c898b7 --- /dev/null +++ b/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/config/WebSocketConfig.java @@ -0,0 +1,38 @@ +package com.logpm.aftersales.config; + +import com.logpm.aftersales.launcher.OpcSessionHandler; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +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.ServerEndpointExporter; + +import javax.annotation.Resource; + +/** + * 开启WebSocket支持 + * @author 12702 + */ +@Slf4j +@Configuration +@EnableWebSocket +@ConditionalOnProperty(name = "system.websocket.isOpen",havingValue = "true") +//@ConditionalOnProperty(name = "spring.profiles.active",havingValue = "dev") +public class WebSocketConfig implements WebSocketConfigurer { + + @Resource + private OpcSessionHandler opcSessionHandler; + +// @Bean +// public ServerEndpointExporter serverEndpointExporter() { +// return new ServerEndpointExporter(); +// } + + @Override + public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { + registry.addHandler(opcSessionHandler, "/ws/automate").setAllowedOrigins("*"); + } +} diff --git a/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/launcher/OpcSessionHandler.java b/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/launcher/OpcSessionHandler.java new file mode 100644 index 000000000..122e63215 --- /dev/null +++ b/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/launcher/OpcSessionHandler.java @@ -0,0 +1,78 @@ +package com.logpm.aftersales.launcher; + + +import lombok.extern.slf4j.Slf4j; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.*; +import java.io.IOException; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * @author tarzan + */ +@Component +@Slf4j +public class OpcSessionHandler implements WebSocketHandler { + + + + private static final CopyOnWriteArraySet SESSIONS=new CopyOnWriteArraySet<>(); + + + private final static String ALERT="alert"; + private final static String CONTROL="control"; + + @Override + public void afterConnectionEstablished(@NonNull WebSocketSession session) throws Exception { + SESSIONS.add(session); + log.info(session.getId()+" OpcSessionHandler当前在线人数:"+SESSIONS.size()); + } + + @Override + public void handleMessage(WebSocketSession session, WebSocketMessage message) throws Exception { + String msg = message.getPayload().toString(); + log.info("接收消息"+session.getId()+":"+msg); + + } + + + + @Override + public void handleTransportError(WebSocketSession session,@NonNull Throwable exception) throws Exception { + log.error("OpcSessionHandler连接出错"+session.getId()); + SESSIONS.remove(session); + if (!session.isOpen()) { + session.close(); + } + } + + @Override + public void afterConnectionClosed(WebSocketSession session, @NonNull CloseStatus closeStatus) throws Exception { + log.info("OpcSessionHandler关闭连接"+session.getId()); + SESSIONS.remove(session); + } + + + @Override + public boolean supportsPartialMessages() { + return false; + } + + + public void sendMessageToUser(WebSocketSession session, String contents) { + if (session != null && session.isOpen()) { + TextMessage message = new TextMessage(contents); + try { + session.sendMessage(message); + } catch (IOException e) { + log.error(e.getMessage()); + } + } + } + + public void sendMessageToAllUsers(String contents) { + SESSIONS.forEach(session->sendMessageToUser(session, contents)); + } + +} diff --git a/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/resp/NoticeWebsocketResp.java b/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/resp/NoticeWebsocketResp.java new file mode 100644 index 000000000..f947a4fe1 --- /dev/null +++ b/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/resp/NoticeWebsocketResp.java @@ -0,0 +1,20 @@ +package com.logpm.aftersales.resp; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author 12702 + */ +@Data +@ApiModel("ws通知返回对象") +public class NoticeWebsocketResp { + + @ApiModelProperty(value = "通知类型") + private String noticeType; + + @ApiModelProperty(value = "通知内容") + private T noticeInfo; + +} diff --git a/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/service/WebSocketServer.java b/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/service/WebSocketServer.java new file mode 100644 index 000000000..2a65474af --- /dev/null +++ b/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/service/WebSocketServer.java @@ -0,0 +1,105 @@ +package com.logpm.aftersales.service; + + +import com.alibaba.fastjson.JSONObject; +import com.logpm.aftersales.config.WebSocketConfig; +import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.tool.utils.CollectionUtil; +import org.springblade.core.tool.utils.Func; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +import javax.websocket.*; +import javax.websocket.server.ServerEndpoint; +import java.util.List; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * @author tarzan + */ +@ConditionalOnClass(value = WebSocketConfig.class) +@ServerEndpoint("/ws/automate") +@Slf4j +@EqualsAndHashCode +public class WebSocketServer { + + /** 虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。 */ + private static final CopyOnWriteArraySet WEBSOCKET_CLIENTS =new CopyOnWriteArraySet<>(); + /** 当前session */ + private Session session; + /** 当前session订阅的话题 */ + private List topics; + + public WebSocketServer() { + } + + @OnOpen + public void onOpen(Session session) { + this.session = session; + WEBSOCKET_CLIENTS.add(this); + log.info("WebSocketServer有新客户端连接加入:{},当前在线人数为:{}", session.getId(),WEBSOCKET_CLIENTS.size()); + } + + @OnClose + public void onClose() { + WEBSOCKET_CLIENTS.remove(this); + log.info("有一连接关闭:{},当前在线人数为:{}", this.session.getId(), WEBSOCKET_CLIENTS.size()); + } + + @OnMessage + public void onMessage(String message, Session session) { + this.topics= Func.toStrList(message); + log.info("服务端收到客户端[{}]的消息:{}", session.getId(), message); + } + + @OnError + public void onError(Session session, Throwable error) { + log.error("发生错误:"+error.getMessage()); + WEBSOCKET_CLIENTS.remove(this); + log.info("有一连接异常:{},当前在线人数为:{}", session.getId(), WEBSOCKET_CLIENTS.size()); + } + + public void sendData(T data, String topicName) { + JSONObject result=new JSONObject(); + result.put("topic",topicName); + result.put("data",data); + //显示值为null的字段 + // String jsonString = JSON.toJSONString(result, SerializerFeature.WriteMapNullValue); + sendMessage(result.toString(),topicName); + } + + /** + * 发送消息广播 + * + * @param message 消息文本 + * @author tarzan + * @date 2022年12月08日 09:24:34 + */ + public void sendMessage(String message,String topic){ + try { + WEBSOCKET_CLIENTS.forEach(client ->{ + if (client.session.isOpen()&&CollectionUtil.isNotEmpty(client.topics)&&client.topics.contains(topic)) { + client.session.getAsyncRemote().sendText(message); + } + }); + } catch (Exception e) { + log.error(e.getMessage()); + } + } + + public static void sendMessageToAllUsers(String message){ + try { + WEBSOCKET_CLIENTS.forEach(client -> { + if (client.session.isOpen()) { + client.session.getAsyncRemote().sendText(message); + } + }); + } catch (Exception e) { + log.error(e.getMessage()); + } + } + + +} diff --git a/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/service/impl/AftersalesExchangeServiceImpl.java b/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/service/impl/AftersalesExchangeServiceImpl.java index 0c698aebc..8613b1323 100644 --- a/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/service/impl/AftersalesExchangeServiceImpl.java +++ b/blade-service/logpm-aftersales/src/main/java/com/logpm/aftersales/service/impl/AftersalesExchangeServiceImpl.java @@ -19,14 +19,16 @@ package com.logpm.aftersales.service.impl; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.logpm.aftersales.entity.AftersalesExchangeEntity; +import com.logpm.aftersales.service.WebSocketServer; import com.logpm.aftersales.vo.AftersalesExchangeVO; import com.logpm.aftersales.excel.AftersalesExchangeExcel; import com.logpm.aftersales.mapper.AftersalesExchangeMapper; import com.logpm.aftersales.service.IAftersalesExchangeService; +import lombok.AllArgsConstructor; import org.springblade.core.mp.support.Condition; import org.springblade.core.secure.BladeUser; import org.springblade.core.secure.utils.AuthUtil; -import org.springblade.core.tool.utils.AesUtil; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.metadata.IPage; @@ -43,8 +45,11 @@ import java.util.Map; * @since 2023-11-17 */ @Service +@AllArgsConstructor public class AftersalesExchangeServiceImpl extends BaseServiceImpl implements IAftersalesExchangeService { + private static int count=0; + @Override public IPage selectAftersalesExchangePage(IPage page, AftersalesExchangeVO aftersalesExchange) { return page.setRecords(baseMapper.selectAftersalesExchangePage(page, aftersalesExchange)); @@ -69,6 +74,7 @@ public class AftersalesExchangeServiceImpl extends BaseServiceImpl