feat(recall): jvs-dw 接上宿主跟进闸 —— DW「潜在治疗池」的患者不进召回
DW 新表 fact_complex_cases_out(comment 原文「潜在治疗ID」)= FRIDAY 侧
complex_case_info「复杂病例」:宿主自己在跟的患者,PAC 不该再发起电话召回。
落点复用 FRIDAY 那次已建好的 patient_profiles.host_follow_up_active,
canonical / 副表 / 召回 SQL 一行未动 —— 本次全部改动落在 yaml + 通用引擎。
口径与 FRIDAY 对齐(实测 113,657 行 / 88,300 患者):
未删除(is_del=1;98.8% 为 1,反直觉但两家源库一致)
且 case_stage ∈ 1待跟进/2已咨询/3已预约/5诊疗中 → 在跟(70,275 患者)
6已成单 / 7已丢单 = 宿主停手 → 交还召回
【判定为什么内联进主档 SQL,而不是 lookup / join】
FRIDAY 的演进方向是「宿主侧 join,PAC 侧不 lookup」(其 manifest 四处注明),
jvs-dw 是 pull、SQL 由 PAC 自己写,故用纪律允许的 `IN (SELECT …)` 形状,
且 tuple 版天然带 brand —— 集团内同号跨品牌是两个人,单键会误标瑞泰同号患者。
更关键的是这样判定是**实时全量**的:每次拉到主档都重算,不依赖增量窗口。
若改成"拉变化的病例再 join",窗口外的人会 lookup 未命中 → full upsert 落 null
→ 正在跟进的患者被静默洗回召回池。
【通用层两处改动 —— 都是既有脆弱性,被这个 SQL 形状第一次触发】
1. SQL 改写器改按括号深度定位顶层关键字(新增 topLevelIndexOf / splitSelectFrom):
主档 query 头一次出现 SELECT 列里的标量子查询,而
· injectIncrementalCursor 的 /SELECT (.+?) FROM (\w+)/ 非贪婪会撞上子查询的 FROM
→ 切出半截列表 + 错误表名,增量拉错表
· extractBusinessFilters 抓第一个 WHERE → 把子查询的 is_del=1 当成主档业务过滤
搬到外层 → 主档无该列 → CH 报错(增量空转探针一并错)
两者都是静默错法,故补 13 项测试锁死。
2. 反向拉主档:表名从 manifest.cohort.reverse_pull_from 读(不配 → 历史四张默认,
行为不变),键列/主档表名同步改读 cohort 配置。原先四张 jvs-dw 表名硬编码在通用
代码里,新增一张就要改 service —— 与「yaml 是宿主唯一差异」相悖。
⭐ 同时修一处真 bug:反向拉写死 `SELECT *`,会丢掉主档 query 的派生列 ——
该列缺席 → canonical 无此键 → full 分支落 null → 在跟患者被反向拉这步洗回池。
改为复用主档 query 的 SELECT 列。
【增量怎么感知】主档 cursor 是 last_visit_time,人不来诊就拉不到 → 病例开/关 PAC 不知道。
故把该表作为独立 query 拉(不产 fact、无 assembler),仅为把变化的患者带进 cohort,
再由反向拉补出主档、重算派生列。cursor 用 SELECT 别名 changed_at =
coalesce(updated_gmt_at, created_gmt_at):updated 有 44% 为 NULL(建后没改过,
恰是刚入池的新病例),直接当 cursor 会 `NULL > x` 恒 UNKNOWN 静默漏掉这批。
测试服 DW 实跑验证:主档/增量两条改写后的 SQL 均可执行,标记数 70,275 与直接统计一致。
790 tests / 51 suites 全绿。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Found errors in your .gitlab-ci.yml:
- jobs:openapi-drift config contains unknown keys: rules
| Status | Job ID | Name | Coverage |
|---|