-
Notifications
You must be signed in to change notification settings - Fork 0
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
5 - rank system #6
base: develop
Are you sure you want to change the base?
Changes from 14 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
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,23 @@ | ||
package com.example.domaserver.domain.rank.config; | ||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.data.redis.connection.RedisConnectionFactory; | ||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; | ||
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; | ||
|
||
@EnableRedisRepositories | ||
@Configuration | ||
public class RedisConfig { | ||
@Value("${spring.redis.host}") | ||
private String redisHost; | ||
|
||
@Value("${spring.redis.port}") | ||
private int redisPort; | ||
|
||
@Bean | ||
public RedisConnectionFactory redisConnectionFactory() { | ||
return new LettuceConnectionFactory(redisHost, redisPort); | ||
} | ||
} |
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,46 @@ | ||
package com.example.domaserver.domain.rank.presentation; | ||
|
||
|
||
import com.example.domaserver.domain.rank.entity.Rank; | ||
import com.example.domaserver.domain.rank.service.RankService; | ||
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 org.springframework.beans.factory.annotation.Autowired; | ||
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; | ||
|
||
@RestController | ||
@RequestMapping("/home") | ||
public class RankController { | ||
private final RankService rankService; | ||
private final UserService userService; | ||
private final JwtService jwtService; | ||
|
||
@Autowired | ||
public RankController(RankService rankService, UserService userService, JwtService jwtService) { | ||
this.rankService = rankService; | ||
this.userService = userService; | ||
this.jwtService = jwtService; | ||
} | ||
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. @Autowired를 쓰는거 보다 클래스에 @requiredargsconstructor로 생성자 생성 할 수 있습니다 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. |
||
|
||
@GetMapping("/rank") | ||
public ResponseEntity<List<Rank>> getRanks() { | ||
List<Rank> topRanks = rankService.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 = rankService.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,11 @@ | ||
package com.example.domaserver.domain.rank.service; | ||
|
||
import com.example.domaserver.domain.rank.entity.Rank; | ||
import com.example.domaserver.domain.user.entity.User; | ||
import java.util.List; | ||
|
||
public interface RankService { | ||
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. service는 기능을 분리해서 구현해주세요 |
||
Long getRanking(User user); | ||
void updateRank(Rank rank); | ||
List<Rank> getTopRanking(int topN); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package com.example.domaserver.domain.rank.service.impl; | ||
|
||
import com.example.domaserver.domain.rank.entity.Rank; | ||
import com.example.domaserver.domain.rank.service.RankService; | ||
import com.example.domaserver.domain.user.entity.User; | ||
import com.example.domaserver.global.annotation.ServiceWithTransaction; | ||
import jakarta.transaction.Transactional; | ||
import org.springframework.beans.factory.annotation.Qualifier; | ||
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; | ||
|
||
|
||
@ServiceWithTransaction | ||
public class RankServiceImpl implements RankService { | ||
|
||
private static final String RANK_KEY = "USER_RANKING"; | ||
|
||
private final RedisTemplate redisTemplate; | ||
|
||
public RankServiceImpl(@Qualifier("redisTemplate") RedisTemplate redisTemplate) { | ||
this.redisTemplate = redisTemplate; | ||
} | ||
|
||
@Transactional | ||
public Long getRanking(User user) { | ||
Long rank = redisTemplate.opsForZSet().rank(RANK_KEY, user.getId()); | ||
return (rank != null) ? rank + 1 : null; | ||
} | ||
|
||
@Transactional | ||
public void updateRank(Rank rank) { | ||
redisTemplate.opsForZSet().add(RANK_KEY, rank.getUser().getId(), rank.getPenaltyPoints()); | ||
} | ||
|
||
@Transactional | ||
public List<Rank> getTopRanking(int topN) { | ||
Set<ZSetOperations.TypedTuple<Long>> rankedUsers = | ||
redisTemplate.opsForZSet().reverseRangeWithScores(RANK_KEY, 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,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,34 @@ | ||
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 org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
|
||
import java.util.UUID; | ||
|
||
@ServiceWithTransaction | ||
public class UserServiceImpl implements UserService { | ||
|
||
private final UserRepository userRepository; | ||
|
||
@Autowired | ||
public UserServiceImpl(UserRepository userRepository) { | ||
this.userRepository = userRepository; | ||
} | ||
|
||
@Override | ||
public User findByUsername(String username) { | ||
return userRepository.findByUsername(username) | ||
.orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username)); | ||
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을 만들어서 예외처리를 해보는건 어떨까요 |
||
} | ||
|
||
@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.
redis 부분은 제가 만들어 놨는데 또 만드신 이유가 있나요????????
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.
35e0844
수정했습니다!