diff --git a/src/main/java/com/bbteam/budgetbuddies/common/BaseEntity.java b/src/main/java/com/bbteam/budgetbuddies/common/BaseEntity.java index b8e40e17..b66eef77 100644 --- a/src/main/java/com/bbteam/budgetbuddies/common/BaseEntity.java +++ b/src/main/java/com/bbteam/budgetbuddies/common/BaseEntity.java @@ -21,7 +21,6 @@ @AllArgsConstructor @SuperBuilder @SoftDelete // boolean 타입의 deleted 필드가 추가 -@Getter public abstract class BaseEntity { @Id diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/entity/Comment.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/entity/Comment.java index 49dbf6dd..ce3ecccc 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/comment/entity/Comment.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/comment/entity/Comment.java @@ -4,14 +4,17 @@ import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; import com.bbteam.budgetbuddies.domain.user.entity.User; -import jakarta.persistence.*; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; -import org.hibernate.annotations.Where; -import org.springframework.lang.Nullable; @Entity @Getter @@ -20,22 +23,22 @@ @SuperBuilder public class Comment extends BaseEntity { - @Column(nullable = false, length = 1000) - private String content; + @Column(nullable = false, length = 1000) + private String content; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") - private User user; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "discount_info_id") - private DiscountInfo discountInfo; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "discount_info_id") + private DiscountInfo discountInfo; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "support_info_id") - private SupportInfo supportInfo; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "support_info_id") + private SupportInfo supportInfo; - @Column(nullable = false) - private Integer anonymousNumber; + @Column(nullable = false) + private Integer anonymousNumber; } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalApi.java index c13498b5..4e739a8a 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalApi.java @@ -25,9 +25,10 @@ public interface ConsumptionGoalApi { ResponseEntity getTopGoalCategories(@RequestParam(name = "top", defaultValue = "5") int top, Long userId, int peerAgeStart, int peerAgeEnd, String peerGender); - @Operation(summary = "소비 목표 조회 API", description = "date={yyyy-MM-dd} 형식의 query string을 통해서 사용자의 목표 달을 조회하는 API 입니다.") - @Parameters({@Parameter(name = "date", description = "yyyy-MM-dd 형식으로 목표 달의 소비를 조회")}) - ResponseEntity findUserConsumptionGoal(LocalDate date, Long userId); + @Operation(summary = "또래가 가장 큰 계획을 세운 카테고리와 이번 주 사용한 금액 조회 API", description = "로그인 한 유저의 또래 중 가장 큰 소비 목표 금액을 가진 카테고리와 이번 주 사용한 금액을 조회하는 API 입니다") + @ApiResponses(value = {@ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) + @Parameters({@Parameter(name = "userId", description = "로그인 한 유저 아이디")}) + ResponseEntity getTopGoalCategory(@RequestParam(name = "userId") Long userId); @Operation(summary = "또래나이와 성별 조회 API", description = "또래나이와 성별을 조회하는 API 입니다.") @ApiResponses(value = {@ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) @@ -37,7 +38,12 @@ ResponseEntity getTopGoalCategories(@RequestParam(name = "top", defaultValue @Parameter(name = "peerGender", description = "또래 성별")}) ResponseEntity getPeerInfo(Long userId, int peerAgeStart, int peerAgeEnd, String peerGender); + @Operation(summary = "소비 목표 조회 API", description = "date={yyyy-MM-dd} 형식의 query string을 통해서 사용자의 목표 달을 조회하는 API 입니다.") + @Parameters({@Parameter(name = "date", description = "yyyy-MM-dd 형식으로 목표 달의 소비를 조회")}) + ResponseEntity findUserConsumptionGoal(LocalDate date, Long userId); + @Operation(summary = "이번 달 소비 목표 수정 API", description = "다른 달의 소비 목표를 업데이트하는 것은 불가능하고 오직 이번 달의 소비 목표만 업데이트 하는 API 입니다.") ResponseEntity updateOrElseGenerateConsumptionGoal(Long userId, ConsumptionGoalListRequestDto consumptionGoalListRequestDto); + } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalController.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalController.java index 53c9330f..1cf418c1 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalController.java @@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionAnalysisResponseDTO; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalListRequestDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDTO; @@ -39,12 +40,9 @@ public ResponseEntity getTopGoalCategories(@RequestParam(name = "top", defaul return ResponseEntity.ok(topCategory); } - @GetMapping("/{userId}") - public ResponseEntity findUserConsumptionGoal( - @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date, @PathVariable Long userId) { - - ConsumptionGoalResponseListDto response = consumptionGoalService.findUserConsumptionGoal(userId, date); - + @GetMapping("/top-category") + public ResponseEntity getTopGoalCategory(@RequestParam(name = "userId") Long userId) { + ConsumptionAnalysisResponseDTO response = consumptionGoalService.getTopCategoryAndConsumptionAmount(userId); return ResponseEntity.ok(response); } @@ -57,6 +55,15 @@ public ResponseEntity getPeerInfo(@RequestParam(name = "userId") Long userId, return ResponseEntity.ok(response); } + @GetMapping("/{userId}") + public ResponseEntity findUserConsumptionGoal( + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date, @PathVariable Long userId) { + + ConsumptionGoalResponseListDto response = consumptionGoalService.findUserConsumptionGoal(userId, date); + + return ResponseEntity.ok(response); + } + @PostMapping("/{userId}") public ResponseEntity updateOrElseGenerateConsumptionGoal(@PathVariable Long userId, @RequestBody ConsumptionGoalListRequestDto consumptionGoalListRequestDto) { diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionAnalysisConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionAnalysisConverter.java new file mode 100644 index 00000000..27b34819 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionAnalysisConverter.java @@ -0,0 +1,15 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.converter; + +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionAnalysisResponseDTO; +import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; + +public class ConsumptionAnalysisConverter { + + public static ConsumptionAnalysisResponseDTO fromEntity(ConsumptionGoal consumptionGoal, Long topAmount) { + + return ConsumptionAnalysisResponseDTO.builder() + .goalCategory(consumptionGoal.getCategory().getName()) + .currentWeekConsumptionAmount(topAmount) + .build(); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionAnalysisResponseDTO.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionAnalysisResponseDTO.java new file mode 100644 index 00000000..10c505be --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionAnalysisResponseDTO.java @@ -0,0 +1,17 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ConsumptionAnalysisResponseDTO { + + private String goalCategory; + + private Long currentWeekConsumptionAmount; +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepository.java index 34220a0b..40ee25d7 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepository.java @@ -17,20 +17,20 @@ @Repository public interface ConsumptionGoalRepository extends JpaRepository { - @Query("SELECT cg FROM ConsumptionGoal cg " - + "WHERE cg.category.isDefault = true " - + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " - + "AND cg.user.gender = :peerGender " + @Query("SELECT cg FROM ConsumptionGoal cg " + "WHERE cg.category.isDefault = true " + + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + "AND cg.user.gender = :peerGender " + "ORDER BY cg.goalAmount DESC limit :top") - List findTopCategoriesAndGoalAmount( - @Param("top") int top, - @Param("peerAgeStart") int peerAgeStart, - @Param("peerAgeEnd") int peerAgeEnd, - @Param("peerGender") Gender peerGender); + List findTopCategoriesAndGoalAmount(@Param("top") int top, @Param("peerAgeStart") int peerAgeStart, + @Param("peerAgeEnd") int peerAgeEnd, @Param("peerGender") Gender peerGender); @Query(value = "SELECT cg FROM ConsumptionGoal AS cg WHERE cg.user.id = :userId AND cg.goalMonth = :goalMonth") List findConsumptionGoalByUserIdAndGoalMonth(Long userId, LocalDate goalMonth); Optional findConsumptionGoalByUserAndCategoryAndGoalMonth(User user, Category category, LocalDate goalMonth); + + @Query("SELECT cg FROM ConsumptionGoal cg JOIN cg.category c WHERE c.id = :categoryId AND cg.goalMonth " + + "BETWEEN :startOfWeek AND :endOfWeek ORDER BY cg.consumeAmount DESC") + Optional findTopConsumptionByCategoryIdAndCurrentWeek(@Param("categoryId") Long categoryId, + @Param("startOfWeek") LocalDate startOfWeek, @Param("endOfWeek") LocalDate endOfWeek); } \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalService.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalService.java index e647a0ec..cb4d297b 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalService.java @@ -5,6 +5,7 @@ import org.springframework.stereotype.Service; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionAnalysisResponseDTO; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalListRequestDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDTO; @@ -22,4 +23,6 @@ List getTopGoalCategories(int top, Long userId, int ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId, ConsumptionGoalListRequestDto consumptionGoalListRequestDto); + + ConsumptionAnalysisResponseDTO getTopCategoryAndConsumptionAmount(Long userId); } \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceImpl.java index 553bb514..7ca9cc5e 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceImpl.java @@ -1,6 +1,8 @@ package com.bbteam.budgetbuddies.domain.consumptiongoal.service; +import java.time.DayOfWeek; import java.time.LocalDate; +import java.time.temporal.TemporalAdjusters; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -13,9 +15,11 @@ import com.bbteam.budgetbuddies.domain.category.entity.Category; import com.bbteam.budgetbuddies.domain.category.repository.CategoryRepository; +import com.bbteam.budgetbuddies.domain.consumptiongoal.converter.ConsumptionAnalysisConverter; import com.bbteam.budgetbuddies.domain.consumptiongoal.converter.ConsumptionGoalConverter; import com.bbteam.budgetbuddies.domain.consumptiongoal.converter.PeerInfoConverter; import com.bbteam.budgetbuddies.domain.consumptiongoal.converter.TopCategoryConverter; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionAnalysisResponseDTO; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalListRequestDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalRequestDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseDto; @@ -69,6 +73,69 @@ public PeerInfoResponseDTO getPeerInfo(Long userId, int peerAgeS, int peerAgeE, return PeerInfoConverter.fromEntity(peerAgeStart, peerAgeEnd, peerGender); } + @Override + @Transactional(readOnly = true) + public ConsumptionAnalysisResponseDTO getTopCategoryAndConsumptionAmount(Long userId) { + + User user = findUserById(userId); + + checkPeerInfo(user, 0, 0, "none"); + + ConsumptionGoal topConsumptionGoal = consumptionGoalRepository.findTopCategoriesAndGoalAmount(1, peerAgeStart, + peerAgeEnd, peerGender).get(0); + + LocalDate today = LocalDate.now(); + LocalDate startOfWeek = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); + LocalDate endOfWeek = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)); + + ConsumptionGoal currentWeekConsumptionAmount = consumptionGoalRepository.findTopConsumptionByCategoryIdAndCurrentWeek( + topConsumptionGoal.getCategory().getId(), startOfWeek, endOfWeek) + .orElseThrow(IllegalArgumentException::new); + + Long totalConsumptionAmountForCurrentWeek = currentWeekConsumptionAmount.getConsumeAmount(); + + return ConsumptionAnalysisConverter.fromEntity(topConsumptionGoal, totalConsumptionAmountForCurrentWeek); + } + + @Override + @Transactional + public ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId, + ConsumptionGoalListRequestDto consumptionGoalListRequestDto) { + LocalDate thisMonth = LocalDate.now().withDayOfMonth(1); + User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("Not found user")); + + List updatedConsumptionGoal = consumptionGoalListRequestDto.getConsumptionGoalList() + .stream() + .map(c -> updateConsumptionGoalWithRequestDto(user, c, thisMonth)) + .toList(); + + List response = consumptionGoalRepository.saveAll(updatedConsumptionGoal) + .stream() + .map(consumptionGoalConverter::toConsumptionGoalResponseDto) + .toList(); + + return new ConsumptionGoalResponseListDto(thisMonth, response); + } + + @Override + @Transactional(readOnly = true) + public ConsumptionGoalResponseListDto findUserConsumptionGoal(Long userId, LocalDate date) { + LocalDate goalMonth = date.withDayOfMonth(1); + Map goalMap = initializeGoalMap(userId, goalMonth); + + updateGoalMapWithPreviousMonth(userId, goalMonth, goalMap); + updateGoalMapWithCurrentMonth(userId, goalMonth, goalMap); + + return new ConsumptionGoalResponseListDto(goalMonth, new ArrayList<>(goalMap.values())); + } + + private Map initializeGoalMap(Long userId, LocalDate goalMonth) { + return categoryRepository.findUserCategoryByUserId(userId) + .stream() + .collect(Collectors.toMap(Category::getId, + category -> consumptionGoalConverter.toConsumptionGoalResponseDto(category))); + } + private User findUserById(Long userId) { Optional user = userRepository.findById(userId); @@ -112,25 +179,6 @@ private void setAgeGroupByUser(int userAge) { } } - @Override - @Transactional(readOnly = true) - public ConsumptionGoalResponseListDto findUserConsumptionGoal(Long userId, LocalDate date) { - LocalDate goalMonth = date.withDayOfMonth(1); - Map goalMap = initializeGoalMap(userId, goalMonth); - - updateGoalMapWithPreviousMonth(userId, goalMonth, goalMap); - updateGoalMapWithCurrentMonth(userId, goalMonth, goalMap); - - return new ConsumptionGoalResponseListDto(goalMonth, new ArrayList<>(goalMap.values())); - } - - private Map initializeGoalMap(Long userId, LocalDate goalMonth) { - return categoryRepository.findUserCategoryByUserId(userId) - .stream() - .collect(Collectors.toMap(Category::getId, - category -> consumptionGoalConverter.toConsumptionGoalResponseDto(category))); - } - private void updateGoalMapWithPreviousMonth(Long userId, LocalDate goalMonth, Map goalMap) { updateGoalMap(userId, goalMonth.minusMonths(1), goalMap); @@ -148,26 +196,6 @@ private void updateGoalMap(Long userId, LocalDate month, Map goalMap.put(goal.getCategoryId(), goal)); } - @Override - @Transactional - public ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId, - ConsumptionGoalListRequestDto consumptionGoalListRequestDto) { - LocalDate thisMonth = LocalDate.now().withDayOfMonth(1); - User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("Not found user")); - - List updatedConsumptionGoal = consumptionGoalListRequestDto.getConsumptionGoalList() - .stream() - .map(c -> updateConsumptionGoalWithRequestDto(user, c, thisMonth)) - .toList(); - - List response = consumptionGoalRepository.saveAll(updatedConsumptionGoal) - .stream() - .map(consumptionGoalConverter::toConsumptionGoalResponseDto) - .toList(); - - return new ConsumptionGoalResponseListDto(thisMonth, response); - } - private ConsumptionGoal updateConsumptionGoalWithRequestDto(User user, ConsumptionGoalRequestDto consumptionGoalRequestDto, LocalDate goalMonth) {