refactor(workbench): simplify appointment tasks

This commit is contained in:
2026-09-24 12:01:11 +08:00
parent 436d5500f1
commit ddf953b641
28 changed files with 443 additions and 1384 deletions
@@ -26,27 +26,11 @@ const workOrder: ScheduledConditionItem = {
source: "调度方案",
dispatcher: "调度",
priority: "urgent",
subtasks: [{
id: "work-1-subtask-1",
title: "调整阀门开度",
summary: "现场调整并记录开度。",
status: "running",
scheduledAt: "2026-08-19T10:00:00.000Z",
location: "边界阀门 BV-07",
assignee: "班组",
durationMinutes: 40,
replyWindowMinutes: 20,
stages: [{
id: "execution",
sequence: 1,
title: "执行",
status: "current",
owner: "班组",
timeLabel: "10:00 - 10:40",
description: "现场调整阀门。"
}],
replyRequirements: ["回传阀门开度"]
}]
location: "边界阀门 BV-07",
assignee: "班组",
feedbackRequirements: ["回传阀门开度"],
activity: [{ sequence: 1, occurredAt: "2026-08-19T10:00:00.000Z", actor: "班组",
kind: "updated", title: "现场反馈", detail: "现场调整阀门。" }]
};
describe("operational timeline model", () => {
@@ -60,8 +44,8 @@ describe("operational timeline model", () => {
expect(events[0].correlationId).toBe(events[1].correlationId);
});
it("does not invent actual execution times for backend work orders", () => {
const events = createOperationalEvents([{ ...workOrder, dataSource: "backend" }]);
it("does not invent actual execution times without activity", () => {
const events = createOperationalEvents([{ ...workOrder, dataSource: "backend", activity: [] }]);
expect(events).toHaveLength(1);
expect(events[0]).toMatchObject({ lane: "plan", actualTime: null });
@@ -195,13 +195,10 @@ function createConditionEvents(item: ScheduledConditionRecord): OperationalEvent
}
function createWorkOrderEvents(item: ScheduledWorkOrderItem): OperationalEvent[] {
const subtask = getRepresentativeWorkOrderSubtask(item);
const planEvent: OperationalEvent = {
id: `plan-${item.id}`,
title: item.title,
description: subtask
? `${subtask.title} · ${subtask.location} · ${subtask.assignee}`
: item.summary,
description: [item.summary, item.location, item.assignee].filter(Boolean).join(" · "),
lane: "plan",
status: "planned",
importance: item.priority === "urgent" ? "important" : "normal",
@@ -215,32 +212,16 @@ function createWorkOrderEvents(item: ScheduledWorkOrderItem): OperationalEvent[]
mapTargets: getWorkOrderMapTargets(item)
};
if (item.dataSource === "backend") {
return [planEvent];
}
if (item.status === "pending") {
return [planEvent];
}
const start = Date.parse(item.scheduledAt);
const actualTime = new Date(
item.status === "running" ? start + 3 * 60_000 : start + (item.durationMinutes ?? 0) * 60_000
).toISOString();
const actualEvent: OperationalEvent = {
return [planEvent, ...item.activity.map((entry): OperationalEvent => ({
...planEvent,
id: `actual-${item.id}`,
id: `activity-${item.id}-${entry.sequence}`,
title: entry.title,
lane: "actual",
status: "executed",
actualTime,
cursorTime: actualTime,
description:
item.status === "running"
? `${subtask?.assignee ?? "执行班组"}正在执行,位置:${subtask?.location ?? "待确认"}`
: `${subtask?.assignee ?? "执行班组"}已完成现场执行,等待调度复令`
};
return [planEvent, actualEvent];
actualTime: entry.occurredAt,
cursorTime: entry.occurredAt,
description: entry.detail
}))];
}
function getConditionMapTargets(item: ScheduledConditionRecord): FeatureTarget[] {
@@ -254,32 +235,10 @@ function getConditionMapTargets(item: ScheduledConditionRecord): FeatureTarget[]
}
function getWorkOrderMapTargets(item: ScheduledWorkOrderItem): FeatureTarget[] {
const targets = item.subtasks.flatMap((subtask): FeatureTarget[] => {
if (subtask.location.includes("阀")) {
return [{ sourceId: "valves", featureId: extractAssetId(subtask.location) }];
}
if (subtask.location.includes("流量计")) {
return [{ sourceId: "scada", featureId: extractAssetId(subtask.location) }];
}
return [];
});
return targets.filter((target, index) =>
targets.findIndex((candidate) =>
candidate.sourceId === target.sourceId && candidate.featureId === target.featureId
) === index
);
}
function getRepresentativeWorkOrderSubtask(item: ScheduledWorkOrderItem) {
return item.subtasks.find((subtask) => subtask.status === "running")
?? item.subtasks.find((subtask) => subtask.status === "blocked")
?? item.subtasks.find((subtask) =>
subtask.stages.some((stage) => stage.status === "current")
)
?? item.subtasks.find((subtask) => subtask.status === "pending")
?? item.subtasks.at(-1)
?? null;
const location = item.location ?? "";
if (location.includes("阀")) return [{ sourceId: "valves", featureId: extractAssetId(location) }];
if (location.includes("流量计")) return [{ sourceId: "scada", featureId: extractAssetId(location) }];
return [];
}
function extractAssetId(location: string) {