6.1 KiB
患者查询会话恢复:后端修复与验证
日期:2026-09-18。修改目录仅 C:/kefu/wechat_rpa;未修改 C:/wechat_rpa、未部署、未查询真实患者。测试使用合成身份、临时数据库和模拟上游响应,测试运行器阻断外部网络。
根因与接口行为
桌面归档凭据与 ZYT 患者查询凭据各有有效期。admin_backend.Database.desktop_session 只验证和续期桌面凭据;原保存的 ZYT token 不会跟随桌面心跳续期,因此归档正常不能证明患者查询仍登录。父任务已实测 desktop/me 为 200、ZYT/session 为失效响应,患者查询上游登录超时;本分支未重复访问真实患者。
现在上游 HTTP 401、code=-1 或明确的登录过期消息会映射为:
{"detail":{"code":"zyt_session_expired","message":"ZYT 患者查询登录已过期,请重新验证账号。"}}
HTTP 状态为 409。存储的 ZYT 会话缺失或不能解密也采用这一可恢复结果。HTTP 403 或权限拒绝保持可读 403,不触发反复登录。其他患者接口上游故障仍为 502。
新增 POST /api/v2/desktop/auth/zyt-session,使用现有桌面 Bearer 凭据,JSON 请求体为 {"zyt_token":"新的 ZYT token"},成功返回 {"ok":true}。仅验证为 terminal=7、active 且 user_id 与当前桌面账号严格一致的身份可更新。异账号返回 403。接口只更新原租户的加密 ZYT token 与当前账号权限、角色和 root 标识,不创建桌面凭据、不切换账号或租户。
上游身份服务网络错误、超时、HTTP 5xx、无效 JSON/身份数据返回 502,详情 code 为 zyt_unavailable,提示稍后重试;真正认证失效为 409,权限拒绝为 403。此分类复用 zyt_auth 保存的异常原因;没有修改该适配器或增加部署依赖。
并发与权限
archive_store.refresh_zyt_session 在 BEGIN IMMEDIATE 事务内重新检查原桌面 token、session/device 撤销状态、账号启用状态、账号与设备归属、租户及身份匹配,然后一并更新权限和加密 token。上游验证期间撤销桌面会话、撤销设备或停用账号时,返回 401,不能续活。任何写入失败会回滚权限和 token,避免部分更新。
新事务采用 expires_at >= now;现有 Database.desktop_session 采用相同条件。没有修改表结构或桌面凭据续期政策。
验证结果
- 首轮 60 项全通过:
test_zyt_session_recovery当时 18 项,以及test_zyt_patient_client、test_archive_api、test_admin_api.AuthTest既有 42 项。结果见backend-after.json/.log。 - 补充网络分类后,恢复专项 23 项全部通过,0 失败、0 错误、0 跳过;结果见
backend-final.json/.log。总计覆盖 65 项不同测试,最后一轮仅重复变更相关专项。 - 新增覆盖:HTTP 401/403、业务过期和权限消息、缺失会话恢复、同账号更新、异账号拒绝、桌面归档仍有效、验证中撤销、设备撤销、账号停用、权限撤回和恢复、事务回滚,以及通过真实
zyt_auth适配器模拟网络异常、超时、HTTP 500/502/503、坏 JSON、非对象响应、缺失身份、HTTP 401、业务过期、HTTP 403。 - 没有用真实患者查询验证恢复后的结果,没有声称生产已部署。生产验证由父任务负责。
部署文件
仅四个生产文件:
zyt_patient_client.py:异常分型及上游错误分类。archive_api.py:409/403 对外映射。archive_store.py:缺失会话可恢复、原子刷新及再次校验。admin_api.py:新请求体和恢复接口、身份服务故障分类。
测试文件 test_zyt_session_recovery.py 不必进入运行镜像。原件备份在 before-backend/,前后 SHA-256 在 backend-manifest.json。没有 admin_backend.py、rbac.py、zyt_auth.py 或 schema 变更。目标目录的 admin_api.py 有其他协议版差异,应依据备份与修改后文件定向应用本轮差异。
PHP 只读审计及边界
本地完整 PHP 源码位于 C:/zyt,由现有部署文档定位;本轮只读,未更改。它不能直接证明线上 PHP 版本完全一致。
server/config/project.php:36:管理员 token 有效期 8 小时,临近过期窗口 1 小时;terminal=7 共用此政策。LoginMiddleware.php:53:过期/失效 token 在普通接口返回“登录超时,请重新登录”。第 63 行在 token 仍有效且进入窗口时调用AdminTokenService::overtimeToken,续原 token,不签发另一枚 token。DesktopController.php:23:只有专用session标记免普通登录中间件限制,但方法内部仍验证身份、terminal=7 和账号状态。GET/adminapi/desktop/session经过登录中间件,能保活尚未过期的 token,不能复活已过期 token。该请求首次触发续期时响应里的 expire_time 来自旧的 request.adminInfo,下一次读取才反映新有效期。AdminTokenService.php位于server/app/adminapi/service/:登录按(admin_id, terminal)找会话;过期或禁用同终端多点登录时替换 token,其他情况下复用有效 token 并续期。网页与 terminal=7 分开,不能笼统认为网页登录会踢桌面。- 没有发现独立 refresh_token 接口;现有可用路径是仍有效时读专用 session 保活,以及过期后走已支持的登录接口重新认证。
- IP 校验没有 terminal=7 豁免。
AuthMiddleware.php:50对非免登录路由无条件比较缓存 login_ip 与当前请求 IP。专用desktop/session绕过该限制,tcm.diagnosis/lists不绕过。因此新 token 可被管理服务器验证身份,不等同于必然可以从不同出口查询患者。桌面与管理服务器若共用公网出口则不一定触发;确实不同出口时可能出现“ip地址发生变化,请重新登录”。本轮没有放宽这一限制,也没有改变患者数据范围和菜单权限。
对仍有效 token,可由桌面通过专用 session 做有限保活;过期后仅恢复原账号的患者凭据。若实际部署确认不同出口被拦截,需要另行设计有明确身份和患者数据权限的受限接口,不应全局取消 IP 校验。