From d8cf778235b2012044bf80c8f862e21465b6d999 Mon Sep 17 00:00:00 2001 From: Zepelown Date: Sat, 16 Aug 2025 11:31:34 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B0=99=EC=9D=80=20=EB=B6=84=EA=B3=BC?= =?UTF-8?q?=20=EB=AA=A8=EC=A7=91=20=EC=A4=91=20=EB=8F=99=EC=95=84=EB=A6=AC?= =?UTF-8?q?=20=EC=A4=91=EC=97=90=20=EC=B6=94=EC=B2=9C=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../club/payload/dto/ClubDetailedResult.java | 111 +++++++++--------- .../club/repository/ClubSearchRepository.java | 34 ++++++ .../club/service/ClubProfileService.java | 9 +- 3 files changed, 99 insertions(+), 55 deletions(-) diff --git a/backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java b/backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java index 234e235ee..9f1a9214b 100644 --- a/backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java +++ b/backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; + import lombok.Builder; import moadong.club.entity.Club; import moadong.club.entity.ClubRecruitmentInformation; @@ -11,70 +12,72 @@ @Builder public record ClubDetailedResult( - String id, - String name, - String logo, - String cover, - List tags, - String state, - List feeds, - String introduction, - String description, - String presidentName, - String presidentPhoneNumber, - String recruitmentPeriod, - String recruitmentTarget, - String recruitmentStatus, - String externalApplicationUrl, - Map socialLinks, - String category, - String division, - List faqs + String id, + String name, + String logo, + String cover, + List tags, + String state, + List feeds, + String introduction, + String description, + String presidentName, + String presidentPhoneNumber, + String recruitmentPeriod, + String recruitmentTarget, + String recruitmentStatus, + String externalApplicationUrl, + Map socialLinks, + String category, + String division, + List faqs, + List recommendClubs ) { - public static ClubDetailedResult of(Club club) { + public static ClubDetailedResult of(Club club, List recommendClubs) { String period = "미정"; ClubRecruitmentInformation clubRecruitmentInformation = club.getClubRecruitmentInformation(); if (clubRecruitmentInformation.hasRecruitmentPeriod()) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm"); period = clubRecruitmentInformation.getRecruitmentStart().format(formatter) + " ~ " - + clubRecruitmentInformation.getRecruitmentEnd().format(formatter); + + clubRecruitmentInformation.getRecruitmentEnd().format(formatter); } return ClubDetailedResult.builder() - .id(club.getId() == null ? "" : club.getId()) - .name(club.getName() == null ? "" : club.getName()) - .logo(clubRecruitmentInformation.getLogo() == null ? "" - : clubRecruitmentInformation.getLogo()) - .cover(clubRecruitmentInformation.getCover() == null ? "" - : clubRecruitmentInformation.getCover()) - .tags(clubRecruitmentInformation.getTags() == null ? List.of() - : clubRecruitmentInformation.getTags()) - .state(club.getState() == null ? "" : club.getState().getDesc()) - .feeds(clubRecruitmentInformation.getFeedImages() == null ? List.of() - : clubRecruitmentInformation.getFeedImages()) - .category(club.getCategory() == null ? "" : club.getCategory()) - .division(club.getDivision() == null ? "" : club.getDivision()) - .introduction(clubRecruitmentInformation.getIntroduction() == null ? "" - : clubRecruitmentInformation.getIntroduction()) - .description(clubRecruitmentInformation.getDescription() == null ? "" - : clubRecruitmentInformation.getDescription()) - .presidentName(clubRecruitmentInformation.getPresidentName() == null ? "" - : clubRecruitmentInformation.getPresidentName()) - .presidentPhoneNumber( - clubRecruitmentInformation.getPresidentTelephoneNumber() == null ? "" - : clubRecruitmentInformation.getPresidentTelephoneNumber()) - .recruitmentPeriod(period) - .recruitmentTarget(clubRecruitmentInformation.getRecruitmentTarget() == null ? "" - : clubRecruitmentInformation.getRecruitmentTarget()) - .recruitmentStatus(clubRecruitmentInformation.getClubRecruitmentStatus() == null - ? "" : clubRecruitmentInformation.getClubRecruitmentStatus().getDescription()) - .externalApplicationUrl(club.getClubRecruitmentInformation().getExternalApplicationUrl()== null ? "" : + .id(club.getId() == null ? "" : club.getId()) + .name(club.getName() == null ? "" : club.getName()) + .logo(clubRecruitmentInformation.getLogo() == null ? "" + : clubRecruitmentInformation.getLogo()) + .cover(clubRecruitmentInformation.getCover() == null ? "" + : clubRecruitmentInformation.getCover()) + .tags(clubRecruitmentInformation.getTags() == null ? List.of() + : clubRecruitmentInformation.getTags()) + .state(club.getState() == null ? "" : club.getState().getDesc()) + .feeds(clubRecruitmentInformation.getFeedImages() == null ? List.of() + : clubRecruitmentInformation.getFeedImages()) + .category(club.getCategory() == null ? "" : club.getCategory()) + .division(club.getDivision() == null ? "" : club.getDivision()) + .introduction(clubRecruitmentInformation.getIntroduction() == null ? "" + : clubRecruitmentInformation.getIntroduction()) + .description(clubRecruitmentInformation.getDescription() == null ? "" + : clubRecruitmentInformation.getDescription()) + .presidentName(clubRecruitmentInformation.getPresidentName() == null ? "" + : clubRecruitmentInformation.getPresidentName()) + .presidentPhoneNumber( + clubRecruitmentInformation.getPresidentTelephoneNumber() == null ? "" + : clubRecruitmentInformation.getPresidentTelephoneNumber()) + .recruitmentPeriod(period) + .recruitmentTarget(clubRecruitmentInformation.getRecruitmentTarget() == null ? "" + : clubRecruitmentInformation.getRecruitmentTarget()) + .recruitmentStatus(clubRecruitmentInformation.getClubRecruitmentStatus() == null + ? "" : clubRecruitmentInformation.getClubRecruitmentStatus().getDescription()) + .externalApplicationUrl(club.getClubRecruitmentInformation().getExternalApplicationUrl() == null ? "" : club.getClubRecruitmentInformation().getExternalApplicationUrl()) - .socialLinks(club.getSocialLinks() == null ? Map.of() - : club.getSocialLinks()) - .faqs(club.getClubRecruitmentInformation().getFaqs() == null ? List.of() - : club.getClubRecruitmentInformation().getFaqs()) - .build(); + .socialLinks(club.getSocialLinks() == null ? Map.of() + : club.getSocialLinks()) + .faqs(club.getClubRecruitmentInformation().getFaqs() == null ? List.of() + : club.getClubRecruitmentInformation().getFaqs()) + .recommendClubs(recommendClubs) + .build(); } } diff --git a/backend/src/main/java/moadong/club/repository/ClubSearchRepository.java b/backend/src/main/java/moadong/club/repository/ClubSearchRepository.java index e339674e0..453090421 100644 --- a/backend/src/main/java/moadong/club/repository/ClubSearchRepository.java +++ b/backend/src/main/java/moadong/club/repository/ClubSearchRepository.java @@ -65,6 +65,40 @@ public List searchClubsByKeyword(String keyword, String recrui return results.getMappedResults(); } + public List searchRecommendClubs(String category, String excludeClubId) { + + List operations = new ArrayList<>(); + // 모집 상태 & 같은 category & 제외할 club _id 필터 + operations.add(Aggregation.match( + new Criteria() + .and("state").is(ClubState.AVAILABLE.getName()) + .and("category").is(category) + .and("_id").ne(excludeClubId) + .and("recruitmentInformation.clubRecruitmentStatus") + .in( + ClubRecruitmentStatus.ALWAYS.toString(), + ClubRecruitmentStatus.OPEN.toString(), + ClubRecruitmentStatus.UPCOMING.toString() + ) + )); + // 랜덤 추출 (5개) + operations.add(Aggregation.sample(5L)); + // 필요한 필드만 매핑 + operations.add( + Aggregation.project("name", "state", "category", "division") + .and("recruitmentInformation.introduction").as("introduction") + .and("recruitmentInformation.clubRecruitmentStatus").as("recruitmentStatus") + .and(ConditionalOperators.ifNull("$recruitmentInformation.logo").then("")).as("logo") + .and(ConditionalOperators.ifNull("$recruitmentInformation.tags").then("")).as("tags") + ); + + Aggregation aggregation = Aggregation.newAggregation(operations); + AggregationResults results = mongoTemplate.aggregate(aggregation, "clubs", + ClubSearchResult.class); + + return results.getMappedResults(); + } + private Criteria getMatchedCriteria(String recruitmentStatus, String division, String category) { List criteriaList = new ArrayList<>(); diff --git a/backend/src/main/java/moadong/club/service/ClubProfileService.java b/backend/src/main/java/moadong/club/service/ClubProfileService.java index c1086067f..302f5708c 100644 --- a/backend/src/main/java/moadong/club/service/ClubProfileService.java +++ b/backend/src/main/java/moadong/club/service/ClubProfileService.java @@ -3,10 +3,12 @@ import lombok.AllArgsConstructor; import moadong.club.entity.Club; import moadong.club.payload.dto.ClubDetailedResult; +import moadong.club.payload.dto.ClubSearchResult; import moadong.club.payload.request.ClubInfoRequest; import moadong.club.payload.request.ClubRecruitmentInfoUpdateRequest; import moadong.club.payload.response.ClubDetailedResponse; import moadong.club.repository.ClubRepository; +import moadong.club.repository.ClubSearchRepository; import moadong.club.util.RecruitmentStateCalculator; import moadong.global.exception.ErrorCode; import moadong.global.exception.RestApiException; @@ -15,11 +17,14 @@ import org.bson.types.ObjectId; import org.springframework.stereotype.Service; +import java.util.List; + @Service @AllArgsConstructor public class ClubProfileService { private final ClubRepository clubRepository; + private final ClubSearchRepository clubSearchRepository; public void updateClubInfo(ClubInfoRequest request, CustomUserDetails user) { Club club = clubRepository.findClubByUserId(user.getId()) @@ -47,8 +52,10 @@ public ClubDetailedResponse getClubDetail(String clubId) { Club club = clubRepository.findClubById(objectId) .orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND)); + List clubSearchResults = clubSearchRepository.searchRecommendClubs(club.getCategory(), clubId); + ClubDetailedResult clubDetailedResult = ClubDetailedResult.of( - club + club,clubSearchResults ); return new ClubDetailedResponse(clubDetailedResult); }