fix: hydrate id export metrics and remove new tier columns
This commit is contained in:
parent
38da39589f
commit
37f7e0b5e6
@ -30,7 +30,6 @@ const GENDER_LABELS = ["男性", "女性"];
|
||||
const AGE_LABELS = ["18-23", "24-30", "31-40", "41-50", "50+"];
|
||||
const CITY_TIER_LABELS = [
|
||||
"一线城市",
|
||||
"新一线城市",
|
||||
"二线城市",
|
||||
"三线城市",
|
||||
"四线城市",
|
||||
|
||||
@ -296,6 +296,7 @@ export function createMarketController(options: CreateMarketControllerOptions) {
|
||||
`识别 ${parsed.ids.length + parsed.duplicates.length + parsed.invalidTokens.length} 个,去重后 ${parsed.ids.length} 个,非法 ${parsed.invalidTokens.length} 个`
|
||||
);
|
||||
|
||||
const backendMetricsByAuthorId = await loadBackendMetricsMap(parsed.ids);
|
||||
const rows: AudienceProfileExportRow[] = [];
|
||||
for (let index = 0; index < parsed.ids.length; index += 1) {
|
||||
const authorId = parsed.ids[index];
|
||||
@ -303,7 +304,12 @@ export function createMarketController(options: CreateMarketControllerOptions) {
|
||||
toolbar,
|
||||
`按ID画像导出中 ${index + 1}/${parsed.ids.length}...`
|
||||
);
|
||||
rows.push(await loadAudienceProfileRowById(authorId));
|
||||
rows.push(
|
||||
await loadAudienceProfileRowById(
|
||||
authorId,
|
||||
backendMetricsByAuthorId.get(authorId)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
options.onCsvReady?.(
|
||||
@ -761,12 +767,20 @@ export function createMarketController(options: CreateMarketControllerOptions) {
|
||||
}
|
||||
|
||||
async function loadAudienceProfileRowById(
|
||||
authorId: string
|
||||
authorId: string,
|
||||
backendMetrics?: BackendMetrics
|
||||
): Promise<AudienceProfileExportRow> {
|
||||
const baseRecord = await loadAuthorBaseInfoSafe(authorId);
|
||||
const [baseRecord, metricsResult] = await Promise.all([
|
||||
loadAuthorBaseInfoSafe(authorId),
|
||||
loadAuthorMetricsSafe(authorId)
|
||||
]);
|
||||
const recordForRequests = {
|
||||
...baseRecord,
|
||||
authorName: baseRecord.authorName || authorId
|
||||
authorName: baseRecord.authorName || authorId,
|
||||
...(metricsResult.success ? { rates: metricsResult.rates } : {}),
|
||||
...(backendMetrics
|
||||
? { backendMetrics, backendMetricsStatus: "success" as const }
|
||||
: {})
|
||||
};
|
||||
const [profiles, businessAbility] = await Promise.all([
|
||||
loadAudienceProfileSet(recordForRequests),
|
||||
@ -814,6 +828,40 @@ export function createMarketController(options: CreateMarketControllerOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAuthorMetricsSafe(
|
||||
authorId: string
|
||||
): Promise<MarketApiResult> {
|
||||
try {
|
||||
return await loadAuthorMetrics(authorId);
|
||||
} catch {
|
||||
return {
|
||||
reason: "request-failed",
|
||||
success: false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function loadBackendMetricsMap(
|
||||
authorIds: string[]
|
||||
): Promise<Map<string, BackendMetrics>> {
|
||||
const metricsMap = new Map<string, BackendMetrics>();
|
||||
if (!searchBackendMetrics || authorIds.length === 0) {
|
||||
return metricsMap;
|
||||
}
|
||||
|
||||
try {
|
||||
const rows = await searchBackendMetrics(authorIds);
|
||||
rows.forEach((row) => {
|
||||
const { starId, ...backendMetrics } = row;
|
||||
metricsMap.set(starId, backendMetrics);
|
||||
});
|
||||
} catch {
|
||||
return metricsMap;
|
||||
}
|
||||
|
||||
return metricsMap;
|
||||
}
|
||||
|
||||
function collectAudienceProfileRowFailures(
|
||||
baseRecord: MarketRecord,
|
||||
profiles: AudienceProfileExportRow["profiles"],
|
||||
|
||||
@ -102,6 +102,9 @@ describe("audience-profile-csv", () => {
|
||||
expect(headerLine).toContain("观众画像-31-40占比");
|
||||
expect(headerLine).toContain("粉丝画像-一线城市占比");
|
||||
expect(headerLine).toContain("铁粉画像-都市蓝领占比");
|
||||
expect(headerLine).not.toContain("观众画像-新一线城市占比");
|
||||
expect(headerLine).not.toContain("粉丝画像-新一线城市占比");
|
||||
expect(headerLine).not.toContain("铁粉画像-新一线城市占比");
|
||||
expect(headerLine).not.toContain("省份");
|
||||
expect(headerLine).not.toContain("地域TOP");
|
||||
expect(headerLine).not.toContain("兴趣TOP");
|
||||
@ -183,10 +186,10 @@ describe("audience-profile-csv", () => {
|
||||
|
||||
expect(readCsvValue(csv, "铁粉画像-41-50占比")).toBe("0%");
|
||||
expect(readCsvValue(csv, "铁粉画像-50+占比")).toBe("0%");
|
||||
expect(readCsvValue(csv, "铁粉画像-新一线城市占比")).toBe("0%");
|
||||
expect(readCsvValue(csv, "铁粉画像-五线城市占比")).toBe("0%");
|
||||
expect(readCsvValue(csv, "铁粉画像-都市银发占比")).toBe("0%");
|
||||
expect(readCsvValue(csv, "铁粉画像-Z世代占比")).toBe("0%");
|
||||
expect(csv.split("\n")[0]).not.toContain("新一线城市占比");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -1728,6 +1728,26 @@ describe("market-content-entry", () => {
|
||||
gender: [{ label: "男性", value: "60%" }],
|
||||
status: "success" as const
|
||||
}));
|
||||
const loadAuthorMetrics = vi.fn(async (authorId: string) => ({
|
||||
rates: {
|
||||
personalVideoAfterSearchRate:
|
||||
authorId === "6866044569306267651" ? "12.3%" : "45.6%",
|
||||
singleVideoAfterSearchRate:
|
||||
authorId === "6866044569306267651" ? "7.8%" : "9.1%"
|
||||
},
|
||||
success: true as const
|
||||
}));
|
||||
const searchBackendMetrics = vi.fn(async (starIds: string[]) =>
|
||||
starIds.map((starId) => ({
|
||||
a3IncreaseCount: starId === "6866044569306267651" ? "100" : "200",
|
||||
afterViewSearchCount: starId === "6866044569306267651" ? "300" : "400",
|
||||
afterViewSearchRate: starId === "6866044569306267651" ? "1.1%" : "2.2%",
|
||||
cpSearch: starId === "6866044569306267651" ? "10" : "20",
|
||||
cpa3: starId === "6866044569306267651" ? "30" : "40",
|
||||
newA3Rate: starId === "6866044569306267651" ? "3.3%" : "4.4%",
|
||||
starId
|
||||
}))
|
||||
);
|
||||
const onCsvReady = vi.fn();
|
||||
|
||||
const { createMarketController } = await import("../src/content/market/index");
|
||||
@ -1737,10 +1757,7 @@ describe("market-content-entry", () => {
|
||||
loadAuthorBaseInfo,
|
||||
loadBusinessAbility,
|
||||
loadAudienceProfile,
|
||||
loadAuthorMetrics: async () => ({
|
||||
success: false,
|
||||
reason: "request-failed"
|
||||
}),
|
||||
loadAuthorMetrics,
|
||||
onCsvReady,
|
||||
promptAuthorIds: () => `
|
||||
6866044569306267651
|
||||
@ -1748,10 +1765,13 @@ describe("market-content-entry", () => {
|
||||
6866044569306267651
|
||||
bad-id
|
||||
`,
|
||||
searchBackendMetrics,
|
||||
window
|
||||
}));
|
||||
|
||||
await controller.ready;
|
||||
loadAuthorMetrics.mockClear();
|
||||
searchBackendMetrics.mockClear();
|
||||
click('[data-plugin-export-audience-profile-by-id="button"]');
|
||||
await waitForMockCall(buildAudienceProfileCsv, 40, 50);
|
||||
|
||||
@ -1761,23 +1781,56 @@ describe("market-content-entry", () => {
|
||||
]);
|
||||
expect(loadAudienceProfile).toHaveBeenCalledTimes(6);
|
||||
expect(loadBusinessAbility).toHaveBeenCalledTimes(2);
|
||||
expect(loadAuthorMetrics.mock.calls.map(([authorId]) => authorId)).toEqual([
|
||||
"6866044569306267651",
|
||||
"7040323176106033165"
|
||||
]);
|
||||
expect(searchBackendMetrics).toHaveBeenCalledTimes(1);
|
||||
expect(searchBackendMetrics).toHaveBeenCalledWith([
|
||||
"6866044569306267651",
|
||||
"7040323176106033165"
|
||||
]);
|
||||
expect(buildAudienceProfileCsv).toHaveBeenCalledWith([
|
||||
expect.objectContaining({
|
||||
record: expect.objectContaining({
|
||||
authorId: "6866044569306267651",
|
||||
authorName: "小九儿",
|
||||
backendMetrics: expect.objectContaining({
|
||||
a3IncreaseCount: "100",
|
||||
afterViewSearchCount: "300",
|
||||
afterViewSearchRate: "1.1%",
|
||||
cpSearch: "10",
|
||||
cpa3: "30",
|
||||
newA3Rate: "3.3%"
|
||||
}),
|
||||
exportFields: {
|
||||
达人ID: "6866044569306267651",
|
||||
达人名称: "小九儿",
|
||||
导出状态: "成功",
|
||||
失败原因: ""
|
||||
},
|
||||
rates: {
|
||||
personalVideoAfterSearchRate: "12.3%",
|
||||
singleVideoAfterSearchRate: "7.8%"
|
||||
}
|
||||
})
|
||||
}),
|
||||
expect.objectContaining({
|
||||
record: expect.objectContaining({
|
||||
authorId: "7040323176106033165",
|
||||
authorName: "达人 B"
|
||||
authorName: "达人 B",
|
||||
backendMetrics: expect.objectContaining({
|
||||
a3IncreaseCount: "200",
|
||||
afterViewSearchCount: "400",
|
||||
afterViewSearchRate: "2.2%",
|
||||
cpSearch: "20",
|
||||
cpa3: "40",
|
||||
newA3Rate: "4.4%"
|
||||
}),
|
||||
rates: {
|
||||
personalVideoAfterSearchRate: "45.6%",
|
||||
singleVideoAfterSearchRate: "9.1%"
|
||||
}
|
||||
})
|
||||
})
|
||||
]);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user