559 lines
18 KiB
Go
559 lines
18 KiB
Go
package renew
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"strconv"
|
|
"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/model"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity"
|
|
|
|
"github.com/gogf/gf/v2/database/gdb"
|
|
"github.com/gogf/gf/v2/encoding/gjson"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
)
|
|
|
|
type Processor struct {
|
|
repo *Repository
|
|
config batchimport.ProcessorConfig
|
|
}
|
|
|
|
func NewProcessor() *Processor {
|
|
return &Processor{
|
|
repo: NewRepository(),
|
|
config: batchimport.ProcessorConfig{
|
|
Code: "sim_card_renew",
|
|
BatchType: consts.BatchTypeRenew,
|
|
BatchNoPrefix: consts.BatchNoPrefixRenew,
|
|
TaskName: consts.ResourceBatchDownloadTaskSimCardRenew,
|
|
ExportScene: consts.ResourceExportSceneRenew,
|
|
ImportDir: consts.SimCardRenewSubDirImportExcel,
|
|
ResultDir: consts.SimCardRenewSubDirResultExcel,
|
|
MaxFileSize: consts.SimCardRenewMaxFileSize,
|
|
TemplateHeaders: ExcelTemplateHeaders(),
|
|
RequiredHeaders: []string{"ICCID"},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (p *Processor) Config() batchimport.ProcessorConfig {
|
|
return p.config
|
|
}
|
|
|
|
func (p *Processor) ParseRows(ctx context.Context, filePath string) ([]batchimport.Row, error) {
|
|
return nil, gerror.New("卡续费不支持文件导入")
|
|
}
|
|
|
|
func (p *Processor) ValidateRows(ctx context.Context, rows []batchimport.Row) error {
|
|
renewRows := make([]*Row, 0, len(rows))
|
|
for _, item := range rows {
|
|
row, ok := item.(*Row)
|
|
if !ok {
|
|
item.AddErrorReason("续费行类型错误")
|
|
continue
|
|
}
|
|
renewRows = append(renewRows, row)
|
|
}
|
|
if err := normalizeRenewIdentityRows(ctx, renewRows); err != nil {
|
|
return err
|
|
}
|
|
return validateRows(renewRows)
|
|
}
|
|
|
|
func (p *Processor) Execute(ctx context.Context, tx gdb.TX, req *batchimport.SubmitRequest, rows []batchimport.Row) error {
|
|
if req == nil {
|
|
return gerror.New("续费请求不能为空")
|
|
}
|
|
in, ok := req.BizParams.(*model.TSimCardRenewBatchSubmitParam)
|
|
if !ok || in == nil {
|
|
return gerror.New("续费参数类型错误")
|
|
}
|
|
|
|
renewRows := make([]*Row, 0, len(rows))
|
|
for _, item := range rows {
|
|
row, ok := item.(*Row)
|
|
if !ok {
|
|
return gerror.New("续费行类型错误")
|
|
}
|
|
renewRows = append(renewRows, row)
|
|
}
|
|
options, err := loadExecutionOptions(ctx, tx, req.BatchNo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
applyExecutionOptions(in, options)
|
|
|
|
prepared, err := p.validateRenewContext(ctx, tx, in, req.Operator, renewRows, req.BatchNo, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
priceSnapshot, err := ensurePriceSnapshot(ctx, tx, req.Operator, prepared, options.PriceSnapshot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
durationValue := renewDisplayDurationValue(in)
|
|
if durationValue <= 0 {
|
|
return gerror.New("续费时长必须大于0")
|
|
}
|
|
if !containsRenewDurationOption(priceSnapshot.RenewableDurationOptions, durationValue) {
|
|
return gerror.New("续费时长不在当前套餐可续费范围内")
|
|
}
|
|
|
|
output, err := p.Complete(ctx, tx, &CompleteInput{
|
|
SubmitParam: in,
|
|
BatchNo: req.BatchNo,
|
|
Rows: renewRows,
|
|
Operator: req.Operator,
|
|
PriceSnapshot: priceSnapshot,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.ExecutionResult = output
|
|
return nil
|
|
}
|
|
|
|
func (p *Processor) BuildBatchInfo(ctx context.Context, req batchimport.SubmitRequest, result *batchimport.Result, importOss *model.SysOssSaveContentModel, resultOss *model.SysOssSaveContentModel, downloadId int64) *gjson.Json {
|
|
info := g.Map{}
|
|
if result != nil && result.ImportFileOssId > 0 {
|
|
info["importFileOssId"] = result.ImportFileOssId
|
|
} else if importOss != nil && importOss.OssId > 0 {
|
|
info["importFileOssId"] = importOss.OssId
|
|
}
|
|
if resultOss != nil {
|
|
info["resultFileOssId"] = resultOss.OssId
|
|
}
|
|
if downloadId > 0 {
|
|
info["resultDownloadId"] = downloadId
|
|
}
|
|
if in, ok := req.BizParams.(*model.TSimCardRenewBatchSubmitParam); ok && in != nil {
|
|
durationValue := renewDisplayDurationValue(in)
|
|
if durationValue > 0 {
|
|
actualDurationMonths := renewActualDurationMonths(nil, durationValue)
|
|
info["durationYears"] = durationValue
|
|
info["durationMonths"] = actualDurationMonths
|
|
info["durationValue"] = durationValue
|
|
info["actualDurationMonths"] = actualDurationMonths
|
|
}
|
|
info["giftDurationMonths"] = in.GiftDurationMonths
|
|
info["amount"] = strings.TrimSpace(in.Amount)
|
|
if in.PaymentSnapshot != nil {
|
|
appendRenewPaymentSnapshot(info, in.PaymentSnapshot)
|
|
} else if paymentMode := strings.TrimSpace(in.PaymentMode); paymentMode != "" {
|
|
info["paymentMode"] = paymentMode
|
|
}
|
|
if packageCode := strings.TrimSpace(in.PackageCode); packageCode != "" {
|
|
info["packageCode"] = packageCode
|
|
}
|
|
info["offlineContractCompleted"] = in.OfflineContractCompleted
|
|
if sourceBatchNo := strings.TrimSpace(in.SourceBatchNo); sourceBatchNo != "" {
|
|
info["sourceBatchNo"] = sourceBatchNo
|
|
}
|
|
if buyerNote := strings.TrimSpace(in.BuyerNote); buyerNote != "" {
|
|
info["buyerNote"] = buyerNote
|
|
}
|
|
if adminNote := strings.TrimSpace(in.AdminNote); adminNote != "" {
|
|
info["adminNote"] = adminNote
|
|
}
|
|
if remark := strings.TrimSpace(in.Remark); remark != "" {
|
|
info["remark"] = remark
|
|
}
|
|
}
|
|
if snapshot, ok := req.ExecutionResult.(*model.TSimCardRenewPriceSnapshot); ok && snapshot != nil {
|
|
if result != nil && result.BatchType == consts.BatchTypePrepare {
|
|
info["targetBatchType"] = consts.BatchTypeRenew
|
|
info["targetBatchName"] = "卡续费"
|
|
}
|
|
info["customerAgentId"] = snapshot.CustomerAgentId
|
|
info["packageCode"] = snapshot.PackageCode
|
|
info["packageName"] = snapshot.PackageName
|
|
info["billingMethod"] = snapshot.BillingMethod
|
|
info["renewDurationUnit"] = snapshot.RenewDurationUnit
|
|
info["renewDurationUnitText"] = snapshot.RenewDurationUnitText
|
|
if durationValue := gconv.Int(info["durationValue"]); durationValue > 0 {
|
|
actualDurationMonths := renewActualDurationMonths(snapshot, durationValue)
|
|
info["durationMonths"] = actualDurationMonths
|
|
info["actualDurationMonths"] = actualDurationMonths
|
|
}
|
|
info["quantity"] = result.ValidRows
|
|
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
|
|
}
|
|
if output, ok := req.ExecutionResult.(*CompleteOutput); ok && output != nil {
|
|
info["quantity"] = output.Quantity
|
|
info["packageCode"] = output.PackageCode
|
|
info["orderSn"] = output.OrderSn
|
|
info["renewTime"] = output.RenewTime
|
|
if sourceBatchNo := strings.TrimSpace(output.SourceBatchNo); sourceBatchNo != "" {
|
|
info["sourceBatchNo"] = sourceBatchNo
|
|
}
|
|
}
|
|
return gjson.New(info)
|
|
}
|
|
|
|
func appendRenewPaymentSnapshot(info g.Map, snapshot *model.TSimCardRenewPaymentSnapshot) {
|
|
if info == nil || snapshot == nil {
|
|
return
|
|
}
|
|
info["paymentMode"] = strings.TrimSpace(snapshot.PaymentMode)
|
|
info["payType"] = snapshot.PayType
|
|
info["payStatus"] = snapshot.PayStatus
|
|
info["payAmount"] = snapshot.PayAmount
|
|
info["balanceAmount"] = snapshot.BalanceAmount
|
|
info["offlinePaidAmount"] = snapshot.OfflinePaidAmount
|
|
info["balanceBefore"] = snapshot.BalanceBefore
|
|
info["balanceAfter"] = snapshot.BalanceAfter
|
|
info["balanceDetailSerialNo"] = strings.TrimSpace(snapshot.BalanceDetailSerialNo)
|
|
info["payerDeptId"] = snapshot.PayerDeptId
|
|
info["payerUserId"] = snapshot.PayerUserId
|
|
info["isTopDept"] = snapshot.IsTopDept
|
|
if snapshot.PaymentTime != nil {
|
|
info["paymentTime"] = snapshot.PaymentTime.Format("Y-m-d H:i:s")
|
|
}
|
|
info["paymentSnapshot"] = snapshot
|
|
}
|
|
|
|
func (p *Processor) ValidatePrepareContext(ctx context.Context, tx gdb.TX, in *model.TSimCardRenewBatchSubmitParam, operator batchimport.OperatorContext, rows []*Row) (*PrepareContext, error) {
|
|
return p.validateRenewContext(ctx, tx, in, operator, rows, "", false)
|
|
}
|
|
|
|
func (p *Processor) RevalidatePrepareContext(ctx context.Context, tx gdb.TX, in *model.TSimCardRenewBatchSubmitParam, operator batchimport.OperatorContext, rows []*Row, currentBatchNo string) (*PrepareContext, bool, error) {
|
|
prepared, err := p.validateRenewContext(ctx, tx, in, operator, rows, currentBatchNo, true)
|
|
return prepared, hasInvalidRows(rows), err
|
|
}
|
|
|
|
func (p *Processor) validateRenewContext(ctx context.Context, tx gdb.TX, in *model.TSimCardRenewBatchSubmitParam, operator batchimport.OperatorContext, rows []*Row, currentBatchNo string, allowPartial bool) (*PrepareContext, error) {
|
|
iccids := rowsToIccids(rows)
|
|
cardsByIccid, err := p.repo.LockCardsByIccids(ctx, tx, iccids)
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "校验续费卡信息失败")
|
|
}
|
|
bindsByIccid, err := p.repo.LockLatestBindsByIccids(ctx, tx, iccids)
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "校验续费订购记录失败")
|
|
}
|
|
conflictingBatches, err := p.repo.UnfinishedRenewBatchNosByIccids(
|
|
ctx,
|
|
tx,
|
|
operator.TenantId,
|
|
iccids,
|
|
currentBatchNo,
|
|
)
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "校验未完成续费批次失败")
|
|
}
|
|
|
|
sourceBatchNo := strings.TrimSpace(in.SourceBatchNo)
|
|
packageCodes := make(map[string]struct{})
|
|
customerAgentIds := make(map[int64]struct{})
|
|
currentPackagePeriods := make(map[string]struct{})
|
|
renewBeginDates := make([]string, 0, len(rows))
|
|
packageCode := ""
|
|
customerAgentId := int64(0)
|
|
for _, row := range rows {
|
|
if row == nil || row.Reason != "" {
|
|
continue
|
|
}
|
|
card := cardsByIccid[row.Iccid]
|
|
bind := bindsByIccid[row.Iccid]
|
|
if strings.TrimSpace(conflictingBatches[row.Iccid]) != "" {
|
|
row.AddReason("存在未完成续费批次,不能重复续费")
|
|
continue
|
|
}
|
|
reason := validateRenewRow(sourceBatchNo, operator, card, bind)
|
|
if reason != "" {
|
|
row.AddReason(reason)
|
|
continue
|
|
}
|
|
baseExpireAt := renewBaseExpireTime(card, bind)
|
|
|
|
periodKey := currentPackagePeriodKeyWithExpire(bind, baseExpireAt)
|
|
if periodKey == "" {
|
|
row.AddReason("不是统一批次")
|
|
continue
|
|
}
|
|
currentPackageCode := strings.TrimSpace(card.CurrentPackageCode)
|
|
packageCodes[currentPackageCode] = struct{}{}
|
|
customerAgentIds[card.DeptId] = struct{}{}
|
|
currentPackagePeriods[periodKey] = struct{}{}
|
|
packageCode = currentPackageCode
|
|
customerAgentId = card.DeptId
|
|
renewBeginAt := renewBeginDateFromExpire(baseExpireAt)
|
|
if renewBeginAt != nil {
|
|
renewBeginDates = append(renewBeginDates, renewBeginAt.Format("Y-m-d"))
|
|
}
|
|
}
|
|
|
|
if len(packageCodes) > 1 {
|
|
markRowsWithoutReason(rows, "当前批次下存在多个套餐,不能统一续费")
|
|
}
|
|
if len(customerAgentIds) > 1 {
|
|
markRowsWithoutReason(rows, "当前批次下存在多个客户,不能统一续费")
|
|
}
|
|
if len(currentPackagePeriods) > 1 {
|
|
markRowsWithoutReason(rows, "不是统一批次")
|
|
}
|
|
if len(customerAgentIds) == 1 && customerAgentId > 0 {
|
|
if err := validateRenewCustomerDeptScope(ctx, tx, operator, customerAgentId); err != nil {
|
|
markRowsWithoutReason(rows, err.Error())
|
|
}
|
|
}
|
|
|
|
if hasInvalidRows(rows) && !allowPartial {
|
|
markRemainingRowsSkipped(rows)
|
|
return nil, gerror.New("存在不满足续费条件的卡,本次未执行")
|
|
}
|
|
validCount := countValidRows(rows)
|
|
if validCount == 0 {
|
|
return &PrepareContext{}, nil
|
|
}
|
|
renewBeginDate, renewBeginDateMin, renewBeginDateMax, sameRenewBeginDate := summarizeRenewBeginDates(renewBeginDates)
|
|
return &PrepareContext{
|
|
CustomerAgentId: customerAgentId,
|
|
PackageCode: packageCode,
|
|
Quantity: validCount,
|
|
RenewBeginDate: renewBeginDate,
|
|
RenewBeginDateMin: renewBeginDateMin,
|
|
RenewBeginDateMax: renewBeginDateMax,
|
|
SameRenewBeginDate: sameRenewBeginDate,
|
|
}, nil
|
|
}
|
|
|
|
func currentPackagePeriodKey(bind *entity.TSimCardBindPkg) string {
|
|
if bind == nil {
|
|
return ""
|
|
}
|
|
return currentPackagePeriodKeyWithExpire(bind, bind.ExpectedExpireTime)
|
|
}
|
|
|
|
func currentPackagePeriodKeyWithExpire(bind *entity.TSimCardBindPkg, expireAt *gtime.Time) string {
|
|
if bind == nil || bind.EffectiveTime == nil {
|
|
return ""
|
|
}
|
|
if expireAt == nil {
|
|
expireAt = bind.ExpectedExpireTime
|
|
}
|
|
if expireAt == nil {
|
|
return ""
|
|
}
|
|
return bind.EffectiveTime.Format("Y-m-d") + "|" + expireAt.Format("Y-m-d")
|
|
}
|
|
|
|
func summarizeRenewBeginDates(dates []string) (sameDate string, minDate string, maxDate string, same bool) {
|
|
cleaned := make([]string, 0, len(dates))
|
|
for _, date := range dates {
|
|
date = strings.TrimSpace(date)
|
|
if date != "" {
|
|
cleaned = append(cleaned, date)
|
|
}
|
|
}
|
|
if len(cleaned) == 0 {
|
|
return "", "", "", false
|
|
}
|
|
sort.Strings(cleaned)
|
|
minDate = cleaned[0]
|
|
maxDate = cleaned[len(cleaned)-1]
|
|
same = minDate == maxDate
|
|
if same {
|
|
sameDate = minDate
|
|
}
|
|
return sameDate, minDate, maxDate, same
|
|
}
|
|
|
|
func renewBeginDateFromExpire(expireAt *gtime.Time) *gtime.Time {
|
|
if expireAt == nil {
|
|
return nil
|
|
}
|
|
return normalizeRenewDateAtDayEnd(expireAt)
|
|
}
|
|
|
|
func normalizeRenewDateAtDayEnd(expireAt *gtime.Time) *gtime.Time {
|
|
if expireAt == nil || expireAt.IsZero() {
|
|
return nil
|
|
}
|
|
t := expireAt.Time
|
|
return gtime.NewFromTime(
|
|
time.Date(
|
|
t.Year(),
|
|
t.Month(),
|
|
t.Day(),
|
|
23,
|
|
59,
|
|
59,
|
|
0,
|
|
t.Location(),
|
|
),
|
|
)
|
|
}
|
|
|
|
func validateRenewRow(sourceBatchNo string, operator batchimport.OperatorContext, card *entity.TSimCardInfo, bind *entity.TSimCardBindPkg) string {
|
|
if card == nil {
|
|
return "ICCID不存在正式卡记录,不能续费"
|
|
}
|
|
if card.BossCardStatus == consts.V3ApiSimBossCardStatusStopped {
|
|
return "ICCID当前为停机状态,不能续费"
|
|
}
|
|
if bind == nil {
|
|
return "ICCID没有订购记录,不能续费"
|
|
}
|
|
if strings.TrimSpace(card.CurrentPackageCode) == "" || card.PackageStatus == consts.PackageStatusNone {
|
|
return "ICCID没有当前套餐,不能续费"
|
|
}
|
|
if strings.TrimSpace(card.CurrentPackageCode) != strings.TrimSpace(bind.PackageCode) {
|
|
return "ICCID当前套餐与最新订购记录不一致,不能续费"
|
|
}
|
|
baseExpireAt := renewBaseExpireTime(card, bind)
|
|
if baseExpireAt == nil {
|
|
return "ICCID最新订购没有预计到期时间,不能续费"
|
|
}
|
|
if renewExpireAfterAllowedWindow(baseExpireAt) {
|
|
return "距离到期超过6个月,不能续费"
|
|
}
|
|
if sourceBatchNo != "" && strings.TrimSpace(card.OutboundBatchNo) != sourceBatchNo {
|
|
return "ICCID不属于来源出库批次,不能续费"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func renewBaseExpireTime(card *entity.TSimCardInfo, bind *entity.TSimCardBindPkg) *gtime.Time {
|
|
var expireAt *gtime.Time
|
|
if bind != nil {
|
|
expireAt = laterRenewExpireTime(expireAt, bind.ExpectedExpireTime)
|
|
}
|
|
if card != nil {
|
|
expireAt = laterRenewExpireTime(expireAt, card.FlowPkgEndAt)
|
|
expireAt = laterRenewExpireTime(expireAt, card.FlowPkgPresentAt)
|
|
expireAt = laterRenewExpireTime(expireAt, card.FlowPkgRealPresentAt)
|
|
}
|
|
return expireAt
|
|
}
|
|
|
|
func laterRenewExpireTime(current, candidate *gtime.Time) *gtime.Time {
|
|
if candidate == nil || candidate.IsZero() {
|
|
return current
|
|
}
|
|
if current == nil || current.IsZero() || candidate.After(current) {
|
|
return candidate
|
|
}
|
|
return current
|
|
}
|
|
|
|
func renewExpireAfterAllowedWindow(expireAt *gtime.Time) bool {
|
|
if expireAt == nil {
|
|
return false
|
|
}
|
|
return expireAt.After(gtime.Now().AddDate(0, 6, 0))
|
|
}
|
|
|
|
func validateRenewCustomerDeptScope(ctx context.Context, tx gdb.TX, operator batchimport.OperatorContext, customerDeptID int64) error {
|
|
if operator.DeptId <= 0 || operator.TenantId == "" {
|
|
return gerror.New("当前用户未绑定组织或租户")
|
|
}
|
|
if customerDeptID <= 0 {
|
|
return gerror.New("ICCID不属于当前机构,不能续费")
|
|
}
|
|
if customerDeptID == operator.DeptId {
|
|
return nil
|
|
}
|
|
dept := &entity.SysDept{}
|
|
if err := tx.Ctx(ctx).Model(dao.SysDept.Table()).
|
|
Fields(dao.SysDept.Columns().DeptId, dao.SysDept.Columns().ParentId, dao.SysDept.Columns().Ancestors).
|
|
Where(dao.SysDept.Columns().TenantId, operator.TenantId).
|
|
Where(dao.SysDept.Columns().DeptId, customerDeptID).
|
|
WhereNull(dao.SysDept.Columns().DeletedAt).
|
|
Scan(dept); err != nil {
|
|
return gerror.Wrap(err, "校验ICCID归属失败")
|
|
}
|
|
if dept.DeptId == 0 {
|
|
return gerror.New("ICCID不属于当前机构,不能续费")
|
|
}
|
|
if dept.ParentId == operator.DeptId || renewDeptAncestorsContain(dept.Ancestors, operator.DeptId) {
|
|
return nil
|
|
}
|
|
return gerror.New("ICCID不属于当前机构,不能续费")
|
|
}
|
|
|
|
func renewDeptAncestorsContain(ancestors string, deptID int64) bool {
|
|
if deptID <= 0 {
|
|
return false
|
|
}
|
|
target := strconv.FormatInt(deptID, 10)
|
|
for _, item := range strings.Split(strings.Trim(ancestors, ","), ",") {
|
|
if strings.TrimSpace(item) == target {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func hasInvalidRows(rows []*Row) bool {
|
|
for _, row := range rows {
|
|
if row != nil && row.Reason != "" {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func countValidRows(rows []*Row) int {
|
|
count := 0
|
|
for _, row := range rows {
|
|
if row != nil && row.Valid() {
|
|
count++
|
|
}
|
|
}
|
|
return count
|
|
}
|
|
|
|
func markRowsWithoutReason(rows []*Row, reason string) {
|
|
for _, row := range rows {
|
|
if row != nil && row.Reason == "" {
|
|
row.AddReason(reason)
|
|
}
|
|
}
|
|
}
|
|
|
|
func markRemainingRowsSkipped(rows []*Row) {
|
|
for _, row := range rows {
|
|
if row != nil && row.Reason == "" {
|
|
row.AddReason("同批存在不满足续费条件的卡,本行未执行")
|
|
}
|
|
}
|
|
}
|
|
|
|
func validateRows(rows []*Row) error {
|
|
firstByIccid := make(map[string]*Row, len(rows))
|
|
for _, row := range rows {
|
|
row.Iccid = strings.TrimSpace(row.Iccid)
|
|
if row.Iccid == "" {
|
|
row.AddReason("ICCID不能为空")
|
|
continue
|
|
}
|
|
if first, ok := firstByIccid[row.Iccid]; ok {
|
|
row.AddReason("ICCID重复")
|
|
first.AddReason("ICCID重复")
|
|
continue
|
|
}
|
|
firstByIccid[row.Iccid] = row
|
|
}
|
|
return nil
|
|
}
|