实现套餐生效条件覆盖与购买快照
This commit is contained in:
@@ -12,11 +12,13 @@ import (
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
|
||||
packagedomain "github.com/break/junhong_cmp_fiber/internal/domain/package"
|
||||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||
packagepkg "github.com/break/junhong_cmp_fiber/internal/service/package"
|
||||
"github.com/break/junhong_cmp_fiber/internal/service/packageprice"
|
||||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||||
pkgerrors "github.com/break/junhong_cmp_fiber/pkg/errors"
|
||||
)
|
||||
|
||||
// AutoPurchasePayload 充值后自动购包任务载荷
|
||||
@@ -496,7 +498,11 @@ func (h *AutoPurchaseHandler) activateMainPackage(
|
||||
carrierID uint,
|
||||
now time.Time,
|
||||
) error {
|
||||
_ = ctx
|
||||
terms, err := h.resolvePackageTerms(ctx, pkg, order.SellerShopID)
|
||||
if err != nil {
|
||||
h.logger.Error("自动购包生成套餐计时快照失败", zap.Uint("package_id", pkg.ID), zap.Error(err))
|
||||
return err
|
||||
}
|
||||
if err := h.lockPackageCarrier(ctx, tx, carrierType, carrierID); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -512,6 +518,13 @@ func (h *AutoPurchaseHandler) activateMainPackage(
|
||||
var expiresAt time.Time
|
||||
var nextResetAt *time.Time
|
||||
var pendingRealnameActivation bool
|
||||
if terms.ExpiryBase == constants.PackageExpiryBaseFromActivation {
|
||||
realnamed, realnameErr := h.isCarrierRealnamed(ctx, tx, carrierType, carrierID)
|
||||
if realnameErr != nil {
|
||||
return realnameErr
|
||||
}
|
||||
pendingRealnameActivation = !realnamed
|
||||
}
|
||||
|
||||
if hasCurrentMain {
|
||||
status = constants.PackageUsageStatusPending
|
||||
@@ -522,16 +535,17 @@ func (h *AutoPurchaseHandler) activateMainPackage(
|
||||
Scan(&maxPriority)
|
||||
priority = maxPriority + 1
|
||||
} else {
|
||||
status = constants.PackageUsageStatusActive
|
||||
priority = 1
|
||||
activatedAt = now
|
||||
expiresAt = packagepkg.CalculateExpiryTime(pkg.CalendarType, activatedAt, pkg.DurationMonths, pkg.DurationDays)
|
||||
nextResetAt = packagepkg.CalculateNextResetTime(pkg.DataResetCycle, pkg.CalendarType, now, activatedAt)
|
||||
if pendingRealnameActivation {
|
||||
status = constants.PackageUsageStatusPending
|
||||
} else {
|
||||
status = constants.PackageUsageStatusActive
|
||||
activatedAt = now
|
||||
expiresAt = packagepkg.CalculateExpiryTime(terms.CalendarType, activatedAt, terms.DurationMonths, terms.DurationDays)
|
||||
nextResetAt = packagepkg.CalculateNextResetTime(pkg.DataResetCycle, terms.CalendarType, now, activatedAt)
|
||||
}
|
||||
}
|
||||
|
||||
// REALNAME-02: 自动购包属于 C 端充值触发,不需要等实名(C 端已做前置实名检查)
|
||||
// ExpiryBase 仅影响后台囤货路径,此处无需判断
|
||||
|
||||
virtualTotalMBSnapshot, displayGainRatioSnapshot, enableVirtualDataSnapshot := model.BuildPackageUsageSnapshotValues(pkg)
|
||||
retailAmount := order.TotalAmount
|
||||
usage := &model.PackageUsage{
|
||||
@@ -558,6 +572,7 @@ func (h *AutoPurchaseHandler) activateMainPackage(
|
||||
PackagePriceConfigStatus: pkg.PriceConfigStatus,
|
||||
PackageIsGift: pkg.IsGift,
|
||||
}
|
||||
terms.Apply(usage)
|
||||
|
||||
if carrierType == constants.AssetWalletResourceTypeIotCard {
|
||||
usage.IotCardID = carrierID
|
||||
@@ -581,6 +596,30 @@ func (h *AutoPurchaseHandler) activateMainPackage(
|
||||
}).Error
|
||||
}
|
||||
|
||||
// isCarrierRealnamed 查询自动购包载体是否已满足实名激活条件。
|
||||
func (h *AutoPurchaseHandler) isCarrierRealnamed(ctx context.Context, tx *gorm.DB, carrierType string, carrierID uint) (bool, error) {
|
||||
switch carrierType {
|
||||
case constants.AssetWalletResourceTypeIotCard, "card":
|
||||
var card model.IotCard
|
||||
if err := tx.WithContext(ctx).Select("real_name_status").First(&card, carrierID).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return card.RealNameStatus == constants.RealNameStatusVerified, nil
|
||||
case constants.AssetWalletResourceTypeDevice:
|
||||
var count int64
|
||||
subQuery := tx.WithContext(ctx).Model(&model.DeviceSimBinding{}).
|
||||
Select("iot_card_id").Where("device_id = ? AND bind_status = ?", carrierID, constants.BindStatusBound)
|
||||
if err := tx.WithContext(ctx).Model(&model.IotCard{}).
|
||||
Where("id IN (?) AND real_name_status = ?", subQuery, constants.RealNameStatusVerified).
|
||||
Count(&count).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
default:
|
||||
return false, pkgerrors.New(pkgerrors.CodeInvalidParam, "无效的套餐载体类型")
|
||||
}
|
||||
}
|
||||
|
||||
func (h *AutoPurchaseHandler) lockPackageCarrier(ctx context.Context, tx *gorm.DB, carrierType string, carrierID uint) error {
|
||||
switch carrierType {
|
||||
case constants.AssetWalletResourceTypeIotCard, "card":
|
||||
@@ -603,7 +642,11 @@ func (h *AutoPurchaseHandler) activateAddonPackage(
|
||||
carrierID uint,
|
||||
now time.Time,
|
||||
) error {
|
||||
_ = ctx
|
||||
terms, err := h.resolvePackageTerms(ctx, pkg, order.SellerShopID)
|
||||
if err != nil {
|
||||
h.logger.Error("自动购包生成加油包计时快照失败", zap.Uint("package_id", pkg.ID), zap.Error(err))
|
||||
return err
|
||||
}
|
||||
mainPackage, err := packagepkg.FindAttachableMainPackageForAddon(tx, carrierType, carrierID, now)
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return errors.New("必须有主套餐才能购买加油包")
|
||||
@@ -649,6 +692,7 @@ func (h *AutoPurchaseHandler) activateAddonPackage(
|
||||
PackagePriceConfigStatus: pkg.PriceConfigStatus,
|
||||
PackageIsGift: pkg.IsGift,
|
||||
}
|
||||
terms.Apply(usage)
|
||||
|
||||
if carrierType == constants.AssetWalletResourceTypeIotCard {
|
||||
usage.IotCardID = carrierID
|
||||
@@ -659,6 +703,20 @@ func (h *AutoPurchaseHandler) activateAddonPackage(
|
||||
return tx.Create(usage).Error
|
||||
}
|
||||
|
||||
func (h *AutoPurchaseHandler) resolvePackageTerms(ctx context.Context, pkg *model.Package, sellerShopID *uint) (packagedomain.TermsSnapshot, error) {
|
||||
var allocation *model.ShopPackageAllocation
|
||||
if sellerShopID != nil && *sellerShopID > 0 {
|
||||
found, err := h.shopPackageAllocationStore.GetByShopAndPackageForSystem(ctx, *sellerShopID, pkg.ID)
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return packagedomain.TermsSnapshot{}, err
|
||||
}
|
||||
if err == nil {
|
||||
allocation = found
|
||||
}
|
||||
}
|
||||
return packagedomain.ResolveTermsSnapshot(pkg, allocation)
|
||||
}
|
||||
|
||||
func parseLinkedPackageIDs(raw []byte) ([]uint, error) {
|
||||
var packageIDs []uint
|
||||
if len(raw) == 0 {
|
||||
|
||||
Reference in New Issue
Block a user