270 lines
9.0 KiB
Go
270 lines
9.0 KiB
Go
package app
|
||
|
||
import (
|
||
"context"
|
||
"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/entity"
|
||
|
||
"github.com/gogf/gf/v2/errors/gerror"
|
||
"github.com/gogf/gf/v2/os/gtime"
|
||
)
|
||
|
||
type appRenewPackageContext struct {
|
||
Card *appCardQueryRow
|
||
Package *entity.TSimFlowPackageInfo
|
||
PackageCode string
|
||
ExpectedCurrentPackageCode string
|
||
Subscription *entity.TSimCardBindPkg
|
||
LatestBind *entity.TSimCardBindPkg
|
||
}
|
||
|
||
type appRenewTargetContext struct {
|
||
Package *entity.TSimFlowPackageInfo
|
||
Price *appRenewPriceSnapshot
|
||
Durations []int
|
||
}
|
||
|
||
// resolveRenewPackageContext 统一解析“当前可续费套餐”。
|
||
// 当前套餐只用于锁定卡片和读取 renewable_package_codes,续费目标套餐由后续步骤单独解析。
|
||
func (s *sAppCard) resolveRenewPackageContext(ctx context.Context, card *appCardQueryRow, subscriptionNo string) (*appRenewPackageContext, error) {
|
||
if card == nil || strings.TrimSpace(card.Iccid) == "" {
|
||
return nil, gerror.New("卡信息不存在或无权续费")
|
||
}
|
||
|
||
currentPackageCode := appCardCurrentPackageCode(card)
|
||
renewCtx := &appRenewPackageContext{
|
||
Card: card,
|
||
ExpectedCurrentPackageCode: currentPackageCode,
|
||
}
|
||
|
||
subscriptionNo = strings.TrimSpace(subscriptionNo)
|
||
if subscriptionNo != "" {
|
||
bind, err := s.loadRenewSubscription(ctx, card, subscriptionNo)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if currentPackageCode != "" && strings.TrimSpace(bind.PackageCode) != currentPackageCode {
|
||
return nil, gerror.New("订购记录不是当前卡片套餐,请刷新后重试")
|
||
}
|
||
renewCtx.Subscription = bind
|
||
}
|
||
|
||
latestBind, err := s.loadLatestRenewBind(ctx, card)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
renewCtx.LatestBind = latestBind
|
||
if err = appValidateRenewLatestBind(card, currentPackageCode, latestBind); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
pkg, err := s.findRenewPackageOrNil(ctx, card.TenantId, currentPackageCode)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if pkg == nil {
|
||
return nil, gerror.Newf("当前卡片套餐编码 %s 未配置套餐信息", currentPackageCode)
|
||
}
|
||
renewCtx.Package = pkg
|
||
renewCtx.PackageCode = pkg.PackageCode
|
||
return renewCtx, nil
|
||
}
|
||
|
||
func (s *sAppCard) resolveRenewTargetPackages(ctx context.Context, user *entity.SysUser, renewCtx *appRenewPackageContext, requestedPackageCode string) ([]string, []*appRenewTargetContext, *appRenewTargetContext, error) {
|
||
if renewCtx == nil || renewCtx.Card == nil || renewCtx.Package == nil {
|
||
return nil, nil, nil, gerror.New("当前套餐信息不完整")
|
||
}
|
||
codes := appChangePackageCodes(renewCtx.Package.RenewablePackageCodes)
|
||
if len(codes) == 0 {
|
||
return nil, nil, nil, gerror.New("当前套餐未配置可续费套餐")
|
||
}
|
||
requestedPackageCode = appRenewRequestedTargetPackageCode(renewCtx, requestedPackageCode, codes)
|
||
|
||
packages, err := s.loadRenewPackagesByCodes(ctx, renewCtx.Card.TenantId, codes)
|
||
if err != nil {
|
||
return nil, nil, nil, err
|
||
}
|
||
targets := make([]*appRenewTargetContext, 0, len(codes))
|
||
var selected *appRenewTargetContext
|
||
for _, code := range codes {
|
||
pkg := packages[strings.TrimSpace(code)]
|
||
if pkg == nil {
|
||
continue
|
||
}
|
||
durations, err := appRenewableDurationYearOptions(pkg)
|
||
if err != nil {
|
||
return nil, nil, nil, gerror.Newf("续费套餐%s:%v", pkg.PackageName, err)
|
||
}
|
||
price, err := s.resolveRenewUnitPrice(ctx, user, renewCtx.Card, pkg)
|
||
if err != nil {
|
||
return nil, nil, nil, err
|
||
}
|
||
if price.UnitPrice < 0 {
|
||
return nil, nil, nil, gerror.Newf("续费套餐%s价格配置不正确", pkg.PackageName)
|
||
}
|
||
target := &appRenewTargetContext{
|
||
Package: pkg,
|
||
Price: price,
|
||
Durations: durations,
|
||
}
|
||
targets = append(targets, target)
|
||
if requestedPackageCode != "" && strings.TrimSpace(pkg.PackageCode) == requestedPackageCode {
|
||
selected = target
|
||
}
|
||
}
|
||
if len(targets) == 0 {
|
||
return nil, nil, nil, gerror.New("当前套餐可续费套餐不存在或未配置续费年限")
|
||
}
|
||
if requestedPackageCode != "" && selected == nil {
|
||
return nil, nil, nil, gerror.New("续费套餐不存在")
|
||
}
|
||
if selected == nil {
|
||
selected = targets[0]
|
||
}
|
||
return codes, targets, selected, nil
|
||
}
|
||
|
||
func (s *sAppCard) loadRenewPackagesByCodes(ctx context.Context, tenantId string, codes []string) (map[string]*entity.TSimFlowPackageInfo, error) {
|
||
result := make(map[string]*entity.TSimFlowPackageInfo, len(codes))
|
||
codes = appNormalizePackageCodes(codes)
|
||
if len(codes) == 0 {
|
||
return result, nil
|
||
}
|
||
|
||
var rows []*entity.TSimFlowPackageInfo
|
||
err := dao.TSimFlowPackageInfo.Ctx(ctx).
|
||
Where(dao.TSimFlowPackageInfo.Columns().TenantId, strings.TrimSpace(tenantId)).
|
||
WhereIn(dao.TSimFlowPackageInfo.Columns().PackageCode, codes).
|
||
Scan(&rows)
|
||
if err != nil {
|
||
return nil, gerror.Wrap(err, "查询可续费套餐列表失败")
|
||
}
|
||
for _, row := range rows {
|
||
if row != nil && strings.TrimSpace(row.PackageCode) != "" {
|
||
result[strings.TrimSpace(row.PackageCode)] = row
|
||
}
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
func appRenewRequestedTargetPackageCode(renewCtx *appRenewPackageContext, requestedPackageCode string, renewableCodes []string) string {
|
||
requestedPackageCode = strings.TrimSpace(requestedPackageCode)
|
||
if requestedPackageCode == "" {
|
||
return ""
|
||
}
|
||
if appContainsString(renewableCodes, requestedPackageCode) {
|
||
return requestedPackageCode
|
||
}
|
||
if renewCtx != nil && strings.TrimSpace(renewCtx.PackageCode) == requestedPackageCode {
|
||
return ""
|
||
}
|
||
return requestedPackageCode
|
||
}
|
||
|
||
func appContainsString(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 appValidateRenewLatestBind(card *appCardQueryRow, currentPackageCode string, latestBind *entity.TSimCardBindPkg) error {
|
||
if card == nil {
|
||
return gerror.New("卡信息不存在或无权续费")
|
||
}
|
||
currentPackageCode = strings.TrimSpace(currentPackageCode)
|
||
if currentPackageCode == "" || card.PackageStatus == consts.PackageStatusNone {
|
||
return gerror.Newf("ICCID没有当前套餐,不能续费:%s", card.Iccid)
|
||
}
|
||
if latestBind == nil || latestBind.Id == 0 {
|
||
return gerror.Newf("ICCID没有订购记录,不能续费:%s", card.Iccid)
|
||
}
|
||
if strings.TrimSpace(latestBind.PackageCode) != currentPackageCode {
|
||
return gerror.Newf("ICCID当前套餐与最新订购记录不一致,不能续费:%s", card.Iccid)
|
||
}
|
||
baseExpireAt := appRenewBaseExpireTimeFromRow(card, latestBind)
|
||
if baseExpireAt == nil || baseExpireAt.IsZero() {
|
||
return gerror.Newf("ICCID最新订购记录没有预计到期时间,不能续费:%s", card.Iccid)
|
||
}
|
||
if appRenewExpireAfterAllowedWindow(baseExpireAt) {
|
||
return gerror.New("距离到期超过6个月,不能续费")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (s *sAppCard) loadRenewSubscription(ctx context.Context, card *appCardQueryRow, subscriptionNo string) (*entity.TSimCardBindPkg, error) {
|
||
var bind *entity.TSimCardBindPkg
|
||
err := dao.TSimCardBindPkg.Ctx(ctx).
|
||
Where(dao.TSimCardBindPkg.Columns().SubscriptionNo, strings.TrimSpace(subscriptionNo)).
|
||
Limit(1).
|
||
Scan(&bind)
|
||
if err != nil {
|
||
return nil, gerror.Wrap(err, "查询订购记录失败")
|
||
}
|
||
if bind == nil || bind.Id == 0 || strings.TrimSpace(bind.Iccid) != strings.TrimSpace(card.Iccid) {
|
||
return nil, gerror.New("订购记录不存在或不属于当前卡片")
|
||
}
|
||
return bind, nil
|
||
}
|
||
|
||
func (s *sAppCard) loadLatestRenewBind(ctx context.Context, card *appCardQueryRow) (*entity.TSimCardBindPkg, error) {
|
||
var bind *entity.TSimCardBindPkg
|
||
err := dao.TSimCardBindPkg.Ctx(ctx).
|
||
Where(dao.TSimCardBindPkg.Columns().Iccid, card.Iccid).
|
||
OrderDesc(dao.TSimCardBindPkg.Columns().ExpectedExpireTime).
|
||
OrderDesc(dao.TSimCardBindPkg.Columns().Id).
|
||
Limit(1).
|
||
Scan(&bind)
|
||
if err != nil {
|
||
return nil, gerror.Wrap(err, "查询最新订购记录失败")
|
||
}
|
||
if bind == nil || bind.Id == 0 {
|
||
return nil, nil
|
||
}
|
||
return bind, nil
|
||
}
|
||
|
||
func (s *sAppCard) findRenewPackageOrNil(ctx context.Context, tenantId string, packageCode string) (*entity.TSimFlowPackageInfo, error) {
|
||
packageCode = strings.TrimSpace(packageCode)
|
||
if packageCode == "" {
|
||
return nil, nil
|
||
}
|
||
|
||
var pkg *entity.TSimFlowPackageInfo
|
||
err := dao.TSimFlowPackageInfo.Ctx(ctx).
|
||
Where(dao.TSimFlowPackageInfo.Columns().TenantId, strings.TrimSpace(tenantId)).
|
||
Where(dao.TSimFlowPackageInfo.Columns().PackageCode, packageCode).
|
||
Limit(1).
|
||
Scan(&pkg)
|
||
if err != nil {
|
||
return nil, gerror.Wrap(err, "查询续费套餐失败")
|
||
}
|
||
if pkg == nil || pkg.Id == 0 {
|
||
return nil, nil
|
||
}
|
||
return pkg, nil
|
||
}
|
||
|
||
func (renewCtx *appRenewPackageContext) expireAt() *gtime.Time {
|
||
if renewCtx == nil {
|
||
return nil
|
||
}
|
||
expireAt := appRenewBaseExpireTimeFromRow(renewCtx.Card, renewCtx.LatestBind)
|
||
if renewCtx.Subscription != nil &&
|
||
renewCtx.Subscription.ExpectedExpireTime != nil &&
|
||
!renewCtx.Subscription.ExpectedExpireTime.IsZero() &&
|
||
(expireAt == nil || renewCtx.Subscription.ExpectedExpireTime.After(expireAt)) {
|
||
return renewCtx.Subscription.ExpectedExpireTime
|
||
}
|
||
return expireAt
|
||
}
|