All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m32s
- 新增 RechargeOrder 和 Payment 模型及对应 Store - 新增 ClientRechargeOrderHandler 提供充值订单列表/详情接口 - 修改 client_wallet.go 使用新表读写充值数据 - 修改 callback/payment.go 将 CRCH 订单路由到 rechargeOrderService - 修改 client_order/service.go 的强充流程使用新表 - 修改 auto_purchase.go 从 tb_recharge_order 读取 linked_package_ids - 修改 order/service.go 的 WalletPay 使用 tb_payment 记录 - 修改 wechat_config_store.go 从 tb_recharge_order 统计待支付充值数 - 移除 AssetRechargeStore 和 AssetRechargeRecord 的注册引用 - 修复文档生成器缺失 ClientRechargeOrder handler - 状态枚举改为 0-based: Pending=0, Paid=1, Closed=2, Refunded=3
199 lines
7.2 KiB
Go
199 lines
7.2 KiB
Go
package recharge
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
|
||
"github.com/break/junhong_cmp_fiber/internal/model"
|
||
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
|
||
"github.com/break/junhong_cmp_fiber/pkg/constants"
|
||
"github.com/break/junhong_cmp_fiber/pkg/errors"
|
||
"github.com/break/junhong_cmp_fiber/pkg/payment"
|
||
"go.uber.org/zap"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// ForceRechargeRequirement 强充要求信息
|
||
type ForceRechargeRequirement struct {
|
||
NeedForceRecharge bool `json:"need_force_recharge"` // 是否需要强充
|
||
ForceRechargeAmount int64 `json:"force_recharge_amount"` // 强充金额(分)
|
||
TriggerType string `json:"trigger_type"` // 触发类型: single_recharge/accumulated_recharge
|
||
MinAmount int64 `json:"min_amount"` // 最小充值金额
|
||
MaxAmount int64 `json:"max_amount"` // 最大充值金额
|
||
CurrentAccumulated int64 `json:"current_accumulated"` // 当前累计充值
|
||
Threshold int64 `json:"threshold"` // 佣金触发阈值
|
||
Message string `json:"message"` // 提示信息
|
||
}
|
||
|
||
// WechatConfigServiceInterface 支付配置服务接口
|
||
type WechatConfigServiceInterface interface {
|
||
GetActiveConfig(ctx context.Context) (*model.WechatConfig, error)
|
||
}
|
||
|
||
// Service 充值服务
|
||
// 负责资产钱包(IoT卡/设备)的充值订单创建、预检、支付回调处理等业务逻辑
|
||
type Service struct {
|
||
db *gorm.DB
|
||
assetWalletStore *postgres.AssetWalletStore
|
||
assetWalletTransactionStore *postgres.AssetWalletTransactionStore
|
||
iotCardStore *postgres.IotCardStore
|
||
deviceStore *postgres.DeviceStore
|
||
shopSeriesAllocationStore *postgres.ShopSeriesAllocationStore
|
||
packageSeriesStore *postgres.PackageSeriesStore
|
||
commissionRecordStore *postgres.CommissionRecordStore
|
||
wechatConfigService WechatConfigServiceInterface
|
||
paymentLoader payment.PaymentConfigLoader
|
||
logger *zap.Logger
|
||
}
|
||
|
||
// New 创建充值服务实例
|
||
func New(
|
||
db *gorm.DB,
|
||
assetWalletStore *postgres.AssetWalletStore,
|
||
assetWalletTransactionStore *postgres.AssetWalletTransactionStore,
|
||
iotCardStore *postgres.IotCardStore,
|
||
deviceStore *postgres.DeviceStore,
|
||
shopSeriesAllocationStore *postgres.ShopSeriesAllocationStore,
|
||
packageSeriesStore *postgres.PackageSeriesStore,
|
||
commissionRecordStore *postgres.CommissionRecordStore,
|
||
wechatConfigService WechatConfigServiceInterface,
|
||
paymentLoader payment.PaymentConfigLoader,
|
||
logger *zap.Logger,
|
||
) *Service {
|
||
return &Service{
|
||
db: db,
|
||
assetWalletStore: assetWalletStore,
|
||
assetWalletTransactionStore: assetWalletTransactionStore,
|
||
iotCardStore: iotCardStore,
|
||
deviceStore: deviceStore,
|
||
shopSeriesAllocationStore: shopSeriesAllocationStore,
|
||
packageSeriesStore: packageSeriesStore,
|
||
commissionRecordStore: commissionRecordStore,
|
||
wechatConfigService: wechatConfigService,
|
||
paymentLoader: paymentLoader,
|
||
logger: logger,
|
||
}
|
||
}
|
||
|
||
// GetRechargeCheck 充值预检
|
||
// 返回强充要求、金额限制等信息
|
||
func (s *Service) GetRechargeCheck(ctx context.Context, resourceType string, resourceID uint) (*ForceRechargeRequirement, error) {
|
||
// 验证资源类型
|
||
if resourceType != "iot_card" && resourceType != "device" {
|
||
return nil, errors.New(errors.CodeInvalidParam, "无效的资源类型")
|
||
}
|
||
|
||
// 验证资源是否存在
|
||
if resourceType == "iot_card" {
|
||
if _, err := s.iotCardStore.GetByID(ctx, resourceID); err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, errors.New(errors.CodeIotCardNotFound, "IoT卡不存在")
|
||
}
|
||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询IoT卡失败")
|
||
}
|
||
} else {
|
||
if _, err := s.deviceStore.GetByID(ctx, resourceID); err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, errors.New(errors.CodeNotFound, "设备不存在")
|
||
}
|
||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询设备失败")
|
||
}
|
||
}
|
||
|
||
return s.checkForceRechargeRequirement(ctx, resourceType, resourceID)
|
||
}
|
||
|
||
// checkForceRechargeRequirement 检查强充要求
|
||
// 根据资源类型和ID检查是否需要强制充值指定金额
|
||
func (s *Service) checkForceRechargeRequirement(ctx context.Context, resourceType string, resourceID uint) (*ForceRechargeRequirement, error) {
|
||
result := &ForceRechargeRequirement{
|
||
NeedForceRecharge: false,
|
||
MinAmount: constants.AssetRechargeMinAmount,
|
||
MaxAmount: constants.AssetRechargeMaxAmount,
|
||
Message: "无强充要求,可自由充值",
|
||
}
|
||
|
||
var seriesID *uint
|
||
var shopID *uint
|
||
var accumulatedRecharge int64
|
||
var firstCommissionPaid bool
|
||
|
||
if resourceType == "iot_card" {
|
||
card, err := s.iotCardStore.GetByID(ctx, resourceID)
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, errors.New(errors.CodeIotCardNotFound, "IoT卡不存在")
|
||
}
|
||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询IoT卡失败")
|
||
}
|
||
seriesID = card.SeriesID
|
||
shopID = card.ShopID
|
||
if seriesID != nil {
|
||
accumulatedRecharge = card.GetAccumulatedRechargeBySeries(*seriesID)
|
||
firstCommissionPaid = card.IsFirstRechargeTriggeredBySeries(*seriesID)
|
||
}
|
||
} else if resourceType == "device" {
|
||
device, err := s.deviceStore.GetByID(ctx, resourceID)
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, errors.New(errors.CodeNotFound, "设备不存在")
|
||
}
|
||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询设备失败")
|
||
}
|
||
seriesID = device.SeriesID
|
||
shopID = device.ShopID
|
||
if seriesID != nil {
|
||
accumulatedRecharge = device.GetAccumulatedRechargeBySeries(*seriesID)
|
||
firstCommissionPaid = device.IsFirstRechargeTriggeredBySeries(*seriesID)
|
||
}
|
||
}
|
||
|
||
result.CurrentAccumulated = accumulatedRecharge
|
||
|
||
if seriesID == nil || shopID == nil {
|
||
return result, nil
|
||
}
|
||
|
||
series, err := s.packageSeriesStore.GetByID(ctx, *seriesID)
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return result, nil
|
||
}
|
||
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询套餐系列失败")
|
||
}
|
||
|
||
config, err := series.GetOneTimeCommissionConfig()
|
||
if err != nil || config == nil || !config.Enable {
|
||
return result, nil
|
||
}
|
||
|
||
result.Threshold = config.Threshold
|
||
result.TriggerType = config.TriggerType
|
||
|
||
if firstCommissionPaid {
|
||
result.Message = "一次性佣金已发放,无强充要求"
|
||
return result, nil
|
||
}
|
||
|
||
if config.TriggerType == model.OneTimeCommissionTriggerFirstRecharge {
|
||
result.NeedForceRecharge = true
|
||
result.ForceRechargeAmount = config.Threshold
|
||
result.Message = fmt.Sprintf("首次充值必须充值%d分", config.Threshold)
|
||
} else if config.EnableForceRecharge {
|
||
result.NeedForceRecharge = true
|
||
if config.ForceAmount > 0 {
|
||
result.ForceRechargeAmount = config.ForceAmount
|
||
} else {
|
||
result.ForceRechargeAmount = config.Threshold
|
||
}
|
||
result.Message = fmt.Sprintf("每次充值必须充值%d分", result.ForceRechargeAmount)
|
||
} else {
|
||
result.Message = "累计充值模式,可自由充值"
|
||
}
|
||
|
||
return result, nil
|
||
}
|
||
|
||
// updateAccumulatedRechargeInTx 更新累计充值(事务内使用)
|
||
// 同时更新旧的 accumulated_recharge 字段和新的 accumulated_recharge_by_series JSON 字段
|