From 189672157529669cc2e4c81bba24568180727803 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 23 Apr 2026 01:17:48 +0800 Subject: [PATCH] ✨ feat(im): 群/好友更新消息改为事件通知,客户端自行拉取 - ImPrivateMessageDTO#ofFriendUpdate 移除 content 字段,删除 FriendUpdateMessage - 新增 ImMessageTypeEnum.GROUP_MEMBER_UPDATE(203) 和 ofGroupMemberUpdate 工厂方法 - ImGroupMemberServiceImpl#updateGroupMember 推送成员变更事件(多端同步,仅推自己) --- yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java | 5 +++-- yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/user/ImUserController.java | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/user/vo/ImUserRespVO.java | 41 +++++++++++++++++++++++++++++++++++++++++ yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImpl.java | 5 +---- yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImpl.java | 10 ++++++++++ yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImGroupMessageDTO.java | 14 ++++++++++++++ yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImPrivateMessageDTO.java | 7 ++----- yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/friend/FriendUpdateMessage.java | 22 ---------------------- 8 files changed, 138 insertions(+), 33 deletions(-) create mode 100644 yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/user/ImUserController.java create mode 100644 yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/user/vo/ImUserRespVO.java delete mode 100644 yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/friend/FriendUpdateMessage.java diff --git a/yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java b/yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java index 74d84e7..8fad3b1 100644 --- a/yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java +++ b/yudao-module-im/yudao-module-im-api/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageTypeEnum.java @@ -30,11 +30,12 @@ public enum ImMessageTypeEnum implements ArrayValuable { FRIEND_ADD(100, "好友添加"), // 暂无 FRIEND_DELETE(101, "好友删除"), // 暂无 - FRIEND_UPDATE(102, "好友更新"), // 对应 FriendUpdateMessage 类 + FRIEND_UPDATE(102, "好友更新"), // 暂无(客户端收到后自行拉取) GROUP_CREATE(200, "群创建"), // 暂无 GROUP_UPDATE(201, "群信息变更"), // 暂无 - GROUP_DELETE(202, "群删除"); // 暂无(解散/退群/踢出均用此类型) + GROUP_DELETE(202, "群删除"), // 暂无(解散/退群/踢出均用此类型) + GROUP_MEMBER_UPDATE(203, "群成员信息变更"); // 暂无(客户端收到后自行拉取) public static final Integer[] ARRAYS = Arrays.stream(values()).map(ImMessageTypeEnum::getType).toArray(Integer[]::new); diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/user/ImUserController.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/user/ImUserController.java new file mode 100644 index 0000000..3855cd9 --- /dev/null +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/user/ImUserController.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.im.controller.admin.user; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; +import cn.iocoder.yudao.module.im.controller.admin.user.vo.ImUserRespVO; +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - IM 用户查询") +@RestController +@RequestMapping("/im/user") +@Validated +public class ImUserController { + + @Resource + private AdminUserApi adminUserApi; + + @GetMapping("/get-self") + @Operation(summary = "获取当前登录用户信息") + public CommonResult getSelfInfo() { + Long userId = SecurityFrameworkUtils.getLoginUserId(); + AdminUserRespDTO user = adminUserApi.getUser(userId); + return success(BeanUtils.toBean(user, ImUserRespVO.class)); + } + + @GetMapping("/get") + @Operation(summary = "按用户编号查询用户(用于点头像弹名片)") + @Parameter(name = "id", description = "用户编号", required = true, example = "2048") + public CommonResult getUser(@RequestParam("id") Long id) { + AdminUserRespDTO user = adminUserApi.getUser(id); + if (user == null) { + return success(null); + } + return success(BeanUtils.toBean(user, ImUserRespVO.class)); + } + + @GetMapping("/list-by-name") + @Operation(summary = "按昵称模糊搜索用户(用于加好友场景)") + @Parameter(name = "name", description = "昵称关键词", required = true, example = "芋道") + public CommonResult> getUserListByName(@RequestParam("name") String name) { + if (name == null || name.trim().isEmpty()) { + return success(Collections.emptyList()); + } + List users = adminUserApi.getUserListByNickname(name.trim()); + // TODO @芋艿:【对齐】这里需要支持按「用户名 or 昵称」搜索; + // AdminUser 按用户名模糊的接口暂未暴露,先仅按昵称。后续可扩 AdminUserApi 加一个 + // `getUserListByUsernameOrNickname(keyword)` 或让前端输入时选搜索模式。 + return success(CollectionUtils.convertList(users, u -> BeanUtils.toBean(u, ImUserRespVO.class))); + } + +} diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/user/vo/ImUserRespVO.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/user/vo/ImUserRespVO.java new file mode 100644 index 0000000..5d6f9e8 --- /dev/null +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/controller/admin/user/vo/ImUserRespVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.im.controller.admin.user.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * IM 用户 Response VO(对 AdminUser 的字段窄投影) + *

