# 创建小说

创建小说元信息。导入正文请使用批量上传接口。

`POST /api/v2/novelinfo`

```json
{
  "platformId": 1,
  "authorId": 2,
  "title": "示例小说",
  "summary": "小说简介",
  "coverUrl": "https://example.com/cover.jpg",
  "status": "ONGOING",
  "tagIds": [3, 5]
}
```

| 请求键 | 类型 | 必填 | 默认/约束 |
| --- | --- | --- | --- |
| `platformId` | integer | 是 | 已存在的平台 ID。 |
| `authorId` | integer / null | 否 | 省略/null 保存为 null；非空时作者必须存在且属于 `platformId`。 |
| `title` | string | 是 | 去除首尾空白后不能为空。书名不唯一，不按书名自动去重。 |
| `summary` | string / null | 否 | 省略/null 保存为 null；空字符串与 null 不同。 |
| `coverUrl` | string / null | 否 | 省略/null 保存为 null；不验证或下载图片。 |
| `status` | string | 否 | `ONGOING` 连载中或 `COMPLETED` 已完结，区分大小写。省略/null/空字符串按 `ONGOING`。 |
| `tagIds` | integer[] / null | 否 | 标签库中已存在的标签 ID，不得重复。省略/null 按 `[]`；任一标签不存在返回 400，整次写入回滚。 |

POST 成功返回 `201` 和 `Location: /api/v2/novelinfo?id=10`；PUT 成功返回 `200`。
两者以及 GET 单条的响应结构：

```json
{
  "id": 10,
  "platformId": 1,
  "authorId": 2,
  "title": "示例小说",
  "summary": "小说简介",
  "coverUrl": "https://example.com/cover.jpg",
  "status": "ONGOING",
  "tagIds": [3, 5],
  "createdAt": "2026-10-29T04:00:00Z",
  "updatedAt": "2026-10-29T04:00:00Z"
}
```

`id`、`createdAt`、`updatedAt` 为只读生成字段；其余键定义与写入字段表相同。
可空字段即使为 null 也会返回。GET 列表为上述对象组成的数组。
`tagIds` 始终返回数组，按标签 ID 升序；无标签时为 `[]`。标签名称单独通过标签库查询，不在小说中重复存储。
批量创建的 `novel.tagIds` 使用相同规则；以 `novelId` 续传保留已有标签引用。
