feat: 增强API请求和邀请码验证的日志记录

This commit is contained in:
Roe-xin
2026-01-27 17:42:26 +08:00
parent 8daea722bd
commit 173132399e
2 changed files with 202 additions and 96 deletions

View File

@ -2,18 +2,28 @@
* API 客户端 * API 客户端
* 封装与后端的 HTTP 通信 * 封装与后端的 HTTP 通信
*/ */
import * as vscode from 'vscode'; import * as vscode from "vscode";
import * as https from 'https'; import * as https from "https";
import * as http from 'http'; import * as http from "http";
import { URL } from 'url'; import { URL } from "url";
import { getApiUrl, getConfig } from '../config/settings'; import { getApiUrl, getConfig } from "../config/settings";
import type { ToolCallResult, AnswerRequest, ToolResultResponse, AnswerResponse, ToolConfirmResponse, UserInfoResponse, InvitationVerifyRequest, InvitationVerifyResponse, InvitationStatusResponse } from '../types/api'; import type {
ToolCallResult,
AnswerRequest,
ToolResultResponse,
AnswerResponse,
ToolConfirmResponse,
UserInfoResponse,
InvitationVerifyRequest,
InvitationVerifyResponse,
InvitationStatusResponse,
} from "../types/api";
/** /**
* HTTP 请求选项 * HTTP 请求选项
*/ */
interface RequestOptions { interface RequestOptions {
method: 'GET' | 'POST' | 'PUT' | 'DELETE'; method: "GET" | "POST" | "PUT" | "DELETE";
headers?: Record<string, string>; headers?: Record<string, string>;
body?: unknown; body?: unknown;
timeout?: number; timeout?: number;
@ -24,7 +34,9 @@ interface RequestOptions {
*/ */
async function getAuthToken(): Promise<string | undefined> { async function getAuthToken(): Promise<string | undefined> {
try { try {
const session = await vscode.authentication.getSession('iccoder', [], { silent: true }); const session = await vscode.authentication.getSession("iccoder", [], {
silent: true,
});
return session?.accessToken; return session?.accessToken;
} catch { } catch {
return undefined; return undefined;
@ -41,7 +53,7 @@ async function request<T>(path: string, options: RequestOptions): Promise<T> {
// 自动获取 Token // 自动获取 Token
const token = await getAuthToken(); const token = await getAuthToken();
const isHttps = url.protocol === 'https:'; const isHttps = url.protocol === "https:";
const httpModule = isHttps ? https : http; const httpModule = isHttps ? https : http;
const requestOptions: http.RequestOptions = { const requestOptions: http.RequestOptions = {
@ -50,49 +62,84 @@ async function request<T>(path: string, options: RequestOptions): Promise<T> {
path: url.pathname + url.search, path: url.pathname + url.search,
method: options.method, method: options.method,
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
...(token ? { 'Authorization': `Bearer ${token}` } : {}), ...(token ? { Authorization: `Bearer ${token}` } : {}),
...options.headers ...options.headers,
}, },
timeout: options.timeout || timeout timeout: options.timeout || timeout,
}; };
console.log("[HTTP] 请求详情:", {
url: url.toString(),
method: options.method,
headers: requestOptions.headers,
hasToken: !!token,
body: options.body,
});
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const req = httpModule.request(requestOptions, (res) => { const req = httpModule.request(requestOptions, (res) => {
let data = ''; let data = "";
res.on('data', (chunk) => { // console.log('[HTTP] 响应状态码:', res.statusCode);
// console.log('[HTTP] 响应头:', res.headers);
res.on("data", (chunk) => {
data += chunk; data += chunk;
}); });
res.on('end', () => { res.on("end", () => {
console.log("[HTTP] 响应体:", data);
try { try {
const json = JSON.parse(data); const json = JSON.parse(data);
// console.log('[HTTP] 解析后的响应:', JSON.stringify(json, null, 2));
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) { if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
console.log("[HTTP] 请求成功");
resolve(json as T); resolve(json as T);
} else { } else {
reject(new Error(json.error || json.message || `HTTP ${res.statusCode}`)); console.error("[HTTP] 请求失败:", {
statusCode: res.statusCode,
error: json.error,
message: json.message,
msg: json.msg,
});
reject(
new Error(
json.error ||
json.message ||
json.msg ||
`HTTP ${res.statusCode}`,
),
);
} }
} catch (e) { } catch (e) {
// console.error('[HTTP] 解析响应失败:', e);
// console.error('[HTTP] 原始响应:', data);
reject(new Error(`解析响应失败: ${data}`)); reject(new Error(`解析响应失败: ${data}`));
} }
}); });
}); });
req.on('error', (error) => { req.on("error", (error) => {
// console.error('[HTTP] 请求错误:', error);
reject(error); reject(error);
}); });
req.on('timeout', () => { req.on("timeout", () => {
// console.error('[HTTP] 请求超时');
req.destroy(); req.destroy();
reject(new Error('请求超时')); reject(new Error("请求超时"));
}); });
if (options.body) { if (options.body) {
req.write(JSON.stringify(options.body)); const bodyStr = JSON.stringify(options.body);
// console.log('[HTTP] 发送请求体:', bodyStr);
req.write(bodyStr);
} }
req.end(); req.end();
// console.log('[HTTP] 请求已发送');
}); });
} }
@ -100,11 +147,13 @@ async function request<T>(path: string, options: RequestOptions): Promise<T> {
* 提交工具执行结果 * 提交工具执行结果
* POST /api/tool/result * POST /api/tool/result
*/ */
export async function submitToolResult(result: ToolCallResult): Promise<ToolResultResponse> { export async function submitToolResult(
result: ToolCallResult,
): Promise<ToolResultResponse> {
console.log(`[API] 提交工具结果: callId=${result.id}`); console.log(`[API] 提交工具结果: callId=${result.id}`);
return request<ToolResultResponse>('/api/tool/result', { return request<ToolResultResponse>("/api/tool/result", {
method: 'POST', method: "POST",
body: result body: result,
}); });
} }
@ -112,11 +161,13 @@ export async function submitToolResult(result: ToolCallResult): Promise<ToolResu
* 提交用户回答 * 提交用户回答
* POST /api/task/answer * POST /api/task/answer
*/ */
export async function submitAnswer(answer: AnswerRequest): Promise<AnswerResponse> { export async function submitAnswer(
answer: AnswerRequest,
): Promise<AnswerResponse> {
console.log(`[API] 提交用户回答: askId=${answer.askId}`); console.log(`[API] 提交用户回答: askId=${answer.askId}`);
return request<AnswerResponse>('/api/task/answer', { return request<AnswerResponse>("/api/task/answer", {
method: 'POST', method: "POST",
body: answer body: answer,
}); });
} }
@ -124,11 +175,15 @@ export async function submitAnswer(answer: AnswerRequest): Promise<AnswerRespons
* 提交工具确认响应Ask 模式) * 提交工具确认响应Ask 模式)
* POST /api/tool/confirm * POST /api/tool/confirm
*/ */
export async function submitToolConfirm(response: ToolConfirmResponse): Promise<ToolResultResponse> { export async function submitToolConfirm(
console.log(`[API] 提交工具确认: confirmId=${response.confirmId}, approved=${response.approved}`); response: ToolConfirmResponse,
return request<ToolResultResponse>('/api/tool/confirm', { ): Promise<ToolResultResponse> {
method: 'POST', console.log(
body: response `[API] 提交工具确认: confirmId=${response.confirmId}, approved=${response.approved}`,
);
return request<ToolResultResponse>("/api/tool/confirm", {
method: "POST",
body: response,
}); });
} }
@ -137,9 +192,9 @@ export async function submitToolConfirm(response: ToolConfirmResponse): Promise<
* GET /api/dialog/health * GET /api/dialog/health
*/ */
export async function healthCheck(): Promise<{ status: string }> { export async function healthCheck(): Promise<{ status: string }> {
return request<{ status: string }>('/api/dialog/health', { return request<{ status: string }>("/api/dialog/health", {
method: 'GET', method: "GET",
timeout: 5000 timeout: 5000,
}); });
} }
@ -166,9 +221,9 @@ export interface StopDialogResponse {
*/ */
export async function stopDialog(taskId: string): Promise<StopDialogResponse> { export async function stopDialog(taskId: string): Promise<StopDialogResponse> {
console.log(`[API] 停止对话: taskId=${taskId}`); console.log(`[API] 停止对话: taskId=${taskId}`);
return request<StopDialogResponse>('/api/dialog/stop', { return request<StopDialogResponse>("/api/dialog/stop", {
method: 'POST', method: "POST",
body: { taskId } body: { taskId },
}); });
} }
@ -184,11 +239,13 @@ export interface CompactDialogResponse {
* 手动压缩对话历史 * 手动压缩对话历史
* POST /api/dialog/compact * POST /api/dialog/compact
*/ */
export async function compactDialog(taskId: string): Promise<CompactDialogResponse> { export async function compactDialog(
taskId: string,
): Promise<CompactDialogResponse> {
console.log(`[API] 压缩对话: taskId=${taskId}`); console.log(`[API] 压缩对话: taskId=${taskId}`);
return request<CompactDialogResponse>('/api/dialog/compact', { return request<CompactDialogResponse>("/api/dialog/compact", {
method: 'POST', method: "POST",
body: { taskId } body: { taskId },
}); });
} }
@ -197,37 +254,44 @@ export async function compactDialog(taskId: string): Promise<CompactDialogRespon
*/ */
export function createSuccessResult(id: number, text: string): ToolCallResult { export function createSuccessResult(id: number, text: string): ToolCallResult {
return { return {
jsonrpc: '2.0', jsonrpc: "2.0",
id, id,
result: { result: {
content: [{ type: 'text', text }], content: [{ type: "text", text }],
isError: false isError: false,
} },
}; };
} }
/** /**
* 创建业务错误的工具结果(如编译失败) * 创建业务错误的工具结果(如编译失败)
*/ */
export function createBusinessErrorResult(id: number, errorMessage: string): ToolCallResult { export function createBusinessErrorResult(
id: number,
errorMessage: string,
): ToolCallResult {
return { return {
jsonrpc: '2.0', jsonrpc: "2.0",
id, id,
result: { result: {
content: [{ type: 'text', text: errorMessage }], content: [{ type: "text", text: errorMessage }],
isError: true isError: true,
} },
}; };
} }
/** /**
* 创建系统错误的工具结果 * 创建系统错误的工具结果
*/ */
export function createSystemErrorResult(id: number, code: number, message: string): ToolCallResult { export function createSystemErrorResult(
id: number,
code: number,
message: string,
): ToolCallResult {
return { return {
jsonrpc: '2.0', jsonrpc: "2.0",
id, id,
error: { code, message } error: { code, message },
}; };
} }
@ -236,9 +300,9 @@ export function createSystemErrorResult(id: number, code: number, message: strin
* GET /system/user/getInfo * GET /system/user/getInfo
*/ */
export async function getUserInfo(): Promise<UserInfoResponse> { export async function getUserInfo(): Promise<UserInfoResponse> {
console.log('[API] 获取用户信息'); console.log("[API] 获取用户信息");
return request<UserInfoResponse>('/system/user/getInfo', { return request<UserInfoResponse>("/system/user/getInfo", {
method: 'GET' method: "GET",
}); });
} }
@ -253,25 +317,45 @@ export interface CreditBalanceResponse {
* 查询用户资源点余额 * 查询用户资源点余额
* GET /api/dialog/balance?userId=xxx * GET /api/dialog/balance?userId=xxx
*/ */
export async function getCreditBalance(userId: string): Promise<CreditBalanceResponse> { export async function getCreditBalance(
console.log('[API] 查询余额: userId=', userId); userId: string,
return request<CreditBalanceResponse>(`/api/dialog/balance?userId=${userId}`, { ): Promise<CreditBalanceResponse> {
method: 'GET', console.log("[API] 查询余额: userId=", userId);
timeout: 5000 return request<CreditBalanceResponse>(
}); `/api/dialog/balance?userId=${userId}`,
{
method: "GET",
timeout: 5000,
},
);
} }
/** /**
* 验证邀请码 * 验证邀请码
* POST /api/invitation/verify * POST /api/invitation/verify
*/ */
export async function verifyInvitationCode(code: string): Promise<InvitationVerifyResponse> { export async function verifyInvitationCode(
console.log('[API] 验证邀请码'); code: string,
): Promise<InvitationVerifyResponse> {
// console.log('[API] 验证邀请码 - 开始');
console.log("[API] 邀请码:", code);
const body: InvitationVerifyRequest = { code }; const body: InvitationVerifyRequest = { code };
return request<InvitationVerifyResponse>('/api/invitation/verify', { console.log("[API] 请求体:", JSON.stringify(body));
method: 'POST',
body try {
}); const response = await request<InvitationVerifyResponse>(
"/api/invitation/verify",
{
method: "POST",
body,
},
);
console.log("[API] 验证邀请码 - 响应:", JSON.stringify(response));
return response;
} catch (error) {
console.error("[API] 验证邀请码 - 错误:", error);
throw error;
}
} }
/** /**
@ -279,8 +363,8 @@ export async function verifyInvitationCode(code: string): Promise<InvitationVeri
* GET /api/invitation/status * GET /api/invitation/status
*/ */
export async function checkInvitationStatus(): Promise<InvitationStatusResponse> { export async function checkInvitationStatus(): Promise<InvitationStatusResponse> {
console.log('[API] 查询邀请码验证状态'); console.log("[API] 查询邀请码验证状态");
return request<InvitationStatusResponse>('/api/invitation/status', { return request<InvitationStatusResponse>("/api/invitation/status", {
method: 'GET' method: "GET",
}); });
} }

View File

@ -1,8 +1,8 @@
/** /**
* 邀请码验证服务 * 邀请码验证服务
*/ */
import * as vscode from 'vscode'; import * as vscode from "vscode";
import { verifyInvitationCode, checkInvitationStatus } from './apiClient'; import { verifyInvitationCode, checkInvitationStatus } from "./apiClient";
/** /**
* 邀请码验证服务类 * 邀请码验证服务类
@ -13,34 +13,51 @@ export class InvitationService {
*/ */
static async isVerified(context: vscode.ExtensionContext): Promise<boolean> { static async isVerified(context: vscode.ExtensionContext): Promise<boolean> {
// 【临时】使用本地验证,不调用后端 // 【临时】使用本地验证,不调用后端
const localVerified = context.globalState.get<boolean>('invitationCodeVerified'); const localVerified = context.globalState.get<boolean>(
"invitationCodeVerified",
);
return localVerified || false; return localVerified || false;
} }
/** /**
* 验证邀请码 * 验证邀请码
*/ */
static async verifyCode(code: string): Promise<{ success: boolean; message: string }> { static async verifyCode(
code: string,
): Promise<{ success: boolean; message: string }> {
try { try {
console.log('[InvitationService] 验证邀请码:', code); // console.log('[InvitationService] ========== 开始验证邀请码 ==========');
// console.log('[InvitationService] 邀请码:', code);
// console.log('[InvitationService] 邀请码长度:', code.length);
const response = await verifyInvitationCode(code); const response = await verifyInvitationCode(code);
// console.log('[InvitationService] 收到响应:', JSON.stringify(response, null, 2));
// console.log('[InvitationService] 响应代码:', response.code);
// console.log('[InvitationService] 响应消息:', response.msg);
// console.log('[InvitationService] 验证结果:', response.data?.verified);
if (response.code === 200 && response.data?.verified) { if (response.code === 200 && response.data?.verified) {
console.log("[InvitationService] ✓ 验证成功");
return { return {
success: true, success: true,
message: response.msg || '验证成功' message: response.msg || "验证成功",
}; };
} else { } else {
console.log("[InvitationService] ✗ 验证失败");
return { return {
success: false, success: false,
message: response.msg || '验证失败' message: response.msg || "验证失败",
}; };
} }
} catch (error: any) { } catch (error: any) {
console.error('[InvitationService] 验证邀请码失败:', error); // console.error('[InvitationService] ========== 验证邀请码异常 ==========');
// console.error('[InvitationService] 错误类型:', error.constructor.name);
// console.error('[InvitationService] 错误消息:', error.message);
// console.error('[InvitationService] 错误堆栈:', error.stack);
return { return {
success: false, success: false,
message: error.message || '网络连接失败,请检查网络后重试' message: error.message || "网络连接失败,请检查网络后重试",
}; };
} }
} }
@ -51,20 +68,25 @@ export class InvitationService {
static async saveVerificationStatus( static async saveVerificationStatus(
context: vscode.ExtensionContext, context: vscode.ExtensionContext,
code: string, code: string,
verifiedTime?: string verifiedTime?: string,
): Promise<void> { ): Promise<void> {
await context.globalState.update('invitationCodeVerified', true); await context.globalState.update("invitationCodeVerified", true);
await context.globalState.update('invitationCode', code); await context.globalState.update("invitationCode", code);
await context.globalState.update('invitationVerifiedTime', verifiedTime || new Date().toISOString()); await context.globalState.update(
"invitationVerifiedTime",
verifiedTime || new Date().toISOString(),
);
} }
/** /**
* 清除验证状态(用于退出登录或更换邀请码) * 清除验证状态(用于退出登录或更换邀请码)
*/ */
static async clearVerificationStatus(context: vscode.ExtensionContext): Promise<void> { static async clearVerificationStatus(
await context.globalState.update('invitationCodeVerified', undefined); context: vscode.ExtensionContext,
await context.globalState.update('invitationCode', undefined); ): Promise<void> {
await context.globalState.update('invitationVerifiedTime', undefined); await context.globalState.update("invitationCodeVerified", undefined);
await context.globalState.update("invitationCode", undefined);
await context.globalState.update("invitationVerifiedTime", undefined);
} }
/** /**
@ -72,18 +94,18 @@ export class InvitationService {
*/ */
static async showInputDialog(): Promise<string | undefined> { static async showInputDialog(): Promise<string | undefined> {
const code = await vscode.window.showInputBox({ const code = await vscode.window.showInputBox({
prompt: '请输入邀请码以继续使用 IC Coder', prompt: "请输入邀请码以继续使用 IC Coder",
placeHolder: '例如INVITE2024ABC', placeHolder: "例如INVITE2024ABC",
ignoreFocusOut: true, ignoreFocusOut: true,
validateInput: (value) => { validateInput: (value) => {
if (!value || value.trim().length === 0) { if (!value || value.trim().length === 0) {
return '邀请码不能为空'; return "邀请码不能为空";
} }
if (value.trim().length < 6) { if (value.trim().length < 6) {
return '邀请码格式不正确'; return "邀请码格式不正确";
} }
return null; return null;
} },
}); });
return code?.trim(); return code?.trim();