React+Firebase生日APP:如何实现每日FCM个性化生日推送?
问题描述
我正在开发一款基于React Js、Firebase的生日Web应用,支持Firebase登录功能,用户可添加好友生日并查看个性化生日列表。
我需要每日在特定时间通过Firebase Cloud Messaging(FCM)向所有用户发送个性化通知——通知内容要根据Firebase数据库中该用户当天的生日列表生成。试了几种方法都没成功,求可行的实现方案。
已尝试的方法
- 仅通过React前端实现通知
- 缺陷:必须保持应用处于开启状态才能触发,而且移动端的
visibilitychange事件无法正常工作。 - 代码示例:
let notification let interval document.addEventListener("visibilitychange", () => { if (document.visibilityState === "hidden") { let date = new Date() let dateNumber = date.getDate() let monthId = date.getMonth() + 1 let hours = date.getHours() let minutes = date.getMinutes() if (hours === 0) { let requiredNames = [] if (allBirthdays.length !== 0) { requiredNames = allBirthdays .filter((p) => p.monthId == monthId && p.day == dateNumber) .map((p) => { return `${p.name}`; }) } notification = new Notification(`Today's Birthdays(${dateNumber}-${monthId})`, { body: `${requiredNames.length !== 0 ? requiredNames : "No Birthdays"}`, icon: logo, tag: "Same Time" }) } interval = setInterval(() => { date = new Date() dateNumber = date.getDate() monthId = date.getMonth() + 1 hours = date.getHours() minutes = date.getMinutes() if (hours === 0 && minutes < 15) { let requiredNames = [] if (allBirthdays.length !== 0) { requiredNames = allBirthdays .filter((p) => p.monthId == monthId && p.day == dateNumber) .map((p) => { return `${p.name}`; }) } notification = new Notification(`Today's Birthdays(${dateNumber}-${monthId})`, { body: `${requiredNames.length !== 0 ? requiredNames : "No Birthdays"}`, icon: logo, tag: "Next Interval" }) } }, 900000); } else { if (interval) clearInterval(interval) if (notification) notification.close() } })
- 缺陷:必须保持应用处于开启状态才能触发,而且移动端的
- 查阅视频教程
- 缺陷:内容仅涉及FCM推送测试,没有覆盖实际落地的个性化定时推送逻辑。
可行实现方案
核心思路
用Firebase Cloud Functions + Firestore + FCM组合实现,前端负责注册FCM令牌并关联用户,后端通过定时任务处理个性化内容并推送。
步骤分解
前端:存储FCM令牌到Firestore
- 用户登录后,调用FCM的
getToken()获取推送令牌,将令牌与用户ID关联,存入Firestore的users集合(比如在用户文档中添加fcmTokens数组字段)。 - 监听令牌刷新事件,当FCM令牌更新时,及时同步更新Firestore中的数据,避免推送失败。
- 用户登录后,调用FCM的
Cloud Functions:编写定时触发的推送函数
- 使用Firebase的定时触发器(比如设置为每天凌晨0点触发),函数核心逻辑:
- 获取当前日期的日、月信息。
- 遍历所有用户,查询该用户的生日列表,筛选出当天过生日的好友。
- 为有当日生日的用户生成个性化通知内容。
- 调用FCM API向用户的令牌推送通知。
- 简化版代码示例:
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); // 每天凌晨0点触发(可配置时区) exports.sendDailyBirthdayNotifications = functions.pubsub.schedule('0 0 * * *') .timeZone('Asia/Shanghai') .onRun(async (context) => { const today = new Date(); const day = today.getDate(); const month = today.getMonth() + 1; const usersSnapshot = await admin.firestore().collection('users').get(); const promises = []; usersSnapshot.forEach(async (userDoc) => { const userId = userDoc.id; const fcmTokens = userDoc.data().fcmTokens; if (!fcmTokens || fcmTokens.length === 0) return; // 查询该用户当天的生日记录 const birthdaysSnapshot = await admin.firestore() .collection('users') .doc(userId) .collection('birthdays') .where('day', '==', day) .where('monthId', '==', month) .get(); const birthdayNames = birthdaysSnapshot.docs.map(doc => doc.data().name); if (birthdayNames.length === 0) return; // 构建通知 payload const payload = { notification: { title: `今日生日(${day}-${month})`, body: birthdayNames.join('、'), icon: '你的应用图标URL' }, tokens: fcmTokens }; // 批量推送通知 promises.push(admin.messaging().sendMulticast(payload)); }); return Promise.all(promises); });
- 使用Firebase的定时触发器(比如设置为每天凌晨0点触发),函数核心逻辑:
配置与部署
- 确保Firebase项目已启用Cloud Functions、Firestore、FCM服务。
- 安装Firebase CLI,本地运行
firebase deploy --only functions部署定时函数。 - 测试时可临时将触发器改为每分钟触发(
* * * * *),验证逻辑正常后改回目标时间。
注意事项
- 性能优化:用户量较大时,避免一次性遍历所有用户,可分批处理;或提前在Firestore中建立按生日日期索引的集合,减少查询耗时。
- 错误处理:捕获FCM推送失败的令牌,从Firestore中移除无效令牌,避免后续无效推送。
- 时区配置:定时触发器的时区要匹配用户所在时区,确保通知在正确时间发送。
内容的提问来源于stack exchange,提问作者AS Tn
相关产品推荐
相关产品推荐

