# Token 过期检查实现方案

## 1. 概述

实现三个关键时机的 Token 过期检查：
- 插件激活时
- 发起 API 请求前
- 用户交互时（打开面板/侧边栏）

## 2. 数据存储

### 2.1 存储位置
使用 VS Code 的 `globalState` 存储：
```typescript
context.globalState.update('tokenExp', exp);
```

### 2.2 存储内容
- `token`: 用户 token
- `tokenExp`: 过期时间戳（秒）
- `userInfo`: 用户信息

## 3. 核心函数设计

### 3.1 过期检查函数
```typescript
/**
 * 检查 token 是否过期
 * @param exp - 过期时间戳（秒）
 * @param bufferSeconds - 提前判断过期的缓冲时间（默认 60 秒）
 * @returns true 表示已过期或即将过期
 */
function isTokenExpired(exp: number | undefined, bufferSeconds: number = 60): boolean {
  if (!exp) {
    return true; // 没有过期时间，视为已过期
  }

  const now = Math.floor(Date.now() / 1000); // 当前时间戳（秒）
  return now >= (exp - bufferSeconds); // 提前 60 秒判断过期
}
```

### 3.2 清除登录状态函数
```typescript
/**
 * 清除所有登录相关状态
 */
async function clearAuthState(context: vscode.ExtensionContext): Promise<void> {
  await context.globalState.update('token', undefined);
  await context.globalState.update('tokenExp', undefined);
  await context.globalState.update('userInfo', undefined);
}
```

### 3.3 统一过期处理函数
```typescript
/**
 * 处理 token 过期情况
 * @param context - 扩展上下文
 * @param showMessage - 是否显示提示消息
 */
async function handleTokenExpired(
  context: vscode.ExtensionContext,
  showMessage: boolean = true
): Promise<void> {
  await clearAuthState(context);

  if (showMessage) {
    const action = await vscode.window.showWarningMessage(
      '登录已过期，请重新登录',
      '立即登录'
    );

    if (action === '立即登录') {
      // 触发登录流程（打开登录面板）
      vscode.commands.executeCommand('ic-coder.openPanel');
    }
  }
}
```

## 4. 三个检查时机实现

### 4.1 插件激活时检查

**位置**: `src/extension.ts` 的 `activate` 函数

**实现**:
```typescript
export async function activate(context: vscode.ExtensionContext) {
  console.log('IC Coder 插件正在激活...');

  // 1. 检查 token 是否过期
  const tokenExp = context.globalState.get<number>('tokenExp');
  if (isTokenExpired(tokenExp)) {
    // 静默清除，不显示提示（避免启动时打扰用户）
    await handleTokenExpired(context, false);
  }

  // ... 其他激活逻辑
}
```

**说明**: 启动时静默检查，如果过期则清除状态，但不弹窗提示

---

### 4.2 发起 API 请求前检查

**位置**: `src/utils/messageHandler.ts` 的 API 请求函数

**实现**:
```typescript
// 在发送消息到后端前检查
async function sendMessageToBackend(message: string, context: vscode.ExtensionContext) {
  // 1. 检查 token 是否过期
  const tokenExp = context.globalState.get<number>('tokenExp');
  if (isTokenExpired(tokenExp)) {
    await handleTokenExpired(context, true); // 显示提示
    return; // 中断请求
  }

  const token = context.globalState.get<string>('token');
  if (!token) {
    vscode.window.showWarningMessage('请先登录');
    return;
  }

  // 2. 继续发送请求
  // ... 原有请求逻辑
}
```

**说明**: 每次 API 请求前检查，如果过期则提示用户并中断请求

---

### 4.3 用户交互时检查

**位置**:
- `src/panels/ICHelperPanel.ts` - 打开聊天面板时
- `src/views/ICViewProvider.ts` - 侧边栏视图加载时

