From 98b78b8dc3b63301e3537402d7fa1ff503900ad6 Mon Sep 17 00:00:00 2001 From: songhyeonpk Date: Mon, 14 Apr 2025 16:56:21 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EC=9C=A0=EC=A0=80=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=A8=EB=B0=8D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9D=B4?= =?UTF-8?q?=EB=A0=A5=20=EB=AA=A9=EB=A1=9D=20=EB=B0=8F=20=EA=B7=B8=EB=A3=A8?= =?UTF-8?q?=EB=B0=8D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9D=B4=EB=A0=A5=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=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 --- .../LoadGroomingTestHistoryController.java | 35 +++++ ...adGroomingTestHistoryDetailController.java | 40 +++++ .../GroomingTestHistoryDetailResponse.java | 26 ++++ .../response/GroomingTestHistoryResponse.java | 19 +++ .../GroomingDomainPersistenceAdapter.java | 15 ++ .../GroomingTestResultCustomRepository.java | 8 + ...roomingTestResultCustomRepositoryImpl.java | 33 ++++ .../LoadGroomingTestHistoryDetailUseCase.java | 11 ++ .../LoadGroomingTestHistoryUseCase.java | 11 ++ .../grooming/LoadGroomingTestResultPort.java | 8 + ...ingTestResultByUserIdAndTestedAtQuery.java | 21 +++ .../LoadGroomingTestHistoryDetailService.java | 144 ++++++++++++++++++ .../LoadGroomingTestHistoryService.java | 24 +++ .../grooming/GroomingTestHistoryDetailVo.java | 34 +++++ .../vo/grooming/GroomingTestHistoryVo.java | 24 +++ 15 files changed, 453 insertions(+) create mode 100644 src/main/java/com/ftm/server/adapter/in/web/grooming/controller/LoadGroomingTestHistoryController.java create mode 100644 src/main/java/com/ftm/server/adapter/in/web/grooming/controller/LoadGroomingTestHistoryDetailController.java create mode 100644 src/main/java/com/ftm/server/adapter/in/web/grooming/dto/response/GroomingTestHistoryDetailResponse.java create mode 100644 src/main/java/com/ftm/server/adapter/in/web/grooming/dto/response/GroomingTestHistoryResponse.java create mode 100644 src/main/java/com/ftm/server/application/port/in/grooming/LoadGroomingTestHistoryDetailUseCase.java create mode 100644 src/main/java/com/ftm/server/application/port/in/grooming/LoadGroomingTestHistoryUseCase.java create mode 100644 src/main/java/com/ftm/server/application/query/FindGroomingTestResultByUserIdAndTestedAtQuery.java create mode 100644 src/main/java/com/ftm/server/application/service/grooming/LoadGroomingTestHistoryDetailService.java create mode 100644 src/main/java/com/ftm/server/application/service/grooming/LoadGroomingTestHistoryService.java create mode 100644 src/main/java/com/ftm/server/application/vo/grooming/GroomingTestHistoryDetailVo.java create mode 100644 src/main/java/com/ftm/server/application/vo/grooming/GroomingTestHistoryVo.java diff --git a/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/LoadGroomingTestHistoryController.java b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/LoadGroomingTestHistoryController.java new file mode 100644 index 0000000..f6e2658 --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/LoadGroomingTestHistoryController.java @@ -0,0 +1,35 @@ +package com.ftm.server.adapter.in.web.grooming.controller; + +import com.ftm.server.adapter.in.web.grooming.dto.response.GroomingTestHistoryResponse; +import com.ftm.server.application.port.in.grooming.LoadGroomingTestHistoryUseCase; +import com.ftm.server.application.query.FindByUserIdQuery; +import com.ftm.server.application.vo.grooming.GroomingTestHistoryVo; +import com.ftm.server.common.response.ApiResponse; +import com.ftm.server.common.response.enums.SuccessResponseCode; +import com.ftm.server.infrastructure.security.UserPrincipal; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class LoadGroomingTestHistoryController { + + private final LoadGroomingTestHistoryUseCase loadGroomingTestsHistoryUseCase; + + @GetMapping("/api/grooming/tests/histories") + public ResponseEntity> loadHistories( + @AuthenticationPrincipal UserPrincipal userPrincipal) { + GroomingTestHistoryVo vo = + loadGroomingTestsHistoryUseCase.execute( + FindByUserIdQuery.of(userPrincipal.getId())); + + return ResponseEntity.status(HttpStatus.OK) + .body( + ApiResponse.success( + SuccessResponseCode.OK, GroomingTestHistoryResponse.of(vo))); + } +} diff --git a/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/LoadGroomingTestHistoryDetailController.java b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/LoadGroomingTestHistoryDetailController.java new file mode 100644 index 0000000..c2342fe --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/in/web/grooming/controller/LoadGroomingTestHistoryDetailController.java @@ -0,0 +1,40 @@ +package com.ftm.server.adapter.in.web.grooming.controller; + +import com.ftm.server.adapter.in.web.grooming.dto.response.GroomingTestHistoryDetailResponse; +import com.ftm.server.application.port.in.grooming.LoadGroomingTestHistoryDetailUseCase; +import com.ftm.server.application.query.FindGroomingTestResultByUserIdAndTestedAtQuery; +import com.ftm.server.application.vo.grooming.GroomingTestHistoryDetailVo; +import com.ftm.server.common.response.ApiResponse; +import com.ftm.server.common.response.enums.SuccessResponseCode; +import com.ftm.server.infrastructure.security.UserPrincipal; +import java.time.LocalDate; +import lombok.RequiredArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +public class LoadGroomingTestHistoryDetailController { + + private final LoadGroomingTestHistoryDetailUseCase loadGroomingTestHistoryDetailUseCase; + + @GetMapping("/api/grooming/tests/histories/detail") + public ResponseEntity> loadHistoryDetail( + @RequestParam("date") @DateTimeFormat(pattern = "yyyy.MM.dd") LocalDate date, + @AuthenticationPrincipal UserPrincipal userPrincipal) { + GroomingTestHistoryDetailVo vo = + loadGroomingTestHistoryDetailUseCase.execute( + FindGroomingTestResultByUserIdAndTestedAtQuery.of( + userPrincipal.getId(), date)); + return ResponseEntity.status(HttpStatus.OK) + .body( + ApiResponse.success( + SuccessResponseCode.OK, + GroomingTestHistoryDetailResponse.from(vo))); + } +} diff --git a/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/response/GroomingTestHistoryDetailResponse.java b/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/response/GroomingTestHistoryDetailResponse.java new file mode 100644 index 0000000..b46e8b3 --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/response/GroomingTestHistoryDetailResponse.java @@ -0,0 +1,26 @@ +package com.ftm.server.adapter.in.web.grooming.dto.response; + +import com.ftm.server.application.vo.grooming.*; +import lombok.Getter; + +@Getter +public class GroomingTestHistoryDetailResponse { + + private final String testedAt; + private final GroomingTestResultScoresVo scores; + private final GroomingTestResultGradesVo grades; + private final GroomingLevelVo level; + + private GroomingTestHistoryDetailResponse( + GroomingTestHistoryDetailVo groomingTestHistoryDetailVo) { + this.testedAt = groomingTestHistoryDetailVo.getTestedAt(); + this.scores = groomingTestHistoryDetailVo.getScores(); + this.grades = groomingTestHistoryDetailVo.getGrades(); + this.level = groomingTestHistoryDetailVo.getLevel(); + } + + public static GroomingTestHistoryDetailResponse from( + GroomingTestHistoryDetailVo groomingTestHistoryDetailVo) { + return new GroomingTestHistoryDetailResponse(groomingTestHistoryDetailVo); + } +} diff --git a/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/response/GroomingTestHistoryResponse.java b/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/response/GroomingTestHistoryResponse.java new file mode 100644 index 0000000..4bb5994 --- /dev/null +++ b/src/main/java/com/ftm/server/adapter/in/web/grooming/dto/response/GroomingTestHistoryResponse.java @@ -0,0 +1,19 @@ +package com.ftm.server.adapter.in.web.grooming.dto.response; + +import com.ftm.server.application.vo.grooming.GroomingTestHistoryVo; +import java.util.List; +import lombok.Getter; + +@Getter +public class GroomingTestHistoryResponse { + + private final List historyDates; + + private GroomingTestHistoryResponse(GroomingTestHistoryVo groomingTestHistoryVo) { + this.historyDates = groomingTestHistoryVo.getHistoryDates(); + } + + public static GroomingTestHistoryResponse of(GroomingTestHistoryVo groomingTestHistoryVo) { + return new GroomingTestHistoryResponse(groomingTestHistoryVo); + } +} diff --git a/src/main/java/com/ftm/server/adapter/out/persistence/adapter/grooming/GroomingDomainPersistenceAdapter.java b/src/main/java/com/ftm/server/adapter/out/persistence/adapter/grooming/GroomingDomainPersistenceAdapter.java index d6a83d5..9e258cd 100644 --- a/src/main/java/com/ftm/server/adapter/out/persistence/adapter/grooming/GroomingDomainPersistenceAdapter.java +++ b/src/main/java/com/ftm/server/adapter/out/persistence/adapter/grooming/GroomingDomainPersistenceAdapter.java @@ -7,6 +7,7 @@ import com.ftm.server.application.query.FIndGroomingLevelByScoreQuery; import com.ftm.server.application.query.FindByIdQuery; import com.ftm.server.application.query.FindByUserIdQuery; +import com.ftm.server.application.query.FindGroomingTestResultByUserIdAndTestedAtQuery; import com.ftm.server.common.annotation.Adapter; import com.ftm.server.common.exception.CustomException; import com.ftm.server.common.response.enums.ErrorResponseCode; @@ -121,4 +122,18 @@ public void updateUserGroomingStatus(User user) { public LocalDateTime loadLatestTestedAtByUserId(FindByUserIdQuery query) { return groomingTestResultRepository.fetchLatestTestedAtByUserId(query); } + + @Override + public List loadRecentTestedAtListByUserId(FindByUserIdQuery query) { + return groomingTestResultRepository.fetchRecentTestedAtListByUserId(query); + } + + @Override + public List loadByUserIdAndTestedAt( + FindGroomingTestResultByUserIdAndTestedAtQuery query) { + List results = + groomingTestResultRepository.loadByUserIdAndTestedAt(query); + + return results.stream().map(groomingTestResultMapper::toDomainEntity).toList(); + } } diff --git a/src/main/java/com/ftm/server/adapter/out/persistence/repository/GroomingTestResultCustomRepository.java b/src/main/java/com/ftm/server/adapter/out/persistence/repository/GroomingTestResultCustomRepository.java index 514004a..72c4286 100644 --- a/src/main/java/com/ftm/server/adapter/out/persistence/repository/GroomingTestResultCustomRepository.java +++ b/src/main/java/com/ftm/server/adapter/out/persistence/repository/GroomingTestResultCustomRepository.java @@ -1,9 +1,17 @@ package com.ftm.server.adapter.out.persistence.repository; +import com.ftm.server.adapter.out.persistence.model.GroomingTestResultJpaEntity; import com.ftm.server.application.query.FindByUserIdQuery; +import com.ftm.server.application.query.FindGroomingTestResultByUserIdAndTestedAtQuery; import java.time.LocalDateTime; +import java.util.List; public interface GroomingTestResultCustomRepository { LocalDateTime fetchLatestTestedAtByUserId(FindByUserIdQuery query); + + List fetchRecentTestedAtListByUserId(FindByUserIdQuery query); + + List loadByUserIdAndTestedAt( + FindGroomingTestResultByUserIdAndTestedAtQuery query); } diff --git a/src/main/java/com/ftm/server/adapter/out/persistence/repository/GroomingTestResultCustomRepositoryImpl.java b/src/main/java/com/ftm/server/adapter/out/persistence/repository/GroomingTestResultCustomRepositoryImpl.java index c254c49..48d790d 100644 --- a/src/main/java/com/ftm/server/adapter/out/persistence/repository/GroomingTestResultCustomRepositoryImpl.java +++ b/src/main/java/com/ftm/server/adapter/out/persistence/repository/GroomingTestResultCustomRepositoryImpl.java @@ -2,9 +2,13 @@ import static com.ftm.server.adapter.out.persistence.model.QGroomingTestResultJpaEntity.groomingTestResultJpaEntity; +import com.ftm.server.adapter.out.persistence.model.GroomingTestResultJpaEntity; import com.ftm.server.application.query.FindByUserIdQuery; +import com.ftm.server.application.query.FindGroomingTestResultByUserIdAndTestedAtQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -22,4 +26,33 @@ public LocalDateTime fetchLatestTestedAtByUserId(FindByUserIdQuery query) { .where(groomingTestResultJpaEntity.user.id.eq(query.getUserId())) .fetchOne(); } + + @Override + public List fetchRecentTestedAtListByUserId(FindByUserIdQuery query) { + return queryFactory + .select(groomingTestResultJpaEntity.testedAt) + .distinct() + .from(groomingTestResultJpaEntity) + .where(groomingTestResultJpaEntity.user.id.eq(query.getUserId())) + .orderBy(groomingTestResultJpaEntity.testedAt.desc()) + .limit(5) + .fetch(); + } + + @Override + public List loadByUserIdAndTestedAt( + FindGroomingTestResultByUserIdAndTestedAtQuery query) { + LocalDateTime start = query.getTestedAt().atStartOfDay(); + LocalDateTime end = query.getTestedAt().atTime(LocalTime.MAX); + + return queryFactory + .selectFrom(groomingTestResultJpaEntity) + .where( + groomingTestResultJpaEntity + .user + .id + .eq(query.getUserId()) + .and(groomingTestResultJpaEntity.testedAt.between(start, end))) + .fetch(); + } } diff --git a/src/main/java/com/ftm/server/application/port/in/grooming/LoadGroomingTestHistoryDetailUseCase.java b/src/main/java/com/ftm/server/application/port/in/grooming/LoadGroomingTestHistoryDetailUseCase.java new file mode 100644 index 0000000..6868871 --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/in/grooming/LoadGroomingTestHistoryDetailUseCase.java @@ -0,0 +1,11 @@ +package com.ftm.server.application.port.in.grooming; + +import com.ftm.server.application.query.FindGroomingTestResultByUserIdAndTestedAtQuery; +import com.ftm.server.application.vo.grooming.GroomingTestHistoryDetailVo; +import com.ftm.server.common.annotation.UseCase; + +@UseCase +public interface LoadGroomingTestHistoryDetailUseCase { + + GroomingTestHistoryDetailVo execute(FindGroomingTestResultByUserIdAndTestedAtQuery query); +} diff --git a/src/main/java/com/ftm/server/application/port/in/grooming/LoadGroomingTestHistoryUseCase.java b/src/main/java/com/ftm/server/application/port/in/grooming/LoadGroomingTestHistoryUseCase.java new file mode 100644 index 0000000..04e5492 --- /dev/null +++ b/src/main/java/com/ftm/server/application/port/in/grooming/LoadGroomingTestHistoryUseCase.java @@ -0,0 +1,11 @@ +package com.ftm.server.application.port.in.grooming; + +import com.ftm.server.application.query.FindByUserIdQuery; +import com.ftm.server.application.vo.grooming.GroomingTestHistoryVo; +import com.ftm.server.common.annotation.UseCase; + +@UseCase +public interface LoadGroomingTestHistoryUseCase { + + GroomingTestHistoryVo execute(FindByUserIdQuery query); +} diff --git a/src/main/java/com/ftm/server/application/port/out/persistence/grooming/LoadGroomingTestResultPort.java b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/LoadGroomingTestResultPort.java index 12fa119..93c1c6b 100644 --- a/src/main/java/com/ftm/server/application/port/out/persistence/grooming/LoadGroomingTestResultPort.java +++ b/src/main/java/com/ftm/server/application/port/out/persistence/grooming/LoadGroomingTestResultPort.java @@ -1,11 +1,19 @@ package com.ftm.server.application.port.out.persistence.grooming; import com.ftm.server.application.query.FindByUserIdQuery; +import com.ftm.server.application.query.FindGroomingTestResultByUserIdAndTestedAtQuery; import com.ftm.server.common.annotation.Port; +import com.ftm.server.domain.entity.GroomingTestResult; import java.time.LocalDateTime; +import java.util.List; @Port public interface LoadGroomingTestResultPort { LocalDateTime loadLatestTestedAtByUserId(FindByUserIdQuery query); + + List loadRecentTestedAtListByUserId(FindByUserIdQuery query); + + List loadByUserIdAndTestedAt( + FindGroomingTestResultByUserIdAndTestedAtQuery query); } diff --git a/src/main/java/com/ftm/server/application/query/FindGroomingTestResultByUserIdAndTestedAtQuery.java b/src/main/java/com/ftm/server/application/query/FindGroomingTestResultByUserIdAndTestedAtQuery.java new file mode 100644 index 0000000..d098de3 --- /dev/null +++ b/src/main/java/com/ftm/server/application/query/FindGroomingTestResultByUserIdAndTestedAtQuery.java @@ -0,0 +1,21 @@ +package com.ftm.server.application.query; + +import java.time.LocalDate; +import lombok.Getter; + +@Getter +public class FindGroomingTestResultByUserIdAndTestedAtQuery { + + private final Long userId; + private final LocalDate testedAt; + + private FindGroomingTestResultByUserIdAndTestedAtQuery(Long userId, LocalDate testedAt) { + this.userId = userId; + this.testedAt = testedAt; + } + + public static FindGroomingTestResultByUserIdAndTestedAtQuery of( + Long userId, LocalDate testedAt) { + return new FindGroomingTestResultByUserIdAndTestedAtQuery(userId, testedAt); + } +} diff --git a/src/main/java/com/ftm/server/application/service/grooming/LoadGroomingTestHistoryDetailService.java b/src/main/java/com/ftm/server/application/service/grooming/LoadGroomingTestHistoryDetailService.java new file mode 100644 index 0000000..065d419 --- /dev/null +++ b/src/main/java/com/ftm/server/application/service/grooming/LoadGroomingTestHistoryDetailService.java @@ -0,0 +1,144 @@ +package com.ftm.server.application.service.grooming; + +import static com.ftm.server.common.consts.StaticConsts.*; + +import com.ftm.server.application.port.in.grooming.LoadGroomingTestHistoryDetailUseCase; +import com.ftm.server.application.port.out.cache.LoadGroomingTestsWithCachePort; +import com.ftm.server.application.port.out.persistence.grooming.LoadGroomingLevelPort; +import com.ftm.server.application.port.out.persistence.grooming.LoadGroomingTestResultPort; +import com.ftm.server.application.query.FIndGroomingLevelByScoreQuery; +import com.ftm.server.application.query.FindGroomingTestResultByUserIdAndTestedAtQuery; +import com.ftm.server.application.vo.grooming.*; +import com.ftm.server.common.exception.CustomException; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.domain.entity.GroomingLevel; +import com.ftm.server.domain.entity.GroomingTestResult; +import com.ftm.server.domain.enums.GroomingCategory; +import com.ftm.server.domain.enums.GroomingCategoryGrade; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class LoadGroomingTestHistoryDetailService implements LoadGroomingTestHistoryDetailUseCase { + + private final LoadGroomingTestResultPort loadGroomingTestResultPort; + private final LoadGroomingTestsWithCachePort loadGroomingTestsWithCachePort; + private final LoadGroomingLevelPort loadGroomingLevelPort; + + @Override + public GroomingTestHistoryDetailVo execute( + FindGroomingTestResultByUserIdAndTestedAtQuery query) { + // 특정 날짜의 그루밍 테스트 결과 목록 조회 + // 질문 ID → 선택한 답변 ID 목록으로 매핑 + List histories = + loadGroomingTestResultPort.loadByUserIdAndTestedAt(query); + Map> userAnswerMap = + histories.stream() + .collect( + Collectors.groupingBy( + GroomingTestResult::getGroomingTestQuestionId, + Collectors.mapping( + GroomingTestResult::getGroomingTestAnswerId, + Collectors.toList()))); + + // 그루밍 테스트 계산 (점수, 등급, 레벨) + GroomingTestResultScoresVo scores = calculateScores(userAnswerMap); + GroomingTestResultGradesVo grades = calculateGrades(scores); + GroomingLevel groomingLevel = + loadGroomingLevelPort + .loadGroomingLevelByScore( + FIndGroomingLevelByScoreQuery.of(scores.getTotalScore())) + .orElseThrow( + () -> + new CustomException( + ErrorResponseCode.GROOMING_LEVEL_NOT_FOUND)); + GroomingLevelVo level = GroomingLevelVo.from(groomingLevel); + + return GroomingTestHistoryDetailVo.from(query.getTestedAt(), scores, grades, level); + } + + // 점수 계산 + private GroomingTestResultScoresVo calculateScores(Map> userAnswerMap) { + // 캐싱된 그루밍 테스트 목록 조회 + List infos = + loadGroomingTestsWithCachePort.loadGroomingTestsCache(); + Map questionMap = + infos.stream() + .collect( + Collectors.toMap( + GroomingTestQuestionWithAnswersVo + ::getGroomingTestQuestionId, + Function.identity())); + + // 각 질문별 답변:점수 정보를 관리하는 map + Map> scoreMap = toScoreMap(infos); + + // 그루밍 카테고리별 점수를 관리하는 map + Map categoryScores = new EnumMap<>(GroomingCategory.class); + + // 그루밍 카테고리별 점수 계산 + for (Map.Entry> entry : userAnswerMap.entrySet()) { + Long questionId = entry.getKey(); + + GroomingCategory category = questionMap.get(questionId).getGroomingCategory(); + Map answersMap = scoreMap.get(questionId); + + int score = + entry.getValue().stream() + .mapToInt(ans -> answersMap.getOrDefault(ans, 0)) + .sum(); + + categoryScores.merge(category, score, Integer::sum); + } + + return GroomingTestResultScoresVo.of( + categoryScores.getOrDefault(GroomingCategory.BEAUTY, 0), + categoryScores.getOrDefault(GroomingCategory.HYGIENE, 0), + categoryScores.getOrDefault(GroomingCategory.HAIR, 0), + categoryScores.getOrDefault(GroomingCategory.WORKOUT, 0), + categoryScores.getOrDefault(GroomingCategory.FASHION, 0), + categoryScores.values().stream().mapToInt(Integer::intValue).sum()); + } + + private Map> toScoreMap( + List infos) { + return infos.stream() + .collect( + Collectors.toMap( + GroomingTestQuestionWithAnswersVo::getGroomingTestQuestionId, + question -> + question.getAnswers().stream() + .collect( + Collectors.toMap( + GroomingTestAnswerInfoVo + ::getGroomingTestAnswerId, + GroomingTestAnswerInfoVo + ::getScore)))); + } + + // 등급 계산 + private GroomingTestResultGradesVo calculateGrades(GroomingTestResultScoresVo scores) { + return GroomingTestResultGradesVo.from( + GroomingCategoryGradeInfoVo.from( + GroomingCategoryGrade.fromScore( + scores.getBeautyScore(), BEAUTY_CATEGORY_MAX_SCORE)), + GroomingCategoryGradeInfoVo.from( + GroomingCategoryGrade.fromScore( + scores.getHygieneScore(), HYGIENE_CATEGORY_MAX_SCORE)), + GroomingCategoryGradeInfoVo.from( + GroomingCategoryGrade.fromScore( + scores.getHairScore(), HAIR_CATEGORY_MAX_SCORE)), + GroomingCategoryGradeInfoVo.from( + GroomingCategoryGrade.fromScore( + scores.getWorkoutScore(), WORKOUT_CATEGORY_MAX_SCORE)), + GroomingCategoryGradeInfoVo.from( + GroomingCategoryGrade.fromScore( + scores.getFashionScore(), FASHION_CATEGORY_MAX_SCORE))); + } +} diff --git a/src/main/java/com/ftm/server/application/service/grooming/LoadGroomingTestHistoryService.java b/src/main/java/com/ftm/server/application/service/grooming/LoadGroomingTestHistoryService.java new file mode 100644 index 0000000..0c8e23f --- /dev/null +++ b/src/main/java/com/ftm/server/application/service/grooming/LoadGroomingTestHistoryService.java @@ -0,0 +1,24 @@ +package com.ftm.server.application.service.grooming; + +import com.ftm.server.application.port.in.grooming.LoadGroomingTestHistoryUseCase; +import com.ftm.server.application.port.out.persistence.grooming.LoadGroomingTestResultPort; +import com.ftm.server.application.query.FindByUserIdQuery; +import com.ftm.server.application.vo.grooming.GroomingTestHistoryVo; +import java.time.LocalDateTime; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class LoadGroomingTestHistoryService implements LoadGroomingTestHistoryUseCase { + + private final LoadGroomingTestResultPort loadGroomingTestResultPort; + + @Override + public GroomingTestHistoryVo execute(FindByUserIdQuery query) { + List recentTestedAtList = + loadGroomingTestResultPort.loadRecentTestedAtListByUserId(query); + return GroomingTestHistoryVo.from(recentTestedAtList); + } +} diff --git a/src/main/java/com/ftm/server/application/vo/grooming/GroomingTestHistoryDetailVo.java b/src/main/java/com/ftm/server/application/vo/grooming/GroomingTestHistoryDetailVo.java new file mode 100644 index 0000000..20c4306 --- /dev/null +++ b/src/main/java/com/ftm/server/application/vo/grooming/GroomingTestHistoryDetailVo.java @@ -0,0 +1,34 @@ +package com.ftm.server.application.vo.grooming; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import lombok.Getter; + +@Getter +public class GroomingTestHistoryDetailVo { + + private final String testedAt; + private final GroomingTestResultScoresVo scores; + private final GroomingTestResultGradesVo grades; + private final GroomingLevelVo level; + + private GroomingTestHistoryDetailVo( + String testedAt, + GroomingTestResultScoresVo scores, + GroomingTestResultGradesVo grades, + GroomingLevelVo level) { + this.testedAt = testedAt; + this.scores = scores; + this.grades = grades; + this.level = level; + } + + public static GroomingTestHistoryDetailVo from( + LocalDate testedAt, + GroomingTestResultScoresVo scores, + GroomingTestResultGradesVo grades, + GroomingLevelVo level) { + return new GroomingTestHistoryDetailVo( + testedAt.format(DateTimeFormatter.ofPattern("yyyy.MM.dd")), scores, grades, level); + } +} diff --git a/src/main/java/com/ftm/server/application/vo/grooming/GroomingTestHistoryVo.java b/src/main/java/com/ftm/server/application/vo/grooming/GroomingTestHistoryVo.java new file mode 100644 index 0000000..42e365e --- /dev/null +++ b/src/main/java/com/ftm/server/application/vo/grooming/GroomingTestHistoryVo.java @@ -0,0 +1,24 @@ +package com.ftm.server.application.vo.grooming; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import lombok.Getter; + +@Getter +public class GroomingTestHistoryVo { + + private final List historyDates; + + private GroomingTestHistoryVo(List historyDates) { + this.historyDates = historyDates; + } + + public static GroomingTestHistoryVo from(List historyDates) { + return new GroomingTestHistoryVo( + historyDates.stream() + .map(LocalDateTime::toLocalDate) + .map(date -> date.format(DateTimeFormatter.ofPattern("yyyy.MM.dd"))) + .toList()); + } +} From c1c000e5a5ef99fe1c4b970ccf6f4ccb665ddabb Mon Sep 17 00:00:00 2001 From: songhyeonpk Date: Mon, 14 Apr 2025 16:56:45 +0900 Subject: [PATCH 2/3] =?UTF-8?q?test:=20=EC=9C=A0=EC=A0=80=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=A8=EB=B0=8D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9D=B4?= =?UTF-8?q?=EB=A0=A5=20=EB=AA=A9=EB=A1=9D=20=EB=B0=8F=20=EA=B7=B8=EB=A3=A8?= =?UTF-8?q?=EB=B0=8D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9D=B4=EB=A0=A5=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoadGroomingTestHistoryDetailTest.java | 258 ++++++++++++++++++ .../grooming/LoadGroomingTestHistoryTest.java | 153 +++++++++++ 2 files changed, 411 insertions(+) create mode 100644 src/test/java/com/ftm/server/grooming/LoadGroomingTestHistoryDetailTest.java create mode 100644 src/test/java/com/ftm/server/grooming/LoadGroomingTestHistoryTest.java diff --git a/src/test/java/com/ftm/server/grooming/LoadGroomingTestHistoryDetailTest.java b/src/test/java/com/ftm/server/grooming/LoadGroomingTestHistoryDetailTest.java new file mode 100644 index 0000000..b5fa792 --- /dev/null +++ b/src/test/java/com/ftm/server/grooming/LoadGroomingTestHistoryDetailTest.java @@ -0,0 +1,258 @@ +package com.ftm.server.grooming; + +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.JsonFieldType.*; +import static org.springframework.restdocs.payload.JsonFieldType.STRING; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.in.web.auth.dto.request.GeneralLoginRequest; +import com.ftm.server.adapter.in.web.grooming.dto.request.SaveGroomingTestResultRequest; +import com.ftm.server.application.command.user.GeneralUserCreationCommand; +import com.ftm.server.application.port.out.persistence.auth.LoadUserForAuthPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserImagePort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.application.port.out.security.SecurityAuthenticationPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.domain.entity.User; +import com.ftm.server.domain.entity.UserImage; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.restdocs.request.ParameterDescriptor; +import org.springframework.restdocs.snippet.Attributes; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.transaction.annotation.Transactional; + +public class LoadGroomingTestHistoryDetailTest extends BaseTest { + + @Autowired private SecurityAuthenticationPort securityAuthenticationPort; + @Autowired private SaveUserPort saveUserPort; + @Autowired private SaveUserImagePort saveUserImagePort; + @Autowired private LoadUserForAuthPort loadUserForAuthPort; + + private final ParameterDescriptor queryParametersForDate = + parameterWithName("date") + .description("특정 그루밍 테스트 날짜") + .attributes( + new Attributes.Attribute("constraint", "yyyy.MM.dd or yyyy-MM-dd 형식")); + + private final List responseFieldLoadGroomingTestHistoryDetail = + List.of( + fieldWithPath("status").type(NUMBER).description("응답 상태"), + fieldWithPath("code").type(STRING).description("상태 코드"), + fieldWithPath("message").type(STRING).description("메시지"), + fieldWithPath("data").type(OBJECT).optional().description("응답 데이터"), + fieldWithPath("data.testedAt").type(STRING).description("그루밍 테스트 날짜"), + fieldWithPath("data.scores").type(OBJECT).description("그루밍 테스트 카테고리 별 결과 점수"), + fieldWithPath("data.scores.beautyScore").type(NUMBER).description("뷰티 영역 점수"), + fieldWithPath("data.scores.hygieneScore").type(NUMBER).description("위생 영역 점수"), + fieldWithPath("data.scores.hairScore").type(NUMBER).description("미용 영역 점수"), + fieldWithPath("data.scores.workoutScore").type(NUMBER).description("운동 영역 점수"), + fieldWithPath("data.scores.fashionScore").type(NUMBER).description("패션 영역 점수"), + fieldWithPath("data.scores.totalScore").type(NUMBER).description("총 점수"), + fieldWithPath("data.grades").type(OBJECT).description("그루밍 테스트 카테고리 별 결과 등급"), + fieldWithPath("data.grades.beauty").type(OBJECT).description("뷰티 영역 등급"), + fieldWithPath("data.grades.beauty.grade") + .type(STRING) + .description("뷰티 영역 등급 이름"), + fieldWithPath("data.grades.beauty.level") + .type(NUMBER) + .description("뷰티 영역 등급 레벨(1, 2, 3)"), + fieldWithPath("data.grades.hygiene").type(OBJECT).description("위생 영역 등급"), + fieldWithPath("data.grades.hygiene.grade") + .type(STRING) + .description("위생 영역 등급 이름"), + fieldWithPath("data.grades.hygiene.level") + .type(NUMBER) + .description("위생 영역 등급 레벨(1, 2, 3)"), + fieldWithPath("data.grades.hair").type(OBJECT).description("미용 영역 등급"), + fieldWithPath("data.grades.hair.grade").type(STRING).description("미용 영역 등급 이름"), + fieldWithPath("data.grades.hair.level") + .type(NUMBER) + .description("미용 영역 등급 레벨(1, 2, 3)"), + fieldWithPath("data.grades.workout").type(OBJECT).description("운동 영역 등급"), + fieldWithPath("data.grades.workout.grade") + .type(STRING) + .description("운동 영역 등급 이름"), + fieldWithPath("data.grades.workout.level") + .type(NUMBER) + .description("운동 영역 등급 레벨(1, 2, 3)"), + fieldWithPath("data.grades.fashion").type(OBJECT).description("패션 영역 등급"), + fieldWithPath("data.grades.fashion.grade") + .type(STRING) + .description("패션 영역 등급 이름"), + fieldWithPath("data.grades.fashion.level") + .type(NUMBER) + .description("패션 영역 등급 레벨(1, 2, 3)"), + fieldWithPath("data.level").type(OBJECT).description("그루밍 테스트 결과 레벨"), + fieldWithPath("data.level.groomingLevelId") + .type(NUMBER) + .description("그루밍 레벨 ID"), + fieldWithPath("data.level.mildLevelName") + .type(STRING) + .description("그루밍 레벨 순한맛 이름"), + fieldWithPath("data.level.spicyLevelName") + .type(STRING) + .description("그루밍 레벨 매운맛 이름")); + + private RestDocumentationResultHandler getDocument(Integer identifier) { + return document( + "loadGroomingTestHistoryDetail/" + identifier, + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint(), getModifiedHeader()), + requestHeaders( + headerWithName("Cookie") + .description("로그인 시 발급받은 SESSION Cookie") + .optional()), + responseFields(responseFieldLoadGroomingTestHistoryDetail), + queryParameters(queryParametersForDate), + resource( + ResourceSnippetParameters.builder() + .tag("그루밍 테스트") + .summary("유저 그루밍 테스트 이력 상세 조회 api") + .description("유저 그루밍 테스트 이력 상세 조회 api 입니다.") + .responseFields(responseFieldLoadGroomingTestHistoryDetail) + .build())); + } + + @BeforeEach + void setUp() { + GeneralUserCreationCommand command = + new GeneralUserCreationCommand( + "test@gmail.com", + securityAuthenticationPort.passwordEncode("test1234!"), + "test", + null, + null); + User testUser = saveUserPort.saveUser(User.createGeneralUser(command)); + saveUserImagePort.saveUserDefaultImage(UserImage.createUserImage(testUser.getId())); + } + + @Test + @Transactional + void 그루밍_테스트_이력_상세_조회_성공() throws Exception { + // given + GeneralLoginRequest loginRequest = new GeneralLoginRequest("test@gmail.com", "test1234!"); + User user = + loadUserForAuthPort.loadUserByEmail(FindByEmailQuery.of("test@gmail.com")).get(); + List results = + List.of( + new SaveGroomingTestResultRequest.GroomingTestResult( + 1L, List.of(1L, 2L, 3L)), + new SaveGroomingTestResultRequest.GroomingTestResult(2L, List.of(5L))); + SaveGroomingTestResultRequest saveGroomingTestResultRequest = + new SaveGroomingTestResultRequest(user.getId(), 1L, 10, results); + + String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy.MM.dd")); + + // when + MvcResult loginResult = + mockMvc.perform( + RestDocumentationRequestBuilders.post("/api/auth/login") + .contentType(APPLICATION_JSON_VALUE) + .content(mapper.writeValueAsString(loginRequest))) + .andExpect(status().isOk()) + .andReturn(); + MockHttpSession session = (MockHttpSession) loginResult.getRequest().getSession(false); + + mockMvc.perform( + RestDocumentationRequestBuilders.post("/api/grooming/tests") + .contentType(APPLICATION_JSON_VALUE) + .content(mapper.writeValueAsString(saveGroomingTestResultRequest))); + + ResultActions resultActions = + mockMvc.perform( + RestDocumentationRequestBuilders.get("/api/grooming/tests/histories/detail") + .param("date", today) + .session(session)); + + MvcResult result = resultActions.andReturn(); + result.getRequest().addHeader("Cookie", "SESSION=mock-session-id; Path=/; HttpOnly"); + + // then + resultActions.andExpect(status().isOk()).andDo(print()); + + // documentation + resultActions.andDo(getDocument(1)); + } + + @Test + @Transactional + void 그루밍_테스트_이력_상세_조회_실패1() throws Exception { + // when + ResultActions resultActions = + mockMvc.perform( + RestDocumentationRequestBuilders.get("/api/grooming/tests/histories/detail") + .param("date", "2025.04.10")); + + // then + resultActions + .andExpect(status().is(ErrorResponseCode.NOT_AUTHENTICATED.getHttpStatus().value())) + .andExpect(jsonPath("code").value(ErrorResponseCode.NOT_AUTHENTICATED.getCode())) + .andDo(print()); + + // documentation + resultActions.andDo(getDocument(2)); + } + + @Test + @Transactional + void 그루밍_테스트_이력_상세_조회_실패2() throws Exception { + // given + GeneralLoginRequest loginRequest = new GeneralLoginRequest("test@gmail.com", "test1234!"); + + // when + MvcResult loginResult = + mockMvc.perform( + RestDocumentationRequestBuilders.post("/api/auth/login") + .contentType(APPLICATION_JSON_VALUE) + .content(mapper.writeValueAsString(loginRequest))) + .andExpect(status().isOk()) + .andReturn(); + MockHttpSession session = (MockHttpSession) loginResult.getRequest().getSession(false); + + ResultActions resultActions = + mockMvc.perform( + RestDocumentationRequestBuilders.get("/api/grooming/tests/histories/detail") + .param("date", "2025/04/10 13:00") + .session(session)); + + // then + resultActions + .andExpect( + status().is( + ErrorResponseCode.INVALID_REQUEST_ARGUMENT + .getHttpStatus() + .value())) + .andExpect( + jsonPath("code") + .value(ErrorResponseCode.INVALID_REQUEST_ARGUMENT.getCode())) + .andDo(print()); + + // documentation + resultActions.andDo(getDocument(3)); + } +} diff --git a/src/test/java/com/ftm/server/grooming/LoadGroomingTestHistoryTest.java b/src/test/java/com/ftm/server/grooming/LoadGroomingTestHistoryTest.java new file mode 100644 index 0000000..42ce92c --- /dev/null +++ b/src/test/java/com/ftm/server/grooming/LoadGroomingTestHistoryTest.java @@ -0,0 +1,153 @@ +package com.ftm.server.grooming; + +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.JsonFieldType.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.ftm.server.BaseTest; +import com.ftm.server.adapter.in.web.auth.dto.request.GeneralLoginRequest; +import com.ftm.server.adapter.in.web.grooming.dto.request.SaveGroomingTestResultRequest; +import com.ftm.server.application.command.user.GeneralUserCreationCommand; +import com.ftm.server.application.port.out.persistence.auth.LoadUserForAuthPort; +import com.ftm.server.application.port.out.persistence.user.SaveUserImagePort; +import com.ftm.server.application.port.out.persistence.user.SaveUserPort; +import com.ftm.server.application.port.out.security.SecurityAuthenticationPort; +import com.ftm.server.application.query.FindByEmailQuery; +import com.ftm.server.common.response.enums.ErrorResponseCode; +import com.ftm.server.domain.entity.User; +import com.ftm.server.domain.entity.UserImage; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.payload.FieldDescriptor; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.transaction.annotation.Transactional; + +public class LoadGroomingTestHistoryTest extends BaseTest { + + @Autowired private SecurityAuthenticationPort securityAuthenticationPort; + @Autowired private SaveUserPort saveUserPort; + @Autowired private SaveUserImagePort saveUserImagePort; + @Autowired private LoadUserForAuthPort loadUserForAuthPort; + + private final List responseFieldLoadGroomingTestHistory = + List.of( + fieldWithPath("status").type(NUMBER).description("응답 상태"), + fieldWithPath("code").type(STRING).description("상태 코드"), + fieldWithPath("message").type(STRING).description("메시지"), + fieldWithPath("data").type(OBJECT).optional().description("data"), + fieldWithPath("data.historyDates[]") + .type(ARRAY) + .description("그루밍 테스트 이력 날짜 목록 (최신순)")); + + private RestDocumentationResultHandler getDocument(Integer identifier) { + return document( + "loadGroomingTestHistory/" + identifier, + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint(), getModifiedHeader()), + requestHeaders( + headerWithName("Cookie") + .description("로그인 시 발급받은 SESSION Cookie") + .optional()), + responseFields(responseFieldLoadGroomingTestHistory), + resource( + ResourceSnippetParameters.builder() + .tag("그루밍 테스트") + .summary("유저 그루밍 테스트 이력 목록 조회 api") + .description("유저 그루밍 테스트 이력 목록 조회 api 입니다.") + .responseFields(responseFieldLoadGroomingTestHistory) + .build())); + } + + @BeforeEach + void setUp() { + GeneralUserCreationCommand command = + new GeneralUserCreationCommand( + "test@gmail.com", + securityAuthenticationPort.passwordEncode("test1234!"), + "test", + null, + null); + User testUser = saveUserPort.saveUser(User.createGeneralUser(command)); + saveUserImagePort.saveUserDefaultImage(UserImage.createUserImage(testUser.getId())); + } + + @Test + @Transactional + void 그루밍_테스트_이력_목록_조회_성공() throws Exception { + // given + GeneralLoginRequest loginRequest = new GeneralLoginRequest("test@gmail.com", "test1234!"); + User user = + loadUserForAuthPort.loadUserByEmail(FindByEmailQuery.of("test@gmail.com")).get(); + List results = + List.of( + new SaveGroomingTestResultRequest.GroomingTestResult( + 1L, List.of(1L, 2L, 3L)), + new SaveGroomingTestResultRequest.GroomingTestResult(2L, List.of(5L))); + SaveGroomingTestResultRequest saveGroomingTestResultRequest = + new SaveGroomingTestResultRequest(user.getId(), 1L, 10, results); + + // when + MvcResult loginResult = + mockMvc.perform( + RestDocumentationRequestBuilders.post("/api/auth/login") + .contentType(APPLICATION_JSON_VALUE) + .content(mapper.writeValueAsString(loginRequest))) + .andExpect(status().isOk()) + .andReturn(); + MockHttpSession session = (MockHttpSession) loginResult.getRequest().getSession(false); + + mockMvc.perform( + RestDocumentationRequestBuilders.post("/api/grooming/tests") + .contentType(APPLICATION_JSON_VALUE) + .content(mapper.writeValueAsString(saveGroomingTestResultRequest))); + + ResultActions resultActions = + mockMvc.perform( + RestDocumentationRequestBuilders.get("/api/grooming/tests/histories") + .session(session)); + + MvcResult result = resultActions.andReturn(); + result.getRequest().addHeader("Cookie", "SESSION=mock-session-id; Path=/; HttpOnly"); + + // then + resultActions.andExpect(status().isOk()).andDo(print()); + + // documentation + resultActions.andDo(getDocument(1)); + } + + @Test + @Transactional + void 그루밍_테스트_이력_목록_조회_실패() throws Exception { + // when + ResultActions resultActions = + mockMvc.perform( + RestDocumentationRequestBuilders.get("/api/grooming/tests/histories")); + + // then + resultActions + .andExpect(status().is(ErrorResponseCode.NOT_AUTHENTICATED.getHttpStatus().value())) + .andExpect(jsonPath("code").value(ErrorResponseCode.NOT_AUTHENTICATED.getCode())) + .andDo(print()); + + // documentation + resultActions.andDo(getDocument(2)); + } +} From 994345596d18b3318ea3a3ff3bec1f8d857f73e6 Mon Sep 17 00:00:00 2001 From: songhyeonpk Date: Mon, 14 Apr 2025 16:57:09 +0900 Subject: [PATCH 3/3] =?UTF-8?q?docs:=20=EC=9C=A0=EC=A0=80=20=EA=B7=B8?= =?UTF-8?q?=EB=A3=A8=EB=B0=8D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9D=B4?= =?UTF-8?q?=EB=A0=A5=20=EB=AA=A9=EB=A1=9D=20=EB=B0=8F=20=EA=B7=B8=EB=A3=A8?= =?UTF-8?q?=EB=B0=8D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9D=B4=EB=A0=A5=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20=EB=AC=B8=EC=84=9C?= =?UTF-8?q?=ED=99=94=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/grooming-test-api.adoc | 52 +++++++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/docs/asciidoc/grooming-test-api.adoc b/src/docs/asciidoc/grooming-test-api.adoc index b33f6ef..cefcbb7 100644 --- a/src/docs/asciidoc/grooming-test-api.adoc +++ b/src/docs/asciidoc/grooming-test-api.adoc @@ -31,11 +31,11 @@ include::{snippetsDir}/submitGroomingTests/2/http-response.adoc[] include::{snippetsDir}/submitGroomingTests/1/response-fields.adoc[] ==== 실패 Response -실패 3. 유효하지 않은 그루밍 테스트 질문 ID일 경우 (존재하지 않는 질문 ID, 중복된 질문 ID) +실패 1. 유효하지 않은 그루밍 테스트 질문 ID일 경우 (존재하지 않는 질문 ID, 중복된 질문 ID) include::{snippetsDir}/submitGroomingTests/3/http-response.adoc[] -실패 4. 유효하지 않은 그루밍 테스트 답변 ID일 경우 (존재하지 않는 답변 ID, 질문과 잘못 매핑된 답변 ID, 중복된 답변 ID) +실패 2. 유효하지 않은 그루밍 테스트 답변 ID일 경우 (존재하지 않는 답변 ID, 질문과 잘못 매핑된 답변 ID, 중복된 답변 ID) include::{snippetsDir}/submitGroomingTests/4/http-response.adoc[] @@ -103,3 +103,51 @@ include::{snippetsDir}/saveGroomingTestResult/4/http-response.adoc[] include::{snippetsDir}/saveGroomingTestResult/5/http-response.adoc[] --- + +=== **5. 유저 그루밍 테스트 이력 목록 조회** + +유저의 그루밍 테스트 이력 목록을 조회하는 api 입니다. (최신순 5개의 테스트 한 날짜 응답) + +==== Request +include::{snippetsDir}/loadGroomingTestHistory/1/http-request.adoc[] + +==== 성공 Response +include::{snippetsDir}/loadGroomingTestHistory/1/http-response.adoc[] + +==== Response Body Fields +include::{snippetsDir}/loadGroomingTestHistory/1/response-fields.adoc[] + +==== 실패 Response +실패 1. 인증된 유저가 아닌 경우 + +include::{snippetsDir}/loadGroomingTestHistory/2/http-response.adoc[] + +--- + +=== **6. 유저 그루밍 테스트 이력 상세 조회** + +유저의 그루밍 테스트 이력 내역을 상세 조회하는 api 입니다. (테스트한 날짜로 요청) + +==== Request +include::{snippetsDir}/loadGroomingTestHistoryDetail/1/http-request.adoc[] + +==== Request Query Parameters +include::{snippetsDir}/loadGroomingTestHistoryDetail/1/query-parameters.adoc[] + +==== 성공 Response +include::{snippetsDir}/loadGroomingTestHistoryDetail/1/http-response.adoc[] + +==== Response Body Fields +include::{snippetsDir}/loadGroomingTestHistoryDetail/1/response-fields.adoc[] + +==== 실패 Response +실패 1. 인증된 유저가 아닌 경우 + +include::{snippetsDir}/loadGroomingTestHistoryDetail/2/http-response.adoc[] + +실패 2. 잘못된 형식의 날짜 파라미터를 요청할 경우 ("yyyy.MM.dd" or "yyyy-MM-dd" 형식이 아닐 경우) + +include::{snippetsDir}/loadGroomingTestHistoryDetail/3/http-response.adoc[] + +--- +