forked from Alexey/DAS_2024_1
Compare commits
8 Commits
minhasapov
...
borschevsk
| Author | SHA1 | Date | |
|---|---|---|---|
| a4f9cf13cc | |||
| ef68b506b8 | |||
| 520337f92d | |||
|
|
0c3e973307 | ||
| c474c13c4a | |||
| 829a04a913 | |||
|
|
c8dbd5fb37 | ||
| 253ad80e31 |
38
borschevskaya_anna_lab_2/.gitignore
vendored
Normal file
38
borschevskaya_anna_lab_2/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.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
|
||||
43
borschevskaya_anna_lab_2/README.md
Normal file
43
borschevskaya_anna_lab_2/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Отчет. Лабораторная работа 2
|
||||
|
||||
В рамках лабораторной работы №2 были написаны два сервиса, работающих с текстовыми файлами.
|
||||
Для первого сервиса был выбран вариант задания №5:
|
||||
```
|
||||
Ищет в каталоге /var/data файл с самым коротким названием и перекладывает его в /var/result/data.txt.
|
||||
```
|
||||
А для второго - №2:
|
||||
```
|
||||
Ищет наименьшее число из файла /var/data/data.txt и сохраняет его третью степень в /var/result/result.txt.
|
||||
```
|
||||
## Описание
|
||||
Сначала сервис first перемещает данные из файла с самым коротким названием, находящегося в указанной примонтированной директории, в выходную папку.
|
||||
Доступ к выходной папке имеет второй сервис, который выводит наименьшее число из помещенного первым сервисом файла
|
||||
в третьей степени в выходной файл.
|
||||
Выходной файл расположен в примонтированной директории и доступен на машине, где запускаются сервисы.
|
||||
|
||||
В Dockerfile используется многоэтапная сборка с использованием нескольких базовых образов на каждом этапе.
|
||||
Описание значения каждой строки есть в Dockerfile в сервисе first.
|
||||
|
||||
В файле docker-compose.yml приведено описание новых строк, связанных с подключением примонтированных томов.
|
||||
Стоит отметить, что для "общения" сервисов используется общий том common, который монтируется в контейнер по пути /var/result. Это позволяет сохранять результаты
|
||||
работы первого сервиса для использования вторым сервисом.
|
||||
## Как запустить
|
||||
Для того, чтобы запустить сервисы, необходимо выполнить следующие действия:
|
||||
1. Установить и запустить Docker Engine или Docker Desktop
|
||||
2. Через консоль перейти в папку, в которой расположен файл docker-compose.yml
|
||||
3. Выполнить команду:
|
||||
```
|
||||
docker compose up --build
|
||||
```
|
||||
В случае успешного запуска всех контейнеров в консоли будет выведено следующее сообщение:
|
||||
```
|
||||
✔ Network borschevskaya_anna_lab_2_default Created 0.1s
|
||||
✔ Container borschevskaya_anna_lab_2-first-1 Created 0.1s
|
||||
✔ Container borschevskaya_anna_lab_2-second-1 Created 0.1s
|
||||
Attaching to borschevskaya_anna_lab_2-first-1, borschevskaya_anna_lab_2-second-1
|
||||
```
|
||||
Далее, в консоль каждого сервиса будут выведены сообщения о том, как прошла обработка файлов.
|
||||
В случае отсутствия заданных значений переменных окружения INPUT_PATH и OUTPUT_PATH и
|
||||
в иных исключительных ситуация будет выведена информация об этом.
|
||||
## Видео-отчет
|
||||
Работоспособность лабораторной работы можно оценить в следующем [видео](https://disk.yandex.ru/i/LFxdyRUFQDwXEQ).
|
||||
22
borschevskaya_anna_lab_2/docker-compose.yml
Normal file
22
borschevskaya_anna_lab_2/docker-compose.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
services:
|
||||
first:
|
||||
build: ./first # директория, в которой нужно искать Dockerfile для сборки первого сервиса
|
||||
environment:
|
||||
INPUT_PATH: /var/data/ # директория с входными данными для обработки файлов
|
||||
OUTPUT_PATH: /var/result/ # директория с выходными данными обработки
|
||||
volumes:
|
||||
- ./volumes/input:/var/data # монтируется локальная папка с входными данными в папку внутри контейнера
|
||||
- common:/var/result # монтируется общий для двух сервисов том, в который first сложит результаты обработки по варианту
|
||||
second:
|
||||
build: ./second # директория, в которой нужно искать Dockerfile для сборки второго сервиса
|
||||
depends_on: # сервис second зависит от сервиса first и будет запущен после него
|
||||
- first
|
||||
environment:
|
||||
INPUT_PATH: /var/result/
|
||||
OUTPUT_PATH: /var/data/
|
||||
volumes:
|
||||
- ./volumes/output:/var/data
|
||||
- common:/var/result # монтируется общий для двух сервисов том, из которого second получит результаты обработки first сервиса и выполнит свою логику
|
||||
|
||||
volumes:
|
||||
common:
|
||||
25
borschevskaya_anna_lab_2/first/Dockerfile
Normal file
25
borschevskaya_anna_lab_2/first/Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
||||
# Используем образ Maven для сборки
|
||||
FROM maven:3.8-eclipse-temurin-21-alpine AS build
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем только pom.xml и загружаем зависимости
|
||||
# Так зависимости закэшируются в Docker при изменении кода закэшированные слои с зависимостями будут подгружаться быстрее
|
||||
COPY pom.xml .
|
||||
RUN mvn dependency:go-offline
|
||||
|
||||
# Копируем остальные исходные файлы
|
||||
COPY src ./src
|
||||
|
||||
# Собираем весь проект
|
||||
RUN mvn clean package -DskipTests
|
||||
|
||||
# Используем официальный образ JDK для запуска собранного jar-файла
|
||||
FROM eclipse-temurin:21-jdk-alpine
|
||||
|
||||
# Копируем jar-файл из предыдущего этапа
|
||||
COPY --from=build /app/target/*.jar /app.jar
|
||||
|
||||
# Указываем команду для запуска приложения
|
||||
CMD ["java", "-jar", "app.jar"]
|
||||
37
borschevskaya_anna_lab_2/first/pom.xml
Normal file
37
borschevskaya_anna_lab_2/first/pom.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.first</groupId>
|
||||
<artifactId>first</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<!-- Build an executable JAR -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addClasspath>true</addClasspath>
|
||||
<classpathPrefix>lib/</classpathPrefix>
|
||||
<mainClass>ru.first.Main</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,50 @@
|
||||
package ru.first;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
import static java.util.Objects.isNull;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static final String INPUT_PATH = System.getenv("INPUT_PATH");
|
||||
public static final String OUTPUT_PATH = System.getenv("OUTPUT_PATH");
|
||||
public static final String RESULT_FILE_NAME = "data.txt";
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
if (isNull(INPUT_PATH) || INPUT_PATH.isEmpty() || isNull(OUTPUT_PATH) || OUTPUT_PATH.isEmpty()) {
|
||||
System.out.printf("Отсутствуют переменные окружения INPUT_PATH = '%s' или OUTPUT_PATH = '%s'%n",
|
||||
INPUT_PATH, OUTPUT_PATH);
|
||||
return;
|
||||
}
|
||||
var inputPathDir = Path.of(INPUT_PATH);
|
||||
if (!Files.exists(inputPathDir)) {
|
||||
Files.createDirectory(inputPathDir);
|
||||
}
|
||||
var inputDirectory = new File(INPUT_PATH);
|
||||
var allDirFiles = inputDirectory.listFiles();
|
||||
if (isNull(allDirFiles) || allDirFiles.length == 0) {
|
||||
System.out.println("Директория пуста");
|
||||
return;
|
||||
}
|
||||
var dirFiles = Arrays.stream(allDirFiles).filter(File::isFile).toList();
|
||||
if (dirFiles.isEmpty()) {
|
||||
System.out.println("В указанной директории нет подходящих для обработки файлов");
|
||||
return;
|
||||
}
|
||||
|
||||
var shortestName = dirFiles.stream().min(Comparator.comparing(file -> file.getName().length())).get();
|
||||
|
||||
var outputPathDir = Path.of(OUTPUT_PATH);
|
||||
if (!Files.exists(outputPathDir)) {
|
||||
Files.createDirectory(outputPathDir);
|
||||
}
|
||||
var resultFilePath = Path.of(OUTPUT_PATH + File.separator + RESULT_FILE_NAME);
|
||||
Files.move(Path.of(INPUT_PATH + File.separator + shortestName.getName()), resultFilePath, REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
16
borschevskaya_anna_lab_2/second/Dockerfile
Normal file
16
borschevskaya_anna_lab_2/second/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
FROM maven:3.8-eclipse-temurin-21-alpine AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY pom.xml .
|
||||
RUN mvn dependency:go-offline
|
||||
|
||||
COPY src ./src
|
||||
|
||||
RUN mvn clean package -DskipTests
|
||||
|
||||
FROM eclipse-temurin:21-jdk-alpine
|
||||
|
||||
COPY --from=build /app/target/*.jar /app.jar
|
||||
|
||||
CMD ["java", "-jar", "app.jar"]
|
||||
36
borschevskaya_anna_lab_2/second/pom.xml
Normal file
36
borschevskaya_anna_lab_2/second/pom.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.second</groupId>
|
||||
<artifactId>second</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<addClasspath>true</addClasspath>
|
||||
<classpathPrefix>lib/</classpathPrefix>
|
||||
<mainClass>ru.second.Main</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,51 @@
|
||||
package ru.second;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import static java.util.Objects.isNull;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static final String INPUT_PATH = System.getenv("INPUT_PATH");
|
||||
public static final String INPUT_FILE_NAME = "data.txt";
|
||||
|
||||
public static final String OUTPUT_PATH = System.getenv("OUTPUT_PATH");
|
||||
public static final String RESULT_FILE_NAME = "result.txt";
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (isNull(INPUT_PATH) || INPUT_PATH.isEmpty() || isNull(OUTPUT_PATH) || OUTPUT_PATH.isEmpty()) {
|
||||
System.out.printf("Отсутствуют переменные окружения INPUT_PATH = '%s' или OUTPUT_PATH = '%s'%n",
|
||||
INPUT_PATH, OUTPUT_PATH);
|
||||
return;
|
||||
}
|
||||
|
||||
var inputFile = new File(INPUT_PATH + File.separator + INPUT_FILE_NAME);
|
||||
if (!inputFile.exists()) {
|
||||
System.out.println("Входной файл не существует");
|
||||
return;
|
||||
}
|
||||
|
||||
try (var stream = Files.lines(inputFile.toPath());
|
||||
var writer = new FileWriter(OUTPUT_PATH + File.separator + RESULT_FILE_NAME);
|
||||
) {
|
||||
var min = stream.map(Main::parseInt).reduce(Integer::min);
|
||||
if (min.isEmpty()) {
|
||||
System.out.println("Не найдено минимальное значение среди строк файла");
|
||||
return;
|
||||
}
|
||||
var minValue = Math.pow(min.get(), 3);
|
||||
System.out.printf("Get min value = '%d'%n", min.get());
|
||||
writer.append(Double.toString(minValue));
|
||||
System.out.printf("To file %s was written value %f%n", RESULT_FILE_NAME, minValue);
|
||||
} catch (Exception ex) {
|
||||
System.out.println(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer parseInt(String line) {
|
||||
line = line.replace("\\n", "");
|
||||
return Integer.parseInt(line);
|
||||
}
|
||||
}
|
||||
58
borschevskaya_anna_lab_3/README.md
Normal file
58
borschevskaya_anna_lab_3/README.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Отчет. Лабораторная работа 3
|
||||
|
||||
## Описание
|
||||
В рамках лабораторной работы № 3 были реализованы два сервиса (Java + Spring), осуществляющие CRUD-операции над сущностями.
|
||||
|
||||
Модель данных следующая:
|
||||
|
||||
Сущность "Компания" (сервис company)
|
||||
- идентификатор компании
|
||||
- название
|
||||
- адрес
|
||||
|
||||
Сущность "Вакансия" (сервис vacancy)
|
||||
- идентификатор вакансии
|
||||
- название
|
||||
- описание
|
||||
- нижняя граница зарплаты
|
||||
- верхняя граница зарплаты
|
||||
- идентификатор компании
|
||||
|
||||
Компания с вакансией связана как "один ко многим".
|
||||
|
||||
Каждый из сервисов имеет API с пятью эндпоинтами. При этом в сервисе vacancy при запросе вакансии по id происходит
|
||||
дополнительный запрос в сервис company для получения информации по компании, связанной с вакансией.
|
||||
Происходит это взаимодействие с помощью библиотеки OpenFeign, которая "под капотом" использует HttpClient.
|
||||
|
||||
В качестве хранилища данных использовалась СУБД Postgres. У каждого сервиса своя база данных в поднятой СУБД.
|
||||
Для создания схемы БД была использована библиотека Flyway, которая применила написанные миграции при старте приложения.
|
||||
|
||||
Запросы к сервисам проксирует шлюз на основе Nginx. Для этого перед запуском nginx был описан конфигурационный файл nginx.conf,
|
||||
в котором описан прослушиваемый порт и название сервера (в блоке server), маршруты до микросервисов и параметры проксирования (в блоке location).
|
||||
|
||||
Таким образом, с помощью Docker Compose были подняты сервисы:
|
||||
- company
|
||||
- vacancy
|
||||
- postgres
|
||||
- nginx
|
||||
## Как запустить
|
||||
Для того, чтобы запустить сервисы, необходимо выполнить следующие действия:
|
||||
1. Установить и запустить Docker Engine или Docker Desktop
|
||||
2. Через консоль перейти в папку, в которой расположен файл docker-compose.yml
|
||||
3. Выполнить команду:
|
||||
```
|
||||
docker compose up --build
|
||||
```
|
||||
В случае успешного запуска всех контейнеров в консоли будет выведено следующее сообщение:
|
||||
```
|
||||
[+] Running 5/5
|
||||
✔ Network borschevskaya_anna_lab_3_default Created 0.0s
|
||||
✔ Container postgres Started 0.6s
|
||||
✔ Container vacancy Started 1.1s
|
||||
✔ Container company Started 0.9s
|
||||
✔ Container borschevskaya_anna_lab_3-nginx-1 Started
|
||||
```
|
||||
Далее можно осуществлять запросы к сервисам по адресу http://localhost/{location}, где часть пути location меняется в зависимости от сервиса и запроса.
|
||||
## Видео-отчет
|
||||
Работоспособность лабораторной работы можно оценить в следующем [видео](https://disk.yandex.ru/i/KPNBfnlcgl1auw).
|
||||
Демонстрация взаимодействия с системой производится с применением сервиса Postman.
|
||||
38
borschevskaya_anna_lab_3/company-service/.gitignore
vendored
Normal file
38
borschevskaya_anna_lab_3/company-service/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.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
|
||||
21
borschevskaya_anna_lab_3/company-service/Dockerfile
Normal file
21
borschevskaya_anna_lab_3/company-service/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
# Используем образ Maven для сборки
|
||||
FROM maven:3.8-eclipse-temurin-21-alpine AS build
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем остальные исходные файлы
|
||||
COPY pom.xml .
|
||||
COPY src src
|
||||
|
||||
# Собираем весь проект
|
||||
RUN mvn clean package -DskipTests
|
||||
|
||||
# Используем официальный образ JDK для запуска собранного jar-файла
|
||||
FROM eclipse-temurin:21-jdk-alpine
|
||||
|
||||
# Копируем jar-файл из предыдущего этапа
|
||||
COPY --from=build /app/target/*.jar /app.jar
|
||||
|
||||
# Указываем команду для запуска приложения
|
||||
CMD ["java", "-jar", "app.jar"]
|
||||
66
borschevskaya_anna_lab_3/company-service/pom.xml
Normal file
66
borschevskaya_anna_lab_3/company-service/pom.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.somecompany</groupId>
|
||||
<artifactId>company-service</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,12 @@
|
||||
package ru.somecompany;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class CompanyApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(CompanyApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package ru.somecompany.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import ru.somecompany.domain.CompanyEntity;
|
||||
import ru.somecompany.domain.CreateCompanyRequest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
@Validated
|
||||
@Tag(name = "company", description = "API для управления компаниями")
|
||||
public interface CompanyController {
|
||||
|
||||
@Operation(summary = "Получение всех компаний")
|
||||
@GetMapping(value = "/api/v1/company")
|
||||
List<CompanyEntity> getCompanies();
|
||||
|
||||
@Operation(summary = "Создание компании")
|
||||
@PostMapping(value = "/api/v1/company")
|
||||
CompanyEntity createCompany(@RequestBody @NotNull CreateCompanyRequest companyRequest);
|
||||
|
||||
@Operation(summary = "Получение информации о компании по id")
|
||||
@GetMapping(value = "/api/v1/company/{companyId}")
|
||||
CompanyEntity getCompany(@PathVariable UUID companyId);
|
||||
|
||||
@Operation(summary = "Редактирование компании")
|
||||
@PutMapping(value = "/api/v1/company/{companyId}")
|
||||
CompanyEntity updateCompany(@PathVariable UUID companyId,
|
||||
@RequestBody @NotNull CreateCompanyRequest companyRequest);
|
||||
|
||||
@Operation(summary = "Удалении компании по id")
|
||||
@DeleteMapping(value = "/api/v1/company/{companyId}")
|
||||
void deleteCompany(@PathVariable UUID companyId);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package ru.somecompany.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.somecompany.domain.CompanyEntity;
|
||||
import ru.somecompany.domain.CreateCompanyRequest;
|
||||
import ru.somecompany.service.CompanyService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class CompanyControllerImpl implements CompanyController {
|
||||
|
||||
private final CompanyService companyService;
|
||||
|
||||
@Override
|
||||
public List<CompanyEntity> getCompanies() {
|
||||
return companyService.getCompanies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompanyEntity createCompany(CreateCompanyRequest companyRequest) {
|
||||
return companyService.createCompany(companyRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompanyEntity getCompany(UUID companyId) {
|
||||
return companyService.getCompany(companyId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompanyEntity updateCompany(UUID companyId, CreateCompanyRequest companyRequest) {
|
||||
return companyService.updateCompany(companyId, companyRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteCompany(UUID companyId) {
|
||||
companyService.deleteCompany(companyId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package ru.somecompany.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Builder
|
||||
@Table(name = "company")
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class CompanyEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "name", unique = true, nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "address", unique = true, nullable = false)
|
||||
private String address;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ru.somecompany.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class CreateCompanyRequest {
|
||||
|
||||
private String name;
|
||||
|
||||
private String address;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.springframework.http.HttpStatus.NOT_FOUND;
|
||||
|
||||
@Slf4j
|
||||
public abstract class AbstractWebExceptionHandler {
|
||||
|
||||
public static ResponseEntity<ResponseError> buildResponse(ErrorCode errorCode, HttpStatus status, String msg,
|
||||
boolean details, Throwable ex) {
|
||||
return buildResponse(errorCode.name(), status, msg, details, ex, LocalDateTime.now());
|
||||
}
|
||||
|
||||
public static ResponseEntity<ResponseError> buildResponse(String errorCode, HttpStatus status, String msg,
|
||||
boolean details, Throwable ex, LocalDateTime timestamp) {
|
||||
if (details) {
|
||||
log.error(msg, ex); // with stack-trace
|
||||
} else {
|
||||
var message = StringUtils.equals(msg, ex.getMessage()) ? msg : msg + ": " + ex.getMessage();
|
||||
if (status == NOT_FOUND) {
|
||||
log.warn(message);
|
||||
} else {
|
||||
log.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.status(status.value())
|
||||
.body(ResponseError.builder()
|
||||
.code(errorCode)
|
||||
.message(msg)
|
||||
.timestamp(timestamp)
|
||||
.status(status.value())
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
public enum BasicError implements ErrorCode {
|
||||
NOT_FOUND
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
@Getter
|
||||
public class CompanyAppRuntimeException extends RuntimeException {
|
||||
private final ErrorCode errorCode;
|
||||
private final HttpStatus status;
|
||||
private final boolean details;
|
||||
|
||||
public CompanyAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message) {
|
||||
this(errorCode, status, message, null);
|
||||
}
|
||||
|
||||
public CompanyAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message, Throwable error) {
|
||||
this(errorCode, status, message, true, error);
|
||||
}
|
||||
|
||||
public CompanyAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message, boolean details, Throwable error) {
|
||||
super(message, error);
|
||||
this.errorCode = errorCode;
|
||||
this.status = status;
|
||||
this.details = details;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public interface ErrorCode extends Serializable {
|
||||
|
||||
String name();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static ru.somecompany.exception.BasicError.NOT_FOUND;
|
||||
|
||||
public class ResourceNotFoundException extends CompanyAppRuntimeException {
|
||||
|
||||
public ResourceNotFoundException(ErrorCode errorCode, String message) {
|
||||
this(errorCode, message, null);
|
||||
}
|
||||
|
||||
public ResourceNotFoundException(ErrorCode errorCode, String message, Throwable error) {
|
||||
super(errorCode, HttpStatus.NOT_FOUND, message, error);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound(String message, Object... args) {
|
||||
return new ResourceNotFoundException(NOT_FOUND, String.format(message, args), null);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound() {
|
||||
return new ResourceNotFoundException(NOT_FOUND, "Запрашиваемые данные не найдены", null);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound(Throwable error) {
|
||||
return new ResourceNotFoundException(NOT_FOUND, error.getMessage(), error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ResponseError {
|
||||
@JsonProperty(required = true)
|
||||
private LocalDateTime timestamp;
|
||||
@JsonProperty(required = true)
|
||||
private Integer status;
|
||||
@JsonProperty(required = true)
|
||||
private String message;
|
||||
@JsonProperty(required = true)
|
||||
private String code;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@RestControllerAdvice
|
||||
@RequiredArgsConstructor
|
||||
public class WebExceptionHandler extends AbstractWebExceptionHandler {
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@ExceptionHandler(CompanyAppRuntimeException.class)
|
||||
public ResponseEntity<ResponseError> handleException(CompanyAppRuntimeException ex) {
|
||||
return buildResponse(ex.getErrorCode(), ex.getStatus(), ex.getMessage(), ex.isDetails(), ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ru.somecompany.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import ru.somecompany.domain.CompanyEntity;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface CompanyRepository extends JpaRepository<CompanyEntity, UUID> {
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.somecompany.service;
|
||||
|
||||
import ru.somecompany.domain.CompanyEntity;
|
||||
import ru.somecompany.domain.CreateCompanyRequest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface CompanyService {
|
||||
|
||||
List<CompanyEntity> getCompanies();
|
||||
|
||||
CompanyEntity createCompany(CreateCompanyRequest companyRequest);
|
||||
|
||||
CompanyEntity updateCompany(UUID companyId, CreateCompanyRequest companyRequest);
|
||||
|
||||
void deleteCompany(UUID companyId);
|
||||
|
||||
CompanyEntity getCompany(UUID companyId);
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package ru.somecompany.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.somecompany.domain.CompanyEntity;
|
||||
import ru.somecompany.domain.CreateCompanyRequest;
|
||||
import ru.somecompany.repository.CompanyRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static ru.somecompany.exception.ResourceNotFoundException.notFound;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CompanyServiceImpl implements CompanyService {
|
||||
|
||||
private final CompanyRepository companyRepository;
|
||||
|
||||
@Override
|
||||
public List<CompanyEntity> getCompanies() {
|
||||
return companyRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompanyEntity createCompany(CreateCompanyRequest companyRequest) {
|
||||
var entity = CompanyEntity.builder()
|
||||
.name(companyRequest.getName())
|
||||
.address(companyRequest.getAddress())
|
||||
.build();
|
||||
return companyRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompanyEntity updateCompany(UUID companyId, CreateCompanyRequest companyRequest) {
|
||||
var company = getById(companyId);
|
||||
company.setName(companyRequest.getName());
|
||||
company.setAddress(companyRequest.getAddress());
|
||||
return companyRepository.save(company);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteCompany(UUID companyId) {
|
||||
var company = getById(companyId);
|
||||
companyRepository.delete(company);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompanyEntity getCompany(UUID companyId) {
|
||||
return getById(companyId);
|
||||
}
|
||||
|
||||
private CompanyEntity getById(UUID companyId) {
|
||||
return companyRepository.findById(companyId)
|
||||
.orElseThrow(() -> notFound("Компания не найдена"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
server:
|
||||
port: ${SERVER_PORT:8080}
|
||||
spring:
|
||||
application:
|
||||
name: company-app
|
||||
jpa:
|
||||
database: POSTGRESQL
|
||||
open-in-view: false
|
||||
show-sql: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.PostgreSQLDialect
|
||||
datasource:
|
||||
url: ${DB_URL:jdbc:postgresql://postgres:5433/company}
|
||||
username: ${DB_USERNAME:postgres}
|
||||
password: ${DB_PASSWORD:postgres}
|
||||
driverClassName: org.postgresql.Driver
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
hikari:
|
||||
maximum-pool-size: 15
|
||||
minimum-idle: 4
|
||||
idle-timeout: 180000
|
||||
max-lifetime: 599000
|
||||
flyway:
|
||||
enabled: true
|
||||
locations: db/migration/
|
||||
@@ -0,0 +1,6 @@
|
||||
CREATE TABLE company (
|
||||
id UUID
|
||||
CONSTRAINT company_pk PRIMARY KEY,
|
||||
name VARCHAR(255) UNIQUE NOT NULL,
|
||||
address VARCHAR(255) NOT NULL
|
||||
);
|
||||
43
borschevskaya_anna_lab_3/docker-compose.yml
Normal file
43
borschevskaya_anna_lab_3/docker-compose.yml
Normal file
@@ -0,0 +1,43 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
container_name: postgres
|
||||
environment:
|
||||
POSTGRES_USERNAME: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
PGDATA: "/var/lib/postgresql/data/pgdata"
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- ./postgres_data:/var/lib/postgresql/data/
|
||||
- ./init-database.sh:/docker-entrypoint-initdb.d/init-database.sh
|
||||
company:
|
||||
build: ./company-service
|
||||
container_name: company
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
SERVER_PORT: 8080
|
||||
DB_URL: jdbc:postgresql://postgres:5432/company
|
||||
DB_USERNAME: postgres
|
||||
DB_PASSWORD: postgres
|
||||
vacancy:
|
||||
build: ./vacancy-service
|
||||
container_name: vacancy
|
||||
depends_on:
|
||||
- postgres
|
||||
environment:
|
||||
SERVER_PORT: 8080
|
||||
DB_URL: jdbc:postgresql://postgres:5432/vacancy
|
||||
DB_USERNAME: postgres
|
||||
DB_PASSWORD: postgres
|
||||
COMPANY_URL: http://nginx/
|
||||
nginx:
|
||||
image: nginx
|
||||
depends_on:
|
||||
- vacancy
|
||||
- company
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
|
||||
ports:
|
||||
- 80:80
|
||||
8
borschevskaya_anna_lab_3/init-database.sh
Normal file
8
borschevskaya_anna_lab_3/init-database.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Создаем БД
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
CREATE DATABASE company;
|
||||
CREATE DATABASE vacancy;
|
||||
EOSQL
|
||||
21
borschevskaya_anna_lab_3/nginx/nginx.conf
Normal file
21
borschevskaya_anna_lab_3/nginx/nginx.conf
Normal file
@@ -0,0 +1,21 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name localhost;
|
||||
|
||||
location /api/v1/company {
|
||||
proxy_pass http://company:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
location /api/v1/vacancy {
|
||||
proxy_pass http://vacancy:8080;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
}
|
||||
38
borschevskaya_anna_lab_3/vacancy-service/.gitignore
vendored
Normal file
38
borschevskaya_anna_lab_3/vacancy-service/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.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
|
||||
21
borschevskaya_anna_lab_3/vacancy-service/Dockerfile
Normal file
21
borschevskaya_anna_lab_3/vacancy-service/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
# Используем образ Maven для сборки
|
||||
FROM maven:3.8-eclipse-temurin-21-alpine AS build
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем остальные исходные файлы
|
||||
COPY pom.xml .
|
||||
COPY src src
|
||||
|
||||
# Собираем весь проект
|
||||
RUN mvn clean package -DskipTests
|
||||
|
||||
# Используем официальный образ JDK для запуска собранного jar-файла
|
||||
FROM eclipse-temurin:21-jdk-alpine
|
||||
|
||||
# Копируем jar-файл из предыдущего этапа
|
||||
COPY --from=build /app/target/*.jar /app.jar
|
||||
|
||||
# Указываем команду для запуска приложения
|
||||
CMD ["java", "-jar", "app.jar"]
|
||||
128
borschevskaya_anna_lab_3/vacancy-service/pom.xml
Normal file
128
borschevskaya_anna_lab_3/vacancy-service/pom.xml
Normal file
@@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.somecompany</groupId>
|
||||
<artifactId>vacancy-service</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
||||
<lombok-mapstruct-binding.version>0.2.0</lombok-mapstruct-binding.version>
|
||||
<spring-cloud.version>2023.0.3</spring-cloud.version>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||
<version>${lombok-mapstruct-binding.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>2.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.3.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring-cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok-mapstruct-binding</artifactId>
|
||||
<version>${lombok-mapstruct-binding.version}</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
<compilerArgs>
|
||||
<arg>-Amapstruct.defaultComponentModel=spring</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,14 @@
|
||||
package ru.somecompany;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableFeignClients
|
||||
public class VacancyApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(VacancyApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package ru.somecompany.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import ru.somecompany.domain.CreateVacancyRequest;
|
||||
import ru.somecompany.domain.VacancyEntity;
|
||||
import ru.somecompany.domain.VacancyResponse;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Validated
|
||||
@Tag(name = "vacancy", description = "API для управления вакансиями")
|
||||
public interface VacancyController {
|
||||
|
||||
@Operation(summary = "Получение всех вакансий")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Данные успешно переданы"),
|
||||
})
|
||||
@GetMapping(value = "/api/v1/vacancy")
|
||||
List<VacancyEntity> getVacancies();
|
||||
|
||||
@Operation(summary = "Создание вакансии")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Вакансия успешно создана"),
|
||||
})
|
||||
@PostMapping(value = "/api/v1/vacancy")
|
||||
VacancyEntity createVacancy(@RequestBody @NotNull CreateVacancyRequest request);
|
||||
|
||||
@Operation(summary = "Получение информации о вакансии по id")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Данные успешно переданы"),
|
||||
@ApiResponse(responseCode = "404", description = "Вакансия не найдена")
|
||||
})
|
||||
@GetMapping(value = "/api/v1/vacancy/{vacancyId}")
|
||||
VacancyResponse getVacancy(@PathVariable UUID vacancyId);
|
||||
|
||||
@Operation(summary = "Редактирование вакансии")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Данные вакансии успешно изменены"),
|
||||
@ApiResponse(responseCode = "404", description = "Вакансия не найдена")
|
||||
})
|
||||
@PutMapping(value = "/api/v1/vacancy/{vacancyId}")
|
||||
VacancyEntity updateVacancy(@PathVariable UUID vacancyId,
|
||||
@RequestBody @NotNull CreateVacancyRequest request);
|
||||
|
||||
@Operation(summary = "Удалении вакансии по id")
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "Вакансия успешно удалена"),
|
||||
@ApiResponse(responseCode = "404", description = "Вакансия не найдена")
|
||||
})
|
||||
@DeleteMapping(value = "/api/v1/vacancy/{vacancyId}")
|
||||
void deleteVacancy(@PathVariable UUID vacancyId);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package ru.somecompany.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.somecompany.domain.CreateVacancyRequest;
|
||||
import ru.somecompany.domain.VacancyEntity;
|
||||
import ru.somecompany.domain.VacancyResponse;
|
||||
import ru.somecompany.service.VacancyService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class VacancyControllerImpl implements VacancyController {
|
||||
|
||||
private final VacancyService vacancyService;
|
||||
|
||||
@Override
|
||||
public List<VacancyEntity> getVacancies() {
|
||||
return vacancyService.getVacancies();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VacancyEntity createVacancy(CreateVacancyRequest request) {
|
||||
return vacancyService.createVacancy(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VacancyResponse getVacancy(UUID vacancyId) {
|
||||
return vacancyService.getVacancy(vacancyId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VacancyEntity updateVacancy(UUID vacancyId, CreateVacancyRequest request) {
|
||||
return vacancyService.updateVacancy(vacancyId, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteVacancy(UUID vacancyId) {
|
||||
vacancyService.deleteVacancy(vacancyId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package ru.somecompany.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class CompanyResponse {
|
||||
|
||||
private UUID id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String address;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ru.somecompany.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class CreateVacancyRequest {
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
private Integer salaryLow;
|
||||
|
||||
private Integer salaryHigh;
|
||||
|
||||
private UUID companyId;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package ru.somecompany.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Builder
|
||||
@Table(name = "vacancy")
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class VacancyEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private UUID id;
|
||||
|
||||
@Column(name = "name", nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "description", nullable = true, length = 1000)
|
||||
private String description;
|
||||
|
||||
@Column(name = "salary_low", nullable = false)
|
||||
private Integer salaryLow;
|
||||
|
||||
@Column(name = "salary_high", nullable = false)
|
||||
private Integer salaryHigh;
|
||||
|
||||
@Column(name = "company_id", nullable = false)
|
||||
private UUID companyId;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package ru.somecompany.domain;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@Schema(description = "Данные по вакансии")
|
||||
public class VacancyResponse {
|
||||
|
||||
private UUID id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
private Integer salaryLow;
|
||||
|
||||
private Integer salaryHigh;
|
||||
|
||||
private CompanyResponse company;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.springframework.http.HttpStatus.NOT_FOUND;
|
||||
|
||||
@Slf4j
|
||||
public abstract class AbstractWebExceptionHandler {
|
||||
|
||||
public static ResponseEntity<ResponseError> buildResponse(ErrorCode errorCode, HttpStatus status, String msg,
|
||||
boolean details, Throwable ex) {
|
||||
return buildResponse(errorCode.name(), status, msg, details, ex, LocalDateTime.now());
|
||||
}
|
||||
|
||||
public static ResponseEntity<ResponseError> buildResponse(String errorCode, HttpStatus status, String msg,
|
||||
boolean details, Throwable ex, LocalDateTime timestamp) {
|
||||
if (details) {
|
||||
log.error(msg, ex); // with stack-trace
|
||||
} else {
|
||||
var message = StringUtils.equals(msg, ex.getMessage()) ? msg : msg + ": " + ex.getMessage();
|
||||
if (status == NOT_FOUND) {
|
||||
log.warn(message);
|
||||
} else {
|
||||
log.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
return ResponseEntity.status(status.value())
|
||||
.body(ResponseError.builder()
|
||||
.code(errorCode)
|
||||
.message(msg)
|
||||
.timestamp(timestamp)
|
||||
.status(status.value())
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
public enum BasicError implements ErrorCode {
|
||||
NOT_FOUND
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
@Getter
|
||||
public class CompanyAppRuntimeException extends RuntimeException {
|
||||
private final ErrorCode errorCode;
|
||||
private final HttpStatus status;
|
||||
private final boolean details;
|
||||
|
||||
public CompanyAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message) {
|
||||
this(errorCode, status, message, null);
|
||||
}
|
||||
|
||||
public CompanyAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message, Throwable error) {
|
||||
this(errorCode, status, message, true, error);
|
||||
}
|
||||
|
||||
public CompanyAppRuntimeException(ErrorCode errorCode, HttpStatus status, String message, boolean details, Throwable error) {
|
||||
super(message, error);
|
||||
this.errorCode = errorCode;
|
||||
this.status = status;
|
||||
this.details = details;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public interface ErrorCode extends Serializable {
|
||||
|
||||
String name();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import static ru.somecompany.exception.BasicError.NOT_FOUND;
|
||||
|
||||
public class ResourceNotFoundException extends CompanyAppRuntimeException {
|
||||
|
||||
public ResourceNotFoundException(ErrorCode errorCode, String message) {
|
||||
this(errorCode, message, null);
|
||||
}
|
||||
|
||||
public ResourceNotFoundException(ErrorCode errorCode, String message, Throwable error) {
|
||||
super(errorCode, HttpStatus.NOT_FOUND, message, error);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound(String message, Object... args) {
|
||||
return new ResourceNotFoundException(NOT_FOUND, String.format(message, args), null);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound() {
|
||||
return new ResourceNotFoundException(NOT_FOUND, "Запрашиваемые данные не найдены", null);
|
||||
}
|
||||
|
||||
public static ResourceNotFoundException notFound(Throwable error) {
|
||||
return new ResourceNotFoundException(NOT_FOUND, error.getMessage(), error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ResponseError {
|
||||
@JsonProperty(required = true)
|
||||
private LocalDateTime timestamp;
|
||||
@JsonProperty(required = true)
|
||||
private Integer status;
|
||||
@JsonProperty(required = true)
|
||||
private String message;
|
||||
@JsonProperty(required = true)
|
||||
private String code;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ru.somecompany.exception;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@RestControllerAdvice
|
||||
@RequiredArgsConstructor
|
||||
public class WebExceptionHandler extends AbstractWebExceptionHandler {
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@ExceptionHandler(CompanyAppRuntimeException.class)
|
||||
public ResponseEntity<ResponseError> handleException(CompanyAppRuntimeException ex) {
|
||||
return buildResponse(ex.getErrorCode(), ex.getStatus(), ex.getMessage(), ex.isDetails(), ex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ru.somecompany.external;
|
||||
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import ru.somecompany.domain.CompanyResponse;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@FeignClient(name = "company", url = "${app.feign.company-url}")
|
||||
public interface CompanyClient {
|
||||
|
||||
@GetMapping(value = "/api/v1/company/{companyId}")
|
||||
CompanyResponse getCompany(@PathVariable UUID companyId);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package ru.somecompany.mapper;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import ru.somecompany.domain.CompanyResponse;
|
||||
import ru.somecompany.domain.CreateVacancyRequest;
|
||||
import ru.somecompany.domain.VacancyEntity;
|
||||
import ru.somecompany.domain.VacancyResponse;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Mapper(imports = UUID.class)
|
||||
public interface VacancyMapper {
|
||||
|
||||
@Mapping(target = "company", source = "company")
|
||||
@Mapping(target = "name", source = "entity.name")
|
||||
@Mapping(target = "id", source = "entity.id")
|
||||
VacancyResponse map(VacancyEntity entity, CompanyResponse company);
|
||||
|
||||
VacancyEntity map(CreateVacancyRequest request);
|
||||
|
||||
VacancyEntity map(@MappingTarget VacancyEntity entity, CreateVacancyRequest request);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package ru.somecompany.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.somecompany.domain.VacancyEntity;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface VacancyRepository extends JpaRepository<VacancyEntity, UUID> {
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ru.somecompany.service;
|
||||
|
||||
import ru.somecompany.domain.CreateVacancyRequest;
|
||||
import ru.somecompany.domain.VacancyEntity;
|
||||
import ru.somecompany.domain.VacancyResponse;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface VacancyService {
|
||||
|
||||
List<VacancyEntity> getVacancies();
|
||||
|
||||
VacancyEntity createVacancy(CreateVacancyRequest request);
|
||||
|
||||
VacancyEntity updateVacancy(UUID vacancyId, CreateVacancyRequest request);
|
||||
|
||||
void deleteVacancy(UUID vacancyId);
|
||||
|
||||
VacancyResponse getVacancy(UUID vacancyId);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package ru.somecompany.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.somecompany.domain.CreateVacancyRequest;
|
||||
import ru.somecompany.domain.VacancyEntity;
|
||||
import ru.somecompany.domain.VacancyResponse;
|
||||
import ru.somecompany.mapper.VacancyMapper;
|
||||
import ru.somecompany.repository.VacancyRepository;
|
||||
import ru.somecompany.service.adapter.CompanyAdapter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static ru.somecompany.exception.ResourceNotFoundException.notFound;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class VacancyServiceImpl implements VacancyService {
|
||||
|
||||
private final VacancyRepository vacancyRepository;
|
||||
private final CompanyAdapter companyAdapter;
|
||||
private final VacancyMapper mapper;
|
||||
|
||||
@Override
|
||||
public List<VacancyEntity> getVacancies() {
|
||||
return vacancyRepository.findAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VacancyEntity createVacancy(CreateVacancyRequest request) {
|
||||
var entity = mapper.map(request);
|
||||
return vacancyRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VacancyEntity updateVacancy(UUID vacancyId, CreateVacancyRequest request) {
|
||||
var entity = getById(vacancyId);
|
||||
entity = mapper.map(entity, request);
|
||||
|
||||
return vacancyRepository.save(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteVacancy(UUID vacancyId) {
|
||||
var entity = getById(vacancyId);
|
||||
vacancyRepository.delete(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VacancyResponse getVacancy(UUID vacancyId) {
|
||||
var entity = getById(vacancyId);
|
||||
var company = companyAdapter.getCompanyById(entity.getCompanyId());
|
||||
|
||||
return mapper.map(entity, company);
|
||||
}
|
||||
|
||||
private VacancyEntity getById(UUID vacancyId) {
|
||||
var entity = vacancyRepository.findById(vacancyId);
|
||||
if (entity.isEmpty()) {
|
||||
log.warn("The vacancy with id '{}' was not found", vacancyId);
|
||||
throw notFound("Вакансия не найдена");
|
||||
}
|
||||
return entity.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package ru.somecompany.service.adapter;
|
||||
|
||||
import ru.somecompany.domain.CompanyResponse;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface CompanyAdapter {
|
||||
|
||||
CompanyResponse getCompanyById(UUID companyId);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.somecompany.service.adapter;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.somecompany.domain.CompanyResponse;
|
||||
import ru.somecompany.external.CompanyClient;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CompanyAdapterImpl implements CompanyAdapter {
|
||||
|
||||
private final CompanyClient companyClient;
|
||||
|
||||
@Override
|
||||
public CompanyResponse getCompanyById(UUID companyId) {
|
||||
return companyClient.getCompany(companyId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
server:
|
||||
port: ${SERVER_PORT:8080}
|
||||
spring:
|
||||
application:
|
||||
name: vacancy-app
|
||||
jpa:
|
||||
database: POSTGRESQL
|
||||
open-in-view: false
|
||||
show-sql: false
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
properties:
|
||||
hibernate:
|
||||
dialect: org.hibernate.dialect.PostgreSQLDialect
|
||||
datasource:
|
||||
url: ${DB_URL:jdbc:postgresql://localhost:5433/vacancy}
|
||||
username: ${DB_USERNAME:postgres}
|
||||
password: ${DB_PASSWORD:postgres}
|
||||
driverClassName: org.postgresql.Driver
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
hikari:
|
||||
maximum-pool-size: 15
|
||||
minimum-idle: 4
|
||||
idle-timeout: 180000
|
||||
max-lifetime: 599000
|
||||
flyway:
|
||||
enabled: true
|
||||
locations: db/migration/
|
||||
|
||||
app:
|
||||
feign:
|
||||
company-url: ${COMPANY_URL:http://localhost:8081}
|
||||
@@ -0,0 +1,9 @@
|
||||
CREATE TABLE vacancy (
|
||||
id UUID
|
||||
CONSTRAINT company_pk PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description VARCHAR(1000),
|
||||
salary_low int8 NOT NULL,
|
||||
salary_high int8 NOT NULL,
|
||||
company_id UUID NOT NULL
|
||||
);
|
||||
50
borschevskaya_anna_lab_4/README.md
Normal file
50
borschevskaya_anna_lab_4/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Отчет. Лабораторная работа 4
|
||||
|
||||
## Описание
|
||||
В ходе лабораторной работы были изучены главы туториала о работе с RabbitMQ. Результат выполнения заданий каждой главы
|
||||
отражен на скриншотах в папке /images:
|
||||
- Tutorial-Task1.png
|
||||

|
||||
- Tutorial-Task2.png
|
||||

|
||||
- Tutorial-Task3.png
|
||||

|
||||
|
||||
Задание из 3-ей главы туториала было расширено условиями, которые были поставлены в задании к данной лабораторной работе.
|
||||
Для демонстрации работы сервисов посредством ассинхронного общения через брокер сообщений RabbitMQ была выбрана
|
||||
предметная область "Обработка заказов".
|
||||
|
||||
Сервис-издатель "Publisher" публикует в очередь сообщений событие поступления заказа с некоторым номером.
|
||||
|
||||
Сервисы-подписчики обрабатывают сообщения о заказах, при этом подписчики обрабатывают сообщение по-разному. Один вид
|
||||
подписчика обрабатывает с задержкой в несколько секунд, другой - "мгновенно", они получают одни и те жа сообщения,
|
||||
но соединены с разными очередями.
|
||||
В качестве эксперимента изначально были запущены по одному экземпляру каждого вида.
|
||||
На изображении Consumer2.png представлена работа мгновенно обрабатывающего подписчика. Он справляется с нагрузкой,
|
||||
так как размер очереди не растет.
|
||||

|
||||
На изображении Consumer1.png представлена работа подписчика, обрабатывающего сообщения с задержкой. Как мы видим,
|
||||
в очереди накапливаются сообщения в состоянии 'Ready' - эти сообщения готовы для того, чтобы быть доставленными подписчикам.
|
||||
Сервис не справляется с нагрузкой, так как отправляются сообщения быстрее, чем обрабатываются.
|
||||

|
||||
Для того, чтобы обеспечить равную скорость отправки и обработки, увеличиваем количество экземпляров-подписчиков данного типа до трех.
|
||||
На изображении видно, что теперь длина очереди не растет и система справляется с поступающими сообщениями. Также скорость "publish" и
|
||||
"consumer ack" стали равны.
|
||||

|
||||
## Как запустить
|
||||
Для того, чтобы запустить сервисы, необходимо выполнить следующие действия:
|
||||
1. Установить и запустить Docker Engine или Docker Desktop
|
||||
2. Через консоль перейти в папку, в которой расположен файл docker-compose.yml
|
||||
3. Выполнить команду для запуска брокера сообщений rabbitmq:
|
||||
```
|
||||
docker compose up rabbit -d
|
||||
```
|
||||
4. Выполнить команду для запуска остальных контейнеров:
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
Такой порядок запуска важен для того, чтобы брокер сообщений успел полностью запуститься
|
||||
и произвести действия для того, чтобы быть готовым принимать соединения от сервисов. Потому что указания depends_on не хватает
|
||||
для отслеживания завершения всех необходимых подготовительных процессов брокера.
|
||||
## Видео-отчет
|
||||
Работоспособность лабораторной работы можно оценить в следующем [видео](https://disk.yandex.ru/i/G0vsfp7vwazYHw).
|
||||
23
borschevskaya_anna_lab_4/consumer-app/Dockerfile
Normal file
23
borschevskaya_anna_lab_4/consumer-app/Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
||||
# Используем образ Maven для сборки
|
||||
FROM maven:3.8-eclipse-temurin-21-alpine AS build
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем остальные исходные файлы
|
||||
COPY pom.xml .
|
||||
COPY src src
|
||||
|
||||
# Собираем весь проект
|
||||
RUN mvn clean package -DskipTests
|
||||
RUN mvn dependency:copy-dependencies
|
||||
|
||||
# Используем официальный образ JDK для запуска собранного jar-файла
|
||||
FROM eclipse-temurin:21-jdk-alpine
|
||||
|
||||
# Копируем jar-файл из предыдущего этапа
|
||||
COPY --from=build /app/target/*.jar /app.jar
|
||||
COPY --from=build /app/target/dependency /
|
||||
|
||||
# Указываем команду для запуска приложения
|
||||
CMD ["java", "-jar", "app.jar"]
|
||||
50
borschevskaya_anna_lab_4/consumer-app/pom.xml
Normal file
50
borschevskaya_anna_lab_4/consumer-app/pom.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.somecompany</groupId>
|
||||
<artifactId>consumer-app</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.rabbitmq</groupId>
|
||||
<artifactId>amqp-client</artifactId>
|
||||
<version>5.22.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,15 @@
|
||||
package ru.somecompany;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
||||
import ru.somecompany.config.property.RabbitProperties;
|
||||
|
||||
@SpringBootApplication
|
||||
@ConfigurationPropertiesScan(basePackageClasses = RabbitProperties.class)
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Main.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package ru.somecompany.config;
|
||||
|
||||
import com.rabbitmq.client.BuiltinExchangeType;
|
||||
import com.rabbitmq.client.Channel;
|
||||
import com.rabbitmq.client.Connection;
|
||||
import com.rabbitmq.client.ConnectionFactory;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import ru.somecompany.config.property.RabbitProperties;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class ConnectionFactoryConfig {
|
||||
|
||||
private final RabbitProperties rabbitProperties;
|
||||
|
||||
@Bean
|
||||
public ConnectionFactory connectionFactory() {
|
||||
ConnectionFactory factory = new ConnectionFactory();
|
||||
factory.setHost(rabbitProperties.getHost());
|
||||
factory.setPort(rabbitProperties.getPort());
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Connection connection(ConnectionFactory connectionFactory) throws IOException, TimeoutException {
|
||||
return connectionFactory.newConnection();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Channel channel(Connection connection) throws IOException {
|
||||
var exchange = rabbitProperties.getExchange();
|
||||
var queue = rabbitProperties.getQueue();
|
||||
var channel = connection.createChannel();
|
||||
|
||||
channel.exchangeDeclare(exchange, BuiltinExchangeType.FANOUT);
|
||||
channel.queueDeclare(queue, true, false, true, null);
|
||||
channel.queueBind(queue, exchange, "");
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package ru.somecompany.config.property;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "app.rabbit-properties")
|
||||
public class RabbitProperties {
|
||||
|
||||
private String host;
|
||||
|
||||
private Integer port;
|
||||
|
||||
private Integer delay;
|
||||
|
||||
private String queue;
|
||||
|
||||
private String exchange;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package ru.somecompany.consumer;
|
||||
|
||||
import com.rabbitmq.client.AMQP;
|
||||
import com.rabbitmq.client.Channel;
|
||||
import com.rabbitmq.client.Connection;
|
||||
import com.rabbitmq.client.DefaultConsumer;
|
||||
import com.rabbitmq.client.Envelope;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.somecompany.config.property.RabbitProperties;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class Consumer {
|
||||
|
||||
private final RabbitProperties rabbitProperties;
|
||||
|
||||
private final Connection connection;
|
||||
private final Channel channel;
|
||||
|
||||
@PostConstruct
|
||||
public void consume() {
|
||||
try {
|
||||
channel.basicQos(1);
|
||||
channel.basicConsume(rabbitProperties.getQueue(), false, new DefaultConsumer(channel) {
|
||||
@Override
|
||||
public void handleDelivery(String consumerTag,
|
||||
Envelope envelope,
|
||||
AMQP.BasicProperties properties,
|
||||
byte[] body) throws IOException {
|
||||
long deliveryTag = envelope.getDeliveryTag();
|
||||
|
||||
String message = new String(body, StandardCharsets.UTF_8);
|
||||
System.out.println(" [x] Received '" + message + "'");
|
||||
|
||||
var delay = rabbitProperties.getDelay();
|
||||
try {
|
||||
doWork(delay);
|
||||
} finally {
|
||||
System.out.println(" [x] Processed '" + message + "'");
|
||||
channel.basicAck(deliveryTag, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception exception) {
|
||||
log.error("Error while set up connection with rabbit", exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static void doWork(Integer delay) {
|
||||
if (delay > 0) {
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException _ignored) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void cleanUp() throws Exception {
|
||||
if (channel != null) {
|
||||
channel.close();
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
server:
|
||||
port: ${SERVER_PORT:8081}
|
||||
|
||||
app:
|
||||
rabbit-properties:
|
||||
host: ${RABBIT_HOST:localhost}
|
||||
port: ${RABBIT_PORT:5672}
|
||||
delay: ${PROCESS_DELAY:0}
|
||||
queue: ${QUEUE_NAME:queue-1}
|
||||
exchange: ${EXCHANGE_NAME:order-events}
|
||||
79
borschevskaya_anna_lab_4/docker-compose.yml
Normal file
79
borschevskaya_anna_lab_4/docker-compose.yml
Normal file
@@ -0,0 +1,79 @@
|
||||
services:
|
||||
rabbit:
|
||||
container_name: rabbit
|
||||
image: rabbitmq:3-management
|
||||
ports:
|
||||
- "15672:15672"
|
||||
- "5672:5672"
|
||||
- "5671:5671"
|
||||
networks:
|
||||
- local
|
||||
publisher:
|
||||
build: ./publisher-app
|
||||
container_name: publisher
|
||||
depends_on:
|
||||
- rabbit
|
||||
environment:
|
||||
RABBIT_HOST: rabbit
|
||||
RABBIT_PORT: 5672
|
||||
networks:
|
||||
- local
|
||||
consumer-1:
|
||||
build: ./consumer-app
|
||||
container_name: consumer-1
|
||||
depends_on:
|
||||
- rabbit
|
||||
- publisher
|
||||
environment:
|
||||
RABBIT_HOST: rabbit
|
||||
RABBIT_PORT: 5672
|
||||
PROCESS_DELAY: 3000
|
||||
QUEUE_NAME: queue1
|
||||
EXCHANGE_NAME: order-events
|
||||
networks:
|
||||
- local
|
||||
consumer-2:
|
||||
build: ./consumer-app
|
||||
container_name: consumer-2
|
||||
depends_on:
|
||||
- rabbit
|
||||
- publisher
|
||||
environment:
|
||||
RABBIT_HOST: rabbit
|
||||
RABBIT_PORT: 5672
|
||||
PROCESS_DELAY: 0
|
||||
QUEUE_NAME: queue2
|
||||
EXCHANGE_NAME: order-events
|
||||
networks:
|
||||
- local
|
||||
consumer-12:
|
||||
build: ./consumer-app
|
||||
container_name: consumer-12
|
||||
depends_on:
|
||||
- rabbit
|
||||
- publisher
|
||||
environment:
|
||||
RABBIT_HOST: rabbit
|
||||
RABBIT_PORT: 5672
|
||||
PROCESS_DELAY: 3000
|
||||
QUEUE_NAME: queue1
|
||||
EXCHANGE_NAME: order-events
|
||||
networks:
|
||||
- local
|
||||
consumer-13:
|
||||
build: ./consumer-app
|
||||
container_name: consumer-13
|
||||
depends_on:
|
||||
- rabbit
|
||||
- publisher
|
||||
environment:
|
||||
RABBIT_HOST: rabbit
|
||||
RABBIT_PORT: 5672
|
||||
PROCESS_DELAY: 3000
|
||||
QUEUE_NAME: queue1
|
||||
EXCHANGE_NAME: order-events
|
||||
networks:
|
||||
- local
|
||||
|
||||
networks:
|
||||
local:
|
||||
34
borschevskaya_anna_lab_4/helloworld-tutorial/pom.xml
Normal file
34
borschevskaya_anna_lab_4/helloworld-tutorial/pom.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.somecompany</groupId>
|
||||
<artifactId>helloworld-tutorial</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.rabbitmq</groupId>
|
||||
<artifactId>amqp-client</artifactId>
|
||||
<version>5.22.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.7.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,28 @@
|
||||
package ru.somecompany;
|
||||
|
||||
import com.rabbitmq.client.Channel;
|
||||
import com.rabbitmq.client.Connection;
|
||||
import com.rabbitmq.client.ConnectionFactory;
|
||||
import com.rabbitmq.client.DeliverCallback;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class Main {
|
||||
|
||||
private static final String QUEUE_NAME = "hello-world";
|
||||
|
||||
public static void main(String[] args) {
|
||||
ConnectionFactory factory = new ConnectionFactory();
|
||||
factory.setHost("localhost");
|
||||
try(Connection connection = factory.newConnection();
|
||||
Channel channel = connection.createChannel();) {
|
||||
var sender = new Sender(channel);
|
||||
var receiver = new Receiver(channel);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println(" [*] Error in Hello-World");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.somecompany;
|
||||
|
||||
import com.rabbitmq.client.Channel;
|
||||
import com.rabbitmq.client.DeliverCallback;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Receiver {
|
||||
|
||||
private static final String QUEUE_NAME = "hello-world";
|
||||
|
||||
public Receiver(Channel channel) throws IOException {
|
||||
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
|
||||
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
|
||||
String message = new String(delivery.getBody(), "UTF-8");
|
||||
System.out.println(" [x] Received '" + message + "'");
|
||||
};
|
||||
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package ru.somecompany;
|
||||
|
||||
import com.rabbitmq.client.Channel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class Sender {
|
||||
|
||||
private static final String QUEUE_NAME = "hello-world";
|
||||
|
||||
public Sender(Channel channel) throws IOException {
|
||||
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
|
||||
String message = "Hello World!";
|
||||
channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
|
||||
System.out.println(" [x] Sent '" + message + "'");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Root logger option
|
||||
log4j.rootLogger=INFO, stdout
|
||||
|
||||
# Direct log messages to stdout
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.Target=System.out
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
BIN
borschevskaya_anna_lab_4/images/Consumer1-scaling.PNG
Normal file
BIN
borschevskaya_anna_lab_4/images/Consumer1-scaling.PNG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
borschevskaya_anna_lab_4/images/Consumer1.PNG
Normal file
BIN
borschevskaya_anna_lab_4/images/Consumer1.PNG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
BIN
borschevskaya_anna_lab_4/images/Consumer2.PNG
Normal file
BIN
borschevskaya_anna_lab_4/images/Consumer2.PNG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
BIN
borschevskaya_anna_lab_4/images/Tutorial-Task1.PNG
Normal file
BIN
borschevskaya_anna_lab_4/images/Tutorial-Task1.PNG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 101 KiB |
BIN
borschevskaya_anna_lab_4/images/Tutorial-Task2.PNG
Normal file
BIN
borschevskaya_anna_lab_4/images/Tutorial-Task2.PNG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
BIN
borschevskaya_anna_lab_4/images/Tutorial-Task3.PNG
Normal file
BIN
borschevskaya_anna_lab_4/images/Tutorial-Task3.PNG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 848 KiB |
38
borschevskaya_anna_lab_4/publisher-app/.gitignore
vendored
Normal file
38
borschevskaya_anna_lab_4/publisher-app/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.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
|
||||
21
borschevskaya_anna_lab_4/publisher-app/Dockerfile
Normal file
21
borschevskaya_anna_lab_4/publisher-app/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
# Используем образ Maven для сборки
|
||||
FROM maven:3.8-eclipse-temurin-21-alpine AS build
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем остальные исходные файлы
|
||||
COPY pom.xml .
|
||||
COPY src src
|
||||
|
||||
# Собираем весь проект
|
||||
RUN mvn clean package -DskipTests
|
||||
|
||||
# Используем официальный образ JDK для запуска собранного jar-файла
|
||||
FROM eclipse-temurin:21-jdk-alpine
|
||||
|
||||
# Копируем jar-файл из предыдущего этапа
|
||||
COPY --from=build /app/target/*.jar /app.jar
|
||||
|
||||
# Указываем команду для запуска приложения
|
||||
CMD ["java", "-jar", "app.jar"]
|
||||
49
borschevskaya_anna_lab_4/publisher-app/pom.xml
Normal file
49
borschevskaya_anna_lab_4/publisher-app/pom.xml
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.somecompany</groupId>
|
||||
<artifactId>publisher-app</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.3</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.rabbitmq</groupId>
|
||||
<artifactId>amqp-client</artifactId>
|
||||
<version>5.22.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,17 @@
|
||||
package ru.somecompany;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import ru.somecompany.config.property.RabbitProperties;
|
||||
|
||||
@EnableScheduling
|
||||
@SpringBootApplication
|
||||
@ConfigurationPropertiesScan(basePackageClasses = RabbitProperties.class)
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Main.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package ru.somecompany.config;
|
||||
|
||||
import com.rabbitmq.client.BuiltinExchangeType;
|
||||
import com.rabbitmq.client.Channel;
|
||||
import com.rabbitmq.client.Connection;
|
||||
import com.rabbitmq.client.ConnectionFactory;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import ru.somecompany.config.property.RabbitProperties;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class ConnectionFactoryConfig {
|
||||
|
||||
private final RabbitProperties rabbitProperties;
|
||||
|
||||
@Bean
|
||||
public ConnectionFactory connectionFactory() {
|
||||
ConnectionFactory factory = new ConnectionFactory();
|
||||
factory.setHost(rabbitProperties.getHost());
|
||||
factory.setPort(rabbitProperties.getPort());
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Connection connection(ConnectionFactory connectionFactory) throws IOException, TimeoutException {
|
||||
return connectionFactory.newConnection();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Channel channel(Connection connection) throws IOException {
|
||||
var channel = connection.createChannel();
|
||||
channel.exchangeDeclare(rabbitProperties.getExchange(), BuiltinExchangeType.FANOUT);
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ru.somecompany.config.property;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "app.rabbit-properties")
|
||||
public class RabbitProperties {
|
||||
|
||||
private String host;
|
||||
|
||||
private Integer port;
|
||||
|
||||
private String exchange;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package ru.somecompany.scheduler;
|
||||
|
||||
import com.rabbitmq.client.BuiltinExchangeType;
|
||||
import com.rabbitmq.client.Channel;
|
||||
import com.rabbitmq.client.Connection;
|
||||
import com.rabbitmq.client.ConnectionFactory;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SenderScheduler {
|
||||
|
||||
private static final String EXCHANGE_NAME = "order-events";
|
||||
private static final String MESSAGE = "Поступил заказ №%d";
|
||||
private Integer index = 0;
|
||||
|
||||
private final ConnectionFactory connectionFactory;
|
||||
private final Connection connection;
|
||||
private final Channel channel;
|
||||
|
||||
@Scheduled(cron = "*/1 * * * * *")
|
||||
public void sendMessage() {
|
||||
try {
|
||||
var message = String.format(MESSAGE, index);
|
||||
|
||||
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes(StandardCharsets.UTF_8));
|
||||
index++;
|
||||
System.out.println(" [x] Sent '" + message + "'");
|
||||
} catch (IOException e) {
|
||||
System.out.println(" [x] Error while send message");
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
server:
|
||||
port: ${SERVER_PORT:8080}
|
||||
|
||||
app:
|
||||
rabbit-properties:
|
||||
host: ${RABBIT_HOST:localhost}
|
||||
port: ${RABBIT_PORT:5672}
|
||||
exchange: ${EXCHANGE_NAME:order-events}
|
||||
30
borschevskaya_anna_lab_4/workqueue-tutorial/pom.xml
Normal file
30
borschevskaya_anna_lab_4/workqueue-tutorial/pom.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>ru.somecompany</groupId>
|
||||
<artifactId>workqueue-tutorial</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.rabbitmq</groupId>
|
||||
<artifactId>amqp-client</artifactId>
|
||||
<version>5.22.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.7.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,23 @@
|
||||
package ru.somecompany;
|
||||
|
||||
import com.rabbitmq.client.Channel;
|
||||
import com.rabbitmq.client.Connection;
|
||||
import com.rabbitmq.client.ConnectionFactory;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static final String QUEUE_NAME = "task_queue";
|
||||
|
||||
public static void main(String[] args) {
|
||||
ConnectionFactory factory = new ConnectionFactory();
|
||||
factory.setHost("localhost");
|
||||
try(Connection connection = factory.newConnection();
|
||||
Channel channel = connection.createChannel();) {
|
||||
var sender = new Sender(channel);
|
||||
sender.send("Work Queue message");
|
||||
var receiver = new Receiver(channel);
|
||||
} catch (Exception e) {
|
||||
System.out.println(" [*] Error in Work-Queue: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package ru.somecompany;
|
||||
|
||||
import com.rabbitmq.client.Channel;
|
||||
import com.rabbitmq.client.DeliverCallback;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static ru.somecompany.Main.QUEUE_NAME;
|
||||
|
||||
public class Receiver {
|
||||
|
||||
public Receiver(Channel channel) throws IOException {
|
||||
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
|
||||
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
|
||||
|
||||
channel.basicQos(1);
|
||||
|
||||
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
|
||||
String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
|
||||
|
||||
System.out.println(" [x] Received '" + message + "'");
|
||||
try {
|
||||
doWork(message);
|
||||
} finally {
|
||||
System.out.println(" [x] Done");
|
||||
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
|
||||
}
|
||||
};
|
||||
channel.basicConsume(QUEUE_NAME, false, deliverCallback, consumerTag -> { });
|
||||
}
|
||||
|
||||
private static void doWork(String task) {
|
||||
for (char ch : task.toCharArray()) {
|
||||
if (ch == '.') {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException _ignored) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package ru.somecompany;
|
||||
|
||||
import com.rabbitmq.client.Channel;
|
||||
import com.rabbitmq.client.MessageProperties;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static ru.somecompany.Main.QUEUE_NAME;
|
||||
|
||||
public class Sender {
|
||||
|
||||
private Channel channel;
|
||||
|
||||
public Sender(Channel channel) throws IOException {
|
||||
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public void send(String msg) throws IOException {
|
||||
String message = String.join(" ", msg);
|
||||
|
||||
channel.basicPublish("", QUEUE_NAME,
|
||||
MessageProperties.PERSISTENT_TEXT_PLAIN,
|
||||
message.getBytes("UTF-8"));
|
||||
System.out.println(" [x] Sent '" + message + "'");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Root logger option
|
||||
log4j.rootLogger=INFO, stdout
|
||||
|
||||
# Direct log messages to stdout
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.Target=System.out
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
|
||||
30
kuzarin_maxim_lab_2/.dockerignore
Normal file
30
kuzarin_maxim_lab_2/.dockerignore
Normal file
@@ -0,0 +1,30 @@
|
||||
**/.classpath
|
||||
**/.dockerignore
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.toolstarget
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/*.*proj.user
|
||||
**/*.dbmdl
|
||||
**/*.jfm
|
||||
**/azds.yaml
|
||||
**/bin
|
||||
**/charts
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
||||
!**/.gitignore
|
||||
!.git/HEAD
|
||||
!.git/config
|
||||
!.git/packed-refs
|
||||
!.git/refs/heads/**
|
||||
490
kuzarin_maxim_lab_2/.gitignore
vendored
Normal file
490
kuzarin_maxim_lab_2/.gitignore
vendored
Normal file
@@ -0,0 +1,490 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from `dotnet new gitignore`
|
||||
|
||||
# dotenv files
|
||||
.env
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# Tye
|
||||
.tye/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.tlog
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||
*.vbp
|
||||
|
||||
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||
*.dsw
|
||||
*.dsp
|
||||
|
||||
# Visual Studio 6 technical files
|
||||
*.ncb
|
||||
*.aps
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# Visual Studio History (VSHistory) files
|
||||
.vshistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
# VS Code files for those working on multiple tools
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Windows Installer files from build outputs
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
.idea
|
||||
|
||||
##
|
||||
## Visual studio for Mac
|
||||
##
|
||||
|
||||
|
||||
# globs
|
||||
Makefile.in
|
||||
*.userprefs
|
||||
*.usertasks
|
||||
config.make
|
||||
config.status
|
||||
aclocal.m4
|
||||
install-sh
|
||||
autom4te.cache/
|
||||
*.tar.gz
|
||||
tarballs/
|
||||
test-results/
|
||||
|
||||
# Mac bundle stuff
|
||||
*.dmg
|
||||
*.app
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# Vim temporary swap files
|
||||
*.swp
|
||||
|
||||
# Delete volumes
|
||||
/volumes
|
||||
|
||||
# Delete .vs folder
|
||||
/.vs/*
|
||||
37
kuzarin_maxim_lab_2/DAaS_FileConnections.sln
Normal file
37
kuzarin_maxim_lab_2/DAaS_FileConnections.sln
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.10.34916.146
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DAaS_FileConnections", "DAaS_FileConnections\DAaS_FileConnections.csproj", "{23B07B06-0AB4-42F8-9753-264639E1D38B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FirstApplication", "FirstApplication\FirstApplication.csproj", "{FBC01B2E-C887-42D1-A899-3C4781EF67E9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SecondApplication", "SecondApplication\SecondApplication.csproj", "{5888DB56-82F2-467D-8F6B-AB94CC4A33B5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{23B07B06-0AB4-42F8-9753-264639E1D38B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{23B07B06-0AB4-42F8-9753-264639E1D38B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{23B07B06-0AB4-42F8-9753-264639E1D38B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{23B07B06-0AB4-42F8-9753-264639E1D38B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FBC01B2E-C887-42D1-A899-3C4781EF67E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FBC01B2E-C887-42D1-A899-3C4781EF67E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FBC01B2E-C887-42D1-A899-3C4781EF67E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FBC01B2E-C887-42D1-A899-3C4781EF67E9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5888DB56-82F2-467D-8F6B-AB94CC4A33B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5888DB56-82F2-467D-8F6B-AB94CC4A33B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5888DB56-82F2-467D-8F6B-AB94CC4A33B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5888DB56-82F2-467D-8F6B-AB94CC4A33B5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {E290B7D1-0F4E-406E-ADF3-8F543C0FA96E}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="PIHelperSh.RandomEx" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
29
kuzarin_maxim_lab_2/DAaS_FileConnections/Program.cs
Normal file
29
kuzarin_maxim_lab_2/DAaS_FileConnections/Program.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
using PIHelperSh.RandomEx.Extentions;
|
||||
|
||||
string inputPath = Environment.GetEnvironmentVariable("INPUT_PATH") ?? ".\\input1";
|
||||
string outputPath = Environment.GetEnvironmentVariable("OUTPUT_PATH") ?? ".\\output1";
|
||||
|
||||
if(!Directory.Exists(inputPath))
|
||||
Directory.CreateDirectory(inputPath);
|
||||
|
||||
if(!Directory.Exists(outputPath))
|
||||
Directory.CreateDirectory(outputPath);
|
||||
|
||||
var rnd = new Random();
|
||||
|
||||
for (int i = 0; i < rnd.Next(1000); i++)
|
||||
{
|
||||
using var writer = new StreamWriter(Path.Combine(inputPath, $"{rnd.NextString(10)}.txt"), false);
|
||||
|
||||
for (int j = 0; j < rnd.Next(1000); j++)
|
||||
|
||||
{
|
||||
writer.WriteLine(rnd.Next());
|
||||
}
|
||||
writer.Close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
35
kuzarin_maxim_lab_2/FirstApplication/Dockerfile
Normal file
35
kuzarin_maxim_lab_2/FirstApplication/Dockerfile
Normal file
@@ -0,0 +1,35 @@
|
||||
# Это базовый образ для сборки.
|
||||
FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base
|
||||
# Указываем рабочий каталог для следующих комманд
|
||||
WORKDIR /app
|
||||
|
||||
# Проект нужно сначала собрать, поэтому берём sdk
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
# Это пременная. Будем собирать в режиме Relece.
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
WORKDIR /src
|
||||
# Копируем из папки хоста в контейнер
|
||||
COPY ["FirstApplication.csproj", "FirstApplication/"]
|
||||
# Выполняем комманду для загрузки всех нужных зависимостей
|
||||
RUN dotnet restore "FirstApplication/FirstApplication.csproj"
|
||||
# Назначаем новую базовую папку
|
||||
WORKDIR "/src/FirstApplication"
|
||||
# Копируем всё в папку для сборки
|
||||
COPY . .
|
||||
# Выполняем сборку проекта
|
||||
RUN dotnet build "FirstApplication.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||
|
||||
# В этот этап попадает всё, что было в publish
|
||||
FROM build AS publish
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
# Публикуем всё в папку /app/publish
|
||||
RUN dotnet publish "FirstApplication.csproj" -c $BUILD_CONFIGURATION -o /app/publish --self-contained false --no-restore
|
||||
|
||||
# Берём тот образ, что определили в первой строке
|
||||
FROM base AS final
|
||||
# Указываем рабочую директорию
|
||||
WORKDIR /app
|
||||
# Из опубликованного блока берём то, что опубликовано и закидывае в /app
|
||||
COPY --from=publish /app/publish .
|
||||
# Указываем комманду запуска приложения (когда контейнер запустится, то начнёт с этого)
|
||||
ENTRYPOINT ["dotnet", "FirstApplication.dll"]
|
||||
15
kuzarin_maxim_lab_2/FirstApplication/FirstApplication.csproj
Normal file
15
kuzarin_maxim_lab_2/FirstApplication/FirstApplication.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
17
kuzarin_maxim_lab_2/FirstApplication/Program.cs
Normal file
17
kuzarin_maxim_lab_2/FirstApplication/Program.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
Console.WriteLine("Запущен первый сервис");
|
||||
string inputPath = Environment.GetEnvironmentVariable("INPUT_PATH") ?? "./input1";
|
||||
string outputPath = Environment.GetEnvironmentVariable("OUTPUT_PATH") ?? "./output1";
|
||||
|
||||
if (!Directory.Exists(inputPath))
|
||||
Directory.CreateDirectory(inputPath);
|
||||
|
||||
if (!Directory.Exists(outputPath))
|
||||
Directory.CreateDirectory(outputPath);
|
||||
|
||||
var file = new DirectoryInfo(inputPath).EnumerateFiles().Aggregate((x, y) => x.Length > y.Length ? x : y);
|
||||
|
||||
var outputFilePath = Path.Combine(outputPath, "data.txt");
|
||||
|
||||
if(File.Exists(outputFilePath))
|
||||
File.Delete(outputFilePath);
|
||||
File.Move(file.FullName, Path.Combine(outputPath, "data.txt"));
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user