From db2dc3ae183224633444c9f55391560020b28649 Mon Sep 17 00:00:00 2001 From: "Hyeonjae.kim" Date: Mon, 13 Jan 2025 15:46:01 +0900 Subject: [PATCH 1/8] =?UTF-8?q?=E2=9C=A8=20Feat=20:=20Implement=20DomainId?= =?UTF-8?q?Resolver=20classes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domains/crew/domain/CrewPrincipal.java | 9 ++++ .../crew/service/resolver/CrewIdResolver.java | 44 +++++++++++++++++++ .../domains/user/domain/UserPrincipal.java | 9 ++++ .../user/service/resolver/UserIdResolver.java | 44 +++++++++++++++++++ .../common/resolver/DomainIdResolver.java | 6 +++ .../common/resolver/DomainPrincipal.java | 13 ++++++ .../server/global/config/CacheConfig.java | 11 +++++ 7 files changed, 136 insertions(+) create mode 100644 src/main/java/com/run_us/server/domains/crew/domain/CrewPrincipal.java create mode 100644 src/main/java/com/run_us/server/domains/crew/service/resolver/CrewIdResolver.java create mode 100644 src/main/java/com/run_us/server/domains/user/domain/UserPrincipal.java create mode 100644 src/main/java/com/run_us/server/domains/user/service/resolver/UserIdResolver.java create mode 100644 src/main/java/com/run_us/server/global/common/resolver/DomainIdResolver.java create mode 100644 src/main/java/com/run_us/server/global/common/resolver/DomainPrincipal.java diff --git a/src/main/java/com/run_us/server/domains/crew/domain/CrewPrincipal.java b/src/main/java/com/run_us/server/domains/crew/domain/CrewPrincipal.java new file mode 100644 index 00000000..f859856c --- /dev/null +++ b/src/main/java/com/run_us/server/domains/crew/domain/CrewPrincipal.java @@ -0,0 +1,9 @@ +package com.run_us.server.domains.crew.domain; + +import com.run_us.server.global.common.resolver.DomainPrincipal; + +public class CrewPrincipal extends DomainPrincipal { + public CrewPrincipal(String publicId, Integer internalId) { + super(publicId, internalId); + } +} diff --git a/src/main/java/com/run_us/server/domains/crew/service/resolver/CrewIdResolver.java b/src/main/java/com/run_us/server/domains/crew/service/resolver/CrewIdResolver.java new file mode 100644 index 00000000..f095202f --- /dev/null +++ b/src/main/java/com/run_us/server/domains/crew/service/resolver/CrewIdResolver.java @@ -0,0 +1,44 @@ +package com.run_us.server.domains.crew.service.resolver; + +import com.run_us.server.domains.crew.controller.model.enums.CrewErrorCode; +import com.run_us.server.domains.crew.controller.model.enums.CrewException; +import com.run_us.server.domains.crew.domain.Crew; +import com.run_us.server.domains.crew.domain.CrewPrincipal; +import com.run_us.server.domains.crew.repository.CrewRepository; +import com.run_us.server.global.common.cache.InMemoryCache; +import com.run_us.server.global.common.resolver.DomainIdResolver; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.Duration; + +@Service +@RequiredArgsConstructor +public class CrewIdResolver implements DomainIdResolver { + private final CrewRepository crewRepository; + private final InMemoryCache principalCache; + + private static final Duration CACHE_TTL = Duration.ofMinutes(30); + + @Override + public CrewPrincipal resolve(String publicId) { + return principalCache.get(publicId) + .orElseGet(() -> loadAndCacheCrewPrincipal(publicId)); + } + + @Override + public CrewPrincipal resolve(Integer internalId) { + Crew crew = crewRepository.findById(internalId) + .orElseThrow(() -> new CrewException(CrewErrorCode.CREW_NOT_FOUND)); + return new CrewPrincipal(crew.getPublicId(), internalId); + } + + private CrewPrincipal loadAndCacheCrewPrincipal(String publicId) { + Crew crew = crewRepository.findByPublicId(publicId) + .orElseThrow(() -> new CrewException(CrewErrorCode.CREW_NOT_FOUND)); + + CrewPrincipal principal = new CrewPrincipal(publicId, crew.getId()); + principalCache.put(publicId, principal, CACHE_TTL); + return principal; + } +} diff --git a/src/main/java/com/run_us/server/domains/user/domain/UserPrincipal.java b/src/main/java/com/run_us/server/domains/user/domain/UserPrincipal.java new file mode 100644 index 00000000..d83a9325 --- /dev/null +++ b/src/main/java/com/run_us/server/domains/user/domain/UserPrincipal.java @@ -0,0 +1,9 @@ +package com.run_us.server.domains.user.domain; + +import com.run_us.server.global.common.resolver.DomainPrincipal; + +public class UserPrincipal extends DomainPrincipal { + public UserPrincipal(String publicId, Integer internalId) { + super(publicId, internalId); + } +} diff --git a/src/main/java/com/run_us/server/domains/user/service/resolver/UserIdResolver.java b/src/main/java/com/run_us/server/domains/user/service/resolver/UserIdResolver.java new file mode 100644 index 00000000..94cd355e --- /dev/null +++ b/src/main/java/com/run_us/server/domains/user/service/resolver/UserIdResolver.java @@ -0,0 +1,44 @@ +package com.run_us.server.domains.user.service.resolver; + +import com.run_us.server.domains.user.domain.User; +import com.run_us.server.domains.user.domain.UserPrincipal; +import com.run_us.server.domains.user.exception.UserErrorCode; +import com.run_us.server.domains.user.exception.UserException; +import com.run_us.server.domains.user.repository.UserRepository; +import com.run_us.server.global.common.cache.InMemoryCache; +import com.run_us.server.global.common.resolver.DomainIdResolver; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.Duration; + +@Service +@RequiredArgsConstructor +public class UserIdResolver implements DomainIdResolver { + private final UserRepository userRepository; + private final InMemoryCache principalCache; + + private static final Duration CACHE_TTL = Duration.ofMinutes(30); + + @Override + public UserPrincipal resolve(String publicId) { + return principalCache.get(publicId) + .orElseGet(() -> loadAndCacheUserPrincipal(publicId)); + } + + @Override + public UserPrincipal resolve(Integer internalId) { + User user = userRepository.findById(internalId) + .orElseThrow(() -> new UserException(UserErrorCode.USER_NOT_FOUND)); + return new UserPrincipal(user.getPublicId(), internalId); + } + + private UserPrincipal loadAndCacheUserPrincipal(String publicId) { + User user = userRepository.findByPublicId(publicId) + .orElseThrow(() -> new UserException(UserErrorCode.USER_NOT_FOUND)); + + UserPrincipal principal = new UserPrincipal(publicId, user.getId()); + principalCache.put(publicId, principal, CACHE_TTL); + return principal; + } +} diff --git a/src/main/java/com/run_us/server/global/common/resolver/DomainIdResolver.java b/src/main/java/com/run_us/server/global/common/resolver/DomainIdResolver.java new file mode 100644 index 00000000..9aba4a8e --- /dev/null +++ b/src/main/java/com/run_us/server/global/common/resolver/DomainIdResolver.java @@ -0,0 +1,6 @@ +package com.run_us.server.global.common.resolver; + +public interface DomainIdResolver { + T resolve(String publicId); + T resolve(Integer internalId); +} \ No newline at end of file diff --git a/src/main/java/com/run_us/server/global/common/resolver/DomainPrincipal.java b/src/main/java/com/run_us/server/global/common/resolver/DomainPrincipal.java new file mode 100644 index 00000000..78d183c5 --- /dev/null +++ b/src/main/java/com/run_us/server/global/common/resolver/DomainPrincipal.java @@ -0,0 +1,13 @@ +package com.run_us.server.global.common.resolver; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@EqualsAndHashCode +@RequiredArgsConstructor +public abstract class DomainPrincipal { + private final String publicId; + private final Integer internalId; +} \ No newline at end of file diff --git a/src/main/java/com/run_us/server/global/config/CacheConfig.java b/src/main/java/com/run_us/server/global/config/CacheConfig.java index 41ba821b..41dfe6b7 100644 --- a/src/main/java/com/run_us/server/global/config/CacheConfig.java +++ b/src/main/java/com/run_us/server/global/config/CacheConfig.java @@ -3,6 +3,7 @@ import com.run_us.server.global.common.cache.InMemoryCache; import com.run_us.server.global.common.cache.SpringInMemoryCache; import com.run_us.server.domains.user.domain.TokenStatus; +import com.run_us.server.global.common.resolver.DomainPrincipal; import com.run_us.server.global.security.principal.UserPrincipal; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -47,4 +48,14 @@ public InMemoryCache userPrincipalCache(TaskScheduler cac Duration.ofSeconds(cleanupIntervalSeconds) ); } + + @Bean + public InMemoryCache domainPrincipalCache( + TaskScheduler cacheCleanupScheduler + ) { + return new SpringInMemoryCache<>( + cacheCleanupScheduler, + Duration.ofSeconds(cleanupIntervalSeconds) + ); + } } \ No newline at end of file From df1066a5d67a19766b7df483d127cd3a456c54b2 Mon Sep 17 00:00:00 2001 From: "Hyeonjae.kim" Date: Mon, 13 Jan 2025 16:14:52 +0900 Subject: [PATCH 2/8] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20Fix=20c?= =?UTF-8?q?urrentUser=20security=20annotation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/global/common/GlobalConst.java | 9 ++++ .../server/global/config/CacheConfig.java | 9 ---- .../security/JwtAuthenticationFilter.java | 42 +++++++++++++------ .../resolver/CurrentUserArgumentResolver.java | 8 ++-- .../security/resolver/UserIdResolver.java | 35 ---------------- 5 files changed, 43 insertions(+), 60 deletions(-) delete mode 100644 src/main/java/com/run_us/server/global/security/resolver/UserIdResolver.java diff --git a/src/main/java/com/run_us/server/global/common/GlobalConst.java b/src/main/java/com/run_us/server/global/common/GlobalConst.java index a8522320..629e0158 100644 --- a/src/main/java/com/run_us/server/global/common/GlobalConst.java +++ b/src/main/java/com/run_us/server/global/common/GlobalConst.java @@ -4,6 +4,9 @@ import lombok.NoArgsConstructor; import java.time.ZoneId; +import java.util.List; + +import static com.run_us.server.global.common.SocketConst.WS_CONNECT_ENDPOINT; @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class GlobalConst { @@ -13,4 +16,10 @@ public final class GlobalConst { public static final String WS_USER_AUTH_HEADER = "user-id"; public static final String SESSION_ATTRIBUTE_USER = "user-info"; + + public static final List WHITE_LIST_PATHS = List.of( + "/test/auth", + WS_CONNECT_ENDPOINT, + "/auth" + ); } diff --git a/src/main/java/com/run_us/server/global/config/CacheConfig.java b/src/main/java/com/run_us/server/global/config/CacheConfig.java index 41dfe6b7..a2d00aa2 100644 --- a/src/main/java/com/run_us/server/global/config/CacheConfig.java +++ b/src/main/java/com/run_us/server/global/config/CacheConfig.java @@ -4,7 +4,6 @@ import com.run_us.server.global.common.cache.SpringInMemoryCache; import com.run_us.server.domains.user.domain.TokenStatus; import com.run_us.server.global.common.resolver.DomainPrincipal; -import com.run_us.server.global.security.principal.UserPrincipal; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -41,14 +40,6 @@ public InMemoryCache tokenStatusCache(TaskScheduler cacheCl ); } - @Bean - public InMemoryCache userPrincipalCache(TaskScheduler cacheCleanupScheduler) { - return new SpringInMemoryCache<>( - cacheCleanupScheduler, - Duration.ofSeconds(cleanupIntervalSeconds) - ); - } - @Bean public InMemoryCache domainPrincipalCache( TaskScheduler cacheCleanupScheduler diff --git a/src/main/java/com/run_us/server/global/security/JwtAuthenticationFilter.java b/src/main/java/com/run_us/server/global/security/JwtAuthenticationFilter.java index 0a87fae7..b396036a 100644 --- a/src/main/java/com/run_us/server/global/security/JwtAuthenticationFilter.java +++ b/src/main/java/com/run_us/server/global/security/JwtAuthenticationFilter.java @@ -11,11 +11,15 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; +import java.util.Collections; -import static com.run_us.server.global.common.SocketConst.WS_CONNECT_ENDPOINT; +import static com.run_us.server.global.common.GlobalConst.WHITE_LIST_PATHS; @Slf4j public class JwtAuthenticationFilter extends OncePerRequestFilter { @@ -33,15 +37,23 @@ public JwtAuthenticationFilter(JwtService jwtService) { @Override protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException { - if (!hasValidAuthorizationHeader(request)) { - setErrorResponse(UserErrorCode.JWT_NOT_FOUND, response); - return; + try{ + if (!hasValidAuthorizationHeader(request)) { + setErrorResponse(UserErrorCode.JWT_NOT_FOUND, response); + return; + } + String jwt = extractToken(request); + if (!processToken(jwt, request, response)) { + return; + } + filterChain.doFilter(request, response); + } catch (Exception e) { + log.warn("[ERROR]JWT broken : {}", e.getMessage()); + setErrorResponse(UserErrorCode.JWT_BROKEN, response); } - String jwt = extractToken(request); - if (!processToken(jwt, request, response)) { - return; + finally { + SecurityContextHolder.clearContext(); } - filterChain.doFilter(request, response); } private boolean hasValidAuthorizationHeader(HttpServletRequest request) { @@ -56,6 +68,13 @@ private String extractToken(HttpServletRequest request) { private boolean processToken(String jwt, HttpServletRequest request, HttpServletResponse response) throws IOException { try { String publicId = jwtService.getUserIdFromAccessToken(jwt); + Authentication authentication = new UsernamePasswordAuthenticationToken( + publicId, + null, + Collections.emptyList() + ); + SecurityContextHolder.getContext().setAuthentication(authentication); + request.setAttribute("publicUserId", publicId); return true; } catch (TokenExpiredException e) { @@ -77,9 +96,8 @@ private void setErrorResponse(UserErrorCode errorCode, HttpServletResponse respo // JWT 필터는 로그인, 회원가입, 테스트용 API, 웹소켓 연결 요청에 대해서는 필터링을 하지 않는다. @Override - protected boolean shouldNotFilter(HttpServletRequest request) { - return request.getServletPath().startsWith("/test/auth") - || request.getServletPath().startsWith(WS_CONNECT_ENDPOINT) - || request.getServletPath().startsWith("/auth"); + protected boolean shouldNotFilter(@NonNull HttpServletRequest request) { + return WHITE_LIST_PATHS.stream() + .anyMatch(path -> request.getServletPath().startsWith(path)); } } diff --git a/src/main/java/com/run_us/server/global/security/resolver/CurrentUserArgumentResolver.java b/src/main/java/com/run_us/server/global/security/resolver/CurrentUserArgumentResolver.java index 7b6cc9dd..2f1642b6 100644 --- a/src/main/java/com/run_us/server/global/security/resolver/CurrentUserArgumentResolver.java +++ b/src/main/java/com/run_us/server/global/security/resolver/CurrentUserArgumentResolver.java @@ -5,6 +5,7 @@ import com.run_us.server.global.security.annotation.CurrentUser; import lombok.RequiredArgsConstructor; import org.springframework.core.MethodParameter; +import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; @@ -16,7 +17,6 @@ @Component @RequiredArgsConstructor public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver { - private final UserIdResolver userIdResolver; @Override public boolean supportsParameter(MethodParameter parameter) { @@ -26,13 +26,13 @@ public boolean supportsParameter(MethodParameter parameter) { @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { - String userPublicId = extractUserPublicId(); - return userIdResolver.resolveUser(userPublicId); + return extractUserPublicId(); } private String extractUserPublicId() throws UserException { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication == null) { + if (authentication == null || !authentication.isAuthenticated() || + authentication instanceof AnonymousAuthenticationToken) { throw new UserException(UserErrorCode.JWT_BROKEN); } return authentication.getName(); diff --git a/src/main/java/com/run_us/server/global/security/resolver/UserIdResolver.java b/src/main/java/com/run_us/server/global/security/resolver/UserIdResolver.java deleted file mode 100644 index 07282d28..00000000 --- a/src/main/java/com/run_us/server/global/security/resolver/UserIdResolver.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.run_us.server.global.security.resolver; - -import com.run_us.server.domains.user.domain.User; -import com.run_us.server.domains.user.exception.UserErrorCode; -import com.run_us.server.domains.user.exception.UserException; -import com.run_us.server.domains.user.repository.UserRepository; -import com.run_us.server.global.common.cache.InMemoryCache; -import com.run_us.server.global.security.principal.UserPrincipal; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -import java.time.Duration; - -@Component -@RequiredArgsConstructor -public class UserIdResolver { - private final InMemoryCache principalCache; - private final UserRepository userRepository; - - private static final Duration CACHE_TTL = Duration.ofMinutes(30); - - public UserPrincipal resolveUser(String publicId) { - return principalCache.get(publicId) - .orElseGet(() -> loadAndCacheUserPrincipal(publicId)); - } - - private UserPrincipal loadAndCacheUserPrincipal(String publicId) { - User user = userRepository.findByPublicId(publicId) - .orElseThrow(() -> new UserException(UserErrorCode.USER_NOT_FOUND)); - - UserPrincipal principal = new UserPrincipal(publicId, user.getId()); - principalCache.put(publicId, principal, CACHE_TTL); - return principal; - } -} From 7c68845600261bb3aee6c48688bd5bd5f4a96bec Mon Sep 17 00:00:00 2001 From: "Hyeonjae.kim" Date: Mon, 13 Jan 2025 17:07:06 +0900 Subject: [PATCH 3/8] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20Apply?= =?UTF-8?q?=20changed=20currentUser=20annotation=20&=20IdResolvers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crew/controller/CrewController.java | 23 ++-- .../crew/service/usecase/CrewJoinUseCase.java | 8 +- .../service/usecase/CrewJoinUseCaseImpl.java | 106 +++++++++++------- 3 files changed, 83 insertions(+), 54 deletions(-) diff --git a/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java b/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java index 70abb5e9..71306b4b 100644 --- a/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java +++ b/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java @@ -12,7 +12,6 @@ import com.run_us.server.global.common.SuccessResponse; import com.run_us.server.global.security.annotation.CurrentUser; -import com.run_us.server.global.security.principal.UserPrincipal; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import lombok.RequiredArgsConstructor; @@ -47,24 +46,24 @@ public ResponseEntity> createCrew( @PostMapping("/{crewPublicId}/join-requests") public ResponseEntity> requestJoin( @PathVariable String crewPublicId, - @CurrentUser UserPrincipal userPrincipal, + @CurrentUser String curentUserPublicId, @Valid @RequestBody CreateJoinRequest request ) { - log.info("action=request_join crewPublicId={} userPublicId={}", crewPublicId, userPrincipal.getPublicId()); - CrewJoinRequestInternalResponse response = crewJoinUseCase.createJoinRequest(crewPublicId, userPrincipal.getInternalId(), request); + log.info("action=request_join crewPublicId={} userPublicId={}", crewPublicId, curentUserPublicId); + CrewJoinRequestInternalResponse response = crewJoinUseCase.createJoinRequest(crewPublicId, curentUserPublicId, request); return ResponseEntity.ok( SuccessResponse.of( CrewHttpResponseCode.JOIN_REQUEST_CREATED, - response.toPublicCreateResponse(userPrincipal.getPublicId()))); + response.toPublicCreateResponse(curentUserPublicId))); } @DeleteMapping("/{crewPublicId}/join-requests") public ResponseEntity> cancelJoinRequest( @PathVariable String crewPublicId, - @CurrentUser UserPrincipal userPrincipal + @CurrentUser String curentUserPublicId ) { - log.info("action=cancel_join_request crewPublicId={} userPublicId={}", crewPublicId, userPrincipal.getPublicId()); - CrewJoinRequestInternalResponse response = crewJoinUseCase.cancelJoinRequest(crewPublicId, userPrincipal.getInternalId()); + log.info("action=cancel_join_request crewPublicId={} userPublicId={}", crewPublicId, curentUserPublicId); + CrewJoinRequestInternalResponse response = crewJoinUseCase.cancelJoinRequest(crewPublicId, curentUserPublicId); return ResponseEntity.ok( SuccessResponse.of( CrewHttpResponseCode.JOIN_REQUEST_CANCELLED, @@ -78,14 +77,14 @@ public ResponseEntity>> getJoinRe @PathVariable String crewPublicId, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int limit, - @CurrentUser UserPrincipal userPrincipal + @CurrentUser String curentUserPublicId ) { log.info("action=get_join_requests_start crewPublicId={} page={} limit={}", crewPublicId, page, limit); List responses = crewJoinUseCase.getJoinRequests( crewPublicId, PageRequest.of(page, limit), - userPrincipal.getInternalId() + curentUserPublicId ); log.info("action=get_join_requests_complete crewPublicId={} page={} limit={} result_count={}", @@ -102,7 +101,7 @@ public ResponseEntity>> getJoinRe public ResponseEntity> reviewJoinRequest( @PathVariable String crewPublicId, @PathVariable Integer requestId, - @CurrentUser UserPrincipal userPrincipal, + @CurrentUser String curentUserPublicId, @Valid @RequestBody ReviewJoinRequest request ) { log.info("action=process_join_request_start crewPublicId={} requestId={} status={}", @@ -112,7 +111,7 @@ public ResponseEntity> reviewJoinRequ crewPublicId, requestId, CrewJoinRequestStatus.valueOf(request.getStatus()), - userPrincipal.getInternalId() + curentUserPublicId ); log.info("action=process_join_request_complete crewPublicId={} requestId={}", diff --git a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCase.java b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCase.java index de6423f7..a04758a3 100644 --- a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCase.java +++ b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCase.java @@ -10,8 +10,8 @@ import java.util.List; public interface CrewJoinUseCase { - CrewJoinRequestInternalResponse createJoinRequest(String crewPublicId, Integer userId, CreateJoinRequest request); - CrewJoinRequestInternalResponse cancelJoinRequest(String crewPublicId, Integer userId); - List getJoinRequests(String crewPublicId, PageRequest pageRequest, Integer userId); - ReviewJoinRequestResponse reviewJoinRequest(String crewPublicId, Integer requestId, CrewJoinRequestStatus status, Integer userId); + CrewJoinRequestInternalResponse createJoinRequest(String crewPublicId, String userPublicId, CreateJoinRequest request); + CrewJoinRequestInternalResponse cancelJoinRequest(String crewPublicId, String userPublicId); + List getJoinRequests(String crewPublicId, PageRequest pageRequest, String userPublicId); + ReviewJoinRequestResponse reviewJoinRequest(String crewPublicId, Integer requestId, CrewJoinRequestStatus status, String userPublicId); } diff --git a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCaseImpl.java b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCaseImpl.java index 367a9112..9eb3a49d 100644 --- a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCaseImpl.java +++ b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCaseImpl.java @@ -6,11 +6,15 @@ import com.run_us.server.domains.crew.controller.model.response.ReviewJoinRequestResponse; import com.run_us.server.domains.crew.domain.Crew; import com.run_us.server.domains.crew.domain.CrewJoinRequest; +import com.run_us.server.domains.crew.domain.CrewPrincipal; import com.run_us.server.domains.crew.domain.enums.CrewJoinRequestStatus; import com.run_us.server.domains.crew.service.CrewService; import com.run_us.server.domains.crew.service.CrewValidator; +import com.run_us.server.domains.crew.service.resolver.CrewIdResolver; import com.run_us.server.domains.user.domain.User; +import com.run_us.server.domains.user.domain.UserPrincipal; import com.run_us.server.domains.user.service.UserService; +import com.run_us.server.domains.user.service.resolver.UserIdResolver; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -25,89 +29,115 @@ @Service @RequiredArgsConstructor public class CrewJoinUseCaseImpl implements CrewJoinUseCase { + private final CrewService crewService; private final CrewValidator crewValidator; private final UserService userService; + private final CrewIdResolver crewIdResolver; + private final UserIdResolver userIdResolver; @Override @Transactional - public CrewJoinRequestInternalResponse createJoinRequest(String crewPublicId, Integer userInternalId, CreateJoinRequest request) { - log.debug("action=create_join_request_start crewPublicId={} userInternalId={}", crewPublicId, userInternalId); + public CrewJoinRequestInternalResponse createJoinRequest( + String crewPublicId, String userPublicId, CreateJoinRequest request) { + CrewPrincipal crewPrincipal = crewIdResolver.resolve(crewPublicId); + UserPrincipal userPrincipal = userIdResolver.resolve(userPublicId); - Crew crew = crewService.getCrewByPublicId(crewPublicId); - crewValidator.validateCanJoinCrew(userInternalId, crew); + log.info("action=create_join_request_start crewPublicId={} userPublicId={}", + crewPrincipal.getPublicId(), userPrincipal.getPublicId()); + + Crew crew = crewService.getCrewByPublicId(crewPrincipal.getPublicId()); + crewValidator.validateCanJoinCrew(userPrincipal.getInternalId(), crew); - CrewJoinRequest joinRequest = crewService.createJoinRequest(crew, userInternalId, request.getAnswer()); + CrewJoinRequest joinRequest = crewService.createJoinRequest(crew, + userPrincipal.getInternalId(), request.getAnswer()); - log.debug("action=create_join_request_end crewPublicId={} userInternalId={} status={}", - crewPublicId, userInternalId, joinRequest.getStatus()); + log.info("action=create_join_request_end crewPublicId={} userPublicId={} status={}", + crewPrincipal.getPublicId(), userPrincipal.getPublicId(), joinRequest.getStatus()); return CrewJoinRequestInternalResponse.builder() - .crewPublicId(crewPublicId) - .userInternalId(userInternalId) - .status(joinRequest.getStatus()) - .requestedAt(joinRequest.getRequestedAt()) - .build(); + .crewPublicId(crewPublicId) + .userInternalId(userPrincipal.getInternalId()) + .status(joinRequest.getStatus()) + .requestedAt(joinRequest.getRequestedAt()) + .build(); } @Override @Transactional - public CrewJoinRequestInternalResponse cancelJoinRequest(String crewPublicId, Integer userInternalId) { - log.debug("action=cancel_join_request_start crewPublicId={} userInternalId={}", crewPublicId, userInternalId); + public CrewJoinRequestInternalResponse cancelJoinRequest( + String crewPublicId, String userPublicId) { + CrewPrincipal crewPrincipal = crewIdResolver.resolve(crewPublicId); + UserPrincipal userPrincipal = userIdResolver.resolve(userPublicId); + + log.info("action=cancel_join_request_start crewPublicId={} userPublicId={}", + crewPrincipal.getPublicId(), userPrincipal.getPublicId()); Crew crew = crewService.getCrewByPublicId(crewPublicId); - crewService.cancelJoinRequest(crew, userInternalId); + crewService.cancelJoinRequest(crew, userPrincipal.getInternalId()); - log.debug("action=cancel_join_request_end crewPublicId={} userInternalId={}", crewPublicId, userInternalId); + log.info("action=cancel_join_request_end crewPublicId={} userPublicId={}", + crewPrincipal.getPublicId(), userPrincipal.getPublicId()); return CrewJoinRequestInternalResponse.builder() - .crewPublicId(crewPublicId) - .userInternalId(userInternalId) - .status(null) - .requestedAt(null) - .build(); + .crewPublicId(crewPrincipal.getPublicId()) + .userInternalId(userPrincipal.getInternalId()) + .status(null) + .requestedAt(null) + .build(); } @Override @Transactional - public List getJoinRequests(String crewPublicId, PageRequest pageRequest, Integer userInternalId) { - log.debug("action=get_join_requests crewPublicId={} userInternalId={}", crewPublicId, userInternalId); + public List getJoinRequests( + String crewPublicId, PageRequest pageRequest, String userPublicId) { + CrewPrincipal crewPrincipal = crewIdResolver.resolve(crewPublicId); + UserPrincipal userPrincipal = userIdResolver.resolve(userPublicId); + + log.info("action=get_join_requests crewPublicId={} userPublicId={}", + crewPrincipal.getPublicId(), userPrincipal.getPublicId()); Crew crew = crewService.getCrewByPublicId(crewPublicId); - crewValidator.validateCanFetchJoinRequests(userInternalId, crew); + crewValidator.validateCanFetchJoinRequests(userPrincipal.getInternalId(), crew); List joinRequests = crewService.getJoinRequests(crew, pageRequest); List userIds = joinRequests.stream() - .map(CrewJoinRequest::getUserId) - .collect(Collectors.toList()); + .map(CrewJoinRequest::getUserId) + .collect(Collectors.toList()); Map userMap = userService.getUserMapByIds(userIds); return joinRequests.stream() - .map(request -> FetchJoinRequestResponse.from(request, userMap.get(request.getUserId()))) - .collect(Collectors.toList()); + .map( + request -> FetchJoinRequestResponse.from(request, userMap.get(request.getUserId()))) + .collect(Collectors.toList()); } @Override @Transactional - public ReviewJoinRequestResponse reviewJoinRequest(String crewPublicId, Integer requestId, CrewJoinRequestStatus status, Integer userInternalId) { - log.debug("action=review_join_request_start crewPublicId={} requestId={} status={}", - crewPublicId, requestId, status); + public ReviewJoinRequestResponse reviewJoinRequest( + String crewPublicId, Integer requestId, CrewJoinRequestStatus status, String userPublicId) { + CrewPrincipal crewPrincipal = crewIdResolver.resolve(crewPublicId); + UserPrincipal userPrincipal = userIdResolver.resolve(userPublicId); + + log.info("action=review_join_request_start crewPublicId={} requestId={} status={}", + crewPrincipal.getPublicId(), requestId, status); Crew crew = crewService.getCrewByPublicId(crewPublicId); - crewValidator.validateCanReviewJoinRequest(userInternalId, status, crew); + crewValidator.validateCanReviewJoinRequest(userPrincipal.getInternalId(), status, crew); CrewJoinRequest joinRequest = crewService.reviewJoinRequest( - crew, - requestId, - status, - userInternalId + crew, + requestId, + status, + userPrincipal.getInternalId() ); - log.debug("action=review_join_request_end crewPublicId={} requestId={}", crewPublicId, requestId); + log.info("action=review_join_request_end crewPublicId={} requestId={}", + crewPrincipal.getPublicId(), requestId); return new ReviewJoinRequestResponse( - joinRequest.getId() + joinRequest.getId() ); } } \ No newline at end of file From 8ab087e9a3fe97e107e51d5028bea2386c70acde Mon Sep 17 00:00:00 2001 From: "Hyeonjae.kim" Date: Mon, 13 Jan 2025 22:07:54 +0900 Subject: [PATCH 4/8] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20Align?= =?UTF-8?q?=20UseCase=20return=20types=20with=20team=20convention?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crew/controller/CrewController.java | 53 ++++++---------- .../CrewJoinRequestInternalResponse.java | 33 ---------- .../response/ReviewJoinRequestResponse.java | 4 +- .../crew/service/usecase/CrewJoinUseCase.java | 12 ++-- .../service/usecase/CrewJoinUseCaseImpl.java | 61 +++++++++++-------- .../security/principal/UserPrincipal.java | 14 ----- 6 files changed, 63 insertions(+), 114 deletions(-) delete mode 100644 src/main/java/com/run_us/server/domains/crew/controller/model/response/CrewJoinRequestInternalResponse.java delete mode 100644 src/main/java/com/run_us/server/global/security/principal/UserPrincipal.java diff --git a/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java b/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java index 71306b4b..4a2139bd 100644 --- a/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java +++ b/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java @@ -1,6 +1,5 @@ package com.run_us.server.domains.crew.controller; -import com.run_us.server.domains.crew.controller.model.enums.CrewHttpResponseCode; import com.run_us.server.domains.crew.controller.model.request.CreateJoinRequest; import com.run_us.server.domains.crew.controller.model.request.ReviewJoinRequest; import com.run_us.server.domains.crew.controller.model.response.*; @@ -46,30 +45,22 @@ public ResponseEntity> createCrew( @PostMapping("/{crewPublicId}/join-requests") public ResponseEntity> requestJoin( @PathVariable String crewPublicId, - @CurrentUser String curentUserPublicId, + @CurrentUser String currentUserPublicId, @Valid @RequestBody CreateJoinRequest request ) { - log.info("action=request_join crewPublicId={} userPublicId={}", crewPublicId, curentUserPublicId); - CrewJoinRequestInternalResponse response = crewJoinUseCase.createJoinRequest(crewPublicId, curentUserPublicId, request); - return ResponseEntity.ok( - SuccessResponse.of( - CrewHttpResponseCode.JOIN_REQUEST_CREATED, - response.toPublicCreateResponse(curentUserPublicId))); + log.info("action=request_join crewPublicId={} userPublicId={}", crewPublicId, currentUserPublicId); + SuccessResponse response = crewJoinUseCase.createJoinRequest(crewPublicId, currentUserPublicId, request); + return ResponseEntity.status(HttpStatus.CREATED).body(response); } @DeleteMapping("/{crewPublicId}/join-requests") public ResponseEntity> cancelJoinRequest( @PathVariable String crewPublicId, - @CurrentUser String curentUserPublicId + @CurrentUser String currentUserPublicId ) { - log.info("action=cancel_join_request crewPublicId={} userPublicId={}", crewPublicId, curentUserPublicId); - CrewJoinRequestInternalResponse response = crewJoinUseCase.cancelJoinRequest(crewPublicId, curentUserPublicId); - return ResponseEntity.ok( - SuccessResponse.of( - CrewHttpResponseCode.JOIN_REQUEST_CANCELLED, - response.toPublicCancelResponse() - ) - ); + log.info("action=cancel_join_request crewPublicId={} userPublicId={}", crewPublicId, currentUserPublicId); + SuccessResponse response = crewJoinUseCase.cancelJoinRequest(crewPublicId, currentUserPublicId); + return ResponseEntity.status(HttpStatus.OK).body(response); } @GetMapping("/{crewPublicId}/join-requests") @@ -77,50 +68,40 @@ public ResponseEntity>> getJoinRe @PathVariable String crewPublicId, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int limit, - @CurrentUser String curentUserPublicId + @CurrentUser String currentUserPublicId ) { log.info("action=get_join_requests_start crewPublicId={} page={} limit={}", crewPublicId, page, limit); - List responses = crewJoinUseCase.getJoinRequests( + SuccessResponse> responses = crewJoinUseCase.getJoinRequests( crewPublicId, PageRequest.of(page, limit), - curentUserPublicId + currentUserPublicId ); log.info("action=get_join_requests_complete crewPublicId={} page={} limit={} result_count={}", - crewPublicId, page, limit, responses.size()); - return ResponseEntity.ok( - SuccessResponse.of( - CrewHttpResponseCode.JOIN_REQUEST_FETCHED, - responses - ) - ); + crewPublicId, page, limit, responses.getPayload().size()); + return ResponseEntity.status(HttpStatus.OK).body(responses); } @PutMapping("/{crewPublicId}/join-requests/{requestId}") public ResponseEntity> reviewJoinRequest( @PathVariable String crewPublicId, @PathVariable Integer requestId, - @CurrentUser String curentUserPublicId, + @CurrentUser String currentUserPublicId, @Valid @RequestBody ReviewJoinRequest request ) { log.info("action=process_join_request_start crewPublicId={} requestId={} status={}", crewPublicId, requestId, request.getStatus()); - ReviewJoinRequestResponse response = crewJoinUseCase.reviewJoinRequest( + SuccessResponse response = crewJoinUseCase.reviewJoinRequest( crewPublicId, requestId, CrewJoinRequestStatus.valueOf(request.getStatus()), - curentUserPublicId + currentUserPublicId ); log.info("action=process_join_request_complete crewPublicId={} requestId={}", crewPublicId, requestId); - return ResponseEntity.ok( - SuccessResponse.of( - CrewHttpResponseCode.JOIN_REQUEST_REVIEWED, - response - ) - ); + return ResponseEntity.status(HttpStatus.OK).body(response); } } diff --git a/src/main/java/com/run_us/server/domains/crew/controller/model/response/CrewJoinRequestInternalResponse.java b/src/main/java/com/run_us/server/domains/crew/controller/model/response/CrewJoinRequestInternalResponse.java deleted file mode 100644 index 98ca9500..00000000 --- a/src/main/java/com/run_us/server/domains/crew/controller/model/response/CrewJoinRequestInternalResponse.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.run_us.server.domains.crew.controller.model.response; - -import com.run_us.server.domains.crew.domain.enums.CrewJoinRequestStatus; -import lombok.Builder; -import lombok.Getter; - -import java.time.ZonedDateTime; - -@Getter -@Builder -public class CrewJoinRequestInternalResponse { - private final Integer id; - private final String crewPublicId; - private final Integer userInternalId; - private final CrewJoinRequestStatus status; - private final ZonedDateTime requestedAt; - - public CreateJoinRequestResponse toPublicCreateResponse(String userPublicId) { - return CreateJoinRequestResponse.builder() - .requestId(this.id) - .crewPublicId(this.crewPublicId) - .userPublicId(userPublicId) - .status(this.status) - .requestedAt(this.requestedAt) - .build(); - } - - public CancelJoinRequestResponse toPublicCancelResponse() { - return CancelJoinRequestResponse.builder() - .requestId(this.id) - .build(); - } -} diff --git a/src/main/java/com/run_us/server/domains/crew/controller/model/response/ReviewJoinRequestResponse.java b/src/main/java/com/run_us/server/domains/crew/controller/model/response/ReviewJoinRequestResponse.java index 4954aad6..6dd43e9d 100644 --- a/src/main/java/com/run_us/server/domains/crew/controller/model/response/ReviewJoinRequestResponse.java +++ b/src/main/java/com/run_us/server/domains/crew/controller/model/response/ReviewJoinRequestResponse.java @@ -1,10 +1,10 @@ package com.run_us.server.domains.crew.controller.model.response; -import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; @Getter -@AllArgsConstructor +@Builder public class ReviewJoinRequestResponse { private Integer requestId; } diff --git a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCase.java b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCase.java index a04758a3..48265b9a 100644 --- a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCase.java +++ b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCase.java @@ -1,17 +1,19 @@ package com.run_us.server.domains.crew.service.usecase; import com.run_us.server.domains.crew.controller.model.request.CreateJoinRequest; -import com.run_us.server.domains.crew.controller.model.response.CrewJoinRequestInternalResponse; +import com.run_us.server.domains.crew.controller.model.response.CancelJoinRequestResponse; +import com.run_us.server.domains.crew.controller.model.response.CreateJoinRequestResponse; import com.run_us.server.domains.crew.controller.model.response.FetchJoinRequestResponse; import com.run_us.server.domains.crew.controller.model.response.ReviewJoinRequestResponse; import com.run_us.server.domains.crew.domain.enums.CrewJoinRequestStatus; +import com.run_us.server.global.common.SuccessResponse; import org.springframework.data.domain.PageRequest; import java.util.List; public interface CrewJoinUseCase { - CrewJoinRequestInternalResponse createJoinRequest(String crewPublicId, String userPublicId, CreateJoinRequest request); - CrewJoinRequestInternalResponse cancelJoinRequest(String crewPublicId, String userPublicId); - List getJoinRequests(String crewPublicId, PageRequest pageRequest, String userPublicId); - ReviewJoinRequestResponse reviewJoinRequest(String crewPublicId, Integer requestId, CrewJoinRequestStatus status, String userPublicId); + SuccessResponse createJoinRequest(String crewPublicId, String userPublicId, CreateJoinRequest request); + SuccessResponse cancelJoinRequest(String crewPublicId, String userPublicId); + SuccessResponse> getJoinRequests(String crewPublicId, PageRequest pageRequest, String userPublicId); + SuccessResponse reviewJoinRequest(String crewPublicId, Integer requestId, CrewJoinRequestStatus status, String userPublicId); } diff --git a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCaseImpl.java b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCaseImpl.java index 9eb3a49d..1dc79342 100644 --- a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCaseImpl.java +++ b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewJoinUseCaseImpl.java @@ -1,7 +1,9 @@ package com.run_us.server.domains.crew.service.usecase; +import com.run_us.server.domains.crew.controller.model.enums.CrewHttpResponseCode; import com.run_us.server.domains.crew.controller.model.request.CreateJoinRequest; -import com.run_us.server.domains.crew.controller.model.response.CrewJoinRequestInternalResponse; +import com.run_us.server.domains.crew.controller.model.response.CancelJoinRequestResponse; +import com.run_us.server.domains.crew.controller.model.response.CreateJoinRequestResponse; import com.run_us.server.domains.crew.controller.model.response.FetchJoinRequestResponse; import com.run_us.server.domains.crew.controller.model.response.ReviewJoinRequestResponse; import com.run_us.server.domains.crew.domain.Crew; @@ -15,6 +17,7 @@ import com.run_us.server.domains.user.domain.UserPrincipal; import com.run_us.server.domains.user.service.UserService; import com.run_us.server.domains.user.service.resolver.UserIdResolver; +import com.run_us.server.global.common.SuccessResponse; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -38,7 +41,7 @@ public class CrewJoinUseCaseImpl implements CrewJoinUseCase { @Override @Transactional - public CrewJoinRequestInternalResponse createJoinRequest( + public SuccessResponse createJoinRequest( String crewPublicId, String userPublicId, CreateJoinRequest request) { CrewPrincipal crewPrincipal = crewIdResolver.resolve(crewPublicId); UserPrincipal userPrincipal = userIdResolver.resolve(userPublicId); @@ -55,17 +58,21 @@ public CrewJoinRequestInternalResponse createJoinRequest( log.info("action=create_join_request_end crewPublicId={} userPublicId={} status={}", crewPrincipal.getPublicId(), userPrincipal.getPublicId(), joinRequest.getStatus()); - return CrewJoinRequestInternalResponse.builder() - .crewPublicId(crewPublicId) - .userInternalId(userPrincipal.getInternalId()) - .status(joinRequest.getStatus()) - .requestedAt(joinRequest.getRequestedAt()) - .build(); + return SuccessResponse.of( + CrewHttpResponseCode.JOIN_REQUEST_CREATED, + CreateJoinRequestResponse.builder() + .crewPublicId(crewPrincipal.getPublicId()) + .userPublicId(userPrincipal.getPublicId()) + .status(joinRequest.getStatus()) + .requestedAt(joinRequest.getRequestedAt()) + .requestId(joinRequest.getId()) + .build() + ); } @Override @Transactional - public CrewJoinRequestInternalResponse cancelJoinRequest( + public SuccessResponse cancelJoinRequest( String crewPublicId, String userPublicId) { CrewPrincipal crewPrincipal = crewIdResolver.resolve(crewPublicId); UserPrincipal userPrincipal = userIdResolver.resolve(userPublicId); @@ -79,17 +86,17 @@ public CrewJoinRequestInternalResponse cancelJoinRequest( log.info("action=cancel_join_request_end crewPublicId={} userPublicId={}", crewPrincipal.getPublicId(), userPrincipal.getPublicId()); - return CrewJoinRequestInternalResponse.builder() - .crewPublicId(crewPrincipal.getPublicId()) - .userInternalId(userPrincipal.getInternalId()) - .status(null) - .requestedAt(null) - .build(); + return SuccessResponse.of( + CrewHttpResponseCode.JOIN_REQUEST_CANCELLED, + CancelJoinRequestResponse.builder() + .requestId(null) + .build() + ); } @Override @Transactional - public List getJoinRequests( + public SuccessResponse> getJoinRequests( String crewPublicId, PageRequest pageRequest, String userPublicId) { CrewPrincipal crewPrincipal = crewIdResolver.resolve(crewPublicId); UserPrincipal userPrincipal = userIdResolver.resolve(userPublicId); @@ -107,15 +114,18 @@ public List getJoinRequests( .collect(Collectors.toList()); Map userMap = userService.getUserMapByIds(userIds); - return joinRequests.stream() - .map( - request -> FetchJoinRequestResponse.from(request, userMap.get(request.getUserId()))) - .collect(Collectors.toList()); + return SuccessResponse.of( + CrewHttpResponseCode.JOIN_REQUEST_FETCHED, + joinRequests.stream() + .map( + request -> FetchJoinRequestResponse.from(request, userMap.get(request.getUserId()))) + .collect(Collectors.toList()) + ); } @Override @Transactional - public ReviewJoinRequestResponse reviewJoinRequest( + public SuccessResponse reviewJoinRequest( String crewPublicId, Integer requestId, CrewJoinRequestStatus status, String userPublicId) { CrewPrincipal crewPrincipal = crewIdResolver.resolve(crewPublicId); UserPrincipal userPrincipal = userIdResolver.resolve(userPublicId); @@ -136,8 +146,11 @@ public ReviewJoinRequestResponse reviewJoinRequest( log.info("action=review_join_request_end crewPublicId={} requestId={}", crewPrincipal.getPublicId(), requestId); - return new ReviewJoinRequestResponse( - joinRequest.getId() - ); + return SuccessResponse.of( + CrewHttpResponseCode.JOIN_REQUEST_REVIEWED, + ReviewJoinRequestResponse.builder() + .requestId(joinRequest.getId()) + .build() + ) ; } } \ No newline at end of file diff --git a/src/main/java/com/run_us/server/global/security/principal/UserPrincipal.java b/src/main/java/com/run_us/server/global/security/principal/UserPrincipal.java deleted file mode 100644 index 3b3c65d5..00000000 --- a/src/main/java/com/run_us/server/global/security/principal/UserPrincipal.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.run_us.server.global.security.principal; - -import lombok.Getter; - -@Getter -public class UserPrincipal { - private final String publicId; - private final Integer internalId; - - public UserPrincipal(String publicId, Integer userId) { - this.publicId = publicId; - this.internalId = userId; - } -} From 0850b6f9d212f79fbf97142904357eff9627b90f Mon Sep 17 00:00:00 2001 From: "Hyeonjae.kim" Date: Wed, 15 Jan 2025 14:21:36 +0900 Subject: [PATCH 5/8] =?UTF-8?q?=E2=9C=A8=20Feat=20:=20Implement=20KickMemb?= =?UTF-8?q?er=20logics?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crew/controller/CrewController.java | 19 +++++++ .../controller/model/enums/CrewErrorCode.java | 1 + .../model/enums/CrewHttpResponseCode.java | 2 + .../model/response/KickMemberResponse.java | 10 ++++ .../server/domains/crew/domain/Crew.java | 5 ++ .../domains/crew/service/CrewService.java | 10 ++++ .../domains/crew/service/CrewValidator.java | 14 +++++ .../service/usecase/CrewMemberUseCase.java | 8 +++ .../usecase/CrewMemberUseCaseImpl.java | 57 +++++++++++++++++++ 9 files changed, 126 insertions(+) create mode 100644 src/main/java/com/run_us/server/domains/crew/controller/model/response/KickMemberResponse.java create mode 100644 src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCase.java create mode 100644 src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCaseImpl.java diff --git a/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java b/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java index 4a2139bd..840371b9 100644 --- a/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java +++ b/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java @@ -8,6 +8,7 @@ import com.run_us.server.domains.crew.controller.model.request.CreateCrewRequest; import com.run_us.server.domains.crew.controller.model.response.CreateCrewResponse; import com.run_us.server.domains.crew.service.usecase.CreateCrewUseCase; +import com.run_us.server.domains.crew.service.usecase.CrewMemberUseCase; import com.run_us.server.global.common.SuccessResponse; import com.run_us.server.global.security.annotation.CurrentUser; @@ -28,6 +29,7 @@ public class CrewController { private final CrewJoinUseCase crewJoinUseCase; private final CreateCrewUseCase createCrewUseCase; + private final CrewMemberUseCase crewMemberUseCase; @PostMapping @@ -104,4 +106,21 @@ public ResponseEntity> reviewJoinRequ crewPublicId, requestId); return ResponseEntity.status(HttpStatus.OK).body(response); } + + @DeleteMapping("/{crewPublicId}/members/{userPublicId}") + public ResponseEntity> kickMember( + @PathVariable String crewPublicId, + @PathVariable String userPublicId, + @CurrentUser String currentUserPublicId + ) { + log.info("action=remove_member_request_start crewPublicId={} userPublicId={} currentUserPublicId={}", + crewPublicId, userPublicId, currentUserPublicId); + + SuccessResponse response = + crewMemberUseCase.kickMember(crewPublicId, currentUserPublicId, userPublicId); + + log.info("action=remove_member_request_end crewPublicId={} userPublicId={} currentUserPublicId={}", + crewPublicId, userPublicId, currentUserPublicId); + return ResponseEntity.status(HttpStatus.OK).body(response); + } } diff --git a/src/main/java/com/run_us/server/domains/crew/controller/model/enums/CrewErrorCode.java b/src/main/java/com/run_us/server/domains/crew/controller/model/enums/CrewErrorCode.java index 1037f806..478d2cfc 100644 --- a/src/main/java/com/run_us/server/domains/crew/controller/model/enums/CrewErrorCode.java +++ b/src/main/java/com/run_us/server/domains/crew/controller/model/enums/CrewErrorCode.java @@ -15,6 +15,7 @@ public enum CrewErrorCode implements CustomResponseCode { SUSPENDED_CREW("CEH4004", "Suspended crew", "Suspended crew", HttpStatus.BAD_REQUEST), INVALID_JOIN_REQUEST_STATUS("CEH4005", "Invalid join request status", "Invalid join request status", HttpStatus.BAD_REQUEST), JOIN_REQUEST_ALREADY_PROCESSED("CEH4006", "Join request already processed", "Join request already processed", HttpStatus.BAD_REQUEST), + NOT_CREW_MEMBER("CEH4007", "User is not a crew member", "User is not a crew member", HttpStatus.BAD_REQUEST), // 403 diff --git a/src/main/java/com/run_us/server/domains/crew/controller/model/enums/CrewHttpResponseCode.java b/src/main/java/com/run_us/server/domains/crew/controller/model/enums/CrewHttpResponseCode.java index abed730b..9b2cd0bf 100644 --- a/src/main/java/com/run_us/server/domains/crew/controller/model/enums/CrewHttpResponseCode.java +++ b/src/main/java/com/run_us/server/domains/crew/controller/model/enums/CrewHttpResponseCode.java @@ -18,6 +18,8 @@ public enum CrewHttpResponseCode implements CustomResponseCode { JOIN_REQUEST_CANCELLED("CSH2008", HttpStatus.OK, "크루 가입 요청 취소 성공", "크루 가입 요청 취소 성공"), JOIN_REQUEST_FETCHED("CSH2009", HttpStatus.OK, "크루 가입 요청 조회 성공", "크루 가입 요청 조회 성공"), JOIN_REQUEST_REVIEWED("CSH2010", HttpStatus.OK, "크루 가입 요청 처리 성공", "크루 가입 요청 처리 성공"), + + KICK_MEMBER_SUCCESS("CSH2031", HttpStatus.OK, "크루 멤버 추방 성공", "크루 멤버 추방 성공"), ; private final String code; diff --git a/src/main/java/com/run_us/server/domains/crew/controller/model/response/KickMemberResponse.java b/src/main/java/com/run_us/server/domains/crew/controller/model/response/KickMemberResponse.java new file mode 100644 index 00000000..77e70e4c --- /dev/null +++ b/src/main/java/com/run_us/server/domains/crew/controller/model/response/KickMemberResponse.java @@ -0,0 +1,10 @@ +package com.run_us.server.domains.crew.controller.model.response; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class KickMemberResponse { + private String userPublicId; +} diff --git a/src/main/java/com/run_us/server/domains/crew/domain/Crew.java b/src/main/java/com/run_us/server/domains/crew/domain/Crew.java index bb40a49c..f286bb87 100644 --- a/src/main/java/com/run_us/server/domains/crew/domain/Crew.java +++ b/src/main/java/com/run_us/server/domains/crew/domain/Crew.java @@ -70,6 +70,11 @@ public void addMember(Integer userId) { this.memberCount++; } + public void removeMember(Integer userId) { + this.crewMemberships.removeIf(membership -> membership.getUserId().equals(userId)); + this.memberCount--; + } + @Override public void prePersist() { this.publicId = TSID.Factory.getTsid().toString(); diff --git a/src/main/java/com/run_us/server/domains/crew/service/CrewService.java b/src/main/java/com/run_us/server/domains/crew/service/CrewService.java index 0f49f207..78a963b7 100644 --- a/src/main/java/com/run_us/server/domains/crew/service/CrewService.java +++ b/src/main/java/com/run_us/server/domains/crew/service/CrewService.java @@ -77,4 +77,14 @@ public CrewJoinRequest reviewJoinRequest(Crew crew, Integer requestId, CrewJoinR crew.getPublicId(), requestId); return request; } + + @Transactional + public void removeMember(Crew crew, Integer targetUserId) { + log.debug("action=remove_member_start crewPublicId={} targetUserId={}", crew.getPublicId(), targetUserId); + + crew.removeMember(targetUserId); + crewRepository.save(crew); + + log.debug("action=remove_member_end crewPublicId={} targetUserId={}", crew.getPublicId(), targetUserId); + } } \ No newline at end of file diff --git a/src/main/java/com/run_us/server/domains/crew/service/CrewValidator.java b/src/main/java/com/run_us/server/domains/crew/service/CrewValidator.java index 01d833f0..6d806ba8 100644 --- a/src/main/java/com/run_us/server/domains/crew/service/CrewValidator.java +++ b/src/main/java/com/run_us/server/domains/crew/service/CrewValidator.java @@ -68,4 +68,18 @@ public void validateCanReviewJoinRequest(Integer userId, CrewJoinRequestStatus s throw new CrewException(CrewErrorCode.SUSPENDED_CREW); } } + + public void validateCanKickMember(Integer userId, Integer targetMemberId, Crew crew) { + if(!crew.getOwner().getId().equals(userId)) { + throw new CrewException(CrewErrorCode.CREW_NOT_FOUND); + } + + if (!crewRepository.existsMembershipByCrewIdAndUserId(crew.getId(), targetMemberId)) { + throw new CrewException(CrewErrorCode.NOT_CREW_MEMBER); + } + + if(!crew.isActive()) { + throw new CrewException(CrewErrorCode.SUSPENDED_CREW); + } + } } \ No newline at end of file diff --git a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCase.java b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCase.java new file mode 100644 index 00000000..362bddfc --- /dev/null +++ b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCase.java @@ -0,0 +1,8 @@ +package com.run_us.server.domains.crew.service.usecase; + +import com.run_us.server.domains.crew.controller.model.response.KickMemberResponse; +import com.run_us.server.global.common.SuccessResponse; + +public interface CrewMemberUseCase { + SuccessResponse kickMember(String crewPublicId, String userPublicId, String memberPublicId); +} diff --git a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCaseImpl.java b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCaseImpl.java new file mode 100644 index 00000000..4cd827fd --- /dev/null +++ b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCaseImpl.java @@ -0,0 +1,57 @@ +package com.run_us.server.domains.crew.service.usecase; + +import com.run_us.server.domains.crew.controller.model.enums.CrewHttpResponseCode; +import com.run_us.server.domains.crew.controller.model.response.KickMemberResponse; +import com.run_us.server.domains.crew.domain.Crew; +import com.run_us.server.domains.crew.domain.CrewPrincipal; +import com.run_us.server.domains.crew.service.CrewService; +import com.run_us.server.domains.crew.service.CrewValidator; +import com.run_us.server.domains.crew.service.resolver.CrewIdResolver; +import com.run_us.server.domains.user.domain.UserPrincipal; +import com.run_us.server.domains.user.service.resolver.UserIdResolver; +import com.run_us.server.global.common.SuccessResponse; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor +public class CrewMemberUseCaseImpl implements CrewMemberUseCase { + private final CrewService crewService; + private final CrewValidator crewValidator; + private final CrewIdResolver crewIdResolver; + private final UserIdResolver userIdResolver; + + @Override + @Transactional + public SuccessResponse kickMember( + String crewPublicId, String actionUserPublicId, String targetMemberPublicId) { + CrewPrincipal crewPrincipal = crewIdResolver.resolve(crewPublicId); + UserPrincipal actionUserPrincipal = userIdResolver.resolve(actionUserPublicId); + UserPrincipal targetMemberPrincipal = userIdResolver.resolve(targetMemberPublicId); + + log.info( + "action=kick_member_start crewPublicId={} actionUserPublicId={} targetMemberPublicId={}", + crewPrincipal.getPublicId(), actionUserPrincipal.getPublicId(), + targetMemberPrincipal.getPublicId()); + + Crew crew = crewService.getCrewByPublicId(crewPrincipal.getPublicId()); + crewValidator.validateCanKickMember( + actionUserPrincipal.getInternalId(), targetMemberPrincipal.getInternalId(), crew); + crewService.removeMember(crew, targetMemberPrincipal.getInternalId()); + + log.info( + "action=kick_member_end crewPublicId={} actionUserPublicId={} targetMemberPublicId={}", + crewPrincipal.getPublicId(), actionUserPrincipal.getPublicId(), + targetMemberPrincipal.getPublicId()); + + return SuccessResponse.of( + CrewHttpResponseCode.KICK_MEMBER_SUCCESS, + KickMemberResponse.builder() + .userPublicId(targetMemberPrincipal.getPublicId()) + .build() + ); + } +} From 504c7e0ccf31a044af33086fe77bd40e0ebe4e81 Mon Sep 17 00:00:00 2001 From: "Hyeonjae.kim" Date: Wed, 15 Jan 2025 16:49:27 +0900 Subject: [PATCH 6/8] =?UTF-8?q?=E2=9C=A8=20Feat=20:=20Implement=20FetchCre?= =?UTF-8?q?wMembers=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crew/controller/CrewController.java | 18 ++++ .../model/enums/CrewHttpResponseCode.java | 1 + .../model/response/FetchMemberResponse.java | 26 ++++++ .../domains/crew/service/CrewValidator.java | 10 ++ .../service/usecase/CrewMemberUseCase.java | 4 + .../usecase/CrewMemberUseCaseImpl.java | 92 ++++++++++++++++++- .../repository/RunRecordRepository.java | 9 ++ .../record/service/RecordQueryService.java | 5 + 8 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/run_us/server/domains/crew/controller/model/response/FetchMemberResponse.java diff --git a/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java b/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java index 840371b9..abbb2067 100644 --- a/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java +++ b/src/main/java/com/run_us/server/domains/crew/controller/CrewController.java @@ -107,6 +107,24 @@ public ResponseEntity> reviewJoinRequ return ResponseEntity.status(HttpStatus.OK).body(response); } + @GetMapping("/{crewPublicId}/members") + public ResponseEntity>> getMembers( + @PathVariable String crewPublicId, + @CurrentUser String currentUserPublicId, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "20") int limit + ) { + log.info("action=get_members_request_start crewPublicId={} currentUserPublicId={}", + crewPublicId, currentUserPublicId); + + SuccessResponse> response = + crewMemberUseCase.getMembers(crewPublicId, currentUserPublicId, PageRequest.of(page, limit)); + + log.info("action=get_members_request_end crewPublicId={} currentUserPublicId={}", + crewPublicId, currentUserPublicId); + return ResponseEntity.status(HttpStatus.OK).body(response); + } + @DeleteMapping("/{crewPublicId}/members/{userPublicId}") public ResponseEntity> kickMember( @PathVariable String crewPublicId, diff --git a/src/main/java/com/run_us/server/domains/crew/controller/model/enums/CrewHttpResponseCode.java b/src/main/java/com/run_us/server/domains/crew/controller/model/enums/CrewHttpResponseCode.java index 9b2cd0bf..048912f9 100644 --- a/src/main/java/com/run_us/server/domains/crew/controller/model/enums/CrewHttpResponseCode.java +++ b/src/main/java/com/run_us/server/domains/crew/controller/model/enums/CrewHttpResponseCode.java @@ -20,6 +20,7 @@ public enum CrewHttpResponseCode implements CustomResponseCode { JOIN_REQUEST_REVIEWED("CSH2010", HttpStatus.OK, "크루 가입 요청 처리 성공", "크루 가입 요청 처리 성공"), KICK_MEMBER_SUCCESS("CSH2031", HttpStatus.OK, "크루 멤버 추방 성공", "크루 멤버 추방 성공"), + GET_MEMBERS_SUCCESS("CSH2032", HttpStatus.OK, "크루 멤버 리스트 조회 성공", "크루 멤버 리스트 조회 성공"), ; private final String code; diff --git a/src/main/java/com/run_us/server/domains/crew/controller/model/response/FetchMemberResponse.java b/src/main/java/com/run_us/server/domains/crew/controller/model/response/FetchMemberResponse.java new file mode 100644 index 00000000..b3fc3384 --- /dev/null +++ b/src/main/java/com/run_us/server/domains/crew/controller/model/response/FetchMemberResponse.java @@ -0,0 +1,26 @@ +package com.run_us.server.domains.crew.controller.model.response; + +import com.run_us.server.domains.crew.domain.CrewMembership; +import com.run_us.server.domains.user.domain.User; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class FetchMemberResponse { + private String nickname; + private String publicId; + private String profileImg; + private String role; + private int runningDistance; + + public static FetchMemberResponse from(User user, CrewMembership crewMembership, int runningDistance) { + return new FetchMemberResponse( + user.getProfile().getNickname(), + user.getPublicId(), + user.getProfile().getImgUrl(), + crewMembership.getRole().name(), + runningDistance + ); + } +} diff --git a/src/main/java/com/run_us/server/domains/crew/service/CrewValidator.java b/src/main/java/com/run_us/server/domains/crew/service/CrewValidator.java index 6d806ba8..158e0a0d 100644 --- a/src/main/java/com/run_us/server/domains/crew/service/CrewValidator.java +++ b/src/main/java/com/run_us/server/domains/crew/service/CrewValidator.java @@ -82,4 +82,14 @@ public void validateCanKickMember(Integer userId, Integer targetMemberId, Crew c throw new CrewException(CrewErrorCode.SUSPENDED_CREW); } } + + public void validateCanFetchMembers(Integer userId, Crew crew) { + if(!crew.getOwner().getId().equals(userId)) { + throw new CrewException(CrewErrorCode.CREW_NOT_FOUND); + } + + if(!crew.isActive()) { + throw new CrewException(CrewErrorCode.SUSPENDED_CREW); + } + } } \ No newline at end of file diff --git a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCase.java b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCase.java index 362bddfc..ece1623c 100644 --- a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCase.java +++ b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCase.java @@ -1,8 +1,12 @@ package com.run_us.server.domains.crew.service.usecase; +import com.run_us.server.domains.crew.controller.model.response.FetchMemberResponse; import com.run_us.server.domains.crew.controller.model.response.KickMemberResponse; import com.run_us.server.global.common.SuccessResponse; +import java.util.List; +import org.springframework.data.domain.PageRequest; public interface CrewMemberUseCase { SuccessResponse kickMember(String crewPublicId, String userPublicId, String memberPublicId); + SuccessResponse> getMembers(String crewPublicId, String userPublicId, PageRequest pageRequest); } diff --git a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCaseImpl.java b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCaseImpl.java index 4cd827fd..ea96de09 100644 --- a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCaseImpl.java +++ b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCaseImpl.java @@ -1,19 +1,31 @@ package com.run_us.server.domains.crew.service.usecase; import com.run_us.server.domains.crew.controller.model.enums.CrewHttpResponseCode; +import com.run_us.server.domains.crew.controller.model.response.FetchMemberResponse; import com.run_us.server.domains.crew.controller.model.response.KickMemberResponse; import com.run_us.server.domains.crew.domain.Crew; +import com.run_us.server.domains.crew.domain.CrewMembership; import com.run_us.server.domains.crew.domain.CrewPrincipal; import com.run_us.server.domains.crew.service.CrewService; import com.run_us.server.domains.crew.service.CrewValidator; import com.run_us.server.domains.crew.service.resolver.CrewIdResolver; +import com.run_us.server.domains.running.record.service.RecordQueryService; +import com.run_us.server.domains.user.domain.User; import com.run_us.server.domains.user.domain.UserPrincipal; +import com.run_us.server.domains.user.service.UserService; import com.run_us.server.domains.user.service.resolver.UserIdResolver; import com.run_us.server.global.common.SuccessResponse; -import jakarta.transaction.Transactional; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; + +import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Slf4j @Service @@ -23,6 +35,8 @@ public class CrewMemberUseCaseImpl implements CrewMemberUseCase { private final CrewValidator crewValidator; private final CrewIdResolver crewIdResolver; private final UserIdResolver userIdResolver; + private final UserService userService; + private final RecordQueryService runRecordService; @Override @Transactional @@ -54,4 +68,80 @@ public SuccessResponse kickMember( .build() ); } + + @Override + @Transactional(readOnly = true) + public SuccessResponse> getMembers( + String crewPublicId, String userPublicId, PageRequest pageRequest){ + CrewPrincipal crewPrincipal = crewIdResolver.resolve(crewPublicId); + UserPrincipal userPrincipal = userIdResolver.resolve(userPublicId); + + log.info("action=get_members_start crewPublicId={} userPublicId={} page={} size={}", + crewPrincipal.getPublicId(), userPrincipal.getPublicId(), + pageRequest.getPageNumber(), pageRequest.getPageSize()); + + Crew crew = crewService.getCrewByPublicId(crewPrincipal.getPublicId()); + crewValidator.validateCanFetchMembers(userPrincipal.getInternalId(), crew); + + List memberResponses = getMemberResponses(crew, pageRequest); + + log.info("action=get_members_end crewPublicId={} userPublicId={}", + crewPrincipal.getPublicId(), userPrincipal.getPublicId()); + + return SuccessResponse.of( + CrewHttpResponseCode.GET_MEMBERS_SUCCESS, + memberResponses + ); + } + + private List getMemberResponses( + Crew crew, + PageRequest pageRequest + ) { + List memberships = crew.getCrewMemberships().stream() + .sorted(Comparator.comparing(CrewMembership::getJoinedAt).reversed()) + .skip((long) pageRequest.getPageNumber() * pageRequest.getPageSize()) + .limit(pageRequest.getPageSize()) + .toList(); + + if (memberships.isEmpty()) { + return Collections.emptyList(); + } + + List userIds = getUserIds(memberships); + + Map userMap = userService.getUserMapByIds(userIds); + Map distanceMap = runRecordService.getTotalDistanceMapByUserIds(userIds); + + return mapToMemberResponses(memberships, userMap, distanceMap); + } + + private List getUserIds(List memberships) { + return memberships.stream() + .map(CrewMembership::getUserId) + .toList(); + } + + private List mapToMemberResponses( + List memberships, + Map userMap, + Map distanceMap + ) { + return memberships.stream() + .map(membership -> createMemberResponse(membership, userMap, distanceMap)) + .toList(); + } + + private FetchMemberResponse createMemberResponse( + CrewMembership membership, + Map userMap, + Map distanceMap + ) { + Integer userId = membership.getUserId(); + return FetchMemberResponse.from( + userMap.get(userId), + membership, + distanceMap.getOrDefault(userId, 0) + ); + } } diff --git a/src/main/java/com/run_us/server/domains/running/record/repository/RunRecordRepository.java b/src/main/java/com/run_us/server/domains/running/record/repository/RunRecordRepository.java index 4e6cd799..e4c75104 100644 --- a/src/main/java/com/run_us/server/domains/running/record/repository/RunRecordRepository.java +++ b/src/main/java/com/run_us/server/domains/running/record/repository/RunRecordRepository.java @@ -6,6 +6,7 @@ import com.run_us.server.domains.running.record.service.model.RecordStatsAggregation; import java.time.ZonedDateTime; import java.util.List; +import java.util.Map; import java.util.Optional; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -73,4 +74,12 @@ List getDailyRunDistanceInTimeRange( nativeQuery = true) List getMonthlyRunDistancesOfYear( Integer userId, ZonedDateTime start, ZonedDateTime end); + + @Query(""" + SELECT r.userId, SUM(r.recordStat.runningDistanceInMeters) + FROM RunRecord r + WHERE r.userId IN :userIds + GROUP BY r.userId + """) + Map findTotalDistanceByUserIds(List userIds); } diff --git a/src/main/java/com/run_us/server/domains/running/record/service/RecordQueryService.java b/src/main/java/com/run_us/server/domains/running/record/service/RecordQueryService.java index adfcdcb3..2248f736 100644 --- a/src/main/java/com/run_us/server/domains/running/record/service/RecordQueryService.java +++ b/src/main/java/com/run_us/server/domains/running/record/service/RecordQueryService.java @@ -9,6 +9,7 @@ import com.run_us.server.domains.running.record.service.model.RecordStatsAggregation; import java.time.ZonedDateTime; import java.util.List; +import java.util.Map; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -70,4 +71,8 @@ public List getMonthlyRunDistancesOfYear( Integer userId, ZonedDateTime start, ZonedDateTime end) { return runRecordRepository.getMonthlyRunDistancesOfYear(userId, start, end); } + + public Map getTotalDistanceMapByUserIds(List userIds) { + return runRecordRepository.findTotalDistanceByUserIds(userIds); + } } From 38b073b5b35958c3c32e0b66f5ab5f5e8391acc3 Mon Sep 17 00:00:00 2001 From: "Hyeonjae.kim" Date: Sun, 19 Jan 2025 16:22:44 +0900 Subject: [PATCH 7/8] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20Optimiz?= =?UTF-8?q?e=20fetchMember=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crew/repository/CrewRepository.java | 13 ++++++--- .../domains/crew/service/CrewService.java | 7 +++++ .../usecase/CrewMemberUseCaseImpl.java | 27 +++++++++++++------ .../repository/RunRecordRepository.java | 9 ------- .../record/service/RecordQueryService.java | 5 ---- .../server/global/common/GlobalConst.java | 4 +-- 6 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/run_us/server/domains/crew/repository/CrewRepository.java b/src/main/java/com/run_us/server/domains/crew/repository/CrewRepository.java index afc03d15..f861442a 100644 --- a/src/main/java/com/run_us/server/domains/crew/repository/CrewRepository.java +++ b/src/main/java/com/run_us/server/domains/crew/repository/CrewRepository.java @@ -1,13 +1,12 @@ package com.run_us.server.domains.crew.repository; import com.run_us.server.domains.crew.domain.Crew; -import com.run_us.server.domains.crew.domain.CrewJoinRequest; +import com.run_us.server.domains.crew.domain.CrewMembership; + import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -17,4 +16,12 @@ public interface CrewRepository extends JpaRepository { @Query("SELECT EXISTS (SELECT 1 FROM Crew c JOIN c.crewMemberships m " + "WHERE c.id = :crewId AND m.userId = :userId)") boolean existsMembershipByCrewIdAndUserId(Integer crewId, Integer userId); + + @Query("SELECT m FROM Crew c JOIN c.crewMemberships m " + + "WHERE c.id = :crewId " + + "ORDER BY m.joinedAt DESC") + List findMembershipsByCrewId( + Integer crewId, + PageRequest pageRequest + ); } \ No newline at end of file diff --git a/src/main/java/com/run_us/server/domains/crew/service/CrewService.java b/src/main/java/com/run_us/server/domains/crew/service/CrewService.java index 78a963b7..62a01b81 100644 --- a/src/main/java/com/run_us/server/domains/crew/service/CrewService.java +++ b/src/main/java/com/run_us/server/domains/crew/service/CrewService.java @@ -4,6 +4,7 @@ import com.run_us.server.domains.crew.controller.model.enums.CrewErrorCode; import com.run_us.server.domains.crew.domain.Crew; import com.run_us.server.domains.crew.domain.CrewJoinRequest; +import com.run_us.server.domains.crew.domain.CrewMembership; import com.run_us.server.domains.crew.domain.enums.CrewJoinRequestStatus; import com.run_us.server.domains.crew.domain.enums.CrewJoinType; import com.run_us.server.domains.crew.repository.CrewJoinRequestRepository; @@ -36,6 +37,12 @@ public List getJoinRequests(Crew crew, PageRequest pageRequest) return crewJoinRequestRepository.findAllByCrewId(crew.getId(), pageRequest).getContent(); } + @Transactional(readOnly = true) + public List getMemberships(Crew crew, PageRequest pageRequest) { + log.debug("action=get_memberships_start crewId={}", crew.getPublicId()); + return crewRepository.findMembershipsByCrewId(crew.getId(), pageRequest); + } + @Transactional public CrewJoinRequest createJoinRequest(Crew crew, Integer userInternalId, String answer) { log.debug("action=create_join_request crewPublicId={} userInternalId={}", crew.getPublicId(), userInternalId); diff --git a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCaseImpl.java b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCaseImpl.java index ea96de09..f16bac3f 100644 --- a/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCaseImpl.java +++ b/src/main/java/com/run_us/server/domains/crew/service/usecase/CrewMemberUseCaseImpl.java @@ -17,9 +17,9 @@ import com.run_us.server.global.common.SuccessResponse; import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -36,7 +36,6 @@ public class CrewMemberUseCaseImpl implements CrewMemberUseCase { private final CrewIdResolver crewIdResolver; private final UserIdResolver userIdResolver; private final UserService userService; - private final RecordQueryService runRecordService; @Override @Transactional @@ -98,11 +97,7 @@ private List getMemberResponses( Crew crew, PageRequest pageRequest ) { - List memberships = crew.getCrewMemberships().stream() - .sorted(Comparator.comparing(CrewMembership::getJoinedAt).reversed()) - .skip((long) pageRequest.getPageNumber() * pageRequest.getPageSize()) - .limit(pageRequest.getPageSize()) - .toList(); + List memberships = crewService.getMemberships(crew, pageRequest); if (memberships.isEmpty()) { return Collections.emptyList(); @@ -111,7 +106,7 @@ private List getMemberResponses( List userIds = getUserIds(memberships); Map userMap = userService.getUserMapByIds(userIds); - Map distanceMap = runRecordService.getTotalDistanceMapByUserIds(userIds); + Map distanceMap = getUserTotalDistance(userMap); return mapToMemberResponses(memberships, userMap, distanceMap); } @@ -122,6 +117,22 @@ private List getUserIds(List memberships) { .toList(); } + private Map getUserTotalDistance(Map userMap) { + return userMap.entrySet().parallelStream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> getDistanceOrDefault(entry.getValue()) + )); + } + + private int getDistanceOrDefault(User user) { + if (user == null || user.getProfile() == null) { + return 0; + } + Integer distance = user.getProfile().getTotalDistance(); + return distance != null ? distance : 0; + } + private List mapToMemberResponses( List memberships, Map userMap, diff --git a/src/main/java/com/run_us/server/domains/running/record/repository/RunRecordRepository.java b/src/main/java/com/run_us/server/domains/running/record/repository/RunRecordRepository.java index e4c75104..4e6cd799 100644 --- a/src/main/java/com/run_us/server/domains/running/record/repository/RunRecordRepository.java +++ b/src/main/java/com/run_us/server/domains/running/record/repository/RunRecordRepository.java @@ -6,7 +6,6 @@ import com.run_us.server.domains.running.record.service.model.RecordStatsAggregation; import java.time.ZonedDateTime; import java.util.List; -import java.util.Map; import java.util.Optional; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -74,12 +73,4 @@ List getDailyRunDistanceInTimeRange( nativeQuery = true) List getMonthlyRunDistancesOfYear( Integer userId, ZonedDateTime start, ZonedDateTime end); - - @Query(""" - SELECT r.userId, SUM(r.recordStat.runningDistanceInMeters) - FROM RunRecord r - WHERE r.userId IN :userIds - GROUP BY r.userId - """) - Map findTotalDistanceByUserIds(List userIds); } diff --git a/src/main/java/com/run_us/server/domains/running/record/service/RecordQueryService.java b/src/main/java/com/run_us/server/domains/running/record/service/RecordQueryService.java index 2248f736..adfcdcb3 100644 --- a/src/main/java/com/run_us/server/domains/running/record/service/RecordQueryService.java +++ b/src/main/java/com/run_us/server/domains/running/record/service/RecordQueryService.java @@ -9,7 +9,6 @@ import com.run_us.server.domains.running.record.service.model.RecordStatsAggregation; import java.time.ZonedDateTime; import java.util.List; -import java.util.Map; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -71,8 +70,4 @@ public List getMonthlyRunDistancesOfYear( Integer userId, ZonedDateTime start, ZonedDateTime end) { return runRecordRepository.getMonthlyRunDistancesOfYear(userId, start, end); } - - public Map getTotalDistanceMapByUserIds(List userIds) { - return runRecordRepository.findTotalDistanceByUserIds(userIds); - } } diff --git a/src/main/java/com/run_us/server/global/common/GlobalConst.java b/src/main/java/com/run_us/server/global/common/GlobalConst.java index 629e0158..9ed44e44 100644 --- a/src/main/java/com/run_us/server/global/common/GlobalConst.java +++ b/src/main/java/com/run_us/server/global/common/GlobalConst.java @@ -3,8 +3,8 @@ import lombok.AccessLevel; import lombok.NoArgsConstructor; +import java.util.Set; import java.time.ZoneId; -import java.util.List; import static com.run_us.server.global.common.SocketConst.WS_CONNECT_ENDPOINT; @@ -17,7 +17,7 @@ public final class GlobalConst { public static final String WS_USER_AUTH_HEADER = "user-id"; public static final String SESSION_ATTRIBUTE_USER = "user-info"; - public static final List WHITE_LIST_PATHS = List.of( + public static final Set WHITE_LIST_PATHS = Set.of( "/test/auth", WS_CONNECT_ENDPOINT, "/auth" From c15422635ff67a97d0fe22532b50d03cf41279d7 Mon Sep 17 00:00:00 2001 From: "Hyeonjae.kim" Date: Sun, 19 Jan 2025 16:30:20 +0900 Subject: [PATCH 8/8] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20:=20Split?= =?UTF-8?q?=20domainPrincipalCache=20by=20specific=20types?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/global/config/CacheConfig.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/run_us/server/global/config/CacheConfig.java b/src/main/java/com/run_us/server/global/config/CacheConfig.java index a2d00aa2..ee3d9b10 100644 --- a/src/main/java/com/run_us/server/global/config/CacheConfig.java +++ b/src/main/java/com/run_us/server/global/config/CacheConfig.java @@ -1,5 +1,7 @@ package com.run_us.server.global.config; +import com.run_us.server.domains.crew.domain.CrewPrincipal; +import com.run_us.server.domains.user.domain.UserPrincipal; import com.run_us.server.global.common.cache.InMemoryCache; import com.run_us.server.global.common.cache.SpringInMemoryCache; import com.run_us.server.domains.user.domain.TokenStatus; @@ -41,12 +43,22 @@ public InMemoryCache tokenStatusCache(TaskScheduler cacheCl } @Bean - public InMemoryCache domainPrincipalCache( - TaskScheduler cacheCleanupScheduler + public InMemoryCache userPrincipalCache( + TaskScheduler cacheCleanupScheduler ) { return new SpringInMemoryCache<>( - cacheCleanupScheduler, - Duration.ofSeconds(cleanupIntervalSeconds) + cacheCleanupScheduler, + Duration.ofSeconds(cleanupIntervalSeconds) + ); + } + + @Bean + public InMemoryCache crewPrincipalCache( + TaskScheduler cacheCleanupScheduler + ) { + return new SpringInMemoryCache<>( + cacheCleanupScheduler, + Duration.ofSeconds(cleanupIntervalSeconds) ); } } \ No newline at end of file