diff --git a/src/main/java/likelion/MZConnent/api/culture/CultureController.java b/src/main/java/likelion/MZConnent/api/culture/CultureController.java index ce5a3c7..50e0bc3 100644 --- a/src/main/java/likelion/MZConnent/api/culture/CultureController.java +++ b/src/main/java/likelion/MZConnent/api/culture/CultureController.java @@ -1,15 +1,19 @@ package likelion.MZConnent.api.culture; import likelion.MZConnent.dto.culture.response.CultureCategoryResponse; +import likelion.MZConnent.dto.culture.response.CultureDetailResponse; import likelion.MZConnent.dto.culture.response.CulturesSimpleResponse; import likelion.MZConnent.dto.paging.response.PageContentResponse; import likelion.MZConnent.dto.review.response.ReviewsSimpleResponse; +import likelion.MZConnent.jwt.principle.UserPrinciple; import likelion.MZConnent.service.culture.CultureCategoryService; import likelion.MZConnent.service.culture.CultureService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -31,9 +35,27 @@ public ResponseEntity getAllCultureCategories() { // 전체 문화 간단 조회 @GetMapping("/api/cultures") - ResponseEntity getReviewSimpleList(@RequestParam(required = false, defaultValue = "0", value = "category") Long category, @RequestParam(required = false, defaultValue = "0", value = "page") int page ) { + public ResponseEntity getCulturesSimpleList(@RequestParam(required = false, defaultValue = "0", value = "category") Long category, @RequestParam(required = false, defaultValue = "0", value = "page") int page) { PageContentResponse response = cultureService.getCulturesSimpleList(category, page); return ResponseEntity.ok(response); } + + + // 나의 관심 문화 간단 조회 + @GetMapping("/api/cultures/interest") + public ResponseEntity getMyInterestCulturesSimpleList(@RequestParam(required = false, defaultValue = "0", value = "category") Long category, @RequestParam(required = false, defaultValue = "0", value = "page") int page, @AuthenticationPrincipal UserPrinciple userPrinciple) { + + PageContentResponse response = cultureService.getMyIntersetCulturesSimpleList(userPrinciple.getEmail(), category, page); + return ResponseEntity.ok(response); + } + + @GetMapping("/api/cultures/{cultureId}") + public ResponseEntity getCultureDetailInfo(@PathVariable("cultureId") Long cultureId) { + CultureDetailResponse response = cultureService.getCultureDetailInfo(cultureId); + + log.info("문화 정보 조회 성공: {}", response.getCultureId()); + + return ResponseEntity.ok(response); + } } diff --git a/src/main/java/likelion/MZConnent/config/SecurityConfig.java b/src/main/java/likelion/MZConnent/config/SecurityConfig.java index 7550702..0aba1ad 100644 --- a/src/main/java/likelion/MZConnent/config/SecurityConfig.java +++ b/src/main/java/likelion/MZConnent/config/SecurityConfig.java @@ -1,6 +1,5 @@ package likelion.MZConnent.config; -import jakarta.servlet.http.HttpServletRequest; import likelion.MZConnent.jwt.JwtAccessDeniedHandler; import likelion.MZConnent.jwt.JwtAuthenticationEntryPoint; import likelion.MZConnent.jwt.JwtFilter; @@ -31,19 +30,30 @@ public class SecurityConfig { private final JwtAccessDeniedHandler jwtAccessDeniedHandler; private final JwtFilter jwtFilter; - //권한별 url + /** + * 권한별 URI + */ + + // 관리자 권한 private final String[] adminUrl = {"/admin/**"}; - // 아무나 접속 가능한 것 + + // 인증된 사용자만 접근 가능한 URI (permitAllUrl과 겹치는 경우에만 추가 -> 미인증 사용자가 접근하는 URI는 모두 인가가 필요하게 설정되어 있어 추가할 필요 없음) + private final String[] authenticatedUrl = { + "/api/cultures/interest" + }; + + // 아무나 접근 가능한 URI private final String[] permitAllUrl = {"/error", "/api/auth/login", // 회원 - "/api/categories/culture", "/api/cultures", "/api/cultures/*", // 문화 + "/api/categories/culture", "/api/cultures", "/api/cultures/**", // 문화 "/api/reviews", // 후기 "/api/categories/region", "/api/clubs/list", "/api/main", "/swagger", "/swagger-ui.html", "/swagger-ui/**", "/api-docs", "/api-docs/**", "/v3/api-docs/**", //swagger "/api/test" }; - // 로그인 안한 사용자만 접속 가능한 것 + + // 인증되지 않은 사용자만 접근 가능한 URI private final String[] anonymousUrl = { "/api/auth/signup" }; @@ -63,8 +73,9 @@ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws .accessDeniedHandler(jwtAccessDeniedHandler) ) .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) - .authorizeHttpRequests(auth -> auth // 접근 url 권한 관리 + .authorizeHttpRequests(auth -> auth // 접근 uri 권한 관리 .requestMatchers(adminUrl).hasAnyRole("ADMIN") + .requestMatchers(authenticatedUrl).authenticated() .requestMatchers(permitAllUrl).permitAll() .requestMatchers(anonymousUrl).anonymous() .anyRequest().authenticated() // 이 외의 url은 인증받은 사용자만 접근 가능 diff --git a/src/main/java/likelion/MZConnent/domain/club/ClubRole.java b/src/main/java/likelion/MZConnent/domain/club/ClubRole.java index 586b01c..15aecc4 100644 --- a/src/main/java/likelion/MZConnent/domain/club/ClubRole.java +++ b/src/main/java/likelion/MZConnent/domain/club/ClubRole.java @@ -1,5 +1,7 @@ package likelion.MZConnent.domain.club; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -11,10 +13,12 @@ public enum ClubRole { private final String clubRole; + @JsonValue public String getClubRole() { return clubRole; } + @JsonProperty public static ClubRole fromRole(String role) { for (ClubRole cR : ClubRole.values()) { if (cR.getClubRole().equals(role)) { diff --git a/src/main/java/likelion/MZConnent/dto/culture/response/CultureDetailResponse.java b/src/main/java/likelion/MZConnent/dto/culture/response/CultureDetailResponse.java new file mode 100644 index 0000000..ebbb7d8 --- /dev/null +++ b/src/main/java/likelion/MZConnent/dto/culture/response/CultureDetailResponse.java @@ -0,0 +1,90 @@ +package likelion.MZConnent.dto.culture.response; + +import likelion.MZConnent.domain.club.AgeRestriction; +import likelion.MZConnent.domain.club.Club; +import likelion.MZConnent.domain.club.ClubRole; +import likelion.MZConnent.domain.club.GenderRestriction; +import likelion.MZConnent.domain.culture.Culture; +import likelion.MZConnent.dto.review.response.ReviewsSimpleResponse; +import lombok.*; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Getter +@Slf4j +@ToString +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class CultureDetailResponse { + private Long cultureId; + private String name; + private String cultureImageUrl; + private String regionName; + private int interestCount; + private String recommendedMember; + private String content; + private List clubs; + private List reviews; + + @Builder + public CultureDetailResponse(Culture culture) { + this.cultureId = culture.getCultureId(); + this.name = culture.getName(); + this.cultureImageUrl = culture.getCultureImageUrl(); + this.regionName = culture.getRegion().getName(); + this.interestCount = culture.getInterestCount(); + this.recommendedMember = culture.getRecommendedMember(); + this.content = culture.getContent(); + this.clubs = culture.getClubs().stream().filter(club -> + (club.getStatus().equals("OPEN") + )).map(club -> + ClubsSimpleResponse.builder() + .club(club).build()).collect(Collectors.toList()); + this.reviews = culture.getReviews().stream().map( + review -> ReviewsSimpleResponse.builder() + .review(review).build() + ).collect(Collectors.toList()); + } + + + @Getter + @ToString + @NoArgsConstructor(access = AccessLevel.PROTECTED) + static private class ClubsSimpleResponse { + private Long clubId; + private String title; + private String regionName; + private String cultureName; + private String leaderProfileImage; + private LocalDate meetingDate; + private LocalDateTime createdDate; + private GenderRestriction genderRestriction; + private AgeRestriction ageRestriction; + private int currentParticipant; + private int maxParticipant; + + @Builder + public ClubsSimpleResponse(Club club) { + this.clubId = club.getClubId(); + this.title = club.getTitle(); + this.regionName = club.getRegion().getName(); + this.cultureName = club.getCulture().getName(); + this.leaderProfileImage = club.getClubMembers().stream().filter((member) -> + member.getClubRole() == ClubRole.LEADER + ).findFirst().orElseThrow(() -> { + log.info("모임장이 존재하지 않음."); + return new IllegalArgumentException("모임장이 존재하지 않습니다."); + }).getMember().getProfileImageUrl(); + this.meetingDate = club.getMeetingDate(); + this.createdDate = club.getCreatedDate(); + this.genderRestriction = club.getGenderRestriction(); + this.ageRestriction = club.getAgeRestriction(); + this.currentParticipant = club.getCurrentParticipant(); + this.maxParticipant = club.getMaxParticipant(); + } + } +} diff --git a/src/main/java/likelion/MZConnent/dto/review/response/ReviewsSimpleResponse.java b/src/main/java/likelion/MZConnent/dto/review/response/ReviewsSimpleResponse.java index d738635..515c034 100644 --- a/src/main/java/likelion/MZConnent/dto/review/response/ReviewsSimpleResponse.java +++ b/src/main/java/likelion/MZConnent/dto/review/response/ReviewsSimpleResponse.java @@ -9,6 +9,7 @@ @Getter @NoArgsConstructor +@ToString public class ReviewsSimpleResponse { private Long reviewId; private MemberProfileDto reviewer; diff --git a/src/main/java/likelion/MZConnent/repository/culture/CultureRepository.java b/src/main/java/likelion/MZConnent/repository/culture/CultureRepository.java index f5e044b..66fb886 100644 --- a/src/main/java/likelion/MZConnent/repository/culture/CultureRepository.java +++ b/src/main/java/likelion/MZConnent/repository/culture/CultureRepository.java @@ -12,4 +12,7 @@ public interface CultureRepository extends JpaRepository { @Query("SELECT c FROM Culture c WHERE :category = 0 OR c.cultureCategory.id = :category") Page findAllByCultureCategory(@Param("category") Long category, Pageable pageable); + + @Query("SELECT c FROM Culture c JOIN c.cultureInterests ci JOIN ci.member m WHERE (:category = 0 OR c.cultureCategory.id = :category) AND m.email = :email") + Page findByMemberAndCategory(@Param("email") String email, @Param("category") Long category, Pageable pageable); } diff --git a/src/main/java/likelion/MZConnent/service/culture/CultureService.java b/src/main/java/likelion/MZConnent/service/culture/CultureService.java index d390555..8a62dd7 100644 --- a/src/main/java/likelion/MZConnent/service/culture/CultureService.java +++ b/src/main/java/likelion/MZConnent/service/culture/CultureService.java @@ -2,6 +2,7 @@ import likelion.MZConnent.domain.culture.Culture; import likelion.MZConnent.domain.review.Review; +import likelion.MZConnent.dto.culture.response.CultureDetailResponse; import likelion.MZConnent.dto.culture.response.CulturesSimpleResponse; import likelion.MZConnent.dto.paging.response.PageContentResponse; import likelion.MZConnent.dto.review.response.ReviewsSimpleResponse; @@ -14,12 +15,14 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.stream.Collectors; @Service @Slf4j +@Transactional @RequiredArgsConstructor public class CultureService { private final CultureRepository cultureRepository; @@ -42,4 +45,31 @@ public PageContentResponse getCulturesSimpleList(Long cu .build(); } + + public PageContentResponse getMyIntersetCulturesSimpleList(String email, Long cultureCategoryId, int page) { + Pageable pageable = PageRequest.of(page, PAGE_SIZE); + + Page cultures = cultureRepository.findByMemberAndCategory(email, cultureCategoryId, pageable); + + List cultureResponse = cultures.stream().map(culture -> CulturesSimpleResponse.builder() + .culture(culture).build() + ).collect(Collectors.toList()); + + return PageContentResponse.builder() + .content(cultureResponse) + .totalPages(cultures.getTotalPages()) + .totalElements(cultures.getTotalElements()) + .size(pageable.getPageSize()) + .build(); + + } + + public CultureDetailResponse getCultureDetailInfo(Long cultureId) { + Culture culture = cultureRepository.findById(cultureId).orElseThrow(() -> { + log.info("해당 문화가 존재하지 않습니다."); + return new IllegalArgumentException("해당 문화가 존재하지 않습니다."); + }); + return CultureDetailResponse.builder() + .culture(culture).build(); + } }