feat(friday): 摄入客户回访 —— customer_return_visit → PatientReturnVisit
FRIDAY 此前没接回访(patient_return_visit 一直空)。全库扫 58 张相关表,
只有 customer.customer_return_visit 有实际数据(2552 行 / 1122 患者 / 27 诊所 / 77 客服),
其余是空表(arrail-medical-server.return_visit 0 行、tenant_apply.return_visit* 全 0)、
配置字典、或「召回」另一套(customer_recall_*,量极小)。
【源表是「统一任务表 + 分类型记录表」结构】
customer_task(task_type: 1咨询 / 2回访 / 4预约备注 / 10…)
├─ 1 → customer_consult (咨询,PAC 早已单独摄入)
└─ 2 → customer_return_visit (回访,本次)
实测零交叉:consult 的 64 条 task_id 全指向 type=1,回访 2552 条全指向 type=2 ——
所以这不是重复摄入咨询。关联 rv.task_id = t.id 实测 2552/2552 全中、零空值
(反向的 t.return_visit_id 全空,是电子病历迁移遗留列,不可用)。
任务头不单独摄入,task_date/task_status 由宿主 inline 进回访行 ——
与 customer_treat_plan_item inline 计划头 organization_id/plan_name 同形态。
【task_date 含未来排程,不能丢】界面「设回访」就是设未来日期(截图实证)。
PAC 靠它区分「已发生」与「排了没做」:详情页倒序展示、召回话术只把已发生的算作
"联系过";而客服名册判「在岗」必须用 sourceCreatedAt 而非 task_date,正因后者含未来
(jvs-dw 实测最远 2033)。测试库当前无未来样本,但字段语义按含未来实现。
【枚举翻中文,与 jvs-dw 逐字对齐】jvs-dw 的 DW 侧本就是 *_name 中文列,FRIDAY 是数字码。
同一张 PatientReturnVisit 表里两个宿主的值必须一致,否则前端展示与按类型筛选会分叉。
注意 return_visit_type 官方注释只写 1-3,实测存在 4/5(311 条,占 12%),不能落 _default 丢掉:
4 → 100% 带治疗项,取值含「取消预约回访」「自定义」→ 自定义/事件驱动回访
5 → 100% 带治疗项且全是诊断名(残根/龋齿/根尖周炎),task_director 全为 System
→ 系统按诊断自动生成的召回
【新增 strip_html 算子】宿主回访内容用富文本编辑器录入,实测 794/2552(31%)带
<p>/<ol>/<li>。PAC 侧该字段是纯文本语义(详情页直接展示 + 喂召回话术 LLM),
带标签会原样显示成 "<p>xxx</p>" 并污染 LLM 上下文,故在摄入层剥掉而非留给每个消费方。
口径:块级标签转换行保住段落边界、实体还原(& 放最后避免二次解码)、全空 → null。
【canonical-fact-layer 闸 4 加特例】该闸按字段名校验 enum_mapping 目标 ∈ canonical-codes,
但 closedSets.status 装的是 PACTreatmentStatuses(治疗状态),与回访的「已回访/未回访」
同名不同义。回访三列是展示用自由文本(不进 fact、不参与召回),口径是与 jvs-dw 中文对齐,
故整个 patient_return_visit 跳过该闸(同 appointment.status 已有的特例)。
宿主多给的列(suggested_return_person / actual_return_person / actual_return_time /
customer_status / is_first)原样带出存进 raw_payload,PAC 当前不映射 —— 将来要用不必再找宿主改。
测试 29 项;契约文档新增第 7 节(source 数 9 → 10);export.sh 补导出语句。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>