refactor(distribution): 重写分销迁移并落地利润百分比分成与货架比例
将 000064-000078 合并为 064/065/066,默认规则改为收号 35%、卖出 40%、服务费 25%。 货架调价改为增加分站 1 元兑换哈夫币比例。sql-guard 仅保护 main 已发布迁移。
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user