refactor(workbench): simplify appointment tasks
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user