Firebase Cloud Function 编写参考及Firestore存储NaN数值问题排查
问题原因
- 文档数据读取错误:Firestore查询返回的文档快照需要调用
.data()方法才能获取字段数据,原代码直接读取.data属性(这是一个函数对象),所有取值都为undefined,参与运算后生成NaN。 - 异步逻辑未等待:所有Firestore的
get()操作都是异步任务,当前代码没有等待定价数据拉取完成就执行费用计算,cleaningPrice/discount/stay_Over/visiting这些变量在计算时还是undefined,和数字运算后直接生成NaN。 - Booking文档查询错误:获取更新的booking文档时硬编码了
.doc('{id}'),实际是在查询ID为字符串"{id}"的不存在文档,snap.data()返回空值,所有从booking取的字段都是undefined,参与运算后生成NaN。 - 异步操作无返回值:Cloud Function要求异步场景必须返回Promise,否则函数会在IO操作完成前被强制回收,逻辑执行不完整,写入数据异常。
- 天数计算逻辑错误:长住场景下用
startDate - endDate计算天数,得到的是负数,且如果日期是Firestore Timestamp类型未转毫秒数,运算结果异常。 - Payments查询逻辑错误:校验关联支付文档时用
.doc().get()是查询随机ID的空文档,没有按booking引用过滤,遍历逻辑完全不生效,还会重复写入大量无效支付文档。 - Booking引用对比逻辑错误:
booking_ref的对比用了集合对象拼接ID,对象转字符串后格式异常,永远匹配不到已存在的支付文档。 - 监听路径配置错误:原触发路径写为
{bookings}/{id}会监听所有根集合的更新事件,实际应该固定为bookings/{id}仅监听预订集合。
修正后代码
import * as functions from 'firebase-functions'; const admin = require('firebase-admin'); admin.initializeApp(); const db = admin.firestore(); exports.paymentCalcAmount = functions.firestore .document(`bookings/{id}`) .onUpdate(async (snapshots, context) => { const id = context.params.id; let cabinetVal = 0; let laundry = 0; let fridge = 0; let oven = 0; let window = 0; let wall = 0; let cleaningPrice: number; let discount: number; let stay_Over: number; let visiting: number; let houspitality_cut: number = 0; let service_provider_cut: number = 0; let TOTALHRS: number = 0; let TOTALPRICE: number = 0; let discountedPrice: number = 0; // 并行拉取定价和booking数据,等待两者都返回再执行计算 const [priceSnap, bookingSnap] = await Promise.all([ db.collection("prices").doc("price").get(), db.collection('bookings').doc(id).get() ]); // 读取定价数据 const priceData = priceSnap.data(); cleaningPrice = priceData.cleaning; discount = 100 - priceData.discount; stay_Over = priceData.stay_over; visiting = priceData.visiting; // 读取booking数据 const bookingData = bookingSnap.data(); const bookingRef = `bookings/${id}`; if (bookingData.isCleaning === true) { if (bookingData.isCabinet === true) cabinetVal = 1; if (bookingData.isLaundry === true) laundry = 1.5; if (bookingData.isFridge === true) fridge = 0.5; if (bookingData.isOven === true) oven = 0.5; if (bookingData.isWindow === true) window = 1; if (bookingData.isWall === true) wall = 1; TOTALHRS = cabinetVal + laundry + fridge + oven + window + wall; TOTALPRICE = TOTALHRS * cleaningPrice; houspitality_cut = (TOTALPRICE / 100) * 27; service_provider_cut = TOTALPRICE - houspitality_cut; if (discount === 100) { discountedPrice = (TOTALPRICE / 100) * discount; TOTALPRICE = discountedPrice; houspitality_cut = (TOTALPRICE / 100) * 27; service_provider_cut = TOTALPRICE - houspitality_cut; } } else { const startDate = bookingData.startDate.toMillis(); const endDate = bookingData.endDate.toMillis(); if (bookingData.stayOver === true) { const days = Math.round(Math.abs(endDate - startDate)/(1000*60*60*24)); TOTALPRICE = stay_Over * days; } else { const difference_in_days = Math.abs(endDate - startDate) / (1000 * 3600 * 24); TOTALPRICE = visiting * difference_in_days; } houspitality_cut = (TOTALPRICE / 100) * 27; service_provider_cut = TOTALPRICE - houspitality_cut; } // 查询关联的支付文档 const paymentSnap = await db.collection("payments").where('booking_ref', '==', bookingRef).limit(1).get(); const paymentData = { booking_ref: bookingRef, payment_total: TOTALPRICE, houspitality_cut: houspitality_cut, service_provider_cut: service_provider_cut, total_hours: TOTALHRS, }; if (paymentSnap.empty) { // 不存在则新建 return db.collection("payments").add(paymentData); } else { // 存在则更新 return paymentSnap.docs[0].ref.update(paymentData); } });
内容的提问来源于stack exchange,提问作者work90210
相关产品推荐
相关产品推荐

