feat(distribution): 完善分销收号资产双视图、商户提现、自主加价、品牌装修与数据大盘
This commit is contained in:
@@ -23,7 +23,8 @@ func distributionTestDB(t *testing.T) *gorm.DB {
|
||||
&model.DistributionSiteMember{}, &model.DistributionRuleVersion{}, &model.DistributionOrderFinancial{},
|
||||
&model.DistributionOrderParty{}, &model.DistributionSiteAccount{}, &model.DistributionSitePaymentChannel{},
|
||||
&model.DistributionSettlementBatch{}, &model.DistributionSettlementItem{},
|
||||
&model.RentalListing{}, &model.GameAccount{},
|
||||
&model.RentalListing{}, &model.GameAccount{}, &model.User{},
|
||||
&model.WalletAccount{}, &model.WalletLedger{}, &model.WithdrawalRequest{},
|
||||
); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -390,3 +391,228 @@ func TestGetPublicSiteInfo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistributionBrandingAndMarkup(t *testing.T) {
|
||||
db := distributionTestDB(t)
|
||||
repo := NewRepository(db)
|
||||
|
||||
site := model.DistributionSite{
|
||||
SiteCode: "brand_site",
|
||||
SiteName: "品牌测试站",
|
||||
Domain: "brand.example.com",
|
||||
Status: "active",
|
||||
DisplayAccountEnabled: true,
|
||||
}
|
||||
if err := db.Create(&site).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// 1. Update branding
|
||||
updated, err := repo.UpdateSiteBranding(t.Context(), 0, site.ID, UpdateSiteBrandingRequest{
|
||||
LogoURL: "https://cdn.example.com/logo.png",
|
||||
FaviconURL: "https://cdn.example.com/favicon.ico",
|
||||
BannersJSON: `[{"image_url":"https://cdn.example.com/b1.jpg"}]`,
|
||||
AnnouncementsJSON: `[{"title":"开业大吉"}]`,
|
||||
FooterInfoJSON: `{"copyright":"2026 Brand"}`,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateSiteBranding failed: %v", err)
|
||||
}
|
||||
if updated.LogoURL != "https://cdn.example.com/logo.png" || updated.FaviconURL != "https://cdn.example.com/favicon.ico" {
|
||||
t.Fatalf("unexpected branding: %+v", updated)
|
||||
}
|
||||
|
||||
// Verify PublicSiteInfo contains branding
|
||||
pubInfo, err := repo.GetPublicSiteInfo(t.Context(), "brand_site", "")
|
||||
if err != nil {
|
||||
t.Fatalf("GetPublicSiteInfo failed: %v", err)
|
||||
}
|
||||
if pubInfo.LogoURL != "https://cdn.example.com/logo.png" {
|
||||
t.Fatalf("expected LogoURL, got: %+v", pubInfo)
|
||||
}
|
||||
|
||||
// 2. Account Markup
|
||||
dsa := model.DistributionSiteAccount{
|
||||
SiteID: site.ID,
|
||||
AccountID: 101,
|
||||
ListingID: 201,
|
||||
DisplayStatus: "enabled",
|
||||
}
|
||||
if err := db.Create(&dsa).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
display, err := repo.UpdateAccountMarkup(t.Context(), 0, dsa.ID, 500)
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateAccountMarkup failed: %v", err)
|
||||
}
|
||||
if display.MarkupAmountCent != 500 {
|
||||
t.Fatalf("expected markup 500, got %d", display.MarkupAmountCent)
|
||||
}
|
||||
|
||||
// Batch update markup
|
||||
if err := repo.BatchUpdateAccountMarkup(t.Context(), 0, site.ID, BatchUpdateAccountMarkupRequest{
|
||||
ListingIDs: []uint64{201},
|
||||
MarkupAmountCent: 800,
|
||||
}); err != nil {
|
||||
t.Fatalf("BatchUpdateAccountMarkup failed: %v", err)
|
||||
}
|
||||
|
||||
displays, err := repo.ListAccountDisplays(t.Context(), site.ID, "")
|
||||
if err != nil || len(displays) == 0 || displays[0].MarkupAmountCent != 800 {
|
||||
t.Fatalf("expected markup 800 after batch, got: %+v", displays)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDistributionReceivedListingsAndWallet(t *testing.T) {
|
||||
db := distributionTestDB(t)
|
||||
repo := NewRepository(db)
|
||||
|
||||
site := model.DistributionSite{
|
||||
SiteCode: "recv_site",
|
||||
SiteName: "收号测试站",
|
||||
Domain: "recv.example.com",
|
||||
Status: "active",
|
||||
DisplayAccountEnabled: true,
|
||||
}
|
||||
if err := db.Create(&site).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
user := model.User{
|
||||
Phone: "13800001234",
|
||||
Nickname: "seller01",
|
||||
}
|
||||
if err := db.Create(&user).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ga := model.GameAccount{
|
||||
OwnerID: user.ID,
|
||||
GameName: "delta_force",
|
||||
ServerRegion: "cn",
|
||||
LoginPlatform: "pc",
|
||||
Title: "满配三角洲账号",
|
||||
}
|
||||
if err := db.Create(&ga).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
listing := model.RentalListing{
|
||||
ListingNo: "RL-99001",
|
||||
AccountID: ga.ID,
|
||||
OwnerID: user.ID,
|
||||
PriceCent: 2000,
|
||||
DepositAmountCent: 5000,
|
||||
Status: "published",
|
||||
ReviewStatus: "approved",
|
||||
DistributionSiteID: &site.ID,
|
||||
}
|
||||
if err := db.Create(&listing).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// 1. List received listings
|
||||
recPage, err := repo.ListReceivedListings(t.Context(), 0, site.ID, ReceivedListingQuery{
|
||||
Page: 1,
|
||||
PageSize: 10,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ListReceivedListings failed: %v", err)
|
||||
}
|
||||
if recPage.Total != 1 || len(recPage.Items) != 1 {
|
||||
t.Fatalf("expected 1 received listing, got %d", recPage.Total)
|
||||
}
|
||||
item := recPage.Items[0]
|
||||
if item.ListingNo != "RL-99001" || item.GameName != "delta_force" || item.OwnerNickname != "seller01" {
|
||||
t.Fatalf("unexpected received listing: %+v", item)
|
||||
}
|
||||
|
||||
// Update received listing status
|
||||
if err := repo.UpdateReceivedListingStatus(t.Context(), 0, listing.ID, "inactive"); err != nil {
|
||||
t.Fatalf("UpdateReceivedListingStatus failed: %v", err)
|
||||
}
|
||||
|
||||
// 2. Settlement batch settles and credits wallet
|
||||
rule := model.DistributionRuleVersion{SiteID: &site.ID, Mode: ModeTwoPartyDistributorReceive, Version: 1, Status: "active", EffectiveFrom: time.Now()}
|
||||
if err := db.Create(&rule).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
batch := model.DistributionSettlementBatch{
|
||||
SiteID: site.ID,
|
||||
BatchNo: "B-WALLET-01",
|
||||
PeriodStart: time.Now().Add(-24 * time.Hour),
|
||||
PeriodEnd: time.Now(),
|
||||
TotalAmountCent: 10000,
|
||||
Status: "pending",
|
||||
}
|
||||
if err := db.Create(&batch).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
batchItem := model.DistributionSettlementItem{
|
||||
BatchID: batch.ID,
|
||||
OrderID: 888,
|
||||
PartyID: site.ID,
|
||||
AmountCent: 10000,
|
||||
Status: "pending",
|
||||
}
|
||||
if err := db.Create(&batchItem).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Mark batch settled -> auto-credits merchant wallet
|
||||
if _, err := repo.MarkSettlementBatch(t.Context(), 0, batch.ID); err != nil {
|
||||
t.Fatalf("MarkSettlementBatch failed: %v", err)
|
||||
}
|
||||
|
||||
walletDTO, err := repo.GetSiteWallet(t.Context(), 0, site.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetSiteWallet failed: %v", err)
|
||||
}
|
||||
if walletDTO.AvailableBalanceCent != 10000 || walletDTO.TotalSettledCent != 10000 {
|
||||
t.Fatalf("expected available balance 10000, got: %+v", walletDTO)
|
||||
}
|
||||
|
||||
// 3. Apply withdrawal
|
||||
wd, err := repo.ApplySiteWithdrawal(t.Context(), 0, site.ID, ApplySiteWithdrawalRequest{
|
||||
SiteID: site.ID,
|
||||
AmountCent: 4000,
|
||||
AccountType: "alipay",
|
||||
AccountName: "分销商李四",
|
||||
AccountNo: "13800005678",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ApplySiteWithdrawal failed: %v", err)
|
||||
}
|
||||
if wd.AmountCent != 4000 || wd.Status != "pending" {
|
||||
t.Fatalf("unexpected withdrawal: %+v", wd)
|
||||
}
|
||||
|
||||
// Check balance after freeze
|
||||
walletAfterWD, err := repo.GetSiteWallet(t.Context(), 0, site.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetSiteWallet after WD failed: %v", err)
|
||||
}
|
||||
if walletAfterWD.AvailableBalanceCent != 6000 || walletAfterWD.FrozenBalanceCent != 4000 {
|
||||
t.Fatalf("expected available 6000 frozen 4000, got: %+v", walletAfterWD)
|
||||
}
|
||||
|
||||
// 4. List withdrawals
|
||||
wdPage, err := repo.ListSiteWithdrawals(t.Context(), 0, site.ID, 1, 10)
|
||||
if err != nil {
|
||||
t.Fatalf("ListSiteWithdrawals failed: %v", err)
|
||||
}
|
||||
if wdPage.Total != 1 || len(wdPage.Items) != 1 {
|
||||
t.Fatalf("expected 1 withdrawal record, got %d", wdPage.Total)
|
||||
}
|
||||
|
||||
// 5. Dashboard analytics
|
||||
analytics, err := repo.GetDashboardAnalytics(t.Context(), 0, site.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetDashboardAnalytics failed: %v", err)
|
||||
}
|
||||
if analytics.TotalReceivedCount != 1 || analytics.AvailableBalanceCent != 6000 {
|
||||
t.Fatalf("unexpected analytics: %+v", analytics)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user