diff --git a/src/assets/titleIcon/PRO+.png b/src/assets/titleIcon/PRO+.png new file mode 100644 index 0000000..0ed3620 Binary files /dev/null and b/src/assets/titleIcon/PRO+.png differ diff --git a/src/assets/titleIcon/PRO-Try.png b/src/assets/titleIcon/PRO-Try.png new file mode 100644 index 0000000..ba7fe75 Binary files /dev/null and b/src/assets/titleIcon/PRO-Try.png differ diff --git a/src/assets/titleIcon/PRO.png b/src/assets/titleIcon/PRO.png new file mode 100644 index 0000000..a6d6dfc Binary files /dev/null and b/src/assets/titleIcon/PRO.png differ diff --git a/src/assets/titleIcon/free.png b/src/assets/titleIcon/free.png new file mode 100644 index 0000000..d7c0614 Binary files /dev/null and b/src/assets/titleIcon/free.png differ diff --git a/src/panels/ICHelperPanel.ts b/src/panels/ICHelperPanel.ts index 80bed3f..a3b0f01 100644 --- a/src/panels/ICHelperPanel.ts +++ b/src/panels/ICHelperPanel.ts @@ -20,6 +20,37 @@ import { ChatHistoryManager } from "../utils/chatHistoryManager"; import { MessageType } from "../types/chatHistory"; import { getCachedUserInfo } from "../services/userService"; +/** + * 获取会员等级图标 URI + */ +function getTierIconUri( + webview: vscode.Webview, + context: vscode.ExtensionContext, + tierCode?: string +): string | undefined { + if (!tierCode) { + return undefined; + } + + const tierIconMap: Record = { + 'BASIC': 'free.png', + 'TRIAL': 'PRO-Try.png', + 'ADVANCED': 'PRO.png', + 'PROFESSIONAL': 'PRO+.png' + }; + + const iconFile = tierIconMap[tierCode]; + if (!iconFile) { + return undefined; + } + + const iconUri = webview.asWebviewUri( + vscode.Uri.joinPath(context.extensionUri, 'src', 'assets', 'titleIcon', iconFile) + ); + + return iconUri.toString(); +} + /** * 创建并显示 IC 助手面板 */ @@ -117,13 +148,15 @@ export async function showICHelperPanel( if (userInfo) { // 使用缓存的用户信息 console.log('[ICHelperPanel] 使用缓存的用户信息:', userInfo); + const tierIconUrl = getTierIconUri(panel.webview, context, userInfo.membership?.tierCode); panel.webview.postMessage({ command: 'updateUserInfo', userInfo: { userId: userInfo.userId, nickname: userInfo.nickname, username: userInfo.username - } + }, + tierIconUrl: tierIconUrl }); } else { // 如果没有缓存,从 session 中获取 diff --git a/src/services/userService.ts b/src/services/userService.ts index 351f04c..c02a2e6 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -7,7 +7,7 @@ import * as http from 'http'; import { URL } from 'url'; import * as vscode from 'vscode'; import { getStrangeLoopApiUrl, getConfig } from '../config/settings'; -import type { UserInfoResponse } from '../types/api'; +import type { UserInfoResponse, MembershipResponse, MultiMembershipVO, MembershipItemVO } from '../types/api'; /** * HTTP 请求选项 @@ -106,6 +106,14 @@ export interface UserInfo { permissions?: string[]; createTime?: string; loginDate?: string; + // 会员信息 + membership?: { + tierCode: string; + tierName: string; + tierLevel: number; + remainingDays?: number; + monthlyCredits?: number; + }; } /** @@ -150,14 +158,76 @@ export async function getUserInfo(token: string): Promise { } } +/** + * 获取用户会员信息 + * GET /strangeloop/api/membership/current + */ +export async function getMembershipInfo(token: string): Promise { + const apiPath = '/strangeloop/api/membership/current'; + const fullUrl = getStrangeLoopApiUrl(apiPath); + console.log('[UserService] 获取会员信息'); + console.log('[UserService] 请求地址:', fullUrl); + console.log('[UserService] Token:', token ? '已提供' : '未提供'); + + try { + const response = await request(apiPath, { + method: 'GET', + token + }); + + // 处理响应数据 - 检查 code 是否为 200 + if (response.code === 200 && response.data) { + console.log('[UserService] 会员信息获取成功:', response.data); + return response.data; + } + + console.error('[UserService] 获取会员信息失败:', response); + return null; + } catch (error) { + console.error('[UserService] 请求会员信息失败:', error); + return null; + } +} + +/** + * 会员等级映射 + */ +const TIER_LEVEL_MAP: Record = { + 'BASIC': 1, + 'TRIAL': 2, + 'ADVANCED': 3, + 'PROFESSIONAL': 4 +}; + +/** + * 获取最高等级的会员信息 + */ +function getHighestTierMembership(allMemberships?: MembershipItemVO[]): MembershipItemVO | null { + if (!allMemberships || allMemberships.length === 0) { + return null; + } + + // 按等级排序,获取最高等级 + return allMemberships.reduce((highest, current) => { + const currentLevel = TIER_LEVEL_MAP[current.tierCode] || 0; + const highestLevel = TIER_LEVEL_MAP[highest.tierCode] || 0; + return currentLevel > highestLevel ? current : highest; + }); +} + /** * 当获取到 token 时自动调用此函数 * 用于在登录成功后立即获取用户信息 */ export async function onTokenReceived(token: string): Promise { try { - console.log('[UserService] Token 已获取,正在获取用户信息...'); - const userInfo = await getUserInfo(token); + console.log('[UserService] Token 已获取,正在获取用户信息和会员信息...'); + + // 并行获取用户信息和会员信息 + const [userInfo, membershipInfo] = await Promise.all([ + getUserInfo(token), + getMembershipInfo(token) + ]); if (!userInfo) { console.warn('[UserService] 未能获取到用户信息'); @@ -192,6 +262,30 @@ export async function onTokenReceived(token: string): Promise { if (userInfo.loginDate) { console.log(`最后登录: ${userInfo.loginDate}`); } + + // 打印会员信息 - 从 allMemberships 中获取最高等级 + if (membershipInfo && membershipInfo.allMemberships) { + const highestTier = getHighestTierMembership(membershipInfo.allMemberships); + + if (highestTier) { + console.log(''); + console.log('会员信息:'); + console.log(`会员等级: ${highestTier.tierName} (${highestTier.tierCode})`); + console.log(`等级层级: ${highestTier.tierLevel}`); + console.log(`剩余天数: ${highestTier.remainingDays === -1 ? '永久' : highestTier.remainingDays + '天'}`); + console.log(`月度积分: ${highestTier.monthlyCredits}`); + + // 将最高等级会员信息合并到用户信息中 + userInfo.membership = { + tierCode: highestTier.tierCode, + tierName: highestTier.tierName, + tierLevel: highestTier.tierLevel, + remainingDays: highestTier.remainingDays, + monthlyCredits: highestTier.monthlyCredits + }; + } + } + console.log('='.repeat(60)); // 保存到持久化存储 diff --git a/src/types/api.ts b/src/types/api.ts index 3aec7ec..13e771a 100644 --- a/src/types/api.ts +++ b/src/types/api.ts @@ -345,6 +345,61 @@ export interface UserInfoResponse { }; } +// ============== 会员信息 ============== + +/** + * 会员单条记录 + */ +export interface MembershipItemVO { + membershipId: number | null; + tierCode: string; + tierName: string; + tierLevel: number; + expireTime: string | null; + remainingDays: number; + permanent: boolean; + nextGrantTime: string | null; + lastGrantTime: string | null; + grantCycle: number; + totalGranted: number; + monthlyCredits: number; + teamSeat: boolean; +} + +/** + * 用户会员信息 + */ +export interface UserMembershipVO { + userId: number; + tierCode: string; + tierName: string; + tierLevel: number; + allowedModelCombinations: string[]; + description?: string; + createdTime?: string; + updatedTime?: string; +} + +/** + * 多会员信息响应 + */ +export interface MultiMembershipVO extends UserMembershipVO { + displayTier?: MembershipItemVO; + allMemberships?: MembershipItemVO[]; + totalMonthlyCredits?: number; +} + +/** + * 会员信息响应 + * GET /strangeloop/api/membership/current + */ +export interface MembershipResponse { + code: number; + msg?: string; + message?: string; + data?: MultiMembershipVO; +} + // ============== 辅助类型 ============== /** 后端工具名称 */ diff --git a/src/views/conversationHistoryBar.ts b/src/views/conversationHistoryBar.ts index b16bf9b..15ac3ae 100644 --- a/src/views/conversationHistoryBar.ts +++ b/src/views/conversationHistoryBar.ts @@ -27,6 +27,8 @@ export function getConversationHistoryBarContent(): string { 加载中... + +