Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE] 체크리스트 카테고리별 질문 비교 기능을 구현한다. #963

Open
wants to merge 9 commits into
base: dev-be
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ private static RoomRequest createDefaultRoomRequest() {
return new RoomRequest(
"예시용 체크리스트", "방끗시 집잘구하구 행복하동", "방방하우스", "잠실", 10,
3000, 60, 5, List.of(1, 3), "지상", 14, "분리형 원룸", 9.5,
37.5153, 127.1030,
12, 9, "초", "방끗공인중개사",
"이곳에 필요한 메모를 작성하세요.", "이곳에 한줄평을 남겨 보세요.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum ExceptionCode {
OPTION_DUPLICATED(HttpStatus.BAD_REQUEST, ClientExceptionCode.CHECKLIST_ERROR, "중복된 옵션이 존재합니다."),

// Question
CATEGORY_NOT_FOUND(HttpStatus.BAD_REQUEST, ClientExceptionCode.QUESTION_ERROR, "카테고리 ID가 존재하지 않습니다"),
QUESTION_ID_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, ClientExceptionCode.QUESTION_ERROR, "중복된 질문 ID가 존재해 질문을 생성할 수 없습니다."),
QUESTION_HIGHLIGHT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, ClientExceptionCode.CHECKLIST_SERVER_ERROR, "잘못된 하이라이트 키워드가 존재해 질문을 생성할 수 없습니다."),
QUESTION_INVALID(HttpStatus.BAD_REQUEST, ClientExceptionCode.QUESTION_ERROR, "잘못된 질문 ID입니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
import com.bang_ggood.auth.config.UserPrincipal;
import com.bang_ggood.question.dto.request.CustomChecklistUpdateRequest;
import com.bang_ggood.question.dto.response.CategoryCustomChecklistQuestionsResponse;
import com.bang_ggood.question.dto.response.ComparisonCategoryChecklistQuestionsResponse;
import com.bang_ggood.question.dto.response.CustomChecklistQuestionsResponse;
import com.bang_ggood.question.service.QuestionManageService;
import com.bang_ggood.user.domain.User;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -34,6 +36,13 @@ public ResponseEntity<CategoryCustomChecklistQuestionsResponse> readAllCustomChe
return ResponseEntity.ok(questionManageService.readAllCustomChecklistQuestions(user));
}

@GetMapping("/v1/comparison/checklists/{checklistId}/categories/{categoryId}/questions")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

queryString으로 설계하지 않고 계층적으로 설계하신 이유가 궁금해요!
계층적 설계가 들어가서 checklists가 먼저 나오니 QuestionController에 위치한 게 어색하다는 생각도 들어서요~

public ResponseEntity<ComparisonCategoryChecklistQuestionsResponse> readComparisonChecklistQuestionsByCategory(
@AuthRequiredPrincipal User user,
@PathVariable("checklistId") long checklistId, @PathVariable("categoryId") int categoryId) {
return ResponseEntity.ok(questionManageService.readComparisonChecklistQuestionsByCategory(user, checklistId, categoryId));
}

