pnu-update 스케줄 추가

This commit is contained in:
2026-02-03 10:30:47 +09:00
parent fa0bec8747
commit 48882c9123
183 changed files with 9650 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
package com.kamco.cd.kamcoback;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class KamcoBackApplication {
public static void main(String[] args) {
SpringApplication.run(KamcoBackApplication.class, args);
}
}

View File

@@ -0,0 +1,189 @@
package com.kamco.cd.kamcoback.code.dto;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.kamco.cd.kamcoback.common.utils.html.HtmlEscapeDeserializer;
import com.kamco.cd.kamcoback.common.utils.html.HtmlUnescapeSerializer;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.time.ZonedDateTime;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
public class CommonCodeDto {
@Schema(name = "CodeAddReq", description = "공통코드 저장 정보")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class AddReq {
@NotEmpty private String code;
@NotEmpty private String name;
private String description;
private int order;
private boolean used;
private Long parentId;
@JsonDeserialize(using = HtmlEscapeDeserializer.class)
private String props1;
@JsonDeserialize(using = HtmlEscapeDeserializer.class)
private String props2;
@JsonDeserialize(using = HtmlEscapeDeserializer.class)
private String props3;
}
@Schema(name = "CodeModifyReq", description = "공통코드 수정 정보")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ModifyReq {
@NotEmpty private String name;
private String description;
private boolean used;
@JsonDeserialize(using = HtmlEscapeDeserializer.class)
private String props1;
@JsonDeserialize(using = HtmlEscapeDeserializer.class)
private String props2;
@JsonDeserialize(using = HtmlEscapeDeserializer.class)
private String props3;
}
@Schema(name = "CodeOrderReq", description = "공통코드 순서 변경 정보")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class OrderReq {
@NotNull private Long id;
@NotNull private Integer order;
}
@Schema(name = "CommonCode Basic", description = "공통코드 기본 정보")
@Getter
public static class Basic {
private Long id;
private String code;
private String description;
private String name;
private Integer order;
private Boolean used;
private Boolean deleted;
private List<CommonCodeDto.Basic> children;
@JsonFormatDttm private ZonedDateTime createdDttm;
@JsonFormatDttm private ZonedDateTime updatedDttm;
@JsonSerialize(using = HtmlUnescapeSerializer.class)
private String props1;
@JsonSerialize(using = HtmlUnescapeSerializer.class)
private String props2;
@JsonSerialize(using = HtmlUnescapeSerializer.class)
private String props3;
@JsonFormatDttm private ZonedDateTime deletedDttm;
public Basic(
Long id,
String code,
String description,
String name,
Integer order,
Boolean used,
Boolean deleted,
List<CommonCodeDto.Basic> children,
ZonedDateTime createdDttm,
ZonedDateTime updatedDttm,
String props1,
String props2,
String props3,
ZonedDateTime deletedDttm) {
this.id = id;
this.code = code;
this.description = description;
this.name = name;
this.order = order;
this.used = used;
this.deleted = deleted;
this.children = children;
this.createdDttm = createdDttm;
this.updatedDttm = updatedDttm;
this.props1 = props1;
this.props2 = props2;
this.props3 = props3;
this.deletedDttm = deletedDttm;
}
}
@Schema(name = "SearchReq", description = "검색 요청")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class SearchReq {
// 검색 조건
private String name;
// 페이징 파라미터
private int page = 0;
private int size = 20;
private String sort;
public Pageable toPageable() {
if (sort != null && !sort.isEmpty()) {
String[] sortParams = sort.split(",");
String property = sortParams[0];
Sort.Direction direction =
sortParams.length > 1 ? Sort.Direction.fromString(sortParams[1]) : Sort.Direction.ASC;
return PageRequest.of(page, size, Sort.by(direction, property));
}
return PageRequest.of(page, size);
}
}
@Getter
public static class Clazzes {
private String code;
private String name;
private Integer order;
private String color;
public Clazzes(String code, String name, Integer order, String color) {
this.code = code;
this.name = name;
this.order = order;
this.color = color;
}
}
@Getter
@AllArgsConstructor
public static class CodeDto {
private String code;
private String name;
}
}

View File

@@ -0,0 +1,61 @@
package com.kamco.cd.kamcoback.common.utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.InetAddress;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.http.HttpHeaders;
public class NetUtils {
public String getLocalIP() {
String ip;
{
try {
InetAddress local = InetAddress.getLocalHost();
ip = local.getHostAddress();
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
return ip;
}
public String dtoToQueryString(Object dto, String queryString) {
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> map = objectMapper.convertValue(dto, Map.class);
String qStr =
map.entrySet().stream()
.filter(entry -> entry.getValue() != null) // null 제외
.map(
entry ->
String.format(
"%s=%s",
entry.getKey(),
URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8)))
.collect(Collectors.joining("&"));
if (queryString == null || queryString.isEmpty()) {
queryString = "?" + qStr;
} else {
queryString = queryString + "&" + qStr;
}
// 2. Map을 쿼리 스트링 문자열로 변환
return queryString;
}
public HttpHeaders jsonHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.ACCEPT, "application/json;charset=UTF-8");
headers.set(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
return headers;
}
}

View File

@@ -0,0 +1,54 @@
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);
}
}

View File

@@ -0,0 +1,20 @@
package com.kamco.cd.kamcoback.common.utils.enums;
public class CodeDto {
private String code;
private String name;
public CodeDto(String code, String name) {
this.code = code;
this.name = name;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,10 @@
package com.kamco.cd.kamcoback.common.utils.enums;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CodeExpose {}

View File

@@ -0,0 +1,10 @@
package com.kamco.cd.kamcoback.common.utils.enums;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CodeHidden {}

View File

@@ -0,0 +1,8 @@
package com.kamco.cd.kamcoback.common.utils.enums;
public interface EnumType {
String getId();
String getText();
}

View File

@@ -0,0 +1,26 @@
package com.kamco.cd.kamcoback.common.utils.enums;
import com.kamco.cd.kamcoback.common.utils.interfaces.EnumValid;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
public class EnumValidator implements ConstraintValidator<EnumValid, String> {
private Set<String> acceptedValues;
@Override
public void initialize(EnumValid constraintAnnotation) {
acceptedValues =
Arrays.stream(constraintAnnotation.enumClass().getEnumConstants())
.map(Enum::name)
.collect(Collectors.toSet());
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return value != null && acceptedValues.contains(value);
}
}

View File

@@ -0,0 +1,84 @@
package com.kamco.cd.kamcoback.common.utils.enums;
import com.kamco.cd.kamcoback.code.dto.CommonCodeDto.CodeDto;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.reflections.Reflections;
public class Enums {
private static final String BASE_PACKAGE = "com.kamco.cd.kamcoback";
/** 노출 가능한 enum만 모아둔 맵 key: enum simpleName (예: RoleType) value: enum Class */
private static final Map<String, Class<? extends Enum<?>>> exposedEnumMap = scanExposedEnumMap();
// code로 enum 찾기
public static <E extends Enum<E> & EnumType> E fromId(Class<E> enumClass, String id) {
if (id == null) {
return null;
}
for (E e : enumClass.getEnumConstants()) {
if (id.equalsIgnoreCase(e.getId())) {
return e;
}
}
return null;
}
// enum -> CodeDto list
public static List<CodeDto> toList(Class<? extends Enum<?>> enumClass) {
Object[] enums = enumClass.getEnumConstants();
return Arrays.stream(enums)
.map(e -> (EnumType) e)
.filter(e -> !isHidden(enumClass, (Enum<?>) e))
.map(e -> new CodeDto(e.getId(), e.getText()))
.toList();
}
private static boolean isHidden(Class<? extends Enum<?>> enumClass, Enum<?> e) {
try {
return enumClass.getField(e.name()).isAnnotationPresent(CodeHidden.class);
} catch (NoSuchFieldException ex) {
return false;
}
}
/** 특정 타입(enum)만 조회 /codes/{type} -> type = RoleType 같은 값 */
public static List<CodeDto> getCodes(String type) {
Class<? extends Enum<?>> enumClass = exposedEnumMap.get(type);
if (enumClass == null) {
throw new IllegalArgumentException("지원하지 않는 코드 타입: " + type);
}
return toList(enumClass);
}
/** 전체 enum 코드 조회 */
public static Map<String, List<CodeDto>> getAllCodes() {
Map<String, List<CodeDto>> result = new HashMap<>();
for (Map.Entry<String, Class<? extends Enum<?>>> e : exposedEnumMap.entrySet()) {
result.put(e.getKey(), toList(e.getValue()));
}
return result;
}
/** CodeExpose + EnumType 인 enum만 스캔해서 Map 구성 */
private static Map<String, Class<? extends Enum<?>>> scanExposedEnumMap() {
Reflections reflections = new Reflections(BASE_PACKAGE);
Set<Class<?>> types = reflections.getTypesAnnotatedWith(CodeExpose.class);
Map<String, Class<? extends Enum<?>>> result = new HashMap<>();
for (Class<?> clazz : types) {
if (clazz.isEnum() && EnumType.class.isAssignableFrom(clazz)) {
result.put(clazz.getSimpleName(), (Class<? extends Enum<?>>) clazz);
}
}
return result;
}
}

View File

@@ -0,0 +1,41 @@
package com.kamco.cd.kamcoback.common.utils.enums;
import java.util.Arrays;
import lombok.AllArgsConstructor;
import lombok.Getter;
@CodeExpose
@Getter
@AllArgsConstructor
public enum ImageryFitStatus implements EnumType {
FIT("적합"),
UNFIT("부적합");
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
public static ImageryFitStatus fromCode(String code) {
if (code == null) {
return null;
}
String c = code.trim();
return Arrays.stream(values())
.filter(v -> v.name().equalsIgnoreCase(c))
.findFirst()
.orElse(null);
}
public static String getDescByCode(String code) {
ImageryFitStatus status = fromCode(code);
return status != null ? status.getDesc() : null;
}
}

View File

@@ -0,0 +1,18 @@
package com.kamco.cd.kamcoback.common.utils.html;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import org.springframework.web.util.HtmlUtils;
public class HtmlEscapeDeserializer extends JsonDeserializer<Object> {
@Override
public Object deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JacksonException {
String value = jsonParser.getValueAsString();
return value == null ? null : HtmlUtils.htmlEscape(value);
}
}

View File

@@ -0,0 +1,20 @@
package com.kamco.cd.kamcoback.common.utils.html;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import org.springframework.web.util.HtmlUtils;
public class HtmlUnescapeSerializer extends JsonSerializer<String> {
@Override
public void serialize(
String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
if (value == null) {
jsonGenerator.writeNull();
} else {
jsonGenerator.writeString(HtmlUtils.htmlUnescape(value));
}
}
}

View File

@@ -0,0 +1,23 @@
package com.kamco.cd.kamcoback.common.utils.interfaces;
import com.kamco.cd.kamcoback.common.utils.enums.EnumValidator;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValidator.class)
public @interface EnumValid {
String message() default "올바르지 않은 값입니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<? extends Enum<?>> enumClass();
}

View File

@@ -0,0 +1,15 @@
package com.kamco.cd.kamcoback.common.utils.interfaces;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.lang.annotation.*;
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@JacksonAnnotationsInside
@JsonFormat(
shape = JsonFormat.Shape.STRING,
pattern = "yyyy-MM-dd'T'HH:mm:ssXXX",
timezone = "Asia/Seoul")
public @interface JsonFormatDttm {}

View File

@@ -0,0 +1,18 @@
package com.kamco.cd.kamcoback.config;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuerydslConfig {
@PersistenceContext private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}

View File

@@ -0,0 +1,153 @@
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);
}
}

View File

@@ -0,0 +1,120 @@
package com.kamco.cd.kamcoback.config.resttemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.nio.charset.StandardCharsets;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
@RequiredArgsConstructor
@Component
@Log4j2
public class ExternalHttpClient {
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
public <T> ExternalCallResult<T> call(
String url, HttpMethod method, Object body, HttpHeaders headers, Class<T> responseType) {
// responseType 기반으로 Accept 동적 세팅
HttpHeaders resolvedHeaders = resolveHeaders(headers, responseType);
logRequestBody(body);
HttpEntity<Object> entity = new HttpEntity<>(body, resolvedHeaders);
try {
// String: raw bytes -> UTF-8 string
if (responseType == String.class) {
ResponseEntity<byte[]> res = restTemplate.exchange(url, method, entity, byte[].class);
String raw =
(res.getBody() == null) ? null : new String(res.getBody(), StandardCharsets.UTF_8);
@SuppressWarnings("unchecked")
T casted = (T) raw;
return new ExternalCallResult<>(res.getStatusCodeValue(), true, casted, null);
}
// byte[]: raw bytes로 받고, JSON이면 에러로 처리
if (responseType == byte[].class) {
ResponseEntity<byte[]> res = restTemplate.exchange(url, method, entity, byte[].class);
MediaType ct = res.getHeaders().getContentType();
byte[] bytes = res.getBody();
if (isJsonLike(ct)) {
String err = (bytes == null) ? null : new String(bytes, StandardCharsets.UTF_8);
return new ExternalCallResult<>(res.getStatusCodeValue(), false, null, err);
}
@SuppressWarnings("unchecked")
T casted = (T) bytes;
return new ExternalCallResult<>(res.getStatusCodeValue(), true, casted, null);
}
// DTO 등: 일반 역직렬화
ResponseEntity<T> res = restTemplate.exchange(url, method, entity, responseType);
return new ExternalCallResult<>(res.getStatusCodeValue(), true, res.getBody(), null);
} catch (HttpStatusCodeException e) {
return new ExternalCallResult<>(
e.getStatusCode().value(), false, null, e.getResponseBodyAsString());
}
}
// 기존 resolveJsonHeaders를 "동적"으로 교체
private HttpHeaders resolveHeaders(HttpHeaders headers, Class<?> responseType) {
// 원본 headers를 그대로 쓰면 외부에서 재사용할 때 사이드이펙트 날 수 있어서 복사 권장
HttpHeaders h = (headers == null) ? new HttpHeaders() : new HttpHeaders(headers);
// 요청 바디 기본은 JSON이라고 가정 (필요하면 호출부에서 덮어쓰기)
if (h.getContentType() == null) {
h.setContentType(MediaType.APPLICATION_JSON);
}
// 호출부에서 Accept를 명시했으면 존중
if (h.getAccept() != null && !h.getAccept().isEmpty()) {
return h;
}
// responseType 기반 Accept 자동 지정
if (responseType == byte[].class) {
h.setAccept(
List.of(
MediaType.APPLICATION_OCTET_STREAM,
MediaType.valueOf("application/zip"),
MediaType.APPLICATION_JSON // 실패(JSON 에러 바디) 대비
));
} else {
h.setAccept(List.of(MediaType.APPLICATION_JSON));
}
return h;
}
private boolean isJsonLike(MediaType ct) {
if (ct == null) return false;
return ct.includes(MediaType.APPLICATION_JSON)
|| "application/problem+json".equalsIgnoreCase(ct.toString());
}
private void logRequestBody(Object body) {
try {
if (body != null) {
log.info("[HTTP-REQ-BODY-JSON] {}", objectMapper.writeValueAsString(body));
}
} catch (Exception e) {
log.warn("[HTTP-REQ-BODY-JSON] serialize failed: {}", e.getMessage());
}
}
public record ExternalCallResult<T>(int statusCode, boolean success, T body, String errBody) {}
}

View File

