# 架构设计 > 本文讲“怎么把技术栈搭起来”:系统结构、职责划分、数据模型、技术难点、开发顺序。 ## 一、系统结构 ```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 变化必须同步更新本文。 - 不在代码里发明文档没有的接口、字段和状态。 - 不把静态内容、用户数据、缓存数据混在一个模型里。 - 高风险模块先单独验证,再接入完整页面或流程。