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

Flutter Firebase项目:开发每月27日触发的Node.js定时Cloud Function

实现每月自动生成订阅账单并发送通知的Firebase云函数

准备工作

  1. 确保你的Firebase项目已启用Cloud Functions、Firestore、**Cloud Messaging(FCM)**服务
  2. 安装所需依赖:
npm install firebase-functions firebase-admin nodemailer

核心实现代码

以下是完整的Node.js云函数代码,包含定时触发、订阅核查、账单创建、邮件及FCM通知功能:

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const nodemailer = require('nodemailer');

// 初始化Firebase Admin SDK
admin.initializeApp();

// 配置SMTP邮件发送器(替换为你的邮箱服务信息)
const emailTransporter = nodemailer.createTransport({
  service: 'Gmail',
  auth: {
    user: 'your-business-email@gmail.com',
    pass: 'your-app-specific-password' // Gmail需启用两步验证后生成应用密码
  }
});

// 定时触发:每月27日0点执行(时区可根据业务调整)
exports.generateMonthlyInvoices = functions.pubsub.schedule('0 0 0 27 * *')
  .timeZone('Asia/Shanghai')
  .onRun(async (context) => {
    try {
      // 1. 查询所有活跃状态的订阅
      const activeSubscriptions = await admin.firestore()
        .collection('subscription')
        .where('status', '==', 'active')
        .get();

      if (activeSubscriptions.empty) {
        functions.logger.info('当前无活跃订阅需要处理');
        return;
      }

      // 批量处理每个订阅
      await Promise.all(activeSubscriptions.docs.map(async (subDoc) => {
        const subData = subDoc.data();
        const userId = subData.userId;
        const invoiceAmount = subData.monthlyFee;
        const userEmail = subData.userEmail;

        // 2. 在invoices集合创建新账单文档
        const newInvoiceRef = await admin.firestore().collection('invoices').add({
          subscriptionId: subDoc.id,
          userId: userId,
          amount: invoiceAmount,
          issueDate: admin.firestore.Timestamp.now(),
          dueDate: admin.firestore.Timestamp.fromDate(
            new Date(new Date().setMonth(new Date().getMonth() + 1))
          ),
          status: 'unpaid',
          paymentLink: '' // 后续填充完整支付链接
        });
        const invoiceId = newInvoiceRef.id;

        // 生成带账单ID的支付链接
        const finalPaymentLink = `https://your-payment-platform.com/pay?invoice=${invoiceId}&user=${userId}`;

        // 更新账单文档的支付链接
        await newInvoiceRef.update({ paymentLink: finalPaymentLink });

        // 3. 获取用户FCM Token(假设users集合存储了token)
        const userDoc = await admin.firestore().collection('users').doc(userId).get();
        if (!userDoc.exists || !userDoc.data().fcmToken) {
          functions.logger.warn(`用户${userId}无有效FCM Token,跳过推送通知`);
        } else {
          // 发送FCM通知
          await admin.messaging().send({
            token: userDoc.data().fcmToken,
            notification: {
              title: '新订阅账单已生成',
              body: `您的月度账单金额为${invoiceAmount}元,请及时支付`
            },
            data: {
              paymentLink: finalPaymentLink,
              invoiceId: invoiceId
            }
          });
        }

        // 4. 发送账单邮件
        await emailTransporter.sendMail({
          from: '你的订阅服务 <your-business-email@gmail.com>',
          to: userEmail,
          subject: '您的月度订阅账单已生成',
          html: `
            <div style="max-width: 600px; margin: 0 auto;">
              <h3>您好,您的月度订阅账单已生成</h3>
              <p>账单详情:</p>
              <ul>
                <li>账单金额:${invoiceAmount}元</li>
                <li>到期日期:${new Date(new Date().setMonth(new Date().getMonth() + 1)).toLocaleDateString()}</li>
                <li>账单ID:${invoiceId}</li>
              </ul>
              <p><a href="${finalPaymentLink}" style="background: #2563eb; color: white; padding: 8px 16px; border-radius: 4px; text-decoration: none;">立即支付</a></p>
            </div>
          `
        });

        functions.logger.info(`账单${invoiceId}已生成并通知用户${userId}`);
      }));

      functions.logger.info('所有月度账单处理完成');
    } catch (err) {
      functions.logger.error('账单处理流程出错:', err);
      throw err;
    }
  });

关键注意事项

  • 时区配置:timeZone参数务必设置为业务所在时区,避免触发时间偏差
  • 邮件服务:如果不用Gmail,可替换为SendGrid、Mailgun等SMTP服务,调整emailTransporter的配置即可
  • FCM Token管理:FCM Token可能过期或失效,建议在代码中添加发送失败的重试逻辑,或记录失败任务后续手动处理
  • 权限控制:部署云函数时,Firebase会自动分配基础权限,若需访问其他资源(如第三方支付API),需额外配置IAM角色
  • 测试验证:可临时修改cron表达式为近未来时间(如'0 5 * * *'即每小时第5分钟执行),测试功能是否正常

部署命令

在Firebase项目根目录执行:

firebase deploy --only functions:generateMonthlyInvoices

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 08:00:30