diff --git a/tht-admin/src/main/java/com/tht/thtadmin/security/CorsConfig.java b/tht-admin/src/main/java/com/tht/thtadmin/security/CorsConfig.java new file mode 100644 index 00000000..bd2150b4 --- /dev/null +++ b/tht-admin/src/main/java/com/tht/thtadmin/security/CorsConfig.java @@ -0,0 +1,19 @@ +package com.tht.thtadmin.security; + +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Component +public class CorsConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("http://localhost:5173") + .allowedHeaders("*") + .allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "PATCH") + .exposedHeaders("Authorization") + .allowCredentials(true); + } +} diff --git a/tht-admin/src/main/java/com/tht/thtadmin/security/SecurityConfiguration.java b/tht-admin/src/main/java/com/tht/thtadmin/security/SecurityConfiguration.java index b8876180..90d91b24 100644 --- a/tht-admin/src/main/java/com/tht/thtadmin/security/SecurityConfiguration.java +++ b/tht-admin/src/main/java/com/tht/thtadmin/security/SecurityConfiguration.java @@ -6,10 +6,16 @@ import org.springframework.boot.autoconfigure.security.servlet.PathRequest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.List; @Configuration @EnableWebSecurity @@ -31,6 +37,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws httpSecurity .csrf().disable() + .cors(Customizer.withDefaults()) .httpBasic().disable() .exceptionHandling(); @@ -51,4 +58,15 @@ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws .build(); } + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(List.of("http://localhost:5173")); + configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); + configuration.setAllowedHeaders(List.of("*")); + configuration.setAllowCredentials(true); + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } } diff --git a/tht-admin/src/main/java/com/tht/thtadmin/security/filter/JwtFilter.java b/tht-admin/src/main/java/com/tht/thtadmin/security/filter/JwtFilter.java index c2f45337..1afd6fbe 100644 --- a/tht-admin/src/main/java/com/tht/thtadmin/security/filter/JwtFilter.java +++ b/tht-admin/src/main/java/com/tht/thtadmin/security/filter/JwtFilter.java @@ -7,6 +7,7 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpMethod; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; @@ -30,7 +31,7 @@ public JwtFilter(TokenProvider tokenProvider, @Value("${jwt.auth-header-name}") @Override protected void doFilterInternal(final HttpServletRequest request, - final HttpServletResponse response, final FilterChain filterChain) + final HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException { final String token = resolveToken(request); @@ -42,8 +43,7 @@ protected void doFilterInternal(final HttpServletRequest request, securityContext.setAuthentication(auth); log.debug("Security Context에 '{}' 인증 정보를 저장했습니다, uri: {}", auth.getName(), requestURI); - } - else { + } else { log.debug("유효한 JWT 토큰이 없습니다, uri: {}", requestURI); } diff --git a/tht-admin/src/main/java/com/tht/thtadmin/ui/user/UserManageUseCase.java b/tht-admin/src/main/java/com/tht/thtadmin/ui/user/UserManageUseCase.java index 3241ae8d..c765d681 100644 --- a/tht-admin/src/main/java/com/tht/thtadmin/ui/user/UserManageUseCase.java +++ b/tht-admin/src/main/java/com/tht/thtadmin/ui/user/UserManageUseCase.java @@ -2,11 +2,11 @@ import com.tht.domain.entity.block.UserBlockService; import com.tht.domain.entity.block.dto.UserBlockDto; -import com.tht.domain.entity.report.dto.UserReportDto; -import com.tht.domain.entity.user.User; -import com.tht.domain.entity.user.service.dto.UserDetailDto; import com.tht.domain.entity.report.UserReportService; +import com.tht.domain.entity.report.dto.UserReportDto; import com.tht.domain.entity.user.service.UserService; +import com.tht.domain.entity.user.service.dto.UserDetailDto; +import com.tht.domain.entity.user.service.dto.UserListDto; import com.tht.domain.entity.user.service.dto.WithDrawUserDto; import com.tht.thtadmin.ui.user.response.*; import lombok.RequiredArgsConstructor; @@ -29,16 +29,18 @@ public class UserManageUseCase { public Page getUserList(final String search, final Pageable pageable) { - final Page pageResult = userService.getSimpleUserPageList(search, pageable); + final Page pageResult = userService.getSimpleUserPageList(search, pageable); final List responses = pageResult .getContent() .stream() - .map(user -> UserSimpleListResponse.of( - user.getUsername(), - user.getUserUuid(), - user.getCreatedAt(), - user.getState()) + .map(dto -> UserSimpleListResponse.of( + dto.username(), + dto.profilePhotoUrl(), + dto.userUuid(), + dto.createdAt(), + dto.userSate() + ) ) .toList(); diff --git a/tht-admin/src/main/java/com/tht/thtadmin/ui/user/response/UserSimpleListResponse.java b/tht-admin/src/main/java/com/tht/thtadmin/ui/user/response/UserSimpleListResponse.java index 20c82c1d..e0c02004 100644 --- a/tht-admin/src/main/java/com/tht/thtadmin/ui/user/response/UserSimpleListResponse.java +++ b/tht-admin/src/main/java/com/tht/thtadmin/ui/user/response/UserSimpleListResponse.java @@ -1,20 +1,17 @@ package com.tht.thtadmin.ui.user.response; import com.tht.enums.EntityState; -import com.tht.thtcommonutils.utils.CustomDateFormatUtils; - -import java.time.LocalDateTime; public record UserSimpleListResponse( String username, + String profilePhotoUrl, String userUuid, String createdAt, EntityState userSate ) { - public static UserSimpleListResponse of(final String username, final String uuid, final LocalDateTime createdAt, final EntityState userSate) { + public static UserSimpleListResponse of(final String username, final String profile, final String uuid, final String createdAt, final EntityState userSate) { - final String convertCreateAt = createdAt.format(CustomDateFormatUtils.getDateTimeInstance()); - return new UserSimpleListResponse(username, uuid, convertCreateAt, userSate); + return new UserSimpleListResponse(username, profile, uuid, createdAt, userSate); } } diff --git a/tht-admin/src/main/resources/static/.gitkeep b/tht-admin/src/main/resources/static/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/tht-admin/src/test/java/com/tht/thtadmin/docs/UserManageDocs.java b/tht-admin/src/test/java/com/tht/thtadmin/docs/UserManageDocs.java index c929d2ae..46aa7d64 100644 --- a/tht-admin/src/test/java/com/tht/thtadmin/docs/UserManageDocs.java +++ b/tht-admin/src/test/java/com/tht/thtadmin/docs/UserManageDocs.java @@ -75,6 +75,7 @@ void getUsers() throws Exception { .responseFields( getPagingFieldDescriptors(List.of( fieldWithPath("content[].username").type(JsonFieldType.STRING).description("유저 이름"), + fieldWithPath("content[].profilePhotoUrl").type(JsonFieldType.STRING).description("유저 프로필 사진 url"), fieldWithPath("content[].userUuid").type(JsonFieldType.STRING).description("유저 고유 번호"), fieldWithPath("content[].createdAt").type(JsonFieldType.STRING).description("생성 시간"), fieldWithPath("content[].userSate").type(JsonFieldType.STRING).description("유저 활동 상태") diff --git a/tht-admin/src/test/java/com/tht/thtadmin/fixture/user/UserSimpleListResponseFixture.java b/tht-admin/src/test/java/com/tht/thtadmin/fixture/user/UserSimpleListResponseFixture.java index a41b026b..426e9775 100644 --- a/tht-admin/src/test/java/com/tht/thtadmin/fixture/user/UserSimpleListResponseFixture.java +++ b/tht-admin/src/test/java/com/tht/thtadmin/fixture/user/UserSimpleListResponseFixture.java @@ -9,11 +9,12 @@ public class UserSimpleListResponseFixture { private static final String username = "유저 닉네밈"; + private static final String profileUrl = "유저 프로필 사진"; private static final String uuid = "user uuid"; private static final String createdAt = LocalDateTime.now().format(CustomDateFormatUtils.getDateTimeInstance()); private static final EntityState sate = EntityState.ACTIVE; public static UserSimpleListResponse make() { - return new UserSimpleListResponse(username, uuid, createdAt, sate); + return new UserSimpleListResponse(username, profileUrl, uuid, createdAt, sate); } } diff --git a/tht-core/domain/src/main/java/com/tht/domain/entity/user/mapper/UserListMapper.java b/tht-core/domain/src/main/java/com/tht/domain/entity/user/mapper/UserListMapper.java new file mode 100644 index 00000000..1814eb64 --- /dev/null +++ b/tht-core/domain/src/main/java/com/tht/domain/entity/user/mapper/UserListMapper.java @@ -0,0 +1,19 @@ +package com.tht.domain.entity.user.mapper; + +import com.querydsl.core.annotations.QueryProjection; +import com.tht.enums.EntityState; + +import java.time.LocalDateTime; + +public record UserListMapper( + String username, + String profilePhotoUrl, + String userUuid, + LocalDateTime createdAt, + EntityState userSate +) { + + @QueryProjection + public UserListMapper { + } +} diff --git a/tht-core/domain/src/main/java/com/tht/domain/entity/user/repository/querydsl/UserCustomRepository.java b/tht-core/domain/src/main/java/com/tht/domain/entity/user/repository/querydsl/UserCustomRepository.java index 5b0abd37..302101c1 100644 --- a/tht-core/domain/src/main/java/com/tht/domain/entity/user/repository/querydsl/UserCustomRepository.java +++ b/tht-core/domain/src/main/java/com/tht/domain/entity/user/repository/querydsl/UserCustomRepository.java @@ -1,6 +1,6 @@ package com.tht.domain.entity.user.repository.querydsl; -import com.tht.domain.entity.user.User; +import com.tht.domain.entity.user.mapper.UserListMapper; import com.tht.domain.entity.user.repository.querydsl.mapper.UserDetailMapper; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -9,7 +9,7 @@ public interface UserCustomRepository { - Page getUserListForPage(final String search, final Pageable pageable); + Page getUserListForPage(final String search, final Pageable pageable); List getDetailInfo(final String userUuid); } diff --git a/tht-core/domain/src/main/java/com/tht/domain/entity/user/repository/querydsl/UserCustomRepositoryImpl.java b/tht-core/domain/src/main/java/com/tht/domain/entity/user/repository/querydsl/UserCustomRepositoryImpl.java index c2410aec..a840b28b 100644 --- a/tht-core/domain/src/main/java/com/tht/domain/entity/user/repository/querydsl/UserCustomRepositoryImpl.java +++ b/tht-core/domain/src/main/java/com/tht/domain/entity/user/repository/querydsl/UserCustomRepositoryImpl.java @@ -5,6 +5,8 @@ import com.tht.domain.entity.idealtype.QIdealType; import com.tht.domain.entity.interesst.QInterest; import com.tht.domain.entity.user.*; +import com.tht.domain.entity.user.mapper.QUserListMapper; +import com.tht.domain.entity.user.mapper.UserListMapper; import com.tht.domain.entity.user.repository.querydsl.mapper.QUserDetailMapper; import com.tht.domain.entity.user.repository.querydsl.mapper.UserDetailMapper; import lombok.RequiredArgsConstructor; @@ -31,10 +33,21 @@ public class UserCustomRepositoryImpl implements UserCustomRepository{ private final JPAQueryFactory queryFactory; @Override - public Page getUserListForPage(final String search, final Pageable pageable) { + public Page getUserListForPage(final String search, final Pageable pageable) { - final List results = queryFactory - .selectFrom(user) + final int profile = 1; + + final List results = queryFactory + .select(new QUserListMapper( + user.username, + userProfilePhoto.url, + user.userUuid, + user.createdAt, + user.state + )) + .from(user) + .innerJoin(userProfilePhoto) + .on(userProfilePhoto.userUuid.eq(user.userUuid).and(userProfilePhoto.priority.eq(profile))) .where(searchText(search)) .orderBy(user.createdAt.desc()) .offset(pageable.getOffset()) diff --git a/tht-core/domain/src/main/java/com/tht/domain/entity/user/service/UserService.java b/tht-core/domain/src/main/java/com/tht/domain/entity/user/service/UserService.java index 8965a2c3..56d10c0b 100644 --- a/tht-core/domain/src/main/java/com/tht/domain/entity/user/service/UserService.java +++ b/tht-core/domain/src/main/java/com/tht/domain/entity/user/service/UserService.java @@ -3,11 +3,13 @@ import com.tht.domain.entity.user.User; import com.tht.domain.entity.user.UserWithDrawLog; import com.tht.domain.entity.user.exception.UserCustomException; +import com.tht.domain.entity.user.mapper.UserListMapper; import com.tht.domain.entity.user.repository.UserRepository; import com.tht.domain.entity.user.repository.UserWithDrawLogRepository; import com.tht.domain.entity.user.repository.querydsl.mapper.UserDetailMapper; import com.tht.domain.entity.user.repository.querydsl.mapper.UserWithDrawLogMapper; import com.tht.domain.entity.user.service.dto.UserDetailDto; +import com.tht.domain.entity.user.service.dto.UserListDto; import com.tht.domain.entity.user.service.dto.WithDrawUserDto; import com.tht.domain.exception.EntityStateException; import com.tht.enums.EntityState; @@ -123,8 +125,10 @@ public void logout(final User user) { save(user); } - public Page getSimpleUserPageList(final String search, final Pageable pageable) { - return userRepository.getUserListForPage(search, pageable); + public Page getSimpleUserPageList(final String search, final Pageable pageable) { + final Page userListForPage = userRepository.getUserListForPage(search, pageable); + final List result = userListForPage.getContent().stream().map(UserListDto::ofMapper).toList(); + return new PageImpl<>(result, pageable, userListForPage.getTotalElements()); } public UserDetailDto getDetailForAdmin(final String userUuid) { diff --git a/tht-core/domain/src/main/java/com/tht/domain/entity/user/service/dto/UserListDto.java b/tht-core/domain/src/main/java/com/tht/domain/entity/user/service/dto/UserListDto.java new file mode 100644 index 00000000..6d9e3892 --- /dev/null +++ b/tht-core/domain/src/main/java/com/tht/domain/entity/user/service/dto/UserListDto.java @@ -0,0 +1,23 @@ +package com.tht.domain.entity.user.service.dto; + +import com.tht.domain.entity.user.mapper.UserListMapper; +import com.tht.enums.EntityState; +import com.tht.thtcommonutils.utils.CustomDateFormatUtils; + +public record UserListDto( + String username, + String profilePhotoUrl, + String userUuid, + String createdAt, + EntityState userSate +) { + public static UserListDto ofMapper(final UserListMapper mapper) { + return new UserListDto( + mapper.username(), + mapper.profilePhotoUrl(), + mapper.userUuid(), + mapper.createdAt().format(CustomDateFormatUtils.getDateTimeInstance()), + mapper.userSate() + ); + } +}