execution-log.md 14 KB

定时任务可靠性加固 Execution Log

2026-07-19 Apply 启动

  • 用户确认按路线图顺序开始开发,首轮只实施 V1。
  • 分支:codex/job-scheduler-reliability。
  • 范围:平台超级管理员控制面;不增加 owner_org_id、普通租户自助、一次性任务或独立时区。
  • 已读取:项目记忆、Forge 编码规范、自动化测试标准、V1 spec.md/tasks.md。
  • 当前最大 Flyway 版本:V1.0.39;V1 迁移计划使用 V1.0.40。
  • 工作区已有无关改动:.DS_Store、forge/.DS_Store、forge-server/db/全量初始化SQL.sql;本变更不修改、不提交这些文件。
  • 服务启动:无。

2026-07-19 Task 1 最小数据库迁移

  • 变更范围:新增 V1.0.40__harden_job_scheduler_reliability.sql、迁移契约测试,并为 job 插件补充测试依赖。
  • 首次执行未带 -Penable-tests 的模块测试时,Maven 构建成功但根 POM 默认 forge.tests.skip=true,测试实际跳过;后续测试命令统一显式启用 enable-tests Profile。
  • 红灯验证:
    • 命令:JAVA_HOME=/opt/homebrew/Cellar/openjdk@17/17.0.13/libexec/openjdk.jdk/Contents/Home PATH=/opt/homebrew/Cellar/openjdk@17/17.0.13/libexec/openjdk.jdk/Contents/Home/bin:$PATH mvn -Penable-tests -pl forge-framework/forge-plugin-parent/forge-plugin-job -am -Dtest=JobReliabilityMigrationContractTest -Dsurefire.failIfNoSpecifiedTests=false test
    • 结果:迁移文件创建前 Tests run: 7, Errors: 7,失败原因均为缺少 V1.0.40__harden_job_scheduler_reliability.sql,符合先失败预期。
  • 实现后首次复跑:Tests run: 7, Failures: 1;根因是契约测试未考虑动态 SQL 字符串中的单引号转义,修正断言后再次执行。
  • 绿灯验证:使用同一 Maven 命令,Tests run: 7, Failures: 0, Errors: 0, Skipped: 0,Reactor BUILD SUCCESS
  • Flyway placeholder 扫描:rg -n '\$\{[^}]+\}' forge-server/db/migration/V1.0.40__harden_job_scheduler_reliability.sql,无输出。
  • 格式检查:git diff --check -- forge-server/db/migration/V1.0.40__harden_job_scheduler_reliability.sql forge-server/forge-framework/forge-plugin-parent/forge-plugin-job/pom.xml forge-server/forge-framework/forge-plugin-parent/forge-plugin-job/src/test/java/com/mdframe/forge/plugin/job/migration/JobReliabilityMigrationContractTest.java,通过。
  • 数据库实跑:本阶段未执行,遵循现有约定由用户在真实开发库验证 Flyway 和 forge_schema_history;不将静态契约测试表述为数据库迁移已实际执行。
  • 服务启动:无。

