Skip to content

Commit 304b205

Browse files
committed
feat: 대시보드 기능 개발
1 parent 0a2d78b commit 304b205

22 files changed

Lines changed: 774 additions & 0 deletions
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.waitit.capstone.domain.dashboard.controller;
2+
3+
import com.waitit.capstone.domain.dashboard.dto.*;
4+
import com.waitit.capstone.domain.dashboard.service.DashboardService;
5+
import io.swagger.v3.oas.annotations.Operation;
6+
import io.swagger.v3.oas.annotations.tags.Tag;
7+
import jakarta.validation.Valid;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.http.HttpStatus;
10+
import org.springframework.http.ResponseEntity;
11+
import org.springframework.security.access.prepost.PreAuthorize;
12+
import org.springframework.web.bind.annotation.*;
13+
14+
import java.util.List;
15+
16+
@RestController
17+
@RequestMapping("/api/v1")
18+
@RequiredArgsConstructor
19+
@Tag(name = "대시보드 API", description = "가게 대시보드 통계 및 관리 관련 API")
20+
public class DashboardController {
21+
22+
private final DashboardService dashboardService;
23+
24+
@GetMapping("/visitors/statistics")
25+
@Operation(summary = "방문 고객 통계 조회", description = "기간별 신규/재방문 고객 비율 등 방문 통계를 조회합니다.")
26+
// TODO: 로그인한 사용자가 자신의 가게 정보만 볼 수 있도록 보안 로직 추가 필요
27+
public ResponseEntity<VisitorStatisticsResponse> getVisitorStatistics(
28+
@RequestParam Long storeId,
29+
@RequestParam String dateRange) {
30+
VisitorStatisticsResponse stats = dashboardService.getVisitorStatistics(storeId, dateRange);
31+
return ResponseEntity.ok(stats);
32+
}
33+
34+
@GetMapping("/waiting/trends")
35+
@Operation(summary = "대기인원 추세 및 예측 조회", description = "기간별 시간대 실제/예측 대기 인원 추세를 조회합니다.")
36+
// TODO: 로그인한 사용자가 자신의 가게 정보만 볼 수 있도록 보안 로직 추가 필요
37+
public ResponseEntity<WaitingTrendsResponse> getWaitingTrends(
38+
@RequestParam Long storeId,
39+
@RequestParam String dateRange) {
40+
WaitingTrendsResponse trends = dashboardService.getWaitingTrends(storeId, dateRange);
41+
return ResponseEntity.ok(trends);
42+
}
43+
44+
@GetMapping("/reports/summary")
45+
@Operation(summary = "리포트 분석 조회", description = "주간/월간 단위 방문자, 이탈률, 재방문률을 종합하여 조회합니다.")
46+
// TODO: 로그인한 사용자가 자신의 가게 정보만 볼 수 있도록 보안 로직 추가 필요
47+
public ResponseEntity<ReportSummaryResponse> getReportSummary(
48+
@RequestParam Long storeId,
49+
@RequestParam(defaultValue = "WEEKLY") String type) {
50+
ReportSummaryResponse summary = dashboardService.getReportSummary(storeId, type);
51+
return ResponseEntity.ok(summary);
52+
}
53+
54+
@GetMapping("/reviews/statistics")
55+
@Operation(summary = "리뷰 및 대기 취소 사유 조회", description = "기간별 대기 취소 사유 통계와 리뷰 목록을 조회합니다.")
56+
// TODO: 로그인한 사용자가 자신의 가게 정보만 볼 수 있도록 보안 로직 추가 필요
57+
public ResponseEntity<ReviewAndCancelStatsResponse> getReviewAndCancelStats(
58+
@RequestParam Long storeId,
59+
@RequestParam String dateRange,
60+
@RequestParam(required = false) Integer ratingMin) {
61+
ReviewAndCancelStatsResponse stats = dashboardService.getReviewAndCancelStats(storeId, dateRange, ratingMin);
62+
return ResponseEntity.ok(stats);
63+
}
64+
65+
@GetMapping("/promotions/events")
66+
@Operation(summary = "프로모션/이벤트 목록 조회", description = "특정 가게의 모든 프로모션 및 기념일 목록을 조회합니다.")
67+
// TODO: 로그인한 사용자가 자신의 가게 정보만 볼 수 있도록 보안 로직 추가 필요
68+
public ResponseEntity<List<PromotionEventResponse>> getPromotionEvents(@RequestParam Long storeId) {
69+
List<PromotionEventResponse> events = dashboardService.getPromotionEvents(storeId);
70+
return ResponseEntity.ok(events);
71+
}
72+
73+
@PostMapping("/promotions/events")
74+
@Operation(summary = "프로모션/이벤트 생성", description = "새로운 프로모션 또는 기념일을 등록합니다.")
75+
@PreAuthorize("hasRole('HOST') or hasRole('ADMIN')") // 자영업자 또는 관리자만 생성 가능
76+
public ResponseEntity<Void> createPromotionEvent(@Valid @RequestBody CreatePromotionEventRequest request) {
77+
dashboardService.createPromotionEvent(request);
78+
return ResponseEntity.status(HttpStatus.CREATED).build();
79+
}
80+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.waitit.capstone.domain.dashboard.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
6+
@Getter
7+
@AllArgsConstructor
8+
public class CancelReasonStatsDto {
9+
private String reason;
10+
private Long count;
11+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.waitit.capstone.domain.dashboard.dto;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
import jakarta.validation.constraints.NotNull;
5+
import lombok.Getter;
6+
import lombok.Setter;
7+
8+
import java.time.LocalDate;
9+
10+
@Getter
11+
@Setter
12+
public class CreatePromotionEventRequest {
13+
14+
@NotNull(message = "가게 ID는 필수입니다.")
15+
private Long storeId;
16+
17+
@NotBlank(message = "이벤트 제목은 필수입니다.")
18+
private String title;
19+
20+
@NotNull(message = "시작일은 필수입니다.")
21+
private LocalDate startDate;
22+
23+
private LocalDate endDate; // 종료일은 선택사항
24+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.waitit.capstone.domain.dashboard.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
6+
@Getter
7+
@AllArgsConstructor
8+
public class HourlyWaitingStats {
9+
private Integer hour;
10+
private Double averageQueueSize;
11+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.waitit.capstone.domain.dashboard.dto;
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude;
4+
import com.waitit.capstone.domain.dashboard.entity.PromotionEvent;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
8+
import java.time.format.DateTimeFormatter;
9+
10+
@Getter
11+
@Builder
12+
@JsonInclude(JsonInclude.Include.NON_NULL) // end 필드가 null일 경우 JSON에서 제외
13+
public class PromotionEventResponse {
14+
15+
private String start;
16+
private String end;
17+
private String title;
18+
19+
public static PromotionEventResponse from(PromotionEvent event) {
20+
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE; // yyyy-MM-dd 형식
21+
return PromotionEventResponse.builder()
22+
.title(event.getTitle())
23+
.start(event.getStartDate().format(formatter))
24+
.end(event.getEndDate() != null ? event.getEndDate().format(formatter) : null)
25+
.build();
26+
}
27+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.waitit.capstone.domain.dashboard.dto;
2+
3+
import lombok.Builder;
4+
import lombok.Getter;
5+
6+
@Getter
7+
@Builder
8+
public class ReportSummaryData {
9+
private String label;
10+
private long visitors;
11+
private double dropRate;
12+
private double returnRate;
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.waitit.capstone.domain.dashboard.dto;
2+
3+
import lombok.Builder;
4+
import lombok.Getter;
5+
6+
import java.util.List;
7+
8+
@Getter
9+
@Builder
10+
public class ReportSummaryResponse {
11+
private String periodType;
12+
private List<ReportSummaryData> data;
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.waitit.capstone.domain.dashboard.dto;
2+
3+
import lombok.Builder;
4+
import lombok.Getter;
5+
6+
import java.util.List;
7+
8+
@Getter
9+
@Builder
10+
public class ReviewAndCancelStatsResponse {
11+
private List<CancelReasonStatsDto> cancelReasons;
12+
private List<ReviewSummaryDto> reviews;
13+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.waitit.capstone.domain.dashboard.dto;
2+
3+
import com.waitit.capstone.domain.dashboard.entity.Review;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
7+
import java.time.format.DateTimeFormatter;
8+
9+
@Getter
10+
@Builder
11+
public class ReviewSummaryDto {
12+
private String date;
13+
private int rating;
14+
private String comment;
15+
16+
public static ReviewSummaryDto from(Review review) {
17+
return ReviewSummaryDto.builder()
18+
.date(review.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE))
19+
.rating(review.getRating())
20+
.comment(review.getComment())
21+
.build();
22+
}
23+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.waitit.capstone.domain.dashboard.dto;
2+
3+
import lombok.Builder;
4+
import lombok.Getter;
5+
6+
@Getter
7+
@Builder
8+
public class VisitorStatisticsResponse {
9+
private long totalVisitors;
10+
private long newVisitors;
11+
private long returningVisitors;
12+
private double returnRate;
13+
}

0 commit comments

Comments
 (0)