From f8e70e3fd5b91dde46170e255e101e8246ce56e8 Mon Sep 17 00:00:00 2001 From: wsb1224 Date: Sun, 7 Jun 2026 14:05:21 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dtoken=E4=BD=99=E9=A2=9D?= =?UTF-8?q?=E4=B8=8D=E8=B6=B3=E6=97=B6=E4=BB=8D=E8=83=BD=E7=BB=A7=E7=BB=AD?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=8A=9F=E8=83=BD=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:后端扣费用的是 fallback_amount=200(最低消费),用户剩14个token 时 14<200 导致扣费失败,但原有逻辑只在 remaining<=0 时才终止会话和 通知前端,remaining=14>0 时扣费失败被静默忽略。 修复: - 后端 voice_chat_router.py:扣费失败时(charge 返回 success=false) 始终终止事件循环并发送 error 事件,不再只判断 remaining<=0 - 后端 chat_router.py:文字聊天扣费失败时也发送 error 事件通知前端 - 前端 chatStore.js:token_update 消息带 warning 字段时(扣费失败), 标记 _realtimeTokenExhausted=true,断开 WebSocket,阻止继续使用 Co-Authored-By: Claude Opus 4.7 (1M context) --- backend/api/chat_router.py | 6 +++--- backend/api/voice_chat_router.py | 4 ++-- frontend/src/stores/chatStore.js | 14 ++++++++++---- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/backend/api/chat_router.py b/backend/api/chat_router.py index aab9287..9595756 100644 --- a/backend/api/chat_router.py +++ b/backend/api/chat_router.py @@ -274,11 +274,11 @@ async def websocket_chat_endpoint(websocket: WebSocket, user_id: str): ) # 余额耗尽时通知前端 remaining = charge_result.get("available_tokens", 0) - if remaining <= 0: - await websocket.send_text(json.dumps({ + # 扣费失败时也通知前端 + await websocket.send_text(json.dumps({ "type": "error", "error": "insufficient_tokens", - "message": "Token不足,请充值", + "message": "Token不足(剩余" + str(remaining) + "),请充值", "available_tokens": remaining, "timestamp": datetime.now().isoformat() })) diff --git a/backend/api/voice_chat_router.py b/backend/api/voice_chat_router.py index f1d8b80..797e2dd 100644 --- a/backend/api/voice_chat_router.py +++ b/backend/api/voice_chat_router.py @@ -1264,8 +1264,8 @@ async def receive_events_loop( })) except Exception: pass - # 余额耗尽时终止事件循环,防止火山引擎继续消费 - if remaining <= 0: + # 扣费失败时终止事件循环(余额不够最低消费也要停) + if True: logger.warning( f"[VOICE_TOKEN_EXHAUSTED] user_id={user_id} mode={charge_mode} " f"remaining={remaining}, breaking event loop and closing" diff --git a/frontend/src/stores/chatStore.js b/frontend/src/stores/chatStore.js index 1e92956..0f6f11d 100644 --- a/frontend/src/stores/chatStore.js +++ b/frontend/src/stores/chatStore.js @@ -1013,6 +1013,7 @@ export const useChatStore = defineStore('chat', () => { case 'token_update': { const remaining = data.remaining_tokens ?? data.data?.remaining_tokens ?? null + const warning = data.warning || data.data?.warning if (remaining !== null) { console.log('[ChatStore] Token update:', remaining, 'remaining') // 余额耗尽时标记并断开 @@ -1024,16 +1025,21 @@ export const useChatStore = defineStore('chat', () => { resetAudioState() break } + // 扣费失败(余额不够最低消费)也标记耗尽 + if (warning) { + console.warn('[ChatStore] Token charge failed:', warning, 'remaining:', remaining) + _realtimeTokenExhausted = true + ElMessage.error(`Token不足(剩余${remaining}),实时通话已结束`) + disconnectRealtimeWebSocket() + resetAudioState() + break + } // 充值后恢复:余额恢复时重置耗尽标记和 AI 处理状态 _realtimeTokenExhausted = false if (isAiProcessing.value) { isAiProcessing.value = false } } - const warning = data.warning || data.data?.warning - if (warning) { - console.warn('[ChatStore] Token warning:', warning) - } } break case 'error':