Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ public interface ClubApi {
- isRecruiting가 true일 경우, 모집 중인 동아리만 조회하며 모집일(마감일)이 빠른 순으로 정렬됩니다.
- isRecruiting가 false일 경우, 전체 동아리를 조회하되 모집 중인 동아리를 먼저 보여줍니다.
- status은 BEFORE(모집 전), ONGOING(모집 중), CLOSED(모집 마감)으로 반환됩니다.
- isAlwaysRecruiting는 상시 모집 여부입니다.
- applicationDeadline는 모집 마감일(endDate)이며, 모집 공고가 없거나 상시 모집이면 null로 반환됩니다.
- isPendingApproval은 가입 승인 대기 중 여부이며, 지원 내역이 존재하지만 아직 멤버가 아닌 경우 true로 반환됩니다.
""")
@GetMapping
ResponseEntity<ClubsResponse> getClubs(
Expand Down
26 changes: 24 additions & 2 deletions src/main/java/gg/agit/konect/domain/club/dto/ClubsResponse.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package gg.agit.konect.domain.club.dto;

import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED;

import java.time.LocalDate;
import java.util.List;
import java.util.Set;

import org.springframework.data.domain.Page;

import com.fasterxml.jackson.annotation.JsonFormat;

import gg.agit.konect.domain.club.enums.RecruitmentStatus;
import gg.agit.konect.domain.club.model.ClubSummaryInfo;
import io.swagger.v3.oas.annotations.media.Schema;
Expand Down Expand Up @@ -45,30 +50,47 @@ public record InnerClubResponse(
@Schema(description = "동아리 모집 상태", example = "ONGOING", requiredMode = REQUIRED)
RecruitmentStatus status,

@Schema(description = "가입 승인 대기 중 여부", example = "false", requiredMode = REQUIRED)
Boolean isPendingApproval,

@Schema(description = "상시 모집 여부", example = "false", requiredMode = REQUIRED)
Boolean isAlwaysRecruiting,

@Schema(description = "지원 마감일(상시 모집이거나 모집 공고가 없으면 null)", example = "2025.12.31", requiredMode = NOT_REQUIRED)
@JsonFormat(pattern = "yyyy.MM.dd")
LocalDate applicationDeadline,

@Schema(description = "동아리 태그 리스트", example = "[\"IT\", \"프로그래밍\"]", requiredMode = REQUIRED)
List<String> tags
) {
public static InnerClubResponse from(ClubSummaryInfo clubSummaryInfo) {
public static InnerClubResponse from(ClubSummaryInfo clubSummaryInfo, boolean isPendingApproval) {
return new InnerClubResponse(
clubSummaryInfo.id(),
clubSummaryInfo.name(),
clubSummaryInfo.imageUrl(),
clubSummaryInfo.categoryName(),
clubSummaryInfo.description(),
clubSummaryInfo.status(),
isPendingApproval,
clubSummaryInfo.isAlwaysRecruiting(),
clubSummaryInfo.applicationDeadline(),
clubSummaryInfo.tags()
);
}
}

public static ClubsResponse of(Page<ClubSummaryInfo> page) {
return of(page, Set.of());
}

public static ClubsResponse of(Page<ClubSummaryInfo> page, Set<Integer> pendingApprovalClubIds) {
return new ClubsResponse(
page.getTotalElements(),
page.getNumberOfElements(),
page.getTotalPages(),
page.getNumber() + 1,
page.stream()
.map(InnerClubResponse::from)
.map(club -> InnerClubResponse.from(club, pendingApprovalClubIds.contains(club.id())))
.toList()
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package gg.agit.konect.domain.club.model;

import java.time.LocalDate;
import java.util.List;

import gg.agit.konect.domain.club.enums.RecruitmentStatus;
Expand All @@ -11,6 +12,8 @@ public record ClubSummaryInfo(
String categoryName,
String description,
RecruitmentStatus status,
Boolean isAlwaysRecruiting,
LocalDate applicationDeadline,
List<String> tags
) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ public interface ClubApplyRepository extends Repository<ClubApply, Integer> {

boolean existsByClubIdAndUserId(Integer clubId, Integer userId);

@Query("""
SELECT ca.club.id
FROM ClubApply ca
WHERE ca.user.id = :userId
AND ca.club.id IN :clubIds
""")
List<Integer> findClubIdsByUserIdAndClubIdIn(
@Param("userId") Integer userId,
@Param("clubIds") List<Integer> clubIds
);

ClubApply save(ClubApply clubApply);

void deleteByUserId(Integer userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,17 @@ default ClubMember getByClubIdAndUserId(Integer clubId, Integer userId) {

boolean existsByClubIdAndUserId(Integer clubId, Integer userId);

@Query("""
SELECT cm.id.clubId
FROM ClubMember cm
WHERE cm.id.userId = :userId
AND cm.id.clubId IN :clubIds
""")
List<Integer> findClubIdsByUserIdAndClubIdIn(
@Param("userId") Integer userId,
@Param("clubIds") List<Integer> clubIds
);

List<ClubMember> findByUserId(Integer userId);

@Query("""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,21 @@ private List<ClubSummaryInfo> convertToSummaryInfo(List<Club> clubs, Map<Integer
ClubRecruitment recruitment = club.getClubRecruitment();
RecruitmentStatus status = RecruitmentStatus.of(recruitment);

boolean isAlwaysRecruiting = recruitment != null
&& Boolean.TRUE.equals(recruitment.getIsAlwaysRecruiting());
LocalDate applicationDeadline = (recruitment != null && !isAlwaysRecruiting)
? recruitment.getEndDate()
: null;

return new ClubSummaryInfo(
club.getId(),
club.getName(),
club.getImageUrl(),
club.getClubCategory().getDescription(),
club.getDescription(),
status,
isAlwaysRecruiting,
applicationDeadline,
clubTagsMap.getOrDefault(club.getId(), List.of())
);
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -92,7 +93,30 @@ public ClubsResponse getClubs(ClubCondition condition, Integer userId) {
pageable, condition.query(), condition.isRecruiting(), user.getUniversity().getId()
);

return ClubsResponse.of(clubSummaryInfoPage);
Set<Integer> pendingApprovalClubIds = findPendingApprovalClubIds(clubSummaryInfoPage, userId);
return ClubsResponse.of(clubSummaryInfoPage, pendingApprovalClubIds);
}

private Set<Integer> findPendingApprovalClubIds(Page<ClubSummaryInfo> clubSummaryInfoPage, Integer userId) {
List<Integer> clubIds = clubSummaryInfoPage.getContent().stream()
.map(ClubSummaryInfo::id)
.filter(Objects::nonNull)
.toList();

if (clubIds.isEmpty()) {
return Set.of();
}

List<Integer> appliedClubIds = clubApplyRepository.findClubIdsByUserIdAndClubIdIn(userId, clubIds);
if (appliedClubIds.isEmpty()) {
return Set.of();
}

Set<Integer> pendingClubIds = new HashSet<>(appliedClubIds);
List<Integer> memberClubIds = clubMemberRepository.findClubIdsByUserIdAndClubIdIn(userId, clubIds);
pendingClubIds.removeAll(memberClubIds);

return pendingClubIds;
}

public ClubDetailResponse getClubDetail(Integer clubId, Integer userId) {
Expand Down