feat: add solutions app, fix HomePage subpage_types, add pytest test suite

- apps/solutions: SolutionIndexPage/SolutionPage (mirrors products/cases)
- apps/home: HomePage.subpage_types now includes products/solutions/cases index pages (previously only blog, blocking admin page creation)
- pytest.ini + conftest.py (root_page/home_page fixtures)
- unit tests for core (SEOablePage, FormBlock API repr), products, cases, forms (lead submit API)
This commit is contained in:
2026-08-06 16:23:49 +08:00
parent 12f7caa158
commit 052fff1a40
16 changed files with 443 additions and 30 deletions
+52 -30
View File
@@ -134,13 +134,13 @@ flowchart TB
### 2.4 Monorepo 目录结构(沿用并细化)
```
wagtailcms/ # 当前仓库根目录(后端优先落地
wagtailcms/ # 当前仓库根目录(后端仓库,独立 git 仓库
├── manage.py
├── requirements/
│ ├── base.txt
│ ├── dev.txt
│ └── production.txt
├── config/ # 原 wagtailcms/ 设置包,拆分为分层 settings
├── wagtailcms/ # 设置包(实际未按早期草案重命名为 config/,沿用项目同名包)
│ ├── settings/
│ │ ├── base.py
│ │ ├── dev.py
@@ -149,19 +149,19 @@ wagtailcms/ # 当前仓库根目录(后端优先落地)
│ ├── wsgi.py
│ └── asgi.py
├── apps/
│ ├── core/ # 基础抽象:SEOablePage、健康检查、公共 Block
│ ├── home/ # 首页
│ ├── blog/ # 技术博客
│ ├── products/ # 产品
│ ├── solutions/ # 解决方案(行业)
│ ├── cases/ # 客户案例
│ ├── forms/ # 线索表单
│ └── api/ # DRF + Wagtail API v2 路由
│ ├── core/ # 基础抽象:SEOablePage、健康检查、公共 Block
│ ├── home/ # 首页
│ ├── blog/ # 技术博客
│ ├── products/ # 产品
│ ├── solutions/ # 解决方案(行业)⬜ 尚未创建
│ ├── cases/ # 客户案例
│ ├── forms/ # 线索表单
│ └── api/ # DRF + Wagtail API v2 路由
├── documents/ # 设计文档(当前目录)
└── frontend/ # 后续新增:Next.js 项目(独立子目录或独立仓库)
└── (frontend/ 已拆分为独立仓库,不在本仓库内,见下方说明)
```
> 说明:是否采用 Turborepo/pnpm workspace 做单仓多包管理,取决于前后端是否同仓维护。若团队分工明确(后端/前端各自团队),建议**拆分为两个独立仓库**,通过 OpenAPI/GraphQL Schema 契约解耦,避免单仓耦合过重;本项目当前后端已独立成仓,建议前端也独立建仓
> 说明:前后端已**拆分为两个独立 git 仓库**(后端本仓库 + `frontend/` 独立仓库),通过 REST APIWagtail API v2 + `/api/v1/custom/`)契约解耦。后端根 `.gitignore` 已排除 `frontend/`,避免嵌套仓库冲突。两个仓库目前均为本地仓库,尚未配置远程/推送
### 2.5 内容模型与数据库设计
@@ -214,24 +214,24 @@ Snippet(非页面树内容,用于跨页面复用):`TeamMember`、`Testim
### 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 | 用途 | 关键字段 | 状态 |
|---|---|---|---|
| HeroBlock | 首屏 | title, subtitle, cta_buttons(list), background_image/video | ✅ 已实现 |
| StatsBlock | 数据展示 | items: [{value, label}] | ✅ 已实现 |
| FeatureGridBlock | 能力/优势 | icon, title, description | ✅ 已实现(对应原 FeatureBlock |
| ProductCardBlock | 产品矩阵 | title, description, image, link | ⬜ 待开发 |
| LogoCloudBlock | 客户 Logo 墙 | logos: [{url, title}](暂为图片列表,未接入 Partner Snippet | ✅ 已实现(简化版) |
| CaseStudyBlock | 案例卡片 | case_page: PageChooser(cases.CaseStudyPage), summary | ✅ 已实现 |
| PricingBlock | 定价 | plans: [{name, price, features}] | ⬜ 待开发 |
| FAQBlock | 常见问题 | items: [{question, answer(richtext)}] | ✅ 已实现 |
| TimelineBlock | 发展历程 | items: [{year, event}] | ⬜ 待开发 |
| TeamBlock | 团队展示 | members: [SnippetChooser(TeamMember)] | ⬜ 待开发(依赖 TeamMember Snippet |
| TechStackBlock | 技术架构图 | items: [{icon, label}] | ⬜ 待开发 |
| VideoBlock | 视频 | video_file/embed_url, poster | ⬜ 待开发 |
| FormBlock | 线索表单 | form: SnippetChooser(forms.FormDefinition)`get_api_representation` 展开完整字段定义供前端渲染 | ✅ 已实现 |
| CTABlock | 行动号召 | heading, button_text, button_link | ✅ 已实现 |
所有 Block 统一放在 `apps/core/blocks/`,前后端各维护一份"type → 组件"映射表,并通过 CI 增加一致性校验(后端 Block 清单 vs 前端 `BlockRenderer` 映射表 diff)。
所有 Block 统一放在 `apps/core/blocks.py``COMMON_BLOCKS`,前端 `frontend/blocks/BlockRenderer.tsx` 维护对应的"type → 组件"映射表,目前两者手动保持一致;CI 自动化一致性校验仍待建立(见 §2.17 风险表)。
### 2.7 Headless API 设计规范(补全缺失细节)
@@ -347,10 +347,32 @@ CI 中要求单元测试覆盖率不低于 70%,核心 `apps/forms`(涉及线
| 阶段 | 目标 | 状态 |
|---|---|---|
| **Phase 0** | 需求与设计文档完善(本文档) | ✅ 已完成 |
| **Phase 1** | Wagtail 基础 CMS + REST API + Next.js 首页渲染 + 基础 SEO | 🚧 即将开始(本次同步启动后端骨架搭建 |
| **Phase 1** | Wagtail 基础 CMS + REST API + Next.js 首页渲染 + 基础 SEO | 🚧 进行中(进度见下方清单 |
| **Phase 2** | GraphQL、OpenSearch 中文搜索、工作流审核、多语言 | 待规划 |
| **Phase 3** | AI 能力(摘要/翻译/SEO 重写)、SaaS 化评估(视业务需要再决定是否引入多租户) | 待规划 |
**Phase 1 详细进度:**
已完成:
- [x] 后端 Django + Wagtail 项目骨架,分层 settingsbase/dev/production),独立 git 仓库并已提交
- [x] `apps/core``SEOablePage` 抽象基类、`COMMON_BLOCKS`9/14 个 Block,见 §2.6)、健康检查 `/healthz` `/readyz`、发布后 `page_published` signal → 前端 revalidate webhook
- [x] `apps/home`、`apps/blog`(含标签 `ClusterTaggableManager`
- [x] `apps/products`ProductIndexPage/ProductPage)、`apps/cases`CaseStudyIndexPage/CaseStudyPage
- [x] `apps/forms``FormDefinition`/`FormDefinitionField`/`Lead`Snippet 方式)+ 提交 API`/api/v1/custom/leads/`,限流 + 邮件通知)
- [x] Wagtail API v2 挂载、分页/过滤/排序(Wagtail 内置)
- [x] Next.js 前端脚手架(独立仓库):Header/Footer/layout、首页、博客/产品/案例列表与详情页、`BlockRenderer`、ISR + revalidate route`lint`/`build` 已验证通过
未完成(待规划排期):
- [ ] `apps/solutions`(解决方案/行业页面)尚未创建
- [ ] Snippet`TeamMember`/`Testimonial`/`Partner`/`NavigationMenu`/`SiteSettings``BaseSiteSetting`)均未创建
- [ ] 预览模式(Wagtail `preview_token` + Next.js Draft Mode)未实现
- [ ] 剩余 5 个 StreamField BlockPricing/Timeline/Team/TechStack/Video/ProductCard)未开发
- [ ] RBAC 落地(Wagtail `Group` + `GroupPagePermission` 实际配置)未开始
- [ ] 测试策略(pytest 单元/集成测试、前端组件测试、E2E)未编写,当前无 CI
- [ ] 生产安全 settings(§2.11 中 `SECURE_*`/CORS/CSRF 白名单等)未在 `production.py` 中逐项落实确认
- [ ] ICP 备案、隐私政策/Cookie 合规清单(§2.15)尚未启动
- [ ] 中文分词、OpenSearch 集成(Phase 2 提前项)未开始
### 2.17 风险与备选方案
| 风险 | 应对 |