Files
crearte-monorepo/docs/specs/2026-09-29-author-page-design.md
T
XingfenD 2f3bade817 docs(spec): P4 author page design (pure frontend aggregation)
- 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
2026-09-29 15:40:27 +08:00

5.2 KiB
Raw Blame History

P4 作者主页(/users/:user)设计 / Author Page Design

  • 状态:设计已批准(2026-09-29),待 spec 审阅后转实现计划。
  • 路线图:docs/ROADMAP.md P4(第二波)。
  • 范围定性:纯作品聚合页,极简列表;零后端改动、零数据迁移。

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. 验收标准

  1. /users/:user 只显示该作者的已上架作品(API+static 合并源),按最新上架倒序。
  2. 旧式单段 id 作品不破坏过滤逻辑(resolveUserSlug 回退路径覆盖)。
  3. 未知用户名 / 无作品:空态渲染,无报错、无 404 路由错误。
  4. GameView 与 GameCard 的作者入口可跳转到作者页。
  5. 新增 Vitest 与 noauth Playwright 用例通过,且存量测试全绿(含 vue-tsc 类型检查)。
  6. crearte-server 零改动(本子项目不产生任何后端 diff)。

7. 明确不做(Out of scope)

  • 作者简介 / 头像 / 关注 / 统计等档案与社交面(对应路线图 P5 之后再议)。
  • 目录分页 / 服务端过滤(独立子项目)。
  • 用户存在性探测、GET /api/users/:user 类端点。
  • 静态兜底目录(P8)。