如何使用 React 防止并发注册并处理 Firestore 数据库中的插槽可用性

问题描述 投票:0回答:1

我正在开发一个网络应用程序,用户可以在其中注册选修科目,并且每个选修科目的可用名额有限。我遇到一个问题,即多个用户可以同时注册,即使只有一个可用位置也是如此。

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
    }
  }
};

当只有一个名额时,多个用户仍然可以注册,即使没有剩余名额,用户也可以注册。 我需要确保:

  1. 当只有一个可用空位时,即使他们同时注册,也只有一名用户可以注册。
  2. 当没有可用空位时,用户将无法注册。

我预计,通过使用 Firestore 事务,当只有一个可用插槽时,只有一个用户能够注册,而当没有可用插槽时,将阻止用户注册。我查了网上的解决方案,每个帖子都只是告诉我实现交易。

reactjs firebase google-cloud-firestore
1个回答
0
投票

我建议在交易中少做一些事情。将其限制为仅处理注册逻辑。

您可以从交易中传递出一个返回值,以让您了解其状态并在结果之后执行所有失败/成功的注册逻辑。

此外,使用事务更新用户文档,以便在事务失败/重试时回滚更新的字段。

尝试这样的事情

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; 
      });
© www.soinside.com 2019 - 2024. All rights reserved.