upravlenie-service
This commit is contained in:
@@ -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 = ?',
|
||||||
|
|||||||
Reference in New Issue
Block a user