Files
skelet/docs/review/phase2-review.md
T

171 lines
9.2 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.
# Phase 2 前台 MVP 评审
评审日期:2026-07-06
评审角色:全栈开发工程师
评审对象:DeepSeek 完成的 Phase 2(T-201 至 T-206)
## 结论
Phase 2 **暂不达标**。
基础页面、首页、项目详情、文章详情等主要页面已经可访问,自动化测试也通过;但筛选搜索的组合参数存在 500 错误,且场景页没有实现任务详单要求的分页。因此不能把 Phase 2 视为完整验收通过。
## 阻断问题
### P0:项目列表 `min_score + q` 组合筛选会 500
- 影响任务:T-204
- 位置:`core/models.py:151` 至 `core/models.py:162`
- 现象:`min_score` 合法时,代码把 `projects` 从 QuerySet 转成 list;后续如果同时存在 `q`,继续调用 `projects.filter(...)`,触发 `AttributeError: 'list' object has no attribute 'filter'`。
- 复现:
```bash
.venv/bin/python3.12.exe manage.py shell -c "from django.test import Client; c=Client(raise_request_exception=False); print(c.get('/projects/?min_score=18&q=Django').status_code)"
```
实际结果:`500`
任务契约要求 `language` / `framework` / `database` / `min_score` / `q` / `page` 多参数同时出现时按 AND 组合,见 `docs/04-architecture.md:157` 至 `docs/04-architecture.md:161`。当前实现只覆盖了单项筛选测试,没有覆盖组合筛选,见 `core/tests.py:119` 至 `core/tests.py:136`。
建议修复方向:
- 保持筛选链路的数据类型一致。
- 可以先用 ORM 对 `q`、language、framework、database 过滤,再对 `min_score` 做 Python 层过滤;也可以用 annotation 计算总分后在 ORM 层过滤。
- 增加组合筛选测试,例如 `/projects/?language=python&framework=django&min_score=18&q=Django`。
### P1:场景页未实现分页
- 影响任务:T-203
- 位置:`core/models.py:32` 至 `core/models.py:40`,`core/templates/core/scenario_page.html:7` 至 `core/templates/core/scenario_page.html:27`
- 现象:场景页直接把全部匹配项目放进 `context["projects"]`,模板也没有分页控件。
- 任务详单要求:场景页只列 live 项目,分页每页 20,空场景渲染空状态。
当前只满足 live 项目和空状态;分页缺失。已有测试只验证有项目和空状态,未验证超过 20 条时分页行为,见 `core/tests.py:40` 至 `core/tests.py:75`。
建议修复方向:
- 在 `ScenarioPage.get_context()` 中使用 `Paginator(projects, 20)`。
- 支持非法 `page` 回退到第一页,行为和项目列表页一致。
- 在 `scenario_page.html` 复用项目列表页的分页 UI。
- 增加 21 个项目的场景页分页测试。
## 非阻断问题
### P2:`docs/current-state.md` 测试数量已过期
- 位置:`docs/current-state.md:19`
- 现象:文档写的是 `home/tests.py` 5 个、`core/tests.py` 17 个;实际 `manage.py test` 输出为 31 个测试。
- 影响:不影响运行,但会误导后续 agent 判断当前状态。
建议修复:更新当前快照里的测试数量。
### P2:文章列表实现偏薄,但可接受
- 影响任务:T-206
- 位置:`core/templates/core/article_index_page.html:7` 至 `core/templates/core/article_index_page.html:13`
- 现状:文章列表通过 `page.get_children.live` 直接渲染标题链接,没有自定义 `get_context()`,也没有分页。
- 评估:T-206 只要求文章可访问、文章详情可链接项目详情;当前能满足最低验收。后续内容量增长时建议补排序、摘要和分页。
## 逐任务验收
| 任务 | 结论 | 说明 |
| --- | --- | --- |
| T-201 基础页面框架 | 达标 | `base.html` 有 viewport meta,导航、页脚、基础 CSS 已实现,容器使用 `max-width` 和响应式宽度。 |
| T-202 首页 | 达标 | 首页展示场景入口、推荐骨架、最新文章,内容来自数据库上下文。 |
| T-203 场景页和项目列表页 | 未达标 | 项目列表页有分页和空状态;场景页有项目和空状态,但缺少每页 20 的分页。 |
| T-204 筛选与基础搜索 | 未达标 | 单项筛选可用,但 `min_score` 与 `q` 组合请求返回 500,不符合 AND 组合契约。 |
| T-205 骨架详情页 | 达标 | 展示详情字段、评分、推荐理由,`is_sponsored=True` 时渲染 Sponsored 标识。 |
| T-206 文章列表和详情 | 达标 | `/articles/` 和文章详情可访问,文章详情可链接关联项目;实现较薄但满足最低验收。 |
## 验证记录
已执行:
```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 core.models import ScenarioPage,SkeletonProjectPage,ArticlePage; print(ScenarioPage.objects.live().count(), SkeletonProjectPage.objects.live().count(), ArticlePage.objects.live().count())"
.venv/bin/python3.12.exe manage.py shell -c "from django.test import Client; c=Client(raise_request_exception=False); paths=['/','/projects/','/articles/','/projects/?language=python','/projects/?min_score=18','/projects/?q=Django','/projects/?min_score=18&q=Django']; [print(p, c.get(p).status_code) for p in paths]"
```
结果摘要:
- `manage.py check`:0 error,9 个 treebeard/Wagtail 兼容 warning。
- `makemigrations --check --dry-run`:No changes detected。
- `manage.py test`:31 tests passed。
- seed 数据:3 个 live 场景、5 个 live 项目、2 篇 live 文章。
- URL 补测:
- `/`:200
- `/projects/`:200
- `/articles/`:200
- `/projects/?language=python`:200
- `/projects/?min_score=18`:200
- `/projects/?q=Django`:200
- `/projects/?min_score=18&q=Django`:500
## 修复验收建议
修复后至少补充并通过以下测试:
1. 项目列表组合筛选:`language + framework + database + min_score + q` 同时存在时返回 200,并只展示满足全部条件的项目。
2. 项目列表组合筛选无结果时返回 200,并展示空状态。
3. 场景页 21 个 live 项目时第一页展示 20 个,第二页展示剩余项目。
4. 场景页非法 `page` 参数返回第一页,不报错。
修复完成后重新运行:
```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
```
## 2026-07-06 修复复查
复查对象:提交 `714d218 Phase 2 评审修复: P0 min_score+&q=500 (ORM→list 顺序) + P1 场景分页(20/page) + P2 文档同步 + 36 tests`
复查结论:原 Phase 2 阻断问题 **已达标**,Phase 2 当前可视为通过复查。
### 复查结果
| 原问题 | 状态 | 证据 |
| --- | --- | --- |
| P0:项目列表 `min_score + q` 组合筛选会 500 | 已达标 | `ProjectIndexPage.get_context()` 先执行 `q` ORM 过滤,再做 `min_score` Python 过滤;`/projects/?min_score=18&q=Django` 复测返回 200。 |
| P1:场景页未实现分页 | 已达标 | `ScenarioPage.get_context()` 已使用 `Paginator(projects, 20)`;`scenario_page.html` 已渲染分页控件;新增 21 条项目分页测试。 |
| P2:`docs/current-state.md` 测试数量已过期 | 已达标 | 当前快照已更新为 31 个测试基线;修复后实际测试为 36 个。 |
| P2:文章列表实现偏薄 | 可接受 | 该项原本非阻断,当前未要求扩展;仍满足 T-206 最低验收。 |
### 代码核对
- `core/models.py:32` 至 `core/models.py:46`:场景页项目列表已分页,非法页码和越界页码回退第一页。
- `core/models.py:151` 至 `core/models.py:168`:项目列表筛选顺序已调整,`q` 不再在 list 上调用 `.filter()`。
- `core/templates/core/scenario_page.html:26` 至 `core/templates/core/scenario_page.html:36`:场景页已展示分页导航。
- `core/tests.py:78` 至 `core/tests.py:116`:新增场景页分页测试。
- `core/tests.py:178` 至 `core/tests.py:190`:新增组合筛选与空状态测试。
### 复查验证命令
已执行:
```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; from core.models import ScenarioPage,SkeletonProjectPage,ArticlePage; c=Client(raise_request_exception=False); scenario=ScenarioPage.objects.live().first(); paths=['/projects/?min_score=18&q=Django','/projects/?language=python&framework=django&min_score=18&q=Django','/projects/?language=go&framework=django&min_score=18', scenario.url, scenario.url+'?page=2', scenario.url+'?page=abc']; print('counts', ScenarioPage.objects.live().count(), SkeletonProjectPage.objects.live().count(), ArticlePage.objects.live().count()); [print(p, c.get(p).status_code) for p in paths]"
```
结果摘要:
- `manage.py check`:0 error,9 个 treebeard/Wagtail 兼容 warning。
- `makemigrations --check --dry-run`:No changes detected。
- `manage.py test`:36 tests passed。
- seed 数据:3 个 live 场景、5 个 live 项目、2 篇 live 文章。
- URL 复测:
- `/projects/?min_score=18&q=Django`:200
- `/projects/?language=python&framework=django&min_score=18&q=Django`:200
- `/projects/?language=go&framework=django&min_score=18`:200
- `/scenarios/modern-desktop-app-templates/`:200
- `/scenarios/modern-desktop-app-templates/?page=2`:200
- `/scenarios/modern-desktop-app-templates/?page=abc`:200