优化抢购逻辑:移除手动提前量、冷却累计停止、默认包数2

- 移除用户手动提前量设置,改为固定 100ms 内部提前量
- 新增冷却累计停止:单账号冷却 ≥5 次自动放弃
- 默认发包数从 1 改为 2
- 前端移除提前量输入框和 localStorage 存取
This commit is contained in:
yml2213
2026-06-27 19:07:02 +08:00
parent 76c942f10d
commit 0f5e85d94e
5 changed files with 20 additions and 33 deletions
@@ -7,12 +7,11 @@ const { scheduleActive, startNow, startSchedule, cancelSchedule, restoreSchedule
const { reloadAccounts, queryAllScore } = useAccounts()
// 从本地缓存恢复定时设置
const { savedTime, savedAdv } = restoreSchedule()
const { savedTime } = restoreSchedule()
const targetTime = ref(savedTime || '18:00:00')
const advance = ref(savedAdv)
const handleSchedule = () => {
startSchedule(targetTime.value, advance.value)
startSchedule(targetTime.value)
}
</script>
@@ -47,17 +46,6 @@ const handleSchedule = () => {
step="1"
>
</div>
<div class="schedule-item">
<label>提前</label>
<input
v-model.number="advance"
type="number"
min="0"
max="5000"
step="100"
>
<span style="color: var(--muted); font-size: 11.5px">ms</span>
</div>
</div>
</div>
</template>
+4 -8
View File
@@ -47,7 +47,7 @@ export function useScheduler() {
}
}
const startSchedule = async (targetTime: string, advance: number) => {
const startSchedule = async (targetTime: string) => {
const ap = buildAccountProducts(selectedProductId.value || '')
if (!ap) return
@@ -72,13 +72,12 @@ export function useScheduler() {
clearLocalResults()
markSelectedPending('定时配置中,等待后端确认')
setRunState('定时配置中')
addLocalLog(`设置定时: ${fullTime}, 提前=${advance}ms`, 'i')
addLocalLog(`设置定时: ${fullTime}`, 'i')
try {
const d = await App.SetSchedule({
accountProducts: ap,
targetTime: fullTime,
advance
targetTime: fullTime
})
if (d?.success === false) {
throw new Error(d.error || '定时设置失败')
@@ -86,7 +85,6 @@ export function useScheduler() {
scheduleActive.value = true
localStorage.setItem('targetTime', timeStr)
localStorage.setItem('advance', String(advance))
markSelectedPending(`定时等待触发: ${d.triggerTime || fullTime}`)
setRunState(`定时已开启,触发 ${d.triggerTime}`)
@@ -113,7 +111,6 @@ export function useScheduler() {
scheduleActive.value = false
localStorage.removeItem('targetTime')
localStorage.removeItem('advance')
stopCountdown()
setRunState('定时已停止')
showToast('定时已停止', '抢购任务已取消', 'warn')
@@ -153,8 +150,7 @@ export function useScheduler() {
const restoreSchedule = () => {
const savedTime = localStorage.getItem('targetTime')
const savedAdv = localStorage.getItem('advance')
return { savedTime, savedAdv: savedAdv ? parseInt(savedAdv) : 300 }
return { savedTime }
}
return {
+1 -1
View File
@@ -25,7 +25,7 @@ const (
BindInfoURL = BaseURL + "/japi/carnivalApi/v2/tencent/bindInfo?actAlias=" + ActAlias
ScoreURL = BaseURL + "/japi/oms/web/peace/credit/balance"
UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36"
DefaultAttempts = 1
DefaultAttempts = 2
DefaultConcurrency = 150
ProductCacheTTL = 30 * time.Second
+13 -9
View File
@@ -33,6 +33,8 @@ const (
seckillMaxDuration = 75 * time.Second
behaviorCooldown = 5 * time.Second
clickFastCooldown = 3 * time.Second
internalAdvance = 100 * time.Millisecond
maxCooldownCount = 5
)
type roundOutcome struct {
@@ -386,6 +388,7 @@ func (s *Scheduler) RunNow(accountProducts map[string]string) {
stoppedAccounts := make(map[string]string)
stoppedProducts := make(map[string]string)
cooldownAccounts := make(map[string]time.Time)
cooldownCount := make(map[string]int)
totalAccounts := uniqueAttemptAccounts(allAttempts)
totalAttempts := 0
startTime := time.Now()
@@ -463,8 +466,13 @@ func (s *Scheduler) RunNow(accountProducts map[string]string) {
for account, until := range outcome.cooldownAccounts {
if until.After(cooldownAccounts[account]) {
cooldownAccounts[account] = until
logger.LogAccountEvent("抢购", "warn", account, "账号临时降频: 账号=%s 冷却至=%s",
account, until.In(constants.CST).Format("15:04:05.000"))
cooldownCount[account]++
logger.LogAccountEvent("抢购", "warn", account, "账号临时降频: 账号=%s 冷却至=%s (第%d次)",
account, until.In(constants.CST).Format("15:04:05.000"), cooldownCount[account])
if cooldownCount[account] >= maxCooldownCount {
stoppedAccounts[account] = "冷却次数过多"
logger.LogAccountEvent("抢购", "warn", account, "账号停止重试: 账号=%s 原因=冷却次数过多(%d次)", account, cooldownCount[account])
}
}
}
@@ -1092,8 +1100,7 @@ func (s *Scheduler) ScheduleRun(req types.SeckillRequest) (map[string]any, error
calibration := s.calibrateDouyuTime("定时")
serverTime := calibration.EstimatedServerNow()
advance := time.Duration(req.Advance) * time.Millisecond
triggerTime := targetTime.Add(-advance)
triggerTime := targetTime.Add(-internalAdvance)
remain := triggerTime.Sub(serverTime)
if remain <= 0 {
return nil, errors.New("目标时间已过")
@@ -1127,7 +1134,7 @@ func (s *Scheduler) ScheduleRun(req types.SeckillRequest) (map[string]any, error
logger.LogEvent("定时", "success", "定时已设置: 目标=%s 触发=%s 提前=%dms 斗鱼估算=%s 本地偏移=%dms 误差<=%dms RTT=%dms 预热=%s 复校=%s",
targetTime.In(constants.CST).Format("2006-01-02 15:04:05.000"),
triggerTime.In(constants.CST).Format("2006-01-02 15:04:05.000"),
req.Advance,
internalAdvance.Milliseconds(),
serverTime.In(constants.CST).Format("2006-01-02 15:04:05.000"),
calibration.Offset.Milliseconds(),
calibration.ErrorBound.Milliseconds(),
@@ -1139,10 +1146,7 @@ func (s *Scheduler) ScheduleRun(req types.SeckillRequest) (map[string]any, error
return map[string]any{
"success": true,
"targetTime": targetTime.In(constants.CST).Format("2006-01-02 15:04:05"),
"triggerTime": triggerTime.In(constants.CST).Format("2006-01-02 15:04:05.000"),
"advance": req.Advance,
"remainSecond": int(remain.Seconds()),
"serverTime": serverTime.In(constants.CST).Format("2006-01-02 15:04:05.000"),
"triggerTime": triggerTime.In(constants.CST).Format("2006-01-02 15:04:05"),
"offsetMs": calibration.Offset.Milliseconds(),
"errorBoundMs": calibration.ErrorBound.Milliseconds(),
"rttMs": calibration.RTT.Milliseconds(),
-1
View File
@@ -90,7 +90,6 @@ type AccountView struct {
type SeckillRequest struct {
AccountProducts map[string]string `json:"accountProducts"`
TargetTime string `json:"targetTime"`
Advance int `json:"advance"`
}
// APIResponse 斗鱼 API 通用响应。