Files
zyt/artifacts/im-chat-history/backend-review.md
T
2026-09-09 15:47:48 +08:00

5.6 KiB
Raw Blame History

聊天归档后端只读复核

检查范围:DiagnosisLogic 的回调、消息键、持久化、患者历史读取、分页与批量同步,DiagnosisController 的读取/触发接口,ImChatSyncSession、ImRoamMessagePager、SyncImChatArchive、定时 SQL 与实际 Crontab 调度器,以及 IM callback 的鉴权入口。

以下为检查时仍存在的三个实质问题。未修改业务代码。

1. [P1] 多元素消息只保存第一个元素,形成不可恢复的内容缺失

位置:DiagnosisLogic.phpparseTimMsgBody;同文件 persistImChatArchiveRowsinsertIgnoreImChatBatch

parseTimMsgBody 遇到首个文本、图片或其他已识别元素就 return $out。因此合法的 MsgBody=[text1,text2] 只归档 text1[text,image] 则丢失图片。数据库行也没有保存原始 MsgBody。新回调和历史补拉都复用这条解析路径;稍后即使再次取回完整消息,相同 msg_id 的 ON DUPLICATE KEY UPDATE msg_id=msg_id 也不会补齐内容。

用真实 normalizeTimMessage 的 Reflection 调用作了无数据库复现:两段 TIMTextElem 分别为 first fragmentsecond fragment,输出 text=first fragment,没有原始消息体字段。官方明确单条消息可包含多个消息元素。腾讯消息格式说明

建议保留全部消息元素或原始 JSON,再由展示层处理;并处理已有不完整归档的补齐,不能只靠重复键忽略。此问题源于已有解析函数,但新增即时归档仍沿用它,未实现完整内容留存。

2. [P2] 定时补拉失败被调度器清成正常状态

位置:SyncImChatArchive.phpCrontab.php

同步返回错误时,命令写日志后 return 1。但 SQL 所注册任务实际由 Crontab::start 调用 Console::call:本项目 ThinkPHP Console::call 执行 Command::run 后没有读取退出码,只返回 Output;Crontab::start 也没有读取输出,紧接着无条件把数据库任务 error 清为空字符串。只要没有抛出异常,任务列表就不会反映腾讯请求或归档写库失败。

证据:Console.php 明确丢弃 run 返回值;Crontab.php 仅以异常作为错误分支。实际结果是 CLI 单独执行可见非零退出码,已部署的数据库定时任务却仍显示正常,历史缺口容易持续被忽略。

建议让该命令与调度器明确传递失败状态并持久化错误,同时保持所需的后续重试安排;仅返回 1 不足以修复这条调度链。

3. [P2] 医生单侧漫游结果被当作整个会话的完整归档

位置:DiagnosisLogic.phpadvanceImChatSyncnextPage 调用与完整检查点写入。

所有补拉固定使用 doctor_* 作为 Operator_Account、patient_* 作为 Peer_Account。医生侧返回 Complete=1 后即建立该患者/医生的完整检查点,后续仅从检查点前两分钟重叠补拉,整个流程没有切换患者侧查询。

腾讯官方说明:任一侧清空或删除会话、删除部分消息,以及 REST 发送时 SyncOtherMachine=2,都会使双方能拉到的历史不同。因此,在首次补历史时,医生侧已经删除但患者侧仍保留的消息会被完全遗漏;医生侧 Complete=1 只证明该侧记录拉完,不能证明患者会话全部归档。新回调不能追溯部署前已发生的消息。腾讯历史消息接口说明

建议分别完成双方视角的补拉后再认定完整,使用现有 msg_id 去重;或至少按视角分别记录检查点,并对只完成一侧的结果保留准确语义。

已发现并在复核过程中修复的项目

SendMsgResult 非零消息原先同样落入普通聊天历史。官方明确发送失败仍触发发送后回调,0 表示成功、非 0 表示失败。腾讯发送后回调说明

复读当前代码已看到 archiveImCallbackMessage 校验整数 SendMsgResult、非零返回 0;此项已解决,不计入上述未解决问题。

未发现确证问题的检查项与验证边界

  • 回调与漫游消息使用相同 from/to/MsgKey 时,实际 normalize 结果 msg_id 一致;旧 seq_random_from 键复用前会核对患者、双向账号与消息时间,没有发现可证实的跨患者键复用。
  • 读取/触发控制器先检查诊单可读权限;读取以真实 patient_id 汇总并再次核对双方 IM 账号;同步 token 绑定诊单、患者及管理员。
  • 同步失败会话不写完整检查点;归档抛异常时页游标不推进。没有发现当前状态机可确定复现的无限续页。
  • 批量查询按 patient_id 分组选择 MAX(id),对静态患者集合可按游标遍历并回绕。检查了已安装 ThinkORM 的 column/字段解析实现,未发现此处可确证的生产 SQL/ORM 语法错误;未连接 MySQL 执行。
  • php server/tests/ImRoamMessagePagerTest.php 通过;php server/tests/ImCallbackTest.php 的 81 项断言通过。两者均使用虚构配置和替身,无业务数据库或腾讯 API 调用。
  • 附加执行了真实消息归一化函数的无数据库复现,确认多元素内容丢失及 callback/roam 消息键相同。

本报告是只读复核;唯一新增文件为本报告。