Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public record FindAllCommentResponseDto(

String comment,

@JsonFormat(pattern = "YYYY-MM-DD'T'hh:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd'T'hh:mm:ss")
LocalDateTime createdAt,

boolean isWriter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand All @@ -17,8 +18,10 @@
import swyp.team5.greening.common.dto.response.ApiResponseDto;
import swyp.team5.greening.common.resolver.LogIn;
import swyp.team5.greening.petPlant.dto.request.CreatePetPlantRequestDto;
import swyp.team5.greening.petPlant.dto.request.FindPetPlantDateRequestDto;
import swyp.team5.greening.petPlant.dto.response.CreatePetPlantResponseDto;
import swyp.team5.greening.petPlant.dto.response.FindAllPetPlantResponseDto;
import swyp.team5.greening.petPlant.dto.response.FindPetPlantDateResponseDto;
import swyp.team5.greening.petPlant.service.PetPlantCommandService;
import swyp.team5.greening.petPlant.service.PetPlantQueryService;

Expand Down Expand Up @@ -58,4 +61,17 @@ public void deletePetPlant(
petPlantCommandService.deletePetPlant(userId, petPlantId);
}

@Operation(summary = "특정 애완 식물 특정 월 정보 조회 API")
@GetMapping("/{petPlantId}")
@ResponseStatus(HttpStatus.OK)
public ApiResponseDto<List<FindPetPlantDateResponseDto>> getMyPetPlantCalender(
@LogIn Long userId,
@PathVariable Long petPlantId,
@ModelAttribute FindPetPlantDateRequestDto requestDto
) {
return ApiResponseDto.of(petPlantQueryService.findMyPetPlantCalender(
userId, petPlantId, requestDto.year(), requestDto.month()
));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package swyp.team5.greening.petPlant.domain.repository;

import java.time.LocalDate;
import java.util.List;
import swyp.team5.greening.petPlant.domain.entity.DailyRecord;

public interface DailyRecordQueryRepository {

List<DailyRecord> findByPetPlantAndWriteDate(
Long petPlantId,
LocalDate startDate,
LocalDate endDate
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package swyp.team5.greening.petPlant.domain.repository;

import java.time.LocalDate;
import java.util.List;
import swyp.team5.greening.petPlant.domain.entity.Watering;

public interface WateringQueryRepository {

List<Watering> findByPetPlantAndWriteDate(
Long petPlantId,
LocalDate startDate,
LocalDate endDate
);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package swyp.team5.greening.petPlant.dto.request;

import jakarta.validation.constraints.NotNull;

public record FindPetPlantDateRequestDto(
@NotNull
Integer year,

@NotNull
Integer month
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package swyp.team5.greening.petPlant.dto.response;

import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDate;

public record FindPetPlantDateResponseDto(
@JsonFormat(pattern = "yyyy-MM-dd")
LocalDate date,

Boolean watering,

Long dailyRecordId
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package swyp.team5.greening.petPlant.infrastructure;

import java.time.LocalDate;
import java.util.List;
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;

public interface DailyRecordJpaQueryRepository extends JpaRepository<DailyRecord, Long>,
DailyRecordQueryRepository {

@Override
@Query("""
SELECT dailyRecord
FROM DailyRecord dailyRecord
WHERE dailyRecord.petPlantId = :petPlantId
AND dailyRecord.writeDate >= :startDate
AND dailyRecord.writeDate < :endDate
ORDER BY dailyRecord.writeDate asc
""")
List<DailyRecord> findByPetPlantAndWriteDate(
@Param("petPlantId") Long petPlantId,
@Param("startDate") LocalDate startDate,
@Param("endDate") LocalDate endDate
);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package swyp.team5.greening.petPlant.infrastructure;

import java.time.LocalDate;
import java.util.List;
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.Watering;
import swyp.team5.greening.petPlant.domain.repository.WateringQueryRepository;

public interface WateringJpaQueryRepository extends JpaRepository<Watering, Long>,
WateringQueryRepository {

@Override
@Query("""
SELECT watering
FROM Watering watering
WHERE watering.petPlantId = :petPlantId
AND watering.writeDate >= :startDate
AND watering.writeDate < :endDate
ORDER BY watering.writeDate asc
""")
List<Watering> findByPetPlantAndWriteDate(
@Param("petPlantId") Long petPlantId,
@Param("startDate") LocalDate startDate,
@Param("endDate") LocalDate endDate
);

}
Original file line number Diff line number Diff line change
@@ -1,21 +1,108 @@
package swyp.team5.greening.petPlant.service;

import java.time.LocalDate;
import java.time.YearMonth;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
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.entity.PetPlantState;
import swyp.team5.greening.petPlant.domain.entity.Watering;
import swyp.team5.greening.petPlant.domain.repository.DailyRecordQueryRepository;
import swyp.team5.greening.petPlant.domain.repository.PetPlantQueryRepository;
import swyp.team5.greening.petPlant.domain.repository.PetPlantRepository;
import swyp.team5.greening.petPlant.domain.repository.WateringQueryRepository;
import swyp.team5.greening.petPlant.dto.response.FindAllPetPlantResponseDto;
import swyp.team5.greening.petPlant.dto.response.FindPetPlantDateResponseDto;
import swyp.team5.greening.petPlant.exception.PetPlantExceptionMessage;

@Service
@RequiredArgsConstructor
public class PetPlantQueryService {

private final PetPlantRepository petPlantRepository;
private final PetPlantQueryRepository petPlantQueryRepository;

private final DailyRecordQueryRepository dailyRecordQueryRepository;
private final WateringQueryRepository wateringQueryRepository;

//나의 애완 식물 목록 조회
@Transactional(readOnly = true)
public List<FindAllPetPlantResponseDto> findMyPetPlants(Long userId) {
return petPlantQueryRepository.findMyPetPlants(userId);
}

//특정 애완 식물 특정 월 정보 조회
//1. 애완 식물 조회
//2. 사용자 유효성 검증
//3. 해당 월에 대한 물 주기 스탬프 목록 조회
//4. 해당 월에 대한 오늘의 일기 식별자 조회
//5. 조합
@Transactional(readOnly = true)
public List<FindPetPlantDateResponseDto> findMyPetPlantCalender(
Long userId,
Long petPlantId,
Integer year,
Integer month
) {
//1
PetPlant petPlant = petPlantRepository.findByIdAndState(petPlantId,
PetPlantState.IN_PROGRESS)
.orElseThrow(() -> new GreeningGlobalException(
PetPlantExceptionMessage.NOT_FOUND_PET_PLANT));

//2
if (!Objects.equals(petPlant.getUserId(), userId)) {
throw new GreeningGlobalException(
PetPlantExceptionMessage.BAD_REQUEST_PET_PLANT_WRITER);
}

YearMonth requestDate = YearMonth.of(year, month);

LocalDate start = requestDate.atDay(1);
LocalDate end = requestDate.plusMonths(1).atDay(1);

//3
List<DailyRecord> dailyRecordsByWriteDate = dailyRecordQueryRepository
.findByPetPlantAndWriteDate(petPlantId, start, end);

//4
List<Watering> wateringByWriteDate = wateringQueryRepository
.findByPetPlantAndWriteDate(petPlantId, start, end);

//5
//날짜 별 일기 정보 조회
Map<LocalDate, Long> dailyRecordIdMap = dailyRecordsByWriteDate.stream()
.collect(Collectors.toMap(DailyRecord::getWriteDate, DailyRecord::getId));

//날짜 별 물 주기 스탬프 조회
Set<LocalDate> wateredDates = wateringByWriteDate.stream()
.map(Watering::getWriteDate)
.collect(Collectors.toSet());

//해당 월에 대한 정보 존재 날짜 집합 정리
Set<LocalDate> allDates = new HashSet<>();
allDates.addAll(dailyRecordIdMap.keySet());
allDates.addAll(wateredDates);

//조합
return allDates.stream()
.map(date -> {
Long dailyRecordId = dailyRecordIdMap.getOrDefault(date, -1L);
boolean isWatered = wateredDates.contains(date);
return new FindPetPlantDateResponseDto(date, isWatered, dailyRecordId);
})
.sorted(Comparator.comparing(FindPetPlantDateResponseDto::date)) // 오름차순 정렬
.toList();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package swyp.team5.greening.petPlant.controller;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
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.PetPlant;
import swyp.team5.greening.petPlant.domain.entity.PetPlantState;
import swyp.team5.greening.petPlant.domain.entity.Watering;
import swyp.team5.greening.petPlant.domain.repository.DailyRecordRepository;
import swyp.team5.greening.petPlant.domain.repository.PetPlantRepository;
import swyp.team5.greening.petPlant.domain.repository.WateringRepository;
import swyp.team5.greening.support.ApiTestSupport;

class PetPlantControllerTest extends ApiTestSupport {

@Autowired
private PetPlantRepository petPlantRepository;

@Autowired
private WateringRepository wateringRepository;

@Autowired
private DailyRecordRepository dailyRecordRepository;

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

@BeforeEach
void init() {
petPlantRepository.deleteAll();
wateringRepository.deleteAll();
dailyRecordRepository.deleteAll();
}

@Nested
@DisplayName("사용자는 애완 식물을 1개 등록해 놓은 상태이다. 7월 3일에 물을 주었고, 7월 5일에 오늘의 일기를 작성하였다.")
class TestCase1 {

PetPlant petPlant;
Watering day3Watering;
DailyRecord day5DailyRecord;

LocalDate day3 = LocalDate.of(2025, 7, 3);
LocalDate day5 = LocalDate.of(2025, 7, 5);

@BeforeEach
void init() {
petPlant = PetPlant.builder()
.name("기요미")
.plantType("민들레")
.state(PetPlantState.IN_PROGRESS)
.userId(loginUser.getId())
.build();
petPlantRepository.save(petPlant);

day3Watering = Watering.builder()
.petPlantId(petPlant.getId())
.writeDate(day3)
.build();
wateringRepository.save(day3Watering);

day5DailyRecord = DailyRecord.builder()
.title("제목")
.writeDate(day5)
.petPlantId(petPlant.getId())
.build();
dailyRecordRepository.save(day5DailyRecord);
}

@Test
@DisplayName("7월 3일과 7월 5일의 정보가 잘 조회된다.")
void getMyPetPlantCalender1() throws Exception {
//when
ResultActions perform = mockMvc.perform(
get("/api/pet-plants/{petPlantId}", petPlant.getId())
.header(HttpHeaders.AUTHORIZATION, accessToken)
.param("year", "2025")
.param("month", "7"));

//then
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.data.size()").value(2))
.andExpect(jsonPath("$.data[0].date").value(day3.format(formatter)))
.andExpect(jsonPath("$.data[0].watering").value(true))
.andExpect(jsonPath("$.data[0].dailyRecordId").value(-1))
.andExpect(jsonPath("$.data[1].date").value(day5.format(formatter)))
.andExpect(jsonPath("$.data[1].watering").value(false))
.andExpect(jsonPath("$.data[1].dailyRecordId").value(day5DailyRecord.getId()));
}

@Test
@DisplayName("다른 월로 조회할 경우, 조회되지 않는다.")
void getMyPetPlantCalender2() throws Exception {
//when
ResultActions perform = mockMvc.perform(
get("/api/pet-plants/{petPlantId}", petPlant.getId())
.header(HttpHeaders.AUTHORIZATION, accessToken)
.param("year", "2025")
.param("month", "6"));

//then
perform.andExpect(status().isOk())
.andExpect(jsonPath("$.data.size()").value(0));
}
}
}
Loading