Skip to content

Commit aa806f0

Browse files
authored
Merge pull request #385 from depromeet/develop
v1.3.5
2 parents 4a4d672 + 1108c71 commit aa806f0

File tree

8 files changed

+176
-6
lines changed

8 files changed

+176
-6
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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.MeaningfulRetrospectMemberResponse;
7+
import org.layer.admin.retrospect.controller.dto.RetrospectStayTimeResponse;
8+
import org.layer.admin.retrospect.service.AdminRetrospectService;
9+
import org.springframework.http.ResponseEntity;
10+
import org.springframework.web.bind.annotation.GetMapping;
11+
import org.springframework.web.bind.annotation.RequestParam;
12+
import org.springframework.web.bind.annotation.RestController;
13+
14+
import lombok.RequiredArgsConstructor;
15+
16+
@RestController
17+
@RequiredArgsConstructor
18+
public class AdminRetrospectController {
19+
20+
private final AdminRetrospectService adminRetrospectService;
21+
22+
@GetMapping("/admin/retrospect/stay-time")
23+
public ResponseEntity<List<RetrospectStayTimeResponse>> getAllRetrospectStayTime(
24+
@RequestParam(name = "startDate") LocalDateTime startDate,
25+
@RequestParam(name = "endDate") LocalDateTime endDate) {
26+
27+
List<RetrospectStayTimeResponse> responses = adminRetrospectService.getAllRetrospectStayTime(
28+
startDate, endDate);
29+
30+
return ResponseEntity.ok().body(responses);
31+
}
32+
33+
@GetMapping("/admin/retrospect/meaningful")
34+
public ResponseEntity<MeaningfulRetrospectMemberResponse> getAllMeaningfulRetrospect(
35+
@RequestParam(name = "startDate") LocalDateTime startDate,
36+
@RequestParam(name = "endDate") LocalDateTime endDate,
37+
@RequestParam(name = "retrospectLength") int retrospectLength,
38+
@RequestParam(name = "retrospectCount") int retrospectCount) {
39+
40+
MeaningfulRetrospectMemberResponse response = adminRetrospectService.getAllMeaningfulRetrospect(
41+
startDate, endDate, retrospectLength, retrospectCount);
42+
43+
return ResponseEntity.ok().body(response);
44+
}
45+
}
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 MeaningfulRetrospectMemberResponse(
4+
long meaningfulMemberCount,
5+
long totalMemberCount
6+
) {
7+
}
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: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,38 @@
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;
78
import org.springframework.data.jpa.repository.JpaRepository;
9+
import org.springframework.data.jpa.repository.Query;
810
import org.springframework.data.repository.query.Param;
911

