NestJS实战06-定时任务

by 雪隐 from juejin.cn/user/143341...

本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可联系授权

概要

前面几章完成了,当日任务和长期目标的基础模块,现在我将要完成定时任务模块。就像我一开始介绍的那样,我要对我每天没有完成的任务,或者长期目标没有达成的情况下,发送电子邮件来提醒我。

如果大家时间充裕的话,可以看下相关的文章使用Cron Jobs和NestJS实现任务自动化通过工作队列发送邮件。重点要看下Cron Jobs,里面有对时间设置的具体说明。

由于个人管理项目,没有什么特别需要处理高并发的需求,所以我只写了普通的邮件发送就足够了,不需要通过工作队列来处理。

定时任务介绍

NestJS 提供了一种非常方便的方式来创建定时任务,通常用于执行周期性的后台任务,例如数据同步、数据清理、报告生成等等。

以下是如何在 NestJS 中创建和使用定时任务的步骤:

  1. 安装相关依赖:
shall 复制代码
$ pnpm install --save @nestjs/schedule
  1. app.module.ts中注册定时任务:
ts 复制代码
// app.module.ts
import { Module } from '@nestjs/common';
import { ScheduleModule } from '@nestjs/schedule';

@Module({
  imports: [
    ScheduleModule.forRoot()
  ],
})
export class AppModule {}
  1. 创建定时任务服务
ts 复制代码
import { Injectable, Logger } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';

@Injectable()
export class TasksService {
  private readonly logger = new Logger(TasksService.name);

  @Cron('45 * * * * *')
  handleCron() {
    this.logger.debug('Called when the current second is 45');
  }
}

详细内容请参照官方文档

邮件发送

在 NestJS 中发送邮件通常涉及使用邮件发送库,如 Nodemailer。下面是如何在 NestJS 中发送邮件的一般步骤:

  1. 安装 Nodemailer:

首先,你需要在你的 NestJS 项目中安装 Nodemailer。可以使用以下命令安装:

ts 复制代码
$ pnpm install nodemailer
  1. 创建一个邮件服务:

在你的 NestJS 应用程序中,创建一个专门的邮件服务,该服务负责配置和发送邮件。你可以创建一个自定义的邮件服务类,也可以将邮件配置添加到现有的服务中。

ts 复制代码
import { Injectable } from '@nestjs/common';
import * as nodemailer from 'nodemailer';
import { ConfigService } from '@nestjs/config';
import { ConfigEnum } from './enum/config.enum';

@Injectable()
export class EmailService {
  private transporter;

  constructor(private readonly configService: ConfigService) {
    const mailConfig = this.configService.get(ConfigEnum.MAIL_CONFIG);

    this.transporter = nodemailer.createTransport({
      host: mailConfig.host,
      port: mailConfig.port,
      secure: true, // 如果是 SMTPS 连接,设置为 true
      auth: {
        user: mailConfig.authUser,
        pass: mailConfig.authPass,
      },
    });
  }

  async sendMail(to: string, subject: string, text: string): Promise<void> {
    const mailOptions = {
      from: this.configService.get(ConfigEnum.MAIL_CONFIG).authUser,
      to,
      subject,
      text,
    };

    await this.transporter.sendMail(mailOptions);
  }
}

在这个示例中,我们创建了一个 EmailService,它接收邮件配置信息并初始化 Nodemailer 的传输器。然后,它提供了一个 sendMail 方法,用于发送邮件。

  1. 注册邮件服务:

EmailService 添加到你的 NestJS 模块的 providers 数组中,以便在整个应用程序中使用它。

ts 复制代码
import { Module } from '@nestjs/common';
import { EmailService } from './email.service';

@Module({
  providers: [EmailService],
  // ...
})
export class AppModule {}
  1. 使用邮件服务:

现在,你可以在你的控制器或其他服务中使用 EmailService 来发送邮件。例如,在你的控制器中:

ts 复制代码
import { Controller, Get } from '@nestjs/common';
import { EmailService } from './email.service';

@Controller('email')
export class EmailController {
  constructor(private readonly emailService: EmailService) {}

  @Get('send')
  async sendEmail() {
    try {
      await this.emailService.sendMail('recipient@example.com', 'Hello', 'This is the email body.');
      return 'Email sent successfully!';
    } catch (error) {
      return 'Email sending failed: ' + error;
    }
  }
}

这是一个简单的示例,说明如何在 NestJS 中发送邮件。你可以根据你的需求扩展和自定义邮件服务,以适应不同的邮件发送场景。确保你的配置信息正确,以及你的邮件服务提供了适当的错误处理来处理可能的发送失败情况。

定时任务做成

  • 首先,因为需要用到当日任务的长期目标模块里面的方法。先导入这2个模块
