Commit f938362b04fde4296f541a58fe440ef79c4d5b2d
1 parent
b18281fc
✨ feat(im): 增加频道消息的已读状态
Showing
11 changed files
with
185 additions
and
9 deletions
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/ImChannelMessageManagerController.java
| ... | ... | @@ -72,7 +72,8 @@ public class ImChannelMessageManagerController { |
| 72 | 72 | convertSet(pageResult.getList(), ImChannelMessageDO::getMaterialId)); |
| 73 | 73 | return success(BeanUtils.toBean(pageResult, ImChannelMessageRespVO.class, vo -> { |
| 74 | 74 | MapUtils.findAndThen(channelMap, vo.getChannelId(), c -> vo.setChannelName(c.getName())); |
| 75 | - MapUtils.findAndThen(materialMap, vo.getMaterialId(), m -> vo.setMaterialTitle(m.getTitle())); | |
| 75 | + MapUtils.findAndThen(materialMap, vo.getMaterialId(), | |
| 76 | + material -> vo.setMaterialTitle(material.getTitle()).setMaterialCoverUrl(material.getCoverUrl())); | |
| 76 | 77 | })); |
| 77 | 78 | } |
| 78 | 79 | ... | ... |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImChannelMessageController.java
| 1 | 1 | package cn.iocoder.yudao.module.im.controller.admin.message; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollUtil; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 5 | 6 | import cn.iocoder.yudao.module.im.controller.admin.message.vo.channel.ImChannelMessagePullRespVO; |
| 6 | 7 | import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO; |
| 8 | +import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum; | |
| 7 | 9 | import cn.iocoder.yudao.module.im.service.message.ImChannelMessageService; |
| 8 | 10 | import io.swagger.v3.oas.annotations.Operation; |
| 11 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 9 | 12 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 10 | 13 | import jakarta.annotation.Resource; |
| 11 | 14 | import jakarta.validation.constraints.Max; |
| ... | ... | @@ -13,13 +16,17 @@ import jakarta.validation.constraints.Min; |
| 13 | 16 | import jakarta.validation.constraints.PositiveOrZero; |
| 14 | 17 | import org.springframework.validation.annotation.Validated; |
| 15 | 18 | import org.springframework.web.bind.annotation.GetMapping; |
| 19 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 16 | 20 | import org.springframework.web.bind.annotation.RequestMapping; |
| 17 | 21 | import org.springframework.web.bind.annotation.RequestParam; |
| 18 | 22 | import org.springframework.web.bind.annotation.RestController; |
| 19 | 23 | |
| 24 | +import java.util.Collections; | |
| 20 | 25 | import java.util.List; |
| 26 | +import java.util.Map; | |
| 21 | 27 | |
| 22 | 28 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 29 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 23 | 30 | import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId; |
| 24 | 31 | |
| 25 | 32 | @Tag(name = "用户 APP - IM 频道消息") |
| ... | ... | @@ -38,8 +45,31 @@ public class ImChannelMessageController { |
| 38 | 45 | @RequestParam(value = "size", defaultValue = "100") |
| 39 | 46 | @Min(value = 1, message = "size 必须大于 0") |
| 40 | 47 | @Max(value = 200, message = "size 一次最多 200 条") Integer size) { |
| 41 | - List<ImChannelMessageDO> list = channelMessageService.getMessageListForPull(getLoginUserId(), minId, size); | |
| 42 | - return success(BeanUtils.toBean(list, ImChannelMessagePullRespVO.class)); | |
| 48 | + // 1. 拉取消息列表 | |
| 49 | + Long userId = getLoginUserId(); | |
| 50 | + List<ImChannelMessageDO> list = channelMessageService.getMessageListForPull(userId, minId, size); | |
| 51 | + if (CollUtil.isEmpty(list)) { | |
| 52 | + return success(Collections.emptyList()); | |
| 53 | + } | |
| 54 | + // 2. 按 Redis 已读游标补 status;device A 已读后 device B 拉到这条不再算入未读 | |
| 55 | + Map<Long, Long> readMaxByChannel = channelMessageService.getChannelReadMaxMessageIdMap( | |
| 56 | + userId, convertSet(list, ImChannelMessageDO::getChannelId)); | |
| 57 | + return success(BeanUtils.toBean(list, ImChannelMessagePullRespVO.class, vo -> { | |
| 58 | + Long readMax = readMaxByChannel.get(vo.getChannelId()); | |
| 59 | + vo.setStatus(readMax != null && readMax >= vo.getId() | |
| 60 | + ? ImMessageStatusEnum.READ.getStatus() | |
| 61 | + : ImMessageStatusEnum.UNREAD.getStatus()); | |
| 62 | + })); | |
| 63 | + } | |
| 64 | + | |
| 65 | + @PutMapping("/read") | |
| 66 | + @Operation(summary = "标记频道消息已读") | |
| 67 | + @Parameter(name = "channelId", description = "频道编号", required = true, example = "1") | |
| 68 | + @Parameter(name = "messageId", description = "已读到的消息编号", required = true, example = "100") | |
| 69 | + public CommonResult<Boolean> readChannelMessages(@RequestParam("channelId") Long channelId, | |
| 70 | + @RequestParam("messageId") Long messageId) { | |
| 71 | + channelMessageService.readChannelMessages(getLoginUserId(), channelId, messageId); | |
| 72 | + return success(true); | |
| 43 | 73 | } |
| 44 | 74 | |
| 45 | 75 | } | ... | ... |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/channel/ImChannelMessagePullRespVO.java
| ... | ... | @@ -24,6 +24,9 @@ public class ImChannelMessagePullRespVO { |
| 24 | 24 | @Schema(description = "消息内容;payload JSON 快照", requiredMode = Schema.RequiredMode.REQUIRED) |
| 25 | 25 | private String content; |
| 26 | 26 | |
| 27 | + @Schema(description = "当前用户的已读态;按 Redis 游标计算填充", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") | |
| 28 | + private Integer status; // 参见 ImMessageStatusEnum 枚举类 | |
| 29 | + | |
| 27 | 30 | @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 28 | 31 | private LocalDateTime sendTime; |
| 29 | 32 | ... | ... |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/redis/RedisKeyConstants.java
| ... | ... | @@ -16,6 +16,13 @@ public interface RedisKeyConstants { |
| 16 | 16 | String GROUP_MESSAGE_READ = "im:group:message:read:%s"; |
| 17 | 17 | |
| 18 | 18 | /** |
| 19 | + * 频道消息已读位置 | |
| 20 | + * KEY 格式: im:channel:message:read:{channelId} | |
| 21 | + * VALUE 数据类型: Hash (field: userId, value: maxReadMessageId) | |
| 22 | + */ | |
| 23 | + String CHANNEL_MESSAGE_READ = "im:channel:message:read:%s"; | |
| 24 | + | |
| 25 | + /** | |
| 19 | 26 | * 好友关系状态缓存(合并「是否好友」+「是否拉黑」两态) |
| 20 | 27 | * <p> |
| 21 | 28 | * KEY 格式:friend_state:{userId}_{friendUserId} | ... | ... |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/redis/message/ImChannelMessageReadRedisDAO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.im.dal.redis.message; | |
| 2 | + | |
| 3 | +import cn.hutool.core.convert.Convert; | |
| 4 | +import jakarta.annotation.Resource; | |
| 5 | +import org.springframework.data.redis.core.StringRedisTemplate; | |
| 6 | +import org.springframework.stereotype.Repository; | |
| 7 | + | |
| 8 | +import static cn.iocoder.yudao.module.im.dal.redis.RedisKeyConstants.CHANNEL_MESSAGE_READ; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * IM 频道消息已读位置 Redis DAO | |
| 12 | + * | |
| 13 | + * @author 芋道源码 | |
| 14 | + */ | |
| 15 | +@Repository | |
| 16 | +public class ImChannelMessageReadRedisDAO { | |
| 17 | + | |
| 18 | + @Resource | |
| 19 | + private StringRedisTemplate stringRedisTemplate; | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * 更新用户在某频道的最大已读消息编号 | |
| 23 | + * | |
| 24 | + * @param channelId 频道编号 | |
| 25 | + * @param userId 用户编号 | |
| 26 | + * @param maxMessageId 最大已读消息编号 | |
| 27 | + */ | |
| 28 | + public void updateReadMaxMessageId(Long channelId, Long userId, Long maxMessageId) { | |
| 29 | + String key = formatKey(channelId); | |
| 30 | + stringRedisTemplate.opsForHash().put(key, userId.toString(), maxMessageId.toString()); | |
| 31 | + } | |
| 32 | + | |
| 33 | + /** | |
| 34 | + * 获取用户在某频道的最大已读消息编号 | |
| 35 | + * | |
| 36 | + * @param channelId 频道编号 | |
| 37 | + * @param userId 用户编号 | |
| 38 | + * @return 最大已读消息编号;不存在则返回 null | |
| 39 | + */ | |
| 40 | + public Long getReadMaxMessageId(Long channelId, Long userId) { | |
| 41 | + String key = formatKey(channelId); | |
| 42 | + Object val = stringRedisTemplate.opsForHash().get(key, userId.toString()); | |
| 43 | + return Convert.toLong(val); | |
| 44 | + } | |
| 45 | + | |
| 46 | + private static String formatKey(Long channelId) { | |
| 47 | + return String.format(CHANNEL_MESSAGE_READ, channelId); | |
| 48 | + } | |
| 49 | + | |
| 50 | +} | ... | ... |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/redis/message/GroupMessageReadRedisDAO.java renamed to yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/redis/message/ImGroupMessageReadRedisDAO.java
| ... | ... | @@ -12,12 +12,12 @@ import java.util.Map; |
| 12 | 12 | import static cn.iocoder.yudao.module.im.dal.redis.RedisKeyConstants.GROUP_MESSAGE_READ; |
| 13 | 13 | |
| 14 | 14 | /** |
| 15 | - * 群消息已读位置 Redis DAO | |
| 15 | + * IM 群消息已读位置 Redis DAO | |
| 16 | 16 | * |
| 17 | 17 | * @author 芋道源码 |
| 18 | 18 | */ |
| 19 | 19 | @Repository |
| 20 | -public class GroupMessageReadRedisDAO { | |
| 20 | +public class ImGroupMessageReadRedisDAO { | |
| 21 | 21 | |
| 22 | 22 | @Resource |
| 23 | 23 | private StringRedisTemplate stringRedisTemplate; | ... | ... |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageService.java
| ... | ... | @@ -6,7 +6,9 @@ import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel.Im |
| 6 | 6 | import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO; |
| 7 | 7 | import jakarta.validation.Valid; |
| 8 | 8 | |
| 9 | +import java.util.Collection; | |
| 9 | 10 | import java.util.List; |
| 11 | +import java.util.Map; | |
| 10 | 12 | |
| 11 | 13 | /** |
| 12 | 14 | * IM 频道消息 Service 接口 |
| ... | ... | @@ -27,6 +29,23 @@ public interface ImChannelMessageService { |
| 27 | 29 | */ |
| 28 | 30 | List<ImChannelMessageDO> getMessageListForPull(Long userId, Long minId, Integer size); |
| 29 | 31 | |
| 32 | + /** | |
| 33 | + * 上报频道消息已读位置;同步推 READ 事件给自己多端 | |
| 34 | + * | |
| 35 | + * @param userId 当前用户编号 | |
| 36 | + * @param channelId 频道编号 | |
| 37 | + * @param messageId 已读到的最大消息编号 | |
| 38 | + */ | |
| 39 | + void readChannelMessages(Long userId, Long channelId, Long messageId); | |
| 40 | + | |
| 41 | + /** | |
| 42 | + * 批量查询用户在多个频道下的已读游标 | |
| 43 | + * | |
| 44 | + * @param userId 当前用户编号 | |
| 45 | + * @param channelIds 频道编号集合 | |
| 46 | + * @return channelId → 已读到的最大消息编号(未读到的频道不出现在返回 map 中) | |
| 47 | + */ | |
| 48 | + Map<Long, Long> getChannelReadMaxMessageIdMap(Long userId, Collection<Long> channelIds); | |
| 30 | 49 | |
| 31 | 50 | // ==================== 管理后台 ==================== |
| 32 | 51 | ... | ... |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.im.service.message; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.lang.Assert; | |
| 5 | +import cn.hutool.extra.spring.SpringUtil; | |
| 4 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 7 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 6 | 8 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| ... | ... | @@ -9,18 +11,23 @@ import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel.Im |
| 9 | 11 | import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelMaterialDO; |
| 10 | 12 | import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO; |
| 11 | 13 | import cn.iocoder.yudao.module.im.dal.mysql.message.ImChannelMessageMapper; |
| 14 | +import cn.iocoder.yudao.module.im.dal.redis.message.ImChannelMessageReadRedisDAO; | |
| 12 | 15 | import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; |
| 13 | 16 | import cn.iocoder.yudao.module.im.service.channel.ImChannelMaterialService; |
| 14 | 17 | import cn.iocoder.yudao.module.im.service.websocket.ImWebSocketService; |
| 15 | 18 | import cn.iocoder.yudao.module.im.service.websocket.dto.ImChannelMessageDTO; |
| 16 | 19 | import cn.iocoder.yudao.module.im.service.websocket.dto.message.MaterialMessage; |
| 17 | 20 | import jakarta.annotation.Resource; |
| 21 | +import org.springframework.scheduling.annotation.Async; | |
| 18 | 22 | import org.springframework.stereotype.Service; |
| 19 | 23 | import org.springframework.transaction.annotation.Transactional; |
| 20 | 24 | import org.springframework.validation.annotation.Validated; |
| 21 | 25 | |
| 22 | 26 | import java.time.LocalDateTime; |
| 27 | +import java.util.Collection; | |
| 28 | +import java.util.HashMap; | |
| 23 | 29 | import java.util.List; |
| 30 | +import java.util.Map; | |
| 24 | 31 | |
| 25 | 32 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 26 | 33 | import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.IM_CHANNEL_MESSAGE_NOT_EXISTS; |
| ... | ... | @@ -36,11 +43,15 @@ public class ImChannelMessageServiceImpl implements ImChannelMessageService { |
| 36 | 43 | |
| 37 | 44 | @Resource |
| 38 | 45 | private ImChannelMessageMapper channelMessageMapper; |
| 46 | + | |
| 39 | 47 | @Resource |
| 40 | 48 | private ImChannelMaterialService channelMaterialService; |
| 41 | 49 | @Resource |
| 42 | 50 | private ImWebSocketService webSocketService; |
| 43 | 51 | |
| 52 | + @Resource | |
| 53 | + private ImChannelMessageReadRedisDAO channelMessageReadRedisDAO; | |
| 54 | + | |
| 44 | 55 | // ==================== 用户端 ==================== |
| 45 | 56 | |
| 46 | 57 | @Override |
| ... | ... | @@ -48,6 +59,47 @@ public class ImChannelMessageServiceImpl implements ImChannelMessageService { |
| 48 | 59 | return channelMessageMapper.selectListByUserAndMinId(userId, minId, size); |
| 49 | 60 | } |
| 50 | 61 | |
| 62 | + @Override | |
| 63 | + public Map<Long, Long> getChannelReadMaxMessageIdMap(Long userId, Collection<Long> channelIds) { | |
| 64 | + Map<Long, Long> result = new HashMap<>(channelIds.size()); | |
| 65 | + for (Long channelId : channelIds) { | |
| 66 | + Long max = channelMessageReadRedisDAO.getReadMaxMessageId(channelId, userId); | |
| 67 | + if (max != null) { | |
| 68 | + result.put(channelId, max); | |
| 69 | + } | |
| 70 | + } | |
| 71 | + return result; | |
| 72 | + } | |
| 73 | + | |
| 74 | + @Override | |
| 75 | + public void readChannelMessages(Long userId, Long channelId, Long messageId) { | |
| 76 | + Assert.notNull(channelId, "频道编号不能为空"); | |
| 77 | + Assert.notNull(messageId, "已读消息编号不能为空"); | |
| 78 | + // 1. 已读位置未前进,直接返回 | |
| 79 | + Long prevMaxMessageId = channelMessageReadRedisDAO.getReadMaxMessageId(channelId, userId); | |
| 80 | + if (prevMaxMessageId != null && prevMaxMessageId >= messageId) { | |
| 81 | + return; | |
| 82 | + } | |
| 83 | + | |
| 84 | + // 2. 更新 Redis 频道已读位置 | |
| 85 | + channelMessageReadRedisDAO.updateReadMaxMessageId(channelId, userId, messageId); | |
| 86 | + | |
| 87 | + // 3. 异步推 READ 事件给自己多端同步 | |
| 88 | + getSelf().readChannelMessageEvent(userId, channelId, messageId); | |
| 89 | + } | |
| 90 | + | |
| 91 | + /** | |
| 92 | + * 发送频道已读 READ 事件给自己其他终端;频道无「给发送方刷回执」概念,不广播 | |
| 93 | + */ | |
| 94 | + @Async | |
| 95 | + public void readChannelMessageEvent(Long userId, Long channelId, Long readId) { | |
| 96 | + webSocketService.sendChannelMessageAsync(userId, ImChannelMessageDTO.ofRead(channelId, readId)); | |
| 97 | + } | |
| 98 | + | |
| 99 | + private ImChannelMessageServiceImpl getSelf() { | |
| 100 | + return SpringUtil.getBean(getClass()); | |
| 101 | + } | |
| 102 | + | |
| 51 | 103 | // ==================== 管理后台 ==================== |
| 52 | 104 | |
| 53 | 105 | @Override | ... | ... |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java
| ... | ... | @@ -17,7 +17,7 @@ import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO; |
| 17 | 17 | import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; |
| 18 | 18 | import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO; |
| 19 | 19 | import cn.iocoder.yudao.module.im.dal.mysql.message.ImGroupMessageMapper; |
| 20 | -import cn.iocoder.yudao.module.im.dal.redis.message.GroupMessageReadRedisDAO; | |
| 20 | +import cn.iocoder.yudao.module.im.dal.redis.message.ImGroupMessageReadRedisDAO; | |
| 21 | 21 | import cn.iocoder.yudao.module.im.enums.group.ImGroupMemberRoleEnum; |
| 22 | 22 | import cn.iocoder.yudao.module.im.enums.message.ImGroupMessageReceiptStatusEnum; |
| 23 | 23 | import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum; |
| ... | ... | @@ -66,7 +66,7 @@ public class ImGroupMessageServiceImpl implements ImGroupMessageService { |
| 66 | 66 | @Resource |
| 67 | 67 | private ImGroupMessageMapper groupMessageMapper; |
| 68 | 68 | @Resource |
| 69 | - private GroupMessageReadRedisDAO groupMessageReadRedisDAO; | |
| 69 | + private ImGroupMessageReadRedisDAO groupMessageReadRedisDAO; | |
| 70 | 70 | |
| 71 | 71 | @Resource |
| 72 | 72 | private ImGroupService groupService; | ... | ... |
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/dto/ImChannelMessageDTO.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.im.service.websocket.dto; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 4 | 4 | import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO; |
| 5 | +import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; | |
| 5 | 6 | import lombok.Data; |
| 6 | 7 | import lombok.experimental.Accessors; |
| 7 | 8 | |
| ... | ... | @@ -53,4 +54,17 @@ public class ImChannelMessageDTO { |
| 53 | 54 | return BeanUtils.toBean(message, ImChannelMessageDTO.class); |
| 54 | 55 | } |
| 55 | 56 | |
| 57 | + /** | |
| 58 | + * 构建已读同步 DTO;多端同步:通知自己的其他终端「我已经读了某频道」 | |
| 59 | + * | |
| 60 | + * @param channelId 频道编号 | |
| 61 | + * @param readId 已读位置(最大已读消息编号) | |
| 62 | + * @return 频道 DTO | |
| 63 | + */ | |
| 64 | + public static ImChannelMessageDTO ofRead(Long channelId, Long readId) { | |
| 65 | + return new ImChannelMessageDTO() | |
| 66 | + .setId(readId).setType(ImMessageTypeEnum.READ.getType()) | |
| 67 | + .setChannelId(channelId); | |
| 68 | + } | |
| 69 | + | |
| 56 | 70 | } | ... | ... |
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImplTest.java
| ... | ... | @@ -10,7 +10,7 @@ import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO; |
| 10 | 10 | import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO; |
| 11 | 11 | import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO; |
| 12 | 12 | import cn.iocoder.yudao.module.im.dal.mysql.message.ImGroupMessageMapper; |
| 13 | -import cn.iocoder.yudao.module.im.dal.redis.message.GroupMessageReadRedisDAO; | |
| 13 | +import cn.iocoder.yudao.module.im.dal.redis.message.ImGroupMessageReadRedisDAO; | |
| 14 | 14 | import cn.iocoder.yudao.module.im.enums.message.ImGroupMessageReceiptStatusEnum; |
| 15 | 15 | import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum; |
| 16 | 16 | import cn.iocoder.yudao.module.im.enums.message.ImMessageTypeEnum; |
| ... | ... | @@ -56,7 +56,7 @@ public class ImGroupMessageServiceImplTest extends BaseMockitoUnitTest { |
| 56 | 56 | @Mock |
| 57 | 57 | private ImSensitiveWordService sensitiveWordService; |
| 58 | 58 | @Mock |
| 59 | - private GroupMessageReadRedisDAO groupMessageReadRedisDAO; | |
| 59 | + private ImGroupMessageReadRedisDAO groupMessageReadRedisDAO; | |
| 60 | 60 | @Mock |
| 61 | 61 | private ImWebSocketService imWebSocketService; |
| 62 | 62 | ... | ... |