@PutMapping("/custom-checklist")
public ResponseEntity<Void> updateCustomChecklist(@AuthRequiredPrincipal User user,
@RequestBody CustomChecklistUpdateRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ public boolean isCategory(Category category) {
return question.getCategory().equals(category);
}

public boolean hasAnswer(Answer answer) {
return this.answer.equals(answer);
}
Comment on lines +65 to +67
Copy link
Contributor

@tkdgur0906 tkdgur0906 Nov 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasAnswer라는 메서드 명이 그냥 Answer를 가지고 있는지 여부를 반환하는 의미로 해석될 수도 있을 것 같아요!
matchesAnswer나 다른 함수명을 고려해보는 것은 어떤가요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

matchAnswer로 변경했습니다!


@Override
public boolean equals(Object o) {
if (this == o) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.bang_ggood.question.domain;

import java.util.List;

public class ChecklistQuestions {

private final List<ChecklistQuestion> questions;

public ChecklistQuestions(List<ChecklistQuestion> questions) {
this.questions = questions;
}

public List<ChecklistQuestion> filterByAnswer(Answer answer) {
return questions.stream()
.filter(question -> question.hasAnswer(answer))
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.bang_ggood.question.dto.response;

import java.util.List;

public record ComparisonCategoryChecklistQuestionResponse(List<QuestionResponse> good,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dto이름이 한눈에 와닿지 않는 것 같은데, 가지고 있는 데이터를 이름에 담는 방향은 어떻게 생각하시나요?

AnswerCategorizedQuestionsResponse 이런 느낌으로 생각해봤는데 정확하게 어떤게 좋을지는 잘 모르겠네요🤔

List<QuestionResponse> bad,
List<QuestionResponse> none) {
public static ComparisonCategoryChecklistQuestionResponse of(List<QuestionResponse> good,
List<QuestionResponse> bad,
List<QuestionResponse> none) {
return new ComparisonCategoryChecklistQuestionResponse(good, bad, none);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.bang_ggood.question.dto.response;

import java.util.List;

public record ComparisonCategoryChecklistQuestionsResponse(ComparisonCategoryChecklistQuestionResponse questions) {

public static ComparisonCategoryChecklistQuestionsResponse of(List<QuestionResponse> good,
List<QuestionResponse> bad,
List<QuestionResponse> none){
return new ComparisonCategoryChecklistQuestionsResponse(new ComparisonCategoryChecklistQuestionResponse(good, bad, none));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.bang_ggood.question.repository;

import com.bang_ggood.global.exception.BangggoodException;
import com.bang_ggood.global.exception.ExceptionCode;
import com.bang_ggood.question.domain.Category;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
Expand All @@ -14,4 +16,8 @@ public interface CategoryRepository extends JpaRepository<Category, Integer> {
+ "WHERE ccq.user_id = :userId AND ccq.deleted = false ",
nativeQuery = true)
List<Category> findAllCustomQuestionCategoriesByUserId(@Param("userId") Long userId);

default Category getById(Integer categoryId){
return findById(categoryId).orElseThrow(() -> new BangggoodException(ExceptionCode.CATEGORY_NOT_FOUND));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ public interface ChecklistQuestionRepository extends JpaRepository<ChecklistQues
+ "AND cq.deleted = false")
List<ChecklistQuestion> findAllByChecklistId(@Param("checklistId") Long checklistId);

@Query("SELECT cq FROM ChecklistQuestion cq "
+ "JOIN FETCH cq.question "
+ "WHERE cq.checklist.id = :checklistId "
+ "AND cq.question.category.id = :categoryId "
+ "AND cq.deleted = false")
List<ChecklistQuestion> findAllByChecklistIdAndCategoryId(@Param("checklistId") Long checklistId, @Param("categoryId") Integer categoryId);

@Modifying(flushAutomatically = true, clearAutomatically = true)
@Transactional
@Query("UPDATE ChecklistQuestion cq "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ public List<CustomChecklistQuestion> readCustomChecklistQuestions(User user) {
return customChecklistQuestionRepository.findAllByUser(user);
}

@Transactional(readOnly = true)
public List<ChecklistQuestion> readChecklistQuestionsByCategory(Checklist checklist, Category category) {
return checklistQuestionRepository.findAllByChecklistIdAndCategoryId(checklist.getId(), category.getId());
}

@Transactional
public void updateCustomChecklist(User user, List<Question> questions) {
validateCustomChecklistQuestionsIsNotEmpty(questions);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.bang_ggood.question.service;

import com.bang_ggood.checklist.domain.Checklist;
import com.bang_ggood.checklist.service.ChecklistService;
import com.bang_ggood.question.domain.Answer;
import com.bang_ggood.question.domain.Category;
import com.bang_ggood.question.domain.ChecklistQuestions;
import com.bang_ggood.question.domain.CustomChecklistQuestion;
import com.bang_ggood.question.domain.Question;
import com.bang_ggood.question.dto.request.CustomChecklistUpdateRequest;
import com.bang_ggood.question.dto.response.CategoryCustomChecklistQuestionResponse;
import com.bang_ggood.question.dto.response.CategoryCustomChecklistQuestionsResponse;
import com.bang_ggood.question.dto.response.CategoryQuestionsResponse;
import com.bang_ggood.question.dto.response.ComparisonCategoryChecklistQuestionsResponse;
import com.bang_ggood.question.dto.response.CustomChecklistQuestionResponse;
import com.bang_ggood.question.dto.response.CustomChecklistQuestionsResponse;
import com.bang_ggood.question.dto.response.QuestionResponse;
Expand All @@ -21,6 +26,7 @@
@Service
public class QuestionManageService {

private final ChecklistService checklistService;
private final ChecklistQuestionService checklistQuestionService;
private final QuestionService questionService;

Expand Down Expand Up @@ -85,6 +91,26 @@ private CategoryCustomChecklistQuestionsResponse categorizeAllQuestionsWithSelec
return CategoryCustomChecklistQuestionsResponse.from(response);
}

@Transactional(readOnly = true)
public ComparisonCategoryChecklistQuestionsResponse readComparisonChecklistQuestionsByCategory(User user, Long checklistId, Integer categoryId) {
Checklist checklist = checklistService.readChecklist(user, checklistId);
Category category = questionService.readCategory(categoryId);
ChecklistQuestions checklistQuestions = new ChecklistQuestions(checklistQuestionService.readChecklistQuestionsByCategory(checklist, category));

List<QuestionResponse> good = categorizeQuestionsByAnswer(checklistQuestions, Answer.GOOD);
List<QuestionResponse> bad = categorizeQuestionsByAnswer(checklistQuestions, Answer.BAD);
List<QuestionResponse> none = categorizeQuestionsByAnswer(checklistQuestions, Answer.NONE);

return ComparisonCategoryChecklistQuestionsResponse.of(good, bad, none);
}

private List<QuestionResponse> categorizeQuestionsByAnswer(ChecklistQuestions checklistQuestions, Answer answer) {
return checklistQuestions.filterByAnswer(answer)
.stream()
.map(checklistQuestion -> new QuestionResponse(checklistQuestion.getQuestion(), questionService.readHighlights(checklistQuestion.getQuestionId())))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Highlight가 비교 페이지에서 꼭 필요하지 않다면 가져오지 않는 방향으로 가는 게 더 좋아보이긴 합니다!

.toList();
}

@Transactional
public void updateCustomChecklist(User user, CustomChecklistUpdateRequest request) {
List<Question> questions = questionService.readAllQuestionByIds(request.questionIds());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ public List<Question> findDefaultQuestions() {
return questionRepository.findAllByIsDefaultTrue();
}

@Transactional(readOnly = true)
public Category readCategory(Integer categoryId) {
return categoryRepository.getById(categoryId);
}

@Transactional(readOnly = true)
public Question readQuestion(Integer questionId) {
return questionRepository.getById(questionId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@ public class Room extends BaseEntity {

private Double size;

private Double latitude;

private Double longitude;

public Room(String name, String address, String buildingName, String station, Integer walkingTime,
FloorLevel floorLevel, Integer floor, Structure structure, Double size) {
FloorLevel floorLevel, Integer floor, Structure structure, Double size, Double latitude, Double longitude) {
this.name = name;
this.address = address;
this.buildingName = buildingName;
Expand All @@ -55,6 +59,8 @@ public Room(String name, String address, String buildingName, String station, In
this.floor = floor;
this.structure = structure;
this.size = size;
this.latitude = latitude;
this.longitude = longitude;
validateFloorAndLevel();
}

Expand All @@ -68,6 +74,8 @@ public void change(Room room) {
this.floor = room.floor;
this.structure = room.structure;
this.size = room.size;
this.latitude = room.latitude;
this.longitude = room.longitude;
validateFloorAndLevel();
}

Expand Down Expand Up @@ -107,6 +115,8 @@ public String toString() {
", floor=" + floor +
", structure=" + structure +
", size=" + size +
", latitude=" + latitude +
", longitude=" + longitude +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ public record RoomRequest(@NotBlank(message = "방 이름이 존재하지 않습
String address, String buildingName, String station,
Integer walkingTime, Integer deposit, Integer rent, Integer maintenanceFee,
List<Integer> includedMaintenances, String floorLevel, Integer floor,
String structure, Double size, Integer contractTerm, Integer occupancyMonth,
String structure, Double size, Double latitude, Double longitude, Integer contractTerm, Integer occupancyMonth,
String occupancyPeriod,
String realEstate, String memo, String summary
) {

public Room toRoomEntity() {
return new Room(roomName, address, buildingName, station, walkingTime,
FloorLevel.from(floorLevel), floor, Structure.from(structure), size);
FloorLevel.from(floorLevel), floor, Structure.from(structure), size, latitude, longitude);
}
}
3 changes: 2 additions & 1 deletion backend/bang-ggood/src/main/resources/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ CREATE TABLE room
floor INTEGER,
structure VARCHAR(255),
size DOUBLE,
latitude DOUBLE,
longitude DOUBLE,
created_at TIMESTAMP(6),
modified_at TIMESTAMP(6),
deleted BOOLEAN
Expand Down Expand Up @@ -106,7 +108,6 @@ CREATE TABLE checklist_maintenance
CREATE TABLE checklist_question
(
id BIGINT AUTO_INCREMENT PRIMARY KEY,
question VARCHAR(255) NOT NULL,
question_id INTEGER NOT NULL,
checklist_id BIGINT NOT NULL,
answer VARCHAR(255),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ void findAllByUser() {
// given
Room room1 = roomRepository.save(RoomFixture.ROOM_1());
Room room2 = roomRepository.save(RoomFixture.ROOM_2());
User user = userRepository.save(UserFixture.USER1());
User user = UserFixture.USER1;
Checklist checklist1 = checklistRepository.save(ChecklistFixture.CHECKLIST1_USER1(room1, user));
Checklist checklist2 = checklistRepository.save(ChecklistFixture.CHECKLIST2_USER1(room2, user));
checklistRepository.saveAll(List.of(checklist1, checklist2));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@

public class ChecklistQuestionFixture {

public static ChecklistQuestion CHECKLIST1_QUESTION1(Checklist checklist, Question question) {
public static ChecklistQuestion CHECKLIST1_QUESTION1_BAD(Checklist checklist, Question question) {
return new ChecklistQuestion(
checklist,
question,
Answer.BAD
);
}

public static ChecklistQuestion CHECKLIST1_QUESTION2(Checklist checklist, Question question) {
public static ChecklistQuestion CHECKLIST1_QUESTION2_GOOD(Checklist checklist, Question question) {
return new ChecklistQuestion(
checklist,
question,
Expand All @@ -41,15 +41,16 @@ public static ChecklistQuestion CHECKLIST1_QUESTION11(Checklist checklist, Quest
}

public static List<ChecklistQuestion> CHECKLIST1_QUESTIONS(Checklist checklist, Question question1, Question question2) {
return List.of(CHECKLIST1_QUESTION1(checklist, question1), CHECKLIST1_QUESTION2(checklist, question2));
return List.of(CHECKLIST1_QUESTION1_BAD(checklist, question1), CHECKLIST1_QUESTION2_GOOD(checklist, question2));
}

public static List<ChecklistQuestion> CHECKLIST1_DUPLICATE(Checklist checklist, Question question) {
return List.of(CHECKLIST1_QUESTION1(checklist, question), CHECKLIST1_QUESTION1(checklist, question));
return List.of(CHECKLIST1_QUESTION1_BAD(checklist, question), CHECKLIST1_QUESTION1_BAD(checklist, question));
}

public static List<ChecklistQuestion> CHECKLIST1_QUESTIONS_UPDATE(Checklist checklist, Question question1, Question question2) {
return List.of(CHECKLIST1_QUESTION1(checklist, question1), CHECKLIST1_QUESTION2_UPDATE_ANSWER(checklist, question2));
return List.of(
CHECKLIST1_QUESTION1_BAD(checklist, question1), CHECKLIST1_QUESTION2_UPDATE_ANSWER(checklist, question2));
}

public static List<ChecklistQuestion> CHECKLIST1_QUESTIONS_DIFFERENT_LENGTH(Checklist checklist, Question question) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ void isDifferentQuestionId_true() {
Room room = RoomFixture.ROOM_1();
User user = UserFixture.USER1();

ChecklistQuestion checklistQuestion1 = ChecklistQuestionFixture.CHECKLIST1_QUESTION1(
ChecklistQuestion checklistQuestion1 = ChecklistQuestionFixture.CHECKLIST1_QUESTION1_BAD(
ChecklistFixture.CHECKLIST1_USER1(room, user), QuestionFixture.QUESTION1_CATEGORY1);
ChecklistQuestion checklistQuestion2 = ChecklistQuestionFixture.CHECKLIST1_QUESTION2(
ChecklistQuestion checklistQuestion2 = ChecklistQuestionFixture.CHECKLIST1_QUESTION2_GOOD(
ChecklistFixture.CHECKLIST1_USER1(room, user), QuestionFixture.QUESTION2_CATEGORY1);

//when & then
Expand All @@ -37,9 +37,9 @@ void isDifferentQuestionId_false() {
//given
Room room = RoomFixture.ROOM_1();
User user = UserFixture.USER1();
ChecklistQuestion checklistQuestion = ChecklistQuestionFixture.CHECKLIST1_QUESTION1(
ChecklistQuestion checklistQuestion = ChecklistQuestionFixture.CHECKLIST1_QUESTION1_BAD(
ChecklistFixture.CHECKLIST1_USER1(room, user), QuestionFixture.QUESTION1_CATEGORY1);
ChecklistQuestion compareChecklistQuestion = ChecklistQuestionFixture.CHECKLIST1_QUESTION1(
ChecklistQuestion compareChecklistQuestion = ChecklistQuestionFixture.CHECKLIST1_QUESTION1_BAD(
ChecklistFixture.CHECKLIST1_USER1(room, user), QuestionFixture.QUESTION1_CATEGORY1);

//when & then
Expand Down
Loading
Loading