191 lines
8.0 KiB
TypeScript
191 lines
8.0 KiB
TypeScript
|
|
import { DomaApplication, DomaApplicationStatus } from '../types';
|
|||
|
|
import { backendApi } from './apiClient';
|
|||
|
|
import { domaGraphQLClient, DomaTicket } from './domaGraphQLClient';
|
|||
|
|
import { settingsService } from './settingsService';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Сервис для работы с Doma AI API
|
|||
|
|
* Интегрирует GraphQL API Doma AI для получения заявок
|
|||
|
|
*/
|
|||
|
|
export const domaService = {
|
|||
|
|
/**
|
|||
|
|
* Инициализация подключения к Doma AI
|
|||
|
|
* Выполняет аутентификацию, если токен не сохранен
|
|||
|
|
* Использует настройки из localStorage или переменные окружения как fallback
|
|||
|
|
*/
|
|||
|
|
async initialize(): Promise<boolean> {
|
|||
|
|
// Обновляем настройки в клиенте
|
|||
|
|
domaGraphQLClient.updateSettings();
|
|||
|
|
|
|||
|
|
// Получаем настройки из localStorage
|
|||
|
|
const settings = settingsService.getDomaAISettings();
|
|||
|
|
|
|||
|
|
// Проверяем, настроен ли URL API (из настроек или переменных окружения)
|
|||
|
|
const apiUrl = settings?.apiUrl || import.meta.env.VITE_DOMA_AI_API_URL;
|
|||
|
|
if (!apiUrl) {
|
|||
|
|
console.warn(
|
|||
|
|
'[domaService] URL API Doma AI не настроен. ' +
|
|||
|
|
'Укажите URL в настройках интеграций или в переменных окружения.'
|
|||
|
|
);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Если в настройках уже есть токен, просто используем его (без health-check запроса)
|
|||
|
|
if (settings?.token) {
|
|||
|
|
domaGraphQLClient.setToken(settings.token);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Если токен уже есть в клиенте, считаем, что авторизация настроена
|
|||
|
|
if (domaGraphQLClient.hasToken()) {
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Пытаемся получить учетные данные из настроек или переменных окружения
|
|||
|
|
const email = settings?.email || import.meta.env.VITE_DOMA_AI_EMAIL;
|
|||
|
|
const password = settings?.password || import.meta.env.VITE_DOMA_AI_PASSWORD;
|
|||
|
|
const phone = settings?.phone || import.meta.env.VITE_DOMA_AI_PHONE;
|
|||
|
|
|
|||
|
|
if (!email && !phone) {
|
|||
|
|
console.warn(
|
|||
|
|
'[domaService] Учетные данные Doma AI не настроены. ' +
|
|||
|
|
'Укажите email/телефон и пароль в настройках интеграций или в переменных окружения.'
|
|||
|
|
);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!password) {
|
|||
|
|
console.warn('[domaService] Пароль Doma AI не настроен. Укажите пароль в настройках интеграций.');
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
if (email && password) {
|
|||
|
|
await domaGraphQLClient.authenticate(email, password);
|
|||
|
|
} else if (phone && password) {
|
|||
|
|
await domaGraphQLClient.authenticateWithPhone(phone, password);
|
|||
|
|
} else {
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
return true;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('[domaService] Ошибка авторизации в Doma AI:', error);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Преобразует заявку из формата Doma AI в формат приложения
|
|||
|
|
*/
|
|||
|
|
mapDomaTicketToApplication(ticket: DomaTicket): DomaApplication {
|
|||
|
|
// Маппинг статусов Doma AI в статусы приложения
|
|||
|
|
const statusMap: Record<string, DomaApplicationStatus> = {
|
|||
|
|
'new': 'new',
|
|||
|
|
'in_progress': 'in_progress',
|
|||
|
|
'inProgress': 'in_progress',
|
|||
|
|
'done': 'done',
|
|||
|
|
'completed': 'done',
|
|||
|
|
'canceled': 'canceled',
|
|||
|
|
'cancelled': 'canceled',
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const statusType = ticket.status?.type?.toLowerCase() || ticket.status?.name?.toLowerCase() || 'new';
|
|||
|
|
const mappedStatus = statusMap[statusType] || 'new';
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
id: parseInt(ticket.id) || Date.now(), // Если ID не число, используем timestamp
|
|||
|
|
number: ticket.number || ticket.id,
|
|||
|
|
status: mappedStatus,
|
|||
|
|
description: ticket.description || ticket.details || 'Без описания',
|
|||
|
|
address: ticket.property?.address || 'Адрес не указан',
|
|||
|
|
apartment: ticket.property?.unitName || '—',
|
|||
|
|
clientName: ticket.client?.name || 'Клиент не указан',
|
|||
|
|
createdAt: ticket.createdAt || new Date().toISOString(),
|
|||
|
|
deadlineAt: ticket.deadline || new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(), // По умолчанию +7 дней
|
|||
|
|
performer: ticket.assignee ? { name: ticket.assignee.name } : undefined,
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Получает список заявок из Doma AI
|
|||
|
|
* @param filters - Фильтры для запроса заявок
|
|||
|
|
* @returns Массив заявок в формате приложения
|
|||
|
|
*/
|
|||
|
|
async getApplications(filters?: {
|
|||
|
|
status?: DomaApplicationStatus;
|
|||
|
|
limit?: number;
|
|||
|
|
}): Promise<DomaApplication[]> {
|
|||
|
|
console.log('[domaService] Получение заявок из Doma AI...');
|
|||
|
|
|
|||
|
|
// Проверяем и инициализируем подключение, если нужно
|
|||
|
|
if (!domaGraphQLClient.hasToken()) {
|
|||
|
|
const initialized = await this.initialize();
|
|||
|
|
if (!initialized) {
|
|||
|
|
console.warn('[domaService] Не удалось подключиться к Doma AI, используем fallback');
|
|||
|
|
return this.getApplicationsFallback();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// Преобразуем фильтры для Doma AI
|
|||
|
|
const domaFilters: any = {};
|
|||
|
|
if (filters?.status) {
|
|||
|
|
// Маппинг статусов обратно (упрощенный вариант)
|
|||
|
|
// В реальности нужно знать ID статусов в Doma AI
|
|||
|
|
domaFilters.status = filters.status;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получаем заявки из Doma AI
|
|||
|
|
const tickets = await domaGraphQLClient.getTickets({
|
|||
|
|
...domaFilters,
|
|||
|
|
limit: filters?.limit || 100,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Преобразуем в формат приложения
|
|||
|
|
const applications = tickets.map(ticket => this.mapDomaTicketToApplication(ticket));
|
|||
|
|
|
|||
|
|
console.log(`[domaService] Получено ${applications.length} заявок из Doma AI`);
|
|||
|
|
return applications;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('[domaService] Ошибка при получении заявок из Doma AI:', error);
|
|||
|
|
|
|||
|
|
// Fallback: пробуем получить из бэкенда
|
|||
|
|
return this.getApplicationsFallback();
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Fallback метод: получает заявки из бэкенда
|
|||
|
|
* Важно: демо-мок-данные больше не используются, чтобы не путать с реальной интеграцией
|
|||
|
|
*/
|
|||
|
|
async getApplicationsFallback(): Promise<DomaApplication[]> {
|
|||
|
|
try {
|
|||
|
|
console.log('[domaService] Попытка получить заявки из бэкенда...');
|
|||
|
|
const apps = await backendApi.getApplications();
|
|||
|
|
return apps;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.warn('[domaService] Бэкенд недоступен, возвращаем пустой список заявок:', error);
|
|||
|
|
return [];
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Обновление статуса заявки в Doma AI
|
|||
|
|
* @param ticketId - ID заявки в Doma AI
|
|||
|
|
* @param status - Новый статус
|
|||
|
|
*/
|
|||
|
|
async updateApplicationStatus(ticketId: string, status: DomaApplicationStatus): Promise<boolean> {
|
|||
|
|
if (!domaGraphQLClient.hasToken()) {
|
|||
|
|
const initialized = await this.initialize();
|
|||
|
|
if (!initialized) {
|
|||
|
|
throw new Error('Не удалось авторизоваться в Doma AI');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TODO: Реализовать мутацию для обновления статуса заявки
|
|||
|
|
// Это зависит от схемы GraphQL в Doma AI
|
|||
|
|
console.warn('[domaService] Обновление статуса заявки в Doma AI пока не реализовано');
|
|||
|
|
return false;
|
|||
|
|
},
|
|||
|
|
};
|