|
|
@@ -178,8 +178,12 @@ const NEAR_30_DAYS_SELECTORS = [
|
|
|
'label:has-text("近30天")',
|
|
|
];
|
|
|
const VIEW_BTN_SELECTORS = (objectId: string) => [
|
|
|
- `div.ant-table-fixed-right tr[data-row-key="${objectId}"] a.detail-wrap`,
|
|
|
+ // 与当前后台 DOM 一致:table.ant-table-fixed 下 tr[data-row-key] 内 a.detail-wrap
|
|
|
+ `table.ant-table-fixed tr[data-row-key="${objectId}"] a.detail-wrap`,
|
|
|
+ `div.ant-table-fixed tr[data-row-key="${objectId}"] a.detail-wrap`,
|
|
|
+ `tr[data-row-key="${objectId}"] div.slot-wrap a.detail-wrap`,
|
|
|
`tr[data-row-key="${objectId}"] a.detail-wrap`,
|
|
|
+ `div.ant-table-fixed-right tr[data-row-key="${objectId}"] a.detail-wrap`,
|
|
|
`tr[data-row-key="${objectId}"] a:has-text("查看")`,
|
|
|
`tr[data-row-key="${objectId}"] a`,
|
|
|
];
|
|
|
@@ -530,17 +534,48 @@ export class WeixinVideoWorkStatisticsImportService {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
+ // 先挂好监听再点击「查看」,避免响应在 waitForResponse 之前就返回导致漏接
|
|
|
+ const FEED_AGGREAGATE_PAGE_URL = 'https://channels.weixin.qq.com/micro/statistic/postDetail';
|
|
|
+ const getReqPageUrl = (req: { url: () => string; postData: () => string | undefined }): string => {
|
|
|
+ try {
|
|
|
+ let pageUrl = new URL(req.url()).searchParams.get('_pageUrl') ?? '';
|
|
|
+ if (pageUrl) return decodeURIComponent(pageUrl);
|
|
|
+ const postData = req.postData();
|
|
|
+ if (typeof postData === 'string') {
|
|
|
+ const p = JSON.parse(postData);
|
|
|
+ const raw = (p?._pageUrl ?? p?._page_url ?? '') as string;
|
|
|
+ return raw ? decodeURIComponent(raw) : '';
|
|
|
+ }
|
|
|
+ } catch {
|
|
|
+ // ignore
|
|
|
+ }
|
|
|
+ return '';
|
|
|
+ };
|
|
|
+ const responsePromise = page.waitForResponse(
|
|
|
+ (r) => {
|
|
|
+ if (!r.url().includes('feed_aggreagate_data_by_tab_type')) return false;
|
|
|
+ const pageUrl = getReqPageUrl(r.request());
|
|
|
+ if (pageUrl !== FEED_AGGREAGATE_PAGE_URL) {
|
|
|
+ logger.info(`[WX WorkStats] feed_aggreagate 忽略 _pageUrl=${pageUrl}`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ { timeout: 15_000 }
|
|
|
+ );
|
|
|
+
|
|
|
let viewClicked = false;
|
|
|
for (const sel of VIEW_BTN_SELECTORS(oid)) {
|
|
|
const viewBtn = page.locator(sel);
|
|
|
if ((await viewBtn.count()) > 0) {
|
|
|
try {
|
|
|
await viewBtn.nth(0).waitFor({ state: 'visible', timeout: 3000 });
|
|
|
+ logger.info(`[WX WorkStats] accountId=${account.id} 点击「查看」oid=${oid} selector=${sel}`);
|
|
|
await viewBtn.nth(0).click();
|
|
|
viewClicked = true;
|
|
|
break;
|
|
|
- } catch {
|
|
|
- // next selector
|
|
|
+ } catch (e) {
|
|
|
+ logger.warn(`[WX WorkStats] 点击「查看」失败 oid=${oid} selector=${sel}`, e);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -548,18 +583,17 @@ export class WeixinVideoWorkStatisticsImportService {
|
|
|
totalSkipped += 1;
|
|
|
continue;
|
|
|
}
|
|
|
- await page.waitForTimeout(2000);
|
|
|
|
|
|
let body: { data?: { dataByFanstype?: Array<{ dataByTabtype?: Array<{ tabTypeName?: string; tabType?: number; data?: any }> }>; feedData?: Array<{ totalData?: any }> } } | null = null;
|
|
|
try {
|
|
|
- const res = await page.waitForResponse(
|
|
|
- (r) => r.url().includes('feed_aggreagate_data_by_tab_type'),
|
|
|
- { timeout: 12_000 }
|
|
|
- );
|
|
|
+ const res = await responsePromise;
|
|
|
const json = await res.json().catch(() => null);
|
|
|
- if (json?.errCode === 0 && json?.data) body = json;
|
|
|
- } catch {
|
|
|
- // timeout or no response
|
|
|
+ if (json?.errCode === 0 && json?.data) {
|
|
|
+ body = json;
|
|
|
+ logger.info(`[WX WorkStats] accountId=${account.id} 收到 feed_aggreagate 详情数据 oid=${oid}`);
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ logger.warn(`[WX WorkStats] accountId=${account.id} 等待 feed_aggreagate(postDetail) 超时或失败 oid=${oid}`, e);
|
|
|
}
|
|
|
|
|
|
if (!body?.data) {
|