测试库不再使用 SQLite 内存库,改为每个测试包独立的 MySQL 库, 表结构由 backend/migrations 的真实迁移建立。 基础设施 - 新增 internal/database/testdb.go:NewTestDB() 保持原签名(28 处调用零改动), 内部连接 MySQL 测试库,首次调用重建库并执行全部真实迁移,之后每次调用清空全表 - 库名 = 测试库基础名 + 包目录名,同包共享、跨包隔离,使 go test ./... 可并发执行 (共用一库时各包会互相 DROP/CREATE 导致 database exists) - 新增 NewDryRunDB():MySQL 方言 + DryRun,供断言 SQL 形态的 5 处测试使用 - 新增 AutoMigrateIfMissing():只对不存在的表 AutoMigrate, 避免改动迁移定义的索引与约束 - 新增 scripts/prepare-test-db.sh:建库与通配授权,可重复执行 - 33 处 sqlite.Open 全部替换,26 个测试文件改用 MySQL - 删除 46 处手写 SQLite 方言 DDL(AUTOINCREMENT / INTEGER PRIMARY KEY / TEXT), 表统一由真实迁移建立 - go.mod 移除 gorm.io/driver/sqlite 与 mattn/go-sqlite3 - 删除生产代码里 distribution/repository.go 的 sqlite 方言分支(strftime) 对齐环境后暴露并修复的真实问题 - DATE(col) 在 MySQL 返回 DATE 类型,使 Date string 字段变成 RFC3339 时间串, 大盘日期会带出时间部分;28 处改为 DATE_FORMAT(col, '%Y-%m-%d') - 系统自动创建分销结算批次时 created_by 写入 0,违反指向 admin_users 的外键, 生产会直接插入失败;改为写 NULL(新增 adminRef helper) - MySQL DATETIME 仅到秒,静默截断 Go 侧写入的时间, 导致租用时长计算与结算时点回读比对偏差; rental_orders 的 9 个业务时间列提到 DATETIME(6) - 测试数据补齐严格模式要求的 NOT NULL 列、修正超出列长的取值、补齐外键父记录 迁移 - 000065 的 ALTER TABLE rental_orders 一并完成业务时间列精度调整, feature 侧不再单开迁移文件 脚本与文档 - check.sh / check-backend-coverage.sh 去掉 -p 1 串行与 SQLite 相关注释 - 更新 docs/Repository层测试补充总结.md 的测试运行说明
475 lines
17 KiB
Go
475 lines
17 KiB
Go
package distribution
|
||
|
||
import (
|
||
"testing"
|
||
"time"
|
||
|
||
"hfb_sys/backend/internal/database"
|
||
"hfb_sys/backend/internal/model"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
func TestResolveDistributionMode(t *testing.T) {
|
||
cases := []struct {
|
||
name string
|
||
buySiteID uint64
|
||
listingSiteID uint64
|
||
want string
|
||
}{
|
||
{name: "主站买+主站货", buySiteID: 0, listingSiteID: 0, want: ""},
|
||
{name: "主站买+分站货", buySiteID: 0, listingSiteID: 11, want: ModeTwoPartyDistributorReceive},
|
||
{name: "分站卖+主站货", buySiteID: 22, listingSiteID: 0, want: ModeTwoPartyPlatformReceive},
|
||
{name: "分站B买+分站A货", buySiteID: 22, listingSiteID: 11, want: ModeThreeParty},
|
||
{name: "本站自卖", buySiteID: 11, listingSiteID: 11, want: ModeSiteSelfSell},
|
||
}
|
||
for _, item := range cases {
|
||
t.Run(item.name, func(t *testing.T) {
|
||
got := resolveDistributionMode(item.buySiteID, item.listingSiteID)
|
||
if got != item.want {
|
||
t.Fatalf("resolveDistributionMode(%d, %d) = %q, want %q", item.buySiteID, item.listingSiteID, got, item.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestResolveOrderSourceTx(t *testing.T) {
|
||
db := distributionTestDB(t)
|
||
|
||
site, rule, err := ResolveOrderSourceTx(db, "", 1, 1, time.Now())
|
||
if err != nil || site != nil || rule != nil {
|
||
t.Fatalf("expected nil for empty siteCode, got site=%v, rule=%v, err=%v", site, rule, err)
|
||
}
|
||
|
||
_, _, err = ResolveOrderSourceTx(db, "unknown_site", 1, 1, time.Now())
|
||
if err != ErrSiteNotFound {
|
||
t.Fatalf("expected ErrSiteNotFound, got %v", err)
|
||
}
|
||
|
||
now := time.Now()
|
||
activeSite := model.DistributionSite{
|
||
SiteCode: "site_test",
|
||
SiteName: "测试分销站",
|
||
Domain: "test.dist.com",
|
||
Status: "active",
|
||
DisplayAccountEnabled: true,
|
||
}
|
||
if err := db.Create(&activeSite).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
_, _, err = ResolveOrderSourceTx(db, "site_test", 10, 20, now)
|
||
if err != ErrAccountDisplayNotFound {
|
||
t.Fatalf("expected ErrAccountDisplayNotFound, got %v", err)
|
||
}
|
||
|
||
listing := model.RentalListing{ListingNo: "L-MAIN-10", AccountID: 20, OwnerID: 1, Status: "published"}
|
||
if err := db.Create(&listing).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
accountDisplay := model.DistributionSiteAccount{
|
||
SiteID: activeSite.ID,
|
||
AccountID: 20,
|
||
ListingID: listing.ID,
|
||
DisplayStatus: "enabled",
|
||
}
|
||
if err := db.Create(&accountDisplay).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
_, _, err = ResolveOrderSourceTx(db, "site_test", listing.ID, 20, now)
|
||
if err != ErrRuleNotFound {
|
||
t.Fatalf("expected ErrRuleNotFound, got %v", err)
|
||
}
|
||
|
||
wrongMode := model.DistributionRuleVersion{
|
||
SiteID: &activeSite.ID,
|
||
Mode: ModeTwoPartyDistributorReceive,
|
||
Version: 1,
|
||
Status: "active",
|
||
DistributorReceiveRatio: 3.5,
|
||
PlatformSellRatio: 4.0,
|
||
ServiceFeeValue: 2.5,
|
||
ServiceFeeUnit: ServiceFeeUnitFixedAmount,
|
||
PaymentFeeBPS: 50,
|
||
EffectiveFrom: now.Add(-1 * time.Hour),
|
||
}
|
||
if err := db.Create(&wrongMode).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
_, _, err = ResolveOrderSourceTx(db, "site_test", listing.ID, 20, now)
|
||
if err != ErrRuleNotFound {
|
||
t.Fatalf("分站卖主站货不得命中 two_party_distributor_receive, got %v", err)
|
||
}
|
||
|
||
ruleVersion := model.DistributionRuleVersion{
|
||
SiteID: &activeSite.ID,
|
||
Mode: ModeTwoPartyPlatformReceive,
|
||
Version: 1,
|
||
Status: "active",
|
||
PlatformReceiveRatio: 3.5,
|
||
DistributorSellRatio: 4.0,
|
||
ServiceFeeValue: 2.5,
|
||
ServiceFeeUnit: ServiceFeeUnitFixedAmount,
|
||
PaymentFeeBPS: 50,
|
||
EffectiveFrom: now.Add(-1 * time.Hour),
|
||
}
|
||
if err := db.Create(&ruleVersion).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
foundSite, foundRule, err := ResolveOrderSourceTx(db, "site_test", listing.ID, 20, now)
|
||
if err != nil {
|
||
t.Fatalf("unexpected error: %v", err)
|
||
}
|
||
if foundSite.ID != activeSite.ID || foundRule.ID != ruleVersion.ID || foundRule.Mode != ModeTwoPartyPlatformReceive {
|
||
t.Fatalf("unexpected result: site=%v, rule=%v", foundSite, foundRule)
|
||
}
|
||
}
|
||
|
||
func TestResolveOrderSourceTxModes(t *testing.T) {
|
||
db := distributionTestDB(t)
|
||
now := time.Now()
|
||
|
||
siteA := mustCreateDistSite(t, db, "site_a", "收号站A", "a.dist.com")
|
||
siteB := mustCreateDistSite(t, db, "site_b", "卖出站B", "b.dist.com")
|
||
|
||
listingA := model.RentalListing{ListingNo: "L-A-1", AccountID: 101, OwnerID: 1, Status: "published", DistributionSiteID: &siteA.ID}
|
||
if err := db.Create(&listingA).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
listingMain := model.RentalListing{ListingNo: "L-MAIN-1", AccountID: 201, OwnerID: 1, Status: "published"}
|
||
if err := db.Create(&listingMain).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
for _, rel := range []model.DistributionSiteAccount{
|
||
{SiteID: siteA.ID, AccountID: listingA.AccountID, ListingID: listingA.ID, DisplayStatus: "enabled"},
|
||
{SiteID: siteB.ID, AccountID: listingA.AccountID, ListingID: listingA.ID, DisplayStatus: "enabled"},
|
||
{SiteID: siteB.ID, AccountID: listingMain.AccountID, ListingID: listingMain.ID, DisplayStatus: "enabled"},
|
||
} {
|
||
if err := db.Create(&rel).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
}
|
||
|
||
recvRule := mustCreateActiveRule(t, db, siteA.ID, ModeTwoPartyDistributorReceive, now)
|
||
sellRule := mustCreateActiveRule(t, db, siteB.ID, ModeTwoPartyPlatformReceive, now)
|
||
threeRule := mustCreateActiveRule(t, db, siteB.ID, ModeThreeParty, now)
|
||
selfRule := mustCreateActiveRule(t, db, siteA.ID, ModeSiteSelfSell, now)
|
||
|
||
t.Run("主站买+分站货", func(t *testing.T) {
|
||
site, rule, err := ResolveOrderSourceTx(db, "", listingA.ID, listingA.AccountID, now)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if site.ID != siteA.ID || rule.ID != recvRule.ID || rule.Mode != ModeTwoPartyDistributorReceive {
|
||
t.Fatalf("got site=%d mode=%s", site.ID, rule.Mode)
|
||
}
|
||
})
|
||
t.Run("分站卖+主站货", func(t *testing.T) {
|
||
site, rule, err := ResolveOrderSourceTx(db, "site_b", listingMain.ID, listingMain.AccountID, now)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if site.ID != siteB.ID || rule.ID != sellRule.ID || rule.Mode != ModeTwoPartyPlatformReceive {
|
||
t.Fatalf("got site=%d mode=%s", site.ID, rule.Mode)
|
||
}
|
||
})
|
||
t.Run("分站B买+分站A货", func(t *testing.T) {
|
||
site, rule, err := ResolveOrderSourceTx(db, "site_b", listingA.ID, listingA.AccountID, now)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if site.ID != siteB.ID || rule.ID != threeRule.ID || rule.Mode != ModeThreeParty {
|
||
t.Fatalf("got site=%d mode=%s", site.ID, rule.Mode)
|
||
}
|
||
})
|
||
t.Run("本站自卖", func(t *testing.T) {
|
||
site, rule, err := ResolveOrderSourceTx(db, "site_a", listingA.ID, listingA.AccountID, now)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if site.ID != siteA.ID || rule.ID != selfRule.ID || rule.Mode != ModeSiteSelfSell {
|
||
t.Fatalf("got site=%d mode=%s", site.ID, rule.Mode)
|
||
}
|
||
})
|
||
t.Run("本站自卖不回落两方规则", func(t *testing.T) {
|
||
if err := db.Model(&model.DistributionRuleVersion{}).Where("id = ?", selfRule.ID).Update("status", "draft").Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
_, _, err := ResolveOrderSourceTx(db, "site_a", listingA.ID, listingA.AccountID, now)
|
||
if err != ErrRuleNotFound {
|
||
t.Fatalf("expected ErrRuleNotFound without self-sell rule, got %v", err)
|
||
}
|
||
})
|
||
}
|
||
|
||
func mustCreateDistSite(t *testing.T, db *gorm.DB, code, name, domain string) model.DistributionSite {
|
||
t.Helper()
|
||
site := model.DistributionSite{SiteCode: code, SiteName: name, Domain: domain, Status: "active", DisplayAccountEnabled: true}
|
||
if err := db.Create(&site).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
return site
|
||
}
|
||
|
||
func mustCreateActiveRule(t *testing.T, db *gorm.DB, siteID uint64, mode string, now time.Time) model.DistributionRuleVersion {
|
||
t.Helper()
|
||
rule := model.DistributionRuleVersion{
|
||
SiteID: &siteID,
|
||
Mode: mode,
|
||
Version: 1,
|
||
Status: "active",
|
||
DistributorReceiveRatio: 3.5,
|
||
PlatformSellRatio: 4,
|
||
PlatformReceiveRatio: 3.5,
|
||
DistributorSellRatio: 4,
|
||
DistributorARatio: 3.5,
|
||
DistributorBRatio: 4,
|
||
ServiceFeeValue: 0,
|
||
ServiceFeeUnit: ServiceFeeUnitFixedAmount,
|
||
PaymentFeeBPS: 0,
|
||
EffectiveFrom: now.Add(-time.Hour),
|
||
}
|
||
if err := db.Create(&rule).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
return rule
|
||
}
|
||
|
||
func TestSnapshotPaidOrderTxAndApplyRefundTx(t *testing.T) {
|
||
db := distributionTestDB(t)
|
||
|
||
now := time.Now()
|
||
site := model.DistributionSite{
|
||
SiteCode: "site_pay",
|
||
SiteName: "支付测试站",
|
||
Domain: "pay.dist.com",
|
||
Status: "active",
|
||
DisplayAccountEnabled: true,
|
||
TaxEnabled: true,
|
||
TaxRateBPS: 100,
|
||
}
|
||
if err := db.Create(&site).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
rule := model.DistributionRuleVersion{
|
||
SiteID: &site.ID,
|
||
Mode: ModeTwoPartyDistributorReceive,
|
||
Version: 1,
|
||
Status: "active",
|
||
DistributorReceiveRatio: 35,
|
||
PlatformSellRatio: 40,
|
||
ServiceFeeValue: 25,
|
||
ServiceFeeUnit: ServiceFeeUnitPercent,
|
||
PaymentFeeBPS: 50,
|
||
TaxEnabled: true,
|
||
TaxRateBPS: 100,
|
||
EffectiveFrom: now.Add(-1 * time.Hour),
|
||
}
|
||
if err := db.Create(&rule).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
order := model.RentalOrder{
|
||
OrderNo: "ORD-DIST-1",
|
||
ListingID: 101,
|
||
AccountID: 201,
|
||
OwnerID: 1,
|
||
RenterID: 2,
|
||
Status: "renting",
|
||
RentAmountCent: 10000,
|
||
OwnerRentAmountCent: 6000,
|
||
PlatformFeeCent: 4000,
|
||
DistributionSiteID: &site.ID,
|
||
DistributionRuleVersionID: &rule.ID,
|
||
}
|
||
if err := db.Create(&order).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
// 1. SnapshotPaidOrderTx
|
||
grossPaid := int64(10000)
|
||
if err := SnapshotPaidOrderTx(db, order.ID, grossPaid); err != nil {
|
||
t.Fatalf("SnapshotPaidOrderTx failed: %v", err)
|
||
}
|
||
|
||
var financial model.DistributionOrderFinancial
|
||
if err := db.Where("order_id = ?", order.ID).First(&financial).Error; err != nil {
|
||
t.Fatalf("find financial failed: %v", err)
|
||
}
|
||
if financial.GrossPaidAmountCent != grossPaid || financial.SiteID != site.ID {
|
||
t.Fatalf("unexpected financial snapshot: %+v", financial)
|
||
}
|
||
if financial.SplitBaseAmountCent != 3950 || financial.ServiceFeeCent != 988 {
|
||
t.Fatalf("profit snapshot = %+v", financial)
|
||
}
|
||
|
||
var parties []model.DistributionOrderParty
|
||
if err := db.Where("order_id = ?", order.ID).Order("id ASC").Find(&parties).Error; err != nil || len(parties) != 2 {
|
||
t.Fatalf("unexpected parties count: len=%d, err=%v", len(parties), err)
|
||
}
|
||
if parties[0].PartyRole != "distributor_receive" || parties[0].PartyType != PartyTypeSite || parties[0].PartyID != site.ID || parties[0].IncomeAmountCent != 1383 {
|
||
t.Fatalf("distributor party = %+v", parties[0])
|
||
}
|
||
if parties[1].PartyRole != "platform_sell" || parties[1].PartyType != PartyTypePlatform || parties[1].PartyID != 0 || parties[1].IncomeAmountCent != 1580 {
|
||
t.Fatalf("platform party = %+v", parties[1])
|
||
}
|
||
|
||
// 幂等写入测试:重复调用不报错且不重复追加 party
|
||
if err := SnapshotPaidOrderTx(db, order.ID, grossPaid); err != nil {
|
||
t.Fatalf("idempotent SnapshotPaidOrderTx failed: %v", err)
|
||
}
|
||
var partiesAfter []model.DistributionOrderParty
|
||
if err := db.Where("order_id = ?", order.ID).Find(&partiesAfter).Error; err != nil || len(partiesAfter) != 2 {
|
||
t.Fatalf("idempotency broken: len=%d", len(partiesAfter))
|
||
}
|
||
|
||
// 2. ApplyRefundTx
|
||
// 非法金额
|
||
if err := ApplyRefundTx(db, order.ID, -10); err != ErrInvalidAmount {
|
||
t.Fatalf("expected ErrInvalidAmount for negative refund, got %v", err)
|
||
}
|
||
// 合法退款
|
||
refundAmount := int64(2000)
|
||
if err := ApplyRefundTx(db, order.ID, refundAmount); err != nil {
|
||
t.Fatalf("ApplyRefundTx failed: %v", err)
|
||
}
|
||
|
||
var updatedFinancial model.DistributionOrderFinancial
|
||
if err := db.Where("order_id = ?", order.ID).First(&updatedFinancial).Error; err != nil {
|
||
t.Fatalf("find updated financial failed: %v", err)
|
||
}
|
||
if updatedFinancial.RefundAmountCent != refundAmount {
|
||
t.Fatalf("expected refund amount %d, got %d", refundAmount, updatedFinancial.RefundAmountCent)
|
||
}
|
||
if updatedFinancial.NetPaidAmountCent != grossPaid-refundAmount {
|
||
t.Fatalf("expected net paid %d, got %d", grossPaid-refundAmount, updatedFinancial.NetPaidAmountCent)
|
||
}
|
||
}
|
||
|
||
func TestSnapshotPaidOrderTxSplitsPercentOfProfit(t *testing.T) {
|
||
db := distributionTestDB(t)
|
||
now := time.Now()
|
||
site := model.DistributionSite{SiteCode: "sell_markup", SiteName: "卖出加价站", Domain: "sell-markup.dist.com", Status: "active", DisplayAccountEnabled: true}
|
||
if err := db.Create(&site).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
rule := model.DistributionRuleVersion{
|
||
SiteID: &site.ID, Mode: ModeTwoPartyPlatformReceive, Version: 1, Status: "active",
|
||
PlatformReceiveRatio: 35, DistributorSellRatio: 40,
|
||
ServiceFeeValue: 25, ServiceFeeUnit: ServiceFeeUnitPercent, PaymentFeeBPS: 50,
|
||
EffectiveFrom: now.Add(-time.Hour),
|
||
}
|
||
if err := db.Create(&rule).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
order := model.RentalOrder{
|
||
OrderNo: "ORD-MARKUP-SELL", ListingID: 1, AccountID: 2, OwnerID: 3, RenterID: 4, Status: "renting",
|
||
RentAmountCent: 10000, OwnerRentAmountCent: 6000, PlatformFeeCent: 4000,
|
||
DistributionSiteID: &site.ID, DistributionRuleVersionID: &rule.ID, DistributionMode: ModeTwoPartyPlatformReceive,
|
||
}
|
||
if err := db.Create(&order).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := SnapshotPaidOrderTx(db, order.ID, 20000); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
var parties []model.DistributionOrderParty
|
||
if err := db.Where("order_id = ?", order.ID).Find(&parties).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
var seller, platform *model.DistributionOrderParty
|
||
for i := range parties {
|
||
switch parties[i].PartyRole {
|
||
case "distributor_sell":
|
||
seller = &parties[i]
|
||
case "platform_receive":
|
||
platform = &parties[i]
|
||
}
|
||
}
|
||
if seller == nil || platform == nil {
|
||
t.Fatalf("missing parties: %+v", parties)
|
||
}
|
||
if seller.IncomeAmountCent != 1580 {
|
||
t.Fatalf("seller 40%% of profit = %d, want 1580", seller.IncomeAmountCent)
|
||
}
|
||
if platform.IncomeAmountCent != 1383 || platform.PartyID != 0 {
|
||
t.Fatalf("platform receive = %+v", platform)
|
||
}
|
||
}
|
||
|
||
func TestApplyRefundTxWithCheckoutAndDeposit(t *testing.T) {
|
||
db := distributionTestDB(t)
|
||
if err := database.AutoMigrateIfMissing(db, &model.OrderCheckout{}); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
now := time.Now()
|
||
site := model.DistributionSite{SiteCode: "checkout_site", SiteName: "结账测试站", Status: "active"}
|
||
if err := db.Create(&site).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
rule := model.DistributionRuleVersion{
|
||
SiteID: &site.ID, Mode: ModeTwoPartyDistributorReceive, Version: 1, Status: "active",
|
||
DistributorReceiveRatio: 35, PlatformSellRatio: 40,
|
||
ServiceFeeValue: 25, ServiceFeeUnit: ServiceFeeUnitPercent, PaymentFeeBPS: 50,
|
||
EffectiveFrom: now.Add(-time.Hour),
|
||
}
|
||
if err := db.Create(&rule).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
order := model.RentalOrder{
|
||
OrderNo: "ORD-CHECKOUT-REFUND", ListingID: 10, AccountID: 20, OwnerID: 30, RenterID: 40, Status: "renting",
|
||
RentAmountCent: 63130, DepositAmountCent: 15500, OwnerRentAmountCent: 55950,
|
||
DistributionSiteID: &site.ID, DistributionRuleVersionID: &rule.ID, DistributionMode: ModeTwoPartyDistributorReceive,
|
||
}
|
||
if err := db.Create(&order).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err := SnapshotPaidOrderTx(db, order.ID, 63130); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
checkout := model.OrderCheckout{
|
||
OrderID: order.ID,
|
||
Status: "accepted",
|
||
RentAmountCent: 53330,
|
||
OwnerRentAmountCent: 46150,
|
||
DepositAmountCent: 15500,
|
||
RenterRefundAmountCent: 25300,
|
||
OwnerIncomeAmountCent: 46150,
|
||
}
|
||
if err := db.Create(&checkout).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
// 退款 25300(包含押金全退 15500 + 租金退款 9800)
|
||
if err := ApplyRefundTx(db, order.ID, 25300); err != nil {
|
||
t.Fatalf("ApplyRefundTx failed: %v", err)
|
||
}
|
||
|
||
var financial model.DistributionOrderFinancial
|
||
if err := db.Where("order_id = ?", order.ID).First(&financial).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if financial.RefundAmountCent != 9800 {
|
||
t.Fatalf("financial.RefundAmountCent = %d, want 9800 (only rent refund)", financial.RefundAmountCent)
|
||
}
|
||
if financial.NetPaidAmountCent != 53330 {
|
||
t.Fatalf("financial.NetPaidAmountCent = %d, want 53330", financial.NetPaidAmountCent)
|
||
}
|
||
if financial.SplitBaseAmountCent != 6913 {
|
||
t.Fatalf("financial.SplitBaseAmountCent = %d, want 6913", financial.SplitBaseAmountCent)
|
||
}
|
||
|
||
var distributorParty model.DistributionOrderParty
|
||
if err := db.Where("order_id = ? AND party_role = 'distributor_receive'", order.ID).First(&distributorParty).Error; err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if distributorParty.IncomeAmountCent != 2420 {
|
||
t.Fatalf("distributorParty.IncomeAmountCent = %d, want 2420", distributorParty.IncomeAmountCent)
|
||
}
|
||
}
|