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

如何让Cloud Function在Firestore重复写入时仅触发一次?

解决Firestore云函数触发两次推送通知的问题

解决这个问题有几种可行的方案,具体如下:

  • 方法一:通过路径参数与文档内容匹配过滤
    你的云函数监听/messages/{toUid}/{fromUid}/{id}路径,每次创建的两个文档中,只有接收方对应的文档会满足「路径参数toUid与文档内toId一致」的条件(发送方的文档中toUid是发送者ID,和toId不符)。在函数开头添加判断,仅处理符合条件的文档即可:

    修改后的代码片段:

    exports.messageNotificationsTrigger = functions
      .region('europe-west2') 
      .firestore
      .document('/messages/{toUid}/{fromUid}/{id}')
      .onCreate(async (snap, context) => {
        const db = admin.firestore();
        const data = snap.data();
        const toId = data.toId;
        
        // 新增判断:仅处理接收方的文档
        if (context.params.toUid !== toId) {
          return null; // 跳过发送方文档的触发
        }
    
        const message = data.text;
        const fromId = data.fromId;
        const fromUserName = data.fromUserName;
        const userReference = db.collection("tokens").doc(`${toId}`);
        const userToSend = await userReference.get();
    
        if (!userToSend.exists) {
          functions.logger.error("No such user", toId);
          return;
        } else {
          const userData = userToSend.data();
          const token = userData?.notificationToken;
    
          // Notification payload
          const payload = {
            notification: {
              title: fromUserName,
              body: message,
            },   
            data : {
              "mutable-content": "1"
            }
          };
          // 补充发送推送的逻辑,例如:await admin.messaging().sendToDevice(token, payload)
        }
      });
    
  • 方法二:给消息文档添加标记字段
    发送消息时,给接收方的文档添加isNotificationTarget: true标记,发送方的文档设为false。云函数触发时先检查该字段,仅处理标记为true的文档:

    发送消息的逻辑示例:

    // 发送方文档
    await db.collection('messages').doc(senderId).doc(receiverId).doc(messageId).set({
      text: message,
      toId: receiverId,
      fromId: senderId,
      fromUserName: senderName,
      isNotificationTarget: false
    });
    // 接收方文档
    await db.collection('messages').doc(receiverId).doc(senderId).doc(messageId).set({
      text: message,
      toId: receiverId,
      fromId: senderId,
      fromUserName: senderName,
      isNotificationTarget: true
    });
    

    云函数中的判断:

    exports.messageNotificationsTrigger = functions
      .region('europe-west2') 
      .firestore
      .document('/messages/{toUid}/{fromUid}/{id}')
      .onCreate(async (snap, context) => {
        const data = snap.data();
        
        // 检查标记字段,非目标文档直接跳过
        if (!data.isNotificationTarget) {
          return null;
        }
    
        // 后续原有逻辑...
      });
    
  • 方法三:优化消息存储结构(长期推荐)
    双向存储相同消息会造成数据冗余,可改为单份存储:将消息存在messages/{messageId},再在每个用户的userMessages/{userId}集合中存储消息的引用或元数据(如messageId、senderId、timestamp)。此时云函数只需监听userMessages中接收方的文档创建事件,从根源避免重复触发。不过该方案需要调整前后端的消息读写逻辑,适合重构时采用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 19:30:25