166 lines
6.3 KiB
Go
166 lines
6.3 KiB
Go
package distribution
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"hfb_sys/backend/internal/model"
|
|
)
|
|
|
|
// TestMarkSettlementBatchRejectsWhenOrderRefunded 断言人工批次创建后若发生退款调减,
|
|
// MarkSettlementBatch 必须拦截并返回 ErrSettlementBatchStale,不得将过时金额入账;
|
|
// 管理员作废批次后,新批次能按最新退款后金额成功生成与结算。
|
|
func TestMarkSettlementBatchRejectsWhenOrderRefunded(t *testing.T) {
|
|
db := distributionTestDB(t)
|
|
repo := NewRepository(db)
|
|
now := time.Now()
|
|
|
|
site := mustCreateDistSite(t, db, "stale_site", "退款防穿透站", "stale.dist.com")
|
|
rule := mustCreateActiveRule(t, db, site.ID, ModeTwoPartyDistributorReceive, now)
|
|
order, _, party := addSettlementOrder(t, db, site.ID, rule.ID, "ORD-STALE-1", 1400)
|
|
|
|
// 管理员创建草稿人工批次:批次总额 1400,明细 1400 pending。
|
|
batch, err := repo.CreateSettlementBatch(t.Context(), 0, CreateSettlementBatchRequest{
|
|
SiteID: site.ID, PeriodStart: now.Add(-time.Hour), PeriodEnd: now.Add(time.Hour),
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if batch.TotalAmountCent != 1400 {
|
|
t.Fatalf("batch total = %d, want 1400", batch.TotalAmountCent)
|
|
}
|
|
|
|
// 窗口期内订单发生退款,参与方收入被调减(例如从 1400 降为 400)。
|
|
if err := db.Model(&party).Update("income_amount_cent", 400).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// 此时管理员点击确认结算:必须被拦截阻断,不得超额结算 1400。
|
|
_, err = repo.MarkSettlementBatch(t.Context(), 0, batch.ID)
|
|
if err != ErrSettlementBatchStale {
|
|
t.Fatalf("MarkSettlementBatch err = %v, want ErrSettlementBatchStale", err)
|
|
}
|
|
|
|
// 断言站点钱包未入账任何资金。
|
|
var wallet model.WalletAccount
|
|
if err := db.Where("user_id = 0 AND distribution_site_id = ?", site.ID).First(&wallet).Error; err == nil {
|
|
if wallet.AvailableBalanceCent != 0 {
|
|
t.Fatalf("被拦截的批次不得向钱包入账,当前余额 = %d", wallet.AvailableBalanceCent)
|
|
}
|
|
}
|
|
|
|
// 管理员作废陈旧批次。
|
|
cancelled, err := repo.CancelSettlementBatch(t.Context(), 0, batch.ID)
|
|
if err != nil {
|
|
t.Fatalf("CancelSettlementBatch failed: %v", err)
|
|
}
|
|
if cancelled.Status != "cancelled" {
|
|
t.Fatalf("cancelled status = %s, want cancelled", cancelled.Status)
|
|
}
|
|
|
|
// 重新生成批次:按最新参与方收入 400 生成。
|
|
newBatch, err := repo.CreateSettlementBatch(t.Context(), 0, CreateSettlementBatchRequest{
|
|
SiteID: site.ID, PeriodStart: now.Add(-time.Hour), PeriodEnd: now.Add(time.Hour),
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if newBatch.TotalAmountCent != 400 {
|
|
t.Fatalf("new batch total = %d, want 400(最新收入)", newBatch.TotalAmountCent)
|
|
}
|
|
|
|
// 确认结算新批次:成功入账 400。
|
|
settled, err := repo.MarkSettlementBatch(t.Context(), 0, newBatch.ID)
|
|
if err != nil {
|
|
t.Fatalf("MarkSettlementBatch on new batch failed: %v", err)
|
|
}
|
|
if settled.Status != "settled" {
|
|
t.Fatalf("settled status = %s, want settled", settled.Status)
|
|
}
|
|
|
|
if err := db.Where("user_id = 0 AND distribution_site_id = ?", site.ID).First(&wallet).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if wallet.AvailableBalanceCent != 400 {
|
|
t.Fatalf("最终钱包余额 = %d, want 400", wallet.AvailableBalanceCent)
|
|
}
|
|
_ = order
|
|
}
|
|
|
|
// TestMarkSettlementBatchRejectsWhenOrderDisputedOrRefunding 断言批次内订单处于维权中或退款中时,
|
|
// MarkSettlementBatch 同样拒绝入账。
|
|
func TestMarkSettlementBatchRejectsWhenOrderDisputedOrRefunding(t *testing.T) {
|
|
db := distributionTestDB(t)
|
|
repo := NewRepository(db)
|
|
now := time.Now()
|
|
|
|
site := mustCreateDistSite(t, db, "block_site", "状态拦截站", "block.dist.com")
|
|
rule := mustCreateActiveRule(t, db, site.ID, ModeTwoPartyDistributorReceive, now)
|
|
order, _, _ := addSettlementOrder(t, db, site.ID, rule.ID, "ORD-BLOCK-1", 1400)
|
|
|
|
batch, err := repo.CreateSettlementBatch(t.Context(), 0, CreateSettlementBatchRequest{
|
|
SiteID: site.ID, PeriodStart: now.Add(-time.Hour), PeriodEnd: now.Add(time.Hour),
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// 模拟订单发起纠纷维权中。
|
|
dispute := model.Dispute{OrderID: order.ID, InitiatorID: 4, TargetUserID: 3, Type: "account_error", Status: "open"}
|
|
if err := db.Create(&dispute).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := repo.MarkSettlementBatch(t.Context(), 0, batch.ID); err != ErrSettlementBatchStale {
|
|
t.Fatalf("维权中批次结算 err = %v, want ErrSettlementBatchStale", err)
|
|
}
|
|
|
|
// 结案纠纷,但订单进入 refunding 状态。
|
|
if err := db.Model(&dispute).Update("status", "closed").Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.Model(&order).Update("refund_status", "refunding").Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := repo.MarkSettlementBatch(t.Context(), 0, batch.ID); err != ErrSettlementBatchStale {
|
|
t.Fatalf("退款中批次结算 err = %v, want ErrSettlementBatchStale", err)
|
|
}
|
|
}
|
|
|
|
// TestCreateSettlementBatchExcludesRefundingAndDisputedOrders 断言创建人工批次时,
|
|
// 退款中或维权中的订单不会被打包进入批次。
|
|
func TestCreateSettlementBatchExcludesRefundingAndDisputedOrders(t *testing.T) {
|
|
db := distributionTestDB(t)
|
|
repo := NewRepository(db)
|
|
now := time.Now()
|
|
|
|
site := mustCreateDistSite(t, db, "excl_site", "批次排除站", "excl.dist.com")
|
|
rule := mustCreateActiveRule(t, db, site.ID, ModeTwoPartyDistributorReceive, now)
|
|
|
|
// 订单 1:正常可结算。
|
|
_, _, _ = addSettlementOrder(t, db, site.ID, rule.ID, "ORD-EXCL-NORMAL", 1000)
|
|
|
|
// 订单 2:退款中。
|
|
orderRefunding, _, _ := addSettlementOrder(t, db, site.ID, rule.ID, "ORD-EXCL-REFUNDING", 2000)
|
|
if err := db.Model(&orderRefunding).Update("refund_status", "refunding").Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// 订单 3:维权中。
|
|
orderDisputed, _, _ := addSettlementOrder(t, db, site.ID, rule.ID, "ORD-EXCL-DISPUTED", 3000)
|
|
if err := db.Create(&model.Dispute{OrderID: orderDisputed.ID, InitiatorID: 4, TargetUserID: 3, Type: "cheat", Status: "open"}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
batch, err := repo.CreateSettlementBatch(t.Context(), 0, CreateSettlementBatchRequest{
|
|
SiteID: site.ID, PeriodStart: now.Add(-time.Hour), PeriodEnd: now.Add(time.Hour),
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// 只有正常订单 1000 被纳入,退款中 2000 和维权中 3000 被排除。
|
|
if batch.TotalAmountCent != 1000 {
|
|
t.Fatalf("批次总额 = %d, want 1000(排除退款与纠纷订单)", batch.TotalAmountCent)
|
|
}
|
|
}
|