refactor(data): 收口关系表运行时数据源
Some checks failed
Deploy Production / Build, push, deploy, verify (push) Has been cancelled

- 移除已迁移业务 AppData 运行时 fallback,改走领域 API 和关系表快读
- 补齐需求产品负责人、版本计划任务 JSON 和成员 username 回填迁移
- 统一治理字典入口,并补充 AI provider、数据源契约和领域服务测试

Co-Authored-By: Codex GPT-5 <codex@openai.com>
This commit is contained in:
2026-07-09 14:59:49 +08:00
parent 1e6fb0c7aa
commit 2e595c7e72
98 changed files with 2938 additions and 1441 deletions

View File

@@ -1,6 +1,6 @@
'use client';
import { useEffect, useMemo, useState, type FormEvent } from 'react';
import { useCallback, useEffect, useMemo, useState, type FormEvent } from 'react';
import { useParams, useRouter } from 'next/navigation';
import { AlertTriangle, Calendar, Check, ChevronLeft, Clock, FileText, Link2, Pencil, Search, Settings, Sparkles, UserPlus, X } from 'lucide-react';
import { useProductStore } from '@/stores/useProductStore';
@@ -142,6 +142,7 @@ export default function VersionDetailPage() {
() => allMembers.map((member) => ({
id: member.id,
name: member.name,
username: member.username,
departmentName: departments.find((department) => department.id === member.departmentId)?.name,
})),
[allMembers, departments],
@@ -163,6 +164,12 @@ export default function VersionDetailPage() {
const [showAnalysisDrawer, setShowAnalysisDrawer] = useState(false);
const [recommendationDataReady, setRecommendationDataReady] = useState(false);
const [v22Scope, setV22Scope] = useState<VersionDataScope | null>(null);
const version = useMemo(() => getVersionDetail(overview, versionId), [overview, versionId]);
const fetchScopedRequirements = useCallback((force = false) => {
if (!version) return Promise.resolve();
if (force) return fetchRequirements({ productId: version.productId, versionId, force: true });
return fetchRequirements({ productId: version.productId, versionId });
}, [fetchRequirements, version, versionId]);
useEffect(() => { fetchOverview(); }, [fetchOverview]);
useEffect(() => {
@@ -182,43 +189,44 @@ export default function VersionDetailPage() {
fetchRecords();
return;
}
if (!version) return;
if (activeTab === 'requirements') {
fetchRequirements();
fetchDevTasks();
fetchScopedRequirements();
fetchDevTasks({ versionId });
return;
}
if (activeTab === 'research' || activeTab === 'product' || activeTab === 'ui') {
fetchRequirements();
fetchPlans();
fetchScopedRequirements();
fetchPlans({ versionId });
return;
}
if (activeTab === 'tasks') {
fetchRequirements();
fetchDevTasks();
fetchScopedRequirements();
fetchDevTasks({ versionId });
return;
}
if (activeTab === 'testcases') {
fetchRequirements();
fetchDevTasks();
fetchTestCases();
fetchBugs();
fetchScopedRequirements();
fetchDevTasks({ versionId });
fetchTestCases({ versionId });
fetchBugs({ versionId });
return;
}
if (activeTab === 'bugs') {
fetchRequirements();
fetchTestCases();
fetchBugs();
fetchScopedRequirements();
fetchTestCases({ versionId });
fetchBugs({ versionId });
}
}, [activeTab, fetchBugs, fetchDevTasks, fetchPlans, fetchRecords, fetchRequirements, fetchTestCases]);
}, [activeTab, fetchBugs, fetchDevTasks, fetchPlans, fetchRecords, fetchScopedRequirements, fetchTestCases, version, versionId]);
useEffect(() => {
if (!showRecommendModal) return;
fetchRecords();
fetchRequirements();
fetchPlans();
fetchDevTasks();
fetchTestCases();
fetchBugs();
}, [fetchBugs, fetchDevTasks, fetchPlans, fetchRecords, fetchRequirements, fetchTestCases, showRecommendModal]);
fetchScopedRequirements(true);
fetchPlans({ versionId });
fetchDevTasks({ versionId });
fetchTestCases({ versionId });
fetchBugs({ versionId });
}, [fetchBugs, fetchDevTasks, fetchPlans, fetchRecords, fetchScopedRequirements, fetchTestCases, showRecommendModal, versionId]);
useEffect(() => {
let active = true;
setRecommendationDataReady(false);
@@ -228,8 +236,7 @@ export default function VersionDetailPage() {
return () => { active = false; };
}, [fetchMembers, fetchCategories]);
const version = useMemo(() => getVersionDetail(overview, versionId), [overview, versionId]);
const appDataVersionScope = useMemo(
const storeVersionScope = useMemo(
() => version
? buildVersionDataScope({
versionId: version.id,
@@ -250,19 +257,20 @@ export default function VersionDetailPage() {
const testCaseWriteReady = requirementsLoaded && devTasksLoaded && testCasesLoaded && bugsLoaded;
const bugWriteReady = requirementsLoaded && testCasesLoaded && bugsLoaded;
const loadAllVersionStores = () => {
if (!version) return;
fetchRecords();
fetchRequirements();
fetchPlans();
fetchDevTasks();
fetchTestCases();
fetchBugs();
fetchRequirements({ productId: version.productId, versionId, force: true });
fetchPlans({ versionId });
fetchDevTasks({ versionId });
fetchTestCases({ versionId });
fetchBugs({ versionId });
};
const versionScope = useMemo(
() => selectVersionDataScope({
appDataScope: appDataVersionScope,
v22Scope: versionWriteStoresReady ? null : v22Scope,
storeScope: storeVersionScope,
v22Scope,
}),
[appDataVersionScope, v22Scope, versionWriteStoresReady],
[storeVersionScope, v22Scope],
);
const releaseProgress = versionScope
? calcScopedVersionProgress(versionScope.plans, versionScope.devTasks, versionScope.testCases)
@@ -327,15 +335,12 @@ export default function VersionDetailPage() {
.some((permission) => canWriteVersionPermission(permission));
const scopedVersionData = versionScope!;
const appDataScopedVersionData = appDataVersionScope ?? scopedVersionData;
const displayRequirements = requirementsLoaded ? requirements : scopedVersionData.requirements;
const displayPlans = plansLoaded ? appDataScopedVersionData.plans : scopedVersionData.plans;
const displayDevTasks = devTasksLoaded ? appDataScopedVersionData.devTasks : scopedVersionData.devTasks;
const displayTestCases = testCasesLoaded ? appDataScopedVersionData.testCases : scopedVersionData.testCases;
const displayBugs = bugsLoaded ? appDataScopedVersionData.bugs : scopedVersionData.bugs;
const displayRequirementIds = requirementsLoaded
? appDataScopedVersionData.requirementIds
: scopedVersionData.requirementIds;
const displayRequirements = scopedVersionData.requirements;
const displayPlans = scopedVersionData.plans;
const displayDevTasks = scopedVersionData.devTasks;
const displayTestCases = scopedVersionData.testCases;
const displayBugs = scopedVersionData.bugs;
const displayRequirementIds = scopedVersionData.requirementIds;
const memberRecommendationGroups = showRecommendModal && recommendationDataReady ? (() => {
const currentSystemParticipation = new Map<string, number>();
overview.forEach((product) => {
@@ -1066,8 +1071,10 @@ export default function VersionDetailPage() {
version={version}
versionDeadline={version.expectedReleaseDate ?? undefined}
currentUserName={user?.name ?? ''}
currentUserReference={user?.username ?? user?.id ?? user?.name ?? ''}
planType={pt}
versionMembers={version.members ?? []}
allMembers={memberCandidates}
linkedRequirements={versionLinkedReqs}
allRequirements={displayRequirements}
readOnly={versionReadonly || !planWriteReady || !canWriteVersionPermission(PLAN_MANAGE_PERMISSION[pt])}

View File

@@ -94,16 +94,9 @@ function VersionsPageContent() {
useEffect(() => { fetchOverview(); }, [fetchOverview]);
const { requirements, fetchRequirements, updateRequirement } = useRequirementStore();
useEffect(() => { fetchRequirements(); }, [fetchRequirements]);
const { plans, fetchPlans } = useVersionPlanStore();
useEffect(() => { fetchPlans(); }, [fetchPlans]);
const { tasks: devTasks, fetchTasks: fetchDevTasks } = useDevTaskStore();
useEffect(() => { fetchDevTasks(); }, [fetchDevTasks]);
const { testCases, fetchTestCases } = useTestCaseStore();
useEffect(() => { fetchTestCases(); }, [fetchTestCases]);
const user = useAuthStore((s) => s.user);
const currentUserName = user?.name || '';
@@ -125,6 +118,14 @@ function VersionsPageContent() {
if (!v.members || v.members.length === 0) return true;
return v.members.some((m) => m.name === currentUserName);
}), [allVersionsRaw, currentUserName, isSuperAdmin]);
useEffect(() => {
for (const version of allVersions) {
void fetchRequirements({ productId: version.productId, versionId: version.id });
void fetchPlans({ versionId: version.id });
void fetchDevTasks({ versionId: version.id });
void fetchTestCases({ versionId: version.id });
}
}, [allVersions, fetchDevTasks, fetchPlans, fetchRequirements, fetchTestCases]);
const versionTree = useMemo(() => buildVersionScopeTree(allVersions, treeKeyword), [allVersions, treeKeyword]);
// Compute overall progress per version from actual data