From 59c0ba1bd530a9c71a4bb431bb8bf38220254289 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sat, 7 Sep 2024 12:18:35 +0900 Subject: [PATCH 001/204] =?UTF-8?q?[feat]=20ConsumptionGoalRepository=20?= =?UTF-8?q?=ED=8A=B9=EC=A0=95=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=B4=20=ED=8A=B9=EC=A0=95=20=EB=8B=AC?= =?UTF-8?q?=20=EC=9D=B4=EC=A0=84=20=EA=B0=80=EC=9E=A5=20=EC=B5=9C=EA=B7=BC?= =?UTF-8?q?=20=EB=8B=A8=EC=9D=BC=20=EC=9C=A0=EC=A0=80=20=EC=86=8C=EB=B9=84?= =?UTF-8?q?=EB=AA=A9=ED=91=9C=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ConsumptionGoalRepository.java | 7 +++++++ 1 file changed, 7 insertions(+) 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 61ab976e..2a928831 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 @@ -122,4 +122,11 @@ Optional findByCategoryIdAndUserId(@Param("categoryId") Long ca @Query("SELECT cg FROM ConsumptionGoal cg WHERE cg.user = :user AND cg.category = :category AND cg.deleted = true") Optional findByUserAndCategoryAndDeletedTrue(@Param("user") User user, @Param("category") Category category); + + @Query("SELECT cg FROM ConsumptionGoal cg " + + "WHERE cg.user.id = :userId AND cg.category.id = :categoryId AND cg.goalMonth <= :localDate " + + "ORDER BY cg.goalMonth DESC LIMIT 1") + Optional findLatelyGoal(@Param("userId") Long userId, @Param("categoryId") Long categoryId, + @Param("localDate") LocalDate localDate); + } From 2e1a8f2411c4cf5c0c67cc7a17c2ad9521feb0ae Mon Sep 17 00:00:00 2001 From: JunRain Date: Sat, 7 Sep 2024 12:19:01 +0900 Subject: [PATCH 002/204] =?UTF-8?q?[fix]=20@Builder=20=ED=8C=A8=ED=84=B4?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20deleted=EC=97=90=20false=EA=B0=80=20?= =?UTF-8?q?=EB=94=94=ED=8F=B4=EB=93=9C=20=EA=B0=92=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=93=A4=EC=96=B4=EA=B0=80=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bbteam/budgetbuddies/domain/category/entity/Category.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/entity/Category.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/entity/Category.java index 547c0f24..fe6b1466 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/entity/Category.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/entity/Category.java @@ -24,7 +24,7 @@ public class Category { private Boolean isDefault; @Column(nullable = false) - @ColumnDefault("false") + @Builder.Default private Boolean deleted = false; @ManyToOne(fetch = FetchType.LAZY) From f7946e7468e98d0ac8aa6f8e80c42512a3d5b2be Mon Sep 17 00:00:00 2001 From: JunRain Date: Sat, 7 Sep 2024 12:19:59 +0900 Subject: [PATCH 003/204] =?UTF-8?q?[feat]=20ConsumptionGoalRepositoryTest?= =?UTF-8?q?=20=ED=8A=B9=EC=A0=95=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=B4=20=ED=8A=B9=EC=A0=95=20=EB=8B=AC?= =?UTF-8?q?=20=EC=9D=B4=EC=A0=84=20=EA=B0=80=EC=9E=A5=20=EC=B5=9C=EA=B7=BC?= =?UTF-8?q?=20=EC=86=8C=EB=B9=84=EB=AA=A9=ED=91=9C=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ConsumptionGoalRepositoryTest.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java index 5682da22..ce3b43eb 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java @@ -1,6 +1,7 @@ package com.bbteam.budgetbuddies.domain.consumptiongoal.repository; import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; import java.time.LocalDate; import java.time.LocalDateTime; @@ -320,4 +321,66 @@ void findTopCategoriesByConsumptionCount_Success() { assertThat(firstResult.getConsumptionCount()).isEqualTo(2); assertThat(secondResult.getConsumptionCount()).isEqualTo(1); } + + @Test + @DisplayName("특정 카테고리에 대해 특정 달 이전 가장 최근 소비목표 조회") + void findLatelyGoal_Success() { + // given + LocalDate targetMonth = LocalDate.of(2024, 7, 1); + User user = userRepository.save( + User.builder().email("email").age(24).name("name").phoneNumber("010-1234-5678").build()); + + Category category = categoryRepository.save( + Category.builder().name("유저 카테고리").user(user).isDefault(false).build()); + + consumptionGoalRepository.save(ConsumptionGoal.builder() + .goalAmount(1L) + .consumeAmount(1L) + .user(user) + .goalMonth(targetMonth) + .category(category) + .build()); + + LocalDate searchDate = LocalDate.of(2024, 9, 1); + + // when + ConsumptionGoal result = consumptionGoalRepository.findLatelyGoal(user.getId(), category.getId(), searchDate).get(); + + // then + assertEquals(result.getGoalMonth(), targetMonth); + } + + @Test + @DisplayName("소비목표가 여러 개 있을 경우, 특정 카테고리에 대해 특정 달 이전 가장 최근 소비목표 조회") + void findLatelyGoal_Success2() { + // given + LocalDate targetMonth = LocalDate.of(2024, 7, 1); + User user = userRepository.save( + User.builder().email("email").age(24).name("name").phoneNumber("010-1234-5678").build()); + + Category category = categoryRepository.save( + Category.builder().name("유저 카테고리").user(user).isDefault(false).build()); + + consumptionGoalRepository.save(ConsumptionGoal.builder() + .goalAmount(1L) + .consumeAmount(1L) + .user(user) + .goalMonth(targetMonth) + .category(category) + .build()); + + consumptionGoalRepository.save(ConsumptionGoal.builder() + .goalAmount(1L) + .consumeAmount(1L) + .user(user) + .goalMonth(targetMonth.minusMonths(1)) + .category(category) + .build()); + + // when + ConsumptionGoal result = consumptionGoalRepository.findLatelyGoal(user.getId(), category.getId(), targetMonth).get(); + + // then + assertEquals(result.getGoalMonth(), targetMonth); + } } \ No newline at end of file From 748c5fe0f2f9fed52714af7776c594c0c7c778e0 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sat, 7 Sep 2024 12:30:47 +0900 Subject: [PATCH 004/204] =?UTF-8?q?[feat]=20ConsumptionGoalServiceImpl=20?= =?UTF-8?q?=EC=9D=B4=EC=A0=84=20=EB=8B=AC=EC=9D=B4=20=EC=95=84=EB=8B=8C=20?= =?UTF-8?q?=EC=B5=9C=EA=B7=BC=20=EB=8B=AC=EC=9D=84=20=EC=B0=B8=EC=A1=B0?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD=ED=95=B4?= =?UTF-8?q?=EC=84=9C=20=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../converter/ConsumptionGoalConverter.java | 10 ++++++++ .../service/ConsumptionGoalServiceImpl.java | 25 ++++++++++--------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java index 0ffd0cea..ba9cd970 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java @@ -34,6 +34,16 @@ public ConsumptionGoalResponseDto toConsumptionGoalResponseDto(ConsumptionGoal c .build(); } + public ConsumptionGoalResponseDto toConsumptionGoalResponseDtoFromPreviousGoal(ConsumptionGoal consumptionGoal) { + return ConsumptionGoalResponseDto.builder() + .categoryName(consumptionGoal.getCategory().getName()) + .categoryId(consumptionGoal.getCategory().getId()) + .goalAmount(consumptionGoal.getGoalAmount()) + .consumeAmount(0L) + .build(); + + } + public ConsumptionGoalResponseListDto toConsumptionGoalResponseListDto( List consumptionGoalList, LocalDate goalMonth) { Long totalGoalAmount = sumTotalGoalAmount(consumptionGoalList); 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 37dc7d7b..6c45cec0 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 @@ -78,7 +78,7 @@ public List getTopConsumptionGoalCategories(Long use .categoryName(getCategoryNameById(avgGoal.getCategoryId())) .goalAmount(avgGoal.getAverageAmount()) .build()) - .collect(Collectors.toList()); + .toList(); } @Override @@ -123,7 +123,7 @@ public List getAllConsumptionGoalCategories(L .amountDifference(consumeAmountDifference) .build(); }) - .collect(Collectors.toList()); + .toList(); } @Override @@ -180,7 +180,7 @@ public List getTopConsumptionCategories(Long userId, .categoryName(getCategoryNameById(category.getCategoryId())) .consumptionCount(category.getConsumptionCount()) .build()) - .collect(Collectors.toList()); + .toList(); } @Override @@ -225,7 +225,7 @@ public List getAllConsumptionCategories(Long .amountDifference(consumeAmountDifference) .build(); }) - .collect(Collectors.toList()); + .toList(); } private User findUserById(Long userId) { @@ -422,7 +422,7 @@ public ConsumptionGoalResponseListDto findUserConsumptionGoalList(Long userId, L LocalDate goalMonth = date.withDayOfMonth(1); Map goalMap = initializeGoalMap(userId); - updateGoalMapWithPreviousMonth(userId, goalMonth, goalMap); + updateGoalMapWithPrevious(userId, goalMonth, goalMap); updateGoalMapWithCurrentMonth(userId, goalMonth, goalMap); List consumptionGoalList = new ArrayList<>(goalMap.values()); @@ -437,18 +437,19 @@ private Map initializeGoalMap(Long userId) { .collect(Collectors.toMap(Category::getId, consumptionGoalConverter::toConsumptionGoalResponseDto)); } - private void updateGoalMapWithPreviousMonth(Long userId, LocalDate goalMonth, + private void updateGoalMapWithPrevious(Long userId, LocalDate goalMonth, Map goalMap) { - updateGoalMap(userId, goalMonth.minusMonths(1), goalMap); + goalMap.keySet().stream().map(categoryId -> + consumptionGoalRepository.findLatelyGoal(userId, categoryId, goalMonth.minusMonths(1))) + .filter(Optional::isPresent) + .map(Optional::get) + .map(consumptionGoalConverter::toConsumptionGoalResponseDtoFromPreviousGoal) + .forEach(goal -> goalMap.put(goal.getCategoryId(), goal)); } private void updateGoalMapWithCurrentMonth(Long userId, LocalDate goalMonth, Map goalMap) { - updateGoalMap(userId, goalMonth, goalMap); - } - - private void updateGoalMap(Long userId, LocalDate month, Map goalMap) { - consumptionGoalRepository.findConsumptionGoalByUserIdAndGoalMonth(userId, month) + consumptionGoalRepository.findConsumptionGoalByUserIdAndGoalMonth(userId, goalMonth) .stream() .map(consumptionGoalConverter::toConsumptionGoalResponseDto) .forEach(goal -> goalMap.put(goal.getCategoryId(), goal)); From 2c9f0311d260e915b2b2a82585fe71ca92401e47 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 8 Sep 2024 21:40:10 +0900 Subject: [PATCH 005/204] =?UTF-8?q?[fix]=20=EC=9D=B4=EB=B2=88=EB=8B=AC=20?= =?UTF-8?q?=EC=86=8C=EB=B9=84=EB=AA=A9=ED=91=9C=EA=B0=80=20=EC=97=86?= =?UTF-8?q?=EC=9D=84=EB=95=8C=20=EC=86=8C=EB=B9=84=EA=B8=88=EC=95=A1?= =?UTF-8?q?=EC=9D=B4=20=EC=B6=94=EA=B0=80=EB=90=98=EB=8A=94=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0,=20=EC=9D=B4=EC=A0=84=EB=8B=AC=EC=9D=B4=20=EC=95=84?= =?UTF-8?q?=EB=8B=8C=20=EC=9D=B4=EC=A0=84=EB=8B=AC=20=ED=8F=AC=ED=95=A8=20?= =?UTF-8?q?=EA=B0=80=EC=9E=A5=20=EC=B5=9C=EA=B7=BC=EB=8B=AC=EC=9D=98=20?= =?UTF-8?q?=EC=86=8C=EB=B9=84=EB=AA=A9=ED=91=9C=EB=A5=BC=20=EA=B0=80?= =?UTF-8?q?=EC=A0=B8=EC=98=A4=EB=8A=94=20=EA=B2=83=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumptiongoal/service/ConsumptionGoalServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 6c45cec0..05ed5ab9 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 @@ -470,8 +470,8 @@ public void recalculateConsumptionAmount(Expense expense, ExpenseUpdateRequestDt } private void restorePreviousGoalConsumptionAmount(Expense expense, User user) { - ConsumptionGoal previousConsumptionGoal = consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth( - user, expense.getCategory(), expense.getExpenseDate().toLocalDate().withDayOfMonth(1)) + ConsumptionGoal previousConsumptionGoal = consumptionGoalRepository.findLatelyGoal( + user.getId(), expense.getCategory().getId(), expense.getExpenseDate().toLocalDate().withDayOfMonth(1)) .orElseThrow(() -> new IllegalArgumentException("Not found consumptionGoal")); previousConsumptionGoal.restoreConsumeAmount(expense.getAmount()); From 32e192b275fa7d9cad2adbb47857aa544e02ec89 Mon Sep 17 00:00:00 2001 From: MJJ Date: Mon, 9 Sep 2024 00:17:08 +0900 Subject: [PATCH 006/204] =?UTF-8?q?[refactor]=20=EB=82=B4=20=EC=86=8C?= =?UTF-8?q?=EB=B9=84=20=EB=AA=A9=ED=91=9C=20=EA=B8=88=EC=95=A1,=20?= =?UTF-8?q?=EC=86=8C=EB=B9=84=20=EA=B8=88=EC=95=A1=20=EC=9D=B4=EB=B2=88=20?= =?UTF-8?q?=EB=8B=AC=EB=A7=8C=20=EC=A1=B0=ED=9A=8C=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ConsumptionGoalRepository.java | 11 ++++++++--- .../service/ConsumptionGoalServiceImpl.java | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) 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 61ab976e..aeb7dc8e 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 @@ -65,9 +65,11 @@ List findAvgConsumptionAmountByCategory( "WHERE cg.category.isDefault = true " + "AND cg.deleted = false " + "AND cg.user.id = :userId " + + "AND cg.goalMonth >= :currentMonth " + "GROUP BY cg.category.id " + "ORDER BY cg.category.id") - List findAllConsumptionAmountByUserId(@Param("userId") Long userId); + List findAllConsumptionAmountByUserId(@Param("userId") Long userId, + @Param("currentMonth") LocalDate currentMonth); @Query("SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AvgConsumptionGoalDto(" + "cg.category.id, AVG(cg.goalAmount))" + @@ -91,9 +93,11 @@ List findAvgGoalAmountByCategory( "WHERE cg.category.isDefault = true " + "AND cg.deleted = false " + "AND cg.user.id = :userId " + + "AND cg.goalMonth >= :currentMonth " + "GROUP BY cg.category.id " + "ORDER BY cg.category.id") - List findAllGoalAmountByUserId(@Param("userId") Long userId); + List findAllGoalAmountByUserId(@Param("userId") Long userId, + @Param("currentMonth") LocalDate currentMonth); @Query("SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.CategoryConsumptionCountDto(" + "e.category.id, COUNT(e)) " + @@ -121,5 +125,6 @@ Optional findByCategoryIdAndUserId(@Param("categoryId") Long ca @Param("userId") Long userId); @Query("SELECT cg FROM ConsumptionGoal cg WHERE cg.user = :user AND cg.category = :category AND cg.deleted = true") - Optional findByUserAndCategoryAndDeletedTrue(@Param("user") User user, @Param("category") Category category); + Optional findByUserAndCategoryAndDeletedTrue(@Param("user") User user, + @Param("category") Category category); } 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 37dc7d7b..3c363fdf 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 @@ -299,7 +299,7 @@ private List getMyConsumptionAmount(Long userId) { List myConsumptionAmountList = new ArrayList<>(); List myConsumptionGoalDto = consumptionGoalRepository.findAllConsumptionAmountByUserId( - userId); + userId, currentMonth); Map myConsumptionMap = myConsumptionGoalDto.stream() .collect(Collectors.toMap(MyConsumptionGoalDto::getCategoryId, Function.identity())); @@ -339,7 +339,7 @@ private List getMyGoalAmount(Long userId) { List myConsumptionAmountList = new ArrayList<>(); List myConsumptionGoalDto = consumptionGoalRepository.findAllGoalAmountByUserId( - userId); + userId, currentMonth); Map myConsumptionMap = myConsumptionGoalDto.stream() .collect(Collectors.toMap(MyConsumptionGoalDto::getCategoryId, Function.identity())); From 4aac8d12fc084301b8866a038f1c20bd13b8e5c5 Mon Sep 17 00:00:00 2001 From: MJJ Date: Mon, 9 Sep 2024 00:18:57 +0900 Subject: [PATCH 007/204] =?UTF-8?q?[refactor]=20=EB=82=B4=20=EC=86=8C?= =?UTF-8?q?=EB=B9=84=20=EB=AA=A9=ED=91=9C=20=EA=B8=88=EC=95=A1,=20?= =?UTF-8?q?=EC=86=8C=EB=B9=84=20=EA=B8=88=EC=95=A1=20=EC=9D=B4=EB=B2=88=20?= =?UTF-8?q?=EB=8B=AC=EB=A7=8C=20=EC=A1=B0=ED=9A=8C=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=EC=9D=B8=ED=95=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ConsumptionGoalRepositoryTest.java | 5 +++-- .../consumptiongoal/service/ConsumptionGoalServiceTest.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java index 5682da22..a39b23e2 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java @@ -201,7 +201,7 @@ void findAvgConsumptionAmountByCategory_Success() { void findAllConsumptionAmountByUserId_Success() { // when List result = consumptionGoalRepository.findAllConsumptionAmountByUserId( - peerUser1.getId()); + peerUser1.getId(), currentMonth); // then assertThat(result).isNotEmpty(); @@ -273,7 +273,8 @@ void findAvgGoalAmountByCategory_Success() { @DisplayName("또래 나이와 성별 정보를 통해 카테고리와 평균 목표 금액 조회 성공") void findAllGoalAmountByUserId_Success() { // when - List result = consumptionGoalRepository.findAllGoalAmountByUserId(peerUser1.getId()); + List result = consumptionGoalRepository.findAllGoalAmountByUserId(peerUser1.getId(), + currentMonth); // then assertThat(result).isNotEmpty(); diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java index 8ec47b51..fbfdf094 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java @@ -438,7 +438,7 @@ void getAllConsumptionGoalCategories_Success() { given(categoryRepository.findAllByIsDefaultTrue()).willReturn(defaultCategories); given(consumptionGoalRepository.findAvgGoalAmountByCategory( anyInt(), anyInt(), any(), any())).willReturn(categoryAvgList); - given(consumptionGoalRepository.findAllGoalAmountByUserId(user.getId())) + given(consumptionGoalRepository.findAllGoalAmountByUserId(user.getId(), currentMonth)) .willReturn(myConsumptionAmountList); given(userRepository.findById(user.getId())).willReturn(Optional.of(user)); From 7674374d9612d43da83d8a7d6e08ad027e8f90af Mon Sep 17 00:00:00 2001 From: MJJ Date: Mon, 9 Sep 2024 23:49:07 +0900 Subject: [PATCH 008/204] =?UTF-8?q?[refactor]=20=EB=B9=84=EA=B5=90?= =?UTF-8?q?=EB=B6=84=EC=84=9D=20=EB=A0=88=ED=8F=AC=ED=8A=B8=20=ED=8F=89?= =?UTF-8?q?=EA=B7=A0=EA=B0=92=20->=20=EC=A4=91=EC=95=99=EA=B0=92=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 카테고리별 소비 목표 금액 중앙값으로 반환 카테고리별 소비 금액 중앙값으로 반환 --- .../repository/ConsumptionGoalRepository.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) 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 aeb7dc8e..748bc16f 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 @@ -99,6 +99,7 @@ List findAvgGoalAmountByCategory( List findAllGoalAmountByUserId(@Param("userId") Long userId, @Param("currentMonth") LocalDate currentMonth); + // TODO expenseRepository로 옮기기 @Query("SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.CategoryConsumptionCountDto(" + "e.category.id, COUNT(e)) " + "FROM Expense e " + @@ -127,4 +128,34 @@ Optional findByCategoryIdAndUserId(@Param("categoryId") Long ca @Query("SELECT cg FROM ConsumptionGoal cg WHERE cg.user = :user AND cg.category = :category AND cg.deleted = true") Optional findByUserAndCategoryAndDeletedTrue(@Param("user") User user, @Param("category") Category category); + + @Query("SELECT cg.goalAmount " + + "FROM ConsumptionGoal cg " + + "WHERE cg.category.isDefault = true " + + "AND cg.deleted = false " + + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + + "AND cg.user.gender = :peerGender " + + "AND cg.goalMonth >= :currentMonth " + + "AND cg.category.id = :categoryId ") + List findGoalAmountsByCategory( + @Param("peerAgeStart") int peerAgeStart, + @Param("peerAgeEnd") int peerAgeEnd, + @Param("peerGender") Gender peerGender, + @Param("currentMonth") LocalDate currentMonth, + @Param("categoryId") Long categoryId); + + @Query("SELECT cg.consumeAmount " + + "FROM ConsumptionGoal cg " + + "WHERE cg.category.isDefault = true " + + "AND cg.deleted = false " + + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + + "AND cg.user.gender = :peerGender " + + "AND cg.goalMonth >= :currentMonth " + + "AND cg.category.id = :categoryId ") + List findConsumeAmountsByCategory( + @Param("peerAgeStart") int peerAgeStart, + @Param("peerAgeEnd") int peerAgeEnd, + @Param("peerGender") Gender peerGender, + @Param("currentMonth") LocalDate currentMonth, + @Param("categoryId") Long categoryId); } From cab3ff2db9eeaee4b91e3ddad234be6b22505fae Mon Sep 17 00:00:00 2001 From: MJJ Date: Tue, 10 Sep 2024 00:02:13 +0900 Subject: [PATCH 009/204] =?UTF-8?q?[refactor]=20=EC=86=8C=EB=B9=84=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=A1=B0=ED=9A=8Ctop3?= =?UTF-8?q?=20repository=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ConsumptionGoalRepository.java | 23 ++----------------- .../expense/repository/ExpenseRepository.java | 22 +++++++++++++++++- 2 files changed, 23 insertions(+), 22 deletions(-) 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 748bc16f..12a8148a 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 @@ -13,7 +13,6 @@ import com.bbteam.budgetbuddies.domain.category.entity.Category; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AvgConsumptionGoalDto; -import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.CategoryConsumptionCountDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.MyConsumptionGoalDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; import com.bbteam.budgetbuddies.domain.user.entity.User; @@ -99,24 +98,6 @@ List findAvgGoalAmountByCategory( List findAllGoalAmountByUserId(@Param("userId") Long userId, @Param("currentMonth") LocalDate currentMonth); - // TODO expenseRepository로 옮기기 - @Query("SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.CategoryConsumptionCountDto(" + - "e.category.id, COUNT(e)) " + - "FROM Expense e " + - "WHERE e.category.isDefault = true " + - "AND e.deleted = false " + - "AND e.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + - "AND e.user.gender = :peerGender " + - "AND e.expenseDate >= :currentMonth " + - "AND e.amount > 0 " + - "GROUP BY e.category.id " + - "ORDER BY COUNT(e) DESC") - List findTopCategoriesByConsumptionCount( - @Param("peerAgeStart") int peerAgeStart, - @Param("peerAgeEnd") int peerAgeEnd, - @Param("peerGender") Gender peerGender, - @Param("currentMonth") LocalDateTime currentMonth); - @Modifying @Query("UPDATE ConsumptionGoal cg SET cg.deleted = TRUE WHERE cg.category.id = :categoryId AND cg.user.id = :userId") void softDeleteByCategoryIdAndUserId(@Param("categoryId") Long categoryId, @Param("userId") Long userId); @@ -137,7 +118,7 @@ Optional findByUserAndCategoryAndDeletedTrue(@Param("user") Use "AND cg.user.gender = :peerGender " + "AND cg.goalMonth >= :currentMonth " + "AND cg.category.id = :categoryId ") - List findGoalAmountsByCategory( + List findGoalAmountsByCategories( @Param("peerAgeStart") int peerAgeStart, @Param("peerAgeEnd") int peerAgeEnd, @Param("peerGender") Gender peerGender, @@ -152,7 +133,7 @@ List findGoalAmountsByCategory( "AND cg.user.gender = :peerGender " + "AND cg.goalMonth >= :currentMonth " + "AND cg.category.id = :categoryId ") - List findConsumeAmountsByCategory( + List findConsumeAmountsByCategories( @Param("peerAgeStart") int peerAgeStart, @Param("peerAgeEnd") int peerAgeEnd, @Param("peerGender") Gender peerGender, diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepository.java index 86dfd81a..b55c55fd 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepository.java @@ -8,19 +8,39 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.CategoryConsumptionCountDto; import com.bbteam.budgetbuddies.domain.expense.entity.Expense; import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.enums.Gender; public interface ExpenseRepository extends JpaRepository { @Query("SELECT e FROM Expense e WHERE e.user = :user AND e.expenseDate BETWEEN :startDate AND :endDate ORDER BY e.expenseDate DESC") List findAllByUserIdForPeriod(@Param("user") User user, @Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate); - List findByCategoryIdAndUserIdAndExpenseDateBetweenAndDeletedFalse(Long categoryId, Long userId, LocalDateTime startDate, LocalDateTime endDate); + List findByCategoryIdAndUserIdAndExpenseDateBetweenAndDeletedFalse(Long categoryId, Long userId, + LocalDateTime startDate, LocalDateTime endDate); List findByCategoryIdAndUserIdAndDeletedFalse(Long categoryId, Long userId); @Modifying @Query("UPDATE Expense e SET e.deleted = TRUE WHERE e.id = :expenseId") void softDeleteById(@Param("expenseId") Long expenseId); + + @Query("SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.CategoryConsumptionCountDto(" + + "e.category.id, COUNT(e)) " + + "FROM Expense e " + + "WHERE e.category.isDefault = true " + + "AND e.deleted = false " + + "AND e.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + + "AND e.user.gender = :peerGender " + + "AND e.expenseDate >= :currentMonth " + + "AND e.amount > 0 " + + "GROUP BY e.category.id " + + "ORDER BY COUNT(e) DESC") + List findTopCategoriesByConsumptionCount( + @Param("peerAgeStart") int peerAgeStart, + @Param("peerAgeEnd") int peerAgeEnd, + @Param("peerGender") Gender peerGender, + @Param("currentMonth") LocalDateTime currentMonth); } From 7260a3889d23286c9591f8f0ee042fcbbf224e12 Mon Sep 17 00:00:00 2001 From: MJJ Date: Tue, 10 Sep 2024 00:02:33 +0900 Subject: [PATCH 010/204] =?UTF-8?q?[refactor]=20=EC=86=8C=EB=B9=84=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=A1=B0=ED=9A=8C=20top?= =?UTF-8?q?3=20repository=20=EC=9D=B4=EB=8F=99=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ConsumptionGoalRepositoryTest.java | 2 +- .../consumptiongoal/service/ConsumptionGoalServiceTest.java | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java index a39b23e2..7ace22d8 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java @@ -302,7 +302,7 @@ void findTopCategoriesByConsumptionCount_Success() { Gender peerGender = Gender.MALE; LocalDate currentMonth = LocalDate.now(); - List result = consumptionGoalRepository.findTopCategoriesByConsumptionCount( + List result = expenseRepository.findTopCategoriesByConsumptionCount( peerAgeStart, peerAgeEnd, peerGender, currentMonth.atStartOfDay()); // then diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java index fbfdf094..49346396 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java @@ -43,6 +43,7 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.repository.ConsumptionGoalRepository; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.entity.Expense; +import com.bbteam.budgetbuddies.domain.expense.repository.ExpenseRepository; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; import com.bbteam.budgetbuddies.enums.Gender; @@ -62,6 +63,8 @@ class ConsumptionGoalServiceTest { private CategoryRepository categoryRepository; @Mock private UserRepository userRepository; + @Mock + private ExpenseRepository expenseRepository; @Spy private ConsumptionGoalConverter consumptionGoalConverter; @@ -383,7 +386,7 @@ void getTopConsumptionCategories_Success() { String peerGender = "MALE"; given(userRepository.findById(user.getId())).willReturn(Optional.of(user)); - given(consumptionGoalRepository.findTopCategoriesByConsumptionCount(peerAgeStart, peerAgeEnd, + given(expenseRepository.findTopCategoriesByConsumptionCount(peerAgeStart, peerAgeEnd, Gender.valueOf(peerGender), currentMonth.atStartOfDay())) .willReturn(List.of(topConsumption1, topConsumption2, topConsumption3)); From e34b8bd51d1aa0e04a46217bd5647068c1277d24 Mon Sep 17 00:00:00 2001 From: MJJ Date: Tue, 10 Sep 2024 00:26:42 +0900 Subject: [PATCH 011/204] =?UTF-8?q?[refactor]=20=EB=B9=84=EA=B5=90?= =?UTF-8?q?=EB=B6=84=EC=84=9D=20=EB=A0=88=ED=8F=AC=ED=8A=B8=20=ED=8F=89?= =?UTF-8?q?=EA=B7=A0=EA=B0=92=20->=20=EC=A4=91=EC=95=99=EA=B0=92=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 소비목표 전체 조회 중앙값으로 수정 - 소비금액 전체 조회 중앙값으로 수정 --- .../service/ConsumptionGoalServiceImpl.java | 115 ++++++++++++++---- 1 file changed, 92 insertions(+), 23 deletions(-) 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 3c363fdf..9b44beee 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 @@ -8,6 +8,7 @@ import java.time.LocalTime; import java.time.temporal.TemporalAdjusters; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -38,6 +39,7 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.repository.ConsumptionGoalRepository; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.entity.Expense; +import com.bbteam.budgetbuddies.domain.expense.repository.ExpenseRepository; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; import com.bbteam.budgetbuddies.enums.Gender; @@ -58,6 +60,7 @@ public class ConsumptionGoalServiceImpl implements ConsumptionGoalService { private final LocalDate currentMonth = LocalDate.now().withDayOfMonth(1); private final LocalDate startOfWeek = LocalDate.now().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); private final LocalDate endOfWeek = LocalDate.now().with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)); + private final ExpenseRepository expenseRepository; private int peerAgeStart; private int peerAgeEnd; private Gender peerGender; @@ -88,7 +91,7 @@ public List getAllConsumptionGoalCategories(L checkPeerInfo(userId, peerAgeS, peerAgeE, peerG); - List categoryAvgList = getAvgGoalAmount(); + List categoryAvgList = getMedianGoalAmount(); List myConsumptionAmountList = getMyGoalAmount(userId); @@ -171,7 +174,7 @@ public List getTopConsumptionCategories(Long userId, checkPeerInfo(userId, peerAgeS, peerAgeE, peerG); - List categoryConsumptionCountDto = consumptionGoalRepository + List categoryConsumptionCountDto = expenseRepository .findTopCategoriesByConsumptionCount(peerAgeStart, peerAgeEnd, peerGender, currentMonth.atStartOfDay()); return categoryConsumptionCountDto.stream() @@ -190,7 +193,7 @@ public List getAllConsumptionCategories(Long checkPeerInfo(userId, peerAgeS, peerAgeE, peerG); - List categoryAvgList = getAvgConsumptionAmount(); + List categoryAvgList = getMedianConsumeAmount(); List myConsumptionAmountList = getMyConsumptionAmount(userId); @@ -272,26 +275,26 @@ private void setAgeGroupByUser(int userAge) { peerAgeEnd = 19; } } - - private List getAvgConsumptionAmount() { - - List defaultCategories = categoryRepository.findAllByIsDefaultTrue(); - List categoryAvgList = new ArrayList<>(); - - List avgConsumptionGoalDto = consumptionGoalRepository - .findAvgConsumptionAmountByCategory(peerAgeStart, peerAgeEnd, peerGender, currentMonth); - - Map categoryAvgMap = avgConsumptionGoalDto.stream() - .collect(Collectors.toMap(AvgConsumptionGoalDto::getCategoryId, Function.identity())); - - for (Category category : defaultCategories) { - AvgConsumptionGoalDto avgDto = categoryAvgMap.getOrDefault(category.getId(), - new AvgConsumptionGoalDto(category.getId(), 0.0)); - - categoryAvgList.add(avgDto); - } - return categoryAvgList; - } + // 평균소비목표금액을 가져오는 메서드 + // private List getAvgConsumptionAmount() { + // + // List defaultCategories = categoryRepository.findAllByIsDefaultTrue(); + // List categoryAvgList = new ArrayList<>(); + // + // List avgConsumptionGoalDto = consumptionGoalRepository + // .findAvgConsumptionAmountByCategory(peerAgeStart, peerAgeEnd, peerGender, currentMonth); + // + // Map categoryAvgMap = avgConsumptionGoalDto.stream() + // .collect(Collectors.toMap(AvgConsumptionGoalDto::getCategoryId, Function.identity())); + // + // for (Category category : defaultCategories) { + // AvgConsumptionGoalDto avgDto = categoryAvgMap.getOrDefault(category.getId(), + // new AvgConsumptionGoalDto(category.getId(), 0.0)); + // + // categoryAvgList.add(avgDto); + // } + // return categoryAvgList; + // } private List getMyConsumptionAmount(Long userId) { @@ -369,6 +372,72 @@ private Long roundToNearest10(Long amount) { return roundedAmount.longValue(); } + private List getMedianGoalAmount() { + + List defaultCategories = categoryRepository.findAllByIsDefaultTrue(); + + List categoryMedianList = new ArrayList<>(); + + for (Category category : defaultCategories) { + + List goalAmounts = consumptionGoalRepository.findGoalAmountsByCategories( + peerAgeStart, peerAgeEnd, peerGender, currentMonth, category.getId()); + + if (goalAmounts != null && !goalAmounts.isEmpty()) { + + double median = calculateMedian(goalAmounts); + categoryMedianList.add(new AvgConsumptionGoalDto(category.getId(), median)); + } else { + // 데이터가 없는 경우 기본 값으로 + categoryMedianList.add(new AvgConsumptionGoalDto(category.getId(), 0.0)); + } + } + return categoryMedianList; + } + + private List getMedianConsumeAmount() { + + List defaultCategories = categoryRepository.findAllByIsDefaultTrue(); + + List categoryMedianList = new ArrayList<>(); + + for (Category category : defaultCategories) { + + List goalAmounts = consumptionGoalRepository.findConsumeAmountsByCategories( + peerAgeStart, peerAgeEnd, peerGender, currentMonth, category.getId()); + + if (goalAmounts != null && !goalAmounts.isEmpty()) { + double median = calculateMedian(goalAmounts); + categoryMedianList.add(new AvgConsumptionGoalDto(category.getId(), median)); + } else { + // 데이터가 없는 경우 기본 값으로 + categoryMedianList.add(new AvgConsumptionGoalDto(category.getId(), 0.0)); + } + } + return categoryMedianList; + } + + private double calculateMedian(List values) { + + // 0 보다 큰(소비 금액이 존재하는) 값만 계산 + List filteredValues = values.stream() + .filter(value -> value > 0) + .collect(Collectors.toList()); + + int size = filteredValues.size(); + + if (size == 0) { + return 0.0; + } + Collections.sort(filteredValues); + + if (size % 2 == 0) { + return (filteredValues.get(size / 2 - 1) + filteredValues.get(size / 2)) / 2.0; + } else { + return filteredValues.get(size / 2); + } + } + @Override @Transactional public ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId, From f2015ccd384ee36417068578d064fd6032de0e9e Mon Sep 17 00:00:00 2001 From: MJJ Date: Tue, 10 Sep 2024 14:13:19 +0900 Subject: [PATCH 012/204] =?UTF-8?q?[refactor]=2020=EB=8C=80=20=EB=AF=B8?= =?UTF-8?q?=EB=A7=8C=20=EC=82=AC=EC=9A=A9=EC=9E=90=201~19=20=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumptiongoal/service/ConsumptionGoalServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9b44beee..9b1aead3 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 @@ -271,7 +271,7 @@ private void setAgeGroupByUser(int userAge) { peerAgeStart = 29; peerAgeEnd = 99; } else { - peerAgeStart = 0; + peerAgeStart = 1; peerAgeEnd = 19; } } From eccf8ad90692fe5ba14fe1062ee175e7224e6fae Mon Sep 17 00:00:00 2001 From: MJJ Date: Wed, 11 Sep 2024 15:54:49 +0900 Subject: [PATCH 013/204] =?UTF-8?q?[refactor]=20=EC=A4=91=EC=95=99?= =?UTF-8?q?=EA=B0=92=20=EA=B3=84=EC=82=B0=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ConsumptionGoalServiceImpl.java | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) 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 11dc73f4..549da334 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 @@ -374,6 +374,14 @@ private Long roundToNearest10(Long amount) { private List getMedianGoalAmount() { + /** + 기본 카테고리만 가져와서 리스트에 저장 + 기본 카테고리 id 별로 소비 목표 데이터를 가져와 리스트로 저장 + 데이터가 존재하는 경우 리스트의 중앙값 계산 + 리스트가 비어 있으면 기본 값 0으로 설정 + 카테고리 별 중앙값 리스트 반환 + **/ + List defaultCategories = categoryRepository.findAllByIsDefaultTrue(); List categoryMedianList = new ArrayList<>(); @@ -397,6 +405,14 @@ private List getMedianGoalAmount() { private List getMedianConsumeAmount() { + /** + 기본 카테고리만 가져와서 리스트에 저장 + 기본 카테고리 id 별로 소비 금액 데이터를 가져와 리스트로 저장 + 데이터가 존재하는 경우 리스트의 중앙값 계산 + 리스트가 비어 있으면 기본 값 0으로 설정 + 카테고리 별 중앙값 리스트 반환 + **/ + List defaultCategories = categoryRepository.findAllByIsDefaultTrue(); List categoryMedianList = new ArrayList<>(); @@ -419,7 +435,13 @@ private List getMedianConsumeAmount() { private double calculateMedian(List values) { - // 0 보다 큰(소비 금액이 존재하는) 값만 계산 + /** + values 리스트에서 0 보다 큰(소비 금액이 존재하는) 값만 필터링 + size에 필터링한 값의 개수를 저장 + 홀수일 경우 size / 2 (가운데) 인덱스에 해당하는 값 반환 + 짝수일 경우 와 size/ 2 -1 인덱스 데이터와 size / 2의 인덱스 데이터의 평균을 처리 + **/ + List filteredValues = values.stream() .filter(value -> value > 0) .collect(Collectors.toList()); @@ -509,7 +531,7 @@ private Map initializeGoalMap(Long userId) { private void updateGoalMapWithPrevious(Long userId, LocalDate goalMonth, Map goalMap) { goalMap.keySet().stream().map(categoryId -> - consumptionGoalRepository.findLatelyGoal(userId, categoryId, goalMonth.minusMonths(1))) + consumptionGoalRepository.findLatelyGoal(userId, categoryId, goalMonth.minusMonths(1))) .filter(Optional::isPresent) .map(Optional::get) .map(consumptionGoalConverter::toConsumptionGoalResponseDtoFromPreviousGoal) From 7063e477ea00ef50e1bf704e554dce22b063c9f9 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Wed, 11 Sep 2024 16:48:30 +0900 Subject: [PATCH 014/204] =?UTF-8?q?[feat]=20Notice=20API,=20Controller=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notice/controller/NoticeApi.java | 17 ++++++++ .../notice/controller/NoticeController.java | 43 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeApi.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeApi.java new file mode 100644 index 00000000..4a037f85 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeApi.java @@ -0,0 +1,17 @@ +package com.bbteam.budgetbuddies.domain.notice.controller; + +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.notice.dto.NoticeRequestDto; +import org.springframework.data.domain.Pageable; + +public interface NoticeApi { + + ApiResponse saveNotice(NoticeRequestDto dto); + ApiResponse findAllWithPaging(Pageable pageable); + + ApiResponse findOne(Long noticeId); + ApiResponse modifyNotice(Long noticeId, NoticeRequestDto noticeRequestDto); + ApiResponse deleteNotice(Long noticeId); + + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java new file mode 100644 index 00000000..2ba83ab4 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java @@ -0,0 +1,43 @@ +package com.bbteam.budgetbuddies.domain.notice.controller; + + +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.notice.dto.NoticeRequestDto; +import com.bbteam.budgetbuddies.domain.notice.service.NoticeService; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@Validated +public class NoticeController implements NoticeApi{ + + private final NoticeService noticeService; + + @Override + public ApiResponse saveNotice(NoticeRequestDto dto) { + return null; + } + + @Override + public ApiResponse findAllWithPaging(Pageable pageable) { + return null; + } + + @Override + public ApiResponse findOne(Long noticeId) { + return null; + } + + @Override + public ApiResponse modifyNotice(Long noticeId, NoticeRequestDto noticeRequestDto) { + return null; + } + + @Override + public ApiResponse deleteNotice(Long noticeId) { + return null; + } +} From 7c084817901b1d73020476b26b0657219175697c Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Wed, 11 Sep 2024 16:48:44 +0900 Subject: [PATCH 015/204] =?UTF-8?q?[feat]=20Notice=20Entity=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notice/entity/Notice.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/notice/entity/Notice.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/entity/Notice.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/entity/Notice.java new file mode 100644 index 00000000..e4ac9095 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/entity/Notice.java @@ -0,0 +1,24 @@ +package com.bbteam.budgetbuddies.domain.notice.entity; + +import com.bbteam.budgetbuddies.common.BaseEntity; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import jakarta.persistence.*; +import lombok.*; +import lombok.experimental.SuperBuilder; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@SuperBuilder +public class Notice extends BaseEntity { + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; + + private String title; + + private String body; + +} From b402565cd7efdc73f7571794e470ab5613ba816d Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 09:43:15 +0900 Subject: [PATCH 016/204] =?UTF-8?q?[refactor]=20Notice=20API=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notice/controller/NoticeApi.java | 2 +- .../notice/controller/NoticeController.java | 22 +++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeApi.java index 4a037f85..7767efad 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeApi.java @@ -6,7 +6,7 @@ public interface NoticeApi { - ApiResponse saveNotice(NoticeRequestDto dto); + ApiResponse saveNotice(Long userId, NoticeRequestDto dto); ApiResponse findAllWithPaging(Pageable pageable); ApiResponse findOne(Long noticeId); diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java index 2ba83ab4..334bd412 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java @@ -4,8 +4,13 @@ import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.notice.dto.NoticeRequestDto; import com.bbteam.budgetbuddies.domain.notice.service.NoticeService; +import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.springdoc.core.annotations.ParameterObject; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; @@ -17,27 +22,30 @@ public class NoticeController implements NoticeApi{ private final NoticeService noticeService; @Override - public ApiResponse saveNotice(NoticeRequestDto dto) { - return null; + public ApiResponse saveNotice(@ExistUser Long userId, NoticeRequestDto dto) { + return ApiResponse.onSuccess(noticeService.save(dto, userId)); } @Override - public ApiResponse findAllWithPaging(Pageable pageable) { - return null; + public ApiResponse findAllWithPaging( + @ParameterObject @PageableDefault(page = 0, size = 20, + sort = "updatedAt", direction = Sort.Direction.DESC) Pageable pageable) { + return ApiResponse.onSuccess(noticeService.findAll(pageable)); } @Override public ApiResponse findOne(Long noticeId) { - return null; + return ApiResponse.onSuccess(noticeService.findOne(noticeId)); } @Override public ApiResponse modifyNotice(Long noticeId, NoticeRequestDto noticeRequestDto) { - return null; + return ApiResponse.onSuccess(noticeService.update(noticeId, noticeRequestDto)); } @Override public ApiResponse deleteNotice(Long noticeId) { - return null; + noticeService.delete(noticeId); + return ApiResponse.onSuccess("ok"); } } From 4e01e04b65fc920b1cf5d7a85bb1a62e56fc3b61 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 09:43:36 +0900 Subject: [PATCH 017/204] =?UTF-8?q?[refactor]=20Notice=20update=20method?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bbteam/budgetbuddies/domain/notice/entity/Notice.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/entity/Notice.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/entity/Notice.java index e4ac9095..83a9cea0 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/entity/Notice.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/entity/Notice.java @@ -1,6 +1,7 @@ package com.bbteam.budgetbuddies.domain.notice.entity; import com.bbteam.budgetbuddies.common.BaseEntity; +import com.bbteam.budgetbuddies.domain.notice.dto.NoticeRequestDto; import com.bbteam.budgetbuddies.domain.user.entity.User; import jakarta.persistence.*; import lombok.*; @@ -21,4 +22,9 @@ public class Notice extends BaseEntity { private String body; + public void update(NoticeRequestDto dto) { + title = dto.getTitle(); + body = dto.getBody(); + } + } From 8434db5ae37c3013eac0b02b4ed73e5d702144f7 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 09:43:49 +0900 Subject: [PATCH 018/204] =?UTF-8?q?[feat]=20Notice=20Converter=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notice/converter/NoticeConverter.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/notice/converter/NoticeConverter.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/converter/NoticeConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/converter/NoticeConverter.java new file mode 100644 index 00000000..1cf74a81 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/converter/NoticeConverter.java @@ -0,0 +1,27 @@ +package com.bbteam.budgetbuddies.domain.notice.converter; + +import com.bbteam.budgetbuddies.domain.notice.dto.NoticeRequestDto; +import com.bbteam.budgetbuddies.domain.notice.dto.NoticeResponseDto; +import com.bbteam.budgetbuddies.domain.notice.entity.Notice; +import com.bbteam.budgetbuddies.domain.user.entity.User; + +public class NoticeConverter { + + public static Notice toEntity(NoticeRequestDto dto, User user) { + return Notice.builder() + .title(dto.getTitle()) + .body(dto.getBody()) + .user(user) + .build(); + } + + public static NoticeResponseDto toDto(Notice notice) { + return NoticeResponseDto.builder() + .noticeId(notice.getId()) + .title(notice.getTitle()) + .body(notice.getBody()) + .userName(notice.getUser().getName()) + .updatedAt(notice.getUpdatedAt()) + .build(); + } +} From ca9a49de549ab329687e9c81e665c2172fe3d78f Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 09:43:58 +0900 Subject: [PATCH 019/204] =?UTF-8?q?[feat]=20Notice=20Dto=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notice/dto/NoticeRequestDto.java | 14 ++++++++++++++ .../domain/notice/dto/NoticeResponseDto.java | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeRequestDto.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeResponseDto.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeRequestDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeRequestDto.java new file mode 100644 index 00000000..d276cbc9 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeRequestDto.java @@ -0,0 +1,14 @@ +package com.bbteam.budgetbuddies.domain.notice.dto; + +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Builder +@Getter +public class NoticeRequestDto { + private Long userId; + private String title; + private String body; +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeResponseDto.java new file mode 100644 index 00000000..627a922c --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeResponseDto.java @@ -0,0 +1,16 @@ +package com.bbteam.budgetbuddies.domain.notice.dto; + +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Builder +@Getter +public class NoticeResponseDto { + private Long noticeId; + private String userName; + private String title; + private String body; + private LocalDateTime updatedAt; +} From d91da72ebcdbd63965c3ea9b0c001fab428abcdd Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 09:44:07 +0900 Subject: [PATCH 020/204] =?UTF-8?q?[feat]=20Notice=20Repository=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notice/repository/NoticeRepository.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/notice/repository/NoticeRepository.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/repository/NoticeRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/repository/NoticeRepository.java new file mode 100644 index 00000000..4887cabe --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/repository/NoticeRepository.java @@ -0,0 +1,11 @@ +package com.bbteam.budgetbuddies.domain.notice.repository; + +import com.bbteam.budgetbuddies.domain.notice.entity.Notice; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NoticeRepository extends JpaRepository { + + Page findAllWithPaging(Pageable pageable); +} From baa18e9096bb1198b1821e810c9372de1a6fbe9c Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 09:44:15 +0900 Subject: [PATCH 021/204] =?UTF-8?q?[feat]=20Notice=20Service=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notice/service/NoticeService.java | 19 ++++++ .../notice/service/NoticeServiceImpl.java | 68 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeService.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeServiceImpl.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeService.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeService.java new file mode 100644 index 00000000..1a5aa087 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeService.java @@ -0,0 +1,19 @@ +package com.bbteam.budgetbuddies.domain.notice.service; + +import com.bbteam.budgetbuddies.domain.notice.dto.NoticeRequestDto; +import com.bbteam.budgetbuddies.domain.notice.dto.NoticeResponseDto; +import org.springframework.data.domain.Pageable; + +import java.util.List; + +public interface NoticeService { + NoticeResponseDto save(NoticeRequestDto dto, Long userId); + + NoticeResponseDto findOne(Long noticeId); + + List findAll(Pageable pageable); + + NoticeResponseDto update(Long noticeId, NoticeRequestDto dto); + + void delete(Long noticeId); +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeServiceImpl.java new file mode 100644 index 00000000..69baa61d --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeServiceImpl.java @@ -0,0 +1,68 @@ +package com.bbteam.budgetbuddies.domain.notice.service; + +import com.bbteam.budgetbuddies.domain.notice.converter.NoticeConverter; +import com.bbteam.budgetbuddies.domain.notice.dto.NoticeRequestDto; +import com.bbteam.budgetbuddies.domain.notice.dto.NoticeResponseDto; +import com.bbteam.budgetbuddies.domain.notice.entity.Notice; +import com.bbteam.budgetbuddies.domain.notice.repository.NoticeRepository; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.NoSuchElementException; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class NoticeServiceImpl implements NoticeService{ + + private final NoticeRepository noticeRepository; + private final UserRepository userRepository; + @Override + @Transactional + public NoticeResponseDto save(NoticeRequestDto dto, Long userId) { + User user = userRepository.findById(userId).orElseThrow(() -> new NoSuchElementException("member x")); + Notice notice = NoticeConverter.toEntity(dto, user); + noticeRepository.save(notice); + return NoticeConverter.toDto(notice); + } + + @Override + public NoticeResponseDto findOne(Long noticeId) { + Notice notice = findNotice(noticeId); + return NoticeConverter.toDto(notice); + + } + + @Override + public List findAll(Pageable pageable) { + return noticeRepository.findAll(pageable).stream() + .map(NoticeConverter::toDto) + .toList(); + } + + @Override + @Transactional + public NoticeResponseDto update(Long noticeId, NoticeRequestDto dto) { + Notice notice = findNotice(noticeId); + notice.update(dto); + return NoticeConverter.toDto(notice); + } + + @Override + @Transactional + public void delete(Long noticeId) { + Notice notice = findNotice(noticeId); + noticeRepository.delete(notice); + } + private Notice findNotice(Long noticeId) { + Notice notice = noticeRepository.findById(noticeId).orElseThrow(() -> new NoSuchElementException("notice x")); + return notice; + } + + +} From e68f6fe032c57dd7b89bbc56805df56983d8e20c Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 09:49:40 +0900 Subject: [PATCH 022/204] =?UTF-8?q?[feat]=20Notice=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apiPayload/code/status/ErrorStatus.java | 1 + .../domain/notice/validation/ExistNotice.java | 17 ++++++++++ .../validation/NoticeExistValidation.java | 33 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/ExistNotice.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/NoticeExistValidation.java diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java index b8dc8c81..d5ccfffa 100644 --- a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java @@ -15,6 +15,7 @@ public enum ErrorStatus implements BaseErrorCode { _BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON400", "잘못된 요청"), USER_NOT_FOUND(HttpStatus.BAD_REQUEST, "USER4001", "사용자가 없습니다."), COMMENT_NOT_FOUND(HttpStatus.BAD_REQUEST, "COMMENT4001", "해당 댓글이 없습니다.") , + NOTICE_NOT_FOUND(HttpStatus.BAD_REQUEST, "NOTICE4001", "해당 공지가 없습니다."), PAGE_LOWER_ZERO(HttpStatus.BAD_REQUEST, "PAGE4001", "요청된 페이지가 0보다 작습니다."); diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/ExistNotice.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/ExistNotice.java new file mode 100644 index 00000000..9e97f397 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/ExistNotice.java @@ -0,0 +1,17 @@ +package com.bbteam.budgetbuddies.domain.notice.validation; + +import com.bbteam.budgetbuddies.domain.user.validation.UserExistValidation; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = NoticeExistValidation.class) +@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExistNotice { + String message() default "해당 user가 존재하지 않습니다."; + Class[] groups() default {}; + Class[] payload() default {}; +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/NoticeExistValidation.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/NoticeExistValidation.java new file mode 100644 index 00000000..575db053 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/NoticeExistValidation.java @@ -0,0 +1,33 @@ +package com.bbteam.budgetbuddies.domain.notice.validation; + +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.bbteam.budgetbuddies.domain.notice.entity.Notice; +import com.bbteam.budgetbuddies.domain.notice.repository.NoticeRepository; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import lombok.RequiredArgsConstructor; + +import java.util.Optional; + +@RequiredArgsConstructor +public class NoticeExistValidation implements ConstraintValidator { + + private final NoticeRepository noticeRepository; + @Override + public boolean isValid(Long aLong, ConstraintValidatorContext constraintValidatorContext) { + Optional notice = noticeRepository.findById(aLong); + + if(notice.isEmpty()) { + constraintValidatorContext.disableDefaultConstraintViolation(); + constraintValidatorContext.buildConstraintViolationWithTemplate(ErrorStatus.NOTICE_NOT_FOUND.toString()).addConstraintViolation(); + return false; + } + + return true; + } + + @Override + public void initialize(ExistNotice constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } +} From be8069a169646829af5c08094864c79bba3086b4 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 14:51:43 +0900 Subject: [PATCH 023/204] =?UTF-8?q?[refactor]=20NoticeRepository=20?= =?UTF-8?q?=EB=B6=88=ED=95=84=EC=9A=94=20method=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/domain/notice/repository/NoticeRepository.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/repository/NoticeRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/repository/NoticeRepository.java index 4887cabe..b888df14 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/repository/NoticeRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/repository/NoticeRepository.java @@ -7,5 +7,4 @@ public interface NoticeRepository extends JpaRepository { - Page findAllWithPaging(Pageable pageable); } From fc3871262e0d1d3b22ff75abcdc122f2a3860956 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 14:52:02 +0900 Subject: [PATCH 024/204] =?UTF-8?q?[refactor]=20findAll=20return=20Page?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notice/service/NoticeService.java | 3 ++- .../domain/notice/service/NoticeServiceImpl.java | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeService.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeService.java index 1a5aa087..c6949cd5 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeService.java @@ -2,6 +2,7 @@ import com.bbteam.budgetbuddies.domain.notice.dto.NoticeRequestDto; import com.bbteam.budgetbuddies.domain.notice.dto.NoticeResponseDto; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import java.util.List; @@ -11,7 +12,7 @@ public interface NoticeService { NoticeResponseDto findOne(Long noticeId); - List findAll(Pageable pageable); + Page findAll(Pageable pageable); NoticeResponseDto update(Long noticeId, NoticeRequestDto dto); diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeServiceImpl.java index 69baa61d..f7a15878 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeServiceImpl.java @@ -8,6 +8,7 @@ import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -39,10 +40,9 @@ public NoticeResponseDto findOne(Long noticeId) { } @Override - public List findAll(Pageable pageable) { - return noticeRepository.findAll(pageable).stream() - .map(NoticeConverter::toDto) - .toList(); + public Page findAll(Pageable pageable) { + return noticeRepository.findAll(pageable) + .map(NoticeConverter::toDto); } @Override From e24c885005fa5fc75dbd5502c4d339cb46c57476 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 14:52:14 +0900 Subject: [PATCH 025/204] =?UTF-8?q?[test]=20NoticeService=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notice/service/NoticeServiceTest.java | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 src/test/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeServiceTest.java diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeServiceTest.java new file mode 100644 index 00000000..0456d9e0 --- /dev/null +++ b/src/test/java/com/bbteam/budgetbuddies/domain/notice/service/NoticeServiceTest.java @@ -0,0 +1,164 @@ +package com.bbteam.budgetbuddies.domain.notice.service; + +import com.bbteam.budgetbuddies.domain.notice.dto.NoticeRequestDto; +import com.bbteam.budgetbuddies.domain.notice.dto.NoticeResponseDto; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.transaction.annotation.Transactional; + +import java.util.NoSuchElementException; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@Transactional +class NoticeServiceTest { + + @Autowired + NoticeService noticeService; + @Autowired + UserRepository userRepository; + + @Test + void save() { + User user1 = User.builder() + .name("tester1") + .email("1234") + .age(5) + .phoneNumber("123456") + .build(); + userRepository.save(user1); + + NoticeRequestDto dto = NoticeRequestDto.builder() + .body("바바이") + .title("헬로우") + .build(); + NoticeResponseDto result = noticeService.save(dto, user1.getId()); + + Assertions.assertThat(result.getUserName()).isEqualTo(user1.getName()); + Assertions.assertThat(result.getTitle()).isEqualTo("헬로우"); + Assertions.assertThat(result.getBody()).isEqualTo("바바이"); + } + + @Test + void findOne() { + User user1 = User.builder() + .name("tester1") + .email("1234") + .age(5) + .phoneNumber("123456") + .build(); + userRepository.save(user1); + + NoticeRequestDto dto = NoticeRequestDto.builder() + .body("바바이") + .title("헬로우") + .build(); + NoticeResponseDto result = noticeService.save(dto, user1.getId()); + + NoticeResponseDto one = noticeService.findOne(result.getNoticeId()); + + Assertions.assertThat(result.getNoticeId()).isEqualTo(one.getNoticeId()); + Assertions.assertThat(result.getUserName()).isEqualTo(one.getUserName()); + } + + @Test + void findAll() { + User user1 = User.builder() + .name("tester1") + .email("1234") + .age(5) + .phoneNumber("123456") + .build(); + userRepository.save(user1); + + NoticeRequestDto dto = NoticeRequestDto.builder() + .body("바바이") + .title("헬로우") + .build(); + NoticeResponseDto result = noticeService.save(dto, user1.getId()); + NoticeRequestDto dto2 = NoticeRequestDto.builder() + .body("바바이") + .title("헬로우") + .build(); + NoticeResponseDto result2 = noticeService.save(dto2, user1.getId()); + NoticeRequestDto dto3 = NoticeRequestDto.builder() + .body("바바이") + .title("헬로우") + .build(); + NoticeResponseDto result3 = noticeService.save(dto3, user1.getId()); + NoticeRequestDto dto4 = NoticeRequestDto.builder() + .body("바바이") + .title("헬로우") + .build(); + NoticeResponseDto result4 = noticeService.save(dto4, user1.getId()); + + PageRequest request1 = PageRequest.of(0, 2); + Page list1 = noticeService.findAll(request1); + Assertions.assertThat(list1.getNumberOfElements()).isEqualTo(2); + + PageRequest request2 = PageRequest.of(1, 2); + Page list2 = noticeService.findAll(request2); + Assertions.assertThat(list2.getNumberOfElements()).isEqualTo(2); + + PageRequest request3 = PageRequest.of(2, 2); + Page list3 = noticeService.findAll(request3); + Assertions.assertThat(list3.getNumberOfElements()).isEqualTo(0); + } + + @Test + void update() { + User user1 = User.builder() + .name("tester1") + .email("1234") + .age(5) + .phoneNumber("123456") + .build(); + userRepository.save(user1); + + NoticeRequestDto dto = NoticeRequestDto.builder() + .body("바바이") + .title("헬로우") + .build(); + NoticeResponseDto result = noticeService.save(dto, user1.getId()); + + NoticeRequestDto updateDto = NoticeRequestDto.builder() + .body("좋아요") + .title("아니에요!") + .build(); + NoticeResponseDto update = noticeService.update(result.getNoticeId(), updateDto); + + Assertions.assertThat(update.getNoticeId()).isEqualTo(result.getNoticeId()); + Assertions.assertThat(update.getBody()).isEqualTo("좋아요"); + Assertions.assertThat(update.getTitle()).isEqualTo("아니에요!"); + } + + @Test + void delete() { + User user1 = User.builder() + .name("tester1") + .email("1234") + .age(5) + .phoneNumber("123456") + .build(); + userRepository.save(user1); + + NoticeRequestDto dto = NoticeRequestDto.builder() + .body("바바이") + .title("헬로우") + .build(); + NoticeResponseDto result = noticeService.save(dto, user1.getId()); + + noticeService.delete(result.getNoticeId()); + + assertThrows(NoSuchElementException.class, () -> noticeService.findOne(result.getNoticeId())); + } + + +} \ No newline at end of file From 57377ced8dfa567097fc3d8b02d9792b9acf9456 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 16:15:04 +0900 Subject: [PATCH 026/204] =?UTF-8?q?[feat]=20NoticeController=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80=20=EB=B0=8F?= =?UTF-8?q?=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20input=20=EB=B0=A9=EC=8B=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notice/controller/NoticeController.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java index 334bd412..130ac4c4 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java @@ -4,29 +4,32 @@ import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.notice.dto.NoticeRequestDto; import com.bbteam.budgetbuddies.domain.notice.service.NoticeService; +import com.bbteam.budgetbuddies.domain.notice.validation.ExistNotice; import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springdoc.core.annotations.ParameterObject; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @RequiredArgsConstructor @Validated +@RequestMapping("/notices") public class NoticeController implements NoticeApi{ private final NoticeService noticeService; @Override - public ApiResponse saveNotice(@ExistUser Long userId, NoticeRequestDto dto) { + @PostMapping + public ApiResponse saveNotice(@ExistUser @RequestParam Long userId, @RequestBody NoticeRequestDto dto) { return ApiResponse.onSuccess(noticeService.save(dto, userId)); } @Override + @GetMapping("/all") public ApiResponse findAllWithPaging( @ParameterObject @PageableDefault(page = 0, size = 20, sort = "updatedAt", direction = Sort.Direction.DESC) Pageable pageable) { @@ -34,17 +37,20 @@ public ApiResponse findAllWithPaging( } @Override - public ApiResponse findOne(Long noticeId) { + @GetMapping("/{noticeId}") + public ApiResponse findOne(@ExistNotice @PathVariable Long noticeId) { return ApiResponse.onSuccess(noticeService.findOne(noticeId)); } @Override - public ApiResponse modifyNotice(Long noticeId, NoticeRequestDto noticeRequestDto) { + @PutMapping("/{noticeId}") + public ApiResponse modifyNotice(@ExistNotice @PathVariable Long noticeId, @RequestBody NoticeRequestDto noticeRequestDto) { return ApiResponse.onSuccess(noticeService.update(noticeId, noticeRequestDto)); } @Override - public ApiResponse deleteNotice(Long noticeId) { + @DeleteMapping("/{noticeId}") + public ApiResponse deleteNotice(@ExistNotice @PathVariable Long noticeId) { noticeService.delete(noticeId); return ApiResponse.onSuccess("ok"); } From 014f74f5d590a20df6dd78c2b018645b81de46f0 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 16:16:27 +0900 Subject: [PATCH 027/204] =?UTF-8?q?[feat]=20NoticeApi=20Swagger=20API=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notice/controller/NoticeApi.java | 100 +++++++++++++++++- 1 file changed, 96 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeApi.java index 7767efad..6c0ca7af 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeApi.java @@ -2,16 +2,108 @@ import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.notice.dto.NoticeRequestDto; +import com.bbteam.budgetbuddies.domain.notice.validation.ExistNotice; +import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.data.domain.Pageable; +import org.springframework.http.MediaType; public interface NoticeApi { - ApiResponse saveNotice(Long userId, NoticeRequestDto dto); + @Operation(summary = "[Admin] 공지 저장 API", description = "공지를 저장하는 API입니다.", + requestBody = @RequestBody( + content = @Content( + schema = @Schema( + allOf = { NoticeRequestDto.class}, + requiredProperties = {"title", "body"} + ), + mediaType = MediaType.APPLICATION_JSON_VALUE, + examples = { + @ExampleObject(name = "someExample1", value = """ + { + "title" : "공지 제목 써주시면 됩니다", + "body" : "공지 내용 써주시면 됩니다." + } + """) + } + ) + ) + ) + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "userId", description = "현재 데이터를 게시하는 어드민입니다. parameter"), + } + ) + ApiResponse saveNotice(@ExistUser Long userId, NoticeRequestDto dto); + + @Operation(summary = "[Admin] 공지 전체 확인 API", description = "공지 전체를 확인하는 API입니다. 페이징 포함합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "size", description = "페이징 size입니다. parameter"), + @Parameter(name = "page", description = "페이징 page입니다. parameter") + } + ) ApiResponse findAllWithPaging(Pageable pageable); - ApiResponse findOne(Long noticeId); - ApiResponse modifyNotice(Long noticeId, NoticeRequestDto noticeRequestDto); - ApiResponse deleteNotice(Long noticeId); + @Operation(summary = "[Admin] 공지 확인 API", description = "공지를 확인하는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "noticeId", description = "확인할 공지 id입니다. pathVariable", in = ParameterIn.PATH), + } + ) + ApiResponse findOne(@ExistNotice Long noticeId); + + @Operation(summary = "[Admin] 공지 수정 API", description = "공지를 수정하는 API입니다.", + requestBody = @RequestBody( + content = @Content( + schema = @Schema( + allOf = { NoticeRequestDto.class}, + requiredProperties = {"title", "body"} + ), + mediaType = MediaType.APPLICATION_JSON_VALUE, + examples = { + @ExampleObject(name = "someExample1", value = """ + { + "title" : "공지 제목 써주시면 됩니다", + "body" : "공지 내용 써주시면 됩니다." + } + """) + } + ) + ) + ) + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "noticeId", description = "수정할 공지 id입니다. pathVariable", in = ParameterIn.PATH), + } + ) + ApiResponse modifyNotice(@ExistNotice Long noticeId, NoticeRequestDto noticeRequestDto); + + @Operation(summary = "[Admin] 공지 삭제 API", description = "공지를 삭제하는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "noticeId", description = "삭제할 공지 id입니다. pathVariable", in = ParameterIn.PATH), + } + ) + ApiResponse deleteNotice(@ExistNotice Long noticeId); } From 39815fa140d1fc7348b840792873543837fbd245 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 16:16:54 +0900 Subject: [PATCH 028/204] =?UTF-8?q?[refactor]=20NoticeRequestDto=20userId?= =?UTF-8?q?=20field=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bbteam/budgetbuddies/domain/notice/dto/NoticeRequestDto.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeRequestDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeRequestDto.java index d276cbc9..777350e4 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeRequestDto.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeRequestDto.java @@ -8,7 +8,6 @@ @Builder @Getter public class NoticeRequestDto { - private Long userId; private String title; private String body; } From 80d6c23127dd05089af4fac49934c0d6a3f08350 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 16:17:32 +0900 Subject: [PATCH 029/204] =?UTF-8?q?[refactor]=20updatedAt=EC=97=90=20jsonF?= =?UTF-8?q?ormat=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/domain/notice/dto/NoticeResponseDto.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeResponseDto.java index 627a922c..7cda191a 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeResponseDto.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeResponseDto.java @@ -1,5 +1,6 @@ package com.bbteam.budgetbuddies.domain.notice.dto; +import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Builder; import lombok.Getter; @@ -12,5 +13,6 @@ public class NoticeResponseDto { private String userName; private String title; private String body; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Asia/Seoul") private LocalDateTime updatedAt; } From 43d605487e4fde318478b82ebad268d15a8a3b5d Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 16:21:35 +0900 Subject: [PATCH 030/204] =?UTF-8?q?[refactor]=20NoticeController=20findAll?= =?UTF-8?q?=20paging=20=EA=B8=B0=EC=A4=80=20createdAt=20DESC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/notice/controller/NoticeController.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java index 130ac4c4..d756b9fa 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/controller/NoticeController.java @@ -3,11 +3,13 @@ import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.notice.dto.NoticeRequestDto; +import com.bbteam.budgetbuddies.domain.notice.dto.NoticeResponseDto; import com.bbteam.budgetbuddies.domain.notice.service.NoticeService; import com.bbteam.budgetbuddies.domain.notice.validation.ExistNotice; import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; import lombok.RequiredArgsConstructor; import org.springdoc.core.annotations.ParameterObject; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; @@ -24,33 +26,33 @@ public class NoticeController implements NoticeApi{ @Override @PostMapping - public ApiResponse saveNotice(@ExistUser @RequestParam Long userId, @RequestBody NoticeRequestDto dto) { + public ApiResponse saveNotice(@ExistUser @RequestParam Long userId, @RequestBody NoticeRequestDto dto) { return ApiResponse.onSuccess(noticeService.save(dto, userId)); } @Override @GetMapping("/all") - public ApiResponse findAllWithPaging( + public ApiResponse> findAllWithPaging( @ParameterObject @PageableDefault(page = 0, size = 20, - sort = "updatedAt", direction = Sort.Direction.DESC) Pageable pageable) { + sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) { return ApiResponse.onSuccess(noticeService.findAll(pageable)); } @Override @GetMapping("/{noticeId}") - public ApiResponse findOne(@ExistNotice @PathVariable Long noticeId) { + public ApiResponse findOne(@ExistNotice @PathVariable Long noticeId) { return ApiResponse.onSuccess(noticeService.findOne(noticeId)); } @Override @PutMapping("/{noticeId}") - public ApiResponse modifyNotice(@ExistNotice @PathVariable Long noticeId, @RequestBody NoticeRequestDto noticeRequestDto) { + public ApiResponse modifyNotice(@ExistNotice @PathVariable Long noticeId, @RequestBody NoticeRequestDto noticeRequestDto) { return ApiResponse.onSuccess(noticeService.update(noticeId, noticeRequestDto)); } @Override @DeleteMapping("/{noticeId}") - public ApiResponse deleteNotice(@ExistNotice @PathVariable Long noticeId) { + public ApiResponse deleteNotice(@ExistNotice @PathVariable Long noticeId) { noticeService.delete(noticeId); return ApiResponse.onSuccess("ok"); } From e74e78bb9c7e74f8d877c443c8fed153086eca87 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 16:24:53 +0900 Subject: [PATCH 031/204] =?UTF-8?q?[refactor]=20NoticeResponseDto=EC=9D=98?= =?UTF-8?q?=20field=20updatedAt=20->=20createdAt=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/domain/notice/dto/NoticeResponseDto.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeResponseDto.java index 7cda191a..5eb72ad9 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeResponseDto.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/dto/NoticeResponseDto.java @@ -14,5 +14,5 @@ public class NoticeResponseDto { private String title; private String body; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Asia/Seoul") - private LocalDateTime updatedAt; + private LocalDateTime createdAt; } From 9e30ecc53f5ebdd5b7d5fefed9b6e8fbc2ebb269 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 12 Sep 2024 16:25:21 +0900 Subject: [PATCH 032/204] =?UTF-8?q?[refactor]=20NoticeConverter=20NoticeRe?= =?UTF-8?q?sponseDto=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?Converter=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/domain/notice/converter/NoticeConverter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/converter/NoticeConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/converter/NoticeConverter.java index 1cf74a81..229f2507 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/converter/NoticeConverter.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/converter/NoticeConverter.java @@ -21,7 +21,7 @@ public static NoticeResponseDto toDto(Notice notice) { .title(notice.getTitle()) .body(notice.getBody()) .userName(notice.getUser().getName()) - .updatedAt(notice.getUpdatedAt()) + .createdAt(notice.getCreatedAt()) .build(); } } From 79d7783c6280941f86a557d8ab01535081cc05de Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 18 Sep 2024 13:37:07 +0900 Subject: [PATCH 033/204] =?UTF-8?q?[feat]=201:N=20=EC=97=B0=EA=B4=80?= =?UTF-8?q?=EA=B4=80=EA=B3=84=20=ED=95=84=EB=93=9C=EC=97=90=20@NotFound(ac?= =?UTF-8?q?tion=20=3D=20NotFoundAction.IGNORE)=20=EC=96=B4=EB=85=B8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > SoftDelete와 FetchType.LAZY를 동시에 사용하기 위함. --- .../budgetbuddies/domain/category/entity/Category.java | 3 +++ .../bbteam/budgetbuddies/domain/comment/entity/Comment.java | 5 +++++ .../domain/consumptiongoal/entity/ConsumptionGoal.java | 4 ++++ .../bbteam/budgetbuddies/domain/expense/entity/Expense.java | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/entity/Category.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/entity/Category.java index fe6b1466..16e647e2 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/entity/Category.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/entity/Category.java @@ -4,6 +4,8 @@ import jakarta.persistence.*; import lombok.*; import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; @Entity @Getter @@ -28,6 +30,7 @@ public class Category { private Boolean deleted = false; @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) @JoinColumn(name = "user_id") private User user; } 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 81827240..f54ac0e3 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 @@ -15,6 +15,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; @Entity @Getter @@ -27,14 +29,17 @@ public class Comment extends BaseEntity { private String content; @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) @JoinColumn(name = "user_id") private User user; @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) @JoinColumn(name = "discount_info_id") private DiscountInfo discountInfo; @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) @JoinColumn(name = "support_info_id") private SupportInfo supportInfo; diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/entity/ConsumptionGoal.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/entity/ConsumptionGoal.java index a0dea332..71d2accc 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/entity/ConsumptionGoal.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/entity/ConsumptionGoal.java @@ -15,6 +15,8 @@ import lombok.*; import lombok.experimental.SuperBuilder; import lombok.extern.slf4j.Slf4j; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; @Entity @Getter @@ -37,10 +39,12 @@ public class ConsumptionGoal extends BaseEntity { private LocalDate goalMonth; @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) @JoinColumn(name = "user_id") private User user; @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) @JoinColumn(name = "category_id") private Category category; diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/entity/Expense.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/entity/Expense.java index 45e44253..e03c72f9 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/entity/Expense.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/entity/Expense.java @@ -15,6 +15,8 @@ import jakarta.validation.constraints.Min; import lombok.*; import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; @Entity @Getter @@ -33,10 +35,12 @@ public class Expense extends BaseEntity { private LocalDateTime expenseDate; @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) @JoinColumn(name = "user_id") private User user; @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) @JoinColumn(name = "category_id") private Category category; From 97ad65f09212934517cc44b76c4df7f133420e2d Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 18 Sep 2024 13:37:44 +0900 Subject: [PATCH 034/204] =?UTF-8?q?[feat]=20User=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=EC=97=90=20=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - role: 사용자 권한 - mobileCarrier: 사용자 통신사 --- .../budgetbuddies/domain/user/entity/User.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java index dc4d6d6d..1a120ca7 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java @@ -3,12 +3,10 @@ import com.bbteam.budgetbuddies.common.BaseEntity; import com.bbteam.budgetbuddies.domain.user.dto.UserDto; import com.bbteam.budgetbuddies.enums.Gender; +import com.bbteam.budgetbuddies.enums.Role; import jakarta.persistence.*; import jakarta.validation.constraints.Min; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import lombok.experimental.SuperBuilder; import java.time.LocalDateTime; @@ -21,6 +19,9 @@ @SuperBuilder public class User extends BaseEntity { + @Builder.Default + private Role role = Role.USER; // 기본값 User 권한 + @Column(nullable = false, unique = true) private String phoneNumber; @@ -37,6 +38,9 @@ public class User extends BaseEntity { @Column(nullable = false, length = 50, unique = true) private String email; + @Column(nullable = false) + private String mobileCarrier; // 통신사 + @Column(length = 1000) private String photoUrl; From f03096a49eecf14670847ee1776be771e282df17 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 18 Sep 2024 13:38:16 +0900 Subject: [PATCH 035/204] =?UTF-8?q?[feat]=20ConnectedInfo=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EB=B0=8F=20=EB=A0=88=ED=8F=AC=EC=A7=80?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../connectedinfo/entity/ConnectedInfo.java | 45 +++++++++++++++++++ .../repository/ConnectedInfoRepository.java | 9 ++++ 2 files changed, 54 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/connectedinfo/entity/ConnectedInfo.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/connectedinfo/repository/ConnectedInfoRepository.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/connectedinfo/entity/ConnectedInfo.java b/src/main/java/com/bbteam/budgetbuddies/domain/connectedinfo/entity/ConnectedInfo.java new file mode 100644 index 00000000..397b1251 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/connectedinfo/entity/ConnectedInfo.java @@ -0,0 +1,45 @@ +package com.bbteam.budgetbuddies.domain.connectedinfo.entity; + +import com.bbteam.budgetbuddies.common.BaseEntity; +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.*; +import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; +import org.hibernate.annotations.SQLRestriction; + +@Entity +@Getter +@Setter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@SuperBuilder +@SQLRestriction("deleted = false") +public class ConnectedInfo extends BaseEntity { + /** + * 연결된 지원정보 또는 할인정보를 나타내는 엔티티 (교차 엔티티) + * supportInfo, discountInfo 둘 중 하나 이상 반드시 연결되어 있어야 함. + */ + + @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) + @JoinColumn(name = "hashtag_id") + private Hashtag hashtag; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "support_info_id") + @NotFound(action = NotFoundAction.IGNORE) + private SupportInfo supportInfo; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "discount_info_id") + @NotFound(action = NotFoundAction.IGNORE) + private DiscountInfo discountInfo; + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/connectedinfo/repository/ConnectedInfoRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/connectedinfo/repository/ConnectedInfoRepository.java new file mode 100644 index 00000000..368dce04 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/connectedinfo/repository/ConnectedInfoRepository.java @@ -0,0 +1,9 @@ +package com.bbteam.budgetbuddies.domain.connectedinfo.repository; + +import com.bbteam.budgetbuddies.domain.connectedinfo.entity.ConnectedInfo; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ConnectedInfoRepository extends JpaRepository { + + +} From c9bc1e7cf15e7fae8cde07b4d8873f524ae24cfb Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 18 Sep 2024 13:38:23 +0900 Subject: [PATCH 036/204] =?UTF-8?q?[feat]=20Faq=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EB=B0=8F=20=EB=A0=88=ED=8F=AC=EC=A7=80=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/domain/faq/entity/Faq.java | 32 +++++++++++++++++++ .../domain/faq/repository/FaqRepository.java | 9 ++++++ 2 files changed, 41 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faq/entity/Faq.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqRepository.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/entity/Faq.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/entity/Faq.java new file mode 100644 index 00000000..d1a0d000 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/entity/Faq.java @@ -0,0 +1,32 @@ +package com.bbteam.budgetbuddies.domain.faq.entity; + +import com.bbteam.budgetbuddies.common.BaseEntity; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.*; +import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; + + +@Entity +@Getter +@Setter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@SuperBuilder +public class Faq extends BaseEntity { + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + @NotFound(action = NotFoundAction.IGNORE) + private User user; + + private String title; + + private String body; + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqRepository.java new file mode 100644 index 00000000..4ab1c63e --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqRepository.java @@ -0,0 +1,9 @@ +package com.bbteam.budgetbuddies.domain.faq.repository; + +import com.bbteam.budgetbuddies.domain.faq.entity.Faq; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface FaqRepository extends JpaRepository { + + +} From 086dd10a29cf3d3182b7dd22fefd60d1abb72049 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 18 Sep 2024 13:38:33 +0900 Subject: [PATCH 037/204] =?UTF-8?q?[feat]=20FavoriteHashtag=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EB=B0=8F=20=EB=A0=88=ED=8F=AC=EC=A7=80?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity/FavoriteHashtag.java | 38 +++++++++++++++++++ .../repository/FavoriteHashtagRepository.java | 9 +++++ 2 files changed, 47 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/entity/FavoriteHashtag.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/repository/FavoriteHashtagRepository.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/entity/FavoriteHashtag.java b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/entity/FavoriteHashtag.java new file mode 100644 index 00000000..0fbbbd81 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/entity/FavoriteHashtag.java @@ -0,0 +1,38 @@ +package com.bbteam.budgetbuddies.domain.favoritehashtag.entity; + +import com.bbteam.budgetbuddies.common.BaseEntity; +import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; +import com.bbteam.budgetbuddies.domain.user.entity.User; +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.NotFound; +import org.hibernate.annotations.NotFoundAction; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@SuperBuilder +public class FavoriteHashtag extends BaseEntity { + /** + * 특정 사용자가 관심있는 해시태그를 나타내는 엔티티 (교차 엔티티) + */ + + @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) + @JoinColumn(name = "user_id") + private User user; + + @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) + @JoinColumn(name = "hashtag_id") + private Hashtag hashtag; + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/repository/FavoriteHashtagRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/repository/FavoriteHashtagRepository.java new file mode 100644 index 00000000..2d8135a8 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/repository/FavoriteHashtagRepository.java @@ -0,0 +1,9 @@ +package com.bbteam.budgetbuddies.domain.favoritehashtag.repository; + +import com.bbteam.budgetbuddies.domain.favoritehashtag.entity.FavoriteHashtag; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface FavoriteHashtagRepository extends JpaRepository { + + +} From a0bcbf646520a6f8f525e99e49255da6d7c41e2c Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 18 Sep 2024 13:38:38 +0900 Subject: [PATCH 038/204] =?UTF-8?q?[feat]=20Hashtag=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EB=B0=8F=20=EB=A0=88=ED=8F=AC=EC=A7=80=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/hashtag/entity/Hashtag.java | 18 ++++++++++++++++++ .../hashtag/repository/HashtagRepository.java | 9 +++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/hashtag/entity/Hashtag.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/hashtag/repository/HashtagRepository.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/entity/Hashtag.java b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/entity/Hashtag.java new file mode 100644 index 00000000..83b32059 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/entity/Hashtag.java @@ -0,0 +1,18 @@ +package com.bbteam.budgetbuddies.domain.hashtag.entity; + +import com.bbteam.budgetbuddies.common.BaseEntity; +import jakarta.persistence.Entity; +import lombok.*; +import lombok.experimental.SuperBuilder; + +@Entity +@Getter +@Setter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@SuperBuilder +public class Hashtag extends BaseEntity { + + private String name; // 해시태그 이름 + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/repository/HashtagRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/repository/HashtagRepository.java new file mode 100644 index 00000000..488c4831 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/repository/HashtagRepository.java @@ -0,0 +1,9 @@ +package com.bbteam.budgetbuddies.domain.hashtag.repository; + +import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface HashtagRepository extends JpaRepository { + + +} From 3bd6698930a20fed2fa9690867b891303b81b4da Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 18 Sep 2024 13:39:33 +0900 Subject: [PATCH 039/204] =?UTF-8?q?[feat]=20Notification=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EB=B0=8F=20=EB=A0=88=ED=8F=AC=EC=A7=80?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/entity/Notification.java | 32 +++++++++++++++++++ .../repository/NotificationRepository.java | 9 ++++++ 2 files changed, 41 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/notification/entity/Notification.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/notification/repository/NotificationRepository.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notification/entity/Notification.java b/src/main/java/com/bbteam/budgetbuddies/domain/notification/entity/Notification.java new file mode 100644 index 00000000..f491d7be --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notification/entity/Notification.java @@ -0,0 +1,32 @@ +package com.bbteam.budgetbuddies.domain.notification.entity; + +import com.bbteam.budgetbuddies.common.BaseEntity; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.enums.NotiType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.*; +import lombok.experimental.SuperBuilder; + +@Entity +@Getter +@Setter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@SuperBuilder +public class Notification extends BaseEntity { + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "user_id") + private User user; + + private String message; // 알림 메시지 내용 + + private Boolean readOrNot; // 읽기 여부 + + private NotiType type; // 알림 타입 (소비경고/정보/시스템/커뮤니티) + + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notification/repository/NotificationRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/notification/repository/NotificationRepository.java new file mode 100644 index 00000000..9bf5ec39 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notification/repository/NotificationRepository.java @@ -0,0 +1,9 @@ +package com.bbteam.budgetbuddies.domain.notification.repository; + +import com.bbteam.budgetbuddies.domain.notification.entity.Notification; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NotificationRepository extends JpaRepository { + + +} From 2beb55dca49aec5eeac909dd61af7dbc29eec731 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 18 Sep 2024 13:40:19 +0900 Subject: [PATCH 040/204] =?UTF-8?q?[feat]=20Report=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EB=B0=8F=20=EB=A0=88=ED=8F=AC=EC=A7=80=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/report/entity/Report.java | 38 +++++++++++++++++++ .../report/repository/ReportRepository.java | 9 +++++ 2 files changed, 47 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/report/entity/Report.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepository.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/entity/Report.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/entity/Report.java new file mode 100644 index 00000000..b5db7e5d --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/entity/Report.java @@ -0,0 +1,38 @@ +package com.bbteam.budgetbuddies.domain.report.entity; + +import com.bbteam.budgetbuddies.common.BaseEntity; +import com.bbteam.budgetbuddies.domain.comment.entity.Comment; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.*; +import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; + +@Entity +@Getter +@Setter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@SuperBuilder +public class Report extends BaseEntity { + /** + * 특정 사용자가 특정 댓글을 신고하는 엔티티 (교차 엔티티) + */ + + @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) + @JoinColumn(name = "user_id") + private User user; + + @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) + @JoinColumn(name = "comment_id") + private Comment comment; + + private String reason; // 신고 사유 + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepository.java new file mode 100644 index 00000000..6661333c --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepository.java @@ -0,0 +1,9 @@ +package com.bbteam.budgetbuddies.domain.report.repository; + +import com.bbteam.budgetbuddies.domain.report.entity.Report; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReportRepository extends JpaRepository { + + +} From cdb4d01968fadbddcc4cb593aec12b6d0cf84292 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 18 Sep 2024 13:41:29 +0900 Subject: [PATCH 041/204] =?UTF-8?q?[feat]=20NotiType=20Enum=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20(=EC=95=8C=EB=A6=BC=20=EC=9C=A0=ED=98=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CONSUMPTION: 소비경고알림 - INFORMATION: 정보알림 - SYSTEM: 시스템알림 - COMMUNITY: 커뮤니티 알림 --- .../java/com/bbteam/budgetbuddies/enums/NotiType.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/enums/NotiType.java diff --git a/src/main/java/com/bbteam/budgetbuddies/enums/NotiType.java b/src/main/java/com/bbteam/budgetbuddies/enums/NotiType.java new file mode 100644 index 00000000..c04ca9f0 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/enums/NotiType.java @@ -0,0 +1,10 @@ +package com.bbteam.budgetbuddies.enums; + +public enum NotiType { + + CONSUMPTION, // 소비경고알림 + INFORMATION, // 정보알림 + SYSTEM, // 시스템알림 + COMMUNITY // 커뮤니티 알림 + +} From 7d65d6cdc839d259c127608a5d60ce1ba413711f Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 18 Sep 2024 13:42:00 +0900 Subject: [PATCH 042/204] =?UTF-8?q?[feat]=20Role=20Enum=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20(=EC=82=AC=EC=9A=A9=EC=9E=90=20=EA=B6=8C=ED=95=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ADMIN: 관리자 - USER: 일반 사용자 --- src/main/java/com/bbteam/budgetbuddies/enums/Role.java | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/enums/Role.java diff --git a/src/main/java/com/bbteam/budgetbuddies/enums/Role.java b/src/main/java/com/bbteam/budgetbuddies/enums/Role.java new file mode 100644 index 00000000..e394418c --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/enums/Role.java @@ -0,0 +1,6 @@ +package com.bbteam.budgetbuddies.enums; + +public enum Role { + ADMIN, + USER +} From dc9f5086d55e13bdfd35fd5ec29210eb622d1280 Mon Sep 17 00:00:00 2001 From: MJJ Date: Thu, 19 Sep 2024 13:15:54 +0900 Subject: [PATCH 043/204] =?UTF-8?q?[feat]=20Gemini=20Api=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../openai/controller/GeminiController.java | 29 +++++++++ .../domain/openai/dto/ChatRequest.java | 57 +++++++++++++++++ .../domain/openai/dto/ChatResponse.java | 62 +++++++++++++++++++ .../domain/openai/service/GeminiService.java | 39 ++++++++++++ .../config/GeminiRestTemplateConfig.java | 23 +++++++ 5 files changed, 210 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/openai/controller/GeminiController.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatRequest.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatResponse.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/openai/service/GeminiService.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/config/GeminiRestTemplateConfig.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/openai/controller/GeminiController.java b/src/main/java/com/bbteam/budgetbuddies/domain/openai/controller/GeminiController.java new file mode 100644 index 00000000..25a5213b --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/openai/controller/GeminiController.java @@ -0,0 +1,29 @@ +package com.bbteam.budgetbuddies.domain.openai.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.HttpClientErrorException; + +import com.bbteam.budgetbuddies.domain.openai.service.GeminiService; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/gemini") +public class GeminiController { + + private final GeminiService geminiService; + + @GetMapping("/chat") + public ResponseEntity gemini(@RequestParam(name = "message") String message) { + try { + return ResponseEntity.ok().body(geminiService.getContents(message)); + } catch (HttpClientErrorException e) { + return ResponseEntity.badRequest().body(e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatRequest.java b/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatRequest.java new file mode 100644 index 00000000..e2c32d44 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatRequest.java @@ -0,0 +1,57 @@ +package com.bbteam.budgetbuddies.domain.openai.dto; + +import java.util.ArrayList; +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ChatRequest { + private List contents; + private GenerationConfig generationConfig; + + @Getter + @Setter + public static class Content { + private Parts parts; + } + + @Getter + @Setter + public static class Parts { + private String text; + + } + + @Getter + @Setter + public static class GenerationConfig { + private int candidate_count; + private int max_output_tokens; + private double temperature; + + } + + public ChatRequest(String prompt) { + this.contents = new ArrayList<>(); + Content content = new Content(); + Parts parts = new Parts(); + + parts.setText(prompt); + content.setParts(parts); + + this.contents.add(content); + this.generationConfig = new GenerationConfig(); + this.generationConfig.setCandidate_count(1); + this.generationConfig.setMax_output_tokens(1000); + this.generationConfig.setTemperature(0.7); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatResponse.java b/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatResponse.java new file mode 100644 index 00000000..415ba958 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatResponse.java @@ -0,0 +1,62 @@ +package com.bbteam.budgetbuddies.domain.openai.dto; + +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class ChatResponse { + + private List candidates; + private PromptFeedback promptFeedback; + + @Getter + @Setter + public static class Candidate { + private Content content; + private String finishReason; + private int index; + private List safetyRatings; + + } + + @Getter + @Setter + @ToString + public static class Content { + private List parts; + private String role; + + } + + @Getter + @Setter + @ToString + public static class Parts { + private String text; + + } + + @Getter + @Setter + public static class SafetyRating { + private String category; + private String probability; + } + + @Getter + @Setter + public static class PromptFeedback { + private List safetyRatings; + + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/openai/service/GeminiService.java b/src/main/java/com/bbteam/budgetbuddies/domain/openai/service/GeminiService.java new file mode 100644 index 00000000..4f72c50f --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/openai/service/GeminiService.java @@ -0,0 +1,39 @@ +package com.bbteam.budgetbuddies.domain.openai.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import com.bbteam.budgetbuddies.domain.openai.dto.ChatRequest; +import com.bbteam.budgetbuddies.domain.openai.dto.ChatResponse; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class GeminiService { + + @Qualifier("geminiRestTemplate") + @Autowired + private RestTemplate restTemplate; + + @Value("${spring.gemini.api.url}") + private String apiUrl; + + @Value("${spring.gemini.api.key}") + private String geminiApiKey; + + public String getContents(String prompt) { + + String requestUrl = apiUrl + "?key=" + geminiApiKey; + + ChatRequest request = new ChatRequest(prompt); + ChatResponse response = restTemplate.postForObject(requestUrl, request, ChatResponse.class); + + String message = response.getCandidates().get(0).getContent().getParts().get(0).getText().toString(); + + return message; + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/config/GeminiRestTemplateConfig.java b/src/main/java/com/bbteam/budgetbuddies/global/config/GeminiRestTemplateConfig.java new file mode 100644 index 00000000..294ba799 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/config/GeminiRestTemplateConfig.java @@ -0,0 +1,23 @@ +package com.bbteam.budgetbuddies.global.config; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +import lombok.RequiredArgsConstructor; + +@Configuration +@RequiredArgsConstructor +public class GeminiRestTemplateConfig { + + @Bean + @Qualifier("geminiRestTemplate") + public RestTemplate geminiRestTemplate() { + RestTemplate restTemplate = new RestTemplate(); + restTemplate.getInterceptors().add((request, body, execution) -> execution.execute(request, body)); + + return restTemplate; + } +} + From 96cbfd4a09b88b5321d8d1b732fcbd5743dd75b7 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 20 Sep 2024 10:49:53 +0900 Subject: [PATCH 044/204] =?UTF-8?q?[fix]=20ExistNotice=20=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/domain/notice/validation/ExistNotice.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/ExistNotice.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/ExistNotice.java index 9e97f397..2c95245f 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/ExistNotice.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/ExistNotice.java @@ -11,7 +11,7 @@ @Target( { ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) public @interface ExistNotice { - String message() default "해당 user가 존재하지 않습니다."; + String message() default "해당 Notice가 존재하지 않습니다."; Class[] groups() default {}; Class[] payload() default {}; } From 618111bf728e49e10761852a5db0916cdc5d9f42 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 20 Sep 2024 15:17:06 +0900 Subject: [PATCH 045/204] =?UTF-8?q?[feat]=20Faq=20DTO=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/faq/dto/FaqRequestDto.java | 29 ++++++++++++++ .../domain/faq/dto/FaqResponseDto.java | 38 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faq/dto/FaqRequestDto.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faq/dto/FaqResponseDto.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/dto/FaqRequestDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/dto/FaqRequestDto.java new file mode 100644 index 00000000..a485e46f --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/dto/FaqRequestDto.java @@ -0,0 +1,29 @@ +package com.bbteam.budgetbuddies.domain.faq.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +public class FaqRequestDto { + + @Getter + @Builder + @AllArgsConstructor + public static class FaqPostRequest { + @NotBlank + private String title; + @NotBlank + private String body; + } + + @Getter + @Builder + @AllArgsConstructor + public static class FaqModifyRequest { + @NotBlank + private String title; + @NotBlank + private String body; + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/dto/FaqResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/dto/FaqResponseDto.java new file mode 100644 index 00000000..3de30f64 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/dto/FaqResponseDto.java @@ -0,0 +1,38 @@ +package com.bbteam.budgetbuddies.domain.faq.dto; + +import lombok.Builder; +import lombok.Getter; + +public class FaqResponseDto { + + // User + @Getter + @Builder + public static class FaqPostResponse { + private Long faqId; + private String username; + private String title; + private String body; + } + + //User + @Getter + @Builder + public static class FaqModifyResponse { + private Long faqId; + private String username; + private String title; + private String body; + } + + //Admin + @Getter + @Builder + public static class FaqFindResponse { + private Long faqId; + private Long userId; + private String username; + private String title; + private String body; + } +} From 27cfb63109abdd1119af7678d5b80a1ee349574b Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 20 Sep 2024 15:17:20 +0900 Subject: [PATCH 046/204] =?UTF-8?q?[feat]=20Faq=20Converter=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/faq/converter/FaqConverter.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faq/converter/FaqConverter.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/converter/FaqConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/converter/FaqConverter.java new file mode 100644 index 00000000..d011fe60 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/converter/FaqConverter.java @@ -0,0 +1,46 @@ +package com.bbteam.budgetbuddies.domain.faq.converter; + +import com.bbteam.budgetbuddies.domain.faq.dto.FaqRequestDto; +import com.bbteam.budgetbuddies.domain.faq.dto.FaqResponseDto; +import com.bbteam.budgetbuddies.domain.faq.entity.Faq; +import com.bbteam.budgetbuddies.domain.user.entity.User; + +public class FaqConverter { + + public static Faq postToEntity(FaqRequestDto.FaqPostRequest dto, User user) { + return Faq.builder() + .title(dto.getTitle()) + .body(dto.getBody()) + .user(user) + .build(); + } + + public static FaqResponseDto.FaqPostResponse entityToPost(Faq faq) { + return FaqResponseDto.FaqPostResponse.builder() + .title(faq.getTitle()) + .body(faq.getBody()) + .faqId(faq.getId()) + .username(faq.getUser().getName()) + .build(); + } + + public static FaqResponseDto.FaqModifyResponse entityToModify(Faq faq) { + return FaqResponseDto.FaqModifyResponse.builder() + .title(faq.getTitle()) + .body(faq.getBody()) + .faqId(faq.getId()) + .username(faq.getUser().getName()) + .build(); + } + + public static FaqResponseDto.FaqFindResponse entityToFind(Faq faq) { + return FaqResponseDto.FaqFindResponse.builder() + .title(faq.getTitle()) + .body(faq.getBody()) + .username(faq.getUser().getName()) + .faqId(faq.getId()) + .userId(faq.getUser().getId()) + .build(); + } + +} From b0ff75056d1249e2fb6e7a7a10e0a4d78450afd3 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 20 Sep 2024 15:38:41 +0900 Subject: [PATCH 047/204] =?UTF-8?q?[feat]=20Faq=20Validator=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/faq/controller/FaqApi.java | 14 ++++++++++++++ .../domain/faq/validation/ExistFaq.java | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faq/validation/ExistFaq.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java new file mode 100644 index 00000000..91cda0d7 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java @@ -0,0 +1,14 @@ +package com.bbteam.budgetbuddies.domain.faq.controller; + + +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.faq.dto.FaqRequestDto; +import org.springframework.data.domain.Pageable; + +public interface FaqApi { + ApiResponse postFaq(Long userId, FaqRequestDto.FaqPostRequest dto); + ApiResponse findFaq(Long FaqId); + ApiResponse findByPaging(Pageable pageable); + ApiResponse modifyFaq(Long faqId, FaqRequestDto.FaqModifyRequest dto); + ApiResponse deleteFaq(Long faqId); +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/validation/ExistFaq.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/validation/ExistFaq.java new file mode 100644 index 00000000..feba4d2d --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/validation/ExistFaq.java @@ -0,0 +1,18 @@ +package com.bbteam.budgetbuddies.domain.faq.validation; + +import com.bbteam.budgetbuddies.domain.notice.validation.NoticeExistValidation; +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = FaqExistValidation.class) +@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExistFaq { + + String message() default "해당 FAQ가 존재하지 않습니다."; + Class[] groups() default {}; + Class[] payload() default {}; +} From 95c5cf452a10e676c54aa8d5c4ff2a492a00aa29 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 20 Sep 2024 15:41:57 +0900 Subject: [PATCH 048/204] =?UTF-8?q?[feat]=20FAQ=20Controller=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/faq/controller/FaqApi.java | 10 ++-- .../domain/faq/controller/FaqController.java | 57 +++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java index 91cda0d7..3fab0607 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java @@ -3,12 +3,14 @@ import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.faq.dto.FaqRequestDto; +import com.bbteam.budgetbuddies.domain.faq.validation.ExistFaq; +import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; import org.springframework.data.domain.Pageable; public interface FaqApi { - ApiResponse postFaq(Long userId, FaqRequestDto.FaqPostRequest dto); - ApiResponse findFaq(Long FaqId); + ApiResponse postFaq(@ExistUser Long userId, FaqRequestDto.FaqPostRequest dto); + ApiResponse findFaq(@ExistFaq Long FaqId); ApiResponse findByPaging(Pageable pageable); - ApiResponse modifyFaq(Long faqId, FaqRequestDto.FaqModifyRequest dto); - ApiResponse deleteFaq(Long faqId); + ApiResponse modifyFaq(@ExistFaq Long faqId, FaqRequestDto.FaqModifyRequest dto); + ApiResponse deleteFaq(@ExistFaq Long faqId); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java new file mode 100644 index 00000000..c2333f75 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java @@ -0,0 +1,57 @@ +package com.bbteam.budgetbuddies.domain.faq.controller; + +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.faq.dto.FaqRequestDto; +import com.bbteam.budgetbuddies.domain.faq.dto.FaqResponseDto; +import com.bbteam.budgetbuddies.domain.faq.service.FaqService; +import com.bbteam.budgetbuddies.domain.faq.validation.ExistFaq; +import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +@Validated +@RestController +@RequestMapping("/faqs") +@RequiredArgsConstructor +public class FaqController implements FaqApi{ + + private final FaqService faqService; + + @Override + @PostMapping("") + public ApiResponse postFaq(@ExistUser Long userId, + @Valid FaqRequestDto.FaqPostRequest dto) { + return ApiResponse.onSuccess(faqService.postFaq(dto, userId)); + } + + @Override + @GetMapping("/{faqId}") + public ApiResponse findFaq(@PathVariable @ExistFaq Long faqId) { + return ApiResponse.onSuccess(faqService.findOneFaq(faqId)); + } + + @Override + @GetMapping("/all") + public ApiResponse> findByPaging(@ParameterObject Pageable pageable) { + return ApiResponse.onSuccess(faqService.findAllWithPaging(pageable)); + } + + @Override + @PutMapping("/{faqId}") + public ApiResponse modifyFaq(@PathVariable @ExistFaq Long faqId, + @Valid FaqRequestDto.FaqModifyRequest dto) { + return ApiResponse.onSuccess(faqService.modifyFaq(dto, faqId)); + } + + @Override + @DeleteMapping("/{faqId}") + public ApiResponse deleteFaq(@PathVariable @ExistFaq Long faqId) { + faqService.deleteFaq(faqId); + return ApiResponse.onSuccess("Delete Success"); + } +} From 58d5f6f0543925b55a510c4c1c11ef785e567a2c Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 20 Sep 2024 15:44:05 +0900 Subject: [PATCH 049/204] =?UTF-8?q?[refactor]=20ErrorStatus=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apiPayload/code/status/ErrorStatus.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java index d5ccfffa..f2fc183f 100644 --- a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java @@ -4,6 +4,7 @@ import com.bbteam.budgetbuddies.apiPayload.code.ErrorReasonDto; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.ToString; import org.springframework.http.HttpStatus; @Getter @@ -13,10 +14,11 @@ public enum ErrorStatus implements BaseErrorCode { _INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500", "서버에러"), _BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON400", "잘못된 요청"), - USER_NOT_FOUND(HttpStatus.BAD_REQUEST, "USER4001", "사용자가 없습니다."), - COMMENT_NOT_FOUND(HttpStatus.BAD_REQUEST, "COMMENT4001", "해당 댓글이 없습니다.") , - NOTICE_NOT_FOUND(HttpStatus.BAD_REQUEST, "NOTICE4001", "해당 공지가 없습니다."), - PAGE_LOWER_ZERO(HttpStatus.BAD_REQUEST, "PAGE4001", "요청된 페이지가 0보다 작습니다."); + _USER_NOT_FOUND(HttpStatus.BAD_REQUEST, "USER4001", "사용자가 없습니다."), + _COMMENT_NOT_FOUND(HttpStatus.BAD_REQUEST, "COMMENT4001", "해당 댓글이 없습니다.") , + _NOTICE_NOT_FOUND(HttpStatus.BAD_REQUEST, "NOTICE4001", "해당 공지가 없습니다."), + _PAGE_LOWER_ZERO(HttpStatus.BAD_REQUEST, "PAGE4001", "요청된 페이지가 0보다 작습니다."), + _FAQ_NOT_FOUND(HttpStatus.NOT_FOUND, "FAQ4004", "해당 FAQ가 존재하지 않습니다."); private HttpStatus httpStatus; From cf97bd53d8f8b73b6e5cf0c18205936aaf7e439f Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 20 Sep 2024 15:44:56 +0900 Subject: [PATCH 050/204] =?UTF-8?q?[feat]=20FaqValidator=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../faq/validation/FaqExistValidation.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faq/validation/FaqExistValidation.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/validation/FaqExistValidation.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/validation/FaqExistValidation.java new file mode 100644 index 00000000..3b9490e9 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/validation/FaqExistValidation.java @@ -0,0 +1,35 @@ +package com.bbteam.budgetbuddies.domain.faq.validation; + +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.bbteam.budgetbuddies.domain.faq.entity.Faq; +import com.bbteam.budgetbuddies.domain.faq.repository.FaqRepository; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import lombok.RequiredArgsConstructor; + +import java.util.Optional; + +@RequiredArgsConstructor +public class FaqExistValidation implements ConstraintValidator { + + private final FaqRepository faqRepository; + @Override + public boolean isValid(Long faqId, ConstraintValidatorContext constraintValidatorContext) { + + Optional faq = faqRepository.findById(faqId); + + if(faq.isEmpty()) { + constraintValidatorContext.disableDefaultConstraintViolation(); + constraintValidatorContext.buildConstraintViolationWithTemplate(ErrorStatus._FAQ_NOT_FOUND.toString()).addConstraintViolation(); + return false; + } + + return true; + + } + + @Override + public void initialize(ExistFaq constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } +} From 74bd69bc7be23ab7c02c19c1abe67708fb1678e1 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 20 Sep 2024 15:45:41 +0900 Subject: [PATCH 051/204] =?UTF-8?q?[refactor]=20Enum=20=EC=9D=B4=EB=A6=84?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/comment/validation/CommentExistValidation.java | 3 +-- .../domain/notice/validation/NoticeExistValidation.java | 2 +- .../domain/user/validation/UserExistValidation.java | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/validation/CommentExistValidation.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/validation/CommentExistValidation.java index fb63bf58..e78c0c2a 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/comment/validation/CommentExistValidation.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/comment/validation/CommentExistValidation.java @@ -8,7 +8,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import java.lang.annotation.Annotation; import java.util.Optional; @RequiredArgsConstructor @@ -22,7 +21,7 @@ public boolean isValid(Long aLong, ConstraintValidatorContext constraintValidato if(comment.isEmpty()) { constraintValidatorContext.disableDefaultConstraintViolation(); - constraintValidatorContext.buildConstraintViolationWithTemplate(ErrorStatus.COMMENT_NOT_FOUND.toString()).addConstraintViolation(); + constraintValidatorContext.buildConstraintViolationWithTemplate(ErrorStatus._COMMENT_NOT_FOUND.toString()).addConstraintViolation(); return false; } return true; diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/NoticeExistValidation.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/NoticeExistValidation.java index 575db053..018807ad 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/NoticeExistValidation.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/validation/NoticeExistValidation.java @@ -19,7 +19,7 @@ public boolean isValid(Long aLong, ConstraintValidatorContext constraintValidato if(notice.isEmpty()) { constraintValidatorContext.disableDefaultConstraintViolation(); - constraintValidatorContext.buildConstraintViolationWithTemplate(ErrorStatus.NOTICE_NOT_FOUND.toString()).addConstraintViolation(); + constraintValidatorContext.buildConstraintViolationWithTemplate(ErrorStatus._NOTICE_NOT_FOUND.toString()).addConstraintViolation(); return false; } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/validation/UserExistValidation.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/validation/UserExistValidation.java index 3c636ba7..44c52940 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/validation/UserExistValidation.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/validation/UserExistValidation.java @@ -1,7 +1,6 @@ package com.bbteam.budgetbuddies.domain.user.validation; import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; -import com.bbteam.budgetbuddies.domain.comment.entity.Comment; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; import jakarta.validation.ConstraintValidator; @@ -21,7 +20,7 @@ public boolean isValid(Long aLong, ConstraintValidatorContext constraintValidato if(foundUser.isEmpty()) { constraintValidatorContext.disableDefaultConstraintViolation(); - constraintValidatorContext.buildConstraintViolationWithTemplate(ErrorStatus.USER_NOT_FOUND.toString()).addConstraintViolation(); + constraintValidatorContext.buildConstraintViolationWithTemplate(ErrorStatus._USER_NOT_FOUND.toString()).addConstraintViolation(); return false; } return true; From 814940849661c22d55f087001bf3d10c76183ec6 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 20 Sep 2024 16:01:06 +0900 Subject: [PATCH 052/204] =?UTF-8?q?[feat]=20FaqService=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/faq/service/FaqService.java | 19 ++++++ .../domain/faq/service/FaqServiceImpl.java | 65 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqService.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceImpl.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqService.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqService.java new file mode 100644 index 00000000..eb6a3718 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqService.java @@ -0,0 +1,19 @@ +package com.bbteam.budgetbuddies.domain.faq.service; + +import com.bbteam.budgetbuddies.domain.faq.dto.FaqRequestDto; +import com.bbteam.budgetbuddies.domain.faq.dto.FaqResponseDto; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface FaqService { + + FaqResponseDto.FaqFindResponse findOneFaq(Long faqId); + + Page findAllWithPaging(Pageable pageable); + + FaqResponseDto.FaqPostResponse postFaq(FaqRequestDto.FaqPostRequest dto, Long userId); + + FaqResponseDto.FaqModifyResponse modifyFaq(FaqRequestDto.FaqModifyRequest dto, Long faqId); + + String deleteFaq(Long faqId); +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceImpl.java new file mode 100644 index 00000000..a28ccba8 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceImpl.java @@ -0,0 +1,65 @@ +package com.bbteam.budgetbuddies.domain.faq.service; + +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; +import com.bbteam.budgetbuddies.domain.faq.converter.FaqConverter; +import com.bbteam.budgetbuddies.domain.faq.dto.FaqRequestDto; +import com.bbteam.budgetbuddies.domain.faq.dto.FaqResponseDto; +import com.bbteam.budgetbuddies.domain.faq.entity.Faq; +import com.bbteam.budgetbuddies.domain.faq.repository.FaqRepository; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class FaqServiceImpl implements FaqService{ + + private final FaqRepository faqRepository; + private final UserRepository userRepository; + + @Override + public FaqResponseDto.FaqFindResponse findOneFaq(Long faqId) { + Faq faq = findFaq(faqId); + return FaqConverter.entityToFind(faq); + } + + @Override + public Page findAllWithPaging(Pageable pageable) { + return faqRepository.findAll(pageable).map(FaqConverter::entityToFind); + } + + @Override + @Transactional + public FaqResponseDto.FaqPostResponse postFaq(FaqRequestDto.FaqPostRequest dto, Long userId) { + User user = userRepository.findById(userId).orElseThrow(() -> new GeneralException(ErrorStatus._USER_NOT_FOUND)); + Faq faq = FaqConverter.postToEntity(dto, user); + faqRepository.save(faq); + return FaqConverter.entityToPost(faq); + } + + @Override + @Transactional + public FaqResponseDto.FaqModifyResponse modifyFaq(FaqRequestDto.FaqModifyRequest dto, Long faqId) { + Faq faq = findFaq(faqId); + faq.update(dto); + return FaqConverter.entityToModify(faq); + } + + @Override + @Transactional + public String deleteFaq(Long faqId) { + Faq faq = findFaq(faqId); + faqRepository.delete(faq); + return "ok!"; + } + + private Faq findFaq(Long faqId) { + return faqRepository.findById(faqId).orElseThrow(() -> new GeneralException(ErrorStatus._FAQ_NOT_FOUND)); + } +} From 32cd05cde9152595688d59ad377a0f5d9b7869b4 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 20 Sep 2024 16:01:32 +0900 Subject: [PATCH 053/204] =?UTF-8?q?[feat]=20Faq=20update=20method=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/bbteam/budgetbuddies/domain/faq/entity/Faq.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/entity/Faq.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/entity/Faq.java index d1a0d000..604f0fb1 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/entity/Faq.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/entity/Faq.java @@ -1,6 +1,7 @@ package com.bbteam.budgetbuddies.domain.faq.entity; import com.bbteam.budgetbuddies.common.BaseEntity; +import com.bbteam.budgetbuddies.domain.faq.dto.FaqRequestDto; import com.bbteam.budgetbuddies.domain.user.entity.User; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -29,4 +30,8 @@ public class Faq extends BaseEntity { private String body; + public void update(FaqRequestDto.FaqModifyRequest dto) { + title = dto.getTitle(); + body = dto.getBody(); + } } From ac20be850fec12d7fbb19cb6529816192652ea7a Mon Sep 17 00:00:00 2001 From: MJJ Date: Fri, 20 Sep 2024 17:11:04 +0900 Subject: [PATCH 054/204] =?UTF-8?q?[feat]=20Gemini=20domain=EB=AA=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{openai => gemini}/controller/GeminiController.java | 6 +++--- .../domain/{openai => gemini}/dto/ChatRequest.java | 2 +- .../domain/{openai => gemini}/dto/ChatResponse.java | 2 +- .../domain/gemini/service/GeminiService.java | 7 +++++++ .../service/GeminiServiceImpl.java} | 9 +++++---- 5 files changed, 17 insertions(+), 9 deletions(-) rename src/main/java/com/bbteam/budgetbuddies/domain/{openai => gemini}/controller/GeminiController.java (81%) rename src/main/java/com/bbteam/budgetbuddies/domain/{openai => gemini}/dto/ChatRequest.java (95%) rename src/main/java/com/bbteam/budgetbuddies/domain/{openai => gemini}/dto/ChatResponse.java (94%) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiService.java rename src/main/java/com/bbteam/budgetbuddies/domain/{openai/service/GeminiService.java => gemini/service/GeminiServiceImpl.java} (79%) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/openai/controller/GeminiController.java b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/controller/GeminiController.java similarity index 81% rename from src/main/java/com/bbteam/budgetbuddies/domain/openai/controller/GeminiController.java rename to src/main/java/com/bbteam/budgetbuddies/domain/gemini/controller/GeminiController.java index 25a5213b..1222baa2 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/openai/controller/GeminiController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/controller/GeminiController.java @@ -1,4 +1,4 @@ -package com.bbteam.budgetbuddies.domain.openai.controller; +package com.bbteam.budgetbuddies.domain.gemini.controller; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -7,7 +7,7 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.HttpClientErrorException; -import com.bbteam.budgetbuddies.domain.openai.service.GeminiService; +import com.bbteam.budgetbuddies.domain.gemini.service.GeminiServiceImpl; import lombok.RequiredArgsConstructor; @@ -16,7 +16,7 @@ @RequestMapping("/gemini") public class GeminiController { - private final GeminiService geminiService; + private final GeminiServiceImpl geminiService; @GetMapping("/chat") public ResponseEntity gemini(@RequestParam(name = "message") String message) { diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatRequest.java b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/dto/ChatRequest.java similarity index 95% rename from src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatRequest.java rename to src/main/java/com/bbteam/budgetbuddies/domain/gemini/dto/ChatRequest.java index e2c32d44..31dbf2c1 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatRequest.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/dto/ChatRequest.java @@ -1,4 +1,4 @@ -package com.bbteam.budgetbuddies.domain.openai.dto; +package com.bbteam.budgetbuddies.domain.gemini.dto; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatResponse.java b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/dto/ChatResponse.java similarity index 94% rename from src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatResponse.java rename to src/main/java/com/bbteam/budgetbuddies/domain/gemini/dto/ChatResponse.java index 415ba958..3ff240cb 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatResponse.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/dto/ChatResponse.java @@ -1,4 +1,4 @@ -package com.bbteam.budgetbuddies.domain.openai.dto; +package com.bbteam.budgetbuddies.domain.gemini.dto; import java.util.List; diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiService.java b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiService.java new file mode 100644 index 00000000..eb1f80fb --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiService.java @@ -0,0 +1,7 @@ +package com.bbteam.budgetbuddies.domain.gemini.service; + +public interface GeminiService { + + String getContents(String prompt); + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/openai/service/GeminiService.java b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiServiceImpl.java similarity index 79% rename from src/main/java/com/bbteam/budgetbuddies/domain/openai/service/GeminiService.java rename to src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiServiceImpl.java index 4f72c50f..a1c727a5 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/openai/service/GeminiService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiServiceImpl.java @@ -1,4 +1,4 @@ -package com.bbteam.budgetbuddies.domain.openai.service; +package com.bbteam.budgetbuddies.domain.gemini.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -6,14 +6,14 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; -import com.bbteam.budgetbuddies.domain.openai.dto.ChatRequest; -import com.bbteam.budgetbuddies.domain.openai.dto.ChatResponse; +import com.bbteam.budgetbuddies.domain.gemini.dto.ChatRequest; +import com.bbteam.budgetbuddies.domain.gemini.dto.ChatResponse; import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor -public class GeminiService { +public class GeminiServiceImpl implements GeminiService { @Qualifier("geminiRestTemplate") @Autowired @@ -36,4 +36,5 @@ public String getContents(String prompt) { return message; } + } From 7da371137d80b12abf5afcb2c64fe0ba958b9d75 Mon Sep 17 00:00:00 2001 From: MJJ Date: Fri, 20 Sep 2024 17:11:54 +0900 Subject: [PATCH 055/204] =?UTF-8?q?[feat]=20=EC=9D=B4=EB=B2=88=20=EB=8B=AC?= =?UTF-8?q?=20=EB=B9=84=EA=B5=90=20=EB=B6=84=EC=84=9D=20=EB=A0=88=ED=8F=AC?= =?UTF-8?q?=ED=8A=B8=20=EC=A1=B0=ED=9A=8C=20dto=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/ConsumeAmountAndGoalAmountDto.java | 15 +++++++++++++++ .../dto/MonthReportResponseDto.java | 16 ++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumeAmountAndGoalAmountDto.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/MonthReportResponseDto.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumeAmountAndGoalAmountDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumeAmountAndGoalAmountDto.java new file mode 100644 index 00000000..bdc631d1 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumeAmountAndGoalAmountDto.java @@ -0,0 +1,15 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class ConsumeAmountAndGoalAmountDto { + + private Long categoryId; + private Long consumeAmount; + private Long goalAmount; +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/MonthReportResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/MonthReportResponseDto.java new file mode 100644 index 00000000..3a26ebf6 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/MonthReportResponseDto.java @@ -0,0 +1,16 @@ +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 MonthReportResponseDto { + + private String facialExpression; + private String comment; +} From 1fe2f3a86ebe6edb9012047b891430bc3307b156 Mon Sep 17 00:00:00 2001 From: MJJ Date: Fri, 20 Sep 2024 17:13:19 +0900 Subject: [PATCH 056/204] =?UTF-8?q?[feat]=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=EB=B3=84=20=EC=86=8C=EB=B9=84=EA=B8=88=EC=95=A1?= =?UTF-8?q?=EA=B3=BC=20=EB=AA=A9=ED=91=9C=EA=B8=88=EC=95=A1=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ConsumptionGoalRepository.java | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 b44dfffc..0a335341 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 @@ -13,6 +13,7 @@ import com.bbteam.budgetbuddies.domain.category.entity.Category; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AvgConsumptionGoalDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumeAmountAndGoalAmountDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.MyConsumptionGoalDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; import com.bbteam.budgetbuddies.domain.user.entity.User; @@ -145,4 +146,14 @@ List findConsumeAmountsByCategories( @Param("peerGender") Gender peerGender, @Param("currentMonth") LocalDate currentMonth, @Param("categoryId") Long categoryId); + + @Query("SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumeAmountAndGoalAmountDto(" + + "cg.category.id, cg.consumeAmount, cg.goalAmount) " + + "FROM ConsumptionGoal cg " + + "WHERE cg.category.isDefault = true " + + "AND cg.user = :user " + + "AND cg.goalMonth >= :currentMonth") + List findConsumeAmountAndGoalAmount( + @Param("user") User user, + @Param("currentMonth") LocalDate currentMonth); } From eff3e2f6f32e9363dffcf72c04b12a8127887a5c Mon Sep 17 00:00:00 2001 From: MJJ Date: Fri, 20 Sep 2024 17:14:09 +0900 Subject: [PATCH 057/204] =?UTF-8?q?[feat]=20=EB=B9=84=EA=B5=90=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EB=A9=94=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=ED=91=9C=EC=A0=95=EB=B3=80=ED=99=94=20/=20=EB=A9=98=ED=8A=B8?= =?UTF-8?q?=20service=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ConsumptionGoalService.java | 13 +- .../service/ConsumptionGoalServiceImpl.java | 306 +++++++++++++----- 2 files changed, 226 insertions(+), 93 deletions(-) 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 1c35f2ce..0a094145 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 @@ -9,6 +9,7 @@ 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.MonthReportResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopCategoryConsumptionDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopGoalCategoryResponseDto; @@ -20,17 +21,17 @@ public interface ConsumptionGoalService { List getTopConsumptionGoalCategories(Long userId, int peerAgeStart, int peerAgeEnd, - String peerGender); + String peerGender); List getAllConsumptionGoalCategories(Long userId, int peerAgeS, int peerAgeE, - String peerG); + String peerG); ConsumptionGoalResponseListDto findUserConsumptionGoalList(Long userId, LocalDate date); PeerInfoResponseDto getPeerInfo(Long userId, int peerAgeStart, int peerAgeEnd, String peerGender); ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId, - ConsumptionGoalListRequestDto consumptionGoalListRequestDto); + ConsumptionGoalListRequestDto consumptionGoalListRequestDto); ConsumptionAnalysisResponseDto getTopCategoryAndConsumptionAmount(Long userId); @@ -41,10 +42,12 @@ ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId, void decreaseConsumeAmount(Long userId, Long categoryId, Long amount, LocalDate expenseDate); List getTopConsumptionCategories(Long userId, int peerAgeStart, int peerAgeEnd, - String peerGender); + String peerGender); List getAllConsumptionCategories(Long userId, int peerAgeS, int peerAgeE, - String peerG); + String peerG); void updateOrCreateDeletedConsumptionGoal(Long userId, Long categoryId, LocalDate goalMonth, Long amount); + + MonthReportResponseDto getMonthReport(Long userId); } 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 549da334..31cea083 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 @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -26,11 +27,13 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AllConsumptionCategoryResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AvgConsumptionGoalDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.CategoryConsumptionCountDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumeAmountAndGoalAmountDto; 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; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.MonthReportResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.MyConsumptionGoalDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopCategoryConsumptionDto; @@ -96,37 +99,35 @@ public List getAllConsumptionGoalCategories(L List myConsumptionAmountList = getMyGoalAmount(userId); List defaultCategories = categoryRepository.findAllByIsDefaultTrue(); - return defaultCategories.stream() - .map(category -> { - MyConsumptionGoalDto myConsumptionAmountDto = myConsumptionAmountList.stream() - .filter(dto -> dto.getCategoryId().equals(category.getId())) - .findFirst() - .orElse(new MyConsumptionGoalDto(category.getId(), 0L)); - - AvgConsumptionGoalDto avgDto = categoryAvgList.stream() - .filter(dto -> dto.getCategoryId().equals(category.getId())) - .findFirst() - .orElse(new AvgConsumptionGoalDto(category.getId(), 0L)); - - Long avgConsumeAmount = avgDto.getAverageAmount(); - Long myConsumeAmount = myConsumptionAmountDto.getMyAmount(); - Long roundedAvgConsumeAmount = roundToNearest10(avgConsumeAmount); - - long consumeAmountDifference; - - if (roundedAvgConsumeAmount == 0L) { - consumeAmountDifference = -myConsumeAmount; - } else { - consumeAmountDifference = myConsumeAmount - roundedAvgConsumeAmount; - } - - return AllConsumptionCategoryResponseDto.builder() - .categoryName(category.getName()) - .avgAmount(roundedAvgConsumeAmount) - .amountDifference(consumeAmountDifference) - .build(); - }) - .toList(); + return defaultCategories.stream().map(category -> { + MyConsumptionGoalDto myConsumptionAmountDto = myConsumptionAmountList.stream() + .filter(dto -> dto.getCategoryId().equals(category.getId())) + .findFirst() + .orElse(new MyConsumptionGoalDto(category.getId(), 0L)); + + AvgConsumptionGoalDto avgDto = categoryAvgList.stream() + .filter(dto -> dto.getCategoryId().equals(category.getId())) + .findFirst() + .orElse(new AvgConsumptionGoalDto(category.getId(), 0L)); + + Long avgConsumeAmount = avgDto.getAverageAmount(); + Long myConsumeAmount = myConsumptionAmountDto.getMyAmount(); + Long roundedAvgConsumeAmount = roundToNearest10(avgConsumeAmount); + + long consumeAmountDifference; + + if (roundedAvgConsumeAmount == 0L) { + consumeAmountDifference = -myConsumeAmount; + } else { + consumeAmountDifference = myConsumeAmount - roundedAvgConsumeAmount; + } + + return AllConsumptionCategoryResponseDto.builder() + .categoryName(category.getName()) + .avgAmount(roundedAvgConsumeAmount) + .amountDifference(consumeAmountDifference) + .build(); + }).toList(); } @Override @@ -145,8 +146,7 @@ public ConsumptionAnalysisResponseDto getTopCategoryAndConsumptionAmount(Long us checkPeerInfo(userId, 0, 0, "none"); List avgConsumptionGoalList = consumptionGoalRepository.findAvgGoalAmountByCategory( - peerAgeStart, - peerAgeEnd, peerGender, currentMonth); + peerAgeStart, peerAgeEnd, peerGender, currentMonth); Long topConsumptionGoalCategoryId = avgConsumptionGoalList.get(0).getCategoryId(); @@ -154,8 +154,8 @@ public ConsumptionAnalysisResponseDto getTopCategoryAndConsumptionAmount(Long us LocalDateTime endOfWeekDateTime = endOfWeek.atTime(LocalTime.MAX); Long currentWeekConsumptionAmount = consumptionGoalRepository - .findAvgConsumptionByCategoryIdAndCurrentWeek(topConsumptionGoalCategoryId, startOfWeekDateTime, - endOfWeekDateTime, + .findAvgConsumptionByCategoryIdAndCurrentWeek( + topConsumptionGoalCategoryId, startOfWeekDateTime, endOfWeekDateTime, peerAgeStart, peerAgeEnd, peerGender) .orElse(0L); @@ -174,8 +174,9 @@ public List getTopConsumptionCategories(Long userId, checkPeerInfo(userId, peerAgeS, peerAgeE, peerG); - List categoryConsumptionCountDto = expenseRepository - .findTopCategoriesByConsumptionCount(peerAgeStart, peerAgeEnd, peerGender, currentMonth.atStartOfDay()); + List categoryConsumptionCountDto = + expenseRepository.findTopCategoriesByConsumptionCount( + peerAgeStart, peerAgeEnd, peerGender, currentMonth.atStartOfDay()); return categoryConsumptionCountDto.stream() .limit(3) @@ -199,36 +200,34 @@ public List getAllConsumptionCategories(Long List defaultCategories = categoryRepository.findAllByIsDefaultTrue(); - return defaultCategories.stream() - .map(category -> { - MyConsumptionGoalDto myConsumptionAmountDto = myConsumptionAmountList.stream() - .filter(dto -> dto.getCategoryId().equals(category.getId())) - .findFirst() - .orElse(new MyConsumptionGoalDto(category.getId(), 0L)); - - AvgConsumptionGoalDto avgDto = categoryAvgList.stream() - .filter(dto -> dto.getCategoryId().equals(category.getId())) - .findFirst() - .orElse(new AvgConsumptionGoalDto(category.getId(), 0L)); - - Long avgConsumeAmount = avgDto.getAverageAmount(); - Long myConsumeAmount = myConsumptionAmountDto.getMyAmount(); - Long roundedAvgConsumeAmount = roundToNearest10(avgConsumeAmount); - - long consumeAmountDifference; - if (roundedAvgConsumeAmount == 0L) { - consumeAmountDifference = -myConsumeAmount; - } else { - consumeAmountDifference = myConsumeAmount - roundedAvgConsumeAmount; - } - - return AllConsumptionCategoryResponseDto.builder() - .categoryName(category.getName()) - .avgAmount(roundedAvgConsumeAmount) - .amountDifference(consumeAmountDifference) - .build(); - }) - .toList(); + return defaultCategories.stream().map(category -> { + MyConsumptionGoalDto myConsumptionAmountDto = myConsumptionAmountList.stream() + .filter(dto -> dto.getCategoryId().equals(category.getId())) + .findFirst() + .orElse(new MyConsumptionGoalDto(category.getId(), 0L)); + + AvgConsumptionGoalDto avgDto = categoryAvgList.stream() + .filter(dto -> dto.getCategoryId().equals(category.getId())) + .findFirst() + .orElse(new AvgConsumptionGoalDto(category.getId(), 0L)); + + Long avgConsumeAmount = avgDto.getAverageAmount(); + Long myConsumeAmount = myConsumptionAmountDto.getMyAmount(); + Long roundedAvgConsumeAmount = roundToNearest10(avgConsumeAmount); + + long consumeAmountDifference; + if (roundedAvgConsumeAmount == 0L) { + consumeAmountDifference = -myConsumeAmount; + } else { + consumeAmountDifference = myConsumeAmount - roundedAvgConsumeAmount; + } + + return AllConsumptionCategoryResponseDto.builder() + .categoryName(category.getName()) + .avgAmount(roundedAvgConsumeAmount) + .amountDifference(consumeAmountDifference) + .build(); + }).toList(); } private User findUserById(Long userId) { @@ -341,8 +340,8 @@ private List getMyGoalAmount(Long userId) { List defaultCategories = categoryRepository.findAllByIsDefaultTrue(); List myConsumptionAmountList = new ArrayList<>(); - List myConsumptionGoalDto = consumptionGoalRepository.findAllGoalAmountByUserId( - userId, currentMonth); + List myConsumptionGoalDto = consumptionGoalRepository.findAllGoalAmountByUserId(userId, + currentMonth); Map myConsumptionMap = myConsumptionGoalDto.stream() .collect(Collectors.toMap(MyConsumptionGoalDto::getCategoryId, Function.identity())); @@ -388,8 +387,8 @@ private List getMedianGoalAmount() { for (Category category : defaultCategories) { - List goalAmounts = consumptionGoalRepository.findGoalAmountsByCategories( - peerAgeStart, peerAgeEnd, peerGender, currentMonth, category.getId()); + List goalAmounts = consumptionGoalRepository.findGoalAmountsByCategories(peerAgeStart, peerAgeEnd, + peerGender, currentMonth, category.getId()); if (goalAmounts != null && !goalAmounts.isEmpty()) { @@ -419,8 +418,8 @@ private List getMedianConsumeAmount() { for (Category category : defaultCategories) { - List goalAmounts = consumptionGoalRepository.findConsumeAmountsByCategories( - peerAgeStart, peerAgeEnd, peerGender, currentMonth, category.getId()); + List goalAmounts = consumptionGoalRepository.findConsumeAmountsByCategories(peerAgeStart, + peerAgeEnd, peerGender, currentMonth, category.getId()); if (goalAmounts != null && !goalAmounts.isEmpty()) { double median = calculateMedian(goalAmounts); @@ -437,14 +436,12 @@ private double calculateMedian(List values) { /** values 리스트에서 0 보다 큰(소비 금액이 존재하는) 값만 필터링 - size에 필터링한 값의 개수를 저장 + size 에 필터링한 값의 개수를 저장 홀수일 경우 size / 2 (가운데) 인덱스에 해당하는 값 반환 짝수일 경우 와 size/ 2 -1 인덱스 데이터와 size / 2의 인덱스 데이터의 평균을 처리 **/ - List filteredValues = values.stream() - .filter(value -> value > 0) - .collect(Collectors.toList()); + List filteredValues = values.stream().filter(value -> value > 0).collect(Collectors.toList()); int size = filteredValues.size(); @@ -530,8 +527,9 @@ private Map initializeGoalMap(Long userId) { private void updateGoalMapWithPrevious(Long userId, LocalDate goalMonth, Map goalMap) { - goalMap.keySet().stream().map(categoryId -> - consumptionGoalRepository.findLatelyGoal(userId, categoryId, goalMonth.minusMonths(1))) + goalMap.keySet() + .stream() + .map(categoryId -> consumptionGoalRepository.findLatelyGoal(userId, categoryId, goalMonth.minusMonths(1))) .filter(Optional::isPresent) .map(Optional::get) .map(consumptionGoalConverter::toConsumptionGoalResponseDtoFromPreviousGoal) @@ -561,8 +559,8 @@ public void recalculateConsumptionAmount(Expense expense, ExpenseUpdateRequestDt } private void restorePreviousGoalConsumptionAmount(Expense expense, User user) { - ConsumptionGoal previousConsumptionGoal = consumptionGoalRepository.findLatelyGoal( - user.getId(), expense.getCategory().getId(), expense.getExpenseDate().toLocalDate().withDayOfMonth(1)) + ConsumptionGoal previousConsumptionGoal = consumptionGoalRepository.findLatelyGoal(user.getId(), + expense.getCategory().getId(), expense.getExpenseDate().toLocalDate().withDayOfMonth(1)) .orElseThrow(() -> new IllegalArgumentException("Not found consumptionGoal")); previousConsumptionGoal.restoreConsumeAmount(expense.getAmount()); @@ -617,16 +615,14 @@ public void updateConsumeAmount(Long userId, Long categoryId, Long amount) { @Override public void decreaseConsumeAmount(Long userId, Long categoryId, Long amount, LocalDate expenseDate) { - User user = userRepository.findById(userId) - .orElseThrow(() -> new IllegalArgumentException("Not found user")); + User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("Not found user")); Category category = categoryRepository.findById(categoryId) .orElseThrow(() -> new IllegalArgumentException("Not found Category")); LocalDate goalMonth = expenseDate.withDayOfMonth(1); - ConsumptionGoal consumptionGoal = consumptionGoalRepository - .findConsumptionGoalByUserAndCategoryAndGoalMonth(user, category, goalMonth) - .orElseThrow(() -> new IllegalArgumentException("Not found ConsumptionGoal")); + ConsumptionGoal consumptionGoal = consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth( + user, category, goalMonth).orElseThrow(() -> new IllegalArgumentException("Not found ConsumptionGoal")); consumptionGoal.decreaseConsumeAmount(amount); consumptionGoalRepository.save(consumptionGoal); @@ -636,8 +632,7 @@ public void decreaseConsumeAmount(Long userId, Long categoryId, Long amount, Loc @Override @Transactional public void updateOrCreateDeletedConsumptionGoal(Long userId, Long categoryId, LocalDate goalMonth, Long amount) { - User user = userRepository.findById(userId) - .orElseThrow(() -> new IllegalArgumentException("Invalid user ID")); + User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("Invalid user ID")); Category category = categoryRepository.findById(categoryId) .orElseThrow(() -> new IllegalArgumentException("Invalid category ID")); @@ -662,4 +657,139 @@ public void updateOrCreateDeletedConsumptionGoal(Long userId, Long categoryId, L consumptionGoalRepository.save(newGoal); } } + + @Override + @Transactional(readOnly = true) + public MonthReportResponseDto getMonthReport(Long userId) { // 이번 달 비교 분석 레포트 (표정 변화, 멘트) + + User user = findUserById(userId); + + // 카테고리별 소비금액과 목표금액 리스트로 반환 + List dtoList = consumptionGoalRepository.findConsumeAmountAndGoalAmount(user, + currentMonth); + + Map consumeRatioByCategories = consumptionRate( + dtoList); // 카테고리별 소비 비율 리스트 (소비 금액 / 목표 금액 * 100) + + Long remainDaysRatio = dateRatio(); // 현재 날짜 기준 이번 달 날짜 비율 (예시 9월 20일 -> 20/30 * 100 = 33%) + + // 두 비율의 차를 통해 카테고리별 표정 변화 로직 + Map facialExpressionByCategoryId = updateFacialExpressionByCategoryId(consumeRatioByCategories, + remainDaysRatio); + + String facialExpression = getMainFacialExpression(facialExpressionByCategoryId); + + String mainComment = getMainComment(dtoList); + + return consumptionGoalConverter.toMonthReportResponseDto(facialExpression, mainComment); + } + + private Map consumptionRate(List list) { + + Map consumeRatio = new HashMap<>(); + for (ConsumeAmountAndGoalAmountDto dto : list) { + + Double cAmount = Double.valueOf(dto.getConsumeAmount()); + Double gAmount = Double.valueOf(dto.getGoalAmount()); + + Long ratio = (long)((cAmount / gAmount) * 100); // 소비 금액 / 목표 금액 * 100 + consumeRatio.put(dto.getCategoryId(), ratio); + } + return consumeRatio; + } + + private Long dateRatio() { + + Double lastDayOfMonth = (double)LocalDate.now().lengthOfMonth(); // 이번 달 마지막 일 + Double nowDayOfMonth = (double)LocalDate.now().getDayOfMonth(); // 현재 일 + + return (long)((nowDayOfMonth / lastDayOfMonth) * 100); // 현재 일 / 마지막 일 * 100 + } + + private Map updateFacialExpressionByCategoryId(Map consumeRatioByCategories, + Long remainDaysRatio) { + + Map facialList = new HashMap<>(); + + // 카테고리 별 소비 비율의 차로 표정 로직 업데이트 + /** + • 성공: 소비 비율이 남은 시간 비율보다 5% 이상 적음 (사용 속도가 매우 느림) + • 잘 지키고 있음: 소비 비율이 남은 시간 비율보다 0~5% 정도 차이 (적절한 소비) + • 기본: 소비 비율이 남은 시간 비율보다 0~10% 정도 높음 (소비가 조금 빠름) + • 아슬아슬함: 소비 비율이 남은 시간 비율보다 10~20% 높음 (조금 더 신경 써야 함) + • 위기: 소비 비율이 남은 시간 비율보다 20~30% 높음 (위험한 수준) + • 실패: 소비 비율이 남은 시간 비율보다 30% 이상 높음 (예산 초과 가능성 큼) + **/ + for (Long key : consumeRatioByCategories.keySet()) { + long ratioDifference = consumeRatioByCategories.get(key) - remainDaysRatio; + + if (ratioDifference <= -5) { + facialList.put(key, "성공"); + } else if (ratioDifference <= 0) { + facialList.put(key, "잘 지키고 있음"); + } else if (ratioDifference <= 10) { + facialList.put(key, "기본"); + } else if (ratioDifference <= 20) { + facialList.put(key, "아슬아슬함"); + } else if (ratioDifference <= 30) { + facialList.put(key, "위기"); + } else { + facialList.put(key, "실패"); + } + } + return facialList; + } + + private String getMainFacialExpression(Map facialList) { + + if (facialList.containsValue("실패")) { + return "실패"; + } else if (facialList.containsValue("위기")) { + return "위기"; + } else if (facialList.containsValue("아슬아슬함")) { + return "아슬아슬함"; + } else if (facialList.containsValue("기본")) { + return "기본"; + } else if (facialList.containsValue("잘 지키고 있음")) { + return "잘 지키고 있음"; + } else { + return "성공"; + } + } + + private String getMainComment(List list) { + + long minDifference = Long.MAX_VALUE; + long minCategoryId = -1L; + + int remainDays = LocalDate.now().lengthOfMonth() - LocalDate.now().getDayOfMonth(); + + for (ConsumeAmountAndGoalAmountDto dto : list) { + Long cAmount = dto.getConsumeAmount(); + Long gAmount = dto.getGoalAmount(); + + long differenceAmount = gAmount - cAmount; + + // 차이가 가장 적은 값을 찾기 + if (differenceAmount < minDifference) { + minDifference = differenceAmount; + minCategoryId = dto.getCategoryId(); + } + } + Optional minCategory = categoryRepository.findById(minCategoryId); + if (minCategory.isEmpty()) { + throw new IllegalArgumentException("해당 카테고리를 찾을 수 없습니다."); + } + + String minCategoryName = minCategory.get().getName(); + + long todayAvailableConsumptionAmount = minDifference / remainDays; + long weekAvailableConsumptionAmount = todayAvailableConsumptionAmount * 7; + + if (minDifference < 0) { + return "이번 달에는 " + minCategoryName + "에 " + Math.abs(minDifference) / 10000 + "만원 이상 초과했어요!"; + } else { + return "이번 주에는 " + minCategoryName + "에 " + (weekAvailableConsumptionAmount / 10000) + "만원 이상 쓰시면 안 돼요!"; + } + } } \ No newline at end of file From c70a3e565ff6b430c646ca0f6a9f104399ba3a9c Mon Sep 17 00:00:00 2001 From: MJJ Date: Fri, 20 Sep 2024 17:14:22 +0900 Subject: [PATCH 058/204] =?UTF-8?q?[feat]=20=EB=B9=84=EA=B5=90=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EB=A9=94=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=ED=91=9C=EC=A0=95=EB=B3=80=ED=99=94=20/=20=EB=A9=98=ED=8A=B8?= =?UTF-8?q?=20converter=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../converter/ConsumptionGoalConverter.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java index ba9cd970..5cd7a72a 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java @@ -9,6 +9,7 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionAnalysisResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.MonthReportResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.UserConsumptionGoalResponse; import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; @@ -92,4 +93,12 @@ public PeerInfoResponseDto toPeerInfo(int peerAgeStart, int peerAgeEnd, Gender p .peerGender(peerGender.name()) .build(); } + + public MonthReportResponseDto toMonthReportResponseDto(String facialExpression, String comment) { + + return MonthReportResponseDto.builder() + .facialExpression(facialExpression) + .comment(comment) + .build(); + } } From 8e74297ca54d16ebcea77ca449039f6d4e148fd6 Mon Sep 17 00:00:00 2001 From: MJJ Date: Fri, 20 Sep 2024 19:09:32 +0900 Subject: [PATCH 059/204] =?UTF-8?q?[feat]=20=EB=B9=84=EA=B5=90=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EB=A9=94=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=ED=91=9C=EC=A0=95=EB=B3=80=ED=99=94=20/=20=EB=A9=98=ED=8A=B8?= =?UTF-8?q?=20controller=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ConsumptionGoalController.java | 7 +++++++ 1 file changed, 7 insertions(+) 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 d14dce3e..678e4890 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 @@ -18,6 +18,7 @@ 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.MonthReportResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopCategoryConsumptionDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopGoalCategoryResponseDto; @@ -113,4 +114,10 @@ public ApiResponse getTopCategoryAndConsumptionA return ApiResponse.onSuccess(response); } + @GetMapping("/facialExpressions") + public ApiResponse getMonthReport(@RequestParam(name = "userId") Long userId) { + MonthReportResponseDto response = consumptionGoalService.getMonthReport(userId); + return ApiResponse.onSuccess(response); + } + } \ No newline at end of file From 053c4d7a2008a2eed10a2e7085708f85a24e408d Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:26:58 +0900 Subject: [PATCH 060/204] =?UTF-8?q?[feat]=20Hashtag=20update=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bbteam/budgetbuddies/domain/hashtag/entity/Hashtag.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/entity/Hashtag.java b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/entity/Hashtag.java index 83b32059..228acc68 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/entity/Hashtag.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/entity/Hashtag.java @@ -1,6 +1,7 @@ package com.bbteam.budgetbuddies.domain.hashtag.entity; import com.bbteam.budgetbuddies.common.BaseEntity; +import com.bbteam.budgetbuddies.domain.hashtag.dto.HashtagRequest; import jakarta.persistence.Entity; import lombok.*; import lombok.experimental.SuperBuilder; @@ -15,4 +16,8 @@ public class Hashtag extends BaseEntity { private String name; // 해시태그 이름 + public void update(HashtagRequest.UpdateHashtagDto updateHashtagDto) { + this.name = updateHashtagDto.getName(); + } + } From 4a8937562f67484c665f47e4797100488f433044 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:27:16 +0900 Subject: [PATCH 061/204] =?UTF-8?q?[feat]=20Hashtag=20repository=20?= =?UTF-8?q?=EB=A9=94=EC=86=8C=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 해시태그 아이디를 기반으로 해시태그 엔티티 가져오기 --- .../domain/hashtag/repository/HashtagRepository.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/repository/HashtagRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/repository/HashtagRepository.java index 488c4831..0faa0a22 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/repository/HashtagRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/repository/HashtagRepository.java @@ -3,7 +3,11 @@ import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface HashtagRepository extends JpaRepository { + // 해시태그 아이디를 기반으로 해시태그 엔티티 가져오기 + List findByIdIn(List hashtagIds); } From 7e2b9d1dcf1903f413ddfbb2ee43663180b35825 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:27:31 +0900 Subject: [PATCH 062/204] =?UTF-8?q?[feat]=20Hashtag=20Controller=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/hashtag/controller/HashtagApi.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/hashtag/controller/HashtagApi.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/controller/HashtagApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/controller/HashtagApi.java new file mode 100644 index 00000000..adcb2e92 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/controller/HashtagApi.java @@ -0,0 +1,75 @@ +package com.bbteam.budgetbuddies.domain.hashtag.controller; + +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.hashtag.dto.HashtagRequest; +import com.bbteam.budgetbuddies.domain.hashtag.dto.HashtagResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; + +public interface HashtagApi { + + @Operation(summary = "[User] 모든 해시태그 리스트 가져오기 API", description = "해시태그 목록을 조회하는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + ApiResponse> getAllHashtag(); + + @Operation(summary = "[ADMIN] 해시태그 등록하기 API", description = "해시태그를 등록하는 API이며, 관리자만 접근 가능합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + ApiResponse registerHashtag( + @RequestBody HashtagRequest.RegisterHashtagDto registerHashtagDto + ); + + @Operation(summary = "[ADMIN] 특정 해시태그 수정하기 API", description = "특정 해시태그를 수정하는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + ApiResponse updateHashtag( + @RequestBody HashtagRequest.UpdateHashtagDto updateHashtagDto + ); + + @Operation(summary = "[ADMIN] 특정 해시태그 삭제하기 API", description = "ID를 통해 특정 해시태그를 삭제하는 API입니다. 해시태그 삭제 시 연관된 지원정보 및 할인정보에 표시되지 않습니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + @Parameters({ + @Parameter(name = "hashtagId", description = "삭제할 해시태그의 id입니다."), + }) + ApiResponse deleteHashtag( + @PathVariable Long hashtagId + ); + + @Operation(summary = "[ADMIN] 특정 해시태그 가져오기 API", description = "ID를 통해 특정 해시태그를 가져오는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + @Parameters({ + @Parameter(name = "hashtagId", description = "조회할 해시태그의 id입니다."), + }) + ApiResponse getHashtag( + @PathVariable Long hashtagId + ); +} From c93e398aa8db2265f918bcc7bbd786c557aa1255 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:27:43 +0900 Subject: [PATCH 063/204] =?UTF-8?q?[feat]=20Hashtag=20Controller=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hashtag/controller/HashtagController.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/hashtag/controller/HashtagController.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/controller/HashtagController.java b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/controller/HashtagController.java new file mode 100644 index 00000000..48a3808f --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/controller/HashtagController.java @@ -0,0 +1,69 @@ +package com.bbteam.budgetbuddies.domain.hashtag.controller; + +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.hashtag.dto.HashtagRequest; +import com.bbteam.budgetbuddies.domain.hashtag.dto.HashtagResponse; +import com.bbteam.budgetbuddies.domain.hashtag.service.HashtagService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/hashtags") +public class HashtagController implements HashtagApi { + + private final HashtagService hashtagService; + + @Override + @GetMapping("/find-all") + public ApiResponse> getAllHashtag() { + + List hashtagResponses = hashtagService.getAllHashtag(); + + return ApiResponse.onSuccess(hashtagResponses); + } + + @Override + @PostMapping("") + public ApiResponse registerHashtag( + @RequestBody HashtagRequest.RegisterHashtagDto registerHashtagDto + ) { + + HashtagResponse hashtagResponse = hashtagService.registerHashtag(registerHashtagDto); + + return ApiResponse.onSuccess(hashtagResponse); + } + + @Override + @PutMapping("") + public ApiResponse updateHashtag( + @RequestBody HashtagRequest.UpdateHashtagDto updateHashtagDto + ) { + + HashtagResponse hashtagResponse = hashtagService.updateHashtag(updateHashtagDto); + + return ApiResponse.onSuccess(hashtagResponse); + } + + @Override + @DeleteMapping("/{hashtagId}") + public ApiResponse deleteHashtag( + @PathVariable Long hashtagId + ) { + String message = hashtagService.deleteHashtag(hashtagId); + + return ApiResponse.onSuccess(message); + } + + @Override + @GetMapping("/{hashtagId}") + public ApiResponse getHashtag( + @PathVariable Long hashtagId + ) { + HashtagResponse hashtagResponse = hashtagService.getHashtagById(hashtagId); + + return ApiResponse.onSuccess(hashtagResponse); + } +} From b0ac5dbbc3122e575c3930586665e9385ac4e212 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:27:57 +0900 Subject: [PATCH 064/204] =?UTF-8?q?[feat]=20Hashtag=20Converter=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hashtag/converter/HashtagConverter.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/hashtag/converter/HashtagConverter.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/converter/HashtagConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/converter/HashtagConverter.java new file mode 100644 index 00000000..881c9685 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/converter/HashtagConverter.java @@ -0,0 +1,37 @@ +package com.bbteam.budgetbuddies.domain.hashtag.converter; + +import com.bbteam.budgetbuddies.domain.hashtag.dto.HashtagRequest; +import com.bbteam.budgetbuddies.domain.hashtag.dto.HashtagResponse; +import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; +import org.springframework.stereotype.Service; + +@Service +public class HashtagConverter { + + /** + * @param entity + * @return responseDto + */ + + public HashtagResponse toDto(Hashtag hashtag) { + + return HashtagResponse.builder() + .id(hashtag.getId()) + .name(hashtag.getName()) + .build(); + + } + + /** + * @param requestDto + * @return entity + */ + public Hashtag toEntity(HashtagRequest.RegisterHashtagDto registerHashtagDto) { + + return Hashtag.builder() + .name(registerHashtagDto.getName()) + .build(); + + } + +} From f779fff022e2f9acd2c49dd8fc551d24233cb7d0 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:28:45 +0900 Subject: [PATCH 065/204] =?UTF-8?q?[feat]=20Hashtag=20Request,=20Response?= =?UTF-8?q?=20DTO=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/hashtag/dto/HashtagRequest.java | 34 +++++++++++++++++++ .../domain/hashtag/dto/HashtagResponse.java | 18 ++++++++++ 2 files changed, 52 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/hashtag/dto/HashtagRequest.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/hashtag/dto/HashtagResponse.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/dto/HashtagRequest.java b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/dto/HashtagRequest.java new file mode 100644 index 00000000..d76dfc51 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/dto/HashtagRequest.java @@ -0,0 +1,34 @@ +package com.bbteam.budgetbuddies.domain.hashtag.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + + +public class HashtagRequest { + + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class RegisterHashtagDto { + + private String name; + + } + + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class UpdateHashtagDto { + + private Long id; + + private String name; + + } + + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/dto/HashtagResponse.java b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/dto/HashtagResponse.java new file mode 100644 index 00000000..59ef52ac --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/dto/HashtagResponse.java @@ -0,0 +1,18 @@ +package com.bbteam.budgetbuddies.domain.hashtag.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class HashtagResponse { + + private Long id; + + private String name; + +} From 32e8a2b4c9f6c5846c3b7ae09471beb9a6b79963 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:29:38 +0900 Subject: [PATCH 066/204] =?UTF-8?q?[feat]=20Hashtag=20Service=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hashtag/service/HashtagService.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/hashtag/service/HashtagService.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/service/HashtagService.java b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/service/HashtagService.java new file mode 100644 index 00000000..17467698 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/service/HashtagService.java @@ -0,0 +1,21 @@ +package com.bbteam.budgetbuddies.domain.hashtag.service; + + +import com.bbteam.budgetbuddies.domain.hashtag.dto.HashtagRequest; +import com.bbteam.budgetbuddies.domain.hashtag.dto.HashtagResponse; + +import java.util.List; + +public interface HashtagService { + + List getAllHashtag(); + + HashtagResponse registerHashtag(HashtagRequest.RegisterHashtagDto registerHashtagDto); + + HashtagResponse updateHashtag(HashtagRequest.UpdateHashtagDto updateHashtagDto); + + String deleteHashtag(Long hashtagId); + + HashtagResponse getHashtagById(Long hashtagId); + +} From 074c7d3ea7976d53ad87710af61a3f21aa72b579 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:29:48 +0900 Subject: [PATCH 067/204] =?UTF-8?q?[feat]=20Hashtag=20Service=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hashtag/service/HashtagServiceImpl.java | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/hashtag/service/HashtagServiceImpl.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/service/HashtagServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/service/HashtagServiceImpl.java new file mode 100644 index 00000000..c7b0367e --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/hashtag/service/HashtagServiceImpl.java @@ -0,0 +1,94 @@ +package com.bbteam.budgetbuddies.domain.hashtag.service; + +import com.bbteam.budgetbuddies.domain.hashtag.converter.HashtagConverter; +import com.bbteam.budgetbuddies.domain.hashtag.dto.HashtagRequest; +import com.bbteam.budgetbuddies.domain.hashtag.dto.HashtagResponse; +import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; +import com.bbteam.budgetbuddies.domain.hashtag.repository.HashtagRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class HashtagServiceImpl implements HashtagService { + + private final HashtagRepository hashtagRepository; + + private final HashtagConverter hashtagConverter; + + @Transactional + @Override + public List getAllHashtag() { + /** + * 모든 해시태그를 찾아 리스트로 리턴합니다. + */ + + List hashtags = hashtagRepository.findAll(); + + return hashtags.stream() + .map(hashtagConverter::toDto) + .toList(); + } + + @Transactional + @Override + public HashtagResponse registerHashtag(HashtagRequest.RegisterHashtagDto registerHashtagDto) { + /** + * 해시태그를 등록합니다. + */ + + Hashtag hashtag = hashtagConverter.toEntity(registerHashtagDto); + + hashtagRepository.save(hashtag); + + return hashtagConverter.toDto(hashtag); + } + + @Transactional + @Override + public HashtagResponse updateHashtag(HashtagRequest.UpdateHashtagDto updateHashtagDto) { + /** + * 해시태그 이름을 수정합니다. + */ + + Hashtag hashtag = hashtagRepository.findById(updateHashtagDto.getId()) + .orElseThrow(() -> new IllegalArgumentException("Hashtag not found")); + + hashtag.update(updateHashtagDto); + + hashtagRepository.save(hashtag); + + return hashtagConverter.toDto(hashtag); + } + + @Transactional + @Override + public String deleteHashtag(Long hashtagId) { + /** + * 해시태그를 삭제합니다. + */ + + Hashtag hashtag = hashtagRepository.findById(hashtagId) + .orElseThrow(() -> new IllegalArgumentException("Hashtag not found")); + + hashtagRepository.deleteById(hashtagId); + + return "Success"; + } + + @Transactional + @Override + public HashtagResponse getHashtagById(Long hashtagId) { + /** + * 특정 해시태그를 하나 조회합니다. + */ + + Hashtag hashtag = hashtagRepository.findById(hashtagId) + .orElseThrow(() -> new IllegalArgumentException("Hashtag not found")); + + return hashtagConverter.toDto(hashtag); + } +} From 3352d031bf70bcb35edcc4195805d86ae9006e01 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:30:30 +0900 Subject: [PATCH 068/204] =?UTF-8?q?[feat]=20ConnectedInfoRepository=20?= =?UTF-8?q?=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ConnectedInfoRepository.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/connectedinfo/repository/ConnectedInfoRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/connectedinfo/repository/ConnectedInfoRepository.java index 368dce04..61e81d66 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/connectedinfo/repository/ConnectedInfoRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/connectedinfo/repository/ConnectedInfoRepository.java @@ -1,9 +1,19 @@ package com.bbteam.budgetbuddies.domain.connectedinfo.repository; import com.bbteam.budgetbuddies.domain.connectedinfo.entity.ConnectedInfo; +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface ConnectedInfoRepository extends JpaRepository { + List findAllByDiscountInfo(DiscountInfo discountInfo); + + void deleteAllByDiscountInfo(DiscountInfo discountInfo); + + List findAllBySupportInfo(SupportInfo supportInfo); + void deleteAllBySupportInfo(SupportInfo supportInfo); } From 3971e898ef8887dfe287945b095b40dd7ecf7cd5 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:32:07 +0900 Subject: [PATCH 069/204] =?UTF-8?q?[feat]=20DiscountInfo=20DTO=EC=97=90=20?= =?UTF-8?q?=ED=95=B4=EC=8B=9C=ED=83=9C=EA=B7=B8=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/discountinfo/dto/DiscountRequest.java | 6 ++++++ .../domain/discountinfo/dto/DiscountResponseDto.java | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/dto/DiscountRequest.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/dto/DiscountRequest.java index 9fc2e34b..89496ba7 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/dto/DiscountRequest.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/dto/DiscountRequest.java @@ -3,6 +3,7 @@ import lombok.*; import java.time.LocalDate; +import java.util.List; public class DiscountRequest { @@ -26,6 +27,8 @@ public static class RegisterDiscountDto { private Boolean isInCalendar; + private List hashtagIds; + } @Getter @@ -49,6 +52,9 @@ public static class UpdateDiscountDto { private String thumbnailUrl; private Boolean isInCalendar; + + private List hashtagIds; + } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/dto/DiscountResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/dto/DiscountResponseDto.java index 3948f419..57fa40d7 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/dto/DiscountResponseDto.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/dto/DiscountResponseDto.java @@ -1,8 +1,10 @@ package com.bbteam.budgetbuddies.domain.discountinfo.dto; +import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; import lombok.*; import java.time.LocalDate; +import java.util.List; @Getter @Builder @@ -28,4 +30,6 @@ public class DiscountResponseDto { private String thumbnailUrl; + private List hashtags; + } From f61ad4027c933d49ea4850ce5ef186ac593eb4e4 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:33:10 +0900 Subject: [PATCH 070/204] =?UTF-8?q?[feat]=20=ED=95=A0=EC=9D=B8=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EC=99=80=20=EC=97=B0=EA=B4=80=EB=90=9C=20=ED=95=B4?= =?UTF-8?q?=EC=8B=9C=ED=83=9C=EA=B7=B8=EB=8F=84=20=ED=95=A8=EA=BB=98=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../converter/DiscountInfoConverter.java | 35 ++++-- .../service/DiscountInfoServiceImpl.java | 107 ++++++++++++------ 2 files changed, 94 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/converter/DiscountInfoConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/converter/DiscountInfoConverter.java index c00cb6fa..9ffa5a13 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/converter/DiscountInfoConverter.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/converter/DiscountInfoConverter.java @@ -1,10 +1,15 @@ package com.bbteam.budgetbuddies.domain.discountinfo.converter; +import com.bbteam.budgetbuddies.domain.connectedinfo.entity.ConnectedInfo; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountRequest; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; import org.springframework.stereotype.Service; +import java.util.List; +import java.util.stream.Collectors; + @Service public class DiscountInfoConverter { @@ -12,19 +17,27 @@ public class DiscountInfoConverter { * @param entity * @return responseDto */ - public DiscountResponseDto toDto(DiscountInfo entity) { + public DiscountResponseDto toDto(DiscountInfo discountInfo, List connectedInfos) { + // 특정 할인정보의 해시태그 가져오기 + List hashtags = connectedInfos.stream() + .filter(connectedInfo -> connectedInfo.getDiscountInfo() != null && connectedInfo.getDiscountInfo().equals(discountInfo)) // 특정 DiscountInfo와 연결된 ConnectedInfo만 필터링 + .map(ConnectedInfo::getHashtag) + .map(Hashtag::getName) + .collect(Collectors.toList()); return DiscountResponseDto.builder() - .id(entity.getId()) - .title(entity.getTitle()) - .startDate(entity.getStartDate()) - .endDate(entity.getEndDate()) - .anonymousNumber(entity.getAnonymousNumber()) - .discountRate(entity.getDiscountRate()) - .likeCount(entity.getLikeCount()) - .siteUrl(entity.getSiteUrl()) - .thumbnailUrl(entity.getThumbnailUrl()) + .id(discountInfo.getId()) + .title(discountInfo.getTitle()) + .startDate(discountInfo.getStartDate()) + .endDate(discountInfo.getEndDate()) + .anonymousNumber(discountInfo.getAnonymousNumber()) + .discountRate(discountInfo.getDiscountRate()) + .likeCount(discountInfo.getLikeCount()) + .siteUrl(discountInfo.getSiteUrl()) + .thumbnailUrl(discountInfo.getThumbnailUrl()) + .hashtags(hashtags) .build(); + } /** @@ -43,7 +56,7 @@ public DiscountInfo toEntity(DiscountRequest.RegisterDiscountDto requestDto) { .likeCount(0) .siteUrl(requestDto.getSiteUrl()) .thumbnailUrl(requestDto.getThumbnailUrl()) - .isInCalendar(requestDto.getIsInCalendar()) + .isInCalendar(requestDto.getIsInCalendar()) .build(); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceImpl.java index 54051dca..22318ace 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceImpl.java @@ -1,5 +1,7 @@ package com.bbteam.budgetbuddies.domain.discountinfo.service; +import com.bbteam.budgetbuddies.domain.connectedinfo.entity.ConnectedInfo; +import com.bbteam.budgetbuddies.domain.connectedinfo.repository.ConnectedInfoRepository; import com.bbteam.budgetbuddies.domain.discountinfo.converter.DiscountInfoConverter; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountRequest; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; @@ -7,6 +9,8 @@ import com.bbteam.budgetbuddies.domain.discountinfo.repository.DiscountInfoRepository; import com.bbteam.budgetbuddies.domain.discountinfolike.entity.DiscountInfoLike; import com.bbteam.budgetbuddies.domain.discountinfolike.repository.DiscountInfoLikeRepository; +import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; +import com.bbteam.budgetbuddies.domain.hashtag.repository.HashtagRepository; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; import lombok.RequiredArgsConstructor; @@ -17,6 +21,7 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; +import java.util.List; import java.util.Optional; @Service @@ -31,15 +36,19 @@ public class DiscountInfoServiceImpl implements DiscountInfoService { private final UserRepository userRepository; + private final HashtagRepository hashtagRepository; + + private final ConnectedInfoRepository connectedInfoRepository; @Transactional(readOnly = true) @Override public Page getDiscountsByYearAndMonth(Integer year, Integer month, Integer page, Integer size) { /** - * 1. Pageable 객체 생성 (사용자로부터 입력받은 page 번호와 size) - * 2. 사용자가 요청한 년월을 기준으로 해당 년월의 1일로 LocalDate 객체 생성 - * 3. 해당 년월에 겹치는 할인정보 데이터 가져오기 - * 4. Entity 리스트를 -> Dto로 모두 변환하여 리턴 + * 1. Pageable 객체 생성: 사용자가 요청한 페이지 번호와 사이즈를 기반으로 Pageable 객체를 생성합니다. + * 2. 사용자가 요청한 년월에 해당하는 LocalDate 객체 생성: 해당 월의 첫날과 마지막 날을 기준으로 LocalDate 객체를 생성합니다. + * 3. 해당 년월에 겹치는 할인정보 데이터 가져오기: Repository를 사용하여 해당 월에 포함되는 할인정보를 페이지네이션하여 가져옵니다. + * 4. DiscountInfo 엔티티를 통해 ConnectedInfo 리스트 가져오기: 각 DiscountInfo에 대해 연결된 ConnectedInfo 리스트를 가져옵니다. + * 5. DiscountInfo와 ConnectedInfo 리스트를 Dto로 변환하여 리턴: 모든 데이터를 DTO로 변환하여 반환합니다. */ Pageable pageable = PageRequest.of(page, size); @@ -48,38 +57,55 @@ public Page getDiscountsByYearAndMonth(Integer year, Intege Page discountInfoPage = discountInfoRepository.findByDateRange(startDate, endDate, pageable); - return discountInfoPage.map(discountInfoConverter::toDto); + return discountInfoPage.map( + discountInfo -> { + List connectedInfos = connectedInfoRepository.findAllByDiscountInfo(discountInfo); + return discountInfoConverter.toDto(discountInfo, connectedInfos); + } + ); } @Transactional @Override public DiscountResponseDto registerDiscountInfo(DiscountRequest.RegisterDiscountDto discountRequestDto) { /** - * 1. RequestDto -> Entity로 변환 - * 2. Entity 저장 - * 3. Entity -> ResponseDto로 변환 후 리턴 + * 1. RequestDto를 Entity로 변환: 입력받은 DTO를 DiscountInfo 엔티티로 변환합니다. + * 2. Entity 저장: 변환된 엔티티를 Repository를 통해 데이터베이스에 저장합니다. + * 3. Hashtag 엔티티 조회 및 연결: 입력받은 해시태그 ID 리스트를 사용하여 Hashtag 엔티티를 조회하고, 각 해시태그와 DiscountInfo를 연결하는 ConnectedInfo 엔티티를 생성하여 저장합니다. + * 4. ConnectedInfo 리스트 조회: 저장된 DiscountInfo와 관련된 ConnectedInfo 리스트를 조회합니다. + * 5. Entity를 ResponseDto로 변환하여 반환: 저장된 DiscountInfo와 관련된 ConnectedInfo 리스트를 포함한 DTO를 반환합니다. */ DiscountInfo entity = discountInfoConverter.toEntity(discountRequestDto); discountInfoRepository.save(entity); - return discountInfoConverter.toDto(entity); + List hashtags = hashtagRepository.findByIdIn(discountRequestDto.getHashtagIds()); + hashtags.forEach(hashtag -> { + ConnectedInfo connectedInfo = ConnectedInfo.builder() + .discountInfo(entity) + .hashtag(hashtag) + .build(); + connectedInfoRepository.save(connectedInfo); + }); + + List connectedInfos = connectedInfoRepository.findAllByDiscountInfo(entity); + return discountInfoConverter.toDto(entity, connectedInfos); } @Transactional @Override public DiscountResponseDto toggleLike(Long userId, Long discountInfoId) { /** - * 1. 사용자 조회 -> 없으면 에러 - * 2. 할인정보 조회 -> 없으면 에러 - * 3. 사용자가 특정 할인정보에 좋아요를 눌렀는지 확인 (DiscountInfoLike 테이블에서 userId로부터 데이터 가져오기) - * 4. 누르지 않은 상태라면, - * 4-1. DiscountInfo의 likeCount 1 증가 - * 4-2. DiscountInfoLike의 - * 5. 이미 누른 상태라면, - * 5-1. DiscountInfo의 likeCount 1 감소 + * 1. 사용자 조회: 주어진 userId로 User 엔티티를 조회하며, 존재하지 않을 경우 에러를 발생시킵니다. + * 2. 할인정보 조회: 주어진 discountInfoId로 DiscountInfo 엔티티를 조회하며, 존재하지 않을 경우 에러를 발생시킵니다. + * 3. 사용자가 특정 할인정보에 좋아요를 눌렀는지 확인: DiscountInfoLike 테이블을 통해 사용자가 해당 할인정보에 좋아요를 눌렀는지 확인합니다. + * 4. 좋아요 상태에 따라 처리: + * - 이미 좋아요를 누른 경우: 좋아요 상태를 변경하고, DiscountInfo의 likeCount를 감소시킵니다. + * - 좋아요를 누르지 않은 경우: 좋아요 상태를 변경하고, DiscountInfo의 likeCount를 증가시킵니다. + * 5. 새 좋아요 객체 생성: 좋아요가 처음 생성된 경우 새로운 DiscountInfoLike 객체를 생성하고 저장합니다. + * 6. 저장된 DiscountInfo와 관련된 ConnectedInfo 리스트 조회: 업데이트된 DiscountInfo와 연결된 ConnectedInfo 리스트를 조회합니다. + * 7. Entity를 ResponseDto로 변환하여 반환: 최종적으로 업데이트된 DiscountInfo와 관련된 ConnectedInfo 리스트를 포함한 DTO를 반환합니다. */ - User user = userRepository.findById(userId) .orElseThrow(() -> new IllegalArgumentException("User not found")); @@ -100,7 +126,7 @@ public DiscountResponseDto toggleLike(Long userId, Long discountInfoId) { discountInfo.addLikeCount(); } } else { - // 처음 객체를 생성한다면 + // 아직 좋아요를 누르지 않은 상태라면 DiscountInfoLike newLike = DiscountInfoLike.builder() .user(user) .discountInfo(discountInfo) @@ -112,20 +138,21 @@ public DiscountResponseDto toggleLike(Long userId, Long discountInfoId) { DiscountInfo savedEntity = discountInfoRepository.save(discountInfo); - return discountInfoConverter.toDto(savedEntity); + List connectedInfos = connectedInfoRepository.findAllByDiscountInfo(discountInfo); + + return discountInfoConverter.toDto(savedEntity, connectedInfos); } @Transactional @Override public DiscountResponseDto updateDiscountInfo(DiscountRequest.UpdateDiscountDto discountRequestDto) { /** - * 1. 할인정보 조회 -> 없으면 에러 - * 2. 변경사항 업데이트 - * 3. 변경사항 저장 - * 4. Entity -> ResponseDto로 변환 후 리턴 + * 1. 할인정보 조회: 주어진 discountRequestDto의 ID로 DiscountInfo 엔티티를 조회하며, 존재하지 않을 경우 에러를 발생시킵니다. + * 2. 변경사항 업데이트: 조회된 DiscountInfo 엔티티에 대해 DTO에서 전달된 변경사항을 업데이트합니다. + * 3. 변경사항 저장: 업데이트된 DiscountInfo 엔티티를 데이터베이스에 저장합니다. + * 4. 저장된 DiscountInfo와 관련된 ConnectedInfo 리스트 조회: 업데이트된 DiscountInfo와 연결된 ConnectedInfo 리스트를 조회합니다. + * 5. Entity를 ResponseDto로 변환하여 반환: 업데이트된 DiscountInfo와 관련된 ConnectedInfo 리스트를 포함한 DTO를 반환합니다. */ - - DiscountInfo discountInfo = discountInfoRepository.findById(discountRequestDto.getId()) .orElseThrow(() -> new IllegalArgumentException("DiscountInfo not found")); @@ -133,39 +160,45 @@ public DiscountResponseDto updateDiscountInfo(DiscountRequest.UpdateDiscountDto discountInfoRepository.save(discountInfo); // 변경사항 저장 - return discountInfoConverter.toDto(discountInfo); + List connectedInfos = connectedInfoRepository.findAllByDiscountInfo(discountInfo); + + return discountInfoConverter.toDto(discountInfo, connectedInfos); } @Transactional @Override public String deleteDiscountInfo(Long discountInfoId) { /** - * 1. 할인정보 조회 -> 없으면 에러 - * 2. Entity 삭제 - * 3. 성공여부 반환 + * 1. 할인정보 조회: 주어진 discountInfoId로 DiscountInfo 엔티티를 조회하며, 존재하지 않을 경우 에러를 발생시킵니다. + * 2. 연관된 ConnectedInfo 삭제: 연결된 모든 ConnectedInfo를 삭제하여 연관된 데이터를 제거합니다. + * 3. DiscountInfo 삭제: DiscountInfo 엔티티를 데이터베이스에서 삭제합니다. + * 4. 성공 여부 반환: 삭제 작업이 완료된 후 "Success"를 반환합니다. */ - DiscountInfo discountInfo = discountInfoRepository.findById(discountInfoId) .orElseThrow(() -> new IllegalArgumentException("DiscountInfo not found")); + // 연결된 ConnectedInfo 삭제 (일단 삭제되지 않도록 주석 처리) +// connectedInfoRepository.deleteAllByDiscountInfo(discountInfo); + + // DiscountInfo 삭제 discountInfoRepository.deleteById(discountInfoId); return "Success"; - } @Transactional @Override public DiscountResponseDto getDiscountInfoById(Long discountInfoId) { /** - * 1. 할인정보 조회 -> 없으면 에러 - * 2. Entity 조회 - * 3. Entity -> ResponseDto로 변환 후 리턴 + * 1. 할인정보 조회: 주어진 discountInfoId로 DiscountInfo 엔티티를 조회하며, 존재하지 않을 경우 에러를 발생시킵니다. + * 2. Entity 조회: 조회된 DiscountInfo와 관련된 ConnectedInfo 리스트를 가져옵니다. + * 3. Entity를 ResponseDto로 변환하여 반환: DiscountInfo와 관련된 ConnectedInfo 리스트를 포함한 DTO를 반환합니다. */ - DiscountInfo discountInfo = discountInfoRepository.findById(discountInfoId) .orElseThrow(() -> new IllegalArgumentException("DiscountInfo not found")); - return discountInfoConverter.toDto(discountInfo); + List connectedInfos = connectedInfoRepository.findAllByDiscountInfo(discountInfo); + + return discountInfoConverter.toDto(discountInfo, connectedInfos); } } From c30fb258f5e4478907152660c08d0b209c75b4b6 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:33:48 +0900 Subject: [PATCH 071/204] =?UTF-8?q?[test]=20=ED=95=A0=EC=9D=B8=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/DiscountInfoServiceTest.java | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceTest.java index eea83d2b..c1514941 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceTest.java @@ -7,6 +7,9 @@ import com.bbteam.budgetbuddies.domain.discountinfo.repository.DiscountInfoRepository; import com.bbteam.budgetbuddies.domain.discountinfolike.entity.DiscountInfoLike; import com.bbteam.budgetbuddies.domain.discountinfolike.repository.DiscountInfoLikeRepository; +import com.bbteam.budgetbuddies.domain.connectedinfo.entity.ConnectedInfo; +import com.bbteam.budgetbuddies.domain.connectedinfo.repository.ConnectedInfoRepository; +import com.bbteam.budgetbuddies.domain.hashtag.repository.HashtagRepository; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; import com.bbteam.budgetbuddies.enums.Gender; @@ -46,6 +49,12 @@ class DiscountInfoServiceTest { @Mock private UserRepository userRepository; + @Mock + private ConnectedInfoRepository connectedInfoRepository; + + @Mock + private HashtagRepository hashtagRepository; + @InjectMocks private DiscountInfoServiceImpl discountInfoService; @@ -79,24 +88,26 @@ void getDiscountsByYearAndMonthTest() { DiscountResponseDto dto1 = new DiscountResponseDto(); DiscountResponseDto dto2 = new DiscountResponseDto(); + when(connectedInfoRepository.findAllByDiscountInfo(discount1)).thenReturn(List.of()); + when(connectedInfoRepository.findAllByDiscountInfo(discount2)).thenReturn(List.of()); + when(discountInfoRepository.findByDateRange(startDate, endDate, pageable)).thenReturn(discountPage); - when(discountInfoConverter.toDto(discount1)).thenReturn(dto1); - when(discountInfoConverter.toDto(discount2)).thenReturn(dto2); + when(discountInfoConverter.toDto(discount1, List.of())).thenReturn(dto1); + when(discountInfoConverter.toDto(discount2, List.of())).thenReturn(dto2); // when Page result = discountInfoService.getDiscountsByYearAndMonth(2024, 7, 0, 10); // then // 1. 데이터가 2개인지 검증 - // 2. 각 데이터 내용이 일치하는지 검증 assertThat(result.getContent()).hasSize(2); + // 2. 각 데이터 내용이 일치하는지 검증 assertThat(result.getContent().get(0)).isEqualTo(dto1); assertThat(result.getContent().get(1)).isEqualTo(dto2); - // 3. 각 메소드가 1번씩만 호출되었는지 검증 verify(discountInfoRepository, times(1)).findByDateRange(startDate, endDate, pageable); - verify(discountInfoConverter, times(1)).toDto(discount1); - verify(discountInfoConverter, times(1)).toDto(discount2); + verify(discountInfoConverter, times(1)).toDto(discount1, List.of()); + verify(discountInfoConverter, times(1)).toDto(discount2, List.of()); } @Test @@ -110,6 +121,7 @@ void registerDiscountInfoTest() { .discountRate(30) .siteUrl("http://example.com") .thumbnailUrl("http://example.com2") + .hashtagIds(List.of()) .build(); DiscountInfo entity = DiscountInfo.builder() @@ -131,11 +143,12 @@ void registerDiscountInfoTest() { .thumbnailUrl("http://example.com2") .likeCount(0) .anonymousNumber(0) + .hashtags(List.of()) .build(); when(discountInfoConverter.toEntity(requestDto)).thenReturn(entity); when(discountInfoRepository.save(entity)).thenReturn(entity); - when(discountInfoConverter.toDto(entity)).thenReturn(responseDto); + when(discountInfoConverter.toDto(entity, List.of())).thenReturn(responseDto); // when DiscountResponseDto result = discountInfoService.registerDiscountInfo(requestDto); @@ -143,11 +156,10 @@ void registerDiscountInfoTest() { // then // 1. 데이터 내용이 일치하는지 검증 assertThat(result).isEqualTo(responseDto); - // 2. 각 메소드가 1번씩만 호출되었는지 검증 verify(discountInfoConverter, times(1)).toEntity(requestDto); verify(discountInfoRepository, times(1)).save(entity); - verify(discountInfoConverter, times(1)).toDto(entity); + verify(discountInfoConverter, times(1)).toDto(entity, List.of()); } @Test @@ -195,11 +207,12 @@ void toggleLikeTest() { .siteUrl("http://example.com") .build(); + when(connectedInfoRepository.findAllByDiscountInfo(discountInfo)).thenReturn(List.of()); when(discountInfoRepository.save(any(DiscountInfo.class))).thenReturn(discountInfo); when(userRepository.findById(anyLong())).thenReturn(Optional.of(user)); when(discountInfoRepository.findById(anyLong())).thenReturn(Optional.of(discountInfo)); when(discountInfoLikeRepository.findByUserAndDiscountInfo(user, discountInfo)).thenReturn(Optional.of(discountInfoLike)); - when(discountInfoConverter.toDto(any(DiscountInfo.class))).thenReturn(responseDto); + when(discountInfoConverter.toDto(any(DiscountInfo.class), anyList())).thenReturn(responseDto); // when DiscountResponseDto result = discountInfoService.toggleLike(1L, 1L); @@ -207,16 +220,11 @@ void toggleLikeTest() { // then // 1. 결과 객체 비교 검증 assertThat(result).isEqualTo(responseDto); - // 2. 좋아요 개수 0 -> 1로 증가했는지 검증 assertThat(discountInfo.getLikeCount()).isEqualTo(1); - // 3. 각 메소드가 1번씩만 호출되었는지 검증 verify(discountInfoRepository, times(1)).findById(1L); verify(discountInfoRepository, times(1)).save(discountInfo); - verify(discountInfoConverter, times(1)).toDto(discountInfo); + verify(discountInfoConverter, times(1)).toDto(discountInfo, List.of()); } - - - -} \ No newline at end of file +} From 68f949c4d4db43985370ced05a1d99dcbc39834d Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:34:36 +0900 Subject: [PATCH 072/204] =?UTF-8?q?[feat]=20SupportInfo=20DTO=EC=97=90=20?= =?UTF-8?q?=ED=95=B4=EC=8B=9C=ED=83=9C=EA=B7=B8=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/domain/supportinfo/dto/SupportRequest.java | 3 +++ .../domain/supportinfo/dto/SupportResponseDto.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/SupportRequest.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/SupportRequest.java index 48cca65f..c40fdf18 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/SupportRequest.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/SupportRequest.java @@ -6,6 +6,7 @@ import lombok.NoArgsConstructor; import java.time.LocalDate; +import java.util.List; public class SupportRequest { @@ -28,6 +29,7 @@ public static class RegisterSupportDto { private Boolean isInCalendar; + private List hashtagIds; } @@ -51,6 +53,7 @@ public static class UpdateSupportDto { private Boolean isInCalendar; + private List hashtagIds; } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/SupportResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/SupportResponseDto.java index 5392e54c..327773a1 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/SupportResponseDto.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/SupportResponseDto.java @@ -6,6 +6,7 @@ import lombok.NoArgsConstructor; import java.time.LocalDate; +import java.util.List; @Getter @Builder @@ -29,4 +30,6 @@ public class SupportResponseDto { private String thumbnailUrl; + private List hashtags; + } From 7b29a6a5eb43dc09fe8761444cdc7c517bbf0319 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:35:52 +0900 Subject: [PATCH 073/204] =?UTF-8?q?[refactor]=20=EC=A7=80=EC=9B=90?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=EC=99=80=20=EC=97=B0=EA=B4=80=EB=90=9C=20?= =?UTF-8?q?=ED=95=B4=EC=8B=9C=ED=83=9C=EA=B7=B8=EB=8F=84=20=ED=95=A8?= =?UTF-8?q?=EA=BB=98=20=EC=B2=98=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../converter/SupportInfoConverter.java | 32 +++++++++---- .../service/SupportInfoServiceImpl.java | 46 ++++++++++++++++--- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/converter/SupportInfoConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/converter/SupportInfoConverter.java index e064492d..c0cb66ca 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/converter/SupportInfoConverter.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/converter/SupportInfoConverter.java @@ -1,27 +1,39 @@ package com.bbteam.budgetbuddies.domain.supportinfo.converter; +import com.bbteam.budgetbuddies.domain.connectedinfo.entity.ConnectedInfo; +import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequest; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; import org.springframework.stereotype.Service; +import java.util.List; +import java.util.stream.Collectors; + @Service public class SupportInfoConverter { /** * @param entity * @return responseDto */ - public SupportResponseDto toDto(SupportInfo entity) { + public SupportResponseDto toDto(SupportInfo supportInfo, List connectedInfos) { + // 특정 지원정보의 해시태그 가져오기 + List hashtags = connectedInfos.stream() + .filter(connectedInfo -> connectedInfo.getSupportInfo() != null && connectedInfo.getSupportInfo().equals(supportInfo)) // 특정 SupportInfo와 연결된 ConnectedInfo만 필터링 + .map(ConnectedInfo::getHashtag) + .map(Hashtag::getName) + .collect(Collectors.toList()); return SupportResponseDto.builder() - .id(entity.getId()) - .title(entity.getTitle()) - .startDate(entity.getStartDate()) - .endDate(entity.getEndDate()) - .anonymousNumber(entity.getAnonymousNumber()) - .likeCount(entity.getLikeCount()) - .siteUrl(entity.getSiteUrl()) - .thumbnailUrl(entity.getThumbnailUrl()) + .id(supportInfo.getId()) + .title(supportInfo.getTitle()) + .startDate(supportInfo.getStartDate()) + .endDate(supportInfo.getEndDate()) + .anonymousNumber(supportInfo.getAnonymousNumber()) + .likeCount(supportInfo.getLikeCount()) + .siteUrl(supportInfo.getSiteUrl()) + .thumbnailUrl(supportInfo.getThumbnailUrl()) + .hashtags(hashtags) .build(); } @@ -40,7 +52,7 @@ public SupportInfo toEntity(SupportRequest.RegisterSupportDto requestDto) { .likeCount(0) .siteUrl(requestDto.getSiteUrl()) .thumbnailUrl(requestDto.getThumbnailUrl()) - .isInCalendar(requestDto.getIsInCalendar()) + .isInCalendar(requestDto.getIsInCalendar()) .build(); } } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceImpl.java index f5ca3f7e..de290465 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceImpl.java @@ -1,5 +1,9 @@ package com.bbteam.budgetbuddies.domain.supportinfo.service; +import com.bbteam.budgetbuddies.domain.connectedinfo.entity.ConnectedInfo; +import com.bbteam.budgetbuddies.domain.connectedinfo.repository.ConnectedInfoRepository; +import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; +import com.bbteam.budgetbuddies.domain.hashtag.repository.HashtagRepository; import com.bbteam.budgetbuddies.domain.supportinfo.converter.SupportInfoConverter; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequest; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; @@ -17,6 +21,7 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; +import java.util.List; import java.util.Optional; @Service @@ -31,6 +36,9 @@ public class SupportInfoServiceImpl implements SupportInfoService { private final UserRepository userRepository; + private final HashtagRepository hashtagRepository; + + private final ConnectedInfoRepository connectedInfoRepository; @Transactional(readOnly = true) @Override @@ -48,22 +56,37 @@ public Page getSupportsByYearAndMonth(Integer year, Integer Page supportInfoPage = supportInfoRepository.findByDateRange(startDate, endDate, pageable); - return supportInfoPage.map(supportInfoConverter::toDto); + return supportInfoPage.map( + supportInfo -> { + List connectedInfos = connectedInfoRepository.findAllBySupportInfo(supportInfo); + return supportInfoConverter.toDto(supportInfo, connectedInfos); + } + ); } @Transactional @Override - public SupportResponseDto registerSupportInfo(SupportRequest.RegisterSupportDto supportRequest) { + public SupportResponseDto registerSupportInfo(SupportRequest.RegisterSupportDto supportRequestDto) { /** * 1. RequestDto -> Entity로 변환 * 2. Entity 저장 * 3. Entity -> ResponseDto로 변환 후 리턴 */ - SupportInfo entity = supportInfoConverter.toEntity(supportRequest); + SupportInfo entity = supportInfoConverter.toEntity(supportRequestDto); supportInfoRepository.save(entity); - return supportInfoConverter.toDto(entity); + List hashtags = hashtagRepository.findByIdIn(supportRequestDto.getHashtagIds()); + hashtags.forEach(hashtag -> { + ConnectedInfo connectedInfo = ConnectedInfo.builder() + .supportInfo(entity) + .hashtag(hashtag) + .build(); + connectedInfoRepository.save(connectedInfo); + }); + + List connectedInfos = connectedInfoRepository.findAllBySupportInfo(entity); + return supportInfoConverter.toDto(entity, connectedInfos); } @Transactional @@ -112,7 +135,9 @@ public SupportResponseDto toggleLike(Long userId, Long supportInfoId) { SupportInfo savedEntity = supportInfoRepository.save(supportInfo); - return supportInfoConverter.toDto(savedEntity); + List connectedInfos = connectedInfoRepository.findAllBySupportInfo(supportInfo); + + return supportInfoConverter.toDto(savedEntity, connectedInfos); } @Transactional @@ -132,7 +157,9 @@ public SupportResponseDto updateSupportInfo(SupportRequest.UpdateSupportDto supp supportInfoRepository.save(supportInfo); // 변경사항 저장 - return supportInfoConverter.toDto(supportInfo); + List connectedInfos = connectedInfoRepository.findAllBySupportInfo(supportInfo); + + return supportInfoConverter.toDto(supportInfo, connectedInfos); } @Transactional @@ -147,6 +174,9 @@ public String deleteSupportInfo(Long supportInfoId) { SupportInfo supportInfo = supportInfoRepository.findById(supportInfoId) .orElseThrow(() -> new IllegalArgumentException("SupportInfo not found")); + // 연결된 ConnectedInfo 삭제 (일단 삭제되지 않도록 주석 처리) +// connectedInfoRepository.deleteAllBySupportInfo(supportInfo); + supportInfoRepository.deleteById(supportInfoId); return "Success"; @@ -165,6 +195,8 @@ public SupportResponseDto getSupportInfoById(Long supportInfoId) { SupportInfo supportInfo = supportInfoRepository.findById(supportInfoId) .orElseThrow(() -> new IllegalArgumentException("SupportInfo not found")); - return supportInfoConverter.toDto(supportInfo); + List connectedInfos = connectedInfoRepository.findAllBySupportInfo(supportInfo); + + return supportInfoConverter.toDto(supportInfo, connectedInfos); } } From 5c5b124617f76d1a293cda3b45851ee00f6fba91 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Fri, 20 Sep 2024 22:36:19 +0900 Subject: [PATCH 074/204] =?UTF-8?q?[test]=20=EC=A7=80=EC=9B=90=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/SupportInfoServiceTest.java | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceTest.java index 258c674b..fe2ff8a0 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceTest.java @@ -1,5 +1,7 @@ package com.bbteam.budgetbuddies.domain.supportinfo.service; +import com.bbteam.budgetbuddies.domain.connectedinfo.repository.ConnectedInfoRepository; +import com.bbteam.budgetbuddies.domain.hashtag.repository.HashtagRepository; import com.bbteam.budgetbuddies.domain.supportinfo.converter.SupportInfoConverter; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequest; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; @@ -48,6 +50,12 @@ class SupportInfoServiceTest { @Mock private UserRepository userRepository; + @Mock + private ConnectedInfoRepository connectedInfoRepository; + + @Mock + private HashtagRepository hashtagRepository; + @InjectMocks private SupportInfoServiceImpl supportInfoService; @@ -79,9 +87,12 @@ void getSupportsByYearAndMonthTest() { SupportResponseDto dto1 = new SupportResponseDto(); SupportResponseDto dto2 = new SupportResponseDto(); + when(connectedInfoRepository.findAllBySupportInfo(support1)).thenReturn(List.of()); + when(connectedInfoRepository.findAllBySupportInfo(support2)).thenReturn(List.of()); + when(supportInfoRepository.findByDateRange(startDate, endDate, pageable)).thenReturn(supportPage); - when(supportInfoConverter.toDto(support1)).thenReturn(dto1); - when(supportInfoConverter.toDto(support2)).thenReturn(dto2); + when(supportInfoConverter.toDto(support1, List.of())).thenReturn(dto1); + when(supportInfoConverter.toDto(support2, List.of())).thenReturn(dto2); // when Page result = supportInfoService.getSupportsByYearAndMonth(2024, 7, 0, 10); @@ -95,8 +106,8 @@ void getSupportsByYearAndMonthTest() { // 3. 각 메소드가 1번씩만 호출되었는지 검증 verify(supportInfoRepository, times(1)).findByDateRange(startDate, endDate, pageable); - verify(supportInfoConverter, times(1)).toDto(support1); - verify(supportInfoConverter, times(1)).toDto(support2); + verify(supportInfoConverter, times(1)).toDto(support1, List.of()); + verify(supportInfoConverter, times(1)).toDto(support2, List.of()); } @Test @@ -129,7 +140,7 @@ void registerSupportInfoTest() { when(supportInfoConverter.toEntity(requestDto)).thenReturn(entity); when(supportInfoRepository.save(entity)).thenReturn(entity); - when(supportInfoConverter.toDto(entity)).thenReturn(responseDto); + when(supportInfoConverter.toDto(entity, List.of())).thenReturn(responseDto); // when SupportResponseDto result = supportInfoService.registerSupportInfo(requestDto); @@ -141,7 +152,7 @@ void registerSupportInfoTest() { // 2. 각 메소드가 1번씩만 호출되었는지 검증 verify(supportInfoConverter, times(1)).toEntity(requestDto); verify(supportInfoRepository, times(1)).save(entity); - verify(supportInfoConverter, times(1)).toDto(entity); + verify(supportInfoConverter, times(1)).toDto(entity, List.of()); } @Test @@ -187,11 +198,12 @@ void toggleLikeTest() { .siteUrl("http://example.com") .build(); + when(connectedInfoRepository.findAllBySupportInfo(supportInfo)).thenReturn(List.of()); when(supportInfoRepository.save(any(SupportInfo.class))).thenReturn(supportInfo); when(userRepository.findById(anyLong())).thenReturn(Optional.of(user)); when(supportInfoRepository.findById(anyLong())).thenReturn(Optional.of(supportInfo)); when(supportInfoLikeRepository.findByUserAndSupportInfo(user, supportInfo)).thenReturn(Optional.of(supportInfoLike)); - when(supportInfoConverter.toDto(any(SupportInfo.class))).thenReturn(responseDto); + when(supportInfoConverter.toDto(any(SupportInfo.class), anyList())).thenReturn(responseDto); // when SupportResponseDto result = supportInfoService.toggleLike(1L, 1L); @@ -206,7 +218,7 @@ void toggleLikeTest() { // 3. 각 메소드가 1번씩만 호출되었는지 검증 verify(supportInfoRepository, times(1)).findById(1L); verify(supportInfoRepository, times(1)).save(supportInfo); - verify(supportInfoConverter, times(1)).toDto(supportInfo); + verify(supportInfoConverter, times(1)).toDto(supportInfo, List.of()); } } \ No newline at end of file From e64470c7350ab67c65e94070826c8d3c60a6160f Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Sat, 21 Sep 2024 23:56:11 +0900 Subject: [PATCH 075/204] =?UTF-8?q?[fix]=20Notice.user=EC=97=90=20@Notfoun?= =?UTF-8?q?d=20=EB=B6=99=EC=9E=84=EC=9C=BC=EB=A1=9C=EC=8D=A8=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/bbteam/budgetbuddies/domain/notice/entity/Notice.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/notice/entity/Notice.java b/src/main/java/com/bbteam/budgetbuddies/domain/notice/entity/Notice.java index 83a9cea0..cced216c 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/notice/entity/Notice.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/notice/entity/Notice.java @@ -6,6 +6,8 @@ import jakarta.persistence.*; import lombok.*; import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; @Entity @Getter @@ -15,6 +17,7 @@ public class Notice extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) @JoinColumn(name = "user_id") private User user; From 09c2ffa5ef944b152fed243e974105fc767bc212 Mon Sep 17 00:00:00 2001 From: MJJ Date: Mon, 23 Sep 2024 17:27:00 +0900 Subject: [PATCH 076/204] =?UTF-8?q?[feat]=20=EB=B9=84=EA=B5=90=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EB=A9=94=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EB=A9=98=ED=8A=B8=201=EB=A7=8C=EC=9B=90=20=EC=9D=B4=ED=95=98?= =?UTF-8?q?=20Locale=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ConsumptionGoalServiceImpl.java | 65 ++++++++++--------- 1 file changed, 36 insertions(+), 29 deletions(-) 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 31cea083..99aed3d3 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 @@ -2,6 +2,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; +import java.text.NumberFormat; import java.time.DayOfWeek; import java.time.LocalDate; import java.time.LocalDateTime; @@ -12,6 +13,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; @@ -153,10 +155,8 @@ public ConsumptionAnalysisResponseDto getTopCategoryAndConsumptionAmount(Long us LocalDateTime startOfWeekDateTime = startOfWeek.atStartOfDay(); LocalDateTime endOfWeekDateTime = endOfWeek.atTime(LocalTime.MAX); - Long currentWeekConsumptionAmount = consumptionGoalRepository - .findAvgConsumptionByCategoryIdAndCurrentWeek( - topConsumptionGoalCategoryId, startOfWeekDateTime, endOfWeekDateTime, - peerAgeStart, peerAgeEnd, peerGender) + Long currentWeekConsumptionAmount = consumptionGoalRepository.findAvgConsumptionByCategoryIdAndCurrentWeek( + topConsumptionGoalCategoryId, startOfWeekDateTime, endOfWeekDateTime, peerAgeStart, peerAgeEnd, peerGender) .orElse(0L); currentWeekConsumptionAmount = roundToNearest10(currentWeekConsumptionAmount); @@ -174,9 +174,8 @@ public List getTopConsumptionCategories(Long userId, checkPeerInfo(userId, peerAgeS, peerAgeE, peerG); - List categoryConsumptionCountDto = - expenseRepository.findTopCategoriesByConsumptionCount( - peerAgeStart, peerAgeEnd, peerGender, currentMonth.atStartOfDay()); + List categoryConsumptionCountDto = expenseRepository.findTopCategoriesByConsumptionCount( + peerAgeStart, peerAgeEnd, peerGender, currentMonth.atStartOfDay()); return categoryConsumptionCountDto.stream() .limit(3) @@ -374,12 +373,12 @@ private Long roundToNearest10(Long amount) { private List getMedianGoalAmount() { /** - 기본 카테고리만 가져와서 리스트에 저장 - 기본 카테고리 id 별로 소비 목표 데이터를 가져와 리스트로 저장 - 데이터가 존재하는 경우 리스트의 중앙값 계산 - 리스트가 비어 있으면 기본 값 0으로 설정 - 카테고리 별 중앙값 리스트 반환 - **/ + * 기본 카테고리만 가져와서 리스트에 저장 + * 기본 카테고리 id 별로 소비 목표 데이터를 가져와 리스트로 저장 + * 데이터가 존재하는 경우 리스트의 중앙값 계산 + * 리스트가 비어 있으면 기본 값 0으로 설정 + * 카테고리 별 중앙값 리스트 반환 + */ List defaultCategories = categoryRepository.findAllByIsDefaultTrue(); @@ -404,13 +403,13 @@ private List getMedianGoalAmount() { private List getMedianConsumeAmount() { - /** - 기본 카테고리만 가져와서 리스트에 저장 - 기본 카테고리 id 별로 소비 금액 데이터를 가져와 리스트로 저장 - 데이터가 존재하는 경우 리스트의 중앙값 계산 - 리스트가 비어 있으면 기본 값 0으로 설정 - 카테고리 별 중앙값 리스트 반환 - **/ + /* + * 기본 카테고리만 가져와서 리스트에 저장 + * 기본 카테고리 id 별로 소비 금액 데이터를 가져와 리스트로 저장 + * 데이터가 존재하는 경우 리스트의 중앙값 계산 + * 리스트가 비어 있으면 기본 값 0으로 설정 + * 카테고리 별 중앙값 리스트 반환 + */ List defaultCategories = categoryRepository.findAllByIsDefaultTrue(); @@ -434,12 +433,12 @@ private List getMedianConsumeAmount() { private double calculateMedian(List values) { - /** - values 리스트에서 0 보다 큰(소비 금액이 존재하는) 값만 필터링 - size 에 필터링한 값의 개수를 저장 - 홀수일 경우 size / 2 (가운데) 인덱스에 해당하는 값 반환 - 짝수일 경우 와 size/ 2 -1 인덱스 데이터와 size / 2의 인덱스 데이터의 평균을 처리 - **/ + /* + * values 리스트에서 0 보다 큰(소비 금액이 존재하는) 값만 필터링 + * size 에 필터링한 값의 개수를 저장 + * 홀수일 경우 size / 2 (가운데) 인덱스에 해당하는 값 반환 + * 짝수일 경우 와 size/ 2 -1 인덱스 데이터와 size / 2의 인덱스 데이터의 평균을 처리 + */ List filteredValues = values.stream().filter(value -> value > 0).collect(Collectors.toList()); @@ -719,7 +718,7 @@ private Map updateFacialExpressionByCategoryId(Map con • 아슬아슬함: 소비 비율이 남은 시간 비율보다 10~20% 높음 (조금 더 신경 써야 함) • 위기: 소비 비율이 남은 시간 비율보다 20~30% 높음 (위험한 수준) • 실패: 소비 비율이 남은 시간 비율보다 30% 이상 높음 (예산 초과 가능성 큼) - **/ + */ for (Long key : consumeRatioByCategories.keySet()) { long ratioDifference = consumeRatioByCategories.get(key) - remainDaysRatio; @@ -786,10 +785,18 @@ private String getMainComment(List list) { long todayAvailableConsumptionAmount = minDifference / remainDays; long weekAvailableConsumptionAmount = todayAvailableConsumptionAmount * 7; - if (minDifference < 0) { + log.info(String.valueOf(weekAvailableConsumptionAmount)); + + NumberFormat nf = NumberFormat.getInstance(Locale.KOREA); // 한국 단위로 locale + + if (weekAvailableConsumptionAmount < 0) { return "이번 달에는 " + minCategoryName + "에 " + Math.abs(minDifference) / 10000 + "만원 이상 초과했어요!"; + } else if (weekAvailableConsumptionAmount <= 10000) { + return "이번 달에는 " + minCategoryName + "에 " + nf.format(weekAvailableConsumptionAmount / 1000 * 1000) + + "원 이상 쓰시면 안 돼요!"; } else { - return "이번 주에는 " + minCategoryName + "에 " + (weekAvailableConsumptionAmount / 10000) + "만원 이상 쓰시면 안 돼요!"; + return "이번 주에는 " + minCategoryName + "에 " + Math.abs(Math.abs(weekAvailableConsumptionAmount) / 10000) + + "만원 이상 쓰시면 안 돼요!"; } } } \ No newline at end of file From 40350c481e43cf50d15ef123a4601269ce809a80 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 26 Sep 2024 14:21:54 +0900 Subject: [PATCH 077/204] =?UTF-8?q?[test]=20Faq=20Test=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/faq/service/FaqServiceTest.java | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 src/test/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceTest.java diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceTest.java new file mode 100644 index 00000000..4e895c18 --- /dev/null +++ b/src/test/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceTest.java @@ -0,0 +1,152 @@ +package com.bbteam.budgetbuddies.domain.faq.service; + +import com.bbteam.budgetbuddies.domain.faq.dto.FaqRequestDto; +import com.bbteam.budgetbuddies.domain.faq.dto.FaqResponseDto; +import com.bbteam.budgetbuddies.domain.faq.entity.Faq; +import com.bbteam.budgetbuddies.domain.faq.repository.FaqRepository; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@Transactional +class FaqServiceTest { + + @Autowired + FaqService faqService; + @Autowired + FaqRepository faqRepository; + @Autowired + UserRepository userRepository; + + static Long userId; + static Long faqId; + + @BeforeEach + void init() { + User user = User.builder() + .email("changjun157@naver.com") + .phoneNumber("010-1234-1234") + .name("tester1") + .mobileCarrier("kt") + .build(); + + userRepository.save(user); + userId = user.getId(); + + Faq faq = Faq.builder() + .title("testtitle1") + .body("testbody") + .user(user) + .build(); + faqRepository.save(faq); + faqId = faq.getId(); + } + + @Test + void findOneFaq() { + FaqResponseDto.FaqFindResponse response = faqService.findOneFaq(faqId); + + assertThat(response.getBody()).isEqualTo("testbody"); + assertThat(response.getTitle()).isEqualTo("testtitle1"); + } + + @Test + void findAllWithPaging() { + User user = userRepository.findById(userId).get(); + + Faq faq1 = Faq.builder() + .title("test1") + .body("test1") + .user(user) + .build(); + faqRepository.save(faq1); + + Faq faq2 = Faq.builder() + .title("test2") + .body("test2") + .user(user) + .build(); + faqRepository.save(faq2); + + Faq faq3 = Faq.builder() + .title("test3") + .body("test3") + .user(user) + .build(); + faqRepository.save(faq3); + + Faq faq4 = Faq.builder() + .title("test4") + .body("test4") + .user(user) + .build(); + faqRepository.save(faq4); + + Faq faq5 = Faq.builder() + .title("test5") + .body("test5") + .user(user) + .build(); + faqRepository.save(faq5); + + PageRequest pageRequest = PageRequest.of(0, 2); + Page page1 = faqService.findAllWithPaging(pageRequest); + assertThat(page1.getNumberOfElements()).isEqualTo(2); + assertThat(page1.getTotalPages()).isEqualTo(3); + } + + @Test + void postFaq() { + FaqRequestDto.FaqPostRequest dto = FaqRequestDto.FaqPostRequest.builder() + .body("안녕하세요") + .title("테스트입니다.") + .build(); + + FaqResponseDto.FaqPostResponse response = faqService.postFaq(dto, userId); + + Faq faq = faqRepository.findById(response.getFaqId()).get(); + + assertThat(faq.getBody()).isEqualTo("안녕하세요"); + assertThat(faq.getTitle()).isEqualTo("테스트입니다."); + } + @Test + void modifyFaq() { + User user = userRepository.findById(userId).get(); + Faq faq = faqRepository.findById(faqId).get(); + + FaqRequestDto.FaqModifyRequest dto = FaqRequestDto.FaqModifyRequest.builder() + .title("modititle") + .body("modibody") + .build(); + + faqService.modifyFaq(dto, faqId); + + assertThat(faq.getTitle()).isEqualTo("modititle"); + assertThat(faq.getBody()).isEqualTo("modibody"); + + + } + + + @Test + void deleteFaq() { + faqService.deleteFaq(faqId); + Optional faq = faqRepository.findById(faqId); + + assertThat(faq.isEmpty()).isTrue(); + } +} \ No newline at end of file From 533f28f58d1104e3119a8b45950a386526b300ba Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 26 Sep 2024 14:22:24 +0900 Subject: [PATCH 078/204] =?UTF-8?q?[refactor]=20FaqController=20annotation?= =?UTF-8?q?=20=EC=9D=BC=EB=B6=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/domain/faq/controller/FaqController.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java index c2333f75..c8da6681 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java @@ -24,8 +24,8 @@ public class FaqController implements FaqApi{ @Override @PostMapping("") - public ApiResponse postFaq(@ExistUser Long userId, - @Valid FaqRequestDto.FaqPostRequest dto) { + public ApiResponse postFaq(@ExistUser @RequestParam Long userId, + @Valid @RequestBody FaqRequestDto.FaqPostRequest dto) { return ApiResponse.onSuccess(faqService.postFaq(dto, userId)); } @@ -44,7 +44,7 @@ public ApiResponse> findByPaging(@Parameter @Override @PutMapping("/{faqId}") public ApiResponse modifyFaq(@PathVariable @ExistFaq Long faqId, - @Valid FaqRequestDto.FaqModifyRequest dto) { + @Valid @RequestBody FaqRequestDto.FaqModifyRequest dto) { return ApiResponse.onSuccess(faqService.modifyFaq(dto, faqId)); } From ea68879469345101c220837dae709fc71210c661 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 26 Sep 2024 14:22:37 +0900 Subject: [PATCH 079/204] =?UTF-8?q?[feat]=20FaqApi=20Swagger=20=EC=84=A4?= =?UTF-8?q?=EB=AA=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/faq/controller/FaqApi.java | 103 +++++++++++++++++- 1 file changed, 101 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java index 3fab0607..f3e0b134 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java @@ -5,12 +5,111 @@ import com.bbteam.budgetbuddies.domain.faq.dto.FaqRequestDto; import com.bbteam.budgetbuddies.domain.faq.validation.ExistFaq; import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.validation.Valid; import org.springframework.data.domain.Pageable; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; public interface FaqApi { - ApiResponse postFaq(@ExistUser Long userId, FaqRequestDto.FaqPostRequest dto); + @Operation(summary = "[User] FAQ 게시 API", description = "FAQ를 게시하는 API입니다.", + requestBody = @RequestBody( + content = @Content( + schema = @Schema( + allOf = { FaqRequestDto.FaqPostRequest.class}, + requiredProperties = {"title", "body"} + ), + mediaType = MediaType.APPLICATION_JSON_VALUE, + examples = { + @ExampleObject(name = "someExample1", value = """ + { + "title" : "FAQ 제목 써주시면 됩니다", + "body" : "FAQ 내용 써주시면 됩니다." + } + """) + } + ) + ) + ) + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "userId", description = "FAQ를 사용하는 userId입니다.. pathVariable", in = ParameterIn.QUERY), + } + ) + ApiResponse postFaq(@ExistUser @RequestParam Long userId, + @Valid @org.springframework.web.bind.annotation.RequestBody FaqRequestDto.FaqPostRequest dto); + @Operation(summary = "[Admin] FAQ 조회 API", description = "FAQ를 조회하는 API입니다." + ) + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "faqId", description = "조회할 공지 id입니다. pathVariable", in = ParameterIn.PATH), + } + ) ApiResponse findFaq(@ExistFaq Long FaqId); + @Operation(summary = "[Admin] FAQ 다건 조회 API", description = "FAQ를 페이징으로 조회하는 API입니다." + ) + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "page", description = "조회할 page입니다. 0부터 시작합니다. pathVariable", in = ParameterIn.QUERY), + @Parameter(name = "size", description = "조회할 페이지의 크기입니다. pathVariable", in = ParameterIn.QUERY) + + } + ) ApiResponse findByPaging(Pageable pageable); - ApiResponse modifyFaq(@ExistFaq Long faqId, FaqRequestDto.FaqModifyRequest dto); + + @Operation(summary = "[User] FAQ 수정 API", description = "FAQ를 수정하는 API입니다.", + requestBody = @RequestBody( + content = @Content( + schema = @Schema( + allOf = { FaqRequestDto.FaqPostRequest.class}, + requiredProperties = {"title", "body"} + ), + mediaType = MediaType.APPLICATION_JSON_VALUE, + examples = { + @ExampleObject(name = "someExample1", value = """ + { + "title" : "FAQ 제목 써주시면 됩니다", + "body" : "FAQ 내용 써주시면 됩니다." + } + """) + } + ) + ) + ) + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "faqId", description = "수정할 FAQ id입니다. pathVariable", in = ParameterIn.PATH), + } + ) + ApiResponse modifyFaq(@PathVariable @ExistFaq Long faqId, + @Valid @org.springframework.web.bind.annotation.RequestBody FaqRequestDto.FaqModifyRequest dto); + + @Operation(summary = "[Admin] FAQ 삭제 API", description = "FAQ를 삭제하는 API입니다." + ) + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "faqId", description = "삭제할 faqId입니다.. pathVariable", in = ParameterIn.PATH), + + } + ) ApiResponse deleteFaq(@ExistFaq Long faqId); } From df94734aa7d201321e46ed20692e3340e25abf20 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Sat, 28 Sep 2024 10:56:31 +0900 Subject: [PATCH 080/204] =?UTF-8?q?[feat]=20=EC=A2=8B=EC=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=95=A0=EC=9D=B8=EC=A0=95=EB=B3=B4=20=EA=B0=80?= =?UTF-8?q?=EC=A0=B8=EC=98=A4=EA=B8=B0=20API=20Controller=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/DiscountInfoApi.java | 18 ++++++++++++++++++ .../controller/DiscountInfoController.java | 12 ++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoApi.java index bf3445b6..a3c880a2 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoApi.java @@ -106,4 +106,22 @@ ApiResponse getDiscountInfo( @PathVariable Long discountInfoId ); + @Operation(summary = "[ADMIN] 특정 사용자가 좋아요를 누른 할인정보 가져오기 API", description = "특정 사용자가 좋아요를 누른 할인정보들을 가져오는 API입니다. 페이징을 포함합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + @Parameters({ + @Parameter(name = "userId", description = "특정 사용자의 id입니다."), + @Parameter(name = "page", description = "페이지 번호, 0번이 1 페이지 입니다. (기본값은 0입니다.)"), + @Parameter(name = "size", description = "한 페이지에 불러올 데이터 개수입니다. (기본값은 10개입니다.)") + }) + ApiResponse> getLikedDiscountInfo( + @PathVariable @ExistUser Long userId, + @RequestParam(defaultValue = "0") Integer page, + @RequestParam(defaultValue = "10") Integer size + ); + } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java index a70a3bf4..2adb897c 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java @@ -81,4 +81,16 @@ public ApiResponse getDiscountInfo( return ApiResponse.onSuccess(discountResponseDto); } + @Override + @GetMapping("/liked-all") + public ApiResponse> getLikedDiscountInfo( + @RequestParam @ExistUser Long userId, + @RequestParam(defaultValue = "0") Integer page, + @RequestParam(defaultValue = "10") Integer size + ) { + Page likedDiscountInfoPage = discountInfoService.getLikedDiscountInfo(userId, page, size); + + return ApiResponse.onSuccess(likedDiscountInfoPage); + } + } From 7966542afdbae69aa5633cf0490986720775139e Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Sat, 28 Sep 2024 10:59:00 +0900 Subject: [PATCH 081/204] =?UTF-8?q?[feat]=20=EC=A2=8B=EC=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=95=A0=EC=9D=B8=EC=A0=95=EB=B3=B4=20=EA=B0=80?= =?UTF-8?q?=EC=A0=B8=EC=98=A4=EA=B8=B0=20API=20Service=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 1. 페이징 설정: 주어진 page와 size를 사용하여 Pageable 객체를 생성합니다. * 2. 사용자 조회: userId로 User 엔티티를 조회하며, 존재하지 않을 경우 IllegalArgumentException을 발생시킵니다. * 3. 좋아요 정보 조회: 조회된 User에 대한 DiscountInfoLike 리스트를 업데이트 시간 기준으로 내림차순 정렬하여 가져옵니다. * 4. ResponseDto로 변환: DiscountInfoLike 리스트를 DiscountResponseDto 페이지로 변환하여 반환합니다. --- .../service/DiscountInfoService.java | 6 ++++++ .../service/DiscountInfoServiceImpl.java | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoService.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoService.java index 3b2aee74..8e51ad99 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoService.java @@ -22,4 +22,10 @@ Page getDiscountsByYearAndMonth( DiscountResponseDto getDiscountInfoById(Long discountInfoId); + Page getLikedDiscountInfo( + Long userId, + Integer page, + Integer size + ); + } \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceImpl.java index 22318ace..2773d773 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceImpl.java @@ -201,4 +201,24 @@ public DiscountResponseDto getDiscountInfoById(Long discountInfoId) { return discountInfoConverter.toDto(discountInfo, connectedInfos); } + + @Transactional + @Override + public Page getLikedDiscountInfo(Long userId, Integer page, Integer size) { + /** + * 1. 페이징 설정: 주어진 page와 size를 사용하여 Pageable 객체를 생성합니다. + * 2. 사용자 조회: userId로 User 엔티티를 조회하며, 존재하지 않을 경우 IllegalArgumentException을 발생시킵니다. + * 3. 좋아요 정보 조회: 조회된 User에 대한 DiscountInfoLike 리스트를 업데이트 시간 기준으로 내림차순 정렬하여 가져옵니다. + * 4. ResponseDto로 변환: DiscountInfoLike 리스트를 DiscountResponseDto 페이지로 변환하여 반환합니다. + */ + + Pageable pageable = PageRequest.of(page, size); + + User user = userRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("User not found")); + + Page likes = discountInfoLikeRepository.findAllByUserOrderByUpdatedAtDesc(user, pageable); + + return discountInfoConverter.toEntityPage(likes); + } } From 62c9d6132fb10c933a26302bcb4dbffeafa4d097 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Sat, 28 Sep 2024 10:59:44 +0900 Subject: [PATCH 082/204] =?UTF-8?q?[feat]=20Page=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=EC=9D=84=20Page?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=ED=99=98=ED=95=98=EB=8A=94=20Converter=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../converter/DiscountInfoConverter.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/converter/DiscountInfoConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/converter/DiscountInfoConverter.java index 9ffa5a13..fba82b05 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/converter/DiscountInfoConverter.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/converter/DiscountInfoConverter.java @@ -1,18 +1,26 @@ package com.bbteam.budgetbuddies.domain.discountinfo.converter; import com.bbteam.budgetbuddies.domain.connectedinfo.entity.ConnectedInfo; +import com.bbteam.budgetbuddies.domain.connectedinfo.repository.ConnectedInfoRepository; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountRequest; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.discountinfolike.entity.DiscountInfoLike; import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; @Service +@RequiredArgsConstructor public class DiscountInfoConverter { + private final ConnectedInfoRepository connectedInfoRepository; + + /** * @param entity * @return responseDto @@ -60,4 +68,44 @@ public DiscountInfo toEntity(DiscountRequest.RegisterDiscountDto requestDto) { .build(); } + + /** + * @param Page + * @return Page + */ + public Page toEntityPage(Page likes) { + + return likes.map(like -> { + // 1. DiscountInfoLike 객체에서 DiscountInfo를 추출합니다. + DiscountInfo discountInfo = like.getDiscountInfo(); + + // 2. 해당 DiscountInfo와 연결된 모든 ConnectedInfo를 connectedInfoRepository를 통해 조회합니다. + List connectedInfos = connectedInfoRepository.findAllByDiscountInfo(discountInfo); + + // 3. ConnectedInfo 리스트에서 DiscountInfo와 연결된 해시태그를 필터링하고, 해시태그의 이름을 추출합니다. + List hashtags = connectedInfos.stream() + // 3-1. DiscountInfo와 연결된 ConnectedInfo만 필터링합니다. + .filter(connectedInfo -> connectedInfo.getDiscountInfo() != null + && connectedInfo.getDiscountInfo().equals(discountInfo)) + // 3-2. ConnectedInfo에서 Hashtag 엔티티를 추출하고, 그 해시태그의 이름을 얻습니다. + .map(ConnectedInfo::getHashtag) + .map(Hashtag::getName) + .toList(); + + // 4. 추출한 데이터를 기반으로 DiscountResponseDto를 생성합니다. + return DiscountResponseDto.builder() + .id(discountInfo.getId()) + .title(discountInfo.getTitle()) + .startDate(discountInfo.getStartDate()) + .endDate(discountInfo.getEndDate()) + .anonymousNumber(discountInfo.getAnonymousNumber()) + .discountRate(discountInfo.getDiscountRate()) + .likeCount(discountInfo.getLikeCount()) + .siteUrl(discountInfo.getSiteUrl()) + .thumbnailUrl(discountInfo.getThumbnailUrl()) + .hashtags(hashtags) + .build(); + }); + } + } From 950adbc2710176d0b8dd1e3a83b91d4d5fd48dc5 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Sat, 28 Sep 2024 11:00:19 +0900 Subject: [PATCH 083/204] =?UTF-8?q?[feat]=20DiscountInfoLikeRepository?= =?UTF-8?q?=EC=97=90=20=EC=82=AC=EC=9A=A9=EC=9E=90=EA=B0=80=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=EB=A5=BC=20=EB=88=84=EB=A5=B8=20=EC=8B=9C?= =?UTF-8?q?=EC=A0=90=EC=9D=84=20=EA=B8=B0=EC=A4=80=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=B5=9C=EC=8B=A0=EC=88=9C=20=EC=A0=95=EB=A0=AC=ED=95=98?= =?UTF-8?q?=EB=8A=94=20Repository=20=EB=A9=94=EC=86=8C=EB=93=9C=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/DiscountInfoLikeRepository.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfolike/repository/DiscountInfoLikeRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfolike/repository/DiscountInfoLikeRepository.java index 3a55285d..bf4606d1 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfolike/repository/DiscountInfoLikeRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfolike/repository/DiscountInfoLikeRepository.java @@ -3,6 +3,8 @@ import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; import com.bbteam.budgetbuddies.domain.discountinfolike.entity.DiscountInfoLike; import com.bbteam.budgetbuddies.domain.user.entity.User; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; @@ -11,4 +13,7 @@ public interface DiscountInfoLikeRepository extends JpaRepository findByUserAndDiscountInfo(User user, DiscountInfo discountInfo); + // 사용자가 좋아요를 누른 시점을 기준으로 최신순 정렬 + Page findAllByUserOrderByUpdatedAtDesc(User user, Pageable pageable); + } From 9f68d7ea61ca66946390559e28fc85051a03ce6f Mon Sep 17 00:00:00 2001 From: MJJ Date: Sun, 29 Sep 2024 05:19:33 +0900 Subject: [PATCH 084/204] =?UTF-8?q?[feat]=20Chat=20GPT=20API=20=EC=84=B8?= =?UTF-8?q?=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../openai/controller/OpenAiController.java | 33 +++++++++++++++++++ .../domain/openai/dto/ChatGPTRequest.java | 19 +++++++++++ .../domain/openai/dto/ChatGPTResponse.java | 24 ++++++++++++++ .../domain/openai/dto/Message.java | 15 +++++++++ .../global/config/OpenAiConfig.java | 22 +++++++++++++ 5 files changed, 113 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/openai/controller/OpenAiController.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatGPTRequest.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatGPTResponse.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/Message.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/config/OpenAiConfig.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/openai/controller/OpenAiController.java b/src/main/java/com/bbteam/budgetbuddies/domain/openai/controller/OpenAiController.java new file mode 100644 index 00000000..40ed89cd --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/openai/controller/OpenAiController.java @@ -0,0 +1,33 @@ +package com.bbteam.budgetbuddies.domain.openai.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +import com.bbteam.budgetbuddies.domain.openai.dto.ChatGPTRequest; +import com.bbteam.budgetbuddies.domain.openai.dto.ChatGPTResponse; + +@RestController +@RequestMapping("/bot") +public class OpenAiController { + @Value("${spring.openai.model}") + private String model; + + @Value("${spring.openai.api.url}") + private String apiURL; + + @Autowired + private RestTemplate template; + + @GetMapping("/chat") + public String chat(@RequestParam(name = "prompt") String prompt) { + ChatGPTRequest request = new ChatGPTRequest(model, prompt); + ChatGPTResponse chatGPTResponse = template.postForObject(apiURL, request, ChatGPTResponse.class); + return chatGPTResponse.getChoices().get(0).getMessage().getContent(); + } + +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatGPTRequest.java b/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatGPTRequest.java new file mode 100644 index 00000000..d1ee13be --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatGPTRequest.java @@ -0,0 +1,19 @@ +package com.bbteam.budgetbuddies.domain.openai.dto; + + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +@Data +public class ChatGPTRequest { + private String model; + private List messages; + + public ChatGPTRequest(String model, String prompt) { + this.model = model; + this.messages = new ArrayList<>(); + this.messages.add(new Message("user", prompt)); + } +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatGPTResponse.java b/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatGPTResponse.java new file mode 100644 index 00000000..a1e54813 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatGPTResponse.java @@ -0,0 +1,24 @@ +package com.bbteam.budgetbuddies.domain.openai.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ChatGPTResponse { + private List choices; + + + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class Choice { + private int index; + private Message message; + + } +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/Message.java b/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/Message.java new file mode 100644 index 00000000..778b23b7 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/Message.java @@ -0,0 +1,15 @@ +package com.bbteam.budgetbuddies.domain.openai.dto; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Message { + private String role; + private String content; + +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/global/config/OpenAiConfig.java b/src/main/java/com/bbteam/budgetbuddies/global/config/OpenAiConfig.java new file mode 100644 index 00000000..be785382 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/config/OpenAiConfig.java @@ -0,0 +1,22 @@ +package com.bbteam.budgetbuddies.global.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class OpenAiConfig { + @Value("${spring.openai.api-key}") + private String openAiKey; + + @Bean + public RestTemplate template() { + RestTemplate restTemplate = new RestTemplate(); + restTemplate.getInterceptors().add((request, body, execution) -> { + request.getHeaders().add("Authorization", "Bearer " + openAiKey); + return execution.execute(request, body); + }); + return restTemplate; + } +} From df8912eba980450251216d381439049cef610a7f Mon Sep 17 00:00:00 2001 From: MJJ Date: Sun, 29 Sep 2024 05:21:08 +0900 Subject: [PATCH 085/204] =?UTF-8?q?[feat]=20=EC=86=8C=EB=B9=84=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=ED=81=B4=EB=A6=AD=20=EC=9C=A0=EB=8F=84=20=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ConsumptionGoalController.java | 5 +++++ .../consumptiongoal/service/ConsumptionGoalService.java | 2 ++ 2 files changed, 7 insertions(+) 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 678e4890..4a75809d 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 @@ -120,4 +120,9 @@ public ApiResponse getMonthReport(@RequestParam(name = " return ApiResponse.onSuccess(response); } + @GetMapping("/cosnumption-ment") + public ApiResponse getConsumptionMention(@RequestParam(name = "userId") Long userId) { + String response = consumptionGoalService.getConsumptionMention(userId); + return ApiResponse.onSuccess(response); + } } \ 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 0a094145..de727f3d 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 @@ -50,4 +50,6 @@ List getAllConsumptionCategories(Long userId, void updateOrCreateDeletedConsumptionGoal(Long userId, Long categoryId, LocalDate goalMonth, Long amount); MonthReportResponseDto getMonthReport(Long userId); + + String getConsumptionMention(Long userId); } From 4190f2dce8278cfffaa3873da62bfe74d189587d Mon Sep 17 00:00:00 2001 From: MJJ Date: Sun, 29 Sep 2024 05:40:13 +0900 Subject: [PATCH 086/204] =?UTF-8?q?[feat]=20Chat=20GPT=20Service=EB=A1=9C?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/openai/dto/ChatGPTResponse.java | 5 ++-- .../domain/openai/service/OpenAiService.java | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/openai/service/OpenAiService.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatGPTResponse.java b/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatGPTResponse.java index a1e54813..1124c67e 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatGPTResponse.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/openai/dto/ChatGPTResponse.java @@ -1,18 +1,17 @@ package com.bbteam.budgetbuddies.domain.openai.dto; +import java.util.List; + import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import java.util.List; - @Data @NoArgsConstructor @AllArgsConstructor public class ChatGPTResponse { private List choices; - @Data @NoArgsConstructor @AllArgsConstructor diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/openai/service/OpenAiService.java b/src/main/java/com/bbteam/budgetbuddies/domain/openai/service/OpenAiService.java new file mode 100644 index 00000000..869784c9 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/openai/service/OpenAiService.java @@ -0,0 +1,28 @@ +package com.bbteam.budgetbuddies.domain.openai.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import com.bbteam.budgetbuddies.domain.openai.dto.ChatGPTRequest; +import com.bbteam.budgetbuddies.domain.openai.dto.ChatGPTResponse; + +@Service +public class OpenAiService { + + @Value("${spring.openai.model}") + private String model; + + @Value("${spring.openai.api.url}") + private String apiURL; + + @Autowired + private RestTemplate template; + + public String chat(String prompt) { + ChatGPTRequest request = new ChatGPTRequest(model, prompt); + ChatGPTResponse chatGPTResponse = template.postForObject(apiURL, request, ChatGPTResponse.class); + return chatGPTResponse.getChoices().get(0).getMessage().getContent(); + } +} From 82d21378db14c88aced877bf1b11905aee102ad9 Mon Sep 17 00:00:00 2001 From: MJJ Date: Sun, 29 Sep 2024 05:40:48 +0900 Subject: [PATCH 087/204] =?UTF-8?q?[feat]=20=EC=86=8C=EB=B9=84=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=ED=81=B4=EB=A6=AD=20=EC=9C=A0=EB=8F=84=20=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20AI=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ConsumptionGoalServiceImpl.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) 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 99aed3d3..3baff436 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 @@ -45,6 +45,8 @@ import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.entity.Expense; import com.bbteam.budgetbuddies.domain.expense.repository.ExpenseRepository; +import com.bbteam.budgetbuddies.domain.gemini.service.GeminiService; +import com.bbteam.budgetbuddies.domain.openai.service.OpenAiService; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; import com.bbteam.budgetbuddies.enums.Gender; @@ -60,6 +62,8 @@ public class ConsumptionGoalServiceImpl implements ConsumptionGoalService { private final ConsumptionGoalRepository consumptionGoalRepository; private final CategoryRepository categoryRepository; private final UserRepository userRepository; + private final GeminiService geminiService; + private final OpenAiService openAiService; private final ConsumptionGoalConverter consumptionGoalConverter; private final LocalDate currentMonth = LocalDate.now().withDayOfMonth(1); @@ -799,4 +803,22 @@ private String getMainComment(List list) { + "만원 이상 쓰시면 안 돼요!"; } } + + @Override + @Transactional(readOnly = true) + public String getConsumptionMention(Long userId) { + + String username = findUserById(userId).getName(); + String categoryName = "패션"; + String consumption = "20000"; + + String prompt = + username + "님 또래는 으로 시작하고 " + categoryName + "," + consumption + + "을 포함해 카테고리 관련 내용(ex. 패션-밥보다 옷을 더 많이 사요, 유흥-술자리에 N만원 써요)같은 멘트나" + + " 카테고리 목표 금액(ex. 패션에 N만원 소비를 계획해요) 트렌드 한 멘트 사용, 인터넷상 바이럴 문구 참고하여 35자 이내 한 문장 만들어줘"; + + return openAiService.chat(prompt); + // return geminiService.getContents(prompt); + } + } \ No newline at end of file From 8ac0b812ceb3ae195e6ae77994db10a73903d41b Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Sun, 29 Sep 2024 14:55:04 +0900 Subject: [PATCH 088/204] =?UTF-8?q?[feat]=20=EC=A7=80=EC=9B=90=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A2=8B=EC=95=84=EC=9A=94=ED=95=9C=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0=20API=20Contro?= =?UTF-8?q?ller=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SupportInfoApi.java | 19 +++++++++++++++++++ .../controller/SupportInfoController.java | 12 ++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoApi.java index 2e87c495..bdbb38e1 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoApi.java @@ -1,6 +1,7 @@ package com.bbteam.budgetbuddies.domain.supportinfo.controller; import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequest; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; @@ -102,4 +103,22 @@ ApiResponse deleteSupportInfo( ApiResponse getSupportInfo( @PathVariable Long supportInfoId ); + + @Operation(summary = "[ADMIN] 특정 사용자가 좋아요를 누른 지원정보 가져오기 API", description = "특정 사용자가 좋아요를 누른 지원정보들을 가져오는 API입니다. 페이징을 포함합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + @Parameters({ + @Parameter(name = "userId", description = "특정 사용자의 id입니다."), + @Parameter(name = "page", description = "페이지 번호, 0번이 1 페이지 입니다. (기본값은 0입니다.)"), + @Parameter(name = "size", description = "한 페이지에 불러올 데이터 개수입니다. (기본값은 10개입니다.)") + }) + ApiResponse> getLikedSupportInfo( + @PathVariable @ExistUser Long userId, + @RequestParam(defaultValue = "0") Integer page, + @RequestParam(defaultValue = "10") Integer size + ); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoController.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoController.java index bbac6fb9..b40f9e81 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoController.java @@ -1,6 +1,7 @@ package com.bbteam.budgetbuddies.domain.supportinfo.controller; import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequest; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; import com.bbteam.budgetbuddies.domain.supportinfo.service.SupportInfoService; @@ -80,4 +81,15 @@ public ApiResponse getSupportInfo( return ApiResponse.onSuccess(supportResponseDto); } + @Override + @GetMapping("/liked-all") + public ApiResponse> getLikedSupportInfo( + @RequestParam @ExistUser Long userId, + @RequestParam(defaultValue = "0") Integer page, + @RequestParam(defaultValue = "10") Integer size + ) { + Page likedSupportInfoPage = supportInfoService.getLikedSupportInfo(userId, page, size); + + return ApiResponse.onSuccess(likedSupportInfoPage); + } } From 755325f07d69c54cca7e81486f26aa4b109cf5e0 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Sun, 29 Sep 2024 14:55:13 +0900 Subject: [PATCH 089/204] =?UTF-8?q?[feat]=20=EC=A7=80=EC=9B=90=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A2=8B=EC=95=84=EC=9A=94=ED=95=9C=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0=20API=20Conver?= =?UTF-8?q?ter=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../converter/SupportInfoConverter.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/converter/SupportInfoConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/converter/SupportInfoConverter.java index c0cb66ca..dfd0d38e 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/converter/SupportInfoConverter.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/converter/SupportInfoConverter.java @@ -1,17 +1,28 @@ package com.bbteam.budgetbuddies.domain.supportinfo.converter; import com.bbteam.budgetbuddies.domain.connectedinfo.entity.ConnectedInfo; +import com.bbteam.budgetbuddies.domain.connectedinfo.repository.ConnectedInfoRepository; +import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import com.bbteam.budgetbuddies.domain.supportinfolike.entity.SupportInfoLike; import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequest; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @Service +@RequiredArgsConstructor public class SupportInfoConverter { + + private final ConnectedInfoRepository connectedInfoRepository; + /** * @param entity * @return responseDto @@ -55,4 +66,48 @@ public SupportInfo toEntity(SupportRequest.RegisterSupportDto requestDto) { .isInCalendar(requestDto.getIsInCalendar()) .build(); } + + /** + * @param Page + * @return Page + */ + public Page toEntityPage(Page likes) { + return likes.map(like -> { + // 1. SupportInfoLike 객체에서 SupportInfo를 추출하고, null인지 확인 + SupportInfo supportInfo = like.getSupportInfo(); + if (supportInfo == null) { + // supportInfo가 null인 경우 null을 반환 + return null; + } + + // 2. 해당 SupportInfo와 연결된 모든 ConnectedInfo를 connectedInfoRepository를 통해 조회합니다. + List connectedInfos = connectedInfoRepository.findAllBySupportInfo(supportInfo); + if (connectedInfos == null) { + connectedInfos = Collections.emptyList(); // connectedInfos가 null일 경우 빈 리스트로 처리 + } + + // 3. ConnectedInfo 리스트에서 SupportInfo와 연결된 해시태그를 필터링하고, 해시태그의 이름을 추출합니다. + List hashtags = connectedInfos.stream() + // 3-1. SupportInfo와 연결된 ConnectedInfo만 필터링하고, Hashtag가 null이 아닌지 확인 + .filter(connectedInfo -> connectedInfo.getSupportInfo() != null + && connectedInfo.getSupportInfo().equals(supportInfo) + && connectedInfo.getHashtag() != null) + .map(ConnectedInfo::getHashtag) + .map(Hashtag::getName) + .toList(); + + // 4. 추출한 데이터를 기반으로 SupportResponseDto를 생성합니다. + return SupportResponseDto.builder() + .id(supportInfo.getId()) + .title(supportInfo.getTitle()) + .startDate(supportInfo.getStartDate()) + .endDate(supportInfo.getEndDate()) + .anonymousNumber(supportInfo.getAnonymousNumber()) + .likeCount(supportInfo.getLikeCount()) + .siteUrl(supportInfo.getSiteUrl()) + .thumbnailUrl(supportInfo.getThumbnailUrl()) + .hashtags(hashtags) + .build(); + }); + } } From 4caaf34457207aa703af1b84d7f35e9768d65405 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Sun, 29 Sep 2024 14:55:24 +0900 Subject: [PATCH 090/204] =?UTF-8?q?[feat]=20=EC=A7=80=EC=9B=90=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=A2=8B=EC=95=84=EC=9A=94=ED=95=9C=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0=20API=20Servic?= =?UTF-8?q?e=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/SupportInfoService.java | 7 ++++++ .../service/SupportInfoServiceImpl.java | 22 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoService.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoService.java index c9ef7783..50bfa5b0 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoService.java @@ -1,5 +1,6 @@ package com.bbteam.budgetbuddies.domain.supportinfo.service; +import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequest; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; import org.springframework.data.domain.Page; @@ -22,4 +23,10 @@ Page getSupportsByYearAndMonth( SupportResponseDto getSupportInfoById(Long supportInfoId); + Page getLikedSupportInfo( + Long userId, + Integer page, + Integer size + ); + } \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceImpl.java index de290465..cf1d72db 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceImpl.java @@ -2,6 +2,8 @@ import com.bbteam.budgetbuddies.domain.connectedinfo.entity.ConnectedInfo; import com.bbteam.budgetbuddies.domain.connectedinfo.repository.ConnectedInfoRepository; +import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; +import com.bbteam.budgetbuddies.domain.discountinfolike.entity.DiscountInfoLike; import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; import com.bbteam.budgetbuddies.domain.hashtag.repository.HashtagRepository; import com.bbteam.budgetbuddies.domain.supportinfo.converter.SupportInfoConverter; @@ -199,4 +201,24 @@ public SupportResponseDto getSupportInfoById(Long supportInfoId) { return supportInfoConverter.toDto(supportInfo, connectedInfos); } + + @Transactional + @Override + public Page getLikedSupportInfo(Long userId, Integer page, Integer size) { + /** + * 1. 페이징 설정: 주어진 page와 size를 사용하여 Pageable 객체를 생성합니다. + * 2. 사용자 조회: userId로 User 엔티티를 조회하며, 존재하지 않을 경우 IllegalArgumentException을 발생시킵니다. + * 3. 좋아요 정보 조회: 조회된 User에 대한 SupportInfoLike 리스트를 업데이트 시간 기준으로 내림차순 정렬하여 가져옵니다. + * 4. ResponseDto로 변환: SupportInfoLike 리스트를 SupportResponseDto 페이지로 변환하여 반환합니다. + */ + + Pageable pageable = PageRequest.of(page, size); + + User user = userRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("User not found")); + + Page likes = supportInfoLikeRepository.findAllByUserOrderByUpdatedAtDesc(user, pageable); + + return supportInfoConverter.toEntityPage(likes); + } } From 19adcaf5050a8c2396930b2eff8ff19db4c89adc Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Sun, 29 Sep 2024 14:55:49 +0900 Subject: [PATCH 091/204] =?UTF-8?q?[feat]=20SupportInfoLikeRepository?= =?UTF-8?q?=EC=97=90=20=EC=82=AC=EC=9A=A9=EC=9E=90=EA=B0=80=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=EB=A5=BC=20=EB=88=84=EB=A5=B8=20=EC=8B=9C?= =?UTF-8?q?=EC=A0=90=EC=9D=84=20=EA=B8=B0=EC=A4=80=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=B5=9C=EC=8B=A0=EC=88=9C=20=EC=A0=95=EB=A0=AC=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/SupportInfoLikeRepository.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfolike/repository/SupportInfoLikeRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfolike/repository/SupportInfoLikeRepository.java index b2618344..9a97fe5e 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfolike/repository/SupportInfoLikeRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfolike/repository/SupportInfoLikeRepository.java @@ -5,6 +5,8 @@ import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; import com.bbteam.budgetbuddies.domain.supportinfolike.entity.SupportInfoLike; import com.bbteam.budgetbuddies.domain.user.entity.User; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; @@ -13,4 +15,7 @@ public interface SupportInfoLikeRepository extends JpaRepository findByUserAndSupportInfo(User user, SupportInfo supportInfo); + // 사용자가 좋아요를 누른 시점을 기준으로 최신순 정렬 + Page findAllByUserOrderByUpdatedAtDesc(User user, Pageable pageable); + } From a549197492816de59ce4c07fc333f9595f2fbaff Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Sun, 29 Sep 2024 19:39:32 +0900 Subject: [PATCH 092/204] =?UTF-8?q?[feat]=20=EA=B3=B5=EC=A2=85=20=EC=9E=91?= =?UTF-8?q?=EC=97=85=EB=AC=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 + .../apiPayload/code/status/ErrorStatus.java | 1 + .../controller/DiscountInfoController.java | 2 + .../domain/user/entity/User.java | 7 ++ .../user/repository/UserRepository.java | 1 + .../global/config/SecurityConfig.java | 9 ++ .../global/security/JwtRequestFilter.java | 76 +++++++++++++ .../global/security/JwtUtil.java | 100 ++++++++++++++++++ .../budgetbuddies/global/user/Login.java | 12 +++ .../global/user/LoginInterceptor.java | 27 +++++ 10 files changed, 239 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/JwtRequestFilter.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/JwtUtil.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/user/Login.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/user/LoginInterceptor.java diff --git a/build.gradle b/build.gradle index dc9cb11d..de8eaa39 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,10 @@ dependencies { annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly('io.jsonwebtoken:jjwt-jackson:0.11.5') // jackson으로 jwt 파싱 } tasks.named('test') { diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java index f2fc183f..83bd72d0 100644 --- a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java @@ -18,6 +18,7 @@ public enum ErrorStatus implements BaseErrorCode { _COMMENT_NOT_FOUND(HttpStatus.BAD_REQUEST, "COMMENT4001", "해당 댓글이 없습니다.") , _NOTICE_NOT_FOUND(HttpStatus.BAD_REQUEST, "NOTICE4001", "해당 공지가 없습니다."), _PAGE_LOWER_ZERO(HttpStatus.BAD_REQUEST, "PAGE4001", "요청된 페이지가 0보다 작습니다."), + _TOKEN_NOT_VALID(HttpStatus.BAD_REQUEST, "TOKEN401", "토큰이 유효하지 않습니다."), _FAQ_NOT_FOUND(HttpStatus.NOT_FOUND, "FAQ4004", "해당 FAQ가 존재하지 않습니다."); diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java index a70a3bf4..5d371117 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java @@ -4,7 +4,9 @@ import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountRequest; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; import com.bbteam.budgetbuddies.domain.discountinfo.service.DiscountInfoService; +import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import com.bbteam.budgetbuddies.global.security.CustomAuthenticationToken; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.*; diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java index 1a120ca7..4ce5e424 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java @@ -8,8 +8,12 @@ import jakarta.validation.constraints.Min; import lombok.*; import lombok.experimental.SuperBuilder; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; @Entity @@ -53,4 +57,7 @@ public void changeUserDate(String email, String name) { this.email = email; } + public List getAuthorities() { + return Collections.singletonList(new SimpleGrantedAuthority(role.name())); + } } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/repository/UserRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/repository/UserRepository.java index f932a14e..78396a35 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/repository/UserRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/repository/UserRepository.java @@ -10,4 +10,5 @@ public interface UserRepository extends JpaRepository { Optional findById(Long userId); + Optional findFirstByPhoneNumber(String phoneNumber); } diff --git a/src/main/java/com/bbteam/budgetbuddies/global/config/SecurityConfig.java b/src/main/java/com/bbteam/budgetbuddies/global/config/SecurityConfig.java index d8b7e218..d578574b 100644 --- a/src/main/java/com/bbteam/budgetbuddies/global/config/SecurityConfig.java +++ b/src/main/java/com/bbteam/budgetbuddies/global/config/SecurityConfig.java @@ -1,6 +1,9 @@ package com.bbteam.budgetbuddies.global.config; +import com.bbteam.budgetbuddies.global.security.OtpService; +import com.bbteam.budgetbuddies.global.security.PhoneNumberAuthenticationProvider; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @@ -21,6 +24,12 @@ @Slf4j public class SecurityConfig { + @Autowired + private final PhoneNumberAuthenticationProvider phoneNumberOtpAuthenticationProvider; + + @Autowired + private final OtpService otpService; + private final Environment env; public SecurityConfig(Environment env) { diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/JwtRequestFilter.java b/src/main/java/com/bbteam/budgetbuddies/global/security/JwtRequestFilter.java new file mode 100644 index 00000000..22a23652 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/JwtRequestFilter.java @@ -0,0 +1,76 @@ +package com.bbteam.budgetbuddies.global.security; + +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +@Component +public class JwtRequestFilter extends OncePerRequestFilter { + + @Autowired + private JwtUtil jwtUtil; + + @Autowired + private MyUserDetailsService userDetailsService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException { + + final String authorizationHeader = request.getHeader("Authorization"); + + Long userId = null; + String phoneNumber = null; + String accessToken = null; + String refreshToken = null; + + String requestURI = request.getRequestURI(); // 요청한 주소 + + + // Extract Bearer token from Authorization header + if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { + accessToken = authorizationHeader.substring(7); + userId = Long.parseLong(jwtUtil.extractUserId(accessToken)); + phoneNumber = jwtUtil.extractPhoneNumber(accessToken); // Assuming you're extracting phone number from JWT + } + + if (isRefreshTokenApiRequest(requestURI)) { // 헤더에 리프레시 토큰이 담긴 요청이라면 리프레시 토큰을 검증 + + } else { // 헤더에 엑세스 토큰이 담긴 요청이라면 엑세스 토큰을 검증 + // Perform authentication if phone number is present and no authentication is set in the request + if (userId != null && phoneNumber != null && request.getAttribute("authenticatedUser") == null) { + User user = this.userDetailsService.loadUserByPhoneNumber(phoneNumber); // Load your custom User object + if (jwtUtil.validateToken(accessToken, user.getId(), user.getPhoneNumber())) { + // Create a custom Authentication object with your User + CustomAuthenticationToken authentication = new CustomAuthenticationToken(user, null, user.getAuthorities()); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + + // Set the custom Authentication in the request attribute + request.setAttribute("authenticatedUser", authentication); + } else { + throw new GeneralException(ErrorStatus._TOKEN_NOT_VALID); + } + } + } + + + // Continue the filter chain + chain.doFilter(request, response); + } + + private boolean isRefreshTokenApiRequest(String requestURI) { + return "/auth/reissue-access-token".equals(requestURI); + +} + diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/JwtUtil.java b/src/main/java/com/bbteam/budgetbuddies/global/security/JwtUtil.java new file mode 100644 index 00000000..3734d967 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/JwtUtil.java @@ -0,0 +1,100 @@ +package com.bbteam.budgetbuddies.global.security; + +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +@Service +@RequiredArgsConstructor +public class JwtUtil { + + private final UserRepository userRepository; + + // JWT 토큰을 서명하는 데 사용되는 비밀 키 + @Value("${jwt.token.key}") + private String SECRET_KEY; + + // 토큰에서 사용자 이름(주체, subject)을 추출하는 메서드 + public String extractUserId(String token) { + return extractClaim(token, Claims::getSubject); + } + + // 토큰에서 만료 시간을 추출하는 메서드 + public Date extractExpiration(String token) { + return extractClaim(token, Claims::getExpiration); + } + + public String extractPhoneNumber(String token) { + // JWT 전화번호 클레임 추출 + return (String) extractAllClaims(token).get("phoneNumber"); + } + + public User extractUser(String token) { + + Long userId = Long.parseLong(extractAllClaims(token).getSubject()); + + User user = userRepository.findById(userId) + .orElseThrow(() -> new GeneralException(ErrorStatus._USER_NOT_FOUND)); + + return user; + } + + // 토큰에서 특정 클레임을 추출하는 메서드 + public T extractClaim(String token, Function claimsResolver) { + final Claims claims = extractAllClaims(token); // 모든 클레임을 추출 + return claimsResolver.apply(claims); // 추출된 클레임에서 원하는 정보를 반환 + } + + // 토큰에서 모든 클레임을 추출하는 메서드 + private Claims extractAllClaims(String token) { + return Jwts.parserBuilder() + .setSigningKey(SECRET_KEY.getBytes()) // 서명 키 설정 + .build() + .parseClaimsJws(token) // 토큰 파싱 + .getBody(); // 클레임 반환 + } + + // 토큰이 만료되었는지 확인하는 메서드 + private Boolean isTokenExpired(String token) { + return extractExpiration(token).before(new Date()); + } + + // 사용자 전화번호 만료 시간을 기반으로 토큰을 생성하는 메서드 + public String generateToken(User user, long expirationTime) { + Map claims = new HashMap<>(); // 클레임으로 빈 맵을 사용 + claims.put("phoneNumber", user.getPhoneNumber()); + return createToken(claims, user.getId(), expirationTime); // 토큰 생성 + } + + // 클레임, 주체, 만료 시간을 기반으로 JWT 토큰을 생성하는 메서드 + private String createToken(Map claims, Long userId, long expirationTime) { + return Jwts.builder() + .setClaims(claims) // 클레임 설정 + .setSubject(String.valueOf(userId)) // 주체 설정 (유저 ID) + .setIssuedAt(new Date(System.currentTimeMillis())) // 현재 시간으로 발행 시간 설정 + .setExpiration(new Date(System.currentTimeMillis() + expirationTime)) // 만료 시간 설정 + .signWith(SignatureAlgorithm.HS256, SECRET_KEY.getBytes()) // 서명 알고리즘과 서명 키 설정 + .compact(); // 최종 토큰을 생성하여 반환 + } + + // 토큰이 유효한지 검증하는 메서드 (사용자 이름과 만료 여부 확인) + public Boolean validateToken(String token, Long userId, String phoneNumber) { + final String extractedUserId = extractUserId(token); // 토큰에서 사용자 ID 추출 + final String extractedPhoneNumber = extractPhoneNumber(token); // 토큰에서 사용자 전화번호 추출 + return (extractedUserId.equals(userId) && extractedPhoneNumber.equals(phoneNumber) && !isTokenExpired(token)); // 사용자 이름이 일치하고 토큰이 만료되지 않았는지 확인 + } +} + + diff --git a/src/main/java/com/bbteam/budgetbuddies/global/user/Login.java b/src/main/java/com/bbteam/budgetbuddies/global/user/Login.java new file mode 100644 index 00000000..71338e72 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/user/Login.java @@ -0,0 +1,12 @@ +package com.bbteam.budgetbuddies.global.user; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface Login { +} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/user/LoginInterceptor.java b/src/main/java/com/bbteam/budgetbuddies/global/user/LoginInterceptor.java new file mode 100644 index 00000000..a149f611 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/user/LoginInterceptor.java @@ -0,0 +1,27 @@ +package com.bbteam.budgetbuddies.global.user; + +import com.bbteam.budgetbuddies.global.security.JwtUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.web.servlet.HandlerInterceptor; + +@RequiredArgsConstructor +public class LoginInterceptor implements HandlerInterceptor { + + private final JwtUtil jwtUtil; + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + String authorization = request.getHeader("Authorization"); + if(authorization == null) { + return false; + } + String jwt = authorization.substring(7); + + Long userId = Long.parseLong(jwtUtil.extractUserId(jwt)); + request.setAttribute("userId", userId); + + + return true; + } +} From a359145692af32ae787e73142da21fa1af83bee2 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Sun, 29 Sep 2024 19:41:23 +0900 Subject: [PATCH 093/204] =?UTF-8?q?[feat]=20=EA=B3=B5=EB=8F=99=20=EC=9E=91?= =?UTF-8?q?=EC=97=85=EB=AC=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/AuthenticationController.java | 103 ++++++++++++++++++ .../security/AuthenticationRequest.java | 41 +++++++ .../security/AuthenticationResponse.java | 52 +++++++++ .../global/security/CertificationNumber.java | 23 ++++ .../security/CustomAuthenticationToken.java | 29 +++++ .../global/security/MyUserDetailsService.java | 38 +++++++ .../global/security/OtpService.java | 12 ++ .../PhoneNumberAuthenticationProvider.java | 54 +++++++++ 8 files changed, 352 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationController.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationRequest.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationResponse.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/CertificationNumber.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/CustomAuthenticationToken.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/MyUserDetailsService.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/OtpService.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/PhoneNumberAuthenticationProvider.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationController.java b/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationController.java new file mode 100644 index 00000000..9b8115fb --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationController.java @@ -0,0 +1,103 @@ +package com.bbteam.budgetbuddies.global.security; + +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +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; + +@RestController +@RequestMapping("/auth") +public class AuthenticationController { + + @Autowired + private AuthenticationManager authenticationManager; // 인증을 처리하는 매니저 + + @Autowired + private JwtUtil jwtUtil; // JWT 유틸리티 클래스 + + @Autowired + private MyUserDetailsService userDetailsService; // 사용자 세부 정보 서비스 + + @Value("${jwt.token.access-token.expiration-time}") + private String accessTokenExpirationTime; // 엑세스 토큰 만료 기한 + + @Value("${jwt.token.refresh-token.expiration-time}") + private String refreshTokenExpirationTime; // 리프레시 토큰 만료 기한 + + + @PostMapping("/get-certification-number") + public ApiResponse getCertificationNumber( + @RequestBody AuthenticationRequest.ToReceiveNumber toReceiveNumber + ) { + + return null; + } + + @PostMapping("/login") + public ApiResponse login( + @RequestBody AuthenticationRequest.ToLogin toLogin + ) { + // 사용자가 입력한 자격 증명으로 인증을 시도 + Authentication authenticate = authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword()) + ); + + authenticate.getDetails(); + + // 인증이 성공하면 사용자 세부 정보를 로드 + final User user = userDetailsService.loadUserByPhoneNumber(toLogin.getPhoneNumber()); + + // AccessToken과 RefreshToken 생성 + final String accessToken = jwtUtil.generateToken(user, Long.parseLong(accessTokenExpirationTime)); // 1시간 유효 + final String refreshToken = jwtUtil.generateToken(user, Long.parseLong(refreshTokenExpirationTime)); // 2주 유효 + + // 생성된 토큰을 포함한 응답을 반환 + AuthenticationResponse.SendTokens response = AuthenticationResponse.SendTokens.builder() + .userId(user.getId()) + .phoneNumber(user.getPhoneNumber()) + .accessToken(accessToken) + .refreshToken(refreshToken) + .build(); + + return ApiResponse.onSuccess(response); + } + + @PostMapping("/reissue-access-token") + public ApiResponse reIssueAccessToken( + @RequestBody AuthenticationRequest.ToReissueAccessToken request + ) { + return null; + } + + + // 사용자 로그인 요청을 처리하는 메서드 + @PostMapping("/login") + public AuthenticationResponse createAuthenticationToken( + @RequestBody AuthenticationRequest authenticationRequest + ) throws Exception { + // 사용자가 입력한 자격 증명으로 인증을 시도 + authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword()) + ); + + // 인증이 성공하면 사용자 세부 정보를 로드 + final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername()); + + // AccessToken과 RefreshToken 생성 + final String accessToken = jwtUtil.generateToken(userDetails.getUsername(), Long.parseLong(accessTokenExpirationTime)); // 1시간 유효 + final String refreshToken = jwtUtil.generateToken(userDetails.getUsername(), Long.parseLong(refreshTokenExpirationTime)); // 1년 유효 + + // 생성된 토큰을 포함한 응답을 반환 + + return ; + } +} + diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationRequest.java b/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationRequest.java new file mode 100644 index 00000000..f5c31dd3 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationRequest.java @@ -0,0 +1,41 @@ +package com.bbteam.budgetbuddies.global.security; + + +import lombok.*; + +public class AuthenticationRequest { + + @Builder + @AllArgsConstructor + @NoArgsConstructor + @Getter + public static class ToReceiveNumber { + // 전화번호 + private String phoneNumber; + + } + + @Builder + @AllArgsConstructor + @NoArgsConstructor + @Getter + public static class ToLogin { + // 전화번호, 인증번호 + private String phoneNumber; + + private String certificationNumber; + } + + @Builder + @AllArgsConstructor + @NoArgsConstructor + @Getter + public static class ToReissueAccessToken { + // 전화번호, 리프레시 토큰 + private String phoneNumber; + + private String RefreshToken; + } + + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationResponse.java b/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationResponse.java new file mode 100644 index 00000000..6e054cf2 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationResponse.java @@ -0,0 +1,52 @@ +package com.bbteam.budgetbuddies.global.security; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +public class AuthenticationResponse { + + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class SendCertificationNumber { // AuthenticationRequest.ToReceiveNumber와 대응 + // 전화번호, 인증번호 객체 + private String phoneNumber; + + private CertificationNumber certificationNumber; + } + + + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class SendTokens { // AuthenticationRequest.ToLogin과 대응 + // 유저 아이디, 전화번호, 액세스 토큰, 리프레시 토큰 + private Long userId; + + private String phoneNumber; + + private String accessToken; + + private String refreshToken; + } + + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class SendAccessToken { // AuthenticationRequest.ToReissueAccessToken와 대응 + // 유저 아이디, 전화번호, 액세스 토큰 + private Long userId; + + private String phoneNumber; + + private String accessToken; + + } + + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/CertificationNumber.java b/src/main/java/com/bbteam/budgetbuddies/global/security/CertificationNumber.java new file mode 100644 index 00000000..60dc6222 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/CertificationNumber.java @@ -0,0 +1,23 @@ +package com.bbteam.budgetbuddies.global.security; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CertificationNumber { + + private String number; // 인증번호 + + private Integer effectiveTime; // 유효시간 (ex. 3분 = 3) + + private LocalDateTime createdAt; // 생성시각 + + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/CustomAuthenticationToken.java b/src/main/java/com/bbteam/budgetbuddies/global/security/CustomAuthenticationToken.java new file mode 100644 index 00000000..3f07df15 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/CustomAuthenticationToken.java @@ -0,0 +1,29 @@ +package com.bbteam.budgetbuddies.global.security; + +import com.bbteam.budgetbuddies.domain.user.entity.User; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +public class CustomAuthenticationToken extends AbstractAuthenticationToken { + private final User user; + private Object credentials; + + public CustomAuthenticationToken(User user, Object credentials, Collection authorities) { + super(authorities); + this.user = user; + this.credentials = credentials; + super.setAuthenticated(true); // This can be set based on the actual authentication logic + } + + @Override + public Object getCredentials() { + return credentials; + } + + @Override + public Object getPrincipal() { + return user; // This returns your custom User object + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/MyUserDetailsService.java b/src/main/java/com/bbteam/budgetbuddies/global/security/MyUserDetailsService.java new file mode 100644 index 00000000..4275ffad --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/MyUserDetailsService.java @@ -0,0 +1,38 @@ +package com.bbteam.budgetbuddies.global.security; + +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +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.ArrayList; + +@Service +@RequiredArgsConstructor +public class MyUserDetailsService implements UserDetailsService { + + private final UserRepository userRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + // 이 예제에서는 고정된 사용자 이름과 비밀번호를 사용 (실제 운영에서는 데이터베이스에서 로드) + + User user = userRepository.findFirstByPhoneNumber(username) + .orElseThrow(() -> new GeneralException(ErrorStatus._USER_NOT_FOUND)); + + + return new org.springframework.security.core.userdetails.User("johndoe", "password", new ArrayList<>()); + } + + public User loadUserByPhoneNumber(String phoneNumber) { + User user = userRepository.findFirstByPhoneNumber(phoneNumber) + .orElseThrow(() -> new GeneralException(ErrorStatus._USER_NOT_FOUND)); + + return user; + } +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/OtpService.java b/src/main/java/com/bbteam/budgetbuddies/global/security/OtpService.java new file mode 100644 index 00000000..11edf226 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/OtpService.java @@ -0,0 +1,12 @@ +package com.bbteam.budgetbuddies.global.security; + +import org.springframework.stereotype.Service; + +@Service +public class OtpService { + + + public boolean verifyOtp(String phoneNumber, String otp) { + return true; + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/PhoneNumberAuthenticationProvider.java b/src/main/java/com/bbteam/budgetbuddies/global/security/PhoneNumberAuthenticationProvider.java new file mode 100644 index 00000000..65ce69d6 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/PhoneNumberAuthenticationProvider.java @@ -0,0 +1,54 @@ +package com.bbteam.budgetbuddies.global.security; + +import com.bbteam.budgetbuddies.domain.user.entity.User; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; + +@Component +@RequiredArgsConstructor +public class PhoneNumberAuthenticationProvider implements AuthenticationProvider { + + private final MyUserDetailsService myUserDetailsService; + private final OtpService otpService; // Service to verify OTPs + + /** + * @param myUserDetailsService + * @param otpService + */ + public PhoneNumberOtpAuthenticationProvider(MyUserDetailsService myUserDetailsService, OtpService otpService) { + this.myUserDetailsService = myUserDetailsService; + this.otpService = otpService; + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String phoneNumber = authentication.getName(); + String otp = authentication.getCredentials().toString(); + + // Verify the OTP using your OTP service + if (!otpService.verifyOtp(phoneNumber, otp)) { + throw new BadCredentialsException("Invalid OTP"); + } + + // Load the user after OTP verification + User user = myUserDetailsService.loadUserByPhoneNumber(phoneNumber); + + // No password check, return a token with the authenticated user + return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>()); + } + + @Override + public boolean supports(Class authentication) { + return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); + } +} From 0a7d83ddca5c708cda7ad787437256106f08651c Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 1 Oct 2024 14:18:29 +0900 Subject: [PATCH 094/204] =?UTF-8?q?[feat]=20ReportController=20=EC=8B=A0?= =?UTF-8?q?=EA=B3=A0=EC=A0=91=EC=88=98=EB=A5=BC=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=8B=A0=EA=B3=A0=EC=9D=B4=EB=A0=A5=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/controller/ReportController.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportController.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportController.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportController.java new file mode 100644 index 00000000..4f4cdebc --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportController.java @@ -0,0 +1,30 @@ +package com.bbteam.budgetbuddies.domain.report.controller; + +import org.springframework.data.repository.query.Param; +import org.springframework.http.HttpStatus; +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 com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.report.service.ReportService; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/{commentId}/report") +@RequiredArgsConstructor +public class ReportController { + private final ReportService reportService; + + @GetMapping("/{userId}") + public ApiResponse isExistReport(@PathVariable @Param("commentId") Long commentId, + @PathVariable @Param("userId") Long userId) { + if (reportService.isExistReport(commentId, userId)) { + return ApiResponse.onSuccess("신고 이력이 존재합니다."); + } + return ApiResponse.onFailure(HttpStatus.NOT_FOUND.toString(), HttpStatus.NOT_FOUND.getReasonPhrase(), + "신고 이력이 존재하지 않습니다."); + } +} From ba3e3242cd84fd9cd796bb27d8248941bfdef45f Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 1 Oct 2024 14:19:16 +0900 Subject: [PATCH 095/204] =?UTF-8?q?[feat]=20ReportService=20userId?= =?UTF-8?q?=EC=99=80=20commentId=EB=A5=BC=20=ED=86=B5=ED=95=9C=20=EC=8B=A0?= =?UTF-8?q?=EA=B3=A0=EC=9D=B4=EB=A0=A5=20=EC=97=AC=EB=B6=80=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/report/service/ReportService.java | 5 +++ .../report/service/ReportServiceImpl.java | 33 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImpl.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java new file mode 100644 index 00000000..12de45ff --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java @@ -0,0 +1,5 @@ +package com.bbteam.budgetbuddies.domain.report.service; + +public interface ReportService { + boolean isExistReport(Long commentId, Long userId); +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImpl.java new file mode 100644 index 00000000..325075d5 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImpl.java @@ -0,0 +1,33 @@ +package com.bbteam.budgetbuddies.domain.report.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.bbteam.budgetbuddies.domain.comment.entity.Comment; +import com.bbteam.budgetbuddies.domain.comment.repository.CommentRepository; +import com.bbteam.budgetbuddies.domain.report.convertor.ReportConvertor; +import com.bbteam.budgetbuddies.domain.report.repository.ReportRepository; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class ReportServiceImpl implements ReportService { + private final ReportRepository reportRepository; + private final CommentRepository commentRepository; + private final UserRepository userRepository; + + private final ReportConvertor reportConvertor; + + @Override + @Transactional(readOnly = true) + public boolean isExistReport(Long commentId, Long userId) { + User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("not found user")); + Comment comment = commentRepository.findById(commentId) + .orElseThrow(() -> new IllegalArgumentException("not found comment")); + + return reportRepository.existsByUserAndComment(user, comment); + } +} From b4b8b158f5af4e2eac34d3552b142c345519b0af Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 1 Oct 2024 14:19:59 +0900 Subject: [PATCH 096/204] =?UTF-8?q?[feat]=20ReportRepository=20User?= =?UTF-8?q?=EC=99=80=20Comment=EB=A5=BC=20=ED=86=B5=ED=95=B4=20DB=EC=97=90?= =?UTF-8?q?=20Report=EA=B0=80=20=EC=9E=88=EB=8A=94=EC=A7=80=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/report/repository/ReportRepository.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepository.java index 6661333c..f27af0ee 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepository.java @@ -1,9 +1,11 @@ package com.bbteam.budgetbuddies.domain.report.repository; -import com.bbteam.budgetbuddies.domain.report.entity.Report; import org.springframework.data.jpa.repository.JpaRepository; -public interface ReportRepository extends JpaRepository { - +import com.bbteam.budgetbuddies.domain.comment.entity.Comment; +import com.bbteam.budgetbuddies.domain.report.entity.Report; +import com.bbteam.budgetbuddies.domain.user.entity.User; +public interface ReportRepository extends JpaRepository { + boolean existsByUserAndComment(User user, Comment comment); } From 6f0d215b36b8de6f1dc1f0a8c2eb02d0133c5bf2 Mon Sep 17 00:00:00 2001 From: MJJ Date: Wed, 2 Oct 2024 19:26:09 +0900 Subject: [PATCH 097/204] =?UTF-8?q?[feat]=20=EA=B0=80=EC=9E=A5=20=ED=81=B0?= =?UTF-8?q?=20=EC=86=8C=EB=B9=84=20=EB=AA=A9=ED=91=9C=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EC=99=80=20=EA=B0=80=EC=9E=A5=20=ED=81=B0=20=EB=AA=A9?= =?UTF-8?q?=ED=91=9C=20=EA=B8=88=EC=95=A1=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ConsumptionGoalRepository.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) 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 0a335341..ad52c9c8 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 @@ -156,4 +156,32 @@ List findConsumeAmountsByCategories( List findConsumeAmountAndGoalAmount( @Param("user") User user, @Param("currentMonth") LocalDate currentMonth); + + @Query("SELECT cg " + + "FROM ConsumptionGoal cg " + + "WHERE cg.category.isDefault = true " + + "AND cg.deleted = false " + + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + + "AND cg.user.gender = :peerGender " + + "AND cg.goalMonth >= :currentMonth " + + "ORDER BY cg.consumeAmount DESC LIMIT 1") + Optional findMaxConsumeAmountByCategory( + @Param("peerAgeStart") int peerAgeStart, + @Param("peerAgeEnd") int peerAgeEnd, + @Param("peerGender") Gender peerGender, + @Param("currentMonth") LocalDate currentMonth); + + @Query("SELECT cg " + + "FROM ConsumptionGoal cg " + + "WHERE cg.category.isDefault = true " + + "AND cg.deleted = false " + + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + + "AND cg.user.gender = :peerGender " + + "AND cg.goalMonth >= :currentMonth " + + "ORDER BY cg.goalAmount DESC LIMIT 1") + Optional findMaxGoalAmountByCategory( + @Param("peerAgeStart") int peerAgeStart, + @Param("peerAgeEnd") int peerAgeEnd, + @Param("peerGender") Gender peerGender, + @Param("currentMonth") LocalDate currentMonth); } From 618083cf7f57d2902d152c65b8be0c6af618b5f0 Mon Sep 17 00:00:00 2001 From: MJJ Date: Wed, 2 Oct 2024 19:28:40 +0900 Subject: [PATCH 098/204] =?UTF-8?q?[feat]=20=EC=86=8C=EB=B9=84=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=ED=81=B4=EB=A6=AD=20=EC=9C=A0=EB=8F=84=20=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20AI=20=ED=94=84=EB=A1=AC=ED=94=84=ED=8A=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ConsumptionGoalServiceImpl.java | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) 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 3baff436..b719c984 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 @@ -60,6 +60,7 @@ public class ConsumptionGoalServiceImpl implements ConsumptionGoalService { private final ConsumptionGoalRepository consumptionGoalRepository; + private final ExpenseRepository expenseRepository; private final CategoryRepository categoryRepository; private final UserRepository userRepository; private final GeminiService geminiService; @@ -69,7 +70,6 @@ public class ConsumptionGoalServiceImpl implements ConsumptionGoalService { private final LocalDate currentMonth = LocalDate.now().withDayOfMonth(1); private final LocalDate startOfWeek = LocalDate.now().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); private final LocalDate endOfWeek = LocalDate.now().with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)); - private final ExpenseRepository expenseRepository; private int peerAgeStart; private int peerAgeEnd; private Gender peerGender; @@ -808,17 +808,54 @@ private String getMainComment(List list) { @Transactional(readOnly = true) public String getConsumptionMention(Long userId) { + /** + * 가장 큰 소비를 한 소비 목표 데이터 정보와 가장 큰 목표로 세운 소비 목표데이터를 각각 가져온다. + * 위 데이터들을 가지고 프롬프트 진행 + * Gemini AI, Chat GPT + */ + + checkPeerInfo(userId, 0, 0, "none"); + + Optional maxConsumeAmount = consumptionGoalRepository.findMaxConsumeAmountByCategory( + peerAgeStart, + peerAgeEnd, + peerGender, currentMonth); + + Optional maxGoalAmount = consumptionGoalRepository.findMaxGoalAmountByCategory( + peerAgeStart, + peerAgeEnd, + peerGender, currentMonth); + + if (!maxConsumeAmount.isPresent()) { + throw new IllegalArgumentException("해당 소비목표 데이터를 찾을 수 없습니다."); + } + String username = findUserById(userId).getName(); - String categoryName = "패션"; - String consumption = "20000"; + String categoryName = maxConsumeAmount.get().getCategory().getName(); + String consumeAmount = String.valueOf(maxConsumeAmount.get().getConsumeAmount()); + + String firstPrompt = "00은 " + username + ", 가장 큰 소비 카테고리 이름은 " + categoryName + + "," + "해당 카테고리 소비금액은" + consumeAmount + "이야"; + + if (!maxGoalAmount.isPresent()) { + throw new IllegalArgumentException("해당 소비목표 데이터를 찾을 수 없습니다."); + } + + categoryName = maxGoalAmount.get().getCategory().getName(); + String goalAmount = String.valueOf(maxGoalAmount.get().getGoalAmount()); + + String secondPrompt = "가장 큰 목표 소비 카테고리 이름은 " + categoryName + + ", 해당 카테고리 목표금액은" + goalAmount + "이야"; - String prompt = - username + "님 또래는 으로 시작하고 " + categoryName + "," + consumption - + "을 포함해 카테고리 관련 내용(ex. 패션-밥보다 옷을 더 많이 사요, 유흥-술자리에 N만원 써요)같은 멘트나" + - " 카테고리 목표 금액(ex. 패션에 N만원 소비를 계획해요) 트렌드 한 멘트 사용, 인터넷상 바이럴 문구 참고하여 35자 이내 한 문장 만들어줘"; + String basePrompt = "소비 분석 관련 멘트를 2개 만들거야 이때," + username + + "님 또래는 ~ 이라는 문장으로 시작하고 35자 이내 한 문장씩 만들어줘" + + firstPrompt + "와" + secondPrompt + "를 사용하고 두 문장의 구분은 줄바꿈으로 해주고, " + + "카테고리 관련 내용(ex. 패션-밥보다 옷을 더 많이 사요, 유흥-술자리에 N만원 써요)같은 멘트나" + + "카테고리 목표 금액(ex. 패션에 N만원 소비를 계획해요)같은 트렌드 한 멘트, 인터넷상 바이럴 문구" + + "참고하여 만들어줘"; - return openAiService.chat(prompt); - // return geminiService.getContents(prompt); + return openAiService.chat(basePrompt); + // return geminiService.getContents(basePrompt); } } \ No newline at end of file From 0162f9ed0a9f52ca9c2952ebc2352213e0642c20 Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 3 Oct 2024 13:16:15 +0900 Subject: [PATCH 099/204] =?UTF-8?q?[doc]=20=EC=8B=A0=EA=B3=A0=EC=9D=B4?= =?UTF-8?q?=EB=A0=A5=EC=A1=B0=ED=9A=8C=20=EB=AC=B8=EC=84=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/report/controller/ReportApi.java | 21 +++++++++++++++++++ .../report/controller/ReportController.java | 2 +- .../domain/report/service/ReportService.java | 6 ++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportApi.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportApi.java new file mode 100644 index 00000000..bd9ec7e4 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportApi.java @@ -0,0 +1,21 @@ +package com.bbteam.budgetbuddies.domain.report.controller; + +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.responses.ApiResponses; + +public interface ReportApi { + @Operation(summary = "[User] 댓글에대한 신고여부 조회", description = "존재하지 않을 경우에만 신고 뷰로 이동") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON404", description = "NOTFOUND, 실패") + }) + @Parameters({ + @Parameter(name = "userId", description = "로그인 한 유저 아이디"), + @Parameter(name = "peerAgeStart", description = "또래나이 시작 범위") + }) + ApiResponse isExistReport(Long commentId, Long userId); +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportController.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportController.java index 4f4cdebc..6cda9de0 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportController.java @@ -15,7 +15,7 @@ @RestController @RequestMapping("/{commentId}/report") @RequiredArgsConstructor -public class ReportController { +public class ReportController implements ReportApi{ private final ReportService reportService; @GetMapping("/{userId}") diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java index 12de45ff..78aff100 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java @@ -1,5 +1,11 @@ package com.bbteam.budgetbuddies.domain.report.service; public interface ReportService { + /** + * 댓글에 대한 신고이력 여부 조회 + * @param commentId + * @param userId + * @return 존재하면 true 존재하지 않으면 false + */ boolean isExistReport(Long commentId, Long userId); } From 0431638442e4458af791ab260a6e0f9ed5f61446 Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 3 Oct 2024 16:36:47 +0900 Subject: [PATCH 100/204] =?UTF-8?q?[feat]=20=EB=8C=93=EA=B8=80=EC=8B=A0?= =?UTF-8?q?=EA=B3=A0=20=EC=9A=94=EC=B2=AD=20=EB=B0=8F=20=EC=9D=91=EB=8B=B5?= =?UTF-8?q?=20DTO=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/dto/request/ReportRequestDto.java | 13 +++++++++++++ .../report/dto/response/ReportResponseDto.java | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/report/dto/request/ReportRequestDto.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/report/dto/response/ReportResponseDto.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/dto/request/ReportRequestDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/dto/request/ReportRequestDto.java new file mode 100644 index 00000000..36049258 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/dto/request/ReportRequestDto.java @@ -0,0 +1,13 @@ +package com.bbteam.budgetbuddies.domain.report.dto.request; + +import jakarta.validation.constraints.NotBlank; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +public class ReportRequestDto { + @NotBlank + private String reason; +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/dto/response/ReportResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/dto/response/ReportResponseDto.java new file mode 100644 index 00000000..c5893e37 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/dto/response/ReportResponseDto.java @@ -0,0 +1,17 @@ +package com.bbteam.budgetbuddies.domain.report.dto.response; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor +@Builder +@Getter +public class ReportResponseDto { + private Long reportId; + private Long userId; + private Long commentId; +} From ab16b77f7f7be6af9586cb50cf20099fde2b356f Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 3 Oct 2024 16:38:03 +0900 Subject: [PATCH 101/204] =?UTF-8?q?[feat]=20ReportController=20=EB=8C=93?= =?UTF-8?q?=EA=B8=80=EC=8B=A0=EA=B3=A0=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/controller/ReportController.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportController.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportController.java index 6cda9de0..a53e6632 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportController.java @@ -4,18 +4,23 @@ import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +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 com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.report.dto.request.ReportRequestDto; +import com.bbteam.budgetbuddies.domain.report.dto.response.ReportResponseDto; import com.bbteam.budgetbuddies.domain.report.service.ReportService; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; @RestController @RequestMapping("/{commentId}/report") @RequiredArgsConstructor -public class ReportController implements ReportApi{ +public class ReportController implements ReportApi { private final ReportService reportService; @GetMapping("/{userId}") @@ -27,4 +32,12 @@ public ApiResponse isExistReport(@PathVariable @Param("commentId") Long return ApiResponse.onFailure(HttpStatus.NOT_FOUND.toString(), HttpStatus.NOT_FOUND.getReasonPhrase(), "신고 이력이 존재하지 않습니다."); } + + @PostMapping("/{userId}") + public ApiResponse reportComment(@Valid @RequestBody ReportRequestDto request, + @PathVariable @Param("commentId") Long commentId, @PathVariable @Param("userId") Long userId) { + + ReportResponseDto response = reportService.reportComment(request, commentId, userId); + return ApiResponse.onSuccess(response); + } } From 649290e5034f05b43127d1c9ca14246a4b08ee71 Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 3 Oct 2024 16:39:36 +0900 Subject: [PATCH 102/204] =?UTF-8?q?[feat]=20ReportService=20=EB=8C=93?= =?UTF-8?q?=EA=B8=80=EC=8B=A0=EA=B3=A0=20=EB=B9=84=EC=A6=88=EB=8B=88?= =?UTF-8?q?=EC=8A=A4=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/convertor/ReportConvertor.java | 24 +++++++++++++++++++ .../domain/report/service/ReportService.java | 5 ++++ .../report/service/ReportServiceImpl.java | 7 ++++++ 3 files changed, 36 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/report/convertor/ReportConvertor.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/convertor/ReportConvertor.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/convertor/ReportConvertor.java new file mode 100644 index 00000000..1fa2fa58 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/convertor/ReportConvertor.java @@ -0,0 +1,24 @@ +package com.bbteam.budgetbuddies.domain.report.convertor; + +import org.springframework.stereotype.Component; + +import com.bbteam.budgetbuddies.domain.comment.entity.Comment; +import com.bbteam.budgetbuddies.domain.report.dto.request.ReportRequestDto; +import com.bbteam.budgetbuddies.domain.report.dto.response.ReportResponseDto; +import com.bbteam.budgetbuddies.domain.report.entity.Report; +import com.bbteam.budgetbuddies.domain.user.entity.User; + +@Component +public class ReportConvertor { + public Report toEntity(ReportRequestDto request, User user, Comment comment) { + return Report.builder().user(user).comment(comment).reason(request.getReason()).build(); + } + + public ReportResponseDto toReportResponse(Report report) { + return ReportResponseDto.builder() + .reportId(report.getId()) + .userId(report.getUser().getId()) + .commentId(report.getComment().getId()) + .build(); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java index 78aff100..31e74e36 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java @@ -1,5 +1,8 @@ package com.bbteam.budgetbuddies.domain.report.service; +import com.bbteam.budgetbuddies.domain.report.dto.request.ReportRequestDto; +import com.bbteam.budgetbuddies.domain.report.dto.response.ReportResponseDto; + public interface ReportService { /** * 댓글에 대한 신고이력 여부 조회 @@ -8,4 +11,6 @@ public interface ReportService { * @return 존재하면 true 존재하지 않으면 false */ boolean isExistReport(Long commentId, Long userId); + + ReportResponseDto reportComment(ReportRequestDto request, Long commentId, Long userId); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImpl.java index 325075d5..a7cfac13 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImpl.java @@ -24,10 +24,17 @@ public class ReportServiceImpl implements ReportService { @Override @Transactional(readOnly = true) public boolean isExistReport(Long commentId, Long userId) { + @Override + @Transactional + public ReportResponseDto reportComment(ReportRequestDto request, Long commentId, Long userId) { User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("not found user")); Comment comment = commentRepository.findById(commentId) .orElseThrow(() -> new IllegalArgumentException("not found comment")); return reportRepository.existsByUserAndComment(user, comment); + Report report = reportConvertor.toEntity(request, user, comment); + Report savedReport = reportRepository.save(report); + + return reportConvertor.toReportResponse(savedReport); } } From e56235035dedbf02c33be2fae6406c2fedb394a7 Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 3 Oct 2024 16:44:40 +0900 Subject: [PATCH 103/204] =?UTF-8?q?[doc]=20=EB=8C=93=EA=B8=80=EC=8B=A0?= =?UTF-8?q?=EA=B3=A0=20=EB=AC=B8=EC=84=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/report/controller/ReportApi.java | 22 +++++++++++++++++-- .../domain/report/service/ReportService.java | 7 ++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportApi.java index bd9ec7e4..158b20fe 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/controller/ReportApi.java @@ -1,11 +1,18 @@ package com.bbteam.budgetbuddies.domain.report.controller; +import org.springframework.data.repository.query.Param; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; + import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.report.dto.request.ReportRequestDto; +import com.bbteam.budgetbuddies.domain.report.dto.response.ReportResponseDto; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.validation.Valid; public interface ReportApi { @Operation(summary = "[User] 댓글에대한 신고여부 조회", description = "존재하지 않을 경우에만 신고 뷰로 이동") @@ -14,8 +21,19 @@ public interface ReportApi { @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON404", description = "NOTFOUND, 실패") }) @Parameters({ - @Parameter(name = "userId", description = "로그인 한 유저 아이디"), - @Parameter(name = "peerAgeStart", description = "또래나이 시작 범위") + @Parameter(name = "commentId", description = "신고할 댓글 아이디"), + @Parameter(name = "userId", description = "로그인 한 유저 아이디") }) ApiResponse isExistReport(Long commentId, Long userId); + + @Operation(summary = "[User] 댓글에대한 신고") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "commentId", description = "신고할 댓글 아이디"), + @Parameter(name = "userId", description = "로그인 한 유저 아이디") + }) + ApiResponse reportComment(@Valid @RequestBody ReportRequestDto request, + @PathVariable @Param("commentId") Long commentId, @PathVariable @Param("userId") Long userId); } \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java index 31e74e36..6335c055 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportService.java @@ -12,5 +12,12 @@ public interface ReportService { */ boolean isExistReport(Long commentId, Long userId); + /** + * 댓글에 대한 신고 생성 + * @param request + * @param commentId + * @param userId + * @return reportID를 포함한 신고정보 + */ ReportResponseDto reportComment(ReportRequestDto request, Long commentId, Long userId); } From bb2d666a8318e85f4da1be0f9c1867369d91e3ae Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 3 Oct 2024 16:47:26 +0900 Subject: [PATCH 104/204] =?UTF-8?q?[refactor]=20Report=EB=A5=BC=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=EA=B0=80=20=EC=95=84=EB=8B=8C=20id?= =?UTF-8?q?=EB=A5=BC=20=ED=86=B5=ED=95=B4=EC=84=9C=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit id를 통해 Entitiy를 DB에서 불러온 후, 추가적인 조회가 이뤄지는 것이 아니기 때문에 DB에 대한 I/O가 감소 --- .../domain/report/repository/ReportRepository.java | 2 +- .../domain/report/service/ReportServiceImpl.java | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepository.java index f27af0ee..30196ae1 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepository.java @@ -7,5 +7,5 @@ import com.bbteam.budgetbuddies.domain.user.entity.User; public interface ReportRepository extends JpaRepository { - boolean existsByUserAndComment(User user, Comment comment); + boolean existsByUser_IdAndComment_Id(Long userId, Long CommentId); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImpl.java index a7cfac13..62839644 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImpl.java @@ -6,6 +6,9 @@ import com.bbteam.budgetbuddies.domain.comment.entity.Comment; import com.bbteam.budgetbuddies.domain.comment.repository.CommentRepository; import com.bbteam.budgetbuddies.domain.report.convertor.ReportConvertor; +import com.bbteam.budgetbuddies.domain.report.dto.request.ReportRequestDto; +import com.bbteam.budgetbuddies.domain.report.dto.response.ReportResponseDto; +import com.bbteam.budgetbuddies.domain.report.entity.Report; import com.bbteam.budgetbuddies.domain.report.repository.ReportRepository; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; @@ -24,6 +27,9 @@ public class ReportServiceImpl implements ReportService { @Override @Transactional(readOnly = true) public boolean isExistReport(Long commentId, Long userId) { + return reportRepository.existsByUser_IdAndComment_Id(userId, commentId); + } + @Override @Transactional public ReportResponseDto reportComment(ReportRequestDto request, Long commentId, Long userId) { @@ -31,7 +37,6 @@ public ReportResponseDto reportComment(ReportRequestDto request, Long commentId, Comment comment = commentRepository.findById(commentId) .orElseThrow(() -> new IllegalArgumentException("not found comment")); - return reportRepository.existsByUserAndComment(user, comment); Report report = reportConvertor.toEntity(request, user, comment); Report savedReport = reportRepository.save(report); From 3b1dc1df6c1be64ddc09f158eec57764d877d1b6 Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 3 Oct 2024 16:49:28 +0900 Subject: [PATCH 105/204] =?UTF-8?q?[fix]=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EC=9D=98=20=EC=9D=98=EB=AF=B8=EC=97=86=EB=8A=94=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=9D=84=20=EB=B0=A9=EC=A7=80=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20@Setter=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/report/entity/Report.java | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/entity/Report.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/entity/Report.java index b5db7e5d..5b05f6a7 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/report/entity/Report.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/entity/Report.java @@ -1,38 +1,39 @@ package com.bbteam.budgetbuddies.domain.report.entity; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; + import com.bbteam.budgetbuddies.common.BaseEntity; import com.bbteam.budgetbuddies.domain.comment.entity.Comment; import com.bbteam.budgetbuddies.domain.user.entity.User; + import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; -import lombok.*; -import lombok.experimental.SuperBuilder; -import org.hibernate.annotations.NotFound; -import org.hibernate.annotations.NotFoundAction; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; @Entity @Getter -@Setter @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor -@SuperBuilder +@Builder public class Report extends BaseEntity { - /** - * 특정 사용자가 특정 댓글을 신고하는 엔티티 (교차 엔티티) - */ - - @ManyToOne(fetch = FetchType.LAZY) - @NotFound(action = NotFoundAction.IGNORE) - @JoinColumn(name = "user_id") - private User user; - - @ManyToOne(fetch = FetchType.LAZY) - @NotFound(action = NotFoundAction.IGNORE) - @JoinColumn(name = "comment_id") - private Comment comment; + @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) + @JoinColumn(name = "user_id") + private User user; - private String reason; // 신고 사유 + @ManyToOne(fetch = FetchType.LAZY) + @NotFound(action = NotFoundAction.IGNORE) + @JoinColumn(name = "comment_id") + private Comment comment; + @NonNull + private String reason; } From 837d4775c029be3a7318dc31b289c9a18583827e Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 3 Oct 2024 16:50:41 +0900 Subject: [PATCH 106/204] =?UTF-8?q?[test]=20commentRepository=20=EC=99=B8?= =?UTF-8?q?=EB=9E=98=ED=82=A4=EC=9D=98=20Entity=EA=B0=80=20=EC=95=84?= =?UTF-8?q?=EB=8B=8C=20ID=EB=A5=BC=20=ED=86=B5=ED=95=9C=20Report=20?= =?UTF-8?q?=EC=A1=B4=EC=9E=AC=20=EC=97=AC=EB=B6=80=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ReportRepositoryTest.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/test/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepositoryTest.java diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepositoryTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepositoryTest.java new file mode 100644 index 00000000..6e7345c7 --- /dev/null +++ b/src/test/java/com/bbteam/budgetbuddies/domain/report/repository/ReportRepositoryTest.java @@ -0,0 +1,48 @@ +package com.bbteam.budgetbuddies.domain.report.repository; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +import com.bbteam.budgetbuddies.domain.comment.entity.Comment; +import com.bbteam.budgetbuddies.domain.comment.repository.CommentRepository; +import com.bbteam.budgetbuddies.domain.report.entity.Report; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import com.bbteam.budgetbuddies.enums.Gender; + +@DisplayName("Report 레포지토리 테스트의") +@DataJpaTest +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +class ReportRepositoryTest { + @Autowired + ReportRepository reportRepository; + + @Autowired + UserRepository userRepository; + @Autowired + CommentRepository commentRepository; + + @Test + void userId와_commentId를_통해서_Report_존재_여부_조회() { + User user = userRepository.save(User.builder() + .email("testUser@example.com") + .mobileCarrier("TEST") + .age(24) + .name("Test User") + .gender(Gender.MALE) + .phoneNumber("010-1234-5678") + .build()); + + Comment comment = commentRepository.save( + Comment.builder().user(user).content("test2").anonymousNumber(1212).build()); + + reportRepository.save(Report.builder().user(user).comment(comment).reason("test1").build()); + + assertTrue(reportRepository.existsByUser_IdAndComment_Id(user.getId(), comment.getId())); + } +} \ No newline at end of file From 1ecce236073fdbc188a1ea34e96fcd6cdd087d45 Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 3 Oct 2024 17:46:16 +0900 Subject: [PATCH 107/204] =?UTF-8?q?[test]=20ReportService=20=EB=8C=93?= =?UTF-8?q?=EA=B8=80=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=8B=A0=EA=B3=A0=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/dto/request/ReportRequestDto.java | 3 +- .../report/service/ReportServiceImplTest.java | 75 +++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImplTest.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/report/dto/request/ReportRequestDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/report/dto/request/ReportRequestDto.java index 36049258..66f5474b 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/report/dto/request/ReportRequestDto.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/report/dto/request/ReportRequestDto.java @@ -1,11 +1,10 @@ package com.bbteam.budgetbuddies.domain.report.dto.request; import jakarta.validation.constraints.NotBlank; -import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -@NoArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor @Getter public class ReportRequestDto { @NotBlank diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImplTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImplTest.java new file mode 100644 index 00000000..f03d0a6c --- /dev/null +++ b/src/test/java/com/bbteam/budgetbuddies/domain/report/service/ReportServiceImplTest.java @@ -0,0 +1,75 @@ +package com.bbteam.budgetbuddies.domain.report.service; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.BDDMockito.*; + +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.bbteam.budgetbuddies.domain.comment.entity.Comment; +import com.bbteam.budgetbuddies.domain.comment.repository.CommentRepository; +import com.bbteam.budgetbuddies.domain.report.convertor.ReportConvertor; +import com.bbteam.budgetbuddies.domain.report.dto.request.ReportRequestDto; +import com.bbteam.budgetbuddies.domain.report.dto.response.ReportResponseDto; +import com.bbteam.budgetbuddies.domain.report.entity.Report; +import com.bbteam.budgetbuddies.domain.report.repository.ReportRepository; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; + +@DisplayName("Report 서비스 테스트의 ") +@ExtendWith(MockitoExtension.class) +class ReportServiceImplTest { + @InjectMocks + ReportServiceImpl reportService; + + @Mock + ReportRepository reportRepository; + @Mock + UserRepository userRepository; + @Mock + CommentRepository commentRepository; + @Spy + ReportConvertor reportConvertor; + + User user; + Comment comment; + + @BeforeEach + void setUp() { + user = Mockito.spy(User.builder().build()); + given(user.getId()).willReturn(-1L); + + comment = Mockito.spy(Comment.builder().build()); + given(comment.getId()).willReturn(-1L); + } + + @Test + void 댓글에_대한_신고_생성_성공() { + // given + ReportRequestDto request = Mockito.spy(new ReportRequestDto()); + given(request.getReason()).willReturn("TEST"); + + // when + when(userRepository.findById(user.getId())).thenReturn(Optional.ofNullable(user)); + when(commentRepository.findById(comment.getId())).thenReturn(Optional.ofNullable(comment)); + when(reportRepository.save(any(Report.class))) + .thenAnswer(invocation -> { + Report spyReport = spy((Report)invocation.getArgument(0)); + when(spyReport.getId()).thenReturn(-1L); + return spyReport; + }); + ReportResponseDto result = reportService.reportComment(request, user.getId(), comment.getId()); + + // then + assertThat(result.getReportId()).isEqualTo(-1L); + } +} \ No newline at end of file From bc36fc29923db50bc585a462b95358ccb945b339 Mon Sep 17 00:00:00 2001 From: MJJ Date: Thu, 3 Oct 2024 20:47:18 +0900 Subject: [PATCH 108/204] =?UTF-8?q?[refactor]=20=EB=B9=84=EA=B5=90=20?= =?UTF-8?q?=EB=B6=84=EC=84=9D=20=EB=A0=88=ED=8F=AC=ED=8A=B8=20API=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ConsumptionGoalServiceImpl.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) 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 b719c984..73e1ba13 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 @@ -680,8 +680,10 @@ public MonthReportResponseDto getMonthReport(Long userId) { // 이번 달 비 Map facialExpressionByCategoryId = updateFacialExpressionByCategoryId(consumeRatioByCategories, remainDaysRatio); + // 표정 변화 로직을 통해 메인 표정 반환 String facialExpression = getMainFacialExpression(facialExpressionByCategoryId); + // 카테고리별 표정 변화 로직을 통해 메인 멘트 반환 String mainComment = getMainComment(dtoList); return consumptionGoalConverter.toMonthReportResponseDto(facialExpression, mainComment); @@ -689,6 +691,8 @@ public MonthReportResponseDto getMonthReport(Long userId) { // 이번 달 비 private Map consumptionRate(List list) { + // 가테고리별 소비 금액을 목표 금액으로 나눈 비율 + Map consumeRatio = new HashMap<>(); for (ConsumeAmountAndGoalAmountDto dto : list) { @@ -762,6 +766,7 @@ private String getMainFacialExpression(Map facialList) { private String getMainComment(List list) { + // 현재 일수 long minDifference = Long.MAX_VALUE; long minCategoryId = -1L; @@ -780,6 +785,7 @@ private String getMainComment(List list) { } } Optional minCategory = categoryRepository.findById(minCategoryId); + if (minCategory.isEmpty()) { throw new IllegalArgumentException("해당 카테고리를 찾을 수 없습니다."); } @@ -809,18 +815,21 @@ private String getMainComment(List list) { public String getConsumptionMention(Long userId) { /** - * 가장 큰 소비를 한 소비 목표 데이터 정보와 가장 큰 목표로 세운 소비 목표데이터를 각각 가져온다. + * 가장 큰 소비를 한 카테고리의 소비 목표 데이터 정보와 가장 큰 목표로 세운 카테고리의 소비 목표 데이터를 각각 가져온다. * 위 데이터들을 가지고 프롬프트 진행 * Gemini AI, Chat GPT */ + // 유저 아이디로 또래 정보 확인 checkPeerInfo(userId, 0, 0, "none"); + // 가장 큰 소비를 한 카테고리의 소비 목표 데이터 가져오기 Optional maxConsumeAmount = consumptionGoalRepository.findMaxConsumeAmountByCategory( peerAgeStart, peerAgeEnd, peerGender, currentMonth); + // 가장 큰 목표로 세운 카테고리의 소비 목표 데이터 가져오기 Optional maxGoalAmount = consumptionGoalRepository.findMaxGoalAmountByCategory( peerAgeStart, peerAgeEnd, @@ -830,10 +839,12 @@ public String getConsumptionMention(Long userId) { throw new IllegalArgumentException("해당 소비목표 데이터를 찾을 수 없습니다."); } + // 유저 이름과 소비 목표 데이터로 카테고리 이름, 소비 금액을 가져 온다. String username = findUserById(userId).getName(); String categoryName = maxConsumeAmount.get().getCategory().getName(); String consumeAmount = String.valueOf(maxConsumeAmount.get().getConsumeAmount()); + // 또래의 상위 소비 금액에 대한 정보로 프롬프트 작성 String firstPrompt = "00은 " + username + ", 가장 큰 소비 카테고리 이름은 " + categoryName + "," + "해당 카테고리 소비금액은" + consumeAmount + "이야"; @@ -841,12 +852,15 @@ public String getConsumptionMention(Long userId) { throw new IllegalArgumentException("해당 소비목표 데이터를 찾을 수 없습니다."); } + // 가장 큰 목표 소비 금액에 대한 정보로 프롬프트 작성 categoryName = maxGoalAmount.get().getCategory().getName(); String goalAmount = String.valueOf(maxGoalAmount.get().getGoalAmount()); + // 또래의 상위 목표 소비 금액에 대한 정보로 프롬프트 작성 String secondPrompt = "가장 큰 목표 소비 카테고리 이름은 " + categoryName + ", 해당 카테고리 목표금액은" + goalAmount + "이야"; + // 프롬프트를 통해 소비 목표에 대한 멘트를 작성 String basePrompt = "소비 분석 관련 멘트를 2개 만들거야 이때," + username + "님 또래는 ~ 이라는 문장으로 시작하고 35자 이내 한 문장씩 만들어줘" + firstPrompt + "와" + secondPrompt + "를 사용하고 두 문장의 구분은 줄바꿈으로 해주고, " From 1637f858af087d08ddd0adbfb76f619c4542598c Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:01:46 +0900 Subject: [PATCH 109/204] =?UTF-8?q?[refactor]=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=9D=B4=EB=8F=99=EC=9C=BC=EB=A1=9C=20=EC=9D=B8?= =?UTF-8?q?=ED=95=B4=20=EC=A0=9C=EA=B1=B0=EB=90=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/AuthenticationController.java | 103 ------------------ .../security/AuthenticationResponse.java | 52 --------- .../global/security/CertificationNumber.java | 23 ---- .../security/CustomAuthenticationToken.java | 29 ----- .../global/security/JwtRequestFilter.java | 76 ------------- .../global/security/JwtUtil.java | 100 ----------------- .../global/security/MyUserDetailsService.java | 38 ------- .../global/security/OtpService.java | 12 -- .../PhoneNumberAuthenticationProvider.java | 54 --------- 9 files changed, 487 deletions(-) delete mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationController.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationResponse.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/CertificationNumber.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/CustomAuthenticationToken.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/JwtRequestFilter.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/JwtUtil.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/MyUserDetailsService.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/OtpService.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/PhoneNumberAuthenticationProvider.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationController.java b/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationController.java deleted file mode 100644 index 9b8115fb..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationController.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.bbteam.budgetbuddies.global.security; - -import com.bbteam.budgetbuddies.apiPayload.ApiResponse; -import com.bbteam.budgetbuddies.domain.user.entity.User; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UserDetails; -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; - -@RestController -@RequestMapping("/auth") -public class AuthenticationController { - - @Autowired - private AuthenticationManager authenticationManager; // 인증을 처리하는 매니저 - - @Autowired - private JwtUtil jwtUtil; // JWT 유틸리티 클래스 - - @Autowired - private MyUserDetailsService userDetailsService; // 사용자 세부 정보 서비스 - - @Value("${jwt.token.access-token.expiration-time}") - private String accessTokenExpirationTime; // 엑세스 토큰 만료 기한 - - @Value("${jwt.token.refresh-token.expiration-time}") - private String refreshTokenExpirationTime; // 리프레시 토큰 만료 기한 - - - @PostMapping("/get-certification-number") - public ApiResponse getCertificationNumber( - @RequestBody AuthenticationRequest.ToReceiveNumber toReceiveNumber - ) { - - return null; - } - - @PostMapping("/login") - public ApiResponse login( - @RequestBody AuthenticationRequest.ToLogin toLogin - ) { - // 사용자가 입력한 자격 증명으로 인증을 시도 - Authentication authenticate = authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword()) - ); - - authenticate.getDetails(); - - // 인증이 성공하면 사용자 세부 정보를 로드 - final User user = userDetailsService.loadUserByPhoneNumber(toLogin.getPhoneNumber()); - - // AccessToken과 RefreshToken 생성 - final String accessToken = jwtUtil.generateToken(user, Long.parseLong(accessTokenExpirationTime)); // 1시간 유효 - final String refreshToken = jwtUtil.generateToken(user, Long.parseLong(refreshTokenExpirationTime)); // 2주 유효 - - // 생성된 토큰을 포함한 응답을 반환 - AuthenticationResponse.SendTokens response = AuthenticationResponse.SendTokens.builder() - .userId(user.getId()) - .phoneNumber(user.getPhoneNumber()) - .accessToken(accessToken) - .refreshToken(refreshToken) - .build(); - - return ApiResponse.onSuccess(response); - } - - @PostMapping("/reissue-access-token") - public ApiResponse reIssueAccessToken( - @RequestBody AuthenticationRequest.ToReissueAccessToken request - ) { - return null; - } - - - // 사용자 로그인 요청을 처리하는 메서드 - @PostMapping("/login") - public AuthenticationResponse createAuthenticationToken( - @RequestBody AuthenticationRequest authenticationRequest - ) throws Exception { - // 사용자가 입력한 자격 증명으로 인증을 시도 - authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword()) - ); - - // 인증이 성공하면 사용자 세부 정보를 로드 - final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername()); - - // AccessToken과 RefreshToken 생성 - final String accessToken = jwtUtil.generateToken(userDetails.getUsername(), Long.parseLong(accessTokenExpirationTime)); // 1시간 유효 - final String refreshToken = jwtUtil.generateToken(userDetails.getUsername(), Long.parseLong(refreshTokenExpirationTime)); // 1년 유효 - - // 생성된 토큰을 포함한 응답을 반환 - - return ; - } -} - diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationResponse.java b/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationResponse.java deleted file mode 100644 index 6e054cf2..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationResponse.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.bbteam.budgetbuddies.global.security; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -public class AuthenticationResponse { - - @Getter - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class SendCertificationNumber { // AuthenticationRequest.ToReceiveNumber와 대응 - // 전화번호, 인증번호 객체 - private String phoneNumber; - - private CertificationNumber certificationNumber; - } - - - @Getter - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class SendTokens { // AuthenticationRequest.ToLogin과 대응 - // 유저 아이디, 전화번호, 액세스 토큰, 리프레시 토큰 - private Long userId; - - private String phoneNumber; - - private String accessToken; - - private String refreshToken; - } - - @Getter - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class SendAccessToken { // AuthenticationRequest.ToReissueAccessToken와 대응 - // 유저 아이디, 전화번호, 액세스 토큰 - private Long userId; - - private String phoneNumber; - - private String accessToken; - - } - - -} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/CertificationNumber.java b/src/main/java/com/bbteam/budgetbuddies/global/security/CertificationNumber.java deleted file mode 100644 index 60dc6222..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/global/security/CertificationNumber.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.bbteam.budgetbuddies.global.security; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -@Getter -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class CertificationNumber { - - private String number; // 인증번호 - - private Integer effectiveTime; // 유효시간 (ex. 3분 = 3) - - private LocalDateTime createdAt; // 생성시각 - - -} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/CustomAuthenticationToken.java b/src/main/java/com/bbteam/budgetbuddies/global/security/CustomAuthenticationToken.java deleted file mode 100644 index 3f07df15..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/global/security/CustomAuthenticationToken.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.bbteam.budgetbuddies.global.security; - -import com.bbteam.budgetbuddies.domain.user.entity.User; -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; - -import java.util.Collection; - -public class CustomAuthenticationToken extends AbstractAuthenticationToken { - private final User user; - private Object credentials; - - public CustomAuthenticationToken(User user, Object credentials, Collection authorities) { - super(authorities); - this.user = user; - this.credentials = credentials; - super.setAuthenticated(true); // This can be set based on the actual authentication logic - } - - @Override - public Object getCredentials() { - return credentials; - } - - @Override - public Object getPrincipal() { - return user; // This returns your custom User object - } -} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/JwtRequestFilter.java b/src/main/java/com/bbteam/budgetbuddies/global/security/JwtRequestFilter.java deleted file mode 100644 index 22a23652..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/global/security/JwtRequestFilter.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.bbteam.budgetbuddies.global.security; - -import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; -import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; -import com.bbteam.budgetbuddies.domain.user.entity.User; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; - -@Component -public class JwtRequestFilter extends OncePerRequestFilter { - - @Autowired - private JwtUtil jwtUtil; - - @Autowired - private MyUserDetailsService userDetailsService; - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) - throws ServletException, IOException { - - final String authorizationHeader = request.getHeader("Authorization"); - - Long userId = null; - String phoneNumber = null; - String accessToken = null; - String refreshToken = null; - - String requestURI = request.getRequestURI(); // 요청한 주소 - - - // Extract Bearer token from Authorization header - if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { - accessToken = authorizationHeader.substring(7); - userId = Long.parseLong(jwtUtil.extractUserId(accessToken)); - phoneNumber = jwtUtil.extractPhoneNumber(accessToken); // Assuming you're extracting phone number from JWT - } - - if (isRefreshTokenApiRequest(requestURI)) { // 헤더에 리프레시 토큰이 담긴 요청이라면 리프레시 토큰을 검증 - - } else { // 헤더에 엑세스 토큰이 담긴 요청이라면 엑세스 토큰을 검증 - // Perform authentication if phone number is present and no authentication is set in the request - if (userId != null && phoneNumber != null && request.getAttribute("authenticatedUser") == null) { - User user = this.userDetailsService.loadUserByPhoneNumber(phoneNumber); // Load your custom User object - if (jwtUtil.validateToken(accessToken, user.getId(), user.getPhoneNumber())) { - // Create a custom Authentication object with your User - CustomAuthenticationToken authentication = new CustomAuthenticationToken(user, null, user.getAuthorities()); - authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - - // Set the custom Authentication in the request attribute - request.setAttribute("authenticatedUser", authentication); - } else { - throw new GeneralException(ErrorStatus._TOKEN_NOT_VALID); - } - } - } - - - // Continue the filter chain - chain.doFilter(request, response); - } - - private boolean isRefreshTokenApiRequest(String requestURI) { - return "/auth/reissue-access-token".equals(requestURI); - -} - diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/JwtUtil.java b/src/main/java/com/bbteam/budgetbuddies/global/security/JwtUtil.java deleted file mode 100644 index 3734d967..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/global/security/JwtUtil.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.bbteam.budgetbuddies.global.security; - -import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; -import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; -import com.bbteam.budgetbuddies.domain.user.entity.User; -import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - -@Service -@RequiredArgsConstructor -public class JwtUtil { - - private final UserRepository userRepository; - - // JWT 토큰을 서명하는 데 사용되는 비밀 키 - @Value("${jwt.token.key}") - private String SECRET_KEY; - - // 토큰에서 사용자 이름(주체, subject)을 추출하는 메서드 - public String extractUserId(String token) { - return extractClaim(token, Claims::getSubject); - } - - // 토큰에서 만료 시간을 추출하는 메서드 - public Date extractExpiration(String token) { - return extractClaim(token, Claims::getExpiration); - } - - public String extractPhoneNumber(String token) { - // JWT 전화번호 클레임 추출 - return (String) extractAllClaims(token).get("phoneNumber"); - } - - public User extractUser(String token) { - - Long userId = Long.parseLong(extractAllClaims(token).getSubject()); - - User user = userRepository.findById(userId) - .orElseThrow(() -> new GeneralException(ErrorStatus._USER_NOT_FOUND)); - - return user; - } - - // 토큰에서 특정 클레임을 추출하는 메서드 - public T extractClaim(String token, Function claimsResolver) { - final Claims claims = extractAllClaims(token); // 모든 클레임을 추출 - return claimsResolver.apply(claims); // 추출된 클레임에서 원하는 정보를 반환 - } - - // 토큰에서 모든 클레임을 추출하는 메서드 - private Claims extractAllClaims(String token) { - return Jwts.parserBuilder() - .setSigningKey(SECRET_KEY.getBytes()) // 서명 키 설정 - .build() - .parseClaimsJws(token) // 토큰 파싱 - .getBody(); // 클레임 반환 - } - - // 토큰이 만료되었는지 확인하는 메서드 - private Boolean isTokenExpired(String token) { - return extractExpiration(token).before(new Date()); - } - - // 사용자 전화번호 만료 시간을 기반으로 토큰을 생성하는 메서드 - public String generateToken(User user, long expirationTime) { - Map claims = new HashMap<>(); // 클레임으로 빈 맵을 사용 - claims.put("phoneNumber", user.getPhoneNumber()); - return createToken(claims, user.getId(), expirationTime); // 토큰 생성 - } - - // 클레임, 주체, 만료 시간을 기반으로 JWT 토큰을 생성하는 메서드 - private String createToken(Map claims, Long userId, long expirationTime) { - return Jwts.builder() - .setClaims(claims) // 클레임 설정 - .setSubject(String.valueOf(userId)) // 주체 설정 (유저 ID) - .setIssuedAt(new Date(System.currentTimeMillis())) // 현재 시간으로 발행 시간 설정 - .setExpiration(new Date(System.currentTimeMillis() + expirationTime)) // 만료 시간 설정 - .signWith(SignatureAlgorithm.HS256, SECRET_KEY.getBytes()) // 서명 알고리즘과 서명 키 설정 - .compact(); // 최종 토큰을 생성하여 반환 - } - - // 토큰이 유효한지 검증하는 메서드 (사용자 이름과 만료 여부 확인) - public Boolean validateToken(String token, Long userId, String phoneNumber) { - final String extractedUserId = extractUserId(token); // 토큰에서 사용자 ID 추출 - final String extractedPhoneNumber = extractPhoneNumber(token); // 토큰에서 사용자 전화번호 추출 - return (extractedUserId.equals(userId) && extractedPhoneNumber.equals(phoneNumber) && !isTokenExpired(token)); // 사용자 이름이 일치하고 토큰이 만료되지 않았는지 확인 - } -} - - diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/MyUserDetailsService.java b/src/main/java/com/bbteam/budgetbuddies/global/security/MyUserDetailsService.java deleted file mode 100644 index 4275ffad..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/global/security/MyUserDetailsService.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.bbteam.budgetbuddies.global.security; - -import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; -import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; -import com.bbteam.budgetbuddies.domain.user.entity.User; -import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; -import lombok.RequiredArgsConstructor; -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.ArrayList; - -@Service -@RequiredArgsConstructor -public class MyUserDetailsService implements UserDetailsService { - - private final UserRepository userRepository; - - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - // 이 예제에서는 고정된 사용자 이름과 비밀번호를 사용 (실제 운영에서는 데이터베이스에서 로드) - - User user = userRepository.findFirstByPhoneNumber(username) - .orElseThrow(() -> new GeneralException(ErrorStatus._USER_NOT_FOUND)); - - - return new org.springframework.security.core.userdetails.User("johndoe", "password", new ArrayList<>()); - } - - public User loadUserByPhoneNumber(String phoneNumber) { - User user = userRepository.findFirstByPhoneNumber(phoneNumber) - .orElseThrow(() -> new GeneralException(ErrorStatus._USER_NOT_FOUND)); - - return user; - } -} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/OtpService.java b/src/main/java/com/bbteam/budgetbuddies/global/security/OtpService.java deleted file mode 100644 index 11edf226..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/global/security/OtpService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.bbteam.budgetbuddies.global.security; - -import org.springframework.stereotype.Service; - -@Service -public class OtpService { - - - public boolean verifyOtp(String phoneNumber, String otp) { - return true; - } -} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/PhoneNumberAuthenticationProvider.java b/src/main/java/com/bbteam/budgetbuddies/global/security/PhoneNumberAuthenticationProvider.java deleted file mode 100644 index 65ce69d6..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/global/security/PhoneNumberAuthenticationProvider.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.bbteam.budgetbuddies.global.security; - -import com.bbteam.budgetbuddies.domain.user.entity.User; -import lombok.AllArgsConstructor; -import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; - -@Component -@RequiredArgsConstructor -public class PhoneNumberAuthenticationProvider implements AuthenticationProvider { - - private final MyUserDetailsService myUserDetailsService; - private final OtpService otpService; // Service to verify OTPs - - /** - * @param myUserDetailsService - * @param otpService - */ - public PhoneNumberOtpAuthenticationProvider(MyUserDetailsService myUserDetailsService, OtpService otpService) { - this.myUserDetailsService = myUserDetailsService; - this.otpService = otpService; - } - - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - String phoneNumber = authentication.getName(); - String otp = authentication.getCredentials().toString(); - - // Verify the OTP using your OTP service - if (!otpService.verifyOtp(phoneNumber, otp)) { - throw new BadCredentialsException("Invalid OTP"); - } - - // Load the user after OTP verification - User user = myUserDetailsService.loadUserByPhoneNumber(phoneNumber); - - // No password check, return a token with the authenticated user - return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>()); - } - - @Override - public boolean supports(Class authentication) { - return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); - } -} From 4d534894bf402d1768897b02b68c242328027e11 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:03:07 +0900 Subject: [PATCH 110/204] =?UTF-8?q?[feat]=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EA=B4=80=EB=A0=A8=20Controller=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/controller/AuthenticationApi.java | 44 ++++++++++ .../controller/AuthenticationController.java | 84 +++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/auth/controller/AuthenticationApi.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/auth/controller/AuthenticationController.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/auth/controller/AuthenticationApi.java b/src/main/java/com/bbteam/budgetbuddies/global/security/auth/controller/AuthenticationApi.java new file mode 100644 index 00000000..96ea50c9 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/auth/controller/AuthenticationApi.java @@ -0,0 +1,44 @@ +package com.bbteam.budgetbuddies.global.security.auth.controller; + +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.global.security.auth.dto.AuthenticationRequest; +import com.bbteam.budgetbuddies.global.security.auth.dto.AuthenticationResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.web.bind.annotation.RequestBody; + +public interface AuthenticationApi { + + @Operation(summary = "OTP 요청 API", description = "전화번호를 입력받아 해당 번호로 OTP를 발송하고, 발송된 OTP 정보를 반환합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH4001", description = "전화번호 형식이 유효하지 않습니다. (예: 01012341234)", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + ApiResponse getOtpNumber( + @RequestBody AuthenticationRequest.ToReceiveNumber request + ); + + @Operation(summary = "로그인 API", description = "전화번호와 OTP를 받아 로그인 처리 후, 인증 토큰을 반환합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "OTP4001", description = "인증번호가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH4001", description = "전화번호 형식이 유효하지 않습니다. (예: 01012341234)", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + ApiResponse login( + @RequestBody AuthenticationRequest.ToLogin request + ); + + @Operation(summary = "액세스 토큰 재발급 API", description = "현재 인증된 사용자로부터 새로운 액세스 토큰을 발급받습니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH4001", description = "전화번호 형식이 유효하지 않습니다. (예: 01012341234)", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + ApiResponse reIssueAccessToken(); +} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/auth/controller/AuthenticationController.java b/src/main/java/com/bbteam/budgetbuddies/global/security/auth/controller/AuthenticationController.java new file mode 100644 index 00000000..2e63dca9 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/auth/controller/AuthenticationController.java @@ -0,0 +1,84 @@ +package com.bbteam.budgetbuddies.global.security.auth.controller; + +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.global.security.auth.dto.AuthenticationRequest; +import com.bbteam.budgetbuddies.global.security.auth.dto.AuthenticationResponse; +import com.bbteam.budgetbuddies.global.security.auth.service.AuthenticationService; +import com.bbteam.budgetbuddies.global.security.otp.OtpNumber; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/auth") // 인증 관련 요청을 처리하는 컨트롤러 +public class AuthenticationController implements AuthenticationApi { + + private final AuthenticationService authenticationService; // 인증 관련 서비스 + + /** + * OTP를 요청하는 엔드포인트. + * 전화번호를 입력받아 해당 번호로 OTP를 발송하고, 발송된 OTP 정보를 반환합니다. + * + * @param request OTP 요청에 필요한 전화번호 정보 + * @return 성공 시 전화번호와 생성된 OTP를 포함한 ApiResponse + */ + @PostMapping("/get-otp") + @Override + public ApiResponse getOtpNumber( + @Valid @RequestBody AuthenticationRequest.ToReceiveNumber request + ) { + String phoneNumber = request.getPhoneNumber(); // 요청에서 전화번호 추출 + OtpNumber generatedOtp = authenticationService.generateOtp(phoneNumber); // OTP 생성 + AuthenticationResponse.SendOtpNumber response = AuthenticationResponse.SendOtpNumber.builder() + .phoneNumber(phoneNumber) // 전화번호 설정 + .otpNumber(generatedOtp) // 생성된 OTP 정보 설정 + .build(); + return ApiResponse.onSuccess(response); // 성공 응답 반환 + } + + /** + * 로그인 요청을 처리하는 엔드포인트. + * 전화번호와 OTP를 받아 로그인 처리 후, 인증 토큰을 반환합니다. + * + * @param request 로그인 요청에 필요한 전화번호 및 OTP 정보 + * @return 성공 시 인증 토큰 정보를 포함한 ApiResponse + */ + @PostMapping("/login") + @Override + public ApiResponse login( + @Valid @RequestBody AuthenticationRequest.ToLogin request + ) { + AuthenticationResponse.SendTokens response = authenticationService.login( + request.getPhoneNumber(), // 전화번호 추출 + request.getOtpNumber() // OTP 추출 + ); + return ApiResponse.onSuccess(response); // 성공 응답 반환 + } + + /** + * 액세스 토큰 재발급 요청을 처리하는 엔드포인트. + * 현재 인증된 사용자로부터 새로운 액세스 토큰을 발급받습니다. + * + * @return 성공 시 새로 발급된 액세스 토큰 정보를 포함한 ApiResponse + */ + @GetMapping("/reissue-access-token") + @Override + public ApiResponse reIssueAccessToken() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // 현재 인증 정보 가져오기 + User user = null; + + // 인증된 사용자 정보가 있는지 확인 + if (authentication != null && authentication.isAuthenticated()) { + user = (User) authentication.getPrincipal(); // 인증된 사용자 추출 + } + + // 새로운 액세스 토큰 발급 + AuthenticationResponse.SendAccessToken response = authenticationService.reIssueAccessToken(user); + return ApiResponse.onSuccess(response); // 성공 응답 반환 + } +} + From dda245dcee39aa7346c6921a92f5166d15df3618 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:03:40 +0900 Subject: [PATCH 111/204] =?UTF-8?q?[feat]=20Cool-SMS=20SDK=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index de8eaa39..5ae542b7 100644 --- a/build.gradle +++ b/build.gradle @@ -30,6 +30,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-security' // security implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' //Swagger + + implementation 'net.nurigo:sdk:4.2.7' // 문자메시지 대행 서비스 + compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' From 2ad0640122c2333c79dfffee1b37456345525eb6 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:05:31 +0900 Subject: [PATCH 112/204] =?UTF-8?q?[feat]=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EA=B4=80=EB=A0=A8=20DTO=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/AuthenticationRequest.java | 41 ------------ .../auth/dto/AuthenticationRequest.java | 34 ++++++++++ .../auth/dto/AuthenticationResponse.java | 62 +++++++++++++++++++ 3 files changed, 96 insertions(+), 41 deletions(-) delete mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationRequest.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/auth/dto/AuthenticationRequest.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/auth/dto/AuthenticationResponse.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationRequest.java b/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationRequest.java deleted file mode 100644 index f5c31dd3..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/global/security/AuthenticationRequest.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.bbteam.budgetbuddies.global.security; - - -import lombok.*; - -public class AuthenticationRequest { - - @Builder - @AllArgsConstructor - @NoArgsConstructor - @Getter - public static class ToReceiveNumber { - // 전화번호 - private String phoneNumber; - - } - - @Builder - @AllArgsConstructor - @NoArgsConstructor - @Getter - public static class ToLogin { - // 전화번호, 인증번호 - private String phoneNumber; - - private String certificationNumber; - } - - @Builder - @AllArgsConstructor - @NoArgsConstructor - @Getter - public static class ToReissueAccessToken { - // 전화번호, 리프레시 토큰 - private String phoneNumber; - - private String RefreshToken; - } - - -} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/auth/dto/AuthenticationRequest.java b/src/main/java/com/bbteam/budgetbuddies/global/security/auth/dto/AuthenticationRequest.java new file mode 100644 index 00000000..11fb3021 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/auth/dto/AuthenticationRequest.java @@ -0,0 +1,34 @@ +package com.bbteam.budgetbuddies.global.security.auth.dto; + + +import com.bbteam.budgetbuddies.global.security.auth.validation.ValidPhoneNumber; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +public class AuthenticationRequest { + + @Builder + @AllArgsConstructor + @NoArgsConstructor + @Getter + public static class ToReceiveNumber { + @ValidPhoneNumber // 전화번호 유효성 검증 + private String phoneNumber; + } + + @Builder + @AllArgsConstructor + @NoArgsConstructor + @Getter + public static class ToLogin { + // 전화번호, 인증번호 + @ValidPhoneNumber // 전화번호 유효성 검증 + private String phoneNumber; + + private String otpNumber; + } + + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/auth/dto/AuthenticationResponse.java b/src/main/java/com/bbteam/budgetbuddies/global/security/auth/dto/AuthenticationResponse.java new file mode 100644 index 00000000..b7346f3d --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/auth/dto/AuthenticationResponse.java @@ -0,0 +1,62 @@ +package com.bbteam.budgetbuddies.global.security.auth.dto; + +import com.bbteam.budgetbuddies.global.security.otp.OtpNumber; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +public class AuthenticationResponse { + + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Schema(description = "OTP 요청에 대한 응답 DTO") + public static class SendOtpNumber { // AuthenticationRequest.ToReceiveNumber와 대응 + + @Schema(description = "전화번호", example = "01012341234") + private String phoneNumber; // 전화번호 + + @Schema(description = "생성된 OTP 정보") + private OtpNumber otpNumber; // 인증번호 객체 + } + + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Schema(description = "로그인 요청에 대한 응답 DTO") + public static class SendTokens { // AuthenticationRequest.ToLogin과 대응 + + @Schema(description = "유저 ID") + private Long userId; // 유저 아이디 + + @Schema(description = "전화번호", example = "01012341234") + private String phoneNumber; // 전화번호 + + @Schema(description = "액세스 토큰") + private String accessToken; // 액세스 토큰 + + @Schema(description = "리프레시 토큰") + private String refreshToken; // 리프레시 토큰 + } + + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Schema(description = "액세스 토큰 재발급 요청에 대한 응답 DTO") + public static class SendAccessToken { + + @Schema(description = "유저 ID") + private Long userId; // 유저 아이디 + + @Schema(description = "전화번호", example = "01012341234") + private String phoneNumber; // 전화번호 + + @Schema(description = "액세스 토큰") + private String accessToken; // 액세스 토큰 + } +} \ No newline at end of file From 824496c50b5e30c1c1e0409567e17af9e30dc568 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:05:59 +0900 Subject: [PATCH 113/204] =?UTF-8?q?[feat]=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EA=B4=80=EB=A0=A8=20Service=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EB=AF=B8=EA=B5=AC=ED=98=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/service/AuthenticationService.java | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/auth/service/AuthenticationService.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/auth/service/AuthenticationService.java b/src/main/java/com/bbteam/budgetbuddies/global/security/auth/service/AuthenticationService.java new file mode 100644 index 00000000..974f4b73 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/auth/service/AuthenticationService.java @@ -0,0 +1,84 @@ +package com.bbteam.budgetbuddies.global.security.auth.service; + +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import com.bbteam.budgetbuddies.global.security.auth.dto.AuthenticationResponse; +import com.bbteam.budgetbuddies.global.security.jwt.JwtUtil; +import com.bbteam.budgetbuddies.global.security.otp.OtpNumber; +import com.bbteam.budgetbuddies.global.security.otp.OtpService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class AuthenticationService { + + private final JwtUtil jwtUtil; // JWT 관련 유틸리티 클래스 + private final OtpService otpService; // OTP 관련 서비스 클래스 + private final UserRepository userRepository; // 사용자 정보 저장소 + + /** + * OTP를 생성하여 반환하는 메서드. + * @param phoneNumber 전화번호 + * @return 생성된 OTP 정보 + */ + public OtpNumber generateOtp(String phoneNumber) { + // 전화번호를 사용하여 OTP를 생성하고 반환합니다. + return otpService.generateOtp(phoneNumber); + } + + /** + * 로그인 처리 메서드. + * 전화번호와 OTP를 검증한 후, 인증이 성공하면 JWT 토큰을 발급합니다. + * + * @param phoneNumber 전화번호 + * @param otpNumber 인증번호 + * @return 발급된 JWT 액세스 토큰과 리프레시 토큰 정보 + */ + public AuthenticationResponse.SendTokens login(String phoneNumber, String otpNumber) { + // 입력된 OTP가 유효한지 검증 + if (!otpService.validateOtp(phoneNumber, otpNumber)) { + throw new GeneralException(ErrorStatus._OTP_NOT_VALID); // 유효하지 않은 OTP일 경우 예외 발생 + } + + // 전화번호로 사용자를 로드, 존재하지 않으면 새 사용자 생성 + final User user = userRepository.findFirstByPhoneNumber(phoneNumber) + .orElseGet(() -> userRepository.save(User.builder() // 사용자 정보가 없으면 새로 생성 + .phoneNumber(phoneNumber) + .build() + )); + + // JWT 액세스 토큰 발급 + final String accessToken = jwtUtil.generateAccessToken(user); + // JWT 리프레시 토큰 발급 + final String refreshToken = jwtUtil.generateRefreshToken(user); + + // 발급된 토큰 정보를 포함한 응답 객체를 반환 + return AuthenticationResponse.SendTokens.builder() + .userId(user.getId()) // 사용자 ID + .phoneNumber(user.getPhoneNumber()) // 전화번호 + .accessToken(accessToken) // 액세스 토큰 + .refreshToken(refreshToken) // 리프레시 토큰 + .build(); + } + + /** + * 새로운 액세스 토큰을 재발급하는 메서드. + * + * @param user 사용자 정보 + * @return 발급된 새로운 액세스 토큰 정보 + */ + public AuthenticationResponse.SendAccessToken reIssueAccessToken(User user) { + // 새로운 액세스 토큰 발급 + String newAccessToken = jwtUtil.generateAccessToken(user); + + // 발급된 새로운 토큰 정보를 포함한 응답 객체를 반환 + return AuthenticationResponse.SendAccessToken.builder() + .userId(user.getId()) // 사용자 ID + .phoneNumber(user.getPhoneNumber()) // 전화번호 + .accessToken(newAccessToken) // 새로운 액세스 토큰 + .build(); + } +} \ No newline at end of file From 182a4c188b8a13e209e370e57e9b699eba5dbc5d Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:07:17 +0900 Subject: [PATCH 114/204] =?UTF-8?q?[feat]=20JWT=20=EC=9C=A0=ED=8B=B8=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/jwt/JwtUtil.java | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/jwt/JwtUtil.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/jwt/JwtUtil.java b/src/main/java/com/bbteam/budgetbuddies/global/security/jwt/JwtUtil.java new file mode 100644 index 00000000..4c3fe9c8 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/jwt/JwtUtil.java @@ -0,0 +1,163 @@ +package com.bbteam.budgetbuddies.global.security.jwt; + +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.global.security.refreshtoken.RefreshTokenRepository; +import com.bbteam.budgetbuddies.global.security.refreshtoken.RefreshTokenService; +import com.bbteam.budgetbuddies.global.security.utils.MyUserDetailsService; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +@Service +@RequiredArgsConstructor +@Transactional +@Slf4j +public class JwtUtil { + + private final MyUserDetailsService myUserDetailsService; + + private final RefreshTokenRepository refreshTokenRepository; + + private final RefreshTokenService refreshTokenService; + + // JWT 토큰을 서명하는 데 사용되는 비밀 키 + @Value("${jwt.token.key}") + private String SECRET_KEY; + + @Value("${jwt.token.access-token.expiration-time}") + private Long accessTokenExpirationTime; // 엑세스 토큰 만료 기한 + + @Value("${jwt.token.refresh-token.expiration-time}") + private Long refreshTokenExpirationTime; // 리프레시 토큰 만료 기한 + + // 토큰에서 사용자 이름(주체, subject)을 추출하는 메서드 + public String extractUserId(String token) { + return extractClaim(token, Claims::getSubject); + } + + // 토큰에서 만료 시간을 추출하는 메서드 + public Date extractExpiration(String token) { + return extractClaim(token, Claims::getExpiration); + } + + public String extractPhoneNumber(String token) { + // JWT 전화번호 클레임 추출 + return (String) extractAllClaims(token).get("phoneNumber"); + } + + public User extractUser(String token) { + + Long userId = Long.parseLong(extractAllClaims(token).getSubject()); + + User user = myUserDetailsService.loadUserByUserId(userId); + + return user; + } + + // 토큰에서 특정 클레임을 추출하는 메서드 + public T extractClaim(String token, Function claimsResolver) { + final Claims claims = extractAllClaims(token); // 모든 클레임을 추출 + return claimsResolver.apply(claims); // 추출된 클레임에서 원하는 정보를 반환 + } + + // 토큰에서 모든 클레임을 추출하는 메서드 + private Claims extractAllClaims(String token) { + return Jwts.parserBuilder() + .setSigningKey(SECRET_KEY.getBytes()) // 서명 키 설정 + .build() + .parseClaimsJws(token) // 토큰 파싱 + .getBody(); // 클레임 반환 + } + + // 토큰이 만료되었는지 확인하는 메서드 + private void isTokenExpired(String token) throws GeneralException { + if (extractExpiration(token).before(new Date())) { + throw new GeneralException(ErrorStatus._TOKEN_EXPIRED); + } + } + + // 사용자 전화번호를 기반으로 엑세스 토큰을 생성하는 메서드 + public String generateAccessToken(User user) { + Map claims = new HashMap<>(); // 클레임으로 빈 맵을 사용 + claims.put("phoneNumber", user.getPhoneNumber()); + + String token = createToken(claims, user.getId(), accessTokenExpirationTime);// 토큰 생성: 1시간 유효 + + log.info("access token: {}", token); + + return token; + } + + // 사용자 전화번호를 기반으로 리프레시 토큰을 생성하는 메서드 + public String generateRefreshToken(User user) { + Map claims = new HashMap<>(); // 클레임으로 빈 맵을 사용 + claims.put("phoneNumber", user.getPhoneNumber()); + + String token = createToken(claims, user.getId(), refreshTokenExpirationTime);// 토큰 생성: 1년 유효 + + log.info("refresh token: {}", token); + + refreshTokenService.saveOrUpdateRefreshToken(user, token); + + return token; + } + + // 클레임, 주체, 만료 시간을 기반으로 JWT 토큰을 생성하는 메서드 + private String createToken(Map claims, Long userId, Long expirationTime) { + + return Jwts.builder() + .setClaims(claims) // 클레임 설정 + .setSubject(String.valueOf(userId)) // 주체 설정 (유저 ID) + .setIssuedAt(new Date(System.currentTimeMillis())) // 현재 시간으로 발행 시간 설정 + .setExpiration(new Date(System.currentTimeMillis() + expirationTime)) // 만료 시간 설정 + .signWith(SignatureAlgorithm.HS256, SECRET_KEY.getBytes()) // 서명 알고리즘과 서명 키 설정 + .compact(); // 최종 토큰을 생성하여 반환 + } + + // 엑세스 토큰이 유효한지 검증하는 메서드 (사용자 이름과 만료 여부 확인) + public Boolean validateAccessToken(String token, Long userId, String phoneNumber) throws GeneralException { + final String extractedUserId = extractUserId(token); // 토큰에서 사용자 ID 추출 + final String extractedPhoneNumber = extractPhoneNumber(token); // 토큰에서 사용자 전화번호 추출 + + log.info("extractedUserId: {}", extractedUserId); + log.info("extractedPhoneNumber: {}", extractedPhoneNumber); + + isTokenExpired(token); // 만료 확인, 만료된 경우 예외 발생 + + return ( + extractedUserId.equals(String.valueOf(userId)) // 사용자 ID 일치 여부 검증 + && extractedPhoneNumber.equals(phoneNumber) // 사용자 전화번호 일치 여부 검증 + ); + } + + // 리프레시 토큰이 유효한지 검증하는 메서드 (사용자 이름과 만료 여부 확인 및 DB 값과 비교) + public Boolean validateRefreshToken(String token, Long userId, String phoneNumber) { + final String extractedUserId = extractUserId(token); // 토큰에서 사용자 ID 추출 + final String extractedPhoneNumber = extractPhoneNumber(token); // 토큰에서 사용자 전화번호 추출 + + User user = myUserDetailsService.loadUserByUserId(userId); + + isTokenExpired(token); // 만료 확인, 만료된 경우 예외 발생 + + // 리프레시 토큰 검증 + return ( + refreshTokenService.validateRefreshToken(user, token) && + extractedUserId.equals(String.valueOf(userId)) && + extractedPhoneNumber.equals(phoneNumber) + ); + } +} + + From dd87375ec8c6506ba32a87c6620abd8072661675 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:07:56 +0900 Subject: [PATCH 115/204] =?UTF-8?q?[feat]=20JWT=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/jwt/JwtRequestFilter.java | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/jwt/JwtRequestFilter.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/jwt/JwtRequestFilter.java b/src/main/java/com/bbteam/budgetbuddies/global/security/jwt/JwtRequestFilter.java new file mode 100644 index 00000000..882f14b6 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/jwt/JwtRequestFilter.java @@ -0,0 +1,148 @@ +package com.bbteam.budgetbuddies.global.security.jwt; + +import com.bbteam.budgetbuddies.apiPayload.code.ErrorReasonDto; +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.global.security.utils.CustomAuthenticationToken; +import com.bbteam.budgetbuddies.global.security.utils.MyUserDetailsService; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.jsonwebtoken.ExpiredJwtException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.List; + + +@Slf4j +@RequiredArgsConstructor +@Component +public class JwtRequestFilter extends OncePerRequestFilter { + + private final JwtUtil jwtUtil; + + private final MyUserDetailsService myUserDetailsService; + + private final String BEARER = "Bearer "; + + // Swagger 및 인증 관련 요청 주소 목록 + private final List swaggers = List.of( + "/swagger-ui", + "/v3/api-docs" + ); + private final List auth = List.of( + "/auth/get-otp", + "/auth/login" + ); + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException { + String requestURI = request.getRequestURI(); // 요청한 URI 확인 (리프레시 토큰 요청 여부 확인) + + // Swagger 또는 인증 관련 URI일 경우 필터 통과 + if (swaggers.stream().anyMatch(requestURI::startsWith) || auth.stream().anyMatch(requestURI::startsWith)) { + chain.doFilter(request, response); // 필터 통과 + return; // 메소드 종료 + } + + final String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION); // Authorization 헤더 추출 + Long userId = null; + String phoneNumber = null; + String token = null; + + // Bearer 토큰이 포함된 Authorization 헤더에서 토큰 추출 + if (authorizationHeader != null && authorizationHeader.startsWith(BEARER)) { + token = authorizationHeader.substring(7); // "Bearer " 이후의 부분을 추출 + userId = Long.parseLong(jwtUtil.extractUserId(token)); // JWT에서 사용자 ID 추출 + phoneNumber = jwtUtil.extractPhoneNumber(token); // JWT에서 전화번호 추출 + log.info("request header's token: {}", token); + log.info("request header's userId: {}", userId); + log.info("request header's phoneNumber: {}", phoneNumber); + } + + try { + // 토큰이 없는 경우 예외 발생 + if (token == null) { + throw new GeneralException(ErrorStatus._TOKEN_NOT_FOUND); + } + + // 인증 로직 + if (userId != null && phoneNumber != null && request.getAttribute("authenticatedUser") == null) { + if (isRefreshTokenApiRequest(requestURI)) { + authenticateToken(request, token, phoneNumber, true); // 리프레시 토큰 검증 + } else { + authenticateToken(request, token, phoneNumber, false); // 액세스 토큰 검증 + } + } + + // 필터 체인을 계속해서 실행 + chain.doFilter(request, response); + + } catch (ExpiredJwtException e) { + // 토큰 만료 시 GeneralException 발생 + throw new GeneralException(ErrorStatus._TOKEN_EXPIRED); + } catch (GeneralException ex) { + // GeneralException 처리 및 클라이언트에 에러 응답 전송 + ErrorReasonDto errorReason = ex.getErrorReasonHttpStatus(); + response.setStatus(errorReason.getHttpStatus().value()); // HTTP 상태 코드 설정 + response.setContentType("application/json; charset=UTF-8"); // JSON 형식 및 UTF-8 설정 + response.setCharacterEncoding("UTF-8"); // 응답 인코딩 설정 + response.getWriter().write(new ObjectMapper().writeValueAsString(errorReason)); // JSON 형식으로 에러 응답 전송 + } + } + + // 리프레시 토큰 API 요청 여부 확인 + private boolean isRefreshTokenApiRequest(String requestURI) { + return "/auth/reissue-access-token".equals(requestURI); + } + + /** + * 공통 인증 로직: 토큰 검증 및 인증 생성 + * + * @param request HttpServletRequest 객체 + * @param token JWT 토큰 + * @param phoneNumber JWT에서 추출한 전화번호 + * @param isRefreshToken 리프레시 토큰 여부 + */ + private void authenticateToken(HttpServletRequest request, String token, String phoneNumber, boolean isRefreshToken) { + // 사용자 정보 로드 + User user = this.myUserDetailsService.loadUserByPhoneNumber(phoneNumber); + + // 토큰 검증: 리프레시 토큰이면 리프레시 토큰 검증, 액세스 토큰이면 액세스 토큰 검증 + boolean isValidToken = isRefreshToken + ? jwtUtil.validateRefreshToken(token, user.getId(), user.getPhoneNumber()) // 리프레시 토큰 검증 + : jwtUtil.validateAccessToken(token, user.getId(), user.getPhoneNumber()); // 액세스 토큰 검증 + + if (isValidToken) { + // 인증 객체 생성 + CustomAuthenticationToken authentication = new CustomAuthenticationToken(user, null, user.getAuthorities()); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + + // SecurityContext에 인증 정보 저장 + SecurityContextHolder.getContext().setAuthentication(authentication); + + /** + * <사용 예시> + * Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + * if (authentication != null && authentication.isAuthenticated()) { + * User user = (User) authentication.getPrincipal(); + * // User 객체를 통해 필요한 정보를 사용 + * } + */ + } else { + // 토큰이 유효하지 않을 경우 예외 발생 + throw new GeneralException(ErrorStatus._TOKEN_NOT_VALID); + } + } +} \ No newline at end of file From 197b7f12ab786e2b8e98e69c48f7ee4b7abffbda Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:08:22 +0900 Subject: [PATCH 116/204] =?UTF-8?q?[feat]=20JWT=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/jwt/JwtExceptionFilter.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/jwt/JwtExceptionFilter.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/jwt/JwtExceptionFilter.java b/src/main/java/com/bbteam/budgetbuddies/global/security/jwt/JwtExceptionFilter.java new file mode 100644 index 00000000..efde7d61 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/jwt/JwtExceptionFilter.java @@ -0,0 +1,78 @@ +package com.bbteam.budgetbuddies.global.security.jwt; + +import com.bbteam.budgetbuddies.apiPayload.code.ErrorReasonDto; +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.jsonwebtoken.JwtException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +@Slf4j +@RequiredArgsConstructor +@Component +public class JwtExceptionFilter extends OncePerRequestFilter { + + // JWT 검증 중 발생한 예외를 처리하는 필터 메소드 + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { + try { + chain.doFilter(request, response); + } catch (JwtException ex) { + String message = ex.getMessage(); + + // 토큰이 없는 경우 + if(ErrorStatus._TOKEN_NOT_FOUND.getMessage().equals(message)) { + log.info("error message: {}", message); + setResponse(response, ErrorStatus._TOKEN_NOT_FOUND); + } + // 토큰 만료된 경우 + else if(message.startsWith("JWT expired at")) { + log.info("error message: {}", message); + setResponse(response, ErrorStatus._TOKEN_EXPIRED); + } + // 잘못된 형식의 토큰인 경우 (페이로드 혹은 시그니처 불일치) + else if(message.startsWith("JWT signature does not match")) { + log.info("error message: {}", message); + setResponse(response, ErrorStatus._TOKEN_PAYLOAD_OR_SIGNATURE_NOT_VALID); + } + // 잘못된 형식의 토큰인 경우 (잘못된 헤더 정보) + else if(message.startsWith("Malformed JWT JSON:")) { + log.info("error message: {}", message); + setResponse(response, ErrorStatus._TOKEN_HEADER_NOT_VALID); + } + // 그 외: 유효하지 않는 토큰 + else { + log.info("error message: {}", message); + setResponse(response, ErrorStatus._TOKEN_NOT_VALID); + } + + } + } + + // 에러 응답을 설정하는 메소드 + private void setResponse(HttpServletResponse response, ErrorStatus errorStatus) throws RuntimeException, IOException { + // ErrorReasonDto를 빌더로 생성하여 에러 정보 설정 + ErrorReasonDto errorReason = ErrorReasonDto.builder() + .message(errorStatus.getMessage()) // 에러 메시지 + .code(errorStatus.getCode()) // 에러 코드 + .isSuccess(false) // 성공 여부는 false + .httpStatus(errorStatus.getHttpStatus()) // HTTP 상태 코드 + .build(); + + // 응답 설정 + response.setStatus(errorReason.getHttpStatus().value()); // HTTP 상태 코드 설정 + response.setContentType("application/json; charset=UTF-8"); // JSON 형식 및 UTF-8 설정 + response.setCharacterEncoding("UTF-8"); // 응답 인코딩 설정 + + // JSON 형식으로 에러 정보를 클라이언트에 전송 + response.getWriter().write(new ObjectMapper().writeValueAsString(errorReason)); + } +} \ No newline at end of file From cc62470fa04f00cd214b0ff199f8ef2a8ea444e2 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:09:02 +0900 Subject: [PATCH 117/204] =?UTF-8?q?[feat]=20RefreshToken=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=84=9C=EB=B9=84=EC=8A=A4,=20=EB=A0=88=ED=8F=AC?= =?UTF-8?q?=EC=A7=80=ED=86=A0=EB=A6=AC,=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(DB=EC=97=90=20=EC=A0=80=EC=9E=A5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/refreshtoken/RefreshToken.java | 26 +++++++ .../refreshtoken/RefreshTokenRepository.java | 15 ++++ .../refreshtoken/RefreshTokenService.java | 72 +++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshToken.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshTokenRepository.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshTokenService.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshToken.java b/src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshToken.java new file mode 100644 index 00000000..47f31a98 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshToken.java @@ -0,0 +1,26 @@ +package com.bbteam.budgetbuddies.global.security.refreshtoken; + +import com.bbteam.budgetbuddies.common.BaseEntity; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import lombok.*; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Entity +public class RefreshToken extends BaseEntity { + + @OneToOne + @JoinColumn(name = "user_id", referencedColumnName = "id") + private User user; + + @Setter + @Column(nullable = false, unique = true) + private String token; + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshTokenRepository.java b/src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshTokenRepository.java new file mode 100644 index 00000000..cda7f70b --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshTokenRepository.java @@ -0,0 +1,15 @@ +package com.bbteam.budgetbuddies.global.security.refreshtoken; + +import com.bbteam.budgetbuddies.domain.user.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface RefreshTokenRepository extends JpaRepository { + + Optional findByToken(String token); + + Optional findByUser(User user); + + void deleteByUser(User user); +} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshTokenService.java b/src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshTokenService.java new file mode 100644 index 00000000..a1dfa809 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshTokenService.java @@ -0,0 +1,72 @@ +package com.bbteam.budgetbuddies.global.security.refreshtoken; + +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class RefreshTokenService { + + private final RefreshTokenRepository refreshTokenRepository; + + /** + * 리프레시 토큰을 저장하거나 갱신하는 메서드. + * 해당 사용자의 리프레시 토큰이 이미 존재하면 새 토큰으로 갱신하고, + * 존재하지 않으면 새 리프레시 토큰을 생성하여 저장합니다. + * + * @param user 사용자 정보 + * @param newToken 새로운 리프레시 토큰 + */ + public void saveOrUpdateRefreshToken(User user, String newToken) { + // 해당 사용자의 리프레시 토큰이 이미 존재하는지 확인 + Optional existingToken = refreshTokenRepository.findByUser(user); + + // 기존 토큰이 있으면 갱신, 없으면 새로 저장 + if (existingToken.isPresent()) { + // 기존 리프레시 토큰을 새로운 토큰으로 갱신 + existingToken.get().setToken(newToken); + refreshTokenRepository.save(existingToken.get()); + } else { + // 새 리프레시 토큰 생성 및 저장 + RefreshToken refreshToken = RefreshToken.builder() + .user(user) + .token(newToken) + .build(); + refreshTokenRepository.save(refreshToken); + } + } + + /** + * 리프레시 토큰이 해당 사용자와 일치하는지 검증하는 메서드. + * 사용자와 연관된 리프레시 토큰이 없으면 예외를 발생시키고, + * 있으면 토큰이 일치하는지 여부를 반환합니다. + * + * @param user 해당 사용자 + * @param token 리프레시 토큰 문자열 + * @return 토큰이 사용자와 일치하는지 여부 + */ + public boolean validateRefreshToken(User user, String token) { + // 사용자와 연결된 리프레시 토큰이 없으면 예외 발생 + RefreshToken refreshToken = refreshTokenRepository.findByUser(user) + .orElseThrow(() -> new GeneralException(ErrorStatus._TOKEN_NOT_FOUND)); + + // 리프레시 토큰 문자열이 일치하는지 검증 + return refreshToken.getToken().equals(token); + } + + /** + * 리프레시 토큰을 삭제하는 메서드. + * 사용자 정보를 기반으로 리프레시 토큰을 삭제합니다. + * + * @param user 사용자 정보 + */ + public void deleteRefreshToken(User user) { + // 사용자와 연관된 리프레시 토큰 삭제 + refreshTokenRepository.deleteByUser(user); + } +} \ No newline at end of file From 7e1849b4289732246a0b4849139172e9ddff9c32 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:09:51 +0900 Subject: [PATCH 118/204] =?UTF-8?q?[feat]=20OTP=20Service=20=EB=B0=8F=20DT?= =?UTF-8?q?O=20=EA=B5=AC=ED=98=84=20(=EC=9D=B8=EC=A6=9D=EB=B2=88=ED=98=B8?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=A7=81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/otp/OtpNumber.java | 20 +++ .../global/security/otp/OtpService.java | 118 ++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpNumber.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpService.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpNumber.java b/src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpNumber.java new file mode 100644 index 00000000..9f13963e --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpNumber.java @@ -0,0 +1,20 @@ +package com.bbteam.budgetbuddies.global.security.otp; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class OtpNumber { + + private String otp; // 인증번호 + + private LocalDateTime expirationTime; // 만료시각 + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpService.java b/src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpService.java new file mode 100644 index 00000000..62413937 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpService.java @@ -0,0 +1,118 @@ +package com.bbteam.budgetbuddies.global.security.otp; + + +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import net.nurigo.sdk.NurigoApp; +import net.nurigo.sdk.message.model.Message; +import net.nurigo.sdk.message.request.SingleMessageSendingRequest; +import net.nurigo.sdk.message.response.SingleMessageSentResponse; +import net.nurigo.sdk.message.service.DefaultMessageService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Service +@Slf4j +public class OtpService { + + // OTP를 저장할 캐시. ConcurrentHashMap을 사용하여 다중 스레드 환경에서 안전하게 OTP를 저장 + private final Map otpCache = new ConcurrentHashMap<>(); + + // Cool-SMS 메시지 서비스를 위한 변수 + private DefaultMessageService messageService; + + // OTP 인증번호 유효 시간 (초 단위): 3분 + private static final long OTP_VALID_DURATION = 3 * 60; + + @Value("${cool-sms.api-key}") + private String apiKey; + + @Value("${cool-sms.api-secret}") + private String apiSecret; + + @Value("${cool-sms.sender-phone-number}") + private String senderPhoneNumber; + + // 초기화 메소드: Cool-SMS 메시지 서비스 인스턴스를 초기화 + @PostConstruct + public void init() { + this.messageService = NurigoApp.INSTANCE.initialize(apiKey, apiSecret, "https://api.coolsms.co.kr"); + } + + /** + * OTP를 생성하고 캐시에 저장하는 메소드 + * + * @param phoneNumber 사용자의 전화번호 + * @return 생성된 OTP 정보가 담긴 OtpNumber 객체 + */ + public OtpNumber generateOtp(String phoneNumber) { + // 6자리 랜덤 OTP 번호 생성 + String randomNumber = String.valueOf((int)(Math.random() * 900000) + 100000); + + // OTP 만료 시간 설정 (현재 시간 기준으로 3분 뒤) + LocalDateTime expirationTime = LocalDateTime.now().plusSeconds(OTP_VALID_DURATION); + + // OTP 정보를 담은 객체 생성 + OtpNumber otp = OtpNumber.builder() + .otp(randomNumber) + .expirationTime(expirationTime) + .build(); + + // 생성된 OTP를 전화번호를 키로 하여 캐시에 저장 + otpCache.put(phoneNumber, otp); + + // 실제 메시지 전송은 주석 처리되어 있음 (실제 배포 시 주석 해제) + // sendMessage(phoneNumber, otp); + + return otp; + } + + /** + * OTP 문자 메시지를 전송하는 메소드 + * + * @param phoneNumber 수신자의 전화번호 + * @param otp 생성된 OTP 객체 + */ + public void sendMessage(String phoneNumber, OtpNumber otp) { + Message message = new Message(); + + // 발신 번호 및 수신 번호 설정 (01012345678 형태로 입력해야 함) + message.setFrom(senderPhoneNumber); // 발신 번호 설정 + message.setTo(phoneNumber); // 수신 번호 설정 + + // 메시지 내용 설정 (한글 45자 이하일 경우 자동으로 SMS로 전송) + message.setText("[빈주머니즈]\n인증번호: " + otp.getOtp()); + + // 메시지 전송 요청 및 응답 로그 출력 + SingleMessageSentResponse response = this.messageService.sendOne(new SingleMessageSendingRequest(message)); + log.info("전송된 메시지: {}", response); + } + + /** + * OTP가 유효한지 검증하는 메소드 + * + * @param phoneNumber 사용자의 전화번호 + * @param otp 사용자가 입력한 OTP + * @return OTP가 유효하고 만료되지 않았으면 true, 그렇지 않으면 false + */ + public boolean validateOtp(String phoneNumber, String otp) { + // 캐시에서 전화번호에 해당하는 OTP 가져오기 + OtpNumber otpEntry = otpCache.get(phoneNumber); + + if (otpEntry == null) { + return false; // 캐시에 OTP가 없으면 false 반환 + } + + // OTP가 일치하고 유효 기간 내에 있는지 확인 + if (otpEntry.getOtp().equals(otp) && otpEntry.getExpirationTime().isAfter(LocalDateTime.now())) { + otpCache.remove(phoneNumber); // OTP가 유효하면 사용 후 제거 + return true; + } + + return false; // OTP가 일치하지 않거나 만료된 경우 false 반환 + } +} \ No newline at end of file From ad6c0386afb25e6a3562c767aec72656eee135b1 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:10:17 +0900 Subject: [PATCH 119/204] =?UTF-8?q?[feat]=20UserDetailService=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/utils/MyUserDetailsService.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/utils/MyUserDetailsService.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/utils/MyUserDetailsService.java b/src/main/java/com/bbteam/budgetbuddies/global/security/utils/MyUserDetailsService.java new file mode 100644 index 00000000..a8e0a47b --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/utils/MyUserDetailsService.java @@ -0,0 +1,45 @@ +package com.bbteam.budgetbuddies.global.security.utils; + +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +@Qualifier("MyUserDetailsService") +@RequiredArgsConstructor +public class MyUserDetailsService { + + private final UserRepository userRepository; // 사용자 정보를 조회하기 위한 레포지토리 + + /** + * 전화번호를 통해 사용자 정보를 조회하는 메서드. + * + * @param phoneNumber 사용자 전화번호 + * @return 조회된 사용자 정보 (User 객체) + */ + public User loadUserByPhoneNumber(String phoneNumber) { + // 전화번호로 사용자 정보를 조회하고 없을 시 예외 발생 + User user = userRepository.findFirstByPhoneNumber(phoneNumber) + .orElseThrow(() -> new GeneralException(ErrorStatus._USER_NOT_FOUND)); + return user; // 조회된 사용자 반환 + } + + /** + * 사용자 ID를 통해 사용자 정보를 조회하는 메서드. + * + * @param userId 사용자 ID + * @return 조회된 사용자 정보 (User 객체) + */ + public User loadUserByUserId(Long userId) { + // 사용자 ID로 사용자 정보를 조회하고 없을 시 예외 발생 + User user = userRepository.findById(userId) + .orElseThrow(() -> new GeneralException(ErrorStatus._USER_NOT_FOUND)); + return user; // 조회된 사용자 반환 + } +} \ No newline at end of file From 2d7b43b6df62828b3aa7450c317795ae415e2dbd Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:10:47 +0900 Subject: [PATCH 120/204] =?UTF-8?q?[feat]=20CustomAuthenticationToken=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../utils/CustomAuthenticationToken.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/utils/CustomAuthenticationToken.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/utils/CustomAuthenticationToken.java b/src/main/java/com/bbteam/budgetbuddies/global/security/utils/CustomAuthenticationToken.java new file mode 100644 index 00000000..357d272f --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/utils/CustomAuthenticationToken.java @@ -0,0 +1,47 @@ +package com.bbteam.budgetbuddies.global.security.utils; + +import com.bbteam.budgetbuddies.domain.user.entity.User; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +public class CustomAuthenticationToken extends AbstractAuthenticationToken { + + private final User user; // 사용자 정보가 담긴 커스텀 User 객체 + private Object credentials; // 사용자 인증 자격 정보 (비밀번호 또는 토큰 등) + + /** + * CustomAuthenticationToken 생성자. + * + * @param user 커스텀 User 객체 + * @param credentials 인증 자격 정보 (예: 비밀번호 또는 토큰) + * @param authorities GrantedAuthority 컬렉션 (사용자의 권한 정보) + */ + public CustomAuthenticationToken(User user, Object credentials, Collection authorities) { + super(authorities); // 부모 클래스에 권한 정보 전달 + this.user = user; // 사용자 정보 설정 + this.credentials = credentials; // 인증 자격 정보 설정 + super.setAuthenticated(true); // 인증 완료 상태로 설정 (인증된 상태로 처리) + } + + /** + * 사용자 인증 자격 정보를 반환하는 메서드. + * + * @return 인증 자격 정보 (비밀번호 또는 토큰 등) + */ + @Override + public Object getCredentials() { + return credentials; + } + + /** + * 인증된 사용자 정보를 반환하는 메서드. + * + * @return 커스텀 User 객체 (사용자 정보) + */ + @Override + public User getPrincipal() { + return user; // 사용자 정보를 담고 있는 커스텀 User 객체 반환 + } +} From 02fa662c6da7bb48df523d43aedaa1ea0fa3e8f6 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:11:29 +0900 Subject: [PATCH 121/204] =?UTF-8?q?[feat]=20ErrorStatus=EC=97=90=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EB=B0=8F=20=EC=9D=B8=EC=A6=9D=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EB=B0=8F=20=EC=A0=84=ED=99=94=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=98=88=EC=99=B8=20enum=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apiPayload/code/status/ErrorStatus.java | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java index 83bd72d0..8c9515b8 100644 --- a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java @@ -4,7 +4,6 @@ import com.bbteam.budgetbuddies.apiPayload.code.ErrorReasonDto; import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.ToString; import org.springframework.http.HttpStatus; @Getter @@ -12,13 +11,22 @@ public enum ErrorStatus implements BaseErrorCode { - _INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500", "서버에러"), - _BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON400", "잘못된 요청"), + _INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON5000", "서버에러"), + _BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON4000", "잘못된 요청"), _USER_NOT_FOUND(HttpStatus.BAD_REQUEST, "USER4001", "사용자가 없습니다."), _COMMENT_NOT_FOUND(HttpStatus.BAD_REQUEST, "COMMENT4001", "해당 댓글이 없습니다.") , _NOTICE_NOT_FOUND(HttpStatus.BAD_REQUEST, "NOTICE4001", "해당 공지가 없습니다."), _PAGE_LOWER_ZERO(HttpStatus.BAD_REQUEST, "PAGE4001", "요청된 페이지가 0보다 작습니다."), - _TOKEN_NOT_VALID(HttpStatus.BAD_REQUEST, "TOKEN401", "토큰이 유효하지 않습니다."), + + // TOKEN 관련 예외 + _TOKEN_NOT_VALID(HttpStatus.BAD_REQUEST, "TOKEN4001", "토큰이 유효하지 않습니다."), + _TOKEN_NOT_FOUND(HttpStatus.BAD_REQUEST, "TOKEN4002", "토큰이 존재하지 않습니다."), + _TOKEN_EXPIRED(HttpStatus.BAD_REQUEST, "TOKEN4003", "토큰이 만료되었습니다."), + _TOKEN_PAYLOAD_OR_SIGNATURE_NOT_VALID(HttpStatus.BAD_REQUEST, "TOKEN4004", "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다."), + _TOKEN_HEADER_NOT_VALID(HttpStatus.BAD_REQUEST, "TOKEN4005", "토큰 헤더가 유효하지 않습니다."), + + _OTP_NOT_VALID(HttpStatus.BAD_REQUEST, "OTP4001", "인증번호가 유효하지 않습니다."), + _PHONE_NUMBER_NOT_VALID(HttpStatus.BAD_REQUEST, "AUTH4001", "전화번호 형식이 유효하지 않습니다. (예: 01012341234)"), _FAQ_NOT_FOUND(HttpStatus.NOT_FOUND, "FAQ4004", "해당 FAQ가 존재하지 않습니다."); @@ -30,19 +38,19 @@ public enum ErrorStatus implements BaseErrorCode { @Override public ErrorReasonDto getReason() { return ErrorReasonDto.builder() - .message(message) - .code(code) - .isSuccess(false) - .build(); + .message(message) + .code(code) + .isSuccess(false) + .build(); } @Override public ErrorReasonDto getReasonHttpStatus() { return ErrorReasonDto.builder() - .message(message) - .code(code) - .isSuccess(false) - .httpStatus(httpStatus) - .build(); + .message(message) + .code(code) + .isSuccess(false) + .httpStatus(httpStatus) + .build(); } } From 861208a8094e03e325d34ad90736d4cbc8c771b2 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:12:05 +0900 Subject: [PATCH 122/204] =?UTF-8?q?[feat]=20=EC=A0=84=ED=99=94=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EA=B4=80=EB=A0=A8=20Validation=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../validation/PhoneNumberValidation.java | 34 +++++++++++++++++++ .../auth/validation/ValidPhoneNumber.java | 17 ++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/auth/validation/PhoneNumberValidation.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/auth/validation/ValidPhoneNumber.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/auth/validation/PhoneNumberValidation.java b/src/main/java/com/bbteam/budgetbuddies/global/security/auth/validation/PhoneNumberValidation.java new file mode 100644 index 00000000..01bbf656 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/auth/validation/PhoneNumberValidation.java @@ -0,0 +1,34 @@ +package com.bbteam.budgetbuddies.global.security.auth.validation; + +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import lombok.RequiredArgsConstructor; + +import java.util.regex.Pattern; + +@RequiredArgsConstructor +public class PhoneNumberValidation implements ConstraintValidator { + + // 전화번호 형식은 '01012341234'와 같아야 합니다. + private static final String PHONE_NUMBER_PATTERN = "^010\\d{8}$"; + + @Override + public boolean isValid(String phoneNumber, ConstraintValidatorContext constraintValidatorContext) { + // Check if phone number matches the specified format + if (phoneNumber == null || !Pattern.matches(PHONE_NUMBER_PATTERN, phoneNumber)) { + // 커스텀 에러 코드와 메시지 사용 + constraintValidatorContext.disableDefaultConstraintViolation(); + constraintValidatorContext.buildConstraintViolationWithTemplate( + ErrorStatus._PHONE_NUMBER_NOT_VALID.getCode() + ": " + ErrorStatus._PHONE_NUMBER_NOT_VALID.getMessage() + ).addConstraintViolation(); + return false; + } + return true; + } + + @Override + public void initialize(ValidPhoneNumber constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/auth/validation/ValidPhoneNumber.java b/src/main/java/com/bbteam/budgetbuddies/global/security/auth/validation/ValidPhoneNumber.java new file mode 100644 index 00000000..1852dd76 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/auth/validation/ValidPhoneNumber.java @@ -0,0 +1,17 @@ +package com.bbteam.budgetbuddies.global.security.auth.validation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; + +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = PhoneNumberValidation.class) +@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValidPhoneNumber { + + String message() default "전화번호 형식이 유효하지 않습니다. (예: 01012341234)"; + Class[] groups() default {}; + Class[] payload() default {}; +} From 7601d7d6569d3464757c7374a860412d35a67dc5 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:12:47 +0900 Subject: [PATCH 123/204] =?UTF-8?q?[feat]=20PhoneNumberAuthenticationProvi?= =?UTF-8?q?der=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PhoneNumberAuthenticationProvider.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/utils/PhoneNumberAuthenticationProvider.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/utils/PhoneNumberAuthenticationProvider.java b/src/main/java/com/bbteam/budgetbuddies/global/security/utils/PhoneNumberAuthenticationProvider.java new file mode 100644 index 00000000..c453c3b2 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/utils/PhoneNumberAuthenticationProvider.java @@ -0,0 +1,56 @@ +package com.bbteam.budgetbuddies.global.security.utils; + +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.global.security.otp.OtpService; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class PhoneNumberAuthenticationProvider implements AuthenticationProvider { + + private final MyUserDetailsService myUserDetailsService; // 사용자 정보를 로드하기 위한 서비스 + + private final OtpService otpService; // OTP를 검증하기 위한 서비스 + + /** + * 전화번호와 OTP를 이용해 인증을 처리하는 메서드. + * + * @param authentication 인증 객체 (전화번호와 OTP 정보 포함) + * @return 인증 성공 시 UsernamePasswordAuthenticationToken 반환 + * @throws AuthenticationException 인증 실패 시 발생하는 예외 + */ + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String phoneNumber = authentication.getName(); // 전화번호 (사용자 이름에 해당) + String otp = authentication.getCredentials().toString(); // OTP (비밀번호에 해당) + + // OTP 검증 (OTP가 유효하지 않으면 예외 발생) + if (!otpService.validateOtp(phoneNumber, otp)) { + throw new GeneralException(ErrorStatus._OTP_NOT_VALID); // OTP가 유효하지 않음 + } + + // OTP 검증이 완료되면 사용자 정보 로드 + User user = myUserDetailsService.loadUserByPhoneNumber(phoneNumber); + + // 비밀번호 검증 없이 인증된 사용자 정보로 토큰 생성 + return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); + } + + /** + * 해당 인증 제공자가 지원하는 인증 타입을 명시하는 메서드. + * + * @param authentication 지원 여부를 확인할 인증 타입 클래스 + * @return UsernamePasswordAuthenticationToken 타입을 지원하는지 여부 + */ + @Override + public boolean supports(Class authentication) { + return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); // UsernamePasswordAuthenticationToken 지원 + } +} From 4c106f2f12550b97af1fbe4f3e1d376d714b6281 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:13:35 +0900 Subject: [PATCH 124/204] =?UTF-8?q?[feat]=20SecurityConfig=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(JWT=20=ED=95=84=ED=84=B0=20=EC=B6=94=EA=B0=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/SecurityConfig.java | 71 +++++++++---------- 1 file changed, 32 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/global/config/SecurityConfig.java b/src/main/java/com/bbteam/budgetbuddies/global/config/SecurityConfig.java index d578574b..56654536 100644 --- a/src/main/java/com/bbteam/budgetbuddies/global/config/SecurityConfig.java +++ b/src/main/java/com/bbteam/budgetbuddies/global/config/SecurityConfig.java @@ -1,39 +1,47 @@ package com.bbteam.budgetbuddies.global.config; -import com.bbteam.budgetbuddies.global.security.OtpService; -import com.bbteam.budgetbuddies.global.security.PhoneNumberAuthenticationProvider; +import com.bbteam.budgetbuddies.global.security.utils.PhoneNumberAuthenticationProvider; +import com.bbteam.budgetbuddies.global.security.jwt.*; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import java.util.Objects; - -import static org.springframework.security.config.Customizer.withDefaults; +import java.util.List; @Configuration @EnableWebSecurity @Slf4j +@EnableMethodSecurity public class SecurityConfig { - @Autowired - private final PhoneNumberAuthenticationProvider phoneNumberOtpAuthenticationProvider; - @Autowired - private final OtpService otpService; + private final JwtRequestFilter jwtRequestFilter; + + private final JwtExceptionFilter jwtExceptionFilter; + + private final PhoneNumberAuthenticationProvider phoneNumberAuthenticationProvider; - private final Environment env; + private final List swaggers = List.of( // Swagger 관련 URL 목록 + "/swagger-ui/**", + "/v3/api-docs/**" + ); - public SecurityConfig(Environment env) { - this.env = env; + private final List auth = List.of( // 인증 관련 URL 목록 + "/auth/get-otp", + "/auth/login" + ); + + + public SecurityConfig(JwtRequestFilter jwtRequestFilter, JwtExceptionFilter jwtExceptionFilter, PhoneNumberAuthenticationProvider phoneNumberAuthenticationProvider) { + this.jwtRequestFilter = jwtRequestFilter; + this.jwtExceptionFilter = jwtExceptionFilter; + this.phoneNumberAuthenticationProvider = phoneNumberAuthenticationProvider; } @Bean @@ -42,31 +50,16 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .csrf(AbstractHttpConfigurer::disable) // csrf 설정 비활성화 .authorizeHttpRequests(authorizeRequests -> authorizeRequests - .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").hasRole("ADMIN") - .anyRequest().permitAll() + .requestMatchers(swaggers.toArray(new String[0])).permitAll() // 스웨거 주소 허용 + .requestMatchers(auth.toArray(new String[0])).permitAll() // 로그인 주소 허용 + .anyRequest().authenticated() // 그 외 모든 요청에 대해 인증 요구 ) - .formLogin(withDefaults()) - .httpBasic(withDefaults()); + .httpBasic(AbstractHttpConfigurer::disable) + .authenticationProvider(phoneNumberAuthenticationProvider) + .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(jwtExceptionFilter, JwtRequestFilter.class); // jwt 에러처리를 위한 필터등록 return http.build(); } - @Bean - public InMemoryUserDetailsManager userDetailsService() { - String username = env.getProperty("spring.security.user.name"); - String password = env.getProperty("spring.security.user.password"); - - log.info("username : {}", username); - log.info("password : {}", password); - - UserDetails user = User.withDefaultPasswordEncoder() - .username(Objects.requireNonNull(username)) - .password(Objects.requireNonNull(password)) - .roles("ADMIN") - .build(); - - return new InMemoryUserDetailsManager(user); - } - - } \ No newline at end of file From 57972febd56901e030b176935e4a8d34272f7c58 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Mon, 7 Oct 2024 22:14:01 +0900 Subject: [PATCH 125/204] =?UTF-8?q?[fix]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/discountinfo/controller/DiscountInfoController.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java index 5d371117..a70a3bf4 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java @@ -4,9 +4,7 @@ import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountRequest; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; import com.bbteam.budgetbuddies.domain.discountinfo.service.DiscountInfoService; -import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; -import com.bbteam.budgetbuddies.global.security.CustomAuthenticationToken; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.*; From 594832eb9d1c85bd32e5d3c190e2c8f752ba9dd0 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Tue, 8 Oct 2024 21:42:37 +0900 Subject: [PATCH 126/204] =?UTF-8?q?[fix]=20=EC=93=B0=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/global/user/Login.java | 12 --------- .../global/user/LoginInterceptor.java | 27 ------------------- 2 files changed, 39 deletions(-) delete mode 100644 src/main/java/com/bbteam/budgetbuddies/global/user/Login.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/global/user/LoginInterceptor.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/user/Login.java b/src/main/java/com/bbteam/budgetbuddies/global/user/Login.java deleted file mode 100644 index 71338e72..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/global/user/Login.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.bbteam.budgetbuddies.global.user; - - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -public @interface Login { -} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/user/LoginInterceptor.java b/src/main/java/com/bbteam/budgetbuddies/global/user/LoginInterceptor.java deleted file mode 100644 index a149f611..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/global/user/LoginInterceptor.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.bbteam.budgetbuddies.global.user; - -import com.bbteam.budgetbuddies.global.security.JwtUtil; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.web.servlet.HandlerInterceptor; - -@RequiredArgsConstructor -public class LoginInterceptor implements HandlerInterceptor { - - private final JwtUtil jwtUtil; - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - String authorization = request.getHeader("Authorization"); - if(authorization == null) { - return false; - } - String jwt = authorization.substring(7); - - Long userId = Long.parseLong(jwtUtil.extractUserId(jwt)); - request.setAttribute("userId", userId); - - - return true; - } -} From 3d2ac114953bb38653b09afdae0fcbf3f3e80af2 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Tue, 8 Oct 2024 21:42:56 +0900 Subject: [PATCH 127/204] =?UTF-8?q?[feat]=20@Transactional=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/refreshtoken/RefreshTokenService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshTokenService.java b/src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshTokenService.java index a1dfa809..fffc5f69 100644 --- a/src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshTokenService.java +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/refreshtoken/RefreshTokenService.java @@ -5,6 +5,7 @@ import com.bbteam.budgetbuddies.domain.user.entity.User; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.Optional; @@ -22,6 +23,7 @@ public class RefreshTokenService { * @param user 사용자 정보 * @param newToken 새로운 리프레시 토큰 */ + @Transactional public void saveOrUpdateRefreshToken(User user, String newToken) { // 해당 사용자의 리프레시 토큰이 이미 존재하는지 확인 Optional existingToken = refreshTokenRepository.findByUser(user); @@ -65,6 +67,7 @@ public boolean validateRefreshToken(User user, String token) { * * @param user 사용자 정보 */ + @Transactional public void deleteRefreshToken(User user) { // 사용자와 연관된 리프레시 토큰 삭제 refreshTokenRepository.deleteByUser(user); From 1d19ed50a4735c6b43b0f049d299a9cd60ba23b8 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Tue, 8 Oct 2024 21:43:11 +0900 Subject: [PATCH 128/204] =?UTF-8?q?[fix]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/bbteam/budgetbuddies/domain/user/entity/User.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java index 4ce5e424..d0ddf032 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java @@ -45,11 +45,6 @@ public class User extends BaseEntity { @Column(nullable = false) private String mobileCarrier; // 통신사 - @Column(length = 1000) - private String photoUrl; - - private String consumptionPattern; - private LocalDateTime lastLoginAt; public void changeUserDate(String email, String name) { From 338608c8035ce9a2ec6c8c2d8d9380b8b1d9e0e1 Mon Sep 17 00:00:00 2001 From: MJJ Date: Thu, 10 Oct 2024 13:54:58 +0900 Subject: [PATCH 129/204] =?UTF-8?q?[test]=20=EB=98=90=EB=9E=98=EB=B3=84=20?= =?UTF-8?q?=EA=B0=80=EC=9E=A5=20=EB=86=92=EC=9D=80=20=EC=86=8C=EB=B9=84=20?= =?UTF-8?q?=EA=B8=88=EC=95=A1=20=EB=B0=8F=20=EA=B0=80=EC=9E=A5=20=EB=86=92?= =?UTF-8?q?=EC=9D=80=20=EB=AA=A9=ED=91=9C=20=EA=B8=88=EC=95=A1=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20repository=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ConsumptionGoalRepositoryTest.java | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java index 25b355f9..49634967 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java @@ -6,6 +6,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -61,6 +62,7 @@ void setUp() { .name("Peer User 1") .gender(Gender.MALE) .phoneNumber("010-1111-1111") + .mobileCarrier("SK Telecom") .build()); peerUser2 = userRepository.save( @@ -70,6 +72,7 @@ void setUp() { .name("Peer User 2") .gender(Gender.MALE) .phoneNumber("010-2222-2222") + .mobileCarrier("KT") .build()); currentMonth = LocalDate.now(); @@ -345,7 +348,8 @@ void findLatelyGoal_Success() { LocalDate searchDate = LocalDate.of(2024, 9, 1); // when - ConsumptionGoal result = consumptionGoalRepository.findLatelyGoal(user.getId(), category.getId(), searchDate).get(); + ConsumptionGoal result = consumptionGoalRepository.findLatelyGoal(user.getId(), category.getId(), searchDate) + .get(); // then assertEquals(result.getGoalMonth(), targetMonth); @@ -379,9 +383,44 @@ void findLatelyGoal_Success2() { .build()); // when - ConsumptionGoal result = consumptionGoalRepository.findLatelyGoal(user.getId(), category.getId(), targetMonth).get(); + ConsumptionGoal result = consumptionGoalRepository.findLatelyGoal(user.getId(), category.getId(), targetMonth) + .get(); // then assertEquals(result.getGoalMonth(), targetMonth); } + + @Test + @DisplayName("또래 나이, 성별, 카테고리로 최대 소비 금액 조회 성공") + void findMaxConsumeAmountByCategory_Success() { + // when + int peerAgeStart = 23; + int peerAgeEnd = 25; + Gender peerGender = Gender.MALE; + + Optional result = consumptionGoalRepository.findMaxConsumeAmountByCategory( + peerAgeStart, peerAgeEnd, peerGender, currentMonth); + + // then + assertThat(result).isPresent(); + assertThat(result.get().getConsumeAmount()).isEqualTo(150L); + assertThat(result.get().getCategory().getId()).isEqualTo(defaultCategory2.getId()); + } + + @Test + @DisplayName("또래 나이, 성별, 카테고리로 최대 목표 금액 조회 성공") + void findMaxGoalAmountByCategory_Success() { + // when + int peerAgeStart = 23; + int peerAgeEnd = 25; + Gender peerGender = Gender.MALE; + + Optional result = consumptionGoalRepository.findMaxGoalAmountByCategory( + peerAgeStart, peerAgeEnd, peerGender, currentMonth); + + // then + assertThat(result).isPresent(); + assertThat(result.get().getGoalAmount()).isEqualTo(200L); + assertThat(result.get().getCategory().getId()).isEqualTo(defaultCategory2.getId()); + } } \ No newline at end of file From 6f66217fe9bc92df0407be981bc0653f3a9f7e73 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Thu, 10 Oct 2024 21:16:04 +0900 Subject: [PATCH 130/204] =?UTF-8?q?[refactor]=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EC=A0=84=EC=86=A1=20=EB=A9=94=EC=86=8C=EB=93=9C=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=ED=95=B4=EC=A0=9C=20(=EC=8B=A4=EC=A0=9C?= =?UTF-8?q?=20=EB=A9=94=EC=8B=9C=EC=A7=80=EA=B0=80=20=EC=A0=84=EC=86=A1?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=84=A4=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bbteam/budgetbuddies/global/security/otp/OtpService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpService.java b/src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpService.java index 62413937..ff76eb7f 100644 --- a/src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpService.java +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpService.java @@ -65,8 +65,8 @@ public OtpNumber generateOtp(String phoneNumber) { // 생성된 OTP를 전화번호를 키로 하여 캐시에 저장 otpCache.put(phoneNumber, otp); - // 실제 메시지 전송은 주석 처리되어 있음 (실제 배포 시 주석 해제) - // sendMessage(phoneNumber, otp); + // 실제 메시지 전송 + sendMessage(phoneNumber, otp); return otp; } From fead581e6b29be8df68b40b377b287a0bd28aab5 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 11 Oct 2024 11:04:10 +0900 Subject: [PATCH 131/204] =?UTF-8?q?[fix]=20UserConverter=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20field=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/domain/user/converter/UserConverter.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java index 7de36315..a1df84e1 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java @@ -12,8 +12,6 @@ public static UserDto.ResponseUserDto toDto(User user) { .phoneNumber(user.getPhoneNumber()) .lastLoginAt(user.getLastLoginAt()) .gender(user.getGender()) - .consumptionPattern(user.getConsumptionPattern()) - .photoUrl(user.getPhotoUrl()) .email(user.getEmail()) .age(user.getAge()) .build(); @@ -25,9 +23,7 @@ public static User toUser(UserDto.RegisterUserDto dto) { .email(dto.getEmail()) .age(dto.getAge()) .name(dto.getName()) - .consumptionPattern(dto.getConsumptionPattern()) .gender(dto.getGender()) - .photoUrl(dto.getPhotoUrl()) .build(); } } From 32e4b31812c71199ae06cf482cce9887baf308dc Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 11 Oct 2024 11:04:28 +0900 Subject: [PATCH 132/204] =?UTF-8?q?[refactor]=20apiPayload=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=20=EC=95=88=EC=88=98=EB=8A=94=20class,=20interface=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/apiPayload/ApiResponse.java | 7 ---- .../apiPayload/code/BaseCode.java | 8 ---- .../apiPayload/code/ReasonDto.java | 16 -------- .../code/status/CommonErrorStatus.java | 41 ------------------- 4 files changed, 72 deletions(-) delete mode 100644 src/main/java/com/bbteam/budgetbuddies/apiPayload/code/BaseCode.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/apiPayload/code/ReasonDto.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/CommonErrorStatus.java diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/ApiResponse.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/ApiResponse.java index 86a85c2d..d3baa699 100644 --- a/src/main/java/com/bbteam/budgetbuddies/apiPayload/ApiResponse.java +++ b/src/main/java/com/bbteam/budgetbuddies/apiPayload/ApiResponse.java @@ -1,7 +1,6 @@ package com.bbteam.budgetbuddies.apiPayload; -import com.bbteam.budgetbuddies.apiPayload.code.BaseCode; import com.bbteam.budgetbuddies.apiPayload.code.status.SuccessStatus; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; @@ -25,12 +24,6 @@ public static ApiResponse onSuccess(T result){ } - public static ApiResponse of(BaseCode code, T result){ - return new ApiResponse<>(true, code.getReasonHttpStatus().getCode(), - code.getReasonHttpStatus().getMessage(), result); - } - - public static ApiResponse onFailure(String code, String message, T data){ return new ApiResponse<>(false, code,message, data); } diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/BaseCode.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/BaseCode.java deleted file mode 100644 index f21b4b56..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/BaseCode.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.bbteam.budgetbuddies.apiPayload.code; - -public interface BaseCode { - - ReasonDto getReason(); - - ReasonDto getReasonHttpStatus(); -} diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/ReasonDto.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/ReasonDto.java deleted file mode 100644 index aa642b35..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/ReasonDto.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.bbteam.budgetbuddies.apiPayload.code; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Builder -@AllArgsConstructor -@Getter -public class ReasonDto { - String message; - String code; - Boolean isSuccess; - HttpStatus httpStatus; -} diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/CommonErrorStatus.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/CommonErrorStatus.java deleted file mode 100644 index e88c7f5b..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/CommonErrorStatus.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.bbteam.budgetbuddies.apiPayload.code.status; - -import com.bbteam.budgetbuddies.apiPayload.code.BaseErrorCode; -import com.bbteam.budgetbuddies.apiPayload.code.ErrorReasonDto; -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -@AllArgsConstructor -public enum CommonErrorStatus implements BaseErrorCode { - - _INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500", "서버에러"), - _BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON400", "잘못된 요청입니다."), - _UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "COMMON401", "인증이 필요합니다"), - _FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "금지된 요청입니다."),; - - private final HttpStatus httpStatus; - private final String code; - private final String message; - - - @Override - public ErrorReasonDto getReason() { - return ErrorReasonDto.builder() - .message(message) - .code(code) - .isSuccess(false) - .build(); - } - - @Override - public ErrorReasonDto getReasonHttpStatus() { - return ErrorReasonDto.builder() - .message(message) - .code(code) - .isSuccess(false) - .httpStatus(httpStatus) - .build(); - } -} From df9fdee97cb4461ea42fb2325e1f5d01962eeb5e Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 11 Oct 2024 15:56:38 +0900 Subject: [PATCH 133/204] =?UTF-8?q?[feat]=20Querydsl=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.gradle b/build.gradle index 5ae542b7..9a9f417e 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,12 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-api:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' runtimeOnly('io.jsonwebtoken:jjwt-jackson:0.11.5') // jackson으로 jwt 파싱 + + // querydsl 관련 + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" } tasks.named('test') { From fe81866f30f275f58e7f8c5b1673239ebdf17eba Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 11 Oct 2024 15:57:12 +0900 Subject: [PATCH 134/204] =?UTF-8?q?[feat]=20=EB=8F=99=EC=A0=81=20=EC=BF=BC?= =?UTF-8?q?=EB=A6=AC=EB=A5=BC=20=EB=8B=B4=EB=8B=B9=ED=95=98=EB=8A=94=20Faq?= =?UTF-8?q?SearchRepository=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/faq/repository/FaqRepository.java | 2 +- .../faq/repository/FaqSearchRepository.java | 10 ++++ .../repository/FaqSearchRepositoryImpl.java | 59 +++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqSearchRepository.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqSearchRepositoryImpl.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqRepository.java index 4ab1c63e..d9c6cf4f 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqRepository.java @@ -3,7 +3,7 @@ import com.bbteam.budgetbuddies.domain.faq.entity.Faq; import org.springframework.data.jpa.repository.JpaRepository; -public interface FaqRepository extends JpaRepository { +public interface FaqRepository extends JpaRepository, FaqSearchRepository { } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqSearchRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqSearchRepository.java new file mode 100644 index 00000000..b54ab5be --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqSearchRepository.java @@ -0,0 +1,10 @@ +package com.bbteam.budgetbuddies.domain.faq.repository; + +import com.bbteam.budgetbuddies.domain.faq.entity.Faq; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface FaqSearchRepository { + + Page searchFaq(Pageable pageable, String searchCondition); +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqSearchRepositoryImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqSearchRepositoryImpl.java new file mode 100644 index 00000000..81205dbf --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqSearchRepositoryImpl.java @@ -0,0 +1,59 @@ +package com.bbteam.budgetbuddies.domain.faq.repository; + +import com.bbteam.budgetbuddies.domain.faq.entity.Faq; +import com.querydsl.core.types.dsl.BooleanExpression; +import com.querydsl.jpa.JPAExpressions; +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; + +import java.util.List; + +import static com.bbteam.budgetbuddies.domain.faq.entity.QFaq.*; +import static com.bbteam.budgetbuddies.domain.faqkeyword.domain.QFaqKeyword.*; + +public class FaqSearchRepositoryImpl implements FaqSearchRepository{ + + private final JPAQueryFactory queryFactory; + + public FaqSearchRepositoryImpl(EntityManager em) { + this.queryFactory = new JPAQueryFactory(em); + } + + @Override + public Page searchFaq(Pageable pageable, String searchCondition) { + List result = queryFactory.select(faq) + .from(faq) + .where(faq.in( + JPAExpressions + .select(faqKeyword.faq) + .from(faqKeyword) + .where(keywordMatch(searchCondition)) + )) + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + Long total = queryFactory.select(faq.count()) + .from(faq) + .where(faq.in( + JPAExpressions + .select(faqKeyword.faq) + .from(faqKeyword) + .where(keywordMatch(searchCondition)) + )) + .fetchOne(); + + return new PageImpl<>(result, pageable, total); + + } + + private BooleanExpression keywordMatch(String searchCondition) { + return searchCondition != null ? faqKeyword.searchKeyword.keyword.eq(searchCondition) : null; + } + +} + From fccb9ffb407b63114abc767461805cb935dc1110 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 11 Oct 2024 16:01:09 +0900 Subject: [PATCH 135/204] =?UTF-8?q?[feat]=20FaqService=20searchFaq=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bbteam/budgetbuddies/domain/faq/service/FaqService.java | 2 ++ .../budgetbuddies/domain/faq/service/FaqServiceImpl.java | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqService.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqService.java index eb6a3718..2f276470 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqService.java @@ -11,6 +11,8 @@ public interface FaqService { Page findAllWithPaging(Pageable pageable); + Page searchFaq(Pageable pageable, String searchCondition); + FaqResponseDto.FaqPostResponse postFaq(FaqRequestDto.FaqPostRequest dto, Long userId); FaqResponseDto.FaqModifyResponse modifyFaq(FaqRequestDto.FaqModifyRequest dto, Long faqId); diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceImpl.java index a28ccba8..70c11286 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceImpl.java @@ -62,4 +62,9 @@ public String deleteFaq(Long faqId) { private Faq findFaq(Long faqId) { return faqRepository.findById(faqId).orElseThrow(() -> new GeneralException(ErrorStatus._FAQ_NOT_FOUND)); } + + @Override + public Page searchFaq(Pageable pageable, String searchCondition) { + return faqRepository.searchFaq(pageable, searchCondition).map(FaqConverter::entityToFind); + } } From f24d9635ba108b4ee0b10a627e46dce3be8467c8 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Fri, 11 Oct 2024 16:01:59 +0900 Subject: [PATCH 136/204] =?UTF-8?q?[feat]=20FaqApi=20findByPaging=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EB=B0=8F=20=ED=98=B8?= =?UTF-8?q?=EC=B6=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bbteam/budgetbuddies/domain/faq/controller/FaqApi.java | 3 ++- .../budgetbuddies/domain/faq/controller/FaqController.java | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java index f3e0b134..06bc6e16 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java @@ -17,6 +17,7 @@ import jakarta.validation.Valid; import org.springframework.data.domain.Pageable; import org.springframework.http.MediaType; +import org.springframework.lang.Nullable; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; @@ -70,7 +71,7 @@ ApiResponse postFaq(@ExistUser @RequestParam Long userId, } ) - ApiResponse findByPaging(Pageable pageable); + ApiResponse findByPaging(Pageable pageable, String SearchCondition); @Operation(summary = "[User] FAQ 수정 API", description = "FAQ를 수정하는 API입니다.", requestBody = @RequestBody( diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java index c8da6681..74a6e41f 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java @@ -7,10 +7,12 @@ import com.bbteam.budgetbuddies.domain.faq.validation.ExistFaq; import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; import jakarta.validation.Valid; +import jakarta.validation.constraints.Null; import lombok.RequiredArgsConstructor; import org.springdoc.core.annotations.ParameterObject; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.lang.Nullable; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -37,8 +39,9 @@ public ApiResponse findFaq(@PathVariable @ExistF @Override @GetMapping("/all") - public ApiResponse> findByPaging(@ParameterObject Pageable pageable) { - return ApiResponse.onSuccess(faqService.findAllWithPaging(pageable)); + public ApiResponse> findByPaging(@ParameterObject Pageable pageable, + @RequestParam @Nullable String searchCondition) { + return ApiResponse.onSuccess(faqService.searchFaq(pageable, searchCondition)); } @Override From c48c90718682c9fcf904ede8c6145a3319e2330c Mon Sep 17 00:00:00 2001 From: MJJ Date: Sat, 12 Oct 2024 02:59:41 +0900 Subject: [PATCH 137/204] =?UTF-8?q?[refactor]=20=EC=97=94=EB=93=9C?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumptiongoal/controller/ConsumptionGoalController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4a75809d..18508185 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 @@ -120,7 +120,7 @@ public ApiResponse getMonthReport(@RequestParam(name = " return ApiResponse.onSuccess(response); } - @GetMapping("/cosnumption-ment") + @GetMapping("/consumption-ment") public ApiResponse getConsumptionMention(@RequestParam(name = "userId") Long userId) { String response = consumptionGoalService.getConsumptionMention(userId); return ApiResponse.onSuccess(response); From 90088a70e80f5ab2109575dd4d20f6dd3ee98411 Mon Sep 17 00:00:00 2001 From: MJJ Date: Sat, 12 Oct 2024 03:59:17 +0900 Subject: [PATCH 138/204] =?UTF-8?q?[refactor]=20AI=20=EC=9D=91=EB=8B=B5?= =?UTF-8?q?=EC=9D=B4=20=EC=97=86=EC=9D=84=20=EA=B2=BD=EC=9A=B0=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EB=A9=98=ED=8A=B8=20=EC=9D=91=EB=8B=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ConsumptionGoalServiceImpl.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) 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 73e1ba13..2eeb306e 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 @@ -785,7 +785,7 @@ private String getMainComment(List list) { } } Optional minCategory = categoryRepository.findById(minCategoryId); - + if (minCategory.isEmpty()) { throw new IllegalArgumentException("해당 카테고리를 찾을 수 없습니다."); } @@ -795,8 +795,6 @@ private String getMainComment(List list) { long todayAvailableConsumptionAmount = minDifference / remainDays; long weekAvailableConsumptionAmount = todayAvailableConsumptionAmount * 7; - log.info(String.valueOf(weekAvailableConsumptionAmount)); - NumberFormat nf = NumberFormat.getInstance(Locale.KOREA); // 한국 단위로 locale if (weekAvailableConsumptionAmount < 0) { @@ -817,7 +815,7 @@ public String getConsumptionMention(Long userId) { /** * 가장 큰 소비를 한 카테고리의 소비 목표 데이터 정보와 가장 큰 목표로 세운 카테고리의 소비 목표 데이터를 각각 가져온다. * 위 데이터들을 가지고 프롬프트 진행 - * Gemini AI, Chat GPT + * Chat GPT */ // 유저 아이디로 또래 정보 확인 @@ -835,26 +833,26 @@ public String getConsumptionMention(Long userId) { peerAgeEnd, peerGender, currentMonth); - if (!maxConsumeAmount.isPresent()) { + if (maxConsumeAmount.isEmpty()) { throw new IllegalArgumentException("해당 소비목표 데이터를 찾을 수 없습니다."); } // 유저 이름과 소비 목표 데이터로 카테고리 이름, 소비 금액을 가져 온다. String username = findUserById(userId).getName(); String categoryName = maxConsumeAmount.get().getCategory().getName(); - String consumeAmount = String.valueOf(maxConsumeAmount.get().getConsumeAmount()); + long consumeAmount = maxConsumeAmount.get().getConsumeAmount(); // 또래의 상위 소비 금액에 대한 정보로 프롬프트 작성 String firstPrompt = "00은 " + username + ", 가장 큰 소비 카테고리 이름은 " + categoryName + "," + "해당 카테고리 소비금액은" + consumeAmount + "이야"; - if (!maxGoalAmount.isPresent()) { + if (maxGoalAmount.isEmpty()) { throw new IllegalArgumentException("해당 소비목표 데이터를 찾을 수 없습니다."); } // 가장 큰 목표 소비 금액에 대한 정보로 프롬프트 작성 categoryName = maxGoalAmount.get().getCategory().getName(); - String goalAmount = String.valueOf(maxGoalAmount.get().getGoalAmount()); + long goalAmount = maxGoalAmount.get().getGoalAmount(); // 또래의 상위 목표 소비 금액에 대한 정보로 프롬프트 작성 String secondPrompt = "가장 큰 목표 소비 카테고리 이름은 " + categoryName @@ -868,8 +866,15 @@ public String getConsumptionMention(Long userId) { + "카테고리 목표 금액(ex. 패션에 N만원 소비를 계획해요)같은 트렌드 한 멘트, 인터넷상 바이럴 문구" + "참고하여 만들어줘"; - return openAiService.chat(basePrompt); - // return geminiService.getContents(basePrompt); + String response = openAiService.chat(basePrompt); + + if (response == null) { + NumberFormat nf = NumberFormat.getInstance(Locale.KOREA); + response = "총 " + nf.format(goalAmount - consumeAmount) + "원 더 쓸 수 있어요."; + return response; + } + + return response; } } \ No newline at end of file From 0af3f0c86b3c0d356a7b7b8251d7d7525fd4ca66 Mon Sep 17 00:00:00 2001 From: MJJ Date: Sat, 12 Oct 2024 04:03:56 +0900 Subject: [PATCH 139/204] =?UTF-8?q?[chore]=20gemini=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gemini/controller/GeminiController.java | 29 --------- .../domain/gemini/dto/ChatRequest.java | 57 ----------------- .../domain/gemini/dto/ChatResponse.java | 62 ------------------- .../domain/gemini/service/GeminiService.java | 7 --- .../gemini/service/GeminiServiceImpl.java | 40 ------------ .../config/GeminiRestTemplateConfig.java | 23 ------- 6 files changed, 218 deletions(-) delete mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/gemini/controller/GeminiController.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/gemini/dto/ChatRequest.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/gemini/dto/ChatResponse.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiService.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiServiceImpl.java delete mode 100644 src/main/java/com/bbteam/budgetbuddies/global/config/GeminiRestTemplateConfig.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/gemini/controller/GeminiController.java b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/controller/GeminiController.java deleted file mode 100644 index 1222baa2..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/gemini/controller/GeminiController.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.bbteam.budgetbuddies.domain.gemini.controller; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.client.HttpClientErrorException; - -import com.bbteam.budgetbuddies.domain.gemini.service.GeminiServiceImpl; - -import lombok.RequiredArgsConstructor; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/gemini") -public class GeminiController { - - private final GeminiServiceImpl geminiService; - - @GetMapping("/chat") - public ResponseEntity gemini(@RequestParam(name = "message") String message) { - try { - return ResponseEntity.ok().body(geminiService.getContents(message)); - } catch (HttpClientErrorException e) { - return ResponseEntity.badRequest().body(e.getMessage()); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/gemini/dto/ChatRequest.java b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/dto/ChatRequest.java deleted file mode 100644 index 31dbf2c1..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/gemini/dto/ChatRequest.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.bbteam.budgetbuddies.domain.gemini.dto; - -import java.util.ArrayList; -import java.util.List; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class ChatRequest { - private List contents; - private GenerationConfig generationConfig; - - @Getter - @Setter - public static class Content { - private Parts parts; - } - - @Getter - @Setter - public static class Parts { - private String text; - - } - - @Getter - @Setter - public static class GenerationConfig { - private int candidate_count; - private int max_output_tokens; - private double temperature; - - } - - public ChatRequest(String prompt) { - this.contents = new ArrayList<>(); - Content content = new Content(); - Parts parts = new Parts(); - - parts.setText(prompt); - content.setParts(parts); - - this.contents.add(content); - this.generationConfig = new GenerationConfig(); - this.generationConfig.setCandidate_count(1); - this.generationConfig.setMax_output_tokens(1000); - this.generationConfig.setTemperature(0.7); - } -} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/gemini/dto/ChatResponse.java b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/dto/ChatResponse.java deleted file mode 100644 index 3ff240cb..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/gemini/dto/ChatResponse.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.bbteam.budgetbuddies.domain.gemini.dto; - -import java.util.List; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class ChatResponse { - - private List candidates; - private PromptFeedback promptFeedback; - - @Getter - @Setter - public static class Candidate { - private Content content; - private String finishReason; - private int index; - private List safetyRatings; - - } - - @Getter - @Setter - @ToString - public static class Content { - private List parts; - private String role; - - } - - @Getter - @Setter - @ToString - public static class Parts { - private String text; - - } - - @Getter - @Setter - public static class SafetyRating { - private String category; - private String probability; - } - - @Getter - @Setter - public static class PromptFeedback { - private List safetyRatings; - - } -} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiService.java b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiService.java deleted file mode 100644 index eb1f80fb..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiService.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.bbteam.budgetbuddies.domain.gemini.service; - -public interface GeminiService { - - String getContents(String prompt); - -} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiServiceImpl.java deleted file mode 100644 index a1c727a5..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/gemini/service/GeminiServiceImpl.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.bbteam.budgetbuddies.domain.gemini.service; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; - -import com.bbteam.budgetbuddies.domain.gemini.dto.ChatRequest; -import com.bbteam.budgetbuddies.domain.gemini.dto.ChatResponse; - -import lombok.RequiredArgsConstructor; - -@Service -@RequiredArgsConstructor -public class GeminiServiceImpl implements GeminiService { - - @Qualifier("geminiRestTemplate") - @Autowired - private RestTemplate restTemplate; - - @Value("${spring.gemini.api.url}") - private String apiUrl; - - @Value("${spring.gemini.api.key}") - private String geminiApiKey; - - public String getContents(String prompt) { - - String requestUrl = apiUrl + "?key=" + geminiApiKey; - - ChatRequest request = new ChatRequest(prompt); - ChatResponse response = restTemplate.postForObject(requestUrl, request, ChatResponse.class); - - String message = response.getCandidates().get(0).getContent().getParts().get(0).getText().toString(); - - return message; - } - -} diff --git a/src/main/java/com/bbteam/budgetbuddies/global/config/GeminiRestTemplateConfig.java b/src/main/java/com/bbteam/budgetbuddies/global/config/GeminiRestTemplateConfig.java deleted file mode 100644 index 294ba799..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/global/config/GeminiRestTemplateConfig.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.bbteam.budgetbuddies.global.config; - -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestTemplate; - -import lombok.RequiredArgsConstructor; - -@Configuration -@RequiredArgsConstructor -public class GeminiRestTemplateConfig { - - @Bean - @Qualifier("geminiRestTemplate") - public RestTemplate geminiRestTemplate() { - RestTemplate restTemplate = new RestTemplate(); - restTemplate.getInterceptors().add((request, body, execution) -> execution.execute(request, body)); - - return restTemplate; - } -} - From c939da46b71e8cc728809bbaa2947e08278f24fb Mon Sep 17 00:00:00 2001 From: MJJ Date: Sat, 12 Oct 2024 04:04:12 +0900 Subject: [PATCH 140/204] =?UTF-8?q?[chore]=20gemini=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumptiongoal/service/ConsumptionGoalServiceImpl.java | 2 -- 1 file changed, 2 deletions(-) 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 2eeb306e..7af006ca 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 @@ -45,7 +45,6 @@ import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.entity.Expense; import com.bbteam.budgetbuddies.domain.expense.repository.ExpenseRepository; -import com.bbteam.budgetbuddies.domain.gemini.service.GeminiService; import com.bbteam.budgetbuddies.domain.openai.service.OpenAiService; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; @@ -63,7 +62,6 @@ public class ConsumptionGoalServiceImpl implements ConsumptionGoalService { private final ExpenseRepository expenseRepository; private final CategoryRepository categoryRepository; private final UserRepository userRepository; - private final GeminiService geminiService; private final OpenAiService openAiService; private final ConsumptionGoalConverter consumptionGoalConverter; From 21bf119a4b5c3a066648b82b39f147e4d96b9af5 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Thu, 10 Oct 2024 17:55:06 +0900 Subject: [PATCH 141/204] =?UTF-8?q?[fix]=20=EA=B8=B0=ED=9A=8D=20=EC=9D=98?= =?UTF-8?q?=EB=8F=84=20=EB=B0=98=EC=98=81=ED=95=98=EC=97=AC=20=EC=86=8C?= =?UTF-8?q?=EB=B9=84=ED=8C=A8=ED=84=B4,=20=EC=82=AC=EC=A7=84=20=EC=9E=A0?= =?UTF-8?q?=EC=8B=9C=20=EB=B3=B4=EB=A5=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/domain/user/converter/UserConverter.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java index a1df84e1..e8a70d9a 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java @@ -12,6 +12,8 @@ public static UserDto.ResponseUserDto toDto(User user) { .phoneNumber(user.getPhoneNumber()) .lastLoginAt(user.getLastLoginAt()) .gender(user.getGender()) +// .consumptionPattern(user.getConsumptionPattern()) +// .photoUrl(user.getPhotoUrl()) .email(user.getEmail()) .age(user.getAge()) .build(); @@ -23,7 +25,9 @@ public static User toUser(UserDto.RegisterUserDto dto) { .email(dto.getEmail()) .age(dto.getAge()) .name(dto.getName()) +// .consumptionPattern(dto.getConsumptionPattern()) .gender(dto.getGender()) +// .photoUrl(dto.getPhotoUrl()) .build(); } } From f5f27a7a5c5b08ece8bc55df102901bcba3b6c44 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Thu, 10 Oct 2024 17:55:41 +0900 Subject: [PATCH 142/204] =?UTF-8?q?[fix]=20swagger=20description=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bbteam/budgetbuddies/domain/user/controller/UserApi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserApi.java index 901f97fb..09d05514 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserApi.java @@ -15,7 +15,7 @@ import java.util.List; public interface UserApi { - @Operation(summary = "[Admin] 기본 카테고리 생성 API ", description = "기본 카테고리가 없는 사용자에게 기본 카테고리를 생성합니다.") + @Operation(summary = "[Admin] 기본 카테고리 생성 API ", description = "(회원 가입시 필수적으로 자동 생성되어야 합니다!)기본 카테고리가 없는 사용자에게 기본 카테고리를 생성합니다.") @ApiResponses({ @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), }) From 9e9c7c58be9ab1a20f408848582713a4db2f851f Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Thu, 10 Oct 2024 19:12:44 +0900 Subject: [PATCH 143/204] =?UTF-8?q?[fix]=20User=EC=99=80=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=EB=90=9C=20=EB=82=B4=EC=9A=A9=EB=93=A4=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/controller/UserApi.java | 6 ++++-- .../domain/user/converter/UserConverter.java | 6 +++++- .../budgetbuddies/domain/user/dto/UserDto.java | 12 ++++++++---- .../budgetbuddies/domain/user/entity/User.java | 5 ++++- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserApi.java index 09d05514..47550d20 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserApi.java @@ -33,10 +33,12 @@ public interface UserApi { @Parameter(name = "phoneNumber", description = "휴대폰 번호. requestBody"), @Parameter(name = "name", description = "사용자 이름. requestBody"), @Parameter(name = "age", description = "사용자 나이. requestBody"), + @Parameter(name = "mobileCarrier", description = "통신사. requestBody"), + @Parameter(name = "region", description = "지역. requestBody"), @Parameter(name = "gender", description = "사용자 성별 / MALE, FEMALE, NONE requestBody"), @Parameter(name = "email", description = "메일 주소. requestBody"), - @Parameter(name = "photoUrl", description = "사진 Url. 아마 사용 x requestBody"), - @Parameter(name = "consumptionPattern", description = "소비 패턴. 아마 사용 x requestBody") +// @Parameter(name = "photoUrl", description = "사진 Url. 아마 사용 x requestBody"), +// @Parameter(name = "consumptionPattern", description = "소비 패턴. 아마 사용 x requestBody") }) ApiResponse registerUser(@RequestBody UserDto.RegisterUserDto dto); @Operation(summary = "[Admin] User 찾기 ", description = "ID를 기반으로 해당 사용자를 찾습니다.") diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java index e8a70d9a..78971dc5 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java @@ -12,6 +12,8 @@ public static UserDto.ResponseUserDto toDto(User user) { .phoneNumber(user.getPhoneNumber()) .lastLoginAt(user.getLastLoginAt()) .gender(user.getGender()) + .region(user.getRegion()) + .mobileCarrier(user.getMobileCarrier()) // .consumptionPattern(user.getConsumptionPattern()) // .photoUrl(user.getPhotoUrl()) .email(user.getEmail()) @@ -25,8 +27,10 @@ public static User toUser(UserDto.RegisterUserDto dto) { .email(dto.getEmail()) .age(dto.getAge()) .name(dto.getName()) -// .consumptionPattern(dto.getConsumptionPattern()) + .region(dto.getRegion()) + .mobileCarrier(dto.getMobileCarrier()) .gender(dto.getGender()) +// .consumptionPattern(dto.getConsumptionPattern()) // .photoUrl(dto.getPhotoUrl()) .build(); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java index 449178b2..62774408 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java @@ -19,10 +19,12 @@ public static class RegisterUserDto { private String name; @Min(value = 1, message = "나이는 0또는 음수가 될 수 없습니다.") private Integer age; + private String mobileCarrier; + private String region; private Gender gender; private String email; - private String photoUrl; - private String consumptionPattern; +// private String photoUrl; +// private String consumptionPattern; } @Getter @@ -33,9 +35,11 @@ public static class ResponseUserDto { private String name; private String email; private Integer age; + private String mobileCarrier; + private String region; private Gender gender; - private String photoUrl; - private String consumptionPattern; +// private String photoUrl; +// private String consumptionPattern; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Asia/Seoul") private LocalDateTime lastLoginAt; } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java index d0ddf032..f1baa2d3 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java @@ -42,9 +42,12 @@ public class User extends BaseEntity { @Column(nullable = false, length = 50, unique = true) private String email; - @Column(nullable = false) + @Column(nullable = true) private String mobileCarrier; // 통신사 + @Column(nullable = true) + private String region; // 거주지 + private LocalDateTime lastLoginAt; public void changeUserDate(String email, String name) { From 53c3268efb0623cbc08b3bc8450ff3c8cdd5b58a Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Mon, 14 Oct 2024 18:17:39 +0900 Subject: [PATCH 144/204] =?UTF-8?q?[refactor]=20User=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=EC=8B=9C=20=ED=95=84=EC=9A=94=EC=97=86=EB=8A=94=20photoUrl,=20?= =?UTF-8?q?consumptionPattern=20=EC=A3=BC=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/discountinfo/service/DiscountInfoServiceTest.java | 2 -- .../domain/supportinfo/service/SupportInfoServiceTest.java | 4 ++-- .../domain/user/repository/UserRepositoryTest.java | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceTest.java index c1514941..119a69da 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/discountinfo/service/DiscountInfoServiceTest.java @@ -173,8 +173,6 @@ void toggleLikeTest() { .age(30) .gender(Gender.MALE) .email("john.doe@example.com") - .photoUrl("http://example.com/photo.jpg") - .consumptionPattern("Regular") .lastLoginAt(LocalDateTime.now()) .build(); diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceTest.java index fe2ff8a0..4d8fd68e 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceTest.java @@ -166,8 +166,8 @@ void toggleLikeTest() { .age(30) .gender(Gender.MALE) .email("john.doe@example.com") - .photoUrl("http://example.com/photo.jpg") - .consumptionPattern("Regular") +// .photoUrl("http://example.com/photo.jpg") +// .consumptionPattern("Regular") .lastLoginAt(LocalDateTime.now()) .build(); diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/user/repository/UserRepositoryTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/user/repository/UserRepositoryTest.java index f89b41ba..7fa143f9 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/user/repository/UserRepositoryTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/user/repository/UserRepositoryTest.java @@ -30,8 +30,8 @@ void saveUser() { .age(25) .gender(Gender.MALE) .email("hong@naver.com") - .photoUrl("abc") - .consumptionPattern("TypeA") +// .photoUrl("abc") +// .consumptionPattern("TypeA") .lastLoginAt(LocalDateTime.now()) .createdAt(LocalDateTime.now()) .updatedAt(LocalDateTime.now()) From e31be7c025edf7c5ca956158463265f8186ab48a Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Mon, 14 Oct 2024 18:19:51 +0900 Subject: [PATCH 145/204] =?UTF-8?q?[feat]=20hashtagId=20Dto=ED=99=94?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/controller/UserApi.java | 15 ++++++++------- .../budgetbuddies/domain/user/dto/UserDto.java | 2 ++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserApi.java index 47550d20..0922fb8d 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserApi.java @@ -30,13 +30,14 @@ public interface UserApi { @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), }) @Parameters({ - @Parameter(name = "phoneNumber", description = "휴대폰 번호. requestBody"), - @Parameter(name = "name", description = "사용자 이름. requestBody"), - @Parameter(name = "age", description = "사용자 나이. requestBody"), - @Parameter(name = "mobileCarrier", description = "통신사. requestBody"), - @Parameter(name = "region", description = "지역. requestBody"), - @Parameter(name = "gender", description = "사용자 성별 / MALE, FEMALE, NONE requestBody"), - @Parameter(name = "email", description = "메일 주소. requestBody"), + @Parameter(name = "phoneNumber", description = "휴대폰 번호"), + @Parameter(name = "name", description = "사용자 이름"), + @Parameter(name = "age", description = "사용자 나이"), + @Parameter(name = "mobileCarrier", description = "통신사"), + @Parameter(name = "region", description = "지역"), + @Parameter(name = "gender", description = "사용자 성별 / MALE, FEMALE, NONE"), + @Parameter(name = "email", description = "메일 주소"), + @Parameter(name = "hashtagIds", description = "사용자가 선택한 해시태그 ID 리스트(제공되는 리스트 기반으로 Id 매칭 필요(통신사, 지역 포함))") // @Parameter(name = "photoUrl", description = "사진 Url. 아마 사용 x requestBody"), // @Parameter(name = "consumptionPattern", description = "소비 패턴. 아마 사용 x requestBody") }) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java index 62774408..8bcc83e9 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java @@ -8,6 +8,7 @@ import org.hibernate.validator.constraints.Length; import java.time.LocalDateTime; +import java.util.List; public class UserDto { @@ -23,6 +24,7 @@ public static class RegisterUserDto { private String region; private Gender gender; private String email; + private List hashtagIds; // 사용자가 선택한 해시태그 ID 목록 // private String photoUrl; // private String consumptionPattern; } From c6ca2d37a2b66b280d26649cc0aa23d32c42de96 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Mon, 14 Oct 2024 18:20:42 +0900 Subject: [PATCH 146/204] =?UTF-8?q?[refactor]=20hashtag=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1=EC=9D=84=20=EC=9C=84=ED=95=9C=20controller=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/controller/UserController.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserController.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserController.java index 6d49ba8b..93fad058 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserController.java @@ -28,6 +28,12 @@ public ResponseEntity> createConsumptionGoals( @PostMapping("/register") public ApiResponse registerUser(@RequestBody UserDto.RegisterUserDto dto) { + // 유저 정보 저장 + UserDto.ResponseUserDto savedUser = userService.saveUser(dto); + + // 유저가 선택한 해시태그를 저장 + userService.saveFavoriteHashtags(savedUser.getId(), dto.getHashtagIds()); + return ApiResponse.onSuccess(userService.saveUser(dto)); } From 7c541142dc021f5dc0cca1a38cb923776cb9c073 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Mon, 14 Oct 2024 18:21:52 +0900 Subject: [PATCH 147/204] =?UTF-8?q?[feat]=20=EC=9C=A0=EC=A0=80=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20API=EC=97=90=20=ED=95=B4=EC=8B=9C=ED=83=9C=EA=B7=B8?= =?UTF-8?q?=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EA=B4=80=EB=A0=A8=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/service/UserService.java | 2 ++ .../domain/user/service/UserServiceImpl.java | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserService.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserService.java index 0cb29dba..f9e3ffa8 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserService.java @@ -7,6 +7,8 @@ public interface UserService { List createConsumptionGoalWithDefaultGoals(Long userId); + void saveFavoriteHashtags(Long userId, List hashtagIds); + UserDto.ResponseUserDto saveUser(UserDto.RegisterUserDto dto); UserDto.ResponseUserDto findUser(Long userId); diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserServiceImpl.java index 2ce9dbb1..8dbe683c 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserServiceImpl.java @@ -6,6 +6,10 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.UserConsumptionGoalResponse; import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; import com.bbteam.budgetbuddies.domain.consumptiongoal.repository.ConsumptionGoalRepository; +import com.bbteam.budgetbuddies.domain.favoritehashtag.entity.FavoriteHashtag; +import com.bbteam.budgetbuddies.domain.favoritehashtag.repository.FavoriteHashtagRepository; +import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; +import com.bbteam.budgetbuddies.domain.hashtag.repository.HashtagRepository; import com.bbteam.budgetbuddies.domain.user.converter.UserConverter; import com.bbteam.budgetbuddies.domain.user.dto.UserDto; import com.bbteam.budgetbuddies.domain.user.entity.User; @@ -28,6 +32,8 @@ public class UserServiceImpl implements UserService { private final CategoryRepository categoryRepository; private final ConsumptionGoalRepository consumptionGoalRepository; private final ConsumptionGoalConverter consumptionGoalConverter; + private final HashtagRepository hashtagRepository; + private final FavoriteHashtagRepository favoriteHashtagRepository; @Override @Transactional @@ -53,6 +59,26 @@ public List createConsumptionGoalWithDefaultGoals(L .collect(Collectors.toList()); } + @Override + @Transactional + public void saveFavoriteHashtags(Long userId, List hashtagIds) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new NoSuchElementException("User not found")); + + List favoriteHashtags = hashtagIds.stream() + .map(hashtagId -> { + Hashtag hashtag = hashtagRepository.findById(hashtagId) + .orElseThrow(() -> new NoSuchElementException("Hashtag not found")); + return FavoriteHashtag.builder() + .user(user) + .hashtag(hashtag) + .build(); + }) + .collect(Collectors.toList()); + + favoriteHashtagRepository.saveAll(favoriteHashtags); + } + @Override @Transactional public UserDto.ResponseUserDto saveUser(UserDto.RegisterUserDto dto) { From 8b8ab14b36d71450f0fabe5aa97b69596aac8a52 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Mon, 14 Oct 2024 18:25:28 +0900 Subject: [PATCH 148/204] =?UTF-8?q?[feat]=20=ED=95=A0=EC=9D=B8=EC=A0=95?= =?UTF-8?q?=EB=B3=B4,=20=EC=A7=80=EC=9B=90=EC=A0=95=EB=B3=B4=20id=20->=20?= =?UTF-8?q?=ED=95=B4=EB=8B=B9=20=EC=A0=95=EB=B3=B4=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=ED=83=9C=EA=B7=B8=EB=A5=BC=20=EB=B3=B4=EC=9C=A0?= =?UTF-8?q?=ED=95=98=EA=B3=A0=20=EC=9E=88=EB=8A=94=20=EC=9C=A0=EC=A0=80=20?= =?UTF-8?q?id=20response=ED=95=98=EB=8A=94=20api=20=EC=97=94=EB=93=9C?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/FavoriteHashtagApi.java | 27 +++++++++++++++++++ .../controller/FavoriteHashtagController.java | 27 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/controller/FavoriteHashtagApi.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/controller/FavoriteHashtagController.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/controller/FavoriteHashtagApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/controller/FavoriteHashtagApi.java new file mode 100644 index 00000000..40011d83 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/controller/FavoriteHashtagApi.java @@ -0,0 +1,27 @@ +package com.bbteam.budgetbuddies.domain.favoritehashtag.controller; + +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.favoritehashtag.dto.FavoriteHashtagResponseDto; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +public interface FavoriteHashtagApi { + + @Operation(summary = "[User] 해당되는 해시태그를 설정한 유저 조회 API", description = "특정 할인정보 또는 지원정보에 등록된 해시태그를 설정한 유저를 조회합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "discountInfoId", description = "조회할 할인정보 ID", required = false), + @Parameter(name = "supportInfoId", description = "조회할 지원정보 ID", required = false), + }) + ApiResponse> getUsersByHashtags( + @RequestParam(required = false) Long discountInfoId, + @RequestParam(required = false) Long supportInfoId + ); +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/controller/FavoriteHashtagController.java b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/controller/FavoriteHashtagController.java new file mode 100644 index 00000000..7e567845 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/controller/FavoriteHashtagController.java @@ -0,0 +1,27 @@ +package com.bbteam.budgetbuddies.domain.favoritehashtag.controller; + +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.favoritehashtag.dto.FavoriteHashtagResponseDto; +import com.bbteam.budgetbuddies.domain.favoritehashtag.service.FavoriteHashtagService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/favoriteHashtags") +public class FavoriteHashtagController implements FavoriteHashtagApi { + + private final FavoriteHashtagService favoriteHashtagService; + + @Override + @GetMapping("/applicable-users") + public ApiResponse> getUsersByHashtags( + @RequestParam(required = false) Long discountInfoId, + @RequestParam(required = false) Long supportInfoId + ) { + List users = favoriteHashtagService.findUsersByHashtag(discountInfoId, supportInfoId); + return ApiResponse.onSuccess(users); + } +} From 45728d63eddfc9f4e82b354c4ba293d18287fb4b Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Mon, 14 Oct 2024 18:25:54 +0900 Subject: [PATCH 149/204] =?UTF-8?q?[feat]=20response=EC=97=90=20=EB=93=A4?= =?UTF-8?q?=EC=96=B4=EA=B0=88=20userId=EB=A5=BC=20=ED=8F=AC=ED=95=A8?= =?UTF-8?q?=ED=95=9C=20Dto=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/FavoriteHashtagResponseDto.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/dto/FavoriteHashtagResponseDto.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/dto/FavoriteHashtagResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/dto/FavoriteHashtagResponseDto.java new file mode 100644 index 00000000..4eec9293 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/dto/FavoriteHashtagResponseDto.java @@ -0,0 +1,16 @@ +package com.bbteam.budgetbuddies.domain.favoritehashtag.dto; + +import com.bbteam.budgetbuddies.domain.user.entity.User; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor + +public class FavoriteHashtagResponseDto { + private Long userId; + + public FavoriteHashtagResponseDto(User user) { + this.userId = user.getId(); + } +} From d9a6fe0d6965e3df03bdfbdc4a98f9178c85208c Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Mon, 14 Oct 2024 18:26:44 +0900 Subject: [PATCH 150/204] =?UTF-8?q?[feat]=20userId=EB=A1=9C=20user?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/discountinfo/entity/DiscountInfo.java | 6 ++++++ .../domain/supportinfo/entity/SupportInfo.java | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/entity/DiscountInfo.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/entity/DiscountInfo.java index 91153236..ccbed0f5 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/entity/DiscountInfo.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/entity/DiscountInfo.java @@ -64,4 +64,10 @@ public void update(DiscountRequest.UpdateDiscountDto discountRequestDto) { this.isInCalendar = discountRequestDto.getIsInCalendar(); } + public static DiscountInfo withId(Long id) { + DiscountInfo discountInfo = new DiscountInfo(); + discountInfo.setId(id); + return discountInfo; + } + } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/entity/SupportInfo.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/entity/SupportInfo.java index 57152c04..bcc21a4d 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/entity/SupportInfo.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/entity/SupportInfo.java @@ -60,5 +60,9 @@ public void update(SupportRequest.UpdateSupportDto supportRequestDto) { this.thumbnailUrl = supportRequestDto.getThumbnailUrl(); this.isInCalendar = supportRequestDto.getIsInCalendar(); } - + public static SupportInfo withId(Long id) { + SupportInfo supportInfo = new SupportInfo(); + supportInfo.setId(id); + return supportInfo; + } } From bcaf5d09128063c029ea470f9a1e6af86a8fd96c Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Mon, 14 Oct 2024 18:27:23 +0900 Subject: [PATCH 151/204] =?UTF-8?q?[feat]=20=EC=9C=A0=EC=A0=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EB=A0=88=ED=8F=AC?= =?UTF-8?q?=EC=A7=80=ED=86=A0=EB=A6=AC=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/FavoriteHashtagRepository.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/repository/FavoriteHashtagRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/repository/FavoriteHashtagRepository.java index 2d8135a8..1d7dd47f 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/repository/FavoriteHashtagRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/repository/FavoriteHashtagRepository.java @@ -1,9 +1,13 @@ package com.bbteam.budgetbuddies.domain.favoritehashtag.repository; import com.bbteam.budgetbuddies.domain.favoritehashtag.entity.FavoriteHashtag; +import com.bbteam.budgetbuddies.domain.user.entity.User; import org.springframework.data.jpa.repository.JpaRepository; -public interface FavoriteHashtagRepository extends JpaRepository { +import java.util.List; +public interface FavoriteHashtagRepository extends JpaRepository { + List findByUser(User user); -} + List findByHashtagIdIn(List hashtagIds); +} \ No newline at end of file From 5d896fa3dd3c65782f17fc6d05f60ae7723568c3 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Mon, 14 Oct 2024 18:27:48 +0900 Subject: [PATCH 152/204] =?UTF-8?q?[feat]=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/FavoriteHashtagService.java | 12 +++ .../service/FavoriteHashtagServiceImpl.java | 76 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/service/FavoriteHashtagService.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/service/FavoriteHashtagServiceImpl.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/service/FavoriteHashtagService.java b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/service/FavoriteHashtagService.java new file mode 100644 index 00000000..1945a5e7 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/service/FavoriteHashtagService.java @@ -0,0 +1,12 @@ +package com.bbteam.budgetbuddies.domain.favoritehashtag.service; + +import com.bbteam.budgetbuddies.domain.favoritehashtag.dto.FavoriteHashtagResponseDto; + +import java.util.List; + +public interface FavoriteHashtagService { + List getUsersForHashtag(Long discountInfoId, Long supportInfoId); + + List findUsersByHashtag(Long discountInfoId, Long supportInfoId); + +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/service/FavoriteHashtagServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/service/FavoriteHashtagServiceImpl.java new file mode 100644 index 00000000..8f336b80 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/favoritehashtag/service/FavoriteHashtagServiceImpl.java @@ -0,0 +1,76 @@ +package com.bbteam.budgetbuddies.domain.favoritehashtag.service; + +import com.bbteam.budgetbuddies.domain.connectedinfo.entity.ConnectedInfo; +import com.bbteam.budgetbuddies.domain.connectedinfo.repository.ConnectedInfoRepository; +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.favoritehashtag.dto.FavoriteHashtagResponseDto; +import com.bbteam.budgetbuddies.domain.favoritehashtag.entity.FavoriteHashtag; +import com.bbteam.budgetbuddies.domain.favoritehashtag.repository.FavoriteHashtagRepository; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class FavoriteHashtagServiceImpl implements FavoriteHashtagService { + + private final ConnectedInfoRepository connectedInfoRepository; + private final FavoriteHashtagRepository favoriteHashtagRepository; + private final UserRepository userRepository; + + @Override + public List getUsersForHashtag(Long discountInfoId, Long supportInfoId) { + List connectedInfos; + + if (discountInfoId != null) { + DiscountInfo discountInfo = DiscountInfo.withId(discountInfoId); + connectedInfos = connectedInfoRepository.findAllByDiscountInfo(discountInfo); + } else if (supportInfoId != null) { + SupportInfo supportInfo = SupportInfo.withId(supportInfoId); + connectedInfos = connectedInfoRepository.findAllBySupportInfo(supportInfo); + } else { + throw new IllegalArgumentException("discountInfoId 또는 supportInfoId 중 하나는 필수입니다."); + } + + List hashtagIds = connectedInfos.stream() + .map(connectedInfo -> connectedInfo.getHashtag().getId()) + .collect(Collectors.toList()); + System.out.println("Connected Hashtags IDs: " + hashtagIds); + + + List favoriteHashtags = favoriteHashtagRepository.findByHashtagIdIn(hashtagIds); + System.out.println("Favorite Hashtags: " + favoriteHashtags); + + return favoriteHashtags.stream() + .map(favoriteHashtag -> favoriteHashtag.getUser().getId()) + .distinct() + .collect(Collectors.toList()); + } + + @Override + public List findUsersByHashtag(Long discountInfoId, Long supportInfoId) { + List userIds = getUsersForHashtag(discountInfoId, supportInfoId); + + return userIds.stream() + .map(userId -> { + Optional optionalUser = userRepository.findById(userId); + optionalUser.ifPresent(user -> System.out.println("User found: " + user)); // 여기에 추가 + return optionalUser.map(FavoriteHashtagResponseDto::new) + .orElseGet(() -> { + System.out.println("User not found with id: " + userId); + return null; + }); + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } +} From 7f02e61fd54c3adadc729f272977580de86ef3e8 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Mon, 14 Oct 2024 18:27:57 +0900 Subject: [PATCH 153/204] =?UTF-8?q?[feat]=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/FavoriteHashtagServiceTest.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 src/test/java/com/bbteam/budgetbuddies/domain/favoritehashtag/service/FavoriteHashtagServiceTest.java diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/favoritehashtag/service/FavoriteHashtagServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/favoritehashtag/service/FavoriteHashtagServiceTest.java new file mode 100644 index 00000000..49195057 --- /dev/null +++ b/src/test/java/com/bbteam/budgetbuddies/domain/favoritehashtag/service/FavoriteHashtagServiceTest.java @@ -0,0 +1,100 @@ +package com.bbteam.budgetbuddies.domain.favoritehashtag.service; + +import com.bbteam.budgetbuddies.domain.connectedinfo.entity.ConnectedInfo; +import com.bbteam.budgetbuddies.domain.connectedinfo.repository.ConnectedInfoRepository; +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.discountinfo.repository.DiscountInfoRepository; +import com.bbteam.budgetbuddies.domain.favoritehashtag.dto.FavoriteHashtagResponseDto; +import com.bbteam.budgetbuddies.domain.favoritehashtag.entity.FavoriteHashtag; +import com.bbteam.budgetbuddies.domain.favoritehashtag.repository.FavoriteHashtagRepository; +import com.bbteam.budgetbuddies.domain.hashtag.entity.Hashtag; +import com.bbteam.budgetbuddies.domain.hashtag.repository.HashtagRepository; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +@ActiveProfiles("test") +public class FavoriteHashtagServiceTest { + + @Autowired + private FavoriteHashtagRepository favoriteHashtagRepository; + + @Autowired + private UserRepository userRepository; + + @Autowired + private HashtagRepository hashtagRepository; + + @Autowired + private FavoriteHashtagService favoriteHashtagService; + + @Autowired + private ConnectedInfoRepository connectedInfoRepository; + + @Autowired + private DiscountInfoRepository discountInfoRepository; + + private User user; + private Hashtag hashtag; + private DiscountInfo discountInfo; + + @BeforeEach + void setUp() { + // 데이터 초기화 + connectedInfoRepository.deleteAll(); + favoriteHashtagRepository.deleteAll(); + discountInfoRepository.deleteAll(); + hashtagRepository.deleteAll(); + userRepository.deleteAll(); + + // Given: 사용자와 해시태그, 할인정보를 생성 + user = userRepository.save(User.builder() + .phoneNumber("01012345678") + .name("Test User") + .age(25) + .email("test1@example.com") + .build()); + + hashtag = hashtagRepository.save(new Hashtag("식비")); + discountInfo = discountInfoRepository.save(DiscountInfo.withId(1L)); + + favoriteHashtagRepository.save(FavoriteHashtag.builder().user(user).hashtag(hashtag).build()); + + connectedInfoRepository.save(ConnectedInfo.builder() + .discountInfo(discountInfo) + .hashtag(hashtag) + .build()); + } + + @Test + void testSaveFavoriteHashtags() { + // When: 사용자가 관심 있는 해시태그를 선택하여 저장함 + FavoriteHashtag favorite1 = FavoriteHashtag.builder().user(user).hashtag(hashtag).build(); + favoriteHashtagRepository.save(favorite1); + + // Then: FavoriteHashtag에 잘 저장되었는지 검증 + List favorites = favoriteHashtagRepository.findByUser(user); + assertThat(favorites).hasSize(1); + assertThat(favorites).extracting(fav -> fav.getHashtag().getName()) + .containsExactly("식비"); + } + + @Test + void testFindUsersByHashtagForDiscountInfo() { + // When: 할인정보에 연결된 해시태그를 기반으로 사용자를 조회 + List userResponses = favoriteHashtagService.findUsersByHashtag(discountInfo.getId(), hashtag.getId()); + + // Then: 해당 해시태그를 가진 유저가 응답되는지 확인 + assertThat(userResponses).hasSize(1); + assertThat(userResponses.get(0).getUserId()).isEqualTo(user.getId()); + } +} From 4167d6101159b46ce214c7911de3a3752c58ea31 Mon Sep 17 00:00:00 2001 From: MJJ Date: Tue, 15 Oct 2024 14:13:54 +0900 Subject: [PATCH 154/204] =?UTF-8?q?[refactor]=20=20Async=EB=A1=9C=20?= =?UTF-8?q?=EB=B9=84=EB=8F=99=EA=B8=B0=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bbteam/budgetbuddies/BudgetbuddiesApplication.java | 2 ++ .../controller/ConsumptionGoalController.java | 10 +++++++--- .../service/ConsumptionGoalService.java | 3 ++- .../service/ConsumptionGoalServiceImpl.java | 9 ++++++--- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/BudgetbuddiesApplication.java b/src/main/java/com/bbteam/budgetbuddies/BudgetbuddiesApplication.java index 42ea116f..9443b8db 100644 --- a/src/main/java/com/bbteam/budgetbuddies/BudgetbuddiesApplication.java +++ b/src/main/java/com/bbteam/budgetbuddies/BudgetbuddiesApplication.java @@ -3,9 +3,11 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableJpaAuditing +@EnableAsync public class BudgetbuddiesApplication { public static void main(String[] args) { 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 18508185..9a85ba0c 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 @@ -2,6 +2,8 @@ import java.time.LocalDate; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; @@ -121,8 +123,10 @@ public ApiResponse getMonthReport(@RequestParam(name = " } @GetMapping("/consumption-ment") - public ApiResponse getConsumptionMention(@RequestParam(name = "userId") Long userId) { - String response = consumptionGoalService.getConsumptionMention(userId); - return ApiResponse.onSuccess(response); + public ApiResponse getConsumptionMention(@RequestParam(name = "userId") Long userId) throws + ExecutionException, + InterruptedException { + CompletableFuture response = consumptionGoalService.getConsumptionMention(userId); + return ApiResponse.onSuccess(response.get()); } } \ 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 de727f3d..f3bd74dc 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 @@ -2,6 +2,7 @@ import java.time.LocalDate; import java.util.List; +import java.util.concurrent.CompletableFuture; import org.springframework.stereotype.Service; @@ -51,5 +52,5 @@ List getAllConsumptionCategories(Long userId, MonthReportResponseDto getMonthReport(Long userId); - String getConsumptionMention(Long userId); + CompletableFuture getConsumptionMention(Long userId); } 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 7af006ca..57239627 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 @@ -17,9 +17,11 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.stream.Collectors; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -807,8 +809,9 @@ private String getMainComment(List list) { } @Override + @Async @Transactional(readOnly = true) - public String getConsumptionMention(Long userId) { + public CompletableFuture getConsumptionMention(Long userId) { /** * 가장 큰 소비를 한 카테고리의 소비 목표 데이터 정보와 가장 큰 목표로 세운 카테고리의 소비 목표 데이터를 각각 가져온다. @@ -869,10 +872,10 @@ public String getConsumptionMention(Long userId) { if (response == null) { NumberFormat nf = NumberFormat.getInstance(Locale.KOREA); response = "총 " + nf.format(goalAmount - consumeAmount) + "원 더 쓸 수 있어요."; - return response; + return CompletableFuture.completedFuture(response); } - return response; + return CompletableFuture.completedFuture(response); } } \ No newline at end of file From 1ede00dd54c87387ae2da6695999a6120db24872 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 16 Oct 2024 21:33:09 +0900 Subject: [PATCH 155/204] =?UTF-8?q?[feat]=20=EC=9D=B8=EC=A6=9D=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=82=B4=EC=9A=A9=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bbteam/budgetbuddies/global/security/otp/OtpService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpService.java b/src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpService.java index ff76eb7f..40c2eac3 100644 --- a/src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpService.java +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/otp/OtpService.java @@ -66,7 +66,7 @@ public OtpNumber generateOtp(String phoneNumber) { otpCache.put(phoneNumber, otp); // 실제 메시지 전송 - sendMessage(phoneNumber, otp); + sendMessage(phoneNumber, otp); return otp; } @@ -85,7 +85,7 @@ public void sendMessage(String phoneNumber, OtpNumber otp) { message.setTo(phoneNumber); // 수신 번호 설정 // 메시지 내용 설정 (한글 45자 이하일 경우 자동으로 SMS로 전송) - message.setText("[빈주머니즈]\n인증번호: " + otp.getOtp()); + message.setText("[빈주머니즈]\n인증번호: " + otp.getOtp() + "\n보안을 위해 번호를 타인과 공유하지 마세요."); // 메시지 전송 요청 및 응답 로그 출력 SingleMessageSentResponse response = this.messageService.sendOne(new SingleMessageSendingRequest(message)); From e5a7e1bfa9ea2ad836d6e3d1dbcb0c7a117d6602 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 16 Oct 2024 21:33:51 +0900 Subject: [PATCH 156/204] =?UTF-8?q?[feat]=20WebConfig=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(AuthArgumentResolver=20=EC=A3=BC=EC=9E=85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/WebConfig.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/config/WebConfig.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/config/WebConfig.java b/src/main/java/com/bbteam/budgetbuddies/global/config/WebConfig.java new file mode 100644 index 00000000..712fb35a --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/config/WebConfig.java @@ -0,0 +1,21 @@ +package com.bbteam.budgetbuddies.global.config; + +import com.bbteam.budgetbuddies.global.security.utils.AuthArgumentResolver; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@Configuration +@RequiredArgsConstructor +public class WebConfig implements WebMvcConfigurer { + + private final AuthArgumentResolver authArgumentResolver; + + @Override + public void addArgumentResolvers(List argumentResolvers) { + argumentResolvers.add(authArgumentResolver); + } +} \ No newline at end of file From f5212a37372987b55711090644da750a9c752aa0 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 16 Oct 2024 21:34:36 +0900 Subject: [PATCH 157/204] =?UTF-8?q?[feat]=20AuthUserDto=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(=EC=9D=B8=EC=A6=9D=EC=9D=84=20=EC=9C=84=ED=95=9C?= =?UTF-8?q?=20id=EB=A5=BC=20=EC=A0=80=EC=9E=A5=ED=95=9C=20DTO)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/bbteam/budgetbuddies/domain/user/dto/UserDto.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java index 449178b2..55312f17 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java @@ -46,4 +46,10 @@ public static class ModifyUserDto { private String email; private String name; } + + @Getter + @Builder + public static class AuthUserDto { + private Long id; + } } From f987492d894125c7631e7d5ad4a1a7b3c532b898 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 16 Oct 2024 21:35:12 +0900 Subject: [PATCH 158/204] =?UTF-8?q?[feat]=20AuthUser=20=EC=96=B4=EB=85=B8?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/security/utils/AuthUser.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/utils/AuthUser.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/utils/AuthUser.java b/src/main/java/com/bbteam/budgetbuddies/global/security/utils/AuthUser.java new file mode 100644 index 00000000..9fa4a0e1 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/utils/AuthUser.java @@ -0,0 +1,12 @@ +package com.bbteam.budgetbuddies.global.security.utils; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface AuthUser { +} From 8064ce0c28fe58e0932edfb4f4eb6b1c9291e0eb Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Wed, 16 Oct 2024 21:35:45 +0900 Subject: [PATCH 159/204] =?UTF-8?q?[feat]=20AuthArgumentResolver=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/utils/AuthArgumentResolver.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/security/utils/AuthArgumentResolver.java diff --git a/src/main/java/com/bbteam/budgetbuddies/global/security/utils/AuthArgumentResolver.java b/src/main/java/com/bbteam/budgetbuddies/global/security/utils/AuthArgumentResolver.java new file mode 100644 index 00000000..7560b0f2 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/security/utils/AuthArgumentResolver.java @@ -0,0 +1,47 @@ +package com.bbteam.budgetbuddies.global.security.utils; + +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +@Component +@RequiredArgsConstructor +public class AuthArgumentResolver implements HandlerMethodArgumentResolver { + + // @Auth 존재 여부 확인 + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(AuthUser.class); + } + + // @Auth 존재 시, 사용자 정보 확인하여 반환 + @Override + public Object resolveArgument(@NonNull MethodParameter parameter, ModelAndViewContainer mavContainer, @NonNull NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null) { + throw new GeneralException(ErrorStatus._USER_NOT_FOUND); + } + + Object principal = authentication.getPrincipal(); + if (!(principal instanceof User user)) { + throw new GeneralException(ErrorStatus._USER_NOT_FOUND); + } + + UserDto.AuthUserDto authUserDto = UserDto.AuthUserDto.builder() + .id(user.getId()) + .build(); + + return authUserDto; + } +} \ No newline at end of file From a25275ebd31dd61bcdd9683c187d5c5a125d4a17 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Thu, 17 Oct 2024 22:46:49 +0900 Subject: [PATCH 160/204] =?UTF-8?q?[refactor]=20=EC=84=9C=EB=B2=84?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EB=AC=B8=EA=B5=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/apiPayload/code/status/ErrorStatus.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java index 8c9515b8..fed1aaaa 100644 --- a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java @@ -11,7 +11,7 @@ public enum ErrorStatus implements BaseErrorCode { - _INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON5000", "서버에러"), + _INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON5000", "서버 에러. 관리자에게 문의하세요."), _BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON4000", "잘못된 요청"), _USER_NOT_FOUND(HttpStatus.BAD_REQUEST, "USER4001", "사용자가 없습니다."), _COMMENT_NOT_FOUND(HttpStatus.BAD_REQUEST, "COMMENT4001", "해당 댓글이 없습니다.") , From e55dba81fdb2c815d6bf0f7fdb288e8c9ee885d7 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Thu, 17 Oct 2024 22:47:24 +0900 Subject: [PATCH 161/204] =?UTF-8?q?[refactor]=20=ED=95=A0=EC=9D=B8?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=8A=A4=EC=9B=A8=EA=B1=B0=20=EC=A0=95=EB=B3=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/DiscountInfoApi.java | 96 ++++++++++++------- 1 file changed, 60 insertions(+), 36 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoApi.java index a3c880a2..f2e0bf76 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoApi.java @@ -1,12 +1,17 @@ package com.bbteam.budgetbuddies.domain.discountinfo.controller; import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.apiPayload.code.ErrorReasonDto; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountRequest; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import com.bbteam.budgetbuddies.global.security.utils.AuthUser; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.PathVariable; @@ -17,10 +22,14 @@ public interface DiscountInfoApi { @Operation(summary = "[User] 특정 년월 할인정보 리스트 가져오기 API", description = "특정 년도와 월에 해당하는 할인정보 목록을 조회하는 API이며, 페이징을 포함합니다.") @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "PAGE4001", description = "요청된 페이지가 0보다 작습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON5000", description = "서버 에러. 관리자에게 문의하세요.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))) }) @Parameters({ @Parameter(name = "year", description = "데이터를 가져올 연도입니다."), @@ -37,11 +46,13 @@ ApiResponse> getDiscountsByYearAndMonth( @Operation(summary = "[ADMIN] 할인정보 등록하기 API", description = "할인정보를 등록하는 API이며, 추후에는 관리자만 접근 가능하도록 할 예정입니다.") @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) - }) + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON5000", description = "서버 에러. 관리자에게 문의하세요.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))) }) @Parameters({ // @Parameter(name = "discountRequestDto", description = "등록할 할인 정보의 전체 내용입니다."), }) @@ -51,27 +62,31 @@ ApiResponse registerDiscountInfo( @Operation(summary = "[User] 특정 할인정보에 좋아요 클릭 API", description = "특정 할인정보에 좋아요 버튼을 클릭하는 API이며, 일단은 사용자 ID를 입력하여 사용합니다. (추후 토큰으로 검증)") @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) - }) + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON5000", description = "서버 에러. 관리자에게 문의하세요.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))) }) @Parameters({ - @Parameter(name = "userId", description = "좋아요를 누른 사용자의 id입니다."), @Parameter(name = "discountInfoId", description = "좋아요를 누를 할인정보의 id입니다."), }) ApiResponse likeDiscountInfo( - @RequestParam @ExistUser Long userId, + @AuthUser UserDto.AuthUserDto user, @PathVariable Long discountInfoId ); @Operation(summary = "[ADMIN] 특정 할인정보 수정하기 API", description = "특정 할인정보를 수정하는 API입니다.") @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) - }) + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "PAGE4001", description = "요청된 페이지가 0보다 작습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON5000", description = "서버 에러. 관리자에게 문의하세요.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))) }) @Parameters({ }) ApiResponse updateDiscountInfo( @@ -80,10 +95,13 @@ ApiResponse updateDiscountInfo( @Operation(summary = "[ADMIN] 특정 할인정보 삭제하기 API", description = "ID를 통해 특정 할인정보를 삭제하는 API입니다.") @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON5000", description = "서버 에러. 관리자에게 문의하세요.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))) }) @Parameters({ @Parameter(name = "discountInfoId", description = "삭제할 할인 정보의 id입니다."), @@ -94,11 +112,14 @@ ApiResponse deleteDiscountInfo( @Operation(summary = "[ADMIN] 특정 할인정보 가져오기 API", description = "ID를 통해 특정 할인정보를 가져오는 API입니다.") @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) - }) + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "PAGE4001", description = "요청된 페이지가 0보다 작습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON5000", description = "서버 에러. 관리자에게 문의하세요.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))) }) @Parameters({ @Parameter(name = "discountInfoId", description = "조회할 할인 정보의 id입니다."), }) @@ -108,18 +129,21 @@ ApiResponse getDiscountInfo( @Operation(summary = "[ADMIN] 특정 사용자가 좋아요를 누른 할인정보 가져오기 API", description = "특정 사용자가 좋아요를 누른 할인정보들을 가져오는 API입니다. 페이징을 포함합니다.") @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) - }) + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "PAGE4001", description = "요청된 페이지가 0보다 작습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON5000", description = "서버 에러. 관리자에게 문의하세요.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))) }) @Parameters({ @Parameter(name = "userId", description = "특정 사용자의 id입니다."), @Parameter(name = "page", description = "페이지 번호, 0번이 1 페이지 입니다. (기본값은 0입니다.)"), @Parameter(name = "size", description = "한 페이지에 불러올 데이터 개수입니다. (기본값은 10개입니다.)") }) ApiResponse> getLikedDiscountInfo( - @PathVariable @ExistUser Long userId, + @AuthUser UserDto.AuthUserDto user, @RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "10") Integer size ); From e697d7c13383463d94fc6f8b2d84cd1a1ceea9ba Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Thu, 17 Oct 2024 22:47:56 +0900 Subject: [PATCH 162/204] =?UTF-8?q?[refactor]=20=ED=95=A0=EC=9D=B8?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=EB=90=9C=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EA=B0=80=EC=A0=B8=EC=98=A4?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/DiscountInfoController.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java index 2adb897c..9a29fe89 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java @@ -4,7 +4,8 @@ import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountRequest; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; import com.bbteam.budgetbuddies.domain.discountinfo.service.DiscountInfoService; -import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; +import com.bbteam.budgetbuddies.global.security.utils.AuthUser; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.*; @@ -42,10 +43,10 @@ public ApiResponse registerDiscountInfo( @Override @PostMapping("/likes/{discountInfoId}") public ApiResponse likeDiscountInfo( - @RequestParam @ExistUser Long userId, + @AuthUser UserDto.AuthUserDto user, @PathVariable Long discountInfoId ) { - DiscountResponseDto discountResponseDto = discountInfoService.toggleLike(userId, discountInfoId); + DiscountResponseDto discountResponseDto = discountInfoService.toggleLike(user.getId(), discountInfoId); return ApiResponse.onSuccess(discountResponseDto); } @@ -84,11 +85,11 @@ public ApiResponse getDiscountInfo( @Override @GetMapping("/liked-all") public ApiResponse> getLikedDiscountInfo( - @RequestParam @ExistUser Long userId, + @AuthUser UserDto.AuthUserDto user, @RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "10") Integer size ) { - Page likedDiscountInfoPage = discountInfoService.getLikedDiscountInfo(userId, page, size); + Page likedDiscountInfoPage = discountInfoService.getLikedDiscountInfo(user.getId(), page, size); return ApiResponse.onSuccess(likedDiscountInfoPage); } From 9e991ec1753cbddf19e3afee79338034a831e415 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Thu, 17 Oct 2024 22:56:24 +0900 Subject: [PATCH 163/204] =?UTF-8?q?[refactor]=20=EC=A7=80=EC=9B=90?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=8A=A4=EC=9B=A8=EA=B1=B0=20=EC=A0=95=EB=B3=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SupportInfoApi.java | 91 +++++++++++++------ 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoApi.java index bdbb38e1..b92e9257 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoApi.java @@ -1,13 +1,18 @@ package com.bbteam.budgetbuddies.domain.supportinfo.controller; import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.apiPayload.code.ErrorReasonDto; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequest; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import com.bbteam.budgetbuddies.global.security.utils.AuthUser; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.PathVariable; @@ -18,10 +23,15 @@ public interface SupportInfoApi { @Operation(summary = "[User] 특정 년월 지원정보 리스트 가져오기 API", description = "특정 년도와 월에 해당하는 지원정보 목록을 조회하는 API이며, 페이징을 포함합니다.") @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "PAGE4001", description = "요청된 페이지가 0보다 작습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON5000", description = "서버 에러. 관리자에게 문의하세요.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))) + }) @Parameters({ @Parameter(name = "year", description = "데이터를 가져올 연도입니다."), @@ -38,10 +48,13 @@ ApiResponse> getSupportsByYearAndMonth( @Operation(summary = "[ADMIN] 지원정보 등록하기 API", description = "지원정보를 등록하는 API이며, 추후에는 관리자만 접근 가능하도록 할 예정입니다.") @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON5000", description = "서버 에러. 관리자에게 문의하세요.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))) }) ApiResponse registerSupportInfo( @RequestBody SupportRequest.RegisterSupportDto requestDto @@ -49,26 +62,33 @@ ApiResponse registerSupportInfo( @Operation(summary = "[User] 특정 지원정보에 좋아요 클릭 API", description = "특정 지원정보에 좋아요 버튼을 클릭하는 API이며, 일단은 사용자 ID를 입력하여 사용합니다. (추후 토큰으로 검증)") @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON5000", description = "서버 에러. 관리자에게 문의하세요.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))) }) @Parameters({ @Parameter(name = "userId", description = "좋아요를 누른 사용자의 id입니다."), @Parameter(name = "supportInfoId", description = "좋아요를 누를 지원정보의 id입니다."), }) ApiResponse likeSupportInfo( - @RequestParam @ExistUser Long userId, + @AuthUser UserDto.AuthUserDto user, @PathVariable Long supportInfoId ); @Operation(summary = "[ADMIN] 특정 지원정보 수정하기 API", description = "ID를 통해 특정 지원정보를 수정하는 API입니다.") @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "PAGE4001", description = "요청된 페이지가 0보다 작습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON5000", description = "서버 에러. 관리자에게 문의하세요.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))) }) @Parameters({ }) @@ -78,10 +98,13 @@ ApiResponse updateSupportInfo( @Operation(summary = "[ADMIN] 특정 지원정보 삭제하기 API", description = "ID를 통해 특정 지원정보를 삭제하는 API입니다.") @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON5000", description = "서버 에러. 관리자에게 문의하세요.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))) }) @Parameters({ @Parameter(name = "supportInfoId", description = "삭제할 지원 정보의 id입니다."), @@ -92,10 +115,14 @@ ApiResponse deleteSupportInfo( @Operation(summary = "[ADMIN] 특정 지원정보 가져오기 API", description = "ID를 통해 특정 지원정보를 가져오는 API입니다.") @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "PAGE4001", description = "요청된 페이지가 0보다 작습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON5000", description = "서버 에러. 관리자에게 문의하세요.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))) }) @Parameters({ @Parameter(name = "supportInfoId", description = "조회할 지원 정보의 id입니다."), @@ -106,10 +133,14 @@ ApiResponse getSupportInfo( @Operation(summary = "[ADMIN] 특정 사용자가 좋아요를 누른 지원정보 가져오기 API", description = "특정 사용자가 좋아요를 누른 지원정보들을 가져오는 API입니다. 페이징을 포함합니다.") @ApiResponses({ - @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), -// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "PAGE4001", description = "요청된 페이지가 0보다 작습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4001", description = "토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4002", description = "토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4003", description = "토큰이 만료되었습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4004", description = "토큰의 페이로드 혹은 시그니처가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "TOKEN4005", description = "토큰 헤더가 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON5000", description = "서버 에러. 관리자에게 문의하세요.", content = @Content(schema = @Schema(implementation = ErrorReasonDto.class))) }) @Parameters({ @Parameter(name = "userId", description = "특정 사용자의 id입니다."), @@ -117,7 +148,7 @@ ApiResponse getSupportInfo( @Parameter(name = "size", description = "한 페이지에 불러올 데이터 개수입니다. (기본값은 10개입니다.)") }) ApiResponse> getLikedSupportInfo( - @PathVariable @ExistUser Long userId, + @AuthUser UserDto.AuthUserDto user, @RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "10") Integer size ); From d3bcf1d7ff7bb8d3468cd0c48ee8a8c0c6fe0a95 Mon Sep 17 00:00:00 2001 From: kangseungmin Date: Thu, 17 Oct 2024 22:56:37 +0900 Subject: [PATCH 164/204] =?UTF-8?q?[refactor]=20=EC=A7=80=EC=9B=90?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=EB=90=9C=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EA=B0=80=EC=A0=B8=EC=98=A4?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../supportinfo/controller/SupportInfoController.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoController.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoController.java index b40f9e81..ca04c776 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoController.java @@ -5,7 +5,9 @@ import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequest; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; import com.bbteam.budgetbuddies.domain.supportinfo.service.SupportInfoService; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import com.bbteam.budgetbuddies.global.security.utils.AuthUser; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.*; @@ -43,10 +45,10 @@ public ApiResponse registerSupportInfo( @Override @PostMapping("/likes/{supportInfoId}") public ApiResponse likeSupportInfo( - @RequestParam @ExistUser Long userId, + @AuthUser UserDto.AuthUserDto user, @PathVariable Long supportInfoId ) { - SupportResponseDto supportResponseDto = supportInfoService.toggleLike(userId, supportInfoId); + SupportResponseDto supportResponseDto = supportInfoService.toggleLike(user.getId(), supportInfoId); return ApiResponse.onSuccess(supportResponseDto); } @@ -84,11 +86,11 @@ public ApiResponse getSupportInfo( @Override @GetMapping("/liked-all") public ApiResponse> getLikedSupportInfo( - @RequestParam @ExistUser Long userId, + @AuthUser UserDto.AuthUserDto user, @RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "10") Integer size ) { - Page likedSupportInfoPage = supportInfoService.getLikedSupportInfo(userId, page, size); + Page likedSupportInfoPage = supportInfoService.getLikedSupportInfo(user.getId(), page, size); return ApiResponse.onSuccess(likedSupportInfoPage); } From 37726fe27f4dae2a2fa4cd39cd1ef8096005fd52 Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 24 Oct 2024 22:13:53 +0900 Subject: [PATCH 165/204] =?UTF-8?q?[refactor]=20ExpenseController=20@AuthU?= =?UTF-8?q?ser=EB=A5=BC=20=ED=86=B5=ED=95=B4=20userId=20=EC=A3=BC=EC=9E=85?= =?UTF-8?q?=EB=B0=9B=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/expense/controller/ExpenseApi.java | 5 +++-- .../domain/expense/controller/ExpenseController.java | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseApi.java index 50a8cc9a..475ec7f5 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseApi.java @@ -16,6 +16,7 @@ import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.MonthlyExpenseResponseDto; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -43,8 +44,8 @@ ResponseEntity createExpense( @ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), @ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class)))}) ResponseEntity findExpensesForMonth( - @PathVariable @Param("userId") Long userId, - @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date); + UserDto.AuthUserDto user, + LocalDate date); @Operation(summary = "[User] 단일 소비 조회하기", description = "queryParameter를 통해 소비 Id를 전달 받아서 응답값을 조회") @ApiResponses({ diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseController.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseController.java index c6499330..979306d2 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseController.java @@ -19,6 +19,8 @@ import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.MonthlyExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.service.ExpenseService; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; +import com.bbteam.budgetbuddies.global.security.utils.AuthUser; import io.swagger.v3.oas.annotations.Parameter; import lombok.RequiredArgsConstructor; @@ -39,12 +41,11 @@ public ResponseEntity createExpense( } @Override - @GetMapping("/{userId}") - public ResponseEntity findExpensesForMonth( - @PathVariable @Param("userId") Long userId, + @GetMapping() + public ResponseEntity findExpensesForMonth(@AuthUser UserDto.AuthUserDto user, @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) { - return ResponseEntity.ok(expenseService.getMonthlyExpense(userId, date)); + return ResponseEntity.ok(expenseService.getMonthlyExpense(user.getId(), date)); } @Override From 0cb9cff5e51ee1d7423a593b2c2cb431cb5b7945 Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 24 Oct 2024 22:15:24 +0900 Subject: [PATCH 166/204] =?UTF-8?q?[refactor]=20ExpenseServiceImpl=20?= =?UTF-8?q?=EC=9B=94=EB=B3=84=EC=86=8C=EB=B9=84=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=9B=94=EB=A7=90=EC=9D=84=20=EB=8B=A4=EC=9D=8C=EB=8B=AC=2000?= =?UTF-8?q?=EC=8B=9C00=EB=B6=84=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/expense/service/ExpenseServiceImpl.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImpl.java index a51df924..9b5b1090 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImpl.java @@ -113,12 +113,10 @@ public void deleteExpense(Long expenseId) { @Transactional(readOnly = true) public MonthlyExpenseResponseDto getMonthlyExpense(Long userId, LocalDate localDate) { LocalDate startOfMonth = localDate.withDayOfMonth(1); - LocalDate endOfMonth = localDate.withDayOfMonth(startOfMonth.lengthOfMonth()); + LocalDate nextMonth = startOfMonth.plusMonths(1L); - User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("Invalid user ID")); - - List expenseSlice = expenseRepository.findAllByUserIdForPeriod(user, - startOfMonth.atStartOfDay(), endOfMonth.atTime(LocalTime.MAX)); + List expenseSlice = expenseRepository.findAllByUserIdForPeriod(userId, + startOfMonth.atStartOfDay(), nextMonth.atStartOfDay()); return expenseConverter.toMonthlyExpenseResponseDto(expenseSlice, startOfMonth); } From 1cec340fb4ced1bec2214409b172f942b1e83e18 Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 24 Oct 2024 22:17:08 +0900 Subject: [PATCH 167/204] =?UTF-8?q?[refactor]=20ExpenseRepository=20?= =?UTF-8?q?=EC=9B=94=EB=B3=84=EC=86=8C=EB=B9=84=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=9B=94=EC=B4=88=2000=EC=8B=9C=2000=EB=B6=84=20=EB=B6=80?= =?UTF-8?q?=ED=84=B0=20=EB=8B=A4=EC=9D=8C=EB=8B=AC=201=EC=9D=BC=2000?= =?UTF-8?q?=EC=8B=9C=2000=EB=B6=84=EC=9C=BC=EB=A1=9C=20=EC=A1=B0=ED=9A=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/expense/repository/ExpenseRepository.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepository.java index b55c55fd..62279b8b 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepository.java @@ -14,8 +14,8 @@ import com.bbteam.budgetbuddies.enums.Gender; public interface ExpenseRepository extends JpaRepository { - @Query("SELECT e FROM Expense e WHERE e.user = :user AND e.expenseDate BETWEEN :startDate AND :endDate ORDER BY e.expenseDate DESC") - List findAllByUserIdForPeriod(@Param("user") User user, @Param("startDate") LocalDateTime startDate, + @Query("SELECT e FROM Expense e WHERE e.user.id = :userId AND e.expenseDate >= :startDate AND e.expenseDate < :endDate ORDER BY e.expenseDate DESC") + List findAllByUserIdForPeriod(@Param("userId") Long userId, @Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate); List findByCategoryIdAndUserIdAndExpenseDateBetweenAndDeletedFalse(Long categoryId, Long userId, From 2c1a39586861e0444c252cbc07a5efd59c05034e Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 24 Oct 2024 22:17:38 +0900 Subject: [PATCH 168/204] =?UTF-8?q?[test]=20ExpenseRepositoryTest=20?= =?UTF-8?q?=EC=9B=94=EB=B3=84=EC=A1=B0=ED=9A=8C=20=EA=B2=BD=EA=B3=97?= =?UTF-8?q?=EA=B0=92=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ExpenseRepositoryTest.java | 85 ++++++++++--------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepositoryTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepositoryTest.java index c1b34ada..97806cd2 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepositoryTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepositoryTest.java @@ -3,7 +3,6 @@ import static org.assertj.core.api.Assertions.*; import java.time.LocalDate; -import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.DisplayName; @@ -30,59 +29,63 @@ class ExpenseRepositoryTest { private CategoryRepository categoryRepository; @Test - @DisplayName("findAllByUserIdFOrPeriod 성공") - void findAllByUserIdForPeriod_Success() { + void 월별_소비조회_월초_월말_조회_성공() { // given - User user = userRepository.save( - User.builder().email("email").age(24).name("name").phoneNumber("010-1234-5678").build()); + User user = userRepository.save(User.builder() + .email("email") + .age(24) + .name("test user") + .phoneNumber("010-1234-5678") + .mobileCarrier("TEST") + .build()); Category userCategory = categoryRepository.save( Category.builder().name("유저 카테고리").user(user).isDefault(false).build()); - LocalDate startDate = LocalDate.of(2024, 7, 1); - LocalDate endDate = startDate.withDayOfMonth(startDate.lengthOfMonth()); + LocalDate startMonth = LocalDate.of(2024, 7, 1); + LocalDate nextMonth = startMonth.plusMonths(1L); - List expected = setExpense(user, userCategory, startDate); + List expected = getExpectedExpense(user, userCategory); + setUnExpectedExpense(user, userCategory, nextMonth); // when - List result = expenseRepository.findAllByUserIdForPeriod(user, - startDate.atStartOfDay(), endDate.atStartOfDay()); + List result = expenseRepository.findAllByUserIdForPeriod(user.getId(), startMonth.atStartOfDay(), + nextMonth.atStartOfDay()); assertThat(result).usingRecursiveComparison().isEqualTo(expected); } - private List setExpense(User user, Category userCategory, LocalDate startDate) { - setUnexpectedExpense(user, userCategory, startDate); - - return setExpectedExpenseOrderByDateDesc(user, userCategory, startDate); - } - - private List setExpectedExpenseOrderByDateDesc(User user, Category userCategory, LocalDate startDate) { - List expenses = new ArrayList<>(); - - for (int i = startDate.lengthOfMonth(); i > startDate.lengthOfMonth() - 5; i--) { - Expense expense = Expense.builder() - .user(user) - .category(userCategory) - .amount(100000L * i) - .expenseDate(startDate.withDayOfMonth(i).atStartOfDay()) - .build(); - - expenses.add(expenseRepository.save(expense)); - } - return expenses; + private List getExpectedExpense(User user, Category userCategory) { + Expense monthOfStartExpense = Expense.builder() + .user(user) + .category(userCategory) + .amount(100000L) + .expenseDate(LocalDate.of(2024, 7, 1).atStartOfDay()) + .build(); + + Expense monthOfLastExpense = Expense.builder() + .user(user) + .category(userCategory) + .amount(100000L) + .expenseDate(LocalDate.of(2024, 7, 31).atTime(23, 59)) + .build(); + + return List.of(expenseRepository.save(monthOfLastExpense), expenseRepository.save(monthOfStartExpense)); } - private void setUnexpectedExpense(User user, Category userCategory, LocalDate startDate) { - for (int i = 1; i <= 5; i++) { - Expense expense = Expense.builder() - .user(user) - .category(userCategory) - .amount(100000L * i) - .expenseDate(startDate.withMonth(8).atStartOfDay()) - .build(); - - expenseRepository.save(expense); - } + private void setUnExpectedExpense(User user, Category userCategory, LocalDate nextMonth) { + expenseRepository.save(Expense.builder() + .user(user) + .category(userCategory) + .amount(100000L) + .expenseDate(LocalDate.of(2024, 8, 1).atStartOfDay()) + .build()); + + expenseRepository.save(Expense.builder() + .user(user) + .category(userCategory) + .amount(100000L) + .expenseDate(LocalDate.of(2024, 6, 30).atTime(23, 59)) + .build()); } } \ No newline at end of file From 2925e2615f1ad36efce3ff055bb9ddce36ded463 Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 24 Oct 2024 22:18:17 +0900 Subject: [PATCH 169/204] =?UTF-8?q?[test]=20ExpenseServiceImplTest=20?= =?UTF-8?q?=EC=9B=94=EB=B3=84=20=EC=86=8C=EB=B9=84=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ExpenseServiceImplTest.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImplTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImplTest.java index 2c7a8484..fd1a8d5f 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImplTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImplTest.java @@ -54,11 +54,8 @@ void setUp() { } @Test - @DisplayName("월별 소비 조회 소비를 DailyExpenseResponseDto로 반환") - void getMonthlyExpense_Success() { + void 월별_소비목록_조회_소비일을_기준으로_소비를_반환_성공() { // given - given(userRepository.findById(user.getId())).willReturn(Optional.of(user)); - Category userCategory = Mockito.spy(Category.builder().build()); given(userCategory.getId()).willReturn(-1L); @@ -66,11 +63,21 @@ void getMonthlyExpense_Success() { List expenses = generateExpenseList(requestMonth, user, userCategory); - given(expenseRepository.findAllByUserIdForPeriod(any(User.class), any(LocalDateTime.class), + given(expenseRepository.findAllByUserIdForPeriod(anyLong(), any(LocalDateTime.class), any(LocalDateTime.class))).willReturn(expenses); - MonthlyExpenseResponseDto expected = MonthlyExpenseResponseDto.builder() - .expenseMonth(LocalDate.of(2024, 07, 01)) + MonthlyExpenseResponseDto expected = getExpectedMonthlyExpense(userCategory); + + // when + MonthlyExpenseResponseDto result = expenseService.getMonthlyExpense(user.getId(), requestMonth); + + // then + assertThat(result).usingRecursiveComparison().isEqualTo(expected); + } + + private MonthlyExpenseResponseDto getExpectedMonthlyExpense(Category userCategory) { + return MonthlyExpenseResponseDto.builder() + .expenseMonth(LocalDate.of(2024, 7, 1)) .totalConsumptionAmount(300_000L) .dailyExpenses(List.of(DailyExpenseResponseDto.builder() .daysOfMonth(2) From 163f03daab9672b0fdc2e67847cb4611de17a405 Mon Sep 17 00:00:00 2001 From: JunRain Date: Fri, 25 Oct 2024 15:42:03 +0900 Subject: [PATCH 170/204] =?UTF-8?q?[refactor]=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=EC=86=8C=EB=B9=84=EC=A1=B0=ED=9A=8C=20DTO=20=EB=AA=85=EC=8B=9C?= =?UTF-8?q?=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20?= =?UTF-8?q?userId=20=EC=B9=BC=EB=9F=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/expense/converter/ExpenseConverter.java | 7 +++---- ...penseResponseDto.java => DetailExpenseResponseDto.java} | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) rename src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/{ExpenseResponseDto.java => DetailExpenseResponseDto.java} (91%) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/converter/ExpenseConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/converter/ExpenseConverter.java index 665849d1..ec248bc8 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/converter/ExpenseConverter.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/converter/ExpenseConverter.java @@ -13,7 +13,7 @@ import com.bbteam.budgetbuddies.domain.expense.dto.CompactExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.dto.DailyExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; -import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseResponseDto; +import com.bbteam.budgetbuddies.domain.expense.dto.DetailExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.dto.MonthlyExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.entity.Expense; import com.bbteam.budgetbuddies.domain.user.entity.User; @@ -31,10 +31,9 @@ public Expense toExpenseEntity(ExpenseRequestDto expenseRequestDto, User user, C .build(); } - public ExpenseResponseDto toExpenseResponseDto(Expense expense) { - return ExpenseResponseDto.builder() + public DetailExpenseResponseDto toExpenseResponseDto(Expense expense) { + return DetailExpenseResponseDto.builder() .expenseId(expense.getId()) - .userId(expense.getUser().getId()) .categoryId(expense.getCategory().getId()) .categoryName(expense.getCategory().getName()) .amount(expense.getAmount()) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/DetailExpenseResponseDto.java similarity index 91% rename from src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseResponseDto.java rename to src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/DetailExpenseResponseDto.java index f0996be0..ef0fdef5 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseResponseDto.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/DetailExpenseResponseDto.java @@ -13,9 +13,8 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class ExpenseResponseDto { +public class DetailExpenseResponseDto { private Long expenseId; - private Long userId; private Long categoryId; private String categoryName; private Long amount; From 456c8e0f0db93c8769ade740a8e6eda5dd9d4f07 Mon Sep 17 00:00:00 2001 From: JunRain Date: Fri, 25 Oct 2024 15:43:36 +0900 Subject: [PATCH 171/204] =?UTF-8?q?[refactor]=20ExpenseController=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=EC=86=8C=EB=B9=84=EC=A1=B0=ED=9A=8C=20@AuthU?= =?UTF-8?q?ser=EB=A5=BC=20=ED=86=B5=ED=95=9C=20userId=20=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/expense/controller/ExpenseApi.java | 15 +++++-------- .../expense/controller/ExpenseController.java | 22 +++++++++---------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseApi.java index 475ec7f5..bd550b32 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseApi.java @@ -3,17 +3,15 @@ import java.time.LocalDate; import org.springframework.data.repository.query.Param; -import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; -import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseResponseDto; +import com.bbteam.budgetbuddies.domain.expense.dto.DetailExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.MonthlyExpenseResponseDto; import com.bbteam.budgetbuddies.domain.user.dto.UserDto; @@ -32,9 +30,9 @@ public interface ExpenseApi { @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), @ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), @ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class)))}) - ResponseEntity createExpense( - @Parameter(description = "user_id") @PathVariable Long userId, - @Parameter(description = "category_id, amount, description, expenseDate") @RequestBody ExpenseRequestDto expenseRequestDto + ResponseEntity createExpense( + @Parameter(description = "user_id") @PathVariable Long userId, + @Parameter(description = "category_id, amount, description, expenseDate") @RequestBody ExpenseRequestDto expenseRequestDto ); @Operation(summary = "[User] 월별 소비 조회", description = "무한 스크롤을 통한 조회로 예상하여 Slice를 통해서 조회") @@ -53,8 +51,7 @@ ResponseEntity findExpensesForMonth( @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), @ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), @ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class)))}) - @GetMapping("/{userId}/{expenseId}") - ResponseEntity findExpense(@Param("userId") Long userId, @Param("expenseId") Long expenseId); + ResponseEntity findExpense(UserDto.AuthUserDto user, Long expenseId); @Operation(summary = "[User] 단일 소비 업데이트하기", description = "소비 아이디와 카테고리 아이디, amount(소비 금액)을 body에 담아서 소비를 업데이트") @ApiResponses({ @@ -64,7 +61,7 @@ ResponseEntity findExpensesForMonth( @ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class)))}) @GetMapping("/{userId}/{expenseId}") @PostMapping("/{userId}") - ResponseEntity updateExpense(@PathVariable @Param("userId") Long userId, + ResponseEntity updateExpense(@PathVariable @Param("userId") Long userId, @RequestBody ExpenseUpdateRequestDto request); @Operation(summary = "[User] 소비 내역 삭제", description = "사용자가 소비 내역을 삭제합니다.") diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseController.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseController.java index 979306d2..7382c8b3 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseController.java @@ -33,10 +33,10 @@ public class ExpenseController implements ExpenseApi { @Override @PostMapping("/add/{userId}") - public ResponseEntity createExpense( - @Parameter(description = "user_id") @PathVariable Long userId, - @Parameter(description = "category_id, amount, description, expenseDate") @RequestBody ExpenseRequestDto expenseRequestDto) { - ExpenseResponseDto response = expenseService.createExpense(userId, expenseRequestDto); + public ResponseEntity createExpense( + @Parameter(description = "user_id") @PathVariable Long userId, + @Parameter(description = "category_id, amount, description, expenseDate") @RequestBody ExpenseRequestDto expenseRequestDto) { + DetailExpenseResponseDto response = expenseService.createExpense(userId, expenseRequestDto); return ResponseEntity.ok(response); } @@ -49,24 +49,22 @@ public ResponseEntity findExpensesForMonth(@AuthUser } @Override - @GetMapping("/{userId}/{expenseId}") - public ResponseEntity findExpense(@PathVariable @Param("userId") Long userId, + @GetMapping("/{expenseId}") + public ResponseEntity findExpense(@AuthUser UserDto.AuthUserDto user, @PathVariable @Param("expenseId") Long expenseId) { - return ResponseEntity.ok(expenseService.findExpenseResponseFromUserIdAndExpenseId(userId, expenseId)); + return ResponseEntity.ok(expenseService.findDetailExpenseResponse(user.getId(), expenseId)); } @Override @PostMapping("/{userId}") - public ResponseEntity updateExpense(@PathVariable @Param("userId") Long userId, + public ResponseEntity updateExpense(@PathVariable @Param("userId") Long userId, @RequestBody ExpenseUpdateRequestDto request) { - ExpenseResponseDto response = expenseService.updateExpense(userId, request); + DetailExpenseResponseDto response = expenseService.updateExpense(userId, request); return ResponseEntity.ok(response); } @DeleteMapping("/delete/{expenseId}") - public ResponseEntity deleteExpense( - @Parameter(description = "expense_id") - @PathVariable Long expenseId) { + public ResponseEntity deleteExpense(@Parameter(description = "expense_id") @PathVariable Long expenseId) { expenseService.deleteExpense(expenseId); return ResponseEntity.ok("Successfully deleted expense!"); } From 3d4a69967b6b500067863e13b4a55cb7f1600b70 Mon Sep 17 00:00:00 2001 From: JunRain Date: Fri, 25 Oct 2024 15:51:28 +0900 Subject: [PATCH 172/204] =?UTF-8?q?[refactor]=20=EC=9C=A0=EC=A0=80?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=B2=B4=ED=81=AC=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=EB=B0=8F=20Expense=20=EC=A1=B0=ED=9A=8C=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../expense/service/ExpenseServiceImpl.java | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImpl.java index 9b5b1090..06232ce1 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImpl.java @@ -1,7 +1,6 @@ package com.bbteam.budgetbuddies.domain.expense.service; import java.time.LocalDate; -import java.time.LocalTime; import java.util.List; import org.springframework.stereotype.Service; @@ -13,7 +12,7 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.service.ConsumptionGoalService; import com.bbteam.budgetbuddies.domain.expense.converter.ExpenseConverter; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; -import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseResponseDto; +import com.bbteam.budgetbuddies.domain.expense.dto.DetailExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.MonthlyExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.entity.Expense; @@ -35,7 +34,7 @@ public class ExpenseServiceImpl implements ExpenseService { private final ConsumptionGoalService consumptionGoalService; @Override - public ExpenseResponseDto createExpense(Long userId, ExpenseRequestDto expenseRequestDto) { + public DetailExpenseResponseDto createExpense(Long userId, ExpenseRequestDto expenseRequestDto) { User user = userRepository.findById(userId) .orElseThrow(() -> new IllegalArgumentException("Invalid user ID")); Category category = categoryRepository.findById(expenseRequestDto.getCategoryId()) @@ -73,12 +72,13 @@ else if (!category.getIsDefault() && category.getUser().getId().equals(userId)) if (expenseDateMonth.equals(currentMonth)) { // 현재 월의 소비 내역일 경우 ConsumptionGoal을 업데이트 - consumptionGoalService.updateConsumeAmount(userId, expenseRequestDto.getCategoryId(), expenseRequestDto.getAmount()); + consumptionGoalService.updateConsumeAmount(userId, expenseRequestDto.getCategoryId(), + expenseRequestDto.getAmount()); } -// else { -// // 과거 월의 소비 내역일 경우 해당 월의 ConsumptionGoal을 업데이트 또는 삭제 상태로 생성 -// consumptionGoalService.updateOrCreateDeletedConsumptionGoal(userId, expenseRequestDto.getCategoryId(), expenseDateMonth, expenseRequestDto.getAmount()); -// } + // else { + // // 과거 월의 소비 내역일 경우 해당 월의 ConsumptionGoal을 업데이트 또는 삭제 상태로 생성 + // consumptionGoalService.updateOrCreateDeletedConsumptionGoal(userId, expenseRequestDto.getCategoryId(), expenseDateMonth, expenseRequestDto.getAmount()); + // } return expenseConverter.toExpenseResponseDto(expense); /* @@ -91,7 +91,7 @@ else if (!category.getIsDefault() && category.getUser().getId().equals(userId)) @Transactional public void deleteExpense(Long expenseId) { Expense expense = expenseRepository.findById(expenseId) - .orElseThrow(() -> new IllegalArgumentException("Not found Expense")); + .orElseThrow(() -> new IllegalArgumentException("Not found Expense")); Long userId = expense.getUser().getId(); Long categoryId = expense.getCategory().getId(); @@ -122,28 +122,22 @@ public MonthlyExpenseResponseDto getMonthlyExpense(Long userId, LocalDate localD } @Override - public ExpenseResponseDto findExpenseResponseFromUserIdAndExpenseId(Long userId, Long expenseId) { - Expense expense = expenseRepository.findById(expenseId) - .orElseThrow(() -> new IllegalArgumentException("Not found expense")); - - checkUserAuthority(userId, expense); - - return expenseConverter.toExpenseResponseDto(expense); + public DetailExpenseResponseDto findDetailExpenseResponse(Long userId, Long expenseId) { + return expenseConverter.toExpenseResponseDto(getExpense(expenseId)); } - private void checkUserAuthority(Long userId, Expense expense) { - if (!expense.getUser().getId().equals(userId)) - throw new IllegalArgumentException("Unauthorized user"); + private Expense getExpense(Long expenseId) { + return expenseRepository.findById(expenseId) + .orElseThrow(() -> new IllegalArgumentException("Not found expense")); } @Override @Transactional - public ExpenseResponseDto updateExpense(Long userId, ExpenseUpdateRequestDto request) { + public DetailExpenseResponseDto updateExpense(Long userId, ExpenseUpdateRequestDto request) { Expense expense = expenseRepository.findById(request.getExpenseId()) .orElseThrow(() -> new IllegalArgumentException("Not found expense")); User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("Not found user")); - checkUserAuthority(userId, expense); Category categoryToReplace = categoryService.handleCategoryChange(expense, request, user); From 3f44baef02930ee2392c139f7b3877cc57329b6d Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 27 Oct 2024 16:49:27 +0900 Subject: [PATCH 173/204] =?UTF-8?q?[fix]=20ExpenseController=20=EC=86=8C?= =?UTF-8?q?=EB=B9=84=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20@AuthUser?= =?UTF-8?q?=EB=A5=BC=20=ED=86=B5=ED=95=B4=20User=20=EC=A3=BC=EC=9E=85?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/expense/controller/ExpenseApi.java | 10 ++-------- .../domain/expense/controller/ExpenseController.java | 9 ++++----- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseApi.java index bd550b32..45856956 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseApi.java @@ -2,16 +2,13 @@ import java.time.LocalDate; -import org.springframework.data.repository.query.Param; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; -import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.DetailExpenseResponseDto; +import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.MonthlyExpenseResponseDto; import com.bbteam.budgetbuddies.domain.user.dto.UserDto; @@ -59,10 +56,7 @@ ResponseEntity findExpensesForMonth( @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), @ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), @ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class)))}) - @GetMapping("/{userId}/{expenseId}") - @PostMapping("/{userId}") - ResponseEntity updateExpense(@PathVariable @Param("userId") Long userId, - @RequestBody ExpenseUpdateRequestDto request); + ResponseEntity updateExpense(UserDto.AuthUserDto user, ExpenseUpdateRequestDto request); @Operation(summary = "[User] 소비 내역 삭제", description = "사용자가 소비 내역을 삭제합니다.") @ApiResponses({ diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseController.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseController.java index 7382c8b3..75329f71 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseController.java @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RestController; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; -import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseResponseDto; +import com.bbteam.budgetbuddies.domain.expense.dto.DetailExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.MonthlyExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.service.ExpenseService; @@ -56,11 +56,10 @@ public ResponseEntity findExpense(@AuthUser UserDto.Au } @Override - @PostMapping("/{userId}") - public ResponseEntity updateExpense(@PathVariable @Param("userId") Long userId, + @PostMapping() + public ResponseEntity updateExpense(@AuthUser UserDto.AuthUserDto user, @RequestBody ExpenseUpdateRequestDto request) { - DetailExpenseResponseDto response = expenseService.updateExpense(userId, request); - return ResponseEntity.ok(response); + return ResponseEntity.ok(expenseService.updateExpense(user.getId(), request)); } @DeleteMapping("/delete/{expenseId}") From fee02a78fcd46d71c9a61c4fa4132b13c99bbe63 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 27 Oct 2024 16:52:33 +0900 Subject: [PATCH 174/204] =?UTF-8?q?[refactor]=20ExpenseUpdateRequestDto=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/expense/converter/ExpenseConverter.java | 2 +- .../domain/expense/dto/ExpenseUpdateRequestDto.java | 2 ++ .../domain/expense/service/ExpenseService.java | 8 ++++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/converter/ExpenseConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/converter/ExpenseConverter.java index ec248bc8..3c639164 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/converter/ExpenseConverter.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/converter/ExpenseConverter.java @@ -31,7 +31,7 @@ public Expense toExpenseEntity(ExpenseRequestDto expenseRequestDto, User user, C .build(); } - public DetailExpenseResponseDto toExpenseResponseDto(Expense expense) { + public DetailExpenseResponseDto toDetailExpenseResponseDto(Expense expense) { return DetailExpenseResponseDto.builder() .expenseId(expense.getId()) .categoryId(expense.getCategory().getId()) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseUpdateRequestDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseUpdateRequestDto.java index 14312ca2..464cd098 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseUpdateRequestDto.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseUpdateRequestDto.java @@ -16,9 +16,11 @@ @Builder public class ExpenseUpdateRequestDto { private Long expenseId; + private Long categoryId; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime expenseDate; + private Long amount; } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseService.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseService.java index 67df0f7e..b14c8332 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseService.java @@ -3,18 +3,18 @@ import java.time.LocalDate; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; -import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseResponseDto; +import com.bbteam.budgetbuddies.domain.expense.dto.DetailExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.MonthlyExpenseResponseDto; public interface ExpenseService { - ExpenseResponseDto createExpense(Long userId, ExpenseRequestDto expenseRequestDto); + DetailExpenseResponseDto createExpense(Long userId, ExpenseRequestDto expenseRequestDto); MonthlyExpenseResponseDto getMonthlyExpense(Long userId, LocalDate localDate); - ExpenseResponseDto findExpenseResponseFromUserIdAndExpenseId(Long userId, Long expenseId); + DetailExpenseResponseDto findDetailExpenseResponse(Long userId, Long expenseId); - ExpenseResponseDto updateExpense(Long userId, ExpenseUpdateRequestDto request); + DetailExpenseResponseDto updateExpense(Long userId, ExpenseUpdateRequestDto request); void deleteExpense(Long expenseId); } From 3f85a28661f8d4f1c6c46b598b6c06e204d9be7d Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 27 Oct 2024 16:54:03 +0900 Subject: [PATCH 175/204] =?UTF-8?q?[refactor]=20ExpenseServiceImpl=20?= =?UTF-8?q?=EC=86=8C=EB=B9=84=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EA=B5=AC=EC=A1=B0=EB=A5=BC=20=EB=AA=85?= =?UTF-8?q?=EC=8B=9C=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../expense/service/ExpenseServiceImpl.java | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImpl.java index 06232ce1..5d6bc445 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImpl.java @@ -9,16 +9,18 @@ import com.bbteam.budgetbuddies.domain.category.entity.Category; import com.bbteam.budgetbuddies.domain.category.repository.CategoryRepository; import com.bbteam.budgetbuddies.domain.category.service.CategoryService; +import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; import com.bbteam.budgetbuddies.domain.consumptiongoal.service.ConsumptionGoalService; import com.bbteam.budgetbuddies.domain.expense.converter.ExpenseConverter; -import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.DetailExpenseResponseDto; +import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.MonthlyExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.entity.Expense; import com.bbteam.budgetbuddies.domain.expense.repository.ExpenseRepository; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import com.bbteam.budgetbuddies.domain.user.service.UserService; import lombok.RequiredArgsConstructor; @@ -28,6 +30,7 @@ public class ExpenseServiceImpl implements ExpenseService { private final ExpenseRepository expenseRepository; private final UserRepository userRepository; + private final UserService userService; private final CategoryRepository categoryRepository; private final CategoryService categoryService; private final ExpenseConverter expenseConverter; @@ -80,7 +83,7 @@ else if (!category.getIsDefault() && category.getUser().getId().equals(userId)) // consumptionGoalService.updateOrCreateDeletedConsumptionGoal(userId, expenseRequestDto.getCategoryId(), expenseDateMonth, expenseRequestDto.getAmount()); // } - return expenseConverter.toExpenseResponseDto(expense); + return expenseConverter.toDetailExpenseResponseDto(expense); /* Case 1 결과) 해당 유저의 user_id + immutable 필드 중 하나의 조합으로 Expense 테이블에 저장 Case 2 결과) 내가 직접 생성한 카테고리 중 하나로 카테고리를 설정하여 Expense 테이블에 저장 @@ -123,7 +126,7 @@ public MonthlyExpenseResponseDto getMonthlyExpense(Long userId, LocalDate localD @Override public DetailExpenseResponseDto findDetailExpenseResponse(Long userId, Long expenseId) { - return expenseConverter.toExpenseResponseDto(getExpense(expenseId)); + return expenseConverter.toDetailExpenseResponseDto(getExpense(expenseId)); } private Expense getExpense(Long expenseId) { @@ -134,16 +137,21 @@ private Expense getExpense(Long expenseId) { @Override @Transactional public DetailExpenseResponseDto updateExpense(Long userId, ExpenseUpdateRequestDto request) { - Expense expense = expenseRepository.findById(request.getExpenseId()) - .orElseThrow(() -> new IllegalArgumentException("Not found expense")); + User user = userService.getUser(userId); + Expense expense = getExpense(request.getExpenseId()); + Category categoryToReplace = categoryService.getCategory(request.getCategoryId()); - User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("Not found user")); + expense.updateExpenseFromRequest(request, categoryToReplace); - Category categoryToReplace = categoryService.handleCategoryChange(expense, request, user); + ConsumptionGoal beforeConsumptionGoal = consumptionGoalService.getUserConsumptionGoal( + user, expense.getCategory(), expense.getExpenseDate().toLocalDate().withDayOfMonth(1)); + ConsumptionGoal afterConsumptionGoal = consumptionGoalService.getUserConsumptionGoal( + user, categoryToReplace, request.getExpenseDate().toLocalDate().withDayOfMonth(1)); - expense.updateExpenseFromRequest(request, categoryToReplace); + consumptionGoalService.recalculateConsumptionAmount(beforeConsumptionGoal, expense.getAmount(), + afterConsumptionGoal, request.getAmount()); - return expenseConverter.toExpenseResponseDto(expenseRepository.save(expense)); + return expenseConverter.toDetailExpenseResponseDto(expenseRepository.save(expense)); } } From 19dc666dad171d903ce80e35955fc335edf4b6be Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 27 Oct 2024 18:11:57 +0900 Subject: [PATCH 176/204] =?UTF-8?q?[feature]=20UserService=20userId?= =?UTF-8?q?=EB=A5=BC=20=ED=86=B5=ED=95=9C=20User=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/domain/user/service/UserService.java | 3 +++ .../budgetbuddies/domain/user/service/UserServiceImpl.java | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserService.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserService.java index 0cb29dba..ac0d046b 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserService.java @@ -1,6 +1,7 @@ package com.bbteam.budgetbuddies.domain.user.service; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.UserConsumptionGoalResponse; import com.bbteam.budgetbuddies.domain.user.dto.UserDto; +import com.bbteam.budgetbuddies.domain.user.entity.User; import java.util.List; @@ -14,4 +15,6 @@ public interface UserService { UserDto.ResponseUserDto modifyUser(Long userId, UserDto.ModifyUserDto dto); List findAll(); + + User getUser(Long userId); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserServiceImpl.java index 2ce9dbb1..6b9bb191 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserServiceImpl.java @@ -82,4 +82,9 @@ public List findAll() { .map(UserConverter::toDto) .toList(); } + + @Override + public User getUser(Long userId) { + return userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("Not found user")); + } } From b612cddbfddd45ee50c21ce7c73fd5cb43cf44b7 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 27 Oct 2024 18:14:43 +0900 Subject: [PATCH 177/204] =?UTF-8?q?[feat]=20CategoryService=20Category=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../category/service/CategoryService.java | 4 +- .../category/service/CategoryServiceImpl.java | 57 ++++++++----------- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryService.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryService.java index 92083193..675d1581 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryService.java @@ -14,8 +14,8 @@ public interface CategoryService { List getUserCategories(Long userId); - Category handleCategoryChange(Expense expense, ExpenseUpdateRequestDto request, User user); - void deleteCategory(Long id, Long userId); + + Category getCategory(Long categoryId); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryServiceImpl.java index af6ae372..c8dab75f 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryServiceImpl.java @@ -5,7 +5,6 @@ import java.util.Optional; import java.util.stream.Collectors; -import com.bbteam.budgetbuddies.domain.expense.repository.ExpenseRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -17,8 +16,8 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; import com.bbteam.budgetbuddies.domain.consumptiongoal.repository.ConsumptionGoalRepository; import com.bbteam.budgetbuddies.domain.consumptiongoal.service.ConsumptionGoalService; -import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.entity.Expense; +import com.bbteam.budgetbuddies.domain.expense.repository.ExpenseRepository; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; @@ -40,11 +39,11 @@ public class CategoryServiceImpl implements CategoryService { @Transactional public CategoryResponseDto createCategory(Long userId, CategoryRequestDto categoryRequestDto) { User user = userRepository.findById(userId) - .orElseThrow(() -> new IllegalArgumentException("Invalid user ID")); + .orElseThrow(() -> new IllegalArgumentException("Invalid user ID")); // 동일한 이름의 삭제된 카테고리가 존재하는지 확인 Optional existingCategory = categoryRepository.findByNameAndUserIdAndDeletedTrue( - categoryRequestDto.getName(), userId); + categoryRequestDto.getName(), userId); if (existingCategory.isPresent()) { // 삭제된 카테고리가 존재하면 복구 (deleted = false) @@ -54,7 +53,7 @@ public CategoryResponseDto createCategory(Long userId, CategoryRequestDto catego // 해당 카테고리의 삭제된 ConsumptionGoal도 복구 Optional existingConsumptionGoal = consumptionGoalRepository.findByUserAndCategoryAndDeletedTrue( - user, categoryToRestore); + user, categoryToRestore); if (existingConsumptionGoal.isPresent()) { ConsumptionGoal consumptionGoalToRestore = existingConsumptionGoal.get(); @@ -65,13 +64,13 @@ public CategoryResponseDto createCategory(Long userId, CategoryRequestDto catego } else { // ConsumptionGoal이 존재하지 않으면 새로 생성 ConsumptionGoal newConsumptionGoal = ConsumptionGoal.builder() - .user(user) - .category(categoryToRestore) - .goalMonth(LocalDate.now().withDayOfMonth(1)) // 현재 달로 목표 설정 - .consumeAmount(0L) - .goalAmount(0L) - .deleted(false) // 생성할 때 삭제 상태가 아니도록 - .build(); + .user(user) + .category(categoryToRestore) + .goalMonth(LocalDate.now().withDayOfMonth(1)) // 현재 달로 목표 설정 + .consumeAmount(0L) + .goalAmount(0L) + .deleted(false) // 생성할 때 삭제 상태가 아니도록 + .build(); consumptionGoalRepository.save(newConsumptionGoal); } @@ -83,13 +82,13 @@ public CategoryResponseDto createCategory(Long userId, CategoryRequestDto catego // 새로운 카테고리에 대한 ConsumptionGoal도 생성 ConsumptionGoal newConsumptionGoal = ConsumptionGoal.builder() - .user(user) - .category(newCategory) - .goalMonth(LocalDate.now().withDayOfMonth(1)) // 현재 달로 목표 설정 - .consumeAmount(0L) - .goalAmount(0L) - .deleted(false) // 생성할 때 삭제 상태가 아니도록 - .build(); + .user(user) + .category(newCategory) + .goalMonth(LocalDate.now().withDayOfMonth(1)) // 현재 달로 목표 설정 + .consumeAmount(0L) + .goalAmount(0L) + .deleted(false) // 생성할 때 삭제 상태가 아니도록 + .build(); consumptionGoalRepository.save(newConsumptionGoal); return categoryConverter.toCategoryResponseDto(newCategory); @@ -100,21 +99,15 @@ public CategoryResponseDto createCategory(Long userId, CategoryRequestDto catego public List getUserCategories(Long userId) { userRepository.findById(userId) - .orElseThrow(() -> new IllegalArgumentException("User not found with id: " + userId)); + .orElseThrow(() -> new IllegalArgumentException("User not found with id: " + userId)); List categories = categoryRepository.findUserCategoryByUserId(userId); return categories.stream().map(categoryConverter::toCategoryResponseDto).collect(Collectors.toList()); } @Override - @Transactional(readOnly = true) - public Category handleCategoryChange(Expense expense, ExpenseUpdateRequestDto request, User user) { - Category categoryToReplace = categoryRepository.findById(request.getCategoryId()) - .orElseThrow(() -> new IllegalArgumentException("Not found category")); - - consumptionGoalService.recalculateConsumptionAmount(expense, request, user); - - return categoryToReplace; + public Category getCategory(Long id) { + return categoryRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("Not found category")); } @Override @@ -131,15 +124,15 @@ public void deleteCategory(Long categoryId, Long userId) { // 현재 월에 해당하는 삭제되지 않은 Expense 조회 (deleted = false) List currentMonthExpenses = expenseRepository.findByCategoryIdAndUserIdAndExpenseDateBetweenAndDeletedFalse( - categoryId, userId, startOfMonth.atStartOfDay(), endOfMonth.atTime(23, 59, 59)); + categoryId, userId, startOfMonth.atStartOfDay(), endOfMonth.atTime(23, 59, 59)); long totalAmount = currentMonthExpenses.stream() - .mapToLong(Expense::getAmount) - .sum(); + .mapToLong(Expense::getAmount) + .sum(); // category_id = 10(기타 카테고리)의 소비 목표 업데이트 (custom 카테고리 삭제로 인한 소비 내역은 삭제되지 않고 기타 카테고리로..) ConsumptionGoal goal = consumptionGoalRepository.findByCategoryIdAndUserId(10L, userId) - .orElseThrow(() -> new IllegalArgumentException("No consumption goal found for category_id 10.")); + .orElseThrow(() -> new IllegalArgumentException("No consumption goal found for category_id 10.")); goal.setConsumeAmount(goal.getConsumeAmount() + totalAmount); consumptionGoalRepository.save(goal); From 342b1029fa80b890572000be2fdadcb683e45904 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 27 Oct 2024 18:16:22 +0900 Subject: [PATCH 178/204] =?UTF-8?q?[refactor]=20ConsumptionGoal=20?= =?UTF-8?q?=EC=86=8C=EB=B9=84=EC=B4=9D=EC=95=A1=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=AA=85=EC=8B=9C=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/consumptiongoal/entity/ConsumptionGoal.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/entity/ConsumptionGoal.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/entity/ConsumptionGoal.java index 71d2accc..229f2df3 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/entity/ConsumptionGoal.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/entity/ConsumptionGoal.java @@ -48,7 +48,7 @@ public class ConsumptionGoal extends BaseEntity { @JoinColumn(name = "category_id") private Category category; - public void updateConsumeAmount(Long amount) { + public void addConsumeAmount(Long amount) { this.consumeAmount += amount; } From 6df37c5e52eeaff68c1e82c9bbf15bef5bece821 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 27 Oct 2024 18:17:19 +0900 Subject: [PATCH 179/204] =?UTF-8?q?[refactor]=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EB=AA=85=EC=97=90=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=97=94=ED=8B=B0=ED=8B=B0=EB=AA=85=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumptiongoal/repository/ConsumptionGoalRepository.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 ad52c9c8..72284b9b 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 @@ -25,8 +25,7 @@ public interface ConsumptionGoalRepository extends JpaRepository findConsumptionGoalByUserIdAndGoalMonth(Long userId, LocalDate goalMonth); - Optional findConsumptionGoalByUserAndCategoryAndGoalMonth(User user, Category category, - LocalDate goalMonth); + Optional findByUserAndCategoryAndGoalMonth(User user, Category category, LocalDate goalMonth); @Query("SELECT AVG(cg.consumeAmount) FROM ConsumptionGoal cg " + "JOIN cg.category c " + From 57c85a120d788f8bca17438aef1bcefb78a0dbaf Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 27 Oct 2024 18:19:42 +0900 Subject: [PATCH 180/204] =?UTF-8?q?[feat]=20ConsumptionGoalService=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=EC=9D=98=20=EC=86=8C=EB=B9=84=EB=AA=A9?= =?UTF-8?q?=ED=91=9C=20=EC=A1=B0=ED=9A=8C=20=EB=B0=8F=20=EC=86=8C=EB=B9=84?= =?UTF-8?q?=EA=B8=88=EC=95=A1=20=EC=9E=AC=EA=B3=84=EC=82=B0=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=8B=9C=EA=B7=B8=EB=8B=88=EC=B2=98=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ConsumptionGoalService.java | 9 +- .../service/ConsumptionGoalServiceImpl.java | 89 ++++++++----------- 2 files changed, 43 insertions(+), 55 deletions(-) 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 de727f3d..aecf4416 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.category.entity.Category; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AllConsumptionCategoryResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionAnalysisResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalListRequestDto; @@ -13,8 +14,7 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopCategoryConsumptionDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopGoalCategoryResponseDto; -import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; -import com.bbteam.budgetbuddies.domain.expense.entity.Expense; +import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; import com.bbteam.budgetbuddies.domain.user.entity.User; @Service @@ -35,7 +35,8 @@ ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId, ConsumptionAnalysisResponseDto getTopCategoryAndConsumptionAmount(Long userId); - void recalculateConsumptionAmount(Expense expense, ExpenseUpdateRequestDto request, User user); + void recalculateConsumptionAmount(ConsumptionGoal beforeConsumptionGoal, Long beforeAmount, + ConsumptionGoal afterConsumptionGoal, Long afterAmount); void updateConsumeAmount(Long userId, Long categoryId, Long amount); @@ -52,4 +53,6 @@ List getAllConsumptionCategories(Long userId, MonthReportResponseDto getMonthReport(Long userId); String getConsumptionMention(Long userId); + + ConsumptionGoal getUserConsumptionGoal(User user, Category category, LocalDate goalDate); } 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 73e1ba13..67d681ce 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 @@ -42,8 +42,6 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopGoalCategoryResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; import com.bbteam.budgetbuddies.domain.consumptiongoal.repository.ConsumptionGoalRepository; -import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; -import com.bbteam.budgetbuddies.domain.expense.entity.Expense; import com.bbteam.budgetbuddies.domain.expense.repository.ExpenseRepository; import com.bbteam.budgetbuddies.domain.gemini.service.GeminiService; import com.bbteam.budgetbuddies.domain.openai.service.OpenAiService; @@ -556,49 +554,10 @@ private List orderByRemainingBalanceDescending( @Override @Transactional - public void recalculateConsumptionAmount(Expense expense, ExpenseUpdateRequestDto request, User user) { - restorePreviousGoalConsumptionAmount(expense, user); - calculatePresentGoalConsumptionAmount(request, user); - } - - private void restorePreviousGoalConsumptionAmount(Expense expense, User user) { - ConsumptionGoal previousConsumptionGoal = consumptionGoalRepository.findLatelyGoal(user.getId(), - expense.getCategory().getId(), expense.getExpenseDate().toLocalDate().withDayOfMonth(1)) - .orElseThrow(() -> new IllegalArgumentException("Not found consumptionGoal")); - - previousConsumptionGoal.restoreConsumeAmount(expense.getAmount()); - consumptionGoalRepository.save(previousConsumptionGoal); - } - - private void calculatePresentGoalConsumptionAmount(ExpenseUpdateRequestDto request, User user) { - Category categoryToReplace = categoryRepository.findById(request.getCategoryId()) - .orElseThrow(() -> new IllegalArgumentException("Not found category")); - - ConsumptionGoal consumptionGoal = consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth( - user, categoryToReplace, request.getExpenseDate().toLocalDate().withDayOfMonth(1)) - .orElseGet(() -> this.generateGoalByPreviousOrElseNew(user, categoryToReplace, - request.getExpenseDate().toLocalDate().withDayOfMonth(1))); - - consumptionGoal.updateConsumeAmount(request.getAmount()); - consumptionGoalRepository.save(consumptionGoal); - } - - private ConsumptionGoal generateGoalByPreviousOrElseNew(User user, Category category, LocalDate goalMonth) { - LocalDate previousMonth = goalMonth.minusMonths(1); - - return consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, category, previousMonth) - .map(this::generateGoalByPrevious) - .orElseGet(() -> generateNewConsumptionGoal(user, category, goalMonth)); - } - - private ConsumptionGoal generateGoalByPrevious(ConsumptionGoal consumptionGoal) { - return ConsumptionGoal.builder() - .goalMonth(consumptionGoal.getGoalMonth().plusMonths(1)) - .user(consumptionGoal.getUser()) - .category(consumptionGoal.getCategory()) - .consumeAmount(0L) - .goalAmount(consumptionGoal.getGoalAmount()) - .build(); + public void recalculateConsumptionAmount(ConsumptionGoal beforeConsumptionGoal, Long beforeAmount, + ConsumptionGoal afterConsumptionGoal, Long afterAmount) { + beforeConsumptionGoal.restoreConsumeAmount(beforeAmount); + afterConsumptionGoal.addConsumeAmount(afterAmount); } @Override @@ -609,10 +568,10 @@ public void updateConsumeAmount(Long userId, Long categoryId, Long amount) { .orElseThrow(() -> new IllegalArgumentException("Not found Category")); LocalDate thisMonth = LocalDate.now().withDayOfMonth(1); - ConsumptionGoal consumptionGoal = consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth( + ConsumptionGoal consumptionGoal = consumptionGoalRepository.findByUserAndCategoryAndGoalMonth( user, category, thisMonth).orElseGet(() -> generateNewConsumptionGoal(user, category, thisMonth)); - consumptionGoal.updateConsumeAmount(amount); + consumptionGoal.addConsumeAmount(amount); consumptionGoalRepository.save(consumptionGoal); } @@ -624,7 +583,7 @@ public void decreaseConsumeAmount(Long userId, Long categoryId, Long amount, Loc .orElseThrow(() -> new IllegalArgumentException("Not found Category")); LocalDate goalMonth = expenseDate.withDayOfMonth(1); - ConsumptionGoal consumptionGoal = consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth( + ConsumptionGoal consumptionGoal = consumptionGoalRepository.findByUserAndCategoryAndGoalMonth( user, category, goalMonth).orElseThrow(() -> new IllegalArgumentException("Not found ConsumptionGoal")); consumptionGoal.decreaseConsumeAmount(amount); @@ -640,12 +599,12 @@ public void updateOrCreateDeletedConsumptionGoal(Long userId, Long categoryId, L .orElseThrow(() -> new IllegalArgumentException("Invalid category ID")); // 해당 월의 ConsumptionGoal이 존재하는지 확인 - Optional existingGoal = consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth( + Optional existingGoal = consumptionGoalRepository.findByUserAndCategoryAndGoalMonth( user, category, goalMonth); if (existingGoal.isPresent()) { // 존재하는 경우, consumeAmount 업데이트 ConsumptionGoal consumptionGoal = existingGoal.get(); - consumptionGoal.updateConsumeAmount(amount); + consumptionGoal.addConsumeAmount(amount); consumptionGoalRepository.save(consumptionGoal); } else { // 존재하지 않는 경우, 새로운 ConsumptionGoal을 생성 (이 때 목표 금액은 0) ConsumptionGoal newGoal = ConsumptionGoal.builder() @@ -656,7 +615,7 @@ public void updateOrCreateDeletedConsumptionGoal(Long userId, Long categoryId, L .goalAmount(0L) .build(); - newGoal.updateConsumeAmount(amount); // 신규 생성된 목표에 소비 금액 추가 + newGoal.addConsumeAmount(amount); // 신규 생성된 목표에 소비 금액 추가 consumptionGoalRepository.save(newGoal); } } @@ -785,7 +744,7 @@ private String getMainComment(List list) { } } Optional minCategory = categoryRepository.findById(minCategoryId); - + if (minCategory.isEmpty()) { throw new IllegalArgumentException("해당 카테고리를 찾을 수 없습니다."); } @@ -872,4 +831,30 @@ public String getConsumptionMention(Long userId) { // return geminiService.getContents(basePrompt); } + @Override + @Transactional + public ConsumptionGoal getUserConsumptionGoal(User user, Category category, LocalDate goalDate) { + LocalDate goalMonth = goalDate.withDayOfMonth(1); + + return consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth) + .orElseGet(() -> this.generateGoalFromPreviousOrNew(user, category, goalMonth)); + } + + private ConsumptionGoal generateGoalFromPreviousOrNew(User user, Category category, LocalDate goalMonth) { + LocalDate previousMonth = goalMonth.minusMonths(1); + + return consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, previousMonth) + .map(this::generateGoalByPrevious) + .orElseGet(() -> generateNewConsumptionGoal(user, category, goalMonth)); + } + + private ConsumptionGoal generateGoalByPrevious(ConsumptionGoal consumptionGoal) { + return ConsumptionGoal.builder() + .goalMonth(consumptionGoal.getGoalMonth().plusMonths(1)) + .user(consumptionGoal.getUser()) + .category(consumptionGoal.getCategory()) + .consumeAmount(0L) + .goalAmount(consumptionGoal.getGoalAmount()) + .build(); + } } \ No newline at end of file From 65c629a486c6ca82ad0002e061dbb0e5b7306ee0 Mon Sep 17 00:00:00 2001 From: MJJ Date: Tue, 29 Oct 2024 13:27:32 +0900 Subject: [PATCH 181/204] =?UTF-8?q?[refactor]=20=20Cache=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=97=AC=20AI=20=EB=A9=98=ED=8A=B8=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 84 ++++++++++--------- .../BudgetbuddiesApplication.java | 2 + .../service/ConsumptionGoalServiceImpl.java | 3 + .../global/config/CachingConfig.java | 22 +++++ 4 files changed, 70 insertions(+), 41 deletions(-) create mode 100644 src/main/java/com/bbteam/budgetbuddies/global/config/CachingConfig.java diff --git a/build.gradle b/build.gradle index 5ae542b7..ff0fc828 100644 --- a/build.gradle +++ b/build.gradle @@ -1,67 +1,69 @@ plugins { - id 'java' - id 'org.springframework.boot' version '3.2.7' - id 'io.spring.dependency-management' version '1.1.5' - id 'com.google.cloud.tools.jib' version '3.4.3' + id 'java' + id 'org.springframework.boot' version '3.2.7' + id 'io.spring.dependency-management' version '1.1.5' + id 'com.google.cloud.tools.jib' version '3.4.3' } group = 'com.bbteam' version = '0.0.1-SNAPSHOT' java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } } configurations { - compileOnly { - extendsFrom annotationProcessor - } + compileOnly { + extendsFrom annotationProcessor + } } repositories { - mavenCentral() + mavenCentral() } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework.boot:spring-boot-starter-security' // security - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' //Swagger + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-security' // security + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' //Swagger - implementation 'net.nurigo:sdk:4.2.7' // 문자메시지 대행 서비스 + implementation 'net.nurigo:sdk:4.2.7' // 문자메시지 대행 서비스 - compileOnly 'org.projectlombok:lombok' - runtimeOnly 'com.mysql:mysql-connector-j' - annotationProcessor 'org.projectlombok:lombok' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'org.springframework.boot:spring-boot-starter-cache' // cache - implementation 'io.jsonwebtoken:jjwt-api:0.11.5' - runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' - runtimeOnly('io.jsonwebtoken:jjwt-jackson:0.11.5') // jackson으로 jwt 파싱 + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'com.mysql:mysql-connector-j' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly('io.jsonwebtoken:jjwt-jackson:0.11.5') // jackson으로 jwt 파싱 } tasks.named('test') { - useJUnitPlatform() + useJUnitPlatform() } jib { - from { - image = 'openjdk:17-alpine' - platforms { - platform { - architecture = 'amd64' - os = 'linux' - } - } - } - to { - image = 'binjumeoniz1/binjumeoniz:latest' - } - container { - jvmFlags = ['-Dspring.profiles.active=dev'] - } + from { + image = 'openjdk:17-alpine' + platforms { + platform { + architecture = 'amd64' + os = 'linux' + } + } + } + to { + image = 'binjumeoniz1/binjumeoniz:latest' + } + container { + jvmFlags = ['-Dspring.profiles.active=dev'] + } } diff --git a/src/main/java/com/bbteam/budgetbuddies/BudgetbuddiesApplication.java b/src/main/java/com/bbteam/budgetbuddies/BudgetbuddiesApplication.java index 9443b8db..8563c078 100644 --- a/src/main/java/com/bbteam/budgetbuddies/BudgetbuddiesApplication.java +++ b/src/main/java/com/bbteam/budgetbuddies/BudgetbuddiesApplication.java @@ -2,12 +2,14 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableJpaAuditing @EnableAsync +@EnableCaching public class BudgetbuddiesApplication { public static void main(String[] args) { 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 57239627..dd40480b 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 @@ -21,6 +21,7 @@ import java.util.function.Function; import java.util.stream.Collectors; +import org.springframework.cache.annotation.Cacheable; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -811,6 +812,7 @@ private String getMainComment(List list) { @Override @Async @Transactional(readOnly = true) + @Cacheable(value = "openAiResponses", key = "#userId") public CompletableFuture getConsumptionMention(Long userId) { /** @@ -869,6 +871,7 @@ public CompletableFuture getConsumptionMention(Long userId) { String response = openAiService.chat(basePrompt); + // GPT 프롬프트 실패 시 기본 멘트 생성 반환 if (response == null) { NumberFormat nf = NumberFormat.getInstance(Locale.KOREA); response = "총 " + nf.format(goalAmount - consumeAmount) + "원 더 쓸 수 있어요."; diff --git a/src/main/java/com/bbteam/budgetbuddies/global/config/CachingConfig.java b/src/main/java/com/bbteam/budgetbuddies/global/config/CachingConfig.java new file mode 100644 index 00000000..e916d36b --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/global/config/CachingConfig.java @@ -0,0 +1,22 @@ +package com.bbteam.budgetbuddies.global.config; + +import java.util.List; + +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.concurrent.ConcurrentMapCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@EnableCaching +@Configuration +public class CachingConfig { + + @Bean + public CacheManager cacheManager() { + ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); + cacheManager.setAllowNullValues(false); + cacheManager.setCacheNames(List.of("openAiResponses")); + return cacheManager; + } +} From 2fe131cc2170fc1a19bb13bb826f953589dc4b64 Mon Sep 17 00:00:00 2001 From: MJJ Date: Tue, 29 Oct 2024 14:34:08 +0900 Subject: [PATCH 182/204] =?UTF-8?q?[refactor]=20=20Cache=20value=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumptiongoal/service/ConsumptionGoalServiceImpl.java | 2 +- .../com/bbteam/budgetbuddies/global/config/CachingConfig.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 dd40480b..da24d1ed 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 @@ -812,7 +812,7 @@ private String getMainComment(List list) { @Override @Async @Transactional(readOnly = true) - @Cacheable(value = "openAiResponses", key = "#userId") + @Cacheable(value = "consumptionMent", key = "#userId") public CompletableFuture getConsumptionMention(Long userId) { /** diff --git a/src/main/java/com/bbteam/budgetbuddies/global/config/CachingConfig.java b/src/main/java/com/bbteam/budgetbuddies/global/config/CachingConfig.java index e916d36b..54ad8a20 100644 --- a/src/main/java/com/bbteam/budgetbuddies/global/config/CachingConfig.java +++ b/src/main/java/com/bbteam/budgetbuddies/global/config/CachingConfig.java @@ -16,7 +16,7 @@ public class CachingConfig { public CacheManager cacheManager() { ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); cacheManager.setAllowNullValues(false); - cacheManager.setCacheNames(List.of("openAiResponses")); + cacheManager.setCacheNames(List.of("consumptionMent")); return cacheManager; } } From 8c42b49621450152fa293fc30f1065c4e9e36bba Mon Sep 17 00:00:00 2001 From: MJJ Date: Tue, 29 Oct 2024 15:34:02 +0900 Subject: [PATCH 183/204] =?UTF-8?q?[refactor]=20=20@AuthUser=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20=EB=B0=8F=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ConsumptionGoalApi.java | 43 ++++++++----- .../controller/ConsumptionGoalController.java | 63 +++++++++++-------- 2 files changed, 67 insertions(+), 39 deletions(-) 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 69094dbc..304fd7c5 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 @@ -2,18 +2,20 @@ import java.time.LocalDate; import java.util.List; +import java.util.concurrent.ExecutionException; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PathVariable; import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AllConsumptionCategoryResponseDto; 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.MonthReportResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopCategoryConsumptionDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopGoalCategoryResponseDto; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -25,62 +27,75 @@ public interface ConsumptionGoalApi { @Operation(summary = "[User] 또래들이 가장 큰 계획을 세운 카테고리 조회 Top4", description = "특정 사용자의 소비 목표 카테고리별 소비 목표 금액을 조회하는 API 입니다.") @ApiResponses(value = { @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) - @Parameters({@Parameter(name = "userId", description = "로그인 한 유저 아이디"), + @Parameters({ @Parameter(name = "peerAgeStart", description = "또래나이 시작 범위"), @Parameter(name = "peerAgeEnd", description = "또래나이 끝 범위"), @Parameter(name = "peerGender", description = "또래 성별")}) ApiResponse> getTopConsumptionGoalCategories( - Long userId, int peerAgeStart, int peerAgeEnd, String peerGender); + UserDto.AuthUserDto user, int peerAgeStart, int peerAgeEnd, String peerGender); @Operation(summary = "[User] 또래들이 가장 많이 계획한 카테고리와 평균 금액 및 내 목표금액 차이 조회", description = "특정 사용자의 또래 소비 카테고리별 평균 목표 금액을 조회하는 API 입니다.") @ApiResponses(value = { @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) - @Parameters({@Parameter(name = "userId", description = "로그인 한 유저 아이디"), + @Parameters({ @Parameter(name = "peerAgeStart", description = "또래나이 시작 범위"), @Parameter(name = "peerAgeEnd", description = "또래나이 끝 범위"), @Parameter(name = "peerGender", description = "또래 성별")}) - ApiResponse> getAllConsumptionGoalCategories(Long userId, int peerAgeStart, + ApiResponse> getAllConsumptionGoalCategories(UserDto.AuthUserDto user, + int peerAgeStart, int peerAgeEnd, String peerGender); @Operation(summary = "[User] 또래나이와 성별 조회", description = "또래나이와 성별을 조회하는 API 입니다.") @ApiResponses(value = { @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) - @Parameters({@Parameter(name = "userId", description = "로그인 한 유저 아이디"), + @Parameters({ @Parameter(name = "peerAgeStart", description = "또래나이 시작 범위"), @Parameter(name = "peerAgeEnd", description = "또래나이 끝 범위"), @Parameter(name = "peerGender", description = "또래 성별")}) - ApiResponse getPeerInfo(Long userId, int peerAgeStart, int peerAgeEnd, String peerGender); + ApiResponse getPeerInfo(UserDto.AuthUserDto user, int peerAgeStart, int peerAgeEnd, + String peerGender); @Operation(summary = "[User] 소비 목표 조회", description = "date={yyyy-MM-dd} 형식의 query string을 통해서 사용자의 목표 달을 조회하는 API 입니다.") @Parameters({@Parameter(name = "date", description = "yyyy-MM-dd 형식으로 목표 달의 소비를 조회")}) - ApiResponse findUserConsumptionGoal(LocalDate date, Long userId); + ApiResponse findUserConsumptionGoal(LocalDate date, UserDto.AuthUserDto user); @Operation(summary = "[User] 이번 달 소비 목표 수정", description = "다른 달의 소비 목표를 업데이트하는 것은 불가능하고 오직 이번 달의 소비 목표만 업데이트 하는 API 입니다.") - ResponseEntity updateOrElseGenerateConsumptionGoal(Long userId, + ResponseEntity updateOrElseGenerateConsumptionGoal(UserDto.AuthUserDto user, ConsumptionGoalListRequestDto consumptionGoalListRequestDto); @Operation(summary = "[User] 또래들이 가장 많이한 소비 카테고리 조회 Top3", description = "특정 사용자의 또래 소비 카테고리별 소비 건 수을 조회하는 API 입니다.") @ApiResponses(value = { @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) - @Parameters({@Parameter(name = "userId", description = "로그인 한 유저 아이디"), + @Parameters({ @Parameter(name = "peerAgeStart", description = "또래나이 시작 범위"), @Parameter(name = "peerAgeEnd", description = "또래나이 끝 범위"), @Parameter(name = "peerGender", description = "또래 성별")}) - ApiResponse> getTopConsumptionCategories(Long userId, int peerAgeStart, + ApiResponse> getTopConsumptionCategories(UserDto.AuthUserDto user, int peerAgeStart, int peerAgeEnd, String peerGender); @Operation(summary = "[User] 또래들이 가장 많이한 소비 카테고리와 평균 금액 및 내 소비금액 차이 조회", description = "특정 사용자의 또래 소비 카테고리별 평균 소비 금액을 조회하는 API 입니다.") @ApiResponses(value = { @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) - @Parameters({@Parameter(name = "userId", description = "로그인 한 유저 아이디"), + @Parameters({ @Parameter(name = "peerAgeStart", description = "또래나이 시작 범위"), @Parameter(name = "peerAgeEnd", description = "또래나이 끝 범위"), @Parameter(name = "peerGender", description = "또래 성별")}) - ApiResponse> getAllConsumptionCategories(Long userId, int peerAgeStart, + ApiResponse> getAllConsumptionCategories(UserDto.AuthUserDto user, + int peerAgeStart, int peerAgeEnd, String peerGender); @Operation(summary = "[User] 또래들이 가장 큰 목표로 세운 카테고리와 그 카테고리에서 이번주 사용한 금액 조회", description = "특정 사용자의 또래 소비 카테고리별 이번주 소비 금액을 조회하는 API 입니다.") @ApiResponses(value = { @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) - ApiResponse getTopCategoryAndConsumptionAmount(@PathVariable Long userId); + ApiResponse getTopCategoryAndConsumptionAmount(UserDto.AuthUserDto user); + + @Operation(summary = "[User] 이번 달 레포트 표정, 멘트 조회 ", description = "특정 사용자의 이번 달 레포트 표정, 멘트를 조회하는 API 입니다.") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) + ApiResponse getMonthReport(UserDto.AuthUserDto user); + + @Operation(summary = "[User] 소비 분석 멘트 생성", description = "특정 사용자의 소비 분석 멘트를 생성 하는 API 입니다.") + @ApiResponses(value = { + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) + ApiResponse getConsumptionMention(UserDto.AuthUserDto user) throws ExecutionException, InterruptedException; } \ No newline at end of file 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 9a85ba0c..4424cc9a 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 @@ -8,7 +8,6 @@ import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -25,6 +24,8 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopCategoryConsumptionDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopGoalCategoryResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.service.ConsumptionGoalService; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; +import com.bbteam.budgetbuddies.global.security.utils.AuthUser; import lombok.RequiredArgsConstructor; @@ -38,95 +39,107 @@ public class ConsumptionGoalController implements ConsumptionGoalApi { @Override @GetMapping("/categories/top-goals/top-4") public ApiResponse> getTopConsumptionGoalCategories( - @RequestParam(name = "userId") Long userId, + @AuthUser UserDto.AuthUserDto user, @RequestParam(name = "peerAgeStart", defaultValue = "0") int peerAgeStart, @RequestParam(name = "peerAgeEnd", defaultValue = "0") int peerAgeEnd, @RequestParam(name = "peerGender", defaultValue = "none") String peerGender) { List response = consumptionGoalService.getTopConsumptionGoalCategories( - userId, peerAgeStart, peerAgeEnd, peerGender); + user.getId(), peerAgeStart, peerAgeEnd, peerGender); return ApiResponse.onSuccess(response); } + @Override @GetMapping("/categories/top-goals") public ApiResponse> getAllConsumptionGoalCategories( - @RequestParam(name = "userId") Long userId, + @AuthUser UserDto.AuthUserDto user, @RequestParam(name = "peerAgeStart", defaultValue = "0") int peerAgeStart, @RequestParam(name = "peerAgeEnd", defaultValue = "0") int peerAgeEnd, @RequestParam(name = "peerGender", defaultValue = "none") String peerGender) { List response = consumptionGoalService.getAllConsumptionGoalCategories( - userId, + user.getId(), peerAgeStart, peerAgeEnd, peerGender); return ApiResponse.onSuccess(response); } @Override @GetMapping("/peer-info") - public ApiResponse getPeerInfo(@RequestParam(name = "userId") Long userId, + public ApiResponse getPeerInfo( + @AuthUser UserDto.AuthUserDto user, @RequestParam(name = "peerAgeStart", defaultValue = "0") int peerAgeStart, @RequestParam(name = "peerAgeEnd", defaultValue = "0") int peerAgeEnd, @RequestParam(name = "peerGender", defaultValue = "none") String peerGender) { - PeerInfoResponseDto response = consumptionGoalService.getPeerInfo(userId, peerAgeStart, peerAgeEnd, peerGender); + PeerInfoResponseDto response = consumptionGoalService.getPeerInfo(user.getId(), peerAgeStart, peerAgeEnd, + peerGender); return ApiResponse.onSuccess(response); } + @Override @GetMapping("/{userId}") public ApiResponse findUserConsumptionGoal( - @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date, @PathVariable Long userId) { - - ConsumptionGoalResponseListDto response = consumptionGoalService.findUserConsumptionGoalList(userId, date); + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date, + @AuthUser UserDto.AuthUserDto user) { + ConsumptionGoalResponseListDto response = consumptionGoalService.findUserConsumptionGoalList(user.getId(), + date); return ApiResponse.onSuccess(response); } @Override @PostMapping("/{userId}") - public ResponseEntity updateOrElseGenerateConsumptionGoal(@PathVariable Long userId, + public ResponseEntity updateOrElseGenerateConsumptionGoal( + @AuthUser UserDto.AuthUserDto user, @RequestBody ConsumptionGoalListRequestDto consumptionGoalListRequestDto) { - return ResponseEntity.ok() - .body(consumptionGoalService.updateConsumptionGoals(userId, consumptionGoalListRequestDto)); + .body(consumptionGoalService.updateConsumptionGoals(user.getId(), consumptionGoalListRequestDto)); } + @Override @GetMapping("/categories/top-consumptions/top-3") public ApiResponse> getTopConsumptionCategories( - @RequestParam(name = "userId") Long userId, + @AuthUser UserDto.AuthUserDto user, @RequestParam(name = "peerAgeStart", defaultValue = "0") int peerAgeStart, @RequestParam(name = "peerAgeEnd", defaultValue = "0") int peerAgeEnd, @RequestParam(name = "peerGender", defaultValue = "none") String peerGender) { - List response = consumptionGoalService.getTopConsumptionCategories(userId, + List response = consumptionGoalService.getTopConsumptionCategories(user.getId(), peerAgeStart, peerAgeEnd, peerGender); return ApiResponse.onSuccess(response); } + @Override @GetMapping("/categories/top-consumptions") public ApiResponse> getAllConsumptionCategories( - @RequestParam(name = "userId") Long userId, + @AuthUser UserDto.AuthUserDto user, @RequestParam(name = "peerAgeStart", defaultValue = "0") int peerAgeStart, @RequestParam(name = "peerAgeEnd", defaultValue = "0") int peerAgeEnd, @RequestParam(name = "peerGender", defaultValue = "none") String peerGender) { - List response = consumptionGoalService.getAllConsumptionCategories(userId, + List response = consumptionGoalService.getAllConsumptionCategories( + user.getId(), peerAgeStart, peerAgeEnd, peerGender); return ApiResponse.onSuccess(response); } + @Override @GetMapping("/category/top-goals") public ApiResponse getTopCategoryAndConsumptionAmount( - @RequestParam(name = "userId") Long userId) { - ConsumptionAnalysisResponseDto response = consumptionGoalService.getTopCategoryAndConsumptionAmount(userId); + @AuthUser UserDto.AuthUserDto user) { + ConsumptionAnalysisResponseDto response = consumptionGoalService.getTopCategoryAndConsumptionAmount( + user.getId()); return ApiResponse.onSuccess(response); } + @Override @GetMapping("/facialExpressions") - public ApiResponse getMonthReport(@RequestParam(name = "userId") Long userId) { - MonthReportResponseDto response = consumptionGoalService.getMonthReport(userId); + public ApiResponse getMonthReport( + @AuthUser UserDto.AuthUserDto user) { + MonthReportResponseDto response = consumptionGoalService.getMonthReport(user.getId()); return ApiResponse.onSuccess(response); } + @Override @GetMapping("/consumption-ment") - public ApiResponse getConsumptionMention(@RequestParam(name = "userId") Long userId) throws - ExecutionException, - InterruptedException { - CompletableFuture response = consumptionGoalService.getConsumptionMention(userId); + public ApiResponse getConsumptionMention( + @AuthUser UserDto.AuthUserDto user) throws ExecutionException, InterruptedException { + CompletableFuture response = consumptionGoalService.getConsumptionMention(user.getId()); return ApiResponse.onSuccess(response.get()); } } \ No newline at end of file From bce3d383f850fb78580ec0990663b06e0a54b4f5 Mon Sep 17 00:00:00 2001 From: MJJ Date: Tue, 29 Oct 2024 15:36:40 +0900 Subject: [PATCH 184/204] =?UTF-8?q?[refactor]=20=20=EC=97=94=EB=93=9C?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumptiongoal/controller/ConsumptionGoalController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4424cc9a..5dbf5e05 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 @@ -128,7 +128,7 @@ public ApiResponse getTopCategoryAndConsumptionA } @Override - @GetMapping("/facialExpressions") + @GetMapping("/month-report") public ApiResponse getMonthReport( @AuthUser UserDto.AuthUserDto user) { MonthReportResponseDto response = consumptionGoalService.getMonthReport(user.getId()); From 3afd8d0378f8295da7877d20b53cbe03b9fb1d2e Mon Sep 17 00:00:00 2001 From: MJJ Date: Tue, 29 Oct 2024 15:40:45 +0900 Subject: [PATCH 185/204] =?UTF-8?q?[refactor]=20=EC=86=8C=EB=B9=84?= =?UTF-8?q?=EB=AA=A9=ED=91=9C=20=EC=A1=B0=ED=9A=8C/=20=EC=9D=B4=EB=B2=88?= =?UTF-8?q?=20=EB=8B=AC=20=EC=86=8C=EB=B9=84=20=EB=AA=A9=ED=91=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20userId=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumptiongoal/controller/ConsumptionGoalController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 5dbf5e05..6a18274e 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 @@ -74,7 +74,7 @@ public ApiResponse getPeerInfo( } @Override - @GetMapping("/{userId}") + @GetMapping() public ApiResponse findUserConsumptionGoal( @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date, @AuthUser UserDto.AuthUserDto user) { @@ -85,7 +85,7 @@ public ApiResponse findUserConsumptionGoal( } @Override - @PostMapping("/{userId}") + @PostMapping() public ResponseEntity updateOrElseGenerateConsumptionGoal( @AuthUser UserDto.AuthUserDto user, @RequestBody ConsumptionGoalListRequestDto consumptionGoalListRequestDto) { From 67d98e2cdfce52b03980d6101e401fee70ef4fab Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 29 Oct 2024 21:18:09 +0900 Subject: [PATCH 186/204] =?UTF-8?q?[test]=20ConsumptionGoalServiceImplTest?= =?UTF-8?q?=20=EC=86=8C=EB=B9=84=EB=AA=A9=ED=91=9C=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ConsumptionGoalServiceTest.java | 179 +++++------------- 1 file changed, 46 insertions(+), 133 deletions(-) diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java index 49346396..0b7dcf3d 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java @@ -18,7 +18,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; @@ -41,8 +40,6 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopCategoryConsumptionDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; import com.bbteam.budgetbuddies.domain.consumptiongoal.repository.ConsumptionGoalRepository; -import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; -import com.bbteam.budgetbuddies.domain.expense.entity.Expense; import com.bbteam.budgetbuddies.domain.expense.repository.ExpenseRepository; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; @@ -230,10 +227,10 @@ void updateConsumptionGoal_Success() { .category(defaultCategory) .goalMonth(thisMonth) .build(); - given(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, defaultCategory, + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, defaultCategory, thisMonth)).willReturn(Optional.ofNullable(defaultCategoryGoal)); - given(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, userCategory, + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, userCategory, thisMonth)).willReturn(Optional.ofNullable(null)); when(consumptionGoalRepository.saveAll(any())).thenAnswer(invocation -> { @@ -515,166 +512,82 @@ void getAllConsumptionCategories_Success() { } @Test - @DisplayName("지난 달, 이번 달 소비 목표가 없는 카테고리에 대한 소비 업데이트를 진행하는 경우 새로운 소비 목표를 생성해 소비 금액을 갱신") - void recalculateConsumptionAmount_notExistPreviousMonthAndThisMonthGoal() { + void 이번달_사용자_소비목표_조회하기() { // given - Category existGoalCategory = Category.builder().name("유저 카테고리").user(user).isDefault(false).build(); - Category notExistGoalCategory = Mockito.spy(Category.builder().name("디폴트 카테고리").isDefault(true).build()); - given(notExistGoalCategory.getId()).willReturn(-1L); - - Expense expense = Mockito.spy( - Expense.builder().category(existGoalCategory).expenseDate(GOAL_MONTH.atStartOfDay()).amount(1000L).build()); - when(expense.getId()).thenReturn(-1L); - - ExpenseUpdateRequestDto request = ExpenseUpdateRequestDto.builder() - .amount(1000L) - .expenseId(expense.getId()) - .expenseDate(LocalDate.of(2024, 8, 7).atStartOfDay()) - .categoryId(notExistGoalCategory.getId()) - .build(); + Category category = Mockito.spy(Category.builder().name("TEST CATEGORY").user(user).isDefault(false).build()); + LocalDate goalMonth = LocalDate.of(2024, 7, 1); - ConsumptionGoal oldGoal = ConsumptionGoal.builder().consumeAmount(1000L).category(existGoalCategory).build(); - ConsumptionGoal expected = ConsumptionGoal.builder() - .goalMonth(LocalDate.of(2024, 8, 1)) - .goalAmount(0L) - .consumeAmount(1000L) - .category(notExistGoalCategory) + ConsumptionGoal userConsumptionGoal = ConsumptionGoal.builder() + .goalAmount(1_000_000L) + .consumeAmount(200_000L) .user(user) + .category(category) + .goalMonth(goalMonth) .build(); - // when - when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, expense.getCategory(), - expense.getExpenseDate().toLocalDate().withDayOfMonth(1))).thenReturn(Optional.ofNullable(oldGoal)); - - when(categoryRepository.findById(request.getCategoryId())).thenReturn(Optional.of(notExistGoalCategory)); - when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, notExistGoalCategory, - request.getExpenseDate().toLocalDate().withDayOfMonth(1))).thenReturn(Optional.empty()); - - when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, notExistGoalCategory, - request.getExpenseDate().minusMonths(1).toLocalDate().withDayOfMonth(1))).thenReturn(Optional.empty()); - - consumptionGoalService.recalculateConsumptionAmount(expense, request, user); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth)) + .willReturn(Optional.of(userConsumptionGoal)); - ArgumentCaptor consumptionGoalCaptor = ArgumentCaptor.forClass(ConsumptionGoal.class); - verify(consumptionGoalRepository, times(2)).save(consumptionGoalCaptor.capture()); - - List savedConsumptionGoals = consumptionGoalCaptor.getAllValues(); + // when + ConsumptionGoal result = consumptionGoalService.getUserConsumptionGoal(user, category, goalMonth); // then - assertEquals(oldGoal.getConsumeAmount(), 0L); - assertThat(savedConsumptionGoals.get(1)).usingRecursiveComparison().isEqualTo(expected); + assertEquals(result, userConsumptionGoal); } @Test - @DisplayName("이번달 소비 목표가 없는 카테고리에 대한 소비 업데이트를 진행하는 경우 지난 달 소비 목표의 목표 금액을 복사한 이번 달 소비 목표를 생성해 소비 금액을 갱신") - void recalculateConsumptionAmount_notExistThisMonthGoal() { + void 이번달_소비목표가_없는경우_저번달_기준으로_소비목표_생성() { // given - Category existGoalCategory = Category.builder().name("유저 카테고리").user(user).isDefault(false).build(); - Category notExistThisMonthGoalCategory = Mockito.spy( - Category.builder().name("디폴트 카테고리").isDefault(true).build()); - given(notExistThisMonthGoalCategory.getId()).willReturn(-1L); - - Expense expense = Mockito.spy( - Expense.builder().category(existGoalCategory).expenseDate(GOAL_MONTH.atStartOfDay()).amount(1000L).build()); - when(expense.getId()).thenReturn(-1L); - - ExpenseUpdateRequestDto request = ExpenseUpdateRequestDto.builder() - .amount(1000L) - .expenseId(expense.getId()) - .expenseDate(LocalDate.of(2024, 8, 7).atStartOfDay()) - .categoryId(notExistThisMonthGoalCategory.getId()) - .build(); + Category category = Mockito.spy(Category.builder().name("TEST CATEGORY").user(user).isDefault(false).build()); + LocalDate goalMonth = LocalDate.of(2024, 7, 1); - ConsumptionGoal oldGoal = ConsumptionGoal.builder().consumeAmount(1000L).category(existGoalCategory).build(); - - ConsumptionGoal previousMonthGoal = ConsumptionGoal.builder() - .goalMonth(LocalDate.of(2024, 7, 1)) - .goalAmount(3000L) - .consumeAmount(3000L) - .category(notExistThisMonthGoalCategory) + ConsumptionGoal beforeUserConsumptionGoal = ConsumptionGoal.builder() + .goalAmount(1_000_000L) + .consumeAmount(200_000L) .user(user) + .category(category) + .goalMonth(LocalDate.of(2024, 6, 1)) .build(); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth)) + .willReturn(Optional.empty()); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth.minusMonths(1))) + .willReturn(Optional.of(beforeUserConsumptionGoal)); ConsumptionGoal expected = ConsumptionGoal.builder() - .goalMonth(LocalDate.of(2024, 8, 1)) - .goalAmount(3000L) - .consumeAmount(1000L) - .category(notExistThisMonthGoalCategory) + .goalAmount(1_000_000L) + .consumeAmount(0L) .user(user) + .category(category) + .goalMonth(LocalDate.of(2024, 7, 1)) .build(); - // when - when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, expense.getCategory(), - expense.getExpenseDate().toLocalDate().withDayOfMonth(1))).thenReturn(Optional.ofNullable(oldGoal)); - - when(categoryRepository.findById(request.getCategoryId())).thenReturn( - Optional.of(notExistThisMonthGoalCategory)); - when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, - notExistThisMonthGoalCategory, request.getExpenseDate().toLocalDate().withDayOfMonth(1))).thenReturn( - Optional.empty()); - - when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, - notExistThisMonthGoalCategory, - request.getExpenseDate().minusMonths(1).toLocalDate().withDayOfMonth(1))).thenReturn( - Optional.ofNullable(previousMonthGoal)); - - consumptionGoalService.recalculateConsumptionAmount(expense, request, user); - - ArgumentCaptor consumptionGoalCaptor = ArgumentCaptor.forClass(ConsumptionGoal.class); - verify(consumptionGoalRepository, times(2)).save(consumptionGoalCaptor.capture()); - - List savedConsumptionGoals = consumptionGoalCaptor.getAllValues(); + ConsumptionGoal result = consumptionGoalService.getUserConsumptionGoal(user, category, goalMonth); // then - assertEquals(oldGoal.getConsumeAmount(), 0L); - assertThat(savedConsumptionGoals.get(1)).usingRecursiveComparison().isEqualTo(expected); + assertThat(result).usingRecursiveComparison().isEqualTo(expected); } @Test - @DisplayName("이번달 소비 목표가 있는 경우 이번 달 소비 목표의 소비 금액을 갱신") - void recalculateConsumptionAmount_existThisMonthGoal() { + void 이번달_저번달_소비목표가_없는경우_소비목표를_새로_생성() { // given - Category existGoalCategory = Mockito.spy( - Category.builder().name("유저 카테고리").user(user).isDefault(false).build()); - given(existGoalCategory.getId()).willReturn(-1L); - - Expense expense = Mockito.spy( - Expense.builder().category(existGoalCategory).expenseDate(GOAL_MONTH.atStartOfDay()).amount(1000L).build()); - when(expense.getId()).thenReturn(-1L); - - ExpenseUpdateRequestDto request = ExpenseUpdateRequestDto.builder() - .amount(2000L) - .expenseId(expense.getId()) - .expenseDate(LocalDate.of(2024, 8, 7).atStartOfDay()) - .categoryId(existGoalCategory.getId()) - .build(); + Category category = Mockito.spy(Category.builder().name("TEST CATEGORY").user(user).isDefault(false).build()); + LocalDate goalMonth = LocalDate.of(2024, 7, 1); - ConsumptionGoal oldGoal = ConsumptionGoal.builder() - .goalMonth(LocalDate.of(2024, 8, 1)) - .goalAmount(3000L) - .consumeAmount(1000L) - .category(existGoalCategory) - .user(user) - .build(); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth)) + .willReturn(Optional.empty()); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth.minusMonths(1))) + .willReturn(Optional.empty()); ConsumptionGoal expected = ConsumptionGoal.builder() - .goalMonth(LocalDate.of(2024, 8, 1)) - .goalAmount(3000L) - .consumeAmount(2000L) - .category(existGoalCategory) + .goalAmount(0L) + .consumeAmount(0L) .user(user) + .category(category) + .goalMonth(LocalDate.of(2024, 7, 1)) .build(); - // when - when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, expense.getCategory(), - expense.getExpenseDate().toLocalDate().withDayOfMonth(1))).thenReturn(Optional.ofNullable(oldGoal)); - - when(categoryRepository.findById(request.getCategoryId())).thenReturn(Optional.of(existGoalCategory)); - when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, existGoalCategory, - request.getExpenseDate().toLocalDate().withDayOfMonth(1))).thenReturn(Optional.ofNullable(oldGoal)); - - consumptionGoalService.recalculateConsumptionAmount(expense, request, user); + ConsumptionGoal result = consumptionGoalService.getUserConsumptionGoal(user, category, goalMonth); // then - assertThat(oldGoal).usingRecursiveComparison().isEqualTo(expected); + assertThat(result).usingRecursiveComparison().isEqualTo(expected); } } \ No newline at end of file From d13c04815d5234c4504d8c5de41a1cfe3b157cc4 Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 29 Oct 2024 21:25:42 +0900 Subject: [PATCH 187/204] =?UTF-8?q?[test]=20ExpenseServiceImplTest=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ExpenseServiceImplTest.java | 62 +++++++++---------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImplTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImplTest.java index fd1a8d5f..3de8bbed 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImplTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImplTest.java @@ -24,7 +24,7 @@ import com.bbteam.budgetbuddies.domain.expense.converter.ExpenseConverter; import com.bbteam.budgetbuddies.domain.expense.dto.CompactExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.dto.DailyExpenseResponseDto; -import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseResponseDto; +import com.bbteam.budgetbuddies.domain.expense.dto.DetailExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.dto.MonthlyExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.entity.Expense; import com.bbteam.budgetbuddies.domain.expense.repository.ExpenseRepository; @@ -79,32 +79,29 @@ private MonthlyExpenseResponseDto getExpectedMonthlyExpense(Category userCategor return MonthlyExpenseResponseDto.builder() .expenseMonth(LocalDate.of(2024, 7, 1)) .totalConsumptionAmount(300_000L) - .dailyExpenses(List.of(DailyExpenseResponseDto.builder() - .daysOfMonth(2) - .daysOfTheWeek("화요일") - .expenses(List.of(CompactExpenseResponseDto.builder() - .amount(200_000L) - .description("User 소비") - .expenseId(-2L) - .categoryId(userCategory.getId()) - .build())) - .build(), DailyExpenseResponseDto.builder() - .daysOfMonth(1) - .daysOfTheWeek("월요일") - .expenses(List.of(CompactExpenseResponseDto.builder() - .amount(100_000L) - .description("User 소비") - .expenseId(-1L) - .categoryId(userCategory.getId()) - .build())) - .build())) + .dailyExpenses( + List.of( + DailyExpenseResponseDto.builder() + .daysOfMonth(2) + .daysOfTheWeek("화요일") + .expenses(List.of(CompactExpenseResponseDto.builder() + .amount(200_000L) + .description("User 소비") + .expenseId(-2L) + .categoryId(userCategory.getId()) + .build())) + .build(), + DailyExpenseResponseDto.builder() + .daysOfMonth(1) + .daysOfTheWeek("월요일") + .expenses(List.of(CompactExpenseResponseDto.builder() + .amount(100_000L) + .description("User 소비") + .expenseId(-1L) + .categoryId(userCategory.getId()) + .build())) + .build())) .build(); - - // when - MonthlyExpenseResponseDto result = expenseService.getMonthlyExpense(user.getId(), requestMonth); - - // then - assertThat(result).usingRecursiveComparison().isEqualTo(expected); } private List generateExpenseList(LocalDate month, User user, Category userCategory) { @@ -130,8 +127,7 @@ private List generateExpenseList(LocalDate month, User user, Category u } @Test - @DisplayName("findExpenseFromUserIdAndExpenseId : 성공") - void findExpenseResponseFromUserIdAndExpenseId_Success() { + void 소비상세조회_성공() { // given final Long expenseId = -1L; @@ -142,8 +138,7 @@ void findExpenseResponseFromUserIdAndExpenseId_Success() { given(expense.getId()).willReturn(expenseId); given(expenseRepository.findById(expense.getId())).willReturn(Optional.of(expense)); - ExpenseResponseDto expected = ExpenseResponseDto.builder() - .userId(user.getId()) + DetailExpenseResponseDto expected = DetailExpenseResponseDto.builder() .expenseId(-1L) .description("유저 소비") .categoryName("유저 카테고리") @@ -151,15 +146,14 @@ void findExpenseResponseFromUserIdAndExpenseId_Success() { .build(); // when - ExpenseResponseDto result = expenseService.findExpenseResponseFromUserIdAndExpenseId(user.getId(), expenseId); + DetailExpenseResponseDto result = expenseService.findDetailExpenseResponse(user.getId(), expenseId); // then assertThat(result).usingRecursiveComparison().isEqualTo(expected); } @Test - @DisplayName("findExpenseFromUserIdAndExpenseId : 소비 유저와 다른 유저로 인한 예외 반환") - void findExpenseResponseFromUserIdAndExpenseId_Fail() { + void 조회_권한이_없는_유저의_소비조회로_인한_예외_반환() { // given final Long expenseId = -1L; @@ -171,6 +165,6 @@ void findExpenseResponseFromUserIdAndExpenseId_Fail() { // then assertThrows(IllegalArgumentException.class, - () -> expenseService.findExpenseResponseFromUserIdAndExpenseId(-2L, expenseId)); + () -> expenseService.findDetailExpenseResponse(-2L, expenseId)); } } \ No newline at end of file From c5e071b3f06072a1a677b6f329794a93b012a3a8 Mon Sep 17 00:00:00 2001 From: JunRain Date: Wed, 30 Oct 2024 22:17:45 +0900 Subject: [PATCH 188/204] =?UTF-8?q?[refactor]=20ConsumptionGoalController?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=86=8C=EB=B9=84=EB=AA=A9?= =?UTF-8?q?=ED=91=9C=20=EC=A1=B0=ED=9A=8C=20@AuthUser=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumptiongoal/controller/ConsumptionGoalApi.java | 3 ++- .../controller/ConsumptionGoalController.java | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) 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 69094dbc..abdda415 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 @@ -14,6 +14,7 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopCategoryConsumptionDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopGoalCategoryResponseDto; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -53,7 +54,7 @@ ApiResponse> getAllConsumptionGoalCatego @Operation(summary = "[User] 소비 목표 조회", description = "date={yyyy-MM-dd} 형식의 query string을 통해서 사용자의 목표 달을 조회하는 API 입니다.") @Parameters({@Parameter(name = "date", description = "yyyy-MM-dd 형식으로 목표 달의 소비를 조회")}) - ApiResponse findUserConsumptionGoal(LocalDate date, Long userId); + ApiResponse findUserConsumptionGoal(LocalDate date, UserDto.AuthUserDto user); @Operation(summary = "[User] 이번 달 소비 목표 수정", description = "다른 달의 소비 목표를 업데이트하는 것은 불가능하고 오직 이번 달의 소비 목표만 업데이트 하는 API 입니다.") ResponseEntity updateOrElseGenerateConsumptionGoal(Long userId, 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 4a75809d..aec1b5a2 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 @@ -23,6 +23,8 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopCategoryConsumptionDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopGoalCategoryResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.service.ConsumptionGoalService; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; +import com.bbteam.budgetbuddies.global.security.utils.AuthUser; import lombok.RequiredArgsConstructor; @@ -67,13 +69,11 @@ public ApiResponse getPeerInfo(@RequestParam(name = "userId return ApiResponse.onSuccess(response); } - @GetMapping("/{userId}") + @GetMapping() public ApiResponse findUserConsumptionGoal( - @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date, @PathVariable Long userId) { + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date, @AuthUser UserDto.AuthUserDto user) { - ConsumptionGoalResponseListDto response = consumptionGoalService.findUserConsumptionGoalList(userId, date); - - return ApiResponse.onSuccess(response); + return ApiResponse.onSuccess(consumptionGoalService.findUserConsumptionGoalList(user.getId(), date)); } @Override From a7dca1017be57ba15b2f519270607259893f11b6 Mon Sep 17 00:00:00 2001 From: JunRain Date: Wed, 30 Oct 2024 22:19:25 +0900 Subject: [PATCH 189/204] =?UTF-8?q?[feat]=20CategoryService=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20Category=20=EC=97=94=ED=8B=B0=ED=8B=B0=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/category/service/CategoryService.java | 2 ++ .../domain/category/service/CategoryServiceImpl.java | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryService.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryService.java index 675d1581..0a75b0c9 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryService.java @@ -17,5 +17,7 @@ public interface CategoryService { void deleteCategory(Long id, Long userId); Category getCategory(Long categoryId); + + List getUserCategoryList(Long userId); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryServiceImpl.java index c8dab75f..49126290 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryServiceImpl.java @@ -154,4 +154,8 @@ public void deleteCategory(Long categoryId, Long userId) { }); } + @Override + public List getUserCategoryList(Long userId) { + return categoryRepository.findUserCategoryByUserId(userId); + } } \ No newline at end of file From 293c6a64b784591b0651351fc1ed07057a80241a Mon Sep 17 00:00:00 2001 From: JunRain Date: Wed, 30 Oct 2024 22:22:20 +0900 Subject: [PATCH 190/204] =?UTF-8?q?[refactor]=20ConsumptionGoalService=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=86=8C=EB=B9=84=EB=AA=A9?= =?UTF-8?q?=ED=91=9C=20=EC=A1=B0=ED=9A=8C=20=EA=B5=AC=ED=98=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JPA 쿼리 관련 최적화를 진행해야할 것 같음. 모든 카테고리에 대한 이번달 저번달 소비목표가 없는 경우 2N(카테고리 수)만큼 쿼리문이 나가게 된다. --- .../converter/ConsumptionGoalConverter.java | 34 ++++--------- .../service/ConsumptionGoalServiceImpl.java | 50 ++++--------------- 2 files changed, 22 insertions(+), 62 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java index 5cd7a72a..44c0dd08 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java @@ -1,11 +1,11 @@ package com.bbteam.budgetbuddies.domain.consumptiongoal.converter; import java.time.LocalDate; +import java.util.Comparator; import java.util.List; import org.springframework.stereotype.Component; -import com.bbteam.budgetbuddies.domain.category.entity.Category; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionAnalysisResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto; @@ -17,15 +17,6 @@ @Component public class ConsumptionGoalConverter { - public ConsumptionGoalResponseDto toConsumptionGoalResponseDto(Category category) { - return ConsumptionGoalResponseDto.builder() - .categoryName(category.getName()) - .categoryId(category.getId()) - .goalAmount(0L) - .consumeAmount(0L) - .build(); - } - public ConsumptionGoalResponseDto toConsumptionGoalResponseDto(ConsumptionGoal consumptionGoal) { return ConsumptionGoalResponseDto.builder() .categoryName(consumptionGoal.getCategory().getName()) @@ -35,27 +26,24 @@ public ConsumptionGoalResponseDto toConsumptionGoalResponseDto(ConsumptionGoal c .build(); } - public ConsumptionGoalResponseDto toConsumptionGoalResponseDtoFromPreviousGoal(ConsumptionGoal consumptionGoal) { - return ConsumptionGoalResponseDto.builder() - .categoryName(consumptionGoal.getCategory().getName()) - .categoryId(consumptionGoal.getCategory().getId()) - .goalAmount(consumptionGoal.getGoalAmount()) - .consumeAmount(0L) - .build(); + public ConsumptionGoalResponseListDto toConsumptionGoalResponseListDto( + List consumptionGoalList, LocalDate goalMonth) { - } + List consumptionGoalResponseList = consumptionGoalList + .stream() + .map(this::toConsumptionGoalResponseDto) + .sorted(Comparator.comparingLong(ConsumptionGoalResponseDto::getRemainingBalance).reversed()) + .toList(); - public ConsumptionGoalResponseListDto toConsumptionGoalResponseListDto( - List consumptionGoalList, LocalDate goalMonth) { - Long totalGoalAmount = sumTotalGoalAmount(consumptionGoalList); - Long totalConsumptionAmount = sumTotalConsumptionAmount(consumptionGoalList); + Long totalGoalAmount = sumTotalGoalAmount(consumptionGoalResponseList); + Long totalConsumptionAmount = sumTotalConsumptionAmount(consumptionGoalResponseList); return ConsumptionGoalResponseListDto.builder() .goalMonth(goalMonth) .totalGoalAmount(totalGoalAmount) .totalConsumptionAmount(totalConsumptionAmount) .totalRemainingBalance(totalGoalAmount - totalConsumptionAmount) - .consumptionGoalList(consumptionGoalList) + .consumptionGoalList(consumptionGoalResponseList) .build(); } 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 67d681ce..c7ff3991 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 @@ -25,6 +25,7 @@ import com.bbteam.budgetbuddies.domain.category.entity.Category; import com.bbteam.budgetbuddies.domain.category.repository.CategoryRepository; +import com.bbteam.budgetbuddies.domain.category.service.CategoryService; import com.bbteam.budgetbuddies.domain.consumptiongoal.converter.ConsumptionGoalConverter; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AllConsumptionCategoryResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AvgConsumptionGoalDto; @@ -47,6 +48,7 @@ import com.bbteam.budgetbuddies.domain.openai.service.OpenAiService; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import com.bbteam.budgetbuddies.domain.user.service.UserService; import com.bbteam.budgetbuddies.enums.Gender; import lombok.RequiredArgsConstructor; @@ -60,7 +62,9 @@ public class ConsumptionGoalServiceImpl implements ConsumptionGoalService { private final ConsumptionGoalRepository consumptionGoalRepository; private final ExpenseRepository expenseRepository; private final CategoryRepository categoryRepository; + private final CategoryService categoryService; private final UserRepository userRepository; + private final UserService userService; private final GeminiService geminiService; private final OpenAiService openAiService; @@ -506,50 +510,18 @@ private ConsumptionGoal generateNewConsumptionGoal(User user, Category category, } @Override - @Transactional(readOnly = true) + @Transactional public ConsumptionGoalResponseListDto findUserConsumptionGoalList(Long userId, LocalDate date) { LocalDate goalMonth = date.withDayOfMonth(1); - Map goalMap = initializeGoalMap(userId); - - updateGoalMapWithPrevious(userId, goalMonth, goalMap); - updateGoalMapWithCurrentMonth(userId, goalMonth, goalMap); - - List consumptionGoalList = new ArrayList<>(goalMap.values()); - - return consumptionGoalConverter.toConsumptionGoalResponseListDto( - orderByRemainingBalanceDescending(consumptionGoalList), goalMonth); - } - - private Map initializeGoalMap(Long userId) { - return categoryRepository.findUserCategoryByUserId(userId) - .stream() - .collect(Collectors.toMap(Category::getId, consumptionGoalConverter::toConsumptionGoalResponseDto)); - } - - private void updateGoalMapWithPrevious(Long userId, LocalDate goalMonth, - Map goalMap) { - goalMap.keySet() - .stream() - .map(categoryId -> consumptionGoalRepository.findLatelyGoal(userId, categoryId, goalMonth.minusMonths(1))) - .filter(Optional::isPresent) - .map(Optional::get) - .map(consumptionGoalConverter::toConsumptionGoalResponseDtoFromPreviousGoal) - .forEach(goal -> goalMap.put(goal.getCategoryId(), goal)); - } + User user = userService.getUser(userId); + List categoryList = categoryService.getUserCategoryList(userId); - private void updateGoalMapWithCurrentMonth(Long userId, LocalDate goalMonth, - Map goalMap) { - consumptionGoalRepository.findConsumptionGoalByUserIdAndGoalMonth(userId, goalMonth) + List thisMonthUserConsumptionGoal = categoryList .stream() - .map(consumptionGoalConverter::toConsumptionGoalResponseDto) - .forEach(goal -> goalMap.put(goal.getCategoryId(), goal)); - } - - private List orderByRemainingBalanceDescending( - List consumptionGoalList) { - return consumptionGoalList.stream() - .sorted(Comparator.comparingLong(ConsumptionGoalResponseDto::getRemainingBalance).reversed()) + .map(category -> this.getUserConsumptionGoal(user, category, goalMonth)) .toList(); + + return consumptionGoalConverter.toConsumptionGoalResponseListDto(thisMonthUserConsumptionGoal, goalMonth); } @Override From 218a2cfde1c3cc8a0837aefc47348e278c64bbb2 Mon Sep 17 00:00:00 2001 From: JunRain Date: Wed, 30 Oct 2024 22:24:51 +0900 Subject: [PATCH 191/204] =?UTF-8?q?[test]=20ConsumptionGoalServiceTest=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=86=8C=EB=B9=84=EB=AA=A9?= =?UTF-8?q?=ED=91=9C=20=EC=A1=B0=ED=9A=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ConsumptionGoalServiceTest.java | 125 ++++++++---------- 1 file changed, 58 insertions(+), 67 deletions(-) diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java index 0b7dcf3d..06e585eb 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java @@ -12,7 +12,6 @@ import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; -import java.util.Random; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -26,6 +25,7 @@ import com.bbteam.budgetbuddies.domain.category.entity.Category; import com.bbteam.budgetbuddies.domain.category.repository.CategoryRepository; +import com.bbteam.budgetbuddies.domain.category.service.CategoryService; import com.bbteam.budgetbuddies.domain.consumptiongoal.converter.ConsumptionGoalConverter; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AllConsumptionCategoryResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AvgConsumptionGoalDto; @@ -43,15 +43,15 @@ import com.bbteam.budgetbuddies.domain.expense.repository.ExpenseRepository; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import com.bbteam.budgetbuddies.domain.user.service.UserService; import com.bbteam.budgetbuddies.enums.Gender; @DisplayName("ConsumptionGoalImpl 서비스 테스트의 ") @ExtendWith(MockitoExtension.class) class ConsumptionGoalServiceTest { - private final LocalDate GOAL_MONTH = LocalDate.of(2024, 07, 01); private final LocalDate currentMonth = LocalDate.now().withDayOfMonth(1); private User user; - private LocalDate goalMonthRandomDay; + private LocalDate requestMonth; @InjectMocks private ConsumptionGoalServiceImpl consumptionGoalService; @Mock @@ -59,17 +59,19 @@ class ConsumptionGoalServiceTest { @Mock private CategoryRepository categoryRepository; @Mock + private CategoryService categoryService; + @Mock private UserRepository userRepository; @Mock + private UserService userService; + @Mock private ExpenseRepository expenseRepository; @Spy private ConsumptionGoalConverter consumptionGoalConverter; @BeforeEach void setUp() { - Random random = new Random(); - int randomDay = random.nextInt(30) + 1; - goalMonthRandomDay = LocalDate.of(GOAL_MONTH.getYear(), GOAL_MONTH.getMonth(), randomDay); + requestMonth = LocalDate.of(2024, 7, 15); user = Mockito.spy(User.builder() .email("email") @@ -81,26 +83,22 @@ void setUp() { } @Test - @DisplayName("findUserConsumptionGoal : 생성된 ConsumptionGoal이 없고 카테고리만 있는 경우 목표 금액, 소비 금액 0으로 초기화") - void findUserConsumptionGoal_onlyCategory() { + void 유저소비목표조회시_이번달_이전달_소비목표가_없는_경우_소비목표를_새로_생성() { // given Category defaultCategory = Mockito.spy(Category.builder().name("디폴트 카테고리").user(null).isDefault(true).build()); given(defaultCategory.getId()).willReturn(-1L); - Category userCategory = Mockito.spy(Category.builder().name("유저 카테고리").user(user).isDefault(false).build()); given(userCategory.getId()).willReturn(-2L); - List categoryList = List.of(defaultCategory, userCategory); + given(categoryService.getUserCategoryList(user.getId())).willReturn(List.of(defaultCategory, userCategory)); List expected = List.of( new ConsumptionGoalResponseDto(defaultCategory.getName(), defaultCategory.getId(), 0L, 0L), new ConsumptionGoalResponseDto(userCategory.getName(), userCategory.getId(), 0L, 0L)); // when - when(categoryRepository.findUserCategoryByUserId(user.getId())).thenReturn(categoryList); - ConsumptionGoalResponseListDto result = consumptionGoalService.findUserConsumptionGoalList(user.getId(), - goalMonthRandomDay); + requestMonth); // then assertThat(result.getConsumptionGoalList()).usingRecursiveComparison().isEqualTo(expected); @@ -108,94 +106,87 @@ void findUserConsumptionGoal_onlyCategory() { } @Test - @DisplayName("findUserConsumptionGoal : 한달전 ConsumptionGoal만 있을 경우 한달전으로 초기화") - void findUserConsumptionGoal_previousMonth() { + void 소비목표조회시_지난달_소비목표만_존재할시_지난달_기준으로_소비목표를_생성() { // given Category defaultCategory = Mockito.spy(Category.builder().name("디폴트 카테고리").user(null).isDefault(true).build()); given(defaultCategory.getId()).willReturn(-1L); - Category userCategory = Mockito.spy(Category.builder().name("유저 카테고리").user(user).isDefault(false).build()); given(userCategory.getId()).willReturn(-2L); - given(categoryRepository.findUserCategoryByUserId(user.getId())).willReturn( - List.of(defaultCategory, userCategory)); + given(categoryService.getUserCategoryList(user.getId())).willReturn(List.of(defaultCategory, userCategory)); + given(userService.getUser(user.getId())).willReturn(user); ConsumptionGoal previousMonthDefaultCategoryGoal = ConsumptionGoal.builder() .goalAmount(1_000_000L) .consumeAmount(200_000L) .user(user) .category(defaultCategory) - .goalMonth(goalMonthRandomDay.minusMonths(1)) + .goalMonth(requestMonth.withDayOfMonth(1).minusMonths(1)) .build(); - Long previousMonthDefaultGoalRemainingBalance = - previousMonthDefaultCategoryGoal.getGoalAmount() - previousMonthDefaultCategoryGoal.getConsumeAmount(); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, defaultCategory, + requestMonth.withDayOfMonth(1))).willReturn(Optional.empty()); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, defaultCategory, + requestMonth.withDayOfMonth(1).minusMonths(1))).willReturn(Optional.of(previousMonthDefaultCategoryGoal)); ConsumptionGoal previousMonthUserCategoryGoal = ConsumptionGoal.builder() .goalAmount(1_000_000L) .consumeAmount(20_000L) .user(user) .category(userCategory) - .goalMonth(goalMonthRandomDay.minusMonths(1)) + .goalMonth(requestMonth.withDayOfMonth(1).minusMonths(1)) .build(); - Long previousMonthUseGoalRemainingBalance = - previousMonthUserCategoryGoal.getGoalAmount() - previousMonthUserCategoryGoal.getConsumeAmount(); - - List previousGoalList = List.of(previousMonthDefaultCategoryGoal, - previousMonthUserCategoryGoal); - - List expected = List.of( - consumptionGoalConverter.toConsumptionGoalResponseDto(previousMonthUserCategoryGoal), - consumptionGoalConverter.toConsumptionGoalResponseDto(previousMonthDefaultCategoryGoal)); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, userCategory, + requestMonth.withDayOfMonth(1))).willReturn(Optional.empty()); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, userCategory, + requestMonth.withDayOfMonth(1).minusMonths(1))).willReturn(Optional.of(previousMonthUserCategoryGoal)); // when - when(consumptionGoalRepository.findConsumptionGoalByUserIdAndGoalMonth(user.getId(), - GOAL_MONTH.minusMonths(1))).thenReturn(previousGoalList); - ConsumptionGoalResponseListDto result = consumptionGoalService.findUserConsumptionGoalList(user.getId(), - goalMonthRandomDay); + requestMonth); // then - assertThat(result.getConsumptionGoalList()).usingRecursiveComparison().isEqualTo(expected); - assertEquals(result.getTotalRemainingBalance(), - previousMonthDefaultGoalRemainingBalance + previousMonthUseGoalRemainingBalance); + assertThat(result.getTotalRemainingBalance()).isEqualTo(2_000_000L); + assertThat(result.getTotalConsumptionAmount()).isEqualTo(0L); } @Test - @DisplayName("findUserConsumptionGoal : 한달 전과 목표 달 ConsumptionGoal이 있을 경우 목표 달로 초기화") - void findUserConsumptionGoal_previousMonthAndGoalMonth() { + void 소비목표_조회시_이번달_소비목표가_존재하는_경우_이번달_소비목표를_반환() { // given + Category defaultCategory = Mockito.spy(Category.builder().name("디폴트 카테고리").user(null).isDefault(true).build()); + given(defaultCategory.getId()).willReturn(-1L); Category userCategory = Mockito.spy(Category.builder().name("유저 카테고리").user(user).isDefault(false).build()); given(userCategory.getId()).willReturn(-2L); - ConsumptionGoal previousMonthUserCategoryGoal = ConsumptionGoal.builder() + given(categoryService.getUserCategoryList(user.getId())).willReturn(List.of(defaultCategory, userCategory)); + given(userService.getUser(user.getId())).willReturn(user); + + ConsumptionGoal thisMonthDefaultCategoryGoal = ConsumptionGoal.builder() .goalAmount(1_000_000L) - .consumeAmount(20_000L) + .consumeAmount(200_000L) .user(user) - .category(userCategory) - .goalMonth(goalMonthRandomDay.minusMonths(1)) + .category(defaultCategory) + .goalMonth(requestMonth.withDayOfMonth(1)) .build(); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, defaultCategory, + requestMonth.withDayOfMonth(1))).willReturn(Optional.of(thisMonthDefaultCategoryGoal)); - ConsumptionGoal goalMonthUserCategoryGoal = ConsumptionGoal.builder() - .goalAmount(2_000_000L) - .consumeAmount(30_000L) + ConsumptionGoal thisMonthUserCategoryGoal = ConsumptionGoal.builder() + .goalAmount(1_000_000L) + .consumeAmount(20_000L) .user(user) .category(userCategory) - .goalMonth(goalMonthRandomDay) + .goalMonth(requestMonth.withDayOfMonth(1)) .build(); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, userCategory, + requestMonth.withDayOfMonth(1))).willReturn(Optional.of(thisMonthUserCategoryGoal)); // when - when(consumptionGoalRepository.findConsumptionGoalByUserIdAndGoalMonth(user.getId(), - GOAL_MONTH.minusMonths(1))).thenReturn(List.of(previousMonthUserCategoryGoal)); - - when(consumptionGoalRepository.findConsumptionGoalByUserIdAndGoalMonth(user.getId(), GOAL_MONTH)).thenReturn( - List.of(goalMonthUserCategoryGoal)); - ConsumptionGoalResponseListDto result = consumptionGoalService.findUserConsumptionGoalList(user.getId(), - goalMonthRandomDay); + requestMonth); // then - assertThat(result.getConsumptionGoalList()).usingRecursiveComparison() - .isEqualTo(List.of(consumptionGoalConverter.toConsumptionGoalResponseDto(goalMonthUserCategoryGoal))); + assertThat(result.getTotalRemainingBalance()).isEqualTo(1_780_000L); + assertThat(result.getTotalConsumptionAmount()).isEqualTo(220_000L); } @Test @@ -524,8 +515,8 @@ void getAllConsumptionCategories_Success() { .category(category) .goalMonth(goalMonth) .build(); - given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth)) - .willReturn(Optional.of(userConsumptionGoal)); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth)).willReturn( + Optional.of(userConsumptionGoal)); // when ConsumptionGoal result = consumptionGoalService.getUserConsumptionGoal(user, category, goalMonth); @@ -547,10 +538,10 @@ void getAllConsumptionCategories_Success() { .category(category) .goalMonth(LocalDate.of(2024, 6, 1)) .build(); - given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth)) - .willReturn(Optional.empty()); - given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth.minusMonths(1))) - .willReturn(Optional.of(beforeUserConsumptionGoal)); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth)).willReturn( + Optional.empty()); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, + goalMonth.minusMonths(1))).willReturn(Optional.of(beforeUserConsumptionGoal)); ConsumptionGoal expected = ConsumptionGoal.builder() .goalAmount(1_000_000L) @@ -572,10 +563,10 @@ void getAllConsumptionCategories_Success() { Category category = Mockito.spy(Category.builder().name("TEST CATEGORY").user(user).isDefault(false).build()); LocalDate goalMonth = LocalDate.of(2024, 7, 1); - given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth)) - .willReturn(Optional.empty()); - given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth.minusMonths(1))) - .willReturn(Optional.empty()); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth)).willReturn( + Optional.empty()); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, + goalMonth.minusMonths(1))).willReturn(Optional.empty()); ConsumptionGoal expected = ConsumptionGoal.builder() .goalAmount(0L) From 629b8de916efc3c0f6019ba39f8d47b43f369bd7 Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 31 Oct 2024 20:49:45 +0900 Subject: [PATCH 192/204] =?UTF-8?q?[fix]=20CategoryService=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20ConsumptionG?= =?UTF-8?q?oalService=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A0=9C=EA=B1=B0?= =?UTF-8?q?=EB=A1=9C=20=EC=88=9C=ED=99=98=EC=B0=B8=EC=A1=B0=20=EB=B0=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/category/service/CategoryServiceImpl.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryServiceImpl.java index 49126290..7a010856 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryServiceImpl.java @@ -15,7 +15,6 @@ import com.bbteam.budgetbuddies.domain.category.repository.CategoryRepository; import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; import com.bbteam.budgetbuddies.domain.consumptiongoal.repository.ConsumptionGoalRepository; -import com.bbteam.budgetbuddies.domain.consumptiongoal.service.ConsumptionGoalService; import com.bbteam.budgetbuddies.domain.expense.entity.Expense; import com.bbteam.budgetbuddies.domain.expense.repository.ExpenseRepository; import com.bbteam.budgetbuddies.domain.user.entity.User; @@ -29,7 +28,6 @@ public class CategoryServiceImpl implements CategoryService { private final CategoryRepository categoryRepository; - private final ConsumptionGoalService consumptionGoalService; private final UserRepository userRepository; private final CategoryConverter categoryConverter; private final ConsumptionGoalRepository consumptionGoalRepository; From 4bec92dc9e2c2e4c87217c2a825cc17eb199ff17 Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 31 Oct 2024 20:52:02 +0900 Subject: [PATCH 193/204] =?UTF-8?q?[refactor]=20ConsumptionGoalController?= =?UTF-8?q?=20=EC=86=8C=EB=B9=84=EB=AA=A9=ED=91=9C=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20@AuthUser=EB=A5=BC=20=ED=86=B5=ED=95=9C=20?= =?UTF-8?q?User=20=EC=A3=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../consumptiongoal/controller/ConsumptionGoalApi.java | 2 +- .../controller/ConsumptionGoalController.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) 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 abdda415..09d38867 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 @@ -57,7 +57,7 @@ ApiResponse> getAllConsumptionGoalCatego ApiResponse findUserConsumptionGoal(LocalDate date, UserDto.AuthUserDto user); @Operation(summary = "[User] 이번 달 소비 목표 수정", description = "다른 달의 소비 목표를 업데이트하는 것은 불가능하고 오직 이번 달의 소비 목표만 업데이트 하는 API 입니다.") - ResponseEntity updateOrElseGenerateConsumptionGoal(Long userId, + ResponseEntity updateOrElseGenerateConsumptionGoal(UserDto.AuthUserDto user, ConsumptionGoalListRequestDto consumptionGoalListRequestDto); @Operation(summary = "[User] 또래들이 가장 많이한 소비 카테고리 조회 Top3", description = "특정 사용자의 또래 소비 카테고리별 소비 건 수을 조회하는 API 입니다.") 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 aec1b5a2..b65b5904 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 @@ -6,7 +6,6 @@ import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -77,12 +76,13 @@ public ApiResponse findUserConsumptionGoal( } @Override - @PostMapping("/{userId}") - public ResponseEntity updateOrElseGenerateConsumptionGoal(@PathVariable Long userId, + @PostMapping() + public ResponseEntity updateOrElseGenerateConsumptionGoal( + @AuthUser UserDto.AuthUserDto user, @RequestBody ConsumptionGoalListRequestDto consumptionGoalListRequestDto) { return ResponseEntity.ok() - .body(consumptionGoalService.updateConsumptionGoals(userId, consumptionGoalListRequestDto)); + .body(consumptionGoalService.updateConsumptionGoals(user.getId(), consumptionGoalListRequestDto)); } @GetMapping("/categories/top-consumptions/top-3") From de0f89013ab4bcd6ff08d1549e225dfc0c75213b Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 31 Oct 2024 20:53:41 +0900 Subject: [PATCH 194/204] =?UTF-8?q?[refactor]=20ConsumptionGoalService=20?= =?UTF-8?q?=EC=86=8C=EB=B9=84=EB=AA=A9=ED=91=9C=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EB=B3=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ConsumptionGoalServiceImpl.java | 45 +++++++------------ 1 file changed, 17 insertions(+), 28 deletions(-) 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 c7ff3991..6bc6b4da 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 @@ -34,7 +34,6 @@ 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; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.MonthReportResponseDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.MyConsumptionGoalDto; @@ -467,48 +466,27 @@ private double calculateMedian(List values) { public ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId, ConsumptionGoalListRequestDto consumptionGoalListRequestDto) { LocalDate thisMonth = LocalDate.now().withDayOfMonth(1); - User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("Not found user")); + User user = userService.getUser(userId); List updatedConsumptionGoal = consumptionGoalListRequestDto.getConsumptionGoalList() .stream() .map(c -> updateConsumptionGoalWithRequestDto(user, c, thisMonth)) .toList(); - List response = consumptionGoalRepository.saveAll(updatedConsumptionGoal) - .stream() - .map(consumptionGoalConverter::toConsumptionGoalResponseDto) - .toList(); - - return consumptionGoalConverter.toConsumptionGoalResponseListDto(response, thisMonth); + return consumptionGoalConverter.toConsumptionGoalResponseListDto(updatedConsumptionGoal, thisMonth); } private ConsumptionGoal updateConsumptionGoalWithRequestDto(User user, ConsumptionGoalRequestDto consumptionGoalRequestDto, LocalDate goalMonth) { - Category category = categoryRepository.findById(consumptionGoalRequestDto.getCategoryId()) - .orElseThrow(() -> new IllegalArgumentException("Not found Category")); + Category category = categoryService.getCategory(consumptionGoalRequestDto.getCategoryId()); - ConsumptionGoal consumptionGoal = findOrElseGenerateConsumptionGoal(user, category, goalMonth); + ConsumptionGoal consumptionGoal = this.getUserConsumptionGoal(user, category, goalMonth); consumptionGoal.updateGoalAmount(consumptionGoalRequestDto.getGoalAmount()); return consumptionGoal; } - private ConsumptionGoal findOrElseGenerateConsumptionGoal(User user, Category category, LocalDate goalMonth) { - return consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, category, goalMonth) - .orElseGet(() -> generateNewConsumptionGoal(user, category, goalMonth)); - } - - private ConsumptionGoal generateNewConsumptionGoal(User user, Category category, LocalDate goalMonth) { - return ConsumptionGoal.builder() - .goalMonth(goalMonth) - .user(user) - .category(category) - .consumeAmount(0L) - .goalAmount(0L) - .build(); - } - @Override @Transactional public ConsumptionGoalResponseListDto findUserConsumptionGoalList(Long userId, LocalDate date) { @@ -821,12 +799,23 @@ private ConsumptionGoal generateGoalFromPreviousOrNew(User user, Category catego } private ConsumptionGoal generateGoalByPrevious(ConsumptionGoal consumptionGoal) { - return ConsumptionGoal.builder() + return consumptionGoalRepository.save(ConsumptionGoal.builder() .goalMonth(consumptionGoal.getGoalMonth().plusMonths(1)) .user(consumptionGoal.getUser()) .category(consumptionGoal.getCategory()) .consumeAmount(0L) .goalAmount(consumptionGoal.getGoalAmount()) - .build(); + .build()); } + + private ConsumptionGoal generateNewConsumptionGoal(User user, Category category, LocalDate goalMonth) { + return consumptionGoalRepository.save(ConsumptionGoal.builder() + .goalMonth(goalMonth) + .user(user) + .category(category) + .consumeAmount(0L) + .goalAmount(0L) + .build()); + } + } \ No newline at end of file From 265c0e5af2cbd5cf35ca072dc210642d2fc0df3b Mon Sep 17 00:00:00 2001 From: JunRain Date: Thu, 31 Oct 2024 20:54:08 +0900 Subject: [PATCH 195/204] =?UTF-8?q?[test]=20ConsumptionGoalServiceTest=20?= =?UTF-8?q?=EC=86=8C=EB=B9=84=EB=AA=A9=ED=91=9C=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ConsumptionGoalServiceTest.java | 113 +++++++----------- 1 file changed, 42 insertions(+), 71 deletions(-) diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java index 06e585eb..a010109f 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.AdditionalAnswers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; @@ -91,6 +92,7 @@ void setUp() { given(userCategory.getId()).willReturn(-2L); given(categoryService.getUserCategoryList(user.getId())).willReturn(List.of(defaultCategory, userCategory)); + given(consumptionGoalRepository.save(any(ConsumptionGoal.class))).will(AdditionalAnswers.returnsFirstArg()); List expected = List.of( new ConsumptionGoalResponseDto(defaultCategory.getName(), defaultCategory.getId(), 0L, 0L), @@ -140,6 +142,8 @@ void setUp() { given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, userCategory, requestMonth.withDayOfMonth(1).minusMonths(1))).willReturn(Optional.of(previousMonthUserCategoryGoal)); + given(consumptionGoalRepository.save(any(ConsumptionGoal.class))).will(AdditionalAnswers.returnsFirstArg()); + // when ConsumptionGoalResponseListDto result = consumptionGoalService.findUserConsumptionGoalList(user.getId(), requestMonth); @@ -190,26 +194,19 @@ void setUp() { } @Test - @DisplayName("updateConsumptionGoal : 이번달 목표가 있는 경우(defaultCategory)와 목표가 없는 경우(userCategory)") - void updateConsumptionGoal_Success() { + void 소비목표_업데이트_성공() { // given - Long defaultGoalAmount = 100L; - Long userGoalAmount = 200L; + Long updateAmount = 100L; LocalDate thisMonth = LocalDate.now().withDayOfMonth(1); - given(userRepository.findById(user.getId())).willReturn(Optional.ofNullable(user)); + given(userService.getUser(user.getId())).willReturn(user); ConsumptionGoalListRequestDto request = new ConsumptionGoalListRequestDto( - List.of(new ConsumptionGoalRequestDto(-1L, defaultGoalAmount), - new ConsumptionGoalRequestDto(-2L, userGoalAmount))); + List.of(new ConsumptionGoalRequestDto(-1L, updateAmount))); Category defaultCategory = Mockito.spy(Category.builder().name("디폴트 카테고리").user(null).isDefault(true).build()); given(defaultCategory.getId()).willReturn(-1L); - given(categoryRepository.findById(defaultCategory.getId())).willReturn(Optional.of(defaultCategory)); - - Category userCategory = Mockito.spy(Category.builder().name("유저 카테고리").user(user).isDefault(false).build()); - given(userCategory.getId()).willReturn(-2L); - given(categoryRepository.findById(userCategory.getId())).willReturn(Optional.of(userCategory)); + given(categoryService.getCategory(defaultCategory.getId())).willReturn(defaultCategory); ConsumptionGoal defaultCategoryGoal = ConsumptionGoal.builder() .goalAmount(1_000_000L) @@ -218,27 +215,14 @@ void updateConsumptionGoal_Success() { .category(defaultCategory) .goalMonth(thisMonth) .build(); - given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, defaultCategory, - thisMonth)).willReturn(Optional.ofNullable(defaultCategoryGoal)); - - given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, userCategory, - thisMonth)).willReturn(Optional.ofNullable(null)); - - when(consumptionGoalRepository.saveAll(any())).thenAnswer(invocation -> { - List goalsToSave = invocation.getArgument(0); - return goalsToSave; - }); + given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, defaultCategory, thisMonth)).willReturn( + Optional.ofNullable(defaultCategoryGoal)); List expected = List.of(ConsumptionGoalResponseDto.builder() - .goalAmount(defaultGoalAmount) + .goalAmount(updateAmount) .consumeAmount(defaultCategoryGoal.getConsumeAmount()) .categoryName(defaultCategory.getName()) .categoryId(defaultCategory.getId()) - .build(), ConsumptionGoalResponseDto.builder() - .goalAmount(userGoalAmount) - .consumeAmount(0L) - .categoryName(userCategory.getName()) - .categoryId(userCategory.getId()) .build()); // when @@ -290,11 +274,7 @@ void getPeerInfo_UserNotFound() { @DisplayName("getTopCategoryAndConsumptionAmount : 가장 큰 계획 카테고리와 이번 주 소비 금액 조회 성공") void getTopCategoryAndConsumptionAmount_Success() { // given - Category defaultCategory = Mockito.spy(Category.builder() - .name("디폴트 카테고리") - .user(null) - .isDefault(true) - .build()); + Category defaultCategory = Mockito.spy(Category.builder().name("디폴트 카테고리").user(null).isDefault(true).build()); given(defaultCategory.getId()).willReturn(1L); LocalDate goalMonthRandomDay = LocalDate.now(); @@ -321,25 +301,21 @@ void getTopCategoryAndConsumptionAmount_Success() { .build(); List avgConsumptionGoalList = List.of( - new AvgConsumptionGoalDto(defaultCategory.getId(), 5000L) - ); + new AvgConsumptionGoalDto(defaultCategory.getId(), 5000L)); int peerAgeStart = 23; int peerAgeEnd = 25; Gender peerGender = Gender.MALE; given(userRepository.findById(user.getId())).willReturn(Optional.of(user)); - given(consumptionGoalRepository.findAvgGoalAmountByCategory( - peerAgeStart, peerAgeEnd, peerGender, currentMonth)) - .willReturn(avgConsumptionGoalList); + given(consumptionGoalRepository.findAvgGoalAmountByCategory(peerAgeStart, peerAgeEnd, peerGender, + currentMonth)).willReturn(avgConsumptionGoalList); - given(consumptionGoalRepository.findAvgConsumptionByCategoryIdAndCurrentWeek( - defaultCategory.getId(), startOfWeekDateTime, endOfWeekDateTime, - peerAgeStart, peerAgeEnd, peerGender)) - .willReturn(Optional.of(currentWeekConsumptionGoal.getConsumeAmount())); + given(consumptionGoalRepository.findAvgConsumptionByCategoryIdAndCurrentWeek(defaultCategory.getId(), + startOfWeekDateTime, endOfWeekDateTime, peerAgeStart, peerAgeEnd, peerGender)).willReturn( + Optional.of(currentWeekConsumptionGoal.getConsumeAmount())); - given(categoryRepository.findById(defaultCategory.getId())) - .willReturn(Optional.of(defaultCategory)); + given(categoryRepository.findById(defaultCategory.getId())).willReturn(Optional.of(defaultCategory)); // when ConsumptionAnalysisResponseDto result = consumptionGoalService.getTopCategoryAndConsumptionAmount(user.getId()); @@ -374,17 +350,17 @@ void getTopConsumptionCategories_Success() { String peerGender = "MALE"; given(userRepository.findById(user.getId())).willReturn(Optional.of(user)); - given(expenseRepository.findTopCategoriesByConsumptionCount(peerAgeStart, peerAgeEnd, - Gender.valueOf(peerGender), currentMonth.atStartOfDay())) - .willReturn(List.of(topConsumption1, topConsumption2, topConsumption3)); + given( + expenseRepository.findTopCategoriesByConsumptionCount(peerAgeStart, peerAgeEnd, Gender.valueOf(peerGender), + currentMonth.atStartOfDay())).willReturn(List.of(topConsumption1, topConsumption2, topConsumption3)); given(categoryRepository.findById(defaultCategory1.getId())).willReturn(Optional.of(defaultCategory1)); given(categoryRepository.findById(defaultCategory2.getId())).willReturn(Optional.of(defaultCategory2)); given(categoryRepository.findById(defaultCategory3.getId())).willReturn(Optional.of(defaultCategory3)); // when - List result = consumptionGoalService.getTopConsumptionCategories( - user.getId(), peerAgeStart, peerAgeEnd, peerGender); + List result = consumptionGoalService.getTopConsumptionCategories(user.getId(), + peerAgeStart, peerAgeEnd, peerGender); // then assertThat(result).hasSize(3); @@ -414,23 +390,19 @@ void getAllConsumptionGoalCategories_Success() { defaultCategory2.setName("디폴트 카테고리2"); defaultCategory2.setIsDefault(true); - List categoryAvgList = List.of( - new AvgConsumptionGoalDto(1L, 3000L), - new AvgConsumptionGoalDto(2L, 4000L) - ); + List categoryAvgList = List.of(new AvgConsumptionGoalDto(1L, 3000L), + new AvgConsumptionGoalDto(2L, 4000L)); - List myConsumptionAmountList = List.of( - new MyConsumptionGoalDto(1L, 5000L), - new MyConsumptionGoalDto(2L, 2000L) - ); + List myConsumptionAmountList = List.of(new MyConsumptionGoalDto(1L, 5000L), + new MyConsumptionGoalDto(2L, 2000L)); List defaultCategories = List.of(defaultCategory1, defaultCategory2); given(categoryRepository.findAllByIsDefaultTrue()).willReturn(defaultCategories); - given(consumptionGoalRepository.findAvgGoalAmountByCategory( - anyInt(), anyInt(), any(), any())).willReturn(categoryAvgList); - given(consumptionGoalRepository.findAllGoalAmountByUserId(user.getId(), currentMonth)) - .willReturn(myConsumptionAmountList); + given(consumptionGoalRepository.findAvgGoalAmountByCategory(anyInt(), anyInt(), any(), any())).willReturn( + categoryAvgList); + given(consumptionGoalRepository.findAllGoalAmountByUserId(user.getId(), currentMonth)).willReturn( + myConsumptionAmountList); given(userRepository.findById(user.getId())).willReturn(Optional.of(user)); // when @@ -466,21 +438,18 @@ void getAllConsumptionCategories_Success() { defaultCategory2.setName("디폴트 카테고리2"); defaultCategory2.setIsDefault(true); - List categoryAvgList = List.of( - new AvgConsumptionGoalDto(1L, 3000L), - new AvgConsumptionGoalDto(2L, 4000L) - ); + List categoryAvgList = List.of(new AvgConsumptionGoalDto(1L, 3000L), + new AvgConsumptionGoalDto(2L, 4000L)); - List myConsumptionAmountList = List.of( - new MyConsumptionGoalDto(1L, 5000L), - new MyConsumptionGoalDto(2L, 2000L) - ); + List myConsumptionAmountList = List.of(new MyConsumptionGoalDto(1L, 5000L), + new MyConsumptionGoalDto(2L, 2000L)); List defaultCategories = List.of(defaultCategory1, defaultCategory2); given(categoryRepository.findAllByIsDefaultTrue()).willReturn(defaultCategories); - given(consumptionGoalRepository.findAvgConsumptionAmountByCategory( - anyInt(), anyInt(), any(), any())).willReturn(categoryAvgList); + given( + consumptionGoalRepository.findAvgConsumptionAmountByCategory(anyInt(), anyInt(), any(), any())).willReturn( + categoryAvgList); given(userRepository.findById(user.getId())).willReturn(Optional.of(user)); // when @@ -542,6 +511,7 @@ void getAllConsumptionCategories_Success() { Optional.empty()); given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth.minusMonths(1))).willReturn(Optional.of(beforeUserConsumptionGoal)); + given(consumptionGoalRepository.save(any(ConsumptionGoal.class))).will(AdditionalAnswers.returnsFirstArg()); ConsumptionGoal expected = ConsumptionGoal.builder() .goalAmount(1_000_000L) @@ -567,6 +537,7 @@ void getAllConsumptionCategories_Success() { Optional.empty()); given(consumptionGoalRepository.findByUserAndCategoryAndGoalMonth(user, category, goalMonth.minusMonths(1))).willReturn(Optional.empty()); + given(consumptionGoalRepository.save(any(ConsumptionGoal.class))).will(AdditionalAnswers.returnsFirstArg()); ConsumptionGoal expected = ConsumptionGoal.builder() .goalAmount(0L) From 16a3ee7b13151a7b216c2f6478add5ce9838e9b4 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Wed, 13 Nov 2024 16:17:34 +0900 Subject: [PATCH 196/204] =?UTF-8?q?[feat]=20ErrorStatus=20Faq=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20status=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/apiPayload/code/status/ErrorStatus.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java index 8c9515b8..e3d44bb8 100644 --- a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java @@ -27,7 +27,9 @@ public enum ErrorStatus implements BaseErrorCode { _OTP_NOT_VALID(HttpStatus.BAD_REQUEST, "OTP4001", "인증번호가 유효하지 않습니다."), _PHONE_NUMBER_NOT_VALID(HttpStatus.BAD_REQUEST, "AUTH4001", "전화번호 형식이 유효하지 않습니다. (예: 01012341234)"), - _FAQ_NOT_FOUND(HttpStatus.NOT_FOUND, "FAQ4004", "해당 FAQ가 존재하지 않습니다."); + _FAQ_NOT_FOUND(HttpStatus.NOT_FOUND, "FAQ4004", "해당 FAQ가 존재하지 않습니다."), + _SEARCH_KEYWORD_NOT_FOUND(HttpStatus.NOT_FOUND, "SEARCH_KEYWORD4004", "해당 SearchKeyword가 존재하지 않습니다."), + _FAQ_KEYWORD_NOT_FOUND(HttpStatus.NOT_FOUND, "FAQ_KEYWORD4004", "해당 FaqKeyword가 존재하지 않습니다."); private HttpStatus httpStatus; From 9772e0f8146e4e4f3bb19fa3b0dacf75d276a1a7 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Wed, 13 Nov 2024 16:18:09 +0900 Subject: [PATCH 197/204] =?UTF-8?q?[feat]=20FAQ,=20SearchKeyword=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../budgetbuddies/domain/faq/controller/FaqApi.java | 5 +++++ .../domain/faq/controller/FaqController.java | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java index 06bc6e16..8485960f 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java @@ -113,4 +113,9 @@ ApiResponse modifyFaq(@PathVariable @ExistFaq Long faqId, } ) ApiResponse deleteFaq(@ExistFaq Long faqId); + + ApiResponse addKeyword(@ExistFaq Long faqId, Long searchKeywordId); + + ApiResponse deleteKeyword(@ExistFaq Long faqId, Long searchKeywordId); + } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java index 74a6e41f..c15fe5d2 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java @@ -57,4 +57,16 @@ public ApiResponse deleteFaq(@PathVariable @ExistFaq Long faqId) { faqService.deleteFaq(faqId); return ApiResponse.onSuccess("Delete Success"); } + + @Override + @PostMapping("/{faqId}/keyword") + public ApiResponse addKeyword(@PathVariable @ExistFaq Long faqId, @RequestParam Long searchKeywordId) { + return ApiResponse.onSuccess(faqService.addKeyword(faqId, searchKeywordId)); + } + + @Override + @DeleteMapping("/{faqId}/keyword") + public ApiResponse deleteKeyword(@PathVariable @ExistFaq Long faqId, @RequestParam Long searchKeywordId) { + return ApiResponse.onSuccess(faqService.removeKeyword(faqId, searchKeywordId)); + } } From 64e64e362200449bb19906ab09cd9f8518eef33e Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Wed, 13 Nov 2024 16:19:04 +0900 Subject: [PATCH 198/204] =?UTF-8?q?[feat]=20Faq=EC=99=80=20SearchKeyword?= =?UTF-8?q?=EC=9D=98=20=EC=A4=91=EA=B0=84=20mapping=20=EA=B0=9D=EC=B2=B4,?= =?UTF-8?q?=20FaqKeyword=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/faqkeyword/domain/FaqKeyword.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faqkeyword/domain/FaqKeyword.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faqkeyword/domain/FaqKeyword.java b/src/main/java/com/bbteam/budgetbuddies/domain/faqkeyword/domain/FaqKeyword.java new file mode 100644 index 00000000..6f5e9f14 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faqkeyword/domain/FaqKeyword.java @@ -0,0 +1,32 @@ +package com.bbteam.budgetbuddies.domain.faqkeyword.domain; + +import com.bbteam.budgetbuddies.common.BaseEntity; +import com.bbteam.budgetbuddies.domain.faq.entity.Faq; +import com.bbteam.budgetbuddies.domain.searchkeyword.domain.SearchKeyword; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.*; +import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@SuperBuilder +public class FaqKeyword extends BaseEntity { + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "faq_id") + @NotFound(action = NotFoundAction.IGNORE) + private Faq faq; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "search_keyword_id") + @NotFound(action = NotFoundAction.IGNORE) + private SearchKeyword searchKeyword; + +} From bd62f553458109e0530ed844940fb4f5859b13bd Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Wed, 13 Nov 2024 16:20:02 +0900 Subject: [PATCH 199/204] =?UTF-8?q?[feat]=20FaqKeyword=20repository,=20dto?= =?UTF-8?q?,=20test=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../faqkeyword/dto/FaqKeywordResponseDto.java | 21 ++++ .../repository/FaqKeywordRepository.java | 12 +++ .../faq/repository/FaqRepositoryTest.java | 101 ++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faqkeyword/dto/FaqKeywordResponseDto.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/faqkeyword/repository/FaqKeywordRepository.java create mode 100644 src/test/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqRepositoryTest.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faqkeyword/dto/FaqKeywordResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/faqkeyword/dto/FaqKeywordResponseDto.java new file mode 100644 index 00000000..da6ef042 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faqkeyword/dto/FaqKeywordResponseDto.java @@ -0,0 +1,21 @@ +package com.bbteam.budgetbuddies.domain.faqkeyword.dto; + +import com.bbteam.budgetbuddies.domain.faqkeyword.domain.FaqKeyword; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class FaqKeywordResponseDto { + + private Long faqId; + private Long searchKeywordId; + + private String faqTitle; + private String keyword; + + public static FaqKeywordResponseDto toDto(FaqKeyword faqKeyword) { + return new FaqKeywordResponseDto(faqKeyword.getFaq().getId(), faqKeyword.getSearchKeyword().getId(), + faqKeyword.getFaq().getTitle(), faqKeyword.getSearchKeyword().getKeyword()); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faqkeyword/repository/FaqKeywordRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/faqkeyword/repository/FaqKeywordRepository.java new file mode 100644 index 00000000..c0606ba0 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faqkeyword/repository/FaqKeywordRepository.java @@ -0,0 +1,12 @@ +package com.bbteam.budgetbuddies.domain.faqkeyword.repository; + +import com.bbteam.budgetbuddies.domain.faq.entity.Faq; +import com.bbteam.budgetbuddies.domain.faqkeyword.domain.FaqKeyword; +import com.bbteam.budgetbuddies.domain.searchkeyword.domain.SearchKeyword; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface FaqKeywordRepository extends JpaRepository { + Optional findByFaqAndSearchKeyword(Faq faq, SearchKeyword searchKeyword); +} diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqRepositoryTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqRepositoryTest.java new file mode 100644 index 00000000..6263fd38 --- /dev/null +++ b/src/test/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqRepositoryTest.java @@ -0,0 +1,101 @@ +package com.bbteam.budgetbuddies.domain.faq.repository; + +import com.bbteam.budgetbuddies.domain.faq.entity.Faq; +import com.bbteam.budgetbuddies.domain.faqkeyword.domain.FaqKeyword; +import com.bbteam.budgetbuddies.domain.faqkeyword.repository.FaqKeywordRepository; +import com.bbteam.budgetbuddies.domain.searchkeyword.domain.SearchKeyword; +import com.bbteam.budgetbuddies.domain.searchkeyword.repository.SearchKeywordRepository; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.transaction.annotation.Transactional; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@Transactional +@ActiveProfiles("test") +class FaqRepositoryTest { + @Autowired + FaqRepository faqRepository; + @Autowired + FaqKeywordRepository faqKeywordRepository; + @Autowired + SearchKeywordRepository searchKeywordRepository; + @Autowired + UserRepository userRepository; + + + @Test + void searchTest() { + + User user1 = User.builder() + .name("tester1") + .email("1234") + .age(5) + .phoneNumber("123456") + .mobileCarrier("skt") + .build(); + userRepository.save(user1); + + Faq faq1 = Faq.builder() + .user(user1) + .title("test1") + .body("test1") + .build(); + faqRepository.save(faq1); + + Faq faq2 = Faq.builder() + .user(user1) + .title("test2") + .body("test2") + .build(); + faqRepository.save(faq2); + + SearchKeyword keyword1 = SearchKeyword.builder() + .keyword("러닝") + .build(); + searchKeywordRepository.save(keyword1); + + FaqKeyword faqKeyword = FaqKeyword.builder() + .searchKeyword(keyword1) + .faq(faq1) + .build(); + faqKeywordRepository.save(faqKeyword); + + SearchKeyword keyword2 = SearchKeyword.builder() + .keyword("헬스") + .build(); + searchKeywordRepository.save(keyword2); + + FaqKeyword faqKeyword2 = FaqKeyword.builder() + .searchKeyword(keyword2) + .faq(faq1) + .build(); + faqKeywordRepository.save(faqKeyword2); + + FaqKeyword faqKeyword3 = FaqKeyword.builder() + .searchKeyword(keyword1) + .faq(faq2) + .build(); + faqKeywordRepository.save(faqKeyword3); + + PageRequest pageRequest = PageRequest.of(0, 5); + + Page result1 = faqRepository.searchFaq(pageRequest, "러닝"); + + Page result2 = faqRepository.searchFaq(pageRequest, "헬스"); + + assertThat(result1.getNumberOfElements()).isEqualTo(2); + assertThat(result2.getNumberOfElements()).isEqualTo(1); + + } + + +} \ No newline at end of file From f6166e6dd77ed8f60778de33dd805456f712bab1 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Wed, 13 Nov 2024 16:20:30 +0900 Subject: [PATCH 200/204] =?UTF-8?q?[feat]=20FaqService=20keyword=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/faq/service/FaqService.java | 5 +++ .../domain/faq/service/FaqServiceImpl.java | 34 ++++++++++++++++ .../domain/faq/service/FaqServiceTest.java | 40 +++++++++++++++++++ 3 files changed, 79 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqService.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqService.java index 2f276470..f334dac5 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqService.java @@ -2,6 +2,7 @@ import com.bbteam.budgetbuddies.domain.faq.dto.FaqRequestDto; import com.bbteam.budgetbuddies.domain.faq.dto.FaqResponseDto; +import com.bbteam.budgetbuddies.domain.faqkeyword.dto.FaqKeywordResponseDto; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -18,4 +19,8 @@ public interface FaqService { FaqResponseDto.FaqModifyResponse modifyFaq(FaqRequestDto.FaqModifyRequest dto, Long faqId); String deleteFaq(Long faqId); + + FaqKeywordResponseDto addKeyword(Long faqId, Long searchKeywordId); + + String removeKeyword(Long faqId, Long searchKeywordId); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceImpl.java index 70c11286..6b732095 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceImpl.java @@ -7,6 +7,11 @@ import com.bbteam.budgetbuddies.domain.faq.dto.FaqResponseDto; import com.bbteam.budgetbuddies.domain.faq.entity.Faq; import com.bbteam.budgetbuddies.domain.faq.repository.FaqRepository; +import com.bbteam.budgetbuddies.domain.faqkeyword.domain.FaqKeyword; +import com.bbteam.budgetbuddies.domain.faqkeyword.dto.FaqKeywordResponseDto; +import com.bbteam.budgetbuddies.domain.faqkeyword.repository.FaqKeywordRepository; +import com.bbteam.budgetbuddies.domain.searchkeyword.domain.SearchKeyword; +import com.bbteam.budgetbuddies.domain.searchkeyword.repository.SearchKeywordRepository; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; import lombok.RequiredArgsConstructor; @@ -22,6 +27,8 @@ public class FaqServiceImpl implements FaqService{ private final FaqRepository faqRepository; private final UserRepository userRepository; + private final FaqKeywordRepository faqKeywordRepository; + private final SearchKeywordRepository searchKeywordRepository; @Override public FaqResponseDto.FaqFindResponse findOneFaq(Long faqId) { @@ -67,4 +74,31 @@ private Faq findFaq(Long faqId) { public Page searchFaq(Pageable pageable, String searchCondition) { return faqRepository.searchFaq(pageable, searchCondition).map(FaqConverter::entityToFind); } + + @Override + @Transactional + public FaqKeywordResponseDto addKeyword(Long faqId, Long searchKeywordId) { + Faq faq = findFaq(faqId); + SearchKeyword searchKeyword = searchKeywordRepository.findById(searchKeywordId).orElseThrow(() -> new GeneralException(ErrorStatus._SEARCH_KEYWORD_NOT_FOUND)); + + FaqKeyword faqKeyword = FaqKeyword.builder() + .searchKeyword(searchKeyword) + .faq(faq) + .build(); + + faqKeywordRepository.save(faqKeyword); + return FaqKeywordResponseDto.toDto(faqKeyword); + } + + @Override + @Transactional + public String removeKeyword(Long faqId, Long searchKeywordId) { + Faq faq = findFaq(faqId); + SearchKeyword searchKeyword = searchKeywordRepository.findById(searchKeywordId).orElseThrow(() -> new GeneralException(ErrorStatus._SEARCH_KEYWORD_NOT_FOUND)); + + FaqKeyword faqKeyword = faqKeywordRepository.findByFaqAndSearchKeyword(faq, searchKeyword).orElseThrow(() -> new GeneralException(ErrorStatus._FAQ_KEYWORD_NOT_FOUND)); + faqKeywordRepository.delete(faqKeyword); + + return "ok"; + } } diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceTest.java index 4e895c18..67d5e7b1 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/faq/service/FaqServiceTest.java @@ -4,6 +4,10 @@ import com.bbteam.budgetbuddies.domain.faq.dto.FaqResponseDto; import com.bbteam.budgetbuddies.domain.faq.entity.Faq; import com.bbteam.budgetbuddies.domain.faq.repository.FaqRepository; +import com.bbteam.budgetbuddies.domain.faqkeyword.dto.FaqKeywordResponseDto; +import com.bbteam.budgetbuddies.domain.faqkeyword.repository.FaqKeywordRepository; +import com.bbteam.budgetbuddies.domain.searchkeyword.domain.SearchKeyword; +import com.bbteam.budgetbuddies.domain.searchkeyword.repository.SearchKeywordRepository; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; import org.assertj.core.api.Assertions; @@ -14,6 +18,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.test.annotation.Rollback; import org.springframework.transaction.annotation.Transactional; import java.util.Optional; @@ -31,6 +36,10 @@ class FaqServiceTest { FaqRepository faqRepository; @Autowired UserRepository userRepository; + @Autowired + FaqKeywordRepository faqKeywordRepository; + @Autowired + SearchKeywordRepository searchKeywordRepository; static Long userId; static Long faqId; @@ -149,4 +158,35 @@ void deleteFaq() { assertThat(faq.isEmpty()).isTrue(); } + + @Test + void addKeywordAndRemoveKeyword() { + User user = userRepository.findById(userId).get(); + + Faq faq1 = Faq.builder() + .title("test1") + .body("test1") + .user(user) + .build(); + faqRepository.save(faq1); + + SearchKeyword searchKeyword = SearchKeyword.builder() + .keyword("testKeyword") + .build(); + searchKeywordRepository.save(searchKeyword); + faqService.addKeyword(faq1.getId(), searchKeyword.getId()); + PageRequest pageRequest = PageRequest.of(0, 1); + + Page result1 = faqService.searchFaq(pageRequest, "test"); + Assertions.assertThat(result1.getNumberOfElements()).isEqualTo(1); + + Page result2 = faqService.searchFaq(pageRequest, "no"); + Assertions.assertThat(result2.getNumberOfElements()).isEqualTo(0); + + faqService.removeKeyword(faq1.getId(), searchKeyword.getId()); + Page result3 = faqService.searchFaq(pageRequest, "test"); + Assertions.assertThat(result3.getNumberOfElements()).isEqualTo(0); + + + } } \ No newline at end of file From f00df26c03493636d385d24a95e236a62644cb4c Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Wed, 13 Nov 2024 16:20:55 +0900 Subject: [PATCH 201/204] =?UTF-8?q?[feat]=20FaqSearchRepository=EC=9D=98?= =?UTF-8?q?=20=EB=8F=99=EC=A0=81=20=EC=BF=BC=EB=A6=AC=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(QueryDSL)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../faq/repository/FaqSearchRepositoryImpl.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqSearchRepositoryImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqSearchRepositoryImpl.java index 81205dbf..1e3116ab 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqSearchRepositoryImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/repository/FaqSearchRepositoryImpl.java @@ -1,6 +1,7 @@ package com.bbteam.budgetbuddies.domain.faq.repository; import com.bbteam.budgetbuddies.domain.faq.entity.Faq; +import com.bbteam.budgetbuddies.domain.searchkeyword.domain.QSearchKeyword; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -14,6 +15,7 @@ import static com.bbteam.budgetbuddies.domain.faq.entity.QFaq.*; import static com.bbteam.budgetbuddies.domain.faqkeyword.domain.QFaqKeyword.*; +import static com.bbteam.budgetbuddies.domain.searchkeyword.domain.QSearchKeyword.*; public class FaqSearchRepositoryImpl implements FaqSearchRepository{ @@ -27,11 +29,11 @@ public FaqSearchRepositoryImpl(EntityManager em) { public Page searchFaq(Pageable pageable, String searchCondition) { List result = queryFactory.select(faq) .from(faq) - .where(faq.in( + .where(faq.id.in( JPAExpressions - .select(faqKeyword.faq) + .select(faqKeyword.faq.id) .from(faqKeyword) - .where(keywordMatch(searchCondition)) + .join(searchKeyword).on(keywordMatch(searchCondition)) )) .offset(pageable.getOffset()) .limit(pageable.getPageSize()) @@ -39,11 +41,11 @@ public Page searchFaq(Pageable pageable, String searchCondition) { Long total = queryFactory.select(faq.count()) .from(faq) - .where(faq.in( + .where(faq.id.in( JPAExpressions - .select(faqKeyword.faq) + .select(faqKeyword.faq.id) .from(faqKeyword) - .where(keywordMatch(searchCondition)) + .join(searchKeyword).on(keywordMatch(searchCondition)) )) .fetchOne(); @@ -52,7 +54,7 @@ public Page searchFaq(Pageable pageable, String searchCondition) { } private BooleanExpression keywordMatch(String searchCondition) { - return searchCondition != null ? faqKeyword.searchKeyword.keyword.eq(searchCondition) : null; + return searchCondition != null ? searchKeyword.keyword.contains(searchCondition) : null; } } From d78df08091e6478a4af08a805f714663901cee86 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Wed, 13 Nov 2024 16:21:35 +0900 Subject: [PATCH 202/204] =?UTF-8?q?[feat]=20SearchKeyword=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SearchKeywordController.java | 48 +++++++++++++++++++ .../searchkeyword/domain/SearchKeyword.java | 23 +++++++++ .../repository/SearchKeywordRepository.java | 7 +++ .../service/SearchKeywordService.java | 44 +++++++++++++++++ 4 files changed, 122 insertions(+) create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/controller/SearchKeywordController.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/domain/SearchKeyword.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/repository/SearchKeywordRepository.java create mode 100644 src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/service/SearchKeywordService.java diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/controller/SearchKeywordController.java b/src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/controller/SearchKeywordController.java new file mode 100644 index 00000000..45786bbe --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/controller/SearchKeywordController.java @@ -0,0 +1,48 @@ +package com.bbteam.budgetbuddies.domain.searchkeyword.controller; + +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.searchkeyword.domain.SearchKeyword; +import com.bbteam.budgetbuddies.domain.searchkeyword.service.SearchKeywordService; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/search-keyword") +public class SearchKeywordController { + + private final SearchKeywordService searchKeywordService; + + @PostMapping("") + public ApiResponse saveKeyword(String keyword) { + + return ApiResponse.onSuccess(searchKeywordService.saveKeyword(keyword)); + } + + @GetMapping("") + public ApiResponse findOne(Long searchKeywordId) { + return ApiResponse.onSuccess(searchKeywordService.findOne(searchKeywordId)); + } + + @GetMapping("/all") + public ApiResponse> findAll(Pageable pageable) { + return ApiResponse.onSuccess(searchKeywordService.findAll(pageable)); + } + + @PutMapping("") + public ApiResponse modifyOne(Long searchKeywordId, String newKeyword) { + return ApiResponse.onSuccess((searchKeywordService.modifyOne(searchKeywordId, newKeyword))); + } + + @DeleteMapping("") + public ApiResponse deleteOne(Long searchKeywordId) { + searchKeywordService.deleteOne(searchKeywordId); + return ApiResponse.onSuccess("OK"); + } + + + + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/domain/SearchKeyword.java b/src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/domain/SearchKeyword.java new file mode 100644 index 00000000..970d72f4 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/domain/SearchKeyword.java @@ -0,0 +1,23 @@ +package com.bbteam.budgetbuddies.domain.searchkeyword.domain; + +import com.bbteam.budgetbuddies.common.BaseEntity; +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SearchKeyword extends BaseEntity { + + private String keyword; + + public void changeKeyword(String newKeyword) { + this.keyword = newKeyword; + } + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/repository/SearchKeywordRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/repository/SearchKeywordRepository.java new file mode 100644 index 00000000..1fcea8b7 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/repository/SearchKeywordRepository.java @@ -0,0 +1,7 @@ +package com.bbteam.budgetbuddies.domain.searchkeyword.repository; + +import com.bbteam.budgetbuddies.domain.searchkeyword.domain.SearchKeyword; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface SearchKeywordRepository extends JpaRepository { +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/service/SearchKeywordService.java b/src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/service/SearchKeywordService.java new file mode 100644 index 00000000..b960855d --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/searchkeyword/service/SearchKeywordService.java @@ -0,0 +1,44 @@ +package com.bbteam.budgetbuddies.domain.searchkeyword.service; + +import com.bbteam.budgetbuddies.apiPayload.code.status.ErrorStatus; +import com.bbteam.budgetbuddies.apiPayload.exception.GeneralException; +import com.bbteam.budgetbuddies.domain.searchkeyword.domain.SearchKeyword; +import com.bbteam.budgetbuddies.domain.searchkeyword.repository.SearchKeywordRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +@RequiredArgsConstructor +public class SearchKeywordService { + + private final SearchKeywordRepository searchKeywordRepository; + + public SearchKeyword saveKeyword(String keyword) { + SearchKeyword searchKeyword = SearchKeyword.builder().keyword(keyword).build(); + searchKeywordRepository.save(searchKeyword); + return searchKeyword; + } + + public SearchKeyword findOne(Long searchKeywordId) { + return searchKeywordRepository.findById(searchKeywordId).orElseThrow(() -> new GeneralException(ErrorStatus._SEARCH_KEYWORD_NOT_FOUND)); + } + + public Page findAll(Pageable pageable) { + return searchKeywordRepository.findAll(pageable); + } + + public SearchKeyword modifyOne(Long searchKeywordId, String keyword) { + SearchKeyword searchKeyword = searchKeywordRepository.findById(searchKeywordId).orElseThrow(() -> new GeneralException(ErrorStatus._SEARCH_KEYWORD_NOT_FOUND)); + searchKeyword.changeKeyword(keyword); + return searchKeyword; + } + + public void deleteOne(Long searchKeywordId) { + SearchKeyword searchKeyword = searchKeywordRepository.findById(searchKeywordId).orElseThrow(() -> new GeneralException(ErrorStatus._SEARCH_KEYWORD_NOT_FOUND)); + searchKeywordRepository.delete(searchKeyword); + } +} From d440dd2c376769ad38d93695a90e14261cf016e7 Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 28 Nov 2024 14:27:22 +0900 Subject: [PATCH 203/204] =?UTF-8?q?[refactor]=20UserId=20->=20AuthUser=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=20=EB=B0=9B=EB=8A=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/controller/CommentControllerApi.java | 9 ++++----- .../budgetbuddies/domain/faq/controller/FaqApi.java | 9 +++------ .../domain/faq/controller/FaqController.java | 8 ++++---- .../domain/mainpage/controller/MainPageApi.java | 12 +++--------- .../mainpage/controller/MainPageController.java | 10 ++++------ 5 files changed, 18 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/CommentControllerApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/CommentControllerApi.java index b8279e41..69336b43 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/CommentControllerApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/CommentControllerApi.java @@ -5,7 +5,8 @@ import com.bbteam.budgetbuddies.domain.comment.dto.CommentRequestDto; import com.bbteam.budgetbuddies.domain.comment.dto.CommentResponseDto; import com.bbteam.budgetbuddies.domain.comment.validation.ExistComment; -import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; +import com.bbteam.budgetbuddies.global.security.utils.AuthUser; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; @@ -24,12 +25,11 @@ public interface CommentControllerApi { @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), }) @Parameters({ - @Parameter(name = "userId", description = "현재 댓글을 다는 유저 id입니다. parameter"), @Parameter(name = "discountInfoId", description = "댓글을 다는 할인 정보 게시글 id입니다. requestBody"), @Parameter(name = "content", description = "댓글 내용입니다. requestBody"), }) ApiResponse saveDiscountInfoComment( - @ExistUser Long userId, + @AuthUser UserDto.AuthUserDto userDto, CommentRequestDto.DiscountInfoCommentRequestDto discountInfoCommentRequestDto); @@ -51,12 +51,11 @@ ApiResponse> findAllByDi @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), }) @Parameters({ - @Parameter(name = "userId", description = "현재 댓글을 다는 유저 id입니다. parameter"), @Parameter(name = "supportInfoId", description = "댓글을 다는 지원 정보 게시글 id입니다. requestBody"), @Parameter(name = "content", description = "댓글 내용입니다. requestBody"), }) ApiResponse saveSupportInfoComment( - @ExistUser Long userId, + @AuthUser UserDto.AuthUserDto userDto, CommentRequestDto.SupportInfoCommentRequestDto supportInfoCommentRequestDto); @Operation(summary = "[User] 특정 지원 정보 게시글의 댓글 조회하기", description = "특정 지원 정보 게시글의 댓글을 가져오는 API입니다.") diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java index 8485960f..1f2d5fb7 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqApi.java @@ -4,7 +4,8 @@ import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.faq.dto.FaqRequestDto; import com.bbteam.budgetbuddies.domain.faq.validation.ExistFaq; -import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; +import com.bbteam.budgetbuddies.global.security.utils.AuthUser; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; @@ -44,11 +45,7 @@ public interface FaqApi { @ApiResponses({ @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), }) - @Parameters({ - @Parameter(name = "userId", description = "FAQ를 사용하는 userId입니다.. pathVariable", in = ParameterIn.QUERY), - } - ) - ApiResponse postFaq(@ExistUser @RequestParam Long userId, + ApiResponse postFaq(@AuthUser UserDto.AuthUserDto userDto, @Valid @org.springframework.web.bind.annotation.RequestBody FaqRequestDto.FaqPostRequest dto); @Operation(summary = "[Admin] FAQ 조회 API", description = "FAQ를 조회하는 API입니다." ) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java index c15fe5d2..5c0b1a1b 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/faq/controller/FaqController.java @@ -5,9 +5,9 @@ import com.bbteam.budgetbuddies.domain.faq.dto.FaqResponseDto; import com.bbteam.budgetbuddies.domain.faq.service.FaqService; import com.bbteam.budgetbuddies.domain.faq.validation.ExistFaq; -import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; +import com.bbteam.budgetbuddies.global.security.utils.AuthUser; import jakarta.validation.Valid; -import jakarta.validation.constraints.Null; import lombok.RequiredArgsConstructor; import org.springdoc.core.annotations.ParameterObject; import org.springframework.data.domain.Page; @@ -26,9 +26,9 @@ public class FaqController implements FaqApi{ @Override @PostMapping("") - public ApiResponse postFaq(@ExistUser @RequestParam Long userId, + public ApiResponse postFaq(@AuthUser UserDto.AuthUserDto userDto, @Valid @RequestBody FaqRequestDto.FaqPostRequest dto) { - return ApiResponse.onSuccess(faqService.postFaq(dto, userId)); + return ApiResponse.onSuccess(faqService.postFaq(dto, userDto.getId())); } @Override diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageApi.java index 757722f8..9caf9f26 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageApi.java @@ -2,13 +2,10 @@ import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.mainpage.dto.MainPageResponseDto; -import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; +import com.bbteam.budgetbuddies.global.security.utils.AuthUser; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.responses.ApiResponses; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestParam; public interface MainPageApi { @@ -16,8 +13,5 @@ public interface MainPageApi { @ApiResponses({ @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), }) - @Parameters({ - @Parameter(name = "userId", description = "현재 데이터를 요청하는 사용자입니다. parameter"), - }) - ApiResponse getMainPage(@RequestParam("userId") @ExistUser Long userId); + ApiResponse getMainPage(@AuthUser UserDto.AuthUserDto userDto); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageController.java b/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageController.java index 534bb66a..1be20827 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageController.java @@ -3,13 +3,11 @@ import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.mainpage.dto.MainPageResponseDto; import com.bbteam.budgetbuddies.domain.mainpage.service.MainPageService; -import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; +import com.bbteam.budgetbuddies.global.security.utils.AuthUser; import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @@ -21,8 +19,8 @@ public class MainPageController implements MainPageApi{ @GetMapping("/main") public ApiResponse getMainPage - (@RequestParam("userId") @ExistUser Long userId) { - MainPageResponseDto mainPage = mainPageService.getMainPage(userId); + (@AuthUser UserDto.AuthUserDto userDto) { + MainPageResponseDto mainPage = mainPageService.getMainPage(userDto.getId()); return ApiResponse.onSuccess(mainPage); } } From 2e3fd217e1f84a52a8cc5233a2881858b3e7965b Mon Sep 17 00:00:00 2001 From: wnd01jun Date: Thu, 28 Nov 2024 14:27:44 +0900 Subject: [PATCH 204/204] =?UTF-8?q?[refactor]=20UserId=20->=20AuthUser=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=20=EB=B0=9B=EB=8A=94=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/comment/controller/CommentController.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/CommentController.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/CommentController.java index 3d96a560..ece131ec 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/CommentController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/CommentController.java @@ -5,7 +5,8 @@ import com.bbteam.budgetbuddies.domain.comment.dto.CommentResponseDto; import com.bbteam.budgetbuddies.domain.comment.service.CommentService; import com.bbteam.budgetbuddies.domain.comment.validation.ExistComment; -import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; +import com.bbteam.budgetbuddies.global.security.utils.AuthUser; import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.domain.Page; @@ -36,9 +37,9 @@ public CommentController(CommentService saveDiscountInfoComment( - @RequestParam("userId") @ExistUser Long userId, + @AuthUser UserDto.AuthUserDto userDto, @RequestBody CommentRequestDto.DiscountInfoCommentRequestDto discountInfoCommentRequestDto){ - CommentResponseDto.DiscountInfoCommentResponseDto dto = discountCommentService.saveComment(userId, discountInfoCommentRequestDto); + CommentResponseDto.DiscountInfoCommentResponseDto dto = discountCommentService.saveComment(userDto.getId(), discountInfoCommentRequestDto); return ApiResponse.onSuccess(dto); } @@ -54,9 +55,9 @@ public ApiResponse> find @PostMapping("/supports/comments") public ApiResponse saveSupportInfoComment( - @RequestParam("userId") @ExistUser Long userId, + @AuthUser UserDto.AuthUserDto userDto, @RequestBody CommentRequestDto.SupportInfoCommentRequestDto supportInfoCommentRequestDto){ - CommentResponseDto.SupportInfoCommentResponseDto dto = supportCommentService.saveComment(userId, supportInfoCommentRequestDto); + CommentResponseDto.SupportInfoCommentResponseDto dto = supportCommentService.saveComment(userDto.getId(), supportInfoCommentRequestDto); return ApiResponse.onSuccess(dto); }