API 로그저장, ExceptionHandler 저장, 감사,에러로그API 작업 진행중

This commit is contained in:
2025-11-20 14:36:59 +09:00
parent aaabd85c9c
commit 107bd6b20f
23 changed files with 1791 additions and 8 deletions

View File

@@ -0,0 +1,58 @@
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.postgres.repository.log.AuditLogRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class AuditLogCoreService implements BaseCoreService<AuditLogDto.AuditList, Long, AuditLogDto.DailySearchReq> {
private final AuditLogRepository auditLogRepository;
@Override
public void remove(Long aLong) {
}
@Override
public AuditLogDto.AuditList getOneById(Long aLong) {
return null;
}
@Override
public Page<AuditLogDto.AuditList> search(AuditLogDto.DailySearchReq searchReq) {
return null;
}
public Page<AuditLogDto.AuditList> getLogByDaily(AuditLogDto.DailySearchReq searchRange, LocalDate startDate, LocalDate endDate) {
return auditLogRepository.findLogByDaily(searchRange, startDate, endDate);
}
public Page<AuditLogDto.AuditList> getLogByMenu(AuditLogDto.MenuUserSearchReq searchRange, String searchValue) {
return auditLogRepository.findLogByMenu(searchRange, searchValue);
}
public Page<AuditLogDto.AuditList> getLogByAccount(AuditLogDto.MenuUserSearchReq searchRange, String searchValue) {
return auditLogRepository.findLogByAccount(searchRange, searchValue);
}
public Page<AuditLogDto.AuditDetail> getLogByDailyResult(AuditLogDto.DailySearchReq searchRange, LocalDate logDate) {
return auditLogRepository.findLogByDailyResult(searchRange, logDate);
}
public Page<AuditLogDto.AuditDetail> getLogByMenuResult(AuditLogDto.MenuUserSearchReq searchRange, String menuId) {
return auditLogRepository.findLogByMenuResult(searchRange, menuId);
}
public Page<AuditLogDto.AuditDetail> getLogByAccountResult(AuditLogDto.MenuUserSearchReq searchRange, Long accountId) {
return auditLogRepository.findLogByAccountResult(searchRange, accountId);
}
}

View File

@@ -0,0 +1,38 @@
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.ErrorLogDto;
import com.kamco.cd.kamcoback.postgres.repository.log.AuditLogRepository;
import com.kamco.cd.kamcoback.postgres.repository.log.ErrorLogRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ErrorLogCoreService implements BaseCoreService<ErrorLogDto.Basic, Long, ErrorLogDto.ErrorSearchReq> {
private final ErrorLogRepository errorLogRepository;
public Page<ErrorLogDto.Basic> findLogByError(ErrorLogDto.ErrorSearchReq searchReq) {
return errorLogRepository.findLogByError(searchReq);
}
@Override
public void remove(Long aLong) {}
@Override
public ErrorLogDto.Basic getOneById(Long aLong) {
return null;
}
@Override
public Page<ErrorLogDto.Basic> search(ErrorLogDto.ErrorSearchReq searchReq) {
return null;
}
}

View File

@@ -0,0 +1,85 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.log.dto.AuditLogDto;
import com.kamco.cd.kamcoback.log.dto.EventStatus;
import com.kamco.cd.kamcoback.log.dto.EventType;
import com.kamco.cd.kamcoback.postgres.CommonCreateEntity;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "tb_audit_log")
public class AuditLogEntity extends CommonCreateEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "audit_log_uid")
private Long id;
@Column(name = "user_uid")
private Long userUid;
@Enumerated(EnumType.STRING)
private EventType eventType;
@Enumerated(EnumType.STRING)
private EventStatus eventStatus;
@Column(name = "menu_uid")
private String menuUid;
@Column(name = "ip_address")
private String ipAddress;
@Column(name = "request_uri")
private String requestUri;
@Column(name = "request_body")
private String requestBody;
@Column(name = "error_log_uid")
private Long errorLogUid;
public AuditLogEntity(Long userUid, EventType eventType, EventStatus eventStatus, String menuUid, String ipAddress, String requestUri, String requestBody, Long errorLogUid) {
this.userUid = userUid;
this.eventType = eventType;
this.eventStatus = eventStatus;
this.menuUid = menuUid;
this.ipAddress = ipAddress;
this.requestUri = requestUri;
this.requestBody = requestBody;
this.errorLogUid = errorLogUid;
}
public AuditLogDto.Basic toDto() {
return new AuditLogDto.Basic(
this.id,
this.userUid,
this.eventType,
this.eventStatus,
this.menuUid,
this.ipAddress,
this.requestUri,
this.requestBody,
this.errorLogUid,
super.getCreatedDate());
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append(this.id).append("\n")
.append(this.userUid).append("\n")
.append(this.eventType).append("\n")
.append(this.eventStatus).append("\n")
.append(this.menuUid).append("\n")
.append(this.ipAddress).append("\n")
.append(this.requestUri).append("\n")
.append(this.requestBody).append("\n")
.append(this.errorLogUid);
return sb.toString();
}
}

