feat(asset-ai): generate contextual presentations and drilldown plans
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
import {
|
||||
CATEGORY_LABELS,
|
||||
GROUP_LABELS,
|
||||
type AssistantResult,
|
||||
type Dimension,
|
||||
type QueryPlan,
|
||||
} from "../../../shared/assets/assistant";
|
||||
|
||||
export function compactAnalysisPath(
|
||||
breadcrumbs: AssistantResult["breadcrumbs"] = [],
|
||||
) {
|
||||
const path: NonNullable<AssistantResult["breadcrumbs"]> = [];
|
||||
for (const crumb of breadcrumbs) {
|
||||
if (path.at(-1)?.label === crumb.label) path[path.length - 1] = crumb;
|
||||
else path.push(crumb);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
export function queryPath(plan: QueryPlan): string[] {
|
||||
return [
|
||||
plan.kind === "flow" ? "交退换车" : CATEGORY_LABELS[plan.category],
|
||||
...Object.entries(plan.filters).map(
|
||||
([dimension, values]) =>
|
||||
`${GROUP_LABELS[dimension as Dimension]}:${values.join("、")}`,
|
||||
),
|
||||
...Object.entries(plan.excludeFilters || {}).map(
|
||||
([dimension, values]) =>
|
||||
`排除${GROUP_LABELS[dimension as Dimension]}:${values.join("、")}`,
|
||||
),
|
||||
...(plan.vehicleIds?.length ? [`已选${plan.vehicleIds.length}台车辆`] : []),
|
||||
...(plan.exception
|
||||
? [
|
||||
plan.exception === "overdue"
|
||||
? "已超期"
|
||||
: plan.exception === "dueSoon"
|
||||
? "即将到期"
|
||||
: "未填写预计完成时间",
|
||||
]
|
||||
: []),
|
||||
plan.groupBy === "none" && !plan.secondaryGroupBy
|
||||
? "总量"
|
||||
: `按${[GROUP_LABELS[plan.groupBy], plan.secondaryGroupBy && GROUP_LABELS[plan.secondaryGroupBy]].filter(Boolean).join(" × ")}统计`,
|
||||
];
|
||||
}
|
||||
|
||||
export function resultDisplay(
|
||||
plan: QueryPlan,
|
||||
): "metrics" | "chart" | "table" | "list" {
|
||||
if (
|
||||
plan.kind === "assets" &&
|
||||
plan.groupBy === "none" &&
|
||||
!plan.secondaryGroupBy &&
|
||||
plan.view === "chart"
|
||||
)
|
||||
return "metrics";
|
||||
return plan.view;
|
||||
}
|
||||
|
||||
export function displayTime(value: string | null | undefined) {
|
||||
if (!value) return "未提供";
|
||||
const date = new Date(value);
|
||||
return Number.isNaN(date.getTime())
|
||||
? value
|
||||
: date.toLocaleString("zh-CN", { hour12: false });
|
||||
}
|
||||
|
||||
export function resultScope(result: AssistantResult, subject: string | null) {
|
||||
if (result.scope) return result.scope;
|
||||
const { plan } = result;
|
||||
return {
|
||||
company: subject || "全部所属公司",
|
||||
permission: "当前账号可见范围",
|
||||
filters: [
|
||||
CATEGORY_LABELS[plan.category],
|
||||
...Object.entries(plan.filters).map(
|
||||
([dimension, values]) =>
|
||||
`${GROUP_LABELS[dimension as Dimension]}:${values.join("、")}`,
|
||||
),
|
||||
...Object.entries(plan.excludeFilters || {}).map(
|
||||
([dimension, values]) =>
|
||||
`排除${GROUP_LABELS[dimension as Dimension]}:${values.join("、")}`,
|
||||
),
|
||||
...(plan.vehicleIds?.length
|
||||
? [`指定车辆:${plan.vehicleIds.length}台`]
|
||||
: []),
|
||||
...(plan.exception
|
||||
? [
|
||||
plan.exception === "overdue"
|
||||
? "已超过预计完成时间"
|
||||
: plan.exception === "dueSoon"
|
||||
? "即将到期"
|
||||
: "未填写预计完成时间",
|
||||
]
|
||||
: []),
|
||||
],
|
||||
grouping: [
|
||||
GROUP_LABELS[plan.groupBy],
|
||||
plan.secondaryGroupBy && GROUP_LABELS[plan.secondaryGroupBy],
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" × "),
|
||||
period: plan.start
|
||||
? `${plan.start} — ${plan.end || plan.start}`
|
||||
: "当前资产台账",
|
||||
definitions: [],
|
||||
sourceUpdatedAt: null,
|
||||
};
|
||||
}
|
||||
|
||||
export function copyResultText(
|
||||
result: AssistantResult,
|
||||
subject: string | null,
|
||||
correctionMessage?: string,
|
||||
): string {
|
||||
if (result.sections?.length) return [
|
||||
result.title, result.summary,
|
||||
...(correctionMessage ? [`【此回答已撤回】${correctionMessage}`] : []),
|
||||
...result.sections.map((section, index) => `\n${index + 1}. ${section.title}\n${section.result ? copyResultText(section.result, subject) : section.error || "暂未完成"}`),
|
||||
...(result.unanswered?.length ? ["尚未回答:", ...result.unanswered.map(item => `${item.question}:${item.reason}`)] : []),
|
||||
].join("\n");
|
||||
const scope = resultScope(result, subject);
|
||||
return [
|
||||
result.presentation?.title || result.title,
|
||||
...(correctionMessage ? [`【此回答已撤回】${correctionMessage}`] : []),
|
||||
...(result.correction ? [`【纠正说明】${result.correction.message}`] : []),
|
||||
result.summary,
|
||||
...(result.presentation?.blocks.filter(block => block.kind === "narrative" && block.text).map(block => `${block.title ? `${block.title}:` : ""}${block.text}`) || []),
|
||||
...result.metrics.map(
|
||||
(metric) =>
|
||||
`${metric.label}:${metric.value.toLocaleString()}${metric.unit}`,
|
||||
),
|
||||
...(result.plan.kind === "assets" &&
|
||||
result.plan.analysis !== "briefing" &&
|
||||
result.groups.length
|
||||
? [
|
||||
"分组统计:",
|
||||
...result.groups.map(
|
||||
(group) =>
|
||||
`${group.name}:${group.count}台${group.share === undefined ? "" : `,占比${group.share}%`},运营率${group.rate}%${group.abnormal === undefined ? "" : `,其中异动${group.abnormal}台`}${group.pending === undefined ? "" : `,其中待交车${group.pending}台`}`,
|
||||
),
|
||||
]
|
||||
: []),
|
||||
...(result.plan.kind === "flow" && result.daily?.length
|
||||
? [
|
||||
"每日流转(车次):替换交车包含在交车中,不重复相加。",
|
||||
...result.daily.map(
|
||||
(day) =>
|
||||
`${day.date}:交车${day.delivered}车次,还车${day.returned}车次,其中替换交车${day.replaced}车次,合计${day.total}车次`,
|
||||
),
|
||||
]
|
||||
: []),
|
||||
...(result.insights?.length ? ["分析发现:", ...result.insights] : []),
|
||||
...(result.inventoryBreakdown
|
||||
? [
|
||||
result.inventoryBreakdown.title,
|
||||
result.inventoryBreakdown.summary,
|
||||
...result.inventoryBreakdown.groups.map(
|
||||
(group) =>
|
||||
`${group.name}:${group.count}台${group.share === undefined ? "" : `,占库存${group.share}%`}`,
|
||||
),
|
||||
]
|
||||
: []),
|
||||
...(result.comparison?.length
|
||||
? [
|
||||
`对比期间:${result.comparisonPeriod?.start || "未提供"} — ${result.comparisonPeriod?.end || "未提供"}`,
|
||||
...result.comparison.map(
|
||||
(metric) =>
|
||||
`${metric.label}:本期${metric.current},上期${metric.previous},差值${metric.delta},变化率${metric.rate === null ? "基期为0,不计算" : `${metric.rate}%`}`,
|
||||
),
|
||||
]
|
||||
: []),
|
||||
...(result.unanswered?.length
|
||||
? [
|
||||
"尚未回答:",
|
||||
...result.unanswered.map(
|
||||
(item) => `${item.question}:${item.reason}`,
|
||||
),
|
||||
]
|
||||
: []),
|
||||
`公司:${scope.company}`,
|
||||
`权限范围:${scope.permission}`,
|
||||
`筛选条件:${scope.filters.join(";") || "无额外筛选"}`,
|
||||
`分组:${scope.grouping}`,
|
||||
`统计期间:${scope.period}`,
|
||||
...(result.plan.topN
|
||||
? [`仅展示前${result.plan.topN}组;总计与明细包含全部匹配车辆`]
|
||||
: []),
|
||||
...scope.definitions.map((definition) => `统计口径:${definition}`),
|
||||
`来源:${result.source}`,
|
||||
`源数据更新时间:${displayTime(scope.sourceUpdatedAt)}`,
|
||||
`查询时间:${displayTime(result.fetchedAt)}`,
|
||||
].join("\n");
|
||||
}
|
||||
Reference in New Issue
Block a user