# 群聊 @ 联动私聊列表：能力需求与联调验收

> 2026-10-12 产品语义修正：私聊卡片的未读数字、红点及读屏提示只反映直接私聊消息。群内 @ 互动不在私聊卡片产生任何未读指示，包括直接消息未读归零后的替代角标、`@N` 和附加绿点。群互动摘要与排序可继续展示，原群的已读状态和提醒由原群管理，不因进入私聊而批量标已读。Host/SDK/Tools 查询仍保留群互动事实。本页下文是历史需求记录，其中要求在私聊卡片显示群互动未读提示的规则已被此修正取代。

状态：产品交互已确认；本轮完成最新后端代码核对，尚未实现或部署本功能。本文不代表已上线，不替代现有私聊群聊日历合同。

## 1. 要解决的问题

双方在群聊中的 @ 互动已经显示在私聊内容区，但第二栏私聊卡片仍只消费直接消息。因此，没有直接消息的联系人卡片只有名称，新的群互动也不能在对应私聊卡片上提醒；在群里读过之后更没有对应的联动消除。

目标不是在私聊中复制一条消息，而是让同一条群消息在联系人的列表卡片上有正确的展示和提醒。

## 2. 已确认的产品规则

| 场景 | 私聊卡片表现 |
| --- | --- |
| 对方在群里 @ 我，原消息尚未读 | 显示群互动摘要、时间和未读提示 |
| 我在群里 @ 对方 | 更新摘要、时间和活动排序；我的发送不产生自己的未读提示 |
| 没有直接私聊消息，但存在双方群互动 | 仍显示互动摘要，例如“群名 · 对方：消息内容” |
| 较新的直接私聊消息到达 | 摘要切换到较新的直接消息 |
| 在群里读到原消息 | 对应互动提示消除；其他未读互动和直接私聊未读保留 |
| 有历史互动 | 补齐摘要；不能因首次加载、升级或重新登录而把历史全部标成新消息 |

摘要和活动时间比较的是消息/互动发生的时间，而不是接口请求时间或缓存更新时间；原有置顶、隐藏、免打扰等策略继续生效。

同一原群消息在群卡片和私聊卡片中出现，不能给全局未读总数额外增加一份。现有直接消息未读与新增互动提醒须能区分，不能把它们无条件相加后覆盖私聊游标。

本轮要求“群里阅读 → 私聊互动提示消除”。不要求为了消除私聊提示而批量把整个群标记已读；单纯打开私聊也不能清掉尚未看到的群消息。

## 3. 最新代码核对结果

核对时间：2026-09-19（北京时间）。

- Chat：远程 `master`，`60336c85c8c7a4e4d0bdd3153525c0fef06aff76`，本地独立只读检出。没有修改或部署后端。
- World：已刷新 `origin/master`，`06f3b56`；本次对比确认互动模块相对本地旧检出无变化。
- 桌面插件：`src/services/source-service.ts` 的 root directory 投影只从当前会话 `latest_preview`、`unread_snapshot` 生成卡片；`ArkmeSidebar.tsx` 的互动请求在激活某个私聊后才运行，不提供目录级互动摘要。

| 已有能力 | 代码证据及限制 |
| --- | --- |
| 私聊/群聊目录、会话已读游标、@ 已确认状态 | Chat 已有 `/chats/list`、`/chats/cursor/update`、`/chats/mentions/*`；这些底层事实可复用，并非需要重建一套消息系统 |
| 私聊群互动日历统计、范围明细、定位 | 已在上述 Chat master 注册 `/chats/interwoven/statistics/query`、`items/query`、`items/around`；合同明确只覆盖 Chat-owned @ 关系，不覆盖旧 World 历史，合入不等于生产部署和联调完成 |
| Chat 旧互动 summary/bootstrap | `gin/api/chat_private_interwoven.go:147` 的 summary 直接将 `HasNew` 设为 `len(items)>0`，`NewCount` 设为条目数；不是未读口径，不能当红点 |
| World 互动 summary | `internal/interwovenmoments/service.go` 根据互动列表 last-viewed 时间判断更新；不等价于原群消息已读 |
| 当前用户的 @ 列表 | `gin/api/chat_mentions.go:158` 只返回有上限的首批条目，没有下一页游标；不能用这一批推断所有联系人的完整未读，也不含“我 @ 对方”的完整双向摘要 |
| 目录排序与展示 | `gin/api/chat_api_helpers.go:70` 的会话 bundle 没有按对端聚合的群互动状态；`resolveChatConversationSortActiveAt` 按该会话活动确定排序，没有将其他群中的双方 @ 纳入私聊列表排序 |

结论：缺的不是群互动明细本身，而是**供会话列表使用的、可及时更新的双向互动摘要与单向未读投影**。现有零散接口足以做部分展示，但不能仅凭其首批结果承诺完整提醒和全目录排序。

## 4. 后端需提供的能力（不指定路由、字段或内部实现）

