diff --git a/src/main/java/com/cotato/squadus/api/fee/controller/ClubFeeController.java b/src/main/java/com/cotato/squadus/api/fee/controller/ClubFeeController.java index a64c6da..7a447a7 100644 --- a/src/main/java/com/cotato/squadus/api/fee/controller/ClubFeeController.java +++ b/src/main/java/com/cotato/squadus/api/fee/controller/ClubFeeController.java @@ -35,10 +35,17 @@ public ResponseEntity createClubFee(@AuthenticationPrinci return ResponseEntity.ok(createdClubFee); } + @GetMapping("/usages") + @Operation(summary = "동아리 회비 전체에 대한 사용내역 조회", description = "clubId를 지정하여 해당 동아리의 회비 사용내역 전체를 조회합니다.") + public ResponseEntity findAllClubFeeUsage(@AuthenticationPrincipal CustomOAuth2Member customOAuth2Member, @PathVariable("clubId") Long clubId) { + ClubFeeUsageResponseList clubFeeUsageResponseList = clubFeeService.findAllClubFeeUsage(customOAuth2Member, clubId); + return ResponseEntity.ok(clubFeeUsageResponseList); + } + @GetMapping("/{feeTypeId}/usage") - @Operation(summary = "동아리 회비 사용내역 조회", description = "회비의 id를 지정하여 동아리 회비의 사용내역을 조회합니다.") - public ResponseEntity findAllClubFeeUsage(@AuthenticationPrincipal CustomOAuth2Member customOAuth2Member, @PathVariable("clubId") Long clubId, @PathVariable("feeTypeId") Long feeTypeId) { - ClubFeeUsageResponseList clubFeeUsageResponseList = clubFeeService.findAllClubFeeUsage(customOAuth2Member, clubId, feeTypeId); + @Operation(summary = "동아리 회비 하나에 대한 사용내역 조회", description = "회비의 id를 지정하여 해당하는 동아리 회비의 사용내역을 조회합니다.") + public ResponseEntity findAllClubFeeUsageByFeeTypeId(@AuthenticationPrincipal CustomOAuth2Member customOAuth2Member, @PathVariable("clubId") Long clubId, @PathVariable("feeTypeId") Long feeTypeId) { + ClubFeeUsageResponseList clubFeeUsageResponseList = clubFeeService.findAllClubFeeUsageByFeeTypeId(customOAuth2Member, clubId, feeTypeId); return ResponseEntity.ok(clubFeeUsageResponseList); } @@ -59,6 +66,19 @@ public ResponseEntity findClubFeePaymentInfo( return ResponseEntity.ok(clubFeePaymentInfo); } + @PatchMapping("/{feeTypeId}/payment") + @Operation(summary = "동아리 회비 입금 여부 수정", description = "동아리원의 회비 입금 여부를 수정합니다.") + public ResponseEntity updateClubFeePaymentInfo( + @AuthenticationPrincipal CustomOAuth2Member customOAuth2Member, + @PathVariable("clubId") Long clubId, + @PathVariable("feeTypeId") Long feeTypeId, + @RequestBody ClubFeePaymentUpdateRequest clubFeePaymentUpdateRequest) { + + ClubFeePaymentUpdateResponse clubFeePaymentUpdateResponse = clubFeeService.updateClubFeePaymentInfo(customOAuth2Member, clubId, feeTypeId, clubFeePaymentUpdateRequest); + return ResponseEntity.ok(clubFeePaymentUpdateResponse); + } + + diff --git a/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeePaymentUpdateRequest.java b/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeePaymentUpdateRequest.java new file mode 100644 index 0000000..c165762 --- /dev/null +++ b/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeePaymentUpdateRequest.java @@ -0,0 +1,7 @@ +package com.cotato.squadus.api.fee.dto; +import java.util.Map; + +public record ClubFeePaymentUpdateRequest( + Map paymentsInfo +) { +} diff --git a/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeePaymentUpdateResponse.java b/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeePaymentUpdateResponse.java new file mode 100644 index 0000000..642f779 --- /dev/null +++ b/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeePaymentUpdateResponse.java @@ -0,0 +1,6 @@ +package com.cotato.squadus.api.fee.dto; + +public record ClubFeePaymentUpdateResponse( + Long feeTypeId +) { +} diff --git a/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeeSummaryResponseList.java b/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeeSummaryResponseList.java index 9691692..8fee7d5 100644 --- a/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeeSummaryResponseList.java +++ b/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeeSummaryResponseList.java @@ -3,10 +3,11 @@ import java.util.List; public record ClubFeeSummaryResponseList( + Long totalBalance, List clubFeeSummaryResponseList ) { - public static ClubFeeSummaryResponseList from(List clubFeeSummaryResponseList) { - return new ClubFeeSummaryResponseList(clubFeeSummaryResponseList); + public static ClubFeeSummaryResponseList from(Long totalBalance, List clubFeeSummaryResponseList) { + return new ClubFeeSummaryResponseList(totalBalance, clubFeeSummaryResponseList); } } diff --git a/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeeUsageResponse.java b/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeeUsageResponse.java index d720241..13bcadd 100644 --- a/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeeUsageResponse.java +++ b/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeeUsageResponse.java @@ -5,6 +5,7 @@ import java.time.LocalDate; public record ClubFeeUsageResponse( + Long feeTypeId, Long feeUsageId, LocalDate usedAt, String description, @@ -13,6 +14,7 @@ public record ClubFeeUsageResponse( public static ClubFeeUsageResponse from(FeeUsage feeUsage) { return new ClubFeeUsageResponse( + feeUsage.getFeeType().getFeeTypeId(), feeUsage.getFeeUsageId(), feeUsage.getUsedAt(), feeUsage.getDescription(), diff --git a/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeeUsageResponseList.java b/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeeUsageResponseList.java index 3e001b8..f84681a 100644 --- a/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeeUsageResponseList.java +++ b/src/main/java/com/cotato/squadus/api/fee/dto/ClubFeeUsageResponseList.java @@ -3,9 +3,9 @@ import java.util.List; public record ClubFeeUsageResponseList( - List clubFeeUsageResponseList + List dateGroupedClubFeeUsageResponseList ) { - public static ClubFeeUsageResponseList from(List clubFeeUsageResponseList) { - return new ClubFeeUsageResponseList(clubFeeUsageResponseList); + public static ClubFeeUsageResponseList from(List dateGroupedClubFeeUsageResponseList) { + return new ClubFeeUsageResponseList(dateGroupedClubFeeUsageResponseList); } } diff --git a/src/main/java/com/cotato/squadus/api/fee/dto/DateGroupedClubFeeUsageResponse.java b/src/main/java/com/cotato/squadus/api/fee/dto/DateGroupedClubFeeUsageResponse.java new file mode 100644 index 0000000..cc6b5cd --- /dev/null +++ b/src/main/java/com/cotato/squadus/api/fee/dto/DateGroupedClubFeeUsageResponse.java @@ -0,0 +1,10 @@ +package com.cotato.squadus.api.fee.dto; + +import java.time.LocalDate; +import java.util.List; + +public record DateGroupedClubFeeUsageResponse( + LocalDate date, + List usages +) { +} diff --git a/src/main/java/com/cotato/squadus/common/error/ErrorCode.java b/src/main/java/com/cotato/squadus/common/error/ErrorCode.java index da488e7..a444054 100644 --- a/src/main/java/com/cotato/squadus/common/error/ErrorCode.java +++ b/src/main/java/com/cotato/squadus/common/error/ErrorCode.java @@ -91,6 +91,7 @@ public enum ErrorCode { ENUM_NOT_RESOLVED(HttpStatus.BAD_REQUEST, "S-005", "입력한 Enum이 존재하지 않습니다."), NO_BALANCE_ERROR(HttpStatus.BAD_REQUEST, "F-001", "회비의 잔액보다 사용내역의 금액이 더 큽니다."), + PAYMENT_CHANGE_DENIED(HttpStatus.BAD_REQUEST, "F-002", "이미 사용중인 회비의 입금현황을 변경할 수 없습니다."), ; private final HttpStatus httpStatus; diff --git a/src/main/java/com/cotato/squadus/domain/club/fee/entity/FeePayment.java b/src/main/java/com/cotato/squadus/domain/club/fee/entity/FeePayment.java index b4acf84..9dfc60e 100644 --- a/src/main/java/com/cotato/squadus/domain/club/fee/entity/FeePayment.java +++ b/src/main/java/com/cotato/squadus/domain/club/fee/entity/FeePayment.java @@ -1,7 +1,6 @@ package com.cotato.squadus.domain.club.fee.entity; -import com.cotato.squadus.domain.club.common.entity.Club; import com.cotato.squadus.domain.club.common.entity.ClubMember; import jakarta.persistence.*; import lombok.Builder; @@ -28,6 +27,10 @@ public class FeePayment { @JoinColumn(name = "club_member_idx") private ClubMember clubMember; + public void updateIsPaid(Boolean isPaid) { + this.isPaid = isPaid; + } + @Builder public FeePayment(Boolean isPaid, FeeType feeType, ClubMember clubMember) { this.isPaid = isPaid; diff --git a/src/main/java/com/cotato/squadus/domain/club/fee/entity/FeeType.java b/src/main/java/com/cotato/squadus/domain/club/fee/entity/FeeType.java index bd77931..c6fbbe0 100644 --- a/src/main/java/com/cotato/squadus/domain/club/fee/entity/FeeType.java +++ b/src/main/java/com/cotato/squadus/domain/club/fee/entity/FeeType.java @@ -60,6 +60,10 @@ public void updateFeeUsages(FeeUsage feeUsage) { this.feeUsages.add(feeUsage); } + public void updateTotalPrice(Long totalPrice) { + this.totalPrice = totalPrice; + } + @Builder public FeeType(Club club, String feeTypeName, Long price, String memo, List clubMembers, FeeCategory feeCategory, LocalDate startDate, LocalDate endDate, Long totalPrice, Long balance) { this.club = club; @@ -84,6 +88,6 @@ public void update(String feeTypeName, Long price, String memo, FeeCategory feeC public void updateBalance(Long price) { - this.balance -= price; + this.balance += price; } } diff --git a/src/main/java/com/cotato/squadus/domain/club/fee/repository/FeeUsageRepository.java b/src/main/java/com/cotato/squadus/domain/club/fee/repository/FeeUsageRepository.java index 1b08d29..6286c70 100644 --- a/src/main/java/com/cotato/squadus/domain/club/fee/repository/FeeUsageRepository.java +++ b/src/main/java/com/cotato/squadus/domain/club/fee/repository/FeeUsageRepository.java @@ -9,4 +9,6 @@ public interface FeeUsageRepository extends JpaRepository { List findAllByFeeType(FeeType feeType); + + List findAllByFeeType_Club_ClubId(Long clubId); } diff --git a/src/main/java/com/cotato/squadus/domain/club/fee/service/ClubFeeService.java b/src/main/java/com/cotato/squadus/domain/club/fee/service/ClubFeeService.java index 34fdaec..c744d7b 100644 --- a/src/main/java/com/cotato/squadus/domain/club/fee/service/ClubFeeService.java +++ b/src/main/java/com/cotato/squadus/domain/club/fee/service/ClubFeeService.java @@ -22,8 +22,12 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDate; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; @Slf4j @Service @@ -41,8 +45,16 @@ public class ClubFeeService { public ClubFeeSummaryResponseList findAllClubFeeTypesSummary(CustomOAuth2Member customOAuth2Member, Long clubId) { - List clubFeeSummaryResponseList = feeTypeRepository.findAllByClub_ClubId(clubId).stream().map(ClubFeeSummaryResponse::from).toList(); - return ClubFeeSummaryResponseList.from(clubFeeSummaryResponseList); + List clubFeeSummaryResponseList = feeTypeRepository.findAllByClub_ClubId(clubId) + .stream(). + map(ClubFeeSummaryResponse::from) + .toList(); + + Long totalBalance = clubFeeSummaryResponseList.stream() + .mapToLong(ClubFeeSummaryResponse::balance) + .sum(); + + return ClubFeeSummaryResponseList.from(totalBalance, clubFeeSummaryResponseList); } @Transactional @@ -128,7 +140,7 @@ public ClubFeeUsageCreateResponse createClubFeeUsage(CustomOAuth2Member customOA if (clubFeeUsageRequest.price() > feeType.getBalance()) { throw new AppException(ErrorCode.NO_BALANCE_ERROR); } - feeType.updateBalance(clubFeeUsageRequest.price()); // price 만큼 사용 + feeType.updateBalance(-clubFeeUsageRequest.price()); // price 만큼 사용 FeeUsage feeUsage = FeeUsage.builder() .feeType(feeType) @@ -164,7 +176,21 @@ public ClubFeePaymentInfoResponseList findClubFeePaymentInfo(CustomOAuth2Member } - public ClubFeeUsageResponseList findAllClubFeeUsage(CustomOAuth2Member customOAuth2Member, Long clubId, Long feeTypeId) { + public ClubFeeUsageResponseList findAllClubFeeUsage(CustomOAuth2Member customOAuth2Member, Long clubId) { + + // clubId에 해당하는 모든 FeeUsage를 조회 및 변환 + List allUsages = feeUsageRepository.findAllByFeeType_Club_ClubId(clubId) + .stream() + .map(ClubFeeUsageResponse::from) + .toList(); + + List dateGroupedResponses = getDateGroupedClubFeeUsageResponses(allUsages); + + return ClubFeeUsageResponseList.from(dateGroupedResponses); + + } + + public ClubFeeUsageResponseList findAllClubFeeUsageByFeeTypeId(CustomOAuth2Member customOAuth2Member, Long clubId, Long feeTypeId) { FeeType feeType = feeTypeRepository.findById(feeTypeId) .orElseThrow(() -> new EntityNotFoundException("해당 id를 가진 회비를 찾을 수 없습니다.")); @@ -173,7 +199,50 @@ public ClubFeeUsageResponseList findAllClubFeeUsage(CustomOAuth2Member customOAu .map(ClubFeeUsageResponse::from) .toList(); - return ClubFeeUsageResponseList.from(clubFeeUsageResponseList); + List dateGroupedResponses = getDateGroupedClubFeeUsageResponses(clubFeeUsageResponseList); + + return ClubFeeUsageResponseList.from(dateGroupedResponses); + + } + + private List getDateGroupedClubFeeUsageResponses(List clubFeeUsageResponseList) { + // 사용 내역을 날짜별로 그룹화 + Map> groupedByDate = clubFeeUsageResponseList.stream() + .collect(Collectors.groupingBy(ClubFeeUsageResponse::usedAt)); + + // 날짜별로 그룹화된 데이터를 적절한 응답 형태로 변환 + List dateGroupedResponses = groupedByDate.entrySet().stream() + .map(entry -> new DateGroupedClubFeeUsageResponse(entry.getKey(), entry.getValue())) + .sorted(Comparator.comparing(DateGroupedClubFeeUsageResponse::date).reversed()) // 날짜별로 최신 순 정렬 + .toList(); + return dateGroupedResponses; + } + + @Transactional + public ClubFeePaymentUpdateResponse updateClubFeePaymentInfo(CustomOAuth2Member customOAuth2Member, Long clubId, Long feeTypeId, ClubFeePaymentUpdateRequest clubFeePaymentUpdateRequest) { + List feePayments = feePaymentRepository.findAllByFeeType_FeeTypeId(feeTypeId); + FeeType feeType = feeTypeRepository.findById(feeTypeId) + .orElseThrow(() -> new EntityNotFoundException("해당 아이디를 가진 feeType을 찾을 수 없습니다.")); + Long totalPrice = 0L; + if(!feeType.getBalance().equals(0L)) { + throw new AppException(ErrorCode.PAYMENT_CHANGE_DENIED); + } + for (FeePayment feePayment : feePayments) { + Long clubMemberIdx = feePayment.getClubMember().getClubMemberIdx(); + if(clubFeePaymentUpdateRequest.paymentsInfo().get(clubMemberIdx)) { + feePayment.updateIsPaid(true); + totalPrice += feeType.getPrice(); + } else { + feePayment.updateIsPaid(false); + } + feePaymentRepository.save(feePayment); + } + feeType.updateTotalPrice(totalPrice); + feeType.updateBalance(totalPrice); + feeTypeRepository.save(feeType); + return new ClubFeePaymentUpdateResponse(feeTypeId); } + + }