Commit ee01a16f8b358e4b7149822fd1bcb1fdbfc4d433

Authored by YunaiV
1 parent eb2d3c39

🐛 fix(im):好友模块 codex 评审修复——1209 改 @TransactionalEventListener(AFTER_COMMIT) 避免幽灵…

…通知、Consumer 过滤下推 SQL、申请表索引带 id 后缀覆盖游标排序、limit 加 @Max(200) 上限
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/friend/ImFriendRequestController.java
... ... @@ -16,6 +16,7 @@ import io.swagger.v3.oas.annotations.Parameter;
16 16 import io.swagger.v3.oas.annotations.tags.Tag;
17 17 import jakarta.annotation.Resource;
18 18 import jakarta.validation.Valid;
  19 +import jakarta.validation.constraints.Max;
19 20 import jakarta.validation.constraints.Min;
20 21 import jakarta.validation.constraints.NotNull;
21 22 import jakarta.validation.constraints.Size;
... ... @@ -81,7 +82,8 @@ public class ImFriendRequestController {
81 82 public CommonResult<List<ImFriendRequestRespVO>> getMyFriendRequestList(
82 83 @Parameter(description = "当前列表最旧记录的 id;首页不传")
83 84 @RequestParam(value = "lastRequestId", required = false) Long lastRequestId,
84   - @Parameter(description = "单次拉取条数", required = true) @RequestParam("limit") @Min(1) Integer limit) {
  85 + @Parameter(description = "单次拉取条数", required = true)
  86 + @RequestParam("limit") @Min(1) @Max(200) Integer limit) {
85 87 List<ImFriendRequestDO> list = friendRequestService.getMyFriendRequestList(getLoginUserId(), lastRequestId, limit);
86 88 return success(buildList(list));
87 89 }
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/mq/consumer/friend/AdminUserProfileUpdateConsumer.java
1 1 package cn.iocoder.yudao.module.im.mq.consumer.friend;
2 2  
3   -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 3 import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO;
5 4 import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum;
6 5 import cn.iocoder.yudao.module.im.service.friend.ImFriendService;
... ... @@ -10,9 +9,10 @@ import cn.iocoder.yudao.module.im.service.websocket.dto.notification.friend.Frie
10 9 import cn.iocoder.yudao.module.system.api.message.user.AdminUserProfileUpdateMessage;
11 10 import jakarta.annotation.Resource;
12 11 import lombok.extern.slf4j.Slf4j;
13   -import org.springframework.context.event.EventListener;
14 12 import org.springframework.scheduling.annotation.Async;
15 13 import org.springframework.stereotype.Component;
  14 +import org.springframework.transaction.event.TransactionPhase;
  15 +import org.springframework.transaction.event.TransactionalEventListener;
16 16  
17 17 import java.util.List;
18 18  
... ... @@ -30,30 +30,25 @@ public class AdminUserProfileUpdateConsumer {
30 30 @Resource
31 31 private ImWebSocketService websocketService;
32 32  
33   - @EventListener
34   - @Async // Spring Event 默认在 Producer 发送的线程,通过 @Async 实现异步
  33 + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true)
  34 + @Async // Spring Event 默认在 Producer 发送的线程,通过 @Async 实现异步;事务提交后触发,避免回滚误推幽灵通知 / Consumer 抢在 commit 前读旧值
35 35 public void onMessage(AdminUserProfileUpdateMessage message) {
36 36 log.info("[onMessage][消息内容({})]", message);
37 37 Long userId = message.getUserId();
38   - // 1. 找当前用户的所有有效好友(DISABLE 已删的不推)
39   - List<ImFriendDO> friends = friendService.getFriendList(userId);
  38 + // 1. SQL 层过滤 ENABLE 好友
  39 + List<ImFriendDO> friends = friendService.getEnableFriendList(userId);
40 40 if (friends.isEmpty()) {
41 41 return;
42 42 }
43 43  
44 44 // 2. 给每个好友的多端推 FRIEND_INFO_UPDATED;payload 里 operatorUserId / friendUserId 都是「资料被改的人」
45   - int notifyCount = 0;
46 45 for (ImFriendDO friend : friends) {
47   - if (CommonStatusEnum.isDisable(friend.getStatus())) {
48   - continue;
49   - }
50 46 FriendInfoUpdatedNotification payload = (FriendInfoUpdatedNotification) new FriendInfoUpdatedNotification()
51 47 .setOperatorUserId(userId).setFriendUserId(userId);
52 48 websocketService.sendPrivateMessageAsync(friend.getFriendUserId(), ImPrivateMessageDTO.ofFriendNotification(
53 49 ImMessageTypeEnum.FRIEND_INFO_UPDATED.getType(), userId, friend.getFriendUserId(), payload));
54   - notifyCount++;
55 50 }
56   - log.info("[onMessage][userId({}) 推送 FRIEND_INFO_UPDATED 给 {} 位好友]", userId, notifyCount);
  51 + log.info("[onMessage][userId({}) 推送 FRIEND_INFO_UPDATED 给 {} 位好友]", userId, friends.size());
57 52 }
58 53  
59 54 }
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendService.java
... ... @@ -33,6 +33,11 @@ public interface ImFriendService {
33 33 List<ImFriendDO> getFriendList(Long userId);
34 34  
35 35 /**
  36 + * 获得当前用户的有效好友列表(仅 ENABLE 状态)
  37 + */
  38 + List<ImFriendDO> getEnableFriendList(Long userId);
  39 +
  40 + /**
36 41 * 获得当前用户与指定用户之间的有效好友列表(仅 ENABLE 状态)
37 42 */
38 43 List<ImFriendDO> getActiveFriendList(Long userId, Collection<Long> friendUserIds);
... ...
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImpl.java
... ... @@ -76,6 +76,11 @@ public class ImFriendServiceImpl implements ImFriendService {
76 76 }
77 77  
78 78 @Override
  79 + public List<ImFriendDO> getEnableFriendList(Long userId) {
  80 + return friendMapper.selectListByUserIdAndStatus(userId, CommonStatusEnum.ENABLE.getStatus());
  81 + }
  82 +
  83 + @Override
79 84 public List<ImFriendDO> getActiveFriendList(Long userId, Collection<Long> friendUserIds) {
80 85 return friendMapper.selectListByUserIdAndFriendUserIdsAndStatus(userId, friendUserIds,
81 86 CommonStatusEnum.ENABLE.getStatus());
... ...