diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 00000000..d6b47f1f --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,46 @@ +name: server + + +on: + push: + branches: + - dev + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 17 + + - name: Ensure resource directory exists + run: mkdir -p ./src/main/resources + + - name : injection-yml + run : echo -E "${{ secrets.YML }}" > ./src/main/resources/application.yml + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Execute Gradle build and analyze + run: ./gradlew jib + + - name: Run scripts in server + uses: appleboy/ssh-action@master + with: + key: ${{ secrets.PRIVATE_KEY }} + host: ${{ secrets.HOST_DEV }} + username: ${{ secrets.USERNAME }} + script: ${{ secrets.SCRIPT }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index c2065bc2..7dbfa83c 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ out/ ### VS Code ### .vscode/ + +### Setting File ### +src/main/resources/application.yml \ No newline at end of file diff --git a/README.md b/README.md index 580d57db..a4b2469e 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@
# 빈주머니즈_Spring - +![빈주머니즈_커버_대지 1 사본](https://github.com/user-attachments/assets/744ef4c6-87cd-4db9-9c0b-f848231a203c) ![Java](https://img.shields.io/badge/java-%23ED8B00.svg?style=for-the-badge&logo=openjdk&logoColor=white) ![MySQL](https://img.shields.io/badge/mysql-4479A1.svg?style=for-the-badge&logo=mysql&logoColor=white) ![IntelliJ IDEA](https://img.shields.io/badge/IntelliJIDEA-000000.svg?style=for-the-badge&logo=intellij-idea&logoColor=white) ![Postman](https://img.shields.io/badge/Postman-FF6C37?style=for-the-badge&logo=postman&logoColor=white) + + ## Team ||||||| |:-:|:-:|:-:|:-:|:-:|:-:| diff --git a/build.gradle b/build.gradle index 7c2d02cc..f2596c13 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'java' id 'org.springframework.boot' version '3.2.7' id 'io.spring.dependency-management' version '1.1.5' + id 'com.google.cloud.tools.jib' version '3.4.3' } group = 'com.bbteam' @@ -38,3 +39,21 @@ dependencies { tasks.named('test') { useJUnitPlatform() } + +jib { + from { + image = 'openjdk:17-alpine' + platforms { + platform { + architecture = 'amd64' + os = 'linux' + } + } + } + to { + image = 'binjumeoniz1/binjumeoniz:latest' + } + container { + jvmFlags = ['-Dspring.profiles.active=dev'] + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/ApiResponse.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/ApiResponse.java new file mode 100644 index 00000000..b9d069a0 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/apiPayload/ApiResponse.java @@ -0,0 +1,38 @@ +package com.bbteam.budgetbuddies.apiPayload; + +import com.bbteam.budgetbuddies.apiPayload.code.BaseCode; +import com.bbteam.budgetbuddies.apiPayload.code.status.SuccessStatus; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +@JsonPropertyOrder({"isSuccess", "code", "message", "result"}) +public class ApiResponse { + + @JsonProperty("isSuccess") + private final Boolean isSuccess; + private final String code; + private final String message; + @JsonInclude(JsonInclude.Include.NON_NULL) + private T result; + + + // 성공한 경우 응답 생성 + public static ApiResponse onSuccess(T result){ + return new ApiResponse<>(true, SuccessStatus.OK.getCode() , SuccessStatus.OK.getMessage(), result); + } + + public static ApiResponse of(BaseCode code, T result){ + return new ApiResponse<>(true, code.getReasonHttpStatus().getCode() , code.getReasonHttpStatus().getMessage(), result); + } + + + // 실패한 경우 응답 생성 + public static ApiResponse onFailure(String code, String message, T data){ + return new ApiResponse<>(true, code, message, data); + } +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/BaseCode.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/BaseCode.java new file mode 100644 index 00000000..d5fb4d79 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/BaseCode.java @@ -0,0 +1,10 @@ +package com.bbteam.budgetbuddies.apiPayload.code; + +public interface BaseCode { + ReasonHttpStatus getReasonHttpStatus(); + + interface ReasonHttpStatus { + String getCode(); + String getMessage(); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/BaseErrorCode.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/BaseErrorCode.java new file mode 100644 index 00000000..c3388e55 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/BaseErrorCode.java @@ -0,0 +1,4 @@ +package com.bbteam.budgetbuddies.apiPayload.code; + +public interface BaseErrorCode { +} diff --git a/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/SuccessStatus.java b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/SuccessStatus.java new file mode 100644 index 00000000..414fe213 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/apiPayload/code/status/SuccessStatus.java @@ -0,0 +1,14 @@ +package com.bbteam.budgetbuddies.apiPayload.code.status; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum SuccessStatus { + OK("200", "OK"); + + private final String code; + private final String message; +} + diff --git a/src/main/java/com/bbteam/budgetbuddies/common/BaseEntity.java b/src/main/java/com/bbteam/budgetbuddies/common/BaseEntity.java index 7a9756c1..21800f97 100644 --- a/src/main/java/com/bbteam/budgetbuddies/common/BaseEntity.java +++ b/src/main/java/com/bbteam/budgetbuddies/common/BaseEntity.java @@ -1,7 +1,12 @@ package com.bbteam.budgetbuddies.common; import jakarta.persistence.*; -import lombok.*; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + import lombok.experimental.SuperBuilder; import org.hibernate.annotations.SoftDelete; import org.springframework.data.annotation.CreatedDate; @@ -17,6 +22,7 @@ @Getter @SuperBuilder @SoftDelete // boolean 타입의 deleted 필드가 추가 +@Getter public abstract class BaseEntity { @Id 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 new file mode 100644 index 00000000..bc98ff3f --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/calendar/controller/CalendarController.java @@ -0,0 +1,37 @@ +package com.bbteam.budgetbuddies.domain.calendar.controller; + +import com.bbteam.budgetbuddies.domain.calendar.dto.CalendarDto; +import com.bbteam.budgetbuddies.domain.calendar.service.CalendarService; +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 lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/calendar") +public class CalendarController { + + private final CalendarService calendarService; + @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 = "호출할 연도의 월입니다."), + }) + @GetMapping("/") + public ResponseEntity request( + @RequestParam("year") Integer year, @RequestParam("month") Integer month + ) { + CalendarDto.CalendarMonthResponseDto result = calendarService.find(year, month); + return ResponseEntity.ok(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 new file mode 100644 index 00000000..e62cd00b --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/calendar/converter/CalendarConverter.java @@ -0,0 +1,55 @@ +package com.bbteam.budgetbuddies.domain.calendar.converter; + +import com.bbteam.budgetbuddies.domain.calendar.dto.CalendarDto; +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; + +import java.util.List; + +public class CalendarConverter { + + public static CalendarDto.CalendarMonthResponseDto toCalendarMonthResponseDto(CalendarDto.CalendarMonthInfoDto calendarMonthInfoDto, CalendarDto.CalendarMonthInfoDto recommendMonthInfoDto){ + return CalendarDto.CalendarMonthResponseDto.builder() + .calendarMonthInfoDto(calendarMonthInfoDto) + .recommendMonthInfoDto(recommendMonthInfoDto) + .build(); + } + + public static CalendarDto.CalendarMonthInfoDto toCalendarMonthInfoDto(List discountInfoList, + List supportInfoList) { + List discountInfoDtoList = discountInfoList.stream() + .map(CalendarConverter::toCalendarDiscountInfoDto) + .toList(); + List supportInfoDtoList = supportInfoList.stream() + .map(CalendarConverter::toCalendarSupportInfoDto) + .toList(); + + return CalendarDto.CalendarMonthInfoDto.builder() + .discountInfoDtoList(discountInfoDtoList) + .supportInfoDtoList(supportInfoDtoList) + .build(); + } + + public static CalendarDto.CalendarDiscountInfoDto toCalendarDiscountInfoDto(DiscountInfo discountInfo) { + return CalendarDto.CalendarDiscountInfoDto.builder() + .id(discountInfo.getId()) + .title(discountInfo.getTitle()) + .likeCount(discountInfo.getLikeCount()) + .startDate(discountInfo.getStartDate()) + .endDate(discountInfo.getEndDate()) + .discountRate(discountInfo.getDiscountRate()) + .build(); + } + + public static CalendarDto.CalendarSupportInfoDto toCalendarSupportInfoDto(SupportInfo supportInfo) { + return CalendarDto.CalendarSupportInfoDto.builder() + .id(supportInfo.getId()) + .title(supportInfo.getTitle()) + .likeCount(supportInfo.getLikeCount()) + .startDate(supportInfo.getStartDate()) + .endDate(supportInfo.getEndDate()) + .build(); + } + + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/calendar/dto/CalendarDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/calendar/dto/CalendarDto.java new file mode 100644 index 00000000..78f6ccf7 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/calendar/dto/CalendarDto.java @@ -0,0 +1,48 @@ +package com.bbteam.budgetbuddies.domain.calendar.dto; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +import java.time.LocalDate; +import java.util.List; + +public class CalendarDto { + + @Getter + @Builder + public static class CalendarMonthResponseDto { + CalendarMonthInfoDto calendarMonthInfoDto; + CalendarMonthInfoDto recommendMonthInfoDto; + } + + @Getter + @Builder + public static class CalendarMonthInfoDto { + private List discountInfoDtoList; + private List supportInfoDtoList; + } + + @Getter + @Builder + @EqualsAndHashCode + public static class CalendarDiscountInfoDto { + private Long id; + private String title; + private LocalDate startDate; + private LocalDate endDate; + private Integer likeCount; + private Integer discountRate; + } + + @Getter + @Builder + @EqualsAndHashCode + public static class CalendarSupportInfoDto { + private Long id; + private String title; + private LocalDate startDate; + private LocalDate endDate; + private Integer likeCount; + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/calendar/service/CalendarService.java b/src/main/java/com/bbteam/budgetbuddies/domain/calendar/service/CalendarService.java new file mode 100644 index 00000000..bb4383c3 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/calendar/service/CalendarService.java @@ -0,0 +1,9 @@ +package com.bbteam.budgetbuddies.domain.calendar.service; + +import com.bbteam.budgetbuddies.domain.calendar.dto.CalendarDto; + +public interface CalendarService { + + CalendarDto.CalendarMonthResponseDto find(int year, int month); + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/calendar/service/CalendarServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/calendar/service/CalendarServiceImpl.java new file mode 100644 index 00000000..bd543261 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/calendar/service/CalendarServiceImpl.java @@ -0,0 +1,49 @@ +package com.bbteam.budgetbuddies.domain.calendar.service; + +import com.bbteam.budgetbuddies.domain.calendar.converter.CalendarConverter; +import com.bbteam.budgetbuddies.domain.calendar.dto.CalendarDto; +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.discountinfo.repository.DiscountInfoRepository; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import com.bbteam.budgetbuddies.domain.supportinfo.repository.SupportInfoRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.List; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class CalendarServiceImpl implements CalendarService{ + + private final DiscountInfoRepository discountInfoRepository; + + private final SupportInfoRepository supportInfoRepository; + + public CalendarDto.CalendarMonthResponseDto find(int year, int month){ + LocalDate firstDay = LocalDate.of(year, month, 1); + LocalDate lastDay = firstDay.withDayOfMonth(firstDay.lengthOfMonth()); + CalendarDto.CalendarMonthResponseDto result = getCalendarMonthResponseDto(firstDay, lastDay); + return result; + } + + private CalendarDto.CalendarMonthResponseDto getCalendarMonthResponseDto(LocalDate firstDay, LocalDate lastDay) { + CalendarDto.CalendarMonthInfoDto calendarMonthInfoDto = getCalendarMonthInfoDto(firstDay, lastDay); + CalendarDto.CalendarMonthInfoDto recommendMonthInfoDto = getRecommendMonthInfoDto(firstDay, lastDay); + return CalendarConverter.toCalendarMonthResponseDto(calendarMonthInfoDto, recommendMonthInfoDto); + } + + private CalendarDto.CalendarMonthInfoDto getCalendarMonthInfoDto(LocalDate firstDay, LocalDate lastDay) { + List monthDiscountInfoList = discountInfoRepository.findByMonth(firstDay, lastDay); + List monthSupportInfoList = supportInfoRepository.findByMonth(firstDay, lastDay); + return CalendarConverter.toCalendarMonthInfoDto(monthDiscountInfoList, monthSupportInfoList); + } + + 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); + } +} 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 new file mode 100644 index 00000000..beb16f89 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/controller/CategoryController.java @@ -0,0 +1,38 @@ +package com.bbteam.budgetbuddies.domain.category.controller; + +import com.bbteam.budgetbuddies.domain.category.dto.CategoryRequestDTO; +import com.bbteam.budgetbuddies.domain.category.dto.CategoryResponseDTO; +import com.bbteam.budgetbuddies.domain.category.service.CategoryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/categories") +public class CategoryController { + + private final CategoryService categoryService; + + @Operation(summary = "카테고리 추가", description = "사용자가 직접 카테고리를 추가합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + @PostMapping("/add") + public ResponseEntity createCategory( + @Parameter(description = "user_id, name(사용자가 입력한 카테고리명), is_default(default 카테고리 여부)로 request") + @RequestBody CategoryRequestDTO categoryRequestDTO + ) { + CategoryResponseDTO response = categoryService.createCategory(categoryRequestDTO); + return ResponseEntity.ok(response); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/controller/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/controller/package-info.java deleted file mode 100644 index 3ed2cb13..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/controller/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.category.controller; \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/converter/CategoryConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/converter/CategoryConverter.java new file mode 100644 index 00000000..69069b9d --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/converter/CategoryConverter.java @@ -0,0 +1,28 @@ +package com.bbteam.budgetbuddies.domain.category.converter; + +import com.bbteam.budgetbuddies.domain.category.dto.CategoryRequestDTO; +import com.bbteam.budgetbuddies.domain.category.dto.CategoryResponseDTO; +import com.bbteam.budgetbuddies.domain.category.entity.Category; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import org.springframework.stereotype.Component; + +@Component +public class CategoryConverter { + + public Category toCategoryEntity(CategoryRequestDTO categoryRequestDTO, User user) { + return Category.builder() + .name(categoryRequestDTO.getName()) + .isDefault(categoryRequestDTO.getIsDefault()) + .user(user) + .build(); + } + + public CategoryResponseDTO toCategoryResponseDTO(Category category) { + return CategoryResponseDTO.builder() + .id(category.getId()) + .name(category.getName()) + .userId(category.getUser().getId()) + .isDefault(category.getIsDefault()) + .build(); + } +} 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 new file mode 100644 index 00000000..3f8eee35 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/dto/CategoryRequestDTO.java @@ -0,0 +1,12 @@ +package com.bbteam.budgetbuddies.domain.category.dto; + +import lombok.Getter; +import lombok.Setter; + +@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 new file mode 100644 index 00000000..41381edb --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/dto/CategoryResponseDTO.java @@ -0,0 +1,14 @@ +package com.bbteam.budgetbuddies.domain.category.dto; + +import com.bbteam.budgetbuddies.domain.user.entity.User; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class CategoryResponseDTO { + private Long id; + private Long userId; + private String name; + private Boolean isDefault; +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/dto/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/dto/package-info.java deleted file mode 100644 index 54a5f6c4..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/dto/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.category.dto; \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/entity/Category.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/entity/Category.java index 366167ef..b25530f3 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/entity/Category.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/entity/Category.java @@ -3,9 +3,17 @@ import com.bbteam.budgetbuddies.common.BaseEntity; import com.bbteam.budgetbuddies.domain.user.entity.User; import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; import org.hibernate.annotations.ColumnDefault; @Entity +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder public class Category { @Id diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/repository/CategoryRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/repository/CategoryRepository.java new file mode 100644 index 00000000..c6b571bb --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/repository/CategoryRepository.java @@ -0,0 +1,16 @@ +package com.bbteam.budgetbuddies.domain.category.repository; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import com.bbteam.budgetbuddies.domain.category.entity.Category; + +public interface CategoryRepository extends JpaRepository { + @Query(value = "SELECT c FROM Category AS c WHERE c.isDefault=TRUE OR c.user.id=:id") + List findUserCategoryByUserId(@Param("id") Long id); + + boolean existsByUserIdAndName(Long userId, String name); +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/repository/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/repository/package-info.java deleted file mode 100644 index 995bc7a0..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/repository/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.category.repository; \ No newline at end of file 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 new file mode 100644 index 00000000..352b64c6 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryService.java @@ -0,0 +1,9 @@ +package com.bbteam.budgetbuddies.domain.category.service; + +import com.bbteam.budgetbuddies.domain.category.dto.CategoryRequestDTO; +import com.bbteam.budgetbuddies.domain.category.dto.CategoryResponseDTO; + +public interface CategoryService { + CategoryResponseDTO createCategory(CategoryRequestDTO categoryRequestDTO); +} + 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 new file mode 100644 index 00000000..52b26658 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/category/service/CategoryServiceImpl.java @@ -0,0 +1,38 @@ +package com.bbteam.budgetbuddies.domain.category.service; + +import com.bbteam.budgetbuddies.domain.category.converter.CategoryConverter; +import com.bbteam.budgetbuddies.domain.category.dto.CategoryRequestDTO; +import com.bbteam.budgetbuddies.domain.category.dto.CategoryResponseDTO; +import com.bbteam.budgetbuddies.domain.category.entity.Category; +import com.bbteam.budgetbuddies.domain.category.repository.CategoryRepository; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional +public class CategoryServiceImpl implements CategoryService { + + private final CategoryRepository categoryRepository; + private final UserRepository userRepository; + private final CategoryConverter categoryConverter; + + @Override + public CategoryResponseDTO createCategory(CategoryRequestDTO categoryRequestDTO) { + User user = userRepository.findById(categoryRequestDTO.getUserId()) + .orElseThrow(() -> new IllegalArgumentException("cannot find user")); + + if (categoryRepository.existsByUserIdAndName(categoryRequestDTO.getUserId(), categoryRequestDTO.getName())) { + throw new IllegalArgumentException("User already has a category with the same name"); + } + + + Category category = categoryConverter.toCategoryEntity(categoryRequestDTO, user); + Category savedCategory = categoryRepository.save(category); + + return categoryConverter.toCategoryResponseDTO(savedCategory); + } +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/category/service/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/category/service/package-info.java deleted file mode 100644 index 69cdabf3..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/category/service/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.category.service; \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/CommentController.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/CommentController.java new file mode 100644 index 00000000..36500a8b --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/CommentController.java @@ -0,0 +1,83 @@ +package com.bbteam.budgetbuddies.domain.comment.controller; + + +import com.bbteam.budgetbuddies.domain.comment.dto.CommentRequestDto; +import com.bbteam.budgetbuddies.domain.comment.dto.CommentResponseDto; +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.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +public interface CommentController { + @Operation(summary = "[User] 특정 할인 정보 게시글에 댓글달기", description = "특정 할인 정보 게시글에 댓글을 다는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "userId", description = "현재 댓글을 다는 유저 id입니다. parameter"), + @Parameter(name = "discountInfoId", description = "댓글을 다는 할인 정보 게시글 id입니다. requestBody"), + @Parameter(name = "content", description = "댓글 내용입니다. requestBody"), + }) + ResponseEntity saveDiscountInfoComment( + Long userId, + CommentRequestDto.DiscountInfoCommentDto discountInfoCommentDto); + + + @Operation(summary = "[User] 특정 할인 정보 게시글의 댓글 조회하기", description = "특정 할인 정보 게시글의 댓글을 가져오는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "discountInfoId", description = "댓글을 가져올 할인 정보 게시글 id입니다. parameter"), + @Parameter(name = "page", description = "페이징을 위한 페이지 번호입니다. 0부터 시작합니다. parameter"), + @Parameter(name = "size", description = "페이징을 위한 페이지 사이즈입니다. default는 20입니다. parameter") + }) + ResponseEntity> findAllByDiscountInfo( + Long discountInfoId, + Pageable pageable); + + @Operation(summary = "[User] 특정 지원 정보 게시글에 댓글달기", description = "특정 지원 정보 게시글에 댓글을 다는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "userId", description = "현재 댓글을 다는 유저 id입니다. parameter"), + @Parameter(name = "supportInfoId", description = "댓글을 다는 지원 정보 게시글 id입니다. requestBody"), + @Parameter(name = "content", description = "댓글 내용입니다. requestBody"), + }) + ResponseEntity saveSupportInfoComment( + Long userId, + CommentRequestDto.SupportInfoCommentDto supportInfoCommentDto); + + @Operation(summary = "[User] 특정 지원 정보 게시글의 댓글 조회하기", description = "특정 지원 정보 게시글의 댓글을 가져오는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "supportInfoId", description = "댓글을 가져올 지원 정보 게시글 id입니다. parameter"), + @Parameter(name = "page", description = "페이징을 위한 페이지 번호입니다. 0부터 시작합니다. parameter"), + @Parameter(name = "size", description = "페이징을 위한 페이지 사이즈입니다. default는 20입니다. parameter") + + + }) + ResponseEntity> findAllBySupportInfo( + Long supportInfoId, + Pageable pageable); + + @Operation(summary = "[User] 특정 댓글 삭제하기", description = "특정 댓글을 삭제하는 API입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + }) + @Parameters({ + @Parameter(name = "commentId", description = "삭제할 댓글 id 입니다. parameter") + }) + @GetMapping("/comments/delete") + ResponseEntity deleteComment(Long commentId); +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/CommentControllerImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/CommentControllerImpl.java new file mode 100644 index 00000000..8f9691d7 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/CommentControllerImpl.java @@ -0,0 +1,60 @@ +package com.bbteam.budgetbuddies.domain.comment.controller; + +import com.bbteam.budgetbuddies.domain.comment.dto.CommentRequestDto; +import com.bbteam.budgetbuddies.domain.comment.dto.CommentResponseDto; +import com.bbteam.budgetbuddies.domain.comment.service.CommentService; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +public class CommentControllerImpl implements CommentController{ + + private final CommentService commentService; + + @PostMapping("/discounts/comments") + public ResponseEntity saveDiscountInfoComment( + @RequestParam("userId") Long userId, + @RequestBody CommentRequestDto.DiscountInfoCommentDto discountInfoCommentDto){ + CommentResponseDto.DiscountInfoSuccessDto dto = commentService.saveDiscountComment(userId, discountInfoCommentDto); + return ResponseEntity.ok(dto); + } + + + @GetMapping("/discounts/comments") + public ResponseEntity> findAllByDiscountInfo( + @RequestParam("discountInfoId") Long discountInfoId, + @PageableDefault(size = 20, page = 0) Pageable pageable){ + Page result = commentService.findByDiscountInfoWithPaging(discountInfoId, pageable); + return ResponseEntity.ok(result); + } + + + @PostMapping("/supports/comments") + public ResponseEntity saveSupportInfoComment( + @RequestParam("userId") Long userId, + @RequestBody CommentRequestDto.SupportInfoCommentDto supportInfoCommentDto){ + CommentResponseDto.SupportInfoSuccessDto dto = commentService.saveSupportComment(userId, supportInfoCommentDto); + return ResponseEntity.ok(dto); + } + + + @GetMapping("/supports/comments") + public ResponseEntity> findAllBySupportInfo( + @RequestParam("supportInfoId") Long supportInfoId, + @PageableDefault(size = 20, page = 0)Pageable pageable){ + Page result = commentService.findBySupportInfoWithPaging(supportInfoId, pageable); + return ResponseEntity.ok(result); + } + + + public ResponseEntity deleteComment(@RequestParam("commentId") Long commentId) { + commentService.deleteComment(commentId); + return ResponseEntity.ok("ok"); + } + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/package-info.java deleted file mode 100644 index 6a816c5b..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/comment/controller/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.comment.controller; \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/converter/CommentConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/converter/CommentConverter.java new file mode 100644 index 00000000..626a1e6f --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/comment/converter/CommentConverter.java @@ -0,0 +1,72 @@ +package com.bbteam.budgetbuddies.domain.comment.converter; + +import com.bbteam.budgetbuddies.domain.comment.dto.CommentRequestDto; +import com.bbteam.budgetbuddies.domain.comment.dto.CommentResponseDto; +import com.bbteam.budgetbuddies.domain.comment.entity.Comment; +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import com.bbteam.budgetbuddies.domain.user.entity.User; + +public class CommentConverter { + + public static Comment toDiscountComment(CommentRequestDto.DiscountInfoCommentDto dto, User user, DiscountInfo discountInfo, + Integer anonymousNumber) { + return Comment.builder() + .user(user) + .discountInfo(discountInfo) + .content(dto.getContent()) + .anonymousNumber(anonymousNumber) + .build(); + } + + public static Comment toSupportComment(CommentRequestDto.SupportInfoCommentDto dto, User user, SupportInfo supportInfo, + Integer anonymousNumber) { + return Comment.builder() + .user(user) + .supportInfo(supportInfo) + .content(dto.getContent()) + .anonymousNumber(anonymousNumber) + .build(); + } + + public static CommentResponseDto.DiscountInfoCommentDto toDiscountInfoCommentDto(Comment comment){ + return CommentResponseDto.DiscountInfoCommentDto.builder() + .commentId(comment.getId()) + .discountInfoId(comment.getDiscountInfo().getId()) + .userId(comment.getUser().getId()) + .content(comment.getContent()) + .anonymousNumber(comment.getAnonymousNumber()) + .build(); + + } + + public static CommentResponseDto.SupportInfoCommentDto toSupportInfoCommentDto(Comment comment){ + return CommentResponseDto.SupportInfoCommentDto.builder() + .commentId(comment.getId()) + .supportInfoId(comment.getSupportInfo().getId()) + .userId(comment.getUser().getId()) + .content(comment.getContent()) + .anonymousNumber(comment.getAnonymousNumber()) + .build(); + + } + + public static CommentResponseDto.DiscountInfoSuccessDto toDiscountInfoSuccessDto(Comment comment){ + return CommentResponseDto.DiscountInfoSuccessDto.builder() + .commentId(comment.getId()) + .discountInfoId(comment.getDiscountInfo().getId()) + .userId(comment.getUser().getId()) + .content(comment.getContent()) + .build(); + } + + public static CommentResponseDto.SupportInfoSuccessDto toSupportInfoSuccessDto(Comment comment){ + return CommentResponseDto.SupportInfoSuccessDto.builder() + .commentId(comment.getId()) + .supportInfoId(comment.getSupportInfo().getId()) + .userId(comment.getUser().getId()) + .content(comment.getContent()) + .build(); + } + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/dto/CommentRequestDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/dto/CommentRequestDto.java new file mode 100644 index 00000000..45b91e8e --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/comment/dto/CommentRequestDto.java @@ -0,0 +1,22 @@ +package com.bbteam.budgetbuddies.domain.comment.dto; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + + +public class CommentRequestDto { + @Getter + @Builder + public static class DiscountInfoCommentDto { + private String content; + private Long discountInfoId; + } + + @Getter + @Builder + public static class SupportInfoCommentDto { + private String content; + private Long supportInfoId; + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/dto/CommentResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/dto/CommentResponseDto.java new file mode 100644 index 00000000..bfc27405 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/comment/dto/CommentResponseDto.java @@ -0,0 +1,47 @@ +package com.bbteam.budgetbuddies.domain.comment.dto; + + +import lombok.Builder; +import lombok.Getter; + +public class CommentResponseDto { + + @Getter + @Builder + public static class DiscountInfoCommentDto{ + private Long commentId; + private Long userId; + private Long discountInfoId; + private String content; + private Integer anonymousNumber; + } + + @Getter + @Builder + public static class SupportInfoCommentDto{ + private Long commentId; + private Long userId; + private Long supportInfoId; + private String content; + private Integer anonymousNumber; + } + + @Getter + @Builder + public static class DiscountInfoSuccessDto{ + private Long commentId; + private Long userId; + private Long discountInfoId; + private String content; + } + + @Getter + @Builder + public static class SupportInfoSuccessDto{ + private Long commentId; + private Long userId; + private Long supportInfoId; + private String content; + } + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/entity/Comment.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/entity/Comment.java index 43918096..49dbf6dd 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/comment/entity/Comment.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/comment/entity/Comment.java @@ -10,6 +10,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; +import org.hibernate.annotations.Where; +import org.springframework.lang.Nullable; @Entity @Getter @@ -21,16 +23,19 @@ public class Comment extends BaseEntity { @Column(nullable = false, length = 1000) private String content; - @ManyToOne(fetch = FetchType.EAGER) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; - @ManyToOne(fetch = FetchType.EAGER) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "discount_info_id") private DiscountInfo discountInfo; - @ManyToOne(fetch = FetchType.EAGER) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "support_info_id") private SupportInfo supportInfo; + @Column(nullable = false) + private Integer anonymousNumber; + } diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/repository/CommentRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/repository/CommentRepository.java new file mode 100644 index 00000000..b734eca7 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/comment/repository/CommentRepository.java @@ -0,0 +1,38 @@ +package com.bbteam.budgetbuddies.domain.comment.repository; + +import com.bbteam.budgetbuddies.domain.comment.entity.Comment; +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import 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; + +import java.util.List; +import java.util.Optional; + +public interface CommentRepository extends JpaRepository { + @Query("select c from Comment c where c.discountInfo.id = :discountInfoId" + + " order by c.createdAt asc") // 익명번호 부여용 + List findByDiscountInfo(@Param("discountInfoId")Long discountInfoId); + + @Query("select c from Comment c where c.discountInfo.id = :discountInfoId" + + " order by c.createdAt asc") + Page findByDiscountInfoWithPaging(@Param("discountInfoId")Long discountInfoId, + Pageable pageable); + + @Query("select c from Comment c where c.supportInfo.id = :supportInfoId" + + " order by c.createdAt asc") + List findBySupportInfo(@Param("supportInfoId")Long supportInfoId); + + @Query("select c from Comment c where c.supportInfo.id = :supportInfoId" + + " order by c.createdAt asc") + Page findBySupportInfoWithPaging(@Param("supportInfoId")Long supportInfoId, + Pageable pageable); + + Optional findTopByUserAndDiscountInfo(User user, DiscountInfo discountInfo); + Optional findTopByUserAndSupportInfo(User user, SupportInfo supportInfo); + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/repository/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/repository/package-info.java deleted file mode 100644 index 4d31a705..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/comment/repository/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.comment.repository; \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/service/CommentService.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/service/CommentService.java new file mode 100644 index 00000000..ac49bc5a --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/comment/service/CommentService.java @@ -0,0 +1,40 @@ +package com.bbteam.budgetbuddies.domain.comment.service; + +import com.bbteam.budgetbuddies.domain.comment.dto.CommentRequestDto; +import com.bbteam.budgetbuddies.domain.comment.dto.CommentResponseDto; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.List; + +public interface CommentService { + CommentResponseDto.SupportInfoSuccessDto saveSupportComment(Long userId, CommentRequestDto.SupportInfoCommentDto dto); + CommentResponseDto.DiscountInfoSuccessDto saveDiscountComment(Long userId, CommentRequestDto.DiscountInfoCommentDto dto); + + + /** + * + * @param discountInfoId + * @return List + * 해당 로직은 익명 구분을 위한 익명 구분 숫자도 같이 return 합니다. + */ + List findByDiscountInfo(Long discountInfoId); + + /** + * + * @param supportInfoId + * @return List + * 해당 로직은 익명 구분을 위한 익명 구분 숫자도 같이 return 합니다. + */ + List findBySupportInfo(Long supportInfoId); + + Page findByDiscountInfoWithPaging(Long discountInfoId, Pageable pageable); + Page findBySupportInfoWithPaging(Long supportInfoId, Pageable pageable); + + void deleteComment(Long commentId); + + + + + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/service/CommentServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/service/CommentServiceImpl.java new file mode 100644 index 00000000..df8063fa --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/comment/service/CommentServiceImpl.java @@ -0,0 +1,140 @@ +package com.bbteam.budgetbuddies.domain.comment.service; + + +import com.bbteam.budgetbuddies.domain.comment.converter.CommentConverter; +import com.bbteam.budgetbuddies.domain.comment.dto.CommentRequestDto; +import com.bbteam.budgetbuddies.domain.comment.dto.CommentResponseDto; +import com.bbteam.budgetbuddies.domain.comment.entity.Comment; +import com.bbteam.budgetbuddies.domain.comment.repository.CommentRepository; +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.discountinfo.repository.DiscountInfoRepository; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import com.bbteam.budgetbuddies.domain.supportinfo.repository.SupportInfoRepository; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashMap; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.stream.Collectors; + +// 임시로 유저는 service에서 찾아서 처리하는 로직으로 작성함 +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class CommentServiceImpl implements CommentService{ + + private final CommentRepository commentRepository; + private final UserRepository userRepository; + private final DiscountInfoRepository discountInfoRepository; + private final SupportInfoRepository supportInfoRepository; + + @Override + @Transactional + public CommentResponseDto.SupportInfoSuccessDto saveSupportComment(Long userId, CommentRequestDto.SupportInfoCommentDto dto) { + User user = userRepository.findById(userId).orElseThrow(() -> new NoSuchElementException("유저 존재 x")); + SupportInfo supportInfo = supportInfoRepository.findById(dto.getSupportInfoId()).orElseThrow(() -> new NoSuchElementException()); + int anonymousNumber = getSupportAnonymousNumber(user, supportInfo); + Comment comment = CommentConverter.toSupportComment(dto, user, supportInfo, anonymousNumber); + Comment savedComment = commentRepository.save(comment); + + return CommentConverter.toSupportInfoSuccessDto(savedComment); + } + + private int getSupportAnonymousNumber(User user, SupportInfo supportInfo) { + int anonymousNumber; + Optional foundComment = commentRepository.findTopByUserAndSupportInfo(user, supportInfo); + if(foundComment.isEmpty()){ + anonymousNumber = supportInfo.addAndGetAnonymousNumber(); + } else { + anonymousNumber = foundComment.get().getAnonymousNumber(); + } + return anonymousNumber; + } + + + @Override + @Transactional + public CommentResponseDto.DiscountInfoSuccessDto saveDiscountComment(Long userId, CommentRequestDto.DiscountInfoCommentDto dto) { + User user = userRepository.findById(userId).orElseThrow(() -> new NoSuchElementException("유저 존재 x")); + DiscountInfo discountInfo = discountInfoRepository.findById(dto.getDiscountInfoId()).orElseThrow(() -> new NoSuchElementException()); + int anonymousNumber = getDiscountAnonymousNumber(user, discountInfo); + Comment comment = CommentConverter.toDiscountComment(dto, user, discountInfo, anonymousNumber); + Comment savedComment = commentRepository.save(comment); + + return CommentConverter.toDiscountInfoSuccessDto(savedComment); + } + + private int getDiscountAnonymousNumber(User user, DiscountInfo discountInfo) { + int anonymousNumber; + Optional foundComment = commentRepository.findTopByUserAndDiscountInfo(user, discountInfo); + if(foundComment.isEmpty()){ + anonymousNumber = discountInfo.addAndGetAnonymousNumber(); + } else { + anonymousNumber = foundComment.get().getAnonymousNumber(); + } + return anonymousNumber; + } + + @Override + public List findByDiscountInfo(Long discountInfoId) { + List commentList = commentRepository.findByDiscountInfo(discountInfoId); + + HashMap anonymousMapping = countAnonymousNumber(commentList); + List collect = commentList.stream() + .map(CommentConverter::toDiscountInfoCommentDto) + .collect(Collectors.toList()); + return collect; + + } + + @Override + public List findBySupportInfo(Long supportInfoId) { + List commentList = commentRepository.findBySupportInfo(supportInfoId); + HashMap anonymousMapping = countAnonymousNumber(commentList); + List collect = commentList.stream() + .map(CommentConverter::toSupportInfoCommentDto) + .collect(Collectors.toList()); + return collect; + } + + private static HashMap countAnonymousNumber(List commentList) { + HashMap anonymousMapping = new HashMap<>(); + Long count = 1L; + for (Comment comment : commentList) { + Long id = comment.getUser().getId(); + if(!anonymousMapping.containsKey(id)){ + anonymousMapping.put(id, count); + count++; + } + } + return anonymousMapping; + } + + @Override + public Page findByDiscountInfoWithPaging(Long discountInfoId, Pageable pageable) { + Page commentPage = commentRepository.findByDiscountInfoWithPaging(discountInfoId, pageable); + Page result = commentPage.map(CommentConverter::toDiscountInfoCommentDto); + return result; + } + + @Override + public Page findBySupportInfoWithPaging(Long supportInfoId, Pageable pageable) { + Page commentPage = commentRepository.findBySupportInfoWithPaging(supportInfoId, pageable); + Page result = commentPage.map(CommentConverter::toSupportInfoCommentDto); + return result; + } + + @Override + @Transactional + public void deleteComment(Long commentId) { + Comment comment = commentRepository.findById(commentId).orElseThrow(() -> new NoSuchElementException("No such id")); + commentRepository.delete(comment); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/comment/service/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/comment/service/package-info.java deleted file mode 100644 index 4ffc9ed9..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/comment/service/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.comment.service; \ No newline at end of file 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 new file mode 100644 index 00000000..c13498b5 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalApi.java @@ -0,0 +1,43 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.controller; + +import java.time.LocalDate; + +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; + +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.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; + +public interface ConsumptionGoalApi { + @Operation(summary = "또래들이 가장 큰 계획을 세운 카테고리 조회 API", description = "특정 사용자의 소비 목표 카테고리별 소비 목표 금액을 조회하는 API 입니다.") + @ApiResponses(value = {@ApiResponse(responseCode = "COMMON200", description = "OK, 성공")}) + @Parameters({@Parameter(name = "top", description = "가장 큰 목표를 세운 카테고리의 개수를 지정합니다. (기본값은 5입니다)"), + @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, + int peerAgeStart, int peerAgeEnd, String peerGender); + + @Operation(summary = "소비 목표 조회 API", description = "date={yyyy-MM-dd} 형식의 query string을 통해서 사용자의 목표 달을 조회하는 API 입니다.") + @Parameters({@Parameter(name = "date", description = "yyyy-MM-dd 형식으로 목표 달의 소비를 조회")}) + ResponseEntity findUserConsumptionGoal(LocalDate date, Long userId); + + @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 getPeerInfo(Long userId, int peerAgeStart, int peerAgeEnd, String peerGender); + + @Operation(summary = "이번 달 소비 목표 수정 API", description = "다른 달의 소비 목표를 업데이트하는 것은 불가능하고 오직 이번 달의 소비 목표만 업데이트 하는 API 입니다.") + ResponseEntity updateOrElseGenerateConsumptionGoal(Long userId, + ConsumptionGoalListRequestDto consumptionGoalListRequestDto); +} 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 new file mode 100644 index 00000000..53c9330f --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/ConsumptionGoalController.java @@ -0,0 +1,67 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.controller; + +import java.time.LocalDate; +import java.util.List; + +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalListRequestDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDTO; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopGoalCategoryResponseDTO; +import com.bbteam.budgetbuddies.domain.consumptiongoal.service.ConsumptionGoalService; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/consumption-goal") +public class ConsumptionGoalController implements ConsumptionGoalApi { + + private final ConsumptionGoalService consumptionGoalService; + + @GetMapping("/top-categories") + public ResponseEntity getTopGoalCategories(@RequestParam(name = "top", defaultValue = "5") 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, + peerAgeStart, peerAgeEnd, peerGender); + return ResponseEntity.ok(topCategory); + } + + @GetMapping("/{userId}") + public ResponseEntity findUserConsumptionGoal( + @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date, @PathVariable Long userId) { + + ConsumptionGoalResponseListDto response = consumptionGoalService.findUserConsumptionGoal(userId, date); + + return ResponseEntity.ok(response); + } + + @GetMapping("/peer-info") + public ResponseEntity getPeerInfo(@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) { + PeerInfoResponseDTO response = consumptionGoalService.getPeerInfo(userId, peerAgeStart, peerAgeEnd, peerGender); + return ResponseEntity.ok(response); + } + + @PostMapping("/{userId}") + public ResponseEntity updateOrElseGenerateConsumptionGoal(@PathVariable Long userId, + @RequestBody ConsumptionGoalListRequestDto consumptionGoalListRequestDto) { + + return ResponseEntity.ok() + .body(consumptionGoalService.updateConsumptionGoals(userId, consumptionGoalListRequestDto)); + } +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/package-info.java deleted file mode 100644 index c4780977..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/controller/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.consumptiongoal.controller; \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java new file mode 100644 index 00000000..a2c835cc --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/ConsumptionGoalConverter.java @@ -0,0 +1,28 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.converter; + +import org.springframework.stereotype.Component; + +import com.bbteam.budgetbuddies.domain.category.entity.Category; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; + +@Component +public class ConsumptionGoalConverter { + public ConsumptionGoalResponseDto toConsumptionGoalResponseDto(Category category) { + return ConsumptionGoalResponseDto.builder() + .categoryName(category.getName()) + .categoryId(category.getId()) + .goalAmount(0L) + .consumeAmount(0L) + .build(); + } + + public ConsumptionGoalResponseDto toConsumptionGoalResponseDto(ConsumptionGoal consumptionGoal) { + return ConsumptionGoalResponseDto.builder() + .categoryName(consumptionGoal.getCategory().getName()) + .categoryId(consumptionGoal.getCategory().getId()) + .goalAmount(consumptionGoal.getGoalAmount()) + .consumeAmount(consumptionGoal.getConsumeAmount()) + .build(); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/PeerInfoConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/PeerInfoConverter.java new file mode 100644 index 00000000..d6c3eb24 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/PeerInfoConverter.java @@ -0,0 +1,16 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.converter; + +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDTO; +import com.bbteam.budgetbuddies.enums.Gender; + +public class PeerInfoConverter { + + public static PeerInfoResponseDTO fromEntity(int peerAgeStart, int peerAgeEnd, Gender peerGender) { + + return PeerInfoResponseDTO.builder() + .peerAgeStart(peerAgeStart) + .peerAgeEnd(peerAgeEnd) + .peerGender(peerGender.name()) + .build(); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/TopCategoryConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/TopCategoryConverter.java new file mode 100644 index 00000000..239dc8da --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/converter/TopCategoryConverter.java @@ -0,0 +1,21 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.converter; + +import org.springframework.stereotype.Component; + +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopGoalCategoryResponseDTO; +import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; + +@Component +public class TopCategoryConverter { + + public static TopGoalCategoryResponseDTO fromEntity(ConsumptionGoal consumptionGoal) { + if (consumptionGoal == null || consumptionGoal.getCategory() == null) { + return null; + } + + return TopGoalCategoryResponseDTO.builder() + .categoryName(consumptionGoal.getCategory().getName()) + .goalAmount(consumptionGoal.getGoalAmount()) + .build(); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionGoalListRequestDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionGoalListRequestDto.java new file mode 100644 index 00000000..a74c986f --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionGoalListRequestDto.java @@ -0,0 +1,15 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.dto; + +import java.util.List; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor +public class ConsumptionGoalListRequestDto { + List consumptionGoalList; +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionGoalRequestDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionGoalRequestDto.java new file mode 100644 index 00000000..da0463d7 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionGoalRequestDto.java @@ -0,0 +1,14 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.dto; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor +public class ConsumptionGoalRequestDto { + private Long categoryId; + private Long goalAmount; +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionGoalResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionGoalResponseDto.java new file mode 100644 index 00000000..bd8aca1b --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionGoalResponseDto.java @@ -0,0 +1,20 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.dto; + +import lombok.Builder; +import lombok.Getter; + +@Getter +public class ConsumptionGoalResponseDto { + private String categoryName; + private Long categoryId; + private Long goalAmount; + private Long consumeAmount; + + @Builder + public ConsumptionGoalResponseDto(String categoryName, Long categoryId, Long goalAmount, Long consumeAmount) { + this.categoryName = categoryName; + this.categoryId = categoryId; + this.goalAmount = goalAmount; + this.consumeAmount = consumeAmount; + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionGoalResponseListDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionGoalResponseListDto.java new file mode 100644 index 00000000..e7f5a54e --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/ConsumptionGoalResponseListDto.java @@ -0,0 +1,20 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.dto; + +import java.time.LocalDate; +import java.util.List; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ConsumptionGoalResponseListDto { + private LocalDate goalMonth; + private List consumptionGoalList; + + public ConsumptionGoalResponseListDto(LocalDate goalMonth, List consumptionGoalList) { + this.goalMonth = goalMonth; + this.consumptionGoalList = consumptionGoalList; + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/PeerInfoResponseDTO.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/PeerInfoResponseDTO.java new file mode 100644 index 00000000..3ac0d041 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/PeerInfoResponseDTO.java @@ -0,0 +1,19 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PeerInfoResponseDTO { + + private int peerAgeStart; + + private int peerAgeEnd; + + private String peerGender; +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/TopGoalCategoryResponseDTO.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/TopGoalCategoryResponseDTO.java new file mode 100644 index 00000000..99c394b4 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/TopGoalCategoryResponseDTO.java @@ -0,0 +1,18 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class TopGoalCategoryResponseDTO { + + private String categoryName; + + private Long goalAmount; + +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/package-info.java deleted file mode 100644 index 65ea60a1..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/dto/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.consumptiongoal.dto; \ 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 932a9bf4..a0df97bc 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 @@ -1,9 +1,16 @@ package com.bbteam.budgetbuddies.domain.consumptiongoal.entity; +import java.time.LocalDate; + import com.bbteam.budgetbuddies.common.BaseEntity; import com.bbteam.budgetbuddies.domain.category.entity.Category; import com.bbteam.budgetbuddies.domain.user.entity.User; -import jakarta.persistence.*; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import jakarta.validation.constraints.Min; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -11,8 +18,6 @@ import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; -import java.time.LocalDate; - @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -20,23 +25,26 @@ @SuperBuilder public class ConsumptionGoal extends BaseEntity { - @Column(nullable = false) - @Min(value = 1, message = "0 또는 음수의 목표금액을 설정할 수 없습니다.") - private Long goalAmount; + @Column(nullable = false) + @Min(value = 0, message = "음수의 목표금액을 설정할 수 없습니다.") + private Long goalAmount; - @Column(nullable = false) - @Min(value = 1, message = "0 또는 음수의 소비금액을 설정할 수 없습니다.") - private Long consumeAmount; + @Column(nullable = false) + @Min(value = 0, message = "음수의 소비금액을 설정할 수 없습니다.") + private Long consumeAmount; - @Column(nullable = false) - private LocalDate goalMonth; + @Column(nullable = false) + private LocalDate goalMonth; - @ManyToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "user_id") - private User user; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; - @ManyToOne(fetch = FetchType.EAGER) - @JoinColumn(name = "category_id") - private Category category; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "category_id") + private Category category; + public void updateGoalAmount(Long goalAmount) { + this.goalAmount = goalAmount; + } } 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 new file mode 100644 index 00000000..34220a0b --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepository.java @@ -0,0 +1,36 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.repository; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import com.bbteam.budgetbuddies.domain.category.entity.Category; +import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.enums.Gender; + +@Repository +public interface ConsumptionGoalRepository extends JpaRepository { + + @Query("SELECT cg FROM ConsumptionGoal cg " + + "WHERE cg.category.isDefault = true " + + "AND cg.user.age BETWEEN :peerAgeStart AND :peerAgeEnd " + + "AND cg.user.gender = :peerGender " + + "ORDER BY cg.goalAmount DESC limit :top") + List findTopCategoriesAndGoalAmount( + @Param("top") int top, + @Param("peerAgeStart") int peerAgeStart, + @Param("peerAgeEnd") int peerAgeEnd, + @Param("peerGender") Gender peerGender); + + @Query(value = "SELECT cg FROM ConsumptionGoal AS cg WHERE cg.user.id = :userId AND cg.goalMonth = :goalMonth") + List findConsumptionGoalByUserIdAndGoalMonth(Long userId, LocalDate goalMonth); + + Optional findConsumptionGoalByUserAndCategoryAndGoalMonth(User user, Category category, + LocalDate goalMonth); +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/package-info.java deleted file mode 100644 index ce8587a8..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.consumptiongoal.repository; \ 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 new file mode 100644 index 00000000..e647a0ec --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalService.java @@ -0,0 +1,25 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.service; + +import java.time.LocalDate; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalListRequestDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDTO; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.TopGoalCategoryResponseDTO; + +@Service +public interface ConsumptionGoalService { + + List getTopGoalCategories(int top, Long userId, int peerAgeStart, int peerAgeEnd, + String peerGender); + + ConsumptionGoalResponseListDto findUserConsumptionGoal(Long userId, LocalDate date); + + PeerInfoResponseDTO getPeerInfo(Long userId, int peerAgeStart, int peerAgeEnd, String peerGender); + + ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId, + ConsumptionGoalListRequestDto consumptionGoalListRequestDto); +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceImpl.java new file mode 100644 index 00000000..553bb514 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceImpl.java @@ -0,0 +1,191 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.service; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.bbteam.budgetbuddies.domain.category.entity.Category; +import com.bbteam.budgetbuddies.domain.category.repository.CategoryRepository; +import com.bbteam.budgetbuddies.domain.consumptiongoal.converter.ConsumptionGoalConverter; +import com.bbteam.budgetbuddies.domain.consumptiongoal.converter.PeerInfoConverter; +import com.bbteam.budgetbuddies.domain.consumptiongoal.converter.TopCategoryConverter; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalListRequestDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalRequestDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.PeerInfoResponseDTO; +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.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import com.bbteam.budgetbuddies.enums.Gender; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class ConsumptionGoalServiceImpl implements ConsumptionGoalService { + + private final ConsumptionGoalRepository consumptionGoalRepository; + private final CategoryRepository categoryRepository; + private final UserRepository userRepository; + + private final ConsumptionGoalConverter consumptionGoalConverter; + + private int peerAgeStart; + private int peerAgeEnd; + private Gender peerGender; + + @Override + @Transactional(readOnly = true) + public List getTopGoalCategories(int top, Long userId, int peerAgeS, int peerAgeE, + String peerG) { + + User user = findUserById(userId); + + checkPeerInfo(user, peerAgeS, peerAgeE, peerG); + + List topGoals = consumptionGoalRepository.findTopCategoriesAndGoalAmount(top, peerAgeStart, + peerAgeEnd, peerGender); + return topGoals.stream().map(TopCategoryConverter::fromEntity).collect(Collectors.toList()); + } + + @Override + @Transactional(readOnly = true) + public PeerInfoResponseDTO getPeerInfo(Long userId, int peerAgeS, int peerAgeE, String peerG) { + + User user = findUserById(userId); + + checkPeerInfo(user, peerAgeS, peerAgeE, peerG); + + return PeerInfoConverter.fromEntity(peerAgeStart, peerAgeEnd, peerGender); + } + + private User findUserById(Long userId) { + Optional user = userRepository.findById(userId); + + if (user.isEmpty()) { + throw new NoSuchElementException("유저를 찾을 수 없습니다."); + } + + return user.get(); + } + + private void checkPeerInfo(User user, int peerAgeS, int peerAgeE, String peerG) { + + Gender gender = Gender.valueOf(peerG.toUpperCase()); + + if (peerAgeS == 0 || peerAgeE == 0 || gender == Gender.NONE) { + peerGender = user.getGender(); + setAgeGroupByUser(user.getAge()); + } else { + peerAgeStart = peerAgeS; + peerAgeEnd = peerAgeE; + peerGender = gender; + } + } + + private void setAgeGroupByUser(int userAge) { + if (userAge >= 20 && userAge <= 22) { + peerAgeStart = 20; + peerAgeEnd = 22; + } else if (userAge >= 23 && userAge <= 25) { + peerAgeStart = 23; + peerAgeEnd = 25; + } else if (userAge >= 26 && userAge <= 28) { + peerAgeStart = 26; + peerAgeEnd = 28; + } else if (userAge >= 29) { + peerAgeStart = 29; + peerAgeEnd = 99; + } else { + peerAgeStart = 0; + peerAgeEnd = 19; + } + } + + @Override + @Transactional(readOnly = true) + public ConsumptionGoalResponseListDto findUserConsumptionGoal(Long userId, LocalDate date) { + LocalDate goalMonth = date.withDayOfMonth(1); + Map goalMap = initializeGoalMap(userId, goalMonth); + + updateGoalMapWithPreviousMonth(userId, goalMonth, goalMap); + updateGoalMapWithCurrentMonth(userId, goalMonth, goalMap); + + return new ConsumptionGoalResponseListDto(goalMonth, new ArrayList<>(goalMap.values())); + } + + private Map initializeGoalMap(Long userId, LocalDate goalMonth) { + return categoryRepository.findUserCategoryByUserId(userId) + .stream() + .collect(Collectors.toMap(Category::getId, + category -> consumptionGoalConverter.toConsumptionGoalResponseDto(category))); + } + + private void updateGoalMapWithPreviousMonth(Long userId, LocalDate goalMonth, + Map goalMap) { + updateGoalMap(userId, goalMonth.minusMonths(1), goalMap); + } + + private void updateGoalMapWithCurrentMonth(Long userId, LocalDate goalMonth, + 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)); + } + + @Override + @Transactional + public ConsumptionGoalResponseListDto updateConsumptionGoals(Long userId, + ConsumptionGoalListRequestDto consumptionGoalListRequestDto) { + LocalDate thisMonth = LocalDate.now().withDayOfMonth(1); + User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("Not found user")); + + List updatedConsumptionGoal = consumptionGoalListRequestDto.getConsumptionGoalList() + .stream() + .map(c -> updateConsumptionGoalWithRequestDto(user, c, thisMonth)) + .toList(); + + List response = consumptionGoalRepository.saveAll(updatedConsumptionGoal) + .stream() + .map(consumptionGoalConverter::toConsumptionGoalResponseDto) + .toList(); + + return new ConsumptionGoalResponseListDto(thisMonth, response); + } + + private ConsumptionGoal updateConsumptionGoalWithRequestDto(User user, + ConsumptionGoalRequestDto consumptionGoalRequestDto, LocalDate goalMonth) { + + Category category = categoryRepository.findById(consumptionGoalRequestDto.getCategoryId()) + .orElseThrow(() -> new IllegalArgumentException("Not found Category")); + + ConsumptionGoal consumptionGoal = findOrElseGenerateConsumptionGoal(user, category, goalMonth); + consumptionGoal.updateGoalAmount(consumptionGoalRequestDto.getGoalAmount()); + + return consumptionGoal; + } + + private ConsumptionGoal findOrElseGenerateConsumptionGoal(User user, Category category, LocalDate goalMonth) { + return consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, category, goalMonth) + .orElseGet(() -> generateConsumptionGoal(user, category, goalMonth)); + } + + private ConsumptionGoal generateConsumptionGoal(User user, Category category, LocalDate goalMonth) { + return ConsumptionGoal.builder().goalMonth(goalMonth).user(user).category(category).consumeAmount(0L).build(); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/package-info.java deleted file mode 100644 index 95d9a924..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.consumptiongoal.service; \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/entity/DiscountInfo.java b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/entity/DiscountInfo.java index 69908cd0..2beb881a 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/entity/DiscountInfo.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/discountinfo/entity/DiscountInfo.java @@ -42,4 +42,9 @@ public void subLikeCount() { this.likeCount--; } + public Integer addAndGetAnonymousNumber() { + this.anonymousNumber++; + return anonymousNumber; + } + } 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 e41ffe49..8aa49237 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 @@ -5,12 +5,22 @@ 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; import java.time.LocalDate; +import java.util.List; public interface DiscountInfoRepository extends JpaRepository { @Query("SELECT i FROM DiscountInfo i WHERE (i.startDate <= :endDate AND i.endDate >= :startDate)") Page 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)") + 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") + List findRecommendInfoByMonth(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate); + } 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 new file mode 100644 index 00000000..159c6fc9 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/ExpenseController.java @@ -0,0 +1,37 @@ +package com.bbteam.budgetbuddies.domain.expense.controller; + +import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; +import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseResponseDto; +import com.bbteam.budgetbuddies.domain.expense.service.ExpenseService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/expenses") +public class ExpenseController { + + private final ExpenseService expenseService; + + @Operation(summary = "소비 내역 추가", description = "사용자가 소비 내역을 추가합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + @PostMapping("/add") + public ResponseEntity createExpense( + @Parameter(description = "user_id, category_id, amount, description, expenseDate") + @RequestBody ExpenseRequestDto expenseRequestDto) { + ExpenseResponseDto response = expenseService.createExpense(expenseRequestDto); + return ResponseEntity.ok(response); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/package-info.java deleted file mode 100644 index cd648f5a..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/controller/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.expense.controller; \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/converter/ExpenseConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/converter/ExpenseConverter.java new file mode 100644 index 00000000..ac88217c --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/converter/ExpenseConverter.java @@ -0,0 +1,34 @@ +package com.bbteam.budgetbuddies.domain.expense.converter; + +import com.bbteam.budgetbuddies.domain.category.entity.Category; +import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; +import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseResponseDto; +import com.bbteam.budgetbuddies.domain.expense.entity.Expense; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import org.springframework.stereotype.Component; + +@Component +public class ExpenseConverter { + + public Expense toExpenseEntity(ExpenseRequestDto expenseRequestDto, User user, Category category) { + return Expense.builder() + .user(user) + .category(category) + .amount(expenseRequestDto.getAmount()) + .description(expenseRequestDto.getDescription()) + .expenseDate(expenseRequestDto.getExpenseDate()) + .build(); + } + + public ExpenseResponseDto toExpenseResponseDto(Expense expense) { + return ExpenseResponseDto.builder() + .expenseId(expense.getId()) + .userId(expense.getUser().getId()) + .categoryId(expense.getCategory().getId()) + .amount(expense.getAmount()) + .description(expense.getDescription()) + .expenseDate(expense.getExpenseDate()) + .build(); + } +} + diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseRequestDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseRequestDto.java new file mode 100644 index 00000000..680be344 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseRequestDto.java @@ -0,0 +1,20 @@ +package com.bbteam.budgetbuddies.domain.expense.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ExpenseRequestDto { + private Long userId; + private Long categoryId; + private Long amount; + private String description; + private LocalDateTime expenseDate; +} \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseResponseDto.java new file mode 100644 index 00000000..79b74cf6 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/ExpenseResponseDto.java @@ -0,0 +1,21 @@ +package com.bbteam.budgetbuddies.domain.expense.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ExpenseResponseDto { + private Long expenseId; + private Long userId; + private Long categoryId; + private Long amount; + private String description; + private LocalDateTime expenseDate; +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/package-info.java deleted file mode 100644 index cd54ca0d..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/dto/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.expense.dto; \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepository.java new file mode 100644 index 00000000..c846f92e --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/repository/ExpenseRepository.java @@ -0,0 +1,18 @@ +package com.bbteam.budgetbuddies.domain.expense.repository; + +import com.bbteam.budgetbuddies.domain.expense.entity.Expense; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface ExpenseRepository extends JpaRepository { + + // 추후 적용 예정 + @Query("SELECT e FROM Expense e WHERE e.user.id = :userId AND e.category.id = :categoryId") + List findByUserIdAndCategoryId(@Param("userId") Long userId, @Param("categoryId") Long categoryId); + + @Query("SELECT e FROM Expense e WHERE e.user.id = :userId") + List findByUserId(@Param("userId") Long userId); +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/repository/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/repository/package-info.java deleted file mode 100644 index c5184ef7..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/repository/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.expense.repository; \ No newline at end of file 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 new file mode 100644 index 00000000..cbcddb72 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseService.java @@ -0,0 +1,11 @@ +package com.bbteam.budgetbuddies.domain.expense.service; + +import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; +import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseResponseDto; +import com.bbteam.budgetbuddies.domain.category.dto.CategoryResponseDTO; + +import java.util.List; + +public interface ExpenseService { + ExpenseResponseDto createExpense(ExpenseRequestDto expenseRequestDto); +} 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 new file mode 100644 index 00000000..b4675ebe --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/ExpenseServiceImpl.java @@ -0,0 +1,36 @@ +package com.bbteam.budgetbuddies.domain.expense.service; + +import com.bbteam.budgetbuddies.domain.category.entity.Category; +import com.bbteam.budgetbuddies.domain.category.repository.CategoryRepository; +import com.bbteam.budgetbuddies.domain.expense.converter.ExpenseConverter; +import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseRequestDto; +import com.bbteam.budgetbuddies.domain.expense.dto.ExpenseResponseDto; +import com.bbteam.budgetbuddies.domain.expense.entity.Expense; +import com.bbteam.budgetbuddies.domain.expense.repository.ExpenseRepository; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ExpenseServiceImpl implements ExpenseService { + + private final ExpenseRepository expenseRepository; + private final UserRepository userRepository; + private final CategoryRepository categoryRepository; + private final ExpenseConverter expenseConverter; + + @Override + public ExpenseResponseDto createExpense(ExpenseRequestDto expenseRequestDto) { + User user = userRepository.findById(expenseRequestDto.getUserId()) + .orElseThrow(() -> new IllegalArgumentException("Invalid user ID")); + Category category = categoryRepository.findById(expenseRequestDto.getCategoryId()) + .orElseThrow(() -> new IllegalArgumentException("Invalid category ID")); + + Expense expense = expenseConverter.toExpenseEntity(expenseRequestDto, user, category); + expenseRepository.save(expense); + + return expenseConverter.toExpenseResponseDto(expense); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/package-info.java deleted file mode 100644 index ece79aae..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/expense/service/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.expense.service; \ No newline at end of file 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 new file mode 100644 index 00000000..059ae9b6 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/SupportInfoController.java @@ -0,0 +1,86 @@ +package com.bbteam.budgetbuddies.domain.supportinfo.controller; + +import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountRequestDto; +import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; +import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequestDto; +import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; +import com.bbteam.budgetbuddies.domain.supportinfo.service.SupportInfoService; +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 lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/supports") +public class SupportInfoController { + + private final SupportInfoService supportInfoService; + + @Operation(summary = "[User] 특정 년월 지원정보 리스트 가져오기 API", description = "특정 년도와 월에 해당하는 지원정보 목록을 조회하는 API이며, 페이징을 포함합니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + @Parameters({ + @Parameter(name = "year", description = "데이터를 가져올 연도입니다."), + @Parameter(name = "month", description = "데이터를 가져올 월입니다."), + @Parameter(name = "page", description = "페이지 번호, 0번이 1 페이지 입니다. (기본값은 0입니다.)"), + @Parameter(name = "size", description = "한 페이지에 불러올 데이터 개수입니다. (기본값은 10개입니다.)") + }) + @GetMapping("") + public ResponseEntity> getSupportsByYearAndMonth( + @RequestParam Integer year, + @RequestParam Integer month, + @RequestParam(defaultValue = "0") Integer page, + @RequestParam(defaultValue = "10") Integer size + ) { + Page supports = supportInfoService.getSupportsByYearAndMonth(year, month, page, size); + + return ResponseEntity.ok(supports); + } + + @Operation(summary = "[ADMIN] 지원정보 등록하기 API", description = "지원정보를 등록하는 API이며, 추후에는 관리자만 접근 가능하도록 할 예정입니다.") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + @PostMapping("") + public ResponseEntity registerDiscountInfo( + @RequestBody SupportRequestDto requestDto + ) { + SupportResponseDto supportResponseDto = supportInfoService.registerSupportInfo(requestDto); + + return ResponseEntity.ok(supportResponseDto); + } + + @Operation(summary = "[User] 특정 지원정보에 좋아요 클릭 API", description = "특정 지원정보에 좋아요 버튼을 클릭하는 API이며, 일단은 사용자 ID를 입력하여 사용합니다. (추후 토큰으로 검증)") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200", description = "OK, 성공"), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "access 토큰 만료", content = @Content(schema = @Schema(implementation = ApiResponse.class))), +// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "access 토큰 모양이 이상함", content = @Content(schema = @Schema(implementation = ApiResponse.class))) + }) + @Parameters({ + @Parameter(name = "userId", description = "좋아요를 누른 사용자의 id입니다."), + @Parameter(name = "supportInfoId", description = "좋아요를 누를 지원정보의 id입니다."), + }) + @PostMapping("/{supportInfoId}/likes") + public ResponseEntity likeDiscountInfo( + @RequestParam Long userId, + @PathVariable Long supportInfoId + ) { + SupportResponseDto supportResponseDto = supportInfoService.toggleLike(userId, supportInfoId); + + return ResponseEntity.ok(supportResponseDto); + } + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/package-info.java deleted file mode 100644 index a4bccaf4..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/controller/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.supportinfo.controller; \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/converter/SupportInfoConverter.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/converter/SupportInfoConverter.java new file mode 100644 index 00000000..8e9bd62f --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/converter/SupportInfoConverter.java @@ -0,0 +1,46 @@ +package com.bbteam.budgetbuddies.domain.supportinfo.converter; + +import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountRequestDto; +import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequestDto; +import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import org.springframework.stereotype.Service; + +@Service +public class SupportInfoConverter { + /** + * @param entity + * @return responseDto + */ + public SupportResponseDto toDto(SupportInfo entity) { + + return SupportResponseDto.builder() + .id(entity.getId()) + .title(entity.getTitle()) + .startDate(entity.getStartDate()) + .endDate(entity.getEndDate()) + .anonymousNumber(entity.getAnonymousNumber()) + .likeCount(entity.getLikeCount()) + .siteUrl(entity.getSiteUrl()) + .build(); + } + + /** + * + * @param requestDto + * @return entity + */ + public SupportInfo toEntity(SupportRequestDto requestDto) { + + return SupportInfo.builder() + .title(requestDto.getTitle()) + .startDate(requestDto.getStartDate()) + .endDate(requestDto.getEndDate()) + .anonymousNumber(0) + .likeCount(0) + .siteUrl(requestDto.getSiteUrl()) + .build(); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/SupportRequestDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/SupportRequestDto.java new file mode 100644 index 00000000..ff883f7d --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/SupportRequestDto.java @@ -0,0 +1,24 @@ +package com.bbteam.budgetbuddies.domain.supportinfo.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SupportRequestDto { + + private String title; + + private LocalDate startDate; + + private LocalDate endDate; + + private String siteUrl; + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/SupportResponseDto.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/SupportResponseDto.java new file mode 100644 index 00000000..b04825f7 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/SupportResponseDto.java @@ -0,0 +1,30 @@ +package com.bbteam.budgetbuddies.domain.supportinfo.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SupportResponseDto { + + private Long id; + + private String title; + + private LocalDate startDate; + + private LocalDate endDate; + + private Integer anonymousNumber; + + private Integer likeCount; + + private String siteUrl; + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/package-info.java deleted file mode 100644 index 7137c5b3..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/dto/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.supportinfo.dto; \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/entity/SupportInfo.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/entity/SupportInfo.java index 78ffa7ca..cc83973e 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/entity/SupportInfo.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/entity/SupportInfo.java @@ -27,9 +27,25 @@ public class SupportInfo extends BaseEntity { private LocalDate endDate; @ColumnDefault("0") - private Integer likeCount; + private Integer likeCount = 0; + + @ColumnDefault("0") + private Integer anonymousNumber = 0; @Column(length = 1000) private String siteUrl; + public void addLikeCount() { + this.likeCount++; + } + + public void subLikeCount() { + this.likeCount--; + } + + public Integer addAndGetAnonymousNumber() { + this.anonymousNumber++; + return anonymousNumber; + } + } 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 new file mode 100644 index 00000000..4d6ba4b1 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/repository/SupportInfoRepository.java @@ -0,0 +1,27 @@ +package com.bbteam.budgetbuddies.domain.supportinfo.repository; + +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +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; + +import java.time.LocalDate; +import java.util.List; + +public interface SupportInfoRepository extends JpaRepository { + + @Query("SELECT i FROM SupportInfo i WHERE (i.startDate <= :endDate AND i.endDate >= :startDate)") + 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)") + 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)" + + " 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/supportinfo/repository/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/repository/package-info.java deleted file mode 100644 index 8a76d616..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/repository/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.supportinfo.repository; \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoService.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoService.java new file mode 100644 index 00000000..bc01ebd9 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoService.java @@ -0,0 +1,20 @@ +package com.bbteam.budgetbuddies.domain.supportinfo.service; + +import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountRequestDto; +import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; +import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequestDto; +import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; +import org.springframework.data.domain.Page; + +public interface SupportInfoService { + Page getSupportsByYearAndMonth( + Integer year, + Integer month, + Integer page, + Integer size + ); + + SupportResponseDto registerSupportInfo(SupportRequestDto supportRequestDto); + + SupportResponseDto toggleLike(Long userId, Long supportInfoId); +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceImpl.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceImpl.java new file mode 100644 index 00000000..6f5568f9 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/SupportInfoServiceImpl.java @@ -0,0 +1,116 @@ +package com.bbteam.budgetbuddies.domain.supportinfo.service; + +import com.bbteam.budgetbuddies.domain.discountinfo.converter.DiscountInfoConverter; +import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountRequestDto; +import com.bbteam.budgetbuddies.domain.discountinfo.dto.DiscountResponseDto; +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.discountinfo.repository.DiscountInfoRepository; +import com.bbteam.budgetbuddies.domain.discountinfolike.entity.DiscountInfoLike; +import com.bbteam.budgetbuddies.domain.discountinfolike.repository.DiscountInfoLikeRepository; +import com.bbteam.budgetbuddies.domain.supportinfo.converter.SupportInfoConverter; +import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportRequestDto; +import com.bbteam.budgetbuddies.domain.supportinfo.dto.SupportResponseDto; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import com.bbteam.budgetbuddies.domain.supportinfo.repository.SupportInfoRepository; +import com.bbteam.budgetbuddies.domain.supportinfolike.entity.SupportInfoLike; +import com.bbteam.budgetbuddies.domain.supportinfolike.repository.SupportInfoLikeRepository; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class SupportInfoServiceImpl implements SupportInfoService { + + private final SupportInfoRepository supportInfoRepository; + + private final SupportInfoLikeRepository supportInfoLikeRepository; + + private final SupportInfoConverter supportInfoConverter; + + private final UserRepository userRepository; + + + @Transactional(readOnly = true) + @Override + public Page getSupportsByYearAndMonth(Integer year, Integer month, Integer page, Integer size) { + /** + * 1. Pageable 객체 생성 (사용자로부터 입력받은 page 번호와 size) + * 2. 사용자가 요청한 년월을 기준으로 해당 년월의 1일로 LocalDate 객체 생성 + * 3. 해당 년월에 겹치는 할인정보 데이터 가져오기 + * 4. Entity 리스트를 -> Dto로 모두 변환하여 리턴 + */ + Pageable pageable = PageRequest.of(page, size); + + LocalDate startDate = LocalDate.of(year, month, 1); + LocalDate endDate = startDate.withDayOfMonth(startDate.lengthOfMonth()); + + Page supportInfoPage = supportInfoRepository.findByDateRange(startDate, endDate, pageable); + + return supportInfoPage.map(supportInfoConverter::toDto); + } + + @Transactional + @Override + public SupportResponseDto registerSupportInfo(SupportRequestDto supportRequestDto) { + /** + * 1. RequestDto -> Entity로 변환 + * 2. Entity 저장 + * 3. Entity -> ResponseDto로 변환 후 리턴 + */ + SupportInfo entity = supportInfoConverter.toEntity(supportRequestDto); + + supportInfoRepository.save(entity); + + return supportInfoConverter.toDto(entity); + } + + @Transactional + @Override + public SupportResponseDto toggleLike(Long userId, Long supportInfoId) { + /** + * 1. 사용자 조회 -> 없으면 에러 + * 2. 할인정보 조회 -> 없으면 에러 + * 3. 사용자가 특정 할인정보에 좋아요를 눌렀는지 확인 (SupportInfoLike 테이블에서 userId로부터 데이터 가져오기) + * 4. 누르지 않은 상태라면, + * 4-1. SupportInfo의 likeCount 1 증가 + * 4-2. SupportInfoLike의 + * 5. 이미 누른 상태라면, + * 5-1. SupportInfo의 likeCount 1 감소 + */ + + User user = userRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("User not found")); + + SupportInfo supportInfo = supportInfoRepository.findById(supportInfoId) + .orElseThrow(() -> new IllegalArgumentException("SupportInfo not found")); + + Optional existingLike = supportInfoLikeRepository.findByUserAndSupportInfo(user, supportInfo); + + if (existingLike.isPresent()) { + // 이미 좋아요를 누른 상태라면 + supportInfoLikeRepository.delete(existingLike.get()); + supportInfo.subLikeCount(); + } else { + // 아직 좋아요를 누르지 않은 상태라면 + SupportInfoLike newLike = SupportInfoLike.builder() + .user(user) + .supportInfo(supportInfo) + .build(); + supportInfoLikeRepository.save(newLike); + supportInfo.addLikeCount(); + } + + SupportInfo savedEntity = supportInfoRepository.save(supportInfo); + + return supportInfoConverter.toDto(savedEntity); + } +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/package-info.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/package-info.java deleted file mode 100644 index ceaba27e..00000000 --- a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfo/service/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.bbteam.budgetbuddies.domain.supportinfo.service; \ No newline at end of file diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/supportinfolike/repository/SupportInfoLikeRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfolike/repository/SupportInfoLikeRepository.java new file mode 100644 index 00000000..b2618344 --- /dev/null +++ b/src/main/java/com/bbteam/budgetbuddies/domain/supportinfolike/repository/SupportInfoLikeRepository.java @@ -0,0 +1,16 @@ +package com.bbteam.budgetbuddies.domain.supportinfolike.repository; + +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.discountinfolike.entity.DiscountInfoLike; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import com.bbteam.budgetbuddies.domain.supportinfolike.entity.SupportInfoLike; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface SupportInfoLikeRepository extends JpaRepository { + + Optional findByUserAndSupportInfo(User user, SupportInfo supportInfo); + +} diff --git a/src/main/java/com/bbteam/budgetbuddies/domain/user/repository/UserRepository.java b/src/main/java/com/bbteam/budgetbuddies/domain/user/repository/UserRepository.java index 3495835a..f932a14e 100644 --- a/src/main/java/com/bbteam/budgetbuddies/domain/user/repository/UserRepository.java +++ b/src/main/java/com/bbteam/budgetbuddies/domain/user/repository/UserRepository.java @@ -1,8 +1,13 @@ package com.bbteam.budgetbuddies.domain.user.repository; -import com.bbteam.budgetbuddies.domain.user.entity.User; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; +import com.bbteam.budgetbuddies.domain.user.entity.User; + public interface UserRepository extends JpaRepository { + Optional findById(Long userId); + } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml deleted file mode 100644 index d7740288..00000000 --- a/src/main/resources/application.yaml +++ /dev/null @@ -1,17 +0,0 @@ -spring: - application: - name: budgetbuddies - - jpa: - show-sql: true # sql ???? ??? ???? ??. - properties: - format_sql: true - dialect: org.hibernate.dialect.MySQL8Dialect - hibernate: - ddl-auto: create - - datasource: - url: jdbc:mysql://localhost:3306/budgetbuddies # mysql ?? ?? (?? ?? DB? ?? ? ??) - driver-class-name: com.mysql.cj.jdbc.Driver - username: root - password: root # db ???? \ No newline at end of file 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 new file mode 100644 index 00000000..ded0e299 --- /dev/null +++ b/src/test/java/com/bbteam/budgetbuddies/domain/calendar/service/CalendarServiceTest.java @@ -0,0 +1,202 @@ +package com.bbteam.budgetbuddies.domain.calendar.service; + +import com.bbteam.budgetbuddies.domain.calendar.converter.CalendarConverter; +import com.bbteam.budgetbuddies.domain.calendar.dto.CalendarDto; +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.discountinfo.repository.DiscountInfoRepository; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import com.bbteam.budgetbuddies.domain.supportinfo.repository.SupportInfoRepository; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@Transactional +class CalendarServiceTest { + + @Autowired + CalendarService calendarService; + @Autowired + DiscountInfoRepository discountInfoRepository; + @Autowired + SupportInfoRepository supportInfoRepository; + + @Test + void findTest(){ + LocalDate discountStart1 = LocalDate.of(2024, 8, 1); + LocalDate discountStart2 = LocalDate.of(2024, 7, 20); + LocalDate discountStart3 = LocalDate.of(2024, 8, 31); + LocalDate discountStart4 = LocalDate.of(2024, 7, 20); + LocalDate discountStart5 = LocalDate.of(2024, 9, 1); + + LocalDate discountEnd1 = LocalDate.of(2024, 8, 5); + LocalDate discountEnd2 = LocalDate.of(2024, 8, 3); + LocalDate discountEnd3 = LocalDate.of(2024, 9, 3); + LocalDate discountEnd4 = LocalDate.of(2024, 7, 24); + LocalDate discountEnd5 = LocalDate.of(2024, 9, 5); + + DiscountInfo discountInfo1 = DiscountInfo.builder() + .startDate(discountStart1) + .endDate(discountEnd1) + .title("test1") + .likeCount(0) + .build(); + + DiscountInfo discountInfo2 = DiscountInfo.builder() + .startDate(discountStart2) + .endDate(discountEnd2) + .title("test2") + .likeCount(0) + .build(); + + DiscountInfo discountInfo3 = DiscountInfo.builder() + .startDate(discountStart3) + .endDate(discountEnd3) + .title("test3") + .likeCount(0) + .build(); + + DiscountInfo discountInfo4 = DiscountInfo.builder() + .startDate(discountStart4) + .endDate(discountEnd4) + .title("test4") + .likeCount(0) + .build(); + + DiscountInfo discountInfo5 = DiscountInfo.builder() + .startDate(discountStart5) + .endDate(discountEnd5) + .title("test5") + .likeCount(0) + .build(); + + discountInfoRepository.save(discountInfo1); + discountInfoRepository.save(discountInfo2); + discountInfoRepository.save(discountInfo3); + discountInfoRepository.save(discountInfo4); + discountInfoRepository.save(discountInfo5); + + for(int i = 0; i < 3; i++){ + discountInfo1.addLikeCount(); + } + for(int i = 0; i < 4; i++){ + discountInfo2.addLikeCount(); + } + for(int i = 0; i < 5; i++){ + discountInfo3.addLikeCount(); + } + for(int i = 0; i < 6; i++){ + discountInfo4.addLikeCount(); + } + for(int i = 0; i < 7; i++){ + discountInfo5.addLikeCount(); + } + + LocalDate firstDay = LocalDate.of(2024, 8, 1); + LocalDate lastDay = firstDay.withDayOfMonth(firstDay.lengthOfMonth()); + + LocalDate supportStart1 = LocalDate.of(2024, 8, 1); + LocalDate supportStart2 = LocalDate.of(2024, 7, 20); + LocalDate supportStart3 = LocalDate.of(2024, 8, 31); + LocalDate supportStart4 = LocalDate.of(2024, 7, 20); + LocalDate supportStart5 = LocalDate.of(2024, 9, 1); + + LocalDate supportEnd1 = LocalDate.of(2024, 8, 5); + LocalDate supportEnd2 = LocalDate.of(2024, 7, 23); + LocalDate supportEnd3 = LocalDate.of(2024, 9, 3); + LocalDate supportEnd4 = LocalDate.of(2024, 7, 24); + LocalDate supportEnd5 = LocalDate.of(2024, 9, 5); + + SupportInfo supportInfo1 = SupportInfo.builder() + .startDate(supportStart1) + .endDate(supportEnd1) + .title("test1") + .likeCount(0) + .build(); + + SupportInfo supportInfo2 = SupportInfo.builder() + .startDate(supportStart2) + .endDate(supportEnd2) + .title("test2") + .likeCount(0) + .build(); + + SupportInfo supportInfo3 = SupportInfo.builder() + .startDate(supportStart3) + .endDate(supportEnd3) + .title("test3") + .likeCount(0) + .build(); + + SupportInfo supportInfo4 = SupportInfo.builder() + .startDate(supportStart4) + .endDate(supportEnd4) + .title("test4") + .likeCount(0) + .build(); + + SupportInfo supportInfo5 = SupportInfo.builder() + .startDate(supportStart5) + .endDate(supportEnd5) + .title("test5") + .likeCount(0) + .build(); + + supportInfoRepository.save(supportInfo1); + supportInfoRepository.save(supportInfo2); + supportInfoRepository.save(supportInfo3); + supportInfoRepository.save(supportInfo4); + supportInfoRepository.save(supportInfo5); + + for(int i = 0; i < 10; i++){ + supportInfo1.addLikeCount(); + } + for(int i = 0; i < 9; i++){ + supportInfo2.addLikeCount(); + } + for(int i = 0; i < 8; i++){ + supportInfo3.addLikeCount(); + } + for(int i = 0; i < 11; i++){ + supportInfo4.addLikeCount(); + } + for(int i = 0; i < 12; i++){ + supportInfo5.addLikeCount(); + } + + CalendarDto.CalendarMonthResponseDto result = calendarService.find(2024, 8); + 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); + assertThat(discountInfoDtoList).containsExactlyInAnyOrder(discountDto1, discountDto2, discountDto3); + + assertThat(supportInfoDtoList.size()).isEqualTo(2); + CalendarDto.CalendarSupportInfoDto supportDto1 = CalendarConverter.toCalendarSupportInfoDto(supportInfo1); + CalendarDto.CalendarSupportInfoDto supportDto3 = CalendarConverter.toCalendarSupportInfoDto(supportInfo3); + assertThat(supportInfoDtoList).containsExactlyInAnyOrder(supportDto1, supportDto3); + + List recDiscountDtoList = result.getRecommendMonthInfoDto().getDiscountInfoDtoList(); + List recSupportDtoList = result.getRecommendMonthInfoDto().getSupportInfoDtoList(); + + assertThat(recDiscountDtoList.size()).isEqualTo(2); + assertThat(recDiscountDtoList).containsExactly(discountDto3, discountDto2); + + assertThat(recSupportDtoList.size()).isEqualTo(2); + assertThat(recSupportDtoList).containsExactly(supportDto1, supportDto3); + + + } + +} \ No newline at end of file diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/category/repository/CategoryRepositoryTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/category/repository/CategoryRepositoryTest.java new file mode 100644 index 00000000..f87f8a95 --- /dev/null +++ b/src/test/java/com/bbteam/budgetbuddies/domain/category/repository/CategoryRepositoryTest.java @@ -0,0 +1,67 @@ +package com.bbteam.budgetbuddies.domain.category.repository; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +import com.bbteam.budgetbuddies.domain.category.entity.Category; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; + +@DisplayName("Category 레포지토리 테스트의 ") +@DataJpaTest +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +class CategoryRepositoryTest { + @Autowired + UserRepository userRepository; + @Autowired + CategoryRepository categoryRepository; + + @Test + @DisplayName("Custom 카테고리와 Base 카테고리 조회 성공") + void findUserCategoryByUserIdTest_Success() { + User user = userRepository.save(User.builder() + .email("email") + .age(24) + .name("name") + .phoneNumber("010-1234-5678") + .build()); + + User otherUser = userRepository.save(User.builder() + .email("email2") + .age(25) + .name("name2") + .phoneNumber("010-2345-5678") + .build()); + + Category defaultCategory = categoryRepository.save(Category.builder() + .name("디폴트 카테고리") + .user(null) + .isDefault(true) + .build()); + + Category userCategory = categoryRepository.save(Category.builder() + .name("유저 카테고리") + .user(user) + .isDefault(false) + .build()); + + categoryRepository.save(Category.builder() + .name("다른 유저 카테고리") + .user(otherUser) + .isDefault(false) + .build()); + + // when + List result = categoryRepository.findUserCategoryByUserId(user.getId()); + + // then + assertThat(result).usingRecursiveComparison().isEqualTo(List.of(defaultCategory, userCategory)); + } +} \ No newline at end of file diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/comment/repository/CommentRepositoryTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/comment/repository/CommentRepositoryTest.java new file mode 100644 index 00000000..4aa430a7 --- /dev/null +++ b/src/test/java/com/bbteam/budgetbuddies/domain/comment/repository/CommentRepositoryTest.java @@ -0,0 +1,253 @@ +package com.bbteam.budgetbuddies.domain.comment.repository; + +import com.bbteam.budgetbuddies.domain.comment.entity.Comment; +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.discountinfo.repository.DiscountInfoRepository; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import com.bbteam.budgetbuddies.domain.supportinfo.repository.SupportInfoRepository; +import jakarta.persistence.EntityManager; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + + +@SpringBootTest +@Transactional +class CommentRepositoryTest { + + @Autowired + CommentRepository commentRepository; + + @Autowired + SupportInfoRepository supportInfoRepository; + + @Autowired + DiscountInfoRepository discountInfoRepository; + + @Autowired + EntityManager em; + + + @Test + public void saveTest(){ + Comment comment1 = Comment.builder().content("test1").build(); + Comment comment2 = Comment.builder().content("test2").build(); + + commentRepository.save(comment1); + commentRepository.save(comment2); + + em.flush(); + + Comment found1 = commentRepository.findById(comment1.getId()).get(); + Comment found2 = commentRepository.findById(comment2.getId()).get(); + + + Assertions.assertThat(comment1).isEqualTo(found1); + Assertions.assertThat(comment2).isEqualTo(found2); + } + + @Test + public void findByDiscountInfoTest(){ + DiscountInfo sale1 = DiscountInfo.builder().title("무신사 할인").build(); + DiscountInfo sale2 = DiscountInfo.builder().title("옥션 할인").build(); + + discountInfoRepository.save(sale1); + discountInfoRepository.save(sale2); + Comment comment1 = Comment.builder().content("test1") + .discountInfo(sale1) + .build(); + Comment comment2 = Comment.builder().content("test2") + .discountInfo(sale2) + .build(); + Comment comment3 = Comment.builder().content("test3") + .discountInfo(sale1) + .build(); + Comment comment4 = Comment.builder().content("test4") + .discountInfo(sale1) + .build(); + Comment comment5 = Comment.builder().content("test5") + .discountInfo(sale2) + .build(); + + commentRepository.save(comment1); + commentRepository.save(comment2); + commentRepository.save(comment3); + commentRepository.save(comment4); + commentRepository.save(comment5); + + + em.flush(); + + List found1 = commentRepository.findByDiscountInfo(sale1.getId()); + List found2 = commentRepository.findByDiscountInfo(sale2.getId()); + + Assertions.assertThat(found1.size()).isEqualTo(3); + Assertions.assertThat(found2.size()).isEqualTo(2); + + + Assertions.assertThat(found1.get(0)).isEqualTo(comment1); + Assertions.assertThat(found1.get(1)).isEqualTo(comment3); + Assertions.assertThat(found1.get(2)).isEqualTo(comment4); + Assertions.assertThat(found2.get(0)).isEqualTo(comment2); + Assertions.assertThat(found2.get(1)).isEqualTo(comment5); + + + } + + @Test + public void findBySupportInfoTest(){ +// DiscountInfo sale1 = DiscountInfo.builder().title("무신사 할인").build(); +// DiscountInfo sale2 = DiscountInfo.builder().title("옥션 할인").build(); + + SupportInfo support1 = SupportInfo.builder().title("국가장학금").build(); + SupportInfo support2 = SupportInfo.builder().title("비전장학금").build(); + SupportInfo support3 = SupportInfo.builder().title("좋은장학금").build(); + SupportInfo support4 = SupportInfo.builder().title("서울봉사").build(); + SupportInfo support5 = SupportInfo.builder().title("청년대출").build(); + + supportInfoRepository.save(support1); + supportInfoRepository.save(support2); + supportInfoRepository.save(support3); + supportInfoRepository.save(support4); + supportInfoRepository.save(support5); + + Comment comment1 = Comment.builder().content("test1") + .supportInfo(support1) + .build(); + Comment comment2 = Comment.builder().content("test2") + .supportInfo(support2) + .build(); + Comment comment3 = Comment.builder().content("test3") + .supportInfo(support3) + .build(); + Comment comment4 = Comment.builder().content("test4") + .supportInfo(support4) + .build(); + Comment comment5 = Comment.builder().content("test5") + .supportInfo(support5) + .build(); + Comment comment6 = Comment.builder().content("test6") + .supportInfo(support1) + .build(); + Comment comment7 = Comment.builder().content("test7") + .supportInfo(support1) + .build(); + Comment comment8 = Comment.builder().content("test8") + .supportInfo(support3) + .build(); + Comment comment9 = Comment.builder().content("test9") + .supportInfo(support4) + .build(); + Comment comment10 = Comment.builder().content("test10") + .supportInfo(support4) + .build(); + + + commentRepository.save(comment1); + commentRepository.save(comment2); + commentRepository.save(comment3); + commentRepository.save(comment4); + commentRepository.save(comment5); + commentRepository.save(comment6); + commentRepository.save(comment7); + commentRepository.save(comment8); + commentRepository.save(comment9); + commentRepository.save(comment10); + + + em.flush(); + + List found1 = commentRepository.findBySupportInfo(support1.getId()); + List found2 = commentRepository.findBySupportInfo(support2.getId()); + List found3 = commentRepository.findBySupportInfo(support3.getId()); + List found4 = commentRepository.findBySupportInfo(support4.getId()); + List found5 = commentRepository.findBySupportInfo(support5.getId()); + + Assertions.assertThat(found1.size()).isEqualTo(3); + Assertions.assertThat(found2.size()).isEqualTo(1); + Assertions.assertThat(found3.size()).isEqualTo(2); + Assertions.assertThat(found4.size()).isEqualTo(3); + Assertions.assertThat(found5.size()).isEqualTo(1); + + } + + @Test + public void deleteTest(){ + SupportInfo support1 = SupportInfo.builder().title("국가장학금").build(); + SupportInfo support2 = SupportInfo.builder().title("비전장학금").build(); + SupportInfo support3 = SupportInfo.builder().title("좋은장학금").build(); + SupportInfo support4 = SupportInfo.builder().title("서울봉사").build(); + SupportInfo support5 = SupportInfo.builder().title("청년대출").build(); + + supportInfoRepository.save(support1); + supportInfoRepository.save(support2); + supportInfoRepository.save(support3); + supportInfoRepository.save(support4); + supportInfoRepository.save(support5); + + Comment comment1 = Comment.builder().content("test1") + .supportInfo(support1) + .build(); + Comment comment2 = Comment.builder().content("test2") + .supportInfo(support2) + .build(); + Comment comment3 = Comment.builder().content("test3") + .supportInfo(support3) + .build(); + Comment comment4 = Comment.builder().content("test4") + .supportInfo(support4) + .build(); + Comment comment5 = Comment.builder().content("test5") + .supportInfo(support5) + .build(); + Comment comment6 = Comment.builder().content("test6") + .supportInfo(support1) + .build(); + Comment comment7 = Comment.builder().content("test7") + .supportInfo(support1) + .build(); + Comment comment8 = Comment.builder().content("test8") + .supportInfo(support3) + .build(); + Comment comment9 = Comment.builder().content("test9") + .supportInfo(support4) + .build(); + Comment comment10 = Comment.builder().content("test10") + .supportInfo(support4) + .build(); + + + commentRepository.save(comment1); + commentRepository.save(comment2); + commentRepository.save(comment3); + commentRepository.save(comment4); + commentRepository.save(comment5); + commentRepository.save(comment6); + commentRepository.save(comment7); + commentRepository.save(comment8); + commentRepository.save(comment9); + commentRepository.save(comment10); + + commentRepository.delete(comment1); // comment1이 삭제되면 support1의 개수는 2개가 되어야한다. + + em.flush(); + + List found1 = commentRepository.findBySupportInfo(support1.getId()); + List found2 = commentRepository.findBySupportInfo(support2.getId()); + List found3 = commentRepository.findBySupportInfo(support3.getId()); + List found4 = commentRepository.findBySupportInfo(support4.getId()); + List found5 = commentRepository.findBySupportInfo(support5.getId()); + + Assertions.assertThat(found1.size()).isEqualTo(2); // 해당 로직 검증 + Assertions.assertThat(found2.size()).isEqualTo(1); + Assertions.assertThat(found3.size()).isEqualTo(2); + Assertions.assertThat(found4.size()).isEqualTo(3); + Assertions.assertThat(found5.size()).isEqualTo(1); + } + + +} \ No newline at end of file diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/comment/service/CommentServiceTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/comment/service/CommentServiceTest.java new file mode 100644 index 00000000..47636c82 --- /dev/null +++ b/src/test/java/com/bbteam/budgetbuddies/domain/comment/service/CommentServiceTest.java @@ -0,0 +1,563 @@ +package com.bbteam.budgetbuddies.domain.comment.service; + +import com.bbteam.budgetbuddies.domain.comment.dto.CommentRequestDto; +import com.bbteam.budgetbuddies.domain.comment.dto.CommentResponseDto; +import com.bbteam.budgetbuddies.domain.discountinfo.entity.DiscountInfo; +import com.bbteam.budgetbuddies.domain.discountinfo.repository.DiscountInfoRepository; +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import com.bbteam.budgetbuddies.domain.supportinfo.repository.SupportInfoRepository; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; +import jakarta.persistence.EntityManager; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + + +/** + * comment service는 다음과 같은 기능을 제공해야한다. + * 1. comment 저장 기능 + * 2. 특정 게시글에 따른 comment return + * 현재 게시글의 종류는 2가지로 각각 할인정보, 지원정보이다. + * 즉, 할인정보, 지원정보 ID가 들어오면 해당 게시글에 대한 댓글 정보를 다 가지고 올 수 있어야한다. + * 아마 관리 측면에선 댓글 삭제 기능도 필요할 것이다. + * 3. 특정 userid로 댓글 찾는 기능 + * 얘는 게시글 ID랑 제목 정도 같이??? + * 4. 특정 게시글 id로 댓글 찾는 기능 + */ + + +/* + 테스트마다 테스트케이스가 다를 수 있어서 공통로직으로 처리하지 않아 매우 깁니다... + */ +@SpringBootTest +@Transactional +class CommentServiceTest { + @Autowired + CommentService commentService; + + @Autowired + UserRepository userRepository; + @Autowired + DiscountInfoRepository discountInfoRepository; + @Autowired + SupportInfoRepository supportInfoRepository; + @Autowired + EntityManager em; + + @Test + public void saveDiscountInfoCommentTest(){ + User user1 = User.builder() + .name("tester1") + .email("1234") + .age(5) + .phoneNumber("123456") + .build(); + userRepository.save(user1); + + DiscountInfo sale1 = DiscountInfo.builder().title("무신사 할인") + .anonymousNumber(0) + .build(); + discountInfoRepository.save(sale1); + + CommentRequestDto.DiscountInfoCommentDto dto1 = CommentRequestDto.DiscountInfoCommentDto.builder() + .discountInfoId(sale1.getId()) + .content("굿") + .build(); + + commentService.saveDiscountComment(user1.getId(), dto1); + em.flush(); + + List returnDto = commentService.findByDiscountInfo(sale1.getId()); + + Assertions.assertThat(returnDto.size()).isEqualTo(1); + Assertions.assertThat(returnDto.get(0).getDiscountInfoId()).isEqualTo(sale1.getId()); + Assertions.assertThat(returnDto.get(0).getUserId()).isEqualTo(user1.getId()); + Assertions.assertThat(returnDto.get(0).getContent()).isEqualTo("굿"); + + } + + @Test + public void saveDiscountInfoCommentTest2(){ + User user1 = User.builder() + .name("tester1") + .email("1234") + .age(5) + .phoneNumber("123456") + .build(); + userRepository.save(user1); + + User user2 = User.builder() + .name("tester2") + .email("12345") + .age(7) + .phoneNumber("1234567") + .build(); + userRepository.save(user2); + + DiscountInfo sale1 = DiscountInfo.builder().title("무신사 할인") + .anonymousNumber(0) + .build(); + discountInfoRepository.save(sale1); + DiscountInfo sale2 = DiscountInfo.builder().title("핫트랙스 할인") + .anonymousNumber(0) + .build(); + discountInfoRepository.save(sale2); + + + CommentRequestDto.DiscountInfoCommentDto dto1 = CommentRequestDto.DiscountInfoCommentDto.builder() + .discountInfoId(sale1.getId()) + .content("굿") + .build(); + + CommentRequestDto.DiscountInfoCommentDto dto2 = CommentRequestDto.DiscountInfoCommentDto.builder() + .discountInfoId(sale1.getId()) + .content("좋아요") + .build(); + CommentRequestDto.DiscountInfoCommentDto dto3 = CommentRequestDto.DiscountInfoCommentDto.builder() + .discountInfoId(sale2.getId()) + .content("유용해요!") + .build(); + + commentService.saveDiscountComment(user1.getId(), dto1); + commentService.saveDiscountComment(user2.getId(), dto2); + commentService.saveDiscountComment(user1.getId(), dto3); + + em.flush(); + + List returnDto = commentService.findByDiscountInfo(sale1.getId()); + List returnDto2 = commentService.findByDiscountInfo(sale2.getId()); + Assertions.assertThat(returnDto.size()).isEqualTo(2); + Assertions.assertThat(returnDto.get(0).getDiscountInfoId()).isEqualTo(sale1.getId()); + Assertions.assertThat(returnDto.get(0).getUserId()).isEqualTo(user1.getId()); + Assertions.assertThat(returnDto.get(0).getContent()).isEqualTo("굿"); + Assertions.assertThat(returnDto.get(1).getDiscountInfoId()).isEqualTo(sale1.getId()); + Assertions.assertThat(returnDto.get(1).getUserId()).isEqualTo(user2.getId()); + Assertions.assertThat(returnDto.get(1).getContent()).isEqualTo("좋아요"); + + Assertions.assertThat(returnDto2.size()).isEqualTo(1); + Assertions.assertThat(returnDto2.get(0).getDiscountInfoId()).isEqualTo(sale2.getId()); + Assertions.assertThat(returnDto2.get(0).getUserId()).isEqualTo(user1.getId()); + Assertions.assertThat(returnDto2.get(0).getContent()).isEqualTo("유용해요!"); + + } + + @Test + void DiscountAnonymousCommentTest(){ + User user1 = User.builder() + .name("tester1") + .email("1234") + .age(5) + .phoneNumber("123456") + .build(); + userRepository.save(user1); + + User user2 = User.builder() + .name("tester2") + .email("12345") + .age(7) + .phoneNumber("1234567") + .build(); + + User user3 = User.builder() + .name("tester3") + .email("1234553") + .age(9) + .phoneNumber("1232134567") + .build(); + userRepository.save(user2); + userRepository.save(user3); + + DiscountInfo sale1 = DiscountInfo.builder().anonymousNumber(0).title("무신사 할인").build(); + discountInfoRepository.save(sale1); + DiscountInfo sale2 = DiscountInfo.builder().anonymousNumber(0).title("핫트랙스 할인").build(); + discountInfoRepository.save(sale2); + + + CommentRequestDto.DiscountInfoCommentDto dto1 = CommentRequestDto.DiscountInfoCommentDto.builder() + .discountInfoId(sale1.getId()) + .content("굿") + .build(); + + CommentRequestDto.DiscountInfoCommentDto dto2 = CommentRequestDto.DiscountInfoCommentDto.builder() + .discountInfoId(sale1.getId()) + .content("좋아요") + .build(); + CommentRequestDto.DiscountInfoCommentDto dto3 = CommentRequestDto.DiscountInfoCommentDto.builder() + .discountInfoId(sale2.getId()) + .content("유용해요!") + .build(); + CommentRequestDto.DiscountInfoCommentDto dto4 = CommentRequestDto.DiscountInfoCommentDto.builder() + .discountInfoId(sale1.getId()) + .content("구웃!") + .build(); + + commentService.saveDiscountComment(user1.getId(), dto1); + commentService.saveDiscountComment(user2.getId(), dto2); + commentService.saveDiscountComment(user1.getId(), dto3); + + commentService.saveDiscountComment(user1.getId(), dto4); + commentService.saveDiscountComment(user3.getId(), dto4); + + em.flush(); + + List result = commentService.findByDiscountInfo(sale1.getId()); + Integer test1 = result.get(0).getAnonymousNumber(); + Integer test2 = result.get(1).getAnonymousNumber(); + Integer test3 = result.get(2).getAnonymousNumber(); + Integer test4 = result.get(3).getAnonymousNumber(); + + Assertions.assertThat(test1).isEqualTo(1); + Assertions.assertThat(test2).isEqualTo(2); + Assertions.assertThat(test3).isEqualTo(1); + Assertions.assertThat(test4).isEqualTo(3); + + + } + + @Test + public void saveSupportInfoCommentTest2(){ + User user1 = User.builder() + .name("tester1") + .email("1234") + .age(5) + .phoneNumber("123456") + .build(); + userRepository.save(user1); + + User user2 = User.builder() + .name("tester2") + .email("12345") + .age(7) + .phoneNumber("1234567") + .build(); + userRepository.save(user2); + + SupportInfo info1 = SupportInfo.builder().anonymousNumber(0).title("국가장학금 신청").build(); + supportInfoRepository.save(info1); + SupportInfo info2 = SupportInfo.builder().anonymousNumber(0).title("봉사활동").build(); + supportInfoRepository.save(info2); + + + CommentRequestDto.SupportInfoCommentDto dto1 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info1.getId()) + .content("굿") + .build(); + + CommentRequestDto.SupportInfoCommentDto dto2 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info1.getId()) + .content("좋아요") + .build(); + CommentRequestDto.SupportInfoCommentDto dto3 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info2.getId()) + .content("유용해요!") + .build(); + + commentService.saveSupportComment(user1.getId(), dto1); + commentService.saveSupportComment(user2.getId(), dto2); + commentService.saveSupportComment(user1.getId(), dto3); + + em.flush(); + + List returnDto = commentService.findBySupportInfo(info1.getId()); + List returnDto2 = commentService.findBySupportInfo(info2.getId()); + Assertions.assertThat(returnDto.size()).isEqualTo(2); + Assertions.assertThat(returnDto.get(0).getSupportInfoId()).isEqualTo(info1.getId()); + Assertions.assertThat(returnDto.get(0).getUserId()).isEqualTo(user1.getId()); + Assertions.assertThat(returnDto.get(0).getContent()).isEqualTo("굿"); + Assertions.assertThat(returnDto.get(1).getSupportInfoId()).isEqualTo(info1.getId()); + Assertions.assertThat(returnDto.get(1).getUserId()).isEqualTo(user2.getId()); + Assertions.assertThat(returnDto.get(1).getContent()).isEqualTo("좋아요"); + + Assertions.assertThat(returnDto2.size()).isEqualTo(1); + Assertions.assertThat(returnDto2.get(0).getSupportInfoId()).isEqualTo(info2.getId()); + Assertions.assertThat(returnDto2.get(0).getUserId()).isEqualTo(user1.getId()); + Assertions.assertThat(returnDto2.get(0).getContent()).isEqualTo("유용해요!"); + + } + + @Test + void supportAnonymousCommentTest(){ + User user1 = User.builder() + .name("tester1") + .email("1234") + .age(5) + .phoneNumber("123456") + .build(); + userRepository.save(user1); + + User user2 = User.builder() + .name("tester2") + .email("12345") + .age(7) + .phoneNumber("1234567") + .build(); + User user3 = User.builder() + .name("tester432") + .email("123423445") + .age(7) + .phoneNumber("1423234567") + .build(); + User user4 = User.builder() + .name("test43er2") + .email("1232445") + .age(7) + .phoneNumber("123454267") + .build(); + userRepository.save(user2); + userRepository.save(user3); + userRepository.save(user4); + + SupportInfo info1 = SupportInfo.builder().anonymousNumber(0).title("국가장학금 신청").build(); + supportInfoRepository.save(info1); + SupportInfo info2 = SupportInfo.builder().anonymousNumber(0).title("봉사활동").build(); + supportInfoRepository.save(info2); + + + CommentRequestDto.SupportInfoCommentDto dto1 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info1.getId()) + .content("굿") + .build(); + + CommentRequestDto.SupportInfoCommentDto dto2 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info1.getId()) + .content("좋아요") + .build(); + CommentRequestDto.SupportInfoCommentDto dto3 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info2.getId()) + .content("유용해요!") + .build(); + CommentRequestDto.SupportInfoCommentDto dto6 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info1.getId()) + .content("굿") + .build(); + CommentRequestDto.SupportInfoCommentDto dto4 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info1.getId()) + .content("굿") + .build(); + CommentRequestDto.SupportInfoCommentDto dto5 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info1.getId()) + .content("굿") + .build(); + + commentService.saveSupportComment(user1.getId(), dto1); + commentService.saveSupportComment(user2.getId(), dto2); + commentService.saveSupportComment(user1.getId(), dto3); + commentService.saveSupportComment(user3.getId(), dto4); + commentService.saveSupportComment(user4.getId(), dto5); + commentService.saveSupportComment(user1.getId(), dto6); + + em.flush(); + + List returnDto = commentService.findBySupportInfo(info1.getId()); + List returnDto2 = commentService.findBySupportInfo(info2.getId()); + + Integer test1 = returnDto.get(0).getAnonymousNumber(); + Integer test2 = returnDto.get(1).getAnonymousNumber(); + Integer test3 = returnDto.get(2).getAnonymousNumber(); + Integer test4 = returnDto.get(3).getAnonymousNumber(); + Integer test5 = returnDto.get(4).getAnonymousNumber(); + + Assertions.assertThat(test1).isEqualTo(1); + Assertions.assertThat(test2).isEqualTo(2); + Assertions.assertThat(test3).isEqualTo(3); + Assertions.assertThat(test4).isEqualTo(4); + Assertions.assertThat(test5).isEqualTo(1); + } + + @Test + void DiscountInfoCommentPagingTest() { + User user1 = User.builder() + .name("tester1") + .email("1234") + .age(5) + .phoneNumber("123456") + .build(); + userRepository.save(user1); + + User user2 = User.builder() + .name("tester2") + .email("12345") + .age(7) + .phoneNumber("1234567") + .build(); + + User user3 = User.builder() + .name("tester3") + .email("1234553") + .age(9) + .phoneNumber("1232134567") + .build(); + userRepository.save(user2); + userRepository.save(user3); + + DiscountInfo sale1 = DiscountInfo.builder().anonymousNumber(0).title("무신사 할인").build(); + discountInfoRepository.save(sale1); + DiscountInfo sale2 = DiscountInfo.builder().anonymousNumber(0).title("핫트랙스 할인").build(); + discountInfoRepository.save(sale2); + + + CommentRequestDto.DiscountInfoCommentDto dto1 = CommentRequestDto.DiscountInfoCommentDto.builder() + .discountInfoId(sale1.getId()) + .content("굿") + .build(); + + CommentRequestDto.DiscountInfoCommentDto dto2 = CommentRequestDto.DiscountInfoCommentDto.builder() + .discountInfoId(sale1.getId()) + .content("좋아요") + .build(); + CommentRequestDto.DiscountInfoCommentDto dto3 = CommentRequestDto.DiscountInfoCommentDto.builder() + .discountInfoId(sale2.getId()) + .content("유용해요!") + .build(); + CommentRequestDto.DiscountInfoCommentDto dto4 = CommentRequestDto.DiscountInfoCommentDto.builder() + .discountInfoId(sale1.getId()) + .content("구웃!") + .build(); + + commentService.saveDiscountComment(user1.getId(), dto1); + commentService.saveDiscountComment(user2.getId(), dto2); + commentService.saveDiscountComment(user1.getId(), dto3); + + commentService.saveDiscountComment(user1.getId(), dto4); + commentService.saveDiscountComment(user3.getId(), dto4); + commentService.saveDiscountComment(user2.getId(), dto4); + //sale1 = 5 + em.flush(); + + PageRequest pageRequest1 = PageRequest.of(0, 2); + + Page result1 = commentService.findByDiscountInfoWithPaging(sale1.getId(), pageRequest1); + Assertions.assertThat(result1.getTotalElements()).isEqualTo(5); + Assertions.assertThat(result1.getTotalPages()).isEqualTo(3); + Assertions.assertThat(result1.hasNext()).isTrue(); + Assertions.assertThat(result1.hasPrevious()).isFalse(); + List list1 = result1.getContent(); + Assertions.assertThat(list1.get(0).getUserId()).isEqualTo(user1.getId()); + Assertions.assertThat(list1.get(0).getContent()).isEqualTo("굿"); + Assertions.assertThat(list1.get(0).getAnonymousNumber()).isEqualTo(1); + + PageRequest pageRequest2 = PageRequest.of(1, 3); + + Page result2 = commentService.findByDiscountInfoWithPaging(sale1.getId(), pageRequest2); + Assertions.assertThat(result2.getTotalElements()).isEqualTo(5); + Assertions.assertThat(result2.getTotalPages()).isEqualTo(2); + Assertions.assertThat(result2.hasNext()).isFalse(); + Assertions.assertThat(result2.hasPrevious()).isTrue(); + List list2 = result2.getContent(); + Assertions.assertThat(list2.get(0).getUserId()).isEqualTo(user3.getId()); + Assertions.assertThat(list2.get(0).getContent()).isEqualTo("구웃!"); + Assertions.assertThat(list2.get(0).getAnonymousNumber()).isEqualTo(3); + + + } + + @Test + void SupportInfoPagingTest() { + User user1 = User.builder() + .name("tester1") + .email("1234") + .age(5) + .phoneNumber("123456") + .build(); + userRepository.save(user1); + + User user2 = User.builder() + .name("tester2") + .email("12345") + .age(7) + .phoneNumber("1234567") + .build(); + User user3 = User.builder() + .name("tester432") + .email("123423445") + .age(7) + .phoneNumber("1423234567") + .build(); + User user4 = User.builder() + .name("test43er2") + .email("1232445") + .age(7) + .phoneNumber("123454267") + .build(); + userRepository.save(user2); + userRepository.save(user3); + userRepository.save(user4); + + SupportInfo info1 = SupportInfo.builder().anonymousNumber(0).title("국가장학금 신청").build(); + supportInfoRepository.save(info1); + SupportInfo info2 = SupportInfo.builder().anonymousNumber(0).title("봉사활동").build(); + supportInfoRepository.save(info2); + + + CommentRequestDto.SupportInfoCommentDto dto1 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info1.getId()) + .content("굿") + .build(); + + CommentRequestDto.SupportInfoCommentDto dto2 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info1.getId()) + .content("좋아요") + .build(); + CommentRequestDto.SupportInfoCommentDto dto3 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info2.getId()) + .content("유용해요!") + .build(); + CommentRequestDto.SupportInfoCommentDto dto6 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info1.getId()) + .content("굿") + .build(); + CommentRequestDto.SupportInfoCommentDto dto4 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info1.getId()) + .content("굿") + .build(); + CommentRequestDto.SupportInfoCommentDto dto5 = CommentRequestDto.SupportInfoCommentDto.builder() + .supportInfoId(info1.getId()) + .content("굿") + .build(); + + commentService.saveSupportComment(user1.getId(), dto1); + commentService.saveSupportComment(user2.getId(), dto2); + commentService.saveSupportComment(user1.getId(), dto3); // 얘만 info2 + commentService.saveSupportComment(user3.getId(), dto4); + commentService.saveSupportComment(user4.getId(), dto5); + commentService.saveSupportComment(user1.getId(), dto6); + commentService.saveSupportComment(user2.getId(), dto5); + commentService.saveSupportComment(user3.getId(), dto5); + em.flush(); + + PageRequest pageRequest1 = PageRequest.of(0, 2); + Page result1 = commentService.findBySupportInfoWithPaging(info1.getId(), pageRequest1); + + Assertions.assertThat(result1.getTotalElements()).isEqualTo(7); + Assertions.assertThat(result1.getTotalPages()).isEqualTo(4); + Assertions.assertThat(result1.hasNext()).isTrue(); + Assertions.assertThat(result1.hasPrevious()).isFalse(); + List list1 = result1.getContent(); + Assertions.assertThat(list1.get(0).getUserId()).isEqualTo(user1.getId()); + Assertions.assertThat(list1.get(0).getContent()).isEqualTo("굿"); + Assertions.assertThat(list1.get(0).getAnonymousNumber()).isEqualTo(1); + + PageRequest pageRequest2 = PageRequest.of(1, 5); + Page result2 = commentService.findBySupportInfoWithPaging(info1.getId(), pageRequest2); + + Assertions.assertThat(result2.getTotalElements()).isEqualTo(7); + Assertions.assertThat(result2.getTotalPages()).isEqualTo(2); + Assertions.assertThat(result2.hasNext()).isFalse(); + Assertions.assertThat(result2.hasPrevious()).isTrue(); + List list2 = result2.getContent(); + Assertions.assertThat(list2.get(0).getUserId()).isEqualTo(user2.getId()); + Assertions.assertThat(list2.get(0).getContent()).isEqualTo("굿"); + Assertions.assertThat(list2.get(0).getAnonymousNumber()).isEqualTo(2); + } + + + +} \ No newline at end of file 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 new file mode 100644 index 00000000..571f5189 --- /dev/null +++ b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/repository/ConsumptionGoalRepositoryTest.java @@ -0,0 +1,96 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.repository; + +import static org.assertj.core.api.Assertions.*; + +import java.time.LocalDate; +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +import com.bbteam.budgetbuddies.domain.category.entity.Category; +import com.bbteam.budgetbuddies.domain.category.repository.CategoryRepository; +import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; + +@DisplayName("ConsumptionGoal 레포지토리 테스트의 ") +@DataJpaTest +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +class ConsumptionGoalRepositoryTest { + @Autowired + ConsumptionGoalRepository consumptionGoalRepository; + @Autowired + UserRepository userRepository; + @Autowired + CategoryRepository categoryRepository; + + @Test + @DisplayName("유저 아이디와 goalMonth를 통해 GoalConsumption 조회 성공") + void findConsumptionGoalByUserIdAndGoalMonth_Success() { + // given + // 목표 달 + LocalDate goalMonth = LocalDate.of(2024, 07, 01); + + User mainUser = userRepository.save( + User.builder().email("email").age(24).name("name").phoneNumber("010-1234-5678").build()); + + Category defaultCategory = categoryRepository.save( + Category.builder().name("디폴트 카테고리").user(null).isDefault(true).build()); + + Category userCategory = categoryRepository.save( + Category.builder().name("유저 카테고리").user(mainUser).isDefault(false).build()); + + ConsumptionGoal defaultCategoryConsumptionGoal = consumptionGoalRepository.save(ConsumptionGoal.builder() + .goalAmount(1L) + .consumeAmount(1L) + .user(mainUser) + .goalMonth(goalMonth) + .category(defaultCategory) + .build()); + + ConsumptionGoal userCategoryConsumptionGoal = consumptionGoalRepository.save(ConsumptionGoal.builder() + .goalAmount(1L) + .consumeAmount(1L) + .user(mainUser) + .goalMonth(goalMonth) + .category(userCategory) + .build()); + + setUnselectedConsumptionGoal(mainUser, goalMonth, defaultCategory); + + // when + List result = consumptionGoalRepository.findConsumptionGoalByUserIdAndGoalMonth(mainUser.getId(), + goalMonth); + + // then + assertThat(result).usingRecursiveComparison() + .isEqualTo(List.of(defaultCategoryConsumptionGoal, userCategoryConsumptionGoal)); + } + + private void setUnselectedConsumptionGoal(User mainUser, LocalDate goalMonth, Category defaultCategory) { + User otherUser = userRepository.save( + User.builder().email("email2").age(24).name("name2").phoneNumber("010-1567-5678").build()); + + ConsumptionGoal lastMonthDefaultCategoryConsumptionGoal = consumptionGoalRepository.save( + ConsumptionGoal.builder() + .goalAmount(1L) + .consumeAmount(1L) + .user(mainUser) + .goalMonth(goalMonth.minusMonths(1)) + .category(defaultCategory) + .build()); + + ConsumptionGoal otherUserDefaultCategoryConsumptionGoal = consumptionGoalRepository.save( + ConsumptionGoal.builder() + .goalAmount(1L) + .consumeAmount(1L) + .user(otherUser) + .goalMonth(goalMonth) + .category(defaultCategory) + .build()); + } +} \ No newline at end of file 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 new file mode 100644 index 00000000..261e25f9 --- /dev/null +++ b/src/test/java/com/bbteam/budgetbuddies/domain/consumptiongoal/service/ConsumptionGoalServiceTest.java @@ -0,0 +1,231 @@ +package com.bbteam.budgetbuddies.domain.consumptiongoal.service; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.BDDMockito.*; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; +import java.util.Random; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.bbteam.budgetbuddies.domain.category.entity.Category; +import com.bbteam.budgetbuddies.domain.category.repository.CategoryRepository; +import com.bbteam.budgetbuddies.domain.consumptiongoal.converter.ConsumptionGoalConverter; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalListRequestDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalRequestDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.dto.ConsumptionGoalResponseListDto; +import com.bbteam.budgetbuddies.domain.consumptiongoal.entity.ConsumptionGoal; +import com.bbteam.budgetbuddies.domain.consumptiongoal.repository.ConsumptionGoalRepository; +import com.bbteam.budgetbuddies.domain.user.entity.User; +import com.bbteam.budgetbuddies.domain.user.repository.UserRepository; + +@DisplayName("ConsumptionGoalImpl 서비스 테스트의 ") +@ExtendWith(MockitoExtension.class) +class ConsumptionGoalServiceTest { + private final LocalDate GOAL_MONTH = LocalDate.of(2024, 07, 01); + private User user; + private LocalDate goalMonthRandomDay; + + @InjectMocks + private ConsumptionGoalServiceImpl consumptionGoalService; + @Mock + private ConsumptionGoalRepository consumptionGoalRepository; + @Mock + private CategoryRepository categoryRepository; + @Mock + private UserRepository userRepository; + @Spy + private ConsumptionGoalConverter consumptionGoalConverter; + + @BeforeEach + void setUp() { + Random random = new Random(); + int randomDay = random.nextInt(30) + 1; + goalMonthRandomDay = LocalDate.of(GOAL_MONTH.getYear(), GOAL_MONTH.getMonth(), randomDay); + + user = Mockito.spy(User.builder().email("email").age(24).name("name").phoneNumber("010-1234-5678").build()); + given(user.getId()).willReturn(-1L); + given(userRepository.findById(user.getId())).willReturn(Optional.ofNullable(user)); + } + + @Test + @DisplayName("findUserConsumptionGoal : 생성된 ConsumptionGoal이 없고 카테고리만 있는 경우 목표 금액, 소비 금액 0으로 초기화") + void findUserConsumptionGoal_onlyCategory() { + // given + Category defaultCategory = Mockito.spy(Category.builder().name("디폴트 카테고리").user(null).isDefault(true).build()); + given(defaultCategory.getId()).willReturn(-1L); + + Category userCategory = Mockito.spy(Category.builder().name("유저 카테고리").user(user).isDefault(false).build()); + given(userCategory.getId()).willReturn(-2L); + + List categoryList = List.of(defaultCategory, userCategory); + + given(categoryRepository.findUserCategoryByUserId(user.getId())).willReturn(categoryList); + + List expected = categoryList.stream() + .map(category -> consumptionGoalConverter.toConsumptionGoalResponseDto(category)) + .toList(); + + // when + ConsumptionGoalResponseListDto result = consumptionGoalService.findUserConsumptionGoal(user.getId(), + goalMonthRandomDay); + + // then + assertThat(result.getConsumptionGoalList()).usingRecursiveComparison().isEqualTo(expected); + } + + @Test + @DisplayName("findUserConsumptionGoal : 한달전 ConsumptionGoal만 있을 경우 한달전으로 초기화") + void findUserConsumptionGoal_previousMonth() { + // given + Category defaultCategory = Mockito.spy(Category.builder().name("디폴트 카테고리").user(null).isDefault(true).build()); + given(defaultCategory.getId()).willReturn(-1L); + + Category userCategory = Mockito.spy(Category.builder().name("유저 카테고리").user(user).isDefault(false).build()); + given(userCategory.getId()).willReturn(-2L); + + given(categoryRepository.findUserCategoryByUserId(user.getId())).willReturn( + List.of(defaultCategory, userCategory)); + + ConsumptionGoal previousMonthDefaultCategoryGoal = ConsumptionGoal.builder() + .goalAmount(1_000_000L) + .consumeAmount(20_000L) + .user(user) + .category(defaultCategory) + .goalMonth(goalMonthRandomDay.minusMonths(1)) + .build(); + + ConsumptionGoal previousMonthUserCategoryGoal = ConsumptionGoal.builder() + .goalAmount(1_000_000L) + .consumeAmount(20_000L) + .user(user) + .category(userCategory) + .goalMonth(goalMonthRandomDay.minusMonths(1)) + .build(); + + List previousGoalList = List.of(previousMonthDefaultCategoryGoal, + previousMonthUserCategoryGoal); + + given(consumptionGoalRepository.findConsumptionGoalByUserIdAndGoalMonth(user.getId(), + GOAL_MONTH.minusMonths(1))).willReturn(previousGoalList); + + List expected = previousGoalList.stream() + .map(consumptionGoalConverter::toConsumptionGoalResponseDto) + .toList(); + + // when + ConsumptionGoalResponseListDto result = consumptionGoalService.findUserConsumptionGoal(user.getId(), + goalMonthRandomDay); + + // then + assertThat(result.getConsumptionGoalList()).usingRecursiveComparison().isEqualTo(expected); + } + + @Test + @DisplayName("findUserConsumptionGoal : 한달 전과 목표 달 ConsumptionGoal이 있을 경우 목표 달로 초기화") + void findUserConsumptionGoal_previousMonthAndGoalMonth() { + // given + Category userCategory = Mockito.spy(Category.builder().name("유저 카테고리").user(user).isDefault(false).build()); + given(userCategory.getId()).willReturn(-2L); + + ConsumptionGoal previousMonthUserCategoryGoal = ConsumptionGoal.builder() + .goalAmount(1_000_000L) + .consumeAmount(20_000L) + .user(user) + .category(userCategory) + .goalMonth(goalMonthRandomDay.minusMonths(1)) + .build(); + + ConsumptionGoal goalMonthUserCategoryGoal = ConsumptionGoal.builder() + .goalAmount(2_000_000L) + .consumeAmount(30_000L) + .user(user) + .category(userCategory) + .goalMonth(goalMonthRandomDay) + .build(); + + given(consumptionGoalRepository.findConsumptionGoalByUserIdAndGoalMonth(user.getId(), + GOAL_MONTH.minusMonths(1))).willReturn(List.of(previousMonthUserCategoryGoal)); + + given(consumptionGoalRepository.findConsumptionGoalByUserIdAndGoalMonth(user.getId(), GOAL_MONTH)).willReturn( + List.of(goalMonthUserCategoryGoal)); + + // when + ConsumptionGoalResponseListDto result = consumptionGoalService.findUserConsumptionGoal(user.getId(), + goalMonthRandomDay); + + // then + assertThat(result.getConsumptionGoalList()).usingRecursiveComparison() + .isEqualTo(List.of(consumptionGoalConverter.toConsumptionGoalResponseDto(goalMonthUserCategoryGoal))); + } + + @Test + @DisplayName("updateConsumptionGoal : 이번달 목표가 있는 경우(defaultCategory)와 목표가 없는 경우(userCategory)") + void updateConsumptionGoal_Success() { + // given + Long defaultGoalAmount = 100L; + Long userGoalAmount = 200L; + + ConsumptionGoalListRequestDto request = new ConsumptionGoalListRequestDto( + List.of(new ConsumptionGoalRequestDto(-1L, defaultGoalAmount), + new ConsumptionGoalRequestDto(-2L, userGoalAmount))); + + Category defaultCategory = Mockito.spy(Category.builder().name("디폴트 카테고리").user(null).isDefault(true).build()); + given(defaultCategory.getId()).willReturn(-1L); + given(categoryRepository.findById(defaultCategory.getId())).willReturn(Optional.of(defaultCategory)); + + Category userCategory = Mockito.spy(Category.builder().name("유저 카테고리").user(user).isDefault(false).build()); + given(userCategory.getId()).willReturn(-2L); + given(categoryRepository.findById(userCategory.getId())).willReturn(Optional.of(userCategory)); + + ConsumptionGoal defaultCategoryGoal = ConsumptionGoal.builder() + .goalAmount(1_000_000L) + .consumeAmount(20_000L) + .user(user) + .category(defaultCategory) + .goalMonth(GOAL_MONTH) + .build(); + given(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, defaultCategory, + GOAL_MONTH)).willReturn(Optional.ofNullable(defaultCategoryGoal)); + + given(consumptionGoalRepository.findConsumptionGoalByUserAndCategoryAndGoalMonth(user, userCategory, + GOAL_MONTH)).willReturn(Optional.ofNullable(null)); + + when(consumptionGoalRepository.saveAll(any())).thenAnswer(invocation -> { + List goalsToSave = invocation.getArgument(0); + return goalsToSave; + }); + + List expected = List.of( + ConsumptionGoalResponseDto.builder() + .goalAmount(defaultGoalAmount) + .consumeAmount(defaultCategoryGoal.getConsumeAmount()) + .categoryName(defaultCategory.getName()) + .categoryId(defaultCategory.getId()) + .build(), + ConsumptionGoalResponseDto.builder() + .goalAmount(userGoalAmount) + .consumeAmount(0L) + .categoryName(userCategory.getName()) + .categoryId(userCategory.getId()) + .build() + ); + + // when + ConsumptionGoalResponseListDto result = consumptionGoalService.updateConsumptionGoals(user.getId(), request); + + // then + assertThat(result.getConsumptionGoalList()).usingRecursiveComparison().isEqualTo(expected); + } +} \ No newline at end of file diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/discountinfo/repository/DiscountInfoRepositoryTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/discountinfo/repository/DiscountInfoRepositoryTest.java index 22348c61..467f624a 100644 --- a/src/test/java/com/bbteam/budgetbuddies/domain/discountinfo/repository/DiscountInfoRepositoryTest.java +++ b/src/test/java/com/bbteam/budgetbuddies/domain/discountinfo/repository/DiscountInfoRepositoryTest.java @@ -70,4 +70,164 @@ void findByDateRangeFailure() { // then assertThat(results).hasSize(0); } + + @Test + void findByMonthTest(){ + LocalDate start1 = LocalDate.of(2024, 8, 1); + LocalDate start2 = LocalDate.of(2024, 7, 20); + LocalDate start3 = LocalDate.of(2024, 8, 31); + LocalDate start4 = LocalDate.of(2024, 7, 20); + LocalDate start5 = LocalDate.of(2024, 9, 1); + + LocalDate end1 = LocalDate.of(2024, 8, 5); + LocalDate end2 = LocalDate.of(2024, 8, 3); + LocalDate end3 = LocalDate.of(2024, 9, 3); + LocalDate end4 = LocalDate.of(2024, 7, 24); + LocalDate end5 = LocalDate.of(2024, 9, 5); + + DiscountInfo info1 = DiscountInfo.builder() + .startDate(start1) + .endDate(end1) + .title("test1") + .likeCount(0) + .build(); + + DiscountInfo info2 = DiscountInfo.builder() + .startDate(start2) + .endDate(end2) + .title("test2") + .likeCount(0) + .build(); + + DiscountInfo info3 = DiscountInfo.builder() + .startDate(start3) + .endDate(end3) + .title("test3") + .likeCount(0) + .build(); + + DiscountInfo info4 = DiscountInfo.builder() + .startDate(start4) + .endDate(end4) + .title("test4") + .likeCount(0) + .build(); + + DiscountInfo info5 = DiscountInfo.builder() + .startDate(start5) + .endDate(end5) + .title("test5") + .likeCount(0) + .build(); + + discountInfoRepository.save(info1); + discountInfoRepository.save(info2); + discountInfoRepository.save(info3); + discountInfoRepository.save(info4); + discountInfoRepository.save(info5); + + LocalDate firstDay = LocalDate.of(2024, 8, 1); + LocalDate lastDay = firstDay.withDayOfMonth(firstDay.lengthOfMonth()); + + List result = discountInfoRepository.findByMonth(firstDay, lastDay); + + Assertions.assertThat(result.size()).isEqualTo(3); + Assertions.assertThat(result).containsExactlyInAnyOrder(info1, info2, info3); + } + + @Test + void findRecommendInfoByMonthTest(){ + LocalDate start1 = LocalDate.of(2024, 8, 1); + LocalDate start2 = LocalDate.of(2024, 7, 20); + LocalDate start3 = LocalDate.of(2024, 8, 31); + LocalDate start4 = LocalDate.of(2024, 7, 20); + LocalDate start5 = LocalDate.of(2024, 9, 1); + + LocalDate end1 = LocalDate.of(2024, 8, 5); + LocalDate end2 = LocalDate.of(2024, 8, 3); + LocalDate end3 = LocalDate.of(2024, 9, 3); + LocalDate end4 = LocalDate.of(2024, 7, 24); + LocalDate end5 = LocalDate.of(2024, 9, 5); + + DiscountInfo info1 = DiscountInfo.builder() + .startDate(start1) + .endDate(end1) + .title("test1") + .likeCount(0) + .build(); + + DiscountInfo info2 = DiscountInfo.builder() + .startDate(start2) + .endDate(end2) + .title("test2") + .likeCount(0) + .build(); + + DiscountInfo info3 = DiscountInfo.builder() + .startDate(start3) + .endDate(end3) + .title("test3") + .likeCount(0) + .build(); + + DiscountInfo info4 = DiscountInfo.builder() + .startDate(start4) + .endDate(end4) + .title("test4") + .likeCount(0) + .build(); + + DiscountInfo info5 = DiscountInfo.builder() + .startDate(start5) + .endDate(end5) + .title("test5") + .likeCount(0) + .build(); + + discountInfoRepository.save(info1); + discountInfoRepository.save(info2); + discountInfoRepository.save(info3); + discountInfoRepository.save(info4); + discountInfoRepository.save(info5); + + for(int i = 0; i < 3; i++){ + info1.addLikeCount(); + } + for(int i = 0; i < 4; i++){ + info2.addLikeCount(); + } + for(int i = 0; i < 5; i++){ + info3.addLikeCount(); + } + for(int i = 0; i < 6; i++){ + info4.addLikeCount(); + } + for(int i = 0; i < 7; i++){ + info5.addLikeCount(); + } + LocalDate firstDay = LocalDate.of(2024, 8, 1); + LocalDate lastDay = firstDay.withDayOfMonth(firstDay.lengthOfMonth()); + + List result = discountInfoRepository.findRecommendInfoByMonth(firstDay, lastDay); + + Assertions.assertThat(result.size()).isEqualTo(2); + Assertions.assertThat(result).containsExactly(info3, info2); + + discountInfoRepository.delete(info3); + + List result2 = discountInfoRepository.findRecommendInfoByMonth(firstDay, lastDay); + + Assertions.assertThat(result2.size()).isEqualTo(2); + Assertions.assertThat(result2).containsExactly(info2, info1); + + discountInfoRepository.delete(info2); + + List result3 = discountInfoRepository.findRecommendInfoByMonth(firstDay, lastDay); + + Assertions.assertThat(result3.size()).isEqualTo(1); + Assertions.assertThat(result3).containsExactly(info1); + + + } + } \ No newline at end of file diff --git a/src/test/java/com/bbteam/budgetbuddies/domain/supportinfo/repository/SupportInfoRepositoryTest.java b/src/test/java/com/bbteam/budgetbuddies/domain/supportinfo/repository/SupportInfoRepositoryTest.java new file mode 100644 index 00000000..7d490ee8 --- /dev/null +++ b/src/test/java/com/bbteam/budgetbuddies/domain/supportinfo/repository/SupportInfoRepositoryTest.java @@ -0,0 +1,181 @@ +package com.bbteam.budgetbuddies.domain.supportinfo.repository; + +import com.bbteam.budgetbuddies.domain.supportinfo.entity.SupportInfo; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@Transactional +class SupportInfoRepositoryTest { + + @Autowired + SupportInfoRepository supportInfoRepository; + + + @Test + void findByMonthTest(){ + LocalDate start1 = LocalDate.of(2024, 7, 27); + LocalDate start2 = LocalDate.of(2024, 7, 20); + LocalDate start3 = LocalDate.of(2024, 8, 31); + LocalDate start4 = LocalDate.of(2024, 7, 20); + LocalDate start5 = LocalDate.of(2024, 8, 30); + + LocalDate end1 = LocalDate.of(2024, 8, 5); + LocalDate end2 = LocalDate.of(2024, 8, 3); + LocalDate end3 = LocalDate.of(2024, 9, 3); + LocalDate end4 = LocalDate.of(2024, 7, 24); + LocalDate end5 = LocalDate.of(2024, 9, 5); + + SupportInfo info1 = SupportInfo.builder() + .startDate(start1) + .endDate(end1) + .title("test1") + .likeCount(0) + .build(); + + SupportInfo info2 = SupportInfo.builder() + .startDate(start2) + .endDate(end2) + .title("test2") + .likeCount(0) + .build(); + + SupportInfo info3 = SupportInfo.builder() + .startDate(start3) + .endDate(end3) + .title("test3") + .likeCount(0) + .build(); + + SupportInfo info4 = SupportInfo.builder() + .startDate(start4) + .endDate(end4) + .title("test4") + .likeCount(0) + .build(); + + SupportInfo info5 = SupportInfo.builder() + .startDate(start5) + .endDate(end5) + .title("test5") + .likeCount(0) + .build(); + + supportInfoRepository.save(info1); + supportInfoRepository.save(info2); + supportInfoRepository.save(info3); + supportInfoRepository.save(info4); + supportInfoRepository.save(info5); + + LocalDate firstDay = LocalDate.of(2024, 8, 1); + LocalDate lastDay = firstDay.withDayOfMonth(firstDay.lengthOfMonth()); + + List result = supportInfoRepository.findByMonth(firstDay, lastDay); + + Assertions.assertThat(result.size()).isEqualTo(4); + Assertions.assertThat(result).containsExactlyInAnyOrder(info1, info2, info3, info5); + } + + @Test + void findRecommendInfoByMonthTest(){ + LocalDate start1 = LocalDate.of(2024, 8, 1); + LocalDate start2 = LocalDate.of(2024, 7, 20); + LocalDate start3 = LocalDate.of(2024, 8, 31); + LocalDate start4 = LocalDate.of(2024, 7, 20); + LocalDate start5 = LocalDate.of(2024, 9, 1); + + LocalDate end1 = LocalDate.of(2024, 8, 5); + LocalDate end2 = LocalDate.of(2024, 8, 3); + LocalDate end3 = LocalDate.of(2024, 9, 3); + LocalDate end4 = LocalDate.of(2024, 7, 24); + LocalDate end5 = LocalDate.of(2024, 9, 5); + + SupportInfo info1 = SupportInfo.builder() + .startDate(start1) + .endDate(end1) + .title("test1") + .likeCount(0) + .build(); + + SupportInfo info2 = SupportInfo.builder() + .startDate(start2) + .endDate(end2) + .title("test2") + .likeCount(0) + .build(); + + SupportInfo info3 = SupportInfo.builder() + .startDate(start3) + .endDate(end3) + .title("test3") + .likeCount(0) + .build(); + + SupportInfo info4 = SupportInfo.builder() + .startDate(start4) + .endDate(end4) + .title("test4") + .likeCount(0) + .build(); + + SupportInfo info5 = SupportInfo.builder() + .startDate(start5) + .endDate(end5) + .title("test5") + .likeCount(0) + .build(); + + supportInfoRepository.save(info1); + supportInfoRepository.save(info2); + supportInfoRepository.save(info3); + supportInfoRepository.save(info4); + supportInfoRepository.save(info5); + + for(int i = 0; i < 10; i++){ + info1.addLikeCount(); + } + for(int i = 0; i < 9; i++){ + info2.addLikeCount(); + } + for(int i = 0; i < 8; i++){ + info3.addLikeCount(); + } + for(int i = 0; i < 11; i++){ + info4.addLikeCount(); + } + for(int i = 0; i < 12; i++){ + info5.addLikeCount(); + } + LocalDate firstDay = LocalDate.of(2024, 8, 1); + LocalDate lastDay = firstDay.withDayOfMonth(firstDay.lengthOfMonth()); + + List result = supportInfoRepository.findRecommendInfoByMonth(firstDay, lastDay); + + Assertions.assertThat(result.size()).isEqualTo(2); + Assertions.assertThat(result).containsExactly(info1, info2); + + supportInfoRepository.delete(info3); + + List result2 = supportInfoRepository.findRecommendInfoByMonth(firstDay, lastDay); + + Assertions.assertThat(result2.size()).isEqualTo(2); + Assertions.assertThat(result2).containsExactly(info1, info2); + + supportInfoRepository.delete(info1); + + List result3 = supportInfoRepository.findRecommendInfoByMonth(firstDay, lastDay); + + Assertions.assertThat(result3.size()).isEqualTo(1); + Assertions.assertThat(result3).containsExactly(info2); + + + } +} \ No newline at end of file