**实现 - 聊天面板**:
```typescript
// ICHelperPanel.ts
public static render(extensionUri: vscode.Uri, context: vscode.ExtensionContext) {
  // 1. 检查 token 是否过期
  const tokenExp = context.globalState.get<number>('tokenExp');
  if (isTokenExpired(tokenExp)) {
    handleTokenExpired(context, true); // 显示提示
    // 继续渲染面板，但会显示未登录状态
  }

  // 2. 创建或显示面板
  // ... 原有逻辑
}
```

**实现 - 侧边栏视图**:
```typescript
// ICViewProvider.ts
public resolveWebviewView(webviewView: vscode.WebviewView) {
  // 1. 检查 token 是否过期
  const tokenExp = this._context.globalState.get<number>('tokenExp');
  if (isTokenExpired(tokenExp)) {
    handleTokenExpired(this._context, false); // 静默清除
    // 继续渲染，显示未登录状态
  }

  // 2. 渲染视图
  // ... 原有逻辑
}
```

**说明**: 打开面板时检查，聊天面板显示提示，侧边栏静默处理

## 5. 后端响应处理

### 5.1 保存 exp 字段

**位置**: `src/utils/messageHandler.ts` 处理登录响应的地方

**实现**:
```typescript
// 处理登录成功响应
if (response.data.token) {
  await context.globalState.update('token', response.data.token);

  // 保存过期时间
  if (response.data.exp) {
    await context.globalState.update('tokenExp', response.data.exp);
  }

  // 保存用户信息
  if (response.data.userInfo) {
    await context.globalState.update('userInfo', response.data.userInfo);
  }
}
```

### 5.2 处理 401 响应

**实现**:
```typescript
// API 请求错误处理
if (error.response?.status === 401) {
  // 后端返回 401，说明 token 无效或过期
  await handleTokenExpired(context, true);
  return;
}
```

## 6. 工具函数位置

建议创建新文件 `src/utils/authHelper.ts`:

```typescript
import * as vscode from 'vscode';

export function isTokenExpired(exp: number | undefined, bufferSeconds: number = 60): boolean {
  if (!exp) {
    return true;
  }
  const now = Math.floor(Date.now() / 1000);
  return now >= (exp - bufferSeconds);
}

export async function clearAuthState(context: vscode.ExtensionContext): Promise<void> {
  await context.globalState.update('token', undefined);
  await context.globalState.update('tokenExp', undefined);
  await context.globalState.update('userInfo', undefined);
}

export async function handleTokenExpired(
  context: vscode.ExtensionContext,
  showMessage: boolean = true
): Promise<void> {
  await clearAuthState(context);

  if (showMessage) {
    const action = await vscode.window.showWarningMessage(
      '登录已过期，请重新登录',
      '立即登录'
    );

    if (action === '立即登录') {
      vscode.commands.executeCommand('ic-coder.openPanel');
    }
  }
}
```

## 7. 测试场景

1. **启动测试**: 设置过期的 exp，重启插件，验证状态被清除
2. **请求测试**: 设置即将过期的 exp，发送消息，验证被拦截
3. **交互测试**: 设置过期的 exp，打开面板，验证提示显示
4. **401 测试**: 模拟后端返回 401，验证状态清除

## 8. 注意事项

- 使用 60 秒缓冲时间，避免请求中途过期
- 启动和侧边栏加载时静默处理，避免打扰用户
- 主动操作（发消息、打开聊天面板）时显示提示
- 所有时间戳使用秒为单位（与后端保持一致）
- 过期检查应该在所有需要 token 的操作前执行

## 9. 修改文件清单

需要修改的文件：
1. **新建**: `src/utils/authHelper.ts` - 认证辅助工具函数
2. **修改**: `src/extension.ts` - 插件激活时检查
3. **修改**: `src/utils/messageHandler.ts` - API 请求前检查 + 保存 exp + 处理 401
4. **修改**: `src/panels/ICHelperPanel.ts` - 打开聊天面板时检查
5. **修改**: `src/views/ICViewProvider.ts` - 侧边栏加载时检查

