Firebase Functions实现关注推送通知:现有代码需补充什么?
实现关注推送通知的补充内容
1. 修正触发器类型
当前代码用onCreate触发器,仅会在用户文档首次创建时触发,但关注操作是更新已有用户文档的Followers数组,需替换为onUpdate触发器,同时对比更新前后的Followers数组,筛选出新增的关注者:
exports.sendPushNotification = functions.firestore.document('/users/{uid}').onUpdate((change, context) => { const beforeData = change.before.data(); // 更新前的数据 const afterData = change.after.data(); // 更新后的数据 const oldFollowers = beforeData.Followers || []; const newFollowers = afterData.Followers || []; // 找出新增的关注者(差集) const addedFollowers = newFollowers.filter(follower => !oldFollowers.includes(follower)); if (addedFollowers.length === 0) { return null; // 无新增关注,直接终止流程 } // 后续逻辑写在此处 });
2. 校验并获取被关注者的FCM令牌
需确保被关注用户的fcmTokens字段存在,若为多设备登录场景,该字段应为数组(sendToDevice支持传入数组):
const targetUserTokens = afterData.fcmTokens; if (!targetUserTokens || targetUserTokens.length === 0) { return null; // 无可用FCM令牌,无法推送通知 }
3. 构建含关注者信息的通知内容
假设Followers数组存储的是关注者UID,需从Firestore批量获取这些用户的信息(如昵称),生成对应通知内容:
// 批量获取新增关注者的用户数据 const followerDocs = await admin.firestore().collection('users').where(admin.firestore.FieldPath.documentId(), 'in', addedFollowers).get(); let notificationBody = ''; if (addedFollowers.length === 1) { const followerData = followerDocs.docs[0].data(); notificationBody = `${followerData.nickname || '某用户'}关注了你`; } else { notificationBody = `有${addedFollowers.length}位用户关注了你`; } const payload = { notification: { title: '新的关注', body: notificationBody, click_action: 'FLUTTER_NOTIFICATION_CLICK' // 根据APP平台调整点击跳转逻辑 }, // 可选:添加数据字段用于APP内业务处理 data: { type: 'follow', followerIds: JSON.stringify(addedFollowers) } };
4. 处理异步操作与错误
因涉及Firestore查询,需将函数改为异步函数,并添加错误捕获与无效令牌清理逻辑:
exports.sendPushNotification = functions.firestore.document('/users/{uid}').onUpdate(async (change, context) => { // 上述逻辑整合在此处,注意添加await关键字 try { // 执行推送 const response = await admin.messaging().sendToDevice(targetUserTokens, payload); // 清理无效FCM令牌 const failedTokens = []; response.results.forEach((result, index) => { const error = result.error; if (error) { functions.logger.error('推送失败的令牌:', targetUserTokens[index], error); if (error.code === 'messaging/invalid-registration-token' || error.code === 'messaging/registration-token-not-registered') { failedTokens.push(targetUserTokens[index]); } } }); if (failedTokens.length > 0) { const validTokens = targetUserTokens.filter(token => !failedTokens.includes(token)); await change.after.ref.update({ fcmTokens: validTokens }); } return null; } catch (error) { functions.logger.error('推送通知出错:', error); return null; } });
完整修正代码
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); exports.sendPushNotification = functions.firestore.document('/users/{uid}').onUpdate(async (change, context) => { const beforeData = change.before.data(); const afterData = change.after.data(); // 获取前后关注者数组,处理空数组情况 const oldFollowers = beforeData.Followers || []; const newFollowers = afterData.Followers || []; // 筛选新增关注者 const addedFollowers = newFollowers.filter(follower => !oldFollowers.includes(follower)); if (addedFollowers.length === 0) { functions.logger.info('无新增关注者,跳过推送'); return null; } // 获取被关注者FCM令牌 const targetUserTokens = afterData.fcmTokens; if (!targetUserTokens || targetUserTokens.length === 0) { functions.logger.info('被关注者无可用FCM令牌'); return null; } try { // 获取新增关注者信息 const followerQuery = await admin.firestore().collection('users') .where(admin.firestore.FieldPath.documentId(), 'in', addedFollowers) .get(); // 构建通知内容 let notificationBody; if (addedFollowers.length === 1) { const followerDoc = followerQuery.docs[0]; const followerNickname = followerDoc.data().nickname || '匿名用户'; notificationBody = `${followerNickname}关注了你`; } else { notificationBody = `有${addedFollowers.length}位用户关注了你`; } const payload = { notification: { title: '新关注提醒', body: notificationBody, click_action: 'FLUTTER_NOTIFICATION_CLICK' // 根据APP平台调整 }, data: { type: 'follow', followerIds: JSON.stringify(addedFollowers) } }; // 发送推送 const response = await admin.messaging().sendToDevice(targetUserTokens, payload); // 清理无效令牌 const failedTokens = []; response.results.forEach((result, index) => { const error = result.error; if (error) { functions.logger.error(`令牌${targetUserTokens[index]}推送失败:`, error); if (['messaging/invalid-registration-token', 'messaging/registration-token-not-registered'].includes(error.code)) { failedTokens.push(targetUserTokens[index]); } } }); if (failedTokens.length > 0) { const validTokens = targetUserTokens.filter(token => !failedTokens.includes(token)); await change.after.ref.update({ fcmTokens: validTokens }); functions.logger.info(`已移除${failedTokens.length}个无效FCM令牌`); } return null; } catch (error) { functions.logger.error('推送通知处理出错:', error); return null; } });
额外注意事项
- 确保用户集合中
Followers为数组类型,存储关注者UID字符串 - 若
fcmTokens为单个字符串,需调整为数组或适配sendToDevice参数格式 - 通知的
click_action需根据APP平台(Android/iOS/Flutter)调整,确保点击跳转逻辑正常
内容的提问来源于stack exchange,提问作者user19802992
相关产品推荐
相关产品推荐

