feat:获取会员信息 并且展示title
This commit is contained in:
BIN
src/assets/titleIcon/PRO+.png
Normal file
BIN
src/assets/titleIcon/PRO+.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
BIN
src/assets/titleIcon/PRO-Try.png
Normal file
BIN
src/assets/titleIcon/PRO-Try.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 302 KiB |
BIN
src/assets/titleIcon/PRO.png
Normal file
BIN
src/assets/titleIcon/PRO.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
BIN
src/assets/titleIcon/free.png
Normal file
BIN
src/assets/titleIcon/free.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 138 KiB |
@ -20,6 +20,37 @@ import { ChatHistoryManager } from "../utils/chatHistoryManager";
|
|||||||
import { MessageType } from "../types/chatHistory";
|
import { MessageType } from "../types/chatHistory";
|
||||||
import { getCachedUserInfo } from "../services/userService";
|
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<string, string> = {
|
||||||
|
'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 助手面板
|
* 创建并显示 IC 助手面板
|
||||||
*/
|
*/
|
||||||
@ -117,13 +148,15 @@ export async function showICHelperPanel(
|
|||||||
if (userInfo) {
|
if (userInfo) {
|
||||||
// 使用缓存的用户信息
|
// 使用缓存的用户信息
|
||||||
console.log('[ICHelperPanel] 使用缓存的用户信息:', userInfo);
|
console.log('[ICHelperPanel] 使用缓存的用户信息:', userInfo);
|
||||||
|
const tierIconUrl = getTierIconUri(panel.webview, context, userInfo.membership?.tierCode);
|
||||||
panel.webview.postMessage({
|
panel.webview.postMessage({
|
||||||
command: 'updateUserInfo',
|
command: 'updateUserInfo',
|
||||||
userInfo: {
|
userInfo: {
|
||||||
userId: userInfo.userId,
|
userId: userInfo.userId,
|
||||||
nickname: userInfo.nickname,
|
nickname: userInfo.nickname,
|
||||||
username: userInfo.username
|
username: userInfo.username
|
||||||
}
|
},
|
||||||
|
tierIconUrl: tierIconUrl
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 如果没有缓存,从 session 中获取
|
// 如果没有缓存,从 session 中获取
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import * as http from 'http';
|
|||||||
import { URL } from 'url';
|
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 } from '../types/api';
|
import type { UserInfoResponse, MembershipResponse, MultiMembershipVO, MembershipItemVO } from '../types/api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP 请求选项
|
* HTTP 请求选项
|
||||||
@ -106,6 +106,14 @@ export interface UserInfo {
|
|||||||
permissions?: string[];
|
permissions?: string[];
|
||||||
createTime?: string;
|
createTime?: string;
|
||||||
loginDate?: 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<UserInfo | null> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户会员信息
|
||||||
|
* GET /strangeloop/api/membership/current
|
||||||
|
*/
|
||||||
|
export async function getMembershipInfo(token: string): Promise<MultiMembershipVO | null> {
|
||||||
|
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<MembershipResponse>(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<string, number> = {
|
||||||
|
'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 时自动调用此函数
|
* 当获取到 token 时自动调用此函数
|
||||||
* 用于在登录成功后立即获取用户信息
|
* 用于在登录成功后立即获取用户信息
|
||||||
*/
|
*/
|
||||||
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 = await getUserInfo(token);
|
|
||||||
|
// 并行获取用户信息和会员信息
|
||||||
|
const [userInfo, membershipInfo] = await Promise.all([
|
||||||
|
getUserInfo(token),
|
||||||
|
getMembershipInfo(token)
|
||||||
|
]);
|
||||||
|
|
||||||
if (!userInfo) {
|
if (!userInfo) {
|
||||||
console.warn('[UserService] 未能获取到用户信息');
|
console.warn('[UserService] 未能获取到用户信息');
|
||||||
@ -192,6 +262,30 @@ export async function onTokenReceived(token: string): Promise<UserInfo | null> {
|
|||||||
if (userInfo.loginDate) {
|
if (userInfo.loginDate) {
|
||||||
console.log(`最后登录: ${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));
|
console.log('='.repeat(60));
|
||||||
|
|
||||||
// 保存到持久化存储
|
// 保存到持久化存储
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
// ============== 辅助类型 ==============
|
// ============== 辅助类型 ==============
|
||||||
|
|
||||||
/** 后端工具名称 */
|
/** 后端工具名称 */
|
||||||
|
|||||||
@ -27,6 +27,8 @@ export function getConversationHistoryBarContent(): string {
|
|||||||
<span class="user-nickname" id="userNickname">加载中...</span>
|
<span class="user-nickname" id="userNickname">加载中...</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<img class="tier-icon" id="tierIcon" style="display: none;" />
|
||||||
|
|
||||||
<button class="new-conversation-button" onclick="createNewConversation()" title="新建对话">
|
<button class="new-conversation-button" onclick="createNewConversation()" title="新建对话">
|
||||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" fill="currentColor"/>
|
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" fill="currentColor"/>
|
||||||
@ -88,6 +90,14 @@ export function getConversationHistoryBarStyles(): string {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tier-icon {
|
||||||
|
width: 110px;
|
||||||
|
height: 35px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.history-dropdown-button {
|
.history-dropdown-button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@ -590,10 +590,19 @@ export function getWebviewContent(
|
|||||||
console.log('[WebView] 收到用户信息:', message.userInfo);
|
console.log('[WebView] 收到用户信息:', message.userInfo);
|
||||||
const userInfo = document.getElementById('userInfo');
|
const userInfo = document.getElementById('userInfo');
|
||||||
const userNickname = document.getElementById('userNickname');
|
const userNickname = document.getElementById('userNickname');
|
||||||
|
const tierIcon = document.getElementById('tierIcon');
|
||||||
if (userInfo && userNickname && message.userInfo) {
|
if (userInfo && userNickname && message.userInfo) {
|
||||||
const displayName = message.userInfo.nickname || message.userInfo.username || '用户';
|
const displayName = message.userInfo.nickname || message.userInfo.username || '用户';
|
||||||
console.log('[WebView] 显示用户名:', displayName);
|
console.log('[WebView] 显示用户名:', displayName);
|
||||||
userNickname.textContent = displayName;
|
userNickname.textContent = displayName;
|
||||||
|
|
||||||
|
// 显示会员等级图标
|
||||||
|
if (tierIcon && message.tierIconUrl) {
|
||||||
|
tierIcon.src = message.tierIconUrl;
|
||||||
|
tierIcon.style.display = 'block';
|
||||||
|
console.log('[WebView] 显示会员图标:', message.tierIconUrl);
|
||||||
|
}
|
||||||
|
|
||||||
userInfo.style.display = 'flex';
|
userInfo.style.display = 'flex';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user