59eb34976a
iotlcm-admin-svr:管理后台服务。 - Go module:iotlcm → git.5iot.com/iotlcm-public/iotlcm-admin-svr(729 文件) - 凭据与地址脱敏:JWT signingKey(含代码里的 MustGet 默认值)、 MySQL DSN、Redis 口令、告警邮件发件人邮箱全部改为占位; 私网 IP → 文档地址段;库名 xiujie_admin → iotlcm - 真实手机号/邮箱 → 合法 11 位编造号 / example.com - 移除内部资产:CI 触发器、过时 DDL(sql/db_iotlcm.sql)、 私有依赖 go get 行、明文 registry 登录口令 - 启动 banner 统一 IOTLCM 方块字 + 社区惯例署名 - Dockerfile 改用公共基础镜像 - 拼错的 RADEME.md → README.md,README 重写 - 需要真实数据库的用例改为可用 IOTLCM_TEST_WITH_DB=1 显式启用 - 补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes 数据库 schema 不随本仓发布(生产 DDL 已过时),README 已说明。
1042 lines
36 KiB
Go
1042 lines
36 KiB
Go
package renew
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchimport"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/logic/iot/packageduration"
|
|
"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"
|
|
|
|
"github.com/gogf/gf/v2/database/gdb"
|
|
"github.com/gogf/gf/v2/encoding/gjson"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
)
|
|
|
|
const (
|
|
renewPaymentModeOffline = "offline"
|
|
renewPaymentModeBalance = "balance"
|
|
|
|
renewFinanceIncomeExpenseExpense = 1
|
|
renewFinanceTransactionTypeConsume = 1
|
|
renewFinanceTransactionChannelBalance = 3
|
|
renewFinanceTransactionStatusPaid = 1
|
|
renewFinanceAccountStatusNormal = 0
|
|
)
|
|
|
|
type executionOptions struct {
|
|
PriceSnapshot *model.TSimCardRenewPriceSnapshot
|
|
PaymentIntent *model.TSimCardRenewPaymentSnapshot
|
|
PackageCode string
|
|
DurationYears int
|
|
DurationMonths int
|
|
GiftDurationMonths int
|
|
PaymentMode string
|
|
OfflineContract bool
|
|
RenewDate string
|
|
PackageBeginTime string
|
|
PackageEndTime string
|
|
SourceBatchNo string
|
|
BuyerNote string
|
|
AdminNote string
|
|
Remark string
|
|
}
|
|
|
|
type paymentExecutionInput struct {
|
|
BatchNo string
|
|
PriceSnapshot *model.TSimCardRenewPriceSnapshot
|
|
Quantity int
|
|
SubmitParam *model.TSimCardRenewBatchSubmitParam
|
|
Operator batchimport.OperatorContext
|
|
}
|
|
|
|
func loadExecutionOptions(ctx context.Context, tx gdb.TX, batchNo string) (*executionOptions, error) {
|
|
batchNo = strings.TrimSpace(batchNo)
|
|
if batchNo == "" {
|
|
return nil, gerror.New("续费批次号不能为空")
|
|
}
|
|
var batch *entity.BatchMain
|
|
if err := tx.Ctx(ctx).Model(dao.BatchMain.Table()).
|
|
Fields(
|
|
dao.BatchMain.Columns().BatchInfo,
|
|
dao.BatchMain.Columns().SourceBatchNo,
|
|
).
|
|
Where(dao.BatchMain.Columns().BatchNo, batchNo).
|
|
Limit(1).
|
|
Scan(&batch); err != nil {
|
|
return nil, gerror.Wrap(err, "查询续费批次快照失败")
|
|
}
|
|
if batch == nil || batch.BatchInfo == nil {
|
|
return &executionOptions{}, nil
|
|
}
|
|
info := batch.BatchInfo
|
|
priceSnapshot := priceSnapshotFromInfo(info)
|
|
durationYears := jsonInt(info, "durationYears", "durationValue")
|
|
durationMonths := jsonInt(info, "durationMonths")
|
|
actualDurationMonths := jsonInt(info, "actualDurationMonths")
|
|
if durationYears <= 0 {
|
|
durationYears = renewDurationYearsFromLegacy(durationMonths, actualDurationMonths, priceSnapshot)
|
|
}
|
|
if durationYears > 0 {
|
|
actualDurationMonths = renewActualDurationMonths(priceSnapshot, durationYears)
|
|
}
|
|
options := &executionOptions{
|
|
PriceSnapshot: priceSnapshot,
|
|
PaymentIntent: paymentSnapshotFromInfo(info),
|
|
PackageCode: jsonString(info, "packageCode", "priceSnapshot.packageCode"),
|
|
DurationYears: durationYears,
|
|
DurationMonths: actualDurationMonths,
|
|
GiftDurationMonths: jsonInt(info, "giftDurationMonths"),
|
|
PaymentMode: jsonString(info, "paymentMode", "paymentSnapshot.paymentMode"),
|
|
OfflineContract: jsonBool(info, "offlineContractCompleted"),
|
|
RenewDate: jsonString(info, "renewDate"),
|
|
PackageBeginTime: jsonString(info, "packageBeginTime"),
|
|
PackageEndTime: jsonString(info, "packageEndTime"),
|
|
SourceBatchNo: jsonString(info, "sourceBatchNo"),
|
|
BuyerNote: jsonString(info, "buyerNote"),
|
|
AdminNote: jsonString(info, "adminNote"),
|
|
Remark: jsonString(info, "remark"),
|
|
}
|
|
if options.SourceBatchNo == "" {
|
|
options.SourceBatchNo = strings.TrimSpace(batch.SourceBatchNo)
|
|
}
|
|
return options, nil
|
|
}
|
|
|
|
func applyExecutionOptions(in *model.TSimCardRenewBatchSubmitParam, options *executionOptions) {
|
|
if in == nil || options == nil {
|
|
return
|
|
}
|
|
if in.DurationYears <= 0 && options.DurationYears > 0 {
|
|
in.DurationYears = options.DurationYears
|
|
}
|
|
if in.DurationMonths <= 0 && options.DurationMonths > 0 {
|
|
in.DurationMonths = options.DurationMonths
|
|
}
|
|
if in.DurationYears <= 0 && in.DurationMonths > 0 {
|
|
in.DurationYears = renewDurationYearsFromLegacy(in.DurationMonths, 0, options.PriceSnapshot)
|
|
}
|
|
if in.GiftDurationMonths <= 0 && options.GiftDurationMonths > 0 {
|
|
in.GiftDurationMonths = options.GiftDurationMonths
|
|
}
|
|
if strings.TrimSpace(in.PaymentMode) == "" {
|
|
in.PaymentMode = strings.TrimSpace(options.PaymentMode)
|
|
}
|
|
if strings.TrimSpace(in.PackageCode) == "" {
|
|
in.PackageCode = strings.TrimSpace(options.PackageCode)
|
|
}
|
|
if in.PaymentSnapshot == nil {
|
|
in.PaymentSnapshot = options.PaymentIntent
|
|
}
|
|
if !in.OfflineContractCompleted {
|
|
in.OfflineContractCompleted = options.OfflineContract
|
|
}
|
|
if strings.TrimSpace(in.SourceBatchNo) == "" {
|
|
in.SourceBatchNo = strings.TrimSpace(options.SourceBatchNo)
|
|
}
|
|
if strings.TrimSpace(in.PackageBeginTime) == "" {
|
|
in.PackageBeginTime = strings.TrimSpace(options.PackageBeginTime)
|
|
}
|
|
if strings.TrimSpace(in.PackageEndTime) == "" {
|
|
in.PackageEndTime = strings.TrimSpace(options.PackageEndTime)
|
|
}
|
|
if strings.TrimSpace(in.BuyerNote) == "" {
|
|
in.BuyerNote = strings.TrimSpace(options.BuyerNote)
|
|
}
|
|
if strings.TrimSpace(in.AdminNote) == "" {
|
|
in.AdminNote = strings.TrimSpace(options.AdminNote)
|
|
}
|
|
if strings.TrimSpace(in.Remark) == "" {
|
|
in.Remark = strings.TrimSpace(options.Remark)
|
|
}
|
|
}
|
|
|
|
func ensurePriceSnapshot(ctx context.Context, tx gdb.TX, operator batchimport.OperatorContext, prepared *PrepareContext, existing *model.TSimCardRenewPriceSnapshot) (*model.TSimCardRenewPriceSnapshot, error) {
|
|
if prepared == nil {
|
|
return nil, gerror.New("续费预处理上下文不能为空")
|
|
}
|
|
snapshot := existing
|
|
if snapshot != nil {
|
|
if snapshot.CustomerAgentId > 0 && snapshot.CustomerAgentId != prepared.CustomerAgentId {
|
|
return nil, gerror.New("卡归属客户已变化,请重新预处理")
|
|
}
|
|
targetPackageCode := strings.TrimSpace(snapshot.PackageCode)
|
|
currentPackageCode := strings.TrimSpace(prepared.PackageCode)
|
|
if targetPackageCode != "" && currentPackageCode != "" && targetPackageCode != currentPackageCode {
|
|
renewableCodes, renewablePackages, err := renewablePackageOptionsTx(ctx, tx, operator.TenantId, prepared.CustomerAgentId, currentPackageCode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !containsRenewPackageCode(renewableCodes, targetPackageCode) {
|
|
return nil, gerror.New("续费套餐不在当前套餐可续费范围内")
|
|
}
|
|
snapshot.RenewablePackageCodes = renewableCodes
|
|
snapshot.RenewablePackages = renewablePackages
|
|
}
|
|
}
|
|
if snapshot == nil ||
|
|
strings.TrimSpace(snapshot.PackageCode) == "" ||
|
|
snapshot.RenewUnitPrice < 0 ||
|
|
len(snapshot.RenewableDurationOptions) == 0 ||
|
|
snapshot.BillingMethod == 0 ||
|
|
strings.TrimSpace(snapshot.RenewDurationUnit) == "" {
|
|
var err error
|
|
snapshot, err = buildRenewPriceSnapshotTx(ctx, tx, operator.TenantId, prepared.CustomerAgentId, prepared.PackageCode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if snapshot.CustomerAgentId <= 0 {
|
|
snapshot.CustomerAgentId = prepared.CustomerAgentId
|
|
}
|
|
if strings.TrimSpace(snapshot.PackageCode) == "" {
|
|
snapshot.PackageCode = prepared.PackageCode
|
|
}
|
|
if len(snapshot.RenewablePackageCodes) == 0 {
|
|
renewableCodes, renewablePackages, err := renewablePackageOptionsTx(ctx, tx, operator.TenantId, prepared.CustomerAgentId, prepared.PackageCode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
snapshot.RenewablePackageCodes = renewableCodes
|
|
snapshot.RenewablePackages = renewablePackages
|
|
}
|
|
|
|
// Keep old batch snapshots readable, but never let the legacy billing method
|
|
// control renewal or flow-cycle behavior.
|
|
snapshot.BillingMethod = consts.BillingMethodMonth
|
|
snapshot.RenewDurationUnit = consts.RenewDurationUnitYear
|
|
snapshot.RenewDurationUnitText = consts.RenewDurationUnitText(consts.RenewDurationUnitYear)
|
|
snapshot.RenewBeginDate = prepared.RenewBeginDate
|
|
snapshot.RenewBeginDateMin = prepared.RenewBeginDateMin
|
|
snapshot.RenewBeginDateMax = prepared.RenewBeginDateMax
|
|
snapshot.SameRenewBeginDate = prepared.SameRenewBeginDate
|
|
return snapshot, nil
|
|
}
|
|
|
|
func executeRenewPayment(ctx context.Context, tx gdb.TX, in paymentExecutionInput) (*model.TSimCardRenewPaymentSnapshot, error) {
|
|
if in.SubmitParam == nil {
|
|
return nil, gerror.New("续费付款参数不能为空")
|
|
}
|
|
isTop, err := operatorIsTopDept(ctx, tx, in.Operator)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
mode, err := normalizePaymentMode(firstNonBlank(in.SubmitParam.PaymentMode, paymentModeOf(in.SubmitParam.PaymentSnapshot)), isTop)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
amount, err := resolveRenewAmount(in.PriceSnapshot, in.SubmitParam, in.Quantity)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
now := gtime.Now()
|
|
switch mode {
|
|
case renewPaymentModeOffline:
|
|
if !in.SubmitParam.OfflineContractCompleted {
|
|
return nil, gerror.New("请确认线下合同、销售和走账已完成")
|
|
}
|
|
return &model.TSimCardRenewPaymentSnapshot{
|
|
PaymentMode: renewPaymentModeOffline,
|
|
PayType: consts.PayTypeOffline,
|
|
PayStatus: consts.PayStatusPaid,
|
|
PayAmount: amount,
|
|
OfflinePaidAmount: amount,
|
|
PaymentTime: now,
|
|
PayerDeptId: renewPayerDeptID(in.PriceSnapshot, in.Operator.DeptId),
|
|
PayerUserId: in.Operator.UserId,
|
|
IsTopDept: isTop,
|
|
}, nil
|
|
case renewPaymentModeBalance:
|
|
payerDeptID := renewPayerDeptID(in.PriceSnapshot, in.Operator.DeptId)
|
|
return deductRenewBalanceTx(ctx, tx, in.BatchNo, amount, now, in.Operator, payerDeptID, isTop)
|
|
default:
|
|
return nil, gerror.New("续费付款方式不正确")
|
|
}
|
|
}
|
|
|
|
func deductRenewBalanceTx(ctx context.Context, tx gdb.TX, batchNo string, amount float64, paymentTime *gtime.Time, operator batchimport.OperatorContext, payerDeptID int64, isTop bool) (*model.TSimCardRenewPaymentSnapshot, error) {
|
|
if amount < 0 {
|
|
return nil, gerror.New("续费金额不正确")
|
|
}
|
|
if operator.DeptId <= 0 || operator.TenantId == "" || operator.UserId <= 0 {
|
|
return nil, gerror.New("当前用户未绑定组织或租户")
|
|
}
|
|
if payerDeptID <= 0 {
|
|
payerDeptID = operator.DeptId
|
|
}
|
|
if paymentTime == nil {
|
|
paymentTime = gtime.Now()
|
|
}
|
|
if amount == 0 {
|
|
balance, _ := currentAgentBalanceTx(ctx, tx, operator, payerDeptID)
|
|
return &model.TSimCardRenewPaymentSnapshot{
|
|
PaymentMode: renewPaymentModeBalance,
|
|
PayType: consts.PayTypeBalance,
|
|
PayStatus: consts.PayStatusPaid,
|
|
PayAmount: 0,
|
|
BalanceAmount: 0,
|
|
BalanceBefore: balance,
|
|
BalanceAfter: balance,
|
|
PaymentTime: paymentTime,
|
|
PayerDeptId: payerDeptID,
|
|
PayerUserId: operator.UserId,
|
|
IsTopDept: isTop,
|
|
}, nil
|
|
}
|
|
serialNo := renewBalanceSerialNo(batchNo)
|
|
if serialNo == "" {
|
|
return nil, gerror.New("续费余额流水号生成失败")
|
|
}
|
|
|
|
account := &entity.TAgentBalance{}
|
|
if err := tx.Ctx(ctx).Model(dao.TAgentBalance.Table()).
|
|
Where(dao.TAgentBalance.Columns().DeptId, payerDeptID).
|
|
Where(dao.TAgentBalance.Columns().TenantId, operator.TenantId).
|
|
LockUpdate().
|
|
Scan(account); err != nil {
|
|
return nil, gerror.Wrap(err, "查询账户余额失败")
|
|
}
|
|
if account.Id == 0 {
|
|
return nil, gerror.New("账户余额不存在,请先充值")
|
|
}
|
|
if account.Status != renewFinanceAccountStatusNormal {
|
|
return nil, gerror.New("账户余额已冻结,不能扣余额")
|
|
}
|
|
record, err := tx.Ctx(ctx).Model(dao.TBalanceDetail.Table()).
|
|
Where(dao.TBalanceDetail.Columns().TenantId, operator.TenantId).
|
|
Where(dao.TBalanceDetail.Columns().SerialNo, serialNo).
|
|
LockUpdate().
|
|
One()
|
|
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
|
return nil, gerror.Wrap(err, "查询续费余额流水失败")
|
|
}
|
|
if record != nil && !record.IsEmpty() {
|
|
return nil, gerror.New("当前续费批次已扣款,请勿重复提交")
|
|
}
|
|
|
|
before := roundRenewAmount(account.Balance)
|
|
if before+0.0001 < amount {
|
|
return nil, gerror.Newf("账户余额不足,需支付%.2f元,当前余额%.2f元", amount, before)
|
|
}
|
|
after := roundRenewAmount(before - amount)
|
|
now := gtime.Now()
|
|
result, err := tx.Ctx(ctx).Model(dao.TAgentBalance.Table()).
|
|
WherePri(account.Id).
|
|
Data(g.Map{
|
|
dao.TAgentBalance.Columns().Balance: after,
|
|
dao.TAgentBalance.Columns().UpdatedBy: operator.UserId,
|
|
dao.TAgentBalance.Columns().UpdatedAt: now,
|
|
}).
|
|
Update()
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "扣减账户余额失败")
|
|
}
|
|
affected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "读取账户余额更新结果失败")
|
|
}
|
|
if affected != 1 {
|
|
return nil, gerror.New("账户余额已变化,请重试")
|
|
}
|
|
|
|
if _, err = tx.Ctx(ctx).Model(dao.TBalanceDetail.Table()).Data(g.Map{
|
|
dao.TBalanceDetail.Columns().DeptId: payerDeptID,
|
|
dao.TBalanceDetail.Columns().SerialNo: serialNo,
|
|
dao.TBalanceDetail.Columns().TransactionTime: paymentTime,
|
|
dao.TBalanceDetail.Columns().IncomeExpenseType: renewFinanceIncomeExpenseExpense,
|
|
dao.TBalanceDetail.Columns().TransactionType: renewFinanceTransactionTypeConsume,
|
|
dao.TBalanceDetail.Columns().TransactionChannel: renewFinanceTransactionChannelBalance,
|
|
dao.TBalanceDetail.Columns().TransactionStatus: renewFinanceTransactionStatusPaid,
|
|
dao.TBalanceDetail.Columns().Amount: amount,
|
|
dao.TBalanceDetail.Columns().BeforeBalance: before,
|
|
dao.TBalanceDetail.Columns().BalanceAfter: after,
|
|
dao.TBalanceDetail.Columns().OrderSn: batchNo,
|
|
dao.TBalanceDetail.Columns().Remark: fmt.Sprintf("SIM卡续费余额支付,批次号:%s", batchNo),
|
|
dao.TBalanceDetail.Columns().TenantId: operator.TenantId,
|
|
dao.TBalanceDetail.Columns().CreatedDept: operator.DeptId,
|
|
dao.TBalanceDetail.Columns().CreatedBy: operator.UserId,
|
|
dao.TBalanceDetail.Columns().CreatedAt: now,
|
|
}).Insert(); err != nil {
|
|
return nil, gerror.Wrap(err, "写入续费余额流水失败")
|
|
}
|
|
|
|
return &model.TSimCardRenewPaymentSnapshot{
|
|
PaymentMode: renewPaymentModeBalance,
|
|
PayType: consts.PayTypeBalance,
|
|
PayStatus: consts.PayStatusPaid,
|
|
PayAmount: amount,
|
|
BalanceAmount: amount,
|
|
BalanceBefore: before,
|
|
BalanceAfter: after,
|
|
BalanceDetailSerialNo: serialNo,
|
|
PaymentTime: paymentTime,
|
|
PayerDeptId: payerDeptID,
|
|
PayerUserId: operator.UserId,
|
|
IsTopDept: isTop,
|
|
}, nil
|
|
}
|
|
|
|
func buildRenewPriceSnapshotTx(ctx context.Context, tx gdb.TX, tenantID string, customerAgentID int64, packageCode string) (*model.TSimCardRenewPriceSnapshot, error) {
|
|
packageCode = strings.TrimSpace(packageCode)
|
|
if tenantID == "" {
|
|
return nil, gerror.New("租户不能为空")
|
|
}
|
|
if customerAgentID <= 0 {
|
|
return nil, gerror.New("客户不能为空")
|
|
}
|
|
if packageCode == "" {
|
|
return nil, gerror.New("套餐不能为空")
|
|
}
|
|
pkg := &entity.TSimFlowPackageInfo{}
|
|
if err := tx.Ctx(ctx).Model(dao.TSimFlowPackageInfo.Table()).
|
|
Where(dao.TSimFlowPackageInfo.Columns().TenantId, tenantID).
|
|
Where(dao.TSimFlowPackageInfo.Columns().PackageCode, packageCode).
|
|
Scan(pkg); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, gerror.New("续费套餐不存在")
|
|
}
|
|
return nil, gerror.Wrap(err, "查询续费套餐失败")
|
|
}
|
|
if pkg.Id == 0 {
|
|
return nil, gerror.New("续费套餐不存在")
|
|
}
|
|
options, err := renewableDurationOptionsTx(pkg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
durationUnit := consts.RenewDurationUnitYear
|
|
snapshot := &model.TSimCardRenewPriceSnapshot{
|
|
CustomerAgentId: customerAgentID,
|
|
PackageCode: pkg.PackageCode,
|
|
PackageName: pkg.PackageName,
|
|
BillingMethod: consts.BillingMethodMonth,
|
|
RenewDurationUnit: durationUnit,
|
|
RenewDurationUnitText: consts.RenewDurationUnitText(durationUnit),
|
|
PriceSource: "system",
|
|
RenewUnitPrice: roundRenewAmount(pkg.CostPrice*normalizeRenewPriceRatio(pkg.SysDefaultRatio) + pkg.SysDefaultProfit),
|
|
RenewableDurationOptions: options,
|
|
}
|
|
renewablePackageCodes, renewablePackages, err := renewablePackageOptionsTx(ctx, tx, tenantID, customerAgentID, packageCode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
snapshot.RenewablePackageCodes = renewablePackageCodes
|
|
snapshot.RenewablePackages = renewablePackages
|
|
priceOwnerDeptID, err := loadRenewPriceOwnerDeptIDTx(ctx, tx, tenantID, customerAgentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
snapshot.PriceOwnerDeptId = priceOwnerDeptID
|
|
salePrice, err := packageprice.ResolveEffectiveSalePrice(ctx, tx, tenantID, priceOwnerDeptID, customerAgentID, packageprice.SourceFromPackageInfo(pkg))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
snapshot.PriceSource = salePrice.Rule.PriceSource
|
|
snapshot.AgentDefaultPriceId = salePrice.Rule.AgentDefaultPriceId
|
|
snapshot.TargetPriceId = salePrice.Rule.TargetPriceId
|
|
snapshot.RenewUnitPrice = salePrice.AgentShipmentPrice
|
|
return snapshot, nil
|
|
}
|
|
|
|
func loadRenewPriceOwnerDeptIDTx(ctx context.Context, tx gdb.TX, tenantID string, customerAgentID int64) (int64, error) {
|
|
dept := &entity.SysDept{}
|
|
if err := tx.Ctx(ctx).Model(dao.SysDept.Table()).
|
|
Where(dao.SysDept.Columns().TenantId, tenantID).
|
|
Where(dao.SysDept.Columns().DeptId, customerAgentID).
|
|
WhereNull(dao.SysDept.Columns().DeletedAt).
|
|
Scan(dept); 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 loadRenewPriceOverrideTx(ctx context.Context, tx gdb.TX, tenantID string, deptID int64, scopeType int, targetAgentID int64, packageCode string) (*entity.TSimFlowPackagePrice, error) {
|
|
price := &entity.TSimFlowPackagePrice{}
|
|
if err := tx.Ctx(ctx).Model(dao.TSimFlowPackagePrice.Table()).
|
|
Where(dao.TSimFlowPackagePrice.Columns().TenantId, tenantID).
|
|
Where(dao.TSimFlowPackagePrice.Columns().DeptId, deptID).
|
|
Where(dao.TSimFlowPackagePrice.Columns().AgentScopeType, scopeType).
|
|
Where(dao.TSimFlowPackagePrice.Columns().TargetAgentId, targetAgentID).
|
|
Where(dao.TSimFlowPackagePrice.Columns().PackageCode, packageCode).
|
|
Where(dao.TSimFlowPackagePrice.Columns().Enabled, 1).
|
|
Scan(price); 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
|
|
}
|
|
|
|
type renewPackageEffectiveSalePrice struct {
|
|
Rule renewPackageEffectivePriceRule
|
|
AgentShipmentPrice float64
|
|
}
|
|
|
|
type renewPackageEffectivePriceRule struct {
|
|
PriceSource string
|
|
AgentDefaultPriceId int64
|
|
TargetPriceId int64
|
|
AgentCostRatio float64
|
|
AgentCostProfit float64
|
|
}
|
|
|
|
func resolvePackageEffectiveSalePrice(ctx context.Context, tx gdb.TX, tenantID string, ownerDeptID int64, targetDeptID int64, pkg *entity.TSimFlowPackageInfo) (renewPackageEffectiveSalePrice, error) {
|
|
baseCost, err := resolvePackageEffectiveAgentBaseCost(ctx, tx, tenantID, ownerDeptID, pkg, make(map[int64]struct{}))
|
|
if err != nil {
|
|
return renewPackageEffectiveSalePrice{}, err
|
|
}
|
|
rule, err := resolvePackageEffectivePriceRule(ctx, tx, tenantID, ownerDeptID, targetDeptID, pkg)
|
|
if err != nil {
|
|
return renewPackageEffectiveSalePrice{}, err
|
|
}
|
|
return renewPackageEffectiveSalePrice{
|
|
Rule: rule,
|
|
AgentShipmentPrice: roundRenewAmount(baseCost*rule.AgentCostRatio + rule.AgentCostProfit),
|
|
}, nil
|
|
}
|
|
|
|
func resolvePackageEffectiveAgentBaseCost(ctx context.Context, tx gdb.TX, tenantID string, deptID int64, pkg *entity.TSimFlowPackageInfo, visited map[int64]struct{}) (float64, error) {
|
|
if pkg == nil || strings.TrimSpace(pkg.PackageCode) == "" {
|
|
return 0, gerror.New("套餐编码不能为空")
|
|
}
|
|
if deptID <= 0 {
|
|
return roundRenewAmount(pkg.CostPrice), nil
|
|
}
|
|
if _, ok := visited[deptID]; ok {
|
|
return 0, gerror.New("组织定价层级存在循环,无法计算生效成本")
|
|
}
|
|
visited[deptID] = struct{}{}
|
|
|
|
parentID, err := loadRenewPriceOwnerDeptIDTx(ctx, tx, tenantID, deptID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if parentID <= 0 {
|
|
return roundRenewAmount(pkg.CostPrice), nil
|
|
}
|
|
parentBaseCost, err := resolvePackageEffectiveAgentBaseCost(ctx, tx, tenantID, parentID, pkg, visited)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
parentRule, err := resolvePackageEffectivePriceRule(ctx, tx, tenantID, parentID, deptID, pkg)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return roundRenewAmount(parentBaseCost*parentRule.AgentCostRatio + parentRule.AgentCostProfit), nil
|
|
}
|
|
|
|
func resolvePackageEffectivePriceRule(ctx context.Context, tx gdb.TX, tenantID string, ownerDeptID int64, targetDeptID int64, pkg *entity.TSimFlowPackageInfo) (renewPackageEffectivePriceRule, error) {
|
|
rule := renewPackageEffectivePriceRule{
|
|
PriceSource: "system",
|
|
AgentCostRatio: normalizeRenewPriceRatio(pkg.SysDefaultRatio),
|
|
AgentCostProfit: pkg.SysDefaultProfit,
|
|
}
|
|
if ownerDeptID <= 0 {
|
|
return rule, nil
|
|
}
|
|
if targetDeptID > 0 {
|
|
targetPrice, err := loadRenewPriceOverrideTx(ctx, tx, tenantID, ownerDeptID, 1, targetDeptID, pkg.PackageCode)
|
|
if err != nil {
|
|
return rule, err
|
|
}
|
|
if targetPrice != nil {
|
|
return renewPackageEffectivePriceRule{
|
|
PriceSource: "target",
|
|
TargetPriceId: targetPrice.Id,
|
|
AgentCostRatio: normalizeRenewPriceRatio(targetPrice.AgentCostRatio),
|
|
AgentCostProfit: targetPrice.AgentCostProfit,
|
|
}, nil
|
|
}
|
|
}
|
|
defaultPrice, err := loadRenewPriceOverrideTx(ctx, tx, tenantID, ownerDeptID, 0, 0, pkg.PackageCode)
|
|
if err != nil {
|
|
return rule, err
|
|
}
|
|
if defaultPrice != nil {
|
|
return renewPackageEffectivePriceRule{
|
|
PriceSource: "agentDefault",
|
|
AgentDefaultPriceId: defaultPrice.Id,
|
|
AgentCostRatio: normalizeRenewPriceRatio(defaultPrice.AgentCostRatio),
|
|
AgentCostProfit: defaultPrice.AgentCostProfit,
|
|
}, nil
|
|
}
|
|
return rule, nil
|
|
}
|
|
|
|
func renewableDurationOptionsTx(pkg *entity.TSimFlowPackageInfo) ([]int, error) {
|
|
return packageduration.RenewableYears(pkg)
|
|
}
|
|
|
|
func renewablePackageOptionsTx(ctx context.Context, tx gdb.TX, tenantID string, customerAgentID int64, currentPackageCode string) ([]string, []model.TSimCardRenewPackageOption, error) {
|
|
currentPackageCode = strings.TrimSpace(currentPackageCode)
|
|
if tenantID == "" || currentPackageCode == "" {
|
|
return nil, nil, nil
|
|
}
|
|
current := &entity.TSimFlowPackageInfo{}
|
|
if err := tx.Ctx(ctx).Model(dao.TSimFlowPackageInfo.Table()).
|
|
Fields(
|
|
dao.TSimFlowPackageInfo.Columns().PackageCode,
|
|
dao.TSimFlowPackageInfo.Columns().PackageName,
|
|
dao.TSimFlowPackageInfo.Columns().RenewablePackageCodes,
|
|
).
|
|
Where(dao.TSimFlowPackageInfo.Columns().TenantId, tenantID).
|
|
Where(dao.TSimFlowPackageInfo.Columns().PackageCode, currentPackageCode).
|
|
Scan(current); err != nil {
|
|
return nil, nil, gerror.Wrap(err, "查询可续费套餐配置失败")
|
|
}
|
|
if current.PackageCode == "" {
|
|
return nil, nil, gerror.New("续费套餐不存在")
|
|
}
|
|
codes := renewablePackageCodesFromPackage(current)
|
|
if len(codes) == 0 {
|
|
return nil, nil, gerror.New("当前套餐未配置可续费套餐")
|
|
}
|
|
|
|
var rows []*entity.TSimFlowPackageInfo
|
|
if err := tx.Ctx(ctx).Model(dao.TSimFlowPackageInfo.Table()).
|
|
Fields(
|
|
dao.TSimFlowPackageInfo.Columns().PackageCode,
|
|
dao.TSimFlowPackageInfo.Columns().PackageName,
|
|
dao.TSimFlowPackageInfo.Columns().CostPrice,
|
|
dao.TSimFlowPackageInfo.Columns().TerminalCost,
|
|
dao.TSimFlowPackageInfo.Columns().SysDefaultRatio,
|
|
dao.TSimFlowPackageInfo.Columns().SysDefaultProfit,
|
|
dao.TSimFlowPackageInfo.Columns().SysDefaultTerminalRatio,
|
|
dao.TSimFlowPackageInfo.Columns().SysDefaultTerminalProfit,
|
|
dao.TSimFlowPackageInfo.Columns().RenewableDurationConfig,
|
|
).
|
|
Where(dao.TSimFlowPackageInfo.Columns().TenantId, tenantID).
|
|
WhereIn(dao.TSimFlowPackageInfo.Columns().PackageCode, codes).
|
|
Scan(&rows); err != nil {
|
|
return nil, nil, gerror.Wrap(err, "查询可续费套餐列表失败")
|
|
}
|
|
nameByCode := make(map[string]string, len(rows))
|
|
packageByCode := make(map[string]*entity.TSimFlowPackageInfo, len(rows))
|
|
for _, row := range rows {
|
|
if row == nil {
|
|
continue
|
|
}
|
|
code := strings.TrimSpace(row.PackageCode)
|
|
nameByCode[code] = strings.TrimSpace(row.PackageName)
|
|
packageByCode[code] = row
|
|
}
|
|
priceOwnerDeptID, err := loadRenewPriceOwnerDeptIDTx(ctx, tx, tenantID, customerAgentID)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
options := make([]model.TSimCardRenewPackageOption, 0, len(codes))
|
|
for _, code := range codes {
|
|
name, ok := nameByCode[code]
|
|
if !ok {
|
|
continue
|
|
}
|
|
pkg := packageByCode[code]
|
|
salePrice, err := packageprice.ResolveEffectiveSalePrice(ctx, tx, tenantID, priceOwnerDeptID, customerAgentID, packageprice.SourceFromPackageInfo(pkg))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
unitPrice := salePrice.AgentShipmentPrice
|
|
durationOptions, err := renewableDurationOptionsTx(pkg)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
options = append(options, model.TSimCardRenewPackageOption{
|
|
PackageCode: code,
|
|
PackageName: name,
|
|
RenewUnitPrice: unitPrice,
|
|
RenewableDurationOptions: durationOptions,
|
|
})
|
|
}
|
|
return codes, options, nil
|
|
}
|
|
|
|
func renewablePackageCodesFromPackage(pkg *entity.TSimFlowPackageInfo) []string {
|
|
if pkg == nil {
|
|
return nil
|
|
}
|
|
var codes []string
|
|
if pkg.RenewablePackageCodes != nil {
|
|
_ = pkg.RenewablePackageCodes.Scan(&codes)
|
|
}
|
|
return uniqueStrings(codes)
|
|
}
|
|
|
|
func containsRenewPackageCode(codes []string, target string) bool {
|
|
target = strings.TrimSpace(target)
|
|
if target == "" {
|
|
return false
|
|
}
|
|
for _, code := range codes {
|
|
if strings.TrimSpace(code) == target {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func operatorIsTopDept(ctx context.Context, tx gdb.TX, operator batchimport.OperatorContext) (bool, error) {
|
|
if operator.DeptId <= 0 || operator.TenantId == "" {
|
|
return false, gerror.New("当前用户未绑定组织或租户")
|
|
}
|
|
dept := &entity.SysDept{}
|
|
if err := tx.Ctx(ctx).Model(dao.SysDept.Table()).
|
|
Fields(dao.SysDept.Columns().DeptId, dao.SysDept.Columns().ParentId, dao.SysDept.Columns().Ancestors).
|
|
Where(dao.SysDept.Columns().TenantId, operator.TenantId).
|
|
Where(dao.SysDept.Columns().DeptId, operator.DeptId).
|
|
WhereNull(dao.SysDept.Columns().DeletedAt).
|
|
Scan(dept); err != nil {
|
|
return false, gerror.Wrap(err, "查询当前组织失败")
|
|
}
|
|
if dept.DeptId == 0 {
|
|
return false, gerror.New("当前组织不存在")
|
|
}
|
|
return dept.ParentId == 0 || strings.Trim(dept.Ancestors, ",") == "0", nil
|
|
}
|
|
|
|
func currentAgentBalanceTx(ctx context.Context, tx gdb.TX, operator batchimport.OperatorContext, deptID int64) (float64, error) {
|
|
if deptID <= 0 {
|
|
deptID = operator.DeptId
|
|
}
|
|
account := &entity.TAgentBalance{}
|
|
if err := tx.Ctx(ctx).Model(dao.TAgentBalance.Table()).
|
|
Fields(dao.TAgentBalance.Columns().Id, dao.TAgentBalance.Columns().Balance).
|
|
Where(dao.TAgentBalance.Columns().DeptId, deptID).
|
|
Where(dao.TAgentBalance.Columns().TenantId, operator.TenantId).
|
|
Scan(account); err != nil {
|
|
return 0, gerror.Wrap(err, "查询账户余额失败")
|
|
}
|
|
if account.Id == 0 {
|
|
return 0, nil
|
|
}
|
|
return roundRenewAmount(account.Balance), nil
|
|
}
|
|
|
|
func renewPayerDeptID(snapshot *model.TSimCardRenewPriceSnapshot, fallbackDeptID int64) int64 {
|
|
if snapshot != nil && snapshot.CustomerAgentId > 0 {
|
|
return snapshot.CustomerAgentId
|
|
}
|
|
return fallbackDeptID
|
|
}
|
|
|
|
func renewDurationUnitFromSnapshot(snapshot *model.TSimCardRenewPriceSnapshot) string {
|
|
return consts.RenewDurationUnitYear
|
|
}
|
|
|
|
func renewDisplayDurationValue(in *model.TSimCardRenewBatchSubmitParam) int {
|
|
if in == nil {
|
|
return 0
|
|
}
|
|
if in.DurationYears > 0 {
|
|
return in.DurationYears
|
|
}
|
|
return renewDurationYearsFromLegacy(in.DurationMonths, 0, nil)
|
|
}
|
|
|
|
func renewActualDurationMonths(snapshot *model.TSimCardRenewPriceSnapshot, displayValue int) int {
|
|
return packageduration.RenewMonths(displayValue)
|
|
}
|
|
|
|
func renewDurationYearsFromLegacy(durationMonths, actualDurationMonths int, snapshot *model.TSimCardRenewPriceSnapshot) int {
|
|
if actualDurationMonths > 0 {
|
|
return renewDurationYearsFromMonths(actualDurationMonths, snapshot)
|
|
}
|
|
if durationMonths > 0 {
|
|
return renewDurationYearsFromMonths(durationMonths, snapshot)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func renewDurationYearsFromMonths(months int, snapshot *model.TSimCardRenewPriceSnapshot) int {
|
|
if months <= 0 {
|
|
return 0
|
|
}
|
|
if snapshot != nil {
|
|
options := packageduration.NormalizeRenewableYears(snapshot.RenewableDurationOptions)
|
|
if containsRenewDurationOption(options, months) {
|
|
return months
|
|
}
|
|
if months%12 == 0 && containsRenewDurationOption(options, months/12) {
|
|
return months / 12
|
|
}
|
|
}
|
|
if months%12 == 0 {
|
|
return months / 12
|
|
}
|
|
return months
|
|
}
|
|
|
|
func normalizePaymentMode(value string, isTopDept bool) (string, error) {
|
|
mode := strings.TrimSpace(value)
|
|
if mode == "" {
|
|
if isTopDept {
|
|
return renewPaymentModeOffline, nil
|
|
}
|
|
return renewPaymentModeBalance, nil
|
|
}
|
|
switch mode {
|
|
case renewPaymentModeOffline:
|
|
if !isTopDept {
|
|
return "", gerror.New("当前组织续费必须使用余额支付")
|
|
}
|
|
return mode, nil
|
|
case renewPaymentModeBalance:
|
|
return mode, nil
|
|
default:
|
|
return "", gerror.New("续费付款方式不正确")
|
|
}
|
|
}
|
|
|
|
func resolveRenewAmount(snapshot *model.TSimCardRenewPriceSnapshot, in *model.TSimCardRenewBatchSubmitParam, quantity int) (float64, error) {
|
|
if in == nil {
|
|
return 0, gerror.New("续费付款参数不能为空")
|
|
}
|
|
durationValue := renewDisplayDurationValue(in)
|
|
if snapshot != nil && snapshot.RenewUnitPrice >= 0 && durationValue > 0 && quantity > 0 {
|
|
return roundRenewAmount(snapshot.RenewUnitPrice * float64(durationValue) * float64(quantity)), nil
|
|
}
|
|
amount, err := parseRenewAmount(in.Amount)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return amount, nil
|
|
}
|
|
|
|
func containsRenewDurationOption(values []int, target int) bool {
|
|
for _, value := range values {
|
|
if value == target {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func parseRenewAmount(value string) (float64, error) {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return 0, nil
|
|
}
|
|
amount, err := strconv.ParseFloat(value, 64)
|
|
if err != nil {
|
|
return 0, gerror.New("订单金额格式不正确")
|
|
}
|
|
if amount < 0 {
|
|
return 0, gerror.New("订单金额不能小于0")
|
|
}
|
|
return roundRenewAmount(amount), nil
|
|
}
|
|
|
|
func formatRenewAmount(amount float64) string {
|
|
return fmt.Sprintf("%.2f", roundRenewAmount(amount))
|
|
}
|
|
|
|
func renewBalanceSerialNo(batchNo string) string {
|
|
batchNo = strings.TrimSpace(batchNo)
|
|
if batchNo == "" {
|
|
return ""
|
|
}
|
|
return "RENPAY" + batchNo
|
|
}
|
|
|
|
func roundRenewAmount(value float64) float64 {
|
|
return math.Round(value*100) / 100
|
|
}
|
|
|
|
func normalizeRenewPriceRatio(ratio float64) float64 {
|
|
if ratio > 10 {
|
|
return ratio / 100
|
|
}
|
|
return ratio
|
|
}
|
|
|
|
func priceSnapshotFromInfo(info *gjson.Json) *model.TSimCardRenewPriceSnapshot {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
options := packageduration.NormalizeRenewableYears(jsonInts(info, "renewableDurationOptions", "priceSnapshot.renewableDurationOptions"))
|
|
snapshot := &model.TSimCardRenewPriceSnapshot{
|
|
CustomerAgentId: jsonInt64(info, "customerAgentId", "customer.deptId", "priceSnapshot.customerAgentId"),
|
|
PriceOwnerDeptId: jsonInt64(info, "priceOwnerDeptId", "priceSnapshot.priceOwnerDeptId"),
|
|
PackageCode: jsonString(info, "packageCode", "priceSnapshot.packageCode"),
|
|
PackageName: jsonString(info, "packageName", "priceSnapshot.packageName"),
|
|
BillingMethod: consts.BillingMethodMonth,
|
|
RenewDurationUnit: jsonString(info, "renewDurationUnit", "priceSnapshot.renewDurationUnit"),
|
|
RenewDurationUnitText: jsonString(info, "renewDurationUnitText", "priceSnapshot.renewDurationUnitText"),
|
|
PriceSource: jsonString(info, "priceSource", "priceSnapshot.priceSource"),
|
|
AgentDefaultPriceId: jsonInt64(info, "agentDefaultPriceId", "priceSnapshot.agentDefaultPriceId"),
|
|
TargetPriceId: jsonInt64(info, "targetPriceId", "priceSnapshot.targetPriceId"),
|
|
RenewUnitPrice: roundRenewAmount(jsonFloat64(info, "renewUnitPrice", "priceSnapshot.renewUnitPrice")),
|
|
RenewableDurationOptions: options,
|
|
RenewablePackageCodes: jsonStrings(info, "renewablePackageCodes", "priceSnapshot.renewablePackageCodes"),
|
|
RenewablePackages: renewPackageOptionsFromInfo(info, "renewablePackages", "priceSnapshot.renewablePackages"),
|
|
RenewBeginDate: jsonString(info, "renewBeginDate", "priceSnapshot.renewBeginDate"),
|
|
RenewBeginDateMin: jsonString(info, "renewBeginDateMin", "priceSnapshot.renewBeginDateMin"),
|
|
RenewBeginDateMax: jsonString(info, "renewBeginDateMax", "priceSnapshot.renewBeginDateMax"),
|
|
SameRenewBeginDate: jsonBool(info, "sameRenewBeginDate", "priceSnapshot.sameRenewBeginDate"),
|
|
}
|
|
if snapshot.CustomerAgentId == 0 && snapshot.PackageCode == "" && snapshot.RenewUnitPrice == 0 && len(snapshot.RenewableDurationOptions) == 0 {
|
|
return nil
|
|
}
|
|
snapshot.RenewDurationUnit = consts.RenewDurationUnitYear
|
|
snapshot.RenewDurationUnitText = consts.RenewDurationUnitText(snapshot.RenewDurationUnit)
|
|
snapshot.BillingMethod = consts.BillingMethodMonth
|
|
return snapshot
|
|
}
|
|
|
|
func paymentSnapshotFromInfo(info *gjson.Json) *model.TSimCardRenewPaymentSnapshot {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
mode := jsonString(info, "paymentMode", "paymentSnapshot.paymentMode")
|
|
if mode == "" {
|
|
return nil
|
|
}
|
|
return &model.TSimCardRenewPaymentSnapshot{
|
|
PaymentMode: mode,
|
|
PayType: jsonInt(info, "payType", "paymentSnapshot.payType"),
|
|
PayStatus: jsonInt(info, "payStatus", "paymentSnapshot.payStatus"),
|
|
PayAmount: jsonFloat64(info, "payAmount", "paymentSnapshot.payAmount"),
|
|
BalanceAmount: jsonFloat64(info, "balanceAmount", "paymentSnapshot.balanceAmount"),
|
|
OfflinePaidAmount: jsonFloat64(info, "offlinePaidAmount", "paymentSnapshot.offlinePaidAmount"),
|
|
BalanceBefore: jsonFloat64(info, "balanceBefore", "paymentSnapshot.balanceBefore"),
|
|
BalanceAfter: jsonFloat64(info, "balanceAfter", "paymentSnapshot.balanceAfter"),
|
|
BalanceDetailSerialNo: jsonString(info, "balanceDetailSerialNo", "paymentSnapshot.balanceDetailSerialNo"),
|
|
PayerDeptId: jsonInt64(info, "payerDeptId", "paymentSnapshot.payerDeptId"),
|
|
PayerUserId: jsonInt64(info, "payerUserId", "paymentSnapshot.payerUserId"),
|
|
IsTopDept: jsonBool(info, "isTopDept", "paymentSnapshot.isTopDept"),
|
|
}
|
|
}
|
|
|
|
func paymentModeOf(snapshot *model.TSimCardRenewPaymentSnapshot) string {
|
|
if snapshot == nil {
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(snapshot.PaymentMode)
|
|
}
|
|
|
|
func firstNonBlank(values ...string) string {
|
|
for _, value := range values {
|
|
if strings.TrimSpace(value) != "" {
|
|
return strings.TrimSpace(value)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func jsonString(info *gjson.Json, paths ...string) string {
|
|
for _, path := range paths {
|
|
if value := info.Get(path); value != nil && !value.IsNil() {
|
|
return strings.TrimSpace(value.String())
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func jsonInt(info *gjson.Json, paths ...string) int {
|
|
for _, path := range paths {
|
|
if value := info.Get(path); value != nil && !value.IsNil() {
|
|
return value.Int()
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func jsonInt64(info *gjson.Json, paths ...string) int64 {
|
|
for _, path := range paths {
|
|
if value := info.Get(path); value != nil && !value.IsNil() {
|
|
return value.Int64()
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func jsonFloat64(info *gjson.Json, paths ...string) float64 {
|
|
for _, path := range paths {
|
|
if value := info.Get(path); value != nil && !value.IsNil() {
|
|
return value.Float64()
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func jsonBool(info *gjson.Json, paths ...string) bool {
|
|
for _, path := range paths {
|
|
if value := info.Get(path); value != nil && !value.IsNil() {
|
|
return value.Bool()
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func jsonStrings(info *gjson.Json, paths ...string) []string {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
for _, path := range paths {
|
|
value := info.Get(path)
|
|
if value == nil || value.IsNil() {
|
|
continue
|
|
}
|
|
values := value.Strings()
|
|
if len(values) > 0 || len(value.Interfaces()) > 0 {
|
|
return uniqueStrings(values)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func renewPackageOptionsFromInfo(info *gjson.Json, paths ...string) []model.TSimCardRenewPackageOption {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
for _, path := range paths {
|
|
value := info.Get(path)
|
|
if value == nil || value.IsNil() {
|
|
continue
|
|
}
|
|
var options []model.TSimCardRenewPackageOption
|
|
if err := gconv.Structs(value.Interfaces(), &options); err == nil && len(options) > 0 {
|
|
return options
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func jsonInts(info *gjson.Json, paths ...string) []int {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
for _, path := range paths {
|
|
value := info.Get(path)
|
|
if value == nil || value.IsNil() {
|
|
continue
|
|
}
|
|
values := value.Ints()
|
|
if len(values) > 0 || len(value.Interfaces()) > 0 {
|
|
return values
|
|
}
|
|
}
|
|
return nil
|
|
}
|