
# 企业官网 Headless CMS 设计方案 —— 分析与完善版

> 本文档基于 `documents/` 目录下现有的 5 份 PDF 设计文档整理分析，并结合当前代码仓库的真实状态（`manage.py` + 裸 Django 6.0 项目骨架，尚未引入 Wagtail），产出一份可直接指导开发的**完善版设计方案**。

---

## 一、现有文档分析

### 1.1 文档清单

| 文档 | 定位 |
|---|---|
| 基于 Wagtail 的现代前后端分离 CMS 设计方案.pdf | 通用架构设计（30 章节，最全面） |
| Next.js + Wagtail 企业级 Headless CMS 完整项目代码架构.pdf | Monorepo + 代码架构细化 |
| Wagtail CMS Backend 完整源码（企业级 Headless CMS）.pdf | 后端代码骨架示例 |
| Next.js 企业官网完整源码模板（基于 Wagtail Headless CMS）.pdf | 前端代码骨架示例 |
| 软件公司企业官网设计方案（基于 Wagtail Headless CMS）.pdf | 官网 IA / 页面 / 视觉设计 |
| 整体设计图.png | 首页高保真视觉稿（TechFlow 品牌示例） |

### 1.2 共性结论

5 份文档高度一致地收敛到同一技术选型：**Django 5 + Wagtail 7 + DRF + Strawberry GraphQL + Next.js 15 (React 19) + PostgreSQL + Redis + Celery + OpenSearch/Elasticsearch + MinIO/S3 + Docker/K8s**。这说明该技术栈选型是合理、成熟的，可以直接作为最终方案基线，**不需要重新论证**。

### 1.3 已发现的不足 / 待完善点

逐份文档都以"罗列技术名词 + 目录结构"为主，**普遍缺少可落地的细节**，具体归纳如下：

**A. 架构完整性缺口**
1. 只给出多租户"共享库 / Schema 隔离"两个方案名词，未给出本项目该如何取舍（本项目是单一企业官网，暂不需要多租户，过度设计）。
2. 没有数据库 ER 图 / 字段级设计，`Page` 模型都只写了 1-2 个示例字段。
3. 没有 Headless 预览（Preview）机制设计 —— 编辑在 Wagtail 后台点"预览"，Next.js 如何展示草稿内容（draft mode / preview token）完全没有提及。
4. 没有"发布后前端缓存失效"的具体实现（只画了 `Content Published → Event Bus → Webhook → Frontend Revalidate` 的框图，没有落地到 Wagtail `page_published` signal + Next.js `revalidateTag` 的代码级方案）。
5. API 只给了 URL 列表，没有分页、排序、过滤、错误码、版本弃用策略等规范。

**B. 安全设计不落地**
6. 安全章节只列了名词（HTTPS/CSP/WAF/JWT Rotation…），没有给出 Django 层面应设置的具体 settings 项（`SECURE_HSTS_SECONDS`、`SESSION_COOKIE_SECURE`、`CSRF_TRUSTED_ORIGINS`、DRF 节流类等）。
7. `CORS_ALLOW_ALL_ORIGINS = True` 是示例代码里的真实隐患，生产环境必须收敛为白名单。
8. 没有考虑富文本 XSS（Wagtail RichTextField 需要配置白名单 `WAGTAILADMIN_RICH_TEXT_EDITORS` / `bleach`）。

**C. 本地化 / 合规缺失（重要）**
9. 所有文档默认使用 **AWS S3 + Cloudflare CDN + Google 字体生态**，但"整体设计图.png"高保真稿明确显示目标是**中国大陆企业官网**（简体中文导航、"沪ICP备xxxxxxxx号-1"、中国移动/招商银行/中国平安/上汽集团/美团 等客户 Logo）。这是一个关键但被忽视的落地风险：
   - AWS S3/Cloudflare 在中国大陆访问速度和合规性都存在问题，需替换为 **阿里云 OSS/腾讯云 COS + 阿里云 CDN/腾讯云 CDN** 等国内方案。
   - 网站备案：需要 **ICP 备案**（个人/企业备案流程、幕布拍照要求等），这会影响上线时间表，必须提前规划。
   - 需要 **隐私政策 / Cookie 同意条**，遵循《个人信息保护法》(PIPL)，而不是文档里默认的 GDPR 话术。
   - Google Fonts / Google Maps 在中国大陆不可用，需要自托管字体（思源黑体/HarmonyOS Sans 子集化）、替换为高德/腾讯地图。
   - 人机验证码建议用极验/腾讯云验证码而非 reCAPTCHA。
   - 表单营销自动化提到 HubSpot/Salesforce，国内客户更常用企业微信 + 探迹/纷享销客等 CRM，需要补充可选国内方案。

**D. 工程化缺口**
10. 没有测试策略（单元测试、集成测试、E2E），文档提到 `pytest` 仅在 lint 章节一笔带过。
11. 没有环境变量与密钥管理方案（`.env.example`、多环境 settings 分层的实际代码）。
12. 没有健康检查 / 就绪探针（`/healthz`、`/readyz`），K8s 部署缺了这个会导致滚动更新时无法探活。
13. 日志没有约定格式（结构化 JSON 日志 + request-id 贯穿链路），排障困难。

**E. 内容建模细节不足**
14. StreamField Block 只给了 `HeroBlock`、`FeatureBlock` 两个例子，官网设计方案里列出的 14 个 Block（Stats/CTA/Pricing/FAQ/CaseStudy/Timeline/Team/LogoCloud/TechStack/BlogList/Video/Form 等）都没有代码，容易导致前后端字段不对齐。
15. 没有说明图片如何做响应式（Wagtail `Renditions` + `srcset`）与前端 `next/image` 的对接方式。

### 1.4 与当前代码仓库现状的差距

当前仓库仅是 `django-admin startproject` 生成的裸项目（Django 6.0.5，未安装 Wagtail/DRF，`INSTALLED_APPS` 只有 Django 内置应用，`urls.py` 只有 `admin/`）。也就是说，**目前处于"设计文档阶段"，代码尚未开始落地**，因此完善设计的同时，应当同步启动 Phase 1 的工程骨架搭建（见本文档第二部分 §18 路线图，以及仓库中已完成的初始化改动）。

---

## 二、完善后的整体设计方案

### 2.1 项目目标

在原有目标基础上明确补充**中国大陆本地化与合规目标**：

- 面向中国大陆企业客户的现代化 B2B 官网 + Headless CMS 内容中台
- 前后端彻底分离：Wagtail（内容与 API）+ Next.js（展示层）
- 满足 ICP 备案、PIPL 合规、国内 CDN/存储加速
- 可平滑演进到多语言（中/英/日）、多站点，**暂不做多租户**（YAGNI，待 SaaS 化需求明确后再引入 `django-tenants`）
- 内容编辑体验优先（StreamField 组件化、预览、审核流）

### 2.2 总体架构

```mermaid
flowchart TB
    subgraph Client
        Browser["浏览器 / 移动端"]
    end
    subgraph Edge["Edge / CDN"]
        CDN["国内 CDN（阿里云 CDN / 腾讯云 CDN）"]
    end
    subgraph FE["Next.js 前端（App Router）"]
        NextApp["React 19 + SSR/ISR"]
    end
    subgraph Gateway
        Nginx["Nginx / Traefik 网关"]
    end
    subgraph BE["Wagtail 后端"]
        Wagtail["Django 5 + Wagtail 7"]
        DRF["DRF REST v2"]
        GQL["Strawberry GraphQL"]
    end
    subgraph Infra["基础设施"]
        PG[(PostgreSQL)]
        Redis[(Redis)]
        Search[(OpenSearch)]
        OSS[(阿里云 OSS / 腾讯云 COS)]
        Celery["Celery Worker"]
    end

    Browser --> CDN --> NextApp --> Nginx --> Wagtail
    Wagtail --> DRF
    Wagtail --> GQL
    Wagtail --> PG
    Wagtail --> Redis
    Wagtail --> Search
    Wagtail --> OSS
    Wagtail -. 异步任务 .-> Celery
    Wagtail -- "publish webhook" --> NextApp
```

### 2.3 技术栈（含国内替代方案对照）

| 分层 | 通用方案（原文档） | 中国大陆推荐替代 / 补充 |
|---|---|---|
| 对象存储 | AWS S3 / MinIO | 阿里云 OSS / 腾讯云 COS（`django-storages` 均支持 S3 兼容协议） |
| CDN | Cloudflare | 阿里云 CDN、腾讯云 CDN、又拍云 |
| 字体 | Google Fonts | 思源黑体 / HarmonyOS Sans，构建期子集化后自托管，避免运行时依赖 Google |
| 地图 | Google Maps | 高德地图 / 腾讯地图 JS SDK |
| 验证码 | reCAPTCHA | 极验 GeeTest / 腾讯云验证码 |
| 短信/通知 | 无 | 阿里云短信服务 + 企业微信机器人 Webhook（替代 Slack） |
| 登录方式（可选） | OAuth2/Auth0/Keycloak | 保留通用 OAuth2；如需 C 端可加"企业微信扫码登录" |
| 搜索 | Elasticsearch/OpenSearch | 同左，国内可用阿里云 OpenSearch 托管版或自建 |
| 备案 | 无 | 需在页脚展示 ICP 备案号，域名/服务器需完成 ICP 备案后方可上线 |

其余后端（Django/Wagtail/DRF/GraphQL/PostgreSQL/Redis/Celery）与前端（Next.js/React/Tailwind/Zustand/React Query）技术栈维持原文档选型不变，属于合理选型。

### 2.4 Monorepo 目录结构（沿用并细化）

```
wagtailcms/                  # 当前仓库根目录（后端优先落地）
├── manage.py
├── requirements/
│   ├── base.txt
│   ├── dev.txt
│   └── production.txt
├── config/                  # 原 wagtailcms/ 设置包，拆分为分层 settings
│   ├── settings/
│   │   ├── base.py
│   │   ├── dev.py
│   │   └── production.py
│   ├── urls.py
│   ├── wsgi.py
│   └── asgi.py
├── apps/
│   ├── core/                # 基础抽象：SEOablePage、健康检查、公共 Block
│   ├── home/                # 首页
│   ├── blog/                # 技术博客
│   ├── products/            # 产品
│   ├── solutions/           # 解决方案（行业）
│   ├── cases/                # 客户案例
│   ├── forms/                # 线索表单
│   └── api/                  # DRF + Wagtail API v2 路由
├── documents/                 # 设计文档（当前目录）
└── frontend/                  # 后续新增：Next.js 项目（独立子目录或独立仓库）
```

> 说明：是否采用 Turborepo/pnpm workspace 做单仓多包管理，取决于前后端是否同仓维护。若团队分工明确（后端/前端各自团队），建议**拆分为两个独立仓库**，通过 OpenAPI/GraphQL Schema 契约解耦，避免单仓耦合过重；本项目当前后端已独立成仓，建议前端也独立建仓。

### 2.5 内容模型与数据库设计

在原有 `Page + StreamField` 思路基础上，补齐字段级设计：

```python
# apps/core/models.py
class SEOablePage(Page):
    """所有业务页面的抽象基类，统一 SEO 字段"""
    seo_title = models.CharField(max_length=70, blank=True)
    seo_description = models.CharField(max_length=160, blank=True)
    og_image = models.ForeignKey(
        "wagtailimages.Image", null=True, blank=True,
        on_delete=models.SET_NULL, related_name="+",
    )
    canonical_url = models.URLField(blank=True)
    schema_json = models.JSONField(blank=True, default=dict)

    promote_panels = Page.promote_panels + [
        FieldPanel("seo_title"),
        FieldPanel("seo_description"),
        FieldPanel("og_image"),
        FieldPanel("canonical_url"),
        FieldPanel("schema_json"),
    ]

    class Meta:
        abstract = True
```

页面树结构（细化到本项目实际官网 IA）：

```
HomePage
 ├── ProductIndexPage
 │     └── ProductPage[]
 ├── SolutionIndexPage
 │     └── SolutionPage[]
 ├── CaseStudyIndexPage
 │     └── CaseStudyPage[]
 ├── BlogIndexPage
 │     └── BlogPage[]
 ├── AboutPage
 ├── CareerIndexPage
 │     └── CareerPage[]
 └── ContactPage
```

Snippet（非页面树内容，用于跨页面复用）：`TeamMember`、`Testimonial`、`Partner/LogoCloud`、`NavigationMenu`、`SiteSettings`（`BaseSiteSetting`，存放公司电话/ICP备案号/社交账号等全局配置）。

### 2.6 StreamField 组件体系（补全清单）

| Block | 用途 | 关键字段 |
|---|---|---|
| HeroBlock | 首屏 | title, subtitle, cta_buttons(list), background_image/video |
| StatsBlock | 数据展示 | items: [{value, label}] |
| FeatureBlock | 能力/优势 | icon, title, description |
| ProductCardBlock | 产品矩阵 | title, description, image, link |
| LogoCloudBlock | 客户 Logo 墙 | logos: [SnippetChooser(Partner)] |
| CaseStudyBlock | 案例卡片 | case_page: PageChooser, summary |
| PricingBlock | 定价 | plans: [{name, price, features}] |
| FAQBlock | 常见问题 | items: [{question, answer(richtext)}] |
| TimelineBlock | 发展历程 | items: [{year, event}] |
| TeamBlock | 团队展示 | members: [SnippetChooser(TeamMember)] |
| TechStackBlock | 技术架构图 | items: [{icon, label}] |
| VideoBlock | 视频 | video_file/embed_url, poster |
| FormBlock | 线索表单 | form: SnippetChooser(FormDefinition) |
| CTABlock | 行动号召 | heading, button_text, button_link |

所有 Block 统一放在 `apps/core/blocks/`，前后端各维护一份"type → 组件"映射表，并通过 CI 增加一致性校验（后端 Block 清单 vs 前端 `BlockRenderer` 映射表 diff）。

### 2.7 Headless API 设计规范（补全缺失细节）

**REST（Wagtail API v2 + DRF 自定义）**
- 统一前缀：`/api/v2/`（Wagtail 官方 API v2）+ `/api/v1/custom/`（自定义业务接口，如表单提交）
- 分页：统一 `limit` / `offset`，响应体包含 `meta: {total, limit, offset}`
- 排序/过滤：`?order=-first_published_at`、`?type=blog.BlogPage`
- 错误响应统一结构：
  ```json
  { "error": { "code": "NOT_FOUND", "message": "..." } }
  ```
- 限流：DRF `ScopedRateThrottle`，公开只读接口 `100/min`，表单提交接口 `5/min`（防刷）
- 版本弃用策略：新版本上线后旧版本保留至少 2 个发布周期，响应头带 `Deprecation` / `Sunset`

**预览模式（原文档缺失，重要补充）**
- Wagtail 后台"预览"生成一次性 `preview_token`（存 Redis，TTL 5 分钟）
- Next.js 提供 `/api/draft` 路由，校验 token 后开启 Next.js Draft Mode，直接向 Wagtail 请求草稿版本 API（`?revision=latest`）

**发布后缓存失效（原文档只有框图，这里补齐实现）**
```python
# apps/core/signals.py
from wagtail.signals import page_published

@receiver(page_published)
def notify_frontend_revalidate(sender, instance, **kwargs):
    requests.post(
        settings.FRONTEND_REVALIDATE_URL,
        json={"tags": [f"page:{instance.id}", instance.url_path]},
        headers={"Authorization": f"Bearer {settings.REVALIDATE_SECRET}"},
        timeout=3,
    )
```
Next.js 侧对应 `revalidateTag` API Route，收到 webhook 后失效对应 ISR 缓存标签。

### 2.8 权限体系（RBAC 矩阵，细化落地）

| 角色 | 页面编辑 | 提交审核 | 审批发布 | 用户管理 | Snippet 管理 | Django Admin |
|---|---|---|---|---|---|---|
| Super Admin | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Tenant/Site Admin | ✅ | ✅ | ✅ | ✅（本站点） | ✅ | ❌ |
| Editor | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ |
| Reviewer | 查看 | ✅ | ✅ | ❌ | 查看 | ❌ |
| Author | ✅（仅自己） | ✅ | ❌ | ❌ | ❌ | ❌ |
| Viewer | 查看 | ❌ | ❌ | ❌ | ❌ | ❌ |

落地方式：使用 Wagtail 原生 `Group` + `GroupPagePermission` + `collections` 权限，不需要额外引入 RBAC 框架。

### 2.9 工作流与内容审核

采用 Wagtail 5+ 内置 `Workflow` 模块，定义 `草稿 → 编辑自检 → 审核 → 通过 → 定时发布`，并在 `TaskState` 变更时通过企业微信机器人 Webhook 通知责任人（替代原文档 Slack 方案）。

### 2.10 搜索、SEO、多语言

- 搜索：中文分词是关键缺失点，OpenSearch 需配置 **IK 分词插件**（原文档完全没提中文分词，这是国内落地必须解决的问题）。
- SEO：站点级 `sitemap.xml`（`wagtail.contrib.sitemaps`）+ 页面级 `schema_json`（JSON-LD）+ `robots.txt` 动态生成。
- 多语言：`WAGTAIL_I18N_ENABLED` + `wagtail-localize`，URL 采用 `/zh/`、`/en/` 前缀，默认 `/zh/` 对国内用户免前缀（根路径直接是中文）。

