package channel import ( "fmt" "sync" "gorm.io/gorm" "github.com/wangjia/pay/internal/model" ) // Registry 按商户缓存已构造的渠道实例,避免每次下单都重新加载密钥。 type Registry struct { db *gorm.DB mu sync.RWMutex cache map[uint64]Channel } func NewRegistry(db *gorm.DB) *Registry { return &Registry{db: db, cache: make(map[uint64]Channel)} } // ByMerchantID 取(或构造)指定商户的渠道。 func (r *Registry) ByMerchantID(id uint64) (Channel, *model.Merchant, error) { var m model.Merchant if err := r.db.First(&m, "id = ? AND enabled = ?", id, true).Error; err != nil { return nil, nil, fmt.Errorf("商户不存在或已停用: %w", err) } ch, err := r.get(&m) return ch, &m, err } // AlipayByAppID 异步回调时用 app_id 反查商户并取其渠道(用于验签)。 func (r *Registry) AlipayByAppID(appID string) (Channel, *model.Merchant, error) { if appID == "" { return nil, nil, fmt.Errorf("回调缺少 app_id") } var m model.Merchant if err := r.db.First(&m, "channel = ? AND app_id = ? AND enabled = ?", "alipay", appID, true).Error; err != nil { return nil, nil, fmt.Errorf("找不到 app_id=%s 对应的支付宝商户: %w", appID, err) } ch, err := r.get(&m) return ch, &m, err } func (r *Registry) get(m *model.Merchant) (Channel, error) { r.mu.RLock() if ch, ok := r.cache[m.ID]; ok { r.mu.RUnlock() return ch, nil } r.mu.RUnlock() ch, err := Build(m) if err != nil { return nil, err } r.mu.Lock() r.cache[m.ID] = ch r.mu.Unlock() return ch, nil } // Invalidate 商户凭证变更后清缓存(预留给管理接口)。 func (r *Registry) Invalidate(id uint64) { r.mu.Lock() delete(r.cache, id) r.mu.Unlock() }