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

Flutter/Firebase自定义定时云函数实现表单提交提醒方案问询

实现Firebase自定义定时云函数:表单连续3天每日提醒

针对你的需求,Firebase原生Pub/Sub调度不支持动态创建一次性定时任务,我们可以结合Firestore存储任务信息+固定间隔轮询调度函数的方案实现,具体步骤如下:

核心方案说明

当管理员创建表单时,在Firestore中生成3条对应未来3天指定时间的提醒任务记录;然后用一个低频率的调度函数(比如每分钟跑一次)轮询检查这些任务,到点后执行提醒逻辑,完成后标记任务已处理。

具体代码实现

1. 创建提醒任务的HTTPS可调用函数

管理员创建表单时调用此函数,生成连续3天的定时任务:

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

exports.createFormReminderTasks = functions.https.onCall(async (data, context) => {
  // 验证管理员权限(根据你的权限逻辑调整)
  if (!context.auth || !context.auth.token?.admin) {
    throw new functions.https.HttpsError('permission-denied', '仅管理员可创建提醒任务');
  }

  const { formId, ownerId, reminderTime } = data; // reminderTime格式如"09:00",注意时区统一
  const formRef = admin.firestore().collection('forms').doc(formId);
  const formDoc = await formRef.get();
  
  if (!formDoc.exists) {
    throw new functions.https.HttpsError('not-found', '表单不存在');
  }

  const formData = formDoc.data();
  if (!formData.isOpened) {
    throw new functions.https.HttpsError('failed-precondition', '表单未开启,无法创建提醒');
  }

  // 生成未来3天的定时任务
  const tasks = [];
  for (let i = 0; i < 3; i++) {
    const scheduledDate = new Date();
    scheduledDate.setDate(scheduledDate.getDate() + i);
    // 设置每日提醒时间
    const [hours, minutes] = reminderTime.split(':').map(Number);
    scheduledDate.setHours(hours, minutes, 0, 0);
    
    // 转换为Firestore时间戳(建议用UTC时间,避免时区问题)
    const scheduledTimestamp = admin.firestore.Timestamp.fromDate(scheduledDate);

    tasks.push({
      formId,
      ownerId,
      scheduledTime: scheduledTimestamp,
      executed: false,
      createdAt: admin.firestore.FieldValue.serverTimestamp()
    });
  }

  // 批量写入任务到Firestore
  const batch = admin.firestore().batch();
  const tasksCollection = admin.firestore().collection('form_reminder_tasks');
  tasks.forEach(task => {
    const docRef = tasksCollection.doc();
    batch.set(docRef, task);
  });

  await batch.commit();
  return { success: true, message: '3天提醒任务已创建' };
});

2. 轮询处理提醒任务的调度函数

此函数定期检查Firestore中的任务,到点后执行提醒逻辑:

exports.processFormReminders = functions.pubsub.schedule('every 1 minutes').onRun(async (context) => {
  const now = admin.firestore.Timestamp.now();
  const tasksCollection = admin.firestore().collection('form_reminder_tasks');
  
  // 查询未执行且已到时间的任务
  const querySnapshot = await tasksCollection
    .where('executed', '==', false)
    .where('scheduledTime', '<=', now)
    .get();

  if (querySnapshot.empty) {
    console.log('暂无待执行的提醒任务');
    return null;
  }

  const batch = admin.firestore().batch();
  const notificationPromises = [];

  for (const doc of querySnapshot.docs) {
    const task = doc.data();
    const formRef = admin.firestore().collection('forms').doc(task.formId);
    const formDoc = await formRef.get();

    // 表单不存在或已关闭,直接标记任务完成
    if (!formDoc.exists || !formDoc.data().isOpened) {
      batch.update(doc.ref, { executed: true });
      continue;
    }

    // 获取未提交表单的用户列表(根据你的业务逻辑实现)
    // 示例:假设表单存储了需提交的用户ID数组,submissions集合记录已提交用户
    const targetUsers = formDoc.data().users || [];
    const submissionsSnapshot = await admin.firestore().collection('form_submissions')
      .where('formId', '==', task.formId)
      .get();
    const submittedUserIds = submissionsSnapshot.docs.map(doc => doc.data().userId);
    const unsubmittedUserIds = targetUsers.filter(id => !submittedUserIds.includes(id));

    // 推送通知给未提交用户
    if (unsubmittedUserIds.length > 0) {
      const notifyPromise = admin.messaging().sendToTopic(task.ownerId, {
        notification: {
          title: '表单提交提醒',
          body: '请尽快完成表单提交',
          clickAction: 'FLUTTER_NOTIFICATION_CLICK',
          sound: 'default',
        },
        data: {
          formId: task.formId,
          type: 'form_reminder'
        }
      });
      notificationPromises.push(notifyPromise);
    }

    // 标记任务已执行
    batch.update(doc.ref, { executed: true });
  }

  // 等待所有通知发送完成,再提交批量更新
  await Promise.all(notificationPromises);
  await batch.commit();

  console.log(`完成${querySnapshot.size}个提醒任务处理`);
  return null;
});

关键注意事项

  • 时区统一:建议所有时间处理使用UTC,避免不同时区导致的触发时间偏差;前端传入reminderTime时需转换为UTC时间对应的时分。
  • 权限控制:务必在HTTPS函数中验证调用者身份,确保只有管理员能创建提醒任务。
  • 性能优化:如果任务量较大,可调整调度间隔(比如每5分钟一次),或对查询结果进行分页处理,避免单次处理过多任务。
  • 替代方案:若需更精准的动态定时,可使用Google Cloud Scheduler API直接创建一次性定时任务,但需要额外配置服务账号权限,实现复杂度更高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 20:09:25