From fd11eadc19f27a1493b10c94f48266c2ceccf2e4 Mon Sep 17 00:00:00 2001 From: XiaoFeng <117837368+Fzhiyu1@users.noreply.github.com> Date: Mon, 12 Jan 2026 16:45:07 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E7=82=B9=E4=BD=99=E9=A2=9D=E6=9F=A5=E8=AF=A2=E4=B8=80=E7=9B=B4?= =?UTF-8?q?=E8=BF=94=E5=9B=9E0=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 改为直接调用 StrangeLoop /api/credit/balance 接口 - 携带 JWT token 认证,绕开 Gateway 缺少接口的问题 - 使用 availableCredits 作为余额值 - 固定 5s 超时,避免阻塞发送前检查 - 401/403 单独提示登录过期 - 补充 json.msg 错误消息兜底 --- src/services/creditsService.ts | 115 +++++++++++++++++++++++++++++---- 1 file changed, 102 insertions(+), 13 deletions(-) diff --git a/src/services/creditsService.ts b/src/services/creditsService.ts index d55b7f2..5b55a61 100644 --- a/src/services/creditsService.ts +++ b/src/services/creditsService.ts @@ -3,7 +3,11 @@ * 负责缓存余额、主动查询、发送前检测 */ -import { getCreditBalance } from './apiClient'; +import * as vscode from 'vscode'; +import * as https from 'https'; +import * as http from 'http'; +import { URL } from 'url'; +import { getStrangeLoopApiUrl } from '../config/settings'; import { getCachedUserInfo } from './userService'; /** 低余额阈值 */ @@ -43,22 +47,41 @@ function isCacheValid(): boolean { } /** - * 主动查询余额 + * StrangeLoop 余额响应类型 + */ +interface StrangeLoopBalanceResponse { + userId?: number; + availableCredits?: number; + totalCredits?: number; + error?: string; + message?: string; +} + +/** + * 主动查询余额(直接调用 StrangeLoop 接口) */ export async function fetchBalance(): Promise { - const userInfo = getCachedUserInfo(); - if (!userInfo?.userId) { - console.warn('[CreditsService] 无法查询余额:未登录'); - return null; - } - try { - const response = await getCreditBalance(userInfo.userId); - if (response.success && response.balance !== undefined) { - updateCachedBalance(response.balance); - return response.balance; + // 获取 JWT token + const session = await vscode.authentication.getSession('iccoder', [], { silent: true }); + if (!session?.accessToken) { + console.warn('[CreditsService] 无法查询余额:未登录'); + return null; + } + + const token = session.accessToken; + console.log('[CreditsService] 开始查询余额,token 长度:', token.length); + + // 直接调用 StrangeLoop 的 /api/credit/balance 接口 + const response = await callStrangeLoopBalance(token); + + if (response.availableCredits !== undefined) { + const balance = response.availableCredits; + updateCachedBalance(balance); + console.log('[CreditsService] 余额查询成功:', balance); + return balance; } else { - console.warn('[CreditsService] 查询余额失败:', response.error); + console.warn('[CreditsService] 查询余额失败:', response.error || response.message); return null; } } catch (error) { @@ -67,6 +90,72 @@ export async function fetchBalance(): Promise { } } +/** + * 调用 StrangeLoop 余额接口 + */ +async function callStrangeLoopBalance(token: string): Promise { + const urlStr = getStrangeLoopApiUrl('/api/credit/balance'); + const url = new URL(urlStr); + + const isHttps = url.protocol === 'https:'; + const httpModule = isHttps ? https : http; + + // 余额查询使用固定短超时,避免阻塞发送前检查 + const BALANCE_TIMEOUT_MS = 5000; + + const requestOptions: http.RequestOptions = { + hostname: url.hostname, + port: url.port || (isHttps ? 443 : 80), + path: url.pathname + url.search, + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + timeout: BALANCE_TIMEOUT_MS + }; + + return new Promise((resolve, reject) => { + const req = httpModule.request(requestOptions, (res) => { + let data = ''; + + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + console.log('[CreditsService] 响应状态码:', res.statusCode); + console.log('[CreditsService] 响应内容:', data); + + try { + const json = JSON.parse(data); + if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) { + resolve(json as StrangeLoopBalanceResponse); + } else if (res.statusCode === 401 || res.statusCode === 403) { + // 登录过期或无权限 + resolve({ error: '登录已过期,请重新登录' }); + } else { + resolve({ error: json.error || json.message || json.msg || `HTTP ${res.statusCode}` }); + } + } catch (e) { + resolve({ error: `解析响应失败: ${data}` }); + } + }); + }); + + req.on('error', (error) => { + reject(error); + }); + + req.on('timeout', () => { + req.destroy(); + reject(new Error('请求超时')); + }); + + req.end(); + }); +} + /** * 获取当前余额(优先使用缓存,过期则主动查询) */