七月迭代短暂完结,还有很多后端的关键东西没有弄,这是一版赶时间做的东西
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m26s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m26s
This commit is contained in:
134
internal/service/paymentmethod/policy.go
Normal file
134
internal/service/paymentmethod/policy.go
Normal file
@@ -0,0 +1,134 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user