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

270 lines
9.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}