diff --git a/pom.xml b/pom.xml
index ec8412a..a38f594 100644
--- a/pom.xml
+++ b/pom.xml
@@ -132,6 +132,10 @@
easyexcel
3.2.1
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
diff --git a/src/main/java/top/xinsin/Main.java b/src/main/java/top/xinsin/Main.java
index 127f10b..075a16f 100644
--- a/src/main/java/top/xinsin/Main.java
+++ b/src/main/java/top/xinsin/Main.java
@@ -3,9 +3,11 @@ package top.xinsin;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@MapperScan("top.xinsin.mapper")
+@EnableScheduling
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class,args);
diff --git a/src/main/java/top/xinsin/config/WebsocketConfiguration.java b/src/main/java/top/xinsin/config/WebsocketConfiguration.java
new file mode 100644
index 0000000..452e022
--- /dev/null
+++ b/src/main/java/top/xinsin/config/WebsocketConfiguration.java
@@ -0,0 +1,22 @@
+package top.xinsin.config;
+
+import org.springframework.beans.factory.annotation.Autowired;
+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.server.standard.ServerEndpointExporter;
+import top.xinsin.controller.WebSocketController;
+import top.xinsin.service.WebSocketService;
+
+@Configuration
+@EnableWebSocket
+public class WebsocketConfiguration {
+ @Bean
+ public ServerEndpointExporter serverEndpointExporter() {
+ return new ServerEndpointExporter();
+ }
+ @Autowired
+ public void socketUserService(WebSocketService webSocketService){
+ WebSocketController.webSocketService= webSocketService;
+ }
+}
diff --git a/src/main/java/top/xinsin/controller/WebSocketController.java b/src/main/java/top/xinsin/controller/WebSocketController.java
new file mode 100644
index 0000000..0b23272
--- /dev/null
+++ b/src/main/java/top/xinsin/controller/WebSocketController.java
@@ -0,0 +1,85 @@
+package top.xinsin.controller;
+
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import jakarta.websocket.*;
+import jakarta.websocket.server.PathParam;
+import jakarta.websocket.server.ServerEndpoint;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import top.xinsin.service.WebSocketService;
+import top.xinsin.util.R;
+
+import java.io.IOException;
+import java.net.http.WebSocket;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Component
+@ServerEndpoint(value = "/api/auth/websocket/{userId}")
+@Slf4j
+public class WebSocketController {
+ private static ConcurrentHashMap webSocketMap = new ConcurrentHashMap<>();
+
+ private Session session;
+ private String userId;
+ public static WebSocketService webSocketService;
+
+ @OnOpen
+ public void onOpen(Session session, @PathParam("userId") String userId) {
+ this.session = session;
+ this.userId = userId;
+ //加入map
+ webSocketMap.put(userId, this);
+ R stringR = webSocketService.addVerify(userId);
+ sendMessageByUserId(userId, stringR);
+ }
+ @OnClose
+ public void onClose() {
+ //从map中删除
+ webSocketMap.remove(userId);
+ }
+ @OnMessage
+ public void onMessage(String message, Session session) {
+ sendMessageByUserId(userId,webSocketService.parseMessage(userId,message,webSocketMap));
+ }
+ @OnError
+ public void onError(Session session, Throwable error) {
+ log.error("用户错误:" + this.userId + ",原因:" + error.getMessage());
+ error.printStackTrace();
+ }
+ /**
+ * 向客户端发送消息
+ */
+ @SneakyThrows
+ public void sendMessage(String message) {
+ this.session.getBasicRemote().sendText(message);
+ }
+
+ /**
+ * 通过userId向客户端发送消息
+ */
+ @SneakyThrows
+ public void sendMessageByUserId(String userId, R> message) {
+ if(webSocketMap.containsKey(userId)){
+ webSocketMap.get(userId).sendMessage(JSON.toJSONString(message));
+ }else{
+ log.error("用户{}不在线",userId);
+ }
+ }
+ @Scheduled(fixedRate = 10000)
+ public void verifyTime(){
+ List expireTimes = webSocketService.getExpireTimes();
+ expireTimes.forEach(e -> {
+ if(webSocketMap.containsKey(e)){
+ webSocketMap.get(e).sendMessage(JSON.toJSONString(R.success(new JSONObject().fluentPut("info","当前登录用户已过期"))));
+ }
+ });
+ }
+}
diff --git a/src/main/java/top/xinsin/service/AccountService.java b/src/main/java/top/xinsin/service/AccountService.java
index c705b44..f3d10c7 100644
--- a/src/main/java/top/xinsin/service/AccountService.java
+++ b/src/main/java/top/xinsin/service/AccountService.java
@@ -187,7 +187,7 @@ public class AccountService implements UserDetailsService {
Verify verify = new Verify(accountAndStoreAdmin.getCreateId(), authAccount.getId(), accountAndStoreAdmin.getCountdown());
int insert1 = verifyMapper.insert(verify);
if (insert1 == 1){
- redisTemplate.opsForValue().set(String.valueOf(authAccount.getId()),authAccount.getUsername(),verify.getCountdown(), TimeUnit.SECONDS);
+// redisTemplate.opsForValue().set(String.valueOf(authAccount.getId()),authAccount.getUsername(),verify.getCountdown(), TimeUnit.SECONDS);
return R.success(new JSONObject().fluentPut("status","添加该商户成功").fluentPut("nextVerificationTime",verify.getCountdown()));
}
}
diff --git a/src/main/java/top/xinsin/service/WebSocketService.java b/src/main/java/top/xinsin/service/WebSocketService.java
new file mode 100644
index 0000000..12d19d2
--- /dev/null
+++ b/src/main/java/top/xinsin/service/WebSocketService.java
@@ -0,0 +1,161 @@
+package top.xinsin.service;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import org.springframework.stereotype.Service;
+import top.xinsin.controller.WebSocketController;
+import top.xinsin.mapper.VerifyMapper;
+import top.xinsin.pojo.Verify;
+import top.xinsin.util.HttpCodes;
+import top.xinsin.util.R;
+import top.xinsin.util.StringConstant;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+import static org.apache.naming.SelectorContext.prefix;
+
+@Service
+@Slf4j
+public class WebSocketService {
+ @Resource
+ private RedisTemplate redisTemplate;
+ @Autowired
+ private VerifyMapper verifyMapper;
+
+ public R addVerify(String userId){
+ RedisSerializer stringSerializer = new StringRedisSerializer();
+ redisTemplate.setValueSerializer(stringSerializer);
+ redisTemplate.setKeySerializer(stringSerializer);
+ if (userId.equals("-114514")){
+ return R.success("检测为管理员登录");
+ }
+ Object o = redisTemplate.opsForValue().get(StringConstant.VERIFY_PRE + userId);
+ Object o1 = redisTemplate.opsForValue().get(StringConstant.VERIFY_PRE + userId + "_no");
+ System.out.println(o == null);
+ System.out.println(o1 == null);
+ if (o == null && o1 == null){
+ Verify verify = verifyMapper.selectOne(new LambdaQueryWrapper().eq(Verify::getStoreId, userId));
+ if (verify != null){
+ if(userId.equals(String.valueOf(verify.getStoreId()))) {
+ redisTemplate.opsForValue().set(StringConstant.VERIFY_PRE + userId, String.valueOf(verify.getNum()), verify.getCountdown(), TimeUnit.SECONDS);
+ redisTemplate.opsForValue().set(StringConstant.VERIFY_PRE + userId + "_no", String.valueOf(verify.getNum()));
+ return R.success("已将该用户过期时间存入");
+ }
+ }
+ }else{
+ return R.success("当前用户已存在");
+ }
+ return R.failed(HttpCodes.HTTP_CODES500,"不存在该用户");
+ }
+ public R parseMessage(String userId, String message, ConcurrentHashMap webSocketMap){
+ JSONObject parse = JSONObject.parse(message);
+ String type = (String) parse.get("type");
+ switch (type) {
+ case "ping" -> {
+ return ping(userId);
+ }
+ case "addVerifyInfo" -> {
+ return addVerifyInfo(userId, parse);
+ }
+ case "getTime" -> {
+ return getTime(userId);
+ }
+ case "confirmVerifyInfo" -> {
+ return confirmVerifyInfo(parse,webSocketMap);
+ }
+ }
+ return null;
+ }
+
+ private R confirmVerifyInfo(JSONObject parse, ConcurrentHashMap webSocketMap) {
+ String verifyResult = String.valueOf(parse.get("verifyResult"));
+ String userId = String.valueOf(parse.get("userId"));
+
+ int i = Integer.parseInt(String.valueOf(redisTemplate.opsForValue().get(StringConstant.VERIFY_PRE + userId + "_no")));
+ System.out.println(i);
+ Verify verify = new Verify();
+ verify.setVerifyResult(verifyResult);
+ verify.setNum(i + 1);
+ int update = verifyMapper.update(verify, new LambdaQueryWrapper().eq(Verify::getStoreId, userId));
+ if (update != 1){
+ return R.failed(HttpCodes.HTTP_CODES500,new JSONObject().fluentPut("info","信息修改失败"));
+ }
+ if (verifyResult.equals("false")){
+ if (webSocketMap.containsKey(userId)){
+ webSocketMap.get(userId).sendMessage(JSON.toJSONString(new JSONObject().fluentPut("info","管理员未同意你的验证信息,你可以重新进行验证")));
+ }
+ return R.success(new JSONObject().fluentPut("info","你已拒绝该商户的验证信息"));
+ }else{
+ if (webSocketMap.containsKey(userId)){
+ webSocketMap.get(userId).sendMessage(JSON.toJSONString(new JSONObject().fluentPut("info","管理员已同意你的验证信息,请重新登录")));
+ redisTemplate.delete(StringConstant.VERIFY_PRE + userId + "_no");
+ Verify verify1 = new Verify();
+ verify.setVerifyPhone("");
+ verify.setVerifyName("");
+ verifyMapper.update(verify1, new LambdaQueryWrapper().eq(Verify::getStoreId, userId));
+ }
+ return R.success(new JSONObject().fluentPut("info","你已同意该商户的验证信息"));
+ }
+ }
+
+ private R getTime(String userId) {
+ Long expire = redisTemplate.getExpire(StringConstant.VERIFY_PRE + userId);
+ assert expire != null;
+ if (expire.equals(-2L)){
+ Object o = redisTemplate.opsForValue().get(StringConstant.VERIFY_PRE + userId + "_no");
+ if (o != null){
+ return R.success(new JSONObject().fluentPut("info","当前用户登录已过期"));
+ }
+ return R.failed(HttpCodes.HTTP_CODES500,new JSONObject().fluentPut("info","没有该用户登录信息"));
+ }else{
+ return R.success(new JSONObject().fluentPut("info",expire));
+ }
+ }
+
+ private R addVerifyInfo(String userId, JSONObject parse) {
+ Long expire = redisTemplate.getExpire(StringConstant.VERIFY_PRE + userId);
+ Object o = redisTemplate.opsForValue().get(StringConstant.VERIFY_PRE + userId + "_no");
+ assert expire != null;
+ if (expire.equals(-2L) && o != null){
+ Object verifyName = parse.get("verifyName");
+ Object verifyPhone = parse.get("verifyPhone");
+ Verify verify = new Verify();
+ verify.setVerifyName(String.valueOf(verifyName));
+ verify.setVerifyPhone(String.valueOf(verifyPhone));
+ int update = verifyMapper.update(verify, new LambdaQueryWrapper().eq(Verify::getStoreId, userId));
+ return update == 1 ? R.success(new JSONObject().fluentPut("info","验证信息添加成功,请耐心等待管理员审核")) : R.failed(HttpCodes.HTTP_CODES500,new JSONObject().fluentPut("info","添加验证信息失败"));
+ }
+ return R.failed(HttpCodes.HTTP_CODES500,new JSONObject().fluentPut("info","当前用户还未过期或者未登陆过"));
+ }
+
+ private R ping(String userId) {
+ return R.success(new JSONObject().fluentPut("info","pong").fluentPut("time",redisTemplate.getExpire(StringConstant.VERIFY_PRE + userId)));
+ }
+ public List getExpireTimes(){
+ List expireUser = new ArrayList<>();
+ Set keys = redisTemplate.keys(prefix.concat(StringConstant.VERIFY_PRE + "*"));
+ assert keys != null;
+ keys.forEach(e -> {
+ if (Objects.equals(redisTemplate.getExpire(e), -1L)){
+ expireUser.add(e);
+ redisTemplate.delete(e);
+ }else if (Objects.equals(redisTemplate.getExpire(e),-2L)){
+ redisTemplate.delete(e);
+ }
+ });
+ log.info("{}",keys);
+ return expireUser;
+ }
+}
diff --git a/src/main/java/top/xinsin/util/StringConstant.java b/src/main/java/top/xinsin/util/StringConstant.java
index 5d8b0dc..8697786 100644
--- a/src/main/java/top/xinsin/util/StringConstant.java
+++ b/src/main/java/top/xinsin/util/StringConstant.java
@@ -5,4 +5,6 @@ public class StringConstant {
public static final String EMAIL_REGEX = "\\w[-\\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\\.)+[A-Za-z]{2,14}";
public static final String USERNAME_REGEX = "[A-Za-z0-9_\\-一-龥]+";
public static final String ID_REGEX = "[0-9]*";
+
+ public static final String VERIFY_PRE = "verify_";
}