SpringBoot+WebSocket实现即时通讯(四)

news/发布时间2024/5/15 11:30:29

前言

紧接着上文《SpringBoot+WebSocket实现即时通讯(三)》

本博客姊妹篇

  • SpringBoot+WebSocket实现即时通讯(一)
  • SpringBoot+WebSocket实现即时通讯(二)
  • SpringBoot+WebSocket实现即时通讯(三)
  • SpringBoot+WebSocket实现即时通讯(四)

一、功能描述

  • 用户管理:业务自己实现,暂从数据库添加
  • 好友管理:添加好友、删除好友、修改备注、好友列表等
  • 群组管理:新建群、解散群、编辑群、变更群主、拉人进群、踢出群等
  • 聊天模式:私聊、群聊
  • 消息类型:系统、文本、语音、图片、视频
  • 聊天管理:删除聊天、置顶聊天、查看聊天记录等

二、消息、聊天会话功能实现

2.1 消息

mapper

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qiangesoft.im.mapper.ImMessageMapper"><select id="listMessage" resultType="com.qiangesoft.im.pojo.vo.ImMessageVO">SELECTb.id,b.sender_id,b.message_type,b.message,b.create_time,c.read_flagFROMim_chat aINNER JOIN im_message b ON a.id = b.chat_idINNER JOIN im_message_receiver c ON b.id = c.message_idWHEREa.del_flag = FALSEAND a.id = #{chatId}AND c.receiver_id = #{userId}<if test="messageType != null and messageType != ''">AND b.message_type = #{messageType}</if><if test="message != null and message != ''">AND b.message like concat('%', #{message}, '%')</if>ORDER BY b.id DESC</select><select id="listUnreadMessage" resultType="com.qiangesoft.im.pojo.vo.ImMessageVO">SELECTb.id,b.sender_id,b.message_type,b.message,b.create_time,c.read_flagFROMim_chat aINNER JOIN im_message b ON a.id = b.chat_idINNER JOIN im_message_receiver c ON b.id = c.message_idWHEREa.del_flag = FALSEAND a.id = #{chatId}AND c.receiver_id = #{userId}AND c.read_flag = FALSEORDER BY b.id DESC</select><select id="listChatUnreadMessage" resultType="com.qiangesoft.im.pojo.bo.ImChatMessageBO">SELECTa.chat_id AS chatId,COUNT(a.id) AS unreadNumFROMim_message a INNER JOINim_message_receiver b ON a.id=b.message_idWHEREb.receiver_id=#{userId}AND b.read_flag=FALSEAND a.chat_id IN<foreach collection="chatIdList" item="chatId" open="(" separator="," close=")">#{chatId}</foreach>GROUP BY a.chat_id</select><select id="listLatestMessage" resultType="com.qiangesoft.im.entity.ImMessage">SELECT * FROM im_message WHERE id IN (SELECTmax(a.id)FROMim_message a INNER JOINim_message_receiver b ON a.id=b.message_idWHEREb.receiver_id=#{userId}AND a.chat_id IN<foreach collection="chatIdList" item="chatId" open="(" separator="," close=")">#{chatId}</foreach>GROUP BY a.chat_id)</select></mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qiangesoft.im.mapper.ImMessageReceiverMapper"><update id="updateRead">UPDATE im_message_receiver aINNER JOIN im_message bON b.id = a.message_idINNER JOIN im_chat c ON c.id = b.chat_idSET read_flag = 1WHEREa.read_flag = 0AND a.receiver_id = #{userId}AND c.id = #{chatId}<if test="messageId != null">AND b.id = #{messageId}</if></update>
</mapper>
package com.qiangesoft.im.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.qiangesoft.im.entity.ImMessage;
import com.qiangesoft.im.pojo.bo.ImChatMessageBO;
import com.qiangesoft.im.pojo.vo.ImMessageVO;
import org.apache.ibatis.annotations.Param;import java.util.List;/*** <p>* 消息 Mapper 接口* </p>** @author qiangesoft* @date 2024-02-07*/
public interface ImMessageMapper extends BaseMapper<ImMessage> {/*** 消息列表** @param page* @param userId* @param chatId* @param messageType* @param message* @return*/IPage<ImMessageVO> listMessage(@Param("page") IPage<ImMessage> page, @Param("userId") Long userId, @Param("chatId") Long chatId,@Param("messageType") String messageType, @Param("message") String message);/*** 未读消息列表** @param userId* @param chatId* @return*/List<ImMessageVO> listUnreadMessage(@Param("userId") Long userId, @Param("chatId") Long chatId);/*** 会话未读消息** @param userId* @param chatIdList* @return*/List<ImChatMessageBO> listChatUnreadMessage(@Param("userId") Long userId, @Param("chatIdList") List<Long> chatIdList);/*** 会话最新消息** @param userId* @param chatIdList* @return*/List<ImMessage> listLatestMessage(@Param("userId") Long userId, @Param("chatIdList") List<Long> chatIdList);
}
package com.qiangesoft.im.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qiangesoft.im.entity.ImMessageReceiver;
import org.apache.ibatis.annotations.Param;/*** <p>* 群用户消息关系 Mapper 接口* </p>** @author qiangesoft* @since 2023-08-23*/
public interface ImMessageReceiverMapper extends BaseMapper<ImMessageReceiver> {/*** 置为已读** @param userId* @param chatId* @param messageId*/void updateRead(@Param("userId") Long userId, @Param("chatId") Long chatId, @Param("messageId") Long messageId);
}