View File

@@ -0,0 +1,50 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.log.dto.ErrorLogDto;
import com.kamco.cd.kamcoback.log.dto.EventType;
import com.kamco.cd.kamcoback.postgres.CommonCreateEntity;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "tb_error_log")
public class ErrorLogEntity extends CommonCreateEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "error_log_uid")
private Long id;
@Column(name = "request_id")
private String requestId;
@Column(name = "error_type")
@Enumerated(EnumType.STRING)
private EventType errorType;
@Enumerated(EnumType.STRING)
private ErrorLogDto.LogErrorLevel errorLevel;
private String errorCode;
private String errorMessage;
private String stackTrace;
private Long handlerUid;
private ZonedDateTime handledDttm;
public ErrorLogEntity(String requestId, EventType errorType, ErrorLogDto.LogErrorLevel errorLevel, String errorCode, String errorMessage, String stackTrace
, Long handlerUid, ZonedDateTime handledDttm) {
this.requestId = requestId;
this.errorType = errorType;
this.errorLevel = errorLevel;
this.errorCode = errorCode;
this.errorMessage = errorMessage;
this.stackTrace = stackTrace;
this.handlerUid = handlerUid;
this.handledDttm = handledDttm;
}
}

View File

@@ -0,0 +1,53 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.log.dto.EventStatus;
import com.kamco.cd.kamcoback.log.dto.EventType;
import com.kamco.cd.kamcoback.postgres.CommonDateEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "tb_menu")
public class MenuEntity extends CommonDateEntity {
@Id
@Column(name = "menu_uid")
private String menuUid;
@Column(name = "menu_nm")
private String menuNm;
@Column(name = "menu_url")
private String menuUrl;
@Column(name = "description")
private String description;
@Column(name = "menu_order")
private Long menuOrder;
@NotNull
@Column(name = "is_use", nullable = true)
private Boolean isUse = true;
@NotNull
@Column(name = "deleted", nullable = false)
private Boolean deleted = false;
private Long createdUid;
private Long updatedUid;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_menu_uid")
private MenuEntity parent;
@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<MenuEntity> children = new ArrayList<>();
}

View File

@@ -0,0 +1,41 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.log.dto.ErrorLogDto;
import com.kamco.cd.kamcoback.log.dto.EventType;
import com.kamco.cd.kamcoback.postgres.CommonDateEntity;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.time.ZonedDateTime;
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "tb_user")
public class UserEntity extends CommonDateEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_uid")
private Long id;
@Column(name = "user_nm")
private String userNm;
@Column(name = "user_id")
private String userId;
@Column(name = "pswd")
private String pswd; //TODO: 암호화
//@Enumerated(EnumType.STRING)
private String state; //TODO: 추후 enum -> ACTIVE : 정상, LOCKED : 잠김, EXPIRED : 만료, WITHDRAWAL : 탈퇴
private ZonedDateTime dateWithdrawal;
private String userEmail;
private Long createdUid;
private Long updatedUid;
}

View File

@@ -0,0 +1,6 @@
package com.kamco.cd.kamcoback.postgres.repository.log;
import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AuditLogRepository extends JpaRepository<AuditLogEntity, Long>, AuditLogRepositoryCustom {}

