fix(权限): 修复版本详情写入权限校验

This commit is contained in:
2026-07-08 19:27:41 +08:00
parent cafa0f134a
commit deebc5404a
5 changed files with 191 additions and 17 deletions

View File

@@ -31,7 +31,7 @@ import { formatDateTime, formatLocalDate } from '@/lib/format';
import { getVersionLinkedRequirementCandidates } from '@/lib/requirement-selector';
import { getRequirementVersionSelectionPatch, getRequirementVersionUnlinkPatch } from '@/lib/requirement-version-link';
import { calcBugSeverityRanking, calcPersonalEffortRanking, calcStageEffortMetrics, calcVersionOverviewEffortTotals } from '@/lib/version-overview';
import { addVersionMembers, DEFAULT_VERSION_MEMBER_ROLE, filterVersionMemberCandidates } from '@/lib/version-members';
import { addVersionMembers, canWriteVersionWork, DEFAULT_VERSION_MEMBER_ROLE, filterVersionMemberCandidates } from '@/lib/version-members';
import { addRecommendedVersionMembers, getDefaultRecommendedMemberNames, recommendVersionMembers, type MemberRecommendationGroup, type RecommendableRole } from '@/lib/member-recommendation';
import { getRequirementCoverageSummary } from '@/lib/version-plan';
import { buildVersionDataScope, selectVersionDataScope, type VersionDataScope } from '@/lib/version-data-scope';
@@ -85,6 +85,12 @@ const TABS = [
{ key: 'bugs', label: 'BUG', permission: 'version.bug:view' },
];
const PLAN_MANAGE_PERMISSION = {
research: 'version.research:manage',
product: 'version.product_plan:manage',
ui: 'version.ui_plan:manage',
} as const;
export default function VersionDetailPage() {
const params = useParams();
const router = useRouter();
@@ -302,6 +308,20 @@ export default function VersionDetailPage() {
);
}
const versionMembers = version.members ?? [];
const canWriteVersionPermission = (permission: string) => canWriteVersionWork({
role: currentRole,
permission,
currentUserName,
versionMembers,
readOnly: versionReadonly,
});
const canManageRequirements = canWriteVersionPermission('version.req:manage');
const canManageDevTasks = canWriteVersionPermission('version.devtask:manage');
const canManageTestCases = canWriteVersionPermission('version.testcase:manage');
const canManageBugs = ['version.bug:create', 'version.bug:edit', 'version.bug:delete']
.some((permission) => canWriteVersionPermission(permission));
const scopedVersionData = versionScope!;
const appDataScopedVersionData = appDataVersionScope ?? scopedVersionData;
const displayRequirements = requirementsLoaded ? requirements : scopedVersionData.requirements;
@@ -987,9 +1007,9 @@ export default function VersionDetailPage() {
devTasks={displayDevTasks}
versionMembers={version.members ?? []}
currentUserName={user?.name ?? ''}
readOnly={versionReadonly || !requirementWriteReady}
readOnly={versionReadonly || !requirementWriteReady || !canManageRequirements}
onLink={(ids, addedBy) => {
if (versionReadonly || !requirementWriteReady) return;
if (versionReadonly || !requirementWriteReady || !canManageRequirements) return;
ids.forEach((id) => {
const requirement = displayRequirements.find((item) => item.id === id);
if (!requirement) return;
@@ -1000,7 +1020,7 @@ export default function VersionDetailPage() {
});
}}
onUnlink={(id) => {
if (versionReadonly || !requirementWriteReady) return;
if (versionReadonly || !requirementWriteReady || !canManageRequirements) return;
const requirement = displayRequirements.find((item) => item.id === id);
if (!requirement) return;
updateRequirement(id, {
@@ -1009,7 +1029,7 @@ export default function VersionDetailPage() {
});
}}
onCreateChange={(data) => {
if (versionReadonly || !requirementWriteReady) return;
if (versionReadonly || !requirementWriteReady || !canManageRequirements) return;
createRequirement({
...data,
productId: version.productId,
@@ -1043,9 +1063,9 @@ export default function VersionDetailPage() {
versionMembers={version.members ?? []}
linkedRequirements={versionLinkedReqs}
allRequirements={displayRequirements}
readOnly={versionReadonly || !planWriteReady}
readOnly={versionReadonly || !planWriteReady || !canWriteVersionPermission(PLAN_MANAGE_PERMISSION[pt])}
onCreate={(data) => {
if (versionReadonly || !planWriteReady) return;
if (versionReadonly || !planWriteReady || !canWriteVersionPermission(PLAN_MANAGE_PERMISSION[pt])) return;
createPlan(data);
if ((pt === 'product') && data.linkedRequirementIds?.length) {
data.linkedRequirementIds.forEach((rid) => {
@@ -1061,7 +1081,7 @@ export default function VersionDetailPage() {
}
}}
onUpdate={(id, data) => {
if (versionReadonly || !planWriteReady) return;
if (versionReadonly || !planWriteReady || !canWriteVersionPermission(PLAN_MANAGE_PERMISSION[pt])) return;
updatePlan(id, data);
if ((pt === 'product') && data.linkedRequirementIds && data.owner) {
data.linkedRequirementIds.forEach((rid) => {
@@ -1071,11 +1091,11 @@ export default function VersionDetailPage() {
}
}}
onComplete={(id, result) => {
if (versionReadonly || !planWriteReady) return;
if (versionReadonly || !planWriteReady || !canWriteVersionPermission(PLAN_MANAGE_PERMISSION[pt])) return;
return completePlan(id, result);
}}
onDelete={(id) => {
if (versionReadonly || !planWriteReady) return;
if (versionReadonly || !planWriteReady || !canWriteVersionPermission(PLAN_MANAGE_PERMISSION[pt])) return;
deletePlan(id);
}}
/>
@@ -1086,7 +1106,7 @@ export default function VersionDetailPage() {
versionId={version.id}
requirementIds={displayRequirementIds}
versionDeadline={version.expectedReleaseDate ?? undefined}
readOnly={versionReadonly || !devTaskWriteReady}
readOnly={versionReadonly || !devTaskWriteReady || !canManageDevTasks}
versionTasks={displayDevTasks}
versionRequirements={displayRequirements}
/>
@@ -1094,7 +1114,7 @@ export default function VersionDetailPage() {
<TestCaseTab
versionId={version.id}
requirementIds={displayRequirementIds}
readOnly={versionReadonly || !testCaseWriteReady}
readOnly={versionReadonly || !testCaseWriteReady || !canManageTestCases}
versionCases={displayTestCases}
versionBugs={displayBugs}
versionDevTasks={displayDevTasks}
@@ -1104,7 +1124,7 @@ export default function VersionDetailPage() {
<BugTab
versionId={version.id}
requirementIds={displayRequirementIds}
readOnly={versionReadonly || !bugWriteReady}
readOnly={versionReadonly || !bugWriteReady || !canManageBugs}
versionBugs={displayBugs}
versionTestCases={displayTestCases}
/>