140 lines
5.4 KiB
JavaScript
140 lines
5.4 KiB
JavaScript
|
|
/**
|
|||
|
|
* Сервис загрузки отзывов через API (2ГИС, Яндекс).
|
|||
|
|
* Без парсинга страниц — только HTTP-запросы к API.
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
let axios;
|
|||
|
|
try {
|
|||
|
|
axios = require('axios');
|
|||
|
|
} catch (err) {
|
|||
|
|
console.warn('[reviewApiService] axios не установлен. Установите: npm install axios');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const API_2GIS_BASE = 'https://catalog.api.2gis.com/3.0/items';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Извлечь firm_id из url_template или settings.
|
|||
|
|
* @param {string} urlTemplate - URL вида https://2gis.ru/ufa/firm/2393065583658695/tab/reviews
|
|||
|
|
* @param {object} settings - объект settings из parsing_settings (может содержать firm_id)
|
|||
|
|
* @returns {string|null}
|
|||
|
|
*/
|
|||
|
|
function extractFirmId(urlTemplate, settings) {
|
|||
|
|
if (settings && settings.firm_id) {
|
|||
|
|
return String(settings.firm_id).trim();
|
|||
|
|
}
|
|||
|
|
if (urlTemplate && typeof urlTemplate === 'string') {
|
|||
|
|
const match = urlTemplate.match(/\/firm\/(\d+)(?:\/|$)/);
|
|||
|
|
if (match) return match[1];
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Загрузка отзывов из API 2ГИС.
|
|||
|
|
* По документации доступна сводка (reviews_summary: рейтинг 0–5, количество отзывов).
|
|||
|
|
* Полный список текстов отзывов — по отдельному согласованию с 2ГИС.
|
|||
|
|
* @param {object} settings - запись из parsing_settings (url_template, api_key, settings)
|
|||
|
|
* @returns {Promise<Array<{ author_name, text, rating, date, source_url, building_id }>>}
|
|||
|
|
*/
|
|||
|
|
async function fetchFrom2GIS(settings) {
|
|||
|
|
const reviews = [];
|
|||
|
|
if (!axios) {
|
|||
|
|
console.warn('[reviewApiService] axios недоступен');
|
|||
|
|
return reviews;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const firmId = extractFirmId(settings.url_template, settings.settings);
|
|||
|
|
if (!firmId) {
|
|||
|
|
console.warn('[reviewApiService] 2ГИС: не удалось извлечь firm_id из URL или settings');
|
|||
|
|
return reviews;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const apiKey = (settings.api_key || '').trim();
|
|||
|
|
if (!apiKey) {
|
|||
|
|
console.warn('[reviewApiService] 2ГИС: api_key не задан');
|
|||
|
|
return reviews;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const urlTemplate = (settings.url_template || '').trim() || `https://2gis.ru/firm/${firmId}`;
|
|||
|
|
const buildingId = (settings.settings && settings.settings.building_id) || null;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const url = `${API_2GIS_BASE}?id=${encodeURIComponent(firmId)}&key=${encodeURIComponent(apiKey)}&fields=items.reviews_summary,items.address,items.name&locale=ru_RU`;
|
|||
|
|
const response = await axios.get(url, { timeout: 15000 });
|
|||
|
|
|
|||
|
|
if (response.status !== 200) {
|
|||
|
|
console.warn('[reviewApiService] 2ГИС: ответ', response.status);
|
|||
|
|
return reviews;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const data = response.data;
|
|||
|
|
const items = (data && data.result && data.result.items) || [];
|
|||
|
|
if (items.length === 0) {
|
|||
|
|
console.warn('[reviewApiService] 2ГИС: объект не найден или пустой items');
|
|||
|
|
return reviews;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const item = items[0];
|
|||
|
|
const summary = item.reviews_summary;
|
|||
|
|
const name = item.name || 'Организация';
|
|||
|
|
const addressName = (item.address && item.address.name) || '';
|
|||
|
|
|
|||
|
|
if (summary != null) {
|
|||
|
|
const rating5 = typeof summary.rating === 'number' ? summary.rating : (summary.general_rating ?? 0);
|
|||
|
|
const count = typeof summary.general_review_count === 'number' ? summary.general_review_count : (summary.review_count ?? 0);
|
|||
|
|
const rating10 = Math.max(1, Math.min(10, Math.round(rating5 * 2)));
|
|||
|
|
const today = new Date().toISOString().split('T')[0];
|
|||
|
|
|
|||
|
|
reviews.push({
|
|||
|
|
author_name: '2ГИС',
|
|||
|
|
text: `Сводка 2ГИС: рейтинг ${rating5.toFixed(1)}, отзывов ${count}. ${name}${addressName ? `, ${addressName}` : ''}`.substring(0, 5000),
|
|||
|
|
rating: rating10,
|
|||
|
|
date: today,
|
|||
|
|
source_url: urlTemplate,
|
|||
|
|
building_id: buildingId
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`[reviewApiService] 2ГИС: загружено записей: ${reviews.length}`);
|
|||
|
|
} catch (err) {
|
|||
|
|
if (err.response) {
|
|||
|
|
console.warn('[reviewApiService] 2ГИС API:', err.response.status, err.response.data && (err.response.data.message || err.response.data.error_message || ''));
|
|||
|
|
} else {
|
|||
|
|
console.warn('[reviewApiService] 2ГИС:', err.message);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return reviews;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Загрузка отзывов из API Яндекса.
|
|||
|
|
* Публичного API отзывов организаций нет — возвращаем пустой массив.
|
|||
|
|
*/
|
|||
|
|
async function fetchFromYandex() {
|
|||
|
|
console.log('[reviewApiService] Яндекс: отзывы через API недоступны. Используйте 2ГИС или виджет.');
|
|||
|
|
return [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Загрузить отзывы из API для указанного источника.
|
|||
|
|
* @param {string} source - '2gis' | 'yandex_maps'
|
|||
|
|
* @param {object} settings - запись из parsing_settings (url_template, api_key, settings)
|
|||
|
|
* @returns {Promise<Array<{ author_name, text, rating, date, source_url, building_id }>>}
|
|||
|
|
*/
|
|||
|
|
async function fetchReviewsFromApi(source, settings) {
|
|||
|
|
if (source === '2gis') {
|
|||
|
|
return fetchFrom2GIS(settings);
|
|||
|
|
}
|
|||
|
|
if (source === 'yandex_maps') {
|
|||
|
|
return fetchFromYandex(settings);
|
|||
|
|
}
|
|||
|
|
console.warn('[reviewApiService] Неизвестный источник:', source);
|
|||
|
|
return [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = {
|
|||
|
|
fetchReviewsFromApi,
|
|||
|
|
extractFirmId
|
|||
|
|
};
|