'use client'; import { create } from 'zustand'; import type { Bug, BugStatus, BugLog } from '@/lib/bug'; import { generateBugNo } from '@/lib/bug'; import { applyBugTransition } from '@/lib/bug-workflow'; import { loadServerData, saveServerData, SERVER_DATA_CACHE_MS } from '@/lib/server-data'; import { makeBugCreatedActivity, makeBugStatusActivity, makeBugTransferredActivity, } from '@/lib/work-activity-factory'; import { useWorkActivityStore } from './useWorkActivityStore'; function saveStored(items: Bug[]) { saveServerData('bugs', items).catch(() => {}); } let lastBugsFetchAt = 0; async function loadStored(): Promise { try { return await loadServerData('bugs'); } catch {} return null; } function makeLog(action: BugLog['action'], operator: string, from?: string, to?: string, remark?: string): BugLog { return { id: `log-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`, action, fromValue: from, toValue: to, operator, remark, createdAt: new Date().toISOString() }; } interface BugState { bugs: Bug[]; loaded: boolean; fetchBugs: (options?: { force?: boolean }) => Promise; createBug: (data: Omit, operator: string) => Bug; updateBug: (id: string, data: Partial) => void; deleteBug: (id: string) => void; changeStatus: (id: string, to: BugStatus, operator: string, extra?: { resolution?: string }) => { ok: boolean; message?: string }; transferBug: (id: string, newAssigneeId: string, operator: string, remark?: string) => { ok: boolean; message?: string }; getByTestCase: (caseId: string) => Bug[]; getByVersion: (versionId: string) => Bug[]; getByAssignee: (assigneeId: string) => Bug[]; } export const useBugStore = create((set, get) => ({ bugs: [], loaded: false, fetchBugs: async (options) => { if (!options?.force && get().loaded && Date.now() - lastBugsFetchAt < SERVER_DATA_CACHE_MS) return; const cached = await loadStored(); if (!options?.force && get().loaded && Date.now() - lastBugsFetchAt < SERVER_DATA_CACHE_MS) return; lastBugsFetchAt = Date.now(); set({ bugs: cached ?? [], loaded: true }); }, createBug: (data, operator) => { const list = get().bugs; const now = new Date().toISOString(); const log = makeLog('create', operator); const bug: Bug = { ...data, id: `bug-${Date.now()}`, bugNo: generateBugNo(list), status: 'open', logs: [log], createdAt: now, updatedAt: now, }; const updated = [...list, bug]; set({ bugs: updated, loaded: true }); saveStored(updated); useWorkActivityStore.getState().addActivity(makeBugCreatedActivity(bug, operator)); return bug; }, updateBug: (id, data) => { const updated = get().bugs.map((b) => b.id === id ? { ...b, ...data, updatedAt: new Date().toISOString() } : b, ); set({ bugs: updated, loaded: true }); saveStored(updated); }, deleteBug: (id) => { const updated = get().bugs.filter((b) => b.id !== id); set({ bugs: updated, loaded: true }); saveStored(updated); }, changeStatus: (id, to, operator, extra) => { const bug = get().bugs.find((b) => b.id === id); if (!bug) return { ok: false, message: 'Bug不存在' }; const result = applyBugTransition(bug, to, operator, { now: new Date(), resolution: extra?.resolution, }); if (!result.ok || !result.patch) return { ok: false, message: result.message }; get().updateBug(id, result.patch); const activity = makeBugStatusActivity(bug, bug.status, to, operator); if (activity) useWorkActivityStore.getState().addActivity(activity); return { ok: true }; }, transferBug: (id, newAssigneeId, operator, remark) => { const bug = get().bugs.find((b) => b.id === id); if (!bug) return { ok: false, message: 'Bug不存在' }; if (bug.assigneeId === newAssigneeId) return { ok: false, message: '已是当前负责人' }; const log = makeLog('transfer', operator, bug.assigneeId, newAssigneeId, remark); get().updateBug(id, { assigneeId: newAssigneeId, logs: [...(bug.logs || []), log] }); useWorkActivityStore.getState().addActivity(makeBugTransferredActivity(bug, operator, newAssigneeId)); return { ok: true }; }, getByTestCase: (caseId) => { return get().bugs.filter((b) => b.testCaseId === caseId); }, getByVersion: (versionId) => { return get().bugs.filter((b) => b.versionId === versionId); }, getByAssignee: (assigneeId) => { return get().bugs.filter((b) => b.assigneeId === assigneeId); }, }));