update websocket 链接限制变为同ip可以多个用户登录,单用户只能登录一个

This commit is contained in:
yyb 2025-04-24 16:10:39 +08:00
parent c596d9ab30
commit 889189e726
14 changed files with 86 additions and 49 deletions

View File

@ -78,6 +78,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(ServiceException.class) @ExceptionHandler(ServiceException.class)
public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request)
{ {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
Integer code = e.getCode(); Integer code = e.getCode();
return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
@ -89,9 +90,10 @@ public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class) @ExceptionHandler(RuntimeException.class)
public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request)
{ {
System.out.println("异常抛出时间"+System.currentTimeMillis());
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestURI, e); log.error("请求地址'{}',发生未知异常.", requestURI, e);
return AjaxResult.error(e.getMessage()); return AjaxResult.error(HttpStatus.BAD_REQUEST,e.getMessage());
} }
/** /**

View File

@ -54,8 +54,4 @@ public class ChatMessageController {
public AjaxResult readMessage(@RequestBody MessagePageVo pageVo) { public AjaxResult readMessage(@RequestBody MessagePageVo pageVo) {
return chatMessageService.readMessage(pageVo.getRoomId()); return chatMessageService.readMessage(pageVo.getRoomId());
} }
} }

View File

@ -2,7 +2,7 @@ package com.m2pool.chat.controller;
import com.m2pool.chat.service.ChatRoomService; import com.m2pool.chat.service.ChatRoomService;
import com.m2pool.chat.vo.CharRoomVo; import com.m2pool.chat.vo.CharRoomVo;
import com.m2pool.chat.vo.MessagePageVo; import com.m2pool.chat.vo.RoomPageVo;
import com.m2pool.common.core.web.Result.AjaxResult; import com.m2pool.common.core.web.Result.AjaxResult;
import com.m2pool.common.core.web.page.TableDataInfo; import com.m2pool.common.core.web.page.TableDataInfo;
import com.m2pool.common.security.annotation.RequiresLogin; import com.m2pool.common.security.annotation.RequiresLogin;
@ -11,6 +11,8 @@ import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
@Api(tags = "聊天室相关接口") @Api(tags = "聊天室相关接口")
@RestController @RestController
@RequestMapping("/rooms") @RequestMapping("/rooms")
@ -23,8 +25,13 @@ public class ChatRoomController {
@PostMapping("/find/room/list") @PostMapping("/find/room/list")
@ApiOperation(value = "查询客服人员的聊天室列表") @ApiOperation(value = "查询客服人员的聊天室列表")
@RequiresLogin @RequiresLogin
public TableDataInfo findRoomList(@RequestBody MessagePageVo messagePageVo) { public TableDataInfo findRoomList(@RequestBody(required = false) RoomPageVo roomPageVo) {
return chatRoomService.findRoomList(messagePageVo.getSendDateTime()); // 如果请求体为空初始化一个默认的 RoomPageVo 对象
if (roomPageVo == null) {
roomPageVo = new RoomPageVo();
roomPageVo.setSendDateTime(LocalDateTime.now());
}
return chatRoomService.findRoomList(roomPageVo);
} }
@GetMapping("/find/room/by/userid") @GetMapping("/find/room/by/userid")

View File

@ -17,7 +17,7 @@ public class ChatMessage {
@TableId(value = "id", type = IdType.AUTO) @TableId(value = "id", type = IdType.AUTO)
private Long id; private Long id;
private Integer type; private Integer type;
private Integer read; private Integer isRead;
private String sendEmail; private String sendEmail;
private String content; private String content;
private Long roomId; private Long roomId;

View File

@ -15,6 +15,7 @@ public class ChatMessageHistory {
@TableId(value = "id", type = IdType.AUTO) @TableId(value = "id", type = IdType.AUTO)
private Long id; private Long id;
private Integer type; private Integer type;
private Integer isRead;
private String sendEmail; private String sendEmail;
private String content; private String content;
private Long roomId; private Long roomId;

View File

@ -23,7 +23,8 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static com.m2pool.chat.constant.CustomHeader.*; import static com.m2pool.chat.constant.CustomHeader.*;
import static com.m2pool.chat.constant.UserType.CUSTOMER; import static com.m2pool.chat.constant.UserType.LOGIN_USER;
import static com.m2pool.chat.constant.UserType.TOURIST;
/** /**
@ -37,9 +38,9 @@ public class WebsocketChannelInterceptor implements ChannelInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(WebsocketChannelInterceptor.class); private static final Logger LOGGER = LoggerFactory.getLogger(WebsocketChannelInterceptor.class);
/** /**
* 当前加入链接的ip * 当前加入链接的ip ,key ip ,VALUE 为用户邮箱
*/ */
private static final ConcurrentHashMap<String, Boolean> ipConnectionCountMap = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<String,String> ipConnectionCountMap = new ConcurrentHashMap<>();
/** /**
@ -78,7 +79,7 @@ public class WebsocketChannelInterceptor implements ChannelInterceptor {
//最大链接数限制 //最大链接数限制
maxConnectionsLimit(); maxConnectionsLimit();
//根据客服端ip + 用户类型限制连接数 //根据客服端ip + 用户类型限制连接数
ipLimit(accessor,type); ipLimit(accessor,type,email);
//链接请求头中用户信息存入stomp中 //链接请求头中用户信息存入stomp中
@ -117,14 +118,23 @@ public class WebsocketChannelInterceptor implements ChannelInterceptor {
* ip 链接个数限制 * ip 链接个数限制
* @param accessor * @param accessor
*/ */
private void ipLimit(StompHeaderAccessor accessor,int type){ private void ipLimit(StompHeaderAccessor accessor,int type,String email){
Map<String, Object> sessionAttributes = accessor.getSessionAttributes(); Map<String, Object> sessionAttributes = accessor.getSessionAttributes();
if (sessionAttributes != null) { if (sessionAttributes != null) {
String ipAddr = (String) sessionAttributes.get(IPADDR); String ipAddr = (String) sessionAttributes.get(IPADDR);
if(type != CUSTOMER && ipConnectionCountMap.get(ipAddr) != null){ System.out.println(accessor.getSessionId());
String hasConnectionEmail = ipConnectionCountMap.get(ipAddr);
//两种ip限制情况
//本次链接为游客 且ip上已经有人链接直接拒绝本次链接
if(type == TOURIST && hasConnectionEmail != null){
throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.IP_LIMIT_CONNECT)); throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.IP_LIMIT_CONNECT));
} }
ipConnectionCountMap.put(ipAddr,true); //本次链接为登录用户,并且已经链接.直接拒绝本次链接(多用户登录)
if ( type == LOGIN_USER && email.equals(hasConnectionEmail)) {
throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.IP_LIMIT_CONNECT));
}
ipConnectionCountMap.put(ipAddr,email);
}else{ }else{
throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.GET_PRINCIPAL_FAIL)); throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.GET_PRINCIPAL_FAIL));
} }
@ -137,7 +147,7 @@ public class WebsocketChannelInterceptor implements ChannelInterceptor {
* @return * @return
*/ */
private String getConnectCustomHeaders(Object raw,String headerKey){ private String getConnectCustomHeaders(Object raw,String headerKey){
String headerValue = ""; String headerValue = "";
if (raw instanceof Map) { if (raw instanceof Map) {
Object value = ((Map<?, ?>) raw).get(headerKey); Object value = ((Map<?, ?>) raw).get(headerKey);
if(value instanceof ArrayList){ if(value instanceof ArrayList){
@ -157,15 +167,12 @@ public class WebsocketChannelInterceptor implements ChannelInterceptor {
Map<String, Object> sessionAttributes = accessor.getSessionAttributes(); Map<String, Object> sessionAttributes = accessor.getSessionAttributes();
if (sessionAttributes != null) { if (sessionAttributes != null) {
String ipAddr = (String) sessionAttributes.get(IPADDR); String ipAddr = (String) sessionAttributes.get(IPADDR);
if(ipConnectionCountMap.get(ipAddr) != null){ ipConnectionCountMap.remove(ipAddr);
ipConnectionCountMap.remove(ipAddr);
}
}else{ }else{
throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.GET_PRINCIPAL_FAIL)); throw new MessageDeliveryException(ExceptionEnum.fromCode(ExceptionEnum.GET_PRINCIPAL_FAIL));
} }
} }
@Override @Override
public void postSend(Message<?> message, MessageChannel channel, boolean sent) { public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
LOGGER.info("------------WebsocketChannelInterceptor-postSend"); LOGGER.info("------------WebsocketChannelInterceptor-postSend");

View File

@ -29,6 +29,6 @@ public interface ChatMessageMapper extends BaseMapper<ChatMessage> {
* @return Map<String, Integer>键为用户邮箱值为未读消息条数 * @return Map<String, Integer>键为用户邮箱值为未读消息条数
*/ */
@MapKey("userEmail") @MapKey("userEmail")
Map<String, Integer> findUnReadNums(@Param("userEmails") List<String> userEmails); Map<String, Map<String,Integer>> findUnReadNums(@Param("userEmails") List<String> userEmails);
} }

View File

@ -3,19 +3,18 @@ package com.m2pool.chat.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import com.m2pool.chat.entity.ChatRoom; import com.m2pool.chat.entity.ChatRoom;
import com.m2pool.chat.vo.CharRoomVo; import com.m2pool.chat.vo.CharRoomVo;
import com.m2pool.chat.vo.RoomPageVo;
import com.m2pool.common.core.web.Result.AjaxResult; import com.m2pool.common.core.web.Result.AjaxResult;
import com.m2pool.common.core.web.page.TableDataInfo; import com.m2pool.common.core.web.page.TableDataInfo;
import java.time.LocalDateTime;
public interface ChatRoomService extends IService<ChatRoom> { public interface ChatRoomService extends IService<ChatRoom> {
/** /**
* 查询客服的聊天室列表 * 查询客服的聊天室列表
* @param sendDateTime 最后一个发送者发送时间 * @param roomPageVo
* @return * @return
*/ */
TableDataInfo findRoomList(LocalDateTime sendDateTime); TableDataInfo findRoomList(RoomPageVo roomPageVo);
/** /**
* 根据当前用户邮箱查询聊天室 * 根据当前用户邮箱查询聊天室

View File

@ -62,9 +62,11 @@ public class ChatMessageServiceImpl implements ChatMessageService {
}else if (username.equals(chatRoom.getUserTwoEmail())) { }else if (username.equals(chatRoom.getUserTwoEmail())) {
updateUserEmail = chatRoom.getUserOneEmail(); updateUserEmail = chatRoom.getUserOneEmail();
} }
int update = chatMessageMapper.update(ChatMessage.builder().read(1).build(), int update = chatMessageMapper.update(ChatMessage.builder().isRead(1).build(),
new LambdaUpdateWrapper<ChatMessage>().eq(ChatMessage::getSendEmail, updateUserEmail).eq(ChatMessage::getRoomId, roomId)); new LambdaUpdateWrapper<ChatMessage>()
return update > 0 ? AjaxResult.success("已读") : AjaxResult.error("消息读取失败"); .eq(ChatMessage::getSendEmail, updateUserEmail)
.eq(ChatMessage::getRoomId, roomId));
return update >= 0 ? AjaxResult.success("已读") : AjaxResult.error("消息读取失败");
} }
return AjaxResult.error("聊天室不存在"); return AjaxResult.error("聊天室不存在");
} }

View File

@ -1,6 +1,7 @@
package com.m2pool.chat.service.impl; package com.m2pool.chat.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.m2pool.chat.dto.ChatRoomDto; import com.m2pool.chat.dto.ChatRoomDto;
import com.m2pool.chat.entity.ChatRoom; import com.m2pool.chat.entity.ChatRoom;
@ -8,6 +9,7 @@ import com.m2pool.chat.mapper.ChatMessageMapper;
import com.m2pool.chat.mapper.ChatRoomMapper; import com.m2pool.chat.mapper.ChatRoomMapper;
import com.m2pool.chat.service.ChatRoomService; import com.m2pool.chat.service.ChatRoomService;
import com.m2pool.chat.vo.CharRoomVo; import com.m2pool.chat.vo.CharRoomVo;
import com.m2pool.chat.vo.RoomPageVo;
import com.m2pool.common.core.constant.HttpStatus; import com.m2pool.common.core.constant.HttpStatus;
import com.m2pool.common.core.utils.PageUtils; import com.m2pool.common.core.utils.PageUtils;
import com.m2pool.common.core.web.Result.AjaxResult; import com.m2pool.common.core.web.Result.AjaxResult;
@ -21,7 +23,6 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
@ -40,18 +41,20 @@ public class ChatRoomServiceImpl extends ServiceImpl<ChatRoomMapper, ChatRoom> i
private RemoteUserService remoteUserService; private RemoteUserService remoteUserService;
@Override @Override
public TableDataInfo findRoomList(LocalDateTime sendDateTime) { public TableDataInfo findRoomList(RoomPageVo roomPageVo) {
String userEmail = SecurityUtils.getUsername(); String userEmail = SecurityUtils.getUsername();
PageUtils.startPage(); PageHelper.startPage(1, 20);
System.out.println("-----时间"+roomPageVo.getSendDateTime()+"-----------用户"+userEmail + "-----当前时间"+ System.currentTimeMillis());
//1.查找当前客服对应的聊天室 //1.查找当前客服对应的聊天室
List<ChatRoomDto> roomList = chatRoomMapper.findRoomList(userEmail,sendDateTime); List<ChatRoomDto> roomList = chatRoomMapper.findRoomList(userEmail,roomPageVo.getSendDateTime());
List<String> userEmails = roomList.stream().map(ChatRoomDto::getUserEmail).collect(Collectors.toList()); List<String> userEmails = roomList.stream().map(ChatRoomDto::getUserEmail).collect(Collectors.toList());
//2.查询未读数量 //2.查询未读数量
if (!userEmails.isEmpty()){ if (!userEmails.isEmpty()){
Map<String, Integer> unReadNums = chatMessageMapper.findUnReadNums(userEmails); Map<String, Map<String,Integer>> unReadNums = chatMessageMapper.findUnReadNums(userEmails);
for (ChatRoomDto room : roomList) { for (ChatRoomDto room : roomList) {
Integer i = unReadNums.get(room.getUserEmail()); Map<String, Integer> stringIntegerMap = unReadNums.get(room.getUserEmail());
room.setUnReadNumber(i == null ? 0 : i); Integer i = stringIntegerMap != null ? stringIntegerMap.get("unReadNumber") : 0;
room.setUnReadNumber(i);
} }
} }

View File

@ -1,10 +1,7 @@
package com.m2pool.chat.vo; package com.m2pool.chat.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data; import lombok.Data;
import java.time.LocalDateTime;
/** /**
* @ClassName MessagePageVo * @ClassName MessagePageVo
* @Description TODO * @Description TODO
@ -17,10 +14,6 @@ public class MessagePageVo {
private Long id; private Long id;
private Integer pageNum; private Integer pageNum;
private Long roomId; private Long roomId;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime sendDateTime;
public MessagePageVo() { public MessagePageVo() {
this.id = 1L; this.id = 1L;
this.pageNum = 20; this.pageNum = 20;

View File

@ -0,0 +1,20 @@
package com.m2pool.chat.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
/**
* @ClassName MessagePageVo
* @Description TODO
* @Author yyb
* @Date 2025/4/22 17:09
*/
@Data
public class RoomPageVo {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime sendDateTime;
}

View File

@ -15,14 +15,13 @@
LIMIT #{pageNum} LIMIT #{pageNum}
</select> </select>
<select id="findUnReadNums" resultType="java.util.Map"> <select id="findUnReadNums" resultType="java.util.Map">
SELECT send_email as userEmail, SELECT send_email as userEmail,count(*) as unReadNums
count(*) as unReadNums
FROM chat_message FROM chat_message
WHERE send_email IN WHERE send_email IN
<foreach collection="userEmails" item="userEmail" open="(" separator="," close=")"> <foreach collection="userEmails" item="userEmail" open="(" separator="," close=")">
#{userEmail} #{userEmail}
</foreach> </foreach>
AND `read` = false AND is_read = false
GROUP BY send_email GROUP BY send_email
</select> </select>

View File

@ -11,9 +11,17 @@
last_customer_send_time as lastCustomerSendTime last_customer_send_time as lastCustomerSendTime
FROM FROM
chat_room chat_room
WHERE <where>
user_two_email = #{userEmail} AND last_user_send_time <![CDATA[ <= ]]> #{sendDateTime} user_two_email = #{userEmail} AND del = FALSE
AND del = FALSE <choose>
<when test="sendDateTime != null">
AND last_user_send_time <![CDATA[ <= ]]> #{sendDateTime}
</when>
<otherwise>
AND last_user_send_time <![CDATA[ <= ]]> NOW()
</otherwise>
</choose>
</where>
ORDER BY ORDER BY
flag DESC, flag DESC,
last_user_send_time DESC last_user_send_time DESC