+ * 面向 IM 场景,主要关注 id / userName / nickName / headImage / sex / signature 这些字段; + * 但 yudao 侧 AdminUser 没有 signature 字段,暂不返回(TODO @芋艿:【对齐】后续可加用户扩展表承载)。 + * + * @author 芋道源码 + */ +@Schema(description = "管理后台 - IM 用户 Response VO") +@Data +public class ImUserRespVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "用户名", example = "admin") + private String username; + + @Schema(description = "昵称", example = "芋道") + private String nickname; + + @Schema(description = "头像") + private String avatar; + + @Schema(description = "性别 0=未知 1=男 2=女", example = "1") + private Integer sex; + + @Schema(description = "手机号") + private String mobile; + + /** + * TODO @芋艿:【对齐】个性签名(signature)字段当前缺失,yudao AdminUser 无此字段; + * 如果要对齐,可走 system 模块的用户扩展表,或 IM 模块独立存。 + */ + +} 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 fd576be..40d80b2 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 @@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO; import cn.iocoder.yudao.module.im.dal.mysql.friend.ImFriendMapper; import cn.iocoder.yudao.module.im.service.message.ImPrivateMessageService; import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService; -import cn.iocoder.yudao.module.im.service.websocket.dto.friend.FriendUpdateMessage; import cn.iocoder.yudao.module.im.service.websocket.dto.ImPrivateMessageDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import jakarta.annotation.Resource; @@ -89,10 +88,8 @@ public class ImFriendServiceImpl implements ImFriendService { imFriendMapper.updateById(new ImFriendDO().setId(friend.getId()).setMuted(reqVO.getMuted())); // 3. 推送好友更新通知(多端同步) - FriendUpdateMessage websocketContent = new FriendUpdateMessage() - .setFriendUserId(reqVO.getFriendUserId()).setMuted(reqVO.getMuted()); imWebSocketService.sendPrivateMessageAsync(userId, - ImPrivateMessageDTO.ofFriendUpdate(userId, reqVO.getFriendUserId(), websocketContent)); + ImPrivateMessageDTO.ofFriendUpdate(userId, reqVO.getFriendUserId())); } @Override diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImpl.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImpl.java index efda2e0..c86db51 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImpl.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImpl.java @@ -6,6 +6,8 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberUpdateReqVO; import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; import cn.iocoder.yudao.module.im.dal.mysql.group.ImGroupMemberMapper; +import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService; +import cn.iocoder.yudao.module.im.service.websocket.dto.ImGroupMessageDTO; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -32,6 +34,9 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService { @Resource private ImGroupMemberMapper groupMemberMapper; + @Resource + private ImWebSocketService webSocketService; + @Override public ImGroupMemberDO getGroupMember(Long id) { return groupMemberMapper.selectById(id); @@ -108,10 +113,15 @@ public class ImGroupMemberServiceImpl implements ImGroupMemberService { public void updateGroupMember(Long userId, ImGroupMemberUpdateReqVO updateReqVO) { // 1. 校验是群的有效成员 ImGroupMemberDO member = validateMemberInGroup(updateReqVO.getGroupId(), userId); + // 2. 更新群成员信息 ImGroupMemberDO updateObj = BeanUtils.toBean(updateReqVO, ImGroupMemberDO.class) .setId(member.getId()); groupMemberMapper.updateById(updateObj); + + // 3. 推送群成员变更通知(多端同步,仅推给自己) + webSocketService.sendGroupMessageAsync(userId, + ImGroupMessageDTO.ofGroupMemberUpdate(userId, updateReqVO.getGroupId())); } @Override diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImGroupMessageDTO.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImGroupMessageDTO.java index 6304bf8..9d06922 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImGroupMessageDTO.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImGroupMessageDTO.java @@ -162,4 +162,18 @@ public class ImGroupMessageDTO { .setSendTime(LocalDateTime.now()); } + /** + * 构建群成员信息变更推送 DTO(多端同步:通知自己"在某群的个人设置变了") + * + * @param senderId 当前用户编号 + * @param groupId 群编号 + * @return 群聊 DTO + */ + public static ImGroupMessageDTO ofGroupMemberUpdate(Long senderId, Long groupId) { + return new ImGroupMessageDTO() + .setType(ImMessageTypeEnum.GROUP_MEMBER_UPDATE.getType()) + .setSenderId(senderId).setGroupId(groupId) + .setSendTime(LocalDateTime.now()); + } + } diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImPrivateMessageDTO.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImPrivateMessageDTO.java index 44f165c..a609561 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImPrivateMessageDTO.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImPrivateMessageDTO.java @@ -1,10 +1,8 @@ package cn.iocoder.yudao.module.im.service.websocket.dto; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO; import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; -import cn.iocoder.yudao.module.im.service.websocket.dto.friend.FriendUpdateMessage; import lombok.Data; import lombok.experimental.Accessors; @@ -137,14 +135,13 @@ public class ImPrivateMessageDTO { * * @param userId 当前用户编号 * @param friendUserId 好友的用户编号 - * @param content 更新后的好友属性 * @return 私聊 DTO */ - public static ImPrivateMessageDTO ofFriendUpdate(Long userId, Long friendUserId, FriendUpdateMessage content) { + public static ImPrivateMessageDTO ofFriendUpdate(Long userId, Long friendUserId) { return new ImPrivateMessageDTO() .setType(ImMessageTypeEnum.FRIEND_UPDATE.getType()) .setSenderId(userId).setReceiverId(friendUserId) - .setContent(JsonUtils.toJsonString(content)).setSendTime(LocalDateTime.now()); + .setSendTime(LocalDateTime.now()); } } diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/friend/FriendUpdateMessage.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/friend/FriendUpdateMessage.java deleted file mode 100644 index c818cbf..0000000 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/friend/FriendUpdateMessage.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.im.service.websocket.dto.friend; - -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * 好友更新消息内容(目前仅免打扰) - */ -@Data -@Accessors(chain = true) -public class FriendUpdateMessage { - - /** - * 好友的用户编号 - */ - private Long friendUserId; - /** - * 是否免打扰 - */ - private Boolean muted; - -} -- libgit2 0.21.4