@@ -0,0 +1,223 @@
package com.kamco.cd.kamcoback.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.kamco.cd.kamcoback.inferface.EnumType;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.springframework.http.HttpStatus;
@Getter
@ToString
public class ApiResponseDto<T> {
private T data;
@JsonInclude(JsonInclude.Include.NON_NULL)
private Error error;
@JsonInclude(JsonInclude.Include.NON_NULL)
private T errorData;
@JsonIgnore private HttpStatus httpStatus;
@JsonIgnore private Long errorLogUid;
public ApiResponseDto(T data) {
this.data = data;
}
private ApiResponseDto(T data, HttpStatus httpStatus) {
this.data = data;
this.httpStatus = httpStatus;
}
public ApiResponseDto(ApiResponseCode code) {
this.error = new Error(code.getId(), code.getMessage());
}
public ApiResponseDto(ApiResponseCode code, String message) {
this.error = new Error(code.getId(), message);
}
public ApiResponseDto(ApiResponseCode code, String message, HttpStatus httpStatus) {
this.error = new Error(code.getId(), message);
this.httpStatus = httpStatus;
}
public ApiResponseDto(
ApiResponseCode code, String message, HttpStatus httpStatus, Long errorLogUid) {
this.error = new Error(code.getId(), message);
this.httpStatus = httpStatus;
this.errorLogUid = errorLogUid;
}
public ApiResponseDto(ApiResponseCode code, String message, T errorData) {
this.error = new Error(code.getId(), message);
this.errorData = errorData;
}
// HTTP 상태 코드가 내장된 ApiResponseDto 반환 메서드들
public static <T> ApiResponseDto<T> createOK(T data) {
return new ApiResponseDto<>(data, HttpStatus.CREATED);
}
public static <T> ApiResponseDto<T> ok(T data) {
return new ApiResponseDto<>(data, HttpStatus.OK);
}
public static <T> ApiResponseDto<ResponseObj> okObject(ResponseObj data) {
if (data.getCode().equals(ApiResponseCode.OK)) {
return new ApiResponseDto<>(data, HttpStatus.NO_CONTENT);
} else {
return new ApiResponseDto<>(data.getCode(), data.getMessage(), HttpStatus.CONFLICT);
}
}
public static <T> ApiResponseDto<T> deleteOk(T data) {
return new ApiResponseDto<>(data, HttpStatus.NO_CONTENT);
}
public static ApiResponseDto<String> createException(ApiResponseCode code) {
return new ApiResponseDto<>(code);
}
public static ApiResponseDto<String> createException(ApiResponseCode code, String message) {
return new ApiResponseDto<>(code, message);
}
public static ApiResponseDto<String> createException(
ApiResponseCode code, String message, HttpStatus httpStatus) {
return new ApiResponseDto<>(code, message, httpStatus);
}
public static ApiResponseDto<String> createException(
ApiResponseCode code, String message, HttpStatus httpStatus, Long errorLogUid) {
return new ApiResponseDto<>(code, message, httpStatus, errorLogUid);
}
public static <T> ApiResponseDto<T> createException(
ApiResponseCode code, String message, T data) {
return new ApiResponseDto<>(code, message, data);
}
@Getter
public static class Error {
private final String code;
private final String message;
public Error(String code, String message) {
this.code = code;
this.message = message;
}
}
/** Error가 아닌 Business상 성공이거나 실패인 경우, 메세지 함께 전달하기 위한 object */
@Getter
public static class ResponseObj {
private final ApiResponseCode code;
private final String message;
public ResponseObj(ApiResponseCode code, String message) {
this.code = code;
this.message = message;
}
}
@Getter
@RequiredArgsConstructor
public enum ApiResponseCode implements EnumType {
// @formatter:off
OK("요청이 성공하였습니다."),
BAD_REQUEST("요청 파라미터가 잘못되었습니다."),
BAD_GATEWAY("네트워크 상태가 불안정합니다."),
ALREADY_EXIST_MALL("이미 등록된 쇼핑센터입니다."),
NOT_FOUND_MAP("지도를 찾을 수 없습니다."),
UNAUTHORIZED("권한이 없습니다."),
CONFLICT("이미 등록된 컨텐츠입니다."),
NOT_FOUND("Resource를 찾을 수 없습니다."),
NOT_FOUND_DATA("데이터를 찾을 수 없습니다."),
NOT_FOUND_WEATHER_DATA("날씨 데이터를 찾을 수 없습니다."),
FAIL_SEND_MESSAGE("메시지를 전송하지 못했습니다."),
TOO_MANY_CONNECTED_MACHINES("연결된 기기가 너무 많습니다."),
UNAUTHENTICATED("인증에 실패하였습니다."),
INVALID_TOKEN("잘못된 토큰입니다."),
EXPIRED_TOKEN("만료된 토큰입니다."),
INTERNAL_SERVER_ERROR("서버에 문제가 발생 하였습니다."),
FORBIDDEN("권한을 확인해주세요."),
INVALID_PASSWORD("잘못된 비밀번호 입니다."),
NOT_FOUND_CAR_IN("입차정보가 없습니다."),
WRONG_STATUS("잘못된 상태입니다."),
FAIL_VERIFICATION("인증에 실패하였습니다."),
INVALID_EMAIL("잘못된 형식의 이메일입니다."),
REQUIRED_EMAIL("이메일은 필수 항목입니다."),
WRONG_PASSWORD("잘못된 패스워드입니다."),
DUPLICATE_EMAIL("이미 가입된 이메일입니다."),
DUPLICATE_DATA("이미 등록되어 있습니다."),
DATA_INTEGRITY_ERROR("데이터 무결성이 위반되어 요청을 처리할수 없습니다."),
FOREIGN_KEY_ERROR("참조 중인 데이터가 있어 삭제할 수 없습니다."),
DUPLICATE_EMPLOYEEID("이미 가입된 사번입니다."),
NOT_FOUND_USER_FOR_EMAIL("이메일로 유저를 찾을 수 없습니다."),
NOT_FOUND_USER("사용자를 찾을 수 없습니다."),
UNPROCESSABLE_ENTITY("이 데이터는 삭제할 수 없습니다."),
LOGIN_ID_NOT_FOUND("아이디를 잘못 입력하셨습니다."),
LOGIN_PASSWORD_MISMATCH("비밀번호를 잘못 입력하셨습니다."),
LOGIN_PASSWORD_EXCEEDED("비밀번호 오류 횟수를 초과하여 이용하실 수 없습니다.\n로그인 오류에 대해 관리자에게 문의하시기 바랍니다."),
INACTIVE_ID("사용할 수 없는 계정입니다."),
INVALID_EMAIL_TOKEN(
"You can only reset your password within 24 hours from when the email was sent.\n"
+ "To reset your password again, please submit a new request through \"Forgot"
+ " Password.\""),
PAYLOAD_TOO_LARGE("업로드 용량 제한을 초과했습니다."),
NOT_FOUND_TARGET_YEAR("기준년도 도엽을 찾을 수 없습니다."),
NOT_FOUND_COMPARE_YEAR("비교년도 도엽을 찾을 수 없습니다."),
FAIL_SAVE_MAP_SHEET("도엽 저장 중 오류가 발생했습니다."),
FAIL_CREATE_MAP_SHEET_FILE("도엽 설정파일 생성 중 오류가 발생했습니다."),
;
// @formatter:on
private final String message;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return message;
}
public static ApiResponseCode getCode(String name) {
return ApiResponseCode.valueOf(name.toUpperCase());
}
public static String getMessage(String name) {
return ApiResponseCode.valueOf(name.toUpperCase()).getText();
}
public static ApiResponseCode from(String codeName, HttpStatus status) {
if (codeName != null && !codeName.isBlank()) {
try {
return ApiResponseCode.valueOf(codeName.toUpperCase());
} catch (IllegalArgumentException ignore) {
// fallback
}
}
if (status != null) {
try {
return ApiResponseCode.valueOf(status.name());
} catch (IllegalArgumentException ignore) {
// fallback
}
}
return INTERNAL_SERVER_ERROR;
}
}
}

View File

