From 24d943fe431d02480f1f46f52cd16b239bfe5e36 Mon Sep 17 00:00:00 2001
From: liu <liujiatong112@163.com>
Date: Tue, 5 Dec 2023 17:58:23 +0800
Subject: [PATCH] =?UTF-8?q?=E9=97=AE=E8=AF=8A=E5=A4=A7=E5=8E=85=E4=BB=A3?=
 =?UTF-8?q?=E7=A0=81=E6=8F=90=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pom.xml                                       |  1 +
 virtual-patient-common/pom.xml                |  6 +++
 .../constant/UserTokenConstant.java           |  3 +-
 virtual-patient-web/pom.xml                   |  1 +
 .../controller/WebSocketServer.java           | 26 ++++++-----
 .../pojo/vo/AskPhysicalResultReqVO.java       |  1 -
 .../service/impl/DiagnoseHallServiceImpl.java | 13 +++---
 .../usermanage/UserResourceCheck.java         | 45 +++++++++++++++++++
 .../usermanage/UserWebSocketDTO.java          |  1 +
 9 files changed, 75 insertions(+), 22 deletions(-)
 create mode 100644 virtual-patient-web/src/main/java/com/supervision/usermanage/UserResourceCheck.java

diff --git a/pom.xml b/pom.xml
index a4a59a2e..48b22f29 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,6 +43,7 @@
         <freemarker.version>2.3.31</freemarker.version>
         <mysql-connector-java.version>8.0.26</mysql-connector-java.version>
         <io-swagger.version>1.5.22</io-swagger.version>
+        <lock4j.version>2.2.5</lock4j.version>
     </properties>
 
     <dependencyManagement>
diff --git a/virtual-patient-common/pom.xml b/virtual-patient-common/pom.xml
index 39517bdf..4eb50d36 100644
--- a/virtual-patient-common/pom.xml
+++ b/virtual-patient-common/pom.xml
@@ -38,6 +38,12 @@
             <groupId>redis.clients</groupId>
             <artifactId>jedis</artifactId>
         </dependency>
+        <!--redis分布式锁 https://gitee.com/baomidou/lock4j -->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>lock4j-redis-template-spring-boot-starter</artifactId>
+            <version>${lock4j.version}</version>
+        </dependency>
 
             <!-- 其他依赖 -->
         <dependency>
diff --git a/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java b/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java
index 2e1d4769..a45b5bd2 100644
--- a/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java
+++ b/virtual-patient-common/src/main/java/com/supervision/constant/UserTokenConstant.java
@@ -1,7 +1,8 @@
 package com.supervision.constant;
 
 public interface UserTokenConstant {
-    String USER_ID_CACHE = "USER:ID:CACHE";
+    String USER_WEBSOCKET_CACHE = "USER:ID:CACHE";
 
     String KICK_CHANNEL = "USER:KICK:CHANNEL";
+
 }
diff --git a/virtual-patient-web/pom.xml b/virtual-patient-web/pom.xml
index 34155a28..4830043f 100644
--- a/virtual-patient-web/pom.xml
+++ b/virtual-patient-web/pom.xml
@@ -58,6 +58,7 @@
             <artifactId>spring-boot-starter-websocket</artifactId>
         </dependency>
 
+
     </dependencies>
 
     <build>
diff --git a/virtual-patient-web/src/main/java/com/supervision/controller/WebSocketServer.java b/virtual-patient-web/src/main/java/com/supervision/controller/WebSocketServer.java
index 05b1b078..0aa39942 100644
--- a/virtual-patient-web/src/main/java/com/supervision/controller/WebSocketServer.java
+++ b/virtual-patient-web/src/main/java/com/supervision/controller/WebSocketServer.java
@@ -2,12 +2,11 @@ package com.supervision.controller;
 
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
-import cn.hutool.json.JSONUtil;
 import com.supervision.constant.UserTokenConstant;
-import com.supervision.usermanage.UserWebSocketDTO;
-import lombok.RequiredArgsConstructor;
+import com.supervision.usermanage.UserResourceCheck;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
 
