From 33e8070fcba3eb15fd80937a68b8b995fb0c6cf3 Mon Sep 17 00:00:00 2001 From: JuseungL <121665437+JuseungL@users.noreply.github.com> Date: Tue, 30 Jul 2024 17:19:15 +0900 Subject: [PATCH 1/4] =?UTF-8?q?FEAT=20:=20=ED=9B=84=EC=9B=90=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=93=B1=EB=A1=9D(=ED=85=8C=EC=8A=A4=ED=8A=B8X)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CanaryProfileController.java | 12 ++++ .../dto/CanaryGetDeliveryInfoResponse.java | 29 ++++++++++ .../repository/CanaryProfileRepository.java | 3 + .../canary/service/CanaryProfileService.java | 15 +++++ .../fledgeserver/common/util/MemberUtil.java | 24 ++++++++ .../fledgeserver/common/util/TimeUtil.java | 19 +++++++ .../fledgeserver/exception/ErrorCode.java | 6 +- .../fledgeserver/response/SuccessStatus.java | 4 +- .../support/controller/SupportController.java | 26 +++++++-- .../dto/request/SupportCreateRequestDto.java | 21 ++++++- .../response/SupportDetailGetResponseDto.java | 56 +++++++++++++++++++ .../support/entity/ExpirationStatus.java | 16 ++++++ .../fledgeserver/support/entity/Support.java | 32 ++++++++++- .../support/repository/SupportRepository.java | 4 ++ .../support/service/SupportService.java | 46 +++++++++++++-- 15 files changed, 295 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/fledge/fledgeserver/canary/dto/CanaryGetDeliveryInfoResponse.java create mode 100644 src/main/java/com/fledge/fledgeserver/common/util/MemberUtil.java create mode 100644 src/main/java/com/fledge/fledgeserver/common/util/TimeUtil.java create mode 100644 src/main/java/com/fledge/fledgeserver/support/dto/response/SupportDetailGetResponseDto.java create mode 100644 src/main/java/com/fledge/fledgeserver/support/entity/ExpirationStatus.java diff --git a/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java b/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java index 8b2c5b6..784a2bd 100644 --- a/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java +++ b/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java @@ -1,13 +1,16 @@ package com.fledge.fledgeserver.canary.controller; +import com.fledge.fledgeserver.canary.dto.CanaryGetDeliveryInfoResponse; import com.fledge.fledgeserver.canary.dto.CanaryProfileRequest; import com.fledge.fledgeserver.canary.dto.CanaryProfileResponse; import com.fledge.fledgeserver.canary.dto.CanaryProfileUpdateRequest; import com.fledge.fledgeserver.canary.service.CanaryProfileService; +import com.fledge.fledgeserver.common.util.MemberUtil; import com.fledge.fledgeserver.response.ApiResponse; import com.fledge.fledgeserver.response.SuccessStatus; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.validation.Valid; +import lombok.Getter; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -18,6 +21,8 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import java.security.Principal; + @Tag(name = "자립준비청년 API", description = "자립준비청년 관리 관련 API") @RestController @RequiredArgsConstructor @@ -65,4 +70,11 @@ public ResponseEntity> updateCanaryProfile( CanaryProfileResponse response = canaryProfileService.updateCanaryProfile(userId, request, userDetails.getUsername()); return ApiResponse.success(SuccessStatus.PROFILE_UPDATE_SUCCESS, response); } + + @Operation(summary = "자립준비청년 배송지 정보 조회", description = "자립준비청년 후원글 작성 시 배송지 정보를 불러올 수 있습니다.") + @GetMapping("/delivery") + public ResponseEntity> getCanaryDeliveryInfo(Principal principal) { + Long memberId = MemberUtil.getMemberId(principal); + return ApiResponse.success(SuccessStatus.DELIVERY_INFO_GET_SUCCESS, canaryProfileService.getCanaryDeliveryInfo(memberId)); + } } \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/canary/dto/CanaryGetDeliveryInfoResponse.java b/src/main/java/com/fledge/fledgeserver/canary/dto/CanaryGetDeliveryInfoResponse.java new file mode 100644 index 0000000..3ecab74 --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/canary/dto/CanaryGetDeliveryInfoResponse.java @@ -0,0 +1,29 @@ +package com.fledge.fledgeserver.canary.dto; + +import lombok.Getter; +import io.swagger.v3.oas.annotations.media.Schema; + +@Getter +@Schema(description = "자립준비청년 배송지 정보 조회 응답 DTO") +public class CanaryGetDeliveryInfoResponse { + + @Schema(description = "거주지", example = "서울특별시 강남구 역삼동") + private String address; + + @Schema(description = "상세 주소", example = "123-45") + private String detailAddress; + + @Schema(description = "우편번호", example = "12345") + private String zip; + + @Schema(description = "전화번호", example = "010-1234-5678") + private String phone; + + + public CanaryGetDeliveryInfoResponse(String phone, String address, String detailAddress, String zip) { + this.phone = phone; + this.address = address; + this.detailAddress = detailAddress; + this.zip = zip; + } +} diff --git a/src/main/java/com/fledge/fledgeserver/canary/repository/CanaryProfileRepository.java b/src/main/java/com/fledge/fledgeserver/canary/repository/CanaryProfileRepository.java index b4733e8..54a74ee 100644 --- a/src/main/java/com/fledge/fledgeserver/canary/repository/CanaryProfileRepository.java +++ b/src/main/java/com/fledge/fledgeserver/canary/repository/CanaryProfileRepository.java @@ -10,4 +10,7 @@ public interface CanaryProfileRepository extends JpaRepository findByMemberId(Long memberId); + + Optional findCanaryProfileByMemberId(Long memberId); + } diff --git a/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java b/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java index 8300e4c..742e941 100644 --- a/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java +++ b/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java @@ -1,5 +1,6 @@ package com.fledge.fledgeserver.canary.service; +import com.fledge.fledgeserver.canary.dto.CanaryGetDeliveryInfoResponse; import com.fledge.fledgeserver.canary.dto.CanaryProfileRequest; import com.fledge.fledgeserver.canary.dto.CanaryProfileResponse; import com.fledge.fledgeserver.canary.dto.CanaryProfileUpdateRequest; @@ -13,6 +14,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Optional; + import static com.fledge.fledgeserver.exception.ErrorCode.MEMBER_FORBIDDEN; import static com.fledge.fledgeserver.exception.ErrorCode.MEMBER_NOT_FOUND; @@ -99,4 +102,16 @@ private Member authenticateAndAuthorize(String currentUserEmail, Long userId) { return member; } + + @Transactional(readOnly = true) + public CanaryGetDeliveryInfoResponse getCanaryDeliveryInfo(Long memberId) { + CanaryProfile canary = canaryProfileRepository.findByMemberId(memberId) + .orElseThrow(() -> new CustomException(ErrorCode.CANARY_NOT_FOUND)); + return new CanaryGetDeliveryInfoResponse( + canary.getAddress(), + canary.getDetailAddress(), + canary.getZip(), + canary.getPhone() + ); + } } \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/common/util/MemberUtil.java b/src/main/java/com/fledge/fledgeserver/common/util/MemberUtil.java new file mode 100644 index 0000000..4c37086 --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/common/util/MemberUtil.java @@ -0,0 +1,24 @@ +package com.fledge.fledgeserver.common.util; + +import com.fledge.fledgeserver.exception.CustomException; +import com.fledge.fledgeserver.exception.ErrorCode; +import lombok.RequiredArgsConstructor; + +import java.security.Principal; + +@RequiredArgsConstructor +public class MemberUtil { + /** + * 현재 사용자의 OAuth2 ID를 반환합니다. + */ + public static Long getMemberId(Principal principal) { + // Principal 객체가 null이면 사용자가 인증되지 않았으므로 예외를 발생시킨다. + if (principal == null) { + throw new CustomException(ErrorCode.MEMBER_NOT_FOUND); + } + // Principal 객체의 이름을 memberId ID로 사용하여 반환한다. + return Long.valueOf(principal.getName()); + } +} + + diff --git a/src/main/java/com/fledge/fledgeserver/common/util/TimeUtil.java b/src/main/java/com/fledge/fledgeserver/common/util/TimeUtil.java new file mode 100644 index 0000000..ed8bc3c --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/common/util/TimeUtil.java @@ -0,0 +1,19 @@ +package com.fledge.fledgeserver.common.util; + +import lombok.RequiredArgsConstructor; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +@RequiredArgsConstructor +public class TimeUtil { + public static String refineTime(LocalDateTime localDateTime) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + return localDateTime.format(formatter); + } + + public static String refineTimeMemberDetail(LocalDateTime localDateTime) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + return localDateTime.format(formatter); + } +} \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/exception/ErrorCode.java b/src/main/java/com/fledge/fledgeserver/exception/ErrorCode.java index 60ab61b..aa4e304 100644 --- a/src/main/java/com/fledge/fledgeserver/exception/ErrorCode.java +++ b/src/main/java/com/fledge/fledgeserver/exception/ErrorCode.java @@ -29,7 +29,11 @@ public enum ErrorCode { // canary profile CANARY_NOT_FOUND(NOT_FOUND, "자립준비청년을 찾을 수 없습니다."), - DUPLICATE_APPLICATION(HttpStatus.CONFLICT, "이미 신청된 유저입니다."); + DUPLICATE_APPLICATION(HttpStatus.CONFLICT, "이미 신청된 유저입니다."), + + // support + SUPPORT_NOT_FOUND(NOT_FOUND, "후원하기 게시글을 찾을 수 없습니다."), + UNAUTHORIZED_MEMBER(UNAUTHORIZED, "후원하기 게시글에 대한 권한이 없습니다. 자립 준비 청소년 인증이 필요합니다."); private final HttpStatus httpStatus; private final String message; diff --git a/src/main/java/com/fledge/fledgeserver/response/SuccessStatus.java b/src/main/java/com/fledge/fledgeserver/response/SuccessStatus.java index 833caf6..22750d0 100644 --- a/src/main/java/com/fledge/fledgeserver/response/SuccessStatus.java +++ b/src/main/java/com/fledge/fledgeserver/response/SuccessStatus.java @@ -20,13 +20,15 @@ public enum SuccessStatus { * support */ CREATE_SUPPORT_SUCCESS(HttpStatus.CREATED, "후원하기 게시글 등록 성공"), + GET_SUPPORT_SUCCESS(HttpStatus.OK, "후원하기 상세 페이지 조회 성공"), /** * canary */ PROFILE_APPLICATION_SUCCESS(HttpStatus.OK, "프로필 신청 성공"), PROFILE_RETRIEVAL_SUCCESS(HttpStatus.OK, "프로필 조회 성공"), - PROFILE_UPDATE_SUCCESS(HttpStatus.NO_CONTENT, "프로필 수정 성공"); + PROFILE_UPDATE_SUCCESS(HttpStatus.NO_CONTENT, "프로필 수정 성공"), + DELIVERY_INFO_GET_SUCCESS(HttpStatus.OK, "배송지 조회 성공"); private final HttpStatus httpStatus; diff --git a/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java b/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java index c815832..ff175d6 100644 --- a/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java +++ b/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java @@ -1,18 +1,20 @@ package com.fledge.fledgeserver.support.controller; +import com.fledge.fledgeserver.common.util.MemberUtil; import com.fledge.fledgeserver.response.ApiResponse; import com.fledge.fledgeserver.support.dto.request.SupportCreateRequestDto; +import com.fledge.fledgeserver.support.dto.response.SupportDetailGetResponseDto; import com.fledge.fledgeserver.support.service.SupportService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.security.Principal; import static com.fledge.fledgeserver.response.SuccessStatus.CREATE_SUPPORT_SUCCESS; +import static com.fledge.fledgeserver.response.SuccessStatus.GET_SUPPORT_SUCCESS; @Tag(name = "후원하기 API", description = "후원하기와 관련된 API") @RestController @@ -23,10 +25,22 @@ public class SupportController { @Operation(summary = "후원하기 게시글 등록", description = "후원하기 게시글을 등록합니다.(자립 준비 청소년만)") @PostMapping - public ResponseEntity> createSupport(@RequestBody SupportCreateRequestDto supportCreateRequestDto){ - supportService.createSupport(supportCreateRequestDto); + public ResponseEntity> createSupport(Principal principal, @RequestBody SupportCreateRequestDto supportCreateRequestDto){ + Long memberId = MemberUtil.getMemberId(principal); + supportService.createSupport(memberId, supportCreateRequestDto); return ApiResponse.success(CREATE_SUPPORT_SUCCESS); } + @Operation(summary = "후원하기 게시글 상세 페이지 조회", description = "후원하기 게시글 상세 페이지 조회입니다.") + @GetMapping("/{supportId}") + public ResponseEntity> getSupport(@PathVariable Long supportId) { + return ApiResponse.success(GET_SUPPORT_SUCCESS, supportService.getSupport(supportId)); + } + + // TODO :: 임박한 후원글 D-7까지 4개씩 최대 20개 + + + // TODO :: 후원글 목록 + } diff --git a/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportCreateRequestDto.java b/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportCreateRequestDto.java index ab958e4..6874745 100644 --- a/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportCreateRequestDto.java +++ b/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportCreateRequestDto.java @@ -6,6 +6,7 @@ import lombok.Setter; import org.hibernate.validator.constraints.URL; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; @@ -52,8 +53,24 @@ public class SupportCreateRequestDto { @Positive(message = "후원 인증 횟수는 0보다 큰 값이어야 합니다.") private int checkCount; - @Schema(description = "만료 시점", required = true, example = "2024-12-31T23:59:59") + @Schema(description = "만료 시점", required = true, example = "2024-12-31") @NotBlank(message = "만료 시점은 필수입니다.") @Future(message = "만료 시점은 현재 시간 이후여야 합니다.") - private LocalDateTime expirationTime; + private LocalDate expirationDate; + + @Schema(description = "거주지", example = "서울특별시 강남구 역삼동") + private String address; + + @Schema(description = "상세 주소", example = "123-45") + private String detailAddress; + + @Schema(description = "우편번호", example = "12345") + private String zip; + + @Schema(description = "이름", example = "이길동") + private String name; + + @Schema(description = "전화번호", example = "010-1234-5678") + private String phone; + } diff --git a/src/main/java/com/fledge/fledgeserver/support/dto/response/SupportDetailGetResponseDto.java b/src/main/java/com/fledge/fledgeserver/support/dto/response/SupportDetailGetResponseDto.java new file mode 100644 index 0000000..505191b --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/support/dto/response/SupportDetailGetResponseDto.java @@ -0,0 +1,56 @@ +package com.fledge.fledgeserver.support.dto.response; + +import com.fledge.fledgeserver.support.entity.Support; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +import java.time.LocalDate; +import java.util.List; + +@Getter +@Schema(description = "후원하기 게시글 조회 DTO") +public class SupportDetailGetResponseDto { + + @Schema(description = "후원 게시글 ID", example = "1") + private Long id; + + @Schema(description = "후원 게시글 제목", example = "후원 요청") + private String title; + + @Schema(description = "후원 필요한 이유", example = "자립을 위한 후원") + private String reason; + + @Schema(description = "후원 물품 명", example = "노트북") + private String item; + + @Schema(description = "구매 URL", example = "https://example.com/product/1") + private String purchaseUrl; + + @Schema(description = "후원 물품 가격", example = "500000") + private int price; + + @Schema(description = "후원 물품 이미지 리스트") + private List images; // 이미지 URL을 String List로 표현 + + @Schema(description = "후원 인증 기간", example = "30") + private int checkPeriod; + + @Schema(description = "후원 인증 횟수", example = "1") + private int checkCount; + + @Schema(description = "만료 시점", example = "2024-12-31") + private LocalDate expirationDate; + + public SupportDetailGetResponseDto(Support support, List presignedImageUrl) { + this.id = support.getId(); + this.title = support.getTitle(); + this.reason = support.getReason(); + this.item = support.getItem(); + this.purchaseUrl = support.getPurchaseUrl(); + this.price = support.getPrice(); + this.images = presignedImageUrl; + this.checkPeriod = support.getCheckPeriod(); + this.checkCount = support.getCheckCount(); + this.expirationDate = support.getExpirationDate(); + } +} \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/support/entity/ExpirationStatus.java b/src/main/java/com/fledge/fledgeserver/support/entity/ExpirationStatus.java new file mode 100644 index 0000000..597c35a --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/support/entity/ExpirationStatus.java @@ -0,0 +1,16 @@ +package com.fledge.fledgeserver.support.entity; + +public enum ExpirationStatus { + ACTIVE("ACTIVE"), + + EXPIRED("EXPIRED"); + private final String key; + + ExpirationStatus(String key) { + this.key = key; + } + + public String getKey() { + return key; + } +} \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/support/entity/Support.java b/src/main/java/com/fledge/fledgeserver/support/entity/Support.java index 83e14a1..b45c529 100644 --- a/src/main/java/com/fledge/fledgeserver/support/entity/Support.java +++ b/src/main/java/com/fledge/fledgeserver/support/entity/Support.java @@ -1,12 +1,14 @@ package com.fledge.fledgeserver.support.entity; import com.fledge.fledgeserver.common.entity.BaseTimeEntity; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -45,20 +47,44 @@ public class Support extends BaseTimeEntity { private int checkCount; @Column(nullable = false) - private LocalDateTime expirationTime; + private LocalDate expirationDate; + + @Column(nullable = false) + private ExpirationStatus expirationStatus = ExpirationStatus.ACTIVE; + + @Column(nullable = false) + private String address; + + @Column(nullable = false) + private String detailAddress; + + @Column(nullable = false) + private String zip; + + @Column(nullable = false) + private String name; + + @Column(nullable = false) + private String phone; // TODO :: 챌린지 구현 후 참여 중이거나 완료한 챌린지(뱃지)에 대한 로직 추가 @Builder - public Support(String title, String reason, String item, String purchaseUrl, int price, int checkPeriod, int checkCount, LocalDateTime expirationTime) { + public Support(String title, String reason, String item, String purchaseUrl, int price, List images, int checkPeriod, int checkCount, LocalDate expirationDate, String address, String detailAddress, String zip, String name, String phone) { this.title = title; this.reason = reason; this.item = item; this.purchaseUrl = purchaseUrl; this.price = price; + this.images = images; this.checkPeriod = checkPeriod; this.checkCount = checkCount; - this.expirationTime = expirationTime; + this.expirationDate = expirationDate; + this.address = address; + this.detailAddress = detailAddress; + this.zip = zip; + this.name = name; + this.phone = phone; } } diff --git a/src/main/java/com/fledge/fledgeserver/support/repository/SupportRepository.java b/src/main/java/com/fledge/fledgeserver/support/repository/SupportRepository.java index fd006ba..a0b3be0 100644 --- a/src/main/java/com/fledge/fledgeserver/support/repository/SupportRepository.java +++ b/src/main/java/com/fledge/fledgeserver/support/repository/SupportRepository.java @@ -3,5 +3,9 @@ import com.fledge.fledgeserver.support.entity.Support; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface SupportRepository extends JpaRepository { + Optional findSupportById(Long supportId); + } diff --git a/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java b/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java index f366415..ad4a7af 100644 --- a/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java +++ b/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java @@ -1,22 +1,34 @@ package com.fledge.fledgeserver.support.service; +import com.fledge.fledgeserver.canary.repository.CanaryProfileRepository; +import com.fledge.fledgeserver.exception.CustomException; +import com.fledge.fledgeserver.exception.ErrorCode; +import com.fledge.fledgeserver.file.FileService; import com.fledge.fledgeserver.support.dto.request.SupportCreateRequestDto; +import com.fledge.fledgeserver.support.dto.response.SupportDetailGetResponseDto; import com.fledge.fledgeserver.support.entity.Support; import com.fledge.fledgeserver.support.entity.SupportImage; -import com.fledge.fledgeserver.support.repository.SupportImageRepository; import com.fledge.fledgeserver.support.repository.SupportRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.stream.Collectors; + @Service -@Transactional @RequiredArgsConstructor public class SupportService { + private final CanaryProfileRepository canaryProfileRepository; private final SupportRepository supportRepository; - private final SupportImageRepository supportImageRepository; + private final FileService fileService; + + @Transactional + public void createSupport(Long memberId, SupportCreateRequestDto supportCreateRequestDto) { + // 자립 청소년인지 검증 -> canary_profile 테이블에 없으면 권한이 없는 것 + canaryProfileRepository.findCanaryProfileByMemberId(memberId) + .orElseThrow(() -> new CustomException(ErrorCode.UNAUTHORIZED_MEMBER)); - public void createSupport(SupportCreateRequestDto supportCreateRequestDto) { Support support = Support.builder() .title(supportCreateRequestDto.getTitle()) @@ -26,9 +38,16 @@ public void createSupport(SupportCreateRequestDto supportCreateRequestDto) { .purchaseUrl(supportCreateRequestDto.getPurchaseUrl()) .checkPeriod(supportCreateRequestDto.getCheckPeriod()) .checkCount(supportCreateRequestDto.getCheckCount()) - .expirationTime(supportCreateRequestDto.getExpirationTime()) + .expirationDate(supportCreateRequestDto.getExpirationDate()) + .address(supportCreateRequestDto.getAddress()) + .detailAddress(supportCreateRequestDto.getDetailAddress()) + .zip(supportCreateRequestDto.getZip()) + .name(supportCreateRequestDto.getName()) + .phone(supportCreateRequestDto.getPhone()) .build(); + supportRepository.save(support); + for (String imageUrl : supportCreateRequestDto.getImages()) { SupportImage supportImage = SupportImage.builder() .support(support) @@ -37,4 +56,21 @@ public void createSupport(SupportCreateRequestDto supportCreateRequestDto) { support.getImages().add(supportImage); } } + + @Transactional(readOnly = true) + public SupportDetailGetResponseDto getSupport(Long supportId) { + // 후원 게시글 조회 + Support support = supportRepository.findSupportById(supportId) + .orElseThrow(() -> new CustomException(ErrorCode.SUPPORT_NOT_FOUND)); + + // 후원 게시글의 이미지 URL -> Presigned URL로 변환 + List presignedImageUrl = support.getImages().stream() + .map(SupportImage::getImageUrl) + .map(fileService::getFileUrl) + .collect(Collectors.toList()); + + // TODO :: 후원내역(SupportRecord)에 대한 내용 함께 반환 + + return new SupportDetailGetResponseDto(support, presignedImageUrl); + } } From fb06cf6dd010f46a828403e5edd7d00757578b9f Mon Sep 17 00:00:00 2001 From: JuseungL <121665437+JuseungL@users.noreply.github.com> Date: Wed, 31 Jul 2024 11:07:38 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=9D=B8=EC=A6=9D=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=EC=9D=B4=EC=8A=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fledgeserver/auth/dto/OAuth2UserInfo.java | 53 -------------- .../auth/dto/OAuthAttributes.java | 73 +++++++++++++++++++ .../fledgeserver/auth/dto/OAuthUserImpl.java | 58 +++++++++++++++ .../auth/dto/PrincipalDetails.java | 63 ---------------- .../fledgeserver/auth/jwt/TokenProvider.java | 9 ++- .../auth/service/CustomOAuth2UserService.java | 19 +++-- .../service/CustomUserDetailsService.java | 45 ++++++++++++ .../controller/CanaryProfileController.java | 26 ++++--- .../canary/service/CanaryProfileService.java | 26 +++---- .../common/{util => utils}/MemberUtil.java | 6 +- .../common/utils/SecurityUtils.java | 71 ++++++++++++++++++ .../common/{util => utils}/TimeUtil.java | 2 +- .../member/controller/MemberController.java | 16 ++-- .../member/service/MemberService.java | 17 +++-- .../support/controller/SupportController.java | 6 +- .../support/service/SupportService.java | 10 ++- 16 files changed, 321 insertions(+), 179 deletions(-) delete mode 100644 src/main/java/com/fledge/fledgeserver/auth/dto/OAuth2UserInfo.java create mode 100644 src/main/java/com/fledge/fledgeserver/auth/dto/OAuthAttributes.java create mode 100644 src/main/java/com/fledge/fledgeserver/auth/dto/OAuthUserImpl.java delete mode 100644 src/main/java/com/fledge/fledgeserver/auth/dto/PrincipalDetails.java create mode 100644 src/main/java/com/fledge/fledgeserver/auth/service/CustomUserDetailsService.java rename src/main/java/com/fledge/fledgeserver/common/{util => utils}/MemberUtil.java (80%) create mode 100644 src/main/java/com/fledge/fledgeserver/common/utils/SecurityUtils.java rename src/main/java/com/fledge/fledgeserver/common/{util => utils}/TimeUtil.java (92%) diff --git a/src/main/java/com/fledge/fledgeserver/auth/dto/OAuth2UserInfo.java b/src/main/java/com/fledge/fledgeserver/auth/dto/OAuth2UserInfo.java deleted file mode 100644 index 84add26..0000000 --- a/src/main/java/com/fledge/fledgeserver/auth/dto/OAuth2UserInfo.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.fledge.fledgeserver.auth.dto; - -import java.util.Map; - -import com.fledge.fledgeserver.exception.AuthException; -import com.fledge.fledgeserver.member.entity.Member; -import com.fledge.fledgeserver.member.entity.Role; -import lombok.Builder; - -import static com.fledge.fledgeserver.exception.ErrorCode.ILLEGAL_REGISTRATION_ID; - -@Builder -public record OAuth2UserInfo( - String nickname, - String registerType, - String email, - String profile, - Long socialId -) { - - public static OAuth2UserInfo of(String registrationId, Map attributes) { - return switch (registrationId) { - case "kakao" -> ofKakao(attributes); - default -> throw new AuthException(ILLEGAL_REGISTRATION_ID); - }; - } - - private static OAuth2UserInfo ofKakao(Map attributes) { - - Map account = (Map) attributes.get("kakao_account"); - Map profile = (Map) account.get("profile"); - - - return OAuth2UserInfo.builder() - .socialId((Long) attributes.get("id")) - .nickname((String) profile.get("nickname")) - .email((String) account.get("email")) - .profile((String) profile.get("profile_image_url")) - .registerType("KAKAO") - .build(); - } - - public Member toEntity() { - return Member.builder() - .socialId(socialId) - .nickname(nickname) - .email(email) - .profile(profile) - .role(Role.USER) - .registerType("KAKAO") - .build(); - } -} diff --git a/src/main/java/com/fledge/fledgeserver/auth/dto/OAuthAttributes.java b/src/main/java/com/fledge/fledgeserver/auth/dto/OAuthAttributes.java new file mode 100644 index 0000000..90a36f8 --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/auth/dto/OAuthAttributes.java @@ -0,0 +1,73 @@ +package com.fledge.fledgeserver.auth.dto; + +import com.fledge.fledgeserver.exception.AuthException; +import com.fledge.fledgeserver.member.entity.Member; +import com.fledge.fledgeserver.member.entity.Role; +import lombok.Builder; +import lombok.Getter; + +import java.util.Map; +import java.util.UUID; + +import static com.fledge.fledgeserver.exception.ErrorCode.ILLEGAL_REGISTRATION_ID; + +@Getter +public class OAuthAttributes { + private final Map attributes; + private final String nameAttributeKey; + private final String nickname; + private final String email; + private final String profile; + private final String registerType; + private final Long socialId; + + @Builder + public OAuthAttributes(Map attributes, String nameAttributeKey, String nickname, String email, String profile, String registerType, Long socialId) { + this.attributes = attributes; + this.nameAttributeKey = nameAttributeKey; + this.nickname = nickname; + this.email = email; + this.profile = profile; + this.registerType = registerType; + this.socialId = socialId; + } + + public static OAuthAttributes of(String socialName, String userNameAttributeName, Map attributes) { + + if ("kakao".equals(socialName)) { + return ofKakao(userNameAttributeName, attributes); + } + + throw new AuthException(ILLEGAL_REGISTRATION_ID); + } + + private static OAuthAttributes ofKakao(String userNameAttributeName, Map attributes) { + Map account = (Map) attributes.get("kakao_account"); + Map profile = (Map) account.get("profile"); + + return OAuthAttributes.builder() + .nameAttributeKey(userNameAttributeName) + .attributes(attributes) + .socialId(Long.valueOf(attributes.get("id").toString())) + .nickname((String) profile.get("nickname")) + .email((String) account.get("email")) + .profile((String) profile.get("profile_image_url")) + .registerType("KAKAO") + .build(); + } + + public Member toEntity() { + return Member.builder() + .socialId(socialId) + .nickname(generateRandomNickname()) + .email(email) + .profile(profile) + .role(Role.USER) + .registerType("KAKAO") + .build(); + } + + private static String generateRandomNickname() { + return "User_" + UUID.randomUUID().toString().replace("-", "").substring(0, 8); + } +} diff --git a/src/main/java/com/fledge/fledgeserver/auth/dto/OAuthUserImpl.java b/src/main/java/com/fledge/fledgeserver/auth/dto/OAuthUserImpl.java new file mode 100644 index 0000000..2a0d1a6 --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/auth/dto/OAuthUserImpl.java @@ -0,0 +1,58 @@ +package com.fledge.fledgeserver.auth.dto; + +import com.fledge.fledgeserver.member.entity.Member; +import lombok.Getter; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.oauth2.core.user.DefaultOAuth2User; + +import java.util.Collection; +import java.util.Map; + +@Getter +public class OAuthUserImpl extends DefaultOAuth2User implements UserDetails { + private final Member member; + + public OAuthUserImpl(Collection authorities, + Map attributes, + String nameAttributeKey, + Member member) { + super(authorities, attributes, nameAttributeKey); + this.member = member; + } + + @Override + public String getPassword() { + return null; + } + + @Override + public String getUsername() { + return member.getId().toString(); + } + + @Override + public boolean isAccountNonExpired() { + return member.isActive(); + } + + @Override + public boolean isAccountNonLocked() { + return member.isActive(); + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return member.isActive(); + } + + public Member getMember() { + return member; + } + +} \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/auth/dto/PrincipalDetails.java b/src/main/java/com/fledge/fledgeserver/auth/dto/PrincipalDetails.java deleted file mode 100644 index 3831e17..0000000 --- a/src/main/java/com/fledge/fledgeserver/auth/dto/PrincipalDetails.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.fledge.fledgeserver.auth.dto; - -import com.fledge.fledgeserver.member.entity.Member; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.oauth2.core.user.OAuth2User; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; - -public record PrincipalDetails( - Member member, - Map attributes, - String attributeKey) implements OAuth2User, UserDetails { - - @Override - public String getName() { - return attributes.get(attributeKey).toString(); - } - - @Override - public Map getAttributes() { - return attributes; - } - - @Override - public Collection getAuthorities() { - return Collections.singletonList( - new SimpleGrantedAuthority(member.getRole().getKey())); - } - - @Override - public String getPassword() { - return null; - } - - @Override - public String getUsername() { - return member.getEmail(); - } - - @Override - public boolean isAccountNonExpired() { - return true; - } - - @Override - public boolean isAccountNonLocked() { - return true; - } - - @Override - public boolean isCredentialsNonExpired() { - return true; - } - - @Override - public boolean isEnabled() { - return true; - } -} \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/auth/jwt/TokenProvider.java b/src/main/java/com/fledge/fledgeserver/auth/jwt/TokenProvider.java index d312df2..4cd942a 100644 --- a/src/main/java/com/fledge/fledgeserver/auth/jwt/TokenProvider.java +++ b/src/main/java/com/fledge/fledgeserver/auth/jwt/TokenProvider.java @@ -1,5 +1,6 @@ package com.fledge.fledgeserver.auth.jwt; +import com.fledge.fledgeserver.auth.service.CustomUserDetailsService; import com.fledge.fledgeserver.auth.service.TokenService; import com.fledge.fledgeserver.exception.TokenException; import io.jsonwebtoken.Claims; @@ -21,6 +22,8 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; @@ -42,6 +45,7 @@ public class TokenProvider { private static final String KEY_ROLE = "role"; private final TokenService tokenService; + private final CustomUserDetailsService userDetailsService; @PostConstruct private void setSecretKey() { @@ -78,8 +82,9 @@ public Authentication getAuthentication(String token) { Claims claims = parseClaims(token); List authorities = getAuthorities(claims); - User principal = new User(claims.getSubject(), "", authorities); - return new UsernamePasswordAuthenticationToken(principal, token, authorities); + UserDetails userDetails = userDetailsService.loadUserByUsername( + claims.getSubject()); + return new UsernamePasswordAuthenticationToken(userDetails, token, authorities); } private List getAuthorities(Claims claims) { diff --git a/src/main/java/com/fledge/fledgeserver/auth/service/CustomOAuth2UserService.java b/src/main/java/com/fledge/fledgeserver/auth/service/CustomOAuth2UserService.java index 4120aa3..2462a90 100644 --- a/src/main/java/com/fledge/fledgeserver/auth/service/CustomOAuth2UserService.java +++ b/src/main/java/com/fledge/fledgeserver/auth/service/CustomOAuth2UserService.java @@ -1,12 +1,14 @@ package com.fledge.fledgeserver.auth.service; +import java.util.Collections; import java.util.Map; +import com.fledge.fledgeserver.auth.dto.OAuthUserImpl; +import com.fledge.fledgeserver.auth.dto.OAuthAttributes; import com.fledge.fledgeserver.member.entity.Member; import com.fledge.fledgeserver.member.repository.MemberRepository; -import com.fledge.fledgeserver.auth.dto.OAuth2UserInfo; -import com.fledge.fledgeserver.auth.dto.PrincipalDetails; import lombok.RequiredArgsConstructor; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; @@ -28,15 +30,16 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic String userNameAttributeName = userRequest.getClientRegistration().getProviderDetails() .getUserInfoEndpoint().getUserNameAttributeName(); - OAuth2UserInfo oAuth2UserInfo = OAuth2UserInfo.of(registrationId, oAuth2UserAttributes); - Member member = getOrSave(oAuth2UserInfo); + OAuthAttributes oauthAttributes = OAuthAttributes.of(registrationId, userNameAttributeName, oAuth2UserAttributes); + Member member = getOrSave(oauthAttributes); - return new PrincipalDetails(member, oAuth2UserAttributes, userNameAttributeName); + return new OAuthUserImpl(Collections.singleton(new SimpleGrantedAuthority(member.getRole().getKey())), + oAuth2UserAttributes, oauthAttributes.getNameAttributeKey(), member); } - private Member getOrSave(OAuth2UserInfo oAuth2UserInfo) { - Member member = memberRepository.findBySocialId(oAuth2UserInfo.socialId()) - .orElseGet(oAuth2UserInfo::toEntity); + private Member getOrSave(OAuthAttributes oauth2Attributes) { + Member member = memberRepository.findBySocialId(oauth2Attributes.getSocialId()) + .orElseGet(oauth2Attributes::toEntity); return memberRepository.save(member); } } \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/auth/service/CustomUserDetailsService.java b/src/main/java/com/fledge/fledgeserver/auth/service/CustomUserDetailsService.java new file mode 100644 index 0000000..5f06c5f --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/auth/service/CustomUserDetailsService.java @@ -0,0 +1,45 @@ +package com.fledge.fledgeserver.auth.service; + +import com.fledge.fledgeserver.auth.dto.OAuthUserImpl; +import com.fledge.fledgeserver.member.entity.Member; +import com.fledge.fledgeserver.member.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class CustomUserDetailsService implements UserDetailsService { + private final MemberRepository memberRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + Member member = memberRepository.findById(Long.valueOf(username)) + .orElseThrow(() -> new UsernameNotFoundException("사용자를 찾을 수 없습니다: " + username)); + + return new OAuthUserImpl( + Collections.singleton(new SimpleGrantedAuthority(member.getRole().name())), + createAttributes(member), + "email", + member + ); + } + + // TODO :: Method 분리 + private Map createAttributes(Member member) { + Map attributes = new HashMap<>(); + attributes.put("socialId", member.getSocialId()); + attributes.put("nickname", member.getNickname()); + attributes.put("email", member.getEmail()); + attributes.put("profile", member.getProfile()); + attributes.put("registerType", member.getRegisterType()); + return attributes; + } +} \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java b/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java index 784a2bd..34e1c06 100644 --- a/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java +++ b/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java @@ -1,20 +1,19 @@ package com.fledge.fledgeserver.canary.controller; +import com.fledge.fledgeserver.auth.dto.OAuthUserImpl; import com.fledge.fledgeserver.canary.dto.CanaryGetDeliveryInfoResponse; import com.fledge.fledgeserver.canary.dto.CanaryProfileRequest; import com.fledge.fledgeserver.canary.dto.CanaryProfileResponse; import com.fledge.fledgeserver.canary.dto.CanaryProfileUpdateRequest; import com.fledge.fledgeserver.canary.service.CanaryProfileService; -import com.fledge.fledgeserver.common.util.MemberUtil; +import com.fledge.fledgeserver.common.utils.SecurityUtils; import com.fledge.fledgeserver.response.ApiResponse; import com.fledge.fledgeserver.response.SuccessStatus; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.validation.Valid; -import lombok.Getter; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; import io.swagger.v3.oas.annotations.Operation; @@ -23,6 +22,7 @@ import java.security.Principal; + @Tag(name = "자립준비청년 API", description = "자립준비청년 관리 관련 API") @RestController @RequiredArgsConstructor @@ -39,8 +39,8 @@ public class CanaryProfileController { @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "409", description = "이미 신청한 유저") }) public ResponseEntity> applyForCanaryProfile(@Valid @RequestBody CanaryProfileRequest request, - @AuthenticationPrincipal UserDetails userDetails) { - canaryProfileService.createCanaryProfile(request, userDetails.getUsername()); + @AuthenticationPrincipal OAuthUserImpl oAuth2User) { + canaryProfileService.createCanaryProfile(request, oAuth2User); return ApiResponse.success(SuccessStatus.PROFILE_APPLICATION_SUCCESS); } @@ -48,8 +48,8 @@ public ResponseEntity> applyForCanaryProfile(@Valid @RequestBo @GetMapping("/{userId}/status") public ResponseEntity> getApprovalStatus( @Parameter(description = "사용자 ID", required = true, example = "1") @PathVariable Long userId, - @AuthenticationPrincipal UserDetails userDetails) { - int status = canaryProfileService.getApprovalStatus(userId, userDetails.getUsername()); + @AuthenticationPrincipal OAuthUserImpl oAuth2User) { + int status = canaryProfileService.getApprovalStatus(userId, oAuth2User); return ApiResponse.success(SuccessStatus.PROFILE_RETRIEVAL_SUCCESS, status); } @@ -66,15 +66,17 @@ public ResponseEntity> getCanaryProfile( public ResponseEntity> updateCanaryProfile( @Parameter(description = "사용자 ID", required = true, example = "1") @PathVariable Long userId, @Valid @RequestBody CanaryProfileUpdateRequest request, - @AuthenticationPrincipal UserDetails userDetails) { - CanaryProfileResponse response = canaryProfileService.updateCanaryProfile(userId, request, userDetails.getUsername()); + @AuthenticationPrincipal OAuthUserImpl oAuth2User) { + CanaryProfileResponse response = canaryProfileService.updateCanaryProfile(userId, request, oAuth2User); return ApiResponse.success(SuccessStatus.PROFILE_UPDATE_SUCCESS, response); } @Operation(summary = "자립준비청년 배송지 정보 조회", description = "자립준비청년 후원글 작성 시 배송지 정보를 불러올 수 있습니다.") @GetMapping("/delivery") - public ResponseEntity> getCanaryDeliveryInfo(Principal principal) { - Long memberId = MemberUtil.getMemberId(principal); - return ApiResponse.success(SuccessStatus.DELIVERY_INFO_GET_SUCCESS, canaryProfileService.getCanaryDeliveryInfo(memberId)); + public ResponseEntity> getCanaryDeliveryInfo(@AuthenticationPrincipal OAuthUserImpl oAuth2User) { + Long userId = oAuth2User.getMember().getId(); + System.out.println("userId = " + userId); + + return ApiResponse.success(SuccessStatus.DELIVERY_INFO_GET_SUCCESS, canaryProfileService.getCanaryDeliveryInfo(userId)); } } \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java b/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java index 742e941..2c732c0 100644 --- a/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java +++ b/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java @@ -1,11 +1,13 @@ package com.fledge.fledgeserver.canary.service; +import com.fledge.fledgeserver.auth.dto.OAuthUserImpl; import com.fledge.fledgeserver.canary.dto.CanaryGetDeliveryInfoResponse; import com.fledge.fledgeserver.canary.dto.CanaryProfileRequest; import com.fledge.fledgeserver.canary.dto.CanaryProfileResponse; import com.fledge.fledgeserver.canary.dto.CanaryProfileUpdateRequest; import com.fledge.fledgeserver.canary.entity.CanaryProfile; import com.fledge.fledgeserver.canary.repository.CanaryProfileRepository; +import com.fledge.fledgeserver.common.utils.SecurityUtils; import com.fledge.fledgeserver.exception.CustomException; import com.fledge.fledgeserver.exception.ErrorCode; import com.fledge.fledgeserver.member.entity.Member; @@ -14,10 +16,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.Optional; - import static com.fledge.fledgeserver.exception.ErrorCode.MEMBER_FORBIDDEN; -import static com.fledge.fledgeserver.exception.ErrorCode.MEMBER_NOT_FOUND; @Service @@ -28,8 +27,8 @@ public class CanaryProfileService { private final MemberRepository memberRepository; @Transactional - public void createCanaryProfile(CanaryProfileRequest request, String currentUserEmail) { - Member member = authenticateAndAuthorize(currentUserEmail, request.getUserId()); + public void createCanaryProfile(CanaryProfileRequest request, OAuthUserImpl oAuth2User) { + Member member = authenticateAndAuthorize(oAuth2User, request.getUserId()); boolean exists = canaryProfileRepository.existsByMember(member); if (exists) { @@ -54,8 +53,8 @@ public void createCanaryProfile(CanaryProfileRequest request, String currentUser } @Transactional(readOnly = true) - public int getApprovalStatus(Long userId, String currentUserEmail) { - authenticateAndAuthorize(currentUserEmail, userId); + public int getApprovalStatus(Long userId, OAuthUserImpl oAuth2User) { + authenticateAndAuthorize(oAuth2User, userId); CanaryProfile canaryProfile = canaryProfileRepository.findByMemberId(userId) .orElse(null); @@ -78,8 +77,8 @@ public CanaryProfileResponse getCanaryProfile(Long userId) { } @Transactional - public CanaryProfileResponse updateCanaryProfile(Long userId, CanaryProfileUpdateRequest request, String currentUserEmail) { - authenticateAndAuthorize(currentUserEmail, userId); + public CanaryProfileResponse updateCanaryProfile(Long userId, CanaryProfileUpdateRequest request, OAuthUserImpl oAuth2User) { + authenticateAndAuthorize(oAuth2User, userId); CanaryProfile existingProfile = canaryProfileRepository.findByMemberId(userId) .orElseThrow(() -> new CustomException(ErrorCode.CANARY_NOT_FOUND)); @@ -92,9 +91,8 @@ public CanaryProfileResponse updateCanaryProfile(Long userId, CanaryProfileUpdat } - private Member authenticateAndAuthorize(String currentUserEmail, Long userId) { - Member member = memberRepository.findByEmailAndActiveTrue(currentUserEmail) - .orElseThrow(() -> new CustomException(MEMBER_NOT_FOUND)); + private Member authenticateAndAuthorize(OAuthUserImpl oAuth2User, Long userId) { + Member member = SecurityUtils.getCurrentMember(oAuth2User); if (member.getId() != userId) { throw new CustomException(MEMBER_FORBIDDEN); @@ -104,8 +102,8 @@ private Member authenticateAndAuthorize(String currentUserEmail, Long userId) { } @Transactional(readOnly = true) - public CanaryGetDeliveryInfoResponse getCanaryDeliveryInfo(Long memberId) { - CanaryProfile canary = canaryProfileRepository.findByMemberId(memberId) + public CanaryGetDeliveryInfoResponse getCanaryDeliveryInfo(Long userId) { + CanaryProfile canary = canaryProfileRepository.findCanaryProfileByMemberId(userId) .orElseThrow(() -> new CustomException(ErrorCode.CANARY_NOT_FOUND)); return new CanaryGetDeliveryInfoResponse( canary.getAddress(), diff --git a/src/main/java/com/fledge/fledgeserver/common/util/MemberUtil.java b/src/main/java/com/fledge/fledgeserver/common/utils/MemberUtil.java similarity index 80% rename from src/main/java/com/fledge/fledgeserver/common/util/MemberUtil.java rename to src/main/java/com/fledge/fledgeserver/common/utils/MemberUtil.java index 4c37086..991dae4 100644 --- a/src/main/java/com/fledge/fledgeserver/common/util/MemberUtil.java +++ b/src/main/java/com/fledge/fledgeserver/common/utils/MemberUtil.java @@ -1,4 +1,4 @@ -package com.fledge.fledgeserver.common.util; +package com.fledge.fledgeserver.common.utils; import com.fledge.fledgeserver.exception.CustomException; import com.fledge.fledgeserver.exception.ErrorCode; @@ -11,13 +11,13 @@ public class MemberUtil { /** * 현재 사용자의 OAuth2 ID를 반환합니다. */ - public static Long getMemberId(Principal principal) { + public static String getEmail(Principal principal) { // Principal 객체가 null이면 사용자가 인증되지 않았으므로 예외를 발생시킨다. if (principal == null) { throw new CustomException(ErrorCode.MEMBER_NOT_FOUND); } // Principal 객체의 이름을 memberId ID로 사용하여 반환한다. - return Long.valueOf(principal.getName()); + return principal.getName(); } } diff --git a/src/main/java/com/fledge/fledgeserver/common/utils/SecurityUtils.java b/src/main/java/com/fledge/fledgeserver/common/utils/SecurityUtils.java new file mode 100644 index 0000000..ee91145 --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/common/utils/SecurityUtils.java @@ -0,0 +1,71 @@ +package com.fledge.fledgeserver.common.utils; + +import com.fledge.fledgeserver.auth.dto.OAuthUserImpl; +import com.fledge.fledgeserver.exception.CustomException; +import com.fledge.fledgeserver.member.entity.Member; +import org.springframework.security.core.Authentication; + +import java.security.Principal; + +import static com.fledge.fledgeserver.exception.ErrorCode.MEMBER_NOT_FOUND; + +import org.springframework.security.core.context.SecurityContextHolder; + +public class SecurityUtils { + + // TODO : 인증객체 가져오는 방식 통일한 뒤 사용하지 않는 메소드 정리 + + public static OAuthUserImpl getCurrentUser(Principal principal) { + if (principal == null) { + throw new CustomException(MEMBER_NOT_FOUND); + } + + if (principal instanceof Authentication) { + Authentication authentication = (Authentication) principal; + Object userPrincipal = authentication.getPrincipal(); + + if (userPrincipal instanceof OAuthUserImpl) { + return (OAuthUserImpl) userPrincipal; + } + } + + throw new CustomException(MEMBER_NOT_FOUND); + } + + public static OAuthUserImpl getCurrentUser() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null || !authentication.isAuthenticated()) { + throw new CustomException(MEMBER_NOT_FOUND); + } + + Object principal = authentication.getPrincipal(); + if (principal instanceof OAuthUserImpl) { + return (OAuthUserImpl) principal; + } else { + throw new CustomException(MEMBER_NOT_FOUND); + } + } + + public static Long getCurrentUserId(Principal principal) { + return Long.valueOf(getCurrentUser(principal).getUsername()); + } + + public static Long getCurrentUserId() { + return Long.valueOf(getCurrentUser().getUsername()); + } + + public static Member getCurrentMember(OAuthUserImpl oAuthUser) { + if (oAuthUser == null) { + throw new CustomException(MEMBER_NOT_FOUND); + } + return oAuthUser.getMember(); + } + + public static Member getCurrentMember() { + if (getCurrentUser() == null) { + throw new CustomException(MEMBER_NOT_FOUND); + } + return getCurrentUser().getMember(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/common/util/TimeUtil.java b/src/main/java/com/fledge/fledgeserver/common/utils/TimeUtil.java similarity index 92% rename from src/main/java/com/fledge/fledgeserver/common/util/TimeUtil.java rename to src/main/java/com/fledge/fledgeserver/common/utils/TimeUtil.java index ed8bc3c..c103570 100644 --- a/src/main/java/com/fledge/fledgeserver/common/util/TimeUtil.java +++ b/src/main/java/com/fledge/fledgeserver/common/utils/TimeUtil.java @@ -1,4 +1,4 @@ -package com.fledge.fledgeserver.common.util; +package com.fledge.fledgeserver.common.utils; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/fledge/fledgeserver/member/controller/MemberController.java b/src/main/java/com/fledge/fledgeserver/member/controller/MemberController.java index 08bd377..f82326d 100644 --- a/src/main/java/com/fledge/fledgeserver/member/controller/MemberController.java +++ b/src/main/java/com/fledge/fledgeserver/member/controller/MemberController.java @@ -1,5 +1,6 @@ package com.fledge.fledgeserver.member.controller; +import com.fledge.fledgeserver.auth.dto.OAuthUserImpl; import com.fledge.fledgeserver.member.dto.MemberNicknameUpdateRequest; import com.fledge.fledgeserver.member.dto.MemberResponse; import com.fledge.fledgeserver.member.service.MemberService; @@ -7,18 +8,13 @@ import com.fledge.fledgeserver.response.SuccessStatus; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.beans.factory.annotation.Autowired; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; import io.swagger.v3.oas.annotations.Operation; -import static com.fledge.fledgeserver.response.SuccessStatus.CREATE_SUPPORT_SUCCESS; -import static com.fledge.fledgeserver.response.SuccessStatus.MEMBER_INFO_RETRIEVAL_SUCCESS; - @Tag(name = "회원 관리 API", description = "회원 관리와 관련된 API") @RestController @RequiredArgsConstructor @@ -31,8 +27,8 @@ public class MemberController { @Operation(summary = "현재 유저 정보 조회", description = "현재 인증된 유저의 정보를 조회합니다.") @GetMapping("/me") public ResponseEntity> memberInfo( - @AuthenticationPrincipal UserDetails userDetails) { - return ApiResponse.success(SuccessStatus.MEMBER_INFO_RETRIEVAL_SUCCESS, memberService.memberInfo(userDetails.getUsername())); + @AuthenticationPrincipal OAuthUserImpl oAuth2User) { + return ApiResponse.success(SuccessStatus.MEMBER_INFO_RETRIEVAL_SUCCESS, memberService.memberInfo(oAuth2User)); } @Operation(summary = "회원 상세 정보 조회", description = "회원 ID로 회원의 상세 정보를 조회합니다.") @@ -48,8 +44,8 @@ public ResponseEntity> getMemberDetails( public ResponseEntity> updateNickname( @Parameter(description = "회원 ID", required = true, example = "1") @PathVariable Long id, @Parameter(description = "닉네임 수정 요청", required = true) @RequestBody MemberNicknameUpdateRequest request, - @AuthenticationPrincipal UserDetails userDetails) { - MemberResponse memberResponse = memberService.updateNickname(id, request.getNickname(), userDetails.getUsername()); + @AuthenticationPrincipal OAuthUserImpl oAuth2User) { + MemberResponse memberResponse = memberService.updateNickname(id, request.getNickname(), oAuth2User); return ApiResponse.success(SuccessStatus.MEMBER_NICKNAME_UPDATE_SUCCESS, memberResponse); } -} +} \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/member/service/MemberService.java b/src/main/java/com/fledge/fledgeserver/member/service/MemberService.java index 2e4994a..2bbf6ca 100644 --- a/src/main/java/com/fledge/fledgeserver/member/service/MemberService.java +++ b/src/main/java/com/fledge/fledgeserver/member/service/MemberService.java @@ -1,5 +1,7 @@ package com.fledge.fledgeserver.member.service; +import com.fledge.fledgeserver.auth.dto.OAuthUserImpl; +import com.fledge.fledgeserver.common.utils.SecurityUtils; import com.fledge.fledgeserver.exception.CustomException; import com.fledge.fledgeserver.member.dto.MemberResponse; import com.fledge.fledgeserver.member.entity.Member; @@ -18,9 +20,9 @@ public class MemberService { private final MemberRepository memberRepository; @Transactional(readOnly = true) - public MemberResponse memberInfo(String email) { - Member member = memberRepository.findByEmailAndActiveTrue(email) - .orElseThrow(() -> new CustomException(MEMBER_NOT_FOUND)); + public MemberResponse memberInfo(OAuthUserImpl oAuth2User) { + Member member = SecurityUtils.getCurrentMember(oAuth2User); + return new MemberResponse(member); } @@ -28,14 +30,13 @@ public MemberResponse memberInfo(String email) { public MemberResponse getMemberDetails(Long memberId) { Member member = memberRepository.findById(memberId) .orElseThrow(() -> new CustomException(MEMBER_NOT_FOUND)); + return new MemberResponse(member); } @Transactional - public MemberResponse updateNickname(Long memberId, String newNickname, String currentUserEmail) { - - Member member = memberRepository.findByEmailAndActiveTrue(currentUserEmail) - .orElseThrow(() -> new CustomException(MEMBER_NOT_FOUND)); + public MemberResponse updateNickname(Long memberId, String newNickname, OAuthUserImpl oAuth2User) { + Member member = SecurityUtils.getCurrentMember(oAuth2User); if (member.getId() != memberId){ throw new CustomException(MEMBER_FORBIDDEN); @@ -45,4 +46,4 @@ public MemberResponse updateNickname(Long memberId, String newNickname, String c memberRepository.save(member); return new MemberResponse(member); } -} +} \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java b/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java index ff175d6..0b72c5c 100644 --- a/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java +++ b/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java @@ -1,6 +1,5 @@ package com.fledge.fledgeserver.support.controller; -import com.fledge.fledgeserver.common.util.MemberUtil; import com.fledge.fledgeserver.response.ApiResponse; import com.fledge.fledgeserver.support.dto.request.SupportCreateRequestDto; import com.fledge.fledgeserver.support.dto.response.SupportDetailGetResponseDto; @@ -26,8 +25,9 @@ public class SupportController { @Operation(summary = "후원하기 게시글 등록", description = "후원하기 게시글을 등록합니다.(자립 준비 청소년만)") @PostMapping public ResponseEntity> createSupport(Principal principal, @RequestBody SupportCreateRequestDto supportCreateRequestDto){ - Long memberId = MemberUtil.getMemberId(principal); - supportService.createSupport(memberId, supportCreateRequestDto); + System.out.println("Start"); +// Long memberId = MemberUtil.getMemberId(principal); +// supportService.createSupport(memberId, supportCreateRequestDto); return ApiResponse.success(CREATE_SUPPORT_SUCCESS); } diff --git a/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java b/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java index ad4a7af..ac7ccec 100644 --- a/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java +++ b/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java @@ -1,9 +1,12 @@ package com.fledge.fledgeserver.support.service; +import com.fledge.fledgeserver.canary.entity.CanaryProfile; import com.fledge.fledgeserver.canary.repository.CanaryProfileRepository; import com.fledge.fledgeserver.exception.CustomException; import com.fledge.fledgeserver.exception.ErrorCode; import com.fledge.fledgeserver.file.FileService; +import com.fledge.fledgeserver.member.entity.Member; +import com.fledge.fledgeserver.member.repository.MemberRepository; import com.fledge.fledgeserver.support.dto.request.SupportCreateRequestDto; import com.fledge.fledgeserver.support.dto.response.SupportDetailGetResponseDto; import com.fledge.fledgeserver.support.entity.Support; @@ -14,20 +17,22 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class SupportService { private final CanaryProfileRepository canaryProfileRepository; + private final MemberRepository memberRepository; private final SupportRepository supportRepository; private final FileService fileService; @Transactional public void createSupport(Long memberId, SupportCreateRequestDto supportCreateRequestDto) { // 자립 청소년인지 검증 -> canary_profile 테이블에 없으면 권한이 없는 것 - canaryProfileRepository.findCanaryProfileByMemberId(memberId) - .orElseThrow(() -> new CustomException(ErrorCode.UNAUTHORIZED_MEMBER)); + canaryProfileRepository.findByMemberId(memberId) + .orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND)); Support support = Support.builder() @@ -55,6 +60,7 @@ public void createSupport(Long memberId, SupportCreateRequestDto supportCreateRe .build(); support.getImages().add(supportImage); } + System.out.println("done"); } @Transactional(readOnly = true) From 1f3d22822c32a6adedb049607fe274814a7c79d5 Mon Sep 17 00:00:00 2001 From: JuseungL <121665437+JuseungL@users.noreply.github.com> Date: Wed, 31 Jul 2024 15:36:51 +0900 Subject: [PATCH 3/4] =?UTF-8?q?FEAT=20:=20=ED=9B=84=EC=9B=90=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D,=20=EC=88=98=EC=A0=95,=20=EB=B0=B0=EC=86=A1=EC=A7=80?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CanaryProfileController.java | 14 ++- .../dto/CanaryGetDeliveryInfoResponse.java | 12 +- .../dto/CanaryProfileUpdateRequest.java | 5 + .../canary/entity/CanaryProfile.java | 7 +- .../repository/CanaryProfileRepository.java | 1 - .../canary/service/CanaryProfileService.java | 3 +- .../fledgeserver/common/utils/MemberUtil.java | 24 ---- .../common/utils/SecurityUtils.java | 2 +- .../utils/{TimeUtil.java => TimeUtils.java} | 11 +- .../config/WebSecurityConfig.java | 4 +- .../fledgeserver/exception/ErrorCode.java | 4 +- .../member/controller/MemberController.java | 2 +- .../member/repository/MemberRepository.java | 2 + .../member/service/MemberService.java | 2 +- .../fledgeserver/response/SuccessStatus.java | 2 + .../support/controller/SupportController.java | 50 ++++++-- .../dto/request/SupportCreateRequestDto.java | 25 ++-- .../dto/request/SupportUpdateRequestDto.java | 82 +++++++++++++ .../response/SupportDetailGetResponseDto.java | 56 --------- .../SupportGetForUpdateResponseDto.java | 59 +++++++++ .../dto/response/SupportGetResponseDto.java | 61 ++++++++++ .../support/entity/ExpirationStatus.java | 16 --- .../fledgeserver/support/entity/Support.java | 64 ++++++---- .../support/repository/SupportRepository.java | 12 +- .../support/service/SupportService.java | 115 ++++++++++++------ 25 files changed, 436 insertions(+), 199 deletions(-) delete mode 100644 src/main/java/com/fledge/fledgeserver/common/utils/MemberUtil.java rename src/main/java/com/fledge/fledgeserver/common/utils/{TimeUtil.java => TimeUtils.java} (61%) create mode 100644 src/main/java/com/fledge/fledgeserver/support/dto/request/SupportUpdateRequestDto.java delete mode 100644 src/main/java/com/fledge/fledgeserver/support/dto/response/SupportDetailGetResponseDto.java create mode 100644 src/main/java/com/fledge/fledgeserver/support/dto/response/SupportGetForUpdateResponseDto.java create mode 100644 src/main/java/com/fledge/fledgeserver/support/dto/response/SupportGetResponseDto.java delete mode 100644 src/main/java/com/fledge/fledgeserver/support/entity/ExpirationStatus.java diff --git a/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java b/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java index 34e1c06..ddb5913 100644 --- a/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java +++ b/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java @@ -7,6 +7,7 @@ import com.fledge.fledgeserver.canary.dto.CanaryProfileUpdateRequest; import com.fledge.fledgeserver.canary.service.CanaryProfileService; import com.fledge.fledgeserver.common.utils.SecurityUtils; +import com.fledge.fledgeserver.member.entity.Member; import com.fledge.fledgeserver.response.ApiResponse; import com.fledge.fledgeserver.response.SuccessStatus; import io.swagger.v3.oas.annotations.responses.ApiResponses; @@ -22,7 +23,6 @@ import java.security.Principal; - @Tag(name = "자립준비청년 API", description = "자립준비청년 관리 관련 API") @RestController @RequiredArgsConstructor @@ -73,10 +73,12 @@ public ResponseEntity> updateCanaryProfile( @Operation(summary = "자립준비청년 배송지 정보 조회", description = "자립준비청년 후원글 작성 시 배송지 정보를 불러올 수 있습니다.") @GetMapping("/delivery") - public ResponseEntity> getCanaryDeliveryInfo(@AuthenticationPrincipal OAuthUserImpl oAuth2User) { - Long userId = oAuth2User.getMember().getId(); - System.out.println("userId = " + userId); - - return ApiResponse.success(SuccessStatus.DELIVERY_INFO_GET_SUCCESS, canaryProfileService.getCanaryDeliveryInfo(userId)); + public ResponseEntity> getCanaryDeliveryInfo( + Principal principal + ) + { + Long memberId = SecurityUtils.getCurrentUserId(principal); + System.out.println("memberId = " + memberId); + return ApiResponse.success(SuccessStatus.DELIVERY_INFO_GET_SUCCESS, canaryProfileService.getCanaryDeliveryInfo(memberId)); } } \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/canary/dto/CanaryGetDeliveryInfoResponse.java b/src/main/java/com/fledge/fledgeserver/canary/dto/CanaryGetDeliveryInfoResponse.java index 3ecab74..cd83b83 100644 --- a/src/main/java/com/fledge/fledgeserver/canary/dto/CanaryGetDeliveryInfoResponse.java +++ b/src/main/java/com/fledge/fledgeserver/canary/dto/CanaryGetDeliveryInfoResponse.java @@ -1,12 +1,17 @@ package com.fledge.fledgeserver.canary.dto; -import lombok.Getter; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Getter; @Getter @Schema(description = "자립준비청년 배송지 정보 조회 응답 DTO") public class CanaryGetDeliveryInfoResponse { + @Schema(description = "실명", example = "홍길동") + private String name; + @Schema(description = "거주지", example = "서울특별시 강남구 역삼동") private String address; @@ -20,10 +25,11 @@ public class CanaryGetDeliveryInfoResponse { private String phone; - public CanaryGetDeliveryInfoResponse(String phone, String address, String detailAddress, String zip) { - this.phone = phone; + public CanaryGetDeliveryInfoResponse(String name, String address, String detailAddress, String zip, String phone) { + this.name = name; this.address = address; this.detailAddress = detailAddress; this.zip = zip; + this.phone = phone; } } diff --git a/src/main/java/com/fledge/fledgeserver/canary/dto/CanaryProfileUpdateRequest.java b/src/main/java/com/fledge/fledgeserver/canary/dto/CanaryProfileUpdateRequest.java index 6dd9e1f..685bb46 100644 --- a/src/main/java/com/fledge/fledgeserver/canary/dto/CanaryProfileUpdateRequest.java +++ b/src/main/java/com/fledge/fledgeserver/canary/dto/CanaryProfileUpdateRequest.java @@ -15,6 +15,11 @@ @Schema(description = "자립준비청년 프로필 수정 요청 DTO") public class CanaryProfileUpdateRequest { + @Schema(description = "실명", required = true, example = "홍길동") + @NotBlank(message = "실명은 필수입니다.") + @Size(max = 10, message = "실명은 최대 10자까지 입력 가능합니다.") + private String name; + @Schema(description = "전화번호", required = true, example = "010-1234-5678") @NotBlank(message = "전화번호는 필수입니다.") @Size(max = 20, message = "전화번호는 최대 20자까지 입력 가능합니다.") diff --git a/src/main/java/com/fledge/fledgeserver/canary/entity/CanaryProfile.java b/src/main/java/com/fledge/fledgeserver/canary/entity/CanaryProfile.java index 0811739..455eec1 100644 --- a/src/main/java/com/fledge/fledgeserver/canary/entity/CanaryProfile.java +++ b/src/main/java/com/fledge/fledgeserver/canary/entity/CanaryProfile.java @@ -24,6 +24,9 @@ public class CanaryProfile extends BaseTimeEntity { @JoinColumn(name = "fk_user_id", nullable = false) private Member member; + @Column(nullable = false) + private String name; + @Column(nullable = false) private String phone; @@ -62,10 +65,11 @@ public class CanaryProfile extends BaseTimeEntity { private Double longitude; @Builder - public CanaryProfile(Member member, String phone, Date birth, Boolean gender, String address, String detailAddress, + public CanaryProfile(Member member, String name, String phone, Date birth, Boolean gender, String address, String detailAddress, String zip, String certificateFilePath, String interestArea, Boolean approvalStatus, Double latitude, Double longitude) { this.member = member; + this.name = name; this.phone = phone; this.birth = birth; this.gender = gender; @@ -81,6 +85,7 @@ public CanaryProfile(Member member, String phone, Date birth, Boolean gender, St } public void update(CanaryProfileUpdateRequest request) { + this.name = request.getName(); this.phone = request.getPhone(); this.birth = request.getBirth(); this.gender = request.getGender(); diff --git a/src/main/java/com/fledge/fledgeserver/canary/repository/CanaryProfileRepository.java b/src/main/java/com/fledge/fledgeserver/canary/repository/CanaryProfileRepository.java index 54a74ee..23b820c 100644 --- a/src/main/java/com/fledge/fledgeserver/canary/repository/CanaryProfileRepository.java +++ b/src/main/java/com/fledge/fledgeserver/canary/repository/CanaryProfileRepository.java @@ -12,5 +12,4 @@ public interface CanaryProfileRepository extends JpaRepository findByMemberId(Long memberId); Optional findCanaryProfileByMemberId(Long memberId); - } diff --git a/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java b/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java index 2c732c0..69722c9 100644 --- a/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java +++ b/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java @@ -11,7 +11,6 @@ import com.fledge.fledgeserver.exception.CustomException; import com.fledge.fledgeserver.exception.ErrorCode; import com.fledge.fledgeserver.member.entity.Member; -import com.fledge.fledgeserver.member.repository.MemberRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -24,7 +23,6 @@ public class CanaryProfileService { private final CanaryProfileRepository canaryProfileRepository; - private final MemberRepository memberRepository; @Transactional public void createCanaryProfile(CanaryProfileRequest request, OAuthUserImpl oAuth2User) { @@ -106,6 +104,7 @@ public CanaryGetDeliveryInfoResponse getCanaryDeliveryInfo(Long userId) { CanaryProfile canary = canaryProfileRepository.findCanaryProfileByMemberId(userId) .orElseThrow(() -> new CustomException(ErrorCode.CANARY_NOT_FOUND)); return new CanaryGetDeliveryInfoResponse( + canary.getName(), canary.getAddress(), canary.getDetailAddress(), canary.getZip(), diff --git a/src/main/java/com/fledge/fledgeserver/common/utils/MemberUtil.java b/src/main/java/com/fledge/fledgeserver/common/utils/MemberUtil.java deleted file mode 100644 index 991dae4..0000000 --- a/src/main/java/com/fledge/fledgeserver/common/utils/MemberUtil.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.fledge.fledgeserver.common.utils; - -import com.fledge.fledgeserver.exception.CustomException; -import com.fledge.fledgeserver.exception.ErrorCode; -import lombok.RequiredArgsConstructor; - -import java.security.Principal; - -@RequiredArgsConstructor -public class MemberUtil { - /** - * 현재 사용자의 OAuth2 ID를 반환합니다. - */ - public static String getEmail(Principal principal) { - // Principal 객체가 null이면 사용자가 인증되지 않았으므로 예외를 발생시킨다. - if (principal == null) { - throw new CustomException(ErrorCode.MEMBER_NOT_FOUND); - } - // Principal 객체의 이름을 memberId ID로 사용하여 반환한다. - return principal.getName(); - } -} - - diff --git a/src/main/java/com/fledge/fledgeserver/common/utils/SecurityUtils.java b/src/main/java/com/fledge/fledgeserver/common/utils/SecurityUtils.java index ee91145..91f54df 100644 --- a/src/main/java/com/fledge/fledgeserver/common/utils/SecurityUtils.java +++ b/src/main/java/com/fledge/fledgeserver/common/utils/SecurityUtils.java @@ -68,4 +68,4 @@ public static Member getCurrentMember() { return getCurrentUser().getMember(); } -} \ No newline at end of file +} diff --git a/src/main/java/com/fledge/fledgeserver/common/utils/TimeUtil.java b/src/main/java/com/fledge/fledgeserver/common/utils/TimeUtils.java similarity index 61% rename from src/main/java/com/fledge/fledgeserver/common/utils/TimeUtil.java rename to src/main/java/com/fledge/fledgeserver/common/utils/TimeUtils.java index c103570..92af39b 100644 --- a/src/main/java/com/fledge/fledgeserver/common/utils/TimeUtil.java +++ b/src/main/java/com/fledge/fledgeserver/common/utils/TimeUtils.java @@ -2,18 +2,19 @@ import lombok.RequiredArgsConstructor; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @RequiredArgsConstructor -public class TimeUtil { - public static String refineTime(LocalDateTime localDateTime) { +public class TimeUtils { + public static String refineToDateTime(LocalDateTime localDateTime) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); return localDateTime.format(formatter); } - public static String refineTimeMemberDetail(LocalDateTime localDateTime) { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); - return localDateTime.format(formatter); + public static String refineToDate(LocalDate localDate) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일"); + return localDate.format(formatter); } } \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/config/WebSecurityConfig.java b/src/main/java/com/fledge/fledgeserver/config/WebSecurityConfig.java index bb67cf1..9196bf6 100644 --- a/src/main/java/com/fledge/fledgeserver/config/WebSecurityConfig.java +++ b/src/main/java/com/fledge/fledgeserver/config/WebSecurityConfig.java @@ -56,7 +56,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti new AntPathRequestMatcher("/actuator/health"), new AntPathRequestMatcher("/oauth2/authorization/**"), new AntPathRequestMatcher("/login/oauth2/code/**"), - new AntPathRequestMatcher("/oauth2/**") + new AntPathRequestMatcher("/oauth2/**"), + new AntPathRequestMatcher("/api/v1/supports/**") ).permitAll() .anyRequest().authenticated() ) @@ -82,7 +83,6 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti public WebSecurityCustomizer webSecurityCustomizer() { return web -> web.ignoring() .requestMatchers("/error", "/favicon.ico", "/swagger-ui/**", "/api-docs/**"); - } diff --git a/src/main/java/com/fledge/fledgeserver/exception/ErrorCode.java b/src/main/java/com/fledge/fledgeserver/exception/ErrorCode.java index aa4e304..60e9800 100644 --- a/src/main/java/com/fledge/fledgeserver/exception/ErrorCode.java +++ b/src/main/java/com/fledge/fledgeserver/exception/ErrorCode.java @@ -32,8 +32,8 @@ public enum ErrorCode { DUPLICATE_APPLICATION(HttpStatus.CONFLICT, "이미 신청된 유저입니다."), // support - SUPPORT_NOT_FOUND(NOT_FOUND, "후원하기 게시글을 찾을 수 없습니다."), - UNAUTHORIZED_MEMBER(UNAUTHORIZED, "후원하기 게시글에 대한 권한이 없습니다. 자립 준비 청소년 인증이 필요합니다."); + UNAUTHORIZED_REQUEST(UNAUTHORIZED, "권한이 없습니다."), + SUPPORT_NOT_FOUND(NOT_FOUND, "후원 요청 게시글을 찾을 수 없습니다."); private final HttpStatus httpStatus; private final String message; diff --git a/src/main/java/com/fledge/fledgeserver/member/controller/MemberController.java b/src/main/java/com/fledge/fledgeserver/member/controller/MemberController.java index f82326d..866d175 100644 --- a/src/main/java/com/fledge/fledgeserver/member/controller/MemberController.java +++ b/src/main/java/com/fledge/fledgeserver/member/controller/MemberController.java @@ -48,4 +48,4 @@ public ResponseEntity> updateNickname( MemberResponse memberResponse = memberService.updateNickname(id, request.getNickname(), oAuth2User); return ApiResponse.success(SuccessStatus.MEMBER_NICKNAME_UPDATE_SUCCESS, memberResponse); } -} \ No newline at end of file +} diff --git a/src/main/java/com/fledge/fledgeserver/member/repository/MemberRepository.java b/src/main/java/com/fledge/fledgeserver/member/repository/MemberRepository.java index ae790c8..01e56b2 100644 --- a/src/main/java/com/fledge/fledgeserver/member/repository/MemberRepository.java +++ b/src/main/java/com/fledge/fledgeserver/member/repository/MemberRepository.java @@ -8,5 +8,7 @@ public interface MemberRepository extends JpaRepository { Optional findBySocialId(Long socialId); Optional findByEmailAndActiveTrue(String email); + + Optional findMemberById(Long memberId); } diff --git a/src/main/java/com/fledge/fledgeserver/member/service/MemberService.java b/src/main/java/com/fledge/fledgeserver/member/service/MemberService.java index 2bbf6ca..58849ba 100644 --- a/src/main/java/com/fledge/fledgeserver/member/service/MemberService.java +++ b/src/main/java/com/fledge/fledgeserver/member/service/MemberService.java @@ -46,4 +46,4 @@ public MemberResponse updateNickname(Long memberId, String newNickname, OAuthUse memberRepository.save(member); return new MemberResponse(member); } -} \ No newline at end of file +} diff --git a/src/main/java/com/fledge/fledgeserver/response/SuccessStatus.java b/src/main/java/com/fledge/fledgeserver/response/SuccessStatus.java index 22750d0..8ab9629 100644 --- a/src/main/java/com/fledge/fledgeserver/response/SuccessStatus.java +++ b/src/main/java/com/fledge/fledgeserver/response/SuccessStatus.java @@ -21,6 +21,8 @@ public enum SuccessStatus { */ CREATE_SUPPORT_SUCCESS(HttpStatus.CREATED, "후원하기 게시글 등록 성공"), GET_SUPPORT_SUCCESS(HttpStatus.OK, "후원하기 상세 페이지 조회 성공"), + GET_SUPPORT_FOR_UPDATE_SUCCESS(HttpStatus.OK, "수정을 위한 후원하기 조회 성공"), + UPDATE_SUPPORT_SUCCESS(HttpStatus.OK, "후원하기 게시글 업데이트 성공"), /** * canary diff --git a/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java b/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java index 0b72c5c..6185a13 100644 --- a/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java +++ b/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java @@ -1,8 +1,12 @@ package com.fledge.fledgeserver.support.controller; +import com.fledge.fledgeserver.common.utils.SecurityUtils; import com.fledge.fledgeserver.response.ApiResponse; +import com.fledge.fledgeserver.response.SuccessStatus; import com.fledge.fledgeserver.support.dto.request.SupportCreateRequestDto; -import com.fledge.fledgeserver.support.dto.response.SupportDetailGetResponseDto; +import com.fledge.fledgeserver.support.dto.request.SupportUpdateRequestDto; +import com.fledge.fledgeserver.support.dto.response.SupportGetForUpdateResponseDto; +import com.fledge.fledgeserver.support.dto.response.SupportGetResponseDto; import com.fledge.fledgeserver.support.service.SupportService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -24,23 +28,49 @@ public class SupportController { @Operation(summary = "후원하기 게시글 등록", description = "후원하기 게시글을 등록합니다.(자립 준비 청소년만)") @PostMapping - public ResponseEntity> createSupport(Principal principal, @RequestBody SupportCreateRequestDto supportCreateRequestDto){ - System.out.println("Start"); -// Long memberId = MemberUtil.getMemberId(principal); -// supportService.createSupport(memberId, supportCreateRequestDto); + public ResponseEntity> createSupport( + Principal principal, + @RequestBody SupportCreateRequestDto supportCreateRequestDto + ) { + Long memberId = SecurityUtils.getCurrentUserId(principal); + supportService.createSupport(memberId, supportCreateRequestDto); return ApiResponse.success(CREATE_SUPPORT_SUCCESS); } - @Operation(summary = "후원하기 게시글 상세 페이지 조회", description = "후원하기 게시글 상세 페이지 조회입니다.") + @Operation(summary = "후원하기 게시글 조회", description = "후원하기 게시글을 조회합니다.(모든 회원 가능)") @GetMapping("/{supportId}") - public ResponseEntity> getSupport(@PathVariable Long supportId) { + public ResponseEntity> getSupport( + @PathVariable(value = "supportId") Long supportId + ) { + // TODO :: 후원하기(후원자) & 후원 인증 관련 로직 추가 return ApiResponse.success(GET_SUPPORT_SUCCESS, supportService.getSupport(supportId)); } - // TODO :: 임박한 후원글 D-7까지 4개씩 최대 20개 - + @Operation(summary = "후원하기 게시글 수정 시 기존 데이터 조회", description = "후원하기 게시글의 기존 데이터를 반환합니다.") + @GetMapping("/{supportId}/update") + public ResponseEntity> getSupportForUpdate( + @PathVariable(value = "supportId") Long supportId, + Principal principal + ) { + Long memberId = SecurityUtils.getCurrentUserId(principal); + return ApiResponse.success( + SuccessStatus.GET_SUPPORT_FOR_UPDATE_SUCCESS, + supportService.getSupportForUpdate(memberId, supportId) + ); + } - // TODO :: 후원글 목록 + @Operation(summary = "후원하기 게시글 수정", description = "후원하기 게시글을 수정합니다.") + @PutMapping("/{supportId}") + public ResponseEntity> updateSupport( + Principal principal, + @PathVariable(value = "supportId") Long supportId, + @RequestBody SupportUpdateRequestDto supportUpdateRequestDto + ) { + Long memberId = SecurityUtils.getCurrentUserId(principal); + supportService.updateSupport(memberId, supportId, supportUpdateRequestDto); + return ApiResponse.success(SuccessStatus.UPDATE_SUPPORT_SUCCESS); + } + // TODO :: 후원하기 게시글 삭제 API } diff --git a/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportCreateRequestDto.java b/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportCreateRequestDto.java index 6874745..9dd037a 100644 --- a/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportCreateRequestDto.java +++ b/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportCreateRequestDto.java @@ -7,7 +7,6 @@ import org.hibernate.validator.constraints.URL; import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.List; @Getter @@ -58,19 +57,23 @@ public class SupportCreateRequestDto { @Future(message = "만료 시점은 현재 시간 이후여야 합니다.") private LocalDate expirationDate; - @Schema(description = "거주지", example = "서울특별시 강남구 역삼동") + @Schema(description = "수령인 이름", required = true, example = "홍길동") + @NotBlank(message = "수령인 이름은 필수입니다.") + private String recipientName; + + @Schema(description = "전화번호", required = true, example = "010-1234-5678") + @NotBlank(message = "전화번호는 필수입니다.") + private String phone; + + @Schema(description = "주소", required = true, example = "서울특별시 노원구 공릉로232") + @NotBlank(message = "주소는 필수입니다.") private String address; - @Schema(description = "상세 주소", example = "123-45") + @Schema(description = "상세 주소", required = true, example = "OO빌라 101호") + @NotBlank(message = "상세 주소는 필수입니다.") private String detailAddress; - @Schema(description = "우편번호", example = "12345") + @Schema(description = "우편번호", required = true, example = "123456") + @NotBlank(message = "우편번호는 필수입니다.") private String zip; - - @Schema(description = "이름", example = "이길동") - private String name; - - @Schema(description = "전화번호", example = "010-1234-5678") - private String phone; - } diff --git a/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportUpdateRequestDto.java b/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportUpdateRequestDto.java new file mode 100644 index 0000000..ed858c7 --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportUpdateRequestDto.java @@ -0,0 +1,82 @@ +package com.fledge.fledgeserver.support.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Future; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.validator.constraints.URL; + +import java.time.LocalDate; +import java.util.List; + +@Getter +@Setter +@Schema(description = "후원하기 게시글 수정 DTO") +public class SupportUpdateRequestDto { + + @Schema(description = "후원 게시글 제목", required = true, example = "후원 요청") + @NotBlank(message = "제목은 필수입니다.") + @Size(max = 100, message = "제목은 최대 100자까지 입력 가능합니다.") + private String title; + + @Schema(description = "후원 필요한 이유", required = true, example = "자립을 위한 후원") + @NotBlank(message = "후원 사유는 필수입니다.") + @Size(max = 500, message = "후원 사유는 최대 500자까지 입력 가능합니다.") + private String reason; + + @Schema(description = "후원 물품 명", required = true, example = "노트북") + @NotBlank(message = "후원 물품 명은 필수입니다.") + @Size(max = 100, message = "후원 물품 명은 최대 100자까지 입력 가능합니다.") + private String item; + + @Schema(description = "구매 URL", required = true, example = "https://example.com/product/1") + @NotBlank(message = "구매 URL은 필수입니다.") + @URL(message = "유효한 URL 형식이어야 합니다.") + private String purchaseUrl; + + @Schema(description = "후원 물품 가격", required = true, example = "500000") + @NotBlank(message = "후원 물품 가격은 필수입니다.") + @Positive(message = "가격은 0보다 큰 값이어야 합니다.") + private int price; + + @Schema(description = "후원 물품 이미지 리스트", required = true) + private List images; + + @Schema(description = "후원 인증 기간", required = true, example = "30") + @NotBlank(message = "후원 인증 기간은 필수입니다.") + @Positive(message = "후원 인증 기간은 0보다 큰 값이어야 합니다.") + private int checkPeriod; + + @Schema(description = "후원 인증 횟수", required = true, example = "1") + @NotBlank(message = "후원 인증 횟수는 필수입니다.") + @Positive(message = "후원 인증 횟수는 0보다 큰 값이어야 합니다.") + private int checkCount; + + @Schema(description = "만료 시점", required = true, example = "2024-12-31") + @NotBlank(message = "만료 시점은 필수입니다.") + @Future(message = "만료 시점은 현재 시간 이후여야 합니다.") + private LocalDate expirationDate; + + @Schema(description = "수령인 이름", required = true, example = "홍길동") + @NotBlank(message = "수령인 이름은 필수입니다.") + private String recipientName; + + @Schema(description = "전화번호", required = true, example = "010-1234-5678") + @NotBlank(message = "전화번호는 필수입니다.") + private String phone; + + @Schema(description = "주소", required = true, example = "서울특별시 노원구 공릉로232") + @NotBlank(message = "주소는 필수입니다.") + private String address; + + @Schema(description = "상세 주소", required = true, example = "OO빌라 101호") + @NotBlank(message = "상세 주소는 필수입니다.") + private String detailAddress; + + @Schema(description = "우편번호", required = true, example = "123456") + @NotBlank(message = "우편번호는 필수입니다.") + private String zip; +} diff --git a/src/main/java/com/fledge/fledgeserver/support/dto/response/SupportDetailGetResponseDto.java b/src/main/java/com/fledge/fledgeserver/support/dto/response/SupportDetailGetResponseDto.java deleted file mode 100644 index 505191b..0000000 --- a/src/main/java/com/fledge/fledgeserver/support/dto/response/SupportDetailGetResponseDto.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.fledge.fledgeserver.support.dto.response; - -import com.fledge.fledgeserver.support.entity.Support; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Getter; - -import java.time.LocalDate; -import java.util.List; - -@Getter -@Schema(description = "후원하기 게시글 조회 DTO") -public class SupportDetailGetResponseDto { - - @Schema(description = "후원 게시글 ID", example = "1") - private Long id; - - @Schema(description = "후원 게시글 제목", example = "후원 요청") - private String title; - - @Schema(description = "후원 필요한 이유", example = "자립을 위한 후원") - private String reason; - - @Schema(description = "후원 물품 명", example = "노트북") - private String item; - - @Schema(description = "구매 URL", example = "https://example.com/product/1") - private String purchaseUrl; - - @Schema(description = "후원 물품 가격", example = "500000") - private int price; - - @Schema(description = "후원 물품 이미지 리스트") - private List images; // 이미지 URL을 String List로 표현 - - @Schema(description = "후원 인증 기간", example = "30") - private int checkPeriod; - - @Schema(description = "후원 인증 횟수", example = "1") - private int checkCount; - - @Schema(description = "만료 시점", example = "2024-12-31") - private LocalDate expirationDate; - - public SupportDetailGetResponseDto(Support support, List presignedImageUrl) { - this.id = support.getId(); - this.title = support.getTitle(); - this.reason = support.getReason(); - this.item = support.getItem(); - this.purchaseUrl = support.getPurchaseUrl(); - this.price = support.getPrice(); - this.images = presignedImageUrl; - this.checkPeriod = support.getCheckPeriod(); - this.checkCount = support.getCheckCount(); - this.expirationDate = support.getExpirationDate(); - } -} \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/support/dto/response/SupportGetForUpdateResponseDto.java b/src/main/java/com/fledge/fledgeserver/support/dto/response/SupportGetForUpdateResponseDto.java new file mode 100644 index 0000000..074d3db --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/support/dto/response/SupportGetForUpdateResponseDto.java @@ -0,0 +1,59 @@ +package com.fledge.fledgeserver.support.dto.response; + +import com.fledge.fledgeserver.common.utils.TimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +import java.time.LocalDate; +import java.util.List; + +@Getter +@Schema(description = "후원하기 게시글 수정 시 기존 데이터 조회 DTO") +public class SupportGetForUpdateResponseDto { + @Schema(description = "멤버 식별자(프로필 팝업 띄우기)", example = "2") + private Long memberId; + + @Schema(description = "작성자 닉네임", example = "카드값줘체리") + private String nickname; + + @Schema(description = "후원 게시글 제목", example = "후원 요청") + private String title; + + @Schema(description = "후원 필요한 이유", example = "자립을 위한 후원") + private String reason; + + @Schema(description = "후원 물품 명", example = "노트북") + private String item; + + @Schema(description = "구매 URL", example = "https://example.com/product/1") + private String purchaseUrl; + + @Schema(description = "후원 물품 가격", example = "500000") + private int price; + + @Schema(description = "후원 물품 이미지 리스트") + private List images; + + @Schema(description = "후원 인증 기간", example = "30") + private int checkPeriod; + + @Schema(description = "후원 인증 횟수", example = "1") + private int checkCount; + + @Schema(description = "후원 만료 시점", example = "2024-07-31") + private String expirationDate; + + public SupportGetForUpdateResponseDto(Long memberId, String nickname, String title, String reason, String item, String purchaseUrl, int price, List images, int checkPeriod, int checkCount, LocalDate expirationDate) { + this.memberId = memberId; + this.nickname = nickname; + this.title = title; + this.reason = reason; + this.item = item; + this.purchaseUrl = purchaseUrl; + this.price = price; + this.images = images; + this.checkPeriod = checkPeriod; + this.checkCount = checkCount; + this.expirationDate = TimeUtils.refineToDate(expirationDate); + } +} diff --git a/src/main/java/com/fledge/fledgeserver/support/dto/response/SupportGetResponseDto.java b/src/main/java/com/fledge/fledgeserver/support/dto/response/SupportGetResponseDto.java new file mode 100644 index 0000000..e96173f --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/support/dto/response/SupportGetResponseDto.java @@ -0,0 +1,61 @@ +package com.fledge.fledgeserver.support.dto.response; + +import com.fledge.fledgeserver.common.utils.TimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +import java.time.LocalDate; +import java.util.List; + +@Getter +@Schema(description = "후원하기 게시글 조회 DTO") +public class SupportGetResponseDto { + + @Schema(description = "멤버 식별자(프로필 팝업 띄우기)", example = "2") + private Long memberId; + +// @Schema(description = "", example = "") +@Schema(description = "작성자 닉네임", example = "카드값줘체리") + private String nickname; + + @Schema(description = "후원 게시글 제목", example = "후원 요청") + private String title; + + @Schema(description = "후원 필요한 이유", example = "자립을 위한 후원") + private String reason; + + @Schema(description = "후원 물품 명", example = "노트북") + private String item; + + @Schema(description = "구매 URL", example = "https://example.com/product/1") + private String purchaseUrl; + + @Schema(description = "후원 물품 가격", example = "500000") + private int price; + + @Schema(description = "후원 물품 이미지 리스트") + private List images; + + @Schema(description = "후원 인증 기간", example = "30") + private int checkPeriod; + + @Schema(description = "후원 인증 횟수", example = "1") + private int checkCount; + + @Schema(description = "후원 만료 시점", example = "2024-07-31") + private String expirationDate; + + public SupportGetResponseDto(Long memberId, String nickname, String title, String reason, String item, String purchaseUrl, int price, List images, int checkPeriod, int checkCount, LocalDate expirationDate) { + this.memberId = memberId; + this.nickname = nickname; + this.title = title; + this.reason = reason; + this.item = item; + this.purchaseUrl = purchaseUrl; + this.price = price; + this.images = images; + this.checkPeriod = checkPeriod; + this.checkCount = checkCount; + this.expirationDate = TimeUtils.refineToDate(expirationDate); + } +} diff --git a/src/main/java/com/fledge/fledgeserver/support/entity/ExpirationStatus.java b/src/main/java/com/fledge/fledgeserver/support/entity/ExpirationStatus.java deleted file mode 100644 index 597c35a..0000000 --- a/src/main/java/com/fledge/fledgeserver/support/entity/ExpirationStatus.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.fledge.fledgeserver.support.entity; - -public enum ExpirationStatus { - ACTIVE("ACTIVE"), - - EXPIRED("EXPIRED"); - private final String key; - - ExpirationStatus(String key) { - this.key = key; - } - - public String getKey() { - return key; - } -} \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/support/entity/Support.java b/src/main/java/com/fledge/fledgeserver/support/entity/Support.java index b45c529..b96509b 100644 --- a/src/main/java/com/fledge/fledgeserver/support/entity/Support.java +++ b/src/main/java/com/fledge/fledgeserver/support/entity/Support.java @@ -1,7 +1,9 @@ package com.fledge.fledgeserver.support.entity; import com.fledge.fledgeserver.common.entity.BaseTimeEntity; -import io.swagger.v3.oas.annotations.media.Schema; +import com.fledge.fledgeserver.member.entity.Member; +import com.fledge.fledgeserver.support.dto.request.SupportCreateRequestDto; +import com.fledge.fledgeserver.support.dto.request.SupportUpdateRequestDto; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Builder; @@ -9,7 +11,6 @@ import lombok.NoArgsConstructor; import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -22,6 +23,10 @@ public class Support extends BaseTimeEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") //댓글 작성자 id + private Member member; + @Column(nullable = false) private String title; @@ -47,10 +52,10 @@ public class Support extends BaseTimeEntity { private int checkCount; @Column(nullable = false) - private LocalDate expirationDate; + private String recipientName; @Column(nullable = false) - private ExpirationStatus expirationStatus = ExpirationStatus.ACTIVE; + private String phone; @Column(nullable = false) private String address; @@ -62,29 +67,46 @@ public class Support extends BaseTimeEntity { private String zip; @Column(nullable = false) - private String name; + private LocalDate expirationDate; @Column(nullable = false) - private String phone; + private Boolean expirationStatus = false; // TODO :: 챌린지 구현 후 참여 중이거나 완료한 챌린지(뱃지)에 대한 로직 추가 @Builder - public Support(String title, String reason, String item, String purchaseUrl, int price, List images, int checkPeriod, int checkCount, LocalDate expirationDate, String address, String detailAddress, String zip, String name, String phone) { - this.title = title; - this.reason = reason; - this.item = item; - this.purchaseUrl = purchaseUrl; - this.price = price; - this.images = images; - this.checkPeriod = checkPeriod; - this.checkCount = checkCount; - this.expirationDate = expirationDate; - this.address = address; - this.detailAddress = detailAddress; - this.zip = zip; - this.name = name; - this.phone = phone; + public Support(Member member, SupportCreateRequestDto supportCreateRequestDto) { + this.member = member; + this.title = supportCreateRequestDto.getTitle(); + this.reason = supportCreateRequestDto.getReason(); + this.item = supportCreateRequestDto.getItem(); + this.purchaseUrl = supportCreateRequestDto.getPurchaseUrl(); + this.price = supportCreateRequestDto.getPrice(); + this.checkPeriod = supportCreateRequestDto.getCheckPeriod(); + this.checkCount = supportCreateRequestDto.getCheckCount(); + this.recipientName = supportCreateRequestDto.getRecipientName(); + this.phone = supportCreateRequestDto.getPhone(); + this.address = supportCreateRequestDto.getAddress(); + this.detailAddress = supportCreateRequestDto.getDetailAddress(); + this.zip = supportCreateRequestDto.getZip(); + this.expirationDate = supportCreateRequestDto.getExpirationDate(); + } + + public void update(SupportUpdateRequestDto supportUpdateRequestDto) { + this.title = supportUpdateRequestDto.getTitle(); + this.reason = supportUpdateRequestDto.getReason(); + this.item = supportUpdateRequestDto.getItem(); + this.purchaseUrl = supportUpdateRequestDto.getPurchaseUrl(); + this.price = supportUpdateRequestDto.getPrice(); + this.checkPeriod = supportUpdateRequestDto.getCheckPeriod(); + this.checkCount = supportUpdateRequestDto.getCheckCount(); + this.recipientName = supportUpdateRequestDto.getRecipientName(); + this.phone = supportUpdateRequestDto.getPhone(); + this.address = supportUpdateRequestDto.getAddress(); + this.detailAddress = supportUpdateRequestDto.getDetailAddress(); + this.zip = supportUpdateRequestDto.getZip(); + this.expirationDate = supportUpdateRequestDto.getExpirationDate(); } + } diff --git a/src/main/java/com/fledge/fledgeserver/support/repository/SupportRepository.java b/src/main/java/com/fledge/fledgeserver/support/repository/SupportRepository.java index a0b3be0..6ce104e 100644 --- a/src/main/java/com/fledge/fledgeserver/support/repository/SupportRepository.java +++ b/src/main/java/com/fledge/fledgeserver/support/repository/SupportRepository.java @@ -2,10 +2,18 @@ import com.fledge.fledgeserver.support.entity.Support; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.Optional; public interface SupportRepository extends JpaRepository { - Optional findSupportById(Long supportId); - + /** + * 한방 쿼리: Fetch Join + */ + @Query("SELECT s FROM Support s " + + "JOIN FETCH s.member m " + + "LEFT JOIN FETCH s.images i " + + "WHERE s.id = :supportId") + Optional findSupportByIdWithFetch(@Param("supportId") Long supportId); } diff --git a/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java b/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java index ac7ccec..2b9c3f6 100644 --- a/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java +++ b/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java @@ -1,56 +1,48 @@ package com.fledge.fledgeserver.support.service; -import com.fledge.fledgeserver.canary.entity.CanaryProfile; import com.fledge.fledgeserver.canary.repository.CanaryProfileRepository; import com.fledge.fledgeserver.exception.CustomException; import com.fledge.fledgeserver.exception.ErrorCode; import com.fledge.fledgeserver.file.FileService; import com.fledge.fledgeserver.member.entity.Member; +import com.fledge.fledgeserver.member.entity.Role; import com.fledge.fledgeserver.member.repository.MemberRepository; import com.fledge.fledgeserver.support.dto.request.SupportCreateRequestDto; -import com.fledge.fledgeserver.support.dto.response.SupportDetailGetResponseDto; +import com.fledge.fledgeserver.support.dto.request.SupportUpdateRequestDto; +import com.fledge.fledgeserver.support.dto.response.SupportGetForUpdateResponseDto; +import com.fledge.fledgeserver.support.dto.response.SupportGetResponseDto; import com.fledge.fledgeserver.support.entity.Support; import com.fledge.fledgeserver.support.entity.SupportImage; +import com.fledge.fledgeserver.support.repository.SupportImageRepository; import com.fledge.fledgeserver.support.repository.SupportRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; @Service +@Transactional @RequiredArgsConstructor public class SupportService { - private final CanaryProfileRepository canaryProfileRepository; private final MemberRepository memberRepository; + private final CanaryProfileRepository canaryProfileRepository; private final SupportRepository supportRepository; private final FileService fileService; + private final SupportImageRepository supportImageRepository; - @Transactional public void createSupport(Long memberId, SupportCreateRequestDto supportCreateRequestDto) { - // 자립 청소년인지 검증 -> canary_profile 테이블에 없으면 권한이 없는 것 - canaryProfileRepository.findByMemberId(memberId) + Member member = memberRepository.findMemberById(memberId) .orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND)); + if (member.getRole() != Role.CANARY) { + throw new CustomException(ErrorCode.UNAUTHORIZED_REQUEST); + } Support support = Support.builder() - .title(supportCreateRequestDto.getTitle()) - .reason(supportCreateRequestDto.getReason()) - .item((supportCreateRequestDto.getItem())) - .price(supportCreateRequestDto.getPrice()) - .purchaseUrl(supportCreateRequestDto.getPurchaseUrl()) - .checkPeriod(supportCreateRequestDto.getCheckPeriod()) - .checkCount(supportCreateRequestDto.getCheckCount()) - .expirationDate(supportCreateRequestDto.getExpirationDate()) - .address(supportCreateRequestDto.getAddress()) - .detailAddress(supportCreateRequestDto.getDetailAddress()) - .zip(supportCreateRequestDto.getZip()) - .name(supportCreateRequestDto.getName()) - .phone(supportCreateRequestDto.getPhone()) + .member(member) + .supportCreateRequestDto(supportCreateRequestDto) .build(); - supportRepository.save(support); for (String imageUrl : supportCreateRequestDto.getImages()) { @@ -60,23 +52,78 @@ public void createSupport(Long memberId, SupportCreateRequestDto supportCreateRe .build(); support.getImages().add(supportImage); } - System.out.println("done"); } - @Transactional(readOnly = true) - public SupportDetailGetResponseDto getSupport(Long supportId) { - // 후원 게시글 조회 - Support support = supportRepository.findSupportById(supportId) + public SupportGetResponseDto getSupport(Long supportId) { + Support support = supportRepository.findSupportByIdWithFetch(supportId) + .orElseThrow(() -> new CustomException(ErrorCode.SUPPORT_NOT_FOUND)); + + return new SupportGetResponseDto( + support.getMember().getId(), + support.getMember().getNickname(), + support.getTitle(), + support.getReason(), + support.getItem(), + support.getPurchaseUrl(), + support.getPrice(), + // Images Presigned-URL처리 + support.getImages().stream() + .map(supportImage -> fileService.getFileUrl(supportImage.getImageUrl())) + .toList(), + support.getCheckPeriod(), + support.getCheckCount(), + support.getExpirationDate() + ); + } + + public SupportGetForUpdateResponseDto getSupportForUpdate(Long memberId, Long supportId) { + Support support = supportRepository.findSupportByIdWithFetch(supportId) + .orElseThrow(() -> new CustomException(ErrorCode.SUPPORT_NOT_FOUND)); + + if (support.getMember().getId() != memberId) { + throw new CustomException(ErrorCode.NO_ACCESS); + } + + // 이미지를 Presigned URL로 처리 + List imageUrls = support.getImages().stream() + .map(supportImage -> fileService.getFileUrl(supportImage.getImageUrl())) + .toList(); + + return new SupportGetForUpdateResponseDto( + support.getMember().getId(), + support.getMember().getNickname(), + support.getTitle(), + support.getReason(), + support.getItem(), + support.getPurchaseUrl(), + support.getPrice(), + imageUrls, + support.getCheckPeriod(), + support.getCheckCount(), + support.getExpirationDate() + ); + } + + public void updateSupport(Long memberId, Long supportId, SupportUpdateRequestDto supportUpdateRequestDto) { + Support support = supportRepository.findSupportByIdWithFetch(supportId) .orElseThrow(() -> new CustomException(ErrorCode.SUPPORT_NOT_FOUND)); - // 후원 게시글의 이미지 URL -> Presigned URL로 변환 - List presignedImageUrl = support.getImages().stream() - .map(SupportImage::getImageUrl) - .map(fileService::getFileUrl) - .collect(Collectors.toList()); + if (support.getMember().getId() != memberId) { + throw new CustomException(ErrorCode.NO_ACCESS); + } + support.update(supportUpdateRequestDto); + support.getImages().clear(); + + List newImages = supportUpdateRequestDto.getImages().stream() + .map(imageUrl -> new SupportImage(support, imageUrl)) + .toList(); + + support.getImages().addAll(newImages); - // TODO :: 후원내역(SupportRecord)에 대한 내용 함께 반환 + // 5. 기존 이미지 삭제 + support.getImages().clear(); // 기존 이미지 제거 - return new SupportDetailGetResponseDto(support, presignedImageUrl); + // 6. 새로운 이미지 추가 + support.getImages().addAll(newImages); } } From 23fa59e00b2bba9af2aefbc335d552652e919910 Mon Sep 17 00:00:00 2001 From: JuseungL <121665437+JuseungL@users.noreply.github.com> Date: Fri, 2 Aug 2024 17:08:48 +0900 Subject: [PATCH 4/4] =?UTF-8?q?FEAT=20:=20=ED=9B=84=EC=9B=90=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=ED=95=98=EA=B8=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CanaryProfileController.java | 21 ++- .../dto/CanaryProfileGetResponseDto.java | 25 +++ .../canary/service/CanaryProfileService.java | 13 +- .../config/WebSecurityConfig.java | 5 +- .../fledgeserver/promise/entity/Promise.java | 14 ++ .../promise/entity/PromiseLog.java | 48 ++++++ .../promise/entity/PromiseStatus.java | 17 +++ .../support/controller/SupportController.java | 94 ++++++++---- .../dto/request/SupportCreateRequestDto.java | 45 +++--- .../fledgeserver/support/entity/Support.java | 66 +++++--- .../support/entity/SupportCategory.java | 19 +++ .../support/entity/SupportRecord.java | 28 +++- .../support/service/SupportService.java | 142 +++++++++--------- 13 files changed, 376 insertions(+), 161 deletions(-) create mode 100644 src/main/java/com/fledge/fledgeserver/canary/dto/CanaryProfileGetResponseDto.java create mode 100644 src/main/java/com/fledge/fledgeserver/promise/entity/Promise.java create mode 100644 src/main/java/com/fledge/fledgeserver/promise/entity/PromiseLog.java create mode 100644 src/main/java/com/fledge/fledgeserver/promise/entity/PromiseStatus.java create mode 100644 src/main/java/com/fledge/fledgeserver/support/entity/SupportCategory.java diff --git a/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java b/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java index ddb5913..82383fa 100644 --- a/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java +++ b/src/main/java/com/fledge/fledgeserver/canary/controller/CanaryProfileController.java @@ -1,15 +1,13 @@ package com.fledge.fledgeserver.canary.controller; import com.fledge.fledgeserver.auth.dto.OAuthUserImpl; -import com.fledge.fledgeserver.canary.dto.CanaryGetDeliveryInfoResponse; -import com.fledge.fledgeserver.canary.dto.CanaryProfileRequest; -import com.fledge.fledgeserver.canary.dto.CanaryProfileResponse; -import com.fledge.fledgeserver.canary.dto.CanaryProfileUpdateRequest; +import com.fledge.fledgeserver.canary.dto.*; import com.fledge.fledgeserver.canary.service.CanaryProfileService; import com.fledge.fledgeserver.common.utils.SecurityUtils; import com.fledge.fledgeserver.member.entity.Member; import com.fledge.fledgeserver.response.ApiResponse; import com.fledge.fledgeserver.response.SuccessStatus; +import com.fledge.fledgeserver.support.dto.response.SupportGetResponseDto; import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @@ -23,6 +21,8 @@ import java.security.Principal; +import static com.fledge.fledgeserver.response.SuccessStatus.GET_SUPPORT_SUCCESS; + @Tag(name = "자립준비청년 API", description = "자립준비청년 관리 관련 API") @RestController @RequiredArgsConstructor @@ -71,6 +71,10 @@ public ResponseEntity> updateCanaryProfile( return ApiResponse.success(SuccessStatus.PROFILE_UPDATE_SUCCESS, response); } + /** + * 이하 API 후원하기 시에 필요 + */ + @Operation(summary = "자립준비청년 배송지 정보 조회", description = "자립준비청년 후원글 작성 시 배송지 정보를 불러올 수 있습니다.") @GetMapping("/delivery") public ResponseEntity> getCanaryDeliveryInfo( @@ -81,4 +85,13 @@ public ResponseEntity> getCanaryDeliv System.out.println("memberId = " + memberId); return ApiResponse.success(SuccessStatus.DELIVERY_INFO_GET_SUCCESS, canaryProfileService.getCanaryDeliveryInfo(memberId)); } + + @Operation(summary = "후원하기 게시글 조회 시 자립준비청년 프로필 조회", description = "후원하기 게시글에서 자립준비청년 프로필을 조회합니다.") + @GetMapping("/{memberId}/supports") + public ResponseEntity> getSupport( + @PathVariable(value = "memberId") Long memberId + ) { + // TODO :: 자립준비청년이 완료한 챌린지 및 후원 인증 스토리 그리고 인증률도 함께 보여줘야함! + return ApiResponse.success(GET_SUPPORT_SUCCESS, canaryProfileService.getCanaryForSupport(memberId)); + } } \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/canary/dto/CanaryProfileGetResponseDto.java b/src/main/java/com/fledge/fledgeserver/canary/dto/CanaryProfileGetResponseDto.java new file mode 100644 index 0000000..46b9586 --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/canary/dto/CanaryProfileGetResponseDto.java @@ -0,0 +1,25 @@ +package com.fledge.fledgeserver.canary.dto; + +import com.fledge.fledgeserver.canary.entity.CanaryProfile; +import com.fledge.fledgeserver.member.entity.Member; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +import java.util.Date; + +@Getter +@Schema(description = "후원하기에서 자립준비청년 프로필 조회 응답 DTO") +public class CanaryProfileGetResponseDto { + + @Schema(description = "닉네임", example = "카드값줘체리") + private String nickname; + + @Schema(description = "자기 소개", example = "안녕하세요, 저는...") + private String introduction; + + + public CanaryProfileGetResponseDto(CanaryProfile canaryProfile) { + this.nickname = canaryProfile.getMember().getNickname(); + this.introduction = canaryProfile.getIntroduction(); + } +} diff --git a/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java b/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java index 69722c9..b52b0b1 100644 --- a/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java +++ b/src/main/java/com/fledge/fledgeserver/canary/service/CanaryProfileService.java @@ -1,10 +1,7 @@ package com.fledge.fledgeserver.canary.service; import com.fledge.fledgeserver.auth.dto.OAuthUserImpl; -import com.fledge.fledgeserver.canary.dto.CanaryGetDeliveryInfoResponse; -import com.fledge.fledgeserver.canary.dto.CanaryProfileRequest; -import com.fledge.fledgeserver.canary.dto.CanaryProfileResponse; -import com.fledge.fledgeserver.canary.dto.CanaryProfileUpdateRequest; +import com.fledge.fledgeserver.canary.dto.*; import com.fledge.fledgeserver.canary.entity.CanaryProfile; import com.fledge.fledgeserver.canary.repository.CanaryProfileRepository; import com.fledge.fledgeserver.common.utils.SecurityUtils; @@ -111,4 +108,12 @@ public CanaryGetDeliveryInfoResponse getCanaryDeliveryInfo(Long userId) { canary.getPhone() ); } + + @Transactional(readOnly = true) + public CanaryProfileGetResponseDto getCanaryForSupport(Long memberId) { + CanaryProfile canaryProfile = canaryProfileRepository.findByMemberId(memberId) + .orElseThrow(() -> new CustomException(ErrorCode.CANARY_NOT_FOUND)); + + return new CanaryProfileGetResponseDto(canaryProfile); + } } \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/config/WebSecurityConfig.java b/src/main/java/com/fledge/fledgeserver/config/WebSecurityConfig.java index 9196bf6..5c01d0f 100644 --- a/src/main/java/com/fledge/fledgeserver/config/WebSecurityConfig.java +++ b/src/main/java/com/fledge/fledgeserver/config/WebSecurityConfig.java @@ -56,8 +56,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti new AntPathRequestMatcher("/actuator/health"), new AntPathRequestMatcher("/oauth2/authorization/**"), new AntPathRequestMatcher("/login/oauth2/code/**"), - new AntPathRequestMatcher("/oauth2/**"), - new AntPathRequestMatcher("/api/v1/supports/**") + new AntPathRequestMatcher("/oauth2/**") ).permitAll() .anyRequest().authenticated() ) @@ -79,7 +78,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti return http.build(); } - @Bean + @Bean public WebSecurityCustomizer webSecurityCustomizer() { return web -> web.ignoring() .requestMatchers("/error", "/favicon.ico", "/swagger-ui/**", "/api-docs/**"); diff --git a/src/main/java/com/fledge/fledgeserver/promise/entity/Promise.java b/src/main/java/com/fledge/fledgeserver/promise/entity/Promise.java new file mode 100644 index 0000000..12812f3 --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/promise/entity/Promise.java @@ -0,0 +1,14 @@ +package com.fledge.fledgeserver.promise.entity; + + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public enum Promise { + ONCE("ONCE"), // 1회 인증 + WEEKLY("WEEKLY"), // 4주간 매 주 인증 + MONTHLY("MONTHLY"); // 3개월간 매 달 인증 + + private final String key; + public String getKey() { return key; } +} \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/promise/entity/PromiseLog.java b/src/main/java/com/fledge/fledgeserver/promise/entity/PromiseLog.java new file mode 100644 index 0000000..cde2607 --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/promise/entity/PromiseLog.java @@ -0,0 +1,48 @@ +package com.fledge.fledgeserver.promise.entity; + +import com.fledge.fledgeserver.common.entity.BaseTimeEntity; +import com.fledge.fledgeserver.member.entity.Member; +import com.fledge.fledgeserver.support.entity.Support; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class PromiseLog extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "support_id", nullable = false) + private Support support; + + @Column(nullable = false) + private LocalDate startDate; + + @Column(nullable = false) + private LocalDate endDate; + + @Column(nullable = false) + private PromiseStatus promiseStatus; + + @Builder + public PromiseLog(Member member, Support support, LocalDate startDate, LocalDate endDate, PromiseStatus promiseStatus) { + this.member = member; + this.support = support; + this.startDate = startDate; + this.endDate = endDate; + this.promiseStatus = promiseStatus; + } +} diff --git a/src/main/java/com/fledge/fledgeserver/promise/entity/PromiseStatus.java b/src/main/java/com/fledge/fledgeserver/promise/entity/PromiseStatus.java new file mode 100644 index 0000000..c795d1b --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/promise/entity/PromiseStatus.java @@ -0,0 +1,17 @@ +package com.fledge.fledgeserver.promise.entity; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public enum PromiseStatus { + PENDING("PENDING"), // 인증 대기중 + VERIFIED("VERIFIED"), // 인증 완료 + UNVERIFIED("UNVERIFIED"); // 인증하지 않음 + + + private final String key; + + public String getStatus() { + return key; + } +} diff --git a/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java b/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java index 6185a13..77ee4ef 100644 --- a/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java +++ b/src/main/java/com/fledge/fledgeserver/support/controller/SupportController.java @@ -25,7 +25,13 @@ @RequiredArgsConstructor public class SupportController { private final SupportService supportService; + /** + * ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ후원 게시글 관련ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ + */ + /** + * CREATE + */ @Operation(summary = "후원하기 게시글 등록", description = "후원하기 게시글을 등록합니다.(자립 준비 청소년만)") @PostMapping public ResponseEntity> createSupport( @@ -37,40 +43,64 @@ public ResponseEntity> createSupport( return ApiResponse.success(CREATE_SUPPORT_SUCCESS); } - @Operation(summary = "후원하기 게시글 조회", description = "후원하기 게시글을 조회합니다.(모든 회원 가능)") - @GetMapping("/{supportId}") - public ResponseEntity> getSupport( - @PathVariable(value = "supportId") Long supportId - ) { - // TODO :: 후원하기(후원자) & 후원 인증 관련 로직 추가 - return ApiResponse.success(GET_SUPPORT_SUCCESS, supportService.getSupport(supportId)); - } + /** + * READ + */ +// +// @Operation(summary = "후원하기 게시글 조회", description = "후원하기 게시글을 조회합니다.(모든 회원 가능)") +// @GetMapping("/{supportId}") +// public ResponseEntity> getSupport( +// @PathVariable(value = "supportId") Long supportId +// ) { +// // TODO :: 후원하기(후원자) & 후원 인증 관련 로직 추가 +// return ApiResponse.success(GET_SUPPORT_SUCCESS, supportService.getSupport(supportId)); +// } +// +// /** +// * UPDATE +// */ +// +// @Operation(summary = "후원하기 게시글 수정 시 기존 데이터 조회", description = "후원하기 게시글의 기존 데이터를 반환합니다.") +// @GetMapping("/{supportId}/update") +// public ResponseEntity> getSupportForUpdate( +// @PathVariable(value = "supportId") Long supportId, +// Principal principal +// ) { +// Long memberId = SecurityUtils.getCurrentUserId(principal); +// return ApiResponse.success( +// SuccessStatus.GET_SUPPORT_FOR_UPDATE_SUCCESS, +// supportService.getSupportForUpdate(memberId, supportId) +// ); +// } +// +// @Operation(summary = "후원하기 게시글 수정", description = "후원하기 게시글을 수정합니다.") +// @PutMapping("/{supportId}") +// public ResponseEntity> updateSupport( +// Principal principal, +// @PathVariable(value = "supportId") Long supportId, +// @RequestBody SupportUpdateRequestDto supportUpdateRequestDto +// ) { +// Long memberId = SecurityUtils.getCurrentUserId(principal); +// supportService.updateSupport(memberId, supportId, supportUpdateRequestDto); +// return ApiResponse.success(SuccessStatus.UPDATE_SUPPORT_SUCCESS); +// } - @Operation(summary = "후원하기 게시글 수정 시 기존 데이터 조회", description = "후원하기 게시글의 기존 데이터를 반환합니다.") - @GetMapping("/{supportId}/update") - public ResponseEntity> getSupportForUpdate( - @PathVariable(value = "supportId") Long supportId, - Principal principal - ) { - Long memberId = SecurityUtils.getCurrentUserId(principal); - return ApiResponse.success( - SuccessStatus.GET_SUPPORT_FOR_UPDATE_SUCCESS, - supportService.getSupportForUpdate(memberId, supportId) - ); - } + /** + * DELETE + */ + // TODO :: 후원하기 게시글 삭제 API - @Operation(summary = "후원하기 게시글 수정", description = "후원하기 게시글을 수정합니다.") - @PutMapping("/{supportId}") - public ResponseEntity> updateSupport( - Principal principal, - @PathVariable(value = "supportId") Long supportId, - @RequestBody SupportUpdateRequestDto supportUpdateRequestDto - ) { - Long memberId = SecurityUtils.getCurrentUserId(principal); - supportService.updateSupport(memberId, supportId, supportUpdateRequestDto); - return ApiResponse.success(SuccessStatus.UPDATE_SUPPORT_SUCCESS); - } + /** + * ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ후원 하기ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ + */ + /** + * 후원하기 팝업에서 정보 조회 + */ + + + /** + * 후원하기 시 후원 처리 + */ - // TODO :: 후원하기 게시글 삭제 API } diff --git a/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportCreateRequestDto.java b/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportCreateRequestDto.java index 9dd037a..48905fe 100644 --- a/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportCreateRequestDto.java +++ b/src/main/java/com/fledge/fledgeserver/support/dto/request/SupportCreateRequestDto.java @@ -24,6 +24,12 @@ public class SupportCreateRequestDto { @Size(max = 500, message = "후원 사유는 최대 500자까지 입력 가능합니다.") private String reason; + @Schema(description = "후원자의 약속", required = true, example = "ONCE") + @NotBlank(message = "후원자의 약속은 필수입니다.") + @Pattern(regexp = "ONCE|WEEKLY|MONTHLY", + message = "후원자의 약속은 ONCE, WEEKLY, MONTHLY 중 하나여야 합니다.") + private String promise; + @Schema(description = "후원 물품 명", required = true, example = "노트북") @NotBlank(message = "후원 물품 명은 필수입니다.") @Size(max = 100, message = "후원 물품 명은 최대 100자까지 입력 가능합니다.") @@ -42,38 +48,37 @@ public class SupportCreateRequestDto { @Schema(description = "후원 물품 이미지 리스트", required = true) private List images; - @Schema(description = "후원 인증 기간", required = true, example = "30") - @NotBlank(message = "후원 인증 기간은 필수입니다.") - @Positive(message = "후원 인증 기간은 0보다 큰 값이어야 합니다.") - private int checkPeriod; - - @Schema(description = "후원 인증 횟수", required = true, example = "1") - @NotBlank(message = "후원 인증 횟수는 필수입니다.") - @Positive(message = "후원 인증 횟수는 0보다 큰 값이어야 합니다.") - private int checkCount; - @Schema(description = "만료 시점", required = true, example = "2024-12-31") @NotBlank(message = "만료 시점은 필수입니다.") @Future(message = "만료 시점은 현재 시간 이후여야 합니다.") private LocalDate expirationDate; - @Schema(description = "수령인 이름", required = true, example = "홍길동") - @NotBlank(message = "수령인 이름은 필수입니다.") + @Schema(description = "후원 카테고리", example = "FOOD") + @NotBlank(message = "후원 카테고리는 필수입니다.") + @Pattern(regexp = "DAILY_NECESSITY|FOOD|HOME_APPLIANCES|EDUCATION|MEDICAL|LEGAL_AID|ETC", + message = "후원 카테고리는 DAILY_NECESSITY, FOOD, HOME_APPLIANCES, EDUCATION, MEDICAL, LEGAL_AID, ETC 중 하나여야 합니다.") + private String supportCategory; + + // MEDICAL, LEGAL_AID인 겨우 + @Schema(description = "은행명", example = "카카오뱅크") + private String bank; + + @Schema(description = "은행 계좌번호", example = "1234-12-1234-12") + private String account; + + // DAILY_NECESSITY, FOOD, HOME_APPLIANCES, EDUCATION, ETC인 경우 + @Schema(description = "수령인 이름", example = "홍길동") private String recipientName; - @Schema(description = "전화번호", required = true, example = "010-1234-5678") - @NotBlank(message = "전화번호는 필수입니다.") + @Schema(description = "전화번호", example = "010-1234-5678") private String phone; - @Schema(description = "주소", required = true, example = "서울특별시 노원구 공릉로232") - @NotBlank(message = "주소는 필수입니다.") + @Schema(description = "주소", example = "서울특별시 노원구 공릉로232") private String address; - @Schema(description = "상세 주소", required = true, example = "OO빌라 101호") - @NotBlank(message = "상세 주소는 필수입니다.") + @Schema(description = "상세 주소", example = "OO빌라 101호") private String detailAddress; - @Schema(description = "우편번호", required = true, example = "123456") - @NotBlank(message = "우편번호는 필수입니다.") + @Schema(description = "우편번호", example = "123456") private String zip; } diff --git a/src/main/java/com/fledge/fledgeserver/support/entity/Support.java b/src/main/java/com/fledge/fledgeserver/support/entity/Support.java index b96509b..de2a7ba 100644 --- a/src/main/java/com/fledge/fledgeserver/support/entity/Support.java +++ b/src/main/java/com/fledge/fledgeserver/support/entity/Support.java @@ -2,6 +2,7 @@ import com.fledge.fledgeserver.common.entity.BaseTimeEntity; import com.fledge.fledgeserver.member.entity.Member; +import com.fledge.fledgeserver.promise.entity.Promise; import com.fledge.fledgeserver.support.dto.request.SupportCreateRequestDto; import com.fledge.fledgeserver.support.dto.request.SupportUpdateRequestDto; import jakarta.persistence.*; @@ -46,32 +47,42 @@ public class Support extends BaseTimeEntity { private List images = new ArrayList<>(); @Column(nullable = false) - private int checkPeriod; + private LocalDate expirationDate; @Column(nullable = false) - private int checkCount; + private Boolean expirationStatus = false; @Column(nullable = false) - private String recipientName; + @Enumerated(value = EnumType.STRING) + private Promise promise; // 인증 주기 및 횟수 @Column(nullable = false) + @Enumerated(value = EnumType.STRING) + private SupportCategory supportCategory; + + // ------의료비 또는 법률구조비------ + @Column(nullable = true) + private String bank; + + @Column(nullable = true) + private String account; + + // -----------기타----------- + @Column(nullable = true) + private String recipientName; + + @Column(nullable = true) private String phone; - @Column(nullable = false) + @Column(nullable = true) private String address; - @Column(nullable = false) + @Column(nullable = true) private String detailAddress; - @Column(nullable = false) + @Column(nullable = true) private String zip; - @Column(nullable = false) - private LocalDate expirationDate; - - @Column(nullable = false) - private Boolean expirationStatus = false; - // TODO :: 챌린지 구현 후 참여 중이거나 완료한 챌린지(뱃지)에 대한 로직 추가 @Builder @@ -82,24 +93,37 @@ public Support(Member member, SupportCreateRequestDto supportCreateRequestDto) { this.item = supportCreateRequestDto.getItem(); this.purchaseUrl = supportCreateRequestDto.getPurchaseUrl(); this.price = supportCreateRequestDto.getPrice(); - this.checkPeriod = supportCreateRequestDto.getCheckPeriod(); - this.checkCount = supportCreateRequestDto.getCheckCount(); - this.recipientName = supportCreateRequestDto.getRecipientName(); - this.phone = supportCreateRequestDto.getPhone(); - this.address = supportCreateRequestDto.getAddress(); - this.detailAddress = supportCreateRequestDto.getDetailAddress(); - this.zip = supportCreateRequestDto.getZip(); this.expirationDate = supportCreateRequestDto.getExpirationDate(); + this.promise = Promise.valueOf(supportCreateRequestDto.getPromise()); + this.supportCategory = SupportCategory.valueOf(supportCreateRequestDto.getSupportCategory()); + + if ("MEDICAL".equals(supportCategory.name()) || "LEGAL_AID".equals(supportCategory.name())) { + this.bank = supportCreateRequestDto.getBank(); + this.account = supportCreateRequestDto.getAccount(); + this.recipientName = null; + this.phone = null; + this.address = null; + this.detailAddress = null; + this.zip = null; + } else { + this.recipientName = supportCreateRequestDto.getRecipientName(); + this.phone = supportCreateRequestDto.getPhone(); + this.address = supportCreateRequestDto.getAddress(); + this.detailAddress = supportCreateRequestDto.getDetailAddress(); + this.zip = supportCreateRequestDto.getZip(); + this.bank = null; + this.account = null; + } } + + public void update(SupportUpdateRequestDto supportUpdateRequestDto) { this.title = supportUpdateRequestDto.getTitle(); this.reason = supportUpdateRequestDto.getReason(); this.item = supportUpdateRequestDto.getItem(); this.purchaseUrl = supportUpdateRequestDto.getPurchaseUrl(); this.price = supportUpdateRequestDto.getPrice(); - this.checkPeriod = supportUpdateRequestDto.getCheckPeriod(); - this.checkCount = supportUpdateRequestDto.getCheckCount(); this.recipientName = supportUpdateRequestDto.getRecipientName(); this.phone = supportUpdateRequestDto.getPhone(); this.address = supportUpdateRequestDto.getAddress(); diff --git a/src/main/java/com/fledge/fledgeserver/support/entity/SupportCategory.java b/src/main/java/com/fledge/fledgeserver/support/entity/SupportCategory.java new file mode 100644 index 0000000..70cc2bc --- /dev/null +++ b/src/main/java/com/fledge/fledgeserver/support/entity/SupportCategory.java @@ -0,0 +1,19 @@ +package com.fledge.fledgeserver.support.entity; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public enum SupportCategory { + DAILY_NECESSITY("DAILY_NECESSITY"), // 생필품 + FOOD("FOOD"), // 식품 + HOME_APPLIANCES("HOME_APPLIANCES"), // 가전제품 + EDUCATION("EDUCATION"), // 교육비/교재비 + MEDICAL("MEDICAL"), // 의료비 + LEGAL_AID("LEGAL_AID"), // 법률 구조비 + ETC("ETC"); // 기타 + + private final String key; + public String getKey() { + return key; + } +} \ No newline at end of file diff --git a/src/main/java/com/fledge/fledgeserver/support/entity/SupportRecord.java b/src/main/java/com/fledge/fledgeserver/support/entity/SupportRecord.java index caf9662..8c0a220 100644 --- a/src/main/java/com/fledge/fledgeserver/support/entity/SupportRecord.java +++ b/src/main/java/com/fledge/fledgeserver/support/entity/SupportRecord.java @@ -1,18 +1,36 @@ package com.fledge.fledgeserver.support.entity; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; +import com.fledge.fledgeserver.common.entity.BaseTimeEntity; +import com.fledge.fledgeserver.member.entity.Member; +import jakarta.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class SupportRecord { +public class SupportRecord extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + @ManyToOne(fetch = FetchType.LAZY) // Member와의 다대일 관계 + @JoinColumn(name = "member_id", nullable = false) + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) // Support와의 다대일 관계 + @JoinColumn(name = "support_id", nullable = false) + private Support support; + + @Column(nullable = false) + private String account; + + @Builder + public SupportRecord(Member member, Support support, String account) { + this.member = member; + this.support = support; + this.account = account; + } } diff --git a/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java b/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java index 2b9c3f6..fdc1c2d 100644 --- a/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java +++ b/src/main/java/com/fledge/fledgeserver/support/service/SupportService.java @@ -54,76 +54,74 @@ public void createSupport(Long memberId, SupportCreateRequestDto supportCreateRe } } - public SupportGetResponseDto getSupport(Long supportId) { - Support support = supportRepository.findSupportByIdWithFetch(supportId) - .orElseThrow(() -> new CustomException(ErrorCode.SUPPORT_NOT_FOUND)); - - return new SupportGetResponseDto( - support.getMember().getId(), - support.getMember().getNickname(), - support.getTitle(), - support.getReason(), - support.getItem(), - support.getPurchaseUrl(), - support.getPrice(), - // Images Presigned-URL처리 - support.getImages().stream() - .map(supportImage -> fileService.getFileUrl(supportImage.getImageUrl())) - .toList(), - support.getCheckPeriod(), - support.getCheckCount(), - support.getExpirationDate() - ); - } - - public SupportGetForUpdateResponseDto getSupportForUpdate(Long memberId, Long supportId) { - Support support = supportRepository.findSupportByIdWithFetch(supportId) - .orElseThrow(() -> new CustomException(ErrorCode.SUPPORT_NOT_FOUND)); - - if (support.getMember().getId() != memberId) { - throw new CustomException(ErrorCode.NO_ACCESS); - } - - // 이미지를 Presigned URL로 처리 - List imageUrls = support.getImages().stream() - .map(supportImage -> fileService.getFileUrl(supportImage.getImageUrl())) - .toList(); - - return new SupportGetForUpdateResponseDto( - support.getMember().getId(), - support.getMember().getNickname(), - support.getTitle(), - support.getReason(), - support.getItem(), - support.getPurchaseUrl(), - support.getPrice(), - imageUrls, - support.getCheckPeriod(), - support.getCheckCount(), - support.getExpirationDate() - ); - } - - public void updateSupport(Long memberId, Long supportId, SupportUpdateRequestDto supportUpdateRequestDto) { - Support support = supportRepository.findSupportByIdWithFetch(supportId) - .orElseThrow(() -> new CustomException(ErrorCode.SUPPORT_NOT_FOUND)); - - if (support.getMember().getId() != memberId) { - throw new CustomException(ErrorCode.NO_ACCESS); - } - support.update(supportUpdateRequestDto); - support.getImages().clear(); - - List newImages = supportUpdateRequestDto.getImages().stream() - .map(imageUrl -> new SupportImage(support, imageUrl)) - .toList(); - - support.getImages().addAll(newImages); - - // 5. 기존 이미지 삭제 - support.getImages().clear(); // 기존 이미지 제거 - - // 6. 새로운 이미지 추가 - support.getImages().addAll(newImages); - } +// public SupportGetResponseDto getSupport(Long supportId) { +// Support support = supportRepository.findSupportByIdWithFetch(supportId) +// .orElseThrow(() -> new CustomException(ErrorCode.SUPPORT_NOT_FOUND)); +// +// return new SupportGetResponseDto( +// support.getMember().getId(), +// support.getMember().getNickname(), +// support.getTitle(), +// support.getReason(), +// support.getItem(), +// support.getPurchaseUrl(), +// support.getPrice(), +// // Images Presigned-URL처리 +// support.getImages().stream() +// .map(supportImage -> fileService.getFileUrl(supportImage.getImageUrl())) +// .toList(), +// support.getExpirationDate() +// ); +// } +// +// public SupportGetForUpdateResponseDto getSupportForUpdate(Long memberId, Long supportId) { +// Support support = supportRepository.findSupportByIdWithFetch(supportId) +// .orElseThrow(() -> new CustomException(ErrorCode.SUPPORT_NOT_FOUND)); +// +// if (support.getMember().getId() != memberId) { +// throw new CustomException(ErrorCode.NO_ACCESS); +// } +// +// // 이미지를 Presigned URL로 처리 +// List imageUrls = support.getImages().stream() +// .map(supportImage -> fileService.getFileUrl(supportImage.getImageUrl())) +// .toList(); +// +// return new SupportGetForUpdateResponseDto( +// support.getMember().getId(), +// support.getMember().getNickname(), +// support.getTitle(), +// support.getReason(), +// support.getItem(), +// support.getPurchaseUrl(), +// support.getPrice(), +// imageUrls, +// support.getCheckPeriod(), +// support.getCheckCount(), +// support.getExpirationDate() +// ); +// } +// +// public void updateSupport(Long memberId, Long supportId, SupportUpdateRequestDto supportUpdateRequestDto) { +// Support support = supportRepository.findSupportByIdWithFetch(supportId) +// .orElseThrow(() -> new CustomException(ErrorCode.SUPPORT_NOT_FOUND)); +// +// if (support.getMember().getId() != memberId) { +// throw new CustomException(ErrorCode.NO_ACCESS); +// } +// support.update(supportUpdateRequestDto); +// support.getImages().clear(); +// +// List newImages = supportUpdateRequestDto.getImages().stream() +// .map(imageUrl -> new SupportImage(support, imageUrl)) +// .toList(); +// +// support.getImages().addAll(newImages); +// +// // 5. 기존 이미지 삭제 +// support.getImages().clear(); // 기존 이미지 제거 +// +// // 6. 새로운 이미지 추가 +// support.getImages().addAll(newImages); +// } }