# 治疗链 5 阶段判定模型

> **版本:** v1.0 · W3 末确立
> **状态:** ✅ 落地(chain-composer.service.ts + canonical-codes.ts TreatmentMilestones)
>
> **重要边界**:本文档讲的是**治疗链展示层**模型 — 给客服详情页看清每个 patient 的治疗链真实进度。
> **跟召回算法解耦**:treatment_initiation_recall scenario 排除口径不变(诊断后有任何同类 actual treatment → 排除),
> 那是召回 S1(发现机会)且未启动治疗的患者。两件事别混。

---

## 0. 5 阶段定义(对齐 design 文档 §0)

```mermaid
flowchart LR
  S1["① 发现治疗机会<br/>影像 / EMR / 医嘱 / 收费"]
  S2["② 进入治疗链<br/>治疗计划 / 收费单 / 收款 / 预约 / 接诊"]
  S3["③ 治疗执行<br/>处置 / 用药 / EMR / 收费 / 退费 / 补费"]
  S4["④ 术后与复查管理<br/>术后医嘱 / 复查计划 / 术后随访"]
  S5["⑤ 治疗链闭环<br/>处置完成 + 复查完成 + 无风险信号"]
  S1 --> S2 --> S3 --> S4 --> S5
```

---

## 1. 信号 → 阶段映射(PAC fact 来源)

| 阶段 | PAC fact 信号 | 实现位置 |
|---|---|---|
| **S1 发现机会** | `diagnosis_record`(K0x 命中 category)、`recommendation_record`(*_RECOMMENDED 命中) | `collectS1Facts` |
| **S2 进入治疗链** | ① `treatment_record(kind=planned)`(同 category)<br/>② `payment_record`(大额,>= S2_PAYMENT_THRESHOLD_CENTS[category]) | `collectS2Facts` |
| **S3 治疗执行** | `treatment_record(kind=actual)`,subtype 关键词命中 `TreatmentMilestones[category].steps`,匹配数 >= `minSteps` | `matchMilestoneSteps` |
| **S4 术后复查** | ① review category 的 `treatment_record`<br/>② 同 category planned review<br/>③ S3 之后任意 `encounter_record`(复诊接诊)<br/>④ `recommendation_record` 含 'REVIEW' 关键词 | `collectS4Facts` |
| **S5 闭环** | S3 reached + S4 hit + lifecycle.maxStage=5 + 无 S3 后 refund + 无同位置反弹诊断 | inferChainStage 内 |

**故意排除的信号**(避免噪音):
- S2 不算 `appointment` / `encounter`(粒度太粗,任一回访都算"进入种植链"会错)
- S2 不算小额 payment(挂号费 58 元被算"进入种植链"会错)
- S5 不要求 `matchedSteps.allSatisfied`(preventive 字典 ['洁牙','涂氟','封闭'] 做一项即可闭环;
  种植 minSteps=2 严格要求"植入+修复"都做)

---

## 2. 字典(canonical-codes.ts 单一真理源)

### TreatmentLifecycles — 决定能否闭环

| key | maxStage | expectedSpanMonths | 说明 |
|---|---|---|---|
| `one_shot` | 5 | 3 | 一次治疗即闭环(充填/拔除/美容) |
| `linear` | 5 | 6 | 多步线性(种植 2 步 / 根管 2 步) |
| `long_term` | 5 | 24 | 长周期(正畸) |
| `periodic` | 5 | 12 | 周期性(预防/年度洁牙) |
| `lifelong_maintenance` | **4** | — | **永不闭环**(牙周炎慢性病,做完一次仍需终身维护) |

### TreatmentMilestones — category × 关键步骤

| category | steps | minSteps | lifecycle |
|---|---|---|---|
| implant | 种植体植入 → 种植上部修复 | 2 | linear |
| endodontic | 开髓 → 根管充填 | 2 | linear |
| orthodontic | 矫治器 → 保持器 | 1 | long_term |
| periodontic | 全口洁治 / 龈下刮治 / 牙周维护 | 1 | lifelong_maintenance |
| restorative | 充填 / 嵌体 | 1 | one_shot |
| prosthodontic | 冠 / 桩核 / 修复 | 1 | one_shot |
| surgical | 拔除 / 拔牙 / 手术 | 1 | one_shot |
| preventive | 洁牙 / 涂氟 / 封闭 | 1 | periodic |
| cosmetic | 美白 / 贴面 | 1 | one_shot |
| pediatric | (无具体步骤,有 actual 即满足) | 1 | one_shot |

