diff --git a/src/main/java/zero/eight/donut/config/SecurityConfig.java b/src/main/java/zero/eight/donut/config/SecurityConfig.java index c134069..6fd52fa 100644 --- a/src/main/java/zero/eight/donut/config/SecurityConfig.java +++ b/src/main/java/zero/eight/donut/config/SecurityConfig.java @@ -42,6 +42,9 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .authorizeHttpRequests((auth) -> { auth .requestMatchers(permitList).permitAll() + //////////////////////////////////////// + /* 역할 검증이 필요한 uri 추가하기 .permit */ + //////////////////////////////////////// .anyRequest().authenticated(); }) // jwtFilter를 UsernamePasswordAuthenticationFilter 앞에 추가 diff --git a/src/main/java/zero/eight/donut/controller/WalletController.java b/src/main/java/zero/eight/donut/controller/WalletController.java new file mode 100644 index 0000000..5fc98fd --- /dev/null +++ b/src/main/java/zero/eight/donut/controller/WalletController.java @@ -0,0 +1,27 @@ +package zero.eight.donut.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import zero.eight.donut.common.response.ApiResponse; +import zero.eight.donut.service.WalletService; + +@RequiredArgsConstructor +@RequestMapping("/api/wallet") +@RestController +public class WalletController { + + private final WalletService walletService; + + @GetMapping("/main") + public ApiResponse walletMain() { + return walletService.walletMain(); + } + + @GetMapping("/{giftId}") + public ApiResponse walletDetail(@PathVariable Long giftId) { + return walletService.walletDetail(giftId); + } +} diff --git a/src/main/java/zero/eight/donut/dto/home/receiver/ReceiverGetGiftResponseDto.java b/src/main/java/zero/eight/donut/dto/home/receiver/GetGiftResponseDto.java similarity index 77% rename from src/main/java/zero/eight/donut/dto/home/receiver/ReceiverGetGiftResponseDto.java rename to src/main/java/zero/eight/donut/dto/home/receiver/GetGiftResponseDto.java index a10ea2e..3e022d9 100644 --- a/src/main/java/zero/eight/donut/dto/home/receiver/ReceiverGetGiftResponseDto.java +++ b/src/main/java/zero/eight/donut/dto/home/receiver/GetGiftResponseDto.java @@ -8,7 +8,7 @@ import java.time.LocalDateTime; @Getter -public class ReceiverGetGiftResponseDto { +public class GetGiftResponseDto { private String product; private Integer price; private LocalDateTime dueDate; @@ -18,7 +18,7 @@ public class ReceiverGetGiftResponseDto { private Long boxId; @Builder - public ReceiverGetGiftResponseDto(String product, Integer price, LocalDateTime dueDate, String imgUrl, Store store, Status status, Long boxId){ + public GetGiftResponseDto(String product, Integer price, LocalDateTime dueDate, String imgUrl, Store store, Status status, Long boxId){ this.product = product; this.price = price; this.dueDate = dueDate; diff --git a/src/main/java/zero/eight/donut/dto/wallet/WalletGiftInfoDto.java b/src/main/java/zero/eight/donut/dto/wallet/WalletGiftInfoDto.java new file mode 100644 index 0000000..7e44def --- /dev/null +++ b/src/main/java/zero/eight/donut/dto/wallet/WalletGiftInfoDto.java @@ -0,0 +1,17 @@ +package zero.eight.donut.dto.wallet; + +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +@Builder +public class WalletGiftInfoDto { + private long giftId; // 기프티콘 고유 ID + private int days; // 디데이 남은 일수 + private String store; // 사용처 + private LocalDateTime dueDate; // 사용 기한 + private String product; // 상품명 + private int price; // 상품 금액 +} diff --git a/src/main/java/zero/eight/donut/dto/wallet/WalletResponseDto.java b/src/main/java/zero/eight/donut/dto/wallet/WalletResponseDto.java new file mode 100644 index 0000000..56982ee --- /dev/null +++ b/src/main/java/zero/eight/donut/dto/wallet/WalletResponseDto.java @@ -0,0 +1,18 @@ +package zero.eight.donut.dto.wallet; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +public class WalletResponseDto { + private int receiver; // 수혜자 수 + private double amount; // 기프티콘 총액 + private int cu; // 보유 기프티콘 개수(CU) + private int gs25; // 보유 기프티콘 개수(GS25) + private int seveneleven; // 보유 기프티콘 개수(7eleven) + private List impendingList; // 임박 기프티콘 리스트 + private List giftList; // 일반 기프티콘 리스트 +} diff --git a/src/main/java/zero/eight/donut/exception/Success.java b/src/main/java/zero/eight/donut/exception/Success.java index 7e112b8..eaa2f23 100644 --- a/src/main/java/zero/eight/donut/exception/Success.java +++ b/src/main/java/zero/eight/donut/exception/Success.java @@ -13,6 +13,7 @@ public enum Success { SUCCESS(HttpStatus.OK, "Request successfully processed"), // 200 OK SUCCESS + GET_WALLET_SUCCESS(HttpStatus.OK, "Success in getting wallet info"), HOME_GIVER_SUCCESS(HttpStatus.OK, "Get request for giver's home info completed successfully"), HOME_RECEIVER_SUCCESS(HttpStatus.OK, "Get request for receiver's home info completed successfully"), HOME_RECEIVER_BOX_SUCCESS(HttpStatus.OK, "Get request for receiver's giftbox info completed successfully"), @@ -27,7 +28,7 @@ public enum Success { //201 CREATED SUCCESS ASSIGN_BENEFIT_SUCCESS(HttpStatus.CREATED, "Successfully assigned benefits"), - DONATE_GIFT_SUCCESS(HttpStatus.CREATED, "Successfully donate gift "), + DONATE_GIFT_SUCCESS(HttpStatus.CREATED, "Successfully donate gift"), CREATE_REPORT_SUCCESS(HttpStatus.CREATED, "Your report is successfully registered"), SIGN_IN_SUCCESS(HttpStatus.CREATED, "Sign in successfully"), SIGN_UP_SUCCESS(HttpStatus.CREATED, "Successfully signed up"), diff --git a/src/main/java/zero/eight/donut/repository/GiftRepository.java b/src/main/java/zero/eight/donut/repository/GiftRepository.java index a2677d4..38961e7 100644 --- a/src/main/java/zero/eight/donut/repository/GiftRepository.java +++ b/src/main/java/zero/eight/donut/repository/GiftRepository.java @@ -2,8 +2,9 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import zero.eight.donut.domain.Gift; -import zero.eight.donut.domain.enums.Store; +import zero.eight.donut.domain.Giver; import java.time.LocalDateTime; import java.util.List; @@ -23,4 +24,7 @@ public interface GiftRepository extends JpaRepository { @Query(value = "SELECT * FROM gift g WHERE g.status = :status AND g.is_assigned = false" + "AND g.due_date BETWEEN :startDate AND :endDate", nativeQuery = true) List findAllByNotAssignedAndStatusAndDueDateBetween(String status,LocalDateTime startDate, LocalDateTime endDate); + + @Query(value = "SELECT * FROM gift g WHERE g.status = 'stored' AND g.giver_id = :giverId AND g.due_date >= :today", nativeQuery = true) + List findAllByGiverAndStatusAndDueDateAfterOrToday(@Param("giverId") Long giverId, @Param("today") LocalDateTime today); } diff --git a/src/main/java/zero/eight/donut/service/HomeReceiverService.java b/src/main/java/zero/eight/donut/service/HomeReceiverService.java index dd28ff0..77134e2 100644 --- a/src/main/java/zero/eight/donut/service/HomeReceiverService.java +++ b/src/main/java/zero/eight/donut/service/HomeReceiverService.java @@ -130,17 +130,39 @@ public ApiResponse receiverGetOneBox(Long boxId){ } @Transactional public ApiResponse receiverGetOneGift(Long giftId){ - // 수혜자 여부 검증 - if (!authUtils.getCurrentUserRole().equals(Role.ROLE_RECEIVER)) { - return ApiResponse.failure(Error.NOT_AUTHENTICATED_EXCEPTION); - } + + // Security Config로 책임 전가 +// // 수혜자 여부 검증 +// if (!authUtils.getCurrentUserRole().equals(Role.ROLE_RECEIVER)) { +// return ApiResponse.failure(Error.NOT_AUTHENTICATED_EXCEPTION); +// } + //Gift 있는지 확인 Optional giftOptional = giftRepository.findById(giftId); if(giftOptional.isEmpty()) return ApiResponse.failure(Error.GIFT_NOT_FOUND_EXCEPTION); + Gift gift = giftOptional.get(); - ReceiverGetGiftResponseDto responseDto = ReceiverGetGiftResponseDto.builder() + // 재사용을 위해 함수로 변경 + /* + GetGiftResponseDto responseDto = GetGiftResponseDto.builder() + .product(gift.getProduct()) + .price(gift.getPrice()) + .dueDate(gift.getDueDate()) + .imgUrl(gift.getImageUrl()) + .store(gift.getStore()) + .status(gift.getStatus()) + .boxId(gift.getGiftbox().getId()) + .build(); + */ + + return ApiResponse.success(Success.HOME_RECEIVER_GIFT_SUCCESS, getGiftInfo(giftId, gift)); + } + + public GetGiftResponseDto getGiftInfo(Long giftId, Gift gift){ + + return GetGiftResponseDto.builder() .product(gift.getProduct()) .price(gift.getPrice()) .dueDate(gift.getDueDate()) @@ -149,6 +171,5 @@ public ApiResponse receiverGetOneGift(Long giftId){ .status(gift.getStatus()) .boxId(gift.getGiftbox().getId()) .build(); - return ApiResponse.success(Success.HOME_RECEIVER_GIFT_SUCCESS, responseDto); } } diff --git a/src/main/java/zero/eight/donut/service/WalletService.java b/src/main/java/zero/eight/donut/service/WalletService.java new file mode 100644 index 0000000..29b827d --- /dev/null +++ b/src/main/java/zero/eight/donut/service/WalletService.java @@ -0,0 +1,130 @@ +package zero.eight.donut.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.cglib.core.Local; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import zero.eight.donut.common.response.ApiResponse; +import zero.eight.donut.config.jwt.AuthUtils; +import zero.eight.donut.domain.Gift; +import zero.eight.donut.domain.Giver; +import zero.eight.donut.domain.enums.Store; +import zero.eight.donut.dto.auth.Role; +import zero.eight.donut.dto.wallet.WalletGiftInfoDto; +import zero.eight.donut.dto.wallet.WalletResponseDto; +import zero.eight.donut.exception.Error; +import zero.eight.donut.exception.Success; +import zero.eight.donut.repository.GiftRepository; +import zero.eight.donut.repository.ReceiverRepository; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +@Service +public class WalletService { + + private final AuthUtils authUtils; + private final ReceiverRepository receiverRepository; + private final GiftRepository giftRepository; + private final HomeReceiverService homeReceiverService; + + @Transactional + // 월렛 화면 조회 + public ApiResponse walletMain() { + // 변수 선언 + LocalDateTime now = LocalDateTime.now(); // 조회 시각 + Giver giver = authUtils.getGiver(); // 기부자 정보 + int receiver = receiverRepository.countBy(); // 수혜자 수 + double amount = 0d; // 기프티콘 총액 + int cu = 0; // 보유 기프티콘 개수(CU) + int gs25 = 0; // 보유 기프티콘 개수(GS25) + int seveneleven = 0; // 보유 기프티콘 개수(7eleven) + List impendingList = new ArrayList<>(); // 임박 기프티콘 리스트 + List giftList = new ArrayList<>(); // 일반 기프티콘 리스트 + + // 월렛 기프티콘 리스트 조회 + List targetList = giftRepository.findAllByGiverAndStatusAndDueDateAfterOrToday(giver.getId(), now); + + if (targetList != null || !targetList.isEmpty()) { // 보유 기프티콘이 있음 + // 사용처별 기프티콘 개수 계산 + Map giftCountMap = countGiftsByStore(targetList); + cu = Math.toIntExact(giftCountMap.get(Store.CU)); + gs25 = Math.toIntExact(giftCountMap.get(Store.GS25)); + seveneleven = Math.toIntExact(giftCountMap.get(Store.SEVENELEVEN)); + + for (Gift g : targetList) { + // 기프티콘 총액 계산 + amount += g.getPrice(); + // 기프티콘 분류 + if (g.getDueDate().isBefore(LocalDateTime.now().plusDays(30))) { // 유효 기간 30일 이내 + // 임박 리스트에 할당 + impendingList.add( + WalletGiftInfoDto.builder() + .giftId(g.getId()) + .days(Math.toIntExact(Duration.between(now, g.getDueDate()).toDaysPart())) + .store(g.getStore().getStore()) + .dueDate(g.getDueDate()) + .product(g.getProduct()) + .price(g.getPrice()) + .build() + ); + } + else { // 유효 기간 30일 이후 + // 일반 리스트에 할당 + giftList.add( + WalletGiftInfoDto.builder() + .giftId(g.getId()) + .days(Math.toIntExact(Duration.between(now, g.getDueDate()).toDaysPart())) + .store(g.getStore().getStore()) + .dueDate(g.getDueDate()) + .product(g.getProduct()) + .price(g.getPrice()) + .build() + ); + } + } + } + + // response data 생성 + WalletResponseDto responseDto = WalletResponseDto.builder() + .receiver(receiver) + .amount(amount) + .cu(cu) + .gs25(gs25) + .seveneleven(seveneleven) + .impendingList(impendingList) + .giftList(giftList) + .build(); + + return ApiResponse.success(Success.GET_WALLET_SUCCESS, responseDto); + } + + // 사용처(store) 필드의 값마다 gift 개수 세기 + public Map countGiftsByStore(List giftList) { + // Gift 객체 리스트를 사용처(store) 필드의 값마다 그룹화하여 각 그룹의 개수를 계산하여 Map으로 반환 + return giftList.stream() + .collect(Collectors.groupingBy(Gift::getStore, Collectors.counting())); + } + + @Transactional + public ApiResponse walletDetail(Long giftId) { + + // Security Config로 책임 전가 +// // 기부자 여부 검증 +// if (!authUtils.getCurrentUserRole().equals(Role.ROLE_GIVER)) { +// return ApiResponse.failure(Error.NOT_AUTHENTICATED_EXCEPTION); +// } + + //Gift 있는지 확인 + Optional giftOptional = giftRepository.findById(giftId); + if(giftOptional.isEmpty()) + return ApiResponse.failure(Error.GIFT_NOT_FOUND_EXCEPTION); + + Gift gift = giftOptional.get(); + + return ApiResponse.success(Success.HOME_RECEIVER_GIFT_SUCCESS, homeReceiverService.getGiftInfo(giftId, gift)); + } +}