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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 11:15:42