Skip to content

Commit

Permalink
Merge branch 'feature/elastic' of https://github.com/nhnacademy-be5-T…
Browse files Browse the repository at this point in the history
…3Team/bookstore-api into feature/elastic
  • Loading branch information
PARKJONGGYEONG committed May 16, 2024
2 parents 374ee61 + 4affeb7 commit 7466f4d
Show file tree
Hide file tree
Showing 141 changed files with 4,681 additions and 347 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ jobs:

- name: Build with Maven
run: ${{ secrets.BUILD }}

- name: Run SonarQube
run: mvn sonar:sonar -Dsonar.projectKey=github-action -Dsonar.host.url=${{secrets.SONAR_HOST}} -Dsonar.login=${{secrets.SONAR_TOKEN}}

- name: Update dependency graph
uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/feature-test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: feature branch test
name: feature-test
on:
push:
branches:
Expand Down
18 changes: 18 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@
<spring-cloud.version>2021.0.8</spring-cloud.version>
</properties>

<repositories>
<repository>
<id>release</id>
<name>edu-springboot</name>
<url>https://github.com/edu-springboot/edu-springboot-maven-repo/raw/master/releases</url>
</repository>
</repositories>

<dependencyManagement>
<dependencies>
<dependency>
Expand Down Expand Up @@ -85,6 +93,11 @@
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
Expand Down Expand Up @@ -118,6 +131,11 @@
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.nhn.dooray.messenger</groupId>
<artifactId>dooray-hook-sender</artifactId>
<version>1.2.0-RELEASE</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@EnableDiscoveryClient
@ConfigurationPropertiesScan
@SpringBootApplication
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.t3t.bookstoreapi.book.annotation;

