CLOUD FUNCTION -如何编写参考文档并修复NaN



事先感谢您的协助。

我正试图从firestore和文档更新时拉价格。然后,它需要遍历许多if语句来确定价格,然后创建一个名为payments的新文档,但是payments文档需要引用预订并循环查看是否已经创建了文档,如果有文档,则需要更新文档。

数字值在数据库中以NaN形式给出,我不明白为什么。

请参阅下面的代码:

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((snapshots, context) => {
const payments = admin.firestore().collection("payments");
const collection = admin.firestore().collection("bookings");
const id = context.params.id;
let startDate;
let endDate;
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;
db.collection("prices")
.doc("price")
.get()
.then(
(snapshot: {
data: {
cleaning: number;
discount: number;
stay_over: number;
visiting: number;
};
}) => {
cleaningPrice = snapshot.data.cleaning;
discount = 100 - snapshot.data.discount;
stay_Over = snapshot.data.stay_over;
visiting = snapshot.data.visiting;
}
);
db.collection('bookings')
.doc('{id}')
.get()
.then(
(snap: {
data: {
isCleaning: boolean;
isLaundry: boolean;
isFridge: boolean;
isOven: boolean;
isCabinet: boolean;
isWindow: boolean;
isWall: boolean;
stayOver: boolean;
startDate: any;
endDate: any;
};
}) => {
if (snap.data.isCleaning === true) {
if (snap.data.isCabinet === true) {
cabinetVal = 1;
}
if (snap.data.isLaundry === true) {
laundry = 1.5;
}
if (snap.data.isFridge === true) {
fridge = 0.5;
}
if (snap.data.isOven === true) {
oven = 0.5;
}
if (snap.data.isWindow === true) {
window = 1;
}
if (snap.data.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 {
if (snap.data.stayOver === true) {
startDate = snap.data.startDate;
endDate = snap.data.endDate;
const days = Math.round((startDate-endDate)/(1000*60*60*24));
TOTALPRICE = stay_Over * days;
houspitality_cut = (TOTALPRICE / 100) * 27;
service_provider_cut = TOTALPRICE - houspitality_cut;
} else {
startDate = snap.data.startDate;
endDate = snap.data.endDate;
const difference_in_time = endDate - startDate;
const difference_in_days = difference_in_time / (1000 * 3600 * 24);
TOTALPRICE = visiting * difference_in_days;
houspitality_cut = (TOTALPRICE / 100) * 27;
service_provider_cut = TOTALPRICE - houspitality_cut;
}
}
db.collection("payments")
.doc()
.get()
.then((Snapshot: { docs: any[] }) => {
Snapshot.docs.forEach((docs) => {
if (
docs.data.booking_ref === `${collection}${id}`
) {
return payments.update({
payment_total: TOTALPRICE,
houspitality_cut: houspitality_cut,
service_provider_cut: service_provider_cut,
total_hours: TOTALHRS,
});
} else {
return payments.add({
booking_ref: collection,id,
payment_total: TOTALPRICE,
houspitality_cut: houspitality_cut,
service_provider_cut: service_provider_cut,
total_hours: TOTALHRS,
});
}
});
});
}
);
});

在您当前的代码中,您重写了不应该重写的类型,并且不正确地使用了禁止的类型,例如any。TypeScript是为你处理类型的,Firebase SDK已经设置为开箱即用来支持TypeScript了。

开始时,您已经将函数配置为响应任何顶级集合(它将响应/users/someId/posts/someId等—包括/payments/someId!):

functions.firestore.document(`{bookings}/{id}`)

应:

functions.firestore.document(`bookings/{id}`)

接下来,您错误地传递了context的ID:

db.collection('bookings').doc('{id}')

应该是:

db.collection('bookings').doc(id)
db.collection('bookings').doc(context.params.id)
snapshots.after.ref

因为这个云函数传递了与这个事件相关的数据,所以你不需要再次获取它的数据:

const bookingSnapshot = await db.collection('bookings').doc(id).get()

可以用

代替
const bookingSnapshot = snapshots.after;

关于覆盖类型,这些行:

docRef
.get()
.then(
(snap: {
data: {
isCleaning: boolean;
isLaundry: boolean;
isFridge: boolean;
isOven: boolean;
isCabinet: boolean;
isWindow: boolean;
isWall: boolean;
stayOver: boolean;
startDate: any;
endDate: any;
};
}) => {

应该是:

docRef
.get()
.then((snap) => { // snap is already a DataSnapshot<DocumentData>

snap对象不是{ data: Record<string, any> },而是DataSnapshot<DocumentData>(看起来更接近{ data: () => Record<string, any> }-注意这是一个方法,而不是一个属性)。通过重写这个类型,通常会抛出错误的检查器就不知道该做什么了。

在你当前的覆盖下,TypeScript不会向你抛出错误,说snap.data.isCleaning总是undefined,snap.data.isLaundry总是undefined,等等

如果要定义此引用处数据的形状,请使用:

docRef
.withConverter(myDataConverter) // see https://googleapis.dev/nodejs/firestore/latest/global.html#FirestoreDataConverter
.get()
.then((snap) => {

docRef
.get()
.then((snap: admin.firestore.DataSnapshot<{
isCleaning: boolean;
isLaundry: boolean;
isFridge: boolean;
isOven: boolean;
isCabinet: boolean;
isWindow: boolean;
isWall: boolean;
stayOver: boolean;
startDate?: number; // <-- don't use any here
endDate?: number; // <-- don't use any here
}>) => {

因为您还覆盖了集合查询的类型,所以您也不会得到这些行的错误:

db.collection("payments")
.doc()
.get()
.then((snapshot: { docs: any[] }) => {

应该是(获取/payments集合中的所有文档):

db.collection("payments") // note: doc() was removed
.get()
.then((querySnapshot) => { // querySnapshot is already a QuerySnapshot<DocumentData>

最后,你使用的变量是在你的承诺链之外定义的。因为函数已经有了当前预订的数据,所以在函数检索cleaningPricediscount等的数据之前执行Promise链中的代码。这意味着你所有的计算最终都会像10 * undefined那样产生NaN。在执行计算之前,您应该确保拥有所需的所有数据。您可以使用Promise.all之类的Promise方法来实现这一点,或者切换到async/await语法。点击这里阅读。

作为示例如何重写函数来纠正这些问题(需要错误处理!):

exports.paymentCalcAmount = functions.firestore
.document(`bookings/{id}`)
.onUpdate(async (change, context) => {
const db = admin.firestore();

const bookingId = context.params.id;
const bookingData = change.after.data() as {
isCleaning: boolean;
isLaundry: boolean;
isFridge: boolean;
isOven: boolean;
isCabinet: boolean;
isWindow: boolean;
isWall: boolean;
stayOver: boolean;
startDate?: number;
endDate?: number;
};
const bookingPaymentRef = db
.collection("payments")
.doc(bookingId);

const pricingData = await db
.collection("prices")
.doc("price")
.get()
.then(snap => snap.data());

if (bookingData.isCleaning) {
const cleaningHourlyRate = pricingData.cleaning;
const cleaningRateMultiplier = (100 - (pricingData.discount || 0)) / 100;
let total_hours = 0;

if (bookingData.isCabinet) total_hours += 1;
if (bookingData.isLaundry) total_hours += 1.5;
if (bookingData.isFridge)  total_hours += 0.5;
if (bookingData.isOven)    total_hours += 0.5;
if (bookingData.isWindow)  total_hours += 1;
if (bookingData.isWall)    total_hours += 1;

const payment_total = cleaningHourlyRate * total_hours * cleaningRateMultiplier;
const houspitality_cut = payment_total * 0.27;
const service_provider_cut = payment_total - houspitality_cut;

await bookingPaymentRef
.set({
payment_total,
houspitality_cut,
service_provider_cut,
total_hours
}, { merge: true });

} else {
const stayOverDailyRate = pricingData.stayOver;
const visitingDailyRate = pricingData.visiting;
const deltaTime = bookingData.endDate - bookingData.startDate;
const deltaTimeInDays = deltaTime/(1000*60*60*24);

const payment_total = bookingData.stayOver
? stayOverDailyRate * Math.round(deltaTimeInDays)
: visitingDailyRate * deltaTimeInDays;

const houspitality_cut = payment_total * 0.27;
const service_provider_cut = payment_total - houspitality_cut;

await bookingPaymentRef
.set({
payment_total,
houspitality_cut,
service_provider_cut,
total_hours: 0,
}, { merge: true });
}

console.log(`Successfully updated booking #${id}`);
});

如果这些值被错误地存储为字符串(注意值周围的双引号),您首先必须解析为数字,以便计算它们。我的意思是,你期望计算是:1 + 1 = 2,但可能发生的是:"1" + "1" = "11"(其中结果是NaN)。

最新更新