feat(characters): use local character catalog
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
|
||||
迁移目标:
|
||||
|
||||
1. 后端提供统一角色目录。
|
||||
1. 前端维护统一的本地角色目录,后端只识别稳定角色 ID。
|
||||
2. 同一用户可以分别与多个角色聊天。
|
||||
3. 不同角色的聊天历史、媒体、锁内容、私密相册和打赏归属必须隔离。
|
||||
4. 钱包、VIP、支付套餐和每日免费额度继续按用户全局共享。
|
||||
@@ -29,79 +29,19 @@
|
||||
| pro 预发环境 | `https://proapi.banlv-ai.com` |
|
||||
| production 生产环境 | `https://api.banlv-ai.com` |
|
||||
|
||||
## 2. 角色目录接口
|
||||
## 2. 前端本地角色目录
|
||||
|
||||
### 2.1 请求
|
||||
角色列表不通过网络接口加载。前端在本地维护只读目录,并使用 `slug` 解析角色路径、使用 `id` 调用后端业务接口。
|
||||
|
||||
```http
|
||||
GET <API_BASE_URL>/api/characters
|
||||
Authorization: Bearer <TOKEN>
|
||||
```
|
||||
当前角色:
|
||||
|
||||
接口支持正式用户 Login Token 和游客 Guest Token。
|
||||
| `id` | `slug` | `displayName` | 头像 | 页面封面 | 私密空间 Banner |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `character_elio` | `elio` | Elio Silvestri | `/images/avatar/elio.png` | `/images/cover/elio.png` | `/images/private-room/banner/elio.png` |
|
||||
| `character_maya` | `maya` | Maya Tan | `/images/avatar/maya.png` | `/images/cover/maya.png` | `/images/private-room/banner/maya.png` |
|
||||
| `character_nayeli` | `nayeli` | Nayeli Cervantes | `/images/avatar/nayeli.png` | `/images/cover/nayeli.png` | `/images/private-room/banner/nayeli.png` |
|
||||
|
||||
### 2.2 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"items": [
|
||||
{
|
||||
"id": "character_elio",
|
||||
"slug": "elio",
|
||||
"displayName": "Elio Silvestri",
|
||||
},
|
||||
{
|
||||
"id": "character_maya",
|
||||
"slug": "maya",
|
||||
"displayName": "Maya Tan",
|
||||
},
|
||||
{
|
||||
"id": "character_nayeli",
|
||||
"slug": "nayeli",
|
||||
"displayName": "Nayeli Cervantes",
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
公开字段只有:
|
||||
|
||||
| 字段 | 类型 | 是否必填 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `id` | string | 是 | 稳定、唯一、不可变的角色业务 ID。 |
|
||||
| `slug` | string | 是 | 稳定、唯一、URL 安全的角色标识。 |
|
||||
| `displayName` | string | 是 | 前端展示名称。 |
|
||||
| `avatarUrl` | string | 是 | HTTPS 头像地址。 |
|
||||
|
||||
后端只返回启用角色,并按照后台运营顺序排列 `items`。前端直接使用数组顺序,不执行二次排序。
|
||||
|
||||
响应中不要增加以下字段:
|
||||
|
||||
- `enabled`
|
||||
- `sortOrder`
|
||||
- `coverUrl`
|
||||
- `chatBackgroundUrl`
|
||||
- `tagline`
|
||||
- `emptyChatGreeting`
|
||||
- `capabilities`
|
||||
- 创建人、更新时间等后台管理字段
|
||||
|
||||
当前约定所有返回的角色都支持聊天、私密空间和打赏。
|
||||
|
||||
### 2.3 角色字段规则
|
||||
|
||||
- `id` 建议使用不可枚举 UUID 或稳定业务 ID,最大长度 64。
|
||||
- `slug` 只允许小写字母、数字和连字符,建议校验 `^[a-z0-9]+(?:-[a-z0-9]+)*$`。
|
||||
- `slug` 发布后不能修改;必须修改时应保留旧 slug 重定向映射。
|
||||
- `displayName` 去除首尾空白后不能为空,建议最大长度 80。
|
||||
- `avatarUrl` 必须为有效 HTTPS URL。
|
||||
- `id` 和 `slug` 都需要数据库唯一约束。
|
||||
|
||||
角色目录不需要分页,也不返回 `total`。
|
||||
本地记录同时保存短名称及 Splash、Private Room、Tip 使用的角色文案。后端不提供头像、封面、展示名称或角色目录接口,但后端配置中的角色 ID 必须与前端目录一致。
|
||||
|
||||
## 3. 聊天接口调整
|
||||
|
||||
@@ -116,7 +56,7 @@ POST /api/chat/unlock-private
|
||||
POST /api/chat/unlock-history
|
||||
```
|
||||
|
||||
`characterId` 使用角色目录中的 `id`,例如 `character_elio`,不能传 slug 或显示名称。
|
||||
`characterId` 使用前端本地角色目录中的 `id`,例如 `character_elio`,不能传 slug 或显示名称。
|
||||
|
||||
后端必须使用以下维度定位聊天数据:
|
||||
|
||||
@@ -268,7 +208,7 @@ Authorization: Bearer <TOKEN>
|
||||
}
|
||||
```
|
||||
|
||||
- `items` 只包含当前启用角色,并保持角色目录顺序。
|
||||
- `items` 按前端本地角色目录顺序返回当前可用角色。
|
||||
- `message` 使用现有 ChatMessage wire 结构;没有历史时返回 `null`。
|
||||
- 锁消息继续遵循现有内容隐藏规则,不能通过预览接口泄露 URL 或锁定内容。
|
||||
- 查询应批量完成,避免后端内部出现按角色逐条查询的 N+1 问题。
|
||||
@@ -399,7 +339,7 @@ Tip 订单和最终支付记录需要保存 `recipient_character_id`,用于归
|
||||
|
||||
### 第一阶段:后端兼容
|
||||
|
||||
- 上线角色目录和新字段。
|
||||
- 上线角色 ID 配置和新字段。
|
||||
- Chat 请求缺少 `characterId` 时暂时默认 `character_elio`。
|
||||
- Private Room 缺少角色时暂时默认 Elio。
|
||||
- Tip 旧客户端缺少 recipient 时,旧 Tip 订单暂时默认 Elio。
|
||||
@@ -459,61 +399,22 @@ Tip 订单和最终支付记录需要保存 `recipient_character_id`,用于归
|
||||
|
||||
后端完成后至少验证:
|
||||
|
||||
1. `GET /api/characters` 只返回 4 个公开字段。
|
||||
2. 角色目录只包含启用角色,并严格按照后台排序。
|
||||
3. Login Token 和 Guest Token 均可获取角色目录并聊天。
|
||||
4. 同一用户在 Elio 和其他角色中的历史完全隔离。
|
||||
5. 发送消息、历史分页、单条解锁和历史解锁都限定当前角色。
|
||||
6. 使用 Elio 的 messageId 配合其他角色 ID 解锁时返回 `CHARACTER_MISMATCH`,且不扣积分。
|
||||
7. Private Room 的标准 characterId 与旧 character slug 在兼容期结果一致。
|
||||
8. Tip 订单正确保存 `recipient_character_id`,VIP 和 Top-up 不保存。
|
||||
9. 角色预览接口一次返回各启用角色的最新消息,没有历史时返回 `null`。
|
||||
10. 禁用角色从目录消失,直接访问业务接口返回 `CHARACTER_DISABLED`。
|
||||
11. 缺少角色字段的旧请求在兼容期仍进入 Elio。
|
||||
12. 历史数据回填前后记录数量、积分和解锁状态一致。
|
||||
1. 前端本地目录中的每个角色 ID 均可调用 Chat、Private Room 和 Tip 业务接口。
|
||||
2. Login Token 和 Guest Token 均可按角色 ID 聊天。
|
||||
3. 同一用户在 Elio 和其他角色中的历史完全隔离。
|
||||
4. 发送消息、历史分页、单条解锁和历史解锁都限定当前角色。
|
||||
5. 使用 Elio 的 messageId 配合其他角色 ID 解锁时返回 `CHARACTER_MISMATCH`,且不扣积分。
|
||||
6. Private Room 的标准 characterId 与旧 character slug 在兼容期结果一致。
|
||||
7. Tip 订单正确保存 `recipient_character_id`,VIP 和 Top-up 不保存。
|
||||
8. 角色预览接口一次返回各启用角色的最新消息,没有历史时返回 `null`。
|
||||
9. 后端禁用角色后,直接访问业务接口返回 `CHARACTER_DISABLED`;前端发布时同步移除本地记录。
|
||||
10. 缺少角色字段的旧请求在兼容期仍进入 Elio。
|
||||
11. 历史数据回填前后记录数量、积分和解锁状态一致。
|
||||
|
||||
## 13. OpenAPI 参考
|
||||
|
||||
```yaml
|
||||
paths:
|
||||
/api/characters:
|
||||
get:
|
||||
summary: List enabled characters in display order
|
||||
security:
|
||||
- bearerAuth: []
|
||||
responses:
|
||||
"200":
|
||||
description: Enabled characters
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [success, data]
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
const: true
|
||||
data:
|
||||
type: object
|
||||
required: [items]
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [id, slug, displayName, avatarUrl]
|
||||
additionalProperties: false
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
slug:
|
||||
type: string
|
||||
pattern: "^[a-z0-9]+(?:-[a-z0-9]+)*$"
|
||||
displayName:
|
||||
type: string
|
||||
avatarUrl:
|
||||
type: string
|
||||
format: uri
|
||||
/api/chat/history:
|
||||
get:
|
||||
parameters:
|
||||
|
||||
Reference in New Issue
Block a user