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

[Feature] 몰입도 기록 API 구현 #80

Merged
merged 9 commits into from
Aug 14, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package timify.com.common.apiPayload.exception.handler;

import timify.com.common.apiPayload.code.BaseErrorCode;
import timify.com.common.apiPayload.exception.GeneralException;

public class StudyTimeHandler extends GeneralException {
public StudyTimeHandler(BaseErrorCode code) {
super(code);
}
}
4 changes: 2 additions & 2 deletions src/main/java/timify/com/member/domain/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import timify.com.domain.MemberMission;
import timify.com.studytime.domain.StudyTime;
import timify.com.todo.domain.Todo;
import timify.com.domain.common.BaseDateTimeEntity;
import timify.com.study.domain.StudyMethod;
import timify.com.study.domain.StudyPlace;
import timify.com.study.domain.StudyType;
import timify.com.studytime.domain.StudyTime;
import timify.com.subject.domain.Subject;
import timify.com.todo.domain.Todo;

@Entity
@Getter
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/timify/com/studytime/StudyTimeConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package timify.com.studytime;

import static timify.com.utils.DateTimeUtil.stringToLocalTime;

import java.time.Duration;
import timify.com.studytime.domain.StudyTime;
import timify.com.studytime.dto.StudyTimeRequest.studyTimeRequest;
import timify.com.studytime.dto.StudyTimeResponse;

public class StudyTimeConverter {

public static StudyTime toStudyTime(studyTimeRequest request, double temp) {

return StudyTime.builder()
.startTime(stringToLocalTime(request.getStartTime()))
.endTime(stringToLocalTime(request.getEndTime()))
.grade(request.getGrade())
.temp(temp)
.build();

}

public static StudyTimeResponse.studyTimeDto toStudyTimeDto(StudyTime studyTime) {
return StudyTimeResponse.studyTimeDto.builder()
.studyTimeId(studyTime.getId())
.startTime(studyTime.getStartTime())
.endTime(studyTime.getEndTime())
.totalTime((int) Duration.between(studyTime.getStartTime(), studyTime.getEndTime()).toMinutes())
.temp(studyTime.getTemp())
.build();
BaekJaehyuk marked this conversation as resolved.
Show resolved Hide resolved
}

}
60 changes: 60 additions & 0 deletions src/main/java/timify/com/studytime/StudyTimeService.java
BaekJaehyuk marked this conversation as resolved.
Show resolved Hide resolved
BaekJaehyuk marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package timify.com.studytime;

import static timify.com.common.apiPayload.code.status.ErrorStatus.STUDY_TYPE_NOT_FOUND;
import static timify.com.utils.DateTimeUtil.stringToLocalTime;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import timify.com.common.apiPayload.exception.handler.StudyTimeHandler;
import timify.com.member.domain.Member;
import timify.com.studytime.domain.StudyTime;
import timify.com.studytime.dto.StudyTimeRequest.studyTimeRequest;
import timify.com.studytime.repository.StudyTimeRepository;
import timify.com.todo.domain.Todo;
import timify.com.todo.repository.TodoRepository;

