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 0d0c761a..788bf807 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 @@ -3,14 +3,12 @@ import com.bbteam.budgetbuddies.common.BaseEntity; import com.bbteam.budgetbuddies.domain.user.entity.User; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import org.hibernate.annotations.ColumnDefault; @Entity @Getter +@Setter @NoArgsConstructor @AllArgsConstructor @Builder 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 a0df97bc..ee63bbd3 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 @@ -44,6 +44,9 @@ public class ConsumptionGoal extends BaseEntity { @JoinColumn(name = "category_id") private Category category; + public void updateConsumeAmount(Long amount) { + this.consumeAmount += amount; + } public void updateGoalAmount(Long goalAmount) { this.goalAmount = goalAmount; } 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 cb4d297b..1114920e 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 @@ -25,4 +25,5 @@ ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId, ConsumptionGoalListRequestDto consumptionGoalListRequestDto); ConsumptionAnalysisResponseDTO getTopCategoryAndConsumptionAmount(Long userId); + void updateConsumeAmount(Long userId, Long categoryId, Long amount); } \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceImpl.java index 4afa9f99..58547008 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 @@ -216,4 +216,21 @@ private void updateGoalMap(Long userId, LocalDate month, Map goalMap.put(goal.getCategoryId(), goal)); } + + @Override + public void updateConsumeAmount(Long userId, Long categoryId, Long amount) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("Not found user")); + + Category category = categoryRepository.findById(categoryId) + .orElseThrow(() -> new IllegalArgumentException("Not found Category")); + + LocalDate thisMonth = LocalDate.now().withDayOfMonth(1); + ConsumptionGoal consumptionGoal = consumptionGoalRepository + .findConsumptionGoalByUserAndCategoryAndGoalMonth(user, category, thisMonth) + .orElseGet(() -> generateConsumptionGoal(user, category, thisMonth)); + + consumptionGoal.updateConsumeAmount(amount); + consumptionGoalRepository.save(consumptionGoal); + } } 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 48fbdaac..d2dff327 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,42 +1,36 @@ package com.bbteam.budgetbuddies.domain.expense.controller; import java.time.LocalDate; - import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; - import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseResponseDto; 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.RequestBody; 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))) + @ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + @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") @RequestBody 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))) + @ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + @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); + ResponseEntity findExpensesForMonth(Pageable pageable, Long userId, LocalDate date); } 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 a28bfabc..30552a4b 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,6 +2,7 @@ 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; @@ -13,13 +14,10 @@ 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.MonthlyExpenseCompactResponseDto; import com.bbteam.budgetbuddies.domain.expense.service.ExpenseService; - -import io.swagger.v3.oas.annotations.Parameter; import lombok.RequiredArgsConstructor; @RestController @@ -31,7 +29,7 @@ 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); } @@ -39,8 +37,8 @@ public ResponseEntity createExpense( @Override @GetMapping("/{userId}") public ResponseEntity findExpensesForMonth(Pageable pageable, - @PathVariable @Param("userId") Long userId, - @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) { + @PathVariable @Param("userId") Long userId, + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) { return ResponseEntity.ok(expenseService.getMonthlyExpense(pageable, userId, date)); } 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 ab9721e0..2bfef5a4 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 @@ -13,15 +13,7 @@ import com.bbteam.budgetbuddies.domain.user.entity.User; public interface ExpenseRepository extends JpaRepository { - - // 추후 적용 예정 - @Query("SELECT e FROM Expense e WHERE e.user.id = :userId AND e.category.id = :categoryId") - List findByUserIdAndCategoryId(@Param("userId") Long userId, @Param("categoryId") Long categoryId); - - @Query("SELECT e FROM Expense e WHERE e.user.id = :userId") - List findByUserId(@Param("userId") Long userId); - @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); + @Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate); } 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 37c1e178..bf289fc1 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,6 +9,7 @@ import com.bbteam.budgetbuddies.domain.category.entity.Category; import com.bbteam.budgetbuddies.domain.category.repository.CategoryRepository; +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; @@ -28,18 +29,46 @@ public class ExpenseServiceImpl implements ExpenseService { private final UserRepository userRepository; private final CategoryRepository categoryRepository; private final ExpenseConverter expenseConverter; + private final ConsumptionGoalService consumptionGoalService; @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()) { + // category.setUser(user); + // default category + } + /* + Case 2) + !default && 키테고리 테이블의 UserId 컬럼의 값이 나와 맞으면 (= custom cateogory) + */ + else if (!category.getIsDefault() && category.getUser().getId().equals(expenseRequestDto.getUserId())) { + // custom category + } + else { + throw new IllegalArgumentException("User and category are not matched properly."); + } Expense expense = expenseConverter.toExpenseEntity(expenseRequestDto, user, category); expenseRepository.save(expense); + // 소비 목표 업데이트 + consumptionGoalService.updateConsumeAmount(expenseRequestDto.getUserId(), expenseRequestDto.getCategoryId(), expenseRequestDto.getAmount()); + return expenseConverter.toExpenseResponseDto(expense); + /* + 결과 Case 1) 해당 유저의 user_id + immutable 필드 중 하나의 조합으로 Expense 테이블에 저장 + 결과 Case 2) 내가 직접 생성한 카테고리 중 하나로 카테고리를 설정하여 Expense 테이블에 저장 + */ } @Override @@ -49,11 +78,12 @@ public MonthlyExpenseCompactResponseDto getMonthlyExpense(Pageable pageable, Lon LocalDate endOfMonth = localDate.withDayOfMonth(startOfMonth.lengthOfMonth()); User user = userRepository.findById(userId) - .orElseThrow(() -> new IllegalArgumentException("Invalid user ID")); + .orElseThrow(() -> new IllegalArgumentException("Invalid user ID")); Slice expenseSlice = expenseRepository.findAllByUserIdForPeriod(pageable, user, - startOfMonth.atStartOfDay(), endOfMonth.atStartOfDay()); + startOfMonth.atStartOfDay(), endOfMonth.atStartOfDay()); return expenseConverter.toMonthlyExpenseCompactResponseDto(expenseSlice, startOfMonth); } } +