로그관리 로직 커밋
This commit is contained in:
@@ -2,6 +2,7 @@ package com.kamco.cd.training.postgres.core;
|
||||
|
||||
import com.kamco.cd.training.common.service.BaseCoreService;
|
||||
import com.kamco.cd.training.log.dto.AuditLogDto;
|
||||
import com.kamco.cd.training.log.dto.AuditLogDto.DownloadReq;
|
||||
import com.kamco.cd.training.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<AuditLogDto.DownloadRes> findLogByAccount(
|
||||
AuditLogDto.searchReq searchReq, DownloadReq downloadReq) {
|
||||
return auditLogRepository.findDownloadLog(searchReq, downloadReq);
|
||||
}
|
||||
|
||||
public Page<AuditLogDto.DailyDetail> getLogByDailyResult(
|
||||
AuditLogDto.searchReq searchRange, LocalDate logDate) {
|
||||
return auditLogRepository.findLogByDailyResult(searchRange, logDate);
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.kamco.cd.training.log.dto.EventStatus;
|
||||
import com.kamco.cd.training.log.dto.EventType;
|
||||
import com.kamco.cd.training.postgres.CommonCreateEntity;
|
||||
import jakarta.persistence.*;
|
||||
import java.util.UUID;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -14,6 +15,7 @@ import lombok.NoArgsConstructor;
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Table(name = "tb_audit_log")
|
||||
public class AuditLogEntity extends CommonCreateEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "audit_log_uid", nullable = false)
|
||||
@@ -43,6 +45,12 @@ public class AuditLogEntity extends CommonCreateEntity {
|
||||
@Column(name = "error_log_uid")
|
||||
private Long errorLogUid;
|
||||
|
||||
@Column(name = "download_uuid")
|
||||
private UUID downloadUuid;
|
||||
|
||||
@Column(name = "login_attempt_id")
|
||||
private String loginAttemptId;
|
||||
|
||||
public AuditLogEntity(
|
||||
Long userUid,
|
||||
EventType eventType,
|
||||
@@ -51,7 +59,9 @@ public class AuditLogEntity extends CommonCreateEntity {
|
||||
String ipAddress,
|
||||
String requestUri,
|
||||
String requestBody,
|
||||
Long errorLogUid) {
|
||||
Long errorLogUid,
|
||||
UUID downloadUuid,
|
||||
String loginAttemptId) {
|
||||
this.userUid = userUid;
|
||||
this.eventType = eventType;
|
||||
this.eventStatus = eventStatus;
|
||||
@@ -60,6 +70,31 @@ public class AuditLogEntity extends CommonCreateEntity {
|
||||
this.requestUri = requestUri;
|
||||
this.requestBody = requestBody;
|
||||
this.errorLogUid = errorLogUid;
|
||||
this.downloadUuid = downloadUuid;
|
||||
this.loginAttemptId = loginAttemptId;
|
||||
}
|
||||
|
||||
/** 파일 다운로드 이력 생성 */
|
||||
public static AuditLogEntity forFileDownload(
|
||||
Long userId,
|
||||
String requestUri,
|
||||
String menuUid,
|
||||
String ip,
|
||||
int httpStatus,
|
||||
UUID downloadUuid) {
|
||||
|
||||
return new AuditLogEntity(
|
||||
userId,
|
||||
EventType.DOWNLOAD, // 이벤트 타입 고정
|
||||
httpStatus < 400 ? EventStatus.SUCCESS : EventStatus.FAILED, // 성공 여부
|
||||
menuUid,
|
||||
ip,
|
||||
requestUri,
|
||||
null, // requestBody 없음
|
||||
null, // errorLogUid 없음
|
||||
downloadUuid,
|
||||
null // loginAttemptId 없음
|
||||
);
|
||||
}
|
||||
|
||||
public AuditLogDto.Basic toDto() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.kamco.cd.training.postgres.repository.log;
|
||||
|
||||
import com.kamco.cd.training.log.dto.AuditLogDto;
|
||||
import com.kamco.cd.training.log.dto.AuditLogDto.DownloadReq;
|
||||
import java.time.LocalDate;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
@@ -15,6 +16,9 @@ public interface AuditLogRepositoryCustom {
|
||||
Page<AuditLogDto.UserAuditList> findLogByAccount(
|
||||
AuditLogDto.searchReq searchReq, String searchValue);
|
||||
|
||||
Page<AuditLogDto.DownloadRes> findDownloadLog(
|
||||
AuditLogDto.searchReq searchReq, DownloadReq downloadReq);
|
||||
|
||||
Page<AuditLogDto.DailyDetail> findLogByDailyResult(
|
||||
AuditLogDto.searchReq searchReq, LocalDate logDate);
|
||||
|
||||
|
||||
@@ -6,32 +6,42 @@ import static com.kamco.cd.training.postgres.entity.QMemberEntity.memberEntity;
|
||||
import static com.kamco.cd.training.postgres.entity.QMenuEntity.menuEntity;
|
||||
|
||||
import com.kamco.cd.training.log.dto.AuditLogDto;
|
||||
import com.kamco.cd.training.log.dto.AuditLogDto.DownloadReq;
|
||||
import com.kamco.cd.training.log.dto.AuditLogDto.searchReq;
|
||||
import com.kamco.cd.training.log.dto.ErrorLogDto;
|
||||
import com.kamco.cd.training.log.dto.EventStatus;
|
||||
import com.kamco.cd.training.log.dto.EventType;
|
||||
import com.kamco.cd.training.postgres.entity.AuditLogEntity;
|
||||
import com.kamco.cd.training.postgres.entity.QMenuEntity;
|
||||
import com.querydsl.core.BooleanBuilder;
|
||||
import com.querydsl.core.types.Projections;
|
||||
import com.querydsl.core.types.dsl.*;
|
||||
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;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class AuditLogRepositoryImpl implements AuditLogRepositoryCustom {
|
||||
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)");
|
||||
|
||||
public AuditLogRepositoryImpl(JPAQueryFactory queryFactory) {
|
||||
super(AuditLogEntity.class);
|
||||
this.queryFactory = queryFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<AuditLogDto.DailyAuditList> findLogByDaily(
|
||||
AuditLogDto.searchReq searchReq, LocalDate startDate, LocalDate endDate) {
|
||||
@@ -87,7 +97,7 @@ public class AuditLogRepositoryImpl implements AuditLogRepositoryCustom {
|
||||
.from(auditLogEntity)
|
||||
.leftJoin(menuEntity)
|
||||
.on(auditLogEntity.menuUid.eq(menuEntity.menuUid))
|
||||
.where(menuNameEquals(searchValue))
|
||||
.where(auditLogEntity.menuUid.ne("SYSTEM"), menuNameEquals(searchValue))
|
||||
.groupBy(auditLogEntity.menuUid)
|
||||
.offset(pageable.getOffset())
|
||||
.limit(pageable.getPageSize())
|
||||
@@ -128,7 +138,7 @@ public class AuditLogRepositoryImpl implements AuditLogRepositoryCustom {
|
||||
.from(auditLogEntity)
|
||||
.leftJoin(memberEntity)
|
||||
.on(auditLogEntity.userUid.eq(memberEntity.id))
|
||||
.where(loginIdOrUsernameContains(searchValue))
|
||||
.where(auditLogEntity.userUid.isNotNull(), loginIdOrUsernameContains(searchValue))
|
||||
.groupBy(auditLogEntity.userUid, memberEntity.employeeNo, memberEntity.name)
|
||||
.offset(pageable.getOffset())
|
||||
.limit(pageable.getPageSize())
|
||||
@@ -147,6 +157,62 @@ public class AuditLogRepositoryImpl implements AuditLogRepositoryCustom {
|
||||
return new PageImpl<>(foundContent, pageable, countQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<AuditLogDto.DownloadRes> findDownloadLog(
|
||||
AuditLogDto.searchReq searchReq, DownloadReq req) {
|
||||
Pageable pageable = searchReq.toPageable();
|
||||
|
||||
BooleanBuilder whereBuilder = new BooleanBuilder();
|
||||
|
||||
whereBuilder.and(auditLogEntity.eventStatus.ne(EventStatus.valueOf("FAILED")));
|
||||
whereBuilder.and(auditLogEntity.eventType.eq(EventType.valueOf("DOWNLOAD")));
|
||||
|
||||
// if (req.getMenuId() != null && !req.getMenuId().isEmpty()) {
|
||||
// whereBuilder.and(auditLogEntity.menuUid.eq(req.getMenuId()));
|
||||
// }
|
||||
|
||||
if (req.getUuid() != null) {
|
||||
whereBuilder.and(auditLogEntity.requestUri.contains(req.getRequestUri()));
|
||||
whereBuilder.and(auditLogEntity.downloadUuid.eq(req.getUuid()));
|
||||
}
|
||||
|
||||
if (req.getSearchValue() != null && !req.getSearchValue().isEmpty()) {
|
||||
whereBuilder.and(
|
||||
memberEntity
|
||||
.name
|
||||
.contains(req.getSearchValue())
|
||||
.or(memberEntity.employeeNo.contains(req.getSearchValue())));
|
||||
}
|
||||
|
||||
List<AuditLogDto.DownloadRes> foundContent =
|
||||
queryFactory
|
||||
.select(
|
||||
Projections.constructor(
|
||||
AuditLogDto.DownloadRes.class,
|
||||
memberEntity.name,
|
||||
memberEntity.employeeNo,
|
||||
auditLogEntity.createdDate.as("downloadDttm")))
|
||||
.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.createdDate.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<AuditLogDto.DailyDetail> findLogByDailyResult(
|
||||
AuditLogDto.searchReq searchReq, LocalDate logDate) {
|
||||
@@ -176,6 +242,9 @@ public class AuditLogRepositoryImpl implements AuditLogRepositoryCustom {
|
||||
memberEntity.employeeNo.as("loginId"),
|
||||
menuEntity.menuNm.as("menuName"),
|
||||
auditLogEntity.eventType.as("eventType"),
|
||||
Expressions.stringTemplate(
|
||||
"to_char({0}, 'YYYY-MM-DD HH24:MI')", auditLogEntity.createdDate)
|
||||
.as("logDateTime"),
|
||||
Projections.constructor(
|
||||
AuditLogDto.LogDetail.class,
|
||||
Expressions.constant("한국자산관리공사"), // serviceName
|
||||
@@ -184,7 +253,7 @@ public class AuditLogRepositoryImpl implements AuditLogRepositoryCustom {
|
||||
menuEntity.menuUrl.as("menuUrl"),
|
||||
menuEntity.description.as("menuDescription"),
|
||||
menuEntity.menuOrder.as("sortOrder"),
|
||||
menuEntity.isUse.as("used"))))
|
||||
menuEntity.isUse.as("used")))) // TODO
|
||||
.from(auditLogEntity)
|
||||
.leftJoin(menuEntity)
|
||||
.on(auditLogEntity.menuUid.eq(menuEntity.menuUid))
|
||||
@@ -238,8 +307,8 @@ public class AuditLogRepositoryImpl implements AuditLogRepositoryCustom {
|
||||
AuditLogDto.MenuDetail.class,
|
||||
auditLogEntity.id.as("logId"),
|
||||
Expressions.stringTemplate(
|
||||
"to_char({0}, 'YYYY-MM-DD')", auditLogEntity.createdDate)
|
||||
.as("logDateTime"), // ??
|
||||
"to_char({0}, 'YYYY-MM-DD HH24:MI')", auditLogEntity.createdDate)
|
||||
.as("logDateTime"),
|
||||
memberEntity.name.as("userName"),
|
||||
memberEntity.employeeNo.as("loginId"),
|
||||
auditLogEntity.eventType.as("eventType"),
|
||||
@@ -305,7 +374,7 @@ public class AuditLogRepositoryImpl implements AuditLogRepositoryCustom {
|
||||
AuditLogDto.UserDetail.class,
|
||||
auditLogEntity.id.as("logId"),
|
||||
Expressions.stringTemplate(
|
||||
"to_char({0}, 'YYYY-MM-DD')", auditLogEntity.createdDate)
|
||||
"to_char({0}, 'YYYY-MM-DD HH24:MI')", auditLogEntity.createdDate)
|
||||
.as("logDateTime"),
|
||||
menuEntity.menuNm.as("menuName"),
|
||||
auditLogEntity.eventType.as("eventType"),
|
||||
@@ -349,12 +418,23 @@ public class AuditLogRepositoryImpl implements AuditLogRepositoryCustom {
|
||||
if (Objects.isNull(startDate) || Objects.isNull(endDate)) {
|
||||
return null;
|
||||
}
|
||||
LocalDateTime startDateTime = startDate.atStartOfDay();
|
||||
LocalDateTime endDateTime = endDate.plusDays(1).atStartOfDay();
|
||||
ZoneId zoneId = ZoneId.of("Asia/Seoul");
|
||||
ZonedDateTime startDateTime = startDate.atStartOfDay(zoneId);
|
||||
ZonedDateTime endDateTime = endDate.plusDays(1).atStartOfDay(zoneId);
|
||||
return auditLogEntity
|
||||
.createdDate
|
||||
.goe(ZonedDateTime.from(startDateTime))
|
||||
.and(auditLogEntity.createdDate.lt(ZonedDateTime.from(endDateTime)));
|
||||
.goe(startDateTime)
|
||||
.and(auditLogEntity.createdDate.lt(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) {
|
||||
@@ -393,11 +473,11 @@ public class AuditLogRepositoryImpl implements AuditLogRepositoryCustom {
|
||||
}
|
||||
|
||||
private BooleanExpression eventEndedAtEqDate(LocalDate logDate) {
|
||||
StringExpression eventEndedDate =
|
||||
Expressions.stringTemplate("to_char({0}, 'YYYY-MM-DD')", auditLogEntity.createdDate);
|
||||
LocalDateTime comparisonDate = logDate.atStartOfDay();
|
||||
ZoneId zoneId = ZoneId.of("Asia/Seoul");
|
||||
ZonedDateTime start = logDate.atStartOfDay(zoneId);
|
||||
ZonedDateTime end = logDate.plusDays(1).atStartOfDay(zoneId);
|
||||
|
||||
return eventEndedDate.eq(comparisonDate.toString());
|
||||
return auditLogEntity.createdDate.goe(start).and(auditLogEntity.createdDate.lt(end));
|
||||
}
|
||||
|
||||
private BooleanExpression menuUidEq(String menuUid) {
|
||||
@@ -410,7 +490,7 @@ public class AuditLogRepositoryImpl implements AuditLogRepositoryCustom {
|
||||
|
||||
private NumberExpression<Integer> readCount() {
|
||||
return new CaseBuilder()
|
||||
.when(auditLogEntity.eventType.eq(EventType.READ))
|
||||
.when(auditLogEntity.eventType.in(EventType.LIST, EventType.DETAIL))
|
||||
.then(1)
|
||||
.otherwise(0)
|
||||
.sum();
|
||||
@@ -418,7 +498,7 @@ public class AuditLogRepositoryImpl implements AuditLogRepositoryCustom {
|
||||
|
||||
private NumberExpression<Integer> cudCount() {
|
||||
return new CaseBuilder()
|
||||
.when(auditLogEntity.eventType.in(EventType.CREATE, EventType.UPDATE, EventType.DELETE))
|
||||
.when(auditLogEntity.eventType.in(EventType.ADDED, EventType.MODIFIED, EventType.REMOVE))
|
||||
.then(1)
|
||||
.otherwise(0)
|
||||
.sum();
|
||||
@@ -426,7 +506,7 @@ public class AuditLogRepositoryImpl implements AuditLogRepositoryCustom {
|
||||
|
||||
private NumberExpression<Integer> printCount() {
|
||||
return new CaseBuilder()
|
||||
.when(auditLogEntity.eventType.eq(EventType.PRINT))
|
||||
.when(auditLogEntity.eventType.eq(EventType.OTHER))
|
||||
.then(1)
|
||||
.otherwise(0)
|
||||
.sum();
|
||||
|
||||
@@ -8,29 +8,35 @@ import static com.kamco.cd.training.postgres.entity.QMenuEntity.menuEntity;
|
||||
import com.kamco.cd.training.log.dto.ErrorLogDto;
|
||||
import com.kamco.cd.training.log.dto.EventStatus;
|
||||
import com.kamco.cd.training.log.dto.EventType;
|
||||
import com.kamco.cd.training.postgres.entity.AuditLogEntity;
|
||||
import com.querydsl.core.types.Projections;
|
||||
import com.querydsl.core.types.dsl.BooleanExpression;
|
||||
import com.querydsl.core.types.dsl.Expressions;
|
||||
import com.querydsl.core.types.dsl.StringExpression;
|
||||
import com.querydsl.jpa.impl.JPAQueryFactory;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class ErrorLogRepositoryImpl implements ErrorLogRepositoryCustom {
|
||||
public class ErrorLogRepositoryImpl extends QuerydslRepositorySupport
|
||||
implements ErrorLogRepositoryCustom {
|
||||
|
||||
private final JPAQueryFactory queryFactory;
|
||||
private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)");
|
||||
|
||||
public ErrorLogRepositoryImpl(JPAQueryFactory queryFactory) {
|
||||
super(AuditLogEntity.class);
|
||||
this.queryFactory = queryFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ErrorLogDto.Basic> findLogByError(ErrorLogDto.ErrorSearchReq searchReq) {
|
||||
Pageable pageable = searchReq.toPageable();
|
||||
@@ -52,7 +58,7 @@ public class ErrorLogRepositoryImpl implements ErrorLogRepositoryCustom {
|
||||
errorLogEntity.errorMessage.as("errorMessage"),
|
||||
errorLogEntity.stackTrace.as("errorDetail"),
|
||||
Expressions.stringTemplate(
|
||||
"to_char({0}, 'YYYY-MM-DD')", errorLogEntity.createdDate)))
|
||||
"to_char({0}, 'YYYY-MM-DD HH24:MI:SS.FF3')", errorLogEntity.createdDate)))
|
||||
.from(errorLogEntity)
|
||||
.leftJoin(auditLogEntity)
|
||||
.on(errorLogEntity.id.eq(auditLogEntity.errorLogUid))
|
||||
@@ -94,12 +100,14 @@ public class ErrorLogRepositoryImpl implements ErrorLogRepositoryCustom {
|
||||
if (Objects.isNull(startDate) || Objects.isNull(endDate)) {
|
||||
return null;
|
||||
}
|
||||
LocalDateTime startDateTime = startDate.atStartOfDay();
|
||||
LocalDateTime endDateTime = endDate.plusDays(1).atStartOfDay();
|
||||
|
||||
ZoneId zoneId = ZoneId.of("Asia/Seoul");
|
||||
ZonedDateTime startDateTime = startDate.atStartOfDay(zoneId);
|
||||
ZonedDateTime endDateTime = endDate.plusDays(1).atStartOfDay(zoneId);
|
||||
return auditLogEntity
|
||||
.createdDate
|
||||
.goe(ZonedDateTime.from(startDateTime))
|
||||
.and(auditLogEntity.createdDate.lt(ZonedDateTime.from(endDateTime)));
|
||||
.goe(startDateTime)
|
||||
.and(auditLogEntity.createdDate.lt(endDateTime));
|
||||
}
|
||||
|
||||
private BooleanExpression eventStatusEqFailed() {
|
||||
|
||||
Reference in New Issue
Block a user