From 8182cbbc69bd32c53b091baf4e8cc646a6eb5bc2 Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Thu, 30 Mar 2023 22:39:05 +0900 Subject: [PATCH 01/21] =?UTF-8?q?refactor=20:=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD,=20question?= =?UTF-8?q?UpdateRequestDto=20record=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/QuestionUpdateRequestDto.java | 30 ++++++++----------- .../dto/response/AnswerResponseDto.java | 1 - .../service/QuestionCommandService.java | 2 +- .../doctorqna/DoctorQnaTests.java | 2 +- .../controller/QuestionControllerTest.java | 2 +- .../service/AnswerCommandServiceTest.java | 2 +- .../service/AnswerQueryServiceTest.java | 2 +- .../service/QuestionCommandServiceTest.java | 4 +-- .../service/QuestionQueryServiceTest.java | 4 +-- 9 files changed, 19 insertions(+), 30 deletions(-) rename src/test/java/com/codelion/animalcare/{ => domain}/doctorqna/DoctorQnaTests.java (99%) rename src/test/java/com/codelion/animalcare/{ => domain}/doctorqna/controller/QuestionControllerTest.java (97%) rename src/test/java/com/codelion/animalcare/{ => domain}/doctorqna/service/AnswerCommandServiceTest.java (98%) rename src/test/java/com/codelion/animalcare/{ => domain}/doctorqna/service/AnswerQueryServiceTest.java (97%) rename src/test/java/com/codelion/animalcare/{ => domain}/doctorqna/service/QuestionCommandServiceTest.java (94%) rename src/test/java/com/codelion/animalcare/{ => domain}/doctorqna/service/QuestionQueryServiceTest.java (94%) diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionUpdateRequestDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionUpdateRequestDto.java index e3d9faea..166e5fea 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionUpdateRequestDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionUpdateRequestDto.java @@ -1,27 +1,21 @@ package com.codelion.animalcare.domain.doctorqna.dto.request; import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; -@Getter -@Setter -@NoArgsConstructor -public class QuestionUpdateRequestDto { - @NotBlank(message = "제목은 필수 항목 입니다.") - @Size(max = 200) - private String title; +public record QuestionUpdateRequestDto ( + @NotBlank(message = "제목은 필수 항목 입니다.") + @Size(max = 200) + String title, - @NotBlank(message = "내용은 필수 항목 입니다.") - private String content; - - @Builder - public QuestionUpdateRequestDto(String title, String content) { - this.title = title; - this.content = content; - } + @NotBlank(message = "내용은 필수 항목 입니다.") + String content +) { + @Builder + public QuestionUpdateRequestDto(String title, String content) { + this.title = title; + this.content = content; + } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/AnswerResponseDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/AnswerResponseDto.java index abe0511b..c2067afb 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/AnswerResponseDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/AnswerResponseDto.java @@ -6,7 +6,6 @@ import java.time.LocalDateTime; public record AnswerResponseDto ( - Long id, String content, Question question, diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java index eda1d3f9..c439462c 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java @@ -35,7 +35,7 @@ public Long save(QuestionSaveRequestDto questionSaveRequestDto, Principal princi public Long update(Long id, QuestionUpdateRequestDto questionUpdateRequestDto){ Question question = questionQueryService.findQuestionByQuestionId(id); - question.update(questionUpdateRequestDto.getTitle(), questionUpdateRequestDto.getContent()); + question.update(questionUpdateRequestDto.title(), questionUpdateRequestDto.content()); return id; } diff --git a/src/test/java/com/codelion/animalcare/doctorqna/DoctorQnaTests.java b/src/test/java/com/codelion/animalcare/domain/doctorqna/DoctorQnaTests.java similarity index 99% rename from src/test/java/com/codelion/animalcare/doctorqna/DoctorQnaTests.java rename to src/test/java/com/codelion/animalcare/domain/doctorqna/DoctorQnaTests.java index 56d15841..c35608c6 100644 --- a/src/test/java/com/codelion/animalcare/doctorqna/DoctorQnaTests.java +++ b/src/test/java/com/codelion/animalcare/domain/doctorqna/DoctorQnaTests.java @@ -1,4 +1,4 @@ -package com.codelion.animalcare.doctorqna; +package com.codelion.animalcare.domain.doctorqna; /* import com.codelion.animalcare.domain.doctorqna.dto.request.AnswerSaveRequestDto; import com.codelion.animalcare.domain.doctorqna.dto.request.AnswerUpdateRequestDto; diff --git a/src/test/java/com/codelion/animalcare/doctorqna/controller/QuestionControllerTest.java b/src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java similarity index 97% rename from src/test/java/com/codelion/animalcare/doctorqna/controller/QuestionControllerTest.java rename to src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java index 3245d014..489bb239 100644 --- a/src/test/java/com/codelion/animalcare/doctorqna/controller/QuestionControllerTest.java +++ b/src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java @@ -1,4 +1,4 @@ -package com.codelion.animalcare.doctorqna.controller; +package com.codelion.animalcare.domain.doctorqna.controller; import com.codelion.animalcare.domain.doctorqna.dto.request.QuestionSaveRequestDto; import com.codelion.animalcare.domain.doctorqna.service.QuestionCommandService; diff --git a/src/test/java/com/codelion/animalcare/doctorqna/service/AnswerCommandServiceTest.java b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/AnswerCommandServiceTest.java similarity index 98% rename from src/test/java/com/codelion/animalcare/doctorqna/service/AnswerCommandServiceTest.java rename to src/test/java/com/codelion/animalcare/domain/doctorqna/service/AnswerCommandServiceTest.java index 41080619..dc7b6547 100644 --- a/src/test/java/com/codelion/animalcare/doctorqna/service/AnswerCommandServiceTest.java +++ b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/AnswerCommandServiceTest.java @@ -1,4 +1,4 @@ -package com.codelion.animalcare.doctorqna.service; +package com.codelion.animalcare.domain.doctorqna.service; import com.codelion.animalcare.domain.doctorqna.dto.request.AnswerSaveRequestDto; import com.codelion.animalcare.domain.doctorqna.dto.request.AnswerUpdateRequestDto; diff --git a/src/test/java/com/codelion/animalcare/doctorqna/service/AnswerQueryServiceTest.java b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/AnswerQueryServiceTest.java similarity index 97% rename from src/test/java/com/codelion/animalcare/doctorqna/service/AnswerQueryServiceTest.java rename to src/test/java/com/codelion/animalcare/domain/doctorqna/service/AnswerQueryServiceTest.java index 2e905d99..7ed51d46 100644 --- a/src/test/java/com/codelion/animalcare/doctorqna/service/AnswerQueryServiceTest.java +++ b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/AnswerQueryServiceTest.java @@ -1,4 +1,4 @@ -package com.codelion.animalcare.doctorqna.service; +package com.codelion.animalcare.domain.doctorqna.service; import com.codelion.animalcare.domain.doctorqna.entity.Answer; import com.codelion.animalcare.domain.doctorqna.service.AnswerQueryService; diff --git a/src/test/java/com/codelion/animalcare/doctorqna/service/QuestionCommandServiceTest.java b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java similarity index 94% rename from src/test/java/com/codelion/animalcare/doctorqna/service/QuestionCommandServiceTest.java rename to src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java index 1d86d13d..1127041e 100644 --- a/src/test/java/com/codelion/animalcare/doctorqna/service/QuestionCommandServiceTest.java +++ b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java @@ -1,11 +1,9 @@ -package com.codelion.animalcare.doctorqna.service; +package com.codelion.animalcare.domain.doctorqna.service; import com.codelion.animalcare.domain.doctorqna.dto.request.QuestionSaveRequestDto; import com.codelion.animalcare.domain.doctorqna.dto.request.QuestionUpdateRequestDto; import com.codelion.animalcare.domain.doctorqna.dto.response.QuestionResponseDto; import com.codelion.animalcare.domain.doctorqna.entity.Question; -import com.codelion.animalcare.domain.doctorqna.service.QuestionCommandService; -import com.codelion.animalcare.domain.doctorqna.service.QuestionQueryService; import com.codelion.animalcare.domain.user.service.MemberService; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/codelion/animalcare/doctorqna/service/QuestionQueryServiceTest.java b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryServiceTest.java similarity index 94% rename from src/test/java/com/codelion/animalcare/doctorqna/service/QuestionQueryServiceTest.java rename to src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryServiceTest.java index cc52db45..aa1f2609 100644 --- a/src/test/java/com/codelion/animalcare/doctorqna/service/QuestionQueryServiceTest.java +++ b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryServiceTest.java @@ -1,9 +1,7 @@ -package com.codelion.animalcare.doctorqna.service; +package com.codelion.animalcare.domain.doctorqna.service; import com.codelion.animalcare.domain.doctorqna.dto.response.QuestionResponseDto; import com.codelion.animalcare.domain.doctorqna.entity.Question; -import com.codelion.animalcare.domain.doctorqna.service.QuestionCommandService; -import com.codelion.animalcare.domain.doctorqna.service.QuestionQueryService; import com.codelion.animalcare.domain.user.entity.Member; import com.codelion.animalcare.domain.user.service.MemberService; import org.junit.jupiter.api.DisplayName; From 922ee2f774715ff48c104ce7b25920d80133cd5d Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Thu, 30 Mar 2023 23:00:45 +0900 Subject: [PATCH 02/21] =?UTF-8?q?refactor=20:=20answerSaveRequestDto=20rec?= =?UTF-8?q?ord=EB=A1=9C=20=EB=B3=80=EA=B2=BD,=20setter=EB=A5=BC=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=ED=95=98=EB=A9=B4=EC=84=9C=20dto=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=B0=A9=EC=8B=9D=20=EC=83=9D=EC=84=B1=EC=9E=90?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AnswerController.java | 8 ++-- .../dto/request/AnswerSaveRequestDto.java | 41 ++++++++----------- .../dto/request/AnswerUpdateRequestDto.java | 24 ++++------- .../dto/request/QuestionUpdateRequestDto.java | 2 +- .../dto/response/QuestionResponseDto.java | 1 - .../service/AnswerCommandService.java | 7 +--- 6 files changed, 33 insertions(+), 50 deletions(-) diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/AnswerController.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/AnswerController.java index e686bd97..ff531aca 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/AnswerController.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/AnswerController.java @@ -3,10 +3,10 @@ import com.codelion.animalcare.domain.doctorqna.dto.request.AnswerSaveRequestDto; import com.codelion.animalcare.domain.doctorqna.dto.request.AnswerUpdateRequestDto; -import com.codelion.animalcare.domain.doctorqna.service.AnswerQueryService; +import com.codelion.animalcare.domain.doctorqna.entity.Question; import com.codelion.animalcare.domain.doctorqna.service.AnswerCommandService; +import com.codelion.animalcare.domain.doctorqna.service.AnswerQueryService; import com.codelion.animalcare.domain.doctorqna.service.QuestionQueryService; -import com.codelion.animalcare.domain.doctorqna.service.QuestionCommandService; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; @@ -30,7 +30,7 @@ public class AnswerController { //답변 작성 @PostMapping("/usr/doctor-qna/{questionId}/answers/write") - public String save(Model model, @PathVariable Long questionId, @Valid AnswerSaveRequestDto answerSaveRequestDto, BindingResult bindingResult, Principal principal){ + public String save(Model model, @PathVariable Long questionId, @Valid AnswerSaveRequestDto answerForm, BindingResult bindingResult, Principal principal){ if(bindingResult.hasErrors()) { model.addAttribute("question", questionQueryService.findById(questionId)); @@ -40,6 +40,8 @@ public String save(Model model, @PathVariable Long questionId, @Valid AnswerSave // if(!answerService.isDoctor(principal)) { // throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "의사만 답변을 작성할 수 있습니다."); // } + Question question = questionQueryService.findQuestionByQuestionId(questionId); + AnswerSaveRequestDto answerSaveRequestDto = new AnswerSaveRequestDto(answerForm.content(), question); answerCommandService.save(questionId, answerSaveRequestDto, principal); return "redirect:/usr/doctor-qna/%d".formatted(questionId); diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerSaveRequestDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerSaveRequestDto.java index 39e509f6..d4b5b051 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerSaveRequestDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerSaveRequestDto.java @@ -4,33 +4,26 @@ import com.codelion.animalcare.domain.doctorqna.entity.Question; import com.codelion.animalcare.domain.user.entity.Doctor; import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; import javax.validation.constraints.NotEmpty; -@Getter -@Setter -@NoArgsConstructor -public class AnswerSaveRequestDto { +public record AnswerSaveRequestDto ( + @NotEmpty(message = "내용은 필수 입력 항목입니다.") + String content, - @NotEmpty(message = "내용은 필수 입력 항목입니다.") - private String content; - @Setter - private Question question; + Question question +) { + @Builder + public AnswerSaveRequestDto(String content, Question question){ + this.content = content; + this.question = question; + } - @Builder - public AnswerSaveRequestDto(String content, Question question){ - this.content = content; - this.question = question; - } - - public Answer toEntity(Doctor doctor) { - return Answer.builder() - .content(content) - .question(question) - .doctor(doctor) - .build(); - } + public Answer toEntity(Doctor doctor) { + return Answer.builder() + .content(content) + .question(question) + .doctor(doctor) + .build(); + } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerUpdateRequestDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerUpdateRequestDto.java index efaef92b..eb92a46f 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerUpdateRequestDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerUpdateRequestDto.java @@ -1,22 +1,14 @@ package com.codelion.animalcare.domain.doctorqna.dto.request; import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; import javax.validation.constraints.NotBlank; - -@Getter -@Setter -@NoArgsConstructor -public class AnswerUpdateRequestDto { - - @NotBlank(message = "내용은 필수 입력 항목입니다.") - private String content; - - @Builder - public AnswerUpdateRequestDto(String content) { - this.content = content; - } +public record AnswerUpdateRequestDto( + @NotBlank(message = "내용은 필수 입력 항목입니다.") + String content +) { + @Builder + public AnswerUpdateRequestDto(String content) { + this.content = content; + } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionUpdateRequestDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionUpdateRequestDto.java index 166e5fea..48157ffe 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionUpdateRequestDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionUpdateRequestDto.java @@ -17,5 +17,5 @@ public record QuestionUpdateRequestDto ( public QuestionUpdateRequestDto(String title, String content) { this.title = title; this.content = content; - } + } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/QuestionResponseDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/QuestionResponseDto.java index ff5b2c55..60cec186 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/QuestionResponseDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/QuestionResponseDto.java @@ -8,7 +8,6 @@ import java.util.List; public record QuestionResponseDto ( - Long id, String title, String content, diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/AnswerCommandService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/AnswerCommandService.java index ad274c34..a4e897c6 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/AnswerCommandService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/AnswerCommandService.java @@ -4,7 +4,6 @@ import com.codelion.animalcare.domain.doctorqna.dto.request.AnswerUpdateRequestDto; import com.codelion.animalcare.domain.doctorqna.entity.Answer; import com.codelion.animalcare.domain.doctorqna.repository.AnswerRepository; -import com.codelion.animalcare.domain.doctorqna.entity.Question; import com.codelion.animalcare.domain.user.entity.Doctor; import com.codelion.animalcare.domain.user.repository.DoctorRepository; import lombok.RequiredArgsConstructor; @@ -25,8 +24,6 @@ public class AnswerCommandService { public Long save(Long questionId, AnswerSaveRequestDto answerSaveRequestDto, Principal principal){ - Question question = questionQueryService.findQuestionByQuestionId(questionId); - answerSaveRequestDto.setQuestion(question); Doctor doctor = doctorRepository.findByEmail(principal.getName()).orElseThrow(() -> new IllegalArgumentException("의사가 존재하지 않습니다.")); @@ -34,14 +31,14 @@ public Long save(Long questionId, AnswerSaveRequestDto answerSaveRequestDto, Pri // throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "의사만 답변을 작성할 수 있습니다."); // } - question.addAnswer(answerSaveRequestDto.toEntity(doctor)); + answerSaveRequestDto.question().addAnswer(answerSaveRequestDto.toEntity(doctor)); return answerRepository.save(answerSaveRequestDto.toEntity(doctor)).getId(); } public Long update(Long answerId, AnswerUpdateRequestDto answerUpdateRequestDto) { Answer answer = answerQueryService.findAnswerByAnswerId(answerId); - answer.update(answerUpdateRequestDto.getContent()); + answer.update(answerUpdateRequestDto.content()); return answerId; } From d37d37acccd8ffeb0567e1d1091fe4b214ec525e Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Thu, 30 Mar 2023 23:15:01 +0900 Subject: [PATCH 03/21] test : write QuestionController save test code --- .../controller/AnswerController.java | 2 +- .../controller/QuestionControllerTest.java | 76 +++++++++++-------- 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/AnswerController.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/AnswerController.java index ff531aca..c4f27ea9 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/AnswerController.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/AnswerController.java @@ -79,7 +79,7 @@ public String modify(@PathVariable Long questionId, @PathVariable Long answerId, public String delete(@PathVariable Long questionId, @PathVariable Long answerId, Principal principal) { if(answerQueryService.answerAuthorized(answerId, principal)){ - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "수정권한이 없습니다."); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "삭제권한이 없습니다."); } answerCommandService.delete(answerId); diff --git a/src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java b/src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java index 489bb239..a8119561 100644 --- a/src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java +++ b/src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java @@ -4,50 +4,64 @@ import com.codelion.animalcare.domain.doctorqna.service.QuestionCommandService; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.MediaType; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.web.WebAppConfiguration; -import org.springframework.test.web.servlet.MockMvc; +import org.springframework.validation.BindingResult; import java.security.Principal; -import static org.mockito.Mockito.times; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.verify; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; @ActiveProfiles("test") -@WebAppConfiguration -@SpringBootTest -@AutoConfigureMockMvc +@ExtendWith(MockitoExtension.class) public class QuestionControllerTest { - @Autowired - private MockMvc mockMvc; - @MockBean + @Mock private QuestionCommandService questionCommandService; + @InjectMocks + private QuestionController questionController; - @DisplayName("hi") + @Mock + private BindingResult bindingResult; + + @Mock + private Principal principal; + + @DisplayName("QuestionController_질문등록_성공") @Test - public void saveQuestion_ValidInput_ShouldSaveQuestion() throws Exception { - QuestionSaveRequestDto dto = new QuestionSaveRequestDto("Test question", "Test content"); - Principal principal = () -> "member1@test.com"; - - mockMvc.perform(post("/usr/doctor-qna/write") - .contentType(MediaType.APPLICATION_JSON) - .param("questionTitle", dto.title()) - .param("content", dto.content()) - .principal(principal)) - .andExpect(status().isFound()) - .andExpect(redirectedUrl("http://localhost/user/login")); - - verify(questionCommandService, times(1)).save(dto, principal); + void save_questionSaveRequestDtoIsValid_success() throws Exception { + // given + QuestionSaveRequestDto requestDto = new QuestionSaveRequestDto("title", "content"); + + // when + String viewName = questionController.save(requestDto, bindingResult, principal); + + // then + assertThat(viewName).isEqualTo("redirect:/usr/doctor-qna"); + verify(questionCommandService).save(requestDto, principal); } + @DisplayName("QuestionController_질문등록_실패") + @Test + void save_questionSaveRequestDtoIsInvalid_failure() throws Exception { + // given + QuestionSaveRequestDto requestDto = new QuestionSaveRequestDto("", ""); + + when(bindingResult.hasErrors()).thenReturn(true); + + // when + String viewName = questionController.save(requestDto, bindingResult, principal); + + // then + assertThat(viewName).isEqualTo("doctorqna/doctorQnaQuestionForm"); + verifyNoInteractions(questionCommandService); + } } + From 14b3970685de8b9735f1afa08d5105bb17a6216b Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Sun, 2 Apr 2023 12:04:20 +0900 Subject: [PATCH 04/21] =?UTF-8?q?test=20:=20questionControllerTest=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1(=EC=9D=B4=ED=9B=84=20=EC=88=98=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 + .../controller/QuestionControllerTest.java | 93 +++++++++++++++---- 2 files changed, 79 insertions(+), 16 deletions(-) diff --git a/build.gradle b/build.gradle index bbde3916..c69c530e 100644 --- a/build.gradle +++ b/build.gradle @@ -57,6 +57,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' //Junit testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.9.0' + testImplementation 'org.mockito:mockito-inline:4.6.1' + } tasks.named('test') { diff --git a/src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java b/src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java index a8119561..77aae0ab 100644 --- a/src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java +++ b/src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java @@ -1,30 +1,58 @@ package com.codelion.animalcare.domain.doctorqna.controller; +import com.codelion.animalcare.domain.appointment.interceptor.HasAnimalsInterceptor; import com.codelion.animalcare.domain.doctorqna.dto.request.QuestionSaveRequestDto; import com.codelion.animalcare.domain.doctorqna.service.QuestionCommandService; +import com.codelion.animalcare.domain.doctorqna.service.QuestionQueryService; +import com.codelion.animalcare.domain.user.service.UserService; +import com.codelion.animalcare.global.config.MvcConfig; +import com.codelion.animalcare.webrtc.controller.WebrtcController; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.test.context.ActiveProfiles; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.FilterType; +import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; import org.springframework.validation.BindingResult; import java.security.Principal; -import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.flash; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; -@ActiveProfiles("test") +@MockBean(JpaMetamodelMappingContext.class) @ExtendWith(MockitoExtension.class) +@WebMvcTest(controllers = QuestionController.class, + excludeFilters = { + @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = MvcConfig.class), + @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = HasAnimalsInterceptor.class), + @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = WebrtcController.class)}) public class QuestionControllerTest { - @Mock + @MockBean private QuestionCommandService questionCommandService; + @MockBean + private QuestionQueryService questionQueryService; + + @MockBean + private UserService userService; + @InjectMocks private QuestionController questionController; @@ -34,33 +62,66 @@ public class QuestionControllerTest { @Mock private Principal principal; - @DisplayName("QuestionController_질문등록_성공") + @Autowired + private MockMvc mockMvc; + + @DisplayName("권한이_없으면_Redirection_된다") + @Test + void saveForm_Fail() throws Exception { + + String viewName = questionController.saveForm(new QuestionSaveRequestDto("title", "content")); + + mockMvc.perform(get("/usr/doctor-qna/write")) + .andExpect(status().is3xxRedirection()); + + + } + + @DisplayName("QuestionController_질문작성폼_보여진다") + @WithMockUser(roles="MEMBER") @Test - void save_questionSaveRequestDtoIsValid_success() throws Exception { + void saveForm_Success() throws Exception { + + String viewName = questionController.saveForm(new QuestionSaveRequestDto("title", "content")); + + mockMvc.perform(get("/usr/doctor-qna/write")) + .andExpect(status().isOk()) + .andExpect(view().name(viewName)); + + } + + @Test + @WithMockUser(roles="MEMBER") + void saveTest_withValidRequestDto() throws Exception { // given QuestionSaveRequestDto requestDto = new QuestionSaveRequestDto("title", "content"); // when - String viewName = questionController.save(requestDto, bindingResult, principal); + mockMvc.perform(post("/usr/doctor-qna/write") + .param("title", requestDto.title()) + .param("content", requestDto.content())) + .andExpect(status().is3xxRedirection()) + .andExpect(view().name("redirect:/usr/doctor-qna")) + .andExpect(flash().attributeExists("message")); // then - assertThat(viewName).isEqualTo("redirect:/usr/doctor-qna"); - verify(questionCommandService).save(requestDto, principal); + verify(questionCommandService, times(1)).save(requestDto, any(Principal.class)); } - @DisplayName("QuestionController_질문등록_실패") @Test - void save_questionSaveRequestDtoIsInvalid_failure() throws Exception { + @WithMockUser(roles="MEMBER") + void saveTest_withInvalidRequestDto() throws Exception { // given QuestionSaveRequestDto requestDto = new QuestionSaveRequestDto("", ""); - when(bindingResult.hasErrors()).thenReturn(true); - // when - String viewName = questionController.save(requestDto, bindingResult, principal); + mockMvc.perform(post("/usr/doctor-qna/write") + .param("title", requestDto.title()) + .param("content", requestDto.content())) + .andExpect(status().isOk()) + .andExpect(view().name("doctorqna/doctorQnaQuestionForm")); // then - assertThat(viewName).isEqualTo("doctorqna/doctorQnaQuestionForm"); verifyNoInteractions(questionCommandService); } } From a2561a3179049dbec69ebd7232c6f4c19f02f642 Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Tue, 4 Apr 2023 01:29:10 +0900 Subject: [PATCH 05/21] feat : add hashtag domain --- .../domain/doctorqna/entity/Hashtag.java | 13 +++++++++++++ .../doctorqna/entity/QuestionHashtag.java | 18 ++++++++++++++++++ .../service/QuestionCommandServiceTest.java | 1 + .../service/QuestionQueryServiceTest.java | 1 + 4 files changed, 33 insertions(+) create mode 100644 src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Hashtag.java create mode 100644 src/main/java/com/codelion/animalcare/domain/doctorqna/entity/QuestionHashtag.java diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Hashtag.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Hashtag.java new file mode 100644 index 00000000..17e26c56 --- /dev/null +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Hashtag.java @@ -0,0 +1,13 @@ +package com.codelion.animalcare.domain.doctorqna.entity; + +import com.codelion.animalcare.global.common.entity.BaseEntity; +import lombok.Getter; + +import javax.persistence.Entity; + +@Getter +@Entity +public class Hashtag extends BaseEntity { + + private String tag; +} diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/QuestionHashtag.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/QuestionHashtag.java new file mode 100644 index 00000000..1a074de3 --- /dev/null +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/QuestionHashtag.java @@ -0,0 +1,18 @@ +package com.codelion.animalcare.domain.doctorqna.entity; + +import com.codelion.animalcare.global.common.entity.BaseEntity; +import lombok.Getter; + +import javax.persistence.Entity; +import javax.persistence.ManyToOne; + +@Getter +@Entity +public class QuestionHashtag extends BaseEntity { + + @ManyToOne + private Question question; + + @ManyToOne + private Hashtag hashtag; +} diff --git a/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java index 1127041e..3d9a6513 100644 --- a/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java +++ b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java @@ -93,5 +93,6 @@ void t4() { //given assertThrows(IllegalArgumentException.class, () -> questionQueryService.findQuestionByQuestionId(3L)); } + } diff --git a/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryServiceTest.java b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryServiceTest.java index aa1f2609..b5b39abc 100644 --- a/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryServiceTest.java +++ b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryServiceTest.java @@ -120,4 +120,5 @@ void t6() { assertEquals("DESC", Objects.requireNonNull(questions.getSort().getOrderFor("createdAt")).getDirection().toString()); } + } \ No newline at end of file From 919a267fb2930e5e814e9020ac0ab3c6e001ecc6 Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Tue, 4 Apr 2023 01:32:03 +0900 Subject: [PATCH 06/21] feat : add hashtag model --- .../doctorqna/repository/HashtagRepository.java | 7 +++++++ .../repository/QuestionHashtagRepository.java | 7 +++++++ .../doctorqna/service/HashtagCommandService.java | 11 +++++++++++ .../service/QuestionHashtagQueryService.java | 11 +++++++++++ 4 files changed, 36 insertions(+) create mode 100644 src/main/java/com/codelion/animalcare/domain/doctorqna/repository/HashtagRepository.java create mode 100644 src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionHashtagRepository.java create mode 100644 src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagCommandService.java create mode 100644 src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagQueryService.java diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/HashtagRepository.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/HashtagRepository.java new file mode 100644 index 00000000..31603641 --- /dev/null +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/HashtagRepository.java @@ -0,0 +1,7 @@ +package com.codelion.animalcare.domain.doctorqna.repository; + +import com.codelion.animalcare.domain.doctorqna.entity.Hashtag; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface HashtagRepository extends JpaRepository { +} diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionHashtagRepository.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionHashtagRepository.java new file mode 100644 index 00000000..471722ac --- /dev/null +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionHashtagRepository.java @@ -0,0 +1,7 @@ +package com.codelion.animalcare.domain.doctorqna.repository; + +import com.codelion.animalcare.domain.doctorqna.entity.QuestionHashtag; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface QuestionHashtagRepository extends JpaRepository { +} diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagCommandService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagCommandService.java new file mode 100644 index 00000000..7cd86ce6 --- /dev/null +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagCommandService.java @@ -0,0 +1,11 @@ +package com.codelion.animalcare.domain.doctorqna.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Transactional +@Service +public class HashtagCommandService { +} diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagQueryService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagQueryService.java new file mode 100644 index 00000000..3a5b4371 --- /dev/null +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagQueryService.java @@ -0,0 +1,11 @@ +package com.codelion.animalcare.domain.doctorqna.service; + +import lombok.NoArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@NoArgsConstructor +@Transactional(readOnly = true) +@Service +public class QuestionHashtagQueryService { +} From f36503d5dea67dc839dcd0e0b9801fb3299d82d7 Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Tue, 4 Apr 2023 02:05:12 +0900 Subject: [PATCH 07/21] TODO : dto layer --- .../domain/doctorqna/service/QuestionQueryService.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryService.java index 1b23aa57..3eca93bf 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryService.java @@ -17,7 +17,16 @@ import java.security.Principal; import java.util.ArrayList; import java.util.List; +/* +TODO : SERVICE단에서 QUESTION ENTITY를 직접 반환하지 않고 DTO를 반환하는 것이 요청에 대한 응답만 함으로써 + 성능, 보안적으로 이점을 얻을 수 있다고 생각한다. 하지만, SERVICE단에서 ENTITY가 필요한 경우에 findQuestionByQuestionId를 통해 직접 ENTITY를 얻고 있는데, + 이는 어찌보면 오버엔지니어링이 아닌가 하는 생각도 있다. 이를 어떻게 해결하는 것이 좋은 방법인지 확신이 서지 않는다. + 이를 개선하기 위해서 떠올린 방법으로는 + 1. 그냥 service단에서 entity를 반환하기 + 2. modelMapper를 사용하기 + + */ @Service @RequiredArgsConstructor @Transactional(readOnly = true) From 8793dd396124ec1849c917b473cdeb7a0f29d0df Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Wed, 5 Apr 2023 08:54:12 +0900 Subject: [PATCH 08/21] feat : add questionTags domain --- .../doctorqna/dto/request/QuestionSaveRequestDto.java | 7 ++++++- .../animalcare/domain/doctorqna/entity/Question.java | 6 +++++- .../domain/doctorqna/service/QuestionCommandService.java | 1 - 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java index 5e8c1ef5..279166d8 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java @@ -1,17 +1,22 @@ package com.codelion.animalcare.domain.doctorqna.dto.request; +import com.codelion.animalcare.domain.doctorqna.entity.Hashtag; import com.codelion.animalcare.domain.doctorqna.entity.Question; import com.codelion.animalcare.domain.user.entity.Member; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; +import java.util.List; public record QuestionSaveRequestDto( @NotBlank(message = "제목은 필수 입력 사항입니다.") @Size(max = 200, message = "제목이 너무 길어요.") String title, - @NotBlank(message = "내용은 필수 입력 사항입니다.") String content + @NotBlank(message = "내용은 필수 입력 사항입니다.") + String content, + + List hashtags ) { public Question toEntity(Member member) { return Question.builder() diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Question.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Question.java index 9f32897c..208ba637 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Question.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Question.java @@ -38,7 +38,11 @@ public class Question extends BaseEntity { private Member member; @OneToMany(mappedBy = "question", cascade = CascadeType.ALL, orphanRemoval = true) - private List QuestionLike; + private List QuestionLike; + + @OneToMany(mappedBy = "question", cascade = CascadeType.ALL, orphanRemoval = true) + private List hashtags; + @Builder public Question(String title, String content, int view, int likeCount, Member member) { this.title = title; diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java index c439462c..9b9ee767 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java @@ -32,7 +32,6 @@ public Long save(QuestionSaveRequestDto questionSaveRequestDto, Principal princi return questionRepository.save(questionSaveRequestDto.toEntity(member)).getId(); } - public Long update(Long id, QuestionUpdateRequestDto questionUpdateRequestDto){ Question question = questionQueryService.findQuestionByQuestionId(id); question.update(questionUpdateRequestDto.title(), questionUpdateRequestDto.content()); From 406fef265a292e9da2959d9f6f94d2b0dec65e77 Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Wed, 5 Apr 2023 10:50:34 +0900 Subject: [PATCH 09/21] =?UTF-8?q?feat=20:=20=ED=95=B4=EC=8B=9C=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=EA=B8=B0=EB=8A=A5=20=EC=9E=84=EC=8B=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80(=EC=B6=94=ED=9B=84=EC=97=90=20=EC=88=98=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/QuestionSaveRequestDto.java | 4 +- .../domain/doctorqna/entity/Hashtag.java | 10 ++++- .../domain/doctorqna/entity/Question.java | 3 -- .../doctorqna/entity/QuestionHashtag.java | 13 +++++++ .../repository/HashtagRepository.java | 5 +++ .../repository/QuestionHashtagRepository.java | 4 ++ .../service/HashtagCommandService.java | 19 ++++++++++ .../service/QuestionCommandService.java | 14 ++++++- .../service/QuestionHashtagQueryService.java | 34 +++++++++++++++-- .../controller/QuestionControllerTest.java | 8 ++-- .../service/QuestionCommandServiceTest.java | 37 ++++++++++++++++++- 11 files changed, 135 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java index 279166d8..087d6aca 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java @@ -6,7 +6,7 @@ import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; -import java.util.List; +import java.util.Set; public record QuestionSaveRequestDto( @NotBlank(message = "제목은 필수 입력 사항입니다.") @@ -16,7 +16,7 @@ public record QuestionSaveRequestDto( @NotBlank(message = "내용은 필수 입력 사항입니다.") String content, - List hashtags + Set hashtags ) { public Question toEntity(Member member) { return Question.builder() diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Hashtag.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Hashtag.java index 17e26c56..0ebc0a80 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Hashtag.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Hashtag.java @@ -1,13 +1,21 @@ package com.codelion.animalcare.domain.doctorqna.entity; import com.codelion.animalcare.global.common.entity.BaseEntity; +import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; import javax.persistence.Entity; @Getter +@NoArgsConstructor @Entity public class Hashtag extends BaseEntity { - private String tag; + private String tagName; + + @Builder + public Hashtag(String tagName) { + this.tagName = tagName; + } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Question.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Question.java index 208ba637..9aa13c8a 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Question.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Question.java @@ -40,9 +40,6 @@ public class Question extends BaseEntity { @OneToMany(mappedBy = "question", cascade = CascadeType.ALL, orphanRemoval = true) private List QuestionLike; - @OneToMany(mappedBy = "question", cascade = CascadeType.ALL, orphanRemoval = true) - private List hashtags; - @Builder public Question(String title, String content, int view, int likeCount, Member member) { this.title = title; diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/QuestionHashtag.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/QuestionHashtag.java index 1a074de3..daa5941b 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/QuestionHashtag.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/QuestionHashtag.java @@ -2,10 +2,12 @@ import com.codelion.animalcare.global.common.entity.BaseEntity; import lombok.Getter; +import lombok.NoArgsConstructor; import javax.persistence.Entity; import javax.persistence.ManyToOne; +@NoArgsConstructor @Getter @Entity public class QuestionHashtag extends BaseEntity { @@ -15,4 +17,15 @@ public class QuestionHashtag extends BaseEntity { @ManyToOne private Hashtag hashtag; + + public QuestionHashtag(Question question, Hashtag hashtag) { + this.question = question; + this.hashtag = hashtag; + } + + public static QuestionHashtag of(Question question, Hashtag hashtag) { + return new QuestionHashtag(question, hashtag); + } + + } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/HashtagRepository.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/HashtagRepository.java index 31603641..b3ac099e 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/HashtagRepository.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/HashtagRepository.java @@ -3,5 +3,10 @@ import com.codelion.animalcare.domain.doctorqna.entity.Hashtag; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface HashtagRepository extends JpaRepository { + + + Optional findByTagName(String tagName); } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionHashtagRepository.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionHashtagRepository.java index 471722ac..4ef45a53 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionHashtagRepository.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionHashtagRepository.java @@ -1,7 +1,11 @@ package com.codelion.animalcare.domain.doctorqna.repository; +import com.codelion.animalcare.domain.doctorqna.entity.Question; import com.codelion.animalcare.domain.doctorqna.entity.QuestionHashtag; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface QuestionHashtagRepository extends JpaRepository { + List findAllByQuestion(Question question); } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagCommandService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagCommandService.java index 7cd86ce6..df2431fd 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagCommandService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagCommandService.java @@ -1,11 +1,30 @@ package com.codelion.animalcare.domain.doctorqna.service; +import com.codelion.animalcare.domain.doctorqna.entity.Hashtag; +import com.codelion.animalcare.domain.doctorqna.repository.HashtagRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Optional; + @RequiredArgsConstructor @Transactional @Service public class HashtagCommandService { + + private final HashtagRepository hashtagRepository; + + public Optional findByTagName(String tagName) { + + return hashtagRepository.findByTagName(tagName); + } + + public Hashtag save(String tagName) { + + return hashtagRepository.save( + Hashtag.builder() + .tagName(tagName) + .build()); + } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java index 9b9ee767..acfcdcb9 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java @@ -21,15 +21,25 @@ public class QuestionCommandService { private final QuestionQueryService questionQueryService; + + private final MemberService memberService; + + private final QuestionHashtagQueryService questionHashtagService; + private final QuestionRepository questionRepository; + private final QuestionLikeRepository questionLikeRepository; - private final MemberService memberService; public Long save(QuestionSaveRequestDto questionSaveRequestDto, Principal principal) { Member member = memberService.findMemberByEmail(principal.getName()); - return questionRepository.save(questionSaveRequestDto.toEntity(member)).getId(); + Question savedQuestion = questionRepository.save(questionSaveRequestDto.toEntity(member)); + + questionHashtagService.saveHashtag(savedQuestion, questionSaveRequestDto.hashtags()); + + + return savedQuestion.getId(); } public Long update(Long id, QuestionUpdateRequestDto questionUpdateRequestDto){ diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagQueryService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagQueryService.java index 3a5b4371..57d6ccf5 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagQueryService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagQueryService.java @@ -1,11 +1,39 @@ package com.codelion.animalcare.domain.doctorqna.service; -import lombok.NoArgsConstructor; +import com.codelion.animalcare.domain.doctorqna.entity.Hashtag; +import com.codelion.animalcare.domain.doctorqna.entity.Question; +import com.codelion.animalcare.domain.doctorqna.entity.QuestionHashtag; +import com.codelion.animalcare.domain.doctorqna.repository.QuestionHashtagRepository; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -@NoArgsConstructor -@Transactional(readOnly = true) +import java.util.List; +import java.util.Set; + +@RequiredArgsConstructor +@Transactional @Service public class QuestionHashtagQueryService { + + private final HashtagCommandService hashtagCommandService; + + private final QuestionHashtagRepository questionHashtagRepository; + public void saveHashtag(Question question, Set hashtags) { + hashtags.stream() + .map(hashtag -> + hashtagCommandService.findByTagName(hashtag.getTagName()) + .orElseGet(() -> hashtagCommandService.save(hashtag.getTagName()))) + .forEach(hashtag -> mappedHashtagToQuestion(question, hashtag)); + } + + private Long mappedHashtagToQuestion(Question question, Hashtag hashtag) { + + return questionHashtagRepository.save(QuestionHashtag.of(question, hashtag)).getId(); + } + + public List findHashtagListByQuestion(Question question) { + + return questionHashtagRepository.findAllByQuestion(question); + } } diff --git a/src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java b/src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java index 77aae0ab..e5de7366 100644 --- a/src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java +++ b/src/test/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionControllerTest.java @@ -69,7 +69,7 @@ public class QuestionControllerTest { @Test void saveForm_Fail() throws Exception { - String viewName = questionController.saveForm(new QuestionSaveRequestDto("title", "content")); + String viewName = questionController.saveForm(new QuestionSaveRequestDto("title", "content", null)); mockMvc.perform(get("/usr/doctor-qna/write")) .andExpect(status().is3xxRedirection()); @@ -82,7 +82,7 @@ void saveForm_Fail() throws Exception { @Test void saveForm_Success() throws Exception { - String viewName = questionController.saveForm(new QuestionSaveRequestDto("title", "content")); + String viewName = questionController.saveForm(new QuestionSaveRequestDto("title", "content", null)); mockMvc.perform(get("/usr/doctor-qna/write")) .andExpect(status().isOk()) @@ -94,7 +94,7 @@ void saveForm_Success() throws Exception { @WithMockUser(roles="MEMBER") void saveTest_withValidRequestDto() throws Exception { // given - QuestionSaveRequestDto requestDto = new QuestionSaveRequestDto("title", "content"); + QuestionSaveRequestDto requestDto = new QuestionSaveRequestDto("title", "content", null); // when mockMvc.perform(post("/usr/doctor-qna/write") @@ -112,7 +112,7 @@ void saveTest_withValidRequestDto() throws Exception { @WithMockUser(roles="MEMBER") void saveTest_withInvalidRequestDto() throws Exception { // given - QuestionSaveRequestDto requestDto = new QuestionSaveRequestDto("", ""); + QuestionSaveRequestDto requestDto = new QuestionSaveRequestDto("", "", null); // when mockMvc.perform(post("/usr/doctor-qna/write") diff --git a/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java index 3d9a6513..b4911060 100644 --- a/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java +++ b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java @@ -3,7 +3,9 @@ import com.codelion.animalcare.domain.doctorqna.dto.request.QuestionSaveRequestDto; import com.codelion.animalcare.domain.doctorqna.dto.request.QuestionUpdateRequestDto; import com.codelion.animalcare.domain.doctorqna.dto.response.QuestionResponseDto; +import com.codelion.animalcare.domain.doctorqna.entity.Hashtag; import com.codelion.animalcare.domain.doctorqna.entity.Question; +import com.codelion.animalcare.domain.doctorqna.entity.QuestionHashtag; import com.codelion.animalcare.domain.user.service.MemberService; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -14,6 +16,9 @@ import org.springframework.transaction.annotation.Transactional; import java.security.Principal; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -30,12 +35,14 @@ public class QuestionCommandServiceTest { private QuestionQueryService questionQueryService; @Autowired private MemberService memberService; + @Autowired + private QuestionHashtagQueryService questionHashtagQueryService; @DisplayName("질문_작성된다") @Test void t1() { //given - QuestionSaveRequestDto question = new QuestionSaveRequestDto("질문이 있습니다.", "테스트 코드는 어떻게 잘 짜나요?"); + QuestionSaveRequestDto question = new QuestionSaveRequestDto("질문이 있습니다.", "테스트 코드는 어떻게 잘 짜나요?", null); Principal principal = () -> "member1@test.com"; //when @@ -94,5 +101,33 @@ void t4() { assertThrows(IllegalArgumentException.class, () -> questionQueryService.findQuestionByQuestionId(3L)); } + @DisplayName("해시태그_저장된다") + @Test + void t5() { + //given + + Hashtag javaTag = Hashtag.builder().tagName("JAVA").build(); + Hashtag testTag = Hashtag.builder().tagName("TEST").build(); + Set hashtags =new HashSet<>(); + hashtags.add(javaTag); + hashtags.add(testTag); + QuestionSaveRequestDto question = new QuestionSaveRequestDto("질문이 있습니다.", "테스트 코드는 어떻게 잘 짜나요?", hashtags); + Principal principal = () -> "member1@test.com"; + + //when + questionCommandService.save(question, principal); + //Testinitdata -> 3 question + Question savedQuestion = questionQueryService.findQuestionByQuestionId(4L); + List hashtagList = questionHashtagQueryService.findHashtagListByQuestion(savedQuestion); + + for(QuestionHashtag questionHashtag : hashtagList) { + System.out.println(questionHashtag.getHashtag().getTagName()); + } + + hashtagList.forEach(hashtag -> System.out.println(hashtag.getHashtag().getTagName())); + + assertEquals(hashtagList.size(), 2); + } + } From 846155875e826a79e883e2583e25a58ffa541acf Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Wed, 5 Apr 2023 22:43:55 +0900 Subject: [PATCH 10/21] =?UTF-8?q?refactor=20:=20hashtags=EC=97=90=EC=84=9C?= =?UTF-8?q?=20cqrs=20=ED=8C=A8=ED=84=B4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{HashtagCommandService.java => HashtagService.java} | 2 +- .../domain/doctorqna/service/QuestionCommandService.java | 2 +- ...shtagQueryService.java => QuestionHashtagService.java} | 8 ++++---- .../doctorqna/service/QuestionCommandServiceTest.java | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) rename src/main/java/com/codelion/animalcare/domain/doctorqna/service/{HashtagCommandService.java => HashtagService.java} (95%) rename src/main/java/com/codelion/animalcare/domain/doctorqna/service/{QuestionHashtagQueryService.java => QuestionHashtagService.java} (84%) diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagCommandService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagService.java similarity index 95% rename from src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagCommandService.java rename to src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagService.java index df2431fd..b10a4d58 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagCommandService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagService.java @@ -11,7 +11,7 @@ @RequiredArgsConstructor @Transactional @Service -public class HashtagCommandService { +public class HashtagService { private final HashtagRepository hashtagRepository; diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java index acfcdcb9..8aed3658 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java @@ -24,7 +24,7 @@ public class QuestionCommandService { private final MemberService memberService; - private final QuestionHashtagQueryService questionHashtagService; + private final QuestionHashtagService questionHashtagService; private final QuestionRepository questionRepository; diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagQueryService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagService.java similarity index 84% rename from src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagQueryService.java rename to src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagService.java index 57d6ccf5..20061922 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagQueryService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagService.java @@ -14,16 +14,16 @@ @RequiredArgsConstructor @Transactional @Service -public class QuestionHashtagQueryService { +public class QuestionHashtagService { - private final HashtagCommandService hashtagCommandService; + private final HashtagService hashtagService; private final QuestionHashtagRepository questionHashtagRepository; public void saveHashtag(Question question, Set hashtags) { hashtags.stream() .map(hashtag -> - hashtagCommandService.findByTagName(hashtag.getTagName()) - .orElseGet(() -> hashtagCommandService.save(hashtag.getTagName()))) + hashtagService.findByTagName(hashtag.getTagName()) + .orElseGet(() -> hashtagService.save(hashtag.getTagName()))) .forEach(hashtag -> mappedHashtagToQuestion(question, hashtag)); } diff --git a/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java index b4911060..0c54f620 100644 --- a/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java +++ b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java @@ -36,7 +36,7 @@ public class QuestionCommandServiceTest { @Autowired private MemberService memberService; @Autowired - private QuestionHashtagQueryService questionHashtagQueryService; + private QuestionHashtagService questionHashtagService; @DisplayName("질문_작성된다") @Test @@ -118,7 +118,7 @@ void t5() { questionCommandService.save(question, principal); //Testinitdata -> 3 question Question savedQuestion = questionQueryService.findQuestionByQuestionId(4L); - List hashtagList = questionHashtagQueryService.findHashtagListByQuestion(savedQuestion); + List hashtagList = questionHashtagService.findHashtagListByQuestion(savedQuestion); for(QuestionHashtag questionHashtag : hashtagList) { System.out.println(questionHashtag.getHashtag().getTagName()); From 31a1a4f116b46099efe9a824a4d1f76dbd450da1 Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Wed, 5 Apr 2023 23:53:36 +0900 Subject: [PATCH 11/21] feat : add tag form --- .../doctorqna/doctorQnaQuestionForm.html | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html b/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html index bfd158a5..31e0e109 100644 --- a/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html +++ b/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html @@ -1,4 +1,7 @@ - +
@@ -15,7 +18,44 @@
질문등록
+
+ + +
    +
  • +
+
+ + + + + From 407e6b687911bf6dade8f280e642e113ae733b6c Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Thu, 6 Apr 2023 00:39:27 +0900 Subject: [PATCH 12/21] feat : fix tag input error --- .../doctorqna/controller/QuestionController.java | 1 - .../dto/request/QuestionSaveRequestDto.java | 5 ++--- .../doctorqna/service/QuestionCommandService.java | 2 +- .../doctorqna/service/QuestionHashtagService.java | 9 ++++----- .../templates/doctorqna/doctorQnaQuestionForm.html | 5 +++-- .../service/QuestionCommandServiceTest.java | 13 ++++--------- 6 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java index f4fc747e..4bbc4934 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java @@ -48,7 +48,6 @@ public String save(@Valid QuestionSaveRequestDto questionSaveRequestDto, Binding if(bindingResult.hasErrors()) { return "doctorqna/doctorQnaQuestionForm"; } - questionCommandService.save(questionSaveRequestDto, principal); return "redirect:/usr/doctor-qna"; diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java index 087d6aca..5b703adc 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java @@ -1,12 +1,11 @@ package com.codelion.animalcare.domain.doctorqna.dto.request; -import com.codelion.animalcare.domain.doctorqna.entity.Hashtag; import com.codelion.animalcare.domain.doctorqna.entity.Question; import com.codelion.animalcare.domain.user.entity.Member; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; -import java.util.Set; +import java.util.List; public record QuestionSaveRequestDto( @NotBlank(message = "제목은 필수 입력 사항입니다.") @@ -16,7 +15,7 @@ public record QuestionSaveRequestDto( @NotBlank(message = "내용은 필수 입력 사항입니다.") String content, - Set hashtags + List hashtags ) { public Question toEntity(Member member) { return Question.builder() diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java index 8aed3658..1f1cca69 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandService.java @@ -35,7 +35,7 @@ public class QuestionCommandService { public Long save(QuestionSaveRequestDto questionSaveRequestDto, Principal principal) { Member member = memberService.findMemberByEmail(principal.getName()); Question savedQuestion = questionRepository.save(questionSaveRequestDto.toEntity(member)); - + questionSaveRequestDto.hashtags().forEach(System.out::println); questionHashtagService.saveHashtag(savedQuestion, questionSaveRequestDto.hashtags()); diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagService.java index 20061922..17b0bc14 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagService.java @@ -9,7 +9,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.Set; @RequiredArgsConstructor @Transactional @@ -19,11 +18,11 @@ public class QuestionHashtagService { private final HashtagService hashtagService; private final QuestionHashtagRepository questionHashtagRepository; - public void saveHashtag(Question question, Set hashtags) { - hashtags.stream() + public void saveHashtag(Question question, List tagNames) { + tagNames.stream() .map(hashtag -> - hashtagService.findByTagName(hashtag.getTagName()) - .orElseGet(() -> hashtagService.save(hashtag.getTagName()))) + hashtagService.findByTagName(hashtag) + .orElseGet(() -> hashtagService.save(hashtag))) .forEach(hashtag -> mappedHashtagToQuestion(question, hashtag)); } diff --git a/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html b/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html index 31e0e109..c6c5c950 100644 --- a/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html +++ b/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html @@ -20,7 +20,7 @@
질문등록
- +
@@ -38,11 +38,12 @@
질문등록
let hashtags = []; function addHashtag(tag) { + tag = tag.replace(/[\[\]]/g, ''); const li = document.createElement("li"); li.innerText = tag; hashtagsList.appendChild(li); hashtags.push(tag); - hiddenHashtagsInput.value = JSON.stringify(hashtags); + hiddenHashtagsInput.value = hashtags.join(","); } hashtagsInput.addEventListener("keydown", (event) => { diff --git a/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java index 0c54f620..fcc55a39 100644 --- a/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java +++ b/src/test/java/com/codelion/animalcare/domain/doctorqna/service/QuestionCommandServiceTest.java @@ -3,7 +3,6 @@ import com.codelion.animalcare.domain.doctorqna.dto.request.QuestionSaveRequestDto; import com.codelion.animalcare.domain.doctorqna.dto.request.QuestionUpdateRequestDto; import com.codelion.animalcare.domain.doctorqna.dto.response.QuestionResponseDto; -import com.codelion.animalcare.domain.doctorqna.entity.Hashtag; import com.codelion.animalcare.domain.doctorqna.entity.Question; import com.codelion.animalcare.domain.doctorqna.entity.QuestionHashtag; import com.codelion.animalcare.domain.user.service.MemberService; @@ -16,9 +15,8 @@ import org.springframework.transaction.annotation.Transactional; import java.security.Principal; -import java.util.HashSet; +import java.util.Arrays; import java.util.List; -import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -106,12 +104,9 @@ void t4() { void t5() { //given - Hashtag javaTag = Hashtag.builder().tagName("JAVA").build(); - Hashtag testTag = Hashtag.builder().tagName("TEST").build(); - Set hashtags =new HashSet<>(); - hashtags.add(javaTag); - hashtags.add(testTag); - QuestionSaveRequestDto question = new QuestionSaveRequestDto("질문이 있습니다.", "테스트 코드는 어떻게 잘 짜나요?", hashtags); + + List list = Arrays.asList("JAVA", "THYMELEAF"); + QuestionSaveRequestDto question = new QuestionSaveRequestDto("질문이 있습니다.", "테스트 코드는 어떻게 잘 짜나요?", list); Principal principal = () -> "member1@test.com"; //when From 607de38a678fff41d0ca1ae817967d26d3033db6 Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Thu, 6 Apr 2023 01:04:01 +0900 Subject: [PATCH 13/21] =?UTF-8?q?feat=20:=20=EA=B2=8C=EC=8B=9C=EB=AC=BC?= =?UTF-8?q?=EC=97=90=20=ED=95=B4=EC=8B=9C=ED=83=9C=EA=B7=B8=20=EC=B6=9C?= =?UTF-8?q?=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/doctorqna/controller/QuestionController.java | 9 +++++++-- .../resources/templates/doctorqna/doctorQnaDetail.html | 5 ++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java index 4bbc4934..a413bae3 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java @@ -4,8 +4,10 @@ import com.codelion.animalcare.domain.doctorqna.dto.request.QuestionSaveRequestDto; import com.codelion.animalcare.domain.doctorqna.dto.request.QuestionUpdateRequestDto; import com.codelion.animalcare.domain.doctorqna.entity.Question; -import com.codelion.animalcare.domain.doctorqna.service.QuestionQueryService; +import com.codelion.animalcare.domain.doctorqna.entity.QuestionHashtag; import com.codelion.animalcare.domain.doctorqna.service.QuestionCommandService; +import com.codelion.animalcare.domain.doctorqna.service.QuestionHashtagService; +import com.codelion.animalcare.domain.doctorqna.service.QuestionQueryService; import com.codelion.animalcare.domain.user.entity.UserInfo; import com.codelion.animalcare.domain.user.service.UserService; import lombok.RequiredArgsConstructor; @@ -25,6 +27,7 @@ import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import java.security.Principal; +import java.util.List; @RequiredArgsConstructor @@ -34,6 +37,7 @@ public class QuestionController { private final QuestionCommandService questionCommandService; private final QuestionQueryService questionQueryService; private final UserService userService; + private final QuestionHashtagService questionHashtagService; //게시글 등록 화면 @GetMapping("/usr/doctor-qna/write") @@ -97,7 +101,8 @@ public String findById(Model model, @PathVariable Long id, AnswerSaveRequestDto } model.addAttribute("like", like); - + List hashtags = questionHashtagService.findHashtagListByQuestion(questionQueryService.findQuestionByQuestionId(id)); + model.addAttribute("hashtags", hashtags); return "doctorqna/doctorQnaDetail"; } //전체 조회 diff --git a/src/main/resources/templates/doctorqna/doctorQnaDetail.html b/src/main/resources/templates/doctorqna/doctorQnaDetail.html index 01c9fc86..3b54ba59 100644 --- a/src/main/resources/templates/doctorqna/doctorQnaDetail.html +++ b/src/main/resources/templates/doctorqna/doctorQnaDetail.html @@ -32,7 +32,10 @@

- + +
+
+
From f14a44e283509d795f2bd96803c45c955255a66a Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Thu, 6 Apr 2023 01:12:24 +0900 Subject: [PATCH 14/21] =?UTF-8?q?TODO=20:=20=EC=B6=94=EA=B0=80=ED=95=A0=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/doctorqna/controller/QuestionController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java index a413bae3..2b9109ae 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java @@ -29,7 +29,7 @@ import java.security.Principal; import java.util.List; - +//TODO : 2022/04/06 -> 참조하지 않는 해시태그는? (배치?), 게시물 수정할때 해시태그도 수정 가능하게 하기, 해시태그 누르면 리스트 띄우기, ... @RequiredArgsConstructor @Controller public class QuestionController { From 86ed7e39ef94296ddd4ae39a136aa632e42f37f9 Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Fri, 7 Apr 2023 06:37:42 +0900 Subject: [PATCH 15/21] =?UTF-8?q?style=20:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=BB=A8=EB=B2=A4=EC=85=98=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AnswerController.java | 26 ++++++---- .../controller/QuestionController.java | 49 +++++++++++-------- .../dto/request/AnswerSaveRequestDto.java | 33 +++++++------ .../dto/request/AnswerUpdateRequestDto.java | 14 +++--- .../dto/request/QuestionSaveRequestDto.java | 21 ++++---- .../dto/request/QuestionUpdateRequestDto.java | 23 ++++----- .../dto/response/AnswerResponseDto.java | 6 ++- .../dto/response/QuestionListResponseDto.java | 6 ++- .../dto/response/QuestionResponseDto.java | 10 ++-- .../domain/doctorqna/entity/Answer.java | 15 +++--- .../domain/doctorqna/entity/Question.java | 3 +- .../repository/HashtagRepository.java | 1 - .../repository/QuestionHashtagRepository.java | 1 + .../repository/QuestionRepository.java | 5 +- .../service/AnswerCommandService.java | 6 ++- .../doctorqna/service/AnswerQueryService.java | 7 +-- .../doctorqna/service/HashtagService.java | 6 +-- .../service/QuestionHashtagService.java | 31 ++++++------ .../service/QuestionQueryService.java | 10 ++-- 19 files changed, 153 insertions(+), 120 deletions(-) diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/AnswerController.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/AnswerController.java index c4f27ea9..1eb954f2 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/AnswerController.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/AnswerController.java @@ -30,9 +30,10 @@ public class AnswerController { //답변 작성 @PostMapping("/usr/doctor-qna/{questionId}/answers/write") - public String save(Model model, @PathVariable Long questionId, @Valid AnswerSaveRequestDto answerForm, BindingResult bindingResult, Principal principal){ + public String save(Model model, @PathVariable Long questionId, + @Valid AnswerSaveRequestDto answerForm, BindingResult bindingResult, Principal principal) { - if(bindingResult.hasErrors()) { + if (bindingResult.hasErrors()) { model.addAttribute("question", questionQueryService.findById(questionId)); return "doctorqna/doctorQnaDetail"; } @@ -41,16 +42,18 @@ public String save(Model model, @PathVariable Long questionId, @Valid AnswerSave // throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "의사만 답변을 작성할 수 있습니다."); // } Question question = questionQueryService.findQuestionByQuestionId(questionId); - AnswerSaveRequestDto answerSaveRequestDto = new AnswerSaveRequestDto(answerForm.content(), question); + AnswerSaveRequestDto answerSaveRequestDto = new AnswerSaveRequestDto(answerForm.content(), + question); answerCommandService.save(questionId, answerSaveRequestDto, principal); return "redirect:/usr/doctor-qna/%d".formatted(questionId); } @GetMapping("/usr/doctor-qna/{questionId}/answers/{answerId}/modify") - public String modify(Model model, @PathVariable Long questionId, @PathVariable Long answerId, AnswerUpdateRequestDto answerUpdateRequestDto, Principal principal){ + public String modify(Model model, @PathVariable Long questionId, @PathVariable Long answerId, + AnswerUpdateRequestDto answerUpdateRequestDto, Principal principal) { - if(answerQueryService.answerAuthorized(answerId, principal)){ + if (answerQueryService.answerAuthorized(answerId, principal)) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "수정권한이 없습니다."); } @@ -60,13 +63,15 @@ public String modify(Model model, @PathVariable Long questionId, @PathVariable L } @PostMapping("/usr/doctor-qna/{questionId}/answers/{answerId}/modify") - public String modify(@PathVariable Long questionId, @PathVariable Long answerId, @Valid AnswerUpdateRequestDto answerUpdateRequestDto, BindingResult bindingResult, Principal principal){ + public String modify(@PathVariable Long questionId, @PathVariable Long answerId, + @Valid AnswerUpdateRequestDto answerUpdateRequestDto, BindingResult bindingResult, + Principal principal) { - if(bindingResult.hasErrors()) { + if (bindingResult.hasErrors()) { return "doctorqna/doctorQnaAnswerModifyForm"; } - if(answerQueryService.answerAuthorized(answerId, principal)){ + if (answerQueryService.answerAuthorized(answerId, principal)) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "수정권한이 없습니다."); } @@ -76,9 +81,10 @@ public String modify(@PathVariable Long questionId, @PathVariable Long answerId, //답변 삭제 @GetMapping("/usr/doctor-qna/{questionId}/answers/{answerId}/delete") - public String delete(@PathVariable Long questionId, @PathVariable Long answerId, Principal principal) { + public String delete(@PathVariable Long questionId, @PathVariable Long answerId, + Principal principal) { - if(answerQueryService.answerAuthorized(answerId, principal)){ + if (answerQueryService.answerAuthorized(answerId, principal)) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "삭제권한이 없습니다."); } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java index 2b9109ae..70b32be2 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java @@ -41,15 +41,16 @@ public class QuestionController { //게시글 등록 화면 @GetMapping("/usr/doctor-qna/write") - public String saveForm(QuestionSaveRequestDto questionSaveRequestDto){ + public String saveForm(QuestionSaveRequestDto questionSaveRequestDto) { return "doctorqna/doctorQnaQuestionForm"; } //게시글 등록 @PostMapping("/usr/doctor-qna/write") - public String save(@Valid QuestionSaveRequestDto questionSaveRequestDto, BindingResult bindingResult, Principal principal) { + public String save(@Valid QuestionSaveRequestDto questionSaveRequestDto, + BindingResult bindingResult, Principal principal) { - if(bindingResult.hasErrors()) { + if (bindingResult.hasErrors()) { return "doctorqna/doctorQnaQuestionForm"; } questionCommandService.save(questionSaveRequestDto, principal); @@ -59,20 +60,22 @@ public String save(@Valid QuestionSaveRequestDto questionSaveRequestDto, Binding //개별 조회 @GetMapping("/usr/doctor-qna/{id}") - public String findById(Model model, @PathVariable Long id, AnswerSaveRequestDto answerSaveRequestDto, HttpServletRequest request, HttpServletResponse response, Principal principal){ + public String findById(Model model, @PathVariable Long id, + AnswerSaveRequestDto answerSaveRequestDto, HttpServletRequest request, + HttpServletResponse response, Principal principal) { // 조회수 쿠키로 중복방지 Cookie oldCookie = null; Cookie[] cookies = request.getCookies(); - if(cookies != null) { - for(var cookie : cookies) { - if(cookie.getName().equals("DoctorQnaView")) { + if (cookies != null) { + for (var cookie : cookies) { + if (cookie.getName().equals("DoctorQnaView")) { oldCookie = cookie; } } } - if(oldCookie != null) { - if(!oldCookie.getValue().contains("[" + id.toString() + "]")) { + if (oldCookie != null) { + if (!oldCookie.getValue().contains("[" + id.toString() + "]")) { questionCommandService.updateView(id); oldCookie.setValue(oldCookie.getValue() + "_[" + id + "]"); oldCookie.setPath("/"); @@ -93,7 +96,7 @@ public String findById(Model model, @PathVariable Long id, AnswerSaveRequestDto UserInfo user = userService.getUserInfo(principal.getName()).orElse(null); - if(user != null) { // 로그인 한 사용자라면 + if (user != null) { // 로그인 한 사용자라면 model.addAttribute("login_id", user.getId()); like = questionQueryService.findLike(id, user); // 로그인 유저의 추천 여부 확인 @@ -101,13 +104,16 @@ public String findById(Model model, @PathVariable Long id, AnswerSaveRequestDto } model.addAttribute("like", like); - List hashtags = questionHashtagService.findHashtagListByQuestion(questionQueryService.findQuestionByQuestionId(id)); + List hashtags = questionHashtagService.findHashtagListByQuestion( + questionQueryService.findQuestionByQuestionId(id)); model.addAttribute("hashtags", hashtags); return "doctorqna/doctorQnaDetail"; } + //전체 조회 @GetMapping("/usr/doctor-qna") - public String findAll(Model model, @RequestParam(value="page", defaultValue="0") int page, String type, String kw) { + public String findAll(Model model, @RequestParam(value = "page", defaultValue = "0") int page, + String type, String kw) { Page paging = questionQueryService.findAll(page, type, kw); @@ -115,28 +121,31 @@ public String findAll(Model model, @RequestParam(value="page", defaultValue="0") model.addAttribute("kw", kw); model.addAttribute("type", type); - return "doctorqna/doctorQnaList"; } @GetMapping("/usr/doctor-qna/{id}/modify") - public String update(Model model, @PathVariable Long id, QuestionUpdateRequestDto questionUpdateRequestDto, Principal principal){ + public String update(Model model, @PathVariable Long id, + QuestionUpdateRequestDto questionUpdateRequestDto, Principal principal) { - if(questionQueryService.questionAuthorized(id, principal)){ + if (questionQueryService.questionAuthorized(id, principal)) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "수정권한이 없습니다."); } model.addAttribute("question", questionQueryService.findById(id)); return "doctorqna/doctorQnaQuestionModifyForm"; } + @PostMapping("/usr/doctor-qna/{id}/modify") - public String update(@PathVariable Long id, @Valid QuestionUpdateRequestDto questionUpdateRequestDto, BindingResult bindingResult, Principal principal){ + public String update(@PathVariable Long id, + @Valid QuestionUpdateRequestDto questionUpdateRequestDto, BindingResult bindingResult, + Principal principal) { - if(bindingResult.hasErrors()) { + if (bindingResult.hasErrors()) { return "doctorqna/doctorQnaQuestionModifyForm"; } - if(questionQueryService.questionAuthorized(id, principal)){ + if (questionQueryService.questionAuthorized(id, principal)) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "수정권한이 없습니다."); } @@ -146,9 +155,9 @@ public String update(@PathVariable Long id, @Valid QuestionUpdateRequestDto ques } @GetMapping("/usr/doctor-qna/{id}/delete") - public String delete(@PathVariable Long id, Principal principal){ + public String delete(@PathVariable Long id, Principal principal) { - if(questionQueryService.questionAuthorized(id, principal)){ + if (questionQueryService.questionAuthorized(id, principal)) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "삭제권한이 없습니다."); } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerSaveRequestDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerSaveRequestDto.java index d4b5b051..cfb45097 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerSaveRequestDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerSaveRequestDto.java @@ -7,23 +7,24 @@ import javax.validation.constraints.NotEmpty; -public record AnswerSaveRequestDto ( - @NotEmpty(message = "내용은 필수 입력 항목입니다.") - String content, +public record AnswerSaveRequestDto( + @NotEmpty(message = "내용은 필수 입력 항목입니다.") + String content, - Question question + Question question ) { - @Builder - public AnswerSaveRequestDto(String content, Question question){ - this.content = content; - this.question = question; - } - public Answer toEntity(Doctor doctor) { - return Answer.builder() - .content(content) - .question(question) - .doctor(doctor) - .build(); - } + @Builder + public AnswerSaveRequestDto(String content, Question question) { + this.content = content; + this.question = question; + } + + public Answer toEntity(Doctor doctor) { + return Answer.builder() + .content(content) + .question(question) + .doctor(doctor) + .build(); + } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerUpdateRequestDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerUpdateRequestDto.java index eb92a46f..6fb2193c 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerUpdateRequestDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/AnswerUpdateRequestDto.java @@ -3,12 +3,14 @@ import lombok.Builder; import javax.validation.constraints.NotBlank; + public record AnswerUpdateRequestDto( - @NotBlank(message = "내용은 필수 입력 항목입니다.") - String content + @NotBlank(message = "내용은 필수 입력 항목입니다.") + String content ) { - @Builder - public AnswerUpdateRequestDto(String content) { - this.content = content; - } + + @Builder + public AnswerUpdateRequestDto(String content) { + this.content = content; + } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java index 5b703adc..5747aa5f 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionSaveRequestDto.java @@ -8,20 +8,21 @@ import java.util.List; public record QuestionSaveRequestDto( - @NotBlank(message = "제목은 필수 입력 사항입니다.") - @Size(max = 200, message = "제목이 너무 길어요.") - String title, + @NotBlank(message = "제목은 필수 입력 사항입니다.") + @Size(max = 200, message = "제목이 너무 길어요.") + String title, - @NotBlank(message = "내용은 필수 입력 사항입니다.") - String content, + @NotBlank(message = "내용은 필수 입력 사항입니다.") + String content, - List hashtags + List hashtags ) { + public Question toEntity(Member member) { return Question.builder() - .title(title()) - .content(content()) - .member(member) - .build(); + .title(title()) + .content(content()) + .member(member) + .build(); } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionUpdateRequestDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionUpdateRequestDto.java index 48157ffe..9dab40d9 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionUpdateRequestDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/request/QuestionUpdateRequestDto.java @@ -5,17 +5,18 @@ import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; -public record QuestionUpdateRequestDto ( - @NotBlank(message = "제목은 필수 항목 입니다.") - @Size(max = 200) - String title, +public record QuestionUpdateRequestDto( + @NotBlank(message = "제목은 필수 항목 입니다.") + @Size(max = 200) + String title, - @NotBlank(message = "내용은 필수 항목 입니다.") - String content + @NotBlank(message = "내용은 필수 항목 입니다.") + String content ) { - @Builder - public QuestionUpdateRequestDto(String title, String content) { - this.title = title; - this.content = content; - } + + @Builder + public QuestionUpdateRequestDto(String title, String content) { + this.title = title; + this.content = content; + } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/AnswerResponseDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/AnswerResponseDto.java index c2067afb..a1870a67 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/AnswerResponseDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/AnswerResponseDto.java @@ -5,15 +5,17 @@ import java.time.LocalDateTime; -public record AnswerResponseDto ( +public record AnswerResponseDto( Long id, String content, Question question, LocalDateTime createdAt, LocalDateTime updatedAt ) { + public AnswerResponseDto(Answer entity) { - this(entity.getId(), entity.getContent(), entity.getQuestion(), entity.getCreatedAt(), entity.getUpdatedAt()); + this(entity.getId(), entity.getContent(), entity.getQuestion(), entity.getCreatedAt(), + entity.getUpdatedAt()); } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/QuestionListResponseDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/QuestionListResponseDto.java index 8a247ee5..257ae0c6 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/QuestionListResponseDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/QuestionListResponseDto.java @@ -5,7 +5,7 @@ import java.time.LocalDateTime; -public record QuestionListResponseDto ( +public record QuestionListResponseDto( Long id, String title, LocalDateTime createdAt, @@ -14,7 +14,9 @@ public record QuestionListResponseDto ( Member member ) { + public QuestionListResponseDto(Question entity) { - this(entity.getId(), entity.getTitle(), entity.getCreatedAt(), entity.getView(), entity.getLikeCount(), entity.getMember()); + this(entity.getId(), entity.getTitle(), entity.getCreatedAt(), entity.getView(), + entity.getLikeCount(), entity.getMember()); } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/QuestionResponseDto.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/QuestionResponseDto.java index 60cec186..726c3761 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/QuestionResponseDto.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/dto/response/QuestionResponseDto.java @@ -7,7 +7,7 @@ import java.time.LocalDateTime; import java.util.List; -public record QuestionResponseDto ( +public record QuestionResponseDto( Long id, String title, String content, @@ -17,9 +17,11 @@ public record QuestionResponseDto ( Member member, int likeCount ) { - public QuestionResponseDto(Question entity){ - this(entity.getId(), entity.getTitle(), entity.getContent(), entity.getCreatedAt(), entity.getView(), - entity.getAnswerList(), entity.getMember(), entity.getLikeCount()); + + public QuestionResponseDto(Question entity) { + this(entity.getId(), entity.getTitle(), entity.getContent(), entity.getCreatedAt(), + entity.getView(), + entity.getAnswerList(), entity.getMember(), entity.getLikeCount()); } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Answer.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Answer.java index 825bb6cc..4d4c86db 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Answer.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Answer.java @@ -1,20 +1,19 @@ package com.codelion.animalcare.domain.doctorqna.entity; +import static javax.persistence.FetchType.LAZY; + import com.codelion.animalcare.domain.user.entity.Doctor; import com.codelion.animalcare.global.common.entity.BaseEntity; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - import javax.persistence.Column; import javax.persistence.ConstraintMode; import javax.persistence.Entity; import javax.persistence.ForeignKey; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; - -import static javax.persistence.FetchType.*; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; @Getter @NoArgsConstructor @@ -40,7 +39,7 @@ public Answer(String content, Question question, Doctor doctor) { this.doctor = doctor; } - public void update(String content){ + public void update(String content) { this.content = content; } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Question.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Question.java index 9aa13c8a..24d31d12 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Question.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/entity/Question.java @@ -49,10 +49,11 @@ public Question(String title, String content, int view, int likeCount, Member me this.likeCount = likeCount; } - public void update(String title, String content){ + public void update(String title, String content) { this.title = title; this.content = content; } + public void addAnswer(Answer answer) { answer.setQuestion(this); //getAnswerList().add(answer); diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/HashtagRepository.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/HashtagRepository.java index b3ac099e..8d03bfa5 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/HashtagRepository.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/HashtagRepository.java @@ -7,6 +7,5 @@ public interface HashtagRepository extends JpaRepository { - Optional findByTagName(String tagName); } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionHashtagRepository.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionHashtagRepository.java index 4ef45a53..e0a2c09a 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionHashtagRepository.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionHashtagRepository.java @@ -7,5 +7,6 @@ import java.util.List; public interface QuestionHashtagRepository extends JpaRepository { + List findAllByQuestion(Question question); } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionRepository.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionRepository.java index dc72b0a6..81aad07c 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionRepository.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/repository/QuestionRepository.java @@ -17,10 +17,11 @@ public interface QuestionRepository extends JpaRepository { Page findByContentContaining(String kw, Pageable pageable); @Query("select q from Question q where q.member.name like concat('%',UPPER(:kw),'%')") - Page findByMemberContaining(@Param("kw")String kw, Pageable pageable); + Page findByMemberContaining(@Param("kw") String kw, Pageable pageable); @Query("select q from Question q where q.title like concat('%', UPPER(:kw), '%') or q.content like concat('%', UPPER(:kw), '%')") - Page findByTitleOrContent(@Param("kw")String kw, Pageable pageable); + Page findByTitleOrContent(@Param("kw") String kw, Pageable pageable); + @Modifying(clearAutomatically = true) @Query("update Question q set q.view = q.view + 1 where q.id = :id") int updateView(@Param("id") Long id); diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/AnswerCommandService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/AnswerCommandService.java index a4e897c6..90e388a1 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/AnswerCommandService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/AnswerCommandService.java @@ -23,9 +23,11 @@ public class AnswerCommandService { private final QuestionQueryService questionQueryService; - public Long save(Long questionId, AnswerSaveRequestDto answerSaveRequestDto, Principal principal){ + public Long save(Long questionId, AnswerSaveRequestDto answerSaveRequestDto, + Principal principal) { - Doctor doctor = doctorRepository.findByEmail(principal.getName()).orElseThrow(() -> new IllegalArgumentException("의사가 존재하지 않습니다.")); + Doctor doctor = doctorRepository.findByEmail(principal.getName()).orElseThrow( + () -> new IllegalArgumentException("의사가 존재하지 않습니다.")); // if(!member.getAuth().equals("doctor")) { // throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "의사만 답변을 작성할 수 있습니다."); diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/AnswerQueryService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/AnswerQueryService.java index a7b84f80..c2469135 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/AnswerQueryService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/AnswerQueryService.java @@ -19,7 +19,7 @@ public class AnswerQueryService { public boolean answerAuthorized(Long answerId, Principal principal) { Answer answer = findAnswerByAnswerId(answerId); - if(answer.getDoctor().getEmail().equals(principal.getName())) { + if (answer.getDoctor().getEmail().equals(principal.getName())) { return false; } @@ -27,10 +27,11 @@ public boolean answerAuthorized(Long answerId, Principal principal) { } public Answer findAnswerByAnswerId(Long answerId) { - return answerRepository.findById(answerId).orElseThrow(() -> new IllegalArgumentException("답변이 존재하지 않습니다.")); + return answerRepository.findById(answerId) + .orElseThrow(() -> new IllegalArgumentException("답변이 존재하지 않습니다.")); } - public AnswerResponseDto findById(Long answerId){ + public AnswerResponseDto findById(Long answerId) { Answer entity = findAnswerByAnswerId(answerId); return new AnswerResponseDto(entity); diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagService.java index b10a4d58..7c32c33f 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/HashtagService.java @@ -23,8 +23,8 @@ public Optional findByTagName(String tagName) { public Hashtag save(String tagName) { return hashtagRepository.save( - Hashtag.builder() - .tagName(tagName) - .build()); + Hashtag.builder() + .tagName(tagName) + .build()); } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagService.java index 17b0bc14..139f556e 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionHashtagService.java @@ -15,24 +15,25 @@ @Service public class QuestionHashtagService { - private final HashtagService hashtagService; + private final HashtagService hashtagService; - private final QuestionHashtagRepository questionHashtagRepository; - public void saveHashtag(Question question, List tagNames) { - tagNames.stream() - .map(hashtag -> - hashtagService.findByTagName(hashtag) - .orElseGet(() -> hashtagService.save(hashtag))) - .forEach(hashtag -> mappedHashtagToQuestion(question, hashtag)); - } + private final QuestionHashtagRepository questionHashtagRepository; - private Long mappedHashtagToQuestion(Question question, Hashtag hashtag) { + public void saveHashtag(Question question, List tagNames) { + tagNames.stream() + .map(hashtag -> + hashtagService.findByTagName(hashtag) + .orElseGet(() -> hashtagService.save(hashtag))) + .forEach(hashtag -> mappedHashtagToQuestion(question, hashtag)); + } - return questionHashtagRepository.save(QuestionHashtag.of(question, hashtag)).getId(); - } + private Long mappedHashtagToQuestion(Question question, Hashtag hashtag) { - public List findHashtagListByQuestion(Question question) { + return questionHashtagRepository.save(QuestionHashtag.of(question, hashtag)).getId(); + } - return questionHashtagRepository.findAllByQuestion(question); - } + public List findHashtagListByQuestion(Question question) { + + return questionHashtagRepository.findAllByQuestion(question); + } } diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryService.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryService.java index 3eca93bf..78dd11d6 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryService.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/service/QuestionQueryService.java @@ -17,6 +17,7 @@ import java.security.Principal; import java.util.ArrayList; import java.util.List; + /* TODO : SERVICE단에서 QUESTION ENTITY를 직접 반환하지 않고 DTO를 반환하는 것이 요청에 대한 응답만 함으로써 @@ -36,15 +37,15 @@ public class QuestionQueryService { private final QuestionLikeRepository questionLikeRepository; - public QuestionResponseDto findById(Long id){ + public QuestionResponseDto findById(Long id) { Question entity = findQuestionByQuestionId(id); return new QuestionResponseDto(entity); } - public boolean questionAuthorized(Long id, Principal principal){ + public boolean questionAuthorized(Long id, Principal principal) { Question question = findQuestionByQuestionId(id); - if(question.getMember().getEmail().equals(principal.getName())) { + if (question.getMember().getEmail().equals(principal.getName())) { return false; } @@ -56,7 +57,8 @@ public boolean findLike(Long id, UserInfo user) { } public Question findQuestionByQuestionId(Long id) { - return questionRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("질문이 존재하지 않습니다.")); + return questionRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("질문이 존재하지 않습니다.")); } public Page findAll(int page, String type, String kw) { From 7e23ed2cf87aa6b06e1bcd33e70b092449b8f334 Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Fri, 7 Apr 2023 06:46:55 +0900 Subject: [PATCH 16/21] =?UTF-8?q?design=20:=20=ED=95=B4=EC=8B=9C=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=9E=85=EB=A0=A5=20=EB=B0=A9=EC=8B=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../doctorqna/doctorQnaQuestionForm.html | 69 ++++++++++--------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html b/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html index c6c5c950..5b6c3397 100644 --- a/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html +++ b/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html @@ -20,43 +20,44 @@
질문등록
- -
    -
  • -
+
+ +
+ +
- - - - - - + + hashtagsInput.addEventListener("keydown", (event) => { + if (event.keyCode === 13) { + event.preventDefault(); + const tag = hashtagsInput.value.trim(); + if (tag) { + addHashtag(tag); + hashtagsInput.value = ""; + } + } + }); + From ab5db396728cfe8a53d797ce5dc82a82ab45a413 Mon Sep 17 00:00:00 2001 From: Shin-sangwon Date: Fri, 7 Apr 2023 07:00:15 +0900 Subject: [PATCH 17/21] =?UTF-8?q?feat=20:=20view=EC=97=90=EC=84=9C=20hasht?= =?UTF-8?q?ag=20=EC=82=AD=EC=A0=9C=20=EA=B0=80=EB=8A=A5=ED=95=98=EA=B2=8C?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/QuestionController.java | 1 + .../doctorqna/doctorQnaQuestionForm.html | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java index 70b32be2..8a18244c 100644 --- a/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java +++ b/src/main/java/com/codelion/animalcare/domain/doctorqna/controller/QuestionController.java @@ -104,6 +104,7 @@ public String findById(Model model, @PathVariable Long id, } model.addAttribute("like", like); + List hashtags = questionHashtagService.findHashtagListByQuestion( questionQueryService.findQuestionByQuestionId(id)); model.addAttribute("hashtags", hashtags); diff --git a/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html b/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html index 5b6c3397..4b1940d8 100644 --- a/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html +++ b/src/main/resources/templates/doctorqna/doctorQnaQuestionForm.html @@ -21,11 +21,12 @@
질문등록
- +
+