核心语义反转:receive_cost_delta 为压价点数,收号比例 = 主站参考比例 R + 点数, 点数越大号主收号价越低;买家售价锚定主站参考比例推导,不再随收号价联动。 - 迁移 000072:distribution_sites 新增 receive_cost_delta(旧 receive_ratio_delta 保留只读) - receive_pricing:新增 Normalize/ApplySiteReceiveCostDelta;分站定价买家侧改用 R 锚点(referenceSellerCoinBasePrice),快照写入 site_receive_cost_delta 与 pricing_model 标记 - site_receive:新增 resolveSiteReceiveCostDelta 读取链路,旧 ResolveReceiveRatioDelta 保留 - 分成不变:压价差经结算层 profit 自动进入 35/40/25 三方分成池; 货架自主加价仍 100% 归分站,两条边界均以主站为锚 - 权限对齐旧字段:主站全局管理员与分站操作员均可设置 - 前端三端同步:主站 pricing.ts/usePricingCalculator 买卖解耦试算、 主站后台弹窗改「收号压价」价格语义文案、分销后台 SiteSettings 同步 - 测试:单测锚定 R=43 时 cost_delta=3 → 收号 217.4 / 卖价 263.2 不变 / markup 池 30.6→54.9
90 lines
2.5 KiB
Go
90 lines
2.5 KiB
Go
package listing
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
)
|
|
|
|
func (s *Service) Create(ctx context.Context, ownerID uint64, req CreateRequest, siteCode ...string) (*ListingDTO, error) {
|
|
if s.repo == nil {
|
|
return nil, ErrDependencyUnavailable
|
|
}
|
|
if !req.AgreedVirtualAssetSale || !req.AgreedSellerAgreement {
|
|
return nil, ErrAgreementRequired
|
|
}
|
|
effectiveSiteCode := ""
|
|
if len(siteCode) > 0 {
|
|
effectiveSiteCode = strings.TrimSpace(siteCode[0])
|
|
}
|
|
delta, onDistribution := s.repo.resolveSiteReceiveCostDelta(ctx, 0, effectiveSiteCode)
|
|
if err := s.applyMainSiteReceivePricing(ctx, &req, 0, delta, onDistribution); err != nil {
|
|
return nil, err
|
|
}
|
|
rules, err := s.publishRules(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := validateRequest(req, rules); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := validateRequiredOnlineTime(req); err != nil {
|
|
return nil, err
|
|
}
|
|
cooldown, err := s.publishCooldown(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reviewRequired, err := s.reviewRequired(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.repo.Create(ctx, ownerID, req, reviewRequired, cooldown, effectiveSiteCode)
|
|
}
|
|
|
|
func (s *Service) Update(ctx context.Context, ownerID uint64, id uint64, req UpdateRequest, siteCode ...string) (*ListingDTO, error) {
|
|
if s.repo == nil {
|
|
return nil, ErrDependencyUnavailable
|
|
}
|
|
effectiveSiteCode := ""
|
|
if len(siteCode) > 0 {
|
|
effectiveSiteCode = strings.TrimSpace(siteCode[0])
|
|
}
|
|
delta, onDistribution := s.repo.resolveSiteReceiveCostDelta(ctx, id, effectiveSiteCode)
|
|
if err := s.applyMainSiteReceivePricing(ctx, &req, 0, delta, onDistribution); err != nil {
|
|
return nil, err
|
|
}
|
|
rules, err := s.publishRules(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := validateRequest(req, rules); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := validateRequiredOnlineTime(req); err != nil {
|
|
return nil, err
|
|
}
|
|
reviewRequired, err := s.reviewRequired(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.repo.Update(ctx, ownerID, id, req, reviewRequired)
|
|
}
|
|
|
|
func (s *Service) SubmitReview(ctx context.Context, ownerID uint64, id uint64) (*ListingDTO, error) {
|
|
if s.repo == nil {
|
|
return nil, ErrDependencyUnavailable
|
|
}
|
|
reviewRequired, err := s.reviewRequired(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.repo.SubmitReview(ctx, ownerID, id, reviewRequired)
|
|
}
|
|
|
|
func (s *Service) Offline(ctx context.Context, ownerID uint64, id uint64) (*ListingDTO, error) {
|
|
if s.repo == nil {
|
|
return nil, ErrDependencyUnavailable
|
|
}
|
|
return s.repo.Offline(ctx, ownerID, id)
|
|
}
|