package app import ( "context" "fmt" "strings" "git.5iot.com/iotlcm-public/iotlcm-app/internal/consts" "git.5iot.com/iotlcm-public/iotlcm-app/internal/dao" "git.5iot.com/iotlcm-public/iotlcm-app/internal/model/do" "git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/os/gtime" ) type appRenewExecutionContext struct { CardPeriods []map[string]any LatestBindsByIccid map[string]*entity.TSimCardBindPkg NewBindsByIccid map[string]*entity.TSimCardBindPkg BeginAtByIccid map[string]*gtime.Time EndAtByIccid map[string]*gtime.Time MinBeginAt *gtime.Time MaxEndAt *gtime.Time } func (s *sAppCard) prepareAppRenewExecution(ctx context.Context, tx gdb.TX, batch *appRenewBatchContext, durationMonths int) (*appRenewExecutionContext, error) { if batch == nil || len(batch.Items) == 0 { return nil, gerror.New("续费卡不能为空") } if durationMonths <= 0 { return nil, gerror.New("续费时长必须大于0") } exec := &appRenewExecutionContext{ CardPeriods: make([]map[string]any, 0, len(batch.Items)), LatestBindsByIccid: make(map[string]*entity.TSimCardBindPkg, len(batch.Items)), NewBindsByIccid: make(map[string]*entity.TSimCardBindPkg, len(batch.Items)), BeginAtByIccid: make(map[string]*gtime.Time, len(batch.Items)), EndAtByIccid: make(map[string]*gtime.Time, len(batch.Items)), } for index, item := range batch.Items { if item == nil || item.Card == nil { return nil, gerror.New("续费卡信息不完整") } lockedCard, err := s.lockRenewCardForSubmit(ctx, tx, item.Card, item.ExpectedCurrentPackageCode) if err != nil { return nil, err } latestBind, err := s.lockLatestRenewBindForSubmit(ctx, tx, lockedCard, item) if err != nil { return nil, err } baseExpireAt := appRenewBaseExpireTimeFromEntity(lockedCard, latestBind) if baseExpireAt == nil || baseExpireAt.IsZero() { return nil, gerror.Newf("ICCID最新订购记录没有预计到期时间,不能续费:%s", lockedCard.Iccid) } if appRenewExpireAfterAllowedWindow(baseExpireAt) { return nil, gerror.New("距离到期超过6个月,不能续费") } beginAt := appRenewBeginDateFromExpire(baseExpireAt) endAt := beginAt.AddDate(0, durationMonths, 0) exec.MinBeginAt = appEarlierTime(exec.MinBeginAt, beginAt) exec.MaxEndAt = appLaterTime(exec.MaxEndAt, endAt) exec.LatestBindsByIccid[item.Card.Iccid] = latestBind exec.BeginAtByIccid[item.Card.Iccid] = beginAt exec.EndAtByIccid[item.Card.Iccid] = endAt item.LatestBind = latestBind exec.CardPeriods = append(exec.CardPeriods, map[string]any{ "rowNo": appRenewBatchSourceRowNo(index), "cardId": item.Card.Id, "iccid": item.Card.Iccid, "msisdn": item.Card.Msisdn, "oldSubscriptionNo": latestBind.SubscriptionNo, "serviceBeginAt": appRenewFormatTime(beginAt), "serviceEndAt": appRenewFormatTime(endAt), "oldExpectedExpireTime": appRenewFormatTime(latestBind.ExpectedExpireTime), }) } return exec, nil } func (s *sAppCard) lockLatestRenewBindForSubmit(ctx context.Context, tx gdb.TX, lockedCard *entity.TSimCardInfo, item *appRenewPackageContext) (*entity.TSimCardBindPkg, error) { if lockedCard == nil || item == nil || item.Card == nil { return nil, gerror.New("续费卡信息不完整") } expectedPackageCode := strings.TrimSpace(item.ExpectedCurrentPackageCode) if expectedPackageCode == "" { return nil, gerror.New("ICCID没有当前套餐,不能续费") } var bind *entity.TSimCardBindPkg err := dao.TSimCardBindPkg.Ctx(ctx).TX(tx). Where(dao.TSimCardBindPkg.Columns().Iccid, lockedCard.Iccid). OrderDesc(dao.TSimCardBindPkg.Columns().ExpectedExpireTime). OrderDesc(dao.TSimCardBindPkg.Columns().Id). LockUpdate(). Limit(1). Scan(&bind) if err != nil { return nil, gerror.Wrap(err, "锁定最新订购记录失败") } if bind == nil || bind.Id == 0 { return nil, gerror.Newf("ICCID没有订购记录,不能续费:%s", lockedCard.Iccid) } if strings.TrimSpace(bind.PackageCode) != expectedPackageCode { return nil, gerror.Newf("ICCID当前套餐与最新订购记录不一致,不能续费:%s", lockedCard.Iccid) } return bind, nil } func (s *sAppCard) insertAppRenewSubscriptionBinds(ctx context.Context, tx gdb.TX, user *entity.SysUser, batch *appRenewBatchContext, exec *appRenewExecutionContext, batchNo string, orderSn string, durationMonths int, now *gtime.Time) error { if user == nil || batch == nil || batch.Package == nil || batch.Party == nil || exec == nil { return gerror.New("续费订购记录信息不完整") } batchNo = strings.TrimSpace(batchNo) orderSn = strings.TrimSpace(orderSn) if batchNo == "" || orderSn == "" { return gerror.New("续费批次号或订单号不能为空") } periodByIccid := make(map[string]map[string]any, len(exec.CardPeriods)) for _, period := range exec.CardPeriods { iccid := strings.TrimSpace(fmt.Sprint(period["iccid"])) if iccid != "" { periodByIccid[iccid] = period } } rows := make([]do.TSimCardBindPkg, 0, len(batch.Items)) for _, item := range batch.Items { card := item.Card if card == nil { return gerror.New("续费卡信息不完整") } latestBind := exec.LatestBindsByIccid[card.Iccid] if latestBind == nil { return gerror.Newf("ICCID最新订购记录不完整:%s", card.Iccid) } periodBegin := exec.BeginAtByIccid[card.Iccid] periodEnd := exec.EndAtByIccid[card.Iccid] if periodBegin == nil || periodBegin.IsZero() || periodEnd == nil || periodEnd.IsZero() { return gerror.Newf("ICCID续费服务周期不完整:%s", card.Iccid) } subscriptionNo := appOrderNo("SUB", user.UserId, now) row := do.TSimCardBindPkg{ SubscriptionNo: subscriptionNo, BatchNo: batchNo, Iccid: card.Iccid, Msisdn: strings.TrimSpace(card.Msisdn), DeptId: fmt.Sprintf("%d", batch.Party.CustomerAgentId), PackageType: batch.Package.PackageType, PackageCode: batch.Package.PackageCode, PreviousPackageCode: item.ExpectedCurrentPackageCode, BusinessType: consts.PackageBusinessTypeRenew, EffectiveStatus: consts.EffectiveStatusPending, HandleTime: now, EffectiveTime: periodBegin, ExpectedExpireTime: periodEnd, DurationMonths: durationMonths, GiftDurationMonths: 0, OrderCode: orderSn, Remark: batchNo, TenantId: user.TenantId, CreatedDept: batch.Party.CreatedDept, CreatedBy: user.UserId, CreatedAt: now, UpdatedBy: user.UserId, UpdatedAt: now, } rows = append(rows, row) newBind := &entity.TSimCardBindPkg{ SubscriptionNo: subscriptionNo, BatchNo: batchNo, Iccid: card.Iccid, Msisdn: strings.TrimSpace(card.Msisdn), DeptId: fmt.Sprintf("%d", batch.Party.CustomerAgentId), PackageType: batch.Package.PackageType, PackageCode: batch.Package.PackageCode, PreviousPackageCode: item.ExpectedCurrentPackageCode, BusinessType: consts.PackageBusinessTypeRenew, EffectiveStatus: consts.EffectiveStatusPending, HandleTime: now, EffectiveTime: periodBegin, ExpectedExpireTime: periodEnd, DurationMonths: durationMonths, GiftDurationMonths: 0, OrderCode: orderSn, Remark: batchNo, TenantId: user.TenantId, CreatedDept: batch.Party.CreatedDept, CreatedBy: user.UserId, CreatedAt: now, UpdatedBy: user.UserId, UpdatedAt: now, } exec.NewBindsByIccid[card.Iccid] = newBind if period := periodByIccid[card.Iccid]; period != nil { period["newSubscriptionNo"] = subscriptionNo period["renewEffectiveTime"] = appRenewFormatTime(periodBegin) period["renewExpectedExpireTime"] = appRenewFormatTime(periodEnd) period["cardFlowPkgEndAt"] = appRenewFormatTime(periodEnd) } } if len(rows) > 0 { if _, err := dao.TSimCardBindPkg.Ctx(ctx).TX(tx).Data(rows).Insert(); err != nil { return gerror.Wrap(err, "写入续费订购记录失败") } } return s.updateCardsAfterAppRenew(ctx, tx, user, batch, exec, now) } func (s *sAppCard) updateCardsAfterAppRenew(ctx context.Context, tx gdb.TX, user *entity.SysUser, batch *appRenewBatchContext, exec *appRenewExecutionContext, now *gtime.Time) error { for _, item := range batch.Items { if item == nil || item.Card == nil { return gerror.New("续费卡信息不完整") } newBind := exec.NewBindsByIccid[item.Card.Iccid] if newBind == nil || newBind.ExpectedExpireTime == nil { return gerror.Newf("ICCID续费订购记录不完整:%s", item.Card.Iccid) } packageStatus := appCalcPackageStatus(now, item.Card.FlowPkgBeginAt, newBind.ExpectedExpireTime) result, err := dao.TSimCardInfo.Ctx(ctx).TX(tx). Where(dao.TSimCardInfo.Columns().TenantId, item.Card.TenantId). Where(dao.TSimCardInfo.Columns().Iccid, item.Card.Iccid). Data(do.TSimCardInfo{ CurrentPackageCode: batch.Package.PackageCode, FlowPkgEndAt: newBind.ExpectedExpireTime, PackageStatus: packageStatus, UpdatedBy: user.UserId, UpdatedAt: now, }). Update() if err != nil { return gerror.Wrap(err, "更新续费后卡套餐到期时间失败") } affected, err := result.RowsAffected() if err != nil { return gerror.Wrap(err, "读取续费后卡信息更新数量失败") } if affected != 1 { return gerror.Newf("ICCID卡信息已变化,请刷新后重试:%s", item.Card.Iccid) } item.Card.FlowPkgEndAt = newBind.ExpectedExpireTime item.Card.PackageStatus = packageStatus item.Card.CurrentPackageCode = batch.Package.PackageCode if err = s.updateTerminalBindAfterAppRenew(ctx, tx, user, batch, item, now); err != nil { return err } } return nil } func (s *sAppCard) updateTerminalBindAfterAppRenew(ctx context.Context, tx gdb.TX, user *entity.SysUser, batch *appRenewBatchContext, item *appRenewPackageContext, now *gtime.Time) error { if item == nil || item.Card == nil || batch == nil || batch.Party == nil || !s.isTerminalUser(ctx, user) { return nil } card := item.Card if card.TerminalBindId <= 0 && card.TerminalUserId <= 0 { return nil } mod := dao.TSimTerminal.Ctx(ctx).TX(tx). Where(dao.TSimTerminal.Columns().TenantId, card.TenantId). Where(dao.TSimTerminal.Columns().Iccid, card.Iccid) if card.TerminalBindId > 0 { mod = mod.Where(dao.TSimTerminal.Columns().Id, card.TerminalBindId) } else { mod = mod.Where(dao.TSimTerminal.Columns().TerminalId, user.UserId) } result, err := mod.Data(do.TSimTerminal{ DeptId: batch.Party.CustomerAgentId, CurrentPackageCode: strings.TrimSpace(batch.Package.PackageCode), UpdatedBy: user.UserId, UpdatedAt: now, }).Update() if err != nil { return gerror.Wrap(err, "更新续费后的终端卡关联信息失败") } affected, err := result.RowsAffected() if err != nil { return gerror.Wrap(err, "读取续费后的终端卡关联更新数量失败") } if affected != 1 { return gerror.Newf("ICCID终端卡关联已变化,请刷新后重试:%s", card.Iccid) } card.TerminalDeptId = batch.Party.CustomerAgentId card.TerminalCurrentPackageCode = strings.TrimSpace(batch.Package.PackageCode) return nil } func appCalcPackageStatus(now, beginAt, endAt *gtime.Time) int { if now == nil { now = gtime.Now() } if beginAt != nil && beginAt.After(now) { return consts.PackageStatusPending } if endAt != nil && !endAt.After(now) { return consts.PackageStatusExpired } return consts.PackageStatusEffective }