Commit ae2f1fe5 by luoqi

feat(recall): ⑤f 就诊冷静 — 近 N 天到过诊的患者本轮不召(防打扰)

患者级抑制门(与具体诊断信号无关):最近一次到诊(encounter/emr)在
POST_VISIT_COOLDOWN_DAYS(默认 14)天内 → 本轮不入召回池,别催刚来过的人。

- 锚"最近到诊",跟诊断 cooldown(锚诊断日、按 K 码定长)不同,二者并存各补各的洞:
  cooldown 给新诊断缓冲;本门拦"旧诊断 + 近期又来过"。
- 配合已有的"0 命中关闭遗留 plan":患者一到诊,下次重算自动从召回列表撤下。
- 不含 appointment(预约不一定到诊;未来预约已由 ⑤b 排除)。
- 现内联在 initiation scenario;将来上复购 scenario 时抽成共享 fragment 复用。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parent 7dc4ed0f
...@@ -74,6 +74,12 @@ import { toothSet } from '../../../sync/pipeline/parsers/tooth-position.util'; ...@@ -74,6 +74,12 @@ import { toothSet } from '../../../sync/pipeline/parsers/tooth-position.util';
/// ⭐ 单一真理源(canonical-codes):chain-composer 同表 → 不立"种植修复·发现机会"潜在链,口径一致。 /// ⭐ 单一真理源(canonical-codes):chain-composer 同表 → 不立"种植修复·发现机会"潜在链,口径一致。
const RESTORATION_INELIGIBLE_NAMES = [...RESTORATION_INELIGIBLE_DX_NAMES]; const RESTORATION_INELIGIBLE_NAMES = [...RESTORATION_INELIGIBLE_DX_NAMES];
/// 就诊冷静(防打扰,⑤f):患者近 N 天到过诊(encounter/emr)→ 本轮不召,别催刚来过的人。
/// 跟诊断 cooldown 不同锚点 — cooldown 锚"诊断日"按 K 码定长;本项锚"最近到诊"统一长度。
/// 二者并存,各补各的洞:cooldown 给新诊断缓冲;本项拦"旧诊断 + 近期又来过"。
/// 患者级、与具体信号无关;将来复购 scenario 应抽成共享 fragment 复用。
const POST_VISIT_COOLDOWN_DAYS = 14;
@Injectable() @Injectable()
export class TreatmentInitiationRecallScenario implements PlanScenarioPlugin { export class TreatmentInitiationRecallScenario implements PlanScenarioPlugin {
readonly key = PlanScenario.TREATMENT_INITIATION_RECALL; readonly key = PlanScenario.TREATMENT_INITIATION_RECALL;
...@@ -459,6 +465,16 @@ export class TreatmentInitiationRecallScenario implements PlanScenarioPlugin { ...@@ -459,6 +465,16 @@ export class TreatmentInitiationRecallScenario implements PlanScenarioPlugin {
'' ''
) )
) )
AND NOT EXISTS ( -- ⑤f 就诊冷静:近 N 天到过诊 → 别催刚来过的人(防打扰)
-- 患者级、与具体信号无关:最近一次到诊(encounter/emr)在 N 天内 → 本轮不召。
-- 锚"最近到诊"而非"诊断日" → 补 cooldown 漏的"旧诊断 + 近期又来过"场景。
-- 不含 appointment(那是预约不一定到诊;未来预约已由 ⑤b 单独排除)。
SELECT 1 FROM patient_facts vis
WHERE vis.patient_id = p.id
AND vis.type IN ('encounter_record', 'emr_record')
AND vis.occurred_at IS NOT NULL
AND vis.occurred_at > ${this.daysAgo(scope.now, POST_VISIT_COOLDOWN_DAYS)}::timestamptz
)
`; `;
// ⭐ 同 patient 同 sub_scenario 的多 sig 按 tooth-overlap 合并(union-find) // ⭐ 同 patient 同 sub_scenario 的多 sig 按 tooth-overlap 合并(union-find)
......
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