ts 复制代码
import { Module } from '@nestjs/common';
import { TasksCronService } from './tasks-cron.service';
import { TasksModule } from '../tasks/tasks.module';
import { LongTeamGoalsModule } from '../long-team-goals/long-team-goals.module';

@Module({
  imports: [TasksModule, LongTeamGoalsModule],
  providers: [TasksCronService],
})
export class TasksCronModule {}
  • 然后都要用到邮件发送,所以要在构造函数中初期化transporter。并且发送成功或者失败,希望能在log日志中能看到,所以loggerErrorloggerNomal2个函数。
ts 复制代码
import * as nodemailer from 'nodemailer';
import { TasksService } from '../tasks/tasks.service';
import { LongTeamGoalsService } from '../long-team-goals/long-team-goals.service';
import { ConfigService } from '@nestjs/config';
import { ConfigEnum } from '../enum/config.enum';
import { Injectable, Logger } from '@nestjs/common';

@Injectable()
export class TasksCronService {
  private transporter;
  private readonly logger = new Logger(TasksCronService.name);

  constructor(
    private readonly configService: ConfigService,
    private readonly tasksService: TasksService,
    private readonly longsService: LongTeamGoalsService,
  ) {
    const MAIL_CONFIG = this.configService.get(ConfigEnum.MAIL_CONFIG);

    this.transporter = nodemailer.createTransport({
      host: MAIL_CONFIG.host,
      port: MAIL_CONFIG.port,
      secure: true,
      auth: {
        user: MAIL_CONFIG.authUser,
        pass: MAIL_CONFIG.authPass,
      },
    });
  }
  
  loggerError(message: string, error?: any) {
    this.logger.error(message, error);
  }

  loggerNomal(message: string, info?: any) {
    this.logger.log(message, info);
  }
  
  // 发送邮件
  async sendReminderEmail(taskTitles: string) {
    const MAIL_CONFIG = this.configService.get(ConfigEnum.MAIL_CONFIG);

    const mailOptions = {
      from: MAIL_CONFIG.authUser,
      to: MAIL_CONFIG.destUser,
      subject: '您有未完成的任务',
      text: `以下是您今天尚未完成的任务:\n\n${taskTitles}`,
    };

    this.transporter.sendMail(mailOptions, (error, info) => {
      if (error) {
        this.loggerError(`邮件发送失败: ${error}`);
      } else {
        this.loggerNomal(`邮件已发送: ${info.response}`);
      }
    });
  }
}
  • 简单的介绍下,项目中定时任务的内容。
  1. 每天下午17:30点,检查当日任务里面有没有没完成的任务。如果有就发送电子邮件
ts 复制代码
  @Cron('30 17 * * *') // 每天下午7点30分执行
  async handleCron() {
    this.loggerNomal('开始检查未完成的任务');

    const currentDate = dayjs().format('YYYY-MM-DD');
    const startDate = currentDate;
    const endDate = currentDate;

    const tasks = await this.tasksService.search({
      startDate,
      endDate,
    });

    // 过滤当日任务中已完成的任务。
    const incompleteTasks = tasks.data.filter((task) => !task.isCompleted);

    if (incompleteTasks.length) {
      const titles = incompleteTasks.map((task) => task.title).join('\n');
      await this.sendReminderEmail(titles);
    }
  }
  1. 每天晚上22:00点,检查长期目标里面有没有临近的任务还没有完成。如果有就发送电子邮件
ts 复制代码
  @Cron('0 22 * * *')
  async handleLongCron() {
    const nearlyExpiredGoals = await this.longsService.findNearlyExpiredGoals(
      3,
    );

    if (nearlyExpiredGoals.length) {
      const titles = nearlyExpiredGoals.map((long) => long.title).join('\n');
      await this.sendReminderEmail(titles);
      await this.longsService.setWarned(nearlyExpiredGoals);
    }
  }
  • 长期目标模块中的方法
ts 复制代码
  // 在你的目标服务中
  async findNearlyExpiredGoals(days: number): Promise<any[]> {
    const deadlineDate = new Date(Date.now() + days * 24 * 60 * 60 * 1000);
    return this.longTermGoalModel
      .find({
        deadline: { $lte: deadlineDate },
        isWarned: false,
      })
      .exec();
  }

总结

这章介绍了下定时任务和邮件发送相关的内容,顺便完成,当日任务和长期目标的提醒功能。 如果大家觉得这篇文章对您们有帮助,请记得点赞评论🙏!

本章代码

代码

相关推荐
也无晴也无风雨41 分钟前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang1 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
阮少年、4 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
郝晨妤5 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
AvatarGiser6 小时前
《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明
前端·vue.js·elementui
喝旺仔la6 小时前
vue的样式知识点
前端·javascript·vue.js
别忘了微笑_cuicui6 小时前
elementUI中2个日期组件实现开始时间、结束时间(禁用日期面板、控制开始时间不能超过结束时间的时分秒)实现方案
前端·javascript·elementui