Skip to content

Commit d460ba5

Browse files
committed
feat : 학과 랭킹 조회 기능
1 parent c4849a0 commit d460ba5

File tree

12 files changed

+604
-2
lines changed

12 files changed

+604
-2
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package com.gpt.geumpumtabackend.rank.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.rank.dto.response.DepartmentRankingResponse;
7+
import com.gpt.geumpumtabackend.rank.service.DepartmentRankService;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.http.ResponseEntity;
10+
import org.springframework.security.access.prepost.PreAuthorize;
11+
import org.springframework.web.bind.annotation.GetMapping;
12+
import org.springframework.web.bind.annotation.RequestMapping;
13+
import org.springframework.web.bind.annotation.RequestParam;
14+
import org.springframework.web.bind.annotation.RestController;
15+
16+
import java.time.LocalDateTime;
17+
18+
@RestController
19+
@RequestMapping("/api/v1/rank/department")
20+
@RequiredArgsConstructor
21+
public class DepartmentRankController {
22+
23+
private final DepartmentRankService departmentRankService;
24+
25+
/*
26+
학과 랭킹 일간 조회
27+
*/
28+
@GetMapping("/daily")
29+
@PreAuthorize("isAuthenticated() AND hasRole('USER')")
30+
@AssignUserId
31+
public ResponseEntity<ResponseBody<DepartmentRankingResponse>> getDailyRanking(Long userId, @RequestParam(required = false) LocalDateTime date){
32+
DepartmentRankingResponse response;
33+
34+
if (date == null) {
35+
// 현재 진행중인 일간 랭킹
36+
response = departmentRankService.getCurrentDailyDepartmentRanking(userId);
37+
} else {
38+
// 특정 날짜의 확정된 일간 랭킹
39+
response = departmentRankService.getCompletedDailyDepartmentRanking(userId, date);
40+
}
41+
42+
return ResponseEntity.ok(ResponseUtil.createSuccessResponse(response));
43+
}
44+
45+
/*
46+
학과 랭킹 주간 조회
47+
*/
48+
@GetMapping("/weekly")
49+
@PreAuthorize("isAuthenticated() AND hasRole('USER')")
50+
@AssignUserId
51+
public ResponseEntity<ResponseBody<DepartmentRankingResponse>> getWeeklyRanking(Long userId, @RequestParam(required = false) LocalDateTime date){
52+
DepartmentRankingResponse response;
53+
54+
if (date == null) {
55+
// 현재 진행중인 주간 랭킹
56+
response = departmentRankService.getCurrentWeeklyDepartmentRanking(userId);
57+
} else {
58+
// 특정 날짜의 확정된 주간 랭킹
59+
response = departmentRankService.getCompletedWeeklyDepartmentRanking(userId, date);
60+
}
61+
62+
return ResponseEntity.ok(ResponseUtil.createSuccessResponse(response));
63+
}
64+
/*
65+
학과 랭킹 월간 조회
66+
*/
67+
@GetMapping("/monthly")
68+
@PreAuthorize("isAuthenticated() AND hasRole('USER')")
69+
@AssignUserId
70+
public ResponseEntity<ResponseBody<DepartmentRankingResponse>> getMonthlyRanking(Long userId, @RequestParam(required = false) LocalDateTime date){
71+
DepartmentRankingResponse response;
72+
73+
if (date == null) {
74+
// 현재 진행중인 월간 랭킹
75+
response = departmentRankService.getCurrentMonthlyDepartmentRanking(userId);
76+
} else {
77+
// 특정 날짜의 확정된 월간 랭킹
78+
response = departmentRankService.getCompletedMonthlyDepartmentRanking(userId, date);
79+
}
80+
81+
return ResponseEntity.ok(ResponseUtil.createSuccessResponse(response));
82+
}
83+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.gpt.geumpumtabackend.rank.domain;
2+
3+
import com.gpt.geumpumtabackend.user.domain.Department;
4+
import jakarta.persistence.*;
5+
import lombok.Builder;
6+
import lombok.NoArgsConstructor;
7+
8+
import java.time.LocalDateTime;
9+
10+
@Entity
11+
@NoArgsConstructor
12+
public class DepartmentRanking {
13+
14+
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
15+
private Long id;
16+
17+
@Column(nullable = false)
18+
@Enumerated(EnumType.STRING)
19+
private Department department;
20+
21+
@Column(nullable = false)
22+
private Integer rank;
23+
24+
@Column(nullable = false)
25+
private Long totalMillis;
26+
27+
@Column(nullable = false)
28+
@Enumerated(EnumType.STRING)
29+
private RankingType rankingType;
30+
31+
@Column(nullable = false)
32+
private LocalDateTime calculatedAt;
33+
34+
@Builder
35+
public DepartmentRanking(Department department, Integer rank, Long totalMillis, RankingType rankingType, LocalDateTime calculatedAt) {
36+
this.department = department;
37+
this.rank = rank;
38+
this.totalMillis = totalMillis;
39+
this.rankingType = rankingType;
40+
this.calculatedAt = calculatedAt;
41+
}
42+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.gpt.geumpumtabackend.rank.domain;
2+
3+
import lombok.Getter;
4+
5+
@Getter
6+
public enum RankingScope {
7+
8+
PERSONAL("개인"),DEPARTMENT("학과");
9+
10+
private final String name;
11+
12+
RankingScope(String name) {
13+
this.name = name;
14+
}
15+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.gpt.geumpumtabackend.rank.dto;
2+
3+
import com.gpt.geumpumtabackend.user.domain.Department;
4+
import lombok.Getter;
5+
6+
@Getter
7+
public class DepartmentRankingTemp {
8+
private Department departmentName;
9+
private Long totalMillis;
10+
private Integer rank;
11+
12+
public DepartmentRankingTemp(Department departmentName, Long totalMillis, Integer rank) {
13+
this.departmentName = departmentName;
14+
this.totalMillis = totalMillis;
15+
this.rank = rank;
16+
}
17+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.gpt.geumpumtabackend.rank.dto.response;
2+
3+
import com.gpt.geumpumtabackend.rank.dto.DepartmentRankingTemp;
4+
import com.gpt.geumpumtabackend.user.domain.Department;
5+
6+
public record DepartmentRankingEntryResponse(
7+
Department departmentName,
8+
Long totalMillis,
9+
Integer rank
10+
) {
11+
public static DepartmentRankingEntryResponse of(DepartmentRankingTemp departmentRankingTemp) {
12+
return new DepartmentRankingEntryResponse(
13+
departmentRankingTemp.getDepartmentName(),
14+
departmentRankingTemp.getTotalMillis(),
15+
departmentRankingTemp.getRank()
16+
);
17+
}
18+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.gpt.geumpumtabackend.rank.dto.response;
2+
3+
4+
import java.util.List;
5+
6+
public record DepartmentRankingResponse(
7+
List<DepartmentRankingEntryResponse> topRanks,
8+
DepartmentRankingEntryResponse myDepartmentRanking)
9+
{ }
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.gpt.geumpumtabackend.rank.repository;
2+
3+
import com.gpt.geumpumtabackend.rank.domain.DepartmentRanking;
4+
import com.gpt.geumpumtabackend.rank.dto.DepartmentRankingTemp;
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+
12+
public interface DepartmentRankingRepository extends JpaRepository<DepartmentRanking, Long> {
13+
14+
15+
/*
16+
끝난 학과
17+
*/
18+
@Query("""
19+
SELECT new com.gpt.geumpumtabackend.rank.dto.DepartmentRankingTemp(
20+
dr.department,
21+
dr.totalMillis,
22+
dr.rank
23+
)
24+
FROM DepartmentRanking dr
25+
WHERE dr.calculatedAt =:period
26+
ORDER BY dr.rank ASC
27+
""")
28+
List<DepartmentRankingTemp> getFinishedDepartmentRanking(@Param("period") LocalDateTime period);
29+
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package com.gpt.geumpumtabackend.rank.service;
2+
3+
import com.gpt.geumpumtabackend.global.exception.BusinessException;
4+
import com.gpt.geumpumtabackend.global.exception.ExceptionType;
5+
import com.gpt.geumpumtabackend.rank.dto.DepartmentRankingTemp;
6+
import com.gpt.geumpumtabackend.rank.dto.response.DepartmentRankingEntryResponse;
7+
import com.gpt.geumpumtabackend.rank.dto.response.DepartmentRankingResponse;
8+
import com.gpt.geumpumtabackend.rank.repository.DepartmentRankingRepository;
9+
import com.gpt.geumpumtabackend.study.repository.StudySessionRepository;
10+
import com.gpt.geumpumtabackend.user.domain.User;
11+
import com.gpt.geumpumtabackend.user.repository.UserRepository;
12+
import lombok.RequiredArgsConstructor;
13+
import org.springframework.stereotype.Service;
14+
15+
import java.time.DayOfWeek;
16+
import java.time.LocalDate;
17+
import java.time.LocalDateTime;
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
21+
@Service
22+
@RequiredArgsConstructor
23+
public class DepartmentRankService {
24+
25+
private final DepartmentRankingRepository departmentRankingRepository;
26+
private final StudySessionRepository studySessionRepository;
27+
private final UserRepository userRepository;
28+
29+
/*
30+
현재 진행중인 학과 랭킹 일간 조회
31+
*/
32+
public DepartmentRankingResponse getCurrentDailyDepartmentRanking(Long userId){
33+
LocalDate today = LocalDate.now();
34+
LocalDateTime startDay = today.atStartOfDay();
35+
LocalDateTime endDay = today.atTime(23, 59, 59);
36+
LocalDateTime nowTime = LocalDateTime.now();
37+
List<DepartmentRankingTemp> departmentRankingList = studySessionRepository.calculateCurrentDepartmentRanking(startDay, endDay, nowTime);
38+
DepartmentRankingEntryResponse myRanking = null;
39+
List<DepartmentRankingEntryResponse> topRankings = new ArrayList<>();
40+
User user = userRepository.findById(userId).orElseThrow(()->new BusinessException(ExceptionType.USER_NOT_FOUND));
41+
for (DepartmentRankingTemp temp : departmentRankingList) {
42+
DepartmentRankingEntryResponse entry = DepartmentRankingEntryResponse.of(temp);
43+
topRankings.add(entry);
44+
45+
if(user.getDepartment().equals(temp.getDepartmentName())){
46+
myRanking = entry;
47+
}
48+
}
49+
return new DepartmentRankingResponse(topRankings, myRanking);
50+
}
51+
52+
/*
53+
완료된 학과 랭킹 일간 조회
54+
*/
55+
public DepartmentRankingResponse getCompletedDailyDepartmentRanking(Long userId, LocalDateTime startDay){
56+
List<DepartmentRankingTemp> departmentRankingList = departmentRankingRepository.getFinishedDepartmentRanking(startDay);
57+
DepartmentRankingEntryResponse myRanking = null;
58+
List<DepartmentRankingEntryResponse> topRankings = new ArrayList<>();
59+
User user = userRepository.findById(userId).orElseThrow(()->new BusinessException(ExceptionType.USER_NOT_FOUND));
60+
for (DepartmentRankingTemp temp : departmentRankingList) {
61+
DepartmentRankingEntryResponse entry = DepartmentRankingEntryResponse.of(temp);
62+
topRankings.add(entry);
63+
64+
if(user.getDepartment().equals(temp.getDepartmentName())){
65+
myRanking = entry;
66+
}
67+
}
68+
return new DepartmentRankingResponse(topRankings, myRanking);
69+
}
70+
71+
/*
72+
현재 진행중인 학과 랭킹 주간 조회
73+
*/
74+
public DepartmentRankingResponse getCurrentWeeklyDepartmentRanking(Long userId){
75+
LocalDate today = LocalDate.now();
76+
LocalDateTime weekStart = today.with(DayOfWeek.MONDAY).atStartOfDay();
77+
LocalDateTime weekEnd = today.with(DayOfWeek.SUNDAY).atTime(23, 59, 59);
78+
LocalDateTime nowTime = LocalDateTime.now();
79+
List<DepartmentRankingTemp> departmentRankingList = studySessionRepository.calculateCurrentDepartmentRanking(weekStart, weekEnd, nowTime);
80+
DepartmentRankingEntryResponse myRanking = null;
81+
List<DepartmentRankingEntryResponse> topRankings = new ArrayList<>();
82+
User user = userRepository.findById(userId).orElseThrow(()->new BusinessException(ExceptionType.USER_NOT_FOUND));
83+
for (DepartmentRankingTemp temp : departmentRankingList) {
84+
DepartmentRankingEntryResponse entry = DepartmentRankingEntryResponse.of(temp);
85+
topRankings.add(entry);
86+
87+
if(user.getDepartment().equals(temp.getDepartmentName())){
88+
myRanking = entry;
89+
}
90+
}
91+
return new DepartmentRankingResponse(topRankings, myRanking);
92+
}
93+
94+
/*
95+
완료된 학과 랭킹 주간 조회
96+
*/
97+
public DepartmentRankingResponse getCompletedWeeklyDepartmentRanking(Long userId, LocalDateTime weekFirstDay){
98+
List<DepartmentRankingTemp> departmentRankingList = departmentRankingRepository.getFinishedDepartmentRanking(weekFirstDay);
99+
DepartmentRankingEntryResponse myRanking = null;
100+
List<DepartmentRankingEntryResponse> topRankings = new ArrayList<>();
101+
User user = userRepository.findById(userId).orElseThrow(()->new BusinessException(ExceptionType.USER_NOT_FOUND));
102+
for (DepartmentRankingTemp temp : departmentRankingList) {
103+
DepartmentRankingEntryResponse entry = DepartmentRankingEntryResponse.of(temp);
104+
topRankings.add(entry);
105+
106+
if(user.getDepartment().equals(temp.getDepartmentName())){
107+
myRanking = entry;
108+
}
109+
}
110+
return new DepartmentRankingResponse(topRankings, myRanking);
111+
}
112+
113+
114+
/*
115+
현재 진행중인 학과 랭킹 월간 조회
116+
*/
117+
public DepartmentRankingResponse getCurrentMonthlyDepartmentRanking(Long userId){
118+
LocalDate today = LocalDate.now();
119+
LocalDateTime startMonth = today.withDayOfMonth(1).atStartOfDay();
120+
LocalDateTime endMonth = today.withDayOfMonth(today.lengthOfMonth()).atTime(23, 59, 59);
121+
LocalDateTime nowTime = LocalDateTime.now();
122+
List<DepartmentRankingTemp> departmentRankingList = studySessionRepository.calculateCurrentDepartmentRanking(startMonth, endMonth, nowTime);
123+
DepartmentRankingEntryResponse myRanking = null;
124+
List<DepartmentRankingEntryResponse> topRankings = new ArrayList<>();
125+
User user = userRepository.findById(userId).orElseThrow(()->new BusinessException(ExceptionType.USER_NOT_FOUND));
126+
for (DepartmentRankingTemp temp : departmentRankingList) {
127+
DepartmentRankingEntryResponse entry = DepartmentRankingEntryResponse.of(temp);
128+
topRankings.add(entry);
129+
130+
if(user.getDepartment().equals(temp.getDepartmentName())){
131+
myRanking = entry;
132+
}
133+
}
134+
return new DepartmentRankingResponse(topRankings, myRanking);
135+
}
136+
137+
138+
/*
139+
완료된 학과 랭킹 월간 조회
140+
*/
141+
public DepartmentRankingResponse getCompletedMonthlyDepartmentRanking(Long userId, LocalDateTime monthFirstDay){
142+
List<DepartmentRankingTemp> departmentRankingList = departmentRankingRepository.getFinishedDepartmentRanking(monthFirstDay);
143+
DepartmentRankingEntryResponse myRanking = null;
144+
List<DepartmentRankingEntryResponse> topRankings = new ArrayList<>();
145+
User user = userRepository.findById(userId).orElseThrow(()->new BusinessException(ExceptionType.USER_NOT_FOUND));
146+
for (DepartmentRankingTemp temp : departmentRankingList) {
147+
DepartmentRankingEntryResponse entry = DepartmentRankingEntryResponse.of(temp);
148+
topRankings.add(entry);
149+
150+
if(user.getDepartment().equals(temp.getDepartmentName())){
151+
myRanking = entry;
152+
}
153+
}
154+
return new DepartmentRankingResponse(topRankings, myRanking);
155+
}
156+
}

src/main/java/com/gpt/geumpumtabackend/study/domain/StudySession.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,19 @@ public class StudySession {
1717
@GeneratedValue(strategy = GenerationType.IDENTITY)
1818
private Long id;
1919

20+
@Column(nullable = false)
2021
private LocalDateTime startTime;
2122

2223
private LocalDateTime endTime;
2324

2425
private Long totalMillis;
2526

27+
@Column(nullable = false)
2628
@Enumerated(EnumType.STRING)
2729
private StudyStatus status;
2830

2931
@ManyToOne(fetch = FetchType.LAZY)
30-
@JoinColumn(name = "user_id")
32+
@JoinColumn(name = "user_id", nullable = false)
3133
private User user;
3234

3335
public void startStudySession(LocalDateTime startTime, User user) {

0 commit comments

Comments
 (0)