Skip to content

Commit 7632a2f

Browse files
authored
Merge pull request #21 from YAPP-Github/feat/T3-87
[T3-87] 루틴, 서브루틴 수정 및 삭제 API 추가 (+ 주요 테이블 복합키(HistoryPk) 설정)
2 parents 2bf4057 + 780ddab commit 7632a2f

31 files changed

Lines changed: 558 additions & 199 deletions

src/main/java/bitnagil/bitnagil_backend/auth/jwt/AuthRedisService.java

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.springframework.data.redis.core.StringRedisTemplate;
77
import org.springframework.stereotype.Component;
88

9+
import bitnagil.bitnagil_backend.global.entity.HistoryPk;
910
import lombok.RequiredArgsConstructor;
1011

1112
@Component
@@ -14,40 +15,52 @@ public class AuthRedisService {
1415
private final RefreshTokenRedisRepository refreshTokenRedisRepository;
1516
private final StringRedisTemplate stringRedisTemplate;
1617

17-
// 🔸 저장
18-
public void saveRefreshToken(Long userId, String token) {
18+
// 저장
19+
public void saveRefreshToken(HistoryPk userPk, String token) {
20+
String redisKey = buildRefreshTokenKey(userPk);
21+
1922
RefreshToken refreshToken = RefreshToken.builder()
20-
.userId(String.valueOf(userId))
23+
.userId(redisKey) // 복합키를 문자열 ID로 저장
2124
.refreshToken(token)
2225
.build();
26+
2327
refreshTokenRedisRepository.save(refreshToken);
2428
}
2529

26-
// 🔸 조회 by userId
27-
public Optional<RefreshToken> getRefreshTokenByUserId(Long userId) {
28-
return refreshTokenRedisRepository.findById(String.valueOf(userId));
30+
// 조회 by 복합키
31+
public Optional<RefreshToken> getRefreshTokenByUserPk(HistoryPk userPk) {
32+
String redisKey = buildRefreshTokenKey(userPk);
33+
return refreshTokenRedisRepository.findById(redisKey);
2934
}
3035

31-
// 🔸 조회 by refreshToken
36+
// 조회 by refreshToken
3237
public Optional<RefreshToken> getRefreshTokenByToken(String token) {
3338
return refreshTokenRedisRepository.findByRefreshToken(token);
3439
}
3540

36-
// 🔸 삭제
37-
public void deleteRefreshToken(Long userId) {
38-
refreshTokenRedisRepository.deleteById(String.valueOf(userId));
41+
// 삭제
42+
public void deleteRefreshToken(HistoryPk userPk) {
43+
String redisKey = buildRefreshTokenKey(userPk);
44+
refreshTokenRedisRepository.deleteById(redisKey);
45+
}
46+
47+
private String buildRefreshTokenKey(HistoryPk userPk) {
48+
return userPk.getId().toString() + ":" + userPk.getHistorySeq();
3949
}
4050

41-
// 🔸 Access Token 블랙리스트 등록
51+
52+
// Access Token 블랙리스트 등록
4253
public void addAccessTokenToBlacklist(String accessToken, long expirationMillis) {
4354
String key = "blacklist:" + accessToken;
4455
// value는 의미 없는 값, 만료시간은 access token의 남은 유효기간(ms)
4556
stringRedisTemplate.opsForValue().set(key, "logout", expirationMillis, TimeUnit.MILLISECONDS);
4657
}
4758

48-
// 🔸 Access Token 블랙리스트 여부 확인
59+
// Access Token 블랙리스트 여부 확인
4960
public boolean isAccessTokenBlacklisted(String accessToken) {
5061
String key = "blacklist:" + accessToken;
5162
return Boolean.TRUE.equals(stringRedisTemplate.hasKey(key));
5263
}
64+
65+
5366
}

src/main/java/bitnagil/bitnagil_backend/auth/jwt/JwtProvider.java

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Collection;
55
import java.util.Collections;
66
import java.util.Date;
7+
import java.util.UUID;
78

89
import org.springframework.beans.factory.annotation.Value;
910
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -13,6 +14,7 @@
1314
import org.springframework.stereotype.Service;
1415
import org.springframework.util.StringUtils;
1516

17+
import bitnagil.bitnagil_backend.global.entity.HistoryPk;
1618
import bitnagil.bitnagil_backend.global.errorcode.ErrorCode;
1719
import bitnagil.bitnagil_backend.global.exception.CustomException;
1820
import bitnagil.bitnagil_backend.user.repository.UserRepository;
@@ -30,7 +32,6 @@
3032
import lombok.RequiredArgsConstructor;
3133
import lombok.extern.slf4j.Slf4j;
3234

33-
@Slf4j
3435
@Service
3536
@RequiredArgsConstructor
3637
public class JwtProvider {
@@ -57,15 +58,16 @@ protected void init() {
5758
this.key = Keys.hmacShaKeyFor(keyBytes);
5859
}
5960

60-
public Token generateToken(Long userId) {
61+
public Token generateToken(HistoryPk userPk) {
6162
Date now = new Date();
6263

6364
// Access Token 생성
6465
Date accessTokenExpiresIn = new Date(now.getTime() + ACCESS_TOKEN_EXPIRE_TIME);
6566

6667
String accessToken = Jwts.builder()
6768
.setSubject(ACCESS_TOKEN_SUBJECT)
68-
.claim("userId", userId)
69+
.claim("userId", userPk.getId())
70+
.claim("userHistorySeq", userPk.getHistorySeq())
6971
.setExpiration(accessTokenExpiresIn)
7072
.signWith(key, SignatureAlgorithm.HS512)
7173
.compact();
@@ -74,12 +76,13 @@ public Token generateToken(Long userId) {
7476
Date refreshTokenExpiresIn = new Date(now.getTime() + REFRESH_TOKEN_EXPIRE_TIME);
7577
String refreshToken = Jwts.builder()
7678
.setSubject(REFRESH_TOKEN_SUBJECT)
77-
.claim("userId", userId)
79+
.claim("userId", userPk.getId())
80+
.claim("userHistorySeq", userPk.getHistorySeq())
7881
.setExpiration(refreshTokenExpiresIn)
7982
.signWith(key, SignatureAlgorithm.HS512)
8083
.compact();
8184

82-
authRedisService.saveRefreshToken(userId, refreshToken);
85+
authRedisService.saveRefreshToken(userPk, refreshToken);
8386

8487
return Token.builder()
8588
.accessToken(accessToken)
@@ -99,16 +102,23 @@ public String resolveToken(HttpServletRequest request) {
99102
}
100103

101104
public Authentication getAuthentication(String accessToken) {
102-
// 토큰 복호화
103-
Claims claims = parseClaims(accessToken);
104-
105-
Long userId = Long.valueOf(claims.get("userId", Integer.class));
106-
User user = userRepository.findById(userId).orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_USER));
105+
User user = findValidUserByRefreshTokenOrAccessToken(accessToken);
107106

108107
return new UsernamePasswordAuthenticationToken(user, null, getAuthorities(user));
109108
// TODO 추후 회원탈퇴한 유저를 어떻게 관리하는지에 따라 추가 검증 필요
110109
}
111110

111+
// RefreshToken 혹은 AccessToken으로 인증된 유효 User 조회
112+
public User findValidUserByRefreshTokenOrAccessToken(String token) {
113+
// JWT에서 유저 관련 정보 추출 후, UserPk 생성
114+
UUID userId = UUID.fromString(parseClaims(token).get("userId", String.class));
115+
Long historySeq = parseClaims(token).get("userHistorySeq", Long.class);
116+
117+
HistoryPk userPk = new HistoryPk(userId, historySeq);
118+
119+
return userRepository.findByUserPk(userPk).orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_USER));
120+
}
121+
112122
public boolean validateToken(String token) {
113123
try {
114124
Claims claims = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
@@ -133,16 +143,9 @@ public Claims parseClaims(String accessToken) {
133143
}
134144
}
135145

136-
// accessToken의 만료 시간 조회
137-
public Long getExpirationTime(String accessToken) {
138-
return parseClaims(accessToken).getExpiration().getTime();
139-
}
140-
141146
private Collection<GrantedAuthority> getAuthorities(User user) {
142147
return Collections.singletonList(
143148
new SimpleGrantedAuthority("ROLE_" + user.getRole().toString())
144149
);
145150
}
146-
147-
148151
}

src/main/java/bitnagil/bitnagil_backend/auth/kakao/domain/CustomOAuth2User.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
88

99
import bitnagil.bitnagil_backend.enums.Role;
10+
import bitnagil.bitnagil_backend.global.entity.HistoryPk;
1011
import lombok.Getter;
1112

1213
/**
@@ -18,13 +19,13 @@
1819
@Getter
1920
public class CustomOAuth2User extends DefaultOAuth2User {
2021

21-
private final Long userId;
22+
private final HistoryPk userPk;
2223
private final Role userRole;
2324

2425
public CustomOAuth2User(Collection<? extends GrantedAuthority> authorities,
25-
Map<String, Object> attributes, String nameAttributeKey, Long userId, Role userRole) {
26+
Map<String, Object> attributes, String nameAttributeKey, HistoryPk userPk, Role userRole) {
2627
super(authorities, attributes, nameAttributeKey);
27-
this.userId = userId;
28+
this.userPk = userPk;
2829
this.userRole = userRole;
2930
}
3031
}

src/main/java/bitnagil/bitnagil_backend/auth/kakao/service/CustomOAuth2UserService.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package bitnagil.bitnagil_backend.auth.kakao.service;
22

3+
import java.time.LocalDateTime;
34
import java.util.Collections;
45
import java.util.Map;
56

@@ -13,6 +14,8 @@
1314

1415
import bitnagil.bitnagil_backend.auth.kakao.domain.CustomOAuth2User;
1516
import bitnagil.bitnagil_backend.auth.kakao.domain.OAuth2Attribute;
17+
import bitnagil.bitnagil_backend.global.errorcode.ErrorCode;
18+
import bitnagil.bitnagil_backend.global.exception.CustomException;
1619
import bitnagil.bitnagil_backend.user.repository.UserRepository;
1720
import bitnagil.bitnagil_backend.enums.SocialType;
1821
import bitnagil.bitnagil_backend.user.domain.User;
@@ -57,7 +60,7 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic
5760

5861
return new CustomOAuth2User(
5962
Collections.singleton(new SimpleGrantedAuthority(createdUser.getRole().getDescription())),
60-
attributes, extractAttributes.getNameAttributeKey(), createdUser.getUserId(), createdUser.getRole());
63+
attributes, extractAttributes.getNameAttributeKey(), createdUser.getUserPk(), createdUser.getRole());
6164
}
6265

6366
private String getUserNameAttributeName(final OAuth2UserRequest userRequest) {
@@ -76,7 +79,12 @@ private SocialType getSocialType(String registrationId) {
7679
}
7780

7881
private User getMember(OAuth2Attribute attributes, SocialType socialType) {
79-
User findUser = userRepository.findBySocialTypeAndSocialId(socialType, attributes.getSocialId()).orElse(null);
82+
LocalDateTime now = LocalDateTime.now();
83+
84+
User findUser = userRepository
85+
.findBySocialTypeAndSocialIdAndHistoryStartDateTimeLessThanAndHistoryEndDateTimeGreaterThanEqual(
86+
socialType, attributes.getSocialId(), now, now)
87+
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_USER));
8088

8189
if (findUser == null) {
8290
return saveMember(attributes, socialType);

src/main/java/bitnagil/bitnagil_backend/changedRoutine/domain/ChangedRoutine.java

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package bitnagil.bitnagil_backend.changedRoutine.domain;
22

3-
import bitnagil.bitnagil_backend.global.BaseTimeEntity;
4-
import bitnagil.bitnagil_backend.global.utils.DayOfWeekConverter;
3+
import bitnagil.bitnagil_backend.global.entity.BaseTimeEntity;
4+
import bitnagil.bitnagil_backend.global.entity.HistoryPk;
55
import bitnagil.bitnagil_backend.routine.domain.Routine;
66
import bitnagil.bitnagil_backend.user.domain.User;
77
import jakarta.persistence.*;
@@ -24,9 +24,12 @@
2424
@Entity
2525
public class ChangedRoutine extends BaseTimeEntity {
2626

27-
@Id
28-
@GeneratedValue(strategy = GenerationType.IDENTITY)
29-
private Long changedRoutineId;
27+
@EmbeddedId
28+
@AttributeOverrides({
29+
@AttributeOverride(name = "id", column = @Column(name = "changed_routine_id")),
30+
@AttributeOverride(name = "historySeq", column = @Column(name = "history_seq"))
31+
})
32+
private HistoryPk changedRoutinePk;
3033

3134
@NotNull
3235
private String changedRoutineName; // 변경된 루틴 이름
@@ -41,32 +44,38 @@ public class ChangedRoutine extends BaseTimeEntity {
4144
private LocalDate changedRoutineDate; // 변경된 루틴 날짜(실제 루틴이 실행될 날짜)
4245

4346
@NotNull
44-
private LocalDateTime historyStartDate; // 이력 시작일시
47+
private LocalDateTime historyStartDateTime; // 이력 시작일시
4548

4649
@NotNull
47-
private LocalDateTime historyEndDate; // 이력 종료일시
50+
private LocalDateTime historyEndDateTime; // 이력 종료일시
4851

4952
@ManyToOne(fetch = FetchType.LAZY)
50-
@JoinColumn(name = "user_id")
53+
@JoinColumns({
54+
@JoinColumn(name = "user_id", referencedColumnName = "user_id"),
55+
@JoinColumn(name = "user_history_seq", referencedColumnName = "history_seq")
56+
})
5157
@NotNull
5258
private User user;
5359

5460
@ManyToOne(fetch = FetchType.LAZY)
55-
@JoinColumn(name = "routine_id")
61+
@JoinColumns({
62+
@JoinColumn(name = "routine_id", referencedColumnName = "routine_id"),
63+
@JoinColumn(name = "routine_history_seq", referencedColumnName = "history_seq")
64+
})
5665
private Routine routine; // 원본 루틴
5766

5867
@Builder
59-
public ChangedRoutine(String changedRoutineName, LocalTime changedExecutionTime, LocalDate originalRoutineDate,
60-
LocalDate changedRoutineDate, LocalDateTime historyStartDate, LocalDateTime historyEndDate,
61-
User user, Routine routine) {
68+
public ChangedRoutine(HistoryPk changedRoutinePk, String changedRoutineName, LocalTime changedExecutionTime,
69+
LocalDate originalRoutineDate, LocalDate changedRoutineDate, LocalDateTime historyStartDateTime,
70+
LocalDateTime historyEndDateTime, User user, Routine routine) {
71+
this.changedRoutinePk = changedRoutinePk;
6272
this.changedRoutineName = changedRoutineName;
6373
this.changedExecutionTime = changedExecutionTime;
6474
this.originalRoutineDate = originalRoutineDate;
6575
this.changedRoutineDate = changedRoutineDate;
66-
this.historyStartDate = historyStartDate;
67-
this.historyEndDate = historyEndDate;
76+
this.historyStartDateTime = historyStartDateTime;
77+
this.historyEndDateTime = historyEndDateTime;
6878
this.user = user;
6979
this.routine = routine;
7080
}
71-
7281
}
Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package bitnagil.bitnagil_backend.changedRoutine.domain;
22

3-
import bitnagil.bitnagil_backend.global.BaseTimeEntity;
4-
import bitnagil.bitnagil_backend.routine.domain.Routine;
3+
import bitnagil.bitnagil_backend.global.entity.BaseTimeEntity;
4+
import bitnagil.bitnagil_backend.global.entity.HistoryPk;
55
import jakarta.persistence.*;
66
import jakarta.validation.constraints.NotNull;
77
import lombok.AccessLevel;
@@ -16,30 +16,29 @@
1616
@Entity
1717
public class ChangedSubRoutine extends BaseTimeEntity {
1818

19-
@Id
20-
@GeneratedValue(strategy = GenerationType.IDENTITY)
21-
private Long changedSubRoutineId;
19+
@EmbeddedId
20+
@AttributeOverrides({
21+
@AttributeOverride(name = "id", column = @Column(name = "changed_sub_routine_id")),
22+
@AttributeOverride(name = "historySeq", column = @Column(name = "history_seq"))
23+
})
24+
private HistoryPk changedSubRoutinePk;
2225

2326
@NotNull
2427
private String changedSubRoutineName;
2528

2629
@NotNull
27-
private LocalDateTime historyStartDate;
30+
private LocalDateTime historyStartDateTime;
2831

2932
@NotNull
30-
private LocalDateTime historyEndDate;
33+
private LocalDateTime historyEndDateTime;
3134

32-
@ManyToOne
33-
@JoinColumn(name = "changed_routine_id")
34-
@NotNull
35-
private ChangedRoutine changedRoutine;
3635

3736
@Builder
38-
public ChangedSubRoutine(String changedSubRoutineName, LocalDateTime historyStartDate, LocalDateTime historyEndDate,
39-
ChangedRoutine changedRoutine) {
37+
public ChangedSubRoutine(HistoryPk changedSubRoutinePk, String changedSubRoutineName,
38+
LocalDateTime historyStartDateTime, LocalDateTime historyEndDateTime) {
39+
this.changedSubRoutinePk = changedSubRoutinePk;
4040
this.changedSubRoutineName = changedSubRoutineName;
41-
this.historyStartDate = historyStartDate;
42-
this.historyEndDate = historyEndDate;
43-
this.changedRoutine = changedRoutine;
41+
this.historyStartDateTime = historyStartDateTime;
42+
this.historyEndDateTime = historyEndDateTime;
4443
}
4544
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package bitnagil.bitnagil_backend.changedRoutine.repository;
22

33
import bitnagil.bitnagil_backend.changedRoutine.domain.ChangedRoutine;
4+
import bitnagil.bitnagil_backend.global.entity.HistoryPk;
5+
46
import org.springframework.data.jpa.repository.JpaRepository;
57

6-
public interface ChangedRoutineRepository extends JpaRepository<ChangedRoutine, Long> {
8+
public interface ChangedRoutineRepository extends JpaRepository<ChangedRoutine, HistoryPk> {
79
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package bitnagil.bitnagil_backend.changedRoutine.repository;
22

33
import bitnagil.bitnagil_backend.changedRoutine.domain.ChangedSubRoutine;
4+
import bitnagil.bitnagil_backend.global.entity.HistoryPk;
5+
46
import org.springframework.data.jpa.repository.JpaRepository;
57

6-
public interface ChangedSubRoutineRepository extends JpaRepository<ChangedSubRoutine, Long> {
8+
public interface ChangedSubRoutineRepository extends JpaRepository<ChangedSubRoutine, HistoryPk> {
79
}

0 commit comments

Comments
 (0)