'use client'; import { create } from 'zustand'; import type { CreateTestCaseInput, TestCase, TestCaseStatus } from '@/lib/test-case'; import { generateCaseNo, normalizeTestCases } from '@/lib/test-case'; import { applyTestCaseTransition, normalizeTestCaseOnCreate } from '@/lib/test-case-workflow'; import { createEntityId, dedupeEntityIds } from '@/lib/entity-id'; import { loadServerData, saveServerData, SERVER_DATA_CACHE_MS } from '@/lib/server-data'; import { makeTestCaseCreatedActivity, makeTestCaseStatusActivity, } from '@/lib/work-activity-factory'; import { useWorkActivityStore } from './useWorkActivityStore'; function saveStored(items: TestCase[]) { saveServerData('test-cases', items).catch(() => {}); } let lastTestCasesFetchAt = 0; async function loadStored(): Promise { try { return await loadServerData('test-cases'); } catch {} return null; } interface TestCaseState { testCases: TestCase[]; loaded: boolean; fetchTestCases: (options?: { force?: boolean }) => Promise; createTestCase: (data: CreateTestCaseInput) => TestCase; createTestCases: (items: CreateTestCaseInput[]) => TestCase[]; updateTestCase: (id: string, data: Partial) => void; deleteTestCase: (id: string) => void; changeStatus: (id: string, to: TestCaseStatus, extra?: { failReason?: string; blockReason?: string }) => { ok: boolean; message?: string }; getByRequirement: (reqId: string) => TestCase[]; getByVersion: (versionId: string) => TestCase[]; getByAssignee: (assigneeId: string) => TestCase[]; } export const useTestCaseStore = create((set, get) => ({ testCases: [], loaded: false, fetchTestCases: async (options) => { if (!options?.force && get().loaded && Date.now() - lastTestCasesFetchAt < SERVER_DATA_CACHE_MS) return; const cached = await loadStored(); if (!options?.force && get().loaded && Date.now() - lastTestCasesFetchAt < SERVER_DATA_CACHE_MS) return; lastTestCasesFetchAt = Date.now(); if (cached) { const result = dedupeEntityIds(normalizeTestCases(cached), 'tc'); if (result.changed) saveStored(result.items); set({ testCases: result.items, loaded: true }); } else { set({ loaded: true }); } }, createTestCase: (data) => { const list = get().testCases; const now = new Date().toISOString(); const tc: TestCase = normalizeTestCaseOnCreate({ ...data, id: createEntityId('tc'), caseNo: generateCaseNo(list), status: 'pending', createdAt: now, updatedAt: now, } as TestCase); const updated = [...list, tc]; set({ testCases: updated, loaded: true }); saveStored(updated); useWorkActivityStore.getState().addActivity(makeTestCaseCreatedActivity(tc, tc.createdBy)); return tc; }, createTestCases: (items) => { if (items.length === 0) return []; let list = get().testCases; const now = new Date().toISOString(); const created: TestCase[] = []; for (const data of items) { const tc: TestCase = normalizeTestCaseOnCreate({ ...data, id: createEntityId('tc'), caseNo: generateCaseNo(list), status: 'pending', createdAt: now, updatedAt: now, } as TestCase); created.push(tc); list = [...list, tc]; } set({ testCases: list, loaded: true }); saveStored(list); created.forEach((tc) => { useWorkActivityStore.getState().addActivity(makeTestCaseCreatedActivity(tc, tc.createdBy)); }); return created; }, updateTestCase: (id, data) => { const updated = get().testCases.map((c) => c.id === id ? { ...c, ...data, aiDraft: false, updatedAt: new Date().toISOString() } : c, ); set({ testCases: updated, loaded: true }); saveStored(updated); }, deleteTestCase: (id) => { const updated = get().testCases.filter((c) => c.id !== id); set({ testCases: updated, loaded: true }); saveStored(updated); }, changeStatus: (id, to, extra) => { const tc = get().testCases.find((c) => c.id === id); if (!tc) return { ok: false, message: '用例不存在' }; const result = applyTestCaseTransition(tc, to, { now: new Date(), failReason: extra?.failReason, blockReason: extra?.blockReason, }); if (!result.ok || !result.patch) return { ok: false, message: result.message }; get().updateTestCase(id, result.patch); const actorId = tc.assigneeId || tc.executedBy || tc.createdBy; const activity = makeTestCaseStatusActivity(tc, tc.status, to, actorId); if (activity) useWorkActivityStore.getState().addActivity(activity); return { ok: true }; }, getByRequirement: (reqId) => { return get().testCases.filter((c) => c.requirementId === reqId); }, getByVersion: (versionId) => { return get().testCases.filter((c) => c.versionId === versionId); }, getByAssignee: (assigneeId) => { return get().testCases.filter((c) => c.assigneeId === assigneeId); }, }));