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

如何在Flutter中通过Firebase Functions实现一对一推送通知

修正Firebase云函数实现一对一用户推送通知

需求:实现一对一用户推送通知,但当前代码会推送给所有用户。

Firestore数据结构:
connect(集合)→userID(文档)→userconnect(集合)→PetID(文档)→message(集合)→messageID(文档)
其中message文档的uid字段为接收者UID,email字段为发送者邮箱。

原代码:

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

exports.notifyUserOnMessage = functions.firestore.document('connect/{userId}/userconnect/{petId}/message/{messageId}')
    .onCreate(async (snapshot, context) => {
        const messageData = snapshot.data();

        // Extract the userId and petId from the context
        const targetUserId = context.params.userId;
        const petId = context.params.petId;

        // Fetch the userconnect document to get the userid field
        const userConnectDoc = await admin.firestore().doc(`connect/${targetUserId}/userconnect/${petId}`).get();
        const userConnectData = userConnectDoc.data();

        let recipientUserId;

        if (messageData.uid === userConnectData.userid) {
            recipientUserId = targetUserId;
        } else {
            recipientUserId = userConnectData.userid;
        }

        const payload = {
            notification: {
                title: 'You Have New Message',
                body: 'New Message Arrive.',
                clickAction: 'FLUTTER_NOTIFICATION_CLICK',
            },
            data: {
                click_action: 'FLUTTER_NOTIFICATION_CLICK', // for background
            }
        };


        return admin.messaging().sendToTopic(recipientUserId, payload);
    });

关键问题分析

当前代码使用sendToTopic(recipientUserId)进行推送,这是主题推送逻辑——所有订阅了以recipientUserId命名主题的设备都会收到通知,而非定向到单个用户。要实现一对一推送,需要使用FCM设备令牌进行精准推送,前提是你的Firestore中已存储用户的FCM令牌(建议在用户文档下添加fcmTokens数组字段,存储该用户所有设备的令牌)。

修改后的代码

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

exports.notifyUserOnMessage = functions.firestore.document('connect/{userId}/userconnect/{petId}/message/{messageId}')
    .onCreate(async (snapshot, context) => {
        const messageData = snapshot.data();
        const targetUserId = context.params.userId;
        const petId = context.params.petId;

        // 获取userconnect文档数据
        const userConnectDoc = await admin.firestore().doc(`connect/${targetUserId}/userconnect/${petId}`).get();
        const userConnectData = userConnectDoc.data();

        // 确定接收者UID
        let recipientUserId;
        if (messageData.uid === userConnectData.userid) {
            recipientUserId = targetUserId;
        } else {
            recipientUserId = userConnectData.userid;
        }

        // 获取接收者的FCM令牌(假设用户令牌存储在connect集合下的userID文档的fcmTokens字段中)
        const recipientUserDoc = await admin.firestore().doc(`connect/${recipientUserId}`).get();
        const recipientUserData = recipientUserDoc.data();
        
        // 检查令牌是否存在
        if (!recipientUserData || !recipientUserData.fcmTokens || recipientUserData.fcmTokens.length === 0) {
            console.log(`用户 ${recipientUserId} 未存储FCM令牌,无法推送通知`);
            return null;
        }

        const payload = {
            notification: {
                title: '你有新消息',
                body: '收到一条新消息',
                clickAction: 'FLUTTER_NOTIFICATION_CLICK',
            },
            data: {
                click_action: 'FLUTTER_NOTIFICATION_CLICK',
            }
        };

        // 使用sendToDevice推送给指定用户的所有设备
        return admin.messaging().sendToDevice(recipientUserData.fcmTokens, payload)
            .then(response => {
                // 清理无效令牌
                const invalidTokens = [];
                response.results.forEach((result, index) => {
                    const error = result.error;
                    if (error) {
                        console.error(`推送令牌 ${recipientUserData.fcmTokens[index]} 失败:`, error);
                        if (error.code === 'messaging/invalid-registration-token' || 
                            error.code === 'messaging/registration-token-not-registered') {
                            invalidTokens.push(recipientUserData.fcmTokens[index]);
                        }
                    }
                });
                // 如果有无效令牌,从Firestore中移除
                if (invalidTokens.length > 0) {
                    return admin.firestore().doc(`connect/${recipientUserId}`).update({
                        fcmTokens: admin.firestore.FieldValue.arrayRemove(...invalidTokens)
                    });
                }
                return null;
            })
            .catch(error => {
                console.error('推送通知失败:', error);
            });
    });

核心改动说明

  • 替换sendToTopic为sendToDevice:该方法接收设备令牌数组,仅向这些设备推送通知,实现一对一精准推送
  • 新增FCM令牌获取逻辑:从用户文档中读取存储的令牌数组,确保能定位到目标用户的设备
  • 添加无效令牌处理:自动清理无法接收通知的无效令牌,避免后续推送报错
  • 优化通知内容:将英文提示改为中文(可根据需求调整)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 13:43:17