Commit 99121a25e0ea9e526a3db42dba1455c7075600e0
1 parent
0ce9edde
✨ feat(message): 优化群聊的 pullGroupMessageList 方法,解决空分页的风险(pro 增强)
Showing
2 changed files
with
162 additions
and
45 deletions
yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java
| @@ -51,8 +51,8 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { | @@ -51,8 +51,8 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { | ||
| 51 | 51 | ||
| 52 | /** | 52 | /** |
| 53 | * 仅用于规避群消息 pull 的"假空页": | 53 | * 仅用于规避群消息 pull 的"假空页": |
| 54 | - * 首批 raw 消息可能因入群时间或定向接收过滤后变成空列表,但后续更大的 id 仍然存在可见消息。 | ||
| 55 | - * 因此仅在过滤结果为空时,按本轮 raw 最大 id 向后再试几次。 | 54 | + * 首批消息可能因入群时间或定向接收过滤后变成空列表,但后续更大的 id 仍然存在可见消息。 |
| 55 | + * 因此仅在过滤结果为空时,按本轮消息最大 id 向后再试几次。 | ||
| 56 | */ | 56 | */ |
| 57 | private static final int PULL_GROUP_MESSAGE_EMPTY_RETRY_TIMES = 3; | 57 | private static final int PULL_GROUP_MESSAGE_EMPTY_RETRY_TIMES = 3; |
| 58 | 58 | ||
| @@ -111,56 +111,71 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { | @@ -111,56 +111,71 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { | ||
| 111 | // 0. 拉取时间窗:超过窗口的老消息不再通过离线通道推送 | 111 | // 0. 拉取时间窗:超过窗口的老消息不再通过离线通道推送 |
| 112 | LocalDateTime minSendTime = LocalDateTime.now().minusDays(MESSAGE_GROUP_PULL_MAX_DAYS); | 112 | LocalDateTime minSendTime = LocalDateTime.now().minusDays(MESSAGE_GROUP_PULL_MAX_DAYS); |
| 113 | 113 | ||
| 114 | - // 1. 拉取一批原始消息(包含当前在群、退群前补齐两部分),并过滤得到当前用户可见的结果 | ||
| 115 | - List<ImGroupMessageDO> result = Collections.emptyList(); | ||
| 116 | - Long currentMinId = minId; // 当前轮次的下限消息编号;初始为用户传入的 minId,后续会根据拉取结果更新 | 114 | + // 1.1 主查询:仅用"当前仍在群"的成员记录驱动;若首批消息过滤后为空,则允许内部重试 |
| 115 | + List<ImGroupMemberDO> activeMembers = groupMemberService.getActiveGroupMemberListByUserId(userId); | ||
| 116 | + Map<Long, ImGroupMemberDO> memberMap = convertMap(activeMembers, ImGroupMemberDO::getGroupId); | ||
| 117 | + List<ImGroupMessageDO> messages = new ArrayList<>(); | ||
| 118 | + if (CollUtil.isNotEmpty(activeMembers)) { | ||
| 119 | + List<Long> groupIds = convertList(activeMembers, ImGroupMemberDO::getGroupId); | ||
| 120 | + messages.addAll(pullActiveGroupMessageList(userId, groupIds, minId, size, minSendTime, memberMap)); | ||
| 121 | + } | ||
| 122 | + // 1.2 补齐"退群前"的消息: | ||
| 123 | + // - 继续基于用户原始 minId 单次查询,不能被主查询内部探测游标带着前进; | ||
| 124 | + // - 若 minId > 0 且能查到对应消息,则进一步把下限抬到该消息的 sendTime,避免把客户端已拥有的老消息再次推送。 | ||
| 125 | + messages.addAll(pullQuitGroupMessageList(userId, minId, size, minSendTime, memberMap)); | ||
| 126 | + | ||
| 127 | + // 2. 合并后统一过滤,得到当前用户可见的结果 | ||
| 128 | + List<ImGroupMessageDO> result = filterGroupMessageList(messages, memberMap, userId, size); | ||
| 129 | + | ||
| 130 | + // 3. 按当前用户补齐:消息已读态(相对 Redis 已读游标)、本人发送的回执消息的已读人数 | ||
| 131 | + appendMessageStatusAndReceipt(userId, result); | ||
| 132 | + log.info("[pullGroupMessageList][userId({}) minId({}) size({}) result({})]", userId, minId, size, result.size()); | ||
| 133 | + return result; | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + /** | ||
| 137 | + * 拉取当前仍在群主路径的消息。 | ||
| 138 | + * | ||
| 139 | + * 仅当首批消息过滤后仍无可见消息时,才按消息最大 id 继续向后探测, | ||
| 140 | + * 直到命中可见消息或确认该来源已耗尽。 | ||
| 141 | + */ | ||
| 142 | + private List<ImGroupMessageDO> pullActiveGroupMessageList(Long userId, List<Long> groupIds, Long minId, | ||
| 143 | + Integer size, LocalDateTime minSendTime, | ||
| 144 | + Map<Long, ImGroupMemberDO> memberMap) { | ||
| 145 | + // 1. 主查询内部探测游标:仅用于向后探测,不代表客户端真实已送达边界 | ||
| 146 | + Long activeMinId = minId; | ||
| 117 | for (int retryCount = 0; retryCount <= PULL_GROUP_MESSAGE_EMPTY_RETRY_TIMES; retryCount++) { | 147 | for (int retryCount = 0; retryCount <= PULL_GROUP_MESSAGE_EMPTY_RETRY_TIMES; retryCount++) { |
| 118 | - // 1.1 查询一批原始消息(包含当前在群、退群前补齐两部分) | ||
| 119 | - Map<Long, ImGroupMemberDO> memberMap = new HashMap<>(); // 群编号 → 群成员记录(包含当前在群、退群前补齐两部分) | ||
| 120 | - List<ImGroupMessageDO> messages = getGroupMessageList(userId, currentMinId, size, minSendTime, memberMap); | 148 | + // 2. 查询本轮消息;若已无更多消息,则当前主路径直接结束 |
| 149 | + List<ImGroupMessageDO> messages = groupMessageMapper.selectListByMinId(groupIds, activeMinId, | ||
| 150 | + minSendTime, size); | ||
| 121 | if (CollUtil.isEmpty(messages)) { | 151 | if (CollUtil.isEmpty(messages)) { |
| 122 | - break; | 152 | + return Collections.emptyList(); |
| 123 | } | 153 | } |
| 124 | - // 1.2 过滤得到当前用户可见的结果(按 id 升序) | ||
| 125 | - result = filterGroupMessageList(messages, memberMap, userId, size); | ||
| 126 | - // 已拿到可见消息,或原始消息已不足一页(说明没有继续重试的必要) | ||
| 127 | - if (CollUtil.isNotEmpty(result) || messages.size() < size) { | ||
| 128 | - break; | 154 | + boolean hasVisibleMessage = CollUtil.anyMatch(messages, |
| 155 | + message -> isMessageVisible(message, memberMap.get(message.getGroupId()), userId)); | ||
| 156 | + boolean sourceExhausted = messages.size() < size; | ||
| 157 | + // 3. 本轮已命中可见消息,或主查询来源已耗尽,直接返回这一轮消息 | ||
| 158 | + if (hasVisibleMessage || sourceExhausted) { | ||
| 159 | + return messages; | ||
| 129 | } | 160 | } |
| 130 | 161 | ||
| 131 | - // 1.3 更新 currentMinId,进行下一轮查询;如果本轮查询的最大消息编号没有前进,则说明后续没有更多消息了,直接 break | 162 | + // 4. 按本轮消息最大 id 推进内部游标,跳过这段不可见区间;若游标未前进则直接停止 |
| 132 | Long maxMessageId = getMaxValue(messages, ImGroupMessageDO::getId); | 163 | Long maxMessageId = getMaxValue(messages, ImGroupMessageDO::getId); |
| 133 | - if (maxMessageId == null || maxMessageId <= currentMinId) { | ||
| 134 | - break; | 164 | + if (maxMessageId <= activeMinId) { |
| 165 | + return Collections.emptyList(); | ||
| 135 | } | 166 | } |
| 136 | - currentMinId = maxMessageId; | 167 | + activeMinId = maxMessageId; |
| 137 | } | 168 | } |
| 138 | - | ||
| 139 | - // 2. 按当前用户补齐:消息已读态(相对 Redis 已读游标)、本人发送的回执消息的已读人数 | ||
| 140 | - appendMessageStatusAndReceipt(userId, result); | ||
| 141 | - log.info("[pullGroupMessageList][userId({}) minId({}) size({}) result({})]", userId, minId, size, result.size()); | ||
| 142 | - return result; | 169 | + return Collections.emptyList(); |
| 143 | } | 170 | } |
| 144 | 171 | ||
| 145 | /** | 172 | /** |
| 146 | - * 查询一批原始群消息(包含当前在群、退群前补齐两部分) | 173 | + * 拉取已离开群路径的消息(退群前消息) |
| 147 | */ | 174 | */ |
| 148 | - private List<ImGroupMessageDO> getGroupMessageList(Long userId, Long minId, Integer size, | ||
| 149 | - LocalDateTime minSendTime, | ||
| 150 | - Map<Long, ImGroupMemberDO> memberMap) { | ||
| 151 | - // 1. 主查询:仅用"当前仍在群"的成员记录驱动(已退群的群由步骤 2 补齐) | ||
| 152 | - List<ImGroupMemberDO> activeMembers = groupMemberService.getActiveGroupMemberListByUserId(userId); | ||
| 153 | - memberMap.putAll(convertMap(activeMembers, ImGroupMemberDO::getGroupId)); | ||
| 154 | - List<ImGroupMessageDO> messages = new ArrayList<>(); | ||
| 155 | - if (CollUtil.isNotEmpty(activeMembers)) { | ||
| 156 | - List<Long> groupIds = convertList(activeMembers, ImGroupMemberDO::getGroupId); | ||
| 157 | - messages.addAll(groupMessageMapper.selectListByMinId(groupIds, minId, minSendTime, size)); | ||
| 158 | - } | ||
| 159 | - | ||
| 160 | - // 2. 补齐"退群前"的消息: | ||
| 161 | - // - 退群时间早于窗口起点的群直接忽略(消息已不在拉取范围); | ||
| 162 | - // - 若 minId > 0 且能查到对应消息,则进一步把下限抬到该消息的 sendTime, | ||
| 163 | - // 避免把客户端已拥有的老消息再次推送。 | 175 | + private List<ImGroupMessageDO> pullQuitGroupMessageList(Long userId, Long minId, Integer size, |
| 176 | + LocalDateTime minSendTime, | ||
| 177 | + Map<Long, ImGroupMemberDO> memberMap) { | ||
| 178 | + // 1. 退群补齐始终基于用户原始 minId 计算时间边界,避免被主查询内部重试游标误伤 | ||
| 164 | LocalDateTime minQuitTime = minSendTime; | 179 | LocalDateTime minQuitTime = minSendTime; |
| 165 | if (minId != null && minId > 0) { | 180 | if (minId != null && minId > 0) { |
| 166 | ImGroupMessageDO minMessage = groupMessageMapper.selectById(minId); | 181 | ImGroupMessageDO minMessage = groupMessageMapper.selectById(minId); |
| @@ -169,8 +184,11 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { | @@ -169,8 +184,11 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { | ||
| 169 | minQuitTime = minMessage.getSendTime(); | 184 | minQuitTime = minMessage.getSendTime(); |
| 170 | } | 185 | } |
| 171 | } | 186 | } |
| 187 | + // 2. 查询用户离开的群记录;若原始 minId 对应消息仍在窗口内,则用它的发送时间抬升退群筛选下限 | ||
| 188 | + List<ImGroupMessageDO> messages = new ArrayList<>(); | ||
| 172 | List<ImGroupMemberDO> quitMembers = groupMemberService.getQuitGroupMemberListByUserId(userId, minQuitTime); | 189 | List<ImGroupMemberDO> quitMembers = groupMemberService.getQuitGroupMemberListByUserId(userId, minQuitTime); |
| 173 | for (ImGroupMemberDO quitMember : quitMembers) { | 190 | for (ImGroupMemberDO quitMember : quitMembers) { |
| 191 | + // 3. 按原始 minId + 退群时间补齐该群退群前消息,并把成员记录写回 memberMap 供统一可见性过滤使用 | ||
| 174 | List<ImGroupMessageDO> quitGroupMessages = groupMessageMapper.selectListByGroupIdAndMinIdAndQuitTimeBefore( | 192 | List<ImGroupMessageDO> quitGroupMessages = groupMessageMapper.selectListByGroupIdAndMinIdAndQuitTimeBefore( |
| 175 | quitMember.getGroupId(), minId, minSendTime, quitMember.getQuitTime(), size); | 193 | quitMember.getGroupId(), minId, minSendTime, quitMember.getQuitTime(), size); |
| 176 | if (CollUtil.isEmpty(quitGroupMessages)) { | 194 | if (CollUtil.isEmpty(quitGroupMessages)) { |
yudao-module-im/yudao-module-im-biz/src/test/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImplTest.java
| @@ -185,6 +185,45 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest { | @@ -185,6 +185,45 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest { | ||
| 185 | } | 185 | } |
| 186 | 186 | ||
| 187 | @Test | 187 | @Test |
| 188 | + public void testPullMessages_activeQueryRetriesWhenFirstBatchInvisible() { | ||
| 189 | + LocalDateTime now = LocalDateTime.now(); | ||
| 190 | + LocalDateTime joinTime = now.minusHours(1); | ||
| 191 | + ImGroupMemberDO member = ImGroupMemberDO.builder() | ||
| 192 | + .groupId(10L).userId(1L) | ||
| 193 | + .status(CommonStatusEnum.ENABLE.getStatus()) | ||
| 194 | + .joinTime(joinTime).build(); | ||
| 195 | + when(groupMemberService.getActiveGroupMemberListByUserId(1L)).thenReturn(List.of(member)); | ||
| 196 | + | ||
| 197 | + ImGroupMessageDO beforeJoin = ImGroupMessageDO.builder() | ||
| 198 | + .id(1L).groupId(10L).senderId(2L) | ||
| 199 | + .sendTime(now.minusHours(3)).build(); | ||
| 200 | + ImGroupMessageDO directedMsg = ImGroupMessageDO.builder() | ||
| 201 | + .id(2L).groupId(10L).senderId(2L) | ||
| 202 | + .receiverUserIds(List.of(2L, 3L)) | ||
| 203 | + .sendTime(now.minusHours(2)).build(); | ||
| 204 | + ImGroupMessageDO visibleMsg = ImGroupMessageDO.builder() | ||
| 205 | + .id(3L).groupId(10L).senderId(2L) | ||
| 206 | + .sendTime(now.minusMinutes(30)).build(); | ||
| 207 | + when(groupMessageMapper.selectListByMinId(eq(List.of(10L)), eq(0L), | ||
| 208 | + any(LocalDateTime.class), eq(2))) | ||
| 209 | + .thenReturn(List.of(beforeJoin, directedMsg)); | ||
| 210 | + when(groupMessageMapper.selectListByMinId(eq(List.of(10L)), eq(2L), | ||
| 211 | + any(LocalDateTime.class), eq(2))) | ||
| 212 | + .thenReturn(List.of(visibleMsg)); | ||
| 213 | + when(groupMemberService.getQuitGroupMemberListByUserId(eq(1L), any(LocalDateTime.class))) | ||
| 214 | + .thenReturn(List.of()); | ||
| 215 | + | ||
| 216 | + List<ImGroupMessageDO> result = groupMessageService.pullGroupMessageList(1L, 0L, 2); | ||
| 217 | + | ||
| 218 | + assertEquals(1, result.size()); | ||
| 219 | + assertEquals(3L, result.get(0).getId()); | ||
| 220 | + verify(groupMessageMapper).selectListByMinId(eq(List.of(10L)), eq(0L), | ||
| 221 | + any(LocalDateTime.class), eq(2)); | ||
| 222 | + verify(groupMessageMapper).selectListByMinId(eq(List.of(10L)), eq(2L), | ||
| 223 | + any(LocalDateTime.class), eq(2)); | ||
| 224 | + } | ||
| 225 | + | ||
| 226 | + @Test | ||
| 188 | public void testPullMessages_quitMemberLoadsPreQuitMessages() { | 227 | public void testPullMessages_quitMemberLoadsPreQuitMessages() { |
| 189 | // 准备:当前群(在群)+ 一个最近退群 | 228 | // 准备:当前群(在群)+ 一个最近退群 |
| 190 | LocalDateTime now = LocalDateTime.now(); | 229 | LocalDateTime now = LocalDateTime.now(); |
| @@ -232,8 +271,6 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest { | @@ -232,8 +271,6 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest { | ||
| 232 | // 准备:minId > 0,边界消息 sendTime 晚于窗口起点,应当被用作 minQuitTime | 271 | // 准备:minId > 0,边界消息 sendTime 晚于窗口起点,应当被用作 minQuitTime |
| 233 | LocalDateTime now = LocalDateTime.now(); | 272 | LocalDateTime now = LocalDateTime.now(); |
| 234 | when(groupMemberService.getActiveGroupMemberListByUserId(1L)).thenReturn(List.of()); | 273 | when(groupMemberService.getActiveGroupMemberListByUserId(1L)).thenReturn(List.of()); |
| 235 | - when(groupMessageMapper.selectListByMinId(anyList(), anyLong(), any(LocalDateTime.class), anyInt())) | ||
| 236 | - .thenReturn(List.of()); | ||
| 237 | 274 | ||
| 238 | LocalDateTime boundarySendTime = now.minusDays(2); | 275 | LocalDateTime boundarySendTime = now.minusDays(2); |
| 239 | ImGroupMessageDO boundary = ImGroupMessageDO.builder() | 276 | ImGroupMessageDO boundary = ImGroupMessageDO.builder() |
| @@ -251,6 +288,70 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest { | @@ -251,6 +288,70 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest { | ||
| 251 | } | 288 | } |
| 252 | 289 | ||
| 253 | @Test | 290 | @Test |
| 291 | + public void testPullMessages_quitQueryKeepsOriginalMinIdWhenActiveQueryRetries() { | ||
| 292 | + LocalDateTime now = LocalDateTime.now(); | ||
| 293 | + LocalDateTime joinTime = now.minusHours(1); | ||
| 294 | + ImGroupMemberDO activeMember = ImGroupMemberDO.builder() | ||
| 295 | + .groupId(10L).userId(1L) | ||
| 296 | + .status(CommonStatusEnum.ENABLE.getStatus()) | ||
| 297 | + .joinTime(joinTime).build(); | ||
| 298 | + when(groupMemberService.getActiveGroupMemberListByUserId(1L)).thenReturn(List.of(activeMember)); | ||
| 299 | + | ||
| 300 | + ImGroupMessageDO beforeJoin = ImGroupMessageDO.builder() | ||
| 301 | + .id(100L).groupId(10L).senderId(2L) | ||
| 302 | + .sendTime(now.minusHours(3)).build(); | ||
| 303 | + ImGroupMessageDO directedMsg = ImGroupMessageDO.builder() | ||
| 304 | + .id(101L).groupId(10L).senderId(2L) | ||
| 305 | + .receiverUserIds(List.of(2L, 3L)) | ||
| 306 | + .sendTime(now.minusMinutes(50)).build(); | ||
| 307 | + ImGroupMessageDO visibleActiveMsg = ImGroupMessageDO.builder() | ||
| 308 | + .id(102L).groupId(10L).senderId(2L) | ||
| 309 | + .sendTime(now.minusMinutes(30)).build(); | ||
| 310 | + when(groupMessageMapper.selectListByMinId(eq(List.of(10L)), eq(8L), | ||
| 311 | + any(LocalDateTime.class), eq(2))) | ||
| 312 | + .thenReturn(List.of(beforeJoin, directedMsg)); | ||
| 313 | + when(groupMessageMapper.selectListByMinId(eq(List.of(10L)), eq(101L), | ||
| 314 | + any(LocalDateTime.class), eq(2))) | ||
| 315 | + .thenReturn(List.of(visibleActiveMsg)); | ||
| 316 | + | ||
| 317 | + LocalDateTime boundarySendTime = now.minusDays(2); | ||
| 318 | + ImGroupMessageDO boundary = ImGroupMessageDO.builder() | ||
| 319 | + .id(8L).groupId(99L).senderId(2L).sendTime(boundarySendTime).build(); | ||
| 320 | + when(groupMessageMapper.selectById(8L)).thenReturn(boundary); | ||
| 321 | + | ||
| 322 | + LocalDateTime quitTime = now.minusHours(12); | ||
| 323 | + ImGroupMemberDO quitMember = ImGroupMemberDO.builder() | ||
| 324 | + .groupId(20L).userId(1L) | ||
| 325 | + .status(CommonStatusEnum.DISABLE.getStatus()) | ||
| 326 | + .joinTime(now.minusDays(10)) | ||
| 327 | + .quitTime(quitTime).build(); | ||
| 328 | + when(groupMemberService.getQuitGroupMemberListByUserId(1L, boundarySendTime)) | ||
| 329 | + .thenReturn(List.of(quitMember)); | ||
| 330 | + | ||
| 331 | + ImGroupMessageDO quitGroupMsg = ImGroupMessageDO.builder() | ||
| 332 | + .id(50L).groupId(20L).senderId(99L) | ||
| 333 | + .sendTime(now.minusDays(1)).build(); | ||
| 334 | + when(groupMessageMapper.selectListByGroupIdAndMinIdAndQuitTimeBefore(eq(20L), eq(8L), | ||
| 335 | + any(LocalDateTime.class), eq(quitTime), eq(2))) | ||
| 336 | + .thenReturn(List.of(quitGroupMsg)); | ||
| 337 | + | ||
| 338 | + List<ImGroupMessageDO> result = groupMessageService.pullGroupMessageList(1L, 8L, 2); | ||
| 339 | + | ||
| 340 | + assertEquals(2, result.size()); | ||
| 341 | + assertEquals(50L, result.get(0).getId()); | ||
| 342 | + assertEquals(102L, result.get(1).getId()); | ||
| 343 | + verify(groupMessageMapper).selectListByMinId(eq(List.of(10L)), eq(8L), | ||
| 344 | + any(LocalDateTime.class), eq(2)); | ||
| 345 | + verify(groupMessageMapper).selectListByMinId(eq(List.of(10L)), eq(101L), | ||
| 346 | + any(LocalDateTime.class), eq(2)); | ||
| 347 | + verify(groupMemberService).getQuitGroupMemberListByUserId(1L, boundarySendTime); | ||
| 348 | + verify(groupMessageMapper).selectListByGroupIdAndMinIdAndQuitTimeBefore(eq(20L), eq(8L), | ||
| 349 | + any(LocalDateTime.class), eq(quitTime), eq(2)); | ||
| 350 | + verify(groupMessageMapper, never()).selectListByGroupIdAndMinIdAndQuitTimeBefore(eq(20L), eq(101L), | ||
| 351 | + any(LocalDateTime.class), eq(quitTime), eq(2)); | ||
| 352 | + } | ||
| 353 | + | ||
| 354 | + @Test | ||
| 254 | public void testPullMessages_receiverUserIdsFilter() { | 355 | public void testPullMessages_receiverUserIdsFilter() { |
| 255 | // 准备 | 356 | // 准备 |
| 256 | LocalDateTime now = LocalDateTime.now(); | 357 | LocalDateTime now = LocalDateTime.now(); |
| @@ -452,9 +553,7 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest { | @@ -452,9 +553,7 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest { | ||
| 452 | List<ImGroupMemberDO> members = List.of( | 553 | List<ImGroupMemberDO> members = List.of( |
| 453 | selfMember, | 554 | selfMember, |
| 454 | ImGroupMemberDO.builder().groupId(10L).userId(2L) | 555 | ImGroupMemberDO.builder().groupId(10L).userId(2L) |
| 455 | - .status(CommonStatusEnum.ENABLE.getStatus()).build(), | ||
| 456 | - ImGroupMemberDO.builder().groupId(10L).userId(3L) | ||
| 457 | - .status(CommonStatusEnum.DISABLE.getStatus()).build() | 556 | + .status(CommonStatusEnum.ENABLE.getStatus()).build() |
| 458 | ); | 557 | ); |
| 459 | when(groupMemberService.getActiveGroupMemberListByGroupId(10L)).thenReturn(members); | 558 | when(groupMemberService.getActiveGroupMemberListByGroupId(10L)).thenReturn(members); |
| 460 | 559 |