如何用Firestore事务或批量操作重构用户注册函数?
如何用Firestore Batch重构用户注册流程保证原子性
首先要明确:Firebase Auth的用户创建操作(createUserWithEmailAndPassword)不属于Firestore的操作范畴,因此无法被Firestore的Batch或Transaction包裹。要解决注册流程的原子性问题,需要结合Batch处理Firestore操作,同时在Firestore操作失败时回滚已创建的Auth用户。
你的尝试代码存在的问题
- 错误地在Transaction中嵌套使用Batch,两者无需同时使用;你的场景都是创建新文档,Batch足够满足需求。
- 未处理Auth用户创建成功但Firestore操作失败的情况,这会导致用户无法再次注册。
重构方案步骤
- 先创建Auth用户,若此步骤失败直接抛出错误,无需后续操作。
- 初始化Firestore Batch,将所有Firestore写操作(创建公司、创建用户元数据、设置用户管理员)统一加入Batch。
- 提交Batch:
- 提交成功:流程完成,返回结果。
- 提交失败:删除已创建的Auth用户,避免用户陷入无法注册的困境。
完整重构代码
假设setUserAsAdmin是往Firestore的adminUsers集合添加文档,我们将其整合到Batch中:
const registerUser = async (user: RegisterUserData) => { let authUser: User | null = null; try { // 1. 创建Auth用户 const userCredential = await createUserWithEmailAndPassword(auth, user.email, user.password); authUser = userCredential.user; const userId = authUser.uid; // 2. 初始化Firestore Batch const batch = writeBatch(db); // 2.1 创建公司文档,获取自动生成的ID const companyRef = doc(db, 'companies'); batch.set(companyRef, { title: user.companyName, userCount: 1 }); // 2.2 设置用户管理员(假设是往adminUsers集合写入) let isAdmin = false; const adminRef = doc(db, 'adminUsers', userId); batch.set(adminRef, { userId }, { merge: true }); isAdmin = true; // 2.3 创建用户元数据文档 const userMetaRef = doc(db, 'users', userId); // 建议用userId作为文档ID,便于后续查询 batch.set(userMetaRef, { email: user.email, userId: userId, firstName: user.firstName, lastName: user.lastName, isAdmin: isAdmin, role: isAdmin ? 'admin' : 'coach', clientCount: 0, companyId: companyRef.id // 直接获取公司文档的自动生成ID }); // 3. 提交Batch,所有Firestore操作原子执行 await batch.commit(); return userCredential; } catch (e) { // 若Auth用户已创建,但后续操作失败,删除Auth用户回滚 if (authUser) { await deleteUser(authUser); } throw new Error('注册用户失败: ' + e); } };
关键说明
- Batch创建新文档:使用
doc(db, 'collection')生成带自动ID的文档引用,通过batch.set()即可创建新文档,提交后可通过ref.id获取生成的ID。 - Auth操作回滚:在catch块中检查是否已创建Auth用户,若有则调用
deleteUser删除,避免用户因邮箱已占用无法再次注册。 - 整合独立Firestore操作:如果你的
setUserAsAdmin原本是独立的Firestore操作,必须将其逻辑加入Batch,否则该操作会脱离原子性保障。
内容的提问来源于stack exchange,提问作者envy
相关产品推荐
相关产品推荐

