NestJS 动态定时任务

123 阅读2分钟

项目开发需求定时发送邮件任务,要求发送时间为截止日期的前一周,前一天,后一天和后一周这四个时间点。当事务完成后中断定时任务。 在 NestJS 中,可以使用 SchedulerRegistry 来动态创建和管理定时任务。 首先在 task.service.ts 文件中引入 @nestjs/schedule, 并注册 SchedulerRegistry

import { Cron, SchedulerRegistry } from '@nestjs/schedule';
@Injectable()
export class TasksService {
    constructor(
        private readonly schedulerRegistry: SchedulerRegistry
    ) { }
}

然后在 TasksService 中定义方法,代码如下:


  // 创建任务
    async createBeforeTask(action: CallReportAction, message: Message, name: string) {
        let job: CronJob
        for (const key of getCronExpression(action.deadline)) {
            job = new CronJob(key, () => {
                this.messageService.sendActionEmail(action, message)
            });
            this.schedulerRegistry.addCronJob(name + Math.random(), job);
            job.start();
        }
    }

/**
 * 定时邮件计算出定时时间
 * @param time
 * @returns Date[]
 */
export const getCronExpression = (time: Date): Date[] => {
    const Dates = [];
    var previousDay = moment(time).subtract(1, 'days').startOf('day').toDate()
    var previousWeek = moment(time).subtract(7, 'days').startOf('day').toDate()
    var afterDay = moment(time).add(1, 'days').startOf('day').toDate()
    var afterWeek = moment(time).add(7, 'days').startOf('day').toDate()

    if (new Date(previousDay) > new Date()) {
        Dates.push(previousDay)
    }
    if (new Date(previousWeek) > new Date()) {
        Dates.push(previousWeek)
    }
    if (new Date(afterWeek) > new Date()) {
        Dates.push(afterWeek)
    }
    if (new Date(afterDay) > new Date()) {
        Dates.push(afterDay)
    }

    return Dates;
}

    // 关闭任务
    deleteTask(name: string): void {
        const tasks = this.schedulerRegistry.getCronJobs();
        for (const [key, value] of tasks) {
            if (key.includes(name)) {
                this.schedulerRegistry.deleteCronJob(key);
                // 这里可以添加日志记录,或者其他逻辑
            }
        }
    }

createBeforeTask 用于动态发起定时任务,name 代表要开启的任务名。getCronExpression 方法计算定时发送的四个时间点。循环遍历四个时间点依次添加定时任务。deleteTask 是用于中断定时任务,接收参数只需要任务名即可。 然后在需要调用方法的地方引入即可,代码如下:

// 引入 TaskServivce
import { TasksService } from '@/modules/tasks/tasks.service';
export class TimedTransmissionProcessor {
  constructor(
    private readonly tasksService: TasksService
  ) { }

 async action(job: Job<any>) {
    try {
      const { newAction, message } = job.data
    //   定义任务名,且具有唯一性
      const name = 'action' + newAction?.id.toString() + '_'
    //   删除存在的同名任务
      this.tasksService.deleteTask(name)
    // 开启定时任务
      await this.tasksService.createBeforeTask(newAction, message, name)
    } catch (error) {
      Logger.log(error)
      throw error
    }
  }
}

这样就可以实现动态定时任务的开启和关闭的功能。