1c4c163743
Plan 3 Task 7:真实 hesupipe 输入(WXR 5.5MB + 媒体 2625 文件/164.53MiB + 侧车 859 条) 全量导入验收证据——断言总表 21 项全过(content_entries=192 / source_records=1122 / terms=451+476 自建 / media_assets=836 全侧车二级命中+19 missing 全列 / routes=2772 逐桶闭合 / published 指针全程未动);换 key 二导全幂等(五表行数不变);临时库验后 DROP;storage/incoming 副本保留(Plan 4 构建用)。 Co-Authored-By: Claude Code <noreply@anthropic.com>
169 lines
16 KiB
Markdown
169 lines
16 KiB
Markdown
# 阶段 3 真实 hesupipe 全量导入验收证据(Plan 3 · Task 7)
|
||
|
||
**verdict:PASS**(断言总表 21 项全过;两处"计划预估值 vs 实测"差异均有完整对账解释,见 §7;无计数硬凑)
|
||
|
||
| 项 | 值 |
|
||
|---|---|
|
||
| 日期 | 2026-10-21 |
|
||
| 环境 | Windows 11 · Node v24.13.0 · pnpm 10.7.1 · tsx 直吃 TS 源码(sf CLI 同机制) |
|
||
| 数据库 | 本项目 PG(docker `sf-postgres`,postgres:16,宿主 localhost:5434)——**与执行进程同机**(计划钉死"DB 须同机/近端");临时库 `sf_phase3_hesupipe`(验后已 DROP,见 §8) |
|
||
| 输入 | 只读 cms 的三个真实输入,副本入本项目 `storage/incoming/`(WXR 副本 + robocopy 媒体副本 + Task 0 侧车原件) |
|
||
| 首跑 job | `45c71c5a-fc56-487a-91c2-38652da2d83b` · status `succeeded_with_issues` · 51,768.0ms |
|
||
| 二导 job | `8cbc9eee-2811-41b3-a8ba-378c5081e445`(`--key phase3-rerun-2`)· `succeeded_with_issues` · 20,271.1ms |
|
||
| 版本块 | adapter=1.0.0 / mapping=1.0.0 / sanitizer=1.0.0 / parser=saxes 6.0.0 |
|
||
| 站点锚点 | site `77a20c60-5267-427a-b2ea-09abbcbfc8df`(新建,origin=https://hesupipe.com,locale=en)· connection `f6b3bd1a-aaad-424c-bf5e-62aeb12d459f` · wxr 快照 `0aa7d5db-c8e2-4d76-8a45-921a61b32862` · media_bundle `d6d1282b-1340-469a-ad5e-7093e198a1a3` · manual_url_list `1f9d1495-55ec-4233-9e74-3d25d1c32634` |
|
||
| 报告产物 | `artifacts/import/45c71c5a…/reports/import.json`(首跑)、`artifacts/import/8cbc9eee…/reports/import.json`(二导)、`artifacts/register/20261021T204522-8vhybz/reports/register.json`(登记;artifacts/ 不入库,仅存本地) |
|
||
|
||
## 1. 副本与登记(耗时实测)
|
||
|
||
| 步骤 | 命令/方式 | 结果 | 耗时 |
|
||
|---|---|---|---|
|
||
| WXR 副本 | `cp D:/project/cms/storage/wxr/fd04d6ba8a1316b6.xml storage/incoming/hesupipe-wxr.xml` | 5,490,399 B;SHA-256 `f9bc5d44…b9f62` 与 Plan 1 基线一致 | 0.12s |
|
||
| 媒体目录副本 | `robocopy … /MIR` → `storage/incoming/hesupipe-media/` | **2625 文件 / 172,532,000 B(164.53 MiB)全部复制,0 失败**(exit=1=有文件复制);find 复核 2625 文件;抽查首文件 SHA-256 与源一致 | **10.59s**(robocopy 自报 16,757,187 B/s) |
|
||
| 临时库 | `docker exec sf-postgres psql -d postgres -c CREATE DATABASE sf_phase3_hesupipe` + `DATABASE_URL=… pnpm --filter @sf/api db:migrate` | 21 表全量迁移成功 | 8.06s(迁移) |
|
||
| 登记 | `pnpm sf source:register --site hesupipe-real --wxr … --media … --media-map … --origin https://hesupipe.com --locale en`(env `DATABASE_URL_TEST` 指临时库) | 三快照新建:wxr 17.1ms / **media_bundle 19,013.2ms**(2625 文件逐个 SHA-256 + content-addressed 副本)/ manual_url_list 9.8ms(副本已存在——Task 0 冒烟已落同哈希副本,内容寻址复用);manifest fileCount=2625、totalBytes=172,532,000 | 19,323.9ms(服务内) |
|
||
|
||
实际执行的命令原文(仓库根 `D:\project\cms-one`,Git Bash):
|
||
|
||
```bash
|
||
cp "D:/project/cms/storage/wxr/fd04d6ba8a1316b6.xml" storage/incoming/hesupipe-wxr.xml
|
||
robocopy "D:\project\cms\storage\media\54f91c49-fffb-4c58-87c8-6c2cf6aaef79" \
|
||
"D:\project\cms-one\storage\incoming\hesupipe-media" /MIR /R:2 /W:1 /NFL /NDL /NP
|
||
docker exec sf-postgres psql -U siteforge -d postgres -c "CREATE DATABASE sf_phase3_hesupipe;"
|
||
DATABASE_URL="postgres://siteforge:siteforge_dev@localhost:5434/sf_phase3_hesupipe" pnpm --filter @sf/api db:migrate
|
||
DATABASE_URL_TEST="postgres://siteforge:siteforge_dev@localhost:5434/sf_phase3_hesupipe" \
|
||
pnpm sf source:register --site hesupipe-real \
|
||
--wxr storage/incoming/hesupipe-wxr.xml \
|
||
--media storage/incoming/hesupipe-media \
|
||
--media-map storage/incoming/media-map-hesupipe.json \
|
||
--origin https://hesupipe.com --locale en
|
||
DATABASE_URL_TEST="postgres://siteforge:siteforge_dev@localhost:5434/sf_phase3_hesupipe" \
|
||
pnpm sf import --site hesupipe-real # 首跑(默认幂等键)
|
||
DATABASE_URL_TEST="postgres://siteforge:siteforge_dev@localhost:5434/sf_phase3_hesupipe" \
|
||
pnpm sf import --site hesupipe-real --key phase3-rerun-2 # 二导(换 key 重跑)
|
||
```
|
||
|
||
断言查询全部经 `docker exec sf-postgres psql -U siteforge -d sf_phase3_hesupipe -tAc "<SQL>"`(下称 SQL 对拍);报告数值取自两份 import.json(报告对拍)。
|
||
|
||
## 2. 首跑断言总表(预期 vs 实测 vs 判定)
|
||
|
||
预期来源:计划 Task 7 节 + Task 0/阶段 2 基线证据(`real-wxr-baseline.md`、`media-map-export.md`、`phase2-smoke.md`)。
|
||
|
||
| # | 断言项 | 预期 | 实测(报告 / SQL 双写) | 判定 |
|
||
|---|---|---|---|---|
|
||
| 1 | content_entries 总数 | **192** | 报告 contentEntries=192;SQL `count(*)`=192 | PASS |
|
||
| 2 | typeKey 分布 | 129 news_article + 48 product + 15 basic_page | SQL:news_article=129 / product=48 / basic_page=15 | PASS |
|
||
| 3 | source_records 总数 | **1122** | 报告 sourceRecords=1122;SQL=1122 | PASS |
|
||
| 4 | parse_status 分布 | imported=1047 / unsupported=75 | 报告 unsupportedRecords=75;SQL:imported=1047 / unsupported=75 | PASS |
|
||
| 5 | terms 总数 | 451 + 自建数 | SQL=**927** = 451 + 476 | PASS |
|
||
| 6 | 自建数(autoCreatedTerms) | **476**(category 5 nicename + post_tag 471 nicename,去重后建) | 报告 autoCreatedTerms=476;SQL `source_id LIKE 'auto:%'`=476(category 5 / post_tag 471,逐 taxonomy 吻合) | PASS |
|
||
| 7 | termsProcessed(wp:term 表侧) | 451 | 报告 451;SQL 非 auto 行=451(product_tag 439 + product_cat 10 + nav_menu 1 + elementor_library_type 1,与阶段 2 基线分层一致) | PASS |
|
||
| 8 | content_terms | 报告口径 | 报告 contentTermsCreated=1444;SQL=1444 | PASS |
|
||
| 9 | media_assets 总数 | **836 命中**(Task 0 精确命中预估) | 报告 mediaRegistered=836;SQL=836 | PASS |
|
||
| 10 | mediaMissing | **19**(Task 0 未命中清单全列) | 报告 19;issueGroups.media 逐条 19 条,URL 与 Task 0 清单**逐字一致**(§5.2) | PASS |
|
||
| 11 | mediaQuarantined | 实测 | 0(本数据集命中的 836 资产无 SVG);SQL status 全 ready | PASS(实测口径) |
|
||
| 12 | mediaAmbiguous | 0 | 0 | PASS |
|
||
| 13 | mediaDanglingRefs | 实测(gallery 悬空等) | **22**(明细 §5.3:外站热链 19 + 缺失资产 featured 引用 1 + WP 尺寸变体 2) | PASS(实测口径) |
|
||
| 14 | routes 总数 | 实测(计划预估 ~2815,见 §7 差异分析) | 报告 routesCreated=**2772**;SQL=2772 | PASS(对账闭合,§4) |
|
||
| 15 | route kind 分布 | html/redirect/asset 计数 | SQL:html=**190** / redirect=**1746** / asset=**836** | PASS(§4 全量对账) |
|
||
| 16 | excludedQueries | 实测 | 19(18 wp:post + 1 wp:product 的 `?p=` 形态链接——不插行+计数+issue) | PASS |
|
||
| 17 | published_revision_id 非空数 | **0**(导入不发布) | SQL=0(current 指针 192 非空、published 0) | PASS |
|
||
| 18 | job 终态 | succeeded_with_issues(attachment 降级汇总必在) | `succeeded_with_issues`;issueGroups.route 末条即"attachment 页降级(前置事实 10):836 条 …"汇总 | PASS |
|
||
| 19 | 清洗统计(与阶段 2 probe 一致) | changedRows=138 / removed-tag=3 / removed-attr=4667 | 报告 sanitize:changedRows=138 / removed-tag=3 / removed-attr=4667 | PASS |
|
||
| 20 | revisionsCreated(首跑) | 192(干净库首跑每 entry 一修订) | 报告 192;SQL content_revisions=192(source_kind=import 全量) | PASS |
|
||
| 21 | media_usages | 实测 | SQL=478(attachmentRefs 92 + data.bodyHtml.img 386) | PASS(实测口径) |
|
||
|
||
附:issues 总量 206 = content 0 + term 2 + media 41 + route 40 + adapter 123 + orchestrator 0(明细 §5);skipped=0(干净库首跑无可跳过修订)。
|
||
|
||
## 3. 二导幂等断言(换 key `phase3-rerun-2`)
|
||
|
||
| 断言项 | 预期 | 实测 | 判定 |
|
||
|---|---|---|---|
|
||
| revisionsCreated | 0 | **0** | PASS |
|
||
| contentEntries | 0 | **0**(skipped=192——checksum 全等跳过,幂等路径正确显影) | PASS |
|
||
| mediaRegistered | 0 | **0**(mediaFromSidecar 亦 0) | PASS |
|
||
| routesCreated / routesUpdated | 0 / 0 | **0 / 0** | PASS |
|
||
| autoCreatedTerms / contentTermsCreated | 0 | **0 / 0** | PASS |
|
||
| sourceRecords | 0 | **0**(unsupportedRecords=75 重现——unsupported 行无身份键去重语义,重跑如实再计,非缺陷) | PASS |
|
||
| routesCollisions | 重跑如实再现(非缺陷) | 首跑即 0,二导仍 0(本数据集无 collision_key 折叠碰撞) | PASS |
|
||
| missing/dangling/excluded 重现 | 如实再现非缺陷 | mediaMissing=19 / mediaDanglingRefs=22 / routesExcludedQueries=19 均原样再现;issues 总量仍 206 | PASS |
|
||
| 五表行数不变 | 不变 | content_entries=192 / content_revisions=192 / terms=927 / media_assets=836 / route_registry=2772 **全部不变**(另 source_records=1122 / content_terms=1444 / media_usages=478 / entry_locales=192 / taxonomies=6 亦不变) | PASS |
|
||
| published 指针 | 仍 0 | SQL=0 | PASS |
|
||
|
||
## 4. 路由处置全量对账(2772 行逐桶闭合)
|
||
|
||
| 桶 | 行数 | 构成(SQL 分组实测) |
|
||
|---|---|---|
|
||
| html | 190 | 内容 permalink 173(news_article 111 + product 47 + basic_page 15)+ 首页 `/` 1 + `/news/` 列表 1 + category 归档 5 + product_cat 归档 10;`/products/` 行存在但 target 是**内容页**(见冲突说明) |
|
||
| redirect | 1746 | attachment 页 301→媒体文件 **836**(路径形态 812 + `?attachment_id=` 查询形态 24——query_policy=exact 的 24 行全在此桶)+ `/tag/` 471→`/news/` + `/product-tag/` 439→`/products/` |
|
||
| asset | 836 | `/wp-content/uploads/…` → target_file(836 注册资产一一对应) |
|
||
| 合计 | **2772** | 190+1746+836 ✓;query_policy:exact=24 / ignore=2748;host 单值 `hesupipe.com`(=production_origin);target_status:200×1026 / 301×1746 |
|
||
|
||
闭环核算:192 entry − 173 有路由 = 19 无 html 路由的 entry = 18 篇 `?p=` 链接的 post + 1 个 `?post_type=product&p=` 链接的 product(source_url 逐条 SQL 核对)= routesExcludedQueries 19——**数目与身份双侧闭合**。
|
||
|
||
冲突说明(1 条 issue,非计数桶):`路由 exact_key 冲突(hesupipe.com/products/)`——basic_page "products" 页(先到,保留)vs CLI 主列表参数 `/products/`(后到,跳过)。routesCollisions 计数桶只计 **collision_key 折叠碰撞**(ADR-007 双键语义,本数据集 0 条);exact_key 异 owner 冲突走 issue 通道不计该数——两通道语义在 route-writer 源码分离(writeRoute 的碰撞预警分支 vs owner 冲突分支),实测与设计一致。
|
||
|
||
## 5. 问题清单全量分层(206 条)
|
||
|
||
### 5.1 adapter 123 = 不支持对象 75 + 行级问题 48
|
||
|
||
- unsupported 75:wp:nav_menu_item 68 + wp:wpcf7_contact_form 3 + wp:elementor_library 2 + wp:wp_navigation 2——与阶段 2 基线分布逐项一致。
|
||
- 行级问题 48:全部为 `wp:product` 的 `meta wppop_sections PHP 解析失败`(WP 页面构建器序列化元数据,PhpParseError 摘录入 issue、原串保留于 extras.phpRaw)——降级不丢数据。
|
||
|
||
### 5.2 media 41 = missing 19 + 悬空 22
|
||
|
||
missing 19 条 URL 与 Task 0 `media-map-export.md` 未命中清单**逐字一致**(8×pdf/txt/zip + 5×mp4 + 5×`、`特殊字符 jpg + 1×全角括号 jpg 形态),全部"清单与侧车均未命中——不插 media_assets 行(sha256 无来源)"。
|
||
|
||
### 5.3 悬空引用 22 条明细特征(实测口径,如实记录)
|
||
|
||
| 特征 | 条数 | 说明 |
|
||
|---|---|---|
|
||
| 外站热链(hesucar.com 2 + www.cn-hdpe.com 17) | 19 | 正文 img 指向姊妹/外站图床,WXR 无 attachment 行亦不在清单——真悬空,报告保留 |
|
||
| featured 引用缺失资产 attachment 3803 | 1 | 即 missing 19 之一的 `_thumbnail_id` 引用(`HDPE-carat-pipe-(2).jpg`) |
|
||
| 本站 WP 尺寸变体(`-1024x677.png` 等) | 2 | WP 生成的中间尺寸图无独立 attachment 行 |
|
||
|
||
quarantined/unsupported 资产的引用不落任何 count 桶(Task 4 口径)——本数据集 quarantined=0,无该形态;侧车 23 条悬空行(859−836)如 Task 0 预告不出现在 mediaDanglingRefs(只能经 issues 可见),实测一致。
|
||
|
||
### 5.4 term 2 = 未知 taxonomy 兜底
|
||
|
||
`nav_menu`、`elementor_library_type` 不在前置事实 9 常量表——object_type 兜底 'post'、`/<key>/` 兜底路径,仅记录不消费(既定行为)。
|
||
|
||
### 5.5 route 40 = 查询排除 19 + exact 冲突 1 + attachment 301 目标缺席 19 + 降级汇总 1
|
||
|
||
attachment 301 目标缺席 19 = 19 个 missing attachment 无 media_assets 行故无路由(与 mediaMissing 同源闭合)。
|
||
|
||
## 6. 三级对拍命中分层(媒体核心验收)
|
||
|
||
| 级 | 结果 | 说明 |
|
||
|---|---|---|
|
||
| 级① 清单精确路径(uploads yyyy/mm 相对路径) | **0 命中** | 媒体目录为扁平 GUID 命名(Task 0 实证),WXR attachment_url 的 yyyy/mm 路径与清单 path 零交集——符合预告 |
|
||
| 级② 侧车(manual_url_list 859 条) | **836 命中(mediaFromSidecar=836)** | 侧车 file_path(GUID 扁平名)作为精确 path 命中清单实体;歧义 0;侧车二级的"文件名候选"子路径 0 次启用 |
|
||
| 级③ missing | 19 | 全列报告(§5.2),不插行 |
|
||
|
||
**mediaRegistered 首跑口径**(Task 4 移交):836 全部来自干净库首跑插入;二导 mediaRegistered=0。836+19=855 attachment 全量闭合;魔数全检(836 全 ready、0 隔离、0 unknown、0 声明不符 issue)。抽查:DB 行 sha256 与 content-addressed 存储副本文件哈希一致(map-location.jpg 例:`0fd30b3c…da87`)。
|
||
|
||
## 7. 差异分析(如实记录,无硬凑)
|
||
|
||
1. **routes 总数 2772 vs 计划预估 ~2815**:预估为规划期粗算(含首页/列表/归档/attachment 各桶上限假设)。实测 2772 的差额可完整解释:预估未扣除 19 条 `?p=` 排除(不插行)与 `/products/` 列表页 exact 冲突 1 条(不插行),且归档桶按 term 总数 925 上估而实际 tag 归档 910(category 5/product_cat 10 走 html 归档非 redirect)。2772 = 全部输入身份的逐桶闭合(§4),无数值缺陷。
|
||
2. **首跑耗时 51.8s vs 估 7-10s(~9300 RT)**:估算口径是批量 DB 写往返;实际编排管道为**逐事件串行分派 + 逐引用交互式查询**(1122 事件 × 四写入器 + 每 taxonomyRefs 引用的 entry 锚点解析 + 每路由 payload 的 exact 预查),交互式往返数为事件数与引用数的乘积级,非 9300 常数。DB 确同机(localhost:5434);无超时/失败;二导全幂等路径仍 20.3s 同因。属口径差异非性能缺陷;若后续需要提速,优化方向是批量锚点解析(已具备结构条件),不在本任务范围。
|
||
3. **mediaFromSidecar=836(级① 0 命中)**:任务提示"836 精确命中预期 + 0 侧车二级?"——实测全命中经侧车(级②),级① 0。与 Task 0 实证(扁平 GUID 目录 × yyyy/mm URL 零精确交集)完全一致,非缺陷。
|
||
4. **unsupportedRecords 二导再现 75**:unsupported 行不走身份键幂等(无 entry 语义),重跑如实重计——计划"missing/quarantined/dangling 重跑如实再现"同族语义,非缺陷。
|
||
5. term issues(未知 taxonomy 兜底)与行级问题(wppop PHP 解析)二导原样再现——issue 通道本就无去重契约,非缺陷。
|
||
|
||
## 8. 清理与保留声明
|
||
|
||
- **临时库已 DROP**:`docker exec sf-postgres psql -U siteforge -d postgres -c "DROP DATABASE sf_phase3_hesupipe WITH (FORCE);"`(验后执行;本项目开发库 `siteforge` 全程零写入——所有 sf 命令均经 `DATABASE_URL_TEST` 指向临时库)。
|
||
- **`storage/incoming/` 副本保留**(WXR 副本 + 媒体副本 172,532,000 B + 侧车,共约 345MB 本地盘)——Plan 4 构建要用;`storage/` 整体 gitignore 不入库。登记期的 content-addressed 快照(`storage/snapshots/`,另约 172MB)随 storage/ 一并保留。
|
||
- cms 源侧(`D:\project\cms`)全程只读:cp/robocopy/sha256sum/find 读源目录,未写未挂载未改配置;旧 cms PostgreSQL(5433 容器)本任务零连接。
|
||
|
||
## 9. 内存与耗时汇总
|
||
|
||
| 指标 | 值 |
|
||
|---|---|
|
||
| 首跑 job durationMs | **51,768.0ms**(CLI 墙钟 53.6s) |
|
||
| 首跑 adapterTiming | parseMs=121.4 / sanitizeMs=235.0(parseMs 含流式 IO 等待口径,见阶段 2 证据) |
|
||
| 二导 job durationMs | 20,271.1ms(墙钟 22.0s) |
|
||
| 登记 durationMs | 19,323.9(media_bundle 快照 19,013.2 为主——2625 文件哈希+副本) |
|
||
| robocopy 媒体副本 | 10.59s(2625 文件 / 164.53 MiB) |
|
||
| 内存 | 峰值 **319,258,624 B ≈ 304.3 MiB**(进程集 WorkingSet 之和,500ms 采样,`node sf.ts` 进程过滤——含 tsx 加载器与子进程,上界口径,仅记录不断言) |
|