사이트맵 신규 개발
eapim-admin CI / build (push) Has been cancelled

This commit is contained in:
eastargh
2026-07-07 13:19:33 +09:00
parent edf024f0a3
commit b16d03517a
7 changed files with 317 additions and 2 deletions
@@ -0,0 +1,23 @@
package com.eactive.eai.rms.common.acl.sitemap;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.eactive.eai.rms.common.base.BaseAnnotationController;
@Controller
@RequiredArgsConstructor
public class SitemapController extends BaseAnnotationController {
private final SitemapService sitemapService;
@GetMapping(value = "/common/acl/sitemap/sitemapMan.view")
public String view(@RequestParam(value = "serviceType", required = false) String serviceType, Model model) {
model.addAttribute("sitemapTree", sitemapService.getSitemapTree(serviceType));
return "/common/acl/sitemap/sitemapMan";
}
}
@@ -0,0 +1,11 @@
package com.eactive.eai.rms.common.acl.sitemap;
import java.util.List;
import com.eactive.eai.rms.common.acl.sitemap.ui.SitemapNode;
public interface SitemapService {
public List<SitemapNode> getSitemapTree(String serviceType);
}
@@ -0,0 +1,96 @@
package com.eactive.eai.rms.common.acl.sitemap;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.eactive.eai.rms.common.acl.sitemap.ui.SitemapNode;
import com.eactive.eai.rms.common.base.BaseService;
import com.eactive.eai.rms.data.entity.man.menu.Menu;
import com.eactive.eai.rms.data.entity.man.menu.MenuService;
import com.eactive.eai.rms.data.entity.man.role.RoleMenuAuthService;
@Service("sitemapService")
@Transactional(transactionManager = "transactionManagerForEMS")
public class SitemapServiceImpl extends BaseService implements SitemapService {
@Autowired
private MenuService menuService;
@Autowired
private RoleMenuAuthService roleMenuAuthService;
public List<SitemapNode> getSitemapTree(String serviceType) {
Menu root = menuService.getById("ROOT_DEV");
Menu langRoot = findLangRoot(root);
List<SitemapNode> tree = new ArrayList<>();
if (langRoot == null) {
return tree;
}
Set<String> serviceTypeMenuIds = StringUtils.isBlank(serviceType)
? null
: new HashSet<>(roleMenuAuthService.findMenuIdsByServiceType(serviceType));
for (Menu menu : sortedChildren(langRoot)) {
if (isVisible(menu) && matchesServiceType(menu, serviceTypeMenuIds)) {
tree.add(buildNode(menu, serviceTypeMenuIds));
}
}
return tree;
}
// 엔티티의 childMenus는 menuId 기준으로 정렬되어 있어, 실제 메뉴(운영화면 GNB)와 동일하게 sortOrder 기준으로 재정렬한다.
private List<Menu> sortedChildren(Menu menu) {
List<Menu> children = new ArrayList<>(menu.getChildMenus());
children.sort(Comparator.comparing(Menu::getSortOrder, Comparator.nullsLast(Comparator.naturalOrder())));
return children;
}
// MenuService.getMenuList()의 GNB 렌더링과 동일하게 "ROOT"(한글)/"ROOT_EN"(영문) 이름의 언어별 루트를 찾는다.
private Menu findLangRoot(Menu root) {
String lang = LocaleContextHolder.getLocale().getLanguage();
String langRootName = (StringUtils.isBlank(lang) || "ko".equalsIgnoreCase(lang))
? "ROOT"
: "ROOT_" + lang.toUpperCase();
return root.getChildMenus().stream()
.filter(m -> langRootName.equalsIgnoreCase(m.getMenuName()))
.findFirst()
.orElse(null);
}
private SitemapNode buildNode(Menu menu, Set<String> serviceTypeMenuIds) {
SitemapNode node = new SitemapNode(menu);
for (Menu child : sortedChildren(menu)) {
if (isVisible(child) && matchesServiceType(child, serviceTypeMenuIds)) {
node.getChildren().add(buildNode(child, serviceTypeMenuIds));
}
}
return node;
}
private boolean isVisible(Menu menu) {
return "Y".equalsIgnoreCase(menu.getDisplayYn()) && "Y".equalsIgnoreCase(menu.getUseYn());
}
// serviceType 필터가 없으면 통과. 있으면 자신이나 하위메뉴 중 하나라도 해당 serviceType 권한이 있어야 통과(상위메뉴 누락 방지).
private boolean matchesServiceType(Menu menu, Set<String> serviceTypeMenuIds) {
if (serviceTypeMenuIds == null) {
return true;
}
if (serviceTypeMenuIds.contains(menu.getMenuId())) {
return true;
}
return menu.getChildMenus().stream().anyMatch(child -> matchesServiceType(child, serviceTypeMenuIds));
}
}
@@ -0,0 +1,29 @@
package com.eactive.eai.rms.common.acl.sitemap.ui;
import java.util.ArrayList;
import java.util.List;
import com.eactive.eai.rms.data.entity.man.menu.Menu;
import lombok.Data;
@Data
public class SitemapNode {
private String menuId;
private String menuName;
private String menuUrl;
private List<SitemapNode> children = new ArrayList<>();
public SitemapNode(Menu menu) {
this.menuId = menu.getMenuId();
this.menuName = menu.getMenuName();
this.menuUrl = menu.getMenuUrl();
}
public SitemapNode() {
}
}
@@ -51,4 +51,17 @@ public class RoleMenuAuthService extends AbstractEMSDataSerivce<RoleMenuAuth, Ro
.fetch();
}
public List<String> findMenuIdsByServiceType(String serviceType) {
QRoleMenuAuth qRoleMenuAuth = QRoleMenuAuth.roleMenuAuth;
return getJPAQueryFactory()
.select(qRoleMenuAuth.id.menuId)
.distinct()
.from(qRoleMenuAuth)
.where(qRoleMenuAuth.id.serviceType.eq(serviceType))
.setHint(QueryHints.CACHEABLE, true)
.fetch();
}
}