feat: 技术债务清理(支付配置动态化、API文档补全、轮询常量提取、废弃代码清理)
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m13s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m13s
This commit is contained in:
141
pkg/payment/loader.go
Normal file
141
pkg/payment/loader.go
Normal file
@@ -0,0 +1,141 @@
|
||||
// Package payment 提供支付配置动态加载功能
|
||||
// 根据 payment_config_id 按需加载支付实例,支持多商户场景
|
||||
package payment
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel"
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"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/wechat"
|
||||
)
|
||||
|
||||
// PaymentConfigLoader 支付配置加载器接口
|
||||
// 根据 payment_config_id 动态加载支付实例,支持多商户场景
|
||||
type PaymentConfigLoader interface {
|
||||
// LoadConfig 加载指定 ID 的支付配置实例
|
||||
LoadConfig(ctx context.Context, configID uint) (wechat.PaymentServiceInterface, error)
|
||||
}
|
||||
|
||||
// paymentConfigLoader PaymentConfigLoader 的具体实现
|
||||
type paymentConfigLoader struct {
|
||||
wechatConfigStore *postgres.WechatConfigStore
|
||||
redis *redis.Client
|
||||
wechatCache kernel.CacheInterface
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewPaymentConfigLoader 创建 PaymentConfigLoader 实例
|
||||
func NewPaymentConfigLoader(
|
||||
store *postgres.WechatConfigStore,
|
||||
rdb *redis.Client,
|
||||
logger *zap.Logger,
|
||||
) PaymentConfigLoader {
|
||||
return &paymentConfigLoader{
|
||||
wechatConfigStore: store,
|
||||
redis: rdb,
|
||||
wechatCache: wechat.NewRedisCache(rdb),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// LoadConfig 根据 configID 加载支付配置实例
|
||||
// 优先从 Redis 缓存读取 WechatConfig(TTL 1 小时),未命中则查数据库
|
||||
func (l *paymentConfigLoader) LoadConfig(ctx context.Context, configID uint) (wechat.PaymentServiceInterface, error) {
|
||||
config, err := l.loadConfigFromCacheOrDB(ctx, configID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l.buildPaymentService(config)
|
||||
}
|
||||
|
||||
// loadConfigFromCacheOrDB 从 Redis 缓存或数据库加载 WechatConfig
|
||||
// 缓存 key: payment:config:{configID},TTL 1 小时
|
||||
func (l *paymentConfigLoader) loadConfigFromCacheOrDB(ctx context.Context, configID uint) (*model.WechatConfig, error) {
|
||||
cacheKey := constants.RedisPaymentConfigKey(configID)
|
||||
|
||||
val, err := l.redis.Get(ctx, cacheKey).Result()
|
||||
if err == nil {
|
||||
var config model.WechatConfig
|
||||
if unmarshalErr := sonic.UnmarshalString(val, &config); unmarshalErr == nil {
|
||||
return &config, nil
|
||||
}
|
||||
}
|
||||
|
||||
config, dbErr := l.wechatConfigStore.GetByID(ctx, configID)
|
||||
if dbErr != nil {
|
||||
if dbErr == gorm.ErrRecordNotFound {
|
||||
return nil, errors.New(errors.CodeWechatConfigNotFound)
|
||||
}
|
||||
return nil, errors.Wrap(errors.CodeInternalError, dbErr, "加载支付配置失败")
|
||||
}
|
||||
|
||||
if data, marshalErr := sonic.MarshalString(config); marshalErr == nil {
|
||||
l.redis.Set(ctx, cacheKey, data, time.Hour)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// buildPaymentService 根据 WechatConfig 构建对应的支付服务实例
|
||||
// 支持 wechat(v3)和 wechat_v2(v2)两种渠道类型
|
||||
func (l *paymentConfigLoader) buildPaymentService(config *model.WechatConfig) (wechat.PaymentServiceInterface, error) {
|
||||
switch config.ProviderType {
|
||||
case model.ProviderTypeWechat:
|
||||
// 微信直连 v3,使用 PowerWeChat SDK
|
||||
app, err := wechat.NewPaymentAppFromConfig(config, config.OaAppID, l.wechatCache, l.logger)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.CodeWechatPayFailed, err, "构建微信支付 v3 实例失败")
|
||||
}
|
||||
return wechat.NewPaymentService(app, l.logger), nil
|
||||
|
||||
case model.ProviderTypeWechatV2:
|
||||
// 微信直连 v2,仅需 APIv2Key,不要求证书
|
||||
svc, err := wechat.NewPaymentV2ServiceFromConfig(config, config.OaAppID, l.logger)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.CodeWechatPayFailed, err, "构建微信支付 v2 实例失败")
|
||||
}
|
||||
// v2 不实现完整接口,通过适配器包装
|
||||
return &v2PaymentAdapter{svc: svc}, nil
|
||||
|
||||
default:
|
||||
return nil, errors.New(errors.CodeWechatConfigNotFound, "不支持的支付渠道类型: "+config.ProviderType)
|
||||
}
|
||||
}
|
||||
|
||||
// v2PaymentAdapter 微信支付 v2 适配器
|
||||
// 将 PaymentV2Service 适配为完整的 PaymentServiceInterface
|
||||
// v2 仅支持 JSAPI 支付,其余方法返回不支持错误
|
||||
type v2PaymentAdapter struct {
|
||||
svc *wechat.PaymentV2Service
|
||||
}
|
||||
|
||||
func (a *v2PaymentAdapter) CreateJSAPIOrder(ctx context.Context, orderNo, description, openID string, amount int) (*wechat.JSAPIPayResult, error) {
|
||||
return a.svc.CreateJSAPIOrder(ctx, orderNo, description, openID, amount)
|
||||
}
|
||||
|
||||
func (a *v2PaymentAdapter) CreateH5Order(_ context.Context, _ string, _ string, _ int, _ *wechat.H5SceneInfo) (*wechat.H5PayResult, error) {
|
||||
return nil, errors.New(errors.CodeWechatPayFailed, "微信支付 v2 不支持 H5 支付")
|
||||
}
|
||||
|
||||
func (a *v2PaymentAdapter) QueryOrder(_ context.Context, _ string) (*wechat.OrderInfo, error) {
|
||||
return nil, errors.New(errors.CodeWechatPayFailed, "微信支付 v2 暂不支持查单接口")
|
||||
}
|
||||
|
||||
func (a *v2PaymentAdapter) CloseOrder(_ context.Context, _ string) error {
|
||||
return errors.New(errors.CodeWechatPayFailed, "微信支付 v2 暂不支持关单接口")
|
||||
}
|
||||
|
||||
func (a *v2PaymentAdapter) HandlePaymentNotify(_ *http.Request, _ wechat.PaymentNotifyCallback) (*http.Response, error) {
|
||||
return nil, errors.New(errors.CodeWechatPayFailed, "微信支付 v2 回调请通过 VerifyCallback 处理")
|
||||
}
|
||||
Reference in New Issue
Block a user