diff --git a/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java b/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java new file mode 100644 index 0000000..272c7d0 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/controller/ManageController.java @@ -0,0 +1,58 @@ +package org.triumers.newsnippetback.Application.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.triumers.newsnippetback.Application.service.ManageService; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; +import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; +import org.triumers.newsnippetback.domain.dto.QuizDTO; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/manage") +public class ManageController { + + private final ManageService manageService; + + @Autowired + public ManageController(ManageService manageService) { + this.manageService = manageService; + } + + @PostMapping("/findCrawlingQuiz") + public List findCrawlingQuizList(@RequestBody Map params) { + return manageService.selectCrawlingQuizListByDate(LocalDate.parse(params.get("date"), + DateTimeFormatter.ISO_DATE)); + } + + @GetMapping("/findCrawlingQuiz/{id}") + public CrawlingQuizDTO findCrawlingQuizById(@PathVariable int id){ + return manageService.selectCrawlingQuizByID(id); + } + + @GetMapping("/addQuiz/{id}") + public ResponseEntity addQuizInList(@PathVariable int id){ + Quiz savedQuiz = manageService.insertSelectedQuizById(id); + + return ResponseEntity.status(HttpStatus.OK).body(savedQuiz); + } + + @GetMapping("/findSelectedQuiz") + public List findSelectedQuizList(){ + return manageService.selectQuizListByDate(LocalDate.now().plusDays(1)); + } + + @DeleteMapping("/deleteQuiz/{id}") + public ResponseEntity deleteQuizInList(@PathVariable int id){ + QuizDTO deletedQuiz = manageService.deleteQuizInListById(id); + + return ResponseEntity.status(HttpStatus.OK).body(deletedQuiz); + } + +} diff --git a/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java new file mode 100644 index 0000000..a84bddb --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/Application/service/ManageService.java @@ -0,0 +1,135 @@ +package org.triumers.newsnippetback.Application.service; + +import jakarta.transaction.Transactional; +import org.modelmapper.ModelMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; +import org.triumers.newsnippetback.domain.aggregate.entity.CrawlingQuiz; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; +import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; +import org.triumers.newsnippetback.domain.dto.QuizDTO; +import org.triumers.newsnippetback.domain.repository.CategoryRepository; +import org.triumers.newsnippetback.domain.repository.CrawlingQuizRepository; +import org.triumers.newsnippetback.domain.repository.QuizRepository; + +import java.time.LocalDate; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.stream.Collectors; + +@Service +public class ManageService { + + private final QuizRepository quizRepository; + private final CrawlingQuizRepository crawlingQuizRepository; + private final CategoryRepository categoryRepository; + private final ModelMapper mapper; + + final static LocalDate nextDate = LocalDate.now().plusDays(1); + + @Autowired + public ManageService(QuizRepository quizRepository, CrawlingQuizRepository crawlingQuizRepository, + CategoryRepository categoryRepository, ModelMapper mapper) { + this.quizRepository = quizRepository; + this.crawlingQuizRepository = crawlingQuizRepository; + this.categoryRepository = categoryRepository; + this.mapper = mapper; + } + + public List selectCrawlingQuizListByDate(LocalDate date) { + + List crawlingQuizList = crawlingQuizRepository.findByNewsDate(date); + + if (!crawlingQuizList.isEmpty()) { + List crawlingQuizDTOList = crawlingQuizList.stream() + .map(crawlingQuiz -> mapper.map(crawlingQuiz, CrawlingQuizDTO.class)) + .collect(Collectors.toList()); + + for (int i = 0; i < crawlingQuizList.size(); i++) { + CrawlingQuiz crawlingQuiz = crawlingQuizList.get(i); + Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()) + .orElseThrow(IllegalAccessError::new); + crawlingQuizDTOList.get(i).setCategory(category); + + boolean isSelected = quizRepository.countByDateAndOriginQuizId + (nextDate, crawlingQuiz.getId()) > 0; + crawlingQuizDTOList.get(i).setSelected(isSelected); + } + return crawlingQuizDTOList; + } else { + // 이후에 크롤링 서버에 문제 생성 요청하기 + throw new NoSuchElementException("문제 정보를 불러올 수 없음"); + } + } + + public CrawlingQuizDTO selectCrawlingQuizByID(int id) { + CrawlingQuiz crawlingQuiz = crawlingQuizRepository.findById(id).orElseThrow(IllegalAccessError::new); + + if (crawlingQuiz != null) { + CrawlingQuizDTO crawlingQuizDTO = mapper.map(crawlingQuiz, CrawlingQuizDTO.class); + + Category category = categoryRepository.findById(crawlingQuiz.getCategoryId()) + .orElseThrow(IllegalAccessError::new); + crawlingQuizDTO.setCategory(category); + + return crawlingQuizDTO; + } + throw new IllegalAccessError("문제 정보를 불러올 수 없음"); + } + + @Transactional + public Quiz insertSelectedQuizById(int id) { + + CrawlingQuizDTO selectedQuiz = selectCrawlingQuizByID(id); + Quiz insertQuiz = mapper.map(selectedQuiz, Quiz.class); + + insertQuiz.setNo(getMaxNo() + 1); + insertQuiz.setDate(nextDate); + insertQuiz.setCategoryId(selectedQuiz.getCategory().getId()); + insertQuiz.setOriginQuizId(selectedQuiz.getId()); + + return quizRepository.save(insertQuiz); + } + + public int getMaxNo() { + return quizRepository.countByDate(nextDate); + } + + public List selectQuizListByDate(LocalDate date) { + List quizList = quizRepository.findByDateOrderByNoAsc(date); + + if (!quizList.isEmpty()) { + List quizDTOList = quizList.stream() + .map(quiz -> mapper.map(quiz, QuizDTO.class)) + .collect(Collectors.toList()); + + for (int i = 0; i < quizList.size(); i++) { + Category category = categoryRepository.findById(quizList.get(i).getCategoryId()) + .orElseThrow(IllegalAccessError::new); + quizDTOList.get(i).setCategory(category); + } + return quizDTOList; + } + throw new NoSuchElementException("문제 정보를 불러올 수 없음"); + } + + @Transactional + public QuizDTO deleteQuizInListById(int id) { + + Quiz deleteQuiz = quizRepository.findByOriginQuizIdAndDate(id, nextDate); + + if (deleteQuiz != null) { + + quizRepository.deleteById(deleteQuiz.getId()); + List modifyQuizList = quizRepository + .findByDateAndNoGreaterThanOrderByNoAsc(nextDate, deleteQuiz.getNo()); + + for (Quiz modifyQuiz : modifyQuizList) { + modifyQuiz.setNo(modifyQuiz.getNo() - 1); + } + return mapper.map(deleteQuiz, QuizDTO.class); + } + throw new IllegalAccessError("문제 정보를 불러올 수 없음"); + } +} diff --git a/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java b/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java index d6e1ac3..5f9d4f7 100644 --- a/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java +++ b/src/main/java/org/triumers/newsnippetback/NewsnippetBackApplication.java @@ -1,7 +1,9 @@ package org.triumers.newsnippetback; +import org.modelmapper.ModelMapper; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; @SpringBootApplication public class NewsnippetBackApplication { @@ -10,4 +12,8 @@ public static void main(String[] args) { SpringApplication.run(NewsnippetBackApplication.class, args); } + @Bean + public ModelMapper modelMapper() { + return new ModelMapper(); + } } diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java index 6ba0f4d..f2ff626 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Category.java @@ -10,6 +10,7 @@ @NoArgsConstructor @AllArgsConstructor @ToString + public class Category { @Id diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java index c5e525e..2eb5d41 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/CrawlingQuiz.java @@ -2,11 +2,8 @@ import jakarta.persistence.*; import lombok.*; - import java.time.LocalDate; -@Entity -@Table(name = "tbl_crawling_quiz") @Getter @Setter @NoArgsConstructor diff --git a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java index da0efd7..3e97a3d 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java +++ b/src/main/java/org/triumers/newsnippetback/domain/aggregate/entity/Quiz.java @@ -1,17 +1,14 @@ package org.triumers.newsnippetback.domain.aggregate.entity; - import jakarta.persistence.*; import lombok.*; - import java.time.LocalDate; -@Entity -@Table(name = "tbl_quiz") @Getter @Setter @NoArgsConstructor @AllArgsConstructor @ToString + public class Quiz { @Id diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java new file mode 100644 index 0000000..d4d8e77 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/CrawlingQuizDTO.java @@ -0,0 +1,34 @@ +package org.triumers.newsnippetback.domain.dto; + +import jakarta.persistence.Column; +import lombok.Data; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; + +import java.time.LocalDate; + +@Data +public class CrawlingQuizDTO { + private int id; + + private String content; + + private String optionA; + + private String optionB; + + private String optionC; + + private String optionD; + + private String answer; + + private String explanation; + + private String newsLink; + + private LocalDate newsDate; + + private Category category; + + private boolean isSelected; +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java index 664fa1f..71367df 100644 --- a/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java +++ b/src/main/java/org/triumers/newsnippetback/domain/dto/QuizDTO.java @@ -1,11 +1,12 @@ package org.triumers.newsnippetback.domain.dto; import jakarta.persistence.*; +import lombok.Data; import org.triumers.newsnippetback.domain.aggregate.entity.Category; -import org.triumers.newsnippetback.domain.aggregate.entity.CrawlingQuiz; import java.time.LocalDate; +@Data public class QuizDTO { private int id; diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java new file mode 100644 index 0000000..81d1c1a --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/CategoryRepository.java @@ -0,0 +1,7 @@ +package org.triumers.newsnippetback.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.triumers.newsnippetback.domain.aggregate.entity.Category; + +public interface CategoryRepository extends JpaRepository { +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/CrawlingQuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/CrawlingQuizRepository.java new file mode 100644 index 0000000..6316308 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/CrawlingQuizRepository.java @@ -0,0 +1,12 @@ +package org.triumers.newsnippetback.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import org.triumers.newsnippetback.domain.aggregate.entity.CrawlingQuiz; + +import java.time.LocalDate; +import java.util.List; + +public interface CrawlingQuizRepository extends JpaRepository { + List findByNewsDate(LocalDate date); +} diff --git a/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java new file mode 100644 index 0000000..6a69868 --- /dev/null +++ b/src/main/java/org/triumers/newsnippetback/domain/repository/QuizRepository.java @@ -0,0 +1,22 @@ +package org.triumers.newsnippetback.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +public interface QuizRepository extends JpaRepository { + List findByDateOrderByNoAsc(LocalDate date); + + List findByDateAndNoGreaterThanOrderByNoAsc(LocalDate localDate, int no); + + Integer countByDate(LocalDate localDate); + + Integer countByDateAndOriginQuizId(LocalDate localDate, int id); + + Quiz findByOriginQuizIdAndDate(int id, LocalDate date); +} diff --git a/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java new file mode 100644 index 0000000..cc5d2dc --- /dev/null +++ b/src/test/java/org/triumers/newsnippetback/Application/service/ManageServiceTest.java @@ -0,0 +1,114 @@ +package org.triumers.newsnippetback.Application.service; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.triumers.newsnippetback.domain.aggregate.entity.Quiz; +import org.triumers.newsnippetback.domain.dto.CrawlingQuizDTO; +import org.triumers.newsnippetback.domain.dto.QuizDTO; + +import java.time.LocalDate; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +class ManageServiceTest { + + ManageService manageService; + + @Autowired + public ManageServiceTest(ManageService manageService) { + this.manageService = manageService; + } + + static Stream getDate() { + return Stream.of( + LocalDate.of(2024, 4, 1), + LocalDate.of(2024, 4, 2), + LocalDate.of(2024, 4, 3), + LocalDate.of(2024, 4, 4), + LocalDate.of(2024, 4, 5) + ); + } + + @ParameterizedTest + @MethodSource("getDate") + void selectCrawlingQuizListByDate(LocalDate date){ + List quizDTOList = manageService.selectCrawlingQuizListByDate(date); + + assertNotNull(quizDTOList); + } + + @ParameterizedTest + @ValueSource(ints = {11, 12, 13}) + void selectCrawlingQuizById(int id){ + CrawlingQuizDTO quizDTO = manageService.selectCrawlingQuizByID(id); + + assertNotNull(quizDTO); + } + + @ParameterizedTest + @ValueSource(ints = {1, 2, 3}) + void addSelectedQuiz(int id){ + Quiz savedQuiz = manageService.insertSelectedQuizById(id); + + assertNotNull(savedQuiz); + } + + @Test + void selectQuizListByDate(){ + List savedQuizList = manageService.selectQuizListByDate(LocalDate.now().plusDays(1)); + + assertNotNull(savedQuizList); + } + + @ParameterizedTest + @ValueSource(ints = {13, 14, 15}) + void deleteQuizInListSuccess(int id){ + QuizDTO quizDTO = manageService.deleteQuizInListById(id); + assertNotNull(quizDTO); + } + + @Test + void deleteQuizInListException(){ + int id = -1; + + assertThrows(IllegalAccessError.class, () -> { + manageService.deleteQuizInListById(id); + } ); + } + + @Test + void selectCrawlingQuizByIdException(){ + int id = -1; + + assertThrows(IllegalAccessError.class, () -> { + manageService.selectCrawlingQuizByID(id); + } ); + } + + @Test + void selectCrawlingQuizListByDateException(){ + LocalDate date = LocalDate.of(2020, 1, 1); + + assertThrows(NoSuchElementException.class, () -> { + manageService.selectCrawlingQuizListByDate(date); + } ); + } + + @Test + void selectQuizListByDateException(){ + LocalDate date = LocalDate.of(2020, 1, 1); + + assertThrows(NoSuchElementException.class, () -> { + manageService.selectQuizListByDate(date); + } ); + } + +} \ No newline at end of file