diff --git a/docker-compose.yml b/docker-compose.yml index b44bc1a..816f9ca 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,23 +4,26 @@ services: build: . container_name: analytics-app ports: - - "8080:8080" + - "8083:8080" environment: SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/analytics_db_dev SPRING_DATASOURCE_USERNAME: postgres SPRING_DATASOURCE_PASSWORD: 123 - SPRING_RABBITMQ_HOST: rabbitmq # RabbitMQ container name or use its IP if needed + SPRING_RABBITMQ_HOST: host.docker.internal SPRING_RABBITMQ_PORT: 5672 SPRING_RABBITMQ_USERNAME: guest SPRING_RABBITMQ_PASSWORD: guest depends_on: - db + extra_hosts: + - "host.docker.internal:host-gateway" + db: image: postgres container_name: analytics-db ports: - - "5432:5432" + - "5435:5432" environment: POSTGRES_DB: analytics_db_dev POSTGRES_USER: postgres diff --git a/src/main/java/com/Podzilla/analytics/api/controllers/CourierReportController.java b/src/main/java/com/Podzilla/analytics/api/controllers/CourierReportController.java index 2fa9cc5..cf35201 100644 --- a/src/main/java/com/Podzilla/analytics/api/controllers/CourierReportController.java +++ b/src/main/java/com/Podzilla/analytics/api/controllers/CourierReportController.java @@ -20,12 +20,14 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @Tag(name = "Courier Reports", description = "Endpoints for courier" + " analytics and performance metrics") @RestController @RequestMapping("/courier-analytics") @RequiredArgsConstructor +@Slf4j public final class CourierReportController { private final CourierAnalyticsService courierAnalyticsService; @@ -37,6 +39,8 @@ public final class CourierReportController { @GetMapping("/delivery-counts") public ResponseEntity> getDeliveryCounts( @Valid @ModelAttribute final DateRangeRequest dateRange) { + log.info("Request on: /courier-analytics/delivery-counts " + + "with attributes: {}", dateRange); List counts = courierAnalyticsService .getCourierDeliveryCounts(dateRange.getStartDate(), dateRange.getEndDate()); @@ -48,6 +52,9 @@ public ResponseEntity> getDeliveryCounts( @GetMapping("/success-rate") public ResponseEntity> getSuccessRate( @Valid @ModelAttribute final DateRangeRequest dateRange) { + log.info("Request on: /courier-analytics/success-rate " + + "with attributes: {}", + dateRange); List rates = courierAnalyticsService .getCourierSuccessRate(dateRange.getStartDate(), dateRange.getEndDate()); @@ -59,6 +66,8 @@ public ResponseEntity> getSuccessRate( @GetMapping("/average-rating") public ResponseEntity> getAverageRating( @Valid @ModelAttribute final DateRangeRequest dateRange) { + log.info("Request on: /courier-analytics/average-rating " + + "with attributes: {}", dateRange); List ratings = courierAnalyticsService .getCourierAverageRating(dateRange.getStartDate(), dateRange.getEndDate()); @@ -70,6 +79,8 @@ public ResponseEntity> getAverageRating( @GetMapping("/performance-report") public ResponseEntity> getReport( @Valid @ModelAttribute final DateRangeRequest dateRange) { + log.info("Request on: /courier-analytics/performance-report " + + "with attributes: {}", dateRange); List report = courierAnalyticsService .getCourierPerformanceReport(dateRange.getStartDate(), dateRange.getEndDate()); diff --git a/src/main/java/com/Podzilla/analytics/api/controllers/CustomerReportController.java b/src/main/java/com/Podzilla/analytics/api/controllers/CustomerReportController.java index 8c167f7..3b3055b 100644 --- a/src/main/java/com/Podzilla/analytics/api/controllers/CustomerReportController.java +++ b/src/main/java/com/Podzilla/analytics/api/controllers/CustomerReportController.java @@ -13,6 +13,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import java.util.List; @@ -21,6 +22,7 @@ @RequiredArgsConstructor @RestController @RequestMapping("/customer-analytics") +@Slf4j public class CustomerReportController { private final CustomerAnalyticsService customerAnalyticsService; @@ -30,6 +32,8 @@ public class CustomerReportController { @GetMapping("/top-spenders") public List getTopSpenders( @Valid @ModelAttribute final DateRangePaginationRequest request) { + log.info("Request on: /customer-analytics/top-spenders " + + "with attributes: {}", request); return customerAnalyticsService.getTopSpenders( request.getStartDate(), request.getEndDate(), diff --git a/src/main/java/com/Podzilla/analytics/api/controllers/FulfillmentReportController.java b/src/main/java/com/Podzilla/analytics/api/controllers/FulfillmentReportController.java index b7fc570..1f452d1 100644 --- a/src/main/java/com/Podzilla/analytics/api/controllers/FulfillmentReportController.java +++ b/src/main/java/com/Podzilla/analytics/api/controllers/FulfillmentReportController.java @@ -25,16 +25,17 @@ public class FulfillmentReportController { private final FulfillmentAnalyticsService fulfillmentAnalyticsService; - @Operation( - summary = "Get average time from order placement to shipping", - description = "Returns the average time (in hours) between when" + @Operation(summary = "Get average time from order placement to shipping", + description = "Returns the average time (in hours) between when" + " an order was placed and when it was shipped, grouped" - + " by the specified dimension" - ) + + " by the specified dimension") @GetMapping("/place-to-ship-time") public ResponseEntity> getPlaceToShipTime( @Valid @ModelAttribute final FulfillmentPlaceToShipRequest req) { + log.info("Request on: /fulfillment-analytics/place-to-ship-time " + + "with attributes: {}", req); + final List reportData = fulfillmentAnalyticsService.getPlaceToShipTimeResponse( req.getStartDate(), @@ -44,22 +45,22 @@ public ResponseEntity> getPlaceToShipTime( return ResponseEntity.ok(reportData); } - - @Operation( - summary = "Get average time from shipping to delivery", - description = "Returns the average time (in hours) between when" + @Operation(summary = "Get average time from shipping to delivery", + description = "Returns the average time (in hours) between when" + " an order was shipped and when it was delivered, grouped" - + " by the specified dimension" - ) + + " by the specified dimension") @GetMapping("/ship-to-deliver-time") public ResponseEntity> getShipToDeliverTime( @Valid @ModelAttribute final FulfillmentShipToDeliverRequest req) { + log.info("Request on: /fulfillment-analytics/ship-to-deliver-time " + + "with attributes: {}", req); + final List reportData = - fulfillmentAnalyticsService.getShipToDeliverTimeResponse( - req.getStartDate(), - req.getEndDate(), - req.getGroupBy()); + fulfillmentAnalyticsService.getShipToDeliverTimeResponse( + req.getStartDate(), + req.getEndDate(), + req.getGroupBy()); return ResponseEntity.ok(reportData); } } diff --git a/src/main/java/com/Podzilla/analytics/api/controllers/InventoryReportController.java b/src/main/java/com/Podzilla/analytics/api/controllers/InventoryReportController.java index 8820458..31a6858 100644 --- a/src/main/java/com/Podzilla/analytics/api/controllers/InventoryReportController.java +++ b/src/main/java/com/Podzilla/analytics/api/controllers/InventoryReportController.java @@ -13,6 +13,7 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -23,6 +24,7 @@ @RequiredArgsConstructor @RestController @RequestMapping("/inventory-analytics") +@Slf4j public class InventoryReportController { private final InventoryAnalyticsService inventoryAnalyticsService; @@ -31,8 +33,10 @@ public class InventoryReportController { + "the total value of inventory " + "grouped by product categories") @GetMapping("/value/by-category") - public List - getInventoryValueByCategory() { + public List getInventoryValueByCategory( + + ) { + log.info("Request on: /inventory-analytics/value/by-category"); return inventoryAnalyticsService.getInventoryValueByCategory(); } @@ -41,6 +45,8 @@ public class InventoryReportController { @GetMapping("/low-stock") public Page getLowStockProducts( @Valid @ModelAttribute final PaginationRequest paginationRequest) { + log.info("Request on: /inventory-analytics/low-stock" + + " with attributes: {}", paginationRequest); return inventoryAnalyticsService.getLowStockProducts( paginationRequest.getPage(), paginationRequest.getSize()); diff --git a/src/main/java/com/Podzilla/analytics/api/controllers/OrderReportController.java b/src/main/java/com/Podzilla/analytics/api/controllers/OrderReportController.java index 5ba95bf..6e843ae 100644 --- a/src/main/java/com/Podzilla/analytics/api/controllers/OrderReportController.java +++ b/src/main/java/com/Podzilla/analytics/api/controllers/OrderReportController.java @@ -10,6 +10,7 @@ import io.swagger.v3.oas.annotations.Operation; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; import java.util.List; @@ -19,7 +20,7 @@ import com.Podzilla.analytics.api.dtos.order.OrderRegionResponse; import com.Podzilla.analytics.api.dtos.order.OrderStatusResponse; - +@Slf4j @RequiredArgsConstructor @RestController @RequestMapping("/order-analytics") @@ -27,48 +28,48 @@ public class OrderReportController { private final OrderAnalyticsService orderAnalyticsService; @Operation(summary = "Get order counts and revenue by region", - description = "Returns the total number of orders" - + "placed in each region and their corresponding average revenue") + description = "Returns the total number of orders" + + "placed in each region and their corresponding average revenue") @GetMapping("/by-region") public ResponseEntity> getOrdersByRegion( - @Valid @ModelAttribute final DateRangeRequest dateRange - ) { - List ordersByRegion = - orderAnalyticsService.getOrdersByRegion( - dateRange.getStartDate(), - dateRange.getEndDate() - ); + @Valid @ModelAttribute final DateRangeRequest dateRange) { + log.info("Request on: /order-analytics/by-region" + + " with attributes: {}", dateRange); + List ordersByRegion = orderAnalyticsService + .getOrdersByRegion( + dateRange.getStartDate(), + dateRange.getEndDate()); return ResponseEntity.ok(ordersByRegion); } @Operation(summary = "Get order status counts", - description = "Returns the total number of orders" - + "in each status (e.g., COMPLETED, SHIPPED, FAILED)") + description = "Returns the total number of orders" + + "in each status (e.g., COMPLETED, SHIPPED, FAILED)") @GetMapping("/status-counts") public ResponseEntity> getOrdersStatusCounts( - @Valid @ModelAttribute final DateRangeRequest dateRange - ) { - List orderStatusCounts = - orderAnalyticsService.getOrdersStatusCounts( - dateRange.getStartDate(), - dateRange.getEndDate() - ); + @Valid @ModelAttribute final DateRangeRequest dateRange) { + log.info("Request on: /order-analytics/status-counts" + + " with attributes: {}", dateRange); + List orderStatusCounts = orderAnalyticsService + .getOrdersStatusCounts( + dateRange.getStartDate(), + dateRange.getEndDate()); return ResponseEntity.ok(orderStatusCounts); } @Operation(summary = "Get order failures", - description = "Returns the percentage of failed orders" - + "and a list of the failure reasons" - + "with their corresponding frequency") + description = "Returns the percentage of failed orders" + + "and a list of the failure reasons" + + "with their corresponding frequency") @GetMapping("/failures") public ResponseEntity getOrdersFailures( - @Valid @ModelAttribute final DateRangeRequest dateRange - ) { - OrderFailureResponse orderFailures = - orderAnalyticsService.getOrdersFailures( - dateRange.getStartDate(), - dateRange.getEndDate() - ); + @Valid @ModelAttribute final DateRangeRequest dateRange) { + log.info("Request on: /order-analytics/failures" + + " with attributes: {}", dateRange); + OrderFailureResponse orderFailures = orderAnalyticsService + .getOrdersFailures( + dateRange.getStartDate(), + dateRange.getEndDate()); return ResponseEntity.ok(orderFailures); } } diff --git a/src/main/java/com/Podzilla/analytics/api/controllers/ProductReportController.java b/src/main/java/com/Podzilla/analytics/api/controllers/ProductReportController.java index b5180d4..388c46f 100644 --- a/src/main/java/com/Podzilla/analytics/api/controllers/ProductReportController.java +++ b/src/main/java/com/Podzilla/analytics/api/controllers/ProductReportController.java @@ -14,10 +14,11 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @RequiredArgsConstructor @RestController - +@Slf4j @RequestMapping("/product-analytics") public class ProductReportController { @@ -27,6 +28,9 @@ public class ProductReportController { public ResponseEntity> getTopSellers( @Valid @ModelAttribute final TopSellerRequest requestDTO) { + log.info("Request on: /product-analytics/top-sellers" + + " with attributes: {}", requestDTO); + List topSellersList = productAnalyticsService .getTopSellers(requestDTO.getStartDate(), requestDTO.getEndDate(), diff --git a/src/main/java/com/Podzilla/analytics/api/controllers/ProfitReportController.java b/src/main/java/com/Podzilla/analytics/api/controllers/ProfitReportController.java index 4e81ea2..78ac65e 100644 --- a/src/main/java/com/Podzilla/analytics/api/controllers/ProfitReportController.java +++ b/src/main/java/com/Podzilla/analytics/api/controllers/ProfitReportController.java @@ -28,17 +28,18 @@ public class ProfitReportController { private final ProfitAnalyticsService profitAnalyticsService; - - @Operation( - summary = "Get profit by product category", - description = "Returns the revenue, cost, and profit metrics " - + "grouped by product category") + @Operation(summary = "Get profit by product category", + description = "Returns the revenue, cost, and profit metrics " + + "grouped by product category") @GetMapping("/by-category") public ResponseEntity> getProfitByCategory( @Valid @ModelAttribute final DateRangeRequest request) { - List profitData = - profitAnalyticsService.getProfitByCategory( + log.info("Request on: /profit-analytics/by-category" + + " with attributes: {}", request); + + List profitData = profitAnalyticsService + .getProfitByCategory( request.getStartDate(), request.getEndDate()); return ResponseEntity.ok(profitData); diff --git a/src/main/java/com/Podzilla/analytics/api/controllers/RevenueReportController.java b/src/main/java/com/Podzilla/analytics/api/controllers/RevenueReportController.java index b2c6555..88fe2f1 100644 --- a/src/main/java/com/Podzilla/analytics/api/controllers/RevenueReportController.java +++ b/src/main/java/com/Podzilla/analytics/api/controllers/RevenueReportController.java @@ -16,7 +16,9 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +@Slf4j @RequiredArgsConstructor @RestController @RequestMapping("/revenue-analytics") @@ -26,6 +28,8 @@ public class RevenueReportController { @GetMapping("/summary") public ResponseEntity> getRevenueSummary( @Valid @ModelAttribute final RevenueSummaryRequest requestDTO) { + log.info("Request on: /revenue-analytics/summary" + + " with attributes: {}", requestDTO); return ResponseEntity.ok(revenueReportService .getRevenueSummary(requestDTO.getStartDate(), requestDTO.getEndDate(), @@ -35,6 +39,8 @@ public ResponseEntity> getRevenueSummary( @GetMapping("/by-category") public ResponseEntity> getRevenueByCategory( @Valid @ModelAttribute final RevenueByCategoryRequest requestDTO) { + log.info("Request on: /revenue-analytics/by-category" + + " with attributes: {}", requestDTO); List summaryList = revenueReportService .getRevenueByCategory( requestDTO.getStartDate(), diff --git a/src/main/java/com/Podzilla/analytics/api/dtos/DateRangePaginationRequest.java b/src/main/java/com/Podzilla/analytics/api/dtos/DateRangePaginationRequest.java index 7bede34..f27a6c8 100644 --- a/src/main/java/com/Podzilla/analytics/api/dtos/DateRangePaginationRequest.java +++ b/src/main/java/com/Podzilla/analytics/api/dtos/DateRangePaginationRequest.java @@ -8,12 +8,12 @@ import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; -import lombok.Getter; +import lombok.Data; import io.swagger.v3.oas.annotations.media.Schema; @ValidDateRange @ValidPagination -@Getter +@Data @AllArgsConstructor public class DateRangePaginationRequest implements IDateRangeRequest, IPaginationRequest { diff --git a/src/main/java/com/Podzilla/analytics/api/dtos/DateRangeRequest.java b/src/main/java/com/Podzilla/analytics/api/dtos/DateRangeRequest.java index 586c69f..c372a3e 100644 --- a/src/main/java/com/Podzilla/analytics/api/dtos/DateRangeRequest.java +++ b/src/main/java/com/Podzilla/analytics/api/dtos/DateRangeRequest.java @@ -8,11 +8,11 @@ import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; -import lombok.Getter; +import lombok.Data; import io.swagger.v3.oas.annotations.media.Schema; @ValidDateRange -@Getter +@Data @AllArgsConstructor public class DateRangeRequest implements IDateRangeRequest { diff --git a/src/main/java/com/Podzilla/analytics/api/dtos/PaginationRequest.java b/src/main/java/com/Podzilla/analytics/api/dtos/PaginationRequest.java index 8cb6f79..15c8ea8 100644 --- a/src/main/java/com/Podzilla/analytics/api/dtos/PaginationRequest.java +++ b/src/main/java/com/Podzilla/analytics/api/dtos/PaginationRequest.java @@ -1,14 +1,14 @@ package com.Podzilla.analytics.api.dtos; import jakarta.validation.constraints.Min; import lombok.AllArgsConstructor; -import lombok.Getter; +import lombok.Data; import com.Podzilla.analytics.validation.annotations.ValidPagination; import io.swagger.v3.oas.annotations.media.Schema; @ValidPagination -@Getter +@Data @AllArgsConstructor public class PaginationRequest implements IPaginationRequest { diff --git a/src/main/java/com/Podzilla/analytics/api/dtos/order/OrderRegionResponse.java b/src/main/java/com/Podzilla/analytics/api/dtos/order/OrderRegionResponse.java index fc924fc..cf2f9f2 100644 --- a/src/main/java/com/Podzilla/analytics/api/dtos/order/OrderRegionResponse.java +++ b/src/main/java/com/Podzilla/analytics/api/dtos/order/OrderRegionResponse.java @@ -6,7 +6,6 @@ import java.math.BigDecimal; import io.swagger.v3.oas.annotations.media.Schema; -import java.util.UUID; @Data @Builder @@ -14,10 +13,6 @@ @AllArgsConstructor public class OrderRegionResponse { - @Schema(description = "Region ID", - example = "4731e9e0-c627-43f9-808a-7e8637abb912") - private UUID regionId; - @Schema(description = "city name", example = "Metropolis") private String city; diff --git a/src/main/java/com/Podzilla/analytics/api/projections/order/OrderRegionProjection.java b/src/main/java/com/Podzilla/analytics/api/projections/order/OrderRegionProjection.java index b15e7c3..c1e9acc 100644 --- a/src/main/java/com/Podzilla/analytics/api/projections/order/OrderRegionProjection.java +++ b/src/main/java/com/Podzilla/analytics/api/projections/order/OrderRegionProjection.java @@ -1,10 +1,8 @@ package com.Podzilla.analytics.api.projections.order; import java.math.BigDecimal; -import java.util.UUID; public interface OrderRegionProjection { - UUID getRegionId(); String getCity(); String getCountry(); Long getOrderCount(); diff --git a/src/main/java/com/Podzilla/analytics/config/DatabaseSeeder.java b/src/main/java/com/Podzilla/analytics/config/DatabaseSeeder.java index 13a8b9d..57eb4a0 100644 --- a/src/main/java/com/Podzilla/analytics/config/DatabaseSeeder.java +++ b/src/main/java/com/Podzilla/analytics/config/DatabaseSeeder.java @@ -257,7 +257,7 @@ private void seedOrders( .id(UUID.randomUUID()) .customer(customers.get(1)).courier(couriers.get(1)) .region(regions.get(1)) - .status(Order.OrderStatus.DELIVERED) + .status(Order.OrderStatus.SHIPPED) .orderPlacedTimestamp(placed2) .shippedTimestamp(placed2.plusDays(ORDER_2_SHIP_DAYS) .plusHours(ORDER_2_SHIP_HOURS)) @@ -284,7 +284,7 @@ private void seedOrders( .customer(customers.get(0)).courier(couriers.get(0)) .region(regions.get(2)) .orderPlacedTimestamp(placed3) - .status(Order.OrderStatus.DELIVERED) + .status(Order.OrderStatus.DELIVERY_FAILED) .orderPlacedTimestamp(placed3) .shippedTimestamp(placed3.plusHours(ORDER_3_SHIP_HOURS)) .deliveredTimestamp(null) diff --git a/src/main/java/com/Podzilla/analytics/messaging/AnalyticsRabbitListener.java b/src/main/java/com/Podzilla/analytics/messaging/AnalyticsRabbitListener.java index 5208367..78ec3a9 100644 --- a/src/main/java/com/Podzilla/analytics/messaging/AnalyticsRabbitListener.java +++ b/src/main/java/com/Podzilla/analytics/messaging/AnalyticsRabbitListener.java @@ -3,13 +3,11 @@ import org.springframework.amqp.rabbit.annotation.RabbitListener; import com.podzilla.mq.EventsConstants; import org.springframework.beans.factory.annotation.Autowired; - import com.podzilla.mq.events.BaseEvent; - import org.springframework.stereotype.Service; +import lombok.extern.slf4j.Slf4j; - - +@Slf4j @Service public class AnalyticsRabbitListener { @@ -20,6 +18,7 @@ public class AnalyticsRabbitListener { queues = EventsConstants.ANALYTICS_USER_EVENT_QUEUE ) public void handleUserEvents(final BaseEvent userEvent) { + log.info("Received user event: {}", userEvent); dispatcher.dispatch(userEvent); } @@ -27,6 +26,7 @@ public void handleUserEvents(final BaseEvent userEvent) { queues = EventsConstants.ANALYTICS_ORDER_EVENT_QUEUE ) public void handleOrderEvents(final BaseEvent orderEvent) { + log.info("Received order event: {}", orderEvent); dispatcher.dispatch(orderEvent); } @@ -34,6 +34,7 @@ public void handleOrderEvents(final BaseEvent orderEvent) { queues = EventsConstants.ANALYTICS_INVENTORY_EVENT_QUEUE ) public void handleInventoryEvents(final BaseEvent inventoryEvent) { + log.info("Received inventory event: {}", inventoryEvent); dispatcher.dispatch(inventoryEvent); } } diff --git a/src/main/java/com/Podzilla/analytics/repositories/OrderRepository.java b/src/main/java/com/Podzilla/analytics/repositories/OrderRepository.java index b85869a..6fabe47 100644 --- a/src/main/java/com/Podzilla/analytics/repositories/OrderRepository.java +++ b/src/main/java/com/Podzilla/analytics/repositories/OrderRepository.java @@ -83,8 +83,7 @@ List findShipToDeliverTimeByCourier( @Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate); - @Query("SELECT r.id AS regionId, " - + "r.city AS city, " + @Query("SELECT r.city AS city, " + "r.country AS country, " + "COUNT(o) AS orderCount, " + "AVG(o.totalAmount) AS averageOrderValue " @@ -92,7 +91,7 @@ List findShipToDeliverTimeByCourier( + "JOIN o.region r " + "WHERE o.finalStatusTimestamp BETWEEN :startDate AND :endDate " + "AND o.status = 'DELIVERED' " - + "GROUP BY r.id, r.city, r.country " + + "GROUP BY r.city, r.country " + "ORDER BY orderCount DESC, averageOrderValue DESC") List findOrdersByRegion( @Param("startDate") LocalDateTime startDate, @@ -119,9 +118,9 @@ List findFailureReasons( @Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate); - @Query("SELECT (SUM(CASE WHEN o.status = 'DELIVERY_FAILED' " + @Query("SELECT COALESCE(SUM(CASE WHEN o.status = 'DELIVERY_FAILED' " + "THEN 1 ELSE 0 END) * 1.0 " - + "/ COUNT(o)) AS failureRate " + + "/ NULLIF(COUNT(o), 0), 0) AS failureRate " + "FROM Order o " + "WHERE o.finalStatusTimestamp BETWEEN :startDate AND :endDate") OrderFailureRateProjection calculateFailureRate( diff --git a/src/main/java/com/Podzilla/analytics/services/CourierAnalyticsService.java b/src/main/java/com/Podzilla/analytics/services/CourierAnalyticsService.java index 33033e2..0238f3a 100644 --- a/src/main/java/com/Podzilla/analytics/services/CourierAnalyticsService.java +++ b/src/main/java/com/Podzilla/analytics/services/CourierAnalyticsService.java @@ -19,11 +19,13 @@ import com.Podzilla.analytics.util.StringToUUIDParser; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @RequiredArgsConstructor @Service +@Slf4j public class CourierAnalyticsService { - private final CourierRepository courierRepository; + private final CourierRepository courierRepository; private List getCourierPerformanceData( final LocalDate startDate, @@ -40,74 +42,78 @@ private List getCourierPerformanceData( public List getCourierDeliveryCounts( final LocalDate startDate, - final LocalDate endDate - ) { - return getCourierPerformanceData(startDate, endDate).stream() - .map(data -> CourierDeliveryCountResponse.builder() - .courierId(data.getCourierId()) - .courierName(data.getCourierName()) - .deliveryCount(data.getDeliveryCount()) - .build()) - .toList(); - } + final LocalDate endDate) { + log.info("Getting courier delivery counts between {} and {}", + startDate, endDate); + return getCourierPerformanceData(startDate, endDate).stream() + .map(data -> CourierDeliveryCountResponse.builder() + .courierId(data.getCourierId()) + .courierName(data.getCourierName()) + .deliveryCount(data.getDeliveryCount()) + .build()) + .toList(); + } - public List getCourierSuccessRate( + public List getCourierSuccessRate( final LocalDate startDate, - final LocalDate endDate - ) { - return getCourierPerformanceData(startDate, endDate).stream() - .map(data -> CourierSuccessRateResponse.builder() - .courierId(data.getCourierId()) - .courierName(data.getCourierName()) - .successRate( - MetricCalculator.calculateRate( - data.getCompletedCount(), - data.getDeliveryCount())) - .build()) - .toList(); - } + final LocalDate endDate) { + log.info("Getting courier success rates between {} and {}", + startDate, endDate); + return getCourierPerformanceData(startDate, endDate).stream() + .map(data -> CourierSuccessRateResponse.builder() + .courierId(data.getCourierId()) + .courierName(data.getCourierName()) + .successRate( + MetricCalculator.calculateRate( + data.getCompletedCount(), + data.getDeliveryCount())) + .build()) + .toList(); + } - public List getCourierAverageRating( + public List getCourierAverageRating( final LocalDate startDate, - final LocalDate endDate - ) { - return getCourierPerformanceData(startDate, endDate).stream() - .map(data -> CourierAverageRatingResponse.builder() - .courierId(data.getCourierId()) - .courierName(data.getCourierName()) - .averageRating(data.getAverageRating()) - .build()) - .toList(); - } + final LocalDate endDate) { + log.info("Getting courier average ratings between {} and {}", + startDate, endDate); + return getCourierPerformanceData(startDate, endDate).stream() + .map(data -> CourierAverageRatingResponse.builder() + .courierId(data.getCourierId()) + .courierName(data.getCourierName()) + .averageRating(data.getAverageRating()) + .build()) + .toList(); + } - public List - getCourierPerformanceReport( + public List getCourierPerformanceReport( final LocalDate startDate, - final LocalDate endDate - ) { - return getCourierPerformanceData(startDate, endDate).stream() - .map(data -> CourierPerformanceReportResponse.builder() - .courierId(data.getCourierId()) - .courierName(data.getCourierName()) - .deliveryCount(data.getDeliveryCount()) - .successRate( - MetricCalculator.calculateRate( - data.getCompletedCount(), - data.getDeliveryCount())) - .averageRating(data.getAverageRating()) - .build()) - .toList(); + final LocalDate endDate) { + log.info("Getting courier performance report between {} and {}", + startDate, endDate); + return getCourierPerformanceData(startDate, endDate).stream() + .map(data -> CourierPerformanceReportResponse.builder() + .courierId(data.getCourierId()) + .courierName(data.getCourierName()) + .deliveryCount(data.getDeliveryCount()) + .successRate( + MetricCalculator.calculateRate( + data.getCompletedCount(), + data.getDeliveryCount())) + .averageRating(data.getAverageRating()) + .build()) + .toList(); } - public void saveCourier( - final String courierId, - final String courierName - ) { - UUID id = StringToUUIDParser.parseStringToUUID(courierId); - Courier courier = Courier.builder() - .id(id) - .name(courierName) - .build(); - courierRepository.save(courier); - } + public void saveCourier( + final String courierId, + final String courierName) { + log.info("Saving courier with id: {} and name: {}", + courierId, courierName); + UUID id = StringToUUIDParser.parseStringToUUID(courierId); + Courier courier = Courier.builder() + .id(id) + .name(courierName) + .build(); + courierRepository.save(courier); + } } diff --git a/src/main/java/com/Podzilla/analytics/services/CustomerAnalyticsService.java b/src/main/java/com/Podzilla/analytics/services/CustomerAnalyticsService.java index 0afeebb..2f14ea0 100644 --- a/src/main/java/com/Podzilla/analytics/services/CustomerAnalyticsService.java +++ b/src/main/java/com/Podzilla/analytics/services/CustomerAnalyticsService.java @@ -10,6 +10,7 @@ import com.Podzilla.analytics.models.Customer; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import java.time.LocalDateTime; import java.time.LocalDate; @@ -18,42 +19,45 @@ @Service @RequiredArgsConstructor +@Slf4j public class CustomerAnalyticsService { - private final CustomerRepository customerRepository; + private final CustomerRepository customerRepository; - public List getTopSpenders( - final LocalDate startDate, - final LocalDate endDate, - final int page, - final int size) { - LocalDateTime startDateTime = DatetimeFormatter - .convertStartDateToDatetime(startDate); - LocalDateTime endDateTime = DatetimeFormatter - .convertEndDateToDatetime(endDate); - PageRequest pageRequest = PageRequest.of(page, size); -List topSpenders = customerRepository -.findTopSpenders(startDateTime, endDateTime, pageRequest) -.stream() -.map(row -> CustomersTopSpendersResponse.builder() -.customerId(row.getCustomerId()) -.customerName(row.getCustomerName()) -.totalSpending(row.getTotalSpending()) -.build()) -.toList(); - return topSpenders; - } + public List getTopSpenders( + final LocalDate startDate, + final LocalDate endDate, + final int page, + final int size) { + log.info("Getting top spenders between {} and {}, page: {}," + + " size: {}", startDate, endDate, page, size); + LocalDateTime startDateTime = DatetimeFormatter + .convertStartDateToDatetime(startDate); + LocalDateTime endDateTime = DatetimeFormatter + .convertEndDateToDatetime(endDate); + PageRequest pageRequest = PageRequest.of(page, size); + List topSpenders = customerRepository + .findTopSpenders(startDateTime, endDateTime, pageRequest) + .stream() + .map(row -> CustomersTopSpendersResponse.builder() + .customerId(row.getCustomerId()) + .customerName(row.getCustomerName()) + .totalSpending(row.getTotalSpending()) + .build()) + .toList(); + log.info("Found {} top spenders", topSpenders.size()); + return topSpenders; + } - public void saveCustomer( - final String customerId, - final String customerName - ) { - UUID id = StringToUUIDParser.parseStringToUUID(customerId); - Customer customer = Customer.builder() - .id(id) - .name(customerName) - .build(); - System.out.println("Customer object created: " - + customer.getName() + " with ID: " + customer.getId()); - customerRepository.save(customer); - } + public void saveCustomer( + final String customerId, + final String customerName) { + UUID id = StringToUUIDParser.parseStringToUUID(customerId); + Customer customer = Customer.builder() + .id(id) + .name(customerName) + .build(); + log.info("Saving customer: {} with ID: {}", + customer.getName(), customer.getId()); + customerRepository.save(customer); + } } diff --git a/src/main/java/com/Podzilla/analytics/services/FulfillmentAnalyticsService.java b/src/main/java/com/Podzilla/analytics/services/FulfillmentAnalyticsService.java index 5fca47b..67f47a6 100644 --- a/src/main/java/com/Podzilla/analytics/services/FulfillmentAnalyticsService.java +++ b/src/main/java/com/Podzilla/analytics/services/FulfillmentAnalyticsService.java @@ -8,6 +8,7 @@ import com.Podzilla.analytics.api.dtos.fulfillment.FulfillmentShipToDeliverRequest.ShipToDeliverGroupBy; import com.Podzilla.analytics.api.projections.fulfillment.FulfillmentTimeProjection; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import java.math.BigDecimal; import java.time.LocalDate; @@ -18,6 +19,7 @@ @RequiredArgsConstructor @Service +@Slf4j public class FulfillmentAnalyticsService { private final OrderRepository orderRepository; @@ -26,6 +28,8 @@ public List getPlaceToShipTimeResponse( final LocalDate startDate, final LocalDate endDate, final PlaceToShipGroupBy groupBy) { + log.info("Getting place-to-ship time response between {} and {}" + + " with groupBy {}", startDate, endDate, groupBy); LocalDateTime startDateTime = DatetimeFormatter .convertStartDateToDatetime(startDate); LocalDateTime endDateTime = DatetimeFormatter @@ -34,13 +38,15 @@ public List getPlaceToShipTimeResponse( switch (groupBy) { case OVERALL: + log.debug("Fetching overall place-to-ship time"); FulfillmentTimeProjection overall = orderRepository .findPlaceToShipTimeOverall(startDateTime, endDateTime); - if (overall != null) { + if (overall != null && overall.getAverageDuration() != null) { results.add(convertToResponse(overall)); } break; case REGION: + log.debug("Fetching place-to-ship time by region"); List byRegion = orderRepository .findPlaceToShipTimeByRegion( startDateTime, endDateTime); @@ -49,6 +55,8 @@ public List getPlaceToShipTimeResponse( .collect(Collectors.toList())); break; default: + log.warn("Unknown groupBy value for place-to-ship: {}", + groupBy); // Handle unknown groupBy or throw an exception break; } @@ -60,6 +68,9 @@ public List getShipToDeliverTimeResponse( final LocalDate startDate, final LocalDate endDate, final ShipToDeliverGroupBy groupBy) { + log.info("Getting ship-to-deliver time response between {} and" + + " {} with groupBy {}", startDate, endDate, + groupBy); LocalDateTime startDateTime = DatetimeFormatter .convertStartDateToDatetime(startDate); LocalDateTime endDateTime = DatetimeFormatter @@ -68,14 +79,16 @@ public List getShipToDeliverTimeResponse( switch (groupBy) { case OVERALL: + log.debug("Fetching overall ship-to-deliver time"); FulfillmentTimeProjection overall = orderRepository .findShipToDeliverTimeOverall( startDateTime, endDateTime); - if (overall != null) { + if (overall != null && overall.getAverageDuration() != null) { results.add(convertToResponse(overall)); } break; case REGION: + log.debug("Fetching ship-to-deliver time by region"); List byRegion = orderRepository .findShipToDeliverTimeByRegion( startDateTime, endDateTime); @@ -84,6 +97,7 @@ public List getShipToDeliverTimeResponse( .collect(Collectors.toList())); break; case COURIER: + log.debug("Fetching ship-to-deliver time by courier"); List byCourier = orderRepository .findShipToDeliverTimeByCourier( startDateTime, endDateTime); @@ -92,6 +106,8 @@ public List getShipToDeliverTimeResponse( .collect(Collectors.toList())); break; default: + log.warn("Unknown groupBy value for ship-to-deliver: {}", + groupBy); // Handle unknown groupBy or throw an exception break; } diff --git a/src/main/java/com/Podzilla/analytics/services/InventoryAnalyticsService.java b/src/main/java/com/Podzilla/analytics/services/InventoryAnalyticsService.java index ca61ada..1bb1255 100644 --- a/src/main/java/com/Podzilla/analytics/services/InventoryAnalyticsService.java +++ b/src/main/java/com/Podzilla/analytics/services/InventoryAnalyticsService.java @@ -18,30 +18,37 @@ import java.time.Instant; import java.time.LocalDateTime; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @Service @RequiredArgsConstructor +@Slf4j public class InventoryAnalyticsService { private final ProductSnapshotRepository inventoryRepo; private final ProductRepository productRepository; -public List getInventoryValueByCategory() { -List invVByCy = inventoryRepo -.getInventoryValueByCategory() -.stream() -.map(row -> InventoryValueByCategoryResponse.builder() -.category(row.getCategory()) -.totalStockValue(row.getTotalStockValue()) -.build()) + public List getInventoryValueByCategory( + + ) { + log.info("Getting inventory value by category"); + List invVByCy = inventoryRepo + .getInventoryValueByCategory() + .stream() + .map(row -> InventoryValueByCategoryResponse.builder() + .category(row.getCategory()) + .totalStockValue(row.getTotalStockValue()) + .build()) .toList(); return invVByCy; } -public Page getLowStockProducts(final int page, - final int size) { + public Page getLowStockProducts( + final int page, final int size) { + log.info("Getting low stock products, page: {}, size: {}", + page, size); PageRequest pageRequest = PageRequest.of(page, size); -Page lowStockPro = - inventoryRepo.getLowStockProducts(pageRequest) + Page lowStockPro = inventoryRepo + .getLowStockProducts(pageRequest) .map(row -> LowStockProductResponse.builder() .productId(row.getProductId()) .productName(row.getProductName()) @@ -54,13 +61,15 @@ public Page getLowStockProducts(final int page, public void saveInventorySnapshot( final String productId, final Integer quantity, - final Instant timestamp - ) { + final Instant timestamp) { + log.info("Saving inventory snapshot for productId: {}," + + " quantity: {}, timestamp: {}", productId, quantity, + timestamp); UUID productUUID = StringToUUIDParser.parseStringToUUID(productId); Product product = productRepository.findById(productUUID) .orElseThrow( - () -> new IllegalArgumentException("Product not found") - ); + () -> new IllegalArgumentException( + "Product not found")); LocalDateTime snapshotTimestamp = DatetimeFormatter .convertIntsantToDateTime(timestamp); ProductSnapshot inventorySnapshot = ProductSnapshot.builder() @@ -70,5 +79,7 @@ public void saveInventorySnapshot( .build(); inventoryRepo.save(inventorySnapshot); + log.info("Inventory snapshot saved for productId: {}", + productId); } } diff --git a/src/main/java/com/Podzilla/analytics/services/OrderAnalyticsService.java b/src/main/java/com/Podzilla/analytics/services/OrderAnalyticsService.java index eb74223..4c05379 100644 --- a/src/main/java/com/Podzilla/analytics/services/OrderAnalyticsService.java +++ b/src/main/java/com/Podzilla/analytics/services/OrderAnalyticsService.java @@ -27,142 +27,144 @@ import com.Podzilla.analytics.models.Courier; import com.Podzilla.analytics.util.StringToUUIDParser; - - import lombok.RequiredArgsConstructor; import java.util.UUID; +import lombok.extern.slf4j.Slf4j; @Service @RequiredArgsConstructor +@Slf4j public class OrderAnalyticsService { - private final OrderRepository orderRepository; private final CustomerRepository customerRepository; private final CourierRepository courierRepository; private final OrderItemService orderItemService; public List getOrdersByRegion( - final LocalDate startDate, - final LocalDate endDate - ) { - LocalDateTime startDateTime = - DatetimeFormatter.convertStartDateToDatetime(startDate); - LocalDateTime endDateTime = - DatetimeFormatter.convertEndDateToDatetime(endDate); - List ordersByRegion = - orderRepository.findOrdersByRegion(startDateTime, endDateTime); + final LocalDate startDate, + final LocalDate endDate) { + log.info("Getting orders by region between {} and {}", + startDate, endDate); + LocalDateTime startDateTime = DatetimeFormatter + .convertStartDateToDatetime(startDate); + LocalDateTime endDateTime = DatetimeFormatter + .convertEndDateToDatetime(endDate); + List ordersByRegion = orderRepository + .findOrdersByRegion(startDateTime, endDateTime); return ordersByRegion.stream() - .map(data -> OrderRegionResponse.builder() - .regionId(data.getRegionId()) - .city(data.getCity()) - .country(data.getCountry()) - .orderCount(data.getOrderCount()) - .averageOrderValue(data.getAverageOrderValue()) - .build()) - .toList(); + .map(data -> OrderRegionResponse.builder() + .city(data.getCity()) + .country(data.getCountry()) + .orderCount(data.getOrderCount()) + .averageOrderValue(data.getAverageOrderValue()) + .build()) + .toList(); } public List getOrdersStatusCounts( - final LocalDate startDate, - final LocalDate endDate - ) { - LocalDateTime startDateTime = - DatetimeFormatter.convertStartDateToDatetime(startDate); - LocalDateTime endDateTime = - DatetimeFormatter.convertEndDateToDatetime(endDate); - List orderStatusCounts = - orderRepository.findOrderStatusCounts(startDateTime, endDateTime); + final LocalDate startDate, + final LocalDate endDate) { + log.info("Getting order status counts between {} and {}", + startDate, endDate); + LocalDateTime startDateTime = DatetimeFormatter + .convertStartDateToDatetime(startDate); + LocalDateTime endDateTime = DatetimeFormatter + .convertEndDateToDatetime(endDate); + List orderStatusCounts = orderRepository + .findOrderStatusCounts(startDateTime, + endDateTime); return orderStatusCounts.stream() - .map(data -> OrderStatusResponse.builder() - .status(data.getStatus()) - .count(data.getCount()) - .build()) - .toList(); + .map(data -> OrderStatusResponse.builder() + .status(data.getStatus()) + .count(data.getCount()) + .build()) + .toList(); } public OrderFailureResponse getOrdersFailures( - final LocalDate startDate, - final LocalDate endDate - ) { - LocalDateTime startDateTime = - DatetimeFormatter.convertStartDateToDatetime(startDate); - LocalDateTime endDateTime = - DatetimeFormatter.convertEndDateToDatetime(endDate); - List failureReasons = - orderRepository.findFailureReasons(startDateTime, endDateTime); - OrderFailureRateProjection failureRate = - orderRepository.calculateFailureRate(startDateTime, endDateTime); - List - failureReasonsDTO = failureReasons.stream() - .map(data -> OrderFailureReasonsResponse.builder() - .reason(data.getReason()) - .count(data.getCount()) - .build()) - .toList(); + final LocalDate startDate, + final LocalDate endDate) { + log.info("Getting order failures between {} and {}", + startDate, endDate); + LocalDateTime startDateTime = DatetimeFormatter + .convertStartDateToDatetime(startDate); + LocalDateTime endDateTime = DatetimeFormatter + .convertEndDateToDatetime(endDate); + List failureReasons = orderRepository + .findFailureReasons(startDateTime, + endDateTime); + OrderFailureRateProjection failureRate = orderRepository + .calculateFailureRate(startDateTime, endDateTime); + List failureReasonsDTO = failureReasons + .stream() + .map(data -> OrderFailureReasonsResponse.builder() + .reason(data.getReason()) + .count(data.getCount()) + .build()) + .toList(); return OrderFailureResponse.builder() - .reasons(failureReasonsDTO) - .failureRate(failureRate.getFailureRate()) - .build(); + .reasons(failureReasonsDTO) + .failureRate(failureRate.getFailureRate()) + .build(); } public Order saveOrder( - final String orderId, - final String customerId, - final List items, - final Region region, - final BigDecimal totalAmount, - final Instant timeStamp - ) { - UUID orderUUID = - StringToUUIDParser.parseStringToUUID(orderId); - UUID customerUUID = - StringToUUIDParser.parseStringToUUID(customerId); - Customer customer = - customerRepository.findById(customerUUID) - .orElseThrow(() -> new RuntimeException("Customer not found")); + final String orderId, + final String customerId, + final List items, + final Region region, + final BigDecimal totalAmount, + final Instant timeStamp) { + log.info("Saving order with orderId: {}, customerId: {}," + + " region: {}, totalAmount: {}, timeStamp: {}", + orderId, customerId, region, totalAmount, timeStamp); + UUID orderUUID = StringToUUIDParser.parseStringToUUID(orderId); + UUID customerUUID = StringToUUIDParser.parseStringToUUID(customerId); + Customer customer = customerRepository.findById(customerUUID) + .orElseThrow(() -> new RuntimeException( + "Customer not found")); int numberOfItems = items.stream() - .mapToInt(com.podzilla.mq.events.OrderItem::getQuantity) - .sum(); - LocalDateTime orderPlacedTimestamp = - DatetimeFormatter.convertIntsantToDateTime(timeStamp); + .mapToInt(com.podzilla.mq.events.OrderItem::getQuantity) + .sum(); + LocalDateTime orderPlacedTimestamp = DatetimeFormatter + .convertIntsantToDateTime(timeStamp); Order order = Order.builder() - .id(orderUUID) - .totalAmount(totalAmount) - .orderPlacedTimestamp(orderPlacedTimestamp) - .finalStatusTimestamp(orderPlacedTimestamp) - .region(region) - .customer(customer) - .numberOfItems(numberOfItems) - .status(OrderStatus.PLACED) - .build(); + .id(orderUUID) + .totalAmount(totalAmount) + .orderPlacedTimestamp(orderPlacedTimestamp) + .finalStatusTimestamp(orderPlacedTimestamp) + .region(region) + .customer(customer) + .numberOfItems(numberOfItems) + .status(OrderStatus.PLACED) + .build(); orderRepository.save(order); - List orderItems = - items.stream() - .map(item -> orderItemService.saveOrderItem( - item.getQuantity(), - item.getPricePerUnit(), - item.getProductId(), - orderUUID.toString() - )) - .toList(); + List orderItems = items + .stream() + .map(item -> orderItemService.saveOrderItem( + item.getQuantity(), + item.getPricePerUnit(), + item.getProductId(), + orderUUID.toString())) + .toList(); order.setOrderItems(orderItems); return orderRepository.save(order); } public Order cancelOrder( - final String orderId, - final String reason, - final Instant timeStamp - ) { - UUID orderUUID = - StringToUUIDParser.parseStringToUUID(orderId); - LocalDateTime orderCancelledTimestamp = - DatetimeFormatter.convertIntsantToDateTime(timeStamp); - Order order = - orderRepository.findById(orderUUID) - .orElseThrow(() -> new RuntimeException("Order not found")); + final String orderId, + final String reason, + final Instant timeStamp) { + log.info("Cancelling order with orderId: {}, reason: {}," + + " timeStamp: {}", orderId, reason, timeStamp); + UUID orderUUID = StringToUUIDParser.parseStringToUUID(orderId); + LocalDateTime orderCancelledTimestamp = DatetimeFormatter + .convertIntsantToDateTime(timeStamp); + Order order = orderRepository.findById(orderUUID) + .orElseThrow(() -> new RuntimeException( + "Order not found")); order.setStatus(OrderStatus.CANCELLED); order.setFailureReason(reason); order.setOrderCancelledTimestamp(orderCancelledTimestamp); @@ -171,33 +173,33 @@ public Order cancelOrder( } public void assignCourier( - final String orderId, - final String courierId - ) { - UUID orderUUID = - StringToUUIDParser.parseStringToUUID(orderId); - UUID courierUUID = - StringToUUIDParser.parseStringToUUID(courierId); - Order order = - orderRepository.findById(orderUUID) - .orElseThrow(() -> new RuntimeException("Order not found")); - Courier courier = - courierRepository.findById(courierUUID) - .orElseThrow(() -> new RuntimeException("Courier not found")); + final String orderId, + final String courierId) { + log.info("Assigning courier with courierId: {} " + + "to orderId: {}", courierId, orderId); + UUID orderUUID = StringToUUIDParser.parseStringToUUID(orderId); + UUID courierUUID = StringToUUIDParser.parseStringToUUID(courierId); + Order order = orderRepository.findById(orderUUID) + .orElseThrow(() -> new RuntimeException( + "Order not found")); + Courier courier = courierRepository.findById(courierUUID) + .orElseThrow(() -> new RuntimeException( + "Courier not found")); order.setCourier(courier); orderRepository.save(order); } + public void markOrderAsOutForDelivery( - final String orderId, - final Instant timeStamp - ) { - UUID orderUUID = - StringToUUIDParser.parseStringToUUID(orderId); - LocalDateTime orderOutForDeliveryTimestamp = - DatetimeFormatter.convertIntsantToDateTime(timeStamp); - Order order = - orderRepository.findById(orderUUID) - .orElseThrow(() -> new RuntimeException("Order not found")); + final String orderId, + final Instant timeStamp) { + log.info("Marking order as out for delivery." + + " orderId: {}, timeStamp: {}", orderId, timeStamp); + UUID orderUUID = StringToUUIDParser.parseStringToUUID(orderId); + LocalDateTime orderOutForDeliveryTimestamp = DatetimeFormatter + .convertIntsantToDateTime(timeStamp); + Order order = orderRepository.findById(orderUUID) + .orElseThrow(() -> new RuntimeException( + "Order not found")); order.setStatus(OrderStatus.SHIPPED); order.setShippedTimestamp(orderOutForDeliveryTimestamp); order.setFinalStatusTimestamp(orderOutForDeliveryTimestamp); @@ -205,17 +207,18 @@ public void markOrderAsOutForDelivery( } public void markOrderAsDelivered( - final String orderId, - final BigDecimal courierRating, - final Instant timeStamp - ) { - UUID orderUUID = - StringToUUIDParser.parseStringToUUID(orderId); - LocalDateTime orderDeliveredTimestamp = - DatetimeFormatter.convertIntsantToDateTime(timeStamp); - Order order = - orderRepository.findById(orderUUID) - .orElseThrow(() -> new RuntimeException("Order not found")); + final String orderId, + final BigDecimal courierRating, + final Instant timeStamp) { + log.info("Marking order as delivered. orderId: {}," + + " courierRating: {}, timeStamp: {}", orderId, courierRating, + timeStamp); + UUID orderUUID = StringToUUIDParser.parseStringToUUID(orderId); + LocalDateTime orderDeliveredTimestamp = DatetimeFormatter + .convertIntsantToDateTime(timeStamp); + Order order = orderRepository.findById(orderUUID) + .orElseThrow(() -> new RuntimeException( + "Order not found")); order.setStatus(OrderStatus.DELIVERED); order.setDeliveredTimestamp(orderDeliveredTimestamp); order.setFinalStatusTimestamp(orderDeliveredTimestamp); @@ -224,43 +227,43 @@ public void markOrderAsDelivered( } public void markOrderAsFailedToDeliver( - final String orderId, - final String reason, - final Instant timeStamp - ) { - UUID orderUUID = - StringToUUIDParser.parseStringToUUID(orderId); - LocalDateTime orderFailedToDeliverTimestamp = - DatetimeFormatter.convertIntsantToDateTime(timeStamp); - Order order = - orderRepository.findById(orderUUID) - .orElseThrow(() -> new RuntimeException("Order not found")); + final String orderId, + final String reason, + final Instant timeStamp) { + log.info("Marking order as failed to deliver." + + " orderId: {}, reason: {}, timeStamp: {}", orderId, reason, + timeStamp); + UUID orderUUID = StringToUUIDParser.parseStringToUUID(orderId); + LocalDateTime orderFailedToDeliverTimestamp = DatetimeFormatter + .convertIntsantToDateTime(timeStamp); + Order order = orderRepository.findById(orderUUID) + .orElseThrow(() -> new RuntimeException( + "Order not found")); order.setStatus(OrderStatus.DELIVERY_FAILED); order.setFailureReason(reason); order.setOrderDeliveryFailedTimestamp( - orderFailedToDeliverTimestamp - ); + orderFailedToDeliverTimestamp); order.setFinalStatusTimestamp(orderFailedToDeliverTimestamp); orderRepository.save(order); } public void markOrderAsFailedToFulfill( - final String orderId, - final String reason, - final Instant timeStamp - ) { - UUID orderUUID = - StringToUUIDParser.parseStringToUUID(orderId); - LocalDateTime orderFulfillmentFailedTimestamp = - DatetimeFormatter.convertIntsantToDateTime(timeStamp); - Order order = - orderRepository.findById(orderUUID) - .orElseThrow(() -> new RuntimeException("Order not found")); + final String orderId, + final String reason, + final Instant timeStamp) { + log.info("Marking order as failed to fulfill." + + " orderId: {}, reason: {}, timeStamp: {}", orderId, reason, + timeStamp); + UUID orderUUID = StringToUUIDParser.parseStringToUUID(orderId); + LocalDateTime orderFulfillmentFailedTimestamp = DatetimeFormatter + .convertIntsantToDateTime(timeStamp); + Order order = orderRepository.findById(orderUUID) + .orElseThrow(() -> new RuntimeException( + "Order not found")); order.setStatus(OrderStatus.FULFILLMENT_FAILED); order.setFailureReason(reason); order.setOrderFulfillmentFailedTimestamp( - orderFulfillmentFailedTimestamp - ); + orderFulfillmentFailedTimestamp); order.setFinalStatusTimestamp(orderFulfillmentFailedTimestamp); orderRepository.save(order); } diff --git a/src/main/java/com/Podzilla/analytics/services/OrderItemService.java b/src/main/java/com/Podzilla/analytics/services/OrderItemService.java index daa91bc..9a5c09c 100644 --- a/src/main/java/com/Podzilla/analytics/services/OrderItemService.java +++ b/src/main/java/com/Podzilla/analytics/services/OrderItemService.java @@ -2,11 +2,11 @@ import org.springframework.stereotype.Service; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import com.Podzilla.analytics.repositories.OrderItemRepository; import com.Podzilla.analytics.repositories.ProductRepository; import com.Podzilla.analytics.repositories.OrderRepository; - import com.Podzilla.analytics.util.StringToUUIDParser; import org.springframework.beans.factory.annotation.Autowired; @@ -14,12 +14,12 @@ import com.Podzilla.analytics.models.Product; import com.Podzilla.analytics.models.Order; - import java.math.BigDecimal; import java.util.UUID; @Service @RequiredArgsConstructor +@Slf4j public class OrderItemService { @Autowired private final OrderItemRepository orderItemRepository; @@ -31,25 +31,47 @@ public class OrderItemService { private final OrderRepository orderRepository; public OrderItem saveOrderItem( - final int quantity, - final BigDecimal pricePerUnit, - final String productId, - final String orderId - ) { + final int quantity, + final BigDecimal pricePerUnit, + final String productId, + final String orderId) { + log.info("Attempting to save OrderItem." + + " quantity: {}, pricePerUnit: {}, productId: {}, orderId: {}", + quantity, pricePerUnit, productId, + orderId); + UUID productUUID = StringToUUIDParser.parseStringToUUID(productId); UUID orderUUID = StringToUUIDParser.parseStringToUUID(orderId); + + log.debug("Parsed productId to UUID: {}, orderId to UUID: {}", + productUUID, orderUUID); + Product product = productRepository.findById(productUUID) - .orElseThrow(() -> new RuntimeException("Product not found")); + .orElseThrow(() -> { + log.error("Product not found. productId: {}", + productId); + return new RuntimeException("Product not found"); + }); + Order order = orderRepository.findById(orderUUID) - .orElseThrow(() -> new RuntimeException("Order not found")); + .orElseThrow(() -> { + log.error("Order not found. orderId: {}", orderId); + return new RuntimeException("Order not found"); + }); OrderItem orderItem = OrderItem.builder() - .quantity(quantity) - .pricePerUnit(pricePerUnit) - .product(product) - .order(order) - .build(); - return orderItemRepository.save(orderItem); - } + .quantity(quantity) + .pricePerUnit(pricePerUnit) + .product(product) + .order(order) + .build(); + log.info("Saving OrderItem for orderId: {}, productId: {}", + orderId, productId); + OrderItem savedOrderItem = orderItemRepository.save(orderItem); + log.info("OrderItem saved successfully. orderItemId: {}", + savedOrderItem.getId()); + + return savedOrderItem; + } } diff --git a/src/main/java/com/Podzilla/analytics/services/ProductAnalyticsService.java b/src/main/java/com/Podzilla/analytics/services/ProductAnalyticsService.java index 350f005..21ca5f9 100644 --- a/src/main/java/com/Podzilla/analytics/services/ProductAnalyticsService.java +++ b/src/main/java/com/Podzilla/analytics/services/ProductAnalyticsService.java @@ -13,12 +13,14 @@ import com.Podzilla.analytics.api.dtos.product.TopSellerResponse; import com.Podzilla.analytics.repositories.ProductRepository; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import com.Podzilla.analytics.models.Product; import com.Podzilla.analytics.util.StringToUUIDParser; import java.util.UUID; @RequiredArgsConstructor @Service +@Slf4j public class ProductAnalyticsService { private final ProductRepository productRepository; @@ -39,8 +41,10 @@ public List getTopSellers( final LocalDate startDate, final LocalDate endDate, final Integer limit, - final SortBy sortBy -) { + final SortBy sortBy) { + log.info("Getting top sellers between {} and {}" + + " with limit {} and sortBy {}", startDate, + endDate, limit, sortBy); final String sortByString = sortBy != null ? sortBy.name() : SortBy.REVENUE.name(); @@ -54,6 +58,8 @@ public List getTopSellers( endDateTime, limit, sortByString); + log.debug("Query returned {} top sellers", queryResults.size()); + List topSellersList = new ArrayList<>(); for (TopSellingProductProjection row : queryResults) { @@ -71,9 +77,11 @@ public List getTopSellers( } topSellersList.sort((a, b) -> b.getValue().compareTo(a.getValue())); if (limit != null && limit > 0 && limit < topSellersList.size()) { + log.debug("Limiting top sellers list to {}", limit); topSellersList = topSellersList.subList(SUBLIST_START_INDEX, limit); } + log.info("Returning {} top sellers", topSellersList.size()); return topSellersList; } @@ -82,8 +90,9 @@ public void saveProduct( final String productName, final String productCategory, final BigDecimal productCost, - final Integer productLowStockThreshold - ) { + final Integer productLowStockThreshold) { + log.info("Saving product with id: {}, name: {}, " + + " category: {}", productId, productName, productCategory); UUID id = StringToUUIDParser.parseStringToUUID(productId); Product product = Product.builder() .id(id) @@ -93,5 +102,6 @@ public void saveProduct( .lowStockThreshold(productLowStockThreshold) .build(); productRepository.save(product); + log.debug("Product saved: {}", product); } } diff --git a/src/main/java/com/Podzilla/analytics/services/ProfitAnalyticsService.java b/src/main/java/com/Podzilla/analytics/services/ProfitAnalyticsService.java index 0daf15e..72a8fa5 100644 --- a/src/main/java/com/Podzilla/analytics/services/ProfitAnalyticsService.java +++ b/src/main/java/com/Podzilla/analytics/services/ProfitAnalyticsService.java @@ -7,6 +7,7 @@ import com.Podzilla.analytics.repositories.OrderItemRepository; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import java.math.BigDecimal; import java.math.RoundingMode; @@ -18,6 +19,7 @@ @RequiredArgsConstructor @Service +@Slf4j public class ProfitAnalyticsService { private final OrderItemRepository salesLineItemRepository; private static final int PERCENTAGE_PRECISION = 4; @@ -25,12 +27,17 @@ public class ProfitAnalyticsService { public List getProfitByCategory( final LocalDate startDate, final LocalDate endDate) { + log.info("Getting profit by category between {} and {}", + startDate, endDate); LocalDateTime startDateTime = startDate.atStartOfDay(); LocalDateTime endDateTime = endDate.atTime(LocalTime.MAX); List salesData = salesLineItemRepository .findSalesByCategoryBetweenDates(startDateTime, endDateTime); + log.debug("Fetched {} sales data records for categories", + salesData.size()); + return salesData.stream() .map(this::convertToDTO) .collect(Collectors.toList()); diff --git a/src/main/java/com/Podzilla/analytics/services/RegionService.java b/src/main/java/com/Podzilla/analytics/services/RegionService.java index f653fed..cc26dec 100644 --- a/src/main/java/com/Podzilla/analytics/services/RegionService.java +++ b/src/main/java/com/Podzilla/analytics/services/RegionService.java @@ -6,29 +6,34 @@ import com.Podzilla.analytics.repositories.RegionRepository; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import com.Podzilla.analytics.models.Region; - - @Service @RequiredArgsConstructor +@Slf4j public class RegionService { @Autowired private final RegionRepository regionRepository; public Region saveRegion( - final String city, - final String state, - final String country, - final String postalCode - ) { + final String city, + final String state, + final String country, + final String postalCode) { + log.info("Saving region with city: {}, state: {}," + + " country: {}, postalCode: {}", + city, state, country, postalCode); Region region = Region.builder() - .city(city) - .state(state) - .country(country) - .postalCode(postalCode) - .build(); - return regionRepository.save(region); + .city(city) + .state(state) + .country(country) + .postalCode(postalCode) + .build(); + Region savedRegion = regionRepository.save(region); + log.info("Region saved successfully with id: {}", + savedRegion.getId()); + return savedRegion; } } diff --git a/src/main/java/com/Podzilla/analytics/services/RevenueReportService.java b/src/main/java/com/Podzilla/analytics/services/RevenueReportService.java index 0de6380..990f051 100644 --- a/src/main/java/com/Podzilla/analytics/services/RevenueReportService.java +++ b/src/main/java/com/Podzilla/analytics/services/RevenueReportService.java @@ -15,9 +15,11 @@ import com.Podzilla.analytics.repositories.OrderRepository; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @RequiredArgsConstructor @Service +@Slf4j public class RevenueReportService { private final OrderRepository orderRepository; @@ -29,6 +31,8 @@ public List getRevenueSummary( LocalDateTime startDateTime = startDate.atStartOfDay(); LocalDateTime endDateTime = endDate.atTime(LocalTime.MAX); + log.info("Getting revenue summary between {} and {}" + + " for period '{}'", startDate, endDate, periodString); final List revenueData = orderRepository .findRevenueSummaryByPeriod( @@ -47,6 +51,7 @@ public List getRevenueSummary( summaryList.add(summaryItem); } + log.info("Revenue summary result size: {}", summaryList.size()); return summaryList; } @@ -63,6 +68,8 @@ public List getRevenueByCategory( LocalDateTime startDateTime = startDate.atStartOfDay(); LocalDateTime endDateTime = endDate.atTime(LocalTime.MAX); + log.info("Getting revenue by category between" + + " {} and {}", startDate, endDate); final List queryResults = orderRepository .findRevenueByCategory( @@ -82,6 +89,8 @@ public List getRevenueByCategory( summaryList.add(summaryItem); } + log.info("Revenue by category result" + + " size: {}", summaryList.size()); return summaryList; } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index ae2f487..494844a 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -10,7 +10,7 @@ spring.datasource.driver-class-name=org.postgresql.Driver spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.generate-ddl=true -spring.jpa.show-sql=true +# spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true spring.jpa.properties.hibernate.use_sql_comments=true diff --git a/src/main/resources/static/favicon.ico b/src/main/resources/static/favicon.ico new file mode 100644 index 0000000..e69de29