@@ -28,16 +27,19 @@ public class WebSocketServer {
     //concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
     private static final ConcurrentHashMap<String, Session> SESSION_POOL = new ConcurrentHashMap<>();
 
+    @Value("${human.resourceMaxNumber}")
+    private String resourceNumber;
+
+    @Autowired
+    private UserResourceCheck userResourceCheck;
+
     /**
      * 有客户端连接成功
      */
     @OnOpen
-    public void onOpen(Session session, @PathParam(value = "uid") String uid) throws IOException {
-        log.info("用户:{}登录,缓存到Redis", uid);
-        // 链接之前先把之前的用户踢下线(ignoreSessionId防止把当前用户踢下线)
-        redisTemplate.convertAndSend(UserTokenConstant.KICK_CHANNEL, JSONUtil.toJsonStr(new UserWebSocketDTO(uid, session.getId())));
+    public void onOpen(Session session, @PathParam(value = "uid") String uid) {
+        userResourceCheck.achieveDiagnoseResourceAndOpenConnection(uid, session);
         SESSION_POOL.put(uid, session);
-        redisTemplate.opsForHash().put(UserTokenConstant.USER_ID_CACHE, uid, session.getId());
     }
 
     /**
@@ -45,9 +47,9 @@ public class WebSocketServer {
      */
     @OnClose
     public void onClose(Session session, @PathParam(value = "uid") String uid) {
-        redisTemplate.opsForHash().delete(UserTokenConstant.USER_ID_CACHE, uid, session.getId());
+        redisTemplate.opsForHash().delete(UserTokenConstant.USER_WEBSOCKET_CACHE, uid, session.getId());
         SESSION_POOL.remove(uid);
-        log.info("用户:{}关闭,从Redis中移除,当前连接数为:{}", uid, redisTemplate.opsForHash().size(UserTokenConstant.USER_ID_CACHE));
+        log.info("用户:{}关闭,从Redis中移除,当前连接数为:{}", uid, redisTemplate.opsForHash().size(UserTokenConstant.USER_WEBSOCKET_CACHE));
     }
 
     /**
@@ -55,9 +57,9 @@ public class WebSocketServer {
      */
     @OnError
     public void onError(Session session, @PathParam(value = "uid") String uid, Throwable throwable) {
-        redisTemplate.opsForHash().delete(UserTokenConstant.USER_ID_CACHE, uid, session.getId());
+        redisTemplate.opsForHash().delete(UserTokenConstant.USER_WEBSOCKET_CACHE, uid, session.getId());
         SESSION_POOL.remove(uid);
-        log.error("用户:{}发生错误,从Redis中移除,当前连接数为:{}", uid, redisTemplate.opsForHash().size(UserTokenConstant.USER_ID_CACHE), throwable);
+        log.error("用户:{}发生错误,从Redis中移除,当前连接数为:{}", uid, redisTemplate.opsForHash().size(UserTokenConstant.USER_WEBSOCKET_CACHE), throwable);
     }
 
     // 实现一个方法用于踢下线用户,走的是Redis的消息队列
diff --git a/virtual-patient-web/src/main/java/com/supervision/pojo/vo/AskPhysicalResultReqVO.java b/virtual-patient-web/src/main/java/com/supervision/pojo/vo/AskPhysicalResultReqVO.java
index d30311b1..8174b7b7 100644
--- a/virtual-patient-web/src/main/java/com/supervision/pojo/vo/AskPhysicalResultReqVO.java
+++ b/virtual-patient-web/src/main/java/com/supervision/pojo/vo/AskPhysicalResultReqVO.java
@@ -18,7 +18,6 @@ public class AskPhysicalResultReqVO {
     private String locationCode;
 
     @ApiModelProperty("初步诊断ID")
-    @NotBlank(message = "初步诊断ID不能为空")
     private String primaryId;
 
     @NotBlank(message = "流程ID不能为空")
diff --git a/virtual-patient-web/src/main/java/com/supervision/service/impl/DiagnoseHallServiceImpl.java b/virtual-patient-web/src/main/java/com/supervision/service/impl/DiagnoseHallServiceImpl.java
index 586c1eda..9bbcf208 100644
--- a/virtual-patient-web/src/main/java/com/supervision/service/impl/DiagnoseHallServiceImpl.java
+++ b/virtual-patient-web/src/main/java/com/supervision/service/impl/DiagnoseHallServiceImpl.java
@@ -1,8 +1,9 @@
 package com.supervision.service.impl;
 
-import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.lock.annotation.Lock4j;
 import com.supervision.constant.UserTokenConstant;
 import com.supervision.service.DiagnoseHallService;
+import com.supervision.usermanage.UserResourceCheck;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
@@ -14,17 +15,13 @@ import org.springframework.stereotype.Service;
 @Slf4j
 public class DiagnoseHallServiceImpl implements DiagnoseHallService {
 
-    private final RedisTemplate<String,String> redisTemplate;
-
-    @Value("${human.resourceMaxNumber}")
-    private String resourceNumber;
+    private final UserResourceCheck userResourceCheck;
 
 
+    @Lock4j(name = "achieveDiagnoseResource")
     @Override
     public boolean achieveDiagnoseResource() {
-        long humanMaxNumber = Long.parseLong(resourceNumber);
-        long currentUserNum = redisTemplate.opsForHash().size(UserTokenConstant.USER_ID_CACHE);
         // 如果小于数字人最大连接数,则可以连接
-        return currentUserNum <= humanMaxNumber;
+        return userResourceCheck.achieveDiagnoseResource();
     }
 }
diff --git a/virtual-patient-web/src/main/java/com/supervision/usermanage/UserResourceCheck.java b/virtual-patient-web/src/main/java/com/supervision/usermanage/UserResourceCheck.java
new file mode 100644
index 00000000..4d18a7ea
--- /dev/null
+++ b/virtual-patient-web/src/main/java/com/supervision/usermanage/UserResourceCheck.java
@@ -0,0 +1,45 @@
+package com.supervision.usermanage;
+
+import cn.hutool.json.JSONUtil;
+import com.baomidou.lock.annotation.Lock4j;
+import com.supervision.constant.UserTokenConstant;
+import com.supervision.exception.BusinessException;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+
+import javax.websocket.*;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class UserResourceCheck {
+
+    @Value("${human.resourceMaxNumber}")
+    private String resourceNumber;
+
+    private final RedisTemplate<String, String> redisTemplate;
+
+    @Lock4j(name = "achieveDiagnoseResource")
+    public boolean achieveDiagnoseResource() {
+        long humanMaxNumber = Long.parseLong(resourceNumber);
+        long currentSize = redisTemplate.opsForHash().size(UserTokenConstant.USER_WEBSOCKET_CACHE);
+        // 如果小于数字人最大连接数,则可以连接
+        return currentSize < humanMaxNumber;
+    }
+
+    @Lock4j(name = "achieveDiagnoseResourceAndOpenConnection")
+    public void achieveDiagnoseResourceAndOpenConnection(String uid, Session session){
+        // 如果小于数字人最大连接数,则可以连接
+        if (!achieveDiagnoseResource()) {
+            throw new BusinessException("暂时没有资源,建立连接失败");
+        }
+        log.info("用户:{}登录,缓存到Redis", uid);
+        // 链接之前先把之前的用户踢下线(ignoreSessionId防止把当前用户踢下线)
+        // 注意,这里如果用户没有进到问诊页面,只是在问诊大厅时,是不会被踢掉的.(因为这时没有建立websocket连接)
+        redisTemplate.convertAndSend(UserTokenConstant.KICK_CHANNEL, JSONUtil.toJsonStr(new UserWebSocketDTO(uid, session.getId())));
+        redisTemplate.opsForHash().put(UserTokenConstant.USER_WEBSOCKET_CACHE, uid, session.getId());
+    }
+}
diff --git a/virtual-patient-web/src/main/java/com/supervision/usermanage/UserWebSocketDTO.java b/virtual-patient-web/src/main/java/com/supervision/usermanage/UserWebSocketDTO.java
index 42a50b7a..063401f1 100644
--- a/virtual-patient-web/src/main/java/com/supervision/usermanage/UserWebSocketDTO.java
+++ b/virtual-patient-web/src/main/java/com/supervision/usermanage/UserWebSocketDTO.java
@@ -13,4 +13,5 @@ public class UserWebSocketDTO {
      * 忽略提除的ID
      */
     private String ignoreSessionId;
+
 }