From ca0bf11b55824cd939f5435efc25207cb3b7fd87 Mon Sep 17 00:00:00 2001 From: ASUS <515617283@qq.com> Date: Fri, 15 Aug 2025 10:06:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=91=BD=E4=BB=A4=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/command/Logzip.php | 76 ++++ app/command/OrderTask.php | 62 ++++ app/command/SpinningTask3.php | 71 ++++ app/command/TempCashOutTask.php | 89 +++++ app/command/TempOrderTask.php | 106 ++++++ composer.json | 3 +- composer.lock | 514 ++++++++++++++++++++++++++- config/plugin/webman/console/app.php | 24 ++ webman | 73 ++++ 9 files changed, 1016 insertions(+), 2 deletions(-) create mode 100644 app/command/Logzip.php create mode 100644 app/command/OrderTask.php create mode 100644 app/command/SpinningTask3.php create mode 100644 app/command/TempCashOutTask.php create mode 100644 app/command/TempOrderTask.php create mode 100644 config/plugin/webman/console/app.php create mode 100644 webman diff --git a/app/command/Logzip.php b/app/command/Logzip.php new file mode 100644 index 0000000..18992ac --- /dev/null +++ b/app/command/Logzip.php @@ -0,0 +1,76 @@ +addArgument('name', InputArgument::OPTIONAL, 'Name description'); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return int + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $dir_arr = [ + 'runtime/admin/log/', + 'runtime/api/log/', + 'runtime/czg/log/', + 'runtime/log/', + ]; + $yestermonth = date("Ym"); + // 如果今天是1号 + if(date('d') == 01 || date('d') == 1) { + $yestermonth = date("Ym", strtotime('-1 month')); + } + foreach ($dir_arr as $dir_a) { + $file_arr = getYesterdayFiles($dir_a . $yestermonth . '/'); + if(!empty($file_arr['files'])) { + foreach ($file_arr['files'] as $k => $file_p) { + $file = $dir_a . $yestermonth . '/' . $file_p; + $zip_filename = str_replace('.log', '.zip', $file_p); + $file_zip = $dir_a . $yestermonth . '/' . $zip_filename; + $is_close = false; + if(!file_exists($file_zip)) { + $zip = new ZipArchive(); + if($zip->open($file_zip, ZipArchive::CREATE) === TRUE) { + if(file_exists($file)) { + if($zip->addFile($file, $file_p)) { + $is_close = true; + print_r($file . "\r\n"); + } + } + if ($is_close) { + if($zip->close()) { + if($is_close) { + unlink($file); + } + } + } + } + } + } + } + } + $output->writeln('ok'); + return self::SUCCESS; + } + +} diff --git a/app/command/OrderTask.php b/app/command/OrderTask.php new file mode 100644 index 0000000..db33634 --- /dev/null +++ b/app/command/OrderTask.php @@ -0,0 +1,62 @@ +addArgument('name', InputArgument::OPTIONAL, 'Name description'); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return int + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + try { + Log::info("未支付订单删除开始"); + // 获取3小时前的时间 + $threeHoursAgo = date('Y-m-d H:i:s', strtotime('-3 hours')); + // 开启事务确保数据一致性 + DatabaseRoute::transactionXa(function () use ($threeHoursAgo, $output) { + $deletedOrders = DatabaseRoute::deleteAllDbDirect('orders', function ($query) use ($threeHoursAgo) { + return $query->where('create_time', '<=', $threeHoursAgo) + ->where('status', '<>', 1); + }); + Log::info("删除了 {$deletedOrders} 条未支付订单"); + $deletedPayments = DatabaseRoute::deleteAllDbDirect('pay_details', function ($query) use ($threeHoursAgo) { + return $query->where('create_time', '<=', $threeHoursAgo) + ->where('state', '<>', 1); + }); + Log::info("删除了 {$deletedPayments} 条未支付支付记录"); + $output->writeln("清理完成: 删除 {$deletedOrders} 个订单, {$deletedPayments} 个支付记录"); + }); + } catch (\Exception $e) { + Log::error("未支付订单清理失败: " . $e->getMessage()); + $output->writeln("清理失败: " . $e->getMessage() . ""); + return 1; // 返回错误码 + } + // 返回成功码 + $output->writeln(0); + return self::SUCCESS; + } + +} diff --git a/app/command/SpinningTask3.php b/app/command/SpinningTask3.php new file mode 100644 index 0000000..0536372 --- /dev/null +++ b/app/command/SpinningTask3.php @@ -0,0 +1,71 @@ +addArgument('name', InputArgument::OPTIONAL, 'Name description'); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return int + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $params = 1; + if($input->hasOption('params')) { + $params = $input->getOption('params'); + } + // 计算时间范围(当前时间 - N*5分钟 到 当前时间 - (N*5+15)分钟) + $now = time(); + $offsetMinutes = (int)$params * -5; // N*5分钟前 + $fiveMinutesAgo = date('Y-m-d H:i:s', strtotime("{$offsetMinutes} minutes", $now)); + $tenMinutesAgo = date('Y-m-d H:i:s', strtotime("-15 minutes", strtotime($fiveMinutesAgo))); + Log::info("大转盘到账补偿时间范围:{$tenMinutesAgo}-----{$fiveMinutesAgo}"); + $list = DatabaseRoute::getAllDbData('disc_spinning_record', function ($query) use($fiveMinutesAgo, $tenMinutesAgo) { + return $query->whereNull('target') + ->whereNull('target_id') + ->where('type', 2) + ->where('create_time', '>=', $tenMinutesAgo) // 大于等于(N*5+15)分钟前 + ->where('create_time', '<=', $fiveMinutesAgo); + })->select(); + if($list) { + $list = $list->toArray(); + Log::info('需要补偿的总条数' . count($list)); + if(count($list) > 0) { + // 推进队列 + $this->execAsync($list); + } + } + $output->writeln("大转盘到账补偿机制结束"); + Log::write("大转盘到账补偿机制结束"); + $output->writeln('Hello SpinningTask3'); + return self::SUCCESS; + } + public function execAsync($list) + { + foreach ($list as $k => $value) { + pushQueue(DiscCompensateJob::class, $value); + } + } +} diff --git a/app/command/TempCashOutTask.php b/app/command/TempCashOutTask.php new file mode 100644 index 0000000..fd66c2a --- /dev/null +++ b/app/command/TempCashOutTask.php @@ -0,0 +1,89 @@ +addArgument('name', InputArgument::OPTIONAL, 'Name description'); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return int + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + try { + $time = date('Y-m-d H:i:s', strtotime('-15 minutes')); + + // 查询待处理的提现订单(你需要根据你的业务逻辑调整 where 条件) + $cashOuts = DatabaseRoute::getAllDbData('cash_out', function ($query) use ($time) { + return $query->where([ + ['state', 'in', [0, 4]], + ['user_type', '=', 1], + ['create_at', '<', $time], + ['order_number', '!=', ''], + ])->whereNotNull('order_number'); + })->select()->toArray(); + + Log::info('定时查询提现订单 待处理订单: ' . count($cashOuts)); + + $sucOrderList = []; + $failOrderList = []; + + foreach ($cashOuts as $cashOut) { + try { + // 调用支付平台接口(需你自定义服务类) + $baseResp = WuYouPayUtils::queryExtractOrder( + $cashOut['order_number'], + $cashOut['user_id'], + $cashOut['user_type'] != 2, + $cashOut['money'] + ); + + // 执行回调(需你自定义服务类) + $result = Order::executeExtractCallback($cashOut, $baseResp); + + if ($result === 1) { + $sucOrderList[] = $cashOut['order_number']; + } else { + $failOrderList[] = $cashOut['order_number']; + } + + } catch (\Throwable $e) { + Log::error('提现定时任务查询出错: ' . $e->getMessage()); + } + } + + Log::info('定时查询提现订单 提现结束, 成功:' . count($sucOrderList) . '条, 失败:' . count($failOrderList) . '条'); + Log::info('定时查询提现订单 成功:' . json_encode($sucOrderList) . ', 失败:' . json_encode($failOrderList)); + } catch (\Exception $e) { + Log::error("定时查询提现订单失败: " . $e->getMessage()); + Log::info($e->getTraceAsString()); + $output->writeln("定时查询提现订单失败: " . $e->getMessage() . ""); + return 1; // 返回错误码 + } + return self::SUCCESS; + } + +} diff --git a/app/command/TempOrderTask.php b/app/command/TempOrderTask.php new file mode 100644 index 0000000..21049d5 --- /dev/null +++ b/app/command/TempOrderTask.php @@ -0,0 +1,106 @@ +addArgument('name', InputArgument::OPTIONAL, 'Name description'); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return int + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + try { + Log::info('订单表数据处理开始'); + + // 查询待处理的支付明细(state=0,create_time 早于 15 分钟前) + $payDetailsList = DatabaseRoute::getAllDbData('pay_details', function ($query) { + return $query + ->where('state', 0) + ->where('create_time', '<', date('Y-m-d H:i:s', strtotime('-15 minutes'))) + ->order('create_time', 'asc') + ->limit(1800); + })->select()->toArray(); + + if (empty($payDetailsList)) { + return 0; + } + + Log::info('待处理数据' . count($payDetailsList) . '条'); + + foreach ($payDetailsList as $details) { + try { + usleep(100 * 1000); // sleep 100ms + + // 根据 orderId 查询 Orders 表 + $order = DatabaseRoute::getAllDbData('orders', function ($query) use ($details) { + return $query->where('orders_no', $details['order_id']) + ->where('user_id', $details['user_id']); + })->find(); + + // 调用支付平台查询订单状态 + $baseResp = WuYouPayUtils::queryOrder( + $details['trade_no'], + $details['user_id'], + (string)$details['money'], + 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X)...' + ); + + Log::info('baseResp: ' . json_encode($baseResp)); + + if (empty($baseResp['code']) || $baseResp['code'] != 200) { + Log::info('code 错误跳过'); + continue; + } + + if (($baseResp['payStatus'] ?? '') === 'SUCCESS' || ($baseResp['payStatus2'] ?? '') === 'SUCCESS') { + Log::info('payDetails: ' . json_encode($details)); + Log::info('order: ' . json_encode($order)); + Orders::updateOrderStatus($details, $order, $order['user_id']); + } else { + Log::info('订单未支付,修改状态: ' . $details['trade_no']); + DatabaseRoute::getDb('orders', $order['user_id'], true, true)->where([ + 'orders_id' => $order['orders_id'] + ])->update([ + 'status' => $order ? 3 : 2 + ]); + } + } catch (\Throwable $e) { + Log::error('订单数据处理异常:' . $e->getMessage()); + } + } + + Log::info('订单表数据处理完毕'); + } catch (\Exception $e) { + Log::error("订单表数据处理失败: " . $e->getMessage()); + Log::info($e->getTraceAsString()); + $output->writeln("订单表数据处理失败: " . $e->getMessage() . ""); + return 1; // 返回错误码 + } + return self::SUCCESS; + } + +} diff --git a/composer.json b/composer.json index 3a8558d..2e9387a 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,8 @@ "firebase/php-jwt": "^6.11", "ext-bcmath": "*", "webman/redis": "^2.1", - "illuminate/events": "^11.45" + "illuminate/events": "^11.45", + "webman/console": "^2.1" }, "suggest": { "ext-event": "For better performance. " diff --git a/composer.lock b/composer.lock index 9716524..8d709cd 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e847739a76e8232a82ad829ae648038b", + "content-hash": "b513a600f1b1dbb2119a8038831ab9e0", "packages": [ { "name": "carbonphp/carbon-doctrine-types", @@ -1573,6 +1573,106 @@ ], "time": "2024-09-25T14:21:43+00:00" }, + { + "name": "symfony/console", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/66c1440edf6f339fd82ed6c7caa76cb006211b44", + "reference": "66c1440edf6f339fd82ed6c7caa76cb006211b44", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v7.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-05-24T10:34:04+00:00" + }, { "name": "symfony/deprecation-contracts", "version": "v3.5.1", @@ -1731,6 +1831,177 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, { "name": "symfony/polyfill-mbstring", "version": "v1.31.0", @@ -1985,6 +2256,188 @@ ], "time": "2024-09-09T11:45:10+00:00" }, + { + "name": "symfony/service-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/string", + "version": "v7.2.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "a214fe7d62bd4df2a76447c67c6b26e1d5e74931" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/a214fe7d62bd4df2a76447c67c6b26e1d5e74931", + "reference": "a214fe7d62bd4df2a76447c67c6b26e1d5e74931", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v7.2.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-04-20T20:18:16+00:00" + }, { "name": "symfony/translation", "version": "v7.3.1", @@ -2561,6 +3014,65 @@ ], "time": "2024-11-21T00:49:12+00:00" }, + { + "name": "webman/console", + "version": "v2.1.3", + "source": { + "type": "git", + "url": "https://github.com/webman-php/console.git", + "reference": "bbee274a5f091eaf90e7a257dd0fbfef47da9a17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webman-php/console/zipball/bbee274a5f091eaf90e7a257dd0fbfef47da9a17", + "reference": "bbee274a5f091eaf90e7a257dd0fbfef47da9a17", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "doctrine/inflector": "^2.0", + "symfony/console": ">=5.0" + }, + "require-dev": { + "workerman/webman": "^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Webman\\Console\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "walkor", + "email": "walkor@workerman.net", + "homepage": "http://www.workerman.net", + "role": "Developer" + } + ], + "description": "Webman console", + "homepage": "http://www.workerman.net", + "keywords": [ + "webman console" + ], + "support": { + "email": "walkor@workerman.net", + "forum": "http://www.workerman.net/questions", + "issues": "https://github.com/webman-php/console/issues", + "source": "https://github.com/webman-php/console", + "wiki": "http://www.workerman.net/doc/webman" + }, + "time": "2025-03-06T08:23:07+00:00" + }, { "name": "webman/redis", "version": "v2.1.2", diff --git a/config/plugin/webman/console/app.php b/config/plugin/webman/console/app.php new file mode 100644 index 0000000..074e986 --- /dev/null +++ b/config/plugin/webman/console/app.php @@ -0,0 +1,24 @@ + true, + + 'build_dir' => BASE_PATH . DIRECTORY_SEPARATOR . 'build', + + 'phar_filename' => 'webman.phar', + + 'bin_filename' => 'webman.bin', + + 'signature_algorithm'=> Phar::SHA256, //set the signature algorithm for a phar and apply it. The signature algorithm must be one of Phar::MD5, Phar::SHA1, Phar::SHA256, Phar::SHA512, or Phar::OPENSSL. + + 'private_key_file' => '', // The file path for certificate or OpenSSL private key file. + + 'exclude_pattern' => '#^(?!.*(composer.json|/.github/|/.idea/|/.git/|/.setting/|/runtime/|/vendor-bin/|/build/|/vendor/webman/admin/))(.*)$#', + + 'exclude_files' => [ + '.env', 'LICENSE', 'composer.json', 'composer.lock', 'start.php', 'webman.phar', 'webman.bin' + ], + + 'custom_ini' => ' +memory_limit = 256M + ', +]; diff --git a/webman b/webman new file mode 100644 index 0000000..f0fa4f9 --- /dev/null +++ b/webman @@ -0,0 +1,73 @@ +#!/usr/bin/env php +setName('webman cli'); +$cli->installInternalCommands(); +if (is_dir($command_path = Util::guessPath(app_path(), '/command', true))) { + $cli->installCommands($command_path); +} + +foreach (config('plugin', []) as $firm => $projects) { + if (isset($projects['app'])) { + foreach (['', '/app'] as $app) { + if ($command_str = Util::guessPath(base_path() . "/plugin/$firm{$app}", 'command')) { + $command_path = base_path() . "/plugin/$firm{$app}/$command_str"; + $cli->installCommands($command_path, "plugin\\$firm" . str_replace('/', '\\', $app) . "\\$command_str"); + } + } + } + foreach ($projects as $name => $project) { + if (!is_array($project)) { + continue; + } + foreach ($project['command'] ?? [] as $class_name) { + $reflection = new \ReflectionClass($class_name); + if ($reflection->isAbstract()) { + continue; + } + $properties = $reflection->getStaticProperties(); + $name = $properties['defaultName']; + if (!$name) { + throw new RuntimeException("Command {$class_name} has no defaultName"); + } + $description = $properties['defaultDescription'] ?? ''; + $command = Container::get($class_name); + $command->setName($name)->setDescription($description); + $cli->add($command); + } + } +} + +$cli->run();