补全过滤入口与直接兑换,优化兑换分类和 WSS 建连

- GUI 接线 Exchange 绑定:新增「直接兑换」按钮,结果带 category/message 并进入结果面板
- 补 -max-price/-min-left(CLI 与 GUI 设置),Target 的价格与库存过滤不再悬空
- 删除死代码 sendUpdateUserInfo 及其请求结构体(浏览器流程不含该帧)
- ClassifyExchange 改为多路判定:合并 msg/actPreCondition/exchangeInfo 文案,status=200 且有 orderId 视为成功,新增 condition 分类(precondition 门禁)
- WSS 建连改为锁外 dial、锁内注册,同 uid 并发调用共享一次握手
- 默认传输层统一为 http(CLI/GUI/README),.gitignore 忽略 .DS_Store
- 同步 Wails 依赖升级(2.12.0 -> 2.15.0)并重新生成前端绑定
This commit is contained in:
yml2213
2026-10-13 19:57:08 +08:00
parent 72d59670d4
commit e8bb4eb571
13 changed files with 411 additions and 120 deletions
+63 -3
View File
@@ -31,9 +31,11 @@ import {
Square,
Settings,
Trash2,
Zap,
} from 'lucide-react'
import {
DefaultConfig,
Exchange,
GetLogs,
GetRecords,
IsRunning,
@@ -56,6 +58,8 @@ type UiConfig = {
pid: number
name: string
targetText: string
maxPrice: number
minLeft: number
cookieText: string
selectedAccountLines: number[]
loops: number
@@ -167,6 +171,14 @@ type ExchangeResp = {
actPreCondition?: ActPreCondition
}
type ExchangeResult = {
uid: number
resp?: ExchangeResp
category?: string
message?: string
error?: string
}
type MonitorResult = {
key?: string
loop?: number
@@ -300,6 +312,7 @@ function categoryTag(category?: string): ReactNode {
already_done: { color: 'purple', label: '已兑' },
off_shelf: { color: 'error', label: '下架' },
sold_out: { color: 'error', label: '抢完' },
condition: { color: 'geekblue', label: '条件' },
failed: { color: 'warning', label: '失败' },
error: { color: 'error', label: '异常' },
}
@@ -399,12 +412,14 @@ function newCookieID(): string {
function pickConfig(values: Partial<UiConfig>): UiConfig {
return {
transport: values.transport || 'wss',
transport: values.transport || 'http',
wssHost: values.wssHost || '',
sid: Number(values.sid || 0),
pid: Number(values.pid || 0),
name: values.name || '',
targetText: values.targetText || '',
maxPrice: Number(values.maxPrice || 0),
minLeft: Number(values.minLeft || 0),
cookieText: values.cookieText || '',
selectedAccountLines: values.selectedAccountLines || [],
loops: Number(values.loops || 1),
@@ -596,7 +611,9 @@ export default function Dashboard() {
const errorCount = useMemo(
() =>
results.filter((item) =>
['error', 'failed', 'not_enough', 'off_shelf', 'sold_out'].includes(item.category || ''),
['error', 'failed', 'not_enough', 'off_shelf', 'sold_out', 'condition'].includes(
item.category || '',
),
).length,
[results],
)
@@ -827,6 +844,8 @@ export default function Dashboard() {
pid: values.pid,
name: values.name,
targetText: values.targetText,
maxPrice: values.maxPrice,
minLeft: values.minLeft,
loops: values.loops,
delayMs: values.delayMs,
concurrency: values.concurrency,
@@ -881,6 +900,33 @@ export default function Dashboard() {
})
}
const directExchange = () => {
const values = config()
if (Number(values.pid || 0) <= 0) {
message.warning('请先在左侧商品列表中选择要兑换的商品')
return undefined
}
return runAction(
'exchange',
() => Exchange(values),
(data) => {
const items = (data || []) as ExchangeResult[]
setResults(
items.map((item, index) => ({
key: `exchange-${item.uid}-${index}-${Date.now()}`,
uid: item.uid,
action: 'exchange',
category: item.category || (item.error ? 'error' : 'failed'),
message: item.message || item.error || item.resp?.msg || '',
exchange: item.resp,
})),
)
const success = items.filter((item) => item.category === 'success').length
message.info(`直接兑换:成功 ${success}/${items.length}`)
},
)
}
const selectAllAccounts = () => {
setSelectedAccountLines(validAccounts.map((item) => item.line))
}
@@ -1180,7 +1226,7 @@ export default function Dashboard() {
</div>
<CountdownBadge countdown={scheduleCountdown} now={now} running={running} />
<Space size={12}>
<Tag>{String(watchedTransport || 'wss').toUpperCase()}</Tag>
<Tag>{String(watchedTransport || 'http').toUpperCase()}</Tag>
<Button icon={<Settings size={16} />} onClick={() => setSettingsOpen(true)}>
设置
</Button>
@@ -1225,6 +1271,14 @@ export default function Dashboard() {
>
开启定时
</Button>
<Button
icon={<Zap size={16} />}
loading={loading === 'exchange'}
disabled={running || validAccounts.length === 0}
onClick={directExchange}
>
直接兑换
</Button>
<Button
icon={<ClipboardPaste size={16} />}
loading={loading === 'clipboard'}
@@ -1456,6 +1510,12 @@ export default function Dashboard() {
<Form.Item name="pid" label="PID">
<InputNumber min={0} />
</Form.Item>
<Form.Item name="maxPrice" label="最高积分">
<InputNumber min={0} />
</Form.Item>
<Form.Item name="minLeft" label="最少库存">
<InputNumber min={0} />
</Form.Item>
<Form.Item name="loops" label="次数">
<InputNumber min={1} max={9999} />
</Form.Item>