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('[email protected]', '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();
  }

总结

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

本章代码

代码

相关推荐
随风九天38 分钟前
使用 Nginx 进行前端灰度发布的策略与实践
运维·前端·nginx·前端灰度发布
黄Java1 小时前
SVG中linearGradient的id冲突的显隐问题深度解析
前端·svg
蜗牛快跑1232 小时前
通过尤大“围绕Vite的前端统一框架”分享,看未来前端发展趋势
前端
skywalk81632 小时前
Mac下安装Zed以及Zed对MCP(模型上下文协议)的支持
服务器·前端·macos
陈龙龙的陈龙龙2 小时前
macOS 安装 Homebrew、nvm 及安装切换 node 版本
前端·macos·bash
asphyxia2 小时前
老龄化项目问题解决
前端
SaebaRyo2 小时前
作为一个前端er如何了解LLM(大语言模型)
前端·llm
悬炫2 小时前
深入解析浏览器渲染原理与性能优化策略
前端·javascript
鸡血园地2 小时前
前端性能优化
前端
铠文2 小时前
垃圾回收机制核心知识点
javascript