主页 > 手机  > 

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


前言

紧接着上文《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"> SELECT b.id, b.sender_id, b.message_type, b.message, b.create_time, c.read_flag FROM im_chat a INNER JOIN im_message b ON a.id = b.chat_id INNER JOIN im_message_receiver c ON b.id = c.message_id WHERE a.del_flag = FALSE AND 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"> SELECT b.id, b.sender_id, b.message_type, b.message, b.create_time, c.read_flag FROM im_chat a INNER JOIN im_message b ON a.id = b.chat_id INNER JOIN im_message_receiver c ON b.id = c.message_id WHERE a.del_flag = FALSE AND a.id = #{chatId} AND c.receiver_id = #{userId} AND c.read_flag = FALSE ORDER BY b.id DESC </select> <select id="listChatUnreadMessage" resultType="com.qiangesoft.im.pojo.bo.ImChatMessageBO"> SELECT a.chat_id AS chatId, COUNT(a.id) AS unreadNum FROM im_message a INNER JOIN im_message_receiver b ON a.id=b.message_id WHERE b.receiver_id=#{userId} AND b.read_flag=FALSE AND 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 (SELECT max(a.id) FROM im_message a INNER JOIN im_message_receiver b ON a.id=b.message_id WHERE b.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 a INNER JOIN im_message b ON b.id = a.message_id INNER JOIN im_chat c ON c.id = b.chat_id SET read_flag = 1 WHERE a.read_flag = 0 AND 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 @Autowired private IImChatService chatService; @Autowired private IImFriendService friendService; @Autowired private IImGroupUserService groupUserService; @Autowired private IImMessageReceiverService messageReceiverService; @Autowired private ISysUserService sysUserService; @Override public 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; } @Override public 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) @Override public 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; } @Override public List<ImChatMessageBO> listChatUnreadMessage(Long userId, List<Long> chatIdList) { return baseMapper.listChatUnreadMessage(userId, chatIdList); } @Override public 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 { @Override public 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 { @Autowired private IImMessageService messageService; @Autowired private 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_time FROM im_chat a INNER JOIN (SELECT chat_id, max(create_time) create_time FROM im_message GROUP BY chat_id) b ON a.id = b.chat_id WHERE a.del_flag = FALSE AND 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 { @Autowired private ISysUserService sysUserService; @Autowired private IImFriendService friendService; @Autowired private IImGroupService groupService; @Autowired private IImGroupUserService groupUserService; @Autowired private IImMessageService messageService; @Override public void removeChat(Long id) { LambdaUpdateWrapper<ImChat> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(ImChat::getId, id) .set(ImChat::getDelFlag, true); baseMapper.update(null, updateWrapper); } @Override public 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; } @Override public void setTop(Long id) { LambdaUpdateWrapper<ImChat> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(ImChat::getId, id) .set(ImChat::getTopFlag, true); baseMapper.update(null, updateWrapper); } @Override public 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

后续内容见下章

标签:

SpringBoot+WebSocket实现即时通讯(四)由讯客互联手机栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“SpringBoot+WebSocket实现即时通讯(四)