fix(data): 增加全局保存失败保护

This commit is contained in:
Script Generator
2026-07-03 19:36:18 +08:00
parent 516a5f1b63
commit b4a3c990b4
17 changed files with 727 additions and 132 deletions

View File

@@ -3,6 +3,7 @@
import { create } from 'zustand';
import type { Requirement, RequirementStatus, Effort, DictItem, SourceType, SourceTarget } from '@/lib/requirement';
import type { Priority } from '@/lib/derive';
import { scheduleSaveWithOptimisticRollback } from '@/lib/optimistic-persistence';
import { loadServerData, saveServerData, SERVER_DATA_CACHE_MS } from '@/lib/server-data';
interface RequirementState {
@@ -55,12 +56,6 @@ const PRESET_PLATFORMS: DictItem[] = [
const MOCK_REQUIREMENTS: Requirement[] = [];
let lastRequirementsFetchAt = 0;
// --- Server persistence helpers ---
function saveStored(state: { requirements: Requirement[]; sourceTargets: SourceTarget[]; types: DictItem[]; platforms: DictItem[] }) {
saveServerData('requirements', state).catch(() => {});
}
async function loadStored(): Promise<{ requirements: Requirement[]; sourceTargets: SourceTarget[]; types: DictItem[]; platforms: DictItem[] } | null> {
try {
return await loadServerData<{ requirements: Requirement[]; sourceTargets: SourceTarget[]; types: DictItem[]; platforms: DictItem[] }>('requirements');
@@ -103,21 +98,36 @@ export const useRequirementStore = create<RequirementState>((set, get) => {
};
const updated = [...requirements, newReq];
set({ requirements: updated, loaded: true });
saveStored({ requirements: updated, sourceTargets, types, platforms });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('requirements', { requirements: updated, sourceTargets, types, platforms }),
expected: updated,
getCurrent: () => get().requirements,
rollback: () => set({ requirements, loaded: true }),
});
},
updateRequirement: (id, data) => {
const { requirements, sourceTargets, types, platforms } = get();
const updated = requirements.map((r) => (r.id === id ? { ...r, ...data } : r));
set({ requirements: updated, loaded: true });
saveStored({ requirements: updated, sourceTargets, types, platforms });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('requirements', { requirements: updated, sourceTargets, types, platforms }),
expected: updated,
getCurrent: () => get().requirements,
rollback: () => set({ requirements, loaded: true }),
});
},
deleteRequirement: (id) => {
const { requirements, sourceTargets, types, platforms } = get();
const updated = requirements.filter((r) => r.id !== id);
set({ requirements: updated, loaded: true });
saveStored({ requirements: updated, sourceTargets, types, platforms });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('requirements', { requirements: updated, sourceTargets, types, platforms }),
expected: updated,
getCurrent: () => get().requirements,
rollback: () => set({ requirements, loaded: true }),
});
},
// --- SourceTarget dict ---
@@ -126,21 +136,36 @@ export const useRequirementStore = create<RequirementState>((set, get) => {
const item: SourceTarget = { id: `st-${Date.now()}`, name, sourceType, createdAt: new Date().toISOString().slice(0, 10) };
const updated = [...sourceTargets, item];
set({ sourceTargets: updated, loaded: true });
saveStored({ requirements, sourceTargets: updated, types, platforms });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('requirements', { requirements, sourceTargets: updated, types, platforms }),
expected: updated,
getCurrent: () => get().sourceTargets,
rollback: () => set({ sourceTargets, loaded: true }),
});
},
updateSourceTarget: (id, name) => {
const { requirements, sourceTargets, types, platforms } = get();
const updated = sourceTargets.map((s) => (s.id === id ? { ...s, name } : s));
set({ sourceTargets: updated, loaded: true });
saveStored({ requirements, sourceTargets: updated, types, platforms });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('requirements', { requirements, sourceTargets: updated, types, platforms }),
expected: updated,
getCurrent: () => get().sourceTargets,
rollback: () => set({ sourceTargets, loaded: true }),
});
},
deleteSourceTarget: (id) => {
const { requirements, sourceTargets, types, platforms } = get();
const updated = sourceTargets.filter((s) => s.id !== id);
set({ sourceTargets: updated, loaded: true });
saveStored({ requirements, sourceTargets: updated, types, platforms });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('requirements', { requirements, sourceTargets: updated, types, platforms }),
expected: updated,
getCurrent: () => get().sourceTargets,
rollback: () => set({ sourceTargets, loaded: true }),
});
},
// --- Type dict ---
@@ -149,21 +174,36 @@ export const useRequirementStore = create<RequirementState>((set, get) => {
const item: DictItem = { id: `type-${Date.now()}`, name, createdAt: new Date().toISOString().slice(0, 10) };
const updated = [...types, item];
set({ types: updated, loaded: true });
saveStored({ requirements, sourceTargets, types: updated, platforms });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('requirements', { requirements, sourceTargets, types: updated, platforms }),
expected: updated,
getCurrent: () => get().types,
rollback: () => set({ types, loaded: true }),
});
},
updateType: (id, name) => {
const { requirements, sourceTargets, types, platforms } = get();
const updated = types.map((t) => (t.id === id ? { ...t, name } : t));
set({ types: updated, loaded: true });
saveStored({ requirements, sourceTargets, types: updated, platforms });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('requirements', { requirements, sourceTargets, types: updated, platforms }),
expected: updated,
getCurrent: () => get().types,
rollback: () => set({ types, loaded: true }),
});
},
deleteType: (id) => {
const { requirements, sourceTargets, types, platforms } = get();
const updated = types.filter((t) => t.id !== id);
set({ types: updated, loaded: true });
saveStored({ requirements, sourceTargets, types: updated, platforms });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('requirements', { requirements, sourceTargets, types: updated, platforms }),
expected: updated,
getCurrent: () => get().types,
rollback: () => set({ types, loaded: true }),
});
},
// --- Platform dict ---
@@ -172,21 +212,36 @@ export const useRequirementStore = create<RequirementState>((set, get) => {
const item: DictItem = { id: `platform-${Date.now()}`, name, createdAt: new Date().toISOString().slice(0, 10) };
const updated = [...platforms, item];
set({ platforms: updated, loaded: true });
saveStored({ requirements, sourceTargets, types, platforms: updated });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('requirements', { requirements, sourceTargets, types, platforms: updated }),
expected: updated,
getCurrent: () => get().platforms,
rollback: () => set({ platforms, loaded: true }),
});
},
updatePlatform: (id, name) => {
const { requirements, sourceTargets, types, platforms } = get();
const updated = platforms.map((p) => (p.id === id ? { ...p, name } : p));
set({ platforms: updated, loaded: true });
saveStored({ requirements, sourceTargets, types, platforms: updated });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('requirements', { requirements, sourceTargets, types, platforms: updated }),
expected: updated,
getCurrent: () => get().platforms,
rollback: () => set({ platforms, loaded: true }),
});
},
deletePlatform: (id) => {
const { requirements, sourceTargets, types, platforms } = get();
const updated = platforms.filter((p) => p.id !== id);
set({ platforms: updated, loaded: true });
saveStored({ requirements, sourceTargets, types, platforms: updated });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('requirements', { requirements, sourceTargets, types, platforms: updated }),
expected: updated,
getCurrent: () => get().platforms,
rollback: () => set({ platforms, loaded: true }),
});
},
};
});