package packageprice import ( "context" "database/sql" "errors" "math" "strings" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" ) type Source struct { PackageCode string CostPrice float64 TerminalCost float64 SysDefaultRatio float64 SysDefaultProfit float64 SysDefaultTerminalRatio float64 SysDefaultTerminalProfit float64 } type BaseCosts struct { AgentCost float64 TerminalCost float64 } type Rule struct { PriceSource string AgentDefaultPriceId int64 TargetPriceId int64 AgentCostRatio float64 AgentCostProfit float64 TerminalCostRatio float64 TerminalCostProfit float64 Remark string UpdatedAt *gtime.Time } type SalePrice struct { Base BaseCosts Rule Rule AgentShipmentPrice float64 TerminalSalePrice float64 } type priceStore interface { ParentDeptID(ctx context.Context, tenantID string, deptID int64) (int64, error) PriceOverride(ctx context.Context, tenantID string, deptID int64, scopeType int, targetAgentID int64, packageCode string) (*entity.TSimFlowPackagePrice, error) } type dbStore struct { tx gdb.TX } func SourceFromPackageInfo(pkg *entity.TSimFlowPackageInfo) Source { if pkg == nil { return Source{} } return Source{ PackageCode: strings.TrimSpace(pkg.PackageCode), CostPrice: pkg.CostPrice, TerminalCost: pkg.TerminalCost, SysDefaultRatio: pkg.SysDefaultRatio, SysDefaultProfit: pkg.SysDefaultProfit, SysDefaultTerminalRatio: pkg.SysDefaultTerminalRatio, SysDefaultTerminalProfit: pkg.SysDefaultTerminalProfit, } } func SourceFromPriceListItem(pkg *model.TSimFlowPackagePriceAgentDefaultPackagePricePackageItem) Source { if pkg == nil { return Source{} } return Source{ PackageCode: strings.TrimSpace(pkg.PackageCode), CostPrice: pkg.CostPrice, TerminalCost: pkg.TerminalCost, SysDefaultRatio: pkg.SysDefaultRatio, SysDefaultProfit: pkg.SysDefaultProfit, SysDefaultTerminalRatio: pkg.SysDefaultTerminalRatio, SysDefaultTerminalProfit: pkg.SysDefaultTerminalProfit, } } func ResolveEffectiveBaseCosts(ctx context.Context, tx gdb.TX, tenantID string, deptID int64, pkg Source) (BaseCosts, error) { return resolveEffectiveBaseCosts(ctx, dbStore{tx: tx}, tenantID, deptID, pkg) } func ResolveEffectiveSalePrice(ctx context.Context, tx gdb.TX, tenantID string, ownerDeptID int64, targetDeptID int64, pkg Source) (SalePrice, error) { return resolveEffectiveSalePrice(ctx, dbStore{tx: tx}, tenantID, ownerDeptID, targetDeptID, pkg) } func NormalizeRatio(ratio float64) float64 { if ratio > 10 { return ratio / 100 } return ratio } func RoundAmount(value float64) float64 { return math.Round(value*100) / 100 } func resolveEffectiveBaseCosts(ctx context.Context, store priceStore, tenantID string, deptID int64, pkg Source) (BaseCosts, error) { visited := make(map[int64]struct{}) return resolveEffectiveBaseCostsDepth(ctx, store, tenantID, deptID, pkg, visited) } func resolveEffectiveSalePrice(ctx context.Context, store priceStore, tenantID string, ownerDeptID int64, targetDeptID int64, pkg Source) (SalePrice, error) { base, err := resolveEffectiveBaseCosts(ctx, store, tenantID, ownerDeptID, pkg) if err != nil { return SalePrice{}, err } rule, err := resolveEffectivePriceRule(ctx, store, tenantID, ownerDeptID, targetDeptID, pkg) if err != nil { return SalePrice{}, err } return SalePrice{ Base: base, Rule: rule, AgentShipmentPrice: RoundAmount(base.AgentCost*rule.AgentCostRatio + rule.AgentCostProfit), TerminalSalePrice: RoundAmount(base.TerminalCost*rule.TerminalCostRatio + rule.TerminalCostProfit), }, nil } func resolveEffectiveBaseCostsDepth(ctx context.Context, store priceStore, tenantID string, deptID int64, pkg Source, visited map[int64]struct{}) (BaseCosts, error) { if strings.TrimSpace(pkg.PackageCode) == "" { return BaseCosts{}, gerror.New("套餐编码不能为空") } if deptID <= 0 { return systemBaseCosts(pkg), nil } if _, ok := visited[deptID]; ok { return BaseCosts{}, gerror.New("组织定价层级存在循环,无法计算生效成本") } visited[deptID] = struct{}{} parentID, err := store.ParentDeptID(ctx, tenantID, deptID) if err != nil { return BaseCosts{}, err } if parentID <= 0 { return systemBaseCosts(pkg), nil } parentBase, err := resolveEffectiveBaseCostsDepth(ctx, store, tenantID, parentID, pkg, visited) if err != nil { return BaseCosts{}, err } parentRule, err := resolveEffectivePriceRule(ctx, store, tenantID, parentID, deptID, pkg) if err != nil { return BaseCosts{}, err } return BaseCosts{ AgentCost: RoundAmount(parentBase.AgentCost*parentRule.AgentCostRatio + parentRule.AgentCostProfit), TerminalCost: RoundAmount(parentBase.TerminalCost*parentRule.TerminalCostRatio + parentRule.TerminalCostProfit), }, nil } func systemBaseCosts(pkg Source) BaseCosts { return BaseCosts{ AgentCost: RoundAmount(pkg.CostPrice), TerminalCost: RoundAmount(pkg.TerminalCost), } } func resolveEffectivePriceRule(ctx context.Context, store priceStore, tenantID string, ownerDeptID int64, targetDeptID int64, pkg Source) (Rule, error) { rule := systemPriceRule(pkg) if ownerDeptID <= 0 { return rule, nil } if targetDeptID > 0 { targetPrice, err := store.PriceOverride(ctx, tenantID, ownerDeptID, 1, targetDeptID, pkg.PackageCode) if err != nil { return rule, err } if targetPrice != nil { return ruleFromOverride("target", targetPrice), nil } } defaultPrice, err := store.PriceOverride(ctx, tenantID, ownerDeptID, 0, 0, pkg.PackageCode) if err != nil { return rule, err } if defaultPrice != nil { return ruleFromOverride("agentDefault", defaultPrice), nil } return rule, nil } func systemPriceRule(pkg Source) Rule { return Rule{ PriceSource: "system", AgentCostRatio: NormalizeRatio(pkg.SysDefaultRatio), AgentCostProfit: pkg.SysDefaultProfit, TerminalCostRatio: NormalizeRatio(pkg.SysDefaultTerminalRatio), TerminalCostProfit: pkg.SysDefaultTerminalProfit, } } func ruleFromOverride(priceSource string, price *entity.TSimFlowPackagePrice) Rule { if price == nil { return Rule{PriceSource: priceSource} } rule := Rule{ PriceSource: priceSource, AgentCostRatio: NormalizeRatio(price.AgentCostRatio), AgentCostProfit: price.AgentCostProfit, TerminalCostRatio: NormalizeRatio(price.UserCostRatio), TerminalCostProfit: price.UserCostProfit, Remark: price.Remark, UpdatedAt: price.UpdatedAt, } if priceSource == "target" { rule.TargetPriceId = price.Id } else { rule.AgentDefaultPriceId = price.Id } return rule } func (s dbStore) ParentDeptID(ctx context.Context, tenantID string, deptID int64) (int64, error) { if deptID <= 0 { return 0, nil } dept := &entity.SysDept{} columns := dao.SysDept.Columns() err := s.model(ctx, dao.SysDept.Table()). Fields(columns.DeptId, columns.ParentId). Where(columns.TenantId, tenantID). Where(columns.DeptId, deptID). WhereNull(columns.DeletedAt). Scan(dept) if err != nil { if errors.Is(err, sql.ErrNoRows) { return 0, gerror.New("组织不存在") } return 0, gerror.Wrap(err, "查询组织上级失败") } if dept.DeptId == 0 { return 0, gerror.New("组织不存在") } return dept.ParentId, nil } func (s dbStore) PriceOverride(ctx context.Context, tenantID string, deptID int64, scopeType int, targetAgentID int64, packageCode string) (*entity.TSimFlowPackagePrice, error) { packageCode = strings.TrimSpace(packageCode) if tenantID == "" || deptID <= 0 || packageCode == "" { return nil, nil } price := &entity.TSimFlowPackagePrice{} columns := dao.TSimFlowPackagePrice.Columns() err := s.model(ctx, dao.TSimFlowPackagePrice.Table()). Where(columns.TenantId, tenantID). Where(columns.DeptId, deptID). Where(columns.AgentScopeType, scopeType). Where(columns.TargetAgentId, targetAgentID). Where(columns.PackageCode, packageCode). Where(columns.Enabled, 1). Scan(price) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, nil } return nil, gerror.Wrap(err, "查询套餐定价失败") } if price.Id == 0 { return nil, nil } return price, nil } func (s dbStore) model(ctx context.Context, table string) *gdb.Model { if s.tx != nil { return s.tx.Ctx(ctx).Model(table) } return g.DB().Ctx(ctx).Model(table) }