Files
ln-bi/src/modules/assets/assistant/result-presentation.ts
T
shishengliang f1b3f8d3da
ci/woodpecker/push/woodpecker Pipeline was successful
feat(assets): add dimension analysis and authorized user previews
Refactor asset drilldowns with combined dimension filters, parent summaries and progressive loading. Add the hidden /who read-only permission preview with scoped authorization, identity mapping and audit logging. Refine inventory counts and AI问数 presentation.
2026-09-28 12:29:51 +08:00

195 lines
7.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
? plan.responseMode === "vehicle-status" ? "车辆状态" : "总量"
: `按${[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");
}