Merge pull request 'shp파일 생성 수정, 미사용 소스 제거' (#326) from feat/infer_dev_260107 into develop
Reviewed-on: https://kamco.gitea.gs.dabeeo.com/dabeeo/kamco-dabeeo-backoffice/pulls/326
This commit is contained in:
@@ -1,71 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.common.api;
|
||||
|
||||
import com.kamco.cd.kamcoback.common.api.HelloDto.Res;
|
||||
import com.kamco.cd.kamcoback.common.service.ExternalJarRunner;
|
||||
import com.kamco.cd.kamcoback.common.service.HelloService;
|
||||
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient;
|
||||
import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Log4j2
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/api/hello")
|
||||
public class HelloApiController {
|
||||
|
||||
private final HelloService helloService;
|
||||
private final ExternalJarRunner externalJarRunner;
|
||||
private final ExternalHttpClient externalHttpClient;
|
||||
|
||||
@GetMapping
|
||||
public HelloDto.Res hello(HelloDto.Req req) {
|
||||
req.valid();
|
||||
|
||||
Res res = helloService.sayHello(req);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@GetMapping("/shp")
|
||||
public void shp(
|
||||
@Parameter(description = "jar 경로", example = "jar/makeshp-1.0.0.jar") @RequestParam
|
||||
String jarPath,
|
||||
@Parameter(description = "batchIds", example = "252,253,257") @RequestParam String batchIds,
|
||||
@Parameter(description = "32길이 문자열 값", example = "") @RequestParam(required = false)
|
||||
String inferenceId,
|
||||
@Parameter(description = "5K 도엽번호", example = "") @RequestParam(required = false)
|
||||
String mapIds) {
|
||||
externalJarRunner.run(jarPath, batchIds, inferenceId, mapIds);
|
||||
}
|
||||
|
||||
@GetMapping("/batch/{batchId}")
|
||||
public String batch(@PathVariable String batchId) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
|
||||
String url = "http://10.100.0.11:8000/batches" + "/" + batchId;
|
||||
ExternalCallResult<String> result =
|
||||
externalHttpClient.call(url, HttpMethod.GET, null, headers, String.class);
|
||||
|
||||
int status = result.statusCode();
|
||||
if (status == 404) {
|
||||
log.info("Batch not found. batchId={}", batchId);
|
||||
return null;
|
||||
}
|
||||
if (status < 200 || status >= 300) {
|
||||
return null;
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.common.api;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
public class HelloDto {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class Req {
|
||||
private String id;
|
||||
|
||||
public void valid() {
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class Res {
|
||||
private String id;
|
||||
private String name;
|
||||
|
||||
@Builder
|
||||
public Res(String id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,18 +21,29 @@ public class ExternalJarRunner {
|
||||
* @param jarPath jar 경로
|
||||
* @param batchIds 배치 아이디
|
||||
* @param inferenceId uid
|
||||
* @param mapIds 도엽 Id
|
||||
* @param mapIds 추론 실행한 도엽 ids
|
||||
* @param mode <p>MERGED - batch-ids 에 해당하는 **모든 데이터를 하나의 Shapefile로 병합 생성,
|
||||
* <p>MAP_IDS - 명시적으로 전달한 map-ids만 대상으로 Shapefile 생성,
|
||||
* <p>RESOLVE - batch-ids 기준으로 **JAR 내부에서 map_ids를 조회**한 뒤 Shapefile 생성
|
||||
*/
|
||||
public void run(String jarPath, String batchIds, String inferenceId, String mapIds) {
|
||||
public void run(String jarPath, String batchIds, String inferenceId, String mapIds, String mode) {
|
||||
List<String> args = new ArrayList<>();
|
||||
|
||||
addArg(args, "converter.inference-id", inferenceId);
|
||||
addArg(args, "converter.map-ids", mapIds);
|
||||
addArg(args, "converter.batch-ids", batchIds);
|
||||
|
||||
if (mapIds != null && !mapIds.isEmpty()) {
|
||||
addArg(args, "converter.map-ids", mapIds);
|
||||
}
|
||||
|
||||
if (mode != null && !mode.isEmpty()) {
|
||||
addArg(args, "converter.mode", mode);
|
||||
}
|
||||
|
||||
execJar(jarPath, args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* geoserver 등록
|
||||
*
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.common.service;
|
||||
|
||||
import com.kamco.cd.kamcoback.common.api.HelloDto;
|
||||
import java.util.UUID;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class HelloService {
|
||||
|
||||
public HelloDto.Res sayHello(HelloDto.Req req) {
|
||||
log.info("hello");
|
||||
String name = UUID.randomUUID().toString();
|
||||
return HelloDto.Res.builder().id(req.getId()).name(name).build();
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package com.kamco.cd.kamcoback.inference.service;
|
||||
|
||||
import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto;
|
||||
import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto.CreateShpRequest;
|
||||
import com.kamco.cd.kamcoback.inference.dto.InferenceResultsTestingDto;
|
||||
import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService;
|
||||
import com.kamco.cd.kamcoback.postgres.core.InferenceResultShpCoreService;
|
||||
import com.kamco.cd.kamcoback.scheduler.service.ShpPipelineService;
|
||||
@@ -31,7 +30,9 @@ public class InferenceResultShpService {
|
||||
@Value("${file.dataset-dir}")
|
||||
private String datasetDir;
|
||||
|
||||
/** inference_results 테이블을 기준으로 분석 결과 테이블과 도형 테이블을 최신 상태로 반영한다. */
|
||||
/**
|
||||
* inference_results 테이블을 기준으로 분석 결과 테이블과 도형 테이블을 최신 상태로 반영한다.
|
||||
*/
|
||||
@Transactional
|
||||
public InferenceResultShpDto.InferenceCntDto saveInferenceResultData(Long id) {
|
||||
return coreService.buildInferenceData(id);
|
||||
@@ -49,25 +50,9 @@ public class InferenceResultShpService {
|
||||
batchIds.add(req.getM2BatchId());
|
||||
batchIds.add(req.getM3BatchId());
|
||||
|
||||
List<InferenceResultsTestingDto.ShpDto> resultList =
|
||||
inferenceResultCoreService.getInferenceResults(batchIds);
|
||||
String inferenceId = "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (InferenceResultsTestingDto.ShpDto dto : resultList) {
|
||||
if (dto.getMapId() == null) {
|
||||
continue;
|
||||
}
|
||||
if (!sb.isEmpty()) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("\"").append(dto.getMapId()).append("\"");
|
||||
}
|
||||
inferenceId = uid;
|
||||
String mapIds = sb.toString();
|
||||
String batchId = req.getM1BatchId() + "," + req.getM2BatchId() + "," + req.getM3BatchId();
|
||||
|
||||
// shp 파일 비동기 생성
|
||||
shpPipelineService.runPipeline(jarPath, datasetDir, batchId, inferenceId, mapIds);
|
||||
shpPipelineService.runPipeline(jarPath, datasetDir, batchId, uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.postgres.core;
|
||||
|
||||
import com.kamco.cd.kamcoback.common.service.BaseCoreService;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.AnimalEntity;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.ZooEntity;
|
||||
import com.kamco.cd.kamcoback.postgres.repository.AnimalRepository;
|
||||
import com.kamco.cd.kamcoback.postgres.repository.ZooRepository;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.AnimalDto;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class AnimalCoreService
|
||||
implements BaseCoreService<AnimalDto.Basic, Long, AnimalDto.SearchReq> {
|
||||
|
||||
private final AnimalRepository animalRepository;
|
||||
private final ZooRepository zooRepository;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public AnimalDto.Basic getDataByUuid(String uuid) {
|
||||
AnimalEntity getZoo =
|
||||
animalRepository
|
||||
.getAnimalByUuid(uuid)
|
||||
.orElseThrow(() -> new EntityNotFoundException("Zoo not found with uuid: " + uuid));
|
||||
return getZoo.toDto();
|
||||
}
|
||||
|
||||
// AddReq를 받는 추가 메서드
|
||||
@Transactional
|
||||
public AnimalDto.Basic create(AnimalDto.AddReq req) {
|
||||
ZooEntity zoo = null;
|
||||
if (req.getZooUuid() != null) {
|
||||
zoo =
|
||||
zooRepository
|
||||
.getZooByUuid(req.getZooUuid())
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new EntityNotFoundException("Zoo not found with uuid: " + req.getZooUuid()));
|
||||
}
|
||||
AnimalEntity entity = new AnimalEntity(req.getCategory(), req.getSpecies(), req.getName(), zoo);
|
||||
AnimalEntity saved = animalRepository.save(entity);
|
||||
return saved.toDto();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void remove(Long id) {
|
||||
AnimalEntity getAnimal =
|
||||
animalRepository
|
||||
.getAnimalByUid(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException("getAnimal not found with id: " + id));
|
||||
getAnimal.deleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimalDto.Basic getOneById(Long id) {
|
||||
AnimalEntity getAnimal =
|
||||
animalRepository
|
||||
.getAnimalByUid(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException("Zoo not found with id: " + id));
|
||||
return getAnimal.toDto();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<AnimalDto.Basic> search(AnimalDto.SearchReq searchReq) {
|
||||
|
||||
Page<AnimalEntity> animalEntities = animalRepository.listAnimal(searchReq);
|
||||
return animalEntities.map(AnimalEntity::toDto);
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.postgres.core;
|
||||
|
||||
import com.kamco.cd.kamcoback.common.service.BaseCoreService;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.ZooEntity;
|
||||
import com.kamco.cd.kamcoback.postgres.repository.ZooRepository;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.ZooDto;
|
||||
import jakarta.persistence.EntityNotFoundException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class ZooCoreService implements BaseCoreService<ZooDto.Detail, Long, ZooDto.SearchReq> {
|
||||
|
||||
private final ZooRepository zooRepository;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public ZooDto.Detail getDataByUuid(String uuid) {
|
||||
ZooEntity zoo =
|
||||
zooRepository
|
||||
.getZooByUuid(uuid)
|
||||
.orElseThrow(() -> new EntityNotFoundException("Zoo not found with uuid: " + uuid));
|
||||
return toDetailDto(zoo);
|
||||
}
|
||||
|
||||
// AddReq를 받는 추가 메서드
|
||||
@Transactional
|
||||
public ZooDto.Detail create(ZooDto.AddReq req) {
|
||||
ZooEntity entity = new ZooEntity(req.getName(), req.getLocation(), req.getDescription());
|
||||
ZooEntity saved = zooRepository.save(entity);
|
||||
return toDetailDto(saved);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void remove(Long id) {
|
||||
ZooEntity zoo =
|
||||
zooRepository
|
||||
.getZooByUid(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException("Zoo not found with id: " + id));
|
||||
zoo.deleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZooDto.Detail getOneById(Long id) {
|
||||
ZooEntity zoo =
|
||||
zooRepository
|
||||
.getZooByUid(id)
|
||||
.orElseThrow(() -> new EntityNotFoundException("Zoo not found with id: " + id));
|
||||
return toDetailDto(zoo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<ZooDto.Detail> search(ZooDto.SearchReq searchReq) {
|
||||
Page<ZooEntity> zooEntities = zooRepository.listZoo(searchReq);
|
||||
|
||||
// N+1 문제 해결: 한 번의 쿼리로 모든 Zoo의 animal count 조회
|
||||
List<Long> zooIds =
|
||||
zooEntities.getContent().stream().map(ZooEntity::getUid).collect(Collectors.toList());
|
||||
|
||||
Map<Long, Long> animalCountMap = zooRepository.countActiveAnimalsByZooIds(zooIds);
|
||||
|
||||
// DTO 변환
|
||||
List<ZooDto.Detail> details =
|
||||
zooEntities.getContent().stream()
|
||||
.map(zoo -> toDetailDtoWithCountMap(zoo, animalCountMap))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new PageImpl<>(details, zooEntities.getPageable(), zooEntities.getTotalElements());
|
||||
}
|
||||
|
||||
// Entity -> Detail DTO 변환 (동물 개수 포함) - 단건 조회용
|
||||
private ZooDto.Detail toDetailDto(ZooEntity zoo) {
|
||||
Long activeAnimalCount = zooRepository.countActiveAnimals(zoo.getUid());
|
||||
return new ZooDto.Detail(
|
||||
zoo.getUid(),
|
||||
zoo.getUuid().toString(),
|
||||
zoo.getName(),
|
||||
zoo.getLocation(),
|
||||
zoo.getDescription(),
|
||||
zoo.getCreatedDate(),
|
||||
zoo.getModifiedDate(),
|
||||
activeAnimalCount);
|
||||
}
|
||||
|
||||
// Entity -> Detail DTO 변환 (동물 개수 포함) - 배치 조회용 (N+1 해결)
|
||||
private ZooDto.Detail toDetailDtoWithCountMap(ZooEntity zoo, Map<Long, Long> countMap) {
|
||||
Long activeAnimalCount = countMap.getOrDefault(zoo.getUid(), 0L);
|
||||
return new ZooDto.Detail(
|
||||
zoo.getUid(),
|
||||
zoo.getUuid().toString(),
|
||||
zoo.getName(),
|
||||
zoo.getLocation(),
|
||||
zoo.getDescription(),
|
||||
zoo.getCreatedDate(),
|
||||
zoo.getModifiedDate(),
|
||||
activeAnimalCount);
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.postgres.entity;
|
||||
|
||||
import com.kamco.cd.kamcoback.postgres.CommonDateEntity;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.AnimalDto;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.AnimalDto.Category;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.AnimalDto.Species;
|
||||
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.Table;
|
||||
import java.util.UUID;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
// 기본구조 관련
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Table(name = "tb_animal")
|
||||
public class AnimalEntity extends CommonDateEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long uid;
|
||||
|
||||
@Column(unique = true)
|
||||
private UUID uuid;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Category category;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
private Species species;
|
||||
|
||||
private String name;
|
||||
private Boolean isDeleted;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "zoo_id")
|
||||
private ZooEntity zoo;
|
||||
|
||||
// Construct
|
||||
public AnimalEntity(Category category, Species species, String name, ZooEntity zoo) {
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.category = category;
|
||||
this.species = species;
|
||||
this.name = name;
|
||||
this.isDeleted = false;
|
||||
this.zoo = zoo;
|
||||
}
|
||||
|
||||
public AnimalDto.Basic toDto() {
|
||||
return new AnimalDto.Basic(
|
||||
this.uid,
|
||||
this.uuid.toString(),
|
||||
this.name,
|
||||
this.category,
|
||||
this.species,
|
||||
super.getCreatedDate(),
|
||||
super.getModifiedDate());
|
||||
}
|
||||
|
||||
public void deleted() {
|
||||
this.isDeleted = true;
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.postgres.entity;
|
||||
|
||||
import com.kamco.cd.kamcoback.postgres.CommonDateEntity;
|
||||
import jakarta.persistence.CascadeType;
|
||||
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.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Entity
|
||||
@Getter
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Table(name = "tb_zoo")
|
||||
public class ZooEntity extends CommonDateEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long uid;
|
||||
|
||||
@Column(unique = true, nullable = false)
|
||||
private UUID uuid;
|
||||
|
||||
@Column(nullable = false, length = 200)
|
||||
private String name;
|
||||
|
||||
@Column(length = 300)
|
||||
private String location;
|
||||
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Boolean isDeleted;
|
||||
|
||||
@OneToMany(mappedBy = "zoo", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
|
||||
private List<AnimalEntity> animals = new ArrayList<>();
|
||||
|
||||
// Constructor
|
||||
public ZooEntity(String name, String location, String description) {
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.name = name;
|
||||
this.location = location;
|
||||
this.description = description;
|
||||
this.isDeleted = false;
|
||||
}
|
||||
|
||||
// 논리 삭제
|
||||
public void deleted() {
|
||||
this.isDeleted = true;
|
||||
}
|
||||
|
||||
// 현재 활성 동물 개수 조회 (삭제되지 않은 동물만)
|
||||
public long getActiveAnimalCount() {
|
||||
return animals.stream().filter(animal -> !animal.getIsDeleted()).count();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.postgres.repository;
|
||||
|
||||
import com.kamco.cd.kamcoback.postgres.entity.AnimalEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface AnimalRepository
|
||||
extends JpaRepository<AnimalEntity, Long>, AnimalRepositoryCustom {}
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.postgres.repository;
|
||||
|
||||
import com.kamco.cd.kamcoback.postgres.entity.AnimalEntity;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.AnimalDto;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
public interface AnimalRepositoryCustom {
|
||||
|
||||
Optional<AnimalEntity> getAnimalByUid(Long uid);
|
||||
|
||||
Optional<AnimalEntity> getAnimalByUuid(String uuid);
|
||||
|
||||
Page<AnimalEntity> listAnimal(AnimalDto.SearchReq req);
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.postgres.repository;
|
||||
|
||||
import com.kamco.cd.kamcoback.postgres.entity.AnimalEntity;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.QAnimalEntity;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.AnimalDto;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.AnimalDto.Category;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.AnimalDto.Species;
|
||||
import com.querydsl.core.types.dsl.BooleanExpression;
|
||||
import com.querydsl.jpa.impl.JPAQueryFactory;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
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 AnimalRepositoryImpl extends QuerydslRepositorySupport
|
||||
implements AnimalRepositoryCustom {
|
||||
|
||||
private final JPAQueryFactory queryFactory;
|
||||
|
||||
public AnimalRepositoryImpl(JPAQueryFactory queryFactory) {
|
||||
super(AnimalEntity.class);
|
||||
this.queryFactory = queryFactory;
|
||||
}
|
||||
|
||||
public Optional<AnimalEntity> getAnimalByUid(Long uid) {
|
||||
QAnimalEntity animal = QAnimalEntity.animalEntity;
|
||||
|
||||
return Optional.ofNullable(
|
||||
queryFactory.selectFrom(animal).where(animal.uid.eq(uid)).fetchFirst());
|
||||
}
|
||||
|
||||
public Optional<AnimalEntity> getAnimalByUuid(String uuid) {
|
||||
QAnimalEntity animal = QAnimalEntity.animalEntity;
|
||||
|
||||
return Optional.ofNullable(
|
||||
queryFactory.selectFrom(animal).where(animal.uuid.eq(UUID.fromString(uuid))).fetchFirst());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<AnimalEntity> listAnimal(AnimalDto.SearchReq req) {
|
||||
QAnimalEntity animal = QAnimalEntity.animalEntity;
|
||||
|
||||
Pageable pageable = req.toPageable();
|
||||
|
||||
List<AnimalEntity> content =
|
||||
queryFactory
|
||||
.selectFrom(animal)
|
||||
.where(
|
||||
animal.isDeleted.eq(false),
|
||||
eqCategory(animal, req.getCategory()),
|
||||
eqSpecies(animal, req.getSpecies()),
|
||||
likeName(animal, req.getName()))
|
||||
.offset(pageable.getOffset())
|
||||
.limit(pageable.getPageSize())
|
||||
.orderBy(animal.createdDate.desc())
|
||||
.fetch();
|
||||
|
||||
// count 쿼리
|
||||
Long total =
|
||||
queryFactory
|
||||
.select(animal.count())
|
||||
.from(animal)
|
||||
.where(
|
||||
animal.isDeleted.eq(false),
|
||||
eqCategory(animal, req.getCategory()),
|
||||
eqSpecies(animal, req.getSpecies()),
|
||||
likeName(animal, req.getName()))
|
||||
.fetchOne();
|
||||
|
||||
return new PageImpl<>(content, pageable, total);
|
||||
}
|
||||
|
||||
private BooleanExpression likeName(QAnimalEntity animal, String nameStr) {
|
||||
if (nameStr == null || nameStr.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return animal.name.containsIgnoreCase(nameStr.trim());
|
||||
}
|
||||
|
||||
private BooleanExpression eqCategory(QAnimalEntity animal, Category category) {
|
||||
if (category == null) {
|
||||
return null;
|
||||
}
|
||||
return animal.category.eq(category);
|
||||
}
|
||||
|
||||
private BooleanExpression eqSpecies(QAnimalEntity animal, Species species) {
|
||||
if (species == null) {
|
||||
return null;
|
||||
}
|
||||
return animal.species.eq(species);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.postgres.repository;
|
||||
|
||||
import com.kamco.cd.kamcoback.postgres.entity.ZooEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface ZooRepository extends JpaRepository<ZooEntity, Long>, ZooRepositoryCustom {}
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.postgres.repository;
|
||||
|
||||
import com.kamco.cd.kamcoback.postgres.entity.ZooEntity;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.ZooDto;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
public interface ZooRepositoryCustom {
|
||||
|
||||
Page<ZooEntity> listZoo(ZooDto.SearchReq searchReq);
|
||||
|
||||
Optional<ZooEntity> getZooByUuid(String uuid);
|
||||
|
||||
Optional<ZooEntity> getZooByUid(Long uid);
|
||||
|
||||
Long countActiveAnimals(Long zooId);
|
||||
|
||||
/**
|
||||
* 여러 Zoo의 활성 동물 수를 한 번에 조회 (N+1 문제 해결)
|
||||
*
|
||||
* @param zooIds Zoo ID 목록
|
||||
* @return Map<Zoo ID, 활성 동물 수>
|
||||
*/
|
||||
Map<Long, Long> countActiveAnimalsByZooIds(List<Long> zooIds);
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.postgres.repository;
|
||||
|
||||
import com.kamco.cd.kamcoback.postgres.entity.QAnimalEntity;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.QZooEntity;
|
||||
import com.kamco.cd.kamcoback.postgres.entity.ZooEntity;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.ZooDto;
|
||||
import com.querydsl.core.types.dsl.BooleanExpression;
|
||||
import com.querydsl.jpa.impl.JPAQuery;
|
||||
import com.querydsl.jpa.impl.JPAQueryFactory;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class ZooRepositoryImpl implements ZooRepositoryCustom {
|
||||
|
||||
private final JPAQueryFactory queryFactory;
|
||||
private final QZooEntity qZoo = QZooEntity.zooEntity;
|
||||
private final QAnimalEntity qAnimal = QAnimalEntity.animalEntity;
|
||||
|
||||
@Override
|
||||
public Page<ZooEntity> listZoo(ZooDto.SearchReq searchReq) {
|
||||
Pageable pageable = searchReq.toPageable();
|
||||
|
||||
JPAQuery<ZooEntity> query =
|
||||
queryFactory
|
||||
.selectFrom(qZoo)
|
||||
.where(
|
||||
qZoo.isDeleted.eq(false),
|
||||
nameContains(searchReq.getName()),
|
||||
locationContains(searchReq.getLocation()));
|
||||
|
||||
long total = query.fetchCount();
|
||||
|
||||
List<ZooEntity> content =
|
||||
query
|
||||
.offset(pageable.getOffset())
|
||||
.limit(pageable.getPageSize())
|
||||
.orderBy(qZoo.createdDate.desc())
|
||||
.fetch();
|
||||
|
||||
return new PageImpl<>(content, pageable, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ZooEntity> getZooByUuid(String uuid) {
|
||||
return Optional.ofNullable(
|
||||
queryFactory
|
||||
.selectFrom(qZoo)
|
||||
.where(qZoo.uuid.eq(UUID.fromString(uuid)), qZoo.isDeleted.eq(false))
|
||||
.fetchOne());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ZooEntity> getZooByUid(Long uid) {
|
||||
return Optional.ofNullable(
|
||||
queryFactory.selectFrom(qZoo).where(qZoo.uid.eq(uid), qZoo.isDeleted.eq(false)).fetchOne());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countActiveAnimals(Long zooId) {
|
||||
Long count =
|
||||
queryFactory
|
||||
.select(qAnimal.count())
|
||||
.from(qAnimal)
|
||||
.where(qAnimal.zoo.uid.eq(zooId), qAnimal.isDeleted.eq(false))
|
||||
.fetchOne();
|
||||
|
||||
return count != null ? count : 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, Long> countActiveAnimalsByZooIds(List<Long> zooIds) {
|
||||
if (zooIds == null || zooIds.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
// QueryDSL group by로 한 번에 조회
|
||||
List<com.querydsl.core.Tuple> results =
|
||||
queryFactory
|
||||
.select(qAnimal.zoo.uid, qAnimal.count())
|
||||
.from(qAnimal)
|
||||
.where(qAnimal.zoo.uid.in(zooIds), qAnimal.isDeleted.eq(false))
|
||||
.groupBy(qAnimal.zoo.uid)
|
||||
.fetch();
|
||||
|
||||
// Map으로 변환
|
||||
Map<Long, Long> countMap = new HashMap<>();
|
||||
for (com.querydsl.core.Tuple tuple : results) {
|
||||
Long zooId = tuple.get(qAnimal.zoo.uid);
|
||||
Long count = tuple.get(qAnimal.count());
|
||||
if (zooId != null && count != null) {
|
||||
countMap.put(zooId, count);
|
||||
}
|
||||
}
|
||||
|
||||
// 조회되지 않은 Zoo는 0으로 설정
|
||||
for (Long zooId : zooIds) {
|
||||
countMap.putIfAbsent(zooId, 0L);
|
||||
}
|
||||
|
||||
return countMap;
|
||||
}
|
||||
|
||||
private BooleanExpression nameContains(String name) {
|
||||
return name != null && !name.isEmpty() ? qZoo.name.contains(name) : null;
|
||||
}
|
||||
|
||||
private BooleanExpression locationContains(String location) {
|
||||
return location != null && !location.isEmpty() ? qZoo.location.contains(location) : null;
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import com.kamco.cd.kamcoback.inference.dto.InferenceDetailDto.InferenceBatchShe
|
||||
import com.kamco.cd.kamcoback.inference.dto.InferenceProgressDto;
|
||||
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.SaveInferenceAiDto;
|
||||
import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.Status;
|
||||
import com.kamco.cd.kamcoback.inference.dto.InferenceResultsTestingDto;
|
||||
import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto;
|
||||
import com.kamco.cd.kamcoback.postgres.core.InferenceResultCoreService;
|
||||
import com.kamco.cd.kamcoback.scheduler.dto.BatchStatusDto;
|
||||
@@ -59,7 +58,9 @@ public class MapSheetInferenceJobService {
|
||||
@Value("${file.dataset-dir}")
|
||||
private String datasetDir;
|
||||
|
||||
/** 추론 진행 배치 1분 60_000 */
|
||||
/**
|
||||
* 추론 진행 배치 1분 60_000
|
||||
*/
|
||||
@Scheduled(fixedDelay = 60_000)
|
||||
public void runBatch() {
|
||||
if (isLocalProfile()) {
|
||||
@@ -250,26 +251,10 @@ public class MapSheetInferenceJobService {
|
||||
batchIds.add(sheet.getM2BatchId());
|
||||
batchIds.add(sheet.getM3BatchId());
|
||||
|
||||
List<InferenceResultsTestingDto.ShpDto> resultList =
|
||||
inferenceResultCoreService.getInferenceResults(batchIds);
|
||||
String inferenceId = "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (InferenceResultsTestingDto.ShpDto dto : resultList) {
|
||||
if (dto.getMapId() == null) {
|
||||
continue;
|
||||
}
|
||||
if (!sb.isEmpty()) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("\"").append(dto.getMapId()).append("\"");
|
||||
}
|
||||
inferenceId = sheet.getUid();
|
||||
String mapIds = sb.toString();
|
||||
String batchId = sheet.getM1BatchId() + "," + sheet.getM2BatchId() + "," + sheet.getM3BatchId();
|
||||
|
||||
// shp 파일 비동기 생성
|
||||
shpPipelineService.runPipeline(jarPath, datasetDir, batchId, inferenceId, mapIds);
|
||||
shpPipelineService.runPipeline(jarPath, datasetDir, batchId, sheet.getUid());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -442,7 +427,8 @@ public class MapSheetInferenceJobService {
|
||||
// 4) 응답 파싱
|
||||
try {
|
||||
List<Map<String, Object>> list =
|
||||
objectMapper.readValue(result.body(), new TypeReference<>() {});
|
||||
objectMapper.readValue(result.body(), new TypeReference<>() {
|
||||
});
|
||||
|
||||
if (list.isEmpty()) {
|
||||
throw new IllegalStateException("Inference response is empty");
|
||||
@@ -470,7 +456,9 @@ public class MapSheetInferenceJobService {
|
||||
return "local".equalsIgnoreCase(profile);
|
||||
}
|
||||
|
||||
/** 모델별 추론 종료 update */
|
||||
/**
|
||||
* 모델별 추론 종료 update
|
||||
*/
|
||||
private void updateProcessingEndTimeByModel(
|
||||
BatchStatusDto dto, UUID uuid, ZonedDateTime dateTime, String type) {
|
||||
SaveInferenceAiDto saveInferenceAiDto = new SaveInferenceAiDto();
|
||||
|
||||
@@ -17,7 +17,7 @@ public class ShpPipelineService {
|
||||
|
||||
@Async("shpExecutor")
|
||||
public void runPipeline(
|
||||
String jarPath, String datasetDir, String batchId, String inferenceId, String mapIds) {
|
||||
String jarPath, String datasetDir, String batchId, String inferenceId) {
|
||||
|
||||
// inferenceId 기준 동시 실행 제한
|
||||
if (!shpKeyLock.tryLock(inferenceId)) {
|
||||
@@ -27,15 +27,14 @@ public class ShpPipelineService {
|
||||
|
||||
try {
|
||||
// uid 기준 merge shp, geojson 파일 생성
|
||||
externalJarRunner.run(jarPath, batchId, inferenceId, "");
|
||||
externalJarRunner.run(jarPath, batchId, inferenceId, "", "MERGED");
|
||||
|
||||
// uid 기준 shp 파일 geoserver 등록
|
||||
String register = datasetDir + "/" + inferenceId + "/merge/" + inferenceId + ".shp";
|
||||
externalJarRunner.run(jarPath, register, inferenceId);
|
||||
|
||||
// uid 기준 도엽별 shp, geojson 파일 생성
|
||||
// TODO 도엽별은 속도 확인 후 다시 체크
|
||||
externalJarRunner.run(jarPath, batchId, inferenceId, mapIds);
|
||||
externalJarRunner.run(jarPath, batchId, inferenceId, "", "RESOLVE");
|
||||
|
||||
log.info("SHP pipeline finished. inferenceId={}", inferenceId);
|
||||
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.zoo;
|
||||
|
||||
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.AnimalDto;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.AnimalDto.Basic;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.AnimalDto.Category;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.AnimalDto.Species;
|
||||
import com.kamco.cd.kamcoback.zoo.service.AnimalService;
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Hidden
|
||||
@Tag(name = "Animal", description = "동물 관리 API")
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping({"/api/animals", "/v1/api/animals"})
|
||||
public class AnimalApiController {
|
||||
|
||||
private final AnimalService animalService;
|
||||
|
||||
/**
|
||||
* 동물 생성
|
||||
*
|
||||
* @param req 동물 생성 요청
|
||||
* @return 생성된 동물 정보
|
||||
*/
|
||||
@Operation(summary = "동물 생성", description = "새로운 동물 정보를 등록합니다.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(
|
||||
responseCode = "201",
|
||||
description = "동물 생성 성공",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = "application/json",
|
||||
schema = @Schema(implementation = AnimalDto.Basic.class))),
|
||||
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content),
|
||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
||||
})
|
||||
@PostMapping
|
||||
public ApiResponseDto<Basic> createAnimal(
|
||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(
|
||||
description = "동물 생성 요청 정보",
|
||||
required = true,
|
||||
content =
|
||||
@Content(
|
||||
mediaType = "application/json",
|
||||
schema = @Schema(implementation = AnimalDto.AddReq.class)))
|
||||
@RequestBody
|
||||
AnimalDto.AddReq req) {
|
||||
AnimalDto.Basic created = animalService.createAnimal(req);
|
||||
return ApiResponseDto.createOK(created);
|
||||
}
|
||||
|
||||
/**
|
||||
* UUID로 동물 조회
|
||||
*
|
||||
* @param uuid 동물 UUID
|
||||
* @return 동물 정보
|
||||
*/
|
||||
@Operation(summary = "동물 단건 조회", description = "UUID로 특정 동물의 상세 정보를 조회합니다.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "조회 성공",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = "application/json",
|
||||
schema = @Schema(implementation = AnimalDto.Basic.class))),
|
||||
@ApiResponse(responseCode = "404", description = "동물을 찾을 수 없음", content = @Content),
|
||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
||||
})
|
||||
@GetMapping("/{uuid}")
|
||||
public ApiResponseDto<AnimalDto.Basic> getAnimal(
|
||||
@Parameter(
|
||||
description = "조회할 동물의 UUID",
|
||||
required = true,
|
||||
example = "550e8400-e29b-41d4-a716-446655440000")
|
||||
@PathVariable
|
||||
String uuid) {
|
||||
Long id = animalService.getAnimalByUuid(uuid);
|
||||
AnimalDto.Basic animal = animalService.getAnimal(id);
|
||||
return ApiResponseDto.ok(animal);
|
||||
}
|
||||
|
||||
/**
|
||||
* UUID로 동물 삭제 (논리 삭제)
|
||||
*
|
||||
* @param uuid 동물 UUID
|
||||
* @return 삭제 성공 메시지
|
||||
*/
|
||||
@Operation(summary = "동물 삭제", description = "UUID로 특정 동물을 삭제합니다 (논리 삭제).")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(responseCode = "204", description = "삭제 성공", content = @Content),
|
||||
@ApiResponse(responseCode = "404", description = "동물을 찾을 수 없음", content = @Content),
|
||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
||||
})
|
||||
@DeleteMapping("/{uuid}")
|
||||
public ApiResponseDto<String> deleteAnimal(
|
||||
@Parameter(
|
||||
description = "삭제할 동물의 UUID",
|
||||
required = true,
|
||||
example = "550e8400-e29b-41d4-a716-446655440000")
|
||||
@PathVariable
|
||||
String uuid) {
|
||||
Long id = animalService.getAnimalByUuid(uuid);
|
||||
animalService.deleteAnimal(id);
|
||||
return ApiResponseDto.deleteOk(uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 동물 검색 (페이징)
|
||||
*
|
||||
* @param name 동물 이름 (선택)
|
||||
* @param category 서식지 타입 (선택)
|
||||
* @param species 동물종 (선택) 개, 고양이등.
|
||||
* @param page 페이지 번호 (기본값: 0)
|
||||
* @param size 페이지 크기 (기본값: 20)
|
||||
* @param sort 정렬 조건 (예: "name,asc")
|
||||
* @return 페이징 처리된 동물 목록
|
||||
*/
|
||||
@Operation(
|
||||
summary = "동물 검색",
|
||||
description =
|
||||
"다양한 조건으로 동물을 검색하고 페이징된 결과를 반환합니다. 모든 검색 조건은 선택적이며, 조건 없이 호출하면 전체 동물 목록을 반환합니다.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "검색 성공",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = "application/json",
|
||||
schema = @Schema(implementation = Page.class))),
|
||||
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
|
||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
||||
})
|
||||
@GetMapping
|
||||
public ApiResponseDto<Page<AnimalDto.Basic>> searchAnimals(
|
||||
@Parameter(description = "동물 이름 (부분 일치 검색)", example = "호랑이") @RequestParam(required = false)
|
||||
String name,
|
||||
@Parameter(description = "서식지 카테고리", example = "MAMMAL") @RequestParam(required = false)
|
||||
Category category,
|
||||
@Parameter(description = "동물 종", example = "TIGER") @RequestParam(required = false)
|
||||
Species species,
|
||||
@Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0")
|
||||
int page,
|
||||
@Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20")
|
||||
int size,
|
||||
@Parameter(description = "정렬 조건 (형식: 필드명,방향)", example = "name,asc")
|
||||
@RequestParam(required = false)
|
||||
String sort) {
|
||||
AnimalDto.SearchReq searchReq =
|
||||
new AnimalDto.SearchReq(name, category, species, page, size, sort);
|
||||
Page<AnimalDto.Basic> animals = animalService.search(searchReq);
|
||||
return ApiResponseDto.ok(animals);
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.zoo;
|
||||
|
||||
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.ZooDto;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.ZooDto.Detail;
|
||||
import com.kamco.cd.kamcoback.zoo.service.ZooService;
|
||||
import io.swagger.v3.oas.annotations.Hidden;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Hidden
|
||||
@Tag(name = "Zoo", description = "동물원 관리 API")
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping({"/api/zoos", "/v1/api/zoos"})
|
||||
public class ZooApiController {
|
||||
|
||||
private final ZooService zooService;
|
||||
|
||||
/**
|
||||
* 동물원 생성
|
||||
*
|
||||
* @param req 동물원 생성 요청
|
||||
* @return 생성된 동물원 정보 (동물 개수 포함)
|
||||
*/
|
||||
@Operation(summary = "동물원 생성", description = "새로운 동물원 정보를 등록합니다.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(
|
||||
responseCode = "201",
|
||||
description = "동물원 생성 성공",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = "application/json",
|
||||
schema = @Schema(implementation = ZooDto.Detail.class))),
|
||||
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content),
|
||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
||||
})
|
||||
@PostMapping
|
||||
public ApiResponseDto<Detail> createZoo(
|
||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(
|
||||
description = "동물원 생성 요청 정보",
|
||||
required = true,
|
||||
content =
|
||||
@Content(
|
||||
mediaType = "application/json",
|
||||
schema = @Schema(implementation = ZooDto.AddReq.class)))
|
||||
@RequestBody
|
||||
ZooDto.AddReq req) {
|
||||
ZooDto.Detail created = zooService.createZoo(req);
|
||||
return ApiResponseDto.createOK(created);
|
||||
}
|
||||
|
||||
/**
|
||||
* UUID로 동물원 조회
|
||||
*
|
||||
* @param uuid 동물원 UUID
|
||||
* @return 동물원 정보 (현재 동물 개수 포함)
|
||||
*/
|
||||
@Operation(summary = "동물원 단건 조회", description = "UUID로 특정 동물원의 상세 정보를 조회합니다.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "조회 성공",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = "application/json",
|
||||
schema = @Schema(implementation = ZooDto.Detail.class))),
|
||||
@ApiResponse(responseCode = "404", description = "동물원을 찾을 수 없음", content = @Content),
|
||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
||||
})
|
||||
@GetMapping("/{uuid}")
|
||||
public ApiResponseDto<ZooDto.Detail> getZoo(
|
||||
@Parameter(
|
||||
description = "조회할 동물원의 UUID",
|
||||
required = true,
|
||||
example = "550e8400-e29b-41d4-a716-446655440000")
|
||||
@PathVariable
|
||||
String uuid) {
|
||||
Long id = zooService.getZooByUuid(uuid);
|
||||
ZooDto.Detail zoo = zooService.getZoo(id);
|
||||
return ApiResponseDto.ok(zoo);
|
||||
}
|
||||
|
||||
/**
|
||||
* UUID로 동물원 삭제 (논리 삭제)
|
||||
*
|
||||
* @param uuid 동물원 UUID
|
||||
* @return 삭제 성공 메시지
|
||||
*/
|
||||
@Operation(summary = "동물원 삭제", description = "UUID로 특정 동물원을 삭제합니다 (논리 삭제).")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(responseCode = "204", description = "삭제 성공", content = @Content),
|
||||
@ApiResponse(responseCode = "404", description = "동물원을 찾을 수 없음", content = @Content),
|
||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
||||
})
|
||||
@DeleteMapping("/{uuid}")
|
||||
public ApiResponseDto<String> deleteZoo(
|
||||
@Parameter(
|
||||
description = "삭제할 동물원의 UUID",
|
||||
required = true,
|
||||
example = "550e8400-e29b-41d4-a716-446655440000")
|
||||
@PathVariable
|
||||
String uuid) {
|
||||
Long id = zooService.getZooByUuid(uuid);
|
||||
zooService.deleteZoo(id);
|
||||
return ApiResponseDto.deleteOk(uuid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 동물원 검색 (페이징)
|
||||
*
|
||||
* @param name 동물원 이름 (선택)
|
||||
* @param location 위치 (선택)
|
||||
* @param page 페이지 번호 (기본값: 0)
|
||||
* @param size 페이지 크기 (기본값: 20)
|
||||
* @param sort 정렬 조건 (예: "name,asc")
|
||||
* @return 페이징 처리된 동물원 목록 (각 동물원의 현재 동물 개수 포함)
|
||||
*/
|
||||
@Operation(
|
||||
summary = "동물원 검색",
|
||||
description =
|
||||
"다양한 조건으로 동물원을 검색하고 페이징된 결과를 반환합니다. 모든 검색 조건은 선택적이며, 조건 없이 호출하면 전체 동물원 목록을 반환합니다.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "검색 성공",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = "application/json",
|
||||
schema = @Schema(implementation = Page.class))),
|
||||
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
|
||||
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
|
||||
})
|
||||
@GetMapping
|
||||
public ApiResponseDto<Page<ZooDto.Detail>> searchZoos(
|
||||
@Parameter(description = "동물원 이름 (부분 일치 검색)", example = "서울동물원")
|
||||
@RequestParam(required = false)
|
||||
String name,
|
||||
@Parameter(description = "위치", example = "서울") @RequestParam(required = false)
|
||||
String location,
|
||||
@Parameter(description = "페이지 번호 (0부터 시작)", example = "0") @RequestParam(defaultValue = "0")
|
||||
int page,
|
||||
@Parameter(description = "페이지 크기", example = "20") @RequestParam(defaultValue = "20")
|
||||
int size,
|
||||
@Parameter(description = "정렬 조건 (형식: 필드명,방향)", example = "name,asc")
|
||||
@RequestParam(required = false)
|
||||
String sort) {
|
||||
ZooDto.SearchReq searchReq = new ZooDto.SearchReq(name, location, page, size, sort);
|
||||
Page<ZooDto.Detail> zoos = zooService.search(searchReq);
|
||||
return ApiResponseDto.ok(zoos);
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.zoo.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.kamco.cd.kamcoback.common.utils.enums.EnumType;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.ZonedDateTime;
|
||||
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 AnimalDto {
|
||||
|
||||
@Schema(name = "AnimalAddReq", description = "동물 생성 요청")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class AddReq {
|
||||
|
||||
private Category category;
|
||||
private Species species;
|
||||
private String name;
|
||||
private String zooUuid; // 동물원 UUID (선택)
|
||||
}
|
||||
|
||||
@Schema(name = "AnimalBasic", description = "동물 기본 정보")
|
||||
@Getter
|
||||
public static class Basic {
|
||||
|
||||
@JsonIgnore private Long id;
|
||||
private String uuid;
|
||||
private Category category;
|
||||
private Species species;
|
||||
private String name;
|
||||
|
||||
@JsonFormat(
|
||||
shape = JsonFormat.Shape.STRING,
|
||||
pattern = "yyyy-MM-dd'T'HH:mm:ssXXX",
|
||||
timezone = "Asia/Seoul")
|
||||
private ZonedDateTime createdDate;
|
||||
|
||||
@JsonFormat(
|
||||
shape = JsonFormat.Shape.STRING,
|
||||
pattern = "yyyy-MM-dd'T'HH:mm:ssXXX",
|
||||
timezone = "Asia/Seoul")
|
||||
private ZonedDateTime modifiedDate;
|
||||
|
||||
public Basic(
|
||||
Long id,
|
||||
String uuid,
|
||||
String name,
|
||||
Category category,
|
||||
Species species,
|
||||
ZonedDateTime createdDate,
|
||||
ZonedDateTime modifiedDate) {
|
||||
this.id = id;
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
this.category = category;
|
||||
this.species = species;
|
||||
this.createdDate = createdDate;
|
||||
this.modifiedDate = modifiedDate;
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum Category implements EnumType {
|
||||
// @formatter:off
|
||||
MAMMALS("100", "포유류"), // 땅에 사는 동물
|
||||
BIRDS("200", "조류"), // 하늘을 나는 동물
|
||||
FISH("300", "어류"),
|
||||
AMPHIBIANS("400", "양서류"),
|
||||
REPTILES("500", "파충류"),
|
||||
INSECTS("500", "곤충"),
|
||||
INVERTEBRATES("500", "무척추동물"),
|
||||
;
|
||||
// @formatter:on
|
||||
private final String id;
|
||||
private final String text;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum Species implements EnumType {
|
||||
// @formatter:off
|
||||
DOG("101", "개"),
|
||||
CAT("102", "강아지"),
|
||||
DOVE("201", "비둘기"),
|
||||
EAGLE("202", "독수리"),
|
||||
SALMON("301", "연어"),
|
||||
TUNA("302", "참치"),
|
||||
;
|
||||
// @formatter:on
|
||||
private final String id;
|
||||
private final String text;
|
||||
}
|
||||
|
||||
@Schema(name = "AnimalSearchReq", description = "동물 검색 요청")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class SearchReq {
|
||||
|
||||
// 검색 조건
|
||||
private String name;
|
||||
private Category category;
|
||||
private Species species;
|
||||
|
||||
// 페이징 파라미터
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.zoo.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.time.ZonedDateTime;
|
||||
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 ZooDto {
|
||||
|
||||
@Schema(name = "ZooAddReq", description = "동물원 생성 요청")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class AddReq {
|
||||
|
||||
private String name;
|
||||
private String location;
|
||||
private String description;
|
||||
}
|
||||
|
||||
@Schema(name = "ZooBasic", description = "동물원 기본 정보")
|
||||
@Getter
|
||||
public static class Basic {
|
||||
|
||||
@JsonIgnore private Long id;
|
||||
private String uuid;
|
||||
private String name;
|
||||
private String location;
|
||||
private String description;
|
||||
|
||||
@JsonFormat(
|
||||
shape = JsonFormat.Shape.STRING,
|
||||
pattern = "yyyy-MM-dd'T'HH:mm:ssXXX",
|
||||
timezone = "Asia/Seoul")
|
||||
private ZonedDateTime createdDate;
|
||||
|
||||
@JsonFormat(
|
||||
shape = JsonFormat.Shape.STRING,
|
||||
pattern = "yyyy-MM-dd'T'HH:mm:ssXXX",
|
||||
timezone = "Asia/Seoul")
|
||||
private ZonedDateTime modifiedDate;
|
||||
|
||||
public Basic(
|
||||
Long id,
|
||||
String uuid,
|
||||
String name,
|
||||
String location,
|
||||
String description,
|
||||
ZonedDateTime createdDate,
|
||||
ZonedDateTime modifiedDate) {
|
||||
this.id = id;
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
this.location = location;
|
||||
this.description = description;
|
||||
this.createdDate = createdDate;
|
||||
this.modifiedDate = modifiedDate;
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(name = "ZooDetail", description = "동물원 상세 정보 (동물 개수 포함)")
|
||||
@Getter
|
||||
public static class Detail extends Basic {
|
||||
|
||||
private Long activeAnimalCount;
|
||||
|
||||
public Detail(
|
||||
Long id,
|
||||
String uuid,
|
||||
String name,
|
||||
String location,
|
||||
String description,
|
||||
ZonedDateTime createdDate,
|
||||
ZonedDateTime modifiedDate,
|
||||
Long activeAnimalCount) {
|
||||
super(id, uuid, name, location, description, createdDate, modifiedDate);
|
||||
this.activeAnimalCount = activeAnimalCount;
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(name = "ZooSearchReq", description = "동물원 검색 요청")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class SearchReq {
|
||||
|
||||
// 검색 조건
|
||||
private String name;
|
||||
private String location;
|
||||
|
||||
// 페이징 파라미터
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.zoo.service;
|
||||
|
||||
import com.kamco.cd.kamcoback.postgres.core.AnimalCoreService;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.AnimalDto;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
public class AnimalService {
|
||||
|
||||
private final AnimalCoreService zooCoreService;
|
||||
|
||||
// 동물의 UUID로 id조회
|
||||
public Long getAnimalByUuid(String uuid) {
|
||||
return zooCoreService.getDataByUuid(uuid).getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 동물 생성
|
||||
*
|
||||
* @param req 동물 생성 요청
|
||||
* @return 생성된 동물 정보
|
||||
*/
|
||||
@Transactional
|
||||
public AnimalDto.Basic createAnimal(AnimalDto.AddReq req) {
|
||||
return zooCoreService.create(req);
|
||||
}
|
||||
|
||||
/**
|
||||
* 동물 삭제 (논리 삭제)
|
||||
*
|
||||
* @param id 동물 ID
|
||||
*/
|
||||
@Transactional
|
||||
public void deleteAnimal(Long id) {
|
||||
zooCoreService.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 동물 단건 조회
|
||||
*
|
||||
* @param id 동물 ID
|
||||
* @return 동물 정보
|
||||
*/
|
||||
public AnimalDto.Basic getAnimal(Long id) {
|
||||
return zooCoreService.getOneById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 동물 검색 (페이징)
|
||||
*
|
||||
* @param searchReq 검색 조건
|
||||
* @return 페이징 처리된 동물 목록
|
||||
*/
|
||||
public Page<AnimalDto.Basic> search(AnimalDto.SearchReq searchReq) {
|
||||
return zooCoreService.search(searchReq);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package com.kamco.cd.kamcoback.zoo.service;
|
||||
|
||||
import com.kamco.cd.kamcoback.postgres.core.ZooCoreService;
|
||||
import com.kamco.cd.kamcoback.zoo.dto.ZooDto;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
public class ZooService {
|
||||
|
||||
private final ZooCoreService zooCoreService;
|
||||
|
||||
// 동물원의 UUID로 id조회
|
||||
public Long getZooByUuid(String uuid) {
|
||||
return zooCoreService.getDataByUuid(uuid).getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 동물원 생성
|
||||
*
|
||||
* @param req 동물원 생성 요청
|
||||
* @return 생성된 동물원 정보 (동물 개수 포함)
|
||||
*/
|
||||
@Transactional
|
||||
public ZooDto.Detail createZoo(ZooDto.AddReq req) {
|
||||
return zooCoreService.create(req);
|
||||
}
|
||||
|
||||
/**
|
||||
* 동물원 삭제 (논리 삭제)
|
||||
*
|
||||
* @param id 동물원 ID
|
||||
*/
|
||||
@Transactional
|
||||
public void deleteZoo(Long id) {
|
||||
zooCoreService.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 동물원 단건 조회
|
||||
*
|
||||
* @param id 동물원 ID
|
||||
* @return 동물원 정보 (동물 개수 포함)
|
||||
*/
|
||||
public ZooDto.Detail getZoo(Long id) {
|
||||
return zooCoreService.getOneById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 동물원 검색 (페이징)
|
||||
*
|
||||
* @param searchReq 검색 조건
|
||||
* @return 페이징 처리된 동물원 목록 (각 동물원의 동물 개수 포함)
|
||||
*/
|
||||
public Page<ZooDto.Detail> search(ZooDto.SearchReq searchReq) {
|
||||
return zooCoreService.search(searchReq);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user