Skip to content

Commit

Permalink
Merge pull request #21 from mju-likelion/feature/introduce-get-studen…
Browse files Browse the repository at this point in the history
…tid-api-#20

Feature/#20 특정 학번 자기소개서 조회 api 개발
  • Loading branch information
Dh3356 authored Feb 26, 2024
2 parents fe13f9a + 71c5c89 commit 1a97bd9
Show file tree
Hide file tree
Showing 15 changed files with 214 additions and 2 deletions.
9 changes: 9 additions & 0 deletions src/main/java/org/mjulikelion/baker/config/CacheConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.mjulikelion.baker.config;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class CacheConfig {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.mjulikelion.baker.constant;

public class RegexPatterns {
public static final String APPLICATION_STUDENT_ID_PATTERN = "^60\\d{6}$";//60으로 시작하고 그 뒤로 숫자 6개인 문자열
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import org.mjulikelion.baker.dto.response.ResponseDto;
import org.mjulikelion.baker.errorcode.ErrorCode;
import org.mjulikelion.baker.errorcode.ValidationErrorCode;
import org.mjulikelion.baker.exception.ApplicationIntroduceNotFoundException;
import org.mjulikelion.baker.exception.ApplicationNotFoundException;
import org.mjulikelion.baker.exception.AuthenticationException;
import org.mjulikelion.baker.exception.InvalidDataException;
import org.mjulikelion.baker.exception.JwtException;
Expand Down Expand Up @@ -144,4 +146,24 @@ public ResponseEntity<ResponseDto<Void>> handleAuthenticationException(
ResponseDto<Void> responseDto = getFromCustomException(authenticationException);
return new ResponseEntity<>(responseDto, HttpStatus.UNAUTHORIZED);
}

//ApplicationNotFoundException 예외를 처리하는 핸들러(해당 지원서가 존재하지 않는 경우)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(ApplicationNotFoundException.class)
public ResponseEntity<ResponseDto<Void>> handleApplicationNotFoundException(
ApplicationNotFoundException applicationNotFoundException) {
log.error("ApplicationNotFoundException: {}", applicationNotFoundException.getMessage());
ResponseDto<Void> responseDto = getFromCustomException(applicationNotFoundException);
return new ResponseEntity<>(responseDto, HttpStatus.NOT_FOUND);
}

//ApplicationIntroduceNotFoundException 예외를 처리하는 핸들러(해당 지원서에 자기소개가 존재하지 않는 경우)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(ApplicationIntroduceNotFoundException.class)
public ResponseEntity<ResponseDto<Void>> handleApplicationIntroduceNotFoundException(
ApplicationIntroduceNotFoundException applicationIntroduceNotFoundException) {
log.error("ApplicationIntroduceNotFoundException: {}", applicationIntroduceNotFoundException.getMessage());
ResponseDto<Void> responseDto = getFromCustomException(applicationIntroduceNotFoundException);
return new ResponseEntity<>(responseDto, HttpStatus.NOT_FOUND);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.mjulikelion.baker.controller;

import static org.mjulikelion.baker.constant.RegexPatterns.APPLICATION_STUDENT_ID_PATTERN;

import jakarta.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import org.mjulikelion.baker.dto.response.ResponseDto;
import org.mjulikelion.baker.dto.response.introduce.IntroduceGetResponseData;
import org.mjulikelion.baker.service.introduce.IntroduceQueryService;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@AllArgsConstructor
@RequestMapping("introduce")
public class IntroduceController {
private final IntroduceQueryService introduceQueryService;

@GetMapping()
@Cacheable(value = "applicationByStudentId", key = "#studentId")
public ResponseEntity<ResponseDto<IntroduceGetResponseData>> getStudentIntroduce(
@RequestParam(value = "studentId")
@Pattern(regexp = APPLICATION_STUDENT_ID_PATTERN, message = "학번이 형식에 맞지 않습니다.") String studentId
) {
return this.introduceQueryService.getStudentIntroduce(studentId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.mjulikelion.baker.dto.response.introduce;

import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
import lombok.Builder;
import org.mjulikelion.baker.model.Part;
import org.mjulikelion.baker.vo.IntroduceDetailVO;

@Builder
public class IntroduceGetResponseData {
@JsonProperty
private final Part part;
@JsonProperty
private final List<IntroduceDetailVO> introduceDetailVOList;
}
2 changes: 2 additions & 0 deletions src/main/java/org/mjulikelion/baker/errorcode/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public enum ErrorCode {
//잘못된 경로, 메소드 오류들
METHOD_NOT_ALLOWED_ERROR("4050", "허용되지 않은 메소드 입니다."),//허용되지 않은 메소드
NO_RESOURCE_ERROR("4040", "해당 리소스를 찾을 수 없습니다."),//리소스를 찾을 수 없음(잘못된 URI)
APPLICATION_NOT_FOUND_ERROR("4041", "해당 지원서가 존재하지 않습니다."),//지원서가 존재하지 않음
APPLICATION_INTRODUCE_NOT_FOUND_ERROR("4042", "해당 지원서에 자기소개가 존재하지 않습니다."),//자기소개가 존재하지 않음
//잘못된 데이터 오류들
INVALID_REQUEST_FORMAT_ERROR("4000", "유효하지 않은 Body 형식입니다."),//요청 형식이 잘못됨
INVALID_PART_ERROR("4001", "유효하지 않은 파트입니다."),//유효하지 않은 파트
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.mjulikelion.baker.exception;

import org.mjulikelion.baker.errorcode.ErrorCode;

public class ApplicationIntroduceNotFoundException extends CustomException {
public ApplicationIntroduceNotFoundException(ErrorCode errorCode) {
super(errorCode);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.mjulikelion.baker.exception;

import org.mjulikelion.baker.errorcode.ErrorCode;

public class ApplicationNotFoundException extends CustomException {
public ApplicationNotFoundException(ErrorCode errorCode) {
super(errorCode);
}
}
2 changes: 1 addition & 1 deletion src/main/java/org/mjulikelion/baker/model/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public class Application {
@CreatedDate
private Date createdAt;

@OneToMany(mappedBy = "application", orphanRemoval = true, fetch = FetchType.LAZY)
@OneToMany(mappedBy = "application", orphanRemoval = true, fetch = FetchType.EAGER)
private List<ApplicationIntroduce> introduces;

@OneToMany(mappedBy = "application", orphanRemoval = true, fetch = FetchType.LAZY)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class ApplicationIntroduce {
@GeneratedValue(strategy = GenerationType.AUTO, generator = "uuid2")
private UUID id;

@ManyToOne(cascade = CascadeType.ALL, optional = false, fetch = FetchType.EAGER)
@ManyToOne(cascade = CascadeType.ALL, optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "application_id")
private Application application;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package org.mjulikelion.baker.repository;

import java.util.List;
import java.util.UUID;
import org.mjulikelion.baker.model.ApplicationIntroduce;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ApplicationIntroduceRepository extends JpaRepository<ApplicationIntroduce, UUID> {
List<ApplicationIntroduce> findByApplicationId(UUID applicationId);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.mjulikelion.baker.repository;

import java.util.Optional;
import java.util.UUID;
import org.mjulikelion.baker.model.Application;
import org.mjulikelion.baker.model.Part;
Expand All @@ -10,5 +11,7 @@
public interface ApplicationRepository extends JpaRepository<Application, UUID> {
Long countAllByPart(Part part);

Optional<Application> findByStudentId(String studentId);

Page<Application> findAllByPart(Part part, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.mjulikelion.baker.service.introduce;

import org.mjulikelion.baker.dto.response.ResponseDto;
import org.mjulikelion.baker.dto.response.introduce.IntroduceGetResponseData;
import org.springframework.http.ResponseEntity;

public interface IntroduceQueryService {
ResponseEntity<ResponseDto<IntroduceGetResponseData>> getStudentIntroduce(String studentId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package org.mjulikelion.baker.service.introduce;

import static org.mjulikelion.baker.errorcode.ErrorCode.APPLICATION_INTRODUCE_NOT_FOUND_ERROR;
import static org.mjulikelion.baker.errorcode.ErrorCode.APPLICATION_NOT_FOUND_ERROR;

import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import org.mjulikelion.baker.dto.response.ResponseDto;
import org.mjulikelion.baker.dto.response.introduce.IntroduceGetResponseData;
import org.mjulikelion.baker.exception.ApplicationIntroduceNotFoundException;
import org.mjulikelion.baker.exception.ApplicationNotFoundException;
import org.mjulikelion.baker.model.Application;
import org.mjulikelion.baker.model.ApplicationIntroduce;
import org.mjulikelion.baker.model.Introduce;
import org.mjulikelion.baker.repository.ApplicationIntroduceRepository;
import org.mjulikelion.baker.repository.ApplicationRepository;
import org.mjulikelion.baker.vo.IntroduceDetailVO;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@AllArgsConstructor
public class IntroduceQueryServiceImpl implements IntroduceQueryService {
private final ApplicationRepository applicationRepository;
private final ApplicationIntroduceRepository applicationIntroduceRepository;

@Override
@Transactional(readOnly = true)
public ResponseEntity<ResponseDto<IntroduceGetResponseData>> getStudentIntroduce(String studentId) {
Application application = this.findApplicationByStudentId(studentId);
List<ApplicationIntroduce> applicationIntroduceList = this.findApplicationIntroduces(application.getId());
List<IntroduceDetailVO> introduceDetailVOList = this.buildIntroduceDetailVOList(applicationIntroduceList);

IntroduceGetResponseData introduceGetResponseData = IntroduceGetResponseData.builder()
.introduceDetailVOList(introduceDetailVOList)
.part(application.getPart())
.build();

return ResponseEntity.ok(ResponseDto.res(HttpStatus.OK, "OK", introduceGetResponseData));
}

private Application findApplicationByStudentId(String studentId) {
return this.applicationRepository.findByStudentId(studentId)
.orElseThrow(() -> new ApplicationNotFoundException(APPLICATION_NOT_FOUND_ERROR));
}

private List<ApplicationIntroduce> findApplicationIntroduces(UUID applicationId) {
List<ApplicationIntroduce> applicationIntroduceList = this.applicationIntroduceRepository.findByApplicationId(
applicationId);
if (applicationIntroduceList.isEmpty()) {
throw new ApplicationIntroduceNotFoundException(APPLICATION_INTRODUCE_NOT_FOUND_ERROR);
}
return applicationIntroduceList;
}

private List<IntroduceDetailVO> buildIntroduceDetailVOList(List<ApplicationIntroduce> applicationIntroduceList) {
Map<Introduce, String> introduceMap = applicationIntroduceList.stream()
.sorted(Comparator.comparingInt(a -> a.getIntroduce().getSequence()))
.collect(Collectors.toMap(
ApplicationIntroduce::getIntroduce,
ApplicationIntroduce::getContent,
(a, b) -> b,
LinkedHashMap::new
));

return introduceMap.keySet().stream()
.map(introduce -> IntroduceDetailVO.builder()
.sequence(introduce.getSequence())
.title(introduce.getTitle())
.content(introduceMap.get(introduce))
.build())
.collect(Collectors.toList());
}
}

14 changes: 14 additions & 0 deletions src/main/java/org/mjulikelion/baker/vo/IntroduceDetailVO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.mjulikelion.baker.vo;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;

@Builder
public class IntroduceDetailVO {
@JsonProperty
private final int sequence;
@JsonProperty
private final String title;
@JsonProperty
private final String content;
}

0 comments on commit 1a97bd9

Please sign in to comment.