-
Notifications
You must be signed in to change notification settings - Fork 0
5 - rank system #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 28 commits
6eb5796
1e8da19
7dce3ef
0ef14a4
91ac7c2
210b0d0
7855b49
bc65f7a
a2f9ec8
f274026
4cce774
07e3606
a42a331
c748742
14e7af4
823fdf7
2b1dd70
1f93210
c2caf7c
2fc1e70
6e642a9
5854644
08478b2
6481c65
35e0844
7e202be
593a11e
7581978
ea0e18e
994ac46
039cbd7
e8c46cd
137867c
9d0d536
d20d357
38a5611
cced1e3
2320115
760ee5a
fcda490
4ef693c
f273c54
c9b7627
dd82968
6aab0e5
85db00e
22c8f80
3b16536
601722c
39b3095
3297aab
ea5b953
a6517ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| package com.example.domaserver.domain.rank.entity; | ||
|
|
||
| import com.example.domaserver.domain.user.entity.User; | ||
| import jakarta.persistence.*; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| @Entity | ||
| @Getter | ||
| @Builder | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| public class Rank { | ||
|
|
||
| @Id | ||
| @GeneratedValue(strategy =GenerationType.IDENTITY) | ||
| private Long RankId; | ||
| private int PenaltyPoints; | ||
| private double RankScore; | ||
|
|
||
| @ManyToOne | ||
| @JoinColumn(name = "user_id") | ||
| private User user; | ||
|
|
||
| public Rank(Long rankId, Double rankScore) { | ||
| RankId = rankId; | ||
| RankScore = rankScore; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| package com.example.domaserver.domain.rank.presentation; | ||
|
|
||
|
|
||
| import com.example.domaserver.domain.rank.entity.Rank; | ||
| import com.example.domaserver.domain.rank.service.GetRankingService; | ||
| import com.example.domaserver.domain.rank.service.GetTopRankingService; | ||
| import com.example.domaserver.domain.rank.service.UpdateRankingService; | ||
| import com.example.domaserver.domain.user.entity.User; | ||
| import com.example.domaserver.domain.user.service.UserService; | ||
| import com.example.domaserver.global.security.jwt.JwtService; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.RequestHeader; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @RequiredArgsConstructor | ||
| @RestController | ||
| @RequestMapping("/home") | ||
| public class RankController { | ||
|
|
||
| private final GetRankingService getRankingService; | ||
| private final GetTopRankingService getTopRankingService; | ||
| private final UpdateRankingService updateRankingService; | ||
| private final UserService userService; | ||
| private final JwtService jwtService; | ||
|
|
||
| @GetMapping("/rank") | ||
| public ResponseEntity<List<Rank>> getRanks() { | ||
| List<Rank> topRanks = getTopRankingService.getTopRanking(25); | ||
| return ResponseEntity.ok(topRanks); | ||
| } | ||
|
|
||
| @GetMapping("/my-rank") | ||
| public ResponseEntity<Long> getMyRank(@RequestHeader("Authorization") String token) { | ||
| User user = jwtService.getUserFromToken(token.substring(7)); | ||
| Long rank = getRankingService.getRanking(user); | ||
| return ResponseEntity.ok(rank); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package com.example.domaserver.domain.rank.presentation.dto.response; | ||
|
|
||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| @Builder | ||
| @Getter | ||
| public class RankResponse { | ||
| private UUID Id; | ||
| private String name; | ||
| private String profileImageUrl; | ||
| private int penaltyPoints; | ||
| private double RankScore; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.example.domaserver.domain.rank.service; | ||
|
|
||
| import com.example.domaserver.domain.user.entity.User; | ||
|
|
||
| public interface GetRankingService { | ||
| Long getRanking(User user); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.example.domaserver.domain.rank.service; | ||
|
|
||
| import com.example.domaserver.domain.rank.entity.Rank; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public interface GetTopRankingService { | ||
| List<Rank> getTopRanking(int topN); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.example.domaserver.domain.rank.service; | ||
|
|
||
| import com.example.domaserver.domain.rank.entity.Rank; | ||
|
|
||
| public interface UpdateRankingService { | ||
| void updateRanking(Rank rank); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package com.example.domaserver.domain.rank.service.impl; | ||
|
|
||
| import com.example.domaserver.domain.rank.service.GetRankingService; | ||
| import com.example.domaserver.domain.user.entity.User; | ||
| import com.example.domaserver.global.annotation.ServiceWithReadOnlyTransactional; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.data.redis.core.RedisTemplate; | ||
|
|
||
| @ServiceWithReadOnlyTransactional | ||
| @RequiredArgsConstructor | ||
| public class GetRankingServiceImpl implements GetRankingService { | ||
|
|
||
| private static final String GET_RANKING = "getRank"; | ||
|
|
||
| private final RedisTemplate redisTemplate; | ||
|
|
||
| public Long getRanking(User user) { | ||
| Long rank = redisTemplate.opsForZSet().rank(GET_RANKING, user.getId()); | ||
| return (rank != null) ? rank + 1 : null; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| package com.example.domaserver.domain.rank.service.impl; | ||
|
|
||
| import com.example.domaserver.domain.rank.entity.Rank; | ||
| import com.example.domaserver.domain.rank.service.GetTopRankingService; | ||
| import com.example.domaserver.global.annotation.ServiceWithReadOnlyTransactional; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.data.redis.core.RedisTemplate; | ||
| import org.springframework.data.redis.core.ZSetOperations; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
|
|
||
| @ServiceWithReadOnlyTransactional | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. get service는 ReadOnly 씁니다. 두개 붙일 필요 없이 ReadOnly만 남겨주세요
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 수정했습니다! |
||
| @RequiredArgsConstructor | ||
| public class GetTopRankingServiceImpl implements GetTopRankingService { | ||
|
|
||
| private static final String GET_TOP_RANKING = "getTopRanking"; | ||
|
|
||
| private final RedisTemplate redisTemplate; | ||
|
|
||
| public List<Rank> getTopRanking(int topN) { | ||
| Set<ZSetOperations.TypedTuple<Long>> rankedUsers = | ||
| redisTemplate.opsForZSet().reverseRange(GET_TOP_RANKING, 0, topN - 1); | ||
|
|
||
| List<Rank> userRanks = new ArrayList<>(); | ||
| for (ZSetOperations.TypedTuple<Long> entry : rankedUsers) { | ||
| Long RankId = entry.getValue(); | ||
| Double RankScore = entry.getScore(); | ||
| userRanks.add(new Rank(RankId, RankScore)); | ||
| } | ||
|
|
||
| return userRanks; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package com.example.domaserver.domain.rank.service.impl; | ||
|
|
||
| import com.example.domaserver.domain.rank.entity.Rank; | ||
| import com.example.domaserver.domain.rank.service.UpdateRankingService; | ||
| import com.example.domaserver.global.annotation.ServiceWithTransaction; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.data.redis.core.RedisTemplate; | ||
|
|
||
|
|
||
| @ServiceWithTransaction | ||
| @RequiredArgsConstructor | ||
| public class UpdateRankingServiceImpl implements UpdateRankingService { | ||
|
|
||
| private static final String UPDATE_RANKING = "update ranking set rank=rank+1 where rank=?"; | ||
|
|
||
| private final RedisTemplate redisTemplate; | ||
|
|
||
| public void updateRanking(Rank rank) { | ||
| redisTemplate.opsForSet().add(UPDATE_RANKING, rank.getUser().getId(), rank.getPenaltyPoints()); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.example.domaserver.domain.user.service; | ||
|
|
||
| import com.example.domaserver.domain.user.entity.User; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| public interface UserService { | ||
| User findByUsername(String name); | ||
| User findById(UUID id); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package com.example.domaserver.domain.user.service.impl; | ||
|
|
||
| import com.example.domaserver.domain.user.entity.User; | ||
| import com.example.domaserver.domain.user.repository.UserRepository; | ||
| import com.example.domaserver.domain.user.service.UserService; | ||
| import com.example.domaserver.global.annotation.ServiceWithTransaction; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| @RequiredArgsConstructor | ||
| @ServiceWithTransaction | ||
| public class UserServiceImpl implements UserService { | ||
|
|
||
| private final UserRepository userRepository; | ||
|
|
||
| @Override | ||
| public User findByUsername(String username) { | ||
| return userRepository.findByUsername(username) | ||
| .orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. custom exception을 만들어서 예외처리를 해보는건 어떨까요
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
|
|
||
| @Override | ||
| public User findById(UUID id) { | ||
| return userRepository.findById(id) | ||
| .orElseThrow(() -> new UsernameNotFoundException("User not found with id: " + id)); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| package com.example.domaserver.global.security.jwt; | ||
|
|
||
|
|
||
| import com.example.domaserver.domain.user.entity.User; | ||
| import com.example.domaserver.domain.user.service.UserService; | ||
| import io.jsonwebtoken.Claims; | ||
| import io.jsonwebtoken.Jwts; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.security.core.userdetails.UserDetails; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.beans.factory.annotation.Value; | ||
|
|
||
| import java.util.Date; | ||
| import java.util.function.Function; | ||
|
|
||
| @Service | ||
| @Slf4j | ||
| public class JwtService { | ||
|
|
||
| private final UserService userService; | ||
|
|
||
| @Value("${jwt.secret}") | ||
| private String secretKey; | ||
|
|
||
| public JwtService(UserService userService) { | ||
| this.userService = userService; | ||
| } | ||
|
|
||
| public String extractUsername(String token) { | ||
| return extractClaim(token, Claims::getSubject); | ||
| } | ||
|
|
||
| public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) { | ||
| final Claims claims = extractAllClaims(token); | ||
| return claimsResolver.apply(claims); | ||
| } | ||
|
|
||
| private Claims extractAllClaims(String token) { | ||
| return Jwts.parser() | ||
| .setSigningKey(secretKey) | ||
| .parseClaimsJws(token) | ||
| .getBody(); | ||
| } | ||
|
|
||
| public boolean isTokenValid(String token, UserDetails userDetails) { | ||
| final String username = extractUsername(token); | ||
| return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); | ||
| } | ||
|
|
||
| private boolean isTokenExpired(String token) { | ||
| return extractExpiration(token).before(new Date()); | ||
| } | ||
|
|
||
| public Date extractExpiration(String token) { | ||
| return extractClaim(token, Claims::getExpiration); | ||
| } | ||
|
|
||
| public User getUserFromToken(String token) { | ||
| String username = extractUsername(token); | ||
| return userService.findByUsername(username); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get service는 ReadOnly 씁니다. 두개 붙일 필요 없이 ReadOnly만 남겨주세요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
593a11e
수정했습니다!