Files
smart_grid_iot/.trae/specs/add-scheduled-job-management/checklist.md
T
andyesfly f4454ad014 feat: 新增电工APP端与定时任务管理功能
- 新增电工APP独立鉴权中间件与登录注册接口
- 新增定时任务管理模块,支持任务配置、执行日志与手动触发
- 新增运维费定价与电工派单成本价目管理
- 新增电工资质证照管理与到期提醒定时任务
- 完善数据库实体与DTO校验,优化权限控制
2026-10-02 16:08:00 +08:00

5.3 KiB
Raw Blame History

Checklist

数据库与种子

  • database/migrations/20261002_job_management.sql 已创建,建 sys_job 与 sys_job_log 两表及索引,可重复执行无报错
  • sys_job 含 job_key 唯一索引与 status 索引;params 为 JSONB 且默认 {}
  • sys_job_log 含 (job_id, created_at DESC) 与 created_at 索引
  • 迁移写入菜单 id 47(定时任务,parent_id=6,/system/job,system:job,menu_type=2,sort_order=6)与 48/49/50 三个按钮权限(parent_id=47,menu_type=3)
  • ADMIN 角色(role_id=1)已绑定菜单 47-50;sys_menu_id_seq 被设为 50
  • 迁移写入 demo_heartbeat(*/5 * * * *)与 log_clean(0 3 * * *,retry_times=2、retry_interval=60)两条 sys_job 种子,重复执行不产生重复行
  • database/db_seed.sql 已同步菜单 47-50、ADMIN 绑定、2 条 sys_job 种子与 sys_menu_id_seq=50;新库初始化与迁移后结构一致
  • 既有菜单 id 7-11 的名称、路径、排序未被改动

后端

  • server/package.json 已显式声明 cron 依赖,yarn dev 可正常编译启动
  • 内置任务注册表 server/src/task/job_registry.ts 提供 jobKey/jobName/description/defaultCron/defaultParams/paramFields/handler,页面参数表单由 paramFields 驱动,无需前端硬编码
  • demo_heartbeat 与 log_clean 两个内置任务均已注册,job_key 与种子数据一致
  • 调度器启动加载全部启用任务,停用任务不被调度
  • 调度初始化失败时不阻塞应用启动(try/catch + 最多 3 次重试),onStop 停止全部定时器
  • 页面修改 cron 后无需重启即按新表达式触发,列表 nextRunTime 同步更新
  • 提交非法 cron 表达式返回 400 且提示明确,配置未落库
  • 停用任务后定时触发停止,nextRunTime 为 null/-,但仍可手动执行
  • 同一任务执行中再次触发时写 status = 3 跳过日志且不并发执行
  • 失败且 attempt <= retry_times 时按 retry_interval 秒后以 trigger_type = 3 重试,attempt 递增
  • sys_job.last_run_at / last_run_status / last_run_cost_ms / last_error 每次执行后正确回写(成功时清空 last_error)
  • log_clean 同时清理 sys_operation_log 与 sys_job_log,摘要含两张表删除条数,默认 keepDays = 90
  • keepDays 越界(如 0 或 5000)返回 400,配置未落库
  • GET /api/jobs 支持任务名称模糊与状态筛选,返回分页结构并携带 paramFields、nextRunTime、上次执行信息
  • PUT /api/jobs/:id、PUT /api/jobs/:id/status、POST /api/jobs/:id/run、GET /api/jobs/:id/logs、DELETE /api/jobs/logs/clear 均可用且返回统一结构
  • 静态路由 /api/jobs/logs/clear 未被 :id 动态路由吞掉
  • error_stack 落库被截断至 4000 字符,message 摘要可读
  • 未注册的 job_key 在页面标记为未注册,编辑/启停/执行返回 400「任务实现不存在」
  • PERMISSION_MAP 已新增 '/api/jobs': 'system:job',非 ADMIN 无该权限时访问返回 403
  • 所有查询/更新走 Sequelize 参数化 API,无字符串拼接 SQL 与模板字符串 LIKE
  • 控制器入参使用 DTO + class-validator,未滥用 any(除既有约定处)
  • 列表接口均使用 limit + offset 分页,无全表扫描式查询
  • 调度器为进程内单例(@Scope(ScopeEnum.Singleton)),保证「启动注册」与「接口重载」操作同一个定时器注册表

前端

  • client/src/api/job.ts 提供 6 个接口方法与 TS 类型
  • client/src/views/system/job/index.vue 含搜索栏 + 表格 + 编辑弹窗 + 立即执行二次确认 + 执行日志抽屉
  • 表格列包含任务名/键、cron、参数摘要、状态开关、上次执行时间与状态与耗时、下次执行时间、重试配置
  • 编辑弹窗中 cron、参数、重试次数、重试间隔可编辑,且保存前后端校验失败时弹窗保留并提示 message
  • 执行日志抽屉支持分页、状态筛选与「清理日志」二次确认(默认清理 90 天前)
  • 路由 /system/job 已注册,meta.permission = 'system:job'
  • 侧边栏「系统管理」下可见「定时任务」(ADMIN),非 ADMIN 访问跳转 403
  • 页面未使用 v-html,cron 与参数输入含长度/范围校验
  • 页面结构与既有页面(操作日志 / 数据字典)保持一致,未引入多余 UI 元素

联调

  • 后端 yarn dev 启动无编译错误,接口逐条通过
  • 前端 yarn dev 启动,编辑 → 立即执行 → 查看日志 → 清理日志 全链路可用
  • 改 cron 即时生效与启停即时生效已验证
  • 重试机制(trigger_type = 3、attempt 递增)已验证
  • 迁移脚本与 db_seed.sql 在空库与存量库两端结果一致
  • 隔离单实例复测:*/1 每分钟恰好触发 1 次、停用后 0 次触发、手动执行正常、恢复后 nextRunTime 正确(见 tasks.md Task 8)

遗留说明(非阻塞)

  • sys_job.job_key 同时存在列级 UNIQUE 自动生成的 sys_job_job_key_key 与显式 uk_sys_job_key,两个索引功能重复(冗余但无害,不影响验收)
  • 非法入参返回的是响应体 code=400,HTTP 状态保持 200,与仓库既有 26 个控制器的约定一致