Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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 @@ -40,13 +40,18 @@ public Optional<ClubDetailReadModel> findClubDetailById(Long id) {
return clubRepository.findClubDetailById(id);
}

@Override
public List<ClubReadModel> findClubReadModelsByIds(List<Long> ids) {
return clubRepository.findClubReadModelsByIds(ids);
}

@Override
public Optional<Club> findClubById(Long id) {
return clubRepository.findById(id);
}

@Override
public List<Long> findSubscribedClubIds(
public List<Long> findSubscribedClubIdsByRootUserIdAndClubIds(
List<Long> clubIds,
Long rootUserId
) {
Expand All @@ -57,6 +62,10 @@ public List<Long> findSubscribedClubIds(
return clubSubscribeRepository.findByClubIdInAndRootUserId(clubIds, rootUserId);
}

@Override
public List<Long> findSubscribedClubIdsByRootUserId(Long rootUserId) {
return clubSubscribeRepository.findClubIdsByRootUserId(rootUserId);
}

@Override
public Long countSubscribers(Long clubId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public interface ClubQueryRepository {

List<ClubReadModel> searchClubs(ClubCategory category, List<ClubDivision> divisions);

List<ClubReadModel> findClubReadModelsByIds(List<Long> ids);

Optional<ClubDetailReadModel> findClubDetailById(Long id);

List<Club> findClubsBetweenDates(LocalDateTime start, LocalDateTime end);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,25 @@ public List<ClubReadModel> searchClubs(
.fetch();
}

@Override
@Transactional(readOnly = true)
public List<ClubReadModel> findClubReadModelsByIds(List<Long> ids) {
return queryFactory
.select(new QClubReadModel(
club.id,
club.name,
club.summary,
club.iconImagePath,
club.category,
club.division,
club.recruitStartAt,
club.recruitEndAt
))
.from(club)
.where(club.id.in(ids))
.fetch();
}

@Override
@Transactional(readOnly = true)
public Optional<ClubDetailReadModel> findClubDetailById(Long id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,11 @@ SELECT cs.club.id AS clubId, COUNT(cs) AS subscriberCount
and cs.rootUser.id = :rootUserId
""")
List<Long> findByClubIdInAndRootUserId(List<Long> clubIds, Long rootUserId);

@Query("""
select cs.club.id
from ClubSubscribe cs
where cs.rootUser.id = :rootUserId
""")
List<Long> findClubIdsByRootUserId(Long rootUserId);
}
Comment thread
jiyun921 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.kustacks.kuring.club.application.port.in.dto.ClubDivisionResult;
import com.kustacks.kuring.club.application.port.in.dto.ClubListCommand;
import com.kustacks.kuring.club.application.port.in.dto.ClubListResult;
import com.kustacks.kuring.club.application.port.in.dto.SubscribedClubListCommand;

import java.util.List;

Expand All @@ -13,6 +14,7 @@ public interface ClubQueryUseCase {

ClubListResult getClubs(ClubListCommand command);

ClubDetailResult getClubDetail(ClubDetailCommand command);
ClubListResult getSubscribedClubs(SubscribedClubListCommand command);

ClubDetailResult getClubDetail(ClubDetailCommand command);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.kustacks.kuring.club.application.port.in.dto;

public record SubscribedClubListCommand(
String email
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public interface ClubQueryPort {

Optional<ClubDetailReadModel> findClubDetailById(Long id);

List<ClubReadModel> findClubReadModelsByIds(List<Long> ids);

List<ClubReadModel> searchClubs(ClubCategory category, List<ClubDivision> divisions);

List<Club> findClubsBetweenDates(LocalDateTime start, LocalDateTime end);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ public interface ClubSubscriptionQueryPort {

Long countSubscriptions(Long rootUserId);

List<Long> findSubscribedClubIds(List<Long> clubIds, Long rootUserId);
List<Long> findSubscribedClubIdsByRootUserIdAndClubIds(List<Long> clubIds, Long rootUserId);

List<Long> findSubscribedClubIdsByRootUserId(Long rootUserId);

Long countSubscribers(Long clubId);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class ClubCommandService implements ClubSubscriptionUseCase {
private final ServerProperties serverProperties;
private final ClubQueryPort clubQueryPort;
private final ClubSubscriptionCommandPort clubSubscriptionCommandPort;
private final ClubSubscriptionQueryPort countSubscriptionsQueryPort;
private final ClubSubscriptionQueryPort clubSubscriptionQueryPort;
private final RootUserQueryPort rootUserQueryPort;
private final UserQueryPort userQueryPort;
private final UserEventPort userEventPort;
Expand All @@ -46,7 +46,7 @@ public long addSubscription(ClubSubscriptionCommand command) {
clubSubscriptionCommandPort.saveSubscription(rootUser, club);
subscribeAllLoggedInDevices(rootUser.getId(), makeTopic(club));

return countSubscriptionsQueryPort.countSubscriptions(rootUser.getId());
return clubSubscriptionQueryPort.countSubscriptions(rootUser.getId());
}

@Override
Expand All @@ -60,11 +60,11 @@ public long removeSubscription(ClubSubscriptionCommand command) {
clubSubscriptionCommandPort.deleteSubscription(rootUser, club);
unsubscribeAllLoggedInDevices(rootUser.getId(), makeTopic(club));

return countSubscriptionsQueryPort.countSubscriptions(rootUser.getId());
return clubSubscriptionQueryPort.countSubscriptions(rootUser.getId());
}

private boolean isAlreadySubscription(RootUser rootUser, Club club) {
return countSubscriptionsQueryPort.existsSubscription(rootUser.getId(), club.getId());
return clubSubscriptionQueryPort.existsSubscription(rootUser.getId(), club.getId());
}

private RootUser findRootUserByEmail(String email) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.kustacks.kuring.club.application.port.in.dto.ClubItemResult;
import com.kustacks.kuring.club.application.port.in.dto.ClubListCommand;
import com.kustacks.kuring.club.application.port.in.dto.ClubListResult;
import com.kustacks.kuring.club.application.port.in.dto.SubscribedClubListCommand;
import com.kustacks.kuring.club.application.port.out.ClubQueryPort;
import com.kustacks.kuring.club.application.port.out.ClubSubscriptionQueryPort;
import com.kustacks.kuring.club.application.port.out.dto.ClubDetailReadModel;
Expand All @@ -15,7 +16,9 @@
import com.kustacks.kuring.club.domain.ClubDivision;
import com.kustacks.kuring.club.domain.ClubRecruitmentStatus;
import com.kustacks.kuring.common.annotation.UseCase;
import com.kustacks.kuring.common.exception.InvalidStateException;
import com.kustacks.kuring.common.exception.NotFoundException;
import com.kustacks.kuring.common.exception.code.ErrorCode;
import com.kustacks.kuring.storage.application.port.out.StoragePort;
import com.kustacks.kuring.user.application.port.out.RootUserQueryPort;
import com.kustacks.kuring.user.domain.RootUser;
Expand Down Expand Up @@ -74,14 +77,7 @@ public ClubListResult getClubs(ClubListCommand command) {

Map<Long, Boolean> subscribedMap = getSubscribedMap(clubIds, rootUser);

List<ClubItemResult> clubItemResults =
clubReadModels.stream()
.map(r -> convertClubItemResult(
r,
subscribedMap.getOrDefault(r.getId(), false),
subscriberCountMap.getOrDefault(r.getId(), 0L)
))
.toList();
List<ClubItemResult> clubItemResults = toClubItemResults(clubReadModels, subscribedMap, subscriberCountMap);

return new ClubListResult(clubItemResults);
}
Expand Down Expand Up @@ -116,6 +112,33 @@ public ClubDetailResult getClubDetail(ClubDetailCommand command) {
);
}


@Override
@Transactional(readOnly = true)
public ClubListResult getSubscribedClubs(SubscribedClubListCommand command) {

RootUser rootUser = findRootUserByEmail(command.email());

List<Long> subscribedClubIds = clubSubscriptionQueryPort.findSubscribedClubIdsByRootUserId(rootUser.getId());

if (subscribedClubIds.isEmpty()) {
return new ClubListResult(List.of());
}

List<ClubReadModel> clubReadModels = clubQueryPort.findClubReadModelsByIds(subscribedClubIds);

Map<Long, Long> subscriberCountMap = clubSubscriptionQueryPort.countSubscribersByClubIds(subscribedClubIds);

List<ClubItemResult> clubItemResults = toSubscribedClubItemResults(clubReadModels, subscriberCountMap);

return new ClubListResult(clubItemResults);
}

private RootUser findRootUserByEmail(String email) {
return rootUserQueryPort.findRootUserByEmail(email)
.orElseThrow(() -> new InvalidStateException(ErrorCode.ROOT_USER_NOT_FOUND));
}

private Map<Long, Boolean> getSubscribedMap(List<Long> clubIds, RootUser rootUser) {

if (rootUser == null) {
Expand All @@ -124,7 +147,7 @@ private Map<Long, Boolean> getSubscribedMap(List<Long> clubIds, RootUser rootUse

Long rootUserId = rootUser.getId();

List<Long> subscribedClubIds = clubSubscriptionQueryPort.findSubscribedClubIds(clubIds, rootUserId);
List<Long> subscribedClubIds = clubSubscriptionQueryPort.findSubscribedClubIdsByRootUserIdAndClubIds(clubIds, rootUserId);

return subscribedClubIds.stream()
.collect(Collectors.toMap(id -> id, id -> true));
Expand Down Expand Up @@ -201,4 +224,33 @@ private ClubDetailResult convertClubDetailResult(
location
);
}

private List<ClubItemResult> toClubItemResults(
List<ClubReadModel> clubReadModels,
Map<Long, Boolean> subscribedMap,
Map<Long, Long> subscriberCountMap
) {
return clubReadModels.stream()
.map(r -> convertClubItemResult(
r,
subscribedMap.getOrDefault(r.getId(), false),
subscriberCountMap.getOrDefault(r.getId(), 0L)
))
.toList();
}

private List<ClubItemResult> toSubscribedClubItemResults(
List<ClubReadModel> clubReadModels,
Map<Long, Long> subscriberCountMap
) {
return clubReadModels.stream()
.map(r -> convertClubItemResult(
r,
true,
subscriberCountMap.getOrDefault(r.getId(), 0L)
))
.toList();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public enum ResponseCodeAndMessages {
CLUB_DETAIL_SEARCH_SUCCESS(HttpStatus.OK.value(), "동아리 상세 조회에 성공하였습니다"),
CLUB_SUBSCRIPTION_ADD_SUCCESS(HttpStatus.OK.value(), "구독에 성공했습니다."),
CLUB_SUBSCRIPTION_DELETE_SUCCESS(HttpStatus.OK.value(), "구독이 취소되었습니다."),
CLUB_SUBSCRIPTION_LIST_SEARCH_SUCCESS(HttpStatus.OK.value(), "구독한 동아리 목록 조회에 성공하였습니다"),

/**
* ErrorCodes about auth
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package com.kustacks.kuring.user.adapter.in.web;

import com.kustacks.kuring.auth.authentication.AuthorizationExtractor;

import com.kustacks.kuring.auth.authentication.AuthorizationType;
import com.kustacks.kuring.auth.token.JwtTokenProvider;
import com.kustacks.kuring.club.adapter.in.web.dto.ClubListResponse;
import com.kustacks.kuring.club.application.port.in.ClubQueryUseCase;
import com.kustacks.kuring.club.application.port.in.ClubSubscriptionUseCase;
import com.kustacks.kuring.club.application.port.in.dto.ClubListResult;
import com.kustacks.kuring.club.application.port.in.dto.ClubSubscriptionCommand;
import com.kustacks.kuring.club.application.port.in.dto.SubscribedClubListCommand;
import com.kustacks.kuring.common.annotation.RestWebAdapter;
import com.kustacks.kuring.common.dto.BaseResponse;
import com.kustacks.kuring.common.exception.InvalidStateException;
Expand All @@ -19,6 +24,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand All @@ -27,6 +33,7 @@
import static com.kustacks.kuring.auth.authentication.AuthorizationExtractor.extractAuthorizationValue;
import static com.kustacks.kuring.common.dto.ResponseCodeAndMessages.CLUB_SUBSCRIPTION_ADD_SUCCESS;
import static com.kustacks.kuring.common.dto.ResponseCodeAndMessages.CLUB_SUBSCRIPTION_DELETE_SUCCESS;
import static com.kustacks.kuring.common.dto.ResponseCodeAndMessages.CLUB_SUBSCRIPTION_LIST_SEARCH_SUCCESS;

Comment thread
jiyun921 marked this conversation as resolved.
@Tag(name = "User-Club-Subscription", description = "동아리 구독")
@Validated
Expand All @@ -37,8 +44,9 @@ class UserClubSubscriptionApiV2 {
private static final String FCM_TOKEN_HEADER_KEY = "User-Token";
private static final String JWT_TOKEN_HEADER_KEY = "JWT";


private final ClubSubscriptionUseCase clubSubscriptionUseCase;
private final ClubQueryUseCase clubQueryUseCase;

private final JwtTokenProvider jwtTokenProvider;

@Operation(summary = "사용자 동아리 구독 추가")
Expand Down Expand Up @@ -71,6 +79,25 @@ public ResponseEntity<BaseResponse<UserClubSubscriptionCountResponse>> deleteSub
return ResponseEntity.ok(new BaseResponse<>(CLUB_SUBSCRIPTION_DELETE_SUCCESS, new UserClubSubscriptionCountResponse(subscriptionCount)));
}

@Operation(summary = "구독한 동아리 목록 조회")
@SecurityRequirement(name = FCM_TOKEN_HEADER_KEY)
@SecurityRequirement(name = JWT_TOKEN_HEADER_KEY)
@GetMapping
public ResponseEntity<BaseResponse<ClubListResponse>> getMySubscriptions(
@RequestHeader(FCM_TOKEN_HEADER_KEY) String userToken,
@RequestHeader(AuthorizationExtractor.AUTHORIZATION) String bearerToken
Comment thread
jiyun921 marked this conversation as resolved.
) {
String email = validateJwtAndGetEmail(extractAuthorizationValue(bearerToken, AuthorizationType.BEARER));

SubscribedClubListCommand command = new SubscribedClubListCommand(email);

ClubListResult result = clubQueryUseCase.getSubscribedClubs(command);

ClubListResponse response = ClubListResponse.from(result);

return ResponseEntity.ok(new BaseResponse<>(CLUB_SUBSCRIPTION_LIST_SEARCH_SUCCESS, response));
}

private String validateJwtAndGetEmail(String jwtToken) {
if (!jwtTokenProvider.validateToken(jwtToken)) {
throw new InvalidStateException(ErrorCode.JWT_INVALID_TOKEN);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class UserAcceptanceTest extends IntegrationTestSupport {

public static final String NEW_EMAIL = "new-client@konkuk.ac.kr";
private static final Long TEST_CLUB_ID = 1L;
private static final Long TEST_CLUB_ID_2 = 2L;
private static final Long TEST_CLUB_ID_3 = 3L;

/**
* Given: 가입되지 않은 사용자가 있다
Expand Down Expand Up @@ -543,4 +545,21 @@ void remove_club_subscription_fail_when_not_subscribed() {

실패_응답_확인(response, HttpStatus.BAD_REQUEST);
}

@DisplayName("[v2] 사용자는 구독한 동아리 목록을 조회할 수 있다")
@Test
void lookup_subscribed_clubs() {
// given
String accessToken = 사용자_로그인_되어_있음(USER_FCM_TOKEN, USER_EMAIL, USER_PASSWORD);

동아리_구독_추가_요청(USER_FCM_TOKEN, accessToken, TEST_CLUB_ID);
Comment thread
jiyun921 marked this conversation as resolved.
동아리_구독_추가_요청(USER_FCM_TOKEN, accessToken, TEST_CLUB_ID_2);
동아리_구독_추가_요청(USER_FCM_TOKEN, accessToken, TEST_CLUB_ID_3);

// when
var response = 구독한_동아리_목록_조회_요청(USER_FCM_TOKEN, accessToken);

// then
구독한_동아리_목록_조회_응답_확인(response, List.of(TEST_CLUB_ID, TEST_CLUB_ID_2, TEST_CLUB_ID_3));
}
}
Loading
Loading