依据 Gitea v1.27.3 modules/charset/escape_stream.go 的判定规则 (analyzeDetectResults:仅当左右相邻字符都不是非 ASCII 时才标记, 且 _common 表恒定生效),把说明文档中会被标记的全角标点 (,:;?!() 等)改为半角,并保证成对括号宽度一致。 中文语境中的全角标点(左右均为中文)保持原样,不动代码与契约语义。 同时删除临时探针目录 _probe/(仅用于确认 Gitea 判定规则)。
53 lines
1.6 KiB
Markdown
53 lines
1.6 KiB
Markdown
# T-WEB-003 /api/songs CRUD
|
|
|
|
- **状态**: done
|
|
- **依赖**: T-WEB-002
|
|
|
|
## 目标
|
|
|
|
曲目的增删改查与分页检索,字段语义符合 `contracts/data-models.md` 2。
|
|
|
|
## load
|
|
|
|
```
|
|
DSH.md
|
|
contracts/rest-api.yaml # 锚点:#/paths/~1api~1songs
|
|
contracts/data-models.md # 锚点:1 通用约定、2 Song
|
|
web-server/src/songs/*
|
|
web-server/src/common/source.dto.ts
|
|
web-server/src/common/pagination.ts
|
|
```
|
|
|
|
## 端点
|
|
|
|
| 方法 | 路径 | 说明 |
|
|
|--|--|--|
|
|
| GET | `/api/songs` | 分页 + `q`/`artist`/`status` 过滤 |
|
|
| POST | `/api/songs` | 新建(来源五元组必填) |
|
|
| GET | `/api/songs/:id` | 详情 |
|
|
| PATCH | `/api/songs/:id` | 更新(来源需五元组同时提交) |
|
|
| DELETE | `/api/songs/:id` | **软删除** |
|
|
| POST | `/api/songs/:id/audio` | 上传音频(multipart,字段名 `file`) |
|
|
| POST | `/api/songs/:id/cover` | 上传封面 |
|
|
|
|
## 关键实现
|
|
|
|
- `likePattern()` 转义 `%` `_` `\`,用户输入不会被当作 LIKE 模式。
|
|
- `assertSourceTupleComplete()` 实现「全有或全无」:PATCH 只给部分来源字段 -> 400。
|
|
- PATCH 直接改 `status` 时校验状态机;非法流转 -> 409 并提示改用 `/api/reviews`。
|
|
- `toDtos()` 批量解析 asset -> URL,避免 N+1。
|
|
|
|
## 验收
|
|
|
|
```bash
|
|
npm run test:smoke
|
|
```
|
|
|
|
冒烟脚本 [1] 段落覆盖:创建 201、id 为 number、来源回显、tags 往返、bpm 为 number、
|
|
分页结构、模糊查询命中、音频上传后 `audioUrl` 以 `/static/` 开头、PATCH 生效、
|
|
部分来源字段 400、软删除后不再出现在列表。
|
|
|
|
## 结果
|
|
|
|
全部通过(见 `web-server/test/smoke.mjs`)。
|