fix(route/xueqiu): serialize show.json requests and skip caching transient failures (#22708)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
sirius60111 2026-07-24 05:22:35 +08:00 committed by GitHub
parent 0fafcbba7a
commit 534967c4ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 38 additions and 28 deletions

View File

@ -1,4 +1,5 @@
import sanitizeHtml from 'sanitize-html';
import pMap from 'p-map';
import { parseToken } from '@/routes/xueqiu/cookies';
import type { Route } from '@/types';
@ -115,36 +116,45 @@ async function handler(ctx) {
const data = response.statuses.filter((s) => s.mark !== 1); // 去除置顶动态
const items = await Promise.all(
data.map((item) =>
cache.tryGet(item.target, async () => {
// legal_user_visible 为 true 时列表已含完整内容,无需再请求详情
if (item.legal_user_visible) {
return buildListItem(item);
}
// Use p-map to limit concurrency and avoid triggering Xueqiu show.json rate limiting.
const items = await pMap(
data,
async (item) => {
try {
return await cache.tryGet(item.target, async () => {
// legal_user_visible 为 true 时列表已含完整内容,无需再请求详情
if (item.legal_user_visible) {
return buildListItem(item);
}
try {
const detail = await ofetch(`${apiUrl}/statuses/show.json`, {
query: {
id: item.id,
},
headers: {
Cookie: cookie,
Referer: link,
},
});
try {
const detail = await ofetch(`${apiUrl}/statuses/show.json`, {
query: { id: item.id },
headers: { Cookie: cookie, Referer: link },
});
return {
title: buildTitle(item, detail),
description: buildDescription(detail),
pubDate: parseDate(item.created_at),
link: rootUrl + item.target,
};
} catch {
return buildListItem(item);
}
})
)
return {
title: buildTitle(item, detail),
description: buildDescription(detail),
pubDate: parseDate(item.created_at),
link: rootUrl + item.target,
};
} catch (error: any) {
// Permanent failures (post deleted / not found): cache the fallback.
const data = error.response?._data || error.data;
if (data && typeof data === 'object' && data.error_code) {
return buildListItem(item);
}
// Transient failures (rate limit / WAF): throw to skip caching, retry next request.
throw error;
}
});
} catch {
// Transient failure: provide a fallback item without caching, retry next request.
return buildListItem(item);
}
},
{ concurrency: 3 },
);
const user = data[0]?.user;