@Service
@RequiredArgsConstructor
public class StudyTimeService {

private final static double DEFAULT_TEMP = 50d;
private final StudyTimeRepository studyTimeRepository;
private final TodoRepository todoRepository;

@Transactional
public StudyTime startTodo(Member member, Long todoId, studyTimeRequest request) {

Todo todo = todoRepository.findById(todoId)
BaekJaehyuk marked this conversation as resolved.
Show resolved Hide resolved
.orElseThrow(() -> new StudyTimeHandler(STUDY_TYPE_NOT_FOUND));

double temp = calculateTemp(request);

StudyTime studyTime = StudyTimeConverter.toStudyTime(request, temp);

studyTime.associateMember(member);
studyTime.associateSubject(todo.getSubject());
studyTime.associateTodo(todo);

return studyTimeRepository.save(studyTime);
}

private double calculateTemp(studyTimeRequest request) {
LocalDateTime startTime = stringToLocalTime(request.getStartTime());
LocalDateTime endTime = stringToLocalTime(request.getEndTime());

long countStudyTime = studyTimeRepository.count();
BaekJaehyuk marked this conversation as resolved.
Show resolved Hide resolved

if (countStudyTime == 0) {
int minutes = (int) Duration.between(startTime, endTime).toMinutes();
return DEFAULT_TEMP + minutes * request.getGrade().getScore();
}

int minutes = (int) Duration.between(startTime, endTime).toMinutes();
return minutes * request.getGrade().getScore();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package timify.com.studytime.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import timify.com.auth.annotation.AuthMember;
import timify.com.common.apiPayload.ApiResponse;
import timify.com.member.domain.Member;
import timify.com.studytime.dto.StudyTimeRequest.studyTimeRequest;
import timify.com.studytime.dto.StudyTimeResponse.studyTimeDto;

@Tag(name = "StudyTime", description = "StudyTime 관련 API")
public interface StudyTimeController {

@Operation(summary = "할 일 스톱워치 API", description = "할 일 스톱워치 API 입니다.")
BaekJaehyuk marked this conversation as resolved.
Show resolved Hide resolved
@Parameters(value = {
@Parameter(name = "todoId", description = "몰입도를 기록할 할 일에 해당하는 todoId 을 입력해 주세요.")
})
ApiResponse<studyTimeDto> startTodo(@AuthMember Member member,
@PathVariable Long todoId,
@RequestBody studyTimeRequest request);

@Operation(summary = "시간 기록 삭제 API", description = "시간 기록 삭제 API 입니다.")
@Parameters(value = {
@Parameter(name = "studyTimeId", description = "기록을 삭제할 StudyTimeId 을 입력해 주세요.")
})
ApiResponse<studyTimeDto> deleteStudyTime(@AuthMember Member member,
@PathVariable Long studyTimeId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package timify.com.studytime.controller;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import timify.com.auth.annotation.AuthMember;
import timify.com.common.apiPayload.ApiResponse;
import timify.com.common.apiPayload.code.status.SuccessStatus;
import timify.com.member.domain.Member;
import timify.com.studytime.StudyTimeConverter;
import timify.com.studytime.StudyTimeService;
import timify.com.studytime.domain.StudyTime;
import timify.com.studytime.dto.StudyTimeRequest.studyTimeRequest;
import timify.com.studytime.dto.StudyTimeResponse.studyTimeDto;

@RestController
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/v1/subject/todo/{todoId}")
public class StudyTimeControllerImpl implements StudyTimeController{

private final StudyTimeService studyTimeService;

@PostMapping("/study_time/start")
public ApiResponse<studyTimeDto> startTodo(@AuthMember Member member,
@PathVariable Long todoId,
@RequestBody studyTimeRequest request) {

StudyTime studyTime = studyTimeService.startTodo(member, todoId, request);

return ApiResponse.onSuccess(StudyTimeConverter.toStudyTimeDto(studyTime));
}

@DeleteMapping("/study_time/delete")
public ApiResponse<studyTimeDto> deleteStudyTime(Member member, Long studyTimeId) {
return null;
}
}
51 changes: 50 additions & 1 deletion src/main/java/timify/com/studytime/domain/StudyTime.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class StudyTime extends BaseDateTimeEntity {
private LocalDateTime endTime;

@Column(nullable = false)
private double score;
private StudyTimeGrade grade; // 최상
BaekJaehyuk marked this conversation as resolved.
Show resolved Hide resolved

@Column(nullable = false)
private double temp;
Expand All @@ -54,4 +54,53 @@ public class StudyTime extends BaseDateTimeEntity {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "todo_id", nullable = false)
private Todo todo;

public void associateMember(Member member) {
if (this.member != null) {
this.member.getStudyTimeList().remove(this);
}
this.member = member;
this.member.getStudyTimeList().add(this);
}

public void disassociateMember(Member member) {
if (member != null) {
member.getStudyTimeList().remove(this);
this.member = null;
}
}

public void associateSubject(Subject subject) {
if (this.subject != null) {
this.subject.getStudyTimeList().remove(this);
}
this.subject = subject;
this.subject.getStudyTimeList().add(this);
}

// 연관관계 해제 메소드
public void disassociateSubject(Subject subject) {
if (subject != null) {
subject.getStudyTimeList().remove(this);
this.subject = null;
}
}

public void associateTodo(Todo todo) {
if (this.todo != null) {
this.todo.getStudyTimeList().remove(this);
}
this.todo = todo;
this.todo.getStudyTimeList().add(this);
}

// 연관관계 해제 메소드
public void disassociateTodo(Todo todo) {
if (todo != null) {
todo.getStudyTimeList().remove(this);
this.todo = null;
}
}


}
19 changes: 19 additions & 0 deletions src/main/java/timify/com/studytime/domain/StudyTimeGrade.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package timify.com.studytime.domain;

import lombok.Getter;

@Getter
public enum StudyTimeGrade {
EXCELLENT(2.0), // 최상
GOOD(1.5), // 상
AVERAGE(1.2), // 중
BELOW_AVERAGE(1.0), // 하
POOR(1.0); // 최하

private double score;

StudyTimeGrade(double score) {
this.score = score;
}

}
23 changes: 23 additions & 0 deletions src/main/java/timify/com/studytime/dto/StudyTimeRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package timify.com.studytime.dto;

import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.NoArgsConstructor;
import timify.com.studytime.domain.StudyTimeGrade;

public class StudyTimeRequest {

@Getter
@NoArgsConstructor
public static class studyTimeRequest {

@NotBlank
String startTime;

@NotBlank
String endTime;
BaekJaehyuk marked this conversation as resolved.
Show resolved Hide resolved

@NotBlank
StudyTimeGrade grade; //최상
}
}
32 changes: 32 additions & 0 deletions src/main/java/timify/com/studytime/dto/StudyTimeResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package timify.com.studytime.dto;

import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import timify.com.studytime.domain.StudyTimeGrade;

public class StudyTimeResponse {

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class studyTimeDto {

Long studyTimeId;

LocalDateTime startTime;

LocalDateTime endTime;

int totalTime;

double temp;

StudyTimeGrade score;

Double status;
}
}
4 changes: 2 additions & 2 deletions src/main/java/timify/com/subject/domain/Subject.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import timify.com.domain.common.BaseDateTimeEntity;
import timify.com.member.domain.Member;
import timify.com.studytime.domain.StudyTime;
import timify.com.todo.domain.Todo;
import timify.com.domain.common.BaseDateTimeEntity;
import timify.com.member.domain.Member;

@Entity
@Getter
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/timify/com/todo/domain/Todo.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@ public class Todo extends BaseDateTimeEntity {
private Subject subject;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "study_type_id", nullable = false)
@JoinColumn(name = "study_type_id")
private StudyType studyType;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "study_method_id", nullable = false)
@JoinColumn(name = "study_method_id")
private StudyMethod studyMethod;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "study_place_id", nullable = false)
@JoinColumn(name = "study_place_id")
private StudyPlace studyPlace;

// studyTime 양방향 매핑
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/timify/com/utils/DateTimeUtil.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package timify.com.utils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

public class DateTimeUtil {
Expand All @@ -16,4 +18,16 @@ public static LocalDate stringToLocalDate(String dateString) {
return LocalDate.parse(dateString, formatter);
}

/**
* HHMMSS 형식의 string을 LocalTime type으로 변환
*
* @param timeString
* @return
*/
public static LocalDateTime stringToLocalTime(String timeString) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HHmmss");
LocalTime localTime = LocalTime.parse(timeString, formatter);
return LocalDateTime.of(LocalDate.now(), localTime);
BaekJaehyuk marked this conversation as resolved.
Show resolved Hide resolved
}

}