From fa474c6f03b5f3b156b5cc612bde8e6c0d9b15ee Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 23 Apr 2026 00:59:17 +0800 Subject: [PATCH] ✨ feat(group): 添加群聊成员限制及邀请好友功能 --- yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/ErrorCodeConstants.java | 2 ++ yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/ImCommonConstants.java | 15 ++++++++++++--- yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/friend/ImFriendMapper.java | 16 ++++++++++++++++ yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendService.java | 10 ++++++++++ yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImpl.java | 7 +++++++ yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java | 45 +++++++++++++++++++++++++++++++-------------- 6 files changed, 78 insertions(+), 17 deletions(-) diff --git a/yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/ErrorCodeConstants.java b/yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/ErrorCodeConstants.java index 4426873..dea7376 100644 --- a/yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/ErrorCodeConstants.java +++ b/yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/ErrorCodeConstants.java @@ -37,6 +37,8 @@ public interface ErrorCodeConstants { ErrorCode GROUP_MEMBER_ALREADY_QUIT = new ErrorCode(1_040_500_002, "您已退出该群"); ErrorCode GROUP_OWNER_CANNOT_QUIT = new ErrorCode(1_040_500_003, "您是群主,不可退出群聊"); ErrorCode GROUP_CANNOT_REMOVE_SELF = new ErrorCode(1_040_500_004, "不能移除自己"); + ErrorCode GROUP_MEMBER_EXCEED = new ErrorCode(1_040_500_005, "群聊人数不能超过{}人"); + ErrorCode GROUP_INVITE_NOT_FRIEND = new ErrorCode(1_040_500_006, "'{}' 不是您的好友,邀请失败"); // ========== 好友 (1-040-600-000) ========== ErrorCode FRIEND_NOT_EXISTS = new ErrorCode(1_040_600_000, "好友关系不存在"); diff --git a/yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/ImCommonConstants.java b/yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/ImCommonConstants.java index 52d4bca..924b96f 100644 --- a/yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/ImCommonConstants.java +++ b/yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/ImCommonConstants.java @@ -13,6 +13,11 @@ public interface ImCommonConstants { int MESSAGE_MAX_PULL_SIZE = 1000; /** + * 群最大成员人数 + */ + int MAX_GROUP_MEMBER = 500; + + /** * 消息撤回时间限制(分钟) */ int MESSAGE_RECALL_TIMEOUT_MINUTES = 5; @@ -50,14 +55,18 @@ public interface ImCommonConstants { /** * 群解散系统提示 */ - String GROUP_DISSOLVE_TIP_MESSAGE = "'{}'解散了群聊"; + String GROUP_DISSOLVE_TIP_MESSAGE = "\"{}\"解散了群聊"; /** * 退群系统提示 */ - String GROUP_QUIT_TIP_MESSAGE = "您已退出群聊"; + String GROUP_QUIT_TIP_MESSAGE = "你已退出群聊"; /** * 踢出群系统提示 */ - String GROUP_REMOVE_TIP_MESSAGE = "您已被移出群聊"; + String GROUP_REMOVE_TIP_MESSAGE = "你已被移出群聊"; + /** + * 邀请入群系统提示 + */ + String GROUP_INVITE_TIP_MESSAGE = "\"{}\"邀请\"{}\"加入了群聊"; } diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/friend/ImFriendMapper.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/friend/ImFriendMapper.java index 620f3b6..dc716f0 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/friend/ImFriendMapper.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/friend/ImFriendMapper.java @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO; import org.apache.ibatis.annotations.Mapper; +import java.util.Collection; import java.util.List; /** @@ -26,5 +27,20 @@ public interface ImFriendMapper extends BaseMapperX { .eq(ImFriendDO::getUserId, userId) .orderByDesc(ImFriendDO::getId)); } + default List selectListByUserIdAndStatus(Long userId, Integer status) { + return selectList(new LambdaQueryWrapperX() + .eq(ImFriendDO::getUserId, userId) + .eq(ImFriendDO::getStatus, status) + .orderByDesc(ImFriendDO::getId)); + } + + default List selectListByUserIdAndFriendUserIdsAndStatus(Long userId, + Collection friendUserIds, + Integer status) { + return selectList(new LambdaQueryWrapperX() + .eq(ImFriendDO::getUserId, userId) + .in(ImFriendDO::getFriendUserId, friendUserIds) + .eq(ImFriendDO::getStatus, status)); + } } diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendService.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendService.java index cde0a60..9dce3bd 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendService.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendService.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.im.service.friend; import cn.iocoder.yudao.module.im.controller.admin.friend.vo.ImFriendUpdateReqVO; import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO; +import java.util.Collection; import java.util.List; /** @@ -33,6 +34,15 @@ public interface ImFriendService { List getFriendList(Long userId); /** + * 获得当前用户与指定用户之间的有效好友列表(仅 ENABLE 状态) + * + * @param userId 用户编号 + * @param friendUserIds 需要校验的好友用户编号集合 + * @return 有效好友关系列表 + */ + List getActiveFriendList(Long userId, Collection friendUserIds); + + /** * 查询一个好友关系记录(仅限本人视角) * * @param userId 当前登录用户编号 diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImpl.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImpl.java index 2fca13a..fd576be 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImpl.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImpl.java @@ -21,6 +21,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.time.LocalDateTime; +import java.util.Collection; import java.util.List; import java.util.Objects; @@ -66,6 +67,12 @@ public class ImFriendServiceImpl implements ImFriendService { } @Override + public List getActiveFriendList(Long userId, Collection friendUserIds) { + return imFriendMapper.selectListByUserIdAndFriendUserIdsAndStatus(userId, friendUserIds, + CommonStatusEnum.ENABLE.getStatus()); + } + + @Override public ImFriendDO getFriend(Long userId, Long friendUserId) { return imFriendMapper.selectByUserIdAndFriendUserId(userId, friendUserId); } diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java index b653d27..dd3fdb7 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java @@ -13,14 +13,16 @@ import cn.iocoder.yudao.module.im.controller.admin.group.vo.ImGroupUpdateReqVO; import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberInviteReqVO; import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberRemoveReqVO; import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberUpdateReqVO; +import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO; import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO; import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; import cn.iocoder.yudao.module.im.dal.mysql.group.ImGroupMapper; +import cn.iocoder.yudao.module.im.service.friend.ImFriendService; import cn.iocoder.yudao.module.im.service.message.ImGroupMessageService; -import cn.iocoder.yudao.module.system.api.user.AdminUserApi; -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService; import cn.iocoder.yudao.module.im.service.websocket.dto.ImGroupMessageDTO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; @@ -30,11 +32,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.time.LocalDateTime; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -66,6 +64,9 @@ public class ImGroupServiceImpl implements ImGroupService { private ImWebSocketService webSocketService; @Resource + private ImFriendService friendService; + + @Resource private AdminUserApi adminUserApi; // ==================== 群的写操作 ==================== @@ -164,8 +165,15 @@ public class ImGroupServiceImpl implements ImGroupService { if (CollUtil.isEmpty(inviteReqVO.getMemberUserIds())) { return; } - // 1.3 校验群人数上限 + // 1.3 校验被邀请人都是当前用户的好友 List memberUserIds = inviteReqVO.getMemberUserIds(); + List friends = friendService.getActiveFriendList(userId, memberUserIds); + Set friendUserIds = convertSet(friends, ImFriendDO::getFriendUserId); + Collection notFriendUserIds = CollUtil.subtract(memberUserIds, friendUserIds); + if (CollUtil.isNotEmpty(notFriendUserIds)) { + throw exception(GROUP_INVITE_NOT_FRIEND, getUserNicknames(notFriendUserIds)); + } + // 1.4 校验群人数上限 if (activeMembers.size() + memberUserIds.size() > MAX_GROUP_MEMBER) { throw exception(GROUP_MEMBER_EXCEED, MAX_GROUP_MEMBER); } @@ -177,12 +185,8 @@ public class ImGroupServiceImpl implements ImGroupService { webSocketService.sendGroupMessageAsync(new HashSet<>(memberUserIds), ImGroupMessageDTO.ofGroupCreate(userId, groupId)); // 3.2 发送邀请提示消息给所有群成员 - AdminUserRespDTO inviter = adminUserApi.getUser(userId); - Map invitedUserMap = adminUserApi.getUserMap(memberUserIds); - String invitedNames = memberUserIds.stream() - .map(id -> invitedUserMap.containsKey(id) ? invitedUserMap.get(id).getNickname() : String.valueOf(id)) - .collect(Collectors.joining(",")); - String tipContent = StrUtil.format(GROUP_INVITE_TIP_MESSAGE, inviter.getNickname(), invitedNames); + String tipContent = StrUtil.format(GROUP_INVITE_TIP_MESSAGE, + getUserNicknames(List.of(userId)), getUserNicknames(memberUserIds)); List allMembers = groupMemberService.getActiveGroupMemberListByGroupId(groupId); Set allMemberUserIds = convertSet(allMembers, ImGroupMemberDO::getUserId); groupMessageService.sendTipGroupMessage(userId, groupId, allMemberUserIds, tipContent); @@ -284,4 +288,17 @@ public class ImGroupServiceImpl implements ImGroupService { return SpringUtil.getBean(getClass()); } + /** + * 根据用户编号集合,拼接用户昵称字符串(逗号分隔) + * + * @param userIds 用户编号集合 + * @return 昵称字符串,如 "张三,李四" + */ + private String getUserNicknames(Collection userIds) { + Map userMap = adminUserApi.getUserMap(userIds); + return userIds.stream() + .map(id -> userMap.containsKey(id) ? userMap.get(id).getNickname() : String.valueOf(id)) + .collect(Collectors.joining(",")); + } + } \ No newline at end of file -- libgit2 0.21.4