From cb99839e39c0536d9e4db93195abcd2c0e973e46 Mon Sep 17 00:00:00 2001 From: Tim <47184194+imgde@users.noreply.github.com> Date: Thu, 12 Jun 2025 22:38:24 +0200 Subject: [PATCH] StatisticsController for SalesVolume and SalesRevenue, fix OrderController --- 00-backend/datasource/database.sqlite | Bin 2629632 -> 2629632 bytes .../webshop/config/ControllerPathConfig.java | 6 ++ .../webshop/config/ParameterConfig.java | 1 + .../webshop/controller/OrderController.java | 3 +- .../controller/StatisticsController.java | 58 +++++++++++++++++ .../webshop/model/MonthlyCatModel.java | 15 +++++ .../webshop/repository/OrderRepository.java | 2 + .../repository/entities/FarmImage.java | 2 +- .../webshop/repository/entities/Order.java | 3 +- .../repository/entities/OrderItem.java | 6 +- .../htwsaar/webshop/service/OrderService.java | 3 + .../webshop/service/SessionService.java | 2 +- .../webshop/service/StatisticsService.java | 10 +++ .../service/impl/OrderServiceImpl.java | 18 ++++++ .../service/impl/SessionServiceImpl.java | 6 +- .../service/impl/StatisticsServiceImpl.java | 59 ++++++++++++++++++ .../de/htwsaar/webshop/util/TimeUtil.java | 18 +++--- 00-backend/src/main/resources/db/initdb.sql | 4 +- 18 files changed, 195 insertions(+), 21 deletions(-) create mode 100644 00-backend/src/main/java/de/htwsaar/webshop/controller/StatisticsController.java create mode 100644 00-backend/src/main/java/de/htwsaar/webshop/model/MonthlyCatModel.java create mode 100644 00-backend/src/main/java/de/htwsaar/webshop/service/StatisticsService.java create mode 100644 00-backend/src/main/java/de/htwsaar/webshop/service/impl/StatisticsServiceImpl.java diff --git a/00-backend/datasource/database.sqlite b/00-backend/datasource/database.sqlite index 50edaa282eaad892c7b57845b6e12dafac214cdf..47cafb1c10c0b7feb90f970b36652ae75e729cf0 100644 GIT binary patch delta 1817 zcmbW%KTH!*90%}s*VZd{yNyb1QB!Fc6oE#JN~9PP2KXKHu!XjeYcV(- zBKe^kM1{c+aC9)n5b5GzLYy2VaYIcc(Zq#CV{nubV_MQ;>F`Tl-peQ7dw+g6{brS# z-mCeV@;D*%3?am{SM*-hFEsiranf}A!OQS!cZ-MJAi_IgQBZ|O`>y@9{jNP|=lD(j zG2hSE*}mGI*<%O_%yqBNmoIDP7%I z(Ih31^#y*vq6Ati57&odXA#&a7+q0>E{MyE#YVneJBrnLa6n*)=(^w)uc=8b);m5j z5{xFoT0#m%wWM@e)78mj=ESAu=FDjue=pqcZ$8%?upi0hP`)&3WMXZl*$m}Mr)-8A zMW0m8Zp%uKwdPRce;1S!%jDQ2(PR0kQQ%!<$NDS`ak|cX#b9i_=Z>1_)1t$XA$2&C zOzGqMjjIev2U@qqXD$jE|9&}{hHvqiic$vxW6;6$iIa3Q!6M1(4YY6K5L4Z?ART7(k_bqEqdJwgM5 z7vUtrDTLGOAX5_@J3=hX3b9?cJJYsJxt}%jPpI$d(Jk3$qRG_K%4a!s@!qJF5xrh& zt|p}p#k6ictm%VYDJ`DN8kV*9bhh<&O1*8Du69ZdSt||cr(5!KYW<6Bakxx7L~!u; zxwq^xJ7(QB?O58Gfi=bP!PGVrs>sC9vt#=)K~;|rcE#0OS~6=$8@V}IOASkVIi98E uPqJt6x4g~H(oROKt)=H~F1&ZAhkwc6D4LmHy0R+=&4f@l(vNoK*qJ{)H$Gng delta 696 zcmb7?L2DCH6oucLWct#XWZtAPGuj#(jXFX#8uAB(iinGJV-cjt8QT=aX(=`$xUeOJ zqsta8U5GS)z=Z^wjSE4#abtgg1ziZ@Ub}TdE3^bpK~}!^#!W_Ko3~jn|zR{`ey&AYB{My zE`O=y-+C~2JB*?mAK&-(t orderItem.setId(null)); Order saved = orderService.save(order); return ResponseEntity.ok(saved != null); } diff --git a/00-backend/src/main/java/de/htwsaar/webshop/controller/StatisticsController.java b/00-backend/src/main/java/de/htwsaar/webshop/controller/StatisticsController.java new file mode 100644 index 0000000..8dfddd1 --- /dev/null +++ b/00-backend/src/main/java/de/htwsaar/webshop/controller/StatisticsController.java @@ -0,0 +1,58 @@ +package de.htwsaar.webshop.controller; + +import de.htwsaar.webshop.model.MonthlyCatModel; +import de.htwsaar.webshop.service.SessionService; +import de.htwsaar.webshop.service.StatisticsService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.UUID; + +import static de.htwsaar.webshop.config.ControllerPathConfig.STATISTICS_REVENUE; +import static de.htwsaar.webshop.config.ControllerPathConfig.STATISTICS_VOLUME; +import static de.htwsaar.webshop.config.ParameterConfig.PARAM_EMAIL; +import static de.htwsaar.webshop.config.ParameterConfig.PARAM_SESSION; +import static de.htwsaar.webshop.util.LoggerUtil.logRequest; + +@RestController +@Slf4j +public class StatisticsController { + + private final StatisticsService statisticsService; + private final SessionService sessionService; + + public StatisticsController(StatisticsService statisticsService, SessionService sessionService) { + this.statisticsService = statisticsService; + this.sessionService = sessionService; + } + + @RequestMapping(value = STATISTICS_VOLUME, method = RequestMethod.GET, produces = "application/json") + public ResponseEntity> getMonthlySalesVolume(HttpServletRequest request, + @RequestParam(value = PARAM_SESSION) UUID session, + @RequestParam(value = PARAM_EMAIL) String email) { + logRequest(request); + if (!sessionService.isAdmin(session, email)) { + log.warn("Invalid session requesting Admin {}", session); + return ResponseEntity.status(403).build(); + } + return ResponseEntity.ok(statisticsService.getSalesVolume()); + } + + @RequestMapping(value = STATISTICS_REVENUE, method = RequestMethod.GET, produces = "application/json") + public ResponseEntity> getMonthlyRevenue(HttpServletRequest request, + @RequestParam(value = PARAM_SESSION) UUID token, + @RequestParam(value = PARAM_EMAIL) String email) { + logRequest(request); + if (!sessionService.isAdmin(token, email)) { + log.warn("Invalid session requesting Admin {}", token); + return ResponseEntity.status(403).build(); + } + return ResponseEntity.ok(statisticsService.getSalesRevenue()); + } + +} diff --git a/00-backend/src/main/java/de/htwsaar/webshop/model/MonthlyCatModel.java b/00-backend/src/main/java/de/htwsaar/webshop/model/MonthlyCatModel.java new file mode 100644 index 0000000..1ada3cf --- /dev/null +++ b/00-backend/src/main/java/de/htwsaar/webshop/model/MonthlyCatModel.java @@ -0,0 +1,15 @@ +package de.htwsaar.webshop.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.util.Map; + +@Getter +@Setter +@AllArgsConstructor +public class MonthlyCatModel { + Map> monthCategoryMap; + +} diff --git a/00-backend/src/main/java/de/htwsaar/webshop/repository/OrderRepository.java b/00-backend/src/main/java/de/htwsaar/webshop/repository/OrderRepository.java index a37d19a..f1456c8 100644 --- a/00-backend/src/main/java/de/htwsaar/webshop/repository/OrderRepository.java +++ b/00-backend/src/main/java/de/htwsaar/webshop/repository/OrderRepository.java @@ -9,4 +9,6 @@ import java.util.List; @Repository public interface OrderRepository extends JpaRepository { List getOrdersByCustomer_Id(Long customerId); + + List getOrdersByTimeBetween(Long timeAfter, Long timeBefore); } diff --git a/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/FarmImage.java b/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/FarmImage.java index 6af5a75..65f772e 100644 --- a/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/FarmImage.java +++ b/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/FarmImage.java @@ -11,7 +11,7 @@ import org.hibernate.type.NumericBooleanConverter; @AllArgsConstructor @ToString @Entity -@Table(name = "FarmImages") +@Table(name = "Farm_Images") public class FarmImage { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/Order.java b/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/Order.java index 10bbc72..01d632e 100644 --- a/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/Order.java +++ b/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/Order.java @@ -31,10 +31,9 @@ public class Order { @Column(name = "status", nullable = false) private OrderStatus status; - @OneToMany(mappedBy = "order", orphanRemoval = true, fetch = FetchType.LAZY, cascade = CascadeType.ALL, targetEntity = OrderItem.class) + @OneToMany(mappedBy = "order", orphanRemoval = true, fetch = FetchType.EAGER, cascade = CascadeType.ALL, targetEntity = OrderItem.class) private List orderItems; - public OrderModel toModel() { return new OrderModel(id, customer.getId(), time, status, orderItems.stream().map(OrderItem::toModel).toList()); } diff --git a/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/OrderItem.java b/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/OrderItem.java index 5f4fd88..b4e5968 100644 --- a/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/OrderItem.java +++ b/00-backend/src/main/java/de/htwsaar/webshop/repository/entities/OrderItem.java @@ -14,7 +14,7 @@ import org.hibernate.annotations.OnDelete; @AllArgsConstructor @NoArgsConstructor @Entity -@Table(name = "OrderItems") +@Table(name = "Order_Items") public class OrderItem { @Id @@ -25,8 +25,8 @@ public class OrderItem { @Min(value = 1, message = "Amount must be at least 1") private Integer amount; - @ManyToOne(fetch = FetchType.LAZY) - @MapsId("orderId") + @ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL, targetEntity = Order.class) + @MapsId("order_id") @JoinColumn(name = "order_id", referencedColumnName = "id", nullable = false) @OnDelete(action = org.hibernate.annotations.OnDeleteAction.CASCADE) private Order order; diff --git a/00-backend/src/main/java/de/htwsaar/webshop/service/OrderService.java b/00-backend/src/main/java/de/htwsaar/webshop/service/OrderService.java index 802dc7e..b1d0df4 100644 --- a/00-backend/src/main/java/de/htwsaar/webshop/service/OrderService.java +++ b/00-backend/src/main/java/de/htwsaar/webshop/service/OrderService.java @@ -4,6 +4,7 @@ import de.htwsaar.webshop.model.OrderModel; import de.htwsaar.webshop.repository.entities.Order; import java.util.List; +import java.util.Map; public interface OrderService { Order save(Order order); @@ -15,4 +16,6 @@ public interface OrderService { Order getOrderById(Long orderId); List getAllOrders(Long customerId); + + Map> getTimeSortedOrders(long fromMilli, long months); } diff --git a/00-backend/src/main/java/de/htwsaar/webshop/service/SessionService.java b/00-backend/src/main/java/de/htwsaar/webshop/service/SessionService.java index f75221a..71fe2ae 100644 --- a/00-backend/src/main/java/de/htwsaar/webshop/service/SessionService.java +++ b/00-backend/src/main/java/de/htwsaar/webshop/service/SessionService.java @@ -18,5 +18,5 @@ public interface SessionService { boolean isValid(UUID token, String email); - boolean isAdmin(Session session, String email); + boolean isAdmin(UUID token, String email); } diff --git a/00-backend/src/main/java/de/htwsaar/webshop/service/StatisticsService.java b/00-backend/src/main/java/de/htwsaar/webshop/service/StatisticsService.java new file mode 100644 index 0000000..50ee146 --- /dev/null +++ b/00-backend/src/main/java/de/htwsaar/webshop/service/StatisticsService.java @@ -0,0 +1,10 @@ +package de.htwsaar.webshop.service; + +import de.htwsaar.webshop.model.MonthlyCatModel; + +public interface StatisticsService { + MonthlyCatModel getSalesVolume(); + + MonthlyCatModel getSalesRevenue(); + +} diff --git a/00-backend/src/main/java/de/htwsaar/webshop/service/impl/OrderServiceImpl.java b/00-backend/src/main/java/de/htwsaar/webshop/service/impl/OrderServiceImpl.java index d1d081c..58a239c 100644 --- a/00-backend/src/main/java/de/htwsaar/webshop/service/impl/OrderServiceImpl.java +++ b/00-backend/src/main/java/de/htwsaar/webshop/service/impl/OrderServiceImpl.java @@ -8,11 +8,14 @@ import de.htwsaar.webshop.repository.entities.OrderItem; import de.htwsaar.webshop.service.ArticleService; import de.htwsaar.webshop.service.CustomerService; import de.htwsaar.webshop.service.OrderService; +import de.htwsaar.webshop.util.TimeUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Service @Slf4j @@ -69,4 +72,19 @@ public class OrderServiceImpl implements OrderService { public List getAllOrders(Long customerId) { return orderRepository.getOrdersByCustomer_Id(customerId); } + + @Override + public Map> getTimeSortedOrders(long fromMilli, long months) { + Map> orders = new HashMap<>(); + long lower = fromMilli; + long upper; + while (months > 0) { + upper = fromMilli + TimeUtil.monthLength(fromMilli); + log.info("Getting Orders from {} to {}", lower, upper); + orders.put(fromMilli, orderRepository.getOrdersByTimeBetween(lower, upper)); + lower = upper; + months--; + } + return orders; + } } diff --git a/00-backend/src/main/java/de/htwsaar/webshop/service/impl/SessionServiceImpl.java b/00-backend/src/main/java/de/htwsaar/webshop/service/impl/SessionServiceImpl.java index 5c4a35a..2f6a6a9 100644 --- a/00-backend/src/main/java/de/htwsaar/webshop/service/impl/SessionServiceImpl.java +++ b/00-backend/src/main/java/de/htwsaar/webshop/service/impl/SessionServiceImpl.java @@ -72,11 +72,11 @@ public class SessionServiceImpl implements SessionService { } @Override - public boolean isAdmin(Session session, String email) { - if(session == null || email == null) { + public boolean isAdmin(UUID token, String email) { + if(token == null || email == null) { return false; } - if(!isValid(session, email)) { + if(!isValid(token, email)) { log.warn("Invalid Session with email {} trying to access Admin Services", email); return false; } diff --git a/00-backend/src/main/java/de/htwsaar/webshop/service/impl/StatisticsServiceImpl.java b/00-backend/src/main/java/de/htwsaar/webshop/service/impl/StatisticsServiceImpl.java new file mode 100644 index 0000000..197f47b --- /dev/null +++ b/00-backend/src/main/java/de/htwsaar/webshop/service/impl/StatisticsServiceImpl.java @@ -0,0 +1,59 @@ +package de.htwsaar.webshop.service.impl; + +import de.htwsaar.webshop.model.MonthlyCatModel; +import de.htwsaar.webshop.repository.entities.Order; +import de.htwsaar.webshop.repository.entities.OrderItem; +import de.htwsaar.webshop.service.OrderService; +import de.htwsaar.webshop.service.StatisticsService; +import de.htwsaar.webshop.util.TimeUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Map; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class StatisticsServiceImpl implements StatisticsService { + private final OrderService orderService; + + @Autowired + public StatisticsServiceImpl(OrderService orderService) { + this.orderService = orderService; + } + + //don't ask pls + //returns Map> + private MonthlyCatModel getMonthCategoryMap(Function mappingFunction, + BinaryOperator reduceFunction) { + return new MonthlyCatModel<>( + orderService.getTimeSortedOrders(TimeUtil.nowMonthsAgo(12), 12).entrySet().stream() + .map(entry -> Map.entry(entry.getKey(), + entry.getValue().stream() + .map(Order::getOrderItems) + .reduce(new ArrayList<>(), (l, r) -> { + l.addAll(r); + return l; + }) + .stream() + .map(orderItem -> Map.entry(orderItem.getArticle().getCategory(), mappingFunction.apply(orderItem))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, reduceFunction)) + )) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) + ); + } + + @Override + public MonthlyCatModel getSalesVolume() { + return getMonthCategoryMap(OrderItem::getAmount, Integer::sum); + } + + @Override + public MonthlyCatModel getSalesRevenue() { + return getMonthCategoryMap(item -> item.getArticle().getPrice100(), Integer::sum); + } +} diff --git a/00-backend/src/main/java/de/htwsaar/webshop/util/TimeUtil.java b/00-backend/src/main/java/de/htwsaar/webshop/util/TimeUtil.java index 5e75a5f..f9fd439 100644 --- a/00-backend/src/main/java/de/htwsaar/webshop/util/TimeUtil.java +++ b/00-backend/src/main/java/de/htwsaar/webshop/util/TimeUtil.java @@ -1,5 +1,9 @@ package de.htwsaar.webshop.util; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; + /** * Helper Class for Unix-Millis related Things */ @@ -41,13 +45,11 @@ public class TimeUtil { return System.currentTimeMillis() + (days * MILLIS_TO_DAY); } - /** - * Check whether the Unix-Millis are valid - * - * @param time the Millis to Check - * @return whether the Time can be valid - */ - public static boolean isValidTime(long time) { - return time >= VALID_MIN_MILLIS_TIMESTAMP; + public static long nowMonthsAgo(long months) { + return LocalDateTime.now().minusMonths(months).atZone(ZoneId.systemDefault()).toEpochSecond(); + } + + public static long monthLength(long epoch) { + return LocalDateTime.ofEpochSecond(epoch, 0, ZoneOffset.UTC).getMonth().length(false) * MILLIS_TO_DAY; } } diff --git a/00-backend/src/main/resources/db/initdb.sql b/00-backend/src/main/resources/db/initdb.sql index c756f24..c18bfc5 100644 --- a/00-backend/src/main/resources/db/initdb.sql +++ b/00-backend/src/main/resources/db/initdb.sql @@ -25,7 +25,7 @@ CREATE TABLE IF NOT EXISTS Images FOREIGN KEY (article_id) REFERENCES Articles (id) ); -CREATE TABLE IF NOT EXISTS FarmImages +CREATE TABLE IF NOT EXISTS Farm_Images ( id INTEGER PRIMARY KEY NOT NULL, article_id INTEGER NOT NULL, @@ -82,7 +82,7 @@ CREATE TABLE IF NOT EXISTS Orders ON UPDATE CASCADE ); -CREATE TABLE IF NOT EXISTS OrderItems +CREATE TABLE IF NOT EXISTS Order_Items ( id INTEGER NOT NULL PRIMARY KEY, order_id INTEGER NOT NULL,