refactor(workbench): simplify appointment tasks
This commit is contained in:
@@ -156,29 +156,21 @@ const taskPermissionsSchema = z.object({
|
||||
can_decide_commands: z.boolean(),
|
||||
can_execute_commands: z.boolean()
|
||||
}).strict();
|
||||
const externalWorkOrderStageSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
const externalWorkOrderActivitySchema = z.object({
|
||||
sequence: z.number().int().positive(),
|
||||
occurred_at: workbenchDateTimeSchema,
|
||||
actor: z.string().min(1),
|
||||
kind: z.enum(["created", "updated", "feedback"]),
|
||||
title: z.string().min(1),
|
||||
status: z.enum(["done", "current", "pending", "blocked", "skipped"]),
|
||||
owner: z.string().min(1),
|
||||
time_label: z.string().min(1),
|
||||
description: z.string().min(1)
|
||||
detail: z.string().min(1)
|
||||
}).strict();
|
||||
const externalWorkOrderSubtaskSchema = z.object({
|
||||
id: z.string().min(1),
|
||||
title: z.string().min(1),
|
||||
summary: z.string().min(1),
|
||||
status: z.enum(["pending", "running", "blocked", "completed", "cancelled"]),
|
||||
scheduled_at: workbenchDateTimeSchema,
|
||||
location: z.string().min(1),
|
||||
assignee: z.string().min(1),
|
||||
duration_minutes: z.number().int().positive(),
|
||||
reply_window_minutes: z.number().int().nonnegative(),
|
||||
stages: z.array(externalWorkOrderStageSchema).min(1),
|
||||
reply_requirements: z.array(z.string().min(1))
|
||||
}).strict();
|
||||
const externalWorkOrderSnapshotSchema = z.object({
|
||||
const externalWorkOrderSnapshotSchema = z.preprocess((value) => {
|
||||
if (value && typeof value === "object" && !Array.isArray(value)) {
|
||||
const { subtasks: _legacySubtasks, ...flat } = value as Record<string, unknown>;
|
||||
return flat;
|
||||
}
|
||||
return value;
|
||||
}, z.object({
|
||||
work_order_id: z.string().uuid(),
|
||||
revision: z.number().int().positive(),
|
||||
title: z.string().min(1),
|
||||
@@ -190,10 +182,11 @@ const externalWorkOrderSnapshotSchema = z.object({
|
||||
work_order_type: z.enum(["general", "water_quality_complaint", "burst_report"]),
|
||||
location: z.string().min(1).nullable(),
|
||||
scenario_code: z.string().min(1).nullable(),
|
||||
subtasks: z.array(externalWorkOrderSubtaskSchema).min(1).nullable(),
|
||||
feedback_requirements: z.array(z.string().min(1)).default([]),
|
||||
activity: z.array(externalWorkOrderActivitySchema).default([]),
|
||||
origin: z.literal("synthetic"),
|
||||
updated_at: workbenchDateTimeSchema
|
||||
}).strict();
|
||||
}).strict());
|
||||
const operationalTaskSchema = taskSummarySchema.extend({
|
||||
latest_plan: planSnapshotSchema.nullable(),
|
||||
latest_report: reportRevisionSchema.nullable(),
|
||||
|
||||
@@ -260,47 +260,11 @@ describe("workbench runtime API client", () => {
|
||||
source: "生产工单系统",
|
||||
dispatcher: "调度中心",
|
||||
priority: "urgent",
|
||||
subtasks: [{
|
||||
id: "wo-1-subtask-1",
|
||||
title: "阀门开度复核",
|
||||
summary: "现场复核阀门开度。",
|
||||
status: "completed",
|
||||
scheduled_at: "2026-09-17T03:00:00Z",
|
||||
location: "北区阀门 V-1",
|
||||
assignee: "北区班组",
|
||||
duration_minutes: 45,
|
||||
reply_window_minutes: 20,
|
||||
stages: [
|
||||
{
|
||||
id: "dispatch",
|
||||
sequence: 1,
|
||||
title: "派发",
|
||||
status: "done",
|
||||
owner: "调度中心",
|
||||
time_label: "10:50",
|
||||
description: "已派发。"
|
||||
},
|
||||
{
|
||||
id: "execution",
|
||||
sequence: 2,
|
||||
title: "执行中",
|
||||
status: "done",
|
||||
owner: "北区班组",
|
||||
time_label: "11:00—11:45",
|
||||
description: "现场执行中。"
|
||||
},
|
||||
{
|
||||
id: "reply",
|
||||
sequence: 3,
|
||||
title: "待复令",
|
||||
status: "done",
|
||||
owner: "北区班组",
|
||||
time_label: "11:45—12:05",
|
||||
description: "等待复令。"
|
||||
}
|
||||
],
|
||||
reply_requirements: ["回传阀门开度"]
|
||||
}]
|
||||
location: "北区阀门 V-1",
|
||||
assignee: "北区班组",
|
||||
feedback_requirements: ["回传阀门开度"],
|
||||
activity: [{ sequence: 1, occurred_at: "2026-09-17T02:30:00Z", actor: "调度中心",
|
||||
kind: "created", title: "工单创建", detail: "等待现场复核。" }]
|
||||
};
|
||||
const snapshot = parseWorkbenchRuntimeSnapshot({
|
||||
...runtimeSnapshot,
|
||||
@@ -315,14 +279,10 @@ describe("workbench runtime API client", () => {
|
||||
code: "WO-20260917-001",
|
||||
status: "completed",
|
||||
source: "生产工单系统",
|
||||
subtasks: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: "wo-1-subtask-1",
|
||||
stages: expect.arrayContaining([
|
||||
expect.objectContaining({ id: "execution", sequence: 2, timeLabel: "11:00—11:45" })
|
||||
])
|
||||
})
|
||||
])
|
||||
location: "北区阀门 V-1",
|
||||
assignee: "北区班组",
|
||||
feedbackRequirements: ["回传阀门开度"],
|
||||
activity: [expect.objectContaining({ title: "工单创建" })]
|
||||
})]);
|
||||
});
|
||||
|
||||
|
||||
@@ -87,50 +87,13 @@ const runtimeLatestAnalysisSchema = z.object({
|
||||
map_payload: z.unknown().nullable()
|
||||
});
|
||||
|
||||
const runtimeWorkOrderStageSchema = z.object({
|
||||
id: z.string().trim().min(1),
|
||||
const runtimeWorkOrderActivitySchema = z.object({
|
||||
sequence: z.number().int().positive(),
|
||||
occurred_at: z.string().datetime({ offset: true }),
|
||||
actor: z.string().trim().min(1),
|
||||
kind: z.enum(["created", "updated", "feedback"]),
|
||||
title: z.string().trim().min(1),
|
||||
status: z.enum(["done", "current", "pending", "blocked", "skipped"]),
|
||||
owner: z.string().trim().min(1),
|
||||
time_label: z.string().trim().min(1),
|
||||
description: z.string().trim().min(1)
|
||||
});
|
||||
|
||||
const runtimeWorkOrderSubtaskSchema = z.object({
|
||||
id: z.string().trim().min(1),
|
||||
title: z.string().trim().min(1),
|
||||
summary: z.string().trim().min(1),
|
||||
status: z.enum(["pending", "running", "blocked", "completed", "cancelled"]),
|
||||
scheduled_at: z.string().datetime({ offset: true }),
|
||||
location: z.string().trim().min(1),
|
||||
assignee: z.string().trim().min(1),
|
||||
duration_minutes: z.number().int().positive(),
|
||||
reply_window_minutes: z.number().int().positive(),
|
||||
stages: z.array(runtimeWorkOrderStageSchema).min(1),
|
||||
reply_requirements: z.array(z.string().trim().min(1))
|
||||
}).superRefine((subtask, context) => {
|
||||
if (subtask.stages.some((stage, index) => stage.sequence !== index + 1)) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "子任务阶段序号必须从 1 连续递增",
|
||||
path: ["stages"]
|
||||
});
|
||||
}
|
||||
if (new Set(subtask.stages.map((stage) => stage.id)).size !== subtask.stages.length) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "子任务阶段编号不能重复",
|
||||
path: ["stages"]
|
||||
});
|
||||
}
|
||||
if (subtask.stages.filter((stage) => stage.status === "current").length > 1) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "单个子任务最多只能有一个当前阶段",
|
||||
path: ["stages"]
|
||||
});
|
||||
}
|
||||
detail: z.string().trim().min(1)
|
||||
});
|
||||
|
||||
const runtimeWorkOrderSchema = z.object({
|
||||
@@ -147,16 +110,11 @@ const runtimeWorkOrderSchema = z.object({
|
||||
duration_minutes: z.number().int().positive(),
|
||||
source: z.string().trim().min(1),
|
||||
dispatcher: z.string().trim().min(1),
|
||||
location: z.string().trim().min(1).nullable().default(null),
|
||||
assignee: z.string().trim().min(1).nullable().default(null),
|
||||
priority: z.enum(["normal", "urgent"]),
|
||||
subtasks: z.array(runtimeWorkOrderSubtaskSchema).min(1)
|
||||
}).superRefine((workOrder, context) => {
|
||||
if (new Set(workOrder.subtasks.map((subtask) => subtask.id)).size !== workOrder.subtasks.length) {
|
||||
context.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "工单子任务编号不能重复",
|
||||
path: ["subtasks"]
|
||||
});
|
||||
}
|
||||
feedback_requirements: z.array(z.string().trim().min(1)).default([]),
|
||||
activity: z.array(runtimeWorkOrderActivitySchema).default([])
|
||||
});
|
||||
|
||||
const runtimeSnapshotSchema = z.object({
|
||||
@@ -278,26 +236,16 @@ export function parseWorkbenchRuntimeSnapshot(payload: unknown): WorkbenchRuntim
|
||||
dispatcher: workOrder.dispatcher,
|
||||
priority: workOrder.priority,
|
||||
dataSource: "backend",
|
||||
subtasks: workOrder.subtasks.map((subtask) => ({
|
||||
id: subtask.id,
|
||||
title: subtask.title,
|
||||
summary: subtask.summary,
|
||||
status: subtask.status,
|
||||
scheduledAt: subtask.scheduled_at,
|
||||
location: subtask.location,
|
||||
assignee: subtask.assignee,
|
||||
durationMinutes: subtask.duration_minutes,
|
||||
replyWindowMinutes: subtask.reply_window_minutes,
|
||||
stages: subtask.stages.map((stage) => ({
|
||||
id: stage.id,
|
||||
sequence: stage.sequence,
|
||||
title: stage.title,
|
||||
status: stage.status,
|
||||
owner: stage.owner,
|
||||
timeLabel: stage.time_label,
|
||||
description: stage.description
|
||||
})),
|
||||
replyRequirements: subtask.reply_requirements
|
||||
location: workOrder.location ?? undefined,
|
||||
assignee: workOrder.assignee ?? undefined,
|
||||
feedbackRequirements: workOrder.feedback_requirements,
|
||||
activity: workOrder.activity.map((entry) => ({
|
||||
sequence: entry.sequence,
|
||||
occurredAt: entry.occurred_at,
|
||||
actor: entry.actor,
|
||||
kind: entry.kind,
|
||||
title: entry.title,
|
||||
detail: entry.detail
|
||||
}))
|
||||
}))
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user