Files
ftb-project-management/apps/web/hooks/useXiaobaoWarningRisks.ts
2026-07-03 11:53:25 +08:00

192 lines
7.0 KiB
TypeScript

'use client';
import { 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,
filterV22XiaobaoSummariesForVisibleVersions,
shouldLoadXiaobaoAppDataFallback,
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<V22XiaobaoSummaryLoadState>('idle');
const [v22Summaries, setV22Summaries] = useState<V22XiaobaoWarningSummary[]>([]);
const today = useMemo(() => new Date().toISOString().slice(0, 10), []);
const v22UserId = user?.name || user?.id || '';
const shouldLoadFallback = shouldLoadXiaobaoAppDataFallback(v22SummaryState);
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) fetchPlans(); }, [fetchPlans, shouldLoadFallback]);
useEffect(() => { if (shouldLoadFallback) fetchRequirements(); }, [fetchRequirements, shouldLoadFallback]);
useEffect(() => { if (shouldLoadFallback) fetchTasks(); }, [fetchTasks, shouldLoadFallback]);
useEffect(() => { if (shouldLoadFallback) fetchTestCases(); }, [fetchTestCases, shouldLoadFallback]);
useEffect(() => { if (shouldLoadFallback) fetchBugs(); }, [fetchBugs, shouldLoadFallback]);
useEffect(() => { if (shouldLoadFallback) fetchActivities(); }, [fetchActivities, shouldLoadFallback]);
useEffect(() => { if (shouldLoadFallback) fetchWorklogs(); }, [fetchWorklogs, shouldLoadFallback]);
useEffect(() => {
if (loadRiskCache) fetchRiskData();
}, [fetchRiskData, loadRiskCache]);
const allVersions = useMemo(() => flattenVersions(overview), [overview]);
const visibleVersions = useMemo(
() => filterXiaobaoWarningVersions(allVersions, { canManage, userName: user?.name }),
[allVersions, canManage, user?.name],
);
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<string, ReturnType<typeof buildXiaobaoWorkItems>>();
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,
pendingInsightKeys,
insightRequestAttempts,
riskDataLoaded,
saveSnapshot,
saveInsight,
beginInsightUpdate,
finishInsightUpdate,
today,
};
}