From 2176dabbb712a5af2ddfc166013f8205cf2a9c6f Mon Sep 17 00:00:00 2001 From: Zds <49744633+zds-s@users.noreply.github.com> Date: Mon, 25 Mar 2024 23:56:41 +0800 Subject: [PATCH 1/7] Splitting the crontab component --- composer.json | 6 +- phpunit.xml | 1 + src/Crontab/README.md | 3 + src/Crontab/composer.json | 35 +++++++ src/Crontab/src/ConfigProvider.php | 27 ++++++ src/Crontab/src/Contract/RegisterContract.php | 18 ++++ src/Crontab/src/Crontab.php | 94 +++++++++++++++++++ src/Crontab/src/CrontabContainer.php | 20 ++++ .../CrontabProcessStarredListener.php | 57 +++++++++++ src/Crontab/src/Schedule.php | 39 ++++++++ 10 files changed, 298 insertions(+), 2 deletions(-) create mode 100644 src/Crontab/README.md create mode 100644 src/Crontab/composer.json create mode 100644 src/Crontab/src/ConfigProvider.php create mode 100644 src/Crontab/src/Contract/RegisterContract.php create mode 100644 src/Crontab/src/Crontab.php create mode 100644 src/Crontab/src/CrontabContainer.php create mode 100644 src/Crontab/src/Listener/CrontabProcessStarredListener.php create mode 100644 src/Crontab/src/Schedule.php diff --git a/composer.json b/composer.json index 1fc3cca4..1ef1670b 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,8 @@ "Mine\\Generator\\": "src/mine-generator/src", "Xmo\\AppStore\\": "src/app-store/src", "Mine\\NextCoreX\\": "src/next-core-x/src", - "Mine\\HttpServer\\": "src/HttpServer/src" + "Mine\\HttpServer\\": "src/HttpServer/src", + "Mine\\Crontab\\": "src/Crontab/src" }, "files": [ "src/mine-helpers/src/functions.php" @@ -32,7 +33,8 @@ "Xmo\\AppStore\\Tests\\": "src/app-store/tests", "Xmo\\MineCore\\Tests\\": "src/mine-core/tests", "Mine\\NextCoreX\\Tests\\": "src/next-core-x/tests", - "Mine\\HttpServer\\Tests\\": "src/HttpServer/tests" + "Mine\\HttpServer\\Tests\\": "src/HttpServer/tests", + "Mine\\Crontab\\Tests\\": "src/Crontab/tests" } }, "authors": [ diff --git a/phpunit.xml b/phpunit.xml index 4d219de5..1b992cd6 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -3,6 +3,7 @@ + ./src/Crontab/tests ./src/HttpServer/tests ./src/app-store/tests ./src/mine-core/tests diff --git a/src/Crontab/README.md b/src/Crontab/README.md new file mode 100644 index 00000000..e8f99fbe --- /dev/null +++ b/src/Crontab/README.md @@ -0,0 +1,3 @@ +# Hyperf Crontab 加强 + +在原有的基础上增加了定时任务从数据库读取的配置 \ No newline at end of file diff --git a/src/Crontab/composer.json b/src/Crontab/composer.json new file mode 100644 index 00000000..4328897d --- /dev/null +++ b/src/Crontab/composer.json @@ -0,0 +1,35 @@ +{ + "name": "mineadmin/crontab", + "description": "加强 Hyperf Crontab", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "x.mo", + "role": "Developer" + }, + { + "name": "zds", + "role": "Developer" + } + ], + "require": { + "hyperf/crontab": "^3.1", + "Hyperf/database": "^3.1" + }, + "autoload": { + "psr-4": { + "Mine\\Crontab\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Mine\\Crontab\\Tests\\": "tests/" + } + }, + "extra": { + "hyperf": { + "config": "\\Mine\\Crontab\\ConfigProvider" + } + } +} \ No newline at end of file diff --git a/src/Crontab/src/ConfigProvider.php b/src/Crontab/src/ConfigProvider.php new file mode 100644 index 00000000..85a9e153 --- /dev/null +++ b/src/Crontab/src/ConfigProvider.php @@ -0,0 +1,27 @@ + [ + CrontabProcessStarredListener::class, + ], + ]; + } +} diff --git a/src/Crontab/src/Contract/RegisterContract.php b/src/Crontab/src/Contract/RegisterContract.php new file mode 100644 index 00000000..d619d6d9 --- /dev/null +++ b/src/Crontab/src/Contract/RegisterContract.php @@ -0,0 +1,18 @@ +getBuilder()->value('name'); + } + + public function isEnable(): bool + { + return (bool) $this->getBuilder()->value(self::ENABLE_COLUMN); + } + + public function getType(): string + { + $type = $this->getBuilder()->value(self::TYPE_COLUMN); + return match ($type) { + 'url', 'class' => 'callback', + default => $type + }; + } + + public function getMemo(): ?string + { + return (string) $this->getBuilder()->value(self::MEMO_COLUMN); + } + + public function getBuilder(): Builder + { + return Db::table(self::TABLE)->where(self::TABLE_KEY, $this->cronId); + } + + /** + * @throws \JsonException + */ + public function getCallback(): mixed + { + $type = $this->getBuilder()->value(self::TYPE_COLUMN); + $value = $this->getBuilder()->value(self::VALUE_COLUMN); + switch ($type) { + case 'eval': + return $value; + case 'url': + return function () use ($value) { + [$method,$url,$data] = explode(',', $value); + $clientFactory = ApplicationContext::getContainer()->get(ClientFactory::class); + $client = $clientFactory->create(); + return $client->request($method, $url, compact('data'))->getBody()->getContents(); + }; + case 'class': + return function () use ($value) { + return [$value, 'execute']; + }; + case 'command': + return json_decode($value, true, 512, JSON_THROW_ON_ERROR); + } + return $value; + } +} diff --git a/src/Crontab/src/CrontabContainer.php b/src/Crontab/src/CrontabContainer.php new file mode 100644 index 00000000..2161775e --- /dev/null +++ b/src/Crontab/src/CrontabContainer.php @@ -0,0 +1,20 @@ +registerCrontab(); + sleep(30); + } + }); + } + + public function registerCrontab(): void + { + $crontabList = $this->schedule->getCrontab(); + foreach ($crontabList as $crontab) { + if (CrontabContainer::has($crontab->getName())) { + continue; + } + $this->crontabManager->register($crontab); + CrontabContainer::set($crontab->getName(), 1); + } + } +} diff --git a/src/Crontab/src/Schedule.php b/src/Crontab/src/Schedule.php new file mode 100644 index 00000000..001a92ed --- /dev/null +++ b/src/Crontab/src/Schedule.php @@ -0,0 +1,39 @@ +where('status', 1)->get(); + if ($crontabList->count() === 0) { + return []; + } + foreach ($crontabList as $crontab) { + $list[] = new \Mine\Crontab\Crontab($crontab->id); + } + return $list; + } +} From 0ebc394bf99f95806e482cd1d8b77eacc7f699f8 Mon Sep 17 00:00:00 2001 From: Zds <49744633+zds-s@users.noreply.github.com> Date: Tue, 26 Mar 2024 12:13:41 +0800 Subject: [PATCH 2/7] Update --- .../2024_03_26_020159_create_crontab.php | 49 +++++++++++++++ ...3_26_020825_create_crontab_execute_log.php | 39 ++++++++++++ .../src/Command/CrontabMigrateCommand.php | 49 +++++++++++++++ src/Crontab/src/Contract/RegisterContract.php | 18 ------ src/Crontab/src/Crontab.php | 24 +++---- src/Crontab/src/CrontabUrl.php | 33 ++++++++++ .../CrontabProcessStarredListener.php | 7 ++- .../Command/CrontabMigrateCommandTest.php | 63 +++++++++++++++++++ .../tests/Cases/ConfigProviderTest.php | 28 +++++++++ .../tests/Cases/CrontabContainerTest.php | 34 ++++++++++ src/Crontab/tests/Cases/CrontabUrlTest.php | 41 ++++++++++++ .../CrontabProcessStarredListenerTest.php | 58 +++++++++++++++++ 12 files changed, 412 insertions(+), 31 deletions(-) create mode 100644 src/Crontab/Database/Migrations/2024_03_26_020159_create_crontab.php create mode 100644 src/Crontab/Database/Migrations/2024_03_26_020825_create_crontab_execute_log.php create mode 100644 src/Crontab/src/Command/CrontabMigrateCommand.php delete mode 100644 src/Crontab/src/Contract/RegisterContract.php create mode 100644 src/Crontab/src/CrontabUrl.php create mode 100644 src/Crontab/tests/Cases/Command/CrontabMigrateCommandTest.php create mode 100644 src/Crontab/tests/Cases/ConfigProviderTest.php create mode 100644 src/Crontab/tests/Cases/CrontabContainerTest.php create mode 100644 src/Crontab/tests/Cases/CrontabUrlTest.php create mode 100644 src/Crontab/tests/Cases/Listener/CrontabProcessStarredListenerTest.php diff --git a/src/Crontab/Database/Migrations/2024_03_26_020159_create_crontab.php b/src/Crontab/Database/Migrations/2024_03_26_020159_create_crontab.php new file mode 100644 index 00000000..9f3ad8e8 --- /dev/null +++ b/src/Crontab/Database/Migrations/2024_03_26_020159_create_crontab.php @@ -0,0 +1,49 @@ +bigIncrements('id'); + /* + * name string 30 + * status tinyint 1 default 0 + * memo string 60 default null + * type string 10 not null + * value string longtext not null + */ + $table->string('name', 30); + $table->tinyInteger('status')->default(0); + $table->string('memo', 60)->default(null); + $table->string('type', 10); + $table->string('rule', 10); + $table->text('value'); + $table->datetimes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('crontab'); + } +} diff --git a/src/Crontab/Database/Migrations/2024_03_26_020825_create_crontab_execute_log.php b/src/Crontab/Database/Migrations/2024_03_26_020825_create_crontab_execute_log.php new file mode 100644 index 00000000..703d1f36 --- /dev/null +++ b/src/Crontab/Database/Migrations/2024_03_26_020825_create_crontab_execute_log.php @@ -0,0 +1,39 @@ +bigIncrements('id'); + $table->bigInteger('crontab_id'); + $table->tinyInteger('status')->default(0); + $table->string('output')->default(0); + $table->datetimes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('crontab_execute_log'); + } +} diff --git a/src/Crontab/src/Command/CrontabMigrateCommand.php b/src/Crontab/src/Command/CrontabMigrateCommand.php new file mode 100644 index 00000000..245f53fb --- /dev/null +++ b/src/Crontab/src/Command/CrontabMigrateCommand.php @@ -0,0 +1,49 @@ +input->getOption('connection'); + if (empty($connection)) { + $connection = 'default'; + } + $migrator = $this->migrator; + $migrator->setConnection($connection); + $this->migrator + ->setOutput(new NullOutput()) + ->run(dirname(__DIR__, 2) . '/Database/Migrations'); + } + + protected function getOptions(): array + { + return [ + new InputOption('connection', 'connection', InputOption::VALUE_OPTIONAL, 'connection name'), + ]; + } +} diff --git a/src/Crontab/src/Contract/RegisterContract.php b/src/Crontab/src/Contract/RegisterContract.php deleted file mode 100644 index d619d6d9..00000000 --- a/src/Crontab/src/Contract/RegisterContract.php +++ /dev/null @@ -1,18 +0,0 @@ -get(ClientFactory::class); - $client = $clientFactory->create(); - return $client->request($method, $url, compact('data'))->getBody()->getContents(); - }; + return [ + CrontabUrl::class, + 'execute', + explode($value, ','), + ]; case 'class': - return function () use ($value) { - return [$value, 'execute']; - }; + return [$value, 'execute']; case 'command': return json_decode($value, true, 512, JSON_THROW_ON_ERROR); } return $value; } + + public function getRule(): ?string + { + return $this->getBuilder()->value(self::RULE_COLUMN); + } } diff --git a/src/Crontab/src/CrontabUrl.php b/src/Crontab/src/CrontabUrl.php new file mode 100644 index 00000000..1a79ac09 --- /dev/null +++ b/src/Crontab/src/CrontabUrl.php @@ -0,0 +1,33 @@ +clientFactory->create(); + } + + public function execute(string $url) + { + return $this->getClient()->get($url); + } +} diff --git a/src/Crontab/src/Listener/CrontabProcessStarredListener.php b/src/Crontab/src/Listener/CrontabProcessStarredListener.php index 20c80a4e..5c16d37a 100644 --- a/src/Crontab/src/Listener/CrontabProcessStarredListener.php +++ b/src/Crontab/src/Listener/CrontabProcessStarredListener.php @@ -16,11 +16,14 @@ use Hyperf\Crontab\Event\CrontabDispatcherStarted; use Hyperf\Engine\Coroutine; use Hyperf\Event\Contract\ListenerInterface; +use Hyperf\Process\ProcessManager; use Mine\Crontab\CrontabContainer; use Mine\Crontab\Schedule; class CrontabProcessStarredListener implements ListenerInterface { + public static int $sleep = 30; + public function __construct( private readonly CrontabManager $crontabManager, private readonly Schedule $schedule @@ -36,9 +39,9 @@ public function listen(): array public function process(object $event): void { Coroutine::create(function () { - while (true) { + while (ProcessManager::isRunning()) { $this->registerCrontab(); - sleep(30); + sleep(self::$sleep); } }); } diff --git a/src/Crontab/tests/Cases/Command/CrontabMigrateCommandTest.php b/src/Crontab/tests/Cases/Command/CrontabMigrateCommandTest.php new file mode 100644 index 00000000..513dabc0 --- /dev/null +++ b/src/Crontab/tests/Cases/Command/CrontabMigrateCommandTest.php @@ -0,0 +1,63 @@ +newInstance(\Mockery::mock(Migrator::class)); + $this->assertTrue(true); + } + + public function testInvoke(): void + { + $reflectionClass = new \ReflectionClass(CrontabMigrateCommand::class); + $migrator = \Mockery::mock(Migrator::class); + $migrator->allows('setOutput')->andReturnUsing(function ($output) use ($migrator) { + $this->assertInstanceOf(NullOutput::class, $output); + return $migrator; + }); + $migrator->allows('run')->andReturnUsing(function ($path) { + $this->assertIsString($path); + $this->assertSame($path, dirname(__DIR__, 3) . '/Database/Migrations'); + return []; + }); + $migrator->allows('setConnection') + ->andReturnUsing(function ($connection) { + $this->assertSame($connection, 'default'); + }, function ($connection) { + $this->assertSame($connection, 'test'); + }); + /** + * @var CrontabMigrateCommand $instance + */ + $instance = $reflectionClass->newInstance($migrator); + $inputMock = \Mockery::mock(InputInterface::class); + $inputMock->allows('getOption')->andReturn(null, 'test'); + $instance->setInput($inputMock); + $instance(); + $instance(); + } +} diff --git a/src/Crontab/tests/Cases/ConfigProviderTest.php b/src/Crontab/tests/Cases/ConfigProviderTest.php new file mode 100644 index 00000000..3868c188 --- /dev/null +++ b/src/Crontab/tests/Cases/ConfigProviderTest.php @@ -0,0 +1,28 @@ +assertIsArray((new ConfigProvider())()); + } +} diff --git a/src/Crontab/tests/Cases/CrontabContainerTest.php b/src/Crontab/tests/Cases/CrontabContainerTest.php new file mode 100644 index 00000000..5b011c8f --- /dev/null +++ b/src/Crontab/tests/Cases/CrontabContainerTest.php @@ -0,0 +1,34 @@ +assertSame(CrontabContainer::get('id'), 'xxx'); + $this->assertTrue(CrontabContainer::has('id')); + $this->assertFalse(CrontabContainer::has('test')); + } +} diff --git a/src/Crontab/tests/Cases/CrontabUrlTest.php b/src/Crontab/tests/Cases/CrontabUrlTest.php new file mode 100644 index 00000000..c33535ee --- /dev/null +++ b/src/Crontab/tests/Cases/CrontabUrlTest.php @@ -0,0 +1,41 @@ +allows('get')->andReturnUsing(function ($url) { + $this->assertSame($url, 'http://mineadmin.com'); + return \Mockery::mock(ResponseInterface::class); + }); + $clientFactory->allows('create')->andReturn($client); + ApplicationContext::getContainer()->set(ClientFactory::class, $clientFactory); + $crontabUrl = ApplicationContext::getContainer()->get(CrontabUrl::class); + $crontabUrl->execute('http://mineadmin.com'); + } +} diff --git a/src/Crontab/tests/Cases/Listener/CrontabProcessStarredListenerTest.php b/src/Crontab/tests/Cases/Listener/CrontabProcessStarredListenerTest.php new file mode 100644 index 00000000..efc9d3ab --- /dev/null +++ b/src/Crontab/tests/Cases/Listener/CrontabProcessStarredListenerTest.php @@ -0,0 +1,58 @@ +getMethod('listen'); + $result = $method->invoke($instance); + $this->assertSame($result, [ + CrontabDispatcherStarted::class, + ]); + } + + public function testProcess(): void + { + $reflectionClass = new \ReflectionClass(CrontabProcessStarredListener::class); + $instance = \Mockery::mock(CrontabProcessStarredListener::class); + CrontabProcessStarredListener::$sleep = 1; + $instance->allows('registerCrontab'); + $method = $reflectionClass->getMethod('process'); + ProcessManager::setRunning(false); + $method->invoke($instance, \Mockery::mock(CrontabDispatcherStarted::class)); + ProcessManager::setRunning(true); + Coroutine::create(function () { + sleep(2); + ProcessManager::setRunning(false); + }); + $method->invoke($instance, \Mockery::mock(CrontabDispatcherStarted::class)); + $this->assertTrue(true); + } +} From 7c5261738b2639e08adb2369a2481de03b3ed7e1 Mon Sep 17 00:00:00 2001 From: Zds <49744633+zds-s@users.noreply.github.com> Date: Tue, 26 Mar 2024 14:31:07 +0800 Subject: [PATCH 3/7] Update --- .../2024_03_26_020159_create_crontab.php | 2 + src/Crontab/src/Crontab.php | 30 +++- src/Crontab/src/Schedule.php | 4 +- src/Crontab/tests/Cases/CrontabTest.php | 156 ++++++++++++++++++ src/Crontab/tests/Cases/ScheduleTest.php | 84 ++++++++++ 5 files changed, 270 insertions(+), 6 deletions(-) create mode 100644 src/Crontab/tests/Cases/CrontabTest.php create mode 100644 src/Crontab/tests/Cases/ScheduleTest.php diff --git a/src/Crontab/Database/Migrations/2024_03_26_020159_create_crontab.php b/src/Crontab/Database/Migrations/2024_03_26_020159_create_crontab.php index 9f3ad8e8..621a6e70 100644 --- a/src/Crontab/Database/Migrations/2024_03_26_020159_create_crontab.php +++ b/src/Crontab/Database/Migrations/2024_03_26_020159_create_crontab.php @@ -31,6 +31,8 @@ public function up(): void */ $table->string('name', 30); $table->tinyInteger('status')->default(0); + $table->tinyInteger('is_on_one_server')->default(0); + $table->tinyInteger('is_singleton')->default(0); $table->string('memo', 60)->default(null); $table->string('type', 10); $table->string('rule', 10); diff --git a/src/Crontab/src/Crontab.php b/src/Crontab/src/Crontab.php index 22737e41..2ca110c0 100644 --- a/src/Crontab/src/Crontab.php +++ b/src/Crontab/src/Crontab.php @@ -32,13 +32,21 @@ class Crontab extends Base public const RULE_COLUMN = 'rule'; + public const NAME_COLUMN = 'name'; + + public const IS_ON_ONE_SERVER_COLUMN = 'is_on_one_server'; + + public const IS_SINGLETON = 'is_singleton'; + + public static string $connectionName = 'default'; + public function __construct( private readonly int $cronId, ) {} public function getName(): ?string { - return $this->getBuilder()->value('name'); + return $this->getBuilder()->value(self::NAME_COLUMN); } public function isEnable(): bool @@ -62,7 +70,7 @@ public function getMemo(): ?string public function getBuilder(): Builder { - return Db::table(self::TABLE)->where(self::TABLE_KEY, $this->cronId); + return Db::connection(self::$connectionName)->table(self::TABLE)->where(self::TABLE_KEY, $this->cronId); } /** @@ -79,11 +87,12 @@ public function getCallback(): mixed return [ CrontabUrl::class, 'execute', - explode($value, ','), + explode(',', $value), ]; case 'class': return [$value, 'execute']; case 'command': + case 'callback': return json_decode($value, true, 512, JSON_THROW_ON_ERROR); } return $value; @@ -93,4 +102,19 @@ public function getRule(): ?string { return $this->getBuilder()->value(self::RULE_COLUMN); } + + public function getCronId(): int + { + return $this->cronId; + } + + public function isOnOneServer(): bool + { + return (bool) $this->getBuilder()->value(self::IS_ON_ONE_SERVER_COLUMN); + } + + public function isSingleton(): bool + { + return (bool) $this->getBuilder()->value(self::IS_SINGLETON); + } } diff --git a/src/Crontab/src/Schedule.php b/src/Crontab/src/Schedule.php index 001a92ed..41459e37 100644 --- a/src/Crontab/src/Schedule.php +++ b/src/Crontab/src/Schedule.php @@ -15,12 +15,10 @@ use Hyperf\Crontab\Crontab; use Hyperf\DbConnection\Db; -final class Schedule +class Schedule { public const CRONTAB_TABLE = 'crontab'; - public function __construct() {} - /** * @return Crontab[] */ diff --git a/src/Crontab/tests/Cases/CrontabTest.php b/src/Crontab/tests/Cases/CrontabTest.php new file mode 100644 index 00000000..00a46360 --- /dev/null +++ b/src/Crontab/tests/Cases/CrontabTest.php @@ -0,0 +1,156 @@ +set(ConfigInterface::class, new Config([])); + $connectionResolverInterface = \Mockery::mock(ConnectionResolverInterface::class); + $connectionInterface = \Mockery::mock(ConnectionInterface::class); + $connectionResolverInterface + ->allows('connection') + ->andReturn($connectionInterface); + $builder = \Mockery::mock(Builder::class); + $builder->allows('where')->with(Crontab::TABLE_KEY, 1)->andReturn($builder); + $builder->allows('value')->with(Crontab::ENABLE_COLUMN)->andReturn(1, 0); + $builder->allows('value')->with(Crontab::IS_SINGLETON)->andReturn(1, 0); + $builder->allows('value')->with(Crontab::IS_ON_ONE_SERVER_COLUMN)->andReturn(1, 0); + $builder->allows('value')->with(Crontab::NAME_COLUMN)->andReturn('xxx'); + $builder->allows('value')->with(Crontab::MEMO_COLUMN)->andReturn('xxx'); + $builder->allows('value') + ->with(Crontab::RULE_COLUMN) + ->andReturn('* * * * *', '0 0 * * *'); + $builder->allows('value') + ->with(Crontab::TYPE_COLUMN) + ->andReturn( + 'xxx', + 'callback', + 'url', + 'class', + 'eval', + 'command', + 'xxx', + 'callback', + 'url', + 'class', + 'eval', + 'command' + ); + $builder->allows('value') + ->with(Crontab::VALUE_COLUMN) + ->andReturn( + 'xxx', + '["xxx","xxx"]', + 'http://baidu.com', + 'AppTest', + 'echo 1;', + '["xxx","xxx"]' + ); + $connectionInterface->allows('table')->andReturnUsing(function ($table) use ($builder) { + $this->assertSame(Crontab::TABLE, $table); + return $builder; + }); + ApplicationContext::getContainer()->set(ConnectionResolverInterface::class, $connectionResolverInterface); + } + + public function testConstruct(): void + { + $crontab = new Crontab(1); + $this->assertSame($crontab->getCronId(), 1); + } + + public function testGetBuilder(): void + { + $crontab = new Crontab(1); + $crontab->getBuilder(); + $this->assertTrue(true); + } + + public function testGetName(): void + { + $crontab = new Crontab(1); + $this->assertSame($crontab->getName(), 'xxx'); + } + + public function testGetMemo(): void + { + $crontab = new Crontab(1); + $this->assertSame($crontab->getMemo(), 'xxx'); + } + + public function testIsEnable(): void + { + $crontab = new Crontab(1); + $this->assertTrue($crontab->isEnable()); + $this->assertFalse($crontab->isEnable()); + } + + public function testGetType(): void + { + $crontab = new Crontab(1); + $this->assertSame('xxx', $crontab->getType()); + $this->assertSame('callback', $crontab->getType()); + $this->assertSame('callback', $crontab->getType()); + $this->assertSame('callback', $crontab->getType()); + $this->assertSame('eval', $crontab->getType()); + $this->assertSame('command', $crontab->getType()); + } + + public function testGetCallback(): void + { + $crontab = new Crontab(1); + $this->assertSame($crontab->getCallback(), 'xxx'); + $this->assertSame($crontab->getCallback(), ['xxx', 'xxx']); + $this->assertSame($crontab->getCallback(), [CrontabUrl::class, 'execute', ['http://baidu.com']]); + $this->assertSame($crontab->getCallback(), ['AppTest', 'execute']); + $this->assertSame($crontab->getCallback(), 'echo 1;'); + $this->assertSame($crontab->getCallback(), ['xxx', 'xxx']); + } + + public function testGetRule(): void + { + $crontab = new Crontab(1); + $this->assertSame($crontab->getRule(), '* * * * *'); + $this->assertSame($crontab->getRule(), '0 0 * * *'); + } + + public function testIsSingleton(): void + { + $crontab = new Crontab(1); + $this->assertTrue($crontab->isSingleton()); + $this->assertFalse($crontab->isSingleton()); + } + + public function testIsOnOneServer(): void + { + $crontab = new Crontab(1); + $this->assertTrue($crontab->isOnOneServer()); + $this->assertFalse($crontab->isOnOneServer()); + } +} diff --git a/src/Crontab/tests/Cases/ScheduleTest.php b/src/Crontab/tests/Cases/ScheduleTest.php new file mode 100644 index 00000000..5e520e39 --- /dev/null +++ b/src/Crontab/tests/Cases/ScheduleTest.php @@ -0,0 +1,84 @@ +set(ConfigInterface::class, $config); + } + + public function testGetCrontab(): void + { + $connectionResolverInterface = \Mockery::mock(ConnectionResolverInterface::class); + $connectionInterface = \Mockery::mock(ConnectionInterface::class); + $connectionResolverInterface + ->allows('connection') + ->andReturn($connectionInterface); + $connectionInterface->allows('table')->andReturnUsing(function ($table) { + $this->assertSame($table, Schedule::CRONTAB_TABLE); + $builder = \Mockery::mock(Builder::class); + $stdclass = new \stdClass(); + $stdclass->id = 1; + $builder->allows('get') + ->andReturn(new Collection([$stdclass])); + $builder->allows('where')->andReturnUsing(function ($column, $val) use ($builder) { + $this->assertSame($column, 'status'); + $this->assertSame($val, 1); + return $builder; + }); + return $builder; + }, function ($table) { + $this->assertSame($table, Schedule::CRONTAB_TABLE); + $builder = \Mockery::mock(Builder::class); + $builder->allows('get') + ->andReturn(new Collection([])); + $builder->allows('where')->andReturnUsing(function ($column, $val) use ($builder) { + $this->assertSame($column, 'status'); + $this->assertSame($val, 1); + return $builder; + }); + return $builder; + }); + ApplicationContext::getContainer()->set(ConnectionResolverInterface::class, $connectionResolverInterface); + + $schedule = new \ReflectionClass(Schedule::class); + $method = $schedule->getMethod('getCrontab'); + $instance = \Mockery::mock(Schedule::class); + $result = $method->invoke($instance); + $this->assertIsArray($result); + $this->assertCount(1, $result); + $result = $method->invoke($instance); + $this->assertIsArray($result); + $this->assertCount(0, $result); + } +} From 650c14f3117d0cb7d3735d1a0fe905e937da6eff Mon Sep 17 00:00:00 2001 From: Zds <49744633+zds-s@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:05:51 +0800 Subject: [PATCH 4/7] Update --- ...3_26_020825_create_crontab_execute_log.php | 4 +- .../src/Aspect/CrontabExecutorAspect.php | 53 +++++++++ src/Crontab/src/ConfigProvider.php | 8 ++ src/Crontab/src/Crontab.php | 4 +- src/Crontab/src/CrontabContainer.php | 2 + .../Aspect/CrontabExecutorAspectTest.php | 102 ++++++++++++++++++ 6 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 src/Crontab/src/Aspect/CrontabExecutorAspect.php create mode 100644 src/Crontab/tests/Cases/Aspect/CrontabExecutorAspectTest.php diff --git a/src/Crontab/Database/Migrations/2024_03_26_020825_create_crontab_execute_log.php b/src/Crontab/Database/Migrations/2024_03_26_020825_create_crontab_execute_log.php index 703d1f36..7cea3e40 100644 --- a/src/Crontab/Database/Migrations/2024_03_26_020825_create_crontab_execute_log.php +++ b/src/Crontab/Database/Migrations/2024_03_26_020825_create_crontab_execute_log.php @@ -23,8 +23,10 @@ public function up(): void Schema::create('crontab_execute_log', function (Blueprint $table) { $table->bigIncrements('id'); $table->bigInteger('crontab_id'); + $table->string('name', 100); $table->tinyInteger('status')->default(0); - $table->string('output')->default(0); + $table->string('target', 200); + $table->text('exception_info'); $table->datetimes(); }); } diff --git a/src/Crontab/src/Aspect/CrontabExecutorAspect.php b/src/Crontab/src/Aspect/CrontabExecutorAspect.php new file mode 100644 index 00000000..10462188 --- /dev/null +++ b/src/Crontab/src/Aspect/CrontabExecutorAspect.php @@ -0,0 +1,53 @@ +getArguments(); + if ($crontab instanceof \Mine\Crontab\Crontab) { + $callback = $crontab->getCallback(); + if (is_array($callback)) { + $callback = json_encode($callback, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE); + } + Db::connection(CrontabContainer::$connectionName) + ->table('crontab_execute_log') + ->insert([ + 'crontab_id' => $crontab->getCronId(), + 'name' => $crontab->getName(), + 'target' => $callback, + 'status' => $isSuccess ? 1 : 0, + 'exception_info' => $throwable === null ? '' : $throwable->getMessage(), + ]); + } + return $proceedingJoinPoint->process(); + } +} diff --git a/src/Crontab/src/ConfigProvider.php b/src/Crontab/src/ConfigProvider.php index 85a9e153..9933cc96 100644 --- a/src/Crontab/src/ConfigProvider.php +++ b/src/Crontab/src/ConfigProvider.php @@ -12,6 +12,8 @@ namespace Mine\Crontab; +use Mine\Crontab\Aspect\CrontabExecutorAspect; +use Mine\Crontab\Command\CrontabMigrateCommand; use Mine\Crontab\Listener\CrontabProcessStarredListener; class ConfigProvider @@ -22,6 +24,12 @@ public function __invoke(): array 'listener' => [ CrontabProcessStarredListener::class, ], + 'aspects' => [ + CrontabExecutorAspect::class, + ], + 'commands' => [ + CrontabMigrateCommand::class, + ], ]; } } diff --git a/src/Crontab/src/Crontab.php b/src/Crontab/src/Crontab.php index 2ca110c0..2450edf2 100644 --- a/src/Crontab/src/Crontab.php +++ b/src/Crontab/src/Crontab.php @@ -38,8 +38,6 @@ class Crontab extends Base public const IS_SINGLETON = 'is_singleton'; - public static string $connectionName = 'default'; - public function __construct( private readonly int $cronId, ) {} @@ -70,7 +68,7 @@ public function getMemo(): ?string public function getBuilder(): Builder { - return Db::connection(self::$connectionName)->table(self::TABLE)->where(self::TABLE_KEY, $this->cronId); + return Db::connection(CrontabContainer::$connectionName)->table(self::TABLE)->where(self::TABLE_KEY, $this->cronId); } /** diff --git a/src/Crontab/src/CrontabContainer.php b/src/Crontab/src/CrontabContainer.php index 2161775e..50835cd6 100644 --- a/src/Crontab/src/CrontabContainer.php +++ b/src/Crontab/src/CrontabContainer.php @@ -17,4 +17,6 @@ final class CrontabContainer { use Container; + + public static string $connectionName = 'default'; } diff --git a/src/Crontab/tests/Cases/Aspect/CrontabExecutorAspectTest.php b/src/Crontab/tests/Cases/Aspect/CrontabExecutorAspectTest.php new file mode 100644 index 00000000..68d8136b --- /dev/null +++ b/src/Crontab/tests/Cases/Aspect/CrontabExecutorAspectTest.php @@ -0,0 +1,102 @@ +set(ConfigInterface::class, new Config([])); + $connectionResolverInterface = \Mockery::mock(ConnectionResolverInterface::class); + $connectionInterface = \Mockery::mock(ConnectionInterface::class); + $connectionResolverInterface + ->allows('connection') + ->andReturn($connectionInterface); + $builder = \Mockery::mock(Builder::class); + $connectionInterface->allows('table')->andReturn($builder); + $builder->allows('where')->with(Crontab::TABLE_KEY, 1)->andReturn($builder); + $builder->allows('value')->with(Crontab::ENABLE_COLUMN)->andReturn(1, 0); + $builder->allows('value')->with(Crontab::IS_SINGLETON)->andReturn(1, 0); + $builder->allows('value')->with(Crontab::IS_ON_ONE_SERVER_COLUMN)->andReturn(1, 0); + $builder->allows('value')->with(Crontab::NAME_COLUMN)->andReturn('xxx'); + $builder->allows('value')->with(Crontab::MEMO_COLUMN)->andReturn('xxx'); + $builder->allows('value') + ->with(Crontab::RULE_COLUMN) + ->andReturn('* * * * *', '0 0 * * *'); + $builder->allows('value') + ->with(Crontab::TYPE_COLUMN) + ->andReturn( + 'xxx', + 'callback', + 'url', + 'class', + 'eval', + 'command', + 'xxx', + 'callback', + 'url', + 'class', + 'eval', + 'command' + ); + $builder->allows('value') + ->with(Crontab::VALUE_COLUMN) + ->andReturn( + 'xxx', + '["xxx","xxx"]', + 'http://baidu.com', + 'AppTest', + 'echo 1;', + '["xxx","xxx"]' + ); + $builder->allows('insert')->andReturnUsing(function ($data) { + return true; + }); + ApplicationContext::getContainer()->set(ConnectionResolverInterface::class, $connectionResolverInterface); + } + + public function testProcess(): void + { + $aspect = new CrontabExecutorAspect(); + $this->assertSame($aspect->classes, [ + Executor::class . '::logResult', + ]); + $proceedingJoinPoint = \Mockery::mock(ProceedingJoinPoint::class); + $proceedingJoinPoint->allows('process'); + $proceedingJoinPoint->allows('getArguments')->andReturn([ + new Crontab(1), + true, + new \Exception('test'), + ]); + $aspect->process($proceedingJoinPoint); + $aspect->process($proceedingJoinPoint); + $aspect->process($proceedingJoinPoint); + $aspect->process($proceedingJoinPoint); + } +} From e96d4050916df09d9546f578b8512b86306a664a Mon Sep 17 00:00:00 2001 From: Zds <49744633+zds-s@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:16:36 +0800 Subject: [PATCH 5/7] Update --- src/HttpServer/tests/Cases/Middleware/I18nMiddlewareTest.php | 5 ++++- src/HttpServer/tests/Cases/ResultTest.php | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/HttpServer/tests/Cases/Middleware/I18nMiddlewareTest.php b/src/HttpServer/tests/Cases/Middleware/I18nMiddlewareTest.php index e74baa10..acb83364 100644 --- a/src/HttpServer/tests/Cases/Middleware/I18nMiddlewareTest.php +++ b/src/HttpServer/tests/Cases/Middleware/I18nMiddlewareTest.php @@ -17,6 +17,8 @@ use Hyperf\Contract\ConfigInterface; use Hyperf\Contract\StdoutLoggerInterface; use Hyperf\Contract\TranslatorInterface; +use Hyperf\Di\Container; +use Hyperf\Di\Definition\DefinitionSourceFactory; use Mine\HttpServer\Contract\Log\RequestIdGeneratorInterface; use Mine\HttpServer\Log\RequestIdGenerator; use Mine\HttpServer\Middleware\I18nMiddleware; @@ -34,6 +36,7 @@ class I18nMiddlewareTest extends TestCase { protected function setUp(): void { + ApplicationContext::setContainer(new Container((new DefinitionSourceFactory(true))())); $config = new Config([ StdoutLoggerInterface::class => [ LogLevel::DEBUG, @@ -58,9 +61,9 @@ protected function setUp(): void public function testProcess(): void { + $translator = ApplicationContext::getContainer()->get(TranslatorInterface::class); $instance = ApplicationContext::getContainer()->get(I18nMiddleware::class); $request = \Mockery::mock(ServerRequestInterface::class); - $translator = ApplicationContext::getContainer()->get(TranslatorInterface::class); $request->allows('hasHeader') ->andReturn(false, true, true, true, true); $request->allows('getHeaderLine') diff --git a/src/HttpServer/tests/Cases/ResultTest.php b/src/HttpServer/tests/Cases/ResultTest.php index 733eb399..759a83c0 100644 --- a/src/HttpServer/tests/Cases/ResultTest.php +++ b/src/HttpServer/tests/Cases/ResultTest.php @@ -16,6 +16,8 @@ use Hyperf\Context\ApplicationContext; use Hyperf\Contract\ConfigInterface; use Hyperf\Contract\StdoutLoggerInterface; +use Hyperf\Di\Container; +use Hyperf\Di\Definition\DefinitionSourceFactory; use Mine\HttpServer\Constant\HttpResultCode; use Mine\HttpServer\Contract\Log\RequestIdGeneratorInterface; use Mine\HttpServer\Log\RequestIdGenerator; @@ -32,6 +34,7 @@ class ResultTest extends TestCase { protected function setUp(): void { + ApplicationContext::setContainer(new Container((new DefinitionSourceFactory(true))())); $config = new Config([ StdoutLoggerInterface::class => [ LogLevel::DEBUG, From 4d13ffd7ffb2661fd0ca1dd15798524e6a984e35 Mon Sep 17 00:00:00 2001 From: Zds <49744633+zds-s@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:22:53 +0800 Subject: [PATCH 6/7] Update --- .../tests/Cases/Middleware/I18nMiddlewareTest.php | 6 +++++- src/HttpServer/tests/Cases/ResultTest.php | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/HttpServer/tests/Cases/Middleware/I18nMiddlewareTest.php b/src/HttpServer/tests/Cases/Middleware/I18nMiddlewareTest.php index acb83364..6343931f 100644 --- a/src/HttpServer/tests/Cases/Middleware/I18nMiddlewareTest.php +++ b/src/HttpServer/tests/Cases/Middleware/I18nMiddlewareTest.php @@ -19,6 +19,7 @@ use Hyperf\Contract\TranslatorInterface; use Hyperf\Di\Container; use Hyperf\Di\Definition\DefinitionSourceFactory; +use Hyperf\Di\Exception\Exception; use Mine\HttpServer\Contract\Log\RequestIdGeneratorInterface; use Mine\HttpServer\Log\RequestIdGenerator; use Mine\HttpServer\Middleware\I18nMiddleware; @@ -34,9 +35,12 @@ */ class I18nMiddlewareTest extends TestCase { + /** + * @throws Exception + */ protected function setUp(): void { - ApplicationContext::setContainer(new Container((new DefinitionSourceFactory(true))())); + ApplicationContext::setContainer(new Container((new DefinitionSourceFactory())())); $config = new Config([ StdoutLoggerInterface::class => [ LogLevel::DEBUG, diff --git a/src/HttpServer/tests/Cases/ResultTest.php b/src/HttpServer/tests/Cases/ResultTest.php index 759a83c0..18919a00 100644 --- a/src/HttpServer/tests/Cases/ResultTest.php +++ b/src/HttpServer/tests/Cases/ResultTest.php @@ -18,6 +18,7 @@ use Hyperf\Contract\StdoutLoggerInterface; use Hyperf\Di\Container; use Hyperf\Di\Definition\DefinitionSourceFactory; +use Hyperf\Di\Exception\Exception; use Mine\HttpServer\Constant\HttpResultCode; use Mine\HttpServer\Contract\Log\RequestIdGeneratorInterface; use Mine\HttpServer\Log\RequestIdGenerator; @@ -32,9 +33,12 @@ */ class ResultTest extends TestCase { + /** + * @throws Exception + */ protected function setUp(): void { - ApplicationContext::setContainer(new Container((new DefinitionSourceFactory(true))())); + ApplicationContext::setContainer(new Container((new DefinitionSourceFactory())())); $config = new Config([ StdoutLoggerInterface::class => [ LogLevel::DEBUG, From adffdeaf617aa28d37bef7dee1e0a98ccc541af0 Mon Sep 17 00:00:00 2001 From: Zds <49744633+zds-s@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:25:08 +0800 Subject: [PATCH 7/7] Update --- CHANGELOG-2.0.md | 3 ++- CHANGELOG-2.0.zh_CN.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG-2.0.md b/CHANGELOG-2.0.md index 8c2fd6cc..6ad25acb 100644 --- a/CHANGELOG-2.0.md +++ b/CHANGELOG-2.0.md @@ -4,4 +4,5 @@ ## Added -- [#53](https://github.com/mineadmin/components/pull/53) Splitting components http-server \ No newline at end of file +- [#53](https://github.com/mineadmin/components/pull/53) Splitting components http-server +- [#55](https://github.com/mineadmin/components/pull/55) Splitting the crontab component \ No newline at end of file diff --git a/CHANGELOG-2.0.zh_CN.md b/CHANGELOG-2.0.zh_CN.md index 38ff172e..257f0d9c 100644 --- a/CHANGELOG-2.0.zh_CN.md +++ b/CHANGELOG-2.0.zh_CN.md @@ -4,4 +4,5 @@ ## Added -- [#53](https://github.com/mineadmin/components/pull/53) 拆分组件 http-server \ No newline at end of file +- [#53](https://github.com/mineadmin/components/pull/53) 拆分组件 http-server +- [#55](https://github.com/mineadmin/components/pull/55) 拆分优化组件 crontab \ No newline at end of file