Firebase Cloud Functions更新分数后用户节点被删除问题求助
问题分析与解决方案
你遇到的问题根源在于错误使用了Firebase的Transaction API:你在单个用户的节点引用userRef上调用了transaction,但这个事务的回调返回了一个Promise——而Firebase Transaction期望回调返回的是要写入当前引用的具体值(或者undefined来中止事务)。这个返回的Promise会被当作无效值处理,最终导致你的用户节点被覆盖为null,也就是被删除了。
而且你的逻辑里根本不需要对单个用户节点做事务,我们需要保护的是父节点上averageScore和userCount的原子更新,避免并发修改时出现计算误差。
修正后的代码
exports.updateAverageScore = functions.database.ref('/users/{userId}/score') .onWrite((change, context) => { // 处理节点创建或删除的边界情况 if (!change.before.exists() || !change.after.exists()) { // 新增用户的场景:同时更新userCount和averageScore if (!change.before.exists() && change.after.exists()) { const parentRef = change.after.ref.parent.parent; return parentRef.transaction((parentData) => { if (parentData) { const newCount = (parentData.userCount || 0) + 1; const newAverage = ((parentData.averageScore || 0) * (newCount - 1) + parseFloat(change.after.val().score)) / newCount; return { ...parentData, userCount: newCount, averageScore: parseFloat(newAverage.toFixed(2)) }; } // 父节点不存在时初始化数据 return { userCount: 1, averageScore: parseFloat(change.after.val().score) }; }); } // 如果需要处理用户删除的场景,可以在这里补充对应的逻辑 return null; } const beforeScore = parseFloat(change.before.val().score); const afterScore = parseFloat(change.after.val().score); const scoreDiff = afterScore - beforeScore; // 获取包含averageScore和userCount的父节点引用 const parentRef = change.after.ref.parent.parent; // 对父节点做事务,确保并发修改时的原子性 return parentRef.transaction((parentData) => { if (!parentData || !parentData.userCount) { // 异常情况:父节点不存在或userCount丢失,直接中止事务 return; } const currentCount = parentData.userCount; const currentAverage = parentData.averageScore || 0; // 计算新平均分:(原总分 + 分数变化) / 用户数 const newTotal = currentAverage * currentCount + scoreDiff; const newAverage = parseFloat((newTotal / currentCount).toFixed(2)); // 返回更新后的父节点数据,事务会自动写入 return { ...parentData, averageScore: newAverage }; }); });
关键修改点说明
- 移除了对
userRef的事务调用:这是导致用户节点被删除的直接原因,我们不需要修改单个用户的节点,完全没必要在这里调用事务。 - 转移事务到父节点:所有涉及
averageScore和userCount的修改都在父节点的事务中完成,保证并发更新时的原子性,避免多个用户同时修改分数导致的计算错误。 - 补充边界场景处理:原代码只处理了分数更新的情况,现在新增了用户创建时同步更新用户数和平均分的逻辑(如果需要处理用户删除,也可以在对应分支补充)。
- 简化计算逻辑:通过计算分数变化值
scoreDiff,避免重复计算总分,逻辑更清晰易维护。
额外注意事项
- 确保
userCount字段始终准确,它是平均分计算的核心基础,用户新增/删除时一定要同步更新这个字段。 - 用
parseFloat处理toFixed的结果,因为toFixed返回的是字符串,直接写入数据库会导致类型异常。
内容的提问来源于stack exchange,提问作者gdakram
相关产品推荐
相关产品推荐

