@@ -1,52 +1,53 @@
# Руководство по API для фронтенда
## Обзор
## Обзор изменений
После рефакторинга ParserController теперь поддерживает эффективную работу с фронтендом через строго типизированные DTO и расширенные возможности фильтрации и пагинации.
После рефакторинга старый ` ParserController` был разделен на три специализированных контроллера для лучшей организации и масштабируемости:
## Основные изменения
- **`MarketItemController` ** (`/api/items` ) - для получения данных
- **`ParserAdminController` ** (`/api/admin/parsers` ) - для управления парсерами
- **`HealthCheckController` ** (`/api/health` ) - для мониторинга системы
### 1. Строгая типизация ответов
---
Все ответы API теперь используют DTO классы вместо `Map<String, Object>` :
## 📊 MarketItemController - Получение данных
- `ApiResponse<T>` - универсальный wrapper для всех ответов
- `ParserResultDto` - результат парсинга
- `ParserStatsDto` - статистика системы
- `HealthCheckDto` - состояние сервиса
**Базовый URL ** : `/api/items`
### 2 . Пагинация и сортировка
Поддержка Spring Data пагинации с параметрами:
- `page` - номер страницы (начиная с 0)
- `size` - количество элементов на странице
- `sort` - поле для сортировки и направление
### 3. Фильтрация данных
Возможность фильтрации по:
- `sourceName` - источнику новостей
- `startDate` - начальной дате
- `endDate` - конечной дате
## API Endpoints
### 1. Получение записей с фильтрацией и пагинацией
### 1 . Получение списка новостей с фильтрацией и пагинацией
``` http
G E T / a p i / p a r s e r / i t e m s ? p a g e = 0 & s i z e = 1 0 & s o r t = p u b l i s h e d A t , d e s c & s o u r c e N a m e = K u r s i v & s t a r t D a t e = 2 0 2 5 - 0 1 - 0 1 & e n d D a t e = 2 0 2 5 - 0 1 - 3 1
G E T / a p i / i t e m s
```
**Параметры: **
**Параметры запроса : **
- `pag e` (optional, default: 0) - номер страницы
- `siz e` (optional, default: 20) - размер страницы
- `sort ` (optional, default: "publishedAt,desc") - сортировка
- `sourceNam e` (optional) - фильтр по источнику
- `startDat e` (optional) - начальная дата (ISO format )
- `endDate ` (optional) - конечная дата (ISO format )
- `sourceNam e` (optional) - фильтр по источнику (например: "Kursiv Media", "Kapital.kz", "LSM.kz", "РБК", "Ведомости")
- `startDat e` (optional) - начальная дата в формате ISO (например: "2024-01-01T00:00:00")
- `endDate ` (optional) - конечная дата в формате ISO (например: "2024-12-31T23:59:59")
- `pag e` (optional) - номер страницы (по умолчанию: 0)
- `siz e` (optional) - размер страницы (по умолчанию: 20 )
- `sort ` (optional) - сортировка (по умолчанию: "publishedAt,desc" )
**Примеры запросов: **
``` javascript
// Получить все новости
fetch ( '/api/items' ) ;
// Получить новости с пагинацией
fetch ( '/api/items?page=0&size=10' ) ;
// Получить новости от конкретного источника
fetch ( '/api/items?sourceName=Kursiv Media' ) ;
// Получить новости за последние 7 дней
const weekAgo = new Date ( Date . now ( ) - 7 * 24 * 60 * 60 * 1000 ) . toISOString ( ) ;
fetch ( ` /api/items?startDate= ${ weekAgo } ` ) ;
// Получить новости с сортировкой по дате (старые сначала)
fetch ( '/api/items?sort=publishedAt,asc' ) ;
```
**Ответ: **
@@ -57,14 +58,14 @@ GET /api/parser/items?page=0&size=10&sort=publishedAt,desc&sourceName=Kursiv&sta
"data" : {
"content" : [
{
"id" : "... " ,
"sourceName" : "Kursiv (Бизнес/экономика) " ,
"id" : "64f1a2b3c4d5e6f7g8h9i0j1 " ,
"sourceName" : "Kursiv Media " ,
"url" : "https://kursiv.media/news/example" ,
"title" : "Заголовок новости" ,
"url" : "https://kursiv.media/... ",
"publish edAt" : "2025 -01-14 T10:30 :00" ,
"addedA t" : "2025-01-14T12:00:00 " ,
"rawText " : "Текст статьи ..." ,
"hash" : "..." ,
"publishedAt" : "2024-01-15T10:30:00 ",
"add edAt" : "2024 -01-15 T10:35 :00" ,
"rawTex t" : "Текст новости без HTML тегов " ,
"hash " : "abc123def456 ..." ,
"category" : "Бизнес" ,
"analytics" : {
"summary" : null ,
@@ -77,36 +78,51 @@ GET /api/parser/items?page=0&size=10&sort=publishedAt,desc&sourceName=Kursiv&sta
"pageable" : {
"sort" : {
"sorted" : true ,
"unsorted" : false ,
"empty" : false
"unsorted" : false
} ,
"pageNumber" : 0 ,
"pageSize" : 10 ,
"pageSize" : 20 ,
"offset" : 0 ,
"paged" : true ,
"unpaged" : false
} ,
"totalElements" : 25 ,
"totalPages" : 3 ,
"totalElements" : 150 ,
"totalPages" : 8 ,
"last" : false ,
"first" : true ,
"numberOfElements" : 10 ,
"size" : 10 ,
"numberOfElements" : 20 ,
"size" : 20 ,
"number" : 0 ,
"sort" : {
"sorted" : true ,
"unsorted" : false ,
"empty" : false
} ,
"empty" : false
}
}
```
### 2. Статистика системы
### 2. Получение новости по ID
``` http
G E T / a p i / p a r s e r / s t a t s
G E T / a p i / i t e m s / { i d }
```
**Пример: **
``` javascript
fetch ( '/api/items/64f1a2b3c4d5e6f7g8h9i0j1' ) ;
```
**Ответ: **
``` json
{
"success" : false ,
"message" : "Функция поиска по ID пока не реализована"
}
```
### 3. Получение статистики
``` http
G E T / a p i / i t e m s / s t a t s
```
**Ответ: **
@@ -116,22 +132,23 @@ GET /api/parser/stats
"success" : true ,
"message" : "Статистика получена успешно" ,
"data" : {
"totalItems" : 150 ,
"totalItems" : 1250 ,
"sourceStatistics" : {
"Kursiv (Бизнес/экономика) " : 75 ,
"Kapital.kz (Бизнес) " : 75
"Kursiv Media " : 300 ,
"Kapital.kz" : 250 ,
"LSM.kz" : 200 ,
"РБК" : 300 ,
"Ведомости" : 200
} ,
"lastUpdate" : "2025 -01-14 T12:00 :00"
"lastUpdate" : "2024 -01-15 T10:35 :00"
}
}
```
### 3 . Запуск парсинга
#### Парсинг всех источников
### 4 . Получение списка источников
``` http
P O S T / a p i / p a r s e r / p a r s e / a l l
G E T / a p i / i t e m s / s o u r c e s
```
**Ответ: **
@@ -139,37 +156,200 @@ POST /api/parser/parse/all
``` json
{
"success" : true ,
"message" : "Парсинг всех источников заверш ен успешно" ,
"message" : "Источники получ ены успешно" ,
"data" : [ "Kursiv Media" , "Kapital.kz" , "LSM.kz" , "РБК" , "Ведомости" ]
}
```
### 5. Получение новостей по конкретному источнику
``` http
G E T / a p i / i t e m s / s o u r c e / { s o u r c e N a m e }
```
**Пример: **
``` javascript
fetch ( '/api/items/source/Kursiv Media?page=0&size=10' ) ;
```
**Ответ: ** Аналогичен ответу от `/api/items` , но только с новостями от указанного источника.
---
## ⚙️ ParserAdminController - Управление парсерами
**Базовый URL ** : `/api/admin/parsers`
> ⚠️ **Важно**: Эти эндпоинты предназначены для административных функций и могут потребовать аутентификации в будущем.
### 1. Запуск конкретного парсера
``` http
P O S T / a p i / a d m i n / p a r s e r s / p a r s e / { s o u r c e N a m e }
```
**Доступные источники: **
- `kursiv`
- `kapital`
- `lsm`
- `rbc`
- `vedomosti`
**Пример: **
``` javascript
// Запустить парсер Kursiv
fetch ( '/api/admin/parsers/parse/kursiv' , { method : 'POST' } ) ;
// Запустить парсер РБК
fetch ( '/api/admin/parsers/parse/rbc' , { method : 'POST' } ) ;
```
**Ответ: **
``` json
{
"success" : true ,
"message" : "Парсинг kursiv завершен" ,
"data" : {
"source" : "kursiv" ,
"processedItems" : 15 ,
"totalItemsInDb" : 1265 ,
"status" : "completed"
}
}
```
### 2. Запуск всех парсеров
``` http
P O S T / a p i / a d m i n / p a r s e r s / p a r s e / a l l
```
**Пример: **
``` javascript
fetch ( '/api/admin/parsers/parse/all' , { method : 'POST' } ) ;
```
**Ответ: **
``` json
{
"success" : true ,
"message" : "Парсинг всех источников завершен" ,
"data" : [
{
"source" : "K ursiv" ,
"processedItems" : 15 ,
"totalItemsInDb" : 90 ,
"source" : "k ursiv" ,
"processedItems" : 12 ,
"totalItemsInDb" : 1277 ,
"status" : "completed"
} ,
{
"source" : "K apital" ,
"processedItems" : 12 ,
"totalItemsInDb" : 90 ,
"source" : "k apital" ,
"processedItems" : 8 ,
"totalItemsInDb" : 1285 ,
"status" : "completed"
} ,
{
"source" : "lsm" ,
"processedItems" : 5 ,
"totalItemsInDb" : 1290 ,
"status" : "completed"
} ,
{
"source" : "rbc" ,
"processedItems" : 20 ,
"totalItemsInDb" : 1310 ,
"status" : "completed"
} ,
{
"source" : "vedomosti" ,
"processedItems" : 7 ,
"totalItemsInDb" : 1317 ,
"status" : "completed"
}
]
}
```
#### Парсинг конкретного источника
### 3. Получение списка доступных парсеров
``` http
P O S T / a p i / p a r s e r / p a r s e / k u r s i v
P O S T / a p i / p a r s e r / p a r s e / k a p i t a l
G E T / a p i / a d m i n / p a r s e r s
```
### 4. Проверка состояния
**Ответ: **
#### Общее состояние
``` json
{
"success" : true ,
"message" : "Список парсеров получен" ,
"data" : [ "kapital" , "kursiv" , "lsm" , "rbc" , "vedomosti" ]
}
```
### 4. Получение информации о парсерах
``` http
G E T / a p i / p a r s e r / h e a l t h
G E T / a p i / a d m i n / p a r s e r s / i n f o
```
**Ответ: **
``` json
{
"success" : true ,
"message" : "Информация о парсерах получена" ,
"data" : {
"totalParsers" : 5 ,
"availableParsers" : [ "kapital" , "kursiv" , "lsm" , "rbc" , "vedomosti" ] ,
"totalItems" : 1317 ,
"sourceStatistics" : {
"Kursiv Media" : 300 ,
"Kapital.kz" : 250 ,
"LSM.kz" : 200 ,
"РБК" : 300 ,
"Ведомости" : 200
}
}
}
```
### 5. Проверка существования парсера
``` http
G E T / a p i / a d m i n / p a r s e r s / e x i s t s / { s o u r c e N a m e }
```
**Пример: **
``` javascript
fetch ( '/api/admin/parsers/exists/kursiv' ) ;
```
**Ответ: **
``` json
{
"success" : true ,
"message" : "Проверка завершена" ,
"data" : true
}
```
---
## 🔍 HealthCheckController - Мониторинг системы
**Базовый URL ** : `/api/health`
### 1. Базовая проверка состояния
``` http
G E T / a p i / h e a l t h
```
**Ответ: **
@@ -181,19 +361,19 @@ GET /api/parser/health
"data" : {
"status" : "UP" ,
"service" : "RSS Parser System" ,
"timestamp" : 1705123456789 ,
"scheduler " : "Enabled - runs every 30 minutes"
"timestamp" : 1705312500000 ,
"details " : "Enabled - runs every 30 minutes"
}
}
```
#### Состояние MongoDB
### 2. Проверка подключения к MongoDB
``` http
G E T / a p i / p a r s e r / h e a l t h / m o n g o d b
G E T / a p i / h e a l t h / m o n g o d b
```
**Ответ при успехе : **
**Ответ ( успех) : **
``` json
{
@@ -201,13 +381,14 @@ GET /api/parser/health/mongodb
"message" : "MongoDB подключение успешно" ,
"data" : {
"status" : "UP" ,
"messag e" : "MongoDB подключение успешно" ,
"timestamp" : 1705123456789
"servic e" : "MongoDB подключение успешно" ,
"timestamp" : 1705312500000 ,
"details" : "Всего записей: 1317"
}
}
```
**Ответ при ошибке : **
**Ответ ( ошибка ) : **
``` json
{
@@ -215,126 +396,260 @@ GET /api/parser/health/mongodb
"message" : "Ошибка подключения к MongoDB" ,
"data" : {
"status" : "DOWN" ,
"messag e" : "Ошибка подключения к MongoDB: Command failed with error 13 (Unauthoriz ed) " ,
"timestamp" : 1705123456789 ,
"servic e" : "Ошибка подключения к MongoDB: Connection refus ed" ,
"timestamp" : 1705312500000 ,
"suggestion" : "Проверьте учетные данные в application.properties"
}
}
```
## Примеры использования
### 3. Информация о планировщике
### 1. Получение последних новостей с пагинацией
``` javascript
// Получить первую страницу с 10 записями, отсортированными по дате публикации
fetch ( '/api/parser/items?page=0&size=10&sort=publishedAt,desc' )
. then ( ( response ) => response . json ( ) )
. then ( ( data ) => {
if ( data . success ) {
const items = data . data . content ;
const totalPages = data . data . totalPages ;
// Обработка данных
}
} ) ;
``` http
G E T / a p i / h e a l t h / s c h e d u l e r
```
### 2. Фильтрация по источнику
``` javascript
// Получить только новости от Kursiv
fetch ( '/api/parser/items?sourceName=Kursiv (Бизнес/экономика)&page=0&size=20' )
. then ( ( response ) => response . json ( ) )
. then ( ( data ) => {
if ( data . success ) {
const kursivNews = data . data . content ;
// Обработка данных
}
} ) ;
```
### 3. Фильтрация по датам
``` javascript
// Получить новости за последнюю неделю
const endDate = new Date ( ) . toISOString ( ) ;
const startDate = new Date ( Date . now ( ) - 7 * 24 * 60 * 60 * 1000 ) . toISOString ( ) ;
fetch ( ` /api/parser/items?startDate= ${ startDate } &endDate= ${ endDate } ` )
. then ( ( response ) => response . json ( ) )
. then ( ( data ) => {
if ( data . success ) {
const recentNews = data . data . content ;
// Обработка данных
}
} ) ;
```
### 4. Комплексная фильтрация
``` javascript
// Получить новости Kapital за последний месяц, отсортированные по дате
const endDate = new Date ( ) . toISOString ( ) ;
const startDate = new Date ( Date . now ( ) - 30 * 24 * 60 * 60 * 1000 ) . toISOString ( ) ;
fetch (
` /api/parser/items?sourceName=Kapital.kz (Бизнес)&startDate= ${ startDate } &endDate= ${ endDate } &sort=publishedAt,desc&page=0&size=50 `
)
. then ( ( response ) => response . json ( ) )
. then ( ( data ) => {
if ( data . success ) {
const kapitalNews = data . data . content ;
const totalCount = data . data . totalElements ;
// Обработка данных
}
} ) ;
```
### 5. Получение статистики
``` javascript
fetch ( '/api/parser/stats' )
. then ( ( response ) => response . json ( ) )
. then ( ( data ) => {
if ( data . success ) {
const stats = data . data ;
console . log ( ` Всего новостей: ${ stats . totalItems } ` ) ;
console . log ( 'По источникам:' , stats . sourceStatistics ) ;
}
} ) ;
```
## Поддерживаемые форматы дат
API поддерживает следующие форматы дат:
- `yyyy-MM-dd` (например: 2025-01-14)
- `yyyy-MM-dd'T'HH:mm:ss` (например: 2025-01-14T10:30:00)
- `yyyy-MM-dd'T'HH:mm:ss.SSS` (например: 2025-01-14T10:30:00.000)
- `yyyy-MM-dd HH:mm:ss` (например: 2025-01-14 10:30:00)
## Обработка ошибок
Все ответы API следуют единому формату:
**Ответ: **
``` json
{
"success" : false ,
"message" : "Описание ошибки " ,
"error " : "Детальная информация об ошибке"
"success" : true ,
"message" : "Информация о планировщике получена " ,
"data " : {
"schedulerEnabled" : true ,
"cronExpression" : "0 0/30 * * * ?" ,
"description" : "Запуск каждые 30 минут (в 0 и 30 минут каждого часа)" ,
"nextRun" : "Следующий запуск будет в ближайшие 0 или 30 минут часа" ,
"activeParsers" : [ "kapital" , "kursiv" , "lsm" , "rbc" , "vedomosti" ] ,
"totalParsers" : 5 ,
"sources" : [ "Kursiv Media" , "Kapital.kz" , "LSM.kz" , "РБК" , "Ведомости" ]
}
}
```
HTTP статус коды:
### 4. Общая информация о системе
- `200` - Успешный запрос
- `500` - Внутренняя ошибка сервера
- `503` - Сервис недоступен (например, MongoDB)
``` http
G E T / a p i / h e a l t h / s y s t e m
```
## Рекомендации для фронтенда
**Ответ: **
1. **Кэширование ** : Используйте кэширование для статистики и часто запрашиваемых данных
2. **Пагинация ** : Реализуйте бесконечную прокрутку или традиционную пагинацию
3. **Фильтры ** : Предоставьте пользователю удобные фильтры по источнику и датам
4. **Обновление ** : Используйте WebSocket или polling для обновления данных в реальном времени
5. **Обработка ошибок ** : Всегда проверяйте поле `success` в ответе
``` json
{
"success" : true ,
"message" : "Информация о системе получена" ,
"data" : {
"parsers" : {
"total" : 5 ,
"available" : [ "kapital" , "kursiv" , "lsm" , "rbc" , "vedomosti" ]
} ,
"data" : {
"totalItems" : 1317 ,
"sourceStatistics" : {
"Kursiv Media" : 300 ,
"Kapital.kz" : 250 ,
"LSM.kz" : 200 ,
"РБК" : 300 ,
"Ведомости" : 200
}
} ,
"system" : {
"javaVersion" : "17.0.2" ,
"osName" : "Linux" ,
"osVersion" : "5.4.0-74-generic" ,
"uptime" : 1705312500000
}
}
}
```
### 5. Проверка состояния парсеров
``` http
G E T / a p i / h e a l t h / p a r s e r s
```
**Ответ: **
``` json
{
"success" : true ,
"message" : "Проверка парсеров завершена" ,
"data" : {
"totalParsers" : 5 ,
"availableParsers" : [ "kapital" , "kursiv" , "lsm" , "rbc" , "vedomosti" ] ,
"status" : "UP" ,
"message" : "Все парсеры доступны"
}
}
```
---
## 🚀 Примеры использования для фронтенда
### React/JavaScript примеры
``` javascript
// Сервис для работы с API
class NewsApiService {
constructor ( baseUrl = '' ) {
this . baseUrl = baseUrl ;
}
// Получить новости с фильтрацией
async getNews ( filters = { } ) {
const params = new URLSearchParams ( ) ;
if ( filters . sourceName ) params . append ( 'sourceName' , filters . sourceName ) ;
if ( filters . startDate ) params . append ( 'startDate' , filters . startDate ) ;
if ( filters . endDate ) params . append ( 'endDate' , filters . endDate ) ;
if ( filters . page !== undefined ) params . append ( 'page' , filters . page ) ;
if ( filters . size ) params . append ( 'size' , filters . size ) ;
if ( filters . sort ) params . append ( 'sort' , filters . sort ) ;
const response = await fetch ( ` ${ this . baseUrl } /api/items? ${ params } ` ) ;
return response . json ( ) ;
}
// Получить статистику
async getStats ( ) {
const response = await fetch ( ` ${ this . baseUrl } /api/items/stats ` ) ;
return response . json ( ) ;
}
// Получить источники
async getSources ( ) {
const response = await fetch ( ` ${ this . baseUrl } /api/items/sources ` ) ;
return response . json ( ) ;
}
// Запустить парсер (админ функция)
async runParser ( sourceName ) {
const response = await fetch (
` ${ this . baseUrl } /api/admin/parsers/parse/ ${ sourceName } ` ,
{
method : 'POST' ,
}
) ;
return response . json ( ) ;
}
// Запустить все парсеры (админ функция)
async runAllParsers ( ) {
const response = await fetch (
` ${ this . baseUrl } /api/admin/parsers/parse/all ` ,
{
method : 'POST' ,
}
) ;
return response . json ( ) ;
}
// Проверить состояние системы
async getHealthStatus ( ) {
const response = await fetch ( ` ${ this . baseUrl } /api/health ` ) ;
return response . json ( ) ;
}
}
// Использование
const apiService = new NewsApiService ( ) ;
// Получить последние новости
const news = await apiService . getNews ( {
page : 0 ,
size : 20 ,
sort : 'publishedAt,desc' ,
} ) ;
// Получить новости от конкретного источника
const kursivNews = await apiService . getNews ( {
sourceName : 'Kursiv Media' ,
page : 0 ,
size : 10 ,
} ) ;
// Получить новости за последнюю неделю
const weekAgo = new Date ( Date . now ( ) - 7 * 24 * 60 * 60 * 1000 ) . toISOString ( ) ;
const recentNews = await apiService . getNews ( {
startDate : weekAgo ,
sort : 'publishedAt,desc' ,
} ) ;
```
### Vue.js примеры
``` javascript
// composable для работы с API
export function useNewsApi ( ) {
const baseUrl = '' ;
const getNews = async ( filters = { } ) => {
const params = new URLSearchParams ( ) ;
Object . entries ( filters ) . forEach ( ( [ key , value ] ) => {
if ( value !== undefined && value !== null ) {
params . append ( key , value ) ;
}
} ) ;
const response = await fetch ( ` ${ baseUrl } /api/items? ${ params } ` ) ;
return response . json ( ) ;
} ;
const getStats = async ( ) => {
const response = await fetch ( ` ${ baseUrl } /api/items/stats ` ) ;
return response . json ( ) ;
} ;
const getSources = async ( ) => {
const response = await fetch ( ` ${ baseUrl } /api/items/sources ` ) ;
return response . json ( ) ;
} ;
return {
getNews ,
getStats ,
getSources ,
} ;
}
```
---
## 📝 Важные замечания
1. **Пагинация ** : Все эндпоинты для получения данных поддерживают пагинацию через параметры `page` и `size` .
2. **Сортировка ** : По умолчанию новости сортируются по дате публикации (новые сначала). Можно изменить через параметр `sort` .
3. **Фильтрация ** : Поддерживается фильтрация по источнику и диапазону дат.
4. **Формат дат ** : Используйте ISO 8601 формат для дат (например: "2024-01-15T10:30:00").
5. **Обработка ошибок ** : Все ответы содержат поле `success` для проверки успешности операции.
6. **Административные функции ** : Эндпоинты `/api/admin/parsers/*` предназначены для административных функций.
7. **Мониторинг ** : Используйте эндпоинты `/api/health/*` для проверки состояния системы.
---
## 🔄 Миграция с старого API
Если у вас был код, использующий старый `ParserController` , вот соответствие эндпоинтов:
| Старый эндпоинт | Новый эндпоинт |
| ---------------------------------- | ----------------------------------------- |
| `POST /api/parser/parse/kursiv` | `POST /api/admin/parsers/parse/kursiv` |
| `POST /api/parser/parse/kapital` | `POST /api/admin/parsers/parse/kapital` |
| `POST /api/parser/parse/lsm` | `POST /api/admin/parsers/parse/lsm` |
| `POST /api/parser/parse/rbc` | `POST /api/admin/parsers/parse/rbc` |
| `POST /api/parser/parse/vedomosti` | `POST /api/admin/parsers/parse/vedomosti` |
| `POST /api/parser/parse/all` | `POST /api/admin/parsers/parse/all` |
| `GET /api/parser/items` | `GET /api/items` |
| `GET /api/parser/stats` | `GET /api/items/stats` |
| `GET /api/parser/health` | `GET /api/health` |
| `GET /api/parser/health/mongodb` | `GET /api/health/mongodb` |
| `GET /api/parser/scheduler/info` | `GET /api/health/scheduler` |