Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
pac
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ai-tools
pac
Commits
70f95af3
Commit
70f95af3
authored
Oct 22, 2026
by
luoqi
Browse files
Options
Browse Files
Download
Plain Diff
Sync archived Meyer and patient facts documents into test
parents
2b926b18
c1e09831
Pipeline
#3860
failed in 0 seconds
Changes
9
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
627 additions
and
0 deletions
+627
-0
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-17-meyer-imaging-adapter-local.md
+134
-0
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-19-meyer-diagnosis-mapping.md
+87
-0
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-19-meyer-open-facts-local.md
+70
-0
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-19-open-patient-facts-test.md
+56
-0
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-20-fact-content-field-contract-review.md
+0
-0
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-20-meyer-open-facts-test.md
+57
-0
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-21-open-content-source-time-test.md
+52
-0
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-21-source-time-eight-hour-readonly-audit.md
+131
-0
docs/_archive/2026-10-meyer-and-patient-facts/README.md
+40
-0
No files found.
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-17-meyer-imaging-adapter-local.md
0 → 100644
View file @
70f95af3
# 美亚影像补充采集:本地实现与验证
> 历史归档(2026-10-22):本文记录当时的实现、审核或验证情况,正文中的“未提交/未部署”等状态仅指原记录日期;不代表当前版本或运行状态。现行说明见本目录索引。
日期:2026-10-17。分支:
`codex/meyer-imaging-adapter`
,从
`main@1cafc8ae2df5a87d1d9d5ff01bb48c6c702b818f`
创建独立工作区。本次未提交、合并或部署。
## 实现
-
独立
`meyer-imaging`
CLI,默认只采集/留底/校验;
`--apply`
才连接明确指定的 PAC 数据库。
-
美亚认证、诊所分页、逐张病症查询均在适配器中;不注册到主服务、不修改主摄入游标。
-
显式诊所到 Friday tenantId/sourceUnit 映射,只匹配已有患者;不创建患者、不移动归属、不触碰其他宿主。
-
首次有效牙位分析转换为现有
`image_study`
和
`diagnosis`
,通过现有解析器、FactWriter 落成标准事实。来源
`image_ai`
,复用共享临床字典精确映射,不改画像或召回算法。
-
无分析完成状态/时间时不伪造 image_analysis;诊断证据时间采用检查时间,原始分析和时间依据保存在私有留底及事务原文。缺时间/空结果等待后续重试。
-
本期不做对账。成功的影像不复查、不自动更新或撤回;未完成可重跑固定窗口。原片不下载,临时 URL 不作为永久地址。
配置、运行方法与取舍见
[
适配器说明
](
../../../apps/pac-service/src/integrations/meyer/README.md
)
。
## 首轮模拟验证结果
| 检查 | 结果 |
|---|---|
|
`pnpm --filter @pac/service type-check`
| 源码和测试 TypeScript 检查通过 |
|
`pnpm --filter @pac/service build`
| Nest 构建通过 |
| 美亚适配器单元/HTTP 测试 | 13 项通过;实际 localhost HTTP,非仅 mock fetch |
| 独立 PostgreSQL 集成测试 | 6 项通过;包含完整 CLI apply 路线 |
| 现有影像分析与 canonical 时区回归 | 110 项通过 |
| 合计 | 4 套、129 项通过,单进程运行 |
测试覆盖标准影像及 K02 诊断落库、原始依据留存、时区与牙位、未知分析不制造阴性结论、重复运行不增版本、跨品牌同 ID 隔离、其他宿主不写入、未知患者拒绝、部分落库失败后恢复、状态锁、改绑拦截、分页数量变化/缺页、超时、HTTP 200 业务失败以及认证失效的一次重试。
数据库使用本地 PostgreSQL 内独立
`pac_meyer_ci_*`
数据库和临时角色,测试后已删除。首轮验证没有访问真实美亚、测试服务器或生产服务器。主目录原有未提交文档保持原状,本地应用未重启。
## 首轮待确认事项(真实联调更新见下)
1.
美亚部署地址、对应 SaaS 应用的读取凭据及诊所范围。PDF 与 SDK 的病症接口参数不一致,本实现按 PDF 带上 patientId。
2.
真实返回的时间和牙位格式;当前源时间按中国部署
`YYYY-MM-DD HH:mm:ss`
解析,不依据本机时区猜测。
3.
首期不承诺迟到分析自动追齐、完整变更同步或删除识别;须检查待处理计数再移动时间窗。
4.
该采集任务不自行重算;事实由现有、已启用的画像水位扫描消费。未来如需实时入队,单独增加既有队列的提交机制,不更改召回方案。
## 真实接口联调(2026-10-17 08:15 上海时间)
**结论:现有 SaaS 源码默认配置可读取真实美亚测试影像和分析;已在一次性本地数据库验证标准事实落库。未部署。**
### 配置与测试数据
-
使用
`friday-saas/emr`
中现有配置,地址
`https://re-pacs.myyun.com/api/v1`
;鉴权 HTTP 200、业务 code 200。凭据未复制进仓库。
-
只读查询 FridaySaaS 测试 MySQL(123.57.209.112:3309)中的
`emr.med_pacs_sync_record`
,核对患者主档和品牌、诊所归属。当前有效患者绑定涉及 4 个诊所。
-
在上述范围查询美亚:
`ba57…4c2b`
返回总数 8,
`7992…8e68`
返回总数 24;后者只取第一页 20 张用于找样本,未全量采集。其余两诊所返回业务 code 460,不能解释成“没有影像”。
-
样本为测试患者“患者01”,源患者 ID
`676698`
,诊所
`7992406f9e4b44c7ba3a67c8de368e68`
。患者主档和 PACS 同步记录均确认品牌
`c464d41cf2f8408187d8b5c525bc0369`
。组织树未查到该诊所名称,因此不猜测名称。
-
以上证明源码配置可以读取测试绑定对应的数据;尚未读取 SaaS 运行进程配置,不能据此保证部署环境没有配置覆盖。
### 真实采集与落库
仅运行该患者的固定入库窗口
`2026-10-16 15:51:31–15:51:33`
。美亚实际返回 2 张,已核对均属于同一患者;其中影像
`369391900555579392`
有 11 条牙位分析,另一张分析为空。
| 验证 | 实际结果 |
|---|---|
| PDF 参数(含 patientId)查询分析 | 业务 code 200,返回 teeth / diagnosis / description |
| 独立 CLI 采集 | 2 张;1 张有效分析、1 张待处理、0 失败 |
| 本地标准事实 | 1 条 image_record + 11 条 diagnosis_record,12 条事务 |
| 信号示例 | 36/37 根尖周炎 → K04,23 埋伏牙 → K01;来源 image_ai,保留影像关联 |
| 重复运行 | 仍为 12 条事实,全部 version=1;已完成影像跳过 |
| 空分析 | deferred=1,无诊断事实;CLI 退出 2 表示仍有待处理,不是全量完成 |
| 画像与召回 | 均为 0;本次不启动队列或计算任务 |
| 清理 | 临时本地数据库和角色已删除;主库及本地应用未变动 |
真实返回的
`modality=IMAGE_2D`
曾被直接透传,导致 PAC image_record 枚举校验拒绝。适配器现将无法明确对应的供应商类型映射到
`other`
,完整原值保存在原始证据;没有凭二维图片类别猜测全景片等类型。修复后真实采集、落库和重复运行全部通过。
共享字典目前精确匹配到 5 条带 K 编码的牙位事实,其余 6 条保留原文、code=null。例如“龋病”未命中现有精确字典;本次没有扩充疾病推断或改变召回规则。综合文本“慢性牙周炎”仅留原始依据,未额外解析成诊断。事实写入成功不等于所有分析术语都已参与召回。
时间测试发现:以返回的秒级时间同时作为开始和结束,接口返回 0;前后扩展一秒则返回 2 张。底层时间精度或边界行为尚不能确定,不能用零宽窗口作为可靠采集方式。
### 回归与证据
-
4 套测试、130 项通过(新增供应商 IMAGE_2D 到 PAC 事实校验的回归用例);Nest 构建通过。
-
真实原文、绑定、私有采集状态和数据库验证结果保存在本机
`/tmp/pac-meyer-live-private/`
(私有目录),未入 Git;该目录是临时联调证据,不作为长期业务存储。
-
本次只对源端执行鉴权和查询。没有修改 SaaS/美亚数据,也没有向测试或生产 PAC 写入数据。
## 原片访问验证(2026-10-17 13:10 上海时间)
继续使用上述测试患者的同一张影像,只查地址与读取 1 字节,不下载完整原片。
-
`imageId=369391900555579392`
;
`imageOssId=369391906454630400`
。后者是美亚接口使用的文件标识,并非 OSS object key。
-
请求
`POST /api/v1/imaging/external/images/urls`
,参数为原影像
`clinicId`
和
`ossIds: [imageOssId]`
,HTTP 200、业务 code 200、
`found=true`
。
-
返回阿里云 OSS 签名 URL,域名
`myy-pre.oss-cn-beijing.aliyuncs.com`
;对象路径形如
`prod/clinic/<美亚内部诊所ID>/20261016/<imageId>/05170433.png`
。不能从文件 ID 直接推导路径,也不能仅凭 bucket 名或路径中的 prod 判断业务环境。
-
本次返回过期时间
`2026-10-17 13:40:36`
,距查询约 30 分钟。应按 expiresAt 刷新,不能硬编码文档默认 12 小时。
-
对签名地址发 HEAD 返回 403;改用 GET +
`Range: bytes=0-0`
返回
**206**
,
`Content-Type: image/png`
,
`Content-Range: bytes 0-0/1270699`
。实际读取
**1 字节**
;验证了该地址可读,未下载完整 1,270,699 字节文件。HEAD 失败不能用来断言 GET 签名链接失效。
-
这是本样本的“原图文件”PNG,不代表验证了 DICOM 或 CT 切片;CT 需按接口返回的 ctSlices 文件 ID 分别获取地址。
结论:现有美亚应用权限已经足够通过文件 ID 换取原图访问地址,无需先拿阿里云账号权限。PAC 原始摄入证据已留存此文件 ID;目前独立适配器尚未提供前端文件地址刷新接口。若未来绕过美亚直读 OSS,仍需保存/获得 bucket 与完整 object key,不能仅凭 imageOssId。
私有请求结果和验证摘要位于
`/tmp/pac-meyer-live-private/file-url-response.json`
、
`file-url-verification.json`
,签名 URL 不写入文档或仓库。
## 增量运行优化(2026-10-17)
继续在原独立工作区开发,没有提交、合并或部署;本轮不启动真实美亚常驻采集。
-
新增
`--tick`
:一轮独立任务,持久化私有 cursor。每轮最多推进一小时,距离当前时间保留一分钟,窗口重叠一分钟;失败不推进,持久化待分析后可推进。
-
新增
`--watch`
:每轮结束后等待五分钟。正常停止信号会收尾并释放锁;失败退出并保留状态。默认未启用,未安装系统定时器。
-
新增私有 pending/:延迟分析与部分落库失败可以重试。每轮最多 20 项、指数退避到一小时,不自动丢弃。重试窄窗重新核对影像身份;已绑定患者发生变化时拒绝写入。未提供入库时间的待处理项需要人工处理。
-
影像成功后仍不复查分析,不增加历史对账、撤回或自动改绑。
-
新增
`--image-files=<imageId>`
:针对已采集影像,通过 imageOssId 获取原图临时 URL,保存到私有 access 文件。终端不输出签名、不下载文件;不提供公开 Web 接口,不替代患者权限检查。
-
同一轮中不同窗口共享美亚鉴权缓存,避免每个待处理影像都重新申请令牌。
验证包括:迟到分析跨窗口补入、失败不推进水位、窗口重叠与重复写入、患者改绑拦截、重试限额与退避、preview 禁止推进水位、文件 ID/诊所检查、签名不进入控制台,以及完整 CLI tick 经真实本地 PostgreSQL 写入两次而不重复。本轮覆盖 5 套、136 项测试(全套 135 项通过后新增限额用例,相关 20 项复跑通过),TypeScript 检查通过。原有影像分析与时区测试继续通过。临时数据库和角色已删除。
运行参数与状态文件说明见适配器 README。旧状态目录需先重跑原窗口生成 pending 后再启用定时模式;生产启用前仍需确认实际部署配置和诊所到品牌映射。
## 保守限额与断点恢复(后续更新,以本节为准)
默认值已改为:轮间隔 15 分钟、新扫描记录每轮最多 100 条、待处理重试最多 10 张、串行请求间隔 500ms;均可配置。新扫描分页查询默认每轮最多 10 页,单个待处理项窄窗查询也受页数上限控制。轮间隔在本轮收尾后计时。
新增 scan-progress(固定窗口/诊所/页码/页内行/页原文)和 scan-ledger(已完成页的影像 ID)。达到限额只保存断点、不推进窗口水位;下轮继续。数量、首页或当前缓存页身份变动时重扫当前诊所;跨页重复阻止窗口完成。签名 URL 更新不触发窗口重置。文件写入先 fsync、原子 rename、再 fsync 目录。
watch 对请求、鉴权或数据库异常记录失败次数,按默认 15/30/60 分钟退避,依赖恢复后继续。停止信号等当前一轮收尾。正常宿主服务不参与采集循环;数据库仍是落库依赖。进程退出/OOM/机器重启需要外部守护,提供 service 示例(Restart=on-failure、持久状态目录、资源约束),本次未启用。明确死亡的本机进程锁可自动回收,存活、其他机器、未知 owner 或锁回收过程本身被中断的情况不自动抢锁。
存储没有新增数据库表:采集运行状态和原始快照存独立持久目录;业务数据复用 patient_transactions / patient_facts,patients 仅查询。目录不能放临时盘,需备份;当前实现是单机单写者。目录丢失/损坏、永久权限或归属错误需要人工处理,不能承诺自动修复数据。
新测试覆盖跨轮页内续跑、重启恢复、限额、续签 URL 不影响断点、offset 插入和跨页重复、第二页接口故障恢复、watch 退避恢复、死亡进程锁回收与存活锁保护。全套 6 套 141 项通过后增加跨页重复用例并单独复跑;最终覆盖 143 项(含新增的单轮看门狗用例)。真实本地 PostgreSQL 集成保留,临时库和角色已清理。
这里只保证自身限额、重复处理和中断不会主动跳过数据。美亚无快照/更新游标,任意历史回填、已完成窗口外的修改和撤回仍需要将来的对账能力,不能由有限重叠窗口提供绝对保证。
补充单轮卡住保护:watch 的 roundTimeoutMs 默认 10 分钟,超时明确退出,交给外部守护从断点重启;不使用 Promise.race 后继续开新轮,避免旧请求仍在写入。看门狗触发及正常结束清理计时器已增加测试。
## 自动诊所/品牌映射(2026-10-17)
-
已核对 PAC 组织树代码:组织关系由业务记录诊所和关联患者品牌派生,名字典仅存名称。现有树的 LIMIT 1 抽样不能发现冲突,本采集器采用独立只读解析,检查全部不同候选;不改主组织树或召回流程。
-
配置改为 clinicIds;禁止手填 tenantId/sourceUnit。缺失、跨品牌、跨租户、孤立关联均拒绝采集;排除美亚补充产物,避免自我确认。
-
每轮重新解析。既有状态目录绑定映射和数据库,变化时停止,保留快照/断点待核查。
-
本机 pac 数据库只读检查:有 1,141,991 条业务记录,但 Friday 宿主对应业务记录为 0;两家此前美亚测试诊所也没有可解析的本地关系。因此不声称真实映射已验证,不自动创建关系或导入患者。
-
所有实际写入验证在临时隔离 PostgreSQL 数据库执行;不访问、修改或部署测试/生产环境。
-
本次验证:6 个测试套件、144 项测试通过;service 类型检查与构建通过。隔离验证数据库已删除。配置示例和 README 已同步更新,未提交、未部署。
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-19-meyer-diagnosis-mapping.md
0 → 100644
View file @
70f95af3
# 美亚分析标签 → PAC K 码:映射核对与风险
> 历史归档(2026-10-22):本文记录当时的实现、审核或验证情况,正文中的“未提交/未部署”等状态仅指原记录日期;不代表当前版本或运行状态。现行说明见本目录索引。
2026-10-19 ·
`codex/meyer-imaging-adapter`
· 本地改动,未提交、未部署
本轮沿用现有美亚逐张分析接口,只核对
`teeth[].content`
的整词映射。不接入尚未实现的回调或批量接口,不改患者身份、采集调度和召回规则。
## 结论
用户清单共 34 项,去掉完全重复的 6 项后为
**28 个名称**
;“龋病 / 牙龋坏”归同一概念后为 27 类。保留原始名称与牙位,不因同一 K 类别而合并不同病变。
-
**补充 4 个名称**
:缺牙→K08,龋病 / 牙龋坏→K02,牙折裂→K03。
-
**保留 9 个现有映射**
:多生牙、乳牙滞留、埋伏牙、残根、残冠、根尖周炎、楔状缺损、根分叉病变、牙体缺损。
-
**15 个名称暂不定 K 码**
:原始所见照常保留。其中裸词“智齿”从本适配器原来的 K01 改为空码,其余维持空码。
**这里的 K 码是 PAC 现有业务分类,不是完整 ICD 编码,更不代表医生确诊或治疗指征。**
PAC 当前按 K 类别组织治疗机会,补码会改变哪些影像信号可能进入画像和召回,不能只追求覆盖率。
## 逐项核对
已对比本地记录的
`origin/main@b7901c9`
:共享诊断字典与业务 K 类别定义没有差异;未将主线其他改动合入当前工作区。
“原映射”以本工作区的美亚适配器为准:只查共享字典精确词,不使用通用关键词规则。
`—`
表示保留原文、
`code=null`
,不表示正常。
| 去重名称 | 原映射 | 本轮映射 | 判断与风险 |
|---|---|---|---|
| 智齿 | K01 | — | 识别到第三磨牙不等于阻生。共享字典依据宿主医生录入习惯归 K01,不能直接套给影像标签;明确“阻生智齿”仍走原映射 |
| 缺牙 | — | K08 | 对齐既有“缺失牙”。不推断缺失原因、是否已修复或是否适合种植;后续仍受现有牙位、年龄和治疗证据规则约束 |
| 多生牙 | K00 | K00 | 沿用发育/萌出异常类别,不据此判定需要拔除 |
| 牙槽骨吸收 | — | — | 影像所见不能直接补成活动性牙周炎;归 K05 会把所见送入“牙周炎未治疗”路径 |
| 残根 | K03 | K03 | 沿用 PAC 业务口径;不等同标准 ICD 子码,也不表示一定无法保留 |
| 残冠 | K03 | K03 | 同上,是否可保存仍需临床判断 |
| 埋伏牙 | K01 | K01 | 沿用阻生/埋伏类别;只作为影像信号 |
| 乳牙滞留 | K00 | K00 | 沿用发育/萌出异常类别;不把正常换牙期直接理解为治疗需求 |
| 龋病 | — | K02 | 与既有“龋齿/龋坏”类别一致,不推断深浅或治疗方式 |
| 修复体牙冠 | — | — | 修复物存在,不等于不良修复体、待修复疾病或完整治疗记录 |
| 种植体 | — | — | 种植体存在,不等于缺牙、种植体周围炎或种植疗程完成 |
| 充填体 | — | — | 充填物存在,不等于继发龋或充填治疗在本次影像时完成 |
| 根尖周炎 | K04 | K04 | 沿用现有映射。影像来源须保留,不能据此区分初治、再治疗、治疗后愈合或直接决定根管治疗 |
| 牙龋坏 | — | K02 | 与“龋病”同一类别,原词留存 |
| 根充 | — | — | 根管充填痕迹,不等于当前牙髓病,也不能伪造治疗完成时间 |
| 金属冠 | — | — | 属于修复体,不能与“不良修复体”混同 |
| 嵌体 | — | — | 属于修复体,不是疾病名称 |
| 牙髓石 | — | — | 通用关键词会因“牙髓”命中 K04,但当前 K04 召回是牙髓/根尖问题未做根管,不能直接套用 |
| 楔状缺损 | K03 | K03 | 沿用牙体损伤类别;不补严重度和必需治疗结论 |
| 牙折裂 | — | K03 | 对齐 PAC 已有“牙折/隐裂/冠折/根折”的业务类别;不推断折裂部位、外伤编码或保留价值 |
| 种植体周围炎 | — | — | 是有临床意义的标签,但现有自然牙牙周类别及治疗解除逻辑不能直接代表种植体疾病;保留待评估,不降成“无问题” |
| 牙周牙髓联合病变 | — | — | 单码 K04 或 K05 会丢掉另一部分;机械拆两条又会改变召回及解除逻辑,本轮不强行归类 |
| 根分叉病变 | K06 | K06 | 沿用 PAC 现有牙龈/牙槽嵴业务类别,不声称这是标准 ICD 对应,也不在此改全局分类 |
| 颌骨病变 | — | — | 名称过宽,不能猜为囊肿 K09 或某一确定疾病;保留原始异常所见 |
| 牙体缺损 | K03 | K03 | 沿用硬组织损伤类别,不推断是否需要充填、冠修复或拔除 |
| 骨结构不良 | — | — | 缺少部位、病因与标准含义,不能猜为牙周病或颌骨病 |
| 骨岛 | — | — | 不具备映射到当前治疗召回类别的充分依据,保留所见 |
| 牙齿矫正 | — | — | 治疗/状态类表述,不等于错颌诊断;归 K07 可能把在矫正者识别为“未启动正畸” |
完全重复项:缺牙、多生牙、残根、残冠、种植体、根尖周炎,均出现两次。金属冠是修复体牙冠的更具体描述,保留来源粒度,不当作完全相同的词删除。
## 为什么不直接打开共享关键词匹配
共享关键词已能命中缺牙、龋病、牙龋坏、牙折裂,但同样会让“牙髓石”和“牙周牙髓联合病变”命中 K04。共享精确字典还会让裸词“智齿”命中 K01。
代码核对显示,现有治疗召回按诊断 K 码匹配,
`code_source=image_ai`
并不是一层独立的医生确认门槛。因此本轮仅在美亚适配层增加整词规则和所见保护,
**不扩大共享字典影响所有宿主的范围,也不对否定、可疑或复合文本做含词推断。**
K03 的残根/残冠、K06 的根分叉病变等是已有业务口径;若以后需要标准 ICD 子码或更精确的影像风险分析,应将“来源标签、标准临床编码、业务用途”分开,不能靠继续往一个大类里塞词解决。
## 本地变更与验证范围
-
`diagnosis-mapping.ts`
:美亚专用整词补充与 15 项空码保护;其余继续共享精确字典。
-
`mapper.ts`
:调用独立映射函数;原文、牙位、影像关联与
`image_ai`
来源保留。
-
已留底 bundle 按旧快照重试,已成功影像仍跳过。此次补码不会自动修正存量,也不删除状态目录或绕过事务哈希检查。
-
本地 6 套测试、121 项通过,覆盖全部 28 项、可疑/否定/复合词、原文留存、共享字典隔离及原有采集/断点/调度回归;服务源码和测试 TypeScript 检查通过。本轮未执行 PostgreSQL 落库测试。
-
不访问服务器,不修改测试或生产数据,不启动常驻采集、画像或召回任务。
## 核对依据
项目实现:
-
[
共享诊断字典
](
../../../apps/pac-service/data/_shared/dict/diagnosis-kcode.yaml
)
:精确映射与含词规则。
-
[
美亚映射
](
../../../apps/pac-service/src/integrations/meyer/mapper.ts
)
、
[
本轮专用映射
](
../../../apps/pac-service/src/integrations/meyer/diagnosis-mapping.ts
)
:本条采集路线实际使用的规则。
-
[
业务类别及纠偏
](
../../../packages/types/src/canonical-codes.ts
)
:残根/残冠等 PAC 业务口径。
-
[
治疗召回场景
](
../../../apps/pac-service/src/modules/plan/engine/scenarios/treatment-initiation-recall.scenario.ts
)
、
[
诊断缺口查询
](
../../../apps/pac-service/src/modules/clinical-gap/potential-treatment-gap.sql.ts
)
:K 码对治疗机会的实际影响。
临床边界参考(用于识别映射风险,不作为自动确诊依据):
-
[
ADA:智齿
](
https://www.ada.org/sitecore/content/ADA-Organization/ADA/MouthHealthy/home/all-topics-a-z/wisdom-teeth
)
:智齿与发生阻生或病变的智齿不是同一概念。
-
[
EFP:牙周炎诊断流程
](
https://www.efp.org/fileadmin/uploads/efp/Documents/Campaigns/New_Classification/Guidance_Notes/report-02b.pdf
)
:影像骨丧失还需结合临床检查,不直接等同待治疗牙周炎。
-
[
EFP/AAP:种植体周围疾病共识
](
https://doi.org/10.1002/jper.17-0739
)
:种植体周围炎有自身的临床和影像判断依据,不能机械套用自然牙治疗路径。
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-19-meyer-open-facts-local.md
0 → 100644
View file @
70f95af3
# 美亚适配器与开放患者接口:本地整合验收
> 历史归档(2026-10-22):本文记录当时的实现、审核或验证情况,正文中的“未提交/未部署”等状态仅指原记录日期;不代表当前版本或运行状态。现行说明见本目录索引。
2026-10-19 ·
`codex/open-patient-facts`
· 未提交、未部署
## 范围
将
`codex/meyer-imaging-adapter`
工作区的 24 个新增文件及两个 CLI 脚本入口整合到当前工作区。原分支上的功能尚未提交,因此此次是文件整合,不是已完成 Git 分支合并。原美亚工作区与主目录的工作区状态及未提交文件哈希前后相同。
初次整合时,适配器运行代码与原工作区逐文件一致;后续本工作区增加影像内分析快照,见末节。继续独立运行,不注册到主应用,不启动常驻采集;保持
`image_record + diagnosis_record(code_source=image_ai)`
,不新增
`image_analysis_record`
,不改摄入标准、画像或召回规则。
## 完整链路
新增验收在既有
`meyer-imaging-postgres.spec.ts`
中,使用本地 HTTP 美亚模拟响应、真实 CLI、真实 PostgreSQL、真实应用鉴权和查询控制器。端口随机分配,数据库和角色为一次性
`pac_meyer_ci_*`
,连接数限制为 2,结束后清理;不连接测试或生产环境,不使用真实应用密钥。
| 验证 | 结果 |
| --- | --- |
| 组织映射 | 从既有 Friday 业务记录自动解析诊所与品牌;冲突拒绝继续 |
| 采集与标准化 | 美亚鉴权、影像查询、病症查询 → 私有留底 → 标准解析与事实落库 |
| 数据产出 | 1 条影像 + 2 条影像来源诊断,无独立分析报告 |
| 开放鉴权 | appId/appSecret 换应用令牌成功;缺令牌查询被拒绝 |
| 开放返回 | 病历号查询返回匹配患者,公开字段通过响应 schema 校验 |
| 影像关联 | diagnosis.content.sourceImageExternalId 对应 image.content.externalId |
| 编码与时间 | 龋齿为 K02,可疑龋保留空码;诊断取检查时间,影像取影像创建时间,均为 UTC |
| 出站范围 | 不返回私有供应商原始响应、OSS ID 或临时签名地址(后续允许标准化的分析结论与描述,见末节) |
| 隔离 | 同宿主不同品牌的同号患者分开返回,未采集品牌无事实;另一宿主令牌查不到本宿主数据 |
| 重复运行 | 增量重复及固定窗口重放均不新增事实,API 返回保持一致 |
| 副作用 | 画像与召回记录数为 0 |
本次源端使用可重复的模拟响应,未再次调用真实美亚接口;此前真实联调记录见原美亚验收文档,不能视为本次实测。
## 验证结果
-
10 套、148 项适配器与开放接口回归通过。
-
3 套、118 项数据库完整链路、既有分析解析和源时区回归通过。
-
服务源码与测试 TypeScript、文档站类型检查通过。
-
单页 API 文档保留 11 类事实,在诊断部分补充影像分析读取说明及关联示例;示例通过真实映射、解析、返回层校验。
-
主目录和美亚工作区未变;未重启业务服务;独立文档站使用 3112。最终页面验证时,原文档预览进程达到 8 GB 堆限制退出,尝试 3 GB Webpack 预览仍内存不足,已保留原缓存到 /tmp 并恢复 Turbopack,堆上限 4 GB;仅调整当前工作区启动命令,未修改项目运行配置。
首轮发现新测试的可空类型声明问题,已显式处理;静态源样本第三轮超出增量窗口被正确拒绝,重放验收改为同一固定窗口,不修改采集器的窗口检查。
最终 3112 页面已恢复 HTTP 200,实际 HTML 核对为 11 类事实,含新增影像分析关联示例。
## 本次补充:影像 content.analysis
本工作区新增通用
`ImageEmbeddedAnalysisSchema`
,由影像 canonical 与事实 content 共用。新采集的美亚影像增加可选
`analysis`
:
-
`source`
:分析来源,美亚为
`meyer`
。
-
`summary / description`
:来源综合结论、描述原文。
-
`analyzedAt`
:真实分析完成时刻,统一 UTC;美亚没有提供,留 null。
-
`findings[]`
:
`name / toothPosition / code`
,复用原有去重及诊断码映射;未知码保留 null。
原有
`diagnosis_record`
的身份、代码映射、时间、落库路径不变;不生成新的
`image_analysis_record`
。影像分析与关联诊断表达同一批病症,不重复计数。
`finding`
原字段保留,不被分析结论替换。未声明的供应商扩展字段在分析标准化时移除。
### 5i5ya 边界
共用同一可选结构,但现有
`image_finding.yaml`
只有
`emr_id`
病历级关联,没有可靠的单张影像标识;
`image.yaml`
也不输出 analysis。本次不根据患者或病历猜配到某张影像,不改 5i5ya 映射。缺失/null 不代表源端没有分析数据。
### 兼容性与验证
-
旧影像缺失 analysis 时不增加默认字段;旧 bundle 按原快照重试,已应用快照仍跳过。本次没有历史补写或状态清理。
-
152 项适配器与开放接口回归通过,覆盖标准结构、未知码、去重顺序、旧快照、文档完整性和示例。
-
119 项隔离数据库及既有分析/时区回归通过:本地模拟美亚 HTTP → 真实 CLI → 真实 PostgreSQL → 应用令牌 → 开放患者查询。
-
实测 1 条带 analysis 的影像 + 2 条既有诊断;数据库和 HTTP 返回 analysis 相同;重复采集不增版本;跨品牌和跨宿主隔离仍成立;画像及召回记录数为 0。
-
服务源码与测试类型检查、共享类型构建、文档站类型检查通过。首次检查发现新测试把 JSON 联合类型直接当对象读取,已改成对象断言后复查通过。
本次未调用真实美亚接口;未部署、未提交,未修改主目录和原美亚工作区,未重启业务服务。临时数据库与角色已清理;文档仍在当前工作区 3112 端口。
最终收尾:影像与文档相关的 34 项用例再次通过(包含 UTC 时间归一化);3112 文档预览热更新触及 4 GB 堆上限退出后,仅恢复当前工作区的文档进程。页面重新返回 HTTP 200,实际 HTML 包含 analysis 字段表、完整嵌套示例和避免重复计数说明。业务服务未重启。
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-19-open-patient-facts-test.md
0 → 100644
View file @
70f95af3
# 开放患者查询:测试环境验收
> 历史归档(2026-10-22):本文记录当时的实现、审核或验证情况,正文中的“未提交/未部署”等状态仅指原记录日期;不代表当前版本或运行状态。现行说明见本目录索引。
日期:2026-10-19。仅测试环境发布,生产未操作。
## 版本与部署
-
功能提交:
`7bdc1a5`
,
`codex/open-patient-facts`
。
-
测试合并:
`1e0c92f`
,本地与远端
`test`
已同步。
-
`main`
保持
`b7901c9`
;未合入 main,未访问生产服务器。
-
测试机
`/root/pac`
的后端、工作台、文档站已部署同一版本。
-
迁移
`20261019170000_open_patient_facts`
已执行,全部迁移完成;运行镜像与构建镜像一致,
`/health`
正常,工作台与文档站 HTTP 200。
-
回退:测试机
`/root/pac-release-backups/20261019-open-patient-facts/`
,包含旧版本、迁移前结构及部署日志;旧镜像以
`rollback-open-facts-20261019`
标签保留。新增列和索引不需回删即可回退旧代码。
-
发布前没有 running 摄入任务或数据库长查询。主目录未提交文件及本地业务服务未改动;独立文档站仍使用 3112。
## 本地复核
9 套、128 项测试通过(含真实 PostgreSQL + HTTP 8 项);源码/测试类型检查、文档站类型检查与文档示例校验通过。修改文件 ESLint 无错误,27 项既有警告已在本地验收文档说明。
测试合并无冲突。合并结果与功能分支在本次后端、共享类型、迁移代码上一致;test 中原有 FridayAI 测试改动保留,未发布 FridayAI。
## FridaySaaS 真实应用联调
使用既有测试环境 FridaySaaS 的 appId/appSecret,无新增应用、无密钥轮换,密钥与令牌不记入本文。
-
公网
`https://pac.jarvismedical.asia/pac/v1/auth/token`
:未授权时返回
`10107`
,验证默认关闭。
-
仅对测试 Friday 宿主执行
`grant-facts-read friday`
;
`friday=true`
、
`jvs-dw=false`
。
-
开通后返回
`code=0`
、Bearer 令牌、有效期 3600 秒,不含登录 code/refreshToken。
-
错误密钥
`10101`
;空/null user
`10002`
;无令牌、篡改令牌
`10106`
;空查询和未知参数
`10002`
。
-
无匹配返回
`items: []`
;查询另一个宿主的独有病历号返回空数组。
-
原 user 换票正常,仍返回 code/accessToken/refreshToken;用户令牌调用新查询返回
`10106`
,应用令牌调用工作台患者时间轴也返回
`10106`
。
## 真实患者与数据库核对
| 测试病历号 | API 事实 / 库内当前有效事实 | 画像 | 可解析关系人 | 服务内查询耗时 |
| --- | --- | --- | --- | --- |
|
`PE0A000868`
| 121 / 121 | 有 | 6 | 44 ms |
|
`PE0A000832`
| 43 / 43 | 有 | 1 | 39 ms |
|
`PE0A002965`
| 34 / 34 | 有 | 1 | 40 ms |
逐条核对事实主体与类型集合,并检查 PAC/宿主患者 ID、关系人范围、手机号掩码、profile 已合入 patient、输出结构以及多条件交集查询。
样本覆盖预约、接诊、病历、诊断、治疗、影像 6 类事实。其他类型由本地 17 类内容示例覆盖,不能据此宣称测试宿主已有全部类型。
公网测量(含本机网络耗时):取令牌 1218 ms;病历
`PE0A000832`
查询 1307 ms、43 条事实;
`PE0A000868`
查询 1693 ms、121 条事实,均返回画像。服务内上述耗时为本次样本测量,不代表并发压测或 SLA。
## 文档与边界
文档站 API spec 的 servers 已核对为
`https://pac.jarvismedical.asia`
,新参考页面标题已核对为“查询患者资料与事实”。
本接口是宿主全范围只读能力,实际使用者权限由宿主后端检查;未知数据保持空值,画像读取已有版本,不触发重算。本次没有回放摄入、重算画像或召回。
验收脚本最初以不存在的
`/auth/me`
作工作台拒绝探针,得到
`10004`
;改用实际
`/patients/:id/timeline`
后验证通过。这是验收路径修正,未修改业务代码。
授权 CLI 在退出时打印了组织预热取消警告;授权已成功,后续实际用户换票、患者查询与服务健康均验证正常。
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-20-fact-content-field-contract-review.md
0 → 100644
View file @
70f95af3
This source diff could not be displayed because it is too large. You can
view the blob
instead.
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-20-meyer-open-facts-test.md
0 → 100644
View file @
70f95af3
# 开放接口精简与美亚分析:测试环境验收
> 历史归档(2026-10-22):本文记录当时的实现、审核或验证情况,正文中的“未提交/未部署”等状态仅指原记录日期;不代表当前版本或运行状态。现行说明见本目录索引。
2026-10-20(发布跨过午夜)。仅测试环境发布;生产未访问、未部署。
## 版本与部署
-
功能提交:
`355a755`
(
`codex/open-patient-facts`
)。
-
test 合并:
`6c3f5f8`
;本地、远端及测试机一致。main 保持
`b7901c9`
。
-
后端、工作台、文档站均发布成功,运行镜像与构建镜像一致,容器重启次数均为 0。
-
`/health`
正文 status=ok;工作台与文档站可访问;58 个迁移全部完成,本次无待执行迁移。
-
发布前没有 running 摄入任务或超过 30 秒的活动数据库查询。
-
回退资料:测试机
`/root/pac-release-backups/20261019-meyer-open-refinement/`
,含旧 HEAD、旧镜像记录和部署日志。三个旧镜像均保留
`rollback-meyer-open-20261019`
标签。
## 验证范围
发布前新增复核:16 套 278 项回归及隔离 PostgreSQL + HTTP 的 10 项验收通过。首次复用旧临时库时发现它已被先前验收删除,重新创建一次性隔离库后通过并清理,未使用开发库代替。上一轮影像完整链路的 119 项回归、源码/测试类型检查、共享类型构建与文档类型检查也已通过。
合并没有冲突;test 与功能分支在本次 service/web/types/docs 范围完全一致。主目录未提交文件、原美亚工作区和本地业务服务保持原状;仅移除了此次创建的干净临时合并工作区,两个功能工作区均保留。
## 真实美亚采集
沿用现有美亚测试读取配置;从测试 PAC 既有宿主记录自动解析诊所、租户与品牌,核对患者归属后才写入。没有人工创建患者或修改组织映射。
-
样本病历号:
`F001000001`
(此前已核实的美亚测试患者)。
-
限定窗口:北京时间
`2026-10-16 15:51:31–15:51:33`
,仅 1 家诊所。
-
预览:2 张影像,1 张取得有效分析,1 张分析为空;failed=0。
-
应用:新增 1 条
`image_record`
与 11 条
`diagnosis_record`
。
-
`image_record.content.analysis`
含来源、综合结论、描述及 11 条 findings;analyzedAt=null,不伪造完成时间。
-
findings 与关联诊断逐条匹配;数据库 analysis 与开放接口返回完全一致。
-
重放:applied=0、alreadyApplied=1、deferred=1、failed=0;12 条事实均仍为 version=1,没有重复写入。
CLI 退出码 2 表示仍有待处理影像,不是本次落库失败。空分析影像保留在 pending 中,没有作为“分析正常”写入。本次只跑限定范围的手动验收,未启动 watch、全量扫描或常驻采集。采集状态与凭据位于测试机
`/root/pac-meyer-test/`
,目录权限 700、文件仅 root 可读;后续补拉须继续使用该状态目录,不得清空强制重跑。
没有改写既有诊断规则、历史事实或手动重算画像/召回。5i5ya 仍因缺乏单张影像可靠关联而不填 analysis,现有诊断摄入保持原样。
## 真实 FridaySaaS 鉴权与查询
使用用户此前提供的现有测试 appId/appSecret;未新建应用、未轮换密钥、未扩大权限。临时本地凭据文件验证后删除,密钥/令牌不记入本文。
通过 17 项鉴权/查询检查:正确凭据、错误密钥、空/null user、缺失/伪造令牌、空/未知参数、无匹配、组合精确查询、宿主隔离及原 user 换票。用户令牌不能调用应用查询,应用令牌不能访问工作台患者时间轴。FridayAI/HMAC 兼容性由本地集成回归验证,本次未部署 FridayAI。
四位患者逐条比对事实版本 ID 与类型集合,并核对关系人归属、患者 PAC/宿主 ID、手机号脱敏和精简后的字段;persona 为标签数组,已排除的内部属性不返回。
| 病历号 | API / 库内有效事实 | 关系数 | 本机回环 HTTP 查询耗时 |
| --- | --- | --- | --- |
|
`F001000001`
| 60 / 60 | 0 | 125 ms |
|
`PE0A000868`
| 121 / 121 | 6 | 411 ms |
|
`PE0A000832`
| 43 / 43 | 1 | 157 ms |
|
`PE0A002965`
| 34 / 34 | 1 | 97 ms |
公网取令牌成功,3 个样本查询均成功(约 0.8–1.0 秒,含本机网络;只是本次样本,不代表并发 SLA)。
`F001000001`
的公网响应也含美亚 analysis。
## 文档
[
测试环境 API 参考
](
https://pac.jarvismedical.asia/docs/api/open/PatientFactsLookupController_lookup
)
已更新。实际页面核对 analysis 字段表和嵌套示例;两个旧 integration 链接均返回 308 到单页 API 参考。OpenAPI Server URL 为
`https://pac.jarvismedical.asia`
。
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-21-open-content-source-time-test.md
0 → 100644
View file @
70f95af3
# 开放患者查询与来源时间:测试环境验收
> 历史归档(2026-10-22):本文记录当时的实现、审核或验证情况,正文中的“未提交/未部署”等状态仅指原记录日期;不代表当前版本或运行状态。现行说明见本目录索引。
范围:仅测试。生产未访问、未部署;历史数据未回填。用户追加确认将工作区中的来源创建时间、退费更新时间和时区校验一起提测。
## 发布范围
-
功能分支
`codex/open-patient-facts`
:
`b502e1b`
。
-
test 合并:
`5b80c04`
。保留 test 原有美亚批量分析、患者归属校验和文件引用能力;合并处增加
`fileRefs`
的嵌套白名单,未把 test 合回 main。
-
开放查询:字段及嵌套扩展按白名单;关联仅使用本事实绑定且宿主/租户/患者/诊所一致的来源证据;病历 11 个段落统一成可读文本。
-
真实旧病历补充兼容:5i5ya 牙位编辑器的象限选择与可读牙位一致时去除重复状态;序列化备份不是正文或牙位来源,不据其恢复已取消选择。未知正文结构仍明确失败。
-
来源时间:新增可空
`source_created_at`
,只在正常新事实版本写入;来源记录的时间不冒充诊断动作时间。保留
`source_updated_at`
;修正退费投影丢失 updated_date;增加归一后时间校验和影像分析嵌套时间归一。
-
没有新增 source_created_at_basis。原有 received_at 保持。
## 本地验证
-
功能工作区:开放接口 216 项回归、时间相关 210 项回归通过;发现真实旧病历格式后补了兼容回归。
-
合并结果:20 套单元测试共 453 项最终通过。初跑 449 项通过,4 项美亚持久化/恢复用例触发本机磁盘同步时限;延长测试运行时限后,200 张持久化用例与恢复套件均通过。业务代码和提交中的测试时限没有因此修改。
-
合并结果隔离 PostgreSQL:来源时间 + 开放查询 13 项、美亚采集落库与开放查询 16 项通过;旧开放鉴权 HTTP 验收 10 项通过。
-
另补文件引用嵌套白名单测试;合并后的 content/单页文档 30 项通过。服务源码及测试、文档类型检查通过。
-
发布前测试库只读预检:两宿主 630 条早期/近期事实全部转换成功;35 位真实患者的 2,693 条当前有效事实全部转换成功。抽样不等于全库审计。
## 部署与线上验证
后端、工作台和文档站已发布。最终核验测试机 test 分支为
`5b80c04`
、工作树干净,三个容器均 running、重启计数为 0,运行镜像与构建镜像一致;后端健康检查 status=ok。来源创建时间迁移完成且未回滚,
`source_created_at`
为可空 timestamptz。发布后最近 20 分钟日志检查未发现 Invalid canonical time、open_fact_content_normalization_failed、schema-violation 或
[
drift
]
;这不代表全量历史无异常。
| 宿主 | 患者 | 当前有效事实 | 验证入口 | 单次耗时(测试机回环) |
| --- | ---: | ---: | --- | --- |
| FridaySaaS | 16 | 551 | HTTP 应用鉴权 + 查询 | 89–977 ms |
| 5i5ya | 23 | 2,310 | 同一查询服务 | 29–2,739 ms |
共 39 位患者、2,861 条事实;逐位比对数据库当前有效版本 ID 集合,全部一致。返回字段通过严格 schema,病历 11 段均为文本或 null。5i5ya 覆盖 11 类(含 5 条有效投诉),FridaySaaS 覆盖 10 类(尚未接投诉)。
关联核验:5i5ya 影像 213 条、诊断 268 条、治疗 464 条、建议 8 条保留有证据的父病历标识,未冒充接诊。FridaySaaS 48 条病历有明确接诊关联、13 条退费有来源付款关联、33 条影像诊断有影像关联。无法确认的引用保持 null。
应用鉴权使用测试服务内现有签名配置生成短期、绑定现有宿主的验收令牌,没有新建/轮换应用密钥;本轮不声称重新验证了外部 appSecret 换票。5i5ya 权限仍关闭,HTTP 正确返回 10107;内容验证不等于已开通它的外部调用权限。
## 来源时间真实原文验证
-
直接只读查询 5i5ya 数仓的 3 条真实退费,完整经过退费 project、assembler、事件生成、parser 和事实写入,sourceCreatedAt/sourceUpdatedAt 均与原文按北京时间转 UTC 的预期一致。
-
使用 PAC 留存的 3 条 FridaySaaS 病历原始事件,经过完整 manifest transforms(含同源行关联)、assembler 和事实写入,创建/更新时间与业务就诊时间分别验证;没有把录入时间冒充就诊时间。
-
写入验证使用一次性验证业务标识和同一数据库事务,核对完成后主动回滚;随后确认验证事实不存在。没有改写原事实、改源记录、触发画像召回或留下假患者。
-
验收脚本首次事务 15 秒超时,自动回滚;改用较长验收时限后通过。Friday 脚本补齐完整 transforms 和空时间断言后通过;这些是临时验收脚本修正,未修改已发布业务版本。
-
此项证明
**当前代码忠实转换原文并写入来源字段**
,不证明旧导出原文本身的时间正确,也不消除历史错误水位。旧/新原文有 8 小时表达差异的病历仍需单独甄别。
-
公网文档 HTTP 200,已核对 clinicExternalId、fileLocation、fileRefs、illnessDescription 与规范化错误说明;旧内容文档链接 308 跳转到单页 API 参考。
## 边界
-
测试库 FridaySaaS 已开通应用查询,5i5ya 未开通;不为验证扩大权限。FridaySaaS 走真实 HTTP 和应用令牌鉴权;5i5ya 直接调用相同查询服务验证内容,同时核验 HTTP 权限拒绝。
-
新代码不会自动纠正历史 8 小时时差或旧判新水位。仅重复摄入相同内容通常不会生成新版本,也不会补写 source_created_at;历史修复需要另行限定范围。
-
回退资料:测试机
`/root/pac-release-backups/20261021-open-contract-time/`
,旧镜像保留
`rollback-open-contract-20261021`
标签。新增可空列向后兼容旧服务。
docs/_archive/2026-10-meyer-and-patient-facts/2026-10-21-source-time-eight-hour-readonly-audit.md
0 → 100644
View file @
70f95af3
# 来源时间 8 小时差异:只读核查
> 历史归档(2026-10-22):本文记录当时的实现、审核或验证情况,正文中的“未提交/未部署”等状态仅指原记录日期;不代表当前版本或运行状态。现行说明见本目录索引。
核查日期:2026-10-21。范围:PAC 测试、生产的运行代码、事实/事件/同步批次、近期容器日志,以及本地 Git 历史。未修改业务代码、数据库或部署;上轮来源创建时间的本地改动保持原状。
## 结论
**主要是历史摄入的字段漏转换,当前转换逻辑已修复,但旧事实的错误时间仍存在;近期正常摄入也会遇到这些旧时间水位而被跳过。**
另外,旧病历业务时间及 Friday 旧 Mongo 导出分别有独立问题,不能把所有时间统一减 8 小时。
| 问题 | 原因与现状 | 影响 |
| --- | --- | --- |
| 多类型
`source_updated_at`
晚 8 小时 | 旧 normalizer 只转换类型登记的字段;许多类型没登记
`updatedAt`
。北京时间裸字符串被 Node UTC 进程直接解析。
`1850094`
(10/16)已补通用转换,两环境运行代码均验证正确 | 历史判新水位偏大,正常重放或较近的源修改可能被判为旧数据 |
| 5i5ya 旧病历
`occurred_at`
晚 8 小时 | 病历事件使用
`createdAt`
,早期它也漏了归一;病历 parser 又沿用事件时间。
`568a456`
(10/8)已把病历
`createdAt`
加入归一字段 | 时间轴、病历排序、末诊/医生关联、使用病历时间的召回判断可能受影响;不是只修更新水位就能解决 |
| Friday 旧病历的部分源字符串提前 8 小时 | 旧
`export.sh`
把 Mongo
`toISOString()`
的 Z 截掉,又按北京时间解释。
`fa9ba3b`
(10/15)已改为保留 Z | 与上面“漏转换”方向不同;历史病历必须按原始来源区分,不能一刀切改时间 |
当前代码修复不会自动改写旧事实。相同内容通常不会生成新版本,错误更新水位还可能让纠正后的输入先被乱序保护挡住。
## 1. 从原文到事实的实际链路
```
text
5i5ya:ClickHouse JSONEachRow → 原表行 → manifest/transforms → assembler
Friday:推送原表行/历史文件导出 → manifest/transforms → assembler
↓
normalizeCanonical(manifest: Asia/Shanghai;保留已有 Z/offset)
↓
TransactionSynthesizer(事件业务时间 + 含更新时间标记的幂等键)
↓
ParserPipeline(canonical.updatedAt → Date)
↓
FactWriter(与已有 source_updated_at 比较 → 写新版本/追加证据/stale skip)
```
-
原始时间保存在
`patient_transactions.raw_payload`
,归一后的判新时间在
`patient_facts.source_updated_at`
。
-
两环境 Node 进程均为 UTC;数据库会话时区测试为 UTC、生产为 Asia/Shanghai。字段是
`timestamptz`
,本次按真实时刻比较,不把显示时区差异当作数据偏差。
-
旧版预约已经登记
`createdAt/updatedAt`
,所以其旧样本正常;病历、接诊、诊断、治疗、影像、收退费等并未统一登记。这解释了为什么同一个宿主有的类型正确、有的类型错误。
-
5i5ya 咨询的
`updatedAt`
来自
`appointment_date`
,是判新代理时间,不是原表修改时间;它也受旧版漏转换影响;5i5ya 影像病症拆出的诊断用
`rq`
作代理判新时间,同样需要区分。投诉则使用主单及跟进聚合的快照时间,应按自己的口径比较。
### 实际例子(全部转为 UTC 比较)
| 环境/记录 | 原始值与正确解释 | 事实存值 | 结论 |
| --- | --- | --- | --- |
| 生产 Friday 旧退费 |
`updated_gmt_at=2021-07-28 14:30:05`
→
`06:30:05Z`
|
`14:30:05Z`
| 更新时间晚 8 小时;同记录退款业务时间
`14:29:49`
→
`06:29:49Z`
正确 |
| 生产 Friday 新收款 |
`updated_gmt_at=2026-10-21 18:46:13`
→
`10:46:13Z`
|
`10:46:13Z`
| 当前转换正确 |
| 生产 5i5ya 旧病历 |
`created_date=2016-05-01 12:00:32`
→
`04:00:32Z`
| 业务时间为
`12:00:32Z`
| 业务时间也晚 8 小时;该事实在 8 月写入 |
| 测试 Friday 同一病历的前后证据 | 9/28 收到创建/更新时间裸串为
`02:51:56 / 02:55:08`
,10/16 对应串变为
`10:51:56 / 10:55:08`
| 旧业务时间
`9/21 18:14Z`
,新版本
`9/22 02:14Z`
| 原文自身也存在 8 小时表达变化,不能仅根据最新原文统一修整个版本链 |
第三类的旧导出代码缺陷已由 Git 历史确认;上述前后证据都已在 PAC 核实。
**本轮未访问 Friday 源数据库或其连接器部署,不能认定每一条旧 push 都由这个导出脚本产生,也不能仅靠原文变化判断源库是否修改过记录。**
## 2. 两环境样本范围
按宿主、类型的
`occurred_at`
升/降序各取 30 个事实版本,按 ID 去重;类型包含咨询、接诊、诊断、治疗、建议、病历、影像、预约、收款、退费、投诉。测试 1,260 个版本、生产 1,220 个版本。读取关联原始事件,再追查 26 个选定主体的版本链和同步批次。
这不是随机抽样,也不是全库填充率:包括历史/现行状态,排序还可能包含空业务时间;不能从比例外推全库数量。查询均限时、只读,未跑全库聚合。
下表是
**源 updated 字段按北京时间解释后,事实水位恰好晚 8 小时的样本数**
;不包含缺值、其他差异或咨询的代理时间。
| 宿主/类型 | 测试 | 生产 |
| --- | ---: | ---: |
| 5i5ya 接诊 | 30 | 30 |
| 5i5ya 诊断 | 30 | 30 |
| 5i5ya 治疗 | 30 | 30 |
| 5i5ya 医生建议 | 0 | 28 |
| 5i5ya 病历 | 30 | 30 |
| 5i5ya 影像 | 30 | 30 |
| 5i5ya 收款 | 30 | 30 |
| Friday 咨询 | 6 | 2 |
| Friday 接诊 | 19 | 0 |
| Friday 诊断 | 2 | 0 |
| Friday 医生建议 | 24 | 15 |
| Friday 病历 | 4 | 0 |
| Friday 老影像 | 30 | 0 |
| Friday 收款 | 60 | 30 |
| Friday 退费 | 60 | 60 |
|
**合计**
|
**385**
|
**315**
|
-
这些样本中 active/fulfilled 状态分别为 344/288 个;选定主体的版本链也确认了有旧偏差仍在最新有效版本。其余是已 superseded 的历史版本,不能一起算成当前业务影响。
-
5i5ya 咨询另有测试/生产各 50 个代理时间水位晚 8 小时;没有真实 updated 字段,故未混入上表。
-
两边 5i5ya 旧病历各 30 个样本的业务时间也晚 8 小时,其中按北京时间显示跨到次日的分别有 14/12 个。生产这 30 个中 27 个 active、3 个 superseded。
-
5i5ya 预约样本两边各 60 个更新时间均正确;退费各 60 个缺失更新时间,属于上轮已修但未部署的选列遗漏,不能归类为时差。
-
今天新摄入的 5i5ya 接诊、诊断、病历、影像、收款等样本,以及生产 Friday 新接诊、病历、收款样本,更新时间均正确。旧偏差样本大多写入于 7—10 月早期,不是今天的归一函数仍普遍出错。
-
Friday 治疗等类型缺少有效 updated 值的样本不能据此判为正确;某类型本次为 0 也不代表全库无问题。
## 3. 当前跳过行为及业务影响
近期各取最多 5,000 行容器日志,测试覆盖
`12:29:01Z—13:03:10Z`
,生产覆盖
`12:28:33Z—13:12:27Z`
:
| 环境 |
`stale skip`
日志 | 其中 incoming 比 latest 早恰好 8 小时 | 类型 |
| --- | ---: | ---: | --- |
| 测试 | 4,487 | 4,487 | 咨询 3,946;诊断 541 |
| 生产 | 4,787 | 4,787 | 咨询 3,832;诊断 955 |
这是有限日志窗口内的
**跳过次数**
,不是唯一患者/事实数量,更不是丢失业务变更条数。窗口里没有
`fact write failed`
或
`bulk write failed`
,而
`stale skip`
是业务保护分支,不会等同写入异常。
进一步按日志反查了测试/生产各 4 个主体(各 2 个咨询、2 个诊断):
-
新旧事件都能关联到同一宿主、患者与业务主体;旧事实仍保存较晚的水位,新事件按当前正确时间进入后未追加到该事实的证据列表。
-
这 8 个主体的前后事件各自
`payload_hash`
相同,原文未变化。结合确切
`incoming < latest`
日志,证明存在历史水位造成的重放跳过;
**这些样例本身不是新业务内容丢失的证据**
。
-
生产咨询示例:来源日期
`2025-04-07`
,旧水位
`2025-04-07T00:00:00Z`
;当前正确输入为
`2025-04-06T16:00:00Z`
,被判为更旧。这里水位来自预约日期代理,不能解释成咨询记录真实修改时刻。
-
日志诊断样例的来源字段是
`rq`
,需按影像病症分析日的代理口径理解,不能当作原表 updated 字段。因此它们不应混入上表“原始 updated 字段”的统计。
影响需分开解释:
1.
**更新时间水位**
:本身不直接参与画像召回计算,但会决定后续事实是否可写入。相同源版本按正确时区重放会比旧错误水位早 8 小时;真正源修改发生在原修改时间之后不足 8 小时,也可能被挡住。若源更新时间已经超过该旧水位,正常新更新仍可写入,并非所有未来更新都被阻塞。
2.
**业务发生时间**
:直接被时间轴、开放事实接口、末诊/医生关联,以及召回的就诊冷却条件使用。旧病历发生时间错误可能改变排序或跨日、边界判断;本轮未计算受影响患者的画像/召回结果,不能把时间偏差数等同误召数量。
3.
**源时间原文变化**
:Friday 旧 Mongo 路线既有 UTC 标记丢失,又叠加 PAC 更新时间漏转换,部分数值可能偶然抵消。对它们套同一减 8 小时脚本会制造新的错误。
4.
**幂等与可观测性**
:事件幂等键包含更新时间文本;归一后带 offset 的文本可能形成新的事件键。但新事件已经留账,不代表事实必然更新,仍要检查写入结果。当前
`factsStaleSkipped`
在管道内计数,常规批次汇总未单独保留这个细分指标,需结合日志判断。
## 4. 后续处理边界(本轮不执行)
-
对水位问题,先按宿主、患者、来源组织、业务主体和版本绑定证据验证;仅将能证明是旧解析结果的水位列为候选,不把代理值、聚合时间、未知/冲突证据混改。仓库已有定向修复工具,但本轮未运行它,包括未跑其 dry-run。
-
对旧病历业务时间,单独评估事实版本、画像及召回依赖;不能夹在更新水位修复中顺便改。
-
对 Friday 旧 Mongo 数据,需要保留来源路径、原始表达和前后版本,必要时对照源系统的真实 UTC 瞬间,不能再用营业时间分布推断时区。
-
当前代码已经修复的路径无需重复改同一转换逻辑。先界定历史纠正范围与验证方式,再决定如何执行。
证据位置:
`field-mapper.ts`
通用时间归一、
`canonical.ts`
各资源时间元数据、
`transaction-synthesizer.ts`
事件键/时间、
`parser-pipeline.service.ts`
更新时间解析、
`fact-writer.service.ts`
单条/批量乱序闸;
`emr.parser.ts`
、
`persona.service.ts`
、
`plan-engine.service.ts`
、
`treatment-initiation-recall.scenario.ts`
、
`lookup-fact.ts`
的业务时间消费。运行状态重新核实:测试仓库
`d383418`
、服务启动 10/21;生产仓库
`d3f517b`
、服务启动 10/18。结论同时核对了运行容器的实际 normalizer,未只凭仓库 HEAD 判断。
## 5. 代码约束补齐(2026-10-21 后续本地修改,未部署)
上文保留只读核查结果。本节记录用户随后授权的代码修复,不表示线上历史数据已纠正。
已有统一入口,但原先有两个缺口:逐资源的时间清单可能漏字段;下游 datetime 契约只是非空字符串,JavaScript 又能按进程时区解析裸串。只核对宿主配置和部分正常样本不足以证明整条链路正确。
-
保留已经修复的通用 createdAt/updatedAt 归一与 Mongo 导出 UTC 标记;不重复改转换方向。
-
canonical 时间契约改为有效、带时区的 ISO 时刻;透传的 createdAt/updatedAt 也校验。源值为字符串 null 时按缺失处理,必填业务时刻仍不能为空。
-
合成流水及解析来源更新时间共用严格读取函数。绕过入口的裸时间/非法时间不再按进程时区猜测,也不静默降为无更新时间。流水合成拒收单条;解析器批量路径记录失败,不写入该条事实。
-
未支持的宿主时区明确报错,不再默认 UTC。当前宿主仍为 Asia/Shanghai;纯日期字段保持原契约。
-
从各资源 schema 自动检查全部 datetime 字段是否列入归一,同时检查全部 Friday/5i5ya assembler 的事件时间与来源创建时间配置。本次由检查发现并补齐
`image.analysis.analyzedAt`
的嵌套归一;原先该字段已有严格校验,裸串会被拒绝,不能称为已证实的时差存量。
-
不改历史事实、事件原文、乱序比较规则,不自动接受较旧版本。旧错误水位造成的跳过仍需另行处理。
本地验证:UTC 下 472 项相关回归通过;北京时间进程下 188 项核心时间回归通过;独立临时 PostgreSQL 中 12 项落库/影像链路测试通过,临时库已删除;服务及测试类型检查通过。回归覆盖保留源原文、显式时区不重复转换、纯日期边界、非法时间拒收、单条/批量写入和来源时间留空。未提交、未部署,未改主目录或现有服务。
docs/_archive/2026-10-meyer-and-patient-facts/README.md
0 → 100644
View file @
70f95af3
# 美亚与患者事实接口:开发历史归档
归档日期:2026-10-22。来源:
`meyer-imaging-adapter`
、
`open-patient-facts`
两个旧工作区。重复文档只保留一份。
## 现行入口
-
[
美亚采集器运行说明
](
../../../apps/pac-service/src/integrations/meyer/README.md
)
-
[
患者事实查询 API
](
../../../apps/pac-docs/content/docs/api/open/PatientFactsLookupController_lookup.mdx
)
-
代码收尾版本:main
`964fdd2`
、test
`2b926b1`
(归档前);两分支的已提交文件内容一致。
-
收尾验证:真实文件及目录同步测试 36 项、接口回归 289 项、权限回归 40 项通过;PAC、FridayAI 后端类型检查通过。
## 保留记录
| 文档 | 留存价值 |
| --- | --- |
|
[
2026-10-17-meyer-imaging-adapter-local.md
](
2026-10-17-meyer-imaging-adapter-local.md
)
| 初期适配器实现、联调、断点恢复及部署准备记录 |
|
[
2026-10-19-meyer-diagnosis-mapping.md
](
2026-10-19-meyer-diagnosis-mapping.md
)
| 影像所见到 PAC 诊断码的映射依据与边界 |
|
[
2026-10-19-meyer-open-facts-local.md
](
2026-10-19-meyer-open-facts-local.md
)
| 适配器与开放查询整合、真实 PostgreSQL 验收记录 |
|
[
2026-10-19-open-patient-facts-test.md
](
2026-10-19-open-patient-facts-test.md
)
| 首轮测试环境发布、回退位置与联调记录 |
|
[
2026-10-20-fact-content-field-contract-review.md
](
2026-10-20-fact-content-field-contract-review.md
)
| 17 类患者事实字段、命名、关联、时间和金额的审核过程 |
|
[
2026-10-20-meyer-open-facts-test.md
](
2026-10-20-meyer-open-facts-test.md
)
| 影像分析与开放接口精简的测试环境验收 |
|
[
2026-10-21-open-content-source-time-test.md
](
2026-10-21-open-content-source-time-test.md
)
| 字段标准及源创建时间修复的测试环境验收 |
|
[
2026-10-21-source-time-eight-hour-readonly-audit.md
](
2026-10-21-source-time-eight-hour-readonly-audit.md
)
| 历史 8 小时差异的只读核查、影响与待评估边界 |
## 阅读边界
这些是开发过程与历史验收记录,不作为现行产品承诺。早期单张分析、分页上限、诊所配置、重试策略等描述已被后续实现替代;以现行代码和上面的正式接口入口为准。日期、部署状态、样本数量及备份路径均保留当时证据,不表示本轮重新核验了服务器。
历史时间水位修正、补推与回填建议继续保留为待评估事项,本次没有执行数据修复。诊断映射中的业务口径不等于临床确诊,现行映射实现已经纳入 main。
## 工作区清理核对
-
美亚旧工作区:23 个代码/配置/测试文件全部在 main 有对应实现;19 个文件与 main 当前内容或历史版本完全一致。其余 mapper、README、两份测试仅缺少 main 历史版本后续增加的分析快照与验收覆盖,没有发现旧工作区独有的实现。
-
患者事实接口旧工作区:没有未提交代码,只有上述 8 份文档;其中两份与美亚旧工作区的副本逐字一致。
-
两个工作区的提交均已进入 main 和 test。33 个未提交文件的原始内容及修改补丁另存可恢复备份,归档文档已重写相对链接并标明历史状态。
-
忽略项仅有依赖、编译产物和文档站生成缓存,不含额外运行凭据或采集状态。
恢复备份:
`/Users/luoqi/.codex/worktree-backups/pac-meyer-closeout-20261022/`
。目录中的
`manifest.json`
记录每个原文件和备份包的 SHA-256;两个
`.tar.gz`
保留原始未提交文件,
`*-tracked.patch`
保留已跟踪文件修改。
清理结果:两处旧工作区及对应本地分支已删除,远端
`codex/open-patient-facts`
已删除(另一分支原本无远端)。现仅保留主工作区与 main、test 两条分支;主目录原有未提交文件未变。归档文档纳入 main,并同步到 test;本次仅提交文档,不部署。
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment