샘플코드 추가
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
group = 'com.cd.detection'
|
||||
version = '0.0.1-SNAPSHOT'
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: 'java'
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom "org.springframework.boot:spring-boot-dependencies:3.5.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
}
|
||||
Reference in New Issue
Block a user