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

如何在React+Firebase中于指定时间向用户列表发送推送通知?

事件触发式推送通知实现方案

核心逻辑

要完成事件发生时给报名用户发送推送,需要结合Firebase的Firestore、Cloud Functions、Firebase Cloud Messaging(FCM)三个核心服务,同时要确保用户UID与FCM推送令牌关联存储。

具体实现步骤

1. 关联用户UID与FCM令牌

前端需要在用户登录后,获取设备的FCM令牌并存储到Firestore的users集合中(文档ID用用户UID):

// React前端代码:获取并存储FCM令牌
import { getMessaging, getToken } from "firebase/messaging";
import { doc, setDoc } from "firebase/firestore";
import { auth } from "./firebaseConfig";

const saveFCMToken = async () => {
  const messaging = getMessaging();
  try {
    const token = await getToken(messaging, { vapidKey: "你的VAPID密钥" });
    if (token && auth.currentUser) {
      await setDoc(
        doc(db, "users", auth.currentUser.uid),
        { fcmToken: token },
        { merge: true }
      );
    }
  } catch (error) {
    console.error("FCM令牌获取失败:", error);
  }
};

// 用户登录成功后调用该函数

2. 用Cloud Functions定时扫描并发送通知

通过Cloud Functions的定时触发器,定期扫描即将发生的事件,获取报名用户的FCM令牌并批量发送推送:

// Cloud Functions代码
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

// 每5分钟扫描一次即将发生的事件(可根据需求调整频率)
exports.sendEventNotifications = functions.pubsub.schedule("every 5 minutes").onRun(async (context) => {
  const now = new Date();
  // 筛选接下来5分钟内要发生的事件
  const eventsSnapshot = await admin.firestore()
    .collection("events")
    .where("datetime", ">=", now)
    .where("datetime", "<=", new Date(now.getTime() + 5 * 60 * 1000))
    .where("notificationSent", "!=", true) // 跳过已发送通知的事件
    .get();

  if (eventsSnapshot.empty) {
    functions.logger.info("无即将发生的事件");
    return null;
  }

  // 遍历事件处理推送
  for (const doc of eventsSnapshot.docs) {
    const { registeredUsers, title, datetime } = doc.data();
    const userTokens = [];

    // 收集报名用户的有效FCM令牌
    for (const uid of registeredUsers) {
      const userDoc = await admin.firestore().collection("users").doc(uid).get();
      if (userDoc.exists && userDoc.data().fcmToken) {
        userTokens.push(userDoc.data().fcmToken);
      }
    }

    if (userTokens.length === 0) {
      functions.logger.info(`事件${doc.id}无有效推送令牌`);
      continue;
    }

    // 构建推送消息
    const message = {
      notification: {
        title: `事件提醒:${title}`,
        body: `你报名的「${title}」即将开始,时间:${datetime.toLocaleString()}`
      },
      tokens: userTokens
    };

    // 发送批量推送
    try {
      const response = await admin.messaging().sendMulticast(message);
      functions.logger.info(`推送完成:成功${response.successCount}条,失败${response.failureCount}条`);
      // 标记事件已发送通知,避免重复推送
      await doc.ref.update({ notificationSent: true });
    } catch (error) {
      functions.logger.error(`推送失败:${error}`);
    }
  }

  return null;
});

3. 前端配置消息接收

在public目录下创建firebase-messaging-sw.js,处理后台消息接收:

// firebase-messaging-sw.js
importScripts("https://www.gstatic.com/firebasejs/9.23.0/firebase-app-compat.js");
importScripts("https://www.gstatic.com/firebasejs/9.23.0/firebase-messaging-compat.js");

firebase.initializeApp({
  apiKey: "你的API密钥",
  authDomain: "你的Auth域名",
  projectId: "你的项目ID",
  storageBucket: "你的存储桶",
  messagingSenderId: "你的消息发送ID",
  appId: "你的AppID"
});

const messaging = firebase.messaging();

messaging.onBackgroundMessage((payload) => {
  const notificationTitle = payload.notification.title;
  const notificationOptions = {
    body: payload.notification.body,
    icon: "/你的应用图标路径"
  };

  self.registration.showNotification(notificationTitle, notificationOptions);
});

关键注意事项

  • 权限申请:前端需先调用Notification.requestPermission()获取用户的通知权限,才能正常获取FCM令牌。
  • 令牌更新:监听FCM令牌变化事件,及时更新Firestore中的令牌存储,避免推送失败。
  • 无效令牌清理:推送失败时,可将无效令牌从users集合中移除,减少后续无效请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 19:26:28