Files
td_official/src/utils/request-go.ts
2025-09-10 01:11:46 +08:00

273 lines
9.3 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 $cache from '@/plugins/cache';
import useUserStore from '@/store/modules/user';
import request from '@/utils/request';
import axios from 'axios';
import sign from '@/utils/sign.js';
import CryptoJS from 'crypto-js';
/**
* 包装 request 请求,统一使用 Go 服务地址作为 baseURL
* @param config 原始请求配置
*/
const BASE_GO_URL = import.meta.env.VITE_APP_BASE_API_GO;
const token = $cache.local.get('goToken');
interface RequestGo extends Function {
(config: any): Promise<any>;
download?: (url: string, params: any, filename: string) => void;
}
// 加密密钥
const corySecretKey = 'happyCoryOrTieHanHan202410151415';
// 配置新建一个 axios 实例
const service = axios.create({
baseURL: BASE_GO_URL as any,
timeout: 120000,
headers: { 'Content-Type': 'application/json' }
});
// 不需要修改项目id的接口数组
const whiteUrl = [
'/api/wxApplet/wxApplet/sysProjectTeam/list',
'/api/wxApplet/wxApplet/sysProjectTeam/add',
'/api/wxApplet/wxApplet/sysProjectTeam/edit',
'/api/v1/test/testFollowInfo/add',
'/api/wxApplet/wxApplet/busConstructionUser/changePay',
// 获取项目资料文件夹
'/api/v1/system/documentData/treeStructureData',
'/api/v1/system/busConstructionUser/exportSalary',
'/api/v1/system/busSalaryDetails/list',
'/api/v1/system/ys7Devices/add',
'/api/v1/system/ys7Devices/edit',
'/api/v1/system/subProject/add',
'/api/v1/system/subProject/edit',
'/api/v1/system/subProject/list',
'/api/v1/system/workStatus/getTree',
'/api/v1/system/sysProjectIntroduce/list',
'/api/v1/system/sysProjectIntroduce/add',
'/api/v1/system/sysProjectIntroduce/edit',
'/video/hat/manage/api/v1/video/device/list',
'/video/hat/manage/api/v1/video/project/bind',
'/api/v1/system/notifications/publish',
'/api/v1/system/notifications/list',
'/api/v1/system/notifications/edit',
'/webodm/api/v1/taskCreate',
'/webodm/api/v1/taskProcess',
'/webodm/api/v1/download',
'/api/v1/system/manageTaskRecord/upDataResource',
'/api/v1/system/ys7Devices/list',
'/api/v1/system/busFolderFile/add',
'/api/v1/system/manageTaskRecordResource/voluntarilyReq',
'/api/v1/system/busAttendanceMachine/edit',
'/api/v1/system/qianqiFangzhen/add',
'/api/v1/system/qianqiNibianqi/add'
];
const exceptionStr = '/api/v1/test/'; // /api/v1/test/*接口拦截
// 添加请求拦截器
service.interceptors.request.use(
(config: any) => {
// 在发送请求之前做些什么 token
if (token) {
config.headers = config.headers || {};
(config.headers as any)['Authorization'] = `Bearer ${token}`;
console.log('🚀 ~ config.headers:', config.headers);
}
const stores = useUserStore();
if (!whiteUrl.includes(config.url) && !config.url.includes(exceptionStr)) {
if (config.params && Reflect.has(config.params, 'projectId')) {
config.params.projectId = stores.selectedProject.goId;
}
// 处理FormData中的projectId
if (config.data instanceof FormData && config.data.has('projectId')) {
config.data.set('projectId', stores.selectedProject.goId);
}
}
let dataInfoReq = {};
if (config.method === 'get' || config.method === 'delete') {
config.params = config.params || {};
dataInfoReq = JSON.parse(JSON.stringify(config.params));
const encryptedParam = encryptAES256(JSON.stringify(dataInfoReq), corySecretKey);
if ($cache.local.get('i18n') != null) {
config.params = { coryKey: encryptedParam, corySimplifiedToTraditional: $cache.local.get('i18n') };
} else {
config.params = { coryKey: encryptedParam };
}
} else {
// 处理FormData类型
if (config.data instanceof FormData) {
// 将FormData转换为普通对象
dataInfoReq = Object.fromEntries(config.data.entries());
// 重新创建一个新的FormData实例
const newFormData = new FormData();
const encryptedData = encryptAES256(JSON.stringify(dataInfoReq), corySecretKey);
newFormData.append('coryKey', encryptedData);
if ($cache.local.get('i18n') != null) {
newFormData.append('corySimplifiedToTraditional', $cache.local.get('i18n'));
}
// 如果有文件,需要重新添加
for (let [key, value] of config.data.entries()) {
if (value instanceof File) {
newFormData.append(key, value);
}
}
config.data = newFormData;
// 设置Content-Type为undefined让浏览器自动设置boundary
config.headers['Content-Type'] = undefined;
} else {
dataInfoReq = config.data;
const encryptedData = encryptAES256(JSON.stringify(dataInfoReq), corySecretKey);
if ($cache.local.get('i18n') != null) {
config.data = { coryKey: encryptedData, corySimplifiedToTraditional: $cache.local.get('i18n') };
} else {
config.data = { coryKey: encryptedData };
}
}
}
const { timestamp, nonce, sign: signature } = sign(dataInfoReq);
config.headers.timestamp = timestamp;
config.headers.nonce = nonce;
config.headers.sign = signature;
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
// 添加响应拦截器
service.interceptors.response.use(
(response) => {
// 对响应数据进行解密操作
try {
// 处理特殊情况data为空值或不需要解密的情况
if (response.data.data === null || response.data.data === '' || response.data.data === undefined) {
return response.data;
}
// 检查响应数据格式
if (typeof response.data.data !== 'string') {
// 尝试将非字符串数据转换为字符串
try {
response.data.data = JSON.stringify(response.data.data);
} catch (convertError) {
throw new Error('响应数据格式不正确,无法转换为字符串');
}
}
// 执行解密
const decryptedData = decryptAES256(response.data.data, corySecretKey);
// 正确处理解密后的数据
if (decryptedData && typeof decryptedData === 'object') {
// 保持response.data的结构不变只替换data字段的内容
response.data = {
...response.data,
data: decryptedData
};
} else {
// 如果解密后的数据不是对象,可能需要特殊处理
response.data.data = decryptedData;
}
if (response.data.code !== 0) {
ElMessage.error(response.data.message);
}
return response.data;
} catch (decryptError) {
// 提供更友好的错误信息
if (decryptError.message.includes('无效的Base64格式')) {
ElMessage.error('数据格式错误:接收到无效的加密数据');
} else {
ElMessage.error('数据解密失败,请联系管理员');
}
return Promise.reject(decryptError);
}
},
(error) => {
// 对响应错误做点什么
if (error.message.indexOf('timeout') !== -1) {
ElMessage.error('网络超时');
} else if (error.message === 'Network Error') {
ElMessage.error('网络连接错误');
} else {
console.log(error, '网络错误');
if (error.response.data) ElMessage.error(error.response.statusText);
else {
ElMessage.error('接口路径找不到');
console.log(error, '接口路径找不到');
}
}
return Promise.reject(error);
}
);
// 加密函数
function encryptAES256(plainText: string, key: string): string | null {
try {
const parsedKey = CryptoJS.enc.Utf8.parse(key);
const iv = CryptoJS.lib.WordArray.random(16);
const plainTextWordArray = CryptoJS.enc.Utf8.parse(plainText);
const encrypted = CryptoJS.AES.encrypt(plainTextWordArray, parsedKey, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
const encryptedData = iv.concat(encrypted.ciphertext);
return encryptedData.toString(CryptoJS.enc.Base64);
} catch (err) {
console.error('Encryption error:', err.message);
return null;
}
}
// 增强的解密函数,处理各种输入情况
function decryptAES256(encryptedData: string, key: string): any {
// eslint-disable-next-line no-useless-catch
try {
// 检查输入是否为有效的Base64字符串
if (!/^[A-Za-z0-9+/=]+$/.test(encryptedData)) {
// 尝试对非标准Base64字符串进行处理
// 移除可能的前缀
const cleanData = encryptedData.replace(/^data:.*?;base64,/, '').replace(/\s/g, ''); // 移除空格
if (!/^[A-Za-z0-9+/=]+$/.test(cleanData)) {
throw new Error('无效的Base64格式');
}
return decryptAES256(cleanData, key);
}
const decodedData = CryptoJS.enc.Base64.parse(encryptedData);
const iv = CryptoJS.lib.WordArray.create(decodedData.words.slice(0, 4));
const encryptedText = CryptoJS.lib.WordArray.create(decodedData.words.slice(4));
const parsedKey = CryptoJS.enc.Utf8.parse(key);
const decrypted = CryptoJS.AES.decrypt({ ciphertext: encryptedText }, parsedKey, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
const decryptedStr = decrypted.toString(CryptoJS.enc.Utf8);
// 尝试解析为JSON
try {
return JSON.parse(decryptedStr);
} catch (jsonError) {
return decryptedStr;
}
} catch (err) {
throw err;
}
}
// 导出 axios 实例
export default service;