权限回收级联清理:管理员被回收权限后自动清理其直建员工的越权授权

- 保存管理员权限时,若其权限被回收,级联删除其直接创建的员工中超出
  该管理员当前有效范围的授权(创建时快照遗留的越权项)
- 只清理越权项,保留员工合法持有的其他授权;超级管理员不受此限制
This commit is contained in:
2026-09-04 00:16:18 +08:00
parent d0db577a9c
commit 92a7a2c478
2 changed files with 76 additions and 0 deletions
@@ -15,6 +15,7 @@ import com.nanri.aiimage.modules.permission.model.vo.ImageVideoDataPermissionUse
import com.nanri.aiimage.modules.permission.model.vo.PermissionMenuItemVo;
import com.nanri.aiimage.modules.permission.model.vo.UserColumnIdsVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -41,6 +42,7 @@ import java.util.stream.Collectors;
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class PermissionMenuService {
public static final String MENU_TYPE_APP = "app";
@@ -401,6 +403,43 @@ public class PermissionMenuService {
entity.setColumnId(columnId);
userColumnPermissionMapper.insert(entity);
}
// 目标管理员的权限被回收后,其直接创建的员工若仍持有超出范围的
// 授权(创建时快照遗留,员工权限独立于管理员后续变更),必须级联
// 删除,否则回收了管理员的菜单权限,员工菜单权限却还在。
cascadeCleanupSubordinateOverreach(target, finalGrantIds);
}
/**
* 删除普通管理员直接创建的员工中,超出该管理员当前有效权限范围的授权。
* 只清理越权项,保留员工合法持有的其他授权。超级管理员无此限制。
*/
private void cascadeCleanupSubordinateOverreach(AdminUserEntity target, Set<Long> targetDirectIds) {
if (target == null || target.getId() == null || isSuperAdmin(target)) {
return;
}
Set<Long> targetEffectiveIds = expandDescendantIds(new LinkedHashSet<>(targetDirectIds), loadMenus(null));
List<AdminUserEntity> subordinates = adminUserMapper.selectList(new LambdaQueryWrapper<AdminUserEntity>()
.eq(AdminUserEntity::getCreatedById, target.getId()));
for (AdminUserEntity subordinate : subordinates) {
Long subordinateId = subordinate.getId();
if (subordinateId == null || subordinateId.equals(target.getId())) {
continue;
}
List<Long> subordinateGrantIds = loadDirectColumnIds(subordinateId);
if (subordinateGrantIds.isEmpty()) {
continue;
}
List<Long> overreachIds = subordinateGrantIds.stream()
.filter(id -> !targetEffectiveIds.contains(id))
.toList();
if (overreachIds.isEmpty()) {
continue;
}
userColumnPermissionMapper.deleteByUserIdAndColumnIds(subordinateId, overreachIds);
log.info("removed overreach permissions target={} subordinate={} droppedColumnIds={}",
target.getId(), subordinateId, overreachIds);
}
}
private List<PermissionMenuEntity> loadMenus(String menuType) {