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/29] =?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/29] =?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/29] =?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/29] =?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/29] =?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/29] =?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/29] =?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 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 08/29] =?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 09/29] =?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 10/29] =?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 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 11/29] =?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 12/29] =?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 13/29] =?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 14/29] =?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 15/29] =?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 16/29] =?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 17/29] =?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 18/29] =?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 19/29] =?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 20/29] =?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 21/29] =?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 22/29] =?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 23/29] =?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 24/29] =?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 25/29] =?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 26/29] =?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 27/29] =?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 28/29] =?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 29/29] =?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); - } }