...
 
Commits (12)
    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 https://gitcode.net/kanyxmo/mineadmin/-/commit/9fb31363d18cf5484e2918829341456f312d7561 add requestid in log 2023-07-29T23:56:25+08:00 wayhood wayhood@163.com https://gitcode.net/kanyxmo/mineadmin/-/commit/4091a3b4fe9241f1a4354238c5c70f5f06a378de Merge pull request #72 from netyum/master 2023-08-07T15:44:35+08:00 X.Mo 261091613@qq.com add requestid in log https://gitcode.net/kanyxmo/mineadmin/-/commit/3558e4316598a52ca9dace4e54b2ac6c5763b9d6 fix 2023-08-08T10:26:31+08:00 X.Mo root@imoi.cn https://gitcode.net/kanyxmo/mineadmin/-/commit/0cd274349ae91c4f0558217e18e933533d82e627 fix: 修复更新系统配置时,提示 `config_select_data` 未定义的bug 2023-08-16T13:55:12+08:00 X.Mo root@imoi.cn https://gitcode.net/kanyxmo/mineadmin/-/commit/22267d1896567cf769fd3d559bd61413f4b2812d fix: 修复上传的文件若在回收站则无法重新上传的问题 2023-08-16T14:54:47+08:00 X.Mo root@imoi.cn https://gitcode.net/kanyxmo/mineadmin/-/commit/15985cff0eb228b6c490039e2dc65d177853e744 feat: 新增sys_config() 和 sys_group_config() 函数 2023-08-16T15:50:37+08:00 X.Mo root@imoi.cn https://gitcode.net/kanyxmo/mineadmin/-/commit/68de22daed7c3a7a37a9bb83560f64d0c647a04e 插件根目录增加.htaccess文件 2023-08-18T16:49:33+08:00 X.Mo root@imoi.cn https://gitcode.net/kanyxmo/mineadmin/-/commit/c45438c5fa9abe747390dee1f4f01084f09f9ffb 感谢 @yimuyangshu 的pr 2023-08-22T16:37:19+08:00 X.Mo root@imoi.cn https://gitcode.net/kanyxmo/mineadmin/-/commit/569258f07c1c78f8140138f32ebfef2a333d6d8c Change 2023-08-25T16:16:40+08:00 X.Mo root@imoi.cn
