diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java index 94ca9de9..62b158b0 100644 --- a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/ErrorStatus.java @@ -12,21 +12,9 @@ public enum ErrorStatus implements BaseErrorCode { _INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500", "서버에러"), - _BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON400", "잘못된 요청입니다."), - _UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "COMMON401", "인증이 필요합니다"), - _FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "금지된 요청입니다."), USER_NOT_FOUND(HttpStatus.BAD_REQUEST, "USER4001", "사용자가 없습니다."), - NICKNAME_NOT_EXIST(HttpStatus.BAD_REQUEST, "MEMBER4002", "닉네임은 필수입니다."), COMMENT_NOT_FOUND(HttpStatus.BAD_REQUEST, "COMMENT4001", "해당 댓글이 없습니다.") , - ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "ARTICLE4002", "게시글이 없습니다."), - TEMP_EXCEPTION(HttpStatus.BAD_REQUEST, "TEMP4001", "test"), - FOOD_CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "FOOD_CATEGORY4004", "해당하는 음식 카테고리가 없습니다."), - STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "STORE4004", "해당하는 가게는 존재하지 않습니다."), - MISSION_ALREADY_ACCEPT(HttpStatus.BAD_REQUEST, "MISSION4001", "해당 미션은 이미 수주되었습니다."), - REVIEW_ALREADY_EXIST(HttpStatus.BAD_REQUEST, "REVIEW4001","해당 가게에 리뷰를 이미 작성하셨습니다."), - PAGE_LOWER_ZERO(HttpStatus.BAD_REQUEST, "PAGE4001", "요청된 페이지가 0보다 작습니다."), - MISSION_NOT_FOUND(HttpStatus.NOT_FOUND, "MISSION4004", "해당 미션이 존재하지 않습니다."), - MISSION_ALREADY_COMPLETE(HttpStatus.BAD_REQUEST, "MISSION4001", "해당 미션은 이미 완료된 미션입니다."); + PAGE_LOWER_ZERO(HttpStatus.BAD_REQUEST, "PAGE4001", "요청된 페이지가 0보다 작습니다."); private HttpStatus httpStatus; diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/calendar/controller/CalendarApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/calendar/controller/CalendarApi.java new file mode 100644 index 00000000..b868b733 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/calendar/controller/CalendarApi.java @@ -0,0 +1,24 @@ +package com.bbteam.budgetbuddies.domain.calendar.controller; + +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; +import com.bbteam.budgetbuddies.domain.calendar.dto.CalendarDto; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +public interface CalendarApi { + + @Operation(summary = "[User] 주머니 캘린더 API", description = "주머니 캘린더 화면에 필요한 API를 호출합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "year", description = "호출할 연도입니다."), + @Parameter(name = "month", description = "호출할 연도의 월입니다."), + }) + public ApiResponse request( + @RequestParam("year") Integer year, @RequestParam("month") Integer month); +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/calendar/controller/CalendarController.java b/src/main/java/com/bbteam/budgetbuddies/domain/calendar/controller/CalendarController.java index bc98ff3f..2620ced7 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/calendar/controller/CalendarController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/calendar/controller/CalendarController.java @@ -1,5 +1,6 @@ package com.bbteam.budgetbuddies.domain.calendar.controller; +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.calendar.dto.CalendarDto; import com.bbteam.budgetbuddies.domain.calendar.service.CalendarService; import io.swagger.v3.oas.annotations.Operation; @@ -16,9 +17,10 @@ @RestController @RequiredArgsConstructor @RequestMapping("/calendar") -public class CalendarController { +public class CalendarController implements CalendarApi{ private final CalendarService calendarService; + @Operation(summary = "[User] 주머니 캘린더 API", description = "주머니 캘린더 화면에 필요한 API를 호출합니다.") @ApiResponses({ @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), @@ -27,11 +29,11 @@ public class CalendarController { @Parameter(name = "year", description = "호출할 연도입니다."), @Parameter(name = "month", description = "호출할 연도의 월입니다."), }) - @GetMapping("/") - public ResponseEntity request( + @GetMapping + public ApiResponse request( @RequestParam("year") Integer year, @RequestParam("month") Integer month ) { CalendarDto.CalendarMonthResponseDto result = calendarService.find(year, month); - return ResponseEntity.ok(result); + return ApiResponse.onSuccess(result); } } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/calendar/converter/CalendarConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/calendar/converter/CalendarConverter.java index c05f111e..d9860092 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/calendar/converter/CalendarConverter.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/calendar/converter/CalendarConverter.java @@ -4,6 +4,7 @@ import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import java.time.LocalDate; import java.util.List; public class CalendarConverter { @@ -16,12 +17,13 @@ public static CalendarDto.CalendarMonthResponseDto toCalendarMonthResponseDto(Ca } public static CalendarDto.CalendarMonthInfoDto toCalendarMonthInfoDto(List discountInfoList, - List supportInfoList) { + List supportInfoList, + LocalDate firstDay, LocalDate lastDay) { List discountInfoDtoList = discountInfoList.stream() - .map(CalendarConverter::toCalendarDiscountInfoDto) + .map(discountInfo -> toCalendarDiscountInfoDto(discountInfo, firstDay, lastDay)) .toList(); List supportInfoDtoList = supportInfoList.stream() - .map(CalendarConverter::toCalendarSupportInfoDto) + .map(supportInfo -> toCalendarSupportInfoDto(supportInfo, firstDay, lastDay)) .toList(); return CalendarDto.CalendarMonthInfoDto.builder() @@ -30,26 +32,62 @@ public static CalendarDto.CalendarMonthInfoDto toCalendarMonthInfoDto(List monthDiscountInfoList = discountInfoRepository.findByMonth(firstDay, lastDay); List monthSupportInfoList = supportInfoRepository.findByMonth(firstDay, lastDay); - return CalendarConverter.toCalendarMonthInfoDto(monthDiscountInfoList, monthSupportInfoList); + return CalendarConverter.toCalendarMonthInfoDto(monthDiscountInfoList, monthSupportInfoList, firstDay, lastDay); } private CalendarDto.CalendarMonthInfoDto getRecommendMonthInfoDto(LocalDate firstDay, LocalDate lastDay) { List recommendDiscountInfoList = discountInfoRepository.findRecommendInfoByMonth(firstDay, lastDay); List recommendSupportInfoList = supportInfoRepository.findRecommendInfoByMonth(firstDay, lastDay); - return CalendarConverter.toCalendarMonthInfoDto(recommendDiscountInfoList, recommendSupportInfoList); + return CalendarConverter.toCalendarMonthInfoDto(recommendDiscountInfoList, recommendSupportInfoList, firstDay, lastDay); } } 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 b299a4a0..ca3be092 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 @@ -22,8 +22,9 @@ public interface CategoryApi { @ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), @ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) }) - @PostMapping("/add") + @PostMapping("/add/{userId}") ResponseEntity createCategory( + @PathVariable Long userId, @Parameter(description = "user_id, name(사용자가 입력한 카테고리명), is_default(default 카테고리 여부)로 request") @RequestBody CategoryRequestDTO categoryRequestDTO); 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 49b9dfdf..1732ac7f 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 @@ -17,9 +17,11 @@ public class CategoryController implements CategoryApi { private final CategoryService categoryService; @Override - @PostMapping("/add") - public ResponseEntity createCategory(@RequestBody CategoryRequestDTO categoryRequestDTO) { - CategoryResponseDTO response = categoryService.createCategory(categoryRequestDTO); + @PostMapping("/add/{userId}") + public ResponseEntity createCategory( + @PathVariable Long userId, + @RequestBody CategoryRequestDTO categoryRequestDTO) { + CategoryResponseDTO response = categoryService.createCategory(userId, categoryRequestDTO); return ResponseEntity.ok(response); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/dto/CategoryRequestDTO.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/dto/CategoryRequestDTO.java index 3f8eee35..b01f93e4 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/dto/CategoryRequestDTO.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/dto/CategoryRequestDTO.java @@ -6,7 +6,6 @@ @Getter @Setter public class CategoryRequestDTO { - private Long userId; private String name; private Boolean isDefault; } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/dto/CategoryResponseDTO.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/dto/CategoryResponseDTO.java index 41381edb..54908eb0 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/dto/CategoryResponseDTO.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/dto/CategoryResponseDTO.java @@ -1,6 +1,5 @@ package com.bbteam.budgetbuddies.domain.category.dto; -import com.bbteam.budgetbuddies.domain.user.entity.User; import lombok.Builder; import lombok.Getter; 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 1a15b327..01e2c994 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 @@ -10,7 +10,7 @@ import com.bbteam.budgetbuddies.domain.user.entity.User; public interface CategoryService { - CategoryResponseDTO createCategory(CategoryRequestDTO categoryRequestDTO); + CategoryResponseDTO createCategory(Long userId, CategoryRequestDTO categoryRequestDTO); List getUserCategories(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 8515aee8..055e9415 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 @@ -34,11 +34,11 @@ public class CategoryServiceImpl implements CategoryService { private final ConsumptionGoalRepository consumptionGoalRepository; @Override - public CategoryResponseDTO createCategory(CategoryRequestDTO categoryRequestDTO) { - User user = userRepository.findById(categoryRequestDTO.getUserId()) - .orElseThrow(() -> new IllegalArgumentException("cannot find user")); + public CategoryResponseDTO createCategory(Long userId, CategoryRequestDTO categoryRequestDTO) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("cannot find user")); - if (categoryRepository.existsByUserIdAndName(categoryRequestDTO.getUserId(), categoryRequestDTO.getName())) { + if (categoryRepository.existsByUserIdAndName(userId, categoryRequestDTO.getName())) { throw new IllegalArgumentException("User already has a category with the same name"); } @@ -47,12 +47,12 @@ public CategoryResponseDTO createCategory(CategoryRequestDTO categoryRequestDTO) // custom 카테고리 생성 -> 소비 목표 테이블에 초기 값 추가 ConsumptionGoal consumptionGoal = ConsumptionGoal.builder() - .user(user) - .category(savedCategory) - .goalMonth(LocalDate.now().withDayOfMonth(1)) // custom 카테고리를 생성한 현재 달(지금)로 설정 - .goalAmount(0L) - .consumeAmount(0L) - .build(); + .user(user) + .category(savedCategory) + .goalMonth(LocalDate.now().withDayOfMonth(1)) // custom 카테고리를 생성한 현재 달(지금)로 설정 + .goalAmount(0L) + .consumeAmount(0L) + .build(); consumptionGoalRepository.save(consumptionGoal); return categoryConverter.toCategoryResponseDTO(savedCategory); diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalApi.java index 3a8c2f7b..25249f26 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalApi.java @@ -2,8 +2,8 @@ import java.time.LocalDate; +import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestParam; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalListRequestDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto; @@ -15,20 +15,30 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; public interface ConsumptionGoalApi { - @Operation(summary = "또래들이 가장 큰 계획을 세운 카테고리 조회 API", description = "특정 사용자의 소비 목표 카테고리별 소비 목표 금액을 조회하는 API 입니다.") + + @Operation(summary = "또래들이 가장 큰 계획을 세운 카테고리 조회 Top4 API", description = "특정 사용자의 소비 목표 카테고리별 소비 목표 금액을 조회하는 API 입니다.") @ApiResponses(value = {@ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) - @Parameters({@Parameter(name = "top", description = "가장 큰 목표를 세운 카테고리의 개수를 지정합니다. (기본값은 5입니다)"), + @Parameters({@Parameter(name = "top", description = "가장 큰 목표를 세운 카테고리의 개수를 지정합니다."), @Parameter(name = "userId", description = "로그인 한 유저 아이디"), @Parameter(name = "peerAgeStart", description = "또래나이 시작 범위"), @Parameter(name = "peerAgeEnd", description = "또래나이 끝 범위"), @Parameter(name = "peerGender", description = "또래 성별")}) - ResponseEntity getTopGoalCategories(@RequestParam(name = "top", defaultValue = "5") int top, Long userId, + ResponseEntity getTopGoalCategoriesList(int top, Long userId, int peerAgeStart, int peerAgeEnd, String peerGender); + @Operation(summary = "또래들이 가장 큰 계획을 세운 카테고리 조회 API", description = "특정 사용자의 소비 목표 카테고리별 소비 목표 금액을 전체 조회하는 API 입니다.") + @ApiResponses(value = {@ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) + @Parameters({@Parameter(name = "userId", description = "로그인 한 유저 아이디"), + @Parameter(name = "peerAgeStart", description = "또래나이 시작 범위"), + @Parameter(name = "peerAgeEnd", description = "또래나이 끝 범위"), + @Parameter(name = "peerGender", description = "또래 성별")}) + ResponseEntity getTopGoalCategoriesPage(Long userId, + int peerAgeStart, int peerAgeEnd, String peerGender, Pageable pageable); + @Operation(summary = "또래가 가장 큰 계획을 세운 카테고리와 이번 주 사용한 금액 조회 API", description = "로그인 한 유저의 또래 중 가장 큰 소비 목표 금액을 가진 카테고리와 이번 주 사용한 금액을 조회하는 API 입니다") @ApiResponses(value = {@ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) @Parameters({@Parameter(name = "userId", description = "로그인 한 유저 아이디")}) - ResponseEntity getTopGoalCategory(@RequestParam(name = "userId") Long userId); + ResponseEntity getTopGoalCategory(Long userId); @Operation(summary = "또래나이와 성별 조회 API", description = "또래나이와 성별을 조회하는 API 입니다.") @ApiResponses(value = {@ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) @@ -46,12 +56,22 @@ ResponseEntity getTopGoalCategories(@RequestParam(name = "top", defaultValue ResponseEntity updateOrElseGenerateConsumptionGoal(Long userId, ConsumptionGoalListRequestDto consumptionGoalListRequestDto); - @Operation(summary = "또래들이 가장 많이한 소비 카테고리 조회 API", description = "특정 사용자의 소비 카테고리별 소비 금액을 조회하는 API 입니다.") + @Operation(summary = "또래들이 가장 많이한 소비 카테고리 조회 Top4 API", description = "특정 사용자의 소비 카테고리별 소비 금액을 조회하는 API 입니다.") @ApiResponses(value = {@ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) - @Parameters({@Parameter(name = "top", description = "가장 큰 소비 카테고리의 개수를 지정합니다. (기본값은 5입니다)"), + @Parameters({@Parameter(name = "top", description = "가장 큰 소비 카테고리의 개수를 지정합니다."), @Parameter(name = "userId", description = "로그인 한 유저 아이디"), @Parameter(name = "peerAgeStart", description = "또래나이 시작 범위"), @Parameter(name = "peerAgeEnd", description = "또래나이 끝 범위"), @Parameter(name = "peerGender", description = "또래 성별")}) - ResponseEntity getConsumptionGoal(int top, Long userId, int peerAgeStart, int peerAgeEnd, String peerGender); -} + ResponseEntity getConsumptionGoalList(int top, Long userId, int peerAgeStart, int peerAgeEnd, + String peerGender); + + @Operation(summary = "또래들이 가장 많이한 소비 카테고리 조회 API", description = "특정 사용자의 소비 카테고리별 소비 금액을 전체 조회하는 API 입니다.") + @ApiResponses(value = {@ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) + @Parameters({@Parameter(name = "userId", description = "로그인 한 유저 아이디"), + @Parameter(name = "peerAgeStart", description = "또래나이 시작 범위"), + @Parameter(name = "peerAgeEnd", description = "또래나이 끝 범위"), + @Parameter(name = "peerGender", description = "또래 성별")}) + ResponseEntity getConsumptionGoalPage(Long userId, int peerAgeStart, int peerAgeEnd, String peerGender, + Pageable pageable); +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalController.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalController.java index 2a179695..c435c94e 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalController.java @@ -3,6 +3,8 @@ import java.time.LocalDate; import java.util.List; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -31,15 +33,26 @@ public class ConsumptionGoalController implements ConsumptionGoalApi { private final ConsumptionGoalService consumptionGoalService; @Override - @GetMapping("/top-categories/top-goal") - public ResponseEntity getTopGoalCategories(@RequestParam(name = "top", defaultValue = "5") int top, + @GetMapping("/top-categories/top-goal/{top}") + public ResponseEntity getTopGoalCategoriesList(@PathVariable(name = "top") int top, @RequestParam(name = "userId") Long userId, @RequestParam(name = "peerAgeStart", defaultValue = "0") int peerAgeStart, @RequestParam(name = "peerAgeEnd", defaultValue = "0") int peerAgeEnd, @RequestParam(name = "peerGender", defaultValue = "none") String peerGender) { - List topCategory = consumptionGoalService.getTopGoalCategories(top, userId, + List topCategoriesList = consumptionGoalService.getTopGoalCategoriesLimit(top, + userId, peerAgeStart, peerAgeEnd, peerGender); - return ResponseEntity.ok(topCategory); + return ResponseEntity.ok(topCategoriesList); + } + + @GetMapping("/top-categories/top-goal") + public ResponseEntity getTopGoalCategoriesPage(@RequestParam(name = "userId") Long userId, + @RequestParam(name = "peerAgeStart", defaultValue = "0") int peerAgeStart, + @RequestParam(name = "peerAgeEnd", defaultValue = "0") int peerAgeEnd, + @RequestParam(name = "peerGender", defaultValue = "none") String peerGender, Pageable pageable) { + Page topCategoriesPage = consumptionGoalService.getTopGoalCategories(userId, + peerAgeStart, peerAgeEnd, peerGender, pageable); + return ResponseEntity.ok(topCategoriesPage.getContent()); } @Override @@ -77,14 +90,24 @@ public ResponseEntity updateOrElseGenerateConsum .body(consumptionGoalService.updateConsumptionGoals(userId, consumptionGoalListRequestDto)); } - @GetMapping("/top-categories/top-consumption") - public ResponseEntity getConsumptionGoal(@RequestParam(name = "top", defaultValue = "5") int top, + @GetMapping("/top-categories/top-consumption/{top}") + public ResponseEntity getConsumptionGoalList(@PathVariable(name = "top") int top, @RequestParam(name = "userId") Long userId, @RequestParam(name = "peerAgeStart", defaultValue = "0") int peerAgeStart, @RequestParam(name = "peerAgeEnd", defaultValue = "0") int peerAgeEnd, @RequestParam(name = "peerGender", defaultValue = "none") String peerGender) { - List response = consumptionGoalService.getTopConsumption(top, userId, + List response = consumptionGoalService.getTopConsumptionsLimit(top, userId, peerAgeStart, peerAgeEnd, peerGender); return ResponseEntity.ok(response); } + + @GetMapping("/top-categories/top-consumption") + public ResponseEntity getConsumptionGoalPage(@RequestParam(name = "userId") Long userId, + @RequestParam(name = "peerAgeStart", defaultValue = "0") int peerAgeStart, + @RequestParam(name = "peerAgeEnd", defaultValue = "0") int peerAgeEnd, + @RequestParam(name = "peerGender", defaultValue = "none") String peerGender, Pageable pageable) { + Page response = consumptionGoalService.getTopConsumptions(userId, + peerAgeStart, peerAgeEnd, peerGender, pageable); + return ResponseEntity.ok(response.getContent()); + } } \ No newline at end of file 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 0c0b5bae..d1dec930 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 @@ -17,12 +17,14 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import lombok.extern.slf4j.Slf4j; @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @SuperBuilder +@Slf4j public class ConsumptionGoal extends BaseEntity { @Column(nullable = false) @@ -55,4 +57,11 @@ public void updateGoalAmount(Long goalAmount) { public void restoreConsumeAmount(Long previousAmount) { this.consumeAmount -= previousAmount; } + + public void decreaseConsumeAmount(Long amount) { + if (this.consumeAmount - amount < 0) { + log.warn("소비 내역 삭제의 결과가 음수이므로 확인이 필요합니다.(현재 소비 금액: {}, 차감할 금액: {}). 소비 금액을 0으로 설정합니다.", this.consumeAmount, amount); + } + this.consumeAmount = Math.max(0, this.consumeAmount - amount); + } } 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 a5444e5f..0550465e 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 @@ -4,6 +4,8 @@ import java.util.List; import java.util.Optional; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -19,9 +21,17 @@ public interface ConsumptionGoalRepository extends JpaRepository findTopCategoriesAndGoalAmount(@Param("top") int top, @Param("peerAgeStart") int peerAgeStart, - @Param("peerAgeEnd") int peerAgeEnd, @Param("peerGender") Gender peerGender); + + "AND cg.goalMonth >= :currentMonth " + "ORDER BY cg.goalAmount DESC limit :top") + List findTopCategoriesAndGoalAmountLimit(@Param("top") int top, + @Param("peerAgeStart") int peerAgeStart, @Param("peerAgeEnd") int peerAgeEnd, + @Param("peerGender") Gender peerGender, @Param("currentMonth") LocalDate currentMonth); + + @Query("SELECT cg FROM ConsumptionGoal cg " + "WHERE cg.category.isDefault = true " + + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + "AND cg.user.gender = :peerGender " + + "AND cg.goalMonth >= :currentMonth " + "ORDER BY cg.goalAmount DESC") + Page findTopCategoriesAndGoalAmount(@Param("peerAgeStart") int peerAgeStart, + @Param("peerAgeEnd") int peerAgeEnd, @Param("peerGender") Gender peerGender, + @Param("currentMonth") LocalDate currentMonth, Pageable pageable); @Query(value = "SELECT cg FROM ConsumptionGoal AS cg WHERE cg.user.id = :userId AND cg.goalMonth = :goalMonth") List findConsumptionGoalByUserIdAndGoalMonth(Long userId, LocalDate goalMonth); @@ -36,7 +46,16 @@ Optional findTopConsumptionByCategoryIdAndCurrentWeek(@Param("c @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.consumeAmount DESC limit :top") - List findTopConsumptionAndConsumeAmount(@Param("top") int top, @Param("peerAgeStart") int peerAgeStart, - @Param("peerAgeEnd") int peerAgeEnd, @Param("peerGender") Gender peerGender); + + "AND cg.goalMonth >= :currentMonth " + "ORDER BY cg.consumeAmount DESC limit :top") + List findTopConsumptionAndConsumeAmountLimit(@Param("top") int top, + @Param("peerAgeStart") int peerAgeStart, @Param("peerAgeEnd") int peerAgeEnd, + @Param("peerGender") Gender peerGender, @Param("currentMonth") LocalDate currentMonth); + + @Query("SELECT cg FROM ConsumptionGoal cg " + "WHERE cg.category.isDefault = true " + + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + "AND cg.user.gender = :peerGender " + + "AND cg.goalMonth >= :currentMonth " + "ORDER BY cg.consumeAmount DESC") + Page findTopConsumptionAndConsumeAmount(@Param("peerAgeStart") int peerAgeStart, + @Param("peerAgeEnd") int peerAgeEnd, @Param("peerGender") Gender peerGender, + @Param("currentMonth") LocalDate currentMonth, Pageable pageable); + } \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalService.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalService.java index 734ca773..041ce876 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 @@ -3,6 +3,8 @@ import java.time.LocalDate; import java.util.List; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionAnalysisResponseDTO; @@ -18,15 +20,18 @@ @Service public interface ConsumptionGoalService { - List getTopGoalCategories(int top, Long userId, int peerAgeStart, int peerAgeEnd, - String peerGender); + List getTopGoalCategoriesLimit(int top, Long userId, int peerAgeStart, int peerAgeEnd, + String peerGender); + + Page getTopGoalCategories(Long userId, int peerAgeStart, int peerAgeEnd, + String peerGender, Pageable pageable); ConsumptionGoalResponseListDto findUserConsumptionGoalList(Long userId, LocalDate date); PeerInfoResponseDTO getPeerInfo(Long userId, int peerAgeStart, int peerAgeEnd, String peerGender); ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId, - ConsumptionGoalListRequestDto consumptionGoalListRequestDto); + ConsumptionGoalListRequestDto consumptionGoalListRequestDto); ConsumptionAnalysisResponseDTO getTopCategoryAndConsumptionAmount(Long userId); @@ -34,5 +39,10 @@ ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId, void updateConsumeAmount(Long userId, Long categoryId, Long amount); + void decreaseConsumeAmount(Long userId, Long categoryId, Long amount, LocalDate expenseDate); + List getTopConsumption(int top, Long userId, int peerAgeS, int peerAgeE, String peerG); -} \ No newline at end of file + + Page getTopConsumptions(Long userId, int peerAgeS, int peerAgeE, + String peerG, Pageable pageable); +} 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 b94319bc..e3fbe04b 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceImpl.java @@ -10,6 +10,8 @@ import java.util.Optional; import java.util.stream.Collectors; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -37,7 +39,9 @@ import com.bbteam.budgetbuddies.enums.Gender; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Service @RequiredArgsConstructor public class ConsumptionGoalServiceImpl implements ConsumptionGoalService { @@ -52,18 +56,27 @@ public class ConsumptionGoalServiceImpl implements ConsumptionGoalService { private int peerAgeEnd; private Gender peerGender; + private final LocalDate currentMonth = LocalDate.now().withDayOfMonth(1); + private final LocalDate startOfWeek = LocalDate.now().with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); + private final LocalDate endOfWeek = LocalDate.now().with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)); + @Override @Transactional(readOnly = true) - public List getTopGoalCategories(int top, Long userId, int peerAgeS, int peerAgeE, - String peerG) { - + public List getTopGoalCategoriesLimit(int top, Long userId, int peerAgeS, int peerAgeE, String peerG) { checkPeerInfo(userId, peerAgeS, peerAgeE, peerG); - - List topGoals = consumptionGoalRepository.findTopCategoriesAndGoalAmount(top, peerAgeStart, - peerAgeEnd, peerGender); + List topGoals = consumptionGoalRepository.findTopCategoriesAndGoalAmountLimit(top, peerAgeStart, peerAgeEnd, peerGender, currentMonth); return topGoals.stream().map(TopCategoryConverter::fromEntity).collect(Collectors.toList()); } + @Override + @Transactional(readOnly = true) + public Page getTopGoalCategories(Long userId, int peerAgeS, int peerAgeE, String peerG, Pageable pageable) { + checkPeerInfo(userId, peerAgeS, peerAgeE, peerG); + Page topGoals = consumptionGoalRepository.findTopCategoriesAndGoalAmount(peerAgeStart, + peerAgeEnd, peerGender, currentMonth, pageable); + return topGoals.map(TopCategoryConverter::fromEntity); + } + @Override @Transactional(readOnly = true) public PeerInfoResponseDTO getPeerInfo(Long userId, int peerAgeS, int peerAgeE, String peerG) { @@ -79,23 +92,40 @@ public ConsumptionAnalysisResponseDTO getTopCategoryAndConsumptionAmount(Long us checkPeerInfo(userId, 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 topConsumptionGoal = consumptionGoalRepository.findTopCategoriesAndGoalAmountLimit(1, peerAgeStart, peerAgeEnd, peerGender, currentMonth).get(0); + ConsumptionGoal currentWeekConsumptionAmount = consumptionGoalRepository.findTopConsumptionByCategoryIdAndCurrentWeek( - topConsumptionGoal.getCategory().getId(), startOfWeek, endOfWeek) - .orElseThrow(() -> new IllegalArgumentException( - "카테고리 ID " + topConsumptionGoal.getCategory().getId() + "에 대한 현재 주 소비 데이터가 없습니다.")); + topConsumptionGoal.getCategory().getId(), startOfWeek, endOfWeek) + .orElseThrow(() -> new IllegalArgumentException( + "카테고리 ID " + topConsumptionGoal.getCategory().getId() + "에 대한 현재 주 소비 데이터가 없습니다.")); Long totalConsumptionAmountForCurrentWeek = currentWeekConsumptionAmount.getConsumeAmount(); return ConsumptionAnalysisConverter.fromEntity(topConsumptionGoal, totalConsumptionAmountForCurrentWeek); } + @Override + public List getTopConsumptionsLimit(int top, Long userId, int peerAgeS, int peerAgeE, + String peerG) { + + checkPeerInfo(userId, peerAgeS, peerAgeE, peerG); + + List topConsumptions = consumptionGoalRepository.findTopConsumptionAndConsumeAmountLimit(top, + peerAgeStart, peerAgeEnd, peerGender, currentMonth); + return topConsumptions.stream().map(TopConsumptionConverter::fromEntity).collect(Collectors.toList()); + } + + @Override + public Page getTopConsumptions(Long userId, int peerAgeS, int peerAgeE, String peerG, + Pageable pageable) { + + checkPeerInfo(userId, peerAgeS, peerAgeE, peerG); + + Page topConsumptions = consumptionGoalRepository.findTopConsumptionAndConsumeAmount( + peerAgeStart, peerAgeEnd, peerGender, currentMonth, pageable); + return topConsumptions.map(TopConsumptionConverter::fromEntity); + } + private User findUserById(Long userId) { Optional user = userRepository.findById(userId); @@ -144,28 +174,28 @@ private void setAgeGroupByUser(int userAge) { @Override @Transactional public ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId, - ConsumptionGoalListRequestDto consumptionGoalListRequestDto) { + ConsumptionGoalListRequestDto consumptionGoalListRequestDto) { LocalDate thisMonth = LocalDate.now().withDayOfMonth(1); User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("Not found user")); List updatedConsumptionGoal = consumptionGoalListRequestDto.getConsumptionGoalList() - .stream() - .map(c -> updateConsumptionGoalWithRequestDto(user, c, thisMonth)) - .toList(); + .stream() + .map(c -> updateConsumptionGoalWithRequestDto(user, c, thisMonth)) + .toList(); List response = consumptionGoalRepository.saveAll(updatedConsumptionGoal) - .stream() - .map(consumptionGoalConverter::toConsumptionGoalResponseDto) - .toList(); + .stream() + .map(consumptionGoalConverter::toConsumptionGoalResponseDto) + .toList(); return consumptionGoalConverter.toConsumptionGoalResponseListDto(response, thisMonth); } private ConsumptionGoal updateConsumptionGoalWithRequestDto(User user, - ConsumptionGoalRequestDto consumptionGoalRequestDto, LocalDate goalMonth) { + ConsumptionGoalRequestDto consumptionGoalRequestDto, LocalDate goalMonth) { Category category = categoryRepository.findById(consumptionGoalRequestDto.getCategoryId()) - .orElseThrow(() -> new IllegalArgumentException("Not found Category")); + .orElseThrow(() -> new IllegalArgumentException("Not found Category")); ConsumptionGoal consumptionGoal = findOrElseGenerateConsumptionGoal(user, category, goalMonth); consumptionGoal.updateGoalAmount(consumptionGoalRequestDto.getGoalAmount()); @@ -175,17 +205,17 @@ private ConsumptionGoal updateConsumptionGoalWithRequestDto(User user, private ConsumptionGoal findOrElseGenerateConsumptionGoal(User user, Category category, LocalDate goalMonth) { return consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, category, goalMonth) - .orElseGet(() -> generateNewConsumptionGoal(user, category, goalMonth)); + .orElseGet(() -> generateNewConsumptionGoal(user, category, goalMonth)); } private ConsumptionGoal generateNewConsumptionGoal(User user, Category category, LocalDate goalMonth) { return ConsumptionGoal.builder() - .goalMonth(goalMonth) - .user(user) - .category(category) - .consumeAmount(0L) - .goalAmount(0L) - .build(); + .goalMonth(goalMonth) + .user(user) + .category(category) + .consumeAmount(0L) + .goalAmount(0L) + .build(); } @Override @@ -202,25 +232,25 @@ public ConsumptionGoalResponseListDto findUserConsumptionGoalList(Long userId, L private Map initializeGoalMap(Long userId) { return categoryRepository.findUserCategoryByUserId(userId) - .stream() - .collect(Collectors.toMap(Category::getId, consumptionGoalConverter::toConsumptionGoalResponseDto)); + .stream() + .collect(Collectors.toMap(Category::getId, consumptionGoalConverter::toConsumptionGoalResponseDto)); } private void updateGoalMapWithPreviousMonth(Long userId, LocalDate goalMonth, - Map goalMap) { + Map goalMap) { updateGoalMap(userId, goalMonth.minusMonths(1), goalMap); } private void updateGoalMapWithCurrentMonth(Long userId, LocalDate goalMonth, - Map goalMap) { + Map goalMap) { updateGoalMap(userId, goalMonth, goalMap); } private void updateGoalMap(Long userId, LocalDate month, Map goalMap) { consumptionGoalRepository.findConsumptionGoalByUserIdAndGoalMonth(userId, month) - .stream() - .map(consumptionGoalConverter::toConsumptionGoalResponseDto) - .forEach(goal -> goalMap.put(goal.getCategoryId(), goal)); + .stream() + .map(consumptionGoalConverter::toConsumptionGoalResponseDto) + .forEach(goal -> goalMap.put(goal.getCategoryId(), goal)); } @Override @@ -232,8 +262,8 @@ public void recalculateConsumptionAmount(Expense expense, ExpenseUpdateRequestDt private void restorePreviousGoalConsumptionAmount(Expense expense, User user) { ConsumptionGoal previousConsumptionGoal = consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth( - user, expense.getCategory(), expense.getExpenseDate().toLocalDate().withDayOfMonth(1)) - .orElseThrow(() -> new IllegalArgumentException("Not found consumptionGoal")); + user, expense.getCategory(), expense.getExpenseDate().toLocalDate().withDayOfMonth(1)) + .orElseThrow(() -> new IllegalArgumentException("Not found consumptionGoal")); previousConsumptionGoal.restoreConsumeAmount(expense.getAmount()); consumptionGoalRepository.save(previousConsumptionGoal); @@ -241,12 +271,12 @@ private void restorePreviousGoalConsumptionAmount(Expense expense, User user) { private void calculatePresentGoalConsumptionAmount(ExpenseUpdateRequestDto request, User user) { Category categoryToReplace = categoryRepository.findById(request.getCategoryId()) - .orElseThrow(() -> new IllegalArgumentException("Not found category")); + .orElseThrow(() -> new IllegalArgumentException("Not found category")); ConsumptionGoal consumptionGoal = consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth( - user, categoryToReplace, request.getExpenseDate().toLocalDate().withDayOfMonth(1)) - .orElseGet(() -> this.generateGoalByPreviousOrElseNew(user, categoryToReplace, - request.getExpenseDate().toLocalDate().withDayOfMonth(1))); + user, categoryToReplace, request.getExpenseDate().toLocalDate().withDayOfMonth(1)) + .orElseGet(() -> this.generateGoalByPreviousOrElseNew(user, categoryToReplace, + request.getExpenseDate().toLocalDate().withDayOfMonth(1))); consumptionGoal.updateConsumeAmount(request.getAmount()); consumptionGoalRepository.save(consumptionGoal); @@ -256,18 +286,18 @@ private ConsumptionGoal generateGoalByPreviousOrElseNew(User user, Category cate LocalDate previousMonth = goalMonth.minusMonths(1); return consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, category, previousMonth) - .map(this::generateGoalByPrevious) - .orElseGet(() -> generateNewConsumptionGoal(user, category, goalMonth)); + .map(this::generateGoalByPrevious) + .orElseGet(() -> generateNewConsumptionGoal(user, category, goalMonth)); } private ConsumptionGoal generateGoalByPrevious(ConsumptionGoal consumptionGoal) { return ConsumptionGoal.builder() - .goalMonth(consumptionGoal.getGoalMonth().plusMonths(1)) - .user(consumptionGoal.getUser()) - .category(consumptionGoal.getCategory()) - .consumeAmount(0L) - .goalAmount(consumptionGoal.getGoalAmount()) - .build(); + .goalMonth(consumptionGoal.getGoalMonth().plusMonths(1)) + .user(consumptionGoal.getUser()) + .category(consumptionGoal.getCategory()) + .consumeAmount(0L) + .goalAmount(consumptionGoal.getGoalAmount()) + .build(); } @Override @@ -275,11 +305,11 @@ 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")); + .orElseThrow(() -> new IllegalArgumentException("Not found Category")); LocalDate thisMonth = LocalDate.now().withDayOfMonth(1); ConsumptionGoal consumptionGoal = consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth( - user, category, thisMonth).orElseGet(() -> generateNewConsumptionGoal(user, category, thisMonth)); + user, category, thisMonth).orElseGet(() -> generateNewConsumptionGoal(user, category, thisMonth)); consumptionGoal.updateConsumeAmount(amount); consumptionGoalRepository.save(consumptionGoal); @@ -287,13 +317,29 @@ public void updateConsumeAmount(Long userId, Long categoryId, Long amount) { @Override public List getTopConsumption(int top, Long userId, int peerAgeS, int peerAgeE, - String peerG) { + String peerG) { checkPeerInfo(userId, peerAgeS, peerAgeE, peerG); List topConsumptions = consumptionGoalRepository.findTopConsumptionAndConsumeAmount(top, - peerAgeStart, peerAgeEnd, peerGender); - + peerAgeStart, peerAgeEnd, peerGender); return topConsumptions.stream().map(TopConsumptionConverter::fromEntity).collect(Collectors.toList()); } + + @Override + public void decreaseConsumeAmount(Long userId, Long categoryId, Long amount, LocalDate expenseDate) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("Not found user")); + + Category category = categoryRepository.findById(categoryId) + .orElseThrow(() -> new IllegalArgumentException("Not found Category")); + + LocalDate goalMonth = expenseDate.withDayOfMonth(1); + ConsumptionGoal consumptionGoal = consumptionGoalRepository + .findConsumptionGoalByUserAndCategoryAndGoalMonth(user, category, goalMonth) + .orElseThrow(() -> new IllegalArgumentException("Not found ConsumptionGoal")); + + consumptionGoal.decreaseConsumeAmount(amount); + consumptionGoalRepository.save(consumptionGoal); + } } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoApi.java index 5603e0f3..0aee8c53 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoApi.java @@ -1,5 +1,6 @@ package com.bbteam.budgetbuddies.domain.discountinfo.controller; +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountRequest; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; import io.swagger.v3.oas.annotations.Operation; @@ -7,7 +8,6 @@ import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.data.domain.Page; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; @@ -27,7 +27,7 @@ public interface DiscountInfoApi { @Parameter(name = "page", description = "페이지 번호, 0번이 1 페이지 입니다. (기본값은 0입니다.)"), @Parameter(name = "size", description = "한 페이지에 불러올 데이터 개수입니다. (기본값은 10개입니다.)") }) - ResponseEntity> getDiscountsByYearAndMonth( + ApiResponse> getDiscountsByYearAndMonth( @RequestParam Integer year, @RequestParam Integer month, @RequestParam(defaultValue = "0") Integer page, @@ -44,7 +44,7 @@ ResponseEntity> getDiscountsByYearAndMonth( @Parameters({ // @Parameter(name = "discountRequestDto", description = "등록할 할인 정보의 전체 내용입니다."), }) - public ResponseEntity registerDiscountInfo( + ApiResponse registerDiscountInfo( @RequestBody DiscountRequest.RegisterDto discountRequestDto ); @@ -59,7 +59,7 @@ public ResponseEntity registerDiscountInfo( @Parameter(name = "userId", description = "좋아요를 누른 사용자의 id입니다."), @Parameter(name = "discountInfoId", description = "좋아요를 누를 할인정보의 id입니다."), }) - public ResponseEntity likeDiscountInfo( + ApiResponse likeDiscountInfo( @RequestParam Long userId, @PathVariable Long discountInfoId ); @@ -75,7 +75,7 @@ public ResponseEntity likeDiscountInfo( @Parameter(name = "userId", description = "수정할 사용자의 id입니다."), // @Parameter(name = "discountRequestDto", description = "수정할 할인 정보의 전체 내용입니다."), }) - public ResponseEntity updateDiscountInfo( + ApiResponse updateDiscountInfo( @RequestParam Long userId, @RequestBody DiscountRequest.UpdateDto discountRequestDto ); @@ -91,7 +91,7 @@ public ResponseEntity updateDiscountInfo( @Parameter(name = "userId", description = "삭제할 사용자의 id입니다."), @Parameter(name = "discountInfoId", description = "삭제할 할인 정보의 id입니다."), }) - public ResponseEntity deleteDiscountInfo( + ApiResponse deleteDiscountInfo( @RequestParam Long userId, @PathVariable Long discountInfoId ); @@ -107,7 +107,7 @@ public ResponseEntity deleteDiscountInfo( @Parameter(name = "userId", description = "조회할 사용자의 id입니다."), @Parameter(name = "discountInfoId", description = "조회할 할인 정보의 id입니다."), }) - public ResponseEntity getDiscountInfo( + ApiResponse getDiscountInfo( @RequestParam Long userId, @PathVariable Long discountInfoId ); diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java index ec1c51d7..918107cc 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/controller/DiscountInfoController.java @@ -1,11 +1,12 @@ package com.bbteam.budgetbuddies.domain.discountinfo.controller; +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountRequest; import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; import com.bbteam.budgetbuddies.domain.discountinfo.service.DiscountInfoService; +import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @@ -17,7 +18,7 @@ public class DiscountInfoController implements DiscountInfoApi { @Override @GetMapping("") - public ResponseEntity> getDiscountsByYearAndMonth( + public ApiResponse> getDiscountsByYearAndMonth( @RequestParam Integer year, @RequestParam Integer month, @RequestParam(defaultValue = "0") Integer page, @@ -25,62 +26,62 @@ public ResponseEntity> getDiscountsByYearAndMonth( ) { Page discounts = discountInfoService.getDiscountsByYearAndMonth(year, month, page, size); - return ResponseEntity.ok(discounts); + return ApiResponse.onSuccess(discounts); } @Override @PostMapping("") - public ResponseEntity registerDiscountInfo( + public ApiResponse registerDiscountInfo( @RequestBody DiscountRequest.RegisterDto discountRequestDto ) { DiscountResponseDto discountResponseDto = discountInfoService.registerDiscountInfo(discountRequestDto); - return ResponseEntity.ok(discountResponseDto); + return ApiResponse.onSuccess(discountResponseDto); } @Override @PostMapping("/likes/{discountInfoId}") - public ResponseEntity likeDiscountInfo( - @RequestParam Long userId, + public ApiResponse likeDiscountInfo( + @RequestParam @ExistUser Long userId, @PathVariable Long discountInfoId ) { DiscountResponseDto discountResponseDto = discountInfoService.toggleLike(userId, discountInfoId); - return ResponseEntity.ok(discountResponseDto); + return ApiResponse.onSuccess(discountResponseDto); } @Override @PutMapping("") - public ResponseEntity updateDiscountInfo( - @RequestParam Long userId, + public ApiResponse updateDiscountInfo( + @RequestParam @ExistUser Long userId, @RequestBody DiscountRequest.UpdateDto discountRequestDto ) { DiscountResponseDto discountResponseDto = discountInfoService.updateDiscountInfo(userId, discountRequestDto); - return ResponseEntity.ok(discountResponseDto); + return ApiResponse.onSuccess(discountResponseDto); } @Override @DeleteMapping("/{discountInfoId}") - public ResponseEntity deleteDiscountInfo( - @RequestParam Long userId, + public ApiResponse deleteDiscountInfo( + @RequestParam @ExistUser Long userId, @PathVariable Long discountInfoId ) { String message = discountInfoService.deleteDiscountInfo(userId, discountInfoId); - return ResponseEntity.ok(message); + return ApiResponse.onSuccess(message); } @Override @GetMapping("/{discountInfoId}") - public ResponseEntity getDiscountInfo( - @RequestParam Long userId, + public ApiResponse getDiscountInfo( + @RequestParam @ExistUser Long userId, @PathVariable Long discountInfoId ) { DiscountResponseDto discountResponseDto = discountInfoService.getDiscountInfoById(userId, discountInfoId); - return ResponseEntity.ok(discountResponseDto); + return ApiResponse.onSuccess(discountResponseDto); } } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/repository/DiscountInfoRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/repository/DiscountInfoRepository.java index fd935c54..f62b9128 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/repository/DiscountInfoRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/repository/DiscountInfoRepository.java @@ -16,12 +16,13 @@ public interface DiscountInfoRepository extends JpaRepository findByDateRange(LocalDate startDate, LocalDate endDate, Pageable pageable); - @Query("SELECT i FROM DiscountInfo i WHERE ((i.startDate BETWEEN :startDate AND :endDate) OR i.endDate BETWEEN :startDate AND :endDate)") + @Query("SELECT i FROM DiscountInfo i WHERE (i.startDate <= :endDate AND i.endDate >= :startDate)" + + " ORDER BY i.likeCount DESC") List findByMonth(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate); - @Query("SELECT i FROM DiscountInfo i WHERE ((i.startDate BETWEEN :startDate AND :endDate) OR i.endDate BETWEEN :startDate AND :endDate)" + - " ORDER BY i.likeCount DESC" + - " LIMIT 2") + @Query("SELECT i FROM DiscountInfo i WHERE (i.startDate <= :endDate AND i.endDate >= :startDate)" + + " ORDER BY i.likeCount DESC" + + " LIMIT 2") List findRecommendInfoByMonth(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate); } 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 c9a3ef26..1751b374 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 @@ -19,38 +19,45 @@ 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; +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(Pageable pageable, Long userId, 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); @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))) + }) + @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 7ee7b908..9e55be5b 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 @@ -7,13 +7,7 @@ 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 org.springframework.web.bind.annotation.*; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseResponseDto; import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; @@ -30,7 +24,8 @@ 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); } @@ -47,17 +42,23 @@ public ResponseEntity findExpensesForMonth(Pag @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) { + expenseService.deleteExpense(expenseId); + return ResponseEntity.ok("Successfully deleted expense!"); + } } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseUpdateRequestDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseUpdateRequestDto.java index 7491c95c..4311b9b9 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseUpdateRequestDto.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseUpdateRequestDto.java @@ -3,11 +3,15 @@ import java.time.LocalDateTime; import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @Getter @NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor +@Builder public class ExpenseUpdateRequestDto { private Long expenseId; private Long categoryId; 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 4bd0d69e..43351aaf 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 @@ -17,4 +17,6 @@ public interface ExpenseService { ExpenseResponseDto findExpenseResponseFromUserIdAndExpenseId(Long userId, Long expenseId); ExpenseResponseDto updateExpense(Long userId, ExpenseUpdateRequestDto request); + + void deleteExpense(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 e2a53ad1..7966462c 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 @@ -53,7 +53,7 @@ public ExpenseResponseDto createExpense(ExpenseRequestDto expenseRequestDto) { } /* Case 2) - !default && 키테고리 테이블의 UserId 컬럼의 값이 나와 맞으면 (= custom cateogory) + !default && 키테고리 테이블의 UserId 컬럼의 값이 나와 맞으면 (= custom category) */ else if (!category.getIsDefault() && category.getUser().getId().equals(expenseRequestDto.getUserId())) { // custom category @@ -96,6 +96,23 @@ 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")); + + Long userId = expense.getUser().getId(); + Long categoryId = expense.getCategory().getId(); + Long amount = expense.getAmount(); + LocalDate expenseDate = expense.getExpenseDate().toLocalDate(); + + expenseRepository.delete(expense); + + // 소비 금액 차감 로직 + consumptionGoalService.decreaseConsumeAmount(userId, categoryId, amount, expenseDate); + } + @Override @Transactional(readOnly = true) public MonthlyExpenseCompactResponseDto getMonthlyExpense(Pageable pageable, Long userId, LocalDate localDate) { diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageApi.java index 7b10b64b..757722f8 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageApi.java @@ -1,6 +1,8 @@ package com.bbteam.budgetbuddies.domain.mainpage.controller; +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.mainpage.dto.MainPageResponseDto; +import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; @@ -17,5 +19,5 @@ public interface MainPageApi { @Parameters({ @Parameter(name = "userId", description = "현재 데이터를 요청하는 사용자입니다. parameter"), }) - ResponseEntity getMainPage(Long userId); + ApiResponse getMainPage(@RequestParam("userId") @ExistUser Long userId); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageController.java b/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageController.java index 0c449f25..534bb66a 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/controller/MainPageController.java @@ -1,9 +1,12 @@ package com.bbteam.budgetbuddies.domain.mainpage.controller; +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.mainpage.dto.MainPageResponseDto; import com.bbteam.budgetbuddies.domain.mainpage.service.MainPageService; +import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -11,14 +14,15 @@ @RestController @RequiredArgsConstructor +@Validated public class MainPageController implements MainPageApi{ private final MainPageService mainPageService; @GetMapping("/main") - public ResponseEntity getMainPage - (@RequestParam("userId") Long userId) { + public ApiResponse getMainPage + (@RequestParam("userId") @ExistUser Long userId) { MainPageResponseDto mainPage = mainPageService.getMainPage(userId); - return ResponseEntity.ok(mainPage); + return ApiResponse.onSuccess(mainPage); } } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/service/MainPageServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/service/MainPageServiceImpl.java index 1fb55e1f..b5089589 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/service/MainPageServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/mainpage/service/MainPageServiceImpl.java @@ -1,5 +1,10 @@ package com.bbteam.budgetbuddies.domain.mainpage.service; +import java.time.LocalDate; +import java.util.List; +import java.util.NoSuchElementException; + +import org.springframework.stereotype.Service; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopGoalCategoryResponseDTO; @@ -10,39 +15,38 @@ import com.bbteam.budgetbuddies.domain.mainpage.dto.MainPageResponseDto; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; import com.bbteam.budgetbuddies.domain.supportinfo.service.SupportInfoService; -import com.bbteam.budgetbuddies.enums.Gender; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import java.time.LocalDate; -import java.util.List; -import java.util.NoSuchElementException; +import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor -public class MainPageServiceImpl implements MainPageService{ - private final DiscountInfoService discountInfoService; - private final SupportInfoService supportInfoService; - private final ConsumptionGoalService consumptionGoalService; - - @Override - public MainPageResponseDto getMainPage(Long userId) { - LocalDate now = LocalDate.now(); - - List discountResponseDtoList = discountInfoService.getDiscountsByYearAndMonth(now.getYear(), now.getMonthValue(), 0, 2) - .getContent(); - List supportResponseDtoList = supportInfoService.getSupportsByYearAndMonth(now.getYear(), now.getMonthValue(), 0, 2) - .getContent(); - - List topGoalCategoryResponseDTOList = consumptionGoalService.getTopGoalCategories(1, userId, 0, 0, "NONE"); - if(topGoalCategoryResponseDTOList.size() == 0){ - throw new NoSuchElementException("Category xx"); - } - TopGoalCategoryResponseDTO topGoalCategoryResponseDTO = topGoalCategoryResponseDTOList.get(0); - - ConsumptionGoalResponseListDto userConsumptionGoal = consumptionGoalService.findUserConsumptionGoalList(userId, now); - - return MainPageConverter.toMainPageResponseDto(discountResponseDtoList, supportResponseDtoList, - topGoalCategoryResponseDTO, userConsumptionGoal); - } +public class MainPageServiceImpl implements MainPageService { + private final DiscountInfoService discountInfoService; + private final SupportInfoService supportInfoService; + private final ConsumptionGoalService consumptionGoalService; + + @Override + public MainPageResponseDto getMainPage(Long userId) { + LocalDate now = LocalDate.now(); + + List discountResponseDtoList = discountInfoService.getDiscountsByYearAndMonth( + now.getYear(), now.getMonthValue(), 0, 2) + .getContent(); + List supportResponseDtoList = supportInfoService.getSupportsByYearAndMonth(now.getYear(), + now.getMonthValue(), 0, 2) + .getContent(); + + List topGoalCategoryResponseDTOList = consumptionGoalService.getTopGoalCategoriesLimit( + 1, userId, 0, 0, "NONE"); + if (topGoalCategoryResponseDTOList.size() == 0) { + throw new NoSuchElementException("Category xx"); + } + TopGoalCategoryResponseDTO topGoalCategoryResponseDTO = topGoalCategoryResponseDTOList.get(0); + + ConsumptionGoalResponseListDto userConsumptionGoal = consumptionGoalService.findUserConsumptionGoalList(userId, + now); + + return MainPageConverter.toMainPageResponseDto(discountResponseDtoList, supportResponseDtoList, + topGoalCategoryResponseDTO, userConsumptionGoal); + } } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoApi.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoApi.java index 0bdba4ab..900b6ff4 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoApi.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoApi.java @@ -1,5 +1,6 @@ package com.bbteam.budgetbuddies.domain.supportinfo.controller; +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequest; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; import io.swagger.v3.oas.annotations.Operation; @@ -7,7 +8,6 @@ import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.responses.ApiResponses; import org.springframework.data.domain.Page; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; @@ -27,7 +27,7 @@ public interface SupportInfoApi { @Parameter(name = "page", description = "페이지 번호, 0번이 1 페이지 입니다. (기본값은 0입니다.)"), @Parameter(name = "size", description = "한 페이지에 불러올 데이터 개수입니다. (기본값은 10개입니다.)") }) - ResponseEntity> getSupportsByYearAndMonth( + ApiResponse> getSupportsByYearAndMonth( @RequestParam Integer year, @RequestParam Integer month, @RequestParam(defaultValue = "0") Integer page, @@ -41,7 +41,7 @@ ResponseEntity> getSupportsByYearAndMonth( // @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))) }) - ResponseEntity registerSupportInfo( + ApiResponse registerSupportInfo( @RequestBody SupportRequest.RegisterDto requestDto ); @@ -56,7 +56,7 @@ ResponseEntity registerSupportInfo( @Parameter(name = "userId", description = "좋아요를 누른 사용자의 id입니다."), @Parameter(name = "supportInfoId", description = "좋아요를 누를 지원정보의 id입니다."), }) - ResponseEntity likeSupportInfo( + ApiResponse likeSupportInfo( @RequestParam Long userId, @PathVariable Long supportInfoId ); @@ -71,7 +71,7 @@ ResponseEntity likeSupportInfo( @Parameters({ @Parameter(name = "userId", description = "수정할 사용자의 id입니다."), }) - ResponseEntity updateSupportInfo( + ApiResponse updateSupportInfo( @RequestParam Long userId, @RequestBody SupportRequest.UpdateDto supportRequestDto ); @@ -87,7 +87,7 @@ ResponseEntity updateSupportInfo( @Parameter(name = "userId", description = "삭제할 사용자의 id입니다."), @Parameter(name = "supportInfoId", description = "삭제할 지원 정보의 id입니다."), }) - ResponseEntity deleteSupportInfo( + ApiResponse deleteSupportInfo( @RequestParam Long userId, @PathVariable Long supportInfoId ); @@ -103,7 +103,7 @@ ResponseEntity deleteSupportInfo( @Parameter(name = "userId", description = "조회할 사용자의 id입니다."), @Parameter(name = "supportInfoId", description = "조회할 지원 정보의 id입니다."), }) - ResponseEntity getSupportInfo( + ApiResponse getSupportInfo( @RequestParam Long userId, @PathVariable Long supportInfoId ); diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoController.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoController.java index 4a5eccb9..eb0c89b5 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoController.java @@ -1,13 +1,12 @@ package com.bbteam.budgetbuddies.domain.supportinfo.controller; -import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountRequest; -import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequest; import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; import com.bbteam.budgetbuddies.domain.supportinfo.service.SupportInfoService; +import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController @@ -19,7 +18,7 @@ public class SupportInfoController implements SupportInfoApi { @Override @GetMapping("") - public ResponseEntity> getSupportsByYearAndMonth( + public ApiResponse> getSupportsByYearAndMonth( @RequestParam Integer year, @RequestParam Integer month, @RequestParam(defaultValue = "0") Integer page, @@ -27,61 +26,61 @@ public ResponseEntity> getSupportsByYearAndMonth( ) { Page supports = supportInfoService.getSupportsByYearAndMonth(year, month, page, size); - return ResponseEntity.ok(supports); + return ApiResponse.onSuccess(supports); } @Override @PostMapping("") - public ResponseEntity registerSupportInfo( + public ApiResponse registerSupportInfo( @RequestBody SupportRequest.RegisterDto requestDto ) { SupportResponseDto supportResponseDto = supportInfoService.registerSupportInfo(requestDto); - return ResponseEntity.ok(supportResponseDto); + return ApiResponse.onSuccess(supportResponseDto); } @Override @PostMapping("/likes/{supportInfoId}") - public ResponseEntity likeSupportInfo( - @RequestParam Long userId, + public ApiResponse likeSupportInfo( + @RequestParam @ExistUser Long userId, @PathVariable Long supportInfoId ) { SupportResponseDto supportResponseDto = supportInfoService.toggleLike(userId, supportInfoId); - return ResponseEntity.ok(supportResponseDto); + return ApiResponse.onSuccess(supportResponseDto); } @Override @PutMapping("") - public ResponseEntity updateSupportInfo( - @RequestParam Long userId, + public ApiResponse updateSupportInfo( + @RequestParam @ExistUser Long userId, @RequestBody SupportRequest.UpdateDto supportRequestDto ) { SupportResponseDto supportResponseDto = supportInfoService.updateSupportInfo(userId, supportRequestDto); - return ResponseEntity.ok(supportResponseDto); + return ApiResponse.onSuccess(supportResponseDto); } @Override @DeleteMapping("/{supportInfoId}") - public ResponseEntity deleteSupportInfo( - @RequestParam Long userId, + public ApiResponse deleteSupportInfo( + @RequestParam @ExistUser Long userId, @PathVariable Long supportInfoId ) { String message = supportInfoService.deleteSupportInfo(userId, supportInfoId); - return ResponseEntity.ok(message); + return ApiResponse.onSuccess(message); } @Override @GetMapping("/{supportInfoId}") - public ResponseEntity getSupportInfo( - @RequestParam Long userId, + public ApiResponse getSupportInfo( + @RequestParam @ExistUser Long userId, @PathVariable Long supportInfoId ) { SupportResponseDto supportResponseDto = supportInfoService.getSupportInfoById(userId, supportInfoId); - return ResponseEntity.ok(supportResponseDto); + return ApiResponse.onSuccess(supportResponseDto); } } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/repository/SupportInfoRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/repository/SupportInfoRepository.java index 36488132..c6e0a13c 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/repository/SupportInfoRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/repository/SupportInfoRepository.java @@ -17,10 +17,11 @@ public interface SupportInfoRepository extends JpaRepository " ORDER BY i.likeCount DESC") Page findByDateRange(LocalDate startDate, LocalDate endDate, Pageable pageable); - @Query("SELECT i FROM SupportInfo i WHERE ((i.startDate BETWEEN :startDate AND :endDate) OR i.endDate BETWEEN :startDate AND :endDate)") + @Query("SELECT i FROM SupportInfo i WHERE (i.startDate <= :endDate AND i.endDate >= :startDate)" + + " ORDER BY i.likeCount DESC") List findByMonth(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate); - @Query("SELECT i FROM SupportInfo i WHERE ((i.startDate BETWEEN :startDate AND :endDate) OR i.endDate BETWEEN :startDate AND :endDate)" + + @Query("SELECT i FROM SupportInfo i WHERE (i.startDate <= :endDate AND i.endDate >= :startDate)" + " ORDER BY i.likeCount DESC" + " LIMIT 2") List findRecommendInfoByMonth(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate); diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserController.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserController.java index 2f517ecc..0cd4b5d4 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserController.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/controller/UserController.java @@ -1,9 +1,13 @@ package com.bbteam.budgetbuddies.domain.user.controller; +import com.bbteam.budgetbuddies.apiPayload.ApiResponse; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.UserConsumptionGoalResponse; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; import com.bbteam.budgetbuddies.domain.user.service.UserService; +import com.bbteam.budgetbuddies.domain.user.validation.ExistUser; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -11,6 +15,7 @@ @RestController @RequestMapping("/users") @RequiredArgsConstructor +@Validated public class UserController { private final UserService userService; @@ -20,4 +25,20 @@ public ResponseEntity> createConsumptionGoals( List consumptionGoals = userService.createConsumptionGoalWithDefaultGoals(userId); return ResponseEntity.ok(consumptionGoals); } + + @PostMapping("/register") + public ApiResponse registerUser(@RequestBody UserDto.RegisterDto dto) { + return ApiResponse.onSuccess(userService.saveUser(dto)); + } + + @GetMapping("/{userId}/find") + public ApiResponse findOne(@PathVariable("userId") @ExistUser Long userId) { + return ApiResponse.onSuccess(userService.findUser(userId)); + } + + @PutMapping("/{userId}/change") + public ApiResponse changeOne(@PathVariable("userId") @ExistUser Long userId, + @RequestParam("email") String email, @RequestParam("name") String name) { + return ApiResponse.onSuccess(userService.changeUser(userId, email, name)); + } } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java new file mode 100644 index 00000000..522cd458 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/converter/UserConverter.java @@ -0,0 +1,33 @@ +package com.bbteam.budgetbuddies.domain.user.converter; + +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; +import com.bbteam.budgetbuddies.domain.user.entity.User; + +public class UserConverter { + + public static UserDto.ResponseDto toDto(User user) { + return UserDto.ResponseDto.builder() + .id(user.getId()) + .name(user.getName()) + .phoneNumber(user.getPhoneNumber()) + .lastLoginAt(user.getLastLoginAt()) + .gender(user.getGender()) + .consumptionPattern(user.getConsumptionPattern()) + .photoUrl(user.getPhotoUrl()) + .email(user.getEmail()) + .age(user.getAge()) + .build(); + } + + public static User toUser(UserDto.RegisterDto dto) { + return User.builder() + .phoneNumber(dto.getPhoneNumber()) + .email(dto.getEmail()) + .age(dto.getAge()) + .name(dto.getName()) + .consumptionPattern(dto.getConsumptionPattern()) + .gender(dto.getGender()) + .photoUrl(dto.getPhotoUrl()) + .build(); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java new file mode 100644 index 00000000..7dd580d3 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/UserDto.java @@ -0,0 +1,43 @@ +package com.bbteam.budgetbuddies.domain.user.dto; + +import com.bbteam.budgetbuddies.enums.Gender; +import jakarta.persistence.Column; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.validation.constraints.Min; +import lombok.Builder; +import lombok.Getter; +import org.hibernate.validator.constraints.Length; + +import java.time.LocalDateTime; + +public class UserDto { + + @Getter + @Builder + public static class RegisterDto { + @Length(min = 11, message = "전화번호 11자리를 입력해주세요.") + private String phoneNumber; + private String name; + @Min(value = 1, message = "나이는 0또는 음수가 될 수 없습니다.") + private Integer age; + private Gender gender; + private String email; + private String photoUrl; + private String consumptionPattern; + } + + @Getter + @Builder + public static class ResponseDto { + private Long id; + private String phoneNumber; + private String name; + private String email; + private Integer age; + private Gender gender; + private String photoUrl; + private String consumptionPattern; + private LocalDateTime lastLoginAt; + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/package-info.java deleted file mode 100644 index dc7d64bd..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/dto/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.user.dto; \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java index c41f790c..dc4d6d6d 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/entity/User.java @@ -1,6 +1,7 @@ package com.bbteam.budgetbuddies.domain.user.entity; import com.bbteam.budgetbuddies.common.BaseEntity; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; import com.bbteam.budgetbuddies.enums.Gender; import jakarta.persistence.*; import jakarta.validation.constraints.Min; @@ -43,4 +44,9 @@ public class User extends BaseEntity { private LocalDateTime lastLoginAt; + public void changeUserDate(String email, String name) { + this.name = name; + this.email = email; + } + } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserService.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserService.java index c8da6a48..b53ca17e 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserService.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserService.java @@ -1,8 +1,15 @@ package com.bbteam.budgetbuddies.domain.user.service; import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.UserConsumptionGoalResponse; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; import java.util.List; public interface UserService { List createConsumptionGoalWithDefaultGoals(Long userId); + + UserDto.ResponseDto saveUser(UserDto.RegisterDto dto); + + UserDto.ResponseDto findUser(Long userId); + + UserDto.ResponseDto changeUser(Long userId, String email, String name); } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserServiceImpl.java index db6efe96..e8794018 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserServiceImpl.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/service/UserServiceImpl.java @@ -6,6 +6,8 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.UserConsumptionGoalResponse; import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; import com.bbteam.budgetbuddies.domain.consumptiongoal.repository.ConsumptionGoalRepository; +import com.bbteam.budgetbuddies.domain.user.converter.UserConverter; +import com.bbteam.budgetbuddies.domain.user.dto.UserDto; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; import lombok.RequiredArgsConstructor; @@ -14,10 +16,12 @@ import java.time.LocalDate; import java.util.List; +import java.util.NoSuchElementException; import java.util.stream.Collectors; @Service @RequiredArgsConstructor +@Transactional(readOnly = true) public class UserServiceImpl implements UserService { private final UserRepository userRepository; @@ -48,4 +52,26 @@ public List createConsumptionGoalWithDefaultGoals(L .map(consumptionGoalConverter::toUserConsumptionGoalResponse) .collect(Collectors.toList()); } + + @Override + @Transactional + public UserDto.ResponseDto saveUser(UserDto.RegisterDto dto) { + User user = UserConverter.toUser(dto); + userRepository.save(user); + return UserConverter.toDto(user); + } + + @Override + public UserDto.ResponseDto findUser(Long userId) { + User user = userRepository.findById(userId).orElseThrow(() -> new NoSuchElementException("해당 유저는 존재하지 않습니다.")); + return UserConverter.toDto(user); + } + + @Override + @Transactional + public UserDto.ResponseDto changeUser(Long userId, String email, String name) { + User user = userRepository.findById(userId).orElseThrow(() -> new NoSuchElementException("No such user")); + user.changeUserDate(email, name); + return UserConverter.toDto(user); + } } diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/calendar/service/CalendarServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/calendar/service/CalendarServiceTest.java index ded0e299..c06e412e 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/calendar/service/CalendarServiceTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/calendar/service/CalendarServiceTest.java @@ -176,15 +176,16 @@ void findTest(){ List discountInfoDtoList = result.getCalendarMonthInfoDto().getDiscountInfoDtoList(); List supportInfoDtoList = result.getCalendarMonthInfoDto().getSupportInfoDtoList(); + assertThat(discountInfoDtoList.size()).isEqualTo(3); - CalendarDto.CalendarDiscountInfoDto discountDto1 = CalendarConverter.toCalendarDiscountInfoDto(discountInfo1); - CalendarDto.CalendarDiscountInfoDto discountDto2 = CalendarConverter.toCalendarDiscountInfoDto(discountInfo2); - CalendarDto.CalendarDiscountInfoDto discountDto3 = CalendarConverter.toCalendarDiscountInfoDto(discountInfo3); + CalendarDto.CalendarDiscountInfoDto discountDto1 = CalendarConverter.toCalendarDiscountInfoDto(discountInfo1, firstDay, lastDay); + CalendarDto.CalendarDiscountInfoDto discountDto2 = CalendarConverter.toCalendarDiscountInfoDto(discountInfo2, firstDay, lastDay); + CalendarDto.CalendarDiscountInfoDto discountDto3 = CalendarConverter.toCalendarDiscountInfoDto(discountInfo3, firstDay, lastDay); assertThat(discountInfoDtoList).containsExactlyInAnyOrder(discountDto1, discountDto2, discountDto3); assertThat(supportInfoDtoList.size()).isEqualTo(2); - CalendarDto.CalendarSupportInfoDto supportDto1 = CalendarConverter.toCalendarSupportInfoDto(supportInfo1); - CalendarDto.CalendarSupportInfoDto supportDto3 = CalendarConverter.toCalendarSupportInfoDto(supportInfo3); + CalendarDto.CalendarSupportInfoDto supportDto1 = CalendarConverter.toCalendarSupportInfoDto(supportInfo1, firstDay, lastDay); + CalendarDto.CalendarSupportInfoDto supportDto3 = CalendarConverter.toCalendarSupportInfoDto(supportInfo3, firstDay, lastDay); assertThat(supportInfoDtoList).containsExactlyInAnyOrder(supportDto1, supportDto3); List recDiscountDtoList = result.getRecommendMonthInfoDto().getDiscountInfoDtoList(); diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java index dc5e29e7..360c66ce 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java @@ -30,6 +30,8 @@ class ConsumptionGoalRepositoryTest { @Autowired CategoryRepository categoryRepository; + LocalDate currentMonth = LocalDate.now().withDayOfMonth(1); + @Test @DisplayName("유저 아이디와 goalMonth를 통해 GoalConsumption 조회 성공") void findConsumptionGoalByUserIdAndGoalMonth_Success() { @@ -90,7 +92,7 @@ void findTopCategoriesAndGoalAmount_Success() { Category defaultCategory = categoryRepository.save( Category.builder().name("디폴트 카테고리").user(null).isDefault(true).build()); - LocalDate goalMonth = LocalDate.of(2024, 07, 01); + LocalDate goalMonth = LocalDate.now(); ConsumptionGoal defaultCategoryConsumptionGoal = consumptionGoalRepository.save(ConsumptionGoal.builder() .goalAmount(1L) @@ -106,8 +108,8 @@ void findTopCategoriesAndGoalAmount_Success() { int peerAgeEnd = 25; Gender peerGender = Gender.MALE; - List result = consumptionGoalRepository.findTopCategoriesAndGoalAmount( - top, peerAgeStart, peerAgeEnd, peerGender); + List result = consumptionGoalRepository.findTopCategoriesAndGoalAmountLimit( + top, peerAgeStart, peerAgeEnd, peerGender, currentMonth); // then ConsumptionGoal resultGoal = result.get(0); @@ -205,7 +207,7 @@ void findTopConsumptionAndConsumeAmount_Success() { Category defaultCategory = categoryRepository.save( Category.builder().name("디폴트 카테고리").user(null).isDefault(true).build()); - LocalDate goalMonth = LocalDate.of(2024, 07, 01); + LocalDate goalMonth = LocalDate.now(); ConsumptionGoal defaultCategoryConsumptionGoal = consumptionGoalRepository.save(ConsumptionGoal.builder() .goalAmount(1L) @@ -221,8 +223,8 @@ void findTopConsumptionAndConsumeAmount_Success() { int peerAgeEnd = 25; Gender peerGender = Gender.MALE; - List result = consumptionGoalRepository.findTopConsumptionAndConsumeAmount( - top, peerAgeStart, peerAgeEnd, peerGender); + List result = consumptionGoalRepository.findTopConsumptionAndConsumeAmountLimit( + top, peerAgeStart, peerAgeEnd, peerGender, currentMonth); // then ConsumptionGoal resultGoal = result.get(0); diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java index 5b83331f..3a2af1eb 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java @@ -16,6 +16,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; @@ -35,6 +36,8 @@ import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopGoalCategoryResponseDTO; import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; import com.bbteam.budgetbuddies.domain.consumptiongoal.repository.ConsumptionGoalRepository; +import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseUpdateRequestDto; +import com.bbteam.budgetbuddies.domain.expense.entity.Expense; import com.bbteam.budgetbuddies.domain.user.entity.User; import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; import com.bbteam.budgetbuddies.enums.Gender; @@ -57,6 +60,8 @@ class ConsumptionGoalServiceTest { @Spy private ConsumptionGoalConverter consumptionGoalConverter; + private final LocalDate currentMonth = LocalDate.now().withDayOfMonth(1); + @BeforeEach void setUp() { Random random = new Random(); @@ -70,7 +75,6 @@ void setUp() { .gender(Gender.MALE) .phoneNumber("010-1234-5678") .build()); - given(user.getId()).willReturn(-1L); } @Test @@ -310,7 +314,8 @@ void getTopCategoryAndConsumptionAmount_Success() { LocalDate endOfWeek = goalMonthRandomDay.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)); given(userRepository.findById(user.getId())).willReturn(Optional.of(user)); - given(consumptionGoalRepository.findTopCategoriesAndGoalAmount(1, 23, 25, Gender.MALE)).willReturn( + given(consumptionGoalRepository.findTopCategoriesAndGoalAmountLimit(1, 23, 25, Gender.MALE, + currentMonth)).willReturn( List.of(topConsumptionGoal)); given( consumptionGoalRepository.findTopConsumptionByCategoryIdAndCurrentWeek(defaultCategory.getId(), startOfWeek, @@ -369,11 +374,13 @@ void getTopGoalCategories_Success() { .build(); given(userRepository.findById(user.getId())).willReturn(Optional.of(user)); - given(consumptionGoalRepository.findTopCategoriesAndGoalAmount(4, 23, 25, Gender.MALE)).willReturn( + given(consumptionGoalRepository.findTopCategoriesAndGoalAmountLimit(4, 23, 25, Gender.MALE, + currentMonth)).willReturn( List.of(topConsumptionGoal1, topConsumptionGoal2, topConsumptionGoal3, topConsumptionGoal4)); // when - List result = consumptionGoalService.getTopGoalCategories(4, user.getId(), 0, 0, + List result = consumptionGoalService.getTopGoalCategoriesLimit(4, user.getId(), 0, + 0, "none"); // then @@ -393,8 +400,10 @@ void getTopGoalCategories_Success() { void getTopConsumptionCategories_Success() { // given Category defaultCategory = Mockito.spy(Category.builder().name("디폴트 카테고리").user(null).isDefault(true).build()); - Category defaultCategory2 = Mockito.spy(Category.builder().name("디폴트 카테고리2").user(null).isDefault(true).build()); - Category defaultCategory3 = Mockito.spy(Category.builder().name("디폴트 카테고리3").user(null).isDefault(true).build()); + Category defaultCategory2 = Mockito.spy( + Category.builder().name("디폴트 카테고리2").user(null).isDefault(true).build()); + Category defaultCategory3 = Mockito.spy( + Category.builder().name("디폴트 카테고리3").user(null).isDefault(true).build()); ConsumptionGoal topConsumptionGoal1 = ConsumptionGoal.builder() .goalAmount(5000L) @@ -421,11 +430,12 @@ void getTopConsumptionCategories_Success() { .build(); given(userRepository.findById(user.getId())).willReturn(Optional.of(user)); - given(consumptionGoalRepository.findTopConsumptionAndConsumeAmount(3, 23, 25, Gender.MALE)).willReturn( + given(consumptionGoalRepository.findTopConsumptionAndConsumeAmountLimit(3, 23, 25, Gender.MALE, + currentMonth)).willReturn( List.of(topConsumptionGoal1, topConsumptionGoal2, topConsumptionGoal3)); // when - List result = consumptionGoalService.getTopConsumption(3, user.getId(), 23, 25, "MALE"); + List result = consumptionGoalService.getTopConsumptionsLimit(3, user.getId(), 23, 25, "MALE"); // then assertThat(result).hasSize(3); @@ -436,4 +446,168 @@ void getTopConsumptionCategories_Success() { assertThat(result.get(2).getCategoryName()).isEqualTo(defaultCategory3.getName()); assertThat(result.get(2).getConsumeAmount()).isEqualTo(topConsumptionGoal3.getConsumeAmount()); } + + @Test + @DisplayName("지난 달, 이번 달 소비 목표가 없는 카테고리에 대한 소비 업데이트를 진행하는 경우 새로운 소비 목표를 생성해 소비 금액을 갱신") + void recalculateConsumptionAmount_notExistPreviousMonthAndThisMonthGoal() { + // given + Category existGoalCategory = Category.builder().name("유저 카테고리").user(user).isDefault(false).build(); + Category notExistGoalCategory = Mockito.spy(Category.builder().name("디폴트 카테고리").isDefault(true).build()); + given(notExistGoalCategory.getId()).willReturn(-1L); + + Expense expense = Mockito.spy( + Expense.builder().category(existGoalCategory).expenseDate(GOAL_MONTH.atStartOfDay()).amount(1000L).build()); + when(expense.getId()).thenReturn(-1L); + + ExpenseUpdateRequestDto request = ExpenseUpdateRequestDto.builder() + .amount(1000L) + .expenseId(expense.getId()) + .expenseDate(LocalDate.of(2024, 8, 7).atStartOfDay()) + .categoryId(notExistGoalCategory.getId()) + .build(); + + ConsumptionGoal oldGoal = ConsumptionGoal.builder().consumeAmount(1000L).category(existGoalCategory).build(); + ConsumptionGoal expected = ConsumptionGoal.builder() + .goalMonth(LocalDate.of(2024, 8, 1)) + .goalAmount(0L) + .consumeAmount(1000L) + .category(notExistGoalCategory) + .user(user) + .build(); + // when + when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, expense.getCategory(), + expense.getExpenseDate().toLocalDate().withDayOfMonth(1))).thenReturn(Optional.ofNullable(oldGoal)); + + when(categoryRepository.findById(request.getCategoryId())).thenReturn(Optional.of(notExistGoalCategory)); + when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, notExistGoalCategory, + request.getExpenseDate().toLocalDate().withDayOfMonth(1))).thenReturn(Optional.empty()); + + when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, notExistGoalCategory, + request.getExpenseDate().minusMonths(1).toLocalDate().withDayOfMonth(1))).thenReturn(Optional.empty()); + + consumptionGoalService.recalculateConsumptionAmount(expense, request, user); + + ArgumentCaptor consumptionGoalCaptor = ArgumentCaptor.forClass(ConsumptionGoal.class); + verify(consumptionGoalRepository, times(2)).save(consumptionGoalCaptor.capture()); + + List savedConsumptionGoals = consumptionGoalCaptor.getAllValues(); + + // then + assertEquals(oldGoal.getConsumeAmount(), 0L); + assertThat(savedConsumptionGoals.get(1)).usingRecursiveComparison().isEqualTo(expected); + } + + @Test + @DisplayName("이번달 소비 목표가 없는 카테고리에 대한 소비 업데이트를 진행하는 경우 지난 달 소비 목표의 목표 금액을 복사한 이번 달 소비 목표를 생성해 소비 금액을 갱신") + void recalculateConsumptionAmount_notExistThisMonthGoal() { + // given + Category existGoalCategory = Category.builder().name("유저 카테고리").user(user).isDefault(false).build(); + Category notExistThisMonthGoalCategory = Mockito.spy( + Category.builder().name("디폴트 카테고리").isDefault(true).build()); + given(notExistThisMonthGoalCategory.getId()).willReturn(-1L); + + Expense expense = Mockito.spy( + Expense.builder().category(existGoalCategory).expenseDate(GOAL_MONTH.atStartOfDay()).amount(1000L).build()); + when(expense.getId()).thenReturn(-1L); + + ExpenseUpdateRequestDto request = ExpenseUpdateRequestDto.builder() + .amount(1000L) + .expenseId(expense.getId()) + .expenseDate(LocalDate.of(2024, 8, 7).atStartOfDay()) + .categoryId(notExistThisMonthGoalCategory.getId()) + .build(); + + ConsumptionGoal oldGoal = ConsumptionGoal.builder().consumeAmount(1000L).category(existGoalCategory).build(); + + ConsumptionGoal previousMonthGoal = ConsumptionGoal.builder() + .goalMonth(LocalDate.of(2024, 7, 1)) + .goalAmount(3000L) + .consumeAmount(3000L) + .category(notExistThisMonthGoalCategory) + .user(user) + .build(); + + ConsumptionGoal expected = ConsumptionGoal.builder() + .goalMonth(LocalDate.of(2024, 8, 1)) + .goalAmount(3000L) + .consumeAmount(1000L) + .category(notExistThisMonthGoalCategory) + .user(user) + .build(); + + // when + when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, expense.getCategory(), + expense.getExpenseDate().toLocalDate().withDayOfMonth(1))).thenReturn(Optional.ofNullable(oldGoal)); + + when(categoryRepository.findById(request.getCategoryId())).thenReturn( + Optional.of(notExistThisMonthGoalCategory)); + when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, + notExistThisMonthGoalCategory, request.getExpenseDate().toLocalDate().withDayOfMonth(1))).thenReturn( + Optional.empty()); + + when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, + notExistThisMonthGoalCategory, + request.getExpenseDate().minusMonths(1).toLocalDate().withDayOfMonth(1))).thenReturn( + Optional.ofNullable(previousMonthGoal)); + + consumptionGoalService.recalculateConsumptionAmount(expense, request, user); + + ArgumentCaptor consumptionGoalCaptor = ArgumentCaptor.forClass(ConsumptionGoal.class); + verify(consumptionGoalRepository, times(2)).save(consumptionGoalCaptor.capture()); + + List savedConsumptionGoals = consumptionGoalCaptor.getAllValues(); + + // then + assertEquals(oldGoal.getConsumeAmount(), 0L); + assertThat(savedConsumptionGoals.get(1)).usingRecursiveComparison().isEqualTo(expected); + } + + @Test + @DisplayName("이번달 소비 목표가 있는 경우 이번 달 소비 목표의 소비 금액을 갱신") + void recalculateConsumptionAmount_existThisMonthGoal() { + // given + Category existGoalCategory = Mockito.spy( + Category.builder().name("유저 카테고리").user(user).isDefault(false).build()); + given(existGoalCategory.getId()).willReturn(-1L); + + Expense expense = Mockito.spy( + Expense.builder().category(existGoalCategory).expenseDate(GOAL_MONTH.atStartOfDay()).amount(1000L).build()); + when(expense.getId()).thenReturn(-1L); + + ExpenseUpdateRequestDto request = ExpenseUpdateRequestDto.builder() + .amount(2000L) + .expenseId(expense.getId()) + .expenseDate(LocalDate.of(2024, 8, 7).atStartOfDay()) + .categoryId(existGoalCategory.getId()) + .build(); + + ConsumptionGoal oldGoal = ConsumptionGoal.builder() + .goalMonth(LocalDate.of(2024, 8, 1)) + .goalAmount(3000L) + .consumeAmount(1000L) + .category(existGoalCategory) + .user(user) + .build(); + + ConsumptionGoal expected = ConsumptionGoal.builder() + .goalMonth(LocalDate.of(2024, 8, 1)) + .goalAmount(3000L) + .consumeAmount(2000L) + .category(existGoalCategory) + .user(user) + .build(); + + // when + when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, expense.getCategory(), + expense.getExpenseDate().toLocalDate().withDayOfMonth(1))).thenReturn(Optional.ofNullable(oldGoal)); + + when(categoryRepository.findById(request.getCategoryId())).thenReturn(Optional.of(existGoalCategory)); + when(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, existGoalCategory, + request.getExpenseDate().toLocalDate().withDayOfMonth(1))).thenReturn(Optional.ofNullable(oldGoal)); + + consumptionGoalService.recalculateConsumptionAmount(expense, request, user); + + // then + assertThat(oldGoal).usingRecursiveComparison().isEqualTo(expected); + } } \ No newline at end of file