...
 
Commits (3)
    https://gitcode.net/kanyxmo/mineadmin/-/commit/7eb2c3170b9bc710726beb2794397ff1298e6091 升级 mine-core 到 1.4.4版本,新增xmo/mine-translatable 依赖 2023-07-18T14:16:07+08:00 X.Mo root@imoi.cn https://gitcode.net/kanyxmo/mineadmin/-/commit/b3362d9cc6b0eae6f068796d94a2c0b6002901af refactor: 感谢最菜兄优化 `bin/reboot.php`,mine-core的amqp队列监听器移动到 App\System\Listener... 2023-07-27T15:53:51+08:00 X.Mo root@imoi.cn https://gitcode.net/kanyxmo/mineadmin/-/commit/be0d45d0050fc839b2f1e7d859406723f4d83b83 fix: 创建setting_datasource表之前,检查表是否存在 2023-07-27T17:25:31+08:00 X.Mo root@imoi.cn
...@@ -15,21 +15,22 @@ class UpdateVersion120 extends Migration ...@@ -15,21 +15,22 @@ class UpdateVersion120 extends Migration
*/ */
public function up(): void public function up(): void
{ {
Schema::create('setting_datasource', function (Blueprint $table) { if (! Schema::hasTable('setting_datasource')) {
$table->engine = 'Innodb'; Schema::create('setting_datasource', function (Blueprint $table) {
$table->comment('数据源表'); $table->engine = 'Innodb';
$table->bigIncrements('id')->comment('主键'); $table->comment('数据源表');
$table->addColumn('string', 'source_name', ['length' => 32, 'comment' => '数据源名称']); $table->bigIncrements('id')->comment('主键');
$table->addColumn('string', 'dsn', ['length'=> 255, 'comment' => '连接dsn字符串'])->nullable(); $table->addColumn('string', 'source_name', ['length' => 32, 'comment' => '数据源名称']);
$table->addColumn('string', 'username', ['length'=> 64, 'comment' => '数据库名称'])->nullable(); $table->addColumn('string', 'dsn', ['length'=> 255, 'comment' => '连接dsn字符串'])->nullable();
$table->addColumn('string', 'password', ['length'=> 32, 'comment' => '数据库用户'])->nullable(); $table->addColumn('string', 'username', ['length'=> 64, 'comment' => '数据库名称'])->nullable();
$table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable(); $table->addColumn('string', 'password', ['length'=> 32, 'comment' => '数据库用户'])->nullable();
$table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable(); $table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable();
$table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable(); $table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable();
$table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable(); $table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable();
$table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable(); $table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable();
}); $table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable();
});
}
// 菜单数据 // 菜单数据
$pid = Db::table('system_menu')->insertGetId( $pid = Db::table('system_menu')->insertGetId(
[ [
......
<?php
/**
* 站内消息队列消费监听器
*/
declare(strict_types=1);
namespace App\System\Listener;
use Mine\Interfaces\ServiceInterface\QueueLogServiceInterface;
use Mine\Amqp\Event\AfterConsume;
use Mine\Amqp\Event\BeforeConsume;
use Mine\Amqp\Event\ConsumeEvent;
use Mine\Amqp\Event\FailToConsume;
use Mine\Amqp\Event\WaitTimeout;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\Event\Annotation\Listener;
/**
* 消费队列监听
* Class QueueConsumeListener
* @package Mine\Amqp\Listener
*/
#[Listener]
class QueueConsumeListener implements ListenerInterface
{
/**
* @Message("未消费")
*/
const CONSUME_STATUS_NO = 1;
/**
* @Message("消费中")
*/
const CONSUME_STATUS_DOING = 2;
/**
* @Message("消费成功")
*/
const CONSUME_STATUS_SUCCESS = 3;
/**
* @Message("消费失败")
*/
const CONSUME_STATUS_FAIL = 4;
/**
* @Message("消费重复")
*/
const CONSUME_STATUS_REPEAT = 5;
private QueueLogServiceInterface $service;
public function listen(): array
{
// 返回一个该监听器要监听的事件数组,可以同时监听多个事件
return [
AfterConsume::class,
BeforeConsume::class,
ConsumeEvent::class,
FailToConsume::class,
WaitTimeout::class,
];
}
/**
* @param object $event
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
*/
public function process(object $event): void
{
$this->service = container()->get(QueueLogServiceInterface::class);
if ($event->message) {
$class = get_class($event);
$func = lcfirst(trim(strrchr($class, '\\'),'\\'));
$this->$func($event);
}
}
/**
* Description:消费前
* User:mike
* @param object $event
*/
public function beforeConsume(object $event)
{
$this->service->update(
(int)$event->data['queue_id'],
['consume_status' => self::CONSUME_STATUS_DOING]
);
}
/**
* Description:消费中
* User:mike
* @param object $event
*/
public function consumeEvent(object $event)
{
// TODO...
}
/**
* Description:消费后
* User:mike
* @param object $event
*/
public function afterConsume(object $event)
{
$this->service->update(
(int)$event->data['queue_id'],
['consume_status' => self::CONSUME_STATUS_SUCCESS]
);
}
/**
* Description:消费失败
* User:mike
* @param object $event
*/
public function failToConsume(object $event)
{
$this->service->update(
(int)$event->data['queue_id'], [
'consume_status' => self::CONSUME_STATUS_REPEAT,
'log_content' => $event->throwable ?: $event->throwable->getMessage()
]);
}
}
<?php
/**
* 站内消息队列生产监听器
*/
declare(strict_types=1);
namespace App\System\Listener;
use App\System\Queue\Producer\MessageProducer;
use Mine\Interfaces\ServiceInterface\QueueMessageServiceInterface;
use Mine\Interfaces\ServiceInterface\QueueLogServiceInterface;
use Hyperf\Context\Context;
use Mine\Amqp\Event\AfterProduce;
use Mine\Amqp\Event\BeforeProduce;
use Mine\Amqp\Event\FailToProduce;
use Mine\Amqp\Event\ProduceEvent;
use Mine\Amqp\Event\WaitTimeout;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\Event\Annotation\Listener;
/**
* 生产队列监听
* Class QueueProduceListener
* @package Mine\Amqp\Listener
*/
#[Listener]
class QueueProduceListener implements ListenerInterface
{
/**
* @Message("未生产")
*/
const PRODUCE_STATUS_WAITING = 1;
/**
* @Message("生产中")
*/
const PRODUCE_STATUS_DOING = 2;
/**
* @Message("生产成功")
*/
const PRODUCE_STATUS_SUCCESS = 3;
/**
* @Message("生产失败")
*/
const PRODUCE_STATUS_FAIL = 4;
/**
* @Message("生产重复")
*/
const PRODUCE_STATUS_REPEAT = 5;
private QueueLogServiceInterface $service;
public function listen(): array
{
// 返回一个该监听器要监听的事件数组,可以同时监听多个事件
return [
AfterProduce::class,
BeforeProduce::class,
ProduceEvent::class,
FailToProduce::class,
WaitTimeout::class,
];
}
/**
* @param object $event
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
* @throws \Exception
*/
public function process(object $event): void
{
$this->service = container()->get(QueueLogServiceInterface::class);
$class = get_class($event);
$func = lcfirst(trim(strrchr($class, '\\'),'\\'));
$this->$func($event);
}
/**
* Description:生产前
* User:mike, x.mo
* @param object $event
*/
public function beforeProduce(object $event)
{
$queueName = strchr($event->producer->getRoutingKey(), '.', true) . '.queue';
$id = $this->service->save([
'exchange_name' => $event->producer->getExchange(),
'routing_key_name' => $event->producer->getRoutingKey(),
'queue_name' => $queueName,
'queue_content' => $event->producer->payload(),
'delay_time' => $event->delayTime ?? 0,
'produce_status' => self::PRODUCE_STATUS_SUCCESS
]);
$this->setId($id);
$payload = json_decode($event->producer->payload(), true);
if (!isset($payload['queue_id'])) {
$event->producer->setPayload([
'queue_id' => $id, 'data' => $payload
]);
}
$this->service->update($id, [ 'queue_content' => $event->producer->payload() ]);
}
/**
* Description:生产中
* User:mike, x.mo
* @param object $event
*/
public function produceEvent(object $event): void
{
// TODO...
}
/**
* Description:生产后
* User:mike, x.mo
* @param object $event
*/
public function afterProduce(object $event): void
{
// 只针对站内消息
if (isset($event->producer) && $event->producer instanceof MessageProducer) {
container()->get(QueueMessageServiceInterface::class)->save(
json_decode($event->producer->payload(), true)['data']
);
}
}
/**
* Description:生产失败
* User:mike, x.mo
*/
public function failToProduce(object $event): void
{
$this->service->update((int) $this->getId(), [
'produce_status' => self::PRODUCE_STATUS_FAIL,
'log_content' => $event->throwable ?: $event->throwable->getMessage()
]);
}
public function setId(int $id): void
{
Context::set('id', $id);
}
public function getId(): int
{
return Context::get('id', 0);
}
}
...@@ -9,30 +9,94 @@ ...@@ -9,30 +9,94 @@
* @Link https://gitee.com/xmo/MineAdmin * @Link https://gitee.com/xmo/MineAdmin
*/ */
/** declare(strict_types=1);
/*
* 强制重启服务脚本,并清理缓存代理类 * 强制重启服务脚本,并清理缓存代理类
*/ */
$env = isset($argv[1]) ? $argv[1] : 'dev';
$pid = shell_exec(sprintf('cat %s/../runtime/hyperf.pid', __DIR__)); $httpPort = isset($argv[2]) ? $argv[2] : 9501;
$rebootCmd = sprintf('rm -rf %s/../runtime/container/* && php %s/hyperf.php start > /dev/null 2>/dev/null &', __DIR__, __DIR__); $messagePort = isset($argv[3]) ? $argv[3] : 9502;
if (shell_exec(sprintf('ps -ef | grep -v grep | grep %s', $pid))) { killHyperfPid();
shell_exec("kill -9 {$pid}"); killHttpPort($httpPort);
shell_exec($rebootCmd); killWebsocketPort($messagePort);
} else {
startService($env);
function startService($env)
{
echo "启动{$env}服务\n";
if ($env == 'dev'){
$rebootCmd = sprintf('php %s/hyperf.php server:watch > /dev/tty', __DIR__);
shell_exec($rebootCmd);
}else{
$rebootCmd = sprintf('php %s/hyperf.php start > /dev/null', __DIR__);
shell_exec($rebootCmd);
}
}
function killHyperfPid()
{
echo "执行killHyperfPid中\n";
$pid = shell_exec(sprintf('cat %s/../runtime/hyperf.pid', __DIR__));
$rebootCmd = sprintf('rm -rf %s/../runtime/container/*', __DIR__);
// $rebootCmd = sprintf('rm -rf %s/../runtime/container/* && php %s/hyperf.php start > /dev/null 2>/dev/null &', __DIR__, __DIR__);
if (shell_exec(sprintf('ps -ef | grep -v grep | grep %s', $pid))) {
shell_exec("kill -9 {$pid}");
}
echo "执行killHyperfPid完成\n";
echo "执行清理缓存代理中\n";
shell_exec($rebootCmd); shell_exec($rebootCmd);
echo "执行清理缓存代理成功\n";
} }
// 执行 lsof 命令并查找 9502 端口 function killWebsocketPort($port = 9502)
$output = shell_exec('lsof -i :9502 | grep LISTEN | awk \'{print $2}\''); {
echo "执行killWebsocketPort中\n";
// 将进程 ID 转换为数组 $command = 'lsof -t -i:' . $port;
$pidList = explode("\n", trim($output)); $output = shell_exec($command);
// 遍历进程 ID 列表并杀死相应进程 if ($output) {
foreach ($pidList as $pid) { $pidArray = explode("\n", trim($output));
if (is_numeric($pid)) {
shell_exec("kill -9 $pid"); $pidList = array_filter($pidArray, 'strlen');
echo "进程 $pid 已杀死\n";
foreach ($pidList as $pid) {
if (is_numeric($pid)) {
shell_exec("kill -9 {$pid}");
echo __FUNCTION__ . ":{$port}端口进程 {$pid} 已杀死\n";
}
}
}
echo "执行killWebsocketPort完成\n";
}
function killHttpPort($port = 9501)
{
echo "执行killHttpPort中\n";
$command = 'lsof -t -i:' . $port;
$output = shell_exec($command);
if ($output) {
$pidArray = explode("\n", trim($output));
$pidList = array_filter($pidArray, 'strlen');
foreach ($pidList as $pid) {
if (is_numeric($pid)) {
shell_exec("kill -9 {$pid}");
echo __FUNCTION__ . ":{$port}端口进程 {$pid} 已杀死\n";
}
}
} }
}
\ No newline at end of file echo "执行killHttpPort完成\n";
}
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
"require": { "require": {
"xmo/mine-core": "1.4.*", "xmo/mine-core": "1.4.*",
"xmo/mine-office": "1.3.*", "xmo/mine-office": "1.3.*",
"xmo/mine-translatable": "1.0.*",
"xmo/jwt-auth": "0.6.*" "xmo/jwt-auth": "0.6.*"
}, },
"require-dev": { "require-dev": {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "90d6294f06a33bf77fbc8d81dd669ca8", "content-hash": "db0e9847e080c9a938edf56073c993a9",
"packages": [ "packages": [
{ {
"name": "aliyuncs/oss-sdk-php", "name": "aliyuncs/oss-sdk-php",
...@@ -8557,16 +8557,16 @@ ...@@ -8557,16 +8557,16 @@
}, },
{ {
"name": "xmo/mine-core", "name": "xmo/mine-core",
"version": "v1.4.3", "version": "v1.4.5",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/kanyxmo/mine.git", "url": "https://github.com/kanyxmo/mine.git",
"reference": "369806385de57bda90745d95f9d9ff4d0f957d5a" "reference": "ece86872644093eba4565297e96dc6d018fce0d9"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/kanyxmo/mine/zipball/369806385de57bda90745d95f9d9ff4d0f957d5a", "url": "https://api.github.com/repos/kanyxmo/mine/zipball/ece86872644093eba4565297e96dc6d018fce0d9",
"reference": "369806385de57bda90745d95f9d9ff4d0f957d5a", "reference": "ece86872644093eba4565297e96dc6d018fce0d9",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
...@@ -8646,9 +8646,9 @@ ...@@ -8646,9 +8646,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/kanyxmo/mine/issues", "issues": "https://github.com/kanyxmo/mine/issues",
"source": "https://github.com/kanyxmo/mine/tree/v1.4.3" "source": "https://github.com/kanyxmo/mine/tree/v1.4.5"
}, },
"time": "2023-07-11T01:58:58+00:00" "time": "2023-07-27T07:07:30+00:00"
}, },
{ {
"name": "xmo/mine-office", "name": "xmo/mine-office",
...@@ -8689,6 +8689,88 @@ ...@@ -8689,6 +8689,88 @@
}, },
"time": "2023-06-15T09:32:43+00:00" "time": "2023-06-15T09:32:43+00:00"
}, },
{
"name": "xmo/mine-translatable",
"version": "v1.0.0",
"source": {
"type": "git",
"url": "https://github.com/kanyxmo/translatable.git",
"reference": "84415003b921b678f869d1fa94573f50840ae8d2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/kanyxmo/translatable/zipball/84415003b921b678f869d1fa94573f50840ae8d2",
"reference": "84415003b921b678f869d1fa94573f50840ae8d2",
"shasum": ""
},
"require": {
"ext-swoole": ">=4.5",
"hyperf/config": "3.0.*",
"hyperf/database": "3.0.*",
"hyperf/db-connection": "3.0.*",
"hyperf/di": "3.0.*",
"hyperf/framework": "3.0.*",
"hyperf/model-listener": "3.0.*",
"hyperf/translation": "3.0.*",
"php": ">=8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.0",
"hyperf/testing": "3.0.*",
"mockery/mockery": "^1.0",
"phpstan/phpstan": "^1.0",
"swoole/ide-helper": "^5.0"
},
"type": "library",
"extra": {
"hyperf": {
"config": "Mine\\Translatable\\ConfigProvider"
}
},
"autoload": {
"psr-4": {
"Mine\\Translatable\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "X.Mo",
"email": "root@imoi.cn"
},
{
"name": "Eric Zhu",
"email": "eric@zhu.email"
},
{
"name": "Tom Witkowski",
"email": "gummibeer@astrotomic.info",
"homepage": "https://gummibeer.de",
"role": "Developer"
},
{
"name": "Dimitrios Savvopoulos",
"email": "ds@dimsav.com",
"homepage": "http://dimsav.com",
"role": "Developer"
}
],
"description": "The Hyperf Multilingual Model package.",
"keywords": [
"database",
"hyperf",
"language",
"php",
"translation"
],
"support": {
"source": "https://github.com/kanyxmo/translatable/tree/v1.0.0"
},
"time": "2023-07-17T06:53:07+00:00"
},
{ {
"name": "yurunsoft/phpmailer-swoole", "name": "yurunsoft/phpmailer-swoole",
"version": "v1.0.2", "version": "v1.0.2",
......
<?php
declare(strict_types=1);
/**
* This file is part of xmo/translatable.
*
* @link https://github.com/xmo/translatable
* @contact root@imoi.cn
* @license https://github.com/xmo/translatable/blob/master/LICENSE
*/
return [
/*
|--------------------------------------------------------------------------
| Application Locales
|--------------------------------------------------------------------------
|
| Contains an array with the applications available locales.
|
*/
'locales' => [
'en',
'zh' => [
'CN',
'TW',
],
],
/*
|--------------------------------------------------------------------------
| Locale separator
|--------------------------------------------------------------------------
|
| This is a string used to glue the language and the country when defining
| the available locales. Example: if set to '-', then the locale for
| colombian spanish will be saved as 'es-CO' into the database.
|
*/
'locale_separator' => '_',
/*
|--------------------------------------------------------------------------
| Default locale
|--------------------------------------------------------------------------
|
| As a default locale, Translatable takes the locale of `hyperf/translation`.
| If for some reason you want to override this,
| you can specify what default should be used here.
| If you set a value here it will only use the current config value
| and never fallback to the translator one.
|
*/
'locale' => null,
/*
|--------------------------------------------------------------------------
| Use fallback
|--------------------------------------------------------------------------
|
| Determine if fallback locales are returned by default or not. To add
| more flexibility and configure this option per "translatable"
| instance, this value will be overridden by the property
| $useTranslationFallback when defined
|
*/
'use_fallback' => false,
/*
|--------------------------------------------------------------------------
| Use fallback per property
|--------------------------------------------------------------------------
|
| The property fallback feature will return the translated value of
| the fallback locale if the property is empty for the selected
| locale. Note that 'use_fallback' must be enabled.
|
*/
'use_property_fallback' => true,
/*
|--------------------------------------------------------------------------
| Fallback Locale
|--------------------------------------------------------------------------
|
| A fallback locale is the locale being used to return a translation
| when the requested translation is not existing. To disable it
| set it to false.
| If set to null it will loop through all configured locales until
| one existing is found or end of list reached. The locales are looped
| from top to bottom and for country based locales the simple one
| is used first. So "es" will be checked before "es_MX".
|
*/
'fallback_locale' => 'en',
/*
|--------------------------------------------------------------------------
| Translation Model Namespace
|--------------------------------------------------------------------------
|
| Defines the default 'Translation' class namespace. For example, if
| you want to use App\Translations\CountryTranslation instead of App\CountryTranslation
| set this to 'App\Translations'.
|
*/
'translation_model_namespace' => null,
/*
|--------------------------------------------------------------------------
| Translation Suffix
|--------------------------------------------------------------------------
|
| Defines the default 'Translation' class suffix. For example, if
| you want to use CountryTrans instead of CountryTranslation
| application, set this to 'Trans'.
|
*/
'translation_suffix' => 'Translation',
/*
|--------------------------------------------------------------------------
| Locale key
|--------------------------------------------------------------------------
|
| Defines the 'locale' field name, which is used by the
| translation model.
|
*/
'locale_key' => 'locale',
/*
|--------------------------------------------------------------------------
| Always load translations when converting to array
|--------------------------------------------------------------------------
|
| Setting this to false will have a performance improvement but will
| not return the translations when using toArray(), unless the
| translations relationship is already loaded.
|
*/
'to_array_always_loads_translations' => true,
/*
|--------------------------------------------------------------------------
| Configure the default behavior of the rule factory
|--------------------------------------------------------------------------
|
| The default values used to control the behavior of the RuleFactory.
| Here you can set your own default format and delimiters for
| your whole app.
|
*/
'rule_factory' => [
'format' => \Mine\Translatable\Validation\RuleFactory::FORMAT_ARRAY,
'prefix' => '%',
'suffix' => '%',
],
];