我正在开发一个网络应用程序,用户可以在其中注册选修科目,并且每个选修科目的可用名额有限。我遇到一个问题,即多个用户可以同时注册,即使只有一个可用位置也是如此。
const handleApprove = async () => {
if (isEnrolled || isConfirming) {
return;
}
setIsConfirming(true);
const shouldEnroll = window.confirm("Are you sure you want to enroll?");
setIsConfirming(false);
if (shouldEnroll) {
try {
const firestore = firebase.firestore();
const electiveRef = firestore.collection("electives").doc(project.id);
const userRef = firestore.collection("users").doc(user.uid);
await firestore.runTransaction(async (transaction) => {
const electiveDoc = await transaction.get(electiveRef);
if (!electiveDoc.exists) {
throw new Error("Elective document does not exist!");
}
const electiveData = electiveDoc.data();
const updatedSlots = electiveData.slots - 1;
if (updatedSlots >= 0) {
const updatedElectiveDoc = await transaction.get(electiveRef);
const updatedElectiveData = updatedElectiveDoc.data();
const remainingSlots = updatedElectiveData.slots;
if (remainingSlots >= 0) {
transaction.update(electiveRef, { slots: updatedSlots });
await userRef.update({
isEnroll: true,
elective: project.name,
subjectCode: project.details,
});
logout();
setIsEnrolled(true);
} else {
alert("No available slots for enrollment in this elective.");
setIsEnrolled(false); // Reset isEnrolled if enrollment fails
}
} else {
alert("No available slots for enrollment.");
setIsEnrolled(false); // Reset isEnrolled if there are no available slots
}
});
} catch (error) {
console.error("Error enrolling:", error);
alert("An error occurred while enrolling. Please try again.");
setIsEnrolled(false); // Reset isEnrolled if enrollment fails
}
}
};
当只有一个名额时,多个用户仍然可以注册,即使没有剩余名额,用户也可以注册。 我需要确保:
我预计,通过使用 Firestore 事务,当只有一个可用插槽时,只有一个用户能够注册,而当没有可用插槽时,将阻止用户注册。我查了网上的解决方案,每个帖子都只是告诉我实现交易。
我建议在交易中少做一些事情。将其限制为仅处理注册逻辑。
您可以从交易中传递出一个返回值,以让您了解其状态并在结果之后执行所有失败/成功的注册逻辑。
此外,使用事务更新用户文档,以便在事务失败/重试时回滚更新的字段。
尝试这样的事情
const didEnroll = await firestore.runTransaction(async (transaction) => {
const electiveDoc = await transaction.get(electiveRef);
if (!electiveDoc.exists) {
throw new Error("Elective document does not exist!");
}
const electiveData = electiveDoc.data();
if (electiveData.slots == 0) {
// Do all the other failed to enroll actions after returning this
return false;
}
// Only do things if there are available slots.
// Let the transaction just end with no change if nothing to be done
transaction.update(electiveRef, {
slots: firestore.FieldValue.increment(-1), // Decrement the counter directly
});
// Update with transaction so its values are rolling back on retry
await transaction.update(userRef, {
isEnroll: true,
elective: project.name,
subjectCode: project.details,
});
return true;
});