import com.t3t.bookstoreapi.book.validator.UniqueParticipantMapValidator;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = UniqueParticipantMapValidator.class)
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface UniqueParticipantMap {
String message() default "중복된 참여자와 역할 조합이 있습니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
203 changes: 199 additions & 4 deletions src/main/java/com/t3t/bookstoreapi/book/controller/BookController.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
package com.t3t.bookstoreapi.book.controller;

import com.t3t.bookstoreapi.book.model.dto.ParticipantMapDto;
import com.t3t.bookstoreapi.book.model.request.BookRegisterRequest;
import com.t3t.bookstoreapi.book.model.request.ModifyBookDetailRequest;
import com.t3t.bookstoreapi.book.model.response.BookDetailResponse;
import com.t3t.bookstoreapi.book.model.response.BookListResponse;
import com.t3t.bookstoreapi.book.service.BookService;
import com.t3t.bookstoreapi.model.response.BaseResponse;
import com.t3t.bookstoreapi.model.response.PageResponse;
import lombok.RequiredArgsConstructor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.Valid;
import java.util.List;

@Slf4j
@RequiredArgsConstructor
@RestController
public class BookController {
private final BookService bookService;

/**
* 도서 식별자로 도서 상세 조회
*
* @param bookId 조회할 도서의 id
* @return 200 OK, 도서의 상세 정보<br>
* @author Yujin-nKim(김유진)
Expand All @@ -29,4 +40,188 @@ public ResponseEntity<BaseResponse<BookDetailResponse>> getBookDetailsById(@Path
new BaseResponse<BookDetailResponse>().data(bookService.getBookDetailsById(bookId))
);
}

/**
* 도서 전체 목록 조회
* @param pageNo 페이지 번호 (기본값: 0)
* @param pageSize 페이지 크기 (기본값: 10)
* @param sortBy 정렬 기준 (기본값: "bookId")
* @return HTTP 상태 및 도서 목록 응답
* @author Yujin-nKim(김유진)
*/
@GetMapping("/books")
public ResponseEntity<BaseResponse<PageResponse<BookListResponse>>> getAllBooks(
@RequestParam(value = "pageNo", defaultValue = "0", required = false) int pageNo,
@RequestParam(value = "pageSize", defaultValue = "10", required = false) int pageSize,
@RequestParam(value = "sortBy", defaultValue = "bookId", required = false) String sortBy) {

Pageable pageable = PageRequest.of(pageNo, pageSize, Sort.by(sortBy).ascending());

PageResponse<BookListResponse> bookList = bookService.getAllBooks(pageable);

if(bookList.getContent().isEmpty()) {
return ResponseEntity.status(HttpStatus.NO_CONTENT).body(
new BaseResponse<PageResponse<BookListResponse>>().message("등록된 도서가 없습니다."));
}
return ResponseEntity.status(HttpStatus.OK).body(
new BaseResponse<PageResponse<BookListResponse>>().data(bookList));
}

/**
* 도서 등록 요청
* @param request 도서를 등록하기 위한 요청 객체
* @return 등록된 도서의 ID를 포함한 응답 객체
* @author Yujin-nKim(김유진)
*/
@PostMapping("/books")
public ResponseEntity<BaseResponse<Long>> createBook(@ModelAttribute @Valid BookRegisterRequest request) {

log.info("도서 등록 요청 = {}",request.toString());

return ResponseEntity.status(HttpStatus.OK).body(
new BaseResponse<Long>()
.data(bookService.createBook(request))
.message("도서 등록 요청이 정상적으로 처리되었습니다."));
}

/**
* 특정 도서의 상세 정보를 수정
* @param bookId 수정할 도서의 식별자
* @param request 수정할 도서의 상세 정보를 담은 요청 객체
* @return 200 OK, 성공 메세지
* @author Yujin-nKim(김유진)
*/
@PutMapping("/books/{bookId}/book-detail")
public ResponseEntity<BaseResponse<Void>> updateBookDetail(
@PathVariable Long bookId,
@RequestBody @Valid ModifyBookDetailRequest request) {

bookService.updateBookDetail(bookId, request);

return ResponseEntity.status(HttpStatus.OK).body(
new BaseResponse<Void>().message("도서 상세 설명 수정 요청이 정상적으로 처리되었습니다."));
}

/**
* 특정 도서의 출판사를 수정
* @param bookId 수정할 도서의 식별자
* @param publisherId 수정할 출판사의 식별자
* @return 200 OK, 성공 메세지
* @author Yujin-nKim(김유진)
*/
@PutMapping("/books/{bookId}/publisher")
public ResponseEntity<BaseResponse<Void>> updateBookPublisher(
@PathVariable Long bookId,
@RequestParam Long publisherId) {

bookService.updatePublisher(bookId,publisherId);

return ResponseEntity.status(HttpStatus.OK).body(
new BaseResponse<Void>().message("도서 출판사 수정 요청이 정상적으로 처리되었습니다."));
}

/**
* 특정 도서의 썸네일을 수정
* @param bookId 수정할 도서의 식별자
* @param image 수정할 썸네일 이미지
* @return 200 OK, 성공 메세지
* @author Yujin-nKim(김유진)
*/
@PutMapping("/books/{bookId}/book-thumbnail")
public ResponseEntity<BaseResponse<Void>> updateBookThumbnail(
@PathVariable Long bookId,
@ModelAttribute MultipartFile image) {

bookService.updateBookThumbnail(bookId, image);

return ResponseEntity.status(HttpStatus.OK).body(
new BaseResponse<Void>().message("도서 썸네일 수정 요청이 정상적으로 처리되었습니다."));
}

/**
* 특정 도서의 이미지를 수정
* @param bookId 수정할 도서의 식별자
* @param imageList 수정할 이미지 리스트
* @return 200 OK, 성공 메세지
* @author Yujin-nKim(김유진)
*/
@PutMapping("/books/{bookId}/book-image")
public ResponseEntity<BaseResponse<Void>> updateBookImage(
@PathVariable Long bookId,
@ModelAttribute List<MultipartFile> imageList) {

bookService.updateBookImage(bookId, imageList);

return ResponseEntity.status(HttpStatus.OK).body(
new BaseResponse<Void>().message("도서 미리보기 이미지 수정 요청이 정상적으로 처리되었습니다."));
}

/**
* 특정 도서의 태그를 수정
* @param bookId 수정할 도서의 식별자
* @param tagList 수정할 태그 리스트
* @return 200 OK, 성공 메세지
* @author Yujin-nKim(김유진)
*/
@PutMapping("/books/{bookId}/tag")
public ResponseEntity<BaseResponse<Void>> updateBookTag(
@PathVariable Long bookId,
@RequestBody @Valid List<Long> tagList) {

bookService.updateBookTag(bookId, tagList);

return ResponseEntity.status(HttpStatus.OK).body(
new BaseResponse<Void>().message("도서 태그 수정 요청이 정상적으로 처리되었습니다."));
}

/**
* 특정 도서의 카테고리를 수정
* @param bookId 수정할 도서의 식별자
* @param categoryList 수정할 카테고리 리스트
* @return 200 OK, 성공 메세지
* @author Yujin-nKim(김유진)
*/
@PutMapping("/books/{bookId}/category")
public ResponseEntity<BaseResponse<Void>> updateBookCategory(
@PathVariable Long bookId,
@RequestBody @Valid List<Integer> categoryList) {

bookService.updateBookCategory(bookId, categoryList);

return ResponseEntity.status(HttpStatus.OK).body(
new BaseResponse<Void>().message("도서 카테고리 수정 요청이 정상적으로 처리되었습니다."));
}

/**
* 특정 도서의 참여자를 수정
* @param bookId 수정할 도서의 식별자
* @param participantList 수정할 참여자 매핑 리스트
* @return 200 OK, 성공 메세지
* @author Yujin-nKim(김유진)
*/
@PutMapping("/books/{bookId}/participant")
public ResponseEntity<BaseResponse<Void>> updateBookParticipant(
@PathVariable Long bookId,
@RequestBody @Valid List<ParticipantMapDto> participantList) {

bookService.updateBookParticipant(bookId, participantList);

return ResponseEntity.status(HttpStatus.OK).body(
new BaseResponse<Void>().message("도서 참여자 수정 요청이 정상적으로 처리되었습니다."));
}

/**
* 특정 도서를 삭제
* @param bookId 삭제할 도서의 식별자
* @return 200 OK, 성공 메세지
* @author Yujin-nKim(김유진)
*/
@DeleteMapping("/books/{bookId}")
public ResponseEntity<BaseResponse<Void>> deleteBook(@PathVariable Long bookId) {

bookService.deleteBook(bookId);

return ResponseEntity.status(HttpStatus.OK).body(
new BaseResponse<Void>().message("도서 삭제 요청이 정상적으로 처리되었습니다."));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.t3t.bookstoreapi.book.converter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.t3t.bookstoreapi.book.exception.ConversionException;
import org.springframework.core.convert.converter.Converter;
import com.t3t.bookstoreapi.book.model.dto.ParticipantMapDto;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

public class StringToParticipantMapDtoConverter implements Converter<String, List<ParticipantMapDto>> {

/**
* 주어진 문자열을 JSON으로 변환하고, 그 JSON을 ParticipantMapDto 객체의 리스트로 변환
*
* @param source JSON 형식의 문자열
* @return ParticipantMapDto 객체의 리스트
* @throws ConversionException 변환 과정에서 발생한 예외
* @author Yujin-nKim(김유진)
*/
@Override
public List<ParticipantMapDto> convert(String source) {
ObjectMapper objectMapper = new ObjectMapper();
try {
// JSON 문자열을 List<ParticipantMapDto> 객체로 변환
return Arrays.asList(objectMapper.readValue(source, ParticipantMapDto[].class));
} catch (IOException e) {
throw new ConversionException("문자열을 ParticipantMapDto 리스트로 변환하는 중에 오류가 발생했습니다.", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.t3t.bookstoreapi.book.exception;

public class BookAlreadyDeletedException extends RuntimeException{

private static final String DEFAULT_MESSAGE = "이미 삭제된 도서입니다.";

public BookAlreadyDeletedException() {
super(DEFAULT_MESSAGE);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.t3t.bookstoreapi.book.exception;

public class BookAlreadyExistsException extends RuntimeException{

private static final String DEFAULT_MESSAGE = "이미 등록된 도서입니다.";

public BookAlreadyExistsException() {
super(DEFAULT_MESSAGE);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.t3t.bookstoreapi.book.exception;

public class ConversionException extends RuntimeException {

/**
* 지정된 메시지와 원인(Throwable)을 사용하여 ConversionException을 생성
*
* @param message 예외에 대한 설명 메시지
* @param cause 원인 예외
* @author Yujin-nKim(김유진)
*/
public ConversionException(String message, Throwable cause) {
super(message, cause);
}
}
Loading

0 comments on commit 7466f4d

Please sign in to comment.