请后端评估复用现有 owner 查询、目录投影及失效通知的最佳组合。可以扩展列表，也可以增加有界批量读取；不要求新建集合、不强制做合并时间轴，不要求前端直接使用大量原子调用。

### 列表摘要与发现

- 能确定当前用户与每个相关联系人的最新可见群 @ 互动，包括双向 @、原群名称、发送方向、摘要、发生时间和稳定来源身份。
- 与直接消息比较后获得列表所需的最新展示和活动排序依据。新互动发生在尚未加载的目录后续页时，也应能把该联系人正确带到前面，不能只重排客户端当前已加载的一页。
- 原本已有私聊入口但零直接消息的联系人必须支持。尚无私聊入口、已主动移除/隐藏入口是否重新出现，应沿用现有会话创建和隐藏策略；若要改变须另确认，不能由本功能擅自恢复。
- 旧 World 互动若暂不纳入权威摘要，需要说明范围和迁移/兼容安排；不能声称覆盖全部历史，也不能用旧 World 的“新互动数”补作未读数。

### 互动未读与阅读联动

- 区分“对方 @ 我且尚未读”和“我发出的 @”；前者才是本轮新增提醒来源。
- 原群消息已读游标及已有 @ 确认事实应复用同一业务判断。能把已读变化关联到受影响的联系人，且不清掉无关互动。
- 支持同一账号另一设备阅读、重新登录和断线重连后的收敛。前端内存或 localStorage 不能成为唯一已读依据。
- 同一原群消息重复通知、重复拉取不能重复记数；消息编辑移除 @、删除/撤回、失去群权限或关闭互动能力后，摘要和提醒需要移除或回退到下一条有效内容。
- 保留已有私聊消息读游标含义。不能把群消息序号写进私聊游标，也不能为满足列表展示伪造私聊消息。

### 更新、成本与安全

- 新消息、双方 @ 变化、原群已读变化及权限变化可触发相关目录项刷新；通知可只用于失效提示，展示内容由鉴权后的查询确定。
- 登录和打开列表不应逐个联系人扫描共同群历史；请求规模应有界，支持现有目录分页和新活动发现。由后端给出实际负载下的查询/刷新成本及建议调用频率，不在需求侧指定索引或存储结构。
- 不把依赖失败、部分覆盖或权限未知当作“没有互动/没有未读”；说明缓存可用范围和重试方式。账号切换清理缓存，迟到响应不得串号或覆盖较新的已读结果。
- 原群私密、隐藏、免打扰等策略与联系人列表策略由后端明确组合规则，客户端沿用统一判断，不绕过权限显示正文。

## 5. 前端和 DSH 的分工

- 桌面/Flutter 消费同一业务口径，实现卡片摘要、红点和实时刷新；不要在两个端各自拼一套完整性不明的未读算法。
- DSH 可以同时使用原子查询和为列表场景优化的业务查询。工具应明确来源范围、摘要时间、未读含义和失败状态，沿用用户身份与权限。
- DSH 应能回答“我和某人的最新群互动是什么”“哪些群互动还没读”。通过对话得到正确答案是功能验收之一，但不能替代列表时延、分页排序、多端同步和去重验收。
- 统计、排序和去重应由确定性代码完成，不由模型从部分历史文本猜测。
- 后端给出实际合同后再接入；本轮不预写假接口、假字段或用 `has_new` 临时替代未读。

## 6. 验收清单

1. 零私聊消息的联系人有历史群互动：卡片能显示摘要，已读历史不因首次加载而变成未读。
2. 我在群里 @ 对方：对方卡片活动更新，我的卡片没有新增未读。
3. 对方在群里 @ 我：无需先打开私聊，列表即可显示摘要和提示。
4. 读到该条原群消息：私聊中这条互动提示消失；较晚尚未读的 @ 与直接私聊未读不受影响。
5. 两个群都有互动，只读其中一个：另一个的未读保留；同一群不同发送人的提示只按实际覆盖的阅读范围清除。
6. 原群已读后又收到新的 @，同时有旧请求迟到：新提示不被旧响应覆盖，旧提示不会复活。
7. 另一台设备阅读、断线重连、退出再登录：状态收敛，历史不重复提醒，全局未读不重复累计。
8. 最新互动被删、@ 被移除或权限收回：卡片回退到下一个有效摘要，无敏感信息残留。
9. 被 @ 联系人在目录后续页：新活动能被发现并正确排序，置顶和主动隐藏规则不变。
10. 互动量超过现有首批上限：摘要、未读不因截断而错误；未知/部分覆盖有明确处理。
11. DSH 与 UI 查询同一账号、同一联系人、同一状态，业务结果一致；分别验证权限和失败场景。

后端回复请包含：可复用的现有能力、建议的最小扩展、实际对接合同与范围限制、通知/重连机制，以及可联调版本和测试环境。字段、路由、查询与缓存策略由后端结合实现评估，两端再共同确认。
