'use client'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useHasPermission } from '@/components/auth/Guard'; import { useAuthStore } from '@/stores/useAuthStore'; import { useBugStore } from '@/stores/useBugStore'; import { useDevTaskStore } from '@/stores/useDevTaskStore'; import { useProductStore } from '@/stores/useProductStore'; import { useRequirementStore } from '@/stores/useRequirementStore'; import { useTaskWorklogStore } from '@/stores/useTaskWorklogStore'; import { useTestCaseStore } from '@/stores/useTestCaseStore'; import { useVersionPlanStore } from '@/stores/useVersionPlanStore'; import { useWorkActivityStore } from '@/stores/useWorkActivityStore'; import { useXiaobaoRiskStore } from '@/stores/useXiaobaoRiskStore'; import { flattenVersions } from '@/lib/derive'; import { loadV22XiaobaoWarnings, type V22XiaobaoWarningSummary } from '@/lib/v22-api'; import { buildVersionDataScopeMap } from '@/lib/version-data-scope'; import { buildXiaobaoRisksFromV22Summaries, collectV22XiaobaoLatestInsights, filterV22XiaobaoSummariesForVisibleVersions, mergeV22XiaobaoLatestInsights, shouldLoadXiaobaoStoreFallback, type V22XiaobaoSummaryLoadState, } from '@/lib/xiaobao-v22-summary'; import { calcXiaobaoVersionRisk } from '@/lib/xiaobao-risk'; import { buildVersionDailyEvidence, buildXiaobaoWorkItems } from '@/lib/xiaobao-risk-evidence'; import { filterXiaobaoWarningVersions } from '@/lib/xiaobao-warning-view'; export function useXiaobaoWarningRisks({ loadRiskCache = false }: { loadRiskCache?: boolean } = {}) { const canManage = useHasPermission('xiaobao.warning:manage'); const user = useAuthStore((s) => s.user); 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 { activities, fetchActivities } = useWorkActivityStore(); const { worklogs, fetchWorklogs } = useTaskWorklogStore(); const { snapshots, insights, pendingInsightKeys, insightRequestAttempts, riskDataLoaded, fetchRiskData, saveSnapshot, saveInsight, beginInsightUpdate, finishInsightUpdate, } = useXiaobaoRiskStore(); const [calculationNow] = useState(() => new Date()); const [v22SummaryState, setV22SummaryState] = useState('idle'); const [v22Summaries, setV22Summaries] = useState([]); const today = useMemo(() => new Date().toISOString().slice(0, 10), []); const v22UserId = user?.name || user?.id || ''; const shouldLoadFallback = shouldLoadXiaobaoStoreFallback(v22SummaryState); const relationInsights = useMemo(() => collectV22XiaobaoLatestInsights(v22Summaries), [v22Summaries]); const mergedInsights = useMemo( () => mergeV22XiaobaoLatestInsights(relationInsights, insights), [insights, relationInsights], ); const allVersions = useMemo(() => flattenVersions(overview), [overview]); const visibleVersions = useMemo( () => filterXiaobaoWarningVersions(allVersions, { canManage, userName: user?.name }), [allVersions, canManage, user?.name], ); const hydrateXiaobaoStoreFallback = useCallback(() => { for (const version of visibleVersions) { void fetchRequirements({ productId: version.productId, versionId: version.id }); void fetchPlans({ versionId: version.id }); void fetchTasks({ versionId: version.id }); void fetchTestCases({ versionId: version.id }); void fetchBugs({ versionId: version.id }); } }, [fetchBugs, fetchPlans, fetchRequirements, fetchTasks, fetchTestCases, visibleVersions]); useEffect(() => { fetchOverview(); }, [fetchOverview]); useEffect(() => { if (!canManage && !v22UserId) { setV22SummaryState('idle'); setV22Summaries([]); return; } let ignore = false; setV22SummaryState('loading'); loadV22XiaobaoWarnings({ userId: v22UserId || undefined, manager: canManage, }).then((summaries) => { if (ignore) return; setV22Summaries(summaries); setV22SummaryState(summaries.length > 0 ? 'ready' : 'empty'); }).catch(() => { if (ignore) return; setV22Summaries([]); setV22SummaryState('failed'); }); return () => { ignore = true; }; }, [canManage, v22UserId]); useEffect(() => { if (shouldLoadFallback) hydrateXiaobaoStoreFallback(); }, [hydrateXiaobaoStoreFallback, shouldLoadFallback]); useEffect(() => { if (shouldLoadFallback) fetchActivities(); }, [fetchActivities, shouldLoadFallback]); useEffect(() => { if (shouldLoadFallback) fetchWorklogs(); }, [fetchWorklogs, shouldLoadFallback]); useEffect(() => { if (loadRiskCache) fetchRiskData(); }, [fetchRiskData, loadRiskCache]); const visibleVersionScopeMap = useMemo(() => buildVersionDataScopeMap({ versionIds: visibleVersions.map((version) => version.id), plans, requirements, devTasks, testCases, bugs, }), [bugs, devTasks, plans, requirements, testCases, visibleVersions]); const workItemsByVersion = useMemo(() => { const map = new Map>(); for (const version of visibleVersions) { const scope = visibleVersionScopeMap[version.id]; if (!scope) continue; map.set(version.id, buildXiaobaoWorkItems({ plans: scope.plans, devTasks: scope.devTasks, testCases: scope.testCases, bugs: scope.bugs, versions: [{ id: version.id, name: version.name, productName: version.productName, projectName: version.projectName, }], requirementVersionMap: new Map(scope.requirements.map((requirement) => [requirement.id, version.id])), })); } return map; }, [visibleVersionScopeMap, visibleVersions]); const risks = useMemo(() => { if (v22SummaryState === 'ready') { return buildXiaobaoRisksFromV22Summaries({ summaries: filterV22XiaobaoSummariesForVisibleVersions(v22Summaries, visibleVersions), versions: visibleVersions, snapshots, now: calculationNow, }); } return visibleVersions.map((version) => { const scope = visibleVersionScopeMap[version.id]; const workItems = workItemsByVersion.get(version.id) ?? []; const dailyEvidence = buildVersionDailyEvidence({ versionId: version.id, workItems, activities, worklogs, }); return calcXiaobaoVersionRisk({ version, devTasks: scope?.devTasks ?? [], testCases: scope?.testCases ?? [], bugs: scope?.bugs ?? [], dailyEvidence, recentActivityCount: dailyEvidence.recentActivityCount, lastActivityAt: dailyEvidence.lastActivityAt, snapshots: snapshots.filter((snapshot) => snapshot.versionId === version.id && snapshot.date < today), now: calculationNow, }); }).sort((a, b) => b.riskScore - a.riskScore); }, [ activities, visibleVersionScopeMap, visibleVersions, workItemsByVersion, worklogs, snapshots, today, calculationNow, v22Summaries, v22SummaryState, ]); return { risks, snapshots, insights: mergedInsights, pendingInsightKeys, insightRequestAttempts, riskDataLoaded, saveSnapshot, saveInsight, beginInsightUpdate, finishInsightUpdate, today, }; }