49 lines
1.2 KiB
Go
49 lines
1.2 KiB
Go
package accounts
|
|
|
|
import (
|
|
"hash/fnv"
|
|
|
|
"github.com/wangjia/pay/config"
|
|
)
|
|
|
|
// Weighted 按 Weight 加权选账户,决策源为 hash(OutTradeNo)(确定性、可复现、跨节点一致,
|
|
// 见计划 D1)。Weight<=0 记 1(对齐 model.Account gorm default:1)。retry 换号由
|
|
// Router 的 ExcludeAccounts 过滤驱动,不依赖随机。
|
|
type Weighted struct{}
|
|
|
|
func NewWeighted() *Weighted { return &Weighted{} }
|
|
|
|
func (Weighted) Pick(_ string, candidates []config.AccountConfig, hint PickHint) (config.AccountConfig, error) {
|
|
if len(candidates) == 0 {
|
|
return config.AccountConfig{}, ErrNoAccount
|
|
}
|
|
if hint.OutTradeNo == "" {
|
|
return candidates[0], nil
|
|
}
|
|
var total uint32
|
|
for _, c := range candidates {
|
|
total += weightOf(c)
|
|
}
|
|
if total == 0 { // 理论不达(weightOf>=1),兜底
|
|
return candidates[0], nil
|
|
}
|
|
h := fnv.New32a()
|
|
_, _ = h.Write([]byte(hint.OutTradeNo))
|
|
target := h.Sum32() % total
|
|
var acc uint32
|
|
for _, c := range candidates {
|
|
acc += weightOf(c)
|
|
if target < acc {
|
|
return c, nil
|
|
}
|
|
}
|
|
return candidates[len(candidates)-1], nil // 浮点无关的整数走位,兜底不达
|
|
}
|
|
|
|
func weightOf(c config.AccountConfig) uint32 {
|
|
if c.Weight <= 0 {
|
|
return 1
|
|
}
|
|
return uint32(c.Weight)
|
|
}
|