From ec874ff62297ff26e3d0bed739a90acd03d3f962 Mon Sep 17 00:00:00 2001 From: bjkim Date: Tue, 5 Aug 2025 14:32:11 +0900 Subject: [PATCH] =?UTF-8?q?[ADD]=20=EC=9B=8C=ED=81=AC=ED=94=8C=EB=A1=9C?= =?UTF-8?q?=EC=9A=B0=20Search=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/WorkflowController.java | 13 +++- .../etri/autoflow/entity/WorkflowEntity.java | 3 +- .../repository/WorkflowRepository.java | 3 +- .../autoflow/service/WorkflowService.java | 30 ++++++++ .../specification/WorkflowSpecification.java | 72 +++++++++++++++++++ 5 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 src/main/java/kr/re/etri/autoflow/specification/WorkflowSpecification.java diff --git a/src/main/java/kr/re/etri/autoflow/controllers/WorkflowController.java b/src/main/java/kr/re/etri/autoflow/controllers/WorkflowController.java index 8130e2b..fe29173 100644 --- a/src/main/java/kr/re/etri/autoflow/controllers/WorkflowController.java +++ b/src/main/java/kr/re/etri/autoflow/controllers/WorkflowController.java @@ -3,9 +3,12 @@ package kr.re.etri.autoflow.controllers; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import kr.re.etri.autoflow.entity.ProjectEntity; import kr.re.etri.autoflow.entity.WorkflowEntity; +import kr.re.etri.autoflow.payload.request.BaseSearchRequest; import kr.re.etri.autoflow.service.WorkflowService; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -35,6 +38,14 @@ public class WorkflowController { .orElse(ResponseEntity.notFound().build()); } + @Operation(summary = "워크플로우 검색 및 페이지네이션 프로젝트 목록 조회") + @GetMapping("/search") + public ResponseEntity> searchProjects( + @ModelAttribute BaseSearchRequest request) { + Page page = workflowService.search(request); + return ResponseEntity.ok(page); + } + @Operation(summary = "워크플로우 등록") @PostMapping public ResponseEntity createWorkflow(@RequestBody WorkflowEntity workflow) { @@ -49,7 +60,7 @@ public class WorkflowController { return workflowService.findById(id) .map(existing -> { - workflow.setWorkflowId(id); + workflow.setId(id); return ResponseEntity.ok(workflowService.save(workflow)); }) .orElse(ResponseEntity.notFound().build()); diff --git a/src/main/java/kr/re/etri/autoflow/entity/WorkflowEntity.java b/src/main/java/kr/re/etri/autoflow/entity/WorkflowEntity.java index 2416c1b..aec0f3e 100644 --- a/src/main/java/kr/re/etri/autoflow/entity/WorkflowEntity.java +++ b/src/main/java/kr/re/etri/autoflow/entity/WorkflowEntity.java @@ -18,8 +18,7 @@ public class WorkflowEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Schema(description = "워크플로우 ID", example = "null") - private Long workflowId; - + private Long Id; @Schema(description = "워크플로우 이름", example = "데이터 전처리 워크플로우") private String workflowName; diff --git a/src/main/java/kr/re/etri/autoflow/repository/WorkflowRepository.java b/src/main/java/kr/re/etri/autoflow/repository/WorkflowRepository.java index aedc3dd..efbb29e 100644 --- a/src/main/java/kr/re/etri/autoflow/repository/WorkflowRepository.java +++ b/src/main/java/kr/re/etri/autoflow/repository/WorkflowRepository.java @@ -2,6 +2,7 @@ package kr.re.etri.autoflow.repository; import kr.re.etri.autoflow.entity.WorkflowEntity; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -public interface WorkflowRepository extends JpaRepository { +public interface WorkflowRepository extends JpaRepository, JpaSpecificationExecutor { } diff --git a/src/main/java/kr/re/etri/autoflow/service/WorkflowService.java b/src/main/java/kr/re/etri/autoflow/service/WorkflowService.java index 45aa7a9..eda2d26 100644 --- a/src/main/java/kr/re/etri/autoflow/service/WorkflowService.java +++ b/src/main/java/kr/re/etri/autoflow/service/WorkflowService.java @@ -1,10 +1,21 @@ package kr.re.etri.autoflow.service; +import kr.re.etri.autoflow.entity.ProjectEntity; import kr.re.etri.autoflow.entity.WorkflowEntity; +import kr.re.etri.autoflow.payload.request.BaseSearchRequest; import kr.re.etri.autoflow.repository.WorkflowRepository; +import kr.re.etri.autoflow.specification.ProjectSpecification; +import kr.re.etri.autoflow.specification.WorkflowSpecification; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; +import java.time.LocalDate; +import java.time.format.DateTimeParseException; import java.util.List; import java.util.Optional; @@ -13,6 +24,8 @@ import java.util.Optional; public class WorkflowService { private final WorkflowRepository workflowRepository; + private final WorkflowSpecification workflowSpecification; + public List findAll() { return workflowRepository.findAll(); @@ -33,4 +46,21 @@ public class WorkflowService { } return false; } + + public Page search(BaseSearchRequest request) { + int pageIndex = request.getPage() > 0 ? request.getPage() - 1 : 0; + + Pageable pageable = PageRequest.of( + pageIndex, + request.getSize(), + Sort.by(Sort.Direction.fromString(request.getSortDirection()), request.getSortField()) + ); + + Specification spec = workflowSpecification.searchByConditions( + request.getSearchType(), + request.getKeyword() + ); + + return workflowRepository.findAll(spec, pageable); + } } diff --git a/src/main/java/kr/re/etri/autoflow/specification/WorkflowSpecification.java b/src/main/java/kr/re/etri/autoflow/specification/WorkflowSpecification.java new file mode 100644 index 0000000..8e3fcb8 --- /dev/null +++ b/src/main/java/kr/re/etri/autoflow/specification/WorkflowSpecification.java @@ -0,0 +1,72 @@ +package kr.re.etri.autoflow.specification; + +import jakarta.annotation.PostConstruct; +import jakarta.persistence.EntityManager; +import jakarta.persistence.metamodel.Attribute; +import jakarta.persistence.metamodel.EntityType; +import jakarta.persistence.metamodel.Metamodel; +import kr.re.etri.autoflow.entity.ProjectEntity; +import kr.re.etri.autoflow.entity.WorkflowEntity; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Component; + +import jakarta.persistence.criteria.Predicate; +import java.time.LocalDate; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class WorkflowSpecification { + + private final EntityManager entityManager; + + private Set stringFields; + + public WorkflowSpecification(EntityManager entityManager) { + this.entityManager = entityManager; + } + + // 스프링 빈 초기화 후 실행 + @PostConstruct + public void init() { + Metamodel metamodel = entityManager.getMetamodel(); + EntityType entityType = metamodel.entity(WorkflowEntity.class); + + // 문자열 타입 필드명만 추출 + stringFields = entityType.getAttributes().stream() + .filter(attr -> attr.getJavaType().equals(String.class)) + .map(Attribute::getName) + .collect(Collectors.toSet()); + + log.info("ProjectEntity string fields: {}", stringFields); + } + + public Specification searchByConditions( + String searchType, String keyword) { + + return (root, query, cb) -> { + Predicate predicate = cb.conjunction(); + + if (keyword != null && !keyword.isEmpty()) { + if (searchType == null || searchType.isEmpty() || + "전체".equalsIgnoreCase(searchType) || "all".equalsIgnoreCase(searchType)) { + Predicate orPredicate = cb.disjunction(); + for (String field : stringFields) { + orPredicate = cb.or(orPredicate, + cb.like(cb.lower(root.get(field)), "%" + keyword.toLowerCase() + "%")); + } + predicate = cb.and(predicate, orPredicate); + + } else if (stringFields.contains(searchType)) { + predicate = cb.and(predicate, + cb.like(cb.lower(root.get(searchType)), "%" + keyword.toLowerCase() + "%")); + } + // 날짜 타입 필드 검색 시 무시 + } + + return predicate; + }; + } +}