diff --git a/src/main/generated/com/moplus/moplus_server/domain/problem/domain/childProblem/QChildProblem.java b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/childProblem/QChildProblem.java index b22ac5b..193076d 100644 --- a/src/main/generated/com/moplus/moplus_server/domain/problem/domain/childProblem/QChildProblem.java +++ b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/childProblem/QChildProblem.java @@ -37,8 +37,6 @@ public class QChildProblem extends EntityPathBase { public final StringPath imageUrl = createString("imageUrl"); - public final NumberPath sequence = createNumber("sequence", Integer.class); - //inherited public final DateTimePath updatedDate = _super.updatedDate; diff --git a/src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblem.java b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblem.java index 74bade6..4850572 100644 --- a/src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblem.java +++ b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblem.java @@ -55,7 +55,7 @@ public class QProblem extends EntityPathBase { public final ListPath prescriptionImageUrls = this.createList("prescriptionImageUrls", String.class, StringPath.class, PathInits.DIRECT2); - public final QProblemAdminId problemAdminId; + public final QProblemCustomId problemCustomId; public final EnumPath problemType = createEnum("problemType", ProblemType.class); @@ -88,7 +88,7 @@ public QProblem(Class type, PathMetadata metadata, PathInits super(type, metadata, inits); this.answer = inits.isInitialized("answer") ? new com.moplus.moplus_server.domain.problem.domain.QAnswer(forProperty("answer")) : null; this.difficulty = inits.isInitialized("difficulty") ? new QDifficulty(forProperty("difficulty")) : null; - this.problemAdminId = inits.isInitialized("problemAdminId") ? new QProblemAdminId(forProperty("problemAdminId")) : null; + this.problemCustomId = inits.isInitialized("problemCustomId") ? new QProblemCustomId(forProperty("problemCustomId")) : null; this.title = inits.isInitialized("title") ? new QTitle(forProperty("title")) : null; } diff --git a/src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblemAdminId.java b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblemAdminId.java deleted file mode 100644 index d7e56ee..0000000 --- a/src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblemAdminId.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.moplus.moplus_server.domain.problem.domain.problem; - -import static com.querydsl.core.types.PathMetadataFactory.*; - -import com.querydsl.core.types.dsl.*; - -import com.querydsl.core.types.PathMetadata; -import javax.annotation.processing.Generated; -import com.querydsl.core.types.Path; - - -/** - * QProblemAdminId is a Querydsl query type for ProblemAdminId - */ -@Generated("com.querydsl.codegen.DefaultEmbeddableSerializer") -public class QProblemAdminId extends BeanPath { - - private static final long serialVersionUID = 348147768L; - - public static final QProblemAdminId problemAdminId = new QProblemAdminId("problemAdminId"); - - public final StringPath id = createString("id"); - - public QProblemAdminId(String variable) { - super(ProblemAdminId.class, forVariable(variable)); - } - - public QProblemAdminId(Path path) { - super(path.getType(), path.getMetadata()); - } - - public QProblemAdminId(PathMetadata metadata) { - super(ProblemAdminId.class, metadata); - } - -} - diff --git a/src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblemCustomId.java b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblemCustomId.java new file mode 100644 index 0000000..896d9cf --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblemCustomId.java @@ -0,0 +1,37 @@ +package com.moplus.moplus_server.domain.problem.domain.problem; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; + + +/** + * QProblemCustomId is a Querydsl query type for ProblemCustomId + */ +@Generated("com.querydsl.codegen.DefaultEmbeddableSerializer") +public class QProblemCustomId extends BeanPath { + + private static final long serialVersionUID = -517009730L; + + public static final QProblemCustomId problemCustomId = new QProblemCustomId("problemCustomId"); + + public final StringPath id = createString("id"); + + public QProblemCustomId(String variable) { + super(ProblemCustomId.class, forVariable(variable)); + } + + public QProblemCustomId(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QProblemCustomId(PathMetadata metadata) { + super(ProblemCustomId.class, metadata); + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/domain/problem/service/mapper/ChildProblemMapperImpl.java b/src/main/generated/com/moplus/moplus_server/domain/problem/service/mapper/ChildProblemMapperImpl.java index dc056d0..3909422 100644 --- a/src/main/generated/com/moplus/moplus_server/domain/problem/service/mapper/ChildProblemMapperImpl.java +++ b/src/main/generated/com/moplus/moplus_server/domain/problem/service/mapper/ChildProblemMapperImpl.java @@ -10,7 +10,7 @@ @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2025-02-12T02:55:03+0900", + date = "2025-02-18T17:01:34+0900", comments = "version: 1.6.3, compiler: javac, environment: Java 17.0.10 (JetBrains s.r.o.)" ) @Component @@ -31,7 +31,6 @@ public ChildProblem from(ChildProblemPostRequest request) { if ( set != null ) { childProblem.conceptTagIds( new LinkedHashSet( set ) ); } - childProblem.sequence( request.sequence() ); return childProblem.build(); } @@ -44,6 +43,7 @@ public ChildProblem from(ChildProblemUpdateRequest request) { ChildProblem.ChildProblemBuilder childProblem = ChildProblem.builder(); + childProblem.id( request.id() ); childProblem.imageUrl( request.imageUrl() ); childProblem.answerType( request.answerType() ); childProblem.answer( request.answer() ); @@ -51,7 +51,6 @@ public ChildProblem from(ChildProblemUpdateRequest request) { if ( set != null ) { childProblem.conceptTagIds( new LinkedHashSet( set ) ); } - childProblem.sequence( request.sequence() ); return childProblem.build(); } diff --git a/src/main/generated/com/moplus/moplus_server/domain/problem/service/mapper/ProblemMapperImpl.java b/src/main/generated/com/moplus/moplus_server/domain/problem/service/mapper/ProblemMapperImpl.java index dbb174c..2b75c34 100644 --- a/src/main/generated/com/moplus/moplus_server/domain/problem/service/mapper/ProblemMapperImpl.java +++ b/src/main/generated/com/moplus/moplus_server/domain/problem/service/mapper/ProblemMapperImpl.java @@ -2,7 +2,7 @@ import com.moplus.moplus_server.domain.problem.domain.practiceTest.PracticeTestTag; import com.moplus.moplus_server.domain.problem.domain.problem.Problem; -import com.moplus.moplus_server.domain.problem.domain.problem.ProblemAdminId; +import com.moplus.moplus_server.domain.problem.domain.problem.ProblemCustomId; import com.moplus.moplus_server.domain.problem.dto.request.ProblemPostRequest; import com.moplus.moplus_server.domain.problem.dto.request.ProblemUpdateRequest; import java.util.ArrayList; @@ -14,15 +14,15 @@ @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2025-02-13T19:05:39+0900", + date = "2025-02-18T15:29:27+0900", comments = "version: 1.6.3, compiler: javac, environment: Java 17.0.10 (JetBrains s.r.o.)" ) @Component public class ProblemMapperImpl implements ProblemMapper { @Override - public Problem from(ProblemPostRequest request, ProblemAdminId problemAdminId, PracticeTestTag practiceTestTag) { - if ( request == null && problemAdminId == null && practiceTestTag == null ) { + public Problem from(ProblemPostRequest request, ProblemCustomId problemCustomId, PracticeTestTag practiceTestTag) { + if ( request == null && problemCustomId == null && practiceTestTag == null ) { return null; } @@ -32,15 +32,15 @@ public Problem from(ProblemPostRequest request, ProblemAdminId problemAdminId, P problem.problemType( request.problemType() ); problem.number( request.number() ); } - problem.problemAdminId( problemAdminId ); + problem.problemCustomId( problemCustomId ); problem.practiceTestTag( practiceTestTag ); return problem.build(); } @Override - public Problem from(ProblemUpdateRequest request, ProblemAdminId problemAdminId, PracticeTestTag practiceTestTag) { - if ( request == null && problemAdminId == null && practiceTestTag == null ) { + public Problem from(ProblemUpdateRequest request, ProblemCustomId problemCustomId, PracticeTestTag practiceTestTag) { + if ( request == null && problemCustomId == null && practiceTestTag == null ) { return null; } @@ -68,7 +68,7 @@ public Problem from(ProblemUpdateRequest request, ProblemAdminId problemAdminId, problem.problemType( request.problemType() ); problem.number( request.number() ); } - problem.problemAdminId( problemAdminId ); + problem.problemCustomId( problemCustomId ); problem.practiceTestTag( practiceTestTag ); return problem.build(); diff --git a/src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QIncorrectProblem.java b/src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QIncorrectProblem.java index b73be8c..9f3b995 100644 --- a/src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QIncorrectProblem.java +++ b/src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QIncorrectProblem.java @@ -37,7 +37,7 @@ public class QIncorrectProblem extends EntityPathBase { public final NumberPath practiceTestId = createNumber("practiceTestId", Long.class); - public final NumberPath problemId = createNumber("problemId", Long.class); + public final NumberPath problemId = createNumber("problemCustomId", Long.class); public final StringPath problemNumber = createString("problemNumber"); diff --git a/src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QProblemImageForTest.java b/src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QProblemImageForTest.java index b7e7668..67b69aa 100644 --- a/src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QProblemImageForTest.java +++ b/src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QProblemImageForTest.java @@ -25,7 +25,7 @@ public class QProblemImageForTest extends EntityPathBase { public final StringPath imageUrl = createString("imageUrl"); - public final NumberPath problemId = createNumber("problemId", Long.class); + public final NumberPath problemId = createNumber("problemCustomId", Long.class); public QProblemImageForTest(String variable) { super(ProblemImageForTest.class, forVariable(variable)); diff --git a/src/main/java/com/moplus/moplus_server/domain/auth/dto/response/AccessTokenResponse.java b/src/main/java/com/moplus/moplus_server/domain/auth/dto/response/AccessTokenResponse.java index f8d70c2..25f5b8f 100644 --- a/src/main/java/com/moplus/moplus_server/domain/auth/dto/response/AccessTokenResponse.java +++ b/src/main/java/com/moplus/moplus_server/domain/auth/dto/response/AccessTokenResponse.java @@ -1,5 +1,9 @@ package com.moplus.moplus_server.domain.auth.dto.response; +import jakarta.validation.constraints.NotNull; + public record AccessTokenResponse( - String accessToken -) {} \ No newline at end of file + @NotNull(message = "accessToken을 입력해주세요.") + String accessToken +) { +} \ No newline at end of file diff --git a/src/main/java/com/moplus/moplus_server/domain/concept/controller/ConceptTagController.java b/src/main/java/com/moplus/moplus_server/domain/concept/controller/ConceptTagController.java index e575025..9670ed7 100644 --- a/src/main/java/com/moplus/moplus_server/domain/concept/controller/ConceptTagController.java +++ b/src/main/java/com/moplus/moplus_server/domain/concept/controller/ConceptTagController.java @@ -17,7 +17,7 @@ @RequiredArgsConstructor public class ConceptTagController { - ConceptTagRepository conceptTagRepository; + private final ConceptTagRepository conceptTagRepository; @GetMapping("") @Operation(summary = "모든 개념 태그 리스트 조회") diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/controller/ProblemController.java b/src/main/java/com/moplus/moplus_server/domain/problem/controller/ProblemController.java index d4acee7..6002770 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/controller/ProblemController.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/controller/ProblemController.java @@ -3,10 +3,12 @@ import com.moplus.moplus_server.domain.problem.dto.request.ProblemPostRequest; import com.moplus.moplus_server.domain.problem.dto.request.ProblemUpdateRequest; import com.moplus.moplus_server.domain.problem.dto.response.ProblemGetResponse; +import com.moplus.moplus_server.domain.problem.service.ChildProblemService; import com.moplus.moplus_server.domain.problem.service.ProblemDeleteService; import com.moplus.moplus_server.domain.problem.service.ProblemGetService; import com.moplus.moplus_server.domain.problem.service.ProblemSaveService; import com.moplus.moplus_server.domain.problem.service.ProblemUpdateService; +import com.moplus.moplus_server.global.response.IdResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; @@ -16,6 +18,7 @@ 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.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -30,6 +33,7 @@ public class ProblemController { private final ProblemUpdateService problemUpdateService; private final ProblemGetService problemGetService; private final ProblemDeleteService problemDeleteService; + private final ChildProblemService childProblemService; @GetMapping("/{id}") @Operation(summary = "문항 조회", description = "문항를 조회합니다.") @@ -40,15 +44,15 @@ public ResponseEntity getProblem( } @PostMapping("") - @Operation(summary = "문항 생성", description = "문제를 생성합니다. 새끼 문항은 list 순서대로 sequence를 저장합니다.") - public ResponseEntity createProblem( + @Operation(summary = "문항 생성", description = "문제를 생성합니다. 기출/변형 문제는 모든 값이 필수이며 창작 문제는 문항 타입만 필수 입니다.") + public ResponseEntity createProblem( @Valid @RequestBody ProblemPostRequest request ) { - return ResponseEntity.ok(problemSaveService.createProblem(request)); + return ResponseEntity.ok(new IdResponse(problemSaveService.createProblem(request))); } - @PostMapping("/{id}") - @Operation(summary = "문항 업데이트", description = "문제를 업데이트합니다. 문항 번호, 모의고사는 수정할 수 없습니다. 새로 추가되는 새끼문항 id는 빈 값입니다.") + @PutMapping("/{id}") + @Operation(summary = "문항 업데이트", description = "문제를 업데이트합니다. 새끼문항은 들어온 list의 순서로 저장됩니다.") public ResponseEntity updateProblem( @PathVariable("id") Long id, @RequestBody ProblemUpdateRequest request @@ -64,4 +68,22 @@ public ResponseEntity updateProblem( problemDeleteService.deleteProblem(id); return ResponseEntity.ok().body(null); } + + @PostMapping("/{problemId}/child-problems") + @Operation(summary = "새끼문항 추가", description = "추가되는 새끼 문항의 id를 반환합니다. 컨펌 이후에는 새끼 문항 추가가 불가능합니다.") + public ResponseEntity createChildProblem( + @PathVariable("problemId") Long problemId + ) { + return ResponseEntity.ok(new IdResponse(childProblemService.createChildProblem(problemId))); + } + + @DeleteMapping("/{problemId}/child-problems/{childProblemId}") + @Operation(summary = "새끼 문항 삭제", description = "컨펌 이후에는 새끼 문항 삭제가 불가능합니다.") + public ResponseEntity deleteChildProblem( + @PathVariable("problemId") Long problemId, + @PathVariable("childProblemId") Long childProblemId + ) { + childProblemService.deleteChildProblem(problemId, childProblemId); + return ResponseEntity.ok().body(null); + } } diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/controller/ProblemSearchController.java b/src/main/java/com/moplus/moplus_server/domain/problem/controller/ProblemSearchController.java index 7ec3490..fab72f2 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/controller/ProblemSearchController.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/controller/ProblemSearchController.java @@ -26,11 +26,12 @@ public class ProblemSearchController { description = "문항 ID, 문제명, 개념 태그리스트로 문제를 검색합니다. 개념 태그리스트는 OR 조건으로 검색하며 값이 없으면 쿼리파라미터에서 빼주세요" ) public ResponseEntity> search( - @RequestParam(value = "problemId", required = false) String problemId, + @RequestParam(value = "problemCustomId", required = false) String problemCustomId, @RequestParam(value = "comment", required = false) String comment, @RequestParam(value = "conceptTagIds", required = false) List conceptTagIds ) { - List problems = problemSearchRepository.search(problemId, comment, conceptTagIds); + List problems = problemSearchRepository.search(problemCustomId, comment, + conceptTagIds); return ResponseEntity.ok(problems); } } diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/domain/childProblem/ChildProblem.java b/src/main/java/com/moplus/moplus_server/domain/problem/domain/childProblem/ChildProblem.java index f42c2d7..8396008 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/domain/childProblem/ChildProblem.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/domain/childProblem/ChildProblem.java @@ -40,17 +40,24 @@ public class ChildProblem extends BaseEntity { private Answer answer; @Enumerated(EnumType.STRING) private AnswerType answerType; - private int sequence; @Builder - public ChildProblem(String imageUrl, AnswerType answerType, String answer, Set conceptTagIds, - int sequence) { + public ChildProblem(Long id, String imageUrl, AnswerType answerType, String answer, Set conceptTagIds) { + this.id = id; validateAnswerByType(answer, answerType); this.imageUrl = imageUrl; this.answerType = answerType; this.answer = new Answer(answer, answerType); this.conceptTagIds = conceptTagIds; - this.sequence = sequence; + } + + public static ChildProblem createEmptyChildProblem() { + return ChildProblem.builder() + .imageUrl("") + .answerType(AnswerType.SHORT_STRING_ANSWER) + .answer("") + .conceptTagIds(Set.of()) + .build(); } public void validateAnswerByType(String answer, AnswerType answerType) { @@ -62,11 +69,13 @@ public void validateAnswerByType(String answer, AnswerType answerType) { } public void update(ChildProblem input) { + if (this.id != input.id) { + throw new InvalidValueException(ErrorCode.INVALID_CHILD_PROBLEM_SEQUENCE); + } this.imageUrl = input.imageUrl; this.answerType = input.answerType; this.answer = input.answer; this.conceptTagIds = input.conceptTagIds; - this.sequence = input.sequence; } public String getAnswer() { diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/Problem.java b/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/Problem.java index bc3f96b..2b08fad 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/Problem.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/Problem.java @@ -5,6 +5,8 @@ import com.moplus.moplus_server.domain.problem.domain.practiceTest.PracticeTestTag; import com.moplus.moplus_server.domain.problem.repository.converter.StringListConverter; 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.CascadeType; import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; @@ -20,9 +22,8 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToMany; -import jakarta.persistence.OrderBy; +import jakarta.persistence.OrderColumn; import java.util.ArrayList; -import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -37,7 +38,7 @@ public class Problem extends BaseEntity { @Embedded - ProblemAdminId problemAdminId; + ProblemCustomId problemCustomId; Long practiceTestId; int number; @Enumerated(EnumType.STRING) @@ -76,7 +77,7 @@ public class Problem extends BaseEntity { @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) @JoinColumn(name = "problem_id") - @OrderBy("sequence ASC") + @OrderColumn(name = "sequence") private List childProblems = new ArrayList<>(); @Builder @@ -85,7 +86,7 @@ public Problem(List childProblems, boolean isConfirmed, AnswerType List prescriptionImageUrls, String seniorTipImageUrl, String readingTipImageUrl, String mainAnalysisImageUrl, String mainProblemImageUrl, String memo, String answer, String title, ProblemType problemType, int number, PracticeTestTag practiceTestTag, - ProblemAdminId problemAdminId) { + ProblemCustomId problemCustomId) { this.childProblems = childProblems; this.isConfirmed = isConfirmed; this.answerType = answerType; @@ -103,22 +104,15 @@ public Problem(List childProblems, boolean isConfirmed, AnswerType this.problemType = problemType; this.number = number; this.practiceTestId = practiceTestTag.getId(); - this.problemAdminId = problemAdminId; + this.problemCustomId = problemCustomId; } public String getAnswer() { return answer.getValue(); } - public void addChildProblem(List inputChildProblems) { - List mutableChildProblems = new ArrayList<>(inputChildProblems); - mutableChildProblems.sort(Comparator.comparingInt(ChildProblem::getSequence)); - mutableChildProblems.forEach(childProblems::add); - mutableChildProblems.forEach(childProblem -> conceptTagIds.addAll(childProblem.getConceptTagIds())); - } - public void update(Problem inputProblem) { - this.problemAdminId = inputProblem.getProblemAdminId(); + this.problemCustomId = inputProblem.getProblemCustomId(); this.practiceTestId = inputProblem.getPracticeTestId(); this.number = inputProblem.getNumber(); this.problemType = inputProblem.getProblemType(); @@ -134,33 +128,20 @@ public void update(Problem inputProblem) { this.seniorTipImageUrl = inputProblem.getSeniorTipImageUrl(); this.prescriptionImageUrls = inputProblem.getPrescriptionImageUrls(); this.answerType = inputProblem.getAnswerType(); - this.isConfirmed = inputProblem.isConfirmed(); } public void updateChildProblem(List inputChildProblems) { - inputChildProblems.forEach(childProblem -> { - this.childProblems.stream() - .filter(existingChildProblem -> existingChildProblem.getId().equals(childProblem.getId())) - .findFirst() - .ifPresentOrElse( - existingChildProblem -> { - existingChildProblem.update(childProblem); - conceptTagIds.addAll(existingChildProblem.getConceptTagIds()); - }, - () -> { - childProblems.add(childProblem); - conceptTagIds.addAll(childProblem.getConceptTagIds()); - } - ); - }); - } - - public void deleteChildProblem(List deleteChildProblems) { - childProblems.removeIf(childProblem -> deleteChildProblems.contains(childProblem.getId())); + if (isConfirmed && this.childProblems.size() != inputChildProblems.size()) { + throw new InvalidValueException(ErrorCode.INVALID_CHILD_PROBLEM_SIZE); + } + + for (int i = 0; i < inputChildProblems.size(); i++) { + this.childProblems.get(i).update(inputChildProblems.get(i)); + } } public boolean isValid() { - return problemAdminId != null + return problemCustomId != null && practiceTestId != null && problemType != null && title != null && !title.getTitle().isEmpty() diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemAdminIdService.java b/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemAdminIdService.java index 8c61f9f..d447346 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemAdminIdService.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemAdminIdService.java @@ -22,7 +22,7 @@ public class ProblemAdminIdService { NN : 번호 (01~99) XX : 2자리 sequence 숫자 */ - public ProblemAdminId nextId(int number, PracticeTestTag practiceTestTag, ProblemType problemType) { + public ProblemCustomId nextId(int number, PracticeTestTag practiceTestTag, ProblemType problemType) { int problemTypeCode = problemType.getCode(); // C (문제 타입) int subject = practiceTestTag.getSubject().getCode(); // S (과목) @@ -37,8 +37,8 @@ public ProblemAdminId nextId(int number, PracticeTestTag practiceTestTag, Proble sequence = SEQUENCE.getAndIncrement() % 100; // 000~999 순환 generatedId = String.format("%d%d%02d%02d%02d%02d", problemTypeCode, subject, year, month, number, sequence); - } while (problemRepository.existsByProblemAdminId(new ProblemAdminId(generatedId))); // ID가 이미 존재하면 재생성 + } while (problemRepository.existsByProblemCustomId(new ProblemCustomId(generatedId))); // ID가 이미 존재하면 재생성 - return new ProblemAdminId(generatedId); + return new ProblemCustomId(generatedId); } } diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemAdminId.java b/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemCustomId.java similarity index 71% rename from src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemAdminId.java rename to src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemCustomId.java index 7c04c6e..85648be 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemAdminId.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemCustomId.java @@ -10,12 +10,12 @@ @Getter @Embeddable @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class ProblemAdminId implements Serializable { +public class ProblemCustomId implements Serializable { - @Column(name = "problem_admin_id", nullable = false) + @Column(name = "problem_custom_id") private String id; - public ProblemAdminId(String id) { + public ProblemCustomId(String id) { this.id = id; } } diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/dto/request/ChildProblemUpdateRequest.java b/src/main/java/com/moplus/moplus_server/domain/problem/dto/request/ChildProblemUpdateRequest.java index 19ba8ff..c791a80 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/dto/request/ChildProblemUpdateRequest.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/dto/request/ChildProblemUpdateRequest.java @@ -1,16 +1,13 @@ package com.moplus.moplus_server.domain.problem.dto.request; import com.moplus.moplus_server.domain.problem.domain.problem.AnswerType; -import io.swagger.v3.oas.annotations.media.Schema; import java.util.Set; public record ChildProblemUpdateRequest( - @Schema(description = "새로 생성되는 새끼문항은 빈 값입니다.") Long id, String imageUrl, AnswerType answerType, String answer, - Set conceptTagIds, - int sequence + Set conceptTagIds ) { } diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/dto/request/ProblemPostRequest.java b/src/main/java/com/moplus/moplus_server/domain/problem/dto/request/ProblemPostRequest.java index 0933dec..94f597f 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/dto/request/ProblemPostRequest.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/dto/request/ProblemPostRequest.java @@ -2,7 +2,7 @@ import com.moplus.moplus_server.domain.problem.domain.practiceTest.PracticeTestTag; import com.moplus.moplus_server.domain.problem.domain.problem.Problem; -import com.moplus.moplus_server.domain.problem.domain.problem.ProblemAdminId; +import com.moplus.moplus_server.domain.problem.domain.problem.ProblemCustomId; import com.moplus.moplus_server.domain.problem.domain.problem.ProblemType; import jakarta.validation.constraints.NotNull; @@ -12,9 +12,9 @@ public record ProblemPostRequest( Long practiceTestId, int number ) { - public Problem toEntity(PracticeTestTag practiceTestTag, ProblemAdminId problemAdminId) { + public Problem toEntity(PracticeTestTag practiceTestTag, ProblemCustomId problemCustomId) { return Problem.builder() - .problemAdminId(problemAdminId) + .problemCustomId(problemCustomId) .practiceTestTag(practiceTestTag) .number(number) .title("") diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/dto/request/ProblemUpdateRequest.java b/src/main/java/com/moplus/moplus_server/domain/problem/dto/request/ProblemUpdateRequest.java index c55fb83..a4d0971 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/dto/request/ProblemUpdateRequest.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/dto/request/ProblemUpdateRequest.java @@ -23,7 +23,6 @@ public record ProblemUpdateRequest( String seniorTipImageUrl, List prescriptionImageUrls, AnswerType answerType, - List updateChildProblems, - List deleteChildProblems + List updateChildProblems ) { } diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/dto/response/ProblemGetResponse.java b/src/main/java/com/moplus/moplus_server/domain/problem/dto/response/ProblemGetResponse.java index 650189b..6354dda 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/dto/response/ProblemGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/dto/response/ProblemGetResponse.java @@ -10,8 +10,10 @@ @Builder public record ProblemGetResponse( - @NotNull(message = "문항 ID은 필수입니다") - String problemId, + @NotNull(message = "문항 ID 필수입니다") + Long id, + @NotNull(message = "문항 custom ID는 필수입니다") + String problemCustomId, Set conceptTagIds, Long practiceTestId, int number, @@ -31,8 +33,10 @@ public record ProblemGetResponse( ) { public static ProblemGetResponse of(Problem problem) { + return ProblemGetResponse.builder() - .problemId(problem.getProblemAdminId().getId()) + .id(problem.getId()) + .problemCustomId(problem.getProblemCustomId().getId()) .conceptTagIds(problem.getConceptTagIds()) .practiceTestId(problem.getPracticeTestId()) .number(problem.getNumber()) diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemRepository.java b/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemRepository.java index 6f62f77..4529f68 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemRepository.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemRepository.java @@ -1,14 +1,14 @@ package com.moplus.moplus_server.domain.problem.repository; import com.moplus.moplus_server.domain.problem.domain.problem.Problem; -import com.moplus.moplus_server.domain.problem.domain.problem.ProblemAdminId; +import com.moplus.moplus_server.domain.problem.domain.problem.ProblemCustomId; 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; public interface ProblemRepository extends JpaRepository { - boolean existsByProblemAdminId(ProblemAdminId problemAdminId); + boolean existsByProblemCustomId(ProblemCustomId problemCustomId); default void existsByIdElseThrow(Long id) { if (!existsById(id)) { @@ -16,8 +16,8 @@ default void existsByIdElseThrow(Long id) { } } - default void existsByProblemAdminIdElseThrow(ProblemAdminId problemAdminId) { - if (!existsByProblemAdminId(problemAdminId)) { + default void existsByProblemAdminIdElseThrow(ProblemCustomId problemCustomId) { + if (!existsByProblemCustomId(problemCustomId)) { throw new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND); } } diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryCustom.java b/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryCustom.java index b2ff859..9c57fd1 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryCustom.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryCustom.java @@ -21,7 +21,7 @@ public class ProblemSearchRepositoryCustom { public List search(String problemId, String comment, List conceptTagIds) { return queryFactory - .select(problem.problemAdminId.id, problem.memo, problem.mainProblemImageUrl) + .select(problem.problemCustomId.id, problem.memo, problem.mainProblemImageUrl) .from(problem) .where( containsProblemId(problemId), @@ -32,7 +32,7 @@ public List search(String problemId, String comment, L .distinct() .transform(GroupBy.groupBy(problem.id).list( Projections.constructor(ProblemSearchGetResponse.class, - problem.problemAdminId.id, + problem.problemCustomId.id, problem.memo, problem.mainProblemImageUrl, GroupBy.set( @@ -45,10 +45,10 @@ public List search(String problemId, String comment, L )); } - //problemId 일부 포함 검색 + //problemCustomId 일부 포함 검색 private BooleanExpression containsProblemId(String problemId) { return (problemId == null || problemId.isEmpty()) ? null - : problem.problemAdminId.id.containsIgnoreCase(problemId); + : problem.problemCustomId.id.containsIgnoreCase(problemId); } //name 조건 (포함 검색) diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/service/ChildProblemService.java b/src/main/java/com/moplus/moplus_server/domain/problem/service/ChildProblemService.java new file mode 100644 index 0000000..d4e7a43 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/domain/problem/service/ChildProblemService.java @@ -0,0 +1,38 @@ +package com.moplus.moplus_server.domain.problem.service; + +import com.moplus.moplus_server.domain.problem.domain.childProblem.ChildProblem; +import com.moplus.moplus_server.domain.problem.domain.problem.Problem; +import com.moplus.moplus_server.domain.problem.repository.ChildProblemRepository; +import com.moplus.moplus_server.domain.problem.repository.ProblemRepository; +import com.moplus.moplus_server.global.error.exception.ErrorCode; +import com.moplus.moplus_server.global.error.exception.InvalidValueException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class ChildProblemService { + + private final ProblemRepository problemRepository; + private final ChildProblemRepository childProblemRepository; + + @Transactional + public Long createChildProblem(Long problemId) { + Problem problem = problemRepository.findByIdElseThrow(problemId); + if (problem.isConfirmed()) { + throw new InvalidValueException(ErrorCode.CHILD_PROBLEM_UPDATE_AFTER_CONFIRMED); + } + + return childProblemRepository.save(ChildProblem.createEmptyChildProblem()).getId(); + } + + @Transactional + public void deleteChildProblem(Long problemId, Long childProblemId) { + Problem problem = problemRepository.findByIdElseThrow(problemId); + if (problem.isConfirmed()) { + throw new InvalidValueException(ErrorCode.CHILD_PROBLEM_UPDATE_AFTER_CONFIRMED); + } + childProblemRepository.deleteById(childProblemId); + } +} diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/service/ImageUploadService.java b/src/main/java/com/moplus/moplus_server/domain/problem/service/ImageUploadService.java index 9617adf..6ba2888 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/service/ImageUploadService.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/service/ImageUploadService.java @@ -1,7 +1,7 @@ package com.moplus.moplus_server.domain.problem.service; import com.amazonaws.HttpMethod; -import com.moplus.moplus_server.domain.problem.domain.problem.ProblemAdminId; +import com.moplus.moplus_server.domain.problem.domain.problem.ProblemCustomId; import com.moplus.moplus_server.domain.problem.domain.problem.ProblemImageType; import com.moplus.moplus_server.domain.problem.repository.ProblemRepository; import com.moplus.moplus_server.global.utils.s3.S3Util; @@ -18,7 +18,7 @@ public class ImageUploadService { private final ProblemRepository problemRepository; public String generateProblemImagePresignedUrl(String problemId, ProblemImageType imageType) { - problemRepository.existsByProblemAdminIdElseThrow(new ProblemAdminId(problemId)); + problemRepository.existsByProblemAdminIdElseThrow(new ProblemCustomId(problemId)); String fileName = generateProblemImageFileName(problemId, imageType); return s3Util.getS3PresignedUrl(fileName, HttpMethod.PUT); } diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemSaveService.java b/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemSaveService.java index a6f6679..24ea2f9 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemSaveService.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemSaveService.java @@ -2,8 +2,8 @@ import com.moplus.moplus_server.domain.problem.domain.practiceTest.PracticeTestTag; import com.moplus.moplus_server.domain.problem.domain.problem.Problem; -import com.moplus.moplus_server.domain.problem.domain.problem.ProblemAdminId; import com.moplus.moplus_server.domain.problem.domain.problem.ProblemAdminIdService; +import com.moplus.moplus_server.domain.problem.domain.problem.ProblemCustomId; import com.moplus.moplus_server.domain.problem.dto.request.ProblemPostRequest; import com.moplus.moplus_server.domain.problem.repository.PracticeTestTagRepository; import com.moplus.moplus_server.domain.problem.repository.ProblemRepository; @@ -24,10 +24,10 @@ public class ProblemSaveService { @Transactional public Long createProblem(ProblemPostRequest request) { PracticeTestTag practiceTestTag = practiceTestRepository.findByIdElseThrow(request.practiceTestId()); - ProblemAdminId problemAdminId = problemAdminIdService.nextId(request.number(), practiceTestTag, + ProblemCustomId problemCustomId = problemAdminIdService.nextId(request.number(), practiceTestTag, request.problemType()); - Problem problem = problemMapper.from(request, problemAdminId, practiceTestTag); + Problem problem = problemMapper.from(request, problemCustomId, practiceTestTag); return problemRepository.save(problem).getId(); } diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemUpdateService.java b/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemUpdateService.java index 81f1086..423fd3b 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemUpdateService.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemUpdateService.java @@ -4,12 +4,10 @@ import com.moplus.moplus_server.domain.problem.domain.childProblem.ChildProblem; import com.moplus.moplus_server.domain.problem.domain.practiceTest.PracticeTestTag; import com.moplus.moplus_server.domain.problem.domain.problem.Problem; -import com.moplus.moplus_server.domain.problem.domain.problem.ProblemAdminId; import com.moplus.moplus_server.domain.problem.domain.problem.ProblemAdminIdService; -import com.moplus.moplus_server.domain.problem.dto.request.ChildProblemUpdateRequest; +import com.moplus.moplus_server.domain.problem.domain.problem.ProblemCustomId; import com.moplus.moplus_server.domain.problem.dto.request.ProblemUpdateRequest; import com.moplus.moplus_server.domain.problem.dto.response.ProblemGetResponse; -import com.moplus.moplus_server.domain.problem.repository.ChildProblemRepository; import com.moplus.moplus_server.domain.problem.repository.PracticeTestTagRepository; import com.moplus.moplus_server.domain.problem.repository.ProblemRepository; import com.moplus.moplus_server.domain.problem.service.mapper.ChildProblemMapper; @@ -27,7 +25,6 @@ public class ProblemUpdateService { private final ProblemAdminIdService problemAdminIdService; private final PracticeTestTagRepository practiceTestRepository; private final ConceptTagRepository conceptTagRepository; - private final ChildProblemRepository childProblemRepository; private final ChildProblemMapper childProblemMapper; private final ProblemMapper problemMapper; @@ -37,12 +34,11 @@ public ProblemGetResponse updateProblem(Long problemId, ProblemUpdateRequest req conceptTagRepository.existsByIdElseThrow(request.conceptTagIds()); Problem problem = problemRepository.findByIdElseThrow(problemId); - ProblemAdminId problemAdminId = problemAdminIdService.nextId(request.number(), practiceTestTag, + ProblemCustomId problemCustomId = problemAdminIdService.nextId(request.number(), practiceTestTag, request.problemType()); - Problem inputProblem = problemMapper.from(request, problemAdminId, practiceTestTag); + Problem inputProblem = problemMapper.from(request, problemCustomId, practiceTestTag); problem.update(inputProblem); - problem.deleteChildProblem(request.deleteChildProblems()); List childProblems = changeToChildProblems(request); problem.updateChildProblem(childProblems); @@ -52,16 +48,7 @@ public ProblemGetResponse updateProblem(Long problemId, ProblemUpdateRequest req private List changeToChildProblems(ProblemUpdateRequest request) { return request.updateChildProblems().stream() - .map(this::getChildProblem) + .map(childProblemMapper::from) .toList(); } - - private ChildProblem getChildProblem(ChildProblemUpdateRequest updateChildProblem) { - if (updateChildProblem.id() == null) { - return childProblemMapper.from(updateChildProblem); - } - ChildProblem childProblem = childProblemRepository.findByIdElseThrow(updateChildProblem.id()); - childProblem.update(childProblemMapper.from(updateChildProblem)); - return childProblem; - } } diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/service/mapper/ChildProblemMapper.java b/src/main/java/com/moplus/moplus_server/domain/problem/service/mapper/ChildProblemMapper.java index d8a6566..e2deb5a 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/service/mapper/ChildProblemMapper.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/service/mapper/ChildProblemMapper.java @@ -4,11 +4,13 @@ import com.moplus.moplus_server.domain.problem.dto.request.ChildProblemPostRequest; import com.moplus.moplus_server.domain.problem.dto.request.ChildProblemUpdateRequest; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; @Mapper(componentModel = "spring") public interface ChildProblemMapper { ChildProblem from(ChildProblemPostRequest request); + @Mapping(target = "id", source = "id") ChildProblem from(ChildProblemUpdateRequest request); } diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/service/mapper/ProblemMapper.java b/src/main/java/com/moplus/moplus_server/domain/problem/service/mapper/ProblemMapper.java index 5497c41..354ab5b 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/service/mapper/ProblemMapper.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/service/mapper/ProblemMapper.java @@ -2,7 +2,7 @@ import com.moplus.moplus_server.domain.problem.domain.practiceTest.PracticeTestTag; import com.moplus.moplus_server.domain.problem.domain.problem.Problem; -import com.moplus.moplus_server.domain.problem.domain.problem.ProblemAdminId; +import com.moplus.moplus_server.domain.problem.domain.problem.ProblemCustomId; import com.moplus.moplus_server.domain.problem.dto.request.ProblemPostRequest; import com.moplus.moplus_server.domain.problem.dto.request.ProblemUpdateRequest; import org.mapstruct.Mapper; @@ -13,14 +13,14 @@ public interface ProblemMapper { @Mappings({ - @Mapping(target = "problemAdminId", source = "problemAdminId"), + @Mapping(target = "problemCustomId", source = "problemCustomId"), @Mapping(target = "practiceTestTag", source = "practiceTestTag"), }) - Problem from(ProblemPostRequest request, ProblemAdminId problemAdminId, PracticeTestTag practiceTestTag); + Problem from(ProblemPostRequest request, ProblemCustomId problemCustomId, PracticeTestTag practiceTestTag); @Mappings({ - @Mapping(target = "problemAdminId", source = "problemAdminId"), + @Mapping(target = "problemCustomId", source = "problemCustomId"), @Mapping(target = "practiceTestTag", source = "practiceTestTag"), }) - Problem from(ProblemUpdateRequest request, ProblemAdminId problemAdminId, PracticeTestTag practiceTestTag); + Problem from(ProblemUpdateRequest request, ProblemCustomId problemCustomId, PracticeTestTag practiceTestTag); } diff --git a/src/main/java/com/moplus/moplus_server/domain/problemset/controller/ProblemSetController.java b/src/main/java/com/moplus/moplus_server/domain/problemset/controller/ProblemSetController.java index f38a715..0b8296e 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problemset/controller/ProblemSetController.java +++ b/src/main/java/com/moplus/moplus_server/domain/problemset/controller/ProblemSetController.java @@ -9,6 +9,7 @@ import com.moplus.moplus_server.domain.problemset.service.ProblemSetGetService; import com.moplus.moplus_server.domain.problemset.service.ProblemSetSaveService; import com.moplus.moplus_server.domain.problemset.service.ProblemSetUpdateService; +import com.moplus.moplus_server.global.response.IdResponse; import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -36,10 +37,10 @@ public class ProblemSetController { @PostMapping("") @Operation(summary = "문항세트 생성", description = "문항세트를 생성합니다. 문항은 요청 순서대로 저장합니다.") - public ResponseEntity createProblemSet( + public ResponseEntity createProblemSet( @RequestBody ProblemSetPostRequest request ) { - return ResponseEntity.ok(problemSetSaveService.createProblemSet(request)); + return ResponseEntity.ok(new IdResponse(problemSetSaveService.createProblemSet(request))); } @Hidden diff --git a/src/main/java/com/moplus/moplus_server/domain/problemset/domain/ProblemSet.java b/src/main/java/com/moplus/moplus_server/domain/problemset/domain/ProblemSet.java index 7a99cfd..ee5fa4f 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problemset/domain/ProblemSet.java +++ b/src/main/java/com/moplus/moplus_server/domain/problemset/domain/ProblemSet.java @@ -35,6 +35,7 @@ public class ProblemSet extends BaseEntity { @Embedded private Title title; + @Column(nullable = false) private boolean isDeleted; @@ -68,7 +69,7 @@ public void toggleConfirm(List problems) { if (this.confirmStatus == ProblemSetConfirmStatus.NOT_CONFIRMED) { List invalidProblemIds = problems.stream() .filter(problem -> !problem.isValid()) - .map(problem -> problem.getProblemAdminId().getId()) + .map(problem -> problem.getProblemCustomId().getId()) .toList(); if (!invalidProblemIds.isEmpty()) { String message = ErrorCode.INVALID_CONFIRM_PROBLEM.getMessage() + diff --git a/src/main/java/com/moplus/moplus_server/domain/problemset/dto/response/ProblemSummaryResponse.java b/src/main/java/com/moplus/moplus_server/domain/problemset/dto/response/ProblemSummaryResponse.java index 78ce956..4c28056 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problemset/dto/response/ProblemSummaryResponse.java +++ b/src/main/java/com/moplus/moplus_server/domain/problemset/dto/response/ProblemSummaryResponse.java @@ -16,7 +16,7 @@ public record ProblemSummaryResponse( public static ProblemSummaryResponse of(Problem problem, String practiceTestName, List tagNames) { return ProblemSummaryResponse.builder() - .problemId(problem.getProblemAdminId().getId()) + .problemId(problem.getProblemCustomId().getId()) .number(problem.getNumber()) .memo(problem.getMemo()) .mainProblemImageUrl(problem.getMainProblemImageUrl()) diff --git a/src/main/java/com/moplus/moplus_server/domain/publish/controller/PublishController.java b/src/main/java/com/moplus/moplus_server/domain/publish/controller/PublishController.java index 0415f17..ddd8728 100644 --- a/src/main/java/com/moplus/moplus_server/domain/publish/controller/PublishController.java +++ b/src/main/java/com/moplus/moplus_server/domain/publish/controller/PublishController.java @@ -5,6 +5,7 @@ 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 com.moplus.moplus_server.global.response.IdResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; @@ -40,10 +41,10 @@ public ResponseEntity> getPublishMonth( @PostMapping("") @Operation(summary = "발행 생성하기", description = "특정 날짜에 문항세트를 발행합니다.") - public ResponseEntity postPublish( + public ResponseEntity postPublish( @Valid @RequestBody PublishPostRequest request ) { - return ResponseEntity.ok(publishSaveService.createPublish(request)); + return ResponseEntity.ok(new IdResponse(publishSaveService.createPublish(request))); } @DeleteMapping("/{publishId}") diff --git a/src/main/java/com/moplus/moplus_server/global/config/WebConfig.java b/src/main/java/com/moplus/moplus_server/global/config/WebConfig.java index 1663f45..784551f 100644 --- a/src/main/java/com/moplus/moplus_server/global/config/WebConfig.java +++ b/src/main/java/com/moplus/moplus_server/global/config/WebConfig.java @@ -3,6 +3,7 @@ import com.moplus.moplus_server.global.annotation.AuthenticationArgumentResolver; import java.util.List; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.transaction.annotation.EnableTransactionManagement; @@ -17,13 +18,13 @@ public class WebConfig implements WebMvcConfigurer { private final AuthenticationArgumentResolver authenticationArgumentResolver; + @Value("${cors-allowed-origins}") + private List corsAllowedOrigins; @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") - .allowedOrigins("https://dev.mopl.kr", "http://dev.mopl.kr", "http://localhost:8080", - "https://www.mopl.kr", "http" - + "://localhost:3000", "https://dev-web.mopl.kr", "http://dev-web.mopl.kr") + .allowedOrigins(corsAllowedOrigins.toArray(new String[0])) .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true); diff --git a/src/main/java/com/moplus/moplus_server/global/error/exception/ErrorCode.java b/src/main/java/com/moplus/moplus_server/global/error/exception/ErrorCode.java index 199a216..f3c83b2 100644 --- a/src/main/java/com/moplus/moplus_server/global/error/exception/ErrorCode.java +++ b/src/main/java/com/moplus/moplus_server/global/error/exception/ErrorCode.java @@ -37,6 +37,10 @@ public enum ErrorCode { //새끼 문항 CHILD_PROBLEM_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 새끼 문제를 찾을 수 없습니다"), + CHILD_PROBLEM_UPDATE_AFTER_CONFIRMED(HttpStatus.BAD_REQUEST, "컨펌 후 문제는 수정할 수 없습니다"), + INVALID_CHILD_PROBLEM_SEQUENCE(HttpStatus.BAD_REQUEST, "새끼 문제의 업데이트 순서가 일치하지 않습니다."), + INVALID_CHILD_PROBLEM_SIZE(HttpStatus.BAD_REQUEST, "새끼 문제의 업데이트 개수가 일치하지 않습니다."), + //개념태그 CONCEPT_TAG_NOT_FOUND_IN_LIST(HttpStatus.NOT_FOUND, "해당 리스트 중 존재하지 않는 개념 태그가 있습니다."), diff --git a/src/main/java/com/moplus/moplus_server/global/response/IdResponse.java b/src/main/java/com/moplus/moplus_server/global/response/IdResponse.java new file mode 100644 index 0000000..8c44006 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/global/response/IdResponse.java @@ -0,0 +1,6 @@ +package com.moplus.moplus_server.global.response; + +public record IdResponse( + Long id +) { +} diff --git a/src/main/java/com/moplus/moplus_server/global/response/ResponseDto.java b/src/main/java/com/moplus/moplus_server/global/response/ResponseDto.java deleted file mode 100644 index 545e075..0000000 --- a/src/main/java/com/moplus/moplus_server/global/response/ResponseDto.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.moplus.moplus_server.global.response; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.moplus.moplus_server.global.error.ErrorResponse; -import org.springframework.http.HttpStatus; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public record ResponseDto( - T data, - String message, - HttpStatus status -) { - public static ResponseDto success(final T data) { - return new ResponseDto<>(data, null, null); - } - - public static ResponseDto fail(ErrorResponse errorResponse) { - return new ResponseDto<>(null, errorResponse.getMessage(), errorResponse.getStatus()); - } -} \ No newline at end of file diff --git a/src/main/java/com/moplus/moplus_server/global/response/ResponseDtoAdvice.java b/src/main/java/com/moplus/moplus_server/global/response/ResponseDtoAdvice.java deleted file mode 100644 index af24122..0000000 --- a/src/main/java/com/moplus/moplus_server/global/response/ResponseDtoAdvice.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.moplus.moplus_server.global.response; - -import com.moplus.moplus_server.global.error.ErrorResponse; -import com.moplus.moplus_server.global.error.exception.ErrorCode; -import org.springframework.core.MethodParameter; -import org.springframework.http.MediaType; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.http.server.ServerHttpRequest; -import org.springframework.http.server.ServerHttpResponse; -import org.springframework.web.bind.annotation.RestControllerAdvice; -import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; - -@RestControllerAdvice( - basePackages = "com.moplus.moplus_server" -) - -public class ResponseDtoAdvice implements ResponseBodyAdvice { - - @Override - public boolean supports(MethodParameter returnType, Class converterType) { - return !(returnType.getParameterType() == ResponseDto.class) - && MappingJackson2HttpMessageConverter.class.isAssignableFrom(converterType); - } - - @Override - public Object beforeBodyWrite( - Object body, - MethodParameter returnType, - MediaType selectedContentType, - Class selectedConverterType, - ServerHttpRequest request, - ServerHttpResponse response - ) { - if (body instanceof ErrorResponse) { - return ResponseDto.fail((ErrorResponse) body); - } - return ResponseDto.success(body); - } -} diff --git a/src/main/resources/templates/imageUploadPage.html b/src/main/resources/templates/imageUploadPage.html index 59fe602..d26dcd4 100644 --- a/src/main/resources/templates/imageUploadPage.html +++ b/src/main/resources/templates/imageUploadPage.html @@ -22,7 +22,7 @@

문제 이미지 업로드

- 문제 ID + 문제 ID 문제 번호 @@ -34,7 +34,7 @@

문제 이미지 업로드

+ th:action="'/admin/practiceTests/uploadImage/' + ${problemForTest.problemCustomId}"> diff --git a/src/test/java/com/moplus/moplus_server/domain/member/controller/MemberControllerTest.java b/src/test/java/com/moplus/moplus_server/domain/member/controller/MemberControllerTest.java index 3d3622d..2ce90a3 100644 --- a/src/test/java/com/moplus/moplus_server/domain/member/controller/MemberControllerTest.java +++ b/src/test/java/com/moplus/moplus_server/domain/member/controller/MemberControllerTest.java @@ -70,9 +70,9 @@ public void setMockMvc() throws Exception { .contentType("application/json") .header(HttpHeaders.AUTHORIZATION, "Bearer " + validToken)) .andExpect(status().isOk()) // 200 응답 확인 - .andExpect(jsonPath("$.data.id").exists()) // MemberGetResponse의 필드 확인 - .andExpect(jsonPath("$.data.name").exists()) - .andExpect(jsonPath("$.data.email").exists()); + .andExpect(jsonPath("$.id").exists()) // MemberGetResponse의 필드 확인 + .andExpect(jsonPath("$.name").exists()) + .andExpect(jsonPath("$.email").exists()); } } } \ No newline at end of file diff --git a/src/test/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemAdminIdServiceTest.java b/src/test/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemCustomIdServiceTest.java similarity index 83% rename from src/test/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemAdminIdServiceTest.java rename to src/test/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemCustomIdServiceTest.java index 184e71c..00c0f57 100644 --- a/src/test/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemAdminIdServiceTest.java +++ b/src/test/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemCustomIdServiceTest.java @@ -19,7 +19,7 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -class ProblemAdminIdServiceTest { +class ProblemCustomIdServiceTest { public static final String ID_LENGTH = "10"; @Mock @@ -43,10 +43,10 @@ void setUp() { // given int 문제번호 = 20; ProblemType problemType = ProblemType.GICHUL_PROBLEM; - when(problemRepository.existsByProblemAdminId(any())).thenReturn(false); // 중복 없음 + when(problemRepository.existsByProblemCustomId(any())).thenReturn(false); // 중복 없음 // when - ProblemAdminId generatedId = problemAdminIdService.nextId(문제번호, practiceTestTag, problemType); + ProblemCustomId generatedId = problemAdminIdService.nextId(문제번호, practiceTestTag, problemType); // then assertThat(generatedId).isNotNull(); @@ -54,7 +54,7 @@ void setUp() { assertThat(generatedId.getId()).startsWith("12240520"); // 문제 ID 중복 확인을 위해 existsById 호출 확인 - verify(problemRepository, atLeastOnce()).existsByProblemAdminId(any()); + verify(problemRepository, atLeastOnce()).existsByProblemCustomId(any()); } @@ -63,12 +63,12 @@ void setUp() { // given int 문제번호 = 2; ProblemType problemType = ProblemType.GICHUL_PROBLEM; - when(problemRepository.existsByProblemAdminId(any())) + when(problemRepository.existsByProblemCustomId(any())) .thenReturn(true) // 첫 번째 생성된 ID는 중복됨 .thenReturn(false); // 두 번째는 중복 없음 // when - ProblemAdminId generatedId = problemAdminIdService.nextId(문제번호, practiceTestTag, problemType); + ProblemCustomId generatedId = problemAdminIdService.nextId(문제번호, practiceTestTag, problemType); // then assertThat(generatedId).isNotNull(); @@ -76,6 +76,6 @@ void setUp() { assertThat(generatedId.getId()).startsWith("12240502"); // 중복된 ID가 나왔으므로 existsById가 최소 두 번 이상 호출되었는지 확인 - verify(problemRepository, atLeast(2)).existsByProblemAdminId(any()); + verify(problemRepository, atLeast(2)).existsByProblemCustomId(any()); } } \ No newline at end of file diff --git a/src/test/java/com/moplus/moplus_server/domain/problem/service/ProblemUpdateServiceTest.java b/src/test/java/com/moplus/moplus_server/domain/problem/service/ProblemUpdateServiceTest.java index 6760214..9b9145d 100644 --- a/src/test/java/com/moplus/moplus_server/domain/problem/service/ProblemUpdateServiceTest.java +++ b/src/test/java/com/moplus/moplus_server/domain/problem/service/ProblemUpdateServiceTest.java @@ -6,7 +6,7 @@ import com.moplus.moplus_server.domain.problem.domain.childProblem.ChildProblem; import com.moplus.moplus_server.domain.problem.domain.problem.AnswerType; import com.moplus.moplus_server.domain.problem.domain.problem.Problem; -import com.moplus.moplus_server.domain.problem.domain.problem.ProblemAdminId; +import com.moplus.moplus_server.domain.problem.domain.problem.ProblemCustomId; import com.moplus.moplus_server.domain.problem.domain.problem.ProblemType; import com.moplus.moplus_server.domain.problem.dto.request.ChildProblemUpdateRequest; import com.moplus.moplus_server.domain.problem.dto.request.ProblemUpdateRequest; @@ -15,7 +15,6 @@ import com.moplus.moplus_server.global.error.exception.NotFoundException; import java.util.List; import java.util.Set; -import java.util.stream.IntStream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -37,35 +36,29 @@ class ProblemUpdateServiceTest { @Autowired private ProblemRepository problemRepository; - private ProblemAdminId problemAdminId; + private ProblemCustomId problemCustomId; private ProblemUpdateRequest problemUpdateRequest; @BeforeEach void setUp() { - problemAdminId = new ProblemAdminId("240520012001"); - - // 새 자식 문제 추가 - ChildProblemUpdateRequest newChildProblem = new ChildProblemUpdateRequest( - null, - "newChild.png", - AnswerType.SHORT_STRING_ANSWER, - "새로운 정답", - Set.of(1L, 2L), - 1 - ); + problemCustomId = new ProblemCustomId("240520012001"); // 기존 자식 문제 업데이트 - ChildProblemUpdateRequest updateChildProblem = new ChildProblemUpdateRequest( - 1L, // 기존 자식 문제 ID - "updatedChild.png", + ChildProblemUpdateRequest updateChildProblem1 = new ChildProblemUpdateRequest( + 1L, + "updatedChild1.png", AnswerType.MULTIPLE_CHOICE, "2", - Set.of(2L, 3L), - 0 + Set.of(2L, 3L) ); - // 기존 자식 문제 삭제 - List deleteChildProblem = List.of(2L); // 삭제할 자식 문제 ID + ChildProblemUpdateRequest updateChildProblem2 = new ChildProblemUpdateRequest( + 2L, + "updatedChild2.png", + AnswerType.SHORT_STRING_ANSWER, + "23", + Set.of(3L, 4L) + ); problemUpdateRequest = new ProblemUpdateRequest( ProblemType.VARIANT_PROBLEM, @@ -83,8 +76,7 @@ void setUp() { "updatedSeniorTip.png", List.of("prescription1.png", "prescription2.png"), // List으로 변경 AnswerType.SHORT_STRING_ANSWER, - List.of(newChildProblem, updateChildProblem), - deleteChildProblem + List.of(updateChildProblem1, updateChildProblem2) ); } @@ -99,7 +91,7 @@ class 문제_업데이트_정상_동작 { // then assertThat(response).isNotNull(); - assertThat(response.problemId()).startsWith("22230310"); // 문제 ID 확인 + assertThat(response.problemCustomId()).startsWith("22230310"); // 문제 ID 확인 assertThat(response.problemType()).isEqualTo(ProblemType.VARIANT_PROBLEM); assertThat(response.practiceTestId()).isEqualTo(2L); assertThat(response.number()).isEqualTo(10); @@ -126,35 +118,21 @@ class 문제_업데이트_정상_동작 { // 자식 문제 검증 List childProblems = updatedProblem.getChildProblems(); - assertThat(childProblems).hasSize(2); // 기존 2개 → 1개 삭제, 1개 추가 후 2개 + assertThat(childProblems).hasSize(2); // 첫 번째 자식 문제 검증 (업데이트된 기존 문제) ChildProblem updatedChild = childProblems.get(0); - assertThat(updatedChild.getId()).isEqualTo(1L); - assertThat(updatedChild.getImageUrl()).isEqualTo("updatedChild.png"); + assertThat(updatedChild.getImageUrl()).isEqualTo("updatedChild1.png"); assertThat(updatedChild.getAnswerType()).isEqualTo(AnswerType.MULTIPLE_CHOICE); assertThat(updatedChild.getAnswer()).isEqualTo("2"); assertThat(updatedChild.getConceptTagIds()).containsExactlyInAnyOrderElementsOf(Set.of(2L, 3L)); - assertThat(updatedChild.getSequence()).isEqualTo(0); // 두 번째 자식 문제 검증 (새로 추가된 문제) ChildProblem newChild = childProblems.get(1); - assertThat(newChild.getImageUrl()).isEqualTo("newChild.png"); + assertThat(newChild.getImageUrl()).isEqualTo("updatedChild2.png"); assertThat(newChild.getAnswerType()).isEqualTo(AnswerType.SHORT_STRING_ANSWER); - assertThat(newChild.getAnswer()).isEqualTo("새로운 정답"); - assertThat(newChild.getConceptTagIds()).containsExactlyInAnyOrderElementsOf(Set.of(1L, 2L)); - assertThat(newChild.getSequence()).isEqualTo(1); - - // 부모 문제의 conceptTagIds가 자식 문제의 conceptTagIds를 모두 포함하는지 검증 - Set problemTags = updatedProblem.getConceptTagIds(); - childProblems.forEach(child -> { - assertThat(problemTags).containsAll(child.getConceptTagIds()); - }); - - // 자식 문제 순서가 올바르게 정렬되었는지 확인 - IntStream.range(0, childProblems.size()).forEach(i -> { - assertThat(childProblems.get(i).getSequence()).isEqualTo(i); - }); + assertThat(newChild.getAnswer()).isEqualTo("23"); + assertThat(newChild.getConceptTagIds()).containsExactlyInAnyOrderElementsOf(Set.of(3L, 4L)); } } @@ -190,7 +168,6 @@ class 문제_업데이트_예외_처리 { "updatedSeniorTip.png", List.of("prescription1.png"), // List으로 변경 AnswerType.SHORT_STRING_ANSWER, - List.of(), List.of() ); diff --git a/src/test/resources/insert-problem.sql b/src/test/resources/insert-problem.sql index add8767..03bc152 100644 --- a/src/test/resources/insert-problem.sql +++ b/src/test/resources/insert-problem.sql @@ -1,5 +1,5 @@ INSERT INTO problem (problem_id, - problem_admin_id, + problem_custom_id, practice_test_id, number, problem_type, @@ -28,7 +28,7 @@ INSERT INTO child_problem (child_problem_id, answer, sequence) VALUES (1, 1, 'child1.png', 'MULTIPLE_CHOICE', '1', 0), - (2, 1, 'child2.png', 'SHORT_STRING_ANSWER', '정답2', 0); + (2, 1, 'child2.png', 'SHORT_STRING_ANSWER', '정답2', 1); -- 문제-컨셉 태그 연결 (기존 문제의 ConceptTag) INSERT INTO problem_concept (problem_id, -- Long 타입으로 변경 (Problem의 id 참조) diff --git a/src/test/resources/insert-problem2.sql b/src/test/resources/insert-problem2.sql index b999009..75f6573 100644 --- a/src/test/resources/insert-problem2.sql +++ b/src/test/resources/insert-problem2.sql @@ -1,6 +1,6 @@ -- problem 데이터 삽입 INSERT INTO problem (problem_id, - problem_admin_id, + problem_custom_id, practice_test_id, number, problem_type, @@ -62,7 +62,7 @@ VALUES (1, 1), -- 유효하지 않은 문제 데이터 삽입 INSERT INTO problem (problem_id, - problem_admin_id, + problem_custom_id, practice_test_id, number, problem_type,