58 lines
5.8 KiB
Markdown
58 lines
5.8 KiB
Markdown
# 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. 验收
|
||
|
||
1. server 容器四连 RC=0;新增/改动的单测全绿;②-3 竞态测试在 `-race` 下通过(若仓内既有 race 跑法,沿用)。
|
||
2. 空库 `-count=1` 集成腿全过、零静默 skip。
|
||
3. curl 冒烟:`GET /api/games/<非法user>/x` 与 `/x/<非法slug>` 各返 400 且 message 可区分;admin 四路由对非法 slug 返 400(负例)。
|
||
4. FE:vitest / typecheck / admin-flow / 主套件 RC=0;五开关在投稿表单可见可勾选、载荷含七键、admin 展开行可改、审核详情只读展示。
|
||
5. 双路独立审查(**依据 spec §1-§3 全量**,非仅任务书)PASS 后合主;wrapper 记账(ROADMAP P8 行分批完成、CHANGELOG 0.2.8、spec/plan 索引)。
|