Skip to content

Commit

Permalink
Merge pull request #55 from BudgetBuddiesTeam/feat/#54
Browse files Browse the repository at this point in the history
  • Loading branch information
JunRain2 authored Jul 29, 2024
2 parents 417a0f5 + 354cb33 commit 25bafc8
Show file tree
Hide file tree
Showing 14 changed files with 544 additions and 140 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.bbteam.budgetbuddies.domain.consumptiongoal.converter;

import java.time.LocalDate;
import java.util.List;

import org.springframework.stereotype.Component;

import com.bbteam.budgetbuddies.domain.category.entity.Category;
import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseDto;
import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto;
import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal;

@Component
Expand All @@ -25,4 +29,23 @@ public ConsumptionGoalResponseDto toConsumptionGoalResponseDto(ConsumptionGoal c
.consumeAmount(consumptionGoal.getConsumeAmount())
.build();
}

public ConsumptionGoalResponseListDto toConsumptionGoalResponseListDto(
List<ConsumptionGoalResponseDto> consumptionGoalList, LocalDate goalMonth) {
return ConsumptionGoalResponseListDto.builder()
.goalMonth(goalMonth)
.totalGoalAmount(sumTotalGoalAmount(consumptionGoalList))
.totalConsumptionAmount(sumTotalConsumptionAmount(consumptionGoalList))
.consumptionGoalList(consumptionGoalList)
.build();

}

private Long sumTotalConsumptionAmount(List<ConsumptionGoalResponseDto> consumptionGoalList) {
return consumptionGoalList.stream().reduce(0L, (sum, c2) -> sum + c2.getConsumeAmount(), Long::sum);
}

private Long sumTotalGoalAmount(List<ConsumptionGoalResponseDto> consumptionGoalList) {
return consumptionGoalList.stream().reduce(0L, (sum, c2) -> sum + c2.getGoalAmount(), Long::sum);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@
import java.util.List;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor
@Builder
public class ConsumptionGoalResponseListDto {
private LocalDate goalMonth;
private Long totalGoalAmount;
private Long totalConsumptionAmount;
private List<ConsumptionGoalResponseDto> consumptionGoalList;

public ConsumptionGoalResponseListDto(LocalDate goalMonth, List<ConsumptionGoalResponseDto> consumptionGoalList) {
this.goalMonth = goalMonth;
this.consumptionGoalList = consumptionGoalList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,45 +97,6 @@ public ConsumptionAnalysisResponseDTO getTopCategoryAndConsumptionAmount(Long us
return ConsumptionAnalysisConverter.fromEntity(topConsumptionGoal, totalConsumptionAmountForCurrentWeek);
}

@Override
@Transactional
public ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId,
ConsumptionGoalListRequestDto consumptionGoalListRequestDto) {
LocalDate thisMonth = LocalDate.now().withDayOfMonth(1);
User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("Not found user"));

List<ConsumptionGoal> updatedConsumptionGoal = consumptionGoalListRequestDto.getConsumptionGoalList()
.stream()
.map(c -> updateConsumptionGoalWithRequestDto(user, c, thisMonth))
.toList();

List<ConsumptionGoalResponseDto> response = consumptionGoalRepository.saveAll(updatedConsumptionGoal)
.stream()
.map(consumptionGoalConverter::toConsumptionGoalResponseDto)
.toList();

return new ConsumptionGoalResponseListDto(thisMonth, response);
}

@Override
@Transactional(readOnly = true)
public ConsumptionGoalResponseListDto findUserConsumptionGoal(Long userId, LocalDate date) {
LocalDate goalMonth = date.withDayOfMonth(1);
Map<Long, ConsumptionGoalResponseDto> goalMap = initializeGoalMap(userId, goalMonth);

updateGoalMapWithPreviousMonth(userId, goalMonth, goalMap);
updateGoalMapWithCurrentMonth(userId, goalMonth, goalMap);

return new ConsumptionGoalResponseListDto(goalMonth, new ArrayList<>(goalMap.values()));
}

private Map<Long, ConsumptionGoalResponseDto> initializeGoalMap(Long userId, LocalDate goalMonth) {
return categoryRepository.findUserCategoryByUserId(userId)
.stream()
.collect(Collectors.toMap(Category::getId,
category -> consumptionGoalConverter.toConsumptionGoalResponseDto(category)));
}

private User findUserById(Long userId) {
Optional<User> user = userRepository.findById(userId);

Expand Down Expand Up @@ -179,21 +140,24 @@ private void setAgeGroupByUser(int userAge) {
}
}

private void updateGoalMapWithPreviousMonth(Long userId, LocalDate goalMonth,
Map<Long, ConsumptionGoalResponseDto> goalMap) {
updateGoalMap(userId, goalMonth.minusMonths(1), goalMap);
}
@Override
@Transactional
public ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId,
ConsumptionGoalListRequestDto consumptionGoalListRequestDto) {
LocalDate thisMonth = LocalDate.now().withDayOfMonth(1);
User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("Not found user"));

