fix: 根治保存必弹磁盘冲突——编辑不再污染乐观锁基准

- editorStore 新增 baseUpdatedAt(磁盘基准版本),仅 loadFlow/syncSavedFlow 更新
- 保存 expectedUpdatedAt 改用 baseUpdatedAt;此前用每次编辑都会拨快的
  flow.meta.updatedAt 做比对,导致任何编辑后首次保存必然误判冲突
- 移除 flows:save/get 诊断日志(保留内容一致放行逻辑)
This commit is contained in:
yu 2026-10-29 15:33:07 +08:00
parent 778f69e14d
commit b40fcbdd35
3 changed files with 23 additions and 5 deletions

View File

@ -127,7 +127,9 @@ function registerIpc(): void {
for (const flow of createExampleFlows(url)) saveFlow(flow);
return listFlows();
});
ipcMain.handle("flows:get", (_event, id: string) => getFlow(id));
ipcMain.handle("flows:get", (_event, id: string) => {
return getFlow(id);
});
ipcMain.handle("flows:save", (_event, flow: FlowDefinition, opts?: FlowSaveOptions): FlowSaveResult => {
if (!opts?.force && opts?.expectedUpdatedAt) {
let disk: FlowDefinition | null = null;

View File

@ -65,7 +65,9 @@ export function EditorPage({ catalog, onToast, onBack }: EditorPageProps) {
setSaving(true);
try {
const result = await window.rpa.flows.save(current, {
expectedUpdatedAt: current.meta?.updatedAt || undefined
// 用「磁盘基准版本」做乐观锁,不能用 current.meta.updatedAt——
// 每次本地编辑都会拨快它,会导致每次保存都误判磁盘冲突
expectedUpdatedAt: useEditorStore.getState().baseUpdatedAt || undefined
});
if (result.conflict) {
if (opts?.interactive) {

View File

@ -57,6 +57,10 @@ function uniqueOutputVar(base: string, nodes: FlowNode[]): string {
interface EditorState {
flow: FlowDefinition | null;
/** 乐观锁基准:最近一次从磁盘加载/保存成功返回的 meta.updatedAt。
* 保存时以此为 expectedUpdatedAt;不能直接用 flow.meta.updatedAt,
* 因为 commit() 每次编辑都会拨快它,会导致每次保存都误判磁盘冲突。 */
baseUpdatedAt: string | null;
catalog: Catalog | null;
selectedNodeId: string | null;
dirty: boolean;
@ -104,6 +108,7 @@ export const useEditorStore = create<EditorState>((set, get) => {
return {
flow: null,
baseUpdatedAt: null,
catalog: null,
selectedNodeId: null,
dirty: false,
@ -116,7 +121,14 @@ export const useEditorStore = create<EditorState>((set, get) => {
// 切换流程时清掉上一次的运行面板状态,避免显示旧流程的运行结果
const runState = useRunStore.getState();
if (!runState.running) runState.clear();
set({ flow, selectedNodeId: null, dirty: false, past: [], future: [] });
set({
flow,
selectedNodeId: null,
dirty: false,
past: [],
future: [],
baseUpdatedAt: flow.meta?.updatedAt ?? null
});
},
selectNode: (id) => set({ selectedNodeId: id }),
@ -340,9 +352,11 @@ export const useEditorStore = create<EditorState>((set, get) => {
},
markSaved: () => set({ dirty: false }),
syncSavedFlow: (flow) => set({ flow, dirty: false }),
syncSavedFlow: (flow) =>
set({ flow, dirty: false, baseUpdatedAt: flow.meta?.updatedAt ?? null }),
reset: () => set({ flow: null, selectedNodeId: null, dirty: false, past: [], future: [] })
reset: () =>
set({ flow: null, baseUpdatedAt: null, selectedNodeId: null, dirty: false, past: [], future: [] })
};
});