diff --git a/src/main/java/hanium/englishfairytale/common/files/FileManageService.java b/src/main/java/hanium/englishfairytale/common/files/FileManageService.java index 792af8b..b1a71a2 100644 --- a/src/main/java/hanium/englishfairytale/common/files/FileManageService.java +++ b/src/main/java/hanium/englishfairytale/common/files/FileManageService.java @@ -3,7 +3,6 @@ import hanium.englishfairytale.common.util.ImageFileUtility; import hanium.englishfairytale.exception.RuntimeIOException; import hanium.englishfairytale.exception.code.ErrorCode; -import hanium.englishfairytale.tale.application.dto.TaleImageInfo; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -18,13 +17,13 @@ public class FileManageService { private final FileStore fileStore; @Transactional - public TaleImageInfo uploadTaleImage(MultipartFile image) { + public ImageInfo uploadImage(MultipartFile image) { try { String originalName = image.getOriginalFilename(); String storedName = ImageFileUtility.createObjectNameByUUID(originalName); String imageUrl = fileStore.upload(storedName, image.getInputStream(), ImageFileUtility.createObjectMetadata(image)); - return new TaleImageInfo(originalName, storedName, imageUrl); + return new ImageInfo(originalName, storedName, imageUrl); } catch (IOException e) { throw new RuntimeIOException(e, ErrorCode.IMAGE_PROCESSING_IO); } diff --git a/src/main/java/hanium/englishfairytale/tale/application/dto/TaleImageInfo.java b/src/main/java/hanium/englishfairytale/common/files/ImageInfo.java similarity index 54% rename from src/main/java/hanium/englishfairytale/tale/application/dto/TaleImageInfo.java rename to src/main/java/hanium/englishfairytale/common/files/ImageInfo.java index 6bbc9bb..745e32f 100644 --- a/src/main/java/hanium/englishfairytale/tale/application/dto/TaleImageInfo.java +++ b/src/main/java/hanium/englishfairytale/common/files/ImageInfo.java @@ -1,13 +1,11 @@ -package hanium.englishfairytale.tale.application.dto; +package hanium.englishfairytale.common.files; import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.NoArgsConstructor; @Getter -@NoArgsConstructor @AllArgsConstructor -public class TaleImageInfo { +public class ImageInfo { String originalFileName; String storedName; String imageUrl; diff --git a/src/main/java/hanium/englishfairytale/exception/code/ErrorCode.java b/src/main/java/hanium/englishfairytale/exception/code/ErrorCode.java index 07976ba..b9f0020 100644 --- a/src/main/java/hanium/englishfairytale/exception/code/ErrorCode.java +++ b/src/main/java/hanium/englishfairytale/exception/code/ErrorCode.java @@ -28,12 +28,13 @@ public enum ErrorCode { EXCEED_KEYWORD_LIMIT(TaleCode.KEYWORD_COUNT_LIMIT.getCode(), BAD_REQUEST, "입력 키워드 개수 초과"), DUPLICATED_KEYWORD(TaleCode.KEYWORD_DUPLICATED.getCode(), BAD_REQUEST, "중복된 키워드가 있는 경우"), IMAGE_PROCESSING_IO(TaleCode.IMAGE_IO.getCode(), BAD_REQUEST, "이미지 처리 중 문제가 발생한 경우"), - IMAGE_NON_EXITED(TaleCode.NON_EXISTED_IMAGE.getCode(), NOT_ACCEPTABLE, "삭제할 이미지가 존재하지 않는 경우"), + TALE_IMAGE_NON_EXISTED(TaleCode.NON_EXISTED_IMAGE.getCode(), NOT_ACCEPTABLE, "삭제할 이미지가 존재하지 않는 경우"), // Member MEMBER_NOT_FOUND(MemberCode.NOT_FOUND.getCode(), NOT_FOUND, "존재하지 않는 회원"), EXISTED_MEMBER(MemberCode.MEMBER_EXISTED.getCode(), CONFLICT, "이미 회원가입 되어있을 경우"), DUPLICATED_NICKNAME(MemberCode.NICKNAME_DUPLICATED.getCode(), CONFLICT, "동일한 닉네임이 존재하는 경우"), + MEMBER_IMAGE_NON_EXISTED(MemberCode.NON_EXISTED_IMAGE.getCode(), CONFLICT, "삭제할 이미지가 존재하지 않는 경우") ; private final String code; diff --git a/src/main/java/hanium/englishfairytale/exception/code/MemberCode.java b/src/main/java/hanium/englishfairytale/exception/code/MemberCode.java index b7b805e..946b4a5 100644 --- a/src/main/java/hanium/englishfairytale/exception/code/MemberCode.java +++ b/src/main/java/hanium/englishfairytale/exception/code/MemberCode.java @@ -9,6 +9,7 @@ public enum MemberCode { NOT_FOUND("M-001"), MEMBER_EXISTED("M-002"), NICKNAME_DUPLICATED("M-003"), + NON_EXISTED_IMAGE("M-004"), ; private final String code; diff --git a/src/main/java/hanium/englishfairytale/member/application/MemberCommandService.java b/src/main/java/hanium/englishfairytale/member/application/MemberCommandService.java index 8cd651a..91ba5ba 100644 --- a/src/main/java/hanium/englishfairytale/member/application/MemberCommandService.java +++ b/src/main/java/hanium/englishfairytale/member/application/MemberCommandService.java @@ -1,40 +1,101 @@ package hanium.englishfairytale.member.application; +import hanium.englishfairytale.common.files.FileManageService; import hanium.englishfairytale.exception.BusinessException; +import hanium.englishfairytale.exception.NotFoundException; import hanium.englishfairytale.exception.code.ErrorCode; import hanium.englishfairytale.member.application.dto.MemberCreateCommand; -import hanium.englishfairytale.member.application.dto.CreateMemberResponse; +import hanium.englishfairytale.member.application.dto.MemberImageUpdateCommand; +import hanium.englishfairytale.member.application.dto.MemberUpdatePasswordCommand; +import hanium.englishfairytale.member.domain.ImageRepository; import hanium.englishfairytale.member.domain.Member; +import hanium.englishfairytale.member.domain.MemberImage; import hanium.englishfairytale.member.domain.MemberRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; @Service @RequiredArgsConstructor public class MemberCommandService { private final MemberRepository memberRepository; + private final ImageRepository imageRepository; + private final FileManageService fileManageService; @Transactional - public CreateMemberResponse register(MemberCreateCommand memberCreateCommand) { - return new CreateMemberResponse(createAndSaveMember(memberCreateCommand)); + public Long register(MemberCreateCommand memberCreateCommand) { + verifyExistedMember(memberCreateCommand); + return createAndSaveMember(memberCreateCommand); } - private Long createAndSaveMember(MemberCreateCommand memberCreateCommand) { - verifyExistedMember(memberCreateCommand); - verifyDuplicatedNickname(memberCreateCommand); - return memberRepository.save(createMember(memberCreateCommand)); + @Transactional + public void updateNickname(Long memberId, String nickname) { + Member member = findMember(memberId); + member.updateNickname(nickname); + } + + @Transactional + public void updatePassword(MemberUpdatePasswordCommand updatePasswordCommand) { + Member member = findMember(updatePasswordCommand.getMemberId()); + member.updatePassword(updatePasswordCommand.getPassword()); + } + + @Transactional + public void updateMemberImage(MemberImageUpdateCommand imageUpdateCommand) { + Member member = findMember(imageUpdateCommand.getMemberId()); + member.updateMemberImage(createAndSaveMemberImage(imageUpdateCommand.getImage())); + } + + @Transactional + public void deleteMemberImage(Long memberId) { + Member member = findMember(memberId); + Long imageId = findImageId(member); + deleteImage(member, imageId); + } + + private void deleteImage(Member member, Long imageId) { + member.makeImageNull(); + imageRepository.delete(imageId); + } + + private Long findImageId(Member member) { + verifyImageIsEmpty(member); + return member.getImageId(); + } + + private void verifyImageIsEmpty(Member member) { + member.checkImageEmpty(); + } + + private Member findMember(Long id) { + return memberRepository.findMemberById(id) + .orElseThrow(() -> new NotFoundException(ErrorCode.MEMBER_NOT_FOUND)); } - private void verifyDuplicatedNickname(MemberCreateCommand memberCreateCommand) { - if (memberRepository.findByNickname(memberCreateCommand.getNickname()).isPresent()) { - throw new BusinessException(ErrorCode.DUPLICATED_NICKNAME); + private Long createAndSaveMember(MemberCreateCommand memberCreateCommand) { + Member member = createMember(memberCreateCommand); + if (!checkImageEmpty(memberCreateCommand)) { + member.putImage(createAndSaveMemberImage(memberCreateCommand.getImage())); } + return saveMember(member); + } + + private boolean checkImageEmpty(MemberCreateCommand memberCreateCommand) { + return memberCreateCommand.getImage().isEmpty(); + } + + private Long saveMember(Member member) { + return memberRepository.save(member); + } + + private MemberImage createAndSaveMemberImage(MultipartFile file) { + return new MemberImage(fileManageService.uploadImage(file)); } private void verifyExistedMember(MemberCreateCommand memberCreateCommand) { - if (memberRepository.findByPhoneNumber(memberCreateCommand.getPhoneNumber()).isPresent()) { + if (memberRepository.findMemberByPhoneNumber(memberCreateCommand.getPhoneNumber()).isPresent()) { throw new BusinessException(ErrorCode.EXISTED_MEMBER); } } diff --git a/src/main/java/hanium/englishfairytale/member/application/MemberQueryService.java b/src/main/java/hanium/englishfairytale/member/application/MemberQueryService.java index 8551a74..ad43b01 100644 --- a/src/main/java/hanium/englishfairytale/member/application/MemberQueryService.java +++ b/src/main/java/hanium/englishfairytale/member/application/MemberQueryService.java @@ -1,35 +1,53 @@ package hanium.englishfairytale.member.application; -import hanium.englishfairytale.tale.application.dto.TalesInfo; -import hanium.englishfairytale.tale.domain.Keyword; -import hanium.englishfairytale.tale.domain.TaleKeyword; -import hanium.englishfairytale.tale.domain.TaleRepository; +import hanium.englishfairytale.exception.BusinessException; +import hanium.englishfairytale.exception.NotFoundException; +import hanium.englishfairytale.exception.code.ErrorCode; +import hanium.englishfairytale.member.application.dto.MemberDetailInfo; +import hanium.englishfairytale.member.application.dto.MemberInfo; +import hanium.englishfairytale.member.domain.Member; +import hanium.englishfairytale.member.domain.MemberRepository; +import hanium.englishfairytale.member.infra.MemberQueryDao; +import hanium.englishfairytale.tale.infra.TaleQueryDao; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.stream.Collectors; - @Service @RequiredArgsConstructor public class MemberQueryService { - private final TaleRepository taleRepository; + private final MemberRepository memberRepository; + private final MemberQueryDao memberQueryDao; + private final TaleQueryDao taleQueryDao; + + @Transactional + public void verifyNickname(String nickname) { + verifyNicknameDuplicated(nickname); + } - @Transactional(readOnly = true) - public List findMainTalesInfo(Long memberId) { + @Transactional + public MemberInfo findMemberInfo(Long memberId) { + return new MemberInfo(findMember(memberId), countTales(memberId)); + } - // memberId -> Tale 조회 -> 최신순, Tale+Member+Image+TaleKeywords 나옴. - // 각 TaleKeyword 의 id값 -> Keywords 조회 -> 위에서 찾은 TaleKeyword 의 id 값을 넣는다. -> + @Transactional + public MemberDetailInfo findMemberDetailInfo(Long memberId) { + return new MemberDetailInfo(findMember(memberId)); + } + private Member findMember(Long memberId) { + return memberQueryDao.findMemberAndImage(memberId) + .orElseThrow(() -> new NotFoundException(ErrorCode.MEMBER_NOT_FOUND)); + } - return null; + private Long countTales(Long memberId) { + return taleQueryDao.countTales(memberId); } - private List getKeywordsFromTaleKeyword(List taleKeywords) { - return taleKeywords.stream() - .map(TaleKeyword::getKeyword) - .collect(Collectors.toList()); + private void verifyNicknameDuplicated(String nickName) { + if (memberRepository.findByMemberNickname(nickName).isPresent()) { + throw new BusinessException(ErrorCode.DUPLICATED_NICKNAME); + } } } diff --git a/src/main/java/hanium/englishfairytale/member/application/dto/MemberCreateCommand.java b/src/main/java/hanium/englishfairytale/member/application/dto/MemberCreateCommand.java index b7ba69f..7fba69a 100644 --- a/src/main/java/hanium/englishfairytale/member/application/dto/MemberCreateCommand.java +++ b/src/main/java/hanium/englishfairytale/member/application/dto/MemberCreateCommand.java @@ -2,6 +2,7 @@ import lombok.Builder; import lombok.Getter; +import org.springframework.web.multipart.MultipartFile; @Builder @Getter @@ -11,4 +12,6 @@ public class MemberCreateCommand { private String nickname; private String email; private String password; + private MultipartFile image; + } diff --git a/src/main/java/hanium/englishfairytale/member/application/dto/MemberDetailInfo.java b/src/main/java/hanium/englishfairytale/member/application/dto/MemberDetailInfo.java new file mode 100644 index 0000000..d06f8fa --- /dev/null +++ b/src/main/java/hanium/englishfairytale/member/application/dto/MemberDetailInfo.java @@ -0,0 +1,19 @@ +package hanium.englishfairytale.member.application.dto; + +import hanium.englishfairytale.member.domain.Member; +import lombok.Getter; + +@Getter +public class MemberDetailInfo { + String nickname; + String email; + String phoneNumber; + String imageUrl; + + public MemberDetailInfo(Member member) { + this.nickname = member.getNickname(); + this.email = member.getEmail(); + this.phoneNumber = member.getPhoneNumber(); + this.imageUrl = member.getImage() == null ? null : member.getImage().getUrl(); + } +} diff --git a/src/main/java/hanium/englishfairytale/member/application/dto/MemberImageUpdateCommand.java b/src/main/java/hanium/englishfairytale/member/application/dto/MemberImageUpdateCommand.java new file mode 100644 index 0000000..160685d --- /dev/null +++ b/src/main/java/hanium/englishfairytale/member/application/dto/MemberImageUpdateCommand.java @@ -0,0 +1,12 @@ +package hanium.englishfairytale.member.application.dto; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.web.multipart.MultipartFile; + +@Builder +@Getter +public class MemberImageUpdateCommand { + private Long memberId; + private MultipartFile image; +} diff --git a/src/main/java/hanium/englishfairytale/member/application/dto/MemberInfo.java b/src/main/java/hanium/englishfairytale/member/application/dto/MemberInfo.java new file mode 100644 index 0000000..8d1611a --- /dev/null +++ b/src/main/java/hanium/englishfairytale/member/application/dto/MemberInfo.java @@ -0,0 +1,17 @@ +package hanium.englishfairytale.member.application.dto; + +import hanium.englishfairytale.member.domain.Member; +import lombok.Getter; + +@Getter +public class MemberInfo { + String nickname; + String imageUrl; + Long taleCounts; + + public MemberInfo(Member member, Long taleCounts) { + this.nickname = member.getNickname(); + this.imageUrl = member.getImage() == null ? null : member.getImage().getUrl(); + this.taleCounts = taleCounts; + } +} diff --git a/src/main/java/hanium/englishfairytale/member/application/dto/CreateMemberResponse.java b/src/main/java/hanium/englishfairytale/member/application/dto/MemberUpdatePasswordCommand.java similarity index 63% rename from src/main/java/hanium/englishfairytale/member/application/dto/CreateMemberResponse.java rename to src/main/java/hanium/englishfairytale/member/application/dto/MemberUpdatePasswordCommand.java index 6bba73c..e069eb2 100644 --- a/src/main/java/hanium/englishfairytale/member/application/dto/CreateMemberResponse.java +++ b/src/main/java/hanium/englishfairytale/member/application/dto/MemberUpdatePasswordCommand.java @@ -1,12 +1,15 @@ package hanium.englishfairytale.member.application.dto; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @Getter -@AllArgsConstructor @NoArgsConstructor -public class CreateMemberResponse { - private Long memberId; +@AllArgsConstructor +@Builder +public class MemberUpdatePasswordCommand { + Long memberId; + String password; } diff --git a/src/main/java/hanium/englishfairytale/member/domain/ImageRepository.java b/src/main/java/hanium/englishfairytale/member/domain/ImageRepository.java new file mode 100644 index 0000000..8eb0a19 --- /dev/null +++ b/src/main/java/hanium/englishfairytale/member/domain/ImageRepository.java @@ -0,0 +1,6 @@ +package hanium.englishfairytale.member.domain; + +public interface ImageRepository { + + void delete(Long memberImageId); +} diff --git a/src/main/java/hanium/englishfairytale/member/domain/Member.java b/src/main/java/hanium/englishfairytale/member/domain/Member.java index e9b52ed..d732d43 100644 --- a/src/main/java/hanium/englishfairytale/member/domain/Member.java +++ b/src/main/java/hanium/englishfairytale/member/domain/Member.java @@ -1,5 +1,7 @@ package hanium.englishfairytale.member.domain; +import hanium.englishfairytale.exception.BusinessException; +import hanium.englishfairytale.exception.code.ErrorCode; import hanium.englishfairytale.tale.domain.Tale; import lombok.AccessLevel; import lombok.Builder; @@ -34,7 +36,7 @@ public class Member { @Column(name = "created_date") private LocalDateTime createdTime; - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) private List tales = new ArrayList<>(); @Builder @@ -45,6 +47,7 @@ public Member(String name, String phoneNumber, String nickname, String email, St this.email = email; this.password = password; this.createdTime = LocalDateTime.now(); + this.image = new Image(); this.tales = new ArrayList<>(); } @@ -54,4 +57,34 @@ public void addTale(Tale newTale) { public void putImage(MemberImage memberImage) { image.putMemberImage(memberImage); } + + public void updateNickname(String nickname) { + this.nickname = nickname; + } + public void updatePassword(String password) { + this.password = password; + } + + public void updateMemberImage(MemberImage memberImage) { + if (image == null) { + this.image = new Image(); + this.image.putMemberImage(memberImage); + } else { + image.putMemberImage(memberImage); + } + } + + public void checkImageEmpty() { + if (image == null) { + throw new BusinessException(ErrorCode.MEMBER_IMAGE_NON_EXISTED); + } + } + + public Long getImageId() { + return this.image.getMemberImage().getId(); + } + + public void makeImageNull() { + this.image = null; + } } diff --git a/src/main/java/hanium/englishfairytale/member/domain/MemberImage.java b/src/main/java/hanium/englishfairytale/member/domain/MemberImage.java index 0d3aa3f..722c34f 100644 --- a/src/main/java/hanium/englishfairytale/member/domain/MemberImage.java +++ b/src/main/java/hanium/englishfairytale/member/domain/MemberImage.java @@ -1,5 +1,6 @@ package hanium.englishfairytale.member.domain; +import hanium.englishfairytale.common.files.ImageInfo; import lombok.*; import javax.persistence.*; @@ -26,17 +27,9 @@ public class MemberImage { @Column(name = "image_url") private String imageUrl; - public static MemberImage createMemberImage(Member member, String originalName, String storedName, String imageUrl) { - MemberImage memberImage = setMemberImage(originalName, storedName, imageUrl); - member.putImage(memberImage); - return memberImage; - } - - private static MemberImage setMemberImage(String originalName, String storedName, String imageUrl) { - return MemberImage.builder() - .originalName(originalName) - .storedName(storedName) - .imageUrl(imageUrl) - .build(); + public MemberImage(ImageInfo imageInfo) { + this.originalName = imageInfo.getOriginalFileName(); + this.storedName = imageInfo.getStoredName(); + this.imageUrl = imageInfo.getImageUrl(); } } diff --git a/src/main/java/hanium/englishfairytale/member/domain/MemberRepository.java b/src/main/java/hanium/englishfairytale/member/domain/MemberRepository.java index bb70162..f63011f 100644 --- a/src/main/java/hanium/englishfairytale/member/domain/MemberRepository.java +++ b/src/main/java/hanium/englishfairytale/member/domain/MemberRepository.java @@ -4,11 +4,11 @@ public interface MemberRepository { - Optional findById(Long userId); + Optional findMemberById(Long userId); - Optional findByPhoneNumber(String phoneNumber); + Optional findMemberByPhoneNumber(String phoneNumber); - Optional findByNickname(String nickName); + Optional findByMemberNickname(String nickName); Long save(Member member); } diff --git a/src/main/java/hanium/englishfairytale/member/infra/MemberImageJpaRepository.java b/src/main/java/hanium/englishfairytale/member/infra/MemberImageJpaRepository.java new file mode 100644 index 0000000..4208352 --- /dev/null +++ b/src/main/java/hanium/englishfairytale/member/infra/MemberImageJpaRepository.java @@ -0,0 +1,21 @@ +package hanium.englishfairytale.member.infra; + +import hanium.englishfairytale.member.domain.ImageRepository; +import hanium.englishfairytale.member.domain.MemberImage; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; + +@Repository +@RequiredArgsConstructor +public class MemberImageJpaRepository implements ImageRepository { + + private final EntityManager em; + + @Override + public void delete(Long memberImageId) { + MemberImage memberImage = em.find(MemberImage.class, memberImageId); + em.remove(memberImage); + } +} diff --git a/src/main/java/hanium/englishfairytale/member/infra/MemberJpaRepository.java b/src/main/java/hanium/englishfairytale/member/infra/MemberJpaRepository.java index 256f13f..4464079 100644 --- a/src/main/java/hanium/englishfairytale/member/infra/MemberJpaRepository.java +++ b/src/main/java/hanium/englishfairytale/member/infra/MemberJpaRepository.java @@ -16,12 +16,12 @@ public class MemberJpaRepository implements MemberRepository { private final EntityManager em; @Override - public Optional findById(Long memberId) { + public Optional findMemberById(Long memberId) { return Optional.ofNullable(em.find(Member.class, memberId)); } @Override - public Optional findByPhoneNumber(String phoneNumber) { + public Optional findMemberByPhoneNumber(String phoneNumber) { List members = em.createQuery("select m from Member m where m.phoneNumber = :phoneNumber", Member.class) .setParameter("phoneNumber", phoneNumber) .getResultList(); @@ -29,7 +29,7 @@ public Optional findByPhoneNumber(String phoneNumber) { } @Override - public Optional findByNickname(String nickName) { + public Optional findByMemberNickname(String nickName) { List members = em.createQuery("select m from Member m where m.nickname = :nickName", Member.class) .setParameter("nickName", nickName) .getResultList(); diff --git a/src/main/java/hanium/englishfairytale/member/infra/MemberQueryDao.java b/src/main/java/hanium/englishfairytale/member/infra/MemberQueryDao.java new file mode 100644 index 0000000..5b9d370 --- /dev/null +++ b/src/main/java/hanium/englishfairytale/member/infra/MemberQueryDao.java @@ -0,0 +1,29 @@ +package hanium.englishfairytale.member.infra; + +import hanium.englishfairytale.member.domain.Member; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import javax.persistence.EntityManager; +import java.util.List; +import java.util.Optional; + +@Repository +@RequiredArgsConstructor +public class MemberQueryDao { + + private final EntityManager em; + + // 회원+사진 조회 + public Optional findMemberAndImage(Long memberId) { + List members = em.createQuery( + "select m from Member m" + + " left join fetch m.image.memberImage mi" + + " where m.id = :memberId", Member.class + ) + .setParameter("memberId" ,memberId) + .getResultList(); + return members.stream().findAny(); + } + +} diff --git a/src/main/java/hanium/englishfairytale/member/infra/http/MemberController.java b/src/main/java/hanium/englishfairytale/member/infra/http/MemberController.java index 436155d..88d3692 100644 --- a/src/main/java/hanium/englishfairytale/member/infra/http/MemberController.java +++ b/src/main/java/hanium/englishfairytale/member/infra/http/MemberController.java @@ -1,17 +1,16 @@ package hanium.englishfairytale.member.infra.http; import hanium.englishfairytale.member.application.MemberCommandService; -import hanium.englishfairytale.member.application.dto.MemberCreateCommand; -import hanium.englishfairytale.member.application.dto.CreateMemberResponse; +import hanium.englishfairytale.member.application.MemberQueryService; +import hanium.englishfairytale.member.application.dto.*; import hanium.englishfairytale.member.infra.http.dto.MemberCreateDto; +import hanium.englishfairytale.member.infra.http.dto.MemberUpdatePasswordDto; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/api/v1/member") @@ -19,14 +18,59 @@ public class MemberController { private final MemberCommandService memberCommandService; + private final MemberQueryService memberQueryService; private final MemberDtoConverter converter; @PostMapping("/register") - public ResponseEntity register(@Validated @RequestBody MemberCreateDto memberCreateDto) { - return new ResponseEntity<>(memberCommandService.register(toCreateCommand(memberCreateDto)), HttpStatus.OK); + public ResponseEntity register(@Validated @RequestPart MemberCreateDto memberCreateDto, + @RequestPart MultipartFile image) { + return new ResponseEntity<>(memberCommandService.register(toCreateCommand(memberCreateDto, image)), HttpStatus.OK); } - private MemberCreateCommand toCreateCommand(MemberCreateDto memberCreateDto) { - return converter.toCommand(memberCreateDto); + @PostMapping("/check") + public void checkNicknameDuplicated(@RequestParam String nickname) { + memberQueryService.verifyNickname(nickname); + } + + @GetMapping("{memberId}") + public ResponseEntity findMemberInfo(@PathVariable Long memberId) { + return new ResponseEntity<>(memberQueryService.findMemberInfo(memberId), HttpStatus.OK); + } + + @GetMapping("{memberId}/detail") + public ResponseEntity findMemberDetailInfo(@PathVariable Long memberId) { + return new ResponseEntity<>(memberQueryService.findMemberDetailInfo(memberId), HttpStatus.OK); + } + + @PatchMapping("/nickname") + public void updateNickname(@RequestParam Long memberId, @RequestParam String nickname) { + memberCommandService.updateNickname(memberId, nickname); + } + + @PatchMapping("/password") + public void updatePassword(@Validated @RequestBody MemberUpdatePasswordDto updatePasswordDto) { + memberCommandService.updatePassword(toPasswordUpdateCommand(updatePasswordDto)); + } + + @PatchMapping("/{memberId}/image") + public void updateImage(@PathVariable Long memberId, @RequestPart MultipartFile image) { + memberCommandService.updateMemberImage(toImageUpdateCommand(memberId, image)); + } + + @DeleteMapping("/{memberId}/image") + public void deleteImage(@PathVariable Long memberId) { + memberCommandService.deleteMemberImage(memberId); + } + + private MemberCreateCommand toCreateCommand(MemberCreateDto memberCreateDto, MultipartFile image) { + return converter.toCommand(memberCreateDto, image); + } + + private MemberUpdatePasswordCommand toPasswordUpdateCommand(MemberUpdatePasswordDto updatePasswordDto) { + return converter.toCommand(updatePasswordDto); + } + + private MemberImageUpdateCommand toImageUpdateCommand(Long memberId, MultipartFile image) { + return converter.toCommand(memberId, image); } } diff --git a/src/main/java/hanium/englishfairytale/member/infra/http/MemberDtoConverter.java b/src/main/java/hanium/englishfairytale/member/infra/http/MemberDtoConverter.java index 84b97f8..3741f79 100644 --- a/src/main/java/hanium/englishfairytale/member/infra/http/MemberDtoConverter.java +++ b/src/main/java/hanium/englishfairytale/member/infra/http/MemberDtoConverter.java @@ -1,18 +1,37 @@ package hanium.englishfairytale.member.infra.http; import hanium.englishfairytale.member.application.dto.MemberCreateCommand; +import hanium.englishfairytale.member.application.dto.MemberImageUpdateCommand; +import hanium.englishfairytale.member.application.dto.MemberUpdatePasswordCommand; import hanium.englishfairytale.member.infra.http.dto.MemberCreateDto; +import hanium.englishfairytale.member.infra.http.dto.MemberUpdatePasswordDto; import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; @Component public class MemberDtoConverter { - public MemberCreateCommand toCommand(MemberCreateDto dto) { + public MemberCreateCommand toCommand(MemberCreateDto dto, MultipartFile image) { return MemberCreateCommand.builder() .name(dto.getName()) .phoneNumber(dto.getPhoneNumber()) .nickname(dto.getNickname()) .email(dto.getEmail()) .password(dto.getPassword()) + .image(image) + .build(); + } + + public MemberUpdatePasswordCommand toCommand(MemberUpdatePasswordDto dto) { + return MemberUpdatePasswordCommand.builder() + .memberId(dto.getMemberId()) + .password(dto.getPassword()) + .build(); + } + + public MemberImageUpdateCommand toCommand(Long id, MultipartFile image) { + return MemberImageUpdateCommand.builder() + .memberId(id) + .image(image) .build(); } } diff --git a/src/main/java/hanium/englishfairytale/member/infra/http/dto/MemberUpdatePasswordDto.java b/src/main/java/hanium/englishfairytale/member/infra/http/dto/MemberUpdatePasswordDto.java new file mode 100644 index 0000000..e8d49f0 --- /dev/null +++ b/src/main/java/hanium/englishfairytale/member/infra/http/dto/MemberUpdatePasswordDto.java @@ -0,0 +1,19 @@ +package hanium.englishfairytale.member.infra.http.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class MemberUpdatePasswordDto { + @NotNull + private Long memberId; + @NotNull + @Pattern(regexp = "^(?i)(?=.*[a-zA-Z])(?=.*[0-9])[a-zA-Z0-9]{2,10}$") + private String password; +} diff --git a/src/main/java/hanium/englishfairytale/tale/application/TaleCommandService.java b/src/main/java/hanium/englishfairytale/tale/application/TaleCommandService.java index 751f557..5746751 100644 --- a/src/main/java/hanium/englishfairytale/tale/application/TaleCommandService.java +++ b/src/main/java/hanium/englishfairytale/tale/application/TaleCommandService.java @@ -41,25 +41,25 @@ public TaleCreateResponse create(TaleCreateCommand taleCreateCommand) { @Transactional public void deleteTale(Long taleId) { - findExistedTale(taleId); + findTale(taleId); taleRepository.deleteByTaleId(taleId); } @Transactional public void updateTaleImage(TaleUpdateCommand taleUpdateCommand) { - Tale tale = findExistedTale(taleUpdateCommand.getTaleId()); - tale.updateTaleImage(saveTaleImage(taleUpdateCommand.getImage())); + Tale tale = findTale(taleUpdateCommand.getTaleId()); + tale.updateTaleImage(createAndSaveTaleImage(taleUpdateCommand.getImage())); } @Transactional public void deleteTaleImage(Long taleId) { - Tale tale = findExistedTale(taleId); + Tale tale = findTale(taleId); Long imageId = findImageId(tale); - tale.makeImageNull(); - deleteImage(imageId); + deleteImage(tale, imageId); } - private void deleteImage(Long imageUrl) { + private void deleteImage(Tale tale, Long imageUrl) { + tale.makeImageNull(); imageRepository.delete(imageUrl); } @@ -69,11 +69,10 @@ private Long findImageId(Tale tale) { } private void verifyImageIsEmpty(Tale tale) { - if (tale.checkImageEmpty()) - throw new BusinessException(ErrorCode.IMAGE_NON_EXITED); + tale.checkImageEmpty(); } - private Tale findExistedTale(Long taleId) { + private Tale findTale(Long taleId) { return taleQueryDao.findTaleByTaleId(taleId) .orElseThrow(() -> new NotFoundException(ErrorCode.TALE_NOT_FOUND)); } @@ -90,7 +89,7 @@ private Tale createTale(TaleCreateCommand taleCreateCommand) { } private Member findMember(Long memberId) { - return memberRepository.findById(memberId) + return memberRepository.findMemberById(memberId) .orElseThrow(() -> new NotFoundException(ErrorCode.MEMBER_NOT_FOUND, memberId)); } @@ -106,7 +105,7 @@ private void verifyInputKeywords(TaleCreateCommand taleCreateCommand) { private TaleCreateResponse saveTaleAndKeywords(Tale tale, List keywords, MultipartFile image) { if (!image.isEmpty()) { - tale.putImage(saveTaleImage(image)); + tale.putImage(createAndSaveTaleImage(image)); } saveTaleKeywords(tale, keywords); return new TaleCreateResponse(tale,keywords); @@ -118,8 +117,8 @@ private void saveTaleKeywords(Tale tale, List keywords) { } } - private TaleImage saveTaleImage(MultipartFile image) { - return new TaleImage(fileManageService.uploadTaleImage(image)); + private TaleImage createAndSaveTaleImage(MultipartFile image) { + return new TaleImage(fileManageService.uploadImage(image)); } private List findAndCreateKeywords(TaleCreateCommand taleCreateCommand) { diff --git a/src/main/java/hanium/englishfairytale/tale/application/TaleQueryService.java b/src/main/java/hanium/englishfairytale/tale/application/TaleQueryService.java index b57ebdc..20637df 100644 --- a/src/main/java/hanium/englishfairytale/tale/application/TaleQueryService.java +++ b/src/main/java/hanium/englishfairytale/tale/application/TaleQueryService.java @@ -23,13 +23,14 @@ public class TaleQueryService { private final MemberRepository memberRepository; // 동화 상세조회 + // TODO: 2023.10.06 좋아요 개수 표시 @Transactional(readOnly = true) public TaleDetailInfo findDetailTale(Long taleId) { Tale tale = findTale(taleId); return convertTaleToDetailInfo(tale); } - // 마이 페이지(전체 조회) + // 동화 전체조회 @Transactional(readOnly = true) public List findAllTales(Long memberId, int offset) { verifyExistedMember(memberId); @@ -55,7 +56,7 @@ private TaleDetailInfo convertTaleToDetailInfo(Tale tale) { } private void verifyExistedMember(Long memberId) { - if (memberRepository.findById(memberId).isEmpty()) { + if (memberRepository.findMemberById(memberId).isEmpty()) { throw new NotFoundException(ErrorCode.MEMBER_NOT_FOUND); } } diff --git a/src/main/java/hanium/englishfairytale/tale/domain/ImageRepository.java b/src/main/java/hanium/englishfairytale/tale/domain/ImageRepository.java index 4ff5c09..89fb280 100644 --- a/src/main/java/hanium/englishfairytale/tale/domain/ImageRepository.java +++ b/src/main/java/hanium/englishfairytale/tale/domain/ImageRepository.java @@ -2,7 +2,5 @@ public interface ImageRepository { - String save(TaleImage taleImage); - void delete(Long taleImageId); } diff --git a/src/main/java/hanium/englishfairytale/tale/domain/Tale.java b/src/main/java/hanium/englishfairytale/tale/domain/Tale.java index dbc2591..b29b813 100644 --- a/src/main/java/hanium/englishfairytale/tale/domain/Tale.java +++ b/src/main/java/hanium/englishfairytale/tale/domain/Tale.java @@ -1,5 +1,7 @@ package hanium.englishfairytale.tale.domain; +import hanium.englishfairytale.exception.BusinessException; +import hanium.englishfairytale.exception.code.ErrorCode; import hanium.englishfairytale.member.domain.Member; import lombok.*; @@ -65,8 +67,10 @@ public void updateTaleImage(TaleImage taleImage) { } } - public boolean checkImageEmpty() { - return image == null; + public void checkImageEmpty() { + if (image == null) { + throw new BusinessException(ErrorCode.TALE_IMAGE_NON_EXISTED); + } } public Long getImageId() { diff --git a/src/main/java/hanium/englishfairytale/tale/domain/TaleImage.java b/src/main/java/hanium/englishfairytale/tale/domain/TaleImage.java index 23dd723..fea62c6 100644 --- a/src/main/java/hanium/englishfairytale/tale/domain/TaleImage.java +++ b/src/main/java/hanium/englishfairytale/tale/domain/TaleImage.java @@ -1,6 +1,6 @@ package hanium.englishfairytale.tale.domain; -import hanium.englishfairytale.tale.application.dto.TaleImageInfo; +import hanium.englishfairytale.common.files.ImageInfo; import lombok.*; import javax.persistence.*; @@ -25,9 +25,9 @@ public class TaleImage { @Column(name = "image_url") private String imageUrl; - public TaleImage(TaleImageInfo taleImageInfo) { - this.originalName = taleImageInfo.getOriginalFileName(); - this.storedName = taleImageInfo.getStoredName(); - this.imageUrl = taleImageInfo.getImageUrl(); + public TaleImage(ImageInfo imageInfo) { + this.originalName = imageInfo.getOriginalFileName(); + this.storedName = imageInfo.getStoredName(); + this.imageUrl = imageInfo.getImageUrl(); } } diff --git a/src/main/java/hanium/englishfairytale/tale/infra/ImageJpaRepository.java b/src/main/java/hanium/englishfairytale/tale/infra/TaleImageJpaRepository.java similarity index 68% rename from src/main/java/hanium/englishfairytale/tale/infra/ImageJpaRepository.java rename to src/main/java/hanium/englishfairytale/tale/infra/TaleImageJpaRepository.java index 9f8fd02..ca88898 100644 --- a/src/main/java/hanium/englishfairytale/tale/infra/ImageJpaRepository.java +++ b/src/main/java/hanium/englishfairytale/tale/infra/TaleImageJpaRepository.java @@ -1,7 +1,6 @@ package hanium.englishfairytale.tale.infra; import hanium.englishfairytale.tale.domain.ImageRepository; -import hanium.englishfairytale.tale.domain.Tale; import hanium.englishfairytale.tale.domain.TaleImage; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; @@ -10,16 +9,10 @@ @Repository @RequiredArgsConstructor -public class ImageJpaRepository implements ImageRepository { +public class TaleImageJpaRepository implements ImageRepository { private final EntityManager em; - @Override - public String save(TaleImage taleImage) { - em.persist(taleImage); - return taleImage.getImageUrl(); - } - @Override public void delete(Long taleImageId) { TaleImage taleImage = em.find(TaleImage.class, taleImageId); diff --git a/src/main/java/hanium/englishfairytale/tale/infra/TaleQueryDao.java b/src/main/java/hanium/englishfairytale/tale/infra/TaleQueryDao.java index b4dc584..7620e56 100644 --- a/src/main/java/hanium/englishfairytale/tale/infra/TaleQueryDao.java +++ b/src/main/java/hanium/englishfairytale/tale/infra/TaleQueryDao.java @@ -40,7 +40,6 @@ public Optional findTaleByTaleId(Long taleId) { ) .setParameter("taleId", taleId) .getResultList(); - return tales.stream().findAny(); } @@ -54,4 +53,14 @@ public List findKeywordByTaleId(Long taleId) { .setParameter("taleId", taleId) .getResultList(); } + + // 동화개수 조회 + public Long countTales(Long memberId) { + return (Long) em.createQuery( + "select count(nullif(t, 0)) from Tale t" + + " where t.member.id = :memberId" + ) + .setParameter("memberId", memberId) + .getSingleResult(); + } } diff --git a/src/main/java/hanium/englishfairytale/tale/infra/http/TaleController.java b/src/main/java/hanium/englishfairytale/tale/infra/http/TaleController.java index e612811..c153678 100644 --- a/src/main/java/hanium/englishfairytale/tale/infra/http/TaleController.java +++ b/src/main/java/hanium/englishfairytale/tale/infra/http/TaleController.java @@ -20,13 +20,13 @@ public class TaleController { private final TaleQueryService taleQueryService; - private final TaleCommandService taleService; + private final TaleCommandService taleCommandService; private final TaleDtoConverter converter; @PostMapping("create") public ResponseEntity create(@Validated @RequestPart TaleCreateDto taleCreateDto, @RequestPart MultipartFile image) { - return new ResponseEntity<>(taleService.create(toCreateCommand(taleCreateDto, image)), HttpStatus.OK); + return new ResponseEntity<>(taleCommandService.create(toCreateCommand(taleCreateDto, image)), HttpStatus.OK); } @GetMapping("/recent") @@ -46,17 +46,17 @@ public ResponseEntity findDetailTale(@PathVariable Long taleId) @DeleteMapping("/{taleId}") public void delete(@PathVariable Long taleId) { - taleService.deleteTale(taleId); + taleCommandService.deleteTale(taleId); } @PutMapping("/{taleId}/image") public void updateImage(@PathVariable Long taleId, @RequestPart MultipartFile image) { - taleService.updateTaleImage(toUpdateCommand(taleId, image)); + taleCommandService.updateTaleImage(toUpdateCommand(taleId, image)); } @DeleteMapping("/{taleId}/image") public void deleteImage(@PathVariable Long taleId) { - taleService.deleteTaleImage(taleId); + taleCommandService.deleteTaleImage(taleId); } private TaleCreateCommand toCreateCommand(TaleCreateDto taleCreateDto, MultipartFile image) {