Squashed commit message
Some checks failed
Telegram Mini App Shop Builder / Compute version metadata (push) Has been cancelled
Telegram Mini App Shop Builder / Run Frontend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run Backend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run PHP_CodeSniffer (push) Has been cancelled
Telegram Mini App Shop Builder / Build module. (push) Has been cancelled
Telegram Mini App Shop Builder / release (push) Has been cancelled
Some checks failed
Telegram Mini App Shop Builder / Compute version metadata (push) Has been cancelled
Telegram Mini App Shop Builder / Run Frontend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run Backend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run PHP_CodeSniffer (push) Has been cancelled
Telegram Mini App Shop Builder / Build module. (push) Has been cancelled
Telegram Mini App Shop Builder / release (push) Has been cancelled
This commit is contained in:
43
backend/src/console/Commands/CacheClearCommand.php
Normal file
43
backend/src/console/Commands/CacheClearCommand.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Console\Commands;
|
||||
|
||||
use Acme\ECommerceFramework\Cache\CacheInterface;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
class CacheClearCommand extends AcmeShopCommand
|
||||
{
|
||||
protected static $defaultName = 'cache:clear';
|
||||
protected static $defaultDescription = 'Очистка кеша модуля AcmeShop';
|
||||
|
||||
private CacheInterface $cache;
|
||||
|
||||
public function __construct(CacheInterface $cache)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$io->title('Очистка кеша модуля AcmeShop');
|
||||
|
||||
try {
|
||||
$this->cache->clear();
|
||||
$io->success('Кеш успешно очищен!');
|
||||
|
||||
return Command::SUCCESS;
|
||||
} catch (\Exception $e) {
|
||||
$io->error('Ошибка при очистке кеша: ' . $e->getMessage());
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
43
backend/src/console/Commands/CustomerCountsCommand.php
Executable file
43
backend/src/console/Commands/CustomerCountsCommand.php
Executable file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Console\Commands;
|
||||
|
||||
use Acme\ECommerceFramework\QueryBuilder\Connections\ConnectionInterface;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
class CustomerCountsCommand extends AcmeShopCommand
|
||||
{
|
||||
protected static $defaultName = 'customer:counts';
|
||||
protected static $defaultDescription = 'Обновление счетчиков заказов для всех клиентов';
|
||||
|
||||
private ConnectionInterface $database;
|
||||
|
||||
public function __construct(ConnectionInterface $database)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->database = $database;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$io->title('Обновление счетчиков заказов клиентов');
|
||||
$io->writeln('Выполняется пересчёт счетчиков заказов...');
|
||||
|
||||
$sql = <<<SQL
|
||||
update acmeshop_customers
|
||||
set orders_count = (select count(*) from oc_order where oc_order.customer_id = acmeshop_customers.oc_customer_id)
|
||||
where true;
|
||||
SQL;
|
||||
|
||||
$this->database->statement($sql);
|
||||
|
||||
$io->success('Счетчики заказов успешно обновлены!');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
156
backend/src/console/Commands/ImagesCacheClearCommand.php
Executable file
156
backend/src/console/Commands/ImagesCacheClearCommand.php
Executable file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace Console\Commands;
|
||||
|
||||
use Acme\ECommerceFramework\Container\Container;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
class ImagesCacheClearCommand extends AcmeShopCommand
|
||||
{
|
||||
protected static $defaultName = 'images:cache-clear';
|
||||
protected static $defaultDescription = 'Очистка кеша изображений товаров';
|
||||
|
||||
private Container $container;
|
||||
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$io->title('Очистка кеша изображений товаров');
|
||||
|
||||
// Получаем пути из конфига
|
||||
$imagesDir = $this->container->getConfigValue('paths.images');
|
||||
$cachePath = $this->container->getConfigValue('paths.images_cache', 'cache/acmeshop');
|
||||
$cachePath = ltrim($cachePath, '/');
|
||||
$fullCachePath = rtrim($imagesDir, '/') . '/' . $cachePath;
|
||||
|
||||
if (!is_dir($fullCachePath)) {
|
||||
$io->warning("Директория кеша не существует: {$fullCachePath}");
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
$io->section('Информация');
|
||||
$io->listing([
|
||||
"Директория изображений: {$imagesDir}",
|
||||
"Путь кеша: {$cachePath}",
|
||||
"Полный путь кеша: {$fullCachePath}",
|
||||
]);
|
||||
|
||||
// Подсчитываем файлы перед удалением
|
||||
$fileCount = 0;
|
||||
$totalSize = 0;
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($fullCachePath, RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isFile()) {
|
||||
$fileCount++;
|
||||
$totalSize += $file->getSize();
|
||||
}
|
||||
}
|
||||
|
||||
if ($fileCount === 0) {
|
||||
$io->info('Кеш пуст, нечего очищать.');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
$io->section('Статистика перед очисткой');
|
||||
$io->listing([
|
||||
"Файлов: {$fileCount}",
|
||||
"Размер: " . $this->formatBytes($totalSize),
|
||||
]);
|
||||
|
||||
// Запрашиваем подтверждение
|
||||
if (!$io->confirm('Вы уверены, что хотите удалить все файлы из кеша?', false)) {
|
||||
$io->info('Очистка кеша отменена.');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
// Удаляем файлы и директории
|
||||
$deletedFiles = 0;
|
||||
$deletedDirs = 0;
|
||||
$errors = 0;
|
||||
|
||||
$progressBar = $io->createProgressBar($fileCount);
|
||||
$progressBar->setFormat(' %current%/%max% [%bar%] %percent:3s%% %message%');
|
||||
$progressBar->setMessage('Удаление файлов...');
|
||||
$progressBar->start();
|
||||
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($fullCachePath, RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
try {
|
||||
if ($file->isFile()) {
|
||||
if (@unlink($file->getPathname())) {
|
||||
$deletedFiles++;
|
||||
} else {
|
||||
$errors++;
|
||||
}
|
||||
$progressBar->advance();
|
||||
} elseif ($file->isDir()) {
|
||||
if (@rmdir($file->getPathname())) {
|
||||
$deletedDirs++;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$errors++;
|
||||
}
|
||||
}
|
||||
|
||||
// Удаляем саму директорию кеша, если она пуста
|
||||
if (is_dir($fullCachePath)) {
|
||||
@rmdir($fullCachePath);
|
||||
}
|
||||
|
||||
$progressBar->setMessage('Завершено');
|
||||
$progressBar->finish();
|
||||
$io->newLine(2);
|
||||
|
||||
// Выводим статистику
|
||||
$io->section('Результаты');
|
||||
$io->table(
|
||||
['Метрика', 'Значение'],
|
||||
[
|
||||
['Удалено файлов', $deletedFiles],
|
||||
['Удалено директорий', $deletedDirs],
|
||||
['Ошибок', $errors],
|
||||
]
|
||||
);
|
||||
|
||||
if ($errors > 0) {
|
||||
$io->warning("Обнаружено {$errors} ошибок при удалении файлов.");
|
||||
} else {
|
||||
$io->success('Кеш изображений успешно очищен!');
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
private function formatBytes(int $bytes, int $precision = 2): string
|
||||
{
|
||||
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
|
||||
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
|
||||
$bytes /= 1024;
|
||||
}
|
||||
|
||||
return round($bytes, $precision) . ' ' . $units[$i];
|
||||
}
|
||||
}
|
||||
|
||||
242
backend/src/console/Commands/ImagesWarmupCacheCommand.php
Executable file
242
backend/src/console/Commands/ImagesWarmupCacheCommand.php
Executable file
@@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
namespace Console\Commands;
|
||||
|
||||
use App\Services\SettingsService;
|
||||
use Exception;
|
||||
use Acme\ECommerceFramework\ImageTool\ImageFactory;
|
||||
use Acme\ECommerceFramework\ImageTool\ImageUtils;
|
||||
use Acme\ECommerceFramework\QueryBuilder\Builder;
|
||||
use Acme\ECommerceFramework\QueryBuilder\JoinClause;
|
||||
use Acme\ECommerceFramework\Support\Arr;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
class ImagesWarmupCacheCommand extends AcmeShopCommand
|
||||
{
|
||||
protected static $defaultName = 'images:warmup';
|
||||
protected static $defaultDescription = 'Прогрев кеша изображений товаров';
|
||||
|
||||
private Builder $queryBuilder;
|
||||
private ImageFactory $image;
|
||||
private SettingsService $settings;
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct(
|
||||
Builder $queryBuilder,
|
||||
ImageFactory $image,
|
||||
SettingsService $settings,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
parent::__construct();
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
$this->image = $image;
|
||||
$this->settings = $settings;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument(
|
||||
'product_id',
|
||||
InputArgument::OPTIONAL,
|
||||
'ID товара для прогрева кеша (если не указан, прогреваются все товары)'
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$productId = $input->getArgument('product_id');
|
||||
|
||||
$io->title('Прогрев кеша изображений товаров');
|
||||
|
||||
// Получаем настройки
|
||||
$aspectRatio = $this->settings->get('app.image_aspect_ratio', '1:1');
|
||||
$cropAlgorithm = $this->settings->get('app.image_crop_algorithm', 'cover');
|
||||
[$imageWidth, $imageHeight] = ImageUtils::aspectRatioToSize($aspectRatio);
|
||||
$languageId = $this->settings->config()->getApp()->getLanguageId();
|
||||
|
||||
$io->section('Настройки');
|
||||
$io->listing([
|
||||
"Соотношение сторон: {$aspectRatio}",
|
||||
"Алгоритм обрезки: {$cropAlgorithm}",
|
||||
"Размер изображения: {$imageWidth}x{$imageHeight}",
|
||||
"Размер миниатюры: 500x500",
|
||||
"Размер большого изображения: 1000x1000",
|
||||
]);
|
||||
|
||||
// Получаем список товаров
|
||||
$products = $this->getProducts($productId, $languageId);
|
||||
|
||||
if (empty($products)) {
|
||||
$io->warning('Товары не найдены');
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
$totalProducts = count($products);
|
||||
$io->section("Найдено товаров: {$totalProducts}");
|
||||
|
||||
$stats = [
|
||||
'products' => 0,
|
||||
'main_images' => 0,
|
||||
'additional_images' => 0,
|
||||
'thumbnails' => 0,
|
||||
'large_images' => 0,
|
||||
'errors' => 0,
|
||||
];
|
||||
|
||||
$progressBar = $io->createProgressBar($totalProducts);
|
||||
$progressBar->setFormat(' %current%/%max% [%bar%] %percent:3s%% %message%');
|
||||
$progressBar->setMessage('Обработка товаров...');
|
||||
$progressBar->start();
|
||||
|
||||
foreach ($products as $product) {
|
||||
$productId = $product['product_id'];
|
||||
$productName = $product['product_name'] ?? "ID: {$productId}";
|
||||
$progressBar->setMessage("Товар: {$productName}");
|
||||
|
||||
try {
|
||||
// Прогреваем основное изображение товара
|
||||
if (!empty($product['product_image'])) {
|
||||
try {
|
||||
$this->image->make($product['product_image'])
|
||||
->crop($cropAlgorithm, $imageWidth, $imageHeight)
|
||||
->url();
|
||||
$stats['main_images']++;
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error("Ошибка при прогреве основного изображения товара {$productId}: " . $e->getMessage());
|
||||
$stats['errors']++;
|
||||
}
|
||||
}
|
||||
|
||||
// Получаем дополнительные изображения товара
|
||||
$additionalImages = $this->getProductAdditionalImages($productId);
|
||||
$processedAdditional = 0;
|
||||
foreach ($additionalImages as $imagePath) {
|
||||
if ($processedAdditional >= 2) {
|
||||
break; // Ограничиваем до 2 дополнительных изображений, как в ProductsService
|
||||
}
|
||||
try {
|
||||
$this->image->make($imagePath)
|
||||
->crop($cropAlgorithm, $imageWidth, $imageHeight)
|
||||
->url();
|
||||
$stats['additional_images']++;
|
||||
$processedAdditional++;
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error("Ошибка при прогреве дополнительного изображения товара {$productId}: " . $e->getMessage());
|
||||
$stats['errors']++;
|
||||
}
|
||||
}
|
||||
|
||||
// Прогреваем изображения для детальной страницы (миниатюры и большие)
|
||||
$allImages = [];
|
||||
if (!empty($product['product_image'])) {
|
||||
$allImages[] = $product['product_image'];
|
||||
}
|
||||
$allImages = array_merge($allImages, $additionalImages);
|
||||
|
||||
foreach ($allImages as $imagePath) {
|
||||
try {
|
||||
// Миниатюра
|
||||
$this->image->make($imagePath)
|
||||
->contain(500, 500)
|
||||
->url();
|
||||
$stats['thumbnails']++;
|
||||
|
||||
// Большое изображение
|
||||
$this->image->make($imagePath)
|
||||
->resize(1000, 1000)
|
||||
->url();
|
||||
$stats['large_images']++;
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error("Ошибка при прогреве изображений для детальной страницы товара {$productId}: " . $e->getMessage());
|
||||
$stats['errors']++;
|
||||
}
|
||||
}
|
||||
|
||||
$stats['products']++;
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error("Ошибка при обработке товара {$productId}: " . $e->getMessage());
|
||||
$stats['errors']++;
|
||||
}
|
||||
|
||||
$progressBar->advance();
|
||||
}
|
||||
|
||||
$progressBar->setMessage('Завершено');
|
||||
$progressBar->finish();
|
||||
$io->newLine(2);
|
||||
|
||||
// Выводим статистику
|
||||
$io->section('Статистика');
|
||||
$io->table(
|
||||
['Метрика', 'Значение'],
|
||||
[
|
||||
['Обработано товаров', $stats['products']],
|
||||
['Основных изображений', $stats['main_images']],
|
||||
['Дополнительных изображений', $stats['additional_images']],
|
||||
['Миниатюр (500x500)', $stats['thumbnails']],
|
||||
['Больших изображений (1000x1000)', $stats['large_images']],
|
||||
['Ошибок', $stats['errors']],
|
||||
]
|
||||
);
|
||||
|
||||
if ($stats['errors'] > 0) {
|
||||
$io->warning("Обнаружено {$stats['errors']} ошибок. Проверьте логи для подробностей.");
|
||||
} else {
|
||||
$io->success('Кеш изображений успешно прогрет!');
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает список товаров для прогрева кеша
|
||||
*/
|
||||
private function getProducts(?string $productId, int $languageId): array
|
||||
{
|
||||
$query = $this->queryBuilder->newQuery()
|
||||
->select([
|
||||
'products.product_id' => 'product_id',
|
||||
'products.image' => 'product_image',
|
||||
'product_description.name' => 'product_name',
|
||||
])
|
||||
->from(db_table('product'), 'products')
|
||||
->join(
|
||||
db_table('product_description') . ' AS product_description',
|
||||
function (JoinClause $join) use ($languageId) {
|
||||
$join->on('products.product_id', '=', 'product_description.product_id')
|
||||
->where('product_description.language_id', '=', $languageId);
|
||||
}
|
||||
)
|
||||
->where('products.status', '=', 1)
|
||||
->whereRaw('products.date_available < NOW()');
|
||||
|
||||
if ($productId !== null) {
|
||||
$query->where('products.product_id', '=', (int) $productId);
|
||||
}
|
||||
|
||||
return $query->orderBy('products.product_id', 'ASC')->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Получает дополнительные изображения товара
|
||||
*/
|
||||
private function getProductAdditionalImages(int $productId): array
|
||||
{
|
||||
$images = $this->queryBuilder->newQuery()
|
||||
->select(['products_images.image' => 'image'])
|
||||
->from(db_table('product_image'), 'products_images')
|
||||
->where('products_images.product_id', '=', $productId)
|
||||
->orderBy('products_images.sort_order')
|
||||
->get();
|
||||
|
||||
return Arr::pluck($images, 'image');
|
||||
}
|
||||
}
|
||||
|
||||
29
backend/src/console/Commands/PulseSendEventsCommand.php
Executable file
29
backend/src/console/Commands/PulseSendEventsCommand.php
Executable file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Console\Commands;
|
||||
|
||||
use Bastion\ScheduledTasks\AcmeShopPulseSendEventsTask;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class PulseSendEventsCommand extends AcmeShopCommand
|
||||
{
|
||||
protected static $defaultName = 'pulse:send';
|
||||
protected static $defaultDescription = 'Manually send pulse events ignoring schedule.';
|
||||
private AcmeShopPulseSendEventsTask $megaPayPulseSendEventsTask;
|
||||
|
||||
public function __construct(AcmeShopPulseSendEventsTask $megaPayPulseSendEventsTask)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->megaPayPulseSendEventsTask = $megaPayPulseSendEventsTask;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln('<info>Sending Pulse events.</info>');
|
||||
$this->megaPayPulseSendEventsTask->execute();
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
80
backend/src/console/Commands/ScheduleRunCommand.php
Executable file
80
backend/src/console/Commands/ScheduleRunCommand.php
Executable file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Console\Commands;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Acme\ECommerceFramework\Config\Settings;
|
||||
use Acme\ECommerceFramework\Scheduler\SchedulerService;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ScheduleRunCommand extends AcmeShopCommand
|
||||
{
|
||||
private SchedulerService $scheduler;
|
||||
private Settings $settings;
|
||||
|
||||
protected static $defaultName = 'schedule:run';
|
||||
protected static $defaultDescription = 'Run scheduled commands';
|
||||
|
||||
public function __construct(SchedulerService $scheduler, Settings $settings)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->scheduler = $scheduler;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addOption(
|
||||
'ignore-global-lock',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'Ignore global scheduler lock (e.g. when running multiple cron instances)'
|
||||
);
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$mode = $this->settings->get('cron.mode', 'disabled');
|
||||
if ($mode !== 'system') {
|
||||
$output->writeln('<comment>Scheduler not in CRON mode. Skipping CLI execution.</comment>');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
$output->writeln(
|
||||
sprintf(
|
||||
'[%s] <info>AcmeShop Scheduler Running...</info>',
|
||||
Carbon::now()->toJSON(),
|
||||
)
|
||||
);
|
||||
|
||||
$ignoreGlobalLock = (bool) $input->getOption('ignore-global-lock');
|
||||
$result = $this->scheduler->run($ignoreGlobalLock);
|
||||
|
||||
// Print Executed
|
||||
if (empty($result->executed)) {
|
||||
$output->writeln('No tasks executed.');
|
||||
} else {
|
||||
foreach ($result->executed as $item) {
|
||||
$output->writeln(sprintf('<info>Executed:</info> %s (%.4fs)', $item['name'], $item['duration']));
|
||||
}
|
||||
}
|
||||
|
||||
// Print Failed
|
||||
foreach ($result->failed as $item) {
|
||||
$output->writeln(sprintf('<error>Failed:</error> %s - %s', $item['name'], $item['error']));
|
||||
}
|
||||
|
||||
// Print Skipped (verbose only)
|
||||
if ($output->isVerbose()) {
|
||||
foreach ($result->skipped as $item) {
|
||||
$output->writeln(sprintf('<comment>Skipped:</comment> %s - %s', $item['name'], $item['reason']));
|
||||
}
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
13
backend/src/console/Commands/TeleCartCommand.php
Executable file
13
backend/src/console/Commands/TeleCartCommand.php
Executable file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
|
||||
abstract class AcmeShopCommand extends Command
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
||||
19
backend/src/console/Commands/VersionCommand.php
Executable file
19
backend/src/console/Commands/VersionCommand.php
Executable file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Console\Commands;
|
||||
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class VersionCommand extends AcmeShopCommand
|
||||
{
|
||||
protected static $defaultName = 'version';
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln('AcmeShop Version: ' . module_version());
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user