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 be9efa3..db5ff60 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 @@ -45,4 +45,11 @@ public interface ImCommonConstants { */ String FRIEND_DELETE_TIP_MESSAGE = "你们的好友关系已被解除"; + // ==================== 群相关提示消息 ==================== + + /** + * 群解散系统提示 + */ + String GROUP_DISSOLVE_TIP_MESSAGE = "'{}'解散了群聊"; + } 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 c778a31..95bbcba 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 @@ -33,7 +33,8 @@ public enum ImMessageTypeEnum implements ArrayValuable { FRIEND_UPDATE(102, "好友更新"), // 对应 FriendUpdateMessage 类 GROUP_CREATE(200, "群创建"), // 暂无 - GROUP_UPDATE(201, "群信息变更"); // 暂无 + GROUP_UPDATE(201, "群信息变更"), // 暂无 + GROUP_DISSOLVE(202, "群解散"); // 暂无 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/service/group/ImGroupService.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupService.java index 99aa13a..7c36059 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupService.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupService.java @@ -61,6 +61,16 @@ public interface ImGroupService { ImGroupDO validateGroupExists(Long groupId); /** + * 校验当前用户是否为群主 + * + * @param groupId 群编号 + * @param userId 用户编号 + * @return 群信息 + */ + @SuppressWarnings("UnusedReturnValue") + ImGroupDO validateGroupOwner(Long groupId, Long userId); + + /** * 获取指定用户加入的所有"有效"群(未退群且未封禁/解散) *

* 返回当前登录用户在群内身份仍有效的所有群。 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 e64a68a..6f0a446 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 @@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.copier.CopyOptions; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -13,6 +14,8 @@ 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.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 jakarta.annotation.Resource; @@ -32,6 +35,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.module.im.dal.redis.RedisKeyConstants.GROUP; import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.im.enums.ImCommonConstants.GROUP_DISSOLVE_TIP_MESSAGE; // TODO @芋艿:群主的调整,暂时不考虑。后续在弄; /** @@ -55,6 +59,9 @@ public class ImGroupServiceImpl implements ImGroupService { @Resource private ImWebSocketService webSocketService; + @Resource + private AdminUserApi adminUserApi; + @Override @Transactional(rollbackFor = Exception.class) public ImGroupDO createGroup(ImGroupCreateReqVO createReqVO, Long userId) { @@ -112,29 +119,29 @@ public class ImGroupServiceImpl implements ImGroupService { @CacheEvict(cacheNames = GROUP, key = "#id") @Transactional(rollbackFor = Exception.class) public void dissolveGroup(Long id, Long userId) { - // 1.1 校验群存在 - ImGroupDO group = validateGroupExists(id); - // 1.2 校验当前用户是群主 - if (ObjUtil.notEqual(group.getOwnerUserId(), userId)) { - throw exception(GROUP_NOT_OWNER); - } - // TODO @AI:增加一个是否为群主的 public 方法;因为别的模块也需要使用; - - // 3. 更新群状态为已解散 - // TODO @AI:链式调用; - ImGroupDO updateObj = new ImGroupDO(); - updateObj.setId(id); - updateObj.setStatus(CommonStatusEnum.DISABLE.getStatus()); - updateObj.setDissolvedTime(LocalDateTime.now()); - groupMapper.updateById(updateObj); - - // 4. 清理已读缓存 + // 1. 校验群存在 + 当前用户是群主 + validateGroupOwner(id, userId); + + // 2.1 获取群成员列表(在移除之前,后续推送需要) + List members = groupMemberService.getActiveGroupMemberListByGroupId(id); + Set memberUserIds = convertSet(members, ImGroupMemberDO::getUserId); + // 2.2 更新群状态为已解散 + groupMapper.updateById(new ImGroupDO().setId(id) + .setStatus(CommonStatusEnum.DISABLE.getStatus()).setDissolvedTime(LocalDateTime.now())); + // 2.3 移除全部群成员 + groupMemberService.removeGroupMembersByGroupId(id); + // 2.4 清理已读缓存 groupMessageService.deleteReadMaxMessageIdMap(id); - // TODO @AI:推送群解散的提示; - // TODO @AI:推送群信息变化; + + // 3.1 发送解散提示消息(TIP_TEXT)并推送 + AdminUserRespDTO user = adminUserApi.getUser(userId); + String tipContent = StrUtil.format(GROUP_DISSOLVE_TIP_MESSAGE, user.getNickname()); + groupMessageService.sendTipGroupMessage(userId, id, memberUserIds, tipContent); + // 3.2 推送群解散事件给所有原群成员 + webSocketService.sendGroupMessageAsync(memberUserIds, + ImGroupMessageDTO.ofGroupDissolve(userId, id)); } - // TODO @AI:缓存还是加在这个方法上;不加在 getGroup 上; @Override public ImGroupDO validateGroupExists(Long id) { ImGroupDO group = getSelf().getGroup(id); @@ -151,6 +158,15 @@ public class ImGroupServiceImpl implements ImGroupService { } @Override + public ImGroupDO validateGroupOwner(Long groupId, Long userId) { + ImGroupDO group = validateGroupExists(groupId); + if (ObjUtil.notEqual(group.getOwnerUserId(), userId)) { + throw exception(GROUP_NOT_OWNER); + } + return group; + } + + @Override @Cacheable(cacheNames = GROUP, key = "#id", unless = "#result == null") public ImGroupDO getGroup(Long id) { return groupMapper.selectById(id); diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageService.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageService.java index 0b6e53f..7a6c946 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageService.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageService.java @@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessa import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO; import java.util.List; +import java.util.Set; /** * IM 群聊消息 Service 接口 @@ -88,5 +89,17 @@ public interface ImGroupMessageService { */ void deleteReadMaxMessageIdMap(Long groupId); + /** + * 发送群聊系统提示消息(TIP_TEXT) + *

+ * 插入一条 TIP_TEXT 消息到群聊记录,并推送给指定用户 + * + * @param senderId 发送人编号(操作者) + * @param groupId 群编号 + * @param memberUserIds 推送目标用户编号集合 + * @param content 提示内容 + */ + void sendTipGroupMessage(Long senderId, Long groupId, Set memberUserIds, String content); + } diff --git a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java index 43b3998..b254efd 100644 --- a/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java +++ b/yudao-module-im/yudao-module-im-biz/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java @@ -481,6 +481,22 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { groupMessageReadRedisDAO.deleteReadMaxMessageIdMap(groupId); } + @Override + public void sendTipGroupMessage(Long senderId, Long groupId, Set memberUserIds, String content) { + // 1. 插入 TIP_TEXT 消息 + ImGroupMessageDO tipMessage = new ImGroupMessageDO() + .setClientMessageId(IdUtil.fastSimpleUUID()) + .setSenderId(senderId).setGroupId(groupId) + .setType(ImMessageTypeEnum.TIP_TEXT.getType()).setContent(content) + .setStatus(ImMessageStatusEnum.UNREAD.getStatus()).setSendTime(LocalDateTime.now()) + .setReceiptStatus(ImGroupMessageReceiptStatusEnum.NO_RECEIPT.getStatus()); + groupMessageMapper.insert(tipMessage); + + // 2. 推送给指定用户 + imWebSocketService.sendGroupMessageAsync(memberUserIds, + ImGroupMessageDTO.ofSend(tipMessage)); + } + private ImGroupMessageServiceImpl getSelf() { return SpringUtil.getBean(getClass()); } 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 91ba494..8aa52c3 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 @@ -146,4 +146,18 @@ public class ImGroupMessageDTO { .setSendTime(LocalDateTime.now()); } + /** + * 构建群解散推送 DTO(通知所有群成员"群已解散") + * + * @param senderId 操作者(群主)用户编号 + * @param groupId 群编号 + * @return 群聊 DTO + */ + public static ImGroupMessageDTO ofGroupDissolve(Long senderId, Long groupId) { + return new ImGroupMessageDTO() + .setType(ImMessageTypeEnum.GROUP_DISSOLVE.getType()) + .setSenderId(senderId).setGroupId(groupId) + .setSendTime(LocalDateTime.now()); + } + }