144 lines
5.6 KiB
Go
144 lines
5.6 KiB
Go
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)
|
||
}
|
||
}
|