「权限 × 状态 × 审计」联合约束机制

This commit is contained in:
Cheng Zhou
2025-12-17 21:48:29 +08:00
parent 13efb0a3a9
commit 4202ed7922
36 changed files with 714 additions and 85 deletions
+105
View File
@@ -83,9 +83,13 @@ import { fetchUsers } from "../../api/users";
import { addMember, listMembers, removeMember, updateMember } from "../../api/members";
import { fetchStudyDetail } from "../../api/studies";
import type { Study, StudyMember, UserInfo } from "../../types/api";
import { useAuthStore } from "../../store/auth";
import { evaluateAction } from "../../guards/actionGuard";
import { logAudit } from "../../audit";
const route = useRoute();
const projectId = computed(() => route.params.projectId as string);
const auth = useAuthStore();
const project = ref<Study | null>(null);
const members = ref<StudyMember[]>([]);
@@ -154,6 +158,21 @@ const openAdd = () => {
const submitAdd = async () => {
if (!projectId.value) return;
const decision = evaluateAction({
actorRole: auth.user?.role || null,
requiredPermission: "project.members.manage",
target: { projectId: projectId.value },
});
if (!decision.allowed) {
ElMessage.warning(decision.reason || "无权限执行该操作");
logAudit(decision.auditType, {
targetId: projectId.value,
targetName: project.value?.name,
reason: decision.reason,
severity: decision.severity,
});
return;
}
await addFormRef.value?.validate();
adding.value = true;
try {
@@ -161,8 +180,21 @@ const submitAdd = async () => {
ElMessage.success("成员已添加");
addVisible.value = false;
loadMembers();
logAudit("PROJECT_MEMBER_UPDATED", {
targetId: projectId.value,
targetName: project.value?.name,
after: { user_id: newMember.user_id, role_in_study: newMember.role_in_study },
severity: "normal",
});
} catch (e: any) {
ElMessage.error(e?.response?.data?.message || "添加失败");
logAudit("PROJECT_MEMBER_UPDATED", {
targetId: projectId.value,
targetName: project.value?.name,
after: { user_id: newMember.user_id, role_in_study: newMember.role_in_study },
severity: "warning",
reason: e?.response?.data?.message,
});
} finally {
adding.value = false;
}
@@ -170,18 +202,61 @@ const submitAdd = async () => {
const updateRole = async (memberId: string, role: string) => {
if (!projectId.value) return;
const decision = evaluateAction({
actorRole: auth.user?.role || null,
requiredPermission: "project.members.manage",
target: { projectId: projectId.value, memberId },
});
if (!decision.allowed) {
ElMessage.warning(decision.reason || "无权限执行该操作");
logAudit(decision.auditType, {
targetId: projectId.value,
targetName: project.value?.name,
reason: decision.reason,
severity: decision.severity,
});
return;
}
try {
await updateMember(projectId.value, memberId, { role_in_study: role });
ElMessage.success("角色已更新");
loadMembers();
logAudit("PROJECT_MEMBER_UPDATED", {
targetId: projectId.value,
targetName: project.value?.name,
after: { member_id: memberId, role_in_study: role },
severity: "normal",
});
} catch (e: any) {
ElMessage.error(e?.response?.data?.message || "更新失败");
loadMembers();
logAudit("PROJECT_MEMBER_UPDATED", {
targetId: projectId.value,
targetName: project.value?.name,
after: { member_id: memberId, role_in_study: role },
severity: "warning",
reason: e?.response?.data?.message,
});
}
};
const toggleActive = async (row: StudyMember) => {
if (!projectId.value) return;
const decision = evaluateAction({
actorRole: auth.user?.role || null,
requiredPermission: "project.members.manage",
target: { projectId: projectId.value, memberId: row.id },
});
if (!decision.allowed) {
ElMessage.warning(decision.reason || "无权限执行该操作");
logAudit(decision.auditType, {
targetId: projectId.value,
targetName: project.value?.name,
reason: decision.reason,
severity: decision.severity,
});
return;
}
if (row.is_active) {
const ok = await ElMessageBox.confirm("该操作将影响项目成员,请确认是否继续", "停用成员", {
type: "warning",
@@ -192,16 +267,46 @@ const toggleActive = async (row: StudyMember) => {
await removeMember(projectId.value, row.id);
ElMessage.success("成员已停用");
loadMembers();
logAudit("PROJECT_MEMBER_UPDATED", {
targetId: projectId.value,
targetName: project.value?.name,
before: { is_active: row.is_active },
after: { is_active: false },
severity: "normal",
});
} catch (e: any) {
ElMessage.error(e?.response?.data?.message || "操作失败");
logAudit("PROJECT_MEMBER_UPDATED", {
targetId: projectId.value,
targetName: project.value?.name,
before: { is_active: row.is_active },
after: { is_active: false },
severity: "warning",
reason: e?.response?.data?.message,
});
}
} else {
try {
await updateMember(projectId.value, row.id, { is_active: true });
ElMessage.success("成员已启用");
loadMembers();
logAudit("PROJECT_MEMBER_UPDATED", {
targetId: projectId.value,
targetName: project.value?.name,
before: { is_active: row.is_active },
after: { is_active: true },
severity: "normal",
});
} catch (e: any) {
ElMessage.error(e?.response?.data?.message || "操作失败");
logAudit("PROJECT_MEMBER_UPDATED", {
targetId: projectId.value,
targetName: project.value?.name,
before: { is_active: row.is_active },
after: { is_active: true },
severity: "warning",
reason: e?.response?.data?.message,
});
}
}
};