Skip to content

Commit 2409065

Browse files
authored
Merge pull request #16 from Geumpumta/feat/statistics
feat: 일간/주간/월간/잔디 통계 추가
2 parents ef10ed4 + 29bbbac commit 2409065

14 files changed

+722
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,5 @@ out/
3535

3636
### VS Code ###
3737
.vscode/
38+
39+
docker-infra/
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package com.gpt.geumpumtabackend.statistics.api;
2+
3+
import com.gpt.geumpumtabackend.global.aop.AssignUserId;
4+
import com.gpt.geumpumtabackend.global.config.swagger.SwaggerApiFailedResponse;
5+
import com.gpt.geumpumtabackend.global.config.swagger.SwaggerApiResponses;
6+
import com.gpt.geumpumtabackend.global.config.swagger.SwaggerApiSuccessResponse;
7+
import com.gpt.geumpumtabackend.global.exception.ExceptionType;
8+
import com.gpt.geumpumtabackend.global.response.ResponseBody;
9+
import com.gpt.geumpumtabackend.global.response.ResponseUtil;
10+
import com.gpt.geumpumtabackend.statistics.dto.response.DailyStatisticsResponse;
11+
import com.gpt.geumpumtabackend.statistics.dto.response.GrassStatisticsResponse;
12+
import com.gpt.geumpumtabackend.statistics.dto.response.MonthlyStatisticsResponse;
13+
import com.gpt.geumpumtabackend.statistics.dto.response.WeeklyStatisticsResponse;
14+
import com.gpt.geumpumtabackend.token.dto.response.TokenResponse;
15+
import io.swagger.v3.oas.annotations.Operation;
16+
import io.swagger.v3.oas.annotations.Parameter;
17+
import io.swagger.v3.oas.annotations.media.Content;
18+
import io.swagger.v3.oas.annotations.media.Schema;
19+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
20+
import io.swagger.v3.oas.annotations.tags.Tag;
21+
import org.springframework.format.annotation.DateTimeFormat;
22+
import org.springframework.http.ResponseEntity;
23+
import org.springframework.security.access.prepost.PreAuthorize;
24+
import org.springframework.web.bind.annotation.GetMapping;
25+
import org.springframework.web.bind.annotation.RequestParam;
26+
27+
import java.time.LocalDate;
28+
29+
@Tag(name = "통계 API", description = "통계 관련 API")
30+
public interface StatisticsApi {
31+
32+
@Operation(
33+
summary = "일간 통계 요청 api",
34+
description = "USER 이상의 권한을 가진 사용자는 일간 통계를 요청합니다."
35+
36+
)
37+
@ApiResponse(content = @Content(schema = @Schema(implementation = DailyStatisticsResponse.class)))
38+
@SwaggerApiResponses(
39+
success = @SwaggerApiSuccessResponse(
40+
response = DailyStatisticsResponse.class,
41+
description = "일간 통계 요청 완료"),
42+
errors = {
43+
@SwaggerApiFailedResponse(ExceptionType.NEED_AUTHORIZED),
44+
@SwaggerApiFailedResponse(ExceptionType.USER_NOT_FOUND),
45+
}
46+
)
47+
@GetMapping("/day")
48+
@AssignUserId
49+
@PreAuthorize("isAuthenticated() and hasRole('USER')")
50+
public ResponseEntity<ResponseBody<DailyStatisticsResponse>> getDailyStatistics(
51+
@RequestParam
52+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
53+
@Parameter(hidden = true) Long userId
54+
);
55+
56+
@Operation(
57+
summary = "주간 통계 요청 api",
58+
description = "USER 이상의 권한을 가진 사용자는 주간 통계를 요청합니다."
59+
60+
)
61+
@ApiResponse(content = @Content(schema = @Schema(implementation = WeeklyStatisticsResponse.class)))
62+
@SwaggerApiResponses(
63+
success = @SwaggerApiSuccessResponse(
64+
response = WeeklyStatisticsResponse.class,
65+
description = "주간 통계 요청 완료"),
66+
errors = {
67+
@SwaggerApiFailedResponse(ExceptionType.NEED_AUTHORIZED),
68+
@SwaggerApiFailedResponse(ExceptionType.USER_NOT_FOUND),
69+
}
70+
)
71+
@GetMapping("/week")
72+
@AssignUserId
73+
@PreAuthorize("isAuthenticated() and hasRole('USER')")
74+
public ResponseEntity<ResponseBody<WeeklyStatisticsResponse>> getWeeklyStatistics(
75+
@RequestParam
76+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
77+
@Parameter(hidden = true) Long userId
78+
);
79+
80+
@Operation(
81+
summary = "월간 통계 요청 api",
82+
description = "USER 이상의 권한을 가진 사용자는 월간 통계를 요청합니다."
83+
84+
)
85+
@ApiResponse(content = @Content(schema = @Schema(implementation = MonthlyStatisticsResponse.class)))
86+
@SwaggerApiResponses(
87+
success = @SwaggerApiSuccessResponse(
88+
response = MonthlyStatisticsResponse.class,
89+
description = "월간 통계 요청 완료"),
90+
errors = {
91+
@SwaggerApiFailedResponse(ExceptionType.NEED_AUTHORIZED),
92+
@SwaggerApiFailedResponse(ExceptionType.USER_NOT_FOUND),
93+
}
94+
)
95+
@GetMapping("/month")
96+
@AssignUserId
97+
@PreAuthorize("isAuthenticated() and hasRole('USER')")
98+
public ResponseEntity<ResponseBody<MonthlyStatisticsResponse>> getMonthlyStatistics(
99+
@RequestParam
100+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
101+
@Parameter(hidden = true) Long userId
102+
);
103+
104+
@Operation(
105+
summary = "잔디 요청 api",
106+
description = "USER 이상의 권한을 가진 사용자는 잔디를 요청합니다."
107+
108+
)
109+
@ApiResponse(content = @Content(schema = @Schema(implementation = GrassStatisticsResponse.class)))
110+
@SwaggerApiResponses(
111+
success = @SwaggerApiSuccessResponse(
112+
response = GrassStatisticsResponse.class,
113+
description = "일간 통계 요청 완료"),
114+
errors = {
115+
@SwaggerApiFailedResponse(ExceptionType.NEED_AUTHORIZED),
116+
@SwaggerApiFailedResponse(ExceptionType.USER_NOT_FOUND),
117+
}
118+
)
119+
@GetMapping("/grass")
120+
@AssignUserId
121+
@PreAuthorize("isAuthenticated() and hasRole('USER')")
122+
public ResponseEntity<ResponseBody<GrassStatisticsResponse>> getGrassStatistics(
123+
@RequestParam
124+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
125+
@Parameter(hidden = true) Long userId
126+
);
127+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.gpt.geumpumtabackend.statistics.controller;
2+
3+
import com.gpt.geumpumtabackend.global.aop.AssignUserId;
4+
import com.gpt.geumpumtabackend.global.response.ResponseBody;
5+
import com.gpt.geumpumtabackend.global.response.ResponseUtil;
6+
import com.gpt.geumpumtabackend.statistics.api.StatisticsApi;
7+
import com.gpt.geumpumtabackend.statistics.dto.response.DailyStatisticsResponse;
8+
import com.gpt.geumpumtabackend.statistics.dto.response.GrassStatisticsResponse;
9+
import com.gpt.geumpumtabackend.statistics.dto.response.MonthlyStatisticsResponse;
10+
import com.gpt.geumpumtabackend.statistics.dto.response.WeeklyStatisticsResponse;
11+
import com.gpt.geumpumtabackend.statistics.service.StatisticsService;
12+
import lombok.RequiredArgsConstructor;
13+
import org.springframework.format.annotation.DateTimeFormat;
14+
import org.springframework.http.ResponseEntity;
15+
import org.springframework.security.access.prepost.PreAuthorize;
16+
import org.springframework.web.bind.annotation.GetMapping;
17+
import org.springframework.web.bind.annotation.RequestMapping;
18+
import org.springframework.web.bind.annotation.RequestParam;
19+
import org.springframework.web.bind.annotation.RestController;
20+
21+
import java.time.LocalDate;
22+
23+
@RestController
24+
@RequiredArgsConstructor
25+
@RequestMapping("/api/v1/statistics")
26+
public class StatisticsController implements StatisticsApi {
27+
28+
private final StatisticsService statisticsService;
29+
30+
@GetMapping("/day")
31+
@AssignUserId
32+
@PreAuthorize("isAuthenticated() and hasRole('USER')")
33+
public ResponseEntity<ResponseBody<DailyStatisticsResponse>> getDailyStatistics(
34+
@RequestParam
35+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
36+
Long userId
37+
) {
38+
return ResponseEntity.ok(ResponseUtil.createSuccessResponse(
39+
statisticsService.getDailyStatistics(date, userId))
40+
);
41+
}
42+
43+
@GetMapping("/week")
44+
@AssignUserId
45+
@PreAuthorize("isAuthenticated() and hasRole('USER')")
46+
public ResponseEntity<ResponseBody<WeeklyStatisticsResponse>> getWeeklyStatistics(
47+
@RequestParam
48+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
49+
Long userId
50+
) {
51+
return ResponseEntity.ok(ResponseUtil.createSuccessResponse(
52+
statisticsService.getWeeklyStatistics(date, userId))
53+
);
54+
}
55+
56+
@GetMapping("/month")
57+
@AssignUserId
58+
@PreAuthorize("isAuthenticated() and hasRole('USER')")
59+
public ResponseEntity<ResponseBody<MonthlyStatisticsResponse>> getMonthlyStatistics(
60+
@RequestParam
61+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
62+
Long userId
63+
){
64+
return ResponseEntity.ok(ResponseUtil.createSuccessResponse(
65+
statisticsService.getMonthlyStatistics(date, userId)
66+
));
67+
}
68+
69+
@GetMapping("/grass")
70+
@AssignUserId
71+
@PreAuthorize("isAuthenticated() and hasRole('USER')")
72+
public ResponseEntity<ResponseBody<GrassStatisticsResponse>> getGrassStatistics(
73+
@RequestParam
74+
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date,
75+
Long userId
76+
){
77+
return ResponseEntity.ok(ResponseUtil.createSuccessResponse(
78+
statisticsService.getGrassStatistics(date, userId)
79+
));
80+
}
81+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.gpt.geumpumtabackend.statistics.dto;
2+
3+
public interface DayMaxFocusAndFullTimeStatistics {
4+
Integer getTotalStudySeconds();
5+
Integer getMaxFocusSeconds();
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.gpt.geumpumtabackend.statistics.dto;
2+
3+
public interface GrassStatistics {
4+
String getDate();
5+
Integer getLevel();
6+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.gpt.geumpumtabackend.statistics.dto;
2+
3+
public interface MonthlyStatistics {
4+
Long getTotalMonthSeconds(); // 총 공부시간(초)
5+
Integer getAverageDailySeconds(); // 월 일수로 나눈 일일 평균(초)
6+
Integer getMaxConsecutiveStudyDays(); // 해당 월 내 최장 연속 공부 일수
7+
Integer getStudiedDays(); // 이번 달 공부 일수(>0초인 날의 수)
8+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.gpt.geumpumtabackend.statistics.dto;
2+
3+
public interface TwoHourSlotStatistics {
4+
String getSlotStart();
5+
String getSlotEnd();
6+
Integer getSecondsStudied();
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.gpt.geumpumtabackend.statistics.dto;
2+
3+
public interface WeeklyStatistics {
4+
Long getTotalWeekSeconds();
5+
Integer getMaxConsecutiveStudyDays();
6+
Integer getAverageDailySeconds(); // 7일 평균(초), 소수점 버림
7+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.gpt.geumpumtabackend.statistics.dto.response;
2+
3+
import com.gpt.geumpumtabackend.statistics.dto.DayMaxFocusAndFullTimeStatistics;
4+
import com.gpt.geumpumtabackend.statistics.dto.TwoHourSlotStatistics;
5+
6+
import java.util.List;
7+
8+
public record DailyStatisticsResponse(
9+
List<TwoHourSlotStatistics> statisticsList,
10+
DayMaxFocusAndFullTimeStatistics dayMaxFocusAndFullTimeStatistics
11+
) {
12+
public static DailyStatisticsResponse from(List<TwoHourSlotStatistics> statisticsList, DayMaxFocusAndFullTimeStatistics dayMaxFocusAndFullTimeStatistics){
13+
return new DailyStatisticsResponse(statisticsList, dayMaxFocusAndFullTimeStatistics);
14+
}
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.gpt.geumpumtabackend.statistics.dto.response;
2+
3+
import com.gpt.geumpumtabackend.statistics.dto.GrassStatistics;
4+
5+
import java.util.List;
6+
7+
public record GrassStatisticsResponse(
8+
List<GrassStatistics> grassStatistics
9+
) {
10+
public static GrassStatisticsResponse from(List<GrassStatistics> grassStatistics) {
11+
return new GrassStatisticsResponse(grassStatistics);
12+
}
13+
}

0 commit comments

Comments
 (0)