shp파일 a 링크로 다운로드할때 이력 저장 변경

This commit is contained in:
2026-02-26 15:25:10 +09:00
parent 2188d426d4
commit eccdfb17e6
13 changed files with 102 additions and 44 deletions

View File

@@ -5,7 +5,9 @@ import com.kamco.cd.kamcoback.common.download.dto.DownloadAuditEvent;
import com.kamco.cd.kamcoback.menu.dto.MenuDto; import com.kamco.cd.kamcoback.menu.dto.MenuDto;
import com.kamco.cd.kamcoback.menu.service.MenuService; import com.kamco.cd.kamcoback.menu.service.MenuService;
import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity; import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity;
import com.kamco.cd.kamcoback.postgres.entity.MemberEntity;
import com.kamco.cd.kamcoback.postgres.repository.log.AuditLogRepository; import com.kamco.cd.kamcoback.postgres.repository.log.AuditLogRepository;
import com.kamco.cd.kamcoback.postgres.repository.members.MembersRepository;
import java.util.Comparator; import java.util.Comparator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
@@ -24,6 +26,7 @@ import org.springframework.transaction.annotation.Transactional;
public class DownloadAuditEventListener { public class DownloadAuditEventListener {
private final AuditLogRepository auditLogRepository; private final AuditLogRepository auditLogRepository;
private final MembersRepository membersRepository;
private final MenuService menuService; private final MenuService menuService;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
@@ -42,9 +45,23 @@ public class DownloadAuditEventListener {
return; return;
} }
Long userId = ev.userId();
if (userId == null) {
// a 링크로 들어온 download는 사번으로 파라미터가 전달 되므로 사번으로 user id 조회 하기
MemberEntity memberEntity =
membersRepository.findByEmployeeNo(ev.employeeNo()).orElse(null);
if (memberEntity == null) {
return; // 매핑 실패 시 로그 저장 안 함
}
userId = memberEntity.getId();
}
AuditLogEntity logEntity = AuditLogEntity logEntity =
AuditLogEntity.forFileDownload( AuditLogEntity.forFileDownload(
ev.userId(), ev.requestUri(), menuUid, ev.ip(), ev.status(), ev.downloadUuid()); userId, ev.requestUri(), menuUid, ev.ip(), ev.status(), ev.downloadUuid());
auditLogRepository.save(logEntity); auditLogRepository.save(logEntity);

View File

@@ -4,6 +4,7 @@ import java.util.UUID;
public record DownloadAuditEvent( public record DownloadAuditEvent(
Long userId, Long userId,
String employeeNo,
String requestUri, String requestUri,
String normalizedUri, String normalizedUri,
String ip, String ip,

View File

@@ -13,6 +13,7 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerInterceptor;
/** 파일 다운로드 log 저장 */
@Slf4j @Slf4j
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@@ -30,9 +31,17 @@ public class FileDownloadInteceptor implements HandlerInterceptor {
if (request.getDispatcherType() != DispatcherType.REQUEST) return; if (request.getDispatcherType() != DispatcherType.REQUEST) return;
Long userId; Long userId;
String employeeNo = "";
try { try {
// a 링크 다운로드일경우 userId가 없으므로 전달받은 사번을 넣는다
userId = userUtil.getId(); userId = userUtil.getId();
if (userId == null) return; // userId null 불가면 스킵 if (userId == null) {
employeeNo = request.getParameter("employeeNo");
if (employeeNo == null) {
return;
}
}
} catch (Exception e) { } catch (Exception e) {
log.warn("Download audit userId resolve failed. uri={}, err={}", uri, e.toString()); log.warn("Download audit userId resolve failed. uri={}, err={}", uri, e.toString());
return; return;
@@ -48,8 +57,9 @@ public class FileDownloadInteceptor implements HandlerInterceptor {
return; // downloadUuid null 불가 -> 스킵 return; // downloadUuid null 불가 -> 스킵
} }
// log저장 DownloadAuditEventListener 클래스 호출
publisher.publishEvent( publisher.publishEvent(
new DownloadAuditEvent(userId, uri, normalizedUri, ip, status, downloadUuid)); new DownloadAuditEvent(userId, employeeNo, uri, normalizedUri, ip, status, downloadUuid));
} }
private UUID extractUuidFromUri(String uri) { private UUID extractUuidFromUri(String uri) {

View File

@@ -77,7 +77,7 @@ public class SecurityConfig {
// 다운로드는 인증 필요 // 다운로드는 인증 필요
.requestMatchers(HttpMethod.GET, DownloadPaths.PATTERNS) .requestMatchers(HttpMethod.GET, DownloadPaths.PATTERNS)
.authenticated() .permitAll()
// 메뉴 등록 ADMIN만 가능 // 메뉴 등록 ADMIN만 가능
.requestMatchers(HttpMethod.POST, "/api/menu/auth") .requestMatchers(HttpMethod.POST, "/api/menu/auth")

View File

@@ -1,4 +1,4 @@
package com.kamco.cd.kamcoback.inference.service; package com.kamco.cd.kamcoback.inference;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;

View File

@@ -18,7 +18,6 @@ import com.kamco.cd.kamcoback.model.dto.ModelMngDto;
import com.kamco.cd.kamcoback.model.service.ModelMngService; import com.kamco.cd.kamcoback.model.service.ModelMngService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
@@ -58,7 +57,8 @@ public class InferenceResultApiController {
private final ModelMngService modelMngService; private final ModelMngService modelMngService;
private final RangeDownloadResponder rangeDownloadResponder; private final RangeDownloadResponder rangeDownloadResponder;
@Operation(summary = "추론관리 목록", description = "어드민 홈 > 추론관리 > 추론관리 > 추론관리 목록") /** 추론관리 목록 화면에서 호출 */
@Operation(summary = "추론관리 목록", description = "추론관리 > 추론관리 목록 ")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
@@ -90,7 +90,8 @@ public class InferenceResultApiController {
return ApiResponseDto.ok(analResList); return ApiResponseDto.ok(analResList);
} }
@Operation(summary = "추론 진행 여부 확인", description = "어드민 홈 > 추론관리 > 추론관리 > 추론관리 목록") /** 추론관리 목록 화면에서 호출 */
@Operation(summary = "추론 진행 여부 확인", description = "추론관리 > 추론관리 목록")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
@@ -112,7 +113,8 @@ public class InferenceResultApiController {
return ApiResponseDto.ok(inferenceResultService.getProcessing()); return ApiResponseDto.ok(inferenceResultService.getProcessing());
} }
@Operation(summary = "년도 목록 조회", description = "어드민 홈 > 추론관리 > 추론목록 > 변화탐지 실행 정보 입력 > 년도 목록 조회") /** 추론관리 목록 화면에서 호출 */
@Operation(summary = "년도 목록 조회", description = "추론관리 > 추론목록 > 변화탐지 실행 정보 입력 > 년도 목록 조회")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
@@ -130,7 +132,8 @@ public class InferenceResultApiController {
return ApiResponseDto.ok(mapSheetMngService.findMapSheetMngDoneYyyyList()); return ApiResponseDto.ok(mapSheetMngService.findMapSheetMngDoneYyyyList());
} }
@Operation(summary = "변화탐지 실행 정보 입력", description = "어드민 홈 > 추론관리 > 추론목록 > 변화탐지 실행 정보 입력") /** 변화탐지 실행 정보 입력화면에서 호출 */
@Operation(summary = "변화탐지 실행 정보 입력", description = "추론관리 > 추론목록 > 변화탐지 실행 정보 입력")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
@@ -155,7 +158,8 @@ public class InferenceResultApiController {
return ApiResponseDto.ok(uuid); return ApiResponseDto.ok(uuid);
} }
@Operation(summary = "추론 종료", description = "추론 종료") /** 추론진행 현황 화면에서 호출 */
@Operation(summary = "추론 종료", description = "추론관리 > 추론목록 > 추론진행 현황")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
@@ -174,7 +178,8 @@ public class InferenceResultApiController {
return ApiResponseDto.ok(uuid); return ApiResponseDto.ok(uuid);
} }
@Operation(summary = "분석 모델 선택 조회", description = "변화탐지 실행 정보 입력 모델선택 팝업 ") /** 변화탐지 실행 정보 입력화면에서 호출 */
@Operation(summary = "분석 모델 선택 조회", description = "추론관리 > 추론목록 > 변화탐지 실행 정보 입력 > 모델선택 팝업 ")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
@@ -205,7 +210,8 @@ public class InferenceResultApiController {
return ApiResponseDto.ok(result); return ApiResponseDto.ok(result);
} }
@Operation(summary = "추론관리 추론진행 서버 현황", description = "추론관리 추론진행 서버 현황") /** 추론진행 현황 화면에서 호출 */
@Operation(summary = "추론관리 추론진행 서버 현황", description = "추론관리 > 추론목록 > 추론진행 현황")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
@@ -224,7 +230,8 @@ public class InferenceResultApiController {
return ApiResponseDto.ok(inferenceResultService.getInferenceServerStatusList()); return ApiResponseDto.ok(inferenceResultService.getInferenceServerStatusList());
} }
@Operation(summary = "추론관리 진행현황 상세", description = "어드민 홈 > 추론관리 > 추론관리 > 진행현황 상세") /** 추론진행 현황 화면에서 호출 */
@Operation(summary = "추론관리 진행현황 상세", description = "추론관리 > 추론진행 현황")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
@@ -248,7 +255,8 @@ public class InferenceResultApiController {
return ApiResponseDto.ok(inferenceResultService.getInferenceStatus(uuid)); return ApiResponseDto.ok(inferenceResultService.getInferenceStatus(uuid));
} }
@Operation(summary = "추론결과 기본정보", description = "추론결과 기본정보") /** 추론결과 화면에서 호출 */
@Operation(summary = "추론결과 기본정보", description = "추론관리 > 추론결과")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
@@ -269,7 +277,8 @@ public class InferenceResultApiController {
return ApiResponseDto.ok(inferenceResultService.getInferenceResultInfo(uuid)); return ApiResponseDto.ok(inferenceResultService.getInferenceResultInfo(uuid));
} }
@Operation(summary = "추론결과 분류별 탐지 건수", description = "추론결과 분류별 탐지 건수") /** 추론결과 화면에서 호출 */
@Operation(summary = "추론결과 분류별 탐지 건수", description = "추론관리 > 추론결과")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
@@ -290,6 +299,7 @@ public class InferenceResultApiController {
return ApiResponseDto.ok(inferenceResultService.getInferenceClassCountList(uuid)); return ApiResponseDto.ok(inferenceResultService.getInferenceClassCountList(uuid));
} }
/** 추론결과 화면에서 호출 */
@Operation(summary = "추론관리 분석결과 상세 목록", description = "추론관리 분석결과 상세 목록 geojson 데이터 조회") @Operation(summary = "추론관리 분석결과 상세 목록", description = "추론관리 분석결과 상세 목록 geojson 데이터 조회")
@ApiResponses( @ApiResponses(
value = { value = {
@@ -329,26 +339,13 @@ public class InferenceResultApiController {
return ApiResponseDto.ok(geomList); return ApiResponseDto.ok(geomList);
} }
@Operation( /** 추론결과 화면에서 호출 */
summary = "shp 파일 다운로드", @Operation(summary = "shp 파일 다운로드", description = "추론관리 분석결과 shp 파일 다운로드")
description = "추론관리 분석결과 shp 파일 다운로드",
parameters = {
@Parameter(
name = "kamco-download-uuid",
in = ParameterIn.HEADER,
required = true,
description = "다운로드 요청 UUID",
schema =
@Schema(
type = "string",
format = "uuid",
example = "69c4e56c-e0bf-4742-9225-bba9aae39052"))
})
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
responseCode = "200", responseCode = "200",
description = "shp zip파일 다운로드", description = "shp 파일 다운로드",
content = content =
@Content( @Content(
mediaType = "application/octet-stream", mediaType = "application/octet-stream",
@@ -357,13 +354,16 @@ public class InferenceResultApiController {
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
}) })
@GetMapping("/download/{uuid}") @GetMapping("/download/{uuid}")
public ResponseEntity<?> download(@PathVariable UUID uuid, HttpServletRequest request) public ResponseEntity<?> download(
@PathVariable UUID uuid,
@Parameter(description = "사번", example = "123456") @RequestParam String employeeNo,
HttpServletRequest request)
throws IOException { throws IOException {
String path; String path;
String uid; String uid;
try { try {
// 추론결과 shp zip 파일 확인하여 다운로드 경로 생성
Map<String, Object> map = inferenceResultService.shpDownloadPath(uuid); Map<String, Object> map = inferenceResultService.shpDownloadPath(uuid);
path = String.valueOf(map.get("path")); path = String.valueOf(map.get("path"));
uid = String.valueOf(map.get("uid")); uid = String.valueOf(map.get("uid"));
@@ -377,6 +377,7 @@ public class InferenceResultApiController {
return rangeDownloadResponder.buildZipResponse(zipPath, uid + ".zip", request); return rangeDownloadResponder.buildZipResponse(zipPath, uid + ".zip", request);
} }
/** 추론결과 화면에서 호출 */
@Operation(summary = "shp 파일 다운로드 이력 조회", description = "추론관리 분석결과 shp 파일 다운로드 이력 조회") @Operation(summary = "shp 파일 다운로드 이력 조회", description = "추론관리 분석결과 shp 파일 다운로드 이력 조회")
@GetMapping(value = "/download-audit/{uuid}") @GetMapping(value = "/download-audit/{uuid}")
@ApiResponses( @ApiResponses(
@@ -419,7 +420,8 @@ public class InferenceResultApiController {
return ApiResponseDto.ok(inferenceResultService.getDownloadAudit(searchReq, downloadReq)); return ApiResponseDto.ok(inferenceResultService.getDownloadAudit(searchReq, downloadReq));
} }
@Operation(summary = "추론 실행중인 도엽 목록", description = "추론관리 실행중인 도엽명 5k 목록") /** 추론진행 현황 화면에서 호출, 분석도엽 부분 옵션일때 분석중인 도엽 확인용 */
@Operation(summary = "추론관리 분석중인 도엽명 5k 목록", description = "추론관리 분석중인 도엽명 50k 목록")
@ApiResponses({ @ApiResponses({
@ApiResponse( @ApiResponse(
responseCode = "200", responseCode = "200",

View File

@@ -107,7 +107,7 @@ public class InferenceResultService {
} }
/** /**
* 추론 진행중인지 확인 * 추론 진행중인지 확인, 변화탐지 설정 등록 버튼 활성화 여부에 필요함
* *
* @return * @return
*/ */
@@ -952,10 +952,10 @@ public class InferenceResultService {
} }
/** /**
* 추론결과 shp zip 파일 다운로드 경로 생성 * 추론결과 shp zip 파일 확인하여 다운로드 경로 생성
* *
* @param uuid * @param uuid 추론 uuid
* @return * @return 32자 추론 uid, shp 파일 경로
*/ */
public Map<String, Object> shpDownloadPath(UUID uuid) { public Map<String, Object> shpDownloadPath(UUID uuid) {
InferenceLearnDto dto = inferenceResultCoreService.getInferenceUid(uuid); InferenceLearnDto dto = inferenceResultCoreService.getInferenceUid(uuid);
@@ -981,7 +981,7 @@ public class InferenceResultService {
} }
/** /**
* 실행중인 추론 도엽명 목록 * 분석중인 추론 도엽명 목록
* *
* @param uuid uuid * @param uuid uuid
* @return * @return

View File

@@ -381,6 +381,9 @@ public class MapSheetMngService {
mapSheetMngCoreService.getSceneInference(yyyy); mapSheetMngCoreService.getSceneInference(yyyy);
} }
/**
* @return
*/
public List<MngYyyyDto> findMapSheetMngDoneYyyyList() { public List<MngYyyyDto> findMapSheetMngDoneYyyyList() {
List<MngDto> mngList = mapSheetMngCoreService.findMapSheetMngList(); List<MngDto> mngList = mapSheetMngCoreService.findMapSheetMngList();

View File

@@ -46,6 +46,13 @@ public class AuditLogCoreService
return auditLogRepository.findLogByAccount(searchRange, searchValue); return auditLogRepository.findLogByAccount(searchRange, searchValue);
} }
/**
* 다운로드 이력 조회
*
* @param searchReq 페이징 파라미터
* @param downloadReq 다운로드 이력 팝업 검색 조건
* @return 다운로드 이력 정보 목록
*/
public Page<AuditLogDto.DownloadRes> findLogByAccount( public Page<AuditLogDto.DownloadRes> findLogByAccount(
AuditLogDto.searchReq searchReq, DownloadReq downloadReq) { AuditLogDto.searchReq searchReq, DownloadReq downloadReq) {
return auditLogRepository.findDownloadLog(searchReq, downloadReq); return auditLogRepository.findDownloadLog(searchReq, downloadReq);

View File

@@ -527,10 +527,10 @@ public class InferenceResultCoreService {
} }
/** /**
* uid 조회 * 추론 정보 조회 하여 batch id, 32자 uid 리턴
* *
* @param uuid * @param uuid 추론 uuid
* @return * @return 추론정보
*/ */
public InferenceLearnDto getInferenceUid(UUID uuid) { public InferenceLearnDto getInferenceUid(UUID uuid) {
MapSheetLearnEntity entity = inferenceResultRepository.getInferenceUid(uuid).orElse(null); MapSheetLearnEntity entity = inferenceResultRepository.getInferenceUid(uuid).orElse(null);
@@ -547,7 +547,7 @@ public class InferenceResultCoreService {
} }
/** /**
* 실행중인 추론 도엽명 목록 * 분석중인 추론 도엽명 목록
* *
* @param uuid 추론 실행중인 uuid * @param uuid 추론 실행중인 uuid
* @return * @return

View File

@@ -16,5 +16,11 @@ public interface InferenceResultRepositoryCustom {
Long getInferenceLearnIdByUuid(UUID uuid); Long getInferenceLearnIdByUuid(UUID uuid);
/**
* 추론 정보 조회
*
* @param uuid 추론 uuid
* @return 추론 정보
*/
Optional<MapSheetLearnEntity> getInferenceUid(UUID uuid); Optional<MapSheetLearnEntity> getInferenceUid(UUID uuid);
} }

View File

@@ -14,5 +14,11 @@ public interface MapSheetLearn5kRepositoryCustom {
List<Long> findCompleted5kList(UUID uuid, List<Long> completedIds, String type); List<Long> findCompleted5kList(UUID uuid, List<Long> completedIds, String type);
/**
* 추론 실행중일때 분석중인 도엽명 목록 조회
*
* @param uuid 추론 uuid
* @return 도엽명+50K 도엽번호
*/
List<String> getInferenceRunMapId(UUID uuid); List<String> getInferenceRunMapId(UUID uuid);
} }

View File

@@ -13,6 +13,12 @@ public interface MembersRepositoryCustom {
boolean existsByEmployeeNo(String employeeNo); boolean existsByEmployeeNo(String employeeNo);
/**
* 사번으로 사용자 조회
*
* @param employeeNo 사번
* @return 사용자 정보 조회
*/
Optional<MemberEntity> findByEmployeeNo(String employeeNo); Optional<MemberEntity> findByEmployeeNo(String employeeNo);
Optional<MemberEntity> findByUserId(String userId); Optional<MemberEntity> findByUserId(String userId);