upravlenie-service

This commit is contained in:
2026-02-25 16:00:39 +05:00
parent f4795d019e
commit 27fd3543ec

View File

@@ -146,6 +146,14 @@ class UpravlenieService {
throw new Error('Для исполнителя необходимо указать api_url организатора'); throw new Error('Для исполнителя необходимо указать api_url организатора');
} }
// Проверяем существование локального пользователя если указан
if (local_user_id) {
const userExists = await this.checkLocalUserExists(local_user_id);
if (!userExists) {
throw new Error(`Пользователь с ID ${local_user_id} не существует`);
}
}
// Проверяем уникальность service_id для активных подключений // Проверяем уникальность service_id для активных подключений
const existing = await this.getConnectionByServiceId(service_id); const existing = await this.getConnectionByServiceId(service_id);
if (existing && existing.is_active) { if (existing && existing.is_active) {
@@ -172,11 +180,6 @@ class UpravlenieService {
const connectionId = this.lastID; const connectionId = this.lastID;
console.log(`✅ Создано подключение ${service_name} (ID: ${connectionId}, service_id: ${service_id})`); console.log(`✅ Создано подключение ${service_name} (ID: ${connectionId}, service_id: ${service_id})`);
// Запускаем синхронизацию для нового подключения
if (sync_enabled) {
this.startSyncJob(connectionId, sync_interval);
}
resolve({ id: connectionId, ...data }); resolve({ id: connectionId, ...data });
} }
}); });
@@ -279,12 +282,6 @@ class UpravlenieService {
reject(err); reject(err);
} else { } else {
console.log(`✅ Подключение ${id} обновлено`); console.log(`✅ Подключение ${id} обновлено`);
// Перезапускаем задачу синхронизации если изменились настройки
if (data.sync_enabled !== undefined || data.sync_interval !== undefined) {
this.restartSyncJob(id);
}
resolve({ id, changes: this.changes }); resolve({ id, changes: this.changes });
} }
}); });
@@ -307,9 +304,9 @@ class UpravlenieService {
sync_enabled: true sync_enabled: true
}); });
connections.forEach(conn => { for (const conn of connections) {
this.startSyncJob(conn.id, conn.sync_interval); this.startSyncJob(conn.id, conn.sync_interval);
}); }
console.log(`✅ Запущено ${connections.length} задач синхронизации`); console.log(`✅ Запущено ${connections.length} задач синхронизации`);
} }
@@ -427,12 +424,23 @@ class UpravlenieService {
throw new Error('Для исполнителя не указан api_url организатора'); throw new Error('Для исполнителя не указан api_url организатора');
} }
// Проверяем наличие локального пользователя
if (!connection.local_user_id) {
console.warn(`⚠️ Для подключения ${connection.service_name} не указан локальный пользователь. Задачи не будут создаваться.`);
return;
}
// Получаем задачи, назначенные локальному пользователю // Получаем задачи, назначенные локальному пользователю
const localTasks = await this.getTasksForLocalUser(connection.local_user_id); const localTasks = await this.getTasksForLocalUser(connection.local_user_id);
// Получаем задачи от организатора // Получаем задачи от организатора
const organizerTasks = await this.fetchTasksFromOrganizer(connection); const organizerTasks = await this.fetchTasksFromOrganizer(connection);
if (organizerTasks.length === 0) {
console.log(`📭 Нет новых задач от организатора для ${connection.service_name}`);
return;
}
// Обновляем локальные задачи // Обновляем локальные задачи
await this.syncTasksWithOrganizer(connection, organizerTasks, localTasks); await this.syncTasksWithOrganizer(connection, organizerTasks, localTasks);
@@ -451,20 +459,43 @@ class UpravlenieService {
sync_enabled: true sync_enabled: true
}); });
if (executors.length === 0) {
console.log(`📭 Нет активных исполнителей для организатора ${connection.service_name}`);
return;
}
for (const executor of executors) { for (const executor of executors) {
try { try {
// Проверяем наличие локального пользователя у исполнителя
if (!executor.local_user_id) {
console.warn(`⚠️ Для исполнителя ${executor.service_name} не указан локальный пользователь. Пропускаем...`);
continue;
}
// Получаем статусы задач от исполнителя // Получаем статусы задач от исполнителя
const taskStatuses = await this.fetchTaskStatusesFromExecutor(executor); const taskStatuses = await this.fetchTaskStatusesFromExecutor(executor);
if (taskStatuses.length > 0) {
// Обновляем статусы в локальной БД // Обновляем статусы в локальной БД
await this.updateTaskStatusesFromExecutor(executor, taskStatuses); await this.updateTaskStatusesFromExecutor(executor, taskStatuses);
console.log(`✅ Получено ${taskStatuses.length} статусов от исполнителя ${executor.service_name}`);
}
} catch (error) { } catch (error) {
console.error(`❌ Ошибка получения статусов от исполнителя ${executor.service_name}:`, error.message); console.error(`❌ Ошибка получения статусов от исполнителя ${executor.service_name}:`, error.message);
} }
} }
} }
/**
* Проверка существования локального пользователя
*/
async checkLocalUserExists(userId) {
return new Promise((resolve) => {
this.db.get('SELECT id FROM users WHERE id = ?', [userId], (err, row) => {
resolve(!!row);
});
});
}
/** /**
* Получение задач для локального пользователя * Получение задач для локального пользователя
*/ */
@@ -582,29 +613,67 @@ class UpravlenieService {
} }
} }
// upravlenie-service.js (исправленная версия метода createTaskFromOrganizer)
/** /**
* Создание задачи из данных организатора * Создание задачи из данных организатора
*/ */
async createTaskFromOrganizer(connection, taskData) { async createTaskFromOrganizer(connection, taskData) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.db.serialize(() => { // Проверяем, что local_user_id не NULL
// Создаем задачу if (!connection.local_user_id) {
this.db.run( reject(new Error('local_user_id не может быть пустым. Укажите локального пользователя для создания задач.'));
`INSERT INTO tasks ( return;
title, description, status, created_by, }
external_task_id, external_service_id, start_date, due_date, task_type
) VALUES (?, ?, 'active', ?, ?, ?, ?, ?, 'external')`, // Сначала проверяем структуру таблицы tasks
[ this.db.all("PRAGMA table_info(tasks)", (err, columns) => {
if (err) {
reject(err);
return;
}
const columnNames = columns.map(c => c.name);
// Формируем SQL запрос динамически
let fields = ['title', 'description', 'status', 'created_by', 'start_date', 'due_date', 'task_type'];
let placeholders = ['?', '?', '?', '?', '?', '?', '?'];
let values = [
taskData.title, taskData.title,
taskData.description || '', taskData.description || '',
connection.local_user_id, 'active',
taskData.id, connection.local_user_id, // теперь точно не NULL
connection.service_id,
taskData.start_date || new Date().toISOString(), taskData.start_date || new Date().toISOString(),
taskData.due_date || null taskData.due_date || null,
], 'external'
function(err) { ];
// Добавляем external_task_id если колонка существует
if (columnNames.includes('external_task_id')) {
fields.push('external_task_id');
placeholders.push('?');
values.push(taskData.id);
}
// Добавляем external_service_id если колонка существует
if (columnNames.includes('external_service_id')) {
fields.push('external_service_id');
placeholders.push('?');
values.push(connection.service_id);
}
const sql = `INSERT INTO tasks (${fields.join(', ')}) VALUES (${placeholders.join(', ')})`;
console.log(`📝 Создание задачи от организатора:`, {
sql,
values,
local_user_id: connection.local_user_id,
service_id: connection.service_id
});
this.db.run(sql, values, function(err) {
if (err) { if (err) {
console.error('❌ Ошибка создания задачи:', err);
reject(err); reject(err);
return; return;
} }
@@ -624,6 +693,7 @@ class UpravlenieService {
], ],
(err) => { (err) => {
if (err) { if (err) {
console.error('❌ Ошибка назначения задачи:', err);
reject(err); reject(err);
return; return;
} }
@@ -632,12 +702,10 @@ class UpravlenieService {
resolve(newTaskId); resolve(newTaskId);
} }
); );
} });
);
}); });
}); });
} }
/** /**
* Обновление задачи из данных организатора * Обновление задачи из данных организатора
*/ */
@@ -668,29 +736,64 @@ class UpravlenieService {
this.db.run( this.db.run(
`UPDATE task_assignments `UPDATE task_assignments
SET status = ?, updated_at = CURRENT_TIMESTAMP SET status = ?, updated_at = CURRENT_TIMESTAMP
WHERE task_id = ( WHERE task_id = ? AND user_id = ?`,
SELECT id FROM tasks
WHERE external_task_id = ? AND external_service_id = ?
) AND user_id = ?`,
[ [
status.status, status.status,
status.external_task_id, status.task_id,
executor.service_id,
executor.local_user_id executor.local_user_id
], ],
function(err) { function(err) {
if (err) reject(err); if (err) reject(err);
else { else {
if (this.changes > 0) { if (this.changes > 0) {
console.log(`✅ Обновлен статус задачи ${status.external_task_id}: ${status.status}`); console.log(`✅ Обновлен статус задачи ${status.task_id}: ${status.status}`);
} }
resolve(); resolve();
} }
} }
); );
}); });
// Если статус "completed" - проверяем и закрываем задачу если все выполнили
if (status.status === 'completed') {
await this.checkAndCloseTaskIfAllCompleted(status.task_id);
} }
} }
}
/**
* Проверка и закрытие задачи если все исполнители выполнили
*/
async checkAndCloseTaskIfAllCompleted(taskId) {
return new Promise((resolve) => {
this.db.all(
'SELECT status FROM task_assignments WHERE task_id = ?',
[taskId],
(err, assignments) => {
if (!err && assignments && assignments.length > 0) {
const allCompleted = assignments.every(a => a.status === 'completed');
if (allCompleted) {
this.db.run(
'UPDATE tasks SET closed_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
[taskId],
(err) => {
if (!err) {
console.log(`✅ Задача ${taskId} автоматически закрыта`);
}
resolve();
}
);
} else {
resolve();
}
} else {
resolve();
}
}
);
});
}
/** /**
* Загрузка файла в удаленный сервис * Загрузка файла в удаленный сервис
@@ -767,7 +870,7 @@ class UpravlenieService {
// Сохраняем файл локально // Сохраняем файл локально
const { createUserTaskFolder } = require('./database'); const { createUserTaskFolder } = require('./database');
const userFolder = createUserTaskFolder(localTaskId, connection.local_user_login); const userFolder = createUserTaskFolder(localTaskId, connection.local_user_login || 'external');
const fileName = response.headers['x-file-name'] || `remote_${Date.now()}.bin`; const fileName = response.headers['x-file-name'] || `remote_${Date.now()}.bin`;
const filePath = path.join(userFolder, fileName); const filePath = path.join(userFolder, fileName);
@@ -904,7 +1007,7 @@ function setupUpravlenieEndpoints(app, db) {
// Middleware для проверки аутентификации // Middleware для проверки аутентификации
const requireAuth = (req, res, next) => { const requireAuth = (req, res, next) => {
if (!req.session.user) { if (!req.session || !req.session.user) {
return res.status(401).json({ error: 'Требуется аутентификация' }); return res.status(401).json({ error: 'Требуется аутентификация' });
} }
next(); next();
@@ -912,7 +1015,10 @@ function setupUpravlenieEndpoints(app, db) {
// Middleware для проверки прав администратора // Middleware для проверки прав администратора
const requireAdmin = (req, res, next) => { const requireAdmin = (req, res, next) => {
if (!req.session.user || req.session.user.role !== 'admin') { if (!req.session || !req.session.user) {
return res.status(401).json({ error: 'Требуется аутентификация' });
}
if (req.session.user.role !== 'admin') {
return res.status(403).json({ error: 'Требуются права администратора' }); return res.status(403).json({ error: 'Требуются права администратора' });
} }
next(); next();
@@ -966,7 +1072,7 @@ function setupUpravlenieEndpoints(app, db) {
res.json({ res.json({
success: true, success: true,
message: 'Подключение создано', message: 'Подключение создано',
connection connection: connection
}); });
} catch (error) { } catch (error) {
console.error('❌ Ошибка создания подключения:', error); console.error('❌ Ошибка создания подключения:', error);
@@ -1066,6 +1172,7 @@ function setupUpravlenieEndpoints(app, db) {
req.externalConnection = connection; req.externalConnection = connection;
next(); next();
} catch (error) { } catch (error) {
console.error('❌ Ошибка аутентификации:', error);
res.status(500).json({ error: 'Ошибка аутентификации' }); res.status(500).json({ error: 'Ошибка аутентификации' });
} }
}; };
@@ -1178,7 +1285,7 @@ function setupUpravlenieEndpoints(app, db) {
// Если статус "completed" - закрываем задачу если все исполнители выполнили // Если статус "completed" - закрываем задачу если все исполнители выполнили
if (status.status === 'completed') { if (status.status === 'completed') {
await checkAndCloseTaskIfAllCompleted(status.task_id); await checkAndCloseTaskIfAllCompleted(db, status.task_id);
} }
} }
@@ -1343,7 +1450,7 @@ function setupUpravlenieEndpoints(app, db) {
}); });
// Вспомогательная функция для проверки и закрытия задачи // Вспомогательная функция для проверки и закрытия задачи
async function checkAndCloseTaskIfAllCompleted(taskId) { async function checkAndCloseTaskIfAllCompleted(db, taskId) {
return new Promise((resolve) => { return new Promise((resolve) => {
db.all( db.all(
'SELECT status FROM task_assignments WHERE task_id = ?', 'SELECT status FROM task_assignments WHERE task_id = ?',