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

如何基于Firebase数据库用户设定日期实现提前一天锻炼推送通知

实现Firebase健身训练提前一天推送通知方案

核心思路

通过Firebase的Cloud Functions结合Cloud Messaging(FCM),配合Firestore/Realtime Database存储的训练数据,实现定时触发通知推送。以下是两种可行的实现方案:


方案一:每日定时扫描数据库

适合批量处理所有用户的训练记录,每天固定时间(如凌晨1点)自动检查次日有训练的用户并推送通知。

步骤1:数据库结构设计

在Firestore中创建trainings集合,每条文档存储用户训练信息:

// Firestore文档示例
{
  userId: "user_xxx",
  trainingDatetime: Timestamp.fromDate(new Date("2024-05-20T09:00:00")), // 训练时间
  fcmToken: "用户设备的FCM推送令牌",
  notificationSent: false // 标记是否已发送通知,避免重复推送
}

步骤2:编写Cloud Functions定时触发器

使用Node.js编写云函数,每日定时扫描符合条件的训练记录:

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

// 每日凌晨1点(北京时间)触发扫描
exports.checkUpcomingTrainings = functions.pubsub.schedule('0 1 * * *')
  .timeZone('Asia/Shanghai')
  .onRun(async (context) => {
    const now = new Date();
    const tomorrowStart = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
    const tomorrowEnd = new Date(tomorrowStart.getTime() + 24 * 60 * 60 * 1000);

    // 查询次日训练且未发送通知的记录
    const querySnapshot = await admin.firestore()
      .collection('trainings')
      .where('trainingDatetime', '>=', tomorrowStart)
      .where('trainingDatetime', '<', tomorrowEnd)
      .where('notificationSent', '==', false)
      .get();

    const taskPromises = [];
    querySnapshot.forEach(doc => {
      const data = doc.data();
      // 构造FCM通知内容
      const message = {
        notification: {
          title: "健身训练提醒",
          body: `你明天有训练安排,时间:${new Date(data.trainingDatetime.toDate()).toLocaleString()},别忘记哦!`
        },
        token: data.fcmToken
      };

      // 发送通知并标记已发送
      const sendTask = admin.messaging().send(message)
        .then(() => doc.ref.update({ notificationSent: true }))
        .catch(err => functions.logger.error("通知发送失败:", err));
      
      taskPromises.push(sendTask);
    });

    return Promise.all(taskPromises);
  });

方案二:创建训练时设置延迟触发

当用户创建/更新训练时间时,直接计算提前24小时的时间点,设置延迟执行的云函数,到点自动推送通知。

步骤1:编写触发型云函数

包含创建训练时的延迟任务、更新训练时的重新调度逻辑:

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

// 训练记录创建时,调度通知任务
exports.scheduleTrainingNotification = functions.firestore
  .document('trainings/{trainingId}')
  .onCreate(async (snap, context) => {
    const data = snap.data();
    const trainingTime = data.trainingDatetime.toDate();
    const now = new Date();
    const delayMs = trainingTime.getTime() - now.getTime() - 24 * 60 * 60 * 1000; // 提前24小时的延迟时间

    if (delayMs <= 0) {
      // 训练时间不足24小时,立即发送通知
      return sendNotification(snap.ref, data);
    }

    // 设置延迟执行任务
    return setTimeout(async () => {
      await sendNotification(snap.ref, data);
    }, delayMs);
  });

// 训练记录更新时,重新调度通知任务
exports.updateTrainingNotification = functions.firestore
  .document('trainings/{trainingId}')
  .onUpdate(async (change, context) => {
    const newData = change.after.data();
    const oldData = change.before.data();

    // 仅当训练时间或FCM令牌变化时重新调度
    if (newData.trainingDatetime.toDate().getTime() !== oldData.trainingDatetime.toDate().getTime() || 
        newData.fcmToken !== oldData.fcmToken) {
      const trainingTime = newData.trainingDatetime.toDate();
      const now = new Date();
      const delayMs = trainingTime.getTime() - now.getTime() - 24 * 60 * 60 * 1000;

      if (delayMs <= 0) {
        return sendNotification(change.after.ref, newData);
      }

      return setTimeout(async () => {
        await sendNotification(change.after.ref, newData);
      }, delayMs);
    }
    return null;
  });

// 通用通知发送函数
async function sendNotification(docRef, data) {
  const message = {
    notification: {
      title: "健身训练提醒",
      body: `你明天有训练安排,时间:${new Date(data.trainingDatetime.toDate()).toLocaleString()},别忘记哦!`
    },
    token: data.fcmToken
  };

  try {
    await admin.messaging().send(message);
    await docRef.update({ notificationSent: true });
    functions.logger.log("通知发送成功");
  } catch (err) {
    functions.logger.error("通知发送失败:", err);
    throw err;
  }
}

关键注意事项

  • 时区处理:确保训练时间和云函数触发时间的时区一致,避免时差导致的推送错误。
  • FCM令牌管理:定期清理失效的FCM令牌(可通过FCM返回的错误码判断),避免无效推送。
  • 重复推送防护:通过notificationSent字段标记已发送的通知,防止重复触发。
  • 错误日志:利用Cloud Functions的日志功能记录推送失败信息,方便排查问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 01:15:51