From 07a04e378f16b6b261376ee890141728aad58ab1 Mon Sep 17 00:00:00 2001 From: daeunkwak Date: Fri, 21 Jul 2023 16:30:25 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20querydsl=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ChallengeController.java | 16 ++++++++++++++ .../repository/ChallengeCustomRepository.java | 10 +++++++++ .../ChallengeCustomRepositoryImpl.java | 22 +++++++++++++++++++ .../repository/ChallengeRepository.java | 10 +++++++-- .../challenge/service/ChallengeService.java | 12 ++++++++++ cider-domain/build.gradle | 16 ++++++++++++-- 6 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepository.java create mode 100644 cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepositoryImpl.java diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java b/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java index da2a859..34c7970 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java @@ -8,6 +8,7 @@ import com.cmc.domains.challenge.dto.response.ChallengeCreateResponseDto; import com.cmc.domains.challenge.service.ChallengeService; import com.cmc.domains.image.service.ImageService; +import com.cmc.domains.member.dto.response.MemberResponseDto; import com.cmc.domains.participate.service.ParticipateService; import com.cmc.global.resolver.RequestMemberId; import com.cmc.participate.Participate; @@ -65,6 +66,21 @@ public ResponseEntity createSuccessExampleImages(@Parameter(hidd return ResponseEntity.ok(CommonResponse.from("인증 예시 이미지가 업로드 되었습니다.")); } + @Tag(name = "challenge") + @Operation(summary = "인기 챌린지, 공식 챌린지 조회 api") + @GetMapping("/home") + public ResponseEntity getMe(@Parameter(hidden = true) @RequestMemberId Long memberId) { + + // 로그인 o + challengeService.getPopularChallenges(memberId); + challengeService.getOfficialChallenges(memberId); + + // 로그인 x + + // return ResponseEntity.ok(MemberResponseDto.from(challengeService.find(memberId))); + return null; + } + @Tag(name = "challenge") @Operation(summary = "챌린지 참여하기 api") @PostMapping(value="/participate") diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepository.java b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepository.java new file mode 100644 index 0000000..f2afcc3 --- /dev/null +++ b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepository.java @@ -0,0 +1,10 @@ +package com.cmc.domains.challenge.repository; + +import com.cmc.challenge.Challenge; + +import java.util.List; + +public interface ChallengeCustomRepository { + + //List getPopularChallenges(); +} diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepositoryImpl.java b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepositoryImpl.java new file mode 100644 index 0000000..64a60c6 --- /dev/null +++ b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepositoryImpl.java @@ -0,0 +1,22 @@ +package com.cmc.domains.challenge.repository; + +import com.cmc.challenge.Challenge; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +@Slf4j +public class ChallengeCustomRepositoryImpl implements ChallengeCustomRepository{ + + //private final JPAQueryFactory jpaQueryFactory; + + @Override + public List getPopularChallenges() { + // return jpaQueryFactory.select(challenge) + return null; + } +} diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeRepository.java b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeRepository.java index 7e2ac29..e0b81ca 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeRepository.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeRepository.java @@ -2,10 +2,16 @@ import com.cmc.challenge.Challenge; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; -@Repository -public interface ChallengeRepository extends JpaRepository { +import java.util.List; +@Repository +public interface ChallengeRepository extends JpaRepository, ChallengeCustomRepository { + @Modifying(clearAutomatically = true) + @Query("select c from Challenge c where f.houseworkComplete.houseWorkCompleteId =:houseWorkCompleteId") + List getPopularChallenges(); } diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/service/ChallengeService.java b/cider-api/src/main/java/com/cmc/domains/challenge/service/ChallengeService.java index 33b1912..96e3ac2 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/service/ChallengeService.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/service/ChallengeService.java @@ -7,6 +7,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Service @Transactional @RequiredArgsConstructor @@ -20,4 +22,14 @@ public Challenge create(ChallengeCreateRequestDto req, Long memberId) { Challenge challenge = req.toEntity(); return challengeRepository.save(challenge); } + + // 인기 챌린지 조회 + public void getPopularChallenges(Long memberId) { + + List challenges = challengeRepository.getPopularChallenges(); + } + + // 공식 챌린지 조회 + public void getOfficialChallenges(Long memberId) { + } } diff --git a/cider-domain/build.gradle b/cider-domain/build.gradle index 6330e66..c0ba6e3 100644 --- a/cider-domain/build.gradle +++ b/cider-domain/build.gradle @@ -14,8 +14,20 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' - // jakarta - implementation 'org.hibernate:hibernate-spatial:6.2.2.Final' +// // jakarta +// implementation 'org.hibernate:hibernate-spatial:6.2.2.Final' +// +// // mysql +// implementation 'mysql:mysql-connector-java:8.0.33' +// +// // h2 +// runtimeOnly 'com.h2database:h2' + + // querydsl + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" } test { From 5af4b6c5e12f7cc4bef29e9ddc434aa10aacc325 Mon Sep 17 00:00:00 2001 From: daeunkwak Date: Sun, 23 Jul 2023 04:05:45 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20=EC=B1=8C=EB=A6=B0=EC=A7=80=20?= =?UTF-8?q?=ED=99=88=20api=20-=20=EC=9D=B8=EA=B8=B0,=20=EA=B3=B5=EC=8B=9D?= =?UTF-8?q?=20=EC=B1=8C=EB=A6=B0=EC=A7=80=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cider-api/build.gradle | 6 ++ .../controller/ChallengeController.java | 44 ++++++++++---- .../response/ChallengeHomeResponseDto.java | 30 ++++++++++ .../dto/response/ChallengeResponseDto.java | 60 +++++++++++++++++++ .../repository/ChallengeCustomRepository.java | 6 +- .../ChallengeCustomRepositoryImpl.java | 41 +++++++++++-- .../repository/ChallengeRepository.java | 4 +- .../challenge/service/ChallengeService.java | 9 ++- .../challenge/vo/ChallengeResponseVo.java | 19 ++++++ .../com/cmc/global/config/QuerydslConfig.java | 18 ++++++ cider-domain/build.gradle | 22 ++++--- .../java/com/cmc/challenge/Challenge.java | 12 ++++ .../com/cmc/challenge/constant/Status.java | 24 ++++++++ 13 files changed, 262 insertions(+), 33 deletions(-) create mode 100644 cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeHomeResponseDto.java create mode 100644 cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeResponseDto.java create mode 100644 cider-api/src/main/java/com/cmc/domains/challenge/vo/ChallengeResponseVo.java create mode 100644 cider-api/src/main/java/com/cmc/global/config/QuerydslConfig.java create mode 100644 cider-domain/src/main/java/com/cmc/challenge/constant/Status.java diff --git a/cider-api/build.gradle b/cider-api/build.gradle index c91c671..ff41e2f 100644 --- a/cider-api/build.gradle +++ b/cider-api/build.gradle @@ -52,6 +52,12 @@ dependencies { // s3 implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-aws', version: '2.2.6.RELEASE' + + // querydsl + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'; + annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" } jar{ diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java b/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java index 34c7970..2c5690b 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java @@ -1,31 +1,34 @@ package com.cmc.domains.challenge.controller; import com.cmc.challenge.Challenge; +import com.cmc.challengeLike.ChallengeLike; import com.cmc.common.response.CommonResponse; -import com.cmc.common.response.CreatedResponse; import com.cmc.domains.challenge.dto.request.ChallengeCreateRequestDto; import com.cmc.domains.challenge.dto.request.ChallengeParticipateRequestDto; import com.cmc.domains.challenge.dto.response.ChallengeCreateResponseDto; +import com.cmc.domains.challenge.dto.response.ChallengeHomeResponseDto; +import com.cmc.domains.challenge.dto.response.ChallengeResponseDto; import com.cmc.domains.challenge.service.ChallengeService; +import com.cmc.domains.challenge.vo.ChallengeResponseVo; import com.cmc.domains.image.service.ImageService; -import com.cmc.domains.member.dto.response.MemberResponseDto; import com.cmc.domains.participate.service.ParticipateService; import com.cmc.global.resolver.RequestMemberId; -import com.cmc.participate.Participate; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; import java.util.List; +import java.util.stream.Collectors; @Slf4j @RestController @@ -69,16 +72,35 @@ public ResponseEntity createSuccessExampleImages(@Parameter(hidd @Tag(name = "challenge") @Operation(summary = "인기 챌린지, 공식 챌린지 조회 api") @GetMapping("/home") - public ResponseEntity getMe(@Parameter(hidden = true) @RequestMemberId Long memberId) { + public ResponseEntity getChallengeHome(@Parameter(hidden = true) @RequestMemberId Long memberId) { - // 로그인 o - challengeService.getPopularChallenges(memberId); - challengeService.getOfficialChallenges(memberId); + // 인기 챌린지 + List popularChallengeVos = challengeService.getPopularChallenges(); - // 로그인 x + List popularChallengeResponseDtos = popularChallengeVos.stream().map(vo -> { + return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), + findIsLike(vo.getChallenge(), memberId), ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); + }).toList(); - // return ResponseEntity.ok(MemberResponseDto.from(challengeService.find(memberId))); - return null; + // 공식 챌린지 + List officialChallengeVos = challengeService.getOfficialChallenges(); + + List officialChallengeResponseDtos = officialChallengeVos.stream().map(vo -> { + return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), + findIsLike(vo.getChallenge(), memberId), ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); + }).toList(); + + return ResponseEntity.ok(ChallengeHomeResponseDto.from(popularChallengeResponseDtos, officialChallengeResponseDtos)); + } + + private Boolean findIsLike(Challenge challenge, Long memberId){ + + for(ChallengeLike challengeLike : challenge.getChallengeLikes()){ + if (challengeLike.getMember().getMemberId().equals(memberId)){ + return true; + } + } + return false; } @Tag(name = "challenge") diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeHomeResponseDto.java b/cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeHomeResponseDto.java new file mode 100644 index 0000000..2bf6c44 --- /dev/null +++ b/cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeHomeResponseDto.java @@ -0,0 +1,30 @@ +package com.cmc.domains.challenge.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +import java.util.List; + +@Data +@Builder +@AllArgsConstructor +public class ChallengeHomeResponseDto { + + @Schema(description = "인기 챌린지 리스트") + private List challengeResponseDto; + + @Schema(description = "공식 챌린지 리스트") + private List officialChallengeResponseDto; + + public static ChallengeHomeResponseDto from(List challengeResponseDto, + List officialChallengeResponseDto){ + + return new ChallengeHomeResponseDtoBuilder() + .challengeResponseDto(challengeResponseDto) + .officialChallengeResponseDto(officialChallengeResponseDto) + .build(); + } + +} diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeResponseDto.java b/cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeResponseDto.java new file mode 100644 index 0000000..a1071f2 --- /dev/null +++ b/cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeResponseDto.java @@ -0,0 +1,60 @@ +package com.cmc.domains.challenge.dto.response; + +import com.cmc.challenge.Challenge; +import com.cmc.challenge.constant.Status; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +@AllArgsConstructor +public class ChallengeResponseDto { + + @Schema(description = "챌린지 id", example = "10") + private Long challengeId; + + @Schema(description = "챌린지 제목", example = "소비습관 고치기") + private String challengeName; + + @Schema(description = "챌린지 상태", example = "RECRUITING: 모집중, POSSIBLE: 참여 가능, IMPOSSIBLE: 참여 불가(종료)") + private Status challengeStatus; + + @Schema(description = "챌린지 대기/참여중 인원", example = "5") + private Integer participateNum; + + @Schema(description = "모집중인 경우 - 디데이", example = "23") + private Long recruitLeft; + + @Schema(description = "챌린지 분야", example = "재태크/돈관리/금융학습/소비절약") + private String interestField; + + @Schema(description = "챌린지 진행 기간", example = "4(주)") + private Integer challengePeriod; + + @Schema(description = "공식 챌린지 여부", example = "true") + private Boolean isOfficial; + + @Schema(description = "리워드 여부", example = "true") + private Boolean isReward; + + @Schema(description = "로그인 한 사용자 - 챌린지 좋아요 여부", example = "false") + private Boolean isLike; + + public static ChallengeResponseDto from(Challenge challenge, Integer participateNum, Boolean isLike, Long recruitLeft){ + + return new ChallengeResponseDtoBuilder() + .challengeId(challenge.getChallengeId()) + .challengeName(challenge.getChallengeName()) + .challengeStatus(challenge.getChallengeStatus()) + .participateNum(participateNum) + .recruitLeft(recruitLeft) + .interestField(challenge.getChallengeBranch()) + .isOfficial(challenge.getIsOfficial()) + .isReward(challenge.getIsReward()) + .isLike(isLike) + .build(); + } + +} diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepository.java b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepository.java index f2afcc3..eb8320f 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepository.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepository.java @@ -1,10 +1,12 @@ package com.cmc.domains.challenge.repository; -import com.cmc.challenge.Challenge; +import com.cmc.domains.challenge.vo.ChallengeResponseVo; import java.util.List; public interface ChallengeCustomRepository { - //List getPopularChallenges(); + List getPopularChallenges(); + + List getOfficialChallenges(); } diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepositoryImpl.java b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepositoryImpl.java index 64a60c6..ca153ba 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepositoryImpl.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepositoryImpl.java @@ -1,22 +1,53 @@ package com.cmc.domains.challenge.repository; -import com.cmc.challenge.Challenge; +import com.cmc.challenge.constant.Status; +import com.cmc.domains.challenge.vo.ChallengeResponseVo; +import com.querydsl.core.types.Projections; +import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Repository; import java.util.List; +import static com.cmc.challenge.QChallenge.challenge; +import static com.cmc.participate.QParticipate.participate; + @Repository @RequiredArgsConstructor @Slf4j public class ChallengeCustomRepositoryImpl implements ChallengeCustomRepository{ - //private final JPAQueryFactory jpaQueryFactory; + private final JPAQueryFactory jpaQueryFactory; @Override - public List getPopularChallenges() { - // return jpaQueryFactory.select(challenge) - return null; + public List getPopularChallenges() { + + return jpaQueryFactory.selectDistinct(Projections.fields(ChallengeResponseVo.class, + challenge, + participate.count())) + .from(challenge, participate) + .innerJoin(participate.challenge, challenge) + .where(challenge.challengeStatus.eq(Status.RECRUITING).or(challenge.challengeStatus.eq(Status.POSSIBLE))) + .groupBy(challenge) + .orderBy(participate.count().desc()) + .limit(10) + .fetch(); } + + @Override + public List getOfficialChallenges() { + + return jpaQueryFactory.selectDistinct(Projections.fields(ChallengeResponseVo.class, + challenge, + participate.count())) + .from(challenge, participate) + .innerJoin(participate.challenge, challenge) + .where(challenge.challengeStatus.eq(Status.POSSIBLE).and(challenge.isOfficial.eq(true))) + .groupBy(challenge) + .orderBy(challenge.createdDate.desc()) + .limit(10) + .fetch(); + } + } diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeRepository.java b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeRepository.java index e0b81ca..c02a973 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeRepository.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeRepository.java @@ -1,6 +1,7 @@ package com.cmc.domains.challenge.repository; import com.cmc.challenge.Challenge; +import com.cmc.domains.challenge.vo.ChallengeResponseVo; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -11,7 +12,4 @@ @Repository public interface ChallengeRepository extends JpaRepository, ChallengeCustomRepository { - @Modifying(clearAutomatically = true) - @Query("select c from Challenge c where f.houseworkComplete.houseWorkCompleteId =:houseWorkCompleteId") - List getPopularChallenges(); } diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/service/ChallengeService.java b/cider-api/src/main/java/com/cmc/domains/challenge/service/ChallengeService.java index 96e3ac2..0d0979e 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/service/ChallengeService.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/service/ChallengeService.java @@ -3,6 +3,7 @@ import com.cmc.challenge.Challenge; import com.cmc.domains.challenge.dto.request.ChallengeCreateRequestDto; import com.cmc.domains.challenge.repository.ChallengeRepository; +import com.cmc.domains.challenge.vo.ChallengeResponseVo; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -24,12 +25,14 @@ public Challenge create(ChallengeCreateRequestDto req, Long memberId) { } // 인기 챌린지 조회 - public void getPopularChallenges(Long memberId) { + public List getPopularChallenges() { - List challenges = challengeRepository.getPopularChallenges(); + return challengeRepository.getPopularChallenges(); } // 공식 챌린지 조회 - public void getOfficialChallenges(Long memberId) { + public List getOfficialChallenges() { + + return challengeRepository.getOfficialChallenges(); } } diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/vo/ChallengeResponseVo.java b/cider-api/src/main/java/com/cmc/domains/challenge/vo/ChallengeResponseVo.java new file mode 100644 index 0000000..73b6912 --- /dev/null +++ b/cider-api/src/main/java/com/cmc/domains/challenge/vo/ChallengeResponseVo.java @@ -0,0 +1,19 @@ +package com.cmc.domains.challenge.vo; + +import com.cmc.challenge.Challenge; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ChallengeResponseVo { + + private Challenge challenge; + + private Integer participateNum; + +} diff --git a/cider-api/src/main/java/com/cmc/global/config/QuerydslConfig.java b/cider-api/src/main/java/com/cmc/global/config/QuerydslConfig.java new file mode 100644 index 0000000..d0b9b46 --- /dev/null +++ b/cider-api/src/main/java/com/cmc/global/config/QuerydslConfig.java @@ -0,0 +1,18 @@ +package com.cmc.global.config; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class QuerydslConfig { + @PersistenceContext + private EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory() { + return new JPAQueryFactory(entityManager); + } +} diff --git a/cider-domain/build.gradle b/cider-domain/build.gradle index c0ba6e3..f9fd441 100644 --- a/cider-domain/build.gradle +++ b/cider-domain/build.gradle @@ -14,17 +14,21 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' -// // jakarta -// implementation 'org.hibernate:hibernate-spatial:6.2.2.Final' -// -// // mysql -// implementation 'mysql:mysql-connector-java:8.0.33' -// -// // h2 -// runtimeOnly 'com.h2database:h2' + // jakarta + implementation 'org.hibernate:hibernate-spatial:6.2.2.Final' + + // data jpa + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + + // mysql + implementation 'mysql:mysql-connector-java:8.0.33' + + // h2 + runtimeOnly 'com.h2database:h2' // querydsl - implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + // TODO : 멀티모듈 의존성 정리 + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'; annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" diff --git a/cider-domain/src/main/java/com/cmc/challenge/Challenge.java b/cider-domain/src/main/java/com/cmc/challenge/Challenge.java index 31b5a84..fcc9f62 100644 --- a/cider-domain/src/main/java/com/cmc/challenge/Challenge.java +++ b/cider-domain/src/main/java/com/cmc/challenge/Challenge.java @@ -1,6 +1,7 @@ package com.cmc.challenge; import com.cmc.base.BaseTimeEntity; +import com.cmc.challenge.constant.Status; import com.cmc.challengeLike.ChallengeLike; import com.cmc.image.certifyExample.CertifyExampleImage; import com.cmc.member.Member; @@ -11,6 +12,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.time.LocalDate; import java.util.ArrayList; import java.util.List; @@ -47,14 +49,24 @@ public class Challenge extends BaseTimeEntity { private Integer challengePeriod; + private LocalDate challengeStartDate; + private Integer recruitPeriod; private String certifyMission; private Boolean isPublic; + private Boolean isOfficial; + + private Boolean isReward; + private Integer certifyNum; + @Enumerated(EnumType.STRING) + @Column(name = "challenge_status", columnDefinition = "VARCHAR(30)") + private Status challengeStatus; + @Builder.Default @OneToMany(mappedBy = "challenge", fetch = FetchType.LAZY) private List certifyExampleImageList = new ArrayList<>(); diff --git a/cider-domain/src/main/java/com/cmc/challenge/constant/Status.java b/cider-domain/src/main/java/com/cmc/challenge/constant/Status.java new file mode 100644 index 0000000..19e5a48 --- /dev/null +++ b/cider-domain/src/main/java/com/cmc/challenge/constant/Status.java @@ -0,0 +1,24 @@ +package com.cmc.challenge.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Optional; + +@Getter +@AllArgsConstructor +public enum Status { + + RECRUITING("R", "모집중"), + POSSIBLE("P", "참여 가능"), + IMPOSSIBLE("I", "참여 불가능") + ; + + private String alias; + private String description; + + public static Optional of(String alias) { + return Arrays.stream(values()).filter(S -> S.alias.equals(alias)).findFirst(); + } +} From 328ef1129dfac03f74d087d9cca49f7dc0e7f4c2 Mon Sep 17 00:00:00 2001 From: daeunkwak Date: Sun, 23 Jul 2023 05:21:30 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20=EC=B1=8C=EB=A6=B0=EC=A7=80=20?= =?UTF-8?q?=ED=99=88=20api=20-=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20?= =?UTF-8?q?=EB=B3=84=20=EC=B1=8C=EB=A6=B0=EC=A7=80=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ChallengeController.java | 18 ++++++++++++- .../repository/ChallengeCustomRepository.java | 2 ++ .../ChallengeCustomRepositoryImpl.java | 25 +++++++++++++++++++ .../challenge/service/ChallengeService.java | 6 +++++ 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java b/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java index 2c5690b..3f304a8 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java @@ -70,7 +70,7 @@ public ResponseEntity createSuccessExampleImages(@Parameter(hidd } @Tag(name = "challenge") - @Operation(summary = "인기 챌린지, 공식 챌린지 조회 api") + @Operation(summary = "홈 - 인기 챌린지, 공식 챌린지 조회 api") @GetMapping("/home") public ResponseEntity getChallengeHome(@Parameter(hidden = true) @RequestMemberId Long memberId) { @@ -93,6 +93,22 @@ public ResponseEntity getChallengeHome(@Parameter(hidd return ResponseEntity.ok(ChallengeHomeResponseDto.from(popularChallengeResponseDtos, officialChallengeResponseDtos)); } + @Tag(name = "challenge") + @Operation(summary = "홈 - 카테고리 별 챌린지 조회 api") + @GetMapping("/home/{category}") + public ResponseEntity> getChallengeHomeCategory(@Parameter(hidden = true) @RequestMemberId Long memberId, + @PathVariable("category") String category) { + + List challengeVos = challengeService.getCategoryChallenges(category); + + List challengeResponseDtos = challengeVos.stream().map(vo -> { + return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), + findIsLike(vo.getChallenge(), memberId), ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); + }).toList(); + + return ResponseEntity.ok(challengeResponseDtos); + } + private Boolean findIsLike(Challenge challenge, Long memberId){ for(ChallengeLike challengeLike : challenge.getChallengeLikes()){ diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepository.java b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepository.java index eb8320f..9a0b786 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepository.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepository.java @@ -9,4 +9,6 @@ public interface ChallengeCustomRepository { List getPopularChallenges(); List getOfficialChallenges(); + + List getCategoryChallenges(String category); } diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepositoryImpl.java b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepositoryImpl.java index ca153ba..0130229 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepositoryImpl.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/repository/ChallengeCustomRepositoryImpl.java @@ -1,13 +1,19 @@ package com.cmc.domains.challenge.repository; +import com.cmc.challenge.QChallenge; +import com.cmc.challenge.constant.InterestField; import com.cmc.challenge.constant.Status; import com.cmc.domains.challenge.vo.ChallengeResponseVo; import com.querydsl.core.types.Projections; +import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Repository; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; import java.util.List; import static com.cmc.challenge.QChallenge.challenge; @@ -50,4 +56,23 @@ public List getOfficialChallenges() { .fetch(); } + @Override + public List getCategoryChallenges(String category) { + + return jpaQueryFactory.selectDistinct(Projections.fields(ChallengeResponseVo.class, + challenge, + participate.count())) + .from(challenge, participate) + .innerJoin(participate.challenge, challenge) + .where(challenge.challengeStatus.eq(Status.RECRUITING).or(challenge.challengeStatus.eq(Status.POSSIBLE)) + .and(challenge.challengeBranch.eq(category))) + .groupBy(challenge) + .fetch(); + } + + private Long getDateBetween(QChallenge challenge){ + + return ChronoUnit.DAYS.between((Temporal) challenge.challengeStartDate, LocalDate.now()); + } + } diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/service/ChallengeService.java b/cider-api/src/main/java/com/cmc/domains/challenge/service/ChallengeService.java index 0d0979e..f809d3a 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/service/ChallengeService.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/service/ChallengeService.java @@ -35,4 +35,10 @@ public List getOfficialChallenges() { return challengeRepository.getOfficialChallenges(); } + + // 카테고리 별 챌린지 조회 + public List getCategoryChallenges(String category) { + + return challengeRepository.getCategoryChallenges(category); + } } From b3128e9bdd7e2575c5c0dfeeccbbcf243303be7a Mon Sep 17 00:00:00 2001 From: daeunkwak Date: Sun, 23 Jul 2023 17:28:34 +0900 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20=EC=B1=8C=EB=A6=B0=EC=A7=80=20?= =?UTF-8?q?=ED=99=88=20api=20-=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=B6=84?= =?UTF-8?q?=EA=B8=B0=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ChallengeController.java | 69 ++++++++++++++----- .../dto/response/ChallengeResponseDto.java | 15 ++++ 2 files changed, 68 insertions(+), 16 deletions(-) diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java b/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java index 3f304a8..defea3d 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java @@ -2,6 +2,7 @@ import com.cmc.challenge.Challenge; import com.cmc.challengeLike.ChallengeLike; +import com.cmc.common.exception.BadRequestException; import com.cmc.common.response.CommonResponse; import com.cmc.domains.challenge.dto.request.ChallengeCreateRequestDto; import com.cmc.domains.challenge.dto.request.ChallengeParticipateRequestDto; @@ -13,12 +14,15 @@ import com.cmc.domains.image.service.ImageService; import com.cmc.domains.participate.service.ParticipateService; import com.cmc.global.resolver.RequestMemberId; +import com.cmc.oauth.service.TokenProvider; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -27,6 +31,7 @@ import java.io.IOException; import java.time.LocalDate; import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -72,23 +77,44 @@ public ResponseEntity createSuccessExampleImages(@Parameter(hidd @Tag(name = "challenge") @Operation(summary = "홈 - 인기 챌린지, 공식 챌린지 조회 api") @GetMapping("/home") - public ResponseEntity getChallengeHome(@Parameter(hidden = true) @RequestMemberId Long memberId) { + public ResponseEntity getChallengeHome(HttpServletRequest httpServletRequest) { + + final String tokenString = httpServletRequest.getHeader(HttpHeaders.AUTHORIZATION); // 인기 챌린지 List popularChallengeVos = challengeService.getPopularChallenges(); + List popularChallengeResponseDtos = new ArrayList<>(); + if (tokenString == null || tokenString.isEmpty()) { + // 로그인 x + popularChallengeResponseDtos = popularChallengeVos.stream().map(vo -> { + return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), + ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); + }).toList(); + } else{ + // 로그인 o + popularChallengeResponseDtos = popularChallengeVos.stream().map(vo -> { + return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), + findIsLike(vo.getChallenge(), TokenProvider.getMemberId(tokenString)), ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); + }).toList(); + } - List popularChallengeResponseDtos = popularChallengeVos.stream().map(vo -> { - return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), - findIsLike(vo.getChallenge(), memberId), ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); - }).toList(); // 공식 챌린지 List officialChallengeVos = challengeService.getOfficialChallenges(); - - List officialChallengeResponseDtos = officialChallengeVos.stream().map(vo -> { - return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), - findIsLike(vo.getChallenge(), memberId), ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); - }).toList(); + List officialChallengeResponseDtos = new ArrayList<>(); + if (tokenString == null || tokenString.isEmpty()) { + // 로그인 x + officialChallengeResponseDtos = officialChallengeVos.stream().map(vo -> { + return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), + ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); + }).toList(); + } else{ + // 로그인 o + officialChallengeResponseDtos = officialChallengeVos.stream().map(vo -> { + return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), + findIsLike(vo.getChallenge(), TokenProvider.getMemberId(tokenString)), ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); + }).toList(); + } return ResponseEntity.ok(ChallengeHomeResponseDto.from(popularChallengeResponseDtos, officialChallengeResponseDtos)); } @@ -96,15 +122,26 @@ public ResponseEntity getChallengeHome(@Parameter(hidd @Tag(name = "challenge") @Operation(summary = "홈 - 카테고리 별 챌린지 조회 api") @GetMapping("/home/{category}") - public ResponseEntity> getChallengeHomeCategory(@Parameter(hidden = true) @RequestMemberId Long memberId, + public ResponseEntity> getChallengeHomeCategory(HttpServletRequest httpServletRequest, @PathVariable("category") String category) { - List challengeVos = challengeService.getCategoryChallenges(category); + final String tokenString = httpServletRequest.getHeader(HttpHeaders.AUTHORIZATION); - List challengeResponseDtos = challengeVos.stream().map(vo -> { - return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), - findIsLike(vo.getChallenge(), memberId), ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); - }).toList(); + List challengeVos = challengeService.getCategoryChallenges(category); + List challengeResponseDtos = new ArrayList<>(); + if (tokenString == null || tokenString.isEmpty()) { + // 로그인 x + challengeResponseDtos = challengeVos.stream().map(vo -> { + return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), + ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); + }).toList(); + } else{ + // 로그인 o + challengeResponseDtos = challengeVos.stream().map(vo -> { + return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), + findIsLike(vo.getChallenge(), TokenProvider.getMemberId(tokenString)), ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); + }).toList(); + } return ResponseEntity.ok(challengeResponseDtos); } diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeResponseDto.java b/cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeResponseDto.java index a1071f2..b15424f 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeResponseDto.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeResponseDto.java @@ -57,4 +57,19 @@ public static ChallengeResponseDto from(Challenge challenge, Integer participate .build(); } + public static ChallengeResponseDto from(Challenge challenge, Integer participateNum, Long recruitLeft){ + + return new ChallengeResponseDtoBuilder() + .challengeId(challenge.getChallengeId()) + .challengeName(challenge.getChallengeName()) + .challengeStatus(challenge.getChallengeStatus()) + .participateNum(participateNum) + .recruitLeft(recruitLeft) + .interestField(challenge.getChallengeBranch()) + .isOfficial(challenge.getIsOfficial()) + .isReward(challenge.getIsReward()) + .isLike(false) + .build(); + } + } From e297b174e119fe49b6894b5beceeb0253e20b0cf Mon Sep 17 00:00:00 2001 From: daeunkwak Date: Sun, 23 Jul 2023 17:38:46 +0900 Subject: [PATCH 5/5] =?UTF-8?q?refactor:=20=EC=B1=8C=EB=A6=B0=EC=A7=80=20?= =?UTF-8?q?=ED=99=88=20api=20-=20=EB=A9=94=EC=84=9C=EB=93=9C=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/ChallengeController.java | 39 +++++-------------- .../dto/response/ChallengeResponseDto.java | 2 +- 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java b/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java index defea3d..5d6d712 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/controller/ChallengeController.java @@ -83,38 +83,11 @@ public ResponseEntity getChallengeHome(HttpServletRequ // 인기 챌린지 List popularChallengeVos = challengeService.getPopularChallenges(); - List popularChallengeResponseDtos = new ArrayList<>(); - if (tokenString == null || tokenString.isEmpty()) { - // 로그인 x - popularChallengeResponseDtos = popularChallengeVos.stream().map(vo -> { - return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), - ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); - }).toList(); - } else{ - // 로그인 o - popularChallengeResponseDtos = popularChallengeVos.stream().map(vo -> { - return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), - findIsLike(vo.getChallenge(), TokenProvider.getMemberId(tokenString)), ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); - }).toList(); - } - + List popularChallengeResponseDtos = makeChallengeResponseDto(tokenString, popularChallengeVos); // 공식 챌린지 List officialChallengeVos = challengeService.getOfficialChallenges(); - List officialChallengeResponseDtos = new ArrayList<>(); - if (tokenString == null || tokenString.isEmpty()) { - // 로그인 x - officialChallengeResponseDtos = officialChallengeVos.stream().map(vo -> { - return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), - ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); - }).toList(); - } else{ - // 로그인 o - officialChallengeResponseDtos = officialChallengeVos.stream().map(vo -> { - return ChallengeResponseDto.from(vo.getChallenge(), vo.getParticipateNum(), - findIsLike(vo.getChallenge(), TokenProvider.getMemberId(tokenString)), ChronoUnit.DAYS.between(LocalDate.now(), vo.getChallenge().getChallengeStartDate())); - }).toList(); - } + List officialChallengeResponseDtos = makeChallengeResponseDto(tokenString, officialChallengeVos); return ResponseEntity.ok(ChallengeHomeResponseDto.from(popularChallengeResponseDtos, officialChallengeResponseDtos)); } @@ -128,6 +101,12 @@ public ResponseEntity> getChallengeHomeCategory(HttpS final String tokenString = httpServletRequest.getHeader(HttpHeaders.AUTHORIZATION); List challengeVos = challengeService.getCategoryChallenges(category); + List challengeResponseDtos = makeChallengeResponseDto(tokenString, challengeVos); + return ResponseEntity.ok(challengeResponseDtos); + } + + private List makeChallengeResponseDto(String tokenString, List challengeVos){ + List challengeResponseDtos = new ArrayList<>(); if (tokenString == null || tokenString.isEmpty()) { // 로그인 x @@ -143,7 +122,7 @@ public ResponseEntity> getChallengeHomeCategory(HttpS }).toList(); } - return ResponseEntity.ok(challengeResponseDtos); + return challengeResponseDtos; } private Boolean findIsLike(Challenge challenge, Long memberId){ diff --git a/cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeResponseDto.java b/cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeResponseDto.java index b15424f..a9be217 100644 --- a/cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeResponseDto.java +++ b/cider-api/src/main/java/com/cmc/domains/challenge/dto/response/ChallengeResponseDto.java @@ -30,7 +30,7 @@ public class ChallengeResponseDto { @Schema(description = "챌린지 분야", example = "재태크/돈관리/금융학습/소비절약") private String interestField; - @Schema(description = "챌린지 진행 기간", example = "4(주)") + @Schema(description = "챌린지 진행 기간", example = "4") private Integer challengePeriod; @Schema(description = "공식 챌린지 여부", example = "true")