From 63fc9db455dcbd8f3e5371fa817609f775e82bfe Mon Sep 17 00:00:00 2001 From: Gyoungmin Kim Date: Sat, 23 Nov 2024 00:10:31 +0900 Subject: [PATCH 1/4] feat: implement get shared member api --- .../kr/mafoo/photo/api/SharedMemberApi.java | 17 +++++++++++++++++ .../controller/SharedMemberController.java | 10 ++++++++++ .../photo/service/SharedMemberService.java | 5 +++++ 3 files changed, 32 insertions(+) diff --git a/photo-service/src/main/java/kr/mafoo/photo/api/SharedMemberApi.java b/photo-service/src/main/java/kr/mafoo/photo/api/SharedMemberApi.java index cff6907..403ac22 100644 --- a/photo-service/src/main/java/kr/mafoo/photo/api/SharedMemberApi.java +++ b/photo-service/src/main/java/kr/mafoo/photo/api/SharedMemberApi.java @@ -19,6 +19,23 @@ @RequestMapping("/v1/shared-members") public interface SharedMemberApi { + @Operation(summary = "공유 사용자 조회", description = "앨범 ID 및 사용자 ID에 해당하는 공유 사용자를 조회합니다.") + @GetMapping + Mono getSharedMemberByAlbumAndMember( + @RequestMemberId + String requestMemberId, + + @ULID + @Parameter(description = "앨범 ID", example = "test_album_id") + @RequestParam + String albumId, + + @ULID + @Parameter(description = "사용자 ID", example = "test_album_id") + @RequestParam + String memberId + ); + @Operation(summary = "공유 사용자 생성", description = "공유 사용자를 생성합니다.") @PostMapping Mono createSharedMember( diff --git a/photo-service/src/main/java/kr/mafoo/photo/controller/SharedMemberController.java b/photo-service/src/main/java/kr/mafoo/photo/controller/SharedMemberController.java index 02ee4df..dc51202 100644 --- a/photo-service/src/main/java/kr/mafoo/photo/controller/SharedMemberController.java +++ b/photo-service/src/main/java/kr/mafoo/photo/controller/SharedMemberController.java @@ -16,6 +16,16 @@ public class SharedMemberController implements SharedMemberApi { private final SharedMemberService sharedMemberService; + @Override + public Mono getSharedMemberByAlbumAndMember( + String requestMemberId, + String albumId, + String memberId + ) { + return sharedMemberService.findSharedMemberByAlbumIdAndMemberId(albumId, memberId) + .map(SharedMemberResponse::fromEntity); + } + @Override public Mono createSharedMember( String memberId, diff --git a/photo-service/src/main/java/kr/mafoo/photo/service/SharedMemberService.java b/photo-service/src/main/java/kr/mafoo/photo/service/SharedMemberService.java index e504297..5cf6370 100644 --- a/photo-service/src/main/java/kr/mafoo/photo/service/SharedMemberService.java +++ b/photo-service/src/main/java/kr/mafoo/photo/service/SharedMemberService.java @@ -20,6 +20,11 @@ public class SharedMemberService { private final AlbumPermissionVerifier albumPermissionVerifier; + @Transactional(readOnly = true) + public Mono findSharedMemberByAlbumIdAndMemberId(String albumId, String requestMemberId) { + return sharedMemberQuery.findByAlbumIdAndMemberId(albumId, requestMemberId); + } + @Transactional public Mono addSharedMember(String albumId, String permissionLevel, String sharingMemberId, String requestMemberId) { return albumPermissionVerifier.verifyOwnershipOrAccessPermission(albumId, requestMemberId, FULL_ACCESS) From 75f74982daf1aefc13768a06a00e70e756ba5a71 Mon Sep 17 00:00:00 2001 From: Gyoungmin Kim Date: Sat, 23 Nov 2024 00:12:22 +0900 Subject: [PATCH 2/4] feat: implement shared member service for user-service --- .../kr/mafoo/user/exception/ErrorCode.java | 2 ++ .../MafooPhotoApiFailedException.java | 7 ++++ .../user/service/SharedMemberService.java | 32 +++++++++++++++++++ .../user/service/dto/SharedMemberDto.java | 10 ++++++ 4 files changed, 51 insertions(+) create mode 100644 user-service/src/main/java/kr/mafoo/user/exception/MafooPhotoApiFailedException.java create mode 100644 user-service/src/main/java/kr/mafoo/user/service/SharedMemberService.java create mode 100644 user-service/src/main/java/kr/mafoo/user/service/dto/SharedMemberDto.java diff --git a/user-service/src/main/java/kr/mafoo/user/exception/ErrorCode.java b/user-service/src/main/java/kr/mafoo/user/exception/ErrorCode.java index 604300c..13a7b34 100644 --- a/user-service/src/main/java/kr/mafoo/user/exception/ErrorCode.java +++ b/user-service/src/main/java/kr/mafoo/user/exception/ErrorCode.java @@ -13,6 +13,8 @@ public enum ErrorCode { TOKEN_TYPE_MISMATCH("AU0001", "토큰 타입이 일치하지 않습니다. (아마 AccessToken?)"), TOKEN_EXPIRED("AU0002", "토큰이 만료되었습니다"), TOKEN_INVALID("AU0003", "토큰이 유효하지 않습니다"), + + MAFOO_PHOTO_API_FAILED("MPE0001", "마푸의 photo-service API 호출이 실패했습니다") ; private final String code; private final String message; diff --git a/user-service/src/main/java/kr/mafoo/user/exception/MafooPhotoApiFailedException.java b/user-service/src/main/java/kr/mafoo/user/exception/MafooPhotoApiFailedException.java new file mode 100644 index 0000000..7d4bb60 --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/exception/MafooPhotoApiFailedException.java @@ -0,0 +1,7 @@ +package kr.mafoo.user.exception; + +public class MafooPhotoApiFailedException extends DomainException { + public MafooPhotoApiFailedException() { + super(ErrorCode.MAFOO_PHOTO_API_FAILED); + } +} diff --git a/user-service/src/main/java/kr/mafoo/user/service/SharedMemberService.java b/user-service/src/main/java/kr/mafoo/user/service/SharedMemberService.java new file mode 100644 index 0000000..a22f7f5 --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/service/SharedMemberService.java @@ -0,0 +1,32 @@ +package kr.mafoo.user.service; +import kr.mafoo.user.exception.MafooPhotoApiFailedException; +import kr.mafoo.user.service.dto.SharedMemberDto; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +@RequiredArgsConstructor +@Service +public class SharedMemberService { + + @Value("${app.gateway.endpoint}") + private String endpoint; + + private final WebClient client; + + public Mono getSharedMemberInfoByAlbumId(String albumId, String memberId, String authorizationToken) { + return client + .get() + .uri(endpoint + "/photo/v1/shared-members?albumId=" + albumId + "&memberId=" + memberId) + .header("Authorization", "Bearer " + authorizationToken) + .retrieve() + .onStatus(status -> status.isSameCodeAs(HttpStatus.BAD_REQUEST), (res) -> Mono.empty()) + .onStatus(status -> !status.is2xxSuccessful(), (res) -> Mono.error(new MafooPhotoApiFailedException())) + .bodyToMono(SharedMemberDto.class); + } + +} + diff --git a/user-service/src/main/java/kr/mafoo/user/service/dto/SharedMemberDto.java b/user-service/src/main/java/kr/mafoo/user/service/dto/SharedMemberDto.java new file mode 100644 index 0000000..3c89f29 --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/service/dto/SharedMemberDto.java @@ -0,0 +1,10 @@ +package kr.mafoo.user.service.dto; + +public record SharedMemberDto( + String sharedMemberId, + String shareStatus, + String permissionLevel, + String albumId, + String memberId +) { +} From 412eae167a40f4b5764a8e873c5ab1da0a387bae Mon Sep 17 00:00:00 2001 From: Gyoungmin Kim Date: Sat, 23 Nov 2024 00:13:20 +0900 Subject: [PATCH 3/4] feat: implement getMemberByKeywordForSharedAlbum in member service --- .../dto/response/MemberDetailResponse.java | 42 +++++++++++++++++++ .../kr/mafoo/user/service/MemberService.java | 9 +++- .../user/service/dto/MemberDetailDto.java | 28 +++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 user-service/src/main/java/kr/mafoo/user/controller/dto/response/MemberDetailResponse.java create mode 100644 user-service/src/main/java/kr/mafoo/user/service/dto/MemberDetailDto.java diff --git a/user-service/src/main/java/kr/mafoo/user/controller/dto/response/MemberDetailResponse.java b/user-service/src/main/java/kr/mafoo/user/controller/dto/response/MemberDetailResponse.java new file mode 100644 index 0000000..a3dd538 --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/controller/dto/response/MemberDetailResponse.java @@ -0,0 +1,42 @@ +package kr.mafoo.user.controller.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import kr.mafoo.user.service.dto.MemberDetailDto; + +@Schema(description = "사용자의 정보 응답") +public record MemberDetailResponse( + @Schema(description = "사용자 ID", example = "test") + String memberId, + + @Schema(description = "사용자 이름", example = "송영민") + String name, + + @Schema(description = "프로필 이미지 URL", example = "https://mafoo.kr/profile.jpg") + String profileImageUrl, + + @Schema(description = "식별 번호", example = "0000") + String serialNumber, + + @Schema(description = "공유 사용자 ID", example = "test_shared_member_id") + String sharedMemberId, + + @Schema(description = "공유 상태", example = "PENDING") + String shareStatus, + + @Schema(description = "권한 단계", example = "FULL_ACCESS") + String permissionLevel +) { + public static MemberDetailResponse fromDto( + MemberDetailDto dto + ) { + return new MemberDetailResponse( + dto.memberId(), + dto.name(), + dto.profileImageUrl(), + dto.serialNumber(), + dto.sharedMemberId(), + dto.shareStatus(), + dto.permissionLevel() + ); + } +} diff --git a/user-service/src/main/java/kr/mafoo/user/service/MemberService.java b/user-service/src/main/java/kr/mafoo/user/service/MemberService.java index 1b30a5e..f0afa27 100644 --- a/user-service/src/main/java/kr/mafoo/user/service/MemberService.java +++ b/user-service/src/main/java/kr/mafoo/user/service/MemberService.java @@ -4,6 +4,7 @@ import kr.mafoo.user.exception.MemberNotFoundException; import kr.mafoo.user.repository.MemberRepository; import kr.mafoo.user.repository.SocialMemberRepository; +import kr.mafoo.user.service.dto.MemberDetailDto; import kr.mafoo.user.util.IdGenerator; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -19,6 +20,7 @@ public class MemberService { private final MemberRepository memberRepository; private final SocialMemberRepository socialMemberRepository; private final SlackService slackService; + private final SharedMemberService sharedMemberService; @Transactional public Mono quitMemberByMemberId(String memberId) { @@ -27,10 +29,13 @@ public Mono quitMemberByMemberId(String memberId) { .then(memberRepository.deleteMemberById(memberId)); } - public Flux getMemberByKeyword(String keyword) { + public Flux getMemberByKeywordForSharedAlbum(String keyword, String albumId, String token) { return memberRepository .findAllByNameContaining(keyword) - .switchIfEmpty(Mono.error(new MemberNotFoundException())); + .switchIfEmpty(Mono.error(new MemberNotFoundException())) + .concatMap(member -> sharedMemberService.getSharedMemberInfoByAlbumId(albumId, member.getId(), token) + .flatMap(sharedMemberDto -> Mono.just(MemberDetailDto.fromSharedMember(member, sharedMemberDto))) + ); } public Mono getMemberByMemberId(String memberId) { diff --git a/user-service/src/main/java/kr/mafoo/user/service/dto/MemberDetailDto.java b/user-service/src/main/java/kr/mafoo/user/service/dto/MemberDetailDto.java new file mode 100644 index 0000000..4a6983f --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/service/dto/MemberDetailDto.java @@ -0,0 +1,28 @@ +package kr.mafoo.user.service.dto; + +import kr.mafoo.user.domain.MemberEntity; + +public record MemberDetailDto( + String memberId, + String name, + String profileImageUrl, + String serialNumber, + String sharedMemberId, + String shareStatus, + String permissionLevel +) { + public static MemberDetailDto fromSharedMember( + MemberEntity memberEntity, + SharedMemberDto sharedMemberDto + ) { + return new MemberDetailDto( + memberEntity.getId(), + memberEntity.getName(), + memberEntity.getProfileImageUrl(), + String.format("%04d", memberEntity.getSerialNumber()), + sharedMemberDto.sharedMemberId(), + sharedMemberDto.shareStatus(), + sharedMemberDto.permissionLevel() + ); + } +} From c2c2c9d93b43c911a1fa68aa63c469de06e6641d Mon Sep 17 00:00:00 2001 From: Gyoungmin Kim Date: Sat, 23 Nov 2024 00:13:46 +0900 Subject: [PATCH 4/4] feat: implement get member related api --- .../main/java/kr/mafoo/user/api/MemberApi.java | 15 ++++++++++++--- .../mafoo/user/controller/MemberController.java | 14 ++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/user-service/src/main/java/kr/mafoo/user/api/MemberApi.java b/user-service/src/main/java/kr/mafoo/user/api/MemberApi.java index 2c0538a..0ee3b6a 100644 --- a/user-service/src/main/java/kr/mafoo/user/api/MemberApi.java +++ b/user-service/src/main/java/kr/mafoo/user/api/MemberApi.java @@ -4,7 +4,9 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import kr.mafoo.user.annotation.RequestMemberId; +import kr.mafoo.user.controller.dto.response.MemberDetailResponse; import kr.mafoo.user.controller.dto.response.MemberResponse; +import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -17,16 +19,23 @@ @Validated @RequestMapping("/v1/members") public interface MemberApi { - @Operation(summary = "사용자 검색", description = "키워드로 사용자를 검색합니다. (이름으로 검색)") + @Operation(summary = "공유 앨범 대상 사용자 검색", description = "키워드로 사용자를 검색합니다. (이름으로 검색)") @GetMapping - Flux getMemberListByName( + Flux getMemberListByNameForSharedAlbum( @RequestMemberId @Parameter(hidden = true) String requesterId, @Parameter(description = "검색어", example = "사람") @RequestParam - String keyword + String keyword, + + @Parameter(description = "앨범 ID", example = "test_album_id") + @RequestParam + String albumId, + + // Authorization Header를 받아올 목적 + ServerHttpRequest serverHttpRequest ); @Operation(summary = "사용자 단건 조회", description = "사용자 단건 정보를 조회합니다.") diff --git a/user-service/src/main/java/kr/mafoo/user/controller/MemberController.java b/user-service/src/main/java/kr/mafoo/user/controller/MemberController.java index 975c8fd..6395730 100644 --- a/user-service/src/main/java/kr/mafoo/user/controller/MemberController.java +++ b/user-service/src/main/java/kr/mafoo/user/controller/MemberController.java @@ -1,9 +1,11 @@ package kr.mafoo.user.controller; import kr.mafoo.user.api.MemberApi; +import kr.mafoo.user.controller.dto.response.MemberDetailResponse; import kr.mafoo.user.controller.dto.response.MemberResponse; import kr.mafoo.user.service.MemberService; import lombok.RequiredArgsConstructor; +import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -14,12 +16,16 @@ public class MemberController implements MemberApi { private final MemberService memberService; @Override - public Flux getMemberListByName( + public Flux getMemberListByNameForSharedAlbum( String requesterId, - String keyword + String keyword, + String albumId, + ServerHttpRequest serverHttpRequest ) { - return memberService.getMemberByKeyword(keyword) - .map(MemberResponse::fromEntity); + String authorizationToken = serverHttpRequest.getHeaders().getFirst("Authorization"); + + return memberService.getMemberByKeywordForSharedAlbum(keyword, albumId, authorizationToken) + .map(MemberDetailResponse::fromDto); } @Override