Commit 5e9744c6 by luoqi

fix(recall): 外院已治疗(回访记录)患者级排除该诊断召回(一线反馈②)

李姝妤 BJ0U005102:回访 4.21"已在外院矫正中",召回正畸(诊断 1.26)误召。

口径(与产品对齐):
- 不影响已治疗事实/数据流 —— gap 算法只读 patient_return_visits(展示表),不写 fact;
- 患者级排除(不看牙位/类目);
- 时间方向:回访日(外院治疗时点)须 ≥ 本次诊断 → 新诊断更晚则不排(重新可召);
- 信号 = "外院" + 治疗词共现(EXTERNAL_TREATMENT_VISIT_PATTERNS:治疗/种植/矫正/矫治/
  修复/根管/已治/做了),排除"外院洁牙/检查"低值噪音。

gapWhere 加 externalTreatmentGate(NOT EXISTS 回访记录命中且 task_date≥诊断)。
验证:李姝妤正畸召回消失(42→41,零误杀);recompute exit=0。全库回访含外院 1812 患者。
仅本地,未部署。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
parent 4fbc08a7
import { Prisma } from '@prisma/client';
import {
EXTERNAL_TREATMENT_VISIT_PATTERNS,
NO_RESTORATION_GAP_EXAM_PATTERNS,
TREATMENT_REFUSAL_SUBTYPE_PATTERNS,
RESTORATION_INELIGIBLE_DX_NAMES,
......@@ -129,6 +130,17 @@ export function buildGapCore(input: GapCoreInput): GapCorePieces {
const afterDxFragRtx = afterDxFor('rtx');
const noRestorMsgRe = NO_RESTORATION_GAP_EXAM_PATTERNS.join('|');
const refusalRe = TREATMENT_REFUSAL_SUBTYPE_PATTERNS.join('|');
const externalTreatRe = EXTERNAL_TREATMENT_VISIT_PATTERNS.join('|');
/// 外院已治疗(回访记录)患者级排除:命中"外院"+治疗词 且 回访日 ≥ 本次诊断 → 排除。
/// 患者级(不看牙位/类目);时间方向同治疗排除(新诊断更晚 → 不排)。只读 patient_return_visits,
/// 不影响已治疗事实。一线反馈②(李姝妤 回访"已在外院矫正中")。
const externalTreatmentGate = Prisma.sql`AND NOT EXISTS (
SELECT 1 FROM patient_return_visits rv
WHERE rv.patient_id = p.id
AND (COALESCE(rv.result, '') || ' ' || COALESCE(rv.follow_content, '')) LIKE '%外院%'
AND (COALESCE(rv.result, '') || ' ' || COALESCE(rv.follow_content, '')) ~ ${Prisma.sql`${externalTreatRe}`}
AND rv.task_date >= COALESCE(sig.occurred_at, sig.planned_for)::date
)`;
/// 拒绝记录时间方向:planned 记录 occurred_at 为空 → 用 COALESCE(occurred_at, planned_for)
const refusalAfterDx = rule.excludeIfEverTreated
? Prisma.sql`AND COALESCE(rfx.occurred_at, rfx.planned_for) >= ${latestDxOfCode}`
......@@ -274,7 +286,8 @@ export function buildGapCore(input: GapCoreInput): GapCorePieces {
const restorationIneligibleFrag = Prisma.sql`AND COALESCE(sig.content->>'name_zh', '') <> ALL(${RESTORATION_INELIGIBLE_NAMES}::text[])`;
const gapWhere = Prisma.sql`AND (
const gapWhere = Prisma.sql`${externalTreatmentGate}
AND (
CASE WHEN ${wholeMouthFlag} THEN
NOT EXISTS (
SELECT 1 FROM patient_facts tx
......
......@@ -8,7 +8,7 @@
| # | 患者 | 一线反馈 → 核验结论 | 状态 |
| --- | -------------- | ------------------------------------------------------------------------------------------------------------------------ | ------ |
| ① | 李然 BA38586 | 47 已种植(1.25)+拆线(2.7)仍被召 → **误召:拆线归 review 不算治疗**;修复 1 后 47 召回消失,剩 16 牙体损伤/17;38 龋(真实待治,该召) | ✅ 已修 |
| ② | 李姝妤 BJ0U005102 | 外院矫正,4.21 转院 → **回访记录有提到外院**(4.21 常规回访,结果"已在外院矫正中",已入 PAC 回访表/历史联系可见,但召回未消费回访文本);客户动态数据与数仓核对中 | ⏸ 待定 |
| ② | 李姝妤 BJ0U005102 | 外院矫正,4.21 转院 → **误召:外院已治疗(回访记录)** — 回访 4.21"已在外院矫正中";召回正畸(诊断 1.26)。修复 6(外院已治疗患者级排除,回访日≥诊断)后消失 | ✅ 已修 |
| ③ | 秦溢泽 BJ0A057103 | 1.27 取资料未回复 → **召回正确**:1.27 EMR"今取正畸资料…约日复诊"+ planned 正畸/充填;2.23 回访"微信约看方案",此后无到诊 → 方案悬置,正是该召的人("取资料"归流程类不算治疗,口径正确) | ✅ 正确召回 |
| ④ | 祁小夏 BJ0U016979 | 31;41 缺,11.22 修复 → **误召:固定桥牙位盲点** — 31;41 由 32;33;42;43 基牙桥修复(11.15 备牙/11.22 戴桥),治疗牙位只写基牙不含桥体 → 重叠判定失败。修复 2(桥区间)后召回消失 | ✅ 已修 |
| ⑤ | 王敏 BJ0U017412 | 26;37;47 缺;"26 无间隙" → **误召:无修复指征在检查文本里** — 诊断只写"牙列缺损",检查 exam_findings 写"缺失,缺牙间隙已关闭"。修复 3 后 26 移除,37;47 真缺牙保留 | ✅ 已修 |
......@@ -20,13 +20,13 @@
| ⑪ | 韩俊和 BJ0U017563 | 26;27,2.3 已种 2.12 戴牙 → **① 同款,修复 1 后种植召回自动消失**(本地仅剩 47 龋·影像AI) | ✅ 随修复1 |
| ⑫ | 刘哲昕 BJ0U017233 | ✓ 认可(正畸) | ⬜ |
| ⑬ | 王晨 BJ0U016815 | ✓ 动态、回访过未停 → 待核(回访记录与召回并存逻辑) | ⬜ |
| ⑭ | 关平 BJ0U007377 | 25 萌出异常 ✗ → **误召:宿主 ICD 错标** — 25 残根拔除后缺失,宿主把"牙齿缺少"标成 K00(萌出障碍)→ 误召"萌出异常"。修复 5(K00→K08 纠偏)后改判"缺失牙未修复"(正确;患者外院矫正后再修是意向) | ✅ 已修 |
| ⑭ | 关平 BJ0U007377 | 25 萌出异常 ✗ → **误召:宿主 ICD 错标** — 25 残根拔除后缺失,宿主把"牙齿缺少"标成 K00(萌出障碍)→ 误召"萌出异常"。修复 5(K00→K08 纠偏)后改判"缺失牙未修复"(正确;患者外院矫正后再修是意向) | ✅ 已修 |
| ⑮ | 李鹏 BJ0U017637 | ✓ 15;27;47 缺失 | ⬜ |
| ⑯ | 高美玲 BJ0U016360 | ✗ 36 缺失;EMR 固定桥修复已做 → **④ 同款(固定桥牙位盲点)**,修复 2 后召回消失 | ✅ 随修复2 |
| ⑰ | 陈秀玲 BJ0U017344 | ✗ 27;1.20 已修复(活动义齿) → 待核 | ⬜ |
| ⑱ | 陈葳 BJ0U017423 | ✓ 46 缺失 | ⬜ |
| ⑲ | 王延春 BJ0U017440 | ✓ 24;25 缺失 | ⬜ |
| ⑳ | 宗明 BJ0E012466 | ✗ 15;25/36;44;正畸已关闭间隙、无片子 → 待核(影像AI 信号 vs 正畸关隙) | ⬜ |
| ⑳ | 宗明 BJ0E012466 | ✗ 15;25/34;44;正畸已关闭间隙、无片子 → 待核(影像AI 信号 vs 正畸关隙) | ⬜ |
| ㉑ | 王颖 BJ0U010770 | ✓ 47(备注空间不足) | ⬜ |
## 修复记录
......@@ -37,3 +37,4 @@
- 21 人本地基线 diff:三修复共修 6 例误召(①47、⑪26;27、④31;41、⑯36、⑤26 收缩、⑥36;46),其余理由零变动、无误杀。
- **修复 4(患者无意愿)**:treatment_record subtype 含"无意愿/不愿/拒绝"(余奕铭⑧:planned"正畸取资料,患者无意愿")→ 患者拒绝该 category,对**本次诊断**排除(类目级,靠时间方向;新诊断更晚→重新可召,非永久非时效)。词典 TREATMENT_REFUSAL_SUBTYPE_PATTERNS;gap 全口分支加 NOT EXISTS、按牙分支加 resolved 拒绝。全库 1225 患者命中。验证:余奕铭正畸/牙周消失,其余 6 正畸+1 牙周召回保留(零误杀)。
- **修复 5(宿主 ICD 错标纠偏)**:K00=牙发育/萌出障碍(含先天缺牙),但宿主把后天缺失的"牙齿缺少"也标 K00(全库 1044 例)→ 误召"萌出异常"(关平⑭ 牙25)。diagnosis parser 加 reconcileDiagnosisCode:code=K00 且 name 含缺(牙/失/少)且无"先天"→ K08;先天缺牙/萌出/多生牙等 K00 不动。需 reparse 存量。
- **修复 6(外院已治疗)**:回访记录(patient_return_visits 展示表)命中"外院"+治疗词(治疗/种植/矫正/修复/根管…)→ 患者级排除该诊断召回,回访日须 ≥ 诊断日(同治疗排除时间方向,新诊断更晚→重新可召)。算法只读回访表、不写已治疗事实。词典 EXTERNAL_TREATMENT_VISIT_PATTERNS;"外院"+治疗词共现排除"外院洁牙/检查"噪音。验证:李姝妤正畸消失(42→41,零误杀)。全库回访含外院 1812 患者。
......@@ -282,6 +282,21 @@ export const RESTORATION_INELIGIBLE_DX_NAMES = ['废用牙', '无功能牙'] as
/// 「间隙不足」(李强 BJ0U017401 牙36;46:缺牙十年邻牙倾倒,空间不够直接修复 → 需先正畸)
/// 也归此类:对 K08 缺牙召回效果一致(不该推"快去种植修复")。共现"缺牙"护栏已挡掉正畸
/// 拥挤语境(全库 844 段「间隙不足」仅 246 段共现缺牙,均为真·缺牙无空间)。
/// 回访记录里"外院已治疗"信号(李姝妤②:回访结果"已在外院矫正中")。
/// 患者去外院做了治疗 → 患者级排除该诊断召回(回访日须晚于诊断,同治疗排除的时间方向;
/// 新诊断更晚 → 重新可召)。需"外院"+治疗词共现,排除"外院洁牙/外院检查"等低值噪音。
/// 信号在 patient_return_visits(展示表),算法只读不写,不影响已治疗事实。
export const EXTERNAL_TREATMENT_VISIT_PATTERNS = [
'治疗',
'种植',
'矫正',
'矫治',
'修复',
'根管',
'已治',
'做了',
] as const;
/// 宿主 std_code 错标纠偏:K00=牙发育/萌出障碍(含先天缺牙),但宿主常把【后天缺失】的
/// "牙齿缺少/缺失/缺牙"也错标成 K00(全库 1044 例)→ 冒出"萌出异常"误召(关平 BJ0U007377 牙25:
/// 残根拔除后缺失,宿主标 K00 → 误召萌出处置)。区分符=「先天」:有先天 → 真 K00(发育障碍),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment