Files
skelet/docs/04-architecture.md
T

229 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 架构设计
> 本文讲“怎么把技术栈搭起来”:系统结构、职责划分、数据模型、技术难点、开发顺序。
## 一、系统结构
```text
游客 / 搜索引擎 / 后台编辑者
|
v
Nginx
|
v
Gunicorn
|
v
Django + Wagtail
|
+--> Wagtail Pages / Snippets / Admin
+--> Templates / Static / Media
|
v
SQLite db.sqlite3
```
第一版没有独立前端应用,也没有公开业务 API。页面由 Wagtail/Django 渲染。
## 二、职责划分
**Wagtail / Django**
- 管理页面、骨架项目、分类、标签、评分和文章。
- 提供后台编辑、草稿、发布、slug、SEO 字段。
- 渲染公开页面。
- 提供基础搜索和筛选。
**模板层**
- 首页、场景页、语言页、框架页、骨架详情页、文章页。
- 展示筛选状态、空状态、评分、推荐理由、外部链接。
- 不承载业务规则和数据修正逻辑。
**数据层**
- SQLite 保存 Wagtail 内容、用户、权限、页面树、项目条目和评分。
- `media/` 保存上传图片。
- 不在数据库保存真实密钥、token 或第三方平台凭证。
**部署层**
- Nginx 处理 HTTPS、静态文件、媒体文件和反向代理。
- Gunicorn 运行 Django。
- SQLite 文件和 media 目录必须纳入备份。
## 三、数据模型
### 3.1 Wagtail Page 类型
| 模型 | 类型 | 说明 |
| --- | --- | --- |
| `HomePage` | Page | 首页,展示场景入口、推荐骨架、最新文章。 |
| `ScenarioIndexPage` | Page | 场景总览页。 |
| `ScenarioPage` | Page | 单个场景页,如 SaaS、管理后台、API 服务。 |
| `ProjectIndexPage` | Page | 项目列表页(`/projects/`),承载筛选与分页。 |
| `SkeletonProjectPage` | Page | 骨架项目详情页。 |
| `ArticleIndexPage` | Page | 文章列表。 |
| `ArticlePage` | Page | 评测、对比、避坑指南等内容文章。 |
模型归属:`HomePage` 在 `home` app(`wagtail start` 生成,沿用不改名);其余 Page、Snippet 和文章模型集中在 `core` app(见本文第六节)。
**页面树结构(唯一权威)**:
```text
HomePage (/)
├── ScenarioIndexPage (/scenarios/)
│ └── ScenarioPage (/scenarios/{slug}/)
├── ProjectIndexPage (/projects/)
│ └── SkeletonProjectPage (/projects/{slug}/)
└── ArticleIndexPage (/articles/)
└── ArticlePage (/articles/{slug}/)
```
- 项目详情页统一挂在 `ProjectIndexPage` 下,**不挂在场景页下**:一个项目属于多个场景(多对多),而页面树上只能有一个父节点。
- `ScenarioPage` 没有子项目页;它通过查询 `SkeletonProjectPage.objects.live().filter(scenarios=...)` 渲染本场景的项目列表。
- 列表筛选与分页在 `ProjectIndexPage.get_context()` 中做服务端查询实现(参数契约见 3.4),**不另建独立 Django view**。
- `/languages/{slug}/`、`/frameworks/{slug}/` MVP 由筛选参数替代,不建 Page(见 `routes.md`)。
**Wagtail 实现注意**:Page 模型上的多对多字段(`scenarios`、`languages`、`frameworks`、`databases`、`features`)必须用 modelcluster 的 `ParentalManyToManyField`,不能用 Django 普通 `ManyToManyField`,否则后台编辑、草稿和预览会出错。
### 3.2 Snippet / 辅助模型
| 模型 | 说明 |
| --- | --- |
| `Language` | Python、TypeScript、Go、Java、PHP 等。 |
| `Framework` | Wagtail、Django、FastAPI、Next.js、Laravel 等。 |
| `DatabaseOption` | SQLite、PostgreSQL、MySQL、MongoDB 等。 |
| `SkeletonFeature` | Auth、Admin、Payment、SEO、Docker、Tests 等功能标签。 |
| `AiCodingScore` | **MVP 不建此模型**:评分维度即 `SkeletonProjectPage` 的 6 个分数字段(见 3.3),避免两处维护同一套维度。 |
| `SponsorSlot` | 后续赞助展示位置,MVP 可先不启用。 |
| `AffiliateLink` | 后续返佣链接,MVP 可先不启用。 |
### 3.3 `SkeletonProjectPage` 核心字段
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `title` | Wagtail Page title | 项目名称。 |
| `summary` | Text | 一句话简介。 |
| `github_url` | URL | GitHub 地址。 |
| `official_url` | URL,可空 | 官网或文档。 |
| `license_name` | Char | 开源协议。 |
| `scenarios` | 多对多 | 适合场景。一个骨架常同时适合多个场景(如 SaaS 和管理后台),必须按多对多实现,避免上线后再迁移。 |
| `languages` | 多选 | 使用语言。 |
| `frameworks` | 多选 | 使用框架。 |
| `databases` | 多选 | 支持数据库。 |
| `features` | 多选 | 功能标签。 |
| `maturity` | Choice | experimental / stable / mature。 |
| `maintenance_status` | Choice | active / slow / unknown / archived。 |
| `structure_score` | Integer 0-5 | 目录结构清晰度。 |
| `docs_score` | Integer 0-5 | 文档完整度。 |
| `tests_score` | Integer 0-5 | 测试可用性。 |
| `example_score` | Integer 0-5 | 示例模块完整度。 |
| `dependency_score` | Integer 0-5 | 依赖克制度。 |
| `incremental_score` | Integer 0-5 | 增量开发难度(agent 在既有模式中添加功能是否有清晰落点;含任务切片友好度)。 |
| `recommended_for` | RichText / StreamField | 适合什么情况。 |
| `not_recommended_for` | RichText / StreamField | 不适合什么情况。 |
| `review_notes` | RichText / StreamField | 编辑评测。 |
| `is_featured` | Boolean | 是否首页推荐。 |
| `is_sponsored` | Boolean | 是否赞助展示,必须前台标识。 |
**必填 / 可空 / 默认(后台校验以此为准,实现不得自行猜测)**:
- 必填:`title`、`summary`、`github_url`、`scenarios`(至少 1 个)、`languages`(至少 1 个)、`maturity`、6 个评分字段、`recommended_for`。
- 可空:`official_url`、`license_name`、`frameworks`、`databases`、`features`、`not_recommended_for`、`review_notes`。
- 默认:`maintenance_status='unknown'`、`is_featured=False`、`is_sponsored=False`。
- 6 个评分字段使用 `MinValueValidator(0)` / `MaxValueValidator(5)`。
**评分模型纪律(唯一权威定义)**:
- AI Coding 友好度评分维度以上表 6 个 0-5 分项为唯一权威:目录结构、文档完整度、测试可用性、示例模块、依赖克制度、增量开发难度。
- **总分不单独录入**,由 6 个分项计算得出(总和 0-30,前台可换算为百分制展示),用模型 property 或注解实现,避免人工维护时总分和分项对不上。
- `business/` 下的文档引用本节,不得自行维护另一份维度清单。
- 每个项目除分数外必须有一句评语:为什么适合 / 不适合 AI coding。
### 3.4 URL 与筛选参数契约(唯一权威)
筛选条件必须可以通过 URL 表达,便于 SEO 和分享。列表页查询参数按下表实现,视图、模板和其他文档不得另造参数名或语义:
| 参数 | 取值 | 语义 |
| --- | --- | --- |
| `language` | `Language.slug`,单值 | 项目 `languages` 包含该语言 |
| `framework` | `Framework.slug`,单值 | 项目 `frameworks` 包含该框架 |
| `database` | `DatabaseOption.slug`,单值 | 项目 `databases` 包含该数据库 |
| `min_score` | 0-30 整数 | `total_score`(6 分项之和)≥ 该值 |
| `q` | 关键词字符串 | `title` 或 `summary` 不区分大小写包含(ORM `icontains`,MVP 不接搜索后端) |
| `page` | ≥1 整数 | 分页页码,每页 20 条 |
组合规则:
- 参数均可选,多个参数同时出现时按 AND 组合。
- MVP 每个参数只取单值;同名参数重复出现时取第一个。
- `min_score` / `page` 无法解析为合法整数时忽略该参数,不报错。
- 未知 slug 正常参与过滤,自然得到空列表并渲染空状态。
示例:
```text
/scenarios/saas/
/scenarios/saas/?language=python
/projects/?framework=wagtail&database=sqlite&min_score=18
/projects/?q=admin&page=2
```
MVP 使用服务端查询和分页,不做前端复杂状态管理。
## 四、关键技术难点
| 难点 | 说明 | 应对 |
| --- | --- | --- |
| Wagtail 内容模型设计 | Page 和 Snippet 边界如果设计错,后续迁移成本高。 | 先实现最小字段,避免过度抽象;模型变化同步文档。 |
| SQLite 上线 | 读多写少可行,但并发写有限。 | MVP 不做前台写入;启用 WAL;设置迁移 PostgreSQL 条件。 |
| 筛选与 SEO | 筛选如果只靠前端状态,不利于搜索引擎和分享。 | 使用服务端查询和可分享 URL。 |
| 评分可信度 | AI 友好度评分容易主观。 | 固定评分维度,详情页展示评分理由。 |
| 赞助与推荐冲突 | 商业化会损害信任。 | Sponsored 必须显式标识,和编辑推荐分离。 |
## 五、推荐开发顺序
1. 初始化 Wagtail 项目骨架,确认 Python 3.12 + SQLite 可运行。
2. 建立基础 Page / Snippet 模型和迁移。
3. 在后台录入首批示例数据。
4. 实现首页、场景页、列表页、详情页。
5. 实现筛选、基础搜索、空状态。
6. 补 SEO、sitemap、robots、部署说明。
7. 上线前配置 SQLite WAL、备份策略、Nginx/Gunicorn。
## 六、项目结构建议
T-001 初始化后目标结构(与 `wagtail start skelet .` 的实际输出对齐):
```text
skelet/
├── docs/
├── manage.py
├── requirements.txt
├── skelet/ # wagtail start 生成的配置包
│ ├── settings/ # base.py / dev.py / production.py
│ ├── urls.py
│ └── wsgi.py
├── home/ # wagtail start 生成,保留,承载 HomePage
├── search/ # wagtail start 生成,保留
├── core/ # 新建业务 app:Snippet、场景页、项目页、文章模型
│ ├── models.py
│ ├── templates/
│ ├── static/
│ └── tests.py
├── media/
└── README.md
```
**结构决策**:保留 `wagtail start` 生成的 `home` 和 `search` app;业务模型全部放在新建的 `core` app;测试放各 app 的 `tests.py`,暂不建独立 `tests/` 目录;`Dockerfile` 已删除。
已初始化:Wagtail 7.4.2 + Django 6.0.6,SQLite 可用,superuser `admin` 已创建。
## 七、架构纪律
- 业务事实和 schema 变化必须同步更新本文。
- 不在代码里发明文档没有的接口、字段和状态。
- 不把静态内容、用户数据、缓存数据混在一个模型里。
- 高风险模块先单独验证,再接入完整页面或流程。