事先感谢您的协助。
我正试图从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> }
-注意这是一个方法,而不是一个属性)。通过重写这个类型,通常会抛出错误的检查器就不知道该做什么了。
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>
最后,你使用的变量是在你的承诺链之外定义的。因为函数已经有了当前预订的数据,所以在函数检索cleaningPrice
、discount
等的数据之前执行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
)。