diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..65f20b1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
diff --git a/README.md b/README.md
index ee9d69b..448015e 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,31 @@
4. Найденный в соответствии с условием задачи месяц должен выводиться на английском языке в нижнем регистре. Если месяцев несколько, то на вывод они все подаются на английском языке в нижнем регистре в порядке их следования в течение года.
## Автор решения
-
+Гуренков Максим Сергеевич
## Описание реализации
-
+Для выполнения задачи было решено использовать библиотеки jackson и lombok.
+### Пакет "application"
+В пакете `application` находятся классы, реализующие основную логику работы программы: `Main`, `Report` и `ReportGenerator`.
+ - В классе `Main`, с помощью библиотеки jackson данными из файла `input.json` инициализируется список объектов типа `Order`, который потом передается на вход статическому методу `generate()` класса `ReportGenerator`. Возвращаемая json-строка выводится на экран.
+ - Класс `Report` содержит в себе только одно поле - список месяцев, в которые были произведены наибольшие затраты. Используется для сериализации результата в json формат.
+ - Класс `ReportGenerator` отвечает за генерацию отчетов. Содержит один публичный (`generate()`) и два приватных метода (`countEarningsForEachMonth()` и `findMonthsWithMaxEarnings()`).
+ + В методе `countEarningsForEachMonth()` создается `Map`, где ключ - порядковый номер месяца (`Integer`), а значение - общая сумма трат в этом месяце (`Double`). В качестве реализации была выбрана коллекция `TreeMap`, так как она поддерживает автоматическую сортировку элементов по ключу в естественном порядке. Далее в цикле выполняется обход массива заказов, и заполняется `Map`.
+ + Метод `findMonthsWithMaxEarnings()` отвечает за формирование списка с месяцами, в которые были произведены максимальные затраты.
+ + Метод `generate()` использует все перечисленные методы для формирования отчета типа `Report`, после чего с помощью библиотеки jackson сериализуется в json формат.
+### Пакет "data"
+В пакете data находятся классы, представляющие из себя объектную модель входных данных: `Order` и `Status`.
+ - Класс `Order` содержит все поля, соответствующие аттрибутам элементов входного массива: `userId`, `orderedAt`, `status`, `total`.
+ - Перечисление `Status` - тип данных для поля `status` класса `Order`: COMPLETED, CANCELED, CREATED, DELIVERY.
+### Пакет "utils"
+В пакете `utils` содержится один класс - `LocalDateTimeDeserializer`, необходимый для корректной десериализации даты в поле типа `LocalDateTime` с помощью jackson.
## Инструкция по сборке и запуску решения
+- Убедитесь, что у вас установлен maven
+- Перейдите в корневую директорию проекта и выполните команду:
+```bash
+mvn package
+```
+- В сгенерированной директории `target` будет лежать jar файл `solution-1.0-SNAPSHOT.jar`, который можно запустить с помощью команды:
+```bash
+java -jar solution-1.0-SNAPSHOT.jar
+```
+**Обратите внимание, что для корректной работы программы входные данные должны находиться в файле с названием `input.json`, который должен лежать в той же директории, что и jar файл.**
\ No newline at end of file
diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml
new file mode 100644
index 0000000..c2d3fe6
--- /dev/null
+++ b/dependency-reduced-pom.xml
@@ -0,0 +1,43 @@
+
+
+ 4.0.0
+ org.example
+ solution
+ 1.0-SNAPSHOT
+
+
+
+ maven-shade-plugin
+ 3.5.1
+
+
+ package
+
+ shade
+
+
+
+
+ application.Main
+
+
+
+
+
+
+
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.30
+ provided
+
+
+
+ 19
+ 19
+ UTF-8
+
+
diff --git a/format.json b/format.json
deleted file mode 100644
index 94aad31..0000000
--- a/format.json
+++ /dev/null
@@ -1,38 +0,0 @@
-[
- {
- "user_id": "3acfb0b7-04bd-4978-be4c-3929372277c1",
- "ordered_at": "2023-01-16T13:56:39.492",
- "status": "COMPLETED",
- "total": "1917.00"
- },
- {
- "user_id": "25b003b9-ab22-4a24-a616-dd0303f983d8",
- "ordered_at": "2023-03-05T08:34:21.123",
- "status": "COMPLETED",
- "total": "13990.00"
- },
- {
- "user_id": "e1470ada-fcbb-4424-8c46-065b6409ca4b",
- "ordered_at": "2016-03-16T13:56:39.492",
- "status": "COMPLETED",
- "total": "215.50"
- },
- {
- "user_id": "081a47a5-b7bf-462c-a11a-68002a179152",
- "ordered_at": "2023-12-08T21:36:59.281",
- "status": "COMPLETED",
- "total": "49499.00"
- },
- {
- "user_id": "0999c6aa-1bac-4ded-9a54-92fff4f34d69",
- "ordered_at": "2023-12-14T11:10:29.408",
- "status": "CANCELED",
- "total": "13650.00"
- },
- {
- "user_id": "0999c6aa-1bac-4ded-9a54-92fff4f34d69",
- "ordered_at": "2023-12-14T11:15:31.108",
- "status": "COMPLETED",
- "total": "14760.00"
- }
-]
\ No newline at end of file
diff --git a/input.json b/input.json
new file mode 100644
index 0000000..d50f878
--- /dev/null
+++ b/input.json
@@ -0,0 +1,20 @@
+[
+ {
+ "user_id": "0999c6aa-1bac-4ded-9a54-92fff4f34d69",
+ "ordered_at": "2023-12-14T11:15:31.108",
+ "status": "COMPLETED",
+ "total": "500"
+ },
+ {
+ "user_id": "0999c6aa-1bac-4ded-9a54-92fff4f34d69",
+ "ordered_at": "2023-12-14T11:15:31.108",
+ "status": "CANCELED",
+ "total": "5000"
+ },
+ {
+ "user_id": "0999c6aa-1bac-4ded-9a54-92fff4f34d69",
+ "ordered_at": "2023-01-14T11:15:31.108",
+ "status": "COMPLETED",
+ "total": "500"
+ }
+]
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..cb48630
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,71 @@
+
+
+ 4.0.0
+
+ org.example
+ solution
+ 1.0-SNAPSHOT
+
+
+ 19
+ 19
+ UTF-8
+
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ 2.16.1
+
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ 2.16.1
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.16.1
+
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.30
+ provided
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.5.1
+
+
+ package
+
+ shade
+
+
+
+
+ application.Main
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/application/Main.java b/src/main/java/application/Main.java
new file mode 100644
index 0000000..a5aa446
--- /dev/null
+++ b/src/main/java/application/Main.java
@@ -0,0 +1,20 @@
+package application;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import data.Order;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+class Main {
+ public static void main(String[] args) {
+ ObjectMapper objectMapper = new ObjectMapper();
+ try {
+ List orders = objectMapper.readValue(new File("input.json"), new TypeReference<>(){});
+ System.out.println(ReportGenerator.generate(orders));
+ } catch (IOException e) {
+ System.out.println(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/application/Report.java b/src/main/java/application/Report.java
new file mode 100644
index 0000000..14c0fd4
--- /dev/null
+++ b/src/main/java/application/Report.java
@@ -0,0 +1,14 @@
+package application;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.List;
+
+public class Report {
+ @JsonProperty("months")
+ private List months;
+
+ public Report(List months) {
+ this.months = months;
+ }
+}
diff --git a/src/main/java/application/ReportGenerator.java b/src/main/java/application/ReportGenerator.java
new file mode 100644
index 0000000..61797cf
--- /dev/null
+++ b/src/main/java/application/ReportGenerator.java
@@ -0,0 +1,52 @@
+package application;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import data.Order;
+import data.Status;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.time.Month;
+import java.util.*;
+
+public class ReportGenerator {
+ public static String generate(List orders) throws IOException {
+ Map earningsForEachMonth = countEarningsForEachMonth(orders); // Формируем мапу из списка всех заказов, ключом в которой
+ // является порядковый номер месяца, а значением - сумма потраченная в этот месяц
+ List maxMonths = findMonthsWithMaxEarnings(earningsForEachMonth); // Формируем список с месяцами, в которые совершались наибольшие затраты
+
+ ObjectMapper objectMapper = new ObjectMapper();
+ StringWriter output = new StringWriter();
+ objectMapper.writeValue(output, new Report(maxMonths));
+ return output.toString();
+ }
+
+ private static Map countEarningsForEachMonth(List orders) {
+ Map earningsForEachMonth = new TreeMap<>(); // Создаем TreeMap, так как тогда коллекция будет по умолчанию
+ // отсортированна по ключу в порядке возрастания (в нашем случае это означает, что месяца будут располагаться в порядке их следования в течение года)
+ for (int i = 0; i < orders.size(); i ++) { // проходимся по списку заказов
+ if (orders.get(i).getStatus() == Status.COMPLETED) {
+ int month = orders.get(i).getOrderedAt().getMonthValue();
+ if (earningsForEachMonth.get(month) == null) {
+ earningsForEachMonth.put(month, orders.get(i).getTotal()); // если в мапе еще нет ни одного
+ // элемента с таким ключом, то кладем его туда, в качестве значения указываем сумму заказа
+ } else {
+ earningsForEachMonth.put(month, earningsForEachMonth.get(month) + orders.get(i).getTotal()); // если в мапе уже есть элемент с таким ключом,
+ // то прибавляем к значению в мапе сумму текущего заказа
+ }
+ }
+ }
+ return earningsForEachMonth;
+ }
+
+ private static List findMonthsWithMaxEarnings(Map earningsForEachMonth) {
+ List maxMonths = new ArrayList<>();
+ if (earningsForEachMonth.isEmpty()) {return maxMonths;} // если не проверить, то при вызове Collections.max() может вылететь ошибка
+ double maxValue = Collections.max(earningsForEachMonth.values());
+ for (Map.Entry entry : earningsForEachMonth.entrySet()) {
+ if (entry.getValue().equals(maxValue)) {
+ maxMonths.add(Month.of(entry.getKey()).toString().toLowerCase());
+ }
+ }
+ return maxMonths;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/data/Order.java b/src/main/java/data/Order.java
new file mode 100644
index 0000000..9ccee84
--- /dev/null
+++ b/src/main/java/data/Order.java
@@ -0,0 +1,18 @@
+package data;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import lombok.Data;
+import utils.LocalDateTimeDeserializer;
+import java.time.LocalDateTime;
+
+@Data
+public class Order {
+ @JsonProperty("user_id")
+ private String userId;
+ @JsonProperty("ordered_at")
+ @JsonDeserialize(using = LocalDateTimeDeserializer.class)
+ private LocalDateTime orderedAt;
+ private Status status;
+ private double total;
+}
diff --git a/src/main/java/data/Status.java b/src/main/java/data/Status.java
new file mode 100644
index 0000000..f889418
--- /dev/null
+++ b/src/main/java/data/Status.java
@@ -0,0 +1,8 @@
+package data;
+
+public enum Status {
+ COMPLETED,
+ CANCELED,
+ CREATED,
+ DELIVERY
+}
diff --git a/src/main/java/utils/LocalDateTimeDeserializer.java b/src/main/java/utils/LocalDateTimeDeserializer.java
new file mode 100644
index 0000000..e3107c3
--- /dev/null
+++ b/src/main/java/utils/LocalDateTimeDeserializer.java
@@ -0,0 +1,27 @@
+package utils;
+
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+public class LocalDateTimeDeserializer extends StdDeserializer {
+ private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
+ public LocalDateTimeDeserializer() {
+ this(null);
+ }
+
+ public LocalDateTimeDeserializer(Class> vc) {
+ super(vc);
+ }
+
+ @Override
+ public LocalDateTime deserialize(JsonParser jsonparser, DeserializationContext context) throws IOException {
+ JsonNode node = jsonparser.getCodec().readTree(jsonparser);
+ String strDate = node.asText();
+ return LocalDateTime.parse(strDate, formatter);
+ }
+}
diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..c539e19
--- /dev/null
+++ b/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: application.Main
+