2026-06-30 24681c81c09022f584a57006f2534b5f74723414
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package cn.iocoder.yudao.module.im.dal.mysql.conversation;
 
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ImConversationReadDO;
import cn.iocoder.yudao.module.im.enums.ImConversationTypeEnum;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
 
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
 
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static org.junit.jupiter.api.Assertions.*;
 
/**
 * {@link ImConversationReadMapper} 的单元测试
 *
 * @author 芋道源码
 */
public class ImConversationReadMapperTest extends BaseDbUnitTest {
 
    @Resource
    private ImConversationReadMapper mapper;
 
    @Test
    public void testUpdateReadMessageIdToLarger_onlyAdvancesWhenLarger() {
        // 准备:用户 1 在群 10 的读位置 = 50
        ImConversationReadDO read = buildRead(1L, ImConversationTypeEnum.GROUP.getType(), 10L, 50L);
        mapper.insert(read);
 
        // 1. 新位置更大 → 前进
        int advanced = mapper.updateReadMessageIdToLarger(read.getId(), 100L, LocalDateTime.now());
        assertEquals(1, advanced);
        assertEquals(100L, mapper.selectById(read.getId()).getMessageId());
 
        // 2. 新位置相等 → 不更新
        int equalUpdate = mapper.updateReadMessageIdToLarger(read.getId(), 100L, LocalDateTime.now());
        assertEquals(0, equalUpdate);
 
        // 3. 新位置更小(乱序上报)→ 不回退
        int smaller = mapper.updateReadMessageIdToLarger(read.getId(), 80L, LocalDateTime.now());
        assertEquals(0, smaller);
        assertEquals(100L, mapper.selectById(read.getId()).getMessageId());
    }
 
    @Test
    public void testSelectByUserIdAndConversation() {
        ImConversationReadDO read = buildRead(1L, ImConversationTypeEnum.GROUP.getType(), 10L, 50L);
        mapper.insert(read);
        // 同用户不同会话类型,不应命中
        mapper.insert(buildRead(1L, ImConversationTypeEnum.PRIVATE.getType(), 10L, 99L));
 
        ImConversationReadDO result = mapper.selectByUserIdAndConversation(
                1L, ImConversationTypeEnum.GROUP.getType(), 10L);
 
        assertNotNull(result);
        assertEquals(50L, result.getMessageId());
    }
 
    @Test
    public void testSelectListByConversation_aggregatesAllUsers() {
        // 群 10 内三个用户的读位置,用于群回执人数聚合
        mapper.insert(buildRead(1L, ImConversationTypeEnum.GROUP.getType(), 10L, 100L));
        mapper.insert(buildRead(2L, ImConversationTypeEnum.GROUP.getType(), 10L, 80L));
        mapper.insert(buildRead(3L, ImConversationTypeEnum.GROUP.getType(), 10L, 50L));
        // 别的群,不应混入
        mapper.insert(buildRead(1L, ImConversationTypeEnum.GROUP.getType(), 20L, 999L));
 
        List<ImConversationReadDO> list = mapper.selectListByConversation(
                ImConversationTypeEnum.GROUP.getType(), 10L);
        Map<Long, Long> positions = convertMap(list,
                ImConversationReadDO::getUserId, ImConversationReadDO::getMessageId);
 
        assertEquals(3, positions.size());
        assertEquals(100L, positions.get(1L));
        assertEquals(80L, positions.get(2L));
        assertEquals(50L, positions.get(3L));
    }
 
    @Test
    public void testSelectListByUserIdAndConversations_batch() {
        // 用户 1 在频道 10 / 20 / 30 的读位置
        mapper.insert(buildRead(1L, ImConversationTypeEnum.CHANNEL.getType(), 10L, 5L));
        mapper.insert(buildRead(1L, ImConversationTypeEnum.CHANNEL.getType(), 20L, 8L));
        mapper.insert(buildRead(1L, ImConversationTypeEnum.CHANNEL.getType(), 30L, 3L));
 
        List<ImConversationReadDO> list = mapper.selectListByUserIdAndConversations(
                1L, ImConversationTypeEnum.CHANNEL.getType(), List.of(10L, 20L));
 
        assertEquals(2, list.size());
    }
 
    private ImConversationReadDO buildRead(Long userId, Integer conversationType, Long conversationId,
                                           Long readMessageId) {
        return ImConversationReadDO.builder()
                .userId(userId)
                .conversationType(conversationType)
                .targetId(conversationId)
                .messageId(readMessageId)
                .readTime(LocalDateTime.now())
                .build();
    }
 
}