refactor(chat): remove legacy unlock response fields
This commit is contained in:
@@ -1,5 +1,21 @@
|
||||
# 前端锁消息解锁 API 接入文档
|
||||
|
||||
## 1. 文档状态
|
||||
|
||||
本文档描述 `POST /api/chat/unlock-private` 与 `GET /api/chat/history` 的最新接口协议,覆盖后端已有锁消息和前端临时创建锁消息两种场景。
|
||||
|
||||
当前协议已经实现并通过自动化测试。前端联调前可通过目标环境的 `/openapi.json` 确认 `UnlockPrivateRequest` 已包含 `messageId`、`lockType` 和 `clientLockId`。
|
||||
|
||||
`unlock-private` 响应已收敛为紧凑结构。历史兼容别名不再返回,前端必须使用本文列出的标准 camelCase 字段。
|
||||
|
||||
环境地址:
|
||||
|
||||
| 环境 | API Base URL |
|
||||
| --- | --- |
|
||||
| test 测试环境 | `https://testapi.banlv-ai.com` |
|
||||
| pro 预发环境 | `https://proapi.banlv-ai.com` |
|
||||
| production 生产环境 | `https://api.banlv-ai.com` |
|
||||
|
||||
## 2. 功能目标
|
||||
|
||||
前端可以先展示一条本地锁消息。用户点击解锁时,即使该消息还没有后端 `messageId`,后端也会创建对应的聊天记录,并根据余额决定是否生成真实内容。
|
||||
@@ -12,6 +28,17 @@
|
||||
4. 刷新页面不会自动解锁。未扣积分、未消耗免费私密额度时,`history` 仍返回 `locked=true`。
|
||||
5. 前端只能使用 `unlocked` 或 `lockDetail.locked` 判断锁状态,不能根据 URL 是否为空判断。
|
||||
|
||||
## 3. 身份认证
|
||||
|
||||
两个接口都使用当前聊天用户的登录 Token:
|
||||
|
||||
```http
|
||||
Authorization: Bearer <TOKEN>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
前端不得保存或传递 Supabase service key。
|
||||
|
||||
## 4. 解锁接口
|
||||
|
||||
### 4.1 请求地址
|
||||
@@ -124,13 +151,12 @@ curl -X POST 'https://api.banlv-ai.com/api/chat/unlock-private' \
|
||||
| --- | --- | --- |
|
||||
| `unlocked` | boolean | 本次操作后是否已真实解锁。 |
|
||||
| `reason` | string/null | 本次操作结果原因。 |
|
||||
| `messageId` / `message_id` | string | 后端真实消息 ID。首次临时锁请求后也会返回。 |
|
||||
| `messageId` | string | 后端真实消息 ID。首次临时锁请求后也会返回。 |
|
||||
| `clientLockId` | string/null | 后端识别到的前端锁 ID。 |
|
||||
| `lockType` | string/null | 规范化后的锁类型。 |
|
||||
| `reply` / `response` | string | 当前消息文字。 |
|
||||
| `content` | string/null | 允许展示时返回内容;私密内容未解锁时可以为 `null`。 |
|
||||
| `type` / `messageType` | string | `text` 或 `voice`。 |
|
||||
| `audioUrl` / `audio_url` | string | 锁定语音为空字符串,成功解锁后是完整 URL。 |
|
||||
| `content` | string/null | 当前消息文字;私密内容未解锁时可以为 `null`。 |
|
||||
| `type` | string | `text` 或 `voice`。 |
|
||||
| `audioUrl` | string | 锁定语音为空字符串,成功解锁后是完整 URL。 |
|
||||
| `image.url` | string/null | 图片完整 URL;是否展示仍由 `lockDetail.locked` 决定。 |
|
||||
| `creditBalance` | number/null | 操作后的当前积分。 |
|
||||
| `creditsCharged` | number | 本次实际扣除积分;失败或免费额度解锁为 0。 |
|
||||
@@ -141,6 +167,8 @@ curl -X POST 'https://api.banlv-ai.com/api/chat/unlock-private' \
|
||||
| `lockDetail.showUpgrade` | boolean | 是否展示充值入口。 |
|
||||
| `lockDetail.reason` | string/null | 锁定原因。 |
|
||||
|
||||
以下重复或与解锁无关的字段已经删除:`message_id`、`messageType`、`reply`、`response`、`audio_url`、`displayMessage`、`localizedMessage`、`paywallTriggered`、`showUpgrade`、`timestamp`、`isGuest`、亲密度字段以及 `blocked` 系列字段。
|
||||
|
||||
## 6. 余额不足
|
||||
|
||||
请求:
|
||||
@@ -163,15 +191,11 @@ curl -X POST 'https://api.banlv-ai.com/api/chat/unlock-private' \
|
||||
"unlocked": false,
|
||||
"reason": "insufficient_credits",
|
||||
"type": "voice",
|
||||
"messageType": "voice",
|
||||
"messageId": "<BACKEND_MESSAGE_ID>",
|
||||
"message_id": "<BACKEND_MESSAGE_ID>",
|
||||
"clientLockId": "fb_voice_37370387172559600_001",
|
||||
"lockType": "voice_message",
|
||||
"reply": "I left a voice message for you. Unlock it to listen.",
|
||||
"content": "I left a voice message for you. Unlock it to listen.",
|
||||
"audioUrl": "",
|
||||
"audio_url": "",
|
||||
"creditBalance": 5,
|
||||
"creditsCharged": 0,
|
||||
"requiredCredits": 20,
|
||||
@@ -215,14 +239,11 @@ curl -X POST 'https://api.banlv-ai.com/api/chat/unlock-private' \
|
||||
"unlocked": true,
|
||||
"reason": "ok",
|
||||
"type": "voice",
|
||||
"messageType": "voice",
|
||||
"messageId": "<BACKEND_MESSAGE_ID>",
|
||||
"clientLockId": "fb_voice_37370387172559600_001",
|
||||
"lockType": "voice_message",
|
||||
"reply": "I made this voice just for you.",
|
||||
"content": "I made this voice just for you.",
|
||||
"audioUrl": "https://api.banlv-ai.com/audio/<FILE>.mp3",
|
||||
"audio_url": "https://api.banlv-ai.com/audio/<FILE>.mp3",
|
||||
"creditBalance": 10,
|
||||
"creditsCharged": 20,
|
||||
"requiredCredits": 20,
|
||||
@@ -277,7 +298,6 @@ curl -X POST 'https://api.banlv-ai.com/api/chat/unlock-private' \
|
||||
"messageId": "<BACKEND_MESSAGE_ID>",
|
||||
"clientLockId": "fb_private_001",
|
||||
"lockType": "private_message",
|
||||
"reply": "<REAL_PRIVATE_REPLY>",
|
||||
"content": "<REAL_PRIVATE_REPLY>",
|
||||
"creditsCharged": 10,
|
||||
"requiredCredits": 10,
|
||||
@@ -391,16 +411,11 @@ interface UnlockPrivateData {
|
||||
unlocked: boolean;
|
||||
reason?: string | null;
|
||||
messageId: string;
|
||||
message_id: string;
|
||||
clientLockId?: string | null;
|
||||
lockType?: LockType | null;
|
||||
type: "text" | "voice";
|
||||
messageType: "text" | "voice";
|
||||
reply: string;
|
||||
response: string;
|
||||
content?: string | null;
|
||||
audioUrl: string;
|
||||
audio_url: string;
|
||||
image: {
|
||||
type?: string | null;
|
||||
url?: string | null;
|
||||
@@ -409,8 +424,6 @@ interface UnlockPrivateData {
|
||||
creditsCharged: number;
|
||||
requiredCredits: number;
|
||||
shortfallCredits: number;
|
||||
displayMessage: string;
|
||||
localizedMessage: string;
|
||||
lockDetail: LockDetail;
|
||||
}
|
||||
```
|
||||
@@ -424,7 +437,7 @@ interface UnlockPrivateData {
|
||||
| `insufficient_credits` | 积分不足 | 保存返回的 `messageId`,保持锁定并进入充值。 |
|
||||
| `content_generation_failed` | 临时锁内容生成失败 | 后端尝试退回本次积分;保持锁定,允许稍后重试。 |
|
||||
| `voice_generation_failed` | 已有语音消息生成失败 | 未扣积分,保持锁定并允许重试。 |
|
||||
| `quota_exhausted` | 已有私密文本每日免费额度用完 | 展示 `displayMessage`。 |
|
||||
| `quota_exhausted` | 已有私密文本每日免费额度用完 | 展示 `lockDetail.hint`。 |
|
||||
| `invalid_lock_type` | `lockType` 不支持 | 记录前端错误并停止请求。 |
|
||||
| `not_found` | 消息不存在且没有有效 `lockType` | 禁用当前解锁按钮或重新同步 history。 |
|
||||
|
||||
@@ -445,3 +458,39 @@ interface UnlockPrivateData {
|
||||
-> GET /api/chat/history
|
||||
-> 只按 lockDetail.locked 恢复锁定或解锁状态
|
||||
```
|
||||
|
||||
## 12. 测试方法
|
||||
|
||||
必须使用测试账号,不要用真实付费用户验证扣费。
|
||||
|
||||
1. 准备余额低于 20 的测试账号。
|
||||
2. 不传 `messageId`,使用唯一 `clientLockId` 请求 `voice_message`。
|
||||
3. 确认 `unlocked=false`、`creditsCharged=0`,并取得非空 `messageId`。
|
||||
4. 调用 history,确认同一 `messageId` 存在且 `locked=true`、`audioUrl=null`。
|
||||
5. 给测试账号增加到至少 20 积分。
|
||||
6. 使用相同 `messageId`、`lockType`、`clientLockId` 再次请求。
|
||||
7. 确认 `unlocked=true`、`creditsCharged=20`、`audioUrl` 非空。
|
||||
8. 再次调用 history,确认同一消息 `locked=false` 且 `audioUrl` 非空。
|
||||
9. 对图片 40 积分和私密文本 10 积分重复验证。
|
||||
10. 测试结束后记录测试账号积分变化;生成的聊天记录会保留在 history 中,没有自动清理接口。
|
||||
|
||||
后端自动化测试:
|
||||
|
||||
```powershell
|
||||
.\.venv\Scripts\python.exe -m pytest tests\test_private_unlock.py -q
|
||||
```
|
||||
|
||||
当前结果:`20 passed`;私密解锁、图片 paywall、私密相册和私密空间相关回归共 `48 passed`。
|
||||
|
||||
## 13. 兼容性与回滚
|
||||
|
||||
- 已有前端继续只传 `messageId` 的调用方式保持兼容。
|
||||
- 请求参数保持兼容:已有前端继续只传 `messageId` 仍可使用。
|
||||
- 响应字段有收敛:旧别名不再返回,前端需要按第 5、9 节使用标准字段。
|
||||
- 本次功能不需要数据库迁移,通过现有 `chat_messages` 和解锁记录保存状态。
|
||||
- 如果上线后需要回滚,只回滚本次后端路由和 schema 代码即可;已生成的聊天记录可保留,不影响旧 history 读取。
|
||||
- 上线前需将代码提交并推送到目标环境对应分支:`test`、`pro` 或 `main`,再部署对应环境。
|
||||
|
||||
## 14. Needs confirmation
|
||||
|
||||
- `https://api.cozsweet.com` 是否继续作为生产 API 别名,不在本次代码和环境配置中确认;本文统一使用正式生产地址 `https://api.banlv-ai.com`。
|
||||
|
||||
Reference in New Issue
Block a user