diff --git a/photo-service/src/main/java/kr/mafoo/photo/controller/AlbumController.java b/photo-service/src/main/java/kr/mafoo/photo/controller/AlbumController.java index 6fe41d12..c573304b 100644 --- a/photo-service/src/main/java/kr/mafoo/photo/controller/AlbumController.java +++ b/photo-service/src/main/java/kr/mafoo/photo/controller/AlbumController.java @@ -50,9 +50,14 @@ public Mono createAlbum( String memberId, AlbumCreateRequest request ){ - if (request.name().equals("SUMONE")) { + if (request.type().equals("SUMONE")) { throw new RuntimeException(); // should not be happened } + if(request.sumoneInviteCode() != null) { + return albumService + .addSumoneAlbum(request.name(), request.type(), memberId, request.sumoneInviteCode()) + .map(AlbumResponse::fromEntity); + } return albumService .addAlbum(request.name(), request.type(), memberId) .map(AlbumResponse::fromEntity); diff --git a/photo-service/src/main/java/kr/mafoo/photo/controller/SumoneController.java b/photo-service/src/main/java/kr/mafoo/photo/controller/SumoneController.java index 261e0e59..21db1d6a 100644 --- a/photo-service/src/main/java/kr/mafoo/photo/controller/SumoneController.java +++ b/photo-service/src/main/java/kr/mafoo/photo/controller/SumoneController.java @@ -12,6 +12,7 @@ import kr.mafoo.photo.domain.enums.AlbumType; import kr.mafoo.photo.domain.enums.BrandType; import kr.mafoo.photo.exception.*; +import kr.mafoo.photo.repository.AlbumRepository; import kr.mafoo.photo.repository.SumoneEventMappingRepository; import kr.mafoo.photo.service.AlbumCommand; import kr.mafoo.photo.service.AlbumQuery; @@ -46,6 +47,7 @@ public class SumoneController { private final RecapService recapService; private final RecapLambdaService recapLambdaService; private final SumoneEventMappingRepository sumoneEventMappingRepository; + private final AlbumRepository albumRepository; @Operation(summary = "통계 api") @GetMapping("/summary") @@ -58,21 +60,10 @@ public Mono getSummary() { @Transactional @Operation(summary = "앨범 생성 api") @PostMapping("/albums") - public Mono createAlbum( - @RequestBody SumoneAlbumCreateRequest request - ) { - if(request.userId() == null || request.userId().isEmpty()) { - return Mono.error(new DomainException(ErrorCode.REQUEST_INPUT_NOT_VALID)); - } - - return sumoneEventMappingRepository - .findById(request.userId()) - .switchIfEmpty(sumoneEventMappingRepository.save(SumoneEventMappingEntity.newEventMember( - request.userId(), - RandomCodeGenerator.generateAlphanumericString(8) - ))) - .then(albumCommand.addAlbum(sumoneAlbumCommonName, "SUMONE", sumoneAlbumCommonMemberId, "SUMONE_" + request.userId()) - .map(SumoneAlbumResponse::fromEntity)); + public Mono createAlbum() { + return albumCommand + .addAlbum(sumoneAlbumCommonName, "SUMONE", sumoneAlbumCommonMemberId, null) + .map(SumoneAlbumResponse::fromEntity); } @Operation(summary = "앨범에 이미지 url 추가") @@ -139,15 +130,26 @@ Mono createRecapVideo( @RequestBody SumoneRecapCreateRequest request ) { - if(request.fileUrls().size() < 1 || request.fileUrls().size() > 10) { + if(request.fileUrls().isEmpty() || request.fileUrls().size() > 10) { return Mono.error(new RecapPhotoCountNotValidException()); } + if(request.userId() == null || request.userId().isEmpty()) { + return Mono.error(new DomainException(ErrorCode.REQUEST_INPUT_NOT_VALID)); + } return albumQuery.findById(albumId).flatMap(album -> { if(album.getType() != AlbumType.SUMONE) { return Mono.error(new AlbumNotFoundException()); } //TODO: add timeout - return recapLambdaService.generateVideo(request.fileUrls()); + + return sumoneEventMappingRepository + .findById(request.userId()) + .switchIfEmpty(sumoneEventMappingRepository.save(SumoneEventMappingEntity.newEventMember( + request.userId(), + RandomCodeGenerator.generateAlphanumericString(8) + ))) + .then(albumRepository.save(album.setExternalId("SUMONE_" + request.userId()))) + .then(recapLambdaService.generateVideo(request.fileUrls())); }); } diff --git a/photo-service/src/main/java/kr/mafoo/photo/controller/dto/request/AlbumCreateRequest.java b/photo-service/src/main/java/kr/mafoo/photo/controller/dto/request/AlbumCreateRequest.java index f308893e..5e08b3f4 100644 --- a/photo-service/src/main/java/kr/mafoo/photo/controller/dto/request/AlbumCreateRequest.java +++ b/photo-service/src/main/java/kr/mafoo/photo/controller/dto/request/AlbumCreateRequest.java @@ -15,6 +15,9 @@ public record AlbumCreateRequest( @MatchEnum(enumClass = AlbumType.class) @Schema(description = "앨범 타입") - String type + String type, + + @Schema(description = "초대 코드") + String sumoneInviteCode ) { } diff --git a/photo-service/src/main/java/kr/mafoo/photo/controller/dto/request/SumoneRecapCreateRequest.java b/photo-service/src/main/java/kr/mafoo/photo/controller/dto/request/SumoneRecapCreateRequest.java index 1a4f571e..150b6959 100644 --- a/photo-service/src/main/java/kr/mafoo/photo/controller/dto/request/SumoneRecapCreateRequest.java +++ b/photo-service/src/main/java/kr/mafoo/photo/controller/dto/request/SumoneRecapCreateRequest.java @@ -10,6 +10,9 @@ public record SumoneRecapCreateRequest( schema = @Schema(description = "파일 URL 목록"), arraySchema = @Schema(example = "[\"file_url_1\", \"file_url_2\", \"file_url_3\"]") ) - List fileUrls + List fileUrls, + + @Schema(description = "썸원 유저ID") + String userId ) { } diff --git a/photo-service/src/main/java/kr/mafoo/photo/domain/AlbumEntity.java b/photo-service/src/main/java/kr/mafoo/photo/domain/AlbumEntity.java index b29e3147..a7c0b60b 100644 --- a/photo-service/src/main/java/kr/mafoo/photo/domain/AlbumEntity.java +++ b/photo-service/src/main/java/kr/mafoo/photo/domain/AlbumEntity.java @@ -99,6 +99,11 @@ public AlbumEntity decreasePhotoCount(int count) { return this; } + public AlbumEntity setExternalId(String externalId) { + this.externalId = externalId; + return this; + } + public static AlbumEntity newAlbum(String albumId, String albumName, AlbumType albumType, String ownerMemberId, String externalId) { AlbumEntity album = new AlbumEntity(); album.albumId = albumId; diff --git a/photo-service/src/main/java/kr/mafoo/photo/repository/AlbumRepository.java b/photo-service/src/main/java/kr/mafoo/photo/repository/AlbumRepository.java index 6bbbe4a5..2183b304 100644 --- a/photo-service/src/main/java/kr/mafoo/photo/repository/AlbumRepository.java +++ b/photo-service/src/main/java/kr/mafoo/photo/repository/AlbumRepository.java @@ -34,4 +34,6 @@ public interface AlbumRepository extends R2dbcRepository { Flux findAllByOwnerMemberIdOrderByAlbumIdDesc(String ownerMemberId, Pageable pageable); Mono countAlbumEntityByType(AlbumType albumType); + + Flux findAllByExternalId(String externalId); } diff --git a/photo-service/src/main/java/kr/mafoo/photo/service/AlbumService.java b/photo-service/src/main/java/kr/mafoo/photo/service/AlbumService.java index 953db874..ef449afb 100644 --- a/photo-service/src/main/java/kr/mafoo/photo/service/AlbumService.java +++ b/photo-service/src/main/java/kr/mafoo/photo/service/AlbumService.java @@ -6,11 +6,17 @@ import java.util.Comparator; import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; + import kr.mafoo.photo.domain.AlbumEntity; import kr.mafoo.photo.domain.enums.AlbumType; +import kr.mafoo.photo.domain.enums.BrandType; import kr.mafoo.photo.exception.AlbumNotFoundException; import kr.mafoo.photo.exception.AlbumOwnerChangeDeniedException; import kr.mafoo.photo.exception.SharedMemberNotFoundException; +import kr.mafoo.photo.repository.AlbumRepository; +import kr.mafoo.photo.repository.PhotoRepository; +import kr.mafoo.photo.repository.SumoneEventMappingRepository; import kr.mafoo.photo.service.dto.AlbumDto; import kr.mafoo.photo.service.dto.SharedAlbumDto; import kr.mafoo.photo.service.dto.SharedMemberDto; @@ -26,6 +32,11 @@ public class AlbumService { private final AlbumQuery albumQuery; private final AlbumCommand albumCommand; + private final AlbumRepository albumRepository; + + private final PhotoCommand photoCommand; + private final PhotoRepository photoRepository; + private final AlbumPermissionVerifier albumPermissionVerifier; @@ -33,46 +44,48 @@ public class AlbumService { private final MemberService memberService; private final SharedMemberCommand sharedMemberCommand; + private final SumoneEventMappingRepository sumoneEventMappingRepository; + @Transactional(readOnly = true) public Flux findAlbumListByMemberId(String memberId, String token) { return Flux.merge( - findOwnedAlbumListByMemberId(memberId), - findSharedAlbumListByMemberId(memberId, token) + findOwnedAlbumListByMemberId(memberId), + findSharedAlbumListByMemberId(memberId, token) ).sort(Comparator.comparing(AlbumDto::createdAt).reversed()); } private Flux findOwnedAlbumListByMemberId(String memberId) { return albumQuery.findByMemberId(memberId) - .onErrorResume(AlbumNotFoundException.class, ex -> Mono.empty()) - .map(AlbumDto::fromOwnedAlbum); + .onErrorResume(AlbumNotFoundException.class, ex -> Mono.empty()) + .map(AlbumDto::fromOwnedAlbum); } private Flux findSharedAlbumListByMemberId(String memberId, String token) { return sharedMemberQuery.findAllByMemberIdWhereStatusNotRejected(memberId) - .onErrorResume(SharedMemberNotFoundException.class, ex -> Mono.empty()) - .concatMap(sharedMember -> albumQuery.findById(sharedMember.getAlbumId()) - .flatMap(album -> memberService.getMemberInfoById(album.getOwnerMemberId(), token) - .flatMap(member -> Mono.just(AlbumDto.fromSharedAlbum(album, sharedMember, member))) - ) - ); + .onErrorResume(SharedMemberNotFoundException.class, ex -> Mono.empty()) + .concatMap(sharedMember -> albumQuery.findById(sharedMember.getAlbumId()) + .flatMap(album -> memberService.getMemberInfoById(album.getOwnerMemberId(), token) + .flatMap(member -> Mono.just(AlbumDto.fromSharedAlbum(album, sharedMember, member))) + ) + ); } @Transactional(readOnly = true) public Mono findAlbumDetailById(String albumId, String requestMemberId, String token) { return albumPermissionVerifier.verifyOwnershipOrAccessPermission(albumId, requestMemberId, VIEW_ACCESS) - .flatMap(album -> sharedMemberQuery.findAllByAlbumIdWhereStatusNotRejected(albumId) - .onErrorResume(SharedMemberNotFoundException.class, ex -> Mono.empty()) - - .flatMap(sharedMember -> memberService.getMemberInfoById(sharedMember.getMemberId(), token) - .map(memberInfo -> SharedMemberDto.fromSharedMember(sharedMember, memberInfo))) - .sort(Comparator.comparing(SharedMemberDto::shareStatus)) - .collectList() - .flatMap(sharedMembers -> memberService.getMemberInfoById(album.getOwnerMemberId(), token) - .map(ownerMember -> SharedAlbumDto.fromSharedAlbum(album, ownerMember, sharedMembers)) - ) - - .switchIfEmpty(Mono.just(SharedAlbumDto.fromOwnedAlbum(album))) - ); + .flatMap(album -> sharedMemberQuery.findAllByAlbumIdWhereStatusNotRejected(albumId) + .onErrorResume(SharedMemberNotFoundException.class, ex -> Mono.empty()) + + .flatMap(sharedMember -> memberService.getMemberInfoById(sharedMember.getMemberId(), token) + .map(memberInfo -> SharedMemberDto.fromSharedMember(sharedMember, memberInfo))) + .sort(Comparator.comparing(SharedMemberDto::shareStatus)) + .collectList() + .flatMap(sharedMembers -> memberService.getMemberInfoById(album.getOwnerMemberId(), token) + .map(ownerMember -> SharedAlbumDto.fromSharedAlbum(album, ownerMember, sharedMembers)) + ) + + .switchIfEmpty(Mono.just(SharedAlbumDto.fromOwnedAlbum(album))) + ); } @Transactional @@ -80,30 +93,57 @@ public Mono addAlbum(String albumName, String albumType, String req return albumCommand.addAlbum(albumName, albumType, requestMemberId, null); } + @Transactional + public Mono addSumoneAlbum(String albumName, String albumType, String requestMemberId, String inviteCode) { + AtomicInteger displayIndex = new AtomicInteger(0); + return albumCommand + .addAlbum(albumName, albumType, requestMemberId, null) + .flatMap(album -> sumoneEventMappingRepository + .findByInviteCode(inviteCode) + .switchIfEmpty(Mono.error(new AlbumNotFoundException())) + .flatMap(sumoneEventMappingEntity -> + sumoneEventMappingRepository.delete(sumoneEventMappingEntity).then(Mono.just(sumoneEventMappingEntity))) + .map(entity -> "SUMONE_" + entity.getId()) + .flatMapMany(albumRepository::findAllByExternalId) + .flatMap(sumoneAlbum -> + photoRepository.findAllByAlbumIdOrderByCreatedAtAsc(sumoneAlbum.getAlbumId()).flatMap(photo -> + photoCommand.addPhoto( + photo.getPhotoUrl(), + BrandType.EXTERNAL, + album.getAlbumId(), + displayIndex.getAndIncrement(), + requestMemberId + ) + ) + ) + .then(albumQuery.findById(album.getAlbumId())) + .flatMap(newAlbum -> albumCommand.increaseAlbumPhotoCount(newAlbum, displayIndex.get()))); + } + @Transactional public Mono modifyAlbumNameAndType(String albumId, String newAlbumName, String newAlbumType, String requestMemberId) { return albumPermissionVerifier.verifyOwnershipOrAccessPermission(albumId, requestMemberId, FULL_ACCESS) - .flatMap(album -> albumCommand.modifyAlbumNameAndType(album, newAlbumName, newAlbumType)); + .flatMap(album -> albumCommand.modifyAlbumNameAndType(album, newAlbumName, newAlbumType)); } @Transactional public Mono modifyAlbumOwnership(String albumId, String newOwnerMemberId, String requestMemberId) { return albumPermissionVerifier.verifyOwnership(albumId, requestMemberId) - .flatMap(album -> sharedMemberQuery.findByAlbumIdAndMemberIdWhereStatusAccepted(albumId, newOwnerMemberId) - .onErrorResume(SharedMemberNotFoundException.class, ex -> - Mono.error(new AlbumOwnerChangeDeniedException()) - ) - .flatMap(sharedMemberCommand::removeSharedMember) - .then(albumCommand.modifyAlbumOwnership(album, newOwnerMemberId)) - .then(sharedMemberCommand.addSharedMember(albumId, String.valueOf(FULL_ACCESS), Optional.of(ACCEPTED), requestMemberId)) - .thenReturn(album) - ); + .flatMap(album -> sharedMemberQuery.findByAlbumIdAndMemberIdWhereStatusAccepted(albumId, newOwnerMemberId) + .onErrorResume(SharedMemberNotFoundException.class, ex -> + Mono.error(new AlbumOwnerChangeDeniedException()) + ) + .flatMap(sharedMemberCommand::removeSharedMember) + .then(albumCommand.modifyAlbumOwnership(album, newOwnerMemberId)) + .then(sharedMemberCommand.addSharedMember(albumId, String.valueOf(FULL_ACCESS), Optional.of(ACCEPTED), requestMemberId)) + .thenReturn(album) + ); } @Transactional public Mono removeAlbum(String albumId, String requestMemberId) { return albumPermissionVerifier.verifyOwnership(albumId, requestMemberId) - .flatMap(albumCommand::removeAlbum); + .flatMap(albumCommand::removeAlbum); } public Mono countAlbumByAlbumType(AlbumType albumType) { diff --git a/user-service/src/main/java/kr/mafoo/user/api/MeApi.java b/user-service/src/main/java/kr/mafoo/user/api/MeApi.java index 1a79f475..ab19ed59 100644 --- a/user-service/src/main/java/kr/mafoo/user/api/MeApi.java +++ b/user-service/src/main/java/kr/mafoo/user/api/MeApi.java @@ -4,11 +4,10 @@ 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.request.ChangeNameRequest; import kr.mafoo.user.controller.dto.response.MemberResponse; 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.RequestMapping; +import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Mono; @Tag(name = "로그인 사용자 정보 API", description = "현재 토큰 주인의 정보를 다루는 API") @@ -26,4 +25,11 @@ Mono getMemberWhoRequested( Mono deleteMemberWhoRequested( @RequestMemberId @Parameter(hidden = true) String memberId ); + + @Operation(summary = "이름 변경", description = "현재 토큰 주인의 이름을 변경합니다.") + @PostMapping("/name") + Mono changeName( + @RequestMemberId @Parameter(hidden = true) String memberId, + @RequestBody ChangeNameRequest name + ); } diff --git a/user-service/src/main/java/kr/mafoo/user/controller/MeController.java b/user-service/src/main/java/kr/mafoo/user/controller/MeController.java index 2cfb6dda..122ce45a 100644 --- a/user-service/src/main/java/kr/mafoo/user/controller/MeController.java +++ b/user-service/src/main/java/kr/mafoo/user/controller/MeController.java @@ -1,6 +1,7 @@ package kr.mafoo.user.controller; import kr.mafoo.user.api.MeApi; +import kr.mafoo.user.controller.dto.request.ChangeNameRequest; import kr.mafoo.user.controller.dto.response.MemberResponse; import kr.mafoo.user.service.MemberService; import lombok.RequiredArgsConstructor; @@ -24,4 +25,11 @@ public Mono deleteMemberWhoRequested(String memberId) { return memberService .quitMemberByMemberId(memberId); } + + @Override + public Mono changeName(String memberId, ChangeNameRequest name) { + return memberService + .changeName(memberId, name.name()) + .map(MemberResponse::fromEntity); + } } diff --git a/user-service/src/main/java/kr/mafoo/user/controller/dto/request/ChangeNameRequest.java b/user-service/src/main/java/kr/mafoo/user/controller/dto/request/ChangeNameRequest.java new file mode 100644 index 00000000..8c57a228 --- /dev/null +++ b/user-service/src/main/java/kr/mafoo/user/controller/dto/request/ChangeNameRequest.java @@ -0,0 +1,10 @@ +package kr.mafoo.user.controller.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "이름 변경 요청") +public record ChangeNameRequest( + @Schema(description = "새 이름", example = "염수연") + String name +) { +} 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 c4e93a93..7afa5070 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 @@ -63,4 +63,16 @@ public Mono createNewMember(String username, String profileImageUr .then(Mono.just(savedMember)) ); } + + @Transactional + public Mono changeName(String memberId, String name) { + return memberRepository + .findById(memberId) + .switchIfEmpty(Mono.error(new MemberNotFoundException())) + .map(member -> { + member.setName(name); + return member; + }) + .flatMap(memberRepository::save); + } }