### 2.11 安全设计（落地到具体配置）

```python
# config/settings/production.py
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = "DENY"
CORS_ALLOWED_ORIGINS = env.list("CORS_ALLOWED_ORIGINS", default=[])
CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[])

REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_CLASSES": ["rest_framework.throttling.ScopedRateThrottle"],
    "DEFAULT_THROTTLE_RATES": {"public": "100/min", "forms": "5/min"},
}
```
富文本安全：`WAGTAILADMIN_RICH_TEXT_EDITORS` 限定可用 feature（禁用危险 HTML embed），后端渲染前统一走 `bleach` 白名单清洗。

### 2.12 可观测性与运维

- 健康检查：`/healthz`（存活）、`/readyz`（含数据库/Redis 连通性检查），供 K8s liveness/readiness 探针使用。
- 日志：`structlog` 输出 JSON，贯穿 `request_id`（中间件生成，透传前端 `X-Request-Id`）。
- 监控：Prometheus + Grafana（`django-prometheus`），错误监控 Sentry，链路追踪 OpenTelemetry。

### 2.13 测试策略（原文档缺失，新增）

| 层级 | 工具 | 覆盖内容 |
|---|---|---|
| 单元测试 | pytest + pytest-django + factory_boy | Model、Block、Serializer |
| 集成测试 | pytest + DRF APIClient | API 端到端响应结构 |
| 前端组件测试 | Vitest + React Testing Library | Block 组件渲染 |
| E2E | Playwright | 关键转化路径（首页 → 联系我们 → 表单提交） |
| 契约测试 | schemathesis（基于 OpenAPI schema） | 防止后端 API 破坏性变更 |

CI 中要求单元测试覆盖率不低于 70%，核心 `apps/forms`（涉及线索转化）不低于 90%。

### 2.14 CI/CD 与环境管理

- `.env.example` 统一列出所有必需环境变量，密钥类使用 GitHub Actions Secrets / 云厂商 KMS，不进仓库。
- 分支策略：`main`（生产）/ `staging`（预发）/ 功能分支 PR，合并前必须通过 Lint + Test + Build。
- 部署流水线：`Lint → Test → Build Image → 推送镜像仓库（阿里云 ACR/腾讯云 TCR）→ 部署 K8s（或轻量场景用 Docker Compose + 云服务器）`。

### 2.15 合规与本地化清单（新增，落地检查表）

- [ ] 完成 ICP 备案（域名 + 服务器均需在国内云厂商完成备案）
- [ ] 页脚展示备案号并链接工信部备案查询
- [ ] 隐私政策 / Cookie 同意条符合 PIPL 要求
- [ ] 字体/地图/验证码/CDN 均替换为可在国内正常访问的服务
- [ ] 表单收集的个人信息需明确告知用途并支持删除请求（PIPL 数据主体权利）

### 2.16 开发路线图（对齐当前仓库状态）

| 阶段 | 目标 | 状态 |
|---|---|---|
| **Phase 0** | 需求与设计文档完善（本文档） | ✅ 已完成 |
| **Phase 1** | Wagtail 基础 CMS + REST API + Next.js 首页渲染 + 基础 SEO | 🚧 即将开始（本次同步启动后端骨架搭建） |
| **Phase 2** | GraphQL、OpenSearch 中文搜索、工作流审核、多语言 | 待规划 |
| **Phase 3** | AI 能力（摘要/翻译/SEO 重写）、SaaS 化评估（视业务需要再决定是否引入多租户） | 待规划 |

### 2.17 风险与备选方案

| 风险 | 应对 |
|---|---|
| 过早引入多租户/微服务导致过度设计 | Phase 1/2 保持 Monolith，指标驱动再拆分 |
| 国内备案周期拖慢上线（通常 7-20 个工作日） | 项目启动即刻同步提交备案，不等开发完成 |
| Elasticsearch/OpenSearch 中文分词效果不佳 | 提前验证 IK 分词插件，或评估阿里云开放搜索托管方案 |
| StreamField Block 前后端字段不一致 | 建立 Block Schema 自动化一致性校验（CI） |
