Firebase Cloud Functions实现定时单次FCM推送通知问题求助
解决方案:实现土耳其时区提醒的精准推送与去重
核心思路
要解决你的问题,关键要搞定时区统一计算、时间达标判断和推送状态标记三个环节,以下是具体实现步骤和代码示例:
1. 统一时区处理
云函数默认使用UTC时区,而你的用户是UTC+3(土耳其)时区,所以需要将当前时间转换为土耳其时区后,再和用户设置的startat时间对比:
- 计算UTC到土耳其时区的毫秒偏移量:
3 * 60 * 60 * 1000 - 将当前UTC时间加上偏移量,得到土耳其本地时间
2. 时间达标判断
因为调度函数每分钟运行一次,我们只处理当前土耳其时间与提醒时间差在0~60秒内的记录,避免因调度延迟导致重复触发。
3. 标记已推送避免重复
在实时数据库的提醒记录中新增notified字段,初始为null或不设置;推送成功后将其设为true,后续调度函数只处理未标记的记录。
完整代码示例
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); // 每分钟运行的调度函数 exports.checkReminders = functions.pubsub.schedule("every 1 minutes").onRun(async (context) => { const db = admin.database(); // 读取所有未推送的提醒记录 const remindersSnapshot = await db.ref("reminders") .orderByChild("notified") .equalTo(null) .once("value"); if (!remindersSnapshot.exists()) { functions.logger.info("无待处理提醒"); return null; } // 计算当前土耳其时区时间 const nowUTC = new Date(); const turkeyOffsetMs = 3 * 60 * 60 * 1000; // UTC+3 毫秒偏移量 const nowTurkey = new Date(nowUTC.getTime() + turkeyOffsetMs); const updateBatch = {}; // 遍历待处理提醒 await Promise.all(remindersSnapshot.forEach(async (childSnap) => { const reminder = childSnap.val(); const reminderId = childSnap.key; const remindTimeTurkey = new Date(reminder.startat); // 假设startat是土耳其时间的ISO字符串/时间戳 // 判断是否到达提醒时间(误差在1分钟内) const timeDiffMs = nowTurkey.getTime() - remindTimeTurkey.getTime(); if (timeDiffMs >= 0 && timeDiffMs < 60 * 1000) { // 构造FCM推送消息 const fcmMessage = { notification: { title: "你的提醒到了", body: reminder.content || "预设的提醒内容" }, token: reminder.fcmToken // 需确保每条记录存储用户的FCM Token }; try { // 推送FCM通知 await admin.messaging().send(fcmMessage); functions.logger.info(`提醒推送成功:${reminderId}`); // 标记为已推送 updateBatch[`/reminders/${reminderId}/notified`] = true; } catch (err) { functions.logger.error(`推送失败 ${reminderId}:${err.message}`); // 可选:添加重试次数限制,避免无限重试 // updateBatch[`/reminders/${reminderId}/retryCount`] = (reminder.retryCount || 0) + 1; } } })); // 批量更新数据库状态,减少IO操作 if (Object.keys(updateBatch).length > 0) { await db.ref().update(updateBatch); } return null; });
注意事项
- 确保用户存储的
startat格式正确(推荐ISO字符串如"2024-05-20T15:30:00"或时间戳),避免Date解析错误。 - 如果可以,建议用户存储UTC时间戳而非本地时间,这样无需时区转换,直接用
nowUTC.getTime()对比即可,更稳妥。 - 若需处理推送失败的情况,可添加
retryCount字段,当重试次数超过阈值(如3次)时,标记为推送失败不再处理。
内容的提问来源于stack exchange,提问作者Berkay Isıkoglu
相关产品推荐
相关产品推荐

