Добавлены новые зависимости для работы с графиками и статистикой: интегрирован пакет recharts для визуализации данных. Обновлены компоненты бизнес-демо и сайдбара, добавлены новые функции для отображения информации о поставках и статистике. Улучшена структура кода и взаимодействие с пользователем. Обновлены GraphQL резолверы для получения статистики Wildberries.

This commit is contained in:
Bivekich
2025-07-22 13:29:15 +03:00
parent 20c27a2fa2
commit a62a09faca
13 changed files with 2388 additions and 122 deletions

View File

@ -6,6 +6,7 @@ import { prisma } from "@/lib/prisma";
import { SmsService } from "@/services/sms-service";
import { DaDataService } from "@/services/dadata-service";
import { MarketplaceService } from "@/services/marketplace-service";
import { WildberriesService } from "@/services/wildberries-service";
import { Prisma } from "@prisma/client";
// Сервисы
@ -4942,10 +4943,163 @@ const adminMutations = {
},
};
// Wildberries статистика
const wildberriesQueries = {
debugWildberriesAdverts: async (
_: unknown,
__: unknown,
context: Context
) => {
if (!context.user) {
throw new GraphQLError("Требуется авторизация", {
extensions: { code: "UNAUTHENTICATED" },
});
}
try {
const user = await prisma.user.findUnique({
where: { id: context.user.id },
include: {
organization: {
include: {
apiKeys: true,
}
},
},
});
if (!user?.organization || user.organization.type !== 'SELLER') {
throw new GraphQLError("Доступно только для продавцов");
}
const wbApiKeyRecord = user.organization.apiKeys?.find(
key => key.marketplace === 'WILDBERRIES' && key.isActive
);
if (!wbApiKeyRecord) {
throw new GraphQLError("WB API ключ не настроен");
}
const wbService = new WildberriesService(wbApiKeyRecord.apiKey);
// Получаем кампании во всех статусах
const [active, completed, paused] = await Promise.all([
wbService.getAdverts(9).catch(() => []), // активные
wbService.getAdverts(7).catch(() => []), // завершенные
wbService.getAdverts(11).catch(() => []) // на паузе
]);
const allCampaigns = [...active, ...completed, ...paused];
return {
success: true,
message: `Found ${active.length} active, ${completed.length} completed, ${paused.length} paused campaigns`,
campaignsCount: allCampaigns.length,
campaigns: allCampaigns.map(c => ({
id: c.advertId,
name: c.name,
status: c.status,
type: c.type
}))
};
} catch (error) {
console.error('Error debugging WB adverts:', error);
return {
success: false,
message: error instanceof Error ? error.message : 'Unknown error',
campaignsCount: 0,
campaigns: []
};
}
},
getWildberriesStatistics: async (
_: unknown,
{ period, startDate, endDate }: {
period?: 'week' | 'month' | 'quarter'
startDate?: string
endDate?: string
},
context: Context
) => {
if (!context.user) {
throw new GraphQLError("Требуется авторизация", {
extensions: { code: "UNAUTHENTICATED" },
});
}
try {
// Получаем организацию пользователя и её WB API ключ
const user = await prisma.user.findUnique({
where: { id: context.user.id },
include: {
organization: {
include: {
apiKeys: true,
}
},
},
});
if (!user?.organization) {
throw new GraphQLError("Организация не найдена");
}
if (user.organization.type !== 'SELLER') {
throw new GraphQLError("Доступно только для продавцов");
}
const wbApiKeyRecord = user.organization.apiKeys?.find(
key => key.marketplace === 'WILDBERRIES' && key.isActive
);
if (!wbApiKeyRecord) {
throw new GraphQLError("WB API ключ не настроен");
}
// Создаем экземпляр сервиса
const wbService = new WildberriesService(wbApiKeyRecord.apiKey);
// Получаем даты
let dateFrom: string;
let dateTo: string;
if (startDate && endDate) {
// Используем пользовательские даты
dateFrom = startDate;
dateTo = endDate;
} else if (period) {
// Используем предустановленный период
dateFrom = WildberriesService.getDatePeriodAgo(period);
dateTo = WildberriesService.formatDate(new Date());
} else {
throw new GraphQLError("Необходимо указать либо period, либо startDate и endDate");
}
// Получаем статистику
const statistics = await wbService.getStatistics(dateFrom, dateTo);
return {
success: true,
data: statistics,
message: null
};
} catch (error) {
console.error('Error fetching WB statistics:', error);
return {
success: false,
message: error instanceof Error ? error.message : 'Ошибка получения статистики',
data: [],
};
}
},
};
// Добавляем админ запросы и мутации к основным резолверам
resolvers.Query = {
...resolvers.Query,
...adminQueries,
...wildberriesQueries,
};
resolvers.Mutation = {

View File

@ -75,6 +75,16 @@ export const typeDefs = gql`
# Админ запросы
adminMe: Admin
allUsers(search: String, limit: Int, offset: Int): UsersResponse!
# Wildberries статистика
getWildberriesStatistics(
period: String
startDate: String
endDate: String
): WildberriesStatisticsResponse!
# Отладка рекламы (временно)
debugWildberriesAdverts: DebugAdvertsResponse!
}
type Mutation {
@ -892,4 +902,36 @@ export const typeDefs = gql`
message: String!
supply: WildberriesSupply
}
# Wildberries статистика
type WildberriesStatistics {
date: String!
sales: Int!
orders: Int!
advertising: Float!
refusals: Int!
returns: Int!
revenue: Float!
buyoutPercentage: Float!
}
type WildberriesStatisticsResponse {
success: Boolean!
message: String
data: [WildberriesStatistics!]!
}
type DebugAdvertsResponse {
success: Boolean!
message: String
campaignsCount: Int!
campaigns: [DebugCampaign!]
}
type DebugCampaign {
id: Int!
name: String!
status: Int!
type: Int!
}
`;