This commit is contained in:
2026-01-27 00:32:06 +05:00
parent 30aa35357f
commit 0fe8f05b73
7 changed files with 2837 additions and 102 deletions

View File

@@ -2,6 +2,19 @@
const path = require('path');
const fs = require('fs');
function getApproverUsers(groupId) {
return new Promise((resolve) => {
db.all(`
SELECT u.id, u.login, u.name, u.email
FROM users u
JOIN user_group_memberships ugm ON u.id = ugm.user_id
WHERE ugm.group_id = ?
`, [groupId], (err, users) => {
resolve(err ? [] : users);
});
});
}
function setupTaskEndpoints(app, db, upload) {
const { logActivity, createUserTaskFolder, saveTaskMetadata, updateTaskMetadata, checkTaskAccess } = require('./database');
const { sendTaskNotifications } = require('./notifications');
@@ -444,7 +457,180 @@ function setupTaskEndpoints(app, db, upload) {
});
});
});
// API для создания задачи согласования документов
app.post('/api/document-approval-tasks', requireAuth, upload.array('files', 15), (req, res) => {
const { title, description, approverGroupId, documentId, dueDate } = req.body;
const createdBy = req.session.user.id;
if (!title) {
return res.status(400).json({ error: 'Название задачи обязательно' });
}
if (!approverGroupId) {
return res.status(400).json({ error: 'Выберите группу для согласования' });
}
if (!dueDate) {
return res.status(400).json({ error: 'Дата и время выполнения обязательны' });
}
// Проверяем, может ли группа согласовывать документы
db.get("SELECT can_approve_documents FROM user_groups WHERE id = ?",
[approverGroupId], (err, group) => {
if (err || !group || !group.can_approve_documents) {
return res.status(400).json({ error: 'Выбранная группа не может согласовывать документы' });
}
db.serialize(async () => {
const startDate = new Date().toISOString();
// Создаем задачу с типом "document_approval"
db.run(
`INSERT INTO tasks (title, description, created_by, task_type,
approver_group_id, document_id, start_date, due_date)
VALUES (?, ?, ?, 'document_approval', ?, ?, ?, ?)`,
[title, description, createdBy, approverGroupId, documentId || null, startDate, dueDate || null],
async function(err) {
if (err) {
res.status(500).json({ error: err.message });
return;
}
const taskId = this.lastID;
// Сохраняем метаданные
saveTaskMetadata(taskId, title, description, createdBy, null, startDate, dueDate);
// Получаем всех пользователей из группы согласующих
const approvers = await getApproverUsers(approverGroupId);
// Назначаем задачу всем согласующим в группе
if (approvers.length > 0) {
approvers.forEach(approver => {
db.run(
"INSERT INTO task_assignments (task_id, user_id, status, start_date, due_date) VALUES (?, ?, 'assigned', ?, ?)",
[taskId, approver.id, startDate, dueDate || null]
);
});
// Отправляем уведомления
sendTaskNotifications('created', taskId, title, description, createdBy);
}
logActivity(taskId, createdBy, 'DOCUMENT_APPROVAL_TASK_CREATED',
`Создана задача согласования документа для группы ${approverGroupId}`);
// Загрузка файлов (если есть)
if (req.files && req.files.length > 0) {
const userFolder = createUserTaskFolder(taskId, req.session.user.login);
req.files.forEach(file => {
const newPath = path.join(userFolder, path.basename(file.filename));
fs.renameSync(file.path, newPath);
const originalName = file.originalname;
db.run(
"INSERT INTO task_files (task_id, user_id, filename, original_name, file_path, file_size) VALUES (?, ?, ?, ?, ?, ?)",
[taskId, createdBy, path.basename(file.filename), originalName, newPath, file.size]
);
logActivity(taskId, createdBy, 'FILE_UPLOADED', `Загружен файл: ${originalName}`);
});
}
res.json({
success: true,
taskId: taskId,
message: 'Задача согласования документа создана',
approversCount: approvers.length
});
}
);
});
});
});
// API для получения задач согласования документов
app.get('/api/document-approval-tasks', requireAuth, (req, res) => {
const userId = req.session.user.id;
const query = `
SELECT DISTINCT
t.*,
u.name as creator_name,
u.login as creator_login,
g.name as approver_group_name,
g.color as approver_group_color,
GROUP_CONCAT(DISTINCT ta.user_id) as assigned_user_ids,
GROUP_CONCAT(DISTINCT u2.name) as assigned_user_names
FROM tasks t
LEFT JOIN users u ON t.created_by = u.id
LEFT JOIN user_groups g ON t.approver_group_id = g.id
LEFT JOIN task_assignments ta ON t.id = ta.task_id
LEFT JOIN users u2 ON ta.user_id = u2.id
WHERE t.task_type = 'document_approval'
AND t.status = 'active'
AND t.closed_at IS NULL
`;
const params = [];
if (req.session.user.role !== 'admin') {
query += ` AND (t.created_by = ? OR ta.user_id = ?)`;
params.push(userId, userId);
}
query += " GROUP BY t.id ORDER BY t.created_at DESC";
db.all(query, params, (err, tasks) => {
if (err) {
res.status(500).json({ error: err.message });
return;
}
const taskPromises = tasks.map(task => {
return new Promise((resolve) => {
// Получаем назначения
db.all(`
SELECT ta.*, u.name as user_name, u.login as user_login
FROM task_assignments ta
LEFT JOIN users u ON ta.user_id = u.id
WHERE ta.task_id = ?
`, [task.id], (err, assignments) => {
if (err) {
task.assignments = [];
} else {
assignments.forEach(assignment => {
if (checkIfOverdue(assignment.due_date, assignment.status) && assignment.status !== 'completed') {
assignment.status = 'overdue';
}
});
task.assignments = assignments || [];
}
// Получаем информацию о группе согласующих
if (task.approver_group_id) {
db.get(`
SELECT name, color, can_approve_documents
FROM user_groups
WHERE id = ?
`, [task.approver_group_id], (err, group) => {
task.approver_group = group || null;
resolve(task);
});
} else {
resolve(task);
}
});
});
});
Promise.all(taskPromises).then(completedTasks => {
res.json(completedTasks);
});
});
});
// API для обновления статуса в Канбане
app.put('/api/kanban-tasks/:taskId/status', requireAuth, (req, res) => {
const { taskId } = req.params;
@@ -1163,4 +1349,4 @@ function setupTaskEndpoints(app, db, upload) {
});
}
module.exports = { setupTaskEndpoints };
module.exports = { setupTaskEndpoints,getApproverUsers };