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

如何借助Cloud Tasks实现Firebase呼叫的定时通知调度与终止?

解决方案:用Cloud Tasks实现定时通知+状态触发终止

核心思路

放弃长驻定时器方案,改用单次延迟任务+循环调度的模式:每次发送通知后检查呼叫状态,若仍为open则创建下一个2分钟后的任务;若状态变为covered,则停止创建新任务。同时监听呼叫状态变更,一旦转为covered就批量取消该呼叫对应的待执行任务。

步骤1:配置Cloud Tasks环境

  1. 在Firebase控制台开启Cloud Tasks服务
  2. 在Cloud Functions项目中安装依赖:
    npm install @google-cloud/tasks
    

步骤2:呼叫创建时启动首个延迟任务

编写数据库触发器,当新呼叫节点创建且状态为open时,立即生成第一个2分钟后执行的通知任务。

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const { CloudTasksClient } = require("@google-cloud/tasks");
const client = new CloudTasksClient();

// 数据库触发器:呼叫创建时初始化任务
exports.onCallCreated = functions.database.ref("/calls/{callId}")
  .onCreate(async (snapshot, context) => {
    const callData = snapshot.val();
    if (callData.status !== "open") return;

    const callId = context.params.callId;
    const project = process.env.GCLOUD_PROJECT;
    const location = "us-central1"; // 替换为你的Cloud Tasks区域
    const queue = "call-notification-queue"; // 提前在控制台创建该队列

    // 构建任务参数
    const task = {
      httpRequest: {
        httpMethod: "POST",
        url: `https://${location}-${project}.cloudfunctions.net/sendCallNotification`,
        body: Buffer.from(JSON.stringify({ callId })).toString("base64"),
        headers: { "Content-Type": "application/json" },
      },
      scheduleTime: { seconds: Date.now() / 1000 + 120 }, // 2分钟后执行
      name: client.taskPath(project, location, queue, `call-${callId}-${Date.now()}`),
    };

    await client.createTask({ parent: client.queuePath(project, location, queue), task });
    functions.logger.info(`初始化呼叫${callId}的通知任务`);
  });

步骤3:通知发送+循环调度逻辑

编写HTTP类型的Cloud Function,负责发送通知,之后检查呼叫状态,若仍为open则创建下一个延迟任务。

exports.sendCallNotification = functions.https.onRequest(async (req, res) => {
  try {
    const { callId } = req.body;
    const callRef = admin.database().ref(`/calls/${callId}`);
    const snapshot = await callRef.once("value");
    const callData = snapshot.val();

    // 状态非open则终止流程
    if (callData.status !== "open") {
      functions.logger.info(`呼叫${callId}状态变更,停止通知`);
      return res.status(200).send("终止通知");
    }

    // 执行通知发送逻辑(替换为你的FCM/短信等代码)
    await sendVolunteerNotification(callId);
    functions.logger.info(`已发送呼叫${callId}的通知`);

    // 创建下一个2分钟后的任务
    const project = process.env.GCLOUD_PROJECT;
    const location = "us-central1";
    const queue = "call-notification-queue";
    const nextTask = {
      httpRequest: {
        httpMethod: "POST",
        url: `https://${location}-${project}.cloudfunctions.net/sendCallNotification`,
        body: Buffer.from(JSON.stringify({ callId })).toString("base64"),
        headers: { "Content-Type": "application/json" },
      },
      scheduleTime: { seconds: Date.now() / 1000 + 120 },
      name: client.taskPath(project, location, queue, `call-${callId}-${Date.now()}`),
    };
    await client.createTask({ parent: client.queuePath(project, location, queue), task: nextTask });

    res.status(200).send("通知发送完成,已调度下一次任务");
  } catch (error) {
    functions.logger.error("通知任务执行失败:", error);
    res.status(500).send("执行错误");
  }
});

// 示例:发送通知的辅助函数
async function sendVolunteerNotification(callId) {
  // 此处编写向志愿者推送通知的逻辑,比如查询志愿者列表、调用FCM API等
}

步骤4:状态变更时取消待执行任务

编写数据库触发器,当呼叫状态从open转为covered时,批量取消该呼叫对应的所有待执行任务。

exports.onCallStatusUpdated = functions.database.ref("/calls/{callId}")
  .onUpdate(async (change, context) => {
    const beforeStatus = change.before.val().status;
    const afterStatus = change.after.val().status;
    const callId = context.params.callId;

    if (beforeStatus === "open" && afterStatus === "covered") {
      const project = process.env.GCLOUD_PROJECT;
      const location = "us-central1";
      const queue = "call-notification-queue";

      // 筛选出该呼叫对应的所有待执行任务
      const [tasks] = await client.listTasks({
        parent: client.queuePath(project, location, queue),
        filter: `name:"call-${callId}"`,
      });

      // 逐个取消任务
      for (const task of tasks) {
        await client.deleteTask({ name: task.name });
        functions.logger.info(`已取消呼叫${callId}的待执行任务:${task.name}`);
      }
    }
  });

关键注意事项

  • 任务命名必须包含呼叫ID,确保后续能精准筛选并取消任务
  • Cloud Tasks队列需提前在控制台创建,可配置合适的重试策略(如失败后重试3次)
  • 确保Cloud Functions拥有Cloud Tasks的操作权限:Firebase部署的函数默认已配置权限,手动部署需添加roles/cloudtasks.enqueuer和roles/cloudtasks.deleter角色
  • 通知发送逻辑需做好错误捕获,避免因异常导致任务无限重试

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 08:22:50