2026-07-19 Task 2 DTO、VO、校验和 Mapper XML

  • 红灯验证:新增 JobConfigValidatorTestJobMapperXmlContractTest 后执行定向测试,testCompileJobConfigSaveRequestJobConfigValidator 尚不存在而失败,符合先失败预期。
  • 实现范围:新增任务查询/保存 DTO、日志查询 DTO、任务 VO 和权威校验器;实体映射同步状态、版本、日志关联与触发类型;任务分页/详情/恢复候选/JobKey 查询及日志分页迁移到 Mapper XML。
  • 校验范围:Cron 表达式、JSON 参数、BEAN/HANDLER/RPC 执行目标、0/1 状态、非负重试次数、字段长度和更新时 JobKey 不可编辑。
  • 首次实现后定向测试:XML 契约 3/3 通过;校验测试因本机 Mockito inline mock-maker 无法附加 JVM 而报错。测试随后改用真实轻量 GenericApplicationContext,移除 Mockito 运行时依赖。
  • 定向绿灯:mvn -Penable-tests -pl forge-framework/forge-plugin-parent/forge-plugin-job -am -Dtest=JobConfigValidatorTest,JobMapperXmlContractTest -Dsurefire.failIfNoSpecifiedTests=false testTests run: 9, Failures: 0, Errors: 0, Skipped: 0
  • 模块回归:mvn -Penable-tests -pl forge-framework/forge-plugin-parent/forge-plugin-job -am -Dsurefire.failIfNoSpecifiedTests=false test,job 模块 Tests run: 16, Failures: 0, Errors: 0, Skipped: 0,Reactor BUILD SUCCESS
  • XML 检查:xmllint --noout 校验 SysJobConfigMapper.xmlSysJobLogMapper.xml,通过。
  • 查询规范扫描:rg -n 'LambdaQueryWrapper' .../service 无输出。
  • 格式检查:git diff --check -- forge-server/forge-framework/forge-plugin-parent/forge-plugin-job,通过。
  • 服务启动:无。

2026-07-19 Task 3 Quartz 异常和幂等同步

  • 红灯验证:新增 JobSchedulerTestJobScheduleCoordinatorTest 后定向测试因 JobScheduleExceptionJobScheduleCoordinator 尚不存在而 testCompile 失败,符合先失败预期。
  • 调度核心:JobScheduler 的新增、更新、删除、暂停、恢复、触发、Cron 更新和存在性检查不再捕获异常后返回 false;统一抛出 JobScheduleException
  • 幂等同步:新增 JobScheduleCoordinator,按 JobKey 对 Quartz 执行新增或替换,并根据数据库状态暂停/恢复;成功写 SYNCED,普通失败写 FAILED,删除失败保留 DELETE_PENDING 并记录错误。
  • 写链路:Service 先落数据库期望状态,再调用协调器,不使用跨数据库/Quartz 的伪原子本地事务;因此 Quartz 失败时数据库记录和同步错误可以保留,异常同时对调用方可见。
  • 重试:retrySynchronization 会清理普通失败错误并重新同步;删除重试保持 DELETE_PENDING 意图,避免误恢复为普通任务。
  • 定向验证:mvn -Penable-tests -pl forge-framework/forge-plugin-parent/forge-plugin-job -am -Dtest=JobSchedulerTest,JobScheduleCoordinatorTest -Dsurefire.failIfNoSpecifiedTests=false testTests run: 5, Failures: 0, Errors: 0, Skipped: 0
  • 模块回归:mvn -Penable-tests -pl forge-framework/forge-plugin-parent/forge-plugin-job -am -Dsurefire.failIfNoSpecifiedTests=false test,job 模块 Tests run: 21, Failures: 0, Errors: 0, Skipped: 0,Reactor BUILD SUCCESS
  • 静默失败扫描:JobScheduler.java 中无调度操作 return false 或 boolean CRUD 签名。
  • XML/格式检查:Mapper XML 通过 xmllint --noout,job 插件目录通过 git diff --check
  • 服务启动:无。

2026-07-19 Task 4 统一启动恢复和注解登记

  • 实现范围:JobAutoRegistrar 只按 job_name + job_group 登记数据库期望配置,不覆盖既有配置、不直接操作 Quartz;新增 JobStartupReconciler 作为唯一 ApplicationRunner 恢复入口;删除旧 JobConfigLoader
  • 启动恢复:统一调用 JobScheduleCoordinator.reconcileOnStartup(),按数据库期望状态补建或更新 Quartz 任务,禁用任务保持暂停,不扫描或删除数据库之外的 Quartz Job。
  • 定向验证:mvn -Penable-tests -pl forge-framework/forge-plugin-parent/forge-plugin-job -am -Dtest=JobAutoRegistrarTest,JobStartupRecoveryContractTest,JobScheduleCoordinatorTest -Dsurefire.failIfNoSpecifiedTests=false testTests run: 8, Failures: 0, Errors: 0, Skipped: 0,Reactor BUILD SUCCESS
  • 模块回归:mvn -Penable-tests -pl forge-framework/forge-plugin-parent/forge-plugin-job -am -Dsurefire.failIfNoSpecifiedTests=false test,job 模块 Tests run: 26, Failures: 0, Errors: 0, Skipped: 0,Reactor BUILD SUCCESS
  • 日志说明:故障分支测试会有预期的 Quartz Scheduler has been shutdown 错误栈,但断言通过,不是构建失败。
  • XML/格式检查:SysJobConfigMapper.xmlSysJobLogMapper.xml 通过 xmllint --noout;job 插件与 starter-job 目录通过 git diff --check
  • 服务启动:无。

