feat(distribution): 支持分销上传来源追踪、主站审核展示与收售结算模式自动判定

This commit is contained in:
yml2213
2026-09-08 15:00:35 +08:00
parent a61828c6ca
commit 0fcb6a513f
14 changed files with 301 additions and 69 deletions
@@ -24,28 +24,66 @@ func distributionTablesPresent(tx *gorm.DB) bool {
return distributionTablePresent
}
// ResolveOrderSourceTx 校验站点、账号展示关系和生效规则,供正常订单创建事务写入快照引用。
// 第一期仅启用 two_party_distributor_receive 模式,其他模式规则不会命中(见设计文档实施顺序)。
// ResolveOrderSourceTx 校验收号方站点与售出方站点、账号展示关系和生效规则,供正常订单创建事务写入快照引用。
func ResolveOrderSourceTx(tx *gorm.DB, siteCode string, listingID, accountID uint64, at time.Time) (*model.DistributionSite, *model.DistributionRuleVersion, error) {
if siteCode == "" {
var listing model.RentalListing
var listingSiteID *uint64
if listingID > 0 {
if err := tx.Select("id, distribution_site_id").First(&listing, listingID).Error; err == nil {
listingSiteID = listing.DistributionSiteID
}
}
// 1. 两方均无分销站:纯主站交易
if siteCode == "" && listingSiteID == nil {
return nil, nil, nil
}
var site model.DistributionSite
if err := tx.Where("site_code = ? AND status = 'active' AND display_account_enabled = ?", siteCode, true).First(&site).Error; err != nil {
// 2. 模式一:分销网收号、主站卖出 (ModeTwoPartyDistributorReceive)
if siteCode == "" && listingSiteID != nil {
var site model.DistributionSite
if err := tx.Where("id = ? AND status = 'active'", *listingSiteID).First(&site).Error; err != nil {
return nil, nil, ErrSiteNotFound
}
var rule model.DistributionRuleVersion
query := tx.Where("mode = ? AND status = 'active' AND effective_from <= ? AND (effective_to IS NULL OR effective_to > ?)", ModeTwoPartyDistributorReceive, at, at).
Where("site_id = ? OR site_id IS NULL", site.ID).
Order("CASE WHEN site_id IS NULL THEN 1 ELSE 0 END, effective_from DESC, id DESC")
if err := query.First(&rule).Error; err != nil {
return nil, nil, ErrRuleNotFound
}
return &site, &rule, nil
}
// 3. 买家在分销站下单:校验售出站点状态与账号展示关系
var sellSite model.DistributionSite
if err := tx.Where("site_code = ? AND status = 'active' AND display_account_enabled = ?", siteCode, true).First(&sellSite).Error; err != nil {
return nil, nil, ErrSiteNotFound
}
var relation model.DistributionSiteAccount
if err := tx.Where("site_id = ? AND listing_id = ? AND account_id = ? AND display_status = 'enabled'", site.ID, listingID, accountID).First(&relation).Error; err != nil {
if err := tx.Where("site_id = ? AND listing_id = ? AND account_id = ? AND display_status = 'enabled'", sellSite.ID, listingID, accountID).First(&relation).Error; err != nil {
return nil, nil, ErrAccountDisplayNotFound
}
// 4. 解析生效规则:优先匹配有效规则版本
targetMode := ModeTwoPartyDistributorReceive
if listingSiteID != nil && *listingSiteID != sellSite.ID {
targetMode = ModeThreeParty
}
var rule model.DistributionRuleVersion
query := tx.Where("mode = ? AND status = 'active' AND effective_from <= ? AND (effective_to IS NULL OR effective_to > ?)", ModeTwoPartyDistributorReceive, at, at).
Where("site_id = ? OR site_id IS NULL", site.ID).
Order("CASE WHEN site_id IS NULL THEN 1 ELSE 0 END, effective_from DESC, id DESC")
query := tx.Where("status = 'active' AND effective_from <= ? AND (effective_to IS NULL OR effective_to > ?)", at, at).
Where("site_id = ? OR site_id IS NULL", sellSite.ID)
if targetMode == ModeThreeParty {
query = query.Where("mode = ?", ModeThreeParty)
} else {
query = query.Where("mode IN (?, ?)", ModeTwoPartyDistributorReceive, ModeTwoPartyPlatformReceive).
Order("CASE WHEN mode = '" + ModeTwoPartyDistributorReceive + "' THEN 0 ELSE 1 END")
}
query = query.Order("CASE WHEN site_id IS NULL THEN 1 ELSE 0 END, effective_from DESC, id DESC")
if err := query.First(&rule).Error; err != nil {
return nil, nil, ErrRuleNotFound
}
return &site, &rule, nil
return &sellSite, &rule, nil
}
// calculateForRule 按规则快照计算手续费、服务费和分成基数。
@@ -99,6 +137,12 @@ func SnapshotPaidOrderTx(tx *gorm.DB, orderID uint64, grossPaidCent int64) error
partyID := *order.DistributionSiteID
if partyType == "platform" {
partyID = 0
} else if split.PartyRole == "distributor_a" {
// 三方模式下 distributor_a 为商品收号站
var listing model.RentalListing
if err := tx.Select("distribution_site_id").First(&listing, order.ListingID).Error; err == nil && listing.DistributionSiteID != nil {
partyID = *listing.DistributionSiteID
}
}
parties = append(parties, model.DistributionOrderParty{OrderID: order.ID, SiteID: *order.DistributionSiteID, PartyRole: split.PartyRole, PartyType: partyType, PartyID: partyID, Ratio: split.Ratio, IncomeAmountCent: split.AmountCent, ProfitAmountCent: split.AmountCent})
}