[UPDATE] SPEC 수정

main
bjkim 11 months ago
parent cbfb56ac52
commit 10602de6c9

@ -12,6 +12,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.ModelAttribute;
import java.util.List;
@ -28,6 +29,7 @@ public class ProjectController {
public ResponseEntity<List<ProjectEntity>> getAllProjects() {
return ResponseEntity.ok(projectService.findAll());
}
@Operation(summary = "ID로 프로젝트 조회")
@GetMapping("/{id}")
public ResponseEntity<ProjectEntity> getProjectById(
@ -41,7 +43,8 @@ public class ProjectController {
@Operation(summary = "검색 및 페이지네이션 프로젝트 목록 조회")
@GetMapping("/search")
public ResponseEntity<Page<ProjectEntity>> searchProjects(BaseSearchRequest request) {
public ResponseEntity<Page<ProjectEntity>> searchProjects(
@ModelAttribute BaseSearchRequest request) {
Page<ProjectEntity> page = projectService.search(request);
return ResponseEntity.ok(page);
}
@ -69,7 +72,6 @@ public class ProjectController {
.orElse(ResponseEntity.notFound().build());
}
@Operation(summary = "프로젝트 삭제")
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteProject(

@ -7,6 +7,8 @@ import org.hibernate.annotations.Comment;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import java.time.LocalDate;
@Schema(description = "프로젝트")
@Comment("프로젝트")
@Entity
@ -39,11 +41,11 @@ public class ProjectEntity {
@Schema(description = "프로젝트 시작일", example = "2025-08-01")
@Comment("프로젝트 시작일")
private String prjStartDt;
private LocalDate prjStartDt;
@Schema(description = "프로젝트 종료일", example = "2025-12-31")
@Comment("프로젝트 종료일")
private String prjEndDt;
private LocalDate prjEndDt;
@Schema(description = "삭제 여부", example = "N")
@Comment("삭제 여부")

@ -3,6 +3,7 @@ package kr.re.etri.security.jwt.payload.request;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate;
@ -10,19 +11,27 @@ import java.time.LocalDate;
@Setter
public class BaseSearchRequest {
private int page = 0; // 페이지 번호 (0부터 시작)
private int size = 10; // 한 페이지당 출력 건수
@Schema(description = "페이지 번호 (0부터 시작)", defaultValue = "0")
private int page = 0;
private String keyword; // 공통 키워드 검색
@Schema(description = "한 페이지당 출력 건수", defaultValue = "10")
private int size = 10;
private String searchType; // 검색 유형 (예: "전체", "제목", "작성자" 등)
@Schema(description = "공통 키워드 검색")
private String keyword;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate startDate; // 등록일자 검색 시작
@Schema(description = "검색 유형 (예: 전체, 제목, 작성자 등)", example = "전체")
private String searchType;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate endDate; // 등록일자 검색 종료
@Schema(description = "등록일자 검색 시작", example = "2025-08-01")
private String startDate;
private String sortField = "id"; // 정렬 기준 필드명
private String sortDirection = "DESC"; // 정렬 방향: ASC / DESC
@Schema(description = "등록일자 검색 종료", example = "2025-08-31")
private String endDate;
@Schema(description = "정렬 기준 필드명", defaultValue = "id", example = "id")
private String sortField = "id";
@Schema(description = "정렬 방향: ASC / DESC", defaultValue = "DESC", allowableValues = {"ASC", "DESC"})
private String sortDirection = "DESC";
}

@ -4,8 +4,9 @@ import kr.re.etri.security.jwt.entity.ProjectEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface ProjectRepository extends JpaRepository<ProjectEntity, Long> {
public interface ProjectRepository extends JpaRepository<ProjectEntity, Long>, JpaSpecificationExecutor<ProjectEntity> {
boolean existsByPrjCd(String prjCd);
Page<ProjectEntity> findByPrjNmContainingIgnoreCase(String keyword, Pageable pageable);

@ -4,12 +4,17 @@ import kr.re.etri.security.jwt.entity.ProjectEntity;
import kr.re.etri.security.jwt.payload.request.BaseSearchRequest;
import kr.re.etri.security.jwt.payload.request.ProjectRequest;
import kr.re.etri.security.jwt.repository.ProjectRepository;
import kr.re.etri.security.jwt.specification.ProjectSpecification;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.List;
import java.util.Optional;
@ -19,6 +24,7 @@ import java.util.Optional;
public class ProjectService {
private final ProjectRepository projectRepository;
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public List<ProjectEntity> findAll() {
return projectRepository.findAll();
@ -35,10 +41,26 @@ public class ProjectService {
Sort.by(Sort.Direction.fromString(request.getSortDirection()), request.getSortField())
);
if (request.getKeyword() != null && !request.getKeyword().isEmpty()) {
return projectRepository.findByPrjNmContainingIgnoreCase(request.getKeyword(), pageable);
} else {
return projectRepository.findAll(pageable);
// String -> LocalDate 변환
LocalDate startDate = parseDate(request.getStartDate());
LocalDate endDate = parseDate(request.getEndDate());
Specification<ProjectEntity> spec = ProjectSpecification.searchByConditions(
request.getSearchType(),
request.getKeyword(),
startDate,
endDate
);
return projectRepository.findAll(spec, pageable);
}
private LocalDate parseDate(String dateStr) {
if (dateStr == null || dateStr.isBlank()) return null;
try {
return LocalDate.parse(dateStr, formatter);
} catch (DateTimeParseException e) {
throw new IllegalArgumentException("날짜 형식이 잘못되었습니다. yyyy-MM-dd 형식이어야 합니다: " + dateStr);
}
}

@ -0,0 +1,51 @@
package kr.re.etri.security.jwt.specification;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import kr.re.etri.security.jwt.entity.ProjectEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.jpa.domain.Specification;
import java.time.LocalDate;
import java.util.Set;
@Slf4j
public class ProjectSpecification {
public static Specification<ProjectEntity> searchByConditions(
String searchType, String keyword,
LocalDate startDate, LocalDate endDate) {
return (root, query, cb) -> {
Predicate predicate = cb.conjunction();
// 문자열 필드만 지정 (날짜 필드 제외)
Set<String> stringFields = Set.of("prjNm", "prjDesc", "delYn");
if (keyword != null && !keyword.isEmpty()) {
if ("전체".equalsIgnoreCase(searchType) || "all".equalsIgnoreCase(searchType)) {
predicate = cb.and(predicate, cb.or(
cb.like(cb.lower(root.get("prjNm")), "%" + keyword.toLowerCase() + "%"),
cb.like(cb.lower(root.get("prjDesc")), "%" + keyword.toLowerCase() + "%"),
cb.like(cb.lower(root.get("delYn")), "%" + keyword.toLowerCase() + "%")
));
} else if (stringFields.contains(searchType)) {
predicate = cb.and(predicate,
cb.like(cb.lower(root.get(searchType)), "%" + keyword.toLowerCase() + "%"));
}
// searchType이 날짜 컬럼이면 keyword 검색 안함
}
if (startDate != null) {
predicate = cb.and(predicate,
cb.greaterThanOrEqualTo(root.get("prjStartDt"), startDate));
}
if (endDate != null) {
predicate = cb.and(predicate,
cb.lessThanOrEqualTo(root.get("prjEndDt"), endDate));
}
return predicate;
};
}
}

@ -3,7 +3,7 @@ spring.datasource.username=cuuva
spring.datasource.password=cuuva
spring.jpa.database-platform=org.hibernate.dialect.MariaDBDialect
spring.jpa.hibernate.ddl-auto= create-drop
spring.jpa.hibernate.ddl-auto=create-only
spring.sql.init.mode=always

@ -1,5 +1,5 @@
-- src/main/resources/data.sql
INSERT INTO tb_role (id, name) VALUES (1, 'ROLE_USER');
INSERT INTO tb_role (id, name) VALUES (2, 'ROLE_MODERATOR');
INSERT INTO tb_role (id, name) VALUES (3, 'ROLE_ADMIN');
# INSERT INTO tb_role (id, name) VALUES (1, 'ROLE_USER');
# INSERT INTO tb_role (id, name) VALUES (2, 'ROLE_MODERATOR');
# INSERT INTO tb_role (id, name) VALUES (3, 'ROLE_ADMIN');

Loading…
Cancel
Save