private void updateGoalMapWithCurrentMonth(Long userId, LocalDate goalMonth,
Map<Long, ConsumptionGoalResponseDto> goalMap) {
updateGoalMap(userId, goalMonth, goalMap);
}
List<ConsumptionGoal> updatedConsumptionGoal = consumptionGoalListRequestDto.getConsumptionGoalList()
.stream()
.map(c -> updateConsumptionGoalWithRequestDto(user, c, thisMonth))
.toList();

private void updateGoalMap(Long userId, LocalDate month, Map<Long, ConsumptionGoalResponseDto> goalMap) {
consumptionGoalRepository.findConsumptionGoalByUserIdAndGoalMonth(userId, month)
List<ConsumptionGoalResponseDto> response = consumptionGoalRepository.saveAll(updatedConsumptionGoal)
.stream()
.map(consumptionGoalConverter::toConsumptionGoalResponseDto)
.forEach(goal -> goalMap.put(goal.getCategoryId(), goal));
.toList();

return consumptionGoalConverter.toConsumptionGoalResponseListDto(response, thisMonth);
}

private ConsumptionGoal updateConsumptionGoalWithRequestDto(User user,
Expand All @@ -216,4 +180,40 @@ private ConsumptionGoal findOrElseGenerateConsumptionGoal(User user, Category ca
private ConsumptionGoal generateConsumptionGoal(User user, Category category, LocalDate goalMonth) {
return ConsumptionGoal.builder().goalMonth(goalMonth).user(user).category(category).consumeAmount(0L).build();
}

@Override
@Transactional(readOnly = true)
public ConsumptionGoalResponseListDto findUserConsumptionGoal(Long userId, LocalDate date) {
LocalDate goalMonth = date.withDayOfMonth(1);
Map<Long, ConsumptionGoalResponseDto> goalMap = initializeGoalMap(userId, goalMonth);

updateGoalMapWithPreviousMonth(userId, goalMonth, goalMap);
updateGoalMapWithCurrentMonth(userId, goalMonth, goalMap);

return consumptionGoalConverter.toConsumptionGoalResponseListDto(new ArrayList<>(goalMap.values()), goalMonth);
}

private Map<Long, ConsumptionGoalResponseDto> initializeGoalMap(Long userId, LocalDate goalMonth) {
return categoryRepository.findUserCategoryByUserId(userId)
.stream()
.collect(Collectors.toMap(Category::getId,
category -> consumptionGoalConverter.toConsumptionGoalResponseDto(category)));
}

private void updateGoalMapWithPreviousMonth(Long userId, LocalDate goalMonth,
Map<Long, ConsumptionGoalResponseDto> goalMap) {
updateGoalMap(userId, goalMonth.minusMonths(1), goalMap);
}

private void updateGoalMapWithCurrentMonth(Long userId, LocalDate goalMonth,
Map<Long, ConsumptionGoalResponseDto> goalMap) {
updateGoalMap(userId, goalMonth, goalMap);
}

private void updateGoalMap(Long userId, LocalDate month, Map<Long, ConsumptionGoalResponseDto> goalMap) {
consumptionGoalRepository.findConsumptionGoalByUserIdAndGoalMonth(userId, month)
.stream()
.map(consumptionGoalConverter::toConsumptionGoalResponseDto)
.forEach(goal -> goalMap.put(goal.getCategoryId(), goal));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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;

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)))
})
ResponseEntity<ExpenseResponseDto> createExpense(
@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<MonthlyExpenseCompactResponseDto> findExpensesForMonth(
Pageable pageable,
Long userId,
LocalDate date);
}
Original file line number Diff line number Diff line change
@@ -1,37 +1,47 @@
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.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.MonthlyExpenseCompactResponseDto;
import com.bbteam.budgetbuddies.domain.expense.service.ExpenseService;
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 lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/expenses")
public class ExpenseController {

private final ExpenseService expenseService;

@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)))
})
@PostMapping("/add")
public ResponseEntity<ExpenseResponseDto> createExpense(
@Parameter(description = "user_id, category_id, amount, description, expenseDate")
@RequestBody ExpenseRequestDto expenseRequestDto) {
ExpenseResponseDto response = expenseService.createExpense(expenseRequestDto);
return ResponseEntity.ok(response);
}
public class ExpenseController implements ExpenseApi {
private final ExpenseService expenseService;

@Override
@PostMapping("/add")
public ResponseEntity<ExpenseResponseDto> createExpense(
@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<MonthlyExpenseCompactResponseDto> findExpensesForMonth(Pageable pageable,
@PathVariable @Param("userId") Long userId,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) {

return ResponseEntity.ok(expenseService.getMonthlyExpense(pageable, userId, date));
}
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,64 @@
package com.bbteam.budgetbuddies.domain.expense.converter;

import java.time.LocalDate;
import java.util.List;

import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Component;

import com.bbteam.budgetbuddies.domain.category.entity.Category;
import com.bbteam.budgetbuddies.domain.expense.dto.CompactExpenseResponseDto;
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.entity.Expense;
import com.bbteam.budgetbuddies.domain.user.entity.User;
import org.springframework.stereotype.Component;

@Component
public class ExpenseConverter {

public Expense toExpenseEntity(ExpenseRequestDto expenseRequestDto, User user, Category category) {
return Expense.builder()
.user(user)
.category(category)
.amount(expenseRequestDto.getAmount())
.description(expenseRequestDto.getDescription())
.expenseDate(expenseRequestDto.getExpenseDate())
.build();
}

public ExpenseResponseDto toExpenseResponseDto(Expense expense) {
return ExpenseResponseDto.builder()
.expenseId(expense.getId())
.userId(expense.getUser().getId())
.categoryId(expense.getCategory().getId())
.amount(expense.getAmount())
.description(expense.getDescription())
.expenseDate(expense.getExpenseDate())
.build();
}
public Expense toExpenseEntity(ExpenseRequestDto expenseRequestDto, User user, Category category) {
return Expense.builder()
.user(user)
.category(category)
.amount(expenseRequestDto.getAmount())
.description(expenseRequestDto.getDescription())
.expenseDate(expenseRequestDto.getExpenseDate())
.build();
}

public ExpenseResponseDto toExpenseResponseDto(Expense expense) {
return ExpenseResponseDto.builder()
.expenseId(expense.getId())
.userId(expense.getUser().getId())
.categoryId(expense.getCategory().getId())
.amount(expense.getAmount())
.description(expense.getDescription())
.expenseDate(expense.getExpenseDate())
.build();
}

public MonthlyExpenseCompactResponseDto toMonthlyExpenseCompactResponseDto(Slice<Expense> expenseSlice,
LocalDate startOfMonth) {
List<CompactExpenseResponseDto> compactResponseList = expenseSlice.getContent().stream()
.map(this::toExpenseCompactResponseDto).toList();

return MonthlyExpenseCompactResponseDto
.builder()
.expenseMonth(startOfMonth)
.currentPage(expenseSlice.getPageable().getPageNumber())
.hasNext(expenseSlice.hasNext())
.expenseList(compactResponseList)
.build();
}

private CompactExpenseResponseDto toExpenseCompactResponseDto(Expense expense) {
return CompactExpenseResponseDto.builder()
.expenseId(expense.getId())
.description(expense.getDescription())
.amount(expense.getAmount())
.expenseDate(expense.getExpenseDate())
.build();
}
}

Loading

0 comments on commit 25bafc8

Please sign in to comment.