Files
scan-sfera/scan-sphera-main/src/app/api/history/route.ts
2025-07-19 17:56:06 +03:00

228 lines
8.8 KiB
TypeScript
Raw 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.

import { NextRequest, NextResponse } from 'next/server';
import { HistoryRecord } from '@/types';
import prisma from '../../lib/prisma';
import { CITIES } from '@/types';
// Функция для форматирования даты в формат ДД.ММ.ГГГГ
function formatDate(date: Date): string {
return date.toLocaleDateString('ru-RU');
}
// GET запрос для получения истории поисков
export async function GET(request: NextRequest) {
try {
// Получение параметров запроса
const { searchParams } = new URL(request.url);
const articleId = searchParams.get('articleId');
const limit = searchParams.get('limit')
? parseInt(searchParams.get('limit') as string)
: 10; // Увеличиваем лимит для лучшего отображения истории
const query = searchParams.get('query'); // Добавляем параметр фильтрации по поисковому запросу
try {
// Получаем данные из БД через Prisma
const searchQueries = await prisma.searchQuery.findMany({
orderBy: {
createdAt: 'desc',
},
take: limit * 2, // Получаем больше записей, чтобы после фильтрации осталось достаточно
include: {
products: true,
positions: {
include: {
product: true,
},
},
},
});
// Фильтруем запросы
let filteredQueries = searchQueries;
// Фильтруем по артикулу, если указан
if (articleId) {
filteredQueries = filteredQueries.filter((q) =>
q.products.some((product) => product.article === articleId)
);
}
// Фильтруем по поисковому запросу, если указан
if (query) {
filteredQueries = filteredQueries.filter(
(q) => q.query.toLowerCase() === query.toLowerCase()
);
// Если указан и артикул, и запрос - строго фильтруем по обоим параметрам
if (articleId) {
// Получаем самый последний запрос с этими параметрами
filteredQueries = filteredQueries.slice(0, 1);
}
}
// Сортируем по дате создания (сначала новые)
filteredQueries = filteredQueries.sort(
(a, b) =>
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
);
// Ограничиваем количество результатов
filteredQueries = filteredQueries.slice(0, limit);
// Преобразуем данные в формат HistoryRecord
const historyRecords: HistoryRecord[] = filteredQueries.map((query) => {
// Находим товары
const myProduct = query.products.find((p) => !p.isCompetitor);
const competitorProduct = query.products.find((p) => p.isCompetitor);
// Формируем список позиций
const positions = CITIES.map((city) => {
// Находим позиции для текущего города (мои)
const myPositionData = query.positions.find(
(p) => p.city === city && p.product.isCompetitor === false
);
// Находим позиции конкурента для текущего города
const competitorPositionData = query.positions.find(
(p) => p.city === city && p.product.isCompetitor === true
);
// Определяем позицию на странице и номер страницы для моих позиций
const position = myPositionData?.position || 0;
const page = myPositionData?.page || 0;
// Вычисляем позицию на странице (примерно 30 товаров на страницу)
const rank = position ? ((position - 1) % 30) + 1 : 0;
const pageRank = page || Math.ceil(position / 30);
// Определяем позицию на странице и номер страницы для позиций конкурента
const competitorPosition = competitorPositionData?.position || 0;
const competitorPage = competitorPositionData?.page || 0;
const competitorRank = competitorPosition ? ((competitorPosition - 1) % 30) + 1 : 0;
const competitorPageRank = competitorPage || Math.ceil(competitorPosition / 30);
return {
city,
rank,
pageRank,
competitorRank: competitorRank > 0 ? competitorRank : undefined,
competitorPageRank:
competitorPageRank > 0 ? competitorPageRank : undefined,
};
}).filter((pos) => pos.rank > 0); // Оставляем только найденные позиции
return {
id: query.id.toString(),
date: formatDate(query.createdAt),
query: query.query,
myArticleId: myProduct?.article || '',
competitorArticleId: competitorProduct?.article || '',
positions,
hasCompetitor: !!competitorProduct?.article, // Добавляем флаг наличия конкурента
};
});
if (historyRecords.length === 0) {
// Если нет данных, возвращаем пустой массив
return NextResponse.json([]);
}
return NextResponse.json(historyRecords);
} catch (dbError) {
console.error('Ошибка при запросе к БД:', dbError);
// В случае ошибки БД возвращаем пустой массив
return NextResponse.json(
{ error: 'Ошибка при получении данных из БД' },
{ status: 500 }
);
}
} catch (error) {
console.error('Ошибка при получении истории:', error);
return NextResponse.json(
{ error: 'Произошла ошибка при обработке запроса' },
{ status: 500 }
);
}
}
// POST запрос для создания новой записи в истории
export async function POST(request: NextRequest) {
try {
const body = await request.json();
// Проверка обязательных полей
if (
!body.query ||
!body.myArticleId ||
!body.competitorArticleId ||
!body.positions
) {
return NextResponse.json(
{ error: 'Не указаны все обязательные поля' },
{ status: 400 }
);
}
try {
// Сохранение в базу данных через Prisma
const newQuery = await prisma.searchQuery.create({
data: {
query: body.query,
products: {
create: [
{ article: body.myArticleId, isCompetitor: false },
{ article: body.competitorArticleId, isCompetitor: true },
],
},
positions: {
createMany: {
data: body.positions.map(
(pos: {
city: string;
rank: number;
pageRank: number;
isCompetitor?: boolean;
}) => ({
city: pos.city,
position: pos.rank,
page: pos.pageRank,
productId: pos.isCompetitor
? body.competitorArticleId
: body.myArticleId,
})
),
},
},
},
include: {
products: true,
positions: true,
},
});
// Формируем ответ
const response: HistoryRecord = {
id: newQuery.id.toString(),
date: formatDate(newQuery.createdAt),
query: newQuery.query,
myArticleId: body.myArticleId,
competitorArticleId: body.competitorArticleId,
positions: body.positions,
hasCompetitor: !!body.competitorArticleId, // Добавляем флаг наличия конкурента
};
return NextResponse.json(response, { status: 201 });
} catch (dbError) {
console.error('Ошибка при сохранении в БД:', dbError);
return NextResponse.json(
{ error: 'Ошибка при сохранении данных в базу' },
{ status: 500 }
);
}
} catch (error) {
console.error('Ошибка при создании записи в истории:', error);
return NextResponse.json(
{ error: 'Произошла ошибка при обработке запроса' },
{ status: 500 }
);
}
}