Skip to content

Commit be17baf

Browse files
authored
feat: 회고 작성 체류시간 조회 (#382)
* feat: 회고 작성 체류 시간 조회 기능 구현 * chore: 예외타입 추가 * fix: 중복데이터 방지 코드 추가
1 parent 1c9c521 commit be17baf

File tree

7 files changed

+126
-6
lines changed

7 files changed

+126
-6
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.layer.admin.retrospect.controller;
2+
3+
import java.time.LocalDateTime;
4+
import java.util.List;
5+
6+
import org.layer.admin.retrospect.controller.dto.RetrospectStayTimeResponse;
7+
import org.layer.admin.retrospect.service.AdminRetrospectService;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.web.bind.annotation.GetMapping;
10+
import org.springframework.web.bind.annotation.RequestParam;
11+
import org.springframework.web.bind.annotation.RestController;
12+
13+
import lombok.RequiredArgsConstructor;
14+
15+
@RestController
16+
@RequiredArgsConstructor
17+
public class AdminRetrospectController {
18+
19+
private final AdminRetrospectService adminRetrospectService;
20+
21+
@GetMapping("/admin/retrospect/stay-time")
22+
public ResponseEntity<List<RetrospectStayTimeResponse>> getAllRetrospectStayTime(
23+
@RequestParam(name = "startDate") LocalDateTime startDate,
24+
@RequestParam(name = "endDate") LocalDateTime endDate) {
25+
26+
List<RetrospectStayTimeResponse> responses = adminRetrospectService.getAllRetrospectStayTime(
27+
startDate, endDate);
28+
29+
return ResponseEntity.ok().body(responses);
30+
}
31+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.layer.admin.retrospect.controller.dto;
2+
3+
public record RetrospectStayTimeResponse(
4+
String answerTimeRangeLabel,
5+
long count
6+
) {
7+
}

layer-admin/src/main/java/org/layer/admin/retrospect/entity/AdminRetrospectAnswerHistory.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.layer.admin.retrospect.entity;
22

3+
import java.time.Duration;
34
import java.time.LocalDateTime;
45

56
import jakarta.persistence.Entity;
@@ -46,6 +47,15 @@ public void updateRetrospectCompleted (LocalDateTime answerEndTime, String answe
4647
this.answerContent = answerContent;
4748
}
4849

50+
public long getAnswerTime() {
51+
if (answerStartTime == null || answerEndTime == null) {
52+
return 0L;
53+
}
54+
Duration duration = Duration.between(answerEndTime, answerStartTime);
55+
56+
return duration.toMinutes();
57+
}
58+
4959
@Builder
5060
private AdminRetrospectAnswerHistory(LocalDateTime eventTime, Long memberId, String eventId, Long spaceId,
5161
Long retrospectId, LocalDateTime answerStartTime, LocalDateTime answerEndTime, String answerContent) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.layer.admin.retrospect.enums;
2+
3+
import lombok.Getter;
4+
import lombok.RequiredArgsConstructor;
5+
6+
@RequiredArgsConstructor
7+
@Getter
8+
public enum AnswerTimeRange {
9+
MIN_0_TO_5(0, 5, "0-5분"),
10+
MIN_5_TO_10(5, 10, "5-10분"),
11+
MIN_10_TO_15(10, 15, "10-15분"),
12+
MIN_15_TO_20(15, 20, "15-20분"),
13+
MIN_20_TO_25(20, 25, "20-25분"),
14+
MIN_25_TO_30(25, 30, "25-30분"),
15+
MIN_30_PLUS(30, Integer.MAX_VALUE, "30분 이상");
16+
17+
private final int min;
18+
private final int max;
19+
private final String label;
20+
21+
public static AnswerTimeRange from(long minutes) {
22+
for (AnswerTimeRange range : values()) {
23+
if (minutes >= range.min && minutes < range.max) {
24+
return range;
25+
}
26+
}
27+
throw new IllegalArgumentException("Unknown time range for minutes: " + minutes);
28+
}
29+
}
Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.layer.admin.retrospect.repository;
22

3-
3+
import java.time.LocalDateTime;
4+
import java.util.List;
45
import java.util.Optional;
56

67
import org.layer.admin.retrospect.entity.AdminRetrospectAnswerHistory;
@@ -9,9 +10,13 @@
910

1011
public interface AdminRetrospectRepository extends JpaRepository<AdminRetrospectAnswerHistory, Long> {
1112

12-
Optional<AdminRetrospectAnswerHistory> findByMemberIdAndSpaceIdAndRetrospectId(
13-
@Param("memberId") Long memberId,
14-
@Param("spaceId") Long spaceId,
15-
@Param("retrospectId") Long retrospectId
13+
Optional<AdminRetrospectAnswerHistory> findTopByMemberIdAndSpaceIdAndRetrospectIdOrderByAnswerStartTimeDesc(
14+
Long memberId, Long spaceId, Long retrospectId);
15+
16+
List<AdminRetrospectAnswerHistory> findAllByEventTimeBetween(
17+
LocalDateTime startTime, LocalDateTime endTime);
18+
19+
void deleteByMemberIdAndSpaceIdAndRetrospectId(
20+
Long memberId, Long spaceId, Long retrospectId
1621
);
1722
}

layer-admin/src/main/java/org/layer/admin/retrospect/service/AdminRetrospectService.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@
22

33
import static org.springframework.transaction.annotation.Propagation.*;
44

5+
import java.time.LocalDateTime;
6+
import java.util.ArrayList;
7+
import java.util.HashMap;
8+
import java.util.List;
9+
import java.util.Map;
510

11+
import org.layer.admin.retrospect.controller.dto.RetrospectStayTimeResponse;
612
import org.layer.admin.retrospect.entity.AdminRetrospectAnswerHistory;
13+
import org.layer.admin.retrospect.enums.AnswerTimeRange;
714
import org.layer.admin.retrospect.repository.AdminRetrospectRepository;
815
import org.layer.event.retrospect.WriteRetrospectEndEvent;
916
import org.layer.event.retrospect.WriteRetrospectStartEvent;
@@ -19,9 +26,32 @@ public class AdminRetrospectService {
1926

2027
private final AdminRetrospectRepository adminRetrospectRepository;
2128

29+
public List<RetrospectStayTimeResponse> getAllRetrospectStayTime(
30+
LocalDateTime startTime, LocalDateTime endTime) {
31+
List<AdminRetrospectAnswerHistory> retrospectAnswerHistories = adminRetrospectRepository.findAllByEventTimeBetween(
32+
startTime, endTime);
33+
34+
Map<AnswerTimeRange, Long> countMap = new HashMap<>();
35+
retrospectAnswerHistories.forEach(history -> {
36+
AnswerTimeRange range = AnswerTimeRange.from(history.getAnswerTime());
37+
countMap.put(range, countMap.getOrDefault(range, 0L) + 1);
38+
});
39+
40+
List<RetrospectStayTimeResponse> response = new ArrayList<>();
41+
for (AnswerTimeRange range : AnswerTimeRange.values()) {
42+
Long count = countMap.getOrDefault(range, 0L);
43+
response.add(new RetrospectStayTimeResponse(range.getLabel(), count));
44+
}
45+
46+
return response;
47+
}
48+
2249
@Transactional(propagation = REQUIRES_NEW)
2350
@Async
2451
public void saveRetrospectAnswerHistory(WriteRetrospectStartEvent event) {
52+
adminRetrospectRepository.deleteByMemberIdAndSpaceIdAndRetrospectId(event.memberId(), event.spaceId(),
53+
event.retrospectId());
54+
2555
AdminRetrospectAnswerHistory retrospectAnswerHistory = AdminRetrospectAnswerHistory.builder()
2656
.eventTime(event.eventTime())
2757
.memberId(event.memberId())
@@ -38,7 +68,8 @@ public void saveRetrospectAnswerHistory(WriteRetrospectStartEvent event) {
3868
@Async
3969
public void updateRetrospectAnswerHistory(WriteRetrospectEndEvent event) {
4070

41-
adminRetrospectRepository.findByMemberIdAndSpaceIdAndRetrospectId(event.memberId(), event.spaceId(), event.retrospectId())
71+
adminRetrospectRepository.findTopByMemberIdAndSpaceIdAndRetrospectIdOrderByAnswerStartTimeDesc(
72+
event.memberId(), event.spaceId(), event.retrospectId())
4273
.ifPresentOrElse(
4374
history -> {
4475
history.updateRetrospectCompleted(event.eventTime(), event.answerContent());

layer-api/src/main/java/org/layer/global/exception/GlobalExceptionHandler.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.springframework.http.converter.HttpMessageNotReadableException;
1414
import org.springframework.web.HttpRequestMethodNotSupportedException;
1515
import org.springframework.web.bind.MethodArgumentNotValidException;
16+
import org.springframework.web.bind.MissingServletRequestParameterException;
1617
import org.springframework.web.bind.annotation.ExceptionHandler;
1718
import org.springframework.web.bind.annotation.RestControllerAdvice;
1819
import org.springframework.web.servlet.resource.NoResourceFoundException;
@@ -74,4 +75,10 @@ public ResponseEntity<ExceptionResponse> handleHttpRequestMethodNotSupportedExce
7475
.body(ExceptionResponse.of(HttpStatus.METHOD_NOT_ALLOWED.name(), "지원하지 않는 HTTP 메서드입니다."));
7576
}
7677

78+
@ExceptionHandler(MissingServletRequestParameterException.class)
79+
public ResponseEntity<ExceptionResponse> handleMissingServletRequestParameterException(Exception e) {
80+
log.warn(String.format(LOG_FORMAT, e.getMessage()), e);
81+
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
82+
.body(ExceptionResponse.of(HttpStatus.BAD_REQUEST.name(), "유효하지 않은 값."));
83+
}
7784
}

0 commit comments

Comments
 (0)