完全移除模块级权限系统,迁移至接口级权限
后端: - 删除 StudyRolePermission 模型和 project_permissions API 文件 - 重写 project_permissions.py core,移除所有模块级权限函数 - 重写 permission_cache.py,移除模块级权限缓存逻辑 - 新增权限模板功能(PermissionTemplate 模型、API、服务层) - 新增 permission_templates 数据库迁移 - 迁移 8 个模块(attachments、audit_logs、dashboard、faqs、 faq_categories、fees_attachments、knowledge_notes、 material_equipments、overview、subject_histories、subject_pds) 至接口级权限检查 - 删除所有模块级权限相关测试文件,新增权限模板测试 前端: - 删除 ProjectPermissions.vue 和 ProjectPermissionsModule.vue - 重写 projectRoutePermissions.ts,改为基于接口级权限格式 - 更新 store/study.ts、router/index.ts、AuditLogs.vue、Projects.vue 中的权限 API 调用,从 fetchProjectRolePermissions 改为 fetchApiEndpointPermissions - 清理 types/api.ts 中的旧模块级权限类型定义 - 新增 PermissionTemplateSelector.vue 组件 - 更新权限管理页面,移除模块级权限 tab Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -30,19 +30,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 标签页:模块级权限 vs 接口级权限 -->
|
||||
<!-- 标签页 -->
|
||||
<el-tabs v-model="activeTab">
|
||||
<!-- 标签1:模块级权限(现有) -->
|
||||
<el-tab-pane label="模块级权限" name="module">
|
||||
<ProjectPermissionsModule
|
||||
:project="project"
|
||||
:matrix="moduleMatrix"
|
||||
@update="onModuleMatrixUpdate"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 标签2:接口级权限(新增) -->
|
||||
<!-- 接口级权限 -->
|
||||
<el-tab-pane label="接口级权限" name="api">
|
||||
<PermissionTemplateSelector
|
||||
:study-id="studyId"
|
||||
:current-permissions="currentPermissionsForTemplate"
|
||||
@applied="onTemplateApplied"
|
||||
/>
|
||||
<el-divider />
|
||||
<ApiEndpointPermissions
|
||||
:project="project"
|
||||
:matrix="apiMatrix"
|
||||
@@ -50,7 +47,7 @@
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 标签3:权限监控(新增) -->
|
||||
<!-- 权限监控 -->
|
||||
<el-tab-pane label="权限监控" name="monitoring">
|
||||
<PermissionMonitoring
|
||||
:metrics="metrics"
|
||||
@@ -69,9 +66,7 @@ import { ref, computed, onMounted } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { Key, RefreshRight, Check } from "@element-plus/icons-vue";
|
||||
import type { Study } from "@/types/api";
|
||||
import type {
|
||||
ProjectRolePermissionsResponse,
|
||||
ApiEndpointPermissionsResponse,
|
||||
PermissionMetricsResponse,
|
||||
CacheStatsResponse,
|
||||
@@ -79,8 +74,6 @@ import type {
|
||||
HealthResponse,
|
||||
} from "@/types/api";
|
||||
import {
|
||||
fetchProjectRolePermissions,
|
||||
updateProjectRolePermissions,
|
||||
fetchApiEndpointPermissions,
|
||||
updateApiEndpointPermissions,
|
||||
fetchPermissionMetrics,
|
||||
@@ -90,14 +83,14 @@ import {
|
||||
resetPermissionMetrics,
|
||||
} from "@/api/projectPermissions";
|
||||
import { useStudyStore } from "@/store/study";
|
||||
import ProjectPermissionsModule from "@/components/ProjectPermissionsModule.vue";
|
||||
import ApiEndpointPermissions from "@/components/ApiEndpointPermissions.vue";
|
||||
import PermissionMonitoring from "@/components/PermissionMonitoring.vue";
|
||||
import PermissionTemplateSelector from "@/components/PermissionTemplateSelector.vue";
|
||||
|
||||
const route = useRoute();
|
||||
const studyStore = useStudyStore();
|
||||
|
||||
const activeTab = ref<"module" | "api" | "monitoring">("module");
|
||||
const activeTab = ref<"api" | "monitoring">("api");
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const resetting = ref(false);
|
||||
@@ -109,7 +102,6 @@ const studyId = computed(() => {
|
||||
});
|
||||
|
||||
// 权限数据
|
||||
const moduleMatrix = ref<ProjectRolePermissionsResponse | null>(null);
|
||||
const apiMatrix = ref<ApiEndpointPermissionsResponse | null>(null);
|
||||
|
||||
// 监控数据
|
||||
@@ -125,12 +117,7 @@ const loadPermissionData = async () => {
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const [moduleRes, apiRes] = await Promise.all([
|
||||
fetchProjectRolePermissions(studyId.value),
|
||||
fetchApiEndpointPermissions(studyId.value),
|
||||
]);
|
||||
|
||||
moduleMatrix.value = moduleRes.data;
|
||||
const apiRes = await fetchApiEndpointPermissions(studyId.value);
|
||||
apiMatrix.value = apiRes.data;
|
||||
dirty.value = false;
|
||||
} catch (error) {
|
||||
@@ -160,28 +147,36 @@ const loadMonitoringData = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const onModuleMatrixUpdate = (newMatrix: ProjectRolePermissionsResponse) => {
|
||||
moduleMatrix.value = newMatrix;
|
||||
dirty.value = true;
|
||||
};
|
||||
|
||||
const onApiMatrixUpdate = (newMatrix: ApiEndpointPermissionsResponse) => {
|
||||
apiMatrix.value = newMatrix;
|
||||
dirty.value = true;
|
||||
};
|
||||
|
||||
// 将当前 apiMatrix 转换为模板所需的 {role: {endpoint_key: bool}} 格式
|
||||
const currentPermissionsForTemplate = computed(() => {
|
||||
if (!apiMatrix.value) return undefined;
|
||||
const result: Record<string, Record<string, boolean>> = {};
|
||||
for (const [role, endpoints] of Object.entries(apiMatrix.value as Record<string, Record<string, { allowed: boolean }>>)) {
|
||||
result[role] = {};
|
||||
for (const [key, val] of Object.entries(endpoints)) {
|
||||
result[role][key] = val.allowed;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
const onTemplateApplied = async (permissions: Record<string, Record<string, { allowed: boolean }>>) => {
|
||||
// 模板应用后刷新权限矩阵
|
||||
await loadPermissionData();
|
||||
ElMessage.success("权限已更新");
|
||||
};
|
||||
|
||||
const save = async () => {
|
||||
if (!studyId.value || !dirty.value) return;
|
||||
|
||||
saving.value = true;
|
||||
try {
|
||||
// 保存当前活跃标签的权限
|
||||
if (activeTab.value === "module" && moduleMatrix.value) {
|
||||
const res = await updateProjectRolePermissions(studyId.value, {
|
||||
roles: moduleMatrix.value.roles,
|
||||
});
|
||||
moduleMatrix.value = res.data;
|
||||
} else if (activeTab.value === "api" && apiMatrix.value) {
|
||||
if (apiMatrix.value) {
|
||||
const res = await updateApiEndpointPermissions(studyId.value, apiMatrix.value);
|
||||
apiMatrix.value = res.data;
|
||||
}
|
||||
@@ -189,7 +184,6 @@ const save = async () => {
|
||||
dirty.value = false;
|
||||
ElMessage.success("权限已保存");
|
||||
|
||||
// 重新加载数据以确保同步
|
||||
await loadPermissionData();
|
||||
} catch (error) {
|
||||
ElMessage.error("保存权限失败");
|
||||
|
||||
Reference in New Issue
Block a user