Skip to content

Commit

Permalink
refactor: 과목 검색 기능 구현 (#175)
Browse files Browse the repository at this point in the history
* feat: 과목 정보 검색 기능 구현

* test: lecture test code

* test: lecture adaptor test

* refactor: remove code smells

* refactor: response body key 설정

* refactor: remove code smells
  • Loading branch information
stophwan authored Sep 12, 2023
1 parent f3a6ed5 commit f7368ab
Show file tree
Hide file tree
Showing 35 changed files with 564 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@

import java.util.Optional;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
Expand All @@ -34,10 +38,19 @@ public ExceptionResponse handleUnAuthorizedException(Exception e) {
return ExceptionResponse.of(HttpStatus.BAD_REQUEST, getMessage(e));
}

@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ExceptionResponse handleValidationException(ConstraintViolationException e) {
log.info("validated exception occurred: {}", e.getMessage(), e);
return ExceptionResponse.of(HttpStatus.BAD_REQUEST, getViolationErrorMessage(e));
}



@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ExceptionResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.debug("validation exception occurred: {}", e.getMessage(), e);
log.debug("validation exception occurred: {}", e.getMessage(), e);
return ExceptionResponse.of(HttpStatus.BAD_REQUEST, getBindingErrorMessage(e));
}

Expand All @@ -59,6 +72,10 @@ private String getMessage(Exception e) {
return Optional.ofNullable(e.getCause()).map(Throwable::getMessage).orElse(e.getMessage());
}

private String getViolationErrorMessage(ConstraintViolationException e) {
return e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).findFirst().orElse(null);
}

private String getBindingErrorMessage(MethodArgumentNotValidException e) {
Optional<ObjectError> objectError = e.getBindingResult().getAllErrors().stream().findFirst();
return objectError.map(DefaultMessageSourceResolvable::getDefaultMessage).orElse(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.plzgraduate.myongjigraduatebe.lecture.adapter.in.web;

import javax.validation.constraints.Size;

import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.plzgraduate.myongjigraduatebe.core.meta.WebAdapter;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.search.SearchLectureCommand;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.search.SearchLectureResponse;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.search.SearchLectureUseCase;

import lombok.RequiredArgsConstructor;

@WebAdapter
@RequiredArgsConstructor
@RequestMapping("/api/v1/lectures")
@Validated
class SearchLectureController {

private final SearchLectureUseCase searchLectureUseCase;

@GetMapping
public SearchLectureResponse searchLecture(
@RequestParam(defaultValue = "name") String type,
@RequestParam @Size(min = 2, message = "검색어를 2자리 이상 입력해주세요.") String keyword
) {
return searchLectureUseCase.searchLectures(SearchLectureCommand.toCommand(type, keyword));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.plzgraduate.myongjigraduatebe.lecture.adapter.out;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
Expand All @@ -21,6 +22,7 @@ public class LectureJpaEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false, unique = true)
private String lectureCode;

private String name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@

import com.plzgraduate.myongjigraduatebe.core.meta.PersistenceAdapter;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.out.FindLecturePort;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.out.SearchLecturePort;
import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture;

import lombok.RequiredArgsConstructor;

@PersistenceAdapter
@RequiredArgsConstructor
class LecturePersistenceAdaptor implements FindLecturePort {
class LecturePersistenceAdaptor implements FindLecturePort, SearchLecturePort {

private final LectureQueryRepository lectureQueryRepository;
private final LectureRepository lectureRepository;
private final LectureMapper lectureMapper;

Expand All @@ -31,4 +33,12 @@ public List<Lecture> findLecturesByIds(List<Long> lectureIds) {
.map(lectureMapper::mapToLectureDomainEntity)
.collect(Collectors.toList());
}

@Override
public List<Lecture> searchLectureByNameOrCode(String type, String keyword) {
List<LectureJpaEntity> lectureJpaEntities = lectureQueryRepository.searchByNameOrCode(type, keyword);
return lectureJpaEntities.stream()
.map(lectureMapper::mapToLectureDomainEntity)
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.plzgraduate.myongjigraduatebe.lecture.adapter.out;

import java.util.List;

import org.springframework.stereotype.Repository;

import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;

import lombok.RequiredArgsConstructor;

@Repository
@RequiredArgsConstructor
class LectureQueryRepository {

private final JPAQueryFactory jpaQueryFactory;
private static final QLectureJpaEntity lecture = QLectureJpaEntity.lectureJpaEntity;

public List<LectureJpaEntity> searchByNameOrCode(String type, String keyword) {
return jpaQueryFactory.selectFrom(lecture)
.where(equalsType(type, keyword))
.fetch();
}

private BooleanExpression equalsType(String type, String keyword) {
if (type.equals("code")) {
return lecture.lectureCode.contains(keyword);
}
return lecture.name.contains(keyword);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.plzgraduate.myongjigraduatebe.lecture.application.port.in;
package com.plzgraduate.myongjigraduatebe.lecture.application.port.in.find;

import java.util.List;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.plzgraduate.myongjigraduatebe.lecture.application.port.in;
package com.plzgraduate.myongjigraduatebe.lecture.application.port.in.find;

import java.util.List;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.plzgraduate.myongjigraduatebe.lecture.application.port.in.search;

import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
public class LectureResponse {
private final Long id;
private final String lectureCode;
private final String name;
private final int credit;
private final boolean isRevoked;

@Builder
private LectureResponse(Long id, String lectureCode, String name, int credit, boolean isRevoked) {
this.id = id;
this.lectureCode = lectureCode;
this.name = name;
this.credit = credit;
this.isRevoked = isRevoked;
}

public static LectureResponse of(Lecture lecture) {
return LectureResponse.builder()
.id(lecture.getId())
.lectureCode(lecture.getLectureCode())
.name(lecture.getName())
.credit(lecture.getCredit())
.isRevoked(lecture.getIsRevoked() == 0)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.plzgraduate.myongjigraduatebe.lecture.application.port.in.search;

import lombok.Builder;
import lombok.Getter;

@Getter
public class SearchLectureCommand {
private final String type;
private final String keyword;

@Builder
private SearchLectureCommand(String type, String keyword) {
this.type = type;
this.keyword = keyword;
}

public static SearchLectureCommand toCommand(String type, String keyword) {
return SearchLectureCommand.builder()
.type(type)
.keyword(keyword)
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.plzgraduate.myongjigraduatebe.lecture.application.port.in.search;

import java.util.List;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class SearchLectureResponse {
private List<LectureResponse> lectures;

@Builder
private SearchLectureResponse(List<LectureResponse> lectures) {
this.lectures = lectures;
}

public static SearchLectureResponse from(List<LectureResponse> lectures) {
return SearchLectureResponse.builder()
.lectures(lectures)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.plzgraduate.myongjigraduatebe.lecture.application.port.in.search;

public interface SearchLectureUseCase {
SearchLectureResponse searchLectures(SearchLectureCommand searchLectureCommand);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.plzgraduate.myongjigraduatebe.lecture.application.port.out;

import java.util.List;

import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture;

public interface SearchLecturePort {
List<Lecture> searchLectureByNameOrCode(String type, String keyword);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.plzgraduate.myongjigraduatebe.lecture.application.service;
package com.plzgraduate.myongjigraduatebe.lecture.application.service.find;

import java.util.List;

import org.springframework.transaction.annotation.Transactional;

import com.plzgraduate.myongjigraduatebe.core.meta.UseCase;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.FindLecturesByIdUseCase;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.find.FindLecturesByIdUseCase;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.out.FindLecturePort;
import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.plzgraduate.myongjigraduatebe.lecture.application.service;
package com.plzgraduate.myongjigraduatebe.lecture.application.service.find;

import java.util.List;

import org.springframework.transaction.annotation.Transactional;

import com.plzgraduate.myongjigraduatebe.core.meta.UseCase;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.FindLecturesByLectureCodeUseCase;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.find.FindLecturesByLectureCodeUseCase;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.out.FindLecturePort;
import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.plzgraduate.myongjigraduatebe.lecture.application.service.search;

import java.util.List;
import java.util.stream.Collectors;

import com.plzgraduate.myongjigraduatebe.core.meta.UseCase;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.search.SearchLectureCommand;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.search.LectureResponse;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.search.SearchLectureResponse;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.search.SearchLectureUseCase;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.out.SearchLecturePort;
import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture;

import lombok.RequiredArgsConstructor;

@UseCase
@RequiredArgsConstructor
public class SearchLectureService implements SearchLectureUseCase {

private final SearchLecturePort searchLecturePort;
@Override
public SearchLectureResponse searchLectures(SearchLectureCommand searchLectureCommand) {
List<Lecture> lectures = searchLecturePort.searchLectureByNameOrCode(
searchLectureCommand.getType(), searchLectureCommand.getKeyword());
return SearchLectureResponse.from(lectures.stream().map(LectureResponse::of).collect(Collectors.toList()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import org.springframework.transaction.annotation.Transactional;

import com.plzgraduate.myongjigraduatebe.core.meta.UseCase;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.FindLecturesByLectureCodeUseCase;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.find.FindLecturesByLectureCodeUseCase;
import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture;
import com.plzgraduate.myongjigraduatebe.takenlecture.application.port.in.save.SaveTakenLectureCommand;
import com.plzgraduate.myongjigraduatebe.takenlecture.application.port.in.save.SaveTakenLectureFromParsingTextUseCase;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import org.springframework.transaction.annotation.Transactional;

import com.plzgraduate.myongjigraduatebe.core.meta.UseCase;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.FindLecturesByIdUseCase;
import com.plzgraduate.myongjigraduatebe.lecture.application.port.in.find.FindLecturesByIdUseCase;
import com.plzgraduate.myongjigraduatebe.lecture.domain.model.Lecture;
import com.plzgraduate.myongjigraduatebe.takenlecture.application.port.in.update.UpdateTakenLectureCommand;
import com.plzgraduate.myongjigraduatebe.takenlecture.application.port.in.update.UpdateTakenLectureUseCase;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.plzgraduate.myongjigraduatebe.user.domain.model;

import java.time.Instant;
import java.util.Objects;

import org.springframework.security.crypto.password.PasswordEncoder;

Expand Down Expand Up @@ -84,4 +85,18 @@ private static int parseEntryYearInStudentNumber(String studentNumber) {
return Integer.parseInt(studentNumber.substring(2, 4));
}

@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
User user = (User)o;
return Objects.equals(authId, user.authId) && Objects.equals(studentNumber, user.studentNumber);
}

@Override
public int hashCode() {
return Objects.hash(authId, studentNumber);
}
}
2 changes: 1 addition & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ spring:

jpa:
hibernate:
ddl-auto: create
ddl-auto: update
properties:
hibernate:
show_sql: true
Expand Down
Loading

0 comments on commit f7368ab

Please sign in to comment.