Skip to content

feat(tasks): 支持任务级周计划与跨天刷新 - #382

Open
1204244136 wants to merge 4 commits into
MaaXYZ:mainfrom
1204244136:codex/task-weekly-schedule
Open

1204244136 wants to merge 4 commits into
MaaXYZ:mainfrom
1204244136:codex/task-weekly-schedule

Conversation

@1204244136

@1204244136 1204244136 commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

为单个任务增加「周计划」,允许在同一配置页中安排每日任务和指定星期执行的任务。例如,将周常任务设置为仅周日执行后,周三启动配置时会跳过该任务,其余已勾选的日常任务继续执行。

现有定时策略负责「这份配置什么时候启动」,周计划负责「本次启动包含哪些任务」。用户无需为不同执行日期重复配置设备连接和任务选项。

启动配置
  读取本次启动时刻的本地星期
  按勾选状态、周计划及显式运行方式筛选任务
    被跳过的任务 → Info 日志
    本次执行的任务 → 后续启动流程

交互与执行规则

  • 在任务复选框的右键菜单中启用或取消周计划。启用时默认选中全部星期并展开任务卡片,再由用户取消不需要执行的日期。
  • 任务卡片显示星期选择按钮、今天的标记和「今天不执行」提示;复选框保留原有三态,不因周计划改变勾选状态。
  • 主视图和仪表盘的常规启动均接入周计划过滤,被跳过的任务不会提交到 MaaFramework,也不会作为任务执行失败处理。
  • 主视图中,今天没有可执行任务时禁用开始按钮,并通过提示说明原因。定时或快捷键等入口触发统一启动流程时,若所有已勾选任务都被周计划跳过,会记录「今日无任务需执行」并在连接设备前结束。
  • 部分任务被跳过时逐条记录 Info 日志,其余任务按原有流程执行;未勾选的任务不计入跳过记录。
  • 补齐简体中文、繁体中文、英语、日语和韩语文案。

主视图统一启动入口中的显式运行规则如下:

触发方式 周计划规则
手动开始、定时策略、快捷键常规启动 按周计划筛选
单次运行(runOnce) 绕过周计划
单独运行(singleTaskId) 绕过周计划
从此处运行(startFromTaskId) 锚点任务绕过,后续任务仍按周计划筛选;后续单次运行任务保留例外

显式运行表达的是用户本次执行的意图,因此允许临时执行计划日期之外的任务。

持久化与兼容性

在 SelectedTask 和 SavedTask 中增加可选字段 weeklySchedule?: number[],使用 0–6 表示星期,0 为周日。缺省或空数组表示未启用周计划,沿用原有执行规则;清空星期选择会取消周计划。

同步补齐配置保存与加载、最近关闭实例的保存与恢复,以及标签页导出导入链路。标签页共享格式增加可选短字段 ws,保留现有 v1 格式,兼容不含周计划的旧数据,避免导出再导入后丢失执行日期。

过滤由客户端在构建待执行任务列表时完成,资源作者无需修改 Pipeline,也不会将正常跳过表现为 Pipeline 识别失败。

跨天处理

  • 工具栏、任务卡片和仪表盘共用日期订阅,在本地午夜刷新,窗口聚焦或可见性变化时重新读取日期,并每分钟校准系统时间变化。
  • 所有订阅组件共享一个计时器;最后一个订阅取消时清理计时器和事件监听。
  • 仪表盘在实际点击启动时读取当前星期和最新任务列表,避免提交上一次渲染时按旧日期筛选的任务。
  • 每批任务按启动时刻固定执行快照,运行中跨天不会改变本批任务的参与范围。

Evidence

Before: 同一配置页中的已勾选任务无法按星期区分执行日期,需要拆分配置或手动切换勾选状态。

After: 周常任务可以只选周日,日常任务保持每日执行;常规启动会过滤计划外任务,显式运行仍可临时执行。

