diff --git a/.gradle/8.13/executionHistory/executionHistory.lock b/.gradle/8.13/executionHistory/executionHistory.lock index 98bfadc..84c866a 100644 Binary files a/.gradle/8.13/executionHistory/executionHistory.lock and b/.gradle/8.13/executionHistory/executionHistory.lock differ diff --git a/.gradle/8.13/fileHashes/fileHashes.bin b/.gradle/8.13/fileHashes/fileHashes.bin index c136885..24c7a4a 100644 Binary files a/.gradle/8.13/fileHashes/fileHashes.bin and b/.gradle/8.13/fileHashes/fileHashes.bin differ diff --git a/.gradle/8.13/fileHashes/fileHashes.lock b/.gradle/8.13/fileHashes/fileHashes.lock index 354f994..56a5e43 100644 Binary files a/.gradle/8.13/fileHashes/fileHashes.lock and b/.gradle/8.13/fileHashes/fileHashes.lock differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 123655e..92ca3d2 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/src/main/java/study/goorm/domain/history/api/HistoryRestController.java b/src/main/java/study/goorm/domain/history/api/HistoryRestController.java new file mode 100644 index 0000000..a4afa85 --- /dev/null +++ b/src/main/java/study/goorm/domain/history/api/HistoryRestController.java @@ -0,0 +1,39 @@ +package study.goorm.domain.history.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import study.goorm.domain.history.application.HistoryService; +import study.goorm.domain.history.dto.HistoryResponseDTO; +import study.goorm.global.common.response.BaseResponse; +import study.goorm.global.error.code.status.SuccessStatus; + +import java.time.YearMonth; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/histories") +@Validated +public class HistoryRestController { + + private final HistoryService historyService; + + @GetMapping("/monthly") + @Operation(summary = "월별 기록 조회 API", description = "Query Parameter로 clokeyId와 month(YYYY-MM)를 전달해주세요. clokeyId를 생략하면 본인 기록을 조회합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "HISTORY_200", description = "기록이 성공적으로 조회되었습니다."), + }) + public BaseResponse getMonthlyHistories( + @RequestParam(value = "clokeyId", required = false) String clokeyId, + @RequestParam("month") @DateTimeFormat(pattern = "yyyy-MM") YearMonth month + ) { + HistoryResponseDTO.MonthlyHistoryViewResult result = historyService.getMonthlyHistories(clokeyId, month); + return BaseResponse.onSuccess(SuccessStatus.HISTORY_VIEW_SUCCESS, result); + } +} \ No newline at end of file diff --git a/src/main/java/study/goorm/domain/history/application/HistoryService.java b/src/main/java/study/goorm/domain/history/application/HistoryService.java new file mode 100644 index 0000000..b3ce6b9 --- /dev/null +++ b/src/main/java/study/goorm/domain/history/application/HistoryService.java @@ -0,0 +1,10 @@ +package study.goorm.domain.history.application; + +import study.goorm.domain.history.dto.HistoryResponseDTO; + +import java.time.LocalDate; +import java.time.YearMonth; + +public interface HistoryService { + HistoryResponseDTO.MonthlyHistoryViewResult getMonthlyHistories(String clokeyId, YearMonth historyDate); +} diff --git a/src/main/java/study/goorm/domain/history/application/HistoryServiceImpl.java b/src/main/java/study/goorm/domain/history/application/HistoryServiceImpl.java new file mode 100644 index 0000000..1cc9b85 --- /dev/null +++ b/src/main/java/study/goorm/domain/history/application/HistoryServiceImpl.java @@ -0,0 +1,71 @@ +package study.goorm.domain.history.application; + +import lombok.RequiredArgsConstructor; +import org.apache.tomcat.util.net.openssl.ciphers.Authentication; +import org.springframework.stereotype.Service; +import study.goorm.domain.history.converter.HistoryConverter; +import study.goorm.domain.history.domain.entity.History; +import study.goorm.domain.history.domain.repository.HistoryRepository; +import study.goorm.domain.history.dto.HistoryResponseDTO; +import study.goorm.domain.member.domain.entity.Member; +import study.goorm.domain.member.domain.exception.MemberException; +import study.goorm.domain.member.domain.repository.MemberRepository; +import study.goorm.global.error.code.status.ErrorStatus; + +import java.time.YearMonth; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class HistoryServiceImpl implements HistoryService { + + private final HistoryRepository historyRepository; + private final MemberRepository memberRepository; + + + @Override + public HistoryResponseDTO.MonthlyHistoryViewResult getMonthlyHistories(String clokeyId, YearMonth month) { + YearMonth yearMonth = YearMonth.from(month); + + if (clokeyId == null) { +// clokeyId = SecurityUtil.getCurrentClokeyId(); + } + + Member member = memberRepository.findByClokeyId(clokeyId) + .orElseThrow(() -> new MemberException(ErrorStatus.NO_SUCH_MEMBER)); + + List histories = historyRepository.findByClokeyIdAndMonth( + clokeyId, yearMonth.getYear(), yearMonth.getMonthValue() + ); + + List historyDTOs = histories.stream() + .map(history -> { + String imageUrl = (history.getPhotos() == null || history.getPhotos().isEmpty()) + ? null + : Optional.ofNullable(history.getPhotos().get(0).getImageUrl()).orElse("비공개입니다"); + + return HistoryConverter.toHistoryItem(history, imageUrl); + }) + .collect(Collectors.toList()); + + return HistoryConverter.toMonthlyHistoryViewResult(member, historyDTOs); + } + + +} + +//public class SecurityUtil { +// +// public static String getCurrentClokeyId() { +// Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); +// +//// if (authentication == null || !authentication.isAuthenticated()) { +//// throw new MemberException(ErrorStatus.UNAUTHORIZED); +//// } +// +// return authentication.getName(); // 또는 ((CustomUserDetails) authentication.getPrincipal()).getClokeyId(); +// } +//} + diff --git a/src/main/java/study/goorm/domain/history/converter/HistoryConverter.java b/src/main/java/study/goorm/domain/history/converter/HistoryConverter.java new file mode 100644 index 0000000..87e4e29 --- /dev/null +++ b/src/main/java/study/goorm/domain/history/converter/HistoryConverter.java @@ -0,0 +1,26 @@ +package study.goorm.domain.history.converter; + +import study.goorm.domain.history.domain.entity.History; +import study.goorm.domain.history.dto.HistoryResponseDTO; +import study.goorm.domain.member.domain.entity.Member; + +import java.util.List; + +public class HistoryConverter { + + public static HistoryResponseDTO.MonthlyHistoryViewResult toMonthlyHistoryViewResult(Member member, List historyItems) { + return HistoryResponseDTO.MonthlyHistoryViewResult.builder() + .memberId(member.getId()) + .nickName(member.getNickname()) + .histories(historyItems) + .build(); + } + + public static HistoryResponseDTO.HistoryItem toHistoryItem(History history, String imageUrl) { + return HistoryResponseDTO.HistoryItem.builder() + .historyId(history.getId()) + .date(history.getHistoryDate()) + .imageUrl(imageUrl) + .build(); + } +} diff --git a/src/main/java/study/goorm/domain/history/domain/entity/History.java b/src/main/java/study/goorm/domain/history/domain/entity/History.java index 0180b18..26c6a1c 100644 --- a/src/main/java/study/goorm/domain/history/domain/entity/History.java +++ b/src/main/java/study/goorm/domain/history/domain/entity/History.java @@ -9,6 +9,8 @@ import study.goorm.domain.model.entity.BaseEntity; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; @Entity @Getter @@ -37,5 +39,8 @@ public class History extends BaseEntity { @JoinColumn(name = "member_id", nullable = false) private Member member; + @OneToMany(mappedBy = "history", cascade = CascadeType.ALL, orphanRemoval = true) + private List photos = new ArrayList<>(); + } diff --git a/src/main/java/study/goorm/domain/history/domain/repository/HistoryRepository.java b/src/main/java/study/goorm/domain/history/domain/repository/HistoryRepository.java index 5f66987..d42635c 100644 --- a/src/main/java/study/goorm/domain/history/domain/repository/HistoryRepository.java +++ b/src/main/java/study/goorm/domain/history/domain/repository/HistoryRepository.java @@ -1,7 +1,21 @@ package study.goorm.domain.history.domain.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import study.goorm.domain.history.domain.entity.History; +import java.time.YearMonth; +import java.util.List; + public interface HistoryRepository extends JpaRepository { + + @Query("SELECT h FROM History h " + + "JOIN h.member m " + + "WHERE m.clokeyId = :clokeyId AND " + + "YEAR(h.historyDate) = :year AND MONTH(h.historyDate) = :month") + List findByClokeyIdAndMonth(@Param("clokeyId") String clokeyId, + @Param("year") int year, + @Param("month") int month); } + diff --git a/src/main/java/study/goorm/domain/history/dto/HistoryRequestDTO.java b/src/main/java/study/goorm/domain/history/dto/HistoryRequestDTO.java new file mode 100644 index 0000000..87594c7 --- /dev/null +++ b/src/main/java/study/goorm/domain/history/dto/HistoryRequestDTO.java @@ -0,0 +1,4 @@ +package study.goorm.domain.history.dto; + +public class HistoryRequestDTO { +} diff --git a/src/main/java/study/goorm/domain/history/dto/HistoryResponseDTO.java b/src/main/java/study/goorm/domain/history/dto/HistoryResponseDTO.java new file mode 100644 index 0000000..e64e770 --- /dev/null +++ b/src/main/java/study/goorm/domain/history/dto/HistoryResponseDTO.java @@ -0,0 +1,32 @@ +package study.goorm.domain.history.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.time.YearMonth; +import java.util.List; + +public class HistoryResponseDTO { + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class MonthlyHistoryViewResult { + private Long memberId; + private String nickName; + private List histories; + } + + @Builder + @Getter + @AllArgsConstructor + public static class HistoryItem { + private Long historyId; + private LocalDate date; + private String imageUrl; + } +} diff --git a/src/main/java/study/goorm/domain/history/exception/HistoryException.java b/src/main/java/study/goorm/domain/history/exception/HistoryException.java new file mode 100644 index 0000000..b2b5304 --- /dev/null +++ b/src/main/java/study/goorm/domain/history/exception/HistoryException.java @@ -0,0 +1,10 @@ +package study.goorm.domain.history.exception; + +import study.goorm.global.error.code.status.BaseErrorCode; +import study.goorm.global.exception.GeneralException; + +public class HistoryException extends GeneralException { + public HistoryException(BaseErrorCode code) { + super(code); + } +} diff --git a/src/main/java/study/goorm/domain/member/domain/exception/MemberException.java b/src/main/java/study/goorm/domain/member/domain/exception/MemberException.java index ed93df3..e783dfe 100644 --- a/src/main/java/study/goorm/domain/member/domain/exception/MemberException.java +++ b/src/main/java/study/goorm/domain/member/domain/exception/MemberException.java @@ -6,6 +6,7 @@ public class MemberException extends GeneralException { public MemberException(BaseErrorCode code) { + super(code); } } \ No newline at end of file diff --git a/src/main/java/study/goorm/global/error/code/status/ErrorStatus.java b/src/main/java/study/goorm/global/error/code/status/ErrorStatus.java index 694cecd..181ae74 100644 --- a/src/main/java/study/goorm/global/error/code/status/ErrorStatus.java +++ b/src/main/java/study/goorm/global/error/code/status/ErrorStatus.java @@ -25,7 +25,13 @@ public enum ErrorStatus implements BaseErrorCode { // Page PAGE_UNDER_ONE(HttpStatus.BAD_REQUEST,"PAGE_4001","페이지는 1이상으로 입력해야 합니다."), - PAGE_SIZE_UNDER_ONE(HttpStatus.BAD_REQUEST,"PAGE_4002","페이지 사이즈는 1이상으로 입력해야 합니다.") + PAGE_SIZE_UNDER_ONE(HttpStatus.BAD_REQUEST,"PAGE_4002","페이지 사이즈는 1이상으로 입력해야 합니다."), + + //History + NO_SUCH_HISTORY(HttpStatus.BAD_REQUEST, "HISTORY_4001","기록이 존재하지 않습니다"), + + //Date + INVALID_MONTH_FORMAT(HttpStatus.BAD_REQUEST, "DATE_4001", "날짜 형식이 올바르지 않습니다"), ; diff --git a/src/main/java/study/goorm/global/error/code/status/SuccessStatus.java b/src/main/java/study/goorm/global/error/code/status/SuccessStatus.java index 103f989..39f3088 100644 --- a/src/main/java/study/goorm/global/error/code/status/SuccessStatus.java +++ b/src/main/java/study/goorm/global/error/code/status/SuccessStatus.java @@ -14,7 +14,10 @@ public enum SuccessStatus implements BaseCode { //Cloth CLOTH_VIEW_SUCCESS(HttpStatus.OK,"CLOTH_200","옷이 성공적으로 조회되었습니다."), CLOTH_CREATED(HttpStatus.CREATED, "CLOTH_201"," 옷이 성공적으로 생성되었습니다."), - CLOTH_DELETED(HttpStatus.NO_CONTENT,"CLOTH_202","옷이 성공적으로 삭제되었습니다") + CLOTH_DELETED(HttpStatus.NO_CONTENT,"CLOTH_202","옷이 성공적으로 삭제되었습니다"), + + //History + HISTORY_VIEW_SUCCESS(HttpStatus.OK,"HISTORY_200","기록이 성공적으로 조회되었습니다."), ;