Commit fa474c6f03b5f3b156b5cc612bde8e6c0d9b15ee

Authored by YunaiV
1 parent 84304718

✨ feat(group): 添加群聊成员限制及邀请好友功能

新增群聊成员人数限制,最大成员数为 500。实现邀请好友功能,确保被邀请人是当前用户的好友,若不是则返回相应错误提示。更新相关提示信息以提升用户体验。
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 {
37 37 ErrorCode GROUP_MEMBER_ALREADY_QUIT = new ErrorCode(1_040_500_002, "您已退出该群");
38 38 ErrorCode GROUP_OWNER_CANNOT_QUIT = new ErrorCode(1_040_500_003, "您是群主,不可退出群聊");
39 39 ErrorCode GROUP_CANNOT_REMOVE_SELF = new ErrorCode(1_040_500_004, "不能移除自己");
  40 + ErrorCode GROUP_MEMBER_EXCEED = new ErrorCode(1_040_500_005, "群聊人数不能超过{}人");
  41 + ErrorCode GROUP_INVITE_NOT_FRIEND = new ErrorCode(1_040_500_006, "'{}' 不是您的好友,邀请失败");
40 42  
41 43 // ========== 好友 (1-040-600-000) ==========
42 44 ErrorCode FRIEND_NOT_EXISTS = new ErrorCode(1_040_600_000, "好友关系不存在");
... ...
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 {
13 13 int MESSAGE_MAX_PULL_SIZE = 1000;
14 14  
15 15 /**
  16 + * 群最大成员人数
  17 + */
  18 + int MAX_GROUP_MEMBER = 500;
  19 +
  20 + /**
16 21 * 消息撤回时间限制(分钟)
17 22 */
18 23 int MESSAGE_RECALL_TIMEOUT_MINUTES = 5;
... ... @@ -50,14 +55,18 @@ public interface ImCommonConstants {
50 55 /**
51 56 * 群解散系统提示
52 57 */
53   - String GROUP_DISSOLVE_TIP_MESSAGE = "'{}'解散了群聊";
  58 + String GROUP_DISSOLVE_TIP_MESSAGE = "\"{}\"解散了群聊";
54 59 /**
55 60 * 退群系统提示
56 61 */
57   - String GROUP_QUIT_TIP_MESSAGE = "您已退出群聊";
  62 + String GROUP_QUIT_TIP_MESSAGE = "你已退出群聊";
58 63 /**
59 64 * 踢出群系统提示
60 65 */
61   - String GROUP_REMOVE_TIP_MESSAGE = "您已被移出群聊";
  66 + String GROUP_REMOVE_TIP_MESSAGE = "你已被移出群聊";
  67 + /**
  68 + * 邀请入群系统提示
  69 + */
  70 + String GROUP_INVITE_TIP_MESSAGE = "\"{}\"邀请\"{}\"加入了群聊";
62 71  
63 72 }
... ...
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;
5 5 import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO;
6 6 import org.apache.ibatis.annotations.Mapper;
7 7  
  8 +import java.util.Collection;
8 9 import java.util.List;
9 10  
10 11 /**
... ... @@ -26,5 +27,20 @@ public interface ImFriendMapper extends BaseMapperX<ImFriendDO> {
26 27 .eq(ImFriendDO::getUserId, userId)
27 28 .orderByDesc(ImFriendDO::getId));
28 29 }
  30 + default List<ImFriendDO> selectListByUserIdAndStatus(Long userId, Integer status) {
  31 + return selectList(new LambdaQueryWrapperX<ImFriendDO>()
  32 + .eq(ImFriendDO::getUserId, userId)
  33 + .eq(ImFriendDO::getStatus, status)
  34 + .orderByDesc(ImFriendDO::getId));
  35 + }
  36 +
  37 + default List<ImFriendDO> selectListByUserIdAndFriendUserIdsAndStatus(Long userId,
  38 + Collection<Long> friendUserIds,
  39 + Integer status) {
  40 + return selectList(new LambdaQueryWrapperX<ImFriendDO>()
  41 + .eq(ImFriendDO::getUserId, userId)
  42 + .in(ImFriendDO::getFriendUserId, friendUserIds)
  43 + .eq(ImFriendDO::getStatus, status));
  44 + }
29 45  
30 46 }
... ...
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;
3 3 import cn.iocoder.yudao.module.im.controller.admin.friend.vo.ImFriendUpdateReqVO;
4 4 import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO;
5 5  
  6 +import java.util.Collection;
6 7 import java.util.List;
7 8  
8 9 /**
... ... @@ -33,6 +34,15 @@ public interface ImFriendService {
33 34 List<ImFriendDO> getFriendList(Long userId);
34 35  
35 36 /**
  37 + * 获得当前用户与指定用户之间的有效好友列表(仅 ENABLE 状态)
  38 + *
  39 + * @param userId 用户编号
  40 + * @param friendUserIds 需要校验的好友用户编号集合
  41 + * @return 有效好友关系列表
  42 + */
  43 + List<ImFriendDO> getActiveFriendList(Long userId, Collection<Long> friendUserIds);
  44 +
  45 + /**
36 46 * 查询一个好友关系记录(仅限本人视角)
37 47 *
38 48 * @param userId 当前登录用户编号
... ...
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;
21 21 import org.springframework.validation.annotation.Validated;
22 22  
23 23 import java.time.LocalDateTime;
  24 +import java.util.Collection;
24 25 import java.util.List;
25 26 import java.util.Objects;
26 27  
... ... @@ -66,6 +67,12 @@ public class ImFriendServiceImpl implements ImFriendService {
66 67 }
67 68  
68 69 @Override
  70 + public List<ImFriendDO> getActiveFriendList(Long userId, Collection<Long> friendUserIds) {
  71 + return imFriendMapper.selectListByUserIdAndFriendUserIdsAndStatus(userId, friendUserIds,
  72 + CommonStatusEnum.ENABLE.getStatus());
  73 + }
  74 +
  75 + @Override
69 76 public ImFriendDO getFriend(Long userId, Long friendUserId) {
70 77 return imFriendMapper.selectByUserIdAndFriendUserId(userId, friendUserId);
71 78 }
... ...
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;
13 13 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberInviteReqVO;
14 14 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberRemoveReqVO;
15 15 import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberUpdateReqVO;
  16 +import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO;
16 17 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
17 18 import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
18 19 import cn.iocoder.yudao.module.im.dal.mysql.group.ImGroupMapper;
  20 +import cn.iocoder.yudao.module.im.service.friend.ImFriendService;
19 21 import cn.iocoder.yudao.module.im.service.message.ImGroupMessageService;
20   -import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
21   -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
22 22 import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService;
23 23 import cn.iocoder.yudao.module.im.service.websocket.dto.ImGroupMessageDTO;
  24 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  25 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
24 26 import jakarta.annotation.Resource;
25 27 import org.springframework.cache.annotation.CacheEvict;
26 28 import org.springframework.cache.annotation.Cacheable;
... ... @@ -30,11 +32,7 @@ import org.springframework.transaction.annotation.Transactional;
30 32 import org.springframework.validation.annotation.Validated;
31 33  
32 34 import java.time.LocalDateTime;
33   -import java.util.Collections;
34   -import java.util.HashSet;
35   -import java.util.List;
36   -import java.util.Map;
37   -import java.util.Set;
  35 +import java.util.*;
38 36 import java.util.stream.Collectors;
39 37  
40 38 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
... ... @@ -66,6 +64,9 @@ public class ImGroupServiceImpl implements ImGroupService {
66 64 private ImWebSocketService webSocketService;
67 65  
68 66 @Resource
  67 + private ImFriendService friendService;
  68 +
  69 + @Resource
69 70 private AdminUserApi adminUserApi;
70 71  
71 72 // ==================== 群的写操作 ====================
... ... @@ -164,8 +165,15 @@ public class ImGroupServiceImpl implements ImGroupService {
164 165 if (CollUtil.isEmpty(inviteReqVO.getMemberUserIds())) {
165 166 return;
166 167 }
167   - // 1.3 校验群人数上限
  168 + // 1.3 校验被邀请人都是当前用户的好友
168 169 List<Long> memberUserIds = inviteReqVO.getMemberUserIds();
  170 + List<ImFriendDO> friends = friendService.getActiveFriendList(userId, memberUserIds);
  171 + Set<Long> friendUserIds = convertSet(friends, ImFriendDO::getFriendUserId);
  172 + Collection<Long> notFriendUserIds = CollUtil.subtract(memberUserIds, friendUserIds);
  173 + if (CollUtil.isNotEmpty(notFriendUserIds)) {
  174 + throw exception(GROUP_INVITE_NOT_FRIEND, getUserNicknames(notFriendUserIds));
  175 + }
  176 + // 1.4 校验群人数上限
169 177 if (activeMembers.size() + memberUserIds.size() > MAX_GROUP_MEMBER) {
170 178 throw exception(GROUP_MEMBER_EXCEED, MAX_GROUP_MEMBER);
171 179 }
... ... @@ -177,12 +185,8 @@ public class ImGroupServiceImpl implements ImGroupService {
177 185 webSocketService.sendGroupMessageAsync(new HashSet<>(memberUserIds),
178 186 ImGroupMessageDTO.ofGroupCreate(userId, groupId));
179 187 // 3.2 发送邀请提示消息给所有群成员
180   - AdminUserRespDTO inviter = adminUserApi.getUser(userId);
181   - Map<Long, AdminUserRespDTO> invitedUserMap = adminUserApi.getUserMap(memberUserIds);
182   - String invitedNames = memberUserIds.stream()
183   - .map(id -> invitedUserMap.containsKey(id) ? invitedUserMap.get(id).getNickname() : String.valueOf(id))
184   - .collect(Collectors.joining(","));
185   - String tipContent = StrUtil.format(GROUP_INVITE_TIP_MESSAGE, inviter.getNickname(), invitedNames);
  188 + String tipContent = StrUtil.format(GROUP_INVITE_TIP_MESSAGE,
  189 + getUserNicknames(List.of(userId)), getUserNicknames(memberUserIds));
186 190 List<ImGroupMemberDO> allMembers = groupMemberService.getActiveGroupMemberListByGroupId(groupId);
187 191 Set<Long> allMemberUserIds = convertSet(allMembers, ImGroupMemberDO::getUserId);
188 192 groupMessageService.sendTipGroupMessage(userId, groupId, allMemberUserIds, tipContent);
... ... @@ -284,4 +288,17 @@ public class ImGroupServiceImpl implements ImGroupService {
284 288 return SpringUtil.getBean(getClass());
285 289 }
286 290  
  291 + /**
  292 + * 根据用户编号集合,拼接用户昵称字符串(逗号分隔)
  293 + *
  294 + * @param userIds 用户编号集合
  295 + * @return 昵称字符串,如 "张三,李四"
  296 + */
  297 + private String getUserNicknames(Collection<Long> userIds) {
  298 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
  299 + return userIds.stream()
  300 + .map(id -> userMap.containsKey(id) ? userMap.get(id).getNickname() : String.valueOf(id))
  301 + .collect(Collectors.joining(","));
  302 + }
  303 +
287 304 }
288 305 \ No newline at end of file
... ...