'use client'; import { create } from 'zustand'; import type { DevTask, DevTaskStatus } from '@/lib/dev-task'; import { generateTaskNo, isLegacyTask } from '@/lib/dev-task'; import { applyDevTaskTransition, normalizeDevTaskOnCreate } from '@/lib/dev-task-workflow'; import { createEntityId, dedupeEntityIds } from '@/lib/entity-id'; import { loadServerData, saveServerData } from '@/lib/server-data'; import { makeDevTaskBlockedActivity, makeDevTaskCreatedActivity, makeDevTaskStatusActivity, makeDevTaskUnblockedActivity, } from '@/lib/work-activity-factory'; import { useWorkActivityStore } from './useWorkActivityStore'; function saveStored(items: DevTask[]) { saveServerData('dev-tasks', items).catch(() => {}); } async function loadStored(): Promise { try { const parsed = await loadServerData('dev-tasks'); if (parsed) { if (Array.isArray(parsed) && parsed.some(isLegacyTask)) { return null; } const result = dedupeEntityIds(parsed, 'task'); if (result.changed) saveStored(result.items); return result.items; } } catch {} return null; } interface DevTaskState { tasks: DevTask[]; fetchTasks: () => Promise; createTask: (data: Omit) => DevTask; updateTask: (id: string, data: Partial) => void; deleteTask: (id: string) => void; changeStatus: (id: string, to: DevTaskStatus, opts?: { delayReason?: string }) => { ok: boolean; message?: string }; setBlocked: (id: string, blocked: boolean, reason?: string, blockedById?: string) => void; getByRequirement: (requirementId: string) => DevTask[]; getByVersionViaRequirements: (requirementIds: string[]) => DevTask[]; getByAssignee: (assigneeId: string) => DevTask[]; } export const useDevTaskStore = create((set, get) => ({ tasks: [], fetchTasks: async () => { const cached = await loadStored(); set({ tasks: cached ?? [] }); }, createTask: (data) => { const list = get().tasks; const now = new Date().toISOString(); const task: DevTask = normalizeDevTaskOnCreate({ ...data, id: createEntityId('task'), taskNo: generateTaskNo(list), isBlocked: false, createdAt: now, updatedAt: now, } as DevTask); const updated = [...list, task]; set({ tasks: updated }); saveStored(updated); useWorkActivityStore.getState().addActivity(makeDevTaskCreatedActivity(task, task.createdBy || task.assigneeId)); return task; }, updateTask: (id, data) => { const updated = get().tasks.map((t) => t.id === id ? { ...t, ...data, aiDraft: false, updatedAt: new Date().toISOString() } : t, ); set({ tasks: updated }); saveStored(updated); }, deleteTask: (id) => { const updated = get().tasks.filter((t) => t.id !== id); set({ tasks: updated }); saveStored(updated); }, changeStatus: (id, to, opts) => { const task = get().tasks.find((t) => t.id === id); if (!task) return { ok: false, message: '任务不存在' }; const result = applyDevTaskTransition(task, to, { now: new Date(), delayReason: opts?.delayReason, }); if (!result.ok || !result.patch) return { ok: false, message: result.message }; get().updateTask(id, result.patch); const activity = makeDevTaskStatusActivity(task, task.status, to, task.assigneeId); if (activity) useWorkActivityStore.getState().addActivity(activity); return { ok: true }; }, setBlocked: (id, blocked, reason, blockedById) => { const task = get().tasks.find((t) => t.id === id); if (!task) return; get().updateTask(id, { isBlocked: blocked, blockReason: blocked ? reason : undefined, blockedById: blocked ? blockedById : undefined, }); const activity = blocked ? makeDevTaskBlockedActivity(task, task.assigneeId, reason, blockedById) : makeDevTaskUnblockedActivity(task, task.assigneeId); useWorkActivityStore.getState().addActivity(activity); }, getByRequirement: (requirementId) => { return get().tasks.filter((t) => t.requirementId === requirementId); }, getByVersionViaRequirements: (requirementIds) => { const idSet = new Set(requirementIds); return get().tasks.filter((t) => idSet.has(t.requirementId)); }, getByAssignee: (assigneeId) => { return get().tasks.filter((t) => t.assigneeId === assigneeId); }, }));