-
Notifications
You must be signed in to change notification settings - Fork 312
[v1.3] Cron 相关修改:bug 修补、i18n、once 表达式增强、升级 cron 库 #1126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release/v1.3
Are you sure you want to change the base?
Changes from all commits
daedba2
510be97
8203c21
73d03d3
b1189fb
3311f3c
dc07958
f9ba900
77f05ee
27acf74
fc37aa6
e298685
8ab3e28
076f8fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -14,11 +14,17 @@ import { proxyUpdateRunStatus } from "../offscreen/client"; | |||||||
| import { BgExecScriptWarp } from "../content/exec_warp"; | ||||||||
| import type ExecScript from "../content/exec_script"; | ||||||||
| import type { ValueUpdateDataEncoded } from "../content/types"; | ||||||||
| import { getStorageName, getMetadataStr, getUserConfigStr } from "@App/pkg/utils/utils"; | ||||||||
| import { getStorageName, getMetadataStr, getUserConfigStr, getISOWeek } from "@App/pkg/utils/utils"; | ||||||||
| import type { EmitEventRequest, ScriptLoadInfo } from "../service_worker/types"; | ||||||||
| import { CATRetryError } from "../content/exec_warp"; | ||||||||
| import { parseUserConfig } from "@App/pkg/utils/yaml"; | ||||||||
| import { decodeRValue } from "@App/pkg/utils/message_value"; | ||||||||
| import { extractCronExpr } from "@App/pkg/utils/cron"; | ||||||||
| import { changeLanguage, initLanguage, t } from "@App/locales/locales"; | ||||||||
|
|
||||||||
| const utime_1min = 60 * 1000; | ||||||||
| const utime_1hr = 60 * 60 * 1000; | ||||||||
| const utime_1day = 24 * 60 * 60 * 1000; | ||||||||
|
|
||||||||
| export class Runtime { | ||||||||
| cronJob: Map<string, Array<CronJob>> = new Map(); | ||||||||
|
|
@@ -181,30 +187,17 @@ export class Runtime { | |||||||
| crontabScript(script: ScriptLoadInfo) { | ||||||||
| // 执行定时脚本 运行表达式 | ||||||||
| if (!script.metadata.crontab) { | ||||||||
| throw new Error(script.name + " - 错误的crontab表达式"); | ||||||||
| throw new Error(script.name + " - " + t("cron_invalid_expr")); | ||||||||
| } | ||||||||
| // 如果有nextruntime,则加入重试队列 | ||||||||
| this.joinRetryList(script); | ||||||||
| this.crontabSripts.push(script); | ||||||||
| let flag = false; | ||||||||
| const cronJobList: Array<CronJob> = []; | ||||||||
| script.metadata.crontab.forEach((val) => { | ||||||||
| let oncePos = 0; | ||||||||
| let crontab = val; | ||||||||
| if (crontab.includes("once")) { | ||||||||
| const vals = crontab.split(" "); | ||||||||
| vals.forEach((item, index) => { | ||||||||
| if (item === "once") { | ||||||||
| oncePos = index; | ||||||||
| } | ||||||||
| }); | ||||||||
| if (vals.length === 5) { | ||||||||
| oncePos += 1; | ||||||||
| } | ||||||||
| crontab = crontab.replace(/once/g, "*"); | ||||||||
| } | ||||||||
| const { cronExpr, oncePos } = extractCronExpr(val); | ||||||||
| try { | ||||||||
| const cron = new CronJob(crontab, this.crontabExec(script, oncePos)); | ||||||||
| const cron = new CronJob(cronExpr, this.crontabExec(script, oncePos)); | ||||||||
| cron.start(); | ||||||||
| cronJobList.push(cron); | ||||||||
| } catch (e) { | ||||||||
|
|
@@ -231,56 +224,41 @@ export class Runtime { | |||||||
| } | ||||||||
|
|
||||||||
| crontabExec(script: ScriptLoadInfo, oncePos: number) { | ||||||||
| if (oncePos) { | ||||||||
| if (oncePos >= 1) { | ||||||||
| return () => { | ||||||||
| // 没有最后一次执行时间表示之前都没执行过,直接执行 | ||||||||
| if (!script.lastruntime) { | ||||||||
| this.execScript(script); | ||||||||
| return; | ||||||||
| } | ||||||||
| const now = new Date(); | ||||||||
| const last = new Date(script.lastruntime); | ||||||||
| let flag = false; | ||||||||
| // 根据once所在的位置去判断执行 | ||||||||
| switch (oncePos) { | ||||||||
| case 1: // 每分钟 | ||||||||
| flag = last.getMinutes() !== now.getMinutes(); | ||||||||
| break; | ||||||||
| case 2: // 每小时 | ||||||||
| flag = last.getHours() !== now.getHours(); | ||||||||
| break; | ||||||||
| case 3: // 每天 | ||||||||
| flag = last.getDay() !== now.getDay(); | ||||||||
| break; | ||||||||
| case 4: // 每月 | ||||||||
| flag = last.getMonth() !== now.getMonth(); | ||||||||
| break; | ||||||||
| case 5: // 每周 | ||||||||
| flag = this.getWeek(last) !== this.getWeek(now); | ||||||||
| break; | ||||||||
| default: | ||||||||
| } | ||||||||
| if (flag) { | ||||||||
| this.execScript(script); | ||||||||
| if (script.lastruntime) { | ||||||||
| const now = new Date(); | ||||||||
| const last = new Date(script.lastruntime); | ||||||||
| // 根据once所在的位置去判断执行 | ||||||||
| const timeDiff = now.getTime() - last.getTime(); | ||||||||
| switch (oncePos) { | ||||||||
| case 1: // 每分钟 | ||||||||
| if (timeDiff < 2 * utime_1min && last.getMinutes() === now.getMinutes()) return; | ||||||||
| break; | ||||||||
| case 2: // 每小时 | ||||||||
| if (timeDiff < 2 * utime_1hr && last.getHours() === now.getHours()) return; | ||||||||
| break; | ||||||||
| case 3: // 每天 | ||||||||
| if (timeDiff < 2 * utime_1day && last.getDay() === now.getDay()) return; | ||||||||
| break; | ||||||||
| case 4: // 每月 | ||||||||
| if (timeDiff < 62 * utime_1day && last.getMonth() === now.getMonth()) return; | ||||||||
|
||||||||
| if (timeDiff < 62 * utime_1day && last.getMonth() === now.getMonth()) return; | |
| // 使用 93 天作为阈值(约等于 3 个月),提高对长时间关机/时间漂移场景的容错性 | |
| if (timeDiff < 93 * utime_1day && last.getMonth() === now.getMonth()) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
62 只是一个约数用来避免 上年3月跟今年3月混在一起
Copilot
AI
Feb 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
缺少对 crontabExec 方法的单元测试。该方法包含了关键的"每天/每周/每月执行一次"的逻辑判断,但没有测试覆盖。建议添加测试用例验证:
- 每天执行一次的逻辑(注意:代码中存在使用
getDay()而非getDate()的 bug) - 边界情况:如跨天、跨月、跨年的场景
timeDiff阈值的正确性lastruntime为空时的行为
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- 原本就打错成 getDay 了。不过实际也可以用来判别是否同一日,所以不改也行
加 单元测试 也行。不过原本这东西就没什么 单元测试
代码简单不用测也行吧
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
使用了
getDay()方法,但该方法返回星期几(0-6),而不是日期(1-31)。应该使用getDate()方法来获取日期,否则会导致"每天执行一次"的判断逻辑出错。例如:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
原本就打错成 getDay 了。不过实际也可以用来判别是否同一日,所以不改也行
反正现在也加了
timeDiff < 2 * utime_1day这东西不会出错