Files
huang 42c5ec912f
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m13s
feat: 技术债务清理(支付配置动态化、API文档补全、轮询常量提取、废弃代码清理)
2026-04-14 11:11:15 +08:00

142 lines
5.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 缓存读取 WechatConfigTTL 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 构建对应的支付服务实例
// 支持 wechatv3和 wechat_v2v2两种渠道类型
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 处理")
}