- docs/specs/2026-09-29-author-page-design.md: /users/:user pure aggregation page, minimal list, zero backend/migration changes - docs/ROADMAP.md: P4 scope refined per design (frontend filtering instead of API author filter), register spec in doc index
5.2 KiB
5.2 KiB
P4 作者主页(/users/:user)设计 / Author Page Design
- 状态:设计已批准(2026-09-29),待 spec 审阅后转实现计划。
- 路线图:
docs/ROADMAP.mdP4(第二波)。 - 范围定性:纯作品聚合页,极简列表;零后端改动、零数据迁移。
1. 背景与意图
crearte 的作品链接已按用户命名空间组织(/games/<user>/<slug>),但站内没有任何按作者聚合的入口:读者读完一个作品想看同作者的其他作品,只能回目录页手动搜索。P4 补上这块:给每位创作者一个可分享的主页 /users/:user,列出其全部已上架作品。
成功标准:从作品详情页点作者名能到作者页,页面只显示该作者的作品,按最新上架排序;未知用户名不报错。
约束(与维护者确认):
- 范围只到"纯作品聚合页":不做作者简介/头像等档案字段(不碰
users表、不碰上传链路)。 - 列表极简:无筛选侧栏、无查询参数(YAGNI,日后需要再加)。
2. 方案选型
| 方案 | 内容 | 取舍 |
|---|---|---|
| A. 纯前端过滤(已选) | AuthorView 复用 repo.listGames(),按 resolveUserSlug(game).user 过滤 |
零后端改动;与目录页同样拉全量列表(现状即如此,ETag/304);分页化另立子项目时再迁服务端过滤 |
B. 后端 GET /api/games?author= |
服务端过滤 + ETag 按参数派生 | 为分页铺路,但当下 YAGNI;动 handler/service/repository 三层 |
C. 新端点 GET /api/users/:user |
聚合用户信息 + 作品 | 引入用户名存在性探测面,超出纯聚合范围 |
对路线图的偏离:docs/ROADMAP.md P4 原文写"API 列表按作者过滤"。设计阶段发现列表 DTO(GameSummary)已含 user?/slug? 与 resolveUserSlug(),前端过滤即可满足需求,因此 P4 不修改 crearte-server(顺带不占用 Postgres 迁移号段)。路线图措辞随本 spec 落地同步修订。
3. 设计详述
3.1 路由与页面
- 新路由
/users/:user(与/games/:user/:slug风格一致),注册于src/app/router/index.ts。 - 新视图
src/app/views/AuthorView.vue,复用既有组件:GameCard、StatePanel、BaseButton等。 - 页面头部:
@<username>+ 作品数。语义注意:展示的是账号username(命名空间所有者),不是作品元数据author_name(自由文本、可空);卡片上的author_name照旧展示,两者不混用、不互相推导。 - 列表按"最新上架"倒序,复用
src/app/lib/filter.ts的既有排序逻辑(优先调用filterGames或其导出的比较器,不复制排序代码)。 - 面包屑「目录 / @user」。
- 入口:
GameView详情页作者名、GameCard作者字样 →/users/:user。
3.2 数据流
AuthorView → useAsync(repo.listGames())(src/app/data/ 的 mergeRepo:API + static 双源合并,API 宕机降级 static,与目录页同一链路)→ 按 resolveUserSlug(game).user 精确过滤(兼容旧式单段 id 作品:取 id 中 / 前段)→ sort: 'new' 排序 → 渲染。
3.3 边界与错误处理
- 未知用户名 / 该作者无已上架作品:空态「该作者暂无已上架作品」+ 返回目录按钮。不做用户存在性探测(需要新端点,YAGNI)。
- 可见性语义与目录页完全一致:列表接口只返回已上架未下架作品,作者页自动继承。
- 加载 / 失败态复用
useAsync+StatePanel既有模式,不做新错误面。
4. 测试
- Vitest:AuthorView 过滤正确性(含 legacy 单段 id 形态与
user字段缺失的回退)、空态渲染、sort: 'new'排序、作者名链接跳转。 - Playwright
noauth:/users/:user只显示该作者作品(基于src/fixtures/catalog现成作品数据)。 - 后端:无(零后端改动)。
5. 影响文件(预估)
| 文件 | 改动 |
|---|---|
src/app/router/index.ts |
注册 /users/:user |
src/app/views/AuthorView.vue |
新建 |
src/app/views/GameView.vue |
作者名加链接 |
src/app/components/GameCard.vue |
作者字样加链接(如展示作者) |
src/app/lib/filter.ts |
导出排序逻辑供 AuthorView 复用(如未导出) |
| 测试文件 | 与视图测试同目录、同命名约定(co-located)新建 |
docs/CHANGELOG.md(crearte 仓) |
双语条目 |
6. 验收标准
/users/:user只显示该作者的已上架作品(API+static 合并源),按最新上架倒序。- 旧式单段 id 作品不破坏过滤逻辑(
resolveUserSlug回退路径覆盖)。 - 未知用户名 / 无作品:空态渲染,无报错、无 404 路由错误。
GameView与GameCard的作者入口可跳转到作者页。- 新增 Vitest 与
noauthPlaywright 用例通过,且存量测试全绿(含vue-tsc类型检查)。 crearte-server零改动(本子项目不产生任何后端 diff)。
7. 明确不做(Out of scope)
- 作者简介 / 头像 / 关注 / 统计等档案与社交面(对应路线图 P5 之后再议)。
- 目录分页 / 服务端过滤(独立子项目)。
- 用户存在性探测、
GET /api/users/:user类端点。 - 静态兜底目录(P8)。