Перейти к основному содержимому

Начало работы

Требования

  • Evolution CMS 3.7+
  • PHP 8.3+
  • Composer 2.2+
  • Одна из: MySQL 8.0+ / MariaDB 10.5+ / PostgreSQL 10+ / SQLite 3.25+

Установка

Шаг 1: Перейдите в директорию Core

cd core

Шаг 2: Обновите Composer

composer update

Шаг 3: Установите пакет

php artisan package:installrequire seiger/stask "*"

Шаг 4: Опубликуйте ресурсы

php artisan vendor:publish --tag=stask

Эта команда опубликует:

  • Файлы конфигурации в core/config/app/aliases/
  • Публичные ресурсы (CSS, JS, изображения) в public/assets/site/
  • Создаст директорию хранилища storage/stask/

Шаг 5: Запустите миграции

php artisan migrate

Это создаст две таблицы базы данных:

  • s_workers - Конфигурации воркеров
  • s_tasks - Записи задач и история выполнения

Также создается право доступа stask для контроля доступа к интерфейсу sTask.

Шаг 6: Настройка Обработчика Задач

Команда обработчика задач автоматически обрабатывает ожидающие задачи. Для непрерывной обработки добавьте в crontab:

* * * * * cd /path/to/your/project && php artisan schedule:run >> /dev/null 2>&1

Примечание: Воркеры автоматически обнаруживаются при открытии вкладки Воркеры в админ-панели. Ручное обнаружение не требуется!

Где найти модуль

После установки, получите доступ к sTask через:

Менеджер → Инструменты → Менеджер Задач

Вы увидите:

  • Вкладка Панель - Статистика задач и последние задачи
  • Вкладка Воркеры - Управление воркерами и автоматическое обнаружение

Быстрое руководство

1. Создайте свою первую задачу (программно)

use Seiger\sTask\Facades\sTask;

// Создать простую задачу
$task = sTask::create(
identifier: 'product_sync', // Идентификатор воркера
action: 'import', // Действие для выполнения
data: [ // Данные задачи
'file' => '/path/to/products.csv',
'delimiter' => ',',
'skip_first_row' => true
],
priority: 'normal', // 'low', 'normal', 'high'
userId: evo()->getLoginUserID()
);

echo "Задача #{$task->id} создана успешно!\n";

2. Проверьте статус задачи

// Получить задачу по ID
$task = \Seiger\sTask\Models\sTaskModel::find(1);

// Проверить статус
if ($task->isPending()) {
echo "Задача ожидает обработки\n";
}

if ($task->isRunning()) {
echo "Задача выполняется сейчас\n";
echo "Прогресс: {$task->progress}%\n";
}

if ($task->isFinished()) {
echo "Задача завершена\n";
echo "Статус: {$task->status_text}\n";
echo "Сообщение: {$task->message}\n";
}

// Получить детальную информацию
echo "Воркер: {$task->identifier}\n";
echo "Действие: {$task->action}\n";
echo "Создано: {$task->created_at}\n";
echo "Запустил: Пользователь #{$task->started_by}\n";

3. Обработайте ожидающие задачи

// Обработать все ожидающие задачи
$processedCount = sTask::processPendingTasks();
echo "Обработано {$processedCount} задач\n";

// Или обработать с собственным размером пакета
$processedCount = sTask::processPendingTasks(batchSize: 5);

4. Просмотрите логи задачи

$task = \Seiger\sTask\Models\sTaskModel::find(1);

// Получить все логи
$logs = $task->getLogs();
foreach ($logs as $log) {
echo "[{$log['timestamp']}] {$log['level']}: {$log['message']}\n";
}

// Получить последние 10 логов
$recentLogs = $task->getLastLogs(10);

// Получить только ошибки
$errorLogs = $task->getErrorLogs();

Базовые примеры использования

Пример 1: Импорт товаров

use Seiger\sTask\Facades\sTask;

// Создать задачу импорта
$task = sTask::create(
identifier: 'product',
action: 'import',
data: [
'file' => storage_path('imports/products_2025.csv'),
'update_existing' => true,
'create_new' => true
],
priority: 'high'
);

echo "Задача импорта создана: #{$task->id}\n";

// Задача будет обработана автоматически воркером
// Вы можете мониторить прогресс в админ интерфейсе

Пример 2: Экспорт данных

// Создать задачу экспорта
$task = sTask::create(
identifier: 'product',
action: 'export',
data: [
'format' => 'csv',
'filters' => [
'category_id' => 5,
'active' => true
],
'fields' => ['id', 'sku', 'name', 'price', 'stock']
],
priority: 'normal'
);

// Ожидать завершения (в реальном сценарии проверяйте через админ или API)
while (!$task->fresh()->isFinished()) {
sleep(1);
}

if ($task->status === 30) { // completed
echo "Экспорт завершен!\n";
echo "Скачать файл: {$task->result}\n";
}

Пример 3: Массовая email кампания

