From 7507cd062a6952cea45f4cd00086b5bdc0cce1e7 Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Mon, 19 Jan 2026 17:28:14 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=EB=8B=A4=EC=9A=B4=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EC=84=B1=EA=B3=B5=EC=97=AC=EB=B6=80=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/kamco/cd/kamcoback/postgres/entity/AuditLogEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/AuditLogEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/AuditLogEntity.java index b6a18f32..c786d59c 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/AuditLogEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/AuditLogEntity.java @@ -77,7 +77,7 @@ public class AuditLogEntity extends CommonCreateEntity { return new AuditLogEntity( userId, EventType.DOWNLOAD, // 이벤트 타입 고정 - httpStatus < 400 ? EventStatus.FAILED : EventStatus.SUCCESS, // 성공 여부 + httpStatus < 400 ? EventStatus.SUCCESS : EventStatus.FAILED, // 성공 여부 menuUid, ip, requestUri, From 098a21ae21b9e2ba92c8e89c16ce6c9f6d5cb778 Mon Sep 17 00:00:00 2001 From: teddy Date: Mon, 19 Jan 2026 17:28:21 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[KC-116]=20shp=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EB=8B=A4=EC=9A=B4=EB=A1=9C=EB=93=9C=20=EC=9D=B4=EB=A0=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InferenceResultApiController.java | 38 +++++++++- .../inference/dto/InferenceResultDto.java | 4 +- .../service/InferenceResultService.java | 19 +++++ .../cd/kamcoback/log/dto/AuditLogDto.java | 47 ++++++++++++ .../postgres/core/AuditLogCoreService.java | 6 ++ .../log/AuditLogRepositoryCustom.java | 3 + .../log/AuditLogRepositoryImpl.java | 74 +++++++++++++++++++ 7 files changed, 188 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java b/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java index cfb122c8..c01092f0 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/InferenceResultApiController.java @@ -8,6 +8,9 @@ import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerSt import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceStatusDetailDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList; import com.kamco.cd.kamcoback.inference.service.InferenceResultService; +import com.kamco.cd.kamcoback.log.dto.AuditLogDto; +import com.kamco.cd.kamcoback.log.dto.AuditLogDto.DownloadReq; +import com.kamco.cd.kamcoback.log.dto.AuditLogDto.searchReq; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngYyyyDto; import com.kamco.cd.kamcoback.mapsheet.service.MapSheetMngService; import com.kamco.cd.kamcoback.model.dto.ModelMngDto; @@ -413,7 +416,7 @@ public class InferenceResultApiController { @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) }) @GetMapping(value = "/download/{uuid}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) - public ResponseEntity downloadZip(@PathVariable UUID uuid) throws IOException { + public ResponseEntity downloadShp(@PathVariable UUID uuid) throws IOException { String path; try { @@ -441,4 +444,37 @@ public class InferenceResultApiController { .contentLength(resource.contentLength()) .body((Resource) resource); } + + @GetMapping(value = "/download-audit/{uuid}") + public ApiResponseDto> downloadAudit( + @Parameter(description = "UUID", example = "0192efc6-9ec2-43ee-9a90-5b73e763c09f") + @PathVariable + UUID uuid, + @Parameter(description = "구분-NAME(이름), EMPLOYEE_NO(사번)", example = "NAME") + @RequestParam(required = false) + String type, + @Parameter(description = "다운로드일 시작", example = "2025-01-01") @RequestParam(required = false) + LocalDate strtDttm, + @Parameter(description = "다운로드일 종료", example = "2026-01-01") @RequestParam(required = false) + LocalDate endDttm, + @Parameter(description = "키워드", example = "변화탐지") @RequestParam(required = false) + String searchValue, + @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0") + int page, + @Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20") + int size) { + AuditLogDto.searchReq searchReq = new searchReq(); + searchReq.setPage(page); + searchReq.setSize(size); + DownloadReq downloadReq = new DownloadReq(); + downloadReq.setUuid(uuid); + downloadReq.setStartDate(strtDttm); + downloadReq.setEndDate(endDttm); + downloadReq.setType(type); + downloadReq.setSearchValue(searchValue); + downloadReq.setMenuId("22"); + downloadReq.setRequestUri("/api/inference/download-audit"); + + return ApiResponseDto.ok(inferenceResultService.getDownloadAudit(searchReq, downloadReq)); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java index 2efdbadb..e8a4da03 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java @@ -23,7 +23,7 @@ import org.springframework.data.domain.Pageable; public class InferenceResultDto { - /** 탐지 데이터 옵션 dto */ + /** 분석대상 도엽 enum */ @Getter @AllArgsConstructor public enum MapSheetScope implements EnumType { @@ -52,7 +52,7 @@ public class InferenceResultDto { } } - /** 분석대상 도엽 enum */ + /** 탐지 데이터 옵션 dto */ @Getter @AllArgsConstructor public enum DetectOption implements EnumType { diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java index 8634d357..00a2aaaa 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java @@ -25,10 +25,13 @@ import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.SaveInferenceAiDt import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.Status; import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto; import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto.pred_requests_areas; +import com.kamco.cd.kamcoback.log.dto.AuditLogDto; +import com.kamco.cd.kamcoback.log.dto.AuditLogDto.DownloadReq; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListCompareDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.TotalListDto; import com.kamco.cd.kamcoback.model.dto.ModelMngDto.Basic; +import com.kamco.cd.kamcoback.postgres.core.AuditLogCoreService; import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService; import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService; import com.kamco.cd.kamcoback.postgres.core.ModelMngCoreService; @@ -62,6 +65,8 @@ public class InferenceResultService { private final InferenceResultCoreService inferenceResultCoreService; private final MapSheetMngCoreService mapSheetMngCoreService; private final ModelMngCoreService modelMngCoreService; + private final AuditLogCoreService auditLogCoreService; + private final ExternalHttpClient externalHttpClient; private final ObjectMapper objectMapper; private final UserUtil userUtil; @@ -554,4 +559,18 @@ public class InferenceResultService { return Path.of(datasetDir).resolve(uid).resolve("merge").resolve(uid + ".zip"); } + + /** + * 다운로드 이력 조회 + * + * @param searchReq 페이징 + * @param startDate 다운로드 시작일 + * @param endDate 다운로드 종료일 + * @param type 검색 구분 + * @param searchValue 키워드 + */ + public Page getDownloadAudit( + AuditLogDto.searchReq searchReq, DownloadReq downloadReq) { + return auditLogCoreService.findLogByAccount(searchReq, downloadReq); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/log/dto/AuditLogDto.java b/src/main/java/com/kamco/cd/kamcoback/log/dto/AuditLogDto.java index 52fab45e..9f19692f 100644 --- a/src/main/java/com/kamco/cd/kamcoback/log/dto/AuditLogDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/log/dto/AuditLogDto.java @@ -1,9 +1,13 @@ package com.kamco.cd.kamcoback.log.dto; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.kamco.cd.kamcoback.common.utils.enums.CodeExpose; +import com.kamco.cd.kamcoback.common.utils.enums.EnumType; import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm; import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDate; import java.time.ZonedDateTime; +import java.util.UUID; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -58,6 +62,7 @@ public class AuditLogDto { @Getter @AllArgsConstructor public static class AuditCommon { + private int readCount; private int cudCount; private int printCount; @@ -68,6 +73,7 @@ public class AuditLogDto { @Schema(name = "DailyAuditList", description = "일자별 목록") @Getter public static class DailyAuditList extends AuditCommon { + private final String baseDate; public DailyAuditList( @@ -85,6 +91,7 @@ public class AuditLogDto { @Schema(name = "MenuAuditList", description = "메뉴별 목록") @Getter public static class MenuAuditList extends AuditCommon { + private final String menuId; private final String menuName; @@ -105,6 +112,7 @@ public class AuditLogDto { @Schema(name = "UserAuditList", description = "사용자별 목록") @Getter public static class UserAuditList extends AuditCommon { + private final Long accountId; private final String loginId; private final String username; @@ -129,6 +137,7 @@ public class AuditLogDto { @Getter @AllArgsConstructor public static class AuditDetail { + private Long logId; private EventType eventType; private LogDetail detail; @@ -137,6 +146,7 @@ public class AuditLogDto { @Schema(name = "DailyDetail", description = "일자별 로그 상세") @Getter public static class DailyDetail extends AuditDetail { + private final String userName; private final String loginId; private final String menuName; @@ -158,6 +168,7 @@ public class AuditLogDto { @Schema(name = "MenuDetail", description = "메뉴별 로그 상세") @Getter public static class MenuDetail extends AuditDetail { + private final String logDateTime; private final String userName; private final String loginId; @@ -179,6 +190,7 @@ public class AuditLogDto { @Schema(name = "UserDetail", description = "사용자별 로그 상세") @Getter public static class UserDetail extends AuditDetail { + private final String logDateTime; private final String menuNm; @@ -194,6 +206,7 @@ public class AuditLogDto { @Setter @AllArgsConstructor public static class LogDetail { + String serviceName; String parentMenuName; String menuName; @@ -226,4 +239,38 @@ public class AuditLogDto { return PageRequest.of(page, size); } } + + @Getter + @Setter + public static class DownloadReq { + + UUID uuid; + LocalDate startDate; + LocalDate endDate; + String type; + String searchValue; + String menuId; + String requestUri; + } + + @CodeExpose + @Getter + @AllArgsConstructor + public enum AuditType implements EnumType { + NAME("이름"), + EMPLOYEE_NO("사번"), + ; + + 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/AuditLogCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/AuditLogCoreService.java index 67a0a3b5..777e8ebb 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/AuditLogCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/AuditLogCoreService.java @@ -2,6 +2,7 @@ package com.kamco.cd.kamcoback.postgres.core; import com.kamco.cd.kamcoback.common.service.BaseCoreService; import com.kamco.cd.kamcoback.log.dto.AuditLogDto; +import com.kamco.cd.kamcoback.log.dto.AuditLogDto.DownloadReq; import com.kamco.cd.kamcoback.postgres.repository.log.AuditLogRepository; import java.time.LocalDate; import lombok.RequiredArgsConstructor; @@ -45,6 +46,11 @@ public class AuditLogCoreService return auditLogRepository.findLogByAccount(searchRange, searchValue); } + public Page findLogByAccount( + AuditLogDto.searchReq searchReq, DownloadReq downloadReq) { + return auditLogRepository.findDownloadLog(searchReq, downloadReq); + } + public Page getLogByDailyResult( AuditLogDto.searchReq searchRange, LocalDate logDate) { return auditLogRepository.findLogByDailyResult(searchRange, logDate); diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/log/AuditLogRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/log/AuditLogRepositoryCustom.java index 037e2789..5670eded 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/log/AuditLogRepositoryCustom.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/log/AuditLogRepositoryCustom.java @@ -1,6 +1,7 @@ package com.kamco.cd.kamcoback.postgres.repository.log; import com.kamco.cd.kamcoback.log.dto.AuditLogDto; +import com.kamco.cd.kamcoback.log.dto.AuditLogDto.DownloadReq; import java.time.LocalDate; import org.springframework.data.domain.Page; @@ -15,6 +16,8 @@ public interface AuditLogRepositoryCustom { Page findLogByAccount( AuditLogDto.searchReq searchReq, String searchValue); + Page findDownloadLog(AuditLogDto.searchReq searchReq, DownloadReq downloadReq); + Page findLogByDailyResult( AuditLogDto.searchReq searchReq, LocalDate logDate); diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/log/AuditLogRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/log/AuditLogRepositoryImpl.java index 45686d2c..f8c07ed2 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/log/AuditLogRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/log/AuditLogRepositoryImpl.java @@ -6,11 +6,13 @@ import static com.kamco.cd.kamcoback.postgres.entity.QMemberEntity.memberEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMenuEntity.menuEntity; import com.kamco.cd.kamcoback.log.dto.AuditLogDto; +import com.kamco.cd.kamcoback.log.dto.AuditLogDto.DownloadReq; import com.kamco.cd.kamcoback.log.dto.ErrorLogDto; import com.kamco.cd.kamcoback.log.dto.EventStatus; import com.kamco.cd.kamcoback.log.dto.EventType; import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity; import com.kamco.cd.kamcoback.postgres.entity.QMenuEntity; +import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.CaseBuilder; @@ -21,6 +23,7 @@ import com.querydsl.jpa.impl.JPAQueryFactory; import io.micrometer.common.util.StringUtils; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.List; import java.util.Objects; @@ -32,6 +35,7 @@ import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport public class AuditLogRepositoryImpl extends QuerydslRepositorySupport implements AuditLogRepositoryCustom { + private static final ZoneId ZONE = ZoneId.of("Asia/Seoul"); private final JPAQueryFactory queryFactory; private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)"); @@ -155,6 +159,66 @@ public class AuditLogRepositoryImpl extends QuerydslRepositorySupport return new PageImpl<>(foundContent, pageable, countQuery); } + @Override + public Page findDownloadLog(AuditLogDto.searchReq searchReq, DownloadReq req) { + Pageable pageable = searchReq.toPageable(); + + BooleanBuilder whereBuilder = new BooleanBuilder(); + + whereBuilder.and(auditLogEntity.eventStatus.ne(EventStatus.valueOf("FAILED"))); + + if (req.getMenuId() != null && !req.getMenuId().isEmpty()) { + whereBuilder.and(auditLogEntity.menuUid.eq(req.getMenuId())); + } + + if (req.getUuid() != null) { + whereBuilder.and(auditLogEntity.requestUri.endsWith(String.valueOf(req.getUuid()))); + } + + if (req.getType() != null && !req.getType().isEmpty()) { + if (req.getType().equals(AuditLogDto.AuditType.NAME.getId())) { + whereBuilder.and(memberEntity.name.contains(req.getSearchValue())); + } else if (req.getType().equals(AuditLogDto.AuditType.EMPLOYEE_NO.getId())) { + whereBuilder.and(memberEntity.employeeNo.contains(req.getSearchValue())); + } + } + + List foundContent = + queryFactory + .select( + Projections.constructor( + AuditLogDto.Basic.class, + auditLogEntity.id, + auditLogEntity.userUid, + auditLogEntity.eventType, + auditLogEntity.eventStatus, + auditLogEntity.menuUid, + auditLogEntity.ipAddress, + auditLogEntity.requestUri, + auditLogEntity.requestBody, + auditLogEntity.errorLogUid, + auditLogEntity.createdDate)) + .from(auditLogEntity) + .leftJoin(memberEntity) + .on(auditLogEntity.userUid.eq(memberEntity.id)) + .where(whereBuilder, createdDateBetween(req.getStartDate(), req.getEndDate())) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + // .orderBy(auditLogEntity.eventEndedAt.max().desc()) + .fetch(); + + Long countQuery = + queryFactory + .select(auditLogEntity.userUid.countDistinct()) + .from(auditLogEntity) + .leftJoin(memberEntity) + .on(auditLogEntity.userUid.eq(memberEntity.id)) + .where(whereBuilder, createdDateBetween(req.getStartDate(), req.getEndDate())) + .fetchOne(); + + return new PageImpl<>(foundContent, pageable, countQuery); + } + @Override public Page findLogByDailyResult( AuditLogDto.searchReq searchReq, LocalDate logDate) { @@ -365,6 +429,16 @@ public class AuditLogRepositoryImpl extends QuerydslRepositorySupport .and(auditLogEntity.createdDate.lt(ZonedDateTime.from(endDateTime))); } + private BooleanExpression createdDateBetween(LocalDate startDate, LocalDate endDate) { + if (startDate == null || endDate == null) { + return null; + } + ZonedDateTime start = startDate.atStartOfDay(ZONE); + ZonedDateTime endExclusive = endDate.plusDays(1).atStartOfDay(ZONE); + + return auditLogEntity.createdDate.goe(start).and(auditLogEntity.createdDate.lt(endExclusive)); + } + private BooleanExpression menuNameEquals(String searchValue) { if (StringUtils.isBlank(searchValue)) { return null;