View File

@@ -0,0 +1,21 @@
package com.kamco.cd.kamcoback.postgres.repository.log;
import com.kamco.cd.kamcoback.log.dto.AuditLogDto;
import org.springframework.data.domain.Page;
import java.time.LocalDate;
public interface AuditLogRepositoryCustom {
Page<AuditLogDto.AuditList> findLogByDaily(AuditLogDto.DailySearchReq searchReq, LocalDate startDate, LocalDate endDate);
Page<AuditLogDto.AuditList> findLogByMenu(AuditLogDto.MenuUserSearchReq searchReq, String searchValue);
Page<AuditLogDto.AuditList> findLogByAccount(AuditLogDto.MenuUserSearchReq searchReq, String searchValue);
Page<AuditLogDto.AuditDetail> findLogByDailyResult(AuditLogDto.DailySearchReq searchReq, LocalDate logDate);
Page<AuditLogDto.AuditDetail> findLogByMenuResult(AuditLogDto.MenuUserSearchReq searchReq, String menuId);
Page<AuditLogDto.AuditDetail> findLogByAccountResult(AuditLogDto.MenuUserSearchReq searchReq, Long accountId);
}

View File

@@ -0,0 +1,427 @@
package com.kamco.cd.kamcoback.postgres.repository.log;
import com.kamco.cd.kamcoback.log.dto.AuditLogDto;
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.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.*;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import io.micrometer.common.util.StringUtils;
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 java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Objects;
import static com.kamco.cd.kamcoback.postgres.entity.QUserEntity.userEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QAuditLogEntity.auditLogEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QErrorLogEntity.errorLogEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMenuEntity.menuEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMenuEntity;
public class AuditLogRepositoryImpl extends QuerydslRepositorySupport implements AuditLogRepositoryCustom {
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.AuditList> findLogByDaily(AuditLogDto.DailySearchReq searchReq, LocalDate startDate, LocalDate endDate) {
DateTimeExpression<LocalDateTime> groupDateTime =
Expressions.dateTimeTemplate(LocalDateTime.class, "date_trunc('day', {0})", auditLogEntity.createdDate);
Pageable pageable = searchReq.toPageable();
List<AuditLogDto.AuditList> foundContent = queryFactory
.select(
Projections.constructor(
AuditLogDto.AuditList.class,
groupDateTime.as("baseDate"),
readCount().as("readCount"),
cudCount().as("cudCount"),
printCount().as("printCount"),
downloadCount().as("downloadCount"),
auditLogEntity.count().as("totalCount")
)
)
.from(auditLogEntity)
.where(
eventEndedAtBetween(startDate, endDate)
)
.groupBy(groupDateTime)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(groupDateTime.desc())
.fetch();
Long countQuery = queryFactory
.select(groupDateTime.countDistinct())
.from(auditLogEntity)
.where(
eventEndedAtBetween(startDate, endDate)
)
.fetchOne();
return new PageImpl<>(foundContent, pageable, countQuery);
}
@Override
public Page<AuditLogDto.AuditList> findLogByMenu(AuditLogDto.MenuUserSearchReq searchReq, String searchValue) {
Pageable pageable = searchReq.toPageable();
List<AuditLogDto.AuditList> foundContent = queryFactory
.select(
Projections.constructor(
AuditLogDto.AuditList.class,
auditLogEntity.menuUid.as("menuId"),
menuEntity.menuNm.max().as("menuName"),
readCount().as("readCount"),
cudCount().as("cudCount"),
printCount().as("printCount"),
downloadCount().as("downloadCount"),
auditLogEntity.count().as("totalCount")
)
)
.from(auditLogEntity)
.leftJoin(menuEntity).on(auditLogEntity.menuUid.eq(menuEntity.menuUid))
.where(
menuNameEquals(searchValue)
)
.groupBy(auditLogEntity.menuUid)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(auditLogEntity.createdDate.max().desc())
.fetch();
// count query group by 를 지정하면 하나의 row 가 아니라 그룹핑된 여러 row 가 나올 수 있다.
// select query 의 group by 대상의 컬럼을 count query 에선 select distinct 로 처리 한다.
Long countQuery = queryFactory
.select(auditLogEntity.menuUid.countDistinct())
.from(auditLogEntity)
.leftJoin(menuEntity).on(auditLogEntity.menuUid.eq(menuEntity.menuUid))
.where(menuNameEquals(searchValue))
.fetchOne();
return new PageImpl<>(foundContent, pageable, countQuery);
}
@Override
public Page<AuditLogDto.AuditList> findLogByAccount(AuditLogDto.MenuUserSearchReq searchReq, String searchValue) {
Pageable pageable = searchReq.toPageable();
List<AuditLogDto.AuditList> foundContent = queryFactory
.select(
Projections.constructor(
AuditLogDto.AuditList.class,
auditLogEntity.userUid.as("accountId"),
userEntity.userId.as("loginId"),
userEntity.userNm.as("username"),
readCount().as("readCount"),
cudCount().as("cudCount"),
printCount().as("printCount"),
downloadCount().as("downloadCount"),
auditLogEntity.count().as("totalCount")
)
)
.from(auditLogEntity)
.leftJoin(userEntity).on(auditLogEntity.userUid.eq(userEntity.id))
.where(
loginIdOrUsernameContains(searchValue)
)
.groupBy(auditLogEntity.userUid, userEntity.userId, userEntity.userNm)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
// .orderBy(auditLogEntity.eventEndedAt.max().desc())
.fetch();
Long countQuery = queryFactory
.select(auditLogEntity.userUid.countDistinct())
.from(auditLogEntity)
.leftJoin(userEntity).on(auditLogEntity.userUid.eq(userEntity.id))
.where(loginIdOrUsernameContains(searchValue))
.fetchOne();
return new PageImpl<>(foundContent, pageable, countQuery);
}
@Override
public Page<AuditLogDto.AuditDetail> findLogByDailyResult(AuditLogDto.DailySearchReq searchReq, LocalDate logDate) {
Pageable pageable = searchReq.toPageable();
QMenuEntity parent = new QMenuEntity("parent");
// 1depth menu name
StringExpression parentMenuName =
new CaseBuilder()
.when(parent.menuUid.isNull()).then(menuEntity.menuNm)
.otherwise(parent.menuNm);
// 2depth menu name
StringExpression menuName =
new CaseBuilder()
.when(parent.menuUid.isNull()).then(NULL_STRING)
.otherwise(menuEntity.menuNm);
List<AuditLogDto.AuditDetail> foundContent = queryFactory
.select(
Projections.constructor(
AuditLogDto.AuditDetail.class,
auditLogEntity.id.as("logId"),
userEntity.userNm.as("userName"),
userEntity.userId.as("loginId"),
menuEntity.menuNm.as("menuName"),
auditLogEntity.eventType.as("eventType"),
Projections.constructor(
AuditLogDto.LogDetail.class,
Expressions.constant("한국자산관리공사"), //serviceName
parentMenuName.as("parentMenuName"),
menuName,
menuEntity.menuUrl.as("menuUrl"),
menuEntity.description.as("menuDescription"),
menuEntity.menuOrder.as("sortOrder"),
menuEntity.isUse.as("used")
)
)
)
.from(auditLogEntity)
.leftJoin(menuEntity).on(auditLogEntity.menuUid.eq(menuEntity.menuUid))
.leftJoin(menuEntity.parent, parent)
.leftJoin(userEntity).on(auditLogEntity.userUid.eq(userEntity.id))
.where(
eventEndedAtEqDate(logDate)
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(auditLogEntity.createdDate.desc())
.fetch();
Long countQuery = queryFactory
.select(auditLogEntity.id.countDistinct())
.from(auditLogEntity)
.leftJoin(menuEntity).on(auditLogEntity.menuUid.eq(menuEntity.menuUid))
.leftJoin(menuEntity.parent, parent)
.leftJoin(userEntity).on(auditLogEntity.userUid.eq(userEntity.id))
.where(
eventEndedAtEqDate(logDate)
)
.fetchOne();
return new PageImpl<>(foundContent, pageable, countQuery);
}
@Override
public Page<AuditLogDto.AuditDetail> findLogByMenuResult(AuditLogDto.MenuUserSearchReq searchReq, String menuUid) {
Pageable pageable = searchReq.toPageable();
QMenuEntity parent = new QMenuEntity("parent");
// 1depth menu name
StringExpression parentMenuName =
new CaseBuilder()
.when(parent.menuUid.isNull()).then(menuEntity.menuNm)
.otherwise(parent.menuNm);
// 2depth menu name
StringExpression menuName =
new CaseBuilder()
.when(parent.menuUid.isNull()).then(NULL_STRING)
.otherwise(menuEntity.menuNm);
List<AuditLogDto.AuditDetail> foundContent = queryFactory
.select(
Projections.constructor(
AuditLogDto.AuditDetail.class,
auditLogEntity.id.as("logId"),
auditLogEntity.createdDate.as("logDateTime"),
userEntity.userNm.as("userName"),
userEntity.userId.as("loginId"),
auditLogEntity.eventType.as("eventType"),
Projections.constructor(
AuditLogDto.LogDetail.class,
Expressions.constant("한국자산관리공사"), //serviceName
parentMenuName.as("parentMenuName"),
menuName,
menuEntity.menuUrl.as("menuUrl"),
menuEntity.description.as("menuDescription"),
menuEntity.menuOrder.as("sortOrder"),
menuEntity.isUse.as("used")
)
)
)
.from(auditLogEntity)
.leftJoin(menuEntity).on(auditLogEntity.menuUid.eq(menuEntity.menuUid))
.leftJoin(menuEntity.parent, parent)
.leftJoin(userEntity).on(auditLogEntity.userUid.eq(userEntity.id))
.where(
menuUidEq(menuUid)
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(auditLogEntity.createdDate.desc())
.fetch();
Long countQuery = queryFactory
.select(auditLogEntity.id.countDistinct())
.from(auditLogEntity)
.leftJoin(menuEntity).on(auditLogEntity.menuUid.eq(menuEntity.menuUid))
.leftJoin(menuEntity.parent, parent)
.leftJoin(userEntity).on(auditLogEntity.userUid.eq(userEntity.id))
.where(
menuUidEq(menuUid)
).fetchOne();
return new PageImpl<>(foundContent, pageable, countQuery);
}
@Override
public Page<AuditLogDto.AuditDetail> findLogByAccountResult(AuditLogDto.MenuUserSearchReq searchReq, Long userUid) {
Pageable pageable = searchReq.toPageable();
QMenuEntity parent = new QMenuEntity("parent");
// 1depth menu name
StringExpression parentMenuName =
new CaseBuilder()
.when(parent.menuUid.isNull()).then(menuEntity.menuNm)
.otherwise(parent.menuNm);
// 2depth menu name
StringExpression menuName =
new CaseBuilder()
.when(parent.menuUid.isNull()).then(NULL_STRING)
.otherwise(menuEntity.menuNm);
List<AuditLogDto.AuditDetail> foundContent = queryFactory
.select(
Projections.constructor(
AuditLogDto.AuditDetail.class,
auditLogEntity.id.as("logId"),
auditLogEntity.createdDate.as("logDateTime"),
menuEntity.menuNm.as("menuName"),
auditLogEntity.eventType.as("eventType"),
Projections.constructor(
AuditLogDto.LogDetail.class,
Expressions.constant("한국자산관리공사"), //serviceName
parentMenuName.as("parentMenuName"),
menuName,
menuEntity.menuUrl.as("menuUrl"),
menuEntity.description.as("menuDescription"),
menuEntity.menuOrder.as("sortOrder"),
menuEntity.isUse.as("used")
)
)
)
.from(auditLogEntity)
.leftJoin(menuEntity).on(auditLogEntity.menuUid.eq(menuEntity.menuUid))
.leftJoin(menuEntity.parent, parent)
.leftJoin(userEntity).on(auditLogEntity.userUid.eq(userEntity.id))
.where(
userUidEq(userUid)
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(auditLogEntity.createdDate.desc())
.fetch();
Long countQuery = queryFactory
.select(auditLogEntity.id.countDistinct())
.from(auditLogEntity)
.leftJoin(menuEntity).on(auditLogEntity.menuUid.eq(menuEntity.menuUid))
.leftJoin(menuEntity.parent, parent)
.leftJoin(userEntity).on(auditLogEntity.userUid.eq(userEntity.id))
.where(
userUidEq(userUid)
)
.fetchOne();
return new PageImpl<>(foundContent, pageable, countQuery);
}
private BooleanExpression eventEndedAtBetween(LocalDate startDate, LocalDate endDate) {
if (Objects.isNull(startDate) || Objects.isNull(endDate)) {
return null;
}
LocalDateTime startDateTime = startDate.atStartOfDay();
LocalDateTime endDateTime = endDate.plusDays(1).atStartOfDay();
return auditLogEntity.createdDate.goe(ZonedDateTime.from(startDateTime))
.and(auditLogEntity.createdDate.lt(ZonedDateTime.from(endDateTime)));
}
private BooleanExpression menuNameEquals(String searchValue) {
if (StringUtils.isBlank(searchValue)) {
return null;
}
return menuEntity.menuNm.contains(searchValue);
}
private BooleanExpression loginIdOrUsernameContains(String searchValue) {
if (StringUtils.isBlank(searchValue)) {
return null;
}
return userEntity.userId.contains(searchValue).or(userEntity.userNm.contains(searchValue));
}
private BooleanExpression eventStatusEqFailed() {
return auditLogEntity.eventStatus.eq(EventStatus.FAILED);
}
private BooleanExpression eventTypeEq(EventType eventType) {
if (Objects.isNull(eventType)) {
return null;
}
return auditLogEntity.eventType.eq(eventType);
}
private BooleanExpression errorLevelEq(ErrorLogDto.LogErrorLevel level) {
if (Objects.isNull(level)) {
return null;
}
return errorLogEntity.errorLevel.eq(ErrorLogDto.LogErrorLevel.valueOf(level.name()));
}
private BooleanExpression eventEndedAtEqDate(LocalDate logDate) {
DateTimeExpression<LocalDateTime> eventEndedDate =
Expressions.dateTimeTemplate(LocalDateTime.class, "date_trunc('day', {0})", auditLogEntity.createdDate);
LocalDateTime comparisonDate = logDate.atStartOfDay();
return eventEndedDate.eq(comparisonDate);
}
private BooleanExpression menuUidEq(String menuUid) {
return auditLogEntity.menuUid.eq(menuUid);
}
private BooleanExpression userUidEq(Long userUid) {
return auditLogEntity.userUid.eq(userUid);
}
private NumberExpression<Integer> readCount() {
return new CaseBuilder()
.when(auditLogEntity.eventType.eq(EventType.READ)).then(1)
.otherwise(0)
.sum();
}
private NumberExpression<Integer> cudCount() {
return new CaseBuilder()
.when(auditLogEntity.eventType.in(EventType.CREATE, EventType.UPDATE, EventType.DELETE)).then(1)
.otherwise(0)
.sum();
}
private NumberExpression<Integer> printCount() {
return new CaseBuilder()
.when(auditLogEntity.eventType.eq(EventType.PRINT)).then(1)
.otherwise(0)
.sum();
}
private NumberExpression<Integer> downloadCount() {
return new CaseBuilder()
.when(auditLogEntity.eventType.eq(EventType.DOWNLOAD)).then(1)
.otherwise(0)
.sum();
}
}

View File

@@ -0,0 +1,6 @@
package com.kamco.cd.kamcoback.postgres.repository.log;
import com.kamco.cd.kamcoback.postgres.entity.ErrorLogEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ErrorLogRepository extends JpaRepository<ErrorLogEntity, Long>, ErrorLogRepositoryCustom {}

View File

@@ -0,0 +1,12 @@
package com.kamco.cd.kamcoback.postgres.repository.log;
import com.kamco.cd.kamcoback.log.dto.AuditLogDto;
import com.kamco.cd.kamcoback.log.dto.ErrorLogDto;
import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity;
import org.springframework.data.domain.Page;
import java.time.LocalDate;
public interface ErrorLogRepositoryCustom {
public Page<ErrorLogDto.Basic> findLogByError(ErrorLogDto.ErrorSearchReq searchReq);
}

View File

@@ -0,0 +1,117 @@
package com.kamco.cd.kamcoback.postgres.repository.log;
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.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 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 java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Objects;
import static com.kamco.cd.kamcoback.postgres.entity.QAuditLogEntity.auditLogEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QErrorLogEntity.errorLogEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMenuEntity.menuEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QUserEntity.userEntity;
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();
List<ErrorLogDto.Basic> foundContent = queryFactory
.select(
Projections.constructor(
ErrorLogDto.Basic.class,
errorLogEntity.id.as("logId"),
Expressions.constant("한국자산관리공사"), //serviceName
menuEntity.menuNm.as("menuName"),
userEntity.userId.as("loginId"),
userEntity.userNm.as("userName"),
errorLogEntity.errorType.as("eventType"),
errorLogEntity.errorMessage.as("errorName"), // 기존에는 errorName 값이 있었는데 신규 테이블에는 없음. 에러 메세지와 동일
errorLogEntity.errorLevel.as("errorLevel"),
errorLogEntity.errorCode.as("errorCode"),
errorLogEntity.errorMessage.as("errorMessage"),
errorLogEntity.stackTrace.as("errorDetail"),
errorLogEntity.createdDate
)
)
.from(errorLogEntity)
.leftJoin(auditLogEntity).on(errorLogEntity.id.eq(auditLogEntity.errorLogUid))
.leftJoin(menuEntity).on(auditLogEntity.menuUid.eq(menuEntity.menuUid))
.leftJoin(userEntity).on(errorLogEntity.handlerUid.eq(userEntity.id))
.where(
eventStatusEqFailed(),
eventEndedAtBetween(searchReq.getStartDate(), searchReq.getEndDate()),
eventTypeEq(searchReq.getEventType()),
errorLevelEq(searchReq.getErrorLevel())
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(errorLogEntity.createdDate.desc())
.fetch();
Long countQuery = queryFactory
.select(errorLogEntity.id.countDistinct())
.from(errorLogEntity)
.leftJoin(auditLogEntity).on(errorLogEntity.id.eq(auditLogEntity.errorLogUid))
.leftJoin(menuEntity).on(auditLogEntity.menuUid.eq(menuEntity.menuUid))
.leftJoin(userEntity).on(errorLogEntity.handlerUid.eq(userEntity.id))
.where(
eventStatusEqFailed(),
eventEndedAtBetween(searchReq.getStartDate(), searchReq.getEndDate()),
eventTypeEq(searchReq.getEventType()),
errorLevelEq(searchReq.getErrorLevel())
)
.fetchOne();
return new PageImpl<>(foundContent, pageable, countQuery);
}
private BooleanExpression eventEndedAtBetween(LocalDate startDate, LocalDate endDate) {
if (Objects.isNull(startDate) || Objects.isNull(endDate)) {
return null;
}
LocalDateTime startDateTime = startDate.atStartOfDay();
LocalDateTime endDateTime = endDate.plusDays(1).atStartOfDay();
return auditLogEntity.createdDate.goe(ZonedDateTime.from(startDateTime))
.and(auditLogEntity.createdDate.lt(ZonedDateTime.from(endDateTime)));
}
private BooleanExpression eventStatusEqFailed() {
return auditLogEntity.eventStatus.eq(EventStatus.FAILED);
}
private BooleanExpression eventTypeEq(EventType eventType) {
if (Objects.isNull(eventType)) {
return null;
}
return auditLogEntity.eventType.eq(eventType);
}
private BooleanExpression errorLevelEq(ErrorLogDto.LogErrorLevel level) {
if (Objects.isNull(level)) {
return null;
}
return errorLogEntity.errorLevel.eq(ErrorLogDto.LogErrorLevel.valueOf(level.name()));
}
}