// Создать задачу email
$task = sTask::create(
identifier: 'email_campaign',
action: 'send',
data: [
'template' => 'newsletter_2025',
'recipients' => [
['email' => 'user1@example.com', 'name' => 'Иван Петров'],
['email' => 'user2@example.com', 'name' => 'Мария Иванова'],
// ... больше получателей
],
'subject' => 'Ежемесячная рассылка - Январь 2025',
'attachments' => [
storage_path('newsletters/january_2025.pdf')
]
],
priority: 'normal'
);

Пример 4: Запланированная очистка

// Создать задачу очистки запланированную на позже
$task = \Seiger\sTask\Models\sTaskModel::create([
'identifier' => 'system',
'action' => 'cleanup',
'status' => 10, // pending
'priority' => 'low',
'start_at' => now()->addHours(2), // Запустить через 2 часа
'meta' => [
'clean_logs' => true,
'clean_cache' => true,
'older_than_days' => 30
]
]);

echo "Очистка запланирована на: {$task->start_at}\n";

Управление воркерами

Обнаружить новые воркеры

// Обнаружить и зарегистрировать новые воркеры
$registered = sTask::discoverWorkers();

echo "Найдено и зарегистрировано " . count($registered) . " новых воркеров:\n";
foreach ($registered as $worker) {
echo "- {$worker->identifier} ({$worker->scope})\n";
}

Пересканировать существующие воркеры

// Обновить метаданные для существующих воркеров
$updated = sTask::rescanWorkers();

echo "Обновлено " . count($updated) . " воркеров\n";

Очистить orphaned воркеры

// Удалить воркеры, классы которых больше не существуют
$deleted = sTask::cleanOrphanedWorkers();

echo "Удалено {$deleted} orphaned воркеров\n";

Список всех воркеров

// Получить всех воркеров
$allWorkers = sTask::getWorkers(activeOnly: false);

echo "Всего воркеров: " . $allWorkers->count() . "\n\n";

foreach ($allWorkers as $worker) {
echo "Воркер: {$worker->identifier}\n";
echo " Scope: {$worker->scope}\n";
echo " Название: {$worker->title}\n";
echo " Статус: " . ($worker->active ? 'Активный' : 'Неактивный') . "\n";
echo " Класс: {$worker->class}\n";
echo "\n";
}

Фильтровать воркеров по scope

use Seiger\sTask\Models\sWorker;

// Получить только sCommerce воркеров
$commerceWorkers = sWorker::byScope('scommerce')
->active()
->ordered()
->get();

foreach ($commerceWorkers as $worker) {
echo "{$worker->identifier}: {$worker->title}\n";
}

Активировать/деактивировать воркеров

// Активировать воркер
sTask::activateWorker('product');
echo "Product воркер активирован\n";

// Деактивировать воркер
sTask::deactivateWorker('old_import');
echo "Old import воркер деактивирован\n";

// Или напрямую через модель
$worker = sWorker::where('identifier', 'product')->first();
$worker->active = true;
$worker->save();

Статистика задач

// Получить комплексную статистику
$stats = sTask::getStats();

echo "Статистика задач:\n";
echo " Ожидают: {$stats['pending']}\n";
echo " Выполняются: {$stats['running']}\n";
echo " Завершены: {$stats['completed']}\n";
echo " Неудачные: {$stats['failed']}\n";
echo " Отменены: {$stats['cancelled']}\n";
echo " Всего: {$stats['total']}\n";

// Получить детали ожидающих задач
$pending = sTask::getPendingTasks(limit: 10);

echo "\nОжидающие задачи:\n";
foreach ($pending as $task) {
echo " #{$task->id}: {$task->identifier} -> {$task->action}\n";
echo " Приоритет: {$task->priority}\n";
echo " Создано: {$task->created_at->diffForHumans()}\n";
}

Операции очистки

Очистить старые задачи

// Удалить завершенные задачи старше 30 дней
$deletedTasks = sTask::cleanOldTasks(days: 30);
echo "Удалено {$deletedTasks} старых задач\n";

// Собственная очистка
use Seiger\sTask\Models\sTaskModel;

// Удалить неудачные задачи старше 7 дней
$deleted = sTaskModel::failed()
->where('finished_at', '<', now()->subDays(7))
->delete();

echo "Удалено {$deleted} старых неудачных задач\n";

Очистить старые логи

// Удалить файлы логов старше 30 дней
$deletedLogs = sTask::cleanOldLogs(days: 30);
echo "Удалено {$deletedLogs} старых файлов логов\n";

// Очистить логи конкретной задачи
$task = sTaskModel::find(1);
$task->clearLogs();
echo "Логи очищены для задачи #{$task->id}\n";

Artisan команды

Обнаружить воркеров

# Базовое обнаружение
php artisan stask:discover-workers

# Обнаружить с пересканированием
php artisan stask:discover-workers --rescan

# Обнаружить с очисткой
php artisan stask:discover-workers --clean

# Обнаружить с обеими опциями
php artisan stask:discover-workers --rescan --clean

Что делает:

  • --rescan - Обновляет метаданные для существующих воркеров
  • --clean - Удаляет orphaned воркеры (классы больше не существуют)

Опубликовать ресурсы

Обработчик задач автоматически выполняется системой через cron job каждую минуту. Вам не нужно запускать его вручную.