2026-07-19 Task 5 日志筛选和安全裁剪

  • 红灯验证:新增 JobLogSanitizerTestJobExecutionLogContractTest 后执行定向测试,testCompileJobLogSanitizer 尚不存在而失败,符合先失败预期。
  • 安全处理:新增结构化 JSON 递归脱敏和文本兜底规则,覆盖 Authorization/Bearer、Token、密码、Secret、Cookie、手机号、身份证号等;任务参数和执行结果上限 2000 字符,异常上限 4000 字符,截断时保留明确标记。
  • 日志链路:JobMonitor 写入前脱敏;日志分页和详情读取时再次清洗,历史记录也不会直接返回原始敏感值;QuartzJobExecutor 不再向应用日志输出原始结果或异常内容。
  • 关联与来源:Quartz JobDataMap 携带 jobConfigId,计划触发记录 SCHEDULED,立即运行覆盖为 MANUAL,最终写入 sys_job_log.job_config_id/trigger_type
  • 时间筛选:Mapper XML 明确使用 trigger_time >= startTimetrigger_time <= endTime;前端改为发送本地 yyyy-MM-dd HH:mm:ss,避免原 toISOString() 的 UTC 偏移和后端格式不匹配。
  • 定向绿灯:mvn -Penable-tests -pl forge-framework/forge-plugin-parent/forge-plugin-job -am -Dtest=JobLogSanitizerTest,JobExecutionLogContractTest,JobMapperXmlContractTest,JobSchedulerTest -Dsurefire.failIfNoSpecifiedTests=false testTests run: 11, Failures: 0, Errors: 0, Skipped: 0
  • 模块回归:mvn -Penable-tests -pl forge-framework/forge-plugin-parent/forge-plugin-job -am -Dsurefire.failIfNoSpecifiedTests=false test,job 模块 Tests run: 33, Failures: 0, Errors: 0, Skipped: 0,Reactor BUILD SUCCESS
  • 前端定向检查:Node v20.19.0 下执行 pnpm exec eslint src/views/system/job-log-list.vue,首次发现一处 prefer-template,修正后复跑无输出、退出码 0。
  • XML/格式检查:SysJobLogMapper.xml 通过 xmllint --noout;job 插件和日志页面通过 git diff --check
  • 服务启动:无。

