Skip to content

Commit d03ae18

Browse files
committed
feat: 방치율 조회 기능 구현
1 parent 22f9c30 commit d03ae18

File tree

8 files changed

+139
-25
lines changed

8 files changed

+139
-25
lines changed

layer-admin/src/main/java/org/layer/admin/common/CommonController.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import org.layer.admin.member.repository.AdminMemberRepository;
44
import org.layer.admin.retrospect.repository.AdminRetrospectAnswerRepository;
5-
import org.layer.admin.retrospect.repository.AdminRetrospectRepository;
5+
import org.layer.admin.retrospect.repository.AdminRetrospectHistoryRepository;
66
import org.layer.admin.space.repository.AdminSpaceRepository;
77
import org.springframework.http.ResponseEntity;
88
import org.springframework.web.bind.annotation.GetMapping;
@@ -17,7 +17,7 @@ public class CommonController {
1717
// 간단한 기능이기에 바로 repository를 사용합니다.
1818
private final AdminMemberRepository adminMemberRepository;
1919
private final AdminSpaceRepository adminSpaceRepository;
20-
private final AdminRetrospectRepository adminRetrospectRepository;
20+
private final AdminRetrospectHistoryRepository adminRetrospectHistoryRepository;
2121
private final AdminRetrospectAnswerRepository adminRetrospectAnswerRepository;
2222

2323

@@ -28,7 +28,7 @@ public ResponseEntity<OutlineResponse> getOutline() {
2828
new OutlineResponse(
2929
adminMemberRepository.count(),
3030
adminSpaceRepository.count(),
31-
adminRetrospectRepository.count(),
31+
adminRetrospectHistoryRepository.count(),
3232
adminRetrospectAnswerRepository.count()
3333
)
3434
);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.layer.admin.retrospect.repository;
2+
3+
import java.time.LocalDateTime;
4+
import java.util.List;
5+
6+
import org.layer.admin.retrospect.entity.AdminRetrospectHistory;
7+
import org.layer.admin.retrospect.repository.dto.SpaceRetrospectCountDto;
8+
import org.springframework.data.jpa.repository.JpaRepository;
9+
import org.springframework.data.jpa.repository.Query;
10+
import org.springframework.data.repository.query.Param;
11+
12+
public interface AdminRetrospectHistoryRepository extends JpaRepository<AdminRetrospectHistory, Long> {
13+
List<AdminRetrospectHistory> findAllByEventTimeBetween(LocalDateTime startTime, LocalDateTime endTime);
14+
15+
List<AdminRetrospectHistory> findAllByEventTimeBefore(LocalDateTime time);
16+
17+
@Query("SELECT new org.layer.admin.retrospect.repository.dto.SpaceRetrospectCountDto(m.spaceId, COUNT(m)) " +
18+
"FROM AdminRetrospectHistory m " +
19+
"WHERE m.eventTime BETWEEN :startTime AND :endTime " +
20+
"GROUP BY m.spaceId ")
21+
List<SpaceRetrospectCountDto> findRetrospectCountGroupedBySpaceWithPeriod(
22+
@Param("startTime") LocalDateTime startTime,
23+
@Param("endTime") LocalDateTime endTime
24+
);
25+
26+
27+
28+
}

layer-admin/src/main/java/org/layer/admin/retrospect/repository/AdminRetrospectRepository.java

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,21 @@
33
import java.time.LocalDateTime;
44
import java.util.List;
55

6-
import org.layer.admin.retrospect.entity.AdminRetrospectHistory;
7-
import org.layer.admin.retrospect.repository.dto.SpaceRetrospectCountDto;
6+
import org.layer.admin.retrospect.entity.AdminRetrospect;
87
import org.springframework.data.jpa.repository.JpaRepository;
98
import org.springframework.data.jpa.repository.Query;
109
import org.springframework.data.repository.query.Param;
1110

12-
public interface AdminRetrospectRepository extends JpaRepository<AdminRetrospectHistory, Long> {
13-
List<AdminRetrospectHistory> findAllByEventTimeBetween(LocalDateTime startTime, LocalDateTime endTime);
11+
public interface AdminRetrospectRepository extends JpaRepository<AdminRetrospect, Long> {
1412

15-
List<AdminRetrospectHistory> findAllByEventTimeBefore(LocalDateTime time);
16-
17-
@Query("SELECT new org.layer.admin.retrospect.repository.dto.SpaceRetrospectCountDto(m.spaceId, COUNT(m)) " +
18-
"FROM AdminRetrospectHistory m " +
19-
"WHERE m.eventTime BETWEEN :startTime AND :endTime " +
20-
"GROUP BY m.spaceId ")
21-
List<SpaceRetrospectCountDto> findRetrospectCountGroupedBySpaceWithPeriod(
22-
@Param("startTime") LocalDateTime startTime,
23-
@Param("endTime") LocalDateTime endTime
13+
@Query("""
14+
SELECT DISTINCT r.spaceId
15+
FROM AdminRetrospect r
16+
WHERE r.retrospectStatus = 'PROCEEDING'
17+
AND r.deadline BETWEEN :startDate AND :endDate
18+
""")
19+
List<Long> findProceedingSpacesByMember(
20+
@Param("startDate") LocalDateTime startDate,
21+
@Param("endDate") LocalDateTime endDate
2422
);
25-
2623
}

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

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,12 @@
3131
import org.layer.admin.retrospect.repository.AdminRetrospectAnswerRepository;
3232
import org.layer.admin.retrospect.repository.AdminRetrospectClickRepository;
3333
import org.layer.admin.retrospect.repository.AdminRetrospectImpressionRepository;
34-
import org.layer.admin.retrospect.repository.AdminRetrospectRepository;
34+
import org.layer.admin.retrospect.repository.AdminRetrospectHistoryRepository;
3535
import org.layer.admin.retrospect.repository.dto.ProceedingRetrospectClickDto;
3636
import org.layer.admin.retrospect.repository.dto.ProceedingRetrospectImpressionDto;
3737
import org.layer.admin.retrospect.repository.dto.RetrospectAnswerCompletionDto;
3838
import org.layer.admin.retrospect.repository.dto.SpaceRetrospectCountDto;
39-
import org.layer.admin.space.controller.dto.ProceedingSpaceCTRAverageResponse;
4039
import org.layer.admin.space.repository.AdminSpaceRepository;
41-
import org.layer.admin.space.repository.dto.ProceedingSpaceImpressionDto;
4240
import org.layer.event.retrospect.ClickRetrospectEvent;
4341
import org.layer.event.retrospect.CreateRetrospectEvent;
4442
import org.layer.event.retrospect.AnswerRetrospectEndEvent;
@@ -54,7 +52,7 @@
5452
@RequiredArgsConstructor
5553
public class AdminRetrospectService {
5654

57-
private final AdminRetrospectRepository adminRetrospectRepository;
55+
private final AdminRetrospectHistoryRepository adminRetrospectHistoryRepository;
5856
private final AdminRetrospectAnswerRepository adminRetrospectAnswerRepository;
5957
private final AdminRetrospectImpressionRepository adminRetrospectImpressionRepository;
6058
private final AdminRetrospectClickRepository adminRetrospectClickRepository;
@@ -92,9 +90,9 @@ public List<RetrospectStayTimeResponse> getAllRetrospectStayTime(
9290
}
9391

9492
public RetrospectRetentionResponse getRetrospectRetention(LocalDateTime startTime, LocalDateTime endTime) {
95-
List<AdminRetrospectHistory> histories = adminRetrospectRepository.findAllByEventTimeBetween(
93+
List<AdminRetrospectHistory> histories = adminRetrospectHistoryRepository.findAllByEventTimeBetween(
9694
startTime, endTime);
97-
List<AdminRetrospectHistory> prevHistories = adminRetrospectRepository.findAllByEventTimeBefore(startTime);
95+
List<AdminRetrospectHistory> prevHistories = adminRetrospectHistoryRepository.findAllByEventTimeBefore(startTime);
9896

9997
Map<Long, Long> retrospectCountMap = new HashMap<>();
10098
histories.forEach(history ->
@@ -185,7 +183,7 @@ private long calculateAverageMinGapInSeconds(List<AdminRetrospectHistory> histor
185183
public CumulativeRetrospectCountResponse getCumulativeRetrospectCount(
186184
LocalDateTime startTime, LocalDateTime endTime) {
187185

188-
List<SpaceRetrospectCountDto> histories = adminRetrospectRepository.findRetrospectCountGroupedBySpaceWithPeriod(startTime,
186+
List<SpaceRetrospectCountDto> histories = adminRetrospectHistoryRepository.findRetrospectCountGroupedBySpaceWithPeriod(startTime,
189187
endTime);
190188

191189
if (histories.isEmpty()) {
@@ -314,7 +312,7 @@ public void saveRetrospectHistory(CreateRetrospectEvent event) {
314312
.targetAnswerCount(event.targetAnswerCount())
315313
.build();
316314

317-
adminRetrospectRepository.save(retrospectHistory);
315+
adminRetrospectHistoryRepository.save(retrospectHistory);
318316
}
319317

320318
@Transactional(propagation = REQUIRES_NEW)

layer-admin/src/main/java/org/layer/admin/space/controller/AdminSpaceController.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.List;
55

66
import org.layer.admin.space.controller.dto.ProceedingSpaceCTRAverageResponse;
7+
import org.layer.admin.space.controller.dto.SpaceAbandonRate;
78
import org.layer.admin.space.controller.dto.SpaceCountResponse;
89
import org.layer.admin.space.controller.dto.TeamSpaceRatioResponse;
910
import org.layer.admin.space.service.AdminSpaceService;
@@ -49,4 +50,14 @@ public ResponseEntity<ProceedingSpaceCTRAverageResponse> getProceedingSpaceCTR(
4950
ProceedingSpaceCTRAverageResponse response = adminSpaceService.getProceedingSpaceCTR(startDate, endDate);
5051
return ResponseEntity.ok().body(response);
5152
}
53+
54+
@GetMapping("/admin/space/abandon-rates")
55+
public ResponseEntity<SpaceAbandonRate> getAbandonRate(
56+
@RequestParam(name = "startDate") LocalDateTime startDate,
57+
@RequestParam(name = "endDate") LocalDateTime endDate,
58+
@RequestParam(name = "day") long day) {
59+
60+
SpaceAbandonRate response = adminSpaceService.calculateAbandonRate(startDate, endDate, day);
61+
return ResponseEntity.ok().body(response);
62+
}
5263
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.layer.admin.space.repository;
2+
3+
import java.time.LocalDateTime;
4+
import java.util.List;
5+
6+
import org.layer.admin.space.entity.AdminMemberSpaceRelation;
7+
import org.layer.admin.space.repository.dto.ProceedingSpaceDto;
8+
import org.springframework.data.jpa.repository.JpaRepository;
9+
import org.springframework.data.jpa.repository.Query;
10+
import org.springframework.data.repository.query.Param;
11+
12+
public interface AdminMemberSpaceRelationRepository extends JpaRepository<AdminMemberSpaceRelation, Long> {
13+
14+
@Query("""
15+
SELECT new org.layer.admin.space.repository.dto.ProceedingSpaceDto(
16+
r.spaceId,
17+
COUNT(r.memberId)
18+
)
19+
FROM AdminMemberSpaceRelation r
20+
WHERE r.createdAt BETWEEN :startDate AND :endDate
21+
GROUP BY r.spaceId
22+
""")
23+
List<ProceedingSpaceDto> findProceedingSpacesWithMemberCount(
24+
@Param("startDate") LocalDateTime startDate,
25+
@Param("endDate") LocalDateTime endDate
26+
);
27+
}

layer-admin/src/main/java/org/layer/admin/space/repository/AdminSpaceClickRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,6 @@ public interface AdminSpaceClickRepository extends JpaRepository<AdminSpaceClick
2424
List<ProceedingSpaceClickDto> findProceedingSpaceClickGroupByMember(
2525
@Param("startDate") LocalDateTime startDate,
2626
@Param("endDate") LocalDateTime endDate);
27+
28+
List<AdminSpaceClick> findAllByEventTimeBetween(LocalDateTime startDate, LocalDateTime endDate);
2729
}

layer-admin/src/main/java/org/layer/admin/space/service/AdminSpaceService.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
import java.util.stream.Collectors;
1010

1111
import org.layer.admin.retrospect.enums.AdminRetrospectStatus;
12+
import org.layer.admin.retrospect.repository.AdminRetrospectRepository;
1213
import org.layer.admin.space.controller.dto.ProceedingSpaceCTRAverageResponse;
14+
import org.layer.admin.space.controller.dto.SpaceAbandonRate;
1315
import org.layer.admin.space.entity.AdminSpaceClick;
1416
import org.layer.admin.space.entity.AdminSpaceImpression;
17+
import org.layer.admin.space.repository.AdminMemberSpaceRelationRepository;
1518
import org.layer.admin.space.repository.AdminSpaceImpressionRepository;
1619
import org.layer.admin.space.repository.dto.ProceedingSpaceClickDto;
1720
import org.layer.admin.space.controller.dto.SpaceCountResponse;
@@ -23,6 +26,7 @@
2326
import org.layer.admin.space.repository.AdminMemberSpaceRepository;
2427
import org.layer.admin.space.repository.AdminSpaceClickRepository;
2528
import org.layer.admin.space.repository.AdminSpaceRepository;
29+
import org.layer.admin.space.repository.dto.ProceedingSpaceDto;
2630
import org.layer.admin.space.repository.dto.ProceedingSpaceImpressionDto;
2731
import org.layer.event.space.ClickSpaceEvent;
2832
import org.layer.event.space.CreateSpaceEvent;
@@ -42,9 +46,11 @@
4246
public class AdminSpaceService {
4347

4448
private final AdminSpaceRepository adminSpaceRepository;
49+
private final AdminRetrospectRepository adminRetrospectRepository;
4550
private final AdminMemberSpaceRepository adminMemberSpaceRepository;
4651
private final AdminSpaceImpressionRepository adminSpaceImpressionRepository;
4752
private final AdminSpaceClickRepository adminSpaceClickRepository;
53+
private final AdminMemberSpaceRelationRepository adminMemberSpaceRelationRepository;
4854

4955
public List<SpaceCountResponse> getSpaceCount(LocalDateTime startDate, LocalDateTime endDate) {
5056
return adminSpaceRepository.findAllByCategory(startDate, endDate);
@@ -102,6 +108,51 @@ public ProceedingSpaceCTRAverageResponse getProceedingSpaceCTR(LocalDateTime sta
102108
return new ProceedingSpaceCTRAverageResponse(averageCTR);
103109
}
104110

111+
public SpaceAbandonRate calculateAbandonRate(LocalDateTime startDate, LocalDateTime endDate, long day) {
112+
LocalDateTime threshold = endDate.minusDays(day);
113+
114+
List<Long> proceedingSpaceIds = adminRetrospectRepository.findProceedingSpacesByMember(startDate, endDate);
115+
116+
Map<Long, Long> spaceMemberCountMap = adminMemberSpaceRelationRepository.findProceedingSpacesWithMemberCount(startDate,
117+
endDate)
118+
.stream()
119+
.collect(Collectors.toMap(
120+
ProceedingSpaceDto::spaceId,
121+
ProceedingSpaceDto::memberCount
122+
));
123+
124+
List<AdminSpaceClick> clicks = adminSpaceClickRepository.findAllByEventTimeBetween(threshold, endDate);
125+
Map<Long, Long> clickCountMap = clicks.stream()
126+
.collect(Collectors.groupingBy(
127+
AdminSpaceClick::getSpaceId, // spaceId로 그룹화
128+
Collectors.collectingAndThen(
129+
Collectors.mapping(
130+
AdminSpaceClick::getMemberId, // memberId만 추출
131+
Collectors.toSet() // 중복 제거
132+
),
133+
set -> (long) set.size() // Set 크기를 Long으로 변환
134+
)
135+
));
136+
137+
double abandonRate = proceedingSpaceIds.stream()
138+
.map(spaceId -> {
139+
Long memberCount = spaceMemberCountMap.getOrDefault(spaceId, 0L);
140+
Long clickCount = clickCountMap.getOrDefault(spaceId, 0L);
141+
142+
if (memberCount == 0) {
143+
return null; // 멤버가 없는 경우 제외
144+
}
145+
146+
return (memberCount - clickCount) / (double)memberCount;
147+
})
148+
.filter(Objects::nonNull) // null 제거
149+
.mapToDouble(Double::doubleValue) // double 스트림으로 변환
150+
.average() // 평균 계산
151+
.orElse(0.0);// 값이 없을 경우 0.0
152+
153+
return new SpaceAbandonRate(abandonRate);
154+
}
155+
105156
@Transactional(propagation = REQUIRES_NEW)
106157
@Async
107158
public void saveSpaceHistory(CreateSpaceEvent event) {

0 commit comments

Comments
 (0)