5 changed files with 154 additions and 2 deletions
@ -0,0 +1,48 @@ |
|||||
|
# 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 验收中复核执行计划与结果。 |
||||
Loading…
Reference in new issue