refactor(distribution): 重写分销迁移并落地利润百分比分成与货架比例

将 000064-000078 合并为 064/065/066,默认规则改为收号 35%、卖出 40%、服务费 25%。
货架调价改为增加分站 1 元兑换哈夫币比例。sql-guard 仅保护 main 已发布迁移。
This commit is contained in:
yml2213
2026-09-08 19:02:58 +08:00
parent 48a2961245
commit d762b33f54
48 changed files with 745 additions and 1073 deletions
@@ -237,11 +237,11 @@ func (r *Repository) FindPublic(ctx context.Context, id uint64, siteCode string)
return nil, err
}
applyPublicListingURLs(dto)
markups, err := r.shelfMarkupByListingID(ctx, siteCode, []uint64{dto.ID})
if err != nil {
items := []ListingDTO{*dto}
if err := r.applyShelfMarkup(ctx, siteCode, items); err != nil {
return nil, err
}
dto.PriceCent += markups[dto.ID]
*dto = items[0]
return dto, nil
}
@@ -253,36 +253,40 @@ func (r *Repository) applyShelfMarkup(ctx context.Context, siteCode string, item
for _, item := range items {
ids = append(ids, item.ID)
}
markups, err := r.shelfMarkupByListingID(ctx, siteCode, ids)
deltas, err := r.shelfRatioDeltaByListingID(ctx, siteCode, ids)
if err != nil {
return err
}
for i := range items {
items[i].PriceCent += markups[items[i].ID]
delta := deltas[items[i].ID]
if delta == 0 {
continue
}
items[i].PriceCent, _, _ = ApplyShelfRatio(items[i].HafCoinAmount, items[i].PriceCent, delta)
}
return nil
}
func (r *Repository) shelfMarkupByListingID(ctx context.Context, siteCode string, listingIDs []uint64) (map[uint64]int64, error) {
result := make(map[uint64]int64, len(listingIDs))
func (r *Repository) shelfRatioDeltaByListingID(ctx context.Context, siteCode string, listingIDs []uint64) (map[uint64]float64, error) {
result := make(map[uint64]float64, len(listingIDs))
if siteCode == "" || len(listingIDs) == 0 {
return result, nil
}
type markupRow struct {
ListingID uint64 `gorm:"column:listing_id"`
MarkupAmountCent int64 `gorm:"column:markup_amount_cent"`
type ratioRow struct {
ListingID uint64 `gorm:"column:listing_id"`
ShelfRatioDelta float64 `gorm:"column:shelf_ratio_delta"`
}
var rows []markupRow
var rows []ratioRow
if err := r.db.WithContext(ctx).Table("distribution_site_accounts AS dsa").
Select("dsa.listing_id, COALESCE(dsa.markup_amount_cent, 0) AS markup_amount_cent").
Select("dsa.listing_id, COALESCE(dsa.shelf_ratio_delta, 0) AS shelf_ratio_delta").
Joins("INNER JOIN distribution_sites AS ds ON ds.id = dsa.site_id AND ds.site_code = ? AND ds.status = 'active'", siteCode).
Where("dsa.display_status = 'enabled' AND dsa.listing_id IN ?", listingIDs).
Scan(&rows).Error; err != nil {
return nil, err
}
for _, row := range rows {
if row.MarkupAmountCent > 0 {
result[row.ListingID] = row.MarkupAmountCent
if row.ShelfRatioDelta > 0 {
result[row.ListingID] = row.ShelfRatioDelta
}
}
return result, nil
@@ -688,12 +688,12 @@ func TestListPublicWithDistributionSiteCode(t *testing.T) {
repo := NewRepository(db, nil)
account1 := model.GameAccount{Title: "号1", GameName: "delta", Status: "active"}
account1 := model.GameAccount{Title: "号1", GameName: "delta", Status: "active", HafCoinAmount: 100_000_000}
account2 := model.GameAccount{Title: "号2", GameName: "delta", Status: "active"}
db.Create(&account1)
db.Create(&account2)
l1 := model.RentalListing{ListingNo: "L101", AccountID: account1.ID, Status: "published", ReviewStatus: "approved", PriceCent: 1000}
l1 := model.RentalListing{ListingNo: "L101", AccountID: account1.ID, Status: "published", ReviewStatus: "approved", PriceCent: 10000}
l2 := model.RentalListing{ListingNo: "L102", AccountID: account2.ID, Status: "published", ReviewStatus: "approved", PriceCent: 2000}
db.Create(&l1)
db.Create(&l2)
@@ -701,7 +701,7 @@ func TestListPublicWithDistributionSiteCode(t *testing.T) {
site := model.DistributionSite{SiteCode: "site_alpha", SiteName: "分销A", Domain: "a.com", Status: "active", DisplayAccountEnabled: true}
db.Create(&site)
dsa := model.DistributionSiteAccount{SiteID: site.ID, AccountID: account1.ID, ListingID: l1.ID, DisplayStatus: "enabled", MarkupAmountCent: 10000}
dsa := model.DistributionSiteAccount{SiteID: site.ID, AccountID: account1.ID, ListingID: l1.ID, DisplayStatus: "enabled", ShelfRatioDelta: 25}
db.Create(&dsa)
res, err := repo.ListPublic(context.Background(), PublicListQuery{
@@ -713,22 +713,22 @@ func TestListPublicWithDistributionSiteCode(t *testing.T) {
if len(res.Items) != 1 || res.Items[0].ListingNo != "L101" {
t.Fatalf("expected only L101, got %+v", res.Items)
}
if res.Items[0].PriceCent != 11000 {
t.Fatalf("expected public price 11000 with markup, got %d", res.Items[0].PriceCent)
if res.Items[0].PriceCent != 8000 {
t.Fatalf("expected public price 8000 with ratio delta, got %d", res.Items[0].PriceCent)
}
detail, err := repo.FindPublic(context.Background(), l1.ID, "site_alpha")
if err != nil {
t.Fatalf("FindPublic err: %v", err)
}
if detail.PriceCent != 11000 {
t.Fatalf("expected detail price 11000 with markup, got %d", detail.PriceCent)
if detail.PriceCent != 8000 {
t.Fatalf("expected detail price 8000 with ratio delta, got %d", detail.PriceCent)
}
mainDetail, err := repo.FindPublic(context.Background(), l1.ID, "")
if err != nil {
t.Fatalf("FindPublic main err: %v", err)
}
if mainDetail.PriceCent != 1000 {
if mainDetail.PriceCent != 10000 {
t.Fatalf("main site must keep original price, got %d", mainDetail.PriceCent)
}
@@ -0,0 +1,34 @@
package listing
// CoinWan 将内部哈夫币数量转为万。
func CoinWan(hafCoinAmount int64) float64 {
return float64(hafCoinAmount) / 10000
}
// BaseExchangeRatio 是主站挂牌的「1 元兑换哈夫币」比例。
func BaseExchangeRatio(hafCoinAmount, priceCent int64) float64 {
if hafCoinAmount <= 0 || priceCent <= 0 {
return 0
}
return roundRatio(CoinWan(hafCoinAmount) / centToYuan(priceCent))
}
// ApplyShelfRatio 按分站加价比例重算售价。delta=0 时保持原价,避免四舍五入漂移。
func ApplyShelfRatio(hafCoinAmount, basePriceCent int64, ratioDelta float64) (priceCent int64, baseRatio, siteRatio float64) {
baseRatio = BaseExchangeRatio(hafCoinAmount, basePriceCent)
if ratioDelta < 0 {
ratioDelta = 0
}
if baseRatio <= 0 || ratioDelta == 0 {
return basePriceCent, baseRatio, baseRatio
}
siteRatio = roundRatio(baseRatio + ratioDelta)
if siteRatio <= 0 {
return basePriceCent, baseRatio, baseRatio
}
priceCent = yuanToCent(CoinWan(hafCoinAmount) / siteRatio)
if priceCent < 0 {
priceCent = 0
}
return priceCent, baseRatio, siteRatio
}
@@ -0,0 +1,24 @@
package listing
import "testing"
func TestApplyShelfRatioIncreasesExchangeRatio(t *testing.T) {
// 100M = 10000 万,主站 100 元 → 1 元 = 100w
price, base, site := ApplyShelfRatio(100_000_000, 10000, 25)
if base != 100 {
t.Fatalf("base ratio = %v, want 100", base)
}
if site != 125 {
t.Fatalf("site ratio = %v, want 125", site)
}
if price != 8000 {
t.Fatalf("site price = %d, want 8000", price)
}
}
func TestApplyShelfRatioZeroDeltaKeepsPrice(t *testing.T) {
price, _, site := ApplyShelfRatio(100_000_000, 10000, 0)
if price != 10000 || site != 100 {
t.Fatalf("price=%d site=%v", price, site)
}
}