diff --git a/src/main/java/com/kamco/cd/kamcoback/auth/MenuAuthorizationManager.java b/src/main/java/com/kamco/cd/kamcoback/auth/MenuAuthorizationManager.java
new file mode 100644
index 00000000..0cc3db39
--- /dev/null
+++ b/src/main/java/com/kamco/cd/kamcoback/auth/MenuAuthorizationManager.java
@@ -0,0 +1,101 @@
+package com.kamco.cd.kamcoback.auth;
+
+import com.kamco.cd.kamcoback.postgres.entity.MenuEntity;
+import com.kamco.cd.kamcoback.postgres.repository.menu.MenuRepository;
+import jakarta.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.function.Supplier;
+import lombok.RequiredArgsConstructor;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
+import org.springframework.stereotype.Component;
+
+/**
+ * DB 기반 메뉴 권한 AuthorizationManager
+ *
+ *
- Redis 사용 안 함 - ADMIN 예외 없음 (DB 매핑 기준) - 한 계정 = role 1개 - menu_url(prefix) 기반 API 접근 제어
+ */
+@Component
+@RequiredArgsConstructor
+public class MenuAuthorizationManager implements AuthorizationManager {
+
+ private static final Logger log = LogManager.getLogger(MenuAuthorizationManager.class);
+
+ private final MenuRepository menuAuthQueryRepository;
+
+ @Override
+ public AuthorizationDecision check(
+ Supplier authenticationSupplier, RequestAuthorizationContext context) {
+
+ HttpServletRequest request = context.getRequest();
+ String requestPath = normalizePath(request.getRequestURI());
+
+ Authentication authentication = authenticationSupplier.get();
+ if (authentication == null || !authentication.isAuthenticated()) {
+ return new AuthorizationDecision(false);
+ }
+
+ String role = extractSingleRole(authentication);
+ if (role == null) {
+ return new AuthorizationDecision(false);
+ }
+
+ // DB에서 role에 허용된 메뉴 조회
+ List allowedMenus = menuAuthQueryRepository.findAllowedMenuUrlsByRole(role);
+
+ if (allowedMenus == null || allowedMenus.isEmpty()) {
+ return new AuthorizationDecision(false);
+ }
+
+ // menu_url(prefix) 기반 접근 허용 판단
+ for (MenuEntity menu : allowedMenus) {
+ String baseUri = menu.getMenuUrl();
+ if (baseUri == null || baseUri.isBlank()) {
+ continue;
+ }
+ if (matchUri(baseUri, requestPath)) {
+ return new AuthorizationDecision(true);
+ }
+ }
+
+ return new AuthorizationDecision(false);
+ }
+
+ /* =========================
+ * role 추출
+ * ========================= */
+ private String extractSingleRole(Authentication authentication) {
+ GrantedAuthority ga = authentication.getAuthorities().stream().findFirst().orElse(null);
+
+ if (ga == null || ga.getAuthority() == null || ga.getAuthority().isBlank()) {
+ return null;
+ }
+
+ String auth = ga.getAuthority();
+ return auth.startsWith("ROLE_") ? auth.substring(5) : auth;
+ }
+
+ /* =========================
+ * URI prefix 매칭
+ * ========================= */
+ private boolean matchUri(String baseUri, String requestPath) {
+ String base = normalizePath(baseUri);
+ String req = normalizePath(requestPath);
+ return req.equals(base) || req.startsWith(base + "/");
+ }
+
+ /* =========================
+ * URI 정규화
+ * ========================= */
+ private String normalizePath(String path) {
+ if (path == null || path.isBlank()) {
+ return "/";
+ }
+ return path.replaceAll("//+", "/");
+ }
+}
diff --git a/src/main/java/com/kamco/cd/kamcoback/auth/RedisAuthorizationManager.java b/src/main/java/com/kamco/cd/kamcoback/auth/RedisAuthorizationManager.java
deleted file mode 100644
index 9fcb3b16..00000000
--- a/src/main/java/com/kamco/cd/kamcoback/auth/RedisAuthorizationManager.java
+++ /dev/null
@@ -1,188 +0,0 @@
-package com.kamco.cd.kamcoback.auth;
-
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.kamco.cd.kamcoback.common.enums.RoleType;
-import com.kamco.cd.kamcoback.menu.dto.MenuDto;
-import jakarta.servlet.http.HttpServletRequest;
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Supplier;
-import lombok.RequiredArgsConstructor;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.springframework.data.redis.core.StringRedisTemplate;
-import org.springframework.security.authorization.AuthorizationDecision;
-import org.springframework.security.authorization.AuthorizationManager;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
-import org.springframework.stereotype.Component;
-
-/** redis에 등록된 메뉴별 권한 확인 */
-@Component
-@RequiredArgsConstructor
-public class RedisAuthorizationManager
- implements AuthorizationManager {
-
- private static final Logger log = LogManager.getLogger(RedisAuthorizationManager.class);
- private static final String REDIS_KEY = "auth:api:role";
-
- private final StringRedisTemplate redisTemplate;
- private final ObjectMapper objectMapper;
-
- @Override
- public AuthorizationDecision check(
- Supplier authenticationSupplier, RequestAuthorizationContext context) {
-
- HttpServletRequest request = context.getRequest();
- String requestPath = normalizePath(request.getRequestURI());
-
- Authentication authentication = authenticationSupplier.get();
- if (authentication == null || !authentication.isAuthenticated()) {
- return new AuthorizationDecision(false);
- }
-
- // ADMIN 은 무조건 허용
- if (hasAdmin(authentication)) {
- return new AuthorizationDecision(true);
- }
-
- // 사용자 role 목록
- List userRoles = extractRoles(authentication);
- if (userRoles.isEmpty()) {
- return new AuthorizationDecision(false);
- }
-
- // Redis 조회 (1회)
- String json = redisTemplate.opsForValue().get(REDIS_KEY);
- if (json == null || json.isBlank()) {
- return new AuthorizationDecision(false);
- }
-
- // JSON 파싱 (1회)
- List menus = parseMenus(json);
- if (menus.isEmpty()) {
- return new AuthorizationDecision(false);
- }
-
- // role + prefix URI 매칭
- for (String role : userRoles) {
- if (isAllowed(menus, role, requestPath)) {
- return new AuthorizationDecision(true);
- }
- }
-
- return new AuthorizationDecision(false);
- }
-
- /* =========================
- * 핵심 권한 판별 로직
- * ========================= */
- private boolean isAllowed(List menus, String role, String requestPath) {
-
- for (MenuDto.MenuWithRolesDto menu : menus) {
- if (menu == null) {
- continue;
- }
-
- String baseUri = menu.getMenuApiUri();
- if (baseUri == null || baseUri.isBlank()) {
- continue;
- }
-
- // role 포함 여부
- if (!hasRole(menu.getRoles(), role)) {
- continue;
- }
-
- // prefix URI 허용
- if (matchUri(baseUri, requestPath)) {
- return true;
- }
- }
- return false;
- }
-
- /* =========================
- * URI prefix 매칭
- * ========================= */
- private boolean matchUri(String baseUri, String requestPath) {
- String base = normalizePath(baseUri);
- String req = normalizePath(requestPath);
-
- // /api/log/audit → /api/log/audit/**
- return req.equals(base) || req.startsWith(base + "/");
- }
-
- /* =========================
- * role 문자열 처리
- * ========================= */
- private boolean hasRole(String rolesCsv, String targetRole) {
- if (rolesCsv == null || rolesCsv.isBlank()) {
- return false;
- }
- if (targetRole == null || targetRole.isBlank()) {
- return false;
- }
-
- for (String r : rolesCsv.split(",")) {
- if (targetRole.equalsIgnoreCase(r.trim())) {
- return true;
- }
- }
- return false;
- }
-
- /* =========================
- * Redis JSON 파싱
- * ========================= */
- private List parseMenus(String json) {
- try {
- return objectMapper.readValue(json, new TypeReference>() {});
- } catch (Exception e) {
- log.warn("Failed to parse redis menu json. key={}", REDIS_KEY, e);
- return Collections.emptyList();
- }
- }
-
- /* =========================
- * ADMIN 판별
- * ========================= */
- private boolean hasAdmin(Authentication authentication) {
- for (GrantedAuthority ga : authentication.getAuthorities()) {
- if (ga == null || ga.getAuthority() == null) {
- continue;
- }
-
- String auth = ga.getAuthority();
- String admin = RoleType.ADMIN.getId();
-
- if (auth.equals(admin) || auth.equals("ROLE_" + admin)) {
- return true;
- }
- }
- return false;
- }
-
- /* =========================
- * ROLE 목록 추출
- * ========================= */
- private List extractRoles(Authentication authentication) {
- return authentication.getAuthorities().stream()
- .map(GrantedAuthority::getAuthority)
- .filter(a -> a != null && !a.isBlank())
- .map(a -> a.startsWith("ROLE_") ? a.substring(5) : a)
- .toList();
- }
-
- /* =========================
- * URI 정규화
- * ========================= */
- private String normalizePath(String path) {
- if (path == null || path.isBlank()) {
- return "/";
- }
- return path.replaceAll("//+", "/");
- }
-}
diff --git a/src/main/java/com/kamco/cd/kamcoback/config/SecurityConfig.java b/src/main/java/com/kamco/cd/kamcoback/config/SecurityConfig.java
index a2be68ad..fe1ab0cf 100644
--- a/src/main/java/com/kamco/cd/kamcoback/config/SecurityConfig.java
+++ b/src/main/java/com/kamco/cd/kamcoback/config/SecurityConfig.java
@@ -2,7 +2,6 @@ package com.kamco.cd.kamcoback.config;
import com.kamco.cd.kamcoback.auth.CustomAuthenticationProvider;
import com.kamco.cd.kamcoback.auth.JwtAuthenticationFilter;
-import com.kamco.cd.kamcoback.auth.RedisAuthorizationManager;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
@@ -29,7 +28,6 @@ public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final CustomAuthenticationProvider customAuthenticationProvider;
- private final RedisAuthorizationManager redisAuthorizationManager;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
diff --git a/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngApiController.java b/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngApiController.java
index f71eb3fb..32c8520f 100644
--- a/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngApiController.java
+++ b/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngApiController.java
@@ -177,7 +177,8 @@ public class MapSheetMngApiController {
@RequestPart("tif") MultipartFile tifFile,
@RequestParam(value = "hstUid", required = false) Long hstUid,
@RequestParam(value = "overwrite", required = false) boolean overwrite) {
- return ApiResponseDto.createOK(mapSheetMngService.uploadPair(tfwFile, tifFile, hstUid, overwrite));
+ return ApiResponseDto.createOK(
+ mapSheetMngService.uploadPair(tfwFile, tifFile, hstUid, overwrite));
}
@Operation(summary = "영상관리 > 파일조회", description = "영상관리 > 파일조회")
diff --git a/src/main/java/com/kamco/cd/kamcoback/menu/MenuApiController.java b/src/main/java/com/kamco/cd/kamcoback/menu/MenuApiController.java
index 3d8f2710..72435eda 100644
--- a/src/main/java/com/kamco/cd/kamcoback/menu/MenuApiController.java
+++ b/src/main/java/com/kamco/cd/kamcoback/menu/MenuApiController.java
@@ -1,7 +1,6 @@
package com.kamco.cd.kamcoback.menu;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.kamco.cd.kamcoback.common.utils.UserUtil;
import com.kamco.cd.kamcoback.config.api.ApiLogFunction;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.menu.dto.MenuDto;
@@ -11,17 +10,14 @@ import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
-import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.LinkedHashMap;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
-@Tag(name = "메뉴 관리", description = "메뉴 관리 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/menu")
@@ -68,43 +64,4 @@ public class MenuApiController {
return ApiResponseDto.ok(ApiLogFunction.getUriMenuInfo(result, apiUri));
}
-
- @Operation(summary = "권한별 메뉴 레디스 저장", description = "권한별 메뉴 레디스 저장")
- @ApiResponses(
- value = {
- @ApiResponse(
- responseCode = "201",
- description = "등록 성공",
- content =
- @Content(
- mediaType = "application/json",
- schema = @Schema(implementation = MenuDto.Basic.class))),
- @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
- @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
- })
- @PostMapping("/auth")
- public ApiResponseDto getFindByRoleRedis() {
- menuService.getFindByRoleRedis();
- return ApiResponseDto.createOK("ok");
- }
-
- @Operation(summary = "권한별 메뉴 조회", description = "로그인 권한별 메뉴 목록")
- @ApiResponses(
- value = {
- @ApiResponse(
- responseCode = "200",
- description = "조회 성공",
- content =
- @Content(
- mediaType = "application/json",
- schema = @Schema(implementation = MenuDto.Basic.class))),
- @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
- @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
- })
- @GetMapping("/auth")
- public ApiResponseDto> getFindAllByRole() {
- UserUtil userUtil = new UserUtil();
- String role = userUtil.getRole();
- return ApiResponseDto.ok(menuService.getFindByRole(role));
- }
}
diff --git a/src/main/java/com/kamco/cd/kamcoback/menu/MyMenuApiController.java b/src/main/java/com/kamco/cd/kamcoback/menu/MyMenuApiController.java
new file mode 100644
index 00000000..ade26a80
--- /dev/null
+++ b/src/main/java/com/kamco/cd/kamcoback/menu/MyMenuApiController.java
@@ -0,0 +1,47 @@
+package com.kamco.cd.kamcoback.menu;
+
+import com.kamco.cd.kamcoback.common.utils.UserUtil;
+import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
+import com.kamco.cd.kamcoback.menu.dto.MenuDto;
+import com.kamco.cd.kamcoback.menu.dto.MyMenuDto;
+import com.kamco.cd.kamcoback.menu.service.MyMenuService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@Tag(name = "메뉴 조회", description = "메뉴 조회 API")
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/my/menus")
+public class MyMenuApiController {
+
+ private final MyMenuService myMenuService;
+
+ @Operation(summary = "사용자별 메뉴 조회", description = "로그인 사용자별 권한 메뉴 목록")
+ @ApiResponses(
+ value = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "조회 성공",
+ content =
+ @Content(
+ mediaType = "application/json",
+ schema = @Schema(implementation = MenuDto.Basic.class))),
+ @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
+ @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
+ })
+ @GetMapping
+ public ApiResponseDto> getFindAllByRole() {
+ UserUtil userUtil = new UserUtil();
+ String role = userUtil.getRole();
+ return ApiResponseDto.ok(myMenuService.getFindByRole(role));
+ }
+}
diff --git a/src/main/java/com/kamco/cd/kamcoback/menu/dto/MenuDto.java b/src/main/java/com/kamco/cd/kamcoback/menu/dto/MenuDto.java
index e331b3df..38a49960 100644
--- a/src/main/java/com/kamco/cd/kamcoback/menu/dto/MenuDto.java
+++ b/src/main/java/com/kamco/cd/kamcoback/menu/dto/MenuDto.java
@@ -45,8 +45,7 @@ public class MenuDto {
Long updatedUid,
List children,
ZonedDateTime createdDttm,
- ZonedDateTime updatedDttm,
- String menuApiUrl) {
+ ZonedDateTime updatedDttm) {
this.menuUid = menuUid;
this.menuNm = menuNm;
this.menuUrl = menuUrl;
@@ -59,7 +58,6 @@ public class MenuDto {
this.children = children;
this.createdDttm = createdDttm;
this.updatedDttm = updatedDttm;
- this.menuApiUrl = menuApiUrl;
}
}
@@ -70,7 +68,6 @@ public class MenuDto {
private String menuUid;
private String menuNm;
private String menuUrl;
- private String menuApiUri;
private String roles; // "ROLE_A,ROLE_B"
}
}
diff --git a/src/main/java/com/kamco/cd/kamcoback/menu/dto/MyMenuDto.java b/src/main/java/com/kamco/cd/kamcoback/menu/dto/MyMenuDto.java
new file mode 100644
index 00000000..16ccda3b
--- /dev/null
+++ b/src/main/java/com/kamco/cd/kamcoback/menu/dto/MyMenuDto.java
@@ -0,0 +1,29 @@
+package com.kamco.cd.kamcoback.menu.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+public class MyMenuDto {
+
+ @Schema(name = "My Menu Basic", description = "사용자별 권한 메뉴 목록")
+ @Getter
+ @NoArgsConstructor
+ public static class Basic {
+
+ private String id;
+ private String name;
+ private String menuUrl;
+ private Long order;
+ private List children = new ArrayList<>();
+
+ public Basic(String menuUid, String menuNm, String menuUrl, Long menuOrder) {
+ this.id = menuUid;
+ this.name = menuNm;
+ this.menuUrl = menuUrl;
+ this.order = menuOrder;
+ }
+ }
+}
diff --git a/src/main/java/com/kamco/cd/kamcoback/menu/service/MenuService.java b/src/main/java/com/kamco/cd/kamcoback/menu/service/MenuService.java
index bee0a81f..bf2db12f 100644
--- a/src/main/java/com/kamco/cd/kamcoback/menu/service/MenuService.java
+++ b/src/main/java/com/kamco/cd/kamcoback/menu/service/MenuService.java
@@ -1,13 +1,9 @@
package com.kamco.cd.kamcoback.menu.service;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.kamco.cd.kamcoback.common.enums.RoleType;
import com.kamco.cd.kamcoback.common.utils.UserUtil;
import com.kamco.cd.kamcoback.menu.dto.MenuDto;
import com.kamco.cd.kamcoback.postgres.core.MenuCoreService;
-import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
@@ -27,60 +23,4 @@ public class MenuService {
public List getFindAll() {
return menuCoreService.getFindAll();
}
-
- /**
- * 권한별 메뉴 목록 redis 등록
- *
- * @return
- */
- public void getFindByRoleRedis() {
-
- for (RoleType role : RoleType.values()) {
- List menus = menuCoreService.getFindByRole(role.name());
-
- try {
- String key = "menu:role:" + role.name();
- String value = objectMapper.writeValueAsString(menus);
- redisTemplate.opsForValue().set(key, value);
- } catch (JsonProcessingException e) {
- throw new RuntimeException(e);
- }
- }
-
- List menusWithRoles = menuCoreService.getMenuWithRoles();
-
- try {
- String key = "auth:api:role";
- String value = objectMapper.writeValueAsString(menusWithRoles);
- redisTemplate.opsForValue().set(key, value);
- } catch (JsonProcessingException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * 권한별 메뉴 목록 조회
- *
- * @param role
- * @return
- */
- public List getFindByRole(String role) {
- String key = "menu:role:" + role;
- String json = redisTemplate.opsForValue().get(key);
- if (json == null) {
- return new ArrayList<>();
- }
-
- JavaType type =
- objectMapper.getTypeFactory().constructCollectionType(List.class, MenuDto.Basic.class);
-
- List cached;
-
- try {
- cached = objectMapper.readValue(json, type);
- } catch (JsonProcessingException e) {
- throw new RuntimeException(e);
- }
- return cached;
- }
}
diff --git a/src/main/java/com/kamco/cd/kamcoback/menu/service/MyMenuService.java b/src/main/java/com/kamco/cd/kamcoback/menu/service/MyMenuService.java
new file mode 100644
index 00000000..9adad491
--- /dev/null
+++ b/src/main/java/com/kamco/cd/kamcoback/menu/service/MyMenuService.java
@@ -0,0 +1,26 @@
+package com.kamco.cd.kamcoback.menu.service;
+
+import com.kamco.cd.kamcoback.menu.dto.MyMenuDto;
+import com.kamco.cd.kamcoback.postgres.core.MenuCoreService;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class MyMenuService {
+
+ private final MenuCoreService menuCoreService;
+
+ /**
+ * 권한별 메뉴 목록 조회
+ *
+ * @param role
+ * @return
+ */
+ public List getFindByRole(String role) {
+ return menuCoreService.getFindByRole(role);
+ }
+}
diff --git a/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java b/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java
index 7989ce31..802a24e1 100644
--- a/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java
+++ b/src/main/java/com/kamco/cd/kamcoback/model/ModelMngApiController.java
@@ -13,7 +13,12 @@ import jakarta.transaction.Transactional;
import java.time.LocalDate;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
@Tag(name = "모델 관리", description = "모델 관리 API")
@RequiredArgsConstructor
diff --git a/src/main/java/com/kamco/cd/kamcoback/model/dto/ModelMngDto.java b/src/main/java/com/kamco/cd/kamcoback/model/dto/ModelMngDto.java
index 588e0018..ea9da363 100644
--- a/src/main/java/com/kamco/cd/kamcoback/model/dto/ModelMngDto.java
+++ b/src/main/java/com/kamco/cd/kamcoback/model/dto/ModelMngDto.java
@@ -16,15 +16,34 @@ import org.springframework.data.domain.Sort;
public class ModelMngDto {
+ @CodeExpose
+ @Getter
+ @AllArgsConstructor
+ public enum ModelType implements EnumType {
+ M1("모델 M1"),
+ M2("모델 M2"),
+ M3("모델 M3");
+
+ private final String desc;
+
+ @Override
+ public String getId() {
+ return name();
+ }
+
+ @Override
+ public String getText() {
+ return desc;
+ }
+ }
+
@Schema(name = "ModelMgmtDto Basic", description = "모델관리 엔티티 기본 정보")
@Getter
@NoArgsConstructor
public static class Basic {
+ private Long modelUid;
private String modelVer;
- private String hyperVer;
- private String epochVer;
- private String dockerFileNm;
@JsonFormatDttm private ZonedDateTime createCompleteDttm;
@JsonFormatDttm private ZonedDateTime recentUseDttm;
@@ -35,22 +54,27 @@ public class ModelMngDto {
@JsonFormatDttm private ZonedDateTime updatedDttm;
private Long updatedUid;
+ private String modelType;
+ private String filePath;
+ private String fileName;
+ private String memo;
+
public Basic(
+ Long modelUid,
String modelVer,
- String hyperVer,
- String epochVer,
- String dockerFileNm,
ZonedDateTime createCompleteDttm,
ZonedDateTime recentUseDttm,
Boolean deleted,
ZonedDateTime createdDttm,
Long createdUid,
ZonedDateTime updatedDttm,
- Long updatedUid) {
+ Long updatedUid,
+ String modelType,
+ String filePath,
+ String fileName,
+ String memo) {
+ this.modelUid = modelUid;
this.modelVer = modelVer;
- this.hyperVer = hyperVer;
- this.epochVer = epochVer;
- this.dockerFileNm = dockerFileNm;
this.createCompleteDttm = createCompleteDttm;
this.recentUseDttm = recentUseDttm;
this.deleted = deleted;
@@ -58,6 +82,10 @@ public class ModelMngDto {
this.createdUid = createdUid;
this.updatedDttm = updatedDttm;
this.updatedUid = updatedUid;
+ this.modelType = modelType;
+ this.filePath = filePath;
+ this.fileName = fileName;
+ this.memo = memo;
}
}
@@ -67,17 +95,18 @@ public class ModelMngDto {
@NoArgsConstructor
@AllArgsConstructor
public static class ModelList {
+
private Integer rowNum;
private String modelVer;
private String fileName;
private String modelType;
private String createCompleteDttm;
- private String recentUseDttm;
private BigDecimal f1Score;
private BigDecimal precision;
private BigDecimal recall;
private BigDecimal accuracy;
private BigDecimal iou;
+ private String memo;
private Boolean deleted;
}
@@ -118,25 +147,4 @@ public class ModelMngDto {
return PageRequest.of(page, size);
}
}
-
- @CodeExpose
- @Getter
- @AllArgsConstructor
- public enum ModelType implements EnumType {
- M1("모델 M1"),
- M2("모델 M2"),
- M3("모델 M3");
-
- private final String desc;
-
- @Override
- public String getId() {
- return name();
- }
-
- @Override
- public String getText() {
- return desc;
- }
- }
}
diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/MenuCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/MenuCoreService.java
index d01054e0..3e9041fe 100644
--- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/MenuCoreService.java
+++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/MenuCoreService.java
@@ -1,8 +1,10 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.menu.dto.MenuDto;
+import com.kamco.cd.kamcoback.menu.dto.MyMenuDto;
import com.kamco.cd.kamcoback.postgres.entity.MenuEntity;
import com.kamco.cd.kamcoback.postgres.repository.menu.MenuRepository;
+import java.util.Comparator;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -23,16 +25,50 @@ public class MenuCoreService {
* @param role
* @return
*/
- public List getFindByRole(String role) {
- return menuRepository.getFindByRole(role).stream().map(MenuEntity::toDto).toList();
+ public List getFindByRole(String role) {
+ List entities = menuRepository.getFindByRole(role);
+
+ return entities.stream()
+ .map(
+ parent -> {
+ MyMenuDto.Basic p =
+ new MyMenuDto.Basic(
+ parent.getMenuUid(),
+ parent.getMenuNm(),
+ parent.getMenuUrl(),
+ parent.getMenuOrder());
+
+ parent.getChildren().stream()
+ .filter(
+ c ->
+ Boolean.TRUE.equals(c.getIsUse()) && Boolean.FALSE.equals(c.getDeleted()))
+ .sorted(
+ Comparator.comparing(
+ MenuEntity::getMenuOrder, Comparator.nullsLast(Long::compareTo)))
+ .map(
+ c ->
+ new MyMenuDto.Basic(
+ c.getMenuUid(), c.getMenuNm(), c.getMenuUrl(), c.getMenuOrder()))
+ .forEach(childDto -> p.getChildren().add(childDto));
+
+ return p;
+ })
+ .sorted(
+ Comparator.comparing(MyMenuDto.Basic::getOrder, Comparator.nullsLast(Long::compareTo)))
+ .toList();
}
/**
- * 메뉴별 권한 조회
+ * 사용자 role 기준으로 접근 가능한 메뉴 URL 목록 조회
*
+ * @param role
* @return
*/
- public List getMenuWithRoles() {
- return menuRepository.getFindByMenuWithRoles();
+ public List findAllowedMenuUrlsByRole(String role) {
+ return menuRepository.findAllowedMenuUrlsByRole(role).stream()
+ .map(MenuEntity::getMenuUrl)
+ .filter(url -> url != null && !url.isBlank())
+ .distinct()
+ .toList();
}
}
diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MenuEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MenuEntity.java
index 15c98cea..d008e247 100644
--- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MenuEntity.java
+++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MenuEntity.java
@@ -2,7 +2,15 @@ package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.menu.dto.MenuDto;
import com.kamco.cd.kamcoback.postgres.CommonDateEntity;
-import jakarta.persistence.*;
+import jakarta.persistence.CascadeType;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
@@ -15,6 +23,7 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "tb_menu")
public class MenuEntity extends CommonDateEntity {
+
@Id
@Column(name = "menu_uid")
private String menuUid;
@@ -49,9 +58,6 @@ public class MenuEntity extends CommonDateEntity {
@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List children = new ArrayList<>();
- @Column(name = "menu_api_uri")
- private String menuApiUri;
-
public MenuDto.Basic toDto() {
return new MenuDto.Basic(
this.menuUid,
@@ -65,7 +71,6 @@ public class MenuEntity extends CommonDateEntity {
this.updatedUid,
this.children.stream().map(MenuEntity::toDto).toList(),
this.getCreatedDate(),
- this.getModifiedDate(),
- this.menuApiUri);
+ this.getModifiedDate());
}
}
diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelMngEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelMngEntity.java
index 9e40a9d9..9c06015f 100644
--- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelMngEntity.java
+++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ModelMngEntity.java
@@ -1,7 +1,12 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.postgres.CommonDateEntity;
-import jakarta.persistence.*;
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
import java.time.ZonedDateTime;
import lombok.Getter;
import lombok.Setter;
@@ -11,6 +16,7 @@ import lombok.Setter;
@Entity
@Table(name = "tb_model_mng")
public class ModelMngEntity extends CommonDateEntity {
+
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "model_uid")
@@ -43,6 +49,9 @@ public class ModelMngEntity extends CommonDateEntity {
@Column(name = "file_name")
private String fileName;
+ @Column(name = "memo")
+ private String memo;
+
public void deleted() {
this.deleted = true;
}
diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/menu/MenuRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/menu/MenuRepositoryCustom.java
index d0f8a9b7..775ff647 100644
--- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/menu/MenuRepositoryCustom.java
+++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/menu/MenuRepositoryCustom.java
@@ -1,6 +1,5 @@
package com.kamco.cd.kamcoback.postgres.repository.menu;
-import com.kamco.cd.kamcoback.menu.dto.MenuDto.MenuWithRolesDto;
import com.kamco.cd.kamcoback.postgres.entity.MenuEntity;
import java.util.List;
@@ -17,9 +16,10 @@ public interface MenuRepositoryCustom {
List getFindByRole(String role);
/**
- * 메뉴별 권한 조회
+ * 사용자 role 기준으로 접근 가능한 메뉴 URL 목록 조회
*
+ * @param role
* @return
*/
- List getFindByMenuWithRoles();
+ List findAllowedMenuUrlsByRole(String role);
}
diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/menu/MenuRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/menu/MenuRepositoryImpl.java
index a995e7d7..62a91213 100644
--- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/menu/MenuRepositoryImpl.java
+++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/menu/MenuRepositoryImpl.java
@@ -3,13 +3,8 @@ package com.kamco.cd.kamcoback.postgres.repository.menu;
import static com.kamco.cd.kamcoback.postgres.entity.QMenuEntity.menuEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMenuMappEntity.menuMappEntity;
-import com.kamco.cd.kamcoback.menu.dto.MenuDto.MenuWithRolesDto;
import com.kamco.cd.kamcoback.postgres.entity.MenuEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMenuEntity;
-import com.kamco.cd.kamcoback.postgres.entity.QMenuMappEntity;
-import com.querydsl.core.types.Expression;
-import com.querydsl.core.types.Projections;
-import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import lombok.RequiredArgsConstructor;
@@ -34,12 +29,14 @@ public class MenuRepositoryImpl implements MenuRepositoryCustom {
@Override
public List getFindByRole(String role) {
+ QMenuEntity parent = QMenuEntity.menuEntity;
QMenuEntity child = new QMenuEntity("child");
+
List content =
queryFactory
- .selectDistinct(menuEntity)
- .from(menuEntity)
- .leftJoin(menuEntity.children, child)
+ .selectDistinct(parent)
+ .from(parent)
+ .leftJoin(parent.children, child)
.fetchJoin()
.leftJoin(menuMappEntity)
.on(
@@ -47,43 +44,39 @@ public class MenuRepositoryImpl implements MenuRepositoryCustom {
.roleCode
.eq(role)
.and(menuMappEntity.deleted.isFalse())
- .and(
- menuMappEntity.menuUid.eq(menuEntity).or(menuMappEntity.menuUid.eq(child))))
+ .and(menuMappEntity.menuUid.eq(child)))
.where(
- menuEntity.parent.isNull(),
- menuEntity.deleted.isFalse(),
- menuEntity.isUse.isTrue(),
+ parent.parent.isNull(),
+ parent.deleted.isFalse(),
+ parent.isUse.isTrue(),
menuMappEntity.id.isNotNull())
- .orderBy(menuEntity.menuOrder.asc().nullsLast(), child.menuOrder.asc().nullsLast())
+ .orderBy(parent.menuOrder.asc().nullsLast(), child.menuOrder.asc().nullsLast())
.fetch();
return content;
}
+ /**
+ * 사용자 role 기준으로 접근 가능한 메뉴 URL 목록 조회
+ *
+ * @param role
+ * @return
+ */
@Override
- public List getFindByMenuWithRoles() {
- QMenuEntity tm = menuEntity;
- QMenuMappEntity tmm = menuMappEntity;
+ public List findAllowedMenuUrlsByRole(String role) {
- Expression roleAgg =
- Expressions.stringTemplate("string_agg({0}, {1})", tmm.roleCode, Expressions.constant(","));
-
- List content =
- queryFactory
- .select(
- Projections.constructor(
- MenuWithRolesDto.class,
- tm.menuUid,
- tm.menuNm,
- tm.menuUrl,
- tm.menuApiUri,
- roleAgg))
- .from(tm)
- .leftJoin(tmm)
- .on(tmm.menuUid.eq(tm).and(tmm.deleted.isFalse()))
- .where(tm.deleted.isFalse())
- .groupBy(tm.menuUid, tm.menuNm, tm.menuUrl, tm.menuApiUri)
- .fetch();
- return content;
+ return queryFactory
+ .selectDistinct(menuEntity)
+ .from(menuMappEntity)
+ .join(menuMappEntity.menuUid, menuEntity)
+ .where(
+ menuMappEntity.roleCode.eq(role),
+ menuMappEntity.deleted.isFalse(),
+ menuEntity.deleted.isFalse(),
+ menuEntity.isUse.isTrue(),
+ menuEntity.menuUrl.isNotNull(),
+ menuEntity.menuUrl.isNotEmpty())
+ .orderBy(menuEntity.menuOrder.asc().nullsLast())
+ .fetch();
}
}
diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/model/ModelMngRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/model/ModelMngRepositoryImpl.java
index c9056fe7..18290f45 100644
--- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/model/ModelMngRepositoryImpl.java
+++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/model/ModelMngRepositoryImpl.java
@@ -9,7 +9,10 @@ import com.kamco.cd.kamcoback.postgres.entity.ModelMngEntity;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Projections;
-import com.querydsl.core.types.dsl.*;
+import com.querydsl.core.types.dsl.BooleanExpression;
+import com.querydsl.core.types.dsl.Expressions;
+import com.querydsl.core.types.dsl.NumberPath;
+import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.math.BigDecimal;
import java.time.LocalDate;
@@ -71,13 +74,12 @@ public class ModelMngRepositoryImpl extends QuerydslRepositorySupport
modelMngEntity.modelType,
Expressions.stringTemplate(
"to_char({0}, 'YYYY-MM-DD')", modelMngEntity.createCompleteDttm),
- Expressions.stringTemplate(
- "to_char({0}, 'YYYY-MM-DD')", modelMngEntity.recentUseDttm),
roundNumericToPercent(modelResultMetricEntity.f1Score),
roundNumericToPercent(modelResultMetricEntity.precision),
roundNumericToPercent(modelResultMetricEntity.recall),
roundNumericToPercent(modelResultMetricEntity.loss),
roundNumericToPercent(modelResultMetricEntity.iou),
+ modelMngEntity.memo,
modelMngEntity.deleted))
.from(modelMngEntity)
.innerJoin(modelResultMetricEntity)