Files
cunkebao_v3/Cunkebao/src/api/utils.ts
笔记本里的永平 92a3d407a7 feat: 本次提交更新内容如下
定版本转移2025年7月17日
2025-07-17 10:22:38 +08:00

195 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { authApi } from './auth';
import { get, post, put, del } from './request';
import type { ApiResponse, PaginatedResponse } from '@/types/common';
// 设置token到localStorage
export const setToken = (token: string) => {
if (typeof window !== 'undefined') {
localStorage.setItem('token', token);
}
};
// 获取token
export const getToken = (): string | null => {
if (typeof window !== 'undefined') {
return localStorage.getItem('token');
}
return null;
};
// 清除token
export const clearToken = () => {
if (typeof window !== 'undefined') {
localStorage.removeItem('token');
localStorage.removeItem('userInfo');
localStorage.removeItem('token_expired');
localStorage.removeItem('s2_accountId');
}
};
// 验证token是否有效
export const validateToken = async (): Promise<boolean> => {
try {
const response = await authApi.getUserInfo();
return response.code === 200;
} catch (error) {
console.error('Token验证失败:', error);
return false;
}
};
// 刷新令牌
export const refreshAuthToken = async (): Promise<boolean> => {
if (typeof window === 'undefined') {
return false;
}
try {
const response = await authApi.refreshToken();
if (response.code === 200 && response.data?.token) {
setToken(response.data.token);
// 更新过期时间
if (response.data.token_expired) {
localStorage.setItem('token_expired', response.data.token_expired);
}
return true;
}
return false;
} catch (error) {
console.error('刷新Token失败:', error);
return false;
}
};
// 检查token是否即将过期
export const isTokenExpiringSoon = (): boolean => {
if (typeof window === 'undefined') {
return false;
}
const tokenExpired = localStorage.getItem('token_expired');
if (!tokenExpired) return true;
try {
const expiredTime = new Date(tokenExpired).getTime();
const currentTime = new Date().getTime();
// 提前10分钟认为即将过期
return currentTime >= (expiredTime - 10 * 60 * 1000);
} catch (error) {
console.error('解析token过期时间失败:', error);
return true;
}
};
// 检查token是否已过期
export const isTokenExpired = (): boolean => {
if (typeof window === 'undefined') {
return false;
}
const tokenExpired = localStorage.getItem('token_expired');
if (!tokenExpired) return true;
try {
const expiredTime = new Date(tokenExpired).getTime();
const currentTime = new Date().getTime();
// 提前5分钟认为过期给刷新留出时间
return currentTime >= (expiredTime - 5 * 60 * 1000);
} catch (error) {
console.error('解析token过期时间失败:', error);
return true;
}
};
// 请求去重器
class RequestDeduplicator {
private pendingRequests = new Map<string, Promise<any>>();
async deduplicate<T>(key: string, requestFn: () => Promise<T>): Promise<T> {
if (this.pendingRequests.has(key)) {
return this.pendingRequests.get(key)!;
}
const promise = requestFn();
this.pendingRequests.set(key, promise);
try {
const result = await promise;
return result;
} finally {
this.pendingRequests.delete(key);
}
}
getPendingCount(): number {
return this.pendingRequests.size;
}
clear(): void {
this.pendingRequests.clear();
}
}
// 请求取消管理器
class RequestCancelManager {
private abortControllers = new Map<string, AbortController>();
createController(key: string): AbortController {
// 取消之前的请求
this.cancelRequest(key);
const controller = new AbortController();
this.abortControllers.set(key, controller);
return controller;
}
cancelRequest(key: string): void {
const controller = this.abortControllers.get(key);
if (controller) {
controller.abort();
this.abortControllers.delete(key);
}
}
cancelAllRequests(): void {
this.abortControllers.forEach(controller => controller.abort());
this.abortControllers.clear();
}
getController(key: string): AbortController | undefined {
return this.abortControllers.get(key);
}
}
// 导出单例实例
export const requestDeduplicator = new RequestDeduplicator();
export const requestCancelManager = new RequestCancelManager();
/**
* 通用文件上传方法(支持图片、文件)
* @param {File} file - 要上传的文件对象
* @param {string} [uploadUrl='/v1/attachment/upload'] - 上传接口地址
* @returns {Promise<string>} - 上传成功后返回文件url
*/
export async function uploadFile(file: File, uploadUrl: string = '/v1/attachment/upload'): Promise<string> {
try {
// 创建 FormData 对象用于文件上传
const formData = new FormData();
formData.append('file', file);
// 使用 post 方法上传文件,设置正确的 Content-Type
const res = await post(uploadUrl, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
// 检查响应结果
if (res?.code === 200 && res?.data?.url) {
return res.data.url;
} else {
throw new Error(res?.msg || '文件上传失败');
}
} catch (e: any) {
throw new Error(e?.message || '文件上传失败');
}
}