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 已说明。
1597 lines
54 KiB
Go
1597 lines
54 KiB
Go
package iot
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchmetadata"
|
|
"math"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"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/library/contexts"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/rabbitmq"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/logic/iot/packageduration"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/logic/iot/simcardbatch/renew"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/do"
|
|
"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/container/gvar"
|
|
"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"
|
|
)
|
|
|
|
type sIotTSimCardRenew struct{}
|
|
|
|
const (
|
|
renewPriceChangedMessage = "续费套餐定价已更新,确认页已刷新,请核对最新单价及应付金额后重新提交"
|
|
renewPrepareChangedMessage = "续费条件或套餐定价已更新,预处理结果已刷新,请核对最新有效卡数量、失败卡及应付金额后重新提交"
|
|
)
|
|
|
|
func NewIotTSimCardRenew() *sIotTSimCardRenew {
|
|
return &sIotTSimCardRenew{}
|
|
}
|
|
|
|
func init() {
|
|
service.RegisterIotTSimCardRenew(NewIotTSimCardRenew())
|
|
}
|
|
|
|
func (s *sIotTSimCardRenew) PrepareBatch(ctx context.Context, in *model.TSimCardRenewBatchPrepareParam) (*model.TSimCardRenewBatchPrepareRes, error) {
|
|
if in == nil {
|
|
in = &model.TSimCardRenewBatchPrepareParam{}
|
|
}
|
|
sourceBatchNo := strings.TrimSpace(in.SourceBatchNo)
|
|
if sourceBatchNo == "" {
|
|
return nil, gerror.New("来源批次号不能为空")
|
|
}
|
|
submitParam := &model.TSimCardRenewBatchSubmitParam{
|
|
SourceBatchNo: sourceBatchNo,
|
|
}
|
|
rows, err := s.buildRenewRows(ctx, submitParam)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.prepareRows(ctx, submitParam, rows, consts.BatchSourceTypeBatchSelect, sourceBatchNo, sourceBatchNo, in)
|
|
}
|
|
|
|
func (s *sIotTSimCardRenew) PrepareBatchAll(ctx context.Context, in *model.TSimCardRenewBatchPrepareAllParam) (*model.TSimCardRenewBatchPrepareRes, error) {
|
|
if in == nil {
|
|
in = &model.TSimCardRenewBatchPrepareAllParam{}
|
|
}
|
|
sourceBatchNo := strings.TrimSpace(in.SourceBatchNo)
|
|
if sourceBatchNo == "" {
|
|
return nil, gerror.New("来源批次号不能为空")
|
|
}
|
|
submitParam := &model.TSimCardRenewBatchSubmitParam{
|
|
SourceBatchNo: sourceBatchNo,
|
|
}
|
|
rows, err := s.buildRenewRows(ctx, submitParam)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.prepareRows(ctx, submitParam, rows, consts.BatchSourceTypeBatchSelect, sourceBatchNo, sourceBatchNo, in)
|
|
}
|
|
|
|
func (s *sIotTSimCardRenew) PrepareInput(ctx context.Context, in *model.TSimCardRenewInputPrepareParam) (*model.TSimCardRenewBatchPrepareRes, error) {
|
|
if in == nil {
|
|
in = &model.TSimCardRenewInputPrepareParam{}
|
|
}
|
|
rows, err := renew.ParseInputRows(ctx, in.Input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
renewRows, err := batchRowsToRenewRows(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.prepareRows(ctx, &model.TSimCardRenewBatchSubmitParam{}, renewRows, consts.BatchSourceTypeBatchInput, "", "input-prepare", nil)
|
|
}
|
|
|
|
// prepareRows 异步构造卡续费预处理批次:batch_type=4、batch_async_status=Pending、targetBatchType=101。
|
|
func (s *sIotTSimCardRenew) prepareRows(ctx context.Context, submitParam *model.TSimCardRenewBatchSubmitParam, rows []*renew.Row, sourceType int, sourceBatchNo string, sourceName string, bizParam any) (*model.TSimCardRenewBatchPrepareRes, error) {
|
|
if submitParam == nil {
|
|
submitParam = &model.TSimCardRenewBatchSubmitParam{}
|
|
}
|
|
submitParam.SourceBatchNo = strings.TrimSpace(submitParam.SourceBatchNo)
|
|
|
|
processor := renew.NewPrepareProcessor()
|
|
importRows := renewRowsToBatchRows(rows)
|
|
sourceBatchNo = strings.TrimSpace(sourceBatchNo)
|
|
sourceName = strings.TrimSpace(sourceName)
|
|
|
|
bizForBuild := bizParam
|
|
if bizForBuild == nil {
|
|
bizForBuild = submitParam
|
|
}
|
|
|
|
req := batchimport.SubmitRequest{
|
|
SourceChannel: batchmetadata.Console,
|
|
ExecutionMode: batchmetadata.Bulk,
|
|
BizParams: bizForBuild,
|
|
SourceType: sourceType,
|
|
SourceBatchNo: sourceBatchNo,
|
|
Operator: currentOperator(ctx),
|
|
}
|
|
result, err := batchimport.CreatePendingRows(ctx, req, processor, importRows, batchimport.PendingOptions{
|
|
PrepareRows: func(ctx context.Context, rows []batchimport.Row) error {
|
|
// 行级校验复用 processor.ValidateRows;批量数据为 nil 时跳过。
|
|
if len(rows) == 0 {
|
|
return nil
|
|
}
|
|
return processor.ValidateRows(ctx, rows)
|
|
},
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res := &model.TSimCardRenewBatchPrepareRes{
|
|
BatchNo: result.BatchNo,
|
|
BatchType: result.BatchType,
|
|
TargetBatchType: consts.BatchTypeRenew,
|
|
Status: pendingStatus(result.ImportStatus),
|
|
BatchAsyncStatus: consts.BatchAsyncStatusPending,
|
|
RunResult: consts.BatchRunResultPending,
|
|
Total: result.TotalRows,
|
|
ValidCount: result.ValidRows,
|
|
FailCount: result.ErrorRows,
|
|
ResultDownloadId: result.ResultDownloadId,
|
|
ResultFileOssId: result.ResultFileOssId,
|
|
}
|
|
if snapshot, ok := result.ExecutionResult.(*model.TSimCardRenewPriceSnapshot); ok && snapshot != nil {
|
|
fillPreparePrice(res, snapshot)
|
|
res.Quantity = result.ValidRows
|
|
}
|
|
publishPendingExecute(ctx, res.BatchNo, res.BatchType, contexts.GetTenantId(ctx))
|
|
return res, nil
|
|
}
|
|
|
|
func (s *sIotTSimCardRenew) ConfirmBatch(ctx context.Context, in *model.TSimCardRenewBatchConfirmParam) (*model.TSimCardRenewBatchConfirmRes, error) {
|
|
if in == nil {
|
|
in = &model.TSimCardRenewBatchConfirmParam{}
|
|
}
|
|
in.BatchNo = strings.TrimSpace(in.BatchNo)
|
|
if in.BatchNo == "" {
|
|
return nil, gerror.New("批次号不能为空")
|
|
}
|
|
durationValue := in.DurationYears
|
|
if durationValue <= 0 {
|
|
durationValue = normalizeLegacyRenewYears(in.DurationMonths)
|
|
}
|
|
if durationValue <= 0 {
|
|
return nil, gerror.New("续费时长必须大于0")
|
|
}
|
|
in.DurationYears = durationValue
|
|
in.DurationMonths = packageduration.RenewMonths(durationValue)
|
|
if in.GiftDurationMonths < 0 {
|
|
return nil, gerror.New("赠送时长不能小于0")
|
|
}
|
|
if err := s.discardRenewPeriodOverrideForNonTop(ctx, in); err != nil {
|
|
return nil, err
|
|
}
|
|
renewDate, err := parseRenewDate(in.RenewDate)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
operator := currentOperator(ctx)
|
|
var snapshot *model.TSimCardRenewPriceSnapshot
|
|
var paymentSnapshot *model.TSimCardRenewPaymentSnapshot
|
|
var sourceBatchNo string
|
|
var packageBeginTime string
|
|
var packageEndTime string
|
|
var converted *renew.AsyncConfirmResult
|
|
var prepared *renew.PrepareContext
|
|
var revalidatedRows []*renew.Row
|
|
var prepareChanged bool
|
|
var priceChanged bool
|
|
|
|
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
batch, err := s.lockPrepareBatchForConfirm(ctx, tx, in.BatchNo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
baseSnapshot, err := snapshotFromBatch(batch)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
prepared, revalidatedRows, prepareChanged, err = s.revalidateRenewBatchForConfirm(ctx, tx, batch, baseSnapshot, operator)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if prepared == nil || prepared.Quantity <= 0 {
|
|
prepareChanged = true
|
|
return s.refreshRenewPrepareState(ctx, tx, batch, revalidatedRows, baseSnapshot)
|
|
}
|
|
snapshot, priceChanged, err = s.resolveRenewConfirmSnapshot(ctx, tx, prepared, baseSnapshot, in.PackageCode)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if prepareChanged || priceChanged {
|
|
return s.refreshRenewPrepareState(ctx, tx, batch, revalidatedRows, snapshot)
|
|
}
|
|
if err = validateRenewConfirmContract(snapshot, durationValue); err != nil {
|
|
return err
|
|
}
|
|
packageBeginTime, packageEndTime, err = s.normalizeRenewConfirmPeriod(ctx, snapshot, in)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
quantity := batch.SuccessCount
|
|
if prepared != nil && prepared.Quantity > 0 {
|
|
quantity = prepared.Quantity
|
|
}
|
|
if quantity <= 0 {
|
|
quantity = batch.ValidCount
|
|
}
|
|
if quantity <= 0 {
|
|
quantity = batch.TotalCount
|
|
}
|
|
if quantity <= 0 {
|
|
return gerror.New("续费预处理批次没有可续费的卡")
|
|
}
|
|
if err = s.ensureNoUnfinishedRenewBatchForConfirm(ctx, tx, batch.BatchNo); err != nil {
|
|
return err
|
|
}
|
|
paymentSnapshot, err = s.buildRenewPaymentSnapshot(ctx, tx, batch.BatchNo, snapshot, quantity, in)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
prepareSourceBatchNo := renewSourceBatchNoFromBatch(batch)
|
|
sourceBatchNo = prepareSourceBatchNo
|
|
|
|
converted, err = renew.ConvertPrepareToAsync(ctx, tx, &renew.AsyncConfirmInput{
|
|
BatchNo: in.BatchNo,
|
|
TargetBatchType: consts.BatchTypeRenew,
|
|
DurationYears: durationValue,
|
|
DurationMonths: packageduration.RenewMonths(durationValue),
|
|
GiftDurationMonths: in.GiftDurationMonths,
|
|
RenewDate: renewDateString(renewDate),
|
|
PackageBeginTime: packageBeginTime,
|
|
PackageEndTime: packageEndTime,
|
|
OfflineContract: in.OfflineContractCompleted,
|
|
BuyerNote: in.BuyerNote,
|
|
AdminNote: in.AdminNote,
|
|
Remark: in.Remark,
|
|
PriceSnapshot: snapshot,
|
|
PaymentSnapshot: paymentSnapshot,
|
|
SourceBatchNo: prepareSourceBatchNo,
|
|
Operator: operator,
|
|
})
|
|
return err
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if prepareChanged || priceChanged {
|
|
message := renewPriceChangedMessage
|
|
if prepareChanged {
|
|
message = renewPrepareChangedMessage
|
|
}
|
|
return &model.TSimCardRenewBatchConfirmRes{
|
|
BatchNo: in.BatchNo,
|
|
PackageCode: renewSnapshotPackageCode(snapshot),
|
|
PackageName: renewSnapshotPackageName(snapshot),
|
|
Status: "prepare_refreshed",
|
|
PrepareChanged: prepareChanged,
|
|
PriceChanged: priceChanged,
|
|
Message: message,
|
|
}, nil
|
|
}
|
|
if converted == nil || snapshot == nil {
|
|
return nil, gerror.New("续费确认结果为空")
|
|
}
|
|
if paymentSnapshot == nil {
|
|
return nil, gerror.New("续费付款结果为空")
|
|
}
|
|
|
|
publishPendingExecute(ctx, converted.BatchNo, converted.BatchType, contexts.GetTenantId(ctx))
|
|
|
|
return &model.TSimCardRenewBatchConfirmRes{
|
|
BatchNo: converted.BatchNo,
|
|
BatchType: converted.BatchType,
|
|
BatchAsyncStatus: converted.BatchAsyncStatus,
|
|
SourceBatchNo: sourceBatchNo,
|
|
PackageCode: snapshot.PackageCode,
|
|
PackageName: snapshot.PackageName,
|
|
DurationYears: durationValue,
|
|
DurationMonths: packageduration.RenewMonths(durationValue),
|
|
GiftDurationMonths: in.GiftDurationMonths,
|
|
Status: "pending",
|
|
PaymentMode: paymentSnapshot.PaymentMode,
|
|
PayType: paymentSnapshot.PayType,
|
|
PayAmount: paymentSnapshot.PayAmount,
|
|
AvailableBalance: paymentSnapshot.BalanceAfter,
|
|
BalanceDetailSerialNo: paymentSnapshot.BalanceDetailSerialNo,
|
|
PackageBeginTime: packageBeginTime,
|
|
PackageEndTime: packageEndTime,
|
|
}, nil
|
|
}
|
|
|
|
func validateRenewConfirmContract(snapshot *model.TSimCardRenewPriceSnapshot, durationMonths int) error {
|
|
if snapshot == nil {
|
|
return gerror.New("续费预处理批次缺少报价信息")
|
|
}
|
|
if !containsInt(snapshot.RenewableDurationOptions, durationMonths) {
|
|
return gerror.New("续费时长不在当前套餐可续费范围内")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *sIotTSimCardRenew) discardRenewPeriodOverrideForNonTop(ctx context.Context, in *model.TSimCardRenewBatchConfirmParam) error {
|
|
if in == nil {
|
|
return nil
|
|
}
|
|
if strings.TrimSpace(in.RenewDate) == "" &&
|
|
strings.TrimSpace(in.PackageBeginTime) == "" &&
|
|
strings.TrimSpace(in.PackageEndTime) == "" {
|
|
return nil
|
|
}
|
|
isTop, _, err := service.AgentDept().IsTopDept(ctx)
|
|
if err != nil {
|
|
return gerror.Wrap(err, "判断当前组织类型失败")
|
|
}
|
|
if isTop {
|
|
return nil
|
|
}
|
|
in.RenewDate = ""
|
|
in.PackageBeginTime = ""
|
|
in.PackageEndTime = ""
|
|
return nil
|
|
}
|
|
|
|
func renewDurationUnit(snapshot *model.TSimCardRenewPriceSnapshot) string {
|
|
return consts.RenewDurationUnitYear
|
|
}
|
|
|
|
func addRenewDurationByUnit(beginAt time.Time, _ string, value int) time.Time {
|
|
if value <= 0 {
|
|
return beginAt
|
|
}
|
|
return addRenewCalendarMonths(beginAt, packageduration.RenewMonths(value))
|
|
}
|
|
|
|
func addRenewCalendarMonths(beginAt time.Time, months int) time.Time {
|
|
if months <= 0 {
|
|
return beginAt
|
|
}
|
|
targetMonth := time.Date(
|
|
beginAt.Year(),
|
|
beginAt.Month()+time.Month(months),
|
|
1,
|
|
beginAt.Hour(),
|
|
beginAt.Minute(),
|
|
beginAt.Second(),
|
|
beginAt.Nanosecond(),
|
|
beginAt.Location(),
|
|
)
|
|
day := beginAt.Day()
|
|
lastDay := time.Date(
|
|
targetMonth.Year(),
|
|
targetMonth.Month()+1,
|
|
0,
|
|
beginAt.Hour(),
|
|
beginAt.Minute(),
|
|
beginAt.Second(),
|
|
beginAt.Nanosecond(),
|
|
beginAt.Location(),
|
|
).Day()
|
|
if day > lastDay {
|
|
day = lastDay
|
|
}
|
|
return renewDayEndTime(time.Date(
|
|
targetMonth.Year(),
|
|
targetMonth.Month(),
|
|
day,
|
|
beginAt.Hour(),
|
|
beginAt.Minute(),
|
|
beginAt.Second(),
|
|
beginAt.Nanosecond(),
|
|
beginAt.Location(),
|
|
))
|
|
}
|
|
|
|
func renewDayEndTime(t time.Time) time.Time {
|
|
return time.Date(
|
|
t.Year(),
|
|
t.Month(),
|
|
t.Day(),
|
|
23,
|
|
59,
|
|
59,
|
|
0,
|
|
t.Location(),
|
|
)
|
|
}
|
|
|
|
func (s *sIotTSimCardRenew) normalizeRenewConfirmPeriod(ctx context.Context, snapshot *model.TSimCardRenewPriceSnapshot, in *model.TSimCardRenewBatchConfirmParam) (string, string, error) {
|
|
beginValue := strings.TrimSpace(in.PackageBeginTime)
|
|
endValue := strings.TrimSpace(in.PackageEndTime)
|
|
if beginValue == "" && endValue == "" {
|
|
return "", "", nil
|
|
}
|
|
isTop, _, err := service.AgentDept().IsTopDept(ctx)
|
|
if err != nil {
|
|
return "", "", gerror.Wrap(err, "判断当前组织类型失败")
|
|
}
|
|
if !isTop {
|
|
return "", "", gerror.New("当前组织不能修改续费日期")
|
|
}
|
|
if beginValue == "" && snapshot != nil {
|
|
beginValue = strings.TrimSpace(snapshot.RenewBeginDate)
|
|
}
|
|
if beginValue == "" {
|
|
return "", "", gerror.New("续费开始日期不能为空")
|
|
}
|
|
beginAt, err := parseRenewPeriodDate(beginValue, "续费开始日期")
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
durationValue := in.DurationYears
|
|
if durationValue <= 0 {
|
|
durationValue = normalizeLegacyRenewYears(in.DurationMonths)
|
|
}
|
|
giftValue := in.GiftDurationMonths
|
|
expectedEndAt := addRenewCalendarMonths(beginAt, packageduration.RenewMonths(durationValue)+giftValue)
|
|
if endValue == "" {
|
|
endValue = expectedEndAt.Format("2006-01-02")
|
|
} else {
|
|
endAt, err := parseRenewPeriodDate(endValue, "续费结束日期")
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
if !sameDate(endAt, expectedEndAt) {
|
|
return "", "", gerror.New("续费结束日期与续费时长不一致")
|
|
}
|
|
}
|
|
return beginAt.Format("2006-01-02"), endValue, nil
|
|
}
|
|
|
|
// PrepareResult 查询预处理批次的当前异步状态。
|
|
func (s *sIotTSimCardRenew) PrepareResult(ctx context.Context, batchNo string) (*model.TSimCardRenewPrepareResultRes, error) {
|
|
res, err := loadPrepareResult(ctx, batchNo, consts.BatchTypeRenew, renewPriceSnapshotFromBatch, buildRenewResultFields)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = s.fillRenewPrepareFailedItems(ctx, res); err != nil {
|
|
return nil, err
|
|
}
|
|
s.fillRenewPrepareResultPaymentView(ctx, res)
|
|
return res, nil
|
|
}
|
|
|
|
func (s *sIotTSimCardRenew) fillRenewPrepareFailedItems(ctx context.Context, res *model.TSimCardRenewPrepareResultRes) error {
|
|
if res == nil || strings.TrimSpace(res.BatchNo) == "" || res.FailCount <= 0 {
|
|
return nil
|
|
}
|
|
const displayLimit = 20
|
|
details := make([]*entity.BatchDetail, 0, displayLimit)
|
|
columns := dao.BatchDetail.Columns()
|
|
if err := dao.BatchDetail.Ctx(ctx).
|
|
Fields(columns.ItemId, columns.ErrorReason).
|
|
Where(columns.BatchNo, res.BatchNo).
|
|
Where(columns.BatchType, consts.BatchTypePrepare).
|
|
Where(columns.DetailStatus, consts.BatchDetailStatusFailed).
|
|
OrderAsc(columns.RowNo).
|
|
Limit(displayLimit).
|
|
Scan(&details); err != nil {
|
|
return gerror.Wrap(err, "查询续费预处理失败卡明细失败")
|
|
}
|
|
res.FailedItems = make([]model.TSimCardRenewPrepareFailedItem, 0, len(details))
|
|
for _, detail := range details {
|
|
if detail == nil {
|
|
continue
|
|
}
|
|
res.FailedItems = append(res.FailedItems, model.TSimCardRenewPrepareFailedItem{
|
|
Iccid: strings.TrimSpace(detail.ItemId),
|
|
Reason: strings.TrimSpace(detail.ErrorReason),
|
|
})
|
|
}
|
|
res.FailedItemsTruncated = res.FailCount > len(res.FailedItems)
|
|
return nil
|
|
}
|
|
|
|
func (s *sIotTSimCardRenew) buildRenewRows(ctx context.Context, in *model.TSimCardRenewBatchSubmitParam) ([]*renew.Row, error) {
|
|
if in == nil {
|
|
return nil, gerror.New("续费参数不能为空")
|
|
}
|
|
in.SourceBatchNo = strings.TrimSpace(in.SourceBatchNo)
|
|
repo := renew.NewRepository()
|
|
sourceBatch, err := repo.GetSourceBatch(ctx, in.SourceBatchNo)
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "查询来源批次失败")
|
|
}
|
|
if sourceBatch == nil {
|
|
return nil, gerror.New("来源批次不存在")
|
|
}
|
|
if tenantId := contexts.GetTenantId(ctx); tenantId != "" && sourceBatch.TenantId != tenantId {
|
|
return nil, gerror.New("来源批次不存在")
|
|
}
|
|
if sourceBatch.BatchType != consts.BatchTypeOutbound {
|
|
return nil, gerror.New("来源批次不是出库批次")
|
|
}
|
|
if sourceBatch.RunResult != consts.BatchRunResultSuccess {
|
|
return nil, gerror.New("来源批次不是成功批次")
|
|
}
|
|
|
|
rows, err := repo.GetSourceBatchRows(ctx, in.SourceBatchNo)
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "查询来源批次卡明细失败")
|
|
}
|
|
if len(rows) == 0 {
|
|
return nil, gerror.New("来源批次没有可续费卡")
|
|
}
|
|
return rows, nil
|
|
}
|
|
|
|
func (s *sIotTSimCardRenew) lockPrepareBatchForConfirm(ctx context.Context, tx gdb.TX, batchNo string) (*entity.BatchMain, error) {
|
|
columns := dao.BatchMain.Columns()
|
|
batch := &entity.BatchMain{}
|
|
err := tx.Ctx(ctx).Model(dao.BatchMain.Table()).
|
|
Where(columns.BatchNo, batchNo).
|
|
Where(columns.BatchType, consts.BatchTypePrepare).
|
|
Where(columns.BatchAsyncStatus, consts.BatchAsyncStatusSuccess).
|
|
Where(columns.RunResult, consts.BatchRunResultSuccess).
|
|
Where(columns.TenantId, contexts.GetTenantId(ctx)).
|
|
Where(columns.CreatedDept, contexts.GetDeptId(ctx)).
|
|
LockUpdate().
|
|
Scan(batch)
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "查询续费预处理批次失败")
|
|
}
|
|
if batch.Id == 0 {
|
|
return nil, gerror.New("续费预处理批次不存在或尚未完成")
|
|
}
|
|
if batch.BatchInfo == nil {
|
|
return nil, gerror.New("续费预处理批次缺少报价信息")
|
|
}
|
|
if batch.BatchInfo.Get("targetBatchType").Int() != consts.BatchTypeRenew {
|
|
return nil, gerror.New("批次不是卡续费预处理批次")
|
|
}
|
|
return batch, nil
|
|
}
|
|
|
|
func (s *sIotTSimCardRenew) ensureNoUnfinishedRenewBatchForConfirm(ctx context.Context, tx gdb.TX, batchNo string) error {
|
|
repo := renew.NewRepository()
|
|
iccids, err := repo.BatchDetailItemIDs(ctx, tx, batchNo, consts.BatchTypePrepare, []int{consts.BatchDetailStatusSuccess})
|
|
if err != nil {
|
|
return gerror.Wrap(err, "查询续费预处理明细失败")
|
|
}
|
|
if len(iccids) == 0 {
|
|
return gerror.New("续费预处理批次没有可续费的卡")
|
|
}
|
|
conflictingBatches, err := repo.UnfinishedRenewBatchNosByIccids(ctx, tx, contexts.GetTenantId(ctx), iccids, "")
|
|
if err != nil {
|
|
return gerror.Wrap(err, "校验未完成续费批次失败")
|
|
}
|
|
if len(conflictingBatches) > 0 {
|
|
return gerror.New("存在未完成续费批次,不能重复续费")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *sIotTSimCardRenew) revalidateRenewBatchForConfirm(ctx context.Context, tx gdb.TX, batch *entity.BatchMain, snapshot *model.TSimCardRenewPriceSnapshot, operator batchimport.OperatorContext) (*renew.PrepareContext, []*renew.Row, bool, error) {
|
|
if batch == nil || strings.TrimSpace(batch.BatchNo) == "" {
|
|
return nil, nil, false, gerror.New("renew prepare batch is empty")
|
|
}
|
|
repo := renew.NewRepository()
|
|
rows, err := repo.GetRenewBatchRowsTx(ctx, tx, batch.BatchNo, consts.BatchTypePrepare)
|
|
if err != nil {
|
|
return nil, nil, false, gerror.Wrap(err, "query renew prepare details failed")
|
|
}
|
|
if len(rows) == 0 {
|
|
return &renew.PrepareContext{}, rows, true, nil
|
|
}
|
|
submitParam := &model.TSimCardRenewBatchSubmitParam{
|
|
SourceBatchNo: renewSourceBatchNoFromBatch(batch),
|
|
}
|
|
prepared, prepareChanged, err := renew.NewProcessor().RevalidatePrepareContext(ctx, tx, submitParam, operator, rows, batch.BatchNo)
|
|
if err != nil {
|
|
return nil, rows, false, err
|
|
}
|
|
if prepared == nil {
|
|
return nil, rows, false, gerror.New("renew prepare validation result is empty")
|
|
}
|
|
if snapshot != nil {
|
|
if snapshot.CustomerAgentId > 0 && prepared.CustomerAgentId > 0 && snapshot.CustomerAgentId != prepared.CustomerAgentId {
|
|
prepareChanged = true
|
|
}
|
|
fillRenewBeginSnapshot(snapshot, prepared)
|
|
}
|
|
return prepared, rows, prepareChanged, nil
|
|
}
|
|
|
|
func (s *sIotTSimCardRenew) resolveRenewConfirmSnapshot(ctx context.Context, tx gdb.TX, prepared *renew.PrepareContext, baseSnapshot *model.TSimCardRenewPriceSnapshot, selectedPackageCode string) (*model.TSimCardRenewPriceSnapshot, bool, error) {
|
|
if prepared == nil {
|
|
return nil, false, gerror.New("renew prepare validation result is empty")
|
|
}
|
|
currentPackageCode := strings.TrimSpace(prepared.PackageCode)
|
|
if currentPackageCode == "" {
|
|
return nil, false, gerror.New("当前套餐不能为空")
|
|
}
|
|
selectedPackageCode = strings.TrimSpace(selectedPackageCode)
|
|
if selectedPackageCode == "" && baseSnapshot != nil {
|
|
selectedPackageCode = strings.TrimSpace(baseSnapshot.PackageCode)
|
|
}
|
|
if selectedPackageCode == "" {
|
|
selectedPackageCode = currentPackageCode
|
|
}
|
|
|
|
renewableCodes, renewablePackages, err := s.loadRenewablePackageOptions(ctx, tx, prepared.CustomerAgentId, currentPackageCode)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
packageSelectionChanged := false
|
|
if !containsString(renewableCodes, selectedPackageCode) {
|
|
selectedPackageCode = currentPackageCode
|
|
if !containsString(renewableCodes, selectedPackageCode) && len(renewableCodes) > 0 {
|
|
selectedPackageCode = renewableCodes[0]
|
|
}
|
|
if selectedPackageCode == "" || !containsString(renewableCodes, selectedPackageCode) {
|
|
return nil, false, gerror.New("当前套餐已无可续费套餐,请重新发起预处理")
|
|
}
|
|
packageSelectionChanged = true
|
|
}
|
|
|
|
snapshot, err := s.buildRenewPriceSnapshot(ctx, tx, prepared.CustomerAgentId, selectedPackageCode)
|
|
if err != nil {
|
|
return nil, false, err
|
|
}
|
|
snapshot.RenewablePackageCodes = renewableCodes
|
|
snapshot.RenewablePackages = renewablePackages
|
|
fillRenewBeginSnapshot(snapshot, prepared)
|
|
priceChanged := packageSelectionChanged
|
|
if baseSnapshot != nil {
|
|
if strings.TrimSpace(baseSnapshot.PackageCode) == selectedPackageCode {
|
|
if !sameRenewPriceSnapshot(baseSnapshot, snapshot) {
|
|
priceChanged = true
|
|
}
|
|
} else {
|
|
oldOption, ok := renewPackageOptionByCode(baseSnapshot.RenewablePackages, selectedPackageCode)
|
|
if !ok || !sameRenewPackageOption(oldOption, snapshot) {
|
|
priceChanged = true
|
|
}
|
|
}
|
|
if !sameRenewPackageOptions(baseSnapshot.RenewablePackages, renewablePackages) {
|
|
priceChanged = true
|
|
}
|
|
}
|
|
return snapshot, priceChanged, nil
|
|
}
|
|
|
|
func (s *sIotTSimCardRenew) refreshRenewPrepareState(ctx context.Context, tx gdb.TX, batch *entity.BatchMain, rows []*renew.Row, snapshot *model.TSimCardRenewPriceSnapshot) error {
|
|
if batch == nil || batch.Id == 0 {
|
|
return gerror.New("续费预处理批次信息不完整")
|
|
}
|
|
info := prepareInfoMap(batch.BatchInfo)
|
|
if info == nil {
|
|
info = map[string]any{}
|
|
}
|
|
if snapshot != nil {
|
|
info["customerAgentId"] = snapshot.CustomerAgentId
|
|
info["packageCode"] = snapshot.PackageCode
|
|
info["packageName"] = snapshot.PackageName
|
|
info["billingMethod"] = snapshot.BillingMethod
|
|
info["renewDurationUnit"] = snapshot.RenewDurationUnit
|
|
info["renewDurationUnitText"] = snapshot.RenewDurationUnitText
|
|
info["priceSource"] = snapshot.PriceSource
|
|
info["priceOwnerDeptId"] = snapshot.PriceOwnerDeptId
|
|
info["agentDefaultPriceId"] = snapshot.AgentDefaultPriceId
|
|
info["targetPriceId"] = snapshot.TargetPriceId
|
|
info["renewUnitPrice"] = snapshot.RenewUnitPrice
|
|
info["renewableDurationOptions"] = snapshot.RenewableDurationOptions
|
|
info["renewablePackageCodes"] = snapshot.RenewablePackageCodes
|
|
info["renewablePackages"] = snapshot.RenewablePackages
|
|
info["renewBeginDate"] = snapshot.RenewBeginDate
|
|
info["renewBeginDateMin"] = snapshot.RenewBeginDateMin
|
|
info["renewBeginDateMax"] = snapshot.RenewBeginDateMax
|
|
info["sameRenewBeginDate"] = snapshot.SameRenewBeginDate
|
|
info["priceSnapshot"] = snapshot
|
|
}
|
|
|
|
successCount := 0
|
|
for _, row := range rows {
|
|
if row == nil {
|
|
continue
|
|
}
|
|
detailStatus := consts.BatchDetailStatusSuccess
|
|
if !row.Valid() {
|
|
detailStatus = consts.BatchDetailStatusFailed
|
|
} else {
|
|
successCount++
|
|
}
|
|
if _, err := tx.Ctx(ctx).Model(dao.BatchDetail.Table()).
|
|
Where(dao.BatchDetail.Columns().BatchNo, batch.BatchNo).
|
|
Where(dao.BatchDetail.Columns().BatchType, consts.BatchTypePrepare).
|
|
Where(dao.BatchDetail.Columns().RowNo, row.RowNo).
|
|
Data(do.BatchDetail{
|
|
DetailStatus: detailStatus,
|
|
ErrorReason: strings.TrimSpace(row.Reason),
|
|
UpdatedBy: contexts.GetUserId(ctx),
|
|
}).
|
|
Update(); err != nil {
|
|
return gerror.Wrap(err, "刷新续费预处理明细失败")
|
|
}
|
|
}
|
|
|
|
totalCount := batch.TotalCount
|
|
if minimumTotal := successCount + batch.FailCount; totalCount < minimumTotal {
|
|
totalCount = minimumTotal
|
|
}
|
|
failCount := totalCount - successCount
|
|
if failCount < 0 {
|
|
failCount = 0
|
|
}
|
|
info["quantity"] = successCount
|
|
updatedInfo := gjson.New(info)
|
|
mainData := do.BatchMain{
|
|
TotalCount: totalCount,
|
|
ValidCount: successCount,
|
|
SuccessCount: successCount,
|
|
FailCount: failCount,
|
|
FailReason: renewPrepareFailReason(failCount),
|
|
BatchInfo: updatedInfo,
|
|
UpdatedBy: contexts.GetUserId(ctx),
|
|
}
|
|
if snapshot != nil && snapshot.CustomerAgentId > 0 {
|
|
mainData.TargetDeptId = snapshot.CustomerAgentId
|
|
}
|
|
if _, err := tx.Ctx(ctx).Model(dao.BatchMain.Table()).
|
|
Where(dao.BatchMain.Columns().Id, batch.Id).
|
|
Data(mainData).
|
|
Update(); err != nil {
|
|
return gerror.Wrap(err, "刷新续费预处理结果失败")
|
|
}
|
|
batch.BatchInfo = updatedInfo
|
|
batch.TotalCount = totalCount
|
|
batch.ValidCount = successCount
|
|
batch.SuccessCount = successCount
|
|
batch.FailCount = failCount
|
|
return nil
|
|
}
|
|
|
|
func renewPrepareFailReason(failCount int) string {
|
|
if failCount <= 0 {
|
|
return ""
|
|
}
|
|
return "续费条件已更新,部分卡已不满足续费条件"
|
|
}
|
|
|
|
func renewSnapshotPackageCode(snapshot *model.TSimCardRenewPriceSnapshot) string {
|
|
if snapshot == nil {
|
|
return ""
|
|
}
|
|
return snapshot.PackageCode
|
|
}
|
|
|
|
func renewSnapshotPackageName(snapshot *model.TSimCardRenewPriceSnapshot) string {
|
|
if snapshot == nil {
|
|
return ""
|
|
}
|
|
return snapshot.PackageName
|
|
}
|
|
|
|
func sameRenewPriceSnapshot(left, right *model.TSimCardRenewPriceSnapshot) bool {
|
|
if left == nil || right == nil {
|
|
return left == right
|
|
}
|
|
return strings.TrimSpace(left.PackageCode) == strings.TrimSpace(right.PackageCode) &&
|
|
left.BillingMethod == right.BillingMethod &&
|
|
math.Abs(left.RenewUnitPrice-right.RenewUnitPrice) < 0.000001 &&
|
|
strings.TrimSpace(left.PriceSource) == strings.TrimSpace(right.PriceSource) &&
|
|
left.PriceOwnerDeptId == right.PriceOwnerDeptId &&
|
|
left.AgentDefaultPriceId == right.AgentDefaultPriceId &&
|
|
left.TargetPriceId == right.TargetPriceId &&
|
|
sameRenewDurationOptions(left.RenewableDurationOptions, right.RenewableDurationOptions) &&
|
|
sameRenewPackageOptions(left.RenewablePackages, right.RenewablePackages)
|
|
}
|
|
|
|
func renewPackageOptionByCode(options []model.TSimCardRenewPackageOption, packageCode string) (model.TSimCardRenewPackageOption, bool) {
|
|
packageCode = strings.TrimSpace(packageCode)
|
|
for _, option := range options {
|
|
if strings.TrimSpace(option.PackageCode) == packageCode {
|
|
return option, true
|
|
}
|
|
}
|
|
return model.TSimCardRenewPackageOption{}, false
|
|
}
|
|
|
|
func sameRenewPackageOption(option model.TSimCardRenewPackageOption, snapshot *model.TSimCardRenewPriceSnapshot) bool {
|
|
if snapshot == nil {
|
|
return false
|
|
}
|
|
return strings.TrimSpace(option.PackageCode) == strings.TrimSpace(snapshot.PackageCode) &&
|
|
math.Abs(option.RenewUnitPrice-snapshot.RenewUnitPrice) < 0.000001 &&
|
|
sameRenewDurationOptions(option.RenewableDurationOptions, snapshot.RenewableDurationOptions)
|
|
}
|
|
|
|
func sameRenewDurationOptions(left, right []int) bool {
|
|
if len(left) != len(right) {
|
|
return false
|
|
}
|
|
for index := range left {
|
|
if left[index] != right[index] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func sameRenewPackageOptions(left, right []model.TSimCardRenewPackageOption) bool {
|
|
if len(left) != len(right) {
|
|
return false
|
|
}
|
|
for index := range left {
|
|
if strings.TrimSpace(left[index].PackageCode) != strings.TrimSpace(right[index].PackageCode) ||
|
|
strings.TrimSpace(left[index].PackageName) != strings.TrimSpace(right[index].PackageName) ||
|
|
math.Abs(left[index].RenewUnitPrice-right[index].RenewUnitPrice) >= 0.000001 ||
|
|
!sameRenewDurationOptions(left[index].RenewableDurationOptions, right[index].RenewableDurationOptions) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (s *sIotTSimCardRenew) loadRenewablePackageOptions(ctx context.Context, tx gdb.TX, customerAgentId int64, currentPackageCode string) ([]string, []model.TSimCardRenewPackageOption, error) {
|
|
currentPackageCode = strings.TrimSpace(currentPackageCode)
|
|
if currentPackageCode == "" {
|
|
return nil, nil, gerror.New("当前套餐不能为空")
|
|
}
|
|
columns := dao.TSimFlowPackageInfo.Columns()
|
|
current := &entity.TSimFlowPackageInfo{}
|
|
if err := tx.Ctx(ctx).Model(dao.TSimFlowPackageInfo.Table()).
|
|
Fields(columns.PackageCode, columns.PackageName, columns.RenewablePackageCodes).
|
|
Where(columns.TenantId, contexts.GetTenantId(ctx)).
|
|
Where(columns.PackageCode, currentPackageCode).
|
|
Scan(current); err != nil {
|
|
return nil, nil, gerror.Wrap(err, "查询可续费套餐配置失败")
|
|
}
|
|
if current.Id == 0 && strings.TrimSpace(current.PackageCode) == "" {
|
|
return nil, nil, gerror.New("当前套餐不存在")
|
|
}
|
|
|
|
var codes []string
|
|
if current.RenewablePackageCodes != nil {
|
|
_ = current.RenewablePackageCodes.Scan(&codes)
|
|
}
|
|
codes = uniqueRenewStrings(codes)
|
|
if len(codes) == 0 {
|
|
return nil, nil, gerror.New("当前套餐未配置可续费套餐")
|
|
}
|
|
|
|
var packages []*entity.TSimFlowPackageInfo
|
|
if err := tx.Ctx(ctx).Model(dao.TSimFlowPackageInfo.Table()).
|
|
Fields(
|
|
columns.PackageCode,
|
|
columns.PackageName,
|
|
columns.CostPrice,
|
|
columns.TerminalCost,
|
|
columns.SysDefaultRatio,
|
|
columns.SysDefaultProfit,
|
|
columns.SysDefaultTerminalRatio,
|
|
columns.SysDefaultTerminalProfit,
|
|
columns.RenewableDurationConfig,
|
|
).
|
|
Where(columns.TenantId, contexts.GetTenantId(ctx)).
|
|
WhereIn(columns.PackageCode, codes).
|
|
Scan(&packages); err != nil {
|
|
return nil, nil, gerror.Wrap(err, "查询可续费套餐列表失败")
|
|
}
|
|
nameByCode := make(map[string]string, len(packages))
|
|
packageByCode := make(map[string]*entity.TSimFlowPackageInfo, len(packages))
|
|
for _, item := range packages {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
code := strings.TrimSpace(item.PackageCode)
|
|
nameByCode[code] = strings.TrimSpace(item.PackageName)
|
|
packageByCode[code] = item
|
|
}
|
|
priceOwnerDeptID, err := s.loadRenewPriceOwnerDeptID(ctx, tx, 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 := resolvePackageEffectiveSalePrice(ctx, tx, contexts.GetTenantId(ctx), priceOwnerDeptID, customerAgentId, packagePriceSourceFromEntity(pkg))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
unitPrice := salePrice.AgentShipmentPrice
|
|
durationOptions, err := renewableDurationOptions(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 (s *sIotTSimCardRenew) buildRenewPriceSnapshot(ctx context.Context, tx gdb.TX, customerAgentId int64, packageCode string) (*model.TSimCardRenewPriceSnapshot, error) {
|
|
packageCode = strings.TrimSpace(packageCode)
|
|
if customerAgentId <= 0 {
|
|
return nil, gerror.New("客户不能为空")
|
|
}
|
|
if packageCode == "" {
|
|
return nil, gerror.New("套餐不能为空")
|
|
}
|
|
|
|
pkg := &entity.TSimFlowPackageInfo{}
|
|
pkgColumns := dao.TSimFlowPackageInfo.Columns()
|
|
if err := tx.Ctx(ctx).Model(dao.TSimFlowPackageInfo.Table()).
|
|
Where(pkgColumns.TenantId, contexts.GetTenantId(ctx)).
|
|
Where(pkgColumns.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 := renewableDurationOptions(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: roundAmount(pkg.CostPrice*normalizePriceRatio(pkg.SysDefaultRatio) + pkg.SysDefaultProfit),
|
|
RenewableDurationOptions: options,
|
|
}
|
|
|
|
priceOwnerDeptID, err := s.loadRenewPriceOwnerDeptID(ctx, tx, customerAgentId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
snapshot.PriceOwnerDeptId = priceOwnerDeptID
|
|
salePrice, err := resolvePackageEffectiveSalePrice(ctx, tx, contexts.GetTenantId(ctx), priceOwnerDeptID, customerAgentId, packagePriceSourceFromEntity(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 (s *sIotTSimCardRenew) loadRenewPriceOwnerDeptID(ctx context.Context, tx gdb.TX, customerAgentId int64) (int64, error) {
|
|
if customerAgentId <= 0 {
|
|
return 0, gerror.New("客户不能为零")
|
|
}
|
|
columns := dao.SysDept.Columns()
|
|
dept := &entity.SysDept{}
|
|
err := tx.Ctx(ctx).Model(dao.SysDept.Table()).
|
|
Where(columns.TenantId, contexts.GetTenantId(ctx)).
|
|
Where(columns.DeptId, customerAgentId).
|
|
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 *sIotTSimCardRenew) loadRenewPriceOverride(ctx context.Context, tx gdb.TX, deptID int64, scopeType int, targetAgentId int64, packageCode string) (*entity.TSimFlowPackagePrice, error) {
|
|
columns := dao.TSimFlowPackagePrice.Columns()
|
|
price := &entity.TSimFlowPackagePrice{}
|
|
err := tx.Ctx(ctx).Model(dao.TSimFlowPackagePrice.Table()).
|
|
Where(columns.TenantId, contexts.GetTenantId(ctx)).
|
|
Where(columns.DeptId, deptID).
|
|
Where(columns.AgentScopeType, scopeType).
|
|
Where(columns.TargetAgentId, targetAgentId).
|
|
Where(columns.PackageCode, packageCode).
|
|
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 batchRowsToRenewRows(rows []batchimport.Row) ([]*renew.Row, error) {
|
|
result := make([]*renew.Row, 0, len(rows))
|
|
for _, item := range rows {
|
|
row, ok := item.(*renew.Row)
|
|
if !ok || row == nil {
|
|
return nil, gerror.New("续费行类型错误")
|
|
}
|
|
result = append(result, row)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func renewRowsToBatchRows(rows []*renew.Row) []batchimport.Row {
|
|
result := make([]batchimport.Row, 0, len(rows))
|
|
for _, row := range rows {
|
|
result = append(result, row)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func fillPreparePrice(res *model.TSimCardRenewBatchPrepareRes, snapshot *model.TSimCardRenewPriceSnapshot) {
|
|
res.CustomerAgentId = snapshot.CustomerAgentId
|
|
res.PackageCode = snapshot.PackageCode
|
|
res.PackageName = snapshot.PackageName
|
|
res.BillingMethod = snapshot.BillingMethod
|
|
res.RenewDurationUnit = snapshot.RenewDurationUnit
|
|
res.RenewDurationUnitText = snapshot.RenewDurationUnitText
|
|
res.PriceSource = snapshot.PriceSource
|
|
res.PriceOwnerDeptId = snapshot.PriceOwnerDeptId
|
|
res.AgentDefaultPriceId = snapshot.AgentDefaultPriceId
|
|
res.TargetPriceId = snapshot.TargetPriceId
|
|
res.RenewUnitPrice = snapshot.RenewUnitPrice
|
|
res.RenewableDurationOptions = snapshot.RenewableDurationOptions
|
|
res.RenewablePackageCodes = snapshot.RenewablePackageCodes
|
|
res.RenewablePackages = snapshot.RenewablePackages
|
|
res.RenewBeginDate = snapshot.RenewBeginDate
|
|
res.RenewBeginDateMin = snapshot.RenewBeginDateMin
|
|
res.RenewBeginDateMax = snapshot.RenewBeginDateMax
|
|
res.SameRenewBeginDate = snapshot.SameRenewBeginDate
|
|
}
|
|
|
|
func fillRenewBeginSnapshot(snapshot *model.TSimCardRenewPriceSnapshot, prepared *renew.PrepareContext) {
|
|
if snapshot == nil || prepared == nil {
|
|
return
|
|
}
|
|
snapshot.RenewBeginDate = strings.TrimSpace(prepared.RenewBeginDate)
|
|
snapshot.RenewBeginDateMin = strings.TrimSpace(prepared.RenewBeginDateMin)
|
|
snapshot.RenewBeginDateMax = strings.TrimSpace(prepared.RenewBeginDateMax)
|
|
snapshot.SameRenewBeginDate = prepared.SameRenewBeginDate
|
|
}
|
|
|
|
func renewSourceBatchNoFromBatch(batch *entity.BatchMain) string {
|
|
if batch == nil {
|
|
return ""
|
|
}
|
|
if batch.BatchInfo != nil {
|
|
return strings.TrimSpace(batch.BatchInfo.Get("sourceBatchNo").String())
|
|
}
|
|
return strings.TrimSpace(batch.SourceBatchNo)
|
|
}
|
|
|
|
func snapshotFromBatch(batch *entity.BatchMain) (*model.TSimCardRenewPriceSnapshot, error) {
|
|
if batch == nil || batch.BatchInfo == nil {
|
|
return nil, gerror.New("续费预处理批次缺少报价信息")
|
|
}
|
|
options := packageduration.NormalizeRenewableYears(jsonInts(batch.BatchInfo, "renewableDurationOptions", "priceSnapshot.renewableDurationOptions"))
|
|
sort.Ints(options)
|
|
snapshot := &model.TSimCardRenewPriceSnapshot{
|
|
CustomerAgentId: jsonInt64(batch.BatchInfo, "customerAgentId", "customer.deptId", "priceSnapshot.customerAgentId"),
|
|
PriceOwnerDeptId: jsonInt64(batch.BatchInfo, "priceOwnerDeptId", "priceSnapshot.priceOwnerDeptId"),
|
|
PackageCode: jsonString(batch.BatchInfo, "packageCode", "priceSnapshot.packageCode"),
|
|
PackageName: jsonString(batch.BatchInfo, "packageName", "priceSnapshot.packageName"),
|
|
BillingMethod: consts.BillingMethodMonth,
|
|
RenewDurationUnit: jsonString(batch.BatchInfo, "renewDurationUnit", "priceSnapshot.renewDurationUnit"),
|
|
RenewDurationUnitText: jsonString(batch.BatchInfo, "renewDurationUnitText", "priceSnapshot.renewDurationUnitText"),
|
|
PriceSource: jsonString(batch.BatchInfo, "priceSource", "priceSnapshot.priceSource"),
|
|
AgentDefaultPriceId: jsonInt64(batch.BatchInfo, "agentDefaultPriceId", "priceSnapshot.agentDefaultPriceId"),
|
|
TargetPriceId: jsonInt64(batch.BatchInfo, "targetPriceId", "priceSnapshot.targetPriceId"),
|
|
RenewUnitPrice: roundAmount(jsonFloat64(batch.BatchInfo, "renewUnitPrice", "priceSnapshot.renewUnitPrice")),
|
|
RenewableDurationOptions: options,
|
|
RenewablePackageCodes: jsonStrings(batch.BatchInfo, "renewablePackageCodes", "priceSnapshot.renewablePackageCodes"),
|
|
RenewablePackages: renewPackageOptionsFromValue(jsonField(batch.BatchInfo, "renewablePackages", "priceSnapshot.renewablePackages")),
|
|
RenewBeginDate: jsonString(batch.BatchInfo, "renewBeginDate", "priceSnapshot.renewBeginDate"),
|
|
RenewBeginDateMin: jsonString(batch.BatchInfo, "renewBeginDateMin", "priceSnapshot.renewBeginDateMin"),
|
|
RenewBeginDateMax: jsonString(batch.BatchInfo, "renewBeginDateMax", "priceSnapshot.renewBeginDateMax"),
|
|
SameRenewBeginDate: jsonBool(batch.BatchInfo, "sameRenewBeginDate", "priceSnapshot.sameRenewBeginDate"),
|
|
}
|
|
if snapshot.PackageCode == "" || snapshot.RenewUnitPrice < 0 || len(snapshot.RenewableDurationOptions) == 0 {
|
|
return nil, gerror.New("续费预处理批次报价信息不完整")
|
|
}
|
|
snapshot.RenewDurationUnit = consts.RenewDurationUnitYear
|
|
snapshot.RenewDurationUnitText = consts.RenewDurationUnitText(snapshot.RenewDurationUnit)
|
|
snapshot.BillingMethod = consts.BillingMethodMonth
|
|
return snapshot, nil
|
|
}
|
|
|
|
func renewableDurationOptions(pkg *entity.TSimFlowPackageInfo) ([]int, error) {
|
|
return packageduration.RenewableYears(pkg)
|
|
}
|
|
|
|
func parseRenewDate(value string) (*gtime.Time, error) {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return gtime.Now(), nil
|
|
}
|
|
t, err := gtime.StrToTime(value)
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "续费办理日期格式不正确")
|
|
}
|
|
return t, nil
|
|
}
|
|
|
|
func renewDateString(t *gtime.Time) string {
|
|
if t == nil {
|
|
return ""
|
|
}
|
|
return t.Format("Y-m-d")
|
|
}
|
|
|
|
func parseRenewPeriodDate(value string, fieldName string) (time.Time, error) {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return time.Time{}, gerror.Newf("%s不能为空", fieldName)
|
|
}
|
|
for _, layout := range []string{"2006-01-02", "2006-01-02 15:04:05"} {
|
|
if parsed, err := time.ParseInLocation(layout, value, time.Local); err == nil {
|
|
return renewDayEndTime(parsed), nil
|
|
}
|
|
}
|
|
return time.Time{}, gerror.Newf("%s格式不正确", fieldName)
|
|
}
|
|
|
|
func sameDate(left time.Time, right time.Time) bool {
|
|
leftYear, leftMonth, leftDay := left.Date()
|
|
rightYear, rightMonth, rightDay := right.Date()
|
|
return leftYear == rightYear && leftMonth == rightMonth && leftDay == rightDay
|
|
}
|
|
|
|
func containsInt(values []int, target int) bool {
|
|
for _, value := range values {
|
|
if value == target {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func containsString(values []string, target string) bool {
|
|
target = strings.TrimSpace(target)
|
|
if target == "" {
|
|
return false
|
|
}
|
|
for _, value := range values {
|
|
if strings.TrimSpace(value) == target {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func normalizeLegacyRenewYears(raw int) int {
|
|
if raw <= 0 {
|
|
return 0
|
|
}
|
|
if raw >= 12 && raw%12 == 0 {
|
|
return raw / 12
|
|
}
|
|
return raw
|
|
}
|
|
|
|
func currentOperator(ctx context.Context) batchimport.OperatorContext {
|
|
return batchimport.OperatorContext{
|
|
TenantId: contexts.GetTenantId(ctx),
|
|
DeptId: contexts.GetDeptId(ctx),
|
|
UserId: contexts.GetUserId(ctx),
|
|
}
|
|
}
|
|
|
|
func roundAmount(value float64) float64 {
|
|
return math.Round(value*100) / 100
|
|
}
|
|
|
|
func pendingStatus(importStatus string) string {
|
|
if importStatus == "" {
|
|
return "pending"
|
|
}
|
|
return importStatus
|
|
}
|
|
|
|
func publishPendingExecute(ctx context.Context, batchNo string, batchType int, tenantId string) {
|
|
if strings.TrimSpace(batchNo) == "" || batchType == 0 {
|
|
return
|
|
}
|
|
if err := rabbitmq.Publish(ctx, consts.QueueBatchExecute, batchimport.Message{
|
|
BatchNo: batchNo,
|
|
BatchType: batchType,
|
|
}); err != nil {
|
|
g.Log().Errorf(ctx, "renew/change batch async publish failed, batchNo=%s, batchType=%d, tenantId=%s, error=%+v",
|
|
batchNo, batchType, tenantId, err)
|
|
}
|
|
}
|
|
|
|
type prepareResultBuilder func(ctx context.Context, batch *entity.BatchMain) map[string]any
|
|
|
|
func loadPrepareResult(
|
|
ctx context.Context,
|
|
batchNo string,
|
|
targetBatchType int,
|
|
snapshotFn func(*entity.BatchMain) (*gjson.Json, error),
|
|
fieldsFn prepareResultBuilder,
|
|
) (*model.TSimCardRenewPrepareResultRes, error) {
|
|
batchNo = strings.TrimSpace(batchNo)
|
|
if batchNo == "" {
|
|
return nil, gerror.New("批次号不能为空")
|
|
}
|
|
columns := dao.BatchMain.Columns()
|
|
batch := &entity.BatchMain{}
|
|
if err := dao.BatchMain.Ctx(ctx).
|
|
Where(columns.BatchNo, batchNo).
|
|
Where(columns.TenantId, contexts.GetTenantId(ctx)).
|
|
Where(columns.CreatedDept, contexts.GetDeptId(ctx)).
|
|
Scan(batch); err != nil {
|
|
return nil, gerror.Wrap(err, "查询预处理批次失败")
|
|
}
|
|
if batch.Id == 0 {
|
|
return nil, gerror.New("预处理批次不存在或不可见")
|
|
}
|
|
if batch.BatchType != consts.BatchTypePrepare && !(batch.BatchInfo != nil && batch.BatchInfo.Get("targetBatchType").Int() == targetBatchType) {
|
|
return nil, gerror.New("批次类型不匹配")
|
|
}
|
|
|
|
asyncStatus := consts.BatchAsyncStatusPending
|
|
runResult := consts.BatchRunResultPending
|
|
statusKey := "pending"
|
|
switch {
|
|
case batch.BatchAsyncStatus == consts.BatchAsyncStatusProcessing || batch.RunResult == consts.BatchRunResultRunning:
|
|
asyncStatus = consts.BatchAsyncStatusProcessing
|
|
runResult = consts.BatchRunResultRunning
|
|
statusKey = "processing"
|
|
case batch.BatchAsyncStatus == consts.BatchAsyncStatusSuccess && batch.RunResult == consts.BatchRunResultSuccess:
|
|
asyncStatus = consts.BatchAsyncStatusSuccess
|
|
runResult = consts.BatchRunResultSuccess
|
|
statusKey = "success"
|
|
case batch.BatchAsyncStatus == consts.BatchAsyncStatusFailed || batch.RunResult == consts.BatchRunResultFailed:
|
|
asyncStatus = consts.BatchAsyncStatusFailed
|
|
runResult = consts.BatchRunResultFailed
|
|
statusKey = "failed"
|
|
case batch.BatchAsyncStatus == consts.BatchAsyncStatusPartialSuccess || batch.RunResult == consts.BatchRunResultPartialSuccess:
|
|
asyncStatus = consts.BatchAsyncStatusFailed
|
|
runResult = consts.BatchRunResultFailed
|
|
statusKey = "failed"
|
|
}
|
|
|
|
res := &model.TSimCardRenewPrepareResultRes{
|
|
BatchNo: batch.BatchNo,
|
|
BatchType: batch.BatchType,
|
|
TargetBatchType: targetBatchType,
|
|
Status: statusKey,
|
|
BatchAsyncStatus: asyncStatus,
|
|
RunResult: runResult,
|
|
Total: batch.TotalCount,
|
|
SuccessCount: batch.SuccessCount,
|
|
FailCount: batch.FailCount,
|
|
FailReason: strings.TrimSpace(batch.FailReason),
|
|
StartTime: batch.StartTime,
|
|
EndTime: batch.EndTime,
|
|
}
|
|
if snapshot, err := snapshotFn(batch); err == nil && snapshot != nil {
|
|
res.Snapshot = snapshot
|
|
}
|
|
if fieldsFn != nil {
|
|
if fields := fieldsFn(ctx, batch); fields != nil {
|
|
if value, ok := fields["customerAgentId"]; ok {
|
|
res.CustomerAgentId = gconv.Int64(value)
|
|
}
|
|
if value, ok := fields["customerName"]; ok {
|
|
res.CustomerName = gconv.String(value)
|
|
}
|
|
if value, ok := fields["packageCode"]; ok {
|
|
res.PackageCode = gconv.String(value)
|
|
}
|
|
if value, ok := fields["packageName"]; ok {
|
|
res.PackageName = gconv.String(value)
|
|
}
|
|
if value, ok := fields["billingMethod"]; ok {
|
|
res.BillingMethod = gconv.Int(value)
|
|
}
|
|
if value, ok := fields["renewDurationUnit"]; ok {
|
|
res.RenewDurationUnit = gconv.String(value)
|
|
}
|
|
if value, ok := fields["renewDurationUnitText"]; ok {
|
|
res.RenewDurationUnitText = gconv.String(value)
|
|
}
|
|
if value, ok := fields["durationValue"]; ok {
|
|
res.DurationValue = gconv.Int(value)
|
|
}
|
|
if value, ok := fields["durationYears"]; ok {
|
|
res.DurationYears = gconv.Int(value)
|
|
}
|
|
if value, ok := fields["oldPackageCode"]; ok {
|
|
res.OldPackageCode = gconv.String(value)
|
|
}
|
|
if value, ok := fields["oldPackageName"]; ok {
|
|
res.OldPackageName = gconv.String(value)
|
|
}
|
|
if value, ok := fields["oldPackageType"]; ok {
|
|
res.OldPackageType = gconv.Int(value)
|
|
}
|
|
if value, ok := fields["oldPackageFlowMb"]; ok {
|
|
res.OldPackageFlowMb = gconv.Float64(value)
|
|
}
|
|
if value, ok := fields["quantity"]; ok {
|
|
res.Quantity = gconv.Int(value)
|
|
}
|
|
if value, ok := fields["renewUnitPrice"]; ok {
|
|
res.RenewUnitPrice = gconv.Float64(value)
|
|
}
|
|
if value, ok := fields["priceSource"]; ok {
|
|
res.PriceSource = gconv.String(value)
|
|
}
|
|
if value, ok := fields["renewableDurationOptions"]; ok {
|
|
res.RenewableDurationOptions = packageduration.NormalizeRenewableYears(gconv.Ints(value))
|
|
}
|
|
if value, ok := fields["renewablePackageCodes"]; ok {
|
|
res.RenewablePackageCodes = uniqueRenewStrings(gconv.Strings(value))
|
|
}
|
|
if value, ok := fields["renewablePackages"]; ok {
|
|
res.RenewablePackages = renewPackageOptionsFromValue(gvar.New(value))
|
|
}
|
|
if value, ok := fields["agentDefaultPriceId"]; ok {
|
|
res.AgentDefaultPriceId = gconv.Int64(value)
|
|
}
|
|
if value, ok := fields["targetPriceId"]; ok {
|
|
res.TargetPriceId = gconv.Int64(value)
|
|
}
|
|
if value, ok := fields["isTopDept"]; ok {
|
|
res.IsTopDept = gconv.Bool(value)
|
|
}
|
|
if value, ok := fields["defaultPaymentMode"]; ok {
|
|
res.DefaultPaymentMode = gconv.String(value)
|
|
}
|
|
if value, ok := fields["availableBalance"]; ok {
|
|
res.AvailableBalance = gconv.Float64(value)
|
|
}
|
|
if value, ok := fields["renewBeginDate"]; ok {
|
|
res.RenewBeginDate = gconv.String(value)
|
|
}
|
|
if value, ok := fields["renewBeginDateMin"]; ok {
|
|
res.RenewBeginDateMin = gconv.String(value)
|
|
}
|
|
if value, ok := fields["renewBeginDateMax"]; ok {
|
|
res.RenewBeginDateMax = gconv.String(value)
|
|
}
|
|
if value, ok := fields["sameRenewBeginDate"]; ok {
|
|
res.SameRenewBeginDate = gconv.Bool(value)
|
|
}
|
|
}
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func renewPriceSnapshotFromBatch(batch *entity.BatchMain) (*gjson.Json, error) {
|
|
if batch == nil {
|
|
return nil, nil
|
|
}
|
|
return gjson.New(buildRenewPrepareFields(batch)), nil
|
|
}
|
|
|
|
func buildRenewResultFields(_ context.Context, batch *entity.BatchMain) map[string]any {
|
|
if batch == nil {
|
|
return nil
|
|
}
|
|
return buildRenewPrepareFields(batch)
|
|
}
|
|
|
|
func buildRenewPrepareFields(batch *entity.BatchMain) map[string]any {
|
|
if batch == nil {
|
|
return nil
|
|
}
|
|
fields := prepareInfoMap(batch.BatchInfo)
|
|
if fields == nil {
|
|
fields = map[string]any{}
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "customerAgentId", "customer.deptId", "priceSnapshot.customerAgentId"); value != nil {
|
|
fields["customerAgentId"] = gconv.Int64(value)
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "customerName", "customer.name"); value != nil {
|
|
fields["customerName"] = strings.TrimSpace(value.String())
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "packageCode", "priceSnapshot.packageCode"); value != nil {
|
|
fields["packageCode"] = strings.TrimSpace(value.String())
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "packageName", "priceSnapshot.packageName"); value != nil {
|
|
fields["packageName"] = strings.TrimSpace(value.String())
|
|
}
|
|
fields["billingMethod"] = consts.BillingMethodMonth
|
|
fields["renewDurationUnit"] = consts.RenewDurationUnitYear
|
|
fields["renewDurationUnitText"] = consts.RenewDurationUnitText(consts.RenewDurationUnitYear)
|
|
if value := jsonField(batch.BatchInfo, "priceSource", "priceSnapshot.priceSource"); value != nil {
|
|
fields["priceSource"] = strings.TrimSpace(value.String())
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "agentDefaultPriceId", "priceSnapshot.agentDefaultPriceId"); value != nil {
|
|
fields["agentDefaultPriceId"] = gconv.Int64(value)
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "targetPriceId", "priceSnapshot.targetPriceId"); value != nil {
|
|
fields["targetPriceId"] = gconv.Int64(value)
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "renewUnitPrice", "priceSnapshot.renewUnitPrice"); value != nil {
|
|
fields["renewUnitPrice"] = roundAmount(gconv.Float64(value))
|
|
}
|
|
if values := jsonInts(batch.BatchInfo, "renewableDurationOptions", "priceSnapshot.renewableDurationOptions"); len(values) > 0 {
|
|
fields["renewableDurationOptions"] = packageduration.NormalizeRenewableYears(values)
|
|
}
|
|
if values := jsonStrings(batch.BatchInfo, "renewablePackageCodes", "priceSnapshot.renewablePackageCodes"); len(values) > 0 {
|
|
fields["renewablePackageCodes"] = values
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "renewablePackages", "priceSnapshot.renewablePackages"); value != nil {
|
|
fields["renewablePackages"] = renewPackageOptionsFromValue(value)
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "renewBeginDate", "priceSnapshot.renewBeginDate"); value != nil {
|
|
fields["renewBeginDate"] = strings.TrimSpace(value.String())
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "renewBeginDateMin", "priceSnapshot.renewBeginDateMin"); value != nil {
|
|
fields["renewBeginDateMin"] = strings.TrimSpace(value.String())
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "renewBeginDateMax", "priceSnapshot.renewBeginDateMax"); value != nil {
|
|
fields["renewBeginDateMax"] = strings.TrimSpace(value.String())
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "sameRenewBeginDate", "priceSnapshot.sameRenewBeginDate"); value != nil {
|
|
fields["sameRenewBeginDate"] = gconv.Bool(value)
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "durationValue", "durationYears"); value != nil {
|
|
fields["durationValue"] = gconv.Int(value)
|
|
fields["durationYears"] = gconv.Int(value)
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "quantity"); value != nil {
|
|
fields["quantity"] = gconv.Int(value)
|
|
} else if batch.TotalCount > 0 {
|
|
fields["quantity"] = batch.TotalCount
|
|
}
|
|
return fields
|
|
}
|
|
|
|
func prepareInfoMap(info *gjson.Json) map[string]any {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
raw := info.Map()
|
|
fields := make(map[string]any, len(raw))
|
|
for key, value := range raw {
|
|
fields[key] = value
|
|
}
|
|
return fields
|
|
}
|
|
|
|
func jsonField(info *gjson.Json, paths ...string) *gvar.Var {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
for _, path := range paths {
|
|
value := info.Get(path)
|
|
if value != nil && !value.IsNil() {
|
|
return value
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func jsonString(info *gjson.Json, paths ...string) string {
|
|
if value := jsonField(info, paths...); value != nil {
|
|
return strings.TrimSpace(value.String())
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func jsonInt64(info *gjson.Json, paths ...string) int64 {
|
|
if value := jsonField(info, paths...); value != nil {
|
|
return gconv.Int64(value)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func jsonInt(info *gjson.Json, paths ...string) int {
|
|
if value := jsonField(info, paths...); value != nil {
|
|
return gconv.Int(value)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func jsonFloat64(info *gjson.Json, paths ...string) float64 {
|
|
if value := jsonField(info, paths...); value != nil {
|
|
return gconv.Float64(value)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func jsonBool(info *gjson.Json, paths ...string) bool {
|
|
if value := jsonField(info, paths...); value != nil {
|
|
return gconv.Bool(value)
|
|
}
|
|
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 uniqueRenewStrings(values)
|
|
}
|
|
}
|
|
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
|
|
}
|
|
|
|
func renewPackageOptionsFromValue(value *gvar.Var) []model.TSimCardRenewPackageOption {
|
|
if value == nil || value.IsNil() {
|
|
return nil
|
|
}
|
|
var options []model.TSimCardRenewPackageOption
|
|
if err := gconv.Structs(value.Interfaces(), &options); err == nil && len(options) > 0 {
|
|
return options
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func uniqueRenewStrings(list []string) []string {
|
|
result := make([]string, 0, len(list))
|
|
seen := make(map[string]struct{}, len(list))
|
|
for _, item := range list {
|
|
item = strings.TrimSpace(item)
|
|
if item == "" {
|
|
continue
|
|
}
|
|
if _, ok := seen[item]; ok {
|
|
continue
|
|
}
|
|
seen[item] = struct{}{}
|
|
result = append(result, item)
|
|
}
|
|
return result
|
|
}
|