feat: improve schedule cleanup and handover

This commit is contained in:
Winnie
2026-08-20 19:12:44 +08:00
parent ae3b6d63e2
commit bc62034241
7 changed files with 306 additions and 63 deletions
@@ -368,6 +368,30 @@ export const useSchedulesStore = create<State & Actions>((set, get) => {
});
}
function applyServerUpdatedSchedule(serverUpdated: Schedule): void {
const validNodeIds = new Set(
serverUpdated.nodes.map((node) => node.node_id),
);
for (const nodeId of [...positionDrafts.keys()]) {
if (!validNodeIds.has(nodeId)) positionDrafts.delete(nodeId);
}
const updated = applyPositionDrafts(serverUpdated);
set((state) => ({
schedule: updated,
schedules: (() => {
const summary = { ...updated, nodes: [], edges: [] };
const index = state.schedules.findIndex(
(item) => item.schedule_id === updated.schedule_id,
);
if (index < 0) return [summary, ...state.schedules];
return state.schedules.map((item) =>
item.schedule_id === updated.schedule_id ? summary : item
);
})(),
positionDraftCount: positionDrafts.size,
}));
}
// Generic mutation wrapper: busy + try/catch + 412 + positionDraft cleanup + schedules list update
async function withMutation(
label: string,
@@ -379,29 +403,9 @@ export const useSchedulesStore = create<State & Actions>((set, get) => {
set({ busy: label });
try {
const serverUpdated = await action();
const validNodeIds = new Set(
serverUpdated.nodes.map((node) => node.node_id),
);
for (const nodeId of [...positionDrafts.keys()]) {
if (!validNodeIds.has(nodeId)) positionDrafts.delete(nodeId);
}
const updated = applyPositionDrafts(serverUpdated);
set((state) => ({
schedule: updated,
schedules: (() => {
const summary = { ...updated, nodes: [], edges: [] };
const index = state.schedules.findIndex(
(item) => item.schedule_id === updated.schedule_id,
);
if (index < 0) return [summary, ...state.schedules];
return state.schedules.map((item) =>
item.schedule_id === updated.schedule_id ? summary : item
);
})(),
positionDraftCount: positionDrafts.size,
}));
applyServerUpdatedSchedule(serverUpdated);
notify({ tone: "success", message: successMessage });
return updated;
return serverUpdated;
} catch (error) {
await handleError(error, `${successMessage}失败`);
return null;
@@ -946,18 +950,37 @@ export const useSchedulesStore = create<State & Actions>((set, get) => {
if (!schedule || !node || state.busy) return;
set({ contextMenu: null });
if (!window.confirm(`确定删除节点"${node.node_name}"吗?`)) return;
const updated = await withMutation(
"delete-node",
() =>
api.deleteScheduleNode(
set({ busy: "delete-node" });
try {
let updated: Schedule;
try {
// 先走普通删除:没有历史记录时不额外打扰用户。
updated = await api.deleteScheduleNode(
schedule.schedule_id,
node.node_id,
schedule.workflow_version,
),
"节点已删除",
);
if (updated && selectedNodeId === node.node_id) {
set({ selectedNodeId: null });
);
} catch (error) {
const requiresHistoryConfirmation =
error instanceof ApiRequestError
&& error.status === 409
&& error.code === "node_execution_history_exists";
if (!requiresHistoryConfirmation) throw error;
if (!window.confirm("该节点有运行日志,是否一并删除?")) return;
updated = await api.deleteScheduleNode(
schedule.schedule_id,
node.node_id,
schedule.workflow_version,
{ delete_execution_history: true },
);
}
applyServerUpdatedSchedule(updated);
if (selectedNodeId === node.node_id) set({ selectedNodeId: null });
notify({ tone: "success", message: "节点及其运行日志已删除" });
} catch (error) {
await handleError(error, "删除节点失败");
} finally {
set({ busy: null });
}
},