Files
mkd/backend/reviewApiService.js
2026-02-04 00:17:04 +05:00

140 lines
5.4 KiB
JavaScript
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Сервис загрузки отзывов через 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: рейтинг 05, количество отзывов).
* Полный список текстов отзывов — по отдельному согласованию с 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
};