1012
public interface AdminRetrospectRepository extends JpaRepository<AdminRetrospectAnswerHistory, Long> {
1113

12-
Optional<AdminRetrospectAnswerHistory> findByMemberIdAndSpaceIdAndRetrospectId(
13-
@Param("memberId") Long memberId,
14-
@Param("spaceId") Long spaceId,
15-
@Param("retrospectId") Long retrospectId
14+
Optional<AdminRetrospectAnswerHistory> findTopByMemberIdAndSpaceIdAndRetrospectIdOrderByAnswerStartTimeDesc(
15+
Long memberId, Long spaceId, Long retrospectId);
16+
17+
@Query("""
18+
SELECT a.memberId
19+
FROM AdminRetrospectAnswerHistory a
20+
WHERE LENGTH(a.answerContent) >= :minLength
21+
AND a.eventTime BETWEEN :start AND :end
22+
GROUP BY a.memberId
23+
HAVING COUNT(a) >= :minCount
24+
""")
25+
List<Long> findMeaningfulMemberIds(
26+
@Param("start") LocalDateTime start,
27+
@Param("end") LocalDateTime end,
28+
@Param("minLength") int minLength,
29+
@Param("minCount") int minCount);
30+
31+
// 엣지 케이스로 답변 종료시간이 없는 경우도 있을 수 있기에 필터링한다.
32+
List<AdminRetrospectAnswerHistory> findAllByEventTimeBetweenAndAnswerEndTimeIsNotNull(
33+
LocalDateTime startTime, LocalDateTime endTime);
34+
35+
void deleteByMemberIdAndSpaceIdAndRetrospectId(
36+
Long memberId, Long spaceId, Long retrospectId
1637
);
1738
}

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,17 @@
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.member.repository.AdminMemberRepository;
12+
import org.layer.admin.retrospect.controller.dto.MeaningfulRetrospectMemberResponse;
13+
import org.layer.admin.retrospect.controller.dto.RetrospectStayTimeResponse;
614
import org.layer.admin.retrospect.entity.AdminRetrospectAnswerHistory;
15+
import org.layer.admin.retrospect.enums.AnswerTimeRange;
716
import org.layer.admin.retrospect.repository.AdminRetrospectRepository;
817
import org.layer.event.retrospect.WriteRetrospectEndEvent;
918
import org.layer.event.retrospect.WriteRetrospectStartEvent;
@@ -18,10 +27,44 @@
1827
public class AdminRetrospectService {
1928

2029
private final AdminRetrospectRepository adminRetrospectRepository;
30+
private final AdminMemberRepository adminMemberRepository;
31+
32+
public MeaningfulRetrospectMemberResponse getAllMeaningfulRetrospect(
33+
LocalDateTime startTime, LocalDateTime endTime, int retrospectLength, int retrospectCount) {
34+
List<Long> meaningfulMemberIds = adminRetrospectRepository.findMeaningfulMemberIds(
35+
startTime, endTime, retrospectLength, retrospectCount);
36+
37+
long totalMemberCount = adminMemberRepository.count();
38+
39+
return new MeaningfulRetrospectMemberResponse(meaningfulMemberIds.size(), totalMemberCount);
40+
}
41+
42+
public List<RetrospectStayTimeResponse> getAllRetrospectStayTime(
43+
LocalDateTime startTime, LocalDateTime endTime) {
44+
List<AdminRetrospectAnswerHistory> retrospectAnswerHistories = adminRetrospectRepository.findAllByEventTimeBetweenAndAnswerEndTimeIsNotNull(
45+
startTime, endTime);
46+
47+
Map<AnswerTimeRange, Long> countMap = new HashMap<>();
48+
retrospectAnswerHistories.forEach(history -> {
49+
AnswerTimeRange range = AnswerTimeRange.from(history.getAnswerTime());
50+
countMap.put(range, countMap.getOrDefault(range, 0L) + 1);
51+
});
52+
53+
List<RetrospectStayTimeResponse> response = new ArrayList<>();
54+
for (AnswerTimeRange range : AnswerTimeRange.values()) {
55+
Long count = countMap.getOrDefault(range, 0L);
56+
response.add(new RetrospectStayTimeResponse(range.getLabel(), count));
57+
}
58+
59+
return response;
60+
}
2161

2262
@Transactional(propagation = REQUIRES_NEW)
2363
@Async
2464
public void saveRetrospectAnswerHistory(WriteRetrospectStartEvent event) {
65+
adminRetrospectRepository.deleteByMemberIdAndSpaceIdAndRetrospectId(event.memberId(), event.spaceId(),
66+
event.retrospectId());
67+
2568
AdminRetrospectAnswerHistory retrospectAnswerHistory = AdminRetrospectAnswerHistory.builder()
2669
.eventTime(event.eventTime())
2770
.memberId(event.memberId())
@@ -38,7 +81,8 @@ public void saveRetrospectAnswerHistory(WriteRetrospectStartEvent event) {
3881
@Async
3982
public void updateRetrospectAnswerHistory(WriteRetrospectEndEvent event) {
4083

41-
adminRetrospectRepository.findByMemberIdAndSpaceIdAndRetrospectId(event.memberId(), event.spaceId(), event.retrospectId())
84+
adminRetrospectRepository.findTopByMemberIdAndSpaceIdAndRetrospectIdOrderByAnswerStartTimeDesc(
85+
event.memberId(), event.spaceId(), event.retrospectId())
4286
.ifPresentOrElse(
4387
history -> {
4488
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)