Skip to content

refactor: 重构定时器#123

Merged
MistEO merged 2 commits intomainfrom
refactor/timer
Mar 20, 2026
Merged

refactor: 重构定时器#123
MistEO merged 2 commits intomainfrom
refactor/timer

Conversation

@MistEO
Copy link
Copy Markdown
Owner

@MistEO MistEO commented Mar 20, 2026

fix MaaEnd/MaaEnd#1459
fix MaaEnd/MaaEnd#1448

由 Sourcery 提供的摘要

将定时任务处理重构为一个集中式服务,并在整个应用中统一更新下载时的代理行为。

错误修复:

  • 确保在睡眠/唤醒后能够补偿定时任务,且在应用处于非活动状态时不会错过任务。
  • 在手动和自动更新下载中一致地遵守代理配置,同时避免对 MirrorChyan 源使用代理。

增强改进:

  • 引入一个可复用的调度服务,用于管理每小时轮询、去重以及调度触发状态的持久化。
  • 改进定时任务执行的日志记录和本地化,包括为补偿运行提供区别明确的日志消息。
Original summary in English

Summary by Sourcery

Refactor scheduled task handling into a centralized service and align update download proxy behavior across the app.

Bug Fixes:

  • Ensure scheduled tasks are compensated after sleep/wake and not missed when the app is inactive.
  • Honor proxy configuration consistently for manual and automatic update downloads, while avoiding proxy use for MirrorChyan sources.

Enhancements:

  • Introduce a reusable schedule service that manages hourly polling, de-duplication, and persistence of schedule trigger state.
  • Improve logging and localization for scheduled task execution, including distinct messages for compensation runs.

Summary by Sourcery

将定时任务处理重构为集中式调度服务,并统一应用内更新下载的代理行为。

Bug Fixes:

  • 确保在睡眠/唤醒之后或应用重新获得焦点时,计划任务仍能执行,而不会在应用非活动期间漏掉应执行的任务。
  • 将代理配置一致地应用到自动和手动的更新下载中,同时避免对 MirrorChyan 源使用代理。

Enhancements:

  • 引入可复用的调度服务:按小时轮询、去重执行、补偿错过的时间片,并在会话之间持久化触发状态。
  • 通过在所有支持的语言中区分常规执行和补偿执行,改进定时任务的日志记录和本地化。
Original summary in English

Summary by Sourcery

Refactor scheduled task handling into a centralized scheduling service and align update download proxy behavior across the app.

Bug Fixes:

  • Ensure scheduled tasks are still executed after sleep/wake or when the app regains focus, without missing runs while inactive.
  • Apply proxy configuration consistently to both automatic and manual update downloads while avoiding proxy usage for MirrorChyan sources.

Enhancements:

  • Introduce a reusable schedule service that polls hourly, de-duplicates executions, compensates missed slots, and persists trigger state across sessions.
  • Improve scheduled task logging and localization by distinguishing between regular and compensation executions in all supported languages.

Copilot AI review requested due to automatic review settings March 20, 2026 18:33
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我已经审查了你的更改,看起来非常棒!


Sourcery 对开源项目是免费的——如果你喜欢我们的评审,请考虑分享给更多人 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据这些反馈来改进对你代码的评审。
Original comment in English

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

