199 lines
7.3 KiB
TypeScript
199 lines
7.3 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,
|
|
collectV22XiaobaoLatestInsights,
|
|
filterV22XiaobaoSummariesForVisibleVersions,
|
|
mergeV22XiaobaoLatestInsights,
|
|
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);
|
|
const relationInsights = useMemo(() => collectV22XiaobaoLatestInsights(v22Summaries), [v22Summaries]);
|
|
const mergedInsights = useMemo(
|
|
() => mergeV22XiaobaoLatestInsights(relationInsights, insights),
|
|
[insights, relationInsights],
|
|
);
|
|
|
|
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: mergedInsights,
|
|
pendingInsightKeys,
|
|
insightRequestAttempts,
|
|
riskDataLoaded,
|
|
saveSnapshot,
|
|
saveInsight,
|
|
beginInsightUpdate,
|
|
finishInsightUpdate,
|
|
today,
|
|
};
|
|
}
|