diff --git a/src/main/java/chzzk/grassdiary/domain/member/controller/MemberController.java b/src/main/java/chzzk/grassdiary/domain/member/controller/MemberController.java index 02da2f6b..5e5b7fc9 100644 --- a/src/main/java/chzzk/grassdiary/domain/member/controller/MemberController.java +++ b/src/main/java/chzzk/grassdiary/domain/member/controller/MemberController.java @@ -1,11 +1,14 @@ package chzzk.grassdiary.domain.member.controller; +import chzzk.grassdiary.domain.member.dto.EquipColorResponseDTO; import chzzk.grassdiary.domain.member.dto.MemberPurchasedColorsResponseDTO; import chzzk.grassdiary.domain.member.service.MemberService; import chzzk.grassdiary.domain.member.service.MyPageService; import chzzk.grassdiary.domain.member.dto.MemberInfoDTO; import chzzk.grassdiary.domain.member.dto.TotalRewardDTO; import chzzk.grassdiary.domain.reward.service.RewardService; +import chzzk.grassdiary.global.auth.common.AuthenticatedMember; +import chzzk.grassdiary.global.auth.service.dto.AuthMemberPayload; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Content; @@ -16,6 +19,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -52,4 +56,11 @@ public ResponseEntity getTotalReward(@PathVariable Long memberId) { public MemberPurchasedColorsResponseDTO getMemberColors(@PathVariable Long memberId) { return memberService.getPurchasedColors(memberId); } + + @PostMapping("/colors/{colorCodeId}/equip") + public EquipColorResponseDTO equipColor( + @PathVariable(name = "colorCodeId") Long colorCodeId, + @AuthenticatedMember AuthMemberPayload payload) { + return memberService.equipColor(payload.id(), colorCodeId); + } } diff --git a/src/main/java/chzzk/grassdiary/domain/member/dto/EquipColorResponseDTO.java b/src/main/java/chzzk/grassdiary/domain/member/dto/EquipColorResponseDTO.java new file mode 100644 index 00000000..2f9d613c --- /dev/null +++ b/src/main/java/chzzk/grassdiary/domain/member/dto/EquipColorResponseDTO.java @@ -0,0 +1,21 @@ +package chzzk.grassdiary.domain.member.dto; + +import chzzk.grassdiary.domain.member.entity.Member; +import chzzk.grassdiary.domain.color.entity.ColorCode; + +public record EquipColorResponseDTO( + Long memberId, + Long colorCodeId, + String colorName, + String rgb +) { + public static EquipColorResponseDTO from(Member member) { + ColorCode currentColorCode = member.getCurrentColorCode(); + return new EquipColorResponseDTO( + member.getId(), + currentColorCode.getId(), + currentColorCode.getColorName(), + currentColorCode.getRgb() + ); + } +} diff --git a/src/main/java/chzzk/grassdiary/domain/member/entity/Member.java b/src/main/java/chzzk/grassdiary/domain/member/entity/Member.java index d09ca311..bfd4a538 100644 --- a/src/main/java/chzzk/grassdiary/domain/member/entity/Member.java +++ b/src/main/java/chzzk/grassdiary/domain/member/entity/Member.java @@ -1,7 +1,7 @@ package chzzk.grassdiary.domain.member.entity; import chzzk.grassdiary.domain.base.BaseTimeEntity; -import chzzk.grassdiary.domain.color.ColorCode; +import chzzk.grassdiary.domain.color.entity.ColorCode; import chzzk.grassdiary.domain.diary.entity.Diary; import chzzk.grassdiary.global.common.error.exception.SystemException; import chzzk.grassdiary.global.common.response.ClientErrorCode; @@ -102,4 +102,11 @@ public void deductRewardPoints(int points) { } this.rewardPoint -= points; } + + public void equipColor(ColorCode newColor) { + if (this.currentColorCode.getId().equals(newColor.getId())) { + throw new SystemException(ClientErrorCode.COLOR_ALREADY_EQUIPPED_ERR); + } + this.currentColorCode = newColor; + } } diff --git a/src/main/java/chzzk/grassdiary/domain/member/service/MemberService.java b/src/main/java/chzzk/grassdiary/domain/member/service/MemberService.java index 4d952fac..a0133135 100644 --- a/src/main/java/chzzk/grassdiary/domain/member/service/MemberService.java +++ b/src/main/java/chzzk/grassdiary/domain/member/service/MemberService.java @@ -1,5 +1,6 @@ package chzzk.grassdiary.domain.member.service; +import chzzk.grassdiary.domain.member.dto.EquipColorResponseDTO; import chzzk.grassdiary.domain.member.dto.MemberPurchasedColorResponseDTO; import chzzk.grassdiary.domain.member.dto.MemberPurchasedColorsResponseDTO; import chzzk.grassdiary.domain.member.entity.MemberPurchasedColorDAO; @@ -78,4 +79,44 @@ private MemberPurchasedColorResponseDTO getDefaultColorDTO() { return MemberPurchasedColorResponseDTO.from(defaultColorCode); } + + public EquipColorResponseDTO equipColor(Long logInMemberId, Long colorCodeId) { + Member member = getMemberById(logInMemberId); + + validateMemberOwnsColor(colorCodeId, logInMemberId); + + validateColorAlreadyEquipped(member, colorCodeId); + + changeMemberColor(member, colorCodeId); + + return EquipColorResponseDTO.from(member); + } + + private Member getMemberById(Long id) { + return memberDAO.findById(id) + .orElseThrow(() -> new SystemException(ClientErrorCode.MEMBER_NOT_FOUND_ERR)); + } + + private void validateMemberOwnsColor(Long memberId, Long colorCodeId) { + boolean ownsColor = memberPurchasedColorDAO.existsByColorCodeIdAndMemberId(memberId, colorCodeId); + if (!ownsColor) { + throw new SystemException(ClientErrorCode.MEMBER_DOES_NOT_OWN_COLOR_ERR); + } + } + + private void validateColorAlreadyEquipped(Member member, Long colorId) { + if (member.getCurrentColorCode().getId().equals(colorId)) { + throw new SystemException(ClientErrorCode.COLOR_ALREADY_EQUIPPED_ERR); + } + } + + private void changeMemberColor(Member member, Long colorCodeId) { + ColorCode newColor = colorCodeDAO.findById(colorCodeId) + .orElseThrow(() -> new SystemException(ClientErrorCode.COLOR_CODE_NOT_FOUND_ERR)); + + member.equipColor(newColor); + + memberDAO.save(member); + } + } diff --git a/src/main/java/chzzk/grassdiary/global/common/response/ClientErrorCode.java b/src/main/java/chzzk/grassdiary/global/common/response/ClientErrorCode.java index a1c4aa5b..06b545bc 100644 --- a/src/main/java/chzzk/grassdiary/global/common/response/ClientErrorCode.java +++ b/src/main/java/chzzk/grassdiary/global/common/response/ClientErrorCode.java @@ -12,12 +12,14 @@ public enum ClientErrorCode implements ErrorCodeModel { AUTH_SESSION_EXPIRED(440, "AUTH_SESSION_EXPIRED", "세션이 만료되었습니다. 다시 로그인 해주세요."), MEMBER_NOT_FOUND_ERR(404, "MEMBER_NOT_FOUND_ERR", "요청하신 사용자를 찾을 수 없습니다."), + MEMBER_DOES_NOT_OWN_COLOR_ERR(409, "MEMBER_DOES_NOT_OWN_COLOR_ERR", "해당 색상을 소유하고 있지 않습니다."), PAST_DIARY_MODIFICATION_NOT_ALLOWED(409, "PAST_DIARY_MODIFICATION_NOT_ALLOWED", "과거의 일기는 수정할 수 없습니다."), DIARY_NOT_FOUND_ERR(404, "DIARY_NOT_FOUND_ERR", "요청하신 다이어리를 찾을 수 없습니다."), DIARY_LIKE_NOT_FOUND(404, "DIARY_LIKE_NOT_FOUND", "해당 다이어리에 좋아요를 누르지 않았습니다."), DIARY_LIKE_ALREADY_EXISTS(409, "DIARY_LIKE_ALREADY_EXISTS", "다이어리에 좋아요를 이미 눌렀습니다."), DIARY_ALREADY_EXISTS_FOR_TODAY(409, "DIARY_ALREADY_EXISTS_FOR_TODAY", "일기는 하루에 하나만 작성 가능합니다."), COMMENT_NOT_FOUND_ERR(404, "COMMENT_NOT_FOUND_ERR", "요청하신 댓글을 찾을 수 없습니다."), + COLOR_ALREADY_EQUIPPED_ERR(409, "COLOR_ALREADY_EQUIPPED_ERR", "현재 사용중인 색상입니다."), INSUFFICIENT_REWARD_POINTS_ERR(409, "INSUFFICIENT_REWARD_POINTS_ERR", "포인트가 충분하지 않습니다."), INVALID_IMAGE_FORMAT(400, "INVALID_IMAGE_FORMAT", "허용되지 않는 파일 형식입니다."), IMAGE_FILE_EMPTY(400, "IMAGE_FILE_EMPTY", "이미지 파일이 비어 있습니다."),