From 964b93098ebfba657622cc762a75c8051897c803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=A7=84?= Date: Mon, 9 Oct 2023 11:45:52 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95API=20=EC=84=A4=EA=B3=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/code/ErrorCode.java | 3 +- .../exception/code/MemberCode.java | 1 + .../application/MemberCommandService.java | 49 +++++++++++++++++++ .../application/MemberQueryService.java | 4 +- .../dto/MemberImageUpdateCommand.java | 12 +++++ .../dto/MemberUpdatePasswordCommand.java | 15 ++++++ .../member/domain/ImageRepository.java | 6 +++ .../member/domain/Member.java | 32 ++++++++++++ .../infra/MemberImageJpaRepository.java | 21 ++++++++ .../member/infra/http/MemberController.java | 38 +++++++++++--- .../member/infra/http/MemberDtoConverter.java | 17 +++++++ .../http/dto/MemberUpdatePasswordDto.java | 19 +++++++ .../tale/application/TaleCommandService.java | 11 ++--- .../englishfairytale/tale/domain/Tale.java | 8 ++- ...itory.java => TaleImageJpaRepository.java} | 3 +- .../tale/infra/http/TaleController.java | 10 ++-- 16 files changed, 225 insertions(+), 24 deletions(-) create mode 100644 src/main/java/hanium/englishfairytale/member/application/dto/MemberImageUpdateCommand.java create mode 100644 src/main/java/hanium/englishfairytale/member/application/dto/MemberUpdatePasswordCommand.java create mode 100644 src/main/java/hanium/englishfairytale/member/domain/ImageRepository.java create mode 100644 src/main/java/hanium/englishfairytale/member/infra/MemberImageJpaRepository.java create mode 100644 src/main/java/hanium/englishfairytale/member/infra/http/dto/MemberUpdatePasswordDto.java rename src/main/java/hanium/englishfairytale/tale/infra/{ImageJpaRepository.java => TaleImageJpaRepository.java} (82%) 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 18171f4..91ba5ba 100644 --- a/src/main/java/hanium/englishfairytale/member/application/MemberCommandService.java +++ b/src/main/java/hanium/englishfairytale/member/application/MemberCommandService.java @@ -2,8 +2,12 @@ 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.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; @@ -17,6 +21,7 @@ public class MemberCommandService { private final MemberRepository memberRepository; + private final ImageRepository imageRepository; private final FileManageService fileManageService; @Transactional @@ -25,6 +30,50 @@ public Long register(MemberCreateCommand memberCreateCommand) { return createAndSaveMember(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 Long createAndSaveMember(MemberCreateCommand memberCreateCommand) { Member member = createMember(memberCreateCommand); if (!checkImageEmpty(memberCreateCommand)) { diff --git a/src/main/java/hanium/englishfairytale/member/application/MemberQueryService.java b/src/main/java/hanium/englishfairytale/member/application/MemberQueryService.java index 69a2a66..ad43b01 100644 --- a/src/main/java/hanium/englishfairytale/member/application/MemberQueryService.java +++ b/src/main/java/hanium/englishfairytale/member/application/MemberQueryService.java @@ -22,8 +22,8 @@ public class MemberQueryService { private final TaleQueryDao taleQueryDao; @Transactional - public void verifyNickName(String nickName) { - verifyNicknameDuplicated(nickName); + public void verifyNickname(String nickname) { + verifyNicknameDuplicated(nickname); } @Transactional 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/MemberUpdatePasswordCommand.java b/src/main/java/hanium/englishfairytale/member/application/dto/MemberUpdatePasswordCommand.java new file mode 100644 index 0000000..e069eb2 --- /dev/null +++ b/src/main/java/hanium/englishfairytale/member/application/dto/MemberUpdatePasswordCommand.java @@ -0,0 +1,15 @@ +package hanium.englishfairytale.member.application.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@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 a3c3e10..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; @@ -55,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/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/http/MemberController.java b/src/main/java/hanium/englishfairytale/member/infra/http/MemberController.java index 8109247..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,12 +1,10 @@ package hanium.englishfairytale.member.infra.http; -import com.amazonaws.Response; import hanium.englishfairytale.member.application.MemberCommandService; import hanium.englishfairytale.member.application.MemberQueryService; -import hanium.englishfairytale.member.application.dto.MemberCreateCommand; -import hanium.englishfairytale.member.application.dto.MemberDetailInfo; -import hanium.englishfairytale.member.application.dto.MemberInfo; +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; @@ -30,8 +28,8 @@ public ResponseEntity register(@Validated @RequestPart MemberCreateDto mem } @PostMapping("/check") - public void checkNicknameDuplicated(@RequestParam String nickName) { - memberQueryService.verifyNickName(nickName); + public void checkNicknameDuplicated(@RequestParam String nickname) { + memberQueryService.verifyNickname(nickname); } @GetMapping("{memberId}") @@ -44,7 +42,35 @@ public ResponseEntity findMemberDetailInfo(@PathVariable Long 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 b3a17e4..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,7 +1,10 @@ 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; @@ -17,4 +20,18 @@ public MemberCreateCommand toCommand(MemberCreateDto dto, MultipartFile image) { .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 4b0784d..5746751 100644 --- a/src/main/java/hanium/englishfairytale/tale/application/TaleCommandService.java +++ b/src/main/java/hanium/englishfairytale/tale/application/TaleCommandService.java @@ -41,19 +41,19 @@ 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 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); deleteImage(tale, imageId); } @@ -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)); } 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/infra/ImageJpaRepository.java b/src/main/java/hanium/englishfairytale/tale/infra/TaleImageJpaRepository.java similarity index 82% rename from src/main/java/hanium/englishfairytale/tale/infra/ImageJpaRepository.java rename to src/main/java/hanium/englishfairytale/tale/infra/TaleImageJpaRepository.java index 7afa5af..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,7 +9,7 @@ @Repository @RequiredArgsConstructor -public class ImageJpaRepository implements ImageRepository { +public class TaleImageJpaRepository implements ImageRepository { private final EntityManager em; 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) {