From 7dadcd1644aca963c5f071ca8d5de8337f9d2f14 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Mon, 19 Oct 2026 12:09:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(role):=20=E5=A2=9E=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E7=BB=91=E5=AE=9A=E8=A7=92=E8=89=B2=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E4=B8=8E=E4=BD=9C=E6=88=98=E8=A1=A8=E5=B1=95?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 逆向并实现虎牙活动绑角色接口 webActUI.checkUserBindGameAccount,支持精准解析游戏角色名、区服和平台 - 增加本地持久化 roles.json 存储,启动即回显角色信息无需重复请求 - 前端账号作战表与 CK 管理列表整合角色展示([微信-安卓] 角色名),直观区分账号归属 - 工具栏增加「查询角色」按钮,并以兑换记录中的历史账号作为双重兜底 - 升级版本至 v0.5.5 并成功交叉编译 Windows 产物 --- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- frontend/package.json.md5 | 2 +- frontend/src/App.tsx | 201 ++++++++++++++++++--- frontend/wailsjs/go/gui/App.d.ts | 6 + frontend/wailsjs/go/gui/App.js | 12 ++ frontend/wailsjs/go/models.ts | 171 ++++++++++++++++++ internal/gui/app.go | 3 + internal/gui/role.go | 288 +++++++++++++++++++++++++++++++ internal/gui/role_test.go | 43 +++++ internal/huya/activity.go | 214 +++++++++++++++++++++++ internal/huya/bind_test.go | 51 ++++++ internal/huya/client.go | 1 + internal/huya/http_client.go | 18 ++ internal/huya/wss_client.go | 21 +++ internal/score/baseline_test.go | 4 + internal/sniper/sniper_test.go | 18 ++ internal/version/version.go | 2 +- wails.json | 2 +- 19 files changed, 1032 insertions(+), 31 deletions(-) create mode 100644 internal/gui/role.go create mode 100644 internal/gui/role_test.go create mode 100644 internal/huya/bind_test.go diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 618cc02..a7b9f0c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "huya-go-frontend", - "version": "0.5.4", + "version": "0.5.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "huya-go-frontend", - "version": "0.5.4", + "version": "0.5.5", "dependencies": { "antd": "^5.26.2", "lucide-react": "^0.468.0", diff --git a/frontend/package.json b/frontend/package.json index 07e02eb..522cd54 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "huya-go-frontend", "private": true, - "version": "0.5.4", + "version": "0.5.5", "type": "module", "scripts": { "dev": "vite --host 127.0.0.1", diff --git a/frontend/package.json.md5 b/frontend/package.json.md5 index 6d86e5d..bf1da1d 100755 --- a/frontend/package.json.md5 +++ b/frontend/package.json.md5 @@ -1 +1 @@ -e0e233c05b00ef1f530e32412926f966 \ No newline at end of file +a9d40923be4354a749fbbe0c2b9f924e \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4caf972..e5833ea 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -36,12 +36,14 @@ import { Square, Settings, Trash2, + UserCheck, Zap, } from 'lucide-react' import { CheckCookies, DefaultConfig, Exchange, + GetCachedRoles, GetInstanceInfo, GetLogFilePath, GetLogs, @@ -52,6 +54,7 @@ import { ListPrizes, OpenLogDir, PreviewAccounts, + QueryRoles, QueryScores, ReconcileScores, SetLogLevel, @@ -144,6 +147,17 @@ type CookieCheckResult = { checkedAt: number } +type RoleItem = { + uid: number + role_name?: string + area_name?: string + plat_name?: string + display_name?: string + game_name?: string + is_bound?: boolean + updated_at?: number +} + type AccountPreview = { line: number uid: number @@ -153,6 +167,7 @@ type AccountPreview = { checkStatus?: CookieCheckStatus checkMessage?: string checkedAt?: number + role?: RoleItem } type CookieEntry = { @@ -165,6 +180,7 @@ type CookieEntry = { checkMessage?: string checkedAt?: number updatedAt: number + role?: RoleItem } type LogEntry = { @@ -752,6 +768,7 @@ export default function Dashboard() { const [productSearch, setProductSearch] = useState('') const [scores, setScores] = useState([]) const [records, setRecords] = useState([]) + const [rolesByUID, setRolesByUID] = useState>(new Map()) const [panelTab, setPanelTab] = useState('logs') const [recordModalData, setRecordModalData] = useState<{ open: boolean @@ -922,6 +939,20 @@ export default function Dashboard() { if (savedCookies.length > 0) { applyCookieEntries(savedCookies, true, true).catch((err: unknown) => message.error(String(err))) } + + GetCachedRoles() + .then((cachedRoles) => { + if (!cachedRoles) return + const m = new Map() + for (const [k, v] of Object.entries(cachedRoles)) { + const uid = Number(k) + if (uid) m.set(uid, v as RoleItem) + } + setRolesByUID(m) + }) + .catch((err: unknown) => { + console.warn('读取本地角色缓存失败:', err) + }) }) .catch((err: unknown) => message.error(String(err))) @@ -1336,6 +1367,56 @@ export default function Dashboard() { } } + const queryAccountRoles = async () => { + if (running) { + message.warning('抢购运行中,请先停止再查询绑定角色') + return + } + const targetAccounts = + selectedAccountLines.length > 0 + ? accounts.filter((item) => selectedAccountLines.includes(item.line)) + : accounts + if (targetAccounts.length === 0) { + message.warning('没有可查询的账号凭据') + return + } + setLoading('roles') + try { + const values = config() + if (selectedAccountLines.length > 0) { + const lines = targetAccounts + .map((a) => cookieEntries[a.line - 1]?.text || '') + .filter(Boolean) + values.cookieText = lines.join('\n') + } + const results = (await QueryRoles(values)) as RoleItem[] + setRolesByUID((prev) => { + const next = new Map(prev) + results.forEach((r) => { + if (r.uid) { + next.set(Number(r.uid), { + uid: Number(r.uid), + role_name: r.role_name, + area_name: r.area_name, + plat_name: r.plat_name, + display_name: r.display_name, + game_name: r.game_name, + is_bound: r.is_bound, + }) + } + }) + return next + }) + const boundCount = results.filter((r) => r.is_bound && r.display_name).length + message.success(`角色查询完成:${results.length} 个账号,${boundCount} 个已绑定角色`) + } catch (err: unknown) { + message.error(`查询角色失败: ${String(err)}`) + } finally { + setLoading('') + } + } + + const clearExpiredCookies = () => { if (failedCookieCount === 0) return modal.confirm({ @@ -1607,22 +1688,42 @@ export default function Dashboard() { const accountColumns: ColumnsType = useMemo( () => [ { - title: '账号', - width: 130, - render: (_, row) => ( -
- {row.uid || `第${row.line}行`} -
- {isCookieCheckUnusable(row.checkStatus) - ? row.checkMessage || 'CK 已失效' - : row.valid - ? row.checkStatus === 'valid' - ? 'CK 可用' - : '已加入任务' - : row.error || '未选择'} + title: '账号 / 角色', + width: 155, + render: (_, row) => { + const role = rolesByUID.get(row.uid) + return ( +
+ {row.uid || `第${row.line}行`} + {role?.display_name ? ( +
+ + {role.display_name} + +
+ ) : role?.is_bound === false ? ( +
+ + 未绑定角色 + +
+ ) : null} +
+ {isCookieCheckUnusable(row.checkStatus) + ? row.checkMessage || 'CK 已失效' + : row.valid + ? row.checkStatus === 'valid' + ? 'CK 可用' + : '已加入任务' + : row.error || '未选择'} +
-
- ), + ) + }, }, { title: '凭据', @@ -1748,22 +1849,43 @@ export default function Dashboard() { running, scoreMap, watchedName, + rolesByUID, ], ) const cookieColumns: ColumnsType = useMemo( () => [ { - title: '账号', - width: 140, - render: (_, row) => ( -
- {row.uid || '-'} -
- {isCookieCheckUnusable(row.checkStatus) ? 'CK 已失效' : row.valid ? '可解析 UID' : row.error || '待校验'} + title: '账号 / 角色', + width: 155, + render: (_, row) => { + const role = rolesByUID.get(row.uid) + return ( +
+ {row.uid || '-'} + {role?.display_name ? ( +
+ + {role.display_name} + +
+ ) : role?.is_bound === false ? ( +
+ + 未绑定角色 + +
+ ) : null} +
+ {isCookieCheckUnusable(row.checkStatus) ? 'CK 已失效' : row.valid ? '可解析 UID' : row.error || '待校验'} +
-
- ), + ) + }, }, { title: '格式', @@ -1812,7 +1934,7 @@ export default function Dashboard() { ), }, ], - [cookieEntries], + [cookieEntries, rolesByUID], ) const prizeColumns: ColumnsType = useMemo( @@ -2266,6 +2388,14 @@ export default function Dashboard() { > CK 有效性 +