v0.4.0:窗口调度/限流分类/可观测性,抢购改每账号独立循环,移除 CLI 只保留 GUI
This commit is contained in:
+29
-7
@@ -97,9 +97,12 @@ type UiConfig = {
|
||||
minLeft: number
|
||||
cookieText: string
|
||||
selectedAccountLines: number[]
|
||||
// loops 是轮次上限:0(留空)表示按窗口运行到窗口结束。
|
||||
loops: number
|
||||
delayMs: number
|
||||
concurrency: number
|
||||
// windowSeconds 是抢购窗口秒数(窗口模式下跑到「开抢+窗口」才收工)。
|
||||
windowSeconds: number
|
||||
stopAfterSuccess: boolean
|
||||
stopAccountAfterSuccess: boolean
|
||||
prewarm: boolean
|
||||
@@ -114,6 +117,10 @@ type UiConfig = {
|
||||
errorBackoffMs: number
|
||||
timeoutSec: number
|
||||
startAt: string
|
||||
// adaptiveThrottle 是「全局自适应降速」开关(表单用正语义)。
|
||||
adaptiveThrottle: boolean
|
||||
// disableAdaptiveThrottle 只发给后端(后端零值=开启,所以用反语义字段),不落本地存储。
|
||||
disableAdaptiveThrottle: boolean
|
||||
}
|
||||
|
||||
type AccountPreview = {
|
||||
@@ -301,7 +308,7 @@ function exchangeOrderID(exchange?: ExchangeResp): string {
|
||||
return exchange?.order_id || exchange?.orderId || ''
|
||||
}
|
||||
|
||||
type SavedConfig = Omit<UiConfig, 'cookieText' | 'selectedAccountLines'>
|
||||
type SavedConfig = Omit<UiConfig, 'cookieText' | 'selectedAccountLines' | 'disableAdaptiveThrottle'>
|
||||
|
||||
type ScheduleCountdown = {
|
||||
targetAt: number
|
||||
@@ -466,6 +473,10 @@ function categoryTag(category?: string): ReactNode {
|
||||
condition: { color: 'geekblue', label: '条件' },
|
||||
failed: { color: 'warning', label: '失败' },
|
||||
error: { color: 'error', label: '异常' },
|
||||
// protocol 是回包结构异常(缺 tRsp/tResp),单独归类便于排查服务端错误信封。
|
||||
protocol: { color: 'error', label: '协议异常' },
|
||||
// session_lost 是「账号未绑定」这类瞬时会话失效,通常下一轮就能恢复。
|
||||
session_lost: { color: 'volcano', label: '会话失效' },
|
||||
}
|
||||
if (!category) return <Tag>待处理</Tag>
|
||||
const item = map[category] || { color: 'default', label: category }
|
||||
@@ -583,9 +594,10 @@ function pickConfig(values: Partial<UiConfig>): UiConfig {
|
||||
minLeft: Number(values.minLeft || 0),
|
||||
cookieText: values.cookieText || '',
|
||||
selectedAccountLines: values.selectedAccountLines || [],
|
||||
loops: Number(values.loops || 1),
|
||||
loops: Number(values.loops || 0),
|
||||
delayMs: Number(values.delayMs || 150),
|
||||
concurrency: Number(values.concurrency || 1),
|
||||
windowSeconds: Number(values.windowSeconds || 30),
|
||||
stopAfterSuccess: Boolean(values.stopAfterSuccess),
|
||||
stopAccountAfterSuccess: values.stopAccountAfterSuccess !== false,
|
||||
prewarm: values.prewarm !== false,
|
||||
@@ -599,6 +611,8 @@ function pickConfig(values: Partial<UiConfig>): UiConfig {
|
||||
errorBackoffMs: Number(values.errorBackoffMs || 300),
|
||||
timeoutSec: Number(values.timeoutSec || 15),
|
||||
startAt: values.startAt || '',
|
||||
adaptiveThrottle: values.adaptiveThrottle !== false,
|
||||
disableAdaptiveThrottle: values.adaptiveThrottle === false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -857,7 +871,7 @@ export default function Dashboard() {
|
||||
const errorCount = useMemo(
|
||||
() =>
|
||||
results.filter((item) =>
|
||||
['error', 'failed', 'not_enough', 'off_shelf', 'sold_out', 'condition'].includes(
|
||||
['error', 'failed', 'not_enough', 'off_shelf', 'sold_out', 'condition', 'protocol'].includes(
|
||||
item.category || '',
|
||||
),
|
||||
).length,
|
||||
@@ -1134,6 +1148,7 @@ export default function Dashboard() {
|
||||
loops: values.loops,
|
||||
delayMs: values.delayMs,
|
||||
concurrency: values.concurrency,
|
||||
windowSeconds: values.windowSeconds,
|
||||
stopAfterSuccess: values.stopAfterSuccess,
|
||||
stopAccountAfterSuccess: values.stopAccountAfterSuccess,
|
||||
prewarm: values.prewarm,
|
||||
@@ -1147,6 +1162,7 @@ export default function Dashboard() {
|
||||
errorBackoffMs: values.errorBackoffMs,
|
||||
timeoutSec: values.timeoutSec,
|
||||
startAt: values.startAt,
|
||||
adaptiveThrottle: values.adaptiveThrottle,
|
||||
}
|
||||
localStorage.setItem(scopedKey(configStorageBase), JSON.stringify(saved))
|
||||
message.success('已保存')
|
||||
@@ -1966,8 +1982,11 @@ export default function Dashboard() {
|
||||
<Form.Item name="minLeft" label="最少库存">
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item name="loops" label="次数">
|
||||
<InputNumber min={1} max={9999} />
|
||||
<Form.Item name="loops" label="次数上限" tooltip="留空或 0 = 按窗口运行到窗口结束;填了就作为轮次上限">
|
||||
<InputNumber min={0} max={9999} placeholder="0=按窗口" />
|
||||
</Form.Item>
|
||||
<Form.Item name="windowSeconds" label="窗口秒" tooltip="窗口模式下跑到「开抢 + 窗口秒数」才收工;开抢后约 9 秒内库存就会见底,默认 30 秒留足库存回流余量">
|
||||
<InputNumber min={1} max={600} addonAfter="s" />
|
||||
</Form.Item>
|
||||
<Form.Item name="delayMs" label="延时">
|
||||
<InputNumber min={10} max={60000} addonAfter="ms" />
|
||||
@@ -1975,8 +1994,8 @@ export default function Dashboard() {
|
||||
<Form.Item name="concurrency" label="线程">
|
||||
<InputNumber min={1} max={100} />
|
||||
</Form.Item>
|
||||
<Form.Item name="leadMs" label="提前">
|
||||
<InputNumber min={0} max={10000} addonAfter="ms" />
|
||||
<Form.Item name="leadMs" label="提前" tooltip="留空或 0 = 按服务端时钟校准误差动态计算(通常几十毫秒);填了就用固定提前量">
|
||||
<InputNumber min={0} max={10000} addonAfter="ms" placeholder="0=动态" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div className="settings-switch-grid">
|
||||
@@ -2001,6 +2020,9 @@ export default function Dashboard() {
|
||||
<Form.Item name="timeSync" label="时间校准" valuePropName="checked" tooltip="定时前用 pingSignal 校准服务端时间,按钟差对齐触发时刻">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Form.Item name="adaptiveThrottle" label="自适应降速" valuePropName="checked" tooltip="连续十几个请求都拿不到业务响应时整体降速,缓解同机同 IP 的限流风暴">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
Reference in New Issue
Block a user