ci/woodpecker/push/woodpecker Pipeline was successful
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.
195 lines
7.1 KiB
TypeScript
195 lines
7.1 KiB
TypeScript
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");
|
||
}
|