feat(import): 全链编排与导入报告
Task 6(Plan 3):四家写入器汇流为默认编排管道。 - import.service:注入 events/pipeline 缺席时走真实事件流(runWxrAdapter 惰性 async generator,mapConfig.locale=site.default_locale 前置事实 8); 分派严格串行 content→term→media→route(禁 Promise.all);finish 硬序 term(sync)→content(sync)→media(await)→route(await);counts 由 ⊕ 覆盖 改为逐 key 求和聚合;新增 stale 回收(同站点 queued/running >24h 一条 SQL 置 stale);报告新增 paths/productsPath+newsPath、issueGroups 六组、 sanitize 清洗统计(事件流 changedRows/byDiffType 聚合)、adapterTiming。 - content-writer:NULL-entry 收编(快照行在而 content_entry 空,五列先验 三分支:无先验新建挂接/同 checksum 挂接 skipped/异 checksum 挂接新版本)。 - route-writer:attachment 降级汇总按去重 sourceId 计(重跑仍必现); normalizeListPath 导出;writeAttachmentRoute 返回 written/resolved/blocked。 - 四家写入器 identity 收敛共享基类型 WriterContext = Pick<ImportJobContext,'db'|'siteId'|'connectionId'>(type-only import, 无运行时环依赖)。 - CLI import 增 --products-path/--news-path(前置事实 9,缺省 /products/ /news/)记入 job 报告。 - 测试:tests/integration/import-full.test.ts 6 例(全链 zh-CN 接线证明 +published 全 NULL/报告四域计数+sanitize+分组/幂等重跑零写语义/ 无 media 快照域缺失不失败/NULL-entry 收编/stale 回收/REPORT_WRITE_FAILED 遗留补测);import-job 契约测试钉死 counts 求和(100⊕3=103)。 全量 323 通过;typecheck/lint 干净。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
+18
-4
@@ -2,7 +2,7 @@
|
||||
// sf CLI(Plan 2 Task 7 + Plan 3 Task 1):commander 三命令——
|
||||
// - wxr:probe:只读诊断(任意可读路径;Task 8 直读 cms 用此通道),出 JSON/HTML 报告不入库;
|
||||
// - source:register:调 registerSource 登记 content-addressed 快照(路径白名单校验在服务内);
|
||||
// - import:调 createImportJob 建 import_jobs 并推进生命周期(Task 1 空管道骨架;Task 6 接真实管道)。
|
||||
// - import:调 createImportJob 建 import_jobs 并推进生命周期(Task 6:默认编排管道——四家写入器汇流)。
|
||||
// 执行机制(Plan 2 评审钉死):根 devDep tsx 直吃 TS 源码(含 apps/api 的 Nest 源码与
|
||||
// packages 的装饰器/TS 源文件),@sf/* 经根/包 tsconfig paths 直连 src——无需预构建 dist。
|
||||
// 注意:CLI 只 import register.service.ts / import.service.ts(零装饰器纯函数文件),
|
||||
@@ -163,16 +163,30 @@ program
|
||||
|
||||
program
|
||||
.command('import')
|
||||
.description('导入已登记的 wxr 快照(Task 1 空管道骨架:job 生命周期 + 报告;真实管道 Task 6 接入)')
|
||||
.description('导入已登记的 wxr 快照(Task 6 编排管道:term/content/media/route 四家写入器汇流 + 导入报告)')
|
||||
.requiredOption('--site <key>', '站点 key')
|
||||
.option('--snapshot <id>', 'wxr 快照 id(缺省按 connection+kind=wxr 选最新)')
|
||||
.option('--key <key>', '幂等键(缺省 = site+snapshot+版本块 自动派生;重复提交同 key 复用既有 job 不重跑)')
|
||||
.option('--connection <name>', '连接名(快照解析的 connection 侧,缺省 default)', 'default')
|
||||
.action(async (opts: { site: string; snapshot?: string; key?: string; connection: string }) => {
|
||||
.option('--products-path <path>', '产品主列表页路径(前置事实 9,规范化值记入 job 报告)', '/products/')
|
||||
.option('--news-path <path>', '新闻主列表页路径(同上)', '/news/')
|
||||
.action(async (opts: {
|
||||
site: string
|
||||
snapshot?: string
|
||||
key?: string
|
||||
connection: string
|
||||
productsPath: string
|
||||
newsPath: string
|
||||
}) => {
|
||||
// getDb 走 DATABASE_URL_TEST ?? DATABASE_URL 回退链——临时库冒烟只需 env 覆盖
|
||||
const db = getDb()
|
||||
// exactOptionalPropertyTypes:缺席键不传 undefined 值(与 source:register 同款)
|
||||
const input: CreateImportJobInput = { siteKey: opts.site, connectionName: opts.connection }
|
||||
const input: CreateImportJobInput = {
|
||||
siteKey: opts.site,
|
||||
connectionName: opts.connection,
|
||||
productsPath: opts.productsPath,
|
||||
newsPath: opts.newsPath,
|
||||
}
|
||||
if (opts.snapshot !== undefined) input.snapshotId = opts.snapshot
|
||||
if (opts.key !== undefined) input.idempotencyKey = opts.key
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user