diff --git a/backend/src/main/java/com/imeeting/controller/RoleController.java b/backend/src/main/java/com/imeeting/controller/RoleController.java index 5a6e57a..66b6231 100644 --- a/backend/src/main/java/com/imeeting/controller/RoleController.java +++ b/backend/src/main/java/com/imeeting/controller/RoleController.java @@ -101,6 +101,18 @@ public class RoleController { // 权限越权校验 Long currentTenantId = getCurrentTenantId(); + SysRole targetRole = sysRoleService.getById(id); + if (targetRole == null) { + return ApiResponse.error("角色不存在"); + } + + // 关键校验:只有平台管理员可以修改 TENANT_ADMIN 角色的权限 + if ("TENANT_ADMIN".equalsIgnoreCase(targetRole.getRoleCode())) { + if (!Long.valueOf(0).equals(currentTenantId)) { + return ApiResponse.error("租户管理员角色的权限只能由平台管理员修改"); + } + } + if (!Long.valueOf(0).equals(currentTenantId)) { List myPerms = sysPermissionService.listByUserId(getCurrentUserId(), currentTenantId); diff --git a/frontend/src/api/http.ts b/frontend/src/api/http.ts index 3f9b75c..a64a096 100644 --- a/frontend/src/api/http.ts +++ b/frontend/src/api/http.ts @@ -1,4 +1,5 @@ import axios from "axios"; +import { message } from "antd"; const http = axios.create({ baseURL: "", @@ -17,8 +18,11 @@ http.interceptors.request.use((config) => { http.interceptors.response.use( (resp) => { const body = resp.data; + // 如果返回的 code 不是 0,表示业务错误 if (body && body.code !== "0") { - const err = new Error(body.msg || "请求失败"); + const errorMsg = body.msg || "请求失败"; + message.error(errorMsg); // 自动展示后端错误消息 + const err = new Error(errorMsg); (err as any).code = body.code; (err as any).msg = body.msg; return Promise.reject(err); @@ -26,17 +30,21 @@ http.interceptors.response.use( return resp; }, (error) => { + // 处理 HTTP 状态码错误 (4xx, 5xx) if (error.response && (error.response.status === 401 || error.response.status === 403)) { - // Clear session/local storage localStorage.removeItem("accessToken"); localStorage.removeItem("refreshToken"); sessionStorage.removeItem("userProfile"); - // Force redirect to login with timeout flag window.location.href = "/login?timeout=1"; + return Promise.reject(error); } - // Process backend error message if available in response body even for non-200 status const body = error.response?.data; + const errorMsg = body?.msg || error.message || "网络异常"; + + // 防止重复弹出相同的提示(可选逻辑,根据需要调整) + message.error(errorMsg); + if (body && body.msg) { const err = new Error(body.msg); (err as any).code = body.code; diff --git a/frontend/src/hooks/usePermission.ts b/frontend/src/hooks/usePermission.ts index a31b434..2ab24e6 100644 --- a/frontend/src/hooks/usePermission.ts +++ b/frontend/src/hooks/usePermission.ts @@ -74,7 +74,6 @@ export function usePermission() { if (isAdmin) return true; if (!codes || codes.length === 0) { - console.log(`Permission check for [${perm}]: codes is empty, returning true`); return true; } @@ -87,8 +86,6 @@ export function usePermission() { } else { result = !hasButtonCodes || codes.includes(perm); } - - console.log(`Permission check for [${perm}]: result=[${result}], hasMenuCodes=[${hasMenuCodes}], hasButtonCodes=[${hasButtonCodes}]`); return result; }; diff --git a/frontend/src/pages/Devices.tsx b/frontend/src/pages/Devices.tsx index cac4bf7..6a0b5ff 100644 --- a/frontend/src/pages/Devices.tsx +++ b/frontend/src/pages/Devices.tsx @@ -52,7 +52,7 @@ export default function Devices() { setData(deviceList || []); setUsers(usersList || []); } catch (e) { - message.error(t('common.error')); + // Handled by interceptor } finally { setLoading(false); } @@ -113,7 +113,7 @@ export default function Devices() { setOpen(false); loadData(); } catch (e) { - if (e instanceof Error && e.message) message.error(e.message); + // Handled by interceptor } finally { setSaving(false); } @@ -125,7 +125,7 @@ export default function Devices() { message.success(t('common.success')); loadData(); } catch (e) { - message.error(t('common.error')); + // Handled by interceptor } }; diff --git a/frontend/src/pages/Login.tsx b/frontend/src/pages/Login.tsx index 6eaca23..db6de63 100644 --- a/frontend/src/pages/Login.tsx +++ b/frontend/src/pages/Login.tsx @@ -25,9 +25,9 @@ export default function Login() { const data = await fetchCaptcha(); setCaptcha(data); } catch (e) { - message.error(t('common.error')); + // Handled by interceptor } - }, [captchaEnabled, t]); + }, [captchaEnabled]); useEffect(() => { const init = async () => { @@ -91,7 +91,6 @@ export default function Login() { message.success(t('common.success')); window.location.href = "/"; } catch (e: any) { - message.error(e.message || t('common.error')); if (captchaEnabled) { loadCaptcha(); } diff --git a/frontend/src/pages/Orgs.tsx b/frontend/src/pages/Orgs.tsx index f77648f..99567bc 100644 --- a/frontend/src/pages/Orgs.tsx +++ b/frontend/src/pages/Orgs.tsx @@ -93,7 +93,7 @@ export default function Orgs() { setSelectedTenantId(list[0].id); } } catch (e) { - message.error(t('common.error')); + // Handled by interceptor } }; @@ -104,7 +104,7 @@ export default function Orgs() { const list = await listOrgs(selectedTenantId); setData(list || []); } catch (e) { - message.error(t('common.error')); + // Handled by interceptor } finally { setLoading(false); } @@ -148,7 +148,7 @@ export default function Orgs() { message.success(t('common.success')); loadOrgs(); } catch (e: any) { - message.error(e.message || t('common.error')); + // Handled by interceptor } }; @@ -166,7 +166,7 @@ export default function Orgs() { setDrawerOpen(false); loadOrgs(); } catch (e) { - if (e instanceof Error && e.message) message.error(e.message); + // Handled by interceptor } finally { setSaving(false); } diff --git a/frontend/src/pages/Permissions.tsx b/frontend/src/pages/Permissions.tsx index 09fef02..d95184b 100644 --- a/frontend/src/pages/Permissions.tsx +++ b/frontend/src/pages/Permissions.tsx @@ -161,7 +161,7 @@ export default function Permissions() { setOpen(false); load(); } catch (e) { - if (e instanceof Error && e.message) message.error(e.message); + // Handled by interceptor } finally { setSaving(false); } @@ -173,7 +173,7 @@ export default function Permissions() { message.success(t('common.success')); load(); } catch (e) { - message.error(t('common.error')); + // Handled by interceptor } }; diff --git a/frontend/src/pages/PlatformSettings.tsx b/frontend/src/pages/PlatformSettings.tsx index e4b3363..83b95a8 100644 --- a/frontend/src/pages/PlatformSettings.tsx +++ b/frontend/src/pages/PlatformSettings.tsx @@ -37,7 +37,7 @@ export default function PlatformSettings() { const data = await getAdminPlatformConfig(); form.setFieldsValue(data); } catch (e) { - message.error(t('common.error')); + // Handled by interceptor } finally { setLoading(false); } @@ -53,7 +53,7 @@ export default function PlatformSettings() { form.setFieldValue(fieldName, url); message.success(t('common.success')); } catch (e) { - message.error(t('common.error')); + // Handled by interceptor } return false; // 阻止自动上传 }; @@ -64,7 +64,7 @@ export default function PlatformSettings() { await updatePlatformConfig(values); message.success(t('common.success')); } catch (e) { - message.error(t('common.error')); + // Handled by interceptor } finally { setSaving(false); } diff --git a/frontend/src/pages/RolePermissionBinding.tsx b/frontend/src/pages/RolePermissionBinding.tsx index 5f1745d..8f9f5e5 100644 --- a/frontend/src/pages/RolePermissionBinding.tsx +++ b/frontend/src/pages/RolePermissionBinding.tsx @@ -74,6 +74,16 @@ export default function RolePermissionBinding() { const [saving, setSaving] = useState(false); const [selectedRoleId, setSelectedRoleId] = useState(null); + // Platform admin check + const isPlatformMode = useMemo(() => { + const profileStr = sessionStorage.getItem("userProfile"); + if (profileStr) { + const profile = JSON.parse(profileStr); + return profile.isPlatformAdmin && localStorage.getItem("activeTenantId") === "0"; + } + return false; + }, []); + // Selection states const [checkedPermIds, setCheckedPermIds] = useState([]); const [halfCheckedIds, setHalfCheckedIds] = useState([]); @@ -102,7 +112,7 @@ export default function RolePermissionBinding() { const list = await listPermissions(); setPermissions(list || []); } catch (e) { - message.error(t('common.error')); + // Handled by interceptor } finally { setLoadingPerms(false); } @@ -121,7 +131,6 @@ export default function RolePermissionBinding() { setHalfCheckedIds([]); } catch (e) { setCheckedPermIds([]); - message.error(t('common.error')); } }; @@ -161,7 +170,7 @@ export default function RolePermissionBinding() { await saveRolePermissions(selectedRoleId, allPermIds); message.success(t('common.success')); } catch (e) { - message.error(t('common.error')); + // Handled by interceptor } finally { setSaving(false); } @@ -179,7 +188,7 @@ export default function RolePermissionBinding() { icon={