5.8 KiB
P8 长尾打包(第一批)· 设计 spec
日期:2026-10-01 | 决策:owner「可以」批准本批范围与顺序(②⑤ 后端小项+文案,① 权限开关 UI)。 本批不含 ③账号注销(新功能,待产品决策)与 ④静态兜底目录(按需)。
0. 一句话
清账批:把前几波刻意记为遗留的碎片收干净——后端三处 triage 小修、两份 CHANGELOG 去模板腔、前端把后端已支持但 UI 未开放的五个权限开关补齐。
1. 现状勘查(grounded,2026-10-01)
②-1 games.Detail 400 细分
handler/games.go:44-50:user/slug 格式校验失败时统一 WriteError(400, "invalid_request", "invalid game id")——调用方无法区分「user 格式非法」「slug 格式非法」。校验辅助已存在:service.UsernamePattern(namespace.go:11)、SlugPattern(:12)。改进:逐字段细分错误消息(如 user: invalid format / slug: invalid format),沿用 ValidationError.Fields 的既有表达风格。
②-2 admin 工作路由 slug 校验
router.go:26-29 四条路由(unpublish/republish/features/revoke)→ handler/admin.go 各 handler 直接调 adminWorkID(c)(:115-117 = JoinWorkID(c.Param("user"), c.Param("slug"))),无格式校验:非法格式会带进 service/DB,最终多半落 404(掩盖了「格式错」与「不存在」的区别)。对照公开面 games.Detail:47 是有校验的。改进:四条 admin 路由的 handler 入口补 UsernamePattern/SlugPattern 前置校验,非法即 400。
②-3 approve 同名竞态测试
JoinWorkID(user, slug) 同名撞车在 service 层有处理(approve precheck + Works().Create 的 ErrConflict→ErrWorkIDTaken),但无并发针对性测试。补一个并发 approve 同名 new_work 的竞态测试,断言最终只有一条成功、其余拿 ErrWorkIDTaken/ErrSubmissionConflict,且库中不出现重复行。
⑤ CHANGELOG 模板腔
crearte/docs/CHANGELOG.md头部仍写「本模板的重要变更建议统一记录在此文件中」+「参考了 Keep a Changelog 的思路,也可以根据团队习惯调整」——仓库已迭代到 0.19.x,是项目不是模板。crearte-server/docs/CHANGELOG.md头部「本项目的重要变更统一记录在此文件中」尚可,但英文行 "should be documented" 偏模板口吻。 改进:两仓头部改为项目自己的口径(保留 Keep a Changelog 的引用作格式出处说明),去掉「模板/建议/可以调整」的占位口吻。
① 权限开关 UI 全量
后端 feature key 全集(service/validate.go:60):eval, inlineScript, inlineStyle, wasm, coop, fullscreen, gamepad(7 个)。
前端 content/features.ts 的 EditableFeatures/FEATURE_ITEMS 只开放前 2 个,注释明写「其余 flag(inline…epad)schema 与后端均已支持,但 UI 暂不开放(YAGNI):默认值已覆盖绝大多数作品,按需再添」。
本批补齐剩余 5 个:inlineStyle、wasm、coop、fullscreen、gamepad。三处 UI 共用 FEATURE_ITEMS(投稿表单勾选区、admin 作品管理展开行、审核详情只读展示),故扩 EditableFeatures/FEATURE_ITEMS/collectFeatures/featuresToForm/hasAnyFeature 即三处同步生效;后端零改动。
2. 决策
- D-1 错误粒度:
games.Detail沿用 400 +invalid_request,仅细分 message(user:/slug:前缀)——不改状态码、不加新错误码,最小变更;既有测试只断言 400 不断言文案者不受影响,若有断言文案则同步更新(实现者核)。 - D-2 admin 校验位置:在 handler 入口补校验(与公开面
games.Detail同层),不在adminWorkID内改(其为纯拼接 helper,改它会波及调用点语义);四条 handler 复用同一小段校验。 - D-3 开关默认值全 false:五个新开关默认关闭(与现有一致——「默认值已覆盖绝大多数作品」)。
collectFeatures输出七键齐全(与既有「两键总是存在」纪律一致,后端据此区分「显式空对象=不放宽」vs「旧提交无键=保留」)。 - D-4 文案优先:五个新开关的
hint要写清风险与适用场景(这是①的主要工作量);文案风格沿用既有两条。 - D-5 ⑤ 仅头部:只改 CHANGELOG 头部占位话术,不动历史条目。
3. 范围
server(→0.14.0):②-1 错误细分、②-2 admin 校验、②-3 竞态测试、⑤ 头部文案;CHANGELOG 0.14.0 记 ②+⑤。 crearte(→0.20.0):① 五开关 UI 全量(三处同步 + 测试)、⑤ 头部文案;CHANGELOG 0.20.0 记 ①+⑤。 deploy:零改动。
4. 红线
- 不动 approve 落库逻辑本身(②-3 只加测试,不改行为);不动路由常量值;不动 CSP 执行面(① 只补 UI,运行时的 flag 消费逻辑已存在)。
- 不新增依赖、不新增迁移。
- 前端三处 UI 一致性:投稿表单 / admin 展开行 / 审核只读展示由同一
FEATURE_ITEMS驱动,不得为某一处另开硬编码。
5. 验收
- server 容器四连 RC=0;新增/改动的单测全绿;②-3 竞态测试在
-race下通过(若仓内既有 race 跑法,沿用)。 - 空库
-count=1集成腿全过、零静默 skip。 - curl 冒烟:
GET /api/games/<非法user>/x与/x/<非法slug>各返 400 且 message 可区分;admin 四路由对非法 slug 返 400(负例)。 - FE:vitest / typecheck / admin-flow / 主套件 RC=0;五开关在投稿表单可见可勾选、载荷含七键、admin 展开行可改、审核详情只读展示。
- 双路独立审查(依据 spec §1-§3 全量,非仅任务书)PASS 后合主;wrapper 记账(ROADMAP P8 行分批完成、CHANGELOG 0.2.8、spec/plan 索引)。