diff --git a/src/main/java/com/yunhai/userbase/service/impl/GroupAuthzService.java b/src/main/java/com/yunhai/userbase/service/impl/GroupAuthzService.java
index 9dddf0c..2922d5e 100644
--- a/src/main/java/com/yunhai/userbase/service/impl/GroupAuthzService.java
+++ b/src/main/java/com/yunhai/userbase/service/impl/GroupAuthzService.java
@@ -910,6 +910,26 @@ public class GroupAuthzService implements IGroupAuthzService {
return null;
}
+ /**
+ * 子组继承闸门:判定「来源组 authority 对 moduleId 的记录」是否对本组 selfGroupId 生效。
+ *
+ *
规则(修复「父级未勾子组继承却仍被继承」的 bug):
+ * ① 来源组 == 本组:本组自身勾选的模块直接生效——{@code inheritFlag} 只约束是否下放到子组,不影响本组自己;
+ * ② 来源组为祖先(往上回溯得来):必须勾选了「子组继承」({@code inheritFlag=true}) 才会下放到本组,
+ * 否则该模块不继承。即「只过滤勾选了子组继承的模块;若来源组对该应用所有模块都没勾子组继承,则本组对该应用返回空」。
+ */
+ private boolean isPermEffectiveForSelf(Integer authority, Integer selfGroupId,
+ Map permMap) {
+ SysGroupModulePerm perm = (permMap == null) ? null : permMap.get(authority);
+ if (perm == null) {
+ return false;
+ }
+ if (authority.equals(selfGroupId)) {
+ return true;
+ }
+ return Boolean.TRUE.equals(perm.getInheritFlag());
+ }
+
/**
* 用户维度权限判定:用户对指定模块是否有权限(就近原则 + 学校截止 + 角色授权并集 + 超管兜底)。
*
@@ -921,6 +941,10 @@ public class GroupAuthzService implements IGroupAuthzService {
* ② 本组无配置(如英语组为空)→ 取最近的有配置祖先(如教师组)为来源,不再越级到更远的学校组;
* ③ 来源组的模块集合即最终集合,更上层同应用模块(如学校组多勾的模块8)一律不合并。
*
+ * 子组继承闸门(修复「父级未勾子组继承仍被继承」的 bug):来源组若是本组,本组勾选的模块直接生效;
+ * 来源组若是祖先,必须该模块记录 {@code inheritFlag=true}(勾选子组继承)才下放到本组,否则不继承——
+ * 即「只过滤勾选了子组继承的模块;若来源组对该应用所有模块都没勾子组继承,则本组对该应用返回空」。
+ *
*
学校截止:祖先链只查到第一个学校组(含)为止,不跨学校到教育局/根。
*
*
其余:归属组需已开通该模块所属应用(双保险,防越权)。
@@ -947,8 +971,9 @@ public class GroupAuthzService implements IGroupAuthzService {
if (authority == null) {
continue;
}
- // 权限来源组对该模块有显式记录 → 许可;否则(来源组未勾此模块)不许可
- if (permMap != null && permMap.get(authority) != null) {
+ // 来源组对该模块有显式记录,且(来源是本组 或 来源是祖先且勾选了子组继承)→ 许可;
+ // 否则(来源组未勾此模块 / 祖先未开启子组继承)不许可。
+ if (isPermEffectiveForSelf(authority, selfGroupId, permMap)) {
return true;
}
}
@@ -1233,7 +1258,7 @@ public class GroupAuthzService implements IGroupAuthzService {
continue;
}
Map pm = modulePermMap.get(moduleId);
- if (pm != null && pm.get(authority) != null) {
+ if (isPermEffectiveForSelf(authority, selfGroupId, pm)) {
return true;
}
}
diff --git a/src/test/java/com/yunhai/userbase/service/impl/GroupAuthzServiceTest.java b/src/test/java/com/yunhai/userbase/service/impl/GroupAuthzServiceTest.java
new file mode 100644
index 0000000..b342f68
--- /dev/null
+++ b/src/test/java/com/yunhai/userbase/service/impl/GroupAuthzServiceTest.java
@@ -0,0 +1,492 @@
+package com.yunhai.userbase.service.impl;
+
+import com.yunhai.userbase.entity.AppCategory;
+import com.yunhai.userbase.entity.AppModule;
+import com.yunhai.userbase.entity.ApplicationInfo;
+import com.yunhai.userbase.entity.SysGroup;
+import com.yunhai.userbase.entity.SysGroupAppRel;
+import com.yunhai.userbase.entity.SysGroupModulePerm;
+import com.yunhai.userbase.entity.SysUser;
+import com.yunhai.userbase.entity.SysUserGroupRel;
+import com.yunhai.userbase.enums.GroupTypeEnum;
+import com.yunhai.userbase.enums.UserTypeEnum;
+import com.yunhai.userbase.model.groupauthz.UserPermissionCategoryVo;
+import com.yunhai.userbase.model.groupauthz.UserPermissionModuleVo;
+import com.yunhai.userbase.repository.AppCategoryRepository;
+import com.yunhai.userbase.repository.AppModuleRepository;
+import com.yunhai.userbase.repository.ApplicationInfoRepository;
+import com.yunhai.userbase.repository.SysAppRoleModuleRelRepository;
+import com.yunhai.userbase.repository.SysAppRoleRepository;
+import com.yunhai.userbase.repository.SysGroupAppRelRepository;
+import com.yunhai.userbase.repository.SysGroupModulePermRepository;
+import com.yunhai.userbase.repository.SysGroupRepository;
+import com.yunhai.userbase.repository.SysUserAppRoleRelRepository;
+import com.yunhai.userbase.repository.SysUserGroupRelRepository;
+import com.yunhai.userbase.repository.SysUserRepository;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.Mockito.when;
+
+/**
+ * GroupAuthzService 权限计算单元测试。
+ * 重点验证「就近原则 + 子组继承闸门」:沿组的祖先链向上,取最近的有显式模块配置的组作为该应用唯一权限来源,
+ * 只返回该来源组的模块集合(不向上合并更远的祖先);且来源组若为祖先,必须勾选子组继承(inheritFlag=true)才下放
+ * (修复「子组只勾1个却继承学校全部」「返回3个而非最近祖先的2个」「父级未勾子组继承仍被继承」三类 bug)。
+ */
+class GroupAuthzServiceTest {
+
+ @InjectMocks
+ private GroupAuthzService groupAuthzService;
+
+ @Mock
+ private SysGroupAppRelRepository sysGroupAppRelRepository;
+ @Mock
+ private SysGroupModulePermRepository sysGroupModulePermRepository;
+ @Mock
+ private SysGroupRepository sysGroupRepository;
+ @Mock
+ private ApplicationInfoRepository applicationInfoRepository;
+ @Mock
+ private AppCategoryRepository appCategoryRepository;
+ @Mock
+ private AppModuleRepository appModuleRepository;
+ @Mock
+ private SysUserGroupRelRepository sysUserGroupRelRepository;
+ @Mock
+ private SysUserRepository sysUserRepository;
+ @Mock
+ private SysAppRoleRepository sysAppRoleRepository;
+ @Mock
+ private SysAppRoleModuleRelRepository sysAppRoleModuleRelRepository;
+ @Mock
+ private SysUserAppRoleRelRepository sysUserAppRoleRelRepository;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ initGroups();
+ }
+
+ /**
+ * 构造一组固定层级:
+ * 5 教育云中心(根)
+ * └─ 27 信阳教育局
+ * └─ 16 云海中学(学校)
+ * ├─ 29 教师组
+ * │ └─ 31 英语组
+ * └─ 30 学生组
+ */
+ private void initGroups() {
+ when(sysGroupRepository.findAll()).thenReturn(Arrays.asList(
+ group(5, "教育云中心", 0, GroupTypeEnum.NORMAL_GROUP),
+ group(27, "信阳教育局", 5, GroupTypeEnum.AREA_GROUP),
+ group(16, "云海中学", 27, GroupTypeEnum.SCHOOL_GROUP),
+ group(28, "信阳高级中学", 27, GroupTypeEnum.SCHOOL_GROUP),
+ group(29, "教师组", 16, GroupTypeEnum.TEACHER_GROUP),
+ group(30, "学生组", 16, GroupTypeEnum.STUDENT_GROUP),
+ group(31, "英语组", 29, GroupTypeEnum.NORMAL_GROUP)
+ ));
+ }
+
+ @Test
+ void findUserPermissionTree_whenSelfGroupConfiguredApp_shouldNotInheritAncestorModules() {
+ // user 1 仅属于英语组 31
+ when(sysUserRepository.findById(1)).thenReturn(Optional.of(user(1, UserTypeEnum.NORMAL)));
+ when(sysUserGroupRelRepository.findByUserId(1)).thenReturn(
+ Collections.singletonList(rel(1, 31, 1)));
+
+ // 祖先链上的应用开通:只有学校 16 开通了 100003
+ when(sysGroupAppRelRepository.findByGroupIdIn(anyList())).thenAnswer(inv -> {
+ List ids = inv.getArgument(0);
+ if (ids.contains(16)) {
+ return Collections.singletonList(appRel(16, 100003));
+ }
+ return Collections.emptyList();
+ });
+
+ ApplicationInfo app = new ApplicationInfo();
+ app.setId(100003);
+ app.setName("通知公告");
+ app.setAppKey(100002);
+ app.setCategoryId(2);
+ when(applicationInfoRepository.findAllById(any())).thenReturn(Collections.singletonList(app));
+
+ AppCategory category = new AppCategory();
+ category.setId(2);
+ category.setName("家校互动");
+ category.setSortNo(2);
+ when(appCategoryRepository.findAllByOrderBySortNoAsc()).thenReturn(Collections.singletonList(category));
+
+ AppModule m8 = module(8, 100003, "已收通知");
+ AppModule m9 = module(9, 100003, "已发通知");
+ AppModule m10 = module(10, 100003, "全部通知");
+ when(appModuleRepository.findByAppIdIn(anyList())).thenReturn(Arrays.asList(m8, m9, m10));
+
+ // 权限记录:英语组 31 只有模块 8;学校 16 有 8/9/10 且都允许继承
+ when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(anyList(), anyList()))
+ .thenReturn(Arrays.asList(
+ perm(31, 8, true),
+ perm(16, 8, true),
+ perm(16, 9, true),
+ perm(16, 10, true)));
+
+ List result = groupAuthzService.findUserPermissionTree(31, 1);
+
+ assertEquals(1, result.size(), "应只有一个分类");
+ assertEquals("家校互动", result.get(0).getName());
+ assertEquals(1, result.get(0).getApps().size(), "应只有一个应用");
+ assertEquals(1, result.get(0).getApps().get(0).getModules().size(), "只应返回模块 8");
+ assertEquals(8, result.get(0).getApps().get(0).getModules().get(0).getId());
+ }
+
+ @Test
+ void findUserPermissionTree_whenSelfGroupNotConfiguredApp_shouldInheritFromSchool() {
+ // user 2 仅属于学生组 30,学生组没有配置通知公告应用的任何模块
+ when(sysUserRepository.findById(2)).thenReturn(Optional.of(user(2, UserTypeEnum.NORMAL)));
+ when(sysUserGroupRelRepository.findByUserId(2)).thenReturn(
+ Collections.singletonList(rel(2, 30, 1)));
+
+ when(sysGroupAppRelRepository.findByGroupIdIn(anyList())).thenAnswer(inv -> {
+ List ids = inv.getArgument(0);
+ if (ids.contains(16)) {
+ return Collections.singletonList(appRel(16, 100003));
+ }
+ return Collections.emptyList();
+ });
+
+ ApplicationInfo app = new ApplicationInfo();
+ app.setId(100003);
+ app.setName("通知公告");
+ app.setAppKey(100002);
+ app.setCategoryId(2);
+ when(applicationInfoRepository.findAllById(any())).thenReturn(Collections.singletonList(app));
+
+ AppCategory category = new AppCategory();
+ category.setId(2);
+ category.setName("家校互动");
+ category.setSortNo(2);
+ when(appCategoryRepository.findAllByOrderBySortNoAsc()).thenReturn(Collections.singletonList(category));
+
+ AppModule m8 = module(8, 100003, "已收通知");
+ AppModule m9 = module(9, 100003, "已发通知");
+ AppModule m10 = module(10, 100003, "全部通知");
+ when(appModuleRepository.findByAppIdIn(anyList())).thenReturn(Arrays.asList(m8, m9, m10));
+
+ // 学校 16 三个模块都开放继承;学生组 30 没有任何记录
+ when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(anyList(), anyList()))
+ .thenReturn(Arrays.asList(
+ perm(16, 8, true),
+ perm(16, 9, true),
+ perm(16, 10, true)));
+
+ List result = groupAuthzService.findUserPermissionTree(30, 2);
+
+ assertEquals(3, result.get(0).getApps().get(0).getModules().size(),
+ "未配置应用时,应继承学校的全部 3 个模块");
+ }
+
+ @Test
+ void hasModulePermission_whenSelfGroupConfiguredApp_onlySelfRecordsCount() {
+ AppModule m8 = module(8, 100003, "已收通知");
+ AppModule m9 = module(9, 100003, "已发通知");
+ AppModule m10 = module(10, 100003, "全部通知");
+ when(appModuleRepository.findById(9)).thenReturn(Optional.of(m9));
+ when(sysUserAppRoleRelRepository.findByUserId(1)).thenReturn(Collections.emptyList());
+
+ when(sysUserGroupRelRepository.findByUserId(1)).thenReturn(
+ Collections.singletonList(rel(1, 31, 1)));
+
+ when(sysGroupAppRelRepository.existsByGroupIdAndAppId(31, 100003)).thenReturn(true);
+ when(appModuleRepository.findByAppIdIn(Collections.singletonList(100003))).thenReturn(Arrays.asList(m8, m9, m10));
+
+ // 英语组 31 配置了应用 100003(模块 8);学校 16 有模块 9(但 31 已就近接管,不应继承)
+ when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(
+ Arrays.asList(8, 9, 10), Arrays.asList(31, 29, 16, 27, 5)))
+ .thenReturn(Arrays.asList(perm(31, 8, true), perm(16, 9, true)));
+
+ assertFalse(groupAuthzService.hasModulePermission(1, 9),
+ "英语组已配置通知公告应用(就近接管),不应再继承学校的已发通知模块");
+ }
+
+ @Test
+ void hasModulePermission_whenSelfGroupHasTheModule_shouldReturnTrue() {
+ AppModule m8 = module(8, 100003, "已收通知");
+ AppModule m9 = module(9, 100003, "已发通知");
+ AppModule m10 = module(10, 100003, "全部通知");
+ when(appModuleRepository.findById(8)).thenReturn(Optional.of(m8));
+ when(sysUserAppRoleRelRepository.findByUserId(1)).thenReturn(Collections.emptyList());
+
+ when(sysUserGroupRelRepository.findByUserId(1)).thenReturn(
+ Collections.singletonList(rel(1, 31, 1)));
+
+ when(sysGroupAppRelRepository.existsByGroupIdAndAppId(31, 100003)).thenReturn(true);
+ when(appModuleRepository.findByAppIdIn(Collections.singletonList(100003))).thenReturn(Arrays.asList(m8, m9, m10));
+
+ when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(
+ Arrays.asList(8, 9, 10), Arrays.asList(31, 29, 16, 27, 5)))
+ .thenReturn(Arrays.asList(perm(31, 8, true), perm(16, 8, true)));
+
+ assertTrue(groupAuthzService.hasModulePermission(1, 8),
+ "英语组自身已配置模块 8,应返回 true");
+ }
+
+ @Test
+ void findUserPermissionTree_whenSelfGroupEmpty_shouldUseNearestAncestorTeacherGroup() {
+ // user 1 仅属于英语组 31;英语组(31)无任何模块授权,教师组(29)有模块9/10,学校(16)有8/9/10
+ when(sysUserRepository.findById(1)).thenReturn(Optional.of(user(1, UserTypeEnum.NORMAL)));
+ when(sysUserGroupRelRepository.findByUserId(1)).thenReturn(
+ Collections.singletonList(rel(1, 31, 1)));
+
+ when(sysGroupAppRelRepository.findByGroupIdIn(anyList())).thenAnswer(inv -> {
+ List ids = inv.getArgument(0);
+ if (ids.contains(16)) {
+ return Collections.singletonList(appRel(16, 100003));
+ }
+ return Collections.emptyList();
+ });
+
+ ApplicationInfo app = new ApplicationInfo();
+ app.setId(100003);
+ app.setName("通知公告");
+ app.setAppKey(100002);
+ app.setCategoryId(2);
+ when(applicationInfoRepository.findAllById(any())).thenReturn(Collections.singletonList(app));
+
+ AppCategory category = new AppCategory();
+ category.setId(2);
+ category.setName("家校互动");
+ category.setSortNo(2);
+ when(appCategoryRepository.findAllByOrderBySortNoAsc()).thenReturn(Collections.singletonList(category));
+
+ AppModule m8 = module(8, 100003, "已收通知");
+ AppModule m9 = module(9, 100003, "已发通知");
+ AppModule m10 = module(10, 100003, "全部通知");
+ when(appModuleRepository.findByAppIdIn(anyList())).thenReturn(Arrays.asList(m8, m9, m10));
+
+ // 英语组31空;教师组29有9/10;学校16有8/9/10
+ when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(anyList(), anyList()))
+ .thenReturn(Arrays.asList(
+ perm(29, 9, true),
+ perm(29, 10, true),
+ perm(16, 8, true),
+ perm(16, 9, true),
+ perm(16, 10, true)));
+
+ List result = groupAuthzService.findUserPermissionTree(31, 1);
+
+ assertEquals(1, result.size(), "应只有一个分类");
+ assertEquals(1, result.get(0).getApps().size(), "应只有一个应用");
+ assertEquals(2, result.get(0).getApps().get(0).getModules().size(),
+ "本组空→取最近教师组29的2个模块,不应合并学校组多出的模块8");
+ Set moduleIds = new LinkedHashSet<>();
+ for (UserPermissionModuleVo mv : result.get(0).getApps().get(0).getModules()) {
+ moduleIds.add(mv.getId());
+ }
+ assertTrue(moduleIds.contains(9) && moduleIds.contains(10) && moduleIds.size() == 2,
+ "应仅含模块 9 和 10,不含学校组多出的模块 8");
+ }
+
+ @Test
+ void findUserPermissionTree_whenNearestAncestorAllNotInheritable_returnsEmpty() {
+ // 英语组31空;教师组29有9/10但 inheritFlag=false(未勾子组继承);学校16有8/9/10
+ when(sysUserRepository.findById(1)).thenReturn(Optional.of(user(1, UserTypeEnum.NORMAL)));
+ when(sysUserGroupRelRepository.findByUserId(1)).thenReturn(
+ Collections.singletonList(rel(1, 31, 1)));
+
+ when(sysGroupAppRelRepository.findByGroupIdIn(anyList())).thenAnswer(inv -> {
+ List ids = inv.getArgument(0);
+ if (ids.contains(16)) {
+ return Collections.singletonList(appRel(16, 100003));
+ }
+ return Collections.emptyList();
+ });
+
+ ApplicationInfo app = new ApplicationInfo();
+ app.setId(100003);
+ app.setName("通知公告");
+ app.setAppKey(100002);
+ app.setCategoryId(2);
+ when(applicationInfoRepository.findAllById(any())).thenReturn(Collections.singletonList(app));
+
+ AppCategory category = new AppCategory();
+ category.setId(2);
+ category.setName("家校互动");
+ category.setSortNo(2);
+ when(appCategoryRepository.findAllByOrderBySortNoAsc()).thenReturn(Collections.singletonList(category));
+
+ AppModule m8 = module(8, 100003, "已收通知");
+ AppModule m9 = module(9, 100003, "已发通知");
+ AppModule m10 = module(10, 100003, "全部通知");
+ when(appModuleRepository.findByAppIdIn(anyList())).thenReturn(Arrays.asList(m8, m9, m10));
+
+ // 教师组29(最近来源)所有模块 inheritFlag=false;即便学校16开放,也不应被继承
+ when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(anyList(), anyList()))
+ .thenReturn(Arrays.asList(
+ perm(29, 9, false),
+ perm(29, 10, false),
+ perm(16, 8, true),
+ perm(16, 9, true),
+ perm(16, 10, true)));
+
+ List result = groupAuthzService.findUserPermissionTree(31, 1);
+
+ // 最近来源(教师组)对该应用所有模块都未勾子组继承 → 本组该应用为空,不应合并学校组
+ assertTrue(result.isEmpty(),
+ "最近祖先(教师组)未勾子组继承,应返回空,不应从学校组继承任何模块");
+ }
+
+ @Test
+ void findUserPermissionTree_whenNearestAncestorPartiallyInheritable_returnsOnlyInheritable() {
+ // 英语组31空;教师组29有9(inheritFlag=true)/10(inheritFlag=false);学校16有8/9/10
+ when(sysUserRepository.findById(1)).thenReturn(Optional.of(user(1, UserTypeEnum.NORMAL)));
+ when(sysUserGroupRelRepository.findByUserId(1)).thenReturn(
+ Collections.singletonList(rel(1, 31, 1)));
+
+ when(sysGroupAppRelRepository.findByGroupIdIn(anyList())).thenAnswer(inv -> {
+ List ids = inv.getArgument(0);
+ if (ids.contains(16)) {
+ return Collections.singletonList(appRel(16, 100003));
+ }
+ return Collections.emptyList();
+ });
+
+ ApplicationInfo app = new ApplicationInfo();
+ app.setId(100003);
+ app.setName("通知公告");
+ app.setAppKey(100002);
+ app.setCategoryId(2);
+ when(applicationInfoRepository.findAllById(any())).thenReturn(Collections.singletonList(app));
+
+ AppCategory category = new AppCategory();
+ category.setId(2);
+ category.setName("家校互动");
+ category.setSortNo(2);
+ when(appCategoryRepository.findAllByOrderBySortNoAsc()).thenReturn(Collections.singletonList(category));
+
+ AppModule m8 = module(8, 100003, "已收通知");
+ AppModule m9 = module(9, 100003, "已发通知");
+ AppModule m10 = module(10, 100003, "全部通知");
+ when(appModuleRepository.findByAppIdIn(anyList())).thenReturn(Arrays.asList(m8, m9, m10));
+
+ when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(anyList(), anyList()))
+ .thenReturn(Arrays.asList(
+ perm(29, 9, true),
+ perm(29, 10, false),
+ perm(16, 8, true),
+ perm(16, 9, true),
+ perm(16, 10, true)));
+
+ List result = groupAuthzService.findUserPermissionTree(31, 1);
+
+ assertEquals(1, result.size(), "应只有一个分类");
+ assertEquals(1, result.get(0).getApps().size(), "应只有一个应用");
+ assertEquals(1, result.get(0).getApps().get(0).getModules().size(),
+ "最近来源(教师组)只有模块9勾了子组继承,应只返回模块9,模块10与学校组模块8均不继承");
+ assertEquals(9, result.get(0).getApps().get(0).getModules().get(0).getId());
+ }
+
+ @Test
+ void hasModulePermission_whenSelfGroupEmpty_nearestAncestorIsTeacherGroup() {
+ // 英语组31空,教师组29有9/10,学校16有8/9/10
+ AppModule m8 = module(8, 100003, "已收通知");
+ AppModule m9 = module(9, 100003, "已发通知");
+ AppModule m10 = module(10, 100003, "全部通知");
+ when(appModuleRepository.findById(8)).thenReturn(Optional.of(m8));
+ when(appModuleRepository.findById(9)).thenReturn(Optional.of(m9));
+ when(appModuleRepository.findById(10)).thenReturn(Optional.of(m10));
+ when(sysUserAppRoleRelRepository.findByUserId(1)).thenReturn(Collections.emptyList());
+ when(sysUserGroupRelRepository.findByUserId(1)).thenReturn(
+ Collections.singletonList(rel(1, 31, 1)));
+ when(sysGroupAppRelRepository.existsByGroupIdAndAppId(31, 100003)).thenReturn(true);
+ when(appModuleRepository.findByAppIdIn(Collections.singletonList(100003))).thenReturn(Arrays.asList(m8, m9, m10));
+ when(sysGroupModulePermRepository.findByModuleIdInAndGroupIdIn(
+ Arrays.asList(8, 9, 10), Arrays.asList(31, 29, 16, 27, 5)))
+ .thenReturn(Arrays.asList(
+ perm(29, 9, true), perm(29, 10, true),
+ perm(16, 8, true), perm(16, 9, true), perm(16, 10, true)));
+
+ assertFalse(groupAuthzService.hasModulePermission(1, 8),
+ "英语组为空→最近来源是教师组29,学校组多勾的模块8不应被继承");
+ assertTrue(groupAuthzService.hasModulePermission(1, 9),
+ "教师组29勾选的模块9应可访问");
+ assertTrue(groupAuthzService.hasModulePermission(1, 10),
+ "教师组29勾选的模块10应可访问");
+ }
+
+ // ===================== helper =====================
+
+ private SysGroup group(Integer id, String name, Integer parentId, GroupTypeEnum type) {
+ SysGroup g = new SysGroup();
+ g.setId(id);
+ g.setName(name);
+ g.setParentId(parentId);
+ g.setType(type);
+ return g;
+ }
+
+ private SysUser user(Integer id, UserTypeEnum type) {
+ SysUser u = new SysUser();
+ u.setId(id);
+ u.setType(type);
+ u.setAccount("u" + id);
+ u.setRealName("用户" + id);
+ return u;
+ }
+
+ private SysUserGroupRel rel(Integer userId, Integer groupId, Integer isMain) {
+ SysUserGroupRel r = new SysUserGroupRel();
+ r.setUserId(userId);
+ r.setGroupId(groupId);
+ r.setIsMain(isMain);
+ return r;
+ }
+
+ private SysGroupAppRel appRel(Integer groupId, Integer appId) {
+ SysGroupAppRel r = new SysGroupAppRel();
+ r.setGroupId(groupId);
+ r.setAppId(appId);
+ return r;
+ }
+
+ private SysGroupModulePerm perm(Integer groupId, Integer moduleId, boolean inherit) {
+ SysGroupModulePerm p = new SysGroupModulePerm();
+ p.setGroupId(groupId);
+ p.setModuleId(moduleId);
+ p.setInheritFlag(inherit);
+ return p;
+ }
+
+ private ApplicationInfo app(Integer id, String name, Integer categoryId) {
+ ApplicationInfo a = new ApplicationInfo();
+ a.setId(id);
+ a.setName(name);
+ a.setCategoryId(categoryId);
+ return a;
+ }
+
+ private AppModule module(Integer id, Integer appId, String name) {
+ AppModule m = new AppModule();
+ m.setId(id);
+ m.setAppId(appId);
+ m.setName(name);
+ m.setCustomId(String.valueOf(id));
+ m.setLevel(1);
+ return m;
+ }
+}