Commit 69561c3a by luoqi

merge: 召回场景 SQL dump 开关 → test

parents 3077902e 38e59329
Pipeline #3609 failed in 0 seconds
......@@ -374,7 +374,7 @@ export class TreatmentInitiationRecallScenario implements PlanScenarioPlugin {
// ║ 输出:每个命中 (patient × sig) 一行,后段 byPatient Map 去重 ║
// ║ 只留 daysSince 最大那条(最早诊断 = 最有召回价值) ║
// ╚═════════════════════════════════════════════════════════════════════╝
const rows: HitRow[] = await this.prisma.$queryRaw`
const scenarioSql = Prisma.sql`
SELECT
p.id AS patient_id,
p.external_id AS patient_external_id,
......@@ -466,6 +466,21 @@ export class TreatmentInitiationRecallScenario implements PlanScenarioPlugin {
)
`;
// ⚡ 调试开关:PAC_RECALL_DUMP_SQL=1 时把本子场景的完整 SQL 打出来。
// 为什么需要:pg_stat_activity.query 被 track_activity_query_size(默认 1024 字节)截断,
// 而本查询远超这个长度 —— 线上抓到"某条子场景查询跑了 6.7 分钟"却拿不到全文做 EXPLAIN,
// 2026-08-29 排查 plan 段耗时就卡在这一步。开关默认关,零开销。
// 用法:PAC_RECALL_DUMP_SQL=1 pnpm recompute-plans:prod -- --host=jvs-dw
// 然后把打出来的 SQL 喂给 EXPLAIN (ANALYZE, BUFFERS)。
if (process.env.PAC_RECALL_DUMP_SQL === '1') {
this.logger.log(
`[dump-sql] subKey=${subKey} primaryCode=${cfg.primaryCode} len=${scenarioSql.sql.length}\n` +
`${scenarioSql.sql}\n` +
`[dump-sql-params] ${JSON.stringify(scenarioSql.values)}`,
);
}
const rows: HitRow[] = await this.prisma.$queryRaw(scenarioSql);
// ⭐ 同 patient 同 sub_scenario 的多 sig 按 tooth-overlap 合并(union-find)
// 跟 chain-composer 的 bucket 合并口径一致 → reason 与 chain 1:1 对齐
// 场景:王燕桦 K01 3 个 sig(38 / 48;38 / 18;28;38)牙位重叠 → 1 个临床 episode
......
......@@ -47,8 +47,18 @@ function flatten(frag: Prisma.Sql | SqlLike | unknown): string {
/// 返回 [] 让调用方走空结果短路,不需要再 mock 后续查询。
function makeSqlCapturingPrisma(): { prisma: PrismaService; sql: () => string } {
const captured: string[] = [];
const queryRaw = (strings: TemplateStringsArray, ...values: unknown[]) => {
const parts = Array.isArray(strings?.raw) ? strings.raw : [String(strings)];
const queryRaw = (strings: TemplateStringsArray | { sql?: string }, ...values: unknown[]) => {
// 两种调用形态都要认:
// ① 标签模板 `$queryRaw`...`` → strings 是 TemplateStringsArray
// ② 传 Prisma.sql 对象 `$queryRaw(sql)` → strings 是 Sql,已摊平好的 .sql 直接用
// (2026-08-29 起主查询走 ② —— 先建 Prisma.sql 对象才能在 PAC_RECALL_DUMP_SQL=1 时
// 打出完整 SQL 做 EXPLAIN;pg_stat_activity 会把它截断在 1024 字节。)
if (typeof (strings as { sql?: string })?.sql === 'string') {
captured.push((strings as { sql: string }).sql);
return Promise.resolve([]);
}
const t = strings as TemplateStringsArray;
const parts = Array.isArray(t?.raw) ? t.raw : [String(strings)];
captured.push(parts.map((s, i) => s + (i < values.length ? flatten(values[i]) : '')).join(''));
return Promise.resolve([]);
};
......
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