feat(平台): 补齐服务端持久化和AI拆解契约

This commit is contained in:
Script Generator
2026-06-25 15:21:32 +08:00
parent 5723356d08
commit 5adc7759ad
73 changed files with 5599 additions and 368 deletions

View File

@@ -33,6 +33,7 @@ import { testCaseIntervals } from '@/lib/test-case';
import { bugIntervals } from '@/lib/bug';
import { planIntervals } from '@/lib/version-plan';
import { formatDateTime } from '@/lib/format';
import { getProjectAdoptedRequirementCandidates } from '@/lib/requirement-selector';
const PRIORITY_STYLE: Record<string, string> = {
P0: 'bg-red-500/10 text-red-600',
@@ -145,9 +146,10 @@ export default function VersionDetailPage() {
);
}
// 权限校验:只有参与人员可以访问
// 权限校验:只有参与人员可以访问(超管不受限)
const currentUserName = user?.name || '';
const isMember = (version.members ?? []).length === 0 || (version.members ?? []).some((m) => m.name === currentUserName);
const isSuperAdmin = !!currentRole && currentRole.permissions.includes('*');
const isMember = isSuperAdmin || (version.members ?? []).length === 0 || (version.members ?? []).some((m) => m.name === currentUserName);
if (!isMember) {
return (
<div className="flex h-full flex-col items-center justify-center gap-3">
@@ -512,7 +514,7 @@ export default function VersionDetailPage() {
{g.items.map((p) => (
<a key={p.id} href={p.resultUrl} target="_blank" rel="noopener noreferrer" className="flex items-center gap-1.5 text-[12px] text-[var(--accent)] hover:underline">
{p.resultType === 'file' ? <FileText className="h-3 w-3" /> : <Link2 className="h-3 w-3" />}
<span className="truncate">{p.resultFileName || p.title}</span>
<span className="truncate">{p.resultTitle || p.resultFileName || p.title}</span>
</a>
))}
</div>
@@ -887,17 +889,18 @@ export default function VersionDetailPage() {
) : (activeTab === 'research' || activeTab === 'product' || activeTab === 'ui') ? (
(() => {
const pt = activeTab as 'research' | 'product' | 'ui';
const versionReqs = requirements.filter((r) => r.versionId === version.id);
const linkedReqs = versionReqs.map((r) => ({ id: r.id, title: r.title, code: r.code, productOwner: r.productOwner }));
const projectAdoptedReqs = getProjectAdoptedRequirementCandidates(requirements, version.projectId);
return (
<PlanTab
plans={plans}
versionId={version.id}
version={version}
versionDeadline={version.expectedReleaseDate ?? undefined}
currentUserName={user?.name ?? ''}
planType={pt}
versionMembers={version.members ?? []}
linkedRequirements={pt !== 'research' ? linkedReqs : undefined}
linkedRequirements={projectAdoptedReqs}
allRequirements={requirements}
onCreate={(data) => {
createPlan(data);
if ((pt === 'product') && data.linkedRequirementIds?.length) {