start tasks module
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Tasks\Services;
|
||||
|
||||
use App\Modules\Tasks\Models\TaskBoardModel;
|
||||
use App\Modules\Tasks\Models\TaskColumnModel;
|
||||
|
||||
class TaskBoardService
|
||||
{
|
||||
protected TaskBoardModel $boardModel;
|
||||
protected TaskColumnModel $columnModel;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->boardModel = new TaskBoardModel();
|
||||
$this->columnModel = new TaskColumnModel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать новую доску с дефолтными колонками
|
||||
*/
|
||||
public function createBoard(array $data, int $userId): int
|
||||
{
|
||||
$boardId = $this->boardModel->insert($data);
|
||||
|
||||
if ($boardId) {
|
||||
// Создаём дефолтные колонки
|
||||
$this->columnModel->createDefaultColumns($boardId);
|
||||
}
|
||||
|
||||
return $boardId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновить доску
|
||||
*/
|
||||
public function updateBoard(int $boardId, array $data, int $organizationId): bool
|
||||
{
|
||||
$board = $this->boardModel->getBoard($boardId, $organizationId);
|
||||
if (!$board) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->boardModel->update($boardId, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить доску
|
||||
*/
|
||||
public function deleteBoard(int $boardId, int $organizationId): bool
|
||||
{
|
||||
$board = $this->boardModel->getBoard($boardId, $organizationId);
|
||||
if (!$board) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->boardModel->delete($boardId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить доску с колонками
|
||||
*/
|
||||
public function getBoardWithColumns(int $boardId, int $organizationId): ?array
|
||||
{
|
||||
$board = $this->boardModel->getBoard($boardId, $organizationId);
|
||||
if (!$board) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$board['columns'] = $this->columnModel->getColumnsByBoard($boardId);
|
||||
|
||||
return $board;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить все доски организации
|
||||
*/
|
||||
public function getOrganizationBoards(int $organizationId): array
|
||||
{
|
||||
return $this->boardModel->getBoardsByOrganization($organizationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать колонку
|
||||
*/
|
||||
public function createColumn(int $boardId, array $data): int
|
||||
{
|
||||
$data['board_id'] = $boardId;
|
||||
$data['order_index'] = $this->columnModel->getNextOrderIndex($boardId);
|
||||
|
||||
return $this->columnModel->insert($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновить колонку
|
||||
*/
|
||||
public function updateColumn(int $columnId, array $data): bool
|
||||
{
|
||||
return $this->columnModel->update($columnId, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить колонку
|
||||
*/
|
||||
public function deleteColumn(int $columnId, int $boardId): bool
|
||||
{
|
||||
$column = $this->columnModel->find($columnId);
|
||||
if (!$column || $column['board_id'] !== $boardId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->columnModel->delete($columnId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Изменить порядок колонок
|
||||
*/
|
||||
public function reorderColumns(array $columnOrders): bool
|
||||
{
|
||||
foreach ($columnOrders as $index => $columnId) {
|
||||
$this->columnModel->update($columnId, ['order_index' => $index]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Tasks\Services;
|
||||
|
||||
use App\Modules\Tasks\Models\TaskModel;
|
||||
use App\Modules\Tasks\Models\TaskAssigneeModel;
|
||||
use App\Modules\Tasks\Models\TaskColumnModel;
|
||||
use CodeIgniter\Events\Events;
|
||||
|
||||
class TaskService
|
||||
{
|
||||
protected TaskModel $taskModel;
|
||||
protected TaskAssigneeModel $assigneeModel;
|
||||
protected TaskColumnModel $columnModel;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->taskModel = new TaskModel();
|
||||
$this->assigneeModel = new TaskAssigneeModel();
|
||||
$this->columnModel = new TaskColumnModel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить модель задач для использования в DataTable
|
||||
*/
|
||||
public function getModel(): TaskModel
|
||||
{
|
||||
return $this->taskModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать новую задачу
|
||||
*/
|
||||
public function createTask(array $data, int $userId, array $assigneeIds = []): int
|
||||
{
|
||||
$data['created_by'] = $userId;
|
||||
|
||||
$taskId = $this->taskModel->insert($data);
|
||||
|
||||
if ($taskId) {
|
||||
// Добавляем исполнителей
|
||||
foreach ($assigneeIds as $userId) {
|
||||
$this->assigneeModel->addAssignee($taskId, (int)$userId);
|
||||
}
|
||||
|
||||
// Генерируем событие
|
||||
Events::trigger('tasks.created', $taskId, $data, $userId);
|
||||
}
|
||||
|
||||
return $taskId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновить задачу
|
||||
*/
|
||||
public function updateTask(int $taskId, array $data, int $userId): bool
|
||||
{
|
||||
$oldTask = $this->taskModel->find($taskId);
|
||||
if (!$oldTask) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $this->taskModel->update($taskId, $data);
|
||||
|
||||
if ($result) {
|
||||
Events::trigger('tasks.updated', $taskId, $data, $userId);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Изменить колонку задачи (drag-and-drop)
|
||||
*/
|
||||
public function changeColumn(int $taskId, int $newColumnId, int $userId): bool
|
||||
{
|
||||
$task = $this->taskModel->find($taskId);
|
||||
if (!$task) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$newColumn = $this->columnModel->find($newColumnId);
|
||||
if (!$newColumn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$oldColumnId = $task['column_id'];
|
||||
|
||||
$data = ['column_id' => $newColumnId];
|
||||
|
||||
// Если задача завершена (новая колонка не "Завершено"), очищаем completed_at
|
||||
if ($newColumn['name'] !== 'Завершено') {
|
||||
$data['completed_at'] = null;
|
||||
}
|
||||
|
||||
$result = $this->taskModel->update($taskId, $data);
|
||||
|
||||
if ($result) {
|
||||
Events::trigger('tasks.column_changed', $taskId, $oldColumnId, $newColumnId, $userId);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Завершить задачу
|
||||
*/
|
||||
public function completeTask(int $taskId, int $userId): bool
|
||||
{
|
||||
$task = $this->taskModel->find($taskId);
|
||||
if (!$task) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $this->taskModel->update($taskId, [
|
||||
'completed_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
|
||||
if ($result) {
|
||||
Events::trigger('tasks.completed', $taskId, $userId);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возобновить задачу
|
||||
*/
|
||||
public function reopenTask(int $taskId, int $userId): bool
|
||||
{
|
||||
$task = $this->taskModel->find($taskId);
|
||||
if (!$task) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $this->taskModel->update($taskId, [
|
||||
'completed_at' => null,
|
||||
]);
|
||||
|
||||
if ($result) {
|
||||
Events::trigger('tasks.reopened', $taskId, $userId);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить задачу
|
||||
*/
|
||||
public function deleteTask(int $taskId, int $userId): bool
|
||||
{
|
||||
$task = $this->taskModel->find($taskId);
|
||||
if (!$task) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $this->taskModel->delete($taskId);
|
||||
|
||||
if ($result) {
|
||||
Events::trigger('tasks.deleted', $taskId, $userId);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить задачу по ID
|
||||
*/
|
||||
public function getTask(int $taskId, int $organizationId): ?array
|
||||
{
|
||||
$task = $this->taskModel->getTask($taskId, $organizationId);
|
||||
if (!$task) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$task['assignees'] = $this->assigneeModel->getAssigneesByTask($taskId);
|
||||
|
||||
return $task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить задачи для Канбана
|
||||
*/
|
||||
public function getTasksForKanban(int $boardId): array
|
||||
{
|
||||
return $this->taskModel->getTasksGroupedByColumn($boardId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить задачи для календаря
|
||||
*/
|
||||
public function getTasksForCalendar(int $organizationId, string $month): array
|
||||
{
|
||||
return $this->taskModel->getTasksForCalendar($organizationId, $month);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить статистику
|
||||
*/
|
||||
public function getStats(int $organizationId): array
|
||||
{
|
||||
return $this->taskModel->getTaskStats($organizationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновить исполнителей задачи
|
||||
*/
|
||||
public function updateAssignees(int $taskId, array $userIds): bool
|
||||
{
|
||||
// Удаляем старых исполнителей
|
||||
$this->assigneeModel->where('task_id', $taskId)->delete();
|
||||
|
||||
// Добавляем новых
|
||||
foreach ($userIds as $userId) {
|
||||
$this->assigneeModel->addAssignee($taskId, (int)$userId);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать задачу из события (например, из сделки CRM)
|
||||
*/
|
||||
public function createFromEvent(string $eventType, array $eventData, int $organizationId): ?int
|
||||
{
|
||||
$taskData = [
|
||||
'organization_id' => $organizationId,
|
||||
'board_id' => $this->getDefaultBoardId($organizationId),
|
||||
'column_id' => $this->getFirstColumnId($organizationId),
|
||||
'title' => $eventData['title'] ?? 'Задача',
|
||||
'description' => $eventData['description'] ?? '',
|
||||
'priority' => $eventData['priority'] ?? 'medium',
|
||||
'due_date' => $eventData['due_date'] ?? null,
|
||||
];
|
||||
|
||||
$assignees = $eventData['assignees'] ?? [];
|
||||
|
||||
return $this->createTask($taskData, $eventData['created_by'] ?? 1, $assignees);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить ID дефолтной доски
|
||||
*/
|
||||
protected function getDefaultBoardId(int $organizationId): int
|
||||
{
|
||||
$boardModel = new TaskBoardModel();
|
||||
$board = $boardModel->getDefaultBoard($organizationId);
|
||||
|
||||
if (!$board) {
|
||||
// Создаём дефолтную доску
|
||||
return $boardModel->createDefaultBoard($organizationId);
|
||||
}
|
||||
|
||||
return $board['id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить ID первой колонки
|
||||
*/
|
||||
protected function getFirstColumnId(int $organizationId): int
|
||||
{
|
||||
$boardId = $this->getDefaultBoardId($organizationId);
|
||||
$columns = $this->columnModel->getColumnsByBoard($boardId);
|
||||
|
||||
return $columns[0]['id'] ?? 1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user