modify:api 接口优化

This commit is contained in:
ECRZ
2026-01-06 11:01:47 +08:00
parent ba478d70cc
commit dc32f18539
5 changed files with 617 additions and 0 deletions

348
loginApi.js Normal file
View File

@@ -0,0 +1,348 @@
const axios = require("axios");
const fs = require('fs');
const path = require('path');
/**
* Token 缓存信息
* @typedef {Object} TokenCache
* @property {string} token - Token 值
* @property {string} expiresAt - 过期时间 (ISO 字符串)
*/
/**
* 外部 Token 获取服务
* 支持文件持久化和自动过期更新
* @class ExternalToken
*/
class ExternalToken {
constructor() {
/**
* Token 文件存储路径
* @type {string}
* @private
*/
this._tokenFilePath = path.join(__dirname, '.token-cache.json');
/**
* Token 有效期(毫秒),默认 30 分钟
* @type {number}
* @private
*/
this._tokenValidityMs = 30 * 60 * 1000;
/**
* 是否正在刷新 Token防止并发重复请求
* @type {Promise|null}
* @private
*/
this._refreshingPromise = null;
/**
* 内存中的 Token 缓存
* @type {TokenCache|null}
* @private
*/
this._memoryCache = null;
// 初始化时从文件加载 Token
this._loadTokenFromFile();
}
/**
* 从文件加载 Token
* @private
*/
_loadTokenFromFile() {
try {
if (fs.existsSync(this._tokenFilePath)) {
const data = fs.readFileSync(this._tokenFilePath, 'utf-8');
const cache = JSON.parse(data);
// 验证数据结构
if (cache.token && cache.expiresAt) {
this._memoryCache = {
token: cache.token,
expiresAt: new Date(cache.expiresAt)
};
const isExpired = this._isTokenExpired();
console.log(`📂 从文件加载 Token ${isExpired ? '(已过期)' : '(有效)'}`);
console.log(` 过期时间: ${this._memoryCache.expiresAt.toLocaleString('zh-CN')}`);
} else {
console.warn('⚠️ Token 文件格式无效,将重新获取');
this._memoryCache = null;
}
} else {
console.log('📄 Token 文件不存在,将创建新文件');
}
} catch (error) {
console.error('❌ 读取 Token 文件失败:', error.message);
this._memoryCache = null;
}
}
/**
* 保存 Token 到文件
* @param {string} token - Token 值
* @private
*/
_saveTokenToFile(token) {
try {
const expiresAt = new Date(Date.now() + this._tokenValidityMs);
const cache = {
token,
expiresAt: expiresAt.toISOString(),
updatedAt: new Date().toISOString()
};
fs.writeFileSync(this._tokenFilePath, JSON.stringify(cache, null, 2), 'utf-8');
console.log(`💾 Token 已保存到文件: ${this._tokenFilePath}`);
console.log(` 过期时间: ${expiresAt.toLocaleString('zh-CN')}`);
} catch (error) {
console.error('❌ 保存 Token 文件失败:', error.message);
}
}
/**
* 删除 Token 文件
* @private
*/
_deleteTokenFile() {
try {
if (fs.existsSync(this._tokenFilePath)) {
fs.unlinkSync(this._tokenFilePath);
console.log('🗑️ Token 文件已删除');
}
} catch (error) {
console.error('❌ 删除 Token 文件失败:', error.message);
}
}
/**
* 检查 Token 是否过期
* @returns {boolean}
* @private
*/
_isTokenExpired() {
if (!this._memoryCache) {
return true;
}
const now = new Date();
return now >= this._memoryCache.expiresAt;
}
/**
* 清除 Token 缓存(内存和文件)
*/
clearTokenCache() {
this._memoryCache = null;
this._deleteTokenFile();
console.log('🗑️ Token 缓存已清除');
}
/**
* 设置 Token 有效期
* @param {number} minutes - 有效期(分钟)
*/
setTokenValidity(minutes) {
this._tokenValidityMs = minutes * 60 * 1000;
console.log(`⏱️ Token 有效期已设置为 ${minutes} 分钟`);
}
/**
* 获取新的登录 Token
* @returns {Promise<string>} Token 值
* @throws {Error} 当获取失败时抛出错误
* @private
*/
async _fetchNewToken() {
const config = {
method: 'POST',
url: 'https://xdwlgyl.yciccloud.com/gdpaas/login/doLogin.htm',
headers: {
'host': 'xdwlgyl.yciccloud.com',
'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
'accept': 'application/json, text/javascript, */*; q=0.01',
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
'x-requested-with': 'XMLHttpRequest',
'sec-ch-ua-mobile': '?0',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'sec-ch-ua-platform': '"Windows"',
'origin': 'https://xdwlgyl.yciccloud.com',
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'referer': 'https://xdwlgyl.yciccloud.com/gdpaas/login/index.htm',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9',
'cookie': 'SYS-A7EE-D0E8-BE614B80=1108%405b2bc1b4b8be40dfb104b1f5c84c1245;SameSite=Lax;HWWAFSESTIME=1767662772583;HWWAFSESID=7817173569731ea415;JSESSIONID=7AF7F82CBD7C1FFC6CBCAF67CFD22AC6',
'Connection': 'keep-alive'
},
data: 'userId=15758339512&pwd=4E71002969FCD46813B869E931AEDF4B&randCode=&langId='
};
try {
console.log('🔄 正在请求新 Token...');
const response = await axios.request(config);
// 安全地提取 tokenId
const tokenId = response?.data?.data?.user?.exts?.tokenId;
if (!tokenId) {
throw new Error('响应中未找到 tokenId');
}
console.log('✅ 新 Token 获取成功:', tokenId);
// 保存到内存和文件
const expiresAt = new Date(Date.now() + this._tokenValidityMs);
this._memoryCache = { token: tokenId, expiresAt };
this._saveTokenToFile(tokenId);
return tokenId;
} catch (error) {
const errorMsg = error.response?.data || error.message;
console.error('❌ Token 获取失败:', errorMsg);
throw new Error(`Token 获取失败: ${errorMsg}`);
}
}
/**
* 获取登录 Token支持文件持久化和自动过期更新
* @param {boolean} forceRefresh - 是否强制刷新 Token
* @returns {Promise<{success: boolean, data?: string, error?: string, timestamp: string, cached?: boolean, source?: string}>}
*/
async getToken(forceRefresh = false) {
try {
// 如果未强制刷新且 Token 未过期,直接返回缓存
if (!forceRefresh && !this._isTokenExpired()) {
const source = this._memoryCache ? '文件缓存' : '内存缓存';
console.log(`♻️ 使用${source}的 Token`);
return {
success: true,
data: this._memoryCache.token,
timestamp: new Date().toISOString(),
cached: true,
source
};
}
// 如果正在刷新,等待刷新完成(防止并发请求)
if (this._refreshingPromise) {
console.log('⏳ Token 刷新中,等待完成...');
await this._refreshingPromise;
return {
success: true,
data: this._memoryCache.token,
timestamp: new Date().toISOString(),
cached: false,
source: '新获取'
};
}
// 开始刷新 Token
this._refreshingPromise = this._fetchNewToken();
const token = await this._refreshingPromise;
// 清除刷新标记
this._refreshingPromise = null;
return {
success: true,
data: token,
timestamp: new Date().toISOString(),
cached: false,
source: '新获取'
};
} catch (error) {
this._refreshingPromise = null;
return {
success: false,
error: error.message,
timestamp: new Date().toISOString()
};
}
}
/**
* 获取当前缓存的 Token不检查过期
* @returns {string|null}
*/
getCachedToken() {
return this._memoryCache?.token || null;
}
/**
* 获取 Token 过期时间
* @returns {Date|null}
*/
getTokenExpiresAt() {
return this._memoryCache?.expiresAt || null;
}
/**
* 检查 Token 是否即将过期(剩余时间少于 5 分钟)
* @returns {boolean}
*/
isTokenExpiringSoon() {
if (!this._memoryCache) {
return true;
}
const now = new Date();
const timeLeft = this._memoryCache.expiresAt - now;
const fiveMinutes = 5 * 60 * 1000;
return timeLeft < fiveMinutes;
}
/**
* 获取 Token 剩余有效时间(秒)
* @returns {number|null} 剩余秒数,如果 Token 不存在或已过期返回 null
*/
getTokenRemainingTime() {
if (!this._memoryCache) {
return null;
}
const now = new Date();
const timeLeft = Math.max(0, this._memoryCache.expiresAt - now);
return Math.floor(timeLeft / 1000);
}
/**
* 获取 Token 文件路径
* @returns {string}
*/
getTokenFilePath() {
return this._tokenFilePath;
}
}
/**
* 导出单例实例
*/
module.exports = new ExternalToken();
/**
* 如果直接运行此文件,执行示例请求
*/
if (require.main === module) {
const collector = require('./loginApi.js');
collector.getToken()
.then(result => {
if (result.success) {
console.log('✅ 数据获取成功');
console.log('📅 时间:', result.timestamp);
console.log('📊 数据预览:', JSON.stringify(result.data, null, 2));
} else {
console.error('❌ 数据获取失败:', result.error);
}
})
.catch(error => {
console.error('💥 未捕获的错误:', error);
});
}