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 { OvertimeRecord } from '@/lib/overtime';
import type { DictItem } from '@/lib/requirement';
import { calcDuration } from '@/lib/overtime';
import { scheduleSaveWithOptimisticRollback } from '@/lib/optimistic-persistence';
import { loadServerData, saveServerData, SERVER_DATA_CACHE_MS } from '@/lib/server-data';
const PRESET_REASONS: DictItem[] = [
@@ -36,10 +37,6 @@ interface OvertimeState {
deleteReason: (id: string) => void;
}
function saveStored(data: { records: OvertimeRecord[]; reasons: DictItem[] }) {
saveServerData('overtime', data).catch(() => {});
}
async function loadStored(): Promise<{ records: OvertimeRecord[]; reasons: DictItem[] } | null> {
try {
return await loadServerData<{ records: OvertimeRecord[]; reasons: DictItem[] }>('overtime');
@@ -62,6 +59,7 @@ export const useOvertimeStore = create<OvertimeState>((set, get) => ({
},
createRecord: (data) => {
const previous = get().records;
const duration = calcDuration(data.startTime, data.endTime);
const record: OvertimeRecord = {
...data,
@@ -69,13 +67,19 @@ export const useOvertimeStore = create<OvertimeState>((set, get) => ({
duration,
createdAt: new Date().toISOString().slice(0, 10),
};
const records = [...get().records, record];
const records = [...previous, record];
set({ records, loaded: true });
saveStored({ records, reasons: get().reasons });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('overtime', { records, reasons: get().reasons }),
expected: records,
getCurrent: () => get().records,
rollback: () => set({ records: previous, loaded: true }),
});
},
updateRecord: (id, data) => {
const records = get().records.map((r) => {
const previous = get().records;
const records = previous.map((r) => {
if (r.id !== id) return r;
const updated = { ...r, ...data };
if (data.startTime || data.endTime) {
@@ -84,31 +88,60 @@ export const useOvertimeStore = create<OvertimeState>((set, get) => ({
return updated;
});
set({ records, loaded: true });
saveStored({ records, reasons: get().reasons });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('overtime', { records, reasons: get().reasons }),
expected: records,
getCurrent: () => get().records,
rollback: () => set({ records: previous, loaded: true }),
});
},
deleteRecord: (id) => {
const records = get().records.filter((r) => r.id !== id);
const previous = get().records;
const records = previous.filter((r) => r.id !== id);
set({ records, loaded: true });
saveStored({ records, reasons: get().reasons });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('overtime', { records, reasons: get().reasons }),
expected: records,
getCurrent: () => get().records,
rollback: () => set({ records: previous, loaded: true }),
});
},
addReason: (name) => {
const previous = get().reasons;
const item: DictItem = { id: `reason-${Date.now()}`, name, createdAt: new Date().toISOString().slice(0, 10) };
const reasons = [...get().reasons, item];
const reasons = [...previous, item];
set({ reasons, loaded: true });
saveStored({ records: get().records, reasons });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('overtime', { records: get().records, reasons }),
expected: reasons,
getCurrent: () => get().reasons,
rollback: () => set({ reasons: previous, loaded: true }),
});
},
updateReason: (id, name) => {
const reasons = get().reasons.map((r) => r.id === id ? { ...r, name } : r);
const previous = get().reasons;
const reasons = previous.map((r) => r.id === id ? { ...r, name } : r);
set({ reasons, loaded: true });
saveStored({ records: get().records, reasons });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('overtime', { records: get().records, reasons }),
expected: reasons,
getCurrent: () => get().reasons,
rollback: () => set({ reasons: previous, loaded: true }),
});
},
deleteReason: (id) => {
const reasons = get().reasons.filter((r) => r.id !== id);
const previous = get().reasons;
const reasons = previous.filter((r) => r.id !== id);
set({ reasons, loaded: true });
saveStored({ records: get().records, reasons });
scheduleSaveWithOptimisticRollback({
save: () => saveServerData('overtime', { records: get().records, reasons }),
expected: reasons,
getCurrent: () => get().reasons,
rollback: () => set({ reasons: previous, loaded: true }),
});
},
}));