Skip to content

[refactor/#91] 문항세트 조회 api 리팩토링 #93

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

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.moplus.moplus_server.domain.problemset.repository;

import static com.moplus.moplus_server.admin.publish.domain.QPublish.*;
import static com.moplus.moplus_server.domain.concept.domain.QConceptTag.*;
import static com.moplus.moplus_server.domain.problem.domain.problem.QProblem.*;
import static com.moplus.moplus_server.domain.problem.domain.childProblem.QChildProblem.*;

import com.moplus.moplus_server.admin.problemset.dto.response.ProblemSetGetResponse;
import com.moplus.moplus_server.admin.problemset.dto.response.ProblemSummaryResponse;
import com.moplus.moplus_server.domain.problemset.domain.ProblemSet;
import com.querydsl.core.Tuple;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.time.LocalDate;
import java.util.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class ProblemSetGetRepositoryCustom {

private final JPAQueryFactory queryFactory;

public ProblemSetGetResponse getProblemSet(ProblemSet problemSet) {
// 발행 날짜 조회 쿼리
List<LocalDate> publishedDates = queryFactory
.select(publish.publishedDate)
.from(publish)
.where(publish.problemSetId.eq(problemSet.getId()))
.fetch();

// 문항 및 새끼문항 조회
List<Tuple> problemData = queryFactory
.select(
problem.id,
problem.problemCustomId.id,
problem.title.title,
problem.memo,
problem.mainProblemImageUrl
)
Copy link
Contributor

Choose a reason for hiding this comment

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

JPA는 select 문에 조회하고 싶은 엔티티를 넣어야지 영속성 컨텍스트에 반영해요
fetch join을 해서 영속성 컨텍스트에 넣어주거나 select 문에 엔티티를 선언해주어야해요

.from(problem)
.leftJoin(problem.childProblems, childProblem) // 자식 문제 JOIN
.where(problem.id.in(problemSet.getProblemIds()))
.distinct()
.fetch();

// 문항 및 새끼 문항의 개념 태그 조회
List<Long> allProblemIds = problemSet.getProblemIds(); // 문제 ID 목록
List<Long> childProblemIds = queryFactory
Copy link
Contributor

Choose a reason for hiding this comment

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

위에서 problem과 childproblem을 fetch join해와서 한번더 조회를 하지않아도 될것 같아요

.select(childProblem.id)
.from(problem)
.join(problem.childProblems, childProblem)
.where(problem.id.in(allProblemIds))
.fetch();

Set<Long> allProblemAndChildProblemIds = new HashSet<>(allProblemIds);
allProblemAndChildProblemIds.addAll(childProblemIds); // 문제 + 자식 문제 ID 합침
Copy link
Contributor

Choose a reason for hiding this comment

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

문항과 새끼문항의 아이디를 set으로 합치는 이유는 어떤걸가요?


// 문항 및 자식 문항의 개념 태그 조회
Map<Long, Set<String>> conceptTagMap = queryFactory
.select(problem.id, conceptTag.name)
.from(problem)
.leftJoin(conceptTag)
.on(conceptTag.id.in(problem.conceptTagIds))
.where(problem.id.in(allProblemAndChildProblemIds)) // 문제 + 자식 문제 ID
Copy link
Contributor

Choose a reason for hiding this comment

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

문항 테이블에서 새끼 문항 id로 검색하는것 같은데요?

.fetch()
.stream()
.collect(
HashMap::new,
(map, tuple) -> map
.computeIfAbsent(tuple.get(problem.id), k -> new HashSet<>())
.add(tuple.get(conceptTag.name)),
HashMap::putAll
);

List<ProblemSummaryResponse> problemSummaries = problemData.stream()
.map(tuple -> ProblemSummaryResponse.builder()
.problemId(tuple.get(problem.id))
.problemCustomId(tuple.get(problem.problemCustomId.id))
.problemTitle(tuple.get(problem.title.title))
.memo(tuple.get(problem.memo))
.mainProblemImageUrl(tuple.get(problem.mainProblemImageUrl))
.tagNames(conceptTagMap.getOrDefault(tuple.get(problem.id), new HashSet<>())) // 태그 매핑
.build()
Copy link
Contributor

Choose a reason for hiding this comment

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

DTO에 대한 정보가 repository까지 내려오는거는 유지보수 관점에서 좋지 않은것 같아요
DTO가 바뀌면 repository까지 수정하러 내려와야할 것 같네요
mapper로직을 넣어주거나 DTO 자체를 repository가 모르게 구성하는건 어떨까요?

)
.toList();

return ProblemSetGetResponse.of(problemSet, publishedDates, problemSummaries);
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
package com.moplus.moplus_server.domain.problemset.service;

import com.moplus.moplus_server.domain.concept.domain.ConceptTag;
import com.moplus.moplus_server.domain.concept.repository.ConceptTagRepository;
import com.moplus.moplus_server.domain.problem.domain.problem.Problem;
import com.moplus.moplus_server.domain.problem.repository.ProblemRepository;
import com.moplus.moplus_server.domain.problemset.domain.ProblemSet;
import com.moplus.moplus_server.admin.problemset.dto.response.ProblemSetGetResponse;
import com.moplus.moplus_server.admin.problemset.dto.response.ProblemSummaryResponse;
import com.moplus.moplus_server.domain.problemset.repository.ProblemSetGetRepositoryCustom;
import com.moplus.moplus_server.domain.problemset.repository.ProblemSetRepository;
import com.moplus.moplus_server.admin.publish.domain.Publish;
import com.moplus.moplus_server.domain.publish.repository.PublishRepository;
import com.moplus.moplus_server.global.error.exception.BusinessException;
import com.moplus.moplus_server.global.error.exception.ErrorCode;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -26,33 +15,14 @@
public class ProblemSetGetService {

private final ProblemSetRepository problemSetRepository;
private final ProblemRepository problemRepository;
private final ConceptTagRepository conceptTagRepository;
private final PublishRepository publishRepository;
private final ProblemSetGetRepositoryCustom problemSetGetRepositoryCustom;

@Transactional(readOnly = true)
public ProblemSetGetResponse getProblemSet(Long problemSetId) {
ProblemSet problemSet = problemSetRepository.findByIdElseThrow(problemSetId);
if (problemSet.isDeleted()) {
throw new BusinessException(ErrorCode.DELETE_PROBLEM_SET_GET_ERROR);
Copy link
Contributor

Choose a reason for hiding this comment

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

명시적인 예외 클래스로 만들어주실 수 있을까요?
DeletedException 이라던지

}
List<LocalDate> publishedDates = publishRepository.findByProblemSetId(problemSetId).stream()
.map(Publish::getPublishedDate)
.toList();

List<ProblemSummaryResponse> problemSummaries = new ArrayList<>();
for (Long problemId : problemSet.getProblemIds()) {
Problem problem = problemRepository.findByIdElseThrow(problemId);
Set<String> tagNames = new HashSet<>(
conceptTagRepository.findAllByIdsElseThrow(problem.getConceptTagIds())
.stream()
.map(ConceptTag::getName)
.toList());
problem.getChildProblems().stream()
.map(childProblem -> conceptTagRepository.findAllByIdsElseThrow(childProblem.getConceptTagIds()))
.forEach(conceptTags -> tagNames.addAll(conceptTags.stream().map(ConceptTag::getName).toList()));
problemSummaries.add(ProblemSummaryResponse.of(problem, tagNames));
}
return ProblemSetGetResponse.of(problemSet, publishedDates, problemSummaries);
return problemSetGetRepositoryCustom.getProblemSet(problemSet);
}
}