该 PR 主要围绕定时器/定时任务触发逻辑进行重构(对应 issue #1459),将原先在 Toolbar 内部的整点定时触发改为独立的 ScheduleService 轮询 + 补偿检查机制,并顺带抽取更新下载代理选择逻辑以消除重复。

Changes:

  • 新增 ScheduleService:按分钟轮询整点时间槽,并在窗口 focus/可见性变化时触发补偿检查。
  • 更新 Toolbar:移除旧的整点 setTimeout + setInterval 实现,改为挂载调度服务回调。
  • 抽取更新下载代理选择逻辑为 proxySettingsForUpdateDownload,并同步更新 App/设置页/更新面板调用;新增“补偿执行”日志文案的多语言翻译键。

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/services/scheduleService.ts 新增调度服务:轮询、补偿检查、触发去重与 localStorage 记录
src/components/Toolbar.tsx 接入 scheduleService,用回调触发定时任务启动与日志写入
src/services/proxyService.ts 新增 proxySettingsForUpdateDownload 统一更新下载代理选择规则
src/App.tsx 自动下载更新时改用 proxySettingsForUpdateDownload
src/components/UpdatePanel.tsx 手动下载更新时改用 proxySettingsForUpdateDownload
src/components/settings/UpdateSection.tsx 设置页下载更新时改用 proxySettingsForUpdateDownload
src/i18n/locales/zh-CN.ts 新增 logs.messages.scheduleCompensating 文案
src/i18n/locales/zh-TW.ts 新增 logs.messages.scheduleCompensating 文案
src/i18n/locales/en-US.ts 新增 logs.messages.scheduleCompensating 文案
src/i18n/locales/ja-JP.ts 新增 logs.messages.scheduleCompensating 文案
src/i18n/locales/ko-KR.ts 新增 logs.messages.scheduleCompensating 文案

@MistEO MistEO requested a review from Copilot March 20, 2026 18:51
@MistEO
Copy link
Copy Markdown
Owner Author

MistEO commented Mar 20, 2026

@sourcery-ai review

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我在这里给出一些整体性的反馈:

  • ScheduleService.check 中,useAppStore.getState() 在嵌套循环里被反复调用(每个 slot 一次,并且在重新获取 freshInst 时对每个 instance 再调用一次);建议在每次检查时只获取一次 state,或者至少每个 slot 获取一次,以避免不必要的重复 store 读取,并减少单次运行中可能出现的不一致问题。
  • normalizeTriggeredSlotKey 的逻辑假设 instanceId 中不会包含 :,并在遇到第一个冒号时截断;如果 instance ID 有可能包含 :,这可能会把不同的 ID 折叠为同一个 key,从而导致触发遗漏或重复。更安全的做法可能是存储一个结构化对象,或者使用一种不可能出现在 ID 中的分隔符。
给 AI Agent 的提示
Please address the comments from this code review:

## Overall Comments
- In `ScheduleService.check`, `useAppStore.getState()` is called repeatedly inside the nested loops (once per slot and again per instance when re-fetching `freshInst`); consider fetching state once per check or at least once per slot to avoid unnecessary repeated store reads and potential inconsistency within a single run.
- The `normalizeTriggeredSlotKey` logic assumes `instanceId` has no `:` and truncates at the first colon; if instance IDs could ever contain `:`, this may collapse distinct IDs into the same key and cause missed or duplicated triggers, so it may be safer to store a structured object or use a delimiter that cannot appear in IDs.

Sourcery 对开源项目免费——如果你觉得我们的 Review 有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进之后的 Review。
Original comment in English

Hey - I've left some high level feedback:

  • In ScheduleService.check, useAppStore.getState() is called repeatedly inside the nested loops (once per slot and again per instance when re-fetching freshInst); consider fetching state once per check or at least once per slot to avoid unnecessary repeated store reads and potential inconsistency within a single run.
  • The normalizeTriggeredSlotKey logic assumes instanceId has no : and truncates at the first colon; if instance IDs could ever contain :, this may collapse distinct IDs into the same key and cause missed or duplicated triggers, so it may be safer to store a structured object or use a delimiter that cannot appear in IDs.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `ScheduleService.check`, `useAppStore.getState()` is called repeatedly inside the nested loops (once per slot and again per instance when re-fetching `freshInst`); consider fetching state once per check or at least once per slot to avoid unnecessary repeated store reads and potential inconsistency within a single run.
- The `normalizeTriggeredSlotKey` logic assumes `instanceId` has no `:` and truncates at the first colon; if instance IDs could ever contain `:`, this may collapse distinct IDs into the same key and cause missed or duplicated triggers, so it may be safer to store a structured object or use a delimiter that cannot appear in IDs.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.


private getLastCheckAt(): number {
const val = localStorage.getItem(STORAGE_KEY_LAST_CHECK);
return val ? parseInt(val, 10) : 0;
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getLastCheckAt() 直接 parseInt 可能返回 NaN(例如 localStorage 值被手动修改或历史版本写入异常),后续比较/构造 Date 会导致本轮调度扫描被跳过。建议在这里对 NaN 做兜底(例如无效时当作 0 并重置该 key)。

Suggested change
return val ? parseInt(val, 10) : 0;
if (!val) {
return 0;
}
const parsed = parseInt(val, 10);
if (Number.isNaN(parsed)) {
// 数据被污染或历史版本写入异常,重置为 0 以避免本轮调度被跳过
this.setLastCheckAt(0);
return 0;
}
return parsed;

Copilot uses AI. Check for mistakes.
Comment on lines +136 to +141
}

return normalized;
} catch {
return new Set();
}
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getTriggeredSlots() 在 JSON 解析失败时直接吞掉异常并返回空集合,会导致调度去重状态悄悄失效且之后每次轮询都重复触发同样的异常。建议在 catch 中记录一次 warn 并清理/重置 mxu_schedule_triggeredSlots,以便自愈并避免持续的异常开销。

Copilot uses AI. Check for mistakes.
@MistEO MistEO merged commit d339cd7 into main Mar 20, 2026
21 checks passed
@MistEO MistEO deleted the refactor/timer branch March 20, 2026 18:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

定时任务未启动 版本更新似乎不走代理?

2 participants