Files
pay/internal/accounts/router.go
T

70 lines
2.4 KiB
Go

package accounts
import "github.com/wangjia/pay/config"
// Router 是 gateway 面向的 Picker 实现:取候选(EnabledFor)→ 按 hint 排除 → 派发策略。
// 策略实例进程内单例(round_robin 计数器需长存);未配置/未知策略默认 round_robin。
type Router struct {
reg *Registry
strategies map[string]string // channel -> 策略名
byName map[string]Strategy
def Strategy
}
// NewRouter 装配路由。routing:channel→策略名(缺省/未知→round_robin);usage=nil→NopUsage。
// limit_aware 复合 round_robin 作为合格集内的均摊基础策略。
func NewRouter(reg *Registry, routing map[string]string, usage UsageSource) *Router {
rr := NewRoundRobin()
byName := map[string]Strategy{
StrategyRoundRobin: rr,
StrategyWeighted: NewWeighted(),
StrategyLimitAware: NewLimitAware(rr, usage),
}
cp := map[string]string{}
for k, v := range routing {
cp[k] = v
}
return &Router{reg: reg, strategies: cp, byName: byName, def: rr}
}
func (r *Router) strategyFor(channel string) Strategy {
if name, ok := r.strategies[channel]; ok {
if s, ok := r.byName[name]; ok {
return s
}
}
return r.def // 缺省 / 未知策略名 → round_robin(见计划 D3)
}
// Pick 实现 accounts.Picker。
// 注:cands 顺序 = EnabledFor 保留的 config.Accounts 声明顺序,原样传给策略——
// weighted 的桶按候选顺序累加边界(见 weighted.go),故 config.yaml 里账户顺序变了
// 等价于重新分桶(reorder = repin),同一 OutTradeNo 命中的账户可能跟着变,不是 bug。
func (r *Router) Pick(channel, region string, hint PickHint) (config.AccountConfig, error) {
cands := r.reg.EnabledFor(channel, region)
if len(cands) == 0 {
return config.AccountConfig{}, ErrNoAccount
}
if len(hint.ExcludeAccounts) > 0 {
if filtered := excludeAccounts(cands, hint.ExcludeAccounts); len(filtered) > 0 {
cands = filtered // 过滤到空则保留全集:单账户渠道 retry 仍可用(见计划 D4)
}
}
return r.strategyFor(channel).Pick(channel+"|"+region, cands, hint)
}
func excludeAccounts(cands []config.AccountConfig, exclude []string) []config.AccountConfig {
skip := make(map[string]struct{}, len(exclude))
for _, id := range exclude {
skip[id] = struct{}{}
}
out := make([]config.AccountConfig, 0, len(cands))
for _, c := range cands {
if _, ok := skip[c.AccountID]; ok {
continue
}
out = append(out, c)
}
return out
}