forked from baskbuddy/zhangshun
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2.6 KiB
2.6 KiB
R01-D3 会话未读数查询收束报告
结论
MessagingService.listConversations 已从逐会话查询最近消息和已读状态,收束为一次分组未读统计。会话数量从 1 增长到 20、50 时,消息相关业务 SQL 数保持固定;本次未改变公网接口、响应字段、权限、消息撤回或已读语义。
修复前问题
会话列表先查询成员关系和会话,再对每个会话调用 countUnread。每次调用都会查询最近 500 条消息及其已读记录,因此总查询数随会话数线性增长。
实现
MessageRepository.countUnreadByConversationIds使用一条分组 SQL 批量统计全部会话。- 使用
ROW_NUMBER() OVER (PARTITION BY conversation_id ORDER BY created_at DESC)保留每个会话“最近 500 条”的历史窗口。 RECALLED消息不计入未读,status IS NULL仍按历史语义计入。- 存在当前用户
message_reads的消息不计入未读。 - 没有未读行的会话由 Service 映射为
0。 getConversation的单会话路径未改动,避免扩大本批范围。
查询预算证据
MessageListQueryBudgetIntegrationTest.listConversationsUsesFixedQueriesForOneTwentyAndFiftyConversations 使用真实 Spring/JPA/H2 数据源代理统计业务 SQL,并混合 DIRECT、TALENT_INVITATION、SENT、RECALLED、已读和未读消息。
| 会话数 | 业务查询数 | 结果 |
|---|---|---|
| 1 | 3 | PASS |
| 20 | 3 | PASS |
| 50 | 3 | PASS |
三条查询分别为成员关系、会话批量读取、未读分组统计,查询数不随会话数增长。
语义边界
groupedUnreadCountKeepsHistoricalLatestFiveHundredWindow 构造 501 条顺序明确的消息,证明最旧消息位于窗口外时不被统计。测试首次编写时,实体的 @CreatedDate 覆盖夹具传入时间,造成 501 条消息时间并列;随后改为保存后按精确消息主键写入确定时间。该失败属于夹具问题,不是生产缺陷,也未通过放宽断言处理。
验证
- 定向边界测试:3/3,Failures=0、Errors=0、Skipped=0。
- 关联消息测试:此前同批运行 42/42 通过。
mvn clean verify:2104 项,Failures=0、Errors=0、Skipped=20,BUILD SUCCESS;20 项均为既有环境条件测试。- 静态 OpenAPI:176 paths / 214 operations,校验通过。
边界
- 未新增或修改 Flyway、表、索引、ErrorCode、公网接口。
- 未改变 DIRECT / TALENT_INVITATION 响应隐私语义。
- 未改变消息发送、撤回、已读、分页和权限语义。
- 原生窗口 SQL 仍需在独立真实 MySQL 验收中复核执行计划与结果。