diff --git a/web/backend/services/douyu_runner.py b/web/backend/services/douyu_runner.py index dd29a99..ed7f298 100644 --- a/web/backend/services/douyu_runner.py +++ b/web/backend/services/douyu_runner.py @@ -1830,6 +1830,12 @@ class DouyuBatchRunner: ) account.bind_status = "goods_exchanged" account.updated_at = datetime.now(timezone.utc) + # 兑换成功后自动刷新积分,更新账号最新积分信息(失败不阻断兑换成功) + points_refresh = None + try: + points_refresh = self._refresh_account_points(client, account, cookie, ctn=ctn) + except Exception as exc: + self._push_log("warning", f"兑换后刷新积分失败: {exc}") self._mark_task( db, task, @@ -1841,6 +1847,7 @@ class DouyuBatchRunner: "game_channel": account.game_channel or "", "account_name": account.nickname or account.username or account.uid or f"#{account.id}", **result, + **(points_refresh or {}), }, ) diff --git a/web/frontend/src/utils/exchangeImage.ts b/web/frontend/src/utils/exchangeImage.ts index ed27858..100e083 100644 --- a/web/frontend/src/utils/exchangeImage.ts +++ b/web/frontend/src/utils/exchangeImage.ts @@ -21,7 +21,6 @@ function rawString(raw: Record | null | undefined, key: string) function goodsOf(task: DouyuTaskItem): { name: string; pic: string; - points: number | null; roleName: string; channel: string; accountName: string; @@ -32,13 +31,11 @@ function goodsOf(task: DouyuTaskItem): { || task.message.replace(/^兑换[^::]*[::]?\s*/, '').trim() || rawString(task.result, 'commodity_id'); const pic = rawString(goods, 'webPic') || rawString(goods, 'pic'); - const pointsValue = task.result?.esports_points_after_exchange; - const points = typeof pointsValue === 'number' && Number.isFinite(pointsValue) ? pointsValue : null; const roleName = rawString(task.result, 'game_name'); const channel = rawString(task.result, 'game_channel'); const accountName = rawString(task.result, 'account_name') || task.account_nickname || task.account_username || task.account_uid || `#${task.account_id}`; - return { name, pic, points, roleName, channel, accountName }; + return { name, pic, roleName, channel, accountName }; } function loadImage(url: string, timeoutMs = 8000): Promise { @@ -172,7 +169,6 @@ export async function drawExchangeImage(task: DouyuTaskItem): Promise { if (info.roleName) infoRows.push(['游戏角色', info.roleName]); if (info.channel) infoRows.push(['游戏区服', info.channel]); infoRows.push(['兑换账号', info.accountName]); - if (info.points != null) infoRows.push(['当前积分', `${info.points}`]); if (time) infoRows.push(['兑换时间', time]); ctx.font = `15px ${FONT_FAMILY}`; @@ -220,16 +216,27 @@ interface CachedImage { } const imageCache = new Map(); +const inflightImages = new Map>(); export async function getExchangeImage(task: DouyuTaskItem): Promise { const cached = imageCache.get(task.id); if (cached) return cached; - const blob = await drawExchangeImage(task); - const entry = { url: URL.createObjectURL(blob), blob }; - const previous = imageCache.get(task.id); - if (previous) URL.revokeObjectURL(previous.url); - imageCache.set(task.id, entry); - return entry; + const pending = inflightImages.get(task.id); + if (pending) return pending; + const creating = (async () => { + const blob = await drawExchangeImage(task); + const entry = { url: URL.createObjectURL(blob), blob }; + const previous = imageCache.get(task.id); + if (previous) URL.revokeObjectURL(previous.url); + imageCache.set(task.id, entry); + return entry; + })(); + inflightImages.set(task.id, creating); + try { + return await creating; + } finally { + inflightImages.delete(task.id); + } } export function clearExchangeImageCache(): void {