package iot import ( "context" "database/sql" "errors" "strings" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/logic/iot/packageprice" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" ) type packagePriceSource = packageprice.Source type packageEffectiveBaseCosts = packageprice.BaseCosts type packageEffectivePriceRule = packageprice.Rule type packageEffectiveSalePrice = packageprice.SalePrice func packagePriceSourceFromEntity(pkg *entity.TSimFlowPackageInfo) packagePriceSource { return packageprice.SourceFromPackageInfo(pkg) } func packagePriceSourceFromPriceListItem(pkg *model.TSimFlowPackagePriceAgentDefaultPackagePricePackageItem) packagePriceSource { return packageprice.SourceFromPriceListItem(pkg) } func resolvePackageEffectiveBaseCosts(ctx context.Context, tx gdb.TX, tenantID string, deptID int64, pkg packagePriceSource) (packageEffectiveBaseCosts, error) { return packageprice.ResolveEffectiveBaseCosts(ctx, tx, tenantID, deptID, pkg) } func resolvePackageEffectiveSalePrice(ctx context.Context, tx gdb.TX, tenantID string, ownerDeptID int64, targetDeptID int64, pkg packagePriceSource) (packageEffectiveSalePrice, error) { return packageprice.ResolveEffectiveSalePrice(ctx, tx, tenantID, ownerDeptID, targetDeptID, pkg) } func resolvePackageEffectiveBaseCostsDepth(ctx context.Context, tx gdb.TX, tenantID string, deptID int64, pkg packagePriceSource, visited map[int64]struct{}) (packageEffectiveBaseCosts, error) { if strings.TrimSpace(pkg.PackageCode) == "" { return packageEffectiveBaseCosts{}, gerror.New("套餐编码不能为空") } if deptID <= 0 { return packageSystemBaseCosts(pkg), nil } if _, ok := visited[deptID]; ok { return packageEffectiveBaseCosts{}, gerror.New("组织定价层级存在循环,无法计算生效成本") } visited[deptID] = struct{}{} parentID, err := loadPackagePriceParentDeptID(ctx, tx, tenantID, deptID) if err != nil { return packageEffectiveBaseCosts{}, err } if parentID <= 0 { return packageSystemBaseCosts(pkg), nil } parentBase, err := resolvePackageEffectiveBaseCostsDepth(ctx, tx, tenantID, parentID, pkg, visited) if err != nil { return packageEffectiveBaseCosts{}, err } parentRule, err := resolvePackageEffectivePriceRule(ctx, tx, tenantID, parentID, deptID, pkg) if err != nil { return packageEffectiveBaseCosts{}, err } return packageEffectiveBaseCosts{ AgentCost: roundAmount(parentBase.AgentCost*parentRule.AgentCostRatio + parentRule.AgentCostProfit), TerminalCost: roundAmount(parentBase.TerminalCost*parentRule.TerminalCostRatio + parentRule.TerminalCostProfit), }, nil } func packageSystemBaseCosts(pkg packagePriceSource) packageEffectiveBaseCosts { return packageEffectiveBaseCosts{ AgentCost: roundAmount(pkg.CostPrice), TerminalCost: roundAmount(pkg.TerminalCost), } } func resolvePackageEffectivePriceRule(ctx context.Context, tx gdb.TX, tenantID string, ownerDeptID int64, targetDeptID int64, pkg packagePriceSource) (packageEffectivePriceRule, error) { rule := packageSystemPriceRule(pkg) if ownerDeptID <= 0 { return rule, nil } if targetDeptID > 0 { targetPrice, err := loadPackagePriceOverrideForCalc(ctx, tx, tenantID, ownerDeptID, 1, targetDeptID, pkg.PackageCode) if err != nil { return rule, err } if targetPrice != nil { return packagePriceRuleFromOverride("target", targetPrice), nil } } defaultPrice, err := loadPackagePriceOverrideForCalc(ctx, tx, tenantID, ownerDeptID, 0, 0, pkg.PackageCode) if err != nil { return rule, err } if defaultPrice != nil { return packagePriceRuleFromOverride("agentDefault", defaultPrice), nil } return rule, nil } func packageSystemPriceRule(pkg packagePriceSource) packageEffectivePriceRule { return packageEffectivePriceRule{ PriceSource: "system", AgentCostRatio: normalizePriceRatio(pkg.SysDefaultRatio), AgentCostProfit: pkg.SysDefaultProfit, TerminalCostRatio: normalizePriceRatio(pkg.SysDefaultTerminalRatio), TerminalCostProfit: pkg.SysDefaultTerminalProfit, } } func packagePriceRuleFromOverride(priceSource string, price *entity.TSimFlowPackagePrice) packageEffectivePriceRule { if price == nil { return packageEffectivePriceRule{PriceSource: priceSource} } rule := packageEffectivePriceRule{ PriceSource: priceSource, AgentCostRatio: normalizePriceRatio(price.AgentCostRatio), AgentCostProfit: price.AgentCostProfit, TerminalCostRatio: normalizePriceRatio(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 loadPackagePriceParentDeptID(ctx context.Context, tx gdb.TX, tenantID string, deptID int64) (int64, error) { if deptID <= 0 { return 0, nil } dept := &entity.SysDept{} columns := dao.SysDept.Columns() err := packagePriceCalcModel(ctx, tx, 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 loadPackagePriceOverrideForCalc(ctx context.Context, tx gdb.TX, 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 := packagePriceCalcModel(ctx, tx, 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 packagePriceCalcModel(ctx context.Context, tx gdb.TX, table string) *gdb.Model { if tx != nil { return tx.Ctx(ctx).Model(table) } return g.DB().Ctx(ctx).Model(table) } func addPackageDefaultPaymentMode(isTopDept bool) string { return "balance" } func normalizeAddPackagePaymentIntent(paymentMode string, offlineContractCompleted bool, isTopDept bool) (string, bool, error) { paymentMode = strings.TrimSpace(paymentMode) if paymentMode == "" { paymentMode = addPackageDefaultPaymentMode(isTopDept) } switch paymentMode { case "balance": return "balance", false, nil case "offline": if !isTopDept { return "", false, gerror.New("当前组织加油包必须使用余额支付") } if !offlineContractCompleted { return "", false, gerror.New("请确认线下合同、销售和走账已完成") } return "offline", true, nil default: return "", false, gerror.New("加油包付款方式不正确") } } func normalizeAddPackagePaymentMode(ctx context.Context, paymentMode string, offlineContractCompleted bool) (string, bool, error) { isTopDept, _, err := service.AgentDept().IsTopDept(ctx) if err != nil { return "", false, gerror.Wrap(err, "判断当前组织类型失败") } return normalizeAddPackagePaymentIntent(paymentMode, offlineContractCompleted, isTopDept) } func queryAddPackagePaymentContext(ctx context.Context) (*model.TSimAddPackageOptionsRes, error) { isTopDept, _, err := service.AgentDept().IsTopDept(ctx) if err != nil { return nil, gerror.Wrap(err, "判断当前组织类型失败") } availableBalance, err := queryAddPackageAvailableBalance(ctx, contexts.GetDeptId(ctx)) if err != nil { return nil, err } return &model.TSimAddPackageOptionsRes{ Items: []*model.TSimAddPackageOptionModel{}, IsTopDept: isTopDept, DefaultPaymentMode: addPackageDefaultPaymentMode(isTopDept), AvailableBalance: availableBalance, }, nil } func queryAddPackageAvailableBalance(ctx context.Context, deptID int64) (float64, error) { if deptID <= 0 || strings.TrimSpace(contexts.GetTenantId(ctx)) == "" { return 0, gerror.New("当前用户未绑定组织或租户") } var account struct { ID int64 `orm:"id"` Balance float64 `orm:"balance"` } columns := dao.TAgentBalance.Columns() if err := dao.TAgentBalance.Ctx(ctx). Fields(columns.Id, columns.Balance). Where(columns.DeptId, deptID). Where(columns.TenantId, contexts.GetTenantId(ctx)). Scan(&account); err != nil { if errors.Is(err, sql.ErrNoRows) { return 0, nil } return 0, gerror.Wrap(err, "查询账户余额失败") } if account.ID == 0 { return 0, nil } return roundAmount(account.Balance), nil }