From 245d2c5a61ba9bf4a5e0d91f043a93c2df59d695 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Sat, 17 Aug 2024 15:31:56 +0900 Subject: [PATCH 01/13] =?UTF-8?q?[feat]=20BaseEntity,=20Expense=EC=97=90?= =?UTF-8?q?=20@Setter=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/common/BaseEntity.java | 6 ++---- .../bbteam/budgetbuddies/domain/expense/entity/Expense.java | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/common/BaseEntity.java b/src/main/java/com/bbteam/budgetbuddies/common/BaseEntity.java index ae774f30..661c4fab 100644 --- a/src/main/java/com/bbteam/budgetbuddies/common/BaseEntity.java +++ b/src/main/java/com/bbteam/budgetbuddies/common/BaseEntity.java @@ -2,10 +2,7 @@ import jakarta.persistence.*; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import lombok.experimental.SuperBuilder; import org.hibernate.annotations.*; @@ -22,6 +19,7 @@ @SuperBuilder @SoftDelete(columnName = "deleted") // boolean 타입의 deleted 필드가 추가 @Getter +@Setter public abstract class BaseEntity { @Id 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 254059ee..45e44253 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 @@ -13,14 +13,12 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.validation.constraints.Min; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import lombok.experimental.SuperBuilder; @Entity @Getter +@Setter @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @SuperBuilder From ff4904d075dfd5de3d9f51327ae002e7bb37c579 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Sat, 17 Aug 2024 15:32:15 +0900 Subject: [PATCH 02/13] =?UTF-8?q?[feat]=20Category=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=EC=97=90=20deleted=20=EC=BB=AC=EB=9F=BC=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 --- .../budgetbuddies/domain/category/entity/Category.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 788bf807..547c0f24 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 @@ -1,6 +1,5 @@ package com.bbteam.budgetbuddies.domain.category.entity; -import com.bbteam.budgetbuddies.common.BaseEntity; import com.bbteam.budgetbuddies.domain.user.entity.User; import jakarta.persistence.*; import lombok.*; @@ -24,8 +23,11 @@ public class Category { @ColumnDefault("1") private Boolean isDefault; + @Column(nullable = false) + @ColumnDefault("false") + private Boolean deleted = false; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; - } From fc529fa2198da1f31e06de9714ec962ed45305e5 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Sat, 17 Aug 2024 15:33:08 +0900 Subject: [PATCH 03/13] =?UTF-8?q?[feat]=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=82=AD=EC=A0=9C=20=EC=97=94=EB=93=9C=ED=8F=AC?= =?UTF-8?q?=EC=9D=B8=ED=8A=B8=20=EC=84=A4=EC=A0=95=20=EB=B0=8F=20swagger?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../category/controller/CategoryApi.java | 17 +++++++++++ .../controller/CategoryController.java | 28 +++++++++++-------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/controller/CategoryApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/controller/CategoryApi.java index ca3be092..cc40653e 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/controller/CategoryApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/controller/CategoryApi.java @@ -4,6 +4,7 @@ import com.bbteam.budgetbuddies.domain.category.dto.CategoryResponseDTO; 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.ApiResponse; @@ -38,4 +39,20 @@ ResponseEntity createCategory( }) @GetMapping("/get/{userId}") ResponseEntity> getUserCategories(@PathVariable Long userId); + + @Operation(summary = "특정 카테고리 삭제하기 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))) + }) + @Parameters({ + @Parameter(name = "userId", description = "해당하는 사용자의 id"), + @Parameter(name = "categoryId", description = "삭제할 카테고리의 id"), + }) + ResponseEntity deleteCategory( + @RequestParam Long userId, + @PathVariable Long categoryId + ); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/controller/CategoryController.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/controller/CategoryController.java index f3dedb20..7191d683 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/controller/CategoryController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/controller/CategoryController.java @@ -3,12 +3,7 @@ import java.util.List; 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; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import com.bbteam.budgetbuddies.domain.category.dto.CategoryRequestDTO; import com.bbteam.budgetbuddies.domain.category.dto.CategoryResponseDTO; @@ -32,10 +27,19 @@ public ResponseEntity createCategory( return ResponseEntity.ok(response); } - @Override - @GetMapping("/get/{userId}") - public ResponseEntity> getUserCategories(@PathVariable Long userId) { - List response = categoryService.getUserCategories(userId); - return ResponseEntity.ok(response); - } + @Override + @GetMapping("/get/{userId}") + public ResponseEntity> getUserCategories(@PathVariable Long userId) { + List response = categoryService.getUserCategories(userId); + return ResponseEntity.ok(response); + } + + @Override + @DeleteMapping("/delete/{categoryId}") + public ResponseEntity deleteCategory( + @RequestParam Long userId, + @PathVariable Long categoryId) { + categoryService.deleteCategory(categoryId, userId); + return ResponseEntity.ok("Successfully deleted category!"); + } } From cb613cd4ec2a9a6a05b07bc2e4e69bdd1da65ad7 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Sat, 17 Aug 2024 15:33:45 +0900 Subject: [PATCH 04/13] =?UTF-8?q?[feat]=20findById=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=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 --- .../domain/category/repository/CategoryRepository.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/repository/CategoryRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/repository/CategoryRepository.java index fb64bd42..5bc49395 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/repository/CategoryRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/repository/CategoryRepository.java @@ -1,6 +1,7 @@ package com.bbteam.budgetbuddies.domain.category.repository; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -15,4 +16,5 @@ public interface CategoryRepository extends JpaRepository { @Query("SELECT c FROM Category c WHERE c.isDefault = true") List findAllByIsDefaultTrue(); boolean existsByUserIdAndName(Long userId, String name); + Optional findById(Long id); } \ No newline at end of file From 316873e8b745e34aec9f88a617013a6556d4c5b0 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Sat, 17 Aug 2024 15:34:05 +0900 Subject: [PATCH 05/13] =?UTF-8?q?[feat]=20findByCategoryIdAndUserId=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=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 --- .../domain/expense/repository/ExpenseRepository.java | 2 ++ 1 file changed, 2 insertions(+) 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 2bfef5a4..c7c99704 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 @@ -16,4 +16,6 @@ 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") Slice findAllByUserIdForPeriod(Pageable pageable, @Param("user") User user, @Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate); + + List findByCategoryIdAndUserId(Long categoryId, Long userId); } From 0c07b0b9fad582a29252e6319fa94b1c902b74e9 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Sat, 17 Aug 2024 15:34:14 +0900 Subject: [PATCH 06/13] =?UTF-8?q?[feat]=20@Setter=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/consumptiongoal/entity/ConsumptionGoal.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 d1dec930..a0dea332 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 @@ -12,15 +12,13 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.validation.constraints.Min; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import lombok.experimental.SuperBuilder; import lombok.extern.slf4j.Slf4j; @Entity @Getter +@Setter @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @SuperBuilder From 65254b6495f95946bf1c5e0c394f67608a04a521 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Sat, 17 Aug 2024 15:35:42 +0900 Subject: [PATCH 07/13] =?UTF-8?q?[feat]=20category=5Fid=EC=99=80=20user=5F?= =?UTF-8?q?id=EB=A1=9C=20=EB=8B=A8=EC=9D=BC=20ConsumptionGoal=EC=9D=84=20?= =?UTF-8?q?=EC=B0=BE=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=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 --- .../repository/ConsumptionGoalRepository.java | 113 +++++++++++------- 1 file changed, 68 insertions(+), 45 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 00986dd8..a635ff32 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 @@ -23,61 +23,84 @@ public interface ConsumptionGoalRepository extends JpaRepository findConsumptionGoalByUserIdAndGoalMonth(Long userId, LocalDate goalMonth); - Optional findConsumptionGoalByUserAndCategoryAndGoalMonth(User user, Category category, - LocalDate goalMonth); + Optional findConsumptionGoalByUserAndCategoryAndGoalMonth(User user, Category category, LocalDate goalMonth); - @Query("SELECT AVG(cg.consumeAmount) FROM ConsumptionGoal cg " + "JOIN cg.category c " + "WHERE c.id = :categoryId " - + "AND cg.goalMonth BETWEEN :startOfWeek AND :endOfWeek " - + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + "AND cg.user.gender = :peerGender") + @Query("SELECT AVG(cg.consumeAmount) FROM ConsumptionGoal cg " + + "JOIN cg.category c " + + "WHERE c.id = :categoryId " + + "AND cg.goalMonth BETWEEN :startOfWeek AND :endOfWeek " + + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + + "AND cg.user.gender = :peerGender") Optional findAvgConsumptionByCategoryIdAndCurrentWeek( - @Param("categoryId") Long categoryId, @Param("startOfWeek") LocalDate startOfWeek, - @Param("endOfWeek") LocalDate endOfWeek, @Param("peerAgeStart") int peerAgeStart, - @Param("peerAgeEnd") int peerAgeEnd, @Param("peerGender") Gender peerGender); + @Param("categoryId") Long categoryId, + @Param("startOfWeek") LocalDate startOfWeek, + @Param("endOfWeek") LocalDate endOfWeek, + @Param("peerAgeStart") int peerAgeStart, + @Param("peerAgeEnd") int peerAgeEnd, + @Param("peerGender") Gender peerGender); - @Query( - "SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AvgConsumptionGoalDto(" - + "cg.category.id, AVG(cg.consumeAmount))" - + "FROM ConsumptionGoal cg " + "WHERE cg.category.isDefault = true " - + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + "AND cg.user.gender = :peerGender " - + "AND cg.goalMonth >= :currentMonth " + "GROUP BY cg.category.id " + "ORDER BY AVG(cg.consumeAmount) DESC") + @Query("SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AvgConsumptionGoalDto(" + + "cg.category.id, AVG(cg.consumeAmount))" + + "FROM ConsumptionGoal cg " + + "WHERE cg.category.isDefault = true " + + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + + "AND cg.user.gender = :peerGender " + + "AND cg.goalMonth >= :currentMonth " + + "GROUP BY cg.category.id " + + "ORDER BY AVG(cg.consumeAmount) DESC") List findAvgConsumptionAmountByCategory( - @Param("peerAgeStart") int peerAgeStart, - @Param("peerAgeEnd") int peerAgeEnd, - @Param("peerGender") Gender peerGender, - @Param("currentMonth") LocalDate currentMonth); + @Param("peerAgeStart") int peerAgeStart, + @Param("peerAgeEnd") int peerAgeEnd, + @Param("peerGender") Gender peerGender, + @Param("currentMonth") LocalDate currentMonth); - @Query("SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.MyConsumptionGoalDto(" - + "cg.category.id, SUM(cg.consumeAmount)) " + "FROM ConsumptionGoal cg " - + "WHERE cg.category.isDefault = true " + "AND cg.user.id = :userId " - + "GROUP BY cg.category.id " + "ORDER BY cg.category.id") + @Query("SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.MyConsumptionGoalDto(" + + "cg.category.id, SUM(cg.consumeAmount)) " + + "FROM ConsumptionGoal cg " + + "WHERE cg.category.isDefault = true " + + "AND cg.user.id = :userId " + + "GROUP BY cg.category.id " + + "ORDER BY cg.category.id") List findAllConsumptionAmountByUserId(@Param("userId") Long userId); - @Query( - "SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AvgConsumptionGoalDto(cg.category.id, AVG(" - + "cg.goalAmount))" - + "FROM ConsumptionGoal cg " + "WHERE cg.category.isDefault = true " - + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + "AND cg.user.gender = :peerGender " - + "AND cg.goalMonth >= :currentMonth " + "GROUP BY cg.category.id " + "ORDER BY AVG(cg.goalAmount) DESC") + @Query("SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.AvgConsumptionGoalDto(" + + "cg.category.id, AVG(cg.goalAmount))" + + "FROM ConsumptionGoal cg " + + "WHERE cg.category.isDefault = true " + + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + + "AND cg.user.gender = :peerGender " + + "AND cg.goalMonth >= :currentMonth " + + "GROUP BY cg.category.id " + + "ORDER BY AVG(cg.goalAmount) DESC") List findAvgGoalAmountByCategory( - @Param("peerAgeStart") int peerAgeStart, - @Param("peerAgeEnd") int peerAgeEnd, - @Param("peerGender") Gender peerGender, - @Param("currentMonth") LocalDate currentMonth); + @Param("peerAgeStart") int peerAgeStart, + @Param("peerAgeEnd") int peerAgeEnd, + @Param("peerGender") Gender peerGender, + @Param("currentMonth") LocalDate currentMonth); - @Query("SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.MyConsumptionGoalDto(" - + "cg.category.id, SUM(cg.goalAmount)) " + "FROM ConsumptionGoal cg " - + "WHERE cg.category.isDefault = true " + "AND cg.user.id = :userId " - + "GROUP BY cg.category.id " + "ORDER BY cg.category.id") + @Query("SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.MyConsumptionGoalDto(" + + "cg.category.id, SUM(cg.goalAmount)) " + + "FROM ConsumptionGoal cg " + + "WHERE cg.category.isDefault = true " + + "AND cg.user.id = :userId " + + "GROUP BY cg.category.id " + + "ORDER BY cg.category.id") List findAllGoalAmountByUserId(@Param("userId") Long userId); - @Query( - "SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.CategoryConsumptionCountDto(" - + "cg.category.id, COUNT(cg)) " + "FROM ConsumptionGoal cg " + "WHERE cg.category.isDefault = true " - + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + "AND cg.user.gender = :peerGender " - + "AND cg.goalMonth >= :currentMonth " + "GROUP BY cg.category.id " + "ORDER BY COUNT(cg) DESC") + @Query("SELECT new com.bbteam.budgetbuddies.domain.consumptiongoal.dto.CategoryConsumptionCountDto(" + + "cg.category.id, COUNT(cg)) " + + "FROM ConsumptionGoal cg " + + "WHERE cg.category.isDefault = true " + + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + + "AND cg.user.gender = :peerGender " + + "AND cg.goalMonth >= :currentMonth " + + "GROUP BY cg.category.id " + + "ORDER BY COUNT(cg) DESC") List findTopCategoriesByConsumptionCount( - @Param("peerAgeStart") int peerAgeStart, - @Param("peerAgeEnd") int peerAgeEnd, - @Param("peerGender") Gender peerGender, - @Param("currentMonth") LocalDate currentMonth); + @Param("peerAgeStart") int peerAgeStart, + @Param("peerAgeEnd") int peerAgeEnd, + @Param("peerGender") Gender peerGender, + @Param("currentMonth") LocalDate currentMonth); + + Optional findByCategoryIdAndUserId(Long categoryId, Long userId); } From 84257a28a5c7c4a1b0f5aa26a181d8aae6948b44 Mon Sep 17 00:00:00 2001 From: ryogaeng Date: Sat, 17 Aug 2024 15:35:52 +0900 Subject: [PATCH 08/13] =?UTF-8?q?[feat]=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=82=AD=EC=A0=9C=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 --- .../category/service/CategoryService.java | 2 + .../category/service/CategoryServiceImpl.java | 39 ++++++++++++++++++- 2 files changed, 39 insertions(+), 2 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 01e2c994..3d12ce55 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 @@ -15,5 +15,7 @@ public interface CategoryService { List getUserCategories(Long userId); Category handleCategoryChange(Expense expense, ExpenseUpdateRequestDto request, User user); + + void deleteCategory(Long id, 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 055e9415..4adf14d0 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 @@ -4,6 +4,7 @@ import java.util.List; import java.util.stream.Collectors; +import com.bbteam.budgetbuddies.domain.expense.repository.ExpenseRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -32,6 +33,7 @@ public class CategoryServiceImpl implements CategoryService { private final UserRepository userRepository; private final CategoryConverter categoryConverter; private final ConsumptionGoalRepository consumptionGoalRepository; + private final ExpenseRepository expenseRepository; @Override public CategoryResponseDTO createCategory(Long userId, CategoryRequestDTO categoryRequestDTO) { @@ -62,7 +64,7 @@ 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()); @@ -72,11 +74,44 @@ public List getUserCategories(Long userId) { @Transactional(readOnly = true) public Category handleCategoryChange(Expense expense, ExpenseUpdateRequestDto request, User user) { Category categoryToReplace = categoryRepository.findById(request.getCategoryId()) - .orElseThrow(() -> new IllegalArgumentException("Not found category")); + .orElseThrow(() -> new IllegalArgumentException("Not found category")); consumptionGoalService.recalculateConsumptionAmount(expense, request, user); return categoryToReplace; } + @Override + @Transactional + public void deleteCategory(Long categoryId, Long userId) { + categoryRepository.findById(categoryId).ifPresent(category -> { + if (category.getIsDefault()) { + throw new IllegalArgumentException("Default categories cannot be deleted."); + } + + List expenses = expenseRepository.findByCategoryIdAndUserId(categoryId, userId); + long totalAmount = expenses.stream().mapToLong(Expense::getAmount).sum(); + + Category etcCategory = categoryRepository.findById(10L) + .orElseThrow(() -> new IllegalArgumentException("etc category not found")); + + expenses.forEach(expense -> { + expense.setCategory(etcCategory); + expenseRepository.save(expense); + }); + + ConsumptionGoal goal = consumptionGoalRepository.findByCategoryIdAndUserId(10L, userId) + .orElseThrow(() -> new IllegalArgumentException("No consumption goal found for category_id 10.")); + goal.setConsumeAmount(goal.getConsumeAmount() + totalAmount); + consumptionGoalRepository.save(goal); + + consumptionGoalRepository.findByCategoryIdAndUserId(categoryId, userId).ifPresent(consumptionGoal -> { + consumptionGoal.setDeleted(true); + consumptionGoalRepository.save(consumptionGoal); + }); + + category.setDeleted(true); + categoryRepository.save(category); + }); + } } \ No newline at end of file From e2c81af2efc1d15dc798f416de4ac0f00497e4e8 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sat, 17 Aug 2024 21:17:06 +0900 Subject: [PATCH 09/13] =?UTF-8?q?[feat]=20=EC=9A=94=EC=9D=BC=EB=B3=84?= =?UTF-8?q?=EB=A1=9C=20=EB=B0=98=ED=99=98=ED=95=98=EA=B8=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20MonthlyExpenseCompactResponseDto=20=ED=95=84?= =?UTF-8?q?=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 --- .../domain/expense/dto/CompactExpenseResponseDto.java | 7 ------- .../expense/dto/MonthlyExpenseCompactResponseDto.java | 7 ++++--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/CompactExpenseResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/CompactExpenseResponseDto.java index 0983666e..06c6594f 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/CompactExpenseResponseDto.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/CompactExpenseResponseDto.java @@ -1,9 +1,5 @@ package com.bbteam.budgetbuddies.domain.expense.dto; -import java.time.LocalDateTime; - -import com.fasterxml.jackson.annotation.JsonFormat; - import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -19,7 +15,4 @@ public class CompactExpenseResponseDto { private String description; private Long amount; private Long categoryId; - - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") - private LocalDateTime expenseDate; } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/MonthlyExpenseCompactResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/MonthlyExpenseCompactResponseDto.java index bd7ce82e..c23523e6 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/MonthlyExpenseCompactResponseDto.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/MonthlyExpenseCompactResponseDto.java @@ -2,6 +2,7 @@ import java.time.LocalDate; import java.util.List; +import java.util.Map; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -15,7 +16,7 @@ @Builder public class MonthlyExpenseCompactResponseDto { private LocalDate expenseMonth; - private int currentPage; - private boolean hasNext; - private List expenseList; + private Long totalConsumptionAmount; + + private Map> expenses; } From 3610569456bc82a88ea64a7657a2921d2bb3d0eb Mon Sep 17 00:00:00 2001 From: JunRain Date: Sat, 17 Aug 2024 21:56:12 +0900 Subject: [PATCH 10/13] =?UTF-8?q?[feat]=20Pageable=20=EC=A0=9C=EA=B1=B0?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20Slice=EC=97=90=EC=84=9C=20List?= =?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/expense/repository/ExpenseRepository.java | 6 ++---- 1 file changed, 2 insertions(+), 4 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 2bfef5a4..38da38e7 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 @@ -3,8 +3,6 @@ import java.time.LocalDateTime; import java.util.List; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -14,6 +12,6 @@ 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") - Slice findAllByUserIdForPeriod(Pageable pageable, @Param("user") User user, - @Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate); + List findAllByUserIdForPeriod(@Param("user") User user, + @Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate); } From 739b50d066c99b71ba09c55476b3f13465b43d92 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sat, 17 Aug 2024 22:00:51 +0900 Subject: [PATCH 11/13] =?UTF-8?q?[feat]=20=EC=9B=94=EB=B3=84=20=EC=86=8C?= =?UTF-8?q?=EB=B9=84=20=EC=A1=B0=ED=9A=8C=EC=97=90=20=EC=86=8C=EB=B9=84=20?= =?UTF-8?q?=EC=B4=9D=EC=95=A1=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=9A=94?= =?UTF-8?q?=EC=9D=BC=EB=B3=84=EB=A1=9C=20=EC=86=8C=EB=B9=84=EB=A5=BC=20?= =?UTF-8?q?=EB=AC=B6=EC=96=B4=EC=84=9C=20=EB=B0=98=ED=99=98=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 --- .../expense/converter/ExpenseConverter.java | 29 ++++++++++++------- .../expense/service/ExpenseService.java | 4 +-- .../expense/service/ExpenseServiceImpl.java | 24 +++++++-------- 3 files changed, 30 insertions(+), 27 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 d4353d6d..f54098eb 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 @@ -1,9 +1,13 @@ package com.bbteam.budgetbuddies.domain.expense.converter; import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.TextStyle; import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; -import org.springframework.data.domain.Slice; import org.springframework.stereotype.Component; import com.bbteam.budgetbuddies.domain.category.entity.Category; @@ -39,26 +43,31 @@ public ExpenseResponseDto toExpenseResponseDto(Expense expense) { .build(); } - public MonthlyExpenseCompactResponseDto toMonthlyExpenseCompactResponseDto(Slice expenseSlice, + public MonthlyExpenseCompactResponseDto toMonthlyExpenseCompactResponseDto(List expenseList, LocalDate startOfMonth) { - List compactResponseList = expenseSlice.getContent().stream() - .map(this::toExpenseCompactResponseDto).toList(); + Long totalConsumptionAmount = expenseList.stream().mapToLong(Expense::getAmount).sum(); - return MonthlyExpenseCompactResponseDto - .builder() + Map> expenses = expenseList.stream().collect( + Collectors.groupingBy(e -> this.convertDayToKorean(e.getExpenseDate()), + Collectors.mapping(this::toExpenseCompactResponseDto, Collectors.toList()))); + + return MonthlyExpenseCompactResponseDto.builder() .expenseMonth(startOfMonth) - .currentPage(expenseSlice.getPageable().getPageNumber()) - .hasNext(expenseSlice.hasNext()) - .expenseList(compactResponseList) + .totalConsumptionAmount(totalConsumptionAmount) + .expenses(expenses) .build(); } + private String convertDayToKorean(LocalDateTime localDateTime) { + return localDateTime.getDayOfMonth() + "일 " + localDateTime.getDayOfWeek() + .getDisplayName(TextStyle.FULL, Locale.KOREAN); + } + private CompactExpenseResponseDto toExpenseCompactResponseDto(Expense expense) { return CompactExpenseResponseDto.builder() .expenseId(expense.getId()) .description(expense.getDescription()) .amount(expense.getAmount()) - .expenseDate(expense.getExpenseDate()) .categoryId(expense.getCategory().getId()) .build(); } 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 43351aaf..1ca3e6cb 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 @@ -2,8 +2,6 @@ import java.time.LocalDate; -import org.springframework.data.domain.Pageable; - import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; @@ -12,7 +10,7 @@ public interface ExpenseService { ExpenseResponseDto createExpense(ExpenseRequestDto expenseRequestDto); - MonthlyExpenseCompactResponseDto getMonthlyExpense(Pageable pageable, Long userId, LocalDate localDate); + MonthlyExpenseCompactResponseDto getMonthlyExpense(Long userId, LocalDate localDate); ExpenseResponseDto findExpenseResponseFromUserIdAndExpenseId(Long userId, Long expenseId); 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 7966462c..ccc35448 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,16 +1,14 @@ package com.bbteam.budgetbuddies.domain.expense.service; import java.time.LocalDate; +import java.util.List; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; 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.service.ConsumptionGoalService; import com.bbteam.budgetbuddies.domain.expense.converter.ExpenseConverter; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; @@ -38,16 +36,17 @@ public class ExpenseServiceImpl implements ExpenseService { @Override public ExpenseResponseDto createExpense(ExpenseRequestDto expenseRequestDto) { User user = userRepository.findById(expenseRequestDto.getUserId()) - .orElseThrow(() -> new IllegalArgumentException("Invalid user ID")); + .orElseThrow(() -> new IllegalArgumentException("Invalid user ID")); Category category = categoryRepository.findById(expenseRequestDto.getCategoryId()) - .orElseThrow(() -> new IllegalArgumentException("Invalid category ID")); + .orElseThrow(() -> new IllegalArgumentException("Invalid category ID")); /* case 1) - 카테고리 ID가 1~10 사이 && default => DB의 immutable 필드인 default category - DB 관리 이슈로 category에 default 카테고리의 중복이 발생할 경우, 이를 대비하기 위해 1<= id <= 10 조건도 추가 */ - if (expenseRequestDto.getCategoryId() >= 1 && expenseRequestDto.getCategoryId() <= 10 && category.getIsDefault()) { + if (expenseRequestDto.getCategoryId() >= 1 && expenseRequestDto.getCategoryId() <= 10 + && category.getIsDefault()) { // category.setUser(user); // default category } @@ -57,8 +56,7 @@ public ExpenseResponseDto createExpense(ExpenseRequestDto expenseRequestDto) { */ else if (!category.getIsDefault() && category.getUser().getId().equals(expenseRequestDto.getUserId())) { // custom category - } - else { + } else { throw new IllegalArgumentException("User and category are not matched properly."); } @@ -96,11 +94,10 @@ else if (!category.getIsDefault() && category.getUser().getId().equals(expenseRe */ } - @Override 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(); @@ -115,15 +112,14 @@ public void deleteExpense(Long expenseId) { @Override @Transactional(readOnly = true) - public MonthlyExpenseCompactResponseDto getMonthlyExpense(Pageable pageable, Long userId, LocalDate localDate) { + public MonthlyExpenseCompactResponseDto getMonthlyExpense(Long userId, LocalDate localDate) { LocalDate startOfMonth = localDate.withDayOfMonth(1); LocalDate endOfMonth = localDate.withDayOfMonth(startOfMonth.lengthOfMonth()); - User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("Invalid user ID")); - Slice expenseSlice = expenseRepository.findAllByUserIdForPeriod(pageable, user, - startOfMonth.atStartOfDay(), endOfMonth.atStartOfDay()); + List expenseSlice = expenseRepository.findAllByUserIdForPeriod(user, + startOfMonth.atStartOfDay(), endOfMonth.atStartOfDay()); return expenseConverter.toMonthlyExpenseCompactResponseDto(expenseSlice, startOfMonth); } From 7ba466124b7aa29bb3fe3ab7012f2c78668d1316 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sat, 17 Aug 2024 22:02:25 +0900 Subject: [PATCH 12/13] =?UTF-8?q?[feat]=20ExpenseController=20=EC=9B=94?= =?UTF-8?q?=EB=B3=84=20=EC=86=8C=EB=B9=84=20=EC=A1=B0=ED=9A=8C=20Paginatio?= =?UTF-8?q?n=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/expense/controller/ExpenseApi.java | 48 +++++++++++-------- .../expense/controller/ExpenseController.java | 34 ++++++++----- 2 files changed, 48 insertions(+), 34 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 62fd75e9..4785623b 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 @@ -1,50 +1,55 @@ package com.bbteam.budgetbuddies.domain.expense.controller; import java.time.LocalDate; -import org.springframework.data.domain.Pageable; + 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.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.MonthlyExpenseCompactResponseDto; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; -import org.springframework.web.bind.annotation.DeleteMapping; public interface ExpenseApi { @Operation(summary = "소비 내역 추가", description = "사용자가 소비 내역을 추가합니다.") @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))), - @ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), - @ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class)))}) + @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))), + @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, category_id, amount, description, expenseDate") ExpenseRequestDto expenseRequestDto); + @Parameter(description = "user_id, category_id, amount, description, expenseDate") ExpenseRequestDto expenseRequestDto); @Operation(summary = "월별 소비 조회", description = "무한 스크롤을 통한 조회로 예상하여 Slice를 통해서 조회") @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))), - @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(Pageable pageable, Long userId, LocalDate date); + @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))), + @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); @Operation(summary = "단일 소비 조회하기", description = "queryParameter를 통해 소비 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))), - @ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), - @ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class)))}) + @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))), + @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); @@ -56,14 +61,15 @@ ResponseEntity createExpense( @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(@PathVariable @Param("userId") Long userId, + @RequestBody ExpenseUpdateRequestDto request); @Operation(summary = "소비 내역 삭제", description = "사용자가 소비 내역을 삭제합니다.") @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, 성공"), + @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))) }) @DeleteMapping("/delete/{expenseId}") ResponseEntity deleteExpense(@Parameter(description = "expense_id") @PathVariable Long expenseId); 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 9e55be5b..7ab52043 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 @@ -2,17 +2,25 @@ import java.time.LocalDate; -import io.swagger.v3.oas.annotations.Parameter; -import org.springframework.data.domain.Pageable; import org.springframework.data.repository.query.Param; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; +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.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +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.ExpenseUpdateRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.MonthlyExpenseCompactResponseDto; import com.bbteam.budgetbuddies.domain.expense.service.ExpenseService; + +import io.swagger.v3.oas.annotations.Parameter; import lombok.RequiredArgsConstructor; @RestController @@ -24,40 +32,40 @@ public class ExpenseController implements ExpenseApi { @Override @PostMapping("/add") public ResponseEntity createExpense( - @Parameter(description = "user_id, category_id, amount, description, expenseDate") - @RequestBody ExpenseRequestDto expenseRequestDto) { + @Parameter(description = "user_id, category_id, amount, description, expenseDate") + @RequestBody ExpenseRequestDto expenseRequestDto) { ExpenseResponseDto response = expenseService.createExpense(expenseRequestDto); return ResponseEntity.ok(response); } @Override @GetMapping("/{userId}") - public ResponseEntity findExpensesForMonth(Pageable pageable, - @PathVariable @Param("userId") Long userId, - @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) { + public ResponseEntity findExpensesForMonth( + @PathVariable @Param("userId") Long userId, + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) { - return ResponseEntity.ok(expenseService.getMonthlyExpense(pageable, userId, date)); + return ResponseEntity.ok(expenseService.getMonthlyExpense(userId, date)); } @Override @GetMapping("/{userId}/{expenseId}") public ResponseEntity findExpense(@PathVariable @Param("userId") Long userId, - @PathVariable @Param("expenseId") Long expenseId) { + @PathVariable @Param("expenseId") Long expenseId) { return ResponseEntity.ok(expenseService.findExpenseResponseFromUserIdAndExpenseId(userId, expenseId)); } @Override @PostMapping("/{userId}") public ResponseEntity updateExpense(@PathVariable @Param("userId") Long userId, - @RequestBody ExpenseUpdateRequestDto request) { + @RequestBody ExpenseUpdateRequestDto request) { ExpenseResponseDto response = expenseService.updateExpense(userId, request); return ResponseEntity.ok(response); } @DeleteMapping("/delete/{expenseId}") public ResponseEntity deleteExpense( - @Parameter(description = "expense_id") - @PathVariable Long expenseId) { + @Parameter(description = "expense_id") + @PathVariable Long expenseId) { expenseService.deleteExpense(expenseId); return ResponseEntity.ok("Successfully deleted expense!"); } From 2e642d436bd4d15378aeb560303fa3776cdfc15c Mon Sep 17 00:00:00 2001 From: JunRain Date: Sat, 17 Aug 2024 22:27:51 +0900 Subject: [PATCH 13/13] =?UTF-8?q?[test]=20=EC=9B=94=EB=B3=84=20=EC=86=8C?= =?UTF-8?q?=EB=B9=84=20=EC=A1=B0=ED=9A=8C=20=EC=86=8C=EB=B9=84=EB=A5=BC=20?= =?UTF-8?q?d=EC=9D=BC=20N=EC=9A=94=EC=9D=BC=EB=A1=9C=20=EB=AC=B6=EC=96=B4?= =?UTF-8?q?=EC=84=9C=20=EB=B0=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ExpenseRepositoryTest.java | 12 +- .../service/ExpenseServiceImplTest.java | 106 ++++++++---------- 2 files changed, 52 insertions(+), 66 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 9c1b1156..c1b34ada 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 @@ -11,9 +11,6 @@ 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 org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; import com.bbteam.budgetbuddies.domain.category.entity.Category; import com.bbteam.budgetbuddies.domain.category.repository.CategoryRepository; @@ -42,17 +39,16 @@ void findAllByUserIdForPeriod_Success() { Category userCategory = categoryRepository.save( Category.builder().name("유저 카테고리").user(user).isDefault(false).build()); - LocalDate startDate = LocalDate.of(2024, 07, 01); + LocalDate startDate = LocalDate.of(2024, 7, 1); LocalDate endDate = startDate.withDayOfMonth(startDate.lengthOfMonth()); List expected = setExpense(user, userCategory, startDate); - Pageable pageable = PageRequest.of(0, 5); - - Slice result = expenseRepository.findAllByUserIdForPeriod(pageable, user, + // when + List result = expenseRepository.findAllByUserIdForPeriod(user, startDate.atStartOfDay(), endDate.atStartOfDay()); - assertThat(result.getContent()).usingRecursiveComparison().isEqualTo(expected); + assertThat(result).usingRecursiveComparison().isEqualTo(expected); } private List setExpense(User user, Category userCategory, LocalDate startDate) { 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 f0f5c05f..a82e77d1 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 @@ -6,8 +6,8 @@ import java.time.LocalDate; import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; @@ -19,10 +19,6 @@ import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; -import org.springframework.data.domain.SliceImpl; import com.bbteam.budgetbuddies.domain.category.entity.Category; import com.bbteam.budgetbuddies.domain.category.repository.CategoryRepository; @@ -58,73 +54,67 @@ void setUp() { } @Test - @DisplayName("getMonthlyExpense : 성공") + @DisplayName("월별 소비 조회 소비를 d일 N요일로 묶어서 반환") void getMonthlyExpense_Success() { // given - final int pageSize = 5; - given(userRepository.findById(user.getId())).willReturn(Optional.of(user)); Category userCategory = Mockito.spy(Category.builder().build()); given(userCategory.getId()).willReturn(-1L); - LocalDate requestMonth = LocalDate.of(2024, 07, 8); - Pageable requestPage = PageRequest.of(0, pageSize); - - List expenses = generateExpenseList(requestMonth, user, userCategory, pageSize); - - Slice expenseSlice = new SliceImpl<>(expenses, requestPage, false); - given(expenseRepository.findAllByUserIdForPeriod(any(Pageable.class), any(User.class), any(LocalDateTime.class), - any(LocalDateTime.class))).willReturn(expenseSlice); - - MonthlyExpenseCompactResponseDto expected = generateExpectation(requestMonth, pageSize); + LocalDate requestMonth = LocalDate.of(2024, 7, 8); + + List expenses = generateExpenseList(requestMonth, user, userCategory); + + given(expenseRepository.findAllByUserIdForPeriod(any(User.class), any(LocalDateTime.class), + any(LocalDateTime.class))).willReturn(expenses); + + MonthlyExpenseCompactResponseDto expected = + MonthlyExpenseCompactResponseDto.builder() + .expenseMonth(LocalDate.of(2024, 07, 01)) + .totalConsumptionAmount(300_000L) + .expenses(Map.of( + "2일 화요일", List.of(CompactExpenseResponseDto.builder() + .amount(200_000L) + .description("User 소비") + .expenseId(-2L) + .categoryId(userCategory.getId()) + .build()), + "1일 월요일", List.of(CompactExpenseResponseDto.builder() + .amount(100_000L) + .description("User 소비") + .expenseId(-1L) + .categoryId(userCategory.getId()) + .build()))) + .build(); // when - MonthlyExpenseCompactResponseDto result = expenseService.getMonthlyExpense(requestPage, user.getId(), - requestMonth); + MonthlyExpenseCompactResponseDto result = expenseService.getMonthlyExpense(user.getId(), requestMonth); // then assertThat(result).usingRecursiveComparison().isEqualTo(expected); } - private List generateExpenseList(LocalDate month, User user, Category userCategory, int repeat) { - List expenses = new ArrayList<>(); - for (int i = repeat; i > 0; i--) { - Expense expense = Mockito.spy(Expense.builder() - .amount(i * 100000L) - .description("User 소비" + i) - .expenseDate(month.withDayOfMonth(i).atStartOfDay()) - .user(user) - .category(userCategory) - .build()); - given(expense.getId()).willReturn((long)-i); - - expenses.add(expense); - } - return expenses; - } - - private MonthlyExpenseCompactResponseDto generateExpectation(LocalDate month, int count) { - return MonthlyExpenseCompactResponseDto.builder() - .expenseMonth(month.withDayOfMonth(1)) - .hasNext(false) - .currentPage(0) - .expenseList(generateCompactExpenseResponseList(month, count)) - .build(); - } - - private List generateCompactExpenseResponseList(LocalDate month, int count) { - List compactExpenses = new ArrayList<>(); - for (int i = count; i > 0; i--) { - compactExpenses.add(CompactExpenseResponseDto.builder() - .description("User 소비" + i) - .expenseId((long)-i) - .expenseDate(month.withDayOfMonth(i).atStartOfDay()) - .amount(i * 100000L) - .categoryId(-1L) - .build()); - } - return compactExpenses; + private List generateExpenseList(LocalDate month, User user, Category userCategory) { + Expense e1 = Mockito.spy(Expense.builder() + .amount(100_000L) + .description("User 소비") + .expenseDate(month.withDayOfMonth(1).atStartOfDay()) + .user(user) + .category(userCategory) + .build()); + given(e1.getId()).willReturn((-1L)); + + Expense e2 = Mockito.spy(Expense.builder() + .amount(200_000L) + .description("User 소비") + .expenseDate(month.withDayOfMonth(2).atStartOfDay()) + .user(user) + .category(userCategory) + .build()); + given(e2.getId()).willReturn((-2L)); + + return List.of(e1, e2); } @Test