9.2 KiB
9.2 KiB
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'。 - 复现:
.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.py5 个、core/tests.py17 个;实际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/ 和文章详情可访问,文章详情可链接关联项目;实现较薄但满足最低验收。 |
验证记录
已执行:
.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
修复验收建议
修复后至少补充并通过以下测试:
- 项目列表组合筛选:
language + framework + database + min_score + q同时存在时返回 200,并只展示满足全部条件的项目。 - 项目列表组合筛选无结果时返回 200,并展示空状态。
- 场景页 21 个 live 项目时第一页展示 20 个,第二页展示剩余项目。
- 场景页非法
page参数返回第一页,不报错。
修复完成后重新运行:
.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:新增组合筛选与空状态测试。
复查验证命令
已执行:
.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