mirror of
https://github.com/babalae/bettergi-scripts-list.git
synced 2026-05-22 22:45:50 +08:00
refactor(cron): 重构Cron时间戳获取功能
- 将getNextCronTimestamp函数改为异步函数,使用HTTP请求替代本地计算 - 移除原有的本地Cron表达式解析和时间计算逻辑 - 添加对远程服务的HTTP请求实现时间戳计算 - 注释掉原有性能较差的本地计算方法 - 移除不再使用的辅助函数parseCron、parseField、parseCronField - 保留isValidCron函数用于验证Cron表达式的功能
This commit is contained in:
@@ -700,10 +700,11 @@ async function init() {
|
|||||||
return timeDifference.total.hours >= value;
|
return timeDifference.total.hours >= value;
|
||||||
case timeType.cron:
|
case timeType.cron:
|
||||||
const nextCronTimestamp = cronUtil.getNextCronTimestamp(`${value}`, timestamp, now);
|
const nextCronTimestamp = cronUtil.getNextCronTimestamp(`${value}`, timestamp, now);
|
||||||
if (!nextCronTimestamp) {
|
// if (!nextCronTimestamp) {
|
||||||
log.error(`cron表达式解析失败: {value}`, value)
|
// log.error(`cron表达式解析失败: {value}`, value)
|
||||||
throw new Error(`cron表达式解析失败: ${value}`)
|
// throw new Error(`cron表达式解析失败: ${value}`)
|
||||||
}
|
// }
|
||||||
|
if (!nextCronTimestamp) return false;
|
||||||
return now >= nextCronTimestamp;
|
return now >= nextCronTimestamp;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
@@ -713,6 +714,7 @@ async function init() {
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (timeFilter?.length > 0) {
|
if (timeFilter?.length > 0) {
|
||||||
//移除CD
|
//移除CD
|
||||||
list = Array.from(new Set(list).difference(new Set(timeFilter)))
|
list = Array.from(new Set(list).difference(new Set(timeFilter)))
|
||||||
@@ -725,6 +727,8 @@ async function init() {
|
|||||||
name: settingsAsName.settings_name
|
name: settingsAsName.settings_name
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
log.debug(`[CD]{0}[CD]`,JSON.stringify([...timeFilter]))
|
||||||
|
log.debug(`[RUN]{0}[RUN]`,JSON.stringify([...list]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 启用自动拾取的实时任务,并配置成启用急速拾取模式
|
// 启用自动拾取的实时任务,并配置成启用急速拾取模式
|
||||||
|
|||||||
@@ -12,6 +12,10 @@
|
|||||||
"links": "https://github.com/Kirito520Asuna"
|
"links": "https://github.com/Kirito520Asuna"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"http_allowed_urls": [
|
||||||
|
"https://*",
|
||||||
|
"http://*"
|
||||||
|
],
|
||||||
"settings_ui": "settings.json",
|
"settings_ui": "settings.json",
|
||||||
"main": "main.js"
|
"main": "main.js"
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,12 @@
|
|||||||
"label": "刷新黑名单 以,分割",
|
"label": "刷新黑名单 以,分割",
|
||||||
"default": "其他,锄地专区,食材与炼金"
|
"default": "其他,锄地专区,食材与炼金"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "cron_http_url",
|
||||||
|
"type": "input-text",
|
||||||
|
"label": "cron解析Http 地址",
|
||||||
|
"default": "http://<ip:port>/bgi/cron/next-timestamp"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "key",
|
"name": "key",
|
||||||
"type": "input-text",
|
"type": "input-text",
|
||||||
|
|||||||
@@ -110,93 +110,117 @@ function parseCron(cron) {
|
|||||||
isValid: true
|
isValid: true
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return { isValid: false, error: e.message, original: cron };
|
return {isValid: false, error: e.message, original: cron};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据 cron 表达式和当前时间,计算下一次执行的时间戳(毫秒)
|
* 获取下一个Cron时间戳
|
||||||
* 支持标准 5 段 cron: 分钟 时 日 月 星期
|
* @param {string} cronExpression - Cron表达式
|
||||||
* @param {string} cron - cron表达式,例如 "30 2 * * 1-5"
|
* @param {number} [startTimestamp=Date.now()] - 开始时间戳,默认为当前时间
|
||||||
* @param {number} [fromTime=Date.now()] - 从这个时间开始找下一个执行点
|
* @param {number} endTimestamp - 结束时间戳
|
||||||
* @returns {number|null} 下一次执行的时间戳(毫秒),找不到返回 null
|
* @returns {Promise} 返回一个Promise,解析为下一个Cron时间戳
|
||||||
*/
|
*/
|
||||||
function getNextCronTimestamp(cron, fromTime = Date.now(),endTime) {
|
async function getNextCronTimestamp(cronExpression, startTimestamp = Date.now(), endTimestamp, url = settings.cron_http_url) {
|
||||||
const parts = cron.trim().split(/\s+/);
|
const result = await http.request("POST", url, JSON.stringify({
|
||||||
if (parts.length < 5 || parts.length > 6) {
|
cronExpression: `${cronExpression}`,
|
||||||
throw new Error("不支持的 cron 格式,应为 5~6 段");
|
startTimestamp: startTimestamp,
|
||||||
}
|
endTimestamp: endTimestamp
|
||||||
|
}), JSON.stringify({
|
||||||
const [minStr, hourStr, dayStr, monthStr, dowStr] = parts;
|
"Content-Type": "application/json"
|
||||||
|
})).then(res => {
|
||||||
// 解析每个字段
|
log.debug(`[{0}]res=>{1}`, 'next', JSON.stringify(res))
|
||||||
const minutes = parseField(minStr, 0, 59);
|
if (res.status_code === 200 && res.body) {
|
||||||
const hours = parseField(hourStr, 0, 23);
|
let result_json = JSON.parse(res.body);
|
||||||
const days = parseField(dayStr, 1, 31);
|
if (result_json?.code === 200) {
|
||||||
const months = parseField(monthStr, 1, 12);
|
return result_json?.data
|
||||||
const dows = parseField(dowStr, 0, 7);
|
}
|
||||||
|
throw new Error("请求失败,error:" + result_json?.message)
|
||||||
// 星期 7 → 0 (周日)
|
|
||||||
if (dows.has(7)) dows.add(0);
|
|
||||||
|
|
||||||
let current = new Date(fromTime);
|
|
||||||
// 如果没有指定 endTime,默认设置为明天 00:00:00
|
|
||||||
if (endTime === undefined) {
|
|
||||||
const tomorrow = new Date(current);
|
|
||||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
||||||
tomorrow.setHours(0, 0, 0, 0);
|
|
||||||
endTime = tomorrow.getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 动态计算最大迭代次数
|
|
||||||
// 将时间差(毫秒)转换为分钟,向上取整,确保覆盖所有可能的分钟点
|
|
||||||
const timeDiffMinutes = Math.ceil((endTime - fromTime) / 60000);
|
|
||||||
|
|
||||||
// 设置最大迭代次数,防止意外情况(如 endTime 极大)导致内存溢出或死循环
|
|
||||||
// 即使 endTime 是 10 年后,也限制在约 2 年内(避免极端情况)
|
|
||||||
// 2年 ≈ 365 * 2 * 24 * 60 = 1,051,200
|
|
||||||
const MAX_ITERATIONS_LIMIT = 1051200;
|
|
||||||
const MAX_ITERATIONS = Math.min(timeDiffMinutes, MAX_ITERATIONS_LIMIT);
|
|
||||||
|
|
||||||
let iteration = 0;
|
|
||||||
while (iteration++ < MAX_ITERATIONS) {
|
|
||||||
// 先推进到下一分钟,避免死循环在同一分钟
|
|
||||||
current.setMinutes(current.getMinutes() + 1);
|
|
||||||
current.setSeconds(0);
|
|
||||||
current.setMilliseconds(0);
|
|
||||||
|
|
||||||
const m = current.getMinutes();
|
|
||||||
const h = current.getHours();
|
|
||||||
const d = current.getDate();
|
|
||||||
const mon = current.getMonth() + 1; // JS 月份 0~11
|
|
||||||
const dow = current.getDay(); // 0=周日, 1=周一, ..., 6=周六
|
|
||||||
|
|
||||||
// 核心匹配条件(日期和星期是 OR 关系)
|
|
||||||
const minuteMatch = minutes.has(m) || minutes.size === 0;
|
|
||||||
const hourMatch = hours.has(h) || hours.size === 0;
|
|
||||||
const monthMatch = months.has(mon) || months.size === 0;
|
|
||||||
const dayMatch = days.has(d) || days.size === 0;
|
|
||||||
const dowMatch = dows.has(dow) || dows.size === 0;
|
|
||||||
|
|
||||||
const dateOrDowMatch = (days.size === 0 && dows.size === 0) || // 两者都是 *
|
|
||||||
(days.size > 0 && dows.size === 0) || // 只指定了日期
|
|
||||||
(days.size === 0 && dows.size > 0) || // 只指定了星期
|
|
||||||
(dayMatch && dowMatch); // 两者都满足才算(最严格)
|
|
||||||
|
|
||||||
if (minuteMatch && hourMatch && monthMatch && dateOrDowMatch) {
|
|
||||||
return current.getTime();
|
|
||||||
}
|
}
|
||||||
}
|
return undefined
|
||||||
|
})
|
||||||
|
|
||||||
// 如果是因为超过 MAX_ITERATIONS_LIMIT 而退出,说明时间跨度太大
|
return result === null || !result ? undefined : result
|
||||||
if (timeDiffMinutes > MAX_ITERATIONS_LIMIT) {
|
|
||||||
log.warn("查找范围过大,已达到最大迭代次数限制");
|
|
||||||
} else {
|
|
||||||
log.warn("未找到合理下一次执行时间");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//影响到性能 改http 第三方
|
||||||
|
// function getNextCronTimestamp(cron, fromTime = Date.now(),endTime) {
|
||||||
|
// const parts = cron.trim().split(/\s+/);
|
||||||
|
// if (parts.length < 5 || parts.length > 6) {
|
||||||
|
// throw new Error("不支持的 cron 格式,应为 5~6 段");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const [minStr, hourStr, dayStr, monthStr, dowStr] = parts;
|
||||||
|
//
|
||||||
|
// // 解析每个字段
|
||||||
|
// const minutes = parseField(minStr, 0, 59);
|
||||||
|
// const hours = parseField(hourStr, 0, 23);
|
||||||
|
// const days = parseField(dayStr, 1, 31);
|
||||||
|
// const months = parseField(monthStr, 1, 12);
|
||||||
|
// const dows = parseField(dowStr, 0, 7);
|
||||||
|
//
|
||||||
|
// // 星期 7 → 0 (周日)
|
||||||
|
// if (dows.has(7)) dows.add(0);
|
||||||
|
//
|
||||||
|
// let current = new Date(fromTime);
|
||||||
|
// // 如果没有指定 endTime,默认设置为明天 00:00:00
|
||||||
|
// if (endTime === undefined) {
|
||||||
|
// const tomorrow = new Date(current);
|
||||||
|
// tomorrow.setDate(tomorrow.getDate() + 1);
|
||||||
|
// tomorrow.setHours(0, 0, 0, 0);
|
||||||
|
// endTime = tomorrow.getTime();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 动态计算最大迭代次数
|
||||||
|
// // 将时间差(毫秒)转换为分钟,向上取整,确保覆盖所有可能的分钟点
|
||||||
|
// const timeDiffMinutes = Math.ceil((endTime - fromTime) / 60000);
|
||||||
|
//
|
||||||
|
// // 设置最大迭代次数,防止意外情况(如 endTime 极大)导致内存溢出或死循环
|
||||||
|
// // 即使 endTime 是 10 年后,也限制在约 2 年内(避免极端情况)
|
||||||
|
// // 2年 ≈ 365 * 2 * 24 * 60 = 1,051,200
|
||||||
|
// const MAX_ITERATIONS_LIMIT = 1051200;
|
||||||
|
// const MAX_ITERATIONS = Math.min(timeDiffMinutes, MAX_ITERATIONS_LIMIT);
|
||||||
|
//
|
||||||
|
// let iteration = 0;
|
||||||
|
// while (iteration++ < MAX_ITERATIONS) {
|
||||||
|
// // 先推进到下一分钟,避免死循环在同一分钟
|
||||||
|
// current.setMinutes(current.getMinutes() + 1);
|
||||||
|
// current.setSeconds(0);
|
||||||
|
// current.setMilliseconds(0);
|
||||||
|
//
|
||||||
|
// const m = current.getMinutes();
|
||||||
|
// const h = current.getHours();
|
||||||
|
// const d = current.getDate();
|
||||||
|
// const mon = current.getMonth() + 1; // JS 月份 0~11
|
||||||
|
// const dow = current.getDay(); // 0=周日, 1=周一, ..., 6=周六
|
||||||
|
//
|
||||||
|
// // 核心匹配条件(日期和星期是 OR 关系)
|
||||||
|
// const minuteMatch = minutes.has(m) || minutes.size === 0;
|
||||||
|
// const hourMatch = hours.has(h) || hours.size === 0;
|
||||||
|
// const monthMatch = months.has(mon) || months.size === 0;
|
||||||
|
// const dayMatch = days.has(d) || days.size === 0;
|
||||||
|
// const dowMatch = dows.has(dow) || dows.size === 0;
|
||||||
|
//
|
||||||
|
// const dateOrDowMatch = (days.size === 0 && dows.size === 0) || // 两者都是 *
|
||||||
|
// (days.size > 0 && dows.size === 0) || // 只指定了日期
|
||||||
|
// (days.size === 0 && dows.size > 0) || // 只指定了星期
|
||||||
|
// (dayMatch && dowMatch); // 两者都满足才算(最严格)
|
||||||
|
//
|
||||||
|
// if (minuteMatch && hourMatch && monthMatch && dateOrDowMatch) {
|
||||||
|
// return current.getTime();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // 如果是因为超过 MAX_ITERATIONS_LIMIT 而退出,说明时间跨度太大
|
||||||
|
// if (timeDiffMinutes > MAX_ITERATIONS_LIMIT) {
|
||||||
|
// log.warn("查找范围过大,已达到最大迭代次数限制");
|
||||||
|
// } else {
|
||||||
|
// log.warn("未找到合理下一次执行时间");
|
||||||
|
// }
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析单个 cron 字段,返回匹配的数值 Set
|
* 解析单个 cron 字段,返回匹配的数值 Set
|
||||||
* 支持: * , - / 数值列表 * /n
|
* 支持: * , - / 数值列表 * /n
|
||||||
@@ -264,13 +288,7 @@ function parseField(field, min, max) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function a(){
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cronUtil = {
|
this.cronUtil = {
|
||||||
getNextCronTimestamp,
|
getNextCronTimestamp,
|
||||||
parseCron,
|
|
||||||
parseField,
|
|
||||||
parseCronField,
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user