Flutter Firebase项目:开发每月27日触发的Node.js定时Cloud Function
实现每月自动生成订阅账单并发送通知的Firebase云函数
准备工作
- 确保你的Firebase项目已启用Cloud Functions、Firestore、**Cloud Messaging(FCM)**服务
- 安装所需依赖:
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
相关产品推荐
相关产品推荐

