feat:显示资源点
- 登录之后就获取资源点并持久化 - 显示剩余资源点到页面上 - 一轮对话完成之后重新获取资源点并且更新缓存
This commit is contained in:
@ -6,6 +6,7 @@ import { ChatHistoryManager } from "./utils/chatHistoryManager";
|
|||||||
import { ICCoderAuthenticationProvider } from "./services/icCoderAuthProvider";
|
import { ICCoderAuthenticationProvider } from "./services/icCoderAuthProvider";
|
||||||
import { VCDFileServer } from "./services/vcdFileServer";
|
import { VCDFileServer } from "./services/vcdFileServer";
|
||||||
import { initUserService } from "./services/userService";
|
import { initUserService } from "./services/userService";
|
||||||
|
import { initCreditsService } from "./services/creditsService";
|
||||||
|
|
||||||
export function activate(context: vscode.ExtensionContext) {
|
export function activate(context: vscode.ExtensionContext) {
|
||||||
console.log("🎉 IC Coder 插件已激活!");
|
console.log("🎉 IC Coder 插件已激活!");
|
||||||
@ -13,6 +14,9 @@ export function activate(context: vscode.ExtensionContext) {
|
|||||||
// 初始化用户服务
|
// 初始化用户服务
|
||||||
initUserService(context);
|
initUserService(context);
|
||||||
|
|
||||||
|
// 初始化 Credits 服务
|
||||||
|
initCreditsService(context);
|
||||||
|
|
||||||
// 初始化 VCD 文件服务器
|
// 初始化 VCD 文件服务器
|
||||||
const vcdFileServer = new VCDFileServer(context.extensionUri);
|
const vcdFileServer = new VCDFileServer(context.extensionUri);
|
||||||
vcdFileServer.start().then((port) => {
|
vcdFileServer.start().then((port) => {
|
||||||
|
|||||||
@ -147,16 +147,20 @@ export async function showICHelperPanel(
|
|||||||
if (userInfo) {
|
if (userInfo) {
|
||||||
// 使用缓存的用户信息
|
// 使用缓存的用户信息
|
||||||
console.log('[ICHelperPanel] 使用缓存的用户信息:', userInfo);
|
console.log('[ICHelperPanel] 使用缓存的用户信息:', userInfo);
|
||||||
|
console.log('[ICHelperPanel] Credits 余额:', userInfo.credits);
|
||||||
const tierIconUrl = getTierIconUri(panel.webview, context, userInfo.membership?.tierCode);
|
const tierIconUrl = getTierIconUri(panel.webview, context, userInfo.membership?.tierCode);
|
||||||
panel.webview.postMessage({
|
const messageData = {
|
||||||
command: 'updateUserInfo',
|
command: 'updateUserInfo',
|
||||||
userInfo: {
|
userInfo: {
|
||||||
userId: userInfo.userId,
|
userId: userInfo.userId,
|
||||||
nickname: userInfo.nickname,
|
nickname: userInfo.nickname,
|
||||||
username: userInfo.username
|
username: userInfo.username,
|
||||||
|
credits: userInfo.credits
|
||||||
},
|
},
|
||||||
tierIconUrl: tierIconUrl
|
tierIconUrl: tierIconUrl
|
||||||
});
|
};
|
||||||
|
console.log('[ICHelperPanel] 发送用户信息到前端:', messageData);
|
||||||
|
panel.webview.postMessage(messageData);
|
||||||
} else {
|
} else {
|
||||||
// 如果没有缓存,从 session 中获取
|
// 如果没有缓存,从 session 中获取
|
||||||
const session = await vscode.authentication.getSession("iccoder", [], {
|
const session = await vscode.authentication.getSession("iccoder", [], {
|
||||||
|
|||||||
@ -22,6 +22,33 @@ let lastUpdateTime: number = 0;
|
|||||||
/** 缓存有效期(5分钟) */
|
/** 缓存有效期(5分钟) */
|
||||||
const CACHE_TTL_MS = 5 * 60 * 1000;
|
const CACHE_TTL_MS = 5 * 60 * 1000;
|
||||||
|
|
||||||
|
/** ExtensionContext 用于持久化存储 */
|
||||||
|
let extensionContext: vscode.ExtensionContext | null = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化 Credits 服务(设置 context)
|
||||||
|
*/
|
||||||
|
export function initCreditsService(context: vscode.ExtensionContext): void {
|
||||||
|
extensionContext = context;
|
||||||
|
// 从持久化存储加载余额
|
||||||
|
const savedBalance = extensionContext.globalState.get<number>('icCoderCreditsBalance');
|
||||||
|
if (savedBalance !== undefined) {
|
||||||
|
cachedBalance = savedBalance;
|
||||||
|
lastUpdateTime = Date.now();
|
||||||
|
console.log('[CreditsService] 从持久化存储加载余额:', savedBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存余额到持久化存储
|
||||||
|
*/
|
||||||
|
async function saveBalance(balance: number): Promise<void> {
|
||||||
|
if (extensionContext) {
|
||||||
|
await extensionContext.globalState.update('icCoderCreditsBalance', balance);
|
||||||
|
console.log('[CreditsService] 余额已保存到持久化存储:', balance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新缓存的余额(从 SSE credit_update 事件调用)
|
* 更新缓存的余额(从 SSE credit_update 事件调用)
|
||||||
*/
|
*/
|
||||||
@ -29,6 +56,10 @@ export function updateCachedBalance(balance: number): void {
|
|||||||
cachedBalance = balance;
|
cachedBalance = balance;
|
||||||
lastUpdateTime = Date.now();
|
lastUpdateTime = Date.now();
|
||||||
console.log('[CreditsService] 余额已更新:', balance);
|
console.log('[CreditsService] 余额已更新:', balance);
|
||||||
|
// 异步保存到持久化存储
|
||||||
|
saveBalance(balance).catch(err => {
|
||||||
|
console.error('[CreditsService] 保存余额失败:', err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -203,8 +234,11 @@ export async function checkBalanceBeforeSend(): Promise<{
|
|||||||
/**
|
/**
|
||||||
* 清除缓存(登出时调用)
|
* 清除缓存(登出时调用)
|
||||||
*/
|
*/
|
||||||
export function clearBalanceCache(): void {
|
export async function clearBalanceCache(): Promise<void> {
|
||||||
cachedBalance = null;
|
cachedBalance = null;
|
||||||
lastUpdateTime = 0;
|
lastUpdateTime = 0;
|
||||||
|
if (extensionContext) {
|
||||||
|
await extensionContext.globalState.update('icCoderCreditsBalance', undefined);
|
||||||
|
}
|
||||||
console.log('[CreditsService] 余额缓存已清除');
|
console.log('[CreditsService] 余额缓存已清除');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { URL } from 'url';
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { getStrangeLoopApiUrl, getConfig } from '../config/settings';
|
import { getStrangeLoopApiUrl, getConfig } from '../config/settings';
|
||||||
import type { UserInfoResponse, MembershipResponse, MultiMembershipVO, MembershipItemVO } from '../types/api';
|
import type { UserInfoResponse, MembershipResponse, MultiMembershipVO, MembershipItemVO } from '../types/api';
|
||||||
|
import { fetchBalance, getCachedBalance } from './creditsService';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP 请求选项
|
* HTTP 请求选项
|
||||||
@ -114,6 +115,8 @@ export interface UserInfo {
|
|||||||
remainingDays?: number;
|
remainingDays?: number;
|
||||||
monthlyCredits?: number;
|
monthlyCredits?: number;
|
||||||
};
|
};
|
||||||
|
// Credits 余额
|
||||||
|
credits?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -221,12 +224,13 @@ function getHighestTierMembership(allMemberships?: MembershipItemVO[]): Membersh
|
|||||||
*/
|
*/
|
||||||
export async function onTokenReceived(token: string): Promise<UserInfo | null> {
|
export async function onTokenReceived(token: string): Promise<UserInfo | null> {
|
||||||
try {
|
try {
|
||||||
console.log('[UserService] Token 已获取,正在获取用户信息和会员信息...');
|
console.log('[UserService] Token 已获取,正在获取用户信息、会员信息和余额...');
|
||||||
|
|
||||||
// 并行获取用户信息和会员信息
|
// 并行获取用户信息、会员信息和余额
|
||||||
const [userInfo, membershipInfo] = await Promise.all([
|
const [userInfo, membershipInfo, credits] = await Promise.all([
|
||||||
getUserInfo(token),
|
getUserInfo(token),
|
||||||
getMembershipInfo(token)
|
getMembershipInfo(token),
|
||||||
|
fetchBalance()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!userInfo) {
|
if (!userInfo) {
|
||||||
@ -234,6 +238,15 @@ export async function onTokenReceived(token: string): Promise<UserInfo | null> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加 Credits 余额到用户信息
|
||||||
|
console.log('[UserService] 获取到的 Credits 余额:', credits);
|
||||||
|
if (credits !== null) {
|
||||||
|
userInfo.credits = credits;
|
||||||
|
console.log('[UserService] Credits 已添加到用户信息');
|
||||||
|
} else {
|
||||||
|
console.warn('[UserService] Credits 余额为 null,未添加到用户信息');
|
||||||
|
}
|
||||||
|
|
||||||
// 打印用户信息到控制台
|
// 打印用户信息到控制台
|
||||||
console.log('='.repeat(60));
|
console.log('='.repeat(60));
|
||||||
console.log('用户信息详情:');
|
console.log('用户信息详情:');
|
||||||
@ -286,6 +299,15 @@ export async function onTokenReceived(token: string): Promise<UserInfo | null> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 打印 Credits 余额
|
||||||
|
console.log('');
|
||||||
|
console.log('资源点余额:');
|
||||||
|
if (userInfo.credits !== undefined) {
|
||||||
|
console.log(`当前余额: ${userInfo.credits} Credits`);
|
||||||
|
} else {
|
||||||
|
console.log('当前余额: 未获取到余额信息');
|
||||||
|
}
|
||||||
|
|
||||||
console.log('='.repeat(60));
|
console.log('='.repeat(60));
|
||||||
|
|
||||||
// 保存到持久化存储
|
// 保存到持久化存储
|
||||||
@ -329,7 +351,18 @@ export function getCachedUserInfo(): UserInfo | null {
|
|||||||
console.warn('[UserService] ExtensionContext 未初始化');
|
console.warn('[UserService] ExtensionContext 未初始化');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return extensionContext.globalState.get<UserInfo>('icCoderUserInfo') || null;
|
const userInfo = extensionContext.globalState.get<UserInfo>('icCoderUserInfo') || null;
|
||||||
|
|
||||||
|
// 从 creditsService 加载余额并合并到用户信息中
|
||||||
|
if (userInfo) {
|
||||||
|
const cachedCredits = getCachedBalance();
|
||||||
|
if (cachedCredits !== null) {
|
||||||
|
userInfo.credits = cachedCredits;
|
||||||
|
console.log('[UserService] 从 creditsService 加载余额:', cachedCredits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return userInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import { ChatHistoryManager } from "./chatHistoryManager";
|
|||||||
import { dialogManager, DialogSession } from "../services/dialogService";
|
import { dialogManager, DialogSession } from "../services/dialogService";
|
||||||
import { userInteractionManager } from "../services/userInteraction";
|
import { userInteractionManager } from "../services/userInteraction";
|
||||||
import { healthCheck } from "../services/apiClient";
|
import { healthCheck } from "../services/apiClient";
|
||||||
import { checkBalanceBeforeSend } from "../services/creditsService";
|
import { checkBalanceBeforeSend, fetchBalance } from "../services/creditsService";
|
||||||
|
|
||||||
import type { RunMode, ServiceTier } from "../types/api";
|
import type { RunMode, ServiceTier } from "../types/api";
|
||||||
|
|
||||||
@ -200,6 +200,17 @@ async function handleUserMessageWithBackend(
|
|||||||
// 最后一次发送完整的段落
|
// 最后一次发送完整的段落
|
||||||
console.log("[MessageHandler] 对话完成, 段落数:", segments.length);
|
console.log("[MessageHandler] 对话完成, 段落数:", segments.length);
|
||||||
|
|
||||||
|
// 对话完成后重新获取余额(因为已经消耗了 Credits)
|
||||||
|
try {
|
||||||
|
console.log("[MessageHandler] 对话完成,重新获取余额...");
|
||||||
|
const newBalance = await fetchBalance();
|
||||||
|
if (newBalance !== null) {
|
||||||
|
console.log("[MessageHandler] 余额已更新:", newBalance);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[MessageHandler] 获取余额失败:", error);
|
||||||
|
}
|
||||||
|
|
||||||
const result = await panel.webview.postMessage({
|
const result = await panel.webview.postMessage({
|
||||||
command: "updateSegments",
|
command: "updateSegments",
|
||||||
segments: segments,
|
segments: segments,
|
||||||
|
|||||||
@ -250,14 +250,27 @@ export function getUserInfoComponentScript(): string {
|
|||||||
|
|
||||||
// 更新剩余 Credits
|
// 更新剩余 Credits
|
||||||
const creditsDetail = document.getElementById('creditsDetail');
|
const creditsDetail = document.getElementById('creditsDetail');
|
||||||
|
console.log('[UserInfoComponent] 更新 Credits 显示');
|
||||||
|
console.log('[UserInfoComponent] currentUserInfo.credits:', currentUserInfo.credits);
|
||||||
|
console.log('[UserInfoComponent] creditsDetail 元素:', creditsDetail);
|
||||||
if (creditsDetail) {
|
if (creditsDetail) {
|
||||||
creditsDetail.textContent = currentUserInfo.credits !== undefined ? currentUserInfo.credits.toString() : '-';
|
const creditsText = currentUserInfo.credits !== undefined ? currentUserInfo.credits.toString() : '-';
|
||||||
|
creditsDetail.textContent = creditsText;
|
||||||
|
console.log('[UserInfoComponent] Credits 已更新为:', creditsText);
|
||||||
|
} else {
|
||||||
|
console.warn('[UserInfoComponent] creditsDetail 元素未找到');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新用户信息显示
|
// 更新用户信息显示
|
||||||
function updateUserInfoDisplay(userInfo) {
|
function updateUserInfoDisplay(userInfo) {
|
||||||
currentUserInfo = userInfo;
|
currentUserInfo = userInfo;
|
||||||
|
console.log('[UserInfoComponent] 更新用户信息:', userInfo);
|
||||||
|
// 如果下拉面板已打开,立即更新显示
|
||||||
|
const dropdown = document.getElementById('userDetailDropdown');
|
||||||
|
if (dropdown && dropdown.classList.contains('active')) {
|
||||||
|
updateUserDetailModal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绑定下拉面板事件
|
// 绑定下拉面板事件
|
||||||
|
|||||||
@ -588,20 +588,25 @@ export function getWebviewContent(
|
|||||||
case 'updateUserInfo':
|
case 'updateUserInfo':
|
||||||
// 更新用户信息
|
// 更新用户信息
|
||||||
console.log('[WebView] 收到用户信息:', message.userInfo);
|
console.log('[WebView] 收到用户信息:', message.userInfo);
|
||||||
|
console.log('[WebView] Credits 字段值:', message.userInfo?.credits);
|
||||||
if (message.userInfo) {
|
if (message.userInfo) {
|
||||||
const userInfoData = {
|
const userInfoData = {
|
||||||
nickname: message.userInfo.nickname || message.userInfo.username || '用户',
|
nickname: message.userInfo.nickname || message.userInfo.username || '用户',
|
||||||
userId: message.userInfo.userId || message.userInfo.id,
|
userId: message.userInfo.userId || message.userInfo.id,
|
||||||
tierName: message.userInfo.tierName,
|
tierName: message.userInfo.tierName,
|
||||||
tierIconUrl: message.tierIconUrl,
|
tierIconUrl: message.tierIconUrl,
|
||||||
registerTime: message.userInfo.registerTime || message.userInfo.createdAt
|
registerTime: message.userInfo.registerTime || message.userInfo.createdAt,
|
||||||
|
credits: message.userInfo.credits
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('[WebView] 显示用户信息:', userInfoData);
|
console.log('[WebView] 显示用户信息:', userInfoData);
|
||||||
|
console.log('[WebView] userInfoData.credits:', userInfoData.credits);
|
||||||
|
|
||||||
// 调用更新用户头像图标按钮的函数
|
// 调用更新用户头像图标按钮的函数
|
||||||
if (typeof updateUserAvatarIconButton === 'function') {
|
if (typeof updateUserAvatarIconButton === 'function') {
|
||||||
updateUserAvatarIconButton(userInfoData);
|
updateUserAvatarIconButton(userInfoData);
|
||||||
|
} else {
|
||||||
|
console.warn('[WebView] updateUserAvatarIconButton 函数不存在');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user