service

package com.qiangesoft.im.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.qiangesoft.im.entity.ImMessage;
import com.qiangesoft.im.pojo.bo.ImChatMessageBO;
import com.qiangesoft.im.pojo.dto.ImMessageDTO;
import com.qiangesoft.im.pojo.dto.query.MessageQueryDTO;
import com.qiangesoft.im.pojo.dto.query.PageQueryDTO;
import com.qiangesoft.im.pojo.vo.ImMessageVO;
import com.qiangesoft.im.pojo.vo.PageResultVO;import java.util.List;/*** <p>* 消息 服务类* </p>** @author qiangesoft* @date 2024-02-07*/
public interface IImMessageService extends IService<ImMessage> {/*** 消息列表** @param pageQuery* @param messageQuery* @return*/PageResultVO<ImMessageVO> listMessage(PageQueryDTO pageQuery, MessageQueryDTO messageQuery);/*** 未读消息列表** @param chatId* @return*/List<ImMessageVO> listUnreadMessage(Long chatId);/*** 发送消息** @param messageDTO* @return*/ImMessage send(ImMessageDTO messageDTO);/*** 聊天未读消息** @param userId* @param chatIdList* @return*/List<ImChatMessageBO> listChatUnreadMessage(Long userId, List<Long> chatIdList);/*** 聊天最新消息** @param userId* @param chatIdList* @return*/List<ImMessage> listLatestMessage(Long userId, List<Long> chatIdList);
}
package com.qiangesoft.im.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.qiangesoft.im.entity.ImMessageReceiver;/*** <p>* 群用户消息关系 服务类* </p>** @author qiangesoft* @since 2023-08-23*/
public interface IImMessageReceiverService extends IService<ImMessageReceiver> {/*** 置为已读** @param chatId* @param messageId*/void updateRead(Long chatId, Long messageId);}
package com.qiangesoft.im.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiangesoft.im.auth.UserUtil;
import com.qiangesoft.im.core.constant.ChatTypeEnum;
import com.qiangesoft.im.entity.*;
import com.qiangesoft.im.exception.ServiceException;
import com.qiangesoft.im.mapper.ImMessageMapper;
import com.qiangesoft.im.pojo.bo.ImChatMessageBO;
import com.qiangesoft.im.pojo.dto.ImMessageDTO;
import com.qiangesoft.im.pojo.dto.query.MessageQueryDTO;
import com.qiangesoft.im.pojo.dto.query.PageQueryDTO;
import com.qiangesoft.im.pojo.vo.ImMessageVO;
import com.qiangesoft.im.pojo.vo.PageResultVO;
import com.qiangesoft.im.pojo.vo.SysUserVo;
import com.qiangesoft.im.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;import java.util.*;
import java.util.stream.Collectors;/*** <p>* 群消息 服务实现类* </p>** @author qiangesoft* @date 2024-02-07*/
@Service
public class ImMessageServiceImpl extends ServiceImpl<ImMessageMapper, ImMessage> implements IImMessageService {@Lazy@Autowiredprivate IImChatService chatService;@Autowiredprivate IImFriendService friendService;@Autowiredprivate IImGroupUserService groupUserService;@Autowiredprivate IImMessageReceiverService messageReceiverService;@Autowiredprivate ISysUserService sysUserService;@Overridepublic PageResultVO<ImMessageVO> listMessage(PageQueryDTO pageQuery, MessageQueryDTO messageQuery) {Long chatId = messageQuery.getChatId();ImChat chat = chatService.getById(chatId);if (chat == null) {throw new ServiceException("聊天不存在");}Long userId = UserUtil.getUserId();Integer pageNum = pageQuery.getPageNum();Integer pageSize = pageQuery.getPageSize();PageResultVO<ImMessageVO> pageResult = new PageResultVO<>();pageResult.setPageNum(pageNum);pageResult.setPageSize(pageSize);IPage<ImMessageVO> messagePage = baseMapper.listMessage(new Page<>(pageNum, pageSize), userId, messageQuery.getChatId(), messageQuery.getMessageType(), messageQuery.getMessage());pageResult.setTotal(messagePage.getTotal());pageResult.setPages(messagePage.getPages());List<ImMessageVO> records = messagePage.getRecords();if (CollectionUtils.isEmpty(records)) {pageResult.setResults(records);return pageResult;}Set<Long> senderIdList = records.stream().map(ImMessageVO::getSenderId).collect(Collectors.toSet());Long targetId = chat.getTargetId();Map<Long, String> avatarMap = sysUserService.listByIds(senderIdList).stream().collect(Collectors.toMap(SysUser::getId, SysUser::getAvatar));Map<Long, String> nickNameMap = new HashMap<>();if (ChatTypeEnum.GROUP.getCode().equals(chat.getChatType())) {LambdaQueryWrapper<ImGroupUser> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(ImGroupUser::getGroupId, targetId).in(ImGroupUser::getUserId, senderIdList);nickNameMap = groupUserService.list(queryWrapper).stream().collect(Collectors.toMap(ImGroupUser::getUserId, ImGroupUser::getNickName));} else {ImFriend friend = friendService.getById(targetId);nickNameMap.put(userId, UserUtil.getNickName());nickNameMap.put(targetId, friend.getRemark());}for (ImMessageVO record : records) {SysUserVo sysUserVo = new SysUserVo();Long senderId = record.getSenderId();sysUserVo.setId(senderId);sysUserVo.setAvatar(avatarMap.get(senderId));sysUserVo.setNickName(nickNameMap.get(senderId));record.setSender(sysUserVo);}pageResult.setResults(records);return pageResult;}@Overridepublic List<ImMessageVO> listUnreadMessage(Long chatId) {ImChat chat = chatService.getById(chatId);if (chat == null) {throw new ServiceException("聊天不存在");}Long userId = UserUtil.getUserId();List<ImMessageVO> records = baseMapper.listUnreadMessage(userId, chatId);if (CollectionUtils.isEmpty(records)) {return records;}Set<Long> senderIdList = records.stream().map(ImMessageVO::getSenderId).collect(Collectors.toSet());Long targetId = chat.getTargetId();Map<Long, String> avatarMap = sysUserService.listByIds(senderIdList).stream().collect(Collectors.toMap(SysUser::getId, SysUser::getAvatar));Map<Long, String> nickNameMap = new HashMap<>();if (ChatTypeEnum.GROUP.getCode().equals(chat.getChatType())) {LambdaQueryWrapper<ImGroupUser> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(ImGroupUser::getGroupId, targetId).in(ImGroupUser::getUserId, senderIdList);nickNameMap = groupUserService.list(queryWrapper).stream().collect(Collectors.toMap(ImGroupUser::getUserId, ImGroupUser::getNickName));} else {ImFriend friend = friendService.getById(targetId);nickNameMap.put(userId, UserUtil.getNickName());nickNameMap.put(targetId, friend.getRemark());}for (ImMessageVO record : records) {SysUserVo sysUserVo = new SysUserVo();Long senderId = record.getSenderId();sysUserVo.setId(senderId);sysUserVo.setAvatar(avatarMap.get(senderId));sysUserVo.setNickName(nickNameMap.get(senderId));record.setSender(sysUserVo);}return records;}@Transactional(rollbackFor = RuntimeException.class)@Overridepublic ImMessage send(ImMessageDTO messageDTO) {Long userId = UserUtil.getUserId();ImMessage message = null;String chatType = messageDTO.getChatType();if (ChatTypeEnum.GROUP.getCode().equals(chatType)) {message = this.sendGroupMessage(messageDTO, userId);}if (ChatTypeEnum.PERSON.getCode().equals(chatType)) {message = this.sendPersonMessage(messageDTO, userId);}return message;}/*** 私聊消息** @param messageDTO* @param userId* @return*/private ImMessage sendPersonMessage(ImMessageDTO messageDTO, Long userId) {Long friendUserId = messageDTO.getTargetId();LambdaQueryWrapper<ImFriend> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(ImFriend::getFriendUserId, friendUserId).eq(ImFriend::getUserId, userId).eq(ImFriend::getDelFlag, false);ImFriend friend = friendService.getOne(queryWrapper);if (friend == null) {throw new ServiceException("非好友关系");}// 聊天会话Long chatId = messageDTO.getChatId();if (chatId == null) {LambdaQueryWrapper<ImChat> queryWrapper1 = new LambdaQueryWrapper<>();queryWrapper1.eq(ImChat::getUserId, userId).eq(ImChat::getChatType, messageDTO.getChatType()).eq(ImChat::getTargetId, friendUserId).eq(ImChat::getDelFlag, false);ImChat chat = chatService.getOne(queryWrapper1);if (chat == null) {chat = new ImChat();chat.setUserId(userId);chat.setChatType(messageDTO.getChatType());chat.setTargetId(friendUserId);chat.setDelFlag(false);chatService.save(chat);}messageDTO.setChatId(chat.getId());}// 消息ImMessage message = new ImMessage();message.setSenderId(userId);message.setChatId(messageDTO.getChatId());message.setMessageType(messageDTO.getMessageType());message.setMessage(messageDTO.getMessage());message.setDelFlag(false);baseMapper.insert(message);// 发送ImMessageReceiver messageReceiver = new ImMessageReceiver();messageReceiver.setMessageId(message.getId());messageReceiver.setReceiverId(friendUserId);messageReceiver.setDelFlag(false);messageReceiver.setReadFlag(false);messageReceiverService.save(messageReceiver);return message;}/*** 群聊消息** @param messageDTO* @param userId* @return*/private ImMessage sendGroupMessage(ImMessageDTO messageDTO, Long userId) {Long groupId = messageDTO.getTargetId();LambdaQueryWrapper<ImGroupUser> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(ImGroupUser::getGroupId, groupId).eq(ImGroupUser::getUserId, userId).eq(ImGroupUser::getDelFlag, false);ImGroupUser imGroupUser = groupUserService.getOne(queryWrapper);if (imGroupUser == null) {throw new ServiceException("无法发言,您已不在群中");}// 聊天会话Long chatId = messageDTO.getChatId();if (chatId == null) {LambdaQueryWrapper<ImChat> queryWrapper1 = new LambdaQueryWrapper<>();queryWrapper1.eq(ImChat::getUserId, userId).eq(ImChat::getChatType, messageDTO.getChatType()).eq(ImChat::getTargetId, groupId).eq(ImChat::getDelFlag, false);ImChat chat = chatService.getOne(queryWrapper1);if (chat == null) {chat = new ImChat();chat.setUserId(userId);chat.setChatType(messageDTO.getChatType());chat.setTargetId(groupId);chat.setDelFlag(false);chatService.save(chat);}messageDTO.setChatId(chat.getId());}// 消息ImMessage message = new ImMessage();message.setSenderId(userId);message.setChatId(messageDTO.getChatId());message.setMessageType(messageDTO.getMessageType());message.setMessage(messageDTO.getMessage());message.setDelFlag(false);baseMapper.insert(message);// 发给群成员List<SysUserVo> groupUserList = groupUserService.listGroupUser(groupId);List<ImMessageReceiver> messageReceiverList = new ArrayList<>();for (SysUserVo sysUserVo : groupUserList) {ImMessageReceiver messageReceiver = new ImMessageReceiver();messageReceiver.setMessageId(message.getId());messageReceiver.setReceiverId(sysUserVo.getId());messageReceiver.setDelFlag(false);messageReceiver.setReadFlag(false);if (userId.equals(sysUserVo.getId())) {messageReceiver.setReadFlag(true);}messageReceiverList.add(messageReceiver);}messageReceiverService.saveBatch(messageReceiverList);return message;}@Overridepublic List<ImChatMessageBO> listChatUnreadMessage(Long userId, List<Long> chatIdList) {return baseMapper.listChatUnreadMessage(userId, chatIdList);}@Overridepublic List<ImMessage> listLatestMessage(Long userId, List<Long> chatIdList) {return baseMapper.listLatestMessage(userId, chatIdList);}
}
package com.qiangesoft.im.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiangesoft.im.auth.UserUtil;
import com.qiangesoft.im.entity.ImMessageReceiver;
import com.qiangesoft.im.mapper.ImMessageReceiverMapper;
import com.qiangesoft.im.service.IImMessageReceiverService;
import org.springframework.stereotype.Service;/*** <p>* 群用户消息关系 服务实现类* </p>** @author qiangesoft* @since 2023-08-23*/
@Service
public class ImMessageReceiverServiceImpl extends ServiceImpl<ImMessageReceiverMapper, ImMessageReceiver> implements IImMessageReceiverService {@Overridepublic void updateRead(Long chatId, Long messageId) {baseMapper.updateRead(UserUtil.getUserId(), chatId, messageId);}
}

controller

package com.qiangesoft.im.controller;import com.qiangesoft.im.core.ImWebSocketServer;
import com.qiangesoft.im.entity.ImMessage;
import com.qiangesoft.im.pojo.bo.ImMessageBO;
import com.qiangesoft.im.pojo.dto.ImMessageDTO;
import com.qiangesoft.im.pojo.dto.ImMessageReadDTO;
import com.qiangesoft.im.pojo.dto.query.MessageQueryDTO;
import com.qiangesoft.im.pojo.dto.query.PageQueryDTO;
import com.qiangesoft.im.pojo.vo.ImMessageVO;
import com.qiangesoft.im.pojo.vo.PageResultVO;
import com.qiangesoft.im.pojo.vo.ResultInfo;
import com.qiangesoft.im.service.IImMessageReceiverService;
import com.qiangesoft.im.service.IImMessageService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;import java.util.List;/*** <p>* 群消息 前端控制器* </p>** @author qiangesoft* @date 2024-02-07*/
@Api(tags = "消息")
@RestController
@RequestMapping("/im/message")
public class ImMessageController {@Autowiredprivate IImMessageService messageService;@Autowiredprivate IImMessageReceiverService messageReceiverService;@GetMapping@ApiOperation(value = "消息列表")public ResultInfo<PageResultVO<ImMessageVO>> listMessage(@Validated PageQueryDTO pageQuery, @Validated MessageQueryDTO messageQuery) {PageResultVO<ImMessageVO> pageResult = messageService.listMessage(pageQuery, messageQuery);return ResultInfo.ok(pageResult);}@GetMapping("/unread")@ApiOperation(value = "未读消息列表")public ResultInfo<List<ImMessageVO>> listUnreadMessage(Long chatId) {List<ImMessageVO> messageList = messageService.listUnreadMessage(chatId);return ResultInfo.ok(messageList);}@PostMapping("/send")@ApiOperation(value = "发送消息")public ResultInfo<Void> send(@Validated @RequestBody ImMessageDTO messageDTO) {ImMessage message = messageService.send(messageDTO);// 发送消息ImMessageBO messageBO = new ImMessageBO();messageBO.setId(message.getId());messageBO.setSenderId(message.getSenderId());messageBO.setChatId(message.getChatId());messageBO.setMessageType(message.getMessageType());messageBO.setMessage(message.getMessage());messageBO.setSendTime(message.getCreateTime());messageBO.setChatType(messageDTO.getChatType());messageBO.setTargetId(messageDTO.getTargetId());messageBO.setExtra(messageDTO.getExtra());messageBO.setTimestamp(messageDTO.getTimestamp());ImWebSocketServer.sendMessage(messageBO);return ResultInfo.ok();}@PutMapping("/read")@ApiOperation(value = "阅读消息")public ResultInfo<Void> read(@Validated @RequestBody ImMessageReadDTO messageReadDTO) {messageReceiverService.updateRead(messageReadDTO.getChatId(), messageReadDTO.getId());return ResultInfo.ok();}
}

2.2 聊天会话

mapper

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qiangesoft.im.mapper.ImChatMapper"><select id="listChat" resultType="com.qiangesoft.im.entity.ImChat">SELECT a.id,a.user_id,a.chat_type,a.target_id,a.del_flag,a.top_flag,b.create_timeFROM im_chat aINNER JOIN (SELECT chat_id, max(create_time) create_time FROM im_message GROUP BY chat_id) bON a.id = b.chat_idWHERE a.del_flag = FALSEAND a.user_id = #{userId}ORDER BY a.top_flag, b.create_time</select></mapper>
package com.qiangesoft.im.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qiangesoft.im.entity.ImChat;
import org.apache.ibatis.annotations.Param;import java.util.List;/*** <p>* 聊天 Mapper 接口* </p>** @author qiangesoft* @date 2024-02-07*/
public interface ImChatMapper extends BaseMapper<ImChat> {/*** 聊天列表** @return*/List<ImChat> listChat(@Param("userId") Long userId);}

service

package com.qiangesoft.im.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.qiangesoft.im.entity.ImChat;
import com.qiangesoft.im.pojo.vo.ImChatVO;import java.util.List;/*** <p>* 聊天 服务类* </p>** @author qiangesoft* @date 2024-02-07*/
public interface IImChatService extends IService<ImChat> {/*** 删除聊天** @param id*/void removeChat(Long id);/*** 聊天列表** @return*/List<ImChatVO> listChat();/*** 置顶聊天** @param id*/void setTop(Long id);/*** 取消置顶聊天** @param id*/void cancelTop(Long id);
}
package com.qiangesoft.im.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiangesoft.im.auth.UserUtil;
import com.qiangesoft.im.core.constant.ChatTypeEnum;
import com.qiangesoft.im.entity.*;
import com.qiangesoft.im.mapper.ImChatMapper;
import com.qiangesoft.im.pojo.bo.ImChatMessageBO;
import com.qiangesoft.im.pojo.vo.ImChatVO;
import com.qiangesoft.im.pojo.vo.ImMessageVO;
import com.qiangesoft.im.pojo.vo.SysUserVo;
import com.qiangesoft.im.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;import java.util.*;
import java.util.stream.Collectors;/*** <p>* 聊天 服务实现类* </p>** @author qiangesoft* @date 2024-02-07*/
@Service
public class ImChatServiceImpl extends ServiceImpl<ImChatMapper, ImChat> implements IImChatService {@Autowiredprivate ISysUserService sysUserService;@Autowiredprivate IImFriendService friendService;@Autowiredprivate IImGroupService groupService;@Autowiredprivate IImGroupUserService groupUserService;@Autowiredprivate IImMessageService messageService;@Overridepublic void removeChat(Long id) {LambdaUpdateWrapper<ImChat> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.eq(ImChat::getId, id).set(ImChat::getDelFlag, true);baseMapper.update(null, updateWrapper);}@Overridepublic List<ImChatVO> listChat() {List<ImChatVO> groupVOList = new ArrayList<>();Long userId = UserUtil.getUserId();List<ImChat> chatList = baseMapper.listChat(userId);if (CollectionUtils.isEmpty(chatList)) {return groupVOList;}// 聊天列表对象信息List<Long> friendIdList = chatList.stream().filter(e -> ChatTypeEnum.PERSON.getCode().equals(e.getChatType())).map(ImChat::getTargetId).collect(Collectors.toList());Map<Long, ImFriend> friendMap = new HashMap<>();Map<Long, SysUser> sysUserMap = new HashMap<>();if (!CollectionUtils.isEmpty(friendIdList)) {List<ImFriend> friendList = friendService.listByIds(friendIdList);friendMap = friendList.stream().collect(Collectors.toMap(ImFriend::getId, imFriend -> imFriend));List<Long> friendUserIdList = friendList.stream().map(ImFriend::getFriendUserId).collect(Collectors.toList());sysUserMap = sysUserService.listByIds(friendUserIdList).stream().collect(Collectors.toMap(SysUser::getId, sysUser -> sysUser));}List<Long> groupIdList = chatList.stream().filter(e -> ChatTypeEnum.GROUP.getCode().equals(e.getChatType())).map(ImChat::getTargetId).collect(Collectors.toList());Map<Long, ImGroup> groupMap = new HashMap<>();if (!CollectionUtils.isEmpty(groupIdList)) {groupMap = groupService.listByIds(groupIdList).stream().collect(Collectors.toMap(ImGroup::getId, imGroup -> imGroup));}// 未读消息List<Long> chatIdList = chatList.stream().map(ImChat::getId).collect(Collectors.toList());List<ImChatMessageBO> unReadMessageList = messageService.listChatUnreadMessage(userId, chatIdList);// 最新消息List<ImMessage> latestMessageList = messageService.listLatestMessage(userId, chatIdList);List<Long> friendChatIdList = chatList.stream().filter(e -> ChatTypeEnum.PERSON.getCode().equals(e.getChatType())).map(ImChat::getId).collect(Collectors.toList());List<Long> groupChatIdList = chatList.stream().filter(e -> ChatTypeEnum.GROUP.getCode().equals(e.getChatType())).map(ImChat::getId).collect(Collectors.toList());List<ImMessage> friendLatestMessageList = latestMessageList.stream().filter(e -> friendChatIdList.contains(e.getChatId())).collect(Collectors.toList());List<ImMessage> groupLatestMessageList = latestMessageList.stream().filter(e -> groupChatIdList.contains(e.getChatId())).collect(Collectors.toList());// 昵称List<Long> sendFriendIdList = friendLatestMessageList.stream().map(ImMessage::getSenderId).collect(Collectors.toList());Map<Long, String> remarkMap = new HashMap<>();if (!CollectionUtils.isEmpty(sendFriendIdList)) {LambdaQueryWrapper<ImFriend> fqueryWrapper = new LambdaQueryWrapper<>();fqueryWrapper.eq(ImFriend::getUserId, userId).in(ImFriend::getFriendUserId, sendFriendIdList);remarkMap = friendService.list(fqueryWrapper).stream().collect(Collectors.toMap(ImFriend::getFriendUserId, ImFriend::getRemark));}List<Long> sendGroupIdList = groupLatestMessageList.stream().map(ImMessage::getSenderId).collect(Collectors.toList());Map<Long, String> nicknameMap = new HashMap<>();if (!CollectionUtils.isEmpty(groupIdList) && !CollectionUtils.isEmpty(sendGroupIdList)) {LambdaQueryWrapper<ImGroupUser> gqueryWrapper = new LambdaQueryWrapper<>();gqueryWrapper.in(ImGroupUser::getGroupId, groupIdList).in(ImGroupUser::getUserId, sendGroupIdList);nicknameMap = groupUserService.list(gqueryWrapper).stream().collect(Collectors.toMap(ImGroupUser::getUserId, ImGroupUser::getNickName));}for (ImChat chat : chatList) {ImChatVO vo = new ImChatVO();vo.setId(chat.getId());
//            vo.setAvatar(avatarMap.get());Long targetId = chat.getTargetId();String chatType = chat.getChatType();vo.setTargetId(targetId);vo.setChatType(chatType);// 未读消息数Optional<ImChatMessageBO> first = unReadMessageList.stream().filter(e -> e.getChatId().equals(chat.getId())).findFirst();Integer unreadNum = first.isPresent() ? first.get().getUnreadNum() : 0;vo.setUnreadNum(unreadNum);String name;String avatar;if (ChatTypeEnum.PERSON.getCode().equals(chatType)) {ImFriend friend = friendMap.get(targetId);name = friend.getRemark();SysUser sysUser = sysUserMap.get(friend.getFriendUserId());avatar = sysUser.getAvatar();Optional<ImMessage> firstLatest = friendLatestMessageList.stream().filter(e -> e.getChatId().equals(chat.getId())).findFirst();if (firstLatest.isPresent()) {ImMessage message = firstLatest.get();ImMessageVO messageVO = new ImMessageVO();messageVO.setId(message.getId());messageVO.setMessageType(message.getMessageType());messageVO.setMessage(message.getMessage());messageVO.setReadFlag(false);messageVO.setSendTime(message.getCreateTime());// 发送人SysUserVo sysUserVo = new SysUserVo();Long senderId = message.getSenderId();sysUserVo.setId(senderId);sysUserVo.setNickName(remarkMap.get(senderId));messageVO.setSender(sysUserVo);vo.setLatestMessage(messageVO);}} else {ImGroup group = groupMap.get(targetId);name = group.getName();avatar = group.getAvatar();Optional<ImMessage> firstLatest = groupLatestMessageList.stream().filter(e -> e.getChatId().equals(chat.getId())).findFirst();if (firstLatest.isPresent()) {ImMessage message = firstLatest.get();ImMessageVO messageVO = new ImMessageVO();messageVO.setId(message.getId());messageVO.setMessageType(message.getMessageType());messageVO.setMessage(message.getMessage());messageVO.setReadFlag(false);messageVO.setSendTime(message.getCreateTime());// 发送人SysUserVo sysUserVo = new SysUserVo();Long senderId = message.getSenderId();sysUserVo.setId(senderId);sysUserVo.setNickName(nicknameMap.get(senderId));messageVO.setSender(sysUserVo);vo.setLatestMessage(messageVO);}}vo.setName(name);vo.setAvatar(avatar);groupVOList.add(vo);}return groupVOList;}@Overridepublic void setTop(Long id) {LambdaUpdateWrapper<ImChat> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.eq(ImChat::getId, id).set(ImChat::getTopFlag, true);baseMapper.update(null, updateWrapper);}@Overridepublic void cancelTop(Long id) {LambdaUpdateWrapper<ImChat> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.eq(ImChat::getId, id).set(ImChat::getTopFlag, false);baseMapper.update(null, updateWrapper);}}
package com.qiangesoft.im.controller;import com.qiangesoft.im.pojo.vo.ImChatVO;
import com.qiangesoft.im.pojo.vo.ResultInfo;
import com.qiangesoft.im.service.IImChatService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;import java.util.List;/*** <p>* 群组 前端控制器* </p>** @author qiangesoft* @date 2024-02-07*/
@Api(tags = "聊天")
@RequiredArgsConstructor
@RestController
@RequestMapping("/im/chat")
public class ImChatController {private final IImChatService chatService;@DeleteMapping("/{id}")@ApiOperation(value = "删除聊天")public ResultInfo<Void> removeChat(@PathVariable Long id) {chatService.removeChat(id);return ResultInfo.ok();}@GetMapping@ApiOperation(value = "聊天列表")public ResultInfo<List<ImChatVO>> listChat() {return ResultInfo.ok(chatService.listChat());}@PutMapping("/setTop/{id}")@ApiOperation(value = "置顶聊天")public ResultInfo<Void> setTop(@PathVariable Long id) {chatService.setTop(id);return ResultInfo.ok();}@PutMapping("/cancelTop/{id}")@ApiOperation(value = "取消置顶聊天")public ResultInfo<Void> cancelTop(@PathVariable Long id) {chatService.cancelTop(id);return ResultInfo.ok();}}

三、消息发送接收测试

在这里插入图片描述
在这里插入图片描述

四、源码地址

源码地址:https://gitee.com/qiangesoft/boot-business/tree/master/boot-business-im

后续内容见下章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.bcls.cn/LRra/1547.shtml

如若内容造成侵权/违法违规/事实不符,请联系编程老四网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

力扣面试150 验证回文串 双指针 Character API

Problem: 125. 验证回文串 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 参考题解 Character.isLetterorDigit(char c)&#xff1a;判读字符 c 是否是字母或者数字 Character.toLowerCase(char c)&#xff1a;将字符 c 转换为小写字母 复杂度 时间复杂度: …

开源CMS Drupal本地快速部署并实现无公网ip环境远程访问

文章目录 前言1. Docker安装Drupal2. 本地局域网访问3 . Linux 安装cpolar4. 配置Drupal公网访问地址5. 公网远程访问Drupal6. 固定Drupal 公网地址 前言 Dupal是一个强大的CMS&#xff0c;适用于各种不同的网站项目&#xff0c;从小型个人博客到大型企业级门户网站。它的学习…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之NavDestination组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之NavDestination组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、NavDestination组件 作为NavRouter组件的子组件&#xff0c;用于显示导…

SQL-2

刷题知识点&#xff1a; null不能用这种判断&#xff0c;要用is null 或者is not null 或者可用 ifnull来判断。 明确&#xff1a;数据库DB是数据存储仓库。 数据库管理系统&#xff08;Database management system&#xff0c;DBMS&#xff09;&#xff0c;是操纵和管理数据库…

Aspose.Words For JAVA 动态制作多维度表格(涵2024最新无水印包)

全网最全Aspose.Words For JAVA 高级使用教程: CSDNhttps://blog.csdn.net/LiHaoHang6/article/details/133989664?spm1001.2014.3001.5501 运行截图&#xff1a; 所谓多维度表格通常包含多个维度, 每个维度都代表一种数据属性,多维度表格可以用于数据分析&#xff0c;通过不…

嵌入式学习-C++Day7QT Day1

思维导图 作业&#xff1a;窗口的一些操作的实现 #include "mywidget.h"Mywidget::Mywidget(QWidget *parent): QWidget(parent) {this->setWindowTitle("QQ");this->setWindowIcon(QIcon("C:\\Users\\xuyan\\Desktop\\others\\1.jpg"));…

Linux网络编程套接字

目录 前言 一、预备知识 1.1 源IP地址和目的IP地址 1.2 区分端口号和进程ID 1.3 TCP协议和UDP协议 1.4 网络字节序 二、socket编程接口 2.1 socket套接字的概念 2.2 socket常见API 2.3 sockaddr结构 三、关于IP和Port的绑定问题 四、编写简单的UDP服务端和客户端 前…

Json-序列化字符串时间格式问题

序列化字符串时间格式问题 一、项目场景二、问题描述三、解决方案 一、项目场景 最近C#中需要将实体进行json序列化&#xff0c;使用了Newtonsoft.Json public static void TestJson(){DataTable dt new DataTable();dt.Columns.Add("Age", Type.GetType("Sys…

白银交易新手指南:怎样选择可靠的现货交易平台?

在投资市场上&#xff0c;白银作为一种贵金属&#xff0c;具有较高的投资价值和风险防范功能。对于白银交易新手来说&#xff0c;选择一个可靠的现货交易平台是至关重要的。那么&#xff0c;如何挑选一个适合自己的现货交易平台呢&#xff1f; 1. 平台资质 一个正规的现货交易…

线阵相机之帧超时

1 帧超时的效果 在帧超时时间内相机若未采集完一张图像所需的行数&#xff0c;则相机会直接完成这张图像的采集&#xff0c;并自动将缺失行数补黑出图&#xff0c;机制有以下几种选择&#xff1a; 1. 丢弃整张补黑的图像 2. 保留补黑部分出图 3.丢弃补黑部分出图

15:00面试,15:08就出来了,问的问题有点变态。。

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 刚从小厂出来&#xff0c;没想到在另一家公司我又寄了。 在这家…

嵌入式Qt 第一个Qt项目

一.创建Qt项目 打开Qt Creator 界面选择 New Project或者选择菜单栏 【文件】-【新建文件或项目】菜单项 弹出New Project对话框&#xff0c;选择Qt Widgets Application 选择【Choose】按钮&#xff0c;弹出如下对话框 设置项目名称和路径&#xff0c;按照向导进行下一步 选…

QT中事件过滤器

Qt添加事件过滤器&#xff0c;设置拖放listWidget、TreeWidget、TableWidget控件。 #include "mainwindow.h" #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupUi(this)…

大数据技术之 Kafka

大数据技术之 Kafka 文章目录 大数据技术之 Kafka第 1 章 Kafka 概述1.1 定义1.2 消息队列1.2.1 传统消息队列的应用场景1.2.2 消息队列的两种模式 1.3 Kafka 基础架构 第 2 章 Kafka 快速入门2.1 安装部署2.1.1 集群规划2.1.2 集群部署2.1.3 集群启停脚本 2.2 Kafka 命令行操作…

C++学习:list

1.list的定义和结构 list的使用频率不高&#xff0c;在做题时几乎遇不到需要使用list的情景。list是一种双向链表容器&#xff0c;它是标准模板库(STL)提供的一种序列容器。list容器以节点(node的形式存储元素&#xff0c;并使用指针将这些节点链接在一起&#xff0c;形成一个…

深入理解input命令只支持单指,是否可以修改实现多指?-千里马android framework实战开发

背景&#xff1a; 经常使用的input命令如下&#xff1a; 正常情况下一般要模拟一个触摸点击事件一般用如下命令 input tap 200 300 执行完成后手机画面一般就会响应一个click事件&#xff0c;这里发现一般都是一个手指&#xff0c;一直寻找看看input命令是否可以支持多个手指…

【RT-DETR有效改进】大核注意力 | LSKAttention助力极限涨点

一、本文介绍 在这篇文章中,我们将讲解如何将LSKAttention大核注意力机制应用于RT-DETR,以实现显著的性能提升。首先,我们介绍LSKAttention机制的基本原理,它主要通过将深度卷积层的2D卷积核分解为水平和垂直1D卷积核,减少了计算复杂性和内存占用。接着,我们介绍将这一…

Python爬虫之Splash详解

爬虫专栏&#xff1a;http://t.csdnimg.cn/WfCSx Splash 的使用 Splash 是一个 JavaScript 渲染服务&#xff0c;是一个带有 HTTP API 的轻量级浏览器&#xff0c;同时它对接了 Python 中的 Twisted 和 QT 库。利用它&#xff0c;我们同样可以实现动态渲染页面的抓取。 1. 功…

有关片式网络变压器的几个常见参数问题

有关片式网络变压器的几个常见问题&#xff1f; 一&#xff0c;线圈漆包线耐压是多少&#xff1f; 答&#xff1a;50V, 50V,不是500V,传统网络变压器耐压为1500V. 因此&#xff0c;它不能像传统网变一样单体打耐压 二&#xff0c;产品极限工作温度是多少&#xff1f;-40~125度 …

【AI链接】 大模型语言模型网站链接

目录 GPT类1. chatgpt2. GROP3. Google AI Studio4. Moonshot AI (国内) 解读论文类&#xff1a;1. txyz 编程辅助插件&#xff1a;1. Fitten Code GPT类 1. chatgpt https://chat.openai.com/ 2. GROP https://groq.com/ 3. Google AI Studio https://aistudio.google…
推荐文章