hz
This commit is contained in:
537
database.js
537
database.js
@@ -195,108 +195,134 @@ function createSQLiteTables() {
|
||||
console.log('✅ Таблица для пользовательских настроек инициализирована');
|
||||
|
||||
// Таблица для типов документов
|
||||
db.run(`CREATE TABLE IF NOT EXISTS document_types (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
template_path TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`);
|
||||
db.run(`CREATE TABLE IF NOT EXISTS document_types (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
template_path TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`);
|
||||
|
||||
// Таблица для документов
|
||||
db.run(`CREATE TABLE IF NOT EXISTS documents (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
document_type_id INTEGER NOT NULL,
|
||||
status TEXT DEFAULT 'draft',
|
||||
created_by INTEGER NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
approved_at DATETIME,
|
||||
approved_by INTEGER,
|
||||
rejected_at DATETIME,
|
||||
rejected_by INTEGER,
|
||||
rejection_reason TEXT,
|
||||
file_path TEXT,
|
||||
file_name TEXT,
|
||||
file_size INTEGER,
|
||||
version INTEGER DEFAULT 1,
|
||||
parent_document_id INTEGER,
|
||||
FOREIGN KEY (document_type_id) REFERENCES document_types (id),
|
||||
FOREIGN KEY (created_by) REFERENCES users (id),
|
||||
FOREIGN KEY (approved_by) REFERENCES users (id),
|
||||
FOREIGN KEY (rejected_by) REFERENCES users (id),
|
||||
FOREIGN KEY (parent_document_id) REFERENCES documents (id)
|
||||
)`);
|
||||
// Таблица для документов
|
||||
db.run(`CREATE TABLE IF NOT EXISTS documents (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
document_type_id INTEGER NOT NULL,
|
||||
status TEXT DEFAULT 'draft',
|
||||
created_by INTEGER NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
approved_at DATETIME,
|
||||
approved_by INTEGER,
|
||||
rejected_at DATETIME,
|
||||
rejected_by INTEGER,
|
||||
rejection_reason TEXT,
|
||||
file_path TEXT,
|
||||
file_name TEXT,
|
||||
file_size INTEGER,
|
||||
version INTEGER DEFAULT 1,
|
||||
parent_document_id INTEGER,
|
||||
FOREIGN KEY (document_type_id) REFERENCES document_types (id),
|
||||
FOREIGN KEY (created_by) REFERENCES users (id),
|
||||
FOREIGN KEY (approved_by) REFERENCES users (id),
|
||||
FOREIGN KEY (rejected_by) REFERENCES users (id),
|
||||
FOREIGN KEY (parent_document_id) REFERENCES documents (id)
|
||||
)`);
|
||||
|
||||
// Таблица для этапов согласования
|
||||
db.run(`CREATE TABLE IF NOT EXISTS approval_stages (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
document_type_id INTEGER NOT NULL,
|
||||
stage_number INTEGER NOT NULL,
|
||||
stage_name TEXT NOT NULL,
|
||||
approver_role TEXT,
|
||||
approver_user_id INTEGER,
|
||||
is_required BOOLEAN DEFAULT true,
|
||||
can_edit BOOLEAN DEFAULT false,
|
||||
can_comment BOOLEAN DEFAULT true,
|
||||
deadline_days INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(document_type_id, stage_number),
|
||||
FOREIGN KEY (document_type_id) REFERENCES document_types (id),
|
||||
FOREIGN KEY (approver_user_id) REFERENCES users (id)
|
||||
)`);
|
||||
// Таблица для этапов согласования
|
||||
db.run(`CREATE TABLE IF NOT EXISTS approval_stages (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
document_type_id INTEGER NOT NULL,
|
||||
stage_number INTEGER NOT NULL,
|
||||
stage_name TEXT NOT NULL,
|
||||
approver_role TEXT,
|
||||
approver_user_id INTEGER,
|
||||
is_required BOOLEAN DEFAULT true,
|
||||
can_edit BOOLEAN DEFAULT false,
|
||||
can_comment BOOLEAN DEFAULT true,
|
||||
deadline_days INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(document_type_id, stage_number),
|
||||
FOREIGN KEY (document_type_id) REFERENCES document_types (id),
|
||||
FOREIGN KEY (approver_user_id) REFERENCES users (id)
|
||||
)`);
|
||||
|
||||
// Таблица для согласования документов
|
||||
db.run(`CREATE TABLE IF NOT EXISTS document_approvals (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
document_id INTEGER NOT NULL,
|
||||
stage_id INTEGER NOT NULL,
|
||||
approver_user_id INTEGER NOT NULL,
|
||||
status TEXT DEFAULT 'pending',
|
||||
comments TEXT,
|
||||
approved_at DATETIME,
|
||||
rejected_at DATETIME,
|
||||
deadline DATETIME,
|
||||
notified_at DATETIME,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(document_id, stage_id, approver_user_id),
|
||||
FOREIGN KEY (document_id) REFERENCES documents (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (stage_id) REFERENCES approval_stages (id),
|
||||
FOREIGN KEY (approver_user_id) REFERENCES users (id)
|
||||
)`);
|
||||
// Таблица для согласования документов
|
||||
db.run(`CREATE TABLE IF NOT EXISTS document_approvals (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
document_id INTEGER NOT NULL,
|
||||
stage_id INTEGER NOT NULL,
|
||||
approver_user_id INTEGER NOT NULL,
|
||||
status TEXT DEFAULT 'pending',
|
||||
comments TEXT,
|
||||
approved_at DATETIME,
|
||||
rejected_at DATETIME,
|
||||
deadline DATETIME,
|
||||
notified_at DATETIME,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(document_id, stage_id, approver_user_id),
|
||||
FOREIGN KEY (document_id) REFERENCES documents (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (stage_id) REFERENCES approval_stages (id),
|
||||
FOREIGN KEY (approver_user_id) REFERENCES users (id)
|
||||
)`);
|
||||
|
||||
// Таблица для комментариев к документам
|
||||
db.run(`CREATE TABLE IF NOT EXISTS document_comments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
document_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
comment TEXT NOT NULL,
|
||||
is_internal BOOLEAN DEFAULT false,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (document_id) REFERENCES documents (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users (id)
|
||||
)`);
|
||||
// Таблица для комментариев к документам
|
||||
db.run(`CREATE TABLE IF NOT EXISTS document_comments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
document_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
comment TEXT NOT NULL,
|
||||
is_internal BOOLEAN DEFAULT false,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (document_id) REFERENCES documents (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users (id)
|
||||
)`);
|
||||
|
||||
// Таблица для истории изменений документов
|
||||
db.run(`CREATE TABLE IF NOT EXISTS document_history (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
document_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
action TEXT NOT NULL,
|
||||
changes TEXT,
|
||||
version INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (document_id) REFERENCES documents (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users (id)
|
||||
)`);
|
||||
// Таблица для истории изменений документов
|
||||
db.run(`CREATE TABLE IF NOT EXISTS document_history (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
document_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
action TEXT NOT NULL,
|
||||
changes TEXT,
|
||||
version INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (document_id) REFERENCES documents (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (user_id) REFERENCES users (id)
|
||||
)`);
|
||||
|
||||
console.log('✅ Таблицы для согласования документов созданы');
|
||||
console.log('✅ Таблицы для согласования документов созданы');
|
||||
|
||||
// ===== НОВЫЕ ТАБЛИЦЫ ДЛЯ ГРУПП ПОЛЬЗОВАТЕЛЕЙ =====
|
||||
|
||||
// Таблица для групп пользователей
|
||||
db.run(`CREATE TABLE IF NOT EXISTS user_groups (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
color TEXT DEFAULT '#3498db',
|
||||
can_approve_documents BOOLEAN DEFAULT false,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)`);
|
||||
|
||||
// Таблица для связи пользователей с группами (многие-ко-многим)
|
||||
db.run(`CREATE TABLE IF NOT EXISTS user_group_memberships (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
group_id INTEGER NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(user_id, group_id),
|
||||
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (group_id) REFERENCES user_groups (id) ON DELETE CASCADE
|
||||
)`);
|
||||
|
||||
console.log('✅ Таблицы для групп пользователей созданы');
|
||||
|
||||
// Запускаем проверку и обновление структуры таблиц
|
||||
setTimeout(() => {
|
||||
@@ -339,7 +365,11 @@ function checkAndUpdateTableStructure() {
|
||||
{ name: 'due_date', type: 'DATETIME' },
|
||||
{ name: 'rework_comment', type: 'TEXT' },
|
||||
{ name: 'closed_at', type: 'DATETIME' },
|
||||
{ name: 'closed_by', type: 'INTEGER' }
|
||||
{ name: 'closed_by', type: 'INTEGER' },
|
||||
// Новые колонки для типа задач
|
||||
{ name: 'task_type', type: 'TEXT DEFAULT "regular"' },
|
||||
{ name: 'approver_group_id', type: 'INTEGER' },
|
||||
{ name: 'document_id', type: 'INTEGER' }
|
||||
],
|
||||
task_assignments: [
|
||||
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
|
||||
@@ -386,6 +416,21 @@ function checkAndUpdateTableStructure() {
|
||||
{ name: 'vk_user_id', type: 'TEXT' },
|
||||
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' },
|
||||
{ name: 'updated_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
|
||||
],
|
||||
user_groups: [
|
||||
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
|
||||
{ name: 'name', type: 'TEXT NOT NULL UNIQUE' },
|
||||
{ name: 'description', type: 'TEXT' },
|
||||
{ name: 'color', type: 'TEXT DEFAULT "#3498db"' },
|
||||
{ name: 'can_approve_documents', type: 'BOOLEAN DEFAULT false' },
|
||||
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' },
|
||||
{ name: 'updated_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
|
||||
],
|
||||
user_group_memberships: [
|
||||
{ name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
|
||||
{ name: 'user_id', type: 'INTEGER NOT NULL' },
|
||||
{ name: 'group_id', type: 'INTEGER NOT NULL' },
|
||||
{ name: 'created_at', type: 'DATETIME DEFAULT CURRENT_TIMESTAMP' }
|
||||
]
|
||||
};
|
||||
|
||||
@@ -447,6 +492,46 @@ function checkAndUpdateTableStructure() {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Создаем индексы для новых таблиц
|
||||
setTimeout(() => {
|
||||
const newIndexes = [
|
||||
"CREATE INDEX IF NOT EXISTS idx_tasks_task_type ON tasks(task_type)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_tasks_approver_group_id ON tasks(approver_group_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_user_groups_can_approve ON user_groups(can_approve_documents)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_user_group_memberships_user_id ON user_group_memberships(user_id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_user_group_memberships_group_id ON user_group_memberships(group_id)"
|
||||
];
|
||||
|
||||
newIndexes.forEach(indexQuery => {
|
||||
db.run(indexQuery, (err) => {
|
||||
if (err) {
|
||||
console.error(`❌ Ошибка создания индекса: ${err.message}`);
|
||||
} else {
|
||||
console.log(`✅ Индекс создан: ${indexQuery}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Создаем группу "Секретарь" по умолчанию, если её нет
|
||||
db.get("SELECT id FROM user_groups WHERE name = 'Секретарь'", (err, group) => {
|
||||
if (err || !group) {
|
||||
console.log('🔧 Создаем группу "Секретарь" по умолчанию...');
|
||||
db.run(
|
||||
`INSERT INTO user_groups (name, description, color, can_approve_documents)
|
||||
VALUES ('Секретарь', 'Группа для согласования документов', '#e74c3c', 1)`,
|
||||
(insertErr) => {
|
||||
if (insertErr) {
|
||||
console.error('❌ Ошибка создания группы "Секретарь":', insertErr.message);
|
||||
} else {
|
||||
console.log('✅ Группа "Секретарь" создана по умолчанию');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
}, 1000);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
@@ -621,7 +706,10 @@ async function createPostgresTables() {
|
||||
due_date TIMESTAMP,
|
||||
rework_comment TEXT,
|
||||
closed_at TIMESTAMP,
|
||||
closed_by INTEGER REFERENCES users(id)
|
||||
closed_by INTEGER REFERENCES users(id),
|
||||
task_type VARCHAR(50) DEFAULT 'regular',
|
||||
approver_group_id INTEGER,
|
||||
document_id INTEGER
|
||||
)
|
||||
`);
|
||||
|
||||
@@ -687,6 +775,129 @@ async function createPostgresTables() {
|
||||
)
|
||||
`);
|
||||
|
||||
// ===== НОВЫЕ ТАБЛИЦЫ ДЛЯ ГРУПП ПОЛЬЗОВАТЕЛЕЙ =====
|
||||
|
||||
// Таблица для групп пользователей
|
||||
await client.query(`
|
||||
CREATE TABLE IF NOT EXISTS user_groups (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(100) UNIQUE NOT NULL,
|
||||
description TEXT,
|
||||
color VARCHAR(20) DEFAULT '#3498db',
|
||||
can_approve_documents BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
// Таблица для связи пользователей с группами
|
||||
await client.query(`
|
||||
CREATE TABLE IF NOT EXISTS user_group_memberships (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
group_id INTEGER NOT NULL REFERENCES user_groups(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(user_id, group_id)
|
||||
)
|
||||
`);
|
||||
|
||||
// Таблицы для документов (существующие)
|
||||
await client.query(`
|
||||
CREATE TABLE IF NOT EXISTS document_types (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(100) UNIQUE NOT NULL,
|
||||
description TEXT,
|
||||
template_path TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
await client.query(`
|
||||
CREATE TABLE IF NOT EXISTS documents (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title VARCHAR(500) NOT NULL,
|
||||
description TEXT,
|
||||
document_type_id INTEGER NOT NULL REFERENCES document_types(id),
|
||||
status VARCHAR(50) DEFAULT 'draft',
|
||||
created_by INTEGER NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
approved_at TIMESTAMP,
|
||||
approved_by INTEGER REFERENCES users(id),
|
||||
rejected_at TIMESTAMP,
|
||||
rejected_by INTEGER REFERENCES users(id),
|
||||
rejection_reason TEXT,
|
||||
file_path TEXT,
|
||||
file_name TEXT,
|
||||
file_size BIGINT,
|
||||
version INTEGER DEFAULT 1,
|
||||
parent_document_id INTEGER REFERENCES documents(id)
|
||||
)
|
||||
`);
|
||||
|
||||
await client.query(`
|
||||
CREATE TABLE IF NOT EXISTS approval_stages (
|
||||
id SERIAL PRIMARY KEY,
|
||||
document_type_id INTEGER NOT NULL REFERENCES document_types(id),
|
||||
stage_number INTEGER NOT NULL,
|
||||
stage_name VARCHAR(100) NOT NULL,
|
||||
approver_role VARCHAR(50),
|
||||
approver_user_id INTEGER REFERENCES users(id),
|
||||
is_required BOOLEAN DEFAULT true,
|
||||
can_edit BOOLEAN DEFAULT false,
|
||||
can_comment BOOLEAN DEFAULT true,
|
||||
deadline_days INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(document_type_id, stage_number)
|
||||
)
|
||||
`);
|
||||
|
||||
await client.query(`
|
||||
CREATE TABLE IF NOT EXISTS document_approvals (
|
||||
id SERIAL PRIMARY KEY,
|
||||
document_id INTEGER NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
||||
stage_id INTEGER NOT NULL REFERENCES approval_stages(id),
|
||||
approver_user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
status VARCHAR(50) DEFAULT 'pending',
|
||||
comments TEXT,
|
||||
approved_at TIMESTAMP,
|
||||
rejected_at TIMESTAMP,
|
||||
deadline TIMESTAMP,
|
||||
notified_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(document_id, stage_id, approver_user_id)
|
||||
)
|
||||
`);
|
||||
|
||||
await client.query(`
|
||||
CREATE TABLE IF NOT EXISTS document_comments (
|
||||
id SERIAL PRIMARY KEY,
|
||||
document_id INTEGER NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
comment TEXT NOT NULL,
|
||||
is_internal BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
await client.query(`
|
||||
CREATE TABLE IF NOT EXISTS document_history (
|
||||
id SERIAL PRIMARY KEY,
|
||||
document_id INTEGER NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id),
|
||||
action VARCHAR(100) NOT NULL,
|
||||
changes TEXT,
|
||||
version INTEGER,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
console.log('✅ Все таблицы PostgreSQL созданы/проверены');
|
||||
|
||||
// Создаем индексы
|
||||
const indexes = [
|
||||
'CREATE INDEX IF NOT EXISTS idx_tasks_created_by ON tasks(created_by)',
|
||||
@@ -698,7 +909,18 @@ async function createPostgresTables() {
|
||||
'CREATE INDEX IF NOT EXISTS idx_task_files_task_id ON task_files(task_id)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_activity_logs_task_id ON activity_logs(task_id)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_activity_logs_created_at ON activity_logs(created_at)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_user_settings_user_id ON user_settings(user_id)'
|
||||
'CREATE INDEX IF NOT EXISTS idx_user_settings_user_id ON user_settings(user_id)',
|
||||
// Новые индексы
|
||||
'CREATE INDEX IF NOT EXISTS idx_tasks_task_type ON tasks(task_type)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_tasks_approver_group_id ON tasks(approver_group_id)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_user_groups_can_approve ON user_groups(can_approve_documents)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_user_group_memberships_user_id ON user_group_memberships(user_id)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_user_group_memberships_group_id ON user_group_memberships(group_id)',
|
||||
// Индексы для документов
|
||||
'CREATE INDEX IF NOT EXISTS idx_documents_status ON documents(status)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_documents_created_by ON documents(created_by)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_document_approvals_document_id ON document_approvals(document_id)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_document_approvals_status ON document_approvals(status)'
|
||||
];
|
||||
|
||||
for (const indexQuery of indexes) {
|
||||
@@ -715,6 +937,20 @@ async function createPostgresTables() {
|
||||
// Проверяем структуру PostgreSQL таблиц
|
||||
await checkPostgresTableStructure();
|
||||
|
||||
// Создаем группу "Секретарь" по умолчанию
|
||||
try {
|
||||
const checkResult = await client.query("SELECT id FROM user_groups WHERE name = 'Секретарь'");
|
||||
if (checkResult.rows.length === 0) {
|
||||
await client.query(`
|
||||
INSERT INTO user_groups (name, description, color, can_approve_documents)
|
||||
VALUES ('Секретарь', 'Группа для согласования документов', '#e74c3c', true)
|
||||
`);
|
||||
console.log('✅ Группа "Секретарь" создана по умолчанию');
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Не удалось создать группу "Секретарь":', error.message);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Ошибка создания таблиц PostgreSQL:', error.message);
|
||||
}
|
||||
@@ -742,6 +978,11 @@ async function checkPostgresTableStructure() {
|
||||
{ name: 'vk_user_id', type: 'TEXT' },
|
||||
{ name: 'created_at', type: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP' },
|
||||
{ name: 'updated_at', type: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP' }
|
||||
],
|
||||
tasks: [
|
||||
{ name: 'task_type', type: 'VARCHAR(50) DEFAULT "regular"' },
|
||||
{ name: 'approver_group_id', type: 'INTEGER' },
|
||||
{ name: 'document_id', type: 'INTEGER' }
|
||||
]
|
||||
};
|
||||
|
||||
@@ -799,6 +1040,96 @@ async function checkPostgresTableStructure() {
|
||||
}
|
||||
}
|
||||
|
||||
// Вспомогательные функции для работы с группами пользователей
|
||||
function getUserGroups(userId, callback) {
|
||||
const query = `
|
||||
SELECT g.* FROM user_groups g
|
||||
JOIN user_group_memberships ugm ON g.id = ugm.group_id
|
||||
WHERE ugm.user_id = ?
|
||||
ORDER BY g.name
|
||||
`;
|
||||
|
||||
db.all(query, [userId], (err, groups) => {
|
||||
if (err) {
|
||||
console.error('❌ Ошибка получения групп пользователя:', err);
|
||||
callback(err, []);
|
||||
} else {
|
||||
callback(null, groups || []);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getGroupMembers(groupId, callback) {
|
||||
const query = `
|
||||
SELECT u.* FROM users u
|
||||
JOIN user_group_memberships ugm ON u.id = ugm.user_id
|
||||
WHERE ugm.group_id = ?
|
||||
ORDER BY u.name
|
||||
`;
|
||||
|
||||
db.all(query, [groupId], (err, members) => {
|
||||
if (err) {
|
||||
console.error('❌ Ошибка получения участников группы:', err);
|
||||
callback(err, []);
|
||||
} else {
|
||||
callback(null, members || []);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getApproverGroups(callback) {
|
||||
const query = `
|
||||
SELECT g.*, COUNT(DISTINCT ugm.user_id) as member_count
|
||||
FROM user_groups g
|
||||
LEFT JOIN user_group_memberships ugm ON g.id = ugm.group_id
|
||||
WHERE g.can_approve_documents = true
|
||||
GROUP BY g.id
|
||||
ORDER BY g.name
|
||||
`;
|
||||
|
||||
db.all(query, [], (err, groups) => {
|
||||
if (err) {
|
||||
console.error('❌ Ошибка получения групп согласующих:', err);
|
||||
callback(err, []);
|
||||
} else {
|
||||
callback(null, groups || []);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addUserToGroup(userId, groupId, callback) {
|
||||
const query = `
|
||||
INSERT INTO user_group_memberships (user_id, group_id)
|
||||
VALUES (?, ?)
|
||||
ON CONFLICT (user_id, group_id) DO NOTHING
|
||||
`;
|
||||
|
||||
db.run(query, [userId, groupId], function(err) {
|
||||
if (err) {
|
||||
console.error('❌ Ошибка добавления пользователя в группу:', err);
|
||||
callback(err);
|
||||
} else {
|
||||
callback(null, this.changes > 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeUserFromGroup(userId, groupId, callback) {
|
||||
const query = `
|
||||
DELETE FROM user_group_memberships
|
||||
WHERE user_id = ? AND group_id = ?
|
||||
`;
|
||||
|
||||
db.run(query, [userId, groupId], function(err) {
|
||||
if (err) {
|
||||
console.error('❌ Ошибка удаления пользователя из группы:', err);
|
||||
callback(err);
|
||||
} else {
|
||||
callback(null, this.changes > 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createTaskFolder(taskId) {
|
||||
const taskFolder = path.join(tasksDir, taskId.toString());
|
||||
createDirIfNotExists(taskFolder);
|
||||
@@ -938,7 +1269,13 @@ module.exports = {
|
||||
checkTaskAccess,
|
||||
USE_POSTGRES,
|
||||
getDatabaseType: () => USE_POSTGRES ? 'PostgreSQL' : 'SQLite',
|
||||
checkAndUpdateTableStructure // Экспортируем для ручного запуска
|
||||
checkAndUpdateTableStructure, // Экспортируем для ручного запуска
|
||||
// Новые функции для работы с группами
|
||||
getUserGroups,
|
||||
getGroupMembers,
|
||||
getApproverGroups,
|
||||
addUserToGroup,
|
||||
removeUserFromGroup
|
||||
};
|
||||
|
||||
// Запускаем инициализацию при экспорте (но она завершится позже)
|
||||
|
||||
Reference in New Issue
Block a user