Skip to content

Commit c4849a0

Browse files
committed
feat : 개인 랭킹 조회 기능
1 parent c09199e commit c4849a0

File tree

9 files changed

+465
-0
lines changed

9 files changed

+465
-0
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.PersonalRankingResponse;
7+
import com.gpt.geumpumtabackend.rank.service.PersonalRankService;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.format.annotation.DateTimeFormat;
10+
import org.springframework.http.ResponseEntity;
11+
import org.springframework.security.access.prepost.PreAuthorize;
12+
import org.springframework.web.bind.annotation.GetMapping;
13+
import org.springframework.web.bind.annotation.RequestMapping;
14+
import org.springframework.web.bind.annotation.RequestParam;
15+
import org.springframework.web.bind.annotation.RestController;
16+
17+
import java.time.LocalDateTime;
18+
19+
@RestController
20+
@RequestMapping("/api/v1/rank/personal")
21+
@RequiredArgsConstructor
22+
public class PersonalRankController {
23+
24+
private final PersonalRankService personalRankService;
25+
26+
/*
27+
개인 일간 랭킹 조회
28+
*/
29+
@GetMapping("/daily")
30+
@PreAuthorize("isAuthenticated() AND hasRole('USER')")
31+
@AssignUserId
32+
public ResponseEntity<ResponseBody<PersonalRankingResponse>> getDailyRanking(Long userId, @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime date){
33+
PersonalRankingResponse response;
34+
35+
if (date == null) {
36+
// 현재 진행중인 일간 랭킹
37+
response = personalRankService.getCurrentDaily(userId);
38+
} else {
39+
// 특정 날짜의 확정된 일간 랭킹
40+
response = personalRankService.getCompletedDaily(userId, date);
41+
}
42+
43+
return ResponseEntity.ok(ResponseUtil.createSuccessResponse(response));
44+
}
45+
/*
46+
개인 주간 랭킹 조회
47+
*/
48+
@GetMapping("/weekly")
49+
@PreAuthorize("isAuthenticated() AND hasRole('USER')")
50+
@AssignUserId
51+
public ResponseEntity<ResponseBody<PersonalRankingResponse>> getWeeklyRanking(Long userId, @RequestParam(required = false) LocalDateTime date){
52+
PersonalRankingResponse response;
53+
54+
if (date == null) {
55+
// 현재 진행중인 주간 랭킹
56+
response = personalRankService.getCurrentWeekly(userId);
57+
} else {
58+
// 특정 날짜의 확정된 주간 랭킹
59+
response = personalRankService.getCompletedWeekly(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<PersonalRankingResponse>> getMonthlyRanking(Long userId, @RequestParam(required = false) LocalDateTime date){
71+
PersonalRankingResponse response;
72+
73+
if (date == null) {
74+
// 현재 진행중인 월간 랭킹
75+
response = personalRankService.getCurrentMonthly(userId);
76+
} else {
77+
// 특정 날짜의 확정된 월간 랭킹
78+
response = personalRankService.getCompletedMonthly(userId, date);
79+
}
80+
81+
return ResponseEntity.ok(ResponseUtil.createSuccessResponse(response));
82+
}
83+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.gpt.geumpumtabackend.rank.domain;
2+
3+
public enum RankingType {
4+
DAILY,
5+
WEEKLY,
6+
MONTHLY
7+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.gpt.geumpumtabackend.rank.domain;
2+
3+
import com.gpt.geumpumtabackend.user.domain.User;
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 UserRanking {
13+
14+
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
15+
private Long id;
16+
17+
@ManyToOne(fetch = FetchType.LAZY)
18+
@JoinColumn(name = "user_id")
19+
private User user;
20+
21+
@Column(nullable = false)
22+
private int 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 UserRanking(Long id, User user, int rank, Long totalMillis, RankingType rankingType, LocalDateTime calculatedAt) {
36+
this.id = id;
37+
this.user = user;
38+
this.rank = rank;
39+
this.totalMillis = totalMillis;
40+
this.rankingType = rankingType;
41+
this.calculatedAt = calculatedAt;
42+
}
43+
}
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;
2+
3+
import lombok.Getter;
4+
5+
@Getter
6+
public class UserRankingTemp {
7+
private Long userId;
8+
private String username;
9+
private Long totalMillis;
10+
private Integer rank;
11+
12+
public UserRankingTemp(Long userId, String username, Long totalMillis, Integer rank) {
13+
this.userId = userId;
14+
this.username = username;
15+
this.totalMillis = totalMillis;
16+
this.rank = rank;
17+
}
18+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.gpt.geumpumtabackend.rank.dto.response;
2+
3+
import com.gpt.geumpumtabackend.rank.dto.UserRankingTemp;
4+
5+
//TODO : 학과 추가
6+
public record PersonalRankingEntryResponse(
7+
Long userId,
8+
Long totalMillis,
9+
Integer rank,
10+
String username
11+
) {
12+
public static PersonalRankingEntryResponse of(UserRankingTemp userRankingTemp) {
13+
return new PersonalRankingEntryResponse(
14+
userRankingTemp.getUserId(),
15+
userRankingTemp.getTotalMillis(),
16+
userRankingTemp.getRank(),
17+
userRankingTemp.getUsername()
18+
);
19+
}
20+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.gpt.geumpumtabackend.rank.dto.response;
2+
3+
import java.util.List;
4+
5+
6+
public record PersonalRankingResponse(
7+
List<PersonalRankingEntryResponse> topRanks, PersonalRankingEntryResponse myRanking
8+
) {
9+
10+
}
11+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.gpt.geumpumtabackend.rank.repository;
2+
3+
import com.gpt.geumpumtabackend.rank.domain.UserRanking;
4+
import com.gpt.geumpumtabackend.rank.dto.UserRankingTemp;
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+
import org.springframework.stereotype.Repository;
9+
10+
import java.time.LocalDateTime;
11+
import java.util.List;
12+
13+
@Repository
14+
public interface UserRankingRepository extends JpaRepository<UserRanking, Long> {
15+
16+
/*
17+
끝난 일간 랭킹
18+
*/
19+
@Query("""
20+
SELECT new com.gpt.geumpumtabackend.rank.dto.UserRankingTemp(
21+
ur.user.id,
22+
ur.user.name,
23+
ur.totalMillis,
24+
ur.rank)
25+
FROM UserRanking ur WHERE ur.calculatedAt =:date
26+
ORDER BY ur.rank ASC
27+
""")
28+
List<UserRankingTemp> getFinishedPersonalRanking(@Param("date") LocalDateTime period);
29+
30+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package com.gpt.geumpumtabackend.rank.service;
2+
3+
import com.gpt.geumpumtabackend.rank.dto.response.PersonalRankingResponse;
4+
import com.gpt.geumpumtabackend.rank.dto.response.PersonalRankingEntryResponse;
5+
import com.gpt.geumpumtabackend.rank.dto.UserRankingTemp;
6+
import com.gpt.geumpumtabackend.rank.repository.UserRankingRepository;
7+
import com.gpt.geumpumtabackend.study.repository.StudySessionRepository;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.stereotype.Service;
10+
11+
import java.time.DayOfWeek;
12+
import java.time.LocalDate;
13+
import java.time.LocalDateTime;
14+
import java.util.ArrayList;
15+
import java.util.List;
16+
17+
18+
19+
@Service
20+
@RequiredArgsConstructor
21+
public class PersonalRankService {
22+
23+
private final UserRankingRepository userRankingRepository;
24+
private final StudySessionRepository studySessionRepository;
25+
/*
26+
현재 진행 중인 세션의 일간 랭킹 조회
27+
*/
28+
public PersonalRankingResponse getCurrentDaily(Long userId) {
29+
LocalDateTime startToday = LocalDate.now().atStartOfDay();
30+
LocalDateTime endToday = LocalDate.now().atTime(23, 59, 59);
31+
LocalDateTime nowTime = LocalDateTime.now();
32+
List<UserRankingTemp> userRankingTempList = studySessionRepository.calculateCurrentPeriodRanking(startToday, endToday, nowTime);
33+
PersonalRankingEntryResponse myRanking = null;
34+
List<PersonalRankingEntryResponse> topRankings = new ArrayList<>();
35+
for (UserRankingTemp temp : userRankingTempList) {
36+
PersonalRankingEntryResponse entry = PersonalRankingEntryResponse.of(temp);
37+
topRankings.add(entry);
38+
39+
if(temp.getUserId().equals(userId)){
40+
myRanking = entry;
41+
}
42+
}
43+
return new PersonalRankingResponse(topRankings, myRanking);
44+
}
45+
46+
/*
47+
완료된 일간 랭킹 조회
48+
*/
49+
public PersonalRankingResponse getCompletedDaily(Long userId, LocalDateTime day) {
50+
List<UserRankingTemp> userRankingTempList = userRankingRepository.getFinishedPersonalRanking(day);
51+
PersonalRankingEntryResponse myRanking = null;
52+
List<PersonalRankingEntryResponse> topRankings = new ArrayList<>();
53+
for (UserRankingTemp temp : userRankingTempList) {
54+
PersonalRankingEntryResponse entry = PersonalRankingEntryResponse.of(temp);
55+
topRankings.add(entry);
56+
57+
if(temp.getUserId().equals(userId)){
58+
myRanking = entry;
59+
}
60+
}
61+
return new PersonalRankingResponse(topRankings, myRanking);
62+
}
63+
64+
/*
65+
현재 진행 중인 세션의 주간 랭킹 조회
66+
*/
67+
public PersonalRankingResponse getCurrentWeekly(Long userId) {
68+
LocalDate today = LocalDate.now();
69+
LocalDateTime weekStart = today.with(DayOfWeek.MONDAY).atStartOfDay();
70+
LocalDateTime weekEnd = today.with(DayOfWeek.SUNDAY).atTime(23, 59, 59);
71+
LocalDateTime nowTime = LocalDateTime.now();
72+
List<UserRankingTemp> userRankingTempList = studySessionRepository.calculateCurrentPeriodRanking(weekStart, weekEnd, nowTime);
73+
PersonalRankingEntryResponse myRanking = null;
74+
List<PersonalRankingEntryResponse> topRankings = new ArrayList<>();
75+
for (UserRankingTemp temp : userRankingTempList) {
76+
PersonalRankingEntryResponse entry = PersonalRankingEntryResponse.of(temp);
77+
topRankings.add(entry);
78+
if(temp.getUserId().equals(userId)){
79+
myRanking = entry;
80+
}
81+
}
82+
return new PersonalRankingResponse(topRankings, myRanking);
83+
}
84+
85+
/*
86+
완료된 주간 랭킹 조회
87+
*/
88+
89+
public PersonalRankingResponse getCompletedWeekly(Long userId, LocalDateTime weekFirstDay) {
90+
List<UserRankingTemp> userRankingTempList = userRankingRepository.getFinishedPersonalRanking(weekFirstDay);
91+
PersonalRankingEntryResponse myRanking = null;
92+
List<PersonalRankingEntryResponse> topRankings = new ArrayList<>();
93+
for (UserRankingTemp temp : userRankingTempList) {
94+
PersonalRankingEntryResponse entry = PersonalRankingEntryResponse.of(temp);
95+
topRankings.add(entry);
96+
97+
if(temp.getUserId().equals(userId)){
98+
myRanking = entry;
99+
}
100+
}
101+
return new PersonalRankingResponse(topRankings, myRanking);
102+
}
103+
104+
/*
105+
현재 진행 중인 세션의 월간 랭킹 조회
106+
*/
107+
public PersonalRankingResponse getCurrentMonthly(Long userId) {
108+
LocalDate today = LocalDate.now();
109+
LocalDateTime startMonth = today.withDayOfMonth(1).atStartOfDay();
110+
LocalDateTime endMonth = today.withDayOfMonth(today.lengthOfMonth()).atTime(23, 59, 59);
111+
LocalDateTime nowTime = LocalDateTime.now();
112+
List<UserRankingTemp> userRankingTempList = studySessionRepository.calculateCurrentPeriodRanking(startMonth, endMonth, nowTime);
113+
PersonalRankingEntryResponse myRanking = null;
114+
List<PersonalRankingEntryResponse> topRankings = new ArrayList<>();
115+
for (UserRankingTemp temp : userRankingTempList) {
116+
PersonalRankingEntryResponse entry = PersonalRankingEntryResponse.of(temp);
117+
topRankings.add(entry);
118+
119+
if(temp.getUserId().equals(userId)){
120+
myRanking = entry;
121+
}
122+
}
123+
return new PersonalRankingResponse(topRankings, myRanking);
124+
}
125+
/*
126+
완료된 월간 랭킹 조회
127+
*/
128+
public PersonalRankingResponse getCompletedMonthly(Long userId, LocalDateTime monthFirstDay) {
129+
List<UserRankingTemp> userRankingTempList = userRankingRepository.getFinishedPersonalRanking(monthFirstDay);
130+
PersonalRankingEntryResponse myRanking = null;
131+
List<PersonalRankingEntryResponse> topRankings = new ArrayList<>();
132+
for (UserRankingTemp temp : userRankingTempList) {
133+
PersonalRankingEntryResponse entry = PersonalRankingEntryResponse.of(temp);
134+
topRankings.add(entry);
135+
136+
if(temp.getUserId().equals(userId)){
137+
myRanking = entry;
138+
}
139+
}
140+
return new PersonalRankingResponse(topRankings, myRanking);
141+
}
142+
}

0 commit comments

Comments
 (0)