@@ -0,0 +1,362 @@
package com.kamco.cd.kamcoback.label.dto;
import com.kamco.cd.kamcoback.common.utils.enums.CodeExpose;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
public class LabelAllocateDto {
@CodeExpose
@Getter
@AllArgsConstructor
public enum LabelMngState implements EnumType {
PENDING("작업대기"),
ASSIGNED("작업할당"),
ING("진행중"),
FINISH("종료");
private String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
@CodeExpose
@Getter
@AllArgsConstructor
public enum LabelState implements EnumType {
WAIT("대기"),
ASSIGNED("배정"),
SKIP("스킵"),
DONE("완료");
private String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
@CodeExpose
@Getter
@AllArgsConstructor
public enum InspectState implements EnumType {
UNCONFIRM("미확인"),
EXCEPT("제외"),
COMPLETE("완료");
private String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
@Getter
@Setter
@AllArgsConstructor
public static class AllocateDto {
@Schema(
description = "회차 마스터 key",
example = "f97dc186-e6d3-4645-9737-3173dde8dc64",
required = true)
private UUID uuid;
@Schema(description = "비교년도-기준년도", example = "2022-2024", required = true)
private String yyyy;
// @Schema(description = "비교년도", example = "2022", required = true)
// private Integer compareYyyy;
//
// @Schema(description = "기준년도", example = "2024", required = true)
// private Integer targetYyyy;
@Schema(description = "회차", example = "4")
private Integer stage;
@Schema(
description = "라벨러 할당 목록",
example =
"""
[
{
"userId": "123456",
"demand": 1000
},
{
"userId": "010222297501",
"demand": 400
},
{
"userId": "01022223333",
"demand": 440
}
]
""")
private List<TargetUser> labelers;
@Schema(
description = "검수자 할당 목록",
example =
"""
["K20251216001",
"01022225555",
"K20251212001"
]
""")
private List<String> inspectors;
}
@Getter
public static class TargetUser {
@Schema(description = "라벨러 사번", example = "labeler44")
private final String userId;
@Schema(description = "할당 건수", example = "200")
private final int demand;
public TargetUser(String userId, int demand) {
this.userId = userId;
this.demand = demand;
}
}
@Getter
@AllArgsConstructor
public static class TargetInspector {
@Schema(description = "검수자 사번", example = "K20251212001")
private final String inspectorUid;
@Schema(description = "할당 명수", example = "3")
private int userCount;
}
@Getter
@Setter
@AllArgsConstructor
public static class Basic {
private UUID assignmentUid;
private Long inferenceGeomUid;
private String workerUid;
private String inspectorUid;
private String workState;
private Character stagnationYn;
private String assignGroupId;
private Long learnGeomUid;
private Long analUid;
private ZonedDateTime createdDttm;
private ZonedDateTime updatedDttm;
private String inspectState;
private ZonedDateTime workStatDttm;
private ZonedDateTime inspectStatDttm;
}
@Getter
@Setter
@AllArgsConstructor
public static class UserList {
private String userRole;
private String employeeNo;
private String name;
}
@Getter
@Setter
@AllArgsConstructor
public static class InferenceDetail {
private String analTitle;
private Integer stage;
private ZonedDateTime gukyuinDttm;
private Long count;
}
@Getter
@Setter
@AllArgsConstructor
public static class LabelerDetail {
private String roleType;
private String name;
private String userId; // 사번
private Long count;
private Long completeCnt;
private Long skipCnt;
private Double percent;
private Integer ranking;
private ZonedDateTime createdDttm;
private String ownerName;
private Long remainCnt;
}
@Getter
@Setter
@AllArgsConstructor
public static class AllocateMoveDto {
@Schema(description = "총 잔여 건수", example = "5061")
private Integer totalCnt;
@Schema(
description = "이관할 라벨러",
example = """
[
"87654321"
]
""")
private List<String> labelers;
@Schema(description = "회차 마스터 key", example = "f97dc186-e6d3-4645-9737-3173dde8dc64")
private String uuid;
@Schema(description = "대상 사번", example = "01022223333")
private String userId;
}
@Getter
@Setter
@AllArgsConstructor
public static class AllocateInfoDto {
private Long geoUid;
private Long mapSheetNum;
private Long pnu;
}
@Getter
@Setter
@AllArgsConstructor
public static class LabelingStatDto {
private String workDate;
private Long dailyTotalCnt;
private Long totalCnt;
private Long assignedCnt;
private Long skipCnt;
private Long completeCnt;
private Long remainCnt;
}
@Schema(name = "searchReq", description = "일자별 작업 목록 요청")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class searchReq {
// 페이징 파라미터
private int page = 0;
private int size = 20;
private String sort;
public Pageable toPageable() {
if (sort != null && !sort.isEmpty()) {
String[] sortParams = sort.split(",");
String property = sortParams[0];
Sort.Direction direction =
sortParams.length > 1 ? Sort.Direction.fromString(sortParams[1]) : Sort.Direction.ASC;
return PageRequest.of(page, size, Sort.by(direction, property));
}
return PageRequest.of(page, size);
}
}
@Getter
@Setter
@AllArgsConstructor
public static class MoveUserList {
private String userRole;
private String employeeNo;
private String name;
private Long remainCnt;
private Double percent;
}
@Getter
@Setter
@AllArgsConstructor
public static class MoveInfo {
private Long totalCnt;
private List<MoveUserList> moveUserList;
}
@Schema(name = "WorkHistoryDto", description = "WorkHistoryDto")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class WorkHistoryDto {
@Schema(description = "행 번호")
private Integer rowNum;
@Schema(description = "변화탐지년도", example = "2021-2022")
private String changeDetectionYear;
@Schema(description = "국유IN 회차")
private Long stage;
@Schema(description = "반영일")
private ZonedDateTime gukyuinApplyDttm;
@Schema(description = "할당건수")
private Long assignedCnt;
@Schema(description = "완료건수")
private Long completeCnt;
@Schema(description = "Skip건수")
private Long skipCnt;
@Schema(description = "잔여건수")
private Long remainCnt;
@Schema(description = "상태 (진행중/완료)")
private String status;
@Schema(description = "진행률 (%)")
private Double percent;
@Schema(description = "작업기간 시작일")
private ZonedDateTime createdDttm;
@Schema(description = "작업기간 종료일")
private ZonedDateTime projectCloseDttm;
}
}

View File

@@ -0,0 +1,22 @@
package com.kamco.cd.kamcoback.label.dto;
import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
public class LabelInspectorDto {
@Getter
@Setter
@AllArgsConstructor
public static class Basic {
private UUID operatorUid;
private Long analUid;
private String inspectorUid;
private ZonedDateTime createdDttm;
private ZonedDateTime updatedDttm;
}
}

View File

@@ -0,0 +1,27 @@
package com.kamco.cd.kamcoback.label.dto;
import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
public class LabelLabelerDto {
@Getter
@Setter
@AllArgsConstructor
public static class Basic {
private UUID lbUsrUid;
private Long analUid;
private String workerUid;
private Long allocateCnt;
private Boolean deleted;
private Boolean reAllocateYn;
private Long reAllocateCnt;
private String reAllocateWorkerUid;
private ZonedDateTime createdDttm;
private ZonedDateTime updatedDttm;
}
}

View File

@@ -0,0 +1,137 @@
package com.kamco.cd.kamcoback.scheduler.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.kamco.cd.kamcoback.scheduler.dto.TrainingDataReviewJobDto.CompleteLabelData.GeoJsonFeature;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
public class TrainingDataReviewJobDto {
@Getter
@Setter
@RequiredArgsConstructor
@AllArgsConstructor
public static class Tasks {
private UUID assignmentUid;
private Long inferenceUid;
private Long analUid;
}
@Getter
@Setter
@RequiredArgsConstructor
@AllArgsConstructor
public static class InspectorPendingDto {
String inspectorUid;
Long pendingCount;
}
@Getter
@Setter
@RequiredArgsConstructor
@AllArgsConstructor
public static class AnalCntInfo {
Long analUid;
String resultUid;
Long allCnt;
Long completeCnt;
Long fileCnt;
}
@Getter
@Setter
@RequiredArgsConstructor
@AllArgsConstructor
public static class AnalMapSheetList {
private Integer compareYyyy;
private Integer targetYyyy;
private String mapSheetNum;
}
@Getter
@Setter
@JsonPropertyOrder({"type", "features"})
public static class FeatureCollection {
private final String type = "FeatureCollection";
private List<GeoJsonFeature> features;
public FeatureCollection(List<GeoJsonFeature> features) {
this.features = features;
}
}
@Getter
@Setter
@JsonPropertyOrder({"type", "geometry", "properties"})
public static class CompleteLabelData {
private Long geoUid;
private String type;
@JsonIgnore private String geomStr;
private JsonNode geometry;
private Properties properties;
public CompleteLabelData(Long geoUid, String type, String geomStr, Properties properties) {
this.geoUid = geoUid;
this.type = type;
this.geomStr = geomStr;
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = null;
try {
if (geomStr != null) {
jsonNode = mapper.readTree(this.geomStr);
}
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
this.geometry = jsonNode;
if (jsonNode != null && jsonNode.isObject()) {
((ObjectNode) jsonNode).remove("crs");
}
this.properties = properties;
}
@Getter
@Setter
@RequiredArgsConstructor
@AllArgsConstructor
public static class Properties {
private String modelId;
private String before;
private String after;
}
@Getter
@AllArgsConstructor
public static class GeoJsonFeature {
private String type;
private JsonNode geometry;
private Properties properties;
public static GeoJsonFeature from(CompleteLabelData data) {
return new GeoJsonFeature(
data.getType(),
data.getGeometry(), // geoUid 없음
data.getProperties());
}
}
}
}

View File

@@ -0,0 +1,22 @@
package com.kamco.cd.kamcoback.enums;
import lombok.EqualsAndHashCode;
import lombok.Getter;
public class ApiConfigEnum {
@Getter
@EqualsAndHashCode(of = "enumValue")
public static class EnumDto<T> {
private final T enumValue;
private final String id;
private final String text;
public EnumDto(T enumValue, String id, String text) {
this.enumValue = enumValue;
this.id = id;
this.text = text;
}
}
}

View File

@@ -0,0 +1,20 @@
package com.kamco.cd.kamcoback.enums;
public class CodeDto {
private String code;
private String name;
public CodeDto(String code, String name) {
this.code = code;
this.name = name;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,46 @@
package com.kamco.cd.kamcoback.enums;
import com.kamco.cd.kamcoback.enums.ApiConfigEnum.EnumDto;
import com.kamco.cd.kamcoback.inferface.EnumType;
import java.util.Arrays;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Common usage status used across the system.
*
* <p>This enum represents whether a resource is active, excluded from processing, or inactive. It
* is commonly used for filtering, business rules, and status management.
*/
@Getter
@AllArgsConstructor
public enum CommonUseStatus implements EnumType {
// @formatter:off
USE("USE", "사용중", 100)
/** Actively used and available */
,
EXCEPT("EXCEPT", "영구 추론제외", 200)
/** Explicitly excluded from use or processing */
,
AUTO_EXCEPT("AUTO_EXCEPT", "자동추론 제외", 300),
NOT_USE("NOT_USE", "사용안함", 999)
/** Not used or disabled */
;
// @formatter:on
private String id;
private String text;
private int ordering;
public static CommonUseStatus getEnumById(String id) {
return Arrays.stream(CommonUseStatus.values())
.filter(x -> x.getId().equals(id))
.findFirst()
.orElse(CommonUseStatus.NOT_USE);
}
public EnumDto<CommonUseStatus> getEnumDto() {
return new EnumDto<>(this, this.id, this.text);
}
}

View File

@@ -0,0 +1,86 @@
package com.kamco.cd.kamcoback.enums;
import com.kamco.cd.kamcoback.inferface.CodeExpose;
import com.kamco.cd.kamcoback.inferface.CodeHidden;
import com.kamco.cd.kamcoback.inferface.EnumType;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.reflections.Reflections;
public class Enums {
private static final String BASE_PACKAGE = "com.kamco.cd.kamcoback";
/** 노출 가능한 enum만 모아둔 맵 key: enum simpleName (예: RoleType) value: enum Class */
private static final Map<String, Class<? extends Enum<?>>> exposedEnumMap = scanExposedEnumMap();
// code로 enum 찾기
public static <E extends Enum<E> & EnumType> E fromId(Class<E> enumClass, String id) {
if (id == null) {
return null;
}
for (E e : enumClass.getEnumConstants()) {
if (id.equalsIgnoreCase(e.getId())) {
return e;
}
}
return null;
}
// enum -> CodeDto list
public static List<CodeDto> toList(Class<? extends Enum<?>> enumClass) {
Object[] enums = enumClass.getEnumConstants();
return Arrays.stream(enums)
.map(e -> (EnumType) e)
.filter(e -> !isHidden(enumClass, (Enum<?>) e))
.map(e -> new CodeDto(e.getId(), e.getText()))
.toList();
}
private static boolean isHidden(Class<? extends Enum<?>> enumClass, Enum<?> e) {
try {
return enumClass.getField(e.name()).isAnnotationPresent(CodeHidden.class);
} catch (NoSuchFieldException ex) {
return false;
}
}
/** 특정 타입(enum)만 조회 /codes/{type} -> type = RoleType 같은 값 */
public static List<CodeDto> getCodes(String type) {
Class<? extends Enum<?>> enumClass = exposedEnumMap.get(type);
if (enumClass == null) {
throw new IllegalArgumentException("지원하지 않는 코드 타입: " + type);
}
return toList(enumClass);
}
/** 전체 enum 코드 조회 */
public static Map<String, List<CodeDto>> getAllCodes() {
Map<String, List<CodeDto>> result = new HashMap<>();
for (Map.Entry<String, Class<? extends Enum<?>>> e : exposedEnumMap.entrySet()) {
result.put(e.getKey(), toList(e.getValue()));
}
return result;
}
/** CodeExpose + EnumType 인 enum만 스캔해서 Map 구성 */
private static Map<String, Class<? extends Enum<?>>> scanExposedEnumMap() {
Reflections reflections = new Reflections(BASE_PACKAGE);
Set<Class<?>> types = reflections.getTypesAnnotatedWith(CodeExpose.class);
Map<String, Class<? extends Enum<?>>> result = new HashMap<>();
for (Class<?> clazz : types) {
if (clazz.isEnum() && EnumType.class.isAssignableFrom(clazz)) {
result.put(clazz.getSimpleName(), (Class<? extends Enum<?>>) clazz);
}
}
return result;
}
}

View File

@@ -0,0 +1,26 @@
package com.kamco.cd.kamcoback.enums;
import com.kamco.cd.kamcoback.inferface.EnumType;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum MngStateType implements EnumType {
NOTYET("동기화 시작"),
PROCESSING("데이터 체크"),
DONE("동기화 작업 종료"),
TAKINGERROR("오류 데이터 처리중");
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}

View File

@@ -0,0 +1,33 @@
package com.kamco.cd.kamcoback.enums;
import com.kamco.cd.kamcoback.inferface.CodeExpose;
import com.kamco.cd.kamcoback.inferface.CodeHidden;
import com.kamco.cd.kamcoback.inferface.EnumType;
import lombok.AllArgsConstructor;
import lombok.Getter;
@CodeExpose
@Getter
@AllArgsConstructor
public enum SyncStateType implements EnumType {
@CodeHidden
NOTYET("미처리"),
NOFILE("파일없음"),
NOTPAIR("페어파일누락"),
DUPLICATE("파일중복"),
TYPEERROR("손상파일"),
@CodeHidden
DONE("완료");
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}

View File

@@ -0,0 +1,117 @@
package com.kamco.cd.kamcoback.gukyuin.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class ChngDetectContDto {
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ContBasic {
private String chnDtctMstId; // 탐지콘텐츠아이디
private String chnDtctContId; // 탐지마스타아이디
private String cprsYr; // 비교년도 2023
private String crtrYr; // 기준년도 2024
private String chnDtctSno; // 차수 (1 | 2 | ...)
private String mpqdNo; // 도엽번호
private String chnDtctId; // 탐지아이디. UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성
private String chnDtctObjtId; // 탐지객체아이디. UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성
private String chnDtctPolygon; // 탐지객체폴리곤
private String chnDtctSqms; // 탐지객체면적
private String chnCd; // 변화코드
private String chnDtctJson; // 변화탐지JSON
private String chnDtctProb; // 변화탐지정확도
private String bfClsCd; // 이전부류코드
private String bfClsProb; // 이전분류정확도
private String afClsCd; // 이후분류코드
private String afClsProb; // 이후분류정확도
private String crtDt; // 생성일시
private String crtEpno; // 생성사원번호
private String crtIp; // 생성사원아이피
private String delYn; // 삭제여부
private String[] pnuList; // pnuList
private String reqEpno; // 요청사원번호
private String reqIp; // 요청사원아이피
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ChnDetectContReqDto {
private String cprsYr; // 비교년도 2023
private String crtrYr; // 기준년도 2024
private String chnDtctSno; // 차수 (1 | 2 | ...)
private String mpqdNo; // 도엽번호
private String chnDtctId; // 탐지아이디. UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성
private String chnDtctObjtId; // 탐지객체아이디. UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성
private String reqEpno; // 사원번호
private String reqIp;
}
@Schema(name = "ResReturn", description = "수행 후 리턴")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ResReturn {
private String flag;
private String message;
}
@Schema(name = "ResultContDto", description = "cont list 리턴 형태")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ResultContDto {
private Integer code;
private String message;
private List<ContBasic> result;
private Boolean success;
}
@Schema(name = "DtoPnuDetectMpng", description = "PNU 결과 형태")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class DtoPnuDetectMpng {
private String pnuDtctId;
private String lrmYmd;
private String pnu;
private String pnuSqms;
private String pnuDtctSqms;
private String chnDtctSqms;
private String chnDtctMstId;
private String chnDtctContId;
private String chnDtctId;
private String chnDtctObjtId;
private String crtDt;
}
@Schema(name = "ResultPnuDto", description = "pnu list 리턴 형태")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ResultPnuDto {
private Integer code;
private String message;
private List<DtoPnuDetectMpng> result;
private Boolean success;
}
}

View File

@@ -0,0 +1,291 @@
package com.kamco.cd.kamcoback.gukyuin.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.ZonedDateTime;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class ChngDetectMastDto {
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class Basic {
private String chnDtctMstId; // 탐지마스터아이디
private String cprsYr; // 비교년도 2023
private String crtrYr; // 기준년도 2024
private String chnDtctSno; // 차수 (1 | 2 | ...)
private String chnDtctId; // 탐지아이디. UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성
private String chnDtctCnt; // 탐지객체개수
private String pnuMpngCnt; // PNU매핑개수
private String lrmYmd; // 지적도일자
private String pathNm; // 탐지결과 절대경로명 /kamco_nas/export/{chnDtctId}
private List<ChnDetectMastExcnStepDto> excnList; // 등록진행상태히스토리 (최근것부터 DESC)
private String excnStepCd; // 실행단계코드
private String excnStep; // 실행단계코드에 해당하는 영문명
private String excnPgrt; // 실행단계진행율
private String excnBngnDt; // 실행단계시작시간
private String excnEndDt; // 실행단계종료시간
private String rmk; // 비고
private String crtDt; // 생성일시
private String crtEpno; // 생성사원번호
private String crtIp; // 생성사원아이피
private String chgDt; // 변경일시
private String chgEpno; // 변경사원번호
private String chgIp; // 변경사원아이피
private String delYn; // 삭제여부
//
private String reqEpno; // 요청사원번호
private String reqIp; // 요청사원어이피
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ChnDetectMastExcnStepDto {
private String srno; // 일련번호
private String chnDtctMstId; // 탐지마스터아이디
private String excnStepCd; // 실행단계코드
private String excnStep; // 실행단계코드에 해당하는 영문명
private String excnPgrt; // 실행단계진행율
private String excnEndDt; // 실행단계종료시간
private String errCd; // 오류코드
private String errMsg; // 오류메세지
private String crtDt; // 실행단계시작시간
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ChnDetectMastReqDto {
@Schema(description = "비교년도", example = "2023")
private String cprsYr;
@Schema(description = "기준년도", example = "2024")
private String crtrYr;
@Schema(description = "차수", example = "1")
private String chnDtctSno;
@Schema(
description = "탐지아이디, UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성",
example = "D5F192EC76D34F6592035BE63A84F591")
private String chnDtctId;
@Schema(
description = "탐지결과 절대경로명 /kamco_nas/export/{chnDtctId}",
example = "/kamco-nfs/dataset/export/D5F192EC76D34F6592035BE63A84F591")
private String pathNm;
@Schema(description = "사원번호", example = "123456")
private String reqEpno;
@Schema(description = "사원아이피", example = "127.0.0.1")
private String reqIp;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ChnDetectContDto {
private String chnDtctMstId; // 탐지콘텐츠아이디
private String chnDtctContId; // 탐지마스타아이디
private String cprsYr; // 비교년도 2023
private String crtrYr; // 기준년도 2024
private String chnDtctSno; // 차수 (1 | 2 | ...)
private String mpqdNo; // 도엽번호
private String chnDtctId; // 탐지아이디. UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성
private String chnDtctObjtId; // 탐지객체아이디. UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성
private String chnDtctPolygon; // 탐지객체폴리곤
private String chnDtctSqms; // 탐지객체면적
private String chnCd; // 변화코드
private String chnDtctJson; // 변화탐지JSON
private String chnDtctProb; // 변화탐지정확도
private String bfClsCd; // 이전부류코드
private String bfClsProb; // 이전분류정확도
private String afClsCd; // 이후분류코드
private String afClsProb; // 이후분류정확도
private String crtDt; // 생성일시
private String crtEpno; // 생성사원번호
private String crtIp; // 생성사원아이피
private String delYn; // 삭제여부
//
private String reqEpno; // 요청사원번호
private String reqIp; // 요청사원아이피
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ChnDetectContReqDto {
private String cprsYr; // 비교년도 2023
private String crtrYr; // 기준년도 2024
private String chnDtctSno; // 차수 (1 | 2 | ...)
private String mpqdNo; // 도엽번호
private String chnDtctId; // 탐지아이디. UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성
private String chnDtctObjtId; // 탐지객체아이디. UUID를 기반으로 '-'를 제거하고 대문자/숫자로 구성
private String reqEpno; // 사원번호
private String reqIp;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ChngDetectMastSearchDto {
// private String chnDtctId;
private String cprsYr;
private String crtrYr;
private String chnDtctSno;
}
@Schema(name = "ResReturn", description = "수행 후 리턴")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ResReturn {
private String flag;
private String message;
}
@Schema(name = "ResultDto", description = "mast list 리턴 형태")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ResultDto {
private Integer code;
private String message;
private List<Basic> result;
private Boolean success;
}
@Schema(name = "RegistResDto", description = "reg 등록 후 리턴 형태")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class RegistResDto {
private Integer code;
private String message;
private Basic result;
private Boolean success;
}
@Schema(name = "LearnKeyDto", description = "learn 엔티티 key 정보")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class LearnKeyDto {
private Long id;
private String uid;
private String chnDtctMstId;
}
@Schema(name = "LabelSendDto", description = "라벨링 전송한 목록")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class LabelSendDto {
private String chnDtctObjtId;
private String labelerId;
private ZonedDateTime labelerWorkDttm;
private String reviewerId;
private ZonedDateTime reviewerWorkDttm;
private ZonedDateTime labelSendDttm;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class ErrorResDto {
private String timestamp;
private Integer status;
private String error;
private String path;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class RlbDtctDto {
private Integer code;
private String message;
private List<RlbDtctMastDto> result;
private Boolean success;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class RlbDtctMastDto {
private String pnuDtctId;
private String pnu;
private String lrmSyncYmd;
private String pnuSyncYmd;
private String mpqdNo; // 도엽번호
private String cprsYr; // 비교년도
private String crtrYr; // 기준년도
private String chnDtctSno; // 회차
private String chnDtctId;
private String chnDtctMstId;
private String chnDtctObjtId;
private String chnDtctContId;
private String chnCd;
private String chnDtctProb;
private String bfClsCd; // 이전분류코드
private String bfClsProb; // 이전분류정확도
private String afClsCd; // 이후분류코드
private String afClsProb; // 이후분류정확도
private String pnuSqms;
private String pnuDtctSqms;
private String chnDtctSqms;
private String stbltYn;
private String incyCd;
private String incyRsnCont;
private String lockYn;
private String lblYn;
private String chgYn;
private String rsatctNo;
private String rmk;
private String crtDt; // 생성일시
private String crtEpno; // 생성사원번호
private String crtIp; // 생성사원아이피
private String chgDt;
private String chgEpno;
private String chgIp;
private String delYn; // 삭제여부
}
}

View File

@@ -0,0 +1,80 @@
package com.kamco.cd.kamcoback.gukyuin.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class DetectMastDto {
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class Basic {
private Long dtctMstId;
private String cprsBfYr;
private String cprsAdYr;
private Integer dtctSno;
private String pathNm;
private String crtEpno;
private String crtIp;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class DetectMastReq {
@NotBlank
@Schema(description = "before 연도", example = "2023")
private String cprsBfYr;
@NotBlank
@Schema(description = "after 연도", example = "2024")
private String cprsAdYr;
@NotNull
@Schema(description = "차수(회차)", example = "4")
private Integer dtctSno;
@NotBlank
@Schema(description = "파일경로", example = "/app/detect/result/2023_2024/4")
private String pathNm;
@NotBlank
@Schema(description = "사원번호", example = "1234567")
private String crtEpno;
@NotBlank
@Schema(description = "아이피", example = "0.0.0.0")
private String crtIp;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class DetectMastSearch {
private String cprsBfYr;
private String cprsAdYr;
private Integer dtctSno;
private String featureId;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class FeaturePnuDto {
private String featureId; // polygon_id
private String pnu; // 랜덤 생성
}
}

View File

@@ -0,0 +1,77 @@
package com.kamco.cd.kamcoback.gukyuin.dto;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
public class GukYuinDto {
/** 실패 코드 enum */
@Getter
@AllArgsConstructor
public enum GukYuinLinkFailCode implements EnumType {
OK("연동 가능"),
NOT_FOUND("대상 회차가 없습니다."),
SCOPE_PART_NOT_ALLOWED("부분 도엽은 연동 불가능 합니다."),
HAS_RUNNING_INFERENCE("라벨링 진행 중 회차가 있습니다."),
OTHER_GUKYUIN_IN_PROGRESS("국유in 연동 진행 중 회차가 있습니다.");
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
@Getter
@Setter
public static class GukYuinLinkableRes {
private boolean linkable;
// private GukYuinLinkFailCode code;
private String message;
}
// Repository가 반환할 Fact(조회 결과)
public record GukYuinLinkFacts(
boolean existsLearn,
boolean isPartScope,
boolean hasRunningInference,
boolean hasOtherUnfinishedGukYuin) {}
@Getter
@Setter
@AllArgsConstructor
public static class LearnInfo {
private Long id;
private UUID uuid;
private Integer compareYyyy;
private Integer targetYyyy;
private Integer stage;
private String uid;
private String applyStatus;
private Boolean applyYn;
public Boolean getApplyYn() {
return this.applyYn != null && this.applyYn;
}
}
@Getter
@Setter
@AllArgsConstructor
public static class GeomUidDto {
private Long geoUid;
private String resultUid;
}
}

View File

@@ -0,0 +1,29 @@
package com.kamco.cd.kamcoback.gukyuin.dto;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum GukYuinStatus implements EnumType {
PENDING("대기"),
IN_PROGRESS("진행중"),
GUK_COMPLETED("국유인 매핑 완료"),
PNU_COMPLETED("PNU 싱크 완료"),
PNU_FAILED("PNU 싱크 중 에러"),
END("종료"),
CANCELED("취소");
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}

View File

@@ -0,0 +1,56 @@
package com.kamco.cd.kamcoback.inference.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum DetectionClassification {
ROAD("road", "도로", 10),
BUILDING("building", "건물", 20),
GREENHOUSE("greenhouse", "비닐하우스", 30),
FIELD("field", "논/밭", 40),
ORCHARD("orchard", "과수원", 50),
GRASS("grass", "초지", 60),
FOREST("forest", "", 70),
WATER("water", "", 80),
STONE("stone", "모래/자갈", 90),
WASTE("waste", "적치물", 100),
CONTAINER("container", "컨테이너", 110),
LAND("land", "일반토지", 120),
SOLAR("solar", "태양광", 130),
TANK("tank", "물탱크", 140),
NDC("NDC", "미분류", 150),
ETC("ETC", "기타", 160);
private final String id;
private final String desc;
private final int order;
/**
* Optional: Helper method to get the enum from a String, case-insensitive, or return ETC if not
* found.
*/
public static DetectionClassification fromString(String text) {
if (text == null || text.trim().isEmpty()) {
return ETC;
}
try {
return DetectionClassification.valueOf(text.toUpperCase());
} catch (IllegalArgumentException e) {
// If the string doesn't match any enum constant name, return ETC
return ETC;
}
}
/**
* Desc 한글명 get 하기
*
* @return
*/
public static String fromStrDesc(String text) {
DetectionClassification dtf = fromString(text);
return dtf.getDesc();
}
}

View File

@@ -0,0 +1,561 @@
package com.kamco.cd.kamcoback.inference.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kamco.cd.kamcoback.common.utils.enums.ImageryFitStatus;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.DetectOption;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetScope;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
public class InferenceDetailDto {
@Schema(name = "InferenceResultBasic", description = "분석결과 기본 정보")
@Getter
public static class Basic {
private Long id;
private String dataName;
private Long mapSheepNum;
private Long detectingCnt;
@JsonFormatDttm private ZonedDateTime analStrtDttm;
@JsonFormatDttm private ZonedDateTime analEndDttm;
private Long analSec;
private String analState;
public Basic(
Long id,
String dataName,
Long mapSheepNum,
Long detectingCnt,
ZonedDateTime analStrtDttm,
ZonedDateTime analEndDttm,
Long analSec,
String analState) {
this.id = id;
this.dataName = dataName;
this.mapSheepNum = mapSheepNum;
this.detectingCnt = detectingCnt;
this.analStrtDttm = analStrtDttm;
this.analEndDttm = analEndDttm;
this.analSec = analSec;
this.analState = analState;
}
}
@Schema(name = "AnalysisResultList", description = "분석결과 목록")
@Getter
public static class AnalResList {
private Long id;
private String analTitle;
private Long detectingCnt;
@JsonFormatDttm private ZonedDateTime analStrtDttm;
@JsonFormatDttm private ZonedDateTime analEndDttm;
private Long analSec;
private Long analPredSec;
private String analState;
private String analStateNm;
private String gukyuinUsed;
public AnalResList(
Long id,
String analTitle,
Long detectingCnt,
ZonedDateTime analStrtDttm,
ZonedDateTime analEndDttm,
Long analSec,
Long analPredSec,
String analState,
String analStateNm,
String gukyuinUsed) {
this.id = id;
this.analTitle = analTitle;
this.detectingCnt = detectingCnt;
this.analStrtDttm = analStrtDttm;
this.analEndDttm = analEndDttm;
this.analSec = analSec;
this.analPredSec = analPredSec;
this.analState = analState;
this.analStateNm = analStateNm;
this.gukyuinUsed = gukyuinUsed;
}
}
@Schema(name = "AnalysisResultSummary", description = "분석결과 요약정보")
@Getter
public static class AnalResSummary {
private Long id;
private String analTitle;
private String modelInfo;
private Integer targetYyyy;
private Integer compareYyyy;
@JsonFormatDttm private ZonedDateTime analStrtDttm;
@JsonFormatDttm private ZonedDateTime analEndDttm;
private Long analSec;
private Long analPredSec;
private String resultUrl;
private Long detectingCnt;
private Double accuracy;
private String analState;
private String analStateNm;
public AnalResSummary(
Long id,
String analTitle,
String modelInfo,
Integer targetYyyy,
Integer compareYyyy,
ZonedDateTime analStrtDttm,
ZonedDateTime analEndDttm,
Long analSec,
Long analPredSec,
String resultUrl,
Long detectingCnt,
Double accuracy,
String analState,
String analStateNm) {
this.id = id;
this.analTitle = analTitle;
this.modelInfo = modelInfo;
this.targetYyyy = targetYyyy;
this.compareYyyy = compareYyyy;
this.analStrtDttm = analStrtDttm;
this.analEndDttm = analEndDttm;
this.analSec = analSec;
this.analPredSec = analPredSec;
this.resultUrl = resultUrl;
this.detectingCnt = detectingCnt;
this.accuracy = accuracy;
this.analState = analState;
this.analStateNm = analStateNm;
}
}
@Getter
public static class Dashboard {
String classAfterCd;
String classAfterName;
Long classAfterCnt;
public Dashboard(String classAfterCd, Long classAfterCnt) {
this.classAfterCd = classAfterCd;
this.classAfterName = DetectionClassification.fromString(classAfterCd).getDesc();
this.classAfterCnt = classAfterCnt;
}
}
@Getter
public static class Detail {
AnalResSummary summary;
List<Dashboard> dashboard;
Long totalCnt;
public Detail(AnalResSummary summary, List<Dashboard> dashboard, Long totalCnt) {
this.summary = summary;
this.dashboard = dashboard;
this.totalCnt = totalCnt;
}
}
// 분석 상세 ROW
@Getter
@AllArgsConstructor
public static class DetailListEntity {
private Uid code;
private Double detectionScore;
private Clazzes compare;
private Clazzes target;
private MapSheet mapSheet;
private Coordinate center;
@JsonFormatDttm private ZonedDateTime updatedDttm;
public DetailListEntity(
UUID uuid,
Double detectionScore,
Clazzes compare,
Clazzes target,
MapSheet mapSheet,
Coordinate center,
ZonedDateTime updatedDttm) {
this.code = new Uid(uuid);
this.detectionScore = detectionScore;
this.compare = compare;
this.target = target;
this.mapSheet = mapSheet;
this.center = center;
this.updatedDttm = updatedDttm;
}
}
@Getter
@AllArgsConstructor
public static class Uid {
private String shortCode;
private String code;
public Uid(UUID uuid) {
if (uuid != null) {
this.shortCode = uuid.toString().substring(0, 8).toUpperCase();
this.code = uuid.toString();
}
}
}
// MAP NO
@Getter
@AllArgsConstructor
public static class MapSheet {
private String number;
private String name;
}
// classification info
@Getter
public static class Clazz {
private String code;
private String name;
@JsonIgnore private Double score;
public Clazz(String code, Double score) {
this.code = code;
this.score = score;
this.name = DetectionClassification.fromString(code).getDesc();
}
public Clazz(String code) {
this.code = code;
this.name = DetectionClassification.fromString(code).getDesc();
}
}
// classification info
@Getter
public static class Clazzes {
private DetectionClassification code;
private String name;
@JsonInclude(JsonInclude.Include.NON_NULL)
private Double score;
private Integer order;
public Clazzes(DetectionClassification classification, Double score) {
this.code = classification;
this.name = classification.getDesc();
this.order = classification.getOrder();
this.score = score;
}
public Clazzes(DetectionClassification classification) {
this.code = classification;
this.name = classification.getDesc();
this.order = classification.getOrder();
}
}
// 좌표 정보 point
@Getter
public static class Coordinate {
private Double lon; // 경도(Longitude)
private Double lat; // 위도(Latitude)
private String srid; // Spatial Reference ID의 약자로, 데이터베이스에서 좌표계를 식별하는 고유 번호 추후enum으로
public Coordinate(Double lon, Double lat) {
this.lon = lon;
this.lat = lat;
this.srid = "EPSG:4326";
}
}
@Getter
public static class Geom {
UUID uuid;
String uid;
Integer compareYyyy;
Integer targetYyyy;
Double cdProb;
String classBeforeCd;
String classBeforeName;
Double classBeforeProb;
String classAfterCd;
String classAfterName;
Double classAfterProb;
Long mapSheetNum;
String mapSheetName;
String subUid;
String pnu;
String fitState;
public Geom(
UUID uuid,
String uid,
Integer compareYyyy,
Integer targetYyyy,
Double cdProb,
String classBeforeCd,
Double classBeforeProb,
String classAfterCd,
Double classAfterProb,
Long mapSheetNum,
String mapSheetName,
String subUid,
String pnu,
String fitState) {
this.uuid = uuid;
this.uid = uid;
this.compareYyyy = compareYyyy;
this.targetYyyy = targetYyyy;
this.cdProb = cdProb;
this.classBeforeCd = classBeforeCd;
this.classBeforeName = DetectionClassification.fromString(classBeforeCd).getDesc();
this.classBeforeProb = classBeforeProb;
this.classAfterCd = classAfterCd;
this.classAfterName = DetectionClassification.fromString(classAfterCd).getDesc();
this.classAfterProb = classAfterProb;
this.mapSheetNum = mapSheetNum;
this.mapSheetName = mapSheetName;
this.subUid = subUid;
this.pnu = pnu;
this.fitState = fitState;
}
@JsonProperty("fitState")
public String getFitState() {
return this.fitState == null ? null : this.fitState;
}
// @JsonIgnore String gemoStr;
// @JsonIgnore String geomCenterStr;
// JsonNode gemo;
// JsonNode geomCenter;
@JsonProperty("fitStateName")
public String fitStateName() {
return ImageryFitStatus.getDescByCode(this.fitState);
}
}
@Schema(name = "InferenceResultSearchReq", description = "분석결과 목록 요청 정보")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class SearchReq {
// 검색 조건
private String statCode;
private String title;
// 페이징 파라미터
private int page = 0;
private int size = 20;
public Pageable toPageable() {
return PageRequest.of(page, size);
}
}
@Schema(name = "InferenceResultSearchReq", description = "분석결과 목록 요청 정보")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class SearchGeoReq {
// 기준년도
private String targetClass;
// 비교년도
private String compareClass;
// 분석도엽
private Long mapSheetNum;
// 페이징 파라미터
private int page = 0;
private int size = 20;
private String sort;
public Pageable toPageable() {
if (sort != null && !sort.isEmpty()) {
String[] sortParams = sort.split(",");
String property = sortParams[0];
Sort.Direction direction =
sortParams.length > 1 ? Sort.Direction.fromString(sortParams[1]) : Sort.Direction.ASC;
return PageRequest.of(page, size, Sort.by(direction, property));
}
return PageRequest.of(page, size);
}
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class InferenceBatchSheet {
private Long id;
private UUID uuid;
private Long m1BatchId;
private Long m2BatchId;
private Long m3BatchId;
private String status;
private String runningModelType;
private UUID m1ModelUuid;
private UUID m2ModelUuid;
private UUID m3ModelUuid;
private String uid;
}
@Schema(name = "AnalResultInfo", description = "추론결과 기본정보")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class AnalResultInfo {
private String analTitle;
private String modelVer1;
private String modelVer2;
private String modelVer3;
private Integer compareYyyy;
private Integer targetYyyy;
private String detectOption;
private String mapSheetScope;
@JsonFormatDttm private ZonedDateTime inferStartDttm;
@JsonFormatDttm private ZonedDateTime inferEndDttm;
private Integer stage;
private String elapsedDuration;
private String subUid;
private Boolean applyYn;
@JsonFormatDttm private ZonedDateTime applyDttm;
private String bboxGeom;
private String bboxCenterPoint;
private UUID inferenceUuid;
public AnalResultInfo(
String analTitle,
String modelVer1,
String modelVer2,
String modelVer3,
Integer compareYyyy,
Integer targetYyyy,
String detectOption,
String mapSheetScope,
ZonedDateTime inferStartDttm,
ZonedDateTime inferEndDttm,
Integer stage,
String subUid,
Boolean applyYn,
ZonedDateTime applyDttm,
UUID inferenceUuid) {
this.analTitle = analTitle;
this.modelVer1 = modelVer1;
this.modelVer2 = modelVer2;
this.modelVer3 = modelVer3;
this.compareYyyy = compareYyyy;
this.targetYyyy = targetYyyy;
this.detectOption = DetectOption.getDescByCode(detectOption);
this.mapSheetScope = MapSheetScope.getDescByCode(mapSheetScope);
this.inferStartDttm = inferStartDttm;
this.inferEndDttm = inferEndDttm;
this.stage = stage;
this.subUid = subUid;
this.applyYn = applyYn;
this.applyDttm = applyDttm;
Duration elapsed =
(inferStartDttm != null && inferEndDttm != null)
? Duration.between(inferStartDttm, inferEndDttm)
: null;
if (elapsed != null) {
long seconds = elapsed.getSeconds();
long abs = Math.abs(seconds);
long h = abs / 3600;
long m = (abs % 3600) / 60;
long s = abs % 60;
this.elapsedDuration = String.format("%02d:%02d:%02d", h, m, s);
}
this.inferenceUuid = inferenceUuid;
}
@JsonProperty("bboxGeom")
public JsonNode getBboxGeom() {
ObjectMapper mapper = new ObjectMapper();
try {
if (this.bboxGeom != null) {
return mapper.readTree(this.bboxGeom);
} else {
return null;
}
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
@JsonProperty("bboxCenterPoint")
public JsonNode getBboxCenterPoint() {
ObjectMapper mapper = new ObjectMapper();
try {
if (this.bboxCenterPoint != null) {
return mapper.readTree(this.bboxCenterPoint);
} else {
return null;
}
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public Boolean getApplyYn() {
return this.applyYn != null && this.applyYn;
}
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class BboxPointDto {
private String bboxGeom;
private String bboxCenterPoint;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class Scene {
private String path;
private Integer size;
}
}

View File

@@ -0,0 +1,679 @@
package com.kamco.cd.kamcoback.inference.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import com.kamco.cd.kamcoback.common.utils.interfaces.EnumValid;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.Duration;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
public class InferenceResultDto {
/** 분석대상 도엽 enum */
@Getter
@AllArgsConstructor
public enum MapSheetScope implements EnumType {
ALL("전체"),
PART("부분"),
;
private final String desc;
public static MapSheetScope fromCode(String code) {
return Arrays.stream(values()).filter(v -> v.name().equals(code)).findFirst().orElse(null);
}
public static String getDescByCode(String code) {
return fromCode(code).getDesc();
}
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
/** 탐지 데이터 옵션 dto */
@Getter
@AllArgsConstructor
public enum DetectOption implements EnumType {
EXCL("추론제외"),
PREV("이전 년도 도엽 사용"),
;
private final String desc;
public static DetectOption fromCode(String code) {
return Arrays.stream(values()).filter(v -> v.name().equals(code)).findFirst().orElse(null);
}
public static String getDescByCode(String code) {
return fromCode(code).getDesc();
}
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
@Getter
@AllArgsConstructor
public enum Status implements EnumType {
READY("대기"),
IN_PROGRESS("진행중"),
END("완료"),
FORCED_END("강제종료");
private final String desc;
public static Status fromCode(String code) {
return Arrays.stream(values()).filter(v -> v.name().equals(code)).findFirst().orElse(null);
}
public static String getDescByCode(String code) {
return fromCode(code).getDesc();
}
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
@Getter
@AllArgsConstructor
public enum ServerStatus implements EnumType {
SAFETY("원활"),
CAUTION("주의"),
FAILUR("장애"),
;
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}
/** 목록조회 dto */
@Schema(name = "ResultList", description = "추론관리 목록")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class ResultList {
private UUID uuid;
@Schema(description = "제목")
private String title;
@Schema(description = "회차")
private Integer stage;
@Schema(description = "상태")
private String status;
@Schema(description = "분석 도엽")
private String mapSheetCnt;
@Schema(description = "탐지건수")
private Long detectingCnt;
@Schema(description = "시작일시")
@JsonFormatDttm
private ZonedDateTime startTime;
@Schema(description = "종료일시")
@JsonFormatDttm
private ZonedDateTime endTime;
@Schema(description = "반영여부")
private Boolean applyYn;
@Schema(description = "반영일시")
@JsonFormatDttm
private ZonedDateTime applyDttm;
@Schema(description = "비교년도")
private Integer compareYyyy;
@Schema(description = "기준년도")
private Integer targetYyyy;
@Schema(description = "uid")
private String uid;
@Schema(description = "uid 앞 8자리")
@JsonProperty("subUid")
public String subUid() {
return this.uid.substring(0, 8).toUpperCase();
}
@Schema(description = "상태명")
@JsonProperty("statusName")
public String statusName() {
return Status.getDescByCode(this.status);
}
@Schema(description = "소요시간")
@JsonProperty("elapsedTim")
public String getElapsedTime() {
if (this.startTime == null || this.endTime == null) {
return null;
}
ZonedDateTime start = this.startTime;
ZonedDateTime end = this.endTime;
Duration d = Duration.between(start, end);
if (d.isNegative()) {
d = d.negated();
}
long s = d.getSeconds();
long h = s / 3600;
long m = (s % 3600) / 60;
long sec = s % 60;
return String.format("%d시간 %d분 %d초", h, m, sec);
}
}
/** 목록조회 검색 조건 dto */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class SearchListReq {
// 검색 조건
private String applyYn;
private LocalDate strtDttm;
private LocalDate endDttm;
private String title;
// 페이징 파라미터
private int page = 0;
private int size = 20;
public Pageable toPageable() {
return PageRequest.of(page, size);
}
}
/** 변화탐지 실행 정보 저장 요청 정보 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class RegReq {
@Schema(description = "제목", example = "2023-2024 변화탐지 테스트")
@NotBlank
private String title;
@Schema(description = "M1", example = "b40e0f68-c1d8-49fc-93f9-a36270093861")
@NotNull
private UUID model1Uuid;
@Schema(description = "M2", example = "ec92b7d2-b5a3-4915-9bdf-35fb3ca8ad27")
@NotNull
private UUID model2Uuid;
@Schema(description = "M3", example = "37f45782-8ccf-4cf6-911c-a055a1510d39")
@NotNull
private UUID model3Uuid;
@Schema(description = "비교년도", example = "2023")
@NotNull
private Integer compareYyyy;
@Schema(description = "탐지년도", example = "2024")
@NotNull
private Integer targetYyyy;
@Schema(description = "분석대상 도엽 - 전체(ALL), 부분(PART)", example = "PART")
@NotBlank
@EnumValid(enumClass = MapSheetScope.class, message = "분석대상 도엽 옵션은 '전체', '부분' 만 사용 가능합니다.")
private String mapSheetScope;
@Schema(description = "탐지 데이터 옵션 - 추론제외(EXCL), 이전 년도 도엽 사용(PREV)", example = "EXCL")
@NotBlank
@EnumValid(
enumClass = DetectOption.class,
message = "탐지 데이터 옵션은 '추론제외', '이전 년도 도엽 사용' 만 사용 가능합니다.")
private String detectOption;
@Schema(description = "5k 도협 번호 목록", example = "[33605,33606, 33610, 34802, 35603, 35611]")
@NotNull
private List<String> mapSheetNum;
}
@Getter
@Setter
public static class MapSheetNumDto {
private String mapSheetNum;
private String mapSheetName;
}
@Getter
@Setter
@NoArgsConstructor
@Schema(name = "InferenceStatusDetailDto", description = "추론(변화탐지) 진행상태")
public static class InferenceStatusDetailDto {
@Schema(description = "탐지대상 도엽수")
private Long detectingCnt;
@Schema(description = "모델1 분석 대기")
private Integer m1PendingJobs;
@Schema(description = "모델2 분석 대기")
private Integer m2PendingJobs;
@Schema(description = "모델3 분석 대기")
private Integer m3PendingJobs;
@Schema(description = "모델1 분석 진행중")
private Integer m1RunningJobs;
@Schema(description = "모델2 분석 진행중")
private Integer m2RunningJobs;
@Schema(description = "모델3 분석 진행중")
private Integer m3RunningJobs;
@Schema(description = "모델1 분석 완료")
private Integer m1CompletedJobs;
@Schema(description = "모델2 분석 완료")
private Integer m2CompletedJobs;
@Schema(description = "모델3 분석 완료")
private Integer m3CompletedJobs;
@Schema(description = "모델1 분석 실패")
private Integer m1FailedJobs;
@Schema(description = "모델2 분석 실패")
private Integer m2FailedJobs;
@Schema(description = "모델3 분석 실패")
private Integer m3FailedJobs;
@Schema(description = "모델1 사용시간 시작일시")
@JsonFormatDttm
ZonedDateTime m1ModelStartDttm;
@Schema(description = "모델2 사용시간 시작일시")
@JsonFormatDttm
ZonedDateTime m2ModelStartDttm;
@Schema(description = "모델3 사용시간 시작일시")
@JsonFormatDttm
ZonedDateTime m3ModelStartDttm;
@Schema(description = "모델1 사용시간 종료일시")
@JsonFormatDttm
ZonedDateTime m1ModelEndDttm;
@Schema(description = "모델2 사용시간 종료일시")
@JsonFormatDttm
ZonedDateTime m2ModelEndDttm;
@Schema(description = "모델3 사용시간 종료일시")
@JsonFormatDttm
ZonedDateTime m3ModelEndDttm;
@Schema(description = "변화탐지 제목")
private String title;
@Schema(description = "비교년도")
private Integer compareYyyy;
@Schema(description = "기준년도")
private Integer targetYyyy;
@Schema(description = "회차")
private Integer stage;
@Schema(description = "변화탐지 시작")
@JsonFormatDttm
private ZonedDateTime inferStartDttm;
@Schema(description = "변화탐지 종료")
@JsonFormatDttm
private ZonedDateTime inferEndDttm;
@Schema(description = "변화탐지 옵션")
private String detectOption;
@Schema(description = "분석도엽")
private String mapSheetScope;
@Schema(description = "모델1 버전")
private String modelVer1;
@Schema(description = "모델2 버전")
private String modelVer2;
@Schema(description = "모델3 버전")
private String modelVer3;
@Schema(description = "탑지 도엽 수")
@JsonIgnore
private Long totalJobs;
public InferenceStatusDetailDto(
Long detectingCnt,
Integer m1PendingJobs,
Integer m2PendingJobs,
Integer m3PendingJobs,
Integer m1RunningJobs,
Integer m2RunningJobs,
Integer m3RunningJobs,
Integer m1CompletedJobs,
Integer m2CompletedJobs,
Integer m3CompletedJobs,
Integer m1FailedJobs,
Integer m2FailedJobs,
Integer m3FailedJobs,
ZonedDateTime m1ModelStartDttm,
ZonedDateTime m2ModelStartDttm,
ZonedDateTime m3ModelStartDttm,
ZonedDateTime m1ModelEndDttm,
ZonedDateTime m2ModelEndDttm,
ZonedDateTime m3ModelEndDttm,
String title,
Integer compareYyyy,
Integer targetYyyy,
Integer stage,
ZonedDateTime inferStartDttm,
ZonedDateTime inferEndDttm,
String detectOption,
String mapSheetScope,
String modelVer1,
String modelVer2,
String modelVer3,
Long totalJobs) {
this.detectingCnt = detectingCnt;
this.m1PendingJobs = m1PendingJobs;
this.m2PendingJobs = m2PendingJobs;
this.m3PendingJobs = m3PendingJobs;
this.m1RunningJobs = m1RunningJobs;
this.m2RunningJobs = m2RunningJobs;
this.m3RunningJobs = m3RunningJobs;
this.m1CompletedJobs = m1CompletedJobs;
this.m2CompletedJobs = m2CompletedJobs;
this.m3CompletedJobs = m3CompletedJobs;
this.m1FailedJobs = m1FailedJobs;
this.m2FailedJobs = m2FailedJobs;
this.m3FailedJobs = m3FailedJobs;
this.m1ModelStartDttm = m1ModelStartDttm;
this.m2ModelStartDttm = m2ModelStartDttm;
this.m3ModelStartDttm = m3ModelStartDttm;
this.m1ModelEndDttm = m1ModelEndDttm;
this.m2ModelEndDttm = m2ModelEndDttm;
this.m3ModelEndDttm = m3ModelEndDttm;
this.title = title;
this.compareYyyy = compareYyyy;
this.targetYyyy = targetYyyy;
this.stage = stage;
this.inferStartDttm = inferStartDttm;
this.inferEndDttm = inferEndDttm;
this.detectOption = detectOption;
this.mapSheetScope = mapSheetScope;
this.modelVer1 = modelVer1;
this.modelVer2 = modelVer2;
this.modelVer3 = modelVer3;
this.totalJobs = totalJobs;
}
@Schema(description = "진행률")
@JsonProperty("progress")
private int getProgress() {
long tiles = this.totalJobs == null ? 0L : this.totalJobs; // 도엽수
int models = 3; // 모델 개수
int completed =
this.m1CompletedJobs
+ this.m2CompletedJobs
+ this.m3CompletedJobs
+ this.m1FailedJobs
+ this.m2FailedJobs
+ this.m3FailedJobs; // 완료수
long total = tiles * models; // 전체 작업량
if (completed >= total) {
return 100;
}
return (int) ((completed * 100L) / total);
}
@Schema(description = "변화탐지 옵션명")
@JsonProperty("detectOptionName")
private String getDetectOptionName() {
return DetectOption.getDescByCode(this.detectOption);
}
@Schema(description = "분석도엽 명")
@JsonProperty("mapSheetScopeName")
private String getMapSheetScopeName() {
return MapSheetScope.getDescByCode(this.mapSheetScope);
}
@Schema(description = "M1 사용시간")
@JsonProperty("m1ElapsedTim")
public String getM1ElapsedTime() {
return formatElapsedTime(this.m1ModelStartDttm, this.m1ModelEndDttm);
}
@Schema(description = "M2 사용시간")
@JsonProperty("m2ElapsedTim")
public String getM2ElapsedTime() {
return formatElapsedTime(this.m2ModelStartDttm, this.m2ModelEndDttm);
}
@Schema(description = "M3 사용시간")
@JsonProperty("m3ElapsedTim")
public String getM3ElapsedTime() {
return formatElapsedTime(this.m3ModelStartDttm, this.m3ModelEndDttm);
}
private String formatElapsedTime(ZonedDateTime start, ZonedDateTime end) {
if (start == null || end == null) {
return null;
}
Duration d = Duration.between(start, end);
if (d.isNegative()) {
d = d.negated();
}
long s = d.getSeconds();
long h = s / 3600;
long m = (s % 3600) / 60;
long sec = s % 60;
return String.format("%d시간 %d분 %d초", h, m, sec);
}
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class InferenceServerStatusDto {
private String serverName;
@JsonIgnore private float cpu_user;
@JsonIgnore private float cpu_system;
@JsonIgnore private float memused;
private Long kbmemused;
private float gpuUtil;
// private String cpuStatusName;
// private String memStatusName;
// private String gpuStatusName;
// private float cpu_use_rate;
// private float gpu_use_rate;
// private float mem_use_rate;
public float getCpuUseRate() {
return this.cpu_user + this.cpu_system;
}
public String getServerStatus() {
String enumId = "SAFETY";
if (this.cpu_user + this.cpu_system + this.gpuUtil + this.kbmemused == 0) {
enumId = "FAILUR";
}
// if( this.cpu_user+this.cpu_system >= 80 )enumId = "CAUTION";
return enumId;
}
public String getServerStatusName() {
String enumStr = ServerStatus.SAFETY.getText();
if (this.cpu_user + this.cpu_system + this.gpuUtil + this.kbmemused == 0) {
enumStr = ServerStatus.FAILUR.getText();
}
return enumStr;
}
public String getCpuStatus() {
String enumId = "SAFETY";
if (this.cpu_user + this.cpu_system >= 80) {
enumId = "CAUTION";
} else if (this.cpu_user + this.cpu_system + this.memused == 0) {
enumId = "FAILUR";
}
return enumId;
}
public String getGpuStatus() {
String enumId = "SAFETY";
if (this.gpuUtil >= 80) {
enumId = "CAUTION";
} else if (this.cpu_user + this.cpu_system == 0) {
enumId = "FAILUR";
}
return enumId;
}
public String getMemStatus() {
String enumId = "SAFETY";
if (this.memused >= 80) {
enumId = "CAUTION";
} else if (this.cpu_user + this.cpu_system + this.memused == 0) {
enumId = "FAILUR";
}
return enumId;
}
public String getCpuStatusName() {
if (this.cpu_user + this.cpu_system >= 80) {
return ServerStatus.CAUTION.getText();
} else if (this.cpu_user + this.cpu_system + this.memused == 0) {
return ServerStatus.FAILUR.getText();
}
return ServerStatus.SAFETY.getText();
}
public String getGpuStatusName() {
if (this.gpuUtil >= 80) {
return ServerStatus.CAUTION.getText();
} else if (this.cpu_user + this.cpu_system + this.memused == 0) {
return ServerStatus.FAILUR.getText();
}
return ServerStatus.SAFETY.getText();
}
public String getMemStatusName() {
if (this.memused >= 80) {
return ServerStatus.CAUTION.getText();
} else if (this.cpu_user + this.cpu_system + this.memused == 0) {
return ServerStatus.FAILUR.getText();
}
return ServerStatus.SAFETY.getText();
}
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class InferenceStatusDetailDto2 {
InferenceServerStatusDto serverStatus;
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class SaveInferenceAiDto {
private UUID uuid;
private Long batchId;
private String status;
private String type;
private ZonedDateTime inferStartDttm;
private ZonedDateTime inferEndDttm;
private Long detectEndCnt;
private String modelComparePath;
private String modelTargetPath;
private String modelModelPath;
private ZonedDateTime modelStartDttm;
private ZonedDateTime modelEndDttm;
private Long updateUid;
private String runningModelType;
private Integer pendingJobs;
private Integer runningJobs;
private Integer completedJobs;
private Integer failedJobs;
}
@Getter
@Setter
public static class InferenceLearnDto {
private String uid;
private Long m1ModelBatchId;
private Long m2ModelBatchId;
private Long m3ModelBatchId;
}
}

View File

@@ -0,0 +1,10 @@
package com.kamco.cd.kamcoback.inferface;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CodeExpose {}

View File

@@ -0,0 +1,10 @@
package com.kamco.cd.kamcoback.inferface;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CodeHidden {}

View File

@@ -0,0 +1,8 @@
package com.kamco.cd.kamcoback.inferface;
public interface EnumType {
String getId();
String getText();
}

View File

@@ -0,0 +1,19 @@
package com.kamco.cd.kamcoback.inferface;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@JacksonAnnotationsInside
@JsonFormat(
shape = JsonFormat.Shape.STRING,
pattern = "yyyy-MM-dd'T'HH:mm:ssXXX",
timezone = "Asia/Seoul")
public @interface JsonFormatDttm {}

View File

@@ -0,0 +1,24 @@
package com.kamco.cd.kamcoback.log.dto;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum EventStatus implements EnumType {
SUCCESS("이벤트 결과 성공"),
FAILED("이벤트 결과 실패");
private final String desc;
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}

View File

@@ -0,0 +1,42 @@
package com.kamco.cd.kamcoback.log.dto;
import com.kamco.cd.kamcoback.common.utils.enums.CodeExpose;
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
import lombok.AllArgsConstructor;
import lombok.Getter;
@CodeExpose
@Getter
@AllArgsConstructor
public enum EventType implements EnumType {
LIST("목록"),
DETAIL("상세"),
POPUP("팝업"),
STATUS("상태"),
ADDED("추가"),
MODIFIED("수정"),
REMOVE("삭제"),
DOWNLOAD("다운로드"),
LOGIN("로그인"),
OTHER("기타");
private final String desc;
public static EventType fromName(String name) {
try {
return EventType.valueOf(name.toUpperCase());
} catch (Exception e) {
return OTHER;
}
}
@Override
public String getId() {
return name();
}
@Override
public String getText() {
return desc;
}
}

View File

@@ -0,0 +1,41 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import com.kamco.cd.kamcoback.postgres.repository.gukyuin.GukYuinPnuJobRepository;
import java.util.List;
import org.springframework.stereotype.Service;
@Service
public class GukYuinPnuJobCoreService {
private final GukYuinPnuJobRepository gukYuinPnuRepository;
public GukYuinPnuJobCoreService(GukYuinPnuJobRepository gukYuinPnuRepository) {
this.gukYuinPnuRepository = gukYuinPnuRepository;
}
public void updateGukYuinApplyStateComplete(Long id, GukYuinStatus status) {
gukYuinPnuRepository.updateGukYuinApplyStateComplete(id, status);
}
public List<LearnKeyDto> findGukyuinApplyStatusUidList(List<String> gukYuinStatus) {
return gukYuinPnuRepository.findGukyuinApplyStatusUidList(gukYuinStatus);
}
public long upsertMapSheetDataAnalGeomPnu(String chnDtctObjtId, String[] pnuList) {
return gukYuinPnuRepository.upsertMapSheetDataAnalGeomPnu(chnDtctObjtId, pnuList);
}
public void updateInferenceGeomDataPnuCnt(String chnDtctObjtId, long pnuCnt) {
gukYuinPnuRepository.updateInferenceGeomDataPnuCnt(chnDtctObjtId, pnuCnt);
}
public Long findMapSheetAnalDataInferenceGeomUid(String chnDtctObjtId) {
return gukYuinPnuRepository.findMapSheetAnalDataInferenceGeomUid(chnDtctObjtId);
}
public void insertGeoUidPnuData(Long geoUid, String[] pnuList) {
gukYuinPnuRepository.insertGeoUidPnuData(geoUid, pnuList);
}
}

View File

@@ -0,0 +1,143 @@
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();
}
}

View File

@@ -0,0 +1,34 @@
package com.kamco.cd.kamcoback.postgres.entity;
import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import java.time.ZonedDateTime;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
@Getter
@MappedSuperclass
public class CommonDateEntity {
@CreatedDate
@Column(name = "created_dttm", updatable = false, nullable = false)
private ZonedDateTime createdDate;
@LastModifiedDate
@Column(name = "updated_dttm", nullable = false)
private ZonedDateTime modifiedDate;
@PrePersist
protected void onPersist() {
this.createdDate = ZonedDateTime.now();
this.modifiedDate = ZonedDateTime.now();
}
@PreUpdate
protected void onUpdate() {
this.modifiedDate = ZonedDateTime.now();
}
}

View File

@@ -0,0 +1,69 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.label.dto.LabelAllocateDto;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.time.ZonedDateTime;
import java.util.UUID;
@Entity
@Table(name = "tb_labeling_assignment")
public class LabelingAssignmentEntity extends CommonDateEntity {
@Id
@Column(name = "assignment_uid")
private UUID assignmentUid;
@Column(name = "inference_geom_uid")
private Long inferenceGeomUid;
@Column(name = "worker_uid")
private String workerUid;
@Column(name = "inspector_uid")
private String inspectorUid;
@Column(name = "work_state")
private String workState;
@Column(name = "stagnation_yn")
private Character stagnationYn;
@Column(name = "assign_group_id")
private String assignGroupId;
@Column(name = "learn_geom_uid")
private Long learnGeomUid;
@Column(name = "anal_uid")
private Long analUid;
@Column(name = "inspect_state")
private String inspectState;
@Column(name = "work_stat_dttm")
private ZonedDateTime workStatDttm;
@Column(name = "inspect_stat_dttm")
private ZonedDateTime inspectStatDttm;
public LabelAllocateDto.Basic toDto() {
return new LabelAllocateDto.Basic(
this.assignmentUid,
this.inferenceGeomUid,
this.workerUid,
this.inspectorUid,
this.workState,
this.stagnationYn,
this.assignGroupId,
this.learnGeomUid,
this.analUid,
super.getCreatedDate(),
super.getModifiedDate(),
this.inspectState,
this.workStatDttm,
this.inspectStatDttm);
}
}

View File

@@ -0,0 +1,32 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.label.dto.LabelInspectorDto;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.UUID;
@Entity
@Table(name = "tb_labeling_inspector")
public class LabelingInspectorEntity extends CommonDateEntity {
@Id
@Column(name = "operator_uid")
private UUID operatorUid;
@Column(name = "anal_uid")
private Long analUid;
@Column(name = "inspector_uid")
private String inspectorUid;
public LabelInspectorDto.Basic toDto() {
return new LabelInspectorDto.Basic(
this.operatorUid,
this.analUid,
this.inspectorUid,
super.getCreatedDate(),
super.getModifiedDate());
}
}

View File

@@ -0,0 +1,48 @@
package com.kamco.cd.kamcoback.postgres.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.locationtech.jts.geom.Geometry;
@Getter
@Setter
@Table(name = "tb_map_inkx_50k")
@Entity
@NoArgsConstructor
public class MapInkx50kEntity extends CommonDateEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tb_map_inkx_50k_fid_seq_gen")
@SequenceGenerator(
name = "tb_map_inkx_50k_fid_seq_gen",
sequenceName = "tb_map_inkx_50k_fid_seq",
allocationSize = 1)
private Integer fid;
@Column(name = "mapidcd_no")
private String mapidcdNo;
@Column(name = "mapid_nm")
private String mapidNm;
@Column(name = "mapid_no")
private String mapidNo;
@Column(name = "geom")
private Geometry geom;
public MapInkx50kEntity(String mapidcdNo, String mapidNm, String mapidNo, Geometry geom) {
this.mapidcdNo = mapidcdNo;
this.mapidNm = mapidNm;
this.mapidNo = mapidNo;
this.geom = geom;
}
}

View File

@@ -0,0 +1,75 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.enums.CommonUseStatus;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.MapSheet;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.locationtech.jts.geom.Geometry;
@Getter
@Setter
@Table(name = "tb_map_inkx_5k")
@Entity
@NoArgsConstructor
public class MapInkx5kEntity extends CommonDateEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tb_map_inkx_5k_fid_seq_gen")
@SequenceGenerator(
name = "tb_map_inkx_5k_fid_seq_gen",
sequenceName = "tb_map_inkx_5k_fid_seq",
allocationSize = 1)
private Integer fid;
@Column(name = "mapidcd_no")
private String mapidcdNo;
@Column(name = "mapid_nm")
private String mapidNm;
@Column(name = "geom")
private Geometry geom;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "fid_k50", referencedColumnName = "fid")
private MapInkx50kEntity mapInkx50k;
// 사용상태 USE,
@Column(name = "use_inference")
@Enumerated(EnumType.STRING)
private CommonUseStatus useInference;
public InferenceDetailDto.MapSheet toEntity() {
return new MapSheet(mapidcdNo, mapidNm);
}
// Constructor
public MapInkx5kEntity(
String mapidcdNo, String mapidNm, Geometry geom, MapInkx50kEntity mapInkx50k) {
this.mapidcdNo = mapidcdNo;
this.mapidNm = mapidNm;
this.geom = geom;
this.mapInkx50k = mapInkx50k;
// 생성시 default 사용함 (사용,제외,사용안함)
this.useInference = CommonUseStatus.USE;
}
// 변경 사용상태 (추론사용여부)
public void updateUseInference(CommonUseStatus useInference) {
this.useInference = useInference;
}
}

View File

@@ -0,0 +1,181 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.inference.dto.DetectionClassification;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto;
import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.Clazzes;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Size;
import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
import org.locationtech.jts.geom.Geometry;
@Getter
@Setter
@Entity
@Table(name = "tb_map_sheet_anal_data_inference_geom")
public class MapSheetAnalDataInferenceGeomEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "geo_uid")
private Long geoUid;
@Column(name = "cd_prob")
private Double cdProb;
@Size(max = 40)
@Column(name = "class_before_cd", length = 40)
private String classBeforeCd;
@Column(name = "class_before_prob")
private Double classBeforeProb;
@Size(max = 40)
@Column(name = "class_after_cd", length = 40)
private String classAfterCd;
@Column(name = "class_after_prob")
private Double classAfterProb;
@Column(name = "map_sheet_num")
private Long mapSheetNum;
@Column(name = "compare_yyyy")
private Integer compareYyyy;
@Column(name = "target_yyyy")
private Integer targetYyyy;
@Column(name = "area")
private Double area;
@Size(max = 100)
@Column(name = "geo_type", length = 100)
private String geoType;
@Column(name = "data_uid")
private Long dataUid;
@ColumnDefault("now()")
@Column(name = "created_dttm")
private ZonedDateTime createdDttm;
@Column(name = "created_uid")
private Long createdUid;
@ColumnDefault("now()")
@Column(name = "updated_dttm")
private ZonedDateTime updatedDttm;
@Column(name = "updated_uid")
private Long updatedUid;
@ColumnDefault("0")
@Column(name = "geom_cnt")
private Long geomCnt;
@ColumnDefault("0")
@Column(name = "pnu")
private Long pnu;
@Size(max = 20)
@Column(name = "fit_state", length = 20)
private String fitState;
@ColumnDefault("now()")
@Column(name = "fit_state_dttm")
private ZonedDateTime fitStateDttm;
@Column(name = "labeler_uid")
private Long labelerUid;
@Size(max = 20)
@ColumnDefault("'0'")
@Column(name = "label_state", length = 20)
private String labelState;
@ColumnDefault("now()")
@Column(name = "label_state_dttm")
private ZonedDateTime labelStateDttm;
@Column(name = "tester_uid")
private Long testerUid;
@Size(max = 20)
@ColumnDefault("'0'")
@Column(name = "test_state", length = 20)
private String testState;
@ColumnDefault("now()")
@Column(name = "test_state_dttm")
private ZonedDateTime testStateDttm;
@Column(name = "fit_state_cmmnt", length = Integer.MAX_VALUE)
private String fitStateCmmnt;
@Column(name = "ref_map_sheet_num")
private Long refMapSheetNum;
@ColumnDefault("uuid_generate_v4()")
@Column(name = "uuid")
private UUID uuid;
@Column(name = "stage")
private Integer stage;
@Column(name = "file_created_yn")
private Boolean fileCreatedYn;
@Column(name = "geom", columnDefinition = "geometry")
private Geometry geom;
@Column(name = "geom_center", columnDefinition = "geometry")
private Geometry geomCenter;
@Column(name = "before_geom", columnDefinition = "geometry")
private Geometry beforeGeom;
@Column(name = "file_created_dttm")
private ZonedDateTime fileCreatedDttm;
@Column(name = "result_uid")
private String resultUid;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "map_5k_id", referencedColumnName = "fid")
private MapInkx5kEntity map5k;
@Column(name = "label_send_dttm")
private ZonedDateTime labelSendDttm;
@Column(name = "lock_yn")
private String lockYn;
public InferenceDetailDto.DetailListEntity toEntity() {
DetectionClassification classification = DetectionClassification.fromString(classBeforeCd);
Clazzes comparedClazz = new Clazzes(classification, classBeforeProb);
DetectionClassification classification1 = DetectionClassification.fromString(classAfterCd);
Clazzes targetClazz = new Clazzes(classification1, classAfterProb);
InferenceDetailDto.MapSheet mapSheet = map5k != null ? map5k.toEntity() : null;
InferenceDetailDto.Coordinate coordinate = null;
if (geomCenter != null) {
org.locationtech.jts.geom.Point point = (org.locationtech.jts.geom.Point) geomCenter;
coordinate = new InferenceDetailDto.Coordinate(point.getX(), point.getY());
}
return new InferenceDetailDto.DetailListEntity(
uuid, cdProb, comparedClazz, targetClazz, mapSheet, coordinate, createdDttm);
}
}

View File

@@ -0,0 +1,165 @@
package com.kamco.cd.kamcoback.postgres.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Size;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
@Getter
@Setter
@Entity
@Table(name = "tb_map_sheet_anal_inference")
public class MapSheetAnalInferenceEntity {
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "tb_map_sheet_anal_inference_id_gen")
@SequenceGenerator(
name = "tb_map_sheet_anal_inference_id_gen",
sequenceName = "tb_map_sheet_anal_inference_uid",
allocationSize = 1)
@Column(name = "anal_uid", nullable = false)
private Long id;
@Column(name = "compare_yyyy")
private Integer compareYyyy;
@Column(name = "target_yyyy")
private Integer targetYyyy;
@Column(name = "model_uid")
private Long modelUid;
@Size(max = 100)
@Column(name = "server_ids", length = 100)
private String serverIds;
@Column(name = "anal_strt_dttm")
private ZonedDateTime analStrtDttm;
@Column(name = "anal_end_dttm")
private ZonedDateTime analEndDttm;
@Column(name = "anal_sec")
private Long analSec;
@Size(max = 20)
@Column(name = "anal_state", length = 20)
private String analState;
@Size(max = 20)
@Column(name = "gukyuin_used", length = 20)
private String gukyuinUsed;
@Column(name = "accuracy")
private Double accuracy;
@Size(max = 255)
@Column(name = "result_url")
private String resultUrl;
@ColumnDefault("now()")
@Column(name = "created_dttm")
private ZonedDateTime createdDttm;
@Column(name = "created_uid")
private Long createdUid;
@ColumnDefault("now()")
@Column(name = "updated_dttm")
private ZonedDateTime updatedDttm;
@Column(name = "updated_uid")
private Long updatedUid;
@Size(max = 255)
@Column(name = "anal_title")
private String analTitle;
@Column(name = "detecting_cnt")
private Long detectingCnt;
@Column(name = "anal_pred_sec")
private Long analPredSec;
@Column(name = "model_ver_uid")
private Long modelVerUid;
@Column(name = "hyper_params")
@JdbcTypeCode(SqlTypes.JSON)
private Map<String, Object> hyperParams;
@Column(name = "tranning_rate")
private List<Double> tranningRate;
@Column(name = "validation_rate")
private List<Double> validationRate;
@Column(name = "test_rate", length = Integer.MAX_VALUE)
private String testRate;
@Size(max = 128)
@Column(name = "detecting_description", length = 128)
private String detectingDescription;
@Size(max = 12)
@Column(name = "base_map_sheet_num", length = 12)
private String baseMapSheetNum;
@ColumnDefault("gen_random_uuid()")
@Column(name = "uuid")
private UUID uuid;
@Size(max = 50)
@Column(name = "model_m1_ver", length = 50)
private String modelM1Ver;
@Size(max = 50)
@Column(name = "model_m2_ver", length = 50)
private String modelM2Ver;
@Size(max = 50)
@Column(name = "model_m3_ver", length = 50)
private String modelM3Ver;
@Size(max = 20)
@Column(name = "anal_target_type", length = 20)
private String analTargetType;
@Column(name = "gukyuin_apply_dttm")
private ZonedDateTime gukyuinApplyDttm;
@Size(max = 20)
@Column(name = "detection_data_option", length = 20)
private String detectionDataOption;
@Column(name = "stage")
private Integer stage;
@Size(max = 1)
@ColumnDefault("'N'")
@Column(name = "labeling_closed_yn", length = 1)
private String labelingClosedYn = "N";
@Size(max = 1)
@ColumnDefault("'N'")
@Column(name = "inspection_closed_yn", length = 1)
private String inspectionClosedYn = "N";
@Column(name = "learn_id")
private Long learnId;
}

View File

@@ -0,0 +1,45 @@
package com.kamco.cd.kamcoback.postgres.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import org.locationtech.jts.geom.Geometry;
/** 라벨링 툴에서 그린 폴리곤 저장 테이블 */
@Getter
@Setter
@Entity
@Table(name = "tb_map_sheet_learn_data_geom")
public class MapSheetLearnDataGeomEntity extends CommonDateEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "geo_uid", nullable = false)
private Long geoUid;
@Column(name = "after_yyyy")
private Integer afterYyyy;
@Column(name = "area")
private Double area;
@Column(name = "before_yyyy")
private Integer beforeYyyy;
@Column(name = "class_after_cd")
private String classAfterCd;
@Column(name = "class_before_cd")
private String classBeforeCd;
@Column(name = "geom")
private Geometry geom;
@Column(name = "file_create_yn")
private Boolean fileCreateYn;
}

View File

@@ -0,0 +1,221 @@
package com.kamco.cd.kamcoback.postgres.entity;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
@Getter
@Setter
@Entity
@Table(name = "tb_map_sheet_learn")
public class MapSheetLearnEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tb_map_sheet_learn_id_gen")
@SequenceGenerator(
name = "tb_map_sheet_learn_id_gen",
sequenceName = "tb_map_sheet_learn_uid",
allocationSize = 1)
@Column(name = "id", nullable = false)
private Long id;
@ColumnDefault("gen_random_uuid()")
@Column(name = "uuid")
private UUID uuid = UUID.randomUUID();
@Size(max = 200)
@NotNull
@Column(name = "title", nullable = false, length = 200)
private String title;
@Size(max = 20)
@Column(name = "status", length = 10)
private String status;
@Column(name = "m1_model_uuid")
private UUID m1ModelUuid;
@Column(name = "m2_model_uuid")
private UUID m2ModelUuid;
@Column(name = "m3_model_uuid")
private UUID m3ModelUuid;
@Column(name = "compare_yyyy")
private Integer compareYyyy;
@Column(name = "target_yyyy")
private Integer targetYyyy;
@Size(max = 20)
@Column(name = "detect_option", length = 20)
private String detectOption;
@Size(max = 100)
@Column(name = "map_sheet_cnt", length = 100)
private String mapSheetCnt;
@Size(max = 20)
@Column(name = "map_sheet_scope", length = 20)
private String mapSheetScope;
@Column(name = "detecting_cnt")
private Long detectingCnt;
@Column(name = "infer_start_dttm")
private ZonedDateTime inferStartDttm;
@Column(name = "infer_end_dttm")
private ZonedDateTime inferEndDttm;
@Column(name = "elapsed_time")
private ZonedDateTime elapsedTime;
@Column(name = "apply_yn")
private Boolean applyYn;
@Column(name = "apply_dttm")
private ZonedDateTime applyDttm;
@org.hibernate.annotations.CreationTimestamp
@Column(name = "created_dttm")
private ZonedDateTime createdDttm;
@Column(name = "created_uid")
private Long createdUid;
@ColumnDefault("now()")
@Column(name = "updated_dttm")
private ZonedDateTime updatedDttm;
@Column(name = "updated_uid")
private Long updatedUid;
@Column(name = "running_model_type")
private String runningModelType;
@Column(name = "detect_end_cnt")
private Long detectEndCnt;
@Column(name = "model_compare_path")
private String modelComparePath;
@Column(name = "model_target_path")
private String modelTargetPath;
@Column(name = "stage")
private Integer stage;
/* ===================== M1 ===================== */
@Column(name = "m1_model_batch_id")
private Long m1ModelBatchId;
@Column(name = "m1_model_start_dttm")
private ZonedDateTime m1ModelStartDttm;
@Column(name = "m1_model_end_dttm")
private ZonedDateTime m1ModelEndDttm;
@Column(name = "m1_pending_jobs", nullable = false)
private int m1PendingJobs = 0;
@Column(name = "m1_running_jobs", nullable = false)
private int m1RunningJobs = 0;
@Column(name = "m1_completed_jobs", nullable = false)
private int m1CompletedJobs = 0;
@Column(name = "m1_failed_jobs", nullable = false)
private int m1FailedJobs = 0;
/* ===================== M2 ===================== */
@Column(name = "m2_model_batch_id")
private Long m2ModelBatchId;
@Column(name = "m2_model_start_dttm")
private ZonedDateTime m2ModelStartDttm;
@Column(name = "m2_model_end_dttm")
private ZonedDateTime m2ModelEndDttm;
@Column(name = "m2_pending_jobs", nullable = false)
private int m2PendingJobs = 0;
@Column(name = "m2_running_jobs", nullable = false)
private int m2RunningJobs = 0;
@Column(name = "m2_completed_jobs", nullable = false)
private int m2CompletedJobs = 0;
@Column(name = "m2_failed_jobs", nullable = false)
private int m2FailedJobs = 0;
/* ===================== M3 ===================== */
@Column(name = "m3_model_batch_id")
private Long m3ModelBatchId;
@Column(name = "m3_model_start_dttm")
private ZonedDateTime m3ModelStartDttm;
@Column(name = "m3_model_end_dttm")
private ZonedDateTime m3ModelEndDttm;
@Column(name = "m3_pending_jobs", nullable = false)
private int m3PendingJobs = 0;
@Column(name = "m3_running_jobs", nullable = false)
private int m3RunningJobs = 0;
@Column(name = "m3_completed_jobs", nullable = false)
private int m3CompletedJobs = 0;
@Column(name = "m3_failed_jobs", nullable = false)
private int m3FailedJobs = 0;
@Column(name = "apply_status")
private String applyStatus = GukYuinStatus.PENDING.getId();
@Column(name = "apply_status_dttm")
private ZonedDateTime applyStatusDttm;
@Column(name = "uid", nullable = false)
private String uid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
@Column(name = "total_jobs")
private Long totalJobs;
@Column(name = "chn_dtct_mst_id")
private String chnDtctMstId;
public InferenceResultDto.ResultList toDto() {
return new InferenceResultDto.ResultList(
this.uuid,
this.title,
this.stage,
this.status,
this.mapSheetCnt,
this.detectingCnt,
this.inferStartDttm,
this.inferEndDttm,
this.applyYn,
this.applyDttm,
this.compareYyyy,
this.targetYyyy,
this.uid);
}
}

View File

@@ -0,0 +1,50 @@
package com.kamco.cd.kamcoback.postgres.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.OffsetDateTime;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
@Getter
@Setter
@Entity
@Table(name = "tb_pnu")
public class PnuEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tb_pnu_id_gen")
@SequenceGenerator(name = "tb_pnu_id_gen", sequenceName = "tb_pnu_uid", allocationSize = 1)
@Column(name = "id", nullable = false)
private Long id;
@NotNull
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "geo_uid", nullable = false)
private MapSheetAnalDataInferenceGeomEntity geo;
@Size(max = 19)
@Column(name = "pnu", length = 19)
private String pnu;
@Column(name = "created_dttm")
private OffsetDateTime createdDttm;
@Column(name = "created_uid")
private Long createdUid;
@ColumnDefault("false")
@Column(name = "del_yn")
private Boolean delYn;
}

View File

@@ -0,0 +1,7 @@
package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface GukYuinPnuJobRepository
extends JpaRepository<MapSheetLearnEntity, Long>, GukYuinPnuJobRepositoryCustom {}

View File

@@ -0,0 +1,20 @@
package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import java.util.List;
public interface GukYuinPnuJobRepositoryCustom {
void updateInferenceGeomDataPnuCnt(String chnDtctObjtId, long pnuCnt);
Long findMapSheetAnalDataInferenceGeomUid(String chnDtctObjtId);
void insertGeoUidPnuData(Long geoUid, String[] pnuList);
void updateGukYuinApplyStateComplete(Long id, GukYuinStatus status);
List<LearnKeyDto> findGukyuinApplyStatusUidList(List<String> gukYuinStatus);
long upsertMapSheetDataAnalGeomPnu(String uid, String[] pnuList);
}

View File

@@ -0,0 +1,110 @@
package com.kamco.cd.kamcoback.postgres.repository.gukyuin;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetLearnEntity.mapSheetLearnEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QPnuEntity.pnuEntity;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectMastDto.LearnKeyDto;
import com.kamco.cd.kamcoback.gukyuin.dto.GukYuinStatus;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import java.time.ZonedDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class GukYuinPnuJobRepositoryImpl implements GukYuinPnuJobRepositoryCustom {
private final JPAQueryFactory queryFactory;
@PersistenceContext private EntityManager em;
@Override
public void updateInferenceGeomDataPnuCnt(String chnDtctObjtId, long pnuCnt) {
queryFactory
.update(mapSheetAnalDataInferenceGeomEntity)
.set(mapSheetAnalDataInferenceGeomEntity.pnu, pnuCnt)
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(chnDtctObjtId))
.execute();
}
@Override
public Long findMapSheetAnalDataInferenceGeomUid(String chnDtctObjtId) {
return queryFactory
.select(mapSheetAnalDataInferenceGeomEntity.geoUid)
.from(mapSheetAnalDataInferenceGeomEntity)
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(chnDtctObjtId))
.fetchOne();
}
@Override
public void insertGeoUidPnuData(Long geoUid, String[] pnuList) {
for (String pnu : pnuList) {
queryFactory
.insert(pnuEntity)
.columns(pnuEntity.geo.geoUid, pnuEntity.pnu, pnuEntity.createdDttm)
.values(geoUid, pnu, ZonedDateTime.now())
.execute();
}
}
@Override
public List<LearnKeyDto> findGukyuinApplyStatusUidList(List<String> status) {
return queryFactory
.select(
Projections.constructor(
LearnKeyDto.class,
mapSheetLearnEntity.id,
mapSheetLearnEntity.uid,
mapSheetLearnEntity.chnDtctMstId))
.from(mapSheetLearnEntity)
.where(mapSheetLearnEntity.applyStatus.in(status))
.fetch();
}
@Override
public long upsertMapSheetDataAnalGeomPnu(String chnDtctObjtId, String[] pnuList) {
long length = pnuList.length;
queryFactory
.update(mapSheetAnalDataInferenceGeomEntity)
.set(mapSheetAnalDataInferenceGeomEntity.pnu, length)
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(chnDtctObjtId))
.execute();
Long geoUid =
queryFactory
.select(mapSheetAnalDataInferenceGeomEntity.geoUid)
.from(mapSheetAnalDataInferenceGeomEntity)
.where(mapSheetAnalDataInferenceGeomEntity.resultUid.eq(chnDtctObjtId))
.fetchOne();
long succCnt = 0;
for (String pnu : pnuList) {
long result =
queryFactory
.insert(pnuEntity)
.columns(pnuEntity.geo.geoUid, pnuEntity.pnu, pnuEntity.createdDttm)
.values(geoUid, pnu, ZonedDateTime.now())
.execute();
if (result > 0) {
succCnt++;
}
}
return succCnt;
}
@Override
@Transactional
public void updateGukYuinApplyStateComplete(Long id, GukYuinStatus status) {
queryFactory
.update(mapSheetLearnEntity)
.set(mapSheetLearnEntity.applyStatus, status.getId())
.set(mapSheetLearnEntity.applyStatusDttm, ZonedDateTime.now())
.where(mapSheetLearnEntity.id.eq(id))
.execute();
}
}

View File

@@ -0,0 +1,7 @@
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,29 @@
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);
}

View File

@@ -0,0 +1,523 @@
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();
}
}

View File

@@ -0,0 +1,220 @@
package com.kamco.cd.kamcoback.scheduler.service;
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.ExternalCallResult;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectContDto;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectContDto.ContBasic;
import com.kamco.cd.kamcoback.gukyuin.dto.ChngDetectContDto.ResultContDto;
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.ResultDto;
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.entity.AuditLogEntity;
import com.kamco.cd.kamcoback.postgres.repository.log.AuditLogRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Log4j2
@Service
@RequiredArgsConstructor
public class GukYuinApiPnuJobService {
private final GukYuinPnuJobCoreService gukYuinPnuJobCoreService;
private final ExternalHttpClient externalHttpClient;
private final NetUtils netUtils = new NetUtils();
private final AuditLogRepository auditLogRepository;
private final UserUtil userUtil;
@Value("${spring.profiles.active}")
private String profile;
@Value("${gukyuin.url}")
private String gukyuinUrl;
@Value("${gukyuin.cdi}")
private String gukyuinCdiUrl;
/**
* 실행중인 profile
*
* @return
*/
private boolean isLocalProfile() {
return "local".equalsIgnoreCase(profile);
}
/** 국유인 등록 완료 후, 탐지객체 조회해서 PNU 업데이트 하는 스케줄링 하루 1번 새벽 1시에 실행 */
@Scheduled(cron = "0 0 1 * * *")
public void findGukYuinContListPnuUpdate() {
if (isLocalProfile()) {
return;
}
List<LearnKeyDto> list =
gukYuinPnuJobCoreService.findGukyuinApplyStatusUidList(
List.of(GukYuinStatus.GUK_COMPLETED.getId(), GukYuinStatus.PNU_FAILED.getId()));
if (list.isEmpty()) {
return;
}
for (LearnKeyDto dto : list) {
try {
long succCnt = processUid(dto.getChnDtctMstId(), dto.getUid());
if (succCnt > 0) {
gukYuinPnuJobCoreService.updateGukYuinApplyStateComplete(
dto.getId(), GukYuinStatus.PNU_COMPLETED);
} else {
gukYuinPnuJobCoreService.updateGukYuinApplyStateComplete(
dto.getId(), GukYuinStatus.PNU_FAILED);
}
} catch (Exception e) {
log.error("[GUKYUIN] failed uid={}", dto.getUid(), e);
gukYuinPnuJobCoreService.updateGukYuinApplyStateComplete(
dto.getId(), GukYuinStatus.PNU_FAILED);
}
}
}
private long processUid(String chnDtctMstId, String uid) {
long succCnt = 0;
String url = gukyuinCdiUrl + "/chn/mast/list/" + chnDtctMstId;
ExternalCallResult<ResultDto> response =
externalHttpClient.call(
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();
if (result == null || result.getResult() == null || result.getResult().isEmpty()) {
return succCnt;
}
ChngDetectMastDto.Basic basic = result.getResult().get(0);
String chnDtctCnt = basic.getChnDtctCnt();
if (chnDtctCnt == null || chnDtctCnt.isEmpty()) {
return succCnt;
}
// page 계산
int pageSize = 100;
int totalCount = Integer.parseInt(chnDtctCnt);
int totalPages = (totalCount + pageSize - 1) / pageSize;
for (int page = 0; page < totalPages; page++) {
succCnt += processPage(uid, page, pageSize);
}
return succCnt;
}
private long processPage(String uid, int page, int pageSize) {
long result = 0;
String url =
gukyuinCdiUrl + "/chn/cont/" + uid + "?pageIndex=" + page + "&pageSize=" + pageSize;
ExternalCallResult<ChngDetectContDto.ResultContDto> response =
externalHttpClient.call(
url,
HttpMethod.GET,
null,
netUtils.jsonHeaders(),
ChngDetectContDto.ResultContDto.class);
List<ContBasic> contList = response.body().getResult();
if (contList == null || contList.isEmpty()) {
return 0;
}
for (ContBasic cont : contList) {
String[] pnuList = cont.getPnuList();
long pnuCnt = pnuList == null ? 0 : pnuList.length;
if (cont.getChnDtctObjtId() != null) {
gukYuinPnuJobCoreService.updateInferenceGeomDataPnuCnt(cont.getChnDtctObjtId(), pnuCnt);
if (pnuCnt > 0) {
Long geoUid =
gukYuinPnuJobCoreService.findMapSheetAnalDataInferenceGeomUid(
cont.getChnDtctObjtId());
gukYuinPnuJobCoreService.insertGeoUidPnuData(geoUid, pnuList);
}
}
}
this.insertGukyuinAuditLog(
EventType.LIST.getId(),
netUtils.getLocalIP(),
userUtil.getId(),
url.replace(gukyuinUrl, ""),
null,
response.body().getSuccess());
ResultContDto cont = response.body();
if (cont == null || cont.getResult().isEmpty()) {
return result; // 외부 API 이상 방어
}
// pnuList 업데이트
for (ChngDetectContDto.ContBasic contBasic : cont.getResult()) {
if (contBasic.getPnuList() == null || contBasic.getChnDtctObjtId() == null) {
return 0;
}
long upsertCnt =
gukYuinPnuJobCoreService.upsertMapSheetDataAnalGeomPnu(
contBasic.getChnDtctObjtId(), contBasic.getPnuList());
result += upsertCnt;
}
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;
}
}
}

View File

@@ -0,0 +1,4 @@
server:
port: 9080

View File

@@ -0,0 +1,71 @@
server:
port: 9080
spring:
application:
name: label-to-review
profiles:
active: dev # 사용할 프로파일 지정 (ex. dev, prod, test)
datasource:
url: jdbc:postgresql://192.168.2.127:15432/kamco_cds
#url: jdbc:postgresql://localhost:5432/kamco_cds
username: kamco_cds
password: kamco_cds_Q!W@E#R$
hikari:
minimum-idle: 1
maximum-pool-size: 5
jpa:
hibernate:
ddl-auto: update # 테이블이 없으면 생성, 있으면 업데이트
properties:
hibernate:
jdbc:
batch_size: 50
default_batch_fetch_size: 100
logging:
level:
root: INFO
org.springframework.web: DEBUG
org.springframework.security: DEBUG
# 헬스체크 노이즈 핵심만 다운
org.springframework.security.web.FilterChainProxy: INFO
org.springframework.security.web.authentication.AnonymousAuthenticationFilter: INFO
org.springframework.security.web.authentication.Http403ForbiddenEntryPoint: INFO
org.springframework.web.servlet.DispatcherServlet: INFO
# actuator
management:
health:
readinessstate:
enabled: true
livenessstate:
enabled: true
endpoint:
health:
probes:
enabled: true
show-details: always
endpoints:
jmx:
exposure:
exclude: "*"
web:
base-path: /monitor
exposure:
include:
- "health"
file:
#sync-root-dir: D:/kamco-nfs/images/
sync-root-dir: /kamco-nfs/images/
sync-tmp-dir: ${file.sync-root-dir}/tmp
sync-file-extention: tfw,tif
sync-auto-exception-start-year: 2025
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

View File

@@ -0,0 +1,71 @@
server:
port: 9080
spring:
application:
name: imagery-make-dataset
profiles:
active: local # 사용할 프로파일 지정 (ex. dev, prod, test)
datasource:
url: jdbc:postgresql://192.168.2.127:15432/kamco_cds
#url: jdbc:postgresql://localhost:5432/kamco_cds
username: kamco_cds
password: kamco_cds_Q!W@E#R$
hikari:
minimum-idle: 1
maximum-pool-size: 5
jpa:
hibernate:
ddl-auto: update # 테이블이 없으면 생성, 있으면 업데이트
properties:
hibernate:
jdbc:
batch_size: 50
default_batch_fetch_size: 100
logging:
level:
root: INFO
org.springframework.web: DEBUG
org.springframework.security: DEBUG
# 헬스체크 노이즈 핵심만 다운
org.springframework.security.web.FilterChainProxy: INFO
org.springframework.security.web.authentication.AnonymousAuthenticationFilter: INFO
org.springframework.security.web.authentication.Http403ForbiddenEntryPoint: INFO
org.springframework.web.servlet.DispatcherServlet: INFO
# actuator
management:
health:
readinessstate:
enabled: true
livenessstate:
enabled: true
endpoint:
health:
probes:
enabled: true
show-details: always
endpoints:
jmx:
exposure:
exclude: "*"
web:
base-path: /monitor
exposure:
include:
- "health"
file:
#sync-root-dir: D:/kamco-nfs/images/
sync-root-dir: /kamco-nfs/images/
sync-tmp-dir: ${file.sync-root-dir}/tmp
sync-file-extention: tfw,tif
sync-auto-exception-start-year: 2025
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

View File

@@ -0,0 +1,71 @@
server:
port: 9080
spring:
application:
name: imagery-make-dataset
profiles:
active: prod # 사용할 프로파일 지정 (ex. dev, prod, test)
datasource:
url: jdbc:postgresql://192.168.2.127:15432/kamco_cds
#url: jdbc:postgresql://localhost:5432/kamco_cds
username: kamco_cds
password: kamco_cds_Q!W@E#R$
hikari:
minimum-idle: 1
maximum-pool-size: 5
jpa:
hibernate:
ddl-auto: update # 테이블이 없으면 생성, 있으면 업데이트
properties:
hibernate:
jdbc:
batch_size: 50
default_batch_fetch_size: 100
logging:
level:
root: INFO
org.springframework.web: DEBUG
org.springframework.security: DEBUG
# 헬스체크 노이즈 핵심만 다운
org.springframework.security.web.FilterChainProxy: INFO
org.springframework.security.web.authentication.AnonymousAuthenticationFilter: INFO
org.springframework.security.web.authentication.Http403ForbiddenEntryPoint: INFO
org.springframework.web.servlet.DispatcherServlet: INFO
# actuator
management:
health:
readinessstate:
enabled: true
livenessstate:
enabled: true
endpoint:
health:
probes:
enabled: true
show-details: always
endpoints:
jmx:
exposure:
exclude: "*"
web:
base-path: /monitor
exposure:
include:
- "health"
file:
#sync-root-dir: D:/kamco-nfs/images/
sync-root-dir: /kamco-nfs/images/
sync-tmp-dir: ${file.sync-root-dir}/tmp
sync-file-extention: tfw,tif
sync-auto-exception-start-year: 2025
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

View File

@@ -0,0 +1,137 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Chunk Upload Test</title>
</head>
<body>
<h2>대용량 파일 청크 업로드 테스트</h2>
* Chunk 테스트 사이즈 10M (10 * 1024 * 1024) - 성능에 따라 변경가능<br><br>
* 업로드 API선택</br></br>
<select name="apiUrl" id="apiUrl" style="width:600px;height:40px;">
<option value="/api/model/file-chunk-upload">모델파일Chunk업로드 ( /api/model/file-chunk-upload )</option>
<option value="/api/upload/file-chunk-upload">파일Chunk업로드(공통) ( /api/upload/file-chunk-upload )</option>
</select>
<br><br>
* 파일첨부<br><br>
<input type="file" id="chunkFile" style="height:40px;"><br><br>
<button onclick="startUpload()" style="height:40px;">업로드 시작</button>
<br><br><br><br>
* 업로드시 업로드 이력을 추적하기 위해 UUID생성해서 전달(파일병합시 사용)(script 예제참고)</br></br>
UUID : <input id="uuid" name="uuid" value="" style="width:300px;height:30px;" readonly><br><br>
* API 호출시 파일정보 추출해서 자동 할당해야 함.(script 예제참고)</br></br>
chunkIndex : <input style="height:30px;" id="chunkIndex" placeholder="chunkIndex" readonly><br><br>
chunkTotalIndex : <input style="height:30px;" id="chunkTotalIndex" placeholder="chunkTotalIndex" readonly ><br><br>
* API 호출시 파일정보 추출해서 자동 할당해야 함.(script 예제참고)</br></br>
fileSize : <input style="height:30px;" id="fileSize" placeholder="fileSize" readonly><br><br>
<!--
fileHash : <input id="fileHash" placeholder="fileHash"><br><br> -->
<br><br>
* 진행율(%)</br></br>
<div style="width:500px;height:30px;border:1px solid #cccccc;"><div id="prgssbar" style="width:100%;height:30px;background:#eeeeee;"></div></div>
<br><br>
* 결과메세지</br></br>
<div id="status" style="padding:20px;width:1200px;height:600px;border:1px solid #000000;"></div>
<script>
async function startUpload() {
const apiUrl = document.getElementById('apiUrl').value;
const file = document.getElementById('chunkFile').files[0];
const fileName = file.name;
//const datasetUid = Number(document.getElementById('datasetUid').value);
//const chunkIndex = document.getElementById('chunkIndex').value;
if (!file) return alert("파일을 선택하세요.");
const CHUNK_SIZE = 10 * 1024 * 1024; // 5MB
const fileSize = file.size;
var totalChunks = Math.ceil(fileSize / CHUNK_SIZE);
const chunkTotalIndex = totalChunks - 1;
const uuid = crypto.randomUUID(); // 고유 ID 생성
//var uuid = "";
document.getElementById('uuid').value = uuid;
document.getElementById('fileSize').value = file.size;
document.getElementById('chunkTotalIndex').value = chunkTotalIndex;
for (let i = 0; i < totalChunks; i++) {
//for (let i = 0; i < 1; i++) {
const start = i * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, file.size);
const chunk = file.slice(start, end);
document.getElementById('chunkIndex').value = i;
const formData = new FormData();
formData.append("uuid", uuid);
formData.append("fileSize", fileSize);
formData.append("fileName", fileName);
formData.append("chunkIndex", i);
formData.append("chunkTotalIndex", chunkTotalIndex);
formData.append("chunkFile", chunk);
try {
const response = await fetch(apiUrl, { method: 'POST', body: formData });
// 2. 응답 상태 확인 (200 OK 등)
if (!response.ok) {
throw new Error(`서버 에러: ${response.status}`);
}
// 3. 서버가 보낸 데이터 읽기 (JSON 형태라고 가정)
const result = await response.json();
document.getElementById('status').innerText = JSON.stringify(result, null, 2);
if( result.data.res != "success")
{
//오류 경고창 띄우는 것으로 처리하시면 됩니다.
break;
}
document.getElementById('prgssbar').style.width = result.data.uploadRate+"%";
} catch (error) {
console.error(`${i}번째 청크 업로드 실패:`, error);
break; // 오류 발생 시 중단
}
}
// 모든 청크 전송 후 최종 완료 요청
//var mergeResult = await completeUpload(uuid);
//document.getElementById('status').innerText = JSON.stringify(mergeResult, null, 2);
}
async function completeUpload(uuid) {
try {
const response = await fetch(`/api/upload/chunk-upload-complete/${uuid}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
});
if (!response.ok) {
throw new Error(`서ver 응답 에러: ${response.status}`);
}
const result = await response.json();
return result;
} catch (error) {
console.error("완료 요청 중 오류 발생:", error);
}
}
</script>
</body>
</html>