fix(distribution): 修复收号站订单详情权限、专属支付通道路由、大盘统计与仲裁积分隔离

- 修复 FindOrder 在收号站模式(two_party_distributor_receive / three_party)下的 403 权限判定,放行参与方站点成员并精准计算收益
- 串联 distribution_site_payment_channels 至 payment.Start 核心支付发起流程,分销订单优先使用绑定的专属商户通道
- 修复数据大盘 GetDashboardAnalytics 趋势中 ReceivedCount 恒为 0 的问题,并对齐今日订单与 GMV 统计
- 修复 dispute/arbitration.go 仲裁完成时向分销租客发放成长积分的泄漏风险
- 提现管理新增 distribution_site_name/distribution_site_code 字段并在后台列表与详情弹窗中区分展示分销站
- GetPublicSiteInfo 增加 X-Distribution-Site-Code 请求头兜底支持
This commit is contained in:
yml2213
2026-09-11 10:31:14 +08:00
parent 6acdead0f0
commit 9ae763dee6
15 changed files with 427 additions and 84 deletions
@@ -12,6 +12,7 @@ import (
"hfb_sys/backend/internal/database"
"hfb_sys/backend/internal/model"
ordermodule "hfb_sys/backend/internal/modules/order"
"hfb_sys/backend/internal/modules/paymentconfig"
"hfb_sys/backend/internal/modules/rentergrowth"
"gorm.io/gorm"
@@ -824,3 +825,106 @@ func TestApplyChannelStatusKeepsTerminalPayment(t *testing.T) {
t.Fatalf("status = %q, want closed", latest.Status)
}
}
func TestStartPaymentRoutesToDistributionSitePaymentChannel(t *testing.T) {
db := setupPaymentTestDB(t)
if err := db.AutoMigrate(
&model.DistributionSite{},
&model.DistributionSitePaymentChannel{},
&model.PaymentMerchantConfig{},
); err != nil {
t.Fatalf("migrate distribution payment schema failed: %v", err)
}
configRepo := paymentconfig.NewRepository(db, &paymentconfig.MockEncryptor{})
orderRepo := ordermodule.NewRepository(db)
repo := NewRepository(db, configRepo, orderRepo)
defaultConfig := model.PaymentMerchantConfig{
Name: "平台默认微信",
Provider: "mock",
MerchantID: "PLATFORM_MOCK",
PayWay: "WX",
IsDefault: true,
Status: "active",
Environment: "sandbox",
}
if err := db.Create(&defaultConfig).Error; err != nil {
t.Fatal(err)
}
siteConfig := model.PaymentMerchantConfig{
Name: "分销站专属微信",
Provider: "mock",
MerchantID: "SITE_MOCK_01",
PayWay: "WX",
IsDefault: false,
Status: "active",
Environment: "sandbox",
}
if err := db.Create(&siteConfig).Error; err != nil {
t.Fatal(err)
}
site := model.DistributionSite{SiteCode: "pay_site", SiteName: "支付分站", Domain: "pay.example.com", Status: "active"}
if err := db.Create(&site).Error; err != nil {
t.Fatal(err)
}
channel := model.DistributionSitePaymentChannel{
SiteID: site.ID,
ConfigID: siteConfig.ID,
Priority: 1,
Status: "active",
}
if err := db.Create(&channel).Error; err != nil {
t.Fatal(err)
}
// Order A: Belongs to distribution site
fixA := createPayableOrderFixture(t, db, "siteA", false)
fixA.Order.DistributionSiteID = &site.ID
if err := db.Save(&fixA.Order).Error; err != nil {
t.Fatal(err)
}
// Order B: Normal platform order
fixB := createPayableOrderFixture(t, db, "siteB", false)
fixB.Order.DistributionSiteID = nil
if err := db.Save(&fixB.Order).Error; err != nil {
t.Fatal(err)
}
// Start payment for Order A -> should route to siteConfig
dtoA, err := repo.Start(t.Context(), fixA.Renter.ID, fixA.Order.ID, StartPaymentRequest{PayWay: "WX"}, "127.0.0.1")
if err != nil {
t.Fatalf("Start Order A failed: %v", err)
}
if dtoA.PaymentConfigID != siteConfig.ID {
t.Fatalf("Order A expected PaymentConfigID=%d, got %d", siteConfig.ID, dtoA.PaymentConfigID)
}
var savedA model.PaymentOrder
if err := db.First(&savedA, dtoA.ID).Error; err != nil {
t.Fatalf("find saved payment A failed: %v", err)
}
if savedA.MerchantID != siteConfig.MerchantID {
t.Fatalf("Order A expected MerchantID=%s, got %s", siteConfig.MerchantID, savedA.MerchantID)
}
// Start payment for Order B -> should route to defaultConfig
dtoB, err := repo.Start(t.Context(), fixB.Renter.ID, fixB.Order.ID, StartPaymentRequest{PayWay: "WX"}, "127.0.0.1")
if err != nil {
t.Fatalf("Start Order B failed: %v", err)
}
if dtoB.PaymentConfigID != defaultConfig.ID {
t.Fatalf("Order B expected PaymentConfigID=%d, got %d", defaultConfig.ID, dtoB.PaymentConfigID)
}
var savedB model.PaymentOrder
if err := db.First(&savedB, dtoB.ID).Error; err != nil {
t.Fatalf("find saved payment B failed: %v", err)
}
if savedB.MerchantID != defaultConfig.MerchantID {
t.Fatalf("Order B expected MerchantID=%s, got %s", defaultConfig.MerchantID, savedB.MerchantID)
}
}