feat: C 端充值、购买、实名链接接口接入实名策略拦截(before_order/after_order)

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-17 17:31:21 +08:00
parent 1363797378
commit 363e0efb34
3 changed files with 49 additions and 5 deletions

View File

@@ -107,6 +107,17 @@ func (h *ClientRealnameHandler) GetRealnameLink(c *fiber.Ctx) error {
return err
}
// after_order 策略:必须先有充值或订单才能实名
if asset.RealnamePolicy == constants.RealnamePolicyAfterOrder {
hasValid, checkErr := h.assetService.HasValidRechargeOrPaidOrder(ctx, asset.AssetType, asset.AssetID)
if checkErr != nil {
logger.GetAppLogger().Warn("检查资产有效充值或订单失败", zap.Error(checkErr))
}
if !hasValid {
return errors.New(errors.CodeRealnameNotAvailable)
}
}
// 6. 检查实名状态
if targetCard.RealNameStatus == constants.RealNameStatusVerified {
return errors.New(errors.CodeInvalidStatus, "该卡已完成实名")

View File

@@ -230,6 +230,11 @@ func (h *ClientWalletHandler) GetRechargeCheck(c *fiber.Ctx) error {
return err
}
// before_order 策略:必须先实名才能充值
if resolved.Asset.RealnamePolicy == constants.RealnamePolicyBeforeOrder && resolved.Asset.RealNameStatus != 1 {
return errors.New(errors.CodeNeedRealname)
}
check, err := h.rechargeService.GetRechargeCheck(resolved.SkipPermissionCtx, resolved.ResourceType, resolved.Asset.AssetID)
if err != nil {
return err
@@ -264,6 +269,11 @@ func (h *ClientWalletHandler) CreateRecharge(c *fiber.Ctx) error {
return err
}
// before_order 策略:必须先实名才能充值
if resolved.Asset.RealnamePolicy == constants.RealnamePolicyBeforeOrder && resolved.Asset.RealNameStatus != 1 {
return errors.New(errors.CodeNeedRealname)
}
wallet, err := h.getOrCreateWallet(resolved)
if err != nil {
return err

View File

@@ -140,11 +140,10 @@ func (s *Service) CreateOrder(ctx context.Context, customerID uint, req *dto.Cli
return nil, err
}
// 暂不做实名认证拦截,由网关侧处理
// REALNAME-03: 普通卡需要实名,行业卡不需要,待后续业务明确后按卡类型分支启用
// if assetInfo.CardCategory == "normal" && assetInfo.RealNameStatus != constants.RealNameStatusVerified {
// return nil, errors.New(errors.CodeNeedRealname)
// }
// before_order 策略:必须先实名才能购买套餐
if assetInfo.RealnamePolicy == constants.RealnamePolicyBeforeOrder && assetInfo.RealNameStatus != 1 {
return nil, errors.New(errors.CodeNeedRealname)
}
// 先判断是否需要强充,避免普通下单时不必要地校验微信配置
forceRecharge := s.checkForceRechargeRequirement(skipCtx, validationResult)
@@ -945,6 +944,30 @@ func (s *Service) PayOrder(ctx context.Context, customerID uint, orderID uint, r
return nil, errors.New(errors.CodeInvalidStatus, "订单状态不允许支付")
}
// before_order 策略:必须先实名才能支付订单
var assetRealnamePolicy string
var assetRealnameStatus int
if order.OrderType == model.OrderTypeSingleCard && order.IotCardID != nil {
card, err := s.iotCardStore.GetByID(skipCtx, *order.IotCardID)
if err == nil && card != nil {
assetRealnamePolicy = card.RealnamePolicy
assetRealnameStatus = card.RealNameStatus
}
} else if order.OrderType == model.OrderTypeDevice && order.DeviceID != nil {
device, err := s.deviceStore.GetByID(skipCtx, *order.DeviceID)
if err == nil && device != nil {
assetRealnamePolicy = device.RealnamePolicy
// 设备的 RealNameStatus 需要通过 Resolve 获取(从当前卡派生)
resolved, resolveErr := s.assetService.Resolve(skipCtx, device.VirtualNo)
if resolveErr == nil && resolved != nil {
assetRealnameStatus = resolved.RealNameStatus
}
}
}
if assetRealnamePolicy == constants.RealnamePolicyBeforeOrder && assetRealnameStatus != 1 {
return nil, errors.New(errors.CodeNeedRealname)
}
switch req.PaymentMethod {
case model.PaymentMethodWallet:
if err := s.orderPaymentService.WalletPay(skipCtx, orderID, model.BuyerTypePersonal, customerID); err != nil {