新增 19 项回归测试,覆盖以下行为:

  • 常规启动的部分跳过、全部跳过、顺序保持,以及缺省和空数组兼容性。
  • 单次运行、单独运行和从此处运行锚点的例外规则。
  • 周计划 [0, 3] 经标签页导出导入后完整保留。
  • 从周三跨到周四后,订阅者更新星期,原本不可执行的周四任务变为可执行。
  • 休眠跨天后的窗口恢复刷新,以及订阅清理和重新订阅。

已验证:

  • pnpm test:25 项测试全部通过。
  • pnpm build:TypeScript 检查和生产构建通过;Vite 提示模块分块及包体积警告。
  • 对本次改动文件执行 Prettier 检查:通过。
  • git diff --check:通过。

上述验证为自动化回归测试与构建检查,未进行真实设备端到端验证。

Merge Danger

Door: two-way

新增字段可选,旧配置无需迁移。代码可以回退,但旧版本不会应用任务周计划,重新保存配置时可能丢弃新增字段;回退时需要检查依赖周计划的定时配置。

Blast Radius: 任务

影响任务执行日期的选择、主视图与仪表盘的启动资格、配置持久化和标签页共享。未配置周计划的任务沿用原有行为,设备连接、资源加载和任务执行本身继续使用现有流程。

本次按客户端本地自然日判断星期,不包含游戏刷新时刻、独立时区设置、通用条件表达式或任务级执行次数;也不增加定时策略与任务周计划无交集的检测。

Sourcery 总结

为任务引入周计划调度,让常规启动按本地星期筛选任务,同时保持显式运行方式和现有配置的兼容性。

新功能:

  • 为任务增加可按本地星期配置的周计划,并在任务界面提供星期选择与执行状态提示。

错误修复:

  • 使主视图、仪表盘及统一启动流程正确跳过计划外任务,避免将正常跳过误报为失败。
  • 在跨天、窗口恢复及系统时间变化后刷新周计划对应的当前星期。

增强功能:

  • 保留单次运行、单独运行及从此处运行锚点等显式执行方式对周计划的例外规则。
  • 将周计划配置接入任务保存、恢复、最近关闭实例及标签页导入导出,并兼容旧配置与旧共享格式。
  • 为周计划跳过、无任务可执行及任务标签显示补充多语言支持。

测试:

  • 新增回归测试,覆盖周计划过滤、显式运行例外、导入导出兼容性及跨天订阅刷新。
Original summary in English

Sourcery 总结

为任务引入按本地星期筛选的周计划,让常规启动仅执行当天计划内的任务,同时保持显式运行方式和现有配置兼容。

新功能:

  • 为任务增加可按本地星期配置的周计划,并在任务界面提供星期选择、当天状态和跳过提示。

错误修复:

  • 使主视图和仪表盘在常规启动时正确跳过计划外任务,并将正常跳过与启动失败区分开来。
  • 在跨午夜、窗口恢复和系统时间变化后刷新当前星期,避免使用过期的周计划筛选结果。

改进:

  • 保留单次运行、单独运行及从此处运行锚点对周计划的显式执行例外。
  • 将周计划接入配置保存恢复、最近关闭实例及标签页导入导出,同时兼容旧配置和旧共享数据。
  • 补充任务跳过、无任务可执行及周计划界面的多语言支持。

测试:

  • 新增回归测试,覆盖周计划过滤、显式运行例外、导入导出兼容性、跨天刷新及多语言星期标签。
Original summary in English

Sourcery 总结

为任务新增按本地星期筛选的周计划功能,使常规启动仅执行当天计划内的任务,同时保留显式运行方式并兼容现有配置。

新功能:

  • 为任务新增可按本地星期配置的周计划,并在任务界面提供星期选择、当日状态和跳过提示。

错误修复:

  • 修复主视图和仪表盘在常规启动时未能正确跳过计划外任务的问题,并区分正常跳过与启动失败。
  • 在跨午夜、窗口恢复及系统时间变化后刷新当前星期,避免使用过期的周计划筛选结果。

