Skip to content
Merged
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
Expand Up @@ -22,7 +22,7 @@ public class ProblemSetSearchController {
@GetMapping("/search")
@Operation(
summary = "문항세트 검색",
description = "문항세트 타이틀, 문항세트 내 포함된 개념태그, 문항세트 내 포함된 문항 타이틀로 검색합니다."
description = "문항세트 타이틀, 문항세트 내 포함된 개념태그, 문항세트 내 포함된 문항 타이틀로 검색합니다. 발행상태는 발행이면 CONFIRMED, 아니면 NOT_CONFIRMED 입니다."
Copy link
Contributor

Choose a reason for hiding this comment

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

발행 쪽의 문항세트 검색에는 컨펌안된 애들은 안뜨는 api가 추가로 필요할것 같아요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

추가하겠습니다!

)
public ResponseEntity<List<ProblemSetSearchGetResponse>> search(
@RequestParam(value = "problemSetTitle", required = false) String problemSetTitle,
Expand All @@ -32,4 +32,18 @@ public ResponseEntity<List<ProblemSetSearchGetResponse>> search(
List<ProblemSetSearchGetResponse> problemSets = problemSetSearchRepository.search(problemSetTitle, problemTitle, conceptTagNames);
return ResponseEntity.ok(problemSets);
}

@GetMapping("/confirm/search")
@Operation(
summary = "발행용 문항세트 검색",
description = "문항세트 타이틀, 문항세트 내 포함된 개념태그, 문항세트 내 포함된 문항 타이틀로 검색합니다. 발행상태가 CONFIRMED 문항세트만 조회됩니다.."
)
public ResponseEntity<List<ProblemSetSearchGetResponse>> confirmSearch(
@RequestParam(value = "problemSetTitle", required = false) String problemSetTitle,
@RequestParam(value = "problemTitle", required = false) String problemTitle,
@RequestParam(value = "conceptTagNames", required = false) List<String> conceptTagNames
) {
List<ProblemSetSearchGetResponse> problemSets = problemSetSearchRepository.confirmSearch(problemSetTitle, problemTitle, conceptTagNames);
return ResponseEntity.ok(problemSets);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,14 @@ public void deleteProblemSet() {

public void toggleConfirm(List<Problem> problems) {
if(this.confirmStatus == ProblemSetConfirmStatus.NOT_CONFIRMED){
// 문항 유효성 검사
for (Problem problem : problems) {
if (!problem.isValid()) {
throw new InvalidValueException(ErrorCode.INVALID_CONFIRM_PROBLEM);
}
List<String> invalidProblemIds = problems.stream()
.filter(problem -> !problem.isValid())
.map(problem -> problem.getId().getId())
.toList();
if (!invalidProblemIds.isEmpty()) {
String message = ErrorCode.INVALID_CONFIRM_PROBLEM.getMessage() +
String.join("번 ", invalidProblemIds) + "번";
throw new InvalidValueException(message, ErrorCode.INVALID_CONFIRM_PROBLEM);
}
}
this.confirmStatus = this.confirmStatus.toggle();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.moplus.moplus_server.domain.problemset.domain.ProblemSet;
import com.moplus.moplus_server.domain.problemset.domain.ProblemSetConfirmStatus;
import java.time.LocalDate;
import java.util.List;
import lombok.Builder;

Expand All @@ -10,14 +11,16 @@ public record ProblemSetGetResponse(
Long id,
String title,
ProblemSetConfirmStatus confirmStatus,
LocalDate publishedDate,
List<ProblemSummaryResponse> problemSummaries
) {
public static ProblemSetGetResponse of(ProblemSet problemSet, List<ProblemSummaryResponse> problemSummaries) {
public static ProblemSetGetResponse of(ProblemSet problemSet, LocalDate publishedDate, List<ProblemSummaryResponse> problemSummaries) {

return ProblemSetGetResponse.builder()
.id(problemSet.getId())
.title(problemSet.getTitle().getValue())
.confirmStatus(problemSet.getConfirmStatus())
.publishedDate(publishedDate)
.problemSummaries(problemSummaries)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.moplus.moplus_server.domain.problemset.dto.response;

import com.moplus.moplus_server.domain.problemset.domain.ProblemSetConfirmStatus;
import java.time.LocalDate;
import java.util.List;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -8,12 +10,16 @@
@NoArgsConstructor
public class ProblemSetSearchGetResponse {
private String problemSetTitle;
private ProblemSetConfirmStatus confirmStatus;
private LocalDate publishedDate;
private List<ProblemThumbnailResponse> problemThumbnailResponses;

public ProblemSetSearchGetResponse(
String problemSetTitle, List<ProblemThumbnailResponse> problemThumbnailResponses
String problemSetTitle, ProblemSetConfirmStatus confirmStatus, LocalDate publishedDate, List<ProblemThumbnailResponse> problemThumbnailResponses
) {
this.problemSetTitle = problemSetTitle;
this.confirmStatus = confirmStatus;
this.publishedDate = publishedDate;
this.problemThumbnailResponses = problemThumbnailResponses;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.moplus.moplus_server.domain.problemset.repository;

import com.moplus.moplus_server.domain.problemset.domain.ProblemSet;
import com.moplus.moplus_server.domain.problemset.domain.ProblemSetConfirmStatus;
import com.moplus.moplus_server.global.error.exception.ErrorCode;
import com.moplus.moplus_server.global.error.exception.NotFoundException;
import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -11,4 +12,11 @@ default ProblemSet findByIdElseThrow(Long problemSetId) {
return findById(problemSetId).orElseThrow(() -> new NotFoundException(ErrorCode.PROBLEM_SET_NOT_FOUND));
}

default void existsConfirmedActiveByIdElseThrow(Long problemSetId) {
if (!existsByIdAndIsDeletedFalseAndConfirmStatus(problemSetId, ProblemSetConfirmStatus.CONFIRMED)) {
throw new NotFoundException(ErrorCode.PROBLEM_SET_NOT_FOUND);
}
}

boolean existsByIdAndIsDeletedFalseAndConfirmStatus(Long problemSetId, ProblemSetConfirmStatus confirmStatus);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import static com.moplus.moplus_server.domain.concept.domain.QConceptTag.conceptTag;
import static com.moplus.moplus_server.domain.problem.domain.problem.QProblem.problem;
import static com.moplus.moplus_server.domain.problemset.domain.ProblemSetConfirmStatus.CONFIRMED;
import static com.moplus.moplus_server.domain.problemset.domain.QProblemSet.problemSet;
import static com.moplus.moplus_server.domain.publish.domain.QPublish.publish;

import com.moplus.moplus_server.domain.problemset.dto.response.ProblemSetSearchGetResponse;
import com.moplus.moplus_server.domain.problemset.dto.response.ProblemThumbnailResponse;
Expand All @@ -25,6 +27,7 @@ public List<ProblemSetSearchGetResponse> search(String problemSetTitle, String p
.from(problemSet)
.leftJoin(problem).on(problem.id.in(problemSet.problemIds)) // 문제 세트 내 포함된 문항과 조인
.leftJoin(conceptTag).on(conceptTag.id.in(problem.conceptTagIds)) // 문제의 개념 태그 조인
.leftJoin(publish).on(publish.problemSetId.eq(problemSet.id)) // 문제 세트와 발행 데이터 조인
.where(
containsProblemSetTitle(problemSetTitle),
containsProblemTitle(problemTitle),
Expand All @@ -34,6 +37,35 @@ public List<ProblemSetSearchGetResponse> search(String problemSetTitle, String p
.transform(GroupBy.groupBy(problemSet.id).list(
Projections.constructor(ProblemSetSearchGetResponse.class,
problemSet.title.value,
problemSet.confirmStatus,
publish.publishedDate, // 발행되지 않은 경우 null 반환
GroupBy.list(
Projections.constructor(ProblemThumbnailResponse.class,
problem.mainProblemImageUrl
)
)
)
));
}

public List<ProblemSetSearchGetResponse> confirmSearch(String problemSetTitle, String problemTitle, List<String> conceptTagNames) {
return queryFactory
.from(problemSet)
.leftJoin(problem).on(problem.id.in(problemSet.problemIds)) // 문제 세트 내 포함된 문항과 조인
.leftJoin(conceptTag).on(conceptTag.id.in(problem.conceptTagIds)) // 문제의 개념 태그 조인
.leftJoin(publish).on(publish.problemSetId.eq(problemSet.id)) // 문제 세트와 발행 데이터 조인
.where(
problemSet.confirmStatus.eq(CONFIRMED),
containsProblemSetTitle(problemSetTitle),
containsProblemTitle(problemTitle),
containsConceptTagNames(conceptTagNames)
)
.distinct()
.transform(GroupBy.groupBy(problemSet.id).list(
Projections.constructor(ProblemSetSearchGetResponse.class,
problemSet.title.value,
problemSet.confirmStatus,
publish.publishedDate, // 발행되지 않은 경우 null 반환
GroupBy.list(
Projections.constructor(ProblemThumbnailResponse.class,
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.

문항 타이틀이랑, 문항 메모도 필요하지 않나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

다지인 변경을 반영안했네요! 바로 반영하겠습니다

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
import com.moplus.moplus_server.domain.problemset.dto.response.ProblemSetGetResponse;
import com.moplus.moplus_server.domain.problemset.dto.response.ProblemSummaryResponse;
import com.moplus.moplus_server.domain.problemset.repository.ProblemSetRepository;
import com.moplus.moplus_server.domain.publish.domain.Publish;
import com.moplus.moplus_server.domain.publish.repository.PublishRepository;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
Expand All @@ -25,11 +28,15 @@ public class ProblemSetGetService {
private final ProblemRepository problemRepository;
private final PracticeTestTagRepository practiceTestTagRepository;
private final ConceptTagRepository conceptTagRepository;
private final PublishRepository publishRepository;

@Transactional(readOnly = true)
public ProblemSetGetResponse getProblemSet(Long problemSetId) {

ProblemSet problemSet = problemSetRepository.findByIdElseThrow(problemSetId);
LocalDate publishedDate = publishRepository.findByProblemSetId(problemSetId)
.map(Publish::getPublishedDate)
.orElse(null);

List<ProblemSummaryResponse> problemSummaries = new ArrayList<>();
for (ProblemId problemId : problemSet.getProblemIds()) {
Expand All @@ -41,6 +48,6 @@ public ProblemSetGetResponse getProblemSet(Long problemSetId) {
.toList();
problemSummaries.add(ProblemSummaryResponse.of(problem, practiceTestTag.getName(), tagNames));
}
return ProblemSetGetResponse.of(problemSet, problemSummaries);
return ProblemSetGetResponse.of(problemSet, publishedDate, problemSummaries);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.moplus.moplus_server.domain.publish.controller;

import com.moplus.moplus_server.domain.publish.dto.request.PublishPostRequest;
import com.moplus.moplus_server.domain.publish.dto.response.PublishMonthGetResponse;
import com.moplus.moplus_server.domain.publish.service.PublishDeleteService;
import com.moplus.moplus_server.domain.publish.service.PublishGetService;
import com.moplus.moplus_server.domain.publish.service.PublishSaveService;
import io.swagger.v3.oas.annotations.Operation;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1/publish")
@RequiredArgsConstructor
public class PublishController {

private final PublishGetService publishGetService;
private final PublishSaveService publishSaveService;
private final PublishDeleteService publishDeleteService;

@GetMapping("/{year}/{month}")
@Operation(summary = "연월별 발행 조회", description = "연월별로 발행된 세트들을 조회합니다.")
public ResponseEntity<List<PublishMonthGetResponse>> getPublishMonth(
@PathVariable int year,
@PathVariable int month
) {
return ResponseEntity.ok(publishGetService.getPublishMonth(year, month));
}

@PostMapping("")
@Operation(summary = "발행 생성하기", description = "특정 날짜에 문항세트를 발행합니다.")
public ResponseEntity<Long> postPublish(
@RequestBody PublishPostRequest request
) {
return ResponseEntity.ok(publishSaveService.createPublish(request));
}

@DeleteMapping("/{publishId}")
@Operation(summary = "발행 삭제", description = "발행을 삭제합니다.")
public ResponseEntity<Void> deleteProblemSet(
@PathVariable Long publishId
) {
publishDeleteService.deletePublish(publishId);
return ResponseEntity.noContent().build();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.moplus.moplus_server.domain.publish.domain;

import com.moplus.moplus_server.global.common.BaseEntity;
import com.moplus.moplus_server.global.error.exception.ErrorCode;
import com.moplus.moplus_server.global.error.exception.InvalidValueException;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
Expand Down Expand Up @@ -34,4 +36,10 @@ public Publish(LocalDate publishedDate, Long problemSetId) {
this.problemSetId = problemSetId;
}

public void validatePublishedDate() {
// 발행 시점 다음날부터 발행 가능
if (this.publishedDate.isBefore(LocalDate.now().plusDays(1))) {
throw new InvalidValueException(ErrorCode.INVALID_DATE_ERROR);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.moplus.moplus_server.domain.publish.dto.request;

import com.moplus.moplus_server.domain.publish.domain.Publish;
import java.time.LocalDate;

public record PublishPostRequest(
LocalDate publishedDate,
Long problemSetId
) {
public Publish toEntity() {
return Publish.builder()
.publishedDate(this.publishedDate)
.problemSetId(this.problemSetId)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.moplus.moplus_server.domain.publish.dto.response;

import lombok.Builder;

@Builder
public record PublishMonthGetResponse(
int day,
PublishProblemSetResponse problemSetInfo
) {
public static PublishMonthGetResponse of(int day, PublishProblemSetResponse problemSetInfos) {

return PublishMonthGetResponse.builder()
.day(day)
.problemSetInfo(problemSetInfos)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.moplus.moplus_server.domain.publish.dto.response;

import com.moplus.moplus_server.domain.problemset.domain.ProblemSet;
import lombok.Builder;

@Builder
public record PublishProblemSetResponse(
Long id,
String title
) {
public static PublishProblemSetResponse of(ProblemSet problemSet) {

return PublishProblemSetResponse.builder()
.id(problemSet.getId())
.title(problemSet.getTitle().getValue())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.moplus.moplus_server.domain.publish.repository;

import com.moplus.moplus_server.domain.publish.domain.Publish;
import com.moplus.moplus_server.global.error.exception.ErrorCode;
import com.moplus.moplus_server.global.error.exception.NotFoundException;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface PublishRepository extends JpaRepository<Publish, Long> {
List<Publish> findByPublishedDateBetween(LocalDate startDate, LocalDate endDate);

default Publish findByIdElseThrow(Long publishId) {
return findById(publishId).orElseThrow(() -> new NotFoundException(ErrorCode.PUBLISH_NOT_FOUND));
}

Optional<Publish> findByProblemSetId(Long problemSetId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.moplus.moplus_server.domain.publish.service;

import com.moplus.moplus_server.domain.publish.domain.Publish;
import com.moplus.moplus_server.domain.publish.repository.PublishRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class PublishDeleteService {

private final PublishRepository publishRepository;

@Transactional
public void deletePublish(Long publishId) {
Publish publish = publishRepository.findByIdElseThrow(publishId);
publishRepository.delete(publish);
}
}
Loading