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记录完整错误日志 } });
调试建议
- 查看Cloud Functions日志:检查是否有
⚠️开头的错误提示,定位具体是哪一步数据读取失败。 - 手动验证数据库数据:直接在Firebase控制台查看
/users/${owner}节点,确认token字段存在且是正确的FCM Token。 - 增加临时日志:在关键步骤打印读取到的数据(比如
console.log('用户数据:', ownerData)),确认数据和预期一致。
内容的提问来源于stack exchange,提问作者Ahmadiah
相关产品推荐
相关产品推荐

