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
+30 -28
View File
@@ -27,34 +27,36 @@ type WithdrawalDTO struct {
// 管理员端详细 DTO
type WithdrawalDetailDTO struct {
ID uint64 `json:"id"`
WithdrawNo string `json:"withdraw_no"`
UserID uint64 `json:"user_id"`
DistributionSiteID uint64 `json:"distribution_site_id"`
UserNickname string `json:"user_nickname"`
UserPhone string `json:"user_phone"`
AmountCent int64 `json:"amount_cent"`
FeeCent int64 `json:"fee_cent"`
ActualAmountCent int64 `json:"actual_amount_cent"`
PaymentAccountID *uint64 `json:"payment_account_id"`
AccountType string `json:"account_type"`
AccountName string `json:"account_name"`
AccountNo string `json:"account_no"` // 管理员可见完整账号
BankName string `json:"bank_name"`
BankBranch string `json:"bank_branch"`
CertificateURLs []string `json:"certificate_urls"` // 收款二维码图片
Status string `json:"status"`
ReviewedBy *uint64 `json:"reviewed_by"`
ReviewedByName string `json:"reviewed_by_name"`
ReviewedAt *time.Time `json:"reviewed_at"`
ReviewRemark string `json:"review_remark"`
PaidBy *uint64 `json:"paid_by"`
PaidByName string `json:"paid_by_name"`
PaidAt *time.Time `json:"paid_at"`
PaymentProofURL string `json:"payment_proof_url"`
PaymentRemark string `json:"payment_remark"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ID uint64 `json:"id"`
WithdrawNo string `json:"withdraw_no"`
UserID uint64 `json:"user_id"`
DistributionSiteID uint64 `json:"distribution_site_id"`
DistributionSiteName string `json:"distribution_site_name"`
DistributionSiteCode string `json:"distribution_site_code"`
UserNickname string `json:"user_nickname"`
UserPhone string `json:"user_phone"`
AmountCent int64 `json:"amount_cent"`
FeeCent int64 `json:"fee_cent"`
ActualAmountCent int64 `json:"actual_amount_cent"`
PaymentAccountID *uint64 `json:"payment_account_id"`
AccountType string `json:"account_type"`
AccountName string `json:"account_name"`
AccountNo string `json:"account_no"` // 管理员可见完整账号
BankName string `json:"bank_name"`
BankBranch string `json:"bank_branch"`
CertificateURLs []string `json:"certificate_urls"` // 收款二维码图片
Status string `json:"status"`
ReviewedBy *uint64 `json:"reviewed_by"`
ReviewedByName string `json:"reviewed_by_name"`
ReviewedAt *time.Time `json:"reviewed_at"`
ReviewRemark string `json:"review_remark"`
PaidBy *uint64 `json:"paid_by"`
PaidByName string `json:"paid_by_name"`
PaidAt *time.Time `json:"paid_at"`
PaymentProofURL string `json:"payment_proof_url"`
PaymentRemark string `json:"payment_remark"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type CreateWithdrawalRequest struct {
@@ -75,12 +75,23 @@ func (r *Repository) toDetailDTO(ctx context.Context, w model.WithdrawalRequest)
}
}
var siteName, siteCode string
if w.DistributionSiteID > 0 {
var site model.DistributionSite
if err := db.Select("site_name, site_code").First(&site, w.DistributionSiteID).Error; err == nil {
siteName = site.SiteName
siteCode = site.SiteCode
}
}
return &WithdrawalDetailDTO{
ID: w.ID,
WithdrawNo: w.WithdrawNo,
UserID: w.UserID,
DistributionSiteID: w.DistributionSiteID,
UserNickname: user.Nickname,
ID: w.ID,
WithdrawNo: w.WithdrawNo,
UserID: w.UserID,
DistributionSiteID: w.DistributionSiteID,
DistributionSiteName: siteName,
DistributionSiteCode: siteCode,
UserNickname: user.Nickname,
UserPhone: user.Phone,
AmountCent: w.AmountCent,
FeeCent: w.FeeCent,
@@ -168,3 +168,33 @@ func TestWithdrawalCreateRollsBackWhenBalanceIsInsufficient(t *testing.T) {
}
f.assertBalance(t, 100_000, 0)
}
func TestWithdrawalDetailIncludesDistributionSiteInfo(t *testing.T) {
f := newWithdrawalFixture(t)
if err := f.db.AutoMigrate(&model.DistributionSite{}); err != nil {
t.Fatal(err)
}
site := model.DistributionSite{SiteCode: "sub_site", SiteName: "分销子站", Domain: "sub.example.com", Status: "active"}
if err := f.db.Create(&site).Error; err != nil {
t.Fatal(err)
}
wReq := model.WithdrawalRequest{
WithdrawNo: "WD2026TESTSITE",
UserID: f.userID,
DistributionSiteID: site.ID,
AmountCent: 5000,
ActualAmountCent: 5000,
Status: "pending",
}
if err := f.db.Create(&wReq).Error; err != nil {
t.Fatal(err)
}
detail, err := f.repo.AdminFindByID(context.Background(), wReq.ID)
if err != nil {
t.Fatalf("AdminFindByID failed: %v", err)
}
if detail.DistributionSiteID != site.ID || detail.DistributionSiteName != "分销子站" || detail.DistributionSiteCode != "sub_site" {
t.Fatalf("unexpected detail site info: %+v", detail)
}
}