feat(chat): migrate chat protocol to lock detail

This commit is contained in:
2026-06-24 14:06:30 +08:00
parent c280a3c1ea
commit 42c03f8901
34 changed files with 609 additions and 373 deletions
+123
View File
@@ -0,0 +1,123 @@
图片 URL 长相
真实图片 URL 大概是这种公开 Supabase Storage 地址:
https://lehwkihwnlqkavhcspel.supabase.co/storage/v1/object/public/elio-schedules/schedules/42/a1b2c3d4.jpg
前端不用拼 URL,也不用鉴权,后端会直接返回完整 URL。前端只读:
data.image?.url
消息接口
POST https://proapi.banlv-ai.com/api/chat/send
响应 data 固定字段:
{
"reply": "这是我昨天傍晚的照片,那会儿在外面走了走。",
"audioUrl": "",
"messageId": "msg_123",
"timestamp": 1782271808947,
"isGuest": false,
"image": {
"type": "elio_schedule",
"url": "https://lehwkihwnlqkavhcspel.supabase.co/storage/v1/object/public/elio-schedules/schedules/42/a1b2c3d4.jpg"
},
"lockDetail": {
"locked": false,
"showContent": true,
"showUpgrade": false,
"reason": null,
"hint": null,
"detail": null
}
}
前端用法:
const d = res.data
if (d.lockDetail?.showContent !== false && d.reply) {
renderText(d.reply)
}
if (d.image?.url) {
renderImage(d.image.url)
}
if (d.audioUrl) {
renderAudio(d.audioUrl)
}
if (d.lockDetail?.showUpgrade) {
showVipPaywall(d.lockDetail.hint)
}
image.type 目前主要是 elio_schedule,无图时:
"image": { "type": null, "url": null }
被限额/需要充值时:
{
"reply": "",
"image": { "type": null, "url": null },
"lockDetail": {
"locked": true,
"showContent": false,
"showUpgrade": true,
"reason": "daily_limit",
"hint": "免费消息额度已用完,开通会员后可以继续聊天。",
"detail": {
"type": "daily_msg_limit",
"usedToday": 3,
"limit": 3
}
}
}
历史接口
GET https://proapi.banlv-ai.com/api/chat/history?limit=50&offset=0
响应结构:
{
"messages": [
{
"role": "user",
"type": "text",
"content": "你最近在干嘛",
"id": "msg_1",
"created_at": "2026-06-24T10:00:00Z",
"audioUrl": null,
"image": { "type": null, "url": null },
"lockDetail": {
"locked": false,
"showContent": true,
"showUpgrade": false,
"reason": null,
"hint": null,
"detail": null
}
},
{
"role": "assistant",
"type": "text",
"content": "",
"id": "msg_2",
"created_at": "2026-06-24T10:00:03Z",
"audioUrl": null,
"image": { "type": null, "url": null },
"lockDetail": {
"locked": true,
"showContent": false,
"showUpgrade": true,
"reason": "private_message",
"hint": "他好像想说什么悄悄话,解锁会员才能看到…",
"detail": { "messageId": "msg_2" }
}
}
],
"total": 200,
"limit": 50,
"offset": 0,
"isVip": false
}
历史前端用法:
for (const m of data.messages) {
if (m.lockDetail?.showContent === false) {
renderLockedCard(m.lockDetail.hint)
if (m.lockDetail?.showUpgrade) showVipEntry()
continue
}
renderMessage(m.role, m.content)
if (m.image?.url) renderImage(m.image.url)
if (m.audioUrl) renderAudio(m.audioUrl)
}
前端不要再读这些旧字段:mode、relationshipStage、currentMood、intimacyChange、newIntimacy、voiceUrl、imageUrl、imageType、privateLocked、privateHint。统一是 audioUrl、image.url、lockDetail。