diff --git a/smarketing-java/store/src/main/java/com/won/smarketing/store/config/JpaConfig.java b/smarketing-java/store/src/main/java/com/won/smarketing/store/config/JpaConfig.java index 4efd00c..3c7e2f9 100644 --- a/smarketing-java/store/src/main/java/com/won/smarketing/store/config/JpaConfig.java +++ b/smarketing-java/store/src/main/java/com/won/smarketing/store/config/JpaConfig.java @@ -1,5 +1,9 @@ package com.won.smarketing.store.config; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @@ -10,7 +14,6 @@ import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @Configuration @EnableJpaAuditing public class JpaConfig { -}리는 50자 이하여야 합니다") private String category; @Schema(description = "가격", example = "4500", required = true) diff --git a/smarketing-java/store/src/main/java/com/won/smarketing/store/controller/MenuController.java b/smarketing-java/store/src/main/java/com/won/smarketing/store/controller/MenuController.java index 9f87a8b..e5e3e4f 100644 --- a/smarketing-java/store/src/main/java/com/won/smarketing/store/controller/MenuController.java +++ b/smarketing-java/store/src/main/java/com/won/smarketing/store/controller/MenuController.java @@ -12,7 +12,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; +import jakarta.validation.Valid; import java.util.List; /** diff --git a/smarketing-java/store/src/main/java/com/won/smarketing/store/controller/StoreController.java b/smarketing-java/store/src/main/java/com/won/smarketing/store/controller/StoreController.java index f4dec35..dbd699e 100644 --- a/smarketing-java/store/src/main/java/com/won/smarketing/store/controller/StoreController.java +++ b/smarketing-java/store/src/main/java/com/won/smarketing/store/controller/StoreController.java @@ -12,7 +12,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; +import jakarta.validation.Valid; /** * 매장 관리를 위한 REST API 컨트롤러 diff --git a/smarketing-java/store/src/main/java/com/won/smarketing/store/dto/MenuUpdateRequest.java b/smarketing-java/store/src/main/java/com/won/smarketing/store/dto/MenuUpdateRequest.java index c10ac54..e597bc5 100644 --- a/smarketing-java/store/src/main/java/com/won/smarketing/store/dto/MenuUpdateRequest.java +++ b/smarketing-java/store/src/main/java/com/won/smarketing/store/dto/MenuUpdateRequest.java @@ -6,8 +6,8 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import javax.validation.constraints.Min; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Size; /** * 메뉴 수정 요청 DTO diff --git a/smarketing-java/store/src/main/java/com/won/smarketing/store/repository/SalesRepository.java b/smarketing-java/store/src/main/java/com/won/smarketing/store/repository/SalesRepository.java index c36d1c5..f34b853 100644 --- a/smarketing-java/store/src/main/java/com/won/smarketing/store/repository/SalesRepository.java +++ b/smarketing-java/store/src/main/java/com/won/smarketing/store/repository/SalesRepository.java @@ -7,6 +7,8 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.List; /** * 매출 정보 데이터 접근을 위한 Repository @@ -14,31 +16,52 @@ import java.math.BigDecimal; */ @Repository public interface SalesRepository extends JpaRepository { - + /** - * 매장의 오늘 매출 조회 - * + * 매장의 특정 날짜 매출 조회 + * + * @param storeId 매장 ID + * @param salesDate 매출 날짜 + * @return 해당 날짜 매출 목록 + */ + List findByStoreIdAndSalesDate(Long storeId, LocalDate salesDate); + + /** + * 매장의 특정 기간 매출 조회 + * + * @param storeId 매장 ID + * @param startDate 시작 날짜 + * @param endDate 종료 날짜 + * @return 해당 기간 매출 목록 + */ + List findByStoreIdAndSalesDateBetween(Long storeId, LocalDate startDate, LocalDate endDate); + + /** + * 매장의 오늘 매출 조회 (네이티브 쿼리) + * * @param storeId 매장 ID * @return 오늘 매출 */ - @Query("SELECT COALESCE(SUM(s.salesAmount), 0) FROM Sales s WHERE s.storeId = :storeId AND s.salesDate = CURRENT_DATE") - BigDecimal findTodaySalesByStoreId(@Param("storeId") Long storeId); - + @Query(value = "SELECT COALESCE(SUM(sales_amount), 0) FROM sales WHERE store_id = :storeId AND sales_date = CURRENT_DATE", nativeQuery = true) + BigDecimal findTodaySalesByStoreIdNative(@Param("storeId") Long storeId); + /** - * 매장의 이번 달 매출 조회 - * + * 매장의 어제 매출 조회 (네이티브 쿼리) + * + * @param storeId 매장 ID + * @return 어제 매출 + */ + @Query(value = "SELECT COALESCE(SUM(sales_amount), 0) FROM sales WHERE store_id = :storeId AND sales_date = CURRENT_DATE - INTERVAL '1 day'", nativeQuery = true) + BigDecimal findYesterdaySalesByStoreIdNative(@Param("storeId") Long storeId); + + /** + * 매장의 이번 달 매출 조회 (네이티브 쿼리) + * * @param storeId 매장 ID * @return 이번 달 매출 */ - @Query("SELECT COALESCE(SUM(s.salesAmount), 0) FROM Sales s WHERE s.storeId = :storeId AND YEAR(s.salesDate) = YEAR(CURRENT_DATE) AND MONTH(s.salesDate) = MONTH(CURRENT_DATE)") - BigDecimal findMonthSalesByStoreId(@Param("storeId") Long storeId); - - /** - * 매장의 전일 대비 매출 변화량 조회 - * - * @param storeId 매장 ID - * @return 전일 대비 매출 변화량 - */ - @Query("SELECT COALESCE((SELECT SUM(s1.salesAmount) FROM Sales s1 WHERE s1.storeId = :storeId AND s1.salesDate = CURRENT_DATE) - (SELECT SUM(s2.salesAmount) FROM Sales s2 WHERE s2.storeId = :storeId AND s2.salesDate = CURRENT_DATE - 1), 0)") - BigDecimal findPreviousDayComparisonByStoreId(@Param("storeId") Long storeId); -} + @Query(value = "SELECT COALESCE(SUM(sales_amount), 0) FROM sales WHERE store_id = :storeId " + + "AND EXTRACT(YEAR FROM sales_date) = EXTRACT(YEAR FROM CURRENT_DATE) " + + "AND EXTRACT(MONTH FROM sales_date) = EXTRACT(MONTH FROM CURRENT_DATE)", nativeQuery = true) + BigDecimal findMonthSalesByStoreIdNative(@Param("storeId") Long storeId); +} \ No newline at end of file diff --git a/smarketing-java/store/src/main/java/com/won/smarketing/store/service/MenuServiceImpl.java b/smarketing-java/store/src/main/java/com/won/smarketing/store/service/MenuServiceImpl.java index 3c66c15..7c31341 100644 --- a/smarketing-java/store/src/main/java/com/won/smarketing/store/service/MenuServiceImpl.java +++ b/smarketing-java/store/src/main/java/com/won/smarketing/store/service/MenuServiceImpl.java @@ -83,7 +83,7 @@ public class MenuServiceImpl implements MenuService { .orElseThrow(() -> new BusinessException(ErrorCode.MENU_NOT_FOUND)); // 메뉴 정보 업데이트 - menu.updateMenuInfo( + menu.updateMenu( request.getMenuName(), request.getCategory(), request.getPrice(), diff --git a/smarketing-java/store/src/main/java/com/won/smarketing/store/service/SalesServiceImpl.java b/smarketing-java/store/src/main/java/com/won/smarketing/store/service/SalesServiceImpl.java index 847b3f9..ded5564 100644 --- a/smarketing-java/store/src/main/java/com/won/smarketing/store/service/SalesServiceImpl.java +++ b/smarketing-java/store/src/main/java/com/won/smarketing/store/service/SalesServiceImpl.java @@ -1,60 +1,84 @@ package com.won.smarketing.store.service; import com.won.smarketing.store.dto.SalesResponse; +import com.won.smarketing.store.entity.Sales; +import com.won.smarketing.store.repository.SalesRepository; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; -import java.math.RoundingMode; +import java.time.LocalDate; +import java.util.List; /** - * 매출 서비스 구현체 - * 매출 조회 기능 구현 (현재는 Mock 데이터) + * 매출 관리 서비스 구현체 + * 매출 조회 기능 구현 */ -@Slf4j @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class SalesServiceImpl implements SalesService { + private final SalesRepository salesRepository; + /** * 매출 정보 조회 - * 현재는 Mock 데이터를 반환 (실제로는 매출 데이터 조회 로직 필요) - * - * @return 매출 정보 + * + * @return 매출 정보 (오늘, 월간, 전일 대비) */ @Override public SalesResponse getSales() { - log.info("매출 정보 조회"); - - // Mock 데이터 (실제로는 데이터베이스에서 조회) - BigDecimal todaySales = new BigDecimal("150000"); - BigDecimal monthSales = new BigDecimal("4500000"); - BigDecimal yesterdaySales = new BigDecimal("125000"); - BigDecimal targetSales = new BigDecimal("176000"); - - // 전일 대비 변화 계산 + // TODO: 현재는 더미 데이터 반환, 실제로는 현재 로그인한 사용자의 매장 ID를 사용해야 함 + Long storeId = 1L; // 임시로 설정 + + // 오늘 매출 계산 + BigDecimal todaySales = calculateSalesByDate(storeId, LocalDate.now()); + + // 이번 달 매출 계산 + BigDecimal monthSales = calculateMonthSales(storeId); + + // 어제 매출 계산 + BigDecimal yesterdaySales = calculateSalesByDate(storeId, LocalDate.now().minusDays(1)); + + // 전일 대비 매출 변화량 계산 BigDecimal previousDayComparison = todaySales.subtract(yesterdaySales); - BigDecimal previousDayChangeRate = yesterdaySales.compareTo(BigDecimal.ZERO) > 0 - ? previousDayComparison.divide(yesterdaySales, 4, RoundingMode.HALF_UP) - .multiply(new BigDecimal("100")) - : BigDecimal.ZERO; - - // 목표 대비 달성률 계산 - BigDecimal goalAchievementRate = targetSales.compareTo(BigDecimal.ZERO) > 0 - ? todaySales.divide(targetSales, 4, RoundingMode.HALF_UP) - .multiply(new BigDecimal("100")) - : BigDecimal.ZERO; - + return SalesResponse.builder() .todaySales(todaySales) .monthSales(monthSales) .previousDayComparison(previousDayComparison) - .previousDayChangeRate(previousDayChangeRate) - .goalAchievementRate(goalAchievementRate) .build(); } -} + /** + * 특정 날짜의 매출 계산 + * + * @param storeId 매장 ID + * @param date 날짜 + * @return 해당 날짜 매출 + */ + private BigDecimal calculateSalesByDate(Long storeId, LocalDate date) { + List salesList = salesRepository.findByStoreIdAndSalesDate(storeId, date); + return salesList.stream() + .map(Sales::getSalesAmount) + .reduce(BigDecimal.ZERO, BigDecimal::add); + } + + /** + * 이번 달 매출 계산 + * + * @param storeId 매장 ID + * @return 이번 달 매출 + */ + private BigDecimal calculateMonthSales(Long storeId) { + LocalDate now = LocalDate.now(); + LocalDate startOfMonth = now.withDayOfMonth(1); + LocalDate endOfMonth = now.withDayOfMonth(now.lengthOfMonth()); + + List salesList = salesRepository.findByStoreIdAndSalesDateBetween(storeId, startOfMonth, endOfMonth); + return salesList.stream() + .map(Sales::getSalesAmount) + .reduce(BigDecimal.ZERO, BigDecimal::add); + } +} \ No newline at end of file diff --git a/smarketing-java/store/src/main/resources/application.yml b/smarketing-java/store/src/main/resources/application.yml index 800c27d..8140918 100644 --- a/smarketing-java/store/src/main/resources/application.yml +++ b/smarketing-java/store/src/main/resources/application.yml @@ -7,7 +7,7 @@ spring: application: name: store-service datasource: - url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:storedb} + url: jdbc:postgresql://${POSTGRES_HOST:localhost}:${POSTGRES_PORT:5432}/${POSTGRES_DB:StoreDB} username: ${POSTGRES_USER:postgres} password: ${POSTGRES_PASSWORD:postgres} jpa: @@ -18,6 +18,11 @@ spring: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect format_sql: true + data: + redis: + host: ${REDIS_HOST:localhost} + port: ${REDIS_PORT:6379} + password: ${REDIS_PASSWORD:} springdoc: swagger-ui: @@ -29,3 +34,8 @@ springdoc: logging: level: com.won.smarketing.store: ${LOG_LEVEL:DEBUG} + +jwt: + secret: ${JWT_SECRET:mySecretKeyForJWTTokenGenerationAndValidation123456789} + access-token-validity: ${JWT_ACCESS_VALIDITY:3600000} + refresh-token-validity: ${JWT_REFRESH_VALIDITY:604800000} \ No newline at end of file