fix: 菜单排序 app 分组优先于 admin,sort_order 按 menuType 作用域解析
Build Backend JAR / build (push) Has been cancelled

- menuComparator 增加 menuType 排序维度(app → admin → 其他)
- resolveSortOrder 查询限定同 menuType,避免跨类型尾部误判
- 抽出 loadRawMenus 保持原始 SQL 与比较器语义一致
This commit is contained in:
2026-08-31 01:21:45 +08:00
parent 483374744a
commit 0408c9752e
@@ -85,7 +85,7 @@ public class PermissionMenuService {
entity.setParentId(parentId); entity.setParentId(parentId);
entity.setMenuType(menuType); entity.setMenuType(menuType);
entity.setRoutePath(routePath); entity.setRoutePath(routePath);
entity.setSortOrder(resolveSortOrder(request.getSortOrder(), null)); entity.setSortOrder(resolveSortOrder(menuType, request.getSortOrder(), null));
permissionMenuMapper.insert(entity); permissionMenuMapper.insert(entity);
return toItemVo(getMenuById(entity.getId())); return toItemVo(getMenuById(entity.getId()));
} }
@@ -118,7 +118,7 @@ public class PermissionMenuService {
entity.setParentId(parentId); entity.setParentId(parentId);
entity.setMenuType(menuType); entity.setMenuType(menuType);
entity.setRoutePath(routePath); entity.setRoutePath(routePath);
entity.setSortOrder(resolveSortOrder(request.getSortOrder(), id)); entity.setSortOrder(resolveSortOrder(menuType, request.getSortOrder(), id));
permissionMenuMapper.updateById(entity); permissionMenuMapper.updateById(entity);
return toItemVo(getMenuById(id)); return toItemVo(getMenuById(id));
} }
@@ -410,12 +410,7 @@ public class PermissionMenuService {
} }
private List<PermissionMenuEntity> loadMenus(String menuType) { private List<PermissionMenuEntity> loadMenus(String menuType) {
String normalizedType = normalizeMenuType(menuType); List<PermissionMenuEntity> rows = loadRawMenus(menuType);
LambdaQueryWrapper<PermissionMenuEntity> query = new LambdaQueryWrapper<PermissionMenuEntity>()
.eq(normalizedType != null, PermissionMenuEntity::getMenuType, normalizedType)
.orderByAsc(PermissionMenuEntity::getSortOrder)
.orderByAsc(PermissionMenuEntity::getId);
List<PermissionMenuEntity> rows = permissionMenuMapper.selectList(query);
if (rows == null || rows.isEmpty()) { if (rows == null || rows.isEmpty()) {
return List.of(); return List.of();
} }
@@ -425,6 +420,20 @@ public class PermissionMenuService {
.toList(); .toList();
} }
/**
* Fetches every menu (optionally scoped to one type) in insertion order.
* Sorting happens in {@link #loadMenus(String)} and only for hierarchies
* that actually need one, keeping raw SQL and comparator semantics in sync.
*/
private List<PermissionMenuEntity> loadRawMenus(String menuType) {
String normalizedType = normalizeMenuType(menuType);
LambdaQueryWrapper<PermissionMenuEntity> query = new LambdaQueryWrapper<PermissionMenuEntity>()
.eq(normalizedType != null, PermissionMenuEntity::getMenuType, normalizedType)
.orderByAsc(PermissionMenuEntity::getSortOrder)
.orderByAsc(PermissionMenuEntity::getId);
return permissionMenuMapper.selectList(query);
}
private List<Long> loadDirectColumnIds(Long userId) { private List<Long> loadDirectColumnIds(Long userId) {
List<UserColumnPermissionEntity> rows = userColumnPermissionMapper.selectByUserId(userId); List<UserColumnPermissionEntity> rows = userColumnPermissionMapper.selectByUserId(userId);
if (rows == null || rows.isEmpty()) { if (rows == null || rows.isEmpty()) {
@@ -751,12 +760,30 @@ public class PermissionMenuService {
return new ArrayList<>(uniqueIds); return new ArrayList<>(uniqueIds);
} }
/** app 分组排在 admin 分组之前;组内按 sort_order, id 排。 */
private Comparator<PermissionMenuEntity> menuComparator() { private Comparator<PermissionMenuEntity> menuComparator() {
return Comparator return Comparator
.comparing(PermissionMenuEntity::getSortOrder, Comparator.nullsLast(Integer::compareTo)) .comparing(PermissionMenuEntity::getMenuType, Comparator.nullsLast(menuTypeRank()))
.thenComparing(PermissionMenuEntity::getSortOrder, Comparator.nullsLast(Integer::compareTo))
.thenComparing(PermissionMenuEntity::getId, Comparator.nullsLast(Long::compareTo)); .thenComparing(PermissionMenuEntity::getId, Comparator.nullsLast(Long::compareTo));
} }
private Comparator<String> menuTypeRank() {
return Comparator.comparing(this::menuTypePriority);
}
/** App 菜单优先展示在 admin 菜单之前。 */
private int menuTypePriority(String menuType) {
if (MENU_TYPE_APP.equals(menuType)) {
return 0;
}
if (MENU_TYPE_ADMIN.equals(menuType)) {
return 1;
}
// Internal/legacy rows stay out of the way of both user-facing groups.
return 2;
}
private List<PermissionMenuItemVo> toItemVos(List<PermissionMenuEntity> visibleMenus, private List<PermissionMenuItemVo> toItemVos(List<PermissionMenuEntity> visibleMenus,
List<PermissionMenuEntity> hierarchyMenus) { List<PermissionMenuEntity> hierarchyMenus) {
Map<Long, PermissionMenuEntity> menuMap = hierarchyMenus.stream() Map<Long, PermissionMenuEntity> menuMap = hierarchyMenus.stream()
@@ -800,11 +827,18 @@ public class PermissionMenuService {
return root.getColumnKey(); return root.getColumnKey();
} }
private Integer resolveSortOrder(Integer sortOrder, Long excludeId) { /**
* Resolves the default sort order when none is supplied, scoped to the
* menu's own type (parent takes precedence, then hierarchy ancestors).
* The DB query mirrors {@link #loadMenus(String)} ordering so the last row
* is the true tail of the type's ordering.
*/
private Integer resolveSortOrder(String menuType, Integer sortOrder, Long excludeId) {
if (sortOrder != null) { if (sortOrder != null) {
return sortOrder; return sortOrder;
} }
PermissionMenuEntity tail = permissionMenuMapper.selectOne(new LambdaQueryWrapper<PermissionMenuEntity>() PermissionMenuEntity tail = permissionMenuMapper.selectOne(new LambdaQueryWrapper<PermissionMenuEntity>()
.eq(PermissionMenuEntity::getMenuType, normalizeMenuType(menuType))
.ne(excludeId != null, PermissionMenuEntity::getId, excludeId) .ne(excludeId != null, PermissionMenuEntity::getId, excludeId)
.orderByDesc(PermissionMenuEntity::getSortOrder) .orderByDesc(PermissionMenuEntity::getSortOrder)
.orderByDesc(PermissionMenuEntity::getId) .orderByDesc(PermissionMenuEntity::getId)