# Phase 3 SEO 与上线准备评审
评审日期:2026-07-06
评审角色:全栈开发工程师
评审对象:DeepSeek 当前 Phase 3 改动
## 结论
Phase 3 **未达标**。
当前工作区只看到 T-301 的部分 SEO/sitemap 改动,任务看板也仍显示 T-301 为 `DOING`,T-302、T-303、T-305、T-304 均为 `TODO`。从 Phase 3 全部任务角度看,部署文档、SQLite 上线检查、访问统计、MVP 完整验收都没有完成。
## 阻断问题
### P0:Phase 3 任务未完成,不能按“全部任务”验收
- 影响任务:T-301、T-302、T-303、T-305、T-304
- 位置:`docs/06-tasks.md:53` 至 `docs/06-tasks.md:57`
- 现象:
- T-301 仍是 `DOING`。
- T-302/T-303/T-305/T-304 仍是 `TODO`。
- `progress.md` 没有 Phase 3 完成记录。
- `docs/current-state.md` 仍停留在 Phase 2 快照。
这说明 DeepSeek 当前提交并没有完成 Phase 3 的所有任务。
### P0:T-301 只完成了 sitemap,核心页面缺少 meta description/canonical
- 影响任务:T-301
- 位置:`skelet/templates/base.html:16` 至 `skelet/templates/base.html:18`
- 现象:模板只有在 `page.search_description` 存在时才输出 ``,但当前核心页面的 `search_description` 均为空。
- 复测页面:
- `/`
- `/projects/`
- `/scenarios/crm/`
- `/projects/djangocrm/`
- `/articles/`
复测结果:上述页面均返回 200,但均未输出 ``,也没有 `rel="canonical"`。
任务要求是“核心页面有 title、description、canonical 或等价设置;sitemap 可用”。当前只有 title 和 sitemap 可用,description/canonical 未达标。
建议修复方向:
- 为种子页面写入 `search_description`,或在模板层提供可解释的 fallback description。
- 明确 canonical 的处理方式:输出 ``,或在文档中说明采用的 Wagtail 等价机制并用测试覆盖。
- 增加测试断言首页、项目详情页、场景页、文章页含 meta description,并断言 sitemap 含项目 URL。
### P1:根目录 `test_sitemap.py` 是非规范测试文件,导入即写数据库并发请求
- 影响任务:T-301 / 验证基线
- 位置:`test_sitemap.py:1` 至 `test_sitemap.py:17`
- 现象:
- 文件位于仓库根目录,命名匹配 Django test discovery。
- 模块顶层直接执行 `django.setup()`、`Site.objects.get_or_create(...)` 和 `Client().get("/sitemap.xml")`。
- `manage.py test` 运行时会打印 `Site created/updated`、`sitemap.xml: 200` 等输出。
这不是稳定的测试写法。测试发现阶段导入模块就修改数据库,会污染开发数据库,也会让测试基线依赖导入副作用。
建议修复方向:
- 删除根目录 `test_sitemap.py`。
- 把 sitemap 验证迁移到 `core/tests.py` 或新的 app 测试文件中,使用 `TestCase` 和断言。
### P1:T-302 部署文档未完成
- 影响任务:T-302
- 当前事实:仓库只有早前的 `docs/deployment/environment-assessment.md`,它是 Lightsail 环境评估,不是可复制执行的部署手册。
- 缺口:
- 没有完整 Gunicorn systemd service 示例。
- 没有 Nginx server block 示例。
- 没有静态文件、media、环境变量、迁移、superuser、备份、重启/回滚步骤。
T-302 要求“2 核 2G VPS 上 Gunicorn + Nginx + SQLite 的部署步骤清楚”,当前未满足。
### P1:T-303 SQLite 上线检查未完成
- 影响任务:T-303
- 缺口:
- 未记录 JSON1 验证命令和结果。
- 未记录 WAL 启用命令和 `PRAGMA journal_mode;` 证据。
- 未记录备份策略。
- 未记录迁移 PostgreSQL 的触发条件。
任务要求这些内容写入部署文档;当前没有。
### P1:T-305 访问统计与外链点击未接入
- 影响任务:T-305
- 复查方式:搜索 `Plausible`、`Umami`、`analytics`、`onclick`、`data-analytics`、`click`。
- 结果:代码没有轻量统计脚本,没有外链点击事件标记或处理逻辑。
T-305 未实现。
### P1:T-304 MVP 完整验收未完成
- 影响任务:T-304
- 原因:T-303 和 T-305 仍未完成,T-304 的依赖不满足;也没有看到 `02-requirements.md` P0 完整验收记录。
T-304 未达标。
## 已完成部分
### Sitemap 基础可用
- 位置:
- `skelet/settings/base.py:56` 至 `skelet/settings/base.py:58`
- `skelet/urls.py:18` 至 `skelet/urls.py:23`
- `skelet/sitemaps.py:1` 至 `skelet/sitemaps.py:22`
- 复测:`/sitemap.xml` 返回 200,并包含项目、文章、场景 URL。
这一部分满足 T-301 的 sitemap 子项。
## 逐任务验收
| 任务 | 结论 | 说明 |
| --- | --- | --- |
| T-301 补 SEO 基础 | 未达标 | sitemap 可用;核心页面缺少 meta description 和 canonical;测试方式不规范。 |
| T-302 补部署文档 | 未达标 | 没有 Gunicorn + Nginx + SQLite 的可执行部署手册。 |
| T-303 SQLite 上线检查 | 未达标 | 没有 JSON1、WAL、备份、PostgreSQL 迁移触发条件记录。 |
| T-305 接入轻量访问统计与外链点击记录 | 未达标 | 未接入 Plausible/Umami 或等价方案,也没有外链点击统计。 |
| T-304 MVP 完整验收 | 未达标 | 依赖任务未完成;没有 P0 完整验收记录。 |
## 验证记录
已执行:
```bash
.venv/bin/python3.12.exe manage.py check
.venv/bin/python3.12.exe manage.py makemigrations --check --dry-run
.venv/bin/python3.12.exe manage.py test
.venv/bin/python3.12.exe manage.py showmigrations sites
.venv/bin/python3.12.exe manage.py shell -c "from wagtail.models import Page; qs=Page.objects.live().public().specific(); print([(p.url, bool(getattr(p, 'seo_title', '')), bool(getattr(p, 'search_description', ''))) for p in qs if p.url in ['/', '/projects/', '/scenarios/crm/', '/projects/djangocrm/', '/articles/']])"
.venv/bin/python3.12.exe manage.py shell -c "from django.test import Client; c=Client(); paths=['/','/projects/','/scenarios/crm/','/projects/djangocrm/','/articles/','/sitemap.xml']; [print(p, c.get(p).status_code, (' /var/www/skelet
```
### 复查验证命令
已执行:
```bash
.venv/bin/python3.12.exe manage.py check
.venv/bin/python3.12.exe manage.py makemigrations --check --dry-run
.venv/bin/python3.12.exe manage.py test
.venv/bin/python3.12.exe manage.py shell -c "from django.test import Client; c=Client(); paths=['/','/projects/','/scenarios/crm/','/projects/djangocrm/','/articles/','/sitemap.xml']; [print(p, c.get(p).status_code, 'desc', '