start tasks module

This commit is contained in:
root
2026-01-23 08:48:06 +03:00
parent 283b9132a0
commit 725c62a179
22 changed files with 2489 additions and 6 deletions
@@ -0,0 +1,76 @@
<?php
namespace App\Modules\Tasks\Models;
use CodeIgniter\Model;
class TaskAssigneeModel extends Model
{
protected $table = 'task_assignees';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $allowedFields = [
'task_id',
'user_id',
'role',
'assigned_at',
];
/**
* Получить всех исполнителей задачи
*/
public function getAssigneesByTask(int $taskId): array
{
return $this->select('task_assignees.*, users.name as user_name, users.email as user_email')
->join('users', 'task_assignees.user_id = users.id', 'left')
->where('task_id', $taskId)
->findAll();
}
/**
* Получить задачи пользователя
*/
public function getTasksByUser(int $userId, int $organizationId): array
{
return $this->select('tasks.*')
->join('tasks', 'task_assignees.task_id = tasks.id')
->where('task_assignees.user_id', $userId)
->where('tasks.organization_id', $organizationId)
->findAll();
}
/**
* Добавить исполнителя
*/
public function addAssignee(int $taskId, int $userId, string $role = 'assignee'): int
{
return $this->insert([
'task_id' => $taskId,
'user_id' => $userId,
'role' => $role,
'assigned_at' => date('Y-m-d H:i:s'),
]);
}
/**
* Удалить исполнителя
*/
public function removeAssignee(int $taskId, int $userId): bool
{
return $this->where('task_id', $taskId)
->where('user_id', $userId)
->delete() > 0;
}
/**
* Проверить, является ли пользователь исполнителем
*/
public function isAssignee(int $taskId, int $userId): bool
{
return $this->where('task_id', $taskId)
->where('user_id', $userId)
->countAllResults() > 0;
}
}
@@ -0,0 +1,81 @@
<?php
namespace App\Modules\Tasks\Models;
use CodeIgniter\Model;
use App\Models\Traits\TenantScopedModel;
class TaskBoardModel extends Model
{
use TenantScopedModel;
protected $table = 'task_boards';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $tenantField = 'organization_id';
protected $allowedFields = [
'organization_id',
'name',
'description',
'is_default',
];
protected $useTimestamps = true;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
/**
* Получить все доски организации
*/
public function getBoardsByOrganization(int $organizationId): array
{
return $this->where('organization_id', $organizationId)
->orderBy('is_default', 'DESC')
->orderBy('created_at', 'DESC')
->findAll();
}
/**
* Получить доску по ID
*/
public function getBoard(int $boardId, int $organizationId): ?array
{
return $this->where('id', $boardId)
->where('organization_id', $organizationId)
->first();
}
/**
* Получить дефолтную доску организации
*/
public function getDefaultBoard(int $organizationId): ?array
{
return $this->where('organization_id', $organizationId)
->where('is_default', 1)
->first();
}
/**
* Создать дефолтную доску для новой организации
*/
public function createDefaultBoard(int $organizationId): int
{
$data = [
'organization_id' => $organizationId,
'name' => 'Мои задачи',
'description' => 'Основная доска задач',
'is_default' => 1,
];
$boardId = $this->insert($data);
if ($boardId) {
// Создаём дефолтные колонки
$columnModel = new TaskColumnModel();
$columnModel->createDefaultColumns($boardId);
}
return $boardId;
}
}
@@ -0,0 +1,89 @@
<?php
namespace App\Modules\Tasks\Models;
use CodeIgniter\Model;
use App\Models\Traits\TenantScopedModel;
class TaskColumnModel extends Model
{
use TenantScopedModel;
protected $table = 'task_columns';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $tenantField = 'organization_id';
protected $allowedFields = [
'board_id',
'name',
'color',
'order_index',
'is_default',
];
protected $useTimestamps = true;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
/**
* Получить колонки доски
*/
public function getColumnsByBoard(int $boardId): array
{
return $this->where('board_id', $boardId)
->orderBy('order_index', 'ASC')
->findAll();
}
/**
* Получить следующий порядковый номер для колонки
*/
public function getNextOrderIndex(int $boardId): int
{
$max = $this->selectMax('order_index')
->where('board_id', $boardId)
->first();
return ($max['order_index'] ?? 0) + 1;
}
/**
* Создать дефолтные колонки для новой доски
*/
public function createDefaultColumns(int $boardId): bool
{
$defaultColumns = [
[
'board_id' => $boardId,
'name' => 'К выполнению',
'color' => '#6B7280',
'order_index' => 1,
'is_default' => 0,
],
[
'board_id' => $boardId,
'name' => 'В работе',
'color' => '#3B82F6',
'order_index' => 2,
'is_default' => 0,
],
[
'board_id' => $boardId,
'name' => 'На проверке',
'color' => '#F59E0B',
'order_index' => 3,
'is_default' => 0,
],
[
'board_id' => $boardId,
'name' => 'Завершено',
'color' => '#10B981',
'order_index' => 4,
'is_default' => 0,
],
];
return $this->insertBatch($defaultColumns);
}
}
+140
View File
@@ -0,0 +1,140 @@
<?php
namespace App\Modules\Tasks\Models;
use CodeIgniter\Model;
use App\Models\Traits\TenantScopedModel;
class TaskModel extends Model
{
use TenantScopedModel;
protected $table = 'tasks';
protected $primaryKey = 'id';
protected $useSoftDeletes = false;
protected $useTimestamps = true;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $returnType = 'array';
protected $tenantField = 'organization_id';
protected $allowedFields = [
'organization_id',
'board_id',
'column_id',
'title',
'description',
'priority',
'due_date',
'completed_at',
'order_index',
'created_by',
];
/**
* Получить задачи с JOIN-ами для таблицы
*/
public function getForTable(int $organizationId): array
{
return $this->select('
tasks.id,
tasks.title,
tasks.description,
tasks.priority,
tasks.due_date,
tasks.completed_at,
tasks.created_at,
tc.name as column_name,
tc.color as column_color,
u.name as created_by_name
')
->join('task_columns tc', 'tasks.column_id = tc.id', 'left')
->join('users u', 'tasks.created_by = u.id', 'left')
->where('tasks.organization_id', $organizationId)
->orderBy('tasks.created_at', 'DESC')
->findAll();
}
/**
* Получить задачи, сгруппированные по колонкам (для Канбана)
*/
public function getTasksGroupedByColumn(int $boardId): array
{
$tasks = $this->select('tasks.*, tc.name as column_name, tc.color as column_color')
->join('task_columns tc', 'tasks.column_id = tc.id', 'left')
->where('tasks.board_id', $boardId)
->orderBy('tc.order_index', 'ASC')
->orderBy('tasks.order_index', 'ASC')
->orderBy('tasks.created_at', 'DESC')
->findAll();
$grouped = [];
foreach ($tasks as $task) {
$columnId = $task['column_id'] ?? 0;
if (!isset($grouped[$columnId])) {
$grouped[$columnId] = [
'column_name' => $task['column_name'] ?? 'Без колонки',
'column_color' => $task['column_color'] ?? '#6B7280',
'tasks' => [],
];
}
$grouped[$columnId]['tasks'][] = $task;
}
return $grouped;
}
/**
* Получить задачи для календаря
*/
public function getTasksForCalendar(int $organizationId, string $month): array
{
return $this->select('tasks.*, tc.color as column_color, tc.name as column_name')
->join('task_columns tc', 'tasks.column_id = tc.id', 'left')
->where('tasks.organization_id', $organizationId)
->where('tasks.due_date >=', date('Y-m-01', strtotime($month)))
->where('tasks.due_date <=', date('Y-m-t', strtotime($month)))
->where('tasks.completed_at', null)
->orderBy('tasks.due_date', 'ASC')
->findAll();
}
/**
* Получить задачу по ID
*/
public function getTask(int $taskId, int $organizationId): ?array
{
return $this->select('tasks.*, tc.name as column_name, tc.color as column_color')
->join('task_columns tc', 'tasks.column_id = tc.id', 'left')
->where('tasks.id', $taskId)
->where('tasks.organization_id', $organizationId)
->first();
}
/**
* Получить статистику по задачам
*/
public function getTaskStats(int $organizationId): array
{
$total = $this->select('COUNT(*) as count')
->where('organization_id', $organizationId)
->countAllResults();
$completed = $this->select('COUNT(*) as count')
->where('organization_id', $organizationId)
->where('completed_at IS NOT NULL')
->countAllResults();
$overdue = $this->select('COUNT(*) as count')
->where('organization_id', $organizationId)
->where('completed_at', null)
->where('due_date <', date('Y-m-d'))
->countAllResults();
return [
'total' => $total,
'completed' => $completed,
'overdue' => $overdue,
'pending' => $total - $completed,
];
}
}