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

Firebase Cloud Functions推送通知问题:实时数据库取FCM Token失败

排查Firebase Cloud Functions推送通知问题:从数据库获取Token失败的原因及修复方案

我来帮你一步步排查这个问题,结合你的代码和场景,主要有几个常见的坑需要注意:

1. 异步回调嵌套导致的函数提前终止

你当前的代码用了多层once('value', callback)的回调嵌套,这种写法没有正确返回Promise链。Firebase Cloud Functions需要你明确返回Promise来等待所有异步操作完成,否则函数可能在推送发送前就被终止了,这是最常见的原因。

2. 数据库路径或字段可能存在错误

  • 检查路径大小写和拼写:比如/users/${owner}和/Users Info/${property.downedBy},确认数据库中的节点名称完全匹配(注意users和Users Info的大小写差异)。
  • 验证Token字段有效性:确认ownerID.token确实存在,且是有效的FCM Token。可能存在字段名写错(比如实际是fcmToken而非token)或者该字段为空的情况。

3. 缺少完整的错误处理

当前代码只在sendToDevice环节加了catch,但前面的数据库读取操作如果出错,你看不到任何日志,根本不知道哪一步出了问题。

4. 服务账号权限问题

确保Cloud Functions使用的默认服务账号有读取实时数据库的权限,检查数据库安全规则是否允许访问/users、/Received Downs、/Users Info这些路径。


修复后的代码(用async/await优化)

我把你的代码重构为async/await风格,同时增加了完整的错误检查和日志输出,方便调试:

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

exports.sendPushNotification = functions.database.ref('Received Downs/{owner}/{propID}')
.onCreate(async (event) => {
  try {
    const owner = event.params.owner; 
    const propID = event.params.propID; 
    console.log('Owner: ' + owner + ' Property ID: ' + propID);

    // 读取目标用户的FCM Token
    const ownerSnapshot = await admin.database().ref(`/users/${owner}`).once('value');
    const ownerData = ownerSnapshot.val();
    if (!ownerData || !ownerData.token) {
      console.error(`⚠️ 未找到用户${owner}的FCM Token`);
      return null;
    }
    const targetToken = ownerData.token;

    // 读取请求关联的用户ID
    const downSnapshot = await admin.database().ref(`/Received Downs/${owner}/${propID}`).once('value');
    const downData = downSnapshot.val();
    if (!downData || !downData.downedBy) {
      console.error('⚠️ Received Downs条目缺少downedBy字段');
      return null;
    }
    const requesterID = downData.downedBy;

    // 读取请求发起者的名称
    const userSnapshot = await admin.database().ref(`/Users Info/${requesterID}`).once('value');
    const userData = userSnapshot.val();
    if (!userData || !userData.name) {
      console.error(`⚠️ 未找到用户${requesterID}的名称信息`);
      return null;
    }
    const requesterName = userData.name;

    // 构建推送内容
    const payload = {
      notification: {
        title: '你有新的关注请求!',
        body: `${requesterName} 关注了你`,
        sound: 'default'
      }
    };

    // 发送推送
    const response = await admin.messaging().sendToDevice(targetToken, payload);
    console.log("✅ 推送发送成功:", response);

    // 可选:清理无效的FCM Token(如果Token过期或无效)
    const cleanupTasks = [];
    response.results.forEach((result) => {
      if (result.error) {
        console.error('❌ 推送失败:', result.error);
        if (result.error.code === 'messaging/invalid-registration-token' ||
            result.error.code === 'messaging/registration-token-not-registered') {
          cleanupTasks.push(admin.database().ref(`/users/${owner}/token`).remove());
        }
      }
    });
    return Promise.all(cleanupTasks);
  } catch (error) {
    console.error('❌ 推送函数执行出错:', error);
    throw error; // 让Firebase记录完整错误日志
  }
});

调试建议

  1. 查看Cloud Functions日志:检查是否有⚠️开头的错误提示,定位具体是哪一步数据读取失败。
  2. 手动验证数据库数据:直接在Firebase控制台查看/users/${owner}节点,确认token字段存在且是正确的FCM Token。
  3. 增加临时日志:在关键步骤打印读取到的数据(比如console.log('用户数据:', ownerData)),确认数据和预期一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:57:48