[KC-116] shp 파일 다운로드 이력 추가

This commit is contained in:
2026-01-19 17:28:21 +09:00
parent 5a9ddc8c66
commit 098a21ae21
7 changed files with 188 additions and 3 deletions

View File

@@ -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.InferenceStatusDetailDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList;
import com.kamco.cd.kamcoback.inference.service.InferenceResultService; 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.dto.MapSheetMngDto.MngYyyyDto;
import com.kamco.cd.kamcoback.mapsheet.service.MapSheetMngService; import com.kamco.cd.kamcoback.mapsheet.service.MapSheetMngService;
import com.kamco.cd.kamcoback.model.dto.ModelMngDto; import com.kamco.cd.kamcoback.model.dto.ModelMngDto;
@@ -413,7 +416,7 @@ public class InferenceResultApiController {
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
}) })
@GetMapping(value = "/download/{uuid}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) @GetMapping(value = "/download/{uuid}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<Resource> downloadZip(@PathVariable UUID uuid) throws IOException { public ResponseEntity<Resource> downloadShp(@PathVariable UUID uuid) throws IOException {
String path; String path;
try { try {
@@ -441,4 +444,37 @@ public class InferenceResultApiController {
.contentLength(resource.contentLength()) .contentLength(resource.contentLength())
.body((Resource) resource); .body((Resource) resource);
} }
@GetMapping(value = "/download-audit/{uuid}")
public ApiResponseDto<Page<AuditLogDto.Basic>> 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));
}
} }

View File

@@ -23,7 +23,7 @@ import org.springframework.data.domain.Pageable;
public class InferenceResultDto { public class InferenceResultDto {
/** 탐지 데이터 옵션 dto */ /** 분석대상 도엽 enum */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum MapSheetScope implements EnumType { public enum MapSheetScope implements EnumType {
@@ -52,7 +52,7 @@ public class InferenceResultDto {
} }
} }
/** 분석대상 도엽 enum */ /** 탐지 데이터 옵션 dto */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum DetectOption implements EnumType { public enum DetectOption implements EnumType {

View File

@@ -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.InferenceResultDto.Status;
import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto; import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto.pred_requests_areas; 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.MngListCompareDto;
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListDto;
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.TotalListDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.TotalListDto;
import com.kamco.cd.kamcoback.model.dto.ModelMngDto.Basic; 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.InferenceResultCoreService;
import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService; import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService;
import com.kamco.cd.kamcoback.postgres.core.ModelMngCoreService; import com.kamco.cd.kamcoback.postgres.core.ModelMngCoreService;
@@ -62,6 +65,8 @@ public class InferenceResultService {
private final InferenceResultCoreService inferenceResultCoreService; private final InferenceResultCoreService inferenceResultCoreService;
private final MapSheetMngCoreService mapSheetMngCoreService; private final MapSheetMngCoreService mapSheetMngCoreService;
private final ModelMngCoreService modelMngCoreService; private final ModelMngCoreService modelMngCoreService;
private final AuditLogCoreService auditLogCoreService;
private final ExternalHttpClient externalHttpClient; private final ExternalHttpClient externalHttpClient;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
private final UserUtil userUtil; private final UserUtil userUtil;
@@ -554,4 +559,18 @@ public class InferenceResultService {
return Path.of(datasetDir).resolve(uid).resolve("merge").resolve(uid + ".zip"); return Path.of(datasetDir).resolve(uid).resolve("merge").resolve(uid + ".zip");
} }
/**
* 다운로드 이력 조회
*
* @param searchReq 페이징
* @param startDate 다운로드 시작일
* @param endDate 다운로드 종료일
* @param type 검색 구분
* @param searchValue 키워드
*/
public Page<AuditLogDto.Basic> getDownloadAudit(
AuditLogDto.searchReq searchReq, DownloadReq downloadReq) {
return auditLogCoreService.findLogByAccount(searchReq, downloadReq);
}
} }

View File

@@ -1,9 +1,13 @@
package com.kamco.cd.kamcoback.log.dto; package com.kamco.cd.kamcoback.log.dto;
import com.fasterxml.jackson.annotation.JsonIgnore; 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 com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@@ -58,6 +62,7 @@ public class AuditLogDto {
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public static class AuditCommon { public static class AuditCommon {
private int readCount; private int readCount;
private int cudCount; private int cudCount;
private int printCount; private int printCount;
@@ -68,6 +73,7 @@ public class AuditLogDto {
@Schema(name = "DailyAuditList", description = "일자별 목록") @Schema(name = "DailyAuditList", description = "일자별 목록")
@Getter @Getter
public static class DailyAuditList extends AuditCommon { public static class DailyAuditList extends AuditCommon {
private final String baseDate; private final String baseDate;
public DailyAuditList( public DailyAuditList(
@@ -85,6 +91,7 @@ public class AuditLogDto {
@Schema(name = "MenuAuditList", description = "메뉴별 목록") @Schema(name = "MenuAuditList", description = "메뉴별 목록")
@Getter @Getter
public static class MenuAuditList extends AuditCommon { public static class MenuAuditList extends AuditCommon {
private final String menuId; private final String menuId;
private final String menuName; private final String menuName;
@@ -105,6 +112,7 @@ public class AuditLogDto {
@Schema(name = "UserAuditList", description = "사용자별 목록") @Schema(name = "UserAuditList", description = "사용자별 목록")
@Getter @Getter
public static class UserAuditList extends AuditCommon { public static class UserAuditList extends AuditCommon {
private final Long accountId; private final Long accountId;
private final String loginId; private final String loginId;
private final String username; private final String username;
@@ -129,6 +137,7 @@ public class AuditLogDto {
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public static class AuditDetail { public static class AuditDetail {
private Long logId; private Long logId;
private EventType eventType; private EventType eventType;
private LogDetail detail; private LogDetail detail;
@@ -137,6 +146,7 @@ public class AuditLogDto {
@Schema(name = "DailyDetail", description = "일자별 로그 상세") @Schema(name = "DailyDetail", description = "일자별 로그 상세")
@Getter @Getter
public static class DailyDetail extends AuditDetail { public static class DailyDetail extends AuditDetail {
private final String userName; private final String userName;
private final String loginId; private final String loginId;
private final String menuName; private final String menuName;
@@ -158,6 +168,7 @@ public class AuditLogDto {
@Schema(name = "MenuDetail", description = "메뉴별 로그 상세") @Schema(name = "MenuDetail", description = "메뉴별 로그 상세")
@Getter @Getter
public static class MenuDetail extends AuditDetail { public static class MenuDetail extends AuditDetail {
private final String logDateTime; private final String logDateTime;
private final String userName; private final String userName;
private final String loginId; private final String loginId;
@@ -179,6 +190,7 @@ public class AuditLogDto {
@Schema(name = "UserDetail", description = "사용자별 로그 상세") @Schema(name = "UserDetail", description = "사용자별 로그 상세")
@Getter @Getter
public static class UserDetail extends AuditDetail { public static class UserDetail extends AuditDetail {
private final String logDateTime; private final String logDateTime;
private final String menuNm; private final String menuNm;
@@ -194,6 +206,7 @@ public class AuditLogDto {
@Setter @Setter
@AllArgsConstructor @AllArgsConstructor
public static class LogDetail { public static class LogDetail {
String serviceName; String serviceName;
String parentMenuName; String parentMenuName;
String menuName; String menuName;
@@ -226,4 +239,38 @@ public class AuditLogDto {
return PageRequest.of(page, size); 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;
}
}
} }

View File

@@ -2,6 +2,7 @@ package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.common.service.BaseCoreService; import com.kamco.cd.kamcoback.common.service.BaseCoreService;
import com.kamco.cd.kamcoback.log.dto.AuditLogDto; 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 com.kamco.cd.kamcoback.postgres.repository.log.AuditLogRepository;
import java.time.LocalDate; import java.time.LocalDate;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -45,6 +46,11 @@ public class AuditLogCoreService
return auditLogRepository.findLogByAccount(searchRange, searchValue); return auditLogRepository.findLogByAccount(searchRange, searchValue);
} }
public Page<AuditLogDto.Basic> findLogByAccount(
AuditLogDto.searchReq searchReq, DownloadReq downloadReq) {
return auditLogRepository.findDownloadLog(searchReq, downloadReq);
}
public Page<AuditLogDto.DailyDetail> getLogByDailyResult( public Page<AuditLogDto.DailyDetail> getLogByDailyResult(
AuditLogDto.searchReq searchRange, LocalDate logDate) { AuditLogDto.searchReq searchRange, LocalDate logDate) {
return auditLogRepository.findLogByDailyResult(searchRange, logDate); return auditLogRepository.findLogByDailyResult(searchRange, logDate);

View File

@@ -1,6 +1,7 @@
package com.kamco.cd.kamcoback.postgres.repository.log; package com.kamco.cd.kamcoback.postgres.repository.log;
import com.kamco.cd.kamcoback.log.dto.AuditLogDto; import com.kamco.cd.kamcoback.log.dto.AuditLogDto;
import com.kamco.cd.kamcoback.log.dto.AuditLogDto.DownloadReq;
import java.time.LocalDate; import java.time.LocalDate;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -15,6 +16,8 @@ public interface AuditLogRepositoryCustom {
Page<AuditLogDto.UserAuditList> findLogByAccount( Page<AuditLogDto.UserAuditList> findLogByAccount(
AuditLogDto.searchReq searchReq, String searchValue); AuditLogDto.searchReq searchReq, String searchValue);
Page<AuditLogDto.Basic> findDownloadLog(AuditLogDto.searchReq searchReq, DownloadReq downloadReq);
Page<AuditLogDto.DailyDetail> findLogByDailyResult( Page<AuditLogDto.DailyDetail> findLogByDailyResult(
AuditLogDto.searchReq searchReq, LocalDate logDate); AuditLogDto.searchReq searchReq, LocalDate logDate);

View File

@@ -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 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;
import com.kamco.cd.kamcoback.log.dto.AuditLogDto.DownloadReq;
import com.kamco.cd.kamcoback.log.dto.ErrorLogDto; import com.kamco.cd.kamcoback.log.dto.ErrorLogDto;
import com.kamco.cd.kamcoback.log.dto.EventStatus; import com.kamco.cd.kamcoback.log.dto.EventStatus;
import com.kamco.cd.kamcoback.log.dto.EventType; import com.kamco.cd.kamcoback.log.dto.EventType;
import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity; import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMenuEntity; import com.kamco.cd.kamcoback.postgres.entity.QMenuEntity;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Projections; import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.CaseBuilder; import com.querydsl.core.types.dsl.CaseBuilder;
@@ -21,6 +23,7 @@ import com.querydsl.jpa.impl.JPAQueryFactory;
import io.micrometer.common.util.StringUtils; import io.micrometer.common.util.StringUtils;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -32,6 +35,7 @@ import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport
public class AuditLogRepositoryImpl extends QuerydslRepositorySupport public class AuditLogRepositoryImpl extends QuerydslRepositorySupport
implements AuditLogRepositoryCustom { implements AuditLogRepositoryCustom {
private static final ZoneId ZONE = ZoneId.of("Asia/Seoul");
private final JPAQueryFactory queryFactory; private final JPAQueryFactory queryFactory;
private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)"); 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); return new PageImpl<>(foundContent, pageable, countQuery);
} }
@Override
public Page<AuditLogDto.Basic> 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<AuditLogDto.Basic> 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 @Override
public Page<AuditLogDto.DailyDetail> findLogByDailyResult( public Page<AuditLogDto.DailyDetail> findLogByDailyResult(
AuditLogDto.searchReq searchReq, LocalDate logDate) { AuditLogDto.searchReq searchReq, LocalDate logDate) {
@@ -365,6 +429,16 @@ public class AuditLogRepositoryImpl extends QuerydslRepositorySupport
.and(auditLogEntity.createdDate.lt(ZonedDateTime.from(endDateTime))); .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) { private BooleanExpression menuNameEquals(String searchValue) {
if (StringUtils.isBlank(searchValue)) { if (StringUtils.isBlank(searchValue)) {
return null; return null;