package payment import ( "testing" "time" "hfb_sys/backend/internal/model" "hfb_sys/backend/internal/modules/distribution" ) // TestUpdateOrderRefundStatusUsesCumulativeRefundedAmount 断言同一订单多笔退款时, // 单笔回调传入的金额不会把订单累计退款额写小,分销财务快照按合计口径重算。 func TestUpdateOrderRefundStatusUsesCumulativeRefundedAmount(t *testing.T) { db := setupPaymentTestDB(t) repo := NewRepository(db, nil, nil) now := time.Now() site := model.DistributionSite{ SiteCode: "cum_site", SiteName: "累计退款站", Domain: "cum.dist.com", Status: "active", DisplayAccountEnabled: true, } if err := db.Create(&site).Error; err != nil { t.Fatal(err) } rule := model.DistributionRuleVersion{ SiteID: &site.ID, Mode: distribution.ModeTwoPartyPlatformReceive, Version: 1, Status: "active", PlatformReceiveRatio: 35, DistributorSellRatio: 40, ServiceFeeValue: 0, ServiceFeeUnit: distribution.ServiceFeeUnitFixedAmount, PaymentFeeBPS: 0, EffectiveFrom: now.Add(-time.Hour), } if err := db.Create(&rule).Error; err != nil { t.Fatal(err) } order := model.RentalOrder{ OrderNo: "ORD-CUM-REFUND", ListingID: 1, AccountID: 1, OwnerID: 1, RenterID: 2, Status: "completed", RentAmountCent: 10000, OwnerRentAmountCent: 6000, DistributionSiteID: &site.ID, DistributionRuleVersionID: &rule.ID, } if err := db.Create(&order).Error; err != nil { t.Fatal(err) } if err := distribution.SnapshotPaidOrderTx(db, order.ID, 10000); err != nil { t.Fatal(err) } // 两笔已退款成功流水:300 + 700 = 1000。 refunds := []model.PaymentOrder{ {PaymentNo: "REF-CUM-1", OrderID: order.ID, OrderNo: order.OrderNo, UserID: order.RenterID, Provider: "mock", ThirdOrderID: "REF-CUM-1", AmountCent: 300, BizType: "admin_refund", Status: "refunded"}, {PaymentNo: "REF-CUM-2", OrderID: order.ID, OrderNo: order.OrderNo, UserID: order.RenterID, Provider: "mock", ThirdOrderID: "REF-CUM-2", AmountCent: 700, BizType: "rent_refund", Status: "refunded"}, } if err := db.Create(&refunds).Error; err != nil { t.Fatal(err) } // 只传其中一笔金额调用:累计口径应取两笔合计 1000。 if err := repo.updateOrderRefundStatus(t.Context(), order.ID, 300, "admin_refund"); err != nil { t.Fatal(err) } var saved model.RentalOrder if err := db.First(&saved, order.ID).Error; err != nil { t.Fatal(err) } if saved.RefundAmountCent != 1000 { t.Fatalf("order.RefundAmountCent = %d, want 1000(两笔退款合计)", saved.RefundAmountCent) } if saved.RefundStatus != "refunded" { t.Fatalf("order.RefundStatus = %s, want refunded", saved.RefundStatus) } var financial model.DistributionOrderFinancial if err := db.Where("order_id = ?", order.ID).First(&financial).Error; err != nil { t.Fatal(err) } if financial.RefundAmountCent != 1000 || financial.NetPaidAmountCent != 9000 || financial.SplitBaseAmountCent != 3000 { t.Fatalf("分销快照未按累计退款重算:refund=%d netPaid=%d splitBase=%d", financial.RefundAmountCent, financial.NetPaidAmountCent, financial.SplitBaseAmountCent) } var party model.DistributionOrderParty if err := db.Where("order_id = ? AND party_role = ?", order.ID, "distributor_sell").First(&party).Error; err != nil { t.Fatal(err) } if party.IncomeAmountCent != 1200 { t.Fatalf("分站份额 = %d, want 1200(利润 3000 的 40%%)", party.IncomeAmountCent) } // 退款结果查询重复回调不应把累计额写小。 if err := repo.updateOrderRefundStatus(t.Context(), order.ID, 300, "admin_refund"); err != nil { t.Fatal(err) } if err := db.First(&saved, order.ID).Error; err != nil { t.Fatal(err) } if saved.RefundAmountCent != 1000 { t.Fatalf("重复回调后 order.RefundAmountCent = %d, want 1000(单调不回退)", saved.RefundAmountCent) } } // TestMarkOrderRefundingAndFailedPreserveCumulativeRefund 断言已成功退款的订单 // 发生后续退款中或退款失败时,累计退款额不会被单笔处理金额冲减缩小。 func TestMarkOrderRefundingAndFailedPreserveCumulativeRefund(t *testing.T) { db := setupPaymentTestDB(t) repo := NewRepository(db, nil, nil) order := model.RentalOrder{ OrderNo: "ORD-REF-PRESERVE", ListingID: 1, AccountID: 1, OwnerID: 1, RenterID: 2, Status: "completed", RentAmountCent: 10000, OwnerRentAmountCent: 6000, } if err := db.Create(&order).Error; err != nil { t.Fatal(err) } // 先前已成功退款 5000。 prevRefund := model.PaymentOrder{ PaymentNo: "REF-PREV-1", OrderID: order.ID, OrderNo: order.OrderNo, UserID: order.RenterID, Provider: "mock", ThirdOrderID: "REF-PREV-1", AmountCent: 5000, BizType: "admin_refund", Status: "refunded", } if err := db.Create(&prevRefund).Error; err != nil { t.Fatal(err) } // 此时发起了另一笔 2000 的退款中:累计额应为 max(2000, 5000) = 5000。 if err := repo.markOrderRefunding(t.Context(), order.ID, 2000); err != nil { t.Fatal(err) } var saved model.RentalOrder if err := db.First(&saved, order.ID).Error; err != nil { t.Fatal(err) } if saved.RefundStatus != "refunding" || saved.RefundAmountCent != 5000 { t.Fatalf("markOrderRefunding 后状态 = %s/%d, want refunding/5000", saved.RefundStatus, saved.RefundAmountCent) } // 该笔 2000 退款最终失败:累计退款额还原为已成功流水合计 5000,而不是单笔 2000。 if err := repo.markOrderRefundFailed(t.Context(), order.ID, 2000); err != nil { t.Fatal(err) } if err := db.First(&saved, order.ID).Error; err != nil { t.Fatal(err) } if saved.RefundStatus != "failed" || saved.RefundAmountCent != 5000 { t.Fatalf("markOrderRefundFailed 后状态 = %s/%d, want failed/5000", saved.RefundStatus, saved.RefundAmountCent) } }