Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/entities/template.task/models/template.task.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,57 @@ function getTemplateTaskById(id) {
return db.query('SELECT * FROM template_tasks WHERE id = ?', [id]);
}

/**
* Get all template tasks for a specific user
* @deprecated Use getAllTemplateTasksWithSubtasksByUser for better performance when subtasks are needed
* @param {number} user_id - User ID
*/
function getAllTemplateTasksByUser(user_id) {
return db.query('SELECT * FROM template_tasks WHERE user_id = ?', [user_id]);
}

/**
* Get all template tasks with their subtasks for a user using SQL JOIN (optimized)
* @param {number} user_id - User ID
* @returns {Promise} Promise resolving to template tasks with subtasks in a flat structure
*/
function getAllTemplateTasksWithSubtasksByUser(user_id) {
return db
.query(
`SELECT
t.id,
t.title,
t.description,
t.category,
t.priority,
t.exp,
t.start_time,
t.end_time,
t.user_id,
t.created_at,
t.special_id,
t.is_active,
t.repeat_rule,
t.start_active_date,
t.end_active_date,
s.id as subtask_id,
s.title as subtask_title,
s.position as subtask_position,
s.special_id as subtask_special_id,
s.created_at as subtask_created_at
FROM template_tasks t
LEFT JOIN template_subtasks s ON t.id = s.template_task_id
WHERE t.user_id = ?
ORDER BY t.id, s.position`,
[user_id]
)
.then(([rows]) => [rows])
.catch((error) => {
console.error('Ошибка при получении шаблонов задач с подзадачами:', error);
throw error;
});
}

function createTemplateTask(templateTask) {
const {
title,
Expand Down Expand Up @@ -139,6 +186,7 @@ function getTemplatesForDay(user_id, dayOfWeek) {
module.exports = {
getTemplateTaskById,
getAllTemplateTasksByUser,
getAllTemplateTasksWithSubtasksByUser,
createTemplateTask,
deleteTemplateTaskById,
updateTemplateTaskById,
Expand Down
2 changes: 1 addition & 1 deletion src/entities/template.task/template.task.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class TemplateTaskController {
return res.status(400).json({ error: 'Не передан user-id в заголовке' });
}

templateTaskService.getTemplateTasksWithSubTasks(userId).then((result) => {
templateTaskService.getTemplateTasksWithSubTasksOptimized(userId).then((result) => {
res.status(result.status).json(result.data);
});
}
Expand Down
80 changes: 76 additions & 4 deletions src/entities/template.task/template.task.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class TemplateTaskService {
}
}

/**
* Get all template tasks with subtasks for a user (deprecated - uses N+1 queries)
* @deprecated Use getTemplateTasksWithSubTasksOptimized instead - it uses optimized SQL JOIN
*/
async getTemplateTasksWithSubTasks(userId) {
try {
const [rows] = await templateTaskModel.getAllTemplateTasksByUser(userId);
Expand All @@ -47,10 +51,78 @@ class TemplateTaskService {

return { status: 200, data: fullTasks };
} catch (err) {
console.error(
'❌ Ошибка при получении шаблонов задач с подзадачами:',
err,
);
console.error('❌ Ошибка при получении шаблонов задач с подзадачами:', err);
return { status: 500, data: { error: err.message } };
}
}

/**
* Get all template tasks with subtasks for a user (optimized with SQL JOIN)
*/
async getTemplateTasksWithSubTasksOptimized(userId) {
try {
const [rows] = await templateTaskModel.getAllTemplateTasksWithSubtasksByUser(userId);

if (!rows.length) {
return { status: 404, data: { error: 'Шаблоны задач не найдены' } };
}

// Группируем результаты по задачам
const tasksMap = new Map();

for (const row of rows) {
const taskId = row.id;

// Создаем задачу, если её еще нет
if (!tasksMap.has(taskId)) {
tasksMap.set(taskId, {
id: row.id,
title: row.title,
description: row.description,
category: row.category,
priority: row.priority,
exp: row.exp,
start_time: row.start_time,
end_time: row.end_time,
user_id: row.user_id,
created_at: row.created_at,
special_id: row.special_id,
is_active: row.is_active,
repeat_rule: (() => {
if (typeof row.repeat_rule !== 'string') {
return row.repeat_rule;
}
// Пытаемся распарсить JSON, если не получается - оставляем строку как есть
try {
return JSON.parse(row.repeat_rule);
} catch {
// Если это не JSON (например, "weekly" или "quests"), возвращаем как есть
return row.repeat_rule;
}
})(),
start_active_date: row.start_active_date,
end_active_date: row.end_active_date,
subtasks: [],
});
}

// Добавляем подзадачу, если она есть
if (row.subtask_id) {
tasksMap.get(taskId).subtasks.push({
id: row.subtask_id,
title: row.subtask_title,
position: row.subtask_position,
special_id: row.subtask_special_id,
created_at: row.subtask_created_at,
template_task_id: taskId,
});
}
}

const fullTasks = Array.from(tasksMap.values());
return { status: 200, data: fullTasks };
} catch (err) {
console.error('❌ Ошибка при получении шаблонов задач с подзадачами:', err);
return { status: 500, data: { error: err.message } };
}
}
Expand Down