Skip to content

Commit

Permalink
Merge pull request #34 from BudgetBuddiesTeam/feat/#32
Browse files Browse the repository at this point in the history
[feat] 메인 페이지 월 소비 분석 api 구현
  • Loading branch information
m3k0813 authored Jul 27, 2024
2 parents 1d0444c + b1c48d9 commit 2e46990
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
@AllArgsConstructor
@SuperBuilder
@SoftDelete // boolean 타입의 deleted 필드가 추가
@Getter
public abstract class BaseEntity {

@Id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@
import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo;
import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo;
import com.bbteam.budgetbuddies.domain.user.entity.User;
import jakarta.persistence.*;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.hibernate.annotations.Where;
import org.springframework.lang.Nullable;

@Entity
@Getter
Expand All @@ -20,22 +23,22 @@
@SuperBuilder
public class Comment extends BaseEntity {

@Column(nullable = false, length = 1000)
private String content;
@Column(nullable = false, length = 1000)
private String content;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "discount_info_id")
private DiscountInfo discountInfo;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "discount_info_id")
private DiscountInfo discountInfo;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "support_info_id")
private SupportInfo supportInfo;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "support_info_id")
private SupportInfo supportInfo;

@Column(nullable = false)
private Integer anonymousNumber;
@Column(nullable = false)
private Integer anonymousNumber;

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ public interface ConsumptionGoalApi {
ResponseEntity<?> getTopGoalCategories(@RequestParam(name = "top", defaultValue = "5") int top, Long userId,
int peerAgeStart, int peerAgeEnd, String peerGender);

@Operation(summary = "소비 목표 조회 API", description = "date={yyyy-MM-dd} 형식의 query string을 통해서 사용자의 목표 달을 조회하는 API 입니다.")
@Parameters({@Parameter(name = "date", description = "yyyy-MM-dd 형식으로 목표 달의 소비를 조회")})
ResponseEntity<ConsumptionGoalResponseListDto> findUserConsumptionGoal(LocalDate date, Long userId);
@Operation(summary = "또래가 가장 큰 계획을 세운 카테고리와 이번 주 사용한 금액 조회 API", description = "로그인 한 유저의 또래 중 가장 큰 소비 목표 금액을 가진 카테고리와 이번 주 사용한 금액을 조회하는 API 입니다")
@ApiResponses(value = {@ApiResponse(responseCode = "COMMON200", description = "OK, 성공")})
@Parameters({@Parameter(name = "userId", description = "로그인 한 유저 아이디")})
ResponseEntity<?> getTopGoalCategory(@RequestParam(name = "userId") Long userId);

@Operation(summary = "또래나이와 성별 조회 API", description = "또래나이와 성별을 조회하는 API 입니다.")
@ApiResponses(value = {@ApiResponse(responseCode = "COMMON200", description = "OK, 성공")})
Expand All @@ -37,7 +38,12 @@ ResponseEntity<?> getTopGoalCategories(@RequestParam(name = "top", defaultValue
@Parameter(name = "peerGender", description = "또래 성별")})
ResponseEntity<?> getPeerInfo(Long userId, int peerAgeStart, int peerAgeEnd, String peerGender);

@Operation(summary = "소비 목표 조회 API", description = "date={yyyy-MM-dd} 형식의 query string을 통해서 사용자의 목표 달을 조회하는 API 입니다.")
@Parameters({@Parameter(name = "date", description = "yyyy-MM-dd 형식으로 목표 달의 소비를 조회")})
ResponseEntity<ConsumptionGoalResponseListDto> findUserConsumptionGoal(LocalDate date, Long userId);

@Operation(summary = "이번 달 소비 목표 수정 API", description = "다른 달의 소비 목표를 업데이트하는 것은 불가능하고 오직 이번 달의 소비 목표만 업데이트 하는 API 입니다.")
ResponseEntity<ConsumptionGoalResponseListDto> updateOrElseGenerateConsumptionGoal(Long userId,
ConsumptionGoalListRequestDto consumptionGoalListRequestDto);

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionAnalysisResponseDTO;
import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalListRequestDto;
import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto;
import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDTO;
Expand All @@ -39,12 +40,9 @@ public ResponseEntity<?> getTopGoalCategories(@RequestParam(name = "top", defaul
return ResponseEntity.ok(topCategory);
}

@GetMapping("/{userId}")
public ResponseEntity<ConsumptionGoalResponseListDto> findUserConsumptionGoal(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date, @PathVariable Long userId) {

ConsumptionGoalResponseListDto response = consumptionGoalService.findUserConsumptionGoal(userId, date);

@GetMapping("/top-category")
public ResponseEntity<?> getTopGoalCategory(@RequestParam(name = "userId") Long userId) {
ConsumptionAnalysisResponseDTO response = consumptionGoalService.getTopCategoryAndConsumptionAmount(userId);
return ResponseEntity.ok(response);
}

Expand All @@ -57,6 +55,15 @@ public ResponseEntity<?> getPeerInfo(@RequestParam(name = "userId") Long userId,
return ResponseEntity.ok(response);
}

@GetMapping("/{userId}")
public ResponseEntity<ConsumptionGoalResponseListDto> findUserConsumptionGoal(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date, @PathVariable Long userId) {

ConsumptionGoalResponseListDto response = consumptionGoalService.findUserConsumptionGoal(userId, date);

return ResponseEntity.ok(response);
}

@PostMapping("/{userId}")
public ResponseEntity<ConsumptionGoalResponseListDto> updateOrElseGenerateConsumptionGoal(@PathVariable Long userId,
@RequestBody ConsumptionGoalListRequestDto consumptionGoalListRequestDto) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.bbteam.budgetbuddies.domain.consumptiongoal.converter;

import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionAnalysisResponseDTO;
import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal;

public class ConsumptionAnalysisConverter {

public static ConsumptionAnalysisResponseDTO fromEntity(ConsumptionGoal consumptionGoal, Long topAmount) {

return ConsumptionAnalysisResponseDTO.builder()
.goalCategory(consumptionGoal.getCategory().getName())
.currentWeekConsumptionAmount(topAmount)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.bbteam.budgetbuddies.domain.consumptiongoal.dto;

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

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ConsumptionAnalysisResponseDTO {

private String goalCategory;

private Long currentWeekConsumptionAmount;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@
@Repository
public interface ConsumptionGoalRepository extends JpaRepository<ConsumptionGoal, Long> {

@Query("SELECT cg FROM ConsumptionGoal cg "
+ "WHERE cg.category.isDefault = true "
+ "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd "
+ "AND cg.user.gender = :peerGender "
@Query("SELECT cg FROM ConsumptionGoal cg " + "WHERE cg.category.isDefault = true "
+ "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + "AND cg.user.gender = :peerGender "
+ "ORDER BY cg.goalAmount DESC limit :top")
List<ConsumptionGoal> findTopCategoriesAndGoalAmount(
@Param("top") int top,
@Param("peerAgeStart") int peerAgeStart,
@Param("peerAgeEnd") int peerAgeEnd,
@Param("peerGender") Gender peerGender);
List<ConsumptionGoal> findTopCategoriesAndGoalAmount(@Param("top") int top, @Param("peerAgeStart") int peerAgeStart,
@Param("peerAgeEnd") int peerAgeEnd, @Param("peerGender") Gender peerGender);

@Query(value = "SELECT cg FROM ConsumptionGoal AS cg WHERE cg.user.id = :userId AND cg.goalMonth = :goalMonth")
List<ConsumptionGoal> findConsumptionGoalByUserIdAndGoalMonth(Long userId, LocalDate goalMonth);

Optional<ConsumptionGoal> findConsumptionGoalByUserAndCategoryAndGoalMonth(User user, Category category,
LocalDate goalMonth);

@Query("SELECT cg FROM ConsumptionGoal cg JOIN cg.category c WHERE c.id = :categoryId AND cg.goalMonth "
+ "BETWEEN :startOfWeek AND :endOfWeek ORDER BY cg.consumeAmount DESC")
Optional<ConsumptionGoal> findTopConsumptionByCategoryIdAndCurrentWeek(@Param("categoryId") Long categoryId,
@Param("startOfWeek") LocalDate startOfWeek, @Param("endOfWeek") LocalDate endOfWeek);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import org.springframework.stereotype.Service;

import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionAnalysisResponseDTO;
import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalListRequestDto;
import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto;
import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDTO;
Expand All @@ -22,4 +23,6 @@ List<TopGoalCategoryResponseDTO> getTopGoalCategories(int top, Long userId, int

ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId,
ConsumptionGoalListRequestDto consumptionGoalListRequestDto);

ConsumptionAnalysisResponseDTO getTopCategoryAndConsumptionAmount(Long userId);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.bbteam.budgetbuddies.domain.consumptiongoal.service;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand All @@ -13,9 +15,11 @@

import com.bbteam.budgetbuddies.domain.category.entity.Category;
import com.bbteam.budgetbuddies.domain.category.repository.CategoryRepository;
import com.bbteam.budgetbuddies.domain.consumptiongoal.converter.ConsumptionAnalysisConverter;
import com.bbteam.budgetbuddies.domain.consumptiongoal.converter.ConsumptionGoalConverter;
import com.bbteam.budgetbuddies.domain.consumptiongoal.converter.PeerInfoConverter;
import com.bbteam.budgetbuddies.domain.consumptiongoal.converter.TopCategoryConverter;
import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionAnalysisResponseDTO;
import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalListRequestDto;
import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalRequestDto;
import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseDto;
Expand Down Expand Up @@ -69,6 +73,69 @@ public PeerInfoResponseDTO getPeerInfo(Long userId, int peerAgeS, int peerAgeE,
return PeerInfoConverter.fromEntity(peerAgeStart, peerAgeEnd, peerGender);
}

@Override
@Transactional(readOnly = true)
public ConsumptionAnalysisResponseDTO getTopCategoryAndConsumptionAmount(Long userId) {

User user = findUserById(userId);

checkPeerInfo(user, 0, 0, "none");

ConsumptionGoal topConsumptionGoal = consumptionGoalRepository.findTopCategoriesAndGoalAmount(1, peerAgeStart,
peerAgeEnd, peerGender).get(0);

LocalDate today = LocalDate.now();
LocalDate startOfWeek = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
LocalDate endOfWeek = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));

ConsumptionGoal currentWeekConsumptionAmount = consumptionGoalRepository.findTopConsumptionByCategoryIdAndCurrentWeek(
topConsumptionGoal.getCategory().getId(), startOfWeek, endOfWeek)
.orElseThrow(IllegalArgumentException::new);

Long totalConsumptionAmountForCurrentWeek = currentWeekConsumptionAmount.getConsumeAmount();

return ConsumptionAnalysisConverter.fromEntity(topConsumptionGoal, totalConsumptionAmountForCurrentWeek);
}

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

List<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 @@ -112,25 +179,6 @@ private void setAgeGroupByUser(int userAge) {
}
}

@Override
@Transactional(readOnly = true)
public ConsumptionGoalResponseListDto findUserConsumptionGoal(Long userId, LocalDate date) {
LocalDate goalMonth = date.withDayOfMonth(1);
Map<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 void updateGoalMapWithPreviousMonth(Long userId, LocalDate goalMonth,
Map<Long, ConsumptionGoalResponseDto> goalMap) {
updateGoalMap(userId, goalMonth.minusMonths(1), goalMap);
Expand All @@ -148,26 +196,6 @@ private void updateGoalMap(Long userId, LocalDate month, Map<Long, ConsumptionGo
.forEach(goal -> goalMap.put(goal.getCategoryId(), goal));
}

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

List<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);
}

private ConsumptionGoal updateConsumptionGoalWithRequestDto(User user,
ConsumptionGoalRequestDto consumptionGoalRequestDto, LocalDate goalMonth) {

Expand Down

0 comments on commit 2e46990

Please sign in to comment.