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:
@@ -43,7 +43,6 @@ const (
|
||||
// 任务类型常量
|
||||
const (
|
||||
TaskTypeEmailSend = "email:send" // 发送邮件
|
||||
TaskTypeDataSync = "data:sync" // 数据同步
|
||||
TaskTypeCommission = "commission:calculate" // 分佣计算
|
||||
TaskTypeIotCardImport = "iot_card:import" // IoT 卡批量导入
|
||||
TaskTypeDeviceImport = "device:import" // 设备批量导入
|
||||
@@ -233,3 +232,48 @@ const (
|
||||
ExchangeAssetTypeIotCard = "iot_card" // 物联网卡
|
||||
ExchangeAssetTypeDevice = "device" // 设备
|
||||
)
|
||||
|
||||
// GetStatusName 获取通用启用/禁用状态名称
|
||||
// 用于 status 字段:0=禁用, 1=启用
|
||||
func GetStatusName(status int) string {
|
||||
switch status {
|
||||
case StatusDisabled:
|
||||
return "禁用"
|
||||
case StatusEnabled:
|
||||
return "启用"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
// GetShelfStatusName 获取上架状态名称
|
||||
// 用于 shelf_status 字段:1=上架, 2=下架
|
||||
func GetShelfStatusName(status int) string {
|
||||
switch status {
|
||||
case ShelfStatusOn:
|
||||
return "上架"
|
||||
case ShelfStatusOff:
|
||||
return "下架"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
// GetExchangeStatusName 获取换货单状态名称
|
||||
// 用于 exchange_status 字段
|
||||
func GetExchangeStatusName(status int) string {
|
||||
switch status {
|
||||
case ExchangeStatusPendingInfo:
|
||||
return "待填写信息"
|
||||
case ExchangeStatusPendingShip:
|
||||
return "待发货"
|
||||
case ExchangeStatusShipped:
|
||||
return "已发货待确认"
|
||||
case ExchangeStatusCompleted:
|
||||
return "已完成"
|
||||
case ExchangeStatusCancelled:
|
||||
return "已取消"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,11 +186,11 @@ const (
|
||||
CommissionStatusPendingReview = 99 // 待人工修正(链路断裂,需平台处理)
|
||||
)
|
||||
|
||||
// 阶梯类型
|
||||
// 阶梯类型([预留] 用于分佣阶梯功能,待产品规划)
|
||||
const (
|
||||
LadderTypeActivation = "activation" // 激活量
|
||||
LadderTypePickup = "pickup" // 提货量
|
||||
LadderTypeDeposit = "deposit" // 充值量
|
||||
LadderTypeActivation = "activation" // 激活量([预留])
|
||||
LadderTypePickup = "pickup" // 提货量([预留])
|
||||
LadderTypeDeposit = "deposit" // 充值量([预留])
|
||||
)
|
||||
|
||||
// 卡类型
|
||||
@@ -199,17 +199,17 @@ const (
|
||||
CardTypeIotCard = "iot_card" // IoT卡
|
||||
)
|
||||
|
||||
// 审批类型
|
||||
// 审批类型([预留] 用于审批流程功能,待产品规划)
|
||||
const (
|
||||
ApprovalTypeAuto = "auto" // 自动
|
||||
ApprovalTypeManual = "manual" // 人工
|
||||
ApprovalTypeAuto = "auto" // 自动([预留])
|
||||
ApprovalTypeManual = "manual" // 人工([预留])
|
||||
)
|
||||
|
||||
// 审批状态
|
||||
// 审批状态([预留] 用于审批流程功能,待产品规划)
|
||||
const (
|
||||
ApprovalStatusPending = 1 // 待审批
|
||||
ApprovalStatusApproved = 2 // 已通过
|
||||
ApprovalStatusRejected = 3 // 已拒绝
|
||||
ApprovalStatusPending = 1 // 待审批([预留])
|
||||
ApprovalStatusApproved = 2 // 已通过([预留])
|
||||
ApprovalStatusRejected = 3 // 已拒绝([预留])
|
||||
)
|
||||
|
||||
// ========================================
|
||||
@@ -231,23 +231,23 @@ const (
|
||||
WithdrawalMethodBank = "bank" // 银行卡
|
||||
)
|
||||
|
||||
// 商户类型
|
||||
// 商户类型([预留] 用于未来商户管理功能,待产品规划)
|
||||
const (
|
||||
MerchantTypeAlipay = "alipay" // 支付宝
|
||||
MerchantTypeWechat = "wechat" // 微信
|
||||
MerchantTypeBank = "bank" // 银行卡
|
||||
MerchantTypeAlipay = "alipay" // 支付宝([预留])
|
||||
MerchantTypeWechat = "wechat" // 微信([预留])
|
||||
MerchantTypeBank = "bank" // 银行卡([预留])
|
||||
)
|
||||
|
||||
// ========================================
|
||||
// 5. 系统管理常量
|
||||
// ========================================
|
||||
|
||||
// 换卡申请状态
|
||||
// 换卡申请状态([预留] 用于换卡申请功能,待产品规划)
|
||||
const (
|
||||
ReplacementStatusPending = 1 // 待处理
|
||||
ReplacementStatusApproved = 2 // 已通过
|
||||
ReplacementStatusRejected = 3 // 已拒绝
|
||||
ReplacementStatusCompleted = 4 // 已完成
|
||||
ReplacementStatusPending = 1 // 待处理([预留])
|
||||
ReplacementStatusApproved = 2 // 已通过([预留])
|
||||
ReplacementStatusRejected = 3 // 已拒绝([预留])
|
||||
ReplacementStatusCompleted = 4 // 已完成([预留])
|
||||
)
|
||||
|
||||
// ========================================
|
||||
@@ -261,10 +261,137 @@ const (
|
||||
CarrierCodeCBN = "CBN" // 广电
|
||||
)
|
||||
|
||||
// 换货原因([预留] 用于换卡原因管理功能,待产品规划)
|
||||
const (
|
||||
ReplacementReasonDamaged = "damaged"
|
||||
ReplacementReasonLost = "lost"
|
||||
ReplacementReasonMalfunction = "malfunction"
|
||||
ReplacementReasonUpgrade = "upgrade"
|
||||
ReplacementReasonOther = "other"
|
||||
ReplacementReasonDamaged = "damaged" // 损坏([预留])
|
||||
ReplacementReasonLost = "lost" // 丢失([预留])
|
||||
ReplacementReasonMalfunction = "malfunction" // 故障([预留])
|
||||
ReplacementReasonUpgrade = "upgrade" // 升级([预留])
|
||||
ReplacementReasonOther = "other" // 其他([预留])
|
||||
)
|
||||
|
||||
// ========================================
|
||||
// 状态名称映射函数
|
||||
// ========================================
|
||||
|
||||
// GetIotCardStatusName 获取 IoT 卡业务状态名称
|
||||
// 对应 IotCardStatus* 常量:1=在库, 2=已分销, 3=已激活, 4=已停用
|
||||
func GetIotCardStatusName(status int) string {
|
||||
switch status {
|
||||
case IotCardStatusInStock:
|
||||
return "在库"
|
||||
case IotCardStatusDistributed:
|
||||
return "已分销"
|
||||
case IotCardStatusActivated:
|
||||
return "已激活"
|
||||
case IotCardStatusSuspended:
|
||||
return "已停用"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
// GetActivationStatusName 获取激活状态名称
|
||||
// 对应 ActivationStatus* 常量:0=未激活, 1=已激活
|
||||
func GetActivationStatusName(status int) string {
|
||||
switch status {
|
||||
case ActivationStatusInactive:
|
||||
return "未激活"
|
||||
case ActivationStatusActive:
|
||||
return "已激活"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
// GetRealNameStatusName 获取实名状态名称
|
||||
// 对应 RealNameStatus* 常量:0=未实名, 1=已实名
|
||||
func GetRealNameStatusName(status int) string {
|
||||
switch status {
|
||||
case RealNameStatusNotVerified:
|
||||
return "未实名"
|
||||
case RealNameStatusVerified:
|
||||
return "已实名"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
// GetNetworkStatusName 获取网络状态名称
|
||||
// 对应 NetworkStatus* 常量:0=停机, 1=开机
|
||||
func GetNetworkStatusName(status int) string {
|
||||
switch status {
|
||||
case NetworkStatusOffline:
|
||||
return "停机"
|
||||
case NetworkStatusOnline:
|
||||
return "开机"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
// GetCommissionRecordStatusName 获取佣金记录状态名称
|
||||
// 对应 CommissionStatus* 常量:1=已冻结, 2=解冻中, 3=已发放, 4=已失效, 99=待人工修正
|
||||
func GetCommissionRecordStatusName(status int) string {
|
||||
switch status {
|
||||
case CommissionStatusFrozen:
|
||||
return "已冻结"
|
||||
case CommissionStatusUnfreezing:
|
||||
return "解冻中"
|
||||
case CommissionStatusReleased:
|
||||
return "已发放"
|
||||
case CommissionStatusInvalid:
|
||||
return "已失效"
|
||||
case CommissionStatusPendingReview:
|
||||
return "待人工修正"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrderPaymentStatusName 获取订单支付状态名称
|
||||
// 1=待支付, 2=已支付, 3=已取消, 4=已退款
|
||||
func GetOrderPaymentStatusName(status int) string {
|
||||
switch status {
|
||||
case 1:
|
||||
return "待支付"
|
||||
case 2:
|
||||
return "已支付"
|
||||
case 3:
|
||||
return "已取消"
|
||||
case 4:
|
||||
return "已退款"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrderCommissionStatusName 获取订单佣金计算状态名称
|
||||
// 1=待计算, 2=已计算
|
||||
func GetOrderCommissionStatusName(status int) string {
|
||||
switch status {
|
||||
case 1:
|
||||
return "待计算"
|
||||
case 2:
|
||||
return "已计算"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
// GetRefundStatusName 获取退款申请状态名称
|
||||
// 1=待审批, 2=已通过, 3=已拒绝, 4=已退回
|
||||
func GetRefundStatusName(status int) string {
|
||||
switch status {
|
||||
case 1:
|
||||
return "待审批"
|
||||
case 2:
|
||||
return "已通过"
|
||||
case 3:
|
||||
return "已拒绝"
|
||||
case 4:
|
||||
return "已退回"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,3 +21,15 @@ const PollingDefaultMaxConcurrency = 300
|
||||
// PollingConcurrencyKeyTTL 并发计数 key 的过期时间(秒)
|
||||
// 保证 Worker 意外崩溃(defer Decr 未执行)时计数器在此时间内自动归零
|
||||
const PollingConcurrencyKeyTTL = 600 // 10 分钟
|
||||
|
||||
// 轮询手动触发日志状态常量
|
||||
const (
|
||||
// PollingManualTriggerStatusPending 轮询手动触发日志状态-待处理
|
||||
PollingManualTriggerStatusPending = "pending"
|
||||
// PollingManualTriggerStatusProcessing 轮询手动触发日志状态-处理中
|
||||
PollingManualTriggerStatusProcessing = "processing"
|
||||
// PollingManualTriggerStatusCompleted 轮询手动触发日志状态-已完成
|
||||
PollingManualTriggerStatusCompleted = "completed"
|
||||
// PollingManualTriggerStatusCancelled 轮询手动触发日志状态-已取消
|
||||
PollingManualTriggerStatusCancelled = "cancelled"
|
||||
)
|
||||
|
||||
@@ -427,3 +427,15 @@ func RedisPollingShardQueueKey(shardID int, taskType string) string {
|
||||
func RedisPollingDeviceOpLockKey(deviceID uint) string {
|
||||
return fmt.Sprintf("polling:device:op_lock:%d", deviceID)
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// 支付配置缓存 Redis Key
|
||||
// ========================================
|
||||
|
||||
// RedisPaymentConfigKey 支付配置缓存 Key
|
||||
// 用途:缓存 WechatConfig 数据(JSON 格式),避免每次支付时重复查询数据库
|
||||
// 格式:payment:config:{configID}
|
||||
// TTL:1 小时
|
||||
func RedisPaymentConfigKey(configID uint) string {
|
||||
return fmt.Sprintf("payment:config:%d", configID)
|
||||
}
|
||||
|
||||
@@ -145,90 +145,3 @@ func RedisAssetWalletBalanceKey(resourceType string, resourceID uint) string {
|
||||
func RedisAssetWalletLockKey(resourceType string, resourceID uint) string {
|
||||
return fmt.Sprintf("asset_wallet:lock:%s:%d", resourceType, resourceID)
|
||||
}
|
||||
|
||||
// ========== 兼容性别名(待清理)==========
|
||||
|
||||
// 以下常量保留用于向后兼容,待旧代码清理后删除
|
||||
|
||||
// WalletTypeMain 主钱包(已废弃,使用 AgentWalletTypeMain)
|
||||
const WalletTypeMain = AgentWalletTypeMain
|
||||
|
||||
// WalletTypeCommission 分佣钱包(已废弃,使用 AgentWalletTypeCommission)
|
||||
const WalletTypeCommission = AgentWalletTypeCommission
|
||||
|
||||
// WalletResourceTypeIotCard 物联网卡钱包(已废弃,使用 AssetWalletResourceTypeIotCard)
|
||||
const WalletResourceTypeIotCard = AssetWalletResourceTypeIotCard
|
||||
|
||||
// WalletResourceTypeDevice 设备钱包(已废弃,使用 AssetWalletResourceTypeDevice)
|
||||
const WalletResourceTypeDevice = AssetWalletResourceTypeDevice
|
||||
|
||||
// WalletResourceTypeShop 店铺钱包(已废弃,代理钱包不再使用 resource_type)
|
||||
const WalletResourceTypeShop = "shop"
|
||||
|
||||
// WalletStatusNormal 钱包状态-正常(已废弃,使用 AgentWalletStatusNormal 或 AssetWalletStatusNormal)
|
||||
const WalletStatusNormal = AgentWalletStatusNormal
|
||||
|
||||
// WalletStatusFrozen 钱包状态-冻结(已废弃,使用 AgentWalletStatusFrozen 或 AssetWalletStatusFrozen)
|
||||
const WalletStatusFrozen = AgentWalletStatusFrozen
|
||||
|
||||
// WalletStatusClosed 钱包状态-关闭(已废弃,使用 AgentWalletStatusClosed 或 AssetWalletStatusClosed)
|
||||
const WalletStatusClosed = AgentWalletStatusClosed
|
||||
|
||||
// TransactionTypeRecharge 交易类型-充值(已废弃,使用 AgentTransactionTypeRecharge 或 AssetTransactionTypeRecharge)
|
||||
const TransactionTypeRecharge = AgentTransactionTypeRecharge
|
||||
|
||||
// TransactionTypeDeduct 交易类型-扣款(已废弃,使用 AgentTransactionTypeDeduct 或 AssetTransactionTypeDeduct)
|
||||
const TransactionTypeDeduct = AgentTransactionTypeDeduct
|
||||
|
||||
// TransactionTypeRefund 交易类型-退款(已废弃,使用 AgentTransactionTypeRefund 或 AssetTransactionTypeRefund)
|
||||
const TransactionTypeRefund = AgentTransactionTypeRefund
|
||||
|
||||
// TransactionTypeCommission 交易类型-分佣(已废弃,使用 AgentTransactionTypeCommission)
|
||||
const TransactionTypeCommission = AgentTransactionTypeCommission
|
||||
|
||||
// TransactionTypeWithdrawal 交易类型-提现(已废弃,使用 AgentTransactionTypeWithdrawal)
|
||||
const TransactionTypeWithdrawal = AgentTransactionTypeWithdrawal
|
||||
|
||||
// RechargeOrderPrefix 充值订单号前缀(已废弃,使用 AgentRechargeOrderPrefix 或 AssetRechargeOrderPrefix)
|
||||
const RechargeOrderPrefix = "RCH"
|
||||
|
||||
// RechargeMinAmount 最小充值金额(已废弃,使用 AgentRechargeMinAmount 或 AssetRechargeMinAmount)
|
||||
const RechargeMinAmount = AssetRechargeMinAmount
|
||||
|
||||
// RechargeMaxAmount 最大充值金额(已废弃,使用 AgentRechargeMaxAmount 或 AssetRechargeMaxAmount)
|
||||
const RechargeMaxAmount = AssetRechargeMaxAmount
|
||||
|
||||
// ========== Card* 废弃别名(向后兼容)==========
|
||||
|
||||
// Deprecated: 使用 AssetWalletResourceTypeIotCard
|
||||
const CardWalletResourceTypeIotCard = AssetWalletResourceTypeIotCard
|
||||
|
||||
// Deprecated: 使用 AssetWalletResourceTypeDevice
|
||||
const CardWalletResourceTypeDevice = AssetWalletResourceTypeDevice
|
||||
|
||||
// Deprecated: 使用 AssetWalletStatusNormal
|
||||
const CardWalletStatusNormal = AssetWalletStatusNormal
|
||||
|
||||
// Deprecated: 使用 AssetWalletStatusFrozen
|
||||
const CardWalletStatusFrozen = AssetWalletStatusFrozen
|
||||
|
||||
// Deprecated: 使用 AssetWalletStatusClosed
|
||||
const CardWalletStatusClosed = AssetWalletStatusClosed
|
||||
|
||||
// Deprecated: 使用 AssetTransactionTypeRecharge
|
||||
const CardTransactionTypeRecharge = AssetTransactionTypeRecharge
|
||||
|
||||
// Deprecated: 使用 AssetTransactionTypeDeduct
|
||||
const CardTransactionTypeDeduct = AssetTransactionTypeDeduct
|
||||
|
||||
// Deprecated: 使用 AssetTransactionTypeRefund
|
||||
const CardTransactionTypeRefund = AssetTransactionTypeRefund
|
||||
|
||||
// Deprecated: 使用 AssetRechargeOrderPrefix
|
||||
const CardRechargeOrderPrefix = AssetRechargeOrderPrefix
|
||||
|
||||
// Deprecated: 使用 AssetRechargeMinAmount
|
||||
const CardRechargeMinAmount = AssetRechargeMinAmount
|
||||
|
||||
// Deprecated: 使用 AssetRechargeMaxAmount
|
||||
const CardRechargeMaxAmount = AssetRechargeMaxAmount
|
||||
|
||||
@@ -87,12 +87,6 @@ func InitPostgreSQL(cfg *config.DatabaseConfig, log *zap.Logger) (*gorm.DB, erro
|
||||
zap.Int("max_idle_conns", maxIdleConns),
|
||||
zap.Duration("conn_max_lifetime", connMaxLifetime))
|
||||
|
||||
// db.AutoMigrate(
|
||||
// &model.Account{},
|
||||
// &model.Role{},
|
||||
// &model.Permission{},
|
||||
// &model.RolePermission{},
|
||||
// )
|
||||
return db, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,6 @@ const (
|
||||
CodeWithdrawalNotFound = 1052 // 提现申请不存在
|
||||
CodeWalletNotFound = 1053 // 钱包不存在
|
||||
CodeInsufficientQuota = 1054 // 额度不足
|
||||
CodeExceedLimit = 1055 // 超过限制
|
||||
|
||||
// IoT 卡相关错误 (1070-1089)
|
||||
CodeIotCardNotFound = 1070 // IoT 卡不存在
|
||||
@@ -224,7 +223,6 @@ var allErrorCodes = []int{
|
||||
CodeWithdrawalNotFound,
|
||||
CodeWalletNotFound,
|
||||
CodeInsufficientQuota,
|
||||
CodeExceedLimit,
|
||||
CodeIotCardNotFound,
|
||||
CodeIotCardBoundToDevice,
|
||||
CodeIotCardStatusNotAllowed,
|
||||
@@ -356,7 +354,6 @@ var errorMessages = map[int]string{
|
||||
CodeWithdrawalNotFound: "提现申请不存在",
|
||||
CodeWalletNotFound: "钱包不存在",
|
||||
CodeInsufficientQuota: "额度不足",
|
||||
CodeExceedLimit: "超过限制",
|
||||
CodeIotCardNotFound: "IoT 卡不存在",
|
||||
CodeIotCardBoundToDevice: "IoT 卡已绑定设备,不能单独操作",
|
||||
CodeIotCardStatusNotAllowed: "卡状态不允许此操作",
|
||||
|
||||
@@ -16,6 +16,7 @@ func BuildDocHandlers() *bootstrap.Handlers {
|
||||
Role: admin.NewRoleHandler(nil, nil),
|
||||
Permission: admin.NewPermissionHandler(nil),
|
||||
PersonalCustomer: app.NewPersonalCustomerHandler(nil, nil),
|
||||
ClientAuth: app.NewClientAuthHandler(nil, nil),
|
||||
ClientAsset: app.NewClientAssetHandler(nil, nil, nil, nil, nil, nil, nil, nil, nil),
|
||||
ClientWallet: app.NewClientWalletHandler(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil),
|
||||
ClientOrder: app.NewClientOrderHandler(nil, nil),
|
||||
@@ -24,6 +25,7 @@ func BuildDocHandlers() *bootstrap.Handlers {
|
||||
ClientDevice: app.NewClientDeviceHandler(nil, nil, nil, nil, nil, nil, nil),
|
||||
Shop: admin.NewShopHandler(nil),
|
||||
ShopRole: admin.NewShopRoleHandler(nil),
|
||||
AdminAuth: admin.NewAuthHandler(nil, nil),
|
||||
ShopCommission: admin.NewShopCommissionHandler(nil),
|
||||
CommissionWithdrawal: admin.NewCommissionWithdrawalHandler(nil, nil),
|
||||
CommissionWithdrawalSetting: admin.NewCommissionWithdrawalSettingHandler(nil),
|
||||
@@ -54,6 +56,7 @@ func BuildDocHandlers() *bootstrap.Handlers {
|
||||
PollingCleanup: admin.NewPollingCleanupHandler(nil),
|
||||
PollingManualTrigger: admin.NewPollingManualTriggerHandler(nil),
|
||||
Asset: admin.NewAssetHandler(nil, nil, nil, nil),
|
||||
AssetLifecycle: admin.NewAssetLifecycleHandler(nil),
|
||||
AssetWallet: admin.NewAssetWalletHandler(nil),
|
||||
WechatConfig: admin.NewWechatConfigHandler(nil),
|
||||
AgentRecharge: admin.NewAgentRechargeHandler(nil, nil),
|
||||
|
||||
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 处理")
|
||||
}
|
||||
@@ -58,14 +58,10 @@ func NewHandler(
|
||||
|
||||
func (h *Handler) RegisterHandlers() *asynq.ServeMux {
|
||||
emailHandler := task.NewEmailHandler(h.redis, h.logger)
|
||||
syncHandler := task.NewSyncHandler(h.db, h.logger)
|
||||
|
||||
h.mux.HandleFunc(constants.TaskTypeEmailSend, emailHandler.HandleEmailSend)
|
||||
h.logger.Info("注册邮件发送任务处理器", zap.String("task_type", constants.TaskTypeEmailSend))
|
||||
|
||||
h.mux.HandleFunc(constants.TaskTypeDataSync, syncHandler.HandleDataSync)
|
||||
h.logger.Info("注册数据同步任务处理器", zap.String("task_type", constants.TaskTypeDataSync))
|
||||
|
||||
h.registerIotCardImportHandler()
|
||||
h.registerDeviceImportHandler()
|
||||
h.registerCommissionStatsHandlers()
|
||||
|
||||
Reference in New Issue
Block a user