diff --git a/README.md b/README.md
index 01d7aa7..17093f7 100644
--- a/README.md
+++ b/README.md
@@ -413,10 +413,10 @@ cnpm install
```bash
npm run package
-npm run make
+# npm run make
```
-
+`npm run make` 等于先执行 `npm run package`(即 *Import project into Forge*),再 *Create distributable*
@@ -468,6 +468,12 @@ npm run serve
+### Step3. 启动项目门禁端
+
+编译并运行client-entrance-guard目录下electron项目即可
+
+
+
## 停止项目
### Step1. 停止各个微服务
diff --git a/backend/microservice-provider-access-8002/pom.xml b/backend/microservice-provider-access-8002/pom.xml
index 5cd9249..79c3553 100644
--- a/backend/microservice-provider-access-8002/pom.xml
+++ b/backend/microservice-provider-access-8002/pom.xml
@@ -103,6 +103,12 @@
com.squareup.okhttp3
okhttp
+
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
diff --git a/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/config/WebSocketConfig.java b/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/config/WebSocketConfig.java
new file mode 100644
index 0000000..6cef8c1
--- /dev/null
+++ b/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/config/WebSocketConfig.java
@@ -0,0 +1,28 @@
+package com.cxyxiaomo.epp.access.config;
+
+import org.springframework.boot.web.servlet.ServletContextInitializer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+/**
+ * 开启WebSocket支持
+ */
+@Configuration
+public class WebSocketConfig implements ServletContextInitializer {
+ /**
+ * 这个bean的注册,用于扫描带有@ServerEndpoint的注解成为websocket,如果你使用外置的tomcat就不需要该配置文件
+ */
+ @Bean
+ public ServerEndpointExporter serverEndpointExporter() {
+ return new ServerEndpointExporter();
+ }
+
+ @Override
+ public void onStartup(ServletContext servletContext) throws ServletException {
+
+ }
+}
diff --git a/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/controller/WebSocketServer.java b/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/controller/WebSocketServer.java
new file mode 100644
index 0000000..51dbd61
--- /dev/null
+++ b/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/controller/WebSocketServer.java
@@ -0,0 +1,112 @@
+package com.cxyxiaomo.epp.access.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.websocket.*;
+import javax.websocket.server.PathParam;
+import javax.websocket.server.ServerEndpoint;
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+@ServerEndpoint("/websocket/{userId}")
+@Component
+@Slf4j
+public class WebSocketServer {
+ // 与某个客户端的连接会话,需要通过它来给客户端发送数据
+ private Session session;
+
+ // session集合,存放对应的session
+ private static ConcurrentHashMap sessionPool = new ConcurrentHashMap<>();
+
+ // concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。
+ private static CopyOnWriteArraySet webSocketSet = new CopyOnWriteArraySet<>();
+
+ /**
+ * 建立WebSocket连接
+ *
+ * @param session
+ * @param userId 用户ID
+ */
+ @OnOpen
+ public void onOpen(Session session, @PathParam(value = "userId") Integer userId) {
+ log.info("WebSocket建立连接中,连接用户ID:{}", userId);
+ try {
+ Session historySession = sessionPool.get(userId);
+ // historySession不为空,说明已经有人登陆账号,应该删除登陆的WebSocket对象
+ if (historySession != null) {
+ webSocketSet.remove(historySession);
+ historySession.close();
+ }
+ } catch (IOException e) {
+ log.error("重复登录异常,错误信息:" + e.getMessage(), e);
+ }
+ // 建立连接
+ this.session = session;
+ webSocketSet.add(this);
+ sessionPool.put(userId, session);
+ log.info("建立连接完成,当前在线人数为:{}", webSocketSet.size());
+ }
+
+ /**
+ * 发生错误
+ *
+ * @param throwable e
+ */
+ @OnError
+ public void onError(Throwable throwable) {
+ throwable.printStackTrace();
+ }
+
+ /**
+ * 连接关闭
+ */
+ @OnClose
+ public void onClose() {
+ webSocketSet.remove(this);
+ log.info("连接断开,当前在线人数为:{}", webSocketSet.size());
+ }
+
+ /**
+ * 接收客户端消息
+ *
+ * @param message 接收的消息
+ */
+ @OnMessage
+ public void onMessage(String message) {
+ log.info("收到客户端发来的消息:{}", message);
+ }
+
+ /**
+ * 推送消息到指定用户
+ *
+ * @param userId 用户ID
+ * @param message 发送的消息
+ */
+ public static void sendMessageByUser(Integer userId, String message) {
+ log.info("用户ID:" + userId + ",推送内容:" + message);
+ Session session = sessionPool.get(userId);
+ try {
+ session.getBasicRemote().sendText(message);
+ } catch (IOException e) {
+ log.error("推送消息到指定用户发生错误:" + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 群发消息
+ *
+ * @param message 发送的消息
+ */
+ public static void sendAllMessage(String message) {
+ log.info("发送消息:{}", message);
+ for (WebSocketServer webSocket : webSocketSet) {
+ try {
+ webSocket.session.getBasicRemote().sendText(message);
+ } catch (IOException e) {
+ log.error("群发消息发生错误:" + e.getMessage(), e);
+ }
+ }
+ }
+}
diff --git a/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/pojo/WebSocketData.java b/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/pojo/WebSocketData.java
new file mode 100644
index 0000000..6051cc1
--- /dev/null
+++ b/backend/microservice-provider-access-8002/src/main/java/com/cxyxiaomo/epp/access/pojo/WebSocketData.java
@@ -0,0 +1,19 @@
+package com.cxyxiaomo.epp.access.pojo;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+@Data
+@NoArgsConstructor
+@Accessors(chain = true) // 链式写法
+public class WebSocketData {
+ // 小程序: "miniprogram" 门禁: "guard"
+ private String type;
+
+ // 需要执行的动作
+ private String action;
+
+ // 传输的数据(JSON字符串)
+ private String json;
+}
diff --git a/backend/pom.xml b/backend/pom.xml
index 4ad23f7..361e908 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -127,6 +127,13 @@
4.10.0
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+ 2.7.6
+
+
com.alibaba.fastjson2
diff --git a/postman-collection/epp.postman_collection.json b/postman-collection/epp.postman_collection.json
index 8954dfe..7896b7b 100644
--- a/postman-collection/epp.postman_collection.json
+++ b/postman-collection/epp.postman_collection.json
@@ -118,6 +118,104 @@
}
},
"response": []
+ },
+ {
+ "name": "[微服务] 获取不限制的小程序码",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8002/access/wechat/getUnlimitedQRCode?scene=t=1&page=pages/index/index",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8002",
+ "path": [
+ "access",
+ "wechat",
+ "getUnlimitedQRCode"
+ ],
+ "query": [
+ {
+ "key": "envVersion",
+ "value": "develop",
+ "disabled": true
+ },
+ {
+ "key": "scene",
+ "value": "t=1"
+ },
+ {
+ "key": "page",
+ "value": "pages/index/index"
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "[线上] 获取不限制的小程序码",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "https://epp.only4.work/access/wechat/getUnlimitedQRCode?scene=t=1&page=pages/index/index",
+ "protocol": "https",
+ "host": [
+ "epp",
+ "only4",
+ "work"
+ ],
+ "path": [
+ "access",
+ "wechat",
+ "getUnlimitedQRCode"
+ ],
+ "query": [
+ {
+ "key": "envVersion",
+ "value": "develop",
+ "disabled": true
+ },
+ {
+ "key": "scene",
+ "value": "t=1"
+ },
+ {
+ "key": "page",
+ "value": "pages/index/index"
+ }
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "TestProvider",
+ "item": [
+ {
+ "name": "http://localhost:8011/hi/1",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "http://localhost:8011/hi/1",
+ "protocol": "http",
+ "host": [
+ "localhost"
+ ],
+ "port": "8011",
+ "path": [
+ "hi",
+ "1"
+ ]
+ }
+ },
+ "response": []
}
]
}
diff --git a/test-code/.gitignore b/test-code/.gitignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/test-code/.gitignore
@@ -0,0 +1 @@
+node_modules
diff --git a/test-code/package-lock.json b/test-code/package-lock.json
new file mode 100644
index 0000000..91a3a44
--- /dev/null
+++ b/test-code/package-lock.json
@@ -0,0 +1,44 @@
+{
+ "name": "test-code",
+ "version": "1.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "test-code",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "ws": "^8.11.0"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmmirror.com/ws/-/ws-8.11.0.tgz",
+ "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ }
+ },
+ "dependencies": {
+ "ws": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmmirror.com/ws/-/ws-8.11.0.tgz",
+ "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
+ "requires": {}
+ }
+ }
+}
diff --git a/test-code/package.json b/test-code/package.json
new file mode 100644
index 0000000..6f95043
--- /dev/null
+++ b/test-code/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "test-code",
+ "version": "1.0.0",
+ "description": "",
+ "main": "websocketTest.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "ws": "^8.11.0"
+ }
+}
diff --git a/test-code/websocketTest.js b/test-code/websocketTest.js
new file mode 100644
index 0000000..8ce01a7
--- /dev/null
+++ b/test-code/websocketTest.js
@@ -0,0 +1,45 @@
+'use strict';
+
+const WebSocket = require('ws');
+
+const webSocketUrl = 'ws://localhost:8002/websocket/1';
+
+global.ws = null; // WebSocket 实例对象
+
+/**
+ * 启动完毕,输出配置信息
+ */
+console.log("Start running ...", "process.env", process.env);
+
+function createWebSocket() {
+ //申请一个WebSocket对象,参数是服务端地址,同http协议使用http://开头一样,WebSocket协议的url使用ws://开头,另外安全的WebSocket协议使用wss://开头
+ global.ws = new WebSocket(webSocketUrl);
+
+ // 当WebSocket创建成功时,触发onopen事件
+ global.ws.onopen = function () {
+ console.log("webhook is open.");
+ }
+
+ // 当客户端收到服务端发送的关闭连接请求时,触发onclose事件
+ global.ws.onclose = function (e) {
+ console.log("webhook is close.");
+ console.log("未知错误被关闭,等待 1s 尝试重新建立连接...");
+ setTimeout(function () {
+ createWebSocket();
+ }, 1000);
+ }
+
+ // 如果出现连接、处理、接收、发送数据失败的时候触发onerror事件
+ global.ws.onerror = function (e) {
+ console.log(e.error);
+ }
+
+ // 当客户端收到服务端发来的消息时,触发onmessage事件,参数e.data包含server传递过来的数据
+ global.ws.onmessage = function (e) {
+ var data = JSON.parse(e.data);
+ console.log(data);
+ }
+}
+
+// 创建连接
+createWebSocket();