You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS提醒功能实现咨询:如何定时触发移动端通知

问题

我在NestJS中开发了一个POST接口,用于添加一个Date对象,该对象对应向移动端发送通知的日期和时间。我需要检测所有用户的提醒是否已到期,从而触发移动端提醒。以下是我在NestJS中编写的函数:

import { Injectable } from '@nestjs/common'
import { UserRepository } from '../auth/repositories/user.repository'
import { User } from '@vitabotx/common/entities/auth/user.entity'

@Injectable()
export class NotificationsCronService {
    constructor(private readonly userRepository: UserRepository) {}

    async sleepReminderCron() {
        const users: User[] =
            await this.userRepository.getAllUsersForSleepReminder()

        // Set up interval to check reminders continuously
        const interval = setInterval(async () => {
            const currentDate = new Date()

            for (const user of users) {
                for (const reminder of user.userReminder) {
                    if (currentDate >= reminder.time) {
                        console.log(
                            `User ${user.id} should receive sleep reminder.`
                        )
                    }
                }
            }
        }, 1000)

        setTimeout(
            () => {
                clearInterval(interval)
            },
            59 * 60 * 1000
        )
    }
}

我原本考虑使用setInterval和setTimeout每秒检查提醒是否到期,而非每分钟查询数据库。想请教业界其他项目实现此类场景的推荐方案是什么?

推荐方案

1. 基于NestJS内置定时任务实现

直接用NestJS的@nestjs/schedule模块做定时调度,比如每分钟执行一次查询,只拉取已到期且未处理的提醒,而非全量用户数据:

import { Injectable, Logger } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';
import { UserRepository } from '../auth/repositories/user.repository';

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

  constructor(private readonly userRepository: UserRepository) {}

  // 每分钟执行一次
  @Cron('* * * * *')
  async handleSleepReminder() {
    const now = new Date();
    // 查询当前时间已到期、未发送的提醒
    const dueReminders = await this.userRepository.findUnsentDueReminders(now);
    
    for (const reminder of dueReminders) {
      this.logger.log(`触发用户${reminder.userId}的睡眠提醒`);
      // 调用移动端推送接口
      // 标记提醒为已发送,避免重复触发
      await this.userRepository.markReminderAsSent(reminder.id);
    }
  }
}

这种方式不用手动管理setInterval,NestJS会自动维护任务生命周期,且每次只处理需要触发的记录,性能更可控。

2. 消息队列+延迟任务方案

如果要求提醒触发精确到秒,推荐用支持延迟任务的消息队列(比如BullMQ、RabbitMQ延迟插件):

  • 用户创建提醒时,直接把推送任务发送到队列,设置延迟时间为「提醒时间 - 当前时间」
  • 到达指定时间后,队列自动触发任务执行推送
  • 无需轮询数据库,资源占用更低,时效性更精准

示例(BullMQ):

// 创建提醒时添加延迟任务
async createReminder(reminderData) {
  const delay = reminderData.time.getTime() - Date.now();
  if (delay > 0) {
    await this.reminderQueue.add('send-sleep-reminder', {
      userId: reminderData.userId,
    }, { delay });
  }
}

// 消费任务
this.reminderQueue.process('send-sleep-reminder', async (job) => {
  const { userId } = job.data;
  console.log(`触发用户${userId}的睡眠提醒`);
  // 执行移动端推送逻辑
});

3. 优化轮询查询逻辑

如果坚持用轮询方案,别一次性拉全量用户:

  • 在userReminder表的time和isSent字段上建联合索引
  • 每次轮询直接查数据库中当前时间已到期且未处理的提醒,减少内存占用和循环开销

4. 分布式场景下的任务控制

如果服务是分布式部署,要避免多实例重复执行任务:

  • 用Redis分布式锁,确保同一时间只有一个实例执行定时任务
  • 或者用专门的分布式任务调度系统(比如XXL-JOB)统一管理任务

内容的提问来源于stack exchange,提问作者Ghosty

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 11:07:04