From e467a8e6e0444b8bdbde15dedcd67c9d7e1db5dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E7=BA=A7=E8=80=81=E7=99=BD=E5=85=94?= Date: Thu, 6 Nov 2025 10:20:39 +0800 Subject: [PATCH] Add typeId to login parameters for session verification in Login component. --- Moncter/.gitignore | 8 + Moncter/LICENSE | 21 + Moncter/README.md | 70 ++++ Moncter/app/controller/IndexController.php | 42 ++ Moncter/app/functions.php | 4 + Moncter/app/middleware/StaticFile.php | 42 ++ Moncter/app/model/Test.php | 29 ++ Moncter/app/process/Http.php | 10 + Moncter/app/process/Monitor.php | 305 ++++++++++++++ Moncter/app/view/index/view.html | 14 + Moncter/composer.json | 55 +++ Moncter/composer.lock | 457 +++++++++++++++++++++ Moncter/config/app.php | 26 ++ Moncter/config/autoload.php | 21 + Moncter/config/bootstrap.php | 17 + Moncter/config/container.php | 15 + Moncter/config/dependence.php | 15 + Moncter/config/exception.php | 17 + Moncter/config/log.php | 32 ++ Moncter/config/middleware.php | 15 + Moncter/config/process.php | 62 +++ Moncter/config/route.php | 21 + Moncter/config/server.php | 23 ++ Moncter/config/session.php | 65 +++ Moncter/config/static.php | 23 ++ Moncter/config/translation.php | 25 ++ Moncter/config/view.php | 22 + Moncter/public/favicon.ico | Bin 0 -> 4286 bytes Moncter/start.php | 5 + Moncter/support/Request.php | 24 ++ Moncter/support/Response.php | 24 ++ Moncter/support/bootstrap.php | 139 +++++++ Moncter/windows.bat | 3 + Moncter/windows.php | 136 ++++++ Moncter/技术方案.md | 348 ++++++++++++++++ Touchkebao/src/pages/login/Login.tsx | 1 + 36 files changed, 2136 insertions(+) create mode 100644 Moncter/.gitignore create mode 100644 Moncter/LICENSE create mode 100644 Moncter/README.md create mode 100644 Moncter/app/controller/IndexController.php create mode 100644 Moncter/app/functions.php create mode 100644 Moncter/app/middleware/StaticFile.php create mode 100644 Moncter/app/model/Test.php create mode 100644 Moncter/app/process/Http.php create mode 100644 Moncter/app/process/Monitor.php create mode 100644 Moncter/app/view/index/view.html create mode 100644 Moncter/composer.json create mode 100644 Moncter/composer.lock create mode 100644 Moncter/config/app.php create mode 100644 Moncter/config/autoload.php create mode 100644 Moncter/config/bootstrap.php create mode 100644 Moncter/config/container.php create mode 100644 Moncter/config/dependence.php create mode 100644 Moncter/config/exception.php create mode 100644 Moncter/config/log.php create mode 100644 Moncter/config/middleware.php create mode 100644 Moncter/config/process.php create mode 100644 Moncter/config/route.php create mode 100644 Moncter/config/server.php create mode 100644 Moncter/config/session.php create mode 100644 Moncter/config/static.php create mode 100644 Moncter/config/translation.php create mode 100644 Moncter/config/view.php create mode 100644 Moncter/public/favicon.ico create mode 100644 Moncter/start.php create mode 100644 Moncter/support/Request.php create mode 100644 Moncter/support/Response.php create mode 100644 Moncter/support/bootstrap.php create mode 100644 Moncter/windows.bat create mode 100644 Moncter/windows.php create mode 100644 Moncter/技术方案.md diff --git a/Moncter/.gitignore b/Moncter/.gitignore new file mode 100644 index 00000000..516299c3 --- /dev/null +++ b/Moncter/.gitignore @@ -0,0 +1,8 @@ +/runtime +/.idea +/.vscode +/vendor +*.log +.env +/tests/tmp +/tests/.phpunit.result.cache diff --git a/Moncter/LICENSE b/Moncter/LICENSE new file mode 100644 index 00000000..2c662929 --- /dev/null +++ b/Moncter/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 walkor and contributors (see https://github.com/walkor/webman/contributors) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Moncter/README.md b/Moncter/README.md new file mode 100644 index 00000000..4031784b --- /dev/null +++ b/Moncter/README.md @@ -0,0 +1,70 @@ +
+

webman

+ +基于workerman开发的超高性能PHP框架 + + +

学习

+ + + +
+ +

赞助商

+ +

特别赞助

+ + + + +

铂金赞助

+ + + + +
+ + +
+ +

请作者喝咖啡

+ + + +
+如果您觉得webman对您有所帮助,欢迎捐赠。 + + +
+ + +
+

LICENSE

+The webman is open-sourced software licensed under the MIT. +
+ +
+ + diff --git a/Moncter/app/controller/IndexController.php b/Moncter/app/controller/IndexController.php new file mode 100644 index 00000000..b9b0da79 --- /dev/null +++ b/Moncter/app/controller/IndexController.php @@ -0,0 +1,42 @@ + + * { + padding: 0; + margin: 0; + } + iframe { + border: none; + overflow: scroll; + } + + +EOF; + } + + public function view(Request $request) + { + return view('index/view', ['name' => 'webman']); + } + + public function json(Request $request) + { + return json(['code' => 0, 'msg' => 'ok']); + } + +} diff --git a/Moncter/app/functions.php b/Moncter/app/functions.php new file mode 100644 index 00000000..5c9c58da --- /dev/null +++ b/Moncter/app/functions.php @@ -0,0 +1,4 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace app\middleware; + +use Webman\MiddlewareInterface; +use Webman\Http\Response; +use Webman\Http\Request; + +/** + * Class StaticFile + * @package app\middleware + */ +class StaticFile implements MiddlewareInterface +{ + public function process(Request $request, callable $handler): Response + { + // Access to files beginning with. Is prohibited + if (strpos($request->path(), '/.') !== false) { + return response('

403 forbidden

', 403); + } + /** @var Response $response */ + $response = $handler($request); + // Add cross domain HTTP header + /*$response->withHeaders([ + 'Access-Control-Allow-Origin' => '*', + 'Access-Control-Allow-Credentials' => 'true', + ]);*/ + return $response; + } +} diff --git a/Moncter/app/model/Test.php b/Moncter/app/model/Test.php new file mode 100644 index 00000000..92d70e38 --- /dev/null +++ b/Moncter/app/model/Test.php @@ -0,0 +1,29 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace app\process; + +use FilesystemIterator; +use RecursiveDirectoryIterator; +use RecursiveIteratorIterator; +use SplFileInfo; +use Workerman\Timer; +use Workerman\Worker; + +/** + * Class FileMonitor + * @package process + */ +class Monitor +{ + /** + * @var array + */ + protected array $paths = []; + + /** + * @var array + */ + protected array $extensions = []; + + /** + * @var array + */ + protected array $loadedFiles = []; + + /** + * @var int + */ + protected int $ppid = 0; + + /** + * Pause monitor + * @return void + */ + public static function pause(): void + { + file_put_contents(static::lockFile(), time()); + } + + /** + * Resume monitor + * @return void + */ + public static function resume(): void + { + clearstatcache(); + if (is_file(static::lockFile())) { + unlink(static::lockFile()); + } + } + + /** + * Whether monitor is paused + * @return bool + */ + public static function isPaused(): bool + { + clearstatcache(); + return file_exists(static::lockFile()); + } + + /** + * Lock file + * @return string + */ + protected static function lockFile(): string + { + return runtime_path('monitor.lock'); + } + + /** + * FileMonitor constructor. + * @param $monitorDir + * @param $monitorExtensions + * @param array $options + */ + public function __construct($monitorDir, $monitorExtensions, array $options = []) + { + $this->ppid = function_exists('posix_getppid') ? posix_getppid() : 0; + static::resume(); + $this->paths = (array)$monitorDir; + $this->extensions = $monitorExtensions; + foreach (get_included_files() as $index => $file) { + $this->loadedFiles[$file] = $index; + if (strpos($file, 'webman-framework/src/support/App.php')) { + break; + } + } + if (!Worker::getAllWorkers()) { + return; + } + $disableFunctions = explode(',', ini_get('disable_functions')); + if (in_array('exec', $disableFunctions, true)) { + echo "\nMonitor file change turned off because exec() has been disabled by disable_functions setting in " . PHP_CONFIG_FILE_PATH . "/php.ini\n"; + } else { + if ($options['enable_file_monitor'] ?? true) { + Timer::add(1, function () { + $this->checkAllFilesChange(); + }); + } + } + + $memoryLimit = $this->getMemoryLimit($options['memory_limit'] ?? null); + if ($memoryLimit && ($options['enable_memory_monitor'] ?? true)) { + Timer::add(60, [$this, 'checkMemory'], [$memoryLimit]); + } + } + + /** + * @param $monitorDir + * @return bool + */ + public function checkFilesChange($monitorDir): bool + { + static $lastMtime, $tooManyFilesCheck; + if (!$lastMtime) { + $lastMtime = time(); + } + clearstatcache(); + if (!is_dir($monitorDir)) { + if (!is_file($monitorDir)) { + return false; + } + $iterator = [new SplFileInfo($monitorDir)]; + } else { + // recursive traversal directory + $dirIterator = new RecursiveDirectoryIterator($monitorDir, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS); + $iterator = new RecursiveIteratorIterator($dirIterator); + } + $count = 0; + foreach ($iterator as $file) { + $count ++; + /** var SplFileInfo $file */ + if (is_dir($file->getRealPath())) { + continue; + } + // check mtime + if (in_array($file->getExtension(), $this->extensions, true) && $lastMtime < $file->getMTime()) { + $lastMtime = $file->getMTime(); + if (DIRECTORY_SEPARATOR === '/' && isset($this->loadedFiles[$file->getRealPath()])) { + echo "$file updated but cannot be reloaded because only auto-loaded files support reload.\n"; + continue; + } + $var = 0; + exec('"'.PHP_BINARY . '" -l ' . $file, $out, $var); + if ($var) { + continue; + } + // send SIGUSR1 signal to master process for reload + if (DIRECTORY_SEPARATOR === '/') { + if ($masterPid = $this->getMasterPid()) { + echo $file . " updated and reload\n"; + posix_kill($masterPid, SIGUSR1); + } else { + echo "Master process has gone away and can not reload\n"; + } + return true; + } + echo $file . " updated and reload\n"; + return true; + } + } + if (!$tooManyFilesCheck && $count > 1000) { + echo "Monitor: There are too many files ($count files) in $monitorDir which makes file monitoring very slow\n"; + $tooManyFilesCheck = 1; + } + return false; + } + + /** + * @return int + */ + public function getMasterPid(): int + { + if ($this->ppid === 0) { + return 0; + } + if (function_exists('posix_kill') && !posix_kill($this->ppid, 0)) { + echo "Master process has gone away\n"; + return $this->ppid = 0; + } + if (PHP_OS_FAMILY !== 'Linux') { + return $this->ppid; + } + $cmdline = "/proc/$this->ppid/cmdline"; + if (!is_readable($cmdline) || !($content = file_get_contents($cmdline)) || (!str_contains($content, 'WorkerMan') && !str_contains($content, 'php'))) { + // Process not exist + $this->ppid = 0; + } + return $this->ppid; + } + + /** + * @return bool + */ + public function checkAllFilesChange(): bool + { + if (static::isPaused()) { + return false; + } + foreach ($this->paths as $path) { + if ($this->checkFilesChange($path)) { + return true; + } + } + return false; + } + + /** + * @param $memoryLimit + * @return void + */ + public function checkMemory($memoryLimit): void + { + if (static::isPaused() || $memoryLimit <= 0) { + return; + } + $masterPid = $this->getMasterPid(); + if ($masterPid <= 0) { + echo "Master process has gone away\n"; + return; + } + + $childrenFile = "/proc/$masterPid/task/$masterPid/children"; + if (!is_file($childrenFile) || !($children = file_get_contents($childrenFile))) { + return; + } + foreach (explode(' ', $children) as $pid) { + $pid = (int)$pid; + $statusFile = "/proc/$pid/status"; + if (!is_file($statusFile) || !($status = file_get_contents($statusFile))) { + continue; + } + $mem = 0; + if (preg_match('/VmRSS\s*?:\s*?(\d+?)\s*?kB/', $status, $match)) { + $mem = $match[1]; + } + $mem = (int)($mem / 1024); + if ($mem >= $memoryLimit) { + posix_kill($pid, SIGINT); + } + } + } + + /** + * Get memory limit + * @param $memoryLimit + * @return int + */ + protected function getMemoryLimit($memoryLimit): int + { + if ($memoryLimit === 0) { + return 0; + } + $usePhpIni = false; + if (!$memoryLimit) { + $memoryLimit = ini_get('memory_limit'); + $usePhpIni = true; + } + + if ($memoryLimit == -1) { + return 0; + } + $unit = strtolower($memoryLimit[strlen($memoryLimit) - 1]); + $memoryLimit = (int)$memoryLimit; + if ($unit === 'g') { + $memoryLimit = 1024 * $memoryLimit; + } else if ($unit === 'k') { + $memoryLimit = ($memoryLimit / 1024); + } else if ($unit === 'm') { + $memoryLimit = (int)($memoryLimit); + } else if ($unit === 't') { + $memoryLimit = (1024 * 1024 * $memoryLimit); + } else { + $memoryLimit = ($memoryLimit / (1024 * 1024)); + } + if ($memoryLimit < 50) { + $memoryLimit = 50; + } + if ($usePhpIni) { + $memoryLimit = (0.8 * $memoryLimit); + } + return (int)$memoryLimit; + } + +} diff --git a/Moncter/app/view/index/view.html b/Moncter/app/view/index/view.html new file mode 100644 index 00000000..67ebb26d --- /dev/null +++ b/Moncter/app/view/index/view.html @@ -0,0 +1,14 @@ + + + + + + + + webman + + + +hello + + diff --git a/Moncter/composer.json b/Moncter/composer.json new file mode 100644 index 00000000..d25c083b --- /dev/null +++ b/Moncter/composer.json @@ -0,0 +1,55 @@ +{ + "name": "workerman/webman", + "type": "project", + "keywords": [ + "high performance", + "http service" + ], + "homepage": "https://www.workerman.net", + "license": "MIT", + "description": "High performance HTTP Service Framework.", + "authors": [ + { + "name": "walkor", + "email": "walkor@workerman.net", + "homepage": "https://www.workerman.net", + "role": "Developer" + } + ], + "support": { + "email": "walkor@workerman.net", + "issues": "https://github.com/walkor/webman/issues", + "forum": "https://wenda.workerman.net/", + "wiki": "https://workerman.net/doc/webman", + "source": "https://github.com/walkor/webman" + }, + "require": { + "php": ">=8.1", + "workerman/webman-framework": "^2.1", + "monolog/monolog": "^2.0" + }, + "suggest": { + "ext-event": "For better performance. " + }, + "autoload": { + "psr-4": { + "": "./", + "app\\": "./app", + "App\\": "./app", + "app\\View\\Components\\": "./app/view/components" + } + }, + "scripts": { + "post-package-install": [ + "support\\Plugin::install" + ], + "post-package-update": [ + "support\\Plugin::install" + ], + "pre-package-uninstall": [ + "support\\Plugin::uninstall" + ] + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/Moncter/composer.lock b/Moncter/composer.lock new file mode 100644 index 00000000..da3f90d6 --- /dev/null +++ b/Moncter/composer.lock @@ -0,0 +1,457 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "691f538563ac6695008ddc51b7722c80", + "packages": [ + { + "name": "monolog/monolog", + "version": "2.10.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "5cf826f2991858b54d5c3809bee745560a1042a7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/5cf826f2991858b54d5c3809bee745560a1042a7", + "reference": "5cf826f2991858b54d5c3809bee745560a1042a7", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2@dev", + "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpspec/prophecy": "^1.15", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5.38 || ^9.6.19", + "predis/predis": "^1.1 || ^2.0", + "rollbar/rollbar": "^1.3 || ^2 || ^3", + "ruflin/elastica": "^7", + "swiftmailer/swiftmailer": "^5.3|^6.0", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/2.10.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2024-11-12T12:43:37+00:00" + }, + { + "name": "nikic/fast-route", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/FastRoute.git", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|~5.7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "FastRoute\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Fast request router for PHP", + "keywords": [ + "router", + "routing" + ], + "support": { + "issues": "https://github.com/nikic/FastRoute/issues", + "source": "https://github.com/nikic/FastRoute/tree/master" + }, + "time": "2018-02-13T20:26:39+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "workerman/coroutine", + "version": "v1.1.4", + "source": { + "type": "git", + "url": "https://github.com/workerman-php/coroutine.git", + "reference": "b0bebfa9d41b992ad0a835ddf2ee8fa5d58eca44" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/workerman-php/coroutine/zipball/b0bebfa9d41b992ad0a835ddf2ee8fa5d58eca44", + "reference": "b0bebfa9d41b992ad0a835ddf2ee8fa5d58eca44", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "psr/log": "*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Workerman\\": "src", + "Workerman\\Coroutine\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Workerman coroutine", + "support": { + "issues": "https://github.com/workerman-php/coroutine/issues", + "source": "https://github.com/workerman-php/coroutine/tree/v1.1.4" + }, + "time": "2025-10-11T15:09:08+00:00" + }, + { + "name": "workerman/webman-framework", + "version": "v2.1.2", + "source": { + "type": "git", + "url": "https://github.com/walkor/webman-framework.git", + "reference": "f803bd867f07bb0929faef060b59a19a44186bfc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/walkor/webman-framework/zipball/f803bd867f07bb0929faef060b59a19a44186bfc", + "reference": "f803bd867f07bb0929faef060b59a19a44186bfc", + "shasum": "" + }, + "require": { + "ext-json": "*", + "nikic/fast-route": "^1.3", + "php": ">=8.1", + "psr/container": ">=1.0", + "psr/log": "^3.0", + "workerman/workerman": "^5.1 || dev-master" + }, + "suggest": { + "ext-event": "For better performance. " + }, + "type": "library", + "autoload": { + "files": [ + "./src/support/helpers.php" + ], + "psr-4": { + "Webman\\": "./src", + "Support\\": "./src/support", + "support\\": "./src/support", + "Support\\View\\": "./src/support/view", + "Support\\Bootstrap\\": "./src/support/bootstrap", + "Support\\Exception\\": "./src/support/exception" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "walkor", + "email": "walkor@workerman.net", + "homepage": "https://www.workerman.net", + "role": "Developer" + } + ], + "description": "High performance HTTP Service Framework.", + "homepage": "https://www.workerman.net", + "keywords": [ + "High Performance", + "http service" + ], + "support": { + "email": "walkor@workerman.net", + "forum": "https://wenda.workerman.net/", + "issues": "https://github.com/walkor/webman/issues", + "source": "https://github.com/walkor/webman-framework", + "wiki": "https://doc.workerman.net/" + }, + "time": "2025-03-10T11:52:22+00:00" + }, + { + "name": "workerman/workerman", + "version": "v5.1.4", + "source": { + "type": "git", + "url": "https://github.com/walkor/workerman.git", + "reference": "ff4e17babdc92b16b3252060233c88f6c2e9a61a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/walkor/workerman/zipball/ff4e17babdc92b16b3252060233c88f6c2e9a61a", + "reference": "ff4e17babdc92b16b3252060233c88f6c2e9a61a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=8.1", + "workerman/coroutine": "^1.1 || dev-main" + }, + "conflict": { + "ext-swow": "=8.1" + }, + "platform-dev": [], + "plugin-api-version": "2.0.0" +} diff --git a/Moncter/config/app.php b/Moncter/config/app.php new file mode 100644 index 00000000..f26e3584 --- /dev/null +++ b/Moncter/config/app.php @@ -0,0 +1,26 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +use support\Request; + +return [ + 'debug' => true, + 'error_reporting' => E_ALL, + 'default_timezone' => 'Asia/Shanghai', + 'request_class' => Request::class, + 'public_path' => base_path() . DIRECTORY_SEPARATOR . 'public', + 'runtime_path' => base_path(false) . DIRECTORY_SEPARATOR . 'runtime', + 'controller_suffix' => 'Controller', + 'controller_reuse' => false, +]; diff --git a/Moncter/config/autoload.php b/Moncter/config/autoload.php new file mode 100644 index 00000000..69a8135e --- /dev/null +++ b/Moncter/config/autoload.php @@ -0,0 +1,21 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + 'files' => [ + base_path() . '/app/functions.php', + base_path() . '/support/Request.php', + base_path() . '/support/Response.php', + ] +]; diff --git a/Moncter/config/bootstrap.php b/Moncter/config/bootstrap.php new file mode 100644 index 00000000..95d2e87e --- /dev/null +++ b/Moncter/config/bootstrap.php @@ -0,0 +1,17 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + support\bootstrap\Session::class, +]; diff --git a/Moncter/config/container.php b/Moncter/config/container.php new file mode 100644 index 00000000..106b7b4a --- /dev/null +++ b/Moncter/config/container.php @@ -0,0 +1,15 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return new Webman\Container; \ No newline at end of file diff --git a/Moncter/config/dependence.php b/Moncter/config/dependence.php new file mode 100644 index 00000000..8e964eda --- /dev/null +++ b/Moncter/config/dependence.php @@ -0,0 +1,15 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return []; \ No newline at end of file diff --git a/Moncter/config/exception.php b/Moncter/config/exception.php new file mode 100644 index 00000000..f2aede33 --- /dev/null +++ b/Moncter/config/exception.php @@ -0,0 +1,17 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + '' => support\exception\Handler::class, +]; \ No newline at end of file diff --git a/Moncter/config/log.php b/Moncter/config/log.php new file mode 100644 index 00000000..7f05de57 --- /dev/null +++ b/Moncter/config/log.php @@ -0,0 +1,32 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + 'default' => [ + 'handlers' => [ + [ + 'class' => Monolog\Handler\RotatingFileHandler::class, + 'constructor' => [ + runtime_path() . '/logs/webman.log', + 7, //$maxFiles + Monolog\Logger::DEBUG, + ], + 'formatter' => [ + 'class' => Monolog\Formatter\LineFormatter::class, + 'constructor' => [null, 'Y-m-d H:i:s', true], + ], + ] + ], + ], +]; diff --git a/Moncter/config/middleware.php b/Moncter/config/middleware.php new file mode 100644 index 00000000..8e964eda --- /dev/null +++ b/Moncter/config/middleware.php @@ -0,0 +1,15 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return []; \ No newline at end of file diff --git a/Moncter/config/process.php b/Moncter/config/process.php new file mode 100644 index 00000000..892dc826 --- /dev/null +++ b/Moncter/config/process.php @@ -0,0 +1,62 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +use support\Log; +use support\Request; +use app\process\Http; + +global $argv; + +return [ + 'webman' => [ + 'handler' => Http::class, + 'listen' => 'http://0.0.0.0:8787', + 'count' => cpu_count() * 4, + 'user' => '', + 'group' => '', + 'reusePort' => false, + 'eventLoop' => '', + 'context' => [], + 'constructor' => [ + 'requestClass' => Request::class, + 'logger' => Log::channel('default'), + 'appPath' => app_path(), + 'publicPath' => public_path() + ] + ], + // File update detection and automatic reload + 'monitor' => [ + 'handler' => app\process\Monitor::class, + 'reloadable' => false, + 'constructor' => [ + // Monitor these directories + 'monitorDir' => array_merge([ + app_path(), + config_path(), + base_path() . '/process', + base_path() . '/support', + base_path() . '/resource', + base_path() . '/.env', + ], glob(base_path() . '/plugin/*/app'), glob(base_path() . '/plugin/*/config'), glob(base_path() . '/plugin/*/api')), + // Files with these suffixes will be monitored + 'monitorExtensions' => [ + 'php', 'html', 'htm', 'env' + ], + 'options' => [ + 'enable_file_monitor' => !in_array('-d', $argv) && DIRECTORY_SEPARATOR === '/', + 'enable_memory_monitor' => DIRECTORY_SEPARATOR === '/', + ] + ] + ] +]; diff --git a/Moncter/config/route.php b/Moncter/config/route.php new file mode 100644 index 00000000..a5064fca --- /dev/null +++ b/Moncter/config/route.php @@ -0,0 +1,21 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +use Webman\Route; + + + + + + diff --git a/Moncter/config/server.php b/Moncter/config/server.php new file mode 100644 index 00000000..054d01fb --- /dev/null +++ b/Moncter/config/server.php @@ -0,0 +1,23 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + 'event_loop' => '', + 'stop_timeout' => 2, + 'pid_file' => runtime_path() . '/webman.pid', + 'status_file' => runtime_path() . '/webman.status', + 'stdout_file' => runtime_path() . '/logs/stdout.log', + 'log_file' => runtime_path() . '/logs/workerman.log', + 'max_package_size' => 10 * 1024 * 1024 +]; diff --git a/Moncter/config/session.php b/Moncter/config/session.php new file mode 100644 index 00000000..043f8c45 --- /dev/null +++ b/Moncter/config/session.php @@ -0,0 +1,65 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +use Webman\Session\FileSessionHandler; +use Webman\Session\RedisSessionHandler; +use Webman\Session\RedisClusterSessionHandler; + +return [ + + 'type' => 'file', // or redis or redis_cluster + + 'handler' => FileSessionHandler::class, + + 'config' => [ + 'file' => [ + 'save_path' => runtime_path() . '/sessions', + ], + 'redis' => [ + 'host' => '127.0.0.1', + 'port' => 6379, + 'auth' => '', + 'timeout' => 2, + 'database' => '', + 'prefix' => 'redis_session_', + ], + 'redis_cluster' => [ + 'host' => ['127.0.0.1:7000', '127.0.0.1:7001', '127.0.0.1:7001'], + 'timeout' => 2, + 'auth' => '', + 'prefix' => 'redis_session_', + ] + ], + + 'session_name' => 'PHPSID', + + 'auto_update_timestamp' => false, + + 'lifetime' => 7*24*60*60, + + 'cookie_lifetime' => 365*24*60*60, + + 'cookie_path' => '/', + + 'domain' => '', + + 'http_only' => true, + + 'secure' => false, + + 'same_site' => '', + + 'gc_probability' => [1, 1000], + +]; diff --git a/Moncter/config/static.php b/Moncter/config/static.php new file mode 100644 index 00000000..63136796 --- /dev/null +++ b/Moncter/config/static.php @@ -0,0 +1,23 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +/** + * Static file settings + */ +return [ + 'enable' => true, + 'middleware' => [ // Static file Middleware + //app\middleware\StaticFile::class, + ], +]; \ No newline at end of file diff --git a/Moncter/config/translation.php b/Moncter/config/translation.php new file mode 100644 index 00000000..96589b2b --- /dev/null +++ b/Moncter/config/translation.php @@ -0,0 +1,25 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +/** + * Multilingual configuration + */ +return [ + // Default language + 'locale' => 'zh_CN', + // Fallback language + 'fallback_locale' => ['zh_CN', 'en'], + // Folder where language files are stored + 'path' => base_path() . '/resource/translations', +]; \ No newline at end of file diff --git a/Moncter/config/view.php b/Moncter/config/view.php new file mode 100644 index 00000000..e3a7b856 --- /dev/null +++ b/Moncter/config/view.php @@ -0,0 +1,22 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +use support\view\Raw; +use support\view\Twig; +use support\view\Blade; +use support\view\ThinkPHP; + +return [ + 'handler' => Raw::class +]; diff --git a/Moncter/public/favicon.ico b/Moncter/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b9f722e0eeed8dc7f3639bd3295a7b0376b98eca GIT binary patch literal 4286 zcmbuDc}!GC9LI-{lvLYdn`%sIykgTPHU44S)cR&9&1g0})ukRu+b zsI4iH+Tum=wxm?+g|1x7X%O&eYs0dFEXuJv?|~SRU%$V%yN_jIWzi)cX5Q?Z`F`g& z^PBfT2tWLcA20Z~I66XzQ9_8f0h+{zfZMeXzI$!B14qNqe+Ms>0t^d|k|#hpcn^q~ z;CVg3dWG2j%ifLf3af($0x zFqHZ@&|>{}K^tIw<{IEO5%LUi;PHV0JOSi}L_G^kF%-24L#d6mnNZLTT>4y=fJs32 z>T3n#v;Jb>u1$s#{kl4$sCDYsB{mVVOo|!b%Y>Or`)Ip-WMT~hhU&qo|1rvkx`)Dc z3eN*q@6-It4HR~%KIfv@I@xr$`J)wSwu_-_>;xY!T~{fgJ^dN=dHt(3eaw6QQ5(&Q zwbSe!_bDQ$p3rC#TC=DQ1t2=B$w95sS3oCG8?O+V{O^KwP2A z#JKe59cj_##AAmi*eL7j1FC3lq3dmSsf7^;4_WBvlMm=;#>pmNa_ zbN6mz6D^FlYx?YA-dafqFMD`^pBqx8l17ELWJ#L6Tv|SjloLN+GW0x%FxdnZY!{J* zO>bu3zE@n@Xrm(g1kiF6>)}|NZnR~9*Ma-cgVuwiY@)K+DvIix zgRsv#uD3~jt+NXh3XhXo&T-yFZlPoKYP(QS z*TQ4y`W)ZW)fHq&oMLL^ISVq=dQNX$&xlL$K1x# z*7(XDl{RE3ywB*nWQl#}H3ly4Cosk+Zyun@=e8?%w36eyYL}hLt={+9v0{aOIj?AY z&fVhxb&f+O2n7A&0BwGLmwI}bwM^2!bF$w)ca6_E*FOh3kc$sBYV94a`E&Ugq}JFv zUI2YO@bePdQ$U|M@LuPAo?E48`R&s6T0+lcHq#kXA^lZmqVmQj`0L|ALEl9{RJ+9? zBwEvF5AT3{ps$4w`&^&nANhHd5c>@!tuxTZHQ}@=WhEJJ-SqVEFZ8Fd2lo#&S)Z8* z_5ps5X+Ag(Ntt}s_vQln>$*J0l6s4#{qxQkI&ok-*hafkmQvcTMCbFO_kQ%<4_@^2 zzeWJ+2eNu`#%p8DK354;Lol7p6fud@%wulH<1{44HRY0Ks{v&TtXTdEpTWA|O34LUvWxqcXMUTb6W9?1XuHtqZ&nG)l^rKAmuHEs4) zB@AIt%@K#v30S=-d0QdDYxZT9iy zD*2z!VqR{nZf6e)`*;iRdk3Gh&V8v;dak0+*M06>|6-+l@VPb}Ttsel z@|n4OhceLL37RLIYdu32dP)cEJ5k+^SlHD-nud)91PZ{o+ zzq%#)Txl0+$CLm(L*M6p4y`kYcOBka9)liSH{d#+x;9dMR}dLv$B}VbAQ?vokWn}= z3x8s4pi9FKr63Hx5d?v8ATSXag$OVU;co-o_?H7Z`k|xHI-r3NX+lrJ=!7o`Ul1Dq E2mbpSrT_o{ literal 0 HcmV?d00001 diff --git a/Moncter/start.php b/Moncter/start.php new file mode 100644 index 00000000..41ad7ef2 --- /dev/null +++ b/Moncter/start.php @@ -0,0 +1,5 @@ +#!/usr/bin/env php + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support; + +/** + * Class Request + * @package support + */ +class Request extends \Webman\Http\Request +{ + +} \ No newline at end of file diff --git a/Moncter/support/Response.php b/Moncter/support/Response.php new file mode 100644 index 00000000..9bc4e1eb --- /dev/null +++ b/Moncter/support/Response.php @@ -0,0 +1,24 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +namespace support; + +/** + * Class Response + * @package support + */ +class Response extends \Webman\Http\Response +{ + +} \ No newline at end of file diff --git a/Moncter/support/bootstrap.php b/Moncter/support/bootstrap.php new file mode 100644 index 00000000..d913defd --- /dev/null +++ b/Moncter/support/bootstrap.php @@ -0,0 +1,139 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +use Dotenv\Dotenv; +use support\Log; +use Webman\Bootstrap; +use Webman\Config; +use Webman\Middleware; +use Webman\Route; +use Webman\Util; +use Workerman\Events\Select; +use Workerman\Worker; + +$worker = $worker ?? null; + +if (empty(Worker::$eventLoopClass)) { + Worker::$eventLoopClass = Select::class; +} + +set_error_handler(function ($level, $message, $file = '', $line = 0) { + if (error_reporting() & $level) { + throw new ErrorException($message, 0, $level, $file, $line); + } +}); + +if ($worker) { + register_shutdown_function(function ($startTime) { + if (time() - $startTime <= 0.1) { + sleep(1); + } + }, time()); +} + +if (class_exists('Dotenv\Dotenv') && file_exists(base_path(false) . '/.env')) { + if (method_exists('Dotenv\Dotenv', 'createUnsafeMutable')) { + Dotenv::createUnsafeMutable(base_path(false))->load(); + } else { + Dotenv::createMutable(base_path(false))->load(); + } +} + +Config::clear(); +support\App::loadAllConfig(['route']); +if ($timezone = config('app.default_timezone')) { + date_default_timezone_set($timezone); +} + +foreach (config('autoload.files', []) as $file) { + include_once $file; +} +foreach (config('plugin', []) as $firm => $projects) { + foreach ($projects as $name => $project) { + if (!is_array($project)) { + continue; + } + foreach ($project['autoload']['files'] ?? [] as $file) { + include_once $file; + } + } + foreach ($projects['autoload']['files'] ?? [] as $file) { + include_once $file; + } +} + +Middleware::load(config('middleware', [])); +foreach (config('plugin', []) as $firm => $projects) { + foreach ($projects as $name => $project) { + if (!is_array($project) || $name === 'static') { + continue; + } + Middleware::load($project['middleware'] ?? []); + } + Middleware::load($projects['middleware'] ?? [], $firm); + if ($staticMiddlewares = config("plugin.$firm.static.middleware")) { + Middleware::load(['__static__' => $staticMiddlewares], $firm); + } +} +Middleware::load(['__static__' => config('static.middleware', [])]); + +foreach (config('bootstrap', []) as $className) { + if (!class_exists($className)) { + $log = "Warning: Class $className setting in config/bootstrap.php not found\r\n"; + echo $log; + Log::error($log); + continue; + } + /** @var Bootstrap $className */ + $className::start($worker); +} + +foreach (config('plugin', []) as $firm => $projects) { + foreach ($projects as $name => $project) { + if (!is_array($project)) { + continue; + } + foreach ($project['bootstrap'] ?? [] as $className) { + if (!class_exists($className)) { + $log = "Warning: Class $className setting in config/plugin/$firm/$name/bootstrap.php not found\r\n"; + echo $log; + Log::error($log); + continue; + } + /** @var Bootstrap $className */ + $className::start($worker); + } + } + foreach ($projects['bootstrap'] ?? [] as $className) { + /** @var string $className */ + if (!class_exists($className)) { + $log = "Warning: Class $className setting in plugin/$firm/config/bootstrap.php not found\r\n"; + echo $log; + Log::error($log); + continue; + } + /** @var Bootstrap $className */ + $className::start($worker); + } +} + +$directory = base_path() . '/plugin'; +$paths = [config_path()]; +foreach (Util::scanDir($directory) as $path) { + if (is_dir($path = "$path/config")) { + $paths[] = $path; + } +} +Route::load($paths); + diff --git a/Moncter/windows.bat b/Moncter/windows.bat new file mode 100644 index 00000000..f07ce532 --- /dev/null +++ b/Moncter/windows.bat @@ -0,0 +1,3 @@ +CHCP 65001 +php windows.php +pause \ No newline at end of file diff --git a/Moncter/windows.php b/Moncter/windows.php new file mode 100644 index 00000000..f37a72c9 --- /dev/null +++ b/Moncter/windows.php @@ -0,0 +1,136 @@ +load(); + } else { + Dotenv::createMutable(base_path())->load(); + } +} + +App::loadAllConfig(['route']); + +$errorReporting = config('app.error_reporting'); +if (isset($errorReporting)) { + error_reporting($errorReporting); +} + +$runtimeProcessPath = runtime_path() . DIRECTORY_SEPARATOR . '/windows'; +$paths = [ + $runtimeProcessPath, + runtime_path('logs'), + runtime_path('views') +]; +foreach ($paths as $path) { + if (!is_dir($path)) { + mkdir($path, 0777, true); + } +} + +$processFiles = []; +if (config('server.listen')) { + $processFiles[] = __DIR__ . DIRECTORY_SEPARATOR . 'start.php'; +} +foreach (config('process', []) as $processName => $config) { + $processFiles[] = write_process_file($runtimeProcessPath, $processName, ''); +} + +foreach (config('plugin', []) as $firm => $projects) { + foreach ($projects as $name => $project) { + if (!is_array($project)) { + continue; + } + foreach ($project['process'] ?? [] as $processName => $config) { + $processFiles[] = write_process_file($runtimeProcessPath, $processName, "$firm.$name"); + } + } + foreach ($projects['process'] ?? [] as $processName => $config) { + $processFiles[] = write_process_file($runtimeProcessPath, $processName, $firm); + } +} + +function write_process_file($runtimeProcessPath, $processName, $firm): string +{ + $processParam = $firm ? "plugin.$firm.$processName" : $processName; + $configParam = $firm ? "config('plugin.$firm.process')['$processName']" : "config('process')['$processName']"; + $fileContent = << true]); + if (!$resource) { + exit("Can not execute $cmd\r\n"); + } + return $resource; +} + +$resource = popen_processes($processFiles); +echo "\r\n"; +while (1) { + sleep(1); + if (!empty($monitor) && $monitor->checkAllFilesChange()) { + $status = proc_get_status($resource); + $pid = $status['pid']; + shell_exec("taskkill /F /T /PID $pid"); + proc_close($resource); + $resource = popen_processes($processFiles); + } +} diff --git a/Moncter/技术方案.md b/Moncter/技术方案.md new file mode 100644 index 00000000..25ea2751 --- /dev/null +++ b/Moncter/技术方案.md @@ -0,0 +1,348 @@ +## 用户标签引擎技术方案(以身份证为主键) + +### 一、目标与范围 +- 构建可扩展的用户标签引擎,统一以身份证为主键的人(person)进行画像与筛选。 +- 支持多数据源接入(交易、行为、社群、外呼等),并聚合多个手机号、多个微信号到同一人。 +- 提供规则驱动的人群筛选、客群快照和线索分发能力,服务销售精细化运营。 + +### 0. 需求整合清单(共识) +- 数据接入:多数据源/多数据库连接,按“数据源-表/接口”粒度定义 Job;增量水位、批量与重试。 +- 标识治理:支持弱标识(手机号等)建“临时人”,获取强标识(身份证/unionid/客户号)后合并;全链路幂等与审计。 +- 标签体系:分通道层与人层;`tag_dict` 定义口径/类型/窗口/聚合/版本;标签写入包含 window/source/version。 +- 聚合计算:通道→人层遵循 `aggregation`(sum/max/avg/any/best_of 等),支持实时触发与离线批处理。 +- 规则与人群:DSL 配置、试算/执行、审计;Redis 维护 cohort/位图,支持快照与导出。 +- 回灌与重算:规则或口径变更可对存量回评估;任务状态、错误与执行明细可观测、可重试。 +- 安全合规:身份证只存哈希(加盐),最小化数据使用,接口鉴权与导出留痕。 + +### 二、总体架构 +- 核心理念:人层(person)是唯一真相;通道层(channel)承载具体手机号/微信号等标识。 +- 组件分层: + - 数据接入层:标准化事件/明细,写入通道层标签。 + - 聚合计算层:将通道层指标按口径聚合到人层标签。 + - 规则/人群层:基于人层标签做筛选、快照、导出与分发。 + - 存储与缓存:MySQL(字典/事实/审计)+ Redis(cohort 人群集与位图)。 + +### 二点五、运行逻辑图(分层架构,数据流视角) +```mermaid +╔═════════════════════════════════════════════════════════════════════════╗ +║ 数据源层 ║ +╚═════════════════════════════════════════════════════════════════════════╝ + ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ + │交易系统│ │APP行为│ │客服系统│ │CRM │ + └─────┘ └─────┘ └─────┘ └─────┘ + │ │ │ │ + └───────┴─────────┘ + ▼ +╔═════════════════════════════════════════════════════════════════════════╗ +║ 接入层 ║ +╚═════════════════════════════════════════════════════════════════════════╝ + ┌───────────────────────────────────────┐ + │ Job调度器 增量水位 │ + └───────────────────────────────────────┘ + ▼ + ┌───────────────────────────────────────┐ + │ 标准化 校验 │ + └───────────────────────────────────────┘ + ▼ +╔═════════════════════════════════════════════════════════════════════════╗ +║ 身份层 ║ +╚═════════════════════════════════════════════════════════════════════════╝ + ┌───────────────────────────────────────┐ + │ IdentifierService │ + │ 手机号→person_id │ + └───────────────────────────────────────┘ + ▼ + ┌───────────────────────────────────────┐ + │ 临时人建表 强标识合并 │ + └───────────────────────────────────────┘ + ▼ +╔═════════════════════════════════════════════════════════════════════════╗ +║ 标签层 ║ +╚═════════════════════════════════════════════════════════════════════════╝ + ┌───────────────────────────────────────┐ + │ channel_tags │ + │ 通道标签存储 │ + └───────────────────────────────────────┘ + ▼ + ┌───────────────────────────────────────┐ + │ Aggregator │ + │ 聚合计算 │ + └───────────────────────────────────────┘ + ▼ + ┌───────────────────────────────────────┐ + │ person_tags │ + │ 人层标签存储 │ + └───────────────────────────────────────┘ + ▼ +╔═════════════════════════════════════════════════════════════════════════╗ +║ 规则层 ║ +╚═════════════════════════════════════════════════════════════════════════╝ + ┌───────────────────────────────────────┐ + │ RuleEngine │ + │ DSL执行 │ + └───────────────────────────────────────┘ + ▼ + ┌───────────────────────────────────────┐ + │ Redis Cohort │ + │ 人群集合 │ + └───────────────────────────────────────┘ + ▼ +╔═════════════════════════════════════════════════════════════════════════╗ +║ 应用层 ║ +╚═════════════════════════════════════════════════════════════════════════╝ + ┌─────┐ ┌─────┐ ┌─────┐ + │人群查询│ │快照导出│ │分发推送│ + └─────┘ └─────┘ └─────┘ + +``` + +### 二点六、运行逻辑图(时序视角) +```mermaid +sequenceDiagram + participant 业务系统 as 业务系统
(交易/APP/客服) + participant 接入服务 as 数据接入服务 + participant 身份服务 as 身份解析服务 + participant 通道标签 as channel_tags
(存储) + participant 聚合服务 as 聚合计算服务 + participant 人层标签 as person_tags
(存储) + participant 规则引擎 as 规则引擎 + participant 人群缓存 as Redis Cohort + + 业务系统->>接入服务: 1. 推送事件/批量数据 + 接入服务->>接入服务: 2. 标准化、校验、去重 + 接入服务->>身份服务: 3. 解析标识
(手机号/微信→person_id) + 身份服务-->>接入服务: 返回person_id
(不存在则建临时人) + 接入服务->>通道标签: 4. 写入通道标签
(幂等、window/source/version) + + 通道标签->>聚合服务: 5. 触发聚合事件
(实时/批量) + 聚合服务->>人层标签: 6. 按口径聚合
(sum/max/avg/any) + + 人层标签->>规则引擎: 7. 标签变更触发
(受影响person_id) + 规则引擎->>规则引擎: 8. 执行DSL规则 + 规则引擎->>人群缓存: 9. 更新cohort
(SADD/SINTER) + + 人群缓存-->>业务系统: 10. 人群查询/导出 +``` + +### 二点七、运行逻辑图(简化版,核心路径) +```mermaid +graph TB + Start([数据源
交易/行为/客服/CRM]) --> Ingest[数据接入
Job调度 + 标准化] + Ingest --> Identity[身份解析
手机号→person_id] + Identity --> Channel[通道标签
channel_tags] + Channel --> Aggregate[聚合计算
通道→人层] + Aggregate --> Person[人层标签
person_tags] + Person --> Rule[规则引擎
DSL筛选] + Rule --> Cohort[人群集合
Redis Cohort] + Cohort --> Export([应用输出
查询/快照/分发]) + + Ingest -.->|状态| Audit1[(ingest_state)] + Ingest -.->|错误| Audit2[(ingest_errors)] + Rule -.->|执行记录| Audit3[(rule_executions)] + + style Start fill:#e1f5ff + style Export fill:#e1f5ff + style Ingest fill:#fff4e1 + style Identity fill:#e8f5e9 + style Channel fill:#f3e5f5 + style Aggregate fill:#f3e5f5 + style Person fill:#f3e5f5 + style Rule fill:#fff9c4 + style Cohort fill:#fff9c4 +``` + +### 三、数据模型 +```sql +-- 人:身份证(脱敏哈希)为主键 +CREATE TABLE person ( + person_id CHAR(32) PRIMARY KEY, -- md5(uppercase(id_card_no_without_spaces)) + id_card_hash CHAR(32) UNIQUE, + name VARCHAR(64) NULL, + gender TINYINT NULL, + birthday DATE NULL, + created_at DATETIME, + updated_at DATETIME +); + +-- 标识绑定:一个人可有多个手机号/微信/外部ID +CREATE TABLE person_identifier ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + person_id CHAR(32), + id_type ENUM('phone','wechat','external','email') NOT NULL, + id_value VARCHAR(128) NOT NULL, + is_primary TINYINT DEFAULT 0, + verified TINYINT DEFAULT 0, + created_at DATETIME, + updated_at DATETIME, + UNIQUE KEY uk_type_value (id_type, id_value), + KEY idx_person (person_id) +); + +-- 标签字典 +CREATE TABLE tag_dict ( + tag_code VARCHAR(128) PRIMARY KEY, -- 例:person.trade.arpu_90d + name VARCHAR(128), + category VARCHAR(64), + level ENUM('person','channel') NOT NULL, + type ENUM('int','bool','enum','set','string','float') NOT NULL, + enum_values JSON NULL, + unit VARCHAR(16) NULL, + aggregation ENUM('sum','max','min','avg','any','best_of') NULL, -- 通道→人层口径 + description TEXT, + version INT DEFAULT 1, + status ENUM('draft','active','deprecated') DEFAULT 'active', + owner VARCHAR(64), + created_at DATETIME, + updated_at DATETIME +); + +-- 人层标签(销售筛选用) +CREATE TABLE person_tags ( + person_id CHAR(32), + tag_code VARCHAR(128), + tag_value VARCHAR(256), + confidence TINYINT DEFAULT 100, + source VARCHAR(64), + window VARCHAR(32), + version INT, + updated_at DATETIME, + PRIMARY KEY (person_id, tag_code), + KEY idx_tag (tag_code, tag_value) +); + +-- 通道层标签(手机号/微信号维度) +CREATE TABLE channel_tags ( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + id_type ENUM('phone','wechat','external') NOT NULL, + id_value VARCHAR(128) NOT NULL, + tag_code VARCHAR(128), + tag_value VARCHAR(256), + source VARCHAR(64), + window VARCHAR(32), + updated_at DATETIME, + UNIQUE KEY uk_dim (id_type, id_value, tag_code), + KEY idx_tag (tag_code, tag_value) +); + +-- 规则配置与执行审计 +CREATE TABLE tag_rules ( + rule_id BIGINT PRIMARY KEY AUTO_INCREMENT, + name VARCHAR(128), + dsl JSON, + status ENUM('draft','active','paused') DEFAULT 'active', + schedule VARCHAR(64) NULL, + output_tag VARCHAR(128) NULL, + owner VARCHAR(64), + created_at DATETIME, + updated_at DATETIME +); + +CREATE TABLE rule_executions ( + exec_id BIGINT PRIMARY KEY AUTO_INCREMENT, + rule_id BIGINT, + started_at DATETIME, + finished_at DATETIME, + affected_users INT, + status ENUM('success','failed','partial'), + message TEXT +); +``` + +### 四、标签规范 +- 分类: + - 基础画像、行为活跃、交易能力、风险合规、社交关系、生命周期、设备画像、地域属性。 +- 命名:`{level}.{category}.{name}_{window}` + - 人层:`person.trade.arpu_90d`,`person.behavior.active_days_30d` + - 通道层:`channel.wechat.group_join_30d`,`channel.phone.outbound_connect_7d` +- 口径:在 `tag_dict` 中声明 `type/enum/range/aggregation/window/update_freq/version`。 +- 聚合: + - 风险类:any(任一通道命中→人层命中)。 + - 活跃/价值:max/sum/avg 视业务定义。 + - 可达性:best_of(如选近30日响应率最高的手机号)。 + +### 五、规则与人群(DSL) +```json +{ + "name": "高潜付费人群", + "logic": "AND", + "conditions": [ + {"tag": "person.trade.arpu_90d", "op": "gte", "value": 500}, + {"tag": "person.behavior.active_days_30d", "op": "gte", "value": 10}, + {"tag": "person.risk.blacklist", "op": "eq", "value": false} + ], + "window": "rolling_90d", + "ttl": "24h" +} +``` + +### 六、计算与刷新策略 +- 批处理(T+1/T+0小时):生成稳定画像(交易统计、生命周期等)。 +- 准实时(秒级/分级):事件驱动更新通道层标签,触发对应人层增量聚合。 +- 回评估:字典/规则变更后对存量人群重算,并写入审计。 + +### 七、系统设计(ThinkPHP 5.1) +- 目录结构(`application/tag`): + - controller:`TagDictController`、`RuleController`、`SegmentController`、`IdentifierController` + - model:`TagDict`、`Person`、`PersonIdentifier`、`PersonTags`、`ChannelTags`、`TagRules`、`RuleExecutions` + - service: + - `IdentifierService`(标识绑定/查找/合并) + - `ChannelTagService`(通道标签写入) + - `PersonTagService`(人层聚合/重算) + - `TagQueryService`(条件解析→Redis/DB组合查询) + - `RuleEngineService`(DSL 校验/执行/审计) + - `CohortCacheService`(cohort 维护、集合/位图运算) + - command:`ExecuteRule`、`RecomputeTags`、`CohortSnapshot` + +- 路由(建议,受 `jwt` 保护): +```php +Route::group('v1/tag', function () { + Route::get('dict', 'app\\tag\\controller\\TagDictController@index'); + Route::post('dict', 'app\\tag\\controller\\TagDictController@create'); + Route::post('identifier/bind', 'app\\tag\\controller\\IdentifierController@bind'); + Route::post('rule/execute/:id', 'app\\tag\\controller\\RuleController@execute'); + Route::post('segment/query', 'app\\tag\\controller\\SegmentController@query'); + Route::post('segment/snapshot', 'app\\tag\\controller\\SegmentController@snapshot'); +})->middleware(['jwt']); +``` + +### 八、查询与筛选 +- 人层为主:`person_tags` 组合条件查询,Redis 保存常用 cohort: + - Redis 示例:`SINTER cohort:person.trade.arpu_90d:gt500 cohort:person.active_days_30d:gte10 SDIFF cohort:person.risk.blacklist:eq1` +- MySQL 组合查询示例: +```sql +SELECT DISTINCT t1.person_id +FROM person_tags t1 +JOIN person_tags t2 ON t2.person_id=t1.person_id +LEFT JOIN person_tags t3 ON t3.person_id=t1.person_id AND t3.tag_code='person.risk.blacklist' +WHERE t1.tag_code='person.trade.arpu_90d' AND CAST(t1.tag_value AS DECIMAL)>=500 + AND t2.tag_code='person.behavior.active_days_30d' AND CAST(t2.tag_value AS SIGNED)>=10 + AND (t3.tag_value IS NULL OR t3.tag_value='0') +LIMIT 50 OFFSET 0; +``` + +### 九、关键流程 +1) 事件接入:收到 `id_type + id_value`(如手机号)→ 查 `person_identifier` → 得到 `person_id`。 +2) 通道标签更新:写 `channel_tags`,并发布“聚合任务”。 +3) 人层聚合:按 `tag_dict.aggregation` 规则,更新 `person_tags`。 +4) 规则评估:对受影响的 `person_id` 运行启用中的规则,更新 cohort/输出标签。 +5) 人群产出:支持分页查询、生成快照、导出或推送 CRM/外呼系统。 + +### 十、缓存与索引 +- Redis: + - 集合/位图存 cohort,Key 规范:`cohort:{tag_code}:{op}{value}` 或区间桶。 + - TTL:默认 24h,可按规则 `ttl` 覆盖。 +- MySQL: + - `person_tags(tag_code, tag_value)`、`channel_tags(tag_code, tag_value)` 倒排索引。 + - 审计表按时间分区或冷热分离。 + +### 十一、合规与安全 +- 身份证只存哈希(不可逆),不落明文;导出脱敏。 +- 最小权限访问,接口留痕审计(规则执行、导出、查看)。 +- 口径透明:标签保留来源、窗口、置信度、版本。 + +### 十二、里程碑(落地计划) +- M1:建表与服务骨架;接入交易/行为两类数据;产出10个核心标签。 +- M2:规则试算与快照;Redis cohort;首批销售客群模板(高价值流失预警)。 +- M3:通道“最佳触达”策略;CRM/外呼对接;质量监控与看板。 + + diff --git a/Touchkebao/src/pages/login/Login.tsx b/Touchkebao/src/pages/login/Login.tsx index 2e83787f..898de6d4 100644 --- a/Touchkebao/src/pages/login/Login.tsx +++ b/Touchkebao/src/pages/login/Login.tsx @@ -89,6 +89,7 @@ const Login: React.FC = () => { const loginParams = { ...values, verifySessionId: verify.verifySessionId, + typeId: 1, }; const response =