Files
junhong_cmp_fiber/internal/service/paymentmethod/policy.go
break 73f5125d3d
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m26s
七月迭代短暂完结,还有很多后端的关键东西没有弄,这是一版赶时间做的东西
2026-07-25 17:06:58 +08:00

135 lines
4.1 KiB
Go

// Package paymentmethod 提供按资产类型读取和校验 C 端支付方式的能力。
package paymentmethod
import (
"context"
stderrors "errors"
"slices"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/bytedance/sonic"
)
// ConfigReader 提供受控系统配置读取能力。
type ConfigReader interface {
GetStrict(ctx context.Context, key string) (string, error)
}
// Policy 负责返回资产允许的支付方式并执行后端强校验。
type Policy struct {
reader ConfigReader
}
// NewPolicy 创建支付方式策略。
func NewPolicy(reader ConfigReader) *Policy {
return &Policy{reader: reader}
}
// AllowedMethods 返回指定资产类型允许的支付方式;配置异常时失败关闭。
func (p *Policy) AllowedMethods(ctx context.Context, assetType string) ([]string, error) {
if p == nil || p.reader == nil {
return nil, errors.New(errors.CodeNoPaymentConfig)
}
key, err := configKey(assetType)
if err != nil {
return nil, err
}
raw, err := p.reader.GetStrict(ctx, key)
if err != nil {
return nil, errors.Wrap(errors.CodeNoPaymentConfig, err, "读取支付方式配置失败")
}
var methods []string
if err := sonic.Unmarshal([]byte(raw), &methods); err != nil || !validMethods(methods) {
return nil, errors.New(errors.CodeNoPaymentConfig, "支付方式配置无效")
}
return stableMethods(methods), nil
}
// EnsureAllowed 校验指定支付方式是否允许。
func (p *Policy) EnsureAllowed(ctx context.Context, assetType, paymentMethod string) error {
methods, err := p.AllowedMethods(ctx, assetType)
if err != nil {
return err
}
if !slices.Contains(methods, paymentMethod) {
return errors.New(errors.CodePaymentMethodUnavailable)
}
return nil
}
// AllowedRechargeMethods 返回资产允许且钱包充值接口支持的第三方支付方式。
func (p *Policy) AllowedRechargeMethods(ctx context.Context, assetType string) ([]string, error) {
methods, err := p.AllowedMethods(ctx, assetType)
if err != nil {
return nil, err
}
result := make([]string, 0, 2)
for _, method := range methods {
if method == model.PaymentMethodWechat || method == model.PaymentMethodAlipay {
result = append(result, method)
}
}
return result, nil
}
// EnsureRechargeAllowed 校验普通充值只使用资产配置允许的第三方支付方式。
func (p *Policy) EnsureRechargeAllowed(ctx context.Context, assetType, paymentMethod string) error {
methods, err := p.AllowedRechargeMethods(ctx, assetType)
if err != nil {
return err
}
if !slices.Contains(methods, paymentMethod) {
return errors.New(errors.CodePaymentMethodUnavailable)
}
return nil
}
func configKey(assetType string) (string, error) {
switch assetType {
case "card", constants.AssetTypeIotCard, model.OrderTypeSingleCard:
return constants.SystemConfigPaymentAllowedCard, nil
case constants.AssetTypeDevice:
return constants.SystemConfigPaymentAllowedDevice, nil
default:
return "", errors.New(errors.CodeInvalidParam, "无效的资产类型")
}
}
func validMethods(methods []string) bool {
if len(methods) == 0 {
return false
}
seen := make(map[string]struct{}, len(methods))
for _, method := range methods {
if method != model.PaymentMethodWallet && method != model.PaymentMethodWechat && method != model.PaymentMethodAlipay {
return false
}
if _, exists := seen[method]; exists {
return false
}
seen[method] = struct{}{}
}
return true
}
// ValidateConfigValue 校验支付方式系统配置的 JSON 集合。
func ValidateConfigValue(value string) error {
var methods []string
if err := sonic.Unmarshal([]byte(value), &methods); err != nil || !validMethods(methods) {
return stderrors.New("支付方式配置必须是非空且不重复的合法数组")
}
return nil
}
func stableMethods(methods []string) []string {
result := make([]string, 0, len(methods))
for _, method := range []string{model.PaymentMethodWallet, model.PaymentMethodWechat, model.PaymentMethodAlipay} {
if slices.Contains(methods, method) {
result = append(result, method)
}
}
return result
}