1094 lines
40 KiB
Go
1094 lines
40 KiB
Go
package iot
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/batchmetadata"
|
|
"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/logic/iot/packageduration"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/logic/iot/simcardbatch/change"
|
|
"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"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/iot_excelize"
|
|
|
|
"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/net/ghttp"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
"github.com/xuri/excelize/v2"
|
|
)
|
|
|
|
const (
|
|
cardChangeEffectiveImmediate = "immediate"
|
|
cardChangeEffectiveNextMonth = "next_month"
|
|
)
|
|
|
|
type sIotTSimCardChange struct{}
|
|
|
|
func NewIotTSimCardChange() *sIotTSimCardChange {
|
|
return &sIotTSimCardChange{}
|
|
}
|
|
|
|
func init() {
|
|
service.RegisterIotTSimCardChange(NewIotTSimCardChange())
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) DownloadExcelTemplate(ctx context.Context) error {
|
|
f := excelize.NewFile()
|
|
defer func() { _ = f.Close() }()
|
|
|
|
headers := change.ExcelTemplateHeaders()
|
|
if err := f.SetSheetRow("Sheet1", "A1", &headers); err != nil {
|
|
return err
|
|
}
|
|
if err := iot_excelize.ApplyHeaderColumnsTextStyle(f); err != nil {
|
|
return err
|
|
}
|
|
return iot_excelize.WriteHTTP(ctx, f, "sim_card_change_template")
|
|
}
|
|
|
|
// SubmitExcel Excel 仅作为 prepare source:构造 batch_type=4 的 Pending 变更预处理批次。
|
|
func (s *sIotTSimCardChange) SubmitExcel(ctx context.Context, in *model.TSimCardChangeExcelSubmitParam) (*model.TSimCardChangeExcelSubmitRes, error) {
|
|
if in == nil {
|
|
in = &model.TSimCardChangeExcelSubmitParam{}
|
|
}
|
|
|
|
file, err := s.getChangeUploadFile(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
excelProcessor := change.NewExcelProcessor()
|
|
processor := change.NewPrepareProcessor()
|
|
req := batchimport.SubmitRequest{
|
|
SourceChannel: batchmetadata.Console,
|
|
ExecutionMode: batchmetadata.Bulk,
|
|
File: file,
|
|
SourceType: consts.BatchSourceTypeFile,
|
|
Operator: currentOperator(ctx),
|
|
BeforeSave: func(ctx context.Context, tx gdb.TX, request *batchimport.SubmitRequest, rows []batchimport.Row) error {
|
|
opts := excelProcessor.ExcelSubmitOptions()
|
|
if opts == nil {
|
|
return gerror.New("excel change params are empty")
|
|
}
|
|
submitParam := opts.ToBatchSubmitParam()
|
|
request.BizParams = submitParam
|
|
changeRows, err := batchRowsToChangeRows(rows)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return s.prepareChangeRows(ctx, tx, request, processor, submitParam, changeRows)
|
|
},
|
|
}
|
|
|
|
result, err := batchimport.CreatePendingUpload(ctx, req, processor, batchimport.PendingOptions{
|
|
PrepareRows: func(ctx context.Context, rows []batchimport.Row) error {
|
|
if len(rows) == 0 {
|
|
return nil
|
|
}
|
|
return excelProcessor.ValidateRows(ctx, rows)
|
|
},
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res := changeExcelSubmitResult(result)
|
|
if result.ImportStatus != consts.ImportStatusSuccess {
|
|
return res, nil
|
|
}
|
|
|
|
if err = s.mergeChangeExcelBatchFileInfo(ctx, result.BatchNo, result.ImportFileOssId, result.ResultFileOssId, result.ResultDownloadId); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
publishPendingExecute(ctx, result.BatchNo, consts.BatchTypePrepare, contexts.GetTenantId(ctx))
|
|
return res, nil
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) PrepareBatch(ctx context.Context, in *model.TSimCardChangeBatchPrepareParam) (*model.TSimCardChangeBatchPrepareRes, error) {
|
|
if in == nil {
|
|
in = &model.TSimCardChangeBatchPrepareParam{}
|
|
}
|
|
in.SourceBatchNo = strings.TrimSpace(in.SourceBatchNo)
|
|
if in.SourceBatchNo == "" {
|
|
return nil, gerror.New("sourceBatchNo cannot be empty")
|
|
}
|
|
|
|
rows, err := s.buildChangeRows(ctx, in.SourceBatchNo)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.prepareRows(ctx, &model.TSimCardChangeBatchSubmitParam{}, rows, consts.BatchSourceTypeBatchSelect, in.SourceBatchNo, in.SourceBatchNo, &model.TSimCardChangeBatchPrepareParam{
|
|
SourceBatchNo: in.SourceBatchNo,
|
|
})
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) PrepareInput(ctx context.Context, in *model.TSimCardChangeInputPrepareParam) (*model.TSimCardChangeBatchPrepareRes, error) {
|
|
if in == nil {
|
|
in = &model.TSimCardChangeInputPrepareParam{}
|
|
}
|
|
rows, err := change.ParseInputRows(ctx, in.Input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
changeRows, err := batchRowsToChangeRows(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.prepareRows(ctx, changeSubmitParamFromInput(in), changeRows, consts.BatchSourceTypeBatchInput, "", "input-prepare", nil)
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) PrepareRange(ctx context.Context, in *model.TSimCardChangeRangePrepareParam) (*model.TSimCardChangeBatchPrepareRes, error) {
|
|
if in == nil {
|
|
in = &model.TSimCardChangeRangePrepareParam{}
|
|
}
|
|
rows, err := change.BuildRangeRows(ctx, in.StartIccid, in.EndIccid, in.StartMsisdn, in.EndMsisdn, in.StartImsi, in.EndImsi)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
changeRows, err := batchRowsToChangeRows(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.prepareRows(ctx, changeSubmitParamFromRange(in), changeRows, consts.BatchSourceTypeRange, "", "range-prepare", nil)
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) InputOptions(ctx context.Context, in *model.TSimCardChangeInputOptionsParam) (*model.TSimCardChangeOptionsRes, error) {
|
|
if in == nil {
|
|
in = &model.TSimCardChangeInputOptionsParam{}
|
|
}
|
|
rows, err := change.ParseInputRows(ctx, in.Input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
changeRows, err := batchRowsToChangeRows(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.changeOptions(ctx, changeRows, "")
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) RangeOptions(ctx context.Context, in *model.TSimCardChangeRangeOptionsParam) (*model.TSimCardChangeOptionsRes, error) {
|
|
if in == nil {
|
|
in = &model.TSimCardChangeRangeOptionsParam{}
|
|
}
|
|
rows, err := change.BuildRangeRows(ctx, in.StartIccid, in.EndIccid, in.StartMsisdn, in.EndMsisdn, in.StartImsi, in.EndImsi)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
changeRows, err := batchRowsToChangeRows(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.changeOptions(ctx, changeRows, "")
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) changeOptions(ctx context.Context, rows []*change.Row, sourceBatchNo string) (*model.TSimCardChangeOptionsRes, error) {
|
|
if len(rows) == 0 {
|
|
return nil, gerror.New("change cards cannot be empty")
|
|
}
|
|
processor := change.NewPrepareProcessor()
|
|
operator := currentOperator(ctx)
|
|
var prepared *change.PrepareContext
|
|
err := g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
var err error
|
|
prepared, err = processor.ValidatePrepareContext(ctx, tx, &model.TSimCardChangeBatchPrepareParam{
|
|
SourceBatchNo: strings.TrimSpace(sourceBatchNo),
|
|
}, operator, rows)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if prepared == nil || prepared.OldPackage == nil {
|
|
return nil, gerror.New("change prepare result is incomplete")
|
|
}
|
|
packages, err := s.loadChangeablePackageOptions(ctx, prepared.CustomerAgentId, prepared.OldPackage)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(packages) == 0 {
|
|
return nil, gerror.New("current package has no available changeable packages")
|
|
}
|
|
return &model.TSimCardChangeOptionsRes{
|
|
CustomerAgentId: prepared.CustomerAgentId,
|
|
CustomerName: prepared.CustomerName,
|
|
OldPackageCode: prepared.OldPackage.PackageCode,
|
|
OldPackageName: prepared.OldPackage.PackageName,
|
|
OldPackageType: prepared.OldPackage.PackageType,
|
|
Quantity: prepared.Quantity,
|
|
Packages: packages,
|
|
}, nil
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) prepareRows(ctx context.Context, submitParam *model.TSimCardChangeBatchSubmitParam, rows []*change.Row, sourceType int, sourceBatchNo string, sourceName string, prepareParam *model.TSimCardChangeBatchPrepareParam) (*model.TSimCardChangeBatchPrepareRes, error) {
|
|
if submitParam == nil {
|
|
submitParam = &model.TSimCardChangeBatchSubmitParam{}
|
|
}
|
|
processor := change.NewPrepareProcessor()
|
|
importRows := changeRowsToBatchRows(rows)
|
|
|
|
req := batchimport.SubmitRequest{
|
|
SourceChannel: batchmetadata.Console,
|
|
ExecutionMode: batchmetadata.Bulk,
|
|
BizParams: prepareParamOrSubmitParam(prepareParam, submitParam),
|
|
SourceType: sourceType,
|
|
SourceBatchNo: strings.TrimSpace(sourceBatchNo),
|
|
Operator: currentOperator(ctx),
|
|
BeforeSave: func(ctx context.Context, tx gdb.TX, request *batchimport.SubmitRequest, batchRows []batchimport.Row) error {
|
|
changeRows, err := batchRowsToChangeRows(batchRows)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return s.prepareChangeRows(ctx, tx, request, processor, submitParam, changeRows)
|
|
},
|
|
}
|
|
result, err := batchimport.CreatePendingRows(ctx, req, processor, importRows, batchimport.PendingOptions{
|
|
PrepareRows: func(ctx context.Context, rows []batchimport.Row) error {
|
|
if len(rows) == 0 {
|
|
return nil
|
|
}
|
|
return processor.ValidateRows(ctx, rows)
|
|
},
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
res := &model.TSimCardChangeBatchPrepareRes{
|
|
BatchNo: result.BatchNo,
|
|
BatchType: result.BatchType,
|
|
TargetBatchType: consts.BatchTypeChange,
|
|
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.TSimCardChangePrepareSnapshot); ok && snapshot != nil {
|
|
fillChangePrepareRes(res, snapshot)
|
|
}
|
|
|
|
publishPendingExecute(ctx, res.BatchNo, res.BatchType, contexts.GetTenantId(ctx))
|
|
return res, nil
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) prepareChangeRows(ctx context.Context, tx gdb.TX, request *batchimport.SubmitRequest, processor *change.PrepareProcessor, submitParam *model.TSimCardChangeBatchSubmitParam, rows []*change.Row) error {
|
|
prepared, err := processor.ValidatePrepareContext(ctx, tx, &model.TSimCardChangeBatchPrepareParam{
|
|
SourceBatchNo: request.SourceBatchNo,
|
|
}, request.Operator, rows)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if prepared == nil || prepared.CustomerAgentId <= 0 || prepared.OldPackage == nil || strings.TrimSpace(prepared.OldPackage.PackageCode) == "" {
|
|
return gerror.New("change prepare result is incomplete")
|
|
}
|
|
if submitParam != nil && strings.TrimSpace(submitParam.NewPackageCode) != "" {
|
|
if strings.TrimSpace(submitParam.NewPackageCode) == strings.TrimSpace(prepared.OldPackage.PackageCode) {
|
|
return gerror.New("newPackageCode cannot equal current package")
|
|
}
|
|
if !containsStringJSON(prepared.OldPackage.ChangeablePackageCodes, submitParam.NewPackageCode) {
|
|
return gerror.New("newPackageCode is not in current package changeable range")
|
|
}
|
|
}
|
|
request.TargetDeptId = prepared.CustomerAgentId
|
|
request.ExecutionResult = &model.TSimCardChangePrepareSnapshot{
|
|
SourceBatchNo: strings.TrimSpace(request.SourceBatchNo),
|
|
CustomerAgentId: prepared.CustomerAgentId,
|
|
CustomerName: prepared.CustomerName,
|
|
OldPackageCode: prepared.OldPackage.PackageCode,
|
|
OldPackageName: prepared.OldPackage.PackageName,
|
|
OldPackageType: prepared.OldPackage.PackageType,
|
|
OldPackageFlowMb: prepared.OldPackage.FlowMb,
|
|
Quantity: prepared.Quantity,
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SubmitBatch 作为 confirm 的语义:CAS 转换 batch_type=4+Success -> batch_type=102 Pending,发布异步任务。
|
|
func (s *sIotTSimCardChange) SubmitBatch(ctx context.Context, in *model.TSimCardChangeBatchSubmitParam) (*model.TSimCardChangeBatchSubmitRes, error) {
|
|
if in == nil {
|
|
in = &model.TSimCardChangeBatchSubmitParam{}
|
|
}
|
|
in.BatchNo = strings.TrimSpace(in.BatchNo)
|
|
in.NewPackageCode = strings.TrimSpace(in.NewPackageCode)
|
|
in.EffectiveMode = strings.TrimSpace(in.EffectiveMode)
|
|
if in.BatchNo == "" {
|
|
return nil, gerror.New("batchNo cannot be empty")
|
|
}
|
|
if in.NewPackageCode == "" {
|
|
return nil, gerror.New("newPackageCode cannot be empty")
|
|
}
|
|
if in.DurationMonths <= 0 {
|
|
return nil, gerror.New("durationMonths must be greater than 0")
|
|
}
|
|
if in.GiftDurationMonths < 0 {
|
|
return nil, gerror.New("giftDurationMonths cannot be negative")
|
|
}
|
|
if !in.OfflineContractCompleted {
|
|
return nil, gerror.New("please confirm offline contract is completed")
|
|
}
|
|
packageBeginTime, err := calcChangeBeginTime(in.EffectiveMode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
operator := currentOperator(ctx)
|
|
var prepareSnapshot *model.TSimCardChangePrepareSnapshot
|
|
var priceSnapshot *model.TSimCardChangePriceSnapshot
|
|
var converted *change.AsyncConfirmResult
|
|
var sourceBatchNo string
|
|
|
|
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
|
|
}
|
|
prepareSnapshot, err = changePrepareSnapshotFromBatch(batch)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
priceSnapshot, err = changePriceSnapshotFromBatch(batch)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
priceSnapshot, err = s.refreshChangeConfirmPriceSnapshot(ctx, tx, prepareSnapshot, priceSnapshot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sourceBatchNo = prepareSnapshot.SourceBatchNo
|
|
if err = validateChangeConfirmContract(
|
|
prepareSnapshot,
|
|
priceSnapshot,
|
|
jsonString(batch.BatchInfo, "effectiveMode", "changePrepareSnapshot.effectiveMode"),
|
|
in,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
converted, err = change.ConvertPrepareToAsync(ctx, tx, &change.AsyncConfirmInput{
|
|
BatchNo: in.BatchNo,
|
|
TargetBatchType: consts.BatchTypeChange,
|
|
NewPackageCode: priceSnapshot.NewPackageCode,
|
|
DurationMonths: in.DurationMonths,
|
|
GiftDurationMonths: in.GiftDurationMonths,
|
|
EffectiveMode: in.EffectiveMode,
|
|
OfflineContract: in.OfflineContractCompleted,
|
|
BuyerNote: in.BuyerNote,
|
|
AdminNote: in.AdminNote,
|
|
Remark: in.Remark,
|
|
PriceSnapshot: priceSnapshot,
|
|
SourceBatchNo: prepareSnapshot.SourceBatchNo,
|
|
PackageBeginTimeStr: formatChangeTime(packageBeginTime),
|
|
Operator: operator,
|
|
})
|
|
return err
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if converted == nil || prepareSnapshot == nil || priceSnapshot == nil {
|
|
return nil, gerror.New("change submit result is empty")
|
|
}
|
|
|
|
publishPendingExecute(ctx, converted.BatchNo, converted.BatchType, contexts.GetTenantId(ctx))
|
|
|
|
return &model.TSimCardChangeBatchSubmitRes{
|
|
BatchNo: converted.BatchNo,
|
|
BatchType: converted.BatchType,
|
|
BatchAsyncStatus: converted.BatchAsyncStatus,
|
|
SourceBatchNo: sourceBatchNo,
|
|
Status: "pending",
|
|
NewPackageCode: priceSnapshot.NewPackageCode,
|
|
NewPackageName: priceSnapshot.NewPackageName,
|
|
OldPackageCode: prepareSnapshot.OldPackageCode,
|
|
OldPackageName: prepareSnapshot.OldPackageName,
|
|
DurationMonths: in.DurationMonths,
|
|
GiftDurationMonths: in.GiftDurationMonths,
|
|
EffectiveMode: in.EffectiveMode,
|
|
PackageBeginTime: packageBeginTime,
|
|
PackageEndTime: nil,
|
|
}, nil
|
|
}
|
|
|
|
func validateChangeConfirmContract(
|
|
prepareSnapshot *model.TSimCardChangePrepareSnapshot,
|
|
priceSnapshot *model.TSimCardChangePriceSnapshot,
|
|
preparedEffectiveMode string,
|
|
in *model.TSimCardChangeBatchSubmitParam,
|
|
) error {
|
|
if prepareSnapshot == nil || priceSnapshot == nil || in == nil {
|
|
return gerror.New("change prepare contract is incomplete")
|
|
}
|
|
if strings.TrimSpace(in.NewPackageCode) == strings.TrimSpace(prepareSnapshot.OldPackageCode) {
|
|
return gerror.New("newPackageCode cannot equal current package")
|
|
}
|
|
if strings.TrimSpace(in.NewPackageCode) != strings.TrimSpace(priceSnapshot.NewPackageCode) {
|
|
return gerror.New("newPackageCode differs from prepare contract, please prepare again")
|
|
}
|
|
if !containsInt(priceSnapshot.RenewableDurationOptions, in.DurationMonths) {
|
|
return gerror.New("durationMonths is not allowed for target package")
|
|
}
|
|
preparedEffectiveMode = strings.TrimSpace(preparedEffectiveMode)
|
|
if preparedEffectiveMode != "" && preparedEffectiveMode != strings.TrimSpace(in.EffectiveMode) {
|
|
return gerror.New("effectiveMode differs from prepare contract, please prepare again")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// PrepareResult 查询预处理批次当前异步状态。
|
|
func (s *sIotTSimCardChange) PrepareResult(ctx context.Context, batchNo string) (*model.TSimCardChangePrepareResultRes, error) {
|
|
return loadPrepareResultChange(ctx, batchNo)
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) refreshChangeConfirmPriceSnapshot(ctx context.Context, tx gdb.TX, prepareSnapshot *model.TSimCardChangePrepareSnapshot, priceSnapshot *model.TSimCardChangePriceSnapshot) (*model.TSimCardChangePriceSnapshot, error) {
|
|
if prepareSnapshot == nil || priceSnapshot == nil {
|
|
return nil, gerror.New("change prepare contract is incomplete")
|
|
}
|
|
oldPackageCode := strings.TrimSpace(prepareSnapshot.OldPackageCode)
|
|
newPackageCode := strings.TrimSpace(priceSnapshot.NewPackageCode)
|
|
if oldPackageCode == "" || newPackageCode == "" {
|
|
return nil, gerror.New("change package contract is incomplete")
|
|
}
|
|
if oldPackageCode == newPackageCode {
|
|
return nil, gerror.New("newPackageCode cannot equal current package")
|
|
}
|
|
|
|
oldPackage := &entity.TSimFlowPackageInfo{}
|
|
columns := dao.TSimFlowPackageInfo.Columns()
|
|
if err := tx.Ctx(ctx).Model(dao.TSimFlowPackageInfo.Table()).
|
|
Where(columns.TenantId, contexts.GetTenantId(ctx)).
|
|
Where(columns.PackageCode, oldPackageCode).
|
|
Where(columns.Enabled, 1).
|
|
LockUpdate().
|
|
Scan(oldPackage); err != nil {
|
|
return nil, gerror.Wrap(err, "load current package failed")
|
|
}
|
|
if oldPackage.Id == 0 {
|
|
return nil, gerror.New("current package not found or disabled")
|
|
}
|
|
if !containsStringJSON(oldPackage.ChangeablePackageCodes, newPackageCode) {
|
|
return nil, gerror.Newf("target package %s is not in current package %s changeable range", newPackageCode, oldPackageCode)
|
|
}
|
|
|
|
refreshed, err := s.buildChangePriceSnapshot(ctx, tx, priceSnapshot.CustomerAgentId, newPackageCode, prepareSnapshot.OldPackageType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return refreshed, nil
|
|
}
|
|
|
|
func loadPrepareResultChange(ctx context.Context, batchNo string) (*model.TSimCardChangePrepareResultRes, 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() == consts.BatchTypeChange) {
|
|
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.BatchAsyncStatusPartialSuccess
|
|
runResult = consts.BatchRunResultPartialSuccess
|
|
statusKey = "partial_success"
|
|
}
|
|
|
|
fields := buildChangePrepareFields(batch)
|
|
res := &model.TSimCardChangePrepareResultRes{
|
|
BatchNo: batch.BatchNo,
|
|
BatchType: batch.BatchType,
|
|
TargetBatchType: consts.BatchTypeChange,
|
|
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,
|
|
Snapshot: gjson.New(fields),
|
|
}
|
|
if fields != nil {
|
|
res.CustomerAgentId = gconv.Int64(fields["customerAgentId"])
|
|
res.CustomerName = strings.TrimSpace(gconv.String(fields["customerName"]))
|
|
res.PackageCode = strings.TrimSpace(gconv.String(fields["newPackageCode"]))
|
|
res.PackageName = strings.TrimSpace(gconv.String(fields["newPackageName"]))
|
|
res.OldPackageCode = strings.TrimSpace(gconv.String(fields["oldPackageCode"]))
|
|
res.OldPackageName = strings.TrimSpace(gconv.String(fields["oldPackageName"]))
|
|
res.OldPackageType = gconv.Int(fields["oldPackageType"])
|
|
res.OldPackageFlowMb = gconv.Float64(fields["oldPackageFlowMb"])
|
|
res.Quantity = gconv.Int(fields["quantity"])
|
|
res.PriceSource = strings.TrimSpace(gconv.String(fields["priceSource"]))
|
|
res.AgentDefaultPriceId = gconv.Int64(fields["agentDefaultPriceId"])
|
|
res.TargetPriceId = gconv.Int64(fields["targetPriceId"])
|
|
res.ChangeUnitPrice = gconv.Float64(fields["changeUnitPrice"])
|
|
res.RenewableDurationOptions = gconv.Ints(fields["renewableDurationOptions"])
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) buildChangeRows(ctx context.Context, sourceBatchNo string) ([]*change.Row, error) {
|
|
rows, err := change.NewRepository().GetSourceBindRows(ctx, sourceBatchNo)
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "load source order batch card details failed")
|
|
}
|
|
if len(rows) == 0 {
|
|
return nil, gerror.New("source order batch has no changeable cards")
|
|
}
|
|
return rows, nil
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) 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, "load change prepare batch failed")
|
|
}
|
|
if batch.Id == 0 {
|
|
return nil, gerror.New("change prepare batch not found or not yet completed")
|
|
}
|
|
if batch.BatchInfo == nil {
|
|
return nil, gerror.New("change prepare batch info is missing")
|
|
}
|
|
if batch.BatchInfo.Get("targetBatchType").Int() != consts.BatchTypeChange {
|
|
return nil, gerror.New("batch is not a sim card change prepare batch")
|
|
}
|
|
return batch, nil
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) buildChangePriceSnapshot(ctx context.Context, tx gdb.TX, customerAgentId int64, packageCode string, oldPackageType int) (*model.TSimCardChangePriceSnapshot, error) {
|
|
packageCode = strings.TrimSpace(packageCode)
|
|
if customerAgentId <= 0 {
|
|
return nil, gerror.New("customerAgentId cannot be empty")
|
|
}
|
|
if packageCode == "" {
|
|
return nil, gerror.New("newPackageCode cannot be empty")
|
|
}
|
|
|
|
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).
|
|
Where(pkgColumns.Enabled, 1).
|
|
Scan(pkg); err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil, gerror.New("target package not found or disabled")
|
|
}
|
|
return nil, gerror.Wrap(err, "load target package failed")
|
|
}
|
|
if pkg.Id == 0 {
|
|
return nil, gerror.New("target package not found or disabled")
|
|
}
|
|
if oldPackageType > 0 && pkg.PackageType != oldPackageType {
|
|
return nil, gerror.New("target package type does not match current package type")
|
|
}
|
|
|
|
options, err := packageduration.ChangeMonths(pkg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
snapshot := &model.TSimCardChangePriceSnapshot{
|
|
CustomerAgentId: customerAgentId,
|
|
NewPackageCode: pkg.PackageCode,
|
|
NewPackageName: pkg.PackageName,
|
|
NewPackageType: pkg.PackageType,
|
|
NewPackageFlowMb: pkg.FlowMb,
|
|
PriceSource: "system",
|
|
ChangeUnitPrice: roundAmount(pkg.CostPrice*normalizePriceRatio(pkg.SysDefaultRatio) + pkg.SysDefaultProfit),
|
|
RenewableDurationOptions: options,
|
|
}
|
|
|
|
priceOwnerDeptID, err := loadPackagePriceParentDeptID(ctx, tx, contexts.GetTenantId(ctx), customerAgentId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
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.ChangeUnitPrice = salePrice.AgentShipmentPrice
|
|
return snapshot, nil
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) loadChangeablePackageOptions(ctx context.Context, customerAgentId int64, oldPackage *entity.TSimFlowPackageInfo) ([]model.TSimCardChangePackageOption, error) {
|
|
if oldPackage == nil {
|
|
return nil, gerror.New("current package is empty")
|
|
}
|
|
codes := stringListFromJSON(oldPackage.ChangeablePackageCodes)
|
|
if len(codes) == 0 {
|
|
return nil, gerror.New("current package has no changeable packages configured")
|
|
}
|
|
|
|
options := make([]model.TSimCardChangePackageOption, 0, len(codes))
|
|
err := g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
for _, code := range codes {
|
|
if code == strings.TrimSpace(oldPackage.PackageCode) {
|
|
continue
|
|
}
|
|
snapshot, err := s.buildChangePriceSnapshot(ctx, tx, customerAgentId, code, oldPackage.PackageType)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
options = append(options, model.TSimCardChangePackageOption{
|
|
PackageCode: snapshot.NewPackageCode,
|
|
PackageName: snapshot.NewPackageName,
|
|
PackageType: snapshot.NewPackageType,
|
|
FlowMb: snapshot.NewPackageFlowMb,
|
|
ChangeUnitPrice: snapshot.ChangeUnitPrice,
|
|
RenewableDurationOptions: snapshot.RenewableDurationOptions,
|
|
})
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return options, nil
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) loadChangePriceOverride(ctx context.Context, tx gdb.TX, 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, contexts.GetDeptId(ctx)).
|
|
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, "load change package price failed")
|
|
}
|
|
if price.Id == 0 {
|
|
return nil, nil
|
|
}
|
|
return price, nil
|
|
}
|
|
|
|
func batchRowsToChangeRows(rows []batchimport.Row) ([]*change.Row, error) {
|
|
result := make([]*change.Row, 0, len(rows))
|
|
for _, item := range rows {
|
|
row, ok := item.(*change.Row)
|
|
if !ok || row == nil {
|
|
return nil, gerror.New("change row type is invalid")
|
|
}
|
|
result = append(result, row)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func changeRowsToBatchRows(rows []*change.Row) []batchimport.Row {
|
|
result := make([]batchimport.Row, 0, len(rows))
|
|
for _, row := range rows {
|
|
result = append(result, row)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func fillChangePrepareRes(res *model.TSimCardChangeBatchPrepareRes, snapshot *model.TSimCardChangePrepareSnapshot) {
|
|
res.SourceBatchNo = snapshot.SourceBatchNo
|
|
res.CustomerAgentId = snapshot.CustomerAgentId
|
|
res.CustomerName = snapshot.CustomerName
|
|
res.OldPackageCode = snapshot.OldPackageCode
|
|
res.OldPackageName = snapshot.OldPackageName
|
|
res.OldPackageType = snapshot.OldPackageType
|
|
res.OldPackageFlowMb = snapshot.OldPackageFlowMb
|
|
}
|
|
|
|
func changePrepareSnapshotFromBatch(batch *entity.BatchMain) (*model.TSimCardChangePrepareSnapshot, error) {
|
|
if batch == nil || batch.BatchInfo == nil {
|
|
return nil, gerror.New("change prepare batch info is missing")
|
|
}
|
|
fields := buildChangePrepareFields(batch)
|
|
snapshot := &model.TSimCardChangePrepareSnapshot{
|
|
SourceBatchNo: normalizedChangePrepareSourceBatchNo(batch, gconv.String(fields["sourceBatchNo"])),
|
|
CustomerAgentId: gconv.Int64(fields["customerAgentId"]),
|
|
CustomerName: strings.TrimSpace(gconv.String(fields["customerName"])),
|
|
OldPackageCode: strings.TrimSpace(gconv.String(fields["oldPackageCode"])),
|
|
OldPackageName: strings.TrimSpace(gconv.String(fields["oldPackageName"])),
|
|
OldPackageType: gconv.Int(fields["oldPackageType"]),
|
|
OldPackageFlowMb: gconv.Float64(fields["oldPackageFlowMb"]),
|
|
Quantity: gconv.Int(fields["quantity"]),
|
|
}
|
|
if snapshot.OldPackageCode == "" {
|
|
return nil, gerror.New("change prepare batch info is incomplete")
|
|
}
|
|
return snapshot, nil
|
|
}
|
|
|
|
func normalizedChangePrepareSourceBatchNo(batch *entity.BatchMain, sourceBatchNo string) string {
|
|
if batch == nil || batch.SourceType != consts.BatchSourceTypeBatchSelect {
|
|
return ""
|
|
}
|
|
batchNo := strings.TrimSpace(batch.BatchNo)
|
|
sourceBatchNo = strings.TrimSpace(sourceBatchNo)
|
|
if sourceBatchNo == "" || sourceBatchNo == batchNo {
|
|
sourceBatchNo = strings.TrimSpace(batch.SourceBatchNo)
|
|
}
|
|
if sourceBatchNo == batchNo {
|
|
return ""
|
|
}
|
|
return sourceBatchNo
|
|
}
|
|
|
|
func changePriceSnapshotFromBatch(batch *entity.BatchMain) (*model.TSimCardChangePriceSnapshot, error) {
|
|
if batch == nil || batch.BatchInfo == nil {
|
|
return nil, gerror.New("change prepare batch price info is missing")
|
|
}
|
|
fields := buildChangePrepareFields(batch)
|
|
options := gconv.Ints(fields["renewableDurationOptions"])
|
|
snapshot := &model.TSimCardChangePriceSnapshot{
|
|
CustomerAgentId: gconv.Int64(fields["customerAgentId"]),
|
|
NewPackageCode: strings.TrimSpace(gconv.String(fields["newPackageCode"])),
|
|
NewPackageName: strings.TrimSpace(gconv.String(fields["newPackageName"])),
|
|
NewPackageType: gconv.Int(fields["newPackageType"]),
|
|
NewPackageFlowMb: gconv.Float64(fields["newPackageFlowMb"]),
|
|
PriceSource: strings.TrimSpace(gconv.String(fields["priceSource"])),
|
|
AgentDefaultPriceId: gconv.Int64(fields["agentDefaultPriceId"]),
|
|
TargetPriceId: gconv.Int64(fields["targetPriceId"]),
|
|
ChangeUnitPrice: roundAmount(gconv.Float64(fields["changeUnitPrice"])),
|
|
RenewableDurationOptions: options,
|
|
}
|
|
if snapshot.CustomerAgentId <= 0 || snapshot.NewPackageCode == "" || snapshot.ChangeUnitPrice < 0 || len(snapshot.RenewableDurationOptions) == 0 {
|
|
return nil, gerror.New("change prepare batch price info is incomplete")
|
|
}
|
|
return snapshot, nil
|
|
}
|
|
|
|
func buildChangePrepareFields(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", "changePrepareSnapshot.customerAgentId", "changePriceSnapshot.customerAgentId"); value != nil {
|
|
fields["customerAgentId"] = gconv.Int64(value)
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "customerName", "customer.name", "changePrepareSnapshot.customerName"); value != nil {
|
|
fields["customerName"] = strings.TrimSpace(value.String())
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "sourceBatchNo", "changePrepareSnapshot.sourceBatchNo"); value != nil {
|
|
fields["sourceBatchNo"] = strings.TrimSpace(value.String())
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "oldPackageCode", "changePrepareSnapshot.oldPackageCode"); value != nil {
|
|
fields["oldPackageCode"] = strings.TrimSpace(value.String())
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "oldPackageName", "changePrepareSnapshot.oldPackageName"); value != nil {
|
|
fields["oldPackageName"] = strings.TrimSpace(value.String())
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "oldPackageType", "changePrepareSnapshot.oldPackageType"); value != nil {
|
|
fields["oldPackageType"] = gconv.Int(value)
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "oldPackageFlowMb", "changePrepareSnapshot.oldPackageFlowMb"); value != nil {
|
|
fields["oldPackageFlowMb"] = gconv.Float64(value)
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "newPackageCode", "changePriceSnapshot.newPackageCode"); value != nil {
|
|
fields["newPackageCode"] = strings.TrimSpace(value.String())
|
|
fields["packageCode"] = strings.TrimSpace(value.String())
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "newPackageName", "changePriceSnapshot.newPackageName"); value != nil {
|
|
fields["newPackageName"] = strings.TrimSpace(value.String())
|
|
fields["packageName"] = strings.TrimSpace(value.String())
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "newPackageType", "changePriceSnapshot.newPackageType"); value != nil {
|
|
fields["newPackageType"] = gconv.Int(value)
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "newPackageFlowMb", "changePriceSnapshot.newPackageFlowMb"); value != nil {
|
|
fields["newPackageFlowMb"] = gconv.Float64(value)
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "priceSource", "changePriceSnapshot.priceSource"); value != nil {
|
|
fields["priceSource"] = strings.TrimSpace(value.String())
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "agentDefaultPriceId", "changePriceSnapshot.agentDefaultPriceId"); value != nil {
|
|
fields["agentDefaultPriceId"] = gconv.Int64(value)
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "targetPriceId", "changePriceSnapshot.targetPriceId"); value != nil {
|
|
fields["targetPriceId"] = gconv.Int64(value)
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "changeUnitPrice", "changePriceSnapshot.changeUnitPrice"); value != nil {
|
|
fields["changeUnitPrice"] = roundAmount(gconv.Float64(value))
|
|
}
|
|
if values := jsonInts(batch.BatchInfo, "renewableDurationOptions", "changePriceSnapshot.renewableDurationOptions"); len(values) > 0 {
|
|
fields["renewableDurationOptions"] = values
|
|
}
|
|
if value := jsonField(batch.BatchInfo, "quantity", "changePrepareSnapshot.quantity"); value != nil {
|
|
fields["quantity"] = gconv.Int(value)
|
|
} else if batch.TotalCount > 0 {
|
|
fields["quantity"] = batch.TotalCount
|
|
}
|
|
return fields
|
|
}
|
|
|
|
func containsStringJSON(jsonValue any, target string) bool {
|
|
target = strings.TrimSpace(target)
|
|
if target == "" || jsonValue == nil {
|
|
return false
|
|
}
|
|
var values []any
|
|
switch value := jsonValue.(type) {
|
|
case interface{ Interfaces() []any }:
|
|
values = value.Interfaces()
|
|
default:
|
|
values = gconv.SliceAny(value)
|
|
}
|
|
for _, item := range values {
|
|
if strings.TrimSpace(gconv.String(item)) == target {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func stringListFromJSON(jsonValue any) []string {
|
|
if jsonValue == nil {
|
|
return nil
|
|
}
|
|
var values []any
|
|
switch value := jsonValue.(type) {
|
|
case interface{ Interfaces() []any }:
|
|
values = value.Interfaces()
|
|
default:
|
|
values = gconv.SliceAny(value)
|
|
}
|
|
if len(values) == 0 {
|
|
return nil
|
|
}
|
|
seen := make(map[string]struct{}, len(values))
|
|
result := make([]string, 0, len(values))
|
|
for _, item := range values {
|
|
value := strings.TrimSpace(gconv.String(item))
|
|
if value == "" {
|
|
continue
|
|
}
|
|
if _, ok := seen[value]; ok {
|
|
continue
|
|
}
|
|
seen[value] = struct{}{}
|
|
result = append(result, value)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func calcChangeBeginTime(effectiveMode string) (*gtime.Time, error) {
|
|
switch strings.TrimSpace(effectiveMode) {
|
|
case cardChangeEffectiveImmediate:
|
|
return gtime.Now(), nil
|
|
case cardChangeEffectiveNextMonth:
|
|
now := time.Now()
|
|
nextMonth := time.Date(now.Year(), now.Month()+1, 1, 0, 0, 0, 0, now.Location())
|
|
return gtime.NewFromTime(nextMonth), nil
|
|
default:
|
|
return nil, gerror.New("effectiveMode is unsupported")
|
|
}
|
|
}
|
|
|
|
func changeSubmitParamFromInput(in *model.TSimCardChangeInputPrepareParam) *model.TSimCardChangeBatchSubmitParam {
|
|
if in == nil {
|
|
return &model.TSimCardChangeBatchSubmitParam{}
|
|
}
|
|
return &model.TSimCardChangeBatchSubmitParam{
|
|
NewPackageCode: strings.TrimSpace(in.NewPackageCode),
|
|
DurationMonths: in.DurationMonths,
|
|
GiftDurationMonths: in.GiftDurationMonths,
|
|
EffectiveMode: strings.TrimSpace(in.EffectiveMode),
|
|
OfflineContractCompleted: in.OfflineContractCompleted,
|
|
}
|
|
}
|
|
|
|
func changeSubmitParamFromRange(in *model.TSimCardChangeRangePrepareParam) *model.TSimCardChangeBatchSubmitParam {
|
|
if in == nil {
|
|
return &model.TSimCardChangeBatchSubmitParam{}
|
|
}
|
|
return &model.TSimCardChangeBatchSubmitParam{
|
|
NewPackageCode: strings.TrimSpace(in.NewPackageCode),
|
|
DurationMonths: in.DurationMonths,
|
|
GiftDurationMonths: in.GiftDurationMonths,
|
|
EffectiveMode: strings.TrimSpace(in.EffectiveMode),
|
|
OfflineContractCompleted: in.OfflineContractCompleted,
|
|
}
|
|
}
|
|
|
|
func prepareParamOrSubmitParam(prepareParam *model.TSimCardChangeBatchPrepareParam, submitParam *model.TSimCardChangeBatchSubmitParam) any {
|
|
if prepareParam != nil {
|
|
return prepareParam
|
|
}
|
|
return submitParam
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) getChangeUploadFile(ctx context.Context) (*ghttp.UploadFile, error) {
|
|
r := g.RequestFromCtx(ctx)
|
|
if r == nil {
|
|
return nil, gerror.New("current context is not an HTTP request")
|
|
}
|
|
file := r.GetUploadFile("file")
|
|
if file == nil {
|
|
return nil, gerror.New("please select upload file")
|
|
}
|
|
return file, nil
|
|
}
|
|
|
|
func changeExcelSubmitResult(result *batchimport.Result) *model.TSimCardChangeExcelSubmitRes {
|
|
res := &model.TSimCardChangeExcelSubmitRes{
|
|
BatchType: consts.BatchTypePrepare,
|
|
TargetBatchType: consts.BatchTypeChange,
|
|
Status: pendingStatus(result.ImportStatus),
|
|
BatchAsyncStatus: consts.BatchAsyncStatusPending,
|
|
RunResult: consts.BatchRunResultPending,
|
|
Total: result.TotalRows,
|
|
}
|
|
if result == nil {
|
|
return res
|
|
}
|
|
res.BatchNo = result.BatchNo
|
|
res.ResultDownloadId = result.ResultDownloadId
|
|
res.ResultFileOssId = result.ResultFileOssId
|
|
return res
|
|
}
|
|
|
|
func (s *sIotTSimCardChange) mergeChangeExcelBatchFileInfo(ctx context.Context, batchNo string, importFileOssId, resultFileOssId, resultDownloadId int64) error {
|
|
operator := currentOperator(ctx)
|
|
columns := dao.BatchMain.Columns()
|
|
batch := &entity.BatchMain{}
|
|
if err := dao.BatchMain.Ctx(ctx).
|
|
Where(columns.BatchNo, strings.TrimSpace(batchNo)).
|
|
Where(columns.TenantId, operator.TenantId).
|
|
Where(columns.CreatedDept, operator.DeptId).
|
|
Scan(batch); err != nil {
|
|
return gerror.Wrap(err, "load change batch failed")
|
|
}
|
|
if batch.Id == 0 {
|
|
return gerror.New("change batch not found")
|
|
}
|
|
|
|
info := changeExcelMergeBatchInfo(batch.BatchInfo, importFileOssId, resultFileOssId, resultDownloadId)
|
|
if _, err := dao.BatchMain.Ctx(ctx).
|
|
Where(columns.BatchNo, strings.TrimSpace(batchNo)).
|
|
Data(do.BatchMain{
|
|
SourceOssId: batchFileOssIDValue(importFileOssId),
|
|
ResultOssId: batchFileOssIDValue(resultFileOssId),
|
|
BatchInfo: info,
|
|
UpdatedBy: operator.UserId,
|
|
UpdatedAt: gtime.Now(),
|
|
}).
|
|
Update(); err != nil {
|
|
return gerror.Wrap(err, "update change batch file info failed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func changeExcelMergeBatchInfo(info *gjson.Json, importFileOssId, resultFileOssId, resultDownloadId int64) *gjson.Json {
|
|
data := g.Map{}
|
|
if info != nil {
|
|
for key, value := range info.Map() {
|
|
data[key] = value
|
|
}
|
|
}
|
|
if importFileOssId > 0 {
|
|
data["importFileOssId"] = importFileOssId
|
|
}
|
|
if resultFileOssId > 0 {
|
|
data["resultFileOssId"] = resultFileOssId
|
|
}
|
|
if resultDownloadId > 0 {
|
|
data["resultDownloadId"] = resultDownloadId
|
|
}
|
|
return gjson.New(data)
|
|
}
|
|
|
|
func batchFileOssIDValue(ossID int64) any {
|
|
if ossID <= 0 {
|
|
return nil
|
|
}
|
|
return ossID
|
|
}
|
|
|
|
func trimChangeExcelFailReason(cause error) string {
|
|
if cause == nil {
|
|
return "change confirm failed"
|
|
}
|
|
reason := strings.TrimSpace(cause.Error())
|
|
if reason == "" {
|
|
return "change confirm failed"
|
|
}
|
|
const maxLen = 500
|
|
runes := []rune(reason)
|
|
if len(runes) <= maxLen {
|
|
return reason
|
|
}
|
|
return string(runes[:maxLen])
|
|
}
|
|
|
|
func formatChangeTime(t *gtime.Time) string {
|
|
if t == nil {
|
|
return ""
|
|
}
|
|
return t.Format("Y-m-d H:i:s")
|
|
}
|