fix(权限): 店铺数据重复检查仅勾菜单无「店铺数据记录」仍403——校验口径与自动补权对齐(任务/重复检查任一菜单+数据权限即可放行,镜像魏振峰反馈: 只勾103未勾94且数据权限已补仍被拒); 顺带修复 controller 直连 mapper 架构违规 resolveVisibleShopNameKeys 下沉 ShopDataCrawlAdminTasksService

This commit is contained in:
2026-09-07 15:08:43 +08:00
parent 4994142a67
commit 6d6cfcb6ff
4 changed files with 156 additions and 15 deletions
@@ -739,6 +739,132 @@ class PermissionMenuServiceTest {
assertThat(captor.getValue().getColumnId()).isEqualTo(10L);
}
@Test
void shopDataTaskAccessAllowsDuplicateCheckMenuOnlyWithDataPermission() {
// 反馈场景(魏振峰):只勾「店铺数据重复检查」菜单、数据权限已自动补上,
// 虽未勾「店铺数据记录」菜单,仍应放行(校验口径与 autoGrant 保持一致)。
PermissionMenuMapper menuMapper = mock(PermissionMenuMapper.class);
UserColumnPermissionMapper permissionMapper = mock(UserColumnPermissionMapper.class);
AdminUserMapper userMapper = mock(AdminUserMapper.class);
PermissionMenuService service = new PermissionMenuService(menuMapper, permissionMapper, userMapper);
PermissionMenuEntity taskMenu = menu(94L, null, "admin", 1);
taskMenu.setColumnKey("admin_shop_data_crawl_tasks");
PermissionMenuEntity duplicateCheckMenu = menu(103L, null, "admin", 2);
duplicateCheckMenu.setColumnKey("admin_shop_data_duplicate_check");
PermissionMenuEntity dataPermission = menu(76L, null, "internal", 0);
dataPermission.setColumnKey("admin_shop_data_crawl_task_data");
when(userMapper.selectById(653L)).thenReturn(user(653L, "admin", 1));
when(menuMapper.selectOne(any())).thenReturn(taskMenu, duplicateCheckMenu, dataPermission);
when(menuMapper.selectById(94L)).thenReturn(taskMenu);
when(menuMapper.selectById(103L)).thenReturn(duplicateCheckMenu);
when(menuMapper.selectById(76L)).thenReturn(dataPermission);
when(menuMapper.selectList(any()))
.thenReturn(List.of(taskMenu, duplicateCheckMenu, dataPermission));
when(permissionMapper.selectByUserId(653L))
.thenReturn(List.of(grant(653L, 103L), grant(653L, 76L)));
AdminUserEntity operator = user(653L, "admin", 1);
service.requireShopDataCrawlTaskAccess(operator);
}
@Test
void shopDataTaskAccessAllowsTaskMenuWithoutDuplicateCheckMenu() {
// 回归:只勾「店铺数据记录」菜单 + 数据权限,放行。
PermissionMenuMapper menuMapper = mock(PermissionMenuMapper.class);
UserColumnPermissionMapper permissionMapper = mock(UserColumnPermissionMapper.class);
AdminUserMapper userMapper = mock(AdminUserMapper.class);
PermissionMenuService service = new PermissionMenuService(menuMapper, permissionMapper, userMapper);
PermissionMenuEntity taskMenu = menu(94L, null, "admin", 1);
taskMenu.setColumnKey("admin_shop_data_crawl_tasks");
PermissionMenuEntity duplicateCheckMenu = menu(103L, null, "admin", 2);
duplicateCheckMenu.setColumnKey("admin_shop_data_duplicate_check");
PermissionMenuEntity dataPermission = menu(76L, null, "internal", 0);
dataPermission.setColumnKey("admin_shop_data_crawl_task_data");
when(userMapper.selectById(653L)).thenReturn(user(653L, "admin", 1));
when(menuMapper.selectOne(any())).thenReturn(taskMenu, duplicateCheckMenu, dataPermission);
when(menuMapper.selectById(94L)).thenReturn(taskMenu);
when(menuMapper.selectById(103L)).thenReturn(duplicateCheckMenu);
when(menuMapper.selectById(76L)).thenReturn(dataPermission);
when(menuMapper.selectList(any()))
.thenReturn(List.of(taskMenu, duplicateCheckMenu, dataPermission));
when(permissionMapper.selectByUserId(653L))
.thenReturn(List.of(grant(653L, 94L), grant(653L, 76L)));
AdminUserEntity operator = user(653L, "admin", 1);
service.requireShopDataCrawlTaskAccess(operator);
}
@Test
void shopDataTaskAccessRejectsWithoutDataPermission() {
// 有店铺数据菜单但缺内部数据权限 → 仍需 403(数据权限走超管「数据范围授权」)。
PermissionMenuMapper menuMapper = mock(PermissionMenuMapper.class);
UserColumnPermissionMapper permissionMapper = mock(UserColumnPermissionMapper.class);
AdminUserMapper userMapper = mock(AdminUserMapper.class);
PermissionMenuService service = new PermissionMenuService(menuMapper, permissionMapper, userMapper);
PermissionMenuEntity taskMenu = menu(94L, null, "admin", 1);
taskMenu.setColumnKey("admin_shop_data_crawl_tasks");
PermissionMenuEntity duplicateCheckMenu = menu(103L, null, "admin", 2);
duplicateCheckMenu.setColumnKey("admin_shop_data_duplicate_check");
PermissionMenuEntity dataPermission = menu(76L, null, "internal", 0);
dataPermission.setColumnKey("admin_shop_data_crawl_task_data");
when(userMapper.selectById(653L)).thenReturn(user(653L, "admin", 1));
when(menuMapper.selectOne(any())).thenReturn(taskMenu, duplicateCheckMenu, dataPermission);
when(menuMapper.selectById(94L)).thenReturn(taskMenu);
when(menuMapper.selectById(103L)).thenReturn(duplicateCheckMenu);
when(menuMapper.selectById(76L)).thenReturn(dataPermission);
when(menuMapper.selectList(any()))
.thenReturn(List.of(taskMenu, duplicateCheckMenu, dataPermission));
when(permissionMapper.selectByUserId(653L)).thenReturn(List.of(grant(653L, 103L)));
AdminUserEntity operator = user(653L, "admin", 1);
assertThatThrownBy(() -> service.requireShopDataCrawlTaskAccess(operator))
.isInstanceOf(BusinessException.class)
.hasMessage("无权查看店铺数据任务");
}
@Test
void shopDataTaskAccessRejectsWithoutAnyShopDataMenu() {
// 两个店铺数据菜单都没有 → 403。
PermissionMenuMapper menuMapper = mock(PermissionMenuMapper.class);
UserColumnPermissionMapper permissionMapper = mock(UserColumnPermissionMapper.class);
AdminUserMapper userMapper = mock(AdminUserMapper.class);
PermissionMenuService service = new PermissionMenuService(menuMapper, permissionMapper, userMapper);
PermissionMenuEntity taskMenu = menu(94L, null, "admin", 1);
taskMenu.setColumnKey("admin_shop_data_crawl_tasks");
PermissionMenuEntity duplicateCheckMenu = menu(103L, null, "admin", 2);
duplicateCheckMenu.setColumnKey("admin_shop_data_duplicate_check");
PermissionMenuEntity dataPermission = menu(76L, null, "internal", 0);
dataPermission.setColumnKey("admin_shop_data_crawl_task_data");
when(userMapper.selectById(653L)).thenReturn(user(653L, "admin", 1));
when(menuMapper.selectOne(any())).thenReturn(taskMenu, duplicateCheckMenu, dataPermission);
when(menuMapper.selectById(94L)).thenReturn(taskMenu);
when(menuMapper.selectById(103L)).thenReturn(duplicateCheckMenu);
when(menuMapper.selectById(76L)).thenReturn(dataPermission);
when(menuMapper.selectList(any()))
.thenReturn(List.of(taskMenu, duplicateCheckMenu, dataPermission));
when(permissionMapper.selectByUserId(653L)).thenReturn(List.of(grant(653L, 5L)));
AdminUserEntity operator = user(653L, "admin", 1);
assertThatThrownBy(() -> service.requireShopDataCrawlTaskAccess(operator))
.isInstanceOf(BusinessException.class)
.hasMessage("无权查看店铺数据任务");
}
@Test
void shopDataTaskAccessAllowsSuperAdminWithoutAnyGrant() {
// 超管直通,不依赖任何菜单/数据授权记录。
PermissionMenuMapper menuMapper = mock(PermissionMenuMapper.class);
UserColumnPermissionMapper permissionMapper = mock(UserColumnPermissionMapper.class);
AdminUserMapper userMapper = mock(AdminUserMapper.class);
PermissionMenuService service = new PermissionMenuService(menuMapper, permissionMapper, userMapper);
service.requireShopDataCrawlTaskAccess(user(1L, "super_admin", 1));
}
@Test
void adminScopedReplacementKeepsExistingShopDataPermissionWithoutDuplicate() {
// 用户已通过超管「数据范围授权」配过数据权限时不重复插入。