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();
  }

总结

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

本章代码

代码

相关推荐
_.Switch39 分钟前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光43 分钟前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   43 分钟前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   44 分钟前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web1 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常1 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇2 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr2 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho3 小时前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常4 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js