feat: 항공영상관리 api 함수 임시 구현 #7

Merged
lucy merged 3 commits from feature/lucy-aerial into develop 2026-04-10 08:57:20 +09:00
33 changed files with 942 additions and 46 deletions
Showing only changes of commit 5c4df5a19a - Show all commits

View File

@@ -152,7 +152,10 @@ api-app # 메인 실행 모듈
│ │ │ ├── DabeeoDetectionApiApplication.java
│ │ │
│ │ │ ├── config # 설정 파일
│ │ │ ├── core # core service (Repository 비즈니스 서비스)
│ │ │ ├── db
│ │ │ │ ├── core # core service (Repository 비즈니스 서비스)
│ │ │ │ └── repository # Repository (DB 인터페이스/구현)
│ │ │ ├── core
│ │ │ ├── domain # 도메인
│ │ │ │ ├── imagery # 영상 데이터 관리
│ │ │ │ ├── labeling # 라벨링 툴
@@ -161,10 +164,7 @@ api-app # 메인 실행 모듈
│ │ │ │ ├── inference # 추론관리
│ │ │ │ ├── system # 시스템관리
│ │ │ │ └── log # 로그관리
│ │ │
│ │ │ ├── entity # 공용 JPA 엔티티 (DB 공통 사용)
│ │ │ │
│ │ │ └── repository # 공용 Repository (DB 공통 인터페이스/구현)
│ │ │
│ │ └── resources # 설정 및 리소스 파일
│ │ ├── application.yml
@@ -174,7 +174,6 @@ api-app # 메인 실행 모듈
│ └── build.gradle
├── infrastructure-db-postgres # Postgres 전용 설정/확장
│ ├── repository # Repository impl 구현
│ └── build.gradle
├── build.gradle
@@ -215,20 +214,19 @@ DB 관련 로직은 별도의 모듈(infrastructure-db-postgres)로 분리되어
---
### 🔄 처리 흐름
### 🔄 Database 교체
Controller → Service → Repository → DB
- `api-app/build.gradle`에서 DB 모듈 의존성 변경
(`infrastructure-db-postgres``infrastructure-db-oracle`)
---
- `application.yml` 또는 DB별 설정 파일(`application-postgres.yml`, `application-oracle.yml`)에서
데이터베이스 연결 정보 변경
### 💡 설계 포인트
- 실행 시 profile을 통해 DB 선택
- API 모듈은 DB 기술에 직접 의존하지 않음
- DB 관련 코드는 별도 모듈에서 관리
- DB 변경 시 API 코드 수정 최소화
```bash
# PostgreSQL
--spring.profiles.active=postgres
---
## 🔥 한 줄 요약
DB 기술 변경에 유연하게 대응하기 위해 persistence 레이어를 모듈로 분리했습니다.
# Oracle
--spring.profiles.active=oracle

View File

@@ -1,37 +1,41 @@
plugins {
id 'org.springframework.boot'
id 'io.spring.dependency-management'
id 'java'
}
dependencies {
runtimeOnly 'com.h2database:h2'
// postgres
// DB 변경시 변경
implementation project(':infrastructure-db-postgres')
// DB (테스트용)
runtimeOnly 'com.h2database:h2'
// Spring
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// OpenAPI
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0'
// QueryDSL
implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta"
// Lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// Devtools
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
// QueryDSL APT
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
// Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
bootJar {
archiveFileName = 'ROOT.jar'
}

View File

@@ -2,6 +2,9 @@ package com.cd.detection;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication
public class DabeeoDetectionApiApplication {

View File

@@ -0,0 +1,40 @@
package com.cd.detection.config.api;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ApiResponseDto<T> {
private final T data;
private final Error error;
private ApiResponseDto(T data, Error error) {
this.data = data;
this.error = error;
}
// 성공
public static <T> ApiResponseDto<T> ok(T data) {
return new ApiResponseDto<>(data, null);
}
// 실패
public static <T> ApiResponseDto<T> fail(String code, String message) {
return new ApiResponseDto<>(null, new Error(code, message));
}
@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;
}
}
}

View File

@@ -0,0 +1,68 @@
package com.cd.detection.db.core;
import com.cd.detection.db.repository.zoo.AnimalRepository;
import com.cd.detection.domain.zoo.dto.AnimalDto;
import com.cd.detection.domain.zoo.dto.AnimalDto.AddRequest;
import com.cd.detection.domain.zoo.dto.AnimalDto.ModifyRequest;
import com.cd.detection.domain.zoo.dto.AnimalDto.SearchListRequest;
import com.cd.detection.entity.AnimalEntity;
import com.cd.detection.entity.ZooEntity;
import jakarta.persistence.EntityNotFoundException;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class AnimalCoreService {
private final AnimalRepository animalRepository;
@Transactional
public void saveZoo(AddRequest dto) {
ZooEntity zoo = new ZooEntity();
zoo.setZooId(dto.getZooId()); // FK만 세팅
AnimalEntity animalEntity = new AnimalEntity();
animalEntity.setZoo(zoo);
animalEntity.setAnimalName(dto.getAnimalName());
animalEntity.setSpecies(dto.getSpecies());
animalEntity.setAge(dto.getAge());
animalRepository.save(animalEntity);
}
@Transactional
public void updateZoo(ModifyRequest dto) {
AnimalEntity animalEntity = animalRepository.findById(dto.getAnimalId()).orElseThrow(() -> new EntityNotFoundException("Animal Id: " + dto.getAnimalId()));
animalEntity.setAnimalName(dto.getAnimalName());
animalEntity.setSpecies(dto.getSpecies());
animalEntity.setUseYn(dto.getUseYn());
animalRepository.save(animalEntity);
}
@Transactional
public void deleteZoo(ModifyRequest dto) {
AnimalEntity animalEntity = animalRepository.findById(dto.getAnimalId()).orElseThrow(() -> new EntityNotFoundException("Animal Id: " + dto.getAnimalId()));
animalEntity.setDelYn(dto.getDelYn());
animalRepository.save(animalEntity);
}
public Page<AnimalDto.Basic> getAnimals(SearchListRequest searchListRequest) {
Page<AnimalEntity> animals = animalRepository.findAllAnimals(searchListRequest);
return animals.map(a -> {
AnimalDto.Basic res = new AnimalDto.Basic();
res.setAnimalId(a.getAnimalId());
res.setAnimalName(a.getAnimalName());
res.setSpecies(a.getSpecies());
res.setAge(a.getAge() != null ? a.getAge().toString() : null);
res.setDelYn(a.getDelYn());
res.setUseYn(a.getUseYn());
return res;
});
}
}

View File

@@ -0,0 +1,74 @@
package com.cd.detection.db.core;
import com.cd.detection.db.repository.zoo.ZooRepository;
import com.cd.detection.domain.zoo.dto.ZooDto.AddRequest;
import com.cd.detection.domain.zoo.dto.ZooDto.AnimalRes;
import com.cd.detection.domain.zoo.dto.ZooDto.ModifyRequest;
import com.cd.detection.domain.zoo.dto.ZooDto.SearchListRequest;
import com.cd.detection.domain.zoo.dto.ZooDto.ZooWithAnimalsRes;
import com.cd.detection.entity.ZooEntity;
import jakarta.persistence.EntityNotFoundException;
import java.util.List;
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
public class ZooCoreService {
private final ZooRepository zooRepository;
@Transactional
public void saveZoo(AddRequest dto) {
ZooEntity zooEntity = new ZooEntity();
zooEntity.setZooName(dto.getZooName());
zooEntity.setDescription(dto.getDescription());
zooRepository.save(zooEntity);
}
@Transactional
public void updateZoo(ModifyRequest dto) {
ZooEntity zooEntity = zooRepository.findById(dto.getId()).orElseThrow(() -> new EntityNotFoundException("Zoo Id: " + dto.getId()));
zooEntity.setZooName(dto.getZooName());
zooEntity.setDescription(dto.getDescription());
zooEntity.setUseYn(dto.getUseYn());
zooRepository.save(zooEntity);
}
@Transactional
public void deleteZoo(ModifyRequest dto) {
ZooEntity zooEntity = zooRepository.findById(dto.getId()).orElseThrow(() -> new EntityNotFoundException("Zoo Id: " + dto.getId()));
zooEntity.setDelYn(dto.getDelYn());
zooRepository.save(zooEntity);
}
public Page<ZooWithAnimalsRes> getZooWithAnimals(SearchListRequest searchListRequest) {
Page<ZooEntity> zoo = zooRepository.findAllWithAnimals(searchListRequest);
return zoo.map(z -> {
ZooWithAnimalsRes res = new ZooWithAnimalsRes();
res.setZooId(z.getZooId());
res.setZooName(z.getZooName());
res.setDescription(z.getDescription());
List<AnimalRes> animals = z.getAnimals().stream()
.map(a -> {
AnimalRes ar = new AnimalRes();
ar.setAnimalId(a.getAnimalId());
ar.setAnimalName(a.getAnimalName());
ar.setSpecies(a.getSpecies());
ar.setAge(a.getAge());
return ar;
})
.toList();
res.setAnimals(animals);
return res;
});
}
}

View File

@@ -0,0 +1,8 @@
package com.cd.detection.db.repository.zoo;
import com.cd.detection.entity.AnimalEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AnimalRepository extends JpaRepository<AnimalEntity, Long>, AnimalRepositoryCustom {
}

View File

@@ -0,0 +1,10 @@
package com.cd.detection.db.repository.zoo;
import com.cd.detection.domain.zoo.dto.AnimalDto;
import com.cd.detection.entity.AnimalEntity;
import java.util.List;
import org.springframework.data.domain.Page;
public interface AnimalRepositoryCustom {
Page<AnimalEntity> findAllAnimals(AnimalDto.SearchListRequest dto);
}

View File

@@ -0,0 +1,46 @@
package com.cd.detection.db.repository.zoo;
import com.cd.detection.domain.zoo.dto.AnimalDto.SearchListRequest;
import com.cd.detection.entity.AnimalEntity;
import com.cd.detection.entity.QAnimalEntity;
import com.cd.detection.entity.QZooEntity;
import com.cd.detection.entity.ZooEntity;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
@Repository
public class AnimalRepositoryImpl implements AnimalRepositoryCustom {
private final JPAQueryFactory queryFactory;
public AnimalRepositoryImpl(JPAQueryFactory queryFactory) {
this.queryFactory = queryFactory;
}
@Override
public Page<AnimalEntity> findAllAnimals(SearchListRequest searchListRequest) {
Pageable pageable = searchListRequest.toPageable();
QAnimalEntity animal = QAnimalEntity.animalEntity;
// content 조회
List<AnimalEntity> content = queryFactory
.selectFrom(animal)
.where(animal.delYn.eq(false))
.distinct()
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
// count 조회 (fetchJoin 제거)
Long total = queryFactory
.select(animal.count())
.from(animal)
.where(animal.delYn.eq(false))
.fetchOne();
return new PageImpl<>(content, pageable, total == null ? 0L : total);
}
}

View File

@@ -0,0 +1,8 @@
package com.cd.detection.db.repository.zoo;
import com.cd.detection.entity.ZooEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ZooRepository extends JpaRepository<ZooEntity, Long>, ZooRepositoryCustom {
}

View File

@@ -0,0 +1,11 @@
package com.cd.detection.db.repository.zoo;
import com.cd.detection.domain.zoo.dto.ZooDto.SearchListRequest;
import com.cd.detection.entity.ZooEntity;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
public interface ZooRepositoryCustom {
Page<ZooEntity> findAllWithAnimals(SearchListRequest searchListRequest);
}

View File

@@ -0,0 +1,45 @@
package com.cd.detection.db.repository.zoo;
import com.cd.detection.domain.zoo.dto.ZooDto.SearchListRequest;
import com.cd.detection.entity.QZooEntity;
import com.cd.detection.entity.ZooEntity;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
@Repository
public class ZooRepositoryImpl implements ZooRepositoryCustom {
private final JPAQueryFactory queryFactory;
public ZooRepositoryImpl(JPAQueryFactory queryFactory) {
this.queryFactory = queryFactory;
}
@Override
public PageImpl<ZooEntity> findAllWithAnimals(SearchListRequest searchListRequest) {
Pageable pageable = searchListRequest.toPageable();
QZooEntity zoo = QZooEntity.zooEntity;
// content 조회
List<ZooEntity> content = queryFactory
.selectFrom(zoo)
.leftJoin(zoo.animals).fetchJoin()
.where(zoo.delYn.eq(false))
.distinct()
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
// count 조회 (fetchJoin 제거)
Long total = queryFactory
.select(zoo.count())
.from(zoo)
.where(zoo.delYn.eq(false))
.fetchOne();
return new PageImpl<>(content, pageable, total == null ? 0L : total);
}
}

View File

@@ -0,0 +1,125 @@
package com.cd.detection.domain.zoo;
import com.cd.detection.config.api.ApiResponseDto;
import com.cd.detection.domain.zoo.dto.AnimalDto;
import com.cd.detection.domain.zoo.service.AnimalService;
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.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
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;
@Tag(name = "동물", description = "동물 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/animal")
public class AnimalController {
private final AnimalService animalService;
@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("/list")
public ApiResponseDto<Page<AnimalDto.Basic>> getZooWithAnimals(
@Parameter(description = "페이지 번호 (0부터 시작)", example = "0")
@RequestParam(defaultValue = "0")
int page,
@Parameter(description = "페이지 크기", example = "20")
@RequestParam(defaultValue = "20")
int size) {
AnimalDto.SearchListRequest searchListRequest = new AnimalDto.SearchListRequest();
searchListRequest.setPage(page);
searchListRequest.setSize(size);
return ApiResponseDto.ok(animalService.getAnimals(searchListRequest));
}
@Operation(summary = "동물 등록", description = "동물 등록 ")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "등록 성공",
content = @Content(
mediaType = "text/plain",
schema = @Schema(type = "string", example = "success")
)),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PostMapping
public ApiResponseDto<String> saveAnimal(
@io.swagger.v3.oas.annotations.parameters.RequestBody(
content = @Content(
schema = @Schema(implementation = AnimalDto.AddRequest.class)
)
)
@RequestBody AnimalDto.AddRequest dto) {
animalService.saveAnimal(dto);
return ApiResponseDto.ok("success");
}
@Operation(summary = "동물 수정", description = "동물 수정 ")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "수정 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = String.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PutMapping
public ApiResponseDto<String> updateAnimal(@RequestBody AnimalDto.ModifyRequest dto) {
animalService.updateAnimal(dto);
return ApiResponseDto.ok("success");
}
@Operation(summary = "동물 삭제", description = "동물 삭제 ")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "삭제 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = String.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@DeleteMapping
public ApiResponseDto<String> deleteAnimal(@RequestBody AnimalDto.ModifyRequest dto) {
animalService.deleteAnimal(dto);
return ApiResponseDto.ok("success");
}
}

View File

@@ -0,0 +1,121 @@
package com.cd.detection.domain.zoo;
import com.cd.detection.config.api.ApiResponseDto;
import com.cd.detection.domain.zoo.dto.ZooDto;
import com.cd.detection.domain.zoo.dto.ZooDto.SearchListRequest;
import com.cd.detection.domain.zoo.dto.ZooDto.ZooWithAnimalsRes;
import com.cd.detection.domain.zoo.service.ZooService;
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.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
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;
@Tag(name = "동물원", description = "동물원 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/zoo")
public class ZooController {
private final ZooService zooService;
@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("/list")
public ApiResponseDto<Page<ZooWithAnimalsRes>> getZooWithAnimals(
@Parameter(description = "페이지 번호 (0부터 시작)", example = "0")
@RequestParam(defaultValue = "0")
int page,
@Parameter(description = "페이지 크기", example = "20")
@RequestParam(defaultValue = "20")
int size) {
SearchListRequest searchListRequest = new SearchListRequest();
searchListRequest.setPage(page);
searchListRequest.setSize(size);
return ApiResponseDto.ok(zooService.getZooWithAnimals(searchListRequest));
}
@Operation(summary = "동물원 등록", description = "동물원 등록 ")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "등록 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = String.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PostMapping
public ApiResponseDto<String> saveZoo(@RequestBody ZooDto.AddRequest dto) {
zooService.saveZoo(dto);
return ApiResponseDto.ok("success");
}
@Operation(summary = "동물원 수정", description = "동물원 수정 ")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "수정 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = String.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PutMapping
public ApiResponseDto<String> updateZoo(@RequestBody ZooDto.ModifyRequest dto) {
zooService.updateZoo(dto);
return ApiResponseDto.ok("success");
}
@Operation(summary = "동물원 삭제", description = "동물원 삭제 ")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "삭제 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = String.class))),
@ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@DeleteMapping
public ApiResponseDto<String> deleteZoo(@RequestBody ZooDto.ModifyRequest dto) {
zooService.deleteZoo(dto);
return ApiResponseDto.ok("success");
}
}

View File

@@ -0,0 +1,78 @@
package com.cd.detection.domain.zoo.dto;
import io.swagger.v3.oas.annotations.media.Schema;
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 AnimalDto {
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class SearchListRequest {
// 페이징 파라미터
private int page = 0;
private int size = 20;
public Pageable toPageable() {
return PageRequest.of(page, size);
}
}
@Getter
@Setter
public static class AddRequest {
@Schema(description = "동물원 id")
private Long zooId;
@Schema(description = "동물명")
private String animalName;
@Schema(description = "")
private String species;
@Schema(description = "나이")
private Integer age;
}
@Getter
@Setter
public static class ModifyRequest {
@Schema(description = "동물 id")
private Long animalId;
@Schema(description = "동물명")
private String animalName;
@Schema(description = "")
private String species;
@Schema(description = "나이")
private String age;
@Schema(description = "삭제여부")
private Boolean delYn;
@Schema(description = "사용여부")
private Boolean useYn;
}
@Getter
@Setter
public static class Basic {
@Schema(description = "동물 id")
private Long animalId;
@Schema(description = "동물명")
private String animalName;
@Schema(description = "")
private String species;
@Schema(description = "나이")
private String age;
@Schema(description = "삭제여부")
private Boolean delYn;
@Schema(description = "사용여부")
private Boolean useYn;
}
}

View File

@@ -0,0 +1,66 @@
package com.cd.detection.domain.zoo.dto;
import java.time.LocalDate;
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;
public class ZooDto {
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class SearchListRequest {
// 페이징 파라미터
private int page = 0;
private int size = 20;
public Pageable toPageable() {
return PageRequest.of(page, size);
}
}
@Getter
@Setter
public static class AddRequest {
private String zooName;
private String description;
}
@Getter
@Setter
public static class ModifyRequest {
private Long id;
private String zooName;
private String description;
private Boolean useYn;
private Boolean delYn;
}
@Getter
@Setter
public static class ZooWithAnimalsRes {
private Long zooId; // 동물원 ID
private String zooName; // 동물원 이름
private String description; // 설명
private List<AnimalRes> animals;
}
@Getter
@Setter
public static class AnimalRes {
private Long animalId;
private String animalName;
private String species;
private Integer age;
}
}

View File

@@ -0,0 +1,32 @@
package com.cd.detection.domain.zoo.service;
import com.cd.detection.db.core.AnimalCoreService;
import com.cd.detection.domain.zoo.dto.AnimalDto;
import com.cd.detection.domain.zoo.dto.AnimalDto.Basic;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class AnimalService {
private final AnimalCoreService animalCoreService;
public Page<Basic> getAnimals(AnimalDto.SearchListRequest searchListRequest) {
return animalCoreService.getAnimals(searchListRequest);
}
public void saveAnimal(AnimalDto.AddRequest dto) {
animalCoreService.saveZoo(dto);
}
public void updateAnimal(AnimalDto.ModifyRequest dto) {
animalCoreService.updateZoo(dto);
}
public void deleteAnimal(AnimalDto.ModifyRequest dto) {
animalCoreService.deleteZoo(dto);
}
}

View File

@@ -0,0 +1,37 @@
package com.cd.detection.domain.zoo.service;
import com.cd.detection.db.core.ZooCoreService;
import com.cd.detection.domain.zoo.dto.ZooDto.AddRequest;
import com.cd.detection.domain.zoo.dto.ZooDto.ModifyRequest;
import com.cd.detection.domain.zoo.dto.ZooDto.SearchListRequest;
import com.cd.detection.domain.zoo.dto.ZooDto.ZooWithAnimalsRes;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ZooService {
private final ZooCoreService zooCoreService;
// 동물원, 동물 조회
public Page<ZooWithAnimalsRes> getZooWithAnimals(SearchListRequest searchListRequest) {
return zooCoreService.getZooWithAnimals(searchListRequest);
}
// 동물원 저장
public void saveZoo(AddRequest dto) {
zooCoreService.saveZoo(dto);
}
// 동물원 수정
public void updateZoo(ModifyRequest dto) {
zooCoreService.updateZoo(dto);
}
// 동물원 삭제
public void deleteZoo(ModifyRequest dto) {
zooCoreService.deleteZoo(dto);
}
}

View File

@@ -0,0 +1,56 @@
package com.cd.detection.entity;
import jakarta.persistence.*;
import java.time.ZonedDateTime;
import lombok.*;
import org.hibernate.annotations.Comment;
@Entity
@Getter
@Setter
@Table(name = "tb_animal")
@Comment("동물 정보")
public class AnimalEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "animal_id")
@Comment("동물 ID")
private Long animalId;
/**
* 동물원 엔티티 (FK)
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "zoo_id", nullable = false)
@Comment("동물원 ID (FK)")
private ZooEntity zoo;
@Column(name = "animal_name", nullable = false)
@Comment("동물 이름")
private String animalName;
@Column(name = "species")
@Comment("")
private String species;
@Column(name = "age")
@Comment("나이")
private Integer age;
@Column(name = "use_yn", nullable = false)
@Comment("사용 여부")
private Boolean useYn = true;
@Column(name = "del_yn", nullable = false)
@Comment("삭제 여부")
private Boolean delYn = false;
@Column(name = "created_dttm", nullable = false)
@Comment("생성 일시")
private ZonedDateTime createdDttm = ZonedDateTime.now();
@Column(name = "updated_dttm", nullable = false)
@Comment("수정 일시")
private ZonedDateTime updatedDttm = ZonedDateTime.now();
}

View File

@@ -0,0 +1,62 @@
package com.cd.detection.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.OneToMany;
import jakarta.persistence.Table;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Comment;
@Getter
@Setter
@Entity
@Table(name = "tb_zoo")
public class ZooEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "zoo_id")
@Comment("동물원 ID")
private Long zooId;
@OneToMany(mappedBy = "zoo", fetch = FetchType.LAZY)
private List<AnimalEntity> animals = new ArrayList<>();
@Column(name = "zoo_name", nullable = false, length = 200)
@Comment("동물원 이름")
private String zooName;
@Column(name = "description")
@Comment("설명")
private String description;
@Column(name = "use_yn", nullable = false)
@Comment("사용 여부")
private Boolean useYn = true;
@Column(name = "del_yn", nullable = false)
@Comment("삭제 여부")
private Boolean delYn = false;
@Column(name = "created_dttm", nullable = false, updatable = false)
@Comment("생성 일시")
private ZonedDateTime createdDttm = ZonedDateTime.now();
@Column(name = "updated_dttm", nullable = false)
@Comment("수정 일시")
private ZonedDateTime updatedDttm = ZonedDateTime.now();
public void addAnimal(AnimalEntity animal) {
animals.add(animal);
animal.setZoo(this);
}
}

View File

@@ -1,26 +1,30 @@
plugins {
id 'java'
id 'io.spring.dependency-management' version '1.1.7' apply false
id 'org.springframework.boot' version '3.5.7' apply false
id 'io.spring.dependency-management' version '1.1.7' apply false
}
allprojects {
group = 'com.cd.detection'
version = '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
}
}
subprojects {
apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
mavenCentral()
}
subprojects {
apply plugin: 'java'
repositories {
mavenCentral()
dependencyManagement {
imports {
mavenBom "org.springframework.boot:spring-boot-dependencies:3.5.7"
}
}
}

View File

@@ -3,6 +3,6 @@ plugins {
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'org.postgresql:postgresql'
// DB 드라이버
implementation 'org.postgresql:postgresql:42.7.3'
}