Files
dygou_go/frontend/src/composables/useAccounts.ts
T

429 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { ref } from 'vue'
import { useToast } from './useToast'
import {
GetAccounts,
QueryXiaodianAssets,
QueryXiaodianRoles,
GetXiaodianBindQRCode,
BindXiaodianRole,
DeleteAccounts,
QueryScore,
ReloadAccounts,
VerifyAllExchange,
VerifyExchange
} from '@wails/go/app/App'
export interface Account {
index: number
name: string
cookie: string
}
export interface AccountStatus {
ok?: boolean
pending?: boolean
message?: string
}
const accounts = ref<Account[]>([])
const selectedIndices = ref<Set<number>>(new Set())
const statusMap = ref<Record<string, AccountStatus | boolean>>({})
const scoreMap = ref<Record<string, string>>({})
const accountProductMap = ref<Record<string, string>>({})
export interface XiaodianAssets {
success: boolean
coupons: number
fragments: number
error?: string
}
const xiaodianAssetsMap = ref<Record<string, XiaodianAssets>>({})
export interface XiaodianRole {
success: boolean
roleId?: string
roleName?: string
openId?: string
type?: string
platId?: number
areaId?: number
error?: string
}
export interface XiaodianQRCode {
success: boolean
accountName?: string
url?: string
gameRoleBindUrl?: string
error?: string
}
const xiaodianRolesMap = ref<Record<string, XiaodianRole>>({})
const xiaodianQRCode = ref<XiaodianQRCode | null>(null)
export function useAccounts() {
const { showToast } = useToast()
const loadAccounts = async () => {
const data = await GetAccounts()
accounts.value = data || []
// 清理无效选择
selectedIndices.value = new Set(
[...selectedIndices.value].filter(i => i >= 0 && i < accounts.value.length)
)
saveSelection()
}
const reloadAccounts = async () => {
const data = await ReloadAccounts()
await loadAccounts()
showToast('账号已刷新', `共 ${data.count} 个账号`, 'success')
}
const deleteAccount = async (accountName: string) => {
if (!accountName) return false
if (!window.confirm(`确定删除账号「${accountName}」及其 CK 吗?`)) return false
try {
const result = await DeleteAccounts([accountName])
if (!result.success) {
showToast('删除失败', result.error || '账号文件未更新', 'error')
return false
}
selectedIndices.value = new Set()
saveSelection()
delete statusMap.value[accountName]
delete accountProductMap.value[accountName]
delete xiaodianAssetsMap.value[accountName]
delete xiaodianRolesMap.value[accountName]
localStorage.setItem('statusMap', JSON.stringify(statusMap.value))
localStorage.setItem('accountProducts', JSON.stringify(accountProductMap.value))
await loadAccounts()
showToast('删除成功', `已删除 ${accountName} 及其 CK`, 'success')
return true
} catch (e: any) {
showToast('删除失败', e?.message || '', 'error')
return false
}
}
const toggleAccount = (index: number) => {
if (selectedIndices.value.has(index)) {
selectedIndices.value.delete(index)
} else {
selectedIndices.value.add(index)
}
saveSelection()
}
const toggleAll = () => {
if (selectedIndices.value.size === accounts.value.length) {
selectedIndices.value.clear()
} else {
accounts.value.forEach((_, i) => selectedIndices.value.add(i))
}
saveSelection()
}
const clearSelection = () => {
selectedIndices.value = new Set()
saveSelection()
}
const saveSelection = () => {
localStorage.setItem('selAccs', JSON.stringify([...selectedIndices.value]))
}
const restoreSelection = () => {
const saved = localStorage.getItem('selAccs')
if (saved) {
try {
selectedIndices.value = new Set(JSON.parse(saved))
} catch {}
}
const savedStatus = localStorage.getItem('statusMap')
if (savedStatus) {
try {
statusMap.value = JSON.parse(savedStatus)
} catch {}
}
const savedProducts = localStorage.getItem('accountProducts')
if (savedProducts) {
try {
accountProductMap.value = JSON.parse(savedProducts)
} catch {}
}
}
const queryAllScore = async () => {
showToast('查询积分', '正在并发查询...', 'info')
const tasks = accounts.value.map(async (a) => {
try {
const d = await QueryScore(a.name)
const score = d.score ?? 'N/A'
scoreMap.value[a.name] = String(score)
localStorage.setItem('accScore_' + a.name, String(score))
} catch {}
})
await Promise.all(tasks)
showToast('积分查询完成', '', 'success')
}
const queryXiaodianAssets = async (accountNames: string[], automatic = false) => {
if (accountNames.length === 0) {
if (!automatic) showToast('查询点券和碎片', '请先勾选要查询的账号', 'warn')
return false
}
showToast(automatic ? '抢购完成' : '查询点券和碎片', automatic ? '正在刷新本次抢购账号的资产...' : '正在查询账号资产...', 'info')
try {
const data = await QueryXiaodianAssets(accountNames)
const queriedResults = data?.results || {}
xiaodianAssetsMap.value = {
...xiaodianAssetsMap.value,
...queriedResults
}
const successCount = Object.values(queriedResults).filter(item => item.success).length
showToast(automatic ? '抢购后资产已刷新' : '资产查询完成', `成功 ${successCount}/${accountNames.length} 个账号`, successCount > 0 ? 'success' : 'warn')
return successCount > 0
} catch (e: any) {
showToast('资产查询失败', e?.message || '', 'error')
return false
}
}
const queryAllXiaodianAssets = async () => queryXiaodianAssets(selectedAccountNames())
const queryAllXiaodianRoles = async () => {
const accountNames = selectedAccountNames()
if (accountNames.length === 0) {
showToast('查询角色信息', '请先勾选要查询的账号', 'warn')
return
}
showToast('查询角色信息', '正在查询小店绑定角色...', 'info')
try {
const data = await QueryXiaodianRoles(accountNames)
const queriedResults = data?.results || {}
xiaodianRolesMap.value = {
...xiaodianRolesMap.value,
...queriedResults
}
const successCount = Object.values(queriedResults).filter(item => item.success).length
showToast('角色查询完成', `成功 ${successCount}/${accountNames.length} 个账号`, successCount > 0 ? 'success' : 'warn')
} catch (e: any) {
showToast('角色查询失败', e?.message || '', 'error')
}
}
const getXiaodianRole = (name: string): XiaodianRole | undefined => xiaodianRolesMap.value[name]
const getXiaodianBindQRCode = async (accountName: string) => {
if (!accountName) {
showToast('获取二维码', '请先选择一个账号', 'warn')
return false
}
try {
const result = await GetXiaodianBindQRCode(accountName)
if (!result?.success || !result.url) {
xiaodianQRCode.value = null
showToast('获取二维码失败', result?.error || '接口未返回二维码链接', 'error')
return false
}
xiaodianQRCode.value = result
return true
} catch (e: any) {
showToast('获取二维码失败', e?.message || '', 'error')
return false
}
}
const bindXiaodianRole = async () => {
const accountName = xiaodianQRCode.value?.accountName || ''
if (!accountName) return false
try {
const result = await BindXiaodianRole(accountName)
xiaodianRolesMap.value = { ...xiaodianRolesMap.value, [accountName]: result }
if (!result?.success || !result.roleName) {
showToast('绑定失败', result?.error || '尚未检测到绑定角色,请先扫码', 'warn')
return false
}
showToast('绑定成功', `${accountName}:${result.roleName}`, 'success')
return true
} catch (e: any) {
showToast('绑定失败', e?.message || '', 'error')
return false
}
}
const closeXiaodianQRCode = () => {
xiaodianQRCode.value = null
}
const getScore = (name: string): string => {
return scoreMap.value[name] || localStorage.getItem('accScore_' + name) || '-'
}
const getXiaodianAssets = (name: string): XiaodianAssets | undefined => xiaodianAssetsMap.value[name]
const resetStatus = () => {
statusMap.value = {}
localStorage.removeItem('statusMap')
}
const setStatuses = (statuses: Record<string, AccountStatus | boolean>) => {
statusMap.value = statuses
if (Object.keys(statusMap.value).length > 0) {
localStorage.setItem('statusMap', JSON.stringify(statusMap.value))
} else {
localStorage.removeItem('statusMap')
}
}
const mergeStatuses = (statuses: Record<string, AccountStatus | boolean>) => {
setStatuses({
...statusMap.value,
...statuses
})
}
const setStatus = (name: string, status: AccountStatus | boolean) => {
mergeStatuses({ [name]: status })
}
const selectedAccountNames = (): string[] => {
return [...selectedIndices.value]
.map(i => accounts.value[i]?.name)
.filter(Boolean) as string[]
}
const markSelectedStatus = (status: AccountStatus | boolean) => {
const next: Record<string, AccountStatus | boolean> = {}
selectedAccountNames().forEach(name => {
next[name] = typeof status === 'object' ? { ...status } : status
})
mergeStatuses(next)
}
const markSelectedPending = (message = '抢购中,等待后端结果') => {
markSelectedStatus({ pending: true, message })
}
const setAccountProduct = (accountName: string, productId: string) => {
if (productId) {
accountProductMap.value[accountName] = productId
} else {
delete accountProductMap.value[accountName]
}
localStorage.setItem('accountProducts', JSON.stringify(accountProductMap.value))
}
const getAccountProduct = (accountName: string): string => {
return accountProductMap.value[accountName] || ''
}
const buildAccountProducts = (productId: string, respectOverrides = true): Record<string, string> | null => {
if (selectedIndices.value.size === 0) {
showToast('提示', '请勾选要抢购的账号', 'warn')
return null
}
const ap: Record<string, string> = {}
selectedIndices.value.forEach(i => {
const account = accounts.value[i]
if (account) {
const selectedProduct = respectOverrides ? getAccountProduct(account.name) : ''
const assignedProduct = selectedProduct || productId
if (assignedProduct) {
ap[account.name] = assignedProduct
}
}
})
if (Object.keys(ap).length === 0) {
showToast('提示', '请先选择商品,或为每个账号分配商品', 'warn')
return null
}
if (Object.keys(ap).length !== selectedIndices.value.size) {
showToast('提示', '还有已选账号没有商品', 'warn')
return null
}
return ap
}
return {
accounts,
selectedIndices,
statusMap,
scoreMap,
xiaodianAssetsMap,
xiaodianRolesMap,
xiaodianQRCode,
accountProductMap,
loadAccounts,
reloadAccounts,
deleteAccount,
toggleAccount,
toggleAll,
clearSelection,
restoreSelection,
queryAllScore,
queryXiaodianAssets,
queryAllXiaodianAssets,
queryAllXiaodianRoles,
getXiaodianRole,
getXiaodianBindQRCode,
bindXiaodianRole,
closeXiaodianQRCode,
getScore,
getXiaodianAssets,
resetStatus,
setStatuses,
mergeStatuses,
setStatus,
markSelectedStatus,
markSelectedPending,
setAccountProduct,
getAccountProduct,
selectedAccountNames,
buildAccountProducts
}
}
export function useVerifyExchange() {
const { accounts } = useAccounts()
const verifyResults = ref<Record<string, any>>({})
const verifying = ref(false)
const verifyAccount = async (accountName: string) => {
try {
const result = await VerifyExchange(accountName)
verifyResults.value[accountName] = result
return result
} catch (e: any) {
verifyResults.value[accountName] = { success: false, error: e.message }
return { success: false, error: e.message }
}
}
const verifyAll = async () => {
verifying.value = true
try {
// 使用后端批量查询接口,自带间隔
const results = await VerifyAllExchange()
verifyResults.value = results?.results || {}
} catch (e: any) {
console.error('批量复验失败:', e)
} finally {
verifying.value = false
}
}
const clearResults = () => {
verifyResults.value = {}
}
return {
verifyResults,
verifying,
verifyAccount,
verifyAll,
clearResults
}
}