Files
iotlcm/app/server/internal/logic/app/app_card_renew_complete.go
T

307 lines
12 KiB
Go

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
}