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 c3779d20..32c8520f 100644
--- a/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngApiController.java
+++ b/src/main/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngApiController.java
@@ -16,14 +16,7 @@ import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.http.MediaType;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RequestPart;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@Tag(name = "영상 관리", description = "영상 관리 API")
@@ -112,6 +105,25 @@ public class MapSheetMngApiController {
return ApiResponseDto.ok(mapSheetMngService.mngComplete(mngYyyy));
}
+ @Operation(summary = "영상 데이터 관리 년도 목록", description = "영상 데이터 관리 년도 목록")
+ @ApiResponses(
+ value = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "조회 성공",
+ content =
+ @Content(
+ mediaType = "application/json",
+ schema = @Schema(implementation = CommonCodeDto.Basic.class))),
+ @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
+ @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
+ })
+ @PostMapping("/mng-year-list")
+ public ApiResponseDto> findMapSheetMngYyyyList() {
+
+ return ApiResponseDto.ok(mapSheetMngService.findMapSheetMngYyyyList());
+ }
+
@Operation(summary = "영상 데이터 관리 오류 목록", description = "영상 데이터 관리 오류 목록")
@ApiResponses(
value = {
@@ -163,8 +175,10 @@ public class MapSheetMngApiController {
public ApiResponseDto uploadPair(
@RequestPart("tfw") MultipartFile tfwFile,
@RequestPart("tif") MultipartFile tifFile,
- @RequestParam(value = "hstUid", required = false) Long hstUid) {
- return ApiResponseDto.createOK(mapSheetMngService.uploadPair(tfwFile, tifFile, hstUid));
+ @RequestParam(value = "hstUid", required = false) Long hstUid,
+ @RequestParam(value = "overwrite", required = false) boolean overwrite) {
+ return ApiResponseDto.createOK(
+ mapSheetMngService.uploadPair(tfwFile, tifFile, hstUid, overwrite));
}
@Operation(summary = "영상관리 > 파일조회", description = "영상관리 > 파일조회")
diff --git a/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java b/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java
new file mode 100644
index 00000000..ffa25353
--- /dev/null
+++ b/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java
@@ -0,0 +1,259 @@
+package com.kamco.cd.kamcoback.mapsheet.service;
+
+import com.kamco.cd.kamcoback.common.utils.FIleChecker;
+import com.kamco.cd.kamcoback.config.FileConfig;
+import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto;
+import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.AddReq;
+import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.DmlReturn;
+import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.ErrorDataDto;
+import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.ErrorSearchReq;
+import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngDto;
+import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngFilesDto;
+import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService;
+import jakarta.validation.Valid;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class MapSheetMngService {
+
+ private final MapSheetMngCoreService mapSheetMngCoreService;
+ private final FileConfig fileConfig;
+
+ public List findMapSheetMngList() {
+ return mapSheetMngCoreService.findMapSheetMngList();
+ }
+
+ public List findMapSheetMngYyyyList() {
+ return mapSheetMngCoreService.findMapSheetMngYyyyList();
+ }
+
+ public MngDto findMapSheetMng(int mngYyyy) {
+ return mapSheetMngCoreService.findMapSheetMng(mngYyyy);
+ }
+
+ @Transactional
+ public DmlReturn mngComplete(int mngYyyy) {
+
+ mapSheetMngCoreService.MapSheetMngComplete(mngYyyy);
+
+ return new DmlReturn("success", "작업완료 처리되었습니다.");
+ }
+
+ public Page findMapSheetErrorList(@Valid ErrorSearchReq searchReq) {
+ return mapSheetMngCoreService.findMapSheetErrorList(searchReq);
+ }
+
+ public ErrorDataDto findMapSheetError(Long hstUid) {
+ return mapSheetMngCoreService.findMapSheetError(hstUid);
+ }
+
+ @Transactional
+ public DmlReturn mngDataSave(AddReq AddReq) {
+ return mapSheetMngCoreService.mngDataSave(AddReq);
+ }
+
+ public DmlReturn uploadProcess(@Valid List hstUidList) {
+ return mapSheetMngCoreService.uploadProcess(hstUidList);
+ }
+
+ public DmlReturn updateExceptUseInference(@Valid List hstUidList) {
+ return mapSheetMngCoreService.updateExceptUseInference(hstUidList);
+ }
+
+ @Transactional
+ public DmlReturn uploadPair(
+ MultipartFile tfwFile, MultipartFile tifFile, Long hstUid, Boolean overwrite) {
+
+ String rootPath = fileConfig.getRootSyncDir();
+ String tmpPath = fileConfig.getTmpSyncDir();
+
+ ErrorDataDto errDto = mapSheetMngCoreService.findMapSheetError(hstUid);
+
+ if (errDto == null) {
+ return new DmlReturn("fail", "NO hstUid Data");
+ }
+
+ // 파일검증용 임시저장 폴더 확인
+ if (!FIleChecker.mkDir(tmpPath)) {
+ return new DmlReturn("fail", "CREATE TEMP FOLDER ERROR");
+ }
+
+ // 파일 유효성 검증
+ if (tfwFile == null || tfwFile.isEmpty() || tfwFile.getSize() == 0) {
+ return new DmlReturn("fail", "TFW SIZE 오류");
+ } else if (tifFile == null || tifFile.isEmpty() || tifFile.getSize() == 0) {
+ return new DmlReturn("fail", "TIF SIZE 오류");
+ }
+
+ // 확장자명 체크
+ if (!FIleChecker.checkExtensions(tfwFile.getOriginalFilename(), "tfw")) {
+ return new DmlReturn("fail", "TFW FILENAME ERROR");
+ } else if (!FIleChecker.checkExtensions(tifFile.getOriginalFilename(), "tif")) {
+ return new DmlReturn("fail", "TIF FILENAME ERROR");
+ }
+
+ MngDto mngDto = mapSheetMngCoreService.findMapSheetMng(errDto.getMngYyyy());
+ String targetYearDir = mngDto.getMngPath();
+
+ // 중복체크
+ List basicTfwList =
+ FIleChecker.getFilesFromAllDepth(targetYearDir, tfwFile.getOriginalFilename(), "tfw");
+
+ List basicTifList =
+ FIleChecker.getFilesFromAllDepth(targetYearDir, tifFile.getOriginalFilename(), "tif");
+
+ int tfwCnt =
+ (int)
+ basicTfwList.stream()
+ .filter(dto -> dto.getExtension().toString().equals("tfw"))
+ .count();
+
+ int tifCnt =
+ (int)
+ basicTifList.stream()
+ .filter(dto -> dto.getExtension().toString().equals("tif"))
+ .count();
+
+ if (!overwrite) {
+ if (tfwCnt > 0 || tifCnt > 0) {
+ String tfwtifMsg = "";
+ if (tfwCnt > 0) tfwtifMsg = tfwFile.getOriginalFilename();
+ if (tifCnt > 0) {
+ if (tfwCnt > 0) tfwtifMsg = "," + tifFile.getOriginalFilename();
+ else tfwtifMsg = tifFile.getOriginalFilename();
+ }
+ return new DmlReturn("duplicate", tfwtifMsg);
+ }
+ }
+
+ File directory = new File(tmpPath);
+ String tfwTmpPath = tmpPath + tfwFile.getOriginalFilename();
+ Path tfwTmpSavePath = Paths.get(tfwTmpPath);
+ String tifTmpPath = tmpPath + tifFile.getOriginalFilename();
+ Path tifTmpSavePath = Paths.get(tifTmpPath);
+ boolean fileUpload = true;
+ try {
+ tfwFile.transferTo(tfwTmpSavePath);
+ tifFile.transferTo(tifTmpSavePath);
+ } catch (IOException e) {
+ // throw new RuntimeException(e);
+ return new DmlReturn("fail", "UPLOAD ERROR");
+ }
+
+ if (!FIleChecker.cmmndGdalInfo(tifTmpPath)) return new DmlReturn("fail", "TIF TYPE ERROR");
+ if (!FIleChecker.checkTfw(tfwTmpPath)) return new DmlReturn("fail", "TFW TYPE ERROR");
+
+ // 싱크파일목록으로 업로드 경로 확인
+ List mngFiles = mapSheetMngCoreService.findIdToMapSheetFileList(hstUid);
+ String uploadPath = "";
+ for (MngFilesDto dto : mngFiles) {
+ uploadPath = dto.getFilePath();
+ break;
+ }
+
+ Path tfwTargetPath = null;
+ Path tifTargetPath = null;
+
+ // 파일이 존재하지 않을 경우(0개) 해당년도 다른 파일경로로 참조
+ if (uploadPath.isEmpty()) {
+ MngFilesDto filesDto =
+ mapSheetMngCoreService.findYyyyToMapSheetFilePathRefer(errDto.getMngYyyy());
+ String referPath = filesDto.getFilePath();
+ uploadPath = Paths.get(referPath).getParent().toString() + "/" + errDto.getRefMapSheetNum();
+ }
+
+ // 업로드 경로 확인(없으면 생성)
+ if (!FIleChecker.mkDir(uploadPath)) {
+ return new DmlReturn("fail", "CREATE FOLDER ERROR");
+ }
+
+ tfwTargetPath = Paths.get(uploadPath).resolve(tfwFile.getOriginalFilename());
+ tifTargetPath = Paths.get(uploadPath).resolve(tifFile.getOriginalFilename());
+
+ try {
+ Files.move(tfwTmpSavePath, tfwTargetPath, StandardCopyOption.REPLACE_EXISTING);
+ Files.move(tifTmpSavePath, tifTargetPath, StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ // hst업데이트
+ MapSheetMngDto.SyncCheckStateReqUpdateDto updReqSyncCheckState =
+ new MapSheetMngDto.SyncCheckStateReqUpdateDto();
+ updReqSyncCheckState.setHstUid(hstUid);
+ updReqSyncCheckState.setFilePath(uploadPath);
+ updReqSyncCheckState.setSyncCheckTfwFileName(tfwFile.getOriginalFilename());
+ updReqSyncCheckState.setSyncCheckTifFileName(tifFile.getOriginalFilename());
+ updReqSyncCheckState.setSyncCheckState("DONE");
+ mapSheetMngCoreService.updateMapSheetMngHstSyncCheckState(updReqSyncCheckState);
+ // 파일정보 업데이트
+ mapSheetMngCoreService.deleteByHstUidMngFile(hstUid);
+
+ MapSheetMngDto.MngFileAddReq addReq = new MapSheetMngDto.MngFileAddReq();
+ addReq.setMngYyyy(errDto.getMngYyyy());
+ addReq.setMapSheetNum(errDto.getMapSheetNum());
+ addReq.setRefMapSheetNum(errDto.getRefMapSheetNum());
+ addReq.setFilePath(uploadPath);
+ addReq.setFileName(tfwFile.getOriginalFilename());
+ addReq.setFileExt("tfw");
+ addReq.setFileSize(tfwFile.getSize());
+ addReq.setHstUid(errDto.getHstUid());
+ addReq.setFileState("DONE");
+
+ mapSheetMngCoreService.mngFileSave(addReq);
+
+ addReq.setFileExt("tif");
+ addReq.setFileSize(tifFile.getSize());
+ mapSheetMngCoreService.mngFileSave(addReq);
+
+ return new DmlReturn("success", "파일 업로드 완료되었습니다.");
+ }
+
+ public List findHstUidToMapSheetFileList(Long hstUid) {
+ return mapSheetMngCoreService.findHstUidToMapSheetFileList(hstUid);
+ }
+
+ @Transactional
+ public DmlReturn deleteByFileUidMngFile(List fileUids) {
+
+ long hstUid = 0;
+ // hstUid = 149049;
+
+ for (Long uid : fileUids) {
+ MapSheetMngDto.MngFilesDto dto = mapSheetMngCoreService.findIdToMapSheetFile(uid);
+ hstUid = dto.getHstUid();
+
+ String filePath = dto.getFilePath() + "/" + dto.getFileName();
+ Path path = Paths.get(filePath);
+ try {
+ boolean isDeleted = Files.deleteIfExists(path);
+ if (isDeleted) {
+ System.out.println("파일 삭제 성공: " + filePath);
+ } else {
+ System.out.println("삭제 실패: 파일이 존재하지 않습니다.");
+ }
+ } catch (IOException e) {
+ System.err.println("파일 삭제 중 오류 발생: " + e.getMessage());
+ }
+ DmlReturn dmlReturn = mapSheetMngCoreService.deleteByFileUidMngFile(uid);
+ }
+
+ // 중복제거 확인후 처리상태(DONE)변경
+ if (hstUid > 0) mapSheetMngCoreService.updateByHstUidSyncCheckState(hstUid);
+
+ return new DmlReturn("success", fileUids.size() + "개 파일이 삭제되었습니다.");
+ }
+}
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/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/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/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/resources/application-local.yml b/src/main/resources/application-local.yml
index 054d08ac..0889d7ff 100644
--- a/src/main/resources/application-local.yml
+++ b/src/main/resources/application-local.yml
@@ -25,9 +25,9 @@ spring:
data:
redis:
- host: localhost
+ host: 192.168.2.109
port: 6379
- password: 1234
+ password: kamco
servlet:
multipart: