Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature : 카테고리별 커뮤니티 게시물 조회 API #37

Merged
merged 9 commits into from
Jan 4, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -133,5 +133,34 @@ public ResponseEntity<CommonApiResponse<Void>> deleteCommunity(
return ResponseEntity.ok(CommonApiResponse.onSuccess(null));
}

//학습 유형 별 필터링 api
@Operation(
summary = "커뮤니티 게시글 카테고리별 조회 필터링 API",
description = "카테고리별 커뮤니티 게시글을 조회합니다. 카테고리를 파라미터에 입력하면 해당하는 게시글들 반환. 기타는 파라미터에 기타를 입력하면 됩니다. postTypes에는 글 유형, learningTypes에는 학습 유형을 각각 최대 두개까지 입력할 수 있습니다." +
"postTypes이 회고일지일 경우 회고일지를 반환합니다 understandinglevel,progresslevel도 반환 회고일지 아닌 경우 null",
responses = {
@ApiResponse(
responseCode = "200",
description = "카테고리별 커뮤니티 게시글 조회 성공"
),
@ApiResponse(
responseCode = "404",
description = "해당 카테고리를 찾을 수 없음"
),
@ApiResponse(
responseCode = "500",
description = "서버 에러"
)
}
)
@GetMapping("/filter")
public ResponseEntity<CommonApiResponse<List<CommunityDto.CombinedCategoryResponse>>> getFilteredCommunity(
@RequestParam(required = false) List<String> postTypes,
@RequestParam(required = false) List<String> learningTypes
){
List<CommunityDto.CombinedCategoryResponse> responses = communityService.getFilteredCommunity(postTypes, learningTypes);
return ResponseEntity.ok(CommonApiResponse.onSuccess(responses));
}


}
4 changes: 2 additions & 2 deletions src/main/java/itstime/reflog/community/domain/Community.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ public class Community {

private String content; // 게시글 내용

@ElementCollection
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "community_post_types", joinColumns = @JoinColumn(name = "community_id"))
@Column(name = "post_type")
private List<String> postTypes; // 글 유형 (최대 2개)

@ElementCollection
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "community_learning_types", joinColumns = @JoinColumn(name = "community_id"))
@Column(name = "learning_type")
private List<String> learningTypes; // 학습 유형 (최대 2개)
Expand Down
53 changes: 50 additions & 3 deletions src/main/java/itstime/reflog/community/dto/CommunityDto.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package itstime.reflog.community.dto;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import itstime.reflog.community.domain.Community;
import itstime.reflog.member.domain.Member;
import itstime.reflog.retrospect.domain.Retrospect;
import itstime.reflog.retrospect.domain.StudyType;
import lombok.*;

public class CommunityDto {

Expand All @@ -17,4 +22,46 @@ public static class CommunitySaveOrUpdateRequest {
private List<String> learningTypes;
private List<String> fileUrls;
}

//카테고리 별 필터링 api dto
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class CombinedCategoryResponse {

private String title;
private String content;
private LocalDateTime createdDate; // Retrospect는 LocalDate로 변경 가능
private List<String> postTypes;
private List<String> learningTypes;
private String writer;
private Integer progressLevel; // Retrospect 전용
private Integer understandingLevel; // Retrospect 전용

public static CombinedCategoryResponse fromCommunity(Community community, String writer) {
return CombinedCategoryResponse.builder()
.title(community.getTitle())
.content(community.getContent())
.createdDate(community.getCreatedAt())
.postTypes(community.getPostTypes())
.learningTypes(community.getLearningTypes())
.writer(writer)
.build();
}

public static CombinedCategoryResponse fromRetrospect(Retrospect retrospect, String writer) {
return CombinedCategoryResponse.builder()
.title(retrospect.getTitle())
.createdDate(retrospect.getCreatedDate().atStartOfDay())
.postTypes(List.of("회고일지")) // 단일 값을 리스트로 변환
.learningTypes(retrospect.getStudyTypes().stream()
.map(studyType -> studyType.getType()) // StudyType을 String으로 변환
.collect(Collectors.toList())) //String 리스트로 변환
.progressLevel(retrospect.getProgressLevel())
.understandingLevel(retrospect.getUnderstandingLevel())
.writer(writer)
.build();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
package itstime.reflog.community.repository;

import io.lettuce.core.dynamic.annotation.Param;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;

import itstime.reflog.community.domain.Community;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

public interface CommunityRepository extends JpaRepository<Community, Long> {

@Query("SELECT DISTINCT c FROM Community c JOIN c.learningTypes lt JOIN c.postTypes pt " +
"WHERE (:learningTypes IS NULL AND :postTypes IS NULL) OR (lt IN :learningTypes OR pt IN :postTypes)")
List<Community> findByLearningTypesAndPostTypes(
@Param("postTypes") List<String> postTypes, @Param("learningTypes") List<String> learningTypes);

//기타는 기타:%s에 해당하는 모든 커뮤니티 반환
@Query("SELECT DISTINCT c FROM Community c JOIN c.learningTypes lt JOIN c.postTypes pt WHERE (:typePrefix IS NULL AND :postTypes IS NULL AND :learningType IS NULL) " +
"OR (lt LIKE CONCAT(:typePrefix, '%') OR pt IN :postTypes OR lt = :learningType)")
List<Community> findCommunitiesByLearningTypePrefix(@Param("postTypes") List<String> postTypes, @Param("typePrefix") String typePrefix, @Param("learningType") String learningType);


}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

import itstime.reflog.mypage.domain.MyPage;
import itstime.reflog.mypage.repository.MyPageRepository;
import itstime.reflog.retrospect.domain.Retrospect;
import itstime.reflog.retrospect.repository.RetrospectRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -27,6 +32,8 @@ public class CommunityService {
private final UploadedFileRepository uploadedFileRepository;
private final AmazonS3Manager amazonS3Manager;
private final MemberRepository memberRepository;
private final MyPageRepository myPageRepository;
private final RetrospectRepository retrospectRepository;

@Transactional
public void createCommunity(Long memberId, CommunityDto.CommunitySaveOrUpdateRequest dto) {
Expand Down Expand Up @@ -129,4 +136,51 @@ public void deleteCommunity(Long communityId) {
// 4. Community 삭제
communityRepository.delete(community);
}
//커뮤니티 게시글 필터링
@Transactional
public List<CommunityDto.CombinedCategoryResponse> getFilteredCommunity(List<String> postTypes, List<String> learningTypes) {

List<Community> communities;

//학습유형에 기타가 있는 경우
if (learningTypes != null && learningTypes.contains("기타")) {
String typePrefix = "기타:%";
String remainingLearningType = learningTypes.stream()
.filter(type -> !"기타".equals(type))
.findFirst()
.orElse(null); //나머지 유형이 없는 경우 null

communities = communityRepository.findCommunitiesByLearningTypePrefix(postTypes, typePrefix, remainingLearningType);
} else {
communities = communityRepository.findByLearningTypesAndPostTypes(postTypes, learningTypes);
}

//커뮤니티 response형태로 반환
List<CommunityDto.CombinedCategoryResponse> responses = communities.stream()
.map(community -> {
String nickname = myPageRepository.findByMember(community.getMember())
.map(MyPage::getNickname)
.orElse("닉네임 없음");
return CommunityDto.CombinedCategoryResponse.fromCommunity(community, nickname);
})
.collect(Collectors.toList());

//글 유형에 회고일지가 있는 경우
if (postTypes != null && postTypes.contains("회고일지")) {
List<Retrospect> retrospects = retrospectRepository.findByVisibilityIsTrue();
List<CommunityDto.CombinedCategoryResponse> retrospectResponses = retrospects.stream()
.map(retrospect -> {
String nickname = myPageRepository.findByMember(retrospect.getMember())
.map(MyPage::getNickname)
.orElse("닉네임 없음");
return CommunityDto.CombinedCategoryResponse.fromRetrospect(retrospect, nickname);
})
.collect(Collectors.toList());
responses.addAll(retrospectResponses); // 두 리스트 합치기(회고일지, 커뮤니티)
}

return responses;
}


}
2 changes: 1 addition & 1 deletion src/main/java/itstime/reflog/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public CorsConfigurationSource corsConfigurationSource() {
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(requests -> requests
.requestMatchers("/test", "/swagger-ui/index.html", "/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/v3/api-docs", "/api/v1/todo/**", "/api/v1/learn/**", "/api/v1/plan/**", "/api/v1/weekly-analysis/**","/api/v1/monthly-analysis/**","/api/v1/retrospect/**","/api/v1/mypage/**","/api/v1/notifications/**").permitAll()
.requestMatchers("/test", "/swagger-ui/index.html", "/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**", "/v3/api-docs", "/api/v1/todo/**", "/api/v1/learn/**", "/api/v1/plan/**", "/api/v1/weekly-analysis/**","/api/v1/monthly-analysis/**","/api/v1/retrospect/**","/api/v1/mypage/**","/api/v1/notifications/**","/api/v1/communities/**").permitAll()
.anyRequest().authenticated() // 나머지 URL은 인증 필요
)
// .addFilterBefore(new TokenAuthenticationFilter(jwtTokenProvider()), UsernamePasswordAuthenticationFilter.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
@Repository
public interface MyPageRepository extends JpaRepository<MyPage, Long> {
Optional<MyPage> findByMember(Member member);

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ public interface RetrospectRepository extends JpaRepository<Retrospect, Long> {
@Query("SELECT MAX(r.createdDate) FROM Retrospect r WHERE r.member.id = :memberId")
Optional<LocalDate> findLatestRetrospectDateByMemberId(@Param("memberId") Long memberId);

List<Retrospect> findByMember(Member member);
List<Retrospect> findByMember(Member member);

List<Retrospect> findByVisibilityIsTrue();

//회고일지에 studytype을 조인해 파라미터로 주어진 type과 동일한 회고일지 반환
@Query("SELECT r FROM Retrospect r JOIN r.studyTypes st WHERE st.type = :type AND r.member = :member")
List<Retrospect> findRetrospectsByTypeAndMember(@Param("type") String type, @Param("member") Member member);

//기타는 기타:%s에 해당하는 모든 회고일지 반환
@Query("SELECT r FROM Retrospect r JOIN r.studyTypes st WHERE st.type LIKE :typePrefix AND r.member = :member")
List<Retrospect> findRetrospectsByTypePrefixAndMember(@Param("typePrefix") String typePrefix, @Param("member") Member member);

}
Loading