| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- import type { FlowLedgerApiResponse, RawRecord } from "@/lib/api/types";
- import type { DemandSummary, FlowLedger, FlowLedgerRow, ScoreItem, ScoreThresholds, TechnicalRetryAttempt, TechnicalRetryDetail, VideoRef } from "./types";
- import { asRecord, maybeText, scoreLabel, text, textList } from "./format";
- function scoreThresholds(value: unknown): ScoreThresholds | null {
- if (typeof value !== "object" || value === null) return null;
- const row = value as Record<string, unknown>;
- const num = (key: string) => (row[key] === null || row[key] === undefined ? undefined : Number(row[key]));
- return {
- pool_total: num("pool_total"),
- pool_query: num("pool_query"),
- review_total: num("review_total"),
- review_query: num("review_query"),
- walk_query: num("walk_query"),
- walk_platform: num("walk_platform"),
- walk_total: num("walk_total")
- };
- }
- function countMap(value: unknown): Record<string, number> {
- const row = asRecord(value);
- return Object.fromEntries(Object.entries(row).map(([key, item]) => [key, Number(item) || 0]));
- }
- function scoreItems(value: unknown): ScoreItem[] {
- if (!Array.isArray(value)) return [];
- return value
- .filter((item): item is RawRecord => typeof item === "object" && item !== null)
- .map((item) => ({
- label: text(item.label, "评分项"),
- score: item.score === null || item.score === undefined ? null : Number(item.score),
- scoreLabel: text(item.score_label, "暂无评分"),
- detail: maybeText(item.detail),
- weight: item.weight === null || item.weight === undefined ? null : Number(item.weight),
- group: maybeText(item.group),
- status: maybeText(item.status)
- }));
- }
- function technicalRetryDetail(value: unknown): TechnicalRetryDetail | null {
- const row = asRecord(value);
- if (!Object.keys(row).length) return null;
- const attempts = Array.isArray(row.attempts) ? row.attempts : [];
- return {
- briefReason: text(row.brief_reason, ""),
- stage: text(row.stage, ""),
- stageLabel: text(row.stage_label, ""),
- failureType: text(row.failure_type, ""),
- failureLabel: text(row.failure_label, ""),
- exceptionType: text(row.exception_type, ""),
- httpStatusCode: row.http_status_code === null || row.http_status_code === undefined ? null : Number(row.http_status_code),
- errorMessage: text(row.error_message, ""),
- retryCount: row.retry_count === null || row.retry_count === undefined ? null : Number(row.retry_count),
- videoSource: text(row.video_source, ""),
- timings: countMapNullable(row.timings),
- sizes: countMapNullable(row.sizes),
- attempts: attempts
- .filter((item): item is RawRecord => typeof item === "object" && item !== null)
- .map((item): TechnicalRetryAttempt => ({
- attempt: item.attempt === null || item.attempt === undefined ? null : Number(item.attempt),
- status: text(item.status, ""),
- durationSeconds: item.duration_seconds === null || item.duration_seconds === undefined ? null : Number(item.duration_seconds),
- failureType: text(item.failure_type, ""),
- exceptionType: text(item.exception_type, ""),
- httpStatusCode: item.http_status_code === null || item.http_status_code === undefined ? null : Number(item.http_status_code)
- })),
- oss: asRecord(row.oss)
- };
- }
- function countMapNullable(value: unknown): Record<string, number | null> {
- const row = asRecord(value);
- return Object.fromEntries(
- Object.entries(row).map(([key, item]) => [key, item === null || item === undefined || item === "" ? null : Number(item)])
- );
- }
- function videoRef(row: RawRecord): VideoRef {
- const decision = asRecord(row.decision);
- return {
- id: text(row.id, "content"),
- contentDiscoveryId: text(row.content_discovery_id, ""),
- platformContentId: text(row.platform_content_id, ""),
- title: text(row.title, "无标题视频"),
- author: text(row.author, "未知作者"),
- platform: text(row.platform, "unknown"),
- platformLabel: text(row.platform_label, ""),
- url: maybeText(row.playable_url) || maybeText(row.oss_url) || null,
- ossUrl: maybeText(row.oss_url) || null,
- mediaStatus: text(row.media_status, "unavailable"),
- mediaStatusLabel: text(row.media_status_label, "视频状态待确认"),
- mediaFailureReason: maybeText(row.media_failure_reason),
- tags: textList(row.tags),
- statistics: countMap(row.statistics),
- decisionAction: text(decision.action, "NOT_JUDGED"),
- decisionLabel: text(decision.label, "未进入判断"),
- decisionReason: text(decision.reason, "no_decision"),
- decisionReasonLabel: text(decision.reason_label, "暂未产生判断结果"),
- walkOutCount: Number(row.walk_out_count || 0),
- score: scoreLabel(decision.score),
- scoreItems: scoreItems(decision.score_items),
- scoreThresholds: scoreThresholds(decision.score_thresholds),
- raw: row,
- decision,
- gemini: asRecord(row.gemini),
- technicalRetryDetail: technicalRetryDetail(row.technical_retry_detail)
- };
- }
- function rowFromApi(row: RawRecord): FlowLedgerRow {
- const source = asRecord(row.source);
- const query = asRecord(row.query);
- const rule = asRecord(row.rule_summary);
- const walk = asRecord(row.walk_summary);
- const asset = asRecord(row.asset_summary);
- const videoStats = countMap(row.video_stats);
- const videos = (Array.isArray(row.first_round_videos) ? row.first_round_videos : [])
- .filter((item): item is RawRecord => typeof item === "object" && item !== null)
- .map(videoRef);
- return {
- id: text(row.id, text(query.id, "")),
- source: {
- sourceKind: text(source.type, "来源待确认"),
- evidenceLabel: text(source.evidence, "来源证据待确认"),
- sourceRef: text(source.source_ref, "来源编号待确认"),
- terms: textList(source.terms),
- details: textList(source.details),
- raw: source
- },
- query: {
- id: text(query.id, ""),
- text: text(query.text, "搜索词待确认"),
- method: text(query.method, "unknown"),
- sourceTerms: textList(query.source_terms),
- effectStatus: text(query.effect_status, "pending"),
- raw: query
- },
- firstRoundVideos: videos,
- firstRoundVideoTotal: Number(row.first_round_video_total || videos.length),
- videoStats,
- duplicateCount: Math.max(0, Number(row.first_round_video_total || 0) - Number(videoStats.total || videos.length)),
- ruleSummary: {
- total: Number(rule.total || 0),
- passCount: Number(rule.pool_count || 0),
- reviewCount: Number(rule.review_count || 0),
- technicalRetryCount: Number(rule.technical_retry_count || 0),
- rejectCount: Number(rule.reject_count || 0),
- unknownCount: 0,
- primaryReason: text(rule.primary_reason, "no_decision"),
- primaryReasonLabel: text(rule.primary_reason_label, "暂未产生判断结果"),
- scoreLabel: "",
- scoreItems: scoreItems(asRecord(rule.score_summary).items),
- decisions: Array.isArray(asRecord(rule.debug).decisions) ? (asRecord(rule.debug).decisions as RawRecord[]) : [],
- headline: text(rule.headline, "")
- },
- walkSummary: {
- total: Number(walk.total || 0),
- expansionCount: Number(walk.expansion_count || 0),
- maxDepth: Number(walk.max_depth || 0),
- edgeCounts: countMap(walk.edge_counts),
- statusCounts: {},
- stopReasons: walk.stop_reason ? { [text(walk.stop_reason, "")]: 1 } : {},
- actions: []
- },
- finalAssets: {
- assetCount: Number(asset.pool_count || 0),
- finalCount: Number(asset.total || 0),
- pathCount: 0,
- paths: [],
- reviewCount: Number(asset.review_count || 0),
- technicalRetryCount: Number(asset.technical_retry_count || 0),
- rejectCount: Number(asset.reject_count || 0),
- headline: text(asset.headline, "")
- }
- };
- }
- function demandSummaryFromApi(row: RawRecord | null | undefined): DemandSummary | undefined {
- if (!row) return undefined;
- return {
- id: text(row.id, ""),
- name: text(row.name, ""),
- description: text(row.description, ""),
- reason: text(row.reason, ""),
- source: text(row.source, ""),
- score: row.score === null || row.score === undefined || row.score === "" ? undefined : text(row.score, ""),
- date: text(row.date, ""),
- type: text(row.type, ""),
- itemsetIds: textList(row.itemset_ids),
- seedTerms: textList(row.seed_terms),
- sourcePostId: text(row.source_post_id, ""),
- support: row.support === null || row.support === undefined || row.support === "" ? undefined : text(row.support, ""),
- patternExecutionId: text(row.pattern_execution_id, ""),
- miningConfigId: text(row.mining_config_id, ""),
- categoryPaths: textList(row.category_paths),
- extractedPoints: textList(row.extracted_points),
- categoryLeafTerms: textList(row.category_leaf_terms)
- };
- }
- export function buildFlowLedger(input: FlowLedgerApiResponse): FlowLedger {
- const rows = (input.rows || []).map(rowFromApi);
- return {
- runId: input.run_id,
- dataOrigin: input.data_origin,
- dataOriginLabel: input.data_origin_label,
- summary: input.summary || {},
- demandSummary: demandSummaryFromApi(input.demand_summary),
- extensionQueries: input.extension_queries || [],
- rows,
- raw: {
- queries: [],
- contentItems: [],
- decisions: [],
- walkActions: [],
- sourcePaths: []
- }
- };
- }
- export function findLedgerRow(ledger: FlowLedger, queryId: string): FlowLedgerRow | undefined {
- return ledger.rows.find((row) => row.id === queryId);
- }
- export function videoFromApi(row: RawRecord): VideoRef {
- return videoRef(row);
- }
|