Что делает:

  • Обрабатывает все ожидающие задачи в очереди
  • Выполняет задачи через соответствующие воркеры
  • Обновляет прогресс и статус задач
  • Очищает старые файлы прогресса при простое
  • Должен быть настроен через cron для непрерывной обработки (см. раздел Настройка)

Примечание: Воркеры автоматически обнаруживаются при открытии вкладки Воркеры в админ-панели. Ручное обнаружение не требуется!

Проверка конфигурации

Если вы разрабатываете собственный пакет, который интегрируется с sTask, проверьте установлен ли sTask:

// Проверить установлен ли sTask
if (evo()->getConfig('check_sTask', false)) {
// sTask доступен
$task = \Seiger\sTask\Facades\sTask::create(
identifier: 'my_worker',
action: 'process',
data: []
);
} else {
// sTask не установлен, используйте fallback
$this->processDirectly();
}

Вы также можете проверить версию:

$version = evo()->getConfig('sTaskVer', 'unknown');
echo "Версия sTask: {$version}\n";

Структура хранилища

sTask создает следующую структуру хранилища:

storage/
└── stask/
├── 1.log # Логи задачи #1
├── 2.log # Логи задачи #2
├── 3.log # Логи задачи #3
├── 1.json # Снимок прогресса задачи #1
├── 2.json # Снимок прогресса задачи #2
├── .gc_progress # Маркер сборки мусора
└── ...

Файлы логов (*.log):

  • Содержат детальные логи выполнения
  • Включают временные метки, уровни (info/warning/error), сообщения
  • Автоматически очищаются после настроенного периода

Файлы прогресса (*.json):

  • Снимки прогресса в реальном времени
  • Используются для мониторинга прогресса в админ интерфейсе
  • Автоматически очищаются через 24 часа

Таблицы базы данных

Таблица s_workers

Хранит конфигурации воркеров:

КолонкаТипОписание
idBIGINTПервичный ключ
uuidUUIDID интеграции внешней системы
identifierVARCHARУникальный идентификатор воркера
scopeVARCHARScope модуля/пакета
classVARCHARНазвание класса воркера
activeBOOLEANСтатус активности
positionINTПорядок отображения
settingsJSONНастройки воркера
hiddenINTФлаг видимости
// Примеры запросов
use Seiger\sTask\Models\sWorker;

// Получить активных воркеров
$active = sWorker::active()->get();

// Получить воркеров по scope
$commerce = sWorker::byScope('scommerce')->get();

// Получить упорядоченных воркеров
$ordered = sWorker::ordered()->get();

Таблица s_tasks

Хранит записи задач:

КолонкаТипОписание
idBIGINTПервичный ключ
identifierVARCHARИдентификатор воркера
actionVARCHARНазвание действия
statusINTСтатус выполнения (10-50)
messageVARCHARСообщение статуса
started_byINTID пользователя
metaJSONДанные задачи
resultTEXTДанные результата/путь к файлу
start_atTIMESTAMPЗапланированный старт
finished_atTIMESTAMPВремя завершения
attemptsINTПопытки выполнения
max_attemptsINTМаксимум попыток
priorityVARCHARПриоритет задачи
progressINTПроцент прогресса
// Примеры запросов
use Seiger\sTask\Models\sTaskModel;

// Получить ожидающие задачи
$pending = sTaskModel::pending()->get();

// Получить выполняющиеся задачи
$running = sTaskModel::running()->get();

// Получить завершенные задачи
$completed = sTaskModel::completed()->get();

// Получить неудачные задачи
$failed = sTaskModel::failed()->get();

// Получить задачи по идентификатору
$productTasks = sTaskModel::byIdentifier('product')->get();

// Получить задачи по действию
$imports = sTaskModel::byAction('import')->get();

// Получить высокоприоритетные задачи
$urgent = sTaskModel::highPriority()->get();

Следующие шаги

Решение проблем

Задачи не обрабатываются

  1. Проверьте активны ли воркеры:
$worker = \Seiger\sTask\Models\sWorker::where('identifier', 'product')->first();
if (!$worker->active) {
echo "Воркер неактивен!\n";
}
  1. Проверьте статус задачи:
$task = \Seiger\sTask\Models\sTaskModel::find(1);
echo "Статус: {$task->status_text}\n";
echo "Сообщение: {$task->message}\n";
  1. Проверьте логи:
$logs = $task->getErrorLogs();
foreach ($logs as $log) {
echo $log['message'] . "\n";
}

Воркеры не обнаружены

  1. Запустите обнаружение вручную:
php artisan stask:discover-workers --clean
  1. Проверьте реализует ли класс TaskInterface:
class MyWorker implements \Seiger\sTask\Contracts\TaskInterface
{
// ...
}
  1. Проверьте что ваш воркер находится в валидном Composer пакете и не в системных пространствах имен (Illuminate, Symfony, и т.д.)

Проблемы с правами доступа

Если вы получаете ошибки прав доступа для storage:

chmod -R 755 storage/stask
chown -R www-data:www-data storage/stask

Или через PHP:

$path = storage_path('stask');
if (!is_writable($path)) {
chmod($path, 0755);
}