Commit 79e63437 by luoqi

fix(召回): 举证触发改为「该动作解不开本场景缺口」—— 硬清单会漏牙周

王祖妹 TS0B006805 牙46:4 年前种植戴牙,2026-04-25 复诊。
  主诉「种植戴牙4年余按约复查」/ 现病史「4年前在本院种植戴牙,无不适,今按约复诊」
  检查所见「冠边缘密合」/ 诊断 K08 牙齿缺少 46
  本次治疗「转洁牙中心全口牙洁治」→ **periodontic**
文本证据完美,却因为触发闸硬写成 ['preventive','review'] 而不生效。

牙周洁治对缺牙缺口同样是"什么也没解决",跟宣教/复查没有区别 —— 判据本来就该是
**类目 ∉ 该场景的 resolverCats**,而不是一张写死的清单。改成 treatedEvidenceTriggersFor()。
生产实测放宽后只多 3 条命中(周海峰/马丽/王振权),形状与王祖妹一致,全部正确。

 但牙周/正畸的牙位是**全口批量**写的(一条洁治记录挂 28 颗),不表达"针对这颗牙",
拿它当牙位锚会大面积误销 → 这两类额外加牙位上限 8。预防/复查类**不设**上限:
那些牙位是医生针对性写的,且全口种植修复复查确实会写满一颌(吴庆安 12 / 高惠霞 14 /
周根娣 15,实测都是对的),一刀切上限反而会砍掉正确命中。

(放宽后的全量复核受限:牙周记录牙位数大,不加上限时导出查询 860s 超时两次;
 加上限后可导出,即上面那 3 条。)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parent 362f484e
......@@ -11,7 +11,9 @@ import {
REVIEW_IMPLIES_TREATMENT,
MISSING_TOOTH_POLISH_EVIDENCE,
TREATED_EVIDENCE_EMR_FIELDS,
TREATED_EVIDENCE_TRIGGER_CATEGORIES,
treatedEvidenceTriggersFor,
TREATED_EVIDENCE_BATCH_CATEGORIES,
TREATED_EVIDENCE_BATCH_MAX_TEETH,
TREATED_EVIDENCE_RESTORATION_TERMS,
TREATED_EVIDENCE_COMPLETION_RE,
TREATED_EVIDENCE_INTENT_EXCLUDE_RE,
......@@ -298,7 +300,14 @@ export function buildGapCore(input: GapCoreInput): GapCorePieces {
AND tvx.type = 'treatment_record' AND tvx.kind = 'actual'
AND tvx.status IN ('active', 'fulfilled')
AND COALESCE(NULLIF(trim(tvx.content->>'tooth_position'), ''), '') != ''
AND tvx.content->>'category' = ANY(${[...TREATED_EVIDENCE_TRIGGER_CATEGORIES]}::text[])
AND tvx.content->>'category' = ANY(${[
...treatedEvidenceTriggersFor(resolverCats),
]}::text[])
AND (
tvx.content->>'category' <> ALL(${[...TREATED_EVIDENCE_BATCH_CATEGORIES]}::text[])
OR COALESCE(array_length(${toothArrSql(Prisma.sql`tvx.content->>'tooth_position'`)}, 1), 0)
<= ${TREATED_EVIDENCE_BATCH_MAX_TEETH}
)
AND (${emrTextSql}) ~ ${evidenceTerms.map((t) => t.pattern).join('|')}
AND (${emrTextSql}) ~ ${TREATED_EVIDENCE_COMPLETION_RE}
AND (${emrTextSql}) !~ ${TREATED_EVIDENCE_INTENT_EXCLUDE_RE}
......
......@@ -14,7 +14,8 @@ import {
TREATED_EVIDENCE_COMPLETION_RE,
TREATED_EVIDENCE_INTENT_EXCLUDE_RE,
TREATED_EVIDENCE_EMR_FIELDS,
TREATED_EVIDENCE_TRIGGER_CATEGORIES,
treatedEvidenceTriggersFor,
TREATED_EVIDENCE_BATCH_CATEGORIES,
resolverCategoriesFor,
} from '@pac/types';
......@@ -87,8 +88,22 @@ describe('表自身自洽', () => {
expect(TREATED_EVIDENCE_INTENT_EXCLUDE_RE).not.toMatch(/(^|\|)计划(\||$)/);
});
it('触发类目只收预防/复查 —— 治疗类走现有 resolver,不进本表', () => {
expect([...TREATED_EVIDENCE_TRIGGER_CATEGORIES].sort()).toEqual(['preventive', 'review']);
it('⭐ 触发条件 = 该动作解不开本场景的缺口(不是硬清单)', () => {
const k08 = resolverCategoriesFor('K08') as readonly string[];
const trig = treatedEvidenceTriggersFor(k08);
// 治疗类走现有 resolver,不进本表
for (const c of ['implant', 'prosthodontic', 'restorative', 'endodontic', 'surgical']) {
expect(trig).not.toContain(c);
}
// 🔴 牙周必须在内 —— 王祖妹 TS0B006805 那次动作是「转洁牙中心全口牙洁治」(periodontic),
// 硬写 ['preventive','review'] 会漏掉她
for (const c of ['preventive', 'review', 'periodontic', 'orthodontic']) {
expect(trig).toContain(c);
}
});
it('⛔ 牙周/正畸是全口批量记录,额外加牙位上限', () => {
expect([...TREATED_EVIDENCE_BATCH_CATEGORIES].sort()).toEqual(['orthodontic', 'periodontic']);
});
it('⛔ 暂不扫检查所见(混合描述) / 处置(夹带 base64)', () => {
......
......@@ -546,8 +546,25 @@ export const TREATED_EVIDENCE_SINGLE_ARCH_ONLY = true;
/// 会参与正则匹配并制造假命中(Jing Dong BJ0U007054 就是这么中的)。要收得先剥字段。
export const TREATED_EVIDENCE_EMR_FIELDS = ['illness_desc', 'pre_illness'] as const;
/// 本次动作是这些类目时才启动补充举证(治疗类不走本表 —— resolver 已正确处理)
export const TREATED_EVIDENCE_TRIGGER_CATEGORIES = ['preventive', 'review'] as const;
/// 启动条件不是硬清单,而是「本次动作**解不开这个场景的缺口**」——
/// 即 category ∉ 该场景的 resolverCats。硬写 ['preventive','review'] 会漏:
/// 王祖妹 TS0B006805 牙46 那次的动作是「转洁牙中心全口牙洁治」(periodontic),
/// 主诉写着「种植戴牙4年余按约复查」,却因类目不在清单里而不触发。
/// 牙周/洗牙对缺牙缺口同样是"什么也没解决",判据应当一致。
/// (生产实测放宽后只多 3 条命中,形状与王祖妹一致,全部正确。)
export const treatedEvidenceTriggersFor = (
resolverCats: readonly string[],
): readonly PACTreatmentCategory[] =>
(Object.keys(PACTreatmentCategories) as PACTreatmentCategory[]).filter(
(c) => !resolverCats.includes(c),
);
/// ⛔ 天然批量的类目:牙周洁治/正畸的牙位是**全口批量**写的(一条记录挂 28 颗),
/// 不表达"针对这颗牙",拿它当牙位锚会大面积误销 → 这两类额外加牙位上限。
/// 预防/复查类不设上限:那些牙位是医生针对性写的,且全口种植修复复查确实会写满一颌
/// (吴庆安 12 颗 / 高惠霞 14 颗 / 周根娣 15 颗,实测都是对的)。
export const TREATED_EVIDENCE_BATCH_CATEGORIES = ['periodontic', 'orthodontic'] as const;
export const TREATED_EVIDENCE_BATCH_MAX_TEETH = 8;
/// 修复体词 → 它蕴含的治疗类目(消费方须用 resolverCategoriesFor 过滤,跨类目不放行)
export const TREATED_EVIDENCE_RESTORATION_TERMS: ReadonlyArray<{
......
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