Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ target/
*.iws
*.iml
*.ipr
amplicode.xml

### NetBeans ###
/nbproject/private/
Expand Down
48 changes: 47 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<h2>Требования к приложению</h2>

<ul>
<ul>
<li><strong>База данных:</strong> Использовать <code>PostgreSQL</code>.</li>
<li><strong>Миграции:</strong> Настроить миграции с помощью <code>Liquibase</code> или <code>Flyway</code>. Миграции должны автоматически применяться при старте приложения и в тестах.</li>
<li><strong>Работа с БД:</strong> Не использовать <code>Hibernate/JPA</code>, вместо этого использовать <code>JdbcTemplate</code>.</li>
Expand Down Expand Up @@ -113,3 +113,49 @@
<li>Пакеты следует логически организовать: <code>controller</code>, <code>service</code>, <code>repository</code>, <code>dto</code>, <code>config</code>, <code>exception</code> и т.д.</li>
<li>Бизнес-ошибки должны обрабатываться централизованно через единый ExceptionHandler.</li>
</ul>

## Модель данных Задачи (TODO item):

* `id` (Long, Primary Key, автоинкремент)
* `userId` (Long, Foreign Key, не null, ссылка на пользователя)
* `title` (String, не null, макс. длина, например, 255)
* `description` (String, опционально, text)
* `completed` (boolean, по умолчанию false)
* `createdAt` (Timestamp, не null, по умолчанию текущее время)
* `updatedAt` (Timestamp, не null, по умолчанию текущее время, обновляется при изменении)
* `dueDate` (Date/Timestamp, опционально, для инновационной функции)

## Функции приложения:

### Необходимые функции (базовый CRUD):

1. **Создание новой задачи:**
* **Эндпоинт:** `POST /api/v1/todos`
* **Вход:** DTO с `title` (обязательно), `description` (опционально).
* **Выход:** DTO созданной задачи с присвоенным `id`, `createdAt`, `updatedAt` и `completed=false`.
* **Действия:** Сохранение новой задачи в БД. Валидация входных данных.

2. **Получение списка всех задач (с пагинацией):**
* **Эндпоинт:** `GET /api/v1/todos`
* **Параметры запроса:** `limit` (int, по умолчанию, например, 10), `offset` (int, по умолчанию 0).
* **Выход:** Список DTO задач, обернутый, возможно, в объект, содержащий также информацию о пагинации (`totalCount`, `limit`, `offset`).
* **Действия:** Извлечение задач из БД с учетом пагинации.

3. **Получение задачи по ID:**
* **Эндпоинт:** `GET /api/v1/todos/{id}`
* **Вход (path variable):** `id` задачи.
* **Выход:** DTO задачи или 404, если задача не найдена.
* **Действия:** Поиск задачи в БД по ID.

4. **Обновление существующей задачи:**
* **Эндпоинт:** `PUT /api/v1/todos/{id}`
* **Вход (path variable):** `id` задачи.
* **Вход (body):** DTO с полями для обновления (`title`, `description`, `completed`).
* **Выход:** DTO обновленной задачи или 404.
* **Действия:** Обновление полей задачи в БД. Валидация входных данных. Обновление `updatedAt`.

5. **Удаление задачи:**
* **Эндпоинт:** `DELETE /api/v1/todos/{id}`
* **Вход (path variable):** `id` задачи.
* **Выход:** Статус 204 No Content или 404.
* **Действия:** Удаление задачи из БД.
132 changes: 129 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.5</version>
<relativePath/> <!-- lookup parent from repository -->
<version>3.5.0</version>
<relativePath/>
</parent>
<groupId>com.emobile</groupId>
<artifactId>SpringToDo</artifactId>
Expand All @@ -15,7 +15,7 @@
<description>SpringToDo</description>

<properties>
<java.version>21</java.version>
<java.version>17</java.version>
</properties>

<dependencies>
Expand All @@ -24,18 +24,144 @@
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope> <!-- или compile, если вы явно работаете с классами драйвера -->
</dependency>

<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.6.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.8.8</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-api</artifactId>
<version>2.8.8</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-testcontainers</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<annotationProcessorPaths>
<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>0.2.0</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.6.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
Expand Down
41 changes: 41 additions & 0 deletions src/main/java/com/emobile/springtodo/api/TagApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.emobile.springtodo.api;

import com.emobile.springtodo.model.dto.*;
import io.swagger.v3.oas.annotations.*;
import jakarta.validation.*;
import org.springframework.web.bind.annotation.*;

import static org.springframework.http.HttpStatus.*;


@RequestMapping("/api/v1/tags")
public interface TagApi {

@PostMapping
@ResponseStatus(CREATED)
TagDto createTag(@Valid @RequestBody TagCreateDto createTodoDto);

@PutMapping("/{id}")
TagDto updateTag(@Parameter(description = "ID of the tag to update") @PathVariable Long id,
@Valid @RequestBody TagUpdateDto requestDto);

@DeleteMapping("/{id}")
@ResponseStatus(NO_CONTENT)
void deleteTagById(@Parameter(description = "ID of the tag item to delete") @PathVariable Long id);

@GetMapping("/{id}")
TagDto getTagById(@Parameter(description = "ID of the tag to retrieve") @PathVariable Long id);

@GetMapping("/")
TagListResponseDto getAllTags();

@PutMapping("/{tagId}/tag/{todoId}")
void appendTagForTodo(
@PathVariable @Parameter(description = "ID of the task to append") Long tagId,
@PathVariable @Parameter(description = "ID of the tag to append to the task") Long todoId);

@DeleteMapping("/{tagId}/tag/{todoId}")
void removeTagForTodo(
@PathVariable @Parameter(description = "ID of the tag to delete from the task") Long tagId,
@PathVariable @Parameter(description = "ID of the task") Long todoId);
}
44 changes: 44 additions & 0 deletions src/main/java/com/emobile/springtodo/api/TodoApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.emobile.springtodo.api;

import com.emobile.springtodo.model.dto.*;
import io.swagger.v3.oas.annotations.*;
import jakarta.validation.*;
import org.springframework.web.bind.annotation.*;

import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.NO_CONTENT;


@RequestMapping("/api/v1/todos")
public interface TodoApi {

@PostMapping
@ResponseStatus(CREATED)
TodoDto createTodo(@Valid @RequestBody TodoCreateDto createTodoDto);

@PutMapping("/{id}")
TodoDto updateTodo(@Parameter(description = "ID of the task to update") @PathVariable Long id,
@Valid @RequestBody TodoUpdateDto requestDto);

@GetMapping("/{id}")
TodoDto getTodoById(@Parameter(description = "ID of the task to retrieve") @PathVariable (value = "id") Long id);

@DeleteMapping("/{id}")
@ResponseStatus(NO_CONTENT)
void deleteTodoById(@Parameter(description = "ID of the TODO item to delete") @PathVariable Long id);

@GetMapping("/user/{userId}")
TodoListResponseDto getAllTodosByUserId(
@PathVariable (value = "userId")
Long userId
);

@PatchMapping("/complete/{id}")
@ResponseStatus(NO_CONTENT)
void completeTodo(@Parameter(description = "ID of the task to complete") @PathVariable Long id);

@PatchMapping("/incomplete/{id}")
@ResponseStatus(NO_CONTENT)
void incompleteTodo(@Parameter(description = "ID of the task to mark incomplete") @PathVariable Long id);

}
Loading