Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

공부 잔디 기능 추가 #280

Merged
merged 6 commits into from
Mar 22, 2024
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
12 changes: 12 additions & 0 deletions my-garden-be/src/docs/asciidoc/dailyroutine/getStudyHours.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[[get-study-hours]]
=== GET [공부 시간 조회]

==== HTTP Request

include::{snippets}/daily-routine/get-study-hours/http-request.adoc[]
include::{snippets}/daily-routine/get-study-hours/request-headers.adoc[]

==== HTTP Response

include::{snippets}/daily-routine/get-study-hours/http-response.adoc[]
include::{snippets}/daily-routine/get-study-hours/response-fields.adoc[]
1 change: 1 addition & 0 deletions my-garden-be/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ include::dailyroutine/get.adoc[]
include::dailyroutine/post.adoc[]
include::dailyroutine/put.adoc[]
include::dailyroutine/delete.adoc[]
include::dailyroutine/getStudyHours.adoc[]

[[board-category]]
== Board Category [게시판 분류]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
import org.hyunggi.mygardenbe.dailyroutine.domain.TimeSplitter;
import org.hyunggi.mygardenbe.dailyroutine.service.DailyRoutineService;
import org.hyunggi.mygardenbe.dailyroutine.service.response.DailyRoutineResponse;
import org.hyunggi.mygardenbe.dailyroutine.service.response.DailyRoutineStudyHourResponse;
import org.hyunggi.mygardenbe.member.entity.MemberEntity;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

Expand Down Expand Up @@ -97,4 +99,9 @@ public ApiResponse<Long> deleteDailyRoutine(@PathVariable final Long timeBlockId

return ApiResponse.ok(deletedId);
}

