Commit 256295db by luoqi

fix(recall): 检查文本"缺牙间隙关闭/无修复间隙"计入无修复指征(一线反馈⑤)

王敏 BJ0U017412 牙26:诊断只写"牙列缺损",但 emr_record.exam_findings 写
"缺失,缺牙间隙已关闭"→ 间隙没了无需修复,召回看不到检查文本 → 误召。

方案A(不升 examine 为主体,排除时读 fact.content):
- gap 核心 resolvedTeethSql 加分支:解析 emr_record.exam_findings(JSON 字符串数组),
  牙位 message 中"缺失/缺牙"与"间隙关闭/无修复间隙"同段共现 → 该牙计入已解决;
- 词典 NO_RESTORATION_GAP_EXAM_PATTERNS 放 types 单一源(与同语义的
  RESTORATION_INELIGIBLE_DX_NAMES 并列);共现限定排除正畸"关闭间隙"治疗噪音
  (单独"间隙关闭"全库多在正畸语境;共现后 104 患者命中);
- 时间方向与治疗家族同口径(afterDxFor('emrx'))。

验证(21 人本地):王敏 26 移除、37;47 真缺牙保留;总理由数 45 不变(零误杀);
recompute exit=0。Layer C 上线后此分支平移给抽取器即可。仅本地,未部署。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
parent 5462fc71
import { Prisma } from '@prisma/client';
import {
NO_RESTORATION_GAP_EXAM_PATTERNS,
RESTORATION_INELIGIBLE_DX_NAMES,
STRUCTURAL_DX_CODE_LIST,
type DxTreatmentRule,
......@@ -125,6 +126,8 @@ export function buildGapCore(input: GapCoreInput): GapCorePieces {
? Prisma.sql`AND ${Prisma.raw(alias)}.occurred_at >= ${latestDxOfCode}`
: Prisma.sql`AND ${Prisma.raw(alias)}.occurred_at >= COALESCE(sig.occurred_at, sig.planned_for)`;
const afterDxFragRtx = afterDxFor('rtx');
const noRestorMsgRe = NO_RESTORATION_GAP_EXAM_PATTERNS.join('|');
// §E (d) 正畸减数位(仅 missing_tooth):该牙有外科拔除 + 患者有正畸语境 → 折进 resolved 减掉。
const orthoExtractBranch = cfgFlags.excludeOrthoExtractionSites
......@@ -193,6 +196,26 @@ export function buildGapCore(input: GapCoreInput): GapCorePieces {
AND btx.content->>'category' = 'prosthodontic'
${afterDxFor('btx')}
UNION
-- (a''') 检查所见"缺牙但间隙关闭/无修复间隙" → 该牙无修复指征,计入 resolved
-- (一线反馈⑤ 王敏 26:诊断"牙列缺损"但检查"缺失,缺牙间隙已关闭"→ 无需修复)。
-- 读 emr_record.exam_findings(JSON 字符串数组),按牙位 message 匹配;
-- 限"缺失/缺牙"与"间隙关闭/无修复间隙"同段共现(排除正畸"关闭间隙"治疗语境噪音)。
SELECT nrt AS t
FROM (
SELECT emrx.content->>'exam_findings' AS ef_text, emrx.occurred_at
FROM patient_facts emrx
WHERE emrx.patient_id = p.id
AND emrx.type = 'emr_record' AND emrx.status IN ('active', 'fulfilled')
AND emrx.content->>'exam_findings' ~ '^\\['
AND emrx.content->>'exam_findings' ~ '缺[牙失]'
AND emrx.content->>'exam_findings' ~ ${Prisma.sql`${noRestorMsgRe}`}
${afterDxFor('emrx')}
) src
CROSS JOIN LATERAL jsonb_array_elements(src.ef_text::jsonb) AS ef
CROSS JOIN unnest(${toothArrSql(Prisma.sql`ef->>'toothPosition'`)}) AS nrt
WHERE (ef->>'message') ~ '缺[牙失]'
AND (ef->>'message') ~ ${Prisma.sql`${noRestorMsgRe}`}
UNION
-- (b) 同牙位以【最新诊断】为准:更晚的真实结构诊断 → 旧诊断对该牙失效
SELECT ldt AS t
FROM patient_facts ldx
......
......@@ -5,29 +5,29 @@
## 逐例核验
| # | 患者 | 一线反馈 → 核验结论 | 状态 |
| --- | -------------- | ------------------------------------------------------------------------------------------------------------------- | ------ |
| ① | 李然 BA38586 | 47 已种植(1.25)+拆线(2.7)仍被召 → **误召:拆线归 review 不算治疗**;修复 1 后 47 召回消失,剩 16 牙体损伤/17;38 龋(真实待治,该召) | ✅ 已修 |
| ② | 李姝妤 BJ0U005102 | 外院矫正,4.21 转院 → **回访记录有提到外院**(4.21 常规回访,结果"已在外院矫正中",已入 PAC 回访表/历史联系可见,但召回未消费回访文本);客户动态数据与数仓核对中 | ⏸ 待定 |
| ③ | 秦溢泽 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 无间隙" → 待核 | ⬜ |
| ⑥ | 李强 BJ0U017401 | 36;46 间隙不足→建议正畸 → 待核 | ⬜ |
| ⑦ | 黄琳 BJ0U015883 | ✓ 认可(17 缺失) | ⬜ |
| ⑧ | 余奕铭 BJ0F022277 | 外院/无意愿 → 待核(预计信息差) | ⬜ |
| ⑨ | 刘强 BJ0U016929 | ✓ 认可(正畸) | ⬜ |
| ⑩ | 李石明 BJ0U017487 | 37;47 缺,25.12.23 活动义齿 → 待核(活动义齿是否入了事实?) | ⬜ |
| ⑪ | 韩俊和 BJ0U017563 | 26;27,2.3 已种 2.12 戴牙 → **① 同款,修复 1 后种植召回自动消失**(本地仅剩 47 龋·影像AI) | ✅ 随修复1 |
| ⑫ | 刘哲昕 BJ0U017233 | ✓ 认可(正畸) | ⬜ |
| ⑬ | 王晨 BJ0U016815 | ✓ 动态、回访过未停 → 待核(回访记录与召回并存逻辑) | ⬜ |
| ⑭ | 关平 BJ0U007377 | 25 萌出异常 ✗(2020.9.18 增平?);外院矫正→完成后全面修复 → 待核 | ⬜ |
| ⑮ | 李鹏 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 正畸关隙) | ⬜ |
| ㉑ | 王颖 BJ0U010770 | ✓ 47(备注空间不足) | ⬜ |
| # | 患者 | 一线反馈 → 核验结论 | 状态 |
| --- | -------------- | --------------------------------------------------------------------------------------------------------------------- | ------ |
| ① | 李然 BA38586 | 47 已种植(1.25)+拆线(2.7)仍被召 → **误召:拆线归 review 不算治疗**;修复 1 后 47 召回消失,剩 16 牙体损伤/17;38 龋(真实待治,该召) | ✅ 已修 |
| ② | 李姝妤 BJ0U005102 | 外院矫正,4.21 转院 → **回访记录有提到外院**(4.21 常规回访,结果"已在外院矫正中",已入 PAC 回访表/历史联系可见,但召回未消费回访文本);客户动态数据与数仓核对中 | ⏸ 待定 |
| ③ | 秦溢泽 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 无间隙" → 待核 | ⬜ |
| ⑥ | 李强 BJ0U017401 | 36;46 间隙不足→建议正畸 → 待核 | ⬜ |
| ⑦ | 黄琳 BJ0U015883 | ✓ 认可(17 缺失) | ⬜ |
| ⑧ | 余奕铭 BJ0F022277 | 外院/无意愿 → 待核(预计信息差) | ⬜ |
| ⑨ | 刘强 BJ0U016929 | ✓ 认可(正畸) | ⬜ |
| ⑩ | 李石明 BJ0U017487 | 37;47 缺,25.12.23 活动义齿 → 待核(活动义齿是否入了事实?) | ⬜ |
| ⑪ | 韩俊和 BJ0U017563 | 26;27,2.3 已种 2.12 戴牙 → **① 同款,修复 1 后种植召回自动消失**(本地仅剩 47 龋·影像AI) | ✅ 随修复1 |
| ⑫ | 刘哲昕 BJ0U017233 | ✓ 认可(正畸) | ⬜ |
| ⑬ | 王晨 BJ0U016815 | ✓ 动态、回访过未停 → 待核(回访记录与召回并存逻辑) | ⬜ |
| ⑭ | 关平 BJ0U007377 | 25 萌出异常 ✗(2020.9.18 增平?);外院矫正→完成后全面修复 → 待核 | ⬜ |
| ⑮ | 李鹏 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 正畸关隙) | ⬜ |
| ㉑ | 王颖 BJ0U010770 | ✓ 47(备注空间不足) | ⬜ |
## 修复记录
......
......@@ -275,6 +275,12 @@ export type DiagnosisTreatmentCode = keyof typeof DiagnosisTreatmentMap;
*/
export const RESTORATION_INELIGIBLE_DX_NAMES = ['废用牙', '无功能牙'] as const;
/// 检查所见(emr_record.exam_findings 的牙位 message)里"缺牙但无修复指征"的说法。
/// 王敏 BJ0U017412 牙26:诊断只写"牙列缺损",检查里写"缺失,缺牙间隙已关闭"→ 间隙没了无需修复。
/// ⚠️ 必须与"缺失/缺牙"在同一牙位 message 共现 —— 单独"间隙关闭"绝大多数是正畸语境
/// (矫正过程关闭间隙=治疗动作),会误排;共现限定后全库 104 患者(2026-06 实测)。
export const NO_RESTORATION_GAP_EXAM_PATTERNS = ['间隙关闭', '间隙已关闭', '无修复间隙'] as const;
/// 判断诊断 name_zh 是否属"无修复必要"(废用牙/无功能牙)
export function isRestorationIneligibleDxName(nameZh: string | null | undefined): boolean {
return !!nameZh && (RESTORATION_INELIGIBLE_DX_NAMES as readonly string[]).includes(nameZh);
......
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