- 默认并发调优至 30:匹配单实例 30 账号最佳配置,配合 300ms 首发错峰使 30 账号全员在 0~300ms 免排队出击,5 实例多开整机在途约 150 - 前后端默认值与平滑迁移:对齐 Concurrency 30、DelayMS 120、WindowSeconds 30、SID 2203 等,旧配置自动升级 - 测试验证:全套 Go 单元测试通过,前端 Vite 构建成功
89 lines
2.6 KiB
Go
89 lines
2.6 KiB
Go
package sniper
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func normalizeConfig(cfg Config) Config {
|
|
cfg.Category = strings.TrimSpace(cfg.Category)
|
|
if cfg.maxAttempts <= 0 && cfg.OpenAt.IsZero() {
|
|
// 非窗口模式必须有个有限轮次,否则会变成死循环。
|
|
cfg.maxAttempts = 1
|
|
}
|
|
// 窗口模式由 deadline 兜底,maxAttempts<=0 表示一直尝试到窗口结束。
|
|
if cfg.Delay <= 0 {
|
|
cfg.Delay = 120 * time.Millisecond
|
|
}
|
|
if cfg.Concurrency <= 0 {
|
|
cfg.Concurrency = 30
|
|
}
|
|
if cfg.TooFrequentBackoff <= 0 {
|
|
cfg.TooFrequentBackoff = maxDuration(cfg.Delay*2, defaultRetryBackoff)
|
|
}
|
|
if cfg.ErrorBackoff <= 0 {
|
|
cfg.ErrorBackoff = maxDuration(cfg.Delay, defaultRetryBackoff)
|
|
}
|
|
// 首发错峰给了 spread 就至少要有 1ms 步长,否则等于没启用。
|
|
if cfg.FirstWaveSpread > 0 && cfg.FirstWaveMaxStep <= 0 {
|
|
cfg.FirstWaveMaxStep = DefaultFirstWaveMaxStep
|
|
}
|
|
// 积分不足是终态:积分不会自己变多,继续打只是浪费请求,默认停手。
|
|
if !cfg.ContinueOnNotEnough {
|
|
cfg.StopOnNotEnough = true
|
|
}
|
|
// 售罄刻意不给默认值:库存可能因别人取消 / 锁单过期而回流,默认继续探测到
|
|
// 窗口结束;需要「售罄即停」时显式设置 StopOnSoldOut。
|
|
if !cfg.ListBeforeExchange {
|
|
cfg.DirectExchange = true
|
|
}
|
|
return cfg
|
|
}
|
|
|
|
// normalizeTargets 去掉空目标与重复目标。
|
|
//
|
|
// 除了完全相同的 target,还按 pid / 名称去重:界面选品会同时提交 pid、名称,
|
|
// 以及 targetText 里的同一个 pid,不去重就会对同一奖品重复兑换(实测踩过)。
|
|
func normalizeTargets(targets []Target) []Target {
|
|
out := make([]Target, 0, len(targets))
|
|
seenKey := map[string]bool{}
|
|
seenPID := map[int32]bool{}
|
|
seenName := map[string]bool{}
|
|
for _, target := range targets {
|
|
target.Name = strings.TrimSpace(target.Name)
|
|
if target.PID <= 0 && target.Name == "" && !target.Auto {
|
|
continue
|
|
}
|
|
if target.PID > 0 && seenPID[target.PID] {
|
|
continue
|
|
}
|
|
if target.Name != "" && seenName[target.Name] {
|
|
continue
|
|
}
|
|
key := targetKey(target)
|
|
if seenKey[key] {
|
|
continue
|
|
}
|
|
seenKey[key] = true
|
|
if target.PID > 0 {
|
|
seenPID[target.PID] = true
|
|
}
|
|
if target.Name != "" {
|
|
seenName[target.Name] = true
|
|
}
|
|
out = append(out, target)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func shouldDirectExchange(target Target, cfg Config) bool {
|
|
// 设置了价格/库存门槛时必须先查列表,否则直兑会绕过过滤条件,
|
|
// 商品临时涨价或库存不足时仍会直接下单。
|
|
return cfg.DirectExchange &&
|
|
!cfg.ListBeforeExchange &&
|
|
target.PID > 0 &&
|
|
!target.Auto &&
|
|
target.MaxPrice <= 0 &&
|
|
target.MinLeft <= 0
|
|
}
|