@GetMapping("/study-hours")
public ApiResponse<List<DailyRoutineStudyHourResponse>> getStudyHours(@WithLoginUserEntity MemberEntity member) {
return ApiResponse.ok(dailyRoutineService.getStudyHours(LocalDate.now(), member));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
import org.hyunggi.mygardenbe.dailyroutine.entity.DailyRoutineEntity;
import org.hyunggi.mygardenbe.dailyroutine.repository.DailyRoutineRepository;
import org.hyunggi.mygardenbe.dailyroutine.service.response.DailyRoutineResponse;
import org.hyunggi.mygardenbe.dailyroutine.service.response.DailyRoutineStudyHourResponse;
import org.hyunggi.mygardenbe.member.entity.MemberEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -165,4 +168,33 @@ public Long deleteDailyRoutine(final Long timeBlockId, final MemberEntity member
dailyRoutineRepository.deleteById(dailyRoutineEntity.getId());
return timeBlockId;
}

/**
* 1년전에서 어제까지의 공부 시간 조회 (오늘 공부 시간은 미포함)
*
* @param today 오늘 날짜
* @param member 유저 Entity
* @return 공부 시간 목록
*/
public List<DailyRoutineStudyHourResponse> getStudyHours(final LocalDate today, final MemberEntity member) {
final LocalDateTime startDateTime = today.atStartOfDay().minusYears(1);
final LocalDateTime endDateTime = today.atTime(LocalTime.MAX).minusDays(1);

return getDailyRoutine(startDateTime, endDateTime, member).stream()
.filter(routine -> routine.isEqualType(RoutineType.STUDY))
.collect(Collectors.groupingBy(DailyRoutineResponse::getStartDate, Collectors.summingInt(DailyRoutineResponse::calculateMinutesBetweenStartAndEnd)))
.entrySet().stream()
.map(entry -> new DailyRoutineStudyHourResponse(entry.getKey(), convertMinToHour(entry.getValue())))
.toList();
}

/**
* 분을 시간으로 변환
*
* @param min 분
* @return 시간
*/
private int convertMinToHour(final int min) {
return min / 60;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package org.hyunggi.mygardenbe.dailyroutine.service.response;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Builder;
import org.hyunggi.mygardenbe.dailyroutine.domain.DailyRoutine;
import org.hyunggi.mygardenbe.dailyroutine.domain.RoutineType;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

/**
* 데일리 루틴 조회 응답
Expand Down Expand Up @@ -51,4 +56,36 @@ public static DailyRoutineResponse of(final Long id, final DailyRoutine dailyRou
.routineDescription(dailyRoutine.getRoutineDescription())
.build();
}

/**
* 루틴 시작 날짜 조회
*
* @return 루틴 시작 날짜
*/
@JsonIgnore
public String getStartDate() {
return startDateTime.split("T")[0];
}

/**
* 루틴 타입 비교
*
* @param type 루틴 타입
* @return 루틴 타입이 같은지 여부
*/
public boolean isEqualType(final RoutineType type) {
return routineType.equals(type.getDescription());
}

/**
* 루틴의 시작 시간과 종료 시간의 차이를 분으로 조회
*
* @return 루틴의 시작 시간과 종료 시간의 차이 (분)
*/
public int calculateMinutesBetweenStartAndEnd() {
final LocalDateTime start = LocalDateTime.parse(startDateTime);
final LocalDateTime end = LocalDateTime.parse(endDateTime);

return (int) start.until(end, ChronoUnit.MINUTES);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.hyunggi.mygardenbe.dailyroutine.service.response;

import com.fasterxml.jackson.annotation.JsonProperty;

/**
* 루틴의 공부 시간 조회 응답
*
* @param date 날짜
* @param studyHour 해당 날짜의 공부 시간
*/
public record DailyRoutineStudyHourResponse(
String date,
@JsonProperty("count")
Integer studyHour) {
public DailyRoutineStudyHourResponse {
validate(date, studyHour);
}

/**
* 루틴의 공부 시간 조회 응답 유효성 검증
*/
private static void validate(String date, Integer studyHour) {
if (date == null) {
throw new IllegalArgumentException("날짜는 null이 될 수 없습니다.");
}
if (studyHour == null || studyHour < 0) {
throw new IllegalArgumentException("공부 시간은 null이 될 수 없고 0보다 커야 합니다.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.hyunggi.mygardenbe.ControllerTestSupportWithMockUser;
import org.hyunggi.mygardenbe.dailyroutine.controller.request.PostRequest;
import org.hyunggi.mygardenbe.dailyroutine.service.response.DailyRoutineResponse;
import org.hyunggi.mygardenbe.dailyroutine.service.response.DailyRoutineStudyHourResponse;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
Expand Down Expand Up @@ -197,4 +198,28 @@ void deleteDailyRoutine() throws Exception {
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value(1L));
}

@Test
@DisplayName("공부 시간 목록을 조회한다.")
void getStudyHours() throws Exception {
//given
BDDMockito.given(dailyRoutineService.getStudyHours(any(), any()))
.willReturn(
List.of(
new DailyRoutineStudyHourResponse("2024-03-21", 3),
new DailyRoutineStudyHourResponse("2024-03-22", 4)
)
);

//when, then
mockMvc.perform(
get("/api/daily-routine/study-hours")
)
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").isArray())
.andExpect(jsonPath("$.data[0].date").value("2024-03-21"))
.andExpect(jsonPath("$.data[0].count").value(3))
.andExpect(jsonPath("$.data[1].date").value("2024-03-22"))
.andExpect(jsonPath("$.data[1].count").value(4));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.hyunggi.mygardenbe.dailyroutine.entity.DailyRoutineEntity;
import org.hyunggi.mygardenbe.dailyroutine.repository.DailyRoutineRepository;
import org.hyunggi.mygardenbe.dailyroutine.service.response.DailyRoutineResponse;
import org.hyunggi.mygardenbe.dailyroutine.service.response.DailyRoutineStudyHourResponse;
import org.hyunggi.mygardenbe.member.domain.Member;
import org.hyunggi.mygardenbe.member.entity.MemberEntity;
import org.hyunggi.mygardenbe.member.repository.MemberRepository;
Expand All @@ -19,6 +20,7 @@
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -95,7 +97,7 @@ void getDailyRoutine() {
final List<RoutineTime> routineTimes = List.of(routineTimeSample1, routineTimeSample2);
final RoutineType routineType = RoutineType.STUDY;
final String routineDescription = "자바 스터디";
dailyRoutineService.postDailyRoutine(routineTimes, routineType, routineDescription, member);
postRoutine(routineTimes, routineType, routineDescription);

//when
final List<DailyRoutineResponse> dailyRoutineResponses = dailyRoutineService.getDailyRoutine(
Expand Down Expand Up @@ -127,7 +129,7 @@ void getDailyRoutine_OnlyMine() {
final List<RoutineTime> routineTimes = List.of(routineTimeSample1, routineTimeSample2);
final RoutineType routineType = RoutineType.STUDY;
final String routineDescription = "자바 스터디";
dailyRoutineService.postDailyRoutine(routineTimes, routineType, routineDescription, member);
postRoutine(routineTimes, routineType, routineDescription);

//when
final List<DailyRoutineResponse> dailyRoutineResponses = dailyRoutineService.getDailyRoutine(
Expand Down Expand Up @@ -292,4 +294,46 @@ void deleteDailyRoutine_OnlyMine() {
.isInstanceOf(BusinessException.class)
.hasMessageContaining("본인의 DailyRoutine만 수정 및 삭제할 수 있습니다.");
}

@Test
@DisplayName("1년전에서 어제까지의 공부 시간 조회 (STUDY 타입만 조회, 오늘 공부 시간은 포함하지 않으며 1시간 단위로 계산)")
void getStudyHours() {
//given
final LocalDate today = LocalDate.of(2024, 3, 3);

final RoutineTime routineTimeSample1 = RoutineTime.of(
LocalDateTime.of(2024, 3, 1, 20, 0, 0),
LocalDateTime.of(2024, 3, 1, 22, 0, 0)
);
final RoutineType routineTypeExercise = RoutineType.EXERCISE;
final String routineDescriptionExercise = "운동";
postRoutine(List.of(routineTimeSample1), routineTypeExercise, routineDescriptionExercise);

final RoutineTime routineTimeSample2 = RoutineTime.of(
LocalDateTime.of(2024, 3, 1, 22, 0, 0),
LocalDateTime.of(2024, 3, 1, 23, 59, 59)
);
final RoutineTime routineTimeSample3 = RoutineTime.of(
LocalDateTime.of(2024, 3, 2, 0, 0, 0),
LocalDateTime.of(2024, 3, 2, 1, 13, 0)
);
final RoutineType routineType = RoutineType.STUDY;
final String routineDescription = "자바 스터디";
postRoutine(List.of(routineTimeSample2, routineTimeSample3), routineType, routineDescription);

//when
final List<DailyRoutineStudyHourResponse> studyHours = dailyRoutineService.getStudyHours(today, member);

//then
assertThat(studyHours).hasSize(2)
.extracting("date", "studyHour")
.containsExactlyInAnyOrder(
Tuple.tuple("2024-03-01", 1),
Tuple.tuple("2024-03-02", 1)
);
}

private void postRoutine(final List<RoutineTime> routineTimeSample3, final RoutineType routineTypeExercise, final String routineDescriptionExercise) {
dailyRoutineService.postDailyRoutine(routineTimeSample3, routineTypeExercise, routineDescriptionExercise, member);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.hyunggi.mygardenbe.dailyroutine.domain.DailyRoutine;
import org.hyunggi.mygardenbe.dailyroutine.domain.RoutineTime;
import org.hyunggi.mygardenbe.dailyroutine.domain.RoutineType;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -59,4 +60,64 @@ void of_withId() {
assertThat(dailyRoutineResponse.routineType()).isEqualTo("수면");
assertThat(dailyRoutineResponse.routineDescription()).isEqualTo("잠을 잔다");
}

@Test
@DisplayName("getStartDate()를 호출하면 DailyRoutine의 시작 날짜를 반환한다")
void getStartDate() {
//given
DailyRoutine dailyRoutine = DailyRoutine.of(
RoutineTime.of(
LocalDateTime.of(2021, 8, 1, 0, 0, 0),
LocalDateTime.of(2021, 8, 1, 1, 0, 0)
),
"SLEEP",
"잠을 잔다"
);

//when
DailyRoutineResponse dailyRoutineResponse = DailyRoutineResponse.of(dailyRoutine);

//then
assertThat(dailyRoutineResponse.getStartDate()).isEqualTo("2021-08-01");
}

@Test
@DisplayName("isEqualType()를 호출하면 DailyRoutine의 루틴 타입이 같은지 여부를 반환한다")
void isEqualType() {
//given
DailyRoutine dailyRoutine = DailyRoutine.of(
RoutineTime.of(
LocalDateTime.of(2021, 8, 1, 0, 0, 0),
LocalDateTime.of(2021, 8, 1, 1, 0, 0)
),
"SLEEP",
"잠을 잔다"
);

//when
DailyRoutineResponse dailyRoutineResponse = DailyRoutineResponse.of(dailyRoutine);

//then
assertThat(dailyRoutineResponse.isEqualType(RoutineType.SLEEP)).isTrue();
}

@Test
@DisplayName("calculateMinutesBetweenStartAndEnd()를 호출하면 DailyRoutine의 시작 시간과 종료 시간의 차이를 분으로 반환한다")
void calculateMinutesBetweenStartAndEnd() {
//given
DailyRoutine dailyRoutine = DailyRoutine.of(
RoutineTime.of(
LocalDateTime.of(2021, 8, 1, 0, 1, 0),
LocalDateTime.of(2021, 8, 1, 1, 12, 0)
),
"SLEEP",
"잠을 잔다"
);

//when
DailyRoutineResponse dailyRoutineResponse = DailyRoutineResponse.of(dailyRoutine);

//then
assertThat(dailyRoutineResponse.calculateMinutesBetweenStartAndEnd()).isEqualTo(71);
}
}
Loading
Loading