From 8b2fdfb695e956c0c96a20c694f7696c013caac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=B8=EC=A4=80?= <74056843+sejoon00@users.noreply.github.com> Date: Sat, 1 Feb 2025 21:42:19 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[feat/#27]=20=EB=AC=B8=ED=95=AD=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- build.gradle | 10 +++ .../domain/concept/domain/QConceptTag.java | 47 ++++++++++ .../domain/member/domain/QMember.java | 53 ++++++++++++ .../domain/problem/domain/QAnswer.java | 37 ++++++++ .../domain/childProblem/QChildProblem.java | 67 +++++++++++++++ .../domain/practiceTest/QPracticeTestTag.java | 47 ++++++++++ .../problem/domain/problem/QProblem.java | 86 +++++++++++++++++++ .../problem/domain/problem/QProblemId.java | 37 ++++++++ .../mapper/ChildProblemMapperImpl.java | 2 +- .../service/mapper/ProblemMapperImpl.java | 2 +- .../entity/QDetailResultApplication.java | 51 +++++++++++ .../TestResult/entity/QEstimatedRating.java | 43 ++++++++++ .../TestResult/entity/QIncorrectProblem.java | 71 +++++++++++++++ .../v0/TestResult/entity/QTestResult.java | 51 +++++++++++ .../v0/practiceTest/domain/QPracticeTest.java | 61 +++++++++++++ .../practiceTest/domain/QProblemForTest.java | 82 ++++++++++++++++++ .../domain/QProblemImageForTest.java | 43 ++++++++++ .../v0/practiceTest/domain/QRatingTable.java | 54 ++++++++++++ .../global/common/QBaseEntity.java | 39 +++++++++ .../controller/ProblemSearchController.java | 34 ++++++++ .../domain/practiceTest/PracticeTestTag.java | 1 + .../response/ConceptTagSearchResponse.java | 16 ++++ .../response/ProblemSearchGetResponse.java | 24 ++++++ .../problem/repository/ProblemRepository.java | 3 +- .../repository/ProblemSearchRepository.java | 8 ++ .../ProblemSearchRepositoryImpl.java | 73 ++++++++++++++++ .../config/querydsl/QuerydslConfig.java | 20 +++++ .../ProblemSearchRepositoryImplTest.java | 73 ++++++++++++++++ .../service/ProblemSaveServiceTest.java | 4 +- src/test/resources/application-h2test.yml | 5 ++ src/test/resources/concept-tag.sql | 19 ++-- src/test/resources/insert-problem.sql | 10 ++- src/test/resources/practice-test-tag.sql | 4 +- 34 files changed, 1155 insertions(+), 25 deletions(-) create mode 100644 src/main/generated/com/moplus/moplus_server/domain/concept/domain/QConceptTag.java create mode 100644 src/main/generated/com/moplus/moplus_server/domain/member/domain/QMember.java create mode 100644 src/main/generated/com/moplus/moplus_server/domain/problem/domain/QAnswer.java create mode 100644 src/main/generated/com/moplus/moplus_server/domain/problem/domain/childProblem/QChildProblem.java create mode 100644 src/main/generated/com/moplus/moplus_server/domain/problem/domain/practiceTest/QPracticeTestTag.java create mode 100644 src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblem.java create mode 100644 src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblemId.java create mode 100644 src/main/generated/com/moplus/moplus_server/domain/v0/DetailResultApplication/entity/QDetailResultApplication.java create mode 100644 src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QEstimatedRating.java create mode 100644 src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QIncorrectProblem.java create mode 100644 src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QTestResult.java create mode 100644 src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QPracticeTest.java create mode 100644 src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QProblemForTest.java create mode 100644 src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QProblemImageForTest.java create mode 100644 src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QRatingTable.java create mode 100644 src/main/generated/com/moplus/moplus_server/global/common/QBaseEntity.java create mode 100644 src/main/java/com/moplus/moplus_server/domain/problem/controller/ProblemSearchController.java create mode 100644 src/main/java/com/moplus/moplus_server/domain/problem/dto/response/ConceptTagSearchResponse.java create mode 100644 src/main/java/com/moplus/moplus_server/domain/problem/dto/response/ProblemSearchGetResponse.java create mode 100644 src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepository.java create mode 100644 src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryImpl.java create mode 100644 src/main/java/com/moplus/moplus_server/global/config/querydsl/QuerydslConfig.java create mode 100644 src/test/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryImplTest.java diff --git a/.gitignore b/.gitignore index 4125023..227c6ad 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ - .env ### STS ### @@ -37,3 +36,5 @@ out/ ### VS Code ### .vscode/ + +src/test/resources/insert-search-problem.sql diff --git a/build.gradle b/build.gradle index f4e44f9..4409a35 100644 --- a/build.gradle +++ b/build.gradle @@ -23,6 +23,7 @@ repositories { mavenCentral() } + dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' @@ -61,6 +62,15 @@ dependencies { annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3' annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0' + // JPA + implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0' + implementation 'jakarta.annotation:jakarta.annotation-api:2.1.1' + + // QueryDSL + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" } tasks.named('test') { diff --git a/src/main/generated/com/moplus/moplus_server/domain/concept/domain/QConceptTag.java b/src/main/generated/com/moplus/moplus_server/domain/concept/domain/QConceptTag.java new file mode 100644 index 0000000..454956c --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/concept/domain/QConceptTag.java @@ -0,0 +1,47 @@ +package com.moplus.moplus_server.domain.concept.domain; + +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; + + +/** + * QConceptTag is a Querydsl query type for ConceptTag + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QConceptTag extends EntityPathBase { + + private static final long serialVersionUID = 652954745L; + + public static final QConceptTag conceptTag = new QConceptTag("conceptTag"); + + public final com.moplus.moplus_server.global.common.QBaseEntity _super = new com.moplus.moplus_server.global.common.QBaseEntity(this); + + //inherited + public final DateTimePath createdDate = _super.createdDate; + + public final NumberPath id = createNumber("id", Long.class); + + public final StringPath name = createString("name"); + + //inherited + public final DateTimePath updatedDate = _super.updatedDate; + + public QConceptTag(String variable) { + super(ConceptTag.class, forVariable(variable)); + } + + public QConceptTag(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QConceptTag(PathMetadata metadata) { + super(ConceptTag.class, metadata); + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/domain/member/domain/QMember.java b/src/main/generated/com/moplus/moplus_server/domain/member/domain/QMember.java new file mode 100644 index 0000000..ad5aeae --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/member/domain/QMember.java @@ -0,0 +1,53 @@ +package com.moplus.moplus_server.domain.member.domain; + +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; + + +/** + * QMember is a Querydsl query type for Member + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QMember extends EntityPathBase { + + private static final long serialVersionUID = -705761779L; + + public static final QMember member = new QMember("member1"); + + public final com.moplus.moplus_server.global.common.QBaseEntity _super = new com.moplus.moplus_server.global.common.QBaseEntity(this); + + //inherited + public final DateTimePath createdDate = _super.createdDate; + + public final StringPath email = createString("email"); + + public final NumberPath id = createNumber("id", Long.class); + + public final StringPath name = createString("name"); + + public final StringPath password = createString("password"); + + public final EnumPath role = createEnum("role", MemberRole.class); + + //inherited + public final DateTimePath updatedDate = _super.updatedDate; + + public QMember(String variable) { + super(Member.class, forVariable(variable)); + } + + public QMember(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QMember(PathMetadata metadata) { + super(Member.class, metadata); + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/domain/problem/domain/QAnswer.java b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/QAnswer.java new file mode 100644 index 0000000..cdda3fe --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/QAnswer.java @@ -0,0 +1,37 @@ +package com.moplus.moplus_server.domain.problem.domain; + +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; + + +/** + * QAnswer is a Querydsl query type for Answer + */ +@Generated("com.querydsl.codegen.DefaultEmbeddableSerializer") +public class QAnswer extends BeanPath { + + private static final long serialVersionUID = 983834524L; + + public static final QAnswer answer = new QAnswer("answer"); + + public final StringPath value = createString("value"); + + public QAnswer(String variable) { + super(Answer.class, forVariable(variable)); + } + + public QAnswer(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QAnswer(PathMetadata metadata) { + super(Answer.class, metadata); + } + +} + 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 new file mode 100644 index 0000000..8dad7ae --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/childProblem/QChildProblem.java @@ -0,0 +1,67 @@ +package com.moplus.moplus_server.domain.problem.domain.childProblem; + +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; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QChildProblem is a Querydsl query type for ChildProblem + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QChildProblem extends EntityPathBase { + + private static final long serialVersionUID = 1030139824L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QChildProblem childProblem = new QChildProblem("childProblem"); + + public final com.moplus.moplus_server.global.common.QBaseEntity _super = new com.moplus.moplus_server.global.common.QBaseEntity(this); + + public final com.moplus.moplus_server.domain.problem.domain.QAnswer answer; + + public final SetPath> conceptTagIds = this.>createSet("conceptTagIds", Long.class, NumberPath.class, PathInits.DIRECT2); + + //inherited + public final DateTimePath createdDate = _super.createdDate; + + public final NumberPath id = createNumber("id", Long.class); + + public final StringPath imageUrl = createString("imageUrl"); + + public final EnumPath problemType = createEnum("problemType", com.moplus.moplus_server.domain.problem.domain.problem.ProblemType.class); + + public final NumberPath sequence = createNumber("sequence", Integer.class); + + //inherited + public final DateTimePath updatedDate = _super.updatedDate; + + public QChildProblem(String variable) { + this(ChildProblem.class, forVariable(variable), INITS); + } + + public QChildProblem(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QChildProblem(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QChildProblem(PathMetadata metadata, PathInits inits) { + this(ChildProblem.class, metadata, inits); + } + + public QChildProblem(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.answer = inits.isInitialized("answer") ? new com.moplus.moplus_server.domain.problem.domain.QAnswer(forProperty("answer")) : null; + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/domain/problem/domain/practiceTest/QPracticeTestTag.java b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/practiceTest/QPracticeTestTag.java new file mode 100644 index 0000000..83588ac --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/practiceTest/QPracticeTestTag.java @@ -0,0 +1,47 @@ +package com.moplus.moplus_server.domain.problem.domain.practiceTest; + +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; + + +/** + * QPracticeTestTag is a Querydsl query type for PracticeTestTag + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QPracticeTestTag extends EntityPathBase { + + private static final long serialVersionUID = -2120162934L; + + public static final QPracticeTestTag practiceTestTag = new QPracticeTestTag("practiceTestTag"); + + public final StringPath area = createString("area"); + + public final NumberPath id = createNumber("id", Long.class); + + public final NumberPath month = createNumber("month", Integer.class); + + public final StringPath name = createString("name"); + + public final EnumPath subject = createEnum("subject", Subject.class); + + public final NumberPath year = createNumber("year", Integer.class); + + public QPracticeTestTag(String variable) { + super(PracticeTestTag.class, forVariable(variable)); + } + + public QPracticeTestTag(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QPracticeTestTag(PathMetadata metadata) { + super(PracticeTestTag.class, metadata); + } + +} + 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 new file mode 100644 index 0000000..fb35ae5 --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblem.java @@ -0,0 +1,86 @@ +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; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QProblem is a Querydsl query type for Problem + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QProblem extends EntityPathBase { + + private static final long serialVersionUID = -1319796686L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QProblem problem = new QProblem("problem"); + + public final com.moplus.moplus_server.global.common.QBaseEntity _super = new com.moplus.moplus_server.global.common.QBaseEntity(this); + + public final com.moplus.moplus_server.domain.problem.domain.QAnswer answer; + + public final ListPath childProblems = this.createList("childProblems", com.moplus.moplus_server.domain.problem.domain.childProblem.ChildProblem.class, com.moplus.moplus_server.domain.problem.domain.childProblem.QChildProblem.class, PathInits.DIRECT2); + + public final StringPath comment = createString("comment"); + + public final SetPath> conceptTagIds = this.>createSet("conceptTagIds", Long.class, NumberPath.class, PathInits.DIRECT2); + + //inherited + public final DateTimePath createdDate = _super.createdDate; + + public final QProblemId id; + + public final BooleanPath isPublished = createBoolean("isPublished"); + + public final BooleanPath isVariation = createBoolean("isVariation"); + + public final StringPath mainAnalysisImageUrl = createString("mainAnalysisImageUrl"); + + public final StringPath mainProblemImageUrl = createString("mainProblemImageUrl"); + + public final NumberPath number = createNumber("number", Integer.class); + + public final NumberPath practiceTestId = createNumber("practiceTestId", Long.class); + + public final StringPath prescriptionImageUrl = createString("prescriptionImageUrl"); + + public final EnumPath problemType = createEnum("problemType", ProblemType.class); + + public final StringPath readingTipImageUrl = createString("readingTipImageUrl"); + + public final StringPath seniorTipImageUrl = createString("seniorTipImageUrl"); + + //inherited + public final DateTimePath updatedDate = _super.updatedDate; + + public QProblem(String variable) { + this(Problem.class, forVariable(variable), INITS); + } + + public QProblem(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QProblem(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QProblem(PathMetadata metadata, PathInits inits) { + this(Problem.class, metadata, inits); + } + + public QProblem(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.answer = inits.isInitialized("answer") ? new com.moplus.moplus_server.domain.problem.domain.QAnswer(forProperty("answer")) : null; + this.id = inits.isInitialized("id") ? new QProblemId(forProperty("id")) : null; + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblemId.java b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblemId.java new file mode 100644 index 0000000..ca7809a --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/problem/domain/problem/QProblemId.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; + + +/** + * QProblemId is a Querydsl query type for ProblemId + */ +@Generated("com.querydsl.codegen.DefaultEmbeddableSerializer") +public class QProblemId extends BeanPath { + + private static final long serialVersionUID = -1309260563L; + + public static final QProblemId problemId = new QProblemId("problemId"); + + public final StringPath id = createString("id"); + + public QProblemId(String variable) { + super(ProblemId.class, forVariable(variable)); + } + + public QProblemId(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QProblemId(PathMetadata metadata) { + super(ProblemId.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 e76d12e..53469fa 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-01-30T21:11:35+0900", + date = "2025-01-31T02:55:57+0900", comments = "version: 1.6.3, compiler: javac, environment: Java 17.0.10 (JetBrains s.r.o.)" ) @Component 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 dc059cb..ba945c8 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 @@ -12,7 +12,7 @@ @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2025-01-30T21:11:35+0900", + date = "2025-01-31T02:55:56+0900", comments = "version: 1.6.3, compiler: javac, environment: Java 17.0.10 (JetBrains s.r.o.)" ) @Component diff --git a/src/main/generated/com/moplus/moplus_server/domain/v0/DetailResultApplication/entity/QDetailResultApplication.java b/src/main/generated/com/moplus/moplus_server/domain/v0/DetailResultApplication/entity/QDetailResultApplication.java new file mode 100644 index 0000000..9ff44d0 --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/v0/DetailResultApplication/entity/QDetailResultApplication.java @@ -0,0 +1,51 @@ +package com.moplus.moplus_server.domain.v0.DetailResultApplication.entity; + +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; + + +/** + * QDetailResultApplication is a Querydsl query type for DetailResultApplication + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QDetailResultApplication extends EntityPathBase { + + private static final long serialVersionUID = 215702330L; + + public static final QDetailResultApplication detailResultApplication = new QDetailResultApplication("detailResultApplication"); + + public final com.moplus.moplus_server.global.common.QBaseEntity _super = new com.moplus.moplus_server.global.common.QBaseEntity(this); + + //inherited + public final DateTimePath createdDate = _super.createdDate; + + public final NumberPath id = createNumber("id", Long.class); + + public final StringPath name = createString("name"); + + public final StringPath phoneNumber = createString("phoneNumber"); + + public final NumberPath testResultId = createNumber("testResultId", Long.class); + + //inherited + public final DateTimePath updatedDate = _super.updatedDate; + + public QDetailResultApplication(String variable) { + super(DetailResultApplication.class, forVariable(variable)); + } + + public QDetailResultApplication(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QDetailResultApplication(PathMetadata metadata) { + super(DetailResultApplication.class, metadata); + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QEstimatedRating.java b/src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QEstimatedRating.java new file mode 100644 index 0000000..7dbaf22 --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QEstimatedRating.java @@ -0,0 +1,43 @@ +package com.moplus.moplus_server.domain.v0.TestResult.entity; + +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; + + +/** + * QEstimatedRating is a Querydsl query type for EstimatedRating + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QEstimatedRating extends EntityPathBase { + + private static final long serialVersionUID = -1923088138L; + + public static final QEstimatedRating estimatedRating1 = new QEstimatedRating("estimatedRating1"); + + public final NumberPath estimatedRating = createNumber("estimatedRating", Integer.class); + + public final NumberPath id = createNumber("id", Long.class); + + public final StringPath ratingProvider = createString("ratingProvider"); + + public final NumberPath testResultId = createNumber("testResultId", Long.class); + + public QEstimatedRating(String variable) { + super(EstimatedRating.class, forVariable(variable)); + } + + public QEstimatedRating(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QEstimatedRating(PathMetadata metadata) { + super(EstimatedRating.class, metadata); + } + +} + 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 new file mode 100644 index 0000000..b73be8c --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QIncorrectProblem.java @@ -0,0 +1,71 @@ +package com.moplus.moplus_server.domain.v0.TestResult.entity; + +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; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QIncorrectProblem is a Querydsl query type for IncorrectProblem + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QIncorrectProblem extends EntityPathBase { + + private static final long serialVersionUID = 1019670237L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QIncorrectProblem incorrectProblem = new QIncorrectProblem("incorrectProblem"); + + public final com.moplus.moplus_server.global.common.QBaseEntity _super = new com.moplus.moplus_server.global.common.QBaseEntity(this); + + public final NumberPath correctRate = createNumber("correctRate", Double.class); + + //inherited + public final DateTimePath createdDate = _super.createdDate; + + public final NumberPath id = createNumber("id", Long.class); + + public final StringPath incorrectAnswer = createString("incorrectAnswer"); + + public final NumberPath point = createNumber("point", Integer.class); + + public final NumberPath practiceTestId = createNumber("practiceTestId", Long.class); + + public final NumberPath problemId = createNumber("problemId", Long.class); + + public final StringPath problemNumber = createString("problemNumber"); + + public final QTestResult testResult; + + //inherited + public final DateTimePath updatedDate = _super.updatedDate; + + public QIncorrectProblem(String variable) { + this(IncorrectProblem.class, forVariable(variable), INITS); + } + + public QIncorrectProblem(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QIncorrectProblem(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QIncorrectProblem(PathMetadata metadata, PathInits inits) { + this(IncorrectProblem.class, metadata, inits); + } + + public QIncorrectProblem(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.testResult = inits.isInitialized("testResult") ? new QTestResult(forProperty("testResult")) : null; + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QTestResult.java b/src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QTestResult.java new file mode 100644 index 0000000..5f8fb8b --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QTestResult.java @@ -0,0 +1,51 @@ +package com.moplus.moplus_server.domain.v0.TestResult.entity; + +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; + + +/** + * QTestResult is a Querydsl query type for TestResult + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QTestResult extends EntityPathBase { + + private static final long serialVersionUID = -1200752334L; + + public static final QTestResult testResult = new QTestResult("testResult"); + + public final com.moplus.moplus_server.global.common.QBaseEntity _super = new com.moplus.moplus_server.global.common.QBaseEntity(this); + + //inherited + public final DateTimePath createdDate = _super.createdDate; + + public final NumberPath id = createNumber("id", Long.class); + + public final NumberPath practiceTestId = createNumber("practiceTestId", Long.class); + + public final NumberPath score = createNumber("score", Integer.class); + + public final ComparablePath solvingTime = createComparable("solvingTime", java.time.Duration.class); + + //inherited + public final DateTimePath updatedDate = _super.updatedDate; + + public QTestResult(String variable) { + super(TestResult.class, forVariable(variable)); + } + + public QTestResult(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QTestResult(PathMetadata metadata) { + super(TestResult.class, metadata); + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QPracticeTest.java b/src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QPracticeTest.java new file mode 100644 index 0000000..38ac923 --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QPracticeTest.java @@ -0,0 +1,61 @@ +package com.moplus.moplus_server.domain.v0.practiceTest.domain; + +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; + + +/** + * QPracticeTest is a Querydsl query type for PracticeTest + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QPracticeTest extends EntityPathBase { + + private static final long serialVersionUID = -700271987L; + + public static final QPracticeTest practiceTest = new QPracticeTest("practiceTest"); + + public final com.moplus.moplus_server.global.common.QBaseEntity _super = new com.moplus.moplus_server.global.common.QBaseEntity(this); + + public final ComparablePath averageSolvingTime = createComparable("averageSolvingTime", java.time.Duration.class); + + //inherited + public final DateTimePath createdDate = _super.createdDate; + + public final NumberPath id = createNumber("id", Long.class); + + public final StringPath name = createString("name"); + + public final StringPath provider = createString("provider"); + + public final NumberPath publicationYear = createNumber("publicationYear", Integer.class); + + public final StringPath round = createString("round"); + + public final NumberPath solvesCount = createNumber("solvesCount", Integer.class); + + public final EnumPath subject = createEnum("subject", com.moplus.moplus_server.domain.problem.domain.practiceTest.Subject.class); + + //inherited + public final DateTimePath updatedDate = _super.updatedDate; + + public final NumberPath viewCount = createNumber("viewCount", Long.class); + + public QPracticeTest(String variable) { + super(PracticeTest.class, forVariable(variable)); + } + + public QPracticeTest(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QPracticeTest(PathMetadata metadata) { + super(PracticeTest.class, metadata); + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QProblemForTest.java b/src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QProblemForTest.java new file mode 100644 index 0000000..031ef66 --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QProblemForTest.java @@ -0,0 +1,82 @@ +package com.moplus.moplus_server.domain.v0.practiceTest.domain; + +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; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QProblemForTest is a Querydsl query type for ProblemForTest + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QProblemForTest extends EntityPathBase { + + private static final long serialVersionUID = 159101820L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QProblemForTest problemForTest = new QProblemForTest("problemForTest"); + + public final com.moplus.moplus_server.global.common.QBaseEntity _super = new com.moplus.moplus_server.global.common.QBaseEntity(this); + + public final StringPath answer = createString("answer"); + + public final EnumPath answerFormat = createEnum("answerFormat", AnswerFormat.class); + + public final StringPath conceptType = createString("conceptType"); + + public final NumberPath correctRate = createNumber("correctRate", Double.class); + + //inherited + public final DateTimePath createdDate = _super.createdDate; + + public final NumberPath id = createNumber("id", Long.class); + + public final QProblemImageForTest image; + + public final NumberPath incorrectNum = createNumber("incorrectNum", Long.class); + + public final NumberPath point = createNumber("point", Integer.class); + + public final QPracticeTest practiceTest; + + public final StringPath problemNumber = createString("problemNumber"); + + public final EnumPath problemRating = createEnum("problemRating", ProblemRating.class); + + public final StringPath subunit = createString("subunit"); + + public final StringPath unit = createString("unit"); + + //inherited + public final DateTimePath updatedDate = _super.updatedDate; + + public QProblemForTest(String variable) { + this(ProblemForTest.class, forVariable(variable), INITS); + } + + public QProblemForTest(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QProblemForTest(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QProblemForTest(PathMetadata metadata, PathInits inits) { + this(ProblemForTest.class, metadata, inits); + } + + public QProblemForTest(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.image = inits.isInitialized("image") ? new QProblemImageForTest(forProperty("image")) : null; + this.practiceTest = inits.isInitialized("practiceTest") ? new QPracticeTest(forProperty("practiceTest")) : null; + } + +} + 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 new file mode 100644 index 0000000..b7e7668 --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QProblemImageForTest.java @@ -0,0 +1,43 @@ +package com.moplus.moplus_server.domain.v0.practiceTest.domain; + +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; + + +/** + * QProblemImageForTest is a Querydsl query type for ProblemImageForTest + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QProblemImageForTest extends EntityPathBase { + + private static final long serialVersionUID = 1499588927L; + + public static final QProblemImageForTest problemImageForTest = new QProblemImageForTest("problemImageForTest"); + + public final StringPath fileName = createString("fileName"); + + public final NumberPath id = createNumber("id", Long.class); + + public final StringPath imageUrl = createString("imageUrl"); + + public final NumberPath problemId = createNumber("problemId", Long.class); + + public QProblemImageForTest(String variable) { + super(ProblemImageForTest.class, forVariable(variable)); + } + + public QProblemImageForTest(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QProblemImageForTest(PathMetadata metadata) { + super(ProblemImageForTest.class, metadata); + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QRatingTable.java b/src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QRatingTable.java new file mode 100644 index 0000000..842309d --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/domain/v0/practiceTest/domain/QRatingTable.java @@ -0,0 +1,54 @@ +package com.moplus.moplus_server.domain.v0.practiceTest.domain; + +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; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QRatingTable is a Querydsl query type for RatingTable + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QRatingTable extends EntityPathBase { + + private static final long serialVersionUID = -1985854959L; + + public static final QRatingTable ratingTable = new QRatingTable("ratingTable"); + + public final com.moplus.moplus_server.global.common.QBaseEntity _super = new com.moplus.moplus_server.global.common.QBaseEntity(this); + + //inherited + public final DateTimePath createdDate = _super.createdDate; + + public final NumberPath id = createNumber("id", Long.class); + + public final NumberPath practiceTestId = createNumber("practiceTestId", Long.class); + + public final StringPath ratingProvider = createString("ratingProvider"); + + public final ListPath> ratingRows = this.>createList("ratingRows", RatingRow.class, SimplePath.class, PathInits.DIRECT2); + + public final EnumPath subject = createEnum("subject", com.moplus.moplus_server.domain.problem.domain.practiceTest.Subject.class); + + //inherited + public final DateTimePath updatedDate = _super.updatedDate; + + public QRatingTable(String variable) { + super(RatingTable.class, forVariable(variable)); + } + + public QRatingTable(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QRatingTable(PathMetadata metadata) { + super(RatingTable.class, metadata); + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/global/common/QBaseEntity.java b/src/main/generated/com/moplus/moplus_server/global/common/QBaseEntity.java new file mode 100644 index 0000000..8c5284b --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/global/common/QBaseEntity.java @@ -0,0 +1,39 @@ +package com.moplus.moplus_server.global.common; + +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; + + +/** + * QBaseEntity is a Querydsl query type for BaseEntity + */ +@Generated("com.querydsl.codegen.DefaultSupertypeSerializer") +public class QBaseEntity extends EntityPathBase { + + private static final long serialVersionUID = -1014955751L; + + public static final QBaseEntity baseEntity = new QBaseEntity("baseEntity"); + + public final DateTimePath createdDate = createDateTime("createdDate", java.time.LocalDateTime.class); + + public final DateTimePath updatedDate = createDateTime("updatedDate", java.time.LocalDateTime.class); + + public QBaseEntity(String variable) { + super(BaseEntity.class, forVariable(variable)); + } + + public QBaseEntity(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QBaseEntity(PathMetadata metadata) { + super(BaseEntity.class, metadata); + } + +} + 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 new file mode 100644 index 0000000..cf8c088 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/domain/problem/controller/ProblemSearchController.java @@ -0,0 +1,34 @@ +package com.moplus.moplus_server.domain.problem.controller; + +import com.moplus.moplus_server.domain.problem.dto.response.ProblemSearchGetResponse; +import com.moplus.moplus_server.domain.problem.repository.ProblemRepository; +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.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/problems") +@RequiredArgsConstructor +public class ProblemSearchController { + + private final ProblemRepository problemRepository; + + @GetMapping("/search") + @Operation( + summary = "문제 검색", + description = "문항 ID, 문제명, 개념 태그리스트로 문제를 검색합니다. 개념 태그리스트는 OR 조건으로 검색하며 값이 없으면 쿼리파라미터에서 빼주세요" + ) + public ResponseEntity> search( + @RequestParam(value = "problemId", required = false) String problemId, + @RequestParam(value = "comment", required = false) String comment, + @RequestParam(value = "conceptTagIds", required = false) List conceptTagIds + ) { + List problems = problemRepository.search(problemId, comment, conceptTagIds); + return ResponseEntity.ok(problems); + } +} diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/domain/practiceTest/PracticeTestTag.java b/src/main/java/com/moplus/moplus_server/domain/problem/domain/practiceTest/PracticeTestTag.java index 1782bd8..b21f78b 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/domain/practiceTest/PracticeTestTag.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/domain/practiceTest/PracticeTestTag.java @@ -19,6 +19,7 @@ public class PracticeTestTag { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "practice_test_tag_id") private Long id; private String name; diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/dto/response/ConceptTagSearchResponse.java b/src/main/java/com/moplus/moplus_server/domain/problem/dto/response/ConceptTagSearchResponse.java new file mode 100644 index 0000000..75e5c73 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/domain/problem/dto/response/ConceptTagSearchResponse.java @@ -0,0 +1,16 @@ +package com.moplus.moplus_server.domain.problem.dto.response; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class ConceptTagSearchResponse { + private Long id; + private String name; // 예시로 태그 이름을 추가 (필요에 따라 변경 가능) + + public ConceptTagSearchResponse(Long id, String name) { + this.id = id; + this.name = name; + } +} diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/dto/response/ProblemSearchGetResponse.java b/src/main/java/com/moplus/moplus_server/domain/problem/dto/response/ProblemSearchGetResponse.java new file mode 100644 index 0000000..7a1710b --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/domain/problem/dto/response/ProblemSearchGetResponse.java @@ -0,0 +1,24 @@ +package com.moplus.moplus_server.domain.problem.dto.response; + +import java.util.Set; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class ProblemSearchGetResponse { + private String problemId; + private String comment; + private String mainProblemImageUrl; + private Set conceptTagResponses; + + public ProblemSearchGetResponse(String problemId, String comment, String mainProblemImageUrl, + Set conceptTagResponses) { + this.problemId = problemId; + this.comment = comment; + this.mainProblemImageUrl = mainProblemImageUrl; + this.conceptTagResponses = conceptTagResponses; + } +} + + 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 494f0c9..1be5b7b 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 @@ -7,7 +7,7 @@ import com.moplus.moplus_server.global.error.exception.NotFoundException; import org.springframework.data.jpa.repository.JpaRepository; -public interface ProblemRepository extends JpaRepository { +public interface ProblemRepository extends JpaRepository, ProblemSearchRepository { boolean existsByPracticeTestIdAndNumber(Long practiceTestId, int number); @@ -23,7 +23,6 @@ default void existsByIdElseThrow(ProblemId problemId) { } } - default Problem findByIdElseThrow(ProblemId problemId) { return findById(problemId).orElseThrow(() -> new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND)); } diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepository.java b/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepository.java new file mode 100644 index 0000000..71b3939 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepository.java @@ -0,0 +1,8 @@ +package com.moplus.moplus_server.domain.problem.repository; + +import com.moplus.moplus_server.domain.problem.dto.response.ProblemSearchGetResponse; +import java.util.List; + +public interface ProblemSearchRepository { + List search(String problemId, String comment, List conceptTagIds); +} diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryImpl.java b/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryImpl.java new file mode 100644 index 0000000..86ecae2 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryImpl.java @@ -0,0 +1,73 @@ +package com.moplus.moplus_server.domain.problem.repository; + +import static com.moplus.moplus_server.domain.concept.domain.QConceptTag.conceptTag; +import static com.moplus.moplus_server.domain.problem.domain.problem.QProblem.problem; + +import com.moplus.moplus_server.domain.problem.domain.problem.Problem; +import com.moplus.moplus_server.domain.problem.dto.response.ConceptTagSearchResponse; +import com.moplus.moplus_server.domain.problem.dto.response.ProblemSearchGetResponse; +import com.querydsl.core.group.GroupBy; +import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; +import org.springframework.stereotype.Repository; + +@Repository +public class ProblemSearchRepositoryImpl extends QuerydslRepositorySupport implements ProblemSearchRepository { + + private final JPAQueryFactory queryFactory; + + @Autowired + public ProblemSearchRepositoryImpl(JPAQueryFactory queryFactory) { + super(Problem.class); + this.queryFactory = queryFactory; + } + + @Override + public List search(String problemId, String comment, List conceptTagIds) { + return queryFactory + .from(problem) + .where( + containsProblemId(problemId), + containsName(comment), + inConceptTagIds(conceptTagIds) + ) + .leftJoin(conceptTag).on(conceptTag.id.in(problem.conceptTagIds)) + .distinct() + .transform(GroupBy.groupBy(problem.id.id).list( + Projections.constructor(ProblemSearchGetResponse.class, + problem.id.id, + problem.comment, + problem.mainProblemImageUrl, + GroupBy.set( + Projections.constructor(ConceptTagSearchResponse.class, + conceptTag.id, + conceptTag.name + ) + ) + ) + )); + } + + //problemId 일부 포함 검색 + private BooleanExpression containsProblemId(String problemId) { + return (problemId == null || problemId.isEmpty()) ? null : problem.id.id.containsIgnoreCase(problemId); + } + + //name 조건 (포함 검색) + private BooleanExpression containsName(String comment) { + if (comment == null || comment.trim().isEmpty()) { + return null; + } + return problem.comment.containsIgnoreCase(comment.trim()); + } + + //conceptTagIds 조건 (하나라도 포함되면 조회) + private BooleanExpression inConceptTagIds(List conceptTagIds) { + return (conceptTagIds == null || conceptTagIds.isEmpty()) ? null + : problem.conceptTagIds.any().in(conceptTagIds); + } +} diff --git a/src/main/java/com/moplus/moplus_server/global/config/querydsl/QuerydslConfig.java b/src/main/java/com/moplus/moplus_server/global/config/querydsl/QuerydslConfig.java new file mode 100644 index 0000000..2889116 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/global/config/querydsl/QuerydslConfig.java @@ -0,0 +1,20 @@ +package com.moplus.moplus_server.global.config.querydsl; + +import com.querydsl.jpa.JPQLTemplates; +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class QuerydslConfig { + + @PersistenceContext + private EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory() { + return new JPAQueryFactory(JPQLTemplates.DEFAULT, entityManager); + } +} diff --git a/src/test/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryImplTest.java b/src/test/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryImplTest.java new file mode 100644 index 0000000..8a80fd8 --- /dev/null +++ b/src/test/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryImplTest.java @@ -0,0 +1,73 @@ +package com.moplus.moplus_server.domain.problem.repository; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.moplus.moplus_server.domain.problem.dto.response.ProblemSearchGetResponse; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@SpringBootTest +@ActiveProfiles("h2test") +@Sql({"/practice-test-tag.sql", "/concept-tag.sql", "/insert-problem.sql"}) +public class ProblemSearchRepositoryImplTest { + + @Autowired + private ProblemRepository problemSearchRepository; + + @Test + void problemId_일부_포함_검색() { + // when + List result = problemSearchRepository.search("240520012", null, null); + + // then + assertThat(result).hasSize(2); + assertThat(result).extracting(ProblemSearchGetResponse::getProblemId) + .containsExactlyInAnyOrder("240520012001", "240520012002"); + } + + @Test + void name_포함_검색() { + // when + List result = problemSearchRepository.search(null, "설명 1 ", null); + + // then + assertThat(result).hasSize(1); + assertThat(result.get(0).getProblemId()).isEqualTo("240520012001"); + } + + @Test + void conceptTagIds_하나라도_포함되면_조회() { + // when + List result = problemSearchRepository.search(null, null, List.of(3L)); + + // then + assertThat(result).hasSize(2); + assertThat(result).extracting(ProblemSearchGetResponse::getProblemId) + .containsExactlyInAnyOrder("240520012001", "240520012002"); + } + + @Test + void problemId_이름_conceptTagIds_모두_적용된_검색() { + // when + List result = problemSearchRepository.search("2405200120", "설명 1", List.of(1L)); + + // then + assertThat(result).hasSize(1); + assertThat(result.get(0).getProblemId()).isEqualTo("240520012001"); + } + + @Test + void 아무_조건도_없으면_모든_데이터_조회() { + // when + List result = problemSearchRepository.search(null, null, null); + + // then + assertThat(result).hasSize(2); + } +} \ No newline at end of file diff --git a/src/test/java/com/moplus/moplus_server/domain/problem/service/ProblemSaveServiceTest.java b/src/test/java/com/moplus/moplus_server/domain/problem/service/ProblemSaveServiceTest.java index 65db7b9..63abbe6 100644 --- a/src/test/java/com/moplus/moplus_server/domain/problem/service/ProblemSaveServiceTest.java +++ b/src/test/java/com/moplus/moplus_server/domain/problem/service/ProblemSaveServiceTest.java @@ -16,7 +16,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.jdbc.Sql; import org.springframework.transaction.annotation.Transactional; @@ -80,10 +79,10 @@ void setUp() { "prescription2.png", List.of(childProblem3, childProblem2, childProblem4, childProblem1) // 🔹 순서 유지 (0,1,2,3) ); + } @Test - @Rollback(value = false) void 정상동작() { // when @@ -113,7 +112,6 @@ void setUp() { } @Test - @Rollback(true) void 자식문제_올바른_순서_저장() { // when ProblemId createdProblemId = problemSaveService.createProblem(problemPostRequestOutOfOrder); diff --git a/src/test/resources/application-h2test.yml b/src/test/resources/application-h2test.yml index f1bf6e6..6095227 100644 --- a/src/test/resources/application-h2test.yml +++ b/src/test/resources/application-h2test.yml @@ -3,6 +3,11 @@ spring: hibernate: ddl-auto: create-drop show-sql: true + properties: + hibernate: + format_sql: true + use_sql_comments: true + highlight_sql: true datasource: url: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false;MODE=MYSQL diff --git a/src/test/resources/concept-tag.sql b/src/test/resources/concept-tag.sql index 6b94678..1d06a9a 100644 --- a/src/test/resources/concept-tag.sql +++ b/src/test/resources/concept-tag.sql @@ -1,12 +1,7 @@ -INSERT INTO concept_tag (name) -VALUES ('미분 개념'); -INSERT INTO concept_tag (name) -VALUES ('적분 개념'); -INSERT INTO concept_tag (name) -VALUES ('삼각함수 개념'); -INSERT INTO concept_tag (name) -VALUES ('행렬 개념'); -INSERT INTO concept_tag (name) -VALUES ('확률과 통계 개념'); -INSERT INTO concept_tag (name) -VALUES ('기하 개념'); \ No newline at end of file +INSERT INTO concept_tag (concept_tag_id, name) +VALUES (1, '미분 개념'), + (2, '적분 개념'), + (3, '삼각함수 개념'), + (4, '행렬 개념'), + (5, '확률과 통계 개념'), + (6, '기하 개념'); \ No newline at end of file diff --git a/src/test/resources/insert-problem.sql b/src/test/resources/insert-problem.sql index 9f81e33..530736b 100644 --- a/src/test/resources/insert-problem.sql +++ b/src/test/resources/insert-problem.sql @@ -6,10 +6,12 @@ FROM child_problem; INSERT INTO problem (problem_id, practice_test_id, number, answer, comment, main_problem_image_url, main_analysis_image_url, reading_tip_image_url, senior_tip_image_url, prescription_image_url, is_published, is_variation) -VALUES ('240520012001', 1, 1, '1', '기존 문제 설명', +VALUES ('240520012001', 1, 1, '1', '기존 문제 설명 1', + 'mainProblem.png', 'mainAnalysis.png', 'readingTip.png', 'seniorTip.png', 'prescription.png', + false, false), + ('240520012002', 1, 1, '1', '기존 문제 설명 2', 'mainProblem.png', 'mainAnalysis.png', 'readingTip.png', 'seniorTip.png', 'prescription.png', false, false); - -- ✅ 기존 자식 문제(ChildProblem) 삽입 INSERT INTO child_problem (child_problem_id, problem_id, image_url, problem_type, answer, sequence) VALUES (1, '240520012001', 'child1.png', 'MULTIPLE_CHOICE', '1', 0), @@ -19,7 +21,9 @@ VALUES (1, '240520012001', 'child1.png', 'MULTIPLE_CHOICE', '1', 0), INSERT INTO problem_concept (problem_id, concept_tag_id) VALUES ('240520012001', 1), ('240520012001', 2), - ('240520012001', 3); + ('240520012001', 3), + ('240520012002', 1), + ('240520012002', 3); -- ✅ 자식 문제-컨셉 태그 연결 INSERT INTO child_problem_concept (child_problem_id, concept_tag_id) diff --git a/src/test/resources/practice-test-tag.sql b/src/test/resources/practice-test-tag.sql index a3ae1b6..58625ce 100644 --- a/src/test/resources/practice-test-tag.sql +++ b/src/test/resources/practice-test-tag.sql @@ -1,2 +1,2 @@ -INSERT INTO practice_test_tag (name, test_year, test_month, subject, area) -VALUES ('2025년 5월 고2 모의고사', 2024, 5, '고2', '수학'); \ No newline at end of file +INSERT INTO practice_test_tag (practice_test_tag_id, name, test_year, test_month, subject, area) +VALUES (1, '2025년 5월 고2 모의고사', 2024, 5, '고2', '수학'); \ No newline at end of file From 7f2b389502d921cdb6874087423fae8ffda3ddaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=B8=EC=A4=80?= <74056843+sejoon00@users.noreply.github.com> Date: Sun, 2 Feb 2025 01:33:33 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[feat/#27]=20querydsl=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EA=B5=AC=ED=98=84=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=EC=97=90=EC=84=9C=20JpaQueryFactory=20=EB=B9=88=20?= =?UTF-8?q?=EC=A3=BC=EC=9E=85=20=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../problem/repository/ProblemSearchRepository.java | 8 -------- ...sitoryImpl.java => ProblemSearchRepositoryCustom.java} | 7 ++++--- ...plTest.java => ProblemSearchRepositoryCustomTest.java} | 0 3 files changed, 4 insertions(+), 11 deletions(-) delete mode 100644 src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepository.java rename src/main/java/com/moplus/moplus_server/domain/problem/repository/{ProblemSearchRepositoryImpl.java => ProblemSearchRepositoryCustom.java} (93%) rename src/test/java/com/moplus/moplus_server/domain/problem/repository/{ProblemSearchRepositoryImplTest.java => ProblemSearchRepositoryCustomTest.java} (100%) diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepository.java b/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepository.java deleted file mode 100644 index 71b3939..0000000 --- a/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.moplus.moplus_server.domain.problem.repository; - -import com.moplus.moplus_server.domain.problem.dto.response.ProblemSearchGetResponse; -import java.util.List; - -public interface ProblemSearchRepository { - List search(String problemId, String comment, List conceptTagIds); -} diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryImpl.java b/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryCustom.java similarity index 93% rename from src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryImpl.java rename to src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryCustom.java index 86ecae2..43a7e45 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryImpl.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryCustom.java @@ -11,12 +11,13 @@ import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; import java.util.List; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; import org.springframework.stereotype.Repository; @Repository -public class ProblemSearchRepositoryImpl extends QuerydslRepositorySupport implements ProblemSearchRepository { +@RequiredArgsConstructor +public class ProblemSearchRepositoryImpl { private final JPAQueryFactory queryFactory; @@ -35,7 +36,7 @@ public List search(String problemId, String comment, L containsName(comment), inConceptTagIds(conceptTagIds) ) - .leftJoin(conceptTag).on(conceptTag.id.in(problem.conceptTagIds)) + .leftJoin(conceptTag).on(conceptTag.id.in(problem.conceptTagIds)).fetchJoin() .distinct() .transform(GroupBy.groupBy(problem.id.id).list( Projections.constructor(ProblemSearchGetResponse.class, diff --git a/src/test/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryImplTest.java b/src/test/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryCustomTest.java similarity index 100% rename from src/test/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryImplTest.java rename to src/test/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryCustomTest.java From f1ad376cbde98b1d1d59640e29aa7a7ca786414c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=84=B8=EC=A4=80?= <74056843+sejoon00@users.noreply.github.com> Date: Sun, 2 Feb 2025 01:38:21 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[fix/#27]=20=ED=95=84=EC=9A=94=ED=95=9C=20c?= =?UTF-8?q?olumn=EB=A7=8C=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../problem/controller/ProblemSearchController.java | 6 +++--- .../domain/problem/repository/ProblemRepository.java | 2 +- .../repository/ProblemSearchRepositoryCustom.java | 12 ++---------- .../global/config/querydsl/QuerydslConfig.java | 4 ++-- .../ProblemSearchRepositoryCustomTest.java | 4 ++-- 5 files changed, 10 insertions(+), 18 deletions(-) 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 cf8c088..7c8c50d 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 @@ -1,7 +1,7 @@ package com.moplus.moplus_server.domain.problem.controller; import com.moplus.moplus_server.domain.problem.dto.response.ProblemSearchGetResponse; -import com.moplus.moplus_server.domain.problem.repository.ProblemRepository; +import com.moplus.moplus_server.domain.problem.repository.ProblemSearchRepositoryCustom; import io.swagger.v3.oas.annotations.Operation; import java.util.List; import lombok.RequiredArgsConstructor; @@ -16,7 +16,7 @@ @RequiredArgsConstructor public class ProblemSearchController { - private final ProblemRepository problemRepository; + private final ProblemSearchRepositoryCustom problemSearchRepository; @GetMapping("/search") @Operation( @@ -28,7 +28,7 @@ public ResponseEntity> search( @RequestParam(value = "comment", required = false) String comment, @RequestParam(value = "conceptTagIds", required = false) List conceptTagIds ) { - List problems = problemRepository.search(problemId, comment, conceptTagIds); + List problems = problemSearchRepository.search(problemId, comment, conceptTagIds); return ResponseEntity.ok(problems); } } 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 1be5b7b..a45c8f4 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 @@ -7,7 +7,7 @@ import com.moplus.moplus_server.global.error.exception.NotFoundException; import org.springframework.data.jpa.repository.JpaRepository; -public interface ProblemRepository extends JpaRepository, ProblemSearchRepository { +public interface ProblemRepository extends JpaRepository { boolean existsByPracticeTestIdAndNumber(Long practiceTestId, int number); 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 43a7e45..97af1a4 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 @@ -3,7 +3,6 @@ import static com.moplus.moplus_server.domain.concept.domain.QConceptTag.conceptTag; import static com.moplus.moplus_server.domain.problem.domain.problem.QProblem.problem; -import com.moplus.moplus_server.domain.problem.domain.problem.Problem; import com.moplus.moplus_server.domain.problem.dto.response.ConceptTagSearchResponse; import com.moplus.moplus_server.domain.problem.dto.response.ProblemSearchGetResponse; import com.querydsl.core.group.GroupBy; @@ -12,24 +11,17 @@ import com.querydsl.jpa.impl.JPAQueryFactory; import java.util.List; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; @Repository @RequiredArgsConstructor -public class ProblemSearchRepositoryImpl { +public class ProblemSearchRepositoryCustom { private final JPAQueryFactory queryFactory; - @Autowired - public ProblemSearchRepositoryImpl(JPAQueryFactory queryFactory) { - super(Problem.class); - this.queryFactory = queryFactory; - } - - @Override public List search(String problemId, String comment, List conceptTagIds) { return queryFactory + .select(problem.id.id, problem.comment, problem.mainProblemImageUrl) .from(problem) .where( containsProblemId(problemId), diff --git a/src/main/java/com/moplus/moplus_server/global/config/querydsl/QuerydslConfig.java b/src/main/java/com/moplus/moplus_server/global/config/querydsl/QuerydslConfig.java index 2889116..9f9ab4c 100644 --- a/src/main/java/com/moplus/moplus_server/global/config/querydsl/QuerydslConfig.java +++ b/src/main/java/com/moplus/moplus_server/global/config/querydsl/QuerydslConfig.java @@ -3,14 +3,14 @@ import com.querydsl.jpa.JPQLTemplates; import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class QuerydslConfig { - @PersistenceContext + @Autowired private EntityManager entityManager; @Bean diff --git a/src/test/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryCustomTest.java b/src/test/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryCustomTest.java index 8a80fd8..712115d 100644 --- a/src/test/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryCustomTest.java +++ b/src/test/java/com/moplus/moplus_server/domain/problem/repository/ProblemSearchRepositoryCustomTest.java @@ -15,10 +15,10 @@ @SpringBootTest @ActiveProfiles("h2test") @Sql({"/practice-test-tag.sql", "/concept-tag.sql", "/insert-problem.sql"}) -public class ProblemSearchRepositoryImplTest { +public class ProblemSearchRepositoryCustomTest { @Autowired - private ProblemRepository problemSearchRepository; + private ProblemSearchRepositoryCustom problemSearchRepository; @Test void problemId_일부_포함_검색() {