docs: SiteForge 规划文档基线 + 纵向切片设计规格 V1

- V3.0 总体方案、主题与板块设计、实施计划
- cms 现状差距分析 V2.0(V1.0 已标注作废)与复用评估 V1.0
- 纵向切片设计规格(路线 C:全新架构 + 白名单资产移植)
- .gitignore 先行(storage/artifacts/env 永不入库)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-20 16:10:34 +08:00
commit d60123ba41
8 changed files with 5746 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
# 依赖与构建产物
node_modules/
dist/
.artifacts/
artifacts/
coverage/
.turbo/
# 试点数据与本地状态(WXR/媒体副本、数据库、密钥——绝不入库)
storage/
.data/
.env
.env.*
*.local
# 日志与临时文件
*.log
tmp/
.DS_Store
@@ -0,0 +1,900 @@
# SiteForge 主题与板块设计方案
> 版本:V1.0
> 日期:2026-10-20
> 目标:支持每个网站拥有独立设计,同时让内容可以在后台结构化编辑并生成纯静态页面。
---
## 1. 设计结论
主题和板块必须解耦:
- **主题(Theme)**负责页面结构、视觉风格、HTML、CSS、动画和响应式实现。
- **板块(Block)**负责内容字段及其顺序,例如标题、图片、按钮、产品参数、FAQ。
- **模板(Template)**规定一类页面默认由哪些区域组成,以及允许使用哪些板块。
- **页面(Page)**保存所选模板和实际板块数据。
推荐模式是:
```text
统一板块数据协议 + 每个主题自己的板块渲染器
```
例如后台都保存 `hero` 数据,但汽车站可以显示全屏视频,工业站可以显示左文右图。两者共享内容语义,不共享强制布局。
V1 不做任意坐标、任意嵌套的低代码编辑器,只提供:
- 选择板块。
- 表单编辑字段。
- 调整板块顺序。
- 启用或隐藏板块。
- 选择少量经过主题声明的样式变体。
- PC/移动端静态预览。
这样既能快速维护,又不会让自由配置破坏设计、响应式和 SEO。
---
## 2. 四层结构
```mermaid
flowchart TB
CONTENT[内容:产品/文章/页面/媒体]
BLOCK[板块实例:type + data + settings]
TEMPLATE[页面模板:区域与板块约束]
THEME[主题:组件、样式、交互]
HTML[静态 HTML/CSS/JS]
CONTENT --> BLOCK
BLOCK --> TEMPLATE
TEMPLATE --> THEME
THEME --> HTML
```
### 2.1 内容层
保存可复用的业务数据,例如产品名称、参数、文章正文和媒体资源。内容不应该写死在主题代码里。
### 2.2 板块层
保存某个页面中的展示内容和顺序。板块数据尽量不包含颜色、像素、CSS 类名等主题实现细节。
### 2.3 模板层
定义首页、产品详情、新闻详情、普通页面等页面类型及允许的区域。模板可以自动插入由业务数据驱动的系统区域。
### 2.4 主题层
每个主题提供自己的 Astro 页面、组件、样式和板块渲染器,最终生成静态文件。
---
## 3. 板块分类
### 3.1 全局板块
全站复用,单独管理:
- Header
- Footer
- 顶部通知条
- 全局询价 CTA
- 联系方式
- 社交媒体链接
- Cookie/隐私提示
修改全局板块后,会重建所有引用它的页面;V1 可以直接触发该站全量构建。
### 3.2 页面内容板块
由管理员在页面中新增、排序和编辑:
- Hero
- Rich Text
- Image + Text
- Image Gallery
- Video
- Feature List
- Statistics
- Logo/Brand List
- Product Grid
- Article List
- Specification Table
- Download List
- FAQ
- Testimonials
- CTA
- Contact Information
- Map Embed
### 3.3 模板系统板块
由模板根据当前内容自动生成,通常不允许页面编辑者删除:
- 面包屑
- 产品主信息
- 产品参数
- 文章标题、日期和正文
- 分类列表与分页
- 相关推荐
- SEO Head
管理员可以在允许范围内控制显示、顺序或样式变体,但不直接维护其核心业务数据。
### 3.4 站点专属板块
某些页面效果只属于一个站点,可以由主题声明自定义板块,例如:
```text
vehicle-360-view
charging-calculator
factory-process-timeline
dealer-map
```
站点专属板块仍需提供数据 schema 和后台表单定义,不能只在代码中写死内容。
### 3.5 Legacy HTML 板块
用于暂时无法结构化的历史内容:
- 只接受清洗后的 HTML。
- 删除脚本、事件属性和危险 URL。
- 使用隔离样式容器。
- 后台醒目标记为“待结构化”。
- 不能作为新页面的默认编辑方式。
---
## 4. 板块数据协议
### 4.1 板块实例
```ts
export interface BlockInstance<TData = Record<string, unknown>> {
id: string;
type: string;
schemaVersion: number;
variant?: string;
data: TData;
settings?: {
anchorId?: string;
themeMode?: 'default' | 'light' | 'dark';
spacing?: 'none' | 'small' | 'medium' | 'large';
};
visibility: {
enabled: boolean;
locales?: string[];
};
}
```
### 4.2 页面内容示例
```json
{
"contentId": "page_home",
"templateKey": "home",
"blocks": [
{
"id": "block_hero_01",
"type": "hero",
"schemaVersion": 1,
"variant": "image-right",
"data": {
"eyebrow": "New Energy Vehicles",
"title": "Build Your Next Journey",
"description": "A short introduction.",
"mediaId": "asset_1001",
"primaryAction": {
"label": "View Products",
"href": "/products/"
}
},
"settings": {
"spacing": "large",
"themeMode": "dark"
},
"visibility": {
"enabled": true
}
}
]
}
```
### 4.3 内容和样式边界
建议保存在板块数据中的字段:
- 标题、描述和按钮文案。
- 媒体资源引用。
- 内部/外部链接。
- 产品、文章、分类等内容引用。
- 允许的语义变体,例如 `featured`、`compact`。
不建议保存:
- 任意 CSS。
- Tailwind/CSS 类名。
- 像素坐标。
- 随意输入的颜色值。
- 主题组件文件路径。
- 可执行 JavaScript。
颜色、字体和间距默认由主题控制。如果确实需要运营配置,只允许主题 manifest 中声明的有限选项。
---
## 5. 板块定义与表单生成
每个板块类型包含两份声明:
1. `schema`:数据校验规则。
2. `uiSchema`:后台如何展示编辑表单。
### 5.1 Hero Schema 示例
```json
{
"$id": "siteforge://blocks/hero/v1",
"type": "object",
"required": ["title"],
"properties": {
"eyebrow": { "type": "string", "maxLength": 80 },
"title": { "type": "string", "minLength": 1, "maxLength": 160 },
"description": { "type": "string", "maxLength": 500 },
"mediaId": { "type": "string" },
"primaryAction": {
"type": "object",
"properties": {
"label": { "type": "string", "maxLength": 40 },
"href": { "type": "string" }
},
"required": ["label", "href"]
}
},
"additionalProperties": false
}
```
### 5.2 UI Schema 示例
```json
{
"title": { "widget": "text", "label": "主标题" },
"description": { "widget": "textarea", "label": "说明" },
"mediaId": { "widget": "media-picker", "label": "主视觉" },
"primaryAction.href": { "widget": "link-picker", "label": "按钮链接" }
}
```
管理后台根据这些声明生成表单,不需要为每个主题重复开发一套内容编辑页面。
推荐使用 JSON Schema 作为跨前后端协议,并使用 Ajv 校验。主题内可以生成对应 TypeScript 类型,避免重复维护接口。
---
## 6. 主题协议
### 6.1 主题清单
```json
{
"id": "automotive-premium",
"name": "Automotive Premium",
"version": "1.0.0",
"engine": "astro",
"siteConfigSchema": "./schemas/site-config.schema.json",
"templates": {
"home": {
"entry": "src/templates/HomePage.astro",
"allowedBlocks": ["hero", "feature-list", "product-grid", "cta"],
"requiredBlocks": ["hero"],
"maxPerType": { "hero": 1 }
},
"product": {
"entry": "src/templates/ProductDetail.astro",
"allowedBlocks": ["gallery", "specification-table", "download-list", "faq", "cta"]
},
"page": {
"entry": "src/templates/GenericPage.astro",
"allowedBlocks": ["hero", "rich-text", "image-text", "gallery", "faq", "cta"]
}
},
"blocks": {
"hero": {
"renderer": "src/blocks/HeroBlock.astro",
"schema": "siteforge://blocks/hero/v1",
"variants": ["fullscreen", "image-right", "minimal"]
},
"product-grid": {
"renderer": "src/blocks/ProductGridBlock.astro",
"schema": "siteforge://blocks/product-grid/v1",
"variants": ["cards", "editorial"]
}
}
}
```
### 6.2 主题目录
```text
themes/automotive-premium/
theme.json
schemas/
site-config.schema.json
src/
templates/
HomePage.astro
ProductDetail.astro
ArticleDetail.astro
GenericPage.astro
blocks/
HeroBlock.astro
RichTextBlock.astro
ProductGridBlock.astro
FaqBlock.astro
components/
Header.astro
Footer.astro
Breadcrumb.astro
layouts/
BaseLayout.astro
styles/
tokens.css
global.css
lib/
block-renderers.ts
public/
```
### 6.3 不使用主题继承
V1 不建议建立复杂的父主题/子主题覆盖机制。公共能力通过普通包复用:
```text
packages/theme-runtime
packages/seo-components
packages/block-contracts
packages/media-components
```
每个主题仍然明确拥有自己的模板和 CSS。这样升级公共包时更可控,也避免多层覆盖难以追踪。
### 6.4 四层组件化复用架构
为了提高新主题和新网站的开发速度,组件按复用程度分为四层:
```mermaid
flowchart TB
C1[第一层:数据契约与工具]
C2[第二层:无样式基础组件]
C3[第三层:业务逻辑与 Headless 组件]
C4[第四层:主题展示组件]
PAGE[页面模板]
C1 --> C2
C1 --> C3
C2 --> C4
C3 --> C4
C4 --> PAGE
```
#### 第一层:数据契约与工具,完全复用
- Block、Page、Media、Menu、SEO 类型。
- JSON Schema 和数据验证。
- 内部链接解析。
- 图片地址、`srcset` 和尺寸计算。
- URL、Canonical、Hreflang 和结构化数据生成。
- HTML 净化、日期格式化和多语言工具。
这一层不包含任何主题样式。
#### 第二层:无样式基础组件,高度复用
- Container
- Grid
- Stack
- Media
- Picture
- Link
- Button 基础行为
- Dialog
- Accordion
- Tabs
- Carousel 控制逻辑
- Pagination
组件提供语义、可访问性和交互状态,不固定颜色、字体和视觉效果。主题通过 CSS、自定义属性和组合方式决定最终外观。
#### 第三层:业务逻辑与 Headless 组件,高度复用
- 产品列表查询与分页。
- 文章列表查询。
- 产品参数分组。
- 菜单树解析。
- 面包屑生成。
- FAQ 展开状态。
- 表单校验和提交状态。
- 搜索数据适配。
- 相关推荐规则。
这类组件输出数据或状态,不直接决定最终 DOM 结构。例如 `createProductGridModel()` 负责得到产品卡片数据,汽车主题和工业主题分别决定如何渲染卡片。
#### 第四层:主题展示组件,按主题独立
- Header、Footer、Mega Menu。
- Hero 的具体布局。
- Product Card 的视觉结构。
- 首页各板块的组合方式。
- 动画、颜色、字体和响应式细节。
这一层允许完全不同,避免为了复用而把所有网站做成同一种风格。
### 6.5 公共组件包结构
```text
packages/
content-contracts/ # Page、Block、Product、Post 等类型
block-contracts/ # 板块 schema、版本和迁移
theme-runtime/ # 主题加载、模板解析、渲染上下文
ui-primitives/ # 无样式基础组件
headless-components/ # 菜单、分页、Tabs、Accordion 等逻辑
content-resolvers/ # 产品/文章/分类引用解析
media-components/ # Picture、Video、资源地址与 srcset
seo-components/ # Meta、Canonical、Hreflang、JSON-LD
form-runtime/ # 表单 schema、校验和提交适配
design-tokens/ # 公共 token 协议,不存具体品牌值
```
主题只引用公共包的正式入口,不能跨主题引用另一个主题内部文件。
```text
允许:theme-a → packages/ui-primitives
禁止:theme-a → themes/theme-b/src/components/ProductCard.astro
```
如果多个主题确实重复使用一个展示组件,应先将其中的通用行为和结构抽到公共包,再由各主题提供外观;不要直接复制后形成多个无法同步的版本。
### 6.6 设计令牌
公共组件只使用语义化 token,不使用某个网站的品牌颜色:
```css
:root {
--sf-color-primary: #1f4b7a;
--sf-color-accent: #e8792e;
--sf-color-text: #1b1f24;
--sf-color-surface: #ffffff;
--sf-radius-control: 0.5rem;
--sf-space-section: clamp(4rem, 8vw, 8rem);
--sf-container-width: 75rem;
}
```
每个主题覆盖这些 token,也可以增加自己的 token。公共包不得依赖只有某个主题才存在的 token。
### 6.7 组合优于大量 Variant
公共组件保持小而稳定,通过组合形成页面板块:
```astro
<Section>
<Container>
<SplitLayout>
<HeroContent data={block.data} />
<ThemeHeroMedia media={media} />
</SplitLayout>
</Container>
</Section>
```
不要给一个公共 Hero 增加几十个布尔属性来适应所有网站,例如 `rounded`、`centered`、`overlay`、`video`、`reverse`、`compact` 的任意组合。稳定的数据协议可以复用,最终板块应由主题组合基础组件实现。
### 6.8 组件复用矩阵
| 能力 | 公共包复用 | 主题独立 |
|---|---:|---:|
| 数据类型与 Schema | 是 | 否 |
| SEO Head | 是 | 可提供少量站点配置 |
| Picture/srcset | 是 | 可控制画面比例和样式 |
| 菜单树和展开逻辑 | 是 | Header 结构和动画独立 |
| 产品查询与分页 | 是 | 产品卡片独立 |
| FAQ 数据与交互状态 | 是 | 排版和图标独立 |
| 表单校验与提交 | 是 | 字段布局和视觉独立 |
| Hero 内容协议 | 是 | Hero 渲染完全独立 |
| 页面模板 | 否 | 是 |
| 品牌色、字体、间距 | 仅协议 | 是 |
### 6.9 组件版本与兼容
- 公共包遵循语义化版本。
- 破坏性变更必须提供迁移说明或兼容适配层。
- 主题 manifest 记录公共运行时的兼容范围。
- 每个公共组件提供最小、完整、异常三个测试 fixture。
- 公共组件升级后,自动构建所有示范主题并执行截图回归。
- 发布 release 锁定公共包和主题版本,回滚时不能自动升级依赖。
### 6.10 组件预览与文档
建立独立的组件预览页,展示:
- 基础组件的状态和可访问性。
- 每种板块的最小/完整数据。
- 同一板块在不同主题中的效果。
- 桌面、平板和手机断点。
- 空数据、超长标题、图片缺失等异常状态。
预览页可以采用 Storybook 或项目内 Astro 组件目录。V1 的重点是可查看和自动截图,不强制依赖某个文档工具。
---
## 7. 模板约束
模板除了渲染页面,还负责约束后台可配置范围。
```ts
export interface TemplateDefinition {
key: string;
contentTypes: string[];
allowedBlocks: string[];
requiredBlocks?: string[];
maxPerType?: Record<string, number>;
lockedRegions?: string[];
}
```
示例:
- 首页必须有一个 Hero,最多一个。
- 产品详情固定显示产品标题、价格/询价入口和核心参数。
- FAQ、下载、CTA 可以由管理员添加、删除和排序。
- SEO Head、Header、Footer 是锁定区域,不出现在自由排序列表里。
后台新增板块时,只显示当前主题、当前模板支持的类型和变体。
---
## 8. 数据库存储
推荐将板块实例按修订版本独立存储,而不是直接写入已发布页面。
```sql
CREATE TABLE page_blocks (
id UUID PRIMARY KEY,
site_id UUID NOT NULL REFERENCES sites(id),
revision_id UUID NOT NULL REFERENCES content_revisions(id),
block_type TEXT NOT NULL,
schema_version INTEGER NOT NULL,
variant TEXT,
position INTEGER NOT NULL,
data JSONB NOT NULL DEFAULT '{}'::jsonb,
settings JSONB NOT NULL DEFAULT '{}'::jsonb,
visibility JSONB NOT NULL DEFAULT '{"enabled":true}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_page_blocks_revision_position
ON page_blocks(revision_id, position);
```
全局板块可以使用:
```text
global_fragments
global_fragment_revisions
global_fragment_usages
```
发布时锁定内容修订和全局板块修订,确保历史 release 可以准确回滚。
---
## 9. 后台编辑体验
### 9.1 页面编辑器布局
```text
┌────────────────────────────────────────────────────────────┐
│ 页面标题 / URL / 模板 / 保存 / 预览 / 发布 │
├──────────────────────┬─────────────────────────────────────┤
│ 板块列表 │ 当前板块表单 │
│ │ │
│ 1. Hero │ 标题 [________________] │
│ 2. Product Grid │ 描述 [________________] │
│ 3. FAQ │ 图片 [选择媒体] │
│ 4. CTA │ 样式变体 [image-right ▼] │
│ │ │
│ [+ 添加板块] │ │
└──────────────────────┴─────────────────────────────────────┘
```
首版功能:
- 点击新增板块。
- 拖动调整纵向顺序。
- 复制、隐藏和删除板块。
- 编辑结构化字段。
- 从媒体库、产品、文章和内部路由中选择引用。
- 保存草稿。
- 创建静态预览。
- 显示字段和模板校验错误。
### 9.2 不在 V1 实现
- 画布上任意拖动位置。
- 任意列宽和像素级布局。
- 无限层级嵌套板块。
- 管理员输入 CSS/JavaScript。
- 在一个主题中自由调用另一个主题的组件。
---
## 10. Astro 渲染实现
### 10.1 渲染器注册
```ts
import HeroBlock from '../blocks/HeroBlock.astro';
import RichTextBlock from '../blocks/RichTextBlock.astro';
import ProductGridBlock from '../blocks/ProductGridBlock.astro';
export const blockRenderers = {
hero: HeroBlock,
'rich-text': RichTextBlock,
'product-grid': ProductGridBlock,
} as const;
```
### 10.2 页面循环
```astro
---
import { blockRenderers } from '../lib/block-renderers';
const { page } = Astro.props;
const blocks = page.blocks.filter((block) => block.visibility.enabled);
---
{blocks.map((block) => {
const Renderer = blockRenderers[block.type];
return Renderer
? <Renderer block={block} page={page} />
: null;
})}
```
实际构建前必须已经验证所有板块类型。生产构建发现未知板块时应失败,不能静默生成空白区域;后台预览可以显示“当前主题不支持该板块”的占位提示。
### 10.3 数据解析顺序
```text
读取发布快照
→ 获取页面与内容引用
→ 获取模板定义
→ 验证允许的板块和数量
→ 按 schema 校验每个板块
→ 解析媒体与内部链接
→ 选择主题渲染器
→ 生成 HTML
→ 执行 SEO/链接/资源验收
```
### 10.4 客户端 JavaScript
Astro 默认输出静态 HTML。只有轮播、菜单、筛选等确需交互的组件才使用客户端 hydration,并选择合适的加载策略。普通文本、产品参数和 FAQ 不应无条件加载完整前端框架。
---
## 11. 主题切换策略
### 11.1 可以直接切换的条件
- 新主题支持当前页面模板。
- 新主题支持页面使用的全部板块类型。
- 每个板块的 schema 版本兼容。
- 主题所需站点配置完整。
### 11.2 切换前检查
系统先生成兼容报告:
```text
支持的模板
缺失的板块渲染器
不支持的 variant
缺失的站点配置
需要迁移的数据版本
```
存在不支持板块时,不允许直接用于生产构建。可以:
- 为新主题增加渲染器。
- 将板块转换为兼容类型。
- 由管理员明确删除或替换。
### 11.3 主题切换不是主要目标
平台需要支持主题版本升级和兼容检查,但不必把“任何网站一键换任意主题”作为 V1 目标。不同网站设计差异很大,强行互换会让板块协议变得臃肿。
---
## 12. 板块版本与数据迁移
板块必须携带 `schemaVersion`。
```ts
type BlockMigration = {
blockType: string;
fromVersion: number;
toVersion: number;
migrate: (oldData: unknown) => unknown;
};
```
例如 Hero V2 将 `buttonText` 和 `buttonUrl` 合并为 `primaryAction`:
```text
v1 data
→ 运行纯函数迁移
→ v2 data
→ schema 校验
→ 生成新的内容修订
```
禁止直接批量覆盖已发布修订。迁移失败的板块进入人工处理列表,旧版本数据继续保留。
---
## 13. API 建议
```text
GET /api/v1/themes
GET /api/v1/themes/:themeId/manifest
GET /api/v1/sites/:siteId/theme
PUT /api/v1/sites/:siteId/theme
GET /api/v1/sites/:siteId/pages/:pageId/blocks
POST /api/v1/sites/:siteId/pages/:pageId/blocks
PATCH /api/v1/sites/:siteId/pages/:pageId/blocks/:blockId
DELETE /api/v1/sites/:siteId/pages/:pageId/blocks/:blockId
POST /api/v1/sites/:siteId/pages/:pageId/blocks/reorder
POST /api/v1/sites/:siteId/pages/:pageId/validate
POST /api/v1/sites/:siteId/pages/:pageId/preview
GET /api/v1/sites/:siteId/fragments
POST /api/v1/sites/:siteId/fragments
PATCH /api/v1/sites/:siteId/fragments/:fragmentId
```
后端负责验证模板、板块类型、schema、variant、媒体和内部链接;不能只在管理后台进行校验。
---
## 14. 测试策略
### 14.1 契约测试
- 每个主题 manifest 格式正确。
- 模板引用的板块都有渲染器。
- 板块数据能通过对应 schema。
- 主题声明的 variant 都能渲染。
- 必需板块和最大数量约束有效。
### 14.2 渲染测试
- 每个模板至少有一份标准 fixture。
- 每个板块测试最少字段、完整字段和异常字段。
- 生成 HTML 不包含预览域名、危险脚本和无效资源。
- 核心内容在输出 HTML 中真实存在,而非仅客户端加载。
### 14.3 视觉测试
- 为首页、产品详情、文章详情和普通页面生成桌面/移动截图。
- 主题版本变化时对比关键页面。
- 视觉差异由超级管理员确认后才能用于生产发布。
### 14.4 性能与可访问性
- 图片包含宽高并使用合适尺寸。
- 交互组件按需加载。
- 标题层级、链接、按钮和表单具有正确语义。
- 颜色对比、键盘操作和焦点状态在主题验收中检查。
---
## 15. V1 实现顺序
### Sprint 1:契约和最小渲染
- 建立 `block-contracts`。
- 建立 `ui-primitives`、`theme-runtime` 和 `content-resolvers` 的最小公共包。
- 定义 Hero、Rich Text、Image Text、Gallery、CTA、FAQ 六个基础板块。
- 建立主题 manifest 和模板协议。
- 使用固定 JSON 生成一个静态首页。
### Sprint 2:数据库和后台编辑
- 建立 `page_blocks` 和内容修订。
- 根据 schema 生成表单。
- 支持新增、编辑、删除、排序和隐藏。
- 支持媒体和内部链接选择。
### Sprint 3:主题与预览
- 完成第一套主题的首页、普通页面、产品详情和文章详情。
- 建立预览构建。
- 增加模板与板块兼容检查。
### Sprint 4:第二主题验证
- 使用相同基础板块数据开发视觉完全不同的第二主题。
- 验证哪些字段真正通用,哪些需要站点专属板块。
- 将两个主题中重复的非视觉逻辑提取到公共组件包。
- 固化主题 SDK、测试 fixture 和开发文档。
### Sprint 5:生产质量
- 板块版本迁移。
- 全局板块。
- 视觉回归、可访问性和性能检查。
- 与发布门禁和不可变 release 集成。
---
## 16. 首版基础板块清单
建议首版只实现以下 12 个,真实试点需要后再增加:
| 板块 | 核心字段 |
|---|---|
| Hero | eyebrow、title、description、media、actions |
| Rich Text | content |
| Image Text | title、content、media、mediaPosition、action |
| Gallery | title、items |
| Feature List | title、items、icon/media |
| Statistics | title、items(value/label) |
| Product Grid | title、productRefs/query、limit、action |
| Article List | title、category/query、limit、action |
| Specification Table | title、groups/rows 或 product field binding |
| Download List | title、assetRefs |
| FAQ | title、items(question/answer) |
| CTA | title、description、media、actions |
Header、Footer、Breadcrumb、产品主信息和文章主内容作为全局/模板系统组件单独实现,不计入可自由添加板块。
---
## 17. 开发验收标准
主题与板块模块达到以下条件即可视为 V1 完成:
1. 超级管理员能为页面选择模板。
2. 能新增、编辑、排序、隐藏和删除模板允许的板块。
3. 后台字段由 schema 驱动,并在前后端双重验证。
4. 相同板块数据能被两套主题渲染成明显不同的设计。
5. 主题不支持某板块时,生产构建会被阻止并给出明确错误。
6. 产品详情和文章详情能混合系统板块与可配置板块。
7. 全局 Header/Footer 修改后能够重新构建站点。
8. 所有发布版本锁定主题版本和板块修订,可准确回滚。
9. 页面输出为静态 HTML,核心内容无需 JavaScript 即可读取。
10. 主题和板块变更进入现有 URL、SEO、内容和视觉发布门禁。
11. 两套主题复用相同的数据契约、媒体、SEO 和 Headless 逻辑,但最终页面视觉明显不同。
---
## 18. 最终建议
首版主题系统的重点不是“让管理员设计网页”,而是让开发者能够快速制作不同主题,让管理员安全地维护结构化内容。
必须守住三个边界:
1. 内容数据中不写 CSS 和组件路径。
2. 主题不能自己决定历史 URL 和 SEO。
3. 未知或不兼容板块不能在生产构建中静默消失。
按“统一协议、独立渲染、有限配置、严格校验”实现,既能保留每个网站的设计自由度,也能让后续内容维护和新站迁移真正复用同一套系统。
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,552 @@
# SiteForge 现有 CMS 项目复用评估 V1.0
> 评估对象:`D:\project\cms`
> 目标项目:`D:\project\cms-one`(SiteForge)
> 评估日期:2026-10-20
> 依据:现有源码、数据库 Schema、测试、构建脚本、基础设施配置,以及 SiteForge V3.0 方案与实施计划。
---
## 1. 结论
`D:\project\cms` 不是简单样例,而是一个已经具备管理后台、控制 API、WordPress 迁移、内容模型、Astro 静态站运行时和基础测试的早期平台原型。
它与 SiteForge 的目标高度同源,适合作为**代码底座和经验来源**,不建议把 SiteForge 完全从零重写。但它当前仍然是“单站 MVP 向平台化演进”的状态,部分文档设计明显领先于实际代码,因此不能整体原样搬入。
推荐原则:
1. SiteForge V3.0 方案继续作为当前项目的唯一产品与架构基线。
2. 选择性继承 `cms` 中已经验证的代码,不继承其过时的权限、发布、路由和数据边界。
3. 优先复用迁移解析、内容版本、管理后台壳、Astro 组件、测试夹具等高价值资产。
4. 对认证、发布、路由注册、主题架构、媒体安全和表单服务进行结构性重构。
5. 不复制缓存、依赖目录、构建产物、真实客户数据和历史存储文件。
---
## 2. 当前项目可以直接参考或复用的部分
### 2.1 Monorepo 工程骨架
来源:
- `D:\project\cms\package.json`
- `D:\project\cms\pnpm-workspace.yaml`
- `D:\project\cms\turbo.json`
- `D:\project\cms\.github\workflows`
可复用内容:
- pnpm Workspace + Turborepo 的项目组织方式。
- 管理后台、控制 API、构建 Worker、站点运行时和公共包的目录边界。
- TypeScript、Vitest、CI 的基础配置。
- 统一执行 `typecheck`、`test`、`build` 的任务编排。
需要调整:
- CI 安装应使用冻结锁文件,而不是 `--no-frozen-lockfile`。
- 增加数据库迁移校验、OpenAPI 契约校验、静态站质量门禁、安全扫描和产物校验。
- 不复制 `node_modules`、`.turbo`、`.data`、本地日志和构建输出。
复用等级:**高,可以直接作为工程骨架参考。**
### 2.2 Art Design Pro 管理后台基础
来源:`D:\project\cms\apps\admin-web`
已有能力:
- Vue 3 + TypeScript + Element Plus。
- Art Design Pro 布局、菜单、路由、Pinia、Axios 和通用组件。
- 站点列表、站点管理、内容管理、媒体、导航、设置、线索和迁移页面。
- WordPress 迁移的步骤式界面。
建议保留:
- 登录后主布局、导航、标签页、请求封装、异常处理和基础表格表单组件。
- 站点切换、站点列表、迁移向导和媒体选择器的交互结构。
- Element Plus 设计体系以及 Art Design Pro 已有的工程规范。
必须精简:
- 删除注册、找回密码、动态权限演示和多角色菜单逻辑。
- 用户角色只保留一个全局 `super_admin`,不设计复杂 RBAC。
- 清理仪表盘、电商演示、Mock 页面和无关组件。
- 将体积过大的站点管理页面拆成独立领域页面和复用组件。
- 内容表单由硬编码逐步改为 Schema 驱动。
- 复用前确认 Art Design Pro 及第三方资源的许可证适用范围。
复用等级:**高,建议直接继承后台壳和通用能力,业务页面按新方案重构。**
### 2.3 NestJS + Fastify + Drizzle 后端基础
来源:`D:\project\cms\services\control-api`
已有模块:
- 认证。
- 站点管理。
- 内容类型、内容条目和内容版本。
- 媒体管理。
- WordPress 迁移。
- 线索管理。
- 构建触发。
可复用内容:
- NestJS 模块化结构和 Fastify 适配。
- Drizzle + PostgreSQL 的访问方式。
- Zod 输入校验思路。
- 显式 `site_id` 上下文和站点隔离测试。
- 内容版本不可变、草稿与发布版本分离的基础设计。
- 站点模板初始化和默认导航生成逻辑。
需要重构:
- 用户表当前为站点级并带历史角色字段,应改为平台级单超管模型。
- 自制 HMAC Token 缺少成熟的过期、刷新、撤销和 MFA 机制,应替换。
- 审计日志、发布记录、构建任务和路由记录需要成为正式数据表,而不是内存或 JSON 附属数据。
- API 应补齐 OpenAPI、统一错误码、幂等键、操作审计和敏感操作二次确认。
复用等级:**高,适合作为后端起点,但认证和发布领域不可原样沿用。**
### 2.4 WordPress REST/WXR 迁移能力
来源:
- `D:\project\cms\services\control-api\src\migration`
- `D:\project\cms\apps\admin-web\src\views\sites\migrate-tab.vue`
- `D:\project\cms\tests`
已有能力:
- WordPress REST API 拉取。
- WXR 文件解析。
- 文章、产品等内容转换为中间结构。
- 媒体下载、去重和关联。
- 类型映射、迁移预览、批次导入和结果报告。
- 基于旧地址生成部分重定向。
- 幂等迁移和相关端到端测试。
这是旧项目中最值得保留的资产之一。现有文档还记录了多个真实网站迁移规模,说明相关代码不是纯概念设计。
必须调整:
- 迁移后不能自动发布,必须进入“导入批次 → 校验 → 人工审核 → 预览 → 发布”的流程。
- 旧 URL 不能只保存在 `siteSettings` JSON 中,应写入正式的 `route_registry`。
- 页面、分类、标签、自定义文章类型、附件和 SEO 元数据需要统一映射策略。
- 不能把无法映射的旧页面一律跳转首页,应进入冲突清单并人工处理。
- 语言不能硬编码为 `en/es`,应使用站点 Locale 配置。
- 增加断点续传、失败重试、限流、增量迁移、差异报告和可回滚批次。
- 从 HTML 提取内容时增加 XSS 清理和资源地址标准化。
复用等级:**很高,保留解析器、UniversalRow 思路、媒体抓取和测试夹具,重做迁移编排。**
### 2.5 内容模型、版本与导入幂等
来源:
- `D:\project\cms\services\control-api\src\content`
- `D:\project\cms\services\control-api\src\db`
- `D:\project\cms\docs\contracts\导入模板V1.md`
可复用内容:
- 内容类型定义与内容条目分离。
- 多语言内容记录。
- 不可变版本记录。
- 通过 `externalRef`、Slug 等键进行幂等合并。
- 导入预校验、错误报告、状态机和 CSV 安全方面的设计经验。
建议作为 SiteForge 内容中台的起点,但需要补充:
- 内容发布状态机。
- 计划发布和撤回。
- 关联内容完整性。
- Slug/URL 冲突检测。
- 导入批次与每条记录的来源追踪。
- 批次级回滚。
- 删除保护和引用检查。
复用等级:**高。**
### 2.6 Astro 静态站运行时和页面质量门禁
来源:`D:\project\cms\runtimes\astro-site-runtime`
已有能力:
- Astro 5 静态站构建。
- 首页、产品、分类、新闻、项目、关于、联系、支持和 Sitemap 页面。
- Header、Footer、Hero、分页、表单、卡片网格、CTA 等组件。
- 基础主题 Token。
- Title、Description、Canonical、H1、内部链接、图片和 Sitemap 检查。
- 重定向文件生成。
- 版本化构建产物和基础回滚脚本。
可直接复用:
- Astro 的构建基础。
- 现有语义组件和样式 Token 的代码经验。
- 门禁脚本和重定向生成器作为第一版基础。
- SEO 页面输出和 Sitemap 生成思路。
必须重构:
- 页面不能继续依赖硬编码站点配置和默认内容。
- 编译器必须读取冻结的发布快照,而不是构建过程中读取变化中的在线数据。
- 主题应拆成主题包、主题清单、Token、布局、区块注册表和站点配置。
- 区块应由统一 Block Renderer 渲染,不能每个网站复制页面文件。
- 构建产物切换要使用真正的原子发布机制,而不是删除后复制。
- 门禁检查应使用 HTML 解析器而不是仅靠正则,并增加重复元数据、结构化数据、hreflang、孤链、重定向环、状态码和性能预算。
复用等级:**中高,适合作为静态运行时原型,不应直接作为最终主题架构。**
### 2.7 主题 Token 和组件契约文档
来源:
- `D:\project\cms\docs\contracts\ThemeToken契约V1.md`
- `D:\project\cms\docs\contracts\组件契约V1.md`
值得吸收的内容:
- 使用语义 Token,而不是在组件中直接写颜色和尺寸。
- 主题变体、版本号和升级迁移规则。
- 组件输入、插槽、SEO、可访问性和懒加载约束。
- Hero、CardGrid、FeatureGrid、CTA 等组件的标准化思路。
- WCAG 2.2 AA 作为可访问性目标。
与 SiteForge 的结合方式:
- 把“组件契约”升级为正式的 Block Manifest。
- 每个组件声明字段 Schema、默认值、允许的布局、SEO 行为、资源依赖和兼容版本。
- 区分基础 UI 组件、内容区块、页面模板和站点主题四个层级。
- 主题只能组合和覆盖,不得复制修改公共组件源码。
复用等级:**高,适合作为当前主题与组件规范的补充输入。**
### 2.8 PostgreSQL、Redis、MinIO 基础设施
来源:`D:\project\cms\infrastructure`
建议沿用:
- PostgreSQL:业务数据库。
- Redis:队列、分布式锁、缓存和限流。
- MinIO:本地开发的 S3 兼容对象存储。
需要修改:
- 生产环境使用受控对象存储或独立部署的兼容服务。
- 镜像必须固定版本,不能长期使用 `latest`。
- 开发密码不得用于生产。
- 增加备份、恢复演练、生命周期、跨环境配置和 Secret 管理。
复用等级:**高。**
---
## 3. 可以参考,但必须重构后使用的部分
### 3.1 认证与权限
旧项目保留了多角色和站点级用户的历史痕迹,而 SiteForge 已明确只需要一个超级管理员。
新设计应为:
- 平台只有一个 `super_admin` 权限模型。
- 可允许配置一个或少量超级管理员账号,但权限完全一致,不建立角色矩阵。
- 使用成熟密码哈希、短期访问凭证、刷新/撤销机制、MFA、登录限流和安全日志。
- 所有高风险操作记录操作者、站点、对象、前后差异、IP、User-Agent 和时间。
旧认证代码只能参考接口结构,不能作为生产安全方案直接使用。
### 3.2 构建、发布与回滚
旧项目当前主要问题:
- 构建记录保存在进程内存中。
- API 进程直接启动构建命令。
- 构建锁仅在单进程内有效。
- Build Worker 目录基本还是占位。
- 发布替换并非严格原子操作。
- 回滚没有完整持久化的 Release 记录。
SiteForge 应改为:
1. API 创建持久化 Build Job。
2. Redis/BullMQ Worker 异步消费。
3. 同站点使用分布式发布锁。
4. 构建读取不可变 Site Snapshot。
5. 产物写入版本目录或对象存储。
6. 门禁通过后原子切换当前 Release 指针。
7. 每次发布记录输入快照、代码版本、主题版本、产物校验和和日志。
8. 回滚只切换到已验证 Release,不重新现场构建。
可继承的是构建命令、Astro 产物组织和基础门禁;发布编排需要重做。
### 3.3 路由与 SEO
旧项目能生成重定向文件,但路由仍是附属设置,不足以支撑“原路径不变”的核心要求。
SiteForge 必须建立独立的 `route_registry`:
- 每条公开 URL 都有唯一注册记录。
- 保存站点、Locale、路径、资源类型、资源 ID、状态、Canonical 和历史来源。
- 写入内容时先预占或校验路由。
- 迁移前导入完整旧站 URL 清单。
- 构建时从注册表生成页面和重定向。
- 发布门禁对旧 URL 覆盖率、404、重定向环和多跳重定向进行检查。
旧项目的 Redirect 生成脚本可以保留,但数据源应切换到路由注册表。
### 3.4 媒体系统
旧媒体模块已有 Sharp 缩略图和来源 URL 去重,可以保留处理经验,但存在以下缺口:
- 主要依赖本地磁盘。
- SVG 缺少严格清理。
- 删除时缺少完整引用检查。
- 未形成原文件、变体、Hash、使用关系和删除状态的完整模型。
SiteForge 应实现:
- S3 兼容对象存储。
- 文件 Hash 去重和 MIME 魔数校验。
- SVG 清理或默认禁用上传。
- 图片尺寸、格式、焦点、Alt 和变体管理。
- `media_usage` 引用关系。
- 软删除、延迟物理删除和可恢复窗口。
- WebP/AVIF 等派生资源异步生成。
### 3.5 表单与线索
旧 Lead 模块只适合作为简单 CRUD 示例,尚不等于可对公网开放的 Form API。
SiteForge 应补充:
- 表单定义和字段 Schema。
- 来源页面、UTM、Referrer、站点和 Locale。
- 服务端校验、蜜罐、验证码、频率限制和重复提交保护。
- 隐私同意记录和可配置保留期限。
- 通知重试、Webhook 签名和失败队列。
- CSV 导出安全处理。
- 所有更新必须同时按 `site_id` 和记录 ID 定位。
### 3.6 站点主题与页面编辑
旧运行时已有 Token 和组件,但页面结构仍偏硬编码。SiteForge 应建立四层结构:
1. **基础组件层**:Button、Container、Image、RichText 等。
2. **业务区块层**:Hero、ProductGrid、NewsList、FAQ、CTA、ContactForm 等。
3. **页面模板层**:首页、产品列表、产品详情、新闻列表、文章详情和普通页面。
4. **主题层**:Token、字体、间距、装饰样式、默认区块组合和可用模板清单。
主题包至少包含:
- `theme.manifest.ts`
- `tokens.ts`
- `blocks/`
- `layouts/`
- `templates/`
- `styles/`
- `migrations/`
后台编辑的是结构化区块配置,前台由 Block Renderer 统一渲染。这样才能同时做到不同网站效果不同、组件可复用、数据可迁移和主题可升级。
---
## 4. 当前阶段不建议复用或应暂缓的部分
### 4.1 数据分析和客户身份体系
旧文档中包含事件网关、ClickHouse、Tracking Plan、客户画像和增长分析等设计。这些能力会显著增加系统复杂度,但不是 SiteForge 第一阶段完成网站迁移、发布和维护的必要条件。
建议:
- V1 只保留基础访问统计接入位和必要操作日志。
- Event Gateway、ClickHouse、统一客户身份和高级分析延后到独立阶段。
### 4.2 复杂 RBAC、多租户商业化和计费
当前需求已经明确只需超级管理员,因此暂不建设:
- 角色与权限矩阵。
- 团队邀请和成员管理。
- 租户套餐、用量计费和支付。
- 审批流。
数据库可保留未来扩展空间,但不应为了可能的需求增加当前实现负担。
### 4.3 旧项目的真实数据与运行目录
不得直接复制:
- `.data`
- `storage`
- `node_modules`
- `.turbo`
- `.playwright-mcp`
- 构建产物和运行日志
- 客户网站内容、媒体、线索和导入文件
- `.env`、Token、密码及其他 Secret
确需使用真实迁移样本时,应先获得授权并完成脱敏,再转换为最小测试夹具。
### 4.4 历史文档的权威地位
旧项目文档中有大量值得借鉴的设计,但部分功能只存在于文档,代码尚未实现;也有部分规则与 SiteForge 当前单超管、统一路由和冻结快照发布模型冲突。
因此:
- SiteForge V3.0 方案是当前项目的权威基线。
- 旧项目文档仅作为输入和参考。
- 任何旧设计进入实现前,都要映射到 SiteForge 的领域模型和验收标准。
---
## 5. 复用优先级矩阵
| 能力 | 现状 | 复用建议 | 优先级 |
|---|---|---|---|
| pnpm/Turborepo 工程骨架 | 已成型 | 直接参考/继承 | P0 |
| Art Design Pro 后台壳 | 已成型 | 保留壳,清理演示与多角色 | P0 |
| NestJS/Fastify/Drizzle | 已成型 | 作为 API 基础 | P0 |
| 站点上下文与隔离测试 | 已有实现 | 保留并扩大覆盖 | P0 |
| WordPress REST/WXR 解析 | 已有实现和测试 | 重点继承,重做迁移流程 | P0 |
| 内容类型和版本 | 基本可用 | 扩展状态机与发布模型 | P0 |
| Astro 静态运行时 | 原型可用 | 保留构建基础,重构主题架构 | P0 |
| 主题 Token/组件契约 | 文档较完整 | 纳入新主题规范 | P0 |
| Redirect 生成 | 已有脚本 | 改用 route_registry 数据源 | P0 |
| SEO Gate | 基础版本 | 保留并增强 | P0 |
| PostgreSQL/Redis/MinIO | 已配置 | 直接沿用技术选型 | P0 |
| 导入幂等和报告 | 有基础 | 扩展为批次、回滚、差异报告 | P1 |
| 媒体缩略图处理 | 有基础 | 保留 Sharp,重构存储与安全 | P1 |
| 站点模板初始化 | 有基础 | 改造成站点蓝图/主题初始化 | P1 |
| Lead CRUD | 简单实现 | 仅参考,重做公网 Form API | P1 |
| BuildService | 原型 | 不直接复用,改为队列 Worker | P1 |
| Event Gateway | 占位/早期 | 暂缓 | P3 |
| ClickHouse/增长分析 | 超出当前范围 | 暂缓 | P3 |
| 多角色 RBAC | 与需求冲突 | 不采用 | 不做 |
---
## 6. 推荐落地方式
### 方案 A:以旧项目代码为技术底座进行净化和重构(推荐)
做法:
- 保留旧项目 Git 历史或创建一条新的 SiteForge 开发分支/仓库。
- 删除缓存、数据、演示页面和超出范围的模块。
- 以 SiteForge V3.0 的数据模型、模块边界和实施计划逐步替换旧实现。
- 每完成一个领域,就补齐迁移脚本、测试和验收门禁。
优点:
- 最大化利用已经写好的管理后台、迁移解析器、API 和 Astro 代码。
- 保留真实问题的修复经验和测试用例。
- 比从空目录重建更快进入可运行状态。
风险:
- 必须主动偿还旧原型留下的结构债务。
- 不能为了短期速度保留内存发布、JSON 路由、自制 Token 等关键缺陷。
### 方案 B:保持 `cms-one` 为全新仓库,选择性移植源码
适用于希望彻底清理历史包袱的情况。
建议只移植:
- 后台通用壳和基础组件。
- WordPress/WXR 解析器与测试夹具。
- 内容版本、幂等导入的核心逻辑。
- Astro 通用组件、主题 Token 和门禁脚本。
- PostgreSQL/Redis/MinIO 开发环境配置。
不要整目录复制。每次移植都应有对应的模块边界、测试和依赖清单。
综合成本与风险,推荐采用**方案 A 的代码继承方式 + 方案 B 的选择性净化原则**:把旧项目当成可运行底座,但按 SiteForge 方案逐域重构,而不是延续旧项目产品定义。
---
## 7. 第一批实际迁移任务
### 第 1 批:建立干净基线
1. 确认源码许可证和第三方资产许可证。
2. 导出仅包含受版本控制源码的干净代码副本。
3. 排除数据、媒体、日志、缓存、依赖和 Secret。
4. 删除后台演示页面、多角色路由和无关分析模块。
5. 固化 Node、pnpm、PostgreSQL、Redis 和 MinIO 版本。
6. 跑通 lint、typecheck、unit、e2e 和静态站构建基线。
### 第 2 批:修正核心架构
1. 实现平台级单超级管理员认证。
2. 建立 `route_registry`、`import_batches`、`build_jobs`、`releases`、`publish_locks` 等核心表。
3. 将构建改为持久化队列 Worker。
4. 将静态站编译输入改为冻结 Site Snapshot。
5. 建立对象存储媒体模型和引用关系。
### 第 3 批:迁移与主题平台化
1. 把现有 WordPress REST/WXR 代码接入导入批次。
2. 建立全量 URL 清单、映射、冲突和覆盖率报告。
3. 将现有 Astro 组件整理为基础组件和业务区块。
4. 实现 Theme Manifest、Block Manifest 和 Block Renderer。
5. 完成第一个真实站点的迁移、预览、门禁和原子发布。
### 第 4 批:运营能力
1. Schema 驱动内容表单。
2. 产品、新闻、普通页面和导航维护。
3. 安全的公网 Form API 和线索后台。
4. 发布历史、差异查看、一键回滚和审计日志。
5. 备份恢复、监控告警和灾难恢复演练。
---
## 8. 需要优先修复的已发现风险
1. **发布记录在内存中**:服务重启会丢失构建历史和锁状态。
2. **API 进程直接构建**:资源隔离、并发、重试和故障恢复不足。
3. **迁移即发布**:不满足人工审核、预览和可回滚要求。
4. **路由保存在 JSON 设置中**:无法可靠保证所有旧路径不变。
5. **自制 Token 能力不足**:缺少完整过期、撤销、MFA 和会话管理。
6. **本地媒体存储和直接删除**:生产可靠性和引用安全不足。
7. **SVG 缺少清理**:存在持久化 XSS 风险。
8. **Lead 更新条件不完整**:部分操作需同时限定 `site_id`。
9. **页面和 Locale 存在硬编码**:无法稳定支持不同站点和语言组合。
10. **文档数据库模型多于实际 Schema**:必须以迁移文件和运行代码确认真实能力。
11. **正则式 HTML Gate 有边界缺陷**:需要 DOM 解析和浏览器级补充验证。
12. **所谓原子发布仍有删除后复制窗口**:生产切换需要重新设计。
---
## 9. 最终建议
当前 SiteForge 项目应把 `D:\project\cms` 定位为:
- **实现底座**:复用工程骨架、后台壳、API 基础、迁移解析和 Astro 运行时。
- **经验库**:复用真实迁移中沉淀的测试、映射和质量门禁经验。
- **非权威需求来源**:产品范围、权限、发布和主题架构以 SiteForge V3.0 为准。
预计最能节省工期的五项资产是:
1. WordPress REST/WXR 迁移解析和测试。
2. Art Design Pro 管理后台基础。
3. NestJS/Fastify/Drizzle 内容 API 基础。
4. Astro 静态站组件与 SEO Gate。
5. 内容版本、幂等导入和站点隔离测试。
预计最不能直接继承的五项是:
1. 当前认证与多角色模型。
2. 内存式构建和发布流程。
3. JSON 化重定向与非正式路由管理。
4. 本地媒体删除和安全策略。
5. 迁移完成后直接发布的工作流。
按这个边界执行,可以保留旧项目的大部分有效研发投入,同时避免把原型阶段的关键技术债带入 SiteForge 的正式版本。
File diff suppressed because it is too large Load Diff
+127
View File
@@ -0,0 +1,127 @@
# cms 代码库现状 vs SiteForge V3.0 差距分析与改造清单
> ⚠️ **本文档已作废(2026-10-20)**:被 `cms现状与SiteForge差距分析_V2.0.md` 取代。V2.0 修正了本文多处评级(隔离残留、非原子发布、SVG/媒体安全、迁移自动发布等,均以代码证据为准)。本文仅作历史参考,请勿据此决策。
> 版本:V1.0|日期:2026-10-20
> 方法:SiteForge V3.0 §2.1 的 17 项必须交付能力为标尺,逐条对照 `D:\project\cms` 的**实际代码**(非文档声称),证据来自代码探索 + 迁移台账 + 契约文档。
> 结论用途:走「C 路线」——以 cms 为基座继续演进,把 V3.0 当架构检查清单逐项吸收。
---
## 1. 总评
**17 项能力:已有 4 项、部分 9 项、缺失 4 项。**
cms 已经用真实数据(两站:hesupipe.com ✅ 完成、magicbridgeauto.com ⏸ 暂停)验证了最难的「迁移 → 静态构建 → 门禁 → 回滚」主干链路,这与 V3.0 的核心路径完全同构。缺口集中在:**部署上线(阻塞)、主题系统(平台化核心)、迁移质量增强(差量/冲突/URL 注册表)、账号与运维硬化**。
一句话:**V3.0 里"值钱的架构思想"cms 已吸收了一半(不可变修订、版本化产物、门禁、幂等),另一半(输入快照、URL 注册表、主题协议、部署器)就是改造清单的主线。**
---
## 2. 十七项能力逐条对照
图例:✅ 已有(实战验证)|🟡 部分(能跑但有明显缺口)|❌ 缺失
| # | V3.0 必须交付 | 评级 | cms 实际现状(代码证据) | 关键差距 |
|---|---|---|---|---|
| 1 | 多站点与环境管理 | 🟡 | sites/域名/设置齐全;站点隔离已修复并有 e2e(`isolation.e2e.test.ts`,文档里的"X-Site-Id 未生效"是过时信息) | 无 staging/production 环境抽象;构建历史存内存(重启即丢) |
| 2 | 单一超级管理员(MFA/恢复/会话撤销) | 🟡 | 登录 + bcrypt + 审计日志可用;单账号种子 | 自研 HMAC token **无过期、无撤销**;无 MFA/恢复码/登录限流;**登录页预填了默认账号密码** |
| 3 | WordPress REST/WXR/SQL/uploads 导入 | ✅ | REST + WXR 双通道 + zip 媒体 sourceUrl 预入库 + externalRef 幂等 + WAF 退避;两站实战(33MB WXR 也解析了) | 缺 SQL 直连通道(第三通道);ACF 字段发现未做 |
| 4 | Sitemap/HTML/Playwright 扫描 | ❌ | 仅拉取老站 sitemap.xml(1 级 index 递归) | 无 HTML 链接发现爬虫、无 Playwright;V3.0 的十来源 URL 全集完全没有 |
| 5 | 原始快照与差量同步 | 🟡 | WXR 原件留档 storage/wxr;重跑幂等更新 | **无冲突队列、无人工编辑保护**(重跑无条件整条覆盖 data)、无新增/更新/删除/冲突的差量分类报告 |
| 6 | 统一内容与内容修订 | ✅ | content_revisions 不可变(插入式)、公开端点只读 published 修订、5 种内容类型带 Schema | 缺乐观锁(last-write-wins,多标签页会互相覆盖);archived 状态无入口 |
| 7 | URL 注册表与路由编译 | 🟡 | 301 存 site_settings.redirects(JSONB);三格式产物(_redirects/nginx conf/csv);三色报告 🔴missing=0 实战达标 | 无独立路由注册表(normalized key/route_kind/indexable);无 410/查询参数路由;无部署能力预检 |
| 8 | SEO 快照与差异报告 | 🟡 | 出站 SEO 全套(title/desc/canonical/hreflang/og/twitter/noindex)+ Yoast 随迁 + 门禁查 title/H1/canonical + sitemap 一致性 | 无"旧站真实 HTML vs 新站"三套数据对比;无 P0–P3 分级差异报告 |
| 9 | 媒体迁移与旧路径保持 | ✅ | 全量转存 + sourceUrl 幂等去重 + alt + 多尺寸;两站 5494 张实战、替换零残留 | 老站已被外链引用的媒体旧 URL 是否 301 未处理(对齐 V3.0"旧路径保持") |
| 10 | 主题/模板/板块协议 | ❌ | templates 表存在但**空表无种子**;主题 Tab 能编辑 token 但**运行时没人消费(死线)**;后台表单全部硬编码;建站只有 1 个硬编码模板选项 | **最大缺口**(平台化文档自己标注)。ERD 里的 block_instances/template_slots/component_versions 设计未落地 |
| 11 | 组件化公共包 | 🟡 | astro runtime 内共享数据源 + base 组件 + 183 个设计 token(契约测试保护);domain-contracts 类型包 | 无四层复用分层、无多主题目录结构(themes/)、无跨主题复用验证 |
| 12 | Astro 全量静态构建 | ✅ | 版本化产物(.artifacts/,KEEP=10)+ 4 阶段原子发布 + 门禁 + 回滚(可再滚回) | **缺构建输入快照**:构建时实时拉 API,失败时**静默回落 seed 演示数据**(高危!) |
| 13 | 预览、验收、发布、回滚 | 🟡 | 验收(gate.mjs)/发布/回滚完整且实战 | **草稿预览缺失**(只能发布后看);无生产冒烟 |
| 14 | Nginx/SSH 发布 | ❌ | 产物只落本地目录;全库 grep 无 rsync/sftp/ssh/部署代码 | **两站上线都阻塞在这**(台账:"部署阿里云(等凭据)"——凭据之外还缺整个部署器) |
| 15 | S3/OSS + CDN 发布(与 14 二选一) | ❌ | MinIO 容器起了但无任何代码使用 | 同上 |
| 16 | 表单基础服务 | 🟡 | leads 提交 + zod 校验 + 状态流转 + 审计 | 无垃圾防护/验证码/限流;**邮件通知是 TODO 注释**(设置里有"接收邮箱"字段但不发);无 CRM 转发 |
| 17 | 备份、监控和操作日志 | 🟡 | audit_logs 覆盖全部变更端点(actor/before/after/requestId) | **无任何备份脚本**(94MB PG + 1.3GB 媒体全在开发机一块盘上);无监控告警;Fastify logger 关闭;ClickHouse/Redis/MinIO 容器闲置 |
---
## 3. cms 超出 V3.0 预期的地方(不要在改造中丢掉)
1. **WXR 正则解析器**(universal-row.ts):无 XML 库依赖,处理了 `_thumbnail_id`、`_product_image_gallery`、`wppop_sections` PHP 反序列化、Yoast 主分类——这些实战细节 V3.0 只写了方向。
2. **WAF/验证码对抗**:REST 通道 202 挑战页快速失败 + 指数退避(30/90/180s)+ 全局节流——SiteGround 拦截是真实踩坑沉淀。
3. **三色报告闭环**:externalRef ∪ sitemap 双源对照,🔴missing=0 作为发布关卡已在两站达标。
4. **契约测试纪律**:18 事件目录冻结、183 token 全量校验、组件零颜色字面量、WP 文本清洗规则——V3.0 §24 要求的契约测试已有雏形。
5. **多语言位**(entry_locales + hreflang 渲染):V3.0 把多语言放到 V1.1,cms 已有编辑位和渲染。
---
## 4. 上线前必须处理的五个高危问题(按危险程度排序)
| # | 问题 | 危险场景 | 修法(对应 V3.0 条目) |
|---|---|---|---|
| 1 | **构建 seed 兜底**:API 拉取失败时静默用演示数据构建 | control-api 抖动 → 演示内容被构建、过门禁、发布上线 → 线上变成假站 | 构建改为**先冻结输入快照**(拉全量数据 → 落盘 → 校验 → 再构建);失败即中止(V3.0 §13.1) |
| 2 | **零备份**:数据库、媒体、产物全在开发机 D 盘 | 磁盘坏 = 两站迁移成果 + 平台数据全灭 | pg_dump 定时 + storage/ 与 .artifacts/ 同步到第二存储;先手动后自动(V3.0 §20) |
| 3 | **认证弱**:token 无过期无撤销 + 登录页预填默认凭据 + 无限流 | 部署到公网后管理后台即刻成为攻击面 | token 加过期+撤销表;删除预填凭据并改密;登录限流;MFA 可后置(V3.0 §19.1) |
| 4 | **无部署器**:产物出不去本地 | 两站永远"差最后一步"上线 | 实现 rsync/SSH 部署器(先)+ OSS/CDN(后);附带上线检查表(V3.0 附录 A) |
| 5 | **重跑覆盖人工编辑**:迁移重跑无条件覆盖 data | 旧站同步时把后台手工修好的内容冲掉 | override 标记 + 冲突队列(V3.0 §8.4) |
---
## 5. 改造路线图(C 路线:四梯队)
### 第一梯队 · 解锁上线与数据安全(立即,1–2 周)
1. **部署器 v1**:rsync/SSH 到阿里云 ECS + nginx conf 应用 + 部署记录 + 产物哈希校验 → 把 hesupipe 真正发上线(对齐 V3.0 #14)
2. **备份**:每日 pg_dump + storage 增量同步 + 恢复演练一次(对齐 V3.0 #17、§20)
3. **构建输入冻结**:移除 seed 兜底;构建前物化站点数据快照;快照哈希进产物元数据(对齐 V3.0 #12、§13.1)
4. **认证加固**:token 过期/撤销、清默认凭据、登录限流(对齐 V3.0 #2)
### 第二梯队 · 平台化核心(3–6 周,magicbridge 复工的前置)
5. **主题系统落地**(V3.0 #10/#11,对应 cms 平台化 P2–P4):
- templates 表种子 + corporate-pro 主题物化(把现在写死的布局抽成第一套主题)
- **打通 token 死线**:让主题 Tab 的编辑真正影响构建产物
- Schema 驱动表单(字段类型注册表 → 动态渲染),下线硬编码表单
- themes/ 多主题目录 + 按 templateKey 构建(为 magicbridge 汽车站主题做准备)
6. **草稿预览**:预览构建目录 + 访问控制 + noindex(V3.0 #13)
### 第三梯队 · 迁移质量增强(按站需要,穿插进行)
7. **差量同步 + 冲突保护**:override 标记、差量分类报告(新增/更新/删除/冲突)、人工修改不被静默覆盖(V3.0 #5)——旧站仍在更新时必需
8. **URL 注册表**:redirects 从 settings JSONB 升级为独立表(ERD 本来就这么设计的),补 route_kind/410/查询参数(V3.0 #7)
9. **SEO 差异报告**:老站 HTML Head 抓取 vs 新站预览对比,P0–P3 分级接入门禁(V3.0 #8)
10. **HTML 爬虫**:链接发现补全 URL 全集(sitemap+WXR+爬虫三源);Playwright 仅按需(V3.0 #4)
### 第四梯队 · 运营与规模(上线后迭代)
11. 表单强化:垃圾防护、邮件通知(消灭 TODO)、CRM 转发(V3.0 #16)
12. 监控告警 + 结构化日志 + 构建历史持久化(V3.0 #17)
13. WP SQL 第三通道、ACF 字段发现(V3.0 #3 补全)
14. event-gateway/ClickHouse 分析栈:**单独决策**——它与建站平台正交,要么砍掉容器要么立项,别挂着
---
## 6. 明确不照搬 V3.0 的部分(单人 + AI 模式的取舍)
| V3.0 设计 | 取舍 | 理由 |
|---|---|---|
| MFA + 恢复码完整体系 | 后置 | 单运营者;先用强密码 + token 过期 + 限流 + IP 白名单顶住,公网暴露后再上 |
| 构建沙箱容器化(断网/限额/无密钥) | 暂缓 | 单人开发无第三方适配器代码风险;**先移除 seed 兜底**(真实风险排序相反) |
| CAS 发布锁 / 环境锁 / 租约心跳 | 暂缓 | 单运营者无并发发布冲突;构建单站串行已足够 |
| 十来源 URL 全集 | 渐进 | sitemap + WXR + 日志三源已覆盖大半;GSC/GA4 等按站补 |
| NestJS + BullMQ 任务队列全家桶 | 观察 | 现状"control-api spawn pnpm"土但能跑;队列在任务变多(定时备份/批量迁移)后再上 |
| 5 人团队 20 周排期 | 不适用 | 1 人 + AI 串行;按梯队推进,每梯队以"真实站点闭环"为验收 |
---
## 7. 文档与现实偏差清单(建议顺手修正)
1. `平台化总体设计与实施计划.md` §3:"X-Site-Id 未生效(已知缺陷)"——**已过时**,代码已修复且有 e2e 测试。
2. ERD-V1(45 张表、ClickHouse 4 表)是**设计蓝图不是实现**:实际 ORM 是 Drizzle(非 Prisma),redirects 是 settings JSONB(非独立表),site_snapshots/publish_manifests/artifacts/releases 等 ERD 表均未建。
3. `services/build-worker`、`services/event-gateway` 是 README 空壳;真实构建在 control-api + astro runtime 脚本里。
4. 登录页(admin-web)预填了 `admin@hesupipe.com / hesu-admin-2026`——上线前必须清除。
5. docker-compose 的 ClickHouse/Redis/MinIO 无代码消费——要么用起来要么先摘掉,避免"看起来有"的假象。
---
## 8. 下一步建议(本周可做)
1. 部署器 v1 动工前,先向用户/托管方确认:目标形态是阿里云 ECS(nginx)还是 OSS+CDN——决定先写 rsync 还是 OSS 上传。
2. 手动做一次全量备份(pg_dump + storage 拷贝到第二块盘/网盘),不等脚本。
3. 删除构建 seed 兜底(一行开关级改动,风险最高、成本最低)。
4. 把 magicbridge 的主题设计输入(HTML 稿/参考站)排上日程——第二梯队一完成它就能复工。
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,249 @@
# SiteForge 最小纵向切片 · 设计文档
> 日期:2026-10-20|状态:待评审
> 上位文档:`SiteForge_完整项目计划方案_V3.0.md`(执行基线)
> 姊妹文档:`cms现状与SiteForge差距分析_V1.0.md`(D:\project\cms 的教训清单)
> 本文决策已经用户逐项确认(建设顺序、切片范围、数据、路线、九项修正)。
---
## 1. 背景与定位
SiteForge 是多站点静态迁移与发布平台(V3.0 定义 17 项必须交付能力)。本切片是整个平台的**第一个里程碑**:用真实试点数据(hesupipe.com 的 WordPress WXR 导出 + 图片包副本)打通最险的核心链路一次:
```text
WXR 导入 → 统一内容模型 → URL 注册表 → 极简主题 → 静态构建 → 门禁 → 本地预览
```
三条前提约束:
1. **所有代码是正式资产**:数据模型、契约、目录结构按 V3.0 设计,后续能力在此之上加厚,不推倒重写。
2. **不改动 D:\project\cms,但白名单资产可移植**(路线 C,2026-10-20 确认):cms 冻结为只读源;试点数据以只读复制方式进入本仓库 `storage/`。**可移植资产(白名单)**:WXR 解析规则与测试夹具(重写到流式 XML 解析器之上)、Astro 页面模板(作为 pilot-minimal 主题起点——其产品 Tabs/相册/表格刚对齐过 hesupipe 老站)、门禁脚本逻辑、docker-compose/CI 配置。**不继承、按 V3.0 重写**:认证、BuildService、release/rollback 脚本、MigrationService 编排(迁移即发布)、settings JSONB 路由、Lead 服务、后台业务页面。
3. **完整性由清单驱动**:每完成一个阶段对照 17 项能力清单打钩,全绿才是项目完成;本切片覆盖其中 4 项的骨架(WordPress 导入、统一内容与修订、URL 注册表、Astro 静态构建)。
**文档权威关系**:`SiteForge_完整项目计划方案_V3.0.md` 是唯一产品与架构基线;`cms现状与SiteForge差距分析_V2.0.md` 是 cms 现状与改造优先级依据(**V1.0 差距分析已作废**);`SiteForge_现有CMS项目复用评估_V1.0.md` 是复用边界参考。
## 2. 目标与非目标
### 2.1 目标(本切片交付)
| # | 交付 | 验收口径 |
|---|---|---|
| G1 | WXR 全量导入管道(幂等,导入不发布) | hesupipe 全量导入成功(全部为 draft);显式 `publish` 后转 published;重复导入第二次产生 0 条新修订 |
| G2 | 统一内容模型 | 产品/新闻/页面落库,不可变修订 + externalRef 幂等键 + checksum |
| G3 | URL 注册表与路由策略 | 路径级保真 + 分类/归档/标签处置;URL 对照报告 🔴=0 |
| G4 | 极简主题静态构建 | 全站页面 + sitemap.xml + robots.txt + 媒体旧路径输出 |
| G5 | 门禁 | 死链=0、sitemap 一致、每页 title/canonical/唯一 H1、产物大小写冲突=0 |
| G6 | 本地预览服务器 | 按 route_registry 真实响应(html/301/404),noindex 头 |
### 2.2 非目标(明确不做,后续阶段做)
后台管理 UI、部署器(rsync/OSS)、差量同步与冲突队列(本切片只预留 `override` 字段)、SEO 三套数据 diff(P0–P3)、爬虫/Playwright、多语言(en 单语)、表单服务、MFA/认证体系(本地脚本无 HTTP 暴露)、Redis/BullMQ(任务先进程内)。
## 3. 技术选型与决策记录
| 决策 | 选择 | 理由(ADR 摘要,正式 ADR 实施时落 docs/adr/) |
|---|---|---|
| D1 ORM | Drizzle + PostgreSQL 16 | cms 实战验证;迁移即 SQL 可审查;SQL 控制力强(ADR-001) |
| D2 任务执行 | 进程内 JobRunner,接口抽象 | 切片无长任务并发需求;接口按 job 语义设计,BullMQ 后置替换不死代码(ADR-002) |
| D3 媒体存储 | 本地磁盘 `storage/`,产物保持旧路径 | cms 教训:MinIO 容器起了没人用;对象存储后置(ADR-003) |
| D4 URL 策略 | 路径级保真优先,301 兜底 | V3.0 §6/§7 要求;详见 §5(ADR-004) |
| D5 服务形态 | NestJS 模块化单体 + CLI 脚本复用服务层 | 架构与 V3.0 一致;切片不启动 HTTP 监听,脚本以 standalone app context 调用模块服务 |
| D6 契约范围 | packages/contracts 只含 UniversalRow + 内容/路由 DTO | YAGNI:无队列即无事件目录 |
| D7 WXR 解析 | 流式 XML 解析器(saxes 类)+ 资源限额 | cms 的正则解析在 33MB 级 WXR、CDATA/实体/命名空间边界有风险(V2.0 §5.1);限额:文件大小、节点数、处理时限;恶意 XML 防护(ADR-005) |
| D8 媒体安全底线 | 登记时魔数校验 + SVG 隔离 | 不信扩展名与声明 MIME;SVG 默认不进产物、单列报告(V2.0 P0-5)(ADR-006) |
运行环境:Node 22 LTS、pnpm workspace、TypeScript strict、vitest。开发机为 Windows(大小写陷阱见 §7 G5)。
## 4. 仓库结构(切片实际创建的部分)
```text
cms-one/
apps/api/ # NestJS:sites/content/routes/media/import/build/audit 模块
packages/contracts/ # UniversalRow、内容 DTO、路由 DTO(冻结+契约测试)
packages/adapter-wordpress/ # WXR 解析器 + WP REST 客户端(REST 通道切片内可选)
themes/pilot-minimal/ # 极简 Astro 主题
scripts/ # CLI:import.ts / build.ts / gate.ts / preview.ts
storage/wxr/ storage/media/ # 试点数据副本(git 忽略)
infra/docker-compose.yml # 仅 PostgreSQL
tests/fixtures/ # WXR/媒体夹具
docs/
plans/ # 根目录现有 4 份规划文档移入(实施第一步)
adr/ specs/ superpowers/
.gitignore # 仓库第一个 commit:node_modules/dist/artifacts/storage//.env/coverage
```
不预建空包(cms README 纪律);V3.0 §22.2 的其余包在对应阶段创建。
## 5. URL 与路由策略(本切片的核心正确性)
### 5.1 目标路径规则(按优先级)
| 旧 URL 形态 | 处置 | route_kind |
|---|---|---|
| 内容页 permalink(含 `.html`/`.php` 后缀、任意目录深度) | **产物按原路径原样输出**(路径级保真,非仅 slug) | html |
| 首页 `/` | 首页 | html |
| `product-category/{slug}`(产品分类) | 生成分类列表页,路径原样 | html |
| `category/{slug}`(文章分类) | 生成文章分类列表页 | html |
| 列表分页 `/page/N/`(含分类下分页) | 列表页分页输出 | html |
| `tag/{slug}`、`product_tag/{slug}`、日期归档、作者页 | 301 → 对应主列表页(/products/ 或 /news/) | redirect |
| 附件/媒体 `wp-content/uploads/...` | 媒体文件按**原路径**进产物 | asset |
| 无法保真的个别内容路径 | 301 → 新路径 | redirect |
| WP `?p=123` 短链 | 记录为已知限制,对照报告单列(不算 🔴,见 §8) | — |
### 5.2 route_registry(独立表,禁止塞 settings JSONB——cms 教训)
切片字段子集(V3.0 §6.4):`site_id / original_path / normalized_lookup_key / route_kind / target_content_id / target_file / redirect_location / indexable / source_status / source_snapshot_id`。
规范化键规则(仅作查重与对照,原始路径永不改写):percent-decode → Unicode NFC → 小写 → 去尾部斜杠。查重命中仅大小写/尾斜杠差异时**报告冲突**,不静默合并(V3.0 §6.3)。
### 5.3 URL 对照报告(门禁输入)
老 URL 全集 = WXR 可推导 URL(内容 permalink ∪ 术语归档 ∪ 分页推导)∪ 老站 sitemap.xml(若可拉取;被挑战页拦截则记说明)。三色口径与 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 清单),不阻塞导入;若被已发布页面引用则门禁降级为 🔴 由用户决定补图或改链。
- 登记时校验文件魔数与声明 MIME 一致,不一致入报告;**SVG 一律隔离**(不复制进产物、预览不可达),报告中单列清单。
## 6. 管道命令与数据流
### 6.1 `scripts/import.ts`(导入)
```text
读 storage/wxr/*.xml → adapter-wordpress 解析为 UniversalRow[]
→ 类型映射(post→news_article;product CPT→product;page→basic_page)
→ locale 取站点配置;未知 locale 记错误并跳过该条(禁止静默归 en——cms 实际踩坑)
→ 每条计算 normalized checksum:
与最新 content_revision.checksum 相同 → skip(不产生新修订)
不同或不存在 → 写入新 content_revision(kind=draft,不可变,source=import)
※ 导入不发布:发布是独立显式动作(见 6.2,V2.0 P0-6)
→ 媒体登记:按 WXR attachment 的 source_url 与本地副本文件名匹配;
全角/编码损坏文件名按规范化通配匹配(cms 踩坑),匹配结果写报告
→ 生成/更新 route_registry(§5.1 规则)与 301 规则
→ 未知 WP 字段保留进 data.extras(不丢弃)
→ 输出导入报告:条目数对照、skip/update/create 计数、媒体匹配率、
URL 冲突清单、行级错误定位
```
幂等键:`(site_id, source_connection_id, source_type, source_id, locale)`,与 externalRef(旧 permalink)双保险。
### 6.2 `scripts/publish.ts`(显式发布)
导入只产生 draft;发布是独立、显式、可审计的动作(V2.0 P0-6:禁止"迁移即发布"):
- 按批次/类型/条目批量将 draft 修订置为 published(更新 entry_locales.published_revision_id 指针)。
- 每次发布动作写 audit_logs(actor、对象、前后状态、时间)。
- 构建只消费 published 修订集合——与 cms「导入后逐条自动发布」的行为刻意相反。
### 6.3 `scripts/build.ts`(构建)
```text
物化 build_input_snapshot:
内容(published 修订集合)+ 路由清单 + 媒体清单 + 站点配置
→ 冻结为 artifacts/<buildId>/input/ 下带哈希的 JSON 文件
Astro 构建(themes/pilot-minimal):
只读输入快照文件,禁止实时查库,禁止任何 seed/演示兜底数据
(输入不完整 → 构建立即失败;此行为写回归测试)
SITE_URL 取站点配置的生产域名(hesupipe.com)→ canonical/og/sitemap 正确
产物:HTML 页面 + sitemap.xml(只含 route_kind=html 且 indexable)+ robots.txt
+ 媒体(旧路径)+ _redirects / nginx-redirects.conf(从 route_registry 编译,验证路由模型)
+ build-manifest.json(输入快照哈希、页面数、checksums)
落 artifacts/<buildId>/,build_jobs 表记录
```
### 6.4 `scripts/gate.ts`(门禁)
| 检查 | 通过条件 |
|---|---|
| URL 对照(§5.3) | 🔴 = 0 |
| 内部死链 | 产物内 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 不代表无环) |
| 兜底数据回归 | 输入损坏时构建必须失败(负向测试) |
### 6.5 `scripts/preview.ts`(预览)
自研 Node 静态服务器(不引入 nginx 依赖):
- 按 route_registry 响应:html → 伺服产物文件;redirect → 301 + Location;无匹配 → 产物 404 页。
- 所有响应加 `X-Robots-Tag: noindex`;禁用目录列表。
- 用途:在本地验证"真实 URL 行为"(V3.0 §7.4 精神),并支撑 §8 的人工抽检。
## 7. 数据模型(切片子集,V3.0 §9 对齐)
| 表 | 关键字段与约束 |
|---|---|
| sites | site_id PK、name、default_locale、production_domain |
| source_connections | 类型=wxr、文件路径 + sha256、adapter 版本 |
| 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 字段;独立表 |
| media_assets | (site_id, source_url) 唯一幂等;sha256、public_path(旧路径)、alt、尺寸 |
| build_jobs / artifacts | 构建记录 + 产物目录 + 输入快照哈希 |
| audit_logs | 追加式;actor(切片为 system/importer)、action、对象、前后值 |
多语言位:模型带 locale,切片只写 en。
## 8. 错误处理
- **行级容错**:单条解析/映射失败记录行号与原因后继续;整批结构错误(文件损坏、站点不存在)才中断。
- **未知字段**:一律进 `data.extras` 保留,报告中汇总清单。
- **构建 fail-fast**:输入快照不完整(内容缺失、路由空、媒体引用悬空)立即失败,无任何降级/兜底路径。
- **已知限制**:`?p=` 短链、WXR 外的老站 URL(日志/GSC 来源)不在本切片对照范围,报告中单列"未覆盖来源"节——不算 🔴,进入下阶段 SEO diff。
- **重跑语义**:无变化 skip / 有变化新修订 / 人工改动保护(override 标记)下阶段实现,本切片在修订上预留字段。
## 9. 测试策略(vitest)
| 层 | 内容 |
|---|---|
| 单元:WXR 解析 | 夹具必须覆盖:`_thumbnail_id`、`_product_image_gallery`、`wppop_sections`(PHP serialize 的 Tab 数据)、Yoast `_yoast_wpseo_title/metadesc`、全角/编码损坏文件名、分页与术语 URL 推导、`.html` 后缀 permalink(全部来自 cms 实际踩坑清单) |
| 单元:路由规则 | §5.1 每行规则一个用例;大小写/尾斜杠冲突报告 |
| 单元:解析器限额 | 超大/畸形 XML(节点数、大小、时限超限)→ 明确失败不崩溃;CDATA、实体、命名空间边界夹具 |
| 单元:媒体安全 | 魔数与扩展名不符 → 报告;SVG → 进隔离清单 |
| 单元:重定向图 | 环 → 🔴;多跳 → 告警 |
| 集成:导入 | 真实 WXR 小切片夹具 → 落库 → 断言修订/checksum/幂等(第二次 0 新修订) |
| 集成:构建 | 快照物化 → Astro 构建 → 产物结构断言(sitemap/robots/媒体旧路径/_redirects) |
| 负向 | 输入损坏 → 构建失败(兜底禁令);route_registry 缺目标 → 门禁 🔴 |
| 契约 | UniversalRow/DTO 字段冻结(packages/contracts) |
| 验收 | §2.1 G1–G6 全部可执行复验(真实 hesupipe 数据 + 抽检 20 个 URL:10 内容页 + 5 分类页 + 3 redirect + 2 404) |
## 10. 实施注意(从 cms 教训直接落地的防线)
0. **动工第零步**:手动备份 cms 数据(pg_dump + storage/ 拷贝至第二存储,约半小时——V2.0 阶段 A 要求)。
1. `.gitignore` 是仓库第一个 commit(storage/、artifacts/、node_modules、.env)。
2. 构建输入快照先于一切(seed 兜底事故的根治)。
3. redirects 走独立表(不进 settings JSONB)。
4. WXR 原件 + sha256 留档 source_connections。
5. 未知字段不丢弃(extras)。
6. 门禁 🔴=0 才算过。
7. Windows 长路径:副本拷贝与构建脚本启用长路径处理(全角文件名目录)。
## 11. 验收标准(切片完成的定义)
> 口径采用 V2.0 §2.2 成熟度分级:本切片验收 = **L3(真实数据本地验证)**;L4(生产部署、HTTP 验收、回滚演练)属后续部署阶段。
1. `import` 两连跑:第一次全量入库为 draft(条目数与 WXR 基数对照一致),`publish` 后转 published,第二次 import 0 新修订。
2. `build` 产出全站产物,`gate` 七项全绿。
3. `preview` 启动后按 §9 抽检 20 URL 行为正确(含 301 跳转与 404)。
4. `pnpm test` 全绿(含负向与契约)。
5. 导入报告与 URL 对照报告可读、可复查(含媒体匹配率与未覆盖来源说明)。
## 12. 切片之后的路(指引后续子项目,不在本切片内)
```text
SP0' 后台 UI(Art Design Pro + 认证/MFA)
SP1' 差量同步与冲突队列(override 字段启用)
SP2' SEO 三套数据 diff(P0–P3)+ 爬虫扩充 URL 全集
SP3' 主题/板块协议(Block Schema/模板约束/第二主题)
SP4' 部署器(rsync/SSH → OSS/CDN)+ 发布锁/回滚
SP5' 表单服务 + 备份监控
```
每阶段对照 `cms现状与SiteForge差距分析_V1.0.md` 的 17 项清单打钩。