feat: initialize drainage network frontend
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
import { formatValue } from "./format-value";
|
||||
|
||||
export type LocalizedFeatureProperty = {
|
||||
key: string;
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
const PROPERTY_LABELS: Record<string, string> = {
|
||||
id: "编号",
|
||||
gid: "要素编号",
|
||||
name: "名称",
|
||||
code: "编码",
|
||||
type: "类型",
|
||||
node1: "起点节点",
|
||||
node2: "终点节点",
|
||||
from_node: "起点节点",
|
||||
to_node: "终点节点",
|
||||
diameter: "管径",
|
||||
dn: "管径",
|
||||
length: "长度",
|
||||
roughness: "粗糙系数",
|
||||
status: "状态",
|
||||
material: "材质",
|
||||
elevation: "高程",
|
||||
demand: "需水量",
|
||||
pressure: "压力",
|
||||
flow: "流量",
|
||||
velocity: "流速",
|
||||
zone: "分区",
|
||||
district: "片区",
|
||||
source: "来源",
|
||||
updated_at: "更新时间"
|
||||
};
|
||||
|
||||
const PROPERTY_ORDER: Record<string, number> = {
|
||||
id: 10,
|
||||
gid: 11,
|
||||
name: 12,
|
||||
code: 13,
|
||||
diameter: 20,
|
||||
dn: 20,
|
||||
length: 21,
|
||||
roughness: 22,
|
||||
material: 23,
|
||||
node1: 24,
|
||||
from_node: 24,
|
||||
node2: 25,
|
||||
to_node: 25,
|
||||
elevation: 30,
|
||||
demand: 31,
|
||||
pressure: 32,
|
||||
flow: 33,
|
||||
velocity: 34,
|
||||
status: 90
|
||||
};
|
||||
|
||||
const STATUS_LABELS: Record<string, string> = {
|
||||
active: "运行中",
|
||||
inactive: "停用",
|
||||
online: "在线",
|
||||
offline: "离线",
|
||||
open: "开启",
|
||||
closed: "关闭",
|
||||
normal: "正常",
|
||||
warning: "告警",
|
||||
critical: "严重"
|
||||
};
|
||||
|
||||
export function getLocalizedFeatureProperties(properties: Record<string, unknown>): LocalizedFeatureProperty[] {
|
||||
return Object.entries(properties).map(([key, value]) => ({
|
||||
key,
|
||||
label: getFeaturePropertyLabel(key),
|
||||
value: formatFeaturePropertyValue(key, value)
|
||||
})).sort((first, second) => getPropertyOrder(first.key) - getPropertyOrder(second.key));
|
||||
}
|
||||
|
||||
export function getFeaturePropertyLabel(key: string) {
|
||||
const normalizedKey = normalizePropertyKey(key);
|
||||
return PROPERTY_LABELS[normalizedKey] ?? key;
|
||||
}
|
||||
|
||||
export function formatFeaturePropertyValue(key: string, value: unknown) {
|
||||
const normalizedKey = normalizePropertyKey(key);
|
||||
|
||||
if (isStatusKey(key) && typeof value === "string") {
|
||||
return STATUS_LABELS[value.toLowerCase()] ?? value;
|
||||
}
|
||||
|
||||
if ((normalizedKey === "diameter" || normalizedKey === "dn") && value !== null && value !== undefined && value !== "") {
|
||||
return `DN${formatValue(value)}`;
|
||||
}
|
||||
|
||||
if ((normalizedKey === "length" || normalizedKey === "elevation") && value !== null && value !== undefined && value !== "") {
|
||||
return `${formatValue(value)} m`;
|
||||
}
|
||||
|
||||
return formatValue(value);
|
||||
}
|
||||
|
||||
function getPropertyOrder(key: string) {
|
||||
return PROPERTY_ORDER[normalizePropertyKey(key)] ?? 100;
|
||||
}
|
||||
|
||||
function normalizePropertyKey(key: string) {
|
||||
return key.trim().replaceAll("-", "_").toLowerCase();
|
||||
}
|
||||
|
||||
function isStatusKey(key: string) {
|
||||
return normalizePropertyKey(key) === "status";
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export function formatValue(value: unknown) {
|
||||
if (typeof value === "number") {
|
||||
return Number.isInteger(value) ? value.toString() : value.toFixed(2);
|
||||
}
|
||||
|
||||
if (value === null || value === undefined || value === "") {
|
||||
return "暂无";
|
||||
}
|
||||
|
||||
return String(value);
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import { getRunningWorkflowDefinition } from "@/features/workbench/data/running-workflows";
|
||||
import type { ScheduledConditionItem, ScheduledConditionStatus, WorkbenchAlert } from "@/features/workbench/types";
|
||||
|
||||
const SCHEDULED_CONDITION_ALERT_ID_PREFIX = "scheduled-condition-alert-";
|
||||
|
||||
const statusLabels: Record<ScheduledConditionStatus, string> = {
|
||||
completed: "完成",
|
||||
running: "执行中",
|
||||
warning: "关注",
|
||||
error: "异常",
|
||||
pending: "预约"
|
||||
};
|
||||
|
||||
export function createConditionConversationPrompt(condition: ScheduledConditionItem) {
|
||||
const workflow =
|
||||
condition.kind === "condition"
|
||||
? getRunningWorkflowDefinition(condition)
|
||||
: null;
|
||||
const evidence = condition.evidence?.map((item, index) => `${index + 1}. ${item}`).join("\n") || "无";
|
||||
const kpis =
|
||||
condition.kind === "condition" && condition.kpis?.length
|
||||
? condition.kpis
|
||||
.map((kpi, index) => {
|
||||
const value = `${kpi.value}${kpi.unit ?? ""}`;
|
||||
const threshold = kpi.threshold ? `,阈值:${kpi.threshold}` : "";
|
||||
return `${index + 1}. ${kpi.label}:${value},状态:${getRiskLabel(kpi.status)}${threshold}`;
|
||||
})
|
||||
.join("\n")
|
||||
: "无";
|
||||
const report =
|
||||
condition.kind === "condition" && condition.report
|
||||
? [
|
||||
condition.report.conclusion,
|
||||
...condition.report.sections.map(
|
||||
(section) => `${section.title}:${section.items.join(";")}`
|
||||
)
|
||||
].join("\n")
|
||||
: null;
|
||||
|
||||
return [
|
||||
"请继续分析这条工况任务,并基于当前上下文给出下一步调度建议。",
|
||||
"",
|
||||
`时间:${formatTime(condition.scheduledAt)}`,
|
||||
"类型:历史工况",
|
||||
workflow ? `任务工作流:${workflow.title}` : null,
|
||||
workflow ? `工作流步骤:${workflow.steps.map((step, index) => `${index + 1}. ${step.title}`).join(";")}` : null,
|
||||
`标题:${condition.title}`,
|
||||
`状态:${statusLabels[condition.status]}`,
|
||||
`风险:${getRiskLabel(condition.riskLevel)}`,
|
||||
`摘要:${condition.summary}`,
|
||||
condition.detail ? `详情:${condition.detail}` : null,
|
||||
`关键证据:\n${evidence}`,
|
||||
`关键指标:\n${kpis}`,
|
||||
report ? `工况报告:\n${report}` : null,
|
||||
condition.recommendation ? `已有建议:${condition.recommendation}` : null,
|
||||
condition.sessionId ? `sessionId:${condition.sessionId}` : null,
|
||||
"",
|
||||
"请先说明当前判断,再列出需要人工确认的事项和建议动作。"
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
export function createAlertQueueConversationPrompt({
|
||||
alerts,
|
||||
conditions
|
||||
}: {
|
||||
alerts: WorkbenchAlert[];
|
||||
conditions: ScheduledConditionItem[];
|
||||
}) {
|
||||
const alertSummaries = alerts.map((alert, index) => {
|
||||
const condition = findConditionForAlert(alert, conditions);
|
||||
const baseSummary = [
|
||||
`${index + 1}. ${alert.title}`,
|
||||
"状态:待复核",
|
||||
`时间:${alert.time}`,
|
||||
`描述:${alert.description}`
|
||||
];
|
||||
|
||||
if (!condition) {
|
||||
return baseSummary.join("\n");
|
||||
}
|
||||
|
||||
return [
|
||||
...baseSummary,
|
||||
`关联工况:${condition.title}`,
|
||||
`工况状态:${statusLabels[condition.status]}`,
|
||||
`风险等级:${getRiskLabel(condition.riskLevel)}`,
|
||||
condition.detail ? `工况详情:${condition.detail}` : null,
|
||||
condition.evidence?.length ? `关键证据:\n${condition.evidence.map((item, evidenceIndex) => `${evidenceIndex + 1}. ${item}`).join("\n")}` : null,
|
||||
condition.recommendation ? `已有建议:${condition.recommendation}` : null,
|
||||
condition.kind === "condition" && condition.analysisInsight?.solutionOptions?.length
|
||||
? `已有处置方案:\n${condition.analysisInsight.solutionOptions.map((option, optionIndex) => `${optionIndex + 1}. ${option.title}:${option.action};代价:${option.tradeoff}`).join("\n")}`
|
||||
: null
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
});
|
||||
|
||||
return [
|
||||
"请作为排水管网调度 Agent,汇总当前待复核工况队列。",
|
||||
"",
|
||||
"目标:",
|
||||
"1. 汇总这一组待复核工况之间的关联关系和处理顺序。",
|
||||
"2. 列出支撑判断的关键证据和不确定性。",
|
||||
"3. 给出处置方案,包含建议动作、适用条件、影响范围和需要人工确认的事项。",
|
||||
"4. 不要自动执行阀门、泵站或工单动作,先给调度员确认。",
|
||||
"",
|
||||
`待复核工况数量:${alerts.length}`,
|
||||
"",
|
||||
"待复核工况上下文:",
|
||||
alertSummaries.join("\n\n"),
|
||||
"",
|
||||
"请先给出总体判断,再按工况列出处置建议,最后列出需要补充核查的数据。"
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function findConditionForAlert(alert: WorkbenchAlert, conditions: ScheduledConditionItem[]) {
|
||||
if (!alert.id.startsWith(SCHEDULED_CONDITION_ALERT_ID_PREFIX)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const conditionId = alert.id.slice(SCHEDULED_CONDITION_ALERT_ID_PREFIX.length);
|
||||
return conditions.find((condition) => condition.id === conditionId) ?? null;
|
||||
}
|
||||
|
||||
function formatTime(value: string) {
|
||||
const match = value.match(/T(\d{2}:\d{2})/);
|
||||
return match?.[1] ?? value;
|
||||
}
|
||||
|
||||
function getRiskLabel(riskLevel: ScheduledConditionItem["riskLevel"]) {
|
||||
if (riskLevel === "critical") {
|
||||
return "需重点复核";
|
||||
}
|
||||
|
||||
if (riskLevel === "attention") {
|
||||
return "关注";
|
||||
}
|
||||
|
||||
return "正常";
|
||||
}
|
||||
Reference in New Issue
Block a user