From a5fd5278511e98b35a0d6d8aaeb54a5dab702844 Mon Sep 17 00:00:00 2001
From: Mikhail Statsenko <112765729+MikhailStatsenko@users.noreply.github.com>
Date: Sun, 28 Apr 2024 23:46:42 +0300
Subject: [PATCH 1/4] =?UTF-8?q?=D0=97=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5?=
=?UTF-8?q?=20=D0=B2=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D0=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +
pom.xml | 68 +++++++++++++++++++
.../croc/analytics/AnalyticsApplication.java | 22 ++++++
.../analytics/ApplicationConfiguration.java | 15 ++++
src/main/java/edu/croc/analytics/Order.java | 27 ++++++++
.../edu/croc/analytics/ReportService.java | 66 ++++++++++++++++++
6 files changed, 200 insertions(+)
create mode 100644 pom.xml
create mode 100644 src/main/java/edu/croc/analytics/AnalyticsApplication.java
create mode 100644 src/main/java/edu/croc/analytics/ApplicationConfiguration.java
create mode 100644 src/main/java/edu/croc/analytics/Order.java
create mode 100644 src/main/java/edu/croc/analytics/ReportService.java
diff --git a/README.md b/README.md
index ee9d69b..650be4b 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,9 @@
4. Найденный в соответствии с условием задачи месяц должен выводиться на английском языке в нижнем регистре. Если месяцев несколько, то на вывод они все подаются на английском языке в нижнем регистре в порядке их следования в течение года.
## Автор решения
+Стаценко Михаил Александрович
## Описание реализации
+
## Инструкция по сборке и запуску решения
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..e5aae90
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,68 @@
+
+
+ 4.0.0
+
+ edu.croc
+ analitics
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.2.2
+
+
+
+
+ UTF-8
+ UTF-8
+
+ 21
+
+ 3.9.6
+
+ 21
+ 21
+ 3.12.1
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ 2.17.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/edu/croc/analytics/AnalyticsApplication.java b/src/main/java/edu/croc/analytics/AnalyticsApplication.java
new file mode 100644
index 0000000..3011a28
--- /dev/null
+++ b/src/main/java/edu/croc/analytics/AnalyticsApplication.java
@@ -0,0 +1,22 @@
+package edu.croc.analytics;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+@RequiredArgsConstructor
+public class AnalyticsApplication implements CommandLineRunner {
+ private final ReportService reportService;
+
+ public static void main(String[] args) {
+ SpringApplication.run(AnalyticsApplication.class);
+ }
+
+ @Override
+ public void run(String... args) throws Exception {
+ String report = reportService.generateReport("format.json");
+ System.out.println(report);
+ }
+}
diff --git a/src/main/java/edu/croc/analytics/ApplicationConfiguration.java b/src/main/java/edu/croc/analytics/ApplicationConfiguration.java
new file mode 100644
index 0000000..01e0e2f
--- /dev/null
+++ b/src/main/java/edu/croc/analytics/ApplicationConfiguration.java
@@ -0,0 +1,15 @@
+package edu.croc.analytics;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ApplicationConfiguration {
+ @Bean
+ public ObjectMapper objectMapper() {
+ return JsonMapper.builder().addModule(new JavaTimeModule()).build();
+ }
+}
diff --git a/src/main/java/edu/croc/analytics/Order.java b/src/main/java/edu/croc/analytics/Order.java
new file mode 100644
index 0000000..a5ffd1e
--- /dev/null
+++ b/src/main/java/edu/croc/analytics/Order.java
@@ -0,0 +1,27 @@
+package edu.croc.analytics;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+public record Order (
+ @JsonProperty("user_id")
+ String userId,
+
+ @JsonProperty("ordered_at")
+ LocalDateTime orderedAt,
+
+ @JsonProperty("status")
+ Status status,
+
+ @JsonProperty("total")
+ BigDecimal total) {
+
+ public enum Status {
+ COMPLETED,
+ CANCELED,
+ CREATED,
+ DELIVERY
+ }
+}
diff --git a/src/main/java/edu/croc/analytics/ReportService.java b/src/main/java/edu/croc/analytics/ReportService.java
new file mode 100644
index 0000000..2fa94c8
--- /dev/null
+++ b/src/main/java/edu/croc/analytics/ReportService.java
@@ -0,0 +1,66 @@
+package edu.croc.analytics;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+import java.time.Month;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Service
+@RequiredArgsConstructor
+public class ReportService {
+ private final ObjectMapper mapper;
+
+ private record Report(List months) {}
+
+ public String generateReport(String filePath) throws IOException {
+ Order[] orders = readOrdersJson(filePath);
+
+ Map totals = getTotalByMonth(orders);
+
+ List months = getMaxTotalMonths(totals);
+ return mapper.writeValueAsString(new Report(months));
+ }
+
+ private Order[] readOrdersJson(String filePath) throws IOException {
+ var source = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8));
+ return mapper.readValue(source, Order[].class);
+ }
+
+ private Map getTotalByMonth(Order[] orders) {
+ Map totals = new ConcurrentHashMap<>(Month.values().length);
+ for (Month month : Month.values()) {
+ totals.put(month, BigDecimal.ZERO);
+ }
+
+ Arrays.stream(orders).parallel()
+ .filter(order -> order.status().equals(Order.Status.COMPLETED))
+ .forEach(order -> totals.compute(
+ order.orderedAt().getMonth(),
+ (moth, currentMothTotal) -> currentMothTotal.add(order.total()))
+ );
+ return totals;
+ }
+
+ private List getMaxTotalMonths(Map totals) {
+ BigDecimal max = Collections.max(totals.values());
+ return totals.entrySet()
+ .stream()
+ .filter(entry -> entry.getValue().equals(max))
+ .map(Map.Entry::getKey)
+ .map(Month::name)
+ .map(String::toLowerCase)
+ .toList();
+ }
+}
From 8b7324f958d58a4411905b5cf65d2bb7bb934eb6 Mon Sep 17 00:00:00 2001
From: Mikhail Statsenko <112765729+MikhailStatsenko@users.noreply.github.com>
Date: Mon, 29 Apr 2024 10:33:42 +0300
Subject: [PATCH 2/4] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?=
=?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8,=20?=
=?UTF-8?q?=D0=BE=D1=82=D1=80=D0=B5=D0=B4=D0=B0=D0=BA=D1=82=D0=B8=D1=80?=
=?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=20=D1=84=D0=B0=D0=B9=D0=BB=20README.md?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 8 +++
pom.xml | 56 ++++++++++++-------
.../croc/analytics/AnalyticsApplication.java | 15 +++--
.../ApplicationConfiguration.java | 2 +-
.../edu/croc/analytics/{ => dto}/Order.java | 2 +-
.../{ => service}/ReportService.java | 11 +++-
6 files changed, 65 insertions(+), 29 deletions(-)
rename src/main/java/edu/croc/analytics/{ => configuration}/ApplicationConfiguration.java (91%)
rename src/main/java/edu/croc/analytics/{ => dto}/Order.java (93%)
rename src/main/java/edu/croc/analytics/{ => service}/ReportService.java (86%)
diff --git a/README.md b/README.md
index 650be4b..b7397ee 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,14 @@
Стаценко Михаил Александрович
## Описание реализации
+Для выполнения поставленной задачи разработано консольное Spring Boot приложение.
+Первым аргументом командной строки передается путь к файлу с исходными данными. `ReportService` в методе `generateReport` при помощи вспомогательных методов читает json файл по указанному пути, затем считает сумму, потраченную пользователями для каждого месяца, после чего находит месяц(-ы) с максимальной суммой и возвращает строку в формате json в соответствии с заданием.
+
+Поскольку речь идет об анализе заказов на маркетплейсе, отчет, вероятно, будет создаваться на основании миллионов заказов, поэтому для подсчета суммы, потраченной пользователями по месяцам используется параллельная обработка, а файл с исходными данными читается при помощи `BufferedReader`.
## Инструкция по сборке и запуску решения
+Последовательно выполнить команды
+
+`mvn clean package`
+`java -jar target/analytics-1.0.jar [путь к файлу с исходными данными]`
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index e5aae90..d106f6d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,8 +5,8 @@
4.0.0
edu.croc
- analitics
- 1.0-SNAPSHOT
+ analytics
+ 1.0
org.springframework.boot
@@ -21,8 +21,6 @@
21
- 3.9.6
-
21
21
3.12.1
@@ -40,29 +38,47 @@
true
-
com.fasterxml.jackson.datatype
jackson-datatype-jsr310
2.17.0
-
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-dependencies
+ prepare-package
+
+ copy-dependencies
+
+
+ ${project.build.directory}/lib
+ false
+ false
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ true
+ lib/
+ edu.croc.analytics.AnalyticsApplication
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/edu/croc/analytics/AnalyticsApplication.java b/src/main/java/edu/croc/analytics/AnalyticsApplication.java
index 3011a28..8bf0563 100644
--- a/src/main/java/edu/croc/analytics/AnalyticsApplication.java
+++ b/src/main/java/edu/croc/analytics/AnalyticsApplication.java
@@ -1,22 +1,29 @@
package edu.croc.analytics;
+import edu.croc.analytics.service.ReportService;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+@Slf4j
@SpringBootApplication
@RequiredArgsConstructor
public class AnalyticsApplication implements CommandLineRunner {
private final ReportService reportService;
public static void main(String[] args) {
- SpringApplication.run(AnalyticsApplication.class);
+ SpringApplication.run(AnalyticsApplication.class, args);
}
@Override
- public void run(String... args) throws Exception {
- String report = reportService.generateReport("format.json");
- System.out.println(report);
+ public void run(String... args) {
+ try {
+ String report = reportService.generateReport(args[0]);
+ System.out.println(report);
+ } catch (Exception e) {
+ log.error("Error generating report", e);
+ }
}
}
diff --git a/src/main/java/edu/croc/analytics/ApplicationConfiguration.java b/src/main/java/edu/croc/analytics/configuration/ApplicationConfiguration.java
similarity index 91%
rename from src/main/java/edu/croc/analytics/ApplicationConfiguration.java
rename to src/main/java/edu/croc/analytics/configuration/ApplicationConfiguration.java
index 01e0e2f..5543903 100644
--- a/src/main/java/edu/croc/analytics/ApplicationConfiguration.java
+++ b/src/main/java/edu/croc/analytics/configuration/ApplicationConfiguration.java
@@ -1,4 +1,4 @@
-package edu.croc.analytics;
+package edu.croc.analytics.configuration;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
diff --git a/src/main/java/edu/croc/analytics/Order.java b/src/main/java/edu/croc/analytics/dto/Order.java
similarity index 93%
rename from src/main/java/edu/croc/analytics/Order.java
rename to src/main/java/edu/croc/analytics/dto/Order.java
index a5ffd1e..27263dd 100644
--- a/src/main/java/edu/croc/analytics/Order.java
+++ b/src/main/java/edu/croc/analytics/dto/Order.java
@@ -1,4 +1,4 @@
-package edu.croc.analytics;
+package edu.croc.analytics.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
diff --git a/src/main/java/edu/croc/analytics/ReportService.java b/src/main/java/edu/croc/analytics/service/ReportService.java
similarity index 86%
rename from src/main/java/edu/croc/analytics/ReportService.java
rename to src/main/java/edu/croc/analytics/service/ReportService.java
index 2fa94c8..5f7e0bf 100644
--- a/src/main/java/edu/croc/analytics/ReportService.java
+++ b/src/main/java/edu/croc/analytics/service/ReportService.java
@@ -1,7 +1,9 @@
-package edu.croc.analytics;
+package edu.croc.analytics.service;
import com.fasterxml.jackson.databind.ObjectMapper;
+import edu.croc.analytics.dto.Order;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
@@ -17,6 +19,7 @@
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+@Slf4j
@Service
@RequiredArgsConstructor
public class ReportService {
@@ -34,8 +37,10 @@ public String generateReport(String filePath) throws IOException {
}
private Order[] readOrdersJson(String filePath) throws IOException {
- var source = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8));
- return mapper.readValue(source, Order[].class);
+ try(var source = new BufferedReader(
+ new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8))) {
+ return mapper.readValue(source, Order[].class);
+ }
}
private Map getTotalByMonth(Order[] orders) {
From 50a9b9c97b9dcb2f113b8a3994b0f2557a471082 Mon Sep 17 00:00:00 2001
From: Mikhail Statsenko <112765729+MikhailStatsenko@users.noreply.github.com>
Date: Mon, 29 Apr 2024 11:19:25 +0300
Subject: [PATCH 3/4] =?UTF-8?q?=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C=D1=88?=
=?UTF-8?q?=D0=B8=D0=B5=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5?=
=?UTF-8?q?=D0=BD=D0=B8=D1=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 6 +++++-
pom.xml | 1 -
.../croc/analytics/service/ReportService.java | 20 +++++++++----------
3 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/README.md b/README.md
index b7397ee..7ad25d2 100644
--- a/README.md
+++ b/README.md
@@ -40,7 +40,11 @@
Поскольку речь идет об анализе заказов на маркетплейсе, отчет, вероятно, будет создаваться на основании миллионов заказов, поэтому для подсчета суммы, потраченной пользователями по месяцам используется параллельная обработка, а файл с исходными данными читается при помощи `BufferedReader`.
## Инструкция по сборке и запуску решения
-Последовательно выполнить команды
+На тестовом стенде должны быть установлены Maven и Java 21+ версии
+Сначала необходимо клонировать репозиторий на локальную машину:
+`git clone https://github.com/MikhailStatsenko/school2024-test-task1.git`
+
+Затем нужно перейти в склонированную директорию и последовательно выполнить команды:
`mvn clean package`
`java -jar target/analytics-1.0.jar [путь к файлу с исходными данными]`
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index d106f6d..8ee326d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,6 @@
21
21
- 3.12.1
diff --git a/src/main/java/edu/croc/analytics/service/ReportService.java b/src/main/java/edu/croc/analytics/service/ReportService.java
index 5f7e0bf..dd0b2e7 100644
--- a/src/main/java/edu/croc/analytics/service/ReportService.java
+++ b/src/main/java/edu/croc/analytics/service/ReportService.java
@@ -13,10 +13,7 @@
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.Month;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@@ -60,12 +57,13 @@ private Map getTotalByMonth(Order[] orders) {
private List getMaxTotalMonths(Map totals) {
BigDecimal max = Collections.max(totals.values());
- return totals.entrySet()
- .stream()
- .filter(entry -> entry.getValue().equals(max))
- .map(Map.Entry::getKey)
- .map(Month::name)
- .map(String::toLowerCase)
- .toList();
+
+ List result = new ArrayList<>();
+ for (Month month : Month.values()) {
+ if (totals.get(month).equals(max)) {
+ result.add(month.name().toLowerCase());
+ }
+ }
+ return result;
}
}
From 4db74f7c839882e49e5f7aa18bf9b8e31f8f8fb0 Mon Sep 17 00:00:00 2001
From: Mikhail Statsenko <112765729+MikhailStatsenko@users.noreply.github.com>
Date: Wed, 1 May 2024 08:45:29 +0300
Subject: [PATCH 4/4] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?=
=?UTF-8?q?=D0=BB=20=D1=81=D1=80=D0=B0=D0=B2=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5?=
=?UTF-8?q?=20BigDecimal?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/edu/croc/analytics/service/ReportService.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/edu/croc/analytics/service/ReportService.java b/src/main/java/edu/croc/analytics/service/ReportService.java
index dd0b2e7..bf0d0fc 100644
--- a/src/main/java/edu/croc/analytics/service/ReportService.java
+++ b/src/main/java/edu/croc/analytics/service/ReportService.java
@@ -60,7 +60,7 @@ private List getMaxTotalMonths(Map totals) {
List result = new ArrayList<>();
for (Month month : Month.values()) {
- if (totals.get(month).equals(max)) {
+ if (totals.get(month).compareTo(max) == 0) {
result.add(month.name().toLowerCase());
}
}