Skip to content

Commit 1d8f71d

Browse files
committed
feat: 버그수정
1 parent 9ac736f commit 1d8f71d

3 files changed

Lines changed: 94 additions & 96 deletions

File tree

Lines changed: 6 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,9 @@
11
package com.waitit.capstone.domain.dashboard.repository;
22

3-
import com.waitit.capstone.domain.dashboard.dto.CancelReasonCountDto;
4-
import com.waitit.capstone.domain.dashboard.dto.HourlyMetricsQueryResult;
5-
import com.waitit.capstone.domain.dashboard.entity.QueueLog;
6-
import org.springframework.data.jpa.repository.JpaRepository;
7-
import org.springframework.data.jpa.repository.Query;
8-
import org.springframework.data.repository.query.Param;
9-
10-
import java.time.LocalDateTime;
11-
import java.util.List;
12-
import java.util.Set;
13-
14-
public interface QueueLogRepository extends JpaRepository<QueueLog, Long> {
15-
16-
long countByHostIdAndRegisteredAtBetween(Long hostId, LocalDateTime startDate, LocalDateTime endDate);
17-
18-
long countByHostIdAndStatusAndEnteredAtBetween(Long hostId, QueueLog.Status status, LocalDateTime startDate, LocalDateTime endDate);
19-
20-
long countByHostIdAndStatusAndCancelledAtBetween(Long hostId, QueueLog.Status status, LocalDateTime startDate, LocalDateTime endDate);
21-
22-
@Query(value = "SELECT AVG(TIMESTAMPDIFF(SECOND, q.registered_at, q.entered_at)) FROM queue_log q " +
23-
"WHERE q.host_id = :hostId AND q.status = 'ENTERED' AND q.entered_at BETWEEN :startDate AND :endDate",
24-
nativeQuery = true)
25-
Double findAverageWaitTimeInSeconds(
26-
@Param("hostId") Long hostId,
27-
@Param("startDate") LocalDateTime startDate,
28-
@Param("endDate") LocalDateTime endDate);
29-
30-
@Query("""
31-
SELECT new com.waitit.capstone.domain.dashboard.dto.HourlyMetricsQueryResult(
32-
CAST(FUNCTION('HOUR', q.registeredAt) AS integer),
33-
COUNT(q.id),
34-
SUM(CASE WHEN q.status = 'ENTERED' THEN 1 ELSE 0 END),
35-
SUM(CASE WHEN q.status = 'CANCELLED' THEN 1 ELSE 0 END)
36-
)
37-
FROM QueueLog q
38-
WHERE q.host.id = :hostId AND q.registeredAt BETWEEN :startDate AND :endDate
39-
GROUP BY FUNCTION('HOUR', q.registeredAt)
40-
ORDER BY FUNCTION('HOUR', q.registeredAt)
41-
""")
42-
List<HourlyMetricsQueryResult> findHourlyMetrics(
43-
@Param("hostId") Long hostId,
44-
@Param("startDate") LocalDateTime startDate,
45-
@Param("endDate") LocalDateTime endDate);
46-
47-
@Query("SELECT DISTINCT q.phoneNumber FROM QueueLog q " +
48-
"WHERE q.host.id = :hostId AND q.status = 'ENTERED' AND q.enteredAt BETWEEN :startDate AND :endDate")
49-
Set<String> findDistinctPhoneNumbersByStatusAndEnteredAtBetween(
50-
@Param("hostId") Long hostId,
51-
@Param("startDate") LocalDateTime startDate,
52-
@Param("endDate") LocalDateTime endDate);
53-
54-
@Query("SELECT DISTINCT q.phoneNumber FROM QueueLog q " +
55-
"WHERE q.host.id = :hostId AND q.status = 'ENTERED' AND q.phoneNumber IN :phoneNumbers AND q.enteredAt < :startDate")
56-
Set<String> findReturningVisitorPhoneNumbers(
57-
@Param("hostId") Long hostId,
58-
@Param("phoneNumbers") Set<String> phoneNumbers,
59-
@Param("startDate") LocalDateTime startDate);
60-
61-
@Query("SELECT new com.waitit.capstone.domain.dashboard.dto.CancelReasonCountDto(q.reason, COUNT(q)) " +
62-
"FROM QueueLog q " +
63-
"WHERE q.host.id = :hostId AND q.status = 'CANCELLED' AND q.cancelledAt BETWEEN :startDate AND :endDate " +
64-
"GROUP BY q.reason")
65-
List<CancelReasonCountDto> findCancelReasonStats(
66-
@Param("hostId") Long hostId,
67-
@Param("startDate") LocalDateTime startDate,
68-
@Param("endDate") LocalDateTime endDate);
3+
/**
4+
* V2 대시보드 기능으로 대체되어 더 이상 사용되지 않는 리포지토리입니다.
5+
* Bean 생성 충돌을 막기 위해 내용을 비활성화합니다.
6+
* 이 파일은 QueueLogRepositoryV2.java 로 대체되었습니다.
7+
*/
8+
public interface QueueLogRepository {
699
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.waitit.capstone.domain.dashboard.repository;
2+
3+
import com.waitit.capstone.domain.dashboard.dto.CancelReasonCountDto;
4+
import com.waitit.capstone.domain.dashboard.entity.QueueLog;
5+
import org.springframework.data.jpa.repository.JpaRepository;
6+
import org.springframework.data.jpa.repository.Query;
7+
import org.springframework.data.repository.query.Param;
8+
9+
import java.time.LocalDateTime;
10+
import java.util.List;
11+
import java.util.Set;
12+
13+
// 인터페이스 이름을 V2로 변경
14+
public interface QueueLogRepositoryV2 extends JpaRepository<QueueLog, Long> {
15+
16+
long countByHostIdAndRegisteredAtBetween(Long hostId, LocalDateTime startDate, LocalDateTime endDate);
17+
18+
long countByHostIdAndStatusAndEnteredAtBetween(Long hostId, QueueLog.Status status, LocalDateTime startDate, LocalDateTime endDate);
19+
20+
long countByHostIdAndStatusAndCancelledAtBetween(Long hostId, QueueLog.Status status, LocalDateTime startDate, LocalDateTime endDate);
21+
22+
@Query(value = "SELECT AVG(TIMESTAMPDIFF(SECOND, q.registered_at, q.entered_at)) FROM queue_log q " +
23+
"WHERE q.host_id = :hostId AND q.status = 'ENTERED' AND q.entered_at BETWEEN :startDate AND :endDate",
24+
nativeQuery = true)
25+
Double findAverageWaitTimeInSeconds(
26+
@Param("hostId") Long hostId,
27+
@Param("startDate") LocalDateTime startDate,
28+
@Param("endDate") LocalDateTime endDate);
29+
30+
@Query(value = "SELECT " +
31+
" HOUR(q.registered_at) as hour, " +
32+
" COUNT(q.id) as totalCount, " +
33+
" SUM(CASE WHEN q.status = 'ENTERED' THEN 1 ELSE 0 END) as enteredCount, " +
34+
" SUM(CASE WHEN q.status = 'CANCELLED' THEN 1 ELSE 0 END) as cancelledCount " +
35+
"FROM queue_log q " +
36+
"WHERE q.host_id = :hostId AND q.registered_at BETWEEN :startDate AND :endDate " +
37+
"GROUP BY hour ORDER BY hour",
38+
nativeQuery = true)
39+
List<Object[]> findHourlyMetricsRaw(
40+
@Param("hostId") Long hostId,
41+
@Param("startDate") LocalDateTime startDate,
42+
@Param("endDate") LocalDateTime endDate);
43+
44+
@Query("SELECT DISTINCT q.phoneNumber FROM QueueLog q " +
45+
"WHERE q.host.id = :hostId AND q.status = 'ENTERED' AND q.enteredAt BETWEEN :startDate AND :endDate")
46+
Set<String> findDistinctPhoneNumbersByStatusAndEnteredAtBetween(
47+
@Param("hostId") Long hostId,
48+
@Param("startDate") LocalDateTime startDate,
49+
@Param("endDate") LocalDateTime endDate);
50+
51+
@Query("SELECT DISTINCT q.phoneNumber FROM QueueLog q " +
52+
"WHERE q.host.id = :hostId AND q.status = 'ENTERED' AND q.phoneNumber IN :phoneNumbers AND q.enteredAt < :startDate")
53+
Set<String> findReturningVisitorPhoneNumbers(
54+
@Param("hostId") Long hostId,
55+
@Param("phoneNumbers") Set<String> phoneNumbers,
56+
@Param("startDate") LocalDateTime startDate);
57+
58+
@Query("SELECT new com.waitit.capstone.domain.dashboard.dto.CancelReasonCountDto(q.reason, COUNT(q)) " +
59+
"FROM QueueLog q " +
60+
"WHERE q.host.id = :hostId AND q.status = 'CANCELLED' AND q.cancelledAt BETWEEN :startDate AND :endDate " +
61+
"GROUP BY q.reason")
62+
List<CancelReasonCountDto> findCancelReasonStats(
63+
@Param("hostId") Long hostId,
64+
@Param("startDate") LocalDateTime startDate,
65+
@Param("endDate") LocalDateTime endDate);
66+
}

src/main/java/com/waitit/capstone/domain/dashboard/service/DashboardServiceV2.java

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import com.waitit.capstone.domain.dashboard.dto.*;
44
import com.waitit.capstone.domain.dashboard.entity.QueueLog;
55
import com.waitit.capstone.domain.dashboard.entity.Review;
6-
import com.waitit.capstone.domain.dashboard.repository.QueueLogRepository;
6+
import com.waitit.capstone.domain.dashboard.repository.QueueLogRepositoryV2; // V2로 변경
77
import com.waitit.capstone.domain.dashboard.repository.ReviewRepository;
88
import lombok.RequiredArgsConstructor;
99
import org.springframework.stereotype.Service;
@@ -22,18 +22,14 @@
2222
@Transactional(readOnly = true)
2323
public class DashboardServiceV2 {
2424

25-
private final QueueLogRepository queueLogRepository;
25+
private final QueueLogRepositoryV2 queueLogRepository; // V2로 변경
2626
private final ReviewRepository reviewRepository;
2727

28-
/**
29-
* 1. 전체 스토어 지표 조회
30-
*/
3128
public StoreMetricsResponse getStoreMetrics(Long storeId, String dateRange) {
3229
String[] dates = dateRange.split("~");
3330
LocalDateTime startDate = LocalDate.parse(dates[0], DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay();
3431
LocalDateTime endDate = LocalDate.parse(dates[1], DateTimeFormatter.ISO_LOCAL_DATE).atTime(LocalTime.MAX);
3532

36-
// 1. 전체 요약 통계 계산
3733
long totalWaitlistCount = queueLogRepository.countByHostIdAndRegisteredAtBetween(storeId, startDate, endDate);
3834
long totalActualUsers = queueLogRepository.countByHostIdAndStatusAndEnteredAtBetween(storeId, QueueLog.Status.ENTERED, startDate, endDate);
3935
long totalDropouts = queueLogRepository.countByHostIdAndStatusAndCancelledAtBetween(storeId, QueueLog.Status.CANCELLED, startDate, endDate);
@@ -48,49 +44,50 @@ public StoreMetricsResponse getStoreMetrics(Long storeId, String dateRange) {
4844
.averageWaitTimeSeconds(avgWaitTime != null ? avgWaitTime : 0.0)
4945
.build();
5046

51-
// 2. 시간대별 세부 통계 계산
52-
List<HourlyMetricsQueryResult> hourlyResults = queueLogRepository.findHourlyMetrics(storeId, startDate, endDate);
47+
List<Object[]> hourlyResults = queueLogRepository.findHourlyMetricsRaw(storeId, startDate, endDate);
5348
List<HourlyStoreMetricsDto> hourlyData = hourlyResults.stream()
54-
.map(result -> HourlyStoreMetricsDto.builder()
55-
.timeSlot(String.format("%02d:00 - %02d:00", result.getHour(), result.getHour() + 1))
56-
.waitlistCount(result.getTotalCount())
57-
.actualUsers(result.getEnteredCount())
58-
.dropouts(result.getCancelledCount())
59-
.build())
49+
.map(result -> {
50+
Integer hour = ((Number) result[0]).intValue();
51+
long totalCount = ((Number) result[1]).longValue();
52+
long enteredCount = ((Number) result[2]).longValue();
53+
long cancelledCount = ((Number) result[3]).longValue();
54+
return HourlyStoreMetricsDto.builder()
55+
.timeSlot(String.format("%02d:00 - %02d:00", hour, hour + 1))
56+
.waitlistCount(totalCount)
57+
.actualUsers(enteredCount)
58+
.dropouts(cancelledCount)
59+
.build();
60+
})
6061
.collect(Collectors.toList());
6162

62-
// 3. 최종 응답 조합
6363
return StoreMetricsResponse.builder()
6464
.summary(summary)
6565
.hourlyData(hourlyData)
6666
.build();
6767
}
6868

69-
/**
70-
* 4. 예상 대기인원 추이 조회
71-
*/
7269
public List<WaitlistTrendHourlyData> getWaitlistTrend(Long storeId, String dateRange) {
7370
String[] dates = dateRange.split("~");
7471
LocalDateTime startDate = LocalDate.parse(dates[0], DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay();
7572
LocalDateTime endDate = LocalDate.parse(dates[1], DateTimeFormatter.ISO_LOCAL_DATE).atTime(LocalTime.MAX);
7673

77-
List<HourlyMetricsQueryResult> hourlyResults = queueLogRepository.findHourlyMetrics(storeId, startDate, endDate);
74+
List<Object[]> hourlyResults = queueLogRepository.findHourlyMetricsRaw(storeId, startDate, endDate);
7875

7976
return hourlyResults.stream()
8077
.map(result -> {
81-
double utilizationRate = (result.getTotalCount() > 0) ? (double) result.getEnteredCount() / result.getTotalCount() : 0.0;
78+
Integer hour = ((Number) result[0]).intValue();
79+
long totalCount = ((Number) result[1]).longValue();
80+
long enteredCount = ((Number) result[2]).longValue();
81+
double utilizationRate = (totalCount > 0) ? (double) enteredCount / totalCount : 0.0;
8282
return WaitlistTrendHourlyData.builder()
83-
.timeSlot(String.format("%02d:00 - %02d:00", result.getHour(), result.getHour() + 1))
84-
.waitlistCount(result.getTotalCount())
83+
.timeSlot(String.format("%02d:00 - %02d:00", hour, hour + 1))
84+
.waitlistCount(totalCount)
8585
.utilizationRate(utilizationRate)
8686
.build();
8787
})
8888
.collect(Collectors.toList());
8989
}
9090

91-
/**
92-
* 3. 재방문율 조회
93-
*/
9491
public ReturnRateResponse getReturnRate(Long storeId, String dateRange) {
9592
String[] dates = dateRange.split("~");
9693
LocalDateTime startDate = LocalDate.parse(dates[0], DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay();
@@ -118,16 +115,12 @@ public ReturnRateResponse getReturnRate(Long storeId, String dateRange) {
118115
.build();
119116
}
120117

121-
/**
122-
* 5. 리뷰 및 대기 취소 사유 조회
123-
*/
124118
public ReviewAndCancelStatsResponse getReviewAndCancelStats(Long storeId, String dateRange, Integer ratingMin) {
125119
String[] dates = dateRange.split("~");
126120
LocalDateTime startDate = LocalDate.parse(dates[0], DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay();
127121
LocalDateTime endDate = LocalDate.parse(dates[1], DateTimeFormatter.ISO_LOCAL_DATE).atTime(LocalTime.MAX);
128122
int minRating = (ratingMin != null) ? ratingMin : 0;
129123

130-
// 1. 취소 사유 통계 계산
131124
List<CancelReasonCountDto> reasonCounts = queueLogRepository.findCancelReasonStats(storeId, startDate, endDate);
132125
long totalCancellations = reasonCounts.stream().mapToLong(CancelReasonCountDto::getCount).sum();
133126

@@ -141,7 +134,6 @@ public ReviewAndCancelStatsResponse getReviewAndCancelStats(Long storeId, String
141134
})
142135
.collect(Collectors.toList());
143136

144-
// 2. 리뷰 목록 조회
145137
List<Review> reviewEntities = reviewRepository.findReviews(storeId, startDate, endDate, minRating);
146138
List<ReviewSummaryDto> reviews = reviewEntities.stream()
147139
.map(ReviewSummaryDto::from)

0 commit comments

Comments
 (0)