......@@ -14,7 +14,7 @@
<img src="https://gitee.com/xmo/MineAdmin/badge/star.svg?theme=dark" />
<img src="https://gitee.com/xmo/MineAdmin/badge/fork.svg?theme=dark" />
<img src="https://svg.hamm.cn/badge.svg?key=License&value=Apache-2.0&color=da4a00" />
<img src="https://svg.hamm.cn/badge.svg?key=MineAdmin&value=v1.4.x" />
<img src="https://svg.hamm.cn/badge.svg?key=MineAdmin&value=v2.0.0" />
</p>
PHP有很多优秀的后台管理系统,但基于Swoole的后台管理系统没找到合适我自己的。
所以就开发了一套后台管理系统。系统可以用于网站管理后台、CMS、CRM、OA、ERP等。
......@@ -140,17 +140,5 @@ php bin/hyperf.php mine:install
## 通过 OSCS 安全认证
[![OSCS Status](https://www.oscs1024.com/platform/badge/kanyxmo/MineAdmin.svg?size=large)](https://www.murphysec.com/dr/9ztZvuSN6OLFjCDGVo)
## 演示图片
<img src="https://s1.ax1x.com/2022/07/31/vklKzR.jpg" />
<img src="https://s1.ax1x.com/2022/07/31/vkl8eK.jpg" />
<img src="https://s1.ax1x.com/2022/07/31/vkl1L6.jpg" />
<img src="https://s1.ax1x.com/2022/07/31/vklNJH.jpg" />
<img src="https://s1.ax1x.com/2022/07/31/vklJoD.jpg" />
<img src="https://s1.ax1x.com/2022/07/31/vkllsx.jpg" />
<img src="https://s1.ax1x.com/2022/07/31/vklZoF.jpg" />
<img src="https://s1.ax1x.com/2022/07/31/vklUWd.jpg" />
<img src="https://s1.ax1x.com/2022/07/31/vkl0yt.jpg" />
<img src="https://s1.ax1x.com/2022/07/31/vkltFe.jpg" />
<img src="https://s1.ax1x.com/2022/07/31/vkluW9.jpg" />
<img src="https://s1.ax1x.com/2022/07/31/vklnJJ.jpg" />
<img src="https://s1.ax1x.com/2022/07/31/vklmi4.jpg" />
## 状态
![Alt](https://repobeats.axiom.co/api/embed/83978ccc011a804a8b58acedfcaf894127d5bc85.svg "Repobeats analytics image")
......@@ -15,21 +15,22 @@ class UpdateVersion120 extends Migration
*/
public function up(): void
{
Schema::create('setting_datasource', function (Blueprint $table) {
$table->engine = 'Innodb';
$table->comment('数据源表');
$table->bigIncrements('id')->comment('主键');
$table->addColumn('string', 'source_name', ['length' => 32, 'comment' => '数据源名称']);
$table->addColumn('string', 'dsn', ['length'=> 255, 'comment' => '连接dsn字符串'])->nullable();
$table->addColumn('string', 'username', ['length'=> 64, 'comment' => '数据库名称'])->nullable();
$table->addColumn('string', 'password', ['length'=> 32, 'comment' => '数据库用户'])->nullable();
$table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable();
$table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable();
$table->addColumn('timestamp', 'created_at', ['precision' => 0, 'comment' => '创建时间'])->nullable();
$table->addColumn('timestamp', 'updated_at', ['precision' => 0, 'comment' => '更新时间'])->nullable();
$table->addColumn('string', 'remark', ['length' => 255, 'comment' => '备注'])->nullable();
});
if (! Schema::hasTable('setting_datasource')) {
Schema::create('setting_datasource', function (Blueprint $table) {
$table->engine = 'Innodb';
$table->comment('数据源表');
$table->bigIncrements('id')->comment('主键');
$table->addColumn('string', 'source_name', ['length' => 32, 'comment' => '数据源名称']);
$table->addColumn('string', 'dsn', ['length'=> 255, 'comment' => '连接dsn字符串'])->nullable();
$table->addColumn('string', 'username', ['length'=> 64, 'comment' => '数据库名称'])->nullable();
$table->addColumn('string', 'password', ['length'=> 32, 'comment' => '数据库用户'])->nullable();
$table->addColumn('bigInteger', 'created_by', ['comment' => '创建者'])->nullable();
$table->addColumn('bigInteger', 'updated_by', ['comment' => '更新者'])->nullable();
$table->addColumn('timestamp', 'created_at', ['precision' => 0, '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(
[
......
......@@ -33,7 +33,7 @@ class SettingConfigSeeder extends Seeder
"INSERT INTO `{$tableName}`(`group_id`, `key`, `value`, `name`, `input_type`, `config_select_data`, `sort`, `remark`) VALUES (1, 'site_record_number', NULL, '网站备案号', 'input', NULL, 95, NULL)",
"INSERT INTO `{$tableName}`(`group_id`, `key`, `value`, `name`, `input_type`, `config_select_data`, `sort`, `remark`) VALUES (2, 'upload_allow_file', 'txt,doc,docx,xls,xlsx,ppt,pptx,rar,zip,7z,gz,pdf,wps,md', '文件类型', 'input', NULL, 0, NULL)",
"INSERT INTO `{$tableName}`(`group_id`, `key`, `value`, `name`, `input_type`, `config_select_data`, `sort`, `remark`) VALUES (2, 'upload_allow_image', 'jpg,jpeg,png,gif,svg,bmp', '图片类型', 'input', NULL, 0, NULL)",
"INSERT INTO `{$tableName}`(`group_id`, `key`, `value`, `name`, `input_type`, `config_select_data`, `sort`, `remark`) VALUES (2, 'upload_mode', '1', '上传模式', 'select', '[{\"label\":\"本地上传\",\"value\":\"1\"},{\"label\":\"阿里云OSS\",\"value\":\"2\"},{\"label\":\"七牛云\",\"value\":\"3\"},{\"label\":\"腾讯云COS\",\"value\":\"4\"}]', 99, NULL)",
"INSERT INTO `{$tableName}`(`group_id`, `key`, `value`, `name`, `input_type`, `config_select_data`, `sort`, `remark`) VALUES (2, 'upload_mode', '1', '上传模式', 'select', '[{\"label\":\"本地上传\",\"value\":\"1\"},{\"label\":\"阿里云OSS\",\"value\":\"2\"},{\"label\":\"七牛云\",\"value\":\"3\"},{\"label\":\"腾讯云COS\",\"value\":\"4\"},{\"label\":\"ftp\",\"value\":\"5\"},{\"label\":\"memory\",\"value\":\"6\"},{\"label\":\"s3\",\"value\":\"7\"},{\"label\":\"minio\",\"value\":\"8\"}]', 99, NULL)",
];
foreach ($sql as $item) {
Db::insert($item);
......
......@@ -32,6 +32,20 @@ class SettingConfigMapper extends AbstractMapper
return $model ? $model->toArray() : [];
}
/**
* 按组的key获取一组配置信息
* @param string $groupKey
* @return array
*/
public function getConfigByGroupKey(string $groupKey): array
{
$prefix = env('DB_PREFIX');
return $this->model::query()->whereRaw(
sprintf('group_id = ( SELECT id FROM %ssetting_config_group WHERE code = ? )', $prefix),
[ $groupKey ]
)->get()->toArray();
}
/**
* 更新配置
* @param string $key
......@@ -40,7 +54,7 @@ class SettingConfigMapper extends AbstractMapper
*/
public function updateConfig(string $key, array $data): bool
{
if (is_array($data['config_select_data'])) {
if (isset($data['config_select_data']) && is_array($data['config_select_data'])) {
$data['config_select_data'] = json_encode($data['config_select_data'], JSON_UNESCAPED_UNICODE);
}
return $this->model::query()->where('key', $key)->update($data) > -1;
......
......@@ -85,6 +85,16 @@ class SettingConfigService extends AbstractService implements ConfigServiceInter
}
}
/**
* 按组的key获取一组配置信息
* @param string $groupKey
* @return array|null
*/
public function getConfigByGroupKey(string $groupKey): ?array
{
return $this->mapper->getConfigByGroupKey($groupKey);
}
/**
* 清除缓存
* @return bool
......
<?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);
}
}
......@@ -41,7 +41,13 @@ class SystemUploadFileMapper extends AbstractMapper
*/
public function getFileInfoByHash(string $hash)
{
return $this->model::query()->where('hash', $hash)->first();
$model = $this->model::query()->where('hash', $hash)->first();
if (! $model) {
$model = $this->model::withTrashed()->where('hash', $hash)->first(['id']);
$model && $model->forceDelete();
return null;
}
return $model;
}
/**
......@@ -79,11 +85,15 @@ class SystemUploadFileMapper extends AbstractMapper
$model = $this->model::withTrashed()->find($id);
if ($model) {
$storageMode = match ( $model->storage_mode ) {
1 => 'local' ,
2 => 'oss' ,
3 => 'qiniu' ,
4 => 'cos' ,
default => 'local' ,
'1' => 'local',
'2' => 'oss',
'3' => 'qiniu',
'4' => 'cos',
'5' => 'ftp',
'6' => 'memory',
'7' => 's3',
'8' => 'minio',
default => 'local',
};
$event = new \Mine\Event\RealDeleteUploadFile(
$model, $this->container->get(FilesystemFactory::class)->get($storageMode)
......
......@@ -9,30 +9,94 @@
* @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__));
$rebootCmd = sprintf('rm -rf %s/../runtime/container/* && php %s/hyperf.php start > /dev/null 2>/dev/null &', __DIR__, __DIR__);
$httpPort = isset($argv[2]) ? $argv[2] : 9501;
$messagePort = isset($argv[3]) ? $argv[3] : 9502;
if (shell_exec(sprintf('ps -ef | grep -v grep | grep %s', $pid))) {
shell_exec("kill -9 {$pid}");
shell_exec($rebootCmd);
} else {
killHyperfPid();
killHttpPort($httpPort);
killWebsocketPort($messagePort);
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);
echo "执行清理缓存代理成功\n";
}
// 执行 lsof 命令并查找 9502 端口
$output = shell_exec('lsof -i :9502 | grep LISTEN | awk \'{print $2}\'');
function killWebsocketPort($port = 9502)
{
echo "执行killWebsocketPort中\n";
// 将进程 ID 转换为数组
$pidList = explode("\n", trim($output));
$command = 'lsof -t -i:' . $port;
$output = shell_exec($command);
// 遍历进程 ID 列表并杀死相应进程
foreach ($pidList as $pid) {
if (is_numeric($pid)) {
shell_exec("kill -9 $pid");
echo "进程 $pid 已杀死\n";
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";
}
}
}
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";
}
......@@ -45,4 +45,41 @@ if (! function_exists('make')) {
return \Hyperf\Support\make($name, $parameters);
}
}
if (! function_exists('sys_config')) {
/**
* 获取后台系统配置
*
* @param string $key
* @param null|mixed $default
* @return mixed
* @throws RedisException
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
*/
function sys_config(string $key, mixed $default = null): mixed
{
return container()->get(\App\Setting\Service\SettingConfigService::class)->getConfigByKey($key) ?? $default;
}
}
if (! function_exists('sys_group_config')) {
/**
* 获取后台系统配置
*
* @param string $groupKey
* @param null|mixed $default
* @return mixed
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
*/
function sys_group_config(string $groupKey, mixed $default = []): mixed
{
return container()->get(\App\Setting\Service\SettingConfigService::class)->getConfigByGroupKey($groupKey) ?? $default;
}
}
\ No newline at end of file
......@@ -12,6 +12,7 @@
"require": {
"xmo/mine-core": "1.4.*",
"xmo/mine-office": "1.3.*",
"xmo/mine-translatable": "1.0.*",
"xmo/jwt-auth": "0.6.*"
},
"require-dev": {
......
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "90d6294f06a33bf77fbc8d81dd669ca8",
"content-hash": "db0e9847e080c9a938edf56073c993a9",
"packages": [
{
"name": "aliyuncs/oss-sdk-php",
......@@ -8557,16 +8557,16 @@
},
{
"name": "xmo/mine-core",
"version": "v1.4.3",
"version": "v1.4.5",
"source": {
"type": "git",
"url": "https://github.com/kanyxmo/mine.git",
"reference": "369806385de57bda90745d95f9d9ff4d0f957d5a"
"reference": "ece86872644093eba4565297e96dc6d018fce0d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/kanyxmo/mine/zipball/369806385de57bda90745d95f9d9ff4d0f957d5a",
"reference": "369806385de57bda90745d95f9d9ff4d0f957d5a",
"url": "https://api.github.com/repos/kanyxmo/mine/zipball/ece86872644093eba4565297e96dc6d018fce0d9",
"reference": "ece86872644093eba4565297e96dc6d018fce0d9",
"shasum": ""
},
"require": {
......@@ -8646,9 +8646,9 @@
],
"support": {
"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",
......@@ -8689,6 +8689,88 @@
},
"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",
"version": "v1.0.2",
......
......@@ -26,6 +26,10 @@ return [
'allowInlineLineBreaks' => true,
],
],
// 暂时先注释了,mine-core 还未发新版支持这个
// 'processor' => [
// 'class' => Mine\Log\Processor\UuidRequestIdProcessor::class,
// ],
],
'sql' => [
'handler' => [
......@@ -43,5 +47,8 @@ return [
'allowInlineLineBreaks' => true,
],
],
// 'processor' => [
// 'class' => Mine\Log\Processor\UuidRequestIdProcessor::class,
// ],
],
];
<?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' => '%',
],
];