fix(distribution): 结算幂等与退款口径加固,补关单释放与回归测试

- 结算明细加唯一键 (order_id, party_id):自动释放与手工批次并发时以唯一键冲突为准跳过该参与方,不再重复入账
- 自动释放先无锁筛出本站未入账参与方,收敛加锁范围;手工批次建批次后先锁周期内财务快照,与自动释放串行化
- 累计退款额改为「本次回调金额与已退款成功流水合计取较大值」,多笔退款与退款结果重复回调不再把口径写小
- 关单三路径(AdminClose / AdminRejectRefund 的 close 分支 / AdminSeal)补写 release_at,接线原死代码 EnsureCompletedReleaseAtTx
- 仲裁分销重算改用实际退款额(扣除押金暂扣),避免站点被多扣
- 订单列表与详情的提价分成取 min(收入, 货架加价),退款压缩加价后不再显示负数
- 新增迁移 000070(结算明细唯一键)、000071(补回填 closed 订单释放时间)
- 新增 5 个测试文件:结算幂等与展示钳制、结算 job 幂等、累计退款、关单补写释放时间、仲裁押金暂扣口径
This commit is contained in:
yml2213
2026-09-10 19:13:39 +08:00
parent e3a75fb2d7
commit 36ebe7a5f1
13 changed files with 1017 additions and 10 deletions
+14 -3
View File
@@ -421,11 +421,22 @@ func (r *Repository) updateOrderRefundStatus(ctx context.Context, orderID uint64
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil {
return err
}
// 同一订单可能多笔退款,且退款结果查询会重复回调:
// 以已退款成功流水合计为口径,并取本次金额与累计额的较大值,
// 保证 order.RefundAmountCent 单调不回退、重复回调幂等。
var refundedTotal int64
if err := tx.Model(&model.PaymentOrder{}).
Where("order_id = ? AND status = ?", orderID, "refunded").
Where("biz_type IN ?", refundBizTypes).
Select("COALESCE(SUM(amount_cent), 0)").Scan(&refundedTotal).Error; err != nil {
return err
}
cumulative := max(refundAmountCent, refundedTotal)
now := time.Now()
order.RefundStatus = "refunded"
order.RefundAmountCent = refundAmountCent
order.RefundAmountCent = cumulative
order.RefundedAt = &now
if bizType == "admin_refund" && order.Status == "completed" && refundAmountCent >= order.RentAmountCent+order.DepositAmountCent {
if bizType == "admin_refund" && order.Status == "completed" && cumulative >= order.RentAmountCent+order.DepositAmountCent {
if err := rentergrowth.RevokeOrderCompleted(tx, &order); err != nil {
return err
}
@@ -433,7 +444,7 @@ func (r *Repository) updateOrderRefundStatus(ctx context.Context, orderID uint64
if err := tx.Save(&order).Error; err != nil {
return err
}
return distribution.ApplyRefundTx(tx, orderID, refundAmountCent)
return distribution.ApplyRefundTx(tx, orderID, cumulative)
})
}
func (r *Repository) markOrderRefunding(ctx context.Context, orderID uint64, refundAmountCent int64) error {