Files
opendance-android/contracts/data-models.md
T

151 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# contracts/data-models.md — 数据模型契约 v0.1.0
Web 服务端是唯一权威定义,本文件是另两端消费的**稳定视图**。
字段名在 JSON 中统一 `camelCase`;数据库列名 `snake_case`(见 `database.md`)。
## 1. 通用约定
- 所有资源返回 `id: number`(BIGINT,超出 2^53 前不会发生)。
- 所有时间为 ISO 8601 UTC 字符串,例:`2026-09-14T10:00:00.000Z`。
- 列表接口统一分页响应:
```json
{ "items": [], "total": 0, "page": 1, "pageSize": 20 }
```
- 错误响应统一:
```json
{ "statusCode": 400, "message": "…", "error": "Bad Request", "details": {} }
```
- **来源(出处)五元组**是必填项,出现在 `songs` / `motions` / `models` 三处:
| 字段 | 类型 | 说明 |
|---|---|---|
| `sourceType` | enum | 见 1.1 |
| `sourceName` | string | 来源名称,如「官方曲库」「作者名」 |
| `sourceUrl` | string | 来源链接,必须为 http(s) |
| `license` | string | 许可标识,如 `CC-BY-4.0`、`MIT`、`proprietary`、`unknown` |
| `author` | string | 作者 / 权利人 |
### 1.1 `SourceType` 枚举
```
original 原创
licensed 已获授权
cc Creative Commons
public_domain 公共领域
user_upload 用户上传(需审核)
other 其他(须在 sourceName 说明)
```
### 1.2 `PublishStatus` 枚举
```
draft 草稿,仅管理端可见
pending 待审核
published 已发布,客户端可见
rejected 已驳回
```
## 2. `Song`
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | number | 读 | |
| `title` | string | ✅ | 曲名,≤200 |
| `artist` | string | ✅ | 演唱者,≤200 |
| `album` | string | | 专辑,≤200 |
| `language` | string | | `zh` / `en` / `ja` / `ko` / `instrumental` |
| `durationMs` | number | | 时长毫秒 |
| `bpm` | number | | 节拍 |
| `difficulty` | number | | 1..5 |
| `tags` | string[] | | |
| `coverUrl` | string\|null | | 封面访问地址 |
| `audioUrl` | string\|null | | 音频访问地址 |
| `source*` | | ✅ | 来源五元组 |
| `status` | `PublishStatus` | | 默认 `draft` |
| `createdAt` / `updatedAt` | string | 读 | |
## 3. `Motion`(动作数据)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | number | 读 | |
| `songId` | number\|null | | 关联曲目 |
| `name` | string | ✅ | 动作名称,≤200 |
| `format` | enum | ✅ | `bvh` / `fbx` / `gltf` / `glb` / `keypoints-json` |
| `fileUrl` | string | 读 | 原始动作文件 |
| `fileSize` | number | 读 | 字节 |
| `fps` | number\|null | | 原始帧率 |
| `frameCount` | number\|null | | 原始帧数 |
| `durationMs` | number\|null | | |
| `keypointFormat` | string\|null | 读 | 预处理后固定为 `mediapipe33@0.1.0` |
| `keypointUrl` | string\|null | 读 | 预处理产物(NDJSON 或 .npz 的 JSON 封装) |
| `keypointFrames` | number\|null | 读 | 关键点帧数 |
| `preprocessState` | enum | 读 | `none` / `running` / `done` / `failed` |
| `preprocessError` | string\|null | 读 | |
| `source*` | | ✅ | 来源五元组 |
| `status` | `PublishStatus` | | |
| `createdAt` / `updatedAt` | string | 读 | |
## 4. `Model`(角色模型,VRM 优先)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | number | 读 | |
| `name` | string | ✅ | 模型名称,≤200 |
| `format` | enum | ✅ | `vrm` / `glb` / `gltf` / `fbx` |
| `vrmVersion` | string\|null | 读 | `0.x` / `1.0`,非 VRM 为 null |
| `isPrimary` | boolean | | 全局默认模型;**同一时刻全库至多一个 `true`** |
| `fileUrl` | string | 读 | |
| `fileSize` | number | 读 | |
| `thumbnailUrl` | string\|null | 读 | |
| `source*` | | ✅ | 来源五元组 |
| `status` | `PublishStatus` | | |
| `createdAt` / `updatedAt` | string | 读 | |
### 4.1 VRM 优先规则(冻结)
客户端选择默认模型的顺序:
```
1. 显式指定 modelId(曲目动作绑定)
2. models 中 format = 'vrm' 且 status = 'published' 且 is_primary = 1
3. models 中 format = 'vrm' 且 status = 'published',按 id 升序取第一个
4. models 中 status = 'published',按 (format='vrm' DESC, id ASC) 取第一个
5. 无可用模型 → 客户端使用内置占位模型
```
`isPrimary` 通过 `POST /api/models/:id/primary` 设置,服务端保证互斥(同一事务内先清零)。
## 5. `ReviewRecord`(审核流水)
| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | number | |
| `targetType` | enum | `song` / `motion` / `model` |
| `targetId` | number | |
| `action` | enum | `submit` / `approve` / `reject` / `withdraw` |
| `fromStatus` / `toStatus` | `PublishStatus` | |
| `reviewer` | string\|null | 审核人标识 |
| `comment` | string\|null | 审核意见 |
| `createdAt` | string | |
## 6. `CatalogBundle`(客户端一次性拉取,`GET /api/catalog/export`)
```json
{
"contractVersion": { "rest-api": "0.1.0", "data-models": "0.1.0", "skeleton-topology": "0.1.0", "websocket-keypoints": "0.1.0" },
"generatedAt": "2026-09-14T10:00:00.000Z",
"songs": [],
"motions": [],
"models": [],
"defaultModelId": 3
}
```
- 仅包含 `status = 'published'` 的记录。
- `defaultModelId` 由 4.1 的规则解析得出,客户端无需自行实现优先级。