改进:

  • 保留单次运行、单独运行及“从此处运行”锚点对周计划的显式执行例外。
  • 将周计划接入配置保存与恢复、最近关闭的实例以及标签页导入与导出,并兼容旧配置和旧共享数据。
  • 为任务跳过、无任务可执行状态及周计划界面补充多语言支持。

测试:

  • 新增回归测试,覆盖周计划筛选、显式运行例外、导入与导出兼容性、跨天刷新及多语言星期标签。
Original summary in English

Sourcery 摘要

为任务引入按本地星期筛选的周计划,让常规启动仅执行当天安排的任务,同时保留显式运行例外并兼容现有配置。

新功能:

  • 为任务提供可按本地星期配置的周计划,并在任务界面支持启用、编辑和查看当天执行状态。

错误修复:

  • 使主视图和仪表盘的常规启动按当天周计划筛选任务,并将计划外任务作为正常跳过处理。
  • 在跨天、窗口恢复和系统时间变化后更新周计划状态,避免使用过期的日期判断。

改进:

  • 保留单次运行、单独运行及“从此处运行”锚点等显式执行方式对周计划的例外规则。
  • 将周计划接入配置保存恢复、最近关闭实例及标签页导入导出,同时兼容旧配置和共享格式。
  • 通过启动参与快照固定每批任务的执行范围,并改善被跳过任务的状态展示和日志信息。
  • 补充周计划相关的多语言界面文案。

测试:

  • 新增回归测试,覆盖周计划筛选、显式运行例外、兼容性、导入导出以及跨天刷新行为。
Original summary in English

Summary by Sourcery

为任务引入按本地星期筛选的周计划,让常规启动仅执行当天安排的任务,同时保留显式运行例外并兼容现有配置。

New Features:

  • 为任务提供可按本地星期配置的周计划,并在任务界面支持启用、编辑和查看当天执行状态。

Bug Fixes:

  • 使主视图和仪表盘的常规启动按当天周计划筛选任务,并将计划外任务作为正常跳过处理。
  • 在跨天、窗口恢复和系统时间变化后更新周计划状态,避免使用过期的日期判断。

Enhancements:

  • 保留单次运行、单独运行及“从此处运行”锚点等显式执行方式对周计划的例外规则。
  • 将周计划接入配置保存恢复、最近关闭实例及标签页导入导出,同时兼容旧配置和共享格式。
  • 通过启动参与快照固定每批任务的执行范围,并改善被跳过任务的状态展示和日志信息。
  • 补充周计划相关的多语言界面文案。

Tests:

  • 新增回归测试,覆盖周计划筛选、显式运行例外、兼容性、导入导出以及跨天刷新行为。

按周计划筛选常规启动任务,保留显式单次运行与运行锚点的例外规则。

同步配置持久化及标签页导入导出,添加共享日期刷新和回归测试。

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

你好——我发现了 4 个问题

面向 AI 代理的提示
请处理此代码审查中的评论:

## 单独评论

### 评论 1
<location path="src/utils/taskRunFilter.ts" line_range="29" />
<code_context>
+  if (!schedule || schedule.length === 0) {
+    return true;
+  }
+  return schedule.includes(todayWeekday);
+}
+
</code_context>
<issue_to_address>
**无效的计划会阻止任务运行**

如果加载或导入的 `weeklySchedule` 包含 0–6 以外的值,`isTaskAllowedByWeeklySchedule` 会直接将今天的星期几与已存储的数组进行比较,因此没有任何值匹配,任务每天都会被跳过。配置加载过程会在未经验证的情况下复制这些值,而且计划 UI 不会显示任何已选星期。

在使用计划之前,请验证并筛除无效的星期值,避免无效条目阻止任务运行。

另请参阅 `src/stores/appStore.ts:835`、`src/stores/appStore.ts:1329`、`src/utils/taskRunFilter.ts:30`、`src/types/interface.ts:353`。
</issue_to_address>