2026-07-19 Task 6 Controller 和前端同步状态

  • 后端接口:新增 POST /job/config/{id}/sync,Controller 调用 retrySynchronization;保存已落库但 Quartz 同步失败时返回“任务配置已保存,但调度同步失败,请在列表中点击‘重新同步’”,避免用户误以为配置完全失败或完全成功。
  • 列表信息架构:默认展示任务、执行内容、执行计划、运行状态、调度同步和操作,不再把 Bean、方法、Handler 等技术字段平铺为主列;常见 Cron 显示自然语言摘要,同步失败显示短错误原因。
  • 操作可理解性:仅 FAILEDDELETE_PENDING 提供“重新同步”;编辑时任务名称和分组禁用;立即运行增加“不改变当前启停状态”的确认;启停、删除、重新同步及同步失败保存后刷新列表,避免乐观版本和同步状态停留在旧值。
  • 范围控制:日志清理移入“日志维护”菜单;移除当前未真正生效的重试、告警和 Webhook 可见配置;继续使用 V1 弹窗,不提前实施 V2 全屏配置工作台。
  • 红灯验证:新增 JobSyncApiContractTest 后,接口契约因缺少 JobConfigController.sync(Long)NoSuchMethodException,UI 契约断言同步入口失败,符合先失败预期。
  • 定向绿灯:实现后执行 JobSyncApiContractTest,接口和 UI 两项契约 2/2 通过。
  • job 模块回归:mvn -Penable-tests -pl forge-framework/forge-plugin-parent/forge-plugin-job -am -Dsurefire.failIfNoSpecifiedTests=false test,job 模块 Tests run: 35, Failures: 0, Errors: 0, Skipped: 0,Reactor BUILD SUCCESS
  • 前端定向检查:Node v20.19.0 下执行 pnpm exec eslint src/views/system/job-config.vue src/views/system/job-log-list.vue,无输出、退出码 0。
  • 前端生产构建:Node v20.19.0 下执行 NODE_OPTIONS=--max-old-space-size=8192 pnpm build8699 modules transformedbuilt in 1m 28s,退出码 0。
  • 构建警告:存在仓库既有的动态/静态导入混用、CSS // 注释、组件命名冲突和大包体提示;未发现指向本次定时任务页面的编译错误,不阻断本阶段。
  • UI 联调:尚未连接真实 Admin、MySQL、Redis 或 Quartz 数据,生产构建通过不等于真实数据交互已验收;Task 7 尝试浏览器静态链路后仍会保留此边界。
  • 服务启动:无。

2026-07-19 Task 7 最终回归验证

  • 收尾审查修正:同步失败或待删除任务原本仍可“运行一次”,可能触发 Quartz 中的旧配置。后端 triggerJob 现要求 sync_status=SYNCED,否则提示“任务尚未同步到调度服务,请先重新同步”;前端仅对已同步任务展示“运行一次”。
  • 操作优先级修正:“重新同步”移到失败任务首要操作;DELETE_PENDING 任务只保留“重新同步”和“查看日志”,避免编辑、启停、再次删除或触发执行与删除意图冲突。
  • 门禁定向测试:SysJobConfigServiceImplTest,JobSyncApiContractTest4/4 通过;前端定向 ESLint 和相关 git diff --check 同时通过。
  • job 模块最终回归:mvn -Penable-tests -pl forge-framework/forge-plugin-parent/forge-plugin-job -am -Dsurefire.failIfNoSpecifiedTests=false testTests run: 37, Failures: 0, Errors: 0, Skipped: 0,Reactor BUILD SUCCESS,总耗时约 14 秒。
  • Admin 聚合构建:mvn -pl forge-admin-server -am package -DskipTests,42 个 Reactor 模块全部 SUCCESS,最终 BUILD SUCCESS,总耗时约 46 秒。
  • 前端最终检查:Node v20.19.0 下定向 ESLint 无输出、退出码 0;pnpm build 完成 8699 modules transformedbuilt in 1m 57s,退出码 0。
  • 静态检查:迁移脚本 Flyway placeholder 扫描无输出;SysJobConfigMapper.xmlSysJobLogMapper.xmlxmllint --noout 通过;Service 未发现 LambdaQueryWrapper;本变更范围 git diff --check 通过。
  • 已知非阻断警告:Quartz 故障测试打印预期的 Scheduler has been shutdown 堆栈;Maven 存在仓库既有废弃 API/未检查操作提示;Vite 存在组件命名冲突、动态/静态导入、CSS // 注释和包体提示。
  • 浏览器验证尝试:已先执行 with_server.py --help。Vite dev 因本机文件监听达到上限报 EMFILE: too many open files, watch;改用生产构建 preview 后,本地端口绑定被沙箱以 listen EPERM 拒绝。辅助脚本均已停止本轮进程,没有遗留服务。
  • 未覆盖边界:未启动真实 Admin、MySQL、Redis 或 Quartz 服务,未实跑 Flyway,未完成真实数据和权限下的 UI 点击验收;这些结论不得表述为已通过。
  • 服务启动:无成功启动的服务;失败尝试均已自动或手动停止。