diff --git a/src/main/java/swyp/team5/greening/petPlant/controller/DailyRecordController.java b/src/main/java/swyp/team5/greening/petPlant/controller/DailyRecordController.java index e1af5a3..79028d7 100644 --- a/src/main/java/swyp/team5/greening/petPlant/controller/DailyRecordController.java +++ b/src/main/java/swyp/team5/greening/petPlant/controller/DailyRecordController.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -15,7 +16,9 @@ import swyp.team5.greening.common.resolver.LogIn; import swyp.team5.greening.petPlant.dto.request.CreateDailyRecordRequestDto; import swyp.team5.greening.petPlant.dto.response.CreateDailyRecordResponseDto; +import swyp.team5.greening.petPlant.dto.response.FindDailyRecordResponseDto; import swyp.team5.greening.petPlant.service.DailyRecordCommandService; +import swyp.team5.greening.petPlant.service.DailyRecordQueryService; @Tag(name = "애완 식물 오늘의 기록 API") @RestController @@ -24,6 +27,7 @@ public class DailyRecordController { private final DailyRecordCommandService dailyRecordCommandService; + private final DailyRecordQueryService dailyRecordQueryService; @Operation(summary = "특정 애완 식물 오늘의 기록 작성 API") @PostMapping("/{petPlantId}/daily-record") @@ -34,7 +38,24 @@ public ApiResponseDto createDailyRecord( @Validated @RequestBody CreateDailyRecordRequestDto requestDto ) { return ApiResponseDto.of( - dailyRecordCommandService.createDailyRecord(userId, petPlantId, requestDto)); + dailyRecordCommandService.createDailyRecord( + userId, + petPlantId, + requestDto + )); + } + + @Operation(summary = "특정 애완 식물 특정 오늘의 기록 조회 API") + @GetMapping("/daily-record/{dailyRecordId}") + @ResponseStatus(HttpStatus.OK) + public ApiResponseDto getDailyRecord( + @LogIn Long userId, + @PathVariable Long dailyRecordId + ) { + return ApiResponseDto.of(dailyRecordQueryService.findDailyRecord( + userId, + dailyRecordId + )); } } diff --git a/src/main/java/swyp/team5/greening/petPlant/domain/entity/DailyRecord.java b/src/main/java/swyp/team5/greening/petPlant/domain/entity/DailyRecord.java index fed6e7b..d194449 100644 --- a/src/main/java/swyp/team5/greening/petPlant/domain/entity/DailyRecord.java +++ b/src/main/java/swyp/team5/greening/petPlant/domain/entity/DailyRecord.java @@ -3,6 +3,8 @@ import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -33,6 +35,10 @@ public class DailyRecord extends BaseTimeEntity { @Column(name = "write_date") private LocalDate writeDate; + @Enumerated(EnumType.STRING) + @Column(name = "state") + private DailyRecordState state; + @Column(name = "pet_plant_id") private Long petPlantId; @@ -43,10 +49,12 @@ public class DailyRecord extends BaseTimeEntity { public DailyRecord( String title, LocalDate writeDate, + DailyRecordState state, Long petPlantId ) { this.title = title; this.writeDate = writeDate; + this.state = state; this.petPlantId = petPlantId; } diff --git a/src/main/java/swyp/team5/greening/petPlant/domain/entity/DailyRecordState.java b/src/main/java/swyp/team5/greening/petPlant/domain/entity/DailyRecordState.java new file mode 100644 index 0000000..1c00f21 --- /dev/null +++ b/src/main/java/swyp/team5/greening/petPlant/domain/entity/DailyRecordState.java @@ -0,0 +1,8 @@ +package swyp.team5.greening.petPlant.domain.entity; + +public enum DailyRecordState { + + IN_PROGRESS, + DELETED + +} diff --git a/src/main/java/swyp/team5/greening/petPlant/domain/repository/DailyRecordQueryRepository.java b/src/main/java/swyp/team5/greening/petPlant/domain/repository/DailyRecordQueryRepository.java index 36c2f37..c37d67e 100644 --- a/src/main/java/swyp/team5/greening/petPlant/domain/repository/DailyRecordQueryRepository.java +++ b/src/main/java/swyp/team5/greening/petPlant/domain/repository/DailyRecordQueryRepository.java @@ -2,7 +2,9 @@ import java.time.LocalDate; import java.util.List; +import java.util.Optional; import swyp.team5.greening.petPlant.domain.entity.DailyRecord; +import swyp.team5.greening.petPlant.dto.data.FindDailyRecordDto; public interface DailyRecordQueryRepository { @@ -11,4 +13,6 @@ List findByPetPlantAndWriteDate( LocalDate startDate, LocalDate endDate ); + + Optional findDailyRecord(Long dailyRecordId); } diff --git a/src/main/java/swyp/team5/greening/petPlant/dto/data/FindDailyRecordDto.java b/src/main/java/swyp/team5/greening/petPlant/dto/data/FindDailyRecordDto.java new file mode 100644 index 0000000..4f65604 --- /dev/null +++ b/src/main/java/swyp/team5/greening/petPlant/dto/data/FindDailyRecordDto.java @@ -0,0 +1,11 @@ +package swyp.team5.greening.petPlant.dto.data; + +import swyp.team5.greening.petPlant.domain.entity.DailyRecord; +import swyp.team5.greening.petPlant.domain.entity.PetPlant; + +public record FindDailyRecordDto( + DailyRecord dailyRecord, + PetPlant petPlant +) { + +} diff --git a/src/main/java/swyp/team5/greening/petPlant/dto/response/FindDailyRecordResponseDto.java b/src/main/java/swyp/team5/greening/petPlant/dto/response/FindDailyRecordResponseDto.java new file mode 100644 index 0000000..d28089f --- /dev/null +++ b/src/main/java/swyp/team5/greening/petPlant/dto/response/FindDailyRecordResponseDto.java @@ -0,0 +1,39 @@ +package swyp.team5.greening.petPlant.dto.response; + +import com.fasterxml.jackson.annotation.JsonFormat; +import java.time.LocalDateTime; +import java.util.Comparator; +import java.util.List; +import swyp.team5.greening.petPlant.domain.entity.DailyRecord; +import swyp.team5.greening.petPlant.domain.entity.DailyRecordContent; +import swyp.team5.greening.petPlant.dto.DailyRecordContentDto; + +public record FindDailyRecordResponseDto( + + String title, + + @JsonFormat(pattern = "yyyy-MM-dd'T'hh:mm:ss") + LocalDateTime createdAt, + + List content + +) { + + public static FindDailyRecordResponseDto of(DailyRecord dailyRecord) { + + List dailyRecordContents = dailyRecord.getDailyRecordContents(); + + return new FindDailyRecordResponseDto( + dailyRecord.getTitle(), + dailyRecord.getCreatedAt(), + dailyRecordContents.stream() + .sorted(Comparator.comparing(DailyRecordContent::getSequence)) + .map(content -> + new DailyRecordContentDto(content.getType().name(), + content.getContent())) + .toList() + ); + + } + +} diff --git a/src/main/java/swyp/team5/greening/petPlant/exception/PetPlantExceptionMessage.java b/src/main/java/swyp/team5/greening/petPlant/exception/PetPlantExceptionMessage.java index 5ed1abe..20c3d6f 100644 --- a/src/main/java/swyp/team5/greening/petPlant/exception/PetPlantExceptionMessage.java +++ b/src/main/java/swyp/team5/greening/petPlant/exception/PetPlantExceptionMessage.java @@ -11,8 +11,9 @@ public enum PetPlantExceptionMessage implements ExceptionMessage { NOT_FOUND_PET_PLANT("존재하지 않는 애완 식물입니다.", "404"), BAD_REQUEST_PET_PLANT_WRITER("내가 등록한 애완 식물이 아닙니다.", "400"), - INVALID_DATE_ACCESS("해당 날짜에만 접근 가능합니다.", "400"); + NOT_FOUND_DAILY_RECORD("해당 날짜의 기록이 존재하지 않습니다.", "404"), + INVALID_DATE_ACCESS("해당 날짜에만 접근 가능합니다.", "400"); private final String message; private final String code; diff --git a/src/main/java/swyp/team5/greening/petPlant/infrastructure/DailyRecordJpaQueryRepository.java b/src/main/java/swyp/team5/greening/petPlant/infrastructure/DailyRecordJpaQueryRepository.java index f4f2ac3..556a1ca 100644 --- a/src/main/java/swyp/team5/greening/petPlant/infrastructure/DailyRecordJpaQueryRepository.java +++ b/src/main/java/swyp/team5/greening/petPlant/infrastructure/DailyRecordJpaQueryRepository.java @@ -2,11 +2,13 @@ import java.time.LocalDate; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import swyp.team5.greening.petPlant.domain.entity.DailyRecord; import swyp.team5.greening.petPlant.domain.repository.DailyRecordQueryRepository; +import swyp.team5.greening.petPlant.dto.data.FindDailyRecordDto; public interface DailyRecordJpaQueryRepository extends JpaRepository, DailyRecordQueryRepository { @@ -18,6 +20,7 @@ public interface DailyRecordJpaQueryRepository extends JpaRepository= :startDate AND dailyRecord.writeDate < :endDate + AND dailyRecord.state = 'IN_PROGRESS' ORDER BY dailyRecord.writeDate asc """) List findByPetPlantAndWriteDate( @@ -26,4 +29,16 @@ List findByPetPlantAndWriteDate( @Param("endDate") LocalDate endDate ); + @Override + @Query(""" + SELECT new swyp.team5.greening.petPlant.dto.data.FindDailyRecordDto( + dailyRecord, petPlant) + FROM DailyRecord dailyRecord + INNER JOIN PetPlant petPlant + ON petPlant.id = dailyRecord.petPlantId + WHERE dailyRecord.id = :dailyRecordId + AND dailyRecord.state = 'IN_PROGRESS' + """) + Optional findDailyRecord(@Param("dailyRecordId") Long dailyRecordId); + } diff --git a/src/main/java/swyp/team5/greening/petPlant/service/DailyRecordCommandService.java b/src/main/java/swyp/team5/greening/petPlant/service/DailyRecordCommandService.java index 0c5fd6a..3e5503a 100644 --- a/src/main/java/swyp/team5/greening/petPlant/service/DailyRecordCommandService.java +++ b/src/main/java/swyp/team5/greening/petPlant/service/DailyRecordCommandService.java @@ -11,6 +11,7 @@ import swyp.team5.greening.petPlant.domain.entity.DailyRecord; import swyp.team5.greening.petPlant.domain.entity.DailyRecordContent; import swyp.team5.greening.petPlant.domain.entity.DailyRecordContentType; +import swyp.team5.greening.petPlant.domain.entity.DailyRecordState; import swyp.team5.greening.petPlant.domain.entity.PetPlant; import swyp.team5.greening.petPlant.domain.entity.PetPlantState; import swyp.team5.greening.petPlant.domain.repository.DailyRecordRepository; @@ -59,6 +60,7 @@ public CreateDailyRecordResponseDto createDailyRecord( DailyRecord dailyRecord = DailyRecord.builder() .title(requestDto.title()) .writeDate(requestDto.today()) + .state(DailyRecordState.IN_PROGRESS) .petPlantId(petPlantId) .build(); diff --git a/src/main/java/swyp/team5/greening/petPlant/service/DailyRecordQueryService.java b/src/main/java/swyp/team5/greening/petPlant/service/DailyRecordQueryService.java new file mode 100644 index 0000000..b2b88dc --- /dev/null +++ b/src/main/java/swyp/team5/greening/petPlant/service/DailyRecordQueryService.java @@ -0,0 +1,41 @@ +package swyp.team5.greening.petPlant.service; + +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import swyp.team5.greening.common.exception.GreeningGlobalException; +import swyp.team5.greening.petPlant.domain.entity.DailyRecord; +import swyp.team5.greening.petPlant.domain.entity.PetPlant; +import swyp.team5.greening.petPlant.domain.repository.DailyRecordQueryRepository; +import swyp.team5.greening.petPlant.dto.data.FindDailyRecordDto; +import swyp.team5.greening.petPlant.dto.response.FindDailyRecordResponseDto; +import swyp.team5.greening.petPlant.exception.PetPlantExceptionMessage; + +@Service +@RequiredArgsConstructor +public class DailyRecordQueryService { + + private final DailyRecordQueryRepository dailyRecordQueryRepository; + + @Transactional + public FindDailyRecordResponseDto findDailyRecord( + Long userId, + Long dailyRecordId + ) { + FindDailyRecordDto dailyRecordDto = dailyRecordQueryRepository.findDailyRecord( + dailyRecordId) + .orElseThrow(() + -> new GreeningGlobalException(PetPlantExceptionMessage.NOT_FOUND_DAILY_RECORD)); + + DailyRecord dailyRecord = dailyRecordDto.dailyRecord(); + PetPlant petPlant = dailyRecordDto.petPlant(); + + if (!Objects.equals(petPlant.getUserId(), userId)) { + throw new GreeningGlobalException(PetPlantExceptionMessage.BAD_REQUEST_PET_PLANT_WRITER); + } + + return FindDailyRecordResponseDto.of(dailyRecord); + } + +} diff --git a/src/main/java/swyp/team5/greening/post/dto/response/FindPostResponseDto.java b/src/main/java/swyp/team5/greening/post/dto/response/FindPostResponseDto.java index 4934462..742f07c 100644 --- a/src/main/java/swyp/team5/greening/post/dto/response/FindPostResponseDto.java +++ b/src/main/java/swyp/team5/greening/post/dto/response/FindPostResponseDto.java @@ -1,6 +1,7 @@ package swyp.team5.greening.post.dto.response; import java.time.LocalDateTime; +import java.util.Comparator; import java.util.List; import swyp.team5.greening.post.domain.entity.Post; import swyp.team5.greening.post.domain.entity.PostContent; @@ -41,7 +42,9 @@ public static FindPostResponseDto of( isLike, isAuthor, postContents.stream() - .map(content -> new PostContentDto(content.getType().name(), content.getContent())) + .sorted(Comparator.comparing(PostContent::getSequence)) + .map(content -> + new PostContentDto(content.getType().name(), content.getContent())) .toList() ); } diff --git a/src/main/java/swyp/team5/greening/post/infrastructure/PostJpaQueryRepository.java b/src/main/java/swyp/team5/greening/post/infrastructure/PostJpaQueryRepository.java index b33441d..885223d 100644 --- a/src/main/java/swyp/team5/greening/post/infrastructure/PostJpaQueryRepository.java +++ b/src/main/java/swyp/team5/greening/post/infrastructure/PostJpaQueryRepository.java @@ -28,6 +28,7 @@ public interface PostJpaQueryRepository extends JpaRepository, PostQ ON likes.userId = :userId AND likes.postId = post.id WHERE post.id = :postId + AND post.state = 'IN_PROGRESS' """) Optional findPost( @Param("postId") Long postId, diff --git a/src/test/java/swyp/team5/greening/petPlant/controller/DailyRecordControllerTest.java b/src/test/java/swyp/team5/greening/petPlant/controller/DailyRecordControllerTest.java index 85a71c7..18fbe7c 100644 --- a/src/test/java/swyp/team5/greening/petPlant/controller/DailyRecordControllerTest.java +++ b/src/test/java/swyp/team5/greening/petPlant/controller/DailyRecordControllerTest.java @@ -18,6 +18,10 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.ResultActions; import swyp.team5.greening.common.exception.GreeningGlobalException; +import swyp.team5.greening.petPlant.domain.entity.DailyRecord; +import swyp.team5.greening.petPlant.domain.entity.DailyRecordContent; +import swyp.team5.greening.petPlant.domain.entity.DailyRecordContentType; +import swyp.team5.greening.petPlant.domain.entity.DailyRecordState; import swyp.team5.greening.petPlant.domain.entity.PetPlant; import swyp.team5.greening.petPlant.domain.entity.PetPlantState; import swyp.team5.greening.petPlant.domain.repository.DailyRecordRepository; @@ -26,7 +30,6 @@ import swyp.team5.greening.petPlant.dto.request.CreateDailyRecordRequestDto; import swyp.team5.greening.petPlant.exception.PetPlantExceptionMessage; import swyp.team5.greening.support.ApiTestSupport; -import swyp.team5.greening.user.domain.repository.UserRepository; class DailyRecordControllerTest extends ApiTestSupport { @@ -39,9 +42,6 @@ class DailyRecordControllerTest extends ApiTestSupport { @Autowired private DailyRecordRepository dailyRecordRepository; - @Autowired - private UserRepository userRepository; - @BeforeEach void init() { petPlantRepository.deleteAll(); @@ -161,4 +161,56 @@ void createDailyRecord3() throws Exception { } } + @Nested + @DisplayName("사용자는 애완 식물 1개에 대해 오늘의 기록을 1개 작성한 상태이다.") + class TestCase2 { + + PetPlant petPlant; + + DailyRecord dailyRecord; + + List contents; + + @BeforeEach + void setUp() { + petPlant = PetPlant.builder() + .name("이름") + .plantType("민들레") + .state(PetPlantState.IN_PROGRESS) + .userId(loginUser.getId()) + .build(); + petPlantRepository.save(petPlant); + + dailyRecord = DailyRecord.builder() + .title("오늘의 일기 7/3") + .writeDate(LocalDate.of(2025, 7, 3)) + .state(DailyRecordState.IN_PROGRESS) + .petPlantId(petPlant.getId()) + .build(); + + contents = List.of(DailyRecordContent.builder() + .type(DailyRecordContentType.TEXT) + .content("3cm 컸다.") + .build()); + + dailyRecord.updateContent(contents); + dailyRecordRepository.save(dailyRecord); + } + + @Test + @DisplayName("사용자는 자신이 작성한 오늘의 기록을 조회할 수 있다.") + void getDailyRecord1() throws Exception { + //when + ResultActions perform = mockMvc.perform( + get("/api/pet-plants/daily-record/{dailyRecord}", dailyRecord.getId()) + .header(HttpHeaders.AUTHORIZATION, accessToken)); + + //then + perform.andExpect(status().isOk()) + .andExpect(jsonPath("$.data.title").value(dailyRecord.getTitle())) + .andExpect(jsonPath("$.data.content.size()").value(1)) + .andExpect(jsonPath("$.data.content[0].value").value(contents.get(0).getContent())); + } + } + } \ No newline at end of file diff --git a/src/test/java/swyp/team5/greening/petPlant/controller/PetPlantControllerTest.java b/src/test/java/swyp/team5/greening/petPlant/controller/PetPlantControllerTest.java index d92a4ca..37ee33e 100644 --- a/src/test/java/swyp/team5/greening/petPlant/controller/PetPlantControllerTest.java +++ b/src/test/java/swyp/team5/greening/petPlant/controller/PetPlantControllerTest.java @@ -13,6 +13,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.test.web.servlet.ResultActions; import swyp.team5.greening.petPlant.domain.entity.DailyRecord; +import swyp.team5.greening.petPlant.domain.entity.DailyRecordState; import swyp.team5.greening.petPlant.domain.entity.PetPlant; import swyp.team5.greening.petPlant.domain.entity.PetPlantState; import swyp.team5.greening.petPlant.domain.entity.Watering; @@ -71,6 +72,7 @@ void init() { day5DailyRecord = DailyRecord.builder() .title("제목") .writeDate(day5) + .state(DailyRecordState.IN_PROGRESS) .petPlantId(petPlant.getId()) .build(); dailyRecordRepository.save(day5DailyRecord);