### 评论 2
<location path="src/utils/tabExportImport.ts" line_range="156" />
<code_context>
     customName: w.cn,
     enabled: w.e,
     enabledByController: w.ec,
+    weeklySchedule: w.ws,
     optionValues: Object.fromEntries(
       Object.entries(w.ov).map(([k, v]) => [k, decodeOptionValue(v)]),
</code_context>
<issue_to_address>
**导入的计划会导致任务启动失败**

如果攻击者提供了经过构造的标签页共享文件或剪贴板负载,其中 `ws` 被设置为一个对象,而用户导入该数据并启动任务,`decodeTask` 会在不检查类型的情况下复制 `w.ws`,因此 `isTaskAllowedByWeeklySchedule` 会对非数组值调用 `.includes()` 并抛出错误;启动流程会因此失败,而不是运行任务。

请将导入的 `ws` 值验证为由 0 到 6 的整数构成的数组,或者在存储前丢弃无效值。

另请参阅 `src/utils/taskRunFilter.ts:30`。
</issue_to_address>

### 评论 3
<location path="src/components/TaskItem.tsx" line_range="566" />
<code_context>
+  const todayWeekday = useCurrentWeekday();
+  // 已勾选、但今天不在周计划内:本次启动会被跳过(单次运行不受此限)
+  const weeklyScheduleBlockedToday = isTaskBlockedByWeeklySchedule(task, todayWeekday);
+  const weekdayLabels = t('schedule.weekdays', { returnObjects: true }) as string[];
+  // 有选项、有描述或启用了周计划时都可以展开
+  const canExpand = hasOptions || hasDescription || hasWeeklySchedule;
</code_context>
<issue_to_address>
**星期按钮显示键名字符**

如果当前语言环境未定义 `schedule.weekdays`,`t` 会回退为缺失的键字符串,因此对 `weekdayLabels` 进行索引时,计划按钮上显示的是单个字符,而不是星期名称。

请在每种语言环境的 `schedule.weekdays` 下添加翻译后的七天数组,或使用现有的已定义星期标签键。
</issue_to_address>

### 评论 4
<location path="tests/taskRunFilter.test.ts" line_range="118" />
<code_context>
+
+test('「从此处运行」:锚点为显式操作绕过周计划,锚点之后仍受约束', () => {
+  const tasks = [
+    mk('anchor', { enabled: true, weeklySchedule: [SUN] }),
+    mk('mid', { enabled: true, weeklySchedule: [SUN] }),
+    mk('daily', { enabled: true }),
</code_context>
<issue_to_address>
**未测试起始位置**

如果 `filterTasksForRun` 忽略起始索引并从头开始筛选,由于此测试样例中的锚点仍然位于第一位,测试依然会通过,无法验证更早的任务是否已被排除。

请在锚点前放置一个已启用的任务,并断言该任务不在结果中。
</issue_to_address>

Sourcery 对开源项目免费——如果您喜欢我们的审查,请考虑分享 ✨
Original comment in English

Hey - I've found 4 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/utils/taskRunFilter.ts" line_range="29" />
<code_context>
+  if (!schedule || schedule.length === 0) {
+    return true;
+  }
+  return schedule.includes(todayWeekday);
+}
+
</code_context>
<issue_to_address>
**Invalid schedules block tasks**

When a loaded or imported `weeklySchedule` contains values outside 0–6, `isTaskAllowedByWeeklySchedule` compares today’s weekday directly with the stored array, so no value matches and the task is skipped every day. Configuration loading copies these values without validation, and the schedule UI shows no selected weekdays.

Validate and filter weekday values before using the schedule so invalid entries cannot block the task.

Also at `src/stores/appStore.ts:835`, `src/stores/appStore.ts:1329`, `src/utils/taskRunFilter.ts:30`, `src/types/interface.ts:353`.
</issue_to_address>

### Comment 2
<location path="src/utils/tabExportImport.ts" line_range="156" />
<code_context>
     customName: w.cn,
     enabled: w.e,
     enabledByController: w.ec,
+    weeklySchedule: w.ws,
     optionValues: Object.fromEntries(
       Object.entries(w.ov).map(([k, v]) => [k, decodeOptionValue(v)]),
</code_context>
<issue_to_address>
**Imported plans crash task starts**

When an attacker supplies a crafted tab-sharing file or clipboard payload with `ws` set to an object, and a user imports it and starts tasks, `decodeTask` copies `w.ws` without checking its type, so `isTaskAllowedByWeeklySchedule` calls `.includes()` on a non-array value and throws; the start flow rejects instead of running tasks.

Validate imported `ws` values as arrays of integers from 0 through 6, or discard invalid values before storing them.

Also at `src/utils/taskRunFilter.ts:30`.
</issue_to_address>

### Comment 3
<location path="src/components/TaskItem.tsx" line_range="566" />
<code_context>
+  const todayWeekday = useCurrentWeekday();
+  // 已勾选、但今天不在周计划内:本次启动会被跳过(单次运行不受此限)
+  const weeklyScheduleBlockedToday = isTaskBlockedByWeeklySchedule(task, todayWeekday);
+  const weekdayLabels = t('schedule.weekdays', { returnObjects: true }) as string[];
+  // 有选项、有描述或启用了周计划时都可以展开
+  const canExpand = hasOptions || hasDescription || hasWeeklySchedule;
</code_context>
<issue_to_address>
**Weekday buttons show key characters**

When the active locale does not define `schedule.weekdays`, `t` falls back to the missing key string, so indexing `weekdayLabels` renders individual characters instead of weekday names on the schedule buttons.

Add a translated seven-day array under `schedule.weekdays` for each locale, or use an existing defined weekday-label key.
</issue_to_address>

### Comment 4
<location path="tests/taskRunFilter.test.ts" line_range="118" />
<code_context>
+
+test('「从此处运行」:锚点为显式操作绕过周计划,锚点之后仍受约束', () => {
+  const tasks = [
+    mk('anchor', { enabled: true, weeklySchedule: [SUN] }),
+    mk('mid', { enabled: true, weeklySchedule: [SUN] }),
+    mk('daily', { enabled: true }),
</code_context>
<issue_to_address>
**Start position goes untested**

When `filterTasksForRun` ignores the start index, when `filterTasksForRun` ignores the start index and filters from the beginning, the anchor is still first in this fixture, so the test passes without checking that earlier tasks are excluded.

Place an enabled task before the anchor and assert that it is absent from the result.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread src/utils/taskRunFilter.ts
Comment thread src/utils/tabExportImport.ts Outdated
Comment thread src/components/TaskItem.tsx
Comment thread tests/taskRunFilter.test.ts
统一清理配置及共享数据中的非法星期,避免任务整周被跳过或因非数组计划抛出异常。

覆盖运行锚点之前的任务排除规则,并验证五种语言的星期标签。
@MistEO

MistEO commented Oct 15, 2026

Copy link
Copy Markdown
Member

来点图瞅瞅,弄成啥样了?

@1204244136

Copy link
Copy Markdown
Contributor Author

来点图瞅瞅,弄成啥样了?

5caf3f6042ac371e9cfe59f270bde620 下面是task自带的,预期用上面的取代下面的

@MistEO

MistEO commented Oct 19, 2026

Copy link
Copy Markdown
Member

任务明明运行了,结果因为计划什么也没做吗。感觉很奇怪,我觉得任务执行就应该是执行了,而不是在里面套这种判断

启动前保留今日不参与提示,运行中复用未勾选任务的紧凑样式,移除冗余统计和未纳入标签。

保存启动参与快照,区分历史结果与跨端新批次,并修正定时触发日志、补充组件回归测试。
@1204244136

Copy link
Copy Markdown
Contributor Author

任务明明运行了,结果因为计划什么也没做吗。感觉很奇怪,我觉得任务执行就应该是执行了,而不是在里面套这种判断

image image 已改,不在计划内的会显式声明,正式运行期间视为未勾选

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