From 23f5ee69d32c51fe5aedc1c18111abf942807133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:52:24 +0900 Subject: [PATCH 01/59] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=EC=9E=AC?= =?UTF-8?q?=EB=B0=9C=EA=B8=89=20=EC=9D=B8=EC=A6=9D=20=EC=9D=B8=EA=B0=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20#81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReissueTokenController.java | 15 +++++++++++---- .../reissue/service/ReissueTokenService.java | 8 ++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/java/poomasi/domain/auth/token/reissue/controller/ReissueTokenController.java b/src/main/java/poomasi/domain/auth/token/reissue/controller/ReissueTokenController.java index fc2dc627..43aeb0c8 100644 --- a/src/main/java/poomasi/domain/auth/token/reissue/controller/ReissueTokenController.java +++ b/src/main/java/poomasi/domain/auth/token/reissue/controller/ReissueTokenController.java @@ -3,12 +3,16 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import poomasi.domain.auth.security.userdetail.UserDetailsImpl; import poomasi.domain.auth.token.reissue.dto.ReissueRequest; import poomasi.domain.auth.token.reissue.dto.ReissueResponse; import poomasi.domain.auth.token.reissue.service.ReissueTokenService; +import poomasi.domain.member.entity.Member; @RestController public class ReissueTokenController { @@ -16,8 +20,11 @@ public class ReissueTokenController { @Autowired private ReissueTokenService reissueTokenService; - @GetMapping("/api/reissue") - public ResponseEntity reissue(@RequestBody ReissueRequest reissueRequest){ - return ResponseEntity.ok(reissueTokenService.reissueToken(reissueRequest)); + @Secured({"ROLE_FARMER", "ROLE_CUSTOMER"}) + @PostMapping("/api/reissue") + public ResponseEntity reissue(@AuthenticationPrincipal UserDetailsImpl userDetails, @RequestBody ReissueRequest reissueRequest){ + Member member = userDetails.getMember(); + return ResponseEntity.ok(reissueTokenService.reissueToken(member.getId(), reissueRequest)); } + } diff --git a/src/main/java/poomasi/domain/auth/token/reissue/service/ReissueTokenService.java b/src/main/java/poomasi/domain/auth/token/reissue/service/ReissueTokenService.java index bc9884fb..89b379e5 100644 --- a/src/main/java/poomasi/domain/auth/token/reissue/service/ReissueTokenService.java +++ b/src/main/java/poomasi/domain/auth/token/reissue/service/ReissueTokenService.java @@ -18,9 +18,13 @@ public class ReissueTokenService { private final RefreshTokenService refreshTokenService; // 토큰 재발급 - public ReissueResponse reissueToken(ReissueRequest reissueRequest) { + public ReissueResponse reissueToken(Long memberId, ReissueRequest reissueRequest) { String refreshToken = reissueRequest.refreshToken(); - Long memberId = jwtUtil.getIdFromToken(refreshToken); + Long requestMemberId = jwtUtil.getIdFromToken(refreshToken); + + if (!requestMemberId.equals(memberId)) { + throw new BusinessException(REFRESH_TOKEN_NOT_VALID); + } checkRefreshToken(refreshToken, memberId); From b3c28df03ef3d40ec0d5576ccc8623cb3ef64f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Thu, 7 Nov 2024 17:10:02 +0900 Subject: [PATCH 02/59] =?UTF-8?q?chore:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EB=A1=9C=EC=A7=81=20member=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=EB=A1=9C=20=EC=9C=84=EC=B9=98=20=EB=B3=80=EA=B2=BD=20#81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../signup/controller/SignupController.java | 25 ----------- .../auth/signup/service/SignupService.java | 44 ------------------- .../member/controller/MemberController.java | 8 ++++ .../dto/request/SignupRequest.java | 2 +- .../dto/response/SignUpResponse.java | 2 +- .../domain/member/service/MemberService.java | 24 ++++++++++ 6 files changed, 34 insertions(+), 71 deletions(-) delete mode 100644 src/main/java/poomasi/domain/auth/signup/controller/SignupController.java delete mode 100644 src/main/java/poomasi/domain/auth/signup/service/SignupService.java rename src/main/java/poomasi/domain/{auth/signup => member}/dto/request/SignupRequest.java (57%) rename src/main/java/poomasi/domain/{auth/signup => member}/dto/response/SignUpResponse.java (56%) diff --git a/src/main/java/poomasi/domain/auth/signup/controller/SignupController.java b/src/main/java/poomasi/domain/auth/signup/controller/SignupController.java deleted file mode 100644 index 3be51a8c..00000000 --- a/src/main/java/poomasi/domain/auth/signup/controller/SignupController.java +++ /dev/null @@ -1,25 +0,0 @@ -package poomasi.domain.auth.signup.controller; - -import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import poomasi.domain.auth.signup.dto.response.SignUpResponse; -import poomasi.domain.auth.signup.service.SignupService; -import poomasi.domain.auth.signup.dto.request.SignupRequest; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/api") -public class SignupController { - - private final SignupService signupService; - - @PostMapping("/sign-up") - public ResponseEntity signUp(@RequestBody SignupRequest signupRequest) { - return ResponseEntity.ok(signupService - .signUp(signupRequest)); - } - -} - - diff --git a/src/main/java/poomasi/domain/auth/signup/service/SignupService.java b/src/main/java/poomasi/domain/auth/signup/service/SignupService.java deleted file mode 100644 index fe8c0859..00000000 --- a/src/main/java/poomasi/domain/auth/signup/service/SignupService.java +++ /dev/null @@ -1,44 +0,0 @@ -package poomasi.domain.auth.signup.service; - -import jdk.jfr.Description; -import lombok.RequiredArgsConstructor; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import poomasi.domain.auth.signup.dto.request.SignupRequest; -import poomasi.domain.auth.signup.dto.response.SignUpResponse; -import poomasi.domain.member.entity.LoginType; -import poomasi.domain.member.repository.MemberRepository; -import poomasi.domain.member.entity.Member; -import poomasi.global.error.BusinessException; - -import static poomasi.domain.member.entity.Role.ROLE_CUSTOMER; -import static poomasi.global.error.BusinessError.*; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class SignupService { - - private final MemberRepository memberRepository; - private final PasswordEncoder passwordEncoder; - - @Description("카카오톡으로 먼저 회원가입이 되어 있는 경우, 계정 연동을 진행합니다. ") - @Transactional - public SignUpResponse signUp(SignupRequest signupRequest) { - String email = signupRequest.email(); - String password = signupRequest.password(); - - memberRepository.findByEmail(email) - .ifPresent(member -> { throw new BusinessException(DUPLICATE_MEMBER_EMAIL); }); - - Member newMember = new Member(email, - passwordEncoder.encode(password), - LoginType.LOCAL, - ROLE_CUSTOMER); - - memberRepository.save(newMember); - return new SignUpResponse(email, "회원 가입 성공"); - } -} - diff --git a/src/main/java/poomasi/domain/member/controller/MemberController.java b/src/main/java/poomasi/domain/member/controller/MemberController.java index 0f1cd7a4..91f04d05 100644 --- a/src/main/java/poomasi/domain/member/controller/MemberController.java +++ b/src/main/java/poomasi/domain/member/controller/MemberController.java @@ -9,6 +9,8 @@ import poomasi.domain.member.dto.request.FarmerQualificationRequest; import poomasi.domain.member.dto.response.MemberResponse; import poomasi.domain.member.service.MemberService; +import poomasi.domain.member.dto.request.SignupRequest; +import poomasi.domain.member.dto.response.SignUpResponse; @RestController @RequiredArgsConstructor @@ -17,6 +19,12 @@ public class MemberController { private final MemberService memberService; + @PostMapping("/sign-up") + public ResponseEntity signUp(@RequestBody SignupRequest signupRequest) { + return ResponseEntity.ok(memberService + .signUp(signupRequest)); + } + @PutMapping("/toFarmer/{memberId}") public ResponseEntity convertToFarmer(@PathVariable Long memberId, @RequestBody FarmerQualificationRequest request) { diff --git a/src/main/java/poomasi/domain/auth/signup/dto/request/SignupRequest.java b/src/main/java/poomasi/domain/member/dto/request/SignupRequest.java similarity index 57% rename from src/main/java/poomasi/domain/auth/signup/dto/request/SignupRequest.java rename to src/main/java/poomasi/domain/member/dto/request/SignupRequest.java index 7799d74a..447bdf9b 100644 --- a/src/main/java/poomasi/domain/auth/signup/dto/request/SignupRequest.java +++ b/src/main/java/poomasi/domain/member/dto/request/SignupRequest.java @@ -1,4 +1,4 @@ -package poomasi.domain.auth.signup.dto.request; +package poomasi.domain.member.dto.request; public record SignupRequest(String email, String password) { } diff --git a/src/main/java/poomasi/domain/auth/signup/dto/response/SignUpResponse.java b/src/main/java/poomasi/domain/member/dto/response/SignUpResponse.java similarity index 56% rename from src/main/java/poomasi/domain/auth/signup/dto/response/SignUpResponse.java rename to src/main/java/poomasi/domain/member/dto/response/SignUpResponse.java index 70da0d1c..02280570 100644 --- a/src/main/java/poomasi/domain/auth/signup/dto/response/SignUpResponse.java +++ b/src/main/java/poomasi/domain/member/dto/response/SignUpResponse.java @@ -1,4 +1,4 @@ -package poomasi.domain.auth.signup.dto.response; +package poomasi.domain.member.dto.response; public record SignUpResponse(String email, String message) { } diff --git a/src/main/java/poomasi/domain/member/service/MemberService.java b/src/main/java/poomasi/domain/member/service/MemberService.java index b362706b..30a95582 100644 --- a/src/main/java/poomasi/domain/member/service/MemberService.java +++ b/src/main/java/poomasi/domain/member/service/MemberService.java @@ -1,13 +1,18 @@ package poomasi.domain.member.service; +import jdk.jfr.Description; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import poomasi.domain.member.dto.response.MemberResponse; +import poomasi.domain.member.entity.LoginType; import poomasi.domain.member.entity.Member; import poomasi.domain.member.repository.MemberRepository; +import poomasi.domain.member.dto.request.SignupRequest; +import poomasi.domain.member.dto.response.SignUpResponse; import poomasi.global.error.BusinessException; import static poomasi.domain.member.entity.Role.ROLE_CUSTOMER; @@ -20,6 +25,25 @@ public class MemberService { private final MemberRepository memberRepository; + private final PasswordEncoder passwordEncoder; + + @Description("카카오톡으로 먼저 회원가입이 되어 있는 경우, 계정 연동을 진행합니다. ") + @Transactional + public SignUpResponse signUp(SignupRequest signupRequest) { + String email = signupRequest.email(); + String password = signupRequest.password(); + + memberRepository.findByEmail(email) + .ifPresent(member -> { throw new BusinessException(DUPLICATE_MEMBER_EMAIL); }); + + Member newMember = new Member(email, + passwordEncoder.encode(password), + LoginType.LOCAL, + ROLE_CUSTOMER); + + memberRepository.save(newMember); + return new SignUpResponse(email, "회원 가입 성공"); + } public MemberResponse getMemberById(Long memberId) { Member member = findMemberById(memberId); From 9001e53336e19ea2ad78bcc9a4ce922ca0d77ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Thu, 7 Nov 2024 18:12:06 +0900 Subject: [PATCH 03/59] =?UTF-8?q?refactor:=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=82=B4=20isFarmer,=20isCustome?= =?UTF-8?q?r=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=82=AD=EC=A0=9C=20#81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/service/MemberService.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/main/java/poomasi/domain/member/service/MemberService.java b/src/main/java/poomasi/domain/member/service/MemberService.java index 30a95582..b2b76ed5 100644 --- a/src/main/java/poomasi/domain/member/service/MemberService.java +++ b/src/main/java/poomasi/domain/member/service/MemberService.java @@ -60,7 +60,7 @@ public Page getAllMembers(Pageable pageable) { public void convertToFarmer(Long memberId, Boolean hasFarmerQualification) { Member member = findMemberById(memberId); - if (isFarmer(memberId)) { + if (member.isFarmer()) { throw new BusinessException(MEMBER_ALREADY_FARMER); } @@ -76,7 +76,7 @@ public void convertToFarmer(Long memberId, Boolean hasFarmerQualification) { public void convertToCustomer(Long memberId) { Member member = findMemberById(memberId); - if (isCustomer(memberId)) { + if (member.isCustomer()) { throw new BusinessException(MEMBER_ALREADY_CUSTOMER); } @@ -89,16 +89,6 @@ public Member findMemberById(Long memberId) { .orElseThrow(() -> new BusinessException(MEMBER_NOT_FOUND)); } - public boolean isCustomer(Long memberId) { - Member member = findMemberById(memberId); - return member.isCustomer(); - } - - public boolean isFarmer(Long memberId) { - Member member = findMemberById(memberId); - return member.isFarmer(); - } - public boolean isAdmin(Long memberId) { Member member = findMemberById(memberId); return member.isAdmin(); From afbac6fb4870aadc8726ab84ef9add31e91740dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Fri, 8 Nov 2024 17:04:15 +0900 Subject: [PATCH 04/59] =?UTF-8?q?feat:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=EC=97=90=20=EB=A9=A4=EB=B2=84=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EB=A9=A4?= =?UTF-8?q?=EB=B2=84=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=EC=9E=90,=20=EB=A9=A4=EB=B2=84=20=EA=B6=8C=ED=95=9C?= =?UTF-8?q?=20=EB=B3=84=EB=A1=9C=20=EB=B6=84=EB=A6=AC=20#81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/image/entity/ImageType.java | 2 +- .../domain/image/service/ImageService.java | 5 +++- .../member/controller/MemberController.java | 13 ++++++++++ .../dto/response/MemberProfileResponse.java | 7 ++++-- .../dto/response/MemberSummaryResponse.java | 13 ++++++++++ .../domain/member/entity/MemberProfile.java | 25 +++++++++++-------- .../domain/member/service/MemberService.java | 7 +++++- .../config/s3/S3PresignedUrlService.java | 2 +- 8 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 src/main/java/poomasi/domain/member/dto/response/MemberSummaryResponse.java diff --git a/src/main/java/poomasi/domain/image/entity/ImageType.java b/src/main/java/poomasi/domain/image/entity/ImageType.java index 8c6f5929..bcdc50b5 100644 --- a/src/main/java/poomasi/domain/image/entity/ImageType.java +++ b/src/main/java/poomasi/domain/image/entity/ImageType.java @@ -1,5 +1,5 @@ package poomasi.domain.image.entity; public enum ImageType { - FARM, FARM_REVIEW, PRODUCT, PRODUCT_REVIEW + FARM, FARM_REVIEW, PRODUCT, PRODUCT_REVIEW, MEMBER } \ No newline at end of file diff --git a/src/main/java/poomasi/domain/image/service/ImageService.java b/src/main/java/poomasi/domain/image/service/ImageService.java index 48157033..0bb9d201 100644 --- a/src/main/java/poomasi/domain/image/service/ImageService.java +++ b/src/main/java/poomasi/domain/image/service/ImageService.java @@ -50,7 +50,10 @@ private Image recoverImageOrThrow(Image existingImage, ImageRequest imageRequest } private void validateImageLimit(ImageRequest imageRequest) { - if (imageRepository.countByTypeAndReferenceIdAndDeletedAtIsNull(imageRequest.type(), imageRequest.referenceId()) >= 5) { + int imageLimit = 5; + if (imageRequest.type() == ImageType.MEMBER) imageLimit = 1; // 멤버 프로필 이미지는 한 장으로 제한 + + if (imageRepository.countByTypeAndReferenceIdAndDeletedAtIsNull(imageRequest.type(), imageRequest.referenceId()) >= imageLimit) { throw new BusinessException(IMAGE_LIMIT_EXCEED); } } diff --git a/src/main/java/poomasi/domain/member/controller/MemberController.java b/src/main/java/poomasi/domain/member/controller/MemberController.java index 91f04d05..73befb94 100644 --- a/src/main/java/poomasi/domain/member/controller/MemberController.java +++ b/src/main/java/poomasi/domain/member/controller/MemberController.java @@ -8,6 +8,7 @@ import org.springframework.web.bind.annotation.*; import poomasi.domain.member.dto.request.FarmerQualificationRequest; import poomasi.domain.member.dto.response.MemberResponse; +import poomasi.domain.member.dto.response.MemberSummaryResponse; import poomasi.domain.member.service.MemberService; import poomasi.domain.member.dto.request.SignupRequest; import poomasi.domain.member.dto.response.SignUpResponse; @@ -50,5 +51,17 @@ public ResponseEntity> getMembers(@PageableDefault(size = 1 return ResponseEntity.ok(memberResponses); } + @GetMapping("/self") + public ResponseEntity getSelfMember(@PathVariable Long memberId) { + MemberResponse memberResponse = memberService.getMemberById(memberId); + return ResponseEntity.ok(memberResponse); + } + + @GetMapping("/summary/{memberId}") + public ResponseEntity getMemberSummaryById(@PathVariable Long memberId) { + MemberSummaryResponse memberSummaryResponse = memberService.getMemberSummary(memberId); + return ResponseEntity.ok(memberSummaryResponse); + } + } diff --git a/src/main/java/poomasi/domain/member/dto/response/MemberProfileResponse.java b/src/main/java/poomasi/domain/member/dto/response/MemberProfileResponse.java index 9a36d9e7..c01ee260 100644 --- a/src/main/java/poomasi/domain/member/dto/response/MemberProfileResponse.java +++ b/src/main/java/poomasi/domain/member/dto/response/MemberProfileResponse.java @@ -1,5 +1,6 @@ package poomasi.domain.member.dto.response; +import poomasi.domain.image.entity.Image; import poomasi.domain.member.entity.MemberProfile; import java.time.LocalDateTime; @@ -12,7 +13,8 @@ public record MemberProfileResponse( Long coordinateX, Long coordinateY, boolean isBanned, - LocalDateTime createdAt + LocalDateTime createdAt, + Image profileImage ) { public static MemberProfileResponse fromEntity(MemberProfile profile) { return new MemberProfileResponse( @@ -23,7 +25,8 @@ public static MemberProfileResponse fromEntity(MemberProfile profile) { profile.getCoordinateX(), profile.getCoordinateY(), profile.isBanned(), - profile.getCreatedAt() + profile.getCreatedAt(), + profile.getProfileImage() ); } } \ No newline at end of file diff --git a/src/main/java/poomasi/domain/member/dto/response/MemberSummaryResponse.java b/src/main/java/poomasi/domain/member/dto/response/MemberSummaryResponse.java new file mode 100644 index 00000000..9691eab0 --- /dev/null +++ b/src/main/java/poomasi/domain/member/dto/response/MemberSummaryResponse.java @@ -0,0 +1,13 @@ +package poomasi.domain.member.dto.response; + +import poomasi.domain.image.entity.Image; +import poomasi.domain.member.entity.Member; + +public record MemberSummaryResponse(String name, Image profileImage) { + public static MemberSummaryResponse fromEntity(Member member) { + return new MemberSummaryResponse( + member.getMemberProfile().getName(), + member.getMemberProfile().getProfileImage() + ); + } +} diff --git a/src/main/java/poomasi/domain/member/entity/MemberProfile.java b/src/main/java/poomasi/domain/member/entity/MemberProfile.java index 6a599a71..7897261f 100644 --- a/src/main/java/poomasi/domain/member/entity/MemberProfile.java +++ b/src/main/java/poomasi/domain/member/entity/MemberProfile.java @@ -1,14 +1,16 @@ package poomasi.domain.member.entity; import jakarta.persistence.*; -import lombok.Getter; -import lombok.Setter; +import lombok.*; +import poomasi.domain.image.entity.Image; import java.time.LocalDateTime; @Getter @Entity @Table(name = "member_profile") +@AllArgsConstructor +@Builder public class MemberProfile { @Id @@ -33,29 +35,30 @@ public class MemberProfile { @Column(nullable=true, length=255) private Long coordinateY; - @Column(nullable = true, length = 50) - private boolean isBanned; + @Column(nullable = false) + @Builder.Default + private boolean isBanned = false; @Column(nullable = false) private LocalDateTime createdAt; + @Setter + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "profile_image_id") + private Image profileImage; + @Setter @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id", referencedColumnName = "id") private Member member; - public MemberProfile(String name, String phoneNumber, String address, Member member) { - this.name = name; - this.phoneNumber = phoneNumber; - this.address = address; - this.isBanned = false; + @PrePersist + public void prePersist() { this.createdAt = LocalDateTime.now(); - this.member = member; } public MemberProfile() { this.name = "UNKNOWN"; // name not null 조건 때문에 임시로 넣었습니다. nullable도 true로 넣었는데 안 되네요 - this.createdAt = LocalDateTime.now(); } } diff --git a/src/main/java/poomasi/domain/member/service/MemberService.java b/src/main/java/poomasi/domain/member/service/MemberService.java index b2b76ed5..d7fbf6bc 100644 --- a/src/main/java/poomasi/domain/member/service/MemberService.java +++ b/src/main/java/poomasi/domain/member/service/MemberService.java @@ -8,6 +8,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import poomasi.domain.member.dto.response.MemberResponse; +import poomasi.domain.member.dto.response.MemberSummaryResponse; import poomasi.domain.member.entity.LoginType; import poomasi.domain.member.entity.Member; import poomasi.domain.member.repository.MemberRepository; @@ -50,12 +51,16 @@ public MemberResponse getMemberById(Long memberId) { return MemberResponse.fromEntity(member); } + public MemberSummaryResponse getMemberSummary(Long memberId) { + Member member = findMemberById(memberId); + return MemberSummaryResponse.fromEntity(member); + } + public Page getAllMembers(Pageable pageable) { Page members = memberRepository.findAll(pageable); return members.map(MemberResponse::fromEntity); } - @Transactional public void convertToFarmer(Long memberId, Boolean hasFarmerQualification) { Member member = findMemberById(memberId); diff --git a/src/main/java/poomasi/global/config/s3/S3PresignedUrlService.java b/src/main/java/poomasi/global/config/s3/S3PresignedUrlService.java index 12330c8f..241375f3 100644 --- a/src/main/java/poomasi/global/config/s3/S3PresignedUrlService.java +++ b/src/main/java/poomasi/global/config/s3/S3PresignedUrlService.java @@ -52,7 +52,7 @@ public String createPresignedPutUrl(String bucketName, String keyPrefix, Map 그럴일 거의 없긴할텐데 생기면 s3 원래 파일 지워짐 String uniqueIdentifier = UUID.randomUUID().toString(); From fb0e7b097d9550ad70928e9ac25647a43a7783e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Fri, 8 Nov 2024 17:35:26 +0900 Subject: [PATCH 05/59] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=20Security=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20#81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/poomasi/domain/member/controller/MemberController.java b/src/main/java/poomasi/domain/member/controller/MemberController.java index 73befb94..8f057dba 100644 --- a/src/main/java/poomasi/domain/member/controller/MemberController.java +++ b/src/main/java/poomasi/domain/member/controller/MemberController.java @@ -5,10 +5,14 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; +import poomasi.domain.auth.security.userdetail.UserDetailsImpl; import poomasi.domain.member.dto.request.FarmerQualificationRequest; import poomasi.domain.member.dto.response.MemberResponse; import poomasi.domain.member.dto.response.MemberSummaryResponse; +import poomasi.domain.member.entity.Member; import poomasi.domain.member.service.MemberService; import poomasi.domain.member.dto.request.SignupRequest; import poomasi.domain.member.dto.response.SignUpResponse; @@ -27,6 +31,7 @@ public ResponseEntity signUp(@RequestBody SignupRequest signupRe } @PutMapping("/toFarmer/{memberId}") + @Secured("ROLE_ADMIN") public ResponseEntity convertToFarmer(@PathVariable Long memberId, @RequestBody FarmerQualificationRequest request) { memberService.convertToFarmer(memberId, request.hasFarmerQualification()); @@ -34,30 +39,36 @@ public ResponseEntity convertToFarmer(@PathVariable Long memberId, } @PutMapping("/toCustomer/{memberId}") + @Secured("ROLE_ADMIN") public ResponseEntity convertToCustomer(@PathVariable Long memberId) { memberService.convertToCustomer(memberId); return ResponseEntity.noContent().build(); } @GetMapping("/{memberId}") + @Secured("ROLE_ADMIN") public ResponseEntity getMemberById(@PathVariable Long memberId) { MemberResponse memberResponse = memberService.getMemberById(memberId); return ResponseEntity.ok(memberResponse); } @GetMapping + @Secured("ROLE_ADMIN") public ResponseEntity> getMembers(@PageableDefault(size = 10) Pageable pageable) { Page memberResponses = memberService.getAllMembers(pageable); return ResponseEntity.ok(memberResponses); } @GetMapping("/self") - public ResponseEntity getSelfMember(@PathVariable Long memberId) { - MemberResponse memberResponse = memberService.getMemberById(memberId); + @Secured({"ROLE_MEMBER", "ROLE_FARMER"}) + public ResponseEntity getSelfMember(@AuthenticationPrincipal UserDetailsImpl userDetails) { + Member member = userDetails.getMember(); + MemberResponse memberResponse = memberService.getMemberById(member.getId()); return ResponseEntity.ok(memberResponse); } @GetMapping("/summary/{memberId}") + @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) public ResponseEntity getMemberSummaryById(@PathVariable Long memberId) { MemberSummaryResponse memberSummaryResponse = memberService.getMemberSummary(memberId); return ResponseEntity.ok(memberSummaryResponse); From d012610bb7d08ba4b5cbd65726b642a0dabf27ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Fri, 8 Nov 2024 21:49:35 +0900 Subject: [PATCH 06/59] =?UTF-8?q?feat:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=EC=97=90=20=EB=A9=A4=EB=B2=84=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=95=84=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20Security=20=EC=A0=81=EC=9A=A9=20#81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/auth/config/SecurityConfig.java | 1 + .../OAuth2UserDetailServiceImpl.java | 2 +- .../image/controller/ImageController.java | 40 ++++++++---- .../domain/image/dto/ImageRequest.java | 2 +- .../domain/image/entity/ImageType.java | 2 +- .../domain/image/service/ImageService.java | 62 ++++++++++++++++--- .../image/validation/FarmOwnerValidator.java | 19 ++++++ .../image/validation/ImageOwnerValidator.java | 5 ++ .../ImageOwnerValidatorFactory.java | 27 ++++++++ .../MemberProfileOwnerValidator.java | 18 ++++++ .../validation/ProductOwnerValidator.java | 19 ++++++ .../validation/ReviewOwnerValidator.java | 18 ++++++ .../{ => _profile}/entity/MemberProfile.java | 3 +- .../repository/MemberProfileRepository.java | 7 +++ .../dto/response/MemberProfileResponse.java | 2 +- .../poomasi/domain/member/entity/Member.java | 1 + .../poomasi/domain/review/entity/Review.java | 15 ++--- .../poomasi/global/error/BusinessError.java | 6 +- 18 files changed, 213 insertions(+), 36 deletions(-) create mode 100644 src/main/java/poomasi/domain/image/validation/FarmOwnerValidator.java create mode 100644 src/main/java/poomasi/domain/image/validation/ImageOwnerValidator.java create mode 100644 src/main/java/poomasi/domain/image/validation/ImageOwnerValidatorFactory.java create mode 100644 src/main/java/poomasi/domain/image/validation/MemberProfileOwnerValidator.java create mode 100644 src/main/java/poomasi/domain/image/validation/ProductOwnerValidator.java create mode 100644 src/main/java/poomasi/domain/image/validation/ReviewOwnerValidator.java rename src/main/java/poomasi/domain/member/{ => _profile}/entity/MemberProfile.java (94%) create mode 100644 src/main/java/poomasi/domain/member/_profile/repository/MemberProfileRepository.java diff --git a/src/main/java/poomasi/domain/auth/config/SecurityConfig.java b/src/main/java/poomasi/domain/auth/config/SecurityConfig.java index 4bef2180..0e91c3c4 100644 --- a/src/main/java/poomasi/domain/auth/config/SecurityConfig.java +++ b/src/main/java/poomasi/domain/auth/config/SecurityConfig.java @@ -77,6 +77,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .requestMatchers(HttpMethod.GET, "/api/farm/**").permitAll() .requestMatchers(HttpMethod.GET, "/api/product/**").permitAll() .requestMatchers(HttpMethod.GET, "/api/review/**").permitAll() + .requestMatchers(HttpMethod.GET, "/api/image/**").permitAll() .requestMatchers("/api/sign-up", "/api/login", "api/reissue").permitAll() .requestMatchers("/api/need-auth/**").authenticated() .anyRequest(). diff --git a/src/main/java/poomasi/domain/auth/security/userdetail/OAuth2UserDetailServiceImpl.java b/src/main/java/poomasi/domain/auth/security/userdetail/OAuth2UserDetailServiceImpl.java index 78dda49a..34e8a261 100644 --- a/src/main/java/poomasi/domain/auth/security/userdetail/OAuth2UserDetailServiceImpl.java +++ b/src/main/java/poomasi/domain/auth/security/userdetail/OAuth2UserDetailServiceImpl.java @@ -11,7 +11,7 @@ import poomasi.domain.auth.security.oauth2.dto.response.OAuth2Response; import poomasi.domain.member.entity.LoginType; import poomasi.domain.member.entity.Member; -import poomasi.domain.member.entity.MemberProfile; +import poomasi.domain.member._profile.entity.MemberProfile; import poomasi.domain.member.entity.Role; import poomasi.domain.member.repository.MemberRepository; diff --git a/src/main/java/poomasi/domain/image/controller/ImageController.java b/src/main/java/poomasi/domain/image/controller/ImageController.java index 38c2f524..d9211481 100644 --- a/src/main/java/poomasi/domain/image/controller/ImageController.java +++ b/src/main/java/poomasi/domain/image/controller/ImageController.java @@ -2,11 +2,15 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; +import poomasi.domain.auth.security.userdetail.UserDetailsImpl; import poomasi.domain.image.dto.ImageRequest; import poomasi.domain.image.entity.Image; import poomasi.domain.image.entity.ImageType; import poomasi.domain.image.service.ImageService; +import poomasi.domain.member.entity.Member; import java.util.List; @@ -18,22 +22,28 @@ public class ImageController { // 이미지 정보 저장 @PostMapping - public ResponseEntity saveImageInfo(@RequestBody ImageRequest imageRequest) { - Image savedImage = imageService.saveImage(imageRequest); - return ResponseEntity.ok(savedImage); + @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) + public ResponseEntity saveImageInfo(@AuthenticationPrincipal UserDetailsImpl userDetails, @RequestBody ImageRequest imageRequest) { + Member member = userDetails.getMember(); + Image savedImage = imageService.saveImage(member.getId(), imageRequest); + return ResponseEntity.ok(savedImage); } // 여러 이미지 정보 저장 @PostMapping("/multiple") - public ResponseEntity> saveMultipleImages(@RequestBody List imageRequests) { - List savedImages = imageService.saveMultipleImages(imageRequests); + @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) + public ResponseEntity> saveMultipleImages(@AuthenticationPrincipal UserDetailsImpl userDetails, @RequestBody List imageRequests) { + Member member = userDetails.getMember(); + List savedImages = imageService.saveMultipleImages(member.getId(), imageRequests); return ResponseEntity.ok(savedImages); } // 특정 이미지 삭제 @DeleteMapping("/delete/{id}") - public ResponseEntity deleteImage(@PathVariable Long id) { - imageService.deleteImage(id); + @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) + public ResponseEntity deleteImage(@AuthenticationPrincipal UserDetailsImpl userDetails, @PathVariable Long id) { + Member member = userDetails.getMember(); + imageService.deleteImage(member.getId(), id); return ResponseEntity.noContent().build(); } @@ -51,15 +61,21 @@ public ResponseEntity> getImagesByTypeAndReference(@PathVariable Ima } // 이미지 정보 수정 - @PutMapping("/{id}") - public ResponseEntity updateImageInfo(@PathVariable Long id, @RequestBody ImageRequest imageRequest) { - Image updatedImage = imageService.updateImage(id, imageRequest); + @PutMapping("update/{id}") + @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) + public ResponseEntity updateImageInfo(@AuthenticationPrincipal UserDetailsImpl userDetails, + @PathVariable Long id, + @RequestBody ImageRequest imageRequest) { + Member member = userDetails.getMember(); + Image updatedImage = imageService.updateImage(member.getId(), id, imageRequest); return ResponseEntity.ok(updatedImage); } @PutMapping("/recover/{id}") - public ResponseEntity recoverImage(@PathVariable Long id) { - imageService.recoverImage(id); + @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) + public ResponseEntity recoverImage(@AuthenticationPrincipal UserDetailsImpl userDetails, @PathVariable Long id) { + Member member = userDetails.getMember(); + imageService.recoverImage(member.getId(), id); return ResponseEntity.noContent().build(); } diff --git a/src/main/java/poomasi/domain/image/dto/ImageRequest.java b/src/main/java/poomasi/domain/image/dto/ImageRequest.java index d6e0521f..7589c9a0 100644 --- a/src/main/java/poomasi/domain/image/dto/ImageRequest.java +++ b/src/main/java/poomasi/domain/image/dto/ImageRequest.java @@ -9,7 +9,7 @@ public Image toEntity(ImageRequest imageRequest){ imageRequest.objectKey, imageRequest.imageUrl, imageRequest.type, - imageRequest.referenceId + imageRequest.referenceId // 타입이 멤버 프로필일 경우 멤버 id가 아닌 멤버 프로필 id를 넣습니다. ); } } diff --git a/src/main/java/poomasi/domain/image/entity/ImageType.java b/src/main/java/poomasi/domain/image/entity/ImageType.java index bcdc50b5..1b2b30fd 100644 --- a/src/main/java/poomasi/domain/image/entity/ImageType.java +++ b/src/main/java/poomasi/domain/image/entity/ImageType.java @@ -1,5 +1,5 @@ package poomasi.domain.image.entity; public enum ImageType { - FARM, FARM_REVIEW, PRODUCT, PRODUCT_REVIEW, MEMBER + FARM, FARM_REVIEW, PRODUCT, PRODUCT_REVIEW, MEMBER_PROFILE } \ No newline at end of file diff --git a/src/main/java/poomasi/domain/image/service/ImageService.java b/src/main/java/poomasi/domain/image/service/ImageService.java index 0bb9d201..eede9992 100644 --- a/src/main/java/poomasi/domain/image/service/ImageService.java +++ b/src/main/java/poomasi/domain/image/service/ImageService.java @@ -7,6 +7,12 @@ import poomasi.domain.image.entity.Image; import poomasi.domain.image.entity.ImageType; import poomasi.domain.image.repository.ImageRepository; +import poomasi.domain.image.validation.ImageOwnerValidator; +import poomasi.domain.image.validation.ImageOwnerValidatorFactory; +import poomasi.domain.member._profile.entity.MemberProfile; +import poomasi.domain.member._profile.repository.MemberProfileRepository; +import poomasi.domain.member.entity.Member; +import poomasi.domain.member.repository.MemberRepository; import poomasi.global.error.BusinessException; import java.util.Date; @@ -21,19 +27,46 @@ @Transactional(readOnly = true) public class ImageService { private final ImageRepository imageRepository; + private final ImageOwnerValidatorFactory validatorFactory; + private final MemberRepository memberRepository; + private final MemberProfileRepository memberProfileRepository; + @Transactional - public Image saveImage(ImageRequest imageRequest) { + public Image saveImage(Long memberId, ImageRequest imageRequest) { // 기존 이미지가 있는 경우 복구 또는 예외 처리 (실제 복구 로직과는 차이가 있음) + validateImageOwner(memberId, imageRequest.type(), imageRequest.referenceId()); validateImageLimit(imageRequest); Image image = findExistingOrRecoverableImage(imageRequest) .map(existingImage -> recoverImageOrThrow(existingImage, imageRequest)) .orElseGet(() -> imageRequest.toEntity(imageRequest)); + if (imageRequest.type() == ImageType.MEMBER_PROFILE) { + linkImageToMemberProfile(imageRequest.referenceId(), image); + } + return imageRepository.save(image); } + // 이미지 주인이 맞는지 검증 + private void validateImageOwner(Long memberId, ImageType type, Long referenceId) { + // 관리자는 제외 + if (isAdmin(memberId)) { + return; + } + ImageOwnerValidator validator = validatorFactory.getValidator(type); + if (validator != null && !validator.validateOwner(memberId, referenceId)) { + throw new BusinessException(IMAGE_OWNER_MISMATCH); + } + } + + private boolean isAdmin(Long memberId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new BusinessException(MEMBER_NOT_FOUND)); + return member.isAdmin(); + } + private Optional findExistingOrRecoverableImage(ImageRequest imageRequest) { return imageRepository.findByObjectKeyAndTypeAndReferenceId( imageRequest.objectKey(), imageRequest.type(), imageRequest.referenceId()); @@ -51,23 +84,32 @@ private Image recoverImageOrThrow(Image existingImage, ImageRequest imageRequest private void validateImageLimit(ImageRequest imageRequest) { int imageLimit = 5; - if (imageRequest.type() == ImageType.MEMBER) imageLimit = 1; // 멤버 프로필 이미지는 한 장으로 제한 + if (imageRequest.type() == ImageType.MEMBER_PROFILE) imageLimit = 1; // 멤버 프로필 이미지는 한 장으로 제한 if (imageRepository.countByTypeAndReferenceIdAndDeletedAtIsNull(imageRequest.type(), imageRequest.referenceId()) >= imageLimit) { throw new BusinessException(IMAGE_LIMIT_EXCEED); } } + private void linkImageToMemberProfile(Long referenceId, Image savedImage) { + MemberProfile memberProfile = memberProfileRepository.findById(referenceId) + .orElseThrow(() -> new BusinessException(MEMBER_PROFILE_NOT_FOUND)); + memberProfile.setProfileImage(savedImage); + memberProfileRepository.save(memberProfile); + } + // 여러 이미지 저장 @Transactional - public List saveMultipleImages(List imageRequests) { + public List saveMultipleImages(Long memberId, List imageRequests) { return imageRequests.stream() - .map(this::saveImage) + .map(imageRequest -> saveImage(memberId, imageRequest)) .collect(Collectors.toList()); } @Transactional - public void deleteImage(Long id) { + public void deleteImage(Long memberId, Long id) { + Image image = getImageById(id); + validateImageOwner(memberId, image.getType(), image.getReferenceId()); imageRepository.deleteById(id); } @@ -82,8 +124,9 @@ public List getImagesByTypeAndReferenceId(ImageType type, Long referenceI // 이미지 수정 @Transactional - public Image updateImage(Long id, ImageRequest imageRequest) { + public Image updateImage(Long memberId, Long id, ImageRequest imageRequest) { Image image = getImageById(id); + validateImageOwner(memberId, image.getType(), image.getReferenceId()); if (!image.getType().equals(imageRequest.type()) || !image.getReferenceId().equals(imageRequest.referenceId())) { @@ -96,9 +139,9 @@ public Image updateImage(Long id, ImageRequest imageRequest) { } @Transactional - public void recoverImage(Long id) { - Image image = imageRepository.findById(id) - .orElseThrow(() -> new BusinessException(IMAGE_NOT_FOUND)); + public void recoverImage(Long memberId, Long id) { + Image image = getImageById(id); + validateImageOwner(memberId, image.getType(), image.getReferenceId()); if (image.getDeletedAt() == null) { throw new BusinessException(IMAGE_ALREADY_EXISTS); @@ -109,4 +152,5 @@ public void recoverImage(Long id) { image.setDeletedAt(null); imageRepository.save(image); } + } \ No newline at end of file diff --git a/src/main/java/poomasi/domain/image/validation/FarmOwnerValidator.java b/src/main/java/poomasi/domain/image/validation/FarmOwnerValidator.java new file mode 100644 index 00000000..ca376272 --- /dev/null +++ b/src/main/java/poomasi/domain/image/validation/FarmOwnerValidator.java @@ -0,0 +1,19 @@ +package poomasi.domain.image.validation; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import poomasi.domain.farm.repository.FarmRepository; + + +@Component +@RequiredArgsConstructor +public class FarmOwnerValidator implements ImageOwnerValidator { + private final FarmRepository farmRepository; + + @Override + public boolean validateOwner(Long memberId, Long referenceId) { + return farmRepository.findById(referenceId) + .filter(farm -> farm.getOwnerId().equals(memberId)) + .isPresent(); + } +} diff --git a/src/main/java/poomasi/domain/image/validation/ImageOwnerValidator.java b/src/main/java/poomasi/domain/image/validation/ImageOwnerValidator.java new file mode 100644 index 00000000..8b51556f --- /dev/null +++ b/src/main/java/poomasi/domain/image/validation/ImageOwnerValidator.java @@ -0,0 +1,5 @@ +package poomasi.domain.image.validation; + +public interface ImageOwnerValidator { + boolean validateOwner(Long memberId, Long referenceId); +} \ No newline at end of file diff --git a/src/main/java/poomasi/domain/image/validation/ImageOwnerValidatorFactory.java b/src/main/java/poomasi/domain/image/validation/ImageOwnerValidatorFactory.java new file mode 100644 index 00000000..d22141b2 --- /dev/null +++ b/src/main/java/poomasi/domain/image/validation/ImageOwnerValidatorFactory.java @@ -0,0 +1,27 @@ +package poomasi.domain.image.validation; + +import org.springframework.stereotype.Component; +import poomasi.domain.image.entity.ImageType; + +import java.util.EnumMap; +import java.util.Map; + +@Component +public class ImageOwnerValidatorFactory { + private final Map validators = new EnumMap<>(ImageType.class); + + public ImageOwnerValidatorFactory(FarmOwnerValidator farmOwnerValidator, + ProductOwnerValidator productOwnerValidator, + ReviewOwnerValidator reviewOwnerValidator, + MemberProfileOwnerValidator memberProfileOwnerValidator) { + validators.put(ImageType.FARM, farmOwnerValidator); + validators.put(ImageType.PRODUCT, productOwnerValidator); + validators.put(ImageType.FARM_REVIEW, reviewOwnerValidator); + validators.put(ImageType.PRODUCT_REVIEW, reviewOwnerValidator); + validators.put(ImageType.MEMBER_PROFILE, memberProfileOwnerValidator); + } + + public ImageOwnerValidator getValidator(ImageType type) { + return validators.get(type); + } +} diff --git a/src/main/java/poomasi/domain/image/validation/MemberProfileOwnerValidator.java b/src/main/java/poomasi/domain/image/validation/MemberProfileOwnerValidator.java new file mode 100644 index 00000000..b597d5b0 --- /dev/null +++ b/src/main/java/poomasi/domain/image/validation/MemberProfileOwnerValidator.java @@ -0,0 +1,18 @@ +package poomasi.domain.image.validation; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import poomasi.domain.member._profile.repository.MemberProfileRepository; + +@Component +@RequiredArgsConstructor +public class MemberProfileOwnerValidator implements ImageOwnerValidator{ + private final MemberProfileRepository memberProfileRepository; + + @Override + public boolean validateOwner(Long memberId, Long referenceId) { + return memberProfileRepository.findById(referenceId) + .filter(memberProfile -> memberProfile.getMember().getId().equals(memberId)) + .isPresent(); + } +} diff --git a/src/main/java/poomasi/domain/image/validation/ProductOwnerValidator.java b/src/main/java/poomasi/domain/image/validation/ProductOwnerValidator.java new file mode 100644 index 00000000..6afc0bfb --- /dev/null +++ b/src/main/java/poomasi/domain/image/validation/ProductOwnerValidator.java @@ -0,0 +1,19 @@ +package poomasi.domain.image.validation; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import poomasi.domain.product.repository.ProductRepository; + +@Component +@RequiredArgsConstructor +public class ProductOwnerValidator implements ImageOwnerValidator{ + private final ProductRepository productRepository; + + @Override + public boolean validateOwner(Long memberId, Long referenceId) { + return productRepository.findById(referenceId) + .filter(product -> product.getFarmerId().equals(memberId)) + .isPresent(); + } +} + diff --git a/src/main/java/poomasi/domain/image/validation/ReviewOwnerValidator.java b/src/main/java/poomasi/domain/image/validation/ReviewOwnerValidator.java new file mode 100644 index 00000000..57073e2d --- /dev/null +++ b/src/main/java/poomasi/domain/image/validation/ReviewOwnerValidator.java @@ -0,0 +1,18 @@ +package poomasi.domain.image.validation; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import poomasi.domain.review.repository.ReviewRepository; + +@Component +@RequiredArgsConstructor +public class ReviewOwnerValidator implements ImageOwnerValidator{ + private final ReviewRepository reviewRepository; + + @Override + public boolean validateOwner(Long memberId, Long referenceId) { + return reviewRepository.findById(referenceId) + .filter(review -> review.getReviewer().getId().equals(memberId)) + .isPresent(); + } +} diff --git a/src/main/java/poomasi/domain/member/entity/MemberProfile.java b/src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java similarity index 94% rename from src/main/java/poomasi/domain/member/entity/MemberProfile.java rename to src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java index 7897261f..10359311 100644 --- a/src/main/java/poomasi/domain/member/entity/MemberProfile.java +++ b/src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java @@ -1,8 +1,9 @@ -package poomasi.domain.member.entity; +package poomasi.domain.member._profile.entity; import jakarta.persistence.*; import lombok.*; import poomasi.domain.image.entity.Image; +import poomasi.domain.member.entity.Member; import java.time.LocalDateTime; diff --git a/src/main/java/poomasi/domain/member/_profile/repository/MemberProfileRepository.java b/src/main/java/poomasi/domain/member/_profile/repository/MemberProfileRepository.java new file mode 100644 index 00000000..ee260f12 --- /dev/null +++ b/src/main/java/poomasi/domain/member/_profile/repository/MemberProfileRepository.java @@ -0,0 +1,7 @@ +package poomasi.domain.member._profile.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import poomasi.domain.member._profile.entity.MemberProfile; + +public interface MemberProfileRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/poomasi/domain/member/dto/response/MemberProfileResponse.java b/src/main/java/poomasi/domain/member/dto/response/MemberProfileResponse.java index c01ee260..d7ad6655 100644 --- a/src/main/java/poomasi/domain/member/dto/response/MemberProfileResponse.java +++ b/src/main/java/poomasi/domain/member/dto/response/MemberProfileResponse.java @@ -1,7 +1,7 @@ package poomasi.domain.member.dto.response; import poomasi.domain.image.entity.Image; -import poomasi.domain.member.entity.MemberProfile; +import poomasi.domain.member._profile.entity.MemberProfile; import java.time.LocalDateTime; diff --git a/src/main/java/poomasi/domain/member/entity/Member.java b/src/main/java/poomasi/domain/member/entity/Member.java index be06d08f..17fa525e 100644 --- a/src/main/java/poomasi/domain/member/entity/Member.java +++ b/src/main/java/poomasi/domain/member/entity/Member.java @@ -6,6 +6,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.annotations.SQLDelete; +import poomasi.domain.member._profile.entity.MemberProfile; import poomasi.domain.wishlist.entity.WishList; import java.time.LocalDateTime; diff --git a/src/main/java/poomasi/domain/review/entity/Review.java b/src/main/java/poomasi/domain/review/entity/Review.java index b72da487..e16be6bd 100644 --- a/src/main/java/poomasi/domain/review/entity/Review.java +++ b/src/main/java/poomasi/domain/review/entity/Review.java @@ -1,17 +1,14 @@ package poomasi.domain.review.entity; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; +import jakarta.persistence.*; + import java.time.LocalDateTime; import lombok.Getter; import lombok.NoArgsConstructor; import org.hibernate.annotations.Comment; import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; +import poomasi.domain.member.entity.Member; import poomasi.domain.review.dto.ReviewRequest; @Entity @@ -42,9 +39,9 @@ public class Review { @Enumerated(EnumType.STRING) private EntityType entityType; -// @Comment("작성자") -// @ManyToOne -// private Member reviewer; + @Comment("작성자") + @ManyToOne(fetch = FetchType.LAZY) + private Member reviewer; public Review(Float rating, String content, Long entityId) { this.rating = rating; diff --git a/src/main/java/poomasi/global/error/BusinessError.java b/src/main/java/poomasi/global/error/BusinessError.java index 518376a0..1760c08d 100644 --- a/src/main/java/poomasi/global/error/BusinessError.java +++ b/src/main/java/poomasi/global/error/BusinessError.java @@ -25,6 +25,9 @@ public enum BusinessError { MEMBER_ALREADY_CUSTOMER(HttpStatus.BAD_REQUEST, "이미 고객인 회원입니다."), MEMBER_ALREADY_FARMER(HttpStatus.BAD_REQUEST, "이미 농부인 회원입니다."), + // MemberProfile + MEMBER_PROFILE_NOT_FOUND(HttpStatus.NOT_FOUND, "회원 세부 정보가 존재하지 않습니다."), + // Auth INVALID_CREDENTIAL(HttpStatus.UNAUTHORIZED, "잘못된 비밀번호 입니다."), REFRESH_TOKEN_NOT_FOUND(HttpStatus.NOT_FOUND, "리프레시 토큰이 없습니다."), @@ -56,7 +59,8 @@ public enum BusinessError { // Image IMAGE_LIMIT_EXCEED(HttpStatus.BAD_REQUEST, "사진은 최대 5장까지 등록 가능합니다."), IMAGE_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 이미지가 존재합니다"), - IMAGE_NOT_FOUND(HttpStatus.NOT_FOUND, "이미지를 찾을 수 없습니다."); + IMAGE_NOT_FOUND(HttpStatus.NOT_FOUND, "이미지를 찾을 수 없습니다."), + IMAGE_OWNER_MISMATCH(HttpStatus.FORBIDDEN, "해당 이미지의 소유자가 아닙니다."); private final HttpStatus httpStatus; From 85d68d7434c5c614997c85f3cab82b189a18755a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Fri, 8 Nov 2024 22:08:02 +0900 Subject: [PATCH 07/59] =?UTF-8?q?feat:=20S3=20Security=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20#81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../poomasi/global/config/s3/S3PresignedUrlController.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/poomasi/global/config/s3/S3PresignedUrlController.java b/src/main/java/poomasi/global/config/s3/S3PresignedUrlController.java index 33b6c157..12c3ebe5 100644 --- a/src/main/java/poomasi/global/config/s3/S3PresignedUrlController.java +++ b/src/main/java/poomasi/global/config/s3/S3PresignedUrlController.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; import org.springframework.web.bind.annotation.*; import poomasi.global.config.aws.AwsProperties; import poomasi.global.config.s3.dto.request.PresignedUrlPutRequest; @@ -14,12 +15,14 @@ public class S3PresignedUrlController { private final AwsProperties awsProperties; @GetMapping("/presigned-url-get") + @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) public ResponseEntity presignedUrlGet(@RequestParam String keyname) { String presignedGetUrl = s3PresignedUrlService.createPresignedGetUrl(awsProperties.getS3().getBucket(), keyname); return ResponseEntity.ok(presignedGetUrl); } @PostMapping("/presigned-url-put") + @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) public ResponseEntity presignedUrlPut(@RequestBody PresignedUrlPutRequest request) { String presignedPutUrl = s3PresignedUrlService.createPresignedPutUrl(awsProperties.getS3().getBucket(), request.keyPrefix(), request.metadata()); return ResponseEntity.ok(presignedPutUrl); From 01d0275d5471331a5572af457bcdf197b910a7d9 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Fri, 8 Nov 2024 22:31:11 +0900 Subject: [PATCH 08/59] =?UTF-8?q?build=20:=20=EB=B9=8C=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index 755c5a68..d528e6bf 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,8 @@ plugins { id 'java' id 'org.springframework.boot' version '3.3.1' id 'io.spring.dependency-management' version '1.1.5' + id 'com.apollographql.apollo3' version '4.0.0-beta.7' + } group = 'camp.nextstep.edu' From a6b85ada805ccb735f20c2be288aec05f52d1c65 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Fri, 8 Nov 2024 22:32:06 +0900 Subject: [PATCH 09/59] =?UTF-8?q?feat=20:=20exception=20=EB=B0=8F=20?= =?UTF-8?q?=ED=8F=AC=ED=8A=B8=EC=9B=90=20=EC=8B=A4=ED=8C=A8=20reason=20han?= =?UTF-8?q?dler=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../poomasi/global/error/BusinessError.java | 7 +++++-- .../global/error/PaymentConfirmError.java | 19 +++++++++++++++++++ .../global/error/PaymentConfirmException.java | 12 ++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 src/main/java/poomasi/global/error/PaymentConfirmError.java create mode 100644 src/main/java/poomasi/global/error/PaymentConfirmException.java diff --git a/src/main/java/poomasi/global/error/BusinessError.java b/src/main/java/poomasi/global/error/BusinessError.java index b0944e04..848b83a6 100644 --- a/src/main/java/poomasi/global/error/BusinessError.java +++ b/src/main/java/poomasi/global/error/BusinessError.java @@ -1,5 +1,6 @@ package poomasi.global.error; +import org.apache.catalina.filters.ExpiresFilter; import org.springframework.http.HttpStatus; import lombok.AllArgsConstructor; @@ -11,6 +12,7 @@ public enum BusinessError { // Product PRODUCT_NOT_FOUND(HttpStatus.NOT_FOUND, "상품을 찾을 수 없습니다."), PRODUCT_STOCK_ZERO(HttpStatus.BAD_REQUEST, "재고가 없습니다."), + STOCK_QUANTITY_EXCEEDED(HttpStatus.BAD_REQUEST, "장바구나 수량이 남은 재고를 초과하였습니다"), // Category CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "카테고리를 찾을 수 없습니다."), @@ -29,7 +31,7 @@ public enum BusinessError { REFRESH_TOKEN_NOT_FOUND(HttpStatus.NOT_FOUND, "리프레시 토큰이 없습니다."), REFRESH_TOKEN_NOT_VALID(HttpStatus.UNAUTHORIZED, "리프레시 토큰이 유효하지 않습니다."), - // Farm + // FarmOrder FARM_NOT_FOUND(HttpStatus.NOT_FOUND, "농장을 찾을 수 없습니다."), FARM_OWNER_MISMATCH(HttpStatus.FORBIDDEN, "해당 농장의 소유자가 아닙니다."), FARM_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 농장이 존재합니다."), @@ -68,7 +70,8 @@ public enum BusinessError { // PAYMENT PAYMENT_NOT_FOUND(HttpStatus.NOT_FOUND , "결제를 찾을 수 없습니다."), - PAYMENT_AMOUNT_MISMATCH(HttpStatus.BAD_REQUEST, "사전 결제 금액과 사후 결제 금액이 일치하지 않습니다.") + PAYMENT_AMOUNT_MISMATCH(HttpStatus.BAD_REQUEST, "사전 결제 금액과 사후 결제 금액이 일치하지 않습니다."), + PAYMENT_BAD_REQUEST(HttpStatus.BAD_REQUEST, "잘못 된 결제 요청입니다.") ; private final HttpStatus httpStatus; diff --git a/src/main/java/poomasi/global/error/PaymentConfirmError.java b/src/main/java/poomasi/global/error/PaymentConfirmError.java new file mode 100644 index 00000000..a04ddafb --- /dev/null +++ b/src/main/java/poomasi/global/error/PaymentConfirmError.java @@ -0,0 +1,19 @@ +package poomasi.global.error; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum PaymentConfirmError { + + PAYMENT_PROUCT_CONFIRM_EXCEPTION(HttpStatus.INTERNAL_SERVER_ERROR, "결제 직전 상품 수량 부족") + ; + + private final HttpStatus httpStatus; + private final String reason; + + +} diff --git a/src/main/java/poomasi/global/error/PaymentConfirmException.java b/src/main/java/poomasi/global/error/PaymentConfirmException.java new file mode 100644 index 00000000..ac03ed78 --- /dev/null +++ b/src/main/java/poomasi/global/error/PaymentConfirmException.java @@ -0,0 +1,12 @@ +package poomasi.global.error; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class PaymentConfirmException extends RuntimeException { + + private final PaymentConfirmError paymentConfirmError; + +} From eb0287f8d74e42066633a526452bcc5970ccd1e0 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Fri, 8 Nov 2024 22:33:28 +0900 Subject: [PATCH 10/59] =?UTF-8?q?refactoring=20:=20=EA=B8=B0=EC=A1=B4=20or?= =?UTF-8?q?der/refund=20refactoring=20-=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/order/_refund/entity/Refund.java | 20 ------ .../order/_refund/entity/RefundStatus.java | 29 -------- .../domain/order/entity/AbstractOrder.java | 33 --------- .../poomasi/domain/order/entity/Order.java | 49 ------------- .../order/entity/OrderProductDetails.java | 71 ------------------- .../OrderProductDetailsRepository.java | 10 --- .../order/repository/OrderRepository.java | 14 ---- 7 files changed, 226 deletions(-) delete mode 100644 src/main/java/poomasi/domain/order/_refund/entity/Refund.java delete mode 100644 src/main/java/poomasi/domain/order/_refund/entity/RefundStatus.java delete mode 100644 src/main/java/poomasi/domain/order/entity/AbstractOrder.java delete mode 100644 src/main/java/poomasi/domain/order/entity/Order.java delete mode 100644 src/main/java/poomasi/domain/order/entity/OrderProductDetails.java delete mode 100644 src/main/java/poomasi/domain/order/repository/OrderProductDetailsRepository.java delete mode 100644 src/main/java/poomasi/domain/order/repository/OrderRepository.java diff --git a/src/main/java/poomasi/domain/order/_refund/entity/Refund.java b/src/main/java/poomasi/domain/order/_refund/entity/Refund.java deleted file mode 100644 index 8b51294d..00000000 --- a/src/main/java/poomasi/domain/order/_refund/entity/Refund.java +++ /dev/null @@ -1,20 +0,0 @@ -package poomasi.domain.order._refund.entity; - - -import jakarta.persistence.*; -import poomasi.domain.order.entity.OrderProductDetails; - -@Entity -@Table(name="refund") -public class Refund { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - -/* @OneToOne(fetch = FetchType.LAZY) - private OrderProductDetails orderProductDetails;*/ - - private String refundReason; - -} diff --git a/src/main/java/poomasi/domain/order/_refund/entity/RefundStatus.java b/src/main/java/poomasi/domain/order/_refund/entity/RefundStatus.java deleted file mode 100644 index 69a08f7c..00000000 --- a/src/main/java/poomasi/domain/order/_refund/entity/RefundStatus.java +++ /dev/null @@ -1,29 +0,0 @@ -package poomasi.domain.order._refund.entity; - -public enum RefundStatus { - REQUESTED("환불 요청됨"), - PROCESSING("환불 처리 중"), - COMPLETED("환불 완료됨"), - REJECTED("환불 거절됨"); - - private final String description; - - RefundStatus(String description) { - this.description = description; - } - - public String getDescription() { - return description; - } -} - -/* -* REQUESTED, // 반품 요청됨 - APPROVED, // 반품 승인됨 - REJECTED, // 반품 거부됨 - RETURNED, // 상품이 반품됨 - REFUNDED, // 환불 완료됨 - CANCELLED, // 반품 요청이 취소됨 - IN_TRANSIT // 반품이 배송 중 - ; -* */ diff --git a/src/main/java/poomasi/domain/order/entity/AbstractOrder.java b/src/main/java/poomasi/domain/order/entity/AbstractOrder.java deleted file mode 100644 index 82a7c4ff..00000000 --- a/src/main/java/poomasi/domain/order/entity/AbstractOrder.java +++ /dev/null @@ -1,33 +0,0 @@ -package poomasi.domain.order.entity; - -import jakarta.persistence.*; -import jdk.jfr.Description; -import jdk.jfr.Timestamp; -import lombok.Getter; -import org.hibernate.annotations.UpdateTimestamp; -import poomasi.domain.member.entity.Member; - -import java.time.LocalDateTime; -import java.util.Date; - -@MappedSuperclass -@Getter -public abstract class AbstractOrder { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @JoinColumn(name = "member_id") - @ManyToOne(fetch = FetchType.LAZY) - private Member member; - - @Column(name = "merchant_uid") - @Description("상품당 결제 id(아임포트 id)") - private String merchantUid = "p" + new Date().getTime(); - - @Column(name = "created_at") - @Timestamp - private LocalDateTime createdAt = LocalDateTime.now(); - -} diff --git a/src/main/java/poomasi/domain/order/entity/Order.java b/src/main/java/poomasi/domain/order/entity/Order.java deleted file mode 100644 index a830bed2..00000000 --- a/src/main/java/poomasi/domain/order/entity/Order.java +++ /dev/null @@ -1,49 +0,0 @@ -package poomasi.domain.order.entity; - - -import jakarta.persistence.*; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.math.BigDecimal; -import java.util.List; - -import static poomasi.domain.order.entity.OrderStatus.PENDING; - -@Entity -@Table(name = "orders") -@Getter -@NoArgsConstructor -public class Order extends AbstractOrder{ - - @Column(name = "order_product_details_id") - @OneToMany(mappedBy = "order", cascade = CascadeType.ALL) - private List orderProductDetails; - - @OneToOne - @JoinColumn(name = "order_details_id") // 여기서 JoinColumn 사용 - private OrderDetails orderDetails; - - @Column(name = "total_amount") - private BigDecimal totalAmount; - - @Enumerated(EnumType.STRING) - private OrderStatus orderStatus = OrderStatus.PENDING; - - public Order(OrderDetails orderDetails) { - this.orderDetails = orderDetails; - } - - public void addOrderDetail(OrderProductDetails orderProductDetails) { - this.orderProductDetails.add(orderProductDetails); - } - public void setOrderStatus(OrderStatus orderStatus) { - this.orderStatus = orderStatus; - } - - public void setTotalAmount(BigDecimal totalAmount) { - this.totalAmount = totalAmount; - } - - -} diff --git a/src/main/java/poomasi/domain/order/entity/OrderProductDetails.java b/src/main/java/poomasi/domain/order/entity/OrderProductDetails.java deleted file mode 100644 index 5be2e668..00000000 --- a/src/main/java/poomasi/domain/order/entity/OrderProductDetails.java +++ /dev/null @@ -1,71 +0,0 @@ -package poomasi.domain.order.entity; - - -import jakarta.persistence.*; -import jdk.jfr.Description; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import poomasi.domain.order._refund.entity.Refund; -import poomasi.domain.product.entity.Product; - -import java.io.Serializable; -import java.math.BigDecimal; - -@Entity -@Table(name = "order_product_details") -@Getter -@NoArgsConstructor -public class OrderProductDetails implements Serializable { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "order_product_details_id") - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "product_id") - private Product product; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "order_id") - private Order order; - - /* @OneToOne(fetch = FetchType.LAZY) - private Refund refund;*/ - - @Column(name = "product_description", nullable = true) - private String productDescription; - - @Column(name = "product_name", length = 255) - private String productName; - - @Description("구매 당시 1개당 가격") - private BigDecimal price; - - @Column(name="count") - private Integer count; - - @Description("송장 번호") - @Column(name = "invoice_number") - private String invoiceNumber; - - //private String sellerName; - //private OneToMany Review; - //refund.. - - @Builder - public OrderProductDetails(Product product, Order order, String productDescription, String productName, BigDecimal price, Integer count) { - this.product = product; - this.order = order; - this.productDescription = productDescription; - this.productName = productName; - this.price = price; - this.count = count; - } - - public void setInvoiceNumber(String invoiceNumber) { - this.invoiceNumber = invoiceNumber; - } -} - diff --git a/src/main/java/poomasi/domain/order/repository/OrderProductDetailsRepository.java b/src/main/java/poomasi/domain/order/repository/OrderProductDetailsRepository.java deleted file mode 100644 index 61432033..00000000 --- a/src/main/java/poomasi/domain/order/repository/OrderProductDetailsRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package poomasi.domain.order.repository; - -import org.springframework.data.jpa.repository.JpaRepository; -import poomasi.domain.order.entity.OrderProductDetails; - -import java.util.List; - -public interface OrderProductDetailsRepository extends JpaRepository { - List findByOrderId(Long orderId); -} diff --git a/src/main/java/poomasi/domain/order/repository/OrderRepository.java b/src/main/java/poomasi/domain/order/repository/OrderRepository.java deleted file mode 100644 index f2476ad0..00000000 --- a/src/main/java/poomasi/domain/order/repository/OrderRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package poomasi.domain.order.repository; - -import org.springframework.data.jpa.repository.JpaRepository; -import poomasi.domain.order.entity.Order; -import poomasi.domain.order.entity.OrderProductDetails; - -import java.util.List; -import java.util.Optional; - -public interface OrderRepository extends JpaRepository { - List findByMemberId(Long memberId); - //List findById(Long id); - Optional findByMerchantUid(String merchantUid); -} From 4bfa69f36854289cf1fc087db6d03736e5fc3424 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Fri, 8 Nov 2024 22:35:04 +0900 Subject: [PATCH 11/59] =?UTF-8?q?refactoring=20:=20order/payment/aftersale?= =?UTF-8?q?s(=ED=99=98=EB=B6=88,=EA=B5=90=ED=99=98,=EC=B7=A8=EC=86=8C)=20e?= =?UTF-8?q?ntity=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity/_farm/FarmAfterSales.java | 6 ++ .../entity/_product/ProductAfterSales.java | 21 +++++ .../_product/ProductAfterSalesDetail.java | 42 +++++++++ .../entity/_product/RefundExchangeDetail.java | 29 ++++++ .../domain/order/_payment/entity/Payment.java | 21 +++-- .../order/_payment/entity/PaymentState.java | 4 + .../domain/order/entity/OrderStatus.java | 5 +- .../order/entity/_abstract/AbstractOrder.java | 88 ++++++++++++++++++ .../domain/order/entity/_farm/FarmOrder.java | 34 +++++++ .../order/entity/_farm/OrderedFarm.java | 20 +++++ .../order/entity/_product/OrderedProduct.java | 90 +++++++++++++++++++ .../order/entity/_product/ProductOrder.java | 44 +++++++++ .../ProductOrderDetails.java} | 12 +-- 13 files changed, 397 insertions(+), 19 deletions(-) create mode 100644 src/main/java/poomasi/domain/order/_aftersales/entity/_farm/FarmAfterSales.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSales.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesDetail.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/entity/_product/RefundExchangeDetail.java create mode 100644 src/main/java/poomasi/domain/order/entity/_abstract/AbstractOrder.java create mode 100644 src/main/java/poomasi/domain/order/entity/_farm/FarmOrder.java create mode 100644 src/main/java/poomasi/domain/order/entity/_farm/OrderedFarm.java create mode 100644 src/main/java/poomasi/domain/order/entity/_product/OrderedProduct.java create mode 100644 src/main/java/poomasi/domain/order/entity/_product/ProductOrder.java rename src/main/java/poomasi/domain/order/entity/{OrderDetails.java => _product/ProductOrderDetails.java} (64%) diff --git a/src/main/java/poomasi/domain/order/_aftersales/entity/_farm/FarmAfterSales.java b/src/main/java/poomasi/domain/order/_aftersales/entity/_farm/FarmAfterSales.java new file mode 100644 index 00000000..055a8ca3 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/entity/_farm/FarmAfterSales.java @@ -0,0 +1,6 @@ +package poomasi.domain.order._aftersales.entity._farm; + +import poomasi.domain.order._aftersales.entity._abstract.AbstractAfterSales; + +public class FarmAfterSales extends AbstractAfterSales { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSales.java b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSales.java new file mode 100644 index 00000000..f2345caa --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSales.java @@ -0,0 +1,21 @@ +package poomasi.domain.order._aftersales.entity._product; + +import jakarta.persistence.*; +import poomasi.domain.order._aftersales.entity._abstract.AbstractAfterSales; +import poomasi.domain.order.entity._product.ProductOrder; + +import java.util.List; + +@Entity +@Table(name="product_after_sales") +public class ProductAfterSales extends AbstractAfterSales { + + @OneToOne + @JoinColumn(name = "product_order_id") + private ProductOrder productOrder; + + @OneToMany + private List productAfterSalesDetail; + +} + diff --git a/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesDetail.java b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesDetail.java new file mode 100644 index 00000000..b37f4fbb --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesDetail.java @@ -0,0 +1,42 @@ + package poomasi.domain.order._aftersales.entity._product; + + import jakarta.persistence.*; + import jdk.jfr.Description; + import poomasi.domain.order.entity._product.OrderedProduct; + + import java.math.BigDecimal; + + @Description("상품 판매 후 교환/환불/추소 history") + @Entity + @Table(name="product_after_sales_detail") + public class ProductAfterSalesDetail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + private ProductAfterSales productAfterSales; + + @OneToOne + private OrderedProduct orderedProduct; + + + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "refund_exchange_detail_id", nullable = true) // 외래 키 설정 + private RefundExchangeDetail refundExchangeDetail; + + @Description("ordered products의 환불/교환/취소 금액") + private BigDecimal amount; + + @Description("환불/교환/취소 사유") + private String reason; + + @Description("환불 받을 계좌번호") + private String refundAccount; + + @Enumerated(EnumType.STRING) + private ProductAfterSalesType productAfterSalesType; + + + } diff --git a/src/main/java/poomasi/domain/order/_aftersales/entity/_product/RefundExchangeDetail.java b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/RefundExchangeDetail.java new file mode 100644 index 00000000..6e81e378 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/RefundExchangeDetail.java @@ -0,0 +1,29 @@ +package poomasi.domain.order._aftersales.entity._product; + + +import jakarta.persistence.*; +import jdk.jfr.Description; + +@Entity +@Table(name= "refund_exchange_detail") +public class RefundExchangeDetail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne(mappedBy = "refundExchangeDetail") // 주인이 아닌 쪽에 mappedBy 설정 + private ProductAfterSalesDetail productAfterSalesDetail; + + @Description("반품 회수지. 기본 값은 보낸 주소") + private String pickupLocation; + + @Description("반송지. 기본 값은 받은 주소") + private String returnAddress; + + @Description("반품/교환 시 운송장 번호") + private String invoiceNumber; + + @Description("반품/교환 시 요청 사항") + private String request; +} diff --git a/src/main/java/poomasi/domain/order/_payment/entity/Payment.java b/src/main/java/poomasi/domain/order/_payment/entity/Payment.java index 321faa88..ddc3048d 100644 --- a/src/main/java/poomasi/domain/order/_payment/entity/Payment.java +++ b/src/main/java/poomasi/domain/order/_payment/entity/Payment.java @@ -1,12 +1,13 @@ package poomasi.domain.order._payment.entity; - import jakarta.persistence.*; import jdk.jfr.Description; import lombok.Getter; -import poomasi.domain.order.entity.Order; +import poomasi.domain.order.entity._farm.FarmOrder; +import poomasi.domain.order.entity._product.ProductOrder; import java.math.BigDecimal; +import java.util.List; @Entity @Getter @@ -16,12 +17,15 @@ public class Payment { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Description("상품 총 가격") + @Description("포트원 결제 금액") private BigDecimal totalPrice; @Description("할인 가격") private BigDecimal discountPrice; - + + @Description("사용 포인트") + private BigDecimal usedPoint; + @Description("최종 가격") private BigDecimal finalPrice; @@ -29,11 +33,10 @@ public class Payment { @Enumerated(EnumType.STRING) private PaymentMethod paymentMethod; - @OneToOne - private Order order; + @OneToOne(mappedBy = "payment") + private ProductOrder productOrder; - @Description("포트원에서 결제 식별을 위한 merchant_uid") - @Column(name = "merchant_uid" , updatable = false) - private String merchantUid; +/* @OneToOne(mappedBy = "payment") + private FarmOrder farmOrder;*/ } diff --git a/src/main/java/poomasi/domain/order/_payment/entity/PaymentState.java b/src/main/java/poomasi/domain/order/_payment/entity/PaymentState.java index 77d04d38..d160f719 100644 --- a/src/main/java/poomasi/domain/order/_payment/entity/PaymentState.java +++ b/src/main/java/poomasi/domain/order/_payment/entity/PaymentState.java @@ -1,5 +1,9 @@ package poomasi.domain.order._payment.entity; + +import jdk.jfr.Description; + +@Description("임시로 남겨둠 .. . .") public enum PaymentState { PENDING, // 결제 대기 중 COMPLETED, // 결제 완료 diff --git a/src/main/java/poomasi/domain/order/entity/OrderStatus.java b/src/main/java/poomasi/domain/order/entity/OrderStatus.java index 6a3b07a8..2e71f97e 100644 --- a/src/main/java/poomasi/domain/order/entity/OrderStatus.java +++ b/src/main/java/poomasi/domain/order/entity/OrderStatus.java @@ -3,9 +3,6 @@ public enum OrderStatus { PENDING, // 결제 대기 중 AWAITING_SELLER_CONFIRMATION, // 판매자 확인 대기 중 - READY_FOR_SHIPMENT, // 배송 대기 중 - IN_TRANSIT, // 배송 중 - DELIVERED, // 배송 완료 - ORDER_COMPLETE // 주문 완료 + SELLER_CONFIRMED // 판매자 확인 완료 ; } \ No newline at end of file diff --git a/src/main/java/poomasi/domain/order/entity/_abstract/AbstractOrder.java b/src/main/java/poomasi/domain/order/entity/_abstract/AbstractOrder.java new file mode 100644 index 00000000..4b784690 --- /dev/null +++ b/src/main/java/poomasi/domain/order/entity/_abstract/AbstractOrder.java @@ -0,0 +1,88 @@ +package poomasi.domain.order.entity._abstract; + +import jakarta.persistence.*; +import jdk.jfr.Description; +import jdk.jfr.Timestamp; +import lombok.Getter; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.UpdateTimestamp; +import poomasi.domain.member.entity.Member; +import poomasi.domain.order._payment.entity.Payment; +import poomasi.domain.order.entity.OrderStatus; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Date; + +@MappedSuperclass +@Getter +public abstract class AbstractOrder { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @JoinColumn(name = "member_id") + @ManyToOne(fetch = FetchType.LAZY) + @Description("주문 한 사람을 참조한다.") + private Member member; + + @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) + private Payment payment; + + @Column(name = "merchant_uid") + @Description("서버 내부 주문 id(아임포트 id)") + private String merchantUid = "p" + new Date().getTime(); + + @Column(name = "imp_uid") + @Description("아임포트 결제 imp_uid") + private String impUid; + + @Column(name = "created_at") + @CreationTimestamp + private LocalDateTime createdAt = LocalDateTime.now(); + + @Column(name = "updated_at") + @UpdateTimestamp + private LocalDateTime updateAt = LocalDateTime.now(); + + @Column(name = "deleted_at") + @Timestamp + private LocalDateTime deletedAt; + + @Column(name = "total_amount") + @Description("총 결제 금액") + private BigDecimal totalAmount; + + @Enumerated(EnumType.STRING) + private OrderStatus orderStatus = OrderStatus.PENDING; + + @Description("checksum") + private BigDecimal checksum; + + public void setCheckSum(BigDecimal checksum) { + this.checksum = checksum; + } + + public void reduceChecksum(BigDecimal amount) { + checksum = checksum.subtract(amount); + } + public void setTotalAmount(BigDecimal totalAmount) { + this.totalAmount = totalAmount; + } + + public void setOrderStatus(OrderStatus orderStatus) { + this.orderStatus = orderStatus; + } + + public void setImpUid(String impUid) { + this.impUid = impUid; + } + + public void setMerchantUid(String merchantUid) { + this.merchantUid = merchantUid; + } + +} + diff --git a/src/main/java/poomasi/domain/order/entity/_farm/FarmOrder.java b/src/main/java/poomasi/domain/order/entity/_farm/FarmOrder.java new file mode 100644 index 00000000..1de8705c --- /dev/null +++ b/src/main/java/poomasi/domain/order/entity/_farm/FarmOrder.java @@ -0,0 +1,34 @@ +package poomasi.domain.order.entity._farm; + +import jakarta.persistence.*; +import org.hibernate.annotations.Comment; +import poomasi.domain.order._payment.entity.Payment; +import poomasi.domain.order.entity._abstract.AbstractOrder; + +//@Entity +//@Table(name = "farm_order") +public class FarmOrder extends AbstractOrder { + /* + @OneToOne(fetch=FetchType.LAZY) + private FarmOrderDetails farmOrderDetails; + + @Column(name = "owner_id") + private Long ownerId; + + @Comment("농장 간단 설명") + private String description; + + @Comment("도로명 주소") + private String address; + + @Comment("상세 주소") + private String addressDetail; + + @Comment("위도") + private Double latitude; + + @Comment("경도") + private Double longitude; + */ +} + diff --git a/src/main/java/poomasi/domain/order/entity/_farm/OrderedFarm.java b/src/main/java/poomasi/domain/order/entity/_farm/OrderedFarm.java new file mode 100644 index 00000000..1c785e39 --- /dev/null +++ b/src/main/java/poomasi/domain/order/entity/_farm/OrderedFarm.java @@ -0,0 +1,20 @@ +package poomasi.domain.order.entity._farm; + + +import jakarta.persistence.*; +import poomasi.domain.farm.entity.Farm; + +@Entity +@Table(name = "ordered_farm") +public class OrderedFarm { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "ordered_farm_id") + private Long id; + + @OneToOne + private Farm farm; + + +} diff --git a/src/main/java/poomasi/domain/order/entity/_product/OrderedProduct.java b/src/main/java/poomasi/domain/order/entity/_product/OrderedProduct.java new file mode 100644 index 00000000..d495a281 --- /dev/null +++ b/src/main/java/poomasi/domain/order/entity/_product/OrderedProduct.java @@ -0,0 +1,90 @@ +package poomasi.domain.order.entity._product; + + +import jakarta.persistence.*; +import jdk.jfr.Description; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import poomasi.domain.order._aftersales.entity._product.ProductAfterSalesDetail; +import poomasi.domain.product.entity.Product; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Entity +@Table(name = "ordered_products") +@Getter +@NoArgsConstructor +public class OrderedProduct implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "ordered_product_id") + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(nullable = true, name = "product_after_sales_detail_id") + private ProductAfterSalesDetail productAfterSalesDetail; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "product_order_id") + private ProductOrder productOrder; + + + //FIXME : store Id를 참조해야 한다. + //나중에 store Id로 변경해야 한다 + private Long storeId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "product_id") + private Product product; + + + + @Column(name = "product_description", nullable = true) + private String productDescription; + + @Column(name = "product_name", length = 255) + private String productName; + + @Description("구매 당시 1개당 가격") + private BigDecimal price; + + @Column(name="count") + private Integer count; + + @Description("송장 번호") + @Column(name = "invoice_number", nullable = true) + private String invoiceNumber; + + private ShippingStatus shippingStatus = ShippingStatus.ORDERED; + + // 웹훅 받아서 조회해야 함. + // findByInvoiceNumber 후 + // web hook controller 만들어서 + // 배송 상태 적절히 변경해야 함 + + @Builder + public OrderedProduct(Product product, ProductOrder productOrder, String productDescription, String productName, BigDecimal price, Integer count) { + this.product = product; + this.productOrder = productOrder; + this.productDescription = productDescription; + this.productName = productName; + this.price = price; + this.count = count; + } + + public void setInvoiceNumber(String invoiceNumber) { + this.invoiceNumber = invoiceNumber; + } + + public void setShippingStatus(ShippingStatus shippingStatus) { + this.shippingStatus = shippingStatus; + } + + public Long getOrderId(){ + return this.productOrder.getId(); + } +} + diff --git a/src/main/java/poomasi/domain/order/entity/_product/ProductOrder.java b/src/main/java/poomasi/domain/order/entity/_product/ProductOrder.java new file mode 100644 index 00000000..5618b63c --- /dev/null +++ b/src/main/java/poomasi/domain/order/entity/_product/ProductOrder.java @@ -0,0 +1,44 @@ +package poomasi.domain.order.entity._product; + + +import jakarta.persistence.*; +import jdk.jfr.Description; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.SQLDelete; +import poomasi.domain.order._aftersales.entity._product.ProductAfterSales; +import poomasi.domain.order.entity._abstract.AbstractOrder; + +import java.util.List; + +@Entity +@Table(name = "product_order") +@Getter +@NoArgsConstructor +@SQLDelete(sql = "UPDATE product_order SET deleted_at=current_timestamp WHERE id = ?") +public class ProductOrder extends AbstractOrder { + + @Column(name = "ordered_products_id") + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) + private List orderedProducts; + + @OneToOne + @JoinColumn(name = "product_order_details_id") // 외래 키 지정 + @Description("상품 배송지, 요청 사항") + private ProductOrderDetails productOrderDetails; + + + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "product_after_sales_id") + @Description("결제 완료 후 취소/교환/환불 관리 객체") + private ProductAfterSales productAfterSales; + + public ProductOrder(ProductOrderDetails productOrderDetails) { + this.productOrderDetails = productOrderDetails; + } + + public void addOrderedProduct(OrderedProduct orderedProduct) { + this.orderedProducts.add(orderedProduct); + } + +} diff --git a/src/main/java/poomasi/domain/order/entity/OrderDetails.java b/src/main/java/poomasi/domain/order/entity/_product/ProductOrderDetails.java similarity index 64% rename from src/main/java/poomasi/domain/order/entity/OrderDetails.java rename to src/main/java/poomasi/domain/order/entity/_product/ProductOrderDetails.java index 5bb7ed09..4d8fb903 100644 --- a/src/main/java/poomasi/domain/order/entity/OrderDetails.java +++ b/src/main/java/poomasi/domain/order/entity/_product/ProductOrderDetails.java @@ -1,4 +1,4 @@ -package poomasi.domain.order.entity; +package poomasi.domain.order.entity._product; import jakarta.persistence.*; import jdk.jfr.Description; @@ -6,17 +6,17 @@ import lombok.NoArgsConstructor; @Entity -@Table(name="order_details") +@Table(name="product_order_details") @Getter @NoArgsConstructor -public class OrderDetails { +public class ProductOrderDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @OneToOne(mappedBy = "orderDetails") - private Order order; + @OneToOne(mappedBy = "productOrderDetails", cascade = CascadeType.ALL) // 필드명으로 지정 + private ProductOrder productOrder; @Column(name = "address") private String address; @@ -28,7 +28,7 @@ public class OrderDetails { @Column(name = "delivery_request", length = 255) private String deliveryRequest; - public OrderDetails(String address, String addressDetail, String deliveryRequest) { + public ProductOrderDetails(String address, String addressDetail, String deliveryRequest) { this.address = address; this.addressDetail = addressDetail; this.deliveryRequest = deliveryRequest; From fdc864494fb60dd90f13097bbc4a797b30abe8d3 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Fri, 8 Nov 2024 22:36:07 +0900 Subject: [PATCH 12/59] =?UTF-8?q?refactoring=20:=20order/payment/aftersale?= =?UTF-8?q?s(=ED=99=98=EB=B6=88,=EA=B5=90=ED=99=98,=EC=B7=A8=EC=86=8C)=20e?= =?UTF-8?q?ntity=20enum=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity/_product/ProductAfterSalesType.java | 7 +++++++ .../entity/_product/ProductsOrderDetailsStatus.java | 11 +++++++++++ .../domain/order/entity/_product/ShippingStatus.java | 8 ++++++++ 3 files changed, 26 insertions(+) create mode 100644 src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesType.java create mode 100644 src/main/java/poomasi/domain/order/entity/_product/ProductsOrderDetailsStatus.java create mode 100644 src/main/java/poomasi/domain/order/entity/_product/ShippingStatus.java diff --git a/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesType.java b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesType.java new file mode 100644 index 00000000..cfe7f4a8 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesType.java @@ -0,0 +1,7 @@ +package poomasi.domain.order._aftersales.entity._product; + +public enum ProductAfterSalesType { + EXCHANGE, + CANCEL, + REFUND +} diff --git a/src/main/java/poomasi/domain/order/entity/_product/ProductsOrderDetailsStatus.java b/src/main/java/poomasi/domain/order/entity/_product/ProductsOrderDetailsStatus.java new file mode 100644 index 00000000..53a53fab --- /dev/null +++ b/src/main/java/poomasi/domain/order/entity/_product/ProductsOrderDetailsStatus.java @@ -0,0 +1,11 @@ +package poomasi.domain.order.entity._product; + +public enum ProductsOrderDetailsStatus { + WAITING_SHIPPING, + IN_SHIPPING, // 배송 중 + DELIVERED_COMPLETE, // 배송 완료 + CANCELLED, // 취소 + RETURNED, // 환불 + EXCHANGED // 교환 + ; +} diff --git a/src/main/java/poomasi/domain/order/entity/_product/ShippingStatus.java b/src/main/java/poomasi/domain/order/entity/_product/ShippingStatus.java new file mode 100644 index 00000000..e2a0a0aa --- /dev/null +++ b/src/main/java/poomasi/domain/order/entity/_product/ShippingStatus.java @@ -0,0 +1,8 @@ +package poomasi.domain.order.entity._product; + +public enum ShippingStatus { + ORDERED, // 주문 완료 (배송 전 시작 상태) -> 배송 보내기 전 단계. 판매자 확인 후 취소 가능 + SHIPMENT_STARTED, // 배송 시작 + IN_TRANSIT, // 배송 중 + DELIVERED // 배송 완료 +} From dced6127ea297de58884b4748aae6f87378034f9 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Fri, 8 Nov 2024 22:36:43 +0900 Subject: [PATCH 13/59] =?UTF-8?q?feat=20:=20farm/product=20=EC=97=B0?= =?UTF-8?q?=EA=B4=80=EA=B4=80=EA=B3=84/=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/poomasi/domain/farm/entity/Farm.java | 8 +++++++- src/main/java/poomasi/domain/product/entity/Product.java | 7 +++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/java/poomasi/domain/farm/entity/Farm.java b/src/main/java/poomasi/domain/farm/entity/Farm.java index 9fd76ef9..7da4ed6f 100644 --- a/src/main/java/poomasi/domain/farm/entity/Farm.java +++ b/src/main/java/poomasi/domain/farm/entity/Farm.java @@ -15,6 +15,8 @@ import poomasi.domain.farm.dto.FarmUpdateRequest; import java.time.LocalDateTime; + +import poomasi.domain.order.entity._farm.OrderedFarm; import poomasi.domain.review.entity.Review; @Entity @@ -69,7 +71,11 @@ public class Farm { @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true) @JoinColumn(name = "entityId") - List reviewList = new ArrayList<>(); + private List reviewList = new ArrayList<>(); + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "ordered_farm_id") + private OrderedFarm orderedFarm; @Builder public Farm(String name, Long ownerId, String address, String addressDetail, Double latitude, Double longitude, String description, Long experiencePrice) { diff --git a/src/main/java/poomasi/domain/product/entity/Product.java b/src/main/java/poomasi/domain/product/entity/Product.java index f7e7bc36..a89483cd 100644 --- a/src/main/java/poomasi/domain/product/entity/Product.java +++ b/src/main/java/poomasi/domain/product/entity/Product.java @@ -17,7 +17,7 @@ import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.UpdateTimestamp; -import poomasi.domain.order.entity.OrderProductDetails; +import poomasi.domain.order.entity._product.OrderedProduct; import poomasi.domain.product.dto.ProductRegisterRequest; import poomasi.domain.review.entity.Review; @@ -70,7 +70,7 @@ public class Product { @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "order_product_details_id") - private List orderProductDetails; + private List orderProductDetails; @Builder @@ -113,6 +113,9 @@ public void addReview(Review pReview) { .orElse(0.0); } + public void subtractStock(Integer stock) { + this.stock -= stock; + } } From 4e0141a6b4e38e921311af7b731ec8c080da6403 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Fri, 8 Nov 2024 22:37:03 +0900 Subject: [PATCH 14/59] =?UTF-8?q?feat=20:=20=EB=A9=A4=EB=B2=84=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/poomasi/domain/member/entity/Member.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/poomasi/domain/member/entity/Member.java b/src/main/java/poomasi/domain/member/entity/Member.java index 318f3cb2..01c8c972 100644 --- a/src/main/java/poomasi/domain/member/entity/Member.java +++ b/src/main/java/poomasi/domain/member/entity/Member.java @@ -6,7 +6,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.annotations.SQLDelete; -import poomasi.domain.order.entity.Order; +import poomasi.domain.order.entity._product.ProductOrder; import poomasi.domain.wishlist.entity.WishList; import java.time.LocalDateTime; @@ -51,7 +51,7 @@ public class Member { private LocalDateTime deletedAt; @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) - private List orderLists; + private List productOrderLists; @Setter @Column(nullable = true) From 1e6f1ce60b9ef0e4b22c236e79883843fee47a2f Mon Sep 17 00:00:00 2001 From: amm0124 Date: Fri, 8 Nov 2024 22:37:19 +0900 Subject: [PATCH 15/59] =?UTF-8?q?feat=20:=20exception=20handler=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/poomasi/global/error/ExceptionAdvice.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/poomasi/global/error/ExceptionAdvice.java b/src/main/java/poomasi/global/error/ExceptionAdvice.java index 5e5e536b..7dc73824 100644 --- a/src/main/java/poomasi/global/error/ExceptionAdvice.java +++ b/src/main/java/poomasi/global/error/ExceptionAdvice.java @@ -30,4 +30,15 @@ public ErrorResponse applicationExceptionHandler(ApplicationException exception) .title(exception.getClass().getSimpleName()) .build(); } + + @ExceptionHandler(PaymentConfirmException.class) + public ErrorResponse paymentConfirmExceptionHandler(PaymentConfirmException exception) { + PaymentConfirmError paymentConfirmError = exception.getPaymentConfirmError(); + log.error("[{}] : {}", paymentConfirmError.name(), paymentConfirmError.getReason()); + return ErrorResponse + .builder(exception, paymentConfirmError.getHttpStatus(), paymentConfirmError.getReason()) + .title(paymentConfirmError.name()) + .build(); + } + } From bf3f90e4b268998fb547ab2752fb6ea7ed3a49a1 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Fri, 8 Nov 2024 22:38:14 +0900 Subject: [PATCH 16/59] =?UTF-8?q?feat=20:=20order/payment=20dto=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_aftersales/dto/FullRefundRequest.java | 6 ++++++ .../_aftersales/dto/PartialRefundRequest.java | 9 +++++++++ .../request/PaymentPreRegisterRequest.java | 1 - .../dto/request/PaymentValidateRequest.java | 4 ++++ .../dto/request/PaymentWebHookRequest.java | 7 +++++-- .../dto/response/PaymentResponse.java | 2 -- .../dto/request/OrderRegisterRequest.java | 6 ++++++ .../dto/response/OrderDetailsResponse.java | 10 +++++----- .../response/OrderProductDetailsResponse.java | 19 +++++++++---------- .../order/dto/response/OrderResponse.java | 14 ++++++-------- 10 files changed, 50 insertions(+), 28 deletions(-) create mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/FullRefundRequest.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/PartialRefundRequest.java create mode 100644 src/main/java/poomasi/domain/order/_payment/dto/request/PaymentValidateRequest.java create mode 100644 src/main/java/poomasi/domain/order/dto/request/OrderRegisterRequest.java diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/FullRefundRequest.java b/src/main/java/poomasi/domain/order/_aftersales/dto/FullRefundRequest.java new file mode 100644 index 00000000..d27e4423 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/dto/FullRefundRequest.java @@ -0,0 +1,6 @@ +package poomasi.domain.order._aftersales.dto; + +public record FullRefundRequest( + String refundReason +) { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/PartialRefundRequest.java b/src/main/java/poomasi/domain/order/_aftersales/dto/PartialRefundRequest.java new file mode 100644 index 00000000..faf155e2 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/dto/PartialRefundRequest.java @@ -0,0 +1,9 @@ +package poomasi.domain.order._aftersales.dto; + +import java.math.BigDecimal; + +public record PartialRefundRequest( + BigDecimal refundAmount, // type check 필요 + String refundReason +) { +} diff --git a/src/main/java/poomasi/domain/order/_payment/dto/request/PaymentPreRegisterRequest.java b/src/main/java/poomasi/domain/order/_payment/dto/request/PaymentPreRegisterRequest.java index c2df4c06..24fb5aae 100644 --- a/src/main/java/poomasi/domain/order/_payment/dto/request/PaymentPreRegisterRequest.java +++ b/src/main/java/poomasi/domain/order/_payment/dto/request/PaymentPreRegisterRequest.java @@ -3,5 +3,4 @@ import java.math.BigDecimal; public record PaymentPreRegisterRequest(String merchantUid, BigDecimal amount) { - } diff --git a/src/main/java/poomasi/domain/order/_payment/dto/request/PaymentValidateRequest.java b/src/main/java/poomasi/domain/order/_payment/dto/request/PaymentValidateRequest.java new file mode 100644 index 00000000..51639b27 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_payment/dto/request/PaymentValidateRequest.java @@ -0,0 +1,4 @@ +package poomasi.domain.order._payment.dto.request; + +public record PaymentValidateRequest(String merchantUid, String amount) { +} diff --git a/src/main/java/poomasi/domain/order/_payment/dto/request/PaymentWebHookRequest.java b/src/main/java/poomasi/domain/order/_payment/dto/request/PaymentWebHookRequest.java index a735aa9b..b9f68f75 100644 --- a/src/main/java/poomasi/domain/order/_payment/dto/request/PaymentWebHookRequest.java +++ b/src/main/java/poomasi/domain/order/_payment/dto/request/PaymentWebHookRequest.java @@ -1,5 +1,8 @@ package poomasi.domain.order._payment.dto.request; -public record PaymentWebHookRequest(String imp_uid, - String merchant_uid) { +import com.fasterxml.jackson.annotation.JsonProperty; + +public record PaymentWebHookRequest(@JsonProperty("imp_uid") String impUid, + @JsonProperty("merchant_uid") String merchantUid, + String status) { } diff --git a/src/main/java/poomasi/domain/order/_payment/dto/response/PaymentResponse.java b/src/main/java/poomasi/domain/order/_payment/dto/response/PaymentResponse.java index 9f6d0fa6..14f8ff20 100644 --- a/src/main/java/poomasi/domain/order/_payment/dto/response/PaymentResponse.java +++ b/src/main/java/poomasi/domain/order/_payment/dto/response/PaymentResponse.java @@ -6,7 +6,6 @@ import java.math.BigDecimal; public record PaymentResponse(Long paymentId, - String merchantUid, BigDecimal totalPrice, BigDecimal discountPrice, BigDecimal finalPrice, @@ -15,7 +14,6 @@ public record PaymentResponse(Long paymentId, public static PaymentResponse fromEntity(Payment payment){ return new PaymentResponse( payment.getId(), - payment.getMerchantUid(), payment.getTotalPrice(), payment.getDiscountPrice(), payment.getFinalPrice(), diff --git a/src/main/java/poomasi/domain/order/dto/request/OrderRegisterRequest.java b/src/main/java/poomasi/domain/order/dto/request/OrderRegisterRequest.java new file mode 100644 index 00000000..b29c8c96 --- /dev/null +++ b/src/main/java/poomasi/domain/order/dto/request/OrderRegisterRequest.java @@ -0,0 +1,6 @@ +package poomasi.domain.order.dto.request; + +public record OrderRegisterRequest(String address, + String addressDetails, + String deliveryRequest) { +} diff --git a/src/main/java/poomasi/domain/order/dto/response/OrderDetailsResponse.java b/src/main/java/poomasi/domain/order/dto/response/OrderDetailsResponse.java index c93a0358..e3002a7c 100644 --- a/src/main/java/poomasi/domain/order/dto/response/OrderDetailsResponse.java +++ b/src/main/java/poomasi/domain/order/dto/response/OrderDetailsResponse.java @@ -1,17 +1,17 @@ package poomasi.domain.order.dto.response; -import poomasi.domain.order.entity.OrderDetails; +import poomasi.domain.order.entity._product.ProductOrderDetails; public record OrderDetailsResponse( String address, String addressDetails, String deliveryRequest ) { - public static OrderDetailsResponse fromEntity(OrderDetails orderDetails) { + public static OrderDetailsResponse fromEntity(ProductOrderDetails productOrderDetails) { return new OrderDetailsResponse( - orderDetails.getAddress(), - orderDetails.getAddressDetail(), - orderDetails.getDeliveryRequest() + productOrderDetails.getAddress(), + productOrderDetails.getAddressDetail(), + productOrderDetails.getDeliveryRequest() ); } } diff --git a/src/main/java/poomasi/domain/order/dto/response/OrderProductDetailsResponse.java b/src/main/java/poomasi/domain/order/dto/response/OrderProductDetailsResponse.java index 9e2ae945..1726d646 100644 --- a/src/main/java/poomasi/domain/order/dto/response/OrderProductDetailsResponse.java +++ b/src/main/java/poomasi/domain/order/dto/response/OrderProductDetailsResponse.java @@ -1,7 +1,6 @@ package poomasi.domain.order.dto.response; -import poomasi.domain.order.entity.OrderProductDetails; -import poomasi.domain.product.dto.ProductResponse; +import poomasi.domain.order.entity._product.OrderedProduct; import java.math.BigDecimal; @@ -14,15 +13,15 @@ public record OrderProductDetailsResponse( BigDecimal price, //총 결제 금액 String invoiceNumber ) { - public static OrderProductDetailsResponse fromEntity(OrderProductDetails orderProductDetails) { + public static OrderProductDetailsResponse fromEntity(OrderedProduct orderedProduct) { return new OrderProductDetailsResponse( - orderProductDetails.getOrder().getId(), - orderProductDetails.getId(), - orderProductDetails.getProduct().getId(), - orderProductDetails.getProductName(), - orderProductDetails.getCount(), - orderProductDetails.getPrice(), - orderProductDetails.getInvoiceNumber() + orderedProduct.getOrderId(), + orderedProduct.getId(), + orderedProduct.getProduct().getId(), + orderedProduct.getProductName(), + orderedProduct.getCount(), + orderedProduct.getPrice(), + orderedProduct.getInvoiceNumber() ); } diff --git a/src/main/java/poomasi/domain/order/dto/response/OrderResponse.java b/src/main/java/poomasi/domain/order/dto/response/OrderResponse.java index 77f8a793..e5621032 100644 --- a/src/main/java/poomasi/domain/order/dto/response/OrderResponse.java +++ b/src/main/java/poomasi/domain/order/dto/response/OrderResponse.java @@ -1,8 +1,6 @@ package poomasi.domain.order.dto.response; -import poomasi.domain.order._payment.dto.response.PaymentResponse; -import poomasi.domain.order.entity.Order; -import poomasi.domain.order.entity.OrderStatus; +import poomasi.domain.order.entity._product.ProductOrder; import java.time.LocalDateTime; import java.util.List; @@ -12,12 +10,12 @@ public record OrderResponse(Long orderId, String merchantUid, LocalDateTime createdAt, List orderProductDetailsResponseList) { - public static OrderResponse fromEntity(Order order) { + public static OrderResponse fromEntity(ProductOrder productOrder) { return new OrderResponse( - order.getId(), - order.getMerchantUid(), - order.getCreatedAt(), - order.getOrderProductDetails() + productOrder.getId(), + productOrder.getMerchantUid(), + productOrder.getCreatedAt(), + productOrder.getOrderedProducts() .stream() .map(OrderProductDetailsResponse::fromEntity) .collect(Collectors.toList()) From e82e79b0b992ed166a78bdf12a1f9321e41d10a6 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Fri, 8 Nov 2024 22:38:54 +0900 Subject: [PATCH 17/59] =?UTF-8?q?feat=20:=20order=20controller/service/rep?= =?UTF-8?q?ository=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/controller/OrderController.java | 24 +++- .../repository/OrderedProductRepository.java | 9 ++ .../repository/ProductOrderRepository.java | 15 +++ .../domain/order/service/OrderService.java | 124 ++++++++++-------- 4 files changed, 114 insertions(+), 58 deletions(-) create mode 100644 src/main/java/poomasi/domain/order/repository/OrderedProductRepository.java create mode 100644 src/main/java/poomasi/domain/order/repository/ProductOrderRepository.java diff --git a/src/main/java/poomasi/domain/order/controller/OrderController.java b/src/main/java/poomasi/domain/order/controller/OrderController.java index 8a8c7a81..a6e17aa1 100644 --- a/src/main/java/poomasi/domain/order/controller/OrderController.java +++ b/src/main/java/poomasi/domain/order/controller/OrderController.java @@ -12,6 +12,7 @@ import poomasi.domain.auth.security.userdetail.UserDetailsImpl; import poomasi.domain.order._payment.dto.request.PaymentPreRegisterRequest; import poomasi.domain.order._payment.service.PaymentService; +import poomasi.domain.order.dto.request.OrderRegisterRequest; import poomasi.domain.order.service.OrderService; import java.io.IOException; @@ -27,15 +28,26 @@ public class OrderController { private final PaymentService paymentService; @Secured({"ROLE_CUSTOMER", "ROLE_FARMER"}) - @PostMapping("/pre-order") - public ResponseEntity createPreOrder(@AuthenticationPrincipal UserDetailsImpl user) throws IOException, IamportResponseException { - PaymentPreRegisterRequest paymentPreRegisterRequest = orderService.preOrderRegister(); + @PostMapping("/product/pre-order") + @Description("product 사전 결제") + public ResponseEntity createProductPreOrder(@RequestBody OrderRegisterRequest orderRegisterRequest) throws IOException, IamportResponseException { + PaymentPreRegisterRequest paymentPreRegisterRequest = orderService.productPreOrderRegister(orderRegisterRequest); return ResponseEntity.ok( paymentService.portonePrePaymentRegister(paymentPreRegisterRequest) ); } - - @Description("멤버의 결제 완료가 된 단건 주문 조회") + + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER"}) + @PostMapping("/farm/pre-order") + @Description("farm 사전 결제") + public ResponseEntity createFarmPreOrder() throws IOException, IamportResponseException { + PaymentPreRegisterRequest paymentPreRegisterRequest = orderService.farmPreOrderRegister(); + return ResponseEntity.ok( + paymentService.portonePrePaymentRegister(paymentPreRegisterRequest) + ); + } + + @Description("멤버의 결제 완료가 된 단건 주문 조회. 특정 건만 조회") @GetMapping("/{orderId}") public ResponseEntity getAllOrdersByMember(@PathVariable Long orderId) { return ResponseEntity.ok( @@ -43,7 +55,7 @@ public ResponseEntity getAllOrdersByMember(@PathVariable Long orderId) { ); } - @Description("멤버의 결제 완료가 된 전체 주문 목록 조회") + @Description("멤버의 결제 완료가 된 전체 주문 목록 조회. 전체 주문 목록 조회") @GetMapping("/") public ResponseEntity getOrdersByMember() { return ResponseEntity.ok( diff --git a/src/main/java/poomasi/domain/order/repository/OrderedProductRepository.java b/src/main/java/poomasi/domain/order/repository/OrderedProductRepository.java new file mode 100644 index 00000000..4827c4de --- /dev/null +++ b/src/main/java/poomasi/domain/order/repository/OrderedProductRepository.java @@ -0,0 +1,9 @@ +package poomasi.domain.order.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import poomasi.domain.order.entity._product.OrderedProduct; + +import java.util.List; + +public interface OrderedProductRepository extends JpaRepository { +} diff --git a/src/main/java/poomasi/domain/order/repository/ProductOrderRepository.java b/src/main/java/poomasi/domain/order/repository/ProductOrderRepository.java new file mode 100644 index 00000000..9f0ba5a1 --- /dev/null +++ b/src/main/java/poomasi/domain/order/repository/ProductOrderRepository.java @@ -0,0 +1,15 @@ +package poomasi.domain.order.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import poomasi.domain.order.entity._product.ProductOrder; + +import java.util.List; +import java.util.Optional; + +public interface ProductOrderRepository extends JpaRepository { + List findByMemberId(Long memberId); + //List findById(Long id); + Optional findByMerchantUid(String merchantUid); + Optional findByImpUid(String impUid); + Optional findByMerchantUidAndImpUid(String merchantUid, String impUid); +} diff --git a/src/main/java/poomasi/domain/order/service/OrderService.java b/src/main/java/poomasi/domain/order/service/OrderService.java index 24175672..4d34b046 100644 --- a/src/main/java/poomasi/domain/order/service/OrderService.java +++ b/src/main/java/poomasi/domain/order/service/OrderService.java @@ -10,17 +10,16 @@ import poomasi.domain.auth.security.userdetail.UserDetailsImpl; import poomasi.domain.member.entity.Member; import poomasi.domain.order._payment.dto.request.PaymentPreRegisterRequest; -import poomasi.domain.order._payment.repository.PaymentRepository; -import poomasi.domain.order._payment.service.PaymentService; +import poomasi.domain.order.dto.request.OrderRegisterRequest; import poomasi.domain.order.dto.response.OrderDetailsResponse; import poomasi.domain.order.dto.response.OrderProductDetailsResponse; import poomasi.domain.order.dto.response.OrderResponse; -import poomasi.domain.order.entity.Order; -import poomasi.domain.order.entity.OrderDetails; -import poomasi.domain.order.entity.OrderProductDetails; +import poomasi.domain.order.entity._product.OrderedProduct; +import poomasi.domain.order.entity._product.ProductOrder; +import poomasi.domain.order.entity._product.ProductOrderDetails; import poomasi.domain.order.entity.OrderStatus; -import poomasi.domain.order.repository.OrderProductDetailsRepository; -import poomasi.domain.order.repository.OrderRepository; +import poomasi.domain.order.repository.OrderedProductRepository; +import poomasi.domain.order.repository.ProductOrderRepository; import poomasi.domain.product._cart.entity.Cart; import poomasi.domain.product._cart.repository.CartRepository; import poomasi.domain.product.entity.Product; @@ -31,7 +30,6 @@ import java.util.List; import java.util.stream.Collectors; -import static poomasi.domain.order.entity.OrderStatus.AWAITING_SELLER_CONFIRMATION; import static poomasi.global.error.BusinessError.*; @RequiredArgsConstructor @@ -39,26 +37,23 @@ @Slf4j public class OrderService { - private final OrderRepository orderRepository; + private final ProductOrderRepository productOrderRepository; private final CartRepository cartRepository; private final ProductRepository productRepository; - private final PaymentRepository paymentRepository; - private final PaymentService paymentService; - private final OrderProductDetailsRepository orderProductDetailsRepository; + private final OrderedProductRepository orderedProductRepository; @Transactional - public PaymentPreRegisterRequest preOrderRegister(){ + public PaymentPreRegisterRequest productPreOrderRegister(OrderRegisterRequest orderRegisterRequest){ Member member = getMember(); Long memberId = member.getId(); List cartList = cartRepository.findByMemberIdAndSelected(memberId); - //TODO : dto에서 address, address detail 꺼내와야 함. -> dto 확정이 안 나서 임시로 넣음 - String address = "금정구"; - String addressDetails = "수림로"; - String deliveryRequest = "조심히 다뤄 주세요"; + String address = orderRegisterRequest.address(); + String addressDetails = orderRegisterRequest.addressDetails(); + String deliveryRequest = orderRegisterRequest.deliveryRequest(); - Order order = new Order( - new OrderDetails(address, + ProductOrder productOrder = new ProductOrder( + new ProductOrderDetails(address, addressDetails, deliveryRequest) ); @@ -66,40 +61,61 @@ public PaymentPreRegisterRequest preOrderRegister(){ //cart에 있는 총 가격 계산하기 BigDecimal totalPrice = BigDecimal.ZERO; - // cart 돌면서 order details 추가 + // cart 돌면서 productOrder details 추가 for (Cart cart : cartList) { Long productId = cart.getProductId(); Product product = productRepository.findById(productId) .orElseThrow(() -> new BusinessException(PRODUCT_NOT_FOUND)); + + Integer productStock = product.getStock(); + Integer quantityInCart = cart.getCount(); + + // 현재 남아있는 재고보다 더 많이 요청하면 + if(quantityInCart > productStock){ + throw new BusinessException(PRODUCT_STOCK_ZERO); + } + String productDescription = product.getDescription(); Integer count = cart.getCount(); String productName = product.getName(); BigDecimal price = BigDecimal.valueOf(product.getPrice()); - OrderProductDetails orderProductDetails = OrderProductDetails + OrderedProduct orderedProduct = OrderedProduct .builder() .product(product) - .order(order) + .productOrder(productOrder) .productDescription(productDescription) .productName(productName) .price(price) .count(count) .build(); - order.addOrderDetail(orderProductDetails); + productOrder.addOrderedProduct(orderedProduct); totalPrice = totalPrice.add(price); } - order.setTotalAmount(totalPrice); - orderRepository.save(order); + productOrder.setTotalAmount(totalPrice); + productOrder.setCheckSum(totalPrice); + productOrderRepository.save(productOrder); - String merchantUid = order.getMerchantUid(); + String merchantUid = productOrder.getMerchantUid(); return new PaymentPreRegisterRequest(merchantUid, totalPrice); } + @Transactional + //TODO : 만들어야 합니다 ~ + public PaymentPreRegisterRequest farmPreOrderRegister(){ + Member member = getMember(); + String merchantUid = ""; + BigDecimal totalPrice = BigDecimal.ZERO; + + return new PaymentPreRegisterRequest(merchantUid, totalPrice); + } + + @Description("멤버 ID 기반으로 모든 order 다 들고 오는 메서드") public List findAllOrdersByMemberId(){ Member member = getMember(); Long memberId = member.getId(); - List orderList = orderRepository.findByMemberId(memberId); - return orderList + List productOrderList = productOrderRepository.findByMemberId(memberId); + return productOrderList .stream() .map(OrderResponse::fromEntity) .collect(Collectors.toList() @@ -109,30 +125,32 @@ public List findAllOrdersByMemberId(){ @Description("멤버 id 기반으로 특정 orderId 들고오는 메서드") public OrderResponse findOrderByMemberId(Long orderId){ Member member = getMember(); - Order order = orderRepository.findById(orderId) + ProductOrder productOrder = productOrderRepository.findById(orderId) .orElseThrow(()-> new BusinessException(ORDER_NOT_FOUND)); - validateOrderOwnership(order, member); - return OrderResponse.fromEntity(order); + validateOrderOwnership(productOrder, member); + return OrderResponse.fromEntity(productOrder); } @Description("orderId 기반으로 order details(주소, 상세주소, 배송 요청 사항 ..등) 들고오는 메서드") public OrderDetailsResponse findOrderDetailsByOrderId(Long orderId){ - Order order = orderRepository.findById(orderId) + ProductOrder productOrder = productOrderRepository.findById(orderId) .orElseThrow(()-> new BusinessException(ORDER_NOT_FOUND)); - OrderDetails orderDetails = order.getOrderDetails(); + ProductOrderDetails productOrderDetails = productOrder.getProductOrderDetails(); - return OrderDetailsResponse.fromEntity(orderDetails); + return OrderDetailsResponse.fromEntity(productOrderDetails); } + + @Description("orderId에 해당하는 order product details 가져오는 메서드") public List findAllOrderProductDetails(Long orderId){ Member member = getMember(); - Order order = orderRepository.findById(orderId) + ProductOrder productOrder = productOrderRepository.findById(orderId) .orElseThrow(()-> new BusinessException(ORDER_NOT_FOUND)); - validateOrderOwnership(order, member); - return order.getOrderProductDetails() + validateOrderOwnership(productOrder, member); + return productOrder.getOrderedProducts() .stream() .map(OrderProductDetailsResponse::fromEntity) .collect(Collectors.toList() @@ -143,44 +161,46 @@ public List findAllOrderProductDetails(Long orderId @Description("orderId에 해당하는 order product Details의 단건 조회") public OrderProductDetailsResponse findOrderProductDetailsById(Long orderId, Long orderProductDetailsId){ Member member = getMember(); - OrderProductDetails orderProductDetails = orderProductDetailsRepository.findById(orderProductDetailsId) + OrderedProduct orderedProduct = orderedProductRepository.findById(orderProductDetailsId) .orElseThrow(()-> new BusinessException(ORDER_PRODUCT_DETAILS_NOT_FOUND)); - Order order = orderProductDetails.getOrder(); + ProductOrder productOrder = orderedProduct.getProductOrder(); - // order product details의 주인 order 검사 그리고 , orderId의 주인 member 검사 - validateOrderProductDetailsByOrderId(order, orderId); - validateOrderOwnership(order, member); + // productOrder product details의 주인 productOrder 검사 그리고 , orderId의 주인 member 검사 + validateOrderProductDetailsByOrderId(productOrder, orderId); + validateOrderOwnership(productOrder, member); - return OrderProductDetailsResponse.fromEntity(orderProductDetails); + return OrderProductDetailsResponse.fromEntity(orderedProduct); } @Description("member의 order인지 검사하는 메서드") - private void validateOrderOwnership(Order order, Member member) { - if (!order.getMember().getId().equals(member.getId())) { + private void validateOrderOwnership(ProductOrder productOrder, Member member) { + if (!productOrder.getMember().getId().equals(member.getId())) { throw new BusinessException(ORDER_NOT_OWNED_EXCEPTION); } } - @Description("orderId에 해당하는 order Product Details인지 조회하는 메서드") - private void validateOrderProductDetailsByOrderId(Order order, Long orderId) { - if(order.getId()!=orderId){ + @Description("orderId에 해당하는 productOrder Product Details인지 조회하는 메서드") + private void validateOrderProductDetailsByOrderId(ProductOrder productOrder, Long orderId) { + if(productOrder.getId()!=orderId){ throw new BusinessException(ORDER_PRODUCT_DETAILS_NOT_OWNED_EXCEPTION); } } + + /* @Description("결제가 완료 된 후, 주문 상태 변경하는 메서드. 굳이 없어도 되긴 함.") private void completePaymentAndUpdateStatus(Long orderId){ - Order order = orderRepository.findById(orderId) + ProductOrder order = productOrderRepository.findById(orderId) .orElseThrow(()-> new BusinessException(ORDER_NOT_FOUND)); order.setOrderStatus(AWAITING_SELLER_CONFIRMATION); - } + }*/ @Description("주문 상태를 변경하는 메서드") private void changeOrderStatus(Long orderId, OrderStatus orderStatus){ - Order order = orderRepository.findById(orderId) + ProductOrder productOrder = productOrderRepository.findById(orderId) .orElseThrow(()-> new BusinessException(ORDER_NOT_FOUND)); - order.setOrderStatus(orderStatus); + productOrder.setOrderStatus(orderStatus); } From 1377ff201fc129249da7b9d0cc12a1909acf1b67 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Fri, 8 Nov 2024 22:39:07 +0900 Subject: [PATCH 18/59] =?UTF-8?q?feat=20:=20security=20config=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/poomasi/domain/auth/config/SecurityConfig.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/poomasi/domain/auth/config/SecurityConfig.java b/src/main/java/poomasi/domain/auth/config/SecurityConfig.java index 7c67b99b..0fe17149 100644 --- a/src/main/java/poomasi/domain/auth/config/SecurityConfig.java +++ b/src/main/java/poomasi/domain/auth/config/SecurityConfig.java @@ -39,7 +39,6 @@ public class SecurityConfig { private final CustomSuccessHandler customSuccessHandler; private final UserDetailsServiceImpl userDetailsService; - @Autowired private OAuth2UserDetailServiceImpl oAuth2UserDetailServiceImpl; @@ -74,7 +73,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti // 기본 경로 및 테스트 경로 http.authorizeHttpRequests((authorize) -> authorize - .requestMatchers(HttpMethod.GET, "/api/farm/**").permitAll() + .requestMatchers(HttpMethod.POST, "/api/farm/**").permitAll() .requestMatchers(HttpMethod.GET, "/api/product/**").permitAll() .requestMatchers(HttpMethod.GET, "/api/review/**").permitAll() .requestMatchers("/api/sign-up", "/api/login", "api/reissue", "api/payment/**", "api/order/**").permitAll() From 51df13cd8e70ae75bdca9cb029a6a8ff515fc096 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Fri, 8 Nov 2024 22:39:27 +0900 Subject: [PATCH 19/59] =?UTF-8?q?feat=20:=20payment=20controller/service?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PaymentController.java | 37 +++- .../_payment/service/PaymentService.java | 182 +++++++++++++++--- 2 files changed, 185 insertions(+), 34 deletions(-) diff --git a/src/main/java/poomasi/domain/order/_payment/controller/PaymentController.java b/src/main/java/poomasi/domain/order/_payment/controller/PaymentController.java index 84acdd44..21324d48 100644 --- a/src/main/java/poomasi/domain/order/_payment/controller/PaymentController.java +++ b/src/main/java/poomasi/domain/order/_payment/controller/PaymentController.java @@ -8,7 +8,6 @@ import org.springframework.web.bind.annotation.*; import poomasi.domain.order._payment.dto.request.PaymentPreRegisterRequest; import poomasi.domain.order._payment.dto.request.PaymentWebHookRequest; -import poomasi.domain.order._payment.dto.response.PaymentResponse; import poomasi.domain.order._payment.service.PaymentService; import java.io.IOException; @@ -23,21 +22,36 @@ public class PaymentController { @Description("사전 결제 api") @Secured({"ROLE_CUSTOMER", "ROLE_FARMER"}) @PostMapping("/pre-payment") - public void postPrepare(PaymentPreRegisterRequest paymentPreRegisterRequest) throws IamportResponseException, IOException { - paymentService.portonePrePaymentRegister(paymentPreRegisterRequest); + public ResponseEntity postPrepare(PaymentPreRegisterRequest paymentPreRegisterRequest) throws IamportResponseException, IOException { + return ResponseEntity.ok( + paymentService.portonePrePaymentRegister(paymentPreRegisterRequest) + ); } @Description("사후 결제(검증 api)") @PostMapping("/validate") - public void validatePayment(PaymentWebHookRequest paymentWebHookRequest) throws IamportResponseException, IOException { - paymentService.portoneVerifyPostPayment(paymentWebHookRequest); + public void validatePayment(@RequestBody PaymentWebHookRequest paymentWebHookRequest) throws IamportResponseException, IOException { + paymentService.handlePortOneProductWebhookEvent(paymentWebHookRequest); + } + + + @Description("포트원 웹훅 수신 api") + @PostMapping("/portone-webhook") + public void handleIamportWebhook(@RequestBody PaymentWebHookRequest paymentWebHookRequest) throws IamportResponseException, IOException { + paymentService.handlePortOneProductWebhookEvent(paymentWebHookRequest); + } + + + @Description("결제 바로 직전 포트원에서 보내는 confirm 요청" + " 결제를 진행하려면 HTTP Status 200 응답, 그렇지 않으면 500 응답 보내기" ) + @PostMapping("/confirm-stock/") + public ResponseEntity confirmProductStock(@RequestParam String merchantUid, @RequestParam String impUid) throws IamportResponseException, IOException { + paymentService.confirmProductStock(merchantUid, impUid); + return ResponseEntity.ok().build(); } - /* - *@Description("포트원 webhook + 동기화") - * */ + /* @GetMapping("/") @Secured("ROLE_CUSTOMER") @@ -55,5 +69,12 @@ public ResponseEntity getPaymentByOrderId(@RequestParam Long orderId){ return ResponseEntity.ok(paymentResponse); } + */ + } + +/**TODO : filter 만들어서 webhook URL에 대해 IP 검증해야 함 + *@Description("포트원 webhook + 동기화") + @PostMapping("/portone-webhook") + * */ \ No newline at end of file diff --git a/src/main/java/poomasi/domain/order/_payment/service/PaymentService.java b/src/main/java/poomasi/domain/order/_payment/service/PaymentService.java index e5d33028..a65ec9dc 100644 --- a/src/main/java/poomasi/domain/order/_payment/service/PaymentService.java +++ b/src/main/java/poomasi/domain/order/_payment/service/PaymentService.java @@ -4,17 +4,15 @@ import com.siot.IamportRestClient.exception.IamportResponseException; import com.siot.IamportRestClient.request.CancelData; import com.siot.IamportRestClient.request.PrepareData; -import com.siot.IamportRestClient.response.AccessToken; import com.siot.IamportRestClient.response.IamportResponse; -import com.siot.IamportRestClient.response.Prepare; import jdk.jfr.Description; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; import poomasi.domain.auth.security.userdetail.UserDetailsImpl; import poomasi.domain.member.entity.Member; @@ -24,15 +22,22 @@ import poomasi.domain.order._payment.dto.response.PaymentResponse; import poomasi.domain.order._payment.entity.Payment; import poomasi.domain.order._payment.repository.PaymentRepository; -import poomasi.domain.order.entity.Order; -import poomasi.domain.order.repository.OrderRepository; +import poomasi.domain.order.entity._product.OrderedProduct; +import poomasi.domain.order.entity._product.ProductOrder; +import poomasi.domain.order.repository.ProductOrderRepository; import poomasi.domain.product._cart.service.CartService; -import poomasi.global.error.BusinessError; +import poomasi.domain.product.entity.Product; import poomasi.global.error.BusinessException; +import poomasi.global.error.PaymentConfirmError; +import poomasi.global.error.PaymentConfirmException; import java.io.IOException; import java.math.BigDecimal; import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import static poomasi.domain.order.entity.OrderStatus.AWAITING_SELLER_CONFIRMATION; import static poomasi.domain.order.entity.OrderStatus.PENDING; @@ -46,18 +51,15 @@ public class PaymentService { @Autowired private final PaymentRepository paymentRepository; private final IamportClient iamportClient; - private final OrderRepository orderRepository; + private final ProductOrderRepository productOrderRepository; private final CartService cartService; - @Description("포트원 api 호출을 위한 accessToken 발급 메서드") - private String getPortOneAccessToken() throws IOException, IamportResponseException { - IamportResponse authResponse = iamportClient.getAuth(); - String accessToken = authResponse.getResponse().getToken(); - return accessToken; - } - - @Description("사전 결제 등록") + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + private final AtomicBoolean isWebhookReceived = new AtomicBoolean(false); // 웹훅 수신 여부 체크 + + @Description("사전 결제 등록. 프론트엔드에게 서버 merchant uid를 return 해야 함") public PaymentPreRegisterResponse portonePrePaymentRegister(PaymentPreRegisterRequest paymentPreRegisterRequest) throws IOException, IamportResponseException { + PrepareData prepareData = new PrepareData(paymentPreRegisterRequest.merchantUid(), paymentPreRegisterRequest.amount() ); @@ -67,7 +69,123 @@ public PaymentPreRegisterResponse portonePrePaymentRegister(PaymentPreRegisterRe ); } - @Transactional + + @Transactional(isolation = Isolation.SERIALIZABLE) + @Description("포트원 결제 직전 바로 받는 confirm 요청. 40초 대기") + public void confirmProductStock(String impUid, String merchantUid) throws IOException, IamportResponseException { + + ProductOrder productOrder = productOrderRepository.findByMerchantUidAndImpUid(merchantUid, impUid) + .orElseThrow(() -> new BusinessException(PAYMENT_NOT_FOUND)); + + List orderedProductList = productOrder.getOrderedProducts(); + //수량 검증 + for(OrderedProduct orderedProduct : orderedProductList) { + Product product = orderedProduct.getProduct(); + Integer remainQuantity = product.getStock(); + Integer orderQuantity = orderedProduct.getCount(); + + //주문 재고가 남은 재고보다 많다면 500 + reason 보내야 함 + if(orderQuantity > remainQuantity){ + throw new PaymentConfirmException(PaymentConfirmError.PAYMENT_PROUCT_CONFIRM_EXCEPTION); + } + } + BigDecimal amountToBePaid = productOrder.getTotalAmount(); + //재고 검증 완료 -> 200 OK 보내야 함 + 웹훅 수신 여부에 따라 분기 + scheduler.schedule(() -> { + try { + if (!isWebhookReceived.get()) { //수신 못 받으면 + if(sendAndValidateAmount(impUid, amountToBePaid)){ //impUid를 가지고 포트원 서버에 요청을 한 후, db와 결제 금액 비교한다. + productOrder.setOrderStatus(AWAITING_SELLER_CONFIRMATION); + decreaseStock(productOrder); + }else{ + //실제 결제 된 금액과 결제 되어야 할 금액이 다르다면 -> 결제 취소 api를 호출해야 한다. + cancelPaymentByImpUid(impUid); + } + } + } catch (IOException | IamportResponseException e) { + e.printStackTrace(); + } + }, 40, TimeUnit.SECONDS); + + } + + @Description("단건 조회 후, 결제 되어야 할 금액과 결제 된 금액이 같은지 확인하는 메서드") + public boolean sendAndValidateAmount(String impUid, BigDecimal amountToBePaid) throws IOException, IamportResponseException{ + BigDecimal amount = getPaymentAmount(impUid); + if(amountToBePaid.compareTo(amount)==0){ + return true; + } + return false; + } + + + @Description("포트원에서 결제 금액 조회하는 메서드") + public BigDecimal getPaymentAmount(String impUid) throws IOException, IamportResponseException{ + IamportResponse iamportResponse = getSingleTransaction(impUid); + return iamportResponse.getResponse().getAmount(); + } + + @Description("웹훅 처리 service -> 결제 정상적으로 성공됨을 보장") + @Transactional(isolation = Isolation.SERIALIZABLE) + public void handlePortOneProductWebhookEvent(PaymentWebHookRequest paymentWebHookRequest) throws IOException, IamportResponseException { + + isWebhookReceived.set(true); //웹훅 수신 플래그 설정하기 + + String impUid = paymentWebHookRequest.impUid(); + ProductOrder productOrder = productOrderRepository.findByImpUid(impUid) + .orElseThrow(() -> new BusinessException(PAYMENT_NOT_FOUND)); + BigDecimal amountToBePaid = productOrder.getTotalAmount(); + + //결제 되어야 할 금액과 결제 된 금액이 같다면 + if(sendAndValidateAmount(impUid, amountToBePaid)){ + productOrder.setOrderStatus(AWAITING_SELLER_CONFIRMATION); + decreaseStock(productOrder); + }else{ + cancelPaymentByImpUid(impUid); //실제 결제 된 금액과 결제 되어야 할 금액이 다르다면 -> 결제 취소 api를 호출해야 한다. + //throw new BusinessException + } + + } + + @Description("재고 차감 메서드") + @Transactional(isolation = Isolation.SERIALIZABLE) + public void decreaseStock(ProductOrder productOrder){ + List orderedProductList = productOrder.getOrderedProducts(); + for (OrderedProduct orderedProduct : orderedProductList){ + Product product = orderedProduct.getProduct(); + Integer remainQuantity = product.getStock(); //남은 수량 + Integer subtractQuantity = orderedProduct.getCount();//빼야 할 수량 + if(subtractQuantity > remainQuantity){ + throw new BusinessException(STOCK_QUANTITY_EXCEEDED); + } + product.subtractStock(subtractQuantity); + } + } + + /* + com.siot.IamportRestClient.response.Payment payment = iamportResponse.getResponse(); + int code = iamportResponse.getCode(); + String message = iamportResponse.getMessage(); + String status = payment.getStatus(); + BigDecimal amount = payment.getAmount(); + */ + @Description("단건 결제 조회 API") + public IamportResponse getSingleTransaction(String impUid) throws IOException, IamportResponseException { + IamportResponse iamportResponse = iamportClient.paymentByImpUid(impUid); + return iamportResponse; + } + + @Description("서버에서 마지막 검증") + public boolean verifyPostPayment(String impUid) throws IOException, IamportResponseException { + ProductOrder productOrder = productOrderRepository.findByImpUid(impUid) + .orElseThrow(() -> new BusinessException(PAYMENT_NOT_FOUND)); + if(productOrder.getOrderStatus() == PENDING){ + throw new BusinessException(PAYMENT_BAD_REQUEST); + } + return true; + } + + /*@Transactional @Description("프론트에서 받아온 결과를 validate하는 메서드") public void portoneVerifyPostPayment(PaymentWebHookRequest paymentWebHookRequest) throws IOException, IamportResponseException { String impUid = paymentWebHookRequest.imp_uid(); @@ -76,7 +194,7 @@ public void portoneVerifyPostPayment(PaymentWebHookRequest paymentWebHookRequest BigDecimal amount = iamportResponse.getResponse() .getAmount(); - Order order = orderRepository.findByMerchantUid(merchantUid) + ProductOrder order = productOrderRepository.findByMerchantUid(merchantUid) .orElseThrow(() -> new BusinessException(ORDER_NOT_FOUND)); if(order.getOrderStatus()!=PENDING){ //이미 처리한 주문이라면 @@ -89,7 +207,10 @@ public void portoneVerifyPostPayment(PaymentWebHookRequest paymentWebHookRequest } order.setOrderStatus(AWAITING_SELLER_CONFIRMATION); // 상태 변경 cartService.removeSelected(); //장바구니 삭제 - } + }*/ + + //private boolean compareCartAndPaymentAmount(Cart cart, BigDecimal ) + private boolean validatePaymentConsistency(BigDecimal prepaymentAmount, BigDecimal postPaymentAmount){ if (prepaymentAmount.compareTo(postPaymentAmount) != 0) { @@ -98,26 +219,35 @@ private boolean validatePaymentConsistency(BigDecimal prepaymentAmount, BigDecim return true; } - @Description("payment 상세 내역 조회를 위한 단건 api 호출") - public void getPaymentDetails(String merchantUid, Long orderId) throws IOException, IamportResponseException { + public void cancelPaymentByImpUid(String impUid) throws IOException, IamportResponseException { + CancelData cancelDate = new CancelData(impUid, false); + iamportClient.cancelPaymentByImpUid(cancelDate); } - @Description("결제 취소 api") - public void cancelPayment(IamportResponse response) throws IOException, IamportResponseException{ + /*@Transactional + @Description("결제 전액 취소/환불 api") + public void cancelPayment(Payment payment, IamportResponse response) throws IOException, IamportResponseException{ + //TODO : 결제내역 단건 조회 통해서 이미 완료가 된 결제인지 확인 //true면 Uid, false면 merchantUid로 판단 CancelData cancelData = new CancelData(response.getResponse().getMerchantUid(), false); iamportClient.cancelPaymentByImpUid(cancelData); - } - @Description("결제 환불 api") + }*/ + + /* @Description("결제 환불 api") public void processRefund() throws IOException, IamportResponseException{ } +*/ - + @Transactional @Description("결제 부분 환불 api 호출") - public void partialRefund() throws IOException, IamportResponseException { + public void partialRefund(BigDecimal checkSum, IamportResponse response, BigDecimal amount) throws IOException, IamportResponseException { + //BigDecimal checkSum = payment.getChecksum(); + CancelData cancelData = new CancelData(response.getResponse().getMerchantUid(), false, amount); + cancelData.setChecksum(checkSum); + iamportClient.cancelPaymentByImpUid(cancelData); } From 3e6be734428f5d93d4da78365e6cfbdc2ca763be Mon Sep 17 00:00:00 2001 From: amm0124 Date: Fri, 8 Nov 2024 22:40:17 +0900 Subject: [PATCH 20/59] =?UTF-8?q?feat=20:=20after=20sales(=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20=EC=A0=84=20=EC=B7=A8=EC=86=8C,=20=ED=99=98?= =?UTF-8?q?=EB=B6=88,=20=EA=B5=90=ED=99=98)=20controller/=20service/entity?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CancelController.java | 8 ++ .../controller/ExchangeController.java | 8 ++ .../controller/RefundController.java | 42 ++++++++++ .../entity/_abstract/AbstractAfterSales.java | 77 +++++++++++++++++++ .../ProductAfterSalesRepository.java | 7 ++ .../_aftersales/service/RefundService.java | 22 ++++++ 6 files changed, 164 insertions(+) create mode 100644 src/main/java/poomasi/domain/order/_aftersales/controller/CancelController.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/controller/ExchangeController.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/controller/RefundController.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/entity/_abstract/AbstractAfterSales.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/repository/ProductAfterSalesRepository.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/service/RefundService.java diff --git a/src/main/java/poomasi/domain/order/_aftersales/controller/CancelController.java b/src/main/java/poomasi/domain/order/_aftersales/controller/CancelController.java new file mode 100644 index 00000000..9691382a --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/controller/CancelController.java @@ -0,0 +1,8 @@ +package poomasi.domain.order._aftersales.controller; + + +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CancelController { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/controller/ExchangeController.java b/src/main/java/poomasi/domain/order/_aftersales/controller/ExchangeController.java new file mode 100644 index 00000000..7d22e7fd --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/controller/ExchangeController.java @@ -0,0 +1,8 @@ +package poomasi.domain.order._aftersales.controller; + + +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ExchangeController { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/controller/RefundController.java b/src/main/java/poomasi/domain/order/_aftersales/controller/RefundController.java new file mode 100644 index 00000000..8226cec4 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/controller/RefundController.java @@ -0,0 +1,42 @@ +package poomasi.domain.order._aftersales.controller; + + +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.*; +import poomasi.domain.order._aftersales.dto.FullRefundRequest; +import poomasi.domain.order._aftersales.dto.PartialRefundRequest; +import poomasi.domain.order._aftersales.service.RefundService; + +@RestController +@RequestMapping("/api/refund") +@RequiredArgsConstructor +public class RefundController { + + private final RefundService refundService; + + + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER"}) + @GetMapping("/{refundId}") + public void getRefund(@PathVariable("refundId") Long refundId) { + + } + + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER"}) + @PostMapping("/{orderProductDetailsId}") + public void processFullRefund (@PathVariable("orderProductDetailsId") Long orderProductDetailsId, + @RequestBody FullRefundRequest fullRefundRequest) { + //TODO : order product details 내부 메서드 보고 + //TODO : 환불 가능하지 받아 와야 함 + } + + + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER"}) + @PostMapping("/api/refund/{orderProductDetailsId}") + public void processPartialRefund (@PathVariable("orderProductDetailsId") Long orderProductDetailsId, + @RequestBody PartialRefundRequest partialRefundRequest) { + + + + } +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/entity/_abstract/AbstractAfterSales.java b/src/main/java/poomasi/domain/order/_aftersales/entity/_abstract/AbstractAfterSales.java new file mode 100644 index 00000000..cbbe0afc --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/entity/_abstract/AbstractAfterSales.java @@ -0,0 +1,77 @@ +package poomasi.domain.order._aftersales.entity._abstract; + +import jakarta.persistence.*; +import jdk.jfr.Timestamp; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; + +import java.time.LocalDateTime; + +@MappedSuperclass +public abstract class AbstractAfterSales { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "created_at") + @CreationTimestamp + private LocalDateTime createdAt = LocalDateTime.now(); + + @Column(name = "updated_at") + @UpdateTimestamp + private LocalDateTime updateAt = LocalDateTime.now(); + + @Column(name = "deleted_at") + @Timestamp + private LocalDateTime deletedAt; + +} + +/* +* package poomasi.domain.order._aftersales.entity; + +import jakarta.persistence.*; +import jdk.jfr.Description; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; +import poomasi.domain.order._payment.entity.Payment; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Entity +@Table(name="refund_history") +public class Refund { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @CreationTimestamp + @Column(name = "created_at") + private LocalDateTime createdAt = LocalDateTime.now(); + + @Column(name = "updated_at") + @UpdateTimestamp + private LocalDateTime updatedAt = LocalDateTime.now(); + + @Description("삭제 시간") + private LocalDateTime deletedAt; + + @ManyToOne(fetch = FetchType.LAZY) + private Payment payment; + + + private BigDecimal refundAmount; + + @OneToOne(fetch = FetchType.LAZY) + private OrderedProduct orderProductDetails; + +private String refundReason; + +} + + * +* */ + diff --git a/src/main/java/poomasi/domain/order/_aftersales/repository/ProductAfterSalesRepository.java b/src/main/java/poomasi/domain/order/_aftersales/repository/ProductAfterSalesRepository.java new file mode 100644 index 00000000..9d274f26 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/repository/ProductAfterSalesRepository.java @@ -0,0 +1,7 @@ +package poomasi.domain.order._aftersales.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import poomasi.domain.order._aftersales.entity._product.ProductAfterSales; + +public interface ProductAfterSalesRepository extends JpaRepository { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/service/RefundService.java b/src/main/java/poomasi/domain/order/_aftersales/service/RefundService.java new file mode 100644 index 00000000..4d47f74e --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/service/RefundService.java @@ -0,0 +1,22 @@ +package poomasi.domain.order._aftersales.service; + + +import com.siot.IamportRestClient.IamportClient; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import poomasi.domain.order._aftersales.repository.ProductAfterSalesRepository; + +@Service +@RequiredArgsConstructor +public class RefundService { + + private final ProductAfterSalesRepository productAfterSalesRepository; + private final IamportClient iamportClient; + + + /* public void refundFull(){ + iamportClient. + }*/ + + +} From 968afd71f4252183e572d1906b56f67380851ccd Mon Sep 17 00:00:00 2001 From: amm0124 Date: Fri, 8 Nov 2024 22:40:33 +0900 Subject: [PATCH 21/59] =?UTF-8?q?feat=20:=20farm=20order=20details(?= =?UTF-8?q?=EB=AF=B8=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/entity/_farm/FarmOrderDetails.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/java/poomasi/domain/order/entity/_farm/FarmOrderDetails.java diff --git a/src/main/java/poomasi/domain/order/entity/_farm/FarmOrderDetails.java b/src/main/java/poomasi/domain/order/entity/_farm/FarmOrderDetails.java new file mode 100644 index 00000000..7b6e194c --- /dev/null +++ b/src/main/java/poomasi/domain/order/entity/_farm/FarmOrderDetails.java @@ -0,0 +1,28 @@ +package poomasi.domain.order.entity._farm; + +import jakarta.persistence.*; +import poomasi.domain.farm.entity.Farm; + +//@Entity +//@Table(name="farm_order_details") +public class FarmOrderDetails { + /* + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "farm_order_details_id") + private Long id; + + @OneToOne + private FarmOrder farmOrder; + + + @Column(name="farm_name") + private String farmName; + + @Column(name="farm_address") + private String farmAddress; + + +*/ + +} From cdb39300e2d8cea19a68504251de6cb146ada8f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=80=EB=AF=BC?= <108014449+stopmin@users.noreply.github.com> Date: Sat, 9 Nov 2024 00:36:19 +0900 Subject: [PATCH 22/59] =?UTF-8?q?feat:=20Reservation=EC=97=90=20price=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#108)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #99 --- .../domain/farm/dto/FarmRegisterRequest.java | 2 +- .../poomasi/domain/farm/dto/FarmResponse.java | 18 +++++++++--------- .../java/poomasi/domain/farm/entity/Farm.java | 6 +++--- .../domain/farm/service/FarmFarmerService.java | 2 +- .../dto/request/ReservationRequest.java | 1 + .../dto/response/ReservationResponse.java | 10 ++-------- .../domain/reservation/entity/Reservation.java | 8 +++++++- 7 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/main/java/poomasi/domain/farm/dto/FarmRegisterRequest.java b/src/main/java/poomasi/domain/farm/dto/FarmRegisterRequest.java index 37a5a51a..2a046509 100644 --- a/src/main/java/poomasi/domain/farm/dto/FarmRegisterRequest.java +++ b/src/main/java/poomasi/domain/farm/dto/FarmRegisterRequest.java @@ -10,7 +10,7 @@ public record FarmRegisterRequest( Double longitude, String phoneNumber, String description, - Long experiencePrice, + int experiencePrice, Integer maxCapacity, Integer maxReservation ) { diff --git a/src/main/java/poomasi/domain/farm/dto/FarmResponse.java b/src/main/java/poomasi/domain/farm/dto/FarmResponse.java index 632957e3..7b7db5cb 100644 --- a/src/main/java/poomasi/domain/farm/dto/FarmResponse.java +++ b/src/main/java/poomasi/domain/farm/dto/FarmResponse.java @@ -3,15 +3,15 @@ import poomasi.domain.farm.entity.Farm; -public record FarmResponse( // FIXME: 사용자 정보 추가 및 설명/전화번호 추가 - Long id, - String name, - String address, - String addressDetail, - Double latitude, - Double longitude, - String description, - Long experiencePrice +public record FarmResponse( + Long id, + String name, + String address, + String addressDetail, + Double latitude, + Double longitude, + String description, + int experiencePrice ) { public static FarmResponse fromEntity(Farm farm) { return new FarmResponse( diff --git a/src/main/java/poomasi/domain/farm/entity/Farm.java b/src/main/java/poomasi/domain/farm/entity/Farm.java index bb11c841..1b4921f5 100644 --- a/src/main/java/poomasi/domain/farm/entity/Farm.java +++ b/src/main/java/poomasi/domain/farm/entity/Farm.java @@ -56,7 +56,7 @@ public class Farm { private FarmStatus status = FarmStatus.OPEN; @Comment("체험 비용") - private Long experiencePrice; + private int experiencePrice; @Comment("팀 최대 인원") private Integer maxCapacity; @@ -80,7 +80,7 @@ public class Farm { List reviewList = new ArrayList<>(); @Builder - public Farm(Long id, String name, Long ownerId, String address, String addressDetail, Double latitude, Double longitude, String description, Long experiencePrice, Integer maxCapacity, Integer maxReservation) { + public Farm(Long id, String name, Long ownerId, String address, String addressDetail, Double latitude, Double longitude, String description, int experiencePrice, Integer maxCapacity, Integer maxReservation) { this.id = id; this.name = name; this.ownerId = ownerId; @@ -104,7 +104,7 @@ public Farm updateFarm(FarmUpdateRequest farmUpdateRequest) { return this; } - public void updateExpPrice(Long expPrice) { + public void updateExpPrice(int expPrice) { this.experiencePrice = expPrice; } diff --git a/src/main/java/poomasi/domain/farm/service/FarmFarmerService.java b/src/main/java/poomasi/domain/farm/service/FarmFarmerService.java index bae12aa7..fe12e1b3 100644 --- a/src/main/java/poomasi/domain/farm/service/FarmFarmerService.java +++ b/src/main/java/poomasi/domain/farm/service/FarmFarmerService.java @@ -40,7 +40,7 @@ private Farm getFarmByFarmId(Long farmId) { return farmRepository.findByIdAndDeletedAtIsNull(farmId).orElseThrow(() -> new BusinessException(FARM_NOT_FOUND)); } - public void updateFarmExpPrice(Long farmerId, Long farmId, Long expPrice) { + public void updateFarmExpPrice(Long farmerId, Long farmId, int expPrice) { Farm farm = this.getFarmByFarmId(farmId); if (!farm.getOwnerId().equals(farmerId)) { throw new BusinessException(FARM_OWNER_MISMATCH); diff --git a/src/main/java/poomasi/domain/reservation/dto/request/ReservationRequest.java b/src/main/java/poomasi/domain/reservation/dto/request/ReservationRequest.java index 9b2d3b01..412391a8 100644 --- a/src/main/java/poomasi/domain/reservation/dto/request/ReservationRequest.java +++ b/src/main/java/poomasi/domain/reservation/dto/request/ReservationRequest.java @@ -23,6 +23,7 @@ public Reservation toEntity(Member member, Farm farm, FarmSchedule farmSchedule) .reservationDate(farmSchedule.getDate()) .memberCount(memberCount) .request(request) + .price(farm.getExperiencePrice() * memberCount) .status(ReservationStatus.ACCEPTED) .build(); } diff --git a/src/main/java/poomasi/domain/reservation/dto/response/ReservationResponse.java b/src/main/java/poomasi/domain/reservation/dto/response/ReservationResponse.java index 0b397eff..c54c3116 100644 --- a/src/main/java/poomasi/domain/reservation/dto/response/ReservationResponse.java +++ b/src/main/java/poomasi/domain/reservation/dto/response/ReservationResponse.java @@ -6,14 +6,8 @@ import java.time.LocalDate; @Builder -public record ReservationResponse( - Long farmId, - Long memberId, - Long scheduleId, - LocalDate reservationDate, - int memberCount, - ReservationStatus status, - String request +public record ReservationResponse(Long farmId, Long memberId, Long scheduleId, LocalDate reservationDate, + int memberCount, ReservationStatus status, String request, int price ) { } diff --git a/src/main/java/poomasi/domain/reservation/entity/Reservation.java b/src/main/java/poomasi/domain/reservation/entity/Reservation.java index a39ca6b0..6249157e 100644 --- a/src/main/java/poomasi/domain/reservation/entity/Reservation.java +++ b/src/main/java/poomasi/domain/reservation/entity/Reservation.java @@ -60,6 +60,10 @@ public class Reservation { @Column(nullable = false) private String request; + @Comment("결제 예정 금액") + @Column(nullable = false) + private int price; + @CreationTimestamp private LocalDateTime createdAt; @@ -71,7 +75,7 @@ public class Reservation { @Builder - public Reservation(Farm farm, Member member, FarmSchedule scheduleId, LocalDate reservationDate, int memberCount, ReservationStatus status, String request) { + public Reservation(Farm farm, Member member, FarmSchedule scheduleId, LocalDate reservationDate, int memberCount, ReservationStatus status, String request, int price) { this.farm = farm; this.member = member; this.scheduleId = scheduleId; @@ -79,6 +83,7 @@ public Reservation(Farm farm, Member member, FarmSchedule scheduleId, LocalDate this.memberCount = memberCount; this.status = status; this.request = request; + this.price = price; } public ReservationResponse toResponse() { @@ -90,6 +95,7 @@ public ReservationResponse toResponse() { .memberCount(memberCount) .status(status) .request(request) + .price(price) .build(); } From c33eca45696c06eb474e91f1c7f06f2cec70b0f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=80=EB=AF=BC?= <108014449+stopmin@users.noreply.github.com> Date: Sat, 9 Nov 2024 01:01:20 +0900 Subject: [PATCH 23/59] =?UTF-8?q?[CICD]=20tf-prod.json=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80=20(#115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: tf-prod.json 추가 #114 * feat: deploy-ecs.yml - java version 다운그레이드 - prod환경으로 수정 #114 --- .github/workflows/deploy-ecs.yml | 6 ++-- tf-prod.json | 48 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 tf-prod.json diff --git a/.github/workflows/deploy-ecs.yml b/.github/workflows/deploy-ecs.yml index 5b04a274..233e8472 100644 --- a/.github/workflows/deploy-ecs.yml +++ b/.github/workflows/deploy-ecs.yml @@ -13,7 +13,7 @@ env: ECR_REPOSITORY: poomasi-server ECS_SERVICE: poomasi-server ECS_CLUSTER: poomasi - ECS_TASK_DEFINITION: tf-staging.json + ECS_TASK_DEFINITION: tf-prod.json CONTAINER_NAME: spring PROGRESS_SLACK_CHANNEL: C080DMAE7MX permissions: @@ -64,10 +64,10 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Set up JDK 22 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: - java-version: '22' + java-version: '21' distribution: 'adopt' - name: Grant execute permission for gradlew diff --git a/tf-prod.json b/tf-prod.json new file mode 100644 index 00000000..8f0a6d52 --- /dev/null +++ b/tf-prod.json @@ -0,0 +1,48 @@ +{ + "family": "poomasi-server", + "containerDefinitions": [ + { + "name": "spring", + "image": "881490087445.dkr.ecr.ap-northeast-2.amazonaws.com/poomasi-server:latest", + "cpu": 0, + "portMappings": [ + { + "name": "http", + "containerPort": 8080, + "hostPort": 0, + "protocol": "tcp", + "appProtocol": "http" + } + ], + "essential": true, + "environment": [], + "mountPoints": [], + "volumesFrom": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "/ecs/poomasi-server", + "mode": "non-blocking", + "awslogs-create-group": "true", + "max-buffer-size": "25m", + "awslogs-region": "ap-northeast-2", + "awslogs-stream-prefix": "ecs" + } + }, + "systemControls": [] + } + ], + "executionRoleArn": "arn:aws:iam::881490087445:role/ecsTaskExecutionRole", + "networkMode": "bridge", + "volumes": [], + "placementConstraints": [], + "requiresCompatibilities": [ + "EC2" + ], + "cpu": "512", + "memory": "512", + "runtimePlatform": { + "cpuArchitecture": "X86_64", + "operatingSystemFamily": "LINUX" + } +} From 9872fe398a38a830eece69854aae14491f588eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=80=EB=AF=BC?= <108014449+stopmin@users.noreply.github.com> Date: Sat, 9 Nov 2024 01:42:50 +0900 Subject: [PATCH 24/59] =?UTF-8?q?[CI]=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=20=EC=8B=9C=20GitHub=20=EB=B4=87=20=EB=8C=93?= =?UTF-8?q?=EA=B8=80=20=EC=95=8C=EB=A6=BC=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#117)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: ci-test.yml #116 * feat: ci-test.yml - permission 추가 #116 * feat: ci-test.yml - gradlew 권한 추가 #116 * feat: ci-test.yml - secondary rate limit 대기시간 추가 #116 * feat: ci-test.yml - 권한 수정 #116 * feat: ci-test.yml - 권한 수정 #116 * feat: ci-test.yml - 권한 수정 #116 * feat: ci-test.yml - 권한 수정 #116 * feat: ci-test.yml - gradlew 실행 권한 부여 #116 --- .github/workflows/ci-test.yml | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/ci-test.yml diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml new file mode 100644 index 00000000..4e8b8de5 --- /dev/null +++ b/.github/workflows/ci-test.yml @@ -0,0 +1,48 @@ +name: Run Tests on PR + +on: + pull_request: + types: [ opened, synchronize, reopened ] + +permissions: + contents: write + pull-requests: write + checks: write + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v3 + with: + java-version: '21' + distribution: 'adopt' + + - name: Cache Gradle packages + uses: actions/cache@v3 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Grant execute permission for gradlew + run: chmod +x ./gradlew + + - name: Run tests + run: ./gradlew test + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: 테스트 결과를 PR에 코멘트로 등록합니다 + uses: EnricoMi/publish-unit-test-result-action@v1 + if: always() + with: + files: '**/build/test-results/test/TEST-*.xml' + github_token: ${{ secrets.GITHUB_TOKEN }} + seconds_between_github_reads: 5 + seconds_between_github_writes: 5 From a01f27f8583a5b62ff845878dd5e539ee3dd38a9 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Sat, 9 Nov 2024 03:12:32 +0900 Subject: [PATCH 25/59] =?UTF-8?q?feat=20:=20=EA=B8=B0=EC=A1=B4=20controlle?= =?UTF-8?q?r=20=EC=82=AD=EC=A0=9C=20-=20=ED=95=98=EB=82=98=EB=A1=9C=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9=20=EC=98=88=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CancelController.java | 8 ---- .../controller/ExchangeController.java | 8 ---- .../controller/RefundController.java | 42 ------------------- 3 files changed, 58 deletions(-) delete mode 100644 src/main/java/poomasi/domain/order/_aftersales/controller/CancelController.java delete mode 100644 src/main/java/poomasi/domain/order/_aftersales/controller/ExchangeController.java delete mode 100644 src/main/java/poomasi/domain/order/_aftersales/controller/RefundController.java diff --git a/src/main/java/poomasi/domain/order/_aftersales/controller/CancelController.java b/src/main/java/poomasi/domain/order/_aftersales/controller/CancelController.java deleted file mode 100644 index 9691382a..00000000 --- a/src/main/java/poomasi/domain/order/_aftersales/controller/CancelController.java +++ /dev/null @@ -1,8 +0,0 @@ -package poomasi.domain.order._aftersales.controller; - - -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class CancelController { -} diff --git a/src/main/java/poomasi/domain/order/_aftersales/controller/ExchangeController.java b/src/main/java/poomasi/domain/order/_aftersales/controller/ExchangeController.java deleted file mode 100644 index 7d22e7fd..00000000 --- a/src/main/java/poomasi/domain/order/_aftersales/controller/ExchangeController.java +++ /dev/null @@ -1,8 +0,0 @@ -package poomasi.domain.order._aftersales.controller; - - -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class ExchangeController { -} diff --git a/src/main/java/poomasi/domain/order/_aftersales/controller/RefundController.java b/src/main/java/poomasi/domain/order/_aftersales/controller/RefundController.java deleted file mode 100644 index 8226cec4..00000000 --- a/src/main/java/poomasi/domain/order/_aftersales/controller/RefundController.java +++ /dev/null @@ -1,42 +0,0 @@ -package poomasi.domain.order._aftersales.controller; - - -import lombok.RequiredArgsConstructor; -import org.springframework.security.access.annotation.Secured; -import org.springframework.web.bind.annotation.*; -import poomasi.domain.order._aftersales.dto.FullRefundRequest; -import poomasi.domain.order._aftersales.dto.PartialRefundRequest; -import poomasi.domain.order._aftersales.service.RefundService; - -@RestController -@RequestMapping("/api/refund") -@RequiredArgsConstructor -public class RefundController { - - private final RefundService refundService; - - - @Secured({"ROLE_CUSTOMER", "ROLE_FARMER"}) - @GetMapping("/{refundId}") - public void getRefund(@PathVariable("refundId") Long refundId) { - - } - - @Secured({"ROLE_CUSTOMER", "ROLE_FARMER"}) - @PostMapping("/{orderProductDetailsId}") - public void processFullRefund (@PathVariable("orderProductDetailsId") Long orderProductDetailsId, - @RequestBody FullRefundRequest fullRefundRequest) { - //TODO : order product details 내부 메서드 보고 - //TODO : 환불 가능하지 받아 와야 함 - } - - - @Secured({"ROLE_CUSTOMER", "ROLE_FARMER"}) - @PostMapping("/api/refund/{orderProductDetailsId}") - public void processPartialRefund (@PathVariable("orderProductDetailsId") Long orderProductDetailsId, - @RequestBody PartialRefundRequest partialRefundRequest) { - - - - } -} From d12704e46a08bbdcec48d18037c1a2d64c59679a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=80=EB=AF=BC?= <108014449+stopmin@users.noreply.github.com> Date: Sat, 9 Nov 2024 15:04:19 +0900 Subject: [PATCH 26/59] =?UTF-8?q?[TEST]=20Farm=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1=20(#112)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: 농장이 존재하지 않는 경우 #93 * chore: Member Builder 수정 - ID 추가 (테스트 용) #93 * test: FarmFarmerServiceTest - 농장 등록 테스트 - 농장 정보 업데이트 테스트 - 농장 삭제 테스트 #93 * test: FarmPlatformServiceTest - ID로 농장 가져오기 - 농장 리스트 가져오기 #93 --- .../poomasi/domain/member/entity/Member.java | 3 +- .../farm/service/FarmFarmerServiceTest.java | 141 ++++++++++++++++++ .../farm/service/FarmPlatformServiceTest.java | 105 +++++++++++++ .../domain/farm/service/FarmServiceTest.java | 16 ++ 4 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 src/test/java/poomasi/domain/farm/service/FarmFarmerServiceTest.java create mode 100644 src/test/java/poomasi/domain/farm/service/FarmPlatformServiceTest.java diff --git a/src/main/java/poomasi/domain/member/entity/Member.java b/src/main/java/poomasi/domain/member/entity/Member.java index 3ae6cc9b..4dc7828b 100644 --- a/src/main/java/poomasi/domain/member/entity/Member.java +++ b/src/main/java/poomasi/domain/member/entity/Member.java @@ -79,7 +79,8 @@ public void setMemberProfile(MemberProfile memberProfile) { } @Builder - public Member(String email, Role role, LoginType loginType, String provideId, MemberProfile memberProfile) { + public Member(Long id, String email, Role role, LoginType loginType, String provideId, MemberProfile memberProfile) { + this.id = id; this.email = email; this.role = role; this.loginType = loginType; diff --git a/src/test/java/poomasi/domain/farm/service/FarmFarmerServiceTest.java b/src/test/java/poomasi/domain/farm/service/FarmFarmerServiceTest.java new file mode 100644 index 00000000..c34ca6f8 --- /dev/null +++ b/src/test/java/poomasi/domain/farm/service/FarmFarmerServiceTest.java @@ -0,0 +1,141 @@ +package poomasi.domain.farm.service; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import poomasi.domain.farm.dto.FarmRegisterRequest; +import poomasi.domain.farm.dto.FarmUpdateRequest; +import poomasi.domain.farm.entity.Farm; +import poomasi.domain.farm.repository.FarmRepository; +import poomasi.domain.member.entity.Member; +import poomasi.global.error.BusinessError; +import poomasi.global.error.BusinessException; + +import java.util.Optional; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class FarmFarmerServiceTest { + + @InjectMocks + private FarmFarmerService farmFarmerService; + + @Mock + private FarmRepository farmRepository; + + @Nested + @DisplayName("농장 등록") + class RegisterFarm { + @Test + @DisplayName("농장이 이미 존재하는 경우 예외를 발생시킨다") + void should_throwException_when_farmAlreadyExists() { + // given + Member member = Member.builder() + .id(1L) + .build(); + Farm existingFarm = Farm.builder() + .id(1L) + .name("Existing Farm") + .ownerId(1L) + .build(); + + given(farmRepository.getFarmByOwnerIdAndDeletedAtIsNull(member.getId())).willReturn(Optional.of(existingFarm)); + + FarmRegisterRequest request = new FarmRegisterRequest("New Farm", "Address", "Detail", 1.0, 1.0, "010-1234-5678", "Description", 10000L, 10, 5); + + // when & then + assertThatThrownBy(() -> farmFarmerService.registerFarm(member, request)) + .isInstanceOf(BusinessException.class) + .hasFieldOrPropertyWithValue("businessError", BusinessError.FARM_ALREADY_EXISTS); + } + } + + @Nested + @DisplayName("농장 정보 업데이트") + class UpdateFarm { + @Test + @DisplayName("농장이 존재하지 않는 경우 예외를 발생시킨다") + void should_throwException_when_farmNotExist() { + // given + Long farmId = 1L; + FarmUpdateRequest request = new FarmUpdateRequest(farmId, "Updated Farm", "Description", "Address", "Detail", 1.0, 1.0); + given(farmRepository.findByIdAndDeletedAtIsNull(farmId)).willReturn(Optional.empty()); + + // when & then + assertThatThrownBy(() -> farmFarmerService.updateFarm(1L, request)) + .isInstanceOf(BusinessException.class) + .hasFieldOrPropertyWithValue("businessError", BusinessError.FARM_NOT_FOUND); + } + + @Test + @DisplayName("농장 소유자가 아닌 경우 예외를 발생시킨다") + void should_throwException_when_ownerMismatch() { + // given + Long farmId = 1L; + Long farmerId = 2L; + Farm farm = Farm.builder() + .id(farmId) + .name("Farm") + .ownerId(3L) + .build(); + given(farmRepository.findByIdAndDeletedAtIsNull(farmId)).willReturn(Optional.of(farm)); + + FarmUpdateRequest request = new FarmUpdateRequest(farmId, "Updated Farm", "Description", "Address", "Detail", 1.0, 1.0); + + // when & then + assertThatThrownBy(() -> farmFarmerService.updateFarm(farmerId, request)) + .isInstanceOf(BusinessException.class) + .hasFieldOrPropertyWithValue("businessError", BusinessError.FARM_OWNER_MISMATCH); + } + } + + @Nested + @DisplayName("농장 삭제") + class DeleteFarm { + @Test + @DisplayName("소유자가 아닌 경우 예외를 발생시킨다") + void should_throwException_when_ownerMismatchOnDelete() { + // given + Long farmId = 1L; + Long farmerId = 2L; + Farm farm = Farm.builder() + .id(farmId) + .name("Farm") + .ownerId(3L) + .build(); + given(farmRepository.findByIdAndDeletedAtIsNull(farmId)).willReturn(Optional.of(farm)); + + // when & then + assertThatThrownBy(() -> farmFarmerService.deleteFarm(farmerId, farmId)) + .isInstanceOf(BusinessException.class) + .hasFieldOrPropertyWithValue("businessError", BusinessError.FARM_OWNER_MISMATCH); + } + + @Test + @DisplayName("농장 삭제에 성공한다") + void should_deleteFarm_when_ownerMatches() { + // given + Long farmId = 1L; + Long farmerId = 1L; + Farm farm = Farm.builder() + .id(farmId) + .name("Farm") + .ownerId(farmerId) + .build(); + given(farmRepository.findByIdAndDeletedAtIsNull(farmId)).willReturn(Optional.of(farm)); + + // when + farmFarmerService.deleteFarm(farmerId, farmId); + + // then + verify(farmRepository).delete(farm); + } + } +} diff --git a/src/test/java/poomasi/domain/farm/service/FarmPlatformServiceTest.java b/src/test/java/poomasi/domain/farm/service/FarmPlatformServiceTest.java new file mode 100644 index 00000000..60759722 --- /dev/null +++ b/src/test/java/poomasi/domain/farm/service/FarmPlatformServiceTest.java @@ -0,0 +1,105 @@ +package poomasi.domain.farm.service; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import poomasi.domain.farm.dto.FarmResponse; +import poomasi.domain.farm.entity.Farm; +import poomasi.global.error.BusinessError; +import poomasi.global.error.BusinessException; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +class FarmPlatformServiceTest { + + @InjectMocks + private FarmPlatformService farmPlatformService; + + @Mock + private FarmService farmService; + + @Nested + @DisplayName("ID로 농장 가져오기") + class GetFarmByFarmId { + @Test + @DisplayName("농장 ID로 농장 정보를 성공적으로 가져온다") + void should_returnFarmResponse_when_farmExists() { + // given + Long farmId = 1L; + Farm farm = Farm.builder() + .id(farmId) + .name("Test Farm") + .ownerId(1L) + .build(); + given(farmService.getFarmByFarmId(farmId)).willReturn(farm); + + // when + FarmResponse response = farmPlatformService.getFarmByFarmId(farmId); + + // then + assertThat(response.id()).isEqualTo(farmId); + assertThat(response.name()).isEqualTo("Test Farm"); + verify(farmService).getFarmByFarmId(farmId); // farmService 호출 확인 + } + + @Test + @DisplayName("농장 ID로 농장 정보를 가져오는데 농장이 존재하지 않는 경우 예외를 발생시킨다") + void should_throwException_when_farmNotExist() { + // given + Long farmId = 1L; + given(farmService.getFarmByFarmId(farmId)).willThrow(new BusinessException(BusinessError.FARM_NOT_FOUND)); + + // when & then + assertThatThrownBy(() -> farmPlatformService.getFarmByFarmId(farmId)) + .isInstanceOf(BusinessException.class) + .hasFieldOrPropertyWithValue("businessError", BusinessError.FARM_NOT_FOUND); + + verify(farmService).getFarmByFarmId(farmId); + } + } + + @Nested + @DisplayName("농장 리스트 가져오기") + class GetFarmList { + @Test + @DisplayName("페이징 요청으로 농장 리스트를 성공적으로 가져온다") + void should_returnFarmResponseList_when_farmsExist() { + // given + Pageable pageable = PageRequest.of(0, 10); + Farm farm1 = Farm.builder() + .id(1L) + .name("Farm 1") + .ownerId(1L) + .build(); + Farm farm2 = Farm.builder() + .id(2L) + .name("Farm 2") + .ownerId(2L) + .build(); + List farmList = List.of(farm1, farm2); + + given(farmService.getFarmList(pageable)).willReturn(farmList); + + // when + List responseList = farmPlatformService.getFarmList(pageable); + + // then + assertThat(responseList).hasSize(2); + assertThat(responseList.get(0).name()).isEqualTo("Farm 1"); + assertThat(responseList.get(1).name()).isEqualTo("Farm 2"); + verify(farmService).getFarmList(pageable); // farmService 호출 확인 + } + } +} diff --git a/src/test/java/poomasi/domain/farm/service/FarmServiceTest.java b/src/test/java/poomasi/domain/farm/service/FarmServiceTest.java index cbcb6935..cc9603f0 100644 --- a/src/test/java/poomasi/domain/farm/service/FarmServiceTest.java +++ b/src/test/java/poomasi/domain/farm/service/FarmServiceTest.java @@ -10,10 +10,13 @@ import poomasi.domain.farm.FarmTestHelper; import poomasi.domain.farm.entity.Farm; import poomasi.domain.farm.repository.FarmRepository; +import poomasi.global.error.BusinessError; +import poomasi.global.error.BusinessException; import java.util.Optional; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.mockito.BDDMockito.given; @ExtendWith(MockitoExtension.class) @@ -42,6 +45,19 @@ void should_successGetFarm_when_farmExist() { assertThat(result.getName()).isEqualTo(farm.getName()); assertThat(result.getStatus()).isEqualTo(farm.getStatus()); } + + @Test + @DisplayName("농장이 존재하지 않는 경우 예외를 발생시킨다") + void should_throwException_when_farmNotExist() { + // given + Long farmId = 1L; + given(farmRepository.findByIdAndDeletedAtIsNull(farmId)).willReturn(Optional.empty()); + + // when & then + assertThatThrownBy(() -> farmService.getFarmByFarmId(farmId)) + .isInstanceOf(BusinessException.class) + .hasFieldOrPropertyWithValue("businessError", BusinessError.FARM_NOT_FOUND); + } } } From 44442b678d51dcdc1a28ffffb3047caa341dc2a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sat, 9 Nov 2024 16:31:53 +0900 Subject: [PATCH 27/59] =?UTF-8?q?refactor:=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=9E=AC=EB=B0=9C=EA=B8=89=20=EC=95=A1=EC=84=B8=EC=8A=A4=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=B6=94=EC=B6=9C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20#124?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReissueTokenController.java | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/java/poomasi/domain/auth/token/reissue/controller/ReissueTokenController.java b/src/main/java/poomasi/domain/auth/token/reissue/controller/ReissueTokenController.java index 43aeb0c8..f0ecadff 100644 --- a/src/main/java/poomasi/domain/auth/token/reissue/controller/ReissueTokenController.java +++ b/src/main/java/poomasi/domain/auth/token/reissue/controller/ReissueTokenController.java @@ -1,30 +1,33 @@ package poomasi.domain.auth.token.reissue.controller; - -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; -import org.springframework.security.access.annotation.Secured; -import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; -import poomasi.domain.auth.security.userdetail.UserDetailsImpl; import poomasi.domain.auth.token.reissue.dto.ReissueRequest; import poomasi.domain.auth.token.reissue.dto.ReissueResponse; import poomasi.domain.auth.token.reissue.service.ReissueTokenService; -import poomasi.domain.member.entity.Member; +import poomasi.domain.auth.token.util.JwtUtil; @RestController +@RequiredArgsConstructor public class ReissueTokenController { - @Autowired - private ReissueTokenService reissueTokenService; + private final ReissueTokenService reissueTokenService; + private final JwtUtil jwtUtil; - @Secured({"ROLE_FARMER", "ROLE_CUSTOMER"}) @PostMapping("/api/reissue") - public ResponseEntity reissue(@AuthenticationPrincipal UserDetailsImpl userDetails, @RequestBody ReissueRequest reissueRequest){ - Member member = userDetails.getMember(); - return ResponseEntity.ok(reissueTokenService.reissueToken(member.getId(), reissueRequest)); + public ResponseEntity reissue(@RequestHeader(HttpHeaders.AUTHORIZATION) String authorizationHeader, + @RequestBody ReissueRequest reissueRequest){ + + String token = authorizationHeader.replace("Bearer ", ""); + + Long memberId = jwtUtil.getIdFromToken(token); + + return ResponseEntity.ok(reissueTokenService.reissueToken(memberId, reissueRequest)); } } From 0c6761947a999c01fc43ec4aab586b3904ec841c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sat, 9 Nov 2024 16:33:56 +0900 Subject: [PATCH 28/59] =?UTF-8?q?refactor:=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=EB=B3=B8=EC=9D=B8=20=EC=A1=B0=ED=9A=8C=20=EA=B6=8C=ED=95=9C?= =?UTF-8?q?=EC=97=90=20=EA=B4=80=EB=A6=AC=EC=9E=90=EB=8F=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20#124?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/poomasi/domain/member/controller/MemberController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/poomasi/domain/member/controller/MemberController.java b/src/main/java/poomasi/domain/member/controller/MemberController.java index 8f057dba..f0b3e7b1 100644 --- a/src/main/java/poomasi/domain/member/controller/MemberController.java +++ b/src/main/java/poomasi/domain/member/controller/MemberController.java @@ -60,7 +60,7 @@ public ResponseEntity> getMembers(@PageableDefault(size = 1 } @GetMapping("/self") - @Secured({"ROLE_MEMBER", "ROLE_FARMER"}) + @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) public ResponseEntity getSelfMember(@AuthenticationPrincipal UserDetailsImpl userDetails) { Member member = userDetails.getMember(); MemberResponse memberResponse = memberService.getMemberById(member.getId()); From abddbd94a7ad2b85ac2f1abba04f9834132a9fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sat, 9 Nov 2024 16:50:48 +0900 Subject: [PATCH 29/59] =?UTF-8?q?refactor:=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EA=B6=8C=ED=95=9C=20=EB=86=8D=EB=B6=80=EB=A1=9C=20=EC=A0=84?= =?UTF-8?q?=ED=99=98=20=EB=A1=9C=EC=A7=81=20=EA=B6=8C=ED=95=9C=EC=9D=84=20?= =?UTF-8?q?CUSTOMER=EB=A1=9C=20=EC=88=98=EC=A0=95=20#124?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/image/controller/ImageController.java | 10 +++++----- .../domain/member/controller/MemberController.java | 13 +++++++------ .../domain/member/service/MemberService.java | 4 +--- .../global/config/s3/S3PresignedUrlController.java | 4 ++-- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/main/java/poomasi/domain/image/controller/ImageController.java b/src/main/java/poomasi/domain/image/controller/ImageController.java index d9211481..26000941 100644 --- a/src/main/java/poomasi/domain/image/controller/ImageController.java +++ b/src/main/java/poomasi/domain/image/controller/ImageController.java @@ -22,7 +22,7 @@ public class ImageController { // 이미지 정보 저장 @PostMapping - @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER", "ROLE_ADMIN"}) public ResponseEntity saveImageInfo(@AuthenticationPrincipal UserDetailsImpl userDetails, @RequestBody ImageRequest imageRequest) { Member member = userDetails.getMember(); Image savedImage = imageService.saveImage(member.getId(), imageRequest); @@ -31,7 +31,7 @@ public ResponseEntity saveImageInfo(@AuthenticationPrincipal UserDetailsImpl // 여러 이미지 정보 저장 @PostMapping("/multiple") - @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER", "ROLE_ADMIN"}) public ResponseEntity> saveMultipleImages(@AuthenticationPrincipal UserDetailsImpl userDetails, @RequestBody List imageRequests) { Member member = userDetails.getMember(); List savedImages = imageService.saveMultipleImages(member.getId(), imageRequests); @@ -40,7 +40,7 @@ public ResponseEntity> saveMultipleImages(@AuthenticationPrincipal U // 특정 이미지 삭제 @DeleteMapping("/delete/{id}") - @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER", "ROLE_ADMIN"}) public ResponseEntity deleteImage(@AuthenticationPrincipal UserDetailsImpl userDetails, @PathVariable Long id) { Member member = userDetails.getMember(); imageService.deleteImage(member.getId(), id); @@ -62,7 +62,7 @@ public ResponseEntity> getImagesByTypeAndReference(@PathVariable Ima // 이미지 정보 수정 @PutMapping("update/{id}") - @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER", "ROLE_ADMIN"}) public ResponseEntity updateImageInfo(@AuthenticationPrincipal UserDetailsImpl userDetails, @PathVariable Long id, @RequestBody ImageRequest imageRequest) { @@ -72,7 +72,7 @@ public ResponseEntity updateImageInfo(@AuthenticationPrincipal UserDetailsImp } @PutMapping("/recover/{id}") - @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER", "ROLE_ADMIN"}) public ResponseEntity recoverImage(@AuthenticationPrincipal UserDetailsImpl userDetails, @PathVariable Long id) { Member member = userDetails.getMember(); imageService.recoverImage(member.getId(), id); diff --git a/src/main/java/poomasi/domain/member/controller/MemberController.java b/src/main/java/poomasi/domain/member/controller/MemberController.java index f0b3e7b1..32d8bdf7 100644 --- a/src/main/java/poomasi/domain/member/controller/MemberController.java +++ b/src/main/java/poomasi/domain/member/controller/MemberController.java @@ -30,11 +30,12 @@ public ResponseEntity signUp(@RequestBody SignupRequest signupRe .signUp(signupRequest)); } - @PutMapping("/toFarmer/{memberId}") - @Secured("ROLE_ADMIN") - public ResponseEntity convertToFarmer(@PathVariable Long memberId, + @PutMapping("/toFarmer") + @Secured("ROLE_CUSTOMER") + public ResponseEntity convertToFarmer(@AuthenticationPrincipal UserDetailsImpl userDetails, @RequestBody FarmerQualificationRequest request) { - memberService.convertToFarmer(memberId, request.hasFarmerQualification()); + Member member = userDetails.getMember(); + memberService.convertToFarmer(member, request.hasFarmerQualification()); return ResponseEntity.noContent().build(); } @@ -60,7 +61,7 @@ public ResponseEntity> getMembers(@PageableDefault(size = 1 } @GetMapping("/self") - @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER", "ROLE_ADMIN"}) public ResponseEntity getSelfMember(@AuthenticationPrincipal UserDetailsImpl userDetails) { Member member = userDetails.getMember(); MemberResponse memberResponse = memberService.getMemberById(member.getId()); @@ -68,7 +69,7 @@ public ResponseEntity getSelfMember(@AuthenticationPrincipal Use } @GetMapping("/summary/{memberId}") - @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER", "ROLE_ADMIN"}) public ResponseEntity getMemberSummaryById(@PathVariable Long memberId) { MemberSummaryResponse memberSummaryResponse = memberService.getMemberSummary(memberId); return ResponseEntity.ok(memberSummaryResponse); diff --git a/src/main/java/poomasi/domain/member/service/MemberService.java b/src/main/java/poomasi/domain/member/service/MemberService.java index 9a9d51b2..6ea5669a 100644 --- a/src/main/java/poomasi/domain/member/service/MemberService.java +++ b/src/main/java/poomasi/domain/member/service/MemberService.java @@ -62,9 +62,7 @@ public Page getAllMembers(Pageable pageable) { } @Transactional - public void convertToFarmer(Long memberId, Boolean hasFarmerQualification) { - Member member = findMemberById(memberId); - + public void convertToFarmer(Member member, Boolean hasFarmerQualification) { if (member.isFarmer()) { throw new BusinessException(MEMBER_ALREADY_FARMER); } diff --git a/src/main/java/poomasi/global/config/s3/S3PresignedUrlController.java b/src/main/java/poomasi/global/config/s3/S3PresignedUrlController.java index 12c3ebe5..44f5e52b 100644 --- a/src/main/java/poomasi/global/config/s3/S3PresignedUrlController.java +++ b/src/main/java/poomasi/global/config/s3/S3PresignedUrlController.java @@ -15,14 +15,14 @@ public class S3PresignedUrlController { private final AwsProperties awsProperties; @GetMapping("/presigned-url-get") - @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER", "ROLE_ADMIN"}) public ResponseEntity presignedUrlGet(@RequestParam String keyname) { String presignedGetUrl = s3PresignedUrlService.createPresignedGetUrl(awsProperties.getS3().getBucket(), keyname); return ResponseEntity.ok(presignedGetUrl); } @PostMapping("/presigned-url-put") - @Secured({"ROLE_MEMBER", "ROLE_FARMER", "ROLE_ADMIN"}) + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER", "ROLE_ADMIN"}) public ResponseEntity presignedUrlPut(@RequestBody PresignedUrlPutRequest request) { String presignedPutUrl = s3PresignedUrlService.createPresignedPutUrl(awsProperties.getS3().getBucket(), request.keyPrefix(), request.metadata()); return ResponseEntity.ok(presignedPutUrl); From f5d3a9e9b828b38631f6f0fa2022dc3b2b28a588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sat, 9 Nov 2024 17:16:41 +0900 Subject: [PATCH 30/59] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=EC=A0=9C=ED=95=9C=20=EA=B0=AF=EC=88=98=20=EC=83=81=EC=88=98?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20#124?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../poomasi/domain/image/service/ImageService.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/poomasi/domain/image/service/ImageService.java b/src/main/java/poomasi/domain/image/service/ImageService.java index eede9992..bb099c7a 100644 --- a/src/main/java/poomasi/domain/image/service/ImageService.java +++ b/src/main/java/poomasi/domain/image/service/ImageService.java @@ -26,6 +26,10 @@ @RequiredArgsConstructor @Transactional(readOnly = true) public class ImageService { + + private static final int DEFAULT_IMAGE_LIMIT = 5; + private static final int MEMBER_PROFILE_IMAGE_LIMIT = 1; + private final ImageRepository imageRepository; private final ImageOwnerValidatorFactory validatorFactory; private final MemberRepository memberRepository; @@ -83,8 +87,10 @@ private Image recoverImageOrThrow(Image existingImage, ImageRequest imageRequest } private void validateImageLimit(ImageRequest imageRequest) { - int imageLimit = 5; - if (imageRequest.type() == ImageType.MEMBER_PROFILE) imageLimit = 1; // 멤버 프로필 이미지는 한 장으로 제한 + int imageLimit = DEFAULT_IMAGE_LIMIT; + if (imageRequest.type() == ImageType.MEMBER_PROFILE) { + imageLimit = MEMBER_PROFILE_IMAGE_LIMIT; // 멤버 프로필 이미지는 한 장으로 제한 + } if (imageRepository.countByTypeAndReferenceIdAndDeletedAtIsNull(imageRequest.type(), imageRequest.referenceId()) >= imageLimit) { throw new BusinessException(IMAGE_LIMIT_EXCEED); From f6642973f88beaf92c32bdea6a943e0b3c4abbce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sat, 9 Nov 2024 17:38:35 +0900 Subject: [PATCH 31/59] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=EC=84=9C=EB=B9=84=EC=8A=A4=EC=97=90=EC=84=9C=20=EB=A9=A4?= =?UTF-8?q?=EB=B2=84=20=ED=94=84=EB=A1=9C=ED=95=84=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=EB=A5=BC=20=EC=9D=98=EC=A1=B4=20#124?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/image/service/ImageService.java | 8 ++--- .../dto/request/MemberProfileRequest.java | 11 +++++++ .../member/_profile/entity/MemberProfile.java | 3 ++ .../service/MemberProfileService.java | 30 +++++++++++++++++++ 4 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 src/main/java/poomasi/domain/member/_profile/dto/request/MemberProfileRequest.java create mode 100644 src/main/java/poomasi/domain/member/_profile/service/MemberProfileService.java diff --git a/src/main/java/poomasi/domain/image/service/ImageService.java b/src/main/java/poomasi/domain/image/service/ImageService.java index bb099c7a..bff0bf1d 100644 --- a/src/main/java/poomasi/domain/image/service/ImageService.java +++ b/src/main/java/poomasi/domain/image/service/ImageService.java @@ -10,7 +10,6 @@ import poomasi.domain.image.validation.ImageOwnerValidator; import poomasi.domain.image.validation.ImageOwnerValidatorFactory; import poomasi.domain.member._profile.entity.MemberProfile; -import poomasi.domain.member._profile.repository.MemberProfileRepository; import poomasi.domain.member.entity.Member; import poomasi.domain.member.repository.MemberRepository; import poomasi.global.error.BusinessException; @@ -33,7 +32,7 @@ public class ImageService { private final ImageRepository imageRepository; private final ImageOwnerValidatorFactory validatorFactory; private final MemberRepository memberRepository; - private final MemberProfileRepository memberProfileRepository; + private final MemberProfileService memberProfileService; @Transactional @@ -98,10 +97,9 @@ private void validateImageLimit(ImageRequest imageRequest) { } private void linkImageToMemberProfile(Long referenceId, Image savedImage) { - MemberProfile memberProfile = memberProfileRepository.findById(referenceId) - .orElseThrow(() -> new BusinessException(MEMBER_PROFILE_NOT_FOUND)); + MemberProfile memberProfile = memberProfileService.getMemberProfileById(referenceId); memberProfile.setProfileImage(savedImage); - memberProfileRepository.save(memberProfile); + memberProfileService.saveMemberProfile(memberProfile); } // 여러 이미지 저장 diff --git a/src/main/java/poomasi/domain/member/_profile/dto/request/MemberProfileRequest.java b/src/main/java/poomasi/domain/member/_profile/dto/request/MemberProfileRequest.java new file mode 100644 index 00000000..d1ff89c0 --- /dev/null +++ b/src/main/java/poomasi/domain/member/_profile/dto/request/MemberProfileRequest.java @@ -0,0 +1,11 @@ +package poomasi.domain.member._profile.dto.request; + +import poomasi.domain.member._profile.entity.MemberProfile; + +public record MemberProfileRequest(String name) { + public static MemberProfile toEntity(MemberProfileRequest memberProfileRequest){ + return new MemberProfile( + memberProfileRequest.name + ); + } +} diff --git a/src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java b/src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java index 10359311..9c1836ea 100644 --- a/src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java +++ b/src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java @@ -62,4 +62,7 @@ public MemberProfile() { this.name = "UNKNOWN"; // name not null 조건 때문에 임시로 넣었습니다. nullable도 true로 넣었는데 안 되네요 } + public MemberProfile(String name){ + this.name = name; + } } diff --git a/src/main/java/poomasi/domain/member/_profile/service/MemberProfileService.java b/src/main/java/poomasi/domain/member/_profile/service/MemberProfileService.java new file mode 100644 index 00000000..ad25beb3 --- /dev/null +++ b/src/main/java/poomasi/domain/member/_profile/service/MemberProfileService.java @@ -0,0 +1,30 @@ +package poomasi.domain.member._profile.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import poomasi.domain.member._profile.dto.request.MemberProfileRequest; +import poomasi.domain.member._profile.entity.MemberProfile; +import poomasi.domain.member._profile.repository.MemberProfileRepository; +import poomasi.global.error.BusinessException; + +import static poomasi.global.error.BusinessError.MEMBER_PROFILE_NOT_FOUND; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class MemberProfileService { + + private final MemberProfileRepository memberProfileRepository; + + public MemberProfile getMemberProfileById(Long id){ + return memberProfileRepository.findById(id) + .orElseThrow(() -> new BusinessException(MEMBER_PROFILE_NOT_FOUND)); + } + + @Transactional + public MemberProfile saveMemberProfile(MemberProfileRequest memberProfileRequest){ + MemberProfile memberProfile = MemberProfileRequest.toEntity(memberProfileRequest); + return memberProfileRepository.save(memberProfile); + } +} From bb5d8c24fee099520fbdfb58f13dafbe8e87f2b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sat, 9 Nov 2024 17:43:25 +0900 Subject: [PATCH 32/59] =?UTF-8?q?fix:=20Bean=20=EB=AA=BB=20=EC=B0=BE?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0=20#124?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/image/repository/ImageRepository.java | 2 ++ .../poomasi/domain/image/service/ImageService.java | 1 + .../controller/MemberProfileController.java | 14 ++++++++++++++ .../repository/MemberProfileRepository.java | 2 ++ 4 files changed, 19 insertions(+) create mode 100644 src/main/java/poomasi/domain/member/_profile/controller/MemberProfileController.java diff --git a/src/main/java/poomasi/domain/image/repository/ImageRepository.java b/src/main/java/poomasi/domain/image/repository/ImageRepository.java index 2b82bad1..3c7162b5 100644 --- a/src/main/java/poomasi/domain/image/repository/ImageRepository.java +++ b/src/main/java/poomasi/domain/image/repository/ImageRepository.java @@ -1,12 +1,14 @@ package poomasi.domain.image.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; import poomasi.domain.image.entity.Image; import poomasi.domain.image.entity.ImageType; import java.util.List; import java.util.Optional; +@Repository public interface ImageRepository extends JpaRepository { long countByTypeAndReferenceIdAndDeletedAtIsNull(ImageType type, Long referenceId); List findByTypeAndReferenceIdAndDeletedAtIsNull(ImageType type, Long referenceId); diff --git a/src/main/java/poomasi/domain/image/service/ImageService.java b/src/main/java/poomasi/domain/image/service/ImageService.java index bff0bf1d..cf72c618 100644 --- a/src/main/java/poomasi/domain/image/service/ImageService.java +++ b/src/main/java/poomasi/domain/image/service/ImageService.java @@ -10,6 +10,7 @@ import poomasi.domain.image.validation.ImageOwnerValidator; import poomasi.domain.image.validation.ImageOwnerValidatorFactory; import poomasi.domain.member._profile.entity.MemberProfile; +import poomasi.domain.member._profile.service.MemberProfileService; import poomasi.domain.member.entity.Member; import poomasi.domain.member.repository.MemberRepository; import poomasi.global.error.BusinessException; diff --git a/src/main/java/poomasi/domain/member/_profile/controller/MemberProfileController.java b/src/main/java/poomasi/domain/member/_profile/controller/MemberProfileController.java new file mode 100644 index 00000000..97de1cdf --- /dev/null +++ b/src/main/java/poomasi/domain/member/_profile/controller/MemberProfileController.java @@ -0,0 +1,14 @@ +package poomasi.domain.member._profile.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import poomasi.domain.member._profile.service.MemberProfileService; + +@RestController +@RequiredArgsConstructor +@RequestMapping("api/member/profile") +public class MemberProfileController { + + private final MemberProfileService memberProfileService; +} diff --git a/src/main/java/poomasi/domain/member/_profile/repository/MemberProfileRepository.java b/src/main/java/poomasi/domain/member/_profile/repository/MemberProfileRepository.java index ee260f12..433f671c 100644 --- a/src/main/java/poomasi/domain/member/_profile/repository/MemberProfileRepository.java +++ b/src/main/java/poomasi/domain/member/_profile/repository/MemberProfileRepository.java @@ -1,7 +1,9 @@ package poomasi.domain.member._profile.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; import poomasi.domain.member._profile.entity.MemberProfile; +@Repository public interface MemberProfileRepository extends JpaRepository { } \ No newline at end of file From 99deee95a2fcbd01a8b2d8e72df59f7a1a2b7456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sat, 9 Nov 2024 17:48:06 +0900 Subject: [PATCH 33/59] =?UTF-8?q?refactor:=20reissueToken=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20=EC=9D=B8=EC=9E=90=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20#124?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../token/refreshtoken/service/RefreshTokenService.java | 1 - .../token/reissue/controller/ReissueTokenController.java | 8 ++------ .../auth/token/reissue/service/ReissueTokenService.java | 4 +++- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main/java/poomasi/domain/auth/token/refreshtoken/service/RefreshTokenService.java b/src/main/java/poomasi/domain/auth/token/refreshtoken/service/RefreshTokenService.java index c7111a58..5673353a 100644 --- a/src/main/java/poomasi/domain/auth/token/refreshtoken/service/RefreshTokenService.java +++ b/src/main/java/poomasi/domain/auth/token/refreshtoken/service/RefreshTokenService.java @@ -1,7 +1,6 @@ package poomasi.domain.auth.token.refreshtoken.service; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/main/java/poomasi/domain/auth/token/reissue/controller/ReissueTokenController.java b/src/main/java/poomasi/domain/auth/token/reissue/controller/ReissueTokenController.java index f0ecadff..24f80ba0 100644 --- a/src/main/java/poomasi/domain/auth/token/reissue/controller/ReissueTokenController.java +++ b/src/main/java/poomasi/domain/auth/token/reissue/controller/ReissueTokenController.java @@ -10,24 +10,20 @@ import poomasi.domain.auth.token.reissue.dto.ReissueRequest; import poomasi.domain.auth.token.reissue.dto.ReissueResponse; import poomasi.domain.auth.token.reissue.service.ReissueTokenService; -import poomasi.domain.auth.token.util.JwtUtil; @RestController @RequiredArgsConstructor public class ReissueTokenController { private final ReissueTokenService reissueTokenService; - private final JwtUtil jwtUtil; @PostMapping("/api/reissue") public ResponseEntity reissue(@RequestHeader(HttpHeaders.AUTHORIZATION) String authorizationHeader, @RequestBody ReissueRequest reissueRequest){ - String token = authorizationHeader.replace("Bearer ", ""); + String accessToken = authorizationHeader.replace("Bearer ", ""); - Long memberId = jwtUtil.getIdFromToken(token); - - return ResponseEntity.ok(reissueTokenService.reissueToken(memberId, reissueRequest)); + return ResponseEntity.ok(reissueTokenService.reissueToken(accessToken, reissueRequest)); } } diff --git a/src/main/java/poomasi/domain/auth/token/reissue/service/ReissueTokenService.java b/src/main/java/poomasi/domain/auth/token/reissue/service/ReissueTokenService.java index 89b379e5..b0b21182 100644 --- a/src/main/java/poomasi/domain/auth/token/reissue/service/ReissueTokenService.java +++ b/src/main/java/poomasi/domain/auth/token/reissue/service/ReissueTokenService.java @@ -18,7 +18,9 @@ public class ReissueTokenService { private final RefreshTokenService refreshTokenService; // 토큰 재발급 - public ReissueResponse reissueToken(Long memberId, ReissueRequest reissueRequest) { + public ReissueResponse reissueToken(String accessToken, ReissueRequest reissueRequest) { + Long memberId = jwtUtil.getIdFromToken(accessToken); + String refreshToken = reissueRequest.refreshToken(); Long requestMemberId = jwtUtil.getIdFromToken(refreshToken); From d8a987d29f792d03a00701fe56db1199fc06cde8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sat, 9 Nov 2024 20:51:25 +0900 Subject: [PATCH 34/59] =?UTF-8?q?fix:=20=EC=98=A4=EB=A5=98=20=EC=9D=BC?= =?UTF-8?q?=EB=8B=A8=20=ED=95=B4=EA=B2=B0=20#124?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/_profile/service/MemberProfileService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/poomasi/domain/member/_profile/service/MemberProfileService.java b/src/main/java/poomasi/domain/member/_profile/service/MemberProfileService.java index ad25beb3..6e1f4d1c 100644 --- a/src/main/java/poomasi/domain/member/_profile/service/MemberProfileService.java +++ b/src/main/java/poomasi/domain/member/_profile/service/MemberProfileService.java @@ -23,8 +23,7 @@ public MemberProfile getMemberProfileById(Long id){ } @Transactional - public MemberProfile saveMemberProfile(MemberProfileRequest memberProfileRequest){ - MemberProfile memberProfile = MemberProfileRequest.toEntity(memberProfileRequest); + public MemberProfile saveMemberProfile(MemberProfile memberProfile){ return memberProfileRepository.save(memberProfile); } } From f7b354078dfa944175c70f21d6e904a82e9a1975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sat, 9 Nov 2024 21:19:55 +0900 Subject: [PATCH 35/59] =?UTF-8?q?feat:=20=EA=B3=A0=EA=B0=9D,=20=EB=86=8D?= =?UTF-8?q?=EB=B6=80=20=ED=94=84=EB=A1=9C=ED=95=84=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EC=83=9D=EC=84=B1=20#82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_profile/entity/CustomerProfile.java | 21 +++++++++ .../member/_profile/entity/FarmerProfile.java | 45 +++++++++++++++++++ .../member/_profile/entity/MemberProfile.java | 14 +----- .../repository/MemberProfileRepository.java | 10 +++++ 4 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 src/main/java/poomasi/domain/member/_profile/entity/CustomerProfile.java create mode 100644 src/main/java/poomasi/domain/member/_profile/entity/FarmerProfile.java diff --git a/src/main/java/poomasi/domain/member/_profile/entity/CustomerProfile.java b/src/main/java/poomasi/domain/member/_profile/entity/CustomerProfile.java new file mode 100644 index 00000000..e18930cc --- /dev/null +++ b/src/main/java/poomasi/domain/member/_profile/entity/CustomerProfile.java @@ -0,0 +1,21 @@ +package poomasi.domain.member._profile.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; + +@Entity +@DiscriminatorValue("CUSTOMER") +public class CustomerProfile extends MemberProfile{ + @Column(nullable = true, length = 255) + private String address; + + @Column(nullable = true, length = 255) + private String addressDetail; + + @Column(nullable=true, length=255) + private Long coordinateX; + + @Column(nullable=true, length=255) + private Long coordinateY; +} diff --git a/src/main/java/poomasi/domain/member/_profile/entity/FarmerProfile.java b/src/main/java/poomasi/domain/member/_profile/entity/FarmerProfile.java new file mode 100644 index 00000000..a28b21fe --- /dev/null +++ b/src/main/java/poomasi/domain/member/_profile/entity/FarmerProfile.java @@ -0,0 +1,45 @@ +package poomasi.domain.member._profile.entity; + +import jakarta.persistence.*; + +import java.util.List; + +@Entity +@DiscriminatorValue("FARMER") +public class FarmerProfile extends MemberProfile{ + @Column(nullable = true, length = 100, unique = true) + private String storeName; + + @Column(nullable = true, length = 100) + private String storeAddress; + + @Column(nullable = true, length = 100) + private String storeAddressDetail; + + @Column(nullable=true, length=100) + private Long storeCoordinateX; + + @Column(nullable=true, length=100) + private Long storeCoordinateY; + + @Column(nullable = true, length = 100, unique = true) + private String farmName; + + @Column(nullable = true, length = 100) + private String farmAddress; + + @Column(nullable = true, length = 100) + private String farmAddressDetail; + + @Column(nullable=true, length=100) + private Long farmCoordinateX; + + @Column(nullable=true, length=100) + private Long farmCoordinateY; + + @ElementCollection + @CollectionTable(name = "business_registration_numbers", joinColumns = @JoinColumn(name = "farmer_profile_id")) + @Column(nullable = true, length=255) + private List businessRegistrationNumbers; + +} diff --git a/src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java b/src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java index 9c1836ea..c40cec72 100644 --- a/src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java +++ b/src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java @@ -12,6 +12,8 @@ @Table(name = "member_profile") @AllArgsConstructor @Builder +@Inheritance(strategy = InheritanceType.JOINED) +@DiscriminatorColumn(name = "profile_type") public class MemberProfile { @Id @@ -24,18 +26,6 @@ public class MemberProfile { @Column(nullable = true, length = 20) private String phoneNumber; - @Column(nullable = true, length = 255) - private String address; - - @Column(nullable = true, length = 255) - private String addressDetail; - - @Column(nullable=true, length=255) - private Long coordinateX; - - @Column(nullable=true, length=255) - private Long coordinateY; - @Column(nullable = false) @Builder.Default private boolean isBanned = false; diff --git a/src/main/java/poomasi/domain/member/_profile/repository/MemberProfileRepository.java b/src/main/java/poomasi/domain/member/_profile/repository/MemberProfileRepository.java index 433f671c..f59a2490 100644 --- a/src/main/java/poomasi/domain/member/_profile/repository/MemberProfileRepository.java +++ b/src/main/java/poomasi/domain/member/_profile/repository/MemberProfileRepository.java @@ -1,9 +1,19 @@ package poomasi.domain.member._profile.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import poomasi.domain.member._profile.entity.FarmerProfile; import poomasi.domain.member._profile.entity.MemberProfile; +import java.util.Optional; + @Repository public interface MemberProfileRepository extends JpaRepository { + + // FarmerProfile 타입만 조회 +// @Query("SELECT p FROM FarmerProfile p WHERE p.farmName = :farmName") +// Optional findFarmerByFarmName(@Param("farmName") String farmName); + } \ No newline at end of file From 26cfd3b4058918659cc80e9c08bca1915313402f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sat, 9 Nov 2024 21:38:19 +0900 Subject: [PATCH 36/59] =?UTF-8?q?refactor:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=8B=9C=20name=EB=8F=84=20=EB=B0=9B=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20#82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/_profile/entity/MemberProfile.java | 8 -------- .../member/_profile/service/MemberProfileService.java | 1 - .../domain/member/dto/request/SignupRequest.java | 2 +- .../member/dto/response/MemberSummaryResponse.java | 2 +- .../java/poomasi/domain/member/entity/Member.java | 11 +++++------ .../poomasi/domain/member/service/MemberService.java | 3 ++- 6 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java b/src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java index c40cec72..af2b5849 100644 --- a/src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java +++ b/src/main/java/poomasi/domain/member/_profile/entity/MemberProfile.java @@ -20,9 +20,6 @@ public class MemberProfile { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(nullable = true, length = 50) - private String name; - @Column(nullable = true, length = 20) private String phoneNumber; @@ -49,10 +46,5 @@ public void prePersist() { } public MemberProfile() { - this.name = "UNKNOWN"; // name not null 조건 때문에 임시로 넣었습니다. nullable도 true로 넣었는데 안 되네요 - } - - public MemberProfile(String name){ - this.name = name; } } diff --git a/src/main/java/poomasi/domain/member/_profile/service/MemberProfileService.java b/src/main/java/poomasi/domain/member/_profile/service/MemberProfileService.java index 6e1f4d1c..3f88b3c4 100644 --- a/src/main/java/poomasi/domain/member/_profile/service/MemberProfileService.java +++ b/src/main/java/poomasi/domain/member/_profile/service/MemberProfileService.java @@ -3,7 +3,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import poomasi.domain.member._profile.dto.request.MemberProfileRequest; import poomasi.domain.member._profile.entity.MemberProfile; import poomasi.domain.member._profile.repository.MemberProfileRepository; import poomasi.global.error.BusinessException; diff --git a/src/main/java/poomasi/domain/member/dto/request/SignupRequest.java b/src/main/java/poomasi/domain/member/dto/request/SignupRequest.java index 447bdf9b..85bbe7ab 100644 --- a/src/main/java/poomasi/domain/member/dto/request/SignupRequest.java +++ b/src/main/java/poomasi/domain/member/dto/request/SignupRequest.java @@ -1,4 +1,4 @@ package poomasi.domain.member.dto.request; -public record SignupRequest(String email, String password) { +public record SignupRequest(String name, String email, String password) { } diff --git a/src/main/java/poomasi/domain/member/dto/response/MemberSummaryResponse.java b/src/main/java/poomasi/domain/member/dto/response/MemberSummaryResponse.java index 9691eab0..f57c1bfa 100644 --- a/src/main/java/poomasi/domain/member/dto/response/MemberSummaryResponse.java +++ b/src/main/java/poomasi/domain/member/dto/response/MemberSummaryResponse.java @@ -6,7 +6,7 @@ public record MemberSummaryResponse(String name, Image profileImage) { public static MemberSummaryResponse fromEntity(Member member) { return new MemberSummaryResponse( - member.getMemberProfile().getName(), + member.getName(), member.getMemberProfile().getProfileImage() ); } diff --git a/src/main/java/poomasi/domain/member/entity/Member.java b/src/main/java/poomasi/domain/member/entity/Member.java index 4dc7828b..c975f4fd 100644 --- a/src/main/java/poomasi/domain/member/entity/Member.java +++ b/src/main/java/poomasi/domain/member/entity/Member.java @@ -24,6 +24,9 @@ public class Member { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(nullable = true, length = 50) + private String name; + @Column(unique = true, nullable = true, length = 50) private String email; @@ -59,18 +62,14 @@ public class Member { @Column(nullable = true) private String farmerTierCode; - public Member(String email, String password, LoginType loginType, Role role) { + public Member(String name, String email, String password, LoginType loginType, Role role) { + this.name = name; this.email = email; this.password = password; this.loginType = loginType; this.role = role; } - public Member(String email, Role role) { - this.email = email; - this.role = role; - } - public void setMemberProfile(MemberProfile memberProfile) { this.memberProfile = memberProfile; if (memberProfile != null) { diff --git a/src/main/java/poomasi/domain/member/service/MemberService.java b/src/main/java/poomasi/domain/member/service/MemberService.java index 6ea5669a..3c6ac294 100644 --- a/src/main/java/poomasi/domain/member/service/MemberService.java +++ b/src/main/java/poomasi/domain/member/service/MemberService.java @@ -31,13 +31,14 @@ public class MemberService { @Description("카카오톡으로 먼저 회원가입이 되어 있는 경우, 계정 연동을 진행합니다. ") @Transactional public SignUpResponse signUp(SignupRequest signupRequest) { + String name = signupRequest.name(); String email = signupRequest.email(); String password = signupRequest.password(); memberRepository.findByEmail(email) .ifPresent(member -> { throw new BusinessException(DUPLICATE_MEMBER_EMAIL); }); - Member newMember = new Member(email, + Member newMember = new Member(name, email, passwordEncoder.encode(password), LoginType.LOCAL, ROLE_CUSTOMER); From e50d93008e7808b827c653e5b3641068c77bb933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:17:11 +0900 Subject: [PATCH 37/59] =?UTF-8?q?feat:=20Role=EB=B3=84=20Profile=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20#82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/CommonProfileResponse.java | 22 +++++++++++ .../response/CustomerProfileResponse.java} | 16 +++----- .../dto/response/FarmerProfileResponse.java | 37 +++++++++++++++++++ .../dto/response/MemberProfileResponse.java | 18 +++++++++ .../_profile/entity/CustomerProfile.java | 2 + .../member/_profile/entity/FarmerProfile.java | 2 + .../member/dto/response/MemberResponse.java | 5 ++- .../member/dto/response/SignUpResponse.java | 2 +- .../domain/member/service/MemberService.java | 2 +- 9 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 src/main/java/poomasi/domain/member/_profile/dto/response/CommonProfileResponse.java rename src/main/java/poomasi/domain/member/{dto/response/MemberProfileResponse.java => _profile/dto/response/CustomerProfileResponse.java} (53%) create mode 100644 src/main/java/poomasi/domain/member/_profile/dto/response/FarmerProfileResponse.java create mode 100644 src/main/java/poomasi/domain/member/_profile/dto/response/MemberProfileResponse.java diff --git a/src/main/java/poomasi/domain/member/_profile/dto/response/CommonProfileResponse.java b/src/main/java/poomasi/domain/member/_profile/dto/response/CommonProfileResponse.java new file mode 100644 index 00000000..9b896d70 --- /dev/null +++ b/src/main/java/poomasi/domain/member/_profile/dto/response/CommonProfileResponse.java @@ -0,0 +1,22 @@ +package poomasi.domain.member._profile.dto.response; + +import poomasi.domain.image.entity.Image; +import poomasi.domain.member._profile.entity.MemberProfile; + +import java.time.LocalDateTime; + +public record CommonProfileResponse( + String phoneNumber, + boolean isBanned, + LocalDateTime createdAt, + Image profileImage +) { + public static CommonProfileResponse fromEntity(MemberProfile profile) { + return new CommonProfileResponse( + profile.getPhoneNumber(), + profile.isBanned(), + profile.getCreatedAt(), + profile.getProfileImage() + ); + } +} diff --git a/src/main/java/poomasi/domain/member/dto/response/MemberProfileResponse.java b/src/main/java/poomasi/domain/member/_profile/dto/response/CustomerProfileResponse.java similarity index 53% rename from src/main/java/poomasi/domain/member/dto/response/MemberProfileResponse.java rename to src/main/java/poomasi/domain/member/_profile/dto/response/CustomerProfileResponse.java index d7ad6655..f96323d3 100644 --- a/src/main/java/poomasi/domain/member/dto/response/MemberProfileResponse.java +++ b/src/main/java/poomasi/domain/member/_profile/dto/response/CustomerProfileResponse.java @@ -1,29 +1,23 @@ -package poomasi.domain.member.dto.response; +package poomasi.domain.member._profile.dto.response; import poomasi.domain.image.entity.Image; -import poomasi.domain.member._profile.entity.MemberProfile; +import poomasi.domain.member._profile.entity.CustomerProfile; import java.time.LocalDateTime; -public record MemberProfileResponse( - String name, +public record CustomerProfileResponse( String phoneNumber, String address, String addressDetail, - Long coordinateX, - Long coordinateY, boolean isBanned, LocalDateTime createdAt, Image profileImage ) { - public static MemberProfileResponse fromEntity(MemberProfile profile) { - return new MemberProfileResponse( - profile.getName(), + public static CustomerProfileResponse fromEntity(CustomerProfile profile) { + return new CustomerProfileResponse( profile.getPhoneNumber(), profile.getAddress(), profile.getAddressDetail(), - profile.getCoordinateX(), - profile.getCoordinateY(), profile.isBanned(), profile.getCreatedAt(), profile.getProfileImage() diff --git a/src/main/java/poomasi/domain/member/_profile/dto/response/FarmerProfileResponse.java b/src/main/java/poomasi/domain/member/_profile/dto/response/FarmerProfileResponse.java new file mode 100644 index 00000000..b364097a --- /dev/null +++ b/src/main/java/poomasi/domain/member/_profile/dto/response/FarmerProfileResponse.java @@ -0,0 +1,37 @@ +package poomasi.domain.member._profile.dto.response; + +import poomasi.domain.image.entity.Image; +import poomasi.domain.member._profile.entity.FarmerProfile; + +import java.time.LocalDateTime; +import java.util.List; + +public record FarmerProfileResponse( + String phoneNumber, + boolean isBanned, + LocalDateTime createdAt, + Image profileImage, + String storeName, + String farmName, + List businessRegistrationNumbers, + String storeAddress, + String storeAddressDetail, + String farmAddress, + String farmAddressDetail +) { + public static FarmerProfileResponse fromEntity(FarmerProfile profile) { + return new FarmerProfileResponse( + profile.getPhoneNumber(), + profile.isBanned(), + profile.getCreatedAt(), + profile.getProfileImage(), + profile.getStoreName(), + profile.getFarmName(), + profile.getBusinessRegistrationNumbers(), + profile.getStoreAddress(), + profile.getStoreAddressDetail(), + profile.getFarmAddress(), + profile.getFarmAddressDetail() + ); + } +} \ No newline at end of file diff --git a/src/main/java/poomasi/domain/member/_profile/dto/response/MemberProfileResponse.java b/src/main/java/poomasi/domain/member/_profile/dto/response/MemberProfileResponse.java new file mode 100644 index 00000000..c4ff7b4d --- /dev/null +++ b/src/main/java/poomasi/domain/member/_profile/dto/response/MemberProfileResponse.java @@ -0,0 +1,18 @@ +package poomasi.domain.member._profile.dto.response; + +import poomasi.domain.member._profile.entity.CustomerProfile; +import poomasi.domain.member._profile.entity.FarmerProfile; +import poomasi.domain.member._profile.entity.MemberProfile; + +public class MemberProfileResponse { + + public static Object fromEntity(MemberProfile profile) { + if (profile instanceof FarmerProfile farmerProfile) { + return FarmerProfileResponse.fromEntity(farmerProfile); + } else if (profile instanceof CustomerProfile customerProfile) { + return CustomerProfileResponse.fromEntity(customerProfile); + } else { + return CommonProfileResponse.fromEntity(profile); + } + } +} \ No newline at end of file diff --git a/src/main/java/poomasi/domain/member/_profile/entity/CustomerProfile.java b/src/main/java/poomasi/domain/member/_profile/entity/CustomerProfile.java index e18930cc..ea7b83ad 100644 --- a/src/main/java/poomasi/domain/member/_profile/entity/CustomerProfile.java +++ b/src/main/java/poomasi/domain/member/_profile/entity/CustomerProfile.java @@ -3,8 +3,10 @@ import jakarta.persistence.Column; import jakarta.persistence.DiscriminatorValue; import jakarta.persistence.Entity; +import lombok.Getter; @Entity +@Getter @DiscriminatorValue("CUSTOMER") public class CustomerProfile extends MemberProfile{ @Column(nullable = true, length = 255) diff --git a/src/main/java/poomasi/domain/member/_profile/entity/FarmerProfile.java b/src/main/java/poomasi/domain/member/_profile/entity/FarmerProfile.java index a28b21fe..06b946c7 100644 --- a/src/main/java/poomasi/domain/member/_profile/entity/FarmerProfile.java +++ b/src/main/java/poomasi/domain/member/_profile/entity/FarmerProfile.java @@ -1,10 +1,12 @@ package poomasi.domain.member._profile.entity; import jakarta.persistence.*; +import lombok.Getter; import java.util.List; @Entity +@Getter @DiscriminatorValue("FARMER") public class FarmerProfile extends MemberProfile{ @Column(nullable = true, length = 100, unique = true) diff --git a/src/main/java/poomasi/domain/member/dto/response/MemberResponse.java b/src/main/java/poomasi/domain/member/dto/response/MemberResponse.java index f19c14c3..ca01e60b 100644 --- a/src/main/java/poomasi/domain/member/dto/response/MemberResponse.java +++ b/src/main/java/poomasi/domain/member/dto/response/MemberResponse.java @@ -1,16 +1,19 @@ package poomasi.domain.member.dto.response; +import poomasi.domain.member._profile.dto.response.MemberProfileResponse; import poomasi.domain.member.entity.Member; public record MemberResponse( Long id, + String name, String email, String role, - MemberProfileResponse memberProfile + Object memberProfile ) { public static MemberResponse fromEntity(Member member) { return new MemberResponse( member.getId(), + member.getName(), member.getEmail(), member.getRole().name(), member.getMemberProfile() != null ? MemberProfileResponse.fromEntity(member.getMemberProfile()) : null diff --git a/src/main/java/poomasi/domain/member/dto/response/SignUpResponse.java b/src/main/java/poomasi/domain/member/dto/response/SignUpResponse.java index 02280570..bf4a5ecb 100644 --- a/src/main/java/poomasi/domain/member/dto/response/SignUpResponse.java +++ b/src/main/java/poomasi/domain/member/dto/response/SignUpResponse.java @@ -1,4 +1,4 @@ package poomasi.domain.member.dto.response; -public record SignUpResponse(String email, String message) { +public record SignUpResponse(String name, String email, String message) { } diff --git a/src/main/java/poomasi/domain/member/service/MemberService.java b/src/main/java/poomasi/domain/member/service/MemberService.java index 3c6ac294..9e7f22ef 100644 --- a/src/main/java/poomasi/domain/member/service/MemberService.java +++ b/src/main/java/poomasi/domain/member/service/MemberService.java @@ -44,7 +44,7 @@ public SignUpResponse signUp(SignupRequest signupRequest) { ROLE_CUSTOMER); memberRepository.save(newMember); - return new SignUpResponse(email, "회원 가입 성공"); + return new SignUpResponse(name, email, "회원 가입 성공"); } public MemberResponse getMemberById(Long memberId) { From 5e2dcc2f53782387697aa332aa9727039bc2c9f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:23:57 +0900 Subject: [PATCH 38/59] =?UTF-8?q?fix:=20findByEmailAndDeletedAtIsNull?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95=20#82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/security/userdetail/OAuth2UserDetailServiceImpl.java | 2 +- .../auth/security/userdetail/UserDetailsServiceImpl.java | 2 +- .../poomasi/domain/member/repository/MemberRepository.java | 4 ++-- .../java/poomasi/domain/member/service/MemberService.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/poomasi/domain/auth/security/userdetail/OAuth2UserDetailServiceImpl.java b/src/main/java/poomasi/domain/auth/security/userdetail/OAuth2UserDetailServiceImpl.java index 34e8a261..a595b326 100644 --- a/src/main/java/poomasi/domain/auth/security/userdetail/OAuth2UserDetailServiceImpl.java +++ b/src/main/java/poomasi/domain/auth/security/userdetail/OAuth2UserDetailServiceImpl.java @@ -52,7 +52,7 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic //일단 없으면 가입시키는 쪽으로 구현ㄴ - Member member = memberRepository.findByEmail(email).orElse(null); + Member member = memberRepository.findByEmailAndDeletedAtIsNull(email).orElse(null); if(member == null) { member = Member.builder() .email(email) diff --git a/src/main/java/poomasi/domain/auth/security/userdetail/UserDetailsServiceImpl.java b/src/main/java/poomasi/domain/auth/security/userdetail/UserDetailsServiceImpl.java index e14131fa..86e2ff7b 100644 --- a/src/main/java/poomasi/domain/auth/security/userdetail/UserDetailsServiceImpl.java +++ b/src/main/java/poomasi/domain/auth/security/userdetail/UserDetailsServiceImpl.java @@ -19,7 +19,7 @@ public UserDetailsServiceImpl(MemberRepository memberRepository) { @Override public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { - Member member = memberRepository.findByEmail(email) + Member member = memberRepository.findByEmailAndDeletedAtIsNull(email) .orElseThrow(() -> new BusinessException(BusinessError.MEMBER_NOT_FOUND)); return new UserDetailsImpl(member); } diff --git a/src/main/java/poomasi/domain/member/repository/MemberRepository.java b/src/main/java/poomasi/domain/member/repository/MemberRepository.java index f9bcb1c3..4e3cea7e 100644 --- a/src/main/java/poomasi/domain/member/repository/MemberRepository.java +++ b/src/main/java/poomasi/domain/member/repository/MemberRepository.java @@ -7,6 +7,6 @@ import java.util.Optional; public interface MemberRepository extends JpaRepository { - Optional findByEmail(String email); + Optional findByEmailAndDeletedAtIsNull(String email); Optional findByIdAndDeletedAtIsNull(Long id); -} +} \ No newline at end of file diff --git a/src/main/java/poomasi/domain/member/service/MemberService.java b/src/main/java/poomasi/domain/member/service/MemberService.java index 9e7f22ef..e39a9a9c 100644 --- a/src/main/java/poomasi/domain/member/service/MemberService.java +++ b/src/main/java/poomasi/domain/member/service/MemberService.java @@ -35,7 +35,7 @@ public SignUpResponse signUp(SignupRequest signupRequest) { String email = signupRequest.email(); String password = signupRequest.password(); - memberRepository.findByEmail(email) + memberRepository.findByEmailAndDeletedAtIsNull(email) .ifPresent(member -> { throw new BusinessException(DUPLICATE_MEMBER_EMAIL); }); Member newMember = new Member(name, email, From 0ad5160f361f7e202d80614b4079e7521c2d3399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:36:38 +0900 Subject: [PATCH 39/59] =?UTF-8?q?refactor:=20=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=20->=20=EC=A0=84?= =?UTF-8?q?=EC=B2=B4=20=EC=9A=94=EC=95=BD=20=EC=A1=B0=ED=9A=8C=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20#82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/controller/MemberController.java | 8 ++++---- .../java/poomasi/domain/member/service/MemberService.java | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/poomasi/domain/member/controller/MemberController.java b/src/main/java/poomasi/domain/member/controller/MemberController.java index 32d8bdf7..2ee754f3 100644 --- a/src/main/java/poomasi/domain/member/controller/MemberController.java +++ b/src/main/java/poomasi/domain/member/controller/MemberController.java @@ -53,11 +53,11 @@ public ResponseEntity getMemberById(@PathVariable Long memberId) return ResponseEntity.ok(memberResponse); } - @GetMapping + @GetMapping("/summary") @Secured("ROLE_ADMIN") - public ResponseEntity> getMembers(@PageableDefault(size = 10) Pageable pageable) { - Page memberResponses = memberService.getAllMembers(pageable); - return ResponseEntity.ok(memberResponses); + public ResponseEntity> getMembersSummary(@PageableDefault(size = 10) Pageable pageable) { + Page memberSummaryResponses = memberService.getAllMembersSummary(pageable); + return ResponseEntity.ok(memberSummaryResponses); } @GetMapping("/self") diff --git a/src/main/java/poomasi/domain/member/service/MemberService.java b/src/main/java/poomasi/domain/member/service/MemberService.java index e39a9a9c..c386ee3c 100644 --- a/src/main/java/poomasi/domain/member/service/MemberService.java +++ b/src/main/java/poomasi/domain/member/service/MemberService.java @@ -57,9 +57,9 @@ public MemberSummaryResponse getMemberSummary(Long memberId) { return MemberSummaryResponse.fromEntity(member); } - public Page getAllMembers(Pageable pageable) { + public Page getAllMembersSummary(Pageable pageable) { Page members = memberRepository.findAll(pageable); - return members.map(MemberResponse::fromEntity); + return members.map(MemberSummaryResponse::fromEntity); } @Transactional From 49b90b2b018748b9c642ef6672e83f68b925c42b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sat, 9 Nov 2024 23:52:00 +0900 Subject: [PATCH 40/59] =?UTF-8?q?refactor:=20MemberProfileResponse=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20#82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/MemberProfileRequest.java | 8 +----- .../dto/response/CommonProfileResponse.java | 15 ++++++---- .../dto/response/CustomerProfileResponse.java | 18 ++++++------ .../dto/response/FarmerProfileResponse.java | 28 ++++++++++--------- .../dto/response/MemberProfileResponse.java | 4 +-- .../member/dto/response/MemberResponse.java | 2 +- 6 files changed, 38 insertions(+), 37 deletions(-) diff --git a/src/main/java/poomasi/domain/member/_profile/dto/request/MemberProfileRequest.java b/src/main/java/poomasi/domain/member/_profile/dto/request/MemberProfileRequest.java index d1ff89c0..3492e58d 100644 --- a/src/main/java/poomasi/domain/member/_profile/dto/request/MemberProfileRequest.java +++ b/src/main/java/poomasi/domain/member/_profile/dto/request/MemberProfileRequest.java @@ -1,11 +1,5 @@ package poomasi.domain.member._profile.dto.request; -import poomasi.domain.member._profile.entity.MemberProfile; -public record MemberProfileRequest(String name) { - public static MemberProfile toEntity(MemberProfileRequest memberProfileRequest){ - return new MemberProfile( - memberProfileRequest.name - ); - } +public record MemberProfileRequest() { } diff --git a/src/main/java/poomasi/domain/member/_profile/dto/response/CommonProfileResponse.java b/src/main/java/poomasi/domain/member/_profile/dto/response/CommonProfileResponse.java index 9b896d70..cb665655 100644 --- a/src/main/java/poomasi/domain/member/_profile/dto/response/CommonProfileResponse.java +++ b/src/main/java/poomasi/domain/member/_profile/dto/response/CommonProfileResponse.java @@ -1,16 +1,18 @@ package poomasi.domain.member._profile.dto.response; +import lombok.AllArgsConstructor; import poomasi.domain.image.entity.Image; import poomasi.domain.member._profile.entity.MemberProfile; import java.time.LocalDateTime; -public record CommonProfileResponse( - String phoneNumber, - boolean isBanned, - LocalDateTime createdAt, - Image profileImage -) { +@AllArgsConstructor +public class CommonProfileResponse implements MemberProfileResponse { + String phoneNumber; + boolean isBanned; + LocalDateTime createdAt; + Image profileImage; + public static CommonProfileResponse fromEntity(MemberProfile profile) { return new CommonProfileResponse( profile.getPhoneNumber(), @@ -19,4 +21,5 @@ public static CommonProfileResponse fromEntity(MemberProfile profile) { profile.getProfileImage() ); } + } diff --git a/src/main/java/poomasi/domain/member/_profile/dto/response/CustomerProfileResponse.java b/src/main/java/poomasi/domain/member/_profile/dto/response/CustomerProfileResponse.java index f96323d3..53501e03 100644 --- a/src/main/java/poomasi/domain/member/_profile/dto/response/CustomerProfileResponse.java +++ b/src/main/java/poomasi/domain/member/_profile/dto/response/CustomerProfileResponse.java @@ -1,18 +1,20 @@ package poomasi.domain.member._profile.dto.response; +import lombok.AllArgsConstructor; import poomasi.domain.image.entity.Image; import poomasi.domain.member._profile.entity.CustomerProfile; import java.time.LocalDateTime; -public record CustomerProfileResponse( - String phoneNumber, - String address, - String addressDetail, - boolean isBanned, - LocalDateTime createdAt, - Image profileImage -) { +@AllArgsConstructor +public class CustomerProfileResponse implements MemberProfileResponse{ + String phoneNumber; + String address; + String addressDetail; + boolean isBanned; + LocalDateTime createdAt; + Image profileImage; + public static CustomerProfileResponse fromEntity(CustomerProfile profile) { return new CustomerProfileResponse( profile.getPhoneNumber(), diff --git a/src/main/java/poomasi/domain/member/_profile/dto/response/FarmerProfileResponse.java b/src/main/java/poomasi/domain/member/_profile/dto/response/FarmerProfileResponse.java index b364097a..53fed2f5 100644 --- a/src/main/java/poomasi/domain/member/_profile/dto/response/FarmerProfileResponse.java +++ b/src/main/java/poomasi/domain/member/_profile/dto/response/FarmerProfileResponse.java @@ -1,24 +1,26 @@ package poomasi.domain.member._profile.dto.response; +import lombok.AllArgsConstructor; import poomasi.domain.image.entity.Image; import poomasi.domain.member._profile.entity.FarmerProfile; import java.time.LocalDateTime; import java.util.List; -public record FarmerProfileResponse( - String phoneNumber, - boolean isBanned, - LocalDateTime createdAt, - Image profileImage, - String storeName, - String farmName, - List businessRegistrationNumbers, - String storeAddress, - String storeAddressDetail, - String farmAddress, - String farmAddressDetail -) { +@AllArgsConstructor +public class FarmerProfileResponse implements MemberProfileResponse { + String phoneNumber; + boolean isBanned; + LocalDateTime createdAt; + Image profileImage; + String storeName; + String farmName; + List businessRegistrationNumbers; + String storeAddress; + String storeAddressDetail; + String farmAddress; + String farmAddressDetail; + public static FarmerProfileResponse fromEntity(FarmerProfile profile) { return new FarmerProfileResponse( profile.getPhoneNumber(), diff --git a/src/main/java/poomasi/domain/member/_profile/dto/response/MemberProfileResponse.java b/src/main/java/poomasi/domain/member/_profile/dto/response/MemberProfileResponse.java index c4ff7b4d..b191dda0 100644 --- a/src/main/java/poomasi/domain/member/_profile/dto/response/MemberProfileResponse.java +++ b/src/main/java/poomasi/domain/member/_profile/dto/response/MemberProfileResponse.java @@ -4,9 +4,9 @@ import poomasi.domain.member._profile.entity.FarmerProfile; import poomasi.domain.member._profile.entity.MemberProfile; -public class MemberProfileResponse { +public interface MemberProfileResponse { - public static Object fromEntity(MemberProfile profile) { + static MemberProfileResponse fromEntity(MemberProfile profile) { if (profile instanceof FarmerProfile farmerProfile) { return FarmerProfileResponse.fromEntity(farmerProfile); } else if (profile instanceof CustomerProfile customerProfile) { diff --git a/src/main/java/poomasi/domain/member/dto/response/MemberResponse.java b/src/main/java/poomasi/domain/member/dto/response/MemberResponse.java index ca01e60b..98a8c464 100644 --- a/src/main/java/poomasi/domain/member/dto/response/MemberResponse.java +++ b/src/main/java/poomasi/domain/member/dto/response/MemberResponse.java @@ -8,7 +8,7 @@ public record MemberResponse( String name, String email, String role, - Object memberProfile + MemberProfileResponse memberProfile ) { public static MemberResponse fromEntity(Member member) { return new MemberResponse( From f92ef30c8c000feeb93d59aef1571dbb176c5da1 Mon Sep 17 00:00:00 2001 From: canyos <31244128+canyos@users.noreply.github.com> Date: Sun, 10 Nov 2024 00:01:15 +0900 Subject: [PATCH 41/59] =?UTF-8?q?store=EC=83=9D=EC=84=B1=ED=95=98=EA=B8=B0?= =?UTF-8?q?=20=20(#104)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: store 생성 * refactor: store를 product에서 빼기 * chore: confilct 나면서 이상한 잡버그 수정 --- .../auth/signup/service/SignupService.java | 47 ++++++++ .../poomasi/domain/member/entity/Member.java | 24 +++- .../domain/product/_cart/entity/Cart.java | 3 +- .../product/_category/entity/Category.java | 8 ++ .../product/dto/ProductRegisterRequest.java | 4 +- .../domain/product/dto/ProductResponse.java | 2 + .../domain/product/entity/Product.java | 22 ++-- .../product/service/ProductFarmerService.java | 9 +- .../domain/review/dto/ReviewResponse.java | 2 +- .../poomasi/domain/review/entity/Review.java | 3 +- .../store/controller/StoreController.java | 41 +++++++ .../domain/store/dto/StoreFeeRequest.java | 7 ++ .../store/dto/StoreRegisterRequest.java | 29 +++++ .../domain/store/dto/StoreResponse.java | 54 +++++++++ .../poomasi/domain/store/entity/Store.java | 70 +++++++++++ .../store/repository/StoreRepository.java | 13 +++ .../domain/store/service/StoreService.java | 56 +++++++++ .../poomasi/global/error/BusinessError.java | 5 +- .../farm/service/FarmFarmerServiceTest.java | 2 +- .../domain/store/StoreServiceTest.java | 110 ++++++++++++++++++ 20 files changed, 486 insertions(+), 25 deletions(-) create mode 100644 src/main/java/poomasi/domain/auth/signup/service/SignupService.java create mode 100644 src/main/java/poomasi/domain/store/controller/StoreController.java create mode 100644 src/main/java/poomasi/domain/store/dto/StoreFeeRequest.java create mode 100644 src/main/java/poomasi/domain/store/dto/StoreRegisterRequest.java create mode 100644 src/main/java/poomasi/domain/store/dto/StoreResponse.java create mode 100644 src/main/java/poomasi/domain/store/entity/Store.java create mode 100644 src/main/java/poomasi/domain/store/repository/StoreRepository.java create mode 100644 src/main/java/poomasi/domain/store/service/StoreService.java create mode 100644 src/test/java/poomasi/domain/store/StoreServiceTest.java diff --git a/src/main/java/poomasi/domain/auth/signup/service/SignupService.java b/src/main/java/poomasi/domain/auth/signup/service/SignupService.java new file mode 100644 index 00000000..7099ec8b --- /dev/null +++ b/src/main/java/poomasi/domain/auth/signup/service/SignupService.java @@ -0,0 +1,47 @@ +package poomasi.domain.auth.signup.service; + +import jdk.jfr.Description; +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import poomasi.domain.member.dto.request.SignupRequest; +import poomasi.domain.member.dto.response.SignUpResponse; +import poomasi.domain.member.entity.LoginType; +import poomasi.domain.member.repository.MemberRepository; +import poomasi.domain.member.entity.Member; +import poomasi.global.error.BusinessException; + +import static poomasi.domain.member.entity.Role.ROLE_CUSTOMER; +import static poomasi.domain.member.entity.Role.ROLE_FARMER; +import static poomasi.global.error.BusinessError.*; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class SignupService { + + private final MemberRepository memberRepository; + private final PasswordEncoder passwordEncoder; + + @Description("카카오톡으로 먼저 회원가입이 되어 있는 경우, 계정 연동을 진행합니다. ") + @Transactional + public SignUpResponse signUp(SignupRequest signupRequest) { + String email = signupRequest.email(); + String password = signupRequest.password(); + + memberRepository.findByEmail(email) + .ifPresent(member -> { throw new BusinessException(DUPLICATE_MEMBER_EMAIL); }); + + Member newMember = Member.builder() + .email(email) + .password(passwordEncoder.encode(password)) + .loginType(LoginType.LOCAL) + .role(ROLE_FARMER) + .build(); + memberRepository.save(newMember); + return new SignUpResponse(email, "회원 가입 성공"); + } +} + diff --git a/src/main/java/poomasi/domain/member/entity/Member.java b/src/main/java/poomasi/domain/member/entity/Member.java index 4dc7828b..fa2e8759 100644 --- a/src/main/java/poomasi/domain/member/entity/Member.java +++ b/src/main/java/poomasi/domain/member/entity/Member.java @@ -1,17 +1,19 @@ package poomasi.domain.member.entity; import jakarta.persistence.*; +import java.time.LocalDateTime; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.annotations.SQLDelete; import poomasi.domain.order.entity.Order; +import poomasi.domain.store.entity.Store; import poomasi.domain.member._profile.entity.MemberProfile; import poomasi.domain.wishlist.entity.WishList; - -import java.time.LocalDateTime; -import java.util.List; +import poomasi.global.error.BusinessError; +import poomasi.global.error.BusinessException; +import java.util.*; @Getter @Entity @@ -49,7 +51,7 @@ public class Member { @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private List wishLists; - @Column(name="deleted_at") + @Column(name = "deleted_at") private LocalDateTime deletedAt; @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) @@ -59,6 +61,10 @@ public class Member { @Column(nullable = true) private String farmerTierCode; + @Setter + @OneToOne(mappedBy="owner", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + Store store; + public Member(String email, String password, LoginType loginType, Role role) { this.email = email; this.password = password; @@ -79,8 +85,9 @@ public void setMemberProfile(MemberProfile memberProfile) { } @Builder - public Member(Long id, String email, Role role, LoginType loginType, String provideId, MemberProfile memberProfile) { + public Member(Long id, String email, String password, Role role, LoginType loginType, String provideId, MemberProfile memberProfile) { this.id = id; + this.password = password; this.email = email; this.role = role; this.loginType = loginType; @@ -99,4 +106,11 @@ public boolean isFarmer() { public boolean isAdmin() { return role == Role.ROLE_ADMIN; } + + public Store getStore() { + if(store == null) + throw new BusinessException(BusinessError.STORE_NOT_FOUND); + return store; + } + } diff --git a/src/main/java/poomasi/domain/product/_cart/entity/Cart.java b/src/main/java/poomasi/domain/product/_cart/entity/Cart.java index bf474106..7a50b0bb 100644 --- a/src/main/java/poomasi/domain/product/_cart/entity/Cart.java +++ b/src/main/java/poomasi/domain/product/_cart/entity/Cart.java @@ -26,7 +26,8 @@ public class Cart { private Integer count; @Builder - public Cart(Long memberId, Long productId, Boolean selected, Integer count) { + public Cart(Long id, Long memberId, Long productId, Boolean selected, Integer count) { + this.id = id; this.memberId = memberId; this.productId = productId; this.selected = selected; diff --git a/src/main/java/poomasi/domain/product/_category/entity/Category.java b/src/main/java/poomasi/domain/product/_category/entity/Category.java index b1957cdb..2e5e2b78 100644 --- a/src/main/java/poomasi/domain/product/_category/entity/Category.java +++ b/src/main/java/poomasi/domain/product/_category/entity/Category.java @@ -9,6 +9,7 @@ import jakarta.persistence.OneToMany; import java.util.ArrayList; import java.util.List; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import poomasi.domain.product._category.dto.CategoryRequest; @@ -28,6 +29,13 @@ public class Category { @JoinColumn(name = "categoryId") List products = new ArrayList<>(); + @Builder + public Category(Long id, String name) { + this.id = id; + this.name = name; + this.products = new ArrayList<>(); + } + public Category(String name) { this.name = name; } diff --git a/src/main/java/poomasi/domain/product/dto/ProductRegisterRequest.java b/src/main/java/poomasi/domain/product/dto/ProductRegisterRequest.java index 57828406..ffbae25b 100644 --- a/src/main/java/poomasi/domain/product/dto/ProductRegisterRequest.java +++ b/src/main/java/poomasi/domain/product/dto/ProductRegisterRequest.java @@ -1,6 +1,7 @@ package poomasi.domain.product.dto; import poomasi.domain.member.entity.Member; +import poomasi.domain.store.entity.Store; import poomasi.domain.product.entity.Product; public record ProductRegisterRequest( @@ -12,7 +13,7 @@ public record ProductRegisterRequest( Long price ) { - public Product toEntity(Member member) { + public Product toEntity(Member member, Store store) { return Product.builder() .categoryId(categoryId) .farmerId(member.getId()) @@ -22,6 +23,7 @@ public Product toEntity(Member member) { .imageUrl(imageUrl) .stock(stock) .price(price) + .store(store) .build(); } } diff --git a/src/main/java/poomasi/domain/product/dto/ProductResponse.java b/src/main/java/poomasi/domain/product/dto/ProductResponse.java index 6975a2a7..a759c3f4 100644 --- a/src/main/java/poomasi/domain/product/dto/ProductResponse.java +++ b/src/main/java/poomasi/domain/product/dto/ProductResponse.java @@ -14,6 +14,7 @@ public record ProductResponse( String description, String imageUrl, Long categoryId, + String storeName, List tags ) { @@ -27,6 +28,7 @@ public static ProductResponse fromEntity(Product product) { .stock(product.getStock()) .description(product.getDescription()) .imageUrl(product.getImageUrl()) + .storeName(product.getStore().getName()) .categoryId(product.getCategoryId()) .tags(tags) .build(); diff --git a/src/main/java/poomasi/domain/product/entity/Product.java b/src/main/java/poomasi/domain/product/entity/Product.java index 8c171d6a..8c70ba51 100644 --- a/src/main/java/poomasi/domain/product/entity/Product.java +++ b/src/main/java/poomasi/domain/product/entity/Product.java @@ -12,6 +12,7 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import java.time.LocalDateTime; import java.util.ArrayList; @@ -21,9 +22,9 @@ import lombok.NoArgsConstructor; import org.hibernate.annotations.Comment; import org.hibernate.annotations.CreationTimestamp; -import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.UpdateTimestamp; import poomasi.domain.order.entity.OrderProductDetails; +import poomasi.domain.store.entity.Store; import poomasi.domain.product.dto.ProductRegisterRequest; import poomasi.domain.review.entity.Review; @@ -71,6 +72,10 @@ public class Product { @JoinColumn(name = "entityId") List reviewList = new ArrayList<>(); + @ManyToOne + @JoinColumn(name = "store_id") // 외래 키 컬럼 지정 + private Store store; + @ElementCollection(fetch = FetchType.LAZY) @CollectionTable(name = "product_tag", joinColumns = @JoinColumn(name = "product_id")) @Column(name = "enum_value") @@ -93,7 +98,9 @@ public Product(Long productId, String description, String imageUrl, Integer stock, - Long price) { + Long price, + Store store) { + this.id = productId; this.categoryId = categoryId; this.farmerId = farmerId; this.name = name; @@ -101,6 +108,7 @@ public Product(Long productId, this.imageUrl = imageUrl; this.stock = stock; this.price = price; + this.store = store; } public Product modify(ProductRegisterRequest productRegisterRequest) { @@ -117,14 +125,4 @@ public void addStock(Integer stock) { this.stock += stock; } - public void addReview(Review pReview) { - this.reviewList.add(pReview); - this.averageRating = reviewList.stream() - .mapToDouble(Review::getRating) // 각 리뷰의 평점을 double로 변환 - .average() // 평균 계산 - .orElse(0.0); - } - - - } diff --git a/src/main/java/poomasi/domain/product/service/ProductFarmerService.java b/src/main/java/poomasi/domain/product/service/ProductFarmerService.java index a225cfb7..52140d85 100644 --- a/src/main/java/poomasi/domain/product/service/ProductFarmerService.java +++ b/src/main/java/poomasi/domain/product/service/ProductFarmerService.java @@ -6,6 +6,8 @@ import poomasi.domain.member.entity.Member; import poomasi.domain.product._category.entity.Category; import poomasi.domain.product._category.repository.CategoryRepository; +import poomasi.domain.store.entity.Store; +import poomasi.domain.store.repository.StoreRepository; import poomasi.domain.product.dto.ProductRegisterRequest; import poomasi.domain.product.dto.UpdateProductQuantityRequest; import poomasi.domain.product.entity.Product; @@ -19,12 +21,16 @@ public class ProductFarmerService { private final ProductRepository productRepository; private final CategoryRepository categoryRepository; + private final StoreRepository storeRepository; @Transactional public Long registerProduct(Member member, ProductRegisterRequest request) { Category category = getCategory(request.categoryId()); - Product saveProduct = productRepository.save(request.toEntity(member)); + Store store = member.getStore(); + Product saveProduct = productRepository.save(request.toEntity(member,store)); + category.addProduct(saveProduct); + store.addProduct(saveProduct); return saveProduct.getId(); } @@ -47,7 +53,6 @@ public void modifyProduct(Member member, ProductRegisterRequest productRequest, @Transactional public void deleteProduct(Member member, Long productId) { - //TODO: 주인인지 알아보기 Product product = getProductByProductId(productId); checkAuth(member, product); diff --git a/src/main/java/poomasi/domain/review/dto/ReviewResponse.java b/src/main/java/poomasi/domain/review/dto/ReviewResponse.java index ba6d42b0..52524f66 100644 --- a/src/main/java/poomasi/domain/review/dto/ReviewResponse.java +++ b/src/main/java/poomasi/domain/review/dto/ReviewResponse.java @@ -15,7 +15,7 @@ public static ReviewResponse fromEntity(Review review) { return new ReviewResponse( review.getId(), review.getEntityId(), - review.getReviewer().getMemberProfile().getName(), + review.getReviewer().getMemberProfile()==null?"":review.getReviewer().getMemberProfile().getName(), review.getRating(), review.getContent() ); diff --git a/src/main/java/poomasi/domain/review/entity/Review.java b/src/main/java/poomasi/domain/review/entity/Review.java index 9eda7960..dda0c932 100644 --- a/src/main/java/poomasi/domain/review/entity/Review.java +++ b/src/main/java/poomasi/domain/review/entity/Review.java @@ -51,8 +51,9 @@ public class Review { private Member reviewer; @Builder - public Review(Float rating, String content, Long entityId, EntityType entityType, + public Review(Long id, Float rating, String content, Long entityId, EntityType entityType, Member reviewer) { + this.id = id; this.rating = rating; this.content = content; this.entityId = entityId; diff --git a/src/main/java/poomasi/domain/store/controller/StoreController.java b/src/main/java/poomasi/domain/store/controller/StoreController.java new file mode 100644 index 00000000..5101e889 --- /dev/null +++ b/src/main/java/poomasi/domain/store/controller/StoreController.java @@ -0,0 +1,41 @@ +package poomasi.domain.store.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import poomasi.domain.store.dto.StoreRegisterRequest; +import poomasi.domain.store.service.StoreService; + +@Controller +@RequiredArgsConstructor +@RequestMapping("/api/store") +public class StoreController { + + private final StoreService storeService; + + @Secured("ROLE_FARMER") + @PostMapping("") + public ResponseEntity addStore(@RequestBody StoreRegisterRequest storeRegisterRequest) { + storeService.addStore(storeRegisterRequest); + return ResponseEntity.ok().build(); + } + + @Secured("ROLE_FARMER") + @GetMapping("") + public ResponseEntity getStore() { + return ResponseEntity.ok(storeService.getStore()); + } + + @Secured("ROLE_FARMER") + @PutMapping("") + public ResponseEntity updateStore(@RequestBody StoreRegisterRequest storeRegisterRequest) { + storeService.updateStore(storeRegisterRequest); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/poomasi/domain/store/dto/StoreFeeRequest.java b/src/main/java/poomasi/domain/store/dto/StoreFeeRequest.java new file mode 100644 index 00000000..57d332cd --- /dev/null +++ b/src/main/java/poomasi/domain/store/dto/StoreFeeRequest.java @@ -0,0 +1,7 @@ +package poomasi.domain.store.dto; + +public record StoreFeeRequest( + Integer fee +) { + +} diff --git a/src/main/java/poomasi/domain/store/dto/StoreRegisterRequest.java b/src/main/java/poomasi/domain/store/dto/StoreRegisterRequest.java new file mode 100644 index 00000000..08e975d3 --- /dev/null +++ b/src/main/java/poomasi/domain/store/dto/StoreRegisterRequest.java @@ -0,0 +1,29 @@ +package poomasi.domain.store.dto; + +import org.hibernate.annotations.Comment; +import poomasi.domain.member.entity.Member; +import poomasi.domain.store.entity.Store; + +public record StoreRegisterRequest( + String name, + String address, + String phone, + String ownerPhone, + @Comment("사업자 번호") + String businessNumber, + @Comment("배송비") + Integer shipingFee +) { + + public Store toEntity(Member member) { + return Store.builder() + .name(name) + .address(address) + .phone(phone) + .ownerPhone(ownerPhone) + .businessNumber(businessNumber) + .shipingFee(shipingFee) + .owner(member) + .build(); + } +} diff --git a/src/main/java/poomasi/domain/store/dto/StoreResponse.java b/src/main/java/poomasi/domain/store/dto/StoreResponse.java new file mode 100644 index 00000000..cb99e0c7 --- /dev/null +++ b/src/main/java/poomasi/domain/store/dto/StoreResponse.java @@ -0,0 +1,54 @@ +package poomasi.domain.store.dto; + +import jakarta.validation.constraints.Positive; +import java.util.List; +import lombok.Builder; +import org.hibernate.annotations.Comment; +import org.jetbrains.annotations.NotNull; +import poomasi.domain.store.entity.Store; +import poomasi.domain.product.dto.ProductResponse; + +@Builder +public record StoreResponse( + @NotNull + String name, + + @NotNull + String address, + + String phone, + + @NotNull + String ownerPhone, + + @Comment("사업자 번호") + @NotNull + String businessNumber, + + @Comment("배송비") + @NotNull + @Positive + Integer shipingFee, + + @NotNull + String ownerName, + + List products +) { + + public static StoreResponse fromEntity(Store store) { + return StoreResponse.builder() + .name(store.getName()) + .address(store.getAddress()) + .phone(store.getPhone()) + .ownerPhone(store.getOwnerPhone()) + .businessNumber(store.getBusinessNumber()) + .shipingFee(store.getShipingFee()) + //TODO 나중에 삼항연산자 삭제 + .ownerName( store.getOwner().getMemberProfile() == null ? "" + : store.getOwner().getMemberProfile().getName()) + .products(store.getProducts().stream().map(ProductResponse::fromEntity).toList()) + .build(); + + } +} \ No newline at end of file diff --git a/src/main/java/poomasi/domain/store/entity/Store.java b/src/main/java/poomasi/domain/store/entity/Store.java new file mode 100644 index 00000000..c4be3f96 --- /dev/null +++ b/src/main/java/poomasi/domain/store/entity/Store.java @@ -0,0 +1,70 @@ +package poomasi.domain.store.entity; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import java.util.ArrayList; +import java.util.List; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Comment; +import poomasi.domain.member.entity.Member; +import poomasi.domain.store.dto.StoreRegisterRequest; +import poomasi.domain.product.entity.Product; + +@Entity +@NoArgsConstructor +@Getter +public class Store { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String name; + private String address; + private String phone; + + @OneToOne(fetch = FetchType.LAZY) + private Member owner; + + private String ownerPhone; + @Comment("사업자 번호") + private String businessNumber; + @Comment("배송비") + private Integer shipingFee; + + @OneToMany(mappedBy = "store", cascade = CascadeType.ALL) + List products = new ArrayList<>(); + + @Builder + public Store(Long id, String name, String address, String phone, Member owner, + String ownerPhone, String businessNumber, Integer shipingFee) { + this.id = id; + this.name = name; + this.address = address; + this.phone = phone; + this.owner = owner; + this.ownerPhone = ownerPhone; + this.businessNumber = businessNumber; + this.shipingFee = shipingFee; + } + + public void updateStore(StoreRegisterRequest storeRegisterRequest) { + this.name = storeRegisterRequest.name(); + this.address = storeRegisterRequest.address(); + this.phone = storeRegisterRequest.phone(); + this.ownerPhone = storeRegisterRequest.ownerPhone(); + this.businessNumber = storeRegisterRequest.businessNumber(); + this.shipingFee = storeRegisterRequest.shipingFee(); + } + + public void addProduct(Product saveProduct) { + this.products.add(saveProduct); + } +} diff --git a/src/main/java/poomasi/domain/store/repository/StoreRepository.java b/src/main/java/poomasi/domain/store/repository/StoreRepository.java new file mode 100644 index 00000000..397b1b58 --- /dev/null +++ b/src/main/java/poomasi/domain/store/repository/StoreRepository.java @@ -0,0 +1,13 @@ +package poomasi.domain.store.repository; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import poomasi.domain.store.entity.Store; + +@Repository +public interface StoreRepository extends JpaRepository { + + //@Query("select s from Store s where s.owner.id = :id") + Optional findByOwnerId(Long id); +} diff --git a/src/main/java/poomasi/domain/store/service/StoreService.java b/src/main/java/poomasi/domain/store/service/StoreService.java new file mode 100644 index 00000000..5920f908 --- /dev/null +++ b/src/main/java/poomasi/domain/store/service/StoreService.java @@ -0,0 +1,56 @@ +package poomasi.domain.store.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import poomasi.domain.auth.security.userdetail.UserDetailsImpl; +import poomasi.domain.member.entity.Member; +import poomasi.domain.store.dto.StoreRegisterRequest; +import poomasi.domain.store.dto.StoreResponse; +import poomasi.domain.store.entity.Store; +import poomasi.domain.store.repository.StoreRepository; +import poomasi.global.error.BusinessError; +import poomasi.global.error.BusinessException; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class StoreService { + + private final StoreRepository storeRepository; + + @Transactional + public void addStore(StoreRegisterRequest storeRegisterRequest) { + Member member = getMember(); + Store store = storeRegisterRequest.toEntity(member); + member.setStore(store); + storeRepository.save(store); + } + + public StoreResponse getStore() { + Member member = getMember(); + Store store = getStore(member); + return StoreResponse.fromEntity(store); + } + + private Store getStore(Member member) { + return storeRepository.findByOwnerId(member.getId()) + .orElseThrow(() -> new BusinessException(BusinessError.STORE_NOT_FOUND)); + } + + private Member getMember() { + Authentication authentication = SecurityContextHolder + .getContext().getAuthentication(); + Object impl = authentication.getPrincipal(); + return ((UserDetailsImpl) impl).getMember(); + } + + @Transactional + public void updateStore(StoreRegisterRequest storeRegisterRequest) { + Member member = getMember(); + Store store = getStore(member); + store.updateStore(storeRegisterRequest); + } +} diff --git a/src/main/java/poomasi/global/error/BusinessError.java b/src/main/java/poomasi/global/error/BusinessError.java index 653b6b97..67d668d4 100644 --- a/src/main/java/poomasi/global/error/BusinessError.java +++ b/src/main/java/poomasi/global/error/BusinessError.java @@ -84,7 +84,10 @@ public enum BusinessError { // PAYMENT PAYMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "결제를 찾을 수 없습니다."), - PAYMENT_AMOUNT_MISMATCH(HttpStatus.BAD_REQUEST, "사전 결제 금액과 사후 결제 금액이 일치하지 않습니다."); + PAYMENT_AMOUNT_MISMATCH(HttpStatus.BAD_REQUEST, "사전 결제 금액과 사후 결제 금액이 일치하지 않습니다."), + + //Store + STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "등록된 상점이 없습니다."); private final HttpStatus httpStatus; diff --git a/src/test/java/poomasi/domain/farm/service/FarmFarmerServiceTest.java b/src/test/java/poomasi/domain/farm/service/FarmFarmerServiceTest.java index c34ca6f8..3fc65918 100644 --- a/src/test/java/poomasi/domain/farm/service/FarmFarmerServiceTest.java +++ b/src/test/java/poomasi/domain/farm/service/FarmFarmerServiceTest.java @@ -48,7 +48,7 @@ void should_throwException_when_farmAlreadyExists() { given(farmRepository.getFarmByOwnerIdAndDeletedAtIsNull(member.getId())).willReturn(Optional.of(existingFarm)); - FarmRegisterRequest request = new FarmRegisterRequest("New Farm", "Address", "Detail", 1.0, 1.0, "010-1234-5678", "Description", 10000L, 10, 5); + FarmRegisterRequest request = new FarmRegisterRequest("New Farm", "Address", "Detail", 1.0, 1.0, "010-1234-5678", "Description", 10000, 10, 5); // when & then assertThatThrownBy(() -> farmFarmerService.registerFarm(member, request)) diff --git a/src/test/java/poomasi/domain/store/StoreServiceTest.java b/src/test/java/poomasi/domain/store/StoreServiceTest.java new file mode 100644 index 00000000..c1327a03 --- /dev/null +++ b/src/test/java/poomasi/domain/store/StoreServiceTest.java @@ -0,0 +1,110 @@ +package poomasi.domain.store; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.util.Optional; +import poomasi.domain.auth.security.userdetail.UserDetailsImpl; +import poomasi.domain.member.entity.Member; +import poomasi.domain.store.dto.StoreRegisterRequest; +import poomasi.domain.store.dto.StoreResponse; +import poomasi.domain.store.entity.Store; +import poomasi.domain.store.repository.StoreRepository; +import poomasi.domain.store.service.StoreService; +import poomasi.global.error.BusinessError; +import poomasi.global.error.BusinessException; + +@ExtendWith(MockitoExtension.class) +class StoreServiceTest { + + @InjectMocks + private StoreService storeService; + + @Mock + private StoreRepository storeRepository; + + @Mock + private SecurityContext securityContext; + + @Mock + private Authentication authentication; + + private Member testMember; + + @BeforeEach + void setUp() { + // 테스트 멤버와 Authentication 설정 + testMember = Member.builder().build(); + + SecurityContextHolder.setContext(securityContext); + when(securityContext.getAuthentication()).thenReturn(authentication); + when(authentication.getPrincipal()).thenReturn(new UserDetailsImpl(testMember)); + } + + @Test + void addStore_StoreAddedSuccessfully() { + // given + StoreRegisterRequest request = mock(StoreRegisterRequest.class); + Store store = mock(Store.class); + + when(request.toEntity(any(Member.class))).thenReturn(store); + + // when + storeService.addStore(request); + + // then + verify(storeRepository, times(1)).save(store); + } + + @Test + void getStore_StoreExists_ReturnStoreResponse() { + // given + Store store = new Store(1L, "test","test","test",testMember,"test","test",100); + when(storeRepository.findByOwnerId(testMember.getId())).thenReturn(Optional.of(store)); + + // when + StoreResponse response = storeService.getStore(); + + // then + assertNotNull(response); + verify(storeRepository, times(1)).findByOwnerId(testMember.getId()); + } + + @Test + void updateStore_StoreExists_StoreUpdatedSuccessfully() { + // given + StoreRegisterRequest request = mock(StoreRegisterRequest.class); + Store store = mock(Store.class); + + when(storeRepository.findByOwnerId(testMember.getId())).thenReturn(Optional.of(store)); + + // when + storeService.updateStore(request); + + // then + verify(store, times(1)).updateStore(request); + } + + @Test + void updateStore_StoreDoesNotExist_ThrowsBusinessException() { + // given + StoreRegisterRequest request = mock(StoreRegisterRequest.class); + when(storeRepository.findByOwnerId(testMember.getId())).thenReturn(Optional.empty()); + + // when & then + BusinessException exception = assertThrows(BusinessException.class, () -> storeService.updateStore(request)); + assertEquals(BusinessError.STORE_NOT_FOUND, exception.getBusinessError()); + } + +} \ No newline at end of file From 5d90acfd0d52be29fb54840fe68f903d692f955b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sun, 10 Nov 2024 00:14:30 +0900 Subject: [PATCH 42/59] =?UTF-8?q?ReviewResponse=EC=97=90=EC=84=9C=20getNam?= =?UTF-8?q?e=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95=20#129?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/poomasi/domain/review/dto/ReviewResponse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/poomasi/domain/review/dto/ReviewResponse.java b/src/main/java/poomasi/domain/review/dto/ReviewResponse.java index ba6d42b0..1b6ac560 100644 --- a/src/main/java/poomasi/domain/review/dto/ReviewResponse.java +++ b/src/main/java/poomasi/domain/review/dto/ReviewResponse.java @@ -15,7 +15,7 @@ public static ReviewResponse fromEntity(Review review) { return new ReviewResponse( review.getId(), review.getEntityId(), - review.getReviewer().getMemberProfile().getName(), + review.getReviewer().getName(), review.getRating(), review.getContent() ); From f5a8537de1c5d1d3052f79b048a59d6c4db59d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sun, 10 Nov 2024 00:28:45 +0900 Subject: [PATCH 43/59] =?UTF-8?q?fix:=20=EC=B6=A9=EB=8F=8C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20#129?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/signup/service/SignupService.java | 47 ------------------- .../domain/store/dto/StoreResponse.java | 2 +- 2 files changed, 1 insertion(+), 48 deletions(-) delete mode 100644 src/main/java/poomasi/domain/auth/signup/service/SignupService.java diff --git a/src/main/java/poomasi/domain/auth/signup/service/SignupService.java b/src/main/java/poomasi/domain/auth/signup/service/SignupService.java deleted file mode 100644 index 7099ec8b..00000000 --- a/src/main/java/poomasi/domain/auth/signup/service/SignupService.java +++ /dev/null @@ -1,47 +0,0 @@ -package poomasi.domain.auth.signup.service; - -import jdk.jfr.Description; -import lombok.RequiredArgsConstructor; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import poomasi.domain.member.dto.request.SignupRequest; -import poomasi.domain.member.dto.response.SignUpResponse; -import poomasi.domain.member.entity.LoginType; -import poomasi.domain.member.repository.MemberRepository; -import poomasi.domain.member.entity.Member; -import poomasi.global.error.BusinessException; - -import static poomasi.domain.member.entity.Role.ROLE_CUSTOMER; -import static poomasi.domain.member.entity.Role.ROLE_FARMER; -import static poomasi.global.error.BusinessError.*; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class SignupService { - - private final MemberRepository memberRepository; - private final PasswordEncoder passwordEncoder; - - @Description("카카오톡으로 먼저 회원가입이 되어 있는 경우, 계정 연동을 진행합니다. ") - @Transactional - public SignUpResponse signUp(SignupRequest signupRequest) { - String email = signupRequest.email(); - String password = signupRequest.password(); - - memberRepository.findByEmail(email) - .ifPresent(member -> { throw new BusinessException(DUPLICATE_MEMBER_EMAIL); }); - - Member newMember = Member.builder() - .email(email) - .password(passwordEncoder.encode(password)) - .loginType(LoginType.LOCAL) - .role(ROLE_FARMER) - .build(); - memberRepository.save(newMember); - return new SignUpResponse(email, "회원 가입 성공"); - } -} - diff --git a/src/main/java/poomasi/domain/store/dto/StoreResponse.java b/src/main/java/poomasi/domain/store/dto/StoreResponse.java index cb99e0c7..3a821c63 100644 --- a/src/main/java/poomasi/domain/store/dto/StoreResponse.java +++ b/src/main/java/poomasi/domain/store/dto/StoreResponse.java @@ -46,7 +46,7 @@ public static StoreResponse fromEntity(Store store) { .shipingFee(store.getShipingFee()) //TODO 나중에 삼항연산자 삭제 .ownerName( store.getOwner().getMemberProfile() == null ? "" - : store.getOwner().getMemberProfile().getName()) + : store.getOwner().getName()) .products(store.getProducts().stream().map(ProductResponse::fromEntity).toList()) .build(); From e7e62ecc559c4befa012eaacf0b260db3bf78752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=84=ED=83=9D?= <87135698+jjt4515@users.noreply.github.com> Date: Sun, 10 Nov 2024 00:37:45 +0900 Subject: [PATCH 44/59] =?UTF-8?q?fix:=20=EC=88=9C=ED=99=98=20=EC=B0=B8?= =?UTF-8?q?=EC=A1=B0=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0=20#129?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/auth/config/SecurityBeanGenerator.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/main/java/poomasi/domain/auth/config/SecurityBeanGenerator.java b/src/main/java/poomasi/domain/auth/config/SecurityBeanGenerator.java index 2a067e4d..2aa3d5d6 100644 --- a/src/main/java/poomasi/domain/auth/config/SecurityBeanGenerator.java +++ b/src/main/java/poomasi/domain/auth/config/SecurityBeanGenerator.java @@ -21,9 +21,6 @@ @Configuration public class SecurityBeanGenerator { - private final TokenStorageService tokenStorageService; - private final MemberService memberService; - private final TokenBlacklistService tokenBlacklistService; @Bean @Description("AuthenticationProvider를 위한 Spring bean") @@ -37,11 +34,5 @@ MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) { return new MvcRequestMatcher.Builder(introspector); } - @Bean - JwtUtil jwtUtil(){ - return new JwtUtil(tokenBlacklistService, - tokenStorageService, - memberService); - } } From ef211484006c903e812795776e851c99f2286445 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Sun, 10 Nov 2024 16:00:16 +0900 Subject: [PATCH 45/59] =?UTF-8?q?feat=20:=20product=20cancel(=EB=B0=B0?= =?UTF-8?q?=EC=86=A1=20=EC=A0=84=20=EC=B7=A8=EC=86=8C)=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20-=20=EB=B0=B0=EC=86=A1=20=EC=A0=84=EC=9D=B8=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=EC=97=90=EB=A7=8C=20=EC=B7=A8=EC=86=8C?= =?UTF-8?q?=EB=A5=BC=20=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84.=20-=20=EA=B7=B8=20=EC=9D=B4=ED=9B=84=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=EB=8A=94=20=EB=B0=B0=EC=86=A1=EC=A4=91?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=ED=8C=90=EB=8B=A8=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EC=B7=A8=EC=86=8C=20=ED=95=A0=20=EC=88=98=20=EC=97=86=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ProductAfterSalesService.java | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 src/main/java/poomasi/domain/order/_aftersales/service/ProductAfterSalesService.java diff --git a/src/main/java/poomasi/domain/order/_aftersales/service/ProductAfterSalesService.java b/src/main/java/poomasi/domain/order/_aftersales/service/ProductAfterSalesService.java new file mode 100644 index 00000000..c443d9a7 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/service/ProductAfterSalesService.java @@ -0,0 +1,99 @@ +package poomasi.domain.order._aftersales.service; + + +import com.siot.IamportRestClient.exception.IamportResponseException; +import jdk.jfr.Description; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; +import poomasi.domain.auth.security.userdetail.UserDetailsImpl; +import poomasi.domain.member.entity.Member; +import poomasi.domain.order._aftersales.dto.cancel.request.ProductCancelRequest; +import poomasi.domain.order._aftersales.dto.exchange.request.ProductExchangeRequest; +import poomasi.domain.order._aftersales.dto.cancel.response.ProductCancelResponse; +import poomasi.domain.order._aftersales.dto.exchange.response.ProductExchangeResponse; +import poomasi.domain.order._aftersales.entity._product.ProductAfterSalesDetail; +import poomasi.domain.order._aftersales.repository.ProductAfterSalesDetailRepository; +import poomasi.domain.order._payment.util.PaymentUtil; +import poomasi.domain.order.entity._product.OrderedProduct; +import poomasi.domain.order.entity._product.ProductOrder; +import poomasi.domain.order.entity._product.OrderedProductStatus; +import poomasi.domain.order.repository.OrderedProductRepository; +import poomasi.global.error.BusinessException; + +import java.io.IOException; +import java.math.BigDecimal; + +import static poomasi.domain.order._aftersales.entity._product.ProductAfterSalesType.CANCEL; +import static poomasi.domain.order.entity._product.OrderedProductStatus.*; +import static poomasi.global.error.BusinessError.*; + +@Service +@RequiredArgsConstructor +public class ProductAfterSalesService { + + private final OrderedProductRepository orderedProductRepository; + private final PaymentUtil paymentUtil; + private final ProductAfterSalesDetailRepository productAfterSalesDetailRepository; + + //-------------------------cancel---------------------// + + @Description("판매자 확인 전 취소하는 메서드. 판매자 확인 대기 전 경우만 취소 할 수 있음") + public ProductCancelResponse productCancelBeforeSellerApproval(ProductCancelRequest productCancelRequest) throws IOException, IamportResponseException { + + Long orderedProductId = productCancelRequest.orderedProductId(); + OrderedProduct orderedProduct = validateProductRequestByMemberId(orderedProductId); + ProductOrder productOrder = orderedProduct.getProductOrder(); + String impUid = productOrder.getImpUid(); + BigDecimal amoutToBeCancel = orderedProduct.getPrice(); + String cancelReason = productCancelRequest.reason(); + OrderedProductStatus orderedProductStatus = orderedProduct.getOrderedProductStatus(); + + //판매자 확인 대기 전이 아니라면 (잘못 된 요청) + if(orderedProductStatus != PENDING_SELLER_APPROVAL){ + throw new BusinessException(SHIPPING_ALREADY_IN_PROGRESS); + } + + //취소 요청 후 + paymentUtil.cancelPaymentByImpUid(impUid); + + //주문 취소 상태로 변경 + orderedProduct.setOrderedProductStatus(CANCELLED); + + ProductAfterSalesDetail productAfterSalesDetail = new ProductAfterSalesDetail() + .builder() + .orderedProduct(orderedProduct) + .amount(amoutToBeCancel) + .reason(cancelReason) + .productAfterSalesType(CANCEL) + .build(); + + orderedProduct.addProductAfterSalesDetail(productAfterSalesDetail); + productAfterSalesDetailRepository.save(productAfterSalesDetail); + + return new ProductCancelResponse(orderedProductId, orderedProductStatus); + } + + + + @Description("security context에서 member 객체 가져오는 메서드") + private Member getMember() { + Authentication authentication = SecurityContextHolder + .getContext().getAuthentication(); + Object impl = authentication.getPrincipal(); + Member member = ((UserDetailsImpl) impl).getMember(); + return member; + } + + @Description("요청이 멤버 소유인지 확인하는 메서드") + private OrderedProduct validateProductRequestByMemberId(Long orderedProductId){ + Member member = getMember(); + Long memberId = getMember().getId(); + OrderedProduct orderedProduct = orderedProductRepository.findByIdAndMemberId(orderedProductId, memberId) + .orElseThrow(()-> new BusinessException(ORDERED_PRODUCT_NOT_FOUND)); + return orderedProduct; + } + + +} From 1d355f6bd63b6fe287975ded367935551f091ed5 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Mon, 11 Nov 2024 22:41:48 +0900 Subject: [PATCH 46/59] =?UTF-8?q?refactoring=20:=20=EC=B6=94=EC=83=81=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20?= =?UTF-8?q?enum=20type=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_aftersales/dto/FullRefundRequest.java | 6 -- .../_aftersales/dto/PartialRefundRequest.java | 9 --- .../entity/_abstract/AbstractAfterSales.java | 77 ------------------- .../entity/_product/ProductAfterSales.java | 21 ----- .../_product/ProductAfterSalesType.java | 7 -- .../ProductAfterSalesRepository.java | 7 -- .../_aftersales/service/RefundService.java | 22 ------ .../order/_payment/entity/PaymentState.java | 35 --------- .../dto/request/OrderRegisterRequest.java | 6 -- .../domain/order/entity/OrderStatus.java | 8 -- .../order/entity/_product/ShippingStatus.java | 8 -- 11 files changed, 206 deletions(-) delete mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/FullRefundRequest.java delete mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/PartialRefundRequest.java delete mode 100644 src/main/java/poomasi/domain/order/_aftersales/entity/_abstract/AbstractAfterSales.java delete mode 100644 src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSales.java delete mode 100644 src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesType.java delete mode 100644 src/main/java/poomasi/domain/order/_aftersales/repository/ProductAfterSalesRepository.java delete mode 100644 src/main/java/poomasi/domain/order/_aftersales/service/RefundService.java delete mode 100644 src/main/java/poomasi/domain/order/_payment/entity/PaymentState.java delete mode 100644 src/main/java/poomasi/domain/order/dto/request/OrderRegisterRequest.java delete mode 100644 src/main/java/poomasi/domain/order/entity/OrderStatus.java delete mode 100644 src/main/java/poomasi/domain/order/entity/_product/ShippingStatus.java diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/FullRefundRequest.java b/src/main/java/poomasi/domain/order/_aftersales/dto/FullRefundRequest.java deleted file mode 100644 index d27e4423..00000000 --- a/src/main/java/poomasi/domain/order/_aftersales/dto/FullRefundRequest.java +++ /dev/null @@ -1,6 +0,0 @@ -package poomasi.domain.order._aftersales.dto; - -public record FullRefundRequest( - String refundReason -) { -} diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/PartialRefundRequest.java b/src/main/java/poomasi/domain/order/_aftersales/dto/PartialRefundRequest.java deleted file mode 100644 index faf155e2..00000000 --- a/src/main/java/poomasi/domain/order/_aftersales/dto/PartialRefundRequest.java +++ /dev/null @@ -1,9 +0,0 @@ -package poomasi.domain.order._aftersales.dto; - -import java.math.BigDecimal; - -public record PartialRefundRequest( - BigDecimal refundAmount, // type check 필요 - String refundReason -) { -} diff --git a/src/main/java/poomasi/domain/order/_aftersales/entity/_abstract/AbstractAfterSales.java b/src/main/java/poomasi/domain/order/_aftersales/entity/_abstract/AbstractAfterSales.java deleted file mode 100644 index cbbe0afc..00000000 --- a/src/main/java/poomasi/domain/order/_aftersales/entity/_abstract/AbstractAfterSales.java +++ /dev/null @@ -1,77 +0,0 @@ -package poomasi.domain.order._aftersales.entity._abstract; - -import jakarta.persistence.*; -import jdk.jfr.Timestamp; -import org.hibernate.annotations.CreationTimestamp; -import org.hibernate.annotations.UpdateTimestamp; - -import java.time.LocalDateTime; - -@MappedSuperclass -public abstract class AbstractAfterSales { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(name = "created_at") - @CreationTimestamp - private LocalDateTime createdAt = LocalDateTime.now(); - - @Column(name = "updated_at") - @UpdateTimestamp - private LocalDateTime updateAt = LocalDateTime.now(); - - @Column(name = "deleted_at") - @Timestamp - private LocalDateTime deletedAt; - -} - -/* -* package poomasi.domain.order._aftersales.entity; - -import jakarta.persistence.*; -import jdk.jfr.Description; -import org.hibernate.annotations.CreationTimestamp; -import org.hibernate.annotations.UpdateTimestamp; -import poomasi.domain.order._payment.entity.Payment; - -import java.math.BigDecimal; -import java.time.LocalDateTime; - -@Entity -@Table(name="refund_history") -public class Refund { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @CreationTimestamp - @Column(name = "created_at") - private LocalDateTime createdAt = LocalDateTime.now(); - - @Column(name = "updated_at") - @UpdateTimestamp - private LocalDateTime updatedAt = LocalDateTime.now(); - - @Description("삭제 시간") - private LocalDateTime deletedAt; - - @ManyToOne(fetch = FetchType.LAZY) - private Payment payment; - - - private BigDecimal refundAmount; - - @OneToOne(fetch = FetchType.LAZY) - private OrderedProduct orderProductDetails; - -private String refundReason; - -} - - * -* */ - diff --git a/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSales.java b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSales.java deleted file mode 100644 index f2345caa..00000000 --- a/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSales.java +++ /dev/null @@ -1,21 +0,0 @@ -package poomasi.domain.order._aftersales.entity._product; - -import jakarta.persistence.*; -import poomasi.domain.order._aftersales.entity._abstract.AbstractAfterSales; -import poomasi.domain.order.entity._product.ProductOrder; - -import java.util.List; - -@Entity -@Table(name="product_after_sales") -public class ProductAfterSales extends AbstractAfterSales { - - @OneToOne - @JoinColumn(name = "product_order_id") - private ProductOrder productOrder; - - @OneToMany - private List productAfterSalesDetail; - -} - diff --git a/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesType.java b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesType.java deleted file mode 100644 index cfe7f4a8..00000000 --- a/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesType.java +++ /dev/null @@ -1,7 +0,0 @@ -package poomasi.domain.order._aftersales.entity._product; - -public enum ProductAfterSalesType { - EXCHANGE, - CANCEL, - REFUND -} diff --git a/src/main/java/poomasi/domain/order/_aftersales/repository/ProductAfterSalesRepository.java b/src/main/java/poomasi/domain/order/_aftersales/repository/ProductAfterSalesRepository.java deleted file mode 100644 index 9d274f26..00000000 --- a/src/main/java/poomasi/domain/order/_aftersales/repository/ProductAfterSalesRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package poomasi.domain.order._aftersales.repository; - -import org.springframework.data.jpa.repository.JpaRepository; -import poomasi.domain.order._aftersales.entity._product.ProductAfterSales; - -public interface ProductAfterSalesRepository extends JpaRepository { -} diff --git a/src/main/java/poomasi/domain/order/_aftersales/service/RefundService.java b/src/main/java/poomasi/domain/order/_aftersales/service/RefundService.java deleted file mode 100644 index 4d47f74e..00000000 --- a/src/main/java/poomasi/domain/order/_aftersales/service/RefundService.java +++ /dev/null @@ -1,22 +0,0 @@ -package poomasi.domain.order._aftersales.service; - - -import com.siot.IamportRestClient.IamportClient; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import poomasi.domain.order._aftersales.repository.ProductAfterSalesRepository; - -@Service -@RequiredArgsConstructor -public class RefundService { - - private final ProductAfterSalesRepository productAfterSalesRepository; - private final IamportClient iamportClient; - - - /* public void refundFull(){ - iamportClient. - }*/ - - -} diff --git a/src/main/java/poomasi/domain/order/_payment/entity/PaymentState.java b/src/main/java/poomasi/domain/order/_payment/entity/PaymentState.java deleted file mode 100644 index d160f719..00000000 --- a/src/main/java/poomasi/domain/order/_payment/entity/PaymentState.java +++ /dev/null @@ -1,35 +0,0 @@ -package poomasi.domain.order._payment.entity; - - -import jdk.jfr.Description; - -@Description("임시로 남겨둠 .. . .") -public enum PaymentState { - PENDING, // 결제 대기 중 - COMPLETED, // 결제 완료 - FAILED, // 결제 실패 - CANCELLED, // 결제 취소됨 - REFUNDED, // 환불 완료 - DECLINED; // 결제 거부됨 - - @Override - public String toString() { - // 사용자 친화적인 문자열로 반환할 수 있도록 오버라이딩 - switch (this) { - case PENDING: - return "Payment Pending"; - case COMPLETED: - return "Payment Completed"; - case FAILED: - return "Payment Failed"; - case CANCELLED: - return "Payment Cancelled"; - case REFUNDED: - return "Payment Refunded"; - case DECLINED: - return "Payment Declined"; - default: - return super.toString(); - } - } -} diff --git a/src/main/java/poomasi/domain/order/dto/request/OrderRegisterRequest.java b/src/main/java/poomasi/domain/order/dto/request/OrderRegisterRequest.java deleted file mode 100644 index b29c8c96..00000000 --- a/src/main/java/poomasi/domain/order/dto/request/OrderRegisterRequest.java +++ /dev/null @@ -1,6 +0,0 @@ -package poomasi.domain.order.dto.request; - -public record OrderRegisterRequest(String address, - String addressDetails, - String deliveryRequest) { -} diff --git a/src/main/java/poomasi/domain/order/entity/OrderStatus.java b/src/main/java/poomasi/domain/order/entity/OrderStatus.java deleted file mode 100644 index 2e71f97e..00000000 --- a/src/main/java/poomasi/domain/order/entity/OrderStatus.java +++ /dev/null @@ -1,8 +0,0 @@ -package poomasi.domain.order.entity; - -public enum OrderStatus { - PENDING, // 결제 대기 중 - AWAITING_SELLER_CONFIRMATION, // 판매자 확인 대기 중 - SELLER_CONFIRMED // 판매자 확인 완료 - ; -} \ No newline at end of file diff --git a/src/main/java/poomasi/domain/order/entity/_product/ShippingStatus.java b/src/main/java/poomasi/domain/order/entity/_product/ShippingStatus.java deleted file mode 100644 index e2a0a0aa..00000000 --- a/src/main/java/poomasi/domain/order/entity/_product/ShippingStatus.java +++ /dev/null @@ -1,8 +0,0 @@ -package poomasi.domain.order.entity._product; - -public enum ShippingStatus { - ORDERED, // 주문 완료 (배송 전 시작 상태) -> 배송 보내기 전 단계. 판매자 확인 후 취소 가능 - SHIPMENT_STARTED, // 배송 시작 - IN_TRANSIT, // 배송 중 - DELIVERED // 배송 완료 -} From c29b510d926dcea9e49c5ec9a0776bad1cd21fdb Mon Sep 17 00:00:00 2001 From: amm0124 Date: Mon, 11 Nov 2024 22:46:03 +0900 Subject: [PATCH 47/59] =?UTF-8?q?feat=20:=20order/payment/after=20sales=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20-=20=EB=8B=A4=EC=A4=91=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=EC=B7=A8=EC=86=8C=20=EB=93=B1=EB=93=B1=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20entity=20=EA=B5=AC=EC=A1=B0=EB=A5=BC=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=ED=96=88=EC=8A=B5=EB=8B=88=EB=8B=A4=20-=20Ab?= =?UTF-8?q?stractOrder=EB=A5=BC=20=EC=83=81=EC=86=8D=EB=B0=9B=EB=8A=94=20p?= =?UTF-8?q?roductOrder,=20farmOrder=20-=20=EA=B0=81=20Order=20Entity?= =?UTF-8?q?=EB=8A=94=20list=EC=9D=98=20orderedProduct,=20orderedFarm?= =?UTF-8?q?=EC=9D=84=20=EA=B0=80=EC=A7=90=20-=20=EA=B0=81=20Order=EB=8A=94?= =?UTF-8?q?=20Payment=20=EB=B0=A9=EC=8B=9D=EC=9D=84=20=EA=B8=B0=EB=A1=9D?= =?UTF-8?q?=ED=95=9C=20Payment=20entity=EB=A5=BC=20=EA=B0=80=EC=A7=90=20-?= =?UTF-8?q?=20=EA=B0=81=20Order=EB=8A=94=20order=EC=9D=98=20detail?= =?UTF-8?q?=EC=9D=84=20=EA=B0=80=EC=A7=80=EB=8A=94=20orderdetail=EC=9D=84?= =?UTF-8?q?=20=EA=B0=80=EC=A7=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - orderedProduct는 aftersales를 list로 가짐 - aftersales는 refunddetail을 1:1 관계로 가짐 --- .../entity/_farm/FarmAfterSales.java | 4 +- .../_product/ProductAfterSalesDetail.java | 113 +++++++++++++----- .../entity/_product/ProductRefundDetail.java | 76 ++++++++++++ .../entity/_product/RefundExchangeDetail.java | 29 ----- .../domain/order/_payment/entity/Payment.java | 32 ++++- .../domain/order/entity/PaymentStatus.java | 10 ++ .../order/entity/_abstract/AbstractOrder.java | 46 +++---- .../domain/order/entity/_farm/FarmOrder.java | 11 +- .../order/entity/_product/OrderedProduct.java | 67 +++++++++-- .../entity/_product/OrderedProductStatus.java | 31 +++++ .../order/entity/_product/ProductOrder.java | 44 +++++-- .../entity/_product/ProductOrderDetails.java | 23 ++-- 12 files changed, 365 insertions(+), 121 deletions(-) create mode 100644 src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductRefundDetail.java delete mode 100644 src/main/java/poomasi/domain/order/_aftersales/entity/_product/RefundExchangeDetail.java create mode 100644 src/main/java/poomasi/domain/order/entity/PaymentStatus.java create mode 100644 src/main/java/poomasi/domain/order/entity/_product/OrderedProductStatus.java diff --git a/src/main/java/poomasi/domain/order/_aftersales/entity/_farm/FarmAfterSales.java b/src/main/java/poomasi/domain/order/_aftersales/entity/_farm/FarmAfterSales.java index 055a8ca3..c573c2ca 100644 --- a/src/main/java/poomasi/domain/order/_aftersales/entity/_farm/FarmAfterSales.java +++ b/src/main/java/poomasi/domain/order/_aftersales/entity/_farm/FarmAfterSales.java @@ -1,6 +1,6 @@ package poomasi.domain.order._aftersales.entity._farm; -import poomasi.domain.order._aftersales.entity._abstract.AbstractAfterSales; -public class FarmAfterSales extends AbstractAfterSales { + +public class FarmAfterSales { } diff --git a/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesDetail.java b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesDetail.java index b37f4fbb..41a2fe2a 100644 --- a/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesDetail.java +++ b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesDetail.java @@ -1,42 +1,101 @@ - package poomasi.domain.order._aftersales.entity._product; +package poomasi.domain.order._aftersales.entity._product; - import jakarta.persistence.*; - import jdk.jfr.Description; - import poomasi.domain.order.entity._product.OrderedProduct; +import jakarta.persistence.*; +import jdk.jfr.Description; +import jdk.jfr.Timestamp; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; +import poomasi.domain.order.entity._product.OrderedProduct; - import java.math.BigDecimal; +import java.math.BigDecimal; +import java.time.LocalDateTime; - @Description("상품 판매 후 교환/환불/추소 history") - @Entity - @Table(name="product_after_sales_detail") - public class ProductAfterSalesDetail { +@Description("상품 판매 후 교환/환불/추소 history") +@Entity +@Getter +@Table(name="product_after_sales_detail") +@NoArgsConstructor +public class ProductAfterSalesDetail { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; - @ManyToOne - private ProductAfterSales productAfterSales; + @Column(name = "created_at") + @CreationTimestamp + private LocalDateTime createdAt = LocalDateTime.now(); - @OneToOne - private OrderedProduct orderedProduct; + @Column(name = "updated_at") + @UpdateTimestamp + private LocalDateTime updateAt = LocalDateTime.now(); + @Column(name = "deleted_at") + @Timestamp + private LocalDateTime deletedAt; - @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) - @JoinColumn(name = "refund_exchange_detail_id", nullable = true) // 외래 키 설정 - private RefundExchangeDetail refundExchangeDetail; + @ManyToOne + private OrderedProduct orderedProduct; - @Description("ordered products의 환불/교환/취소 금액") - private BigDecimal amount; + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "product_refund_detail_id", nullable = true) // 외래 키 설정 + private ProductRefundDetail productRefundDetail; + + //TODO : payment에 있는 것을 변경해야 함 + private String impUid; - @Description("환불/교환/취소 사유") - private String reason; + @Description("환불/교환/취소 금액") + private BigDecimal adjustAmount; + + @Description("취소/교환/환불 수량") + private Integer adjustmentQuantity; - @Description("환불 받을 계좌번호") - private String refundAccount; + @Description("환불/교환/취소 요청 사유") + private String reason; - @Enumerated(EnumType.STRING) - private ProductAfterSalesType productAfterSalesType; + @Enumerated(EnumType.STRING) + private ProductAfterSalesStatus productAfterSalesStatus; + @Builder + public ProductAfterSalesDetail(OrderedProduct orderedProduct, + BigDecimal adjustAmount, + String reason, + Integer adjustmentQuantity, + ProductAfterSalesStatus productAfterSalesStatus) { + this.orderedProduct = orderedProduct; + this.adjustAmount = adjustAmount; + this.reason = reason; + this.adjustmentQuantity = adjustmentQuantity; + this.productAfterSalesStatus = productAfterSalesStatus; + } + + public void setOrderedProduct(OrderedProduct orderedProduct) { + this.orderedProduct = orderedProduct; + } + + public void setProductAfterSalesStatus(ProductAfterSalesStatus productAfterSalesStatus){ + this.productAfterSalesStatus = productAfterSalesStatus; + } + + public String getProductRefundDeniedReason(){ + return this.productRefundDetail.getProductRefundDeniedReason(); + } + public void setProductRefundDeniedReason(String productRefundDeniedReason){ + this.productRefundDetail.setProductRefundDeniedReason(productRefundDeniedReason); } + + public void setProductRefundDetail(ProductRefundDetail productRefundDetail) { + this.productRefundDetail = productRefundDetail; + productRefundDetail.setProductAfterSalesDetail(this); + } + + public void changeRefundApproveStatus(String invoiceNumber){ + this.productAfterSalesStatus = ProductAfterSalesStatus.REFUND_APPROVED; + this.productRefundDetail.setInvoiceNumber(invoiceNumber); + } + +} + diff --git a/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductRefundDetail.java b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductRefundDetail.java new file mode 100644 index 00000000..954c3fe0 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductRefundDetail.java @@ -0,0 +1,76 @@ +package poomasi.domain.order._aftersales.entity._product; + + +import jakarta.persistence.*; +import jdk.jfr.Description; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import poomasi.domain.product.entity.Product; + +@Entity +@Table(name= "product_refund_detail") +@Getter +@NoArgsConstructor +public class ProductRefundDetail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne(mappedBy = "productRefundDetail") // 주인이 아닌 쪽에 mappedBy 설정 + private ProductAfterSalesDetail productAfterSalesDetail; + + @Description("반품 회수지. 기본 값은 보낸 주소") + private String pickupLocationAddress; + + @Description("반품 회수지 상세 정보") + private String pickupLocationAddressDetail; + + @Description("반송지. 기본 값은 받은 주소") + private String returnAddress; + + @Description("반송지. 기본 값은 받은 주소") + private String returnAddressDetail; + + @Description("반품/교환 시 운송장 번호") + private String invoiceNumber; + + @Description("반품/교환 시 요청 사항") + private String request; + + @Description("환불 거절 사유") + private String productRefundDeniedReason; + + @Builder + public ProductRefundDetail(ProductAfterSalesDetail productAfterSalesDetail, + String pickupLocationAddress, + String pickupLocationAddressDetail, + String returnAddress, + String returnAddressDetail, + String invoiceNumber, + String request, + String productRefundDeniedReason){ + this.productAfterSalesDetail = productAfterSalesDetail; + this.pickupLocationAddress = pickupLocationAddress; + this.pickupLocationAddressDetail = pickupLocationAddressDetail; + this.returnAddress = returnAddress; + this.returnAddressDetail = returnAddressDetail; + this.invoiceNumber = invoiceNumber; + this.request = request; + this.productRefundDeniedReason = productRefundDeniedReason; + } + + public void setProductRefundDeniedReason(String productRefundDeniedReason) { + this.productRefundDeniedReason = productRefundDeniedReason; + } + + public void setProductAfterSalesDetail(ProductAfterSalesDetail productAfterSalesDetail) { + this.productAfterSalesDetail = productAfterSalesDetail; + } + + public void setInvoiceNumber(String invoiceNumber){ + this.invoiceNumber = invoiceNumber; + } + +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/entity/_product/RefundExchangeDetail.java b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/RefundExchangeDetail.java deleted file mode 100644 index 6e81e378..00000000 --- a/src/main/java/poomasi/domain/order/_aftersales/entity/_product/RefundExchangeDetail.java +++ /dev/null @@ -1,29 +0,0 @@ -package poomasi.domain.order._aftersales.entity._product; - - -import jakarta.persistence.*; -import jdk.jfr.Description; - -@Entity -@Table(name= "refund_exchange_detail") -public class RefundExchangeDetail { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @OneToOne(mappedBy = "refundExchangeDetail") // 주인이 아닌 쪽에 mappedBy 설정 - private ProductAfterSalesDetail productAfterSalesDetail; - - @Description("반품 회수지. 기본 값은 보낸 주소") - private String pickupLocation; - - @Description("반송지. 기본 값은 받은 주소") - private String returnAddress; - - @Description("반품/교환 시 운송장 번호") - private String invoiceNumber; - - @Description("반품/교환 시 요청 사항") - private String request; -} diff --git a/src/main/java/poomasi/domain/order/_payment/entity/Payment.java b/src/main/java/poomasi/domain/order/_payment/entity/Payment.java index ddc3048d..0983c68f 100644 --- a/src/main/java/poomasi/domain/order/_payment/entity/Payment.java +++ b/src/main/java/poomasi/domain/order/_payment/entity/Payment.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import jdk.jfr.Description; import lombok.Getter; +import poomasi.domain.order.entity.PaymentStatus; import poomasi.domain.order.entity._farm.FarmOrder; import poomasi.domain.order.entity._product.ProductOrder; @@ -17,6 +18,13 @@ public class Payment { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(name = "imp_uid") + @Description("아임포트 결제 imp_uid") + private String impUid; + + @OneToOne(mappedBy = "payment") + private ProductOrder productOrder; + @Description("포트원 결제 금액") private BigDecimal totalPrice; @@ -26,6 +34,9 @@ public class Payment { @Description("사용 포인트") private BigDecimal usedPoint; + @Description("배송비") + private BigDecimal deliveryFee; + @Description("최종 가격") private BigDecimal finalPrice; @@ -33,10 +44,23 @@ public class Payment { @Enumerated(EnumType.STRING) private PaymentMethod paymentMethod; - @OneToOne(mappedBy = "payment") - private ProductOrder productOrder; + @Description("checksum") + private BigDecimal checkSum; + + @Enumerated(EnumType.STRING) + private PaymentStatus paymentStatus = PaymentStatus.PAYMENT_PENDING; + + public void setCheckSum(BigDecimal checksum) { + this.checkSum = checksum; + } + + public void subtractCheckSum(BigDecimal checksum) { + this.checkSum = this.checkSum.subtract(checksum); + } + + public void setPaymentStatus(PaymentStatus paymentStatus) { + this.paymentStatus = paymentStatus; + } -/* @OneToOne(mappedBy = "payment") - private FarmOrder farmOrder;*/ } diff --git a/src/main/java/poomasi/domain/order/entity/PaymentStatus.java b/src/main/java/poomasi/domain/order/entity/PaymentStatus.java new file mode 100644 index 00000000..b8b4dc21 --- /dev/null +++ b/src/main/java/poomasi/domain/order/entity/PaymentStatus.java @@ -0,0 +1,10 @@ +package poomasi.domain.order.entity; + +public enum PaymentStatus { + PAYMENT_PENDING, // 결제 대기 중 + PAYMENT_COMPLETE, // 결제 성공 + PAYMENT_DECLINED, // 결제 거부 + PAYMENT_INSUFFICIENT_QUANTITY + ; +} + diff --git a/src/main/java/poomasi/domain/order/entity/_abstract/AbstractOrder.java b/src/main/java/poomasi/domain/order/entity/_abstract/AbstractOrder.java index 4b784690..d7a6ae7d 100644 --- a/src/main/java/poomasi/domain/order/entity/_abstract/AbstractOrder.java +++ b/src/main/java/poomasi/domain/order/entity/_abstract/AbstractOrder.java @@ -4,19 +4,23 @@ import jdk.jfr.Description; import jdk.jfr.Timestamp; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.experimental.SuperBuilder; import org.hibernate.annotations.CreationTimestamp; -import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.UpdateTimestamp; import poomasi.domain.member.entity.Member; import poomasi.domain.order._payment.entity.Payment; -import poomasi.domain.order.entity.OrderStatus; import java.math.BigDecimal; import java.time.LocalDateTime; -import java.util.Date; + @MappedSuperclass @Getter +@Setter +@SuperBuilder // 빌더 패턴을 사용하도록 설정 +@NoArgsConstructor public abstract class AbstractOrder { @Id @@ -31,14 +35,6 @@ public abstract class AbstractOrder { @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) private Payment payment; - @Column(name = "merchant_uid") - @Description("서버 내부 주문 id(아임포트 id)") - private String merchantUid = "p" + new Date().getTime(); - - @Column(name = "imp_uid") - @Description("아임포트 결제 imp_uid") - private String impUid; - @Column(name = "created_at") @CreationTimestamp private LocalDateTime createdAt = LocalDateTime.now(); @@ -55,34 +51,22 @@ public abstract class AbstractOrder { @Description("총 결제 금액") private BigDecimal totalAmount; - @Enumerated(EnumType.STRING) - private OrderStatus orderStatus = OrderStatus.PENDING; - - @Description("checksum") - private BigDecimal checksum; - - public void setCheckSum(BigDecimal checksum) { - this.checksum = checksum; + public void setCheckSum(BigDecimal checkSum) { + this.payment.setCheckSum(checkSum); } - public void reduceChecksum(BigDecimal amount) { - checksum = checksum.subtract(amount); - } - public void setTotalAmount(BigDecimal totalAmount) { - this.totalAmount = totalAmount; + public void subtractChecksum(BigDecimal checkSum) { + this.payment.subtractCheckSum(checkSum); } - public void setOrderStatus(OrderStatus orderStatus) { - this.orderStatus = orderStatus; + public BigDecimal getCheckSum(){ + return this.payment.getCheckSum(); } - public void setImpUid(String impUid) { - this.impUid = impUid; + public void setTotalAmount(BigDecimal totalAmount) { + this.totalAmount = totalAmount; } - public void setMerchantUid(String merchantUid) { - this.merchantUid = merchantUid; - } } diff --git a/src/main/java/poomasi/domain/order/entity/_farm/FarmOrder.java b/src/main/java/poomasi/domain/order/entity/_farm/FarmOrder.java index 1de8705c..46501720 100644 --- a/src/main/java/poomasi/domain/order/entity/_farm/FarmOrder.java +++ b/src/main/java/poomasi/domain/order/entity/_farm/FarmOrder.java @@ -1,10 +1,13 @@ package poomasi.domain.order.entity._farm; import jakarta.persistence.*; +import jdk.jfr.Description; import org.hibernate.annotations.Comment; import poomasi.domain.order._payment.entity.Payment; import poomasi.domain.order.entity._abstract.AbstractOrder; +import java.util.Date; + //@Entity //@Table(name = "farm_order") public class FarmOrder extends AbstractOrder { @@ -19,7 +22,7 @@ public class FarmOrder extends AbstractOrder { private String description; @Comment("도로명 주소") - private String address; + private String destinationAddress; @Comment("상세 주소") private String addressDetail; @@ -30,5 +33,11 @@ public class FarmOrder extends AbstractOrder { @Comment("경도") private Double longitude; */ + + @Column(name = "merchant_uid") + @Description("서버 내부 주문 id(아임포트 id)") + private String merchantUid = "f" + new Date().getTime(); + + } diff --git a/src/main/java/poomasi/domain/order/entity/_product/OrderedProduct.java b/src/main/java/poomasi/domain/order/entity/_product/OrderedProduct.java index d495a281..13c988c0 100644 --- a/src/main/java/poomasi/domain/order/entity/_product/OrderedProduct.java +++ b/src/main/java/poomasi/domain/order/entity/_product/OrderedProduct.java @@ -11,6 +11,7 @@ import java.io.Serializable; import java.math.BigDecimal; +import java.util.List; @Entity @Table(name = "ordered_products") @@ -23,25 +24,23 @@ public class OrderedProduct implements Serializable { @Column(name = "ordered_product_id") private Long id; - @OneToOne(fetch = FetchType.LAZY) + @OneToMany(fetch = FetchType.LAZY) @JoinColumn(nullable = true, name = "product_after_sales_detail_id") - private ProductAfterSalesDetail productAfterSalesDetail; + private List productAfterSalesDetails; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "product_order_id") private ProductOrder productOrder; - //FIXME : store Id를 참조해야 한다. //나중에 store Id로 변경해야 한다 - private Long storeId; + //private Store store; + //private Long storeId; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "product_id") private Product product; - - @Column(name = "product_description", nullable = true) private String productDescription; @@ -50,7 +49,8 @@ public class OrderedProduct implements Serializable { @Description("구매 당시 1개당 가격") private BigDecimal price; - + + @Description("구매 수량") @Column(name="count") private Integer count; @@ -58,7 +58,21 @@ public class OrderedProduct implements Serializable { @Column(name = "invoice_number", nullable = true) private String invoiceNumber; - private ShippingStatus shippingStatus = ShippingStatus.ORDERED; + private OrderedProductStatus orderedProductStatus = OrderedProductStatus.PENDING_SELLER_APPROVAL; + + @Description("TODO : product의 delivery fee를 참조해야 한다.") + private BigDecimal deliveryFee; + + @Description("환불 가능한 남은 수량") + @Column(name = "refundable_count") + private Integer adjustableQuantity; + + @Description("취소 된 수량") + @Column(name = "cacnel_quantity") + private Integer cancelQuantity; + + @Description("flag가 설정되어 있으면 배송비 환불하지 않아도 된다") + private boolean isCanceled = false; // 웹훅 받아서 조회해야 함. // findByInvoiceNumber 후 @@ -79,12 +93,45 @@ public void setInvoiceNumber(String invoiceNumber) { this.invoiceNumber = invoiceNumber; } - public void setShippingStatus(ShippingStatus shippingStatus) { - this.shippingStatus = shippingStatus; + public void setOrderedProductStatus(OrderedProductStatus orderedProductStatus) { + this.orderedProductStatus = orderedProductStatus; + } + + public void addProductAfterSalesDetail(ProductAfterSalesDetail productAfterSalesDetail) { + this.productAfterSalesDetails.add(productAfterSalesDetail); + productAfterSalesDetail.setOrderedProduct(this); } public Long getOrderId(){ return this.productOrder.getId(); } + + public void subtractRefundableCount(Integer refundableCount) { + this.adjustableQuantity -= refundableCount; + } + + public void addCancelQuantity(Integer cancelQuantity) { + this.isCanceled = true; + this.cancelQuantity += cancelQuantity; + } + + public OrderedProductStatus changeOrderedProductStatusToCancel() { + if (this.count == this.cancelQuantity) { + this.orderedProductStatus = OrderedProductStatus.CANCELLED; + } + return this.orderedProductStatus; + } + + public String getStoreAddress(){ + //return this.store.getStoreAddress() + return "TODO : store의 address를 참조해야 함"; + } + + public String getStoreAddressDetail(){ + //return this.store.getStoreAddressDetail() + return "TODO: store의 address detail을 참조해야 함"; + } + + } diff --git a/src/main/java/poomasi/domain/order/entity/_product/OrderedProductStatus.java b/src/main/java/poomasi/domain/order/entity/_product/OrderedProductStatus.java new file mode 100644 index 00000000..407a9a9f --- /dev/null +++ b/src/main/java/poomasi/domain/order/entity/_product/OrderedProductStatus.java @@ -0,0 +1,31 @@ +package poomasi.domain.order.entity._product; + +public enum OrderedProductStatus { + + PENDING_SELLER_APPROVAL, // 판매자 수락 전 (주문 완료 후 대기 상태) + SHIPMENT_STARTED, // 배송 시작 (판매자 수락을 하면 바뀌는 상태) + IN_TRANSIT, // 배송 중 + DELIVERED, // 배송 완료 + CANCELLED, // 주문 취소 완료 (취소가 최종적으로 완료된 상태) + + //교환 + EXCHANGE_PENDING, // 교환 요청 대기중 + EXCHANGE_APPROVED, // 교환 요청 승인됨 + EXCHANGE_IN_PROGRESS, // 교환 처리 중 (배송 중이거나 준비 중) + EXCHANGE_COMPLETED, // 교환 완료 + EXCHANGE_DENIED, // 교환 요청 거절됨 + + //환불 + REFUND_REQUESTED, // 환불 요청됨 + REFUND_APPROVED, // 환불 승인됨 + REFUND_SHIPMENT_STARTED, // 환불 배송 시작 (반품 물품의 배송 시작) + REFUND_IN_TRANSIT, // 환불 배송 중 (반품 물품이 배송 중) + REFUND_DELIVERED, // 환불 배송 완료 (반품 물품이 도착함) + REFUND_IN_PROGRESS, // 환불 처리 중 (반품 수거 중이거나 처리 대기 중) + REFUND_COMPLETED, // 환불 완료 + REFUND_DENIED, // 환불 요청 거절됨 + + //주문 취소 + CANCEL_PENDING, // 주문 취소 대기 중 (취소 요청을 받은 상태) + ; +} \ No newline at end of file diff --git a/src/main/java/poomasi/domain/order/entity/_product/ProductOrder.java b/src/main/java/poomasi/domain/order/entity/_product/ProductOrder.java index 5618b63c..90c237e2 100644 --- a/src/main/java/poomasi/domain/order/entity/_product/ProductOrder.java +++ b/src/main/java/poomasi/domain/order/entity/_product/ProductOrder.java @@ -3,21 +3,30 @@ import jakarta.persistence.*; import jdk.jfr.Description; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; import org.hibernate.annotations.SQLDelete; -import poomasi.domain.order._aftersales.entity._product.ProductAfterSales; +import poomasi.domain.member.entity.Member; +import poomasi.domain.order.entity.PaymentStatus; import poomasi.domain.order.entity._abstract.AbstractOrder; +import java.math.BigDecimal; +import java.util.Date; import java.util.List; @Entity @Table(name = "product_order") @Getter -@NoArgsConstructor -@SQLDelete(sql = "UPDATE product_order SET deleted_at=current_timestamp WHERE id = ?") +@SuperBuilder +@SQLDelete(sql = "UPDATE product_order SET deleted_at = current_timestamp WHERE id = ?") public class ProductOrder extends AbstractOrder { + @Column(name = "merchant_uid") + @Description("서버 내부 주문 id(아임포트 id)") + private String merchantUid = "p" + new Date().getTime(); + @Column(name = "ordered_products_id") @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) private List orderedProducts; @@ -27,18 +36,33 @@ public class ProductOrder extends AbstractOrder { @Description("상품 배송지, 요청 사항") private ProductOrderDetails productOrderDetails; + public ProductOrder(){ - @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) - @JoinColumn(name = "product_after_sales_id") - @Description("결제 완료 후 취소/교환/환불 관리 객체") - private ProductAfterSales productAfterSales; - - public ProductOrder(ProductOrderDetails productOrderDetails) { - this.productOrderDetails = productOrderDetails; } public void addOrderedProduct(OrderedProduct orderedProduct) { this.orderedProducts.add(orderedProduct); } + public void setMerchantUid(String merchantUid) { + this.merchantUid = merchantUid; + } + + public String getImpUid(){ + return this.getPayment().getImpUid(); + } + + public PaymentStatus getPaymentStatus(){ + return this.getPayment().getPaymentStatus(); + } + + public void setPaymentStatus(PaymentStatus paymentStatus){ + this.getPayment().setPaymentStatus(paymentStatus); + } + + public void setProductOrderDetails(ProductOrderDetails productOrderDetails){ + this.productOrderDetails = productOrderDetails; + productOrderDetails.setProductOrder(this); + } + } diff --git a/src/main/java/poomasi/domain/order/entity/_product/ProductOrderDetails.java b/src/main/java/poomasi/domain/order/entity/_product/ProductOrderDetails.java index 4d8fb903..0cf2e983 100644 --- a/src/main/java/poomasi/domain/order/entity/_product/ProductOrderDetails.java +++ b/src/main/java/poomasi/domain/order/entity/_product/ProductOrderDetails.java @@ -2,6 +2,7 @@ import jakarta.persistence.*; import jdk.jfr.Description; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -18,21 +19,29 @@ public class ProductOrderDetails { @OneToOne(mappedBy = "productOrderDetails", cascade = CascadeType.ALL) // 필드명으로 지정 private ProductOrder productOrder; - @Column(name = "address") - private String address; + @Column(name = "return_address") + @Description("도착 주소") + private String destinationAddress; - @Column(name = "address_detail") - private String addressDetail; + @Column(name = "destination_address_detail") + @Description("도착 상세 주소") + private String destinationAddressDetail; @Description("배송 요청 사항") @Column(name = "delivery_request", length = 255) private String deliveryRequest; - public ProductOrderDetails(String address, String addressDetail, String deliveryRequest) { - this.address = address; - this.addressDetail = addressDetail; + + @Builder + public ProductOrderDetails(ProductOrder productOrder, String destinationAddress, String destinationAddressDetail, String deliveryRequest) { + this.productOrder = productOrder; + this.destinationAddress = destinationAddress; + this.destinationAddressDetail = destinationAddressDetail; this.deliveryRequest = deliveryRequest; } + public void setProductOrder(ProductOrder productOrder) { + this.productOrder = productOrder; + } } From 59d2daabd173dfeb976060acfebdf1fea09d23a2 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Mon, 11 Nov 2024 22:47:40 +0900 Subject: [PATCH 48/59] =?UTF-8?q?feat=20:=20error=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20ExceptionAdvice=EC=B6=94=EA=B0=80=20-=20IamportResp?= =?UTF-8?q?onseException=EC=9D=84=20=EC=A0=84=EC=97=AD=EC=A0=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9E=A1=EA=B8=B0=20=EC=9C=84=ED=95=B4=20advice?= =?UTF-8?q?=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=98=EC=98=80=EC=8A=B5?= =?UTF-8?q?=EB=8B=88=EB=8B=A4=20-=20IOException=EC=9D=84=20=EC=A0=84?= =?UTF-8?q?=EC=97=AD=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EC=9E=A1=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20advice=EB=A5=BC=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=ED=95=98=EC=98=80=EC=8A=B5=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../poomasi/global/error/BusinessError.java | 17 ++++++++++++- .../poomasi/global/error/ExceptionAdvice.java | 24 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/main/java/poomasi/global/error/BusinessError.java b/src/main/java/poomasi/global/error/BusinessError.java index 3138e5be..a817f64b 100644 --- a/src/main/java/poomasi/global/error/BusinessError.java +++ b/src/main/java/poomasi/global/error/BusinessError.java @@ -79,11 +79,26 @@ public enum BusinessError { ORDER_NOT_OWNED_EXCEPTION(HttpStatus.UNAUTHORIZED, "허가되지 않은 주문입니다."), ORDER_PRODUCT_DETAILS_NOT_FOUND(HttpStatus.NOT_FOUND, "주문을 찾을 수 없습니다."), ORDER_PRODUCT_DETAILS_NOT_OWNED_EXCEPTION(HttpStatus.UNAUTHORIZED, "허가되지 않은 주문입니다."), + ORDERED_PRODUCT_NOT_FOUND(HttpStatus.NOT_FOUND, "찾을 수 없는 주문입니다."), + // PAYMENT PAYMENT_NOT_FOUND(HttpStatus.NOT_FOUND , "결제를 찾을 수 없습니다."), PAYMENT_AMOUNT_MISMATCH(HttpStatus.BAD_REQUEST, "사전 결제 금액과 사후 결제 금액이 일치하지 않습니다."), - PAYMENT_BAD_REQUEST(HttpStatus.BAD_REQUEST, "잘못 된 결제 요청입니다.") + PAYMENT_BAD_REQUEST(HttpStatus.BAD_REQUEST, "잘못 된 결제 요청입니다."), + CHECKSUM_EXCESSIVE_REFUND_AMOUNT(HttpStatus.BAD_REQUEST, "환불 요청 금액이 환불 가능한 금액보다 더 많습니다"), + + + // After sales + SHIPPING_ALREADY_IN_PROGRESS(HttpStatus.BAD_REQUEST, "배송 준비 중이거나 배송 중인 주문입니다."), + EXCHANGE_NOT_ALLOWED_IN_TRANSIT(HttpStatus.BAD_REQUEST, "배송 중인 상품에 대해선 교환 요청을 할 수 없습니다."), + SHIPPING_COST_GREATER_THAN_REFUND(HttpStatus.BAD_REQUEST, "배송비가 환불 금액보다 더 큽니다."), + CANCEL_QUANTITY_EXCEEDED(HttpStatus.BAD_REQUEST, "취소 가능한 수량을 초과한 요청입니다."), + REFUND_QUANTITY_EXCEEDED(HttpStatus.BAD_REQUEST, "환불 가능한 수량을 초과한 요청입니다."), + PURCHASE_ALREADY_CONFIRMED(HttpStatus.BAD_REQUEST, "이미 구매 확정이 된 상태입니다."), + REFUND_NOT_ALLOWED_BEFORE_SHIPPING(HttpStatus.BAD_REQUEST, "배송 대기 전 상태에서는 환불을 요청할 수 없습니다."), + REFUND_AFTER_SALES_NOT_FOUND(HttpStatus.NOT_FOUND , "찾을 수 없는 환불 요청입니다."), + REFUND_AFTER_SALES_REQUEST_INVALID_OWNER(HttpStatus.BAD_REQUEST, "판매자의 환불 요청이 아닙니다."), ; private final HttpStatus httpStatus; diff --git a/src/main/java/poomasi/global/error/ExceptionAdvice.java b/src/main/java/poomasi/global/error/ExceptionAdvice.java index 7dc73824..66dc848d 100644 --- a/src/main/java/poomasi/global/error/ExceptionAdvice.java +++ b/src/main/java/poomasi/global/error/ExceptionAdvice.java @@ -1,11 +1,15 @@ package poomasi.global.error; +import com.siot.IamportRestClient.exception.IamportResponseException; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.ErrorResponse; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import java.io.IOException; + @Slf4j @RestControllerAdvice public class ExceptionAdvice { @@ -41,4 +45,24 @@ public ErrorResponse paymentConfirmExceptionHandler(PaymentConfirmException exce .build(); } + @ExceptionHandler(IamportResponseException.class) + public ErrorResponse IamportResponseExceptionHandler(IamportResponseException exception) { + + log.error("[{}] : {}", "IamportResponseException", exception.getMessage()); + return ErrorResponse + .builder(exception, HttpStatus.BAD_GATEWAY, exception.getMessage()) + .title("아임포트 서버 응답 장애 발생") + .build(); + } + + @ExceptionHandler(IOException.class) + public ErrorResponse IamportResponseExceptionHandler(IOException exception) { + log.error("[{}] : {}", "IOException", exception.getMessage()); + return ErrorResponse + .builder(exception, HttpStatus.BAD_GATEWAY, exception.getMessage()) + .title("통신 도중 IOException 발생") + .build(); + } + + } From 5b4736e651601fc7232af14421b59b1be208c318 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Mon, 11 Nov 2024 22:48:20 +0900 Subject: [PATCH 49/59] =?UTF-8?q?refactoring=20:=20controller=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AfterSalesController.java | 80 +++++++++++++++++++ .../controller/PaymentController.java | 24 +++--- .../order/controller/OrderController.java | 35 ++++---- 3 files changed, 108 insertions(+), 31 deletions(-) create mode 100644 src/main/java/poomasi/domain/order/_aftersales/controller/AfterSalesController.java diff --git a/src/main/java/poomasi/domain/order/_aftersales/controller/AfterSalesController.java b/src/main/java/poomasi/domain/order/_aftersales/controller/AfterSalesController.java new file mode 100644 index 00000000..4d4d8c2e --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/controller/AfterSalesController.java @@ -0,0 +1,80 @@ +package poomasi.domain.order._aftersales.controller; + + +import com.siot.IamportRestClient.exception.IamportResponseException; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.*; +import poomasi.domain.order._aftersales.dto.cancel.request.FarmCancelRequest; +import poomasi.domain.order._aftersales.dto.cancel.request.ProductCancelRequest; +import poomasi.domain.order._aftersales.dto.refund.request.ProductRefundRequest; +import poomasi.domain.order._aftersales.dto.refund.request.ProductRefundRequestApprovalRequest; +import poomasi.domain.order._aftersales.dto.refund.request.ProductRefundRequestDeniedRequest; +import poomasi.domain.order._aftersales.service.FarmAfterSalesService; +import poomasi.domain.order._aftersales.service.ProductAfterSalesService; + +import java.io.IOException; + +@RestController +@RequestMapping("/api/aftersales") +@RequiredArgsConstructor +public class AfterSalesController { + + private final ProductAfterSalesService productAfterSalesService; + private final FarmAfterSalesService farmAfterSalesService; + + //-------------------------product cancel---------------------// + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER"}) + @PostMapping("/product/cancel") + public ResponseEntity productCancel(@RequestBody ProductCancelRequest productCancelRequest) throws IOException, IamportResponseException { + return ResponseEntity.ok( + productAfterSalesService.cancel(productCancelRequest) + ); + } + + //-------------------------product refund---------------------// + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER"}) + @PostMapping("/refund-request") + public ResponseEntity requestRefund(@RequestBody ProductRefundRequest productRefundRequest) { + return ResponseEntity.ok( + productAfterSalesService. + createRefundRequest(productRefundRequest) + ); + } + + @Secured({"ROLE_FARMER"}) + @PostMapping("/approve-refund-request") + public ResponseEntity approveRefundRequest(@RequestBody ProductRefundRequestApprovalRequest productRefundRequestApprovalRequest) throws IOException, IamportResponseException { + return ResponseEntity.ok( + productAfterSalesService.processRefundApproval(productRefundRequestApprovalRequest) + ); + } + + + @Secured({"ROLE_FARMER"}) + @PostMapping("/deniedrefund-request") + public ResponseEntity deniedRefundRequest(@RequestBody ProductRefundRequestDeniedRequest productRefundRequestDeniedRequest) { + return ResponseEntity.ok( + productAfterSalesService.processRefundDenied(productRefundRequestDeniedRequest) + ); + } + + + //-------------------------farm cancel---------------------// + @Secured({"ROLE_CUSTOMER", "ROLE_FARMER"}) + @PostMapping("/farm/cancel") + public ResponseEntity farmCancel(@RequestBody FarmCancelRequest farmCancelRequest) throws IOException, IamportResponseException { + return ResponseEntity.ok( + farmAfterSalesService.farmCancel(farmCancelRequest) + ); + } + + + //------------웹훅 api 받아서 해야 함---------// + + + + + +} diff --git a/src/main/java/poomasi/domain/order/_payment/controller/PaymentController.java b/src/main/java/poomasi/domain/order/_payment/controller/PaymentController.java index 21324d48..58777551 100644 --- a/src/main/java/poomasi/domain/order/_payment/controller/PaymentController.java +++ b/src/main/java/poomasi/domain/order/_payment/controller/PaymentController.java @@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.*; import poomasi.domain.order._payment.dto.request.PaymentPreRegisterRequest; import poomasi.domain.order._payment.dto.request.PaymentWebHookRequest; -import poomasi.domain.order._payment.service.PaymentService; +import poomasi.domain.order._payment.service.ProductPaymentService; import java.io.IOException; @@ -17,37 +17,33 @@ @RequiredArgsConstructor public class PaymentController { - private final PaymentService paymentService; + private final ProductPaymentService productPaymentService; @Description("사전 결제 api") @Secured({"ROLE_CUSTOMER", "ROLE_FARMER"}) @PostMapping("/pre-payment") public ResponseEntity postPrepare(PaymentPreRegisterRequest paymentPreRegisterRequest) throws IamportResponseException, IOException { return ResponseEntity.ok( - paymentService.portonePrePaymentRegister(paymentPreRegisterRequest) + productPaymentService.portonePrePaymentRegister(paymentPreRegisterRequest) ); } - @Description("사후 결제(검증 api)") - @PostMapping("/validate") - public void validatePayment(@RequestBody PaymentWebHookRequest paymentWebHookRequest) throws IamportResponseException, IOException { - paymentService.handlePortOneProductWebhookEvent(paymentWebHookRequest); + @Description("결제 바로 직전 포트원에서 보내는 confirm 요청" + " 결제를 진행하려면 HTTP Status 200 응답, 그렇지 않으면 500 응답 보내기" ) + @PostMapping("/confirm/") + public ResponseEntity confirmProductStock(@RequestParam String merchantUid, @RequestParam String impUid) throws IamportResponseException, IOException { + productPaymentService.confirmBeforePayment(merchantUid, impUid); + return ResponseEntity.ok().build(); } @Description("포트원 웹훅 수신 api") @PostMapping("/portone-webhook") public void handleIamportWebhook(@RequestBody PaymentWebHookRequest paymentWebHookRequest) throws IamportResponseException, IOException { - paymentService.handlePortOneProductWebhookEvent(paymentWebHookRequest); + productPaymentService.handlePortOneProductWebhookEvent(paymentWebHookRequest); } - @Description("결제 바로 직전 포트원에서 보내는 confirm 요청" + " 결제를 진행하려면 HTTP Status 200 응답, 그렇지 않으면 500 응답 보내기" ) - @PostMapping("/confirm-stock/") - public ResponseEntity confirmProductStock(@RequestParam String merchantUid, @RequestParam String impUid) throws IamportResponseException, IOException { - paymentService.confirmProductStock(merchantUid, impUid); - return ResponseEntity.ok().build(); - } + diff --git a/src/main/java/poomasi/domain/order/controller/OrderController.java b/src/main/java/poomasi/domain/order/controller/OrderController.java index a6e17aa1..d36111e6 100644 --- a/src/main/java/poomasi/domain/order/controller/OrderController.java +++ b/src/main/java/poomasi/domain/order/controller/OrderController.java @@ -7,13 +7,13 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import org.springframework.security.access.annotation.Secured; -import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.*; -import poomasi.domain.auth.security.userdetail.UserDetailsImpl; +import poomasi.domain.farm.service.FarmService; import poomasi.domain.order._payment.dto.request.PaymentPreRegisterRequest; -import poomasi.domain.order._payment.service.PaymentService; -import poomasi.domain.order.dto.request.OrderRegisterRequest; -import poomasi.domain.order.service.OrderService; +import poomasi.domain.order._payment.service.ProductPaymentService; +import poomasi.domain.order.dto.request.ProductOrderRegisterRequest; +import poomasi.domain.order.service.FarmOrderService; +import poomasi.domain.order.service.ProductOrderService; import java.io.IOException; @@ -24,16 +24,17 @@ @RequiredArgsConstructor public class OrderController { - private final OrderService orderService; - private final PaymentService paymentService; + private final ProductOrderService productOrderService; + private final FarmOrderService farmOrderService; + private final ProductPaymentService productPaymentService; @Secured({"ROLE_CUSTOMER", "ROLE_FARMER"}) @PostMapping("/product/pre-order") @Description("product 사전 결제") - public ResponseEntity createProductPreOrder(@RequestBody OrderRegisterRequest orderRegisterRequest) throws IOException, IamportResponseException { - PaymentPreRegisterRequest paymentPreRegisterRequest = orderService.productPreOrderRegister(orderRegisterRequest); + public ResponseEntity createProductPreOrder(@RequestBody ProductOrderRegisterRequest productOrderRegisterRequest) throws IOException, IamportResponseException { + PaymentPreRegisterRequest paymentPreRegisterRequest = productOrderService.productPreOrderRegister(productOrderRegisterRequest); return ResponseEntity.ok( - paymentService.portonePrePaymentRegister(paymentPreRegisterRequest) + productPaymentService.portonePrePaymentRegister(paymentPreRegisterRequest) ); } @@ -41,9 +42,9 @@ public ResponseEntity createProductPreOrder(@RequestBody OrderRegisterRequest @PostMapping("/farm/pre-order") @Description("farm 사전 결제") public ResponseEntity createFarmPreOrder() throws IOException, IamportResponseException { - PaymentPreRegisterRequest paymentPreRegisterRequest = orderService.farmPreOrderRegister(); + PaymentPreRegisterRequest paymentPreRegisterRequest = productOrderService.farmPreOrderRegister(); return ResponseEntity.ok( - paymentService.portonePrePaymentRegister(paymentPreRegisterRequest) + productPaymentService.portonePrePaymentRegister(paymentPreRegisterRequest) ); } @@ -51,7 +52,7 @@ public ResponseEntity createFarmPreOrder() throws IOException, IamportRespons @GetMapping("/{orderId}") public ResponseEntity getAllOrdersByMember(@PathVariable Long orderId) { return ResponseEntity.ok( - orderService.findOrderByMemberId(orderId) + productOrderService.findOrderByMemberId(orderId) ); } @@ -59,7 +60,7 @@ public ResponseEntity getAllOrdersByMember(@PathVariable Long orderId) { @GetMapping("/") public ResponseEntity getOrdersByMember() { return ResponseEntity.ok( - orderService.findAllOrdersByMemberId() + productOrderService.findAllOrdersByMemberId() ); } @@ -67,7 +68,7 @@ public ResponseEntity getOrdersByMember() { @GetMapping("/{orderId}/details") public ResponseEntity getOrderDetailsByMember(@PathVariable Long orderId) { return ResponseEntity.ok( - orderService.findOrderDetailsByOrderId(orderId) + productOrderService.findOrderDetailsByOrderId(orderId) ); } @@ -76,7 +77,7 @@ public ResponseEntity getOrderDetailsByMember(@PathVariable Long orderId) { @GetMapping("/{orderId}/product/details") public ResponseEntity getOrderProductDetailsByOrderId(@PathVariable Long orderId) { return ResponseEntity.ok( - orderService.findAllOrderProductDetails(orderId) + productOrderService.findAllOrderProductDetails(orderId) ); } @@ -84,7 +85,7 @@ public ResponseEntity getOrderProductDetailsByOrderId(@PathVariable Long orde @GetMapping("/{orderId}/product/details/{detailsId}") public ResponseEntity getOrderProductDetailsByDetailsId(@PathVariable Long orderId, @PathVariable Long detailsId) { return ResponseEntity.ok( - orderService.findOrderProductDetailsById(orderId, detailsId) + productOrderService.findOrderProductDetailsById(orderId, detailsId) ); } From e93cdb90669eb786dde3c657670485917e742541 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Mon, 11 Nov 2024 22:49:50 +0900 Subject: [PATCH 50/59] =?UTF-8?q?feat=20:=20iamport=20config=EC=99=80=20pa?= =?UTF-8?q?ymentUtil=20=EB=B6=84=EB=A6=AC=20-=20=EA=B8=B0=EC=A1=B4=20payme?= =?UTF-8?q?ntService=EA=B0=80=20=EB=84=88=EB=AC=B4=20=EB=A7=8E=EC=9D=80=20?= =?UTF-8?q?=EA=B2=B0=EC=A0=9C=20=EB=A1=9C=EC=A7=81=EC=9D=84=20=EB=8B=B4?= =?UTF-8?q?=EB=8B=B9=ED=95=98=EC=98=80=EC=8A=B5=EB=8B=88=EB=8B=A4=20?= =?UTF-8?q?=EA=B7=BC=EB=8D=B0=20=EB=8B=A4=EB=A1=A0=20service=EC=97=90?= =?UTF-8?q?=EC=84=9C=EB=8F=84=20=EC=9D=B4=EB=A5=BC=20=ED=98=B8=EC=B6=9C?= =?UTF-8?q?=ED=95=98=EB=8B=88=20=EB=84=88=EB=AC=B4=20=EB=B3=B5=EC=9E=A1?= =?UTF-8?q?=ED=95=9C=20=EC=9D=98=EC=A1=B4=EA=B4=80=EA=B3=84=EA=B0=80=20?= =?UTF-8?q?=EB=90=9C=20=EA=B2=83=20=EA=B0=99=EC=95=84=EC=84=9C=20paymentUt?= =?UTF-8?q?il=EC=9D=84=20=EA=B5=AC=ED=98=84=ED=95=98=EC=98=80=EC=8A=B5?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/_payment/config/IamportConfig.java | 3 +- .../order/_payment/util/PaymentUtil.java | 88 +++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 src/main/java/poomasi/domain/order/_payment/util/PaymentUtil.java diff --git a/src/main/java/poomasi/domain/order/_payment/config/IamportConfig.java b/src/main/java/poomasi/domain/order/_payment/config/IamportConfig.java index 8f5dd755..758f0f92 100644 --- a/src/main/java/poomasi/domain/order/_payment/config/IamportConfig.java +++ b/src/main/java/poomasi/domain/order/_payment/config/IamportConfig.java @@ -9,7 +9,7 @@ @Configuration public class IamportConfig { - @Value("${IMP_API_KEY}") + @Value("${imp.api.key}") private String apiKey; @Value("${imp.api.secretKey}") @@ -19,5 +19,4 @@ public class IamportConfig { public IamportClient iamportClient() { return new IamportClient(apiKey, secretKey); } - } diff --git a/src/main/java/poomasi/domain/order/_payment/util/PaymentUtil.java b/src/main/java/poomasi/domain/order/_payment/util/PaymentUtil.java new file mode 100644 index 00000000..ce1d9212 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_payment/util/PaymentUtil.java @@ -0,0 +1,88 @@ +package poomasi.domain.order._payment.util; + + +import com.siot.IamportRestClient.IamportClient; +import com.siot.IamportRestClient.exception.IamportResponseException; +import com.siot.IamportRestClient.request.CancelData; +import com.siot.IamportRestClient.request.PrepareData; +import com.siot.IamportRestClient.response.IamportResponse; +import com.siot.IamportRestClient.response.Payment; +import jdk.jfr.Description; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import poomasi.global.error.BusinessError; +import poomasi.global.error.BusinessException; + +import java.io.IOException; +import java.math.BigDecimal; + +@Component +public class PaymentUtil { + + private final IamportClient iamportClient; + + @Autowired + public PaymentUtil(IamportClient iamportClient) { + this.iamportClient = iamportClient; + } + + @Description("포트원에서 결제 금액 조회하는 메서드") + public BigDecimal getPaymentAmount(String impUid) throws IOException, IamportResponseException { + IamportResponse iamportResponse = getSingleTransaction(impUid); + return iamportResponse.getResponse().getAmount(); + } + + @Description("단건 결제 조회 API") + public IamportResponse getSingleTransaction(String impUid) throws IOException, IamportResponseException { + IamportResponse iamportResponse = iamportClient.paymentByImpUid(impUid); + return iamportResponse; + } + + @Description("결제 취소 api") + public void cancelPaymentByImpUid(String impUid) throws IOException, IamportResponseException { + CancelData cancelDate = new CancelData(impUid, false); + iamportClient.cancelPaymentByImpUid(cancelDate); + } + + @Transactional + @Description("imp uid로 결제 부분 환불 api 호출") + public void partialRefundByImpUid(String impUid, BigDecimal checkSum, BigDecimal amount, String reason) throws IOException, IamportResponseException { + CancelData cancelData = new CancelData(impUid, true, amount); + cancelData.setChecksum(checkSum); + cancelData.setReason(reason); + iamportClient.cancelPaymentByImpUid(cancelData); + } + + @Transactional + @Description("merchant Uid로 결제 부분 환불 api 호출") + public void partialRefundByMerchantUid(String merchantUid, BigDecimal checkSum, BigDecimal amount, String reason) throws IOException, IamportResponseException { + CancelData cancelData = new CancelData(merchantUid, false, amount); + cancelData.setChecksum(checkSum); + cancelData.setReason(reason); + iamportClient.cancelPaymentByImpUid(cancelData); + } + + + @Description("사전 결제 데이터 전송") + public void sendPrepareData(String merchantUid, BigDecimal amount) throws IOException, IamportResponseException { + PrepareData prepareData = this.generatePrepareData(merchantUid, amount); + iamportClient.postPrepare(prepareData); + } + + @Description("단건 조회 후, 결제 되어야 할 금액과 결제 된 금액이 같은지 확인하는 메서드") + public boolean validatePaymentAmount(String impUid, BigDecimal amountToBePaid) throws IOException, IamportResponseException{ + IamportResponse iamportResponse = getSingleTransaction(impUid); //내가 보냄 + BigDecimal amount = iamportResponse.getResponse().getAmount(); + if(amountToBePaid.compareTo(amount)!=0){ + return false; + } + return true; + } + + @Description("사전 결제를 위한 Prepare Data를 만드는 메서드") + private PrepareData generatePrepareData(String merchantUid, BigDecimal amount) { + return new PrepareData(merchantUid, amount); + } +} From f2ac7d4064d19cc649c4e316b5b18cf20109aa2a Mon Sep 17 00:00:00 2001 From: amm0124 Date: Mon, 11 Nov 2024 22:50:50 +0900 Subject: [PATCH 51/59] =?UTF-8?q?refactoring=20:=20payment=20service=20?= =?UTF-8?q?=EB=82=98=EB=88=94=20-=20=EB=91=90=20=EA=B0=9C=EC=9D=98=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=EC=97=90=20=EB=8C=80=ED=95=B4?= =?UTF-8?q?=EC=84=9C=20service=EB=A5=BC=20=EB=82=98=EB=88=84=EC=97=88?= =?UTF-8?q?=EC=8A=B5=EB=8B=88=EB=8B=A4=20=EB=91=90=20service=EB=8A=94=20?= =?UTF-8?q?=EB=82=98=EC=A4=91=EC=97=90=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=EB=A5=BC=20=EC=83=81=EC=86=8D=EB=B0=9B?= =?UTF-8?q?=EC=95=84=EC=84=9C=20=EA=B5=AC=ED=98=84=ED=95=98=EA=B2=A0?= =?UTF-8?q?=EC=8A=B5=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_payment/service/FarmPaymentService.java | 15 + .../_payment/service/PaymentService.java | 279 ------------------ .../service/ProductPaymentService.java | 191 ++++++++++++ 3 files changed, 206 insertions(+), 279 deletions(-) create mode 100644 src/main/java/poomasi/domain/order/_payment/service/FarmPaymentService.java delete mode 100644 src/main/java/poomasi/domain/order/_payment/service/PaymentService.java create mode 100644 src/main/java/poomasi/domain/order/_payment/service/ProductPaymentService.java diff --git a/src/main/java/poomasi/domain/order/_payment/service/FarmPaymentService.java b/src/main/java/poomasi/domain/order/_payment/service/FarmPaymentService.java new file mode 100644 index 00000000..ff842ae9 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_payment/service/FarmPaymentService.java @@ -0,0 +1,15 @@ +package poomasi.domain.order._payment.service; + + +import com.siot.IamportRestClient.exception.IamportResponseException; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Service +public class FarmPaymentService { + + + + +} diff --git a/src/main/java/poomasi/domain/order/_payment/service/PaymentService.java b/src/main/java/poomasi/domain/order/_payment/service/PaymentService.java deleted file mode 100644 index a65ec9dc..00000000 --- a/src/main/java/poomasi/domain/order/_payment/service/PaymentService.java +++ /dev/null @@ -1,279 +0,0 @@ -package poomasi.domain.order._payment.service; - -import com.siot.IamportRestClient.IamportClient; -import com.siot.IamportRestClient.exception.IamportResponseException; -import com.siot.IamportRestClient.request.CancelData; -import com.siot.IamportRestClient.request.PrepareData; -import com.siot.IamportRestClient.response.IamportResponse; -import jdk.jfr.Description; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Isolation; -import org.springframework.transaction.annotation.Transactional; -import poomasi.domain.auth.security.userdetail.UserDetailsImpl; -import poomasi.domain.member.entity.Member; -import poomasi.domain.order._payment.dto.request.PaymentPreRegisterRequest; -import poomasi.domain.order._payment.dto.request.PaymentWebHookRequest; -import poomasi.domain.order._payment.dto.response.PaymentPreRegisterResponse; -import poomasi.domain.order._payment.dto.response.PaymentResponse; -import poomasi.domain.order._payment.entity.Payment; -import poomasi.domain.order._payment.repository.PaymentRepository; -import poomasi.domain.order.entity._product.OrderedProduct; -import poomasi.domain.order.entity._product.ProductOrder; -import poomasi.domain.order.repository.ProductOrderRepository; -import poomasi.domain.product._cart.service.CartService; -import poomasi.domain.product.entity.Product; -import poomasi.global.error.BusinessException; -import poomasi.global.error.PaymentConfirmError; -import poomasi.global.error.PaymentConfirmException; - -import java.io.IOException; -import java.math.BigDecimal; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import static poomasi.domain.order.entity.OrderStatus.AWAITING_SELLER_CONFIRMATION; -import static poomasi.domain.order.entity.OrderStatus.PENDING; -import static poomasi.global.error.BusinessError.*; - -@Service -@RequiredArgsConstructor -@Slf4j -public class PaymentService { - - @Autowired - private final PaymentRepository paymentRepository; - private final IamportClient iamportClient; - private final ProductOrderRepository productOrderRepository; - private final CartService cartService; - - private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); - private final AtomicBoolean isWebhookReceived = new AtomicBoolean(false); // 웹훅 수신 여부 체크 - - @Description("사전 결제 등록. 프론트엔드에게 서버 merchant uid를 return 해야 함") - public PaymentPreRegisterResponse portonePrePaymentRegister(PaymentPreRegisterRequest paymentPreRegisterRequest) throws IOException, IamportResponseException { - - PrepareData prepareData = new PrepareData(paymentPreRegisterRequest.merchantUid(), - paymentPreRegisterRequest.amount() - ); - iamportClient.postPrepare(prepareData); - return PaymentPreRegisterResponse.from( - paymentPreRegisterRequest.merchantUid() - ); - } - - - @Transactional(isolation = Isolation.SERIALIZABLE) - @Description("포트원 결제 직전 바로 받는 confirm 요청. 40초 대기") - public void confirmProductStock(String impUid, String merchantUid) throws IOException, IamportResponseException { - - ProductOrder productOrder = productOrderRepository.findByMerchantUidAndImpUid(merchantUid, impUid) - .orElseThrow(() -> new BusinessException(PAYMENT_NOT_FOUND)); - - List orderedProductList = productOrder.getOrderedProducts(); - //수량 검증 - for(OrderedProduct orderedProduct : orderedProductList) { - Product product = orderedProduct.getProduct(); - Integer remainQuantity = product.getStock(); - Integer orderQuantity = orderedProduct.getCount(); - - //주문 재고가 남은 재고보다 많다면 500 + reason 보내야 함 - if(orderQuantity > remainQuantity){ - throw new PaymentConfirmException(PaymentConfirmError.PAYMENT_PROUCT_CONFIRM_EXCEPTION); - } - } - BigDecimal amountToBePaid = productOrder.getTotalAmount(); - //재고 검증 완료 -> 200 OK 보내야 함 + 웹훅 수신 여부에 따라 분기 - scheduler.schedule(() -> { - try { - if (!isWebhookReceived.get()) { //수신 못 받으면 - if(sendAndValidateAmount(impUid, amountToBePaid)){ //impUid를 가지고 포트원 서버에 요청을 한 후, db와 결제 금액 비교한다. - productOrder.setOrderStatus(AWAITING_SELLER_CONFIRMATION); - decreaseStock(productOrder); - }else{ - //실제 결제 된 금액과 결제 되어야 할 금액이 다르다면 -> 결제 취소 api를 호출해야 한다. - cancelPaymentByImpUid(impUid); - } - } - } catch (IOException | IamportResponseException e) { - e.printStackTrace(); - } - }, 40, TimeUnit.SECONDS); - - } - - @Description("단건 조회 후, 결제 되어야 할 금액과 결제 된 금액이 같은지 확인하는 메서드") - public boolean sendAndValidateAmount(String impUid, BigDecimal amountToBePaid) throws IOException, IamportResponseException{ - BigDecimal amount = getPaymentAmount(impUid); - if(amountToBePaid.compareTo(amount)==0){ - return true; - } - return false; - } - - - @Description("포트원에서 결제 금액 조회하는 메서드") - public BigDecimal getPaymentAmount(String impUid) throws IOException, IamportResponseException{ - IamportResponse iamportResponse = getSingleTransaction(impUid); - return iamportResponse.getResponse().getAmount(); - } - - @Description("웹훅 처리 service -> 결제 정상적으로 성공됨을 보장") - @Transactional(isolation = Isolation.SERIALIZABLE) - public void handlePortOneProductWebhookEvent(PaymentWebHookRequest paymentWebHookRequest) throws IOException, IamportResponseException { - - isWebhookReceived.set(true); //웹훅 수신 플래그 설정하기 - - String impUid = paymentWebHookRequest.impUid(); - ProductOrder productOrder = productOrderRepository.findByImpUid(impUid) - .orElseThrow(() -> new BusinessException(PAYMENT_NOT_FOUND)); - BigDecimal amountToBePaid = productOrder.getTotalAmount(); - - //결제 되어야 할 금액과 결제 된 금액이 같다면 - if(sendAndValidateAmount(impUid, amountToBePaid)){ - productOrder.setOrderStatus(AWAITING_SELLER_CONFIRMATION); - decreaseStock(productOrder); - }else{ - cancelPaymentByImpUid(impUid); //실제 결제 된 금액과 결제 되어야 할 금액이 다르다면 -> 결제 취소 api를 호출해야 한다. - //throw new BusinessException - } - - } - - @Description("재고 차감 메서드") - @Transactional(isolation = Isolation.SERIALIZABLE) - public void decreaseStock(ProductOrder productOrder){ - List orderedProductList = productOrder.getOrderedProducts(); - for (OrderedProduct orderedProduct : orderedProductList){ - Product product = orderedProduct.getProduct(); - Integer remainQuantity = product.getStock(); //남은 수량 - Integer subtractQuantity = orderedProduct.getCount();//빼야 할 수량 - if(subtractQuantity > remainQuantity){ - throw new BusinessException(STOCK_QUANTITY_EXCEEDED); - } - product.subtractStock(subtractQuantity); - } - } - - /* - com.siot.IamportRestClient.response.Payment payment = iamportResponse.getResponse(); - int code = iamportResponse.getCode(); - String message = iamportResponse.getMessage(); - String status = payment.getStatus(); - BigDecimal amount = payment.getAmount(); - */ - @Description("단건 결제 조회 API") - public IamportResponse getSingleTransaction(String impUid) throws IOException, IamportResponseException { - IamportResponse iamportResponse = iamportClient.paymentByImpUid(impUid); - return iamportResponse; - } - - @Description("서버에서 마지막 검증") - public boolean verifyPostPayment(String impUid) throws IOException, IamportResponseException { - ProductOrder productOrder = productOrderRepository.findByImpUid(impUid) - .orElseThrow(() -> new BusinessException(PAYMENT_NOT_FOUND)); - if(productOrder.getOrderStatus() == PENDING){ - throw new BusinessException(PAYMENT_BAD_REQUEST); - } - return true; - } - - /*@Transactional - @Description("프론트에서 받아온 결과를 validate하는 메서드") - public void portoneVerifyPostPayment(PaymentWebHookRequest paymentWebHookRequest) throws IOException, IamportResponseException { - String impUid = paymentWebHookRequest.imp_uid(); - String merchantUid = paymentWebHookRequest.merchant_uid(); - IamportResponse iamportResponse = iamportClient.paymentByImpUid(impUid); - BigDecimal amount = iamportResponse.getResponse() - .getAmount(); - - ProductOrder order = productOrderRepository.findByMerchantUid(merchantUid) - .orElseThrow(() -> new BusinessException(ORDER_NOT_FOUND)); - - if(order.getOrderStatus()!=PENDING){ //이미 처리한 주문이라면 - throw new BusinessException(ORDER_ALREADY_PROCESSED); - } - - if(validatePaymentConsistency(order.getTotalAmount(), amount)){ //결제 금액이 맞지 않다면 -> 주문 취소 api 호출 - cancelPayment(iamportResponse); - throw new BusinessException(PAYMENT_AMOUNT_MISMATCH); - } - order.setOrderStatus(AWAITING_SELLER_CONFIRMATION); // 상태 변경 - cartService.removeSelected(); //장바구니 삭제 - }*/ - - //private boolean compareCartAndPaymentAmount(Cart cart, BigDecimal ) - - - private boolean validatePaymentConsistency(BigDecimal prepaymentAmount, BigDecimal postPaymentAmount){ - if (prepaymentAmount.compareTo(postPaymentAmount) != 0) { - return false; - } - return true; - } - - - public void cancelPaymentByImpUid(String impUid) throws IOException, IamportResponseException { - CancelData cancelDate = new CancelData(impUid, false); - iamportClient.cancelPaymentByImpUid(cancelDate); - } - - /*@Transactional - @Description("결제 전액 취소/환불 api") - public void cancelPayment(Payment payment, IamportResponse response) throws IOException, IamportResponseException{ - //TODO : 결제내역 단건 조회 통해서 이미 완료가 된 결제인지 확인 - //true면 Uid, false면 merchantUid로 판단 - CancelData cancelData = new CancelData(response.getResponse().getMerchantUid(), false); - iamportClient.cancelPaymentByImpUid(cancelData); - - }*/ - - /* @Description("결제 환불 api") - public void processRefund() throws IOException, IamportResponseException{ - - } -*/ - - @Transactional - @Description("결제 부분 환불 api 호출") - public void partialRefund(BigDecimal checkSum, IamportResponse response, BigDecimal amount) throws IOException, IamportResponseException { - //BigDecimal checkSum = payment.getChecksum(); - CancelData cancelData = new CancelData(response.getResponse().getMerchantUid(), false, amount); - cancelData.setChecksum(checkSum); - iamportClient.cancelPaymentByImpUid(cancelData); - - } - - public PaymentResponse getPayment(Long paymentId) { - Payment payment = paymentRepository.findById(paymentId) - .orElseThrow(() -> new BusinessException(PAYMENT_NOT_FOUND)); - return PaymentResponse.fromEntity(payment); - } - - @Description("orderID로 결제 방법 찾는 메서드") - public PaymentResponse getPaymentByOrderId(Long orderId) { - Member member = getMember(); - Payment payment = paymentRepository.findById(orderId) - .orElseThrow(() -> new BusinessException(PAYMENT_NOT_FOUND)); - return PaymentResponse.fromEntity(payment); - } - - - private Member getMember() { - Authentication authentication = SecurityContextHolder - .getContext().getAuthentication(); - Object impl = authentication.getPrincipal(); - Member member = ((UserDetailsImpl) impl).getMember(); - return member; - } - -} - - diff --git a/src/main/java/poomasi/domain/order/_payment/service/ProductPaymentService.java b/src/main/java/poomasi/domain/order/_payment/service/ProductPaymentService.java new file mode 100644 index 00000000..45751886 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_payment/service/ProductPaymentService.java @@ -0,0 +1,191 @@ +package poomasi.domain.order._payment.service; + +import com.siot.IamportRestClient.IamportClient; +import com.siot.IamportRestClient.exception.IamportResponseException; +import com.siot.IamportRestClient.request.CancelData; +import com.siot.IamportRestClient.response.IamportResponse; +import jdk.jfr.Description; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Transactional; +import poomasi.domain.auth.security.userdetail.UserDetailsImpl; +import poomasi.domain.member.entity.Member; +import poomasi.domain.order._payment.config.IamportConfig; +import poomasi.domain.order._payment.dto.request.PaymentPreRegisterRequest; +import poomasi.domain.order._payment.dto.request.PaymentWebHookRequest; +import poomasi.domain.order._payment.dto.response.PaymentPreRegisterResponse; +import poomasi.domain.order._payment.dto.response.PaymentResponse; +import poomasi.domain.order._payment.entity.Payment; +import poomasi.domain.order._payment.repository.PaymentRepository; +import poomasi.domain.order._payment.util.PaymentUtil; +import poomasi.domain.order.entity._product.OrderedProduct; +import poomasi.domain.order.entity._product.ProductOrder; +import poomasi.domain.order.repository.ProductOrderRepository; +import poomasi.domain.product._cart.service.CartService; +import poomasi.domain.product.entity.Product; +import poomasi.global.error.BusinessException; +import poomasi.global.error.PaymentConfirmError; +import poomasi.global.error.PaymentConfirmException; + +import java.io.IOException; +import java.math.BigDecimal; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import static poomasi.domain.order.entity.PaymentStatus.*; +import static poomasi.global.error.BusinessError.*; + +@Service +@RequiredArgsConstructor +@Slf4j +public class ProductPaymentService{ + + @Autowired + private final PaymentRepository paymentRepository; + private final ProductOrderRepository productOrderRepository; + private final PaymentUtil paymentUtil; + + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + private final AtomicBoolean isWebhookReceived = new AtomicBoolean(false); // 웹훅 수신 여부 체크 + //private final ThreadLocal isWebhookReceived = ThreadLocal.withInitial(AtomicBoolean::new); -> thread local로 제어 + + + @Description("사전 결제 등록. 프론트엔드에게 서버 merchant uid를 return 해야 함") + public PaymentPreRegisterResponse portonePrePaymentRegister(PaymentPreRegisterRequest paymentPreRegisterRequest) throws IOException, IamportResponseException { + + String merchantUid = paymentPreRegisterRequest.merchantUid(); + BigDecimal amount = paymentPreRegisterRequest.amount(); + + paymentUtil.sendPrepareData(merchantUid, amount); + return PaymentPreRegisterResponse.from( + paymentPreRegisterRequest.merchantUid() + ); + } + + @Transactional(isolation = Isolation.SERIALIZABLE) + @Description("포트원 결제 직전 바로 받는 confirm 요청. 40초 대기") + public void confirmBeforePayment(String impUid, String merchantUid) throws IOException, IamportResponseException { + + ProductOrder productOrder = productOrderRepository.findByMerchantUid(merchantUid) + .orElseThrow(() -> new BusinessException(PAYMENT_NOT_FOUND)); + + List orderedProductList = productOrder.getOrderedProducts(); + //수량 검증 + for(OrderedProduct orderedProduct : orderedProductList) { + Product product = orderedProduct.getProduct(); + Integer remainQuantity = product.getStock(); + Integer orderQuantity = orderedProduct.getCount(); + + //주문 재고가 남은 재고보다 많다면 500 + cancelReason 보내야 함 + if(orderQuantity > remainQuantity){ + throw new PaymentConfirmException(PaymentConfirmError.PAYMENT_PROUCT_CONFIRM_EXCEPTION); + } + } + + //결제 되어야 할 금액 + BigDecimal amountToBePaid = productOrder.getTotalAmount(); + + /* + * 1. 200ok 보내기 + * 2. 타이머 세팅 후 + * 타이머 타임 아웃 되면(웹훅을 받지 못하면) 결제 단건 api 호출 + * 만약 웹훅을 받으면 받은 데이터에서 getImpUid후, 결제 단건 호출 및 타이머 초기화 + * */ + + //재고 검증 완료 -> 200 OK 보내야 함 + 웹훅 수신 여부에 따라 분기 + scheduler.schedule(() -> { + try { + if (!isWebhookReceived.get()) { // 웹훅 수신 못 받으면 다시 보내기 + if(paymentUtil.validatePaymentAmount(impUid, amountToBePaid)){ + productOrder.setPaymentStatus(PAYMENT_COMPLETE); + decreaseStock(productOrder); //재고 차감 + }else{ + paymentUtil.cancelPaymentByImpUid(impUid); //실제 결제 된 금액과 결제 되어야 할 금액이 다르다면 -> 결제 취소 api를 호출해야 한다. + productOrder.setPaymentStatus(PAYMENT_DECLINED); + throw new BusinessException(PAYMENT_AMOUNT_MISMATCH); + } + } + } catch (IOException | IamportResponseException e) { + log.error(e.getMessage(), e); + throw new BusinessException(PAYMENT_BAD_REQUEST); + } + }, 40, TimeUnit.SECONDS); + + } + + @Description("웹훅 처리 service -> 결제 정상적으로 성공됨을 보장") + public void handlePortOneProductWebhookEvent(PaymentWebHookRequest paymentWebHookRequest) throws IOException, IamportResponseException { + + isWebhookReceived.set(true); //웹훅 수신 플래그 설정하기 + + String impUid = paymentWebHookRequest.impUid(); + String merchantUid = paymentWebHookRequest.merchantUid(); + ProductOrder productOrder = productOrderRepository.findByMerchantUid(merchantUid) + .orElseThrow(() -> new BusinessException(PAYMENT_NOT_FOUND)); + BigDecimal amountToBePaid = productOrder.getTotalAmount(); + + //결제 되어야 할 금액과 결제 된 금액이 같다면 + if(paymentUtil.validatePaymentAmount(impUid, amountToBePaid)){ + try{ + decreaseStock(productOrder); + productOrder.setPaymentStatus(PAYMENT_COMPLETE); + }catch(BusinessException businessException){ + productOrder.setPaymentStatus(PAYMENT_INSUFFICIENT_QUANTITY); + throw new BusinessException(PAYMENT_BAD_REQUEST); + } + }else{ + paymentUtil.cancelPaymentByImpUid(impUid); //실제 결제 된 금액과 결제 되어야 할 금액이 다르다면 -> 결제 취소 api를 호출해야 한다. + productOrder.setPaymentStatus(PAYMENT_DECLINED); + throw new BusinessException(PAYMENT_AMOUNT_MISMATCH); + } + } + + @Description("재고 차감 메서드. 감소하다 exception이 일어나면 rollback하고 결제 취소 해야 함") + @Transactional(isolation = Isolation.SERIALIZABLE) + public void decreaseStock(ProductOrder productOrder){ + List orderedProductList = productOrder.getOrderedProducts(); + for (OrderedProduct orderedProduct : orderedProductList){ + Product product = orderedProduct.getProduct(); + Integer remainQuantity = product.getStock(); //남은 수량 + Integer subtractQuantity = orderedProduct.getCount();//빼야 할 수량 + if(subtractQuantity > remainQuantity){ + throw new BusinessException(STOCK_QUANTITY_EXCEEDED); + } + product.subtractStock(subtractQuantity); + } + } + + + public PaymentResponse getPayment(Long paymentId) { + Payment payment = paymentRepository.findById(paymentId) + .orElseThrow(() -> new BusinessException(PAYMENT_NOT_FOUND)); + return PaymentResponse.fromEntity(payment); + } + + @Description("orderID로 결제 방법 찾는 메서드") + public PaymentResponse getPaymentByOrderId(Long orderId) { + Member member = getMember(); + Payment payment = paymentRepository.findById(orderId) + .orElseThrow(() -> new BusinessException(PAYMENT_NOT_FOUND)); + return PaymentResponse.fromEntity(payment); + } + + private Member getMember() { + Authentication authentication = SecurityContextHolder + .getContext().getAuthentication(); + Object impl = authentication.getPrincipal(); + Member member = ((UserDetailsImpl) impl).getMember(); + return member; + } + +} + + From 521e2f0efe72302e7c03390054737a8bd10f49a7 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Mon, 11 Nov 2024 22:51:42 +0900 Subject: [PATCH 52/59] =?UTF-8?q?feat=20:=20cancelService=20interface=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20-=20=EB=91=90=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=EC=97=90=EC=84=9C=20=EA=B3=B5=ED=86=B5=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=20can?= =?UTF-8?q?cel=EC=9D=84=20=EC=A0=9C=EB=84=A4=EB=A6=AD=EC=9D=84=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=97=AC=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=EB=A5=BC=20=EC=9E=91=EC=84=B1=ED=95=98?= =?UTF-8?q?=EC=98=80=EC=8A=B5=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/order/_aftersales/service/CancelService.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/poomasi/domain/order/_aftersales/service/CancelService.java diff --git a/src/main/java/poomasi/domain/order/_aftersales/service/CancelService.java b/src/main/java/poomasi/domain/order/_aftersales/service/CancelService.java new file mode 100644 index 00000000..5238467c --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/service/CancelService.java @@ -0,0 +1,9 @@ +package poomasi.domain.order._aftersales.service; + +import com.siot.IamportRestClient.exception.IamportResponseException; + +import java.io.IOException; + +public interface CancelService { + T cancel(P parameter) throws IOException, IamportResponseException; +} From 838b4a3f78731c81f6b0e049d141265c30ed9eec Mon Sep 17 00:00:00 2001 From: amm0124 Date: Mon, 11 Nov 2024 22:52:26 +0900 Subject: [PATCH 53/59] =?UTF-8?q?feat=20:=20after=20service=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20dto=20-=20record=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=EB=A1=9C=20=EA=B5=AC=ED=98=84=ED=95=98=EC=98=80=EC=8A=B5?= =?UTF-8?q?=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/cancel/request/FarmCancelRequest.java | 4 ++++ .../cancel/request/ProductCancelRequest.java | 4 ++++ .../cancel/response/FarmCancelResponse.java | 4 ++++ .../response/ProductCancelResponse.java | 16 ++++++++++++++++ .../request/ProductExchangeRequest.java | 4 ++++ .../response/ProductExchangeResponse.java | 4 ++++ .../refund/request/ProductRefundRequest.java | 13 +++++++++++++ .../ProductRefundRequestApprovalRequest.java | 5 +++++ .../ProductRefundRequestDeniedRequest.java | 5 +++++ .../ProductRefundRequestApprovalResponse.java | 12 ++++++++++++ .../ProductRefundRequestDeniedResponse.java | 8 ++++++++ .../ProductRefundRequestResponse.java | 19 +++++++++++++++++++ .../dto/response/OrderDetailsResponse.java | 4 ++-- 13 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/cancel/request/FarmCancelRequest.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/cancel/request/ProductCancelRequest.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/cancel/response/FarmCancelResponse.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/cancel/response/ProductCancelResponse.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/exchange/request/ProductExchangeRequest.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/exchange/response/ProductExchangeResponse.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/refund/request/ProductRefundRequest.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/refund/request/ProductRefundRequestApprovalRequest.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/refund/request/ProductRefundRequestDeniedRequest.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/refund/response/ProductRefundRequestApprovalResponse.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/refund/response/ProductRefundRequestDeniedResponse.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/dto/refund/response/ProductRefundRequestResponse.java diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/cancel/request/FarmCancelRequest.java b/src/main/java/poomasi/domain/order/_aftersales/dto/cancel/request/FarmCancelRequest.java new file mode 100644 index 00000000..2034f7dd --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/dto/cancel/request/FarmCancelRequest.java @@ -0,0 +1,4 @@ +package poomasi.domain.order._aftersales.dto.cancel.request; + +public record FarmCancelRequest(Long reservationId) { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/cancel/request/ProductCancelRequest.java b/src/main/java/poomasi/domain/order/_aftersales/dto/cancel/request/ProductCancelRequest.java new file mode 100644 index 00000000..20a9a326 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/dto/cancel/request/ProductCancelRequest.java @@ -0,0 +1,4 @@ +package poomasi.domain.order._aftersales.dto.cancel.request; + +public record ProductCancelRequest(Long orderedProductId, Integer cancelRequestQuantity, String cancelReason) { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/cancel/response/FarmCancelResponse.java b/src/main/java/poomasi/domain/order/_aftersales/dto/cancel/response/FarmCancelResponse.java new file mode 100644 index 00000000..8158ee58 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/dto/cancel/response/FarmCancelResponse.java @@ -0,0 +1,4 @@ +package poomasi.domain.order._aftersales.dto.cancel.response; + +public record FarmCancelResponse(Long reservationId, String status) { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/cancel/response/ProductCancelResponse.java b/src/main/java/poomasi/domain/order/_aftersales/dto/cancel/response/ProductCancelResponse.java new file mode 100644 index 00000000..6b11a4b8 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/dto/cancel/response/ProductCancelResponse.java @@ -0,0 +1,16 @@ +package poomasi.domain.order._aftersales.dto.cancel.response; + +import poomasi.domain.order._aftersales.entity._product.ProductAfterSalesStatus; +import poomasi.domain.order.entity._product.OrderedProductStatus; + +import java.math.BigDecimal; + +public record ProductCancelResponse( + Long orderedProductId, + OrderedProductStatus orderedProductStatus, + + Long productAfterSalesDetailId, + Integer cancelQuantity, + ProductAfterSalesStatus productAfterSalesStatus, + BigDecimal finalCancelAmount) { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/exchange/request/ProductExchangeRequest.java b/src/main/java/poomasi/domain/order/_aftersales/dto/exchange/request/ProductExchangeRequest.java new file mode 100644 index 00000000..8b8c60ee --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/dto/exchange/request/ProductExchangeRequest.java @@ -0,0 +1,4 @@ +package poomasi.domain.order._aftersales.dto.exchange.request; + +public record ProductExchangeRequest(Long orderedProductId, String exchangeReason) { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/exchange/response/ProductExchangeResponse.java b/src/main/java/poomasi/domain/order/_aftersales/dto/exchange/response/ProductExchangeResponse.java new file mode 100644 index 00000000..4a8083a8 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/dto/exchange/response/ProductExchangeResponse.java @@ -0,0 +1,4 @@ +package poomasi.domain.order._aftersales.dto.exchange.response; + +public record ProductExchangeResponse(Long orderedProductId, String message) { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/refund/request/ProductRefundRequest.java b/src/main/java/poomasi/domain/order/_aftersales/dto/refund/request/ProductRefundRequest.java new file mode 100644 index 00000000..940b0d4f --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/dto/refund/request/ProductRefundRequest.java @@ -0,0 +1,13 @@ +package poomasi.domain.order._aftersales.dto.refund.request; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; + +public record ProductRefundRequest( + @NotNull Long orderedProductId, // 필수 필드 + @Positive Integer refundRequestQuantity, //필수 + @Size(max = 500) String refundReason, // 필수 필드 + @Size(max = 20) String request // nullable 필드 +) { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/refund/request/ProductRefundRequestApprovalRequest.java b/src/main/java/poomasi/domain/order/_aftersales/dto/refund/request/ProductRefundRequestApprovalRequest.java new file mode 100644 index 00000000..2bef6976 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/dto/refund/request/ProductRefundRequestApprovalRequest.java @@ -0,0 +1,5 @@ +package poomasi.domain.order._aftersales.dto.refund.request; + +public record ProductRefundRequestApprovalRequest(Long productAfterSalesDetailId, + String invoiceNumber) { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/refund/request/ProductRefundRequestDeniedRequest.java b/src/main/java/poomasi/domain/order/_aftersales/dto/refund/request/ProductRefundRequestDeniedRequest.java new file mode 100644 index 00000000..88f91f2f --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/dto/refund/request/ProductRefundRequestDeniedRequest.java @@ -0,0 +1,5 @@ +package poomasi.domain.order._aftersales.dto.refund.request; + +public record ProductRefundRequestDeniedRequest(Long productAfterSalesDetailId, + String refundDeinedReason) { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/refund/response/ProductRefundRequestApprovalResponse.java b/src/main/java/poomasi/domain/order/_aftersales/dto/refund/response/ProductRefundRequestApprovalResponse.java new file mode 100644 index 00000000..1e1bc6bd --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/dto/refund/response/ProductRefundRequestApprovalResponse.java @@ -0,0 +1,12 @@ +package poomasi.domain.order._aftersales.dto.refund.response; + +import java.math.BigDecimal; + +public record ProductRefundRequestApprovalResponse( + Long orderedProductId, + Integer count, + BigDecimal refundAmount, + Long productAfterSalesDetailId, + String invoiceNumber +) { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/refund/response/ProductRefundRequestDeniedResponse.java b/src/main/java/poomasi/domain/order/_aftersales/dto/refund/response/ProductRefundRequestDeniedResponse.java new file mode 100644 index 00000000..109d320f --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/dto/refund/response/ProductRefundRequestDeniedResponse.java @@ -0,0 +1,8 @@ +package poomasi.domain.order._aftersales.dto.refund.response; + +import poomasi.domain.order._aftersales.entity._product.ProductAfterSalesStatus; + +public record ProductRefundRequestDeniedResponse(Long productAfterSalesDetailId, + ProductAfterSalesStatus productAfterSalesStatus, + String productRefundDeniedReason) { +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/dto/refund/response/ProductRefundRequestResponse.java b/src/main/java/poomasi/domain/order/_aftersales/dto/refund/response/ProductRefundRequestResponse.java new file mode 100644 index 00000000..d5c3e26a --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/dto/refund/response/ProductRefundRequestResponse.java @@ -0,0 +1,19 @@ +package poomasi.domain.order._aftersales.dto.refund.response; + +import poomasi.domain.order._aftersales.entity._product.ProductAfterSalesStatus; +import poomasi.domain.order.entity._product.OrderedProductStatus; + +import java.math.BigDecimal; + +public record ProductRefundRequestResponse( + Long orderedProductId, + OrderedProductStatus orderedProductStatus, + + Long productAfterSalesDetailId, + Integer refundQuantity, + ProductAfterSalesStatus productAfterSalesTypem, + BigDecimal finalRefundAmount +) { +} + + diff --git a/src/main/java/poomasi/domain/order/dto/response/OrderDetailsResponse.java b/src/main/java/poomasi/domain/order/dto/response/OrderDetailsResponse.java index e3002a7c..749f9dab 100644 --- a/src/main/java/poomasi/domain/order/dto/response/OrderDetailsResponse.java +++ b/src/main/java/poomasi/domain/order/dto/response/OrderDetailsResponse.java @@ -9,8 +9,8 @@ public record OrderDetailsResponse( ) { public static OrderDetailsResponse fromEntity(ProductOrderDetails productOrderDetails) { return new OrderDetailsResponse( - productOrderDetails.getAddress(), - productOrderDetails.getAddressDetail(), + productOrderDetails.getDestinationAddress(), + productOrderDetails.getDestinationAddressDetail(), productOrderDetails.getDeliveryRequest() ); } From 93820a489891d983a999be5ab66675a4c6869965 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Mon, 11 Nov 2024 22:53:34 +0900 Subject: [PATCH 54/59] =?UTF-8?q?feat=20:=20after=20sales=20service?= =?UTF-8?q?=EB=A5=BC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_product/ProductAfterSalesStatus.java | 22 ++ .../service/FarmAfterSalesService.java | 14 + .../service/ProductAfterSalesService.java | 328 ++++++++++++++++-- 3 files changed, 336 insertions(+), 28 deletions(-) create mode 100644 src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesStatus.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/service/FarmAfterSalesService.java diff --git a/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesStatus.java b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesStatus.java new file mode 100644 index 00000000..5a0ab449 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/entity/_product/ProductAfterSalesStatus.java @@ -0,0 +1,22 @@ +package poomasi.domain.order._aftersales.entity._product; + +public enum ProductAfterSalesStatus { + EXCHANGE, + CANCEL, + REFUND, + + + //환불 + REFUND_REQUESTED, // 환불 요청됨 + REFUND_APPROVED, // 환불 승인됨 + REFUND_SHIPMENT_STARTED, // 환불 배송 시작 (반품 물품의 배송 시작) + REFUND_IN_TRANSIT, // 환불 배송 중 (반품 물품이 배송 중) + REFUND_DELIVERED, // 환불 배송 완료 (반품 물품이 도착함) + REFUND_IN_PROGRESS, // 환불 처리 중 (반품 수거 중이거나 처리 대기 중) + REFUND_COMPLETED, // 환불 완료 + REFUND_DENIED // 환불 요청 거절됨 + + + ; + +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/service/FarmAfterSalesService.java b/src/main/java/poomasi/domain/order/_aftersales/service/FarmAfterSalesService.java new file mode 100644 index 00000000..df44b4bf --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/service/FarmAfterSalesService.java @@ -0,0 +1,14 @@ +package poomasi.domain.order._aftersales.service; + + +import org.springframework.stereotype.Service; +import poomasi.domain.order._aftersales.dto.cancel.request.FarmCancelRequest; + +@Service +public class FarmAfterSalesService { + + public String farmCancel(FarmCancelRequest farmCancelRequest){ + return "success!"; + } + +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/service/ProductAfterSalesService.java b/src/main/java/poomasi/domain/order/_aftersales/service/ProductAfterSalesService.java index c443d9a7..2e938c56 100644 --- a/src/main/java/poomasi/domain/order/_aftersales/service/ProductAfterSalesService.java +++ b/src/main/java/poomasi/domain/order/_aftersales/service/ProductAfterSalesService.java @@ -7,76 +7,358 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import poomasi.domain.auth.security.userdetail.UserDetailsImpl; import poomasi.domain.member.entity.Member; import poomasi.domain.order._aftersales.dto.cancel.request.ProductCancelRequest; -import poomasi.domain.order._aftersales.dto.exchange.request.ProductExchangeRequest; import poomasi.domain.order._aftersales.dto.cancel.response.ProductCancelResponse; -import poomasi.domain.order._aftersales.dto.exchange.response.ProductExchangeResponse; +import poomasi.domain.order._aftersales.dto.refund.request.ProductRefundRequest; +import poomasi.domain.order._aftersales.dto.refund.request.ProductRefundRequestApprovalRequest; +import poomasi.domain.order._aftersales.dto.refund.request.ProductRefundRequestDeniedRequest; +import poomasi.domain.order._aftersales.dto.refund.response.ProductRefundRequestApprovalResponse; +import poomasi.domain.order._aftersales.dto.refund.response.ProductRefundRequestDeniedResponse; +import poomasi.domain.order._aftersales.dto.refund.response.ProductRefundRequestResponse; import poomasi.domain.order._aftersales.entity._product.ProductAfterSalesDetail; +import poomasi.domain.order._aftersales.entity._product.ProductAfterSalesStatus; +import poomasi.domain.order._aftersales.entity._product.ProductRefundDetail; import poomasi.domain.order._aftersales.repository.ProductAfterSalesDetailRepository; +import poomasi.domain.order._aftersales.repository.ProductRefundDetailRepository; import poomasi.domain.order._payment.util.PaymentUtil; import poomasi.domain.order.entity._product.OrderedProduct; import poomasi.domain.order.entity._product.ProductOrder; import poomasi.domain.order.entity._product.OrderedProductStatus; +import poomasi.domain.order.entity._product.ProductOrderDetails; import poomasi.domain.order.repository.OrderedProductRepository; import poomasi.global.error.BusinessException; import java.io.IOException; import java.math.BigDecimal; -import static poomasi.domain.order._aftersales.entity._product.ProductAfterSalesType.CANCEL; +import static poomasi.domain.order._aftersales.entity._product.ProductAfterSalesStatus.CANCEL; import static poomasi.domain.order.entity._product.OrderedProductStatus.*; import static poomasi.global.error.BusinessError.*; @Service @RequiredArgsConstructor -public class ProductAfterSalesService { +public class ProductAfterSalesService implements CancelService{ private final OrderedProductRepository orderedProductRepository; private final PaymentUtil paymentUtil; private final ProductAfterSalesDetailRepository productAfterSalesDetailRepository; + private final ProductRefundDetailRepository productRefundDetailRepository; //-------------------------cancel---------------------// - @Description("판매자 확인 전 취소하는 메서드. 판매자 확인 대기 전 경우만 취소 할 수 있음") - public ProductCancelResponse productCancelBeforeSellerApproval(ProductCancelRequest productCancelRequest) throws IOException, IamportResponseException { + @Override + @Transactional + public ProductCancelResponse cancel(ProductCancelRequest productCancelRequest) throws IOException, IamportResponseException { Long orderedProductId = productCancelRequest.orderedProductId(); - OrderedProduct orderedProduct = validateProductRequestByMemberId(orderedProductId); + String cancelReason = productCancelRequest.cancelReason(); + + //주인 검증 - 유저의 orderedProductId가 맞는지 검증 + OrderedProduct orderedProduct = validateProductCancelRequestByMemberId(orderedProductId); + + //수량 검증 + Integer cancelRequestQuantity = productCancelRequest.cancelRequestQuantity(); + Integer adjustableQuantity = orderedProduct.getAdjustableQuantity(); + if(cancelRequestQuantity > adjustableQuantity){ + throw new BusinessException(CANCEL_QUANTITY_EXCEEDED); + } + + //포트원 취소를 위한 주문 Id 찾기 ProductOrder productOrder = orderedProduct.getProductOrder(); String impUid = productOrder.getImpUid(); - BigDecimal amoutToBeCancel = orderedProduct.getPrice(); - String cancelReason = productCancelRequest.reason(); + + //판매자 확인 대기 전이 아니라면 주문 취소를 할 수 없다 OrderedProductStatus orderedProductStatus = orderedProduct.getOrderedProductStatus(); - - //판매자 확인 대기 전이 아니라면 (잘못 된 요청) if(orderedProductStatus != PENDING_SELLER_APPROVAL){ throw new BusinessException(SHIPPING_ALREADY_IN_PROGRESS); } + + //최종 취소 될 금액 계산 -> 배송비는 처음 한 번 환불 + BigDecimal finalCancelAmount = calculateCancelAmount(orderedProduct, cancelRequestQuantity); + + //배송비 환불 플래그 설정 - //취소 요청 후 - paymentUtil.cancelPaymentByImpUid(impUid); + // checksum 검증 + BigDecimal checkSum = productOrder.getCheckSum(); + + //취소하려는 금액이 남은 환불 가능한 금액보다 크다면 + if(finalCancelAmount.compareTo(checkSum) > 0){ + throw new BusinessException(CHECKSUM_EXCESSIVE_REFUND_AMOUNT); + } + + //취소 요청 후, 주문 취소 상태로 변경 + paymentUtil.partialRefundByImpUid(impUid, checkSum, finalCancelAmount, cancelReason); + //orderedProduct.setOrderedProductStatus(CANCEL_PENDING); + + //checksum 뺴기 : 주문 취소가 정상적으로 완료가 되었다면 동기화 + productOrder.subtractChecksum(finalCancelAmount); - //주문 취소 상태로 변경 - orderedProduct.setOrderedProductStatus(CANCELLED); + //취소/환불/교환 가능 수량 변경 및 플래그 설정 + orderedProduct.subtractRefundableCount(cancelRequestQuantity); + orderedProduct.addCancelQuantity(cancelRequestQuantity); + //모두 취소 해버렸다면 + orderedProductStatus = orderedProduct.changeOrderedProductStatusToCancel(); + + //TODO : 취소 된 수량도 추가해야 하나? 오늘 회의에서 결정함 + //취소 된 상품 수량 증가 + orderedProduct.getProduct().addStock(cancelRequestQuantity); + //취소 내역 저장 ProductAfterSalesDetail productAfterSalesDetail = new ProductAfterSalesDetail() .builder() .orderedProduct(orderedProduct) - .amount(amoutToBeCancel) + .adjustAmount(finalCancelAmount) .reason(cancelReason) - .productAfterSalesType(CANCEL) + .adjustmentQuantity(cancelRequestQuantity) + .productAfterSalesStatus(CANCEL) .build(); + orderedProduct.addProductAfterSalesDetail(productAfterSalesDetail); + productAfterSalesDetailRepository.save(productAfterSalesDetail); + + //응답 반환 + return new ProductCancelResponse(orderedProductId, + orderedProductStatus, + productAfterSalesDetail.getId(), + cancelRequestQuantity, + productAfterSalesDetail.getProductAfterSalesStatus(), + finalCancelAmount + ); + + } + + @Description("요청이 구매자 소유인지 확인하는 메서드") + private OrderedProduct validateProductCancelRequestByMemberId(Long orderedProductId){ + Member member = getMember(); + Long memberId = getMember().getId(); + OrderedProduct orderedProduct = orderedProductRepository.findById(orderedProductId) + .orElseThrow(()-> new BusinessException(ORDERED_PRODUCT_NOT_FOUND)); + + if(orderedProduct.getProductOrder().getMember().getId() != memberId){ + new BusinessException(ORDERED_PRODUCT_NOT_FOUND); // TODO : 메서드 추출 이후, error enum 변경 + } + + return orderedProduct; + } + + + @Description("취소 요청에서 취소 금액 계산하는 메서드. 취소 전적이 한 번이라도 있으면 배송비 환불 x.") + private BigDecimal calculateCancelAmount(OrderedProduct orderedProduct, Integer cancelRequestQuantity){ + + boolean isCanceled = orderedProduct.isCanceled(); + + BigDecimal cancelAmount = orderedProduct.getPrice() + .multiply(new BigDecimal(cancelRequestQuantity) + ); + + if(!isCanceled){ + //배송비 붙여야 한다 + BigDecimal deliveryFee = orderedProduct.getDeliveryFee(); + cancelAmount = cancelAmount.add(deliveryFee); + } + return cancelAmount; + } + + + //-------------------------refund---------------------// + @Description("환불 요청하는 메서드") + @Transactional + public ProductRefundRequestResponse createRefundRequest(ProductRefundRequest productRefundRequest) { + Long orderedProductId = productRefundRequest.orderedProductId(); + String refundReason = productRefundRequest.refundReason(); + + // 주인 검증 - 유저의 orderedProductId가 맞는지 검증 + OrderedProduct orderedProduct = validateProductCancelRequestByMemberId(orderedProductId); + + // 수량 검증 - 조정 가능한 수량보다 환불 수량이 많으면 exception + Integer refundRequestQuantity = productRefundRequest.refundRequestQuantity(); + Integer adjustableQuantity = orderedProduct.getAdjustableQuantity(); + if(refundRequestQuantity > adjustableQuantity){ + throw new BusinessException(REFUND_QUANTITY_EXCEEDED); + } + + //포트원 취소를 위한 주문 Id 찾기 + ProductOrder productOrder = orderedProduct.getProductOrder(); + String impUid = productOrder.getImpUid(); + + //구매 확정 상태라면 환불을 할 수 없다 + OrderedProductStatus orderedProductStatus = orderedProduct.getOrderedProductStatus(); + if(orderedProductStatus == DELIVERED){ + throw new BusinessException(PURCHASE_ALREADY_CONFIRMED); + } + + // 배송 대기 전 상태라면 환불을 할 수 없다. + if(orderedProductStatus == PENDING_SELLER_APPROVAL){ + throw new BusinessException(REFUND_NOT_ALLOWED_BEFORE_SHIPPING); + } + + //최종 환불 금액 계산 + BigDecimal finalRefundAmount = calculateRefundAmount(orderedProduct, refundRequestQuantity); + + // checksum 검증 + BigDecimal checkSum = productOrder.getCheckSum(); + + //취소하려는 금액이 남은 환불 가능한 금액보다 크다면 + if(finalRefundAmount.compareTo(checkSum) > 0){ + throw new BusinessException(CHECKSUM_EXCESSIVE_REFUND_AMOUNT); + } + + //취소/환불/교환 가능 수량 변경 + orderedProduct.subtractRefundableCount(refundRequestQuantity); + //환불 내역 저장 + ProductAfterSalesDetail productAfterSalesDetail = new ProductAfterSalesDetail() + .builder() + .orderedProduct(orderedProduct) + .adjustAmount(finalRefundAmount) + .reason(refundReason) + .adjustmentQuantity(refundRequestQuantity) + .productAfterSalesStatus(ProductAfterSalesStatus.REFUND_REQUESTED) + .build(); + + //환불 상세 저장 + ProductRefundDetail productRefundDetail = createProductRefundDetail(orderedProduct, productOrder, productRefundRequest); + productAfterSalesDetail.setProductRefundDetail(productRefundDetail); orderedProduct.addProductAfterSalesDetail(productAfterSalesDetail); + productAfterSalesDetailRepository.save(productAfterSalesDetail); + productRefundDetailRepository.save(productRefundDetail); + + //응답 반환 + return new ProductRefundRequestResponse( + orderedProductId, + orderedProductStatus, + productAfterSalesDetail.getId(), + refundRequestQuantity, + productAfterSalesDetail.getProductAfterSalesStatus(), + finalRefundAmount + ); + } + + @Description("반품 상세 요청사항 만드는 메서드") + private ProductRefundDetail createProductRefundDetail(OrderedProduct orderedProduct, + ProductOrder productOrder, + ProductRefundRequest productRefundRequest) { + + ProductOrderDetails productOrderDetails = productOrder.getProductOrderDetails(); + String pickupLocationAddress = productOrderDetails.getDestinationAddress(); + String pickUpLocationAddressDetail = productOrderDetails.getDestinationAddressDetail(); + + String returnAddress = orderedProduct.getStoreAddress(); + String returnAddressDetail = orderedProduct.getStoreAddressDetail(); + + String request = productRefundRequest.request(); //nullable field + + ProductRefundDetail productRefundDetail = new ProductRefundDetail() + .builder() + .pickupLocationAddress(pickupLocationAddress) + .pickupLocationAddressDetail(pickUpLocationAddressDetail) + .returnAddress(returnAddress) + .returnAddressDetail(returnAddressDetail) + .request(request) + .build(); + + return productRefundDetail; + } + + + @Description("환불 요청에서 환불 금액 계산하는 메서드") + private BigDecimal calculateRefundAmount(OrderedProduct orderedProduct, Integer refundRequestQuantity){ + + BigDecimal refundAmount = orderedProduct.getPrice() + .multiply(new BigDecimal(refundRequestQuantity)); + + return refundAmount; + } + + + @Description("판매자 환불 거절 메서드") + @Transactional + public ProductRefundRequestDeniedResponse processRefundDenied(ProductRefundRequestDeniedRequest productRefundRequestDeniedRequest){ + + Long productAfterSalesDetailId = productRefundRequestDeniedRequest.productAfterSalesDetailId(); + String refundDeinedReason = productRefundRequestDeniedRequest.refundDeinedReason(); + + //환불 요청이 존재하는지 그리고 자신의 환불 요청인지 검증하고 + ProductAfterSalesDetail productAfterSalesDetail = validateProductRefundRequestByFarmerId(productAfterSalesDetailId); + + //refund detail 만든 후 + ProductRefundDetail productRefundDetail = productAfterSalesDetail.getProductRefundDetail(); + productRefundDetail.setProductRefundDeniedReason(refundDeinedReason); + + //환불 거부 상태로 등록 후 + productAfterSalesDetail.setProductAfterSalesStatus(ProductAfterSalesStatus.REFUND_DENIED); + + productAfterSalesDetail.setProductRefundDetail(productRefundDetail); + //db에 저장한다 + productRefundDetailRepository.save(productRefundDetail); + + //전달한다 + return new ProductRefundRequestDeniedResponse( + productAfterSalesDetail.getId(), + productAfterSalesDetail.getProductAfterSalesStatus(), + productAfterSalesDetail.getProductRefundDeniedReason() + ); + } + + @Description("판매자 환불 확인 메서드") + public ProductRefundRequestApprovalResponse processRefundApproval(ProductRefundRequestApprovalRequest productRefundRequestApprovalRequest) throws IOException, IamportResponseException { + + Long productAfterSalesDetailId = productRefundRequestApprovalRequest.productAfterSalesDetailId(); + String invoiceNumber = productRefundRequestApprovalRequest.invoiceNumber(); + + //환불 요청이 존재하는지 그리고 자신의 환불 요청인지 검증하고 + ProductAfterSalesDetail productAfterSalesDetail = validateProductRefundRequestByFarmerId(productAfterSalesDetailId); + + //환불 결제 금액 찾고 + BigDecimal finalRefundAmount = productAfterSalesDetail.getAdjustAmount(); + + //결제를 찾는다 + OrderedProduct orderedProduct = productAfterSalesDetail.getOrderedProduct(); + ProductOrder productOrder = orderedProduct.getProductOrder(); + + //환불에 필요한 parameter + String refundReason = productAfterSalesDetail.getReason(); + String impUid = productOrder.getImpUid(); + BigDecimal checkSum = productOrder.getCheckSum(); + + //환불 처리 + paymentUtil.partialRefundByImpUid(impUid, checkSum, finalRefundAmount, refundReason); + + //성공하면 checksum 포트원 서버와 동기화 + productOrder.subtractChecksum(finalRefundAmount); + + //운송장 번호와 상태 등록 + productAfterSalesDetail.changeRefundApproveStatus(invoiceNumber); + + return new ProductRefundRequestApprovalResponse( + orderedProduct.getId(), + productAfterSalesDetail.getAdjustmentQuantity(), + finalRefundAmount, + productAfterSalesDetailId, + invoiceNumber + ); - return new ProductCancelResponse(orderedProductId, orderedProductStatus); } + @Description("환불 요청이 존재하고, 판매자 소유인지 확인하는 메서드 ") + private ProductAfterSalesDetail validateProductRefundRequestByFarmerId(Long productAfterSalesDetailId){ + ProductAfterSalesDetail productAfterSalesDetail = productAfterSalesDetailRepository.findById(productAfterSalesDetailId) + .orElseThrow(()-> new BusinessException(REFUND_AFTER_SALES_NOT_FOUND) + ); + Long farmerId = getMember().getId(); + /* + if(farmerId != productAfterSalesDetail.getOrderedProduct().getStoreId().getMember()){ + throw new BusinessException(REFUND_AFTER_SALES_REQUEST_INVALID_OWNER); + } + */ + return productAfterSalesDetail; + } + // ------------------------------// @Description("security context에서 member 객체 가져오는 메서드") private Member getMember() { Authentication authentication = SecurityContextHolder @@ -86,14 +368,4 @@ private Member getMember() { return member; } - @Description("요청이 멤버 소유인지 확인하는 메서드") - private OrderedProduct validateProductRequestByMemberId(Long orderedProductId){ - Member member = getMember(); - Long memberId = getMember().getId(); - OrderedProduct orderedProduct = orderedProductRepository.findByIdAndMemberId(orderedProductId, memberId) - .orElseThrow(()-> new BusinessException(ORDERED_PRODUCT_NOT_FOUND)); - return orderedProduct; - } - - } From c8e5c3647651d466e340d28ce99e303aacb86e02 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Mon, 11 Nov 2024 22:54:05 +0900 Subject: [PATCH 55/59] =?UTF-8?q?feat=20:=20=EC=83=81=ED=92=88=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20dto=20=EB=B0=8F=20after=20sales=20repository=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ProductAfterSalesDetailRepository.java | 11 +++++++++++ .../repository/ProductRefundDetailRepository.java | 9 +++++++++ .../dto/request/ProductOrderRegisterRequest.java | 6 ++++++ 3 files changed, 26 insertions(+) create mode 100644 src/main/java/poomasi/domain/order/_aftersales/repository/ProductAfterSalesDetailRepository.java create mode 100644 src/main/java/poomasi/domain/order/_aftersales/repository/ProductRefundDetailRepository.java create mode 100644 src/main/java/poomasi/domain/order/dto/request/ProductOrderRegisterRequest.java diff --git a/src/main/java/poomasi/domain/order/_aftersales/repository/ProductAfterSalesDetailRepository.java b/src/main/java/poomasi/domain/order/_aftersales/repository/ProductAfterSalesDetailRepository.java new file mode 100644 index 00000000..e7b53e82 --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/repository/ProductAfterSalesDetailRepository.java @@ -0,0 +1,11 @@ +package poomasi.domain.order._aftersales.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import poomasi.domain.order._aftersales.entity._product.ProductAfterSalesDetail; + +@Repository +public interface ProductAfterSalesDetailRepository extends JpaRepository { + + +} diff --git a/src/main/java/poomasi/domain/order/_aftersales/repository/ProductRefundDetailRepository.java b/src/main/java/poomasi/domain/order/_aftersales/repository/ProductRefundDetailRepository.java new file mode 100644 index 00000000..3bd40c3d --- /dev/null +++ b/src/main/java/poomasi/domain/order/_aftersales/repository/ProductRefundDetailRepository.java @@ -0,0 +1,9 @@ +package poomasi.domain.order._aftersales.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import poomasi.domain.order._aftersales.entity._product.ProductRefundDetail; + +@Repository +public interface ProductRefundDetailRepository extends JpaRepository { +} diff --git a/src/main/java/poomasi/domain/order/dto/request/ProductOrderRegisterRequest.java b/src/main/java/poomasi/domain/order/dto/request/ProductOrderRegisterRequest.java new file mode 100644 index 00000000..55b5d106 --- /dev/null +++ b/src/main/java/poomasi/domain/order/dto/request/ProductOrderRegisterRequest.java @@ -0,0 +1,6 @@ +package poomasi.domain.order.dto.request; + +public record ProductOrderRegisterRequest(String destinationAddress, + String destinationAddressDetail, + String deliveryRequest) { +} From 746104be38904ec6b41df81b86fd8f8285eb702e Mon Sep 17 00:00:00 2001 From: amm0124 Date: Mon, 11 Nov 2024 22:55:01 +0900 Subject: [PATCH 56/59] =?UTF-8?q?feat=20:=20farm=20order=20service=20?= =?UTF-8?q?=EB=B0=8F=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20-=20=EC=83=81=EC=84=B8=20=EB=82=B4?= =?UTF-8?q?=EC=9A=A9=EC=9D=80=20=EB=82=B4=EC=9D=BC=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=ED=95=98=EA=B2=A0=EC=8A=B5=EB=8B=88=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../order/service/FarmOrderService.java | 8 + .../domain/order/service/OrderService.java | 215 +----------------- 2 files changed, 9 insertions(+), 214 deletions(-) create mode 100644 src/main/java/poomasi/domain/order/service/FarmOrderService.java diff --git a/src/main/java/poomasi/domain/order/service/FarmOrderService.java b/src/main/java/poomasi/domain/order/service/FarmOrderService.java new file mode 100644 index 00000000..825369cc --- /dev/null +++ b/src/main/java/poomasi/domain/order/service/FarmOrderService.java @@ -0,0 +1,8 @@ +package poomasi.domain.order.service; + + +import org.springframework.stereotype.Service; + +@Service +public class FarmOrderService implements OrderService { +} diff --git a/src/main/java/poomasi/domain/order/service/OrderService.java b/src/main/java/poomasi/domain/order/service/OrderService.java index 4d34b046..1f89f786 100644 --- a/src/main/java/poomasi/domain/order/service/OrderService.java +++ b/src/main/java/poomasi/domain/order/service/OrderService.java @@ -1,218 +1,5 @@ package poomasi.domain.order.service; -import jdk.jfr.Description; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import poomasi.domain.auth.security.userdetail.UserDetailsImpl; -import poomasi.domain.member.entity.Member; -import poomasi.domain.order._payment.dto.request.PaymentPreRegisterRequest; -import poomasi.domain.order.dto.request.OrderRegisterRequest; -import poomasi.domain.order.dto.response.OrderDetailsResponse; -import poomasi.domain.order.dto.response.OrderProductDetailsResponse; -import poomasi.domain.order.dto.response.OrderResponse; -import poomasi.domain.order.entity._product.OrderedProduct; -import poomasi.domain.order.entity._product.ProductOrder; -import poomasi.domain.order.entity._product.ProductOrderDetails; -import poomasi.domain.order.entity.OrderStatus; -import poomasi.domain.order.repository.OrderedProductRepository; -import poomasi.domain.order.repository.ProductOrderRepository; -import poomasi.domain.product._cart.entity.Cart; -import poomasi.domain.product._cart.repository.CartRepository; -import poomasi.domain.product.entity.Product; -import poomasi.domain.product.repository.ProductRepository; -import poomasi.global.error.BusinessException; - -import java.math.BigDecimal; -import java.util.List; -import java.util.stream.Collectors; - -import static poomasi.global.error.BusinessError.*; - -@RequiredArgsConstructor -@Service -@Slf4j -public class OrderService { - - private final ProductOrderRepository productOrderRepository; - private final CartRepository cartRepository; - private final ProductRepository productRepository; - private final OrderedProductRepository orderedProductRepository; - - @Transactional - public PaymentPreRegisterRequest productPreOrderRegister(OrderRegisterRequest orderRegisterRequest){ - Member member = getMember(); - Long memberId = member.getId(); - List cartList = cartRepository.findByMemberIdAndSelected(memberId); - - String address = orderRegisterRequest.address(); - String addressDetails = orderRegisterRequest.addressDetails(); - String deliveryRequest = orderRegisterRequest.deliveryRequest(); - - ProductOrder productOrder = new ProductOrder( - new ProductOrderDetails(address, - addressDetails, - deliveryRequest) - ); - - //cart에 있는 총 가격 계산하기 - BigDecimal totalPrice = BigDecimal.ZERO; - - // cart 돌면서 productOrder details 추가 - for (Cart cart : cartList) { - Long productId = cart.getProductId(); - Product product = productRepository.findById(productId) - .orElseThrow(() -> new BusinessException(PRODUCT_NOT_FOUND)); - - Integer productStock = product.getStock(); - Integer quantityInCart = cart.getCount(); - - // 현재 남아있는 재고보다 더 많이 요청하면 - if(quantityInCart > productStock){ - throw new BusinessException(PRODUCT_STOCK_ZERO); - } - - String productDescription = product.getDescription(); - Integer count = cart.getCount(); - String productName = product.getName(); - BigDecimal price = BigDecimal.valueOf(product.getPrice()); - OrderedProduct orderedProduct = OrderedProduct - .builder() - .product(product) - .productOrder(productOrder) - .productDescription(productDescription) - .productName(productName) - .price(price) - .count(count) - .build(); - productOrder.addOrderedProduct(orderedProduct); - totalPrice = totalPrice.add(price); - } - productOrder.setTotalAmount(totalPrice); - productOrder.setCheckSum(totalPrice); - productOrderRepository.save(productOrder); - - String merchantUid = productOrder.getMerchantUid(); - return new PaymentPreRegisterRequest(merchantUid, totalPrice); - } - - @Transactional - //TODO : 만들어야 합니다 ~ - public PaymentPreRegisterRequest farmPreOrderRegister(){ - Member member = getMember(); - String merchantUid = ""; - BigDecimal totalPrice = BigDecimal.ZERO; - - return new PaymentPreRegisterRequest(merchantUid, totalPrice); - } - - - @Description("멤버 ID 기반으로 모든 order 다 들고 오는 메서드") - public List findAllOrdersByMemberId(){ - Member member = getMember(); - Long memberId = member.getId(); - List productOrderList = productOrderRepository.findByMemberId(memberId); - return productOrderList - .stream() - .map(OrderResponse::fromEntity) - .collect(Collectors.toList() - ); - } - - @Description("멤버 id 기반으로 특정 orderId 들고오는 메서드") - public OrderResponse findOrderByMemberId(Long orderId){ - Member member = getMember(); - ProductOrder productOrder = productOrderRepository.findById(orderId) - .orElseThrow(()-> new BusinessException(ORDER_NOT_FOUND)); - - validateOrderOwnership(productOrder, member); - return OrderResponse.fromEntity(productOrder); - } - - - @Description("orderId 기반으로 order details(주소, 상세주소, 배송 요청 사항 ..등) 들고오는 메서드") - public OrderDetailsResponse findOrderDetailsByOrderId(Long orderId){ - ProductOrder productOrder = productOrderRepository.findById(orderId) - .orElseThrow(()-> new BusinessException(ORDER_NOT_FOUND)); - ProductOrderDetails productOrderDetails = productOrder.getProductOrderDetails(); - - return OrderDetailsResponse.fromEntity(productOrderDetails); - } - - - - @Description("orderId에 해당하는 order product details 가져오는 메서드") - public List findAllOrderProductDetails(Long orderId){ - Member member = getMember(); - ProductOrder productOrder = productOrderRepository.findById(orderId) - .orElseThrow(()-> new BusinessException(ORDER_NOT_FOUND)); - validateOrderOwnership(productOrder, member); - return productOrder.getOrderedProducts() - .stream() - .map(OrderProductDetailsResponse::fromEntity) - .collect(Collectors.toList() - ); - } - - - @Description("orderId에 해당하는 order product Details의 단건 조회") - public OrderProductDetailsResponse findOrderProductDetailsById(Long orderId, Long orderProductDetailsId){ - Member member = getMember(); - OrderedProduct orderedProduct = orderedProductRepository.findById(orderProductDetailsId) - .orElseThrow(()-> new BusinessException(ORDER_PRODUCT_DETAILS_NOT_FOUND)); - ProductOrder productOrder = orderedProduct.getProductOrder(); - - // productOrder product details의 주인 productOrder 검사 그리고 , orderId의 주인 member 검사 - validateOrderProductDetailsByOrderId(productOrder, orderId); - validateOrderOwnership(productOrder, member); - - return OrderProductDetailsResponse.fromEntity(orderedProduct); - } - - - @Description("member의 order인지 검사하는 메서드") - private void validateOrderOwnership(ProductOrder productOrder, Member member) { - if (!productOrder.getMember().getId().equals(member.getId())) { - throw new BusinessException(ORDER_NOT_OWNED_EXCEPTION); - } - } +public interface OrderService { - @Description("orderId에 해당하는 productOrder Product Details인지 조회하는 메서드") - private void validateOrderProductDetailsByOrderId(ProductOrder productOrder, Long orderId) { - if(productOrder.getId()!=orderId){ - throw new BusinessException(ORDER_PRODUCT_DETAILS_NOT_OWNED_EXCEPTION); - } - } - - - /* - @Description("결제가 완료 된 후, 주문 상태 변경하는 메서드. 굳이 없어도 되긴 함.") - private void completePaymentAndUpdateStatus(Long orderId){ - ProductOrder order = productOrderRepository.findById(orderId) - .orElseThrow(()-> new BusinessException(ORDER_NOT_FOUND)); - order.setOrderStatus(AWAITING_SELLER_CONFIRMATION); - }*/ - - @Description("주문 상태를 변경하는 메서드") - private void changeOrderStatus(Long orderId, OrderStatus orderStatus){ - ProductOrder productOrder = productOrderRepository.findById(orderId) - .orElseThrow(()-> new BusinessException(ORDER_NOT_FOUND)); - productOrder.setOrderStatus(orderStatus); - } - - - @Description("security context에서 member 객체 가져오는 메서드") - private Member getMember() { - Authentication authentication = SecurityContextHolder - .getContext().getAuthentication(); - Object impl = authentication.getPrincipal(); - Member member = ((UserDetailsImpl) impl).getMember(); - return member; - } - } - - From 058071ef6d075d8271c1cc6cbfd0553dbc5448a3 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Mon, 11 Nov 2024 22:55:17 +0900 Subject: [PATCH 57/59] =?UTF-8?q?refactoring=20:=20product=20order=20repos?= =?UTF-8?q?itory=EC=99=80=20service=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ProductOrderRepository.java | 5 +- .../order/service/ProductOrderService.java | 224 ++++++++++++++++++ 2 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 src/main/java/poomasi/domain/order/service/ProductOrderService.java diff --git a/src/main/java/poomasi/domain/order/repository/ProductOrderRepository.java b/src/main/java/poomasi/domain/order/repository/ProductOrderRepository.java index 9f0ba5a1..c439df8a 100644 --- a/src/main/java/poomasi/domain/order/repository/ProductOrderRepository.java +++ b/src/main/java/poomasi/domain/order/repository/ProductOrderRepository.java @@ -8,8 +8,7 @@ public interface ProductOrderRepository extends JpaRepository { List findByMemberId(Long memberId); - //List findById(Long id); Optional findByMerchantUid(String merchantUid); - Optional findByImpUid(String impUid); - Optional findByMerchantUidAndImpUid(String merchantUid, String impUid); + //Optional findByImpUid(String impUid); + //Optional findByMerchantUidAndImpUid(String merchantUid, String impUid); } diff --git a/src/main/java/poomasi/domain/order/service/ProductOrderService.java b/src/main/java/poomasi/domain/order/service/ProductOrderService.java new file mode 100644 index 00000000..5edd4359 --- /dev/null +++ b/src/main/java/poomasi/domain/order/service/ProductOrderService.java @@ -0,0 +1,224 @@ +package poomasi.domain.order.service; + +import jdk.jfr.Description; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import poomasi.domain.auth.security.userdetail.UserDetailsImpl; +import poomasi.domain.member.entity.Member; +import poomasi.domain.order._payment.dto.request.PaymentPreRegisterRequest; +import poomasi.domain.order.dto.request.ProductOrderRegisterRequest; +import poomasi.domain.order.dto.response.OrderDetailsResponse; +import poomasi.domain.order.dto.response.OrderProductDetailsResponse; +import poomasi.domain.order.dto.response.OrderResponse; +import poomasi.domain.order.entity._product.OrderedProduct; +import poomasi.domain.order.entity._product.ProductOrder; +import poomasi.domain.order.entity._product.ProductOrderDetails; +import poomasi.domain.order.entity.PaymentStatus; +import poomasi.domain.order.repository.OrderedProductRepository; +import poomasi.domain.order.repository.ProductOrderRepository; +import poomasi.domain.product._cart.entity.Cart; +import poomasi.domain.product._cart.repository.CartRepository; +import poomasi.domain.product.entity.Product; +import poomasi.domain.product.repository.ProductRepository; +import poomasi.global.error.BusinessException; + +import java.math.BigDecimal; +import java.util.List; +import java.util.stream.Collectors; + +import static poomasi.global.error.BusinessError.*; + +@RequiredArgsConstructor +@Service +@Slf4j +public class ProductOrderService { + + private final ProductOrderRepository productOrderRepository; + private final CartRepository cartRepository; + private final ProductRepository productRepository; + private final OrderedProductRepository orderedProductRepository; + + @Transactional + public PaymentPreRegisterRequest productPreOrderRegister(ProductOrderRegisterRequest productOrderRegisterRequest){ + Member member = getMember(); + Long memberId = member.getId(); + List cartList = cartRepository.findByMemberIdAndSelected(memberId); + + String destinationAddress = productOrderRegisterRequest.destinationAddress(); + String destinationAddressDetail = productOrderRegisterRequest.destinationAddressDetail(); + String deliveryRequest = productOrderRegisterRequest.deliveryRequest(); + + ProductOrder productOrder = new ProductOrder() + .builder() + .member(member) + .build(); + + ProductOrderDetails productOrderDetails = new ProductOrderDetails() + .builder() + .destinationAddress(destinationAddress) + .destinationAddressDetail(destinationAddressDetail) + .deliveryRequest(deliveryRequest) + .build(); + + productOrder.setProductOrderDetails(productOrderDetails); + + //cart에 있는 총 가격 계산하기 + BigDecimal totalPrice = BigDecimal.ZERO; + + // cart 돌면서 productOrder details 추가 + for (Cart cart : cartList) { + Long productId = cart.getProductId(); + Product product = productRepository.findById(productId) + .orElseThrow(() -> new BusinessException(PRODUCT_NOT_FOUND)); + + Integer productStock = product.getStock(); + Integer quantityInCart = cart.getCount(); + + // 현재 남아있는 재고보다 더 많이 요청하면 + // pending 상태로 저장이 안 됨. + if(quantityInCart > productStock){ + throw new BusinessException(PRODUCT_STOCK_ZERO); + } + + String productDescription = product.getDescription(); + Integer count = cart.getCount(); + String productName = product.getName(); + BigDecimal price = BigDecimal.valueOf(product.getPrice()); + + //TODO : Store store = product.getStore(); + + OrderedProduct orderedProduct = OrderedProduct + .builder() + .product(product) + .productOrder(productOrder) + //.store(store) + .productDescription(productDescription) + .productName(productName) + .price(price) + .count(count) + .build(); + + productOrder.addOrderedProduct(orderedProduct); + totalPrice = totalPrice.add(price); + } + productOrder.setTotalAmount(totalPrice); + productOrder.setCheckSum(totalPrice); + productOrderRepository.save(productOrder); + + String merchantUid = productOrder.getMerchantUid(); + return new PaymentPreRegisterRequest(merchantUid, totalPrice); + } + + @Transactional + //TODO : 만들어야 합니다 ~ + public PaymentPreRegisterRequest farmPreOrderRegister(){ + Member member = getMember(); + String merchantUid = ""; + BigDecimal totalPrice = BigDecimal.ZERO; + + return new PaymentPreRegisterRequest(merchantUid, totalPrice); + } + + + @Description("멤버 ID 기반으로 모든 order 다 들고 오는 메서드") + public List findAllOrdersByMemberId(){ + Member member = getMember(); + Long memberId = member.getId(); + List productOrderList = productOrderRepository.findByMemberId(memberId); + return productOrderList + .stream() + .map(OrderResponse::fromEntity) + .collect(Collectors.toList() + ); + } + + @Description("멤버 id 기반으로 특정 orderId 들고오는 메서드") + public OrderResponse findOrderByMemberId(Long orderId){ + Member member = getMember(); + ProductOrder productOrder = productOrderRepository.findById(orderId) + .orElseThrow(()-> new BusinessException(ORDER_NOT_FOUND)); + + validateOrderOwnership(productOrder, member); + return OrderResponse.fromEntity(productOrder); + } + + + @Description("orderId 기반으로 order details(주소, 상세주소, 배송 요청 사항 ..등) 들고오는 메서드") + public OrderDetailsResponse findOrderDetailsByOrderId(Long orderId){ + ProductOrder productOrder = productOrderRepository.findById(orderId) + .orElseThrow(()-> new BusinessException(ORDER_NOT_FOUND)); + ProductOrderDetails productOrderDetails = productOrder.getProductOrderDetails(); + + return OrderDetailsResponse.fromEntity(productOrderDetails); + } + + + + @Description("orderId에 해당하는 order product details 가져오는 메서드") + public List findAllOrderProductDetails(Long orderId){ + Member member = getMember(); + ProductOrder productOrder = productOrderRepository.findById(orderId) + .orElseThrow(()-> new BusinessException(ORDER_NOT_FOUND)); + validateOrderOwnership(productOrder, member); + return productOrder.getOrderedProducts() + .stream() + .map(OrderProductDetailsResponse::fromEntity) + .collect(Collectors.toList() + ); + } + + + @Description("orderId에 해당하는 order product Details의 단건 조회") + public OrderProductDetailsResponse findOrderProductDetailsById(Long orderId, Long orderProductDetailsId){ + Member member = getMember(); + OrderedProduct orderedProduct = orderedProductRepository.findById(orderProductDetailsId) + .orElseThrow(()-> new BusinessException(ORDER_PRODUCT_DETAILS_NOT_FOUND)); + ProductOrder productOrder = orderedProduct.getProductOrder(); + + // productOrder product details의 주인 productOrder 검사 그리고 , orderId의 주인 member 검사 + validateOrderProductDetailsByOrderId(productOrder, orderId); + validateOrderOwnership(productOrder, member); + + return OrderProductDetailsResponse.fromEntity(orderedProduct); + } + + + @Description("member의 order인지 검사하는 메서드") + private void validateOrderOwnership(ProductOrder productOrder, Member member) { + if (!productOrder.getMember().getId().equals(member.getId())) { + throw new BusinessException(ORDER_NOT_OWNED_EXCEPTION); + } + } + + @Description("orderId에 해당하는 productOrder Product Details인지 조회하는 메서드") + private void validateOrderProductDetailsByOrderId(ProductOrder productOrder, Long orderId) { + if(productOrder.getId()!=orderId){ + throw new BusinessException(ORDER_PRODUCT_DETAILS_NOT_OWNED_EXCEPTION); + } + } + + + @Description("주문 상태를 변경하는 메서드") + private void changeOrderStatus(Long orderId, PaymentStatus paymentStatus){ + ProductOrder productOrder = productOrderRepository.findById(orderId) + .orElseThrow(()-> new BusinessException(ORDER_NOT_FOUND)); + productOrder.setPaymentStatus(paymentStatus); + } + + + @Description("security context에서 member 객체 가져오는 메서드") + private Member getMember() { + Authentication authentication = SecurityContextHolder + .getContext().getAuthentication(); + Object impl = authentication.getPrincipal(); + Member member = ((UserDetailsImpl) impl).getMember(); + return member; + } + +} + + From ca5ca170cd8ad8ac368931bb086a85c2a0d743fa Mon Sep 17 00:00:00 2001 From: amm0124 Date: Mon, 11 Nov 2024 23:07:59 +0900 Subject: [PATCH 58/59] =?UTF-8?q?conflict=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../poomasi/domain/member/entity/Member.java | 1 - .../poomasi/domain/product/entity/Product.java | 16 ++-------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/src/main/java/poomasi/domain/member/entity/Member.java b/src/main/java/poomasi/domain/member/entity/Member.java index 230b9496..2292127a 100644 --- a/src/main/java/poomasi/domain/member/entity/Member.java +++ b/src/main/java/poomasi/domain/member/entity/Member.java @@ -11,7 +11,6 @@ import poomasi.domain.member._profile.entity.MemberProfile; import poomasi.domain.store.entity.Store; import poomasi.domain.order.entity._product.ProductOrder; -import poomasi.domain.order.entity.Order; import poomasi.domain.store.entity.Store; import poomasi.domain.member._profile.entity.MemberProfile; import poomasi.domain.store.entity.Store; diff --git a/src/main/java/poomasi/domain/product/entity/Product.java b/src/main/java/poomasi/domain/product/entity/Product.java index e8db7331..cd80bfbc 100644 --- a/src/main/java/poomasi/domain/product/entity/Product.java +++ b/src/main/java/poomasi/domain/product/entity/Product.java @@ -1,18 +1,7 @@ package poomasi.domain.product.entity; -import jakarta.persistence.CascadeType; -import jakarta.persistence.CollectionTable; -import jakarta.persistence.Column; -import jakarta.persistence.ElementCollection; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.OneToMany; +import jakarta.persistence.*; + import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -23,7 +12,6 @@ import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.UpdateTimestamp; import poomasi.domain.order.entity._product.OrderedProduct; -import poomasi.domain.order.entity.OrderProductDetails; import poomasi.domain.store.entity.Store; import poomasi.domain.product.dto.ProductRegisterRequest; import poomasi.domain.review.entity.Review; From 74a05a86f4da15bba4484902ec8bbd15bde59e53 Mon Sep 17 00:00:00 2001 From: amm0124 Date: Mon, 11 Nov 2024 23:10:00 +0900 Subject: [PATCH 59/59] =?UTF-8?q?conflict=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/poomasi/global/error/BusinessError.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/poomasi/global/error/BusinessError.java b/src/main/java/poomasi/global/error/BusinessError.java index 552e5e0f..b177035e 100644 --- a/src/main/java/poomasi/global/error/BusinessError.java +++ b/src/main/java/poomasi/global/error/BusinessError.java @@ -12,6 +12,7 @@ public enum BusinessError { // Product PRODUCT_NOT_FOUND(HttpStatus.NOT_FOUND, "상품을 찾을 수 없습니다."), PRODUCT_STOCK_ZERO(HttpStatus.BAD_REQUEST, "재고가 없습니다."), + STOCK_QUANTITY_EXCEEDED(HttpStatus.BAD_REQUEST, "장바구나 수량이 남은 재고를 초과하였습니다"), // Category CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "카테고리를 찾을 수 없습니다."), @@ -94,14 +95,10 @@ public enum BusinessError { - - - - - ; private final HttpStatus httpStatus; private final String message; } +