state-check 스케줄 추가, pnu-update 수정
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
# IMAGERY MAKE DATASET
|
# GUKYUIN PNU UPDATE
|
||||||
|
|
||||||
> 국유인에 연동된 객체 조회하여 pnu update schedule
|
> 국유인에 연동된 객체 조회하여 pnu update schedule
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -650,7 +650,7 @@ code + .copy-button {
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function configurationCacheProblems() { return (
|
function configurationCacheProblems() { return (
|
||||||
// begin-report-data
|
// begin-report-data
|
||||||
{"diagnostics":[{"locations":[{}],"problem":[{"text":"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated."}],"severity":"WARNING","problemDetails":[{"text":"This is scheduled to be removed in Gradle 10.0."}],"contextualLabel":"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.","documentationLink":"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax","problemId":[{"name":"deprecation","displayName":"Deprecation"},{"name":"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl","displayName":"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated."}],"solutions":[[{"text":"Use assignment ('url = <value>') instead."}]]},{"locations":[{"path":"C:\\workspace\\kamco-cd-cron\\label\\review-to-down\\src\\main\\java\\com\\kamco\\cd\\kamcoback\\dto\\LabelAllocateDto.java"},{"taskPath":":compileJava"}],"problem":[{"text":"C:\\workspace\\kamco-cd-cron\\label\\review-to-down\\src\\main\\java\\com\\kamco\\cd\\kamcoback\\dto\\LabelAllocateDto.java uses or overrides a deprecated API."}],"severity":"ADVICE","problemDetails":[{"text":"Note: C:\\workspace\\kamco-cd-cron\\label\\review-to-down\\src\\main\\java\\com\\kamco\\cd\\kamcoback\\dto\\LabelAllocateDto.java uses or overrides a deprecated API."}],"contextualLabel":"C:\\workspace\\kamco-cd-cron\\label\\review-to-down\\src\\main\\java\\com\\kamco\\cd\\kamcoback\\dto\\LabelAllocateDto.java uses or overrides a deprecated API.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.deprecated.filename","displayName":"C:\\workspace\\kamco-cd-cron\\label\\review-to-down\\src\\main\\java\\com\\kamco\\cd\\kamcoback\\dto\\LabelAllocateDto.java uses or overrides a deprecated API."}]},{"locations":[{"path":"C:\\workspace\\kamco-cd-cron\\label\\review-to-down\\src\\main\\java\\com\\kamco\\cd\\kamcoback\\dto\\LabelAllocateDto.java"},{"taskPath":":compileJava"}],"problem":[{"text":"Recompile with -Xlint:deprecation for details."}],"severity":"ADVICE","problemDetails":[{"text":"Note: Recompile with -Xlint:deprecation for details."}],"contextualLabel":"Recompile with -Xlint:deprecation for details.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.deprecated.recompile","displayName":"Recompile with -Xlint:deprecation for details."}]},{"locations":[{"path":"C:\\workspace\\kamco-cd-cron\\label\\review-to-down\\src\\main\\java\\com\\kamco\\cd\\kamcoback\\common\\utils\\enums\\Enums.java"},{"taskPath":":compileJava"}],"problem":[{"text":"Some input files use unchecked or unsafe operations."}],"severity":"ADVICE","problemDetails":[{"text":"Note: Some input files use unchecked or unsafe operations."}],"contextualLabel":"Some input files use unchecked or unsafe operations.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.unchecked.plural","displayName":"Some input files use unchecked or unsafe operations."}]},{"locations":[{"path":"C:\\workspace\\kamco-cd-cron\\label\\review-to-down\\src\\main\\java\\com\\kamco\\cd\\kamcoback\\common\\utils\\enums\\Enums.java"},{"taskPath":":compileJava"}],"problem":[{"text":"Recompile with -Xlint:unchecked for details."}],"severity":"ADVICE","problemDetails":[{"text":"Note: Recompile with -Xlint:unchecked for details."}],"contextualLabel":"Recompile with -Xlint:unchecked for details.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.unchecked.recompile","displayName":"Recompile with -Xlint:unchecked for details."}]}],"problemsReport":{"totalProblemCount":5,"buildName":"kamco-review-to-down-job","requestedTasks":"clean build","documentationLink":"https://docs.gradle.org/8.14/userguide/reporting_problems.html","documentationLinkCaption":"Problem report","summaries":[]}}
|
{"diagnostics":[{"locations":[{}],"problem":[{"text":"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated."}],"severity":"WARNING","problemDetails":[{"text":"This is scheduled to be removed in Gradle 10.0."}],"contextualLabel":"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated.","documentationLink":"https://docs.gradle.org/8.14/userguide/upgrading_version_8.html#groovy_space_assignment_syntax","problemId":[{"name":"deprecation","displayName":"Deprecation"},{"name":"properties-should-be-assigned-using-the-propname-value-syntax-setting-a-property-via-the-gradle-generated-propname-value-or-propname-value-syntax-in-groovy-dsl","displayName":"Properties should be assigned using the 'propName = value' syntax. Setting a property via the Gradle-generated 'propName value' or 'propName(value)' syntax in Groovy DSL has been deprecated."}],"solutions":[[{"text":"Use assignment ('url = <value>') instead."}]]},{"locations":[{"path":"C:\\workspace\\kamco-cd-cron\\gukyuin\\pnu-update\\src\\main\\java\\com\\kamco\\cd\\kamcoback\\config\\resttemplate\\ExternalHttpClient.java"},{"taskPath":":compileJava"}],"problem":[{"text":"Some input files use or override a deprecated API."}],"severity":"ADVICE","problemDetails":[{"text":"Note: Some input files use or override a deprecated API."}],"contextualLabel":"Some input files use or override a deprecated API.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.deprecated.plural","displayName":"Some input files use or override a deprecated API."}]},{"locations":[{"path":"C:\\workspace\\kamco-cd-cron\\gukyuin\\pnu-update\\src\\main\\java\\com\\kamco\\cd\\kamcoback\\config\\resttemplate\\ExternalHttpClient.java"},{"taskPath":":compileJava"}],"problem":[{"text":"Recompile with -Xlint:deprecation for details."}],"severity":"ADVICE","problemDetails":[{"text":"Note: Recompile with -Xlint:deprecation for details."}],"contextualLabel":"Recompile with -Xlint:deprecation for details.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.deprecated.recompile","displayName":"Recompile with -Xlint:deprecation for details."}]},{"locations":[{"path":"C:\\workspace\\kamco-cd-cron\\gukyuin\\pnu-update\\src\\main\\java\\com\\kamco\\cd\\kamcoback\\common\\utils\\enums\\Enums.java"},{"taskPath":":compileJava"}],"problem":[{"text":"Some input files use unchecked or unsafe operations."}],"severity":"ADVICE","problemDetails":[{"text":"Note: Some input files use unchecked or unsafe operations."}],"contextualLabel":"Some input files use unchecked or unsafe operations.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.unchecked.plural","displayName":"Some input files use unchecked or unsafe operations."}]},{"locations":[{"path":"C:\\workspace\\kamco-cd-cron\\gukyuin\\pnu-update\\src\\main\\java\\com\\kamco\\cd\\kamcoback\\common\\utils\\enums\\Enums.java"},{"taskPath":":compileJava"}],"problem":[{"text":"Recompile with -Xlint:unchecked for details."}],"severity":"ADVICE","problemDetails":[{"text":"Note: Recompile with -Xlint:unchecked for details."}],"contextualLabel":"Recompile with -Xlint:unchecked for details.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.unchecked.recompile","displayName":"Recompile with -Xlint:unchecked for details."}]}],"problemsReport":{"totalProblemCount":5,"buildName":"kamco-pnu-update-job","requestedTasks":"clean build","documentationLink":"https://docs.gradle.org/8.14/userguide/reporting_problems.html","documentationLinkCaption":"Problem report","summaries":[]}}
|
||||||
// end-report-data
|
// end-report-data
|
||||||
);}
|
);}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -65,3 +65,7 @@ file:
|
|||||||
sync-auto-exception-start-year: 2025
|
sync-auto-exception-start-year: 2025
|
||||||
sync-auto-exception-before-year-cnt: 3
|
sync-auto-exception-before-year-cnt: 3
|
||||||
|
|
||||||
|
gukyuin:
|
||||||
|
#url: http://localhost:8080
|
||||||
|
url: http://192.168.2.129:5301
|
||||||
|
cdi: ${gukyuin.url}/api/kcd/cdi
|
||||||
@@ -65,3 +65,7 @@ file:
|
|||||||
sync-auto-exception-start-year: 2025
|
sync-auto-exception-start-year: 2025
|
||||||
sync-auto-exception-before-year-cnt: 3
|
sync-auto-exception-before-year-cnt: 3
|
||||||
|
|
||||||
|
gukyuin:
|
||||||
|
#url: http://localhost:8080
|
||||||
|
url: http://192.168.2.129:5301
|
||||||
|
cdi: ${gukyuin.url}/api/kcd/cdi
|
||||||
@@ -65,3 +65,7 @@ file:
|
|||||||
sync-auto-exception-start-year: 2025
|
sync-auto-exception-start-year: 2025
|
||||||
sync-auto-exception-before-year-cnt: 3
|
sync-auto-exception-before-year-cnt: 3
|
||||||
|
|
||||||
|
gukyuin:
|
||||||
|
#url: http://localhost:8080
|
||||||
|
url: http://192.168.2.129:5301
|
||||||
|
cdi: ${gukyuin.url}/api/kcd/cdi
|
||||||
@@ -7,6 +7,6 @@ Spring-Boot-Lib: BOOT-INF/lib/
|
|||||||
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
|
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
|
||||||
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
|
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
|
||||||
Build-Jdk-Spec: 21
|
Build-Jdk-Spec: 21
|
||||||
Implementation-Title: kamco-review-to-down-job
|
Implementation-Title: kamco-pnu-update-job
|
||||||
Implementation-Version: 0.0.1-SNAPSHOT
|
Implementation-Version: 0.0.1-SNAPSHOT
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -1,54 +0,0 @@
|
|||||||
package com.kamco.cd.kamcoback.common.utils;
|
|
||||||
|
|
||||||
import com.kamco.cd.kamcoback.auth.CustomUserDetails;
|
|
||||||
import com.kamco.cd.kamcoback.members.dto.MembersDto;
|
|
||||||
import com.kamco.cd.kamcoback.postgres.entity.MemberEntity;
|
|
||||||
import java.util.Optional;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class UserUtil {
|
|
||||||
|
|
||||||
public MembersDto.Member getCurrentUser() {
|
|
||||||
return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
|
|
||||||
.filter(auth -> auth.getPrincipal() instanceof CustomUserDetails)
|
|
||||||
.map(
|
|
||||||
auth -> {
|
|
||||||
CustomUserDetails user = (CustomUserDetails) auth.getPrincipal();
|
|
||||||
MemberEntity m = user.getMember();
|
|
||||||
return new MembersDto.Member(
|
|
||||||
m.getId(), m.getName(), m.getEmployeeNo(), m.getUserRole());
|
|
||||||
})
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getId() {
|
|
||||||
MembersDto.Member user = getCurrentUser();
|
|
||||||
return user != null ? user.getId() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
MembersDto.Member user = getCurrentUser();
|
|
||||||
return user != null ? user.getName() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEmployeeNo() {
|
|
||||||
MembersDto.Member user = getCurrentUser();
|
|
||||||
return user != null ? user.getEmployeeNo() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRole() {
|
|
||||||
MembersDto.Member user = getCurrentUser();
|
|
||||||
return user != null ? user.getRole() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getIp() {
|
|
||||||
return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication())
|
|
||||||
.map(auth -> auth.getDetails())
|
|
||||||
.map(Object::toString)
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
package com.kamco.cd.kamcoback.config.api;
|
|
||||||
|
|
||||||
import com.kamco.cd.kamcoback.log.dto.EventStatus;
|
|
||||||
import com.kamco.cd.kamcoback.log.dto.EventType;
|
|
||||||
import com.kamco.cd.kamcoback.menu.dto.MenuDto;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.web.util.ContentCachingRequestWrapper;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class ApiLogFunction {
|
|
||||||
|
|
||||||
// 클라이언트 IP 추출
|
|
||||||
public static String getClientIp(HttpServletRequest request) {
|
|
||||||
String[] headers = {
|
|
||||||
"X-Forwarded-For",
|
|
||||||
"Proxy-Client-IP",
|
|
||||||
"WL-Proxy-Client-IP",
|
|
||||||
"HTTP_CLIENT_IP",
|
|
||||||
"HTTP_X_FORWARDED_FOR"
|
|
||||||
};
|
|
||||||
for (String header : headers) {
|
|
||||||
String ip = request.getHeader(header);
|
|
||||||
if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
|
|
||||||
return ip.split(",")[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String ip = request.getRemoteAddr();
|
|
||||||
if ("0:0:0:0:0:0:0:1".equals(ip)) { // local 일 때
|
|
||||||
ip = "127.0.0.1";
|
|
||||||
}
|
|
||||||
return ip;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getXFowardedForIp(HttpServletRequest request) {
|
|
||||||
String ip = request.getHeader("X-Forwarded-For");
|
|
||||||
if (ip != null) {
|
|
||||||
ip = ip.split(",")[0].trim();
|
|
||||||
}
|
|
||||||
return ip;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 사용자 ID 추출 예시 (Spring Security 기준)
|
|
||||||
public static String getUserId(HttpServletRequest request) {
|
|
||||||
try {
|
|
||||||
return request.getUserPrincipal() != null ? request.getUserPrincipal().getName() : null;
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EventType getEventType(HttpServletRequest request) {
|
|
||||||
String method = request.getMethod().toUpperCase();
|
|
||||||
String uri = request.getRequestURI().toLowerCase();
|
|
||||||
|
|
||||||
// URL 기반 DOWNLOAD/PRINT 분류 -> /download는 FileDownloadInterceptor로 옮김
|
|
||||||
if (uri.contains("/download") || uri.contains("/export")) {
|
|
||||||
return EventType.DOWNLOAD;
|
|
||||||
}
|
|
||||||
if (uri.contains("/print")) {
|
|
||||||
return EventType.OTHER;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 일반 CRUD
|
|
||||||
return switch (method) {
|
|
||||||
case "POST" -> EventType.ADDED;
|
|
||||||
case "GET" -> EventType.LIST;
|
|
||||||
case "DELETE" -> EventType.REMOVE;
|
|
||||||
case "PUT", "PATCH" -> EventType.MODIFIED;
|
|
||||||
default -> EventType.OTHER;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getRequestBody(
|
|
||||||
HttpServletRequest servletRequest, ContentCachingRequestWrapper contentWrapper) {
|
|
||||||
StringBuilder resultBody = new StringBuilder();
|
|
||||||
// GET, form-urlencoded POST 파라미터
|
|
||||||
Map<String, String[]> paramMap = servletRequest.getParameterMap();
|
|
||||||
|
|
||||||
String queryParams =
|
|
||||||
paramMap.entrySet().stream()
|
|
||||||
.map(e -> e.getKey() + "=" + String.join(",", e.getValue()))
|
|
||||||
.collect(Collectors.joining("&"));
|
|
||||||
|
|
||||||
resultBody.append(queryParams.isEmpty() ? "" : queryParams);
|
|
||||||
|
|
||||||
// JSON Body
|
|
||||||
if ("POST".equalsIgnoreCase(servletRequest.getMethod())
|
|
||||||
&& servletRequest.getContentType() != null
|
|
||||||
&& servletRequest.getContentType().contains("application/json")) {
|
|
||||||
try {
|
|
||||||
// json인 경우는 Wrapper를 통해 가져오기
|
|
||||||
resultBody.append(getBodyData(contentWrapper));
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
resultBody.append("cannot read JSON body ").append(e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multipart form-data
|
|
||||||
if ("POST".equalsIgnoreCase(servletRequest.getMethod())
|
|
||||||
&& servletRequest.getContentType() != null
|
|
||||||
&& servletRequest.getContentType().startsWith("multipart/form-data")) {
|
|
||||||
resultBody.append("multipart/form-data request");
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultBody.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSON Body 읽기
|
|
||||||
public static String getBodyData(ContentCachingRequestWrapper request) {
|
|
||||||
byte[] buf = request.getContentAsByteArray();
|
|
||||||
if (buf.length == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return new String(buf, request.getCharacterEncoding());
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
return new String(buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApiResponse 의 Status가 2xx 범위이면 SUCCESS, 아니면 FAILED
|
|
||||||
public static EventStatus isSuccessFail(ApiResponseDto<?> apiResponse) {
|
|
||||||
return apiResponse.getHttpStatus().is2xxSuccessful() ? EventStatus.SUCCESS : EventStatus.FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getUriMenuInfo(List<MenuDto.Basic> menuList, String uri) {
|
|
||||||
|
|
||||||
String normalizedUri = uri.replace("/api", "");
|
|
||||||
MenuDto.Basic basic =
|
|
||||||
menuList.stream()
|
|
||||||
.filter(
|
|
||||||
menu -> menu.getMenuUrl() != null && normalizedUri.startsWith(menu.getMenuUrl()))
|
|
||||||
.max(Comparator.comparingInt(m -> m.getMenuUrl().length()))
|
|
||||||
.orElse(null);
|
|
||||||
|
|
||||||
return basic != null ? basic.getMenuUid() : "SYSTEM";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String cutRequestBody(String value) {
|
|
||||||
int MAX_LEN = 255;
|
|
||||||
if (value == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return value.length() <= MAX_LEN ? value : value.substring(0, MAX_LEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
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.Column;
|
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.persistence.EnumType;
|
|
||||||
import jakarta.persistence.Enumerated;
|
|
||||||
import jakarta.persistence.GeneratedValue;
|
|
||||||
import jakarta.persistence.GenerationType;
|
|
||||||
import jakarta.persistence.Id;
|
|
||||||
import jakarta.persistence.Table;
|
|
||||||
import java.util.UUID;
|
|
||||||
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", nullable = false)
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Column(name = "download_uuid")
|
|
||||||
private UUID downloadUuid;
|
|
||||||
|
|
||||||
@Column(name = "login_attempt_id")
|
|
||||||
private String loginAttemptId;
|
|
||||||
|
|
||||||
public AuditLogEntity(
|
|
||||||
Long userUid,
|
|
||||||
EventType eventType,
|
|
||||||
EventStatus eventStatus,
|
|
||||||
String menuUid,
|
|
||||||
String ipAddress,
|
|
||||||
String requestUri,
|
|
||||||
String requestBody,
|
|
||||||
Long errorLogUid,
|
|
||||||
UUID downloadUuid,
|
|
||||||
String loginAttemptId) {
|
|
||||||
this.userUid = userUid;
|
|
||||||
this.eventType = eventType;
|
|
||||||
this.eventStatus = eventStatus;
|
|
||||||
this.menuUid = menuUid;
|
|
||||||
this.ipAddress = ipAddress;
|
|
||||||
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() {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
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 {}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
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;
|
|
||||||
|
|
||||||
public interface AuditLogRepositoryCustom {
|
|
||||||
|
|
||||||
Page<AuditLogDto.DailyAuditList> findLogByDaily(
|
|
||||||
AuditLogDto.searchReq searchReq, LocalDate startDate, LocalDate endDate);
|
|
||||||
|
|
||||||
Page<AuditLogDto.MenuAuditList> findLogByMenu(
|
|
||||||
AuditLogDto.searchReq searchReq, String searchValue);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
Page<AuditLogDto.MenuDetail> findLogByMenuResult(AuditLogDto.searchReq searchReq, String menuId);
|
|
||||||
|
|
||||||
Page<AuditLogDto.UserDetail> findLogByAccountResult(
|
|
||||||
AuditLogDto.searchReq searchReq, Long accountId);
|
|
||||||
}
|
|
||||||
@@ -1,523 +0,0 @@
|
|||||||
package com.kamco.cd.kamcoback.postgres.repository.log;
|
|
||||||
|
|
||||||
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.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;
|
|
||||||
import com.querydsl.core.types.dsl.Expressions;
|
|
||||||
import com.querydsl.core.types.dsl.NumberExpression;
|
|
||||||
import com.querydsl.core.types.dsl.StringExpression;
|
|
||||||
import com.querydsl.jpa.impl.JPAQueryFactory;
|
|
||||||
import io.micrometer.common.util.StringUtils;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.ZonedDateTime;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
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;
|
|
||||||
|
|
||||||
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) {
|
|
||||||
StringExpression groupDateTime =
|
|
||||||
Expressions.stringTemplate("to_char({0}, 'YYYY-MM-DD')", auditLogEntity.createdDate);
|
|
||||||
|
|
||||||
Pageable pageable = searchReq.toPageable();
|
|
||||||
List<AuditLogDto.DailyAuditList> foundContent =
|
|
||||||
queryFactory
|
|
||||||
.select(
|
|
||||||
Projections.constructor(
|
|
||||||
AuditLogDto.DailyAuditList.class,
|
|
||||||
readCount().as("readCount"),
|
|
||||||
cudCount().as("cudCount"),
|
|
||||||
printCount().as("printCount"),
|
|
||||||
downloadCount().as("downloadCount"),
|
|
||||||
auditLogEntity.count().as("totalCount"),
|
|
||||||
groupDateTime.as("baseDate")))
|
|
||||||
.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.MenuAuditList> findLogByMenu(
|
|
||||||
AuditLogDto.searchReq searchReq, String searchValue) {
|
|
||||||
Pageable pageable = searchReq.toPageable();
|
|
||||||
List<AuditLogDto.MenuAuditList> foundContent =
|
|
||||||
queryFactory
|
|
||||||
.select(
|
|
||||||
Projections.constructor(
|
|
||||||
AuditLogDto.MenuAuditList.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(auditLogEntity.menuUid.ne("SYSTEM"), 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.UserAuditList> findLogByAccount(
|
|
||||||
AuditLogDto.searchReq searchReq, String searchValue) {
|
|
||||||
Pageable pageable = searchReq.toPageable();
|
|
||||||
List<AuditLogDto.UserAuditList> foundContent =
|
|
||||||
queryFactory
|
|
||||||
.select(
|
|
||||||
Projections.constructor(
|
|
||||||
AuditLogDto.UserAuditList.class,
|
|
||||||
auditLogEntity.userUid.as("accountId"),
|
|
||||||
memberEntity.employeeNo.as("loginId"),
|
|
||||||
memberEntity.name.as("username"),
|
|
||||||
readCount().as("readCount"),
|
|
||||||
cudCount().as("cudCount"),
|
|
||||||
printCount().as("printCount"),
|
|
||||||
downloadCount().as("downloadCount"),
|
|
||||||
auditLogEntity.count().as("totalCount")))
|
|
||||||
.from(auditLogEntity)
|
|
||||||
.leftJoin(memberEntity)
|
|
||||||
.on(auditLogEntity.userUid.eq(memberEntity.id))
|
|
||||||
.where(auditLogEntity.userUid.isNotNull(), loginIdOrUsernameContains(searchValue))
|
|
||||||
.groupBy(auditLogEntity.userUid, memberEntity.employeeNo, memberEntity.name)
|
|
||||||
.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(loginIdOrUsernameContains(searchValue))
|
|
||||||
.fetchOne();
|
|
||||||
|
|
||||||
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("/api/inference/download/"));
|
|
||||||
whereBuilder.and(auditLogEntity.requestUri.endsWith(String.valueOf(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) {
|
|
||||||
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.DailyDetail> foundContent =
|
|
||||||
queryFactory
|
|
||||||
.select(
|
|
||||||
Projections.constructor(
|
|
||||||
AuditLogDto.DailyDetail.class,
|
|
||||||
auditLogEntity.id.as("logId"),
|
|
||||||
memberEntity.name.as("userName"),
|
|
||||||
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
|
|
||||||
parentMenuName.as("parentMenuName"),
|
|
||||||
menuName,
|
|
||||||
menuEntity.menuUrl.as("menuUrl"),
|
|
||||||
menuEntity.description.as("menuDescription"),
|
|
||||||
menuEntity.menuOrder.as("sortOrder"),
|
|
||||||
menuEntity.isUse.as("used")))) // TODO
|
|
||||||
.from(auditLogEntity)
|
|
||||||
.leftJoin(menuEntity)
|
|
||||||
.on(auditLogEntity.menuUid.eq(menuEntity.menuUid))
|
|
||||||
.leftJoin(menuEntity.parent, parent)
|
|
||||||
.leftJoin(memberEntity)
|
|
||||||
.on(auditLogEntity.userUid.eq(memberEntity.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(memberEntity)
|
|
||||||
.on(auditLogEntity.userUid.eq(memberEntity.id))
|
|
||||||
.where(eventEndedAtEqDate(logDate))
|
|
||||||
.fetchOne();
|
|
||||||
|
|
||||||
return new PageImpl<>(foundContent, pageable, countQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Page<AuditLogDto.MenuDetail> findLogByMenuResult(
|
|
||||||
AuditLogDto.searchReq 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.MenuDetail> foundContent =
|
|
||||||
queryFactory
|
|
||||||
.select(
|
|
||||||
Projections.constructor(
|
|
||||||
AuditLogDto.MenuDetail.class,
|
|
||||||
auditLogEntity.id.as("logId"),
|
|
||||||
Expressions.stringTemplate(
|
|
||||||
"to_char({0}, 'YYYY-MM-DD HH24:MI')", auditLogEntity.createdDate)
|
|
||||||
.as("logDateTime"),
|
|
||||||
memberEntity.name.as("userName"),
|
|
||||||
memberEntity.employeeNo.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(memberEntity)
|
|
||||||
.on(auditLogEntity.userUid.eq(memberEntity.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(memberEntity)
|
|
||||||
.on(auditLogEntity.userUid.eq(memberEntity.id))
|
|
||||||
.where(menuUidEq(menuUid))
|
|
||||||
.fetchOne();
|
|
||||||
|
|
||||||
return new PageImpl<>(foundContent, pageable, countQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Page<AuditLogDto.UserDetail> findLogByAccountResult(
|
|
||||||
AuditLogDto.searchReq 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.UserDetail> foundContent =
|
|
||||||
queryFactory
|
|
||||||
.select(
|
|
||||||
Projections.constructor(
|
|
||||||
AuditLogDto.UserDetail.class,
|
|
||||||
auditLogEntity.id.as("logId"),
|
|
||||||
Expressions.stringTemplate(
|
|
||||||
"to_char({0}, 'YYYY-MM-DD HH24:MI')", 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(memberEntity)
|
|
||||||
.on(auditLogEntity.userUid.eq(memberEntity.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(memberEntity)
|
|
||||||
.on(auditLogEntity.userUid.eq(memberEntity.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;
|
|
||||||
}
|
|
||||||
ZoneId zoneId = ZoneId.of("Asia/Seoul");
|
|
||||||
ZonedDateTime startDateTime = startDate.atStartOfDay(zoneId);
|
|
||||||
ZonedDateTime endDateTime = endDate.plusDays(1).atStartOfDay(zoneId);
|
|
||||||
return auditLogEntity
|
|
||||||
.createdDate
|
|
||||||
.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) {
|
|
||||||
if (StringUtils.isBlank(searchValue)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return menuEntity.menuNm.contains(searchValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BooleanExpression loginIdOrUsernameContains(String searchValue) {
|
|
||||||
if (StringUtils.isBlank(searchValue)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return memberEntity
|
|
||||||
.employeeNo
|
|
||||||
.contains(searchValue)
|
|
||||||
.or(memberEntity.name.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) {
|
|
||||||
ZoneId zoneId = ZoneId.of("Asia/Seoul");
|
|
||||||
ZonedDateTime start = logDate.atStartOfDay(zoneId);
|
|
||||||
ZonedDateTime end = logDate.plusDays(1).atStartOfDay(zoneId);
|
|
||||||
|
|
||||||
return auditLogEntity.createdDate.goe(start).and(auditLogEntity.createdDate.lt(end));
|
|
||||||
}
|
|
||||||
|
|
||||||
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.in(EventType.LIST, EventType.DETAIL))
|
|
||||||
.then(1)
|
|
||||||
.otherwise(0)
|
|
||||||
.sum();
|
|
||||||
}
|
|
||||||
|
|
||||||
private NumberExpression<Integer> cudCount() {
|
|
||||||
return new CaseBuilder()
|
|
||||||
.when(auditLogEntity.eventType.in(EventType.ADDED, EventType.MODIFIED, EventType.REMOVE))
|
|
||||||
.then(1)
|
|
||||||
.otherwise(0)
|
|
||||||
.sum();
|
|
||||||
}
|
|
||||||
|
|
||||||
private NumberExpression<Integer> printCount() {
|
|
||||||
return new CaseBuilder()
|
|
||||||
.when(auditLogEntity.eventType.eq(EventType.OTHER))
|
|
||||||
.then(1)
|
|
||||||
.otherwise(0)
|
|
||||||
.sum();
|
|
||||||
}
|
|
||||||
|
|
||||||
private NumberExpression<Integer> downloadCount() {
|
|
||||||
return new CaseBuilder()
|
|
||||||
.when(auditLogEntity.eventType.eq(EventType.DOWNLOAD))
|
|
||||||
.then(1)
|
|
||||||
.otherwise(0)
|
|
||||||
.sum();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
package com.kamco.cd.kamcoback.scheduler.service;
|
package com.kamco.cd.kamcoback.scheduler.service;
|
||||||
|
|
||||||
import com.kamco.cd.kamcoback.common.utils.NetUtils;
|
import com.kamco.cd.kamcoback.common.utils.NetUtils;
|
||||||
import com.kamco.cd.kamcoback.common.utils.UserUtil;
|
|
||||||
import com.kamco.cd.kamcoback.config.api.ApiLogFunction;
|
|
||||||
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient;
|
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient;
|
||||||
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult;
|
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult;
|
||||||
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectContDto;
|
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectContDto;
|
||||||
@@ -12,11 +10,7 @@ import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto;
|
|||||||
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
|
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
|
||||||
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.ResultDto;
|
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.ResultDto;
|
||||||
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
|
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
|
||||||
import com.kamco.cd.kamcoback.log.dto.EventStatus;
|
|
||||||
import com.kamco.cd.kamcoback.log.dto.EventType;
|
|
||||||
import com.kamco.cd.kamcoback.postgres.core.GukYuinPnuJobCoreService;
|
import com.kamco.cd.kamcoback.postgres.core.GukYuinPnuJobCoreService;
|
||||||
import com.kamco.cd.kamcoback.postgres.entity.AuditLogEntity;
|
|
||||||
import com.kamco.cd.kamcoback.postgres.repository.log.AuditLogRepository;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
@@ -24,8 +18,6 @@ import org.springframework.beans.factory.annotation.Value;
|
|||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
|
|
||||||
@Log4j2
|
@Log4j2
|
||||||
@Service
|
@Service
|
||||||
@@ -35,9 +27,6 @@ public class GukYuinApiPnuJobService {
|
|||||||
private final GukYuinPnuJobCoreService gukYuinPnuJobCoreService;
|
private final GukYuinPnuJobCoreService gukYuinPnuJobCoreService;
|
||||||
private final ExternalHttpClient externalHttpClient;
|
private final ExternalHttpClient externalHttpClient;
|
||||||
private final NetUtils netUtils = new NetUtils();
|
private final NetUtils netUtils = new NetUtils();
|
||||||
private final AuditLogRepository auditLogRepository;
|
|
||||||
|
|
||||||
private final UserUtil userUtil;
|
|
||||||
|
|
||||||
@Value("${spring.profiles.active}")
|
@Value("${spring.profiles.active}")
|
||||||
private String profile;
|
private String profile;
|
||||||
@@ -97,14 +86,6 @@ public class GukYuinApiPnuJobService {
|
|||||||
externalHttpClient.call(
|
externalHttpClient.call(
|
||||||
url, HttpMethod.GET, null, netUtils.jsonHeaders(), ChngDetectMastDto.ResultDto.class);
|
url, HttpMethod.GET, null, netUtils.jsonHeaders(), ChngDetectMastDto.ResultDto.class);
|
||||||
|
|
||||||
this.insertGukyuinAuditLog(
|
|
||||||
EventType.DETAIL.getId(),
|
|
||||||
netUtils.getLocalIP(),
|
|
||||||
userUtil.getId(),
|
|
||||||
url.replace(gukyuinUrl, ""),
|
|
||||||
null,
|
|
||||||
response.body().getSuccess());
|
|
||||||
|
|
||||||
ResultDto result = response.body();
|
ResultDto result = response.body();
|
||||||
if (result == null || result.getResult() == null || result.getResult().isEmpty()) {
|
if (result == null || result.getResult() == null || result.getResult().isEmpty()) {
|
||||||
return succCnt;
|
return succCnt;
|
||||||
@@ -161,14 +142,6 @@ public class GukYuinApiPnuJobService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.insertGukyuinAuditLog(
|
|
||||||
EventType.LIST.getId(),
|
|
||||||
netUtils.getLocalIP(),
|
|
||||||
userUtil.getId(),
|
|
||||||
url.replace(gukyuinUrl, ""),
|
|
||||||
null,
|
|
||||||
response.body().getSuccess());
|
|
||||||
|
|
||||||
ResultContDto cont = response.body();
|
ResultContDto cont = response.body();
|
||||||
if (cont == null || cont.getResult().isEmpty()) {
|
if (cont == null || cont.getResult().isEmpty()) {
|
||||||
return result; // 외부 API 이상 방어
|
return result; // 외부 API 이상 방어
|
||||||
@@ -188,33 +161,4 @@ public class GukYuinApiPnuJobService {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = false)
|
|
||||||
public void insertGukyuinAuditLog(
|
|
||||||
String actionType,
|
|
||||||
String myIp,
|
|
||||||
Long userUid,
|
|
||||||
String requestUri,
|
|
||||||
Object requestBody,
|
|
||||||
boolean successFail) {
|
|
||||||
try {
|
|
||||||
AuditLogEntity log =
|
|
||||||
new AuditLogEntity(
|
|
||||||
userUid,
|
|
||||||
EventType.fromName(actionType),
|
|
||||||
successFail ? EventStatus.SUCCESS : EventStatus.FAILED,
|
|
||||||
"GUKYUIN", // 메뉴도 국유인으로 하나 따기
|
|
||||||
myIp,
|
|
||||||
requestUri,
|
|
||||||
requestBody == null ? null : ApiLogFunction.cutRequestBody(requestBody.toString()),
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null);
|
|
||||||
auditLogRepository.save(log);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error(e.getMessage());
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
gukyuin/state-check/.gradle/8.14/checksums/checksums.lock
Normal file
BIN
gukyuin/state-check/.gradle/8.14/checksums/checksums.lock
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
gukyuin/state-check/.gradle/8.14/fileChanges/last-build.bin
Normal file
BIN
gukyuin/state-check/.gradle/8.14/fileChanges/last-build.bin
Normal file
Binary file not shown.
BIN
gukyuin/state-check/.gradle/8.14/fileHashes/fileHashes.bin
Normal file
BIN
gukyuin/state-check/.gradle/8.14/fileHashes/fileHashes.bin
Normal file
Binary file not shown.
BIN
gukyuin/state-check/.gradle/8.14/fileHashes/fileHashes.lock
Normal file
BIN
gukyuin/state-check/.gradle/8.14/fileHashes/fileHashes.lock
Normal file
Binary file not shown.
Binary file not shown.
0
gukyuin/state-check/.gradle/8.14/gc.properties
Normal file
0
gukyuin/state-check/.gradle/8.14/gc.properties
Normal file
Binary file not shown.
@@ -0,0 +1,2 @@
|
|||||||
|
#Mon Feb 02 19:50:45 KST 2026
|
||||||
|
gradle.version=8.14
|
||||||
BIN
gukyuin/state-check/.gradle/buildOutputCleanup/outputFiles.bin
Normal file
BIN
gukyuin/state-check/.gradle/buildOutputCleanup/outputFiles.bin
Normal file
Binary file not shown.
BIN
gukyuin/state-check/.gradle/file-system.probe
Normal file
BIN
gukyuin/state-check/.gradle/file-system.probe
Normal file
Binary file not shown.
0
gukyuin/state-check/.gradle/vcs-1/gc.properties
Normal file
0
gukyuin/state-check/.gradle/vcs-1/gc.properties
Normal file
112
gukyuin/state-check/CODE_STYLE_SETUP.md
Normal file
112
gukyuin/state-check/CODE_STYLE_SETUP.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# Code Style 설정 가이드
|
||||||
|
|
||||||
|
이 문서는 프로젝트에서 Google Java Style을 자동으로 적용하기 위한 설정 가이드입니다.
|
||||||
|
|
||||||
|
## 자동 포맷팅 구성
|
||||||
|
|
||||||
|
### 1. 커밋 시점 자동 포맷팅 (Git Pre-commit Hook)
|
||||||
|
|
||||||
|
커밋 전에 자동으로 코드를 포맷팅하고 스테이징합니다.
|
||||||
|
|
||||||
|
**설정 완료:** `.git/hooks/pre-commit` 파일이 자동으로 실행됩니다.
|
||||||
|
|
||||||
|
**동작 방식:**
|
||||||
|
- 커밋 시도 시 `./gradlew spotlessApply` 자동 실행
|
||||||
|
- 스테이징된 Java 파일을 자동으로 포맷팅
|
||||||
|
- 포맷팅된 파일을 자동으로 다시 스테이징
|
||||||
|
- 포맷팅이 완료되면 커밋 진행
|
||||||
|
|
||||||
|
**장점:**
|
||||||
|
- 수동으로 `spotlessApply`를 실행할 필요 없음
|
||||||
|
- 항상 일관된 코드 스타일 유지
|
||||||
|
- 포맷팅 누락 방지
|
||||||
|
|
||||||
|
### 2. IntelliJ IDEA 저장 시점 자동 포맷팅
|
||||||
|
|
||||||
|
#### 방법 1: Code Style 설정 임포트 (권장)
|
||||||
|
|
||||||
|
1. **IntelliJ IDEA 열기**
|
||||||
|
2. **Settings/Preferences** (Mac: `⌘,` / Windows: `Ctrl+Alt+S`)
|
||||||
|
3. **Editor > Code Style > Java**
|
||||||
|
4. **⚙️ (톱니바퀴)** 클릭 > **Import Scheme > IntelliJ IDEA code style XML**
|
||||||
|
5. 프로젝트 루트의 `intellij-java-google-style.xml` 파일 선택
|
||||||
|
6. **OK** 클릭
|
||||||
|
|
||||||
|
#### 방법 2: 저장 시 자동 포맷팅 활성화
|
||||||
|
|
||||||
|
|
||||||
|
**Option A: Actions on Save 설정**
|
||||||
|
|
||||||
|
1. **Settings/Preferences** > **Tools > Actions on Save**
|
||||||
|
2. 다음 옵션들을 활성화:
|
||||||
|
- ✅ **Reformat code**
|
||||||
|
- ✅ **Optimize imports**
|
||||||
|
- ✅ **Rearrange code** (선택사항)
|
||||||
|
3. **Changed lines** 또는 **Whole file** 선택
|
||||||
|
4. **OK** 클릭
|
||||||
|
|
||||||
|
**Option B: Save Actions Plugin 사용 (더 많은 옵션)**
|
||||||
|
|
||||||
|
1. **Settings/Preferences** > **Plugins**
|
||||||
|
2. **Marketplace**에서 "Save Actions" 검색 및 설치
|
||||||
|
3. **Settings/Preferences** > **Other Settings > Save Actions**
|
||||||
|
4. 다음 옵션 활성화:
|
||||||
|
- ✅ **Activate save actions on save**
|
||||||
|
- ✅ **Reformat file**
|
||||||
|
- ✅ **Optimize imports**
|
||||||
|
- ✅ **Rearrange fields and methods** (선택사항)
|
||||||
|
|
||||||
|
### 3. Gradle Spotless Plugin 수동 실행
|
||||||
|
|
||||||
|
#### 코드 포맷팅 체크
|
||||||
|
```bash
|
||||||
|
# 포맷팅 문제 확인만 (수정하지 않음)
|
||||||
|
./gradlew spotlessCheck
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 코드 자동 포맷팅
|
||||||
|
```bash
|
||||||
|
# 모든 Java 파일 자동 포맷팅 적용
|
||||||
|
./gradlew spotlessApply
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 빌드 시 자동 체크
|
||||||
|
```bash
|
||||||
|
# 빌드 전에 자동으로 spotlessCheck 실행됨
|
||||||
|
./gradlew build
|
||||||
|
```
|
||||||
|
|
||||||
|
## 코드 스타일 규칙
|
||||||
|
|
||||||
|
프로젝트는 **Google Java Style Guide** 기반으로 다음 규칙을 따릅니다:
|
||||||
|
|
||||||
|
- **Indentation**: 2 spaces (탭 아님)
|
||||||
|
- **Line Length**: 180 characters
|
||||||
|
- **Line Endings**: LF (Unix-style)
|
||||||
|
- **Charset**: UTF-8
|
||||||
|
- **Import Order**: Static imports → 빈 줄 → Regular imports
|
||||||
|
- **Braces**: 모든 if, for, while, do 문에 중괄호 필수
|
||||||
|
|
||||||
|
## 문제 해결
|
||||||
|
|
||||||
|
### Pre-commit hook이 실행되지 않는 경우
|
||||||
|
```bash
|
||||||
|
# 실행 권한 확인 및 부여
|
||||||
|
chmod +x .git/hooks/pre-commit
|
||||||
|
```
|
||||||
|
|
||||||
|
### Spotless 플러그인이 동작하지 않는 경우
|
||||||
|
```bash
|
||||||
|
# Gradle 의존성 다시 다운로드
|
||||||
|
./gradlew clean build --refresh-dependencies
|
||||||
|
```
|
||||||
|
|
||||||
|
### IntelliJ 포맷팅이 다르게 적용되는 경우
|
||||||
|
1. `intellij-java-google-style.xml` 다시 임포트
|
||||||
|
2. **File > Invalidate Caches** > **Invalidate and Restart**
|
||||||
|
|
||||||
|
## 추가 정보
|
||||||
|
|
||||||
|
- **Google Java Style Guide**: https://google.github.io/styleguide/javaguide.html
|
||||||
|
- **Spotless Plugin**: https://github.com/diffplug/spotless
|
||||||
|
- **IntelliJ Code Style**: https://www.jetbrains.com/help/idea/code-style.html
|
||||||
282
gukyuin/state-check/COMMON_CODE_CACHE_REDIS.md
Normal file
282
gukyuin/state-check/COMMON_CODE_CACHE_REDIS.md
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
# 공통코드 Redis 캐시 시스템 - DanielLee
|
||||||
|
|
||||||
|
## 요구사항 검토
|
||||||
|
|
||||||
|
### 1. **API를 통해 공통코드 제공**
|
||||||
|
- **구현 완료**: `CommonCodeApiController`에서 전체 공통코드 조회 API 제공
|
||||||
|
```
|
||||||
|
GET /api/code
|
||||||
|
→ 모든 공통코드 조회
|
||||||
|
```
|
||||||
|
- **추가 구현**: 캐시 갱신 및 상태 확인 API
|
||||||
|
```
|
||||||
|
POST /api/code/cache/refresh → 캐시 갱신
|
||||||
|
GET /api/code/cache/status → 캐시 상태 확인
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. **애플리케이션 로딩시 Redis 캐시에 올리기**
|
||||||
|
- **구현 완료**: `CommonCodeCacheManager` 클래스 생성
|
||||||
|
|
||||||
|
#### 초기화 메커니즘
|
||||||
|
```java
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CommonCodeCacheManager {
|
||||||
|
|
||||||
|
@EventListener(ApplicationReadyEvent.class)
|
||||||
|
public void initializeCommonCodeCache() {
|
||||||
|
// 애플리케이션 완전히 시작된 후 공통코드를 Redis에 미리 로드
|
||||||
|
List<Basic> allCommonCodes = commonCodeService.getFindAll();
|
||||||
|
// @Cacheable이 자동으로 Redis에 캐시함
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 동작 흐름
|
||||||
|
1. 애플리케이션 시작
|
||||||
|
2. Spring이 모든 Bean 생성 완료 (`ApplicationReadyEvent` 발생)
|
||||||
|
3. `CommonCodeCacheManager.initializeCommonCodeCache()` 실행
|
||||||
|
4. `commonCodeService.getFindAll()` 호출 (DB에서 조회)
|
||||||
|
5. `@Cacheable(value = "commonCodes")` 에노테이션이 결과를 Redis에 저장
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. **공통코드 변경시 데이터 갱신**
|
||||||
|
|
||||||
|
#### 자동 갱신
|
||||||
|
- **등록 (CREATE)**: `@CacheEvict` → 캐시 전체 삭제
|
||||||
|
- **수정 (UPDATE)**: `@CacheEvict` → 캐시 전체 삭제
|
||||||
|
- **삭제 (DELETE)**: `@CacheEvict` → 캐시 전체 삭제
|
||||||
|
- **순서 변경**: `@CacheEvict` → 캐시 전체 삭제
|
||||||
|
|
||||||
|
```java
|
||||||
|
@CacheEvict(value = "commonCodes", allEntries = true)
|
||||||
|
public ResponseObj save(CommonCodeDto.AddReq req) {
|
||||||
|
// 공통코드 저장
|
||||||
|
// ↓
|
||||||
|
// 캐시 전체 삭제 (다음 조회 시 DB에서 새로 로드)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 수동 갱신 (관리자)
|
||||||
|
```java
|
||||||
|
POST /api/code/cache/refresh
|
||||||
|
```
|
||||||
|
- 공통코드 설정이 변경된 후 API를 호출하여 캐시를 강제 갱신
|
||||||
|
|
||||||
|
#### 캐시 상태 모니터링
|
||||||
|
```java
|
||||||
|
GET /api/code/cache/status
|
||||||
|
→ 응답: { "data": 150 } // 캐시된 공통코드 150개
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 전체 아키텍처
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ 클라이언트 요청 │
|
||||||
|
└──────────────────┬──────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌──────────▼──────────┐
|
||||||
|
│ CommonCodeApiController
|
||||||
|
└──────────┬──────────┘
|
||||||
|
│
|
||||||
|
┌─────────┴──────────┐
|
||||||
|
│ │
|
||||||
|
┌────▼─────┐ ┌──────▼────────────┐
|
||||||
|
│ 조회 API │ │ 캐시 관리 API │
|
||||||
|
│ (GET) │ │(POST, GET) │
|
||||||
|
└────┬─────┘ └──────┬────────────┘
|
||||||
|
│ │
|
||||||
|
│ ┌────────▼──────────┐
|
||||||
|
│ │CommonCodeCacheManager
|
||||||
|
│ │(캐시 초기화/갱신) │
|
||||||
|
│ └────────┬──────────┘
|
||||||
|
│ │
|
||||||
|
┌────▼─────────────────┬─▼────┐
|
||||||
|
│ CommonCodeService │ │
|
||||||
|
│ (@Cacheable) │ │
|
||||||
|
│ (@CacheEvict) │ │
|
||||||
|
└────┬──────────────────┴──────┘
|
||||||
|
│
|
||||||
|
┌────▼──────────┐
|
||||||
|
│ Redis 캐시 │
|
||||||
|
│ (공통코드) │
|
||||||
|
└────┬──────────┘
|
||||||
|
│
|
||||||
|
┌────▼──────────┐
|
||||||
|
│ PostgreSQL DB │
|
||||||
|
│ (공통코드) │
|
||||||
|
└───────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API 명세
|
||||||
|
|
||||||
|
### 1. 공통코드 조회 (캐시됨)
|
||||||
|
```
|
||||||
|
GET /api/code
|
||||||
|
|
||||||
|
응답:
|
||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"code": "STATUS",
|
||||||
|
"name": "상태",
|
||||||
|
"description": "상태 공통코드",
|
||||||
|
"used": true,
|
||||||
|
...
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 공통코드 캐시 갱신
|
||||||
|
```
|
||||||
|
POST /api/code/cache/refresh
|
||||||
|
|
||||||
|
응답:
|
||||||
|
{
|
||||||
|
"data": "공통코드 캐시가 갱신되었습니다."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 캐시 상태 확인
|
||||||
|
```
|
||||||
|
GET /api/code/cache/status
|
||||||
|
|
||||||
|
응답:
|
||||||
|
{
|
||||||
|
"data": 150 // Redis에 캐시된 공통코드 개수
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 캐시 갱신 흐름
|
||||||
|
|
||||||
|
### 자동 갱신 (CRUD 작업)
|
||||||
|
```
|
||||||
|
관리자가 공통코드 등록/수정/삭제
|
||||||
|
↓
|
||||||
|
CommonCodeService.save() / update() / removeCode()
|
||||||
|
(@CacheEvict 실행)
|
||||||
|
↓
|
||||||
|
Redis 캐시 전체 삭제
|
||||||
|
↓
|
||||||
|
다음 조회 시 DB에서 새로 로드
|
||||||
|
```
|
||||||
|
|
||||||
|
### 수동 갱신 (API 호출)
|
||||||
|
```
|
||||||
|
관리자: POST /api/code/cache/refresh
|
||||||
|
↓
|
||||||
|
CommonCodeCacheManager.refreshCommonCodeCache()
|
||||||
|
↓
|
||||||
|
캐시 정리 + 새로운 데이터 로드
|
||||||
|
↓
|
||||||
|
Redis 캐시 업데이트 완료
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 성능 최적화 효과
|
||||||
|
|
||||||
|
| 항목 | 개선 전 | 개선 후 |
|
||||||
|
|------|--------|--------|
|
||||||
|
| **조회 속도** | DB 직접 조회 (10-100ms) | Redis 캐시 (1-5ms) |
|
||||||
|
| **DB 부하** | 매번 조회 | 캐시 미스시만 조회 |
|
||||||
|
| **네트워크 대역폭** | 높음 (DB 왕복) | 낮음 (로컬 캐시) |
|
||||||
|
| **응답 시간** | 변동적 | 일정 (캐시) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 추가 기능
|
||||||
|
|
||||||
|
### CommonCodeUtil - 전역 공통코드 조회
|
||||||
|
```java
|
||||||
|
@Component
|
||||||
|
public class CommonCodeUtil {
|
||||||
|
// 모든 공통코드 조회 (캐시 활용)
|
||||||
|
public List<Basic> getAllCommonCodes()
|
||||||
|
|
||||||
|
// 특정 코드로 조회
|
||||||
|
public List<Basic> getCommonCodesByCode(String code)
|
||||||
|
|
||||||
|
// ID로 단건 조회
|
||||||
|
public Optional<Basic> getCommonCodeById(Long id)
|
||||||
|
|
||||||
|
// 코드명 조회
|
||||||
|
public Optional<String> getCodeName(String parentCode, String childCode)
|
||||||
|
|
||||||
|
// 하위 코드 조회
|
||||||
|
public List<Basic> getChildCodesByParentCode(String parentCode)
|
||||||
|
|
||||||
|
// 코드 사용 가능 여부 확인
|
||||||
|
public boolean isCodeAvailable(Long parentId, String code)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 사용 예시
|
||||||
|
```java
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RestController
|
||||||
|
public class SomeController {
|
||||||
|
|
||||||
|
private final CommonCodeUtil commonCodeUtil;
|
||||||
|
|
||||||
|
@GetMapping("/example")
|
||||||
|
public void example() {
|
||||||
|
// 1. 모든 공통코드 조회 (캐시됨)
|
||||||
|
List<Basic> allCodes = commonCodeUtil.getAllCommonCodes();
|
||||||
|
|
||||||
|
// 2. 특정 코드 조회
|
||||||
|
Optional<String> name = commonCodeUtil.getCodeName("PARENT", "CHILD");
|
||||||
|
|
||||||
|
// 3. 코드 사용 가능 여부 확인
|
||||||
|
boolean available = commonCodeUtil.isCodeAvailable(1L, "NEW_CODE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 완료 체크리스트
|
||||||
|
|
||||||
|
- Redis 캐싱 어노테이션 적용 (@Cacheable, @CacheEvict)
|
||||||
|
- 애플리케이션 로딩시 캐시 초기화
|
||||||
|
- CRUD 작업시 자동 캐시 갱신
|
||||||
|
- 수동 캐시 갱신 API 제공
|
||||||
|
- 캐시 상태 모니터링 API
|
||||||
|
- 전역 공통코드 조회 유틸리티
|
||||||
|
- 포괄적인 유닛 테스트 (12개)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 모니터링
|
||||||
|
|
||||||
|
캐시 상태를 주기적으로 모니터링:
|
||||||
|
```bash
|
||||||
|
# 캐시 상태 확인
|
||||||
|
curl http://localhost:8080/api/code/cache/status
|
||||||
|
|
||||||
|
# 캐시 갱신
|
||||||
|
curl -X POST http://localhost:8080/api/code/cache/refresh
|
||||||
|
```
|
||||||
|
|
||||||
|
로그 확인:
|
||||||
|
```
|
||||||
|
=== 공통코드 캐시 초기화 시작 ===
|
||||||
|
✓ 공통코드 150개가 Redis 캐시에 로드되었습니다.
|
||||||
|
- [STATUS] 상태 (ID: 1)
|
||||||
|
- [TYPE] 타입 (ID: 2)
|
||||||
|
...
|
||||||
|
=== 공통코드 캐시 초기화 완료 ===
|
||||||
|
```
|
||||||
29
gukyuin/state-check/Dockerfile-dev
Normal file
29
gukyuin/state-check/Dockerfile-dev
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Stage 1: Build stage (gradle build는 Jenkins에서 이미 수행)
|
||||||
|
FROM eclipse-temurin:21-jre-jammy
|
||||||
|
|
||||||
|
# GDAL 설치
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
gdal-bin \
|
||||||
|
libgdal-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ARG UID=1000
|
||||||
|
ARG GID=1000
|
||||||
|
|
||||||
|
RUN groupadd -g ${GID} manager01 \
|
||||||
|
&& useradd -u ${UID} -g ${GID} -m manager01
|
||||||
|
|
||||||
|
USER manager01
|
||||||
|
|
||||||
|
# 작업 디렉토리 설정
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# JAR 파일 복사 (Jenkins에서 빌드된 ROOT.jar)
|
||||||
|
COPY build/libs/ROOT.jar app.jar
|
||||||
|
|
||||||
|
# 포트 노출
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# 애플리케이션 실행
|
||||||
|
# dev 프로파일로 실행
|
||||||
|
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=dev", "app.jar"]
|
||||||
94
gukyuin/state-check/Jenkinsfile-dev
Normal file
94
gukyuin/state-check/Jenkinsfile-dev
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
tools {
|
||||||
|
jdk 'jdk21'
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
BRANCH = 'develop'
|
||||||
|
GIT_REPO = 'https://10.100.0.10:3210/dabeeo/kamco-dabeeo-backoffice.git'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Checkout') {
|
||||||
|
steps {
|
||||||
|
checkout([
|
||||||
|
$class: 'GitSCM',
|
||||||
|
branches: [[name: "${env.BRANCH}"]],
|
||||||
|
userRemoteConfigs: [[
|
||||||
|
url: "${env.GIT_REPO}",
|
||||||
|
credentialsId: 'jenkins-dev-token'
|
||||||
|
]]
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Get Commit Hash') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
env.COMMIT_HASH = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()
|
||||||
|
echo "Current commit hash: ${env.COMMIT_HASH}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Build') {
|
||||||
|
steps {
|
||||||
|
sh "./gradlew clean build -x test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Docker Build & Deploy') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
echo "Building Docker image with tag: ${env.COMMIT_HASH}"
|
||||||
|
|
||||||
|
// IMAGE_TAG 환경변수 설정 후 docker-compose로 빌드 및 배포
|
||||||
|
sh """
|
||||||
|
export IMAGE_TAG=${env.COMMIT_HASH}
|
||||||
|
|
||||||
|
# 기존 컨테이너 중지 및 제거
|
||||||
|
docker-compose -f docker-compose-dev.yml down || true
|
||||||
|
|
||||||
|
# 새 이미지 빌드
|
||||||
|
docker-compose -f docker-compose-dev.yml build
|
||||||
|
|
||||||
|
# latest 태그도 추가
|
||||||
|
docker tag kamco-changedetection-api:${env.COMMIT_HASH} kamco-changedetection-api:latest
|
||||||
|
|
||||||
|
# 컨테이너 시작
|
||||||
|
docker-compose -f docker-compose-dev.yml up -d
|
||||||
|
"""
|
||||||
|
|
||||||
|
// 헬스체크 대기
|
||||||
|
echo "Waiting for application to be ready..."
|
||||||
|
sh """
|
||||||
|
for i in {1..30}; do
|
||||||
|
if docker exec kamco-changedetection-api curl -f http://localhost:8080/monitor/health > /dev/null 2>&1; then
|
||||||
|
echo "✅ Application is healthy!"
|
||||||
|
docker-compose -f docker-compose-dev.yml ps
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "⏳ Waiting for application... (\$i/30)"
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
echo "⚠️ Warning: Health check timeout, checking container status..."
|
||||||
|
docker-compose -f docker-compose-dev.yml ps
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Cleanup Old Images') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
echo "Cleaning up old Docker images..."
|
||||||
|
sh """
|
||||||
|
# Keep latest 5 images, remove older ones
|
||||||
|
docker images kamco-changedetection-api --format "{{.ID}} {{.Tag}}" | \
|
||||||
|
grep -v latest | tail -n +6 | awk '{print \$1}' | xargs -r docker rmi || true
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
gukyuin/state-check/README.md
Normal file
26
gukyuin/state-check/README.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# GUKYUIN STATE CHECK
|
||||||
|
|
||||||
|
> 국유인에 연동된 마스터 정보에서 이노팸에 다운로드 100%가 되었는지 상태를 체크 schedule
|
||||||
|
|
||||||
|
## 📋 프로젝트 소개
|
||||||
|
|
||||||
|
**state-check**는 국유인에 연동된 마스터 정보에서 이노팸에 다운로드 100%가 되었는지 상태를 체크하는 schedule 입니다.
|
||||||
|
|
||||||
|
## 🚀 시작하기
|
||||||
|
GukYuinApiStatusJobService 의 findGukYuinMastCompleteYn 메소드가 매 10분마다 schedule 실행됨
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./gradlew spotlessApply
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
./gradlew clean build
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
Java -jar state-check.jar \
|
||||||
|
```
|
||||||
|
### 필수 요구사항
|
||||||
|
|
||||||
|
- Java 21 (JDK 21)
|
||||||
|
- PostgreSQL 12+ (PostGIS 확장 필요)
|
||||||
|
- Gradle 8.x (또는 Gradle Wrapper 사용)
|
||||||
|
- Docker & Docker Compose (선택사항)
|
||||||
110
gukyuin/state-check/build.gradle
Normal file
110
gukyuin/state-check/build.gradle
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'org.springframework.boot' version '3.5.7'
|
||||||
|
id 'io.spring.dependency-management' version '1.1.7'
|
||||||
|
id 'com.diffplug.spotless' version '6.25.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'com.kamco.cd'
|
||||||
|
version = '0.0.1-SNAPSHOT'
|
||||||
|
description = 'state-check'
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(21)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bootJar {
|
||||||
|
archiveFileName = "state-check.jar"
|
||||||
|
}
|
||||||
|
|
||||||
|
jar {
|
||||||
|
enabled = false // plain.jar 안 만들기(혼동 방지)
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
compileOnly {
|
||||||
|
extendsFrom annotationProcessor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven { url "https://repo.osgeo.org/repository/release/" }
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||||
|
compileOnly 'org.projectlombok:lombok'
|
||||||
|
runtimeOnly 'org.postgresql:postgresql'
|
||||||
|
annotationProcessor 'org.projectlombok:lombok'
|
||||||
|
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||||
|
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||||
|
|
||||||
|
//geometry
|
||||||
|
implementation 'com.fasterxml.jackson.core:jackson-databind'
|
||||||
|
implementation "org.geotools:gt-shapefile:30.0"
|
||||||
|
implementation "org.geotools:gt-referencing:30.0"
|
||||||
|
implementation "org.geotools:gt-geojson:30.0"
|
||||||
|
implementation 'org.locationtech.jts.io:jts-io-common:1.20.0'
|
||||||
|
implementation 'org.locationtech.jts:jts-core:1.19.0'
|
||||||
|
implementation 'org.hibernate:hibernate-spatial:6.2.7.Final'
|
||||||
|
implementation 'org.geotools:gt-main:30.0'
|
||||||
|
implementation("org.geotools:gt-geotiff:30.0") {
|
||||||
|
exclude group: "javax.media", module: "jai_core"
|
||||||
|
}
|
||||||
|
implementation 'org.geotools:gt-epsg-hsql:30.0'
|
||||||
|
|
||||||
|
// QueryDSL JPA
|
||||||
|
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
|
||||||
|
|
||||||
|
// Q클래스 생성용 annotationProcessor
|
||||||
|
annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
|
||||||
|
annotationProcessor 'jakarta.annotation:jakarta.annotation-api'
|
||||||
|
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
|
||||||
|
|
||||||
|
// actuator
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||||
|
|
||||||
|
// Redis
|
||||||
|
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
|
||||||
|
|
||||||
|
// SpringDoc OpenAPI (Swagger)
|
||||||
|
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0'
|
||||||
|
|
||||||
|
// Apache Commons Compress for archive handling
|
||||||
|
implementation 'org.apache.commons:commons-compress:1.26.0'
|
||||||
|
|
||||||
|
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
|
||||||
|
implementation 'org.reflections:reflections:0.10.2'
|
||||||
|
|
||||||
|
|
||||||
|
implementation 'org.locationtech.jts:jts-core:1.19.0'
|
||||||
|
implementation 'org.locationtech.jts.io:jts-io-common:1.19.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations.configureEach {
|
||||||
|
exclude group: 'javax.media', module: 'jai_core'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('test') {
|
||||||
|
useJUnitPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spotless configuration for code formatting (2-space indent)
|
||||||
|
spotless {
|
||||||
|
java {
|
||||||
|
target 'src/**/*.java'
|
||||||
|
googleJavaFormat('1.19.2') // Default Google Style = 2 spaces (NO .aosp()!)
|
||||||
|
trimTrailingWhitespace()
|
||||||
|
endWithNewline()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run spotlessCheck before build
|
||||||
|
tasks.named('build') {
|
||||||
|
dependsOn 'spotlessCheck'
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user