RESTful API сервис, разработанный в рамках международной олимпиады «ИТ-Планета» 2023, конкурса «Прикладное программирование if...else» от компании SimbirSoft
С данным проектом прошел в финал конкурса, набрав 93 балла (функционал 65/70, архитектура 14/15, качество кода 14/15).
С заданием можете ознакомиться по ссылке: https://disk.yandex.ru/i/kjcDG19neVEV0A.
- Spring Boot
- Spring Security
- Spring Web
- Spring Validator
- JPA
- JDBC
- PostgreSQL
- Liquibase
- SPGIST index
- Docker
- Specification
- Java Topology Suite
- Model Mapper
- Patterns: Builder, Factory, Proxy
git clone https://github.com/OlegProg2020/animal-chipization.git
mvnw.cmd clean package -DskipTests=true
docker build ./ -t webapi
docker-compose up -d
Приложение начнет работать по адресу http://localhost:8080
POST - /registration
- request
Body {
"firstName": "string", // Имя пользователя
"lastName": "string", // Фамилия пользователя
"email": "string", // Адрес электронной почты
"password": "string" // Пароль от аккаунта пользователя
}
- response
Body {
"id": "int", // Идентификатор аккаунта пользователя
"firstName": "string", // Имя пользователя
"lastName": "string", // Фамилия пользователя
"email": "string", // Адрес электронной почты
"role": "string", // Роль аккаунта пользователя, по умолчанию при регистрации "USER"
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 201 |
| firstName = null, firstName = "" или состоит из пробелов, lastName = null, lastName = "" или состоит из пробелов, email = null, email = "" или состоит из пробелов, email аккаунта не валидный, password = null, password = "" или состоит из пробелов Неверные авторизационные данные |
400 |
| Запрос от авторизованного аккаунта | 403 |
| Аккаунт с таким email уже существует | 409 |
GET - /accounts/{accountId}
{accountId}: "int" // Идентификатор аккаунта пользователя
- request
Body {
empty
}
- response
Body {
"id": "int", // Идентификатор аккаунта пользователя
"firstName": "string", // Имя пользователя
"lastName": "string", // Фамилия пользователя
"email": "string" // Адрес электронной почты
"role": "string", // Роль аккаунта пользователя, по умолчанию при регистрации "USER"
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| accountId = null, accountId <= 0 |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| Аккаунт с ролью "USER" или "CHIPPER" пытается получить информацию о чужом аккаунте (даже если такого аккаунта нет) |
403 |
| Для аккаунтов с ролями "ADMIN": Аккаунт с таким accountId не найден |
404 |
GET - /accounts/search
?firstName={firstName}
&lastName={lastName}
&email={email}
&from={from}
&size={size}
{firstName}: "string", // Имя пользователя, может использоваться только часть имени без учета регистра, если null - не учитывается
{lastName}: "string", // Фамилия пользователя, может использоваться только часть фамилии без учета регистра, если null - не учитывается
{email}: "string", // Адрес электронной почты, может использоваться только часть адреса электронной почты без учета регистра, если null - не учитывается
{from}: "int" // Количество элементов, которое необходимо пропустить для формирования страницы с результатами (по умолчанию 0)
{size}: "int" // Количество элементов на странице (по умолчанию 10)
- request
Body {
empty
}
- response
Body [
{
"id”: "int", // Идентификатор аккаунта пользователя
"firstName": "string", // Имя пользователя
"lastName": "string", // Фамилия пользователя
"email": "string" // Адрес электронной почты
"role": "string", // Роль аккаунта пользователя, по умолчанию "USER"
}
]
Результаты поиска сортируются по id аккаунта от наименьшего к наибольшему
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| from < 0, size <= 0 |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" | 403 |
POST - /accounts
- request
Body {
"firstName": "string", // Имя пользователя
"lastName": "string", // Фамилия пользователя
"email": "string", // Адрес электронной почты
"password": "string", // Пароль от аккаунта
"role": "string", // Роль аккаунта пользователя, доступные значения "ADMIN", "CHIPPER", "USER"
}
- response
Body {
"id": "int", // Идентификатор аккаунта пользователя
"firstName": "string", // Новое имя пользователя
"lastName": "string", // Новая фамилия пользователя
"email": "string", // Новый адрес электронной почты
"role": "string", // Роль аккаунта пользователя
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 201 |
| firstName = null, firstName = "" или состоит из пробелов, lastName = null, lastName = "" или состоит из пробелов, email = null, email = "" или состоит из пробелов, email аккаунта не валидный, password = null, password = "" или состоит из пробелов, role != "ADMIN", "CHIPPER", "USER" |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" | 403 |
| Аккаунт с таким email уже существует | 409 |
PUT - /accounts/{accountId}
{accountId}: "int" // Идентификатор аккаунта пользователя
- request
Body {
"firstName": "string", // Новое имя пользователя
"lastName": "string", // Новая фамилия пользователя
"email": "string", // Новый адрес электронной почты
"password": "string" // Новый пароль от аккаунта
"role": "string", // Роль аккаунта пользователя, доступные значения "ADMIN", "CHIPPER", "USER"
}
- response
Body {
"id": "int", // Идентификатор аккаунта пользователя
"firstName": "string", // Новое имя пользователя
"lastName": "string", // Новая фамилия пользователя
"email": "string", // Новый адрес электронной почты
"role": "string", // Роль аккаунта пользователя
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| accountId = null, accountId <= 0, firstName = null, firstName = "" или состоит из пробелов, lastName = null, lastName = "" или состоит из пробелов, email = null, email = "" или состоит из пробелов, email аккаунта не валидный, password = null, password = "" или состоит из пробелов, role != "ADMIN", "CHIPPER", "USER" |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| Для аккаунтов с ролями "CHIPPER" и "USER": Обновление не своего аккаунта. Аккаунт не найден |
403 |
| Для аккаунтов с ролью "ADMIN": Аккаунт не найден |
404 |
| Аккаунт с таким email уже существует | 409 |
DELETE - /accounts/{accountId}
{accountId}: "int" // Идентификатор аккаунта пользователя
- request
Body {
empty
}
- response
Body {
empty
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| accountId = null, accountId <= 0, Аккаунт связан с животным |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| Для аккаунтов с ролями "CHIPPER" и "USER": Удаление не своего аккаунта. Аккаунт не найден |
403 |
| Для аккаунтов с ролью "ADMIN": Аккаунт не найден |
404 |
GET - /locations/{pointId}
{pointId}: "long" // Идентификатор точки локации
- request
Body {
empty
}
- response
Body {
"id": "long", // Идентификатор точки локации
"latitude": "double", // Географическая широта в градусах
"longitude": "double" // Географическая долгота в градусах
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| pointId = null, pointId <= 0 |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| Точка локации с таким pointId не найдена | 404 |
POST - /locations
- request
Body {
"latitude": "double", // Географическая широта в градусах
"longitude": "double" // Географическая долгота в градусах
}
- response
Body {
"id": "long", // Идентификатор точки локации
"latitude": "double", // Географическая широта в градусах
"longitude": "double" // Географическая долгота в градусах
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 201 |
| latitude = null, latitude < -90, latitude > 90, longitude = null, longitude < -180, longitude > 180 |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" или "CHIPPER" | 403 |
| Точка локации с такими latitude и longitude уже существует | 409 |
PUT - /locations/{pointId}
{pointId}: "long" // Идентификатор точки локации
- request
Body {
"latitude": "double", // Новая географическая широта в градусах
"longitude": "double" // Новая географическая долгота в градусах
}
- response
Body {
"id": "long", // Идентификатор точки локации
"latitude": "double", // Новая географическая широта в градусах
"longitude": "double" // Новая географическая долгота в градусах
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| pointId = null, pointId <= 0, latitude = null, latitude < -90, latitude > 90, longitude = null, longitude < -180, longitude > 180 Если точка используется как точка чипирования или как посещенная точка, то её изменять нельзя |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" или "CHIPPER" | 403 |
| Точка локации с таким pointId не найдена | 404 |
| Точка локации с такими latitude и longitude уже существует | 409 |
DELETE - /locations/{pointId}
{pointId}: "long" // Идентификатор точки локации
- request
Body {
empty
}
- response
Body {
empty
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| pointId = null, pointId <= 0, Точка локации связана с животным |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" | 403 |
| Точка локации с таким pointId не найдена | 404 |
GET - /areas/{areaId}
{areaId}: "long" // Идентификатор зоны
- request
Body {
empty
}
- response
Body {
"id": "long", // Идентификатор зоны
"name": "string", // Название зоны
"areaPoints": [
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
},
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
},
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
}
]
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| areaId = null, areaId <= 0 |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| Зона с таким areaId не найдена | 404 |
POST - /areas
- request
Body {
"name": "string", // Название зоны
"areaPoints": [
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
},
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
},
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
}
]
}
- response
Body {
"id": "long", // Идентификатор зоны
"name": "string", // Название зоны
"areaPoints": [
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
},
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
},
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
}
]
}
ВНИМАНИЕ! Координаты точек в массиве указываются в порядке их соединения для образования замкнутого САМО НЕПЕРЕСЕКАЮЩЕГОСЯ многоугольника, то есть точки соединяются последовательно от первой к последней, последняя соединяется с первой, при этом полученные отрезки не должны пересекать друг друга
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 201 |
| name = null, name = "" или состоит из пробелов, areaPoints = null, Одна или несколько точек из areaPoints = null, longitude = null, longitude < -180, longitude > 180, latitude = null, latitude < -90, latitude > 90, areaPoints содержит меньше трех точек. Все точки лежат на одной прямой. Границы новой зоны пересекаются между собой. Границы новой зоны пересекают границы уже существующей зоны. Граница новой зоны находятся внутри границ существующей зоны. Границы существующей зоны находятся внутри границ новой зоны. Новая зона имеет дубликаты точек. Новая зона состоит из части точек существующей зоны и находится на площади существующей зоны. |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" | 403 |
| Зона, состоящая из таких точек, уже существует. (При этом важен порядок, в котором указаны точки, но не важна начальная точка). Зона с таким name уже существует |
409 |
PUT - /areas/{areaId}
{areaId}: "long" // Идентификатор зоны
- request
Body {
"name": "string", // Название зоны
"areaPoints": [
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
},
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
},
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
}
]
}
- response
Body {
"id": "long", // Идентификатор зоны
"name": "string" // Название зоны
"areaPoints": [
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
},
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
},
{
"longitude": "double", // Географическая долгота в градусах
"latitude": "double" // Географическая широта в градусах
}
]
}
ВНИМАНИЕ! Координаты точек в массиве указываются в порядке их соединения для образования замкнутого САМО НЕПЕРЕСЕКАЮЩЕГОСЯ многоугольника, то есть точки соединяются последовательно от первой к последней, последняя соединяется с первой, при этом полученные отрезки не должны пересекать друг друга
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| areaId = null, areaId <= 0, name = null, name = "" или состоит из пробелов, areaPoints = null, Одна или несколько точек из areaPoints = null, longitude = null, longitude < -180, longitude > 180, latitude = null, latitude < -90, latitude > 90 areaPoints содержит меньше трех точек. Все точки лежат на одной прямой. Границы новой зоны пересекаются между собой. Границы новой зоны пересекают границы уже существующей зоны. Новая зона имеет дубликаты точек. Граница новой зоны находятся внутри границ существующей зоны. Границы существующей зоны находятся внутри границ новой зоны. Новая зона состоит из части точек существующей зоны и находится на площади существующей зоны. |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" | 403 |
| Зона, состоящая из таких точек, уже существует. (При этом важен порядок, в котором указаны точки, но не важна начальная точка). Зона с таким name уже существует |
409 |
DELETE - /areas/{areaId}
{areaId}: "long" // Идентификатор зоны
- request
Body {
empty
}
- response
Body {
empty
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| areaId = null, areaId <= 0 |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" | 403 |
| Зона с таким areaId не найдена | 404 |
GET - /animals/types/{typeId}
{typeId}: "long" // Идентификатор типа животного
- request
Body {
empty
}
- response
Body {
"id": "long", // Идентификатор типа животного
"type": "string" // Тип животного
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| typeId = null, typeId <= 0 |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| Тип животного с таким typeId не найден | 404 |
POST - /animals/types
- request
Body {
"type": "string" // Тип животного
}
- response
Body {
"id": "long", // Идентификатор типа животного
"type": "string" // Тип животного
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 201 |
| type = null, type = "" или состоит из пробелов |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" или "CHIPPER" | 403 |
| Тип животного с таким type уже существует | 409 |
PUT - /animals/types/{typeId}
{typeId}: "long" // Идентификатор типа животного
- request
Body {
"type": "string" // Новый тип животного
}
- response
Body {
"id": "long", // Идентификатор типа животного
"type": "string" // Новый тип животного
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| typeId = null, typeId <= 0, type = null, type = "" или состоит из пробелов |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" или "CHIPPER" | 403 |
| Тип животного с таким typeId не найден | 404 |
| Тип животного с таким type уже существует | 409 |
DELETE - /animals/types/{typeId}
{typeId}: "long" // Идентификатор типа животного
- request
Body {
empty
}
- response
Body {
empty
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| typeId = null, typeId <= 0 Есть животные с типом с typeId |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" | 403 |
| Тип животного с таким typeId не найден | 404 |
GET - /animals/{animalId}
{animalId}: "long" // Идентификатор животного
- request
Body {
empty
}
- response
Body {
"id": "long", // Идентификатор животного
"animalTypes": "[long]", // Массив идентификаторов типов животного
"weight": "float", // Масса животного, кг
"length": "float", // Длина животного, м
"height": "float", // Высота животного, м
"gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
"lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
"chippingDateTime": "dateTime", // Дата и время чипирования в формате ISO-8601 (устанавливается автоматически на момент добавления животного)
"chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
"chippingLocationId": "long", // Идентификатор точки локации животных
"visitedLocations": "[long]", // Массив идентификаторов объектов с информацией о посещенных точках локаций
"deathDateTime": "dateTime" // Дата и время смерти животного в формате ISO-8601 (устанавливается автоматически при смене lifeStatus на “DEAD”). Равняется null, пока lifeStatus = “ALIVE”.
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| animalId = null, animalId <= 0 |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| Животное с animalId не найдено | 404 |
GET - /animals/search?
startDateTime={startDateTime}
&endDateTime={endDateTime}
&chipperId={chipperId}
&chippingLocationId={chippingLocationId}
&lifeStatus={lifeStatus}
&gender={gender}
&from=0
&size=10
{startDateTime}: "dateTime", // Дата и время, не раньше которых произошло чипирование животного в формате ISO-8601, если null - не учитывается
{endDateTime}: "dateTime", // Дата и время, не позже которых произошло чипирование животного в формате ISO-8601, если null - не учитывается
{chipperId}: "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN", если null - не учитывается
{chippingLocationId}: "long", // Идентификатор локации чипирования животного, если null - не учитывается
{lifeStatus}: "string", // Жизненный статус животного, если null - не учитывается
{gender}: "string", // Гендерная принадлежность животного, если null - не учитывается
{from}: "int", // Количество элементов, которое необходимо пропустить для формирования страницы с результатами (по умолчанию 0)
{size}: "int", // Количество элементов на странице (по умолчанию 10)
- request
Body {
empty
}
- response
Body [
{
"id": "long", // Идентификатор животного
"animalTypes": "[long]", // Массив идентификаторов типов животного
"weight": "float", // Масса животного, кг
"length": "float", // Длина животного, м
"height": "float", // Высота животного, м
"gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
"lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
"chippingDateTime": "dateTime", // Дата и время чипирования в формате ISO-8601 (устанавливается автоматически на момент добавления животного)
"chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
"chippingLocationId": "long", // Идентификатор точки локации животных
"visitedLocations": "[long]", // Массив идентификаторов объектов с информацией о посещенных точках локаций
"deathDateTime": "dateTime" // Дата и время смерти животного в формате ISO-8601 (устанавливается автоматически при смене lifeStatus на “DEAD”). Равняется null, пока lifeStatus = “ALIVE”.
}
]
Результаты поиска сортируются по id животного от наименьшего к наибольшему
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| from < 0, size <= 0, startDateTime - не в формате ISO-8601, endDateTime - не в формате ISO-8601, chipperId <= 0, chippingLocationId <= 0, lifeStatus != “ALIVE”, “DEAD”, gender != “MALE”, “FEMALE”, “OTHER” |
400 |
| Запрос от неавторизованного аккаунта | 401 |
POST - /animals
- request
Body {
"animalTypes": "[long]", // Массив идентификаторов типов животного
"weight": "float", // Масса животного, кг
"length": "float", // Длина животного, м
"height": "float", // Высота животного, м
"gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
"chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
"chippingLocationId": "long" // Идентификатор точки локации животных
}
- response
Body {
"id": "long", // Идентификатор животного
"animalTypes": "[long]", // Массив идентификаторов типов животного
"weight": "float", // Масса животного, кг
"length": "float", // Длина животного, м
"height": "float", // Высота животного, м
"gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
"lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
"chippingDateTime": "dateTime", // Дата и время чипирования в формате ISO-8601 (устанавливается автоматически на момент добавления животного)
"chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
"chippingLocationId": "long", // Идентификатор точки локации животных
"visitedLocations": "[long]", // Массив идентификаторов объектов с информацией о посещенных точках локаций
"deathDateTime": "dateTime" // Дата и время смерти животного в формате ISO-8601 (устанавливается автоматически при смене lifeStatus на “DEAD”). Равняется null, пока lifeStatus = “ALIVE”.
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 201 |
| animalTypes = null, animalTypes.size() <= 0 Элемент массива animalTypes = null Элемент массива animalTypes <= 0 weight = null, weight <=0, length = null, length <=0, height = null, height <=0, gender = null, gender != "MALE", "FEMALE", "OTHER", chipperId = null, chipperId <=0, chippingLocationId = null, chippingLocationId <=0 |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" или "CHIPPER" | 403 |
| Тип животного не найден, Аккаунт с chipperId не найден, Точка локации с chippingLocationId не найдена |
404 |
PUT - /animals/{animalId}
{animalId}: "long" // Идентификатор животного
- request
Body {
"weight": "float", // Масса животного, кг
"length": "float", // Длина животного, м
"height": "float", // Высота животного, м
"gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
"lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
"chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
"chippingLocationId": "long" // Идентификатор точки локации животных
}
- response
Body {
"id": "long", // Идентификатор животного
"animalTypes": "[long]", // Массив идентификаторов типов животного
"weight": "float", // Масса животного, кг
"length": "float", // Длина животного, м
"height": "float", // Высота животного, м
"gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
"lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
"chippingDateTime": "dateTime", // Дата и время чипирования в формате ISO-8601 (устанавливается автоматически на момент добавления животного)
"chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
"chippingLocationId": "long", // Идентификатор точки локации животных
"visitedLocations": "[long]", // Массив идентификаторов объектов с информацией о посещенных точках локаций
"deathDateTime": "dateTime" // Дата и время смерти животного в формате ISO-8601 (устанавливается автоматически при смене lifeStatus на “DEAD”). Равняется null, пока lifeStatus = “ALIVE”.
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| animalId = null, animalId <=0, weight = null weight <=0, length = null length <=0, height = null height <=0, gender != “MALE”, “FEMALE”, “OTHER”, lifeStatus != “ALIVE”, “DEAD”, chipperId = null, chipperId <=0, chippingLocationId = null, chippingLocationId <=0 Установка lifeStatus = “ALIVE”, если у животного lifeStatus = “DEAD” Новая точка чипирования совпадает с первой посещенной точкой локации |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" или "CHIPPER" | 403 |
| Животное с animalId не найдено Аккаунт с chipperId не найден Точка локации с chippingLocationId не найдена |
404 |
DELETE - /animals/{animalId}
{animalId}: "long" // Идентификатор животного
- request
Body {
empty
}
- response
Body {
empty
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| animalId = null, animalId <=0 Животное покинуло локацию чипирования, при этом есть другие посещенные точки |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" | 403 |
| Животное с animalId не найдено | 404 |
POST - /animals/{animalId}/types/{typeId}
{animalId}: "long" // Идентификатор животного
{typeId}: "long" // Идентификатор типа животного
- request
Body {
empty
}
- response
Body {
"id": "long", // Идентификатор животного
"animalTypes": "[long]", // Массив идентификаторов типов животного
"weight": "float", // Масса животного, кг
"length": "float", // Длина животного, м
"height": "float", // Высота животного, м
"gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
"lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
"chippingDateTime": "dateTime", // Дата и время чипирования в формате ISO-8601 (устанавливается автоматически на момент добавления животного)
"chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
"chippingLocationId": "long", // Идентификатор точки локации животных
"visitedLocations": "[long]", // Массив идентификаторов объектов с информацией о посещенных точках локаций
"deathDateTime": "dateTime" // Дата и время смерти животного в формате ISO-8601 (устанавливается автоматически при смене lifeStatus на “DEAD”). Равняется null, пока lifeStatus = “ALIVE”.
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 201 |
| animalId = null, animalId <= 0, typeId = null, typeId <= 0 |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" или "CHIPPER" | 403 |
| Животное с animalId не найдено Тип животного с typeId не найден |
404 |
PUT - /animals/{animalId}/types
{animalId}: "long" // Идентификатор животного
- request
Body {
"oldTypeId": "long", // Идентификатор текущего типа животного
"newTypeId": "long" // Идентификатор нового типа животного для замены
}
- response
Body {
"id": "long", // Идентификатор животного
"animalTypes": "[long]", // Массив идентификаторов типов животного
"weight": "float", // Масса животного, кг
"length": "float", // Длина животного, м
"height": "float", // Высота животного, м
"gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
"lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
"chippingDateTime": "dateTime", // Дата и время чипирования в формате ISO-8601 (устанавливается автоматически на момент добавления животного)
"chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
"chippingLocationId": "long", // Идентификатор точки локации животных
"visitedLocations": "[long]", // Массив идентификаторов объектов с информацией о посещенных точках локаций
"deathDateTime": "dateTime" // Дата и время смерти животного в формате ISO-8601 (устанавливается автоматически при смене lifeStatus на “DEAD”). Равняется null, пока lifeStatus = “ALIVE”.
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| animalId = null, animalId <= 0, oldTypeId = null, oldTypeId <= 0, newTypeId = null, newTypeId <= 0 |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" или "CHIPPER" | 403 |
| Животное с animalId не найдено Тип животного с oldTypeId не найден Тип животного с newTypeId не найден Типа животного с oldTypeId нет у животного с animalId |
404 |
| Тип животного с newTypeId уже есть у животного с animalId Животное с animalId уже имеет типы с oldTypeId и newTypeId |
409 |
DELETE - /animals/{animalId}/types/{typeId}
{animalId}: "long" // Идентификатор животного
{typeId}: "long" // Идентификатор типа животного
- request
Body {
empty
}
- response
Body {
"id": "long", // Идентификатор животного
"animalTypes": "[long]", // Массив идентификаторов типов животного
"weight": "float", // Масса животного, кг
"length": "float", // Длина животного, м
"height": "float", // Высота животного, м
"gender": "string", // Гендерный признак животного, доступные значения “MALE”, “FEMALE”, “OTHER”
"lifeStatus": "string", // Жизненный статус животного, доступные значения “ALIVE”(устанавливается автоматически при добавлении нового животного), “DEAD”(можно установить при обновлении информации о животном)
"chippingDateTime": "dateTime", // Дата и время чипирования в формате ISO-8601 (устанавливается автоматически на момент добавления животного)
"chipperId": "int", // Идентификатор аккаунта с ролью "CHIPPER" или "ADMIN"
"chippingLocationId": "long", // Идентификатор точки локации животных
"visitedLocations": "[long]", // Массив идентификаторов объектов с информацией о посещенных точках локаций
"deathDateTime": "dateTime" // Дата и время смерти животного в формате ISO-8601 (устанавливается автоматически при смене lifeStatus на “DEAD”). Равняется null, пока lifeStatus = “ALIVE”.
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| animalId = null, animalId <= 0, typeId = null, typeId <= 0 У животного только один тип и это тип с typeId |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" или "CHIPPER" | 403 |
| Животное с animalId не найдено Тип животного с typeId не найден У животного с animalId нет типа с typeId |
404 |
GET - /animals/{animalId}/locations
?startDateTime=
&endDateTime=
&from={from}
&size={size}
{animalId}: "long" // Идентификатор животного
{startDateTime}: "dateTime" // Дата и время, не раньше которых нужно искать посещенные точки локации животных в формате ISO-8601, если null - не учитывается
{endDateTime}: "dateTime" // Дата и время, не позже которых нужно искать посещенные точки локации животных в формате ISO-8601, если null - не учитывается
{from}: "int" // Количество элементов, которое необходимо пропустить для формирования страницы с результатами (по умолчанию 0)
{size}: "int" // Количество элементов на странице (по умолчанию 10)
- request
Body {
empty
}
- response
Body[
{
"id": "long", // Идентификатор объекта с информацией о посещенной точке локации
"dateTimeOfVisitLocationPoint": "dateTime", // Дата и время посещения животным точки локации в формате ISO-8601
"locationPointId": "long" // Идентификатор посещенной точки локации
}
]
Результаты поиска сортируются по дате посещения точки от ранней к поздней
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| animalId = null, animalId <= 0, from < 0, size <= 0, startDateTime - не в формате ISO-8601, endDateTime - не в формате ISO-8601 |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| Животное с animalId не найдено | 404 |
POST - /animals/{animalId}/locations/{pointId}
{animalId}: "long" // Идентификатор животного
{pointId}: "long" // Идентификатор точки локации
- request
Body {
empty
}
- response
Body {
"id": "long", // Идентификатор объекта с информацией о посещенной точке локации
"dateTimeOfVisitLocationPoint": "dateTime", // Дата и время посещения животным точки локации в формате ISO-8601
"locationPointId": "long" // Идентификатор посещенной точки локации
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 201 |
| animalId = null, animalId <= 0, pointId= null, pointId <= 0, У животного lifeStatus = "DEAD" Животное находится в точке чипирования и никуда не перемещалось, попытка добавить точку локации, равную точке чипирования. Попытка добавить точку локации, в которой уже находится животное |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" или "CHIPPER" | 403 |
| Животное с animalId не найдено Точка локации с pointId не найдена |
404 |
PUT - /animals/{animalId}/locations
{animalId}: "long" // Идентификатор животного
- request
Body {
"visitedLocationPointId": "long", // Идентификатор объекта с информацией о посещенной точке локации
"locationPointId": "long" // Идентификатор посещенной точки локации
}
- response
Body {
"id": "long", // Идентификатор объекта с информацией о посещенной точке локации
"dateTimeOfVisitLocationPoint": "dateTime", // Дата и время посещения животным точки локации в формате ISO-8601
"locationPointId": "long" // Идентификатор посещенной точки локации
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| animalId = null, animalId <= 0, visitedLocationPointId = null, visitedLocationPointId <= 0, locationPointId = null, locationPointId <= 0 Обновление первой посещенной точки на точку чипирования Обновление точки на такую же точку Обновление точки локации на точку, совпадающую со следующей и/или с предыдущей точками |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" или "CHIPPER" | 403 |
| Животное с animalId не найдено Объект с информацией о посещенной точке локации с visitedLocationPointId не найден. У животного нет объекта с информацией о посещенной точке локации с visitedLocationPointId. Точка локации с locationPointId не найдена |
404 |
DELETE - /animals/{animalId}/locations/{visitedPointId}
{animalId}: "long" // Идентификатор животного
{visitedPointId}: "long" // Идентификатор объекта с информацией о посещенной точке локации
- request
Body {
empty
}
- response
Body {
empty
}
| Условие | Статус |
|---|---|
| Запрос успешно выполнен (Если удаляется первая посещенная точка локации, а вторая точка совпадает с точкой чипирования, то она удаляется автоматически) |
200 |
| animalId = null, animalId <= 0 visitedPointId = null, visitedPointId <= 0 |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| У аккаунта нет роли "ADMIN" | 403 |
| Животное с animalId не найдено Объект с информацией о посещенной точке локации с visitedPointId не найден. У животного нет объекта с информацией о посещенной точке локации с visitedPointId |
404 |
GET - /areas/{areaId}/analytics
?startDate=
&endDate=
{areaId}: "long", // Идентификатор зоны
{startDate}: "date", // Дата начала интервала в формате ISO-8601 (pattern "yyyy-MM-dd")
{endDate}: "date" // Дата конца интервала в формате ISO-8601 (pattern "yyyy-MM-dd")
- request
Body {
empty
}
- response
Body {
"totalQuantityAnimals": "long", // Общее количество животных, находящихся в этой зоне в указанный интервал времени
"totalAnimalsArrived": "long", // Общее количество посещений зоны в указанный интервал времени
"totalAnimalsGone": "long", // Общее количество выходов из зоны в указанный интервал времени
"animalsAnalytics": [
{
"animalType": "string", // Тип животных
"animalTypeId": "long", // Идентификатор типа животных
"quantityAnimals": "long", // Количество животных данного типа, находящихся в этой зоне в указанный интервал времени
"animalsArrived": "long", // Количество животных данного типа, прибывших в эту зону в указанный интервал времени
"animalsGone": "long", // Количество животных данного типа, покинувших эту зону в указанный интервал времени
}
]
}
ВНИМАНИЕ! Одно и то же животное может несколько раз посетить и покинуть зону в указанный период. Для каждого животного учитывается только один вход в зону и один выход из нее.
| Условие | Статус |
|---|---|
| Запрос успешно выполнен | 200 |
| areaId = null, areaId <= 0, startDate - не в формате ISO-8601 (pattern "yyyy-MM-dd") endDate - не в формате ISO-8601 (pattern "yyyy-MM-dd") startDate >= endDate |
400 |
| Запрос от неавторизованного аккаунта | 401 |
| Зона с areaId не найдена | 404 |