基于Firebase实时数据库的事件推送通知实现方案咨询
实现Firebase实时数据库事件推送通知的两种场景方案
我来帮你梳理这两个场景的具体实现方案,都是基于Firebase生态工具完成的:
1. 发送即将到来事件的推送通知
这个场景的核心是主动查询符合条件的事件,再调用FCM发送通知,步骤如下:
前置准备
首先确保完成基础配置:
- 客户端集成Firebase Cloud Messaging(FCM),获取用户的设备令牌(
fcmToken),并将令牌存储到实时数据库的用户节点下(比如users/{userId}/fcmToken) - 服务端(或Cloud Function)初始化Firebase Admin SDK,拥有访问实时数据库和发送FCM通知的权限
具体实现步骤
步骤1:查询即将到来的事件
从实时数据库中筛选出「未来特定时间内(比如1小时后)」的事件,同时关联对应的用户ID。假设你的事件节点结构是events/{eventId},包含timestamp(事件时间戳)、userId(关联用户ID)、title(事件标题)等字段,查询代码示例(Node.js):const now = Date.now(); const oneHourLater = now + 60 * 60 * 1000; // 1小时后的时间戳 const upcomingEventsSnapshot = await admin.database() .ref('events') .orderByChild('timestamp') .startAt(now) .endAt(oneHourLater) .once('value');步骤2:构造FCM通知内容
针对每个事件,构造适合的通知payload,包含标题、正文和跳转参数:const event = upcomingEventsSnapshot.val()[eventId]; const timeLeft = Math.floor((event.timestamp - now) / (60 * 1000)); // 剩余分钟数 const payload = { notification: { title: '事件即将开始', body: `你的「${event.title}」事件将在${timeLeft}分钟后开始`, clickAction: 'OPEN_EVENT_DETAIL' // 客户端点击通知后跳转的动作,根据平台调整 }, data: { eventId: eventId, eventTime: event.timestamp.toString() } };步骤3:调用FCM发送通知
根据事件关联的用户ID,从数据库中取出对应的fcmToken,然后发送通知:const userSnapshot = await admin.database().ref(`users/${event.userId}`).once('value'); const fcmToken = userSnapshot.val()?.fcmToken; if (fcmToken) { try { await admin.messaging().sendToDevice(fcmToken, payload); console.log(`已向用户${event.userId}发送事件提醒`); } catch (error) { console.error(`发送失败:`, error); // 如果令牌无效,可以考虑从数据库中移除该令牌 if (error.code === 'messaging/invalid-registration-token') { await admin.database().ref(`users/${event.userId}/fcmToken`).remove(); } } }
2. 自动触发明日即将到来事件的推送通知
这个场景需要定时检查数据库中的明日事件,并自动发送通知,我们可以用Firebase Cloud Functions的定时触发器来实现:
具体实现步骤
- 步骤1:创建定时触发的Cloud Function
编写一个每天固定时间(比如晚上8点)运行的函数,用来检查第二天的事件。Node.js代码示例:const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); // 每天晚上8点(上海时区)触发,UTC时间为12点 exports.sendTomorrowEventReminders = functions.pubsub.schedule('0 12 * * *') .timeZone('Asia/Shanghai') .onRun(async (context) => { // 计算明天的起始和结束时间戳(毫秒) const tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); const startOfTomorrow = new Date(tomorrow.getFullYear(), tomorrow.getMonth(), tomorrow.getDate(), 0, 0, 0).getTime(); const endOfTomorrow = new Date(tomorrow.getFullYear(), tomorrow.getMonth(), tomorrow.getDate(), 23, 59, 59).getTime(); // 查询明日的所有事件 const tomorrowEventsSnapshot = await admin.database() .ref('events') .orderByChild('timestamp') .startAt(startOfTomorrow) .endAt(endOfTomorrow) .once('value'); const events = tomorrowEventsSnapshot.val(); if (!events) { functions.logger.info('没有明日的事件,无需发送通知'); return null; } // 按用户分组事件,避免同一用户收到多条重复通知 const userEventsMap = new Map(); for (const eventId in events) { const event = events[eventId]; const userId = event.userId; if (!userEventsMap.has(userId)) { userEventsMap.set(userId, []); } userEventsMap.get(userId).push(event); } // 遍历用户发送通知 for (const [userId, userEvents] of userEventsMap.entries()) { const userSnapshot = await admin.database().ref(`users/${userId}`).once('value'); const fcmToken = userSnapshot.val()?.fcmToken; if (!fcmToken) continue; // 构造通知内容,列出用户明日的所有事件 const eventTitles = userEvents.map(e => e.title).join('、'); const payload = { notification: { title: '明日事件提醒', body: `你明天有${userEvents.length}个事件:${eventTitles}`, clickAction: 'OPEN_TOMORROW_EVENTS' }, data: { eventCount: userEvents.length.toString(), eventIds: userEvents.map(e => e.id).join(',') } }; try { await admin.messaging().sendToDevice(fcmToken, payload); functions.logger.info(`已向用户${userId}发送明日事件通知`); } catch (error) { functions.logger.error(`发送通知失败:`, error); // 处理无效令牌 if (error.code === 'messaging/invalid-registration-token') { await admin.database().ref(`users/${userId}/fcmToken`).remove(); } } } return null; });
关键注意事项
- 时区设置:一定要给定时触发器指定正确的时区,避免检查时间错误(比如示例中用的
Asia/Shanghai) - 令牌有效性:FCM令牌可能会过期或失效,发送失败时要及时从数据库中清理,避免重复无效调用
- 数据库结构适配:如果你的事件或用户节点结构和示例不同,需要调整查询和数据提取的逻辑
- 权限配置:确保Cloud Function的服务账号拥有
firebase.database.read和firebase.messaging.send的权限(默认Admin SDK初始化后会自动拥有这些权限)
内容的提问来源于stack exchange,提问作者sudhir
相关产品推荐
相关产品推荐

