# Руководство по API для фронтенда ## Обзор изменений После рефакторинга старый `ParserController` был разделен на три специализированных контроллера для лучшей организации и масштабируемости: - **`MarketItemController`** (`/api/items`) - для получения данных - **`ParserAdminController`** (`/api/admin/parsers`) - для управления парсерами - **`HealthCheckController`** (`/api/health`) - для мониторинга системы --- ## 📊 MarketItemController - Получение данных **Базовый URL**: `/api/items` ### 1. Получение списка новостей с фильтрацией и пагинацией ```http GET /api/items ``` **Параметры запроса:** - `sourceName` (optional) - фильтр по источнику (например: "Kursiv Media", "Kapital.kz", "LSM.kz", "РБК", "Ведомости") - `startDate` (optional) - начальная дата в формате ISO (например: "2024-01-01T00:00:00") - `endDate` (optional) - конечная дата в формате ISO (например: "2024-12-31T23:59:59") - `page` (optional) - номер страницы (по умолчанию: 0) - `size` (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'); ``` **Ответ:** ```json { "success": true, "message": "Записи получены успешно", "data": { "content": [ { "id": "64f1a2b3c4d5e6f7g8h9i0j1", "sourceName": "Kursiv Media", "url": "https://kursiv.media/news/example", "title": "Заголовок новости", "publishedAt": "2024-01-15T10:30:00", "addedAt": "2024-01-15T10:35:00", "rawText": "Текст новости без HTML тегов", "hash": "abc123def456...", "category": "Бизнес", "analytics": { "summary": null, "sentiment": null, "tags": [], "entities": {} } } ], "pageable": { "sort": { "sorted": true, "unsorted": false }, "pageNumber": 0, "pageSize": 20, "offset": 0, "paged": true, "unpaged": false }, "totalElements": 150, "totalPages": 8, "last": false, "first": true, "numberOfElements": 20, "size": 20, "number": 0, "empty": false } } ``` ### 2. Получение новости по ID ```http GET /api/items/{id} ``` **Пример:** ```javascript fetch('/api/items/64f1a2b3c4d5e6f7g8h9i0j1'); ``` **Ответ:** ```json { "success": false, "message": "Функция поиска по ID пока не реализована" } ``` ### 3. Получение статистики ```http GET /api/items/stats ``` **Ответ:** ```json { "success": true, "message": "Статистика получена успешно", "data": { "totalItems": 1250, "sourceStatistics": { "Kursiv Media": 300, "Kapital.kz": 250, "LSM.kz": 200, "РБК": 300, "Ведомости": 200 }, "lastUpdate": "2024-01-15T10:35:00" } } ``` ### 4. Получение списка источников (sourceName) ```http GET /api/items/sources ``` **Описание:** Возвращает список всех уникальных источников новостей, которые есть в базе данных. Этот эндпоинт полезен для: - Построения фильтров по источникам - Отображения списка доступных источников в UI - Динамического создания выпадающих списков **Ответ:** ```json { "success": true, "message": "Источники получены успешно", "data": ["Kursiv (Бизнес/экономика)", "Kapital.kz (Бизнес)", "LSM.kz", "РБК", "Ведомости"] } ``` **Примеры использования:** ```javascript // Получить список всех источников const response = await fetch('/api/items/sources'); const result = await response.json(); if (result.success) { const sources = result.data; console.log('Доступные источники:', sources); // sources = ["Kursiv (Бизнес/экономика)", "Kapital.kz (Бизнес)", "LSM.kz", "РБК", "Ведомости"] } // Использование в React для создания фильтра function SourceFilter({ onSourceChange }) { const [sources, setSources] = useState([]); useEffect(() => { fetch('/api/items/sources') .then((res) => res.json()) .then((data) => { if (data.success) { setSources(data.data); } }); }, []); return ( ); } // Использование в Vue.js export default { data() { return { sources: [], selectedSource: '' }; }, async mounted() { const response = await fetch('/api/items/sources'); const result = await response.json(); if (result.success) { this.sources = result.data; } } }; ``` **Важные замечания:** - Список источников формируется на основе реальных данных в базе - Если в базе нет данных от какого-то источника, он не будет включен в список - Названия источников точно соответствуют тем, что используются в поле `sourceName` при фильтрации - Порядок источников может изменяться в зависимости от того, как MongoDB возвращает уникальные значения ### 5. Получение новостей по конкретному источнику ```http GET /api/items/source/{sourceName} ``` **Пример:** ```javascript fetch('/api/items/source/Kursiv Media?page=0&size=10'); ``` **Ответ:** Аналогичен ответу от `/api/items`, но только с новостями от указанного источника. --- ## ⚙️ ParserAdminController - Управление парсерами **Базовый URL**: `/api/admin/parsers` > ⚠️ **Важно**: Эти эндпоинты предназначены для административных функций и могут потребовать аутентификации в будущем. ### 1. Запуск конкретного парсера ```http POST /api/admin/parsers/parse/{sourceName} ``` **Доступные источники:** - `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 POST /api/admin/parsers/parse/all ``` **Пример:** ```javascript fetch('/api/admin/parsers/parse/all', { method: 'POST' }); ``` **Ответ:** ```json { "success": true, "message": "Парсинг всех источников завершен", "data": [ { "source": "kursiv", "processedItems": 12, "totalItemsInDb": 1277, "status": "completed" }, { "source": "kapital", "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 GET /api/admin/parsers ``` **Ответ:** ```json { "success": true, "message": "Список парсеров получен", "data": ["kapital", "kursiv", "lsm", "rbc", "vedomosti"] } ``` ### 4. Получение информации о парсерах ```http GET /api/admin/parsers/info ``` **Ответ:** ```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 GET /api/admin/parsers/exists/{sourceName} ``` **Пример:** ```javascript fetch('/api/admin/parsers/exists/kursiv'); ``` **Ответ:** ```json { "success": true, "message": "Проверка завершена", "data": true } ``` --- ## 🔍 HealthCheckController - Мониторинг системы **Базовый URL**: `/api/health` ### 1. Базовая проверка состояния ```http GET /api/health ``` **Ответ:** ```json { "success": true, "message": "Сервис работает", "data": { "status": "UP", "service": "RSS Parser System", "timestamp": 1705312500000, "details": "Enabled - runs every 30 minutes" } } ``` ### 2. Проверка подключения к MongoDB ```http GET /api/health/mongodb ``` **Ответ (успех):** ```json { "success": true, "message": "MongoDB подключение успешно", "data": { "status": "UP", "service": "MongoDB подключение успешно", "timestamp": 1705312500000, "details": "Всего записей: 1317" } } ``` **Ответ (ошибка):** ```json { "success": false, "message": "Ошибка подключения к MongoDB", "data": { "status": "DOWN", "service": "Ошибка подключения к MongoDB: Connection refused", "timestamp": 1705312500000, "suggestion": "Проверьте учетные данные в application.properties" } } ``` ### 3. Информация о планировщике ```http GET /api/health/scheduler ``` **Ответ:** ```json { "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", "РБК", "Ведомости"] } } ``` ### 4. Общая информация о системе ```http GET /api/health/system ``` **Ответ:** ```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 GET /api/health/parsers ``` **Ответ:** ```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 getSourcesWithErrorHandling() { try { const response = await fetch(`${this.baseUrl}/api/items/sources`); const result = await response.json(); if (!result.success) { throw new Error(result.message || 'Ошибка при получении источников'); } return result.data; // Возвращаем только массив источников } catch (error) { console.error('Ошибка при получении источников:', error); return []; // Возвращаем пустой массив в случае ошибки } } // Запустить парсер (админ функция) 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' }); // Получить список источников для фильтра const sources = await apiService.getSourcesWithErrorHandling(); console.log('Доступные источники:', sources); // Получить новости от конкретного источника if (sources.length > 0) { const newsFromFirstSource = await apiService.getNews({ sourceName: sources[0], page: 0, size: 10 }); } ``` ### 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(); }; const getSourcesList = async () => { try { const response = await fetch(`${baseUrl}/api/items/sources`); const result = await response.json(); return result.success ? result.data : []; } catch (error) { console.error('Ошибка при получении источников:', error); return []; } }; return { getNews, getStats, getSources, getSourcesList }; } ``` --- ## 🎯 Практические примеры использования эндпоинта источников ### 1. Создание динамического фильтра источников ```javascript // React компонент для фильтрации по источникам import React, { useState, useEffect } from 'react'; function NewsFilter({ onFilterChange }) { const [sources, setSources] = useState([]); const [selectedSource, setSelectedSource] = useState(''); useEffect(() => { // Загружаем список источников при монтировании компонента fetch('/api/items/sources') .then((res) => res.json()) .then((data) => { if (data.success) { setSources(data.data); } }) .catch((err) => console.error('Ошибка загрузки источников:', err)); }, []); const handleSourceChange = (source) => { setSelectedSource(source); onFilterChange({ sourceName: source || undefined }); }; return (