docs(spec): 按评审意见修复规格五项问题
- 媒体缺失两级处置(悬空引用=构建失败;文件缺失=门禁🔴) - 发布=条目级状态指针更新,修订不可变性不受影响 - 路由表补 /products/ /news/ 主列表页;门禁查 redirect 目标存在性 - 作废引用清理(V1.0→V2.0/V3.0 §2.1);scripts 补 publish.ts - 采纳建议:sitemap 改手动文件输入(零服务端抓取)、保留设备名检查、 报告持久化 artifacts/jobs/、悬空外键重映射 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
> 日期:2026-10-20|状态:待评审
|
||||
> 上位文档:`SiteForge_完整项目计划方案_V3.0.md`(执行基线)
|
||||
> 姊妹文档:`cms现状与SiteForge差距分析_V1.0.md`(D:\project\cms 的教训清单)
|
||||
> 姊妹文档:`cms现状与SiteForge差距分析_V2.0.md`(D:\project\cms 的现状基线与教训清单;V1.0 差距分析已作废)
|
||||
> 本文决策已经用户逐项确认(建设顺序、切片范围、数据、路线、九项修正)。
|
||||
|
||||
---
|
||||
@@ -61,9 +61,9 @@ WXR 导入 → 统一内容模型 → URL 注册表 → 极简主题 → 静态
|
||||
cms-one/
|
||||
apps/api/ # NestJS:sites/content/routes/media/import/build/audit 模块
|
||||
packages/contracts/ # UniversalRow、内容 DTO、路由 DTO(冻结+契约测试)
|
||||
packages/adapter-wordpress/ # WXR 解析器 + WP REST 客户端(REST 通道切片内可选)
|
||||
packages/adapter-wordpress/ # WXR 解析器(WP REST 客户端为后续阶段实现,本切片不含)
|
||||
themes/pilot-minimal/ # 极简 Astro 主题
|
||||
scripts/ # CLI:import.ts / build.ts / gate.ts / preview.ts
|
||||
scripts/ # CLI:import.ts / publish.ts / build.ts / gate.ts / preview.ts
|
||||
storage/wxr/ storage/media/ # 试点数据副本(git 忽略)
|
||||
infra/docker-compose.yml # 仅 PostgreSQL
|
||||
tests/fixtures/ # WXR/媒体夹具
|
||||
@@ -83,6 +83,7 @@ cms-one/
|
||||
|---|---|---|
|
||||
| 内容页 permalink(含 `.html`/`.php` 后缀、任意目录深度) | **产物按原路径原样输出**(路径级保真,非仅 slug) | html |
|
||||
| 首页 `/` | 首页 | html |
|
||||
| 主列表页 `/products/`、`/news/`(含其 `/page/N/` 分页) | 构建固定生成的主列表页路由(站点配置声明其路径) | html |
|
||||
| `product-category/{slug}`(产品分类) | 生成分类列表页,路径原样 | html |
|
||||
| `category/{slug}`(文章分类) | 生成文章分类列表页 | html |
|
||||
| 列表分页 `/page/N/`(含分类下分页) | 列表页分页输出 | html |
|
||||
@@ -99,13 +100,13 @@ cms-one/
|
||||
|
||||
### 5.3 URL 对照报告(门禁输入)
|
||||
|
||||
老 URL 全集 = WXR 可推导 URL(内容 permalink ∪ 术语归档 ∪ 分页推导)∪ 老站 sitemap.xml(若可拉取;被挑战页拦截则记说明)。三色口径与 cms 一致:🟢 产物存在同路径页面|🔵 route_registry 有 redirect|🔴 无去处(必须为 0)。
|
||||
老 URL 全集 = WXR 可推导 URL(内容 permalink ∪ 术语归档 ∪ 分页推导)∪ 手动放置的老站 sitemap.xml 文件(`storage/sitemap/`,随 WXR 一起复制;未提供则记说明)。**本切片不做任何服务端抓取(SSRF 面为零)**。三色口径与 cms 一致:🟢 产物存在同路径页面|🔵 route_registry 有 redirect|🔴 无去处(必须为 0)。
|
||||
|
||||
### 5.4 媒体旧路径保持
|
||||
|
||||
- `media_assets.source_url` 存旧完整 URL,`storage_path` 指向本地副本文件,`public_path` = 旧 URL 路径部分(`/wp-content/uploads/...`)。
|
||||
- 构建时媒体复制到产物的 `public_path` 位置;正文 `<img>`、封面、相册引用全部重写为根相对 `public_path`。
|
||||
- 未匹配到本地副本的媒体:列入导入报告(引用它的页面数、URL 清单),不阻塞导入;若被已发布页面引用则门禁降级为 🔴 由用户决定补图或改链。
|
||||
- **媒体缺失分两级处置**(消除歧义):① 内容引用的媒体 URL **不在 media_assets 注册表中**(悬空引用)→ **构建失败**(属输入不完整,fail-fast);② 已注册但**本地文件缺失** → 导入报告列出(引用页面数、URL 清单),不阻塞导入,门禁标 🔴,由用户决定补图或改链。
|
||||
- 登记时校验文件魔数与声明 MIME 一致,不一致入报告;**SVG 一律隔离**(不复制进产物、预览不可达),报告中单列清单。
|
||||
|
||||
## 6. 管道命令与数据流
|
||||
@@ -118,7 +119,7 @@ cms-one/
|
||||
→ locale 取站点配置;未知 locale 记错误并跳过该条(禁止静默归 en——cms 实际踩坑)
|
||||
→ 每条计算 normalized checksum:
|
||||
与最新 content_revision.checksum 相同 → skip(不产生新修订)
|
||||
不同或不存在 → 写入新 content_revision(kind=draft,不可变,source=import)
|
||||
不同或不存在 → 写入新 content_revision(不可变,source=import;发布状态不写在修订上,见 6.2)
|
||||
※ 导入不发布:发布是独立显式动作(见 6.2,V2.0 P0-6)
|
||||
→ 媒体登记:按 WXR attachment 的 source_url 与本地副本文件名匹配;
|
||||
全角/编码损坏文件名按规范化通配匹配(cms 踩坑),匹配结果写报告
|
||||
@@ -132,10 +133,10 @@ cms-one/
|
||||
|
||||
### 6.2 `scripts/publish.ts`(显式发布)
|
||||
|
||||
导入只产生 draft;发布是独立、显式、可审计的动作(V2.0 P0-6:禁止"迁移即发布"):
|
||||
导入只产生未发布内容;发布是独立、显式、可审计的动作(V2.0 P0-6:禁止"迁移即发布"):
|
||||
|
||||
- 按批次/类型/条目批量将 draft 修订置为 published(更新 entry_locales.published_revision_id 指针)。
|
||||
- 每次发布动作写 audit_logs(actor、对象、前后状态、时间)。
|
||||
- **发布 = 更新条目级状态,不动修订**:content_entries.status → published,entry_locales.published_revision_id 指向当前修订。content_revisions 永不 UPDATE——不可变约束不因发布破坏(消除"修订上改 kind"的歧义)。
|
||||
- 按批次/类型/条目批量执行;每次发布动作写 audit_logs(actor、对象、前后状态、时间)。
|
||||
- 构建只消费 published 修订集合——与 cms「导入后逐条自动发布」的行为刻意相反。
|
||||
|
||||
### 6.3 `scripts/build.ts`(构建)
|
||||
@@ -162,10 +163,12 @@ Astro 构建(themes/pilot-minimal):
|
||||
| 内部死链 | 产物内 href/src/srcset 指向的内部目标全部存在(html/asset 命中 route_registry) |
|
||||
| sitemap 一致 | sitemap 条目 ⊆ 实际页面;indexable 页面 ⊆ sitemap |
|
||||
| 页面 SEO 基线 | 每页有 title、canonical;H1 至多 1 个 |
|
||||
| 大小写冲突 | 产物相对路径 lowercase 后无重复(Windows 开发盘不敏感、Linux 部署敏感——V3.0 §6.5) |
|
||||
| 重定向图 | route_registry 的 redirect 关系无环;关键路径多跳(≥3 跳)告警(V2.0 §5.3:🔴=0 不代表无环) |
|
||||
| 大小写冲突 | 产物相对路径 lowercase 后无重复,且不含 Windows 保留设备名(CON/PRN/AUX/NUL/COM1…——V3.0 §6.5) |
|
||||
| 重定向图 | redirect 关系无环;**每个 redirect_location 必须解析为已存在的 html/asset 路由**;关键路径多跳(≥3 跳)告警(V2.0 §5.3:🔴=0 不代表无环、目标存在) |
|
||||
| 兜底数据回归 | 输入损坏时构建必须失败(负向测试) |
|
||||
|
||||
导入与门禁报告均持久化至 `artifacts/jobs/<jobId>/`(JSON + HTML 双格式),§11.5 的"可复查"即指此目录。
|
||||
|
||||
### 6.5 `scripts/preview.ts`(预览)
|
||||
|
||||
自研 Node 静态服务器(不引入 nginx 依赖):
|
||||
@@ -183,8 +186,8 @@ Astro 构建(themes/pilot-minimal):
|
||||
| import_jobs / source_records | 批次状态机;源记录存原始条目位置 + checksum + 解析状态 |
|
||||
| content_entries | (site_id, type_key, external_ref) 唯一;status: draft/published/archived |
|
||||
| entry_locales | (site_id, locale, slug) 唯一;current/published_revision_id 指针 |
|
||||
| content_revisions | **不可变**(插入式,禁 UPDATE);data jsonb、checksum、source=import、import_batch_id;预留 `overridden_fields`(下阶段冲突队列用) |
|
||||
| route_registry | §5.2 字段;独立表 |
|
||||
| content_revisions | **不可变**(插入式,禁 UPDATE);data jsonb、checksum、source=import、import_job_id(→ import_jobs);预留 `overridden_fields`(下阶段冲突队列用) |
|
||||
| route_registry | §5.2 字段(其中来源指向 `source_import_job_id` → import_jobs);独立表 |
|
||||
| media_assets | (site_id, source_url) 唯一幂等;sha256、public_path(旧路径)、alt、尺寸 |
|
||||
| build_jobs / artifacts | 构建记录 + 产物目录 + 输入快照哈希 |
|
||||
| audit_logs | 追加式;actor(切片为 system/importer)、action、对象、前后值 |
|
||||
@@ -246,4 +249,4 @@ SP4' 部署器(rsync/SSH → OSS/CDN)+ 发布锁/回滚
|
||||
SP5' 表单服务 + 备份监控
|
||||
```
|
||||
|
||||
每阶段对照 `cms现状与SiteForge差距分析_V1.0.md` 的 17 项清单打钩。
|
||||
每阶段对照 **V3.0 §2.1 的 17 项必须交付能力**打钩(评级基线见 V2.0 §4)。
|
||||
|
||||
Reference in New Issue
Block a user