From 7903a20d748ffb25c5ba3f0c02f86f6146f9676c 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: Tue, 4 Mar 2025 02:51:21 +0900 Subject: [PATCH 01/48] =?UTF-8?q?[refactor/#83]=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20fetchjoin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/mapper/ChildProblemMapperImpl.java | 2 +- .../problem/service/mapper/ProblemMapperImpl.java | 2 +- .../v0/TestResult/entity/QIncorrectProblem.java | 2 +- .../practiceTest/domain/QProblemImageForTest.java | 2 +- .../domain/problem/domain/problem/Problem.java | 2 +- .../problem/repository/ProblemRepository.java | 14 ++++++++++++++ .../domain/problem/service/ProblemGetService.java | 2 +- 7 files changed, 20 insertions(+), 6 deletions(-) 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 f872cbc1..c18d7e15 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 @@ -11,7 +11,7 @@ @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2025-03-03T17:28:35+0900", + date = "2025-03-04T02:15:33+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 e2bc255c..6842f33f 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 @@ -15,7 +15,7 @@ @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2025-03-03T17:28:35+0900", + date = "2025-03-04T02:15:33+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/TestResult/entity/QIncorrectProblem.java b/src/main/generated/com/moplus/moplus_server/domain/v0/TestResult/entity/QIncorrectProblem.java index 9f3b9956..b73be8c0 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("problemCustomId", Long.class); + public final NumberPath problemId = createNumber("problemId", 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 67b69aa3..b7e7668a 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("problemCustomId", Long.class); + public final NumberPath problemId = createNumber("problemId", Long.class); public QProblemImageForTest(String variable) { super(ProblemImageForTest.class, forVariable(variable)); 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 5185f43f..04cb2887 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 @@ -75,7 +75,7 @@ public class Problem extends BaseEntity { private boolean isConfirmed; - @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) @JoinColumn(name = "problem_id") @OrderColumn(name = "sequence") private List childProblems = new ArrayList<>(); 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 4529f682..8ca0b18c 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 @@ -4,7 +4,10 @@ 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 java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface ProblemRepository extends JpaRepository { @@ -22,7 +25,18 @@ default void existsByProblemAdminIdElseThrow(ProblemCustomId problemCustomId) { } } + @Query("SELECT DISTINCT p FROM Problem p " + + "LEFT JOIN FETCH p.childProblems c " + + "LEFT JOIN FETCH p.conceptTagIds " + + "LEFT JOIN FETCH c.conceptTagIds " + + "WHERE p.id = :id") + Optional findByIdWithFetchJoin(@Param("id") Long id); + default Problem findByIdElseThrow(Long id) { return findById(id).orElseThrow(() -> new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND)); } + + default Problem findByIdWithFetchJoinElseThrow(Long id) { + return findByIdWithFetchJoin(id).orElseThrow(() -> new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND)); + } } diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemGetService.java b/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemGetService.java index a3b0fa14..bc910998 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemGetService.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemGetService.java @@ -15,7 +15,7 @@ public class ProblemGetService { @Transactional(readOnly = true) public ProblemGetResponse getProblem(Long problemId) { - Problem problem = problemRepository.findByIdElseThrow(problemId); + Problem problem = problemRepository.findByIdWithFetchJoinElseThrow(problemId); return ProblemGetResponse.of(problem); } } From 7cca368608d87019f8dd5c2abf0205deb78d3557 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: Tue, 4 Mar 2025 04:02:11 +0900 Subject: [PATCH 02/48] =?UTF-8?q?[refactor/#83]=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EC=8A=A4=EB=A0=88=EB=93=9C=20=EA=B0=9C=EC=88=98=20=EC=A1=B0?= =?UTF-8?q?=EC=A0=88=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-server.yml | 8 ++++++++ src/main/resources/application.yml | 1 + 2 files changed, 9 insertions(+) create mode 100644 src/main/resources/application-server.yml diff --git a/src/main/resources/application-server.yml b/src/main/resources/application-server.yml new file mode 100644 index 00000000..584d7ce3 --- /dev/null +++ b/src/main/resources/application-server.yml @@ -0,0 +1,8 @@ +server: + tomcat: + accept-count: 100 + max-connections: 8192 + + threads: + max: 300 + min-spare: 10 \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f6a00c5a..7923e2b7 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -8,6 +8,7 @@ spring: include: - aws - security + - server mvc: ignore-default-favicon: true From 5cd613df0fb6119b942b805659e96cb87ad2b605 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: Fri, 14 Mar 2025 20:19:36 +0900 Subject: [PATCH 03/48] =?UTF-8?q?[feat/#92]=20=EC=9E=84=EC=8B=9C=20?= =?UTF-8?q?=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Problem/domain/ChildProblemStatistic.java | 32 ++++++++++++++++++ .../Problem/domain/CountStatistic.java | 14 ++++++++ .../statistic/Problem/domain/CountUpper.java | 4 +++ .../Problem/domain/ProblemSetStatistic.java | 31 +++++++++++++++++ .../Problem/domain/ProblemStatistic.java | 33 +++++++++++++++++++ 5 files changed, 114 insertions(+) create mode 100644 src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ChildProblemStatistic.java create mode 100644 src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountStatistic.java create mode 100644 src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountUpper.java create mode 100644 src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemSetStatistic.java create mode 100644 src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemStatistic.java diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ChildProblemStatistic.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ChildProblemStatistic.java new file mode 100644 index 00000000..0dfb5561 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ChildProblemStatistic.java @@ -0,0 +1,32 @@ +package com.moplus.moplus_server.statistic.Problem.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class ChildProblemStatistic { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "child_problem_statistic_id") + private Long id; + + private Long childProblemId; + + @Embedded + private CountStatistic countStatistic; + + public ChildProblemStatistic(Long childProblemId) { + this.childProblemId = childProblemId; + this.countStatistic = new CountStatistic(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountStatistic.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountStatistic.java new file mode 100644 index 00000000..793c0d71 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountStatistic.java @@ -0,0 +1,14 @@ +package com.moplus.moplus_server.statistic.Problem.domain; + +import jakarta.persistence.Embeddable; + +@Embeddable +public class CountStatistic { + private Long viewCount; + private Long submitCount; + + public CountStatistic() { + this.viewCount = 0L; + this.submitCount = 0L; + } +} diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountUpper.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountUpper.java new file mode 100644 index 00000000..fca28a41 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountUpper.java @@ -0,0 +1,4 @@ +package com.moplus.moplus_server.statistic.Problem.domain; + +public interface CountUpper { +} diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemSetStatistic.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemSetStatistic.java new file mode 100644 index 00000000..2a913f21 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemSetStatistic.java @@ -0,0 +1,31 @@ +package com.moplus.moplus_server.statistic.Problem.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class ProblemSetStatistic { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "problem_set_statistic_id") + private Long id; + + private Long problemSetId; + private Long viewCount; + private Long submitCount; + + public ProblemSetStatistic(Long problemSetId, Long viewCount, Long submitCount) { + this.problemSetId = problemSetId; + this.viewCount = viewCount; + this.submitCount = submitCount; + } +} diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemStatistic.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemStatistic.java new file mode 100644 index 00000000..e10c4e2d --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemStatistic.java @@ -0,0 +1,33 @@ +package com.moplus.moplus_server.statistic.Problem.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class ProblemStatistic { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "problem_statistic_id") + private Long id; + + private Long problemId; + private Long viewCount; + private Long submitCount; + + @Builder + public ProblemStatistic(Long problemId, Long viewCount, Long submitCount) { + this.problemId = problemId; + this.viewCount = viewCount; + this.submitCount = submitCount; + } +} From eba2a63b51913fadaa20c6016b7545e35020349a 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, 15 Mar 2025 04:30:45 +0900 Subject: [PATCH 04/48] =?UTF-8?q?[refactor]=20N+1=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../publish/domain/QPublish.java | 4 +- .../submit/domain/QChildProblemSubmit.java | 53 +++++++++++++++++++ .../client/submit/domain/QProblemSubmit.java | 53 +++++++++++++++++++ .../mapper/ChildProblemMapperImpl.java | 4 +- .../service/mapper/ProblemMapperImpl.java | 6 +-- .../{domain => }/member/domain/QMember.java | 4 +- .../member/QCorrectConceptTagStatistics.java | 49 +++++++++++++++++ .../QIncorrectConceptTagStatistics.java | 49 +++++++++++++++++ .../domain/childProblem/ChildProblem.java | 2 + .../problem/domain/problem/Problem.java | 3 +- .../problem/repository/ProblemRepository.java | 2 - .../problem/service/ProblemGetService.java | 2 +- src/main/resources/application-datasource.yml | 5 +- src/main/resources/application-local.yml | 1 + src/main/resources/application-server.yml | 2 +- 15 files changed, 224 insertions(+), 15 deletions(-) rename src/main/generated/com/moplus/moplus_server/{domain => admin}/publish/domain/QPublish.java (92%) create mode 100644 src/main/generated/com/moplus/moplus_server/client/submit/domain/QChildProblemSubmit.java create mode 100644 src/main/generated/com/moplus/moplus_server/client/submit/domain/QProblemSubmit.java rename src/main/generated/com/moplus/moplus_server/{domain => }/member/domain/QMember.java (92%) create mode 100644 src/main/generated/com/moplus/moplus_server/statistic/member/QCorrectConceptTagStatistics.java create mode 100644 src/main/generated/com/moplus/moplus_server/statistic/member/QIncorrectConceptTagStatistics.java diff --git a/src/main/generated/com/moplus/moplus_server/domain/publish/domain/QPublish.java b/src/main/generated/com/moplus/moplus_server/admin/publish/domain/QPublish.java similarity index 92% rename from src/main/generated/com/moplus/moplus_server/domain/publish/domain/QPublish.java rename to src/main/generated/com/moplus/moplus_server/admin/publish/domain/QPublish.java index 41676a40..5e607379 100644 --- a/src/main/generated/com/moplus/moplus_server/domain/publish/domain/QPublish.java +++ b/src/main/generated/com/moplus/moplus_server/admin/publish/domain/QPublish.java @@ -1,4 +1,4 @@ -package com.moplus.moplus_server.domain.publish.domain; +package com.moplus.moplus_server.admin.publish.domain; import static com.querydsl.core.types.PathMetadataFactory.*; @@ -15,7 +15,7 @@ @Generated("com.querydsl.codegen.DefaultEntitySerializer") public class QPublish extends EntityPathBase { - private static final long serialVersionUID = 1565569153L; + private static final long serialVersionUID = 1641302032L; public static final QPublish publish = new QPublish("publish"); diff --git a/src/main/generated/com/moplus/moplus_server/client/submit/domain/QChildProblemSubmit.java b/src/main/generated/com/moplus/moplus_server/client/submit/domain/QChildProblemSubmit.java new file mode 100644 index 00000000..1d70b647 --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/client/submit/domain/QChildProblemSubmit.java @@ -0,0 +1,53 @@ +package com.moplus.moplus_server.client.submit.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; + + +/** + * QChildProblemSubmit is a Querydsl query type for ChildProblemSubmit + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QChildProblemSubmit extends EntityPathBase { + + private static final long serialVersionUID = -1656142683L; + + public static final QChildProblemSubmit childProblemSubmit = new QChildProblemSubmit("childProblemSubmit"); + + public final com.moplus.moplus_server.global.common.QBaseEntity _super = new com.moplus.moplus_server.global.common.QBaseEntity(this); + + public final NumberPath childProblemId = createNumber("childProblemId", Long.class); + + //inherited + public final DateTimePath createdDate = _super.createdDate; + + public final NumberPath id = createNumber("id", Long.class); + + public final NumberPath memberId = createNumber("memberId", Long.class); + + public final NumberPath publishId = createNumber("publishId", Long.class); + + public final EnumPath status = createEnum("status", ChildProblemSubmitStatus.class); + + //inherited + public final DateTimePath updatedDate = _super.updatedDate; + + public QChildProblemSubmit(String variable) { + super(ChildProblemSubmit.class, forVariable(variable)); + } + + public QChildProblemSubmit(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QChildProblemSubmit(PathMetadata metadata) { + super(ChildProblemSubmit.class, metadata); + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/client/submit/domain/QProblemSubmit.java b/src/main/generated/com/moplus/moplus_server/client/submit/domain/QProblemSubmit.java new file mode 100644 index 00000000..e573b013 --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/client/submit/domain/QProblemSubmit.java @@ -0,0 +1,53 @@ +package com.moplus.moplus_server.client.submit.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; + + +/** + * QProblemSubmit is a Querydsl query type for ProblemSubmit + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QProblemSubmit extends EntityPathBase { + + private static final long serialVersionUID = 1682818189L; + + public static final QProblemSubmit problemSubmit = new QProblemSubmit("problemSubmit"); + + 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 memberId = createNumber("memberId", Long.class); + + public final NumberPath problemId = createNumber("problemId", Long.class); + + public final NumberPath publishId = createNumber("publishId", Long.class); + + public final EnumPath status = createEnum("status", ProblemSubmitStatus.class); + + //inherited + public final DateTimePath updatedDate = _super.updatedDate; + + public QProblemSubmit(String variable) { + super(ProblemSubmit.class, forVariable(variable)); + } + + public QProblemSubmit(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QProblemSubmit(PathMetadata metadata) { + super(ProblemSubmit.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 c18d7e15..d528b5c5 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 @@ -1,7 +1,7 @@ package com.moplus.moplus_server.domain.problem.service.mapper; +import com.moplus.moplus_server.admin.problem.dto.request.ChildProblemUpdateRequest; import com.moplus.moplus_server.domain.problem.domain.childProblem.ChildProblem; -import com.moplus.moplus_server.domain.problem.dto.request.ChildProblemUpdateRequest; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; @@ -11,7 +11,7 @@ @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2025-03-04T02:15:33+0900", + date = "2025-03-14T20:25:51+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 6842f33f..c3da7548 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 @@ -1,11 +1,11 @@ package com.moplus.moplus_server.domain.problem.service.mapper; +import com.moplus.moplus_server.admin.problem.dto.request.ProblemPostRequest; +import com.moplus.moplus_server.admin.problem.dto.request.ProblemUpdateRequest; 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.ProblemCustomId; import com.moplus.moplus_server.domain.problem.domain.problem.ProblemType; -import com.moplus.moplus_server.domain.problem.dto.request.ProblemPostRequest; -import com.moplus.moplus_server.domain.problem.dto.request.ProblemUpdateRequest; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; @@ -15,7 +15,7 @@ @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2025-03-04T02:15:33+0900", + date = "2025-03-14T20:25:51+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/member/domain/QMember.java b/src/main/generated/com/moplus/moplus_server/member/domain/QMember.java similarity index 92% rename from src/main/generated/com/moplus/moplus_server/domain/member/domain/QMember.java rename to src/main/generated/com/moplus/moplus_server/member/domain/QMember.java index ad5aeae6..a4dee67f 100644 --- a/src/main/generated/com/moplus/moplus_server/domain/member/domain/QMember.java +++ b/src/main/generated/com/moplus/moplus_server/member/domain/QMember.java @@ -1,4 +1,4 @@ -package com.moplus.moplus_server.domain.member.domain; +package com.moplus.moplus_server.member.domain; import static com.querydsl.core.types.PathMetadataFactory.*; @@ -15,7 +15,7 @@ @Generated("com.querydsl.codegen.DefaultEntitySerializer") public class QMember extends EntityPathBase { - private static final long serialVersionUID = -705761779L; + private static final long serialVersionUID = -1541748259L; public static final QMember member = new QMember("member1"); diff --git a/src/main/generated/com/moplus/moplus_server/statistic/member/QCorrectConceptTagStatistics.java b/src/main/generated/com/moplus/moplus_server/statistic/member/QCorrectConceptTagStatistics.java new file mode 100644 index 00000000..1c5c5f93 --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/statistic/member/QCorrectConceptTagStatistics.java @@ -0,0 +1,49 @@ +package com.moplus.moplus_server.statistic.member; + +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; + + +/** + * QCorrectConceptTagStatistics is a Querydsl query type for CorrectConceptTagStatistics + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QCorrectConceptTagStatistics extends EntityPathBase { + + private static final long serialVersionUID = -313521578L; + + public static final QCorrectConceptTagStatistics correctConceptTagStatistics = new QCorrectConceptTagStatistics("correctConceptTagStatistics"); + + public final com.moplus.moplus_server.global.common.QBaseEntity _super = new com.moplus.moplus_server.global.common.QBaseEntity(this); + + public final NumberPath conceptTagId = createNumber("conceptTagId", Long.class); + + public final NumberPath correctCount = createNumber("correctCount", Integer.class); + + //inherited + public final DateTimePath createdDate = _super.createdDate; + + public final NumberPath id = createNumber("id", Long.class); + + //inherited + public final DateTimePath updatedDate = _super.updatedDate; + + public QCorrectConceptTagStatistics(String variable) { + super(CorrectConceptTagStatistics.class, forVariable(variable)); + } + + public QCorrectConceptTagStatistics(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QCorrectConceptTagStatistics(PathMetadata metadata) { + super(CorrectConceptTagStatistics.class, metadata); + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/statistic/member/QIncorrectConceptTagStatistics.java b/src/main/generated/com/moplus/moplus_server/statistic/member/QIncorrectConceptTagStatistics.java new file mode 100644 index 00000000..7222e577 --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/statistic/member/QIncorrectConceptTagStatistics.java @@ -0,0 +1,49 @@ +package com.moplus.moplus_server.statistic.member; + +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; + + +/** + * QIncorrectConceptTagStatistics is a Querydsl query type for IncorrectConceptTagStatistics + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QIncorrectConceptTagStatistics extends EntityPathBase { + + private static final long serialVersionUID = 1288752209L; + + public static final QIncorrectConceptTagStatistics incorrectConceptTagStatistics = new QIncorrectConceptTagStatistics("incorrectConceptTagStatistics"); + + public final com.moplus.moplus_server.global.common.QBaseEntity _super = new com.moplus.moplus_server.global.common.QBaseEntity(this); + + public final NumberPath conceptTagId = createNumber("conceptTagId", Long.class); + + //inherited + public final DateTimePath createdDate = _super.createdDate; + + public final NumberPath id = createNumber("id", Long.class); + + public final NumberPath incorrectCount = createNumber("incorrectCount", Integer.class); + + //inherited + public final DateTimePath updatedDate = _super.updatedDate; + + public QIncorrectConceptTagStatistics(String variable) { + super(IncorrectConceptTagStatistics.class, forVariable(variable)); + } + + public QIncorrectConceptTagStatistics(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QIncorrectConceptTagStatistics(PathMetadata metadata) { + super(IncorrectConceptTagStatistics.class, metadata); + } + +} + 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 52bda4f5..07e1c121 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 @@ -24,6 +24,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.annotations.BatchSize; @Getter @Entity @@ -34,6 +35,7 @@ public class ChildProblem extends BaseEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "child_problem_id") Long id; + @BatchSize(size = 100) @ElementCollection @CollectionTable(name = "child_problem_concept", joinColumns = @JoinColumn(name = "child_problem_id")) @Column(name = "concept_tag_id") 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 04cb2887..f69903ec 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 @@ -60,6 +60,7 @@ public class Problem extends BaseEntity { @Convert(converter = StringListConverter.class) @Column(columnDefinition = "TEXT") List prescriptionImageUrls; + @ElementCollection @CollectionTable(name = "problem_concept", joinColumns = @JoinColumn(name = "problem_id")) @Column(name = "concept_tag_id") @@ -75,7 +76,7 @@ public class Problem extends BaseEntity { private boolean isConfirmed; - @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) @JoinColumn(name = "problem_id") @OrderColumn(name = "sequence") private List childProblems = new ArrayList<>(); 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 8ca0b18c..d84485a3 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 @@ -27,8 +27,6 @@ default void existsByProblemAdminIdElseThrow(ProblemCustomId problemCustomId) { @Query("SELECT DISTINCT p FROM Problem p " + "LEFT JOIN FETCH p.childProblems c " + - "LEFT JOIN FETCH p.conceptTagIds " + - "LEFT JOIN FETCH c.conceptTagIds " + "WHERE p.id = :id") Optional findByIdWithFetchJoin(@Param("id") Long id); diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemGetService.java b/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemGetService.java index 8b6d4e59..ed3bb707 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemGetService.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/service/ProblemGetService.java @@ -1,7 +1,7 @@ package com.moplus.moplus_server.domain.problem.service; -import com.moplus.moplus_server.domain.problem.domain.problem.Problem; import com.moplus.moplus_server.admin.problem.dto.response.ProblemGetResponse; +import com.moplus.moplus_server.domain.problem.domain.problem.Problem; import com.moplus.moplus_server.domain.problem.repository.ProblemRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; diff --git a/src/main/resources/application-datasource.yml b/src/main/resources/application-datasource.yml index 1876f672..78011112 100644 --- a/src/main/resources/application-datasource.yml +++ b/src/main/resources/application-datasource.yml @@ -15,5 +15,8 @@ spring: properties: hibernate: dialect: org.hibernate.dialect.MySQLDialect - default_batch_fetch_size: 1000 + default_batch_fetch_size: 100 + + + diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index f1132665..095173fa 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -8,6 +8,7 @@ spring: show_sql: true format_sql: true use_sql_comments: true + highlight_sql: true hibernate: ddl-auto: update diff --git a/src/main/resources/application-server.yml b/src/main/resources/application-server.yml index 584d7ce3..268b5f53 100644 --- a/src/main/resources/application-server.yml +++ b/src/main/resources/application-server.yml @@ -4,5 +4,5 @@ server: max-connections: 8192 threads: - max: 300 + max: 200 min-spare: 10 \ No newline at end of file From 0d7480bab38be878265c3faf80690a1f7de6c580 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, 15 Mar 2025 21:49:23 +0900 Subject: [PATCH 05/48] =?UTF-8?q?[refactor/#83]=20problem,=20childproblem?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/mapper/ProblemMapperImpl.java | 2 +- .../problem/domain/problem/ProblemType.java | 8 +++++ .../problem/repository/ProblemRepository.java | 6 ---- .../problem/service/ChildProblemService.java | 27 +++++++++------ .../problem/service/ProblemSaveService.java | 33 +++++++++++++------ .../problem/service/ProblemUpdateService.java | 31 ++++++++++------- 6 files changed, 68 insertions(+), 39 deletions(-) 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 c3da7548..b3e294c2 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 @@ -15,7 +15,7 @@ @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2025-03-14T20:25:51+0900", + date = "2025-03-15T21:23:23+0900", comments = "version: 1.6.3, compiler: javac, environment: Java 17.0.10 (JetBrains s.r.o.)" ) @Component diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemType.java b/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemType.java index e02d1014..baec84ae 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemType.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/domain/problem/ProblemType.java @@ -13,4 +13,12 @@ public enum ProblemType { private final String name; private final int code; + + public boolean isCreationProblem() { + return this == CREATION_PROBLEM; + } + + public boolean requiresPracticeTest() { + return !isCreationProblem(); + } } 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 d84485a3..0a959fc9 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 @@ -19,12 +19,6 @@ default void existsByIdElseThrow(Long id) { } } - default void existsByProblemAdminIdElseThrow(ProblemCustomId problemCustomId) { - if (!existsByProblemCustomId(problemCustomId)) { - throw new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND); - } - } - @Query("SELECT DISTINCT p FROM Problem p " + "LEFT JOIN FETCH p.childProblems c " + "WHERE p.id = :id") 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 index 7cf4753e..86dcee3d 100644 --- 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 @@ -17,23 +17,30 @@ public class ChildProblemService { @Transactional public Long createChildProblem(Long problemId) { - Problem problem = problemRepository.findByIdElseThrow(problemId); - if (problem.isConfirmed()) { - throw new InvalidValueException(ErrorCode.CHILD_PROBLEM_UPDATE_AFTER_CONFIRMED); - } - - problem.addChildProblem(ChildProblem.createEmptyChildProblem()); - - return problemRepository.save(problem).getChildProblems().get(problem.getChildProblems().size() - 1).getId(); + Problem problem = findAndValidateProblem(problemId); + ChildProblem newChildProblem = ChildProblem.createEmptyChildProblem(); + problem.addChildProblem(newChildProblem); + + return getCreatedChildProblemId(problem); } @Transactional public void deleteChildProblem(Long problemId, Long childProblemId) { + Problem problem = findAndValidateProblem(problemId); + problem.deleteChildProblem(childProblemId); + } + + private Problem findAndValidateProblem(Long problemId) { Problem problem = problemRepository.findByIdElseThrow(problemId); if (problem.isConfirmed()) { throw new InvalidValueException(ErrorCode.CHILD_PROBLEM_UPDATE_AFTER_CONFIRMED); } - problem.deleteChildProblem(childProblemId); - problemRepository.save(problem); + return problem; + } + + private Long getCreatedChildProblemId(Problem problem) { + return problem.getChildProblems() + .get(problem.getChildProblems().size() - 1) + .getId(); } } 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 71dc0fd3..0cbc11d8 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 @@ -25,19 +25,32 @@ public class ProblemSaveService { @Transactional public ProblemPostResponse createProblem(ProblemPostRequest request) { - Problem problem; - if (request.problemType() != ProblemType.CREATION_PROBLEM) { - PracticeTestTag practiceTestTag = practiceTestRepository.findByIdElseThrow(request.practiceTestId()); - ProblemCustomId problemCustomId = problemAdminIdService.nextId(request.number(), practiceTestTag, - request.problemType()); + PracticeTestTag practiceTestTag = getPracticeTestTag(request); + ProblemCustomId problemCustomId = createProblemCustomId(request); + Problem problem = createProblem(request, problemCustomId, practiceTestTag); + + return ProblemPostResponse.of(problemRepository.save(problem)); + } - problem = problemMapper.from(request, problemCustomId, practiceTestTag); - } else { - ProblemCustomId problemCustomId = problemAdminIdService.nextId(request.problemType()); - problem = problemMapper.from(request.problemType(), problemCustomId); + private Problem createProblem(ProblemPostRequest request, ProblemCustomId problemCustomId, PracticeTestTag practiceTestTag) { + if (request.problemType().isCreationProblem()) { + return problemMapper.from(request, problemCustomId, practiceTestTag); } + return problemMapper.from(request.problemType(), problemCustomId); + } - return ProblemPostResponse.of(problemRepository.save(problem)); + private ProblemCustomId createProblemCustomId(ProblemPostRequest request) { + if (request.problemType().requiresPracticeTest()) { + PracticeTestTag practiceTestTag = practiceTestRepository.findByIdElseThrow(request.practiceTestId()); + return problemAdminIdService.nextId(request.number(), practiceTestTag, request.problemType()); + } + return problemAdminIdService.nextId(request.problemType()); } + private PracticeTestTag getPracticeTestTag(ProblemPostRequest request) { + if (request.problemType().requiresPracticeTest()) { + return practiceTestRepository.findByIdElseThrow(request.practiceTestId()); + } + return null; + } } 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 248bf112..95834546 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 @@ -1,14 +1,13 @@ package com.moplus.moplus_server.domain.problem.service; +import com.moplus.moplus_server.admin.problem.dto.request.ProblemUpdateRequest; +import com.moplus.moplus_server.admin.problem.dto.response.ProblemGetResponse; import com.moplus.moplus_server.domain.concept.repository.ConceptTagRepository; 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.ProblemAdminIdService; import com.moplus.moplus_server.domain.problem.domain.problem.ProblemCustomId; -import com.moplus.moplus_server.admin.problem.dto.request.ProblemUpdateRequest; -import com.moplus.moplus_server.admin.problem.dto.response.ProblemGetResponse; -import com.moplus.moplus_server.domain.problem.domain.problem.ProblemType; 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; @@ -33,15 +32,8 @@ public class ProblemUpdateService { public ProblemGetResponse updateProblem(Long problemId, ProblemUpdateRequest request) { conceptTagRepository.existsByIdElseThrow(request.conceptTagIds()); Problem problem = problemRepository.findByIdElseThrow(problemId); - PracticeTestTag practiceTestTag = null; - ProblemCustomId problemCustomId = null; - if (request.problemType() != ProblemType.CREATION_PROBLEM) { - practiceTestTag = practiceTestRepository.findByIdElseThrow(request.practiceTestId()); - problemCustomId = problemAdminIdService.nextId(request.number(), practiceTestTag, - request.problemType()); - } else { - problemCustomId = problemAdminIdService.nextId(request.problemType()); - } + PracticeTestTag practiceTestTag = getPracticeTestTag(request); + ProblemCustomId problemCustomId = createProblemCustomId(request); Problem inputProblem = problemMapper.from(request, problemCustomId, practiceTestTag); problem.update(inputProblem); @@ -57,4 +49,19 @@ private List changeToChildProblems(ProblemUpdateRequest request) { .map(childProblemMapper::from) .toList(); } + + private ProblemCustomId createProblemCustomId(ProblemUpdateRequest request) { + if (request.problemType().requiresPracticeTest()) { + PracticeTestTag practiceTestTag = practiceTestRepository.findByIdElseThrow(request.practiceTestId()); + return problemAdminIdService.nextId(request.number(), practiceTestTag, request.problemType()); + } + return problemAdminIdService.nextId(request.problemType()); + } + + private PracticeTestTag getPracticeTestTag(ProblemUpdateRequest request) { + if (request.problemType().requiresPracticeTest()) { + return practiceTestRepository.findByIdElseThrow(request.practiceTestId()); + } + return null; + } } From 6df4aab7cdf75febb706c6404162782baf747a66 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, 15 Mar 2025 22:23:46 +0900 Subject: [PATCH 06/48] =?UTF-8?q?[refactor/#83]=20=EB=AA=A8=EB=93=A0=20?= =?UTF-8?q?=ED=99=98=EA=B2=BD=EC=97=90=20datasource=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-datasource.yml | 10 +--------- src/main/resources/application-local.yml | 10 +++++++++- src/main/resources/application.yml | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/resources/application-datasource.yml b/src/main/resources/application-datasource.yml index 78011112..f90f75cd 100644 --- a/src/main/resources/application-datasource.yml +++ b/src/main/resources/application-datasource.yml @@ -2,15 +2,7 @@ spring: config: activate: on-profile: "datasource" - datasource: - url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true&tinyInt1isBit=false - driver-class-name: com.mysql.cj.jdbc.Driver - password: ${MYSQL_PASSWORD} - username: ${MYSQL_USERNAME} - hikari: - maximum-pool-size: 10 # default - connection-timeout: 3000 # 30 seconds in milliseconds - keepalive-time: 600000 # 5 minutes in milliseconds + jpa: properties: hibernate: diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 095173fa..e0b372ce 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -11,7 +11,15 @@ spring: highlight_sql: true hibernate: ddl-auto: update - + datasource: + url: jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true&tinyInt1isBit=false + driver-class-name: com.mysql.cj.jdbc.Driver + password: ${MYSQL_PASSWORD} + username: ${MYSQL_USERNAME} + hikari: + maximum-pool-size: 10 # default + connection-timeout: 3000 # 30 seconds in milliseconds + keepalive-time: 600000 # 5 minutes in milliseconds swagger: servers: - url: http://localhost:8080 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b16344a7..8db87749 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,8 +3,8 @@ spring: active: ${profile} group: local: "local, datasource" - dev: "dev" - prod: "prod" + dev: "dev, datasource" + prod: "prod, datasource" include: - aws - security From 235ab2ad9947077ab8dc8f6a017da0c94bde9922 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, 16 Mar 2025 00:36:10 +0900 Subject: [PATCH 07/48] =?UTF-8?q?[feat/#92]=20=ED=86=B5=EA=B3=84=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/error/exception/ErrorCode.java | 5 +++ .../Problem/domain/ChildProblemStatistic.java | 12 ++++++- .../Problem/domain/CountStatistic.java | 12 ++++++- .../statistic/Problem/domain/CountUpper.java | 4 --- .../Problem/domain/ProblemSetStatistic.java | 16 ++++++++- .../Problem/domain/ProblemStatistic.java | 16 ++++++++- .../Problem/domain/StatisticCounter.java | 11 +++++++ .../Problem/domain/StatisticEntityTarget.java | 5 +++ .../Problem/domain/StatisticFieldType.java | 16 +++++++++ .../ChildProblemStatisticRepository.java | 13 ++++++++ .../ProblemSetStatisticRepository.java | 13 ++++++++ .../ProblemStatisticRepository.java | 13 ++++++++ .../service/CountStatisticsUpdateService.java | 33 +++++++++++++++++++ 13 files changed, 161 insertions(+), 8 deletions(-) delete mode 100644 src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountUpper.java create mode 100644 src/main/java/com/moplus/moplus_server/statistic/Problem/domain/StatisticCounter.java create mode 100644 src/main/java/com/moplus/moplus_server/statistic/Problem/domain/StatisticEntityTarget.java create mode 100644 src/main/java/com/moplus/moplus_server/statistic/Problem/domain/StatisticFieldType.java create mode 100644 src/main/java/com/moplus/moplus_server/statistic/Problem/repository/ChildProblemStatisticRepository.java create mode 100644 src/main/java/com/moplus/moplus_server/statistic/Problem/repository/ProblemSetStatisticRepository.java create mode 100644 src/main/java/com/moplus/moplus_server/statistic/Problem/repository/ProblemStatisticRepository.java create mode 100644 src/main/java/com/moplus/moplus_server/statistic/Problem/service/CountStatisticsUpdateService.java 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 52a7fa02..8b43db9d 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 @@ -76,6 +76,11 @@ public enum ErrorCode { ALREADY_PUBLISHED_ERROR(HttpStatus.BAD_REQUEST, "이미 발행된 문항세트는 컨펌해제할 수 없습니다."), PROBLEM_SET_DELETED(HttpStatus.BAD_REQUEST, "삭제된 문항세트는 발행할 수 없습니다"), PROBLEM_SET_NOT_CONFIRMED(HttpStatus.BAD_REQUEST, "컨펌되지 않은 문항세트는 발행할 수 없습니다"), + + // 통계 + PROBLEM_STATISTIC_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 문항의 통계 정보를 찾을 수 없습니다"), + PROBLEM_SET_STATISTIC_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 문항세트의 통계 정보를 찾을 수 없습니다"), + CHILD_PROBLEM_STATISTIC_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 새끼 문항의 통계 정보를 찾을 수 없습니다"), ; diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ChildProblemStatistic.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ChildProblemStatistic.java index 0dfb5561..a69f4f99 100644 --- a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ChildProblemStatistic.java +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ChildProblemStatistic.java @@ -13,7 +13,7 @@ @Getter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class ChildProblemStatistic { +public class ChildProblemStatistic implements StatisticCounter { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -29,4 +29,14 @@ public ChildProblemStatistic(Long childProblemId) { this.childProblemId = childProblemId; this.countStatistic = new CountStatistic(); } + + @Override + public void addViewCount() { + this.countStatistic.addViewCount(); + } + + @Override + public void addSubmitCount() { + this.countStatistic.addSubmitCount(); + } } diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountStatistic.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountStatistic.java index 793c0d71..0e38e52d 100644 --- a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountStatistic.java +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountStatistic.java @@ -3,7 +3,7 @@ import jakarta.persistence.Embeddable; @Embeddable -public class CountStatistic { +public class CountStatistic implements StatisticCounter { private Long viewCount; private Long submitCount; @@ -11,4 +11,14 @@ public CountStatistic() { this.viewCount = 0L; this.submitCount = 0L; } + + @Override + public void addViewCount() { + this.viewCount++; + } + + @Override + public void addSubmitCount() { + this.submitCount++; + } } diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountUpper.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountUpper.java deleted file mode 100644 index fca28a41..00000000 --- a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountUpper.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.moplus.moplus_server.statistic.Problem.domain; - -public interface CountUpper { -} diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemSetStatistic.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemSetStatistic.java index 2a913f21..e7021d80 100644 --- a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemSetStatistic.java +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemSetStatistic.java @@ -1,6 +1,7 @@ package com.moplus.moplus_server.statistic.Problem.domain; import jakarta.persistence.Column; +import jakarta.persistence.Embedded; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; @@ -12,7 +13,7 @@ @Getter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class ProblemSetStatistic { +public class ProblemSetStatistic implements StatisticCounter { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -23,9 +24,22 @@ public class ProblemSetStatistic { private Long viewCount; private Long submitCount; + @Embedded + private CountStatistic countStatistic; + public ProblemSetStatistic(Long problemSetId, Long viewCount, Long submitCount) { this.problemSetId = problemSetId; this.viewCount = viewCount; this.submitCount = submitCount; } + + @Override + public void addViewCount() { + this.countStatistic.addViewCount(); + } + + @Override + public void addSubmitCount() { + this.countStatistic.addSubmitCount(); + } } diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemStatistic.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemStatistic.java index e10c4e2d..ba3be5a0 100644 --- a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemStatistic.java +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemStatistic.java @@ -1,6 +1,7 @@ package com.moplus.moplus_server.statistic.Problem.domain; import jakarta.persistence.Column; +import jakarta.persistence.Embedded; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; @@ -13,7 +14,7 @@ @Getter @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class ProblemStatistic { +public class ProblemStatistic implements StatisticCounter { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -24,10 +25,23 @@ public class ProblemStatistic { private Long viewCount; private Long submitCount; + @Embedded + private CountStatistic countStatistic; + @Builder public ProblemStatistic(Long problemId, Long viewCount, Long submitCount) { this.problemId = problemId; this.viewCount = viewCount; this.submitCount = submitCount; } + + @Override + public void addViewCount() { + this.countStatistic.addViewCount(); + } + + @Override + public void addSubmitCount() { + this.countStatistic.addSubmitCount(); + } } diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/StatisticCounter.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/StatisticCounter.java new file mode 100644 index 00000000..3c8b309d --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/StatisticCounter.java @@ -0,0 +1,11 @@ +package com.moplus.moplus_server.statistic.Problem.domain; + +public interface StatisticCounter { + void addSubmitCount(); + + void addViewCount(); + + default void updateCount(StatisticFieldType type) { + type.updateCount(this); + } +} diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/StatisticEntityTarget.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/StatisticEntityTarget.java new file mode 100644 index 00000000..d66eba95 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/StatisticEntityTarget.java @@ -0,0 +1,5 @@ +package com.moplus.moplus_server.statistic.Problem.domain; + +public enum StatisticEntityTarget { + PROBLEM, PROBLEM_SET, CHILD_PROBLEM +} diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/StatisticFieldType.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/StatisticFieldType.java new file mode 100644 index 00000000..771214d7 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/StatisticFieldType.java @@ -0,0 +1,16 @@ +package com.moplus.moplus_server.statistic.Problem.domain; + +import java.util.function.Consumer; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public enum StatisticFieldType { + VIEW(StatisticCounter::addViewCount), + SUBMIT(StatisticCounter::addSubmitCount); + + private final Consumer countUpdater; + + public void updateCount(StatisticCounter counter) { + countUpdater.accept(counter); + } +} \ No newline at end of file diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/repository/ChildProblemStatisticRepository.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/repository/ChildProblemStatisticRepository.java new file mode 100644 index 00000000..31f10665 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/repository/ChildProblemStatisticRepository.java @@ -0,0 +1,13 @@ +package com.moplus.moplus_server.statistic.Problem.repository; + +import com.moplus.moplus_server.global.error.exception.ErrorCode; +import com.moplus.moplus_server.global.error.exception.NotFoundException; +import com.moplus.moplus_server.statistic.Problem.domain.ChildProblemStatistic; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ChildProblemStatisticRepository extends JpaRepository { + default ChildProblemStatistic findByIdElseThrow(Long id) { + return findById(id) + .orElseThrow(() -> new NotFoundException(ErrorCode.CHILD_PROBLEM_STATISTIC_NOT_FOUND)); + } +} \ No newline at end of file diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/repository/ProblemSetStatisticRepository.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/repository/ProblemSetStatisticRepository.java new file mode 100644 index 00000000..349d6d6f --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/repository/ProblemSetStatisticRepository.java @@ -0,0 +1,13 @@ +package com.moplus.moplus_server.statistic.Problem.repository; + +import com.moplus.moplus_server.global.error.exception.ErrorCode; +import com.moplus.moplus_server.global.error.exception.NotFoundException; +import com.moplus.moplus_server.statistic.Problem.domain.ProblemSetStatistic; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ProblemSetStatisticRepository extends JpaRepository { + default ProblemSetStatistic findByIdElseThrow(Long id) { + return findById(id) + .orElseThrow(() -> new NotFoundException(ErrorCode.PROBLEM_SET_STATISTIC_NOT_FOUND)); + } +} \ No newline at end of file diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/repository/ProblemStatisticRepository.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/repository/ProblemStatisticRepository.java new file mode 100644 index 00000000..59bbfcc5 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/repository/ProblemStatisticRepository.java @@ -0,0 +1,13 @@ +package com.moplus.moplus_server.statistic.Problem.repository; + +import com.moplus.moplus_server.global.error.exception.ErrorCode; +import com.moplus.moplus_server.global.error.exception.NotFoundException; +import com.moplus.moplus_server.statistic.Problem.domain.ProblemStatistic; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ProblemStatisticRepository extends JpaRepository { + default ProblemStatistic findByIdElseThrow(Long id) { + return findById(id) + .orElseThrow(() -> new NotFoundException(ErrorCode.PROBLEM_STATISTIC_NOT_FOUND)); + } +} \ No newline at end of file diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/service/CountStatisticsUpdateService.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/service/CountStatisticsUpdateService.java new file mode 100644 index 00000000..29801647 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/service/CountStatisticsUpdateService.java @@ -0,0 +1,33 @@ +package com.moplus.moplus_server.statistic.Problem.service; + +import com.moplus.moplus_server.statistic.Problem.domain.StatisticCounter; +import com.moplus.moplus_server.statistic.Problem.domain.StatisticEntityTarget; +import com.moplus.moplus_server.statistic.Problem.domain.StatisticFieldType; +import com.moplus.moplus_server.statistic.Problem.repository.ChildProblemStatisticRepository; +import com.moplus.moplus_server.statistic.Problem.repository.ProblemSetStatisticRepository; +import com.moplus.moplus_server.statistic.Problem.repository.ProblemStatisticRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class CountStatisticsUpdateService { + private final ProblemStatisticRepository problemStatisticRepository; + private final ProblemSetStatisticRepository problemSetStatisticRepository; + private final ChildProblemStatisticRepository childProblemStatisticRepository; + + @Transactional + public void updateStatistics(Long id, StatisticFieldType type, StatisticEntityTarget target) { + StatisticCounter statistic = findStatistic(id, target); + statistic.updateCount(type); + } + + private StatisticCounter findStatistic(Long id, StatisticEntityTarget target) { + return switch (target) { + case PROBLEM -> problemStatisticRepository.findByIdElseThrow(id); + case PROBLEM_SET -> problemSetStatisticRepository.findByIdElseThrow(id); + case CHILD_PROBLEM -> childProblemStatisticRepository.findByIdElseThrow(id); + }; + } +} From 91cdcb3c16fb120bd1f4b7dc23a83e07783d6de9 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, 16 Mar 2025 16:41:46 +0900 Subject: [PATCH 08/48] =?UTF-8?q?[feat/#92]=20=EC=A1=B0=ED=9A=8C=EC=88=98,?= =?UTF-8?q?=20=ED=92=80=EC=9D=B4=EC=88=98=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=ED=86=B5=EA=B3=84=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EA=B3=B5=ED=86=B5=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/QChildProblemStatistic.java | 53 +++++++++++ .../Problem/domain/QCountStatistic.java | 39 +++++++++ .../Problem/domain/QProblemSetStatistic.java | 53 +++++++++++ .../Problem/domain/QProblemStatistic.java | 53 +++++++++++ .../Problem/domain/ChildProblemStatistic.java | 8 ++ .../Problem/domain/CountStatistic.java | 2 + .../Problem/domain/ProblemSetStatistic.java | 15 ++-- .../Problem/domain/ProblemStatistic.java | 15 ++-- .../CountStatisticsUpdateServiceTest.java | 87 +++++++++++++++++++ 9 files changed, 315 insertions(+), 10 deletions(-) create mode 100644 src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QChildProblemStatistic.java create mode 100644 src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QCountStatistic.java create mode 100644 src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QProblemSetStatistic.java create mode 100644 src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QProblemStatistic.java create mode 100644 src/test/java/com/moplus/moplus_server/statistic/Problem/service/CountStatisticsUpdateServiceTest.java diff --git a/src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QChildProblemStatistic.java b/src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QChildProblemStatistic.java new file mode 100644 index 00000000..a77720c4 --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QChildProblemStatistic.java @@ -0,0 +1,53 @@ +package com.moplus.moplus_server.statistic.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; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QChildProblemStatistic is a Querydsl query type for ChildProblemStatistic + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QChildProblemStatistic extends EntityPathBase { + + private static final long serialVersionUID = 1136828573L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QChildProblemStatistic childProblemStatistic = new QChildProblemStatistic("childProblemStatistic"); + + public final NumberPath childProblemId = createNumber("childProblemId", Long.class); + + public final QCountStatistic countStatistic; + + public final NumberPath id = createNumber("id", Long.class); + + public QChildProblemStatistic(String variable) { + this(ChildProblemStatistic.class, forVariable(variable), INITS); + } + + public QChildProblemStatistic(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QChildProblemStatistic(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QChildProblemStatistic(PathMetadata metadata, PathInits inits) { + this(ChildProblemStatistic.class, metadata, inits); + } + + public QChildProblemStatistic(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.countStatistic = inits.isInitialized("countStatistic") ? new QCountStatistic(forProperty("countStatistic")) : null; + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QCountStatistic.java b/src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QCountStatistic.java new file mode 100644 index 00000000..5b0ba49d --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QCountStatistic.java @@ -0,0 +1,39 @@ +package com.moplus.moplus_server.statistic.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; + + +/** + * QCountStatistic is a Querydsl query type for CountStatistic + */ +@Generated("com.querydsl.codegen.DefaultEmbeddableSerializer") +public class QCountStatistic extends BeanPath { + + private static final long serialVersionUID = 1047466257L; + + public static final QCountStatistic countStatistic = new QCountStatistic("countStatistic"); + + public final NumberPath submitCount = createNumber("submitCount", Long.class); + + public final NumberPath viewCount = createNumber("viewCount", Long.class); + + public QCountStatistic(String variable) { + super(CountStatistic.class, forVariable(variable)); + } + + public QCountStatistic(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QCountStatistic(PathMetadata metadata) { + super(CountStatistic.class, metadata); + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QProblemSetStatistic.java b/src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QProblemSetStatistic.java new file mode 100644 index 00000000..6d992ad2 --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QProblemSetStatistic.java @@ -0,0 +1,53 @@ +package com.moplus.moplus_server.statistic.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; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QProblemSetStatistic is a Querydsl query type for ProblemSetStatistic + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QProblemSetStatistic extends EntityPathBase { + + private static final long serialVersionUID = -1319940803L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QProblemSetStatistic problemSetStatistic = new QProblemSetStatistic("problemSetStatistic"); + + public final QCountStatistic countStatistic; + + public final NumberPath id = createNumber("id", Long.class); + + public final NumberPath problemSetId = createNumber("problemSetId", Long.class); + + public QProblemSetStatistic(String variable) { + this(ProblemSetStatistic.class, forVariable(variable), INITS); + } + + public QProblemSetStatistic(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QProblemSetStatistic(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QProblemSetStatistic(PathMetadata metadata, PathInits inits) { + this(ProblemSetStatistic.class, metadata, inits); + } + + public QProblemSetStatistic(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.countStatistic = inits.isInitialized("countStatistic") ? new QCountStatistic(forProperty("countStatistic")) : null; + } + +} + diff --git a/src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QProblemStatistic.java b/src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QProblemStatistic.java new file mode 100644 index 00000000..8eae98c4 --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/statistic/Problem/domain/QProblemStatistic.java @@ -0,0 +1,53 @@ +package com.moplus.moplus_server.statistic.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; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QProblemStatistic is a Querydsl query type for ProblemStatistic + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QProblemStatistic extends EntityPathBase { + + private static final long serialVersionUID = 843488641L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QProblemStatistic problemStatistic = new QProblemStatistic("problemStatistic"); + + public final QCountStatistic countStatistic; + + public final NumberPath id = createNumber("id", Long.class); + + public final NumberPath problemId = createNumber("problemId", Long.class); + + public QProblemStatistic(String variable) { + this(ProblemStatistic.class, forVariable(variable), INITS); + } + + public QProblemStatistic(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QProblemStatistic(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QProblemStatistic(PathMetadata metadata, PathInits inits) { + this(ProblemStatistic.class, metadata, inits); + } + + public QProblemStatistic(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.countStatistic = inits.isInitialized("countStatistic") ? new QCountStatistic(forProperty("countStatistic")) : null; + } + +} + diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ChildProblemStatistic.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ChildProblemStatistic.java index a69f4f99..9740c72c 100644 --- a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ChildProblemStatistic.java +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ChildProblemStatistic.java @@ -39,4 +39,12 @@ public void addViewCount() { public void addSubmitCount() { this.countStatistic.addSubmitCount(); } + + public Long getViewCount() { + return this.countStatistic.getViewCount(); + } + + public Long getSubmitCount() { + return this.countStatistic.getSubmitCount(); + } } diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountStatistic.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountStatistic.java index 0e38e52d..91e1f0c6 100644 --- a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountStatistic.java +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/CountStatistic.java @@ -1,7 +1,9 @@ package com.moplus.moplus_server.statistic.Problem.domain; import jakarta.persistence.Embeddable; +import lombok.Getter; +@Getter @Embeddable public class CountStatistic implements StatisticCounter { private Long viewCount; diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemSetStatistic.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemSetStatistic.java index e7021d80..92db7f42 100644 --- a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemSetStatistic.java +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemSetStatistic.java @@ -21,16 +21,13 @@ public class ProblemSetStatistic implements StatisticCounter { private Long id; private Long problemSetId; - private Long viewCount; - private Long submitCount; @Embedded private CountStatistic countStatistic; - public ProblemSetStatistic(Long problemSetId, Long viewCount, Long submitCount) { + public ProblemSetStatistic(Long problemSetId) { this.problemSetId = problemSetId; - this.viewCount = viewCount; - this.submitCount = submitCount; + this.countStatistic = new CountStatistic(); } @Override @@ -42,4 +39,12 @@ public void addViewCount() { public void addSubmitCount() { this.countStatistic.addSubmitCount(); } + + public Long getViewCount() { + return this.countStatistic.getViewCount(); + } + + public Long getSubmitCount() { + return this.countStatistic.getSubmitCount(); + } } diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemStatistic.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemStatistic.java index ba3be5a0..0ab5c68e 100644 --- a/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemStatistic.java +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/domain/ProblemStatistic.java @@ -22,17 +22,14 @@ public class ProblemStatistic implements StatisticCounter { private Long id; private Long problemId; - private Long viewCount; - private Long submitCount; @Embedded private CountStatistic countStatistic; @Builder - public ProblemStatistic(Long problemId, Long viewCount, Long submitCount) { + public ProblemStatistic(Long problemId) { this.problemId = problemId; - this.viewCount = viewCount; - this.submitCount = submitCount; + this.countStatistic = new CountStatistic(); } @Override @@ -44,4 +41,12 @@ public void addViewCount() { public void addSubmitCount() { this.countStatistic.addSubmitCount(); } + + public Long getViewCount() { + return this.countStatistic.getViewCount(); + } + + public Long getSubmitCount() { + return this.countStatistic.getSubmitCount(); + } } diff --git a/src/test/java/com/moplus/moplus_server/statistic/Problem/service/CountStatisticsUpdateServiceTest.java b/src/test/java/com/moplus/moplus_server/statistic/Problem/service/CountStatisticsUpdateServiceTest.java new file mode 100644 index 00000000..94e133e3 --- /dev/null +++ b/src/test/java/com/moplus/moplus_server/statistic/Problem/service/CountStatisticsUpdateServiceTest.java @@ -0,0 +1,87 @@ +package com.moplus.moplus_server.statistic.Problem.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +import com.moplus.moplus_server.statistic.Problem.domain.ChildProblemStatistic; +import com.moplus.moplus_server.statistic.Problem.domain.ProblemSetStatistic; +import com.moplus.moplus_server.statistic.Problem.domain.ProblemStatistic; +import com.moplus.moplus_server.statistic.Problem.domain.StatisticEntityTarget; +import com.moplus.moplus_server.statistic.Problem.domain.StatisticFieldType; +import com.moplus.moplus_server.statistic.Problem.repository.ChildProblemStatisticRepository; +import com.moplus.moplus_server.statistic.Problem.repository.ProblemSetStatisticRepository; +import com.moplus.moplus_server.statistic.Problem.repository.ProblemStatisticRepository; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class CountStatisticsUpdateServiceTest { + + @InjectMocks + private CountStatisticsUpdateService countStatisticsUpdateService; + + @Mock + private ProblemStatisticRepository problemStatisticRepository; + @Mock + private ProblemSetStatisticRepository problemSetStatisticRepository; + @Mock + private ChildProblemStatisticRepository childProblemStatisticRepository; + + @Test + void 문항_조회수_증가() { + // given + Long problemId = 1L; + ProblemStatistic problemStatistic = new ProblemStatistic(problemId); + given(problemStatisticRepository.findByIdElseThrow(problemId)) + .willReturn(problemStatistic); + + // when + countStatisticsUpdateService.updateStatistics(problemId, StatisticFieldType.VIEW, + StatisticEntityTarget.PROBLEM); + + // then + verify(problemStatisticRepository).findByIdElseThrow(problemId); + assertThat(problemStatistic.getViewCount()).isEqualTo(1L); + assertThat(problemStatistic.getSubmitCount()).isEqualTo(0L); + } + + @Test + void 문항세트_풀이수_증가() { + // given + Long problemSetId = 1L; + ProblemSetStatistic problemSetStatistic = new ProblemSetStatistic(problemSetId); + given(problemSetStatisticRepository.findByIdElseThrow(problemSetId)) + .willReturn(problemSetStatistic); + + // when + countStatisticsUpdateService.updateStatistics(problemSetId, StatisticFieldType.SUBMIT, + StatisticEntityTarget.PROBLEM_SET); + + // then + verify(problemSetStatisticRepository).findByIdElseThrow(problemSetId); + assertThat(problemSetStatistic.getSubmitCount()).isEqualTo(1L); + assertThat(problemSetStatistic.getViewCount()).isEqualTo(0L); + } + + @Test + void 새끼문항_조회수_증가() { + // given + Long childProblemId = 1L; + ChildProblemStatistic childProblemStatistic = new ChildProblemStatistic(childProblemId); + given(childProblemStatisticRepository.findByIdElseThrow(childProblemId)) + .willReturn(childProblemStatistic); + + // when + countStatisticsUpdateService.updateStatistics(childProblemId, StatisticFieldType.VIEW, + StatisticEntityTarget.CHILD_PROBLEM); + + // then + verify(childProblemStatisticRepository).findByIdElseThrow(childProblemId); + assertThat(childProblemStatistic.getViewCount()).isEqualTo(1L); + assertThat(childProblemStatistic.getSubmitCount()).isEqualTo(0L); + } +} \ No newline at end of file From 2d598e076c14f05ee85b73346f77153b2c6da54a Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 18 Mar 2025 17:37:50 +0900 Subject: [PATCH 09/48] =?UTF-8?q?[feat/#94]=20=EC=A0=84=EC=B2=B4=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=EC=A1=B0=ED=9A=8C=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/submit/controller/.gitkeep | 0 .../controller/ClientGetController.java | 31 ++++++++ .../moplus_server/client/submit/dto/.gitkeep | 0 .../dto/response/AllProblemGetResponse.java | 26 +++++++ .../submit/dto/response/DayProgress.java | 21 ++++++ .../client/submit/repository/.gitkeep | 0 .../repository/ProblemSubmitRepository.java | 9 +++ .../client/submit/service/.gitkeep | 0 .../submit/service/ClientGetService.java | 74 +++++++++++++++++++ 9 files changed, 161 insertions(+) delete mode 100644 src/main/java/com/moplus/moplus_server/client/submit/controller/.gitkeep create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java delete mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/.gitkeep create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/response/AllProblemGetResponse.java create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/response/DayProgress.java delete mode 100644 src/main/java/com/moplus/moplus_server/client/submit/repository/.gitkeep create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/repository/ProblemSubmitRepository.java delete mode 100644 src/main/java/com/moplus/moplus_server/client/submit/service/.gitkeep create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/.gitkeep b/src/main/java/com/moplus/moplus_server/client/submit/controller/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java new file mode 100644 index 00000000..d7ace94e --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java @@ -0,0 +1,31 @@ +package com.moplus.moplus_server.client.submit.controller; + +import com.moplus.moplus_server.client.submit.dto.response.AllProblemGetResponse; +import com.moplus.moplus_server.client.submit.service.ClientGetService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +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.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "클라이언트 조회", description = "클라이언트 조회 관련 API") +@RestController +@RequestMapping("/api/v1/client") +@RequiredArgsConstructor +public class ClientGetController { + + private final ClientGetService clientGetService; + + @GetMapping("allProblem/{year}/{month}") + @Operation(summary = "전체 문제 조회", description = "월별 문제들에 대한 진행도와 정보들을 조회합니다.") + public ResponseEntity> getAllProblem( + @PathVariable int year, + @PathVariable int month + ) { + return ResponseEntity.ok(clientGetService.getAllProblem(year, month)); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/.gitkeep b/src/main/java/com/moplus/moplus_server/client/submit/dto/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/AllProblemGetResponse.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/AllProblemGetResponse.java new file mode 100644 index 00000000..91c3c6fa --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/AllProblemGetResponse.java @@ -0,0 +1,26 @@ +package com.moplus.moplus_server.client.submit.dto.response; + +import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; +import java.time.LocalDate; +import java.util.List; +import lombok.Builder; + +@Builder +public record AllProblemGetResponse( + Long publishId, + LocalDate date, + DayProgress progress, + List problemStatuses, + String mainProblemImageUrl +) { + public static AllProblemGetResponse of(Long publishId, LocalDate date, DayProgress progress, + List problemStatuses, String mainProblemImageUrl) { + return AllProblemGetResponse.builder() + .publishId(publishId) + .date(date) + .progress(progress) + .problemStatuses(problemStatuses) + .mainProblemImageUrl(mainProblemImageUrl) + .build(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/DayProgress.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/DayProgress.java new file mode 100644 index 00000000..5e9251d3 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/DayProgress.java @@ -0,0 +1,21 @@ +package com.moplus.moplus_server.client.submit.dto.response; + +import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; +import java.util.List; + +public enum DayProgress { + COMPLETE, + INCOMPLETE, + IN_PROGRESS; + public static DayProgress determineDayProgress(List problemStatuses) { + if (problemStatuses.isEmpty()) { + return INCOMPLETE; + } + else if (problemStatuses.contains(ProblemSubmitStatus.IN_PROGRESS)) { + return IN_PROGRESS; + } + else{ + return COMPLETE; + } + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/repository/.gitkeep b/src/main/java/com/moplus/moplus_server/client/submit/repository/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/com/moplus/moplus_server/client/submit/repository/ProblemSubmitRepository.java b/src/main/java/com/moplus/moplus_server/client/submit/repository/ProblemSubmitRepository.java new file mode 100644 index 00000000..5edb0892 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/repository/ProblemSubmitRepository.java @@ -0,0 +1,9 @@ +package com.moplus.moplus_server.client.submit.repository; + +import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ProblemSubmitRepository extends JpaRepository { + List findByMemberIdAndPublishId(Long memberId, Long publishId); +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/.gitkeep b/src/main/java/com/moplus/moplus_server/client/submit/service/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java new file mode 100644 index 00000000..0f0b20cf --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java @@ -0,0 +1,74 @@ +package com.moplus.moplus_server.client.submit.service; + + +import com.moplus.moplus_server.admin.publish.domain.Publish; +import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; +import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; +import com.moplus.moplus_server.client.submit.dto.response.AllProblemGetResponse; +import com.moplus.moplus_server.client.submit.dto.response.DayProgress; +import com.moplus.moplus_server.client.submit.repository.ProblemSubmitRepository; +import com.moplus.moplus_server.domain.problem.domain.problem.Problem; +import com.moplus.moplus_server.domain.problem.repository.ProblemRepository; +import com.moplus.moplus_server.domain.problemset.repository.ProblemSetRepository; +import com.moplus.moplus_server.domain.publish.repository.PublishRepository; +import com.moplus.moplus_server.global.error.exception.ErrorCode; +import com.moplus.moplus_server.global.error.exception.InvalidValueException; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class ClientGetService { + + private final PublishRepository publishRepository; + private final ProblemSubmitRepository problemSubmitRepository; + private final ProblemRepository problemRepository; + private final ProblemSetRepository problemSetRepository; + + @Transactional(readOnly = true) + public List getAllProblem(int year, int month) { + + Long memberId = 1L; + + if (month < 1 || month > 12) { + throw new InvalidValueException(ErrorCode.INVALID_MONTH_ERROR); + } + LocalDate startDate = LocalDate.of(year, month, 1); + LocalDate endDate = startDate.withDayOfMonth(startDate.lengthOfMonth()); + + // 해당 월 모든 Publish 조회 + List publishes = publishRepository.findByPublishedDateBetween(startDate, endDate); + + List result = new ArrayList<>(); + + for (Publish publish : publishes) { + Long publishId = publish.getId(); + LocalDate date = publish.getPublishedDate(); + + // 날짜별 사용자 제출 정보 조회 + List submissions = problemSubmitRepository.findByMemberIdAndPublishId(memberId, publishId); + List problemStatuses = submissions.stream() + .map(ProblemSubmit::getStatus) + .toList(); + + // 사용자 제출 정보 바탕으로 진행도 결정 + DayProgress progress = DayProgress.determineDayProgress(problemStatuses); + String mainProblemImageUrl = getMainProblemImageUrl(publish.getProblemSetId()); + + result.add(AllProblemGetResponse.of(publishId, date, progress, problemStatuses, mainProblemImageUrl)); + } + return result; + } + + private String getMainProblemImageUrl(Long problemSetId) { + return problemSetRepository.findById(problemSetId) + .flatMap(problemSet -> problemSet.getProblemIds().stream().findFirst()) + .flatMap(problemRepository::findById) + .map(Problem::getMainProblemImageUrl) + .orElse(null); + } +} From 59057899f0dcec8f6da14902764ce7a214b999c7 Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 18 Mar 2025 19:43:30 +0900 Subject: [PATCH 10/48] =?UTF-8?q?[feat/#94]=20=ED=95=B4=EC=84=A4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ClientGetController.java | 11 +++++ .../response/ChildProblemDetailResponse.java | 20 ++++++++ .../dto/response/CommentaryGetResponse.java | 28 +++++++++++ .../dto/response/PrescriptionResponse.java | 18 +++++++ .../dto/response/ProblemDetailResponse.java | 20 ++++++++ .../ChildProblemSubmitRepository.java | 16 +++++++ .../repository/ProblemSubmitRepository.java | 10 ++++ .../submit/service/ClientGetService.java | 47 +++++++++++++++++++ .../global/error/exception/ErrorCode.java | 6 +++ 9 files changed, 176 insertions(+) create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemDetailResponse.java create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/response/CommentaryGetResponse.java create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/response/PrescriptionResponse.java create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemDetailResponse.java create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/repository/ChildProblemSubmitRepository.java diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java index d7ace94e..d959000f 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java @@ -1,6 +1,7 @@ package com.moplus.moplus_server.client.submit.controller; import com.moplus.moplus_server.client.submit.dto.response.AllProblemGetResponse; +import com.moplus.moplus_server.client.submit.dto.response.CommentaryGetResponse; import com.moplus.moplus_server.client.submit.service.ClientGetService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -10,6 +11,7 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @Tag(name = "클라이언트 조회", description = "클라이언트 조회 관련 API") @@ -20,6 +22,15 @@ public class ClientGetController { private final ClientGetService clientGetService; + @GetMapping("commentary") + @Operation(summary = "해설 조회", description = "문항 별 해설/처방을 조회합니다.") + public ResponseEntity getCommentary( + @RequestParam(value = "publishId", required = false) Long publishId, + @RequestParam(value = "problemId", required = false) Long problemId + ) { + return ResponseEntity.ok(clientGetService.getCommentary(publishId, problemId)); + } + @GetMapping("allProblem/{year}/{month}") @Operation(summary = "전체 문제 조회", description = "월별 문제들에 대한 진행도와 정보들을 조회합니다.") public ResponseEntity> getAllProblem( diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemDetailResponse.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemDetailResponse.java new file mode 100644 index 00000000..8250034d --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemDetailResponse.java @@ -0,0 +1,20 @@ +package com.moplus.moplus_server.client.submit.dto.response; + +import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; +import java.util.List; +import lombok.Builder; + +@Builder +public record ChildProblemDetailResponse( + String imageUrl, + List prescriptionImageUrls, + ChildProblemSubmitStatus submitStatus +) { + public static ChildProblemDetailResponse of(String imageUrl, List prescriptionImageUrls, ChildProblemSubmitStatus submitStatus) { + return ChildProblemDetailResponse.builder() + .imageUrl(imageUrl) + .prescriptionImageUrls(prescriptionImageUrls) + .submitStatus(submitStatus) + .build(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/CommentaryGetResponse.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/CommentaryGetResponse.java new file mode 100644 index 00000000..5c0ba7c7 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/CommentaryGetResponse.java @@ -0,0 +1,28 @@ +package com.moplus.moplus_server.client.submit.dto.response; + +import lombok.Builder; + +@Builder +public record CommentaryGetResponse( + int problemNumber, + String answer, + String mainAnalysisImageUrl, + String mainHandwritingExplanationImageUrl, + String readingTipImageUrl, + String seniorTipImageUrl, + PrescriptionResponse prescription +) { + public static CommentaryGetResponse of(int problemNumber, String answer, String mainAnalysisImageUrl, + String mainHandwritingExplanationImageUrl, String readingTipImageUrl, + String seniorTipImageUrl, PrescriptionResponse prescription) { + return CommentaryGetResponse.builder() + .problemNumber(problemNumber) + .answer(answer) + .mainAnalysisImageUrl(mainAnalysisImageUrl) + .mainHandwritingExplanationImageUrl(mainHandwritingExplanationImageUrl) + .readingTipImageUrl(readingTipImageUrl) + .seniorTipImageUrl(seniorTipImageUrl) + .prescription(prescription) + .build(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/PrescriptionResponse.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/PrescriptionResponse.java new file mode 100644 index 00000000..31e5a01b --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/PrescriptionResponse.java @@ -0,0 +1,18 @@ +package com.moplus.moplus_server.client.submit.dto.response; + +import java.util.List; +import lombok.Builder; + +@Builder +public record PrescriptionResponse( + List childProblem, + ProblemDetailResponse mainProblem +) { + public static PrescriptionResponse of(List childProblem, + ProblemDetailResponse mainProblem) { + return PrescriptionResponse.builder() + .childProblem(childProblem) + .mainProblem(mainProblem) + .build(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemDetailResponse.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemDetailResponse.java new file mode 100644 index 00000000..aa31cf09 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemDetailResponse.java @@ -0,0 +1,20 @@ +package com.moplus.moplus_server.client.submit.dto.response; + +import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; +import java.util.List; +import lombok.Builder; + +@Builder +public record ProblemDetailResponse( + String imageUrl, + List prescriptionImageUrls, + ProblemSubmitStatus submitStatus +) { + public static ProblemDetailResponse of(String imageUrl, List prescriptionImageUrls, ProblemSubmitStatus submitStatus) { + return ProblemDetailResponse.builder() + .imageUrl(imageUrl) + .prescriptionImageUrls(prescriptionImageUrls) + .submitStatus(submitStatus) + .build(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/repository/ChildProblemSubmitRepository.java b/src/main/java/com/moplus/moplus_server/client/submit/repository/ChildProblemSubmitRepository.java new file mode 100644 index 00000000..6d33c1d5 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/repository/ChildProblemSubmitRepository.java @@ -0,0 +1,16 @@ +package com.moplus.moplus_server.client.submit.repository; + +import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmit; +import com.moplus.moplus_server.global.error.exception.ErrorCode; +import com.moplus.moplus_server.global.error.exception.NotFoundException; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ChildProblemSubmitRepository extends JpaRepository { + Optional findByMemberIdAndPublishIdAndChildProblemId(Long memberId, Long publishId, Long problemId); + + default ChildProblemSubmit findByMemberIdAndPublishIdAndChildProblemIdElseThrow(Long memberId, Long publishId, Long problemId) { + return findByMemberIdAndPublishIdAndChildProblemId(memberId, publishId, problemId).orElseThrow( + () -> new NotFoundException(ErrorCode.CHILD_PROBLEM_SUBMIT_NOT_CONFIRMED)); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/repository/ProblemSubmitRepository.java b/src/main/java/com/moplus/moplus_server/client/submit/repository/ProblemSubmitRepository.java index 5edb0892..d9ff2554 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/repository/ProblemSubmitRepository.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/repository/ProblemSubmitRepository.java @@ -1,9 +1,19 @@ package com.moplus.moplus_server.client.submit.repository; import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; +import com.moplus.moplus_server.global.error.exception.ErrorCode; +import com.moplus.moplus_server.global.error.exception.NotFoundException; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; public interface ProblemSubmitRepository extends JpaRepository { List findByMemberIdAndPublishId(Long memberId, Long publishId); + + Optional findByMemberIdAndPublishIdAndProblemId(Long memberId, Long publishId, Long problemId); + + default ProblemSubmit findByMemberIdAndPublishIdAndProblemIdElseThrow(Long memberId, Long publishId, Long problemId) { + return findByMemberIdAndPublishIdAndProblemId(memberId, publishId, problemId).orElseThrow( + () -> new NotFoundException(ErrorCode.PROBLEM_SUBMIT_NOT_CONFIRMED)); + } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java index 0f0b20cf..f545c02b 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java @@ -5,7 +5,12 @@ import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; import com.moplus.moplus_server.client.submit.dto.response.AllProblemGetResponse; +import com.moplus.moplus_server.client.submit.dto.response.ChildProblemDetailResponse; +import com.moplus.moplus_server.client.submit.dto.response.CommentaryGetResponse; import com.moplus.moplus_server.client.submit.dto.response.DayProgress; +import com.moplus.moplus_server.client.submit.dto.response.PrescriptionResponse; +import com.moplus.moplus_server.client.submit.dto.response.ProblemDetailResponse; +import com.moplus.moplus_server.client.submit.repository.ChildProblemSubmitRepository; import com.moplus.moplus_server.client.submit.repository.ProblemSubmitRepository; import com.moplus.moplus_server.domain.problem.domain.problem.Problem; import com.moplus.moplus_server.domain.problem.repository.ProblemRepository; @@ -28,6 +33,48 @@ public class ClientGetService { private final ProblemSubmitRepository problemSubmitRepository; private final ProblemRepository problemRepository; private final ProblemSetRepository problemSetRepository; + private final ChildProblemSubmitRepository childProblemSubmitRepository; + + + @Transactional(readOnly = true) + public CommentaryGetResponse getCommentary(Long publishId, Long problemId) { + + Long memberId = 1L; + + // 문항 제출 조회 + ProblemSubmit problemSubmit = problemSubmitRepository.findByMemberIdAndPublishIdAndProblemIdElseThrow(memberId, + publishId, problemId); + + // 문항 해설 생성 + Problem problem = problemRepository.findByIdElseThrow(problemId); + ProblemDetailResponse mainProblem = ProblemDetailResponse.of( + problem.getMainProblemImageUrl(), + problem.getPrescriptionImageUrls(), + problemSubmit.getStatus() + ); + + // 새끼문항 해설 생성 + List childProblem = problem.getChildProblems().stream() + .map(cp -> ChildProblemDetailResponse.of( + cp.getImageUrl(), + cp.getPrescriptionImageUrls(), + childProblemSubmitRepository.findByMemberIdAndPublishIdAndChildProblemIdElseThrow(memberId, publishId, + cp.getId()).getStatus() + )).toList(); + + // 처방 정보 생성 + PrescriptionResponse prescription = PrescriptionResponse.of(childProblem, mainProblem); + + return CommentaryGetResponse.of( + problem.getNumber(), + problem.getAnswer(), + problem.getMainAnalysisImageUrl(), + problem.getMainHandwritingExplanationImageUrl(), + problem.getReadingTipImageUrl(), + problem.getSeniorTipImageUrl(), + prescription + ); + } @Transactional(readOnly = true) public List getAllProblem(int year, int month) { 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 52a7fa02..193b4f69 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 @@ -76,6 +76,12 @@ public enum ErrorCode { ALREADY_PUBLISHED_ERROR(HttpStatus.BAD_REQUEST, "이미 발행된 문항세트는 컨펌해제할 수 없습니다."), PROBLEM_SET_DELETED(HttpStatus.BAD_REQUEST, "삭제된 문항세트는 발행할 수 없습니다"), PROBLEM_SET_NOT_CONFIRMED(HttpStatus.BAD_REQUEST, "컨펌되지 않은 문항세트는 발행할 수 없습니다"), + + // 문항 제출 + PROBLEM_SUBMIT_NOT_CONFIRMED(HttpStatus.NOT_FOUND, "문항 제출 정보를 찾을 수 없습니다."), + + // 새끼문항 제출 + CHILD_PROBLEM_SUBMIT_NOT_CONFIRMED(HttpStatus.NOT_FOUND, "새끼문항 제출 정보를 찾을 수 없습니다."), ; From 25a7adabcc72a692d40bd543fb1dfc443a5bcb13 Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 18 Mar 2025 20:08:51 +0900 Subject: [PATCH 11/48] =?UTF-8?q?[feat/#94]=20=EC=A7=84=ED=96=89=EC=A4=91?= =?UTF-8?q?=EC=9D=B8=20=EB=AC=B8=ED=95=AD=EC=A0=9C=EC=B6=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ClientSubmitController.java | 30 +++++++++++++++++++ .../request/ProblemSubmitCreateRequest.java | 21 +++++++++++++ .../submit/service/ClientSubmitService.java | 30 +++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/request/ProblemSubmitCreateRequest.java create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java new file mode 100644 index 00000000..3ae0046a --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java @@ -0,0 +1,30 @@ +package com.moplus.moplus_server.client.submit.controller; + +import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitCreateRequest; +import com.moplus.moplus_server.client.submit.service.ClientSubmitService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "클라이언트 제출", description = "클라이언트 제출 관련 API") +@RestController +@RequestMapping("/api/v1/client") +@RequiredArgsConstructor +public class ClientSubmitController { + + private final ClientSubmitService clientSubmitService; + + @PostMapping("problemSubmit") + @Operation(summary = "문항 제출 데이터 생성", description = "문항 제출을 '진행중'으로 생성합니다.") + public ResponseEntity createProblemSubmit( + @RequestBody ProblemSubmitCreateRequest request + ) { + clientSubmitService.createProblemSubmit(request); + return ResponseEntity.ok(null); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ProblemSubmitCreateRequest.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ProblemSubmitCreateRequest.java new file mode 100644 index 00000000..7aec4f0c --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ProblemSubmitCreateRequest.java @@ -0,0 +1,21 @@ +package com.moplus.moplus_server.client.submit.dto.request; + +import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; +import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; +import jakarta.validation.constraints.NotNull; + +public record ProblemSubmitCreateRequest ( + @NotNull(message = "발행 ID는 필수입니다.") + Long publishId, + @NotNull(message = "문항 ID는 필수입니다.") + Long problemId +){ + public ProblemSubmit toEntity(Long memberId) { + return ProblemSubmit.builder() + .memberId(memberId) + .publishId(this.publishId) + .problemId(this.problemId) + .status(ProblemSubmitStatus.IN_PROGRESS) + .build(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java new file mode 100644 index 00000000..6ff89a75 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java @@ -0,0 +1,30 @@ +package com.moplus.moplus_server.client.submit.service; + + +import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; +import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitCreateRequest; +import com.moplus.moplus_server.client.submit.repository.ProblemSubmitRepository; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class ClientSubmitService { + + private final ProblemSubmitRepository problemSubmitRepository; + + @Transactional + public void createProblemSubmit(ProblemSubmitCreateRequest request) { + + Long memberId = 1L; + // 제출이력이 없을때만 생성 + Optional existingProblemSubmit = problemSubmitRepository.findByMemberIdAndPublishIdAndProblemId(memberId, + request.publishId(), request.problemId()); + if (existingProblemSubmit.isEmpty()) { + ProblemSubmit problemSubmit = request.toEntity(memberId); + problemSubmitRepository.save(problemSubmit); + } + } +} From d3e83b4dce250c569f778d0acbb7bb3a615143a9 Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 18 Mar 2025 20:44:16 +0900 Subject: [PATCH 12/48] =?UTF-8?q?[feat/#94]=20=EC=8B=9C=EC=9E=91=EC=A0=84?= =?UTF-8?q?=20=EC=83=88=EB=81=BC=EB=AC=B8=ED=95=AD=EC=A0=9C=EC=B6=9C=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ClientSubmitController.java | 12 +++- .../domain/ChildProblemSubmitStatus.java | 3 +- .../ChildProblemSubmitCreateRequest.java | 7 ++ .../submit/service/ClientSubmitService.java | 65 +++++++++++++++++++ .../problem/repository/ProblemRepository.java | 1 + .../publish/repository/PublishRepository.java | 6 ++ 6 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitCreateRequest.java diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java index 3ae0046a..d9d4426f 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java @@ -1,5 +1,6 @@ package com.moplus.moplus_server.client.submit.controller; +import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitCreateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitCreateRequest; import com.moplus.moplus_server.client.submit.service.ClientSubmitService; import io.swagger.v3.oas.annotations.Operation; @@ -20,11 +21,20 @@ public class ClientSubmitController { private final ClientSubmitService clientSubmitService; @PostMapping("problemSubmit") - @Operation(summary = "문항 제출 데이터 생성", description = "문항 제출을 '진행중'으로 생성합니다.") + @Operation(summary = "문항 제출 생성", description = "문항 제출을 '진행중'으로 생성합니다.") public ResponseEntity createProblemSubmit( @RequestBody ProblemSubmitCreateRequest request ) { clientSubmitService.createProblemSubmit(request); return ResponseEntity.ok(null); } + + @PostMapping("childProblemSubmit") + @Operation(summary = "새끼문항 제출 생성", description = "문항에 속한 새끼문항들을 '시작전'으로 생성합니다.") + public ResponseEntity createProblemSubmit( + @RequestBody ChildProblemSubmitCreateRequest request + ) { + clientSubmitService.createChildProblemSubmit(request); + return ResponseEntity.ok(null); + } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmitStatus.java b/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmitStatus.java index 541c308e..18e1ee04 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmitStatus.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmitStatus.java @@ -3,5 +3,6 @@ public enum ChildProblemSubmitStatus { CORRECT, INCORRECT, - RETRY_CORRECT + RETRY_CORRECT, + NOT_STARTED } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitCreateRequest.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitCreateRequest.java new file mode 100644 index 00000000..daeb5674 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitCreateRequest.java @@ -0,0 +1,7 @@ +package com.moplus.moplus_server.client.submit.dto.request; + +public record ChildProblemSubmitCreateRequest( + Long publishId, + Long problemId +) { +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java index 6ff89a75..57e959aa 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java @@ -1,9 +1,19 @@ package com.moplus.moplus_server.client.submit.service; +import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmit; +import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; +import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; +import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitCreateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitCreateRequest; +import com.moplus.moplus_server.client.submit.repository.ChildProblemSubmitRepository; import com.moplus.moplus_server.client.submit.repository.ProblemSubmitRepository; +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.ProblemRepository; +import com.moplus.moplus_server.domain.publish.repository.PublishRepository; +import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -14,11 +24,21 @@ public class ClientSubmitService { private final ProblemSubmitRepository problemSubmitRepository; + private final ProblemRepository problemRepository; + private final ChildProblemSubmitRepository childProblemSubmitRepository; + private final PublishRepository publishRepository; @Transactional public void createProblemSubmit(ProblemSubmitCreateRequest request) { Long memberId = 1L; + + // 존재하는 발행인지 검증 + publishRepository.existsByIdElseThrow(request.publishId()); + + // 존재하는 문항인지 검증 + problemRepository.existsByIdElseThrow(request.problemId()); + // 제출이력이 없을때만 생성 Optional existingProblemSubmit = problemSubmitRepository.findByMemberIdAndPublishIdAndProblemId(memberId, request.publishId(), request.problemId()); @@ -27,4 +47,49 @@ public void createProblemSubmit(ProblemSubmitCreateRequest request) { problemSubmitRepository.save(problemSubmit); } } + + @Transactional + public void createChildProblemSubmit(ChildProblemSubmitCreateRequest request) { + + Long memberId = 1L; + + // 존재하는 발행인지 검증 + publishRepository.existsByIdElseThrow(request.publishId()); + + // 존재하는 문항인지 검증 + problemRepository.existsByIdElseThrow(request.problemId()); + + // 문항제출 이력이 없으면 문항제출 생성 + Optional existingProblemSubmit = problemSubmitRepository.findByMemberIdAndPublishIdAndProblemId(memberId, + request.publishId(), request.problemId()); + if (existingProblemSubmit.isEmpty()) { + ProblemSubmit problemSubmit = ProblemSubmit.builder() + .memberId(memberId) + .publishId(request.publishId()) + .problemId(request.problemId()) + .status(ProblemSubmitStatus.IN_PROGRESS) + .build(); + problemSubmitRepository.save(problemSubmit); + } + + // 문항의 새끼문항 조회 + Problem problem = problemRepository.findByIdElseThrow(request.problemId()); + List childProblems = problem.getChildProblems(); + + // 제출이력이 없을떄만 생성 + for (ChildProblem childProblem : childProblems) { + Long childProblemId = childProblem.getId(); + + Optional existingChildProblemSubmit = childProblemSubmitRepository.findByMemberIdAndPublishIdAndChildProblemId(memberId, + request.publishId(), childProblemId); + ChildProblemSubmit childProblemSubmit = ChildProblemSubmit.builder() + .memberId(memberId) + .publishId(request.publishId()) + .childProblemId(childProblemId) + .status(ChildProblemSubmitStatus.NOT_STARTED) + .build(); + childProblemSubmitRepository.save(childProblemSubmit); + } + } + } 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 4529f682..3779f0f0 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 @@ -25,4 +25,5 @@ default void existsByProblemAdminIdElseThrow(ProblemCustomId problemCustomId) { default Problem findByIdElseThrow(Long id) { return findById(id).orElseThrow(() -> new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND)); } + } diff --git a/src/main/java/com/moplus/moplus_server/domain/publish/repository/PublishRepository.java b/src/main/java/com/moplus/moplus_server/domain/publish/repository/PublishRepository.java index d7422a1b..37dcce37 100644 --- a/src/main/java/com/moplus/moplus_server/domain/publish/repository/PublishRepository.java +++ b/src/main/java/com/moplus/moplus_server/domain/publish/repository/PublishRepository.java @@ -15,4 +15,10 @@ default Publish findByIdElseThrow(Long publishId) { } List findByProblemSetId(Long problemSetId); + + default void existsByIdElseThrow(Long id) { + if (!existsById(id)) { + throw new NotFoundException(ErrorCode.PUBLISH_NOT_FOUND); + } + } } From 879985ff0e99b9b46e76537440000f185312e39c Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 18 Mar 2025 22:03:31 +0900 Subject: [PATCH 13/48] =?UTF-8?q?[feat/#94]=20=EB=AC=B8=ED=95=AD=20?= =?UTF-8?q?=EC=A0=9C=EC=B6=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20api?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ClientSubmitController.java | 11 ++++++++ .../client/submit/domain/ProblemSubmit.java | 4 +++ .../submit/domain/ProblemSubmitStatus.java | 15 ++++++++++- .../ChildProblemSubmitCreateRequest.java | 4 +++ .../request/ProblemSubmitUpdateRequest.java | 12 +++++++++ .../submit/service/ClientGetService.java | 2 -- .../submit/service/ClientSubmitService.java | 26 ++++++++++++++++--- 7 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/request/ProblemSubmitUpdateRequest.java diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java index d9d4426f..012e0d93 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java @@ -1,13 +1,16 @@ package com.moplus.moplus_server.client.submit.controller; +import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitCreateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitCreateRequest; +import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitUpdateRequest; import com.moplus.moplus_server.client.submit.service.ClientSubmitService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; 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; @@ -29,6 +32,14 @@ public ResponseEntity createProblemSubmit( return ResponseEntity.ok(null); } + @PutMapping("problemSubmit") + @Operation(summary = "문항 제출 업데이트", description = "제출한 답안을 바탕으로 문항 제출의 상태를 업데이트합니다.") + public ResponseEntity updateProblemSubmit( + @RequestBody ProblemSubmitUpdateRequest request + ) { + return ResponseEntity.ok(clientSubmitService.updateProblemSubmit(request)); + } + @PostMapping("childProblemSubmit") @Operation(summary = "새끼문항 제출 생성", description = "문항에 속한 새끼문항들을 '시작전'으로 생성합니다.") public ResponseEntity createProblemSubmit( diff --git a/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmit.java b/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmit.java index 63ae280b..02a6be91 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmit.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmit.java @@ -37,4 +37,8 @@ public ProblemSubmit(Long memberId, Long publishId, Long problemId, ProblemSubmi this.problemId = problemId; this.status = status; } + + public void updateStatus(ProblemSubmitStatus status) { + this.status = status; + } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmitStatus.java b/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmitStatus.java index e42b77d1..be01991f 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmitStatus.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmitStatus.java @@ -4,5 +4,18 @@ public enum ProblemSubmitStatus { CORRECT, INCORRECT, IN_PROGRESS, - RETRY_CORRECT + RETRY_CORRECT; + public static ProblemSubmitStatus determineStatus(ProblemSubmitStatus currentStatus, String memberAnswer, String problemAnswer) { + boolean isCorrect = problemAnswer.trim().equals(memberAnswer.trim()); + + if (currentStatus == CORRECT) { + return isCorrect ? CORRECT : INCORRECT; + } else if (currentStatus == INCORRECT) { + return isCorrect ? RETRY_CORRECT : INCORRECT; + } else if (currentStatus == IN_PROGRESS) { + return isCorrect ? CORRECT : INCORRECT; + } else { + return isCorrect ? RETRY_CORRECT : INCORRECT; + } + } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitCreateRequest.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitCreateRequest.java index daeb5674..9f42241e 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitCreateRequest.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitCreateRequest.java @@ -1,7 +1,11 @@ package com.moplus.moplus_server.client.submit.dto.request; +import jakarta.validation.constraints.NotNull; + public record ChildProblemSubmitCreateRequest( + @NotNull(message = "발행 ID는 필수입니다.") Long publishId, + @NotNull(message = "문항 ID는 필수입니다.") Long problemId ) { } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ProblemSubmitUpdateRequest.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ProblemSubmitUpdateRequest.java new file mode 100644 index 00000000..1b52175b --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ProblemSubmitUpdateRequest.java @@ -0,0 +1,12 @@ +package com.moplus.moplus_server.client.submit.dto.request; + +import jakarta.validation.constraints.NotNull; + +public record ProblemSubmitUpdateRequest( + @NotNull(message = "발행 ID는 필수입니다.") + Long publishId, + @NotNull(message = "문항 ID는 필수입니다.") + Long problemId, + String answer +) { +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java index f545c02b..3079134a 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java @@ -38,7 +38,6 @@ public class ClientGetService { @Transactional(readOnly = true) public CommentaryGetResponse getCommentary(Long publishId, Long problemId) { - Long memberId = 1L; // 문항 제출 조회 @@ -78,7 +77,6 @@ public CommentaryGetResponse getCommentary(Long publishId, Long problemId) { @Transactional(readOnly = true) public List getAllProblem(int year, int month) { - Long memberId = 1L; if (month < 1 || month > 12) { diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java index 57e959aa..3de942ed 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java @@ -7,6 +7,7 @@ import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitCreateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitCreateRequest; +import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitUpdateRequest; import com.moplus.moplus_server.client.submit.repository.ChildProblemSubmitRepository; import com.moplus.moplus_server.client.submit.repository.ProblemSubmitRepository; import com.moplus.moplus_server.domain.problem.domain.childProblem.ChildProblem; @@ -30,12 +31,10 @@ public class ClientSubmitService { @Transactional public void createProblemSubmit(ProblemSubmitCreateRequest request) { - Long memberId = 1L; // 존재하는 발행인지 검증 publishRepository.existsByIdElseThrow(request.publishId()); - // 존재하는 문항인지 검증 problemRepository.existsByIdElseThrow(request.problemId()); @@ -49,13 +48,32 @@ public void createProblemSubmit(ProblemSubmitCreateRequest request) { } @Transactional - public void createChildProblemSubmit(ChildProblemSubmitCreateRequest request) { - + public ProblemSubmitStatus updateProblemSubmit(ProblemSubmitUpdateRequest request) { Long memberId = 1L; // 존재하는 발행인지 검증 publishRepository.existsByIdElseThrow(request.publishId()); + // 문항 조회 + Problem problem = problemRepository.findByIdElseThrow(request.problemId()); + + //문항 제출 데이터 조회 + ProblemSubmit problemSubmit = problemSubmitRepository.findByMemberIdAndPublishIdAndProblemIdElseThrow(memberId, + request.publishId(), request.problemId()); + // 제출한 답안에 대한 상태 결정 + ProblemSubmitStatus status = ProblemSubmitStatus.determineStatus(problemSubmit.getStatus(), request.answer(), + problem.getAnswer()); + + problemSubmit.updateStatus(status); + return status; + } + + @Transactional + public void createChildProblemSubmit(ChildProblemSubmitCreateRequest request) { + Long memberId = 1L; + + // 존재하는 발행인지 검증 + publishRepository.existsByIdElseThrow(request.publishId()); // 존재하는 문항인지 검증 problemRepository.existsByIdElseThrow(request.problemId()); From 2479aa57078885cf4d1cf77edb83b4f8bfe98962 Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 18 Mar 2025 22:13:11 +0900 Subject: [PATCH 14/48] =?UTF-8?q?[feat/#94]=20=EC=83=88=EB=81=BC=EB=AC=B8?= =?UTF-8?q?=ED=95=AD=20=EC=A0=9C=EC=B6=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ClientSubmitController.java | 10 ++++++++ .../submit/domain/ChildProblemSubmit.java | 4 ++++ .../domain/ChildProblemSubmitStatus.java | 15 +++++++++++- .../ChildProblemSubmitUpdateRequest.java | 12 ++++++++++ .../ChildProblemSubmitRepository.java | 6 ++--- .../submit/service/ClientSubmitService.java | 24 +++++++++++++++++++ 6 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitUpdateRequest.java diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java index 012e0d93..d515e533 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java @@ -1,7 +1,9 @@ package com.moplus.moplus_server.client.submit.controller; +import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitCreateRequest; +import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitUpdateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitCreateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitUpdateRequest; import com.moplus.moplus_server.client.submit.service.ClientSubmitService; @@ -48,4 +50,12 @@ public ResponseEntity createProblemSubmit( clientSubmitService.createChildProblemSubmit(request); return ResponseEntity.ok(null); } + + @PutMapping("childProblemSubmit") + @Operation(summary = "새끼문항 제출 업데이트", description = "제출한 답안을 바탕으로 문항 제출의 상태를 업데이트합니다.") + public ResponseEntity updateChildProblemSubmit( + @RequestBody ChildProblemSubmitUpdateRequest request + ) { + return ResponseEntity.ok(clientSubmitService.updateChildProblemSubmit(request)); + } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmit.java b/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmit.java index c7bb92c6..95d9bce2 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmit.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmit.java @@ -37,4 +37,8 @@ public ChildProblemSubmit(Long memberId, Long publishId, Long childProblemId, Ch this.childProblemId = childProblemId; this.status = status; } + + public void updateStatus(ChildProblemSubmitStatus status) { + this.status = status; + } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmitStatus.java b/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmitStatus.java index 18e1ee04..fb154561 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmitStatus.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmitStatus.java @@ -4,5 +4,18 @@ public enum ChildProblemSubmitStatus { CORRECT, INCORRECT, RETRY_CORRECT, - NOT_STARTED + NOT_STARTED; + public static ChildProblemSubmitStatus determineStatus(ChildProblemSubmitStatus currentStatus, String memberAnswer, String childProblemAnswer) { + boolean isCorrect = childProblemAnswer.trim().equals(memberAnswer.trim()); + + if (currentStatus == CORRECT) { + return isCorrect ? CORRECT : INCORRECT; + } else if (currentStatus == INCORRECT) { + return isCorrect ? RETRY_CORRECT : INCORRECT; + } else if (currentStatus == RETRY_CORRECT) { + return isCorrect ? RETRY_CORRECT : INCORRECT; + } else { + return isCorrect ? CORRECT : INCORRECT; + } + } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitUpdateRequest.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitUpdateRequest.java new file mode 100644 index 00000000..23a452da --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitUpdateRequest.java @@ -0,0 +1,12 @@ +package com.moplus.moplus_server.client.submit.dto.request; + +import jakarta.validation.constraints.NotNull; + +public record ChildProblemSubmitUpdateRequest( + @NotNull(message = "발행 ID는 필수입니다.") + Long publishId, + @NotNull(message = "새끼문항 ID는 필수입니다.") + Long childProblemId, + String answer +) { +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/repository/ChildProblemSubmitRepository.java b/src/main/java/com/moplus/moplus_server/client/submit/repository/ChildProblemSubmitRepository.java index 6d33c1d5..40acf5d7 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/repository/ChildProblemSubmitRepository.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/repository/ChildProblemSubmitRepository.java @@ -7,10 +7,10 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface ChildProblemSubmitRepository extends JpaRepository { - Optional findByMemberIdAndPublishIdAndChildProblemId(Long memberId, Long publishId, Long problemId); + Optional findByMemberIdAndPublishIdAndChildProblemId(Long memberId, Long publishId, Long childProblemId); - default ChildProblemSubmit findByMemberIdAndPublishIdAndChildProblemIdElseThrow(Long memberId, Long publishId, Long problemId) { - return findByMemberIdAndPublishIdAndChildProblemId(memberId, publishId, problemId).orElseThrow( + default ChildProblemSubmit findByMemberIdAndPublishIdAndChildProblemIdElseThrow(Long memberId, Long publishId, Long childProblemId) { + return findByMemberIdAndPublishIdAndChildProblemId(memberId, publishId, childProblemId).orElseThrow( () -> new NotFoundException(ErrorCode.CHILD_PROBLEM_SUBMIT_NOT_CONFIRMED)); } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java index 3de942ed..654a5499 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java @@ -6,12 +6,14 @@ import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitCreateRequest; +import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitUpdateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitCreateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitUpdateRequest; import com.moplus.moplus_server.client.submit.repository.ChildProblemSubmitRepository; import com.moplus.moplus_server.client.submit.repository.ProblemSubmitRepository; 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.domain.publish.repository.PublishRepository; import java.util.List; @@ -28,6 +30,7 @@ public class ClientSubmitService { private final ProblemRepository problemRepository; private final ChildProblemSubmitRepository childProblemSubmitRepository; private final PublishRepository publishRepository; + private final ChildProblemRepository childProblemRepository; @Transactional public void createProblemSubmit(ProblemSubmitCreateRequest request) { @@ -110,4 +113,25 @@ public void createChildProblemSubmit(ChildProblemSubmitCreateRequest request) { } } + @Transactional + public ChildProblemSubmitStatus updateChildProblemSubmit(ChildProblemSubmitUpdateRequest request) { + Long memberId = 1L; + + // 존재하는 발행인지 검증 + publishRepository.existsByIdElseThrow(request.publishId()); + + // 새끼문항 조회 + ChildProblem childProblem = childProblemRepository.findByIdElseThrow(request.childProblemId()); + + //새끼문항 제출 데이터 조회 + ChildProblemSubmit childProblemSubmit = childProblemSubmitRepository.findByMemberIdAndPublishIdAndChildProblemIdElseThrow(memberId, + request.publishId(), request.childProblemId()); + // 제출한 답안에 대한 상태 결정 + ChildProblemSubmitStatus status = ChildProblemSubmitStatus.determineStatus(childProblemSubmit.getStatus(), request.answer(), + childProblem.getAnswer()); + + childProblemSubmit.updateStatus(status); + return status; + } + } From 1ae78de25c1915e19adb79be15063b779b243fe1 Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 18 Mar 2025 22:19:50 +0900 Subject: [PATCH 15/48] =?UTF-8?q?[feat/#94]=20=EC=83=88=EB=81=BC=EB=AC=B8?= =?UTF-8?q?=ED=95=AD=20=EC=A0=9C=EC=B6=9C=20=ED=8B=80=EB=A6=BC=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ClientSubmitController.java | 10 ++++++++++ .../submit/domain/ChildProblemSubmit.java | 5 +++++ ...hildProblemSubmitUpdateIncorrectRequest.java | 11 +++++++++++ .../submit/service/ClientSubmitService.java | 17 +++++++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitUpdateIncorrectRequest.java diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java index d515e533..25c495cc 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java @@ -3,6 +3,7 @@ import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitCreateRequest; +import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitUpdateIncorrectRequest; import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitUpdateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitCreateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitUpdateRequest; @@ -58,4 +59,13 @@ public ResponseEntity updateChildProblemSubmit( ) { return ResponseEntity.ok(clientSubmitService.updateChildProblemSubmit(request)); } + + @PutMapping("childProblemSubmit/incorrect") + @Operation(summary = "새끼문항 제출 틀림 업데이트", description = "새끼문항 제출의 상태를 틀림으로 업데이트합니다.") + public ResponseEntity updateChildProblemSubmitIncorrect( + @RequestBody ChildProblemSubmitUpdateIncorrectRequest request + ) { + clientSubmitService.updateChildProblemSubmitIncorrect(request); + return ResponseEntity.ok(null); + } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmit.java b/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmit.java index 95d9bce2..25a869e8 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmit.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmit.java @@ -41,4 +41,9 @@ public ChildProblemSubmit(Long memberId, Long publishId, Long childProblemId, Ch public void updateStatus(ChildProblemSubmitStatus status) { this.status = status; } + + public void updateStatusIncorrect() { + this.status = ChildProblemSubmitStatus.INCORRECT; + + } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitUpdateIncorrectRequest.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitUpdateIncorrectRequest.java new file mode 100644 index 00000000..7ad9628c --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/request/ChildProblemSubmitUpdateIncorrectRequest.java @@ -0,0 +1,11 @@ +package com.moplus.moplus_server.client.submit.dto.request; + +import jakarta.validation.constraints.NotNull; + +public record ChildProblemSubmitUpdateIncorrectRequest( + @NotNull(message = "발행 ID는 필수입니다.") + Long publishId, + @NotNull(message = "새끼문항 ID는 필수입니다.") + Long childProblemId +) { +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java index 654a5499..f6ebd580 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java @@ -6,6 +6,7 @@ import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitCreateRequest; +import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitUpdateIncorrectRequest; import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitUpdateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitCreateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitUpdateRequest; @@ -134,4 +135,20 @@ public ChildProblemSubmitStatus updateChildProblemSubmit(ChildProblemSubmitUpdat return status; } + @Transactional + public void updateChildProblemSubmitIncorrect(ChildProblemSubmitUpdateIncorrectRequest request) { + Long memberId = 1L; + + // 존재하는 발행인지 검증 + publishRepository.existsByIdElseThrow(request.publishId()); + + // 새끼문항 조회 + ChildProblem childProblem = childProblemRepository.findByIdElseThrow(request.childProblemId()); + + //새끼문항 제출 데이터 조회 + ChildProblemSubmit childProblemSubmit = childProblemSubmitRepository.findByMemberIdAndPublishIdAndChildProblemIdElseThrow(memberId, + request.publishId(), request.childProblemId()); + // 틀림 처리 + childProblemSubmit.updateStatusIncorrect(); + } } From f11997345a2d4f50c2e3be76916b5b8bde30b5a8 Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 18 Mar 2025 23:02:17 +0900 Subject: [PATCH 16/48] =?UTF-8?q?[feat/#94]=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=20=EB=AC=B8=ED=95=AD=20=EC=A1=B0=ED=9A=8C=20api=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ClientGetController.java | 10 +++++++ .../response/ProblemClientGetResponse.java | 28 +++++++++++++++++++ .../ChildProblemSubmitRepository.java | 4 +++ .../submit/service/ClientGetService.java | 27 ++++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemClientGetResponse.java diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java index d959000f..ca579baa 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java @@ -2,6 +2,7 @@ import com.moplus.moplus_server.client.submit.dto.response.AllProblemGetResponse; import com.moplus.moplus_server.client.submit.dto.response.CommentaryGetResponse; +import com.moplus.moplus_server.client.submit.dto.response.ProblemClientGetResponse; import com.moplus.moplus_server.client.submit.service.ClientGetService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -39,4 +40,13 @@ public ResponseEntity> getAllProblem( ) { return ResponseEntity.ok(clientGetService.getAllProblem(year, month)); } + + @GetMapping("problem/{publishId}/{problemId}") + @Operation(summary = "문항 조회", description = "사용자에게 보여지는 문항을 조회합니다.") + public ResponseEntity getProblem( + @PathVariable Long publishId, + @PathVariable Long problemId + ) { + return ResponseEntity.ok(clientGetService.getProblem(publishId, problemId)); + } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemClientGetResponse.java new file mode 100644 index 00000000..40d73e5f --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemClientGetResponse.java @@ -0,0 +1,28 @@ +package com.moplus.moplus_server.client.submit.dto.response; + +import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; +import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; +import com.moplus.moplus_server.domain.problem.domain.problem.Problem; +import java.util.List; +import lombok.Builder; + +@Builder +public record ProblemClientGetResponse( + int number, + String imageUrl, + Integer recommendedMinute, + Integer recommendedSecond, + ProblemSubmitStatus status, + List childProblemStatuses +) { + public static ProblemClientGetResponse of(Problem problem, ProblemSubmitStatus status, List childProblemStatuses) { + return ProblemClientGetResponse.builder() + .number(problem.getNumber()) + .imageUrl(problem.getMainProblemImageUrl()) + .status(status) + .childProblemStatuses(childProblemStatuses) + .recommendedMinute(problem.getRecommendedTime().getMinute()) + .recommendedSecond(problem.getRecommendedTime().getSecond()) + .build(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/repository/ChildProblemSubmitRepository.java b/src/main/java/com/moplus/moplus_server/client/submit/repository/ChildProblemSubmitRepository.java index 40acf5d7..33a5be8a 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/repository/ChildProblemSubmitRepository.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/repository/ChildProblemSubmitRepository.java @@ -3,6 +3,7 @@ import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmit; import com.moplus.moplus_server.global.error.exception.ErrorCode; import com.moplus.moplus_server.global.error.exception.NotFoundException; +import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; @@ -13,4 +14,7 @@ default ChildProblemSubmit findByMemberIdAndPublishIdAndChildProblemIdElseThrow( return findByMemberIdAndPublishIdAndChildProblemId(memberId, publishId, childProblemId).orElseThrow( () -> new NotFoundException(ErrorCode.CHILD_PROBLEM_SUBMIT_NOT_CONFIRMED)); } + + List findAllByMemberIdAndPublishIdAndChildProblemIdIn(Long memberId, Long publishId, + List childProblemIds); } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java index 3079134a..d22c3f67 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java @@ -2,6 +2,8 @@ import com.moplus.moplus_server.admin.publish.domain.Publish; +import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmit; +import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; import com.moplus.moplus_server.client.submit.dto.response.AllProblemGetResponse; @@ -9,9 +11,11 @@ import com.moplus.moplus_server.client.submit.dto.response.CommentaryGetResponse; import com.moplus.moplus_server.client.submit.dto.response.DayProgress; import com.moplus.moplus_server.client.submit.dto.response.PrescriptionResponse; +import com.moplus.moplus_server.client.submit.dto.response.ProblemClientGetResponse; import com.moplus.moplus_server.client.submit.dto.response.ProblemDetailResponse; import com.moplus.moplus_server.client.submit.repository.ChildProblemSubmitRepository; import com.moplus.moplus_server.client.submit.repository.ProblemSubmitRepository; +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.ProblemRepository; import com.moplus.moplus_server.domain.problemset.repository.ProblemSetRepository; @@ -116,4 +120,27 @@ private String getMainProblemImageUrl(Long problemSetId) { .map(Problem::getMainProblemImageUrl) .orElse(null); } + + @Transactional(readOnly = true) + public ProblemClientGetResponse getProblem(Long publishId, Long problemId) { + Long memberId = 1L; + + Problem problem = problemRepository.findByIdElseThrow(problemId); + + // 문항 제출 조회 + ProblemSubmit problemSubmit = problemSubmitRepository.findByMemberIdAndPublishIdAndProblemIdElseThrow(memberId, + publishId, problemId); + + // 새끼 문항 제출 상태 조회 + List childProblemIds = problem.getChildProblems().stream() + .map(ChildProblem::getId) + .toList(); + + List childProblemStatuses = childProblemSubmitRepository.findAllByMemberIdAndPublishIdAndChildProblemIdIn( + memberId, publishId, childProblemIds).stream() + .map(ChildProblemSubmit::getStatus) + .toList(); + + return ProblemClientGetResponse.of(problem, problemSubmit.getStatus(), childProblemStatuses); + } } From f39d543ae2276296622b26f107ba44032a5f0abb Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 18 Mar 2025 23:22:22 +0900 Subject: [PATCH 17/48] =?UTF-8?q?[feat/#94]=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=20=EC=83=88=EB=81=BC=EB=AC=B8=ED=95=AD=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ClientGetController.java | 11 ++++++++ .../ChildProblemClientGetResponse.java | 23 ++++++++++++++++ .../submit/service/ClientGetService.java | 27 +++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemClientGetResponse.java diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java index ca579baa..d741a791 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java @@ -1,6 +1,7 @@ package com.moplus.moplus_server.client.submit.controller; import com.moplus.moplus_server.client.submit.dto.response.AllProblemGetResponse; +import com.moplus.moplus_server.client.submit.dto.response.ChildProblemClientGetResponse; import com.moplus.moplus_server.client.submit.dto.response.CommentaryGetResponse; import com.moplus.moplus_server.client.submit.dto.response.ProblemClientGetResponse; import com.moplus.moplus_server.client.submit.service.ClientGetService; @@ -49,4 +50,14 @@ public ResponseEntity getProblem( ) { return ResponseEntity.ok(clientGetService.getProblem(publishId, problemId)); } + + @GetMapping("problem/{publishId}/{problemId}/{childProblemId}") + @Operation(summary = "새끼문항 조회", description = "사용자에게 보여지는 새끼문항을 조회합니다.") + public ResponseEntity getChildProblem( + @PathVariable Long publishId, + @PathVariable Long problemId, + @PathVariable Long childProblemId + ) { + return ResponseEntity.ok(clientGetService.getChildProblem(publishId, problemId, childProblemId)); + } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemClientGetResponse.java new file mode 100644 index 00000000..f299d37b --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemClientGetResponse.java @@ -0,0 +1,23 @@ +package com.moplus.moplus_server.client.submit.dto.response; + +import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; +import lombok.Builder; + +@Builder +public record ChildProblemClientGetResponse( + int problemNumber, + int childProblemNumber, + String imageUrl, + ChildProblemSubmitStatus status +) { + public static ChildProblemClientGetResponse of(int problemNumber, int childProblemNumber, String imageUrl, + ChildProblemSubmitStatus status + ) { + return ChildProblemClientGetResponse.builder() + .problemNumber(problemNumber) + .childProblemNumber(childProblemNumber) + .imageUrl(imageUrl) + .status(status) + .build(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java index d22c3f67..3b34e837 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java @@ -7,6 +7,7 @@ import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; import com.moplus.moplus_server.client.submit.dto.response.AllProblemGetResponse; +import com.moplus.moplus_server.client.submit.dto.response.ChildProblemClientGetResponse; import com.moplus.moplus_server.client.submit.dto.response.ChildProblemDetailResponse; import com.moplus.moplus_server.client.submit.dto.response.CommentaryGetResponse; import com.moplus.moplus_server.client.submit.dto.response.DayProgress; @@ -17,6 +18,7 @@ import com.moplus.moplus_server.client.submit.repository.ProblemSubmitRepository; 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.domain.problemset.repository.ProblemSetRepository; import com.moplus.moplus_server.domain.publish.repository.PublishRepository; @@ -38,6 +40,7 @@ public class ClientGetService { private final ProblemRepository problemRepository; private final ProblemSetRepository problemSetRepository; private final ChildProblemSubmitRepository childProblemSubmitRepository; + private final ChildProblemRepository childProblemRepository; @Transactional(readOnly = true) @@ -125,6 +128,7 @@ private String getMainProblemImageUrl(Long problemSetId) { public ProblemClientGetResponse getProblem(Long publishId, Long problemId) { Long memberId = 1L; + // 문항조회 Problem problem = problemRepository.findByIdElseThrow(problemId); // 문항 제출 조회 @@ -143,4 +147,27 @@ public ProblemClientGetResponse getProblem(Long publishId, Long problemId) { return ProblemClientGetResponse.of(problem, problemSubmit.getStatus(), childProblemStatuses); } + + @Transactional(readOnly = true) + public ChildProblemClientGetResponse getChildProblem(Long publishId, Long problemId, Long childProblemId) { + Long memberId = 1L; + + // 문항/새끼문항 조회 + Problem problem = problemRepository.findByIdElseThrow(problemId); + ChildProblem childProblem = childProblemRepository.findByIdElseThrow(childProblemId); + + // 새끼문항 제출 조회 + ChildProblemSubmit childProblemSubmit = childProblemSubmitRepository.findByMemberIdAndPublishIdAndChildProblemIdElseThrow( + memberId, publishId, childProblemId); + + int sequence = 0; + for (int i = 0; i < problem.getChildProblems().size(); i++) { + if (problem.getChildProblems().get(i).getId().equals(childProblemId)) { + sequence = i + 1; + break; + } + } + + return ChildProblemClientGetResponse.of(problem.getNumber(), sequence, childProblem.getImageUrl(), childProblemSubmit.getStatus()); + } } From a81ec80debb66d4f1cd3a7c050597eb6addc0b89 Mon Sep 17 00:00:00 2001 From: HongGit Date: Sat, 22 Mar 2025 17:34:42 +0900 Subject: [PATCH 18/48] =?UTF-8?q?[fix/#94]=20=EC=98=A4=EB=8A=98=20?= =?UTF-8?q?=EC=9D=B4=ED=9B=84=20=EB=B0=9C=ED=96=89=EC=9D=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=95=88=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../submit/service/ClientGetService.java | 26 ++++++++++++++++--- .../global/error/exception/ErrorCode.java | 1 + 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java index 3b34e837..446bdbeb 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java @@ -42,11 +42,14 @@ public class ClientGetService { private final ChildProblemSubmitRepository childProblemSubmitRepository; private final ChildProblemRepository childProblemRepository; - @Transactional(readOnly = true) public CommentaryGetResponse getCommentary(Long publishId, Long problemId) { Long memberId = 1L; + // 발행 조회 + Publish publish = publishRepository.findByIdElseThrow(publishId); + denyAccessToFuturePublish(publish); + // 문항 제출 조회 ProblemSubmit problemSubmit = problemSubmitRepository.findByMemberIdAndPublishIdAndProblemIdElseThrow(memberId, publishId, problemId); @@ -93,7 +96,10 @@ public List getAllProblem(int year, int month) { LocalDate endDate = startDate.withDayOfMonth(startDate.lengthOfMonth()); // 해당 월 모든 Publish 조회 - List publishes = publishRepository.findByPublishedDateBetween(startDate, endDate); + // 오늘 이후 발행이 있다면 필터링 + List publishes = publishRepository.findByPublishedDateBetween(startDate, endDate).stream() + .filter(publish -> !publish.getPublishedDate().isAfter(LocalDate.now())) + .toList(); List result = new ArrayList<>(); @@ -104,7 +110,7 @@ public List getAllProblem(int year, int month) { // 날짜별 사용자 제출 정보 조회 List submissions = problemSubmitRepository.findByMemberIdAndPublishId(memberId, publishId); List problemStatuses = submissions.stream() - .map(ProblemSubmit::getStatus) + .map(ProblemSubmit::getStatus ) .toList(); // 사용자 제출 정보 바탕으로 진행도 결정 @@ -128,6 +134,10 @@ private String getMainProblemImageUrl(Long problemSetId) { public ProblemClientGetResponse getProblem(Long publishId, Long problemId) { Long memberId = 1L; + // 발행 조회 + Publish publish = publishRepository.findByIdElseThrow(publishId); + denyAccessToFuturePublish(publish); + // 문항조회 Problem problem = problemRepository.findByIdElseThrow(problemId); @@ -152,6 +162,10 @@ public ProblemClientGetResponse getProblem(Long publishId, Long problemId) { public ChildProblemClientGetResponse getChildProblem(Long publishId, Long problemId, Long childProblemId) { Long memberId = 1L; + // 발행 조회 + Publish publish = publishRepository.findByIdElseThrow(publishId); + denyAccessToFuturePublish(publish); + // 문항/새끼문항 조회 Problem problem = problemRepository.findByIdElseThrow(problemId); ChildProblem childProblem = childProblemRepository.findByIdElseThrow(childProblemId); @@ -170,4 +184,10 @@ public ChildProblemClientGetResponse getChildProblem(Long publishId, Long proble return ChildProblemClientGetResponse.of(problem.getNumber(), sequence, childProblem.getImageUrl(), childProblemSubmit.getStatus()); } + + private void denyAccessToFuturePublish(Publish publish){ + if (publish.getPublishedDate().isAfter(LocalDate.now())) { + throw new InvalidValueException(ErrorCode.FUTURE_PUBLISH_NOT_ACCESSIBLE); + } + } } 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 193b4f69..eadf8884 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 @@ -76,6 +76,7 @@ public enum ErrorCode { ALREADY_PUBLISHED_ERROR(HttpStatus.BAD_REQUEST, "이미 발행된 문항세트는 컨펌해제할 수 없습니다."), PROBLEM_SET_DELETED(HttpStatus.BAD_REQUEST, "삭제된 문항세트는 발행할 수 없습니다"), PROBLEM_SET_NOT_CONFIRMED(HttpStatus.BAD_REQUEST, "컨펌되지 않은 문항세트는 발행할 수 없습니다"), + FUTURE_PUBLISH_NOT_ACCESSIBLE(HttpStatus.BAD_REQUEST, "오늘 이후의 발행을 조회할 수 없습니다."), // 문항 제출 PROBLEM_SUBMIT_NOT_CONFIRMED(HttpStatus.NOT_FOUND, "문항 제출 정보를 찾을 수 없습니다."), From 67a9f0ad8fecbe8cb0ef84f4e40f2b74fab03257 Mon Sep 17 00:00:00 2001 From: HongGit Date: Sat, 22 Mar 2025 17:48:11 +0900 Subject: [PATCH 19/48] =?UTF-8?q?[fix/#94]=20=EB=AC=B8=ED=95=AD=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20sequence=20=EA=B8=B0=EC=A4=80=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/ProblemClientGetResponse.java | 4 ++-- .../submit/service/ClientGetService.java | 23 +++++++++++++++++-- .../global/error/exception/ErrorCode.java | 1 + 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemClientGetResponse.java index 40d73e5f..5ab0f5b5 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemClientGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemClientGetResponse.java @@ -15,9 +15,9 @@ public record ProblemClientGetResponse( ProblemSubmitStatus status, List childProblemStatuses ) { - public static ProblemClientGetResponse of(Problem problem, ProblemSubmitStatus status, List childProblemStatuses) { + public static ProblemClientGetResponse of(Problem problem, ProblemSubmitStatus status, List childProblemStatuses, int number) { return ProblemClientGetResponse.builder() - .number(problem.getNumber()) + .number(number) .imageUrl(problem.getMainProblemImageUrl()) .status(status) .childProblemStatuses(childProblemStatuses) diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java index 446bdbeb..472455dd 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java @@ -20,13 +20,16 @@ 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.domain.problemset.domain.ProblemSet; import com.moplus.moplus_server.domain.problemset.repository.ProblemSetRepository; import com.moplus.moplus_server.domain.publish.repository.PublishRepository; import com.moplus.moplus_server.global.error.exception.ErrorCode; import com.moplus.moplus_server.global.error.exception.InvalidValueException; +import com.moplus.moplus_server.global.error.exception.NotFoundException; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; +import javax.swing.undo.CannotUndoException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -74,8 +77,16 @@ public CommentaryGetResponse getCommentary(Long publishId, Long problemId) { // 처방 정보 생성 PrescriptionResponse prescription = PrescriptionResponse.of(childProblem, mainProblem); + // 문항 번호 추출 + ProblemSet problemSet = problemSetRepository.findByIdElseThrow(publish.getProblemSetId()); + List problemIds = problemSet.getProblemIds(); + int number = problemIds.indexOf(problemId); + if (number == -1) { + throw new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND_IN_PROBLEM_SET); + } + return CommentaryGetResponse.of( - problem.getNumber(), + number + 1, problem.getAnswer(), problem.getMainAnalysisImageUrl(), problem.getMainHandwritingExplanationImageUrl(), @@ -138,6 +149,14 @@ public ProblemClientGetResponse getProblem(Long publishId, Long problemId) { Publish publish = publishRepository.findByIdElseThrow(publishId); denyAccessToFuturePublish(publish); + // 문항 번호 추출 + ProblemSet problemSet = problemSetRepository.findByIdElseThrow(publish.getProblemSetId()); + List problemIds = problemSet.getProblemIds(); + int number = problemIds.indexOf(problemId); + if (number == -1) { + throw new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND_IN_PROBLEM_SET); + } + // 문항조회 Problem problem = problemRepository.findByIdElseThrow(problemId); @@ -155,7 +174,7 @@ public ProblemClientGetResponse getProblem(Long publishId, Long problemId) { .map(ChildProblemSubmit::getStatus) .toList(); - return ProblemClientGetResponse.of(problem, problemSubmit.getStatus(), childProblemStatuses); + return ProblemClientGetResponse.of(problem, problemSubmit.getStatus(), childProblemStatuses, number + 1); } @Transactional(readOnly = 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 eadf8884..9f7f67a8 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 @@ -34,6 +34,7 @@ public enum ErrorCode { INVALID_SHORT_NUMBER_ANSWER(HttpStatus.BAD_REQUEST, "주관식 문항의 정답은 0~999 사이의 숫자여야 합니다"), INVALID_CONFIRM_PROBLEM(HttpStatus.BAD_REQUEST, "유효하지 않은 문항들 : "), INVALID_DIFFICULTY(HttpStatus.BAD_REQUEST, "난이도는 1~10 사이의 숫자여야 합니다"), + PROBLEM_NOT_FOUND_IN_PROBLEM_SET(HttpStatus.NOT_FOUND, "해당 날짜에 발행된 문항세트에 존재하는 문항이 아닙니다."), //새끼 문항 CHILD_PROBLEM_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 새끼 문제를 찾을 수 없습니다"), From 68aac4dfaf2ef47999036a6e639ea7fb1c08bb65 Mon Sep 17 00:00:00 2001 From: HongGit Date: Sat, 22 Mar 2025 18:35:34 +0900 Subject: [PATCH 20/48] =?UTF-8?q?[fix/#94]=20=ED=81=B4=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EC=96=B8=ED=8A=B8=20=ED=8C=A8=ED=82=A4=EC=A7=95=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CommentaryGetController.java | 30 ++++++ ...troller.java => ProblemGetController.java} | 27 ++---- .../domain/ChildProblemSubmitStatus.java | 15 ++- .../submit/domain/ProblemSubmitStatus.java | 15 ++- .../submit/service/CommentaryGetService.java | 91 +++++++++++++++++++ ...etService.java => ProblemsGetService.java} | 58 +----------- 6 files changed, 142 insertions(+), 94 deletions(-) create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/controller/CommentaryGetController.java rename src/main/java/com/moplus/moplus_server/client/submit/controller/{ClientGetController.java => ProblemGetController.java} (61%) create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java rename src/main/java/com/moplus/moplus_server/client/submit/service/{ClientGetService.java => ProblemsGetService.java} (73%) diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/CommentaryGetController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/CommentaryGetController.java new file mode 100644 index 00000000..b46ad78a --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/controller/CommentaryGetController.java @@ -0,0 +1,30 @@ +package com.moplus.moplus_server.client.submit.controller; + +import com.moplus.moplus_server.client.submit.dto.response.CommentaryGetResponse; +import com.moplus.moplus_server.client.submit.service.CommentaryGetService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +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; + +@Tag(name = "클라이언트 해설 조회", description = "클라이언트 해설 조회 관련 API") +@RestController +@RequestMapping("/api/v1/client") +@RequiredArgsConstructor +public class CommentaryGetController { + + private final CommentaryGetService commentaryGetService; + + @GetMapping("commentary") + @Operation(summary = "해설 조회", description = "문항 별 해설/처방을 조회합니다.") + public ResponseEntity getCommentary( + @RequestParam(value = "publishId", required = false) Long publishId, + @RequestParam(value = "problemId", required = false) Long problemId + ) { + return ResponseEntity.ok(commentaryGetService.getCommentary(publishId, problemId)); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/ProblemGetController.java similarity index 61% rename from src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java rename to src/main/java/com/moplus/moplus_server/client/submit/controller/ProblemGetController.java index d741a791..0f2ee303 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientGetController.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/controller/ProblemGetController.java @@ -2,9 +2,8 @@ import com.moplus.moplus_server.client.submit.dto.response.AllProblemGetResponse; import com.moplus.moplus_server.client.submit.dto.response.ChildProblemClientGetResponse; -import com.moplus.moplus_server.client.submit.dto.response.CommentaryGetResponse; import com.moplus.moplus_server.client.submit.dto.response.ProblemClientGetResponse; -import com.moplus.moplus_server.client.submit.service.ClientGetService; +import com.moplus.moplus_server.client.submit.service.ProblemsGetService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; @@ -13,33 +12,23 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -@Tag(name = "클라이언트 조회", description = "클라이언트 조회 관련 API") +@Tag(name = "클라이언트 문제 조회", description = "클라이언트 문제 조회 관련 API") @RestController @RequestMapping("/api/v1/client") @RequiredArgsConstructor -public class ClientGetController { +public class ProblemGetController { - private final ClientGetService clientGetService; + private final ProblemsGetService problemsGetService; - @GetMapping("commentary") - @Operation(summary = "해설 조회", description = "문항 별 해설/처방을 조회합니다.") - public ResponseEntity getCommentary( - @RequestParam(value = "publishId", required = false) Long publishId, - @RequestParam(value = "problemId", required = false) Long problemId - ) { - return ResponseEntity.ok(clientGetService.getCommentary(publishId, problemId)); - } - - @GetMapping("allProblem/{year}/{month}") + @GetMapping("problem/all/{year}/{month}") @Operation(summary = "전체 문제 조회", description = "월별 문제들에 대한 진행도와 정보들을 조회합니다.") public ResponseEntity> getAllProblem( @PathVariable int year, @PathVariable int month ) { - return ResponseEntity.ok(clientGetService.getAllProblem(year, month)); + return ResponseEntity.ok(problemsGetService.getAllProblem(year, month)); } @GetMapping("problem/{publishId}/{problemId}") @@ -48,7 +37,7 @@ public ResponseEntity getProblem( @PathVariable Long publishId, @PathVariable Long problemId ) { - return ResponseEntity.ok(clientGetService.getProblem(publishId, problemId)); + return ResponseEntity.ok(problemsGetService.getProblem(publishId, problemId)); } @GetMapping("problem/{publishId}/{problemId}/{childProblemId}") @@ -58,6 +47,6 @@ public ResponseEntity getChildProblem( @PathVariable Long problemId, @PathVariable Long childProblemId ) { - return ResponseEntity.ok(clientGetService.getChildProblem(publishId, problemId, childProblemId)); + return ResponseEntity.ok(problemsGetService.getChildProblem(publishId, problemId, childProblemId)); } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmitStatus.java b/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmitStatus.java index fb154561..c869d59b 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmitStatus.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/domain/ChildProblemSubmitStatus.java @@ -8,14 +8,11 @@ public enum ChildProblemSubmitStatus { public static ChildProblemSubmitStatus determineStatus(ChildProblemSubmitStatus currentStatus, String memberAnswer, String childProblemAnswer) { boolean isCorrect = childProblemAnswer.trim().equals(memberAnswer.trim()); - if (currentStatus == CORRECT) { - return isCorrect ? CORRECT : INCORRECT; - } else if (currentStatus == INCORRECT) { - return isCorrect ? RETRY_CORRECT : INCORRECT; - } else if (currentStatus == RETRY_CORRECT) { - return isCorrect ? RETRY_CORRECT : INCORRECT; - } else { - return isCorrect ? CORRECT : INCORRECT; - } + return switch (currentStatus) { + case CORRECT -> isCorrect ? CORRECT : INCORRECT; + case INCORRECT -> isCorrect ? RETRY_CORRECT : INCORRECT; + case RETRY_CORRECT -> isCorrect ? RETRY_CORRECT : INCORRECT; + default -> isCorrect ? CORRECT : INCORRECT; + }; } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmitStatus.java b/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmitStatus.java index be01991f..34bb4d45 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmitStatus.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmitStatus.java @@ -8,14 +8,11 @@ public enum ProblemSubmitStatus { public static ProblemSubmitStatus determineStatus(ProblemSubmitStatus currentStatus, String memberAnswer, String problemAnswer) { boolean isCorrect = problemAnswer.trim().equals(memberAnswer.trim()); - if (currentStatus == CORRECT) { - return isCorrect ? CORRECT : INCORRECT; - } else if (currentStatus == INCORRECT) { - return isCorrect ? RETRY_CORRECT : INCORRECT; - } else if (currentStatus == IN_PROGRESS) { - return isCorrect ? CORRECT : INCORRECT; - } else { - return isCorrect ? RETRY_CORRECT : INCORRECT; - } + return switch (currentStatus) { + case CORRECT -> isCorrect ? CORRECT : INCORRECT; + case INCORRECT -> isCorrect ? RETRY_CORRECT : INCORRECT; + case IN_PROGRESS -> isCorrect ? CORRECT : INCORRECT; + default -> isCorrect ? RETRY_CORRECT : INCORRECT; + }; } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java new file mode 100644 index 00000000..11cdd254 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java @@ -0,0 +1,91 @@ +package com.moplus.moplus_server.client.submit.service; + +import com.moplus.moplus_server.admin.publish.domain.Publish; +import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; +import com.moplus.moplus_server.client.submit.dto.response.ChildProblemDetailResponse; +import com.moplus.moplus_server.client.submit.dto.response.CommentaryGetResponse; +import com.moplus.moplus_server.client.submit.dto.response.PrescriptionResponse; +import com.moplus.moplus_server.client.submit.dto.response.ProblemDetailResponse; +import com.moplus.moplus_server.client.submit.repository.ChildProblemSubmitRepository; +import com.moplus.moplus_server.client.submit.repository.ProblemSubmitRepository; +import com.moplus.moplus_server.domain.problem.domain.problem.Problem; +import com.moplus.moplus_server.domain.problem.repository.ProblemRepository; +import com.moplus.moplus_server.domain.problemset.domain.ProblemSet; +import com.moplus.moplus_server.domain.problemset.repository.ProblemSetRepository; +import com.moplus.moplus_server.domain.publish.repository.PublishRepository; +import com.moplus.moplus_server.global.error.exception.ErrorCode; +import com.moplus.moplus_server.global.error.exception.InvalidValueException; +import com.moplus.moplus_server.global.error.exception.NotFoundException; +import java.time.LocalDate; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class CommentaryGetService { + + private final PublishRepository publishRepository; + private final ProblemSubmitRepository problemSubmitRepository; + private final ProblemRepository problemRepository; + private final ProblemSetRepository problemSetRepository; + private final ChildProblemSubmitRepository childProblemSubmitRepository; + + @Transactional(readOnly = true) + public CommentaryGetResponse getCommentary(Long publishId, Long problemId) { + Long memberId = 1L; + + // 발행 조회 + Publish publish = publishRepository.findByIdElseThrow(publishId); + denyAccessToFuturePublish(publish); + + // 문항 제출 조회 + ProblemSubmit problemSubmit = problemSubmitRepository.findByMemberIdAndPublishIdAndProblemIdElseThrow(memberId, + publishId, problemId); + + // 문항 해설 생성 + Problem problem = problemRepository.findByIdElseThrow(problemId); + ProblemDetailResponse mainProblem = ProblemDetailResponse.of( + problem.getMainProblemImageUrl(), + problem.getPrescriptionImageUrls(), + problemSubmit.getStatus() + ); + + // 새끼문항 해설 생성 + List childProblem = problem.getChildProblems().stream() + .map(cp -> ChildProblemDetailResponse.of( + cp.getImageUrl(), + cp.getPrescriptionImageUrls(), + childProblemSubmitRepository.findByMemberIdAndPublishIdAndChildProblemIdElseThrow(memberId, publishId, + cp.getId()).getStatus() + )).toList(); + + // 처방 정보 생성 + PrescriptionResponse prescription = PrescriptionResponse.of(childProblem, mainProblem); + + // 문항 번호 추출 + ProblemSet problemSet = problemSetRepository.findByIdElseThrow(publish.getProblemSetId()); + List problemIds = problemSet.getProblemIds(); + int number = problemIds.indexOf(problemId); + if (number == -1) { + throw new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND_IN_PROBLEM_SET); + } + + return CommentaryGetResponse.of( + number + 1, + problem.getAnswer(), + problem.getMainAnalysisImageUrl(), + problem.getMainHandwritingExplanationImageUrl(), + problem.getReadingTipImageUrl(), + problem.getSeniorTipImageUrl(), + prescription + ); + } + + private void denyAccessToFuturePublish(Publish publish){ + if (publish.getPublishedDate().isAfter(LocalDate.now())) { + throw new InvalidValueException(ErrorCode.FUTURE_PUBLISH_NOT_ACCESSIBLE); + } + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemsGetService.java similarity index 73% rename from src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java rename to src/main/java/com/moplus/moplus_server/client/submit/service/ProblemsGetService.java index 472455dd..49d1af12 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemsGetService.java @@ -8,12 +8,8 @@ import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; import com.moplus.moplus_server.client.submit.dto.response.AllProblemGetResponse; import com.moplus.moplus_server.client.submit.dto.response.ChildProblemClientGetResponse; -import com.moplus.moplus_server.client.submit.dto.response.ChildProblemDetailResponse; -import com.moplus.moplus_server.client.submit.dto.response.CommentaryGetResponse; import com.moplus.moplus_server.client.submit.dto.response.DayProgress; -import com.moplus.moplus_server.client.submit.dto.response.PrescriptionResponse; import com.moplus.moplus_server.client.submit.dto.response.ProblemClientGetResponse; -import com.moplus.moplus_server.client.submit.dto.response.ProblemDetailResponse; import com.moplus.moplus_server.client.submit.repository.ChildProblemSubmitRepository; import com.moplus.moplus_server.client.submit.repository.ProblemSubmitRepository; import com.moplus.moplus_server.domain.problem.domain.childProblem.ChildProblem; @@ -29,14 +25,13 @@ import java.time.LocalDate; import java.util.ArrayList; import java.util.List; -import javax.swing.undo.CannotUndoException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor -public class ClientGetService { +public class ProblemsGetService { private final PublishRepository publishRepository; private final ProblemSubmitRepository problemSubmitRepository; @@ -45,57 +40,6 @@ public class ClientGetService { private final ChildProblemSubmitRepository childProblemSubmitRepository; private final ChildProblemRepository childProblemRepository; - @Transactional(readOnly = true) - public CommentaryGetResponse getCommentary(Long publishId, Long problemId) { - Long memberId = 1L; - - // 발행 조회 - Publish publish = publishRepository.findByIdElseThrow(publishId); - denyAccessToFuturePublish(publish); - - // 문항 제출 조회 - ProblemSubmit problemSubmit = problemSubmitRepository.findByMemberIdAndPublishIdAndProblemIdElseThrow(memberId, - publishId, problemId); - - // 문항 해설 생성 - Problem problem = problemRepository.findByIdElseThrow(problemId); - ProblemDetailResponse mainProblem = ProblemDetailResponse.of( - problem.getMainProblemImageUrl(), - problem.getPrescriptionImageUrls(), - problemSubmit.getStatus() - ); - - // 새끼문항 해설 생성 - List childProblem = problem.getChildProblems().stream() - .map(cp -> ChildProblemDetailResponse.of( - cp.getImageUrl(), - cp.getPrescriptionImageUrls(), - childProblemSubmitRepository.findByMemberIdAndPublishIdAndChildProblemIdElseThrow(memberId, publishId, - cp.getId()).getStatus() - )).toList(); - - // 처방 정보 생성 - PrescriptionResponse prescription = PrescriptionResponse.of(childProblem, mainProblem); - - // 문항 번호 추출 - ProblemSet problemSet = problemSetRepository.findByIdElseThrow(publish.getProblemSetId()); - List problemIds = problemSet.getProblemIds(); - int number = problemIds.indexOf(problemId); - if (number == -1) { - throw new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND_IN_PROBLEM_SET); - } - - return CommentaryGetResponse.of( - number + 1, - problem.getAnswer(), - problem.getMainAnalysisImageUrl(), - problem.getMainHandwritingExplanationImageUrl(), - problem.getReadingTipImageUrl(), - problem.getSeniorTipImageUrl(), - prescription - ); - } - @Transactional(readOnly = true) public List getAllProblem(int year, int month) { Long memberId = 1L; From f612ecbe87c5ad92aa17dd4217899b1387b53f88 Mon Sep 17 00:00:00 2001 From: HongGit Date: Sun, 23 Mar 2025 20:40:48 +0900 Subject: [PATCH 21/48] =?UTF-8?q?[fix/#94]=20Dto=20mapper=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/CommentaryGetResponse.java | 15 +++++++-------- .../dto/response/ProblemDetailResponse.java | 7 ++++--- .../submit/service/CommentaryGetService.java | 9 ++------- .../client/submit/service/ProblemsGetService.java | 14 +++++++++++--- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/CommentaryGetResponse.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/CommentaryGetResponse.java index 5c0ba7c7..e1b7c38a 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/CommentaryGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/CommentaryGetResponse.java @@ -1,5 +1,6 @@ package com.moplus.moplus_server.client.submit.dto.response; +import com.moplus.moplus_server.domain.problem.domain.problem.Problem; import lombok.Builder; @Builder @@ -12,16 +13,14 @@ public record CommentaryGetResponse( String seniorTipImageUrl, PrescriptionResponse prescription ) { - public static CommentaryGetResponse of(int problemNumber, String answer, String mainAnalysisImageUrl, - String mainHandwritingExplanationImageUrl, String readingTipImageUrl, - String seniorTipImageUrl, PrescriptionResponse prescription) { + public static CommentaryGetResponse of(int problemNumber, Problem problem, PrescriptionResponse prescription) { return CommentaryGetResponse.builder() .problemNumber(problemNumber) - .answer(answer) - .mainAnalysisImageUrl(mainAnalysisImageUrl) - .mainHandwritingExplanationImageUrl(mainHandwritingExplanationImageUrl) - .readingTipImageUrl(readingTipImageUrl) - .seniorTipImageUrl(seniorTipImageUrl) + .answer(problem.getAnswer()) + .mainAnalysisImageUrl(problem.getMainAnalysisImageUrl()) + .mainHandwritingExplanationImageUrl(problem.getMainHandwritingExplanationImageUrl()) + .readingTipImageUrl(problem.getReadingTipImageUrl()) + .seniorTipImageUrl(problem.getSeniorTipImageUrl()) .prescription(prescription) .build(); } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemDetailResponse.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemDetailResponse.java index aa31cf09..2b6f0478 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemDetailResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemDetailResponse.java @@ -1,6 +1,7 @@ package com.moplus.moplus_server.client.submit.dto.response; import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; +import com.moplus.moplus_server.domain.problem.domain.problem.Problem; import java.util.List; import lombok.Builder; @@ -10,10 +11,10 @@ public record ProblemDetailResponse( List prescriptionImageUrls, ProblemSubmitStatus submitStatus ) { - public static ProblemDetailResponse of(String imageUrl, List prescriptionImageUrls, ProblemSubmitStatus submitStatus) { + public static ProblemDetailResponse of(Problem problem, ProblemSubmitStatus submitStatus) { return ProblemDetailResponse.builder() - .imageUrl(imageUrl) - .prescriptionImageUrls(prescriptionImageUrls) + .imageUrl(problem.getMainProblemImageUrl()) + .prescriptionImageUrls(problem.getPrescriptionImageUrls()) .submitStatus(submitStatus) .build(); } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java index 11cdd254..cbbb9140 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java @@ -47,8 +47,7 @@ public CommentaryGetResponse getCommentary(Long publishId, Long problemId) { // 문항 해설 생성 Problem problem = problemRepository.findByIdElseThrow(problemId); ProblemDetailResponse mainProblem = ProblemDetailResponse.of( - problem.getMainProblemImageUrl(), - problem.getPrescriptionImageUrls(), + problem, problemSubmit.getStatus() ); @@ -74,11 +73,7 @@ public CommentaryGetResponse getCommentary(Long publishId, Long problemId) { return CommentaryGetResponse.of( number + 1, - problem.getAnswer(), - problem.getMainAnalysisImageUrl(), - problem.getMainHandwritingExplanationImageUrl(), - problem.getReadingTipImageUrl(), - problem.getSeniorTipImageUrl(), + problem, prescription ); } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemsGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemsGetService.java index 49d1af12..fc95938b 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemsGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemsGetService.java @@ -137,15 +137,23 @@ public ChildProblemClientGetResponse getChildProblem(Long publishId, Long proble ChildProblemSubmit childProblemSubmit = childProblemSubmitRepository.findByMemberIdAndPublishIdAndChildProblemIdElseThrow( memberId, publishId, childProblemId); - int sequence = 0; + int childProblemNumber = 0; for (int i = 0; i < problem.getChildProblems().size(); i++) { if (problem.getChildProblems().get(i).getId().equals(childProblemId)) { - sequence = i + 1; + childProblemNumber = i + 1; break; } } - return ChildProblemClientGetResponse.of(problem.getNumber(), sequence, childProblem.getImageUrl(), childProblemSubmit.getStatus()); + // 문항 번호 추출 + ProblemSet problemSet = problemSetRepository.findByIdElseThrow(publish.getProblemSetId()); + List problemIds = problemSet.getProblemIds(); + int problemNumber = problemIds.indexOf(problemId); + if (problemNumber == -1) { + throw new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND_IN_PROBLEM_SET); + } + + return ChildProblemClientGetResponse.of(problemNumber + 1, childProblemNumber + 1, childProblem.getImageUrl(), childProblemSubmit.getStatus()); } private void denyAccessToFuturePublish(Publish publish){ From 5b2c7e43bda86977b5b1b7b497461963ccc6669c Mon Sep 17 00:00:00 2001 From: HongGit Date: Sun, 23 Mar 2025 20:43:50 +0900 Subject: [PATCH 22/48] =?UTF-8?q?[fix/#94]=20=EB=A7=A4=EC=A7=81=EB=84=98?= =?UTF-8?q?=EB=B2=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/submit/service/ProblemsGetService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemsGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemsGetService.java index fc95938b..ecf174e5 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemsGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemsGetService.java @@ -39,12 +39,14 @@ public class ProblemsGetService { private final ProblemSetRepository problemSetRepository; private final ChildProblemSubmitRepository childProblemSubmitRepository; private final ChildProblemRepository childProblemRepository; + private static final int MIN_MONTH = 1; + private static final int MAX_MONTH = 12; @Transactional(readOnly = true) public List getAllProblem(int year, int month) { Long memberId = 1L; - if (month < 1 || month > 12) { + if (month < MIN_MONTH || month > MAX_MONTH) { throw new InvalidValueException(ErrorCode.INVALID_MONTH_ERROR); } LocalDate startDate = LocalDate.of(year, month, 1); From 3ba3af2f3f1cc5b340c21f3f166c9a6c08be7282 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: Mon, 24 Mar 2025 01:09:08 +0900 Subject: [PATCH 23/48] =?UTF-8?q?[feat/98]=20=EC=9D=B4=EB=B2=88=EC=A3=BC?= =?UTF-8?q?=20=EB=AC=B8=EC=A0=9C=20=EC=98=81=EC=97=AD=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapper/ChildProblemMapperImpl.java | 2 +- .../service/mapper/ProblemMapperImpl.java | 2 +- .../publish/service/PublishGetService.java | 19 ++++- .../controller/HomeFeedController.java | 27 +++++++ .../dto/response/HomeFeedResponse.java | 68 +++++++++++++++++ .../service/HomeFeedFacadeService.java | 75 +++++++++++++++++++ .../repository/ProblemSubmitRepository.java | 5 +- .../service/ProblemSetGetService.java | 13 +++- .../service/CountStatisticsGetService.java | 18 +++++ 9 files changed, 220 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/moplus/moplus_server/client/homefeed/controller/HomeFeedController.java create mode 100644 src/main/java/com/moplus/moplus_server/client/homefeed/dto/response/HomeFeedResponse.java create mode 100644 src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java create mode 100644 src/main/java/com/moplus/moplus_server/statistic/Problem/service/CountStatisticsGetService.java 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 d528b5c5..7b1bd0c8 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 @@ -11,7 +11,7 @@ @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2025-03-14T20:25:51+0900", + date = "2025-03-24T01:02:22+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 b3e294c2..6992d6a4 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 @@ -15,7 +15,7 @@ @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2025-03-15T21:23:23+0900", + date = "2025-03-24T01:02:22+0900", comments = "version: 1.6.3, compiler: javac, environment: Java 17.0.10 (JetBrains s.r.o.)" ) @Component diff --git a/src/main/java/com/moplus/moplus_server/admin/publish/service/PublishGetService.java b/src/main/java/com/moplus/moplus_server/admin/publish/service/PublishGetService.java index d15f2c20..b7facd2c 100644 --- a/src/main/java/com/moplus/moplus_server/admin/publish/service/PublishGetService.java +++ b/src/main/java/com/moplus/moplus_server/admin/publish/service/PublishGetService.java @@ -1,13 +1,14 @@ package com.moplus.moplus_server.admin.publish.service; -import com.moplus.moplus_server.domain.problemset.domain.ProblemSet; -import com.moplus.moplus_server.domain.problemset.repository.ProblemSetRepository; import com.moplus.moplus_server.admin.publish.domain.Publish; import com.moplus.moplus_server.admin.publish.dto.response.PublishMonthGetResponse; import com.moplus.moplus_server.admin.publish.dto.response.PublishProblemSetResponse; +import com.moplus.moplus_server.domain.problemset.domain.ProblemSet; +import com.moplus.moplus_server.domain.problemset.repository.ProblemSetRepository; import com.moplus.moplus_server.domain.publish.repository.PublishRepository; import com.moplus.moplus_server.global.error.exception.ErrorCode; import com.moplus.moplus_server.global.error.exception.InvalidValueException; +import java.time.DayOfWeek; import java.time.LocalDate; import java.util.List; import java.util.Map; @@ -42,6 +43,7 @@ public List getPublishMonth(int year, int month) { .collect(Collectors.toList()); } + private Map getProblemSetMap(List publishes) { List problemSetIds = publishes.stream() .map(Publish::getProblemSetId) @@ -62,4 +64,17 @@ private PublishMonthGetResponse convertToResponse(Publish publish, Map getCurrentWeekPublishes() { + LocalDate today = LocalDate.now(); + LocalDate startOfWeek = today.with(DayOfWeek.MONDAY); + LocalDate endOfWeek = today.with(DayOfWeek.FRIDAY); + + return getWeeklyPublishes(startOfWeek, endOfWeek); + } + + private List getWeeklyPublishes(LocalDate startDate, LocalDate endDate) { + return publishRepository.findByPublishedDateBetween(startDate, endDate); + } } diff --git a/src/main/java/com/moplus/moplus_server/client/homefeed/controller/HomeFeedController.java b/src/main/java/com/moplus/moplus_server/client/homefeed/controller/HomeFeedController.java new file mode 100644 index 00000000..2e1bc012 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/homefeed/controller/HomeFeedController.java @@ -0,0 +1,27 @@ +package com.moplus.moplus_server.client.homefeed.controller; + +import com.moplus.moplus_server.client.homefeed.dto.response.HomeFeedResponse; +import com.moplus.moplus_server.client.homefeed.service.HomeFeedFacadeService; +import com.moplus.moplus_server.global.annotation.AuthUser; +import com.moplus.moplus_server.member.domain.Member; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "홈 피드 조회", description = "홈 피드 관련 API") +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/client/home-feed") +public class HomeFeedController { + + private final HomeFeedFacadeService homeFeedFacadeService; + + @Operation(summary = "홈 피드 조회", description = "회원의 홈 피드 정보를 조회합니다.") + @GetMapping("") + public HomeFeedResponse getHomeFeed(@AuthUser Member member) { + return homeFeedFacadeService.getHomeFeed(member); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/homefeed/dto/response/HomeFeedResponse.java b/src/main/java/com/moplus/moplus_server/client/homefeed/dto/response/HomeFeedResponse.java new file mode 100644 index 00000000..8ad4c077 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/homefeed/dto/response/HomeFeedResponse.java @@ -0,0 +1,68 @@ +package com.moplus.moplus_server.client.homefeed.dto.response; + +import com.moplus.moplus_server.admin.problemset.dto.response.ProblemSetGetResponse; +import com.moplus.moplus_server.admin.problemset.dto.response.ProblemSummaryResponse; +import java.time.LocalDate; +import java.util.List; + +public record HomeFeedResponse( + List dailyProgresses, + List problemSets +) { + public static HomeFeedResponse of( + List dailyProgresses, + List problemSets + ) { + return new HomeFeedResponse(dailyProgresses, problemSets); + } + + public record DailyProgressResponse( + LocalDate date, + double progressRate + ) { + public static DailyProgressResponse of(LocalDate date, double progressRate) { + return new DailyProgressResponse(date, progressRate); + } + } + + public record ProblemSetHomeFeedResponse( + LocalDate date, + Long problemSetId, + String title, + Long submitCount, + ProblemHomeFeedResponse problemHomeFeedResponse + ) { + public static ProblemSetHomeFeedResponse of(LocalDate date, ProblemSetGetResponse problemSetGetResponse, + Long submitCount) { + return new ProblemSetHomeFeedResponse( + date, + problemSetGetResponse.id(), + problemSetGetResponse.title(), + submitCount, + ProblemHomeFeedResponse.of(problemSetGetResponse.problemSummaries().get(0)) + ); + } + + public static ProblemSetHomeFeedResponse of(LocalDate date) { + return new ProblemSetHomeFeedResponse( + date, + null, + null, + null, + null + ); + } + } + + public record ProblemHomeFeedResponse( + Long problemId, + String mainProblemImageUrl + ) { + public static ProblemHomeFeedResponse of(ProblemSummaryResponse problemSummaryResponse) { + return new ProblemHomeFeedResponse( + problemSummaryResponse.problemId(), + problemSummaryResponse.mainProblemImageUrl() + ); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java b/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java new file mode 100644 index 00000000..3f8399d9 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java @@ -0,0 +1,75 @@ +package com.moplus.moplus_server.client.homefeed.service; + +import com.moplus.moplus_server.admin.problemset.dto.response.ProblemSetGetResponse; +import com.moplus.moplus_server.admin.publish.domain.Publish; +import com.moplus.moplus_server.admin.publish.service.PublishGetService; +import com.moplus.moplus_server.client.homefeed.dto.response.HomeFeedResponse; +import com.moplus.moplus_server.client.homefeed.dto.response.HomeFeedResponse.DailyProgressResponse; +import com.moplus.moplus_server.client.homefeed.dto.response.HomeFeedResponse.ProblemSetHomeFeedResponse; +import com.moplus.moplus_server.domain.problemset.service.ProblemSetGetService; +import com.moplus.moplus_server.member.domain.Member; +import com.moplus.moplus_server.statistic.Problem.domain.ProblemSetStatistic; +import com.moplus.moplus_server.statistic.Problem.repository.ProblemSetStatisticRepository; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class HomeFeedFacadeService { + + private final ProblemSetStatisticRepository problemSetStatisticRepository; + private final PublishGetService publishGetService; + private final ProblemSetGetService problemSetGetService; + + @Transactional(readOnly = true) + public HomeFeedResponse getHomeFeed(Member member) { + Long memberId = member.getId(); + + List dailyProgresses = new ArrayList<>(); // 다음 PR에서 구현 + List problemSets = getWeekdayProblemSets(); + + return HomeFeedResponse.of(dailyProgresses, problemSets); + } + + private List getWeekdayProblemSets() { + LocalDate today = LocalDate.now(); + LocalDate monday = today.with(DayOfWeek.MONDAY); + LocalDate friday = today.with(DayOfWeek.FRIDAY); + + // 월요일부터 금요일까지의 발행된 문제 세트 조회 + List publishes = publishGetService.getCurrentWeekPublishes(); + Map publishByDate = publishes.stream() + .collect(Collectors.toMap(Publish::getPublishedDate, publish -> publish)); + + // 문제 세트 정보 조회 + List problemSetIds = publishes.stream() + .map(Publish::getProblemSetId) + .toList(); + Map problemSetMap = problemSetGetService.getProblemSets(problemSetIds).stream() + .collect(Collectors.toMap(ProblemSetGetResponse::id, response -> response)); + + // 월요일부터 금요일까지의 모든 날짜에 대한 응답 생성 + List responses = new ArrayList<>(); + for (LocalDate date = monday; !date.isAfter(friday); date = date.plusDays(1)) { + Publish publish = publishByDate.get(date); + if (publish != null) { + ProblemSetGetResponse problemSet = problemSetMap.get(publish.getProblemSetId()); + Long submitCount = problemSetStatisticRepository.findById(problemSet.id()) + .map(ProblemSetStatistic::getSubmitCount) + .orElse(0L); + responses.add(ProblemSetHomeFeedResponse.of(date, problemSet, submitCount)); + } else { + responses.add(ProblemSetHomeFeedResponse.of(date)); + } + } + + return responses; + } +} \ No newline at end of file diff --git a/src/main/java/com/moplus/moplus_server/client/submit/repository/ProblemSubmitRepository.java b/src/main/java/com/moplus/moplus_server/client/submit/repository/ProblemSubmitRepository.java index d9ff2554..a78f2f6f 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/repository/ProblemSubmitRepository.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/repository/ProblemSubmitRepository.java @@ -12,8 +12,9 @@ public interface ProblemSubmitRepository extends JpaRepository findByMemberIdAndPublishIdAndProblemId(Long memberId, Long publishId, Long problemId); - default ProblemSubmit findByMemberIdAndPublishIdAndProblemIdElseThrow(Long memberId, Long publishId, Long problemId) { + default ProblemSubmit findByMemberIdAndPublishIdAndProblemIdElseThrow(Long memberId, Long publishId, + Long problemId) { return findByMemberIdAndPublishIdAndProblemId(memberId, publishId, problemId).orElseThrow( () -> new NotFoundException(ErrorCode.PROBLEM_SUBMIT_NOT_CONFIRMED)); } -} +} \ No newline at end of file diff --git a/src/main/java/com/moplus/moplus_server/domain/problemset/service/ProblemSetGetService.java b/src/main/java/com/moplus/moplus_server/domain/problemset/service/ProblemSetGetService.java index ef66f469..8d7791b3 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problemset/service/ProblemSetGetService.java +++ b/src/main/java/com/moplus/moplus_server/domain/problemset/service/ProblemSetGetService.java @@ -1,14 +1,14 @@ package com.moplus.moplus_server.domain.problemset.service; +import com.moplus.moplus_server.admin.problemset.dto.response.ProblemSetGetResponse; +import com.moplus.moplus_server.admin.problemset.dto.response.ProblemSummaryResponse; +import com.moplus.moplus_server.admin.publish.domain.Publish; import com.moplus.moplus_server.domain.concept.domain.ConceptTag; import com.moplus.moplus_server.domain.concept.repository.ConceptTagRepository; import com.moplus.moplus_server.domain.problem.domain.problem.Problem; import com.moplus.moplus_server.domain.problem.repository.ProblemRepository; import com.moplus.moplus_server.domain.problemset.domain.ProblemSet; -import com.moplus.moplus_server.admin.problemset.dto.response.ProblemSetGetResponse; -import com.moplus.moplus_server.admin.problemset.dto.response.ProblemSummaryResponse; import com.moplus.moplus_server.domain.problemset.repository.ProblemSetRepository; -import com.moplus.moplus_server.admin.publish.domain.Publish; import com.moplus.moplus_server.domain.publish.repository.PublishRepository; import com.moplus.moplus_server.global.error.exception.BusinessException; import com.moplus.moplus_server.global.error.exception.ErrorCode; @@ -55,4 +55,11 @@ public ProblemSetGetResponse getProblemSet(Long problemSetId) { } return ProblemSetGetResponse.of(problemSet, publishedDates, problemSummaries); } + + @Transactional(readOnly = true) + public List getProblemSets(List problemSetIds) { + return problemSetIds.stream() + .map(this::getProblemSet) + .toList(); + } } diff --git a/src/main/java/com/moplus/moplus_server/statistic/Problem/service/CountStatisticsGetService.java b/src/main/java/com/moplus/moplus_server/statistic/Problem/service/CountStatisticsGetService.java new file mode 100644 index 00000000..983a87d6 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/statistic/Problem/service/CountStatisticsGetService.java @@ -0,0 +1,18 @@ +package com.moplus.moplus_server.statistic.Problem.service; + +import com.moplus.moplus_server.statistic.Problem.repository.ProblemSetStatisticRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class CountStatisticsGetService { + + private final ProblemSetStatisticRepository problemSetStatisticRepository; + + @Transactional(readOnly = true) + public Long getProblemSetCount(Long id) { + return problemSetStatisticRepository.findByIdElseThrow(id).getSubmitCount(); + } +} From c8549c7e2e55f771e0352c8aa3422297b9c114aa 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: Mon, 24 Mar 2025 01:13:22 +0900 Subject: [PATCH 24/48] =?UTF-8?q?[feat/98]=20=EC=9D=B4=EB=B2=88=EC=A3=BC?= =?UTF-8?q?=20=EB=AC=B8=EC=A0=9C=20=EC=98=81=EC=97=AD=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/publish/service/PublishGetService.java | 11 +---------- .../homefeed/service/HomeFeedFacadeService.java | 2 +- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/moplus/moplus_server/admin/publish/service/PublishGetService.java b/src/main/java/com/moplus/moplus_server/admin/publish/service/PublishGetService.java index b7facd2c..d440d293 100644 --- a/src/main/java/com/moplus/moplus_server/admin/publish/service/PublishGetService.java +++ b/src/main/java/com/moplus/moplus_server/admin/publish/service/PublishGetService.java @@ -8,7 +8,6 @@ import com.moplus.moplus_server.domain.publish.repository.PublishRepository; import com.moplus.moplus_server.global.error.exception.ErrorCode; import com.moplus.moplus_server.global.error.exception.InvalidValueException; -import java.time.DayOfWeek; import java.time.LocalDate; import java.util.List; import java.util.Map; @@ -66,15 +65,7 @@ private PublishMonthGetResponse convertToResponse(Publish publish, Map getCurrentWeekPublishes() { - LocalDate today = LocalDate.now(); - LocalDate startOfWeek = today.with(DayOfWeek.MONDAY); - LocalDate endOfWeek = today.with(DayOfWeek.FRIDAY); - - return getWeeklyPublishes(startOfWeek, endOfWeek); - } - - private List getWeeklyPublishes(LocalDate startDate, LocalDate endDate) { + public List getPublishesBetweenDates(LocalDate startDate, LocalDate endDate) { return publishRepository.findByPublishedDateBetween(startDate, endDate); } } diff --git a/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java b/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java index 3f8399d9..86af53cb 100644 --- a/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java +++ b/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java @@ -44,7 +44,7 @@ private List getWeekdayProblemSets() { LocalDate friday = today.with(DayOfWeek.FRIDAY); // 월요일부터 금요일까지의 발행된 문제 세트 조회 - List publishes = publishGetService.getCurrentWeekPublishes(); + List publishes = publishGetService.getPublishesBetweenDates(monday, friday); Map publishByDate = publishes.stream() .collect(Collectors.toMap(Publish::getPublishedDate, publish -> publish)); From 76e06abcba86fb03937a6ed8c59b338181ec988b 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: Mon, 24 Mar 2025 02:09:03 +0900 Subject: [PATCH 25/48] =?UTF-8?q?[feat/#100]=20=EC=9D=B4=EB=B2=88=EC=A3=BC?= =?UTF-8?q?=20=EC=A7=84=EC=B2=99=EB=8F=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/HomeFeedResponse.java | 7 +-- .../service/HomeFeedFacadeService.java | 32 +++++++++--- .../client/submit/domain/ProgressStatus.java | 7 +++ .../service/ProblemSubmitGetService.java | 50 +++++++++++++++++++ 4 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/domain/ProgressStatus.java create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/service/ProblemSubmitGetService.java diff --git a/src/main/java/com/moplus/moplus_server/client/homefeed/dto/response/HomeFeedResponse.java b/src/main/java/com/moplus/moplus_server/client/homefeed/dto/response/HomeFeedResponse.java index 8ad4c077..2bda92a4 100644 --- a/src/main/java/com/moplus/moplus_server/client/homefeed/dto/response/HomeFeedResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/homefeed/dto/response/HomeFeedResponse.java @@ -2,6 +2,7 @@ import com.moplus.moplus_server.admin.problemset.dto.response.ProblemSetGetResponse; import com.moplus.moplus_server.admin.problemset.dto.response.ProblemSummaryResponse; +import com.moplus.moplus_server.client.submit.domain.ProgressStatus; import java.time.LocalDate; import java.util.List; @@ -18,10 +19,10 @@ public static HomeFeedResponse of( public record DailyProgressResponse( LocalDate date, - double progressRate + ProgressStatus progressStatus ) { - public static DailyProgressResponse of(LocalDate date, double progressRate) { - return new DailyProgressResponse(date, progressRate); + public static DailyProgressResponse of(LocalDate date, ProgressStatus progressStatus) { + return new DailyProgressResponse(date, progressStatus); } } diff --git a/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java b/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java index 86af53cb..3f0760ad 100644 --- a/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java +++ b/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java @@ -6,6 +6,8 @@ import com.moplus.moplus_server.client.homefeed.dto.response.HomeFeedResponse; import com.moplus.moplus_server.client.homefeed.dto.response.HomeFeedResponse.DailyProgressResponse; import com.moplus.moplus_server.client.homefeed.dto.response.HomeFeedResponse.ProblemSetHomeFeedResponse; +import com.moplus.moplus_server.client.submit.domain.ProgressStatus; +import com.moplus.moplus_server.client.submit.service.ProblemSubmitGetService; import com.moplus.moplus_server.domain.problemset.service.ProblemSetGetService; import com.moplus.moplus_server.member.domain.Member; import com.moplus.moplus_server.statistic.Problem.domain.ProblemSetStatistic; @@ -24,27 +26,41 @@ @RequiredArgsConstructor public class HomeFeedFacadeService { + private static final LocalDate today = LocalDate.now(); + private static final LocalDate monday = today.with(DayOfWeek.MONDAY); + private static final LocalDate friday = today.with(DayOfWeek.FRIDAY); private final ProblemSetStatisticRepository problemSetStatisticRepository; private final PublishGetService publishGetService; private final ProblemSetGetService problemSetGetService; + private final ProblemSubmitGetService problemSubmitGetService; @Transactional(readOnly = true) public HomeFeedResponse getHomeFeed(Member member) { Long memberId = member.getId(); - List dailyProgresses = new ArrayList<>(); // 다음 PR에서 구현 - List problemSets = getWeekdayProblemSets(); + List publishes = publishGetService.getPublishesBetweenDates(monday, friday); + + List dailyProgresses = getDailyProgresses(memberId, publishes); + List problemSets = getWeekdayProblemSets(publishes); return HomeFeedResponse.of(dailyProgresses, problemSets); } - private List getWeekdayProblemSets() { - LocalDate today = LocalDate.now(); - LocalDate monday = today.with(DayOfWeek.MONDAY); - LocalDate friday = today.with(DayOfWeek.FRIDAY); + private List getDailyProgresses(Long memberId, List publishes) { + Map progressStatuses = problemSubmitGetService.getProgressStatuses(memberId, + publishes); + + List responses = new ArrayList<>(); + for (LocalDate date = monday; !date.isAfter(friday); date = date.plusDays(1)) { + ProgressStatus status = progressStatuses.getOrDefault(date, ProgressStatus.NOT_STARTED); + responses.add(DailyProgressResponse.of(date, status)); + } + + return responses; + } + + private List getWeekdayProblemSets(List publishes) { - // 월요일부터 금요일까지의 발행된 문제 세트 조회 - List publishes = publishGetService.getPublishesBetweenDates(monday, friday); Map publishByDate = publishes.stream() .collect(Collectors.toMap(Publish::getPublishedDate, publish -> publish)); diff --git a/src/main/java/com/moplus/moplus_server/client/submit/domain/ProgressStatus.java b/src/main/java/com/moplus/moplus_server/client/submit/domain/ProgressStatus.java new file mode 100644 index 00000000..163ce374 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/domain/ProgressStatus.java @@ -0,0 +1,7 @@ +package com.moplus.moplus_server.client.submit.domain; + +public enum ProgressStatus { + COMPLETED, + IN_PROGRESS, + NOT_STARTED +} \ No newline at end of file diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemSubmitGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemSubmitGetService.java new file mode 100644 index 00000000..9e3e404f --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemSubmitGetService.java @@ -0,0 +1,50 @@ +package com.moplus.moplus_server.client.submit.service; + +import com.moplus.moplus_server.admin.publish.domain.Publish; +import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; +import com.moplus.moplus_server.client.submit.domain.ProgressStatus; +import com.moplus.moplus_server.client.submit.repository.ProblemSubmitRepository; +import com.moplus.moplus_server.domain.problemset.domain.ProblemSet; +import com.moplus.moplus_server.domain.problemset.repository.ProblemSetRepository; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class ProblemSubmitGetService { + + private final ProblemSubmitRepository problemSubmitRepository; + private final ProblemSetRepository problemSetRepository; + + @Transactional(readOnly = true) + public ProgressStatus getProgressStatus(Long memberId, Long publishId) { + List submits = problemSubmitRepository.findByMemberIdAndPublishId(memberId, publishId); + + if (submits.isEmpty()) { + return ProgressStatus.NOT_STARTED; + } + + ProblemSet problemSet = problemSetRepository.findByIdElseThrow(submits.get(0).getProblemId()); + + int totalProblems = problemSet.getProblemIds().size(); + + if (submits.size() == totalProblems) { + return ProgressStatus.COMPLETED; + } + return ProgressStatus.IN_PROGRESS; + } + + @Transactional(readOnly = true) + public Map getProgressStatuses(Long memberId, List publishes) { + return publishes.stream() + .collect(Collectors.toMap( + Publish::getPublishedDate, + publish -> getProgressStatus(memberId, publish.getId()) + )); + } +} \ No newline at end of file From eafda906d14221e84d1db9d4fdc79aabde0d7b9e 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: Mon, 24 Mar 2025 03:07:35 +0900 Subject: [PATCH 26/48] =?UTF-8?q?[feat/#102]=20=EB=B0=9C=ED=96=89=20?= =?UTF-8?q?=EC=86=8D=20=EB=AC=B8=ED=95=AD=20=EC=A1=B0=ED=9A=8C=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ProblemGetController.java | 68 ++++++++++++++++ .../dto/response/AllProblemGetResponse.java | 3 +- .../ChildProblemClientGetResponse.java | 2 +- .../response/ProblemClientGetResponse.java | 5 +- .../response/PublishClientGetResponse.java | 26 ++++++ .../service/ProblemsGetService.java | 81 +++++++++++++++---- .../controller/ProblemGetController.java | 52 ------------ .../submit/domain/ProblemSubmitStatus.java | 5 +- 8 files changed, 169 insertions(+), 73 deletions(-) create mode 100644 src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java rename src/main/java/com/moplus/moplus_server/client/{submit => problem}/dto/response/AllProblemGetResponse.java (86%) rename src/main/java/com/moplus/moplus_server/client/{submit => problem}/dto/response/ChildProblemClientGetResponse.java (92%) rename src/main/java/com/moplus/moplus_server/client/{submit => problem}/dto/response/ProblemClientGetResponse.java (83%) create mode 100644 src/main/java/com/moplus/moplus_server/client/problem/dto/response/PublishClientGetResponse.java rename src/main/java/com/moplus/moplus_server/client/{submit => problem}/service/ProblemsGetService.java (66%) delete mode 100644 src/main/java/com/moplus/moplus_server/client/submit/controller/ProblemGetController.java diff --git a/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java b/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java new file mode 100644 index 00000000..6ce2e5bc --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java @@ -0,0 +1,68 @@ +package com.moplus.moplus_server.client.problem.controller; + +import com.moplus.moplus_server.client.problem.dto.response.AllProblemGetResponse; +import com.moplus.moplus_server.client.problem.dto.response.ChildProblemClientGetResponse; +import com.moplus.moplus_server.client.problem.dto.response.ProblemClientGetResponse; +import com.moplus.moplus_server.client.problem.dto.response.PublishClientGetResponse; +import com.moplus.moplus_server.client.problem.service.ProblemsGetService; +import com.moplus.moplus_server.global.annotation.AuthUser; +import com.moplus.moplus_server.member.domain.Member; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +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.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "클라이언트 문제 조회", description = "클라이언트 문제 조회 관련 API") +@RestController +@RequestMapping("/api/v1/client") +@RequiredArgsConstructor +public class ProblemGetController { + + private final ProblemsGetService problemsGetService; + + @GetMapping("problem/all/{year}/{month}") + @Operation(summary = "전체 문제 조회", description = "월별 문제들에 대한 진행도와 정보들을 조회합니다.") + public ResponseEntity> getAllProblem( + @PathVariable("year") int year, + @PathVariable("month") int month, + @AuthUser Member member + ) { + return ResponseEntity.ok(problemsGetService.getAllProblem(member.getId(), year, month)); + } + + @GetMapping("problem/{publishId}") + @Operation(summary = "특정 발행 속 문항들 조회", description = "사용자에게 보여지는 특정 발행에 속한 문항을 조회합니다.") + public ResponseEntity getProblemsInPublish( + @PathVariable("publishId") Long publishId, + @AuthUser Member member + ) { + return ResponseEntity.ok(problemsGetService.getProblemsInPublish(member.getId(), publishId)); + } + + @GetMapping("problem/{publishId}/{problemId}") + @Operation(summary = "문항 조회", description = "사용자에게 보여지는 문항을 조회합니다.") + public ResponseEntity getProblem( + @PathVariable("publishId") Long publishId, + @PathVariable("problemId") Long problemId, + @AuthUser Member member + ) { + return ResponseEntity.ok(problemsGetService.getProblem(member.getId(), publishId, problemId)); + } + + @GetMapping("problem/{publishId}/{problemId}/{childProblemId}") + @Operation(summary = "새끼문항 조회", description = "사용자에게 보여지는 새끼문항을 조회합니다.") + public ResponseEntity getChildProblem( + @PathVariable("publishId") Long publishId, + @PathVariable("problemId") Long problemId, + @PathVariable("childProblemId") Long childProblemId, + @AuthUser Member member + ) { + return ResponseEntity.ok( + problemsGetService.getChildProblem(member.getId(), publishId, problemId, childProblemId)); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/AllProblemGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/AllProblemGetResponse.java similarity index 86% rename from src/main/java/com/moplus/moplus_server/client/submit/dto/response/AllProblemGetResponse.java rename to src/main/java/com/moplus/moplus_server/client/problem/dto/response/AllProblemGetResponse.java index 91c3c6fa..5ae37bea 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/AllProblemGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/AllProblemGetResponse.java @@ -1,6 +1,7 @@ -package com.moplus.moplus_server.client.submit.dto.response; +package com.moplus.moplus_server.client.problem.dto.response; import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; +import com.moplus.moplus_server.client.submit.dto.response.DayProgress; import java.time.LocalDate; import java.util.List; import lombok.Builder; diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java similarity index 92% rename from src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemClientGetResponse.java rename to src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java index f299d37b..5894d6e3 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemClientGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java @@ -1,4 +1,4 @@ -package com.moplus.moplus_server.client.submit.dto.response; +package com.moplus.moplus_server.client.problem.dto.response; import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; import lombok.Builder; diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java similarity index 83% rename from src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemClientGetResponse.java rename to src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java index 5ab0f5b5..4e8b682b 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ProblemClientGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java @@ -1,4 +1,4 @@ -package com.moplus.moplus_server.client.submit.dto.response; +package com.moplus.moplus_server.client.problem.dto.response; import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; @@ -15,7 +15,8 @@ public record ProblemClientGetResponse( ProblemSubmitStatus status, List childProblemStatuses ) { - public static ProblemClientGetResponse of(Problem problem, ProblemSubmitStatus status, List childProblemStatuses, int number) { + public static ProblemClientGetResponse of(Problem problem, ProblemSubmitStatus status, + List childProblemStatuses, int number) { return ProblemClientGetResponse.builder() .number(number) .imageUrl(problem.getMainProblemImageUrl()) diff --git a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/PublishClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/PublishClientGetResponse.java new file mode 100644 index 00000000..00a08a19 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/PublishClientGetResponse.java @@ -0,0 +1,26 @@ +package com.moplus.moplus_server.client.problem.dto.response; + +import com.moplus.moplus_server.admin.publish.domain.Publish; +import com.moplus.moplus_server.domain.problemset.domain.ProblemSet; +import java.time.LocalDate; +import java.util.List; +import lombok.Builder; + +@Builder +public record PublishClientGetResponse( + Long publishId, + LocalDate date, + String title, + List problems +) { + + public static PublishClientGetResponse of(Publish publish, ProblemSet problemSet, + List problems) { + return PublishClientGetResponse.builder() + .publishId(publish.getId()) + .date(publish.getPublishedDate()) + .title(problemSet.getTitle().getValue()) + .problems(problems) + .build(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemsGetService.java b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java similarity index 66% rename from src/main/java/com/moplus/moplus_server/client/submit/service/ProblemsGetService.java rename to src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java index ecf174e5..fd50b575 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ProblemsGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java @@ -1,15 +1,16 @@ -package com.moplus.moplus_server.client.submit.service; +package com.moplus.moplus_server.client.problem.service; import com.moplus.moplus_server.admin.publish.domain.Publish; +import com.moplus.moplus_server.client.problem.dto.response.AllProblemGetResponse; +import com.moplus.moplus_server.client.problem.dto.response.ChildProblemClientGetResponse; +import com.moplus.moplus_server.client.problem.dto.response.ProblemClientGetResponse; +import com.moplus.moplus_server.client.problem.dto.response.PublishClientGetResponse; import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmit; import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; -import com.moplus.moplus_server.client.submit.dto.response.AllProblemGetResponse; -import com.moplus.moplus_server.client.submit.dto.response.ChildProblemClientGetResponse; import com.moplus.moplus_server.client.submit.dto.response.DayProgress; -import com.moplus.moplus_server.client.submit.dto.response.ProblemClientGetResponse; import com.moplus.moplus_server.client.submit.repository.ChildProblemSubmitRepository; import com.moplus.moplus_server.client.submit.repository.ProblemSubmitRepository; import com.moplus.moplus_server.domain.problem.domain.childProblem.ChildProblem; @@ -25,6 +26,7 @@ import java.time.LocalDate; import java.util.ArrayList; import java.util.List; +import java.util.stream.IntStream; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -33,18 +35,33 @@ @RequiredArgsConstructor public class ProblemsGetService { + private static final int MIN_MONTH = 1; + private static final int MAX_MONTH = 12; private final PublishRepository publishRepository; private final ProblemSubmitRepository problemSubmitRepository; private final ProblemRepository problemRepository; private final ProblemSetRepository problemSetRepository; private final ChildProblemSubmitRepository childProblemSubmitRepository; private final ChildProblemRepository childProblemRepository; - private static final int MIN_MONTH = 1; - private static final int MAX_MONTH = 12; @Transactional(readOnly = true) - public List getAllProblem(int year, int month) { - Long memberId = 1L; + public PublishClientGetResponse getProblemsInPublish(Long memberId, Long publishId) { + Publish publish = publishRepository.findByIdElseThrow(publishId); + denyAccessToFuturePublish(publish); + + ProblemSet problemSet = problemSetRepository.findByIdElseThrow(publish.getProblemSetId()); + List problemIds = problemSet.getProblemIds(); + List problems = problemRepository.findAllById(problemIds); + + List problemClientGetResponses = IntStream.range(0, problems.size()) + .mapToObj(i -> getProblemStatus(memberId, publishId, problems.get(i).getId(), i + 1)) + .toList(); + + return PublishClientGetResponse.of(publish, problemSet, problemClientGetResponses); + } + + @Transactional(readOnly = true) + public List getAllProblem(Long memberId, int year, int month) { if (month < MIN_MONTH || month > MAX_MONTH) { throw new InvalidValueException(ErrorCode.INVALID_MONTH_ERROR); @@ -67,7 +84,7 @@ public List getAllProblem(int year, int month) { // 날짜별 사용자 제출 정보 조회 List submissions = problemSubmitRepository.findByMemberIdAndPublishId(memberId, publishId); List problemStatuses = submissions.stream() - .map(ProblemSubmit::getStatus ) + .map(ProblemSubmit::getStatus) .toList(); // 사용자 제출 정보 바탕으로 진행도 결정 @@ -88,8 +105,7 @@ private String getMainProblemImageUrl(Long problemSetId) { } @Transactional(readOnly = true) - public ProblemClientGetResponse getProblem(Long publishId, Long problemId) { - Long memberId = 1L; + public ProblemClientGetResponse getProblem(Long memberId, Long publishId, Long problemId) { // 발행 조회 Publish publish = publishRepository.findByIdElseThrow(publishId); @@ -116,7 +132,7 @@ public ProblemClientGetResponse getProblem(Long publishId, Long problemId) { .toList(); List childProblemStatuses = childProblemSubmitRepository.findAllByMemberIdAndPublishIdAndChildProblemIdIn( - memberId, publishId, childProblemIds).stream() + memberId, publishId, childProblemIds).stream() .map(ChildProblemSubmit::getStatus) .toList(); @@ -124,8 +140,8 @@ public ProblemClientGetResponse getProblem(Long publishId, Long problemId) { } @Transactional(readOnly = true) - public ChildProblemClientGetResponse getChildProblem(Long publishId, Long problemId, Long childProblemId) { - Long memberId = 1L; + public ChildProblemClientGetResponse getChildProblem(Long memberId, Long publishId, Long problemId, + Long childProblemId) { // 발행 조회 Publish publish = publishRepository.findByIdElseThrow(publishId); @@ -155,12 +171,45 @@ public ChildProblemClientGetResponse getChildProblem(Long publishId, Long proble throw new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND_IN_PROBLEM_SET); } - return ChildProblemClientGetResponse.of(problemNumber + 1, childProblemNumber + 1, childProblem.getImageUrl(), childProblemSubmit.getStatus()); + return ChildProblemClientGetResponse.of(problemNumber + 1, childProblemNumber + 1, childProblem.getImageUrl(), + childProblemSubmit.getStatus()); } - private void denyAccessToFuturePublish(Publish publish){ + private void denyAccessToFuturePublish(Publish publish) { if (publish.getPublishedDate().isAfter(LocalDate.now())) { throw new InvalidValueException(ErrorCode.FUTURE_PUBLISH_NOT_ACCESSIBLE); } } + + private ProblemClientGetResponse getProblemStatus(Long memberId, Long publishId, Long problemId, int number) { + // 문항 조회 + Problem problem = problemRepository.findByIdElseThrow(problemId); + + // 문항 제출 상태 조회 (없으면 NOT_STARTED) + ProblemSubmitStatus problemStatus = problemSubmitRepository + .findByMemberIdAndPublishIdAndProblemId(memberId, publishId, problemId) + .map(ProblemSubmit::getStatus) + .orElse(ProblemSubmitStatus.NOT_STARTED); + + // 새끼 문항 제출 상태 조회 + List childProblemIds = problem.getChildProblems().stream() + .map(ChildProblem::getId) + .toList(); + + List childProblemStatuses = new ArrayList<>(childProblemSubmitRepository + .findAllByMemberIdAndPublishIdAndChildProblemIdIn(memberId, publishId, childProblemIds) + .stream() + .map(ChildProblemSubmit::getStatus) + .toList()); + + // 새끼 문항이 있는데 제출 상태가 없는 경우 NOT_STARTED 추가 + if (childProblemStatuses.size() < childProblemIds.size()) { + int remainingCount = childProblemIds.size() - childProblemStatuses.size(); + for (int i = 0; i < remainingCount; i++) { + childProblemStatuses.add(ChildProblemSubmitStatus.NOT_STARTED); + } + } + + return ProblemClientGetResponse.of(problem, problemStatus, childProblemStatuses, number); + } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/ProblemGetController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/ProblemGetController.java deleted file mode 100644 index 0f2ee303..00000000 --- a/src/main/java/com/moplus/moplus_server/client/submit/controller/ProblemGetController.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.moplus.moplus_server.client.submit.controller; - -import com.moplus.moplus_server.client.submit.dto.response.AllProblemGetResponse; -import com.moplus.moplus_server.client.submit.dto.response.ChildProblemClientGetResponse; -import com.moplus.moplus_server.client.submit.dto.response.ProblemClientGetResponse; -import com.moplus.moplus_server.client.submit.service.ProblemsGetService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -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.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@Tag(name = "클라이언트 문제 조회", description = "클라이언트 문제 조회 관련 API") -@RestController -@RequestMapping("/api/v1/client") -@RequiredArgsConstructor -public class ProblemGetController { - - private final ProblemsGetService problemsGetService; - - @GetMapping("problem/all/{year}/{month}") - @Operation(summary = "전체 문제 조회", description = "월별 문제들에 대한 진행도와 정보들을 조회합니다.") - public ResponseEntity> getAllProblem( - @PathVariable int year, - @PathVariable int month - ) { - return ResponseEntity.ok(problemsGetService.getAllProblem(year, month)); - } - - @GetMapping("problem/{publishId}/{problemId}") - @Operation(summary = "문항 조회", description = "사용자에게 보여지는 문항을 조회합니다.") - public ResponseEntity getProblem( - @PathVariable Long publishId, - @PathVariable Long problemId - ) { - return ResponseEntity.ok(problemsGetService.getProblem(publishId, problemId)); - } - - @GetMapping("problem/{publishId}/{problemId}/{childProblemId}") - @Operation(summary = "새끼문항 조회", description = "사용자에게 보여지는 새끼문항을 조회합니다.") - public ResponseEntity getChildProblem( - @PathVariable Long publishId, - @PathVariable Long problemId, - @PathVariable Long childProblemId - ) { - return ResponseEntity.ok(problemsGetService.getChildProblem(publishId, problemId, childProblemId)); - } -} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmitStatus.java b/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmitStatus.java index 34bb4d45..2ccaadea 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmitStatus.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/domain/ProblemSubmitStatus.java @@ -4,7 +4,9 @@ public enum ProblemSubmitStatus { CORRECT, INCORRECT, IN_PROGRESS, - RETRY_CORRECT; + RETRY_CORRECT, + NOT_STARTED, + ; public static ProblemSubmitStatus determineStatus(ProblemSubmitStatus currentStatus, String memberAnswer, String problemAnswer) { boolean isCorrect = problemAnswer.trim().equals(memberAnswer.trim()); @@ -12,6 +14,7 @@ public static ProblemSubmitStatus determineStatus(ProblemSubmitStatus currentSta case CORRECT -> isCorrect ? CORRECT : INCORRECT; case INCORRECT -> isCorrect ? RETRY_CORRECT : INCORRECT; case IN_PROGRESS -> isCorrect ? CORRECT : INCORRECT; + case NOT_STARTED -> isCorrect ? CORRECT : INCORRECT; default -> isCorrect ? RETRY_CORRECT : INCORRECT; }; } From 19049c36e34a77e7a5d8ae79e3815d0bc4f86da1 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: Mon, 24 Mar 2025 03:13:46 +0900 Subject: [PATCH 27/48] =?UTF-8?q?[feat/#102]=20=EB=B0=9C=ED=96=89=20?= =?UTF-8?q?=EC=86=8D=20=EB=AC=B8=ED=95=AD=20=EC=A1=B0=ED=9A=8C=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProblemFeedProgressesGetResponse.java | 22 +++++++++++++++++++ .../response/PublishClientGetResponse.java | 4 ++-- .../problem/service/ProblemsGetService.java | 8 ++++--- 3 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemFeedProgressesGetResponse.java diff --git a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemFeedProgressesGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemFeedProgressesGetResponse.java new file mode 100644 index 00000000..75524804 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemFeedProgressesGetResponse.java @@ -0,0 +1,22 @@ +package com.moplus.moplus_server.client.problem.dto.response; + +import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; +import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; +import java.util.List; +import lombok.Builder; + +@Builder +public record ProblemFeedProgressesGetResponse( + int number, + ProblemSubmitStatus status, + List childProblemStatuses +) { + public static ProblemFeedProgressesGetResponse of(ProblemSubmitStatus status, + List childProblemStatuses, int number) { + return ProblemFeedProgressesGetResponse.builder() + .number(number) + .status(status) + .childProblemStatuses(childProblemStatuses) + .build(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/PublishClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/PublishClientGetResponse.java index 00a08a19..d0ba4f4e 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/PublishClientGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/PublishClientGetResponse.java @@ -11,11 +11,11 @@ public record PublishClientGetResponse( Long publishId, LocalDate date, String title, - List problems + List problems ) { public static PublishClientGetResponse of(Publish publish, ProblemSet problemSet, - List problems) { + List problems) { return PublishClientGetResponse.builder() .publishId(publish.getId()) .date(publish.getPublishedDate()) diff --git a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java index fd50b575..9f7f0b40 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java @@ -5,6 +5,7 @@ import com.moplus.moplus_server.client.problem.dto.response.AllProblemGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ChildProblemClientGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ProblemClientGetResponse; +import com.moplus.moplus_server.client.problem.dto.response.ProblemFeedProgressesGetResponse; import com.moplus.moplus_server.client.problem.dto.response.PublishClientGetResponse; import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmit; import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; @@ -53,7 +54,7 @@ public PublishClientGetResponse getProblemsInPublish(Long memberId, Long publish List problemIds = problemSet.getProblemIds(); List problems = problemRepository.findAllById(problemIds); - List problemClientGetResponses = IntStream.range(0, problems.size()) + List problemClientGetResponses = IntStream.range(0, problems.size()) .mapToObj(i -> getProblemStatus(memberId, publishId, problems.get(i).getId(), i + 1)) .toList(); @@ -181,7 +182,8 @@ private void denyAccessToFuturePublish(Publish publish) { } } - private ProblemClientGetResponse getProblemStatus(Long memberId, Long publishId, Long problemId, int number) { + private ProblemFeedProgressesGetResponse getProblemStatus(Long memberId, Long publishId, Long problemId, + int number) { // 문항 조회 Problem problem = problemRepository.findByIdElseThrow(problemId); @@ -210,6 +212,6 @@ private ProblemClientGetResponse getProblemStatus(Long memberId, Long publishId, } } - return ProblemClientGetResponse.of(problem, problemStatus, childProblemStatuses, number); + return ProblemFeedProgressesGetResponse.of(problemStatus, childProblemStatuses, number); } } From 05c11901033007cf3b2d7a19039c60d14a388d39 Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 25 Mar 2025 00:48:28 +0900 Subject: [PATCH 28/48] =?UTF-8?q?[hotfix]=20=EC=83=88=EB=81=BC=EB=AC=B8?= =?UTF-8?q?=ED=95=AD=20=EC=83=9D=EC=84=B1=20=EC=8B=9C=20=EC=98=81=EC=86=8D?= =?UTF-8?q?=EC=84=B1=EC=BB=A8=ED=85=8D=EC=8A=A4=ED=8A=B8=20flush=ED=95=98?= =?UTF-8?q?=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/problem/service/ChildProblemService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 86dcee3d..5d381d31 100644 --- 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 @@ -20,7 +20,7 @@ public Long createChildProblem(Long problemId) { Problem problem = findAndValidateProblem(problemId); ChildProblem newChildProblem = ChildProblem.createEmptyChildProblem(); problem.addChildProblem(newChildProblem); - + problemRepository.flush(); return getCreatedChildProblemId(problem); } From 967b47fcd6d33f18668e3bf006ab92072cb84130 Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 25 Mar 2025 18:50:12 +0900 Subject: [PATCH 29/48] =?UTF-8?q?[feat/#104]=20=EB=AC=B8=ED=95=AD=20?= =?UTF-8?q?=EC=8D=B8=EB=84=A4=EC=9D=BC=20=EC=A1=B0=ED=9A=8C=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ProblemGetController.java | 10 +++++++++ .../response/ProblemThumbnailResponse.java | 21 ++++++++++++++++++ .../problem/service/ProblemsGetService.java | 22 +++++++++++++++++++ .../global/error/exception/ErrorCode.java | 1 + 4 files changed, 54 insertions(+) create mode 100644 src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemThumbnailResponse.java diff --git a/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java b/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java index 6ce2e5bc..43b431c9 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java @@ -3,6 +3,7 @@ import com.moplus.moplus_server.client.problem.dto.response.AllProblemGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ChildProblemClientGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ProblemClientGetResponse; +import com.moplus.moplus_server.client.problem.dto.response.ProblemThumbnailResponse; import com.moplus.moplus_server.client.problem.dto.response.PublishClientGetResponse; import com.moplus.moplus_server.client.problem.service.ProblemsGetService; import com.moplus.moplus_server.global.annotation.AuthUser; @@ -65,4 +66,13 @@ public ResponseEntity getChildProblem( return ResponseEntity.ok( problemsGetService.getChildProblem(member.getId(), publishId, problemId, childProblemId)); } + + @GetMapping("problem/thumbnail/{publishId}/{number}") + @Operation(summary = "문항 썸네일 조회", description = "바로 풀어보기/단계별로 풀어보기 화면에서 필요한 문항을 조회합니다.") + public ResponseEntity getProblemThumbnail( + @PathVariable Long publishId, + @PathVariable int number + ) { + return ResponseEntity.ok(problemsGetService.getProblemThumbnail(publishId, number)); + } } diff --git a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemThumbnailResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemThumbnailResponse.java new file mode 100644 index 00000000..a0d311dd --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemThumbnailResponse.java @@ -0,0 +1,21 @@ +package com.moplus.moplus_server.client.problem.dto.response; + +import com.moplus.moplus_server.domain.problem.domain.problem.Problem; +import lombok.Builder; + +@Builder +public record ProblemThumbnailResponse( + int number, + String imageUrl, + Integer recommendedMinute, + Integer recommendedSecond +) { + public static ProblemThumbnailResponse of(int number, Problem problem) { + return ProblemThumbnailResponse.builder() + .number(number) + .imageUrl(problem.getMainProblemImageUrl()) + .recommendedMinute(problem.getRecommendedTime().getMinute()) + .recommendedSecond(problem.getRecommendedTime().getSecond()) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java index 9f7f0b40..256ee55e 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java @@ -6,6 +6,7 @@ import com.moplus.moplus_server.client.problem.dto.response.ChildProblemClientGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ProblemClientGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ProblemFeedProgressesGetResponse; +import com.moplus.moplus_server.client.problem.dto.response.ProblemThumbnailResponse; import com.moplus.moplus_server.client.problem.dto.response.PublishClientGetResponse; import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmit; import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; @@ -214,4 +215,25 @@ private ProblemFeedProgressesGetResponse getProblemStatus(Long memberId, Long pu return ProblemFeedProgressesGetResponse.of(problemStatus, childProblemStatuses, number); } + + @Transactional(readOnly = true) + public ProblemThumbnailResponse getProblemThumbnail(Long publishId, int number) { + // 발행 조회 + Publish publish = publishRepository.findByIdElseThrow(publishId); + denyAccessToFuturePublish(publish); + + // 문항 세트 조회 + ProblemSet problemSet = problemSetRepository.findByIdElseThrow(publish.getProblemSetId()); + List problemIds = problemSet.getProblemIds(); + + int index = number - 1; + if (index < 0 || index >= problemIds.size()) { + throw new NotFoundException(ErrorCode.PROBLEM_NUMBER_NOT_FOUND); + } + + //문항 조회 + Long problemId = problemIds.get(index); + Problem problem = problemRepository.findByIdElseThrow(problemId); + return ProblemThumbnailResponse.of(number, problem); + } } 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 c005c57a..d1365518 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 @@ -35,6 +35,7 @@ public enum ErrorCode { INVALID_CONFIRM_PROBLEM(HttpStatus.BAD_REQUEST, "유효하지 않은 문항들 : "), INVALID_DIFFICULTY(HttpStatus.BAD_REQUEST, "난이도는 1~10 사이의 숫자여야 합니다"), PROBLEM_NOT_FOUND_IN_PROBLEM_SET(HttpStatus.NOT_FOUND, "해당 날짜에 발행된 문항세트에 존재하는 문항이 아닙니다."), + PROBLEM_NUMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "번호에 해당하는 문항을 찾을 수 없습니다."), //새끼 문항 CHILD_PROBLEM_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 새끼 문제를 찾을 수 없습니다"), From de40d9d0d3885bcfe4095c9b545ddaf7e1fe5fe3 Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 25 Mar 2025 19:01:05 +0900 Subject: [PATCH 30/48] =?UTF-8?q?[fix/#106]=20=EC=83=88=EB=81=BC=EB=AC=B8?= =?UTF-8?q?=ED=95=AD=20=EC=A0=9C=EC=B6=9C=20=EC=97=86=EC=96=B4=EB=8F=84=20?= =?UTF-8?q?=ED=95=B4=EC=84=A4=20=EC=A1=B0=ED=9A=8C=EA=B0=80=EB=8A=A5?= =?UTF-8?q?=ED=95=98=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/submit/service/CommentaryGetService.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java index cbbb9140..2c826e2a 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java @@ -1,6 +1,8 @@ package com.moplus.moplus_server.client.submit.service; import com.moplus.moplus_server.admin.publish.domain.Publish; +import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmit; +import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; import com.moplus.moplus_server.client.submit.dto.response.ChildProblemDetailResponse; import com.moplus.moplus_server.client.submit.dto.response.CommentaryGetResponse; @@ -56,8 +58,10 @@ public CommentaryGetResponse getCommentary(Long publishId, Long problemId) { .map(cp -> ChildProblemDetailResponse.of( cp.getImageUrl(), cp.getPrescriptionImageUrls(), - childProblemSubmitRepository.findByMemberIdAndPublishIdAndChildProblemIdElseThrow(memberId, publishId, - cp.getId()).getStatus() + childProblemSubmitRepository.findByMemberIdAndPublishIdAndChildProblemId(memberId, publishId, + cp.getId()) + .map(ChildProblemSubmit::getStatus) + .orElse(ChildProblemSubmitStatus.NOT_STARTED) )).toList(); // 처방 정보 생성 From f59e40340b2c7f58537e1a36f2144f94d1de914f Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 25 Mar 2025 19:04:45 +0900 Subject: [PATCH 31/48] =?UTF-8?q?[fix/#106]=20=ED=86=A0=ED=81=B0=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=A9=A4=EB=B2=84=EC=A1=B0=ED=9A=8C=ED=95=98?= =?UTF-8?q?=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ClientSubmitController.java | 27 ++++++++++++------- .../controller/CommentaryGetController.java | 9 ++++--- .../submit/service/ClientSubmitService.java | 15 ++++------- .../submit/service/CommentaryGetService.java | 3 +-- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java index 25c495cc..21153ccc 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java @@ -8,6 +8,8 @@ import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitCreateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitUpdateRequest; import com.moplus.moplus_server.client.submit.service.ClientSubmitService; +import com.moplus.moplus_server.global.annotation.AuthUser; +import com.moplus.moplus_server.member.domain.Member; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -29,43 +31,48 @@ public class ClientSubmitController { @PostMapping("problemSubmit") @Operation(summary = "문항 제출 생성", description = "문항 제출을 '진행중'으로 생성합니다.") public ResponseEntity createProblemSubmit( - @RequestBody ProblemSubmitCreateRequest request + @RequestBody ProblemSubmitCreateRequest request, + @AuthUser Member member ) { - clientSubmitService.createProblemSubmit(request); + clientSubmitService.createProblemSubmit(member.getId(), request); return ResponseEntity.ok(null); } @PutMapping("problemSubmit") @Operation(summary = "문항 제출 업데이트", description = "제출한 답안을 바탕으로 문항 제출의 상태를 업데이트합니다.") public ResponseEntity updateProblemSubmit( - @RequestBody ProblemSubmitUpdateRequest request + @RequestBody ProblemSubmitUpdateRequest request, + @AuthUser Member member ) { - return ResponseEntity.ok(clientSubmitService.updateProblemSubmit(request)); + return ResponseEntity.ok(clientSubmitService.updateProblemSubmit(member.getId(), request)); } @PostMapping("childProblemSubmit") @Operation(summary = "새끼문항 제출 생성", description = "문항에 속한 새끼문항들을 '시작전'으로 생성합니다.") public ResponseEntity createProblemSubmit( - @RequestBody ChildProblemSubmitCreateRequest request + @RequestBody ChildProblemSubmitCreateRequest request, + @AuthUser Member member ) { - clientSubmitService.createChildProblemSubmit(request); + clientSubmitService.createChildProblemSubmit(member.getId(), request); return ResponseEntity.ok(null); } @PutMapping("childProblemSubmit") @Operation(summary = "새끼문항 제출 업데이트", description = "제출한 답안을 바탕으로 문항 제출의 상태를 업데이트합니다.") public ResponseEntity updateChildProblemSubmit( - @RequestBody ChildProblemSubmitUpdateRequest request + @RequestBody ChildProblemSubmitUpdateRequest request, + @AuthUser Member member ) { - return ResponseEntity.ok(clientSubmitService.updateChildProblemSubmit(request)); + return ResponseEntity.ok(clientSubmitService.updateChildProblemSubmit(member.getId(), request)); } @PutMapping("childProblemSubmit/incorrect") @Operation(summary = "새끼문항 제출 틀림 업데이트", description = "새끼문항 제출의 상태를 틀림으로 업데이트합니다.") public ResponseEntity updateChildProblemSubmitIncorrect( - @RequestBody ChildProblemSubmitUpdateIncorrectRequest request + @RequestBody ChildProblemSubmitUpdateIncorrectRequest request, + @AuthUser Member member ) { - clientSubmitService.updateChildProblemSubmitIncorrect(request); + clientSubmitService.updateChildProblemSubmitIncorrect(member.getId(), request); return ResponseEntity.ok(null); } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/CommentaryGetController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/CommentaryGetController.java index b46ad78a..025566c9 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/controller/CommentaryGetController.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/controller/CommentaryGetController.java @@ -2,6 +2,8 @@ import com.moplus.moplus_server.client.submit.dto.response.CommentaryGetResponse; import com.moplus.moplus_server.client.submit.service.CommentaryGetService; +import com.moplus.moplus_server.global.annotation.AuthUser; +import com.moplus.moplus_server.member.domain.Member; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; @@ -23,8 +25,9 @@ public class CommentaryGetController { @Operation(summary = "해설 조회", description = "문항 별 해설/처방을 조회합니다.") public ResponseEntity getCommentary( @RequestParam(value = "publishId", required = false) Long publishId, - @RequestParam(value = "problemId", required = false) Long problemId - ) { - return ResponseEntity.ok(commentaryGetService.getCommentary(publishId, problemId)); + @RequestParam(value = "problemId", required = false) Long problemId, + @AuthUser Member member + ) { + return ResponseEntity.ok(commentaryGetService.getCommentary(member.getId(), publishId, problemId)); } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java index f6ebd580..11c4066e 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java @@ -34,8 +34,7 @@ public class ClientSubmitService { private final ChildProblemRepository childProblemRepository; @Transactional - public void createProblemSubmit(ProblemSubmitCreateRequest request) { - Long memberId = 1L; + public void createProblemSubmit(Long memberId, ProblemSubmitCreateRequest request) { // 존재하는 발행인지 검증 publishRepository.existsByIdElseThrow(request.publishId()); @@ -52,8 +51,7 @@ public void createProblemSubmit(ProblemSubmitCreateRequest request) { } @Transactional - public ProblemSubmitStatus updateProblemSubmit(ProblemSubmitUpdateRequest request) { - Long memberId = 1L; + public ProblemSubmitStatus updateProblemSubmit(Long memberId, ProblemSubmitUpdateRequest request) { // 존재하는 발행인지 검증 publishRepository.existsByIdElseThrow(request.publishId()); @@ -73,8 +71,7 @@ public ProblemSubmitStatus updateProblemSubmit(ProblemSubmitUpdateRequest reques } @Transactional - public void createChildProblemSubmit(ChildProblemSubmitCreateRequest request) { - Long memberId = 1L; + public void createChildProblemSubmit(Long memberId, ChildProblemSubmitCreateRequest request) { // 존재하는 발행인지 검증 publishRepository.existsByIdElseThrow(request.publishId()); @@ -115,8 +112,7 @@ public void createChildProblemSubmit(ChildProblemSubmitCreateRequest request) { } @Transactional - public ChildProblemSubmitStatus updateChildProblemSubmit(ChildProblemSubmitUpdateRequest request) { - Long memberId = 1L; + public ChildProblemSubmitStatus updateChildProblemSubmit(Long memberId, ChildProblemSubmitUpdateRequest request) { // 존재하는 발행인지 검증 publishRepository.existsByIdElseThrow(request.publishId()); @@ -136,8 +132,7 @@ public ChildProblemSubmitStatus updateChildProblemSubmit(ChildProblemSubmitUpdat } @Transactional - public void updateChildProblemSubmitIncorrect(ChildProblemSubmitUpdateIncorrectRequest request) { - Long memberId = 1L; + public void updateChildProblemSubmitIncorrect(Long memberId, ChildProblemSubmitUpdateIncorrectRequest request) { // 존재하는 발행인지 검증 publishRepository.existsByIdElseThrow(request.publishId()); diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java index 2c826e2a..140c6474 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java @@ -35,8 +35,7 @@ public class CommentaryGetService { private final ChildProblemSubmitRepository childProblemSubmitRepository; @Transactional(readOnly = true) - public CommentaryGetResponse getCommentary(Long publishId, Long problemId) { - Long memberId = 1L; + public CommentaryGetResponse getCommentary(Long memberId, Long publishId, Long problemId) { // 발행 조회 Publish publish = publishRepository.findByIdElseThrow(publishId); From f0f7673e3c4390352dee2249d64f9cce386b37a2 Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 25 Mar 2025 19:13:32 +0900 Subject: [PATCH 32/48] =?UTF-8?q?[feat/#108]=20=EB=AC=B8=ED=95=AD=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20=EC=A3=BC=EA=B4=80=EC=8B=9D/?= =?UTF-8?q?=EA=B0=9D=EA=B4=80=EC=8B=9D=20=ED=95=84=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../problem/dto/response/ProblemClientGetResponse.java | 5 ++++- .../client/submit/dto/response/CommentaryGetResponse.java | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java index 4e8b682b..dbe5c762 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java @@ -2,6 +2,7 @@ import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; +import com.moplus.moplus_server.domain.problem.domain.problem.AnswerType; import com.moplus.moplus_server.domain.problem.domain.problem.Problem; import java.util.List; import lombok.Builder; @@ -13,7 +14,8 @@ public record ProblemClientGetResponse( Integer recommendedMinute, Integer recommendedSecond, ProblemSubmitStatus status, - List childProblemStatuses + List childProblemStatuses, + AnswerType answerType ) { public static ProblemClientGetResponse of(Problem problem, ProblemSubmitStatus status, List childProblemStatuses, int number) { @@ -24,6 +26,7 @@ public static ProblemClientGetResponse of(Problem problem, ProblemSubmitStatus s .childProblemStatuses(childProblemStatuses) .recommendedMinute(problem.getRecommendedTime().getMinute()) .recommendedSecond(problem.getRecommendedTime().getSecond()) + .answerType(problem.getAnswerType()) .build(); } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/CommentaryGetResponse.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/CommentaryGetResponse.java index e1b7c38a..9ac9a2db 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/CommentaryGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/CommentaryGetResponse.java @@ -1,5 +1,6 @@ package com.moplus.moplus_server.client.submit.dto.response; +import com.moplus.moplus_server.domain.problem.domain.problem.AnswerType; import com.moplus.moplus_server.domain.problem.domain.problem.Problem; import lombok.Builder; @@ -11,6 +12,7 @@ public record CommentaryGetResponse( String mainHandwritingExplanationImageUrl, String readingTipImageUrl, String seniorTipImageUrl, + AnswerType answerType, PrescriptionResponse prescription ) { public static CommentaryGetResponse of(int problemNumber, Problem problem, PrescriptionResponse prescription) { @@ -21,6 +23,7 @@ public static CommentaryGetResponse of(int problemNumber, Problem problem, Presc .mainHandwritingExplanationImageUrl(problem.getMainHandwritingExplanationImageUrl()) .readingTipImageUrl(problem.getReadingTipImageUrl()) .seniorTipImageUrl(problem.getSeniorTipImageUrl()) + .answerType(problem.getAnswerType()) .prescription(prescription) .build(); } From c5952555ef4c32e676744c7eded4c69c613a45be Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 25 Mar 2025 19:25:32 +0900 Subject: [PATCH 33/48] =?UTF-8?q?[hotfix]=20=EB=AC=B8=ED=95=AD=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=ED=95=84=EC=88=98=20=ED=95=84=EB=93=9C=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../problem/dto/response/ProblemClientGetResponse.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java index dbe5c762..f898f406 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java @@ -4,17 +4,25 @@ import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; import com.moplus.moplus_server.domain.problem.domain.problem.AnswerType; import com.moplus.moplus_server.domain.problem.domain.problem.Problem; +import jakarta.validation.constraints.NotNull; import java.util.List; import lombok.Builder; @Builder public record ProblemClientGetResponse( + @NotNull(message = "문항번호는 필수입니다.") int number, + @NotNull(message = "문항이미지는 필수입니다.") String imageUrl, + @NotNull(message = "추천시간은 필수입니다.") Integer recommendedMinute, + @NotNull(message = "추천시간은 필수입니다.") Integer recommendedSecond, + @NotNull(message = "문항제출 상태는 필수입니다.") ProblemSubmitStatus status, + @NotNull(message = "새끼문항제출 상태는 필수입니다.") List childProblemStatuses, + @NotNull(message = "답변타입은 필수입니다.") AnswerType answerType ) { public static ProblemClientGetResponse of(Problem problem, ProblemSubmitStatus status, From ef5fc4e0526bbb753074f4ffae13b11210b933dd Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 25 Mar 2025 22:27:20 +0900 Subject: [PATCH 34/48] =?UTF-8?q?[hotfix]=20=EB=AC=B8=ED=95=ADid=EB=A1=9C?= =?UTF-8?q?=20=EC=9A=94=EC=B2=AD=EB=B0=9B=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../problem/controller/ProblemGetController.java | 6 +++--- .../client/problem/service/ProblemsGetService.java | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java b/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java index 43b431c9..7197884e 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java @@ -67,12 +67,12 @@ public ResponseEntity getChildProblem( problemsGetService.getChildProblem(member.getId(), publishId, problemId, childProblemId)); } - @GetMapping("problem/thumbnail/{publishId}/{number}") + @GetMapping("problem/thumbnail/{publishId}/{problemId}") @Operation(summary = "문항 썸네일 조회", description = "바로 풀어보기/단계별로 풀어보기 화면에서 필요한 문항을 조회합니다.") public ResponseEntity getProblemThumbnail( @PathVariable Long publishId, - @PathVariable int number + @PathVariable Long problemId ) { - return ResponseEntity.ok(problemsGetService.getProblemThumbnail(publishId, number)); + return ResponseEntity.ok(problemsGetService.getProblemThumbnail(publishId, problemId)); } } diff --git a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java index 256ee55e..257d99f1 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java @@ -217,7 +217,7 @@ private ProblemFeedProgressesGetResponse getProblemStatus(Long memberId, Long pu } @Transactional(readOnly = true) - public ProblemThumbnailResponse getProblemThumbnail(Long publishId, int number) { + public ProblemThumbnailResponse getProblemThumbnail(Long publishId, Long problemId) { // 발행 조회 Publish publish = publishRepository.findByIdElseThrow(publishId); denyAccessToFuturePublish(publish); @@ -226,14 +226,14 @@ public ProblemThumbnailResponse getProblemThumbnail(Long publishId, int number) ProblemSet problemSet = problemSetRepository.findByIdElseThrow(publish.getProblemSetId()); List problemIds = problemSet.getProblemIds(); - int index = number - 1; - if (index < 0 || index >= problemIds.size()) { - throw new NotFoundException(ErrorCode.PROBLEM_NUMBER_NOT_FOUND); + // 문항 번호 추출 + int problemNumber = problemIds.indexOf(problemId); + if (problemNumber == -1) { + throw new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND_IN_PROBLEM_SET); } //문항 조회 - Long problemId = problemIds.get(index); Problem problem = problemRepository.findByIdElseThrow(problemId); - return ProblemThumbnailResponse.of(number, problem); + return ProblemThumbnailResponse.of(problemNumber + 1, problem); } } From 60d63240f200641970a7592b7c430d753b0d523e Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 25 Mar 2025 22:40:56 +0900 Subject: [PATCH 35/48] =?UTF-8?q?[hotfix]=20=EB=AC=B8=ED=95=AD=EC=A0=9C?= =?UTF-8?q?=EC=B6=9C=20=EC=99=84=EB=A3=8C=ED=95=B4=EC=95=BC=20=ED=95=B4?= =?UTF-8?q?=EC=84=A4=20=EC=A1=B0=ED=9A=8C=20=EA=B0=80=EB=8A=A5=ED=95=98?= =?UTF-8?q?=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/problem/controller/ProblemGetController.java | 4 ++-- ...ailResponse.java => ProblemClientThumbnailResponse.java} | 6 +++--- .../client/problem/service/ProblemsGetService.java | 6 +++--- .../client/submit/service/CommentaryGetService.java | 5 +++++ .../moplus_server/global/error/exception/ErrorCode.java | 1 + 5 files changed, 14 insertions(+), 8 deletions(-) rename src/main/java/com/moplus/moplus_server/client/problem/dto/response/{ProblemThumbnailResponse.java => ProblemClientThumbnailResponse.java} (75%) diff --git a/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java b/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java index 7197884e..4e9c161f 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java @@ -3,7 +3,7 @@ import com.moplus.moplus_server.client.problem.dto.response.AllProblemGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ChildProblemClientGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ProblemClientGetResponse; -import com.moplus.moplus_server.client.problem.dto.response.ProblemThumbnailResponse; +import com.moplus.moplus_server.client.problem.dto.response.ProblemClientThumbnailResponse; import com.moplus.moplus_server.client.problem.dto.response.PublishClientGetResponse; import com.moplus.moplus_server.client.problem.service.ProblemsGetService; import com.moplus.moplus_server.global.annotation.AuthUser; @@ -69,7 +69,7 @@ public ResponseEntity getChildProblem( @GetMapping("problem/thumbnail/{publishId}/{problemId}") @Operation(summary = "문항 썸네일 조회", description = "바로 풀어보기/단계별로 풀어보기 화면에서 필요한 문항을 조회합니다.") - public ResponseEntity getProblemThumbnail( + public ResponseEntity getProblemThumbnail( @PathVariable Long publishId, @PathVariable Long problemId ) { diff --git a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemThumbnailResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientThumbnailResponse.java similarity index 75% rename from src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemThumbnailResponse.java rename to src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientThumbnailResponse.java index a0d311dd..6c429dba 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemThumbnailResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientThumbnailResponse.java @@ -4,14 +4,14 @@ import lombok.Builder; @Builder -public record ProblemThumbnailResponse( +public record ProblemClientThumbnailResponse( int number, String imageUrl, Integer recommendedMinute, Integer recommendedSecond ) { - public static ProblemThumbnailResponse of(int number, Problem problem) { - return ProblemThumbnailResponse.builder() + public static ProblemClientThumbnailResponse of(int number, Problem problem) { + return ProblemClientThumbnailResponse.builder() .number(number) .imageUrl(problem.getMainProblemImageUrl()) .recommendedMinute(problem.getRecommendedTime().getMinute()) diff --git a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java index 257d99f1..c23693f6 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java @@ -6,7 +6,7 @@ import com.moplus.moplus_server.client.problem.dto.response.ChildProblemClientGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ProblemClientGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ProblemFeedProgressesGetResponse; -import com.moplus.moplus_server.client.problem.dto.response.ProblemThumbnailResponse; +import com.moplus.moplus_server.client.problem.dto.response.ProblemClientThumbnailResponse; import com.moplus.moplus_server.client.problem.dto.response.PublishClientGetResponse; import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmit; import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; @@ -217,7 +217,7 @@ private ProblemFeedProgressesGetResponse getProblemStatus(Long memberId, Long pu } @Transactional(readOnly = true) - public ProblemThumbnailResponse getProblemThumbnail(Long publishId, Long problemId) { + public ProblemClientThumbnailResponse getProblemThumbnail(Long publishId, Long problemId) { // 발행 조회 Publish publish = publishRepository.findByIdElseThrow(publishId); denyAccessToFuturePublish(publish); @@ -234,6 +234,6 @@ public ProblemThumbnailResponse getProblemThumbnail(Long publishId, Long problem //문항 조회 Problem problem = problemRepository.findByIdElseThrow(problemId); - return ProblemThumbnailResponse.of(problemNumber + 1, problem); + return ProblemClientThumbnailResponse.of(problemNumber + 1, problem); } } diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java index 140c6474..1508838d 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/CommentaryGetService.java @@ -4,6 +4,7 @@ import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmit; import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; import com.moplus.moplus_server.client.submit.domain.ProblemSubmit; +import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; import com.moplus.moplus_server.client.submit.dto.response.ChildProblemDetailResponse; import com.moplus.moplus_server.client.submit.dto.response.CommentaryGetResponse; import com.moplus.moplus_server.client.submit.dto.response.PrescriptionResponse; @@ -44,6 +45,10 @@ public CommentaryGetResponse getCommentary(Long memberId, Long publishId, Long p // 문항 제출 조회 ProblemSubmit problemSubmit = problemSubmitRepository.findByMemberIdAndPublishIdAndProblemIdElseThrow(memberId, publishId, problemId); + if (problemSubmit.getStatus() == ProblemSubmitStatus.IN_PROGRESS + || problemSubmit.getStatus() == ProblemSubmitStatus.NOT_STARTED) { + throw new InvalidValueException(ErrorCode.PROBLEM_SUBMIT_NOT_COMPLETED); + } // 문항 해설 생성 Problem problem = problemRepository.findByIdElseThrow(problemId); 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 d1365518..e3d047b6 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 @@ -87,6 +87,7 @@ public enum ErrorCode { // 문항 제출 PROBLEM_SUBMIT_NOT_CONFIRMED(HttpStatus.NOT_FOUND, "문항 제출 정보를 찾을 수 없습니다."), + PROBLEM_SUBMIT_NOT_COMPLETED(HttpStatus.BAD_REQUEST, "문항 제출을 완료해야 해설을 조회할 수 있습니다."), // 새끼문항 제출 CHILD_PROBLEM_SUBMIT_NOT_CONFIRMED(HttpStatus.NOT_FOUND, "새끼문항 제출 정보를 찾을 수 없습니다."), From efabdb19ce05cd6ad270368ae842153174407f6b Mon Sep 17 00:00:00 2001 From: HongGit Date: Tue, 25 Mar 2025 22:43:44 +0900 Subject: [PATCH 36/48] =?UTF-8?q?[hotfix]=20=EB=B0=9C=ED=96=89=20=EB=AC=B8?= =?UTF-8?q?=ED=95=AD=EB=93=A4=20=EC=A1=B0=ED=9A=8C=20=EC=8B=9C=20=EB=AC=B8?= =?UTF-8?q?=ED=95=AD=EB=B2=88=ED=98=B8=20=EB=8C=80=EC=8B=A0=20ID=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/ProblemFeedProgressesGetResponse.java | 6 +++--- .../client/problem/service/ProblemsGetService.java | 9 ++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemFeedProgressesGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemFeedProgressesGetResponse.java index 75524804..1b8511a3 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemFeedProgressesGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemFeedProgressesGetResponse.java @@ -7,14 +7,14 @@ @Builder public record ProblemFeedProgressesGetResponse( - int number, + Long problemId, ProblemSubmitStatus status, List childProblemStatuses ) { public static ProblemFeedProgressesGetResponse of(ProblemSubmitStatus status, - List childProblemStatuses, int number) { + List childProblemStatuses, Long problemId) { return ProblemFeedProgressesGetResponse.builder() - .number(number) + .problemId(problemId) .status(status) .childProblemStatuses(childProblemStatuses) .build(); diff --git a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java index c23693f6..6db65a87 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java @@ -55,8 +55,8 @@ public PublishClientGetResponse getProblemsInPublish(Long memberId, Long publish List problemIds = problemSet.getProblemIds(); List problems = problemRepository.findAllById(problemIds); - List problemClientGetResponses = IntStream.range(0, problems.size()) - .mapToObj(i -> getProblemStatus(memberId, publishId, problems.get(i).getId(), i + 1)) + List problemClientGetResponses = problems.stream() + .map(problem -> getProblemStatus(memberId, publishId, problem.getId())) .toList(); return PublishClientGetResponse.of(publish, problemSet, problemClientGetResponses); @@ -183,8 +183,7 @@ private void denyAccessToFuturePublish(Publish publish) { } } - private ProblemFeedProgressesGetResponse getProblemStatus(Long memberId, Long publishId, Long problemId, - int number) { + private ProblemFeedProgressesGetResponse getProblemStatus(Long memberId, Long publishId, Long problemId) { // 문항 조회 Problem problem = problemRepository.findByIdElseThrow(problemId); @@ -213,7 +212,7 @@ private ProblemFeedProgressesGetResponse getProblemStatus(Long memberId, Long pu } } - return ProblemFeedProgressesGetResponse.of(problemStatus, childProblemStatuses, number); + return ProblemFeedProgressesGetResponse.of(problemStatus, childProblemStatuses, problemId); } @Transactional(readOnly = true) From ed16fcf34296a5e23bc7128e1292c4a3b2df53fe Mon Sep 17 00:00:00 2001 From: HongGit Date: Thu, 27 Mar 2025 18:34:27 +0900 Subject: [PATCH 37/48] =?UTF-8?q?[feat/#111]=20=EB=AC=B8=ED=95=AD=EC=97=90?= =?UTF-8?q?=20=ED=8F=AC=ED=95=A8=EB=90=9C=20=EC=83=88=EB=81=BC=EB=AC=B8?= =?UTF-8?q?=ED=95=ADID=EB=93=A4=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ProblemGetController.java | 10 +++++++++ .../ChildProblemsClientGetResponse.java | 19 ++++++++++++++++ .../problem/service/ProblemsGetService.java | 22 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemsClientGetResponse.java diff --git a/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java b/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java index 4e9c161f..a38d9940 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/controller/ProblemGetController.java @@ -2,6 +2,7 @@ import com.moplus.moplus_server.client.problem.dto.response.AllProblemGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ChildProblemClientGetResponse; +import com.moplus.moplus_server.client.problem.dto.response.ChildProblemsClientGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ProblemClientGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ProblemClientThumbnailResponse; import com.moplus.moplus_server.client.problem.dto.response.PublishClientGetResponse; @@ -75,4 +76,13 @@ public ResponseEntity getProblemThumbnail( ) { return ResponseEntity.ok(problemsGetService.getProblemThumbnail(publishId, problemId)); } + + @GetMapping("problem/child/{publishId}/{problemId}") + @Operation(summary = "문항에 포함된 새끼문항 정보 조회", description = "단계별로 풀어보기 이후 화면들에서 필요한 정보들을 조회합니다.") + public ResponseEntity getChildProblems( + @PathVariable Long publishId, + @PathVariable Long problemId + ) { + return ResponseEntity.ok(problemsGetService.getChildProblems(publishId, problemId)); + } } diff --git a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemsClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemsClientGetResponse.java new file mode 100644 index 00000000..e2494eb8 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemsClientGetResponse.java @@ -0,0 +1,19 @@ +package com.moplus.moplus_server.client.problem.dto.response; + +import com.moplus.moplus_server.domain.problem.domain.childProblem.ChildProblem; +import com.moplus.moplus_server.domain.problem.domain.problem.Problem; +import java.util.List; +import lombok.Builder; + +@Builder +public record ChildProblemsClientGetResponse( + String mainProblemImageUrl, + List childProblemIds +) { + public static ChildProblemsClientGetResponse of(Problem problem) { + return ChildProblemsClientGetResponse.builder() + .mainProblemImageUrl(problem.getMainProblemImageUrl()) + .childProblemIds(problem.getChildProblems().stream().map(ChildProblem::getId).toList()) + .build(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java index 6db65a87..7f80a4f4 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java @@ -4,6 +4,7 @@ import com.moplus.moplus_server.admin.publish.domain.Publish; import com.moplus.moplus_server.client.problem.dto.response.AllProblemGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ChildProblemClientGetResponse; +import com.moplus.moplus_server.client.problem.dto.response.ChildProblemsClientGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ProblemClientGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ProblemFeedProgressesGetResponse; import com.moplus.moplus_server.client.problem.dto.response.ProblemClientThumbnailResponse; @@ -235,4 +236,25 @@ public ProblemClientThumbnailResponse getProblemThumbnail(Long publishId, Long p Problem problem = problemRepository.findByIdElseThrow(problemId); return ProblemClientThumbnailResponse.of(problemNumber + 1, problem); } + + @Transactional(readOnly = true) + public ChildProblemsClientGetResponse getChildProblems(Long publishId, Long problemId) { + // 발행 조회 + Publish publish = publishRepository.findByIdElseThrow(publishId); + denyAccessToFuturePublish(publish); + + // 문항 세트 조회 + ProblemSet problemSet = problemSetRepository.findByIdElseThrow(publish.getProblemSetId()); + List problemIds = problemSet.getProblemIds(); + + // 발행된 문항세트에 포함된 문항인지 검사 + int problemNumber = problemIds.indexOf(problemId); + if (problemNumber == -1) { + throw new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND_IN_PROBLEM_SET); + } + + //문항 조회 + Problem problem = problemRepository.findByIdElseThrow(problemId); + return ChildProblemsClientGetResponse.of(problem); + } } From 1d7c861f3602bf928a35446befe1d59d8e35cb1e Mon Sep 17 00:00:00 2001 From: HongGit Date: Thu, 27 Mar 2025 18:56:36 +0900 Subject: [PATCH 38/48] =?UTF-8?q?[hotfix]=20=EC=83=88=EB=81=BC=EB=AC=B8?= =?UTF-8?q?=ED=95=AD=20=EC=A1=B0=ED=9A=8C=20=EC=9D=91=EB=8B=B5=20=ED=95=84?= =?UTF-8?q?=EC=88=98=EA=B0=92=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../problem/dto/response/ChildProblemClientGetResponse.java | 5 +++++ .../problem/dto/response/ChildProblemsClientGetResponse.java | 3 +++ 2 files changed, 8 insertions(+) diff --git a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java index 5894d6e3..201f4fd1 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java @@ -1,13 +1,18 @@ package com.moplus.moplus_server.client.problem.dto.response; import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; +import jakarta.validation.constraints.NotNull; import lombok.Builder; @Builder public record ChildProblemClientGetResponse( + @NotNull(message = "문항번호는 필수입니다.") int problemNumber, + @NotNull(message = "새끼문항번호는 필수입니다.") int childProblemNumber, + @NotNull(message = "이미지URL은 필수입니다.") String imageUrl, + @NotNull(message = "새끼문항 제출상태는 필수입니다.") ChildProblemSubmitStatus status ) { public static ChildProblemClientGetResponse of(int problemNumber, int childProblemNumber, String imageUrl, diff --git a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemsClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemsClientGetResponse.java index e2494eb8..d244ac83 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemsClientGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemsClientGetResponse.java @@ -2,12 +2,15 @@ import com.moplus.moplus_server.domain.problem.domain.childProblem.ChildProblem; import com.moplus.moplus_server.domain.problem.domain.problem.Problem; +import jakarta.validation.constraints.NotNull; import java.util.List; import lombok.Builder; @Builder public record ChildProblemsClientGetResponse( + @NotNull(message = "메인문항 이미지URL은 필수입니다.") String mainProblemImageUrl, + @NotNull(message = "새끼문항ID 리스트는 필수입니다.") List childProblemIds ) { public static ChildProblemsClientGetResponse of(Problem problem) { From 7e3e243f96a3f40f5c37711c3b67dbb483ea0702 Mon Sep 17 00:00:00 2001 From: HongGit Date: Thu, 27 Mar 2025 19:02:33 +0900 Subject: [PATCH 39/48] =?UTF-8?q?[hotfix]=20=EC=83=88=EB=81=BC=EB=AC=B8?= =?UTF-8?q?=ED=95=AD=20=EB=8B=B5=EB=B3=80=ED=83=80=EC=9E=85=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/ChildProblemClientGetResponse.java | 11 ++++++++--- .../client/problem/service/ProblemsGetService.java | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java index 201f4fd1..d61b4190 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java @@ -1,6 +1,8 @@ package com.moplus.moplus_server.client.problem.dto.response; import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; +import com.moplus.moplus_server.domain.problem.domain.childProblem.ChildProblem; +import com.moplus.moplus_server.domain.problem.domain.problem.AnswerType; import jakarta.validation.constraints.NotNull; import lombok.Builder; @@ -13,16 +15,19 @@ public record ChildProblemClientGetResponse( @NotNull(message = "이미지URL은 필수입니다.") String imageUrl, @NotNull(message = "새끼문항 제출상태는 필수입니다.") - ChildProblemSubmitStatus status + ChildProblemSubmitStatus status, + @NotNull(message = "답변타입은 필수입니다.") + AnswerType answerType ) { - public static ChildProblemClientGetResponse of(int problemNumber, int childProblemNumber, String imageUrl, + public static ChildProblemClientGetResponse of(int problemNumber, int childProblemNumber, ChildProblem childProblem, ChildProblemSubmitStatus status ) { return ChildProblemClientGetResponse.builder() .problemNumber(problemNumber) .childProblemNumber(childProblemNumber) - .imageUrl(imageUrl) + .imageUrl(childProblem.getImageUrl()) .status(status) + .answerType(childProblem.getAnswerType()) .build(); } } diff --git a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java index 7f80a4f4..c1634671 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java @@ -174,7 +174,7 @@ public ChildProblemClientGetResponse getChildProblem(Long memberId, Long publish throw new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND_IN_PROBLEM_SET); } - return ChildProblemClientGetResponse.of(problemNumber + 1, childProblemNumber + 1, childProblem.getImageUrl(), + return ChildProblemClientGetResponse.of(problemNumber + 1, childProblemNumber + 1, childProblem, childProblemSubmit.getStatus()); } From a4b3430a387a2d3e4257c0830be24aeed45029a6 Mon Sep 17 00:00:00 2001 From: HongGit Date: Thu, 27 Mar 2025 19:10:11 +0900 Subject: [PATCH 40/48] =?UTF-8?q?[hotfix]=20=EB=AC=B8=ED=95=AD,=20?= =?UTF-8?q?=EC=83=88=EB=81=BC=EB=AC=B8=ED=95=AD=20=EC=A0=95=EB=8B=B5?= =?UTF-8?q?=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=20=EC=A0=95=EB=8B=B5=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/ChildProblemClientGetResponse.java | 7 ++++++- .../problem/dto/response/ProblemClientGetResponse.java | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java index d61b4190..64299723 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ChildProblemClientGetResponse.java @@ -1,6 +1,7 @@ package com.moplus.moplus_server.client.problem.dto.response; import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; +import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; import com.moplus.moplus_server.domain.problem.domain.childProblem.ChildProblem; import com.moplus.moplus_server.domain.problem.domain.problem.AnswerType; import jakarta.validation.constraints.NotNull; @@ -17,7 +18,9 @@ public record ChildProblemClientGetResponse( @NotNull(message = "새끼문항 제출상태는 필수입니다.") ChildProblemSubmitStatus status, @NotNull(message = "답변타입은 필수입니다.") - AnswerType answerType + AnswerType answerType, + @NotNull(message = "정답은 필수입니다.") + String answer ) { public static ChildProblemClientGetResponse of(int problemNumber, int childProblemNumber, ChildProblem childProblem, ChildProblemSubmitStatus status @@ -28,6 +31,8 @@ public static ChildProblemClientGetResponse of(int problemNumber, int childProbl .imageUrl(childProblem.getImageUrl()) .status(status) .answerType(childProblem.getAnswerType()) + .answer(status == ChildProblemSubmitStatus.CORRECT || status == ChildProblemSubmitStatus.RETRY_CORRECT + ? childProblem.getAnswer() : null) .build(); } } diff --git a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java index f898f406..c33f3255 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/dto/response/ProblemClientGetResponse.java @@ -2,6 +2,7 @@ import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; import com.moplus.moplus_server.client.submit.domain.ProblemSubmitStatus; +import com.moplus.moplus_server.domain.problem.domain.Answer; import com.moplus.moplus_server.domain.problem.domain.problem.AnswerType; import com.moplus.moplus_server.domain.problem.domain.problem.Problem; import jakarta.validation.constraints.NotNull; @@ -23,7 +24,9 @@ public record ProblemClientGetResponse( @NotNull(message = "새끼문항제출 상태는 필수입니다.") List childProblemStatuses, @NotNull(message = "답변타입은 필수입니다.") - AnswerType answerType + AnswerType answerType, + @NotNull(message = "정답은 필수입니다.") + String answer ) { public static ProblemClientGetResponse of(Problem problem, ProblemSubmitStatus status, List childProblemStatuses, int number) { @@ -35,6 +38,8 @@ public static ProblemClientGetResponse of(Problem problem, ProblemSubmitStatus s .recommendedMinute(problem.getRecommendedTime().getMinute()) .recommendedSecond(problem.getRecommendedTime().getSecond()) .answerType(problem.getAnswerType()) + .answer(status == ProblemSubmitStatus.CORRECT || status == ProblemSubmitStatus.RETRY_CORRECT + ? problem.getAnswer() : null) .build(); } } From 670c3017fe4cfc5cda770965f026cabf4874a7e2 Mon Sep 17 00:00:00 2001 From: HongGit Date: Thu, 27 Mar 2025 19:36:44 +0900 Subject: [PATCH 41/48] =?UTF-8?q?[hotfix]=20=EC=83=88=EB=81=BC=EB=AC=B8?= =?UTF-8?q?=ED=95=AD=20=EC=A0=9C=EC=B6=9C=EB=82=B4=EC=97=AD=20=EC=97=86?= =?UTF-8?q?=EC=9D=84=20=EA=B2=BD=EC=9A=B0=EC=97=90=EB=A7=8C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../submit/service/ClientSubmitService.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java index 11c4066e..8104307b 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java @@ -101,13 +101,15 @@ public void createChildProblemSubmit(Long memberId, ChildProblemSubmitCreateRequ Optional existingChildProblemSubmit = childProblemSubmitRepository.findByMemberIdAndPublishIdAndChildProblemId(memberId, request.publishId(), childProblemId); - ChildProblemSubmit childProblemSubmit = ChildProblemSubmit.builder() - .memberId(memberId) - .publishId(request.publishId()) - .childProblemId(childProblemId) - .status(ChildProblemSubmitStatus.NOT_STARTED) - .build(); - childProblemSubmitRepository.save(childProblemSubmit); + if (existingChildProblemSubmit.isEmpty()) { + ChildProblemSubmit childProblemSubmit = ChildProblemSubmit.builder() + .memberId(memberId) + .publishId(request.publishId()) + .childProblemId(childProblemId) + .status(ChildProblemSubmitStatus.NOT_STARTED) + .build(); + childProblemSubmitRepository.save(childProblemSubmit); + } } } From 65d7c59b07393f66c9f4730344c3292482106b91 Mon Sep 17 00:00:00 2001 From: HongGit Date: Thu, 27 Mar 2025 19:53:13 +0900 Subject: [PATCH 42/48] =?UTF-8?q?[hotfix]=20=EC=83=88=EB=81=BC=EB=AC=B8?= =?UTF-8?q?=ED=95=AD=20=EB=B2=88=ED=98=B8=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/problem/service/ProblemsGetService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java index c1634671..27c16e19 100644 --- a/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java +++ b/src/main/java/com/moplus/moplus_server/client/problem/service/ProblemsGetService.java @@ -174,7 +174,7 @@ public ChildProblemClientGetResponse getChildProblem(Long memberId, Long publish throw new NotFoundException(ErrorCode.PROBLEM_NOT_FOUND_IN_PROBLEM_SET); } - return ChildProblemClientGetResponse.of(problemNumber + 1, childProblemNumber + 1, childProblem, + return ChildProblemClientGetResponse.of(problemNumber + 1, childProblemNumber, childProblem, childProblemSubmit.getStatus()); } From e89436513b1c6125c82b6c93b7433bfddf6cd2d0 Mon Sep 17 00:00:00 2001 From: HongGit Date: Thu, 27 Mar 2025 21:51:01 +0900 Subject: [PATCH 43/48] =?UTF-8?q?[hotfix]=20=EC=A0=95=EB=8B=B5=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=83=88=EB=81=BC=EB=AC=B8=ED=95=AD=EC=A0=95=EB=8B=B5=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ClientSubmitController.java | 3 ++- .../ChildProblemSubmitUpdateResponse.java | 20 +++++++++++++++++++ .../submit/service/ClientSubmitService.java | 5 +++-- 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemSubmitUpdateResponse.java diff --git a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java index 21153ccc..e90d4292 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/controller/ClientSubmitController.java @@ -7,6 +7,7 @@ import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitUpdateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitCreateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitUpdateRequest; +import com.moplus.moplus_server.client.submit.dto.response.ChildProblemSubmitUpdateResponse; import com.moplus.moplus_server.client.submit.service.ClientSubmitService; import com.moplus.moplus_server.global.annotation.AuthUser; import com.moplus.moplus_server.member.domain.Member; @@ -59,7 +60,7 @@ public ResponseEntity createProblemSubmit( @PutMapping("childProblemSubmit") @Operation(summary = "새끼문항 제출 업데이트", description = "제출한 답안을 바탕으로 문항 제출의 상태를 업데이트합니다.") - public ResponseEntity updateChildProblemSubmit( + public ResponseEntity updateChildProblemSubmit( @RequestBody ChildProblemSubmitUpdateRequest request, @AuthUser Member member ) { diff --git a/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemSubmitUpdateResponse.java b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemSubmitUpdateResponse.java new file mode 100644 index 00000000..5f88cb6b --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/client/submit/dto/response/ChildProblemSubmitUpdateResponse.java @@ -0,0 +1,20 @@ +package com.moplus.moplus_server.client.submit.dto.response; + +import com.moplus.moplus_server.client.submit.domain.ChildProblemSubmitStatus; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; + +@Builder +public record ChildProblemSubmitUpdateResponse( + @NotNull(message = "새끼문항제출 상태는 필수입니다.") + ChildProblemSubmitStatus status, + @NotNull(message = "새끼문항 정답은 필수입니다.") + String answer +) { + public static ChildProblemSubmitUpdateResponse of(ChildProblemSubmitStatus status, String answer) { + return ChildProblemSubmitUpdateResponse.builder() + .status(status) + .answer(answer) + .build(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java index 8104307b..ded486fb 100644 --- a/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java +++ b/src/main/java/com/moplus/moplus_server/client/submit/service/ClientSubmitService.java @@ -10,6 +10,7 @@ import com.moplus.moplus_server.client.submit.dto.request.ChildProblemSubmitUpdateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitCreateRequest; import com.moplus.moplus_server.client.submit.dto.request.ProblemSubmitUpdateRequest; +import com.moplus.moplus_server.client.submit.dto.response.ChildProblemSubmitUpdateResponse; import com.moplus.moplus_server.client.submit.repository.ChildProblemSubmitRepository; import com.moplus.moplus_server.client.submit.repository.ProblemSubmitRepository; import com.moplus.moplus_server.domain.problem.domain.childProblem.ChildProblem; @@ -114,7 +115,7 @@ public void createChildProblemSubmit(Long memberId, ChildProblemSubmitCreateRequ } @Transactional - public ChildProblemSubmitStatus updateChildProblemSubmit(Long memberId, ChildProblemSubmitUpdateRequest request) { + public ChildProblemSubmitUpdateResponse updateChildProblemSubmit(Long memberId, ChildProblemSubmitUpdateRequest request) { // 존재하는 발행인지 검증 publishRepository.existsByIdElseThrow(request.publishId()); @@ -130,7 +131,7 @@ public ChildProblemSubmitStatus updateChildProblemSubmit(Long memberId, ChildPro childProblem.getAnswer()); childProblemSubmit.updateStatus(status); - return status; + return ChildProblemSubmitUpdateResponse.of(status, childProblem.getAnswer()); } @Transactional From f5654b492683508833640cdedae65eff279f52a4 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: Thu, 27 Mar 2025 22:38:33 +0900 Subject: [PATCH 44/48] =?UTF-8?q?[hotfix]=20=ED=99=88=20=ED=94=BC=EB=93=9C?= =?UTF-8?q?=20=EB=AC=B8=EC=A0=9C=20=EC=84=B8=ED=8A=B8=20id=20=EB=8C=80?= =?UTF-8?q?=EC=8B=A0=20=EB=B0=9C=ED=96=89=20id=20=EC=A0=9C=EA=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/homefeed/dto/response/HomeFeedResponse.java | 7 ++++--- .../client/homefeed/service/HomeFeedFacadeService.java | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/moplus/moplus_server/client/homefeed/dto/response/HomeFeedResponse.java b/src/main/java/com/moplus/moplus_server/client/homefeed/dto/response/HomeFeedResponse.java index 2bda92a4..7699cb6a 100644 --- a/src/main/java/com/moplus/moplus_server/client/homefeed/dto/response/HomeFeedResponse.java +++ b/src/main/java/com/moplus/moplus_server/client/homefeed/dto/response/HomeFeedResponse.java @@ -28,16 +28,17 @@ public static DailyProgressResponse of(LocalDate date, ProgressStatus progressSt public record ProblemSetHomeFeedResponse( LocalDate date, - Long problemSetId, + Long publishId, String title, Long submitCount, ProblemHomeFeedResponse problemHomeFeedResponse ) { - public static ProblemSetHomeFeedResponse of(LocalDate date, ProblemSetGetResponse problemSetGetResponse, + public static ProblemSetHomeFeedResponse of(LocalDate date, Long publishId, + ProblemSetGetResponse problemSetGetResponse, Long submitCount) { return new ProblemSetHomeFeedResponse( date, - problemSetGetResponse.id(), + publishId, problemSetGetResponse.title(), submitCount, ProblemHomeFeedResponse.of(problemSetGetResponse.problemSummaries().get(0)) diff --git a/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java b/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java index 3f0760ad..4a53ef4a 100644 --- a/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java +++ b/src/main/java/com/moplus/moplus_server/client/homefeed/service/HomeFeedFacadeService.java @@ -80,7 +80,7 @@ private List getWeekdayProblemSets(List pub Long submitCount = problemSetStatisticRepository.findById(problemSet.id()) .map(ProblemSetStatistic::getSubmitCount) .orElse(0L); - responses.add(ProblemSetHomeFeedResponse.of(date, problemSet, submitCount)); + responses.add(ProblemSetHomeFeedResponse.of(date, publish.getId(), problemSet, submitCount)); } else { responses.add(ProblemSetHomeFeedResponse.of(date)); } From d3aa3d7db7379a21415acb85fd7b62db493401ba 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: Thu, 27 Mar 2025 22:52:42 +0900 Subject: [PATCH 45/48] =?UTF-8?q?[hotfix]=20=EC=BB=A8=ED=8E=8C=20=EC=A0=90?= =?UTF-8?q?=20=EC=83=88=EB=81=BC=EB=AC=B8=ED=95=AD=20=EC=9C=A0=ED=9A=A8?= =?UTF-8?q?=EC=84=B1=EB=8F=84=20=ED=8C=90=EB=8B=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/childProblem/ChildProblem.java | 9 +++++ .../problem/domain/problem/Problem.java | 3 +- .../repository/ProblemSetRepository.java | 1 + src/test/resources/insert-problem2.sql | 39 ++++++++++++++----- 4 files changed, 41 insertions(+), 11 deletions(-) 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 07e1c121..df5f090a 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 @@ -93,4 +93,13 @@ public void update(ChildProblem input) { public String getAnswer() { return answer.getValue(); } + + public boolean isValid() { + return imageUrl != null && !imageUrl.isEmpty() + && answer != null && !answer.getValue().isEmpty() + && answerType != null + && conceptTagIds != null && !conceptTagIds.isEmpty() + && prescriptionImageUrls != null && !prescriptionImageUrls.isEmpty() + && prescriptionImageUrls.stream().allMatch(url -> url != null && !url.isEmpty()); + } } 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 43ad2427..c0cca478 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 @@ -180,7 +180,8 @@ public boolean isValid() { && conceptTagIds != null && !conceptTagIds.isEmpty() && recommendedTime != null && recommendedTime.getMinute() != null && recommendedTime.getMinute() >= 0 - && recommendedTime.getSecond() != null && recommendedTime.getSecond() >= 0; + && recommendedTime.getSecond() != null && recommendedTime.getSecond() >= 0 + && childProblems.stream().allMatch(ChildProblem::isValid); } public String getTitle() { diff --git a/src/main/java/com/moplus/moplus_server/domain/problemset/repository/ProblemSetRepository.java b/src/main/java/com/moplus/moplus_server/domain/problemset/repository/ProblemSetRepository.java index 533674a8..b48961e5 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problemset/repository/ProblemSetRepository.java +++ b/src/main/java/com/moplus/moplus_server/domain/problemset/repository/ProblemSetRepository.java @@ -16,6 +16,7 @@ default ProblemSet findByIdElseThrow(Long problemSetId) { default void validatePublishableProblemSet(Long problemSetId) { ProblemSet problemSet = findByIdElseThrow(problemSetId); + //이거 soft delete 어노테이션으로 자동화 해야함(리팩토링 필요) if (problemSet.isDeleted()) { throw new InvalidValueException(ErrorCode.PROBLEM_SET_DELETED); } diff --git a/src/test/resources/insert-problem2.sql b/src/test/resources/insert-problem2.sql index 28340d6d..550bcc76 100644 --- a/src/test/resources/insert-problem2.sql +++ b/src/test/resources/insert-problem2.sql @@ -48,11 +48,12 @@ INSERT INTO child_problem (child_problem_id, image_url, answer_type, answer, - sequence) -VALUES (1, 1, 'child1.png', 'MULTIPLE_CHOICE', '1', 0), - (2, 1, 'child2.png', 'SHORT_ANSWER', '정답2', 1), - (3, 2, 'child3.png', 'MULTIPLE_CHOICE', '2', 0), - (4, 3, 'child4.png', 'SHORT_ANSWER', '3', 0); + sequence, + prescription_image_urls) +VALUES (1, 1, 'child1.png', 'MULTIPLE_CHOICE', '1', 0, 'child1_prescription1.png, child1_prescription2.png'), + (2, 1, 'child2.png', 'SHORT_ANSWER', '정답2', 1, 'child2_prescription1.png, child2_prescription2.png'), + (3, 2, 'child3.png', 'MULTIPLE_CHOICE', '2', 0, 'child3_prescription1.png, child3_prescription2.png'), + (4, 3, 'child4.png', 'SHORT_ANSWER', '3', 0, 'child4_prescription1.png, child4_prescription2.png'); -- 문제-컨셉 태그 연결 INSERT INTO problem_concept (problem_id, concept_tag_id) @@ -74,7 +75,7 @@ VALUES (1, 1), (4, 1), (4, 4); --- 유효하지 않은 문제 데이터 삽입 +-- 유효하지 않은 문제 데이터를 유효한 데이터로 수정 INSERT INTO problem (problem_id, problem_custom_id, practice_test_id, @@ -94,7 +95,25 @@ INSERT INTO problem (problem_id, is_confirmed, recommended_minute, recommended_second) -VALUES (4, '24052001004', 1, 4, 'GICHUL_PROBLEM', '', '', 1, '유효하지 않은 문제 설명', - '', 'mainAnalysis4.png', '', 'readingTip4.png', 'seniorTip4.png', - '', 'MULTIPLE_CHOICE', false, - null, null); \ No newline at end of file +VALUES (4, '24052001004', 1, 4, 'GICHUL_PROBLEM', '제목4', '4', 1, '유효한 문제로 수정', + 'mainProblem4.png', 'mainAnalysis4.png', 'mainHandwriting4.png', 'readingTip4.png', 'seniorTip4.png', + 'prescription7.png, prescription8.png', 'MULTIPLE_CHOICE', false, + 20, 0); + +-- problem 4에 대한 자식 문제 추가 +INSERT INTO child_problem (child_problem_id, + problem_id, + image_url, + answer_type, + answer, + sequence) +VALUES (5, 4, 'child5.png', 'MULTIPLE_CHOICE', '4', 0); + +-- problem 4와 자식 문제에 대한 컨셉 태그 추가 +INSERT INTO problem_concept (problem_id, concept_tag_id) +VALUES (4, 1), + (4, 3); + +INSERT INTO child_problem_concept (child_problem_id, concept_tag_id) +VALUES (5, 2), + (5, 3); \ No newline at end of file From 003f1ec13029bf2569139fbe779fa1c1bf88f862 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, 30 Mar 2025 12:11:03 +0900 Subject: [PATCH 46/48] =?UTF-8?q?[feat]=20=EB=AA=A8=EC=9D=98=EA=B3=A0?= =?UTF-8?q?=EC=82=AC=20=EA=B3=BC=EB=AA=A9=EC=97=90=20=EA=B3=A03=5F?= =?UTF-8?q?=EA=B3=B5=ED=86=B5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/problem/domain/practiceTest/Subject.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/moplus/moplus_server/domain/problem/domain/practiceTest/Subject.java b/src/main/java/com/moplus/moplus_server/domain/problem/domain/practiceTest/Subject.java index 75678f11..d77d8763 100644 --- a/src/main/java/com/moplus/moplus_server/domain/problem/domain/practiceTest/Subject.java +++ b/src/main/java/com/moplus/moplus_server/domain/problem/domain/practiceTest/Subject.java @@ -14,6 +14,7 @@ public enum Subject { 미적분("미적분", 30, 100, 3), 기하("기하", 30, 100, 4), 확률과통계("확률과통계", 30, 100, 5), + 고3_공통("고3_공통", 30, 100, 6), ; private final String value; From 205df589ee8fd4f1ded3ecde6f96ed0379eeabd3 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, 30 Mar 2025 14:24:19 +0900 Subject: [PATCH 47/48] =?UTF-8?q?[feat]=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose-dev.yml | 3 + docker-compose-prod.yml | 3 + .../moplus_server/member/domain/QMember.java | 20 ++- .../member/domain/QOauthInfo.java | 43 +++++ .../auth/controller/AuthController.java | 18 +++ .../auth/controller/AuthControllerDocs.java | 153 +++++++++++------- .../auth/dto/response/LoginResponse.java | 16 ++ .../response/oauth/OauthUserInfoResponse.java | 24 +++ .../oauth/kakao/KakaoAuthResponse.java | 15 ++ .../domain/auth/service/AuthService.java | 29 +++- .../domain/auth/service/OAuthProvider.java | 23 +++ .../auth/service/kakao/KakaoClient.java | 51 ++++++ .../config/restClient/RestClientConfig.java | 22 +++ .../config/security/SecurityConfig.java | 4 +- .../exception/AccessDeniedException.java | 12 ++ .../global/error/exception/ErrorCode.java | 2 + .../moplus_server/member/domain/Member.java | 14 +- .../member/domain/OauthInfo.java | 28 ++++ .../member/repository/MemberRepository.java | 5 +- .../member/service/MemberService.java | 12 ++ src/main/resources/application-security.yml | 9 +- 21 files changed, 442 insertions(+), 64 deletions(-) create mode 100644 src/main/generated/com/moplus/moplus_server/member/domain/QOauthInfo.java create mode 100644 src/main/java/com/moplus/moplus_server/domain/auth/dto/response/LoginResponse.java create mode 100644 src/main/java/com/moplus/moplus_server/domain/auth/dto/response/oauth/OauthUserInfoResponse.java create mode 100644 src/main/java/com/moplus/moplus_server/domain/auth/dto/response/oauth/kakao/KakaoAuthResponse.java create mode 100644 src/main/java/com/moplus/moplus_server/domain/auth/service/OAuthProvider.java create mode 100644 src/main/java/com/moplus/moplus_server/domain/auth/service/kakao/KakaoClient.java create mode 100644 src/main/java/com/moplus/moplus_server/global/config/restClient/RestClientConfig.java create mode 100644 src/main/java/com/moplus/moplus_server/global/error/exception/AccessDeniedException.java create mode 100644 src/main/java/com/moplus/moplus_server/member/domain/OauthInfo.java diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index ed48a4d0..23ec8a5a 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -27,6 +27,9 @@ services: - JWT_ACCESS_TOKEN_EXPIRATION_TIME=${JWT_ACCESS_TOKEN_EXPIRATION_TIME} # 기본값: 2시간 - JWT_REFRESH_TOKEN_EXPIRATION_TIME=${JWT_REFRESH_TOKEN_EXPIRATION_TIME} # 기본값: 7일 - JWT_ISSUER=${JWT_ISSUER} + - KAKAO_LOGIN_CLIENT_ID=${KAKAO_LOGIN_CLIENT_ID} + - KAKAO_LOGIN_CLIENT_SECRET=${KAKAO_LOGIN_CLIENT_SECRET} + - KAKAO_LOGIN_REDIRECT_URI=${KAKAO_LOGIN_DEV_REDIRECT_URI} depends_on: - mysql diff --git a/docker-compose-prod.yml b/docker-compose-prod.yml index e79df62f..6c03ee07 100644 --- a/docker-compose-prod.yml +++ b/docker-compose-prod.yml @@ -27,6 +27,9 @@ services: - JWT_ACCESS_TOKEN_EXPIRATION_TIME=${JWT_ACCESS_TOKEN_EXPIRATION_TIME} # 기본값: 2시간 - JWT_REFRESH_TOKEN_EXPIRATION_TIME=${JWT_REFRESH_TOKEN_EXPIRATION_TIME} # 기본값: 7일 - JWT_ISSUER=${JWT_ISSUER} + - KAKAO_LOGIN_CLIENT_ID=${KAKAO_LOGIN_CLIENT_ID} + - KAKAO_LOGIN_CLIENT_SECRET=${KAKAO_LOGIN_CLIENT_SECRET} + - KAKAO_LOGIN_REDIRECT_URI=${KAKAO_LOGIN_PROD_REDIRECT_URI} depends_on: - mysql networks: diff --git a/src/main/generated/com/moplus/moplus_server/member/domain/QMember.java b/src/main/generated/com/moplus/moplus_server/member/domain/QMember.java index a4dee67f..69c07415 100644 --- a/src/main/generated/com/moplus/moplus_server/member/domain/QMember.java +++ b/src/main/generated/com/moplus/moplus_server/member/domain/QMember.java @@ -7,6 +7,7 @@ import com.querydsl.core.types.PathMetadata; import javax.annotation.processing.Generated; import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; /** @@ -17,6 +18,8 @@ public class QMember extends EntityPathBase { private static final long serialVersionUID = -1541748259L; + private static final PathInits INITS = PathInits.DIRECT2; + 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); @@ -30,6 +33,8 @@ public class QMember extends EntityPathBase { public final StringPath name = createString("name"); + public final QOauthInfo oauthInfo; + public final StringPath password = createString("password"); public final EnumPath role = createEnum("role", MemberRole.class); @@ -38,15 +43,24 @@ public class QMember extends EntityPathBase { public final DateTimePath updatedDate = _super.updatedDate; public QMember(String variable) { - super(Member.class, forVariable(variable)); + this(Member.class, forVariable(variable), INITS); } public QMember(Path path) { - super(path.getType(), path.getMetadata()); + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); } public QMember(PathMetadata metadata) { - super(Member.class, metadata); + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QMember(PathMetadata metadata, PathInits inits) { + this(Member.class, metadata, inits); + } + + public QMember(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.oauthInfo = inits.isInitialized("oauthInfo") ? new QOauthInfo(forProperty("oauthInfo")) : null; } } diff --git a/src/main/generated/com/moplus/moplus_server/member/domain/QOauthInfo.java b/src/main/generated/com/moplus/moplus_server/member/domain/QOauthInfo.java new file mode 100644 index 00000000..f9a677a4 --- /dev/null +++ b/src/main/generated/com/moplus/moplus_server/member/domain/QOauthInfo.java @@ -0,0 +1,43 @@ +package com.moplus.moplus_server.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; + + +/** + * QOauthInfo is a Querydsl query type for OauthInfo + */ +@Generated("com.querydsl.codegen.DefaultEmbeddableSerializer") +public class QOauthInfo extends BeanPath { + + private static final long serialVersionUID = 1483708866L; + + public static final QOauthInfo oauthInfo = new QOauthInfo("oauthInfo"); + + public final StringPath name = createString("name"); + + public final StringPath oauthEmail = createString("oauthEmail"); + + public final StringPath oauthId = createString("oauthId"); + + public final StringPath oauthProvider = createString("oauthProvider"); + + public QOauthInfo(String variable) { + super(OauthInfo.class, forVariable(variable)); + } + + public QOauthInfo(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QOauthInfo(PathMetadata metadata) { + super(OauthInfo.class, metadata); + } + +} + diff --git a/src/main/java/com/moplus/moplus_server/domain/auth/controller/AuthController.java b/src/main/java/com/moplus/moplus_server/domain/auth/controller/AuthController.java index 862b52a3..87bf89ed 100644 --- a/src/main/java/com/moplus/moplus_server/domain/auth/controller/AuthController.java +++ b/src/main/java/com/moplus/moplus_server/domain/auth/controller/AuthController.java @@ -2,6 +2,7 @@ import com.moplus.moplus_server.domain.auth.dto.request.AdminLoginRequest; import com.moplus.moplus_server.domain.auth.dto.response.AccessTokenResponse; +import com.moplus.moplus_server.domain.auth.dto.response.LoginResponse; import com.moplus.moplus_server.domain.auth.dto.response.TokenResponse; import com.moplus.moplus_server.domain.auth.service.AuthService; import com.moplus.moplus_server.global.error.exception.ErrorCode; @@ -17,7 +18,9 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @@ -40,6 +43,21 @@ private static String validateRefreshTokenCookie(HttpServletRequest request) { .orElseThrow(() -> new NotFoundException(ErrorCode.BLANK_INPUT_VALUE)); } + @Override + @PostMapping("/oauth/social-login") + public ResponseEntity socialLogin( + @RequestHeader("social_access_token") String accessToken, + @RequestParam("provider") String provider, + HttpServletResponse response + ) { + LoginResponse loginResponse = authService.socialLogin(accessToken, provider); + + Cookie refreshTokenCookie = cookieUtil.createCookie(loginResponse.refreshToken()); + response.addCookie(refreshTokenCookie); + + return ResponseEntity.ok(loginResponse); + } + @Override @PostMapping("/admin/login") public ResponseEntity adminLogin( diff --git a/src/main/java/com/moplus/moplus_server/domain/auth/controller/AuthControllerDocs.java b/src/main/java/com/moplus/moplus_server/domain/auth/controller/AuthControllerDocs.java index 90a26104..632e5e89 100644 --- a/src/main/java/com/moplus/moplus_server/domain/auth/controller/AuthControllerDocs.java +++ b/src/main/java/com/moplus/moplus_server/domain/auth/controller/AuthControllerDocs.java @@ -2,12 +2,14 @@ import com.moplus.moplus_server.domain.auth.dto.request.AdminLoginRequest; import com.moplus.moplus_server.domain.auth.dto.response.AccessTokenResponse; +import com.moplus.moplus_server.domain.auth.dto.response.LoginResponse; import com.moplus.moplus_server.global.error.ErrorResponse; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.security.SecurityRequirements; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -16,66 +18,109 @@ @Tag(name = "인증", description = "인증 관련 API") public interface AuthControllerDocs { + @SecurityRequirements(value = {}) + @Operation( + summary = "소셜 로그인", + description = "소셜 액세스 토큰으로 로그인하여 자체 액세스 토큰을 발급받고 리프레시 토큰을 쿠키에 설정합니다.", + responses = { + @ApiResponse( + responseCode = "200", + description = "로그인 성공", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = LoginResponse.class) + ), + headers = { + @Header( + name = "Authorization", + description = "발급된 액세스 토큰", + schema = @Schema(type = "string") + ), + @Header( + name = "Set-Cookie", + description = "리프레시 토큰이 담긴 HTTP Only 쿠키", + schema = @Schema( + type = "string", + example = "refreshToken=xxx; Path=/; HttpOnly; Secure; SameSite=None" + ) + ) + } + ), + @ApiResponse( + responseCode = "401", + description = "유효하지 않은 소셜 액세스 토큰", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ) + } + ) + ResponseEntity socialLogin( + String accessToken, + String provider, + HttpServletResponse response + ); + + @SecurityRequirements(value = {}) @Operation( - summary = "어드민 로그인", - description = "이메일과 비밀번호로 로그인하여 액세스 토큰을 발급받고 리프레시 토큰을 쿠키에 설정합니다.", - responses = { - @ApiResponse( - responseCode = "200", - description = "로그인 성공", - content = @Content( - mediaType = "application/json", - schema = @Schema(implementation = AccessTokenResponse.class) - ), - headers = @Header( - name = "Set-Cookie", - description = "리프레시 토큰이 담긴 HTTP Only 쿠키", - schema = @Schema( - type = "string", - example = "refreshToken=xxx; Path=/; HttpOnly; Secure; SameSite=None" + summary = "어드민 로그인", + description = "이메일과 비밀번호로 로그인하여 액세스 토큰을 발급받고 리프레시 토큰을 쿠키에 설정합니다.", + responses = { + @ApiResponse( + responseCode = "200", + description = "로그인 성공", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = AccessTokenResponse.class) + ), + headers = @Header( + name = "Set-Cookie", + description = "리프레시 토큰이 담긴 HTTP Only 쿠키", + schema = @Schema( + type = "string", + example = "refreshToken=xxx; Path=/; HttpOnly; Secure; SameSite=None" + ) + ) + ), + @ApiResponse( + responseCode = "401", + description = "인증 실패 (잘못된 이메일 또는 비밀번호)", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) ) - ) - ), - @ApiResponse( - responseCode = "401", - description = "인증 실패 (잘못된 이메일 또는 비밀번호)", - content = @Content(schema = @Schema(implementation = ErrorResponse.class)) - ) - } + } ) ResponseEntity adminLogin(AdminLoginRequest request); + @SecurityRequirements(value = {}) @Operation( - summary = "토큰 재발급", - description = "리프레시 토큰을 통해 새로운 액세스 토큰을 발급하고 새로운 리프레시 토큰을 쿠키에 설정합니다.", - responses = { - @ApiResponse( - responseCode = "200", - description = "토큰 재발급 성공", - content = @Content( - mediaType = "application/json", - schema = @Schema(implementation = AccessTokenResponse.class) - ), - headers = @Header( - name = "Set-Cookie", - description = "새로운 리프레시 토큰이 담긴 HTTP Only 쿠키", - schema = @Schema( - type = "string", - example = "refreshToken=xxx; Path=/; HttpOnly; Secure; SameSite=None" + summary = "토큰 재발급", + description = "리프레시 토큰을 통해 새로운 액세스 토큰을 발급하고 새로운 리프레시 토큰을 쿠키에 설정합니다.", + responses = { + @ApiResponse( + responseCode = "200", + description = "토큰 재발급 성공", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = AccessTokenResponse.class) + ), + headers = @Header( + name = "Set-Cookie", + description = "새로운 리프레시 토큰이 담긴 HTTP Only 쿠키", + schema = @Schema( + type = "string", + example = "refreshToken=xxx; Path=/; HttpOnly; Secure; SameSite=None" + ) + ) + ), + @ApiResponse( + responseCode = "401", + description = "유효하지 않은 리프레시 토큰", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + @ApiResponse( + responseCode = "404", + description = "리프레시 토큰 쿠키 없음", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) ) - ) - ), - @ApiResponse( - responseCode = "401", - description = "유효하지 않은 리프레시 토큰", - content = @Content(schema = @Schema(implementation = ErrorResponse.class)) - ), - @ApiResponse( - responseCode = "404", - description = "리프레시 토큰 쿠키 없음", - content = @Content(schema = @Schema(implementation = ErrorResponse.class)) - ) - } + } ) ResponseEntity reissueToken(HttpServletRequest request, HttpServletResponse response); } \ No newline at end of file diff --git a/src/main/java/com/moplus/moplus_server/domain/auth/dto/response/LoginResponse.java b/src/main/java/com/moplus/moplus_server/domain/auth/dto/response/LoginResponse.java new file mode 100644 index 00000000..cc9126de --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/domain/auth/dto/response/LoginResponse.java @@ -0,0 +1,16 @@ +package com.moplus.moplus_server.domain.auth.dto.response; + +import com.moplus.moplus_server.member.domain.Member; + +public record LoginResponse( + Long memberId, + String email, + String accessToken, + String refreshToken +) { + + public static LoginResponse of(Member member, TokenResponse tokenPairResponse) { + return new LoginResponse(member.getId(), member.getOauthInfo().getOauthEmail(), tokenPairResponse.accessToken(), + tokenPairResponse.refreshToken()); + } +} diff --git a/src/main/java/com/moplus/moplus_server/domain/auth/dto/response/oauth/OauthUserInfoResponse.java b/src/main/java/com/moplus/moplus_server/domain/auth/dto/response/oauth/OauthUserInfoResponse.java new file mode 100644 index 00000000..a17781d5 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/domain/auth/dto/response/oauth/OauthUserInfoResponse.java @@ -0,0 +1,24 @@ +package com.moplus.moplus_server.domain.auth.dto.response.oauth; + +import com.moplus.moplus_server.domain.auth.service.OAuthProvider; +import com.moplus.moplus_server.member.domain.OauthInfo; +import lombok.Builder; + +@Builder +public record OauthUserInfoResponse( + String oauthId, + String email, + String name, + OAuthProvider provider + +) { + + public OauthInfo toEntity() { + return OauthInfo.builder() + .oauthId(this.oauthId()) + .oauthEmail(this.email()) + .name(this.name()) + .oauthProvider(this.provider().name()) + .build(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/domain/auth/dto/response/oauth/kakao/KakaoAuthResponse.java b/src/main/java/com/moplus/moplus_server/domain/auth/dto/response/oauth/kakao/KakaoAuthResponse.java new file mode 100644 index 00000000..4267ebf1 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/domain/auth/dto/response/oauth/kakao/KakaoAuthResponse.java @@ -0,0 +1,15 @@ +package com.moplus.moplus_server.domain.auth.dto.response.oauth.kakao; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record KakaoAuthResponse( + Long id, + @JsonProperty("kakao_account") KakaoAccountResponse kakaoAccount, + @JsonProperty("properties") PropertiesResponse properties) { + public record KakaoAccountResponse(String email) { + } + + public record PropertiesResponse( + String nickname, String profile_image, String thumbnail_image) { + } +} diff --git a/src/main/java/com/moplus/moplus_server/domain/auth/service/AuthService.java b/src/main/java/com/moplus/moplus_server/domain/auth/service/AuthService.java index 8d215b12..4036ce9e 100644 --- a/src/main/java/com/moplus/moplus_server/domain/auth/service/AuthService.java +++ b/src/main/java/com/moplus/moplus_server/domain/auth/service/AuthService.java @@ -1,12 +1,16 @@ package com.moplus.moplus_server.domain.auth.service; +import com.moplus.moplus_server.domain.auth.dto.response.LoginResponse; import com.moplus.moplus_server.domain.auth.dto.response.TokenResponse; -import com.moplus.moplus_server.member.domain.Member; -import com.moplus.moplus_server.member.service.MemberService; +import com.moplus.moplus_server.domain.auth.dto.response.oauth.OauthUserInfoResponse; +import com.moplus.moplus_server.domain.auth.service.kakao.KakaoClient; import com.moplus.moplus_server.global.error.exception.ErrorCode; import com.moplus.moplus_server.global.error.exception.InvalidValueException; import com.moplus.moplus_server.global.security.exception.JwtInvalidException; import com.moplus.moplus_server.global.security.utils.JwtUtil; +import com.moplus.moplus_server.member.domain.Member; +import com.moplus.moplus_server.member.domain.OauthInfo; +import com.moplus.moplus_server.member.service.MemberService; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.MalformedJwtException; @@ -20,9 +24,30 @@ @RequiredArgsConstructor public class AuthService { + private final KakaoClient kakaoClient; private final JwtUtil jwtUtil; private final MemberService memberService; + @Transactional + public LoginResponse socialLogin(String socialAccessToken, String provider) { + OauthUserInfoResponse oauthUserInfoResponse = getOauthUserInfo(socialAccessToken, OAuthProvider.from(provider)); + OauthInfo oauthInfo = oauthUserInfoResponse.toEntity(); + + Member member = memberService.getMemberByOAuthInfo(oauthInfo); + + String newAccessToken = jwtUtil.generateAccessToken(member); + String newRefreshToken = jwtUtil.generateRefreshToken(member); + + return LoginResponse.of(member, new TokenResponse(newAccessToken, newRefreshToken)); + } + + private OauthUserInfoResponse getOauthUserInfo(String socialAccessToken, OAuthProvider provider) { + return switch (provider) { + case APPLE -> null; //TODO: Apple OAuth + case KAKAO -> kakaoClient.getOauthUserInfo(socialAccessToken); + }; + } + @Transactional public TokenResponse reissueToken(String refreshToken) { if (refreshToken == null) { diff --git a/src/main/java/com/moplus/moplus_server/domain/auth/service/OAuthProvider.java b/src/main/java/com/moplus/moplus_server/domain/auth/service/OAuthProvider.java new file mode 100644 index 00000000..d2733178 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/domain/auth/service/OAuthProvider.java @@ -0,0 +1,23 @@ +package com.moplus.moplus_server.domain.auth.service; + +import com.moplus.moplus_server.global.error.exception.ErrorCode; +import com.moplus.moplus_server.global.error.exception.InvalidValueException; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum OAuthProvider { + KAKAO("KAKAO"), + APPLE("APPLE"), + ; + private final String value; + + public static OAuthProvider from(String provider) { + return switch (provider.toUpperCase()) { + case "APPLE" -> APPLE; + case "KAKAO" -> KAKAO; + default -> throw new InvalidValueException(ErrorCode.INVALID_PROVIDER_TYPE); + }; + } +} diff --git a/src/main/java/com/moplus/moplus_server/domain/auth/service/kakao/KakaoClient.java b/src/main/java/com/moplus/moplus_server/domain/auth/service/kakao/KakaoClient.java new file mode 100644 index 00000000..6d6f2a69 --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/domain/auth/service/kakao/KakaoClient.java @@ -0,0 +1,51 @@ +package com.moplus.moplus_server.domain.auth.service.kakao; + +import com.moplus.moplus_server.domain.auth.dto.response.oauth.OauthUserInfoResponse; +import com.moplus.moplus_server.domain.auth.dto.response.oauth.kakao.KakaoAuthResponse; +import com.moplus.moplus_server.domain.auth.service.OAuthProvider; +import com.moplus.moplus_server.global.error.exception.AccessDeniedException; +import com.moplus.moplus_server.global.error.exception.ErrorCode; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatusCode; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestClient; + +@Slf4j +@Component +@RequiredArgsConstructor +public class KakaoClient { + + public static final String KAKAO_USER_ME_URL = "https://kapi.kakao.com/v2/user/me"; + public static final String TOKEN_PREFIX = "Bearer "; + private final RestClient restClient; + + public OauthUserInfoResponse getOauthUserInfo(String token) { + KakaoAuthResponse kakaoAuthResponse = + restClient + .get() + .uri(KAKAO_USER_ME_URL) + .header("Authorization", TOKEN_PREFIX + token) + .exchange( + (request, response) -> { + HttpStatusCode statusCode = response.getStatusCode(); + log.info("Received response with status: {}", statusCode); + + if (!response.getStatusCode().is2xxSuccessful()) { + log.error("Kakao API error. Status: {}, Response: {}", + statusCode, response.bodyTo(String.class)); + throw new AccessDeniedException(ErrorCode.KAKAO_TOKEN_CLIENT_FAILED); + } + return Objects.requireNonNull( + response.bodyTo(KakaoAuthResponse.class)); + }); + + return new OauthUserInfoResponse( + kakaoAuthResponse.id().toString(), + kakaoAuthResponse.kakaoAccount().email(), + kakaoAuthResponse.properties().nickname(), + OAuthProvider.KAKAO + ); + } +} diff --git a/src/main/java/com/moplus/moplus_server/global/config/restClient/RestClientConfig.java b/src/main/java/com/moplus/moplus_server/global/config/restClient/RestClientConfig.java new file mode 100644 index 00000000..6b56edec --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/global/config/restClient/RestClientConfig.java @@ -0,0 +1,22 @@ +package com.moplus.moplus_server.global.config.restClient; + +import java.time.Duration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestClient; + +@Configuration +public class RestClientConfig { + + @Bean + public RestClient restClient() { + SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); + requestFactory.setConnectTimeout(Duration.ofSeconds(10)); + requestFactory.setReadTimeout(Duration.ofSeconds(5)); + + return RestClient.builder() + .requestFactory(requestFactory) + .build(); + } +} diff --git a/src/main/java/com/moplus/moplus_server/global/config/security/SecurityConfig.java b/src/main/java/com/moplus/moplus_server/global/config/security/SecurityConfig.java index 8d563b29..44a0ee6a 100644 --- a/src/main/java/com/moplus/moplus_server/global/config/security/SecurityConfig.java +++ b/src/main/java/com/moplus/moplus_server/global/config/security/SecurityConfig.java @@ -1,12 +1,12 @@ package com.moplus.moplus_server.global.config.security; -import com.moplus.moplus_server.member.service.MemberService; import com.moplus.moplus_server.global.security.filter.EmailPasswordAuthenticationFilter; import com.moplus.moplus_server.global.security.filter.JwtAuthenticationFilter; import com.moplus.moplus_server.global.security.handler.EmailPasswordSuccessHandler; import com.moplus.moplus_server.global.security.provider.EmailPasswordAuthenticationProvider; import com.moplus.moplus_server.global.security.provider.JwtTokenProvider; import com.moplus.moplus_server.global.security.utils.JwtUtil; +import com.moplus.moplus_server.member.service.MemberService; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; @@ -37,7 +37,7 @@ public class SecurityConfig { private final JwtUtil jwtUtil; private String[] allowUrls = {"/", "/favicon.ico", "/swagger-ui/**", "/v3/**", "/actuator/**", - "/api/v1/auth/reissue"}; + "/api/v1/auth/oauth/**", "/api/v1/auth/reissue"}; @Value("${cors-allowed-origins}") private List corsAllowedOrigins; diff --git a/src/main/java/com/moplus/moplus_server/global/error/exception/AccessDeniedException.java b/src/main/java/com/moplus/moplus_server/global/error/exception/AccessDeniedException.java new file mode 100644 index 00000000..e778626d --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/global/error/exception/AccessDeniedException.java @@ -0,0 +1,12 @@ +package com.moplus.moplus_server.global.error.exception; + +public class AccessDeniedException extends BusinessException { + + public AccessDeniedException(ErrorCode errorCode) { + super(errorCode); + } + + public AccessDeniedException(String message, ErrorCode errorCode) { + super(message, errorCode); + } +} 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 e3d047b6..e14a1d4d 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 @@ -23,6 +23,8 @@ public enum ErrorCode { AT_EXPIRED_AND_RT_NOT_FOUND(HttpStatus.UNAUTHORIZED, "AT는 만료되었고 RT는 비어있습니다."), RT_NOT_FOUND(HttpStatus.UNAUTHORIZED, "RT가 비어있습니다"), INVALID_PRINCIPAL(HttpStatus.UNAUTHORIZED, "잘못된 Principal입니다"), + INVALID_PROVIDER_TYPE(HttpStatus.BAD_REQUEST, "유효하지 않은 제공자 타입입니다"), + KAKAO_TOKEN_CLIENT_FAILED(HttpStatus.UNAUTHORIZED, "카카오 토큰 클라이언트 요청에 실패했습니다"), //모의고사 PRACTICE_TEST_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 모의고사를 찾을 수 없습니다"), diff --git a/src/main/java/com/moplus/moplus_server/member/domain/Member.java b/src/main/java/com/moplus/moplus_server/member/domain/Member.java index 09a00180..e6890cf0 100644 --- a/src/main/java/com/moplus/moplus_server/member/domain/Member.java +++ b/src/main/java/com/moplus/moplus_server/member/domain/Member.java @@ -2,6 +2,7 @@ import com.moplus.moplus_server.global.common.BaseEntity; import jakarta.persistence.Column; +import jakarta.persistence.Embedded; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; @@ -30,12 +31,23 @@ public class Member extends BaseEntity { @Enumerated(EnumType.STRING) private MemberRole role; + @Embedded + private OauthInfo oauthInfo; + @Builder - public Member(String name, String email, String password, MemberRole role) { + public Member(String name, String email, String password, MemberRole role, OauthInfo oauthInfo) { this.name = name; this.email = email; this.password = password; this.role = role; + this.oauthInfo = oauthInfo; + } + + public static Member createDefaultOAuthMember(OauthInfo oauthInfo) { + return Member.builder() + .role(MemberRole.USER) + .oauthInfo(oauthInfo) + .build(); } public boolean isMatchingPassword(String password) { diff --git a/src/main/java/com/moplus/moplus_server/member/domain/OauthInfo.java b/src/main/java/com/moplus/moplus_server/member/domain/OauthInfo.java new file mode 100644 index 00000000..8bc1037d --- /dev/null +++ b/src/main/java/com/moplus/moplus_server/member/domain/OauthInfo.java @@ -0,0 +1,28 @@ +package com.moplus.moplus_server.member.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Embeddable +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class OauthInfo { + + public String oauthId; + private String oauthProvider; + private String oauthEmail; + @Column(name = "oauth_name") + private String name; + + @Builder + public OauthInfo(String oauthId, String oauthProvider, String oauthEmail, String name) { + this.oauthId = oauthId; + this.oauthProvider = oauthProvider; + this.oauthEmail = oauthEmail; + this.name = name; + } +} diff --git a/src/main/java/com/moplus/moplus_server/member/repository/MemberRepository.java b/src/main/java/com/moplus/moplus_server/member/repository/MemberRepository.java index bebed81c..6195a369 100644 --- a/src/main/java/com/moplus/moplus_server/member/repository/MemberRepository.java +++ b/src/main/java/com/moplus/moplus_server/member/repository/MemberRepository.java @@ -1,12 +1,15 @@ package com.moplus.moplus_server.member.repository; -import com.moplus.moplus_server.member.domain.Member; import com.moplus.moplus_server.global.error.exception.ErrorCode; import com.moplus.moplus_server.global.error.exception.NotFoundException; +import com.moplus.moplus_server.member.domain.Member; +import com.moplus.moplus_server.member.domain.OauthInfo; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; public interface MemberRepository extends JpaRepository { + Optional findByOauthInfo(OauthInfo oauthInfo); + Optional findByEmail(String email); default Member findByEmailOrThrow(String email) { diff --git a/src/main/java/com/moplus/moplus_server/member/service/MemberService.java b/src/main/java/com/moplus/moplus_server/member/service/MemberService.java index d045121d..b7c61d46 100644 --- a/src/main/java/com/moplus/moplus_server/member/service/MemberService.java +++ b/src/main/java/com/moplus/moplus_server/member/service/MemberService.java @@ -1,6 +1,7 @@ package com.moplus.moplus_server.member.service; import com.moplus.moplus_server.member.domain.Member; +import com.moplus.moplus_server.member.domain.OauthInfo; import com.moplus.moplus_server.member.repository.MemberRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -12,6 +13,17 @@ public class MemberService { private final MemberRepository memberRepository; + @Transactional + public Member getMemberByOAuthInfo(OauthInfo oauthInfo) { + return memberRepository.findByOauthInfo(oauthInfo) + .orElseGet(() -> createMember(oauthInfo)); + } + + private Member createMember(OauthInfo oauthInfo) { + Member member = Member.createDefaultOAuthMember(oauthInfo); + return memberRepository.save(member); + } + @Transactional(readOnly = true) public Member getMemberByEmail(String email) { return memberRepository.findByEmailOrThrow(email); diff --git a/src/main/resources/application-security.yml b/src/main/resources/application-security.yml index c7ce0914..d94c6917 100644 --- a/src/main/resources/application-security.yml +++ b/src/main/resources/application-security.yml @@ -3,4 +3,11 @@ jwt: refresh-token-secret: ${JWT_REFRESH_TOKEN_SECRET:} access-token-expiration-time: ${JWT_ACCESS_TOKEN_EXPIRATION_TIME:7200} #2시간 refresh-token-expiration-time: ${JWT_REFRESH_TOKEN_EXPIRATION_TIME:604800} #7일 - issuer: ${JWT_ISSUER:} \ No newline at end of file + issuer: ${JWT_ISSUER:} + + +kakao: + login: + client_id: ${KAKAO_LOGIN_CLIENT_ID} + client_secret: ${KAKAO_LOGIN_CLIENT_SECRET} + redirect_uri: ${KAKAO_LOGIN_REDIRECT_URI} \ No newline at end of file From b7ab32c068fa7e01b53109d6dd2d50a88a7d0ea5 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, 30 Mar 2025 14:36:54 +0900 Subject: [PATCH 48/48] =?UTF-8?q?[feat]=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=EC=9D=84=20=EC=9C=84=ED=95=9C=20all?= =?UTF-8?q?owedHeaders=20=EB=AA=85=EC=8B=9C=EC=A0=81=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../moplus/moplus_server/global/config/WebConfig.java | 11 ++++++++++- .../global/config/security/SecurityConfig.java | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) 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 784551f7..89be44fc 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 @@ -26,7 +26,16 @@ public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins(corsAllowedOrigins.toArray(new String[0])) .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") - .allowedHeaders("*") + .allowedHeaders( + "Authorization", + "Content-Type", + "social_access_token", + "X-Requested-With" + ) + .exposedHeaders( + "Authorization", + "Set-Cookie" + ) .allowCredentials(true); } diff --git a/src/main/java/com/moplus/moplus_server/global/config/security/SecurityConfig.java b/src/main/java/com/moplus/moplus_server/global/config/security/SecurityConfig.java index 44a0ee6a..eb5817c6 100644 --- a/src/main/java/com/moplus/moplus_server/global/config/security/SecurityConfig.java +++ b/src/main/java/com/moplus/moplus_server/global/config/security/SecurityConfig.java @@ -120,8 +120,17 @@ public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(corsAllowedOrigins); configuration.addAllowedMethod("*"); - configuration.setAllowedHeaders(List.of("*")); // 허용할 헤더 + configuration.setAllowedHeaders(List.of( + "Authorization", + "Content-Type", + "social_access_token", + "X-Requested-With" + )); configuration.setAllowCredentials(true); + configuration.setExposedHeaders(List.of( + "Authorization", + "Set-Cookie" + )); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); // 모든 경로에 적용