**匹配规则**:`treatment_record.content.subtype.includes(step)` 任一即算该步完成。
host subtype 文本含关键词即可(例 "种植体植入(进口)" includes "种植体植入" → 命中)。

---

## 3. 状态(ChainStatus)

5 阶段对应 4 个 status(uninitiated 已废弃合并入 discovered):

| status | currentStage | 含义 | UI 颜色 |
|---|---|---|---|
| `discovered` | 1 | 仅 S1 命中(诊断/推荐)— **召回算法关注的"潜在新链"** | rose ★ 发现机会 |
| `entered` | 2 | S2 命中(已挂号/预约/付款/有计划) | amber 已进入 |
| `ongoing` | 3 / 4 | S3 治疗中 或 S4 复查中 | sky 在管 |
| `closed` | 5 | 闭环 — lifelong_maintenance 永不到此 | emerald 已闭环 |

`target?: boolean` 标记 = `status IN ('discovered','entered')` — 召回算法关注度高的链。

---

## 4. 验证样本(W3 末 1000-cohort)

| 患者 | 链分布(简化) |
|---|---|
| **路遥** | discovered 种植修复·21;41(K08 73 天) / **ongoing** 牙周治疗·全口(lifelong_maintenance,3-11 洁治 + 5-19 复评) |
| 罗国标 | discovered 预防保健 / ongoing 根管/牙周/种植 / **closed** 外科+充填(one_shot) |
| 陆磊 | entered 牙周/种植(付款进入未启动) / closed 预防+外科 |
| 李忠林 | entered 牙周/根管 / ongoing 种植(植入未修复 — minSteps=2 未满) / closed 龋齿充填+外科 |

---

## 5. 关键设计决策

### D1:跟召回算法解耦
治疗链 5 阶段**纯展示用**。召回算法继续用"诊断后有任何同类 actual" 简单规则,不读 TreatmentMilestones。
未来若要做"治疗链内召回"(例:种植已植入未修复 stage=3 患者催完成),再写新 scenario 读字典。

### D2:S2 收紧 — 大额 payment 阈值
appointment / 小额 payment / encounter 不算 S2(避免挂号费 58 元被算成"进入种植链")。
阈值字典 `S2_PAYMENT_THRESHOLD_CENTS`(种植 ¥5000 / 充填 ¥300 / 等),后续可挪到 host config。

### D3:闭环不要求 milestone 全 steps 满足
preventive 字典 ['洁牙','涂氟','封闭'] 做一项即满 minSteps=1 → 可闭环。
种植 minSteps=2 严格要求"植入+修复"都做才可能 closed。
"全 steps 满足才能闭环" 太严,会导致几乎所有 multi-step category 永远 ongoing。

### D4:同 chainLabel 去重
K08 → primary='implant' 立 1 条链;患者做的"种植上部修复" actual.category='prosthodontic' 也立 1 条。
两条 chainLabel 都叫"种植修复·xx" — 临床上是同一条链。
compose() 末尾去重 pass:同 chainLabel 保留 stage 最高的那条。

### D5:牙周 lifelong_maintenance
牙周炎是慢性病,做完一次洁治仍需终身维护复查。**永远停留 stage=4 ongoing**,不到 closed。
UI 显示"维护期 · 上次维护 YYYY-MM-DD",客服可周期触达。

---

## 6. 待完善(后续迭代)

- **planned treatment 字典扩展**:目前路遥的 K08 EMR.treat_plan 里有"延期种植术"但
  `treatment_planned.yaml` enum_mapping 未覆盖 → 该患者 implant planned 全部丢 → S2 严格判定下 stage=1 discovered
  应补 implant 类 treatName 到字典。
- **risk signals 完整化**:目前 S5 检查 refund 和反弹诊断;`complaint_record` 字段已定义但 DW 暂无源,W5+ 接入后补
- **正畸 closed 精细化**:保持器交付 + 12 月内无新调整 才算 closed;目前简化为有 actual + S4 命中即可
- **Layer C LLM 抽影像/EMR finding** 补 S1 信号源 — W5+ 跟其他 LLM 任务一起做
