feat(v2.2): 完成高频读取热路径

This commit is contained in:
Script Generator
2026-07-03 11:53:25 +08:00
parent 5a90923031
commit 70460c3273
28 changed files with 3042 additions and 121 deletions

View File

@@ -1,6 +1,6 @@
'use client';
import { useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useRouter } from 'next/navigation';
import { Search, FileText, Palette, ClipboardList, Code2, ClipboardCheck, Bug as BugIcon, ExternalLink, CheckCircle2, ChevronRight, ChevronDown, FolderOpen, Layers } from 'lucide-react';
import { useProductStore } from '@/stores/useProductStore';
@@ -28,6 +28,8 @@ import { TEST_CASE_STATUS_LABEL, TEST_CASE_STATUS_COLOR } from '@/lib/test-case'
import { BUG_STATUS_LABEL, BUG_STATUS_COLOR, BUG_SEVERITY_LABEL, BUG_SEVERITY_COLOR } from '@/lib/bug';
import { getWorkspaceDailyReport } from '@/lib/workspace-daily-report';
import { VERSION_STATUS_BG, VERSION_STATUS_LABEL, getVersionReadonlyNotice, isVersionReadonly, type VersionStatus } from '@/lib/version-status';
import { loadV22WorkspaceData, type V22WorkspaceData } from '@/lib/v22-api';
import { selectWorkspaceCollections } from '@/lib/workspace-v22-source';
type TabKey = 'all' | 'plan_research' | 'plan_product' | 'plan_ui' | 'devTask' | 'testCase' | 'bug';
type WorkspaceVersionContext = { id: string; name: string; productName: string; projectName: string; status: VersionStatus };
@@ -54,11 +56,11 @@ const PLAN_STATUS_LABEL: Record<string, string> = { pending: '未开始', in_pro
export default function WorkspacePage() {
const router = useRouter();
const { overview, fetchOverview } = useProductStore();
const { plans, fetchPlans } = useVersionPlanStore();
const { requirements, fetchRequirements } = useRequirementStore();
const { tasks: devTasks, fetchTasks } = useDevTaskStore();
const { testCases, fetchTestCases } = useTestCaseStore();
const { bugs, fetchBugs } = useBugStore();
const { plans, fetchPlans, loaded: plansLoaded } = useVersionPlanStore();
const { requirements, fetchRequirements, loaded: requirementsLoaded } = useRequirementStore();
const { tasks: devTasks, fetchTasks, loaded: devTasksLoaded } = useDevTaskStore();
const { testCases, fetchTestCases, loaded: testCasesLoaded } = useTestCaseStore();
const { bugs, fetchBugs, loaded: bugsLoaded } = useBugStore();
const { worklogs, fetchWorklogs } = useTaskWorklogStore();
const { activities, fetchActivities } = useWorkActivityStore();
const user = useAuthStore((s) => s.user);
@@ -67,17 +69,62 @@ export default function WorkspacePage() {
const [selectedVersionId, setSelectedVersionId] = useState<string | null>(null);
const [drawerItem, setDrawerItem] = useState<WorkItem | null>(null);
const [bugFromTestCaseId, setBugFromTestCaseId] = useState<string | null>(null);
const [v22WorkspaceData, setV22WorkspaceData] = useState<V22WorkspaceData | null>(null);
const [v22WorkspaceLoaded, setV22WorkspaceLoaded] = useState(false);
const [v22WorkspaceFailed, setV22WorkspaceFailed] = useState(false);
useEffect(() => { fetchOverview(); }, [fetchOverview]);
useEffect(() => { fetchPlans(); }, [fetchPlans]);
useEffect(() => { fetchRequirements(); }, [fetchRequirements]);
useEffect(() => { fetchTasks(); }, [fetchTasks]);
useEffect(() => { fetchTestCases(); }, [fetchTestCases]);
useEffect(() => { fetchBugs(); }, [fetchBugs]);
useEffect(() => { fetchWorklogs(); }, [fetchWorklogs]);
useEffect(() => { fetchActivities(); }, [fetchActivities]);
const userName = user?.name ?? '';
const workspaceUserKey = userName || user?.id || '';
useEffect(() => {
if (!workspaceUserKey.trim()) {
setV22WorkspaceData(null);
setV22WorkspaceLoaded(false);
setV22WorkspaceFailed(false);
return;
}
let cancelled = false;
setV22WorkspaceLoaded(false);
setV22WorkspaceFailed(false);
loadV22WorkspaceData(workspaceUserKey)
.then((data) => {
if (cancelled) return;
setV22WorkspaceData(data);
setV22WorkspaceLoaded(true);
})
.catch(() => {
if (cancelled) return;
setV22WorkspaceData(null);
setV22WorkspaceFailed(true);
});
return () => {
cancelled = true;
};
}, [workspaceUserKey]);
useEffect(() => {
if (!workspaceUserKey.trim() || !v22WorkspaceFailed) return;
void fetchPlans();
void fetchRequirements();
void fetchTasks();
void fetchTestCases();
void fetchBugs();
}, [
fetchBugs,
fetchPlans,
fetchRequirements,
fetchTasks,
fetchTestCases,
v22WorkspaceFailed,
workspaceUserKey,
]);
const allVersions = useMemo(() => flattenVersions(overview), [overview]);
const versionMap = useMemo(() => {
@@ -92,9 +139,27 @@ export default function WorkspacePage() {
return map;
}, [requirements]);
const workspaceCollections = useMemo(
() => selectWorkspaceCollections({
v22Loaded: v22WorkspaceLoaded,
v22Failed: v22WorkspaceFailed,
v22Data: v22WorkspaceData,
appData: { plans, devTasks, testCases, bugs },
}),
[bugs, devTasks, plans, testCases, v22WorkspaceData, v22WorkspaceFailed, v22WorkspaceLoaded],
);
const workItems = useMemo(() =>
aggregateWorkItems(userName, plans, devTasks, testCases, bugs, versionMap, requirementVersionMap),
[userName, plans, devTasks, testCases, bugs, versionMap, requirementVersionMap]
aggregateWorkItems(
userName,
workspaceCollections.versionPlans,
workspaceCollections.devTasks,
workspaceCollections.testCases,
workspaceCollections.bugs,
versionMap,
requirementVersionMap,
),
[userName, workspaceCollections, versionMap, requirementVersionMap]
);
const pendingCountByVersion = useMemo(() => getWorkspacePendingCountByVersion(workItems), [workItems]);
@@ -158,6 +223,32 @@ export default function WorkspacePage() {
const selectedVersion = selectedVersionId ? versionMap.get(selectedVersionId) : undefined;
const drawerVersionStatus = drawerItem ? versionMap.get(drawerItem.versionId)?.status : undefined;
const drawerReadOnly = drawerVersionStatus ? isVersionReadonly(drawerVersionStatus) : false;
const ensureWorkspaceDrawerStores = useCallback(async (item: WorkItem) => {
const pendingLoads: Array<Promise<void>> = [];
if (!requirementsLoaded) pendingLoads.push(fetchRequirements());
if ((item.type === 'plan_research' || item.type === 'plan_product' || item.type === 'plan_ui') && !plansLoaded) {
pendingLoads.push(fetchPlans());
}
if (item.type === 'devTask' && !devTasksLoaded) pendingLoads.push(fetchTasks());
if (item.type === 'testCase' && !testCasesLoaded) pendingLoads.push(fetchTestCases());
if (item.type === 'bug' && !bugsLoaded) pendingLoads.push(fetchBugs());
await Promise.all(pendingLoads);
}, [
bugsLoaded,
devTasksLoaded,
fetchBugs,
fetchPlans,
fetchRequirements,
fetchTasks,
fetchTestCases,
plansLoaded,
requirementsLoaded,
testCasesLoaded,
]);
const openWorkItemDrawer = useCallback(async (item: WorkItem) => {
await ensureWorkspaceDrawerStores(item);
setDrawerItem(item);
}, [ensureWorkspaceDrawerStores]);
return (
<div className="flex h-full">
@@ -245,7 +336,7 @@ export default function WorkspacePage() {
item={item}
versionStatus={versionMap.get(item.versionId)?.status}
onNavigate={() => item.versionId && router.push(`/versions/${item.versionId}`)}
onClick={() => setDrawerItem(item)}
onClick={() => { void openWorkItemDrawer(item); }}
/>
))}
</div>