Commit 2b0c5406 by luoqi

chore(cli): 加 backfill-plan-labels —— 别为了填派生列去重算 plan

上线后必须立刻跑一次:迁移只是加了列,全表都是 NULL,而矩阵
`unnest(potential_labels)` 遇到 NULL 什么都不出 ⇒ **部署完到回填完之间矩阵全是 0**。
 别指望夜间任务兜 —— 那要等到次日 03:30。

️ 「重算 plan 也能顺带补齐」是真的(收尾会调 backfillMissing),但**不该拿它当手段**:
  重算会升版本、关闭无信号的 plan、auto_release 释放客服手上的单 ——
  为一个派生列去动召回池,代价完全不对等。
  (测试服上还有一批"特殊处理过另有他用"的在手单,更不该被卷进去。)

用法:
  pnpm backfill-plan-labels           # 只补没算过的(上线后跑这个)
  pnpm backfill-plan-labels -- --all  # 连算过的一起重算(改了标签规则后)
  pnpm backfill-plan-labels -- --check # 只报还差多少,不写库

️ 回填后若仍有剩余 → 报 error 且 exit 1:那说明有写入路径没接上补齐,
   别等矩阵少人了才发现。

验证:tsc 通过;jest 86 套 1342 例全过;eslint 干净;
  本地实跑:--check 报 0 → 人为置空 119 条 → 回填写入 119 条 / 0.1s / 剩余 0。
parent 99032119
......@@ -28,6 +28,7 @@
"sync-incremental:prod": "node --max-old-space-size=4096 dist/cli/sync-incremental.cli.js",
"import-patient": "ts-node --transpile-only src/cli/import-patient.cli.ts",
"recompute-persona": "ts-node --transpile-only src/cli/recompute-persona.cli.ts",
"backfill-plan-labels": "ts-node --transpile-only src/cli/backfill-plan-labels.cli.ts",
"recompute-persona:prod": "node --max-old-space-size=8192 dist/cli/recompute-persona.cli.js",
"recompute-plans": "ts-node --transpile-only src/cli/recompute-plans.cli.ts",
"recompute-plans:prod": "node --max-old-space-size=8192 dist/cli/recompute-plans.cli.js",
......
/**
* Backfill Plan Labels CLI —— 回填 / 重算 `plan_reasons.potential_labels`。
*
* 初选矩阵靠这一列(改造后不再回查 `patient_facts`,958ms → 110ms)。
*
* 🔴 **上线后必须立刻跑一次**:迁移只是加了列,全表都是 NULL,而矩阵
* `unnest(potential_labels)` 遇到 NULL 什么都不出 ⇒ **这段时间矩阵会全是 0**。
* ⛔ 别指望夜间任务兜 —— 那要等到次日 03:30。
*
* ⚠️ ⛔ 不要为了填这一列去「重算 plan」:重算会升版本、关闭无信号的 plan、
* `auto_release` 释放客服手上的单 —— 为一个派生列去动召回池,代价完全不对等。
* (重算收尾确实也会补齐,但那是顺带,不是手段。)
*
* Usage:
* pnpm backfill-plan-labels # 只补没算过的(上线后跑这个)
* pnpm backfill-plan-labels -- --all # 连算过的一起重算(改了标签规则后跑)
* pnpm backfill-plan-labels -- --check # 只报还差多少,不写库
*/
import { NestFactory } from '@nestjs/core';
import { Logger } from '@nestjs/common';
import { AppModule } from '../app.module';
import { PlanLabelService } from '../modules/plan/plan-label.service';
async function main(): Promise<void> {
const log = new Logger('backfill-plan-labels');
const argv = process.argv.slice(2);
const all = argv.includes('--all');
const checkOnly = argv.includes('--check');
const app = await NestFactory.createApplicationContext(AppModule, {
logger: ['error', 'warn', 'log'],
});
try {
const svc = app.get(PlanLabelService);
const before = await svc.countMissing();
log.log(`还没算过的依据:${before} 条`);
if (checkOnly) return;
const t0 = Date.now();
const written = all ? await svc.refreshAll() : await svc.backfillMissing();
const after = await svc.countMissing();
log.log(
`${all ? '全量重算' : '回填'}完成:写入 ${written} 条,` +
`耗时 ${((Date.now() - t0) / 1000).toFixed(1)}s,剩余未算 ${after} 条`,
);
// ⚠️ 回填完还有剩 = 有条写入路径没被覆盖到,值得当场查,⛔ 别等矩阵少人了才发现
if (!all && after > 0) {
log.error(`⚠️ 回填后仍有 ${after} 条没算过 —— 检查是否有新写入路径没接上补齐`);
process.exitCode = 1;
}
} finally {
await app.close();
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
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