feat(douyu): 兑换成功后自动刷新积分 & 修复兑换图片首开裂图

- 兑换成功后自动调用积分查询,更新账号最新积分并写入任务结果
- 兑换图片并发生成竞态修复:同任务共享 in-flight Promise,避免 revoke 导致裂图
- 兑换图片移除积分展示
This commit is contained in:
yml2213
2026-08-01 17:28:36 +08:00
parent e22436bfd6
commit c95d6963b8
2 changed files with 25 additions and 11 deletions
+18 -11
View File
@@ -21,7 +21,6 @@ function rawString(raw: Record<string, unknown> | 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<HTMLImageElement> {
@@ -172,7 +169,6 @@ export async function drawExchangeImage(task: DouyuTaskItem): Promise<Blob> {
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<number, CachedImage>();
const inflightImages = new Map<number, Promise<CachedImage>>();
export async function getExchangeImage(task: DouyuTaskItem): Promise<CachedImage> {
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 {