一张工单为什么被统计了三次?一次重复评价问题的排查
下面这篇依据实际复现结果整理,供你审阅后手动发布,文末保留了 AI 辅助说明。
一张工单为什么被统计了三次?一次重复评价问题的排查
在检查一个开源校园报修系统时,发现了一个容易被忽略的问题:学生对同一张工单重复提交评价,工人名下的订单计数也会重复增加。
如果只看接口返回,这几次请求都显示“评价成功”。订单表里也没有多出记录。异常发生在另一处:工人的累计订单数和平均评分被反复更新了。
这类问题值得留意,因为用户重复点击、网络请求重试,都可能触发同一项业务操作多次执行。
本次检查针对 fix-system-agent 项目中的 mock_backend.py,文件最后修改提交为 7c574faddd6776465666d4dcf967318ba0238574。验证采用临时 SQLite 数据库和合成数据,没有连接线上服务,也没有使用真实校园数据。
从一张已完成的工单开始
为便于观察,测试数据只保留一名工人和一张已完成的工单。工人的初始累计订单数设为 0,随后对同一张工单依次执行三次评价:
-
提交 8 分评价。
-
原样再提交一次 8 分评价。
-
将同一张工单的评价改为 4 分。
测试直接抽取原代码中的 student_order_end 异步处理函数,用测试请求对象提供表单数据,并将数据库连接指向临时数据库。这样可以观察原函数的数据库更新行为,不必启动整套应用。
实际结果是:
第一次评 8 分:工单表只有 1 条记录,工人订单数为 1,平均分为 8.0
原样再次提交:工单表仍是 1 条记录,工人订单数变为 2,平均分为 8.0
将评分改为 4 分:工单表仍是 1 条记录,工人订单数变为 3,平均分为 6.7
三次调用都返回了“评价成功”。
第二次提交尤其容易漏检:平均分仍是 8 分,界面上可能看不出明显变化,但订单计数已经多加了一次。到第三次修改评分时,平均分的异常才显现出来。
更新了一张订单,却又新增了一次统计贡献
该处理函数先更新订单记录中的评分和评价内容,随后读取工人的累计评分信息,执行类似下面的计算:
total = old_average * old_count + grade
new_count = old_count + 1
new_average = round(total / new_count, 1)
这套计算把每次请求都视为一笔新的评分贡献。
首次评价时,这种累加方式可以得到预期结果。相同请求再次到达时,它却没有判断这张工单是否已经评价过,仍然增加计数。修改评价时,旧评分也没有被替换,而是留在累计结果中,新评分再次参与计算。
因此,第三次提交得到的实际上是:
(8 + 8 + 4) ÷ 3 ≈ 6.7
数据库中始终只有一张工单,统计计算却已经累计了三次。
修复前,先明确评价规则
处理这个问题,需要先决定:用户是否可以修改已经提交的评价。
如果评价只允许提交一次,后端应保存明确的已评价状态,重复请求不能再次增加统计值。
如果允许修改评价,就需要替换这张工单原有的评分贡献。例如,同一张工单从 8 分改为 4 分,订单计数应保持不变,平均分则根据修改后的有效评分重新计算。
这里还有一个细节:项目中的订单初始评分默认是 10 分。因此,不能简单地把“评分是否等于 10”作为是否评价过的判断依据。用户第一次就打 10 分,也是合法情况。未评价和已评价需要有明确的区分。
对于这个使用 SQLite 的演示项目,可以评估从有效评价记录重新聚合统计的方式,减少累计计数与订单记录之间的不一致。具体实现仍要结合字段含义决定:如果“完成订单数”和“有效评价数”含义不同,就应分别统计。
订单评价与工人统计的更新,也应放在同一个事务中,避免只更新成功一部分。
回归测试应该覆盖重复请求
这次问题提示,接口测试不能只检查是否返回成功,还要检查操作之后的数据是否一致。
至少应覆盖以下情况:
-
首次评价,评分和统计正确更新。
-
相同评价重复提交,计数与平均分保持不变。
-
修改已有评价,按业务规则更新或拒绝。
-
首次提交满分,仍能正确识别为已评价。
-
数据更新中途失败,订单评价与工人统计一起回滚。
其中前三种情况已在本次隔离测试中复现出当前行为,其余是建议补充的回归用例。本文没有验证 HTTP 链路、前端交互或并发请求,也没有实现和验证修复补丁。
相关问题已提交至项目的 Issue #26,供维护者确认和处理。
一次请求返回成功,只说明这次调用走到了成功分支。对于评价、计数这类会影响累计结果的操作,还需要继续检查:同一项业务操作再执行一次,数据是否仍然正确。
- 点赞
- 收藏
- 关注作者
评论(0)