first commit

This commit is contained in:
2026-01-18 09:52:48 +08:00
commit 836bdc9409
584 changed files with 40891 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[docker-compose.yml]
indent_size = 4
+70
View File
@@ -0,0 +1,70 @@
APP_NAME=sentos
VERSION=1.0.0
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_TIMEZONE=Asia/Shanghai
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
SESSION_DRIVER=file
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=file
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
OSS_ACCESS_KEY_ID=
OSS_ACCESS_KEY_SECRET=
OSS_BUCKET=nowart
OSS_ENDPOINT=oss-cn-hangzhou.aliyuncs.com
VITE_APP_NAME="${APP_NAME}"
+11
View File
@@ -0,0 +1,11 @@
* text=auto eol=lf
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
.styleci.yml export-ignore
+30
View File
@@ -0,0 +1,30 @@
/.phpunit.cache
/node_modules
/public/build
/public/hot
/public/storage
/public/static
/public/assets
/public/*.js
/public/.user.ini
/storage/*.key
/vendor
/resources/admin-bak
.env
.env.backup
.env.production
.phpactor.json
.phpunit.result.cache
Homestead.json
Homestead.yaml
auth.json
npm-debug.log
yarn-error.log
/.fleet
/.idea
/.vscode
composer.lock
bootstrap/cache/*
package-lock.json
yarn.lock
/resources/antdvadmin
+1
View File
@@ -0,0 +1 @@
+194
View File
@@ -0,0 +1,194 @@
木兰宽松许可证,第2版
木兰宽松许可证,第2版
2020年1月 http://license.coscl.org.cn/MulanPSL2
您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束:
0. 定义
“软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
“贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
“法人实体” 是指提交贡献的机构及其“关联实体”。
“关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是
指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
1. 授予版权许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可
以复制、使用、修改、分发其“贡献”,不论修改与否。
2. 授予专利许可
每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定
撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡
献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软
件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“
关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或
其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权
行动之日终止。
3. 无商标许可
“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定
的声明义务而必须使用除外。
4. 分发限制
您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“
本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
5. 免责声明与责任限制
“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对
任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于
何种法律理论,即使其曾被建议有此种损失的可能性。
6. 语言
“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文
版为准。
条款结束
如何将木兰宽松许可证,第2版,应用到您的软件
如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步:
1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中;
3, 请将如下声明文本放入每个源文件的头部注释中。
Copyright (c) [Year] [name of copyright holder]
[Software Name] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan
PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
Mulan Permissive Software LicenseVersion 2
Mulan Permissive Software LicenseVersion 2 (Mulan PSL v2)
January 2020 http://license.coscl.org.cn/MulanPSL2
Your reproduction, use, modification and distribution of the Software shall
be subject to Mulan PSL v2 (this License) with the following terms and
conditions:
0. Definition
Software means the program and related documents which are licensed under
this License and comprise all Contribution(s).
Contribution means the copyrightable work licensed by a particular
Contributor under this License.
Contributor means the Individual or Legal Entity who licenses its
copyrightable work under this License.
Legal Entity means the entity making a Contribution and all its
Affiliates.
Affiliates means entities that control, are controlled by, or are under
common control with the acting entity under this License, control means
direct or indirect ownership of at least fifty percent (50%) of the voting
power, capital or other securities of controlled or commonly controlled
entity.
1. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby
grants to you a perpetual, worldwide, royalty-free, non-exclusive,
irrevocable copyright license to reproduce, use, modify, or distribute its
Contribution, with modification or not.
2. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby
grants to you a perpetual, worldwide, royalty-free, non-exclusive,
irrevocable (except for revocation under this Section) patent license to
make, have made, use, offer for sale, sell, import or otherwise transfer its
Contribution, where such patent license is only limited to the patent claims
owned or controlled by such Contributor now or in future which will be
necessarily infringed by its Contribution alone, or by combination of the
Contribution with the Software to which the Contribution was contributed.
The patent license shall not apply to any modification of the Contribution,
and any other combination which includes the Contribution. If you or your
Affiliates directly or indirectly institute patent litigation (including a
cross claim or counterclaim in a litigation) or other patent enforcement
activities against any individual or entity by alleging that the Software or
any Contribution in it infringes patents, then any patent license granted to
you under this License for the Software shall terminate as of the date such
litigation or activity is filed or taken.
3. No Trademark License
No trademark license is granted to use the trade names, trademarks, service
marks, or product names of Contributor, except as required to fulfill notice
requirements in section 4.
4. Distribution Restriction
You may distribute the Software in any medium with or without modification,
whether in source or executable forms, provided that you provide recipients
with a copy of this License and retain copyright, patent, trademark and
disclaimer statements in the Software.
5. Disclaimer of Warranty and Limitation of Liability
THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY
KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR
COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT
LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING
FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO
MATTER HOW ITS CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGES.
6. Language
THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION
AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF
DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION
SHALL PREVAIL.
END OF THE TERMS AND CONDITIONS
How to Apply the Mulan Permissive Software LicenseVersion 2
(Mulan PSL v2) to Your Software
To apply the Mulan PSL v2 to your work, for easy identification by
recipients, you are suggested to complete following three steps:
i. Fill in the blanks in following statement, including insert your software
name, the year of the first publication of your software, and your name
identified as the copyright owner;
ii. Create a file named "LICENSE" which contains the whole context of this
License in the first directory of your software package;
iii. Attach the statement to the appropriate annotated syntax at the
beginning of each source file.
Copyright (c) [Year] [name of copyright holder]
[Software Name] is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan
PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
+56
View File
@@ -0,0 +1,56 @@
### Laradmin
Laradmin是一个基于Laravel + Vue 开发的后台管理系统,基础功能包括:用户管理、角色管理、菜单管理、部门管理、日志管理、字典管理、参数管理、定时任务、城市管理、客户端管理、图片上传、富文本编辑器
### 演示地址
http://www.sentcms.com
账号:admin
密码:admin888
(请勿修改密码,数据定期清理)
### 运行环境
1. PHP >= 7.2.0
2. MySQL >= 5.7.0
3. Redis >= 4.0.0
4. Laravel >= 11.0.0
5. Vue >= 3.0.0
6. Node >= 16.0.0
7. Vite >= 4.0.0
### 服务器端安装
1. 运行composer install
2. 运行composer install-project
3. 运行php artisan migrate
4. 运行php artisan module:seed
5. 运行php artisan serve
6. 访问http://localhost:8000
### 前端安装
1. cd resources/admin
2. yarn install
3. yarn run serve
4. 访问http://localhost:8080
### 线上部署
1. 运行composer install
2. 运行composer install-project
3. 运行php artisan migrate
4. 运行php artisan module:seed
5. 运行cd resources/admin && yarn install && yarn run build
6. 访问http://域名/admin
### 功能
- [x] 用户管理 后台用户管理
- [x] 部门管理 配置公司的部门结构,支持树形结构
- [x] 菜单管理 配置系统菜单,按钮等等
- [x] 角色管理 配置用户担当的角色,分配权限
- [x] 配置管理 参数的管理
- [x] 字典管理 字典数据管理
- [x] 城市数据 城市数据管理
- [x] 客户端管理 客户端管理
- [x] 定时任务 定时任务管理
- [x] 操作日志 后台用户操作记录
- [x] 图片上传 图片上传管理
- [x] 富文本编辑器 富文本编辑器管理
- [ ] 应用管理 应用管理
+87
View File
@@ -0,0 +1,87 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Console\Commands;
use Illuminate\Console\Command;
use GatewayWorker\BusinessWorker;
use GatewayWorker\Gateway;
use GatewayWorker\Register;
use Workerman\Worker;
use App\Events\WorkermanEvent;
class Workerman extends Command {
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'work {action} {--d}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Start a Workerman server.';
/**
* Execute the console command.
*/
public function handle() {
global $argv;
$action = $this->argument('action');
$argv[0] = 'work';
$argv[1] = $action;
$argv[2] = $this->option('d') ? '-d' : '';
$this->start();
}
public function start() {
$this->startGateWay();
$this->startBusinessWorker();
$this->startRegister();
$workerPath = storage_path('workerman/');
if (!is_dir($workerPath))
mkdir($workerPath, 0755, true);
Worker::$pidFile = $workerPath . config('app.name') . '_workman.pid';
$logPath = $workerPath . date('Ym') . '/';
if (!is_dir($logPath))
mkdir($logPath, 0755, true);
Worker::$logFile = $logPath . date('d') . '.log';
Worker::runAll();
}
public function startGateWay() {
$gateway = new Gateway("websocket://0.0.0.0:2346");
$gateway->name = 'Gateway';
$gateway->count = 4;
$gateway->lanIp = '127.0.0.1';
$gateway->startPort = 2900;
$gateway->pingInterval = 10;
$gateway->pingNotResponseLimit = 1;
$gateway->pingData = '{"type":"pong"}';
$gateway->registerAddress = '127.0.0.1:1236';
}
public function startBusinessWorker() {
$worker = new BusinessWorker();
$worker->name = 'BusinessWorker';
$worker->count = 3;
$worker->registerAddress = '127.0.0.1:1236';
$worker->eventHandler = WorkermanEvent::class;
}
public function startRegister() {
$register = new Register('text://0.0.0.0:1236');
}
}
+81
View File
@@ -0,0 +1,81 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Console\Commands;
use Illuminate\Console\Command;
use GatewayWorker\BusinessWorker;
use GatewayWorker\Gateway;
use GatewayWorker\Register;
use Workerman\Worker;
use App\Events\WorkermanEvent;
class WorkermanWin extends Command {
// 兼容windows
protected $signature = 'wk {action : action} {--start=all : start} {--d : daemon mode}';
protected $description = 'Start a Workerman server.';
public function handle() {
global $argv;
$action = $this->argument('action');
//针对 Windows 一次执行,无法注册多个协议的特殊处理
if ($action === 'single') {
$start = $this->option('start');
if ($start === 'register') {
$this->startRegister();
} elseif ($start === 'gateway') {
$this->startGateWay();
} elseif ($start === 'worker') {
$this->startBusinessWorker();
}
Worker::runAll();
return;
}
$argv[1] = $action;
$argv[2] = $this->option('d') ? '-d' : '';
$this->start();
}
public function start() {
$this->startGateWay();
$this->startBusinessWorker();
$this->startRegister();
Worker::runAll();
}
public function startGateWay() {
$gateway = new Gateway("websocket://0.0.0.0:2346");
$gateway->name = 'Gateway';
$gateway->count = 4;
$gateway->lanIp = '127.0.0.1';
$gateway->startPort = 2900;
$gateway->pingInterval = 10;
$gateway->pingNotResponseLimit = 1;
$gateway->pingData = '{"type":"pong"}';
$gateway->registerAddress = '127.0.0.1:1236';
}
public function startBusinessWorker() {
$worker = new BusinessWorker();
$worker->name = 'BusinessWorker';
$worker->count = 3;
$worker->registerAddress = '127.0.0.1:1236';
$worker->eventHandler = WorkermanEvent::class;
}
public function startRegister() {
$register = new Register('text://0.0.0.0:1236');
}
}
+103
View File
@@ -0,0 +1,103 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Events;
use GatewayWorker\BusinessWorker;
use GatewayWorker\Lib\Gateway;
use Illuminate\Support\Facades\Log;
use Workerman\Lib\Timer;
class WorkermanEvent {
/**
* @title worker 启动时触发
*
* @param BusinessWorker $businessWorker
* @return void
*/
public static function onWorkerStart(BusinessWorker $businessWorker) {
self::log(__FUNCTION__, $businessWorker->workerId);
// 向所有客户端连接发送数据
// Gateway::sendToAll("worker started");
// 定时向所有客户端连接发送数据
Timer::add(1, function() use ($businessWorker) {
$time_now = time();
foreach ($businessWorker->connections as $connection) {
if (empty($connection->lastMessageTime)){
$connection->lastMessageTime = $time_now;
continue;
}
if ($time_now - $connection->lastMessageTime > 10) {
$connection->lastMessageTime = $time_now;
Gateway::sendToClient($connection->id, 'pong');
}
}
});
}
/**
* @title 当客户端连接时触发
*
* @param int $client_id 连接id
* @return void
*/
public static function onConnect($client_id) {
self::log(__FUNCTION__, $client_id);
}
/**
* @title 客户端连接时触发
*
* @param int $client_id 连接id
* @param mixed $message 具体消息
* @return void
*/
public static function onWebSocketConnect($client_id, $data) {
self::log(__FUNCTION__, $client_id, $data);
}
/**
* @title 当客户端发来消息时触发
*
* @param int $client_id 连接id
* @param mixed $message 具体消息
* @return void
*/
public static function onMessage(string $client_id, string $message) {
self::log(__FUNCTION__, $client_id, $message);
if (empty($message)) {
return;
}
Gateway::sendToAll($message);
if ($message == 'ping') {
Gateway::sendToClient($client_id, 'pong');
return;
}
}
/**
* @title 当用户断开连接时触发
*
* @param int $client_id 连接id
* @return void
*/
public static function onClose(string $client_id) {
self::log(__FUNCTION__, $client_id);
Gateway::destoryClient($client_id);
}
protected static function log(string $title, ...$data): void{
if (config('app.debug')) {
Log::info("{$title} | " . json_encode($data, 256));
}
}
}
@@ -0,0 +1,87 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Services\Auth\DepartmentService;
class Department extends BaseController {
/**
* @title 部门列表
*
* @return \Illuminate\Http\JsonResponse
*/
public function index(Request $request, DepartmentService $service){
try {
$this->data['data'] = $service->getDataList($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 添加部门
*
* @param Request $request
* @param Department $service
* @return void
*/
public function add(Request $request, DepartmentService $service){
try {
$this->data['data'] = $service->create($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 修改部门
*
* @param Request $request
* @param DepartmentService $service
* @return void
*/
public function edit(Request $request, DepartmentService $service){
try {
$this->data['data'] = $service->update($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 删除部门
*
* @param Request $request
* @param DepartmentService $service
* @return void
*/
public function delete(Request $request, DepartmentService $service){
try {
$this->data['data'] = $service->delete($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\Auth;
use App\Http\Controllers\BaseController;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Auth\Events\Login;
class Index extends BaseController {
/**
* @title 用户登录
*
* @return \Illuminate\Http\JsonResponse
*/
public function login() {
$credentials = request(['username', 'password']);
$token = auth('admin')->attempt($credentials);
if (!$token) {
$this->data['code'] = 0;
$this->data['message'] = '登录失败!';
}else{
$this->data['code'] = 1;
$this->data['message'] = '登录成功!';
$this->data['data'] = [
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth('admin')->factory()->getTTL() * 60
];
event(new Login('admin', auth('admin')->user(), false));
}
return response()->json($this->data);
}
/**
* @title 获取用户信息
*
* @return \Illuminate\Http\JsonResponse
*/
public function user() {
try {
$user = auth('admin')->userOrFail();
$user->roles = $user->roles()->get();
$this->data['data'] = $user;
} catch (\Tymon\JWTAuth\Exceptions\UserNotDefinedException $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 退出
* @desc 退出
* @method POST
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\JsonResponse
*/
public function logout() {
auth('admin')->logout();
$this->data['message'] = '退出成功';
return response()->json($this->data);
}
/**
* @title 刷新token
* @desc 刷新token
* @method POST
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\JsonResponse
*/
public function refresh() {
$this->data['data'] = auth('admin')->refresh();
return response()->json($this->data);
}
}
+103
View File
@@ -0,0 +1,103 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Services\Auth\MenuService;
class Menu extends BaseController {
/**
* @title 权限节点列表
*
* @return \Illuminate\Http\JsonResponse
*/
public function index(Request $request, MenuService $service){
try {
$this->data['data'] = $service->getDataList($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 获取我的权限节点
*
* @return \Illuminate\Http\JsonResponse
*/
public function my(Request $request, MenuService $service){
try {
$this->data['data'] = $service->getMyMenuList($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 添加权限节点
*
* @param Request $request
* @param Department $service
* @return void
*/
public function add(Request $request, MenuService $service){
try {
$this->data['data'] = $service->create($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 修改权限节点
*
* @param Request $request
* @param MenuService $service
* @return void
*/
public function edit(Request $request, MenuService $service){
try {
$this->data['data'] = $service->update($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 删除权限节点
*
* @param Request $request
* @param MenuService $service
* @return void
*/
public function delete(Request $request, MenuService $service){
try {
$this->data['data'] = $service->delete($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
}
+105
View File
@@ -0,0 +1,105 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Services\Auth\RoleService;
class Role extends BaseController {
/**
* @title 角色列表
*
* @return \Illuminate\Http\JsonResponse
*/
public function index(Request $request, RoleService $service){
try {
$this->data['data'] = $service->getDataList($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 添加角色
*
* @param Request $request
* @param Department $service
* @return void
*/
public function add(Request $request, RoleService $service){
try {
$this->data['data'] = $service->create($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 修改角色
*
* @param Request $request
* @param RoleService $service
* @return void
*/
public function edit(Request $request, RoleService $service){
try {
$this->data['data'] = $service->update($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 删除角色
*
* @param Request $request
* @param RoleService $service
* @return void
*/
public function delete(Request $request, RoleService $service){
try {
$this->data['data'] = $service->delete($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 角色权限
*
* @param Request $request
* @param RoleService $service
* @return void
*/
public function auth(Request $request, RoleService $service){
try {
$this->data['data'] = $service->auth($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
}
+123
View File
@@ -0,0 +1,123 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\Auth;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Services\Auth\UsersService;
class Users extends BaseController {
/**
* @title 用户列表
*
* @return \Illuminate\Http\JsonResponse
*/
public function index(Request $request, UsersService $service){
try {
$this->data['data'] = $service->getDataList($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 添加用户
*
* @param Request $request
* @param UsersService $service
* @return void
*/
public function add(Request $request, UsersService $service){
try {
$this->data['data'] = $service->create($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 修改用户
*
* @param Request $request
* @param UsersService $service
* @return void
*/
public function edit(Request $request, UsersService $service){
try {
$this->data['data'] = $service->update($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 删除用户
*
* @param Request $request
* @param UsersService $service
* @return void
*/
public function delete(Request $request, UsersService $service){
try {
$this->data['data'] = $service->delete($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 修改密码
*
* @param Request $request
* @param UsersService $service
* @return void
*/
public function passwd(Request $request, UsersService $service){
try {
$this->data['data'] = $service->upPassword($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 角色分配
*
* @param Request $request
* @param UsersService $service
* @return void
*/
public function uprole(Request $request, UsersService $service){
try {
$this->data['data'] = $service->uprole($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
}
@@ -0,0 +1,67 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\System;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Services\System\AreaService;
class Area extends BaseController {
/**
* @title 地区列表
*
* @param Request $request
* @param AreaService $service
* @return void
*/
public function index(Request $request, AreaService $service){
try {
$this->data['data'] = $service->getAreaList($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 添加地区
*
* @param Request $request
* @param AreaService $service
* @return void
*/
public function add(Request $request, AreaService $service){
try {
$this->data['data'] = $service->create($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 编辑地区
*
* @param Request $request
* @param AreaService $service
* @return void
*/
public function edit(Request $request, AreaService $service){
try {
$this->data['data'] = $service->update($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
}
@@ -0,0 +1,24 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\System;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Traits\AdminController;
use App\Services\System\ClientService;
class Client extends BaseController {
use AdminController;
protected $service = null;
public function __construct(ClientService $service) {
$this->service = $service;
}
}
@@ -0,0 +1,34 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\System;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Traits\AdminController;
use App\Services\System\CrontabService;
class Crontab extends BaseController {
use AdminController;
protected $service = null;
public function __construct(CrontabService $service) {
$this->service = $service;
}
public function reload(Request $request){
try {
$this->data['data'] = $this->service->reload($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return $this->data;
}
}
+175
View File
@@ -0,0 +1,175 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\System;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Services\System\DictService;
class Dict extends BaseController {
/**
* @title 字典列表
*
* @return void
*/
public function all(Request $request, DictService $service){
try {
$this->data['data'] = $service->getDictionaryAll($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return $this->data;
}
/**
* @title 字典列表
*
* @param Request $request
* @param DictService $service
* @return void
*/
public function lists(Request $request, DictService $service){
try {
$this->data['data'] = $service->getDataList($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 字段分类
*
* @param Request $request
* @param DictService $service
* @return void
*/
public function category(Request $request, DictService $service){
try {
$this->data['data'] = $service->getCategoryList($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 添加字典
*
* @param Request $request
* @param DictService $service
* @return void
*/
public function add(Request $request, DictService $service){
try {
$this->data['data'] = $service->create($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 修改字典
*
* @param Request $request
* @param DictService $service
* @return void
*/
public function edit(Request $request, DictService $service){
try {
$this->data['data'] = $service->update($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 删除字典
*
* @param Request $request
* @param DictService $service
* @return void
*/
public function delete(Request $request, DictService $service){
try {
$this->data['data'] = $service->delete($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 添加字典分类
*
* @param Request $request
* @param DictService $service
* @return void
*/
public function addcate(Request $request, DictService $service){
try {
$this->data['data'] = $service->createCategory($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 修改字典分类
*
* @param Request $request
* @param DictService $service
* @return void
*/
public function editcate(Request $request, DictService $service){
try {
$this->data['data'] = $service->updateCategory($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 删除字典分类
*
* @param Request $request
* @param DictService $service
* @return void
*/
public function deleteCate(Request $request, DictService $service){
try {
$this->data['data'] = $service->deleteCategory($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
}
+179
View File
@@ -0,0 +1,179 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\System;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use App\Http\Controllers\BaseController;
class File extends BaseController {
/**
* @title 上传文件
*
* @param Request $request
* @return void
*/
public function upload(Request $request){
$type = $request->input('type', 'images');
try {
switch ($type) {
case 'avatar':
$this->data['data'] = $this->avatar($request);
break;
case 'images':
$this->data['data'] = $this->images($request);
break;
case 'files':
$this->data['data'] = $this->files($request);
break;
default:
$this->data['data'] = $this->images($request);
break;
}
} catch (\Throwable $th) {
$this->data['code'] = 0;
$this->data['message'] = $th->getMessage();
}
return response()->json($this->data);
}
/**
* @title 文件列表
*
* @param Request $request
* @return void
*/
public function lists(Request $request){
$path = $request->input('groupId', 'images');
$files = Storage::allFiles($path);
$data = [];
foreach ($files as $key => $value) {
$data[] = [
'path' => $value,
'url' => Storage::disk()->url($path),
'name'=> explode('/', $value)[2],
];
}
$this->data['data'] = ['data' => $data];
return response()->json($this->data);
}
/**
* @title 列表菜单
*
* @return void
*/
public function menu(Request $request){
$file = [
['id'=> 'images','title'=>'图片', 'children' => []],
['id'=> 'files','title'=>'文件', 'children' => []],
['id'=> 'videos','title'=>'视频', 'children' => []],
['id'=> 'audios','title'=>'音频', 'children' => []],
];
foreach ($file as $key => $value) {
$paths = Storage::disk()->allDirectories($value['id']);
foreach ($paths as $key => $value) {
$file[$key]['children'][] = ['id'=> $value, 'title'=>explode('/', $value)[1]];
}
}
$this->data['data'] = $file;
return response()->json($this->data);
}
/**
* @title 删除文件
*
* @return void
*/
public function delete(Request $request){
return response()->json($this->data);
}
/**
* @title 上传头像
*
* @param Request $request
* @return void
*/
protected function avatar(Request $request){
$file = $request->file('file');
if ($file->isValid()) {
$ext = $file->extension();
$name = $file->hashName();
$path = $file->store('avatar/'.date('Ymd'));
$data = [
'path' => $path,
'url' => Storage::disk()->url($path),
'src' => Storage::disk()->url($path),
'name'=> $name,
'size'=> $file->getSize(),
'ext' => $ext,
];
return $data;
}else{
throw new \Exception('上传失败');
}
}
protected function images(Request $request){
$file = $request->file('file');
if ($file->isValid()) {
$ext = $file->extension();
$name = $file->hashName();
$path = $file->store('images/'.date('Ymd'));
$data = [
'path' => $path,
'url' => Storage::disk()->url($path),
'src' => Storage::disk()->url($path),
'name'=> $name,
'size'=> $file->getSize(),
'ext' => $ext,
];
return $data;
}else{
throw new \Exception('上传失败');
}
}
protected function files(Request $request){
$file = $request->file('file');
if ($file->isValid()) {
$ext = $file->extension();
$name = $file->hashName();
$path = $file->store('files/'.date('Ymd'));
$data = [
'path' => $path,
'url' => Storage::disk()->url($path),
'src' => Storage::disk()->url($path),
'name'=> $name,
'size'=> $file->getSize(),
'ext' => $ext,
];
return $data;
}else{
throw new \Exception('上传失败');
}
}
}
@@ -0,0 +1,59 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\System;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
class Index extends BaseController {
/**
* @title 系统信息
*
* @return void
*/
public function version(Request $request){
$system_info_mysql = \Illuminate\Support\Facades\DB::select("select version() as v;");
$this->data['data'] = [
['label' => '系统核心版本', 'values' => env('VERSION', '0.0.1')],
['label' => '服务器操作系统', 'values' => PHP_OS],
['label' => '运行环境', 'values' => $_SERVER['SERVER_SOFTWARE']],
['label' => 'MYSQL版本', 'values' => $system_info_mysql[0]->v],
['label' => '上传限制', 'values' => '10']
];
return response()->json($this->data);
}
/**
* @title 系统信息
*
* @return void
*/
public function info(Request $request){
$this->data['data'] = [
['label' => '系统名称', 'values' => env('APP_NAME', 'LarAdmin')],
['label' => '后端框架', 'values' => 'PHP8 + Laravel ' . \Illuminate\Foundation\Application::VERSION],
['label' => '前端框架', 'values' => 'VITE + VUE3 + Element Plus'],
['label' => '系统后台UI', 'values' => 'SentUI 1.0.0'],
['label' => 'Development', 'values' => 'molong']
];
return response()->json($this->data);
}
/**
* @title 清理缓存
*
* @return void
*/
public function clearcache(Request $request){
$this->data['data'] = cache()->flush();
return response()->json($this->data);
}
}
+68
View File
@@ -0,0 +1,68 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\System;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Services\System\LogService;
class Log extends BaseController {
/**
* @title 日志列表
* @param Request $request
* @param LogService $service
* @return \Illuminate\Http\JsonResponse
*/
public function index(Request $request, LogService $service){
try {
$this->data['data'] = $service->getDataList($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 我的日志列表
* @param Request $request
* @param LogService $service
* @return \Illuminate\Http\JsonResponse
*/
public function my(Request $request, LogService $service){
try {
$request->merge(['is_my' => 1]);
$this->data['data'] = $service->getDataList($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 删除日志
* @param Request $request
* @param LogService $service
* @return \Illuminate\Http\JsonResponse
*/
public function delete(Request $request, LogService $service){
try {
$this->data['data'] = $service->delete($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
}
@@ -0,0 +1,24 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\System;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Traits\AdminController;
use App\Services\System\ClientMenuService;
class Menu extends BaseController {
use AdminController;
protected $service = null;
public function __construct(ClientMenuService $service) {
$this->service = $service;
}
}
@@ -0,0 +1,34 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2025 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\System;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Traits\AdminController;
use App\Services\System\ModulesService;
class Modules extends BaseController {
use AdminController;
protected $service = null;
public function __construct(ModulesService $service) {
$this->service = $service;
}
public function update(Request $request){
try {
$this->data['data'] = $this->service->update($request);
} catch (\Throwable $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
}
@@ -0,0 +1,99 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\System;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Services\System\SettingService;
class Setting extends BaseController {
/**
* @title 获取配置列表
*
* @param Request $request
* @param SettingService $service
* @return void
*/
public function index(Request $request, SettingService $service){
try {
$this->data['data'] = $service->getDataList($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 创建配置
*
* @param Request $request
* @param SettingService $service
* @return void
*/
public function add(Request $request, SettingService $service){
try {
$this->data['data'] = $service->create($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
public function edit(Request $request, SettingService $service){
try {
$this->data['data'] = $service->update($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 更新配置
*
* @param Request $request
* @param SettingService $service
* @return void
*/
public function save(Request $request, SettingService $service){
try {
$this->data['data'] = $service->saveSetting($request);
$this->data['message'] = "保存成功!";
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 获取配置字段
*
* @param Request $request
* @param SettingService $service
* @return void
*/
public function fields(Request $request, SettingService $service){
try {
$this->data['data'] = $service->getFields($request);
} catch (\Throwable $th) {
$this->data['code'] = 0;
$this->data['message'] = $th->getMessage();
}
return response()->json($this->data);
}
}
@@ -0,0 +1,88 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Admin\System;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Services\System\TasksService;
class Tasks extends BaseController {
/**
* @title 任务列表
*
* @param Request $request
* @param TasksService $service
* @return void
*/
public function index(Request $request, TasksService $service){
try {
$this->data['data'] = $service->getDataList($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 添加任务
*
* @param Request $request
* @param TasksService $service
* @return void
*/
public function add(Request $request, TasksService $service){
try {
$this->data['data'] = $service->create($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 编辑任务
*
* @param Request $request
* @param TasksService $service
* @return void
*/
public function edit(Request $request, TasksService $service){
try {
$this->data['data'] = $service->update($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
/**
* @title 删除任务
*
* @param Request $request
* @param TasksService $service
* @return void
*/
public function delete(Request $request, TasksService $service){
try {
$this->data['data'] = $service->delete($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Api\System;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Services\System\AreaService;
class Area extends BaseController {
/**
* @title 获取地区数据
*
* @param Request $request
* @return void
*/
public function index(Request $request, AreaService $service){
try {
$this->data['data'] = $service->getDataList($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers\Api\System;
use Illuminate\Http\Request;
use App\Http\Controllers\BaseController;
use App\Services\System\QrcodeService;
class Index extends BaseController {
/**
* @title 生成二维码
*
* @param Request $request
* @return void
*/
public function qrcode(Request $request, QrcodeService $service){
try {
$this->data['data'] = $service->getQrcode($request);
} catch (\Exception $e) {
$this->data['code'] = 0;
$this->data['message'] = $e->getMessage();
}
return response()->json($this->data);
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Process;
class BaseController {
protected $data = ['code' => 1, 'message' => 'success', 'data' => []];
public function __construct(Request $request){
if ($request->filled('page')) {
$offset = ($request->input('page', 1) - 1) * $request->input('limit', 30);
$request->mergeIfMissing(['offset' => $offset]);
}
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace App\Http\Controllers;
abstract class Controller
{
//
}
@@ -0,0 +1,30 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use App\Services\System\LogService;
use Illuminate\Support\Facades\Log;
class AuthCheckMiddleware {
public function handle(Request $request, Closure $next, string $guard = 'admin'): Response {
$response = $next($request);
if (auth($guard)->check()) {
if($guard == 'admin'){
app(LogService::class)->createLog($request);
}
return $response;
}else{
return response()->json(['code' => 2000, 'message' => '未登录,请重新登录!']);
}
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Listeners;
use Illuminate\Auth\Events\Login;
use App\Models\Auth\Admin;
class AdminLogin {
/**
* Create the event listener.
*/
public function __construct() {
//
}
/**
* Handle the event.
*/
public function handle(Login $event): void {
$admin = $event->user;
$admin->last_login_ip = request()->ip();
$admin->last_login_at = date('Y-m-d H:i:s');
$admin->save();
}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models\Auth;
use Tymon\JWTAuth\Contracts\JWTSubject;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Facades\Hash;
use App\Traits\ModelTrait;
class Admin extends Authenticatable implements JWTSubject {
use Notifiable, ModelTrait;
protected $table = 'auth_admins';
protected $primaryKey = 'uid';
protected $fillable = ['username', 'nickname', 'email', 'mobile', 'password', 'department_id', 'status', 'last_login_at', 'last_login_ip'];
protected $hidden = ['password', 'deleted_at'];
protected $with = ['department', 'roles'];
// Rest omitted for brevity
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* @return mixed
*/
public function getJWTIdentifier() {
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* @return array
*/
public function getJWTCustomClaims() {
return [];
}
public function password() : Attribute {
return new Attribute(
set: fn ($value) => Hash::make($value),
);
}
public function department(){
return $this->belongsTo(Department::class, 'department_id', 'id');
}
public function roles(){
return $this->belongsToMany(Role::class, 'auth_admins_roles', 'uid', 'role_id');
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models\Auth;
class Department extends \App\Models\BaseModel {
protected $table = 'auth_departments';
protected $fillable = ['title', 'name', 'parent_id', 'status', 'sort'];
protected $hidden = ['deleted_at'];
protected function casts(): array {
return [
'parent_id' => 'integer',
'sort' => 'integer',
];
}
public function members(){
return $this->hasMany(Admin::class, 'department_id', 'uid');
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models\Auth;
use Illuminate\Database\Eloquent\Casts\Attribute;
class Permission extends \App\Models\BaseModel {
protected $table = 'auth_permissions';
protected $fillable = ['title', 'name', 'parent_id', 'hiddenBreadcrumb', 'hidden', 'affix', 'color', 'fullpage', 'icon', 'status', 'sort'];
protected $hidden = ['deleted_at'];
public function meta() : Attribute {
return Attribute::make(
get: fn (mixed $value, array $data) => [
'id' => $data['id'],
'parent_id' => $data['parent_id'],
'title' => $data['title'],
'hiddenBreadcrumb' => $data['hiddenBreadcrumb'],
'hidden' => $data['hidden'],
'affix' => $data['affix'],
'color' => $data['color'],
'fullpage' => $data['fullpage'],
'icon' => isset($data['icon']) && $data['icon'] ? $data['icon'] : 'el-icon-document',
// 'icon' => isset($data['icon']) && $data['icon'] ? $data['icon'] : 'AIconFileTextOutlined',
],
);
}
public function roles(){
return $this->belongsToMany(Role::class, 'auth_roles_permissions', 'permission_id', 'role_id');
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models\Auth;
class Role extends \App\Models\BaseModel {
protected $table = 'auth_roles';
protected $fillable = ['title', 'name', 'data_range', 'sort', 'status', 'description'];
protected $hidden = ['deleted_at'];
public function admins(){
return $this->belongsToMany(Admin::class, 'auth_admins_roles', 'role_id', 'uid');
}
public function permissions(){
return $this->belongsToMany(Permission::class, 'auth_roles_permissions', 'role_id', 'permission_id');
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Schema;
class BaseModel extends Model {
protected $dateFormat = 'Y-m-d H:i:s';
protected function casts(): array {
return [
'created_at' => 'datetime:Y-m-d H:i:s',
'updated_at' => 'datetime:Y-m-d H:i:s',
'deleted_at' => 'datetime:Y-m-d H:i:s',
];
}
protected function serializeDate($data){
return $data->timezone('Asia/Shanghai')->format('Y-m-d H:i:s');
}
/**
* 过滤移除非当前表的字段参数
*
* @param array $params
*
* @return array
*/
public function setFilterFields(array $params) : array {
$fields = Schema::getColumnListing($this->getTable());
foreach ($params as $key => $param) {
if ( !in_array($key, $fields) ) unset($params[$key]);
}
// 同时过滤时间戳字段【时间戳只允许自动更改,不允许手动设置】
if ( $this->timestamps === true && isset($params[self::CREATED_AT]) ) unset($params[self::CREATED_AT]);
if ( $this->timestamps === true && isset($params[self::UPDATED_AT]) ) unset($params[self::UPDATED_AT]);
return $params;
}
}
+16
View File
@@ -0,0 +1,16 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models\System;
class Area extends \App\Models\BaseModel {
protected $table = 'system_areas';
protected $fillable = ['title', 'parent_id', 'status'];
protected $hidden = ['deleted_at'];
}
+39
View File
@@ -0,0 +1,39 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models\System;
use Illuminate\Database\Eloquent\Casts\Attribute;
class Client extends \App\Models\BaseModel {
protected $table = 'system_client';
protected $fillable = ['title', 'app_id', 'secret', 'redirect', 'status'];
protected function casts(): array {
return [
'status' => 'integer',
];
}
public function appId() : Attribute {
return Attribute::make(
set: function (mixed $value, array $data){
return uniqid();
}
);
}
public function secret() : Attribute {
return Attribute::make(
set: function (mixed $value, array $data){
return md5(uniqid());
}
);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models\System;
use Illuminate\Database\Eloquent\Casts\Attribute;
class ClientMenu extends \App\Models\BaseModel {
protected $table = 'system_client_menu';
protected $with = ['client'];
protected function casts(): array {
return [
'status' => 'integer',
'sort' => 'integer',
];
}
public function client(){
return $this->belongsTo(Client::class, 'client_id', 'id');
}
}
+45
View File
@@ -0,0 +1,45 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models\System;
use Illuminate\Database\Eloquent\Casts\Attribute;
class Config extends \App\Models\BaseModel {
protected $table = 'system_configs';
protected $fillable = ['values', 'name', 'title', 'group', 'remark', 'type', 'status', 'sort'];
protected $hidden = ['deleted_at'];
protected function casts(): array {
return [
'option' => 'json',
];
}
public function options(): Attribute {
return Attribute::make(
get: fn (mixed $value, array $data) => [
'placeholder' => $data['title'],
'options' => $data['option'] ? json_decode($data['option'], true) : [],
],
);
}
public function label() : Attribute {
return Attribute::make(
get: fn (mixed $value, array $data) => $data['title'],
);
}
public function prop() : Attribute {
return Attribute::make(
get: fn (mixed $value, array $data) => $data['title'],
);
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models\System;
use Illuminate\Database\Eloquent\Casts\Attribute;
class Crontab extends \App\Models\BaseModel {
protected $table = 'system_crontabs';
protected $fillable = [];
protected $hidden = ['deleted_at'];
protected function casts(): array {
return [
'status' => 'integer',
];
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models\System;
use Illuminate\Database\Eloquent\Casts\Attribute;
class Dict extends \App\Models\BaseModel {
protected $table = 'system_dicts';
protected $fillable = ['title', 'values', 'group_id', 'group_name', 'sort', 'status'];
protected $hidden = ['deleted_at'];
public function group(){
return $this->belongsTo(DictGroup::class, 'group_id', 'id');
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models\System;
class DictGroup extends \App\Models\BaseModel {
protected $table = 'system_dict_groups';
protected $fillable = ['title', 'name', 'parent_id', 'sort', 'status'];
protected $hidden = ['deleted_at'];
protected function casts(): array {
return [
'parent_id' => 'integer',
'status' => 'integer',
'sort' => 'integer',
];
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models\System;
use App\Models\Auth\Admin;
class Log extends \App\Models\BaseModel {
protected $table = 'system_logs';
protected $fillable = ['title', 'name', 'url', 'method', 'client_ip', 'browser', 'user_id', 'data', 'remark', 'status'];
protected $hidden = ['deleted_at'];
public function user() {
return $this->belongsTo(Admin::class, 'user_id', 'uid');
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models\System;
class Modules extends \App\Models\BaseModel {
//
}
+31
View File
@@ -0,0 +1,31 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Models\System;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Facades\Storage;
use App\Models\Auth\Admin;
class Tasks extends \App\Models\BaseModel {
protected $table = 'system_tasks';
protected $fillable = [];
protected $hidden = ['deleted_at'];
protected $append = ['url'];
public function url(): Attribute{
return Attribute::make(
get: fn (mixed $value, array $data) => Storage::disk('public')->url($data['file']),
);
}
public function user() {
return $this->belongsTo(Admin::class, 'user_id', 'uid');
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Event;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
$loader = \Illuminate\Foundation\AliasLoader::getInstance();
$loader->alias('Debugbar', \Barryvdh\Debugbar\Facades\Debugbar::class);
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
//
}
}
+127
View File
@@ -0,0 +1,127 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Services\Auth;
use App\Models\Auth\Department;
use App\Support\Tree;
class DepartmentService {
/**
* @title 获取部门列表
*
* @param [type] $request
* @return void
*/
public function getDataList($request) {
$map = [];
if ($request->filled('title')) {
$map[] = ['title', 'like', '%' . $request->input('title') . '%'];
}
if ($request->filled('name')) {
$map[] = ['name', 'like', '%' . $request->input('name') . '%'];
}
if ($request->filled('pid')) {
$map[] = ['parent_id', '=', $request->input('pid')];
}
$query = Department::where($map)->orderBy('id', 'desc');
if($request->filled('is_tree') && $request->filled('is_tree') == 1){
$query = $query->get()->toArray();
$tree = new Tree();
$data = $tree->list_to_tree($query, 0, 'id', 'parent_id');
}else{
$data = [
'total' => $query->count(),
'page' => $request->input('page', 1),
'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(),
];
}
return $data;
}
/**
* @title 添加部门
*
* @param [type] $request
* @return void
*/
public function create($request) {
$request->validate([
'title' => 'required|unique:auth_departments',
'name' => 'required|unique:auth_departments',
]);
$data = $request->all();
$data['status'] = 1;
return Department::create($data);
}
/**
* @title 修改部门
*
* @param [type] $request
* @return void
*/
public function update($request) {
try {
$department = Department::findOrFail($request->input('id'));
} catch (\Throwable $th) {
throw new \Exception("部门不存在!", 1);
}
if ($request->filled('title')) {
$department->title = $request->input('title');
}
if ($request->filled('name')) {
$department->name = $request->input('name');
}
if ($request->filled('parent_id')) {
$department->parent_id = $request->input('parent_id');
}
if ($request->filled('status')) {
$department->status = $request->input('status');
}
if ($request->filled('sort')) {
$department->sort = $request->input('sort');
}
$department->save();
return $department;
}
/**
* @title 删除部门
*
* @param [type] $request
* @return void
*/
public function delete($request) {
if($request->filled('id')){
try {
$department = Department::findOrFail($request->input('id'));
} catch (\Throwable $th) {
throw new \Exception("角色不存在!", 1);
}
$department->delete();
}
if($request->filled('ids')){
try {
$ids = $request->input('ids');
$department = Department::whereIn('id', $ids)->delete();
} catch (\Throwable $th) {
throw new \Exception($th->getMessage(), 1);
}
}
return $department;
}
}
+193
View File
@@ -0,0 +1,193 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Services\Auth;
use App\Models\Auth\Permission;
use App\Models\Auth\Admin;
use App\Support\Tree;
class MenuService {
/**
* @title 获取权限节点列表
*
* @param [type] $request
* @return void
*/
public function getDataList($request) {
$map = [];
if ($request->filled('title')) {
$map[] = ['title', 'like', '%' . $request->input('title') . '%'];
}
if ($request->filled('name')) {
$map[] = ['name', 'like', '%' . $request->input('name') . '%'];
}
if ($request->filled('pid')) {
$map[] = ['parent_id', '=', $request->input('pid')];
}
$query = Permission::where($map)->orderBy('sort', 'asc')->orderBy('id', 'desc');
if($request->filled('is_tree') && $request->filled('is_tree') == 1){
$query = $query->get()->toArray();
$tree = new Tree();
$data = $tree->list_to_tree($query, 0, 'id', 'parent_id');
}else{
$data = [
'total' => $query->count(),
'page' => $request->input('page', 1),
'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(),
];
}
return $data;
}
/**
* @title 获取我的权限节点
*
* @param [type] $request
* @return void
*/
public function getMyMenuList($request){
$map = [];
$data = [];
$uid = auth('admin')->user()['uid'];
$tree = new Tree();
if($uid != env('ADMIN_ID', 1)){
$map[] = ['uid', '=', $uid];
$admin = Admin::with(['roles'])->where($map)->first();
$permission = [];
$menu = [];
foreach ($admin->roles as $key => $role) {
$menu = array_merge($menu, $role->permissions->append(['meta'])->toArray());
$permission = array_merge($permission, $role->permissions->pluck('name')->toArray());
}
$data = [
'menu' => $tree->list_to_tree($menu, 0, 'id', 'parent_id'),
'permissions' => $permission
];
}else{
$query = Permission::where($map)->orderBy('sort', 'asc');
$data = [
'menu' => $tree->list_to_tree($query->where('type', '=', 'menu')->get()->append(['meta'])->toArray(), 0, 'id', 'parent_id'),
'permissions' => $query->pluck('name')
];
}
return $data;
}
/**
* @title 添加权限节点
*
* @param [type] $request
* @return void
*/
public function create($request) {
$request->validate([
'title' => 'required|unique:auth_permissions',
'name' => 'required|unique:auth_permissions',
]);
$data = $request->all();
$data['status'] = 1;
return Permission::create($data);
}
/**
* @title 修改权限节点
*
* @param [type] $request
* @return void
*/
public function update($request) {
try {
$permission = Permission::findOrFail($request->input('id'));
} catch (\Throwable $th) {
throw new \Exception("权限节点不存在!", 1);
}
$data = $request->all();
foreach ($data as $key => $value) {
if (in_array($key, ['affix', 'color', 'component', 'fullpage', 'hidden', 'hiddenBreadcrumb', 'icon', 'name', 'parent_id', 'redirect', 'sort', 'title', 'type', 'path', 'status'])) {
$permission->$key = $value;
}
}
$permission->save();
return $permission;
}
/**
* @title 删除权限节点
*
* @param [type] $request
* @return void
*/
public function delete($request) {
if($request->filled('id')){
try {
$permission = Permission::findOrFail($request->input('id'));
} catch (\Throwable $th) {
throw new \Exception("权限节点不存在!", 1);
}
$permission->roles()->detach();
$permission->delete();
}
if($request->filled('ids')){
try {
$permission = Permission::whereIn('id', $request->input('ids'));
foreach ($permission->get() as $item) {
$item->roles()->detach();
}
$permission->delete();
} catch (\Throwable $th) {
throw new \Exception($th->getMessage(), 1);
}
}
return $permission;
}
/**
* @title 批量导入
*
* @param [array] $data
* @return void
*/
public function importMenu($data, $parent_id = 0){
foreach ($data as $key => $value) {
$save = [
'title' => $value['title'],
'name' => $value['name'],
'path' => $value['path'],
'component' => $value['component'],
'type' => $value['type'],
'affix' => isset($value['affix']) ? $value['affix'] : 0,
'parent_id' => $parent_id,
'status' => 1,
'sort' => isset($value['sort']) ? $value['sort'] : $key,
'icon' => isset($value['icon']) ? $value['icon'] : '',
'hidden' => isset($value['hidden']) ? $value['hidden'] : 0,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
$res = Permission::insertGetId($save);
if (isset($value['children']) && !empty($value['children'])) {
$this->importMenu($value['children'], $res);
}
}
}
}
+159
View File
@@ -0,0 +1,159 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Services\Auth;
use App\Models\Auth\Role;
use App\Support\Tree;
class RoleService {
/**
* @title 获取角色列表
*
* @param [type] $request
* @return void
*/
public function getDataList($request) {
$map = [];
if ($request->filled('title')) {
$map[] = ['title', 'like', '%' . $request->input('title') . '%'];
}
if ($request->filled('name')) {
$map[] = ['name', 'like', '%' . $request->input('name') . '%'];
}
$query = Role::with(['permissions'])->where($map)->orderBy('id', 'desc');
if($request->filled('is_tree') && $request->filled('is_tree') == 1){
$query = $query->get()->toArray();
$tree = new Tree();
$data = $tree->list_to_tree($query, 0, 'id', 'parent_id');
}else{
if ($request->filled('page')){
$data = [
'total' => $query->count(),
'page' => $request->input('page', 1),
'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(),
];
}else{
$data = $query->get();
}
}
return $data;
}
/**
* @title 添加角色
*
* @param [type] $request
* @return void
*/
public function create($request) {
$request->validate([
'title' => 'required|unique:auth_roles',
'name' => 'required|alpha_dash:ascii|unique:auth_roles',
]);
$data = $request->all();
$data['status'] = 1;
return Role::create($data);
}
/**
* @title 修改角色
*
* @param [type] $request
* @return void
*/
public function update($request) {
$request->validate([
'title' => 'required|unique:auth_roles,title,' . $request->input('id'),
'name' => 'required|alpha_dash:ascii|unique:auth_roles,name,' . $request->input('id'),
]);
try {
$role = Role::findOrFail($request->input('id'));
} catch (\Throwable $th) {
throw new \Exception("角色不存在!", 1);
}
if ($request->filled('title')) {
$role->title = $request->input('title');
}
if ($request->filled('name')) {
$role->name = $request->input('name');
}
if ($request->filled('status')) {
$role->status = $request->input('status');
}
if ($request->filled('sort')) {
$role->sort = $request->input('sort');
}
$role->save();
return $role;
}
/**
* @title 删除角色
*
* @param [type] $request
* @return void
*/
public function delete($request) {
if($request->filled('id')){
try {
$role = Role::findOrFail($request->input('id'));
} catch (\Throwable $th) {
throw new \Exception("角色不存在!", 1);
}
$role->permissions()->detach();
$role->admins()->detach();
$role->delete();
}
if($request->filled('ids')){
try {
$role = Role::whereIn('id', $request->input('ids'));
foreach ($role->get() as $item) {
$item->permissions()->detach(); //删除关联
$item->admins()->detach(); //删除关联
}
$role->delete();
} catch (\Throwable $th) {
throw new \Exception($th->getMessage(), 1);
}
}
return $role;
}
/**
* @title 角色授权
*
* @param [type] $request
* @return void
*/
public function auth($request){
try {
$role = Role::with(['permissions'])->findOrFail($request->input('id'));
} catch (\Throwable $th) {
throw new \Exception("角色不存在!", 1);
}
if($request->filled('data_range')){
$role->data_range = $request->input('data_range');
}
$role->permissions()->sync($request->input('permissions'));
$role->save();
return $role;
}
}
+194
View File
@@ -0,0 +1,194 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Services\Auth;
use App\Models\Auth\Admin;
class UsersService {
/**
* @title 获取用户列表
*
* @param [type] $request
* @return void
*/
public function getDataList($request) {
$map = [];
if ($request->filled('username')) {
$map[] = ['username', 'like', '%' . $request->input('username') . '%'];
}
if ($request->filled('mobile')) {
$map[] = ['mobile', 'like', '%' . $request->input('mobile') . '%'];
}
if ($request->filled('email')) {
$map[] = ['email', 'like', '%' . $request->input('email') . '%'];
}
if ($request->filled('department_id')) {
$map[] = ['department_id', '=', $request->input('department_id')];
}
$query = Admin::where($map)->orderBy('uid', 'desc');
$data = [
'total' => $query->count(),
'page' => $request->input('page', 1),
'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(),
];
return $data;
}
/**
* @title 添加用户
*
* @param [type] $request
* @return void
*/
public function create($request) {
$request->validate([
'username' => 'required|unique:auth_admins,username',
// 'email' => 'required|unique:auth_admins',
// 'mobile' => 'required|unique:auth_admins',
'password' => 'required|min:8',
]);
$data = $request->all();
$data['status'] = 1;
$data['last_login_at'] = date('Y-m-d H:i:s');
$data['last_login_ip'] = $request->ip();
$admins = Admin::create($data);
$request->whenFilled('roles_id', function ($value) use ($admins) {
$admins->roles()->sync($value);
});
return $admins;
}
/**
* @title 修改用户
*
* @param [type] $request
* @return void
*/
public function update($request) {
$request->validate([
'username' => 'required|unique:auth_admins,username,' . $request->input('uid').',uid',
]);
try {
$admins = Admin::findOrFail($request->input('uid'));
} catch (\Throwable $th) {
throw new \Exception("用户不存在!", 1);
}
if ($request->filled('username')) {
$admins->username = $request->input('username');
}
if ($request->filled('email')) {
$admins->email = $request->input('email');
}
if ($request->filled('mobile')) {
$admins->mobile = $request->input('mobile');
}
if ($request->filled('nickname')) {
$admins->nickname = $request->input('nickname');
}
if ($request->filled('department_id')) {
$admins->department_id = $request->input('department_id');
}
if ($request->filled('avatar')) {
$admins->avatar = $request->input('avatar');
}
if ($request->filled('remark')) {
$admins->remark = $request->input('remark');
}
if ($request->filled('status')) {
$admins->status = $request->input('status');
}
if ($request->filled('password')) {
$admins->password = $request->input('password');
}
$request->whenFilled('roles_id', function ($value) use ($admins) {
$admins->roles()->sync($value);
});
$admins->save();
return $admins;
}
/**
* @title 删除用户
*
* @param [type] $request
* @return void
*/
public function delete($request) {
if($request->filled('id')){
if($request->input('id') == env('ADMIN_ID', 1)){
throw new \Exception("超级管理员不能删除!", 1);
}
try {
$admins = Admin::findOrFail($request->input('id'));
} catch (\Throwable $th) {
throw new \Exception("角色不存在!", 1);
}
$admins->roles()->detach();
$admins->delete();
}
if($request->filled('ids')){
if(in_array(env('ADMIN_ID', 1), $request->input('ids'))){
throw new \Exception("超级管理员不能删除!", 1);
}
try {
$admins = Admin::whereIn('uid', $request->input('ids'));
foreach ($admins->get() as $item) {
$item->roles()->detach(); //删除关联
}
$admins = $admins->delete();
} catch (\Throwable $th) {
throw new \Exception($th->getMessage(), 1);
}
}
return $admins;
}
/**
* @title 修改密码
*
* @param [type] $request
* @return void
*/
public function upPassword($request) {
try {
$admins = Admin::findOrFail($request->input('uid'));
} catch (\Throwable $th) {
throw new \Exception("用户不存在!", 1);
}
$admins->password = $request->input('password');
$admins->save();
return $admins;
}
/**
* @title 设置角色
*
* @param [type] $request
* @return void
*/
public function uprole($request){
try {
$admins = Admin::with(['roles'])->findOrFail($request->input('uid'));
} catch (\Illuminate\Database\Eloquent\ModelNotFoundException $th) {
throw new \Exception($th->getMessage(), 1);
}
$admins->roles()->sync($request->input('roles'));
return $admins;
}
}
+118
View File
@@ -0,0 +1,118 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Services\System;
use App\Models\System\Area;
use App\Support\Tree;
class AreaService {
/**
* @title 区域列表
*
* @param [type] $request
* @return void
*/
public function getAreaList($request){
$map = [];
if($request->filled('title')){
$map[] = ['title', 'like', '%' . $request->input('title') . '%'];
}
if($request->filled('id')){
$map[] = ['id', '=', $request->input('id')];
}
if($request->filled('parent_code')){
$map[] = ['parent_code', '=', $request->input('parent_code')];
}
$query = Area::where($map)->orderBy('id', 'asc');
if($request->filled('page')){
$data = [
'total' => $query->count(),
'page' => $request->input('page', 1),
'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(),
];
}else{
if($request->filled('is_tree') && $request->filled('is_tree') == 1){
$query = $query->get()->toArray();
$tree = new Tree();
$data = $tree->list_to_tree($query, 0, 'code', 'parent_code');
}else{
$data = $query->get();
}
}
return $data;
}
/**
* @title 添加区域
*
* @param [type] $request
* @return void
*/
public function create($request){
$request->validate([
'title' => 'required',
'code' => 'required|unique:system_areas',
], [
'code.unique' => '编码已存在',
'title.required' => '请输入标题',
'code.required' => '请输入编码',
]);
$area = new Area;
foreach ($area->setFilterFields($request->all()) as $key => $value) {
$area->$key = $value;
}
$area->save();
return $area;
}
/**
* @title 编辑区域
*
* @param [type] $request
* @return void
*/
public function update($request){
$request->validate([
'id' => 'required',
'title' => 'required',
'code' => 'required',
], [
'id.required' => '参数错误',
'title.required' => '请输入标题',
'code.required' => '请输入编码',
]);
$area = Area::where('id', '=', $request->input('id'))->first();
foreach ($area->setFilterFields($request->all()) as $key => $value) {
$area->$key = $value;
}
$area->save();
return $area;
}
public function delete($request){
$request->validate([
'id' => 'required',
], [
'id.required' => '参数错误',
]);
$area = Area::where('id', '=', $request->input('id'))->first();
$area->delete();
return $area;
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Services\System;
use App\Models\System\ClientMenu;
use App\Support\Tree;
class ClientMenuService {
public function getDataList($request){
$map = [];
if($request->filled('title')){
$map[] = ['title', 'like', '%' . $request->input('title') . '%'];
}
$query = ClientMenu::where($map)->orderBy('id', 'asc');
if($request->filled('page')){
$data = [
'total' => $query->count(),
'page' => $request->input('page', 1),
'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(),
];
}else{
if($request->filled('is_tree') && $request->filled('is_tree') == 1){
$query = $query->get()->toArray();
$tree = new Tree();
$data = $tree->list_to_tree($query, 0, 'id', 'parent_id');
}else{
$data = $query->get();
}
}
return $data;
}
public function create($request){
$request->validate([
'title' => 'required',
]);
$menu = new ClientMenu();
foreach ($menu->setFilterFields($request->all()) as $key => $value) {
$menu->$key = $value;
}
$menu->save();
return $menu;
}
public function update($request){
$request->validate([
'id' => 'required',
'title' => 'required',
], [
'id.required' => '参数错误',
'title.required' => '请输入标题',
]);
$menu = ClientMenu::where('id', $request->input('id'))->first();
foreach ($menu->setFilterFields($request->all()) as $key => $value) {
$menu->$key = $value;
}
$menu->save();
return $menu;
}
public function delete($request){
$request->validate([
'id' => 'required',
], [
'id.required' => '参数错误',
]);
$menu = ClientMenu::where('id', $request->input('id'))->first();
$menu->delete();
return $menu;
}
}
+84
View File
@@ -0,0 +1,84 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Services\System;
use App\Models\System\Client;
class ClientService {
public function getDataList($request){
$map = [];
if($request->filled('title')){
$map[] = ['title', 'like', '%' . $request->input('title') . '%'];
}
$query = Client::where($map)->orderBy('id', 'asc');
if($request->filled('page')){
$data = [
'total' => $query->count(),
'page' => $request->input('page', 1),
'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(),
];
}else{
$data = $query->get();
}
return $data;
}
public function create($request){
$request->validate([
'title' => 'required',
]);
$client = new Client();
foreach ($client->setFilterFields($request->all()) as $key => $value) {
$client->$key = $value;
}
$client->app_id = '';
$client->secret = '';
$client->redirect = '';
$client->save();
return $client;
}
public function update($request){
$request->validate([
'id' => 'required',
'title' => 'required',
], [
'id.required' => '参数错误',
'title.required' => '请输入标题',
]);
$client = Client::where('id', $request->input('id'))->first();
$client->title = $request->input('title');
$client->status = $request->input('status', 0);
$client->save();
return $client;
}
public function delete($request){
$request->validate([
'id' => 'required',
], [
'id.required' => '参数错误',
]);
$client = Client::where('id', $request->input('id'))->first();
$client->delete();
return $client;
}
}
+119
View File
@@ -0,0 +1,119 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Services\System;
use App\Models\System\Crontab;
class CrontabService {
public function getDataList($request){
$map = [];
if($request->filled('title')){
$map[] = ['title', 'like', '%' . $request->input('title') . '%'];
}
if($request->filled('type')){
$map[] = ['type', '=', $request->input('type')];
}
$query = Crontab::where($map)->orderBy('id', 'asc');
if($request->filled('page')){
$data = [
'total' => $query->count(),
'page' => $request->input('page', 1),
'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(),
];
}else{
$data = $query->get();
}
return $data;
}
public function create($request){
$request->validate([
'title' => 'required',
'type' => 'required',
'expression' => 'required',
'command' => 'required',
], [
'title.required' => '请输入标题',
'type.required' => '请选择类型',
'expression.required' => '请输入表达式',
'command.required' => '请输入命令',
]);
$crontab = new Crontab;
foreach ($crontab->setFilterFields($request->all()) as $key => $value) {
$crontab->$key = $value;
}
$crontab->save();
return $crontab;
}
public function update($request){
$request->validate([
'id' => 'required',
'title' => 'required',
'type' => 'required',
'expression' => 'required',
'command' => 'required',
], [
'id.required' => '参数错误',
'title.required' => '请输入标题',
'type.required' => '请选择类型',
'expression.required' => '请输入表达式',
'command.required' => '请输入命令',
]);
$crontab = Crontab::where('id', $request->input('id'))->first();
foreach ($crontab->setFilterFields($request->all()) as $key => $value) {
$crontab->$key = $value;
}
$crontab->save();
return $crontab;
}
public function delete($request){
$request->validate([
'id' => 'required',
], [
'id.required' => '参数错误',
]);
$crontab = Crontab::where('id', $request->input('id'))->first();
$crontab->delete();
return $crontab;
}
public function reload($request){
$request->validate([
'id' => 'required',
'status' => 'required',
], [
'id.required' => '参数错误',
'status.required' => '参数错误',
]);
$crontab = Crontab::where('id', $request->input('id'))->first();
$crontab->status = $request->input('status');
$crontab->save();
return $crontab;
}
}
+254
View File
@@ -0,0 +1,254 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Services\System;
use App\Models\System\Dict;
use App\Models\System\DictGroup;
use App\Support\Tree;
class DictService {
/**
* @title 获取列表数据
*
* @param [type] $request
* @return void
*/
public function getDataList($request) {
$map = [];
if ($request->filled('title')) {
$map[] = ['title', 'like', '%' . $request->input('title') . '%'];
}
if ($request->filled('name')) {
$map[] = ['group_name', '=', $request->input('name')];
}
if ($request->filled('group_id')) {
$map[] = ['group_id', '=', $request->input('group_id')];
}
$query = Dict::where($map)->orderBy('sort', 'asc')->orderBy('id', 'desc');
if($request->filled('page')){
$data = [
'total' => $query->count(),
'page' => $request->input('page', 1),
'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(),
];
}else{
$data = $query->get();
}
return $data;
}
/**
* @title 字段分类数据
*
* @param [type] $request
* @return void
*/
public function getCategoryList($request){
$map = [];
if ($request->filled('title')) {
$map[] = ['title', 'like', '%' . $request->input('title') . '%'];
}
if ($request->filled('name')) {
$map[] = ['name', 'like', '%' . $request->input('name') . '%'];
}
$query = DictGroup::where($map)->orderBy('id', 'desc');
if($request->filled('is_tree') && $request->filled('is_tree') == 1){
$query = $query->get()->toArray();
$tree = new Tree();
$data = $tree->list_to_tree($query, 0, 'id', 'parent_id');
}else{
$data = [
'total' => $query->count(),
'page' => $request->input('page', 1),
'data' => $query->limit($request->input('limit', 10))->offset($request->input('page', 1) - 1)->get(),
];
}
return $data;
}
/**
* @title 创建字典
*
* @param [type] $request
* @return void
*/
public function create($request){
$request->validate([
'title' => 'required',
'values' => 'required',
]);
$data = $request->all();
$data['group_name'] = DictGroup::where('id', '=', $data['group_id'])->value('name');
$dict = Dict::create($data);
if($dict->values == ''){
$dict->values = $dict->id;
$dict->save();
}
$this->setDictCache(true);
return $dict;
}
/**
* @title 更新字典
*
* @param [type] $request
* @return void
*/
public function update($request){
try {
$dict = Dict::findOrFail($request->input('id'));
} catch (\Throwable $th) {
throw new \Exception("改字典不存在", 1);
}
if ($request->filled('title')) {
$dict->title = $request->input('title');
}
if ($request->filled('values')) {
$dict->values = $request->input('values');
}
if ($request->filled('sort')) {
$dict->sort = $request->input('sort');
}
if ($request->filled('status')) {
$dict->status = $request->input('status');
}
if ($request->filled('group_id')) {
$dict->group_id = $request->input('group_id');
$dict->group_name = DictGroup::where('id', '=', $request->input('group_id'))->value('name');
}
$dict->save();
$this->setDictCache(true);
return $dict;
}
/**
* @title 删除字典
*
* @param [type] $request
* @return void
*/
public function delete($request){
try {
$dict = Dict::findOrFail($request->input('id'));
} catch (\Throwable $th) {
throw new \Exception("改字典不存在", 1);
}
$dict->delete();
$this->setDictCache(true);
return $dict;
}
/**
* @title 创建字典分类
*
* @param [type] $request
* @return void
*/
public function createCategory($request){
$request->validate([
'title' => 'required|unique:system_dict_groups',
'name' => 'required|unique:system_dict_groups',
]);
$data = $request->all();
$data['status'] = 1;
$group = DictGroup::create($data);
$this->setDictCache(true);
return $group;
}
/**
* @title 更新字典分类
*
* @param [type] $request
* @return void
*/
public function updateCategory($request){
try {
$dict = DictGroup::findOrFail($request->input('id'));
} catch (\Throwable $th) {
throw new \Exception("改字典不存在", 1);
}
if ($request->filled('parent_id')) {
$dict->parent_id = $request->input('parent_id');
}
if ($request->filled('title')) {
$dict->title = $request->input('title');
}
if ($request->filled('name')) {
$dict->name = $request->input('name');
}
if ($request->filled('sort')) {
$dict->sort = $request->input('sort');
}
if ($request->filled('status')) {
$dict->status = $request->input('status');
}
$dict->save();
$this->setDictCache(true);
return $dict;
}
/**
* @title 删除字典
*
* @param [type] $request
* @return void
*/
public function deleteCategory($request){
try {
$dict = DictGroup::findOrFail($request->input('id'));
} catch (\Throwable $th) {
throw new \Exception("改字典不存在", 1);
}
$dict->delete();
$this->setDictCache(true);
return $dict;
}
public function getDictionaryAll(){
$map = [];
$map[] = ['status', '=', 1];
$list = Dict::where($map)->get();
$data = [];
foreach ($list as $key => $value) {
$data[$value['group_name']][] = $value;
}
return $data;
}
public function setDictCache($refresh = false){
if (!cache('dict') || $refresh){
$data = $this->getDictionaryAll();
cache('dict', $data);
}
}
}
+113
View File
@@ -0,0 +1,113 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Services\System;
use App\Models\System\Log;
class LogService {
/**
* @title 获取日志列表
*
* @param [type] $request
* @return void
*/
public function getDataList($request) {
$map = [];
if ($request->filled('title')) {
$map[] = ['title', 'like', '%' . $request->input('title') . '%'];
}
if ($request->filled('name')) {
$map[] = ['name', 'like', '%' . $request->input('name') . '%'];
}
if ($request->filled('url')) {
$map[] = ['url', 'like', '%' . $request->input('url') . '%'];
}
if ($request->filled('method')) {
$map[] = ['method', 'like', '%' . $request->input('method') . '%'];
}
if ($request->filled('ip')) {
$map[] = ['ip', 'like', '%' . $request->input('ip') . '%'];
}
if ($request->filled('user_id')) {
$map[] = ['user_id', '=', $request->input('user_id')];
}
if ($request->filled('remark')) {
$map[] = ['remark', 'like', '%' . $request->input('remark') . '%'];
}
if ($request->filled('status')) {
$map[] = ['status', '=', $request->input('status')];
}
if ($request->filled('is_my')) {
$user = auth('admin')->user();
$map[] = ['user_id', '=', $user->uid];
}
$query = Log::with(['user:nickname,username,uid'])->where($map)->orderBy('id', 'desc');
if ($request->filled('date')) {
$query->whereBetween('created_at', $request->input('date'));
}
$data = [
'total' => $query->count(),
'page' => $request->input('page', 1),
'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(),
];
return $data;
}
public function createLog($request){
list($class, $method) = explode('@', $request->route()->getActionName());
$reflection = new \ReflectionMethod("\\" . $class, $method);
$docComment = $reflection->getDocComment();
$matches = [];
preg_match("/@title(.*)(\\r\\n|\\r|\\n)/U", $docComment, $matches);
$data = [
'user_id' => auth('admin')->user()->uid,
'title' => isset($matches[1]) ? trim($matches[1]) : '',
'name' => $request->route()->getName() ?? '',
'client_ip' => $request->ip(),
'url' => $request->path(),
'method' => $request->method(),
'data' => json_encode($request->all()),
'browser' => $request->userAgent(),
'remark' => $request->input('remark', ''),
'status' => 1,
];
$log = Log::create($data);
return $log;
}
/**
* @title 删除日志
* @param Request $request
* @return bool
*/
public function delete($request){
$map = [];
if($request->filled('id')){
$map[] = ['id', '=', $request->input('id')];
}
if($request->filled('ids')){
$map[] = ['id', 'IN', $request->input('ids')];
}
$query = Log::where($map);
if($request->filled('date')){
$query->whereBetween('created_at', $request->input('date'));
}
$query->delete();
return true;
}
}
+147
View File
@@ -0,0 +1,147 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2025 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Services\System;
use Nwidart\Modules\Contracts\ActivatorInterface;
use Nwidart\Modules\Module;
use Nwidart\Modules\Facades\Module as ModuleFacade;
use Illuminate\Container\Container;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Artisan;
use App\Models\System\Modules;
class ModulesService implements ActivatorInterface {
/**
* 模块管理的模型
* @var class-string<Model> $modelClass
*/
private string $modelClass;
/**
* Laravel Filesystem instance
*
* @var Filesystem
*/
private $files;
public function __construct(Container $app) {
$this->modelClass = $app['config']['modules.activators.database.model'];
$this->files = $app['files'];
}
/**
* Enables a module
*/
public function enable(Module $module): void {
$this->setActiveByName($module->getName(), true);
}
/**
* Disables a module
*/
public function disable(Module $module): void {
$this->setActiveByName($module->getName(), false);
}
/**
* Determine whether the given status same with a module status.
*/
public function hasStatus(Module|string $module, bool $status): bool {
$name = $module instanceof Module ? $module->getName() : $module;
$moduleRecord = $this->modelClass::where('name', $name)->first();
if ($moduleRecord) {
return boolval($moduleRecord['status']) === $status;
} else {
return $status === false;
}
}
/**
* Set active state for a module.
*/
public function setActive(Module $module, bool $active): void {
$this->setActiveByName($module->getName(), $active);
}
/**
* Sets a module status by its name
*/
public function setActiveByName(string $name, bool $active): void {
$module = ModuleFacade::getModulePath($name);
$json = $this->readJson($module . 'module.json');
$this->modelClass::upsert(['name' => $name, 'status' => $active, 'title' => $json['alias']], ['name'], ['status', 'title']);
}
/**
* Deletes a module activation status
*/
public function delete(Module $module): void {
$moduleRecord = $this->modelClass::where('name', $module->getName())->first();
if ($moduleRecord) {
$moduleRecord->delete();
}
}
/**
* Deletes any module activation statuses created by this class.
*/
public function reset(): void {
$this->modelClass::truncate();
}
public function getDataList($request){
$map = [];
if($request->filled('title')){
$map[] = ['title', 'like', '%' . $request->input('title') . '%'];
}
$query = Modules::where($map)->orderBy('id', 'asc');
if($request->filled('page')){
$data = [
'total' => $query->count(),
'page' => $request->input('page', 1),
'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(),
];
}else{
$data = $query->get();
}
return $data;
}
public function update($request){
if($request->filled('name')){
$name = $request->input('name');
$module = ModuleFacade::getModulePath($name);
$json = $this->readJson($module . 'module.json');
$module = Modules::where('name', $name)->first();
$module->title = $json['alias'];
$module->save();
return $module;
}
}
/**
* Reads the json file that contains the activation statuses.
*
* @throws FileNotFoundException
*/
private function readJson($file): array {
if (! $this->files->exists($file)) {
return [];
}
return $this->files->json($file);
}
}
+100
View File
@@ -0,0 +1,100 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Services\System;
use App\Models\System\Config;
class SettingService {
/**
* @title 获取配置列表
*
* @param [type] $request
* @return void
*/
public function getDataList($request){
$map = [];
if ($request->filled('group_name')) {
$map[] = ['group', '=', $request->input('group_name')];
}
$data = Config::where($map)->orderBy('sort', 'asc')->pluck('values', 'name');
return $data;
}
public function create($request){
$request->validate([
'title' => 'required',
'name' => 'required',
'values' => 'required',
]);
$data = $request->all();
$config = Config::create($data);
$this->setConfigCache(true);
return $config;
}
public function update($request){
$request->validate([
'title' => 'required',
'name' => 'required',
]);
$config = Config::where('id', $request->input('id'))->first();
foreach ($config->setFilterFields($request->all()) as $key => $value) {
$config->$key = $value;
}
$config->save();
$this->setConfigCache(true);
return $config;
}
public function saveSetting($request){
$data = $request->all();
foreach ($data as $key => $value) {
Config::where('name', $key)->update(['values' => $value]);
}
$this->setConfigCache(true);
return true;
}
public function delete($request){
$data = $request->all();
$config = Config::find($data['id']);
$config->delete();
$this->setConfigCache(true);
return $config;
}
public function getFields($request){
$map = [];
if ($request->filled('group_name')) {
$map[] = ['group', '=', $request->input('group_name')];
}
$config = Config::where($map)->orderBy('sort', 'asc')->get()->append(['options', 'label']);
return $config;
}
public static function setConfigCache($refresh = false){
if (!cache('config') || $refresh){
$config = Config::orderBy('sort', 'asc')->pluck('values', 'name');
cache('config', $config);
}
}
}
+111
View File
@@ -0,0 +1,111 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Services\System;
use App\Models\System\Tasks;
use Illuminate\Support\Facades\Storage;
class TasksService {
public function getDataList($request){
$map = [];
if ($request->filled('title')) {
$map[] = ['title', 'like', '%' . $request->input('title') . '%'];
}
if ($request->filled('name')) {
$map[] = ['name', 'like', '%' . $request->input('name') . '%'];
}
if ($request->filled('url')) {
$map[] = ['url', 'like', '%' . $request->input('url') . '%'];
}
if ($request->filled('method')) {
$map[] = ['method', 'like', '%' . $request->input('method') . '%'];
}
if ($request->filled('ip')) {
$map[] = ['ip', 'like', '%' . $request->input('ip') . '%'];
}
if ($request->filled('user_id')) {
$map[] = ['user_id', '=', $request->input('user_id')];
}
if ($request->filled('data')) {
$map[] = ['data', 'like', '%' . $request->input('data') . '%'];
}
if ($request->filled('remark')) {
$map[] = ['remark', 'like', '%' . $request->input('remark') . '%'];
}
if ($request->filled('status')) {
$map[] = ['status', '=', $request->input('status')];
}
if ($request->filled('is_my')) {
$user = auth('admin')->user();
$map[] = ['user_id', '=', $user->uid];
}
$query = Tasks::with(['user:nickname,username,uid'])->where($map)->orderBy('id', 'desc');
if ($request->filled('page')) {
$data = [
'total' => $query->count(),
'page' => $request->input('page', 1),
'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get()->append(['url']),
];
}else{
$data = $query->get()->append(['url']);
}
return $data;
}
public function create($data){
$task = new Tasks();
$task->type = $data['type'] ? $data['type'] : 'export';
$task->title = $data['title'] ? $data['title'] : $task->type . '任务';
$task->file = $data['file'] ? $data['file'] : '';
$task->status = $data['status'] ? $data['status'] : 0;
$task->user_id = auth('admin')->user()->uid;
$task->save();
return $task;
}
public function update($data){
$task = Tasks::where('id', $data['id'])->first();
if ($data['status'] != '') {
$task->status = $data['status'];
}
if ($data['file'] != '') {
$task->file = $data['file'];
}
if (in_array($data['type'], ['import', 'export'])) {
$task->type = $data['type'];
}
if ($data['title'] != '') {
$task->title = $data['title'];
}
$task->save();
}
public function delete($data){
$task = Tasks::where('id', $data['id'])->first();
if ($task->status == 0) {
throw new \Exception("该任务正在执行中,无法删除");
}
if ($task->file) {
Storage::disk('public')->delete($task->file);
}
$task->delete();
return $task;
}
}
+230
View File
@@ -0,0 +1,230 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Support;
class Regex {
/**
* 验证用户名
*
* @param string $value 验证的值
* @param int $minLen 最小长度
* @param int $maxLen 最大长度
* @param string $type 验证类型,默认‘ALL,EN.验证英文,CN.验证中文,ALL.验证中文和英文
* @return bool
*/
public static function isUsername($value, $minLen = 2, $maxLen = 48, $type = 'ALL')
{
if (empty ($value)) {
return false;
}
switch ($type) {
case 'EN' :
$match = '/^[_\w\d]{' . $minLen . ',' . $maxLen . '}$/iu';
break;
case 'CN' :
$match = '/^[_\x{4e00}-\x{9fa5}\d]{' . $minLen . ',' . $maxLen . '}$/iu';
break;
default :
$match = '/^[_\w\d\x{4e00}-\x{9fa5}]{' . $minLen . ',' . $maxLen . '}$/iu';
}
return preg_match($match, $value) !== 0;
}
/**
* 验证密码
*
* @param string $value 验证的值
* @param int $minLen 最小长度
* @param int $maxLen 最大长度
* @return bool
*/
public static function isPassword($value, $minLen = 6, $maxLen = 16)
{
$value = trim($value);
if (empty ($value)) {
return false;
}
$match = '/^[\\~!@#$%^&*()-_=+|{}\[\],.?\/:;\'\"\d\w]{' . $minLen . ',' . $maxLen . '}$/';
return preg_match($match, $value) !== 0;
}
/**
* 验证eamil
*
* @param string $value 验证的值
* @return bool
*/
public static function isEmail($value)
{
$value = trim($value);
if (empty ($value)) {
return false;
}
$match = '/^[\w\d]+[\w\d-.]*@[\w\d-.]+\.[\w\d]{2,10}$/i';
return preg_match($match, $value) !== 0;
}
/**
* 验证电话号码
*
* @param string $value 验证的值
* @return bool
*/
public static function isTelephone($value)
{
$value = trim($value);
if (empty ($value)) {
return false;
}
$match = '/^0[0-9]{2,3}[-]?\d{7,8}$/';
return preg_match($match, $value) !== 0;
}
/**
* 验证手机
*
* @param string $value 验证的值
* @return bool
*/
public static function isMobile($value)
{
$value = trim($value);
if (empty ($value)) {
return false;
}
$match = '/^[(86)|0]?(1\d{10})$/';
return preg_match($match, $value) !== 0;
}
/**
* 验证邮政编码
*
* @param string $value 验证的值
* @return bool
*/
public static function isPostCode($value)
{
$value = trim($value);
if (empty ($value)) {
return false;
}
$match = '/\d{6}/';
return preg_match($match, $value) !== 0;
}
/**
* 验证IP
*
* @param string $value 验证的值
* @return boolean
*/
public static function isIp($value)
{
$value = trim($value);
if (empty ($value)) {
return false;
}
$match = '/^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])' .
'\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)' .
'\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)' .
'\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$/';
return preg_match($match, $value) !== 0;
}
/**
* 验证身份证号码
*
* @param string $value 验证的值
* @return boolean
*/
public static function isIDCard($value)
{
$value = trim($value);
if (empty ($value)) {
return false;
}
if (strlen($value) > 18) {
return false;
}
$match = '/^\d{6}((1[89])|(2\d))\d{2}((0\d)|(1[0-2]))((3[01])|([0-2]\d))\d{3}(\d|X)$/i';
return preg_match($match, $value) !== 0;
}
/**
* 验证URL
*
* @param string $value 验证的值
* @return boolean
*/
public static function isUrl($value)
{
$value = strtolower(trim($value));
if (empty ($value)) {
return false;
}
$match = '/^(http:\/\/)?(https:\/\/)?([\w\d-]+\.)+[\w-]+(\/[\d\w-.\/?%&=]*)?$/';
return preg_match($match, $value) !== 0;
}
/**
* 是否有数字
* 说明:如果字符串中含有非法字符返回假,没有返回真
*
* @param string $value 验证的值
* @return int
*/
public static function hasNumber($value)
{
return preg_match("/[0-9]/", $value) != false;
}
/**
* 是否含有英文
* 说明:如果字符串中含有非法字符返回假,没有返回真
*
* @param string $value 验证的值
* @return bool
*/
public static function hasEnglish($value)
{
return preg_match("/[a-zA-Z]/", $value) != false;
}
/**
* 是否有中文
* 说明:如果字符串中含有非法字符返回假,没有返回真
*
* @param string $value 验证的值
* @return bool
*/
public static function hasChinese($value)
{
return preg_match("/[\x7f-\xff]/", $value) != false;
}
}
+298
View File
@@ -0,0 +1,298 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Support;
use Illuminate\Support\Carbon;
class Time {
/**
* 返回今日开始和结束的时间戳
*
* @return array
*/
public static function today($data = '') {
[$y, $m, $d] = explode('-', $data ? $data : date('Y-m-d'));
return [
mktime(0, 0, 0, $m, $d, $y),
mktime(23, 59, 59, $m, $d, $y),
];
}
/**
* 返回昨日开始和结束的时间戳
*
* @return array
*/
public static function yesterday() {
$yesterday = date('d') - 1;
return [
mktime(0, 0, 0, date('m'), $yesterday, date('Y')),
mktime(23, 59, 59, date('m'), $yesterday, date('Y')),
];
}
/**
* 返回本周开始和结束的时间戳
*
* @return array
*/
public static function week() {
[$y, $m, $d, $w] = explode('-', date('Y-m-d-w'));
if ($w == 0) $w = 7; //修正周日的问题
return [
mktime(0, 0, 0, $m, $d - $w + 1, $y), mktime(23, 59, 59, $m, $d - $w + 7, $y),
];
}
/**
* 返回上周开始和结束的时间戳
*
* @return array
*/
public static function lastWeek() {
$timestamp = time();
return [
strtotime(date('Y-m-d', strtotime("last week Monday", $timestamp))),
strtotime(date('Y-m-d', strtotime("last week Sunday", $timestamp))) + 24 * 3600 - 1,
];
}
/**
* 返回本月开始和结束的时间戳
*
* @return array
*/
public static function month($data = '') {
$res = [];
$data = $data ? $data : date('Y-m-d');
$nextMoth = date('Y-m-d', strtotime($data . ' +1 month'));
return [
mktime(0, 0, 0, date('m', strtotime($data)), 1, date('Y', strtotime($data))),
mktime(0, 0, 0, date('m', strtotime($nextMoth)), 1, date('Y', strtotime($nextMoth))),
];
}
/**
* 返回上个月开始和结束的时间戳
*
* @return array
*/
public static function lastMonth() {
$y = date('Y');
$m = date('m');
$begin = mktime(0, 0, 0, $m - 1, 1, $y);
$end = mktime(23, 59, 59, $m - 1, date('t', $begin), $y);
return [$begin, $end];
}
/**
* 返回今年开始和结束的时间戳
*
* @return array
*/
public static function year() {
$y = date('Y');
return [
mktime(0, 0, 0, 1, 1, $y),
mktime(23, 59, 59, 12, 31, $y),
];
}
/**
* 返回去年开始和结束的时间戳
*
* @return array
*/
public static function lastYear() {
$year = date('Y') - 1;
return [
mktime(0, 0, 0, 1, 1, $year),
mktime(23, 59, 59, 12, 31, $year),
];
}
/**
* 获取几天前零点到现在/昨日结束的时间戳
*
* @param int $day 天数
* @param bool $now 返回现在或者昨天结束时间戳
* @return array
*/
public static function dayToNow($day = 1, $now = true) {
$end = time();
if (!$now) {
[$foo, $end] = self::yesterday();
}
return [
mktime(0, 0, 0, date('m'), date('d') - $day, date('Y')),
$end,
];
}
/**
* 返回几天前的时间戳
*
* @param int $day
* @return int
*/
public static function daysAgo($day = 1) {
$nowTime = time();
return $nowTime - self::daysToSecond($day);
}
/**
* 返回几天后的时间戳
*
* @param int $day
* @return int
*/
public static function daysAfter($day = 1) {
$nowTime = time();
return $nowTime + self::daysToSecond($day);
}
/**
* 天数转换成秒数
*
* @param int $day
* @return int
*/
public static function daysToSecond($day = 1) {
return $day * 86400;
}
/**
* 周数转换成秒数
*
* @param int $week
* @return int
*/
public static function weekToSecond($week = 1) {
return self::daysToSecond() * 7 * $week;
}
/**
* 获取毫秒级别的时间戳
*/
public static function getMillisecond() {
$time = explode(" ", microtime());
$time = $time[1] . ($time[0] * 1000);
$time2 = explode(".", $time);
$time = $time2[0];
return $time;
}
/**
* 获取相对时间
*
* @param int $timeStamp
* @return string
*/
public static function formatRelative($timeStamp) {
$currentTime = time();
// 判断传入时间戳是否早于当前时间戳
$isEarly = $timeStamp <= $currentTime;
// 获取两个时间戳差值
$diff = abs($currentTime - $timeStamp);
$dirStr = $isEarly ? '前' : '后';
if ($diff < 60) { // 一分钟之内
$resStr = $diff . '秒' . $dirStr;
} elseif ($diff >= 60 && $diff < 3600) { // 多于59秒,少于等于59分钟59秒
$resStr = floor($diff / 60) . '分钟' . $dirStr;
} elseif ($diff >= 3600 && $diff < 86400) { // 多于59分钟59秒,少于等于23小时59分钟59秒
$resStr = floor($diff / 3600) . '小时' . $dirStr;
} elseif ($diff >= 86400 && $diff < 2623860) { // 多于23小时59分钟59秒,少于等于29天59分钟59秒
$resStr = floor($diff / 86400) . '天' . $dirStr;
} elseif ($diff >= 2623860 && $diff <= 31567860 && $isEarly) { // 多于29天59分钟59秒,少于364天23小时59分钟59秒,且传入的时间戳早于当前
$resStr = date('m-d H:i', $timeStamp);
} else {
$resStr = date('Y-m-d', $timeStamp);
}
return $resStr;
}
/**
* 范围日期转换时间戳
*
* @param string $rangeDatetime
* @param int $maxRange 最大时间间隔
* @param string $delimiter
* @return array
*/
public static function parseRange($rangeDatetime, $maxRange = 0, $delimiter = ' - ') {
$rangeDatetime = explode($delimiter, $rangeDatetime, 2);
$rangeDatetime[0] = strtotime($rangeDatetime[0]);
$rangeDatetime[1] = isset($rangeDatetime[1]) ? strtotime($rangeDatetime[1]) : time();
$rangeDatetime = [
min($rangeDatetime[0], $rangeDatetime[1]),
max($rangeDatetime[0], $rangeDatetime[1]),
];
// 如果结束时间小于或等于开始时间 直接返回null
// if ($rangeDatetime[1] < $rangeDatetime[0]) {
// return null;
// }
// 如果大于最大时间间隔 则用结束时间减去最大时间间隔获得开始时间
if ($maxRange > 0 && $rangeDatetime[1] - $rangeDatetime[0] > $maxRange) {
$rangeDatetime[0] = $rangeDatetime[1] - $maxRange;
}
return $rangeDatetime;
}
/**
* 获取指定时间范围内的日期数组
* @param int $startTime
* @param int $endTime
* @return \Carbon\CarbonPeriod
*/
public static function daysUntilOfTimestamp($startTime, $endTime) {
$startTime = Carbon::createFromTimestamp($startTime);
$endTime = Carbon::createFromTimestamp($endTime);
return $startTime->daysUntil($endTime);
}
/**
* 时间排序
*
* @param array $times
* @return array
*/
public static function sort($times) {
usort($times, function ($com1, $com2) {
$com1 = strtotime($com1);
$com2 = strtotime($com2);
return $com1 < $com2 ? -1 : 1;
});
return $times;
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Support;
class Tree {
public static function list_to_tree($list, $root = 0, $pk = 'id', $pid = 'parent_id', $children = 'children') {
// 创建Tree
$tree = array();
if (is_array($list)) {
// 创建基于主键的数组引用
$refer = array();
foreach ($list as $key => $data) {
$refer[$data[$pk]] = &$list[$key];
}
foreach ($list as $key => $data) {
// 判断是否存在parent
$parentId = 0;
if (isset($data[$pid])) {
$parentId = $data[$pid];
}
if ((string)$root == $parentId) {
$tree[] = &$list[$key];
} else {
if (isset($refer[$parentId])) {
$parent = &$refer[$parentId];
$parent[$children][] = &$list[$key];
}
}
}
}
return $tree;
}
public static function tree_to_list($tree = [], $children = 'children') {
if (empty($tree) || !is_array($tree)) {
return $tree;
}
$arrRes = [];
foreach ($tree as $k => $v) {
$arrTmp = $v;
unset($arrTmp[$children]);
$arrRes[] = $arrTmp;
if (!empty($v[$children])) {
$arrTmp = self::tree_to_list($v[$children], $children);
$arrRes = array_merge($arrRes, $arrTmp);
}
}
return $arrRes;
}
/**
* 获得所有的子
*/
public static function get_children($data, $id = 0, $pk = 'id', $pid = 'parent_id') {
$array = [];
foreach ($data as $k => $v) {
if ($v[$pid] == $id) {
$array[] = $v[$pk];
array_merge($array, self::get_children($data, $v[$pk], $pk, $pid));
}
}
return $array;
}
/**
* 获取id的所有父,包含自己
*/
public static function get_parents($data, $id = 0, $pk = 'id', $pid = 'parent_id', $field = '') {
$temp = [];
foreach ($data as $k => $v) {
if ($v[$pk] == $id) {
$temp[] = $v;
$temp = array_merge($temp, self::get_parents($data, $v[$pid]), $pk, $pid, $field);
}
}
if($field){
$temp = array_column($temp, $field);
}
return $temp;
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Traits;
use Illuminate\Http\Request;
trait AdminController {
protected $service = null;
/**
* @title 列表
*
* @param Request $request
* @param $service
* @return void
*/
public function index(Request $request){
try {
$this->data['data'] = $this->service->getDataList($request);
} catch (\Throwable $th) {
$this->data['code'] = 0;
$this->data['message'] = $th->getMessage();
}
return response()->json($this->data);
}
/**
* @title 添加
*
* @param Request $request
* @param $service
* @return void
*/
public function add(Request $request){
try {
$this->data['data'] = $this->service->create($request);
} catch (\Throwable $th) {
$this->data['code'] = 0;
$this->data['message'] = $th->getMessage();
}
return response()->json($this->data);
}
/**
* @title 编辑
*
* @param Request $request
* @param $service
* @return void
*/
public function edit(Request $request){
try {
$this->data['data'] = $this->service->update($request);
} catch (\Throwable $th) {
$this->data['code'] = 0;
$this->data['message'] = $th->getMessage();
}
return response()->json($this->data);
}
/**
* @title 删除
*
* @param Request $request
* @param $service
* @return void
*/
public function delete(Request $request){
try {
$this->data['data'] = $this->service->delete($request);
} catch (\Throwable $th) {
$this->data['code'] = 0;
$this->data['message'] = $th->getMessage();
}
return response()->json($this->data);
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
namespace App\Traits;
use Illuminate\Support\Facades\Schema;
trait ModelTrait {
protected function casts(): array {
return [
'created_at' => 'datetime:Y-m-d H:i:s',
'updated_at' => 'datetime:Y-m-d H:i:s',
'deleted_at' => 'datetime:Y-m-d H:i:s',
];
}
protected function serializeDate($data){
return $data->timezone('Asia/Shanghai')->format('Y-m-d H:i:s');
}
/**
* 过滤移除非当前表的字段参数
*
* @param array $params
*
* @return array
*/
public function setFilterFields(array $params) : array {
$fields = Schema::getColumnListing($this->getTable());
foreach ($params as $key => $param) {
if ( !in_array($key, $fields) ) unset($params[$key]);
}
// 同时过滤时间戳字段【时间戳只允许自动更改,不允许手动设置】
if ( $this->timestamps === true && isset($params[self::CREATED_AT]) ) unset($params[self::CREATED_AT]);
if ( $this->timestamps === true && isset($params[self::UPDATED_AT]) ) unset($params[self::UPDATED_AT]);
return $params;
}
}
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env php
<?php
use Symfony\Component\Console\Input\ArgvInput;
define('LARAVEL_START', microtime(true));
// Register the Composer autoloader...
require __DIR__.'/vendor/autoload.php';
// Bootstrap Laravel and handle the command...
$status = (require_once __DIR__.'/bootstrap/app.php')
->handleCommand(new ArgvInput);
exit($status);
+38
View File
@@ -0,0 +1,38 @@
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Http\Request;
use App\Http\Middleware\AuthCheckMiddleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
then: function(){
Route::middleware(['api'])
->prefix('admin')
->name('admin.')
->group(base_path('routes/admin.php'));
},
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'auth.check' => AuthCheckMiddleware::class
]);
})
->withExceptions(function (Exceptions $exceptions) {
$exceptions->shouldRenderJsonWhen(function(Request $request, Throwable $e){
// if($request->is('admin/*')){
// return true;
// }
return $request->expectsJson();
});
})
->withEvents(discover: [
__DIR__ . '/../app/Listeners'
])
->create();
+2
View File
@@ -0,0 +1,2 @@
*
!.gitignore
+5
View File
@@ -0,0 +1,5 @@
<?php
return [
App\Providers\AppServiceProvider::class,
];
+85
View File
@@ -0,0 +1,85 @@
{
"name": "laravel/laravel",
"type": "project",
"description": "The skeleton application for the Laravel framework.",
"keywords": ["laravel", "framework"],
"license": "MIT",
"require": {
"php": "^8.2",
"alphasnow/aliyun-oss-laravel": "^4.9",
"dcat/easy-excel": "^1.1",
"kitloong/laravel-migrations-generator": "^7.1",
"laravel/framework": "^11.0",
"nwidart/laravel-modules": "^11.0",
"tymon/jwt-auth": "^2.1",
"w7corp/easywechat": "^6.15",
"workerman/gateway-worker": "^3.1"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.13",
"fakerphp/faker": "^1.23",
"laravel/pint": "^1.13",
"laravel/sail": "^1.26",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.0",
"phpunit/phpunit": "^11.0.1",
"spatie/laravel-ignition": "^2.4"
},
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi"
],
"post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
],
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate --ansi",
"@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
"@php artisan migrate --graceful --ansi"
],
"install-project": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
"@php artisan key:generate --ansi",
"@php artisan jwt:secret --ansi",
"@php artisan storage:link --ansi"
]
},
"extra": {
"laravel": {
"dont-discover": []
},
"merge-plugin": {
"include": [
"modules/*/composer.json"
]
}
},
"config": {
"optimize-autoloader": true,
"preferred-install": "dist",
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true,
"wikimedia/composer-merge-plugin": true
}
},
"minimum-stability": "stable",
"prefer-stable": true
}
+126
View File
@@ -0,0 +1,126 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Application Name
|--------------------------------------------------------------------------
|
| This value is the name of your application, which will be used when the
| framework needs to place the application's name in a notification or
| other UI elements where an application name needs to be displayed.
|
*/
'name' => env('APP_NAME', 'Laravel'),
/*
|--------------------------------------------------------------------------
| Application Environment
|--------------------------------------------------------------------------
|
| This value determines the "environment" your application is currently
| running in. This may determine how you prefer to configure various
| services the application utilizes. Set this in your ".env" file.
|
*/
'env' => env('APP_ENV', 'production'),
/*
|--------------------------------------------------------------------------
| Application Debug Mode
|--------------------------------------------------------------------------
|
| When your application is in debug mode, detailed error messages with
| stack traces will be shown on every error that occurs within your
| application. If disabled, a simple generic error page is shown.
|
*/
'debug' => (bool) env('APP_DEBUG', false),
/*
|--------------------------------------------------------------------------
| Application URL
|--------------------------------------------------------------------------
|
| This URL is used by the console to properly generate URLs when using
| the Artisan command line tool. You should set this to the root of
| the application so that it's available within Artisan commands.
|
*/
'url' => env('APP_URL', 'http://localhost'),
/*
|--------------------------------------------------------------------------
| Application Timezone
|--------------------------------------------------------------------------
|
| Here you may specify the default timezone for your application, which
| will be used by the PHP date and date-time functions. The timezone
| is set to "UTC" by default as it is suitable for most use cases.
|
*/
'timezone' => env('APP_TIMEZONE', 'UTC'),
/*
|--------------------------------------------------------------------------
| Application Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the default locale that will be used
| by Laravel's translation / localization methods. This option can be
| set to any locale for which you plan to have translation strings.
|
*/
'locale' => env('APP_LOCALE', 'en'),
'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'),
'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'),
/*
|--------------------------------------------------------------------------
| Encryption Key
|--------------------------------------------------------------------------
|
| This key is utilized by Laravel's encryption services and should be set
| to a random, 32 character string to ensure that all encrypted values
| are secure. You should do this prior to deploying the application.
|
*/
'cipher' => 'AES-256-CBC',
'key' => env('APP_KEY'),
'previous_keys' => [
...array_filter(
explode(',', env('APP_PREVIOUS_KEYS', ''))
),
],
/*
|--------------------------------------------------------------------------
| Maintenance Mode Driver
|--------------------------------------------------------------------------
|
| These configuration options determine the driver used to determine and
| manage Laravel's "maintenance mode" status. The "cache" driver will
| allow maintenance mode to be controlled across multiple machines.
|
| Supported drivers: "file", "cache"
|
*/
'maintenance' => [
'driver' => env('APP_MAINTENANCE_DRIVER', 'file'),
'store' => env('APP_MAINTENANCE_STORE', 'database'),
],
];
+122
View File
@@ -0,0 +1,122 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option defines the default authentication "guard" and password
| reset "broker" for your application. You may change these values
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => env('AUTH_GUARD', 'web'),
'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| which utilizes session storage plus the Eloquent user provider.
|
| All authentication guards have a user provider, which defines how the
| users are actually retrieved out of your database or other storage
| system used by the application. Typically, Eloquent is utilized.
|
| Supported: "session"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'members',
],
'admin' => [
'driver' => 'jwt',
'provider' => 'admins'
],
'api' => [
'driver' => 'jwt',
'provider' => 'members',
]
],
/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication guards have a user provider, which defines how the
| users are actually retrieved out of your database or other storage
| system used by the application. Typically, Eloquent is utilized.
|
| If you have multiple user tables or models you may configure multiple
| providers to represent the model / table. These providers may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/
'providers' => [
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Auth\Admin::class,
],
'members' => [
'driver' => 'eloquent',
'model' => App\Member\Models\Member::class,
],
],
/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| These configuration options specify the behavior of Laravel's password
| reset functionality, including the table utilized for token storage
| and the user provider that is invoked to actually retrieve users.
|
| The expiry time is the number of minutes that each reset token will be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
| The throttle setting is the number of seconds a user must wait before
| generating more password reset tokens. This prevents the user from
| quickly generating a very large amount of password reset tokens.
|
*/
'passwords' => [
'users' => [
'provider' => 'members',
'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
'expire' => 60,
'throttle' => 60,
],
],
/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| window expires and users are asked to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/
'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800),
];
+107
View File
@@ -0,0 +1,107 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Cache Store
|--------------------------------------------------------------------------
|
| This option controls the default cache store that will be used by the
| framework. This connection is utilized if another isn't explicitly
| specified when running a cache operation inside the application.
|
*/
'default' => env('CACHE_STORE', 'database'),
/*
|--------------------------------------------------------------------------
| Cache Stores
|--------------------------------------------------------------------------
|
| Here you may define all of the cache "stores" for your application as
| well as their drivers. You may even define multiple stores for the
| same cache driver to group types of items stored in your caches.
|
| Supported drivers: "array", "database", "file", "memcached",
| "redis", "dynamodb", "octane", "null"
|
*/
'stores' => [
'array' => [
'driver' => 'array',
'serialize' => false,
],
'database' => [
'driver' => 'database',
'table' => env('DB_CACHE_TABLE', 'cache'),
'connection' => env('DB_CACHE_CONNECTION'),
'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'),
],
'file' => [
'driver' => 'file',
'path' => storage_path('framework/cache/data'),
'lock_path' => storage_path('framework/cache/data'),
],
'memcached' => [
'driver' => 'memcached',
'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
'sasl' => [
env('MEMCACHED_USERNAME'),
env('MEMCACHED_PASSWORD'),
],
'options' => [
// Memcached::OPT_CONNECT_TIMEOUT => 2000,
],
'servers' => [
[
'host' => env('MEMCACHED_HOST', '127.0.0.1'),
'port' => env('MEMCACHED_PORT', 11211),
'weight' => 100,
],
],
],
'redis' => [
'driver' => 'redis',
'connection' => env('REDIS_CACHE_CONNECTION', 'cache'),
'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'),
],
'dynamodb' => [
'driver' => 'dynamodb',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),
'endpoint' => env('DYNAMODB_ENDPOINT'),
],
'octane' => [
'driver' => 'octane',
],
],
/*
|--------------------------------------------------------------------------
| Cache Key Prefix
|--------------------------------------------------------------------------
|
| When utilizing the APC, database, memcached, Redis, and DynamoDB cache
| stores, there might be other applications using the same cache. For
| that reason, you may prefix every cache key to avoid collisions.
|
*/
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
];
+34
View File
@@ -0,0 +1,34 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Cross-Origin Resource Sharing (CORS) Configuration
|--------------------------------------------------------------------------
|
| Here you may configure your settings for cross-origin resource sharing
| or "CORS". This determines what cross-origin operations may execute
| in web browsers. You are free to adjust these settings as needed.
|
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
*/
'paths' => ['api/*', 'admin/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
];
+170
View File
@@ -0,0 +1,170 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Database Connection Name
|--------------------------------------------------------------------------
|
| Here you may specify which of the database connections below you wish
| to use as your default connection for database operations. This is
| the connection which will be utilized unless another connection
| is explicitly specified when you execute a query / statement.
|
*/
'default' => env('DB_CONNECTION', 'sqlite'),
/*
|--------------------------------------------------------------------------
| Database Connections
|--------------------------------------------------------------------------
|
| Below are all of the database connections defined for your application.
| An example configuration is provided for each database system which
| is supported by Laravel. You're free to add / remove connections.
|
*/
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'url' => env('DB_URL'),
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
],
'mysql' => [
'driver' => 'mysql',
'url' => env('DB_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => env('DB_CHARSET', 'utf8mb4'),
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
'prefix' => 'sent_',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'mariadb' => [
'driver' => 'mariadb',
'url' => env('DB_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => env('DB_CHARSET', 'utf8mb4'),
'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'),
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'pgsql' => [
'driver' => 'pgsql',
'url' => env('DB_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => env('DB_CHARSET', 'utf8'),
'prefix' => '',
'prefix_indexes' => true,
'search_path' => 'public',
'sslmode' => 'prefer',
],
'sqlsrv' => [
'driver' => 'sqlsrv',
'url' => env('DB_URL'),
'host' => env('DB_HOST', 'localhost'),
'port' => env('DB_PORT', '1433'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => env('DB_CHARSET', 'utf8'),
'prefix' => '',
'prefix_indexes' => true,
// 'encrypt' => env('DB_ENCRYPT', 'yes'),
// 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
],
],
/*
|--------------------------------------------------------------------------
| Migration Repository Table
|--------------------------------------------------------------------------
|
| This table keeps track of all the migrations that have already run for
| your application. Using this information, we can determine which of
| the migrations on disk haven't actually been run on the database.
|
*/
'migrations' => [
'table' => 'migrations',
'update_date_on_publish' => true,
],
/*
|--------------------------------------------------------------------------
| Redis Databases
|--------------------------------------------------------------------------
|
| Redis is an open source, fast, and advanced key-value store that also
| provides a richer body of commands than a typical key-value system
| such as Memcached. You may define your connection settings here.
|
*/
'redis' => [
'client' => env('REDIS_CLIENT', 'phpredis'),
'options' => [
'cluster' => env('REDIS_CLUSTER', 'redis'),
'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
],
'default' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_DB', '0'),
],
'cache' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', '6379'),
'database' => env('REDIS_CACHE_DB', '1'),
],
],
];
+325
View File
@@ -0,0 +1,325 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Debugbar Settings
|--------------------------------------------------------------------------
|
| Debugbar is enabled by default, when debug is set to true in app.php.
| You can override the value by setting enable to true or false instead of null.
|
| You can provide an array of URI's that must be ignored (eg. 'api/*')
|
*/
'enabled' => env('DEBUGBAR_ENABLED', null),
'except' => [
'telescope*',
'horizon*',
],
/*
|--------------------------------------------------------------------------
| Storage settings
|--------------------------------------------------------------------------
|
| DebugBar stores data for session/ajax requests.
| You can disable this, so the debugbar stores data in headers/session,
| but this can cause problems with large data collectors.
| By default, file storage (in the storage folder) is used. Redis and PDO
| can also be used. For PDO, run the package migrations first.
|
| Warning: Enabling storage.open will allow everyone to access previous
| request, do not enable open storage in publicly available environments!
| Specify a callback if you want to limit based on IP or authentication.
| Leaving it to null will allow localhost only.
*/
'storage' => [
'enabled' => true,
'open' => env('DEBUGBAR_OPEN_STORAGE'), // bool/callback.
'driver' => 'file', // redis, file, pdo, socket, custom
'path' => storage_path('debugbar'), // For file driver
'connection' => null, // Leave null for default connection (Redis/PDO)
'provider' => '', // Instance of StorageInterface for custom driver
'hostname' => '127.0.0.1', // Hostname to use with the "socket" driver
'port' => 2304, // Port to use with the "socket" driver
],
/*
|--------------------------------------------------------------------------
| Editor
|--------------------------------------------------------------------------
|
| Choose your preferred editor to use when clicking file name.
|
| Supported: "phpstorm", "vscode", "vscode-insiders", "vscode-remote",
| "vscode-insiders-remote", "vscodium", "textmate", "emacs",
| "sublime", "atom", "nova", "macvim", "idea", "netbeans",
| "xdebug", "espresso"
|
*/
'editor' => env('DEBUGBAR_EDITOR') ?: env('IGNITION_EDITOR', 'phpstorm'),
/*
|--------------------------------------------------------------------------
| Remote Path Mapping
|--------------------------------------------------------------------------
|
| If you are using a remote dev server, like Laravel Homestead, Docker, or
| even a remote VPS, it will be necessary to specify your path mapping.
|
| Leaving one, or both of these, empty or null will not trigger the remote
| URL changes and Debugbar will treat your editor links as local files.
|
| "remote_sites_path" is an absolute base path for your sites or projects
| in Homestead, Vagrant, Docker, or another remote development server.
|
| Example value: "/home/vagrant/Code"
|
| "local_sites_path" is an absolute base path for your sites or projects
| on your local computer where your IDE or code editor is running on.
|
| Example values: "/Users/<name>/Code", "C:\Users\<name>\Documents\Code"
|
*/
'remote_sites_path' => env('DEBUGBAR_REMOTE_SITES_PATH'),
'local_sites_path' => env('DEBUGBAR_LOCAL_SITES_PATH', env('IGNITION_LOCAL_SITES_PATH')),
/*
|--------------------------------------------------------------------------
| Vendors
|--------------------------------------------------------------------------
|
| Vendor files are included by default, but can be set to false.
| This can also be set to 'js' or 'css', to only include javascript or css vendor files.
| Vendor files are for css: font-awesome (including fonts) and highlight.js (css files)
| and for js: jquery and highlight.js
| So if you want syntax highlighting, set it to true.
| jQuery is set to not conflict with existing jQuery scripts.
|
*/
'include_vendors' => true,
/*
|--------------------------------------------------------------------------
| Capture Ajax Requests
|--------------------------------------------------------------------------
|
| The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors),
| you can use this option to disable sending the data through the headers.
|
| Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools.
|
| Note for your request to be identified as ajax requests they must either send the header
| X-Requested-With with the value XMLHttpRequest (most JS libraries send this), or have application/json as a Accept header.
|
| By default `ajax_handler_auto_show` is set to true allowing ajax requests to be shown automatically in the Debugbar.
| Changing `ajax_handler_auto_show` to false will prevent the Debugbar from reloading.
*/
'capture_ajax' => true,
'add_ajax_timing' => false,
'ajax_handler_auto_show' => true,
'ajax_handler_enable_tab' => true,
/*
|--------------------------------------------------------------------------
| Custom Error Handler for Deprecated warnings
|--------------------------------------------------------------------------
|
| When enabled, the Debugbar shows deprecated warnings for Symfony components
| in the Messages tab.
|
*/
'error_handler' => false,
/*
|--------------------------------------------------------------------------
| Clockwork integration
|--------------------------------------------------------------------------
|
| The Debugbar can emulate the Clockwork headers, so you can use the Chrome
| Extension, without the server-side code. It uses Debugbar collectors instead.
|
*/
'clockwork' => false,
/*
|--------------------------------------------------------------------------
| DataCollectors
|--------------------------------------------------------------------------
|
| Enable/disable DataCollectors
|
*/
'collectors' => [
'phpinfo' => true, // Php version
'messages' => true, // Messages
'time' => true, // Time Datalogger
'memory' => true, // Memory usage
'exceptions' => true, // Exception displayer
'log' => true, // Logs from Monolog (merged in messages if enabled)
'db' => true, // Show database (PDO) queries and bindings
'views' => true, // Views with their data
'route' => true, // Current route information
'auth' => false, // Display Laravel authentication status
'gate' => true, // Display Laravel Gate checks
'session' => true, // Display session data
'symfony_request' => true, // Only one can be enabled..
'mail' => true, // Catch mail messages
'laravel' => false, // Laravel version and environment
'events' => false, // All events fired
'default_request' => false, // Regular or special Symfony request logger
'logs' => false, // Add the latest log messages
'files' => false, // Show the included files
'config' => false, // Display config settings
'cache' => false, // Display cache events
'models' => true, // Display models
'livewire' => true, // Display Livewire (when available)
'jobs' => false, // Display dispatched jobs
],
/*
|--------------------------------------------------------------------------
| Extra options
|--------------------------------------------------------------------------
|
| Configure some DataCollectors
|
*/
'options' => [
'time' => [
'memory_usage' => false, // Calculated by subtracting memory start and end, it may be inaccurate
],
'messages' => [
'trace' => true, // Trace the origin of the debug message
],
'memory' => [
'reset_peak' => false, // run memory_reset_peak_usage before collecting
'with_baseline' => false, // Set boot memory usage as memory peak baseline
'precision' => 0, // Memory rounding precision
],
'auth' => [
'show_name' => true, // Also show the users name/email in the debugbar
'show_guards' => true, // Show the guards that are used
],
'db' => [
'with_params' => true, // Render SQL with the parameters substituted
'backtrace' => true, // Use a backtrace to find the origin of the query in your files.
'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults)
'timeline' => false, // Add the queries to the timeline
'duration_background' => true, // Show shaded background on each query relative to how long it took to execute.
'explain' => [ // Show EXPLAIN output on queries
'enabled' => false,
'types' => ['SELECT'], // Deprecated setting, is always only SELECT
],
'hints' => false, // Show hints for common mistakes
'show_copy' => false, // Show copy button next to the query,
'slow_threshold' => false, // Only track queries that last longer than this time in ms
'memory_usage' => false, // Show queries memory usage
'soft_limit' => 100, // After the soft limit, no parameters/backtrace are captured
'hard_limit' => 500, // After the hard limit, queries are ignored
],
'mail' => [
'timeline' => false, // Add mails to the timeline
'show_body' => true,
],
'views' => [
'timeline' => false, // Add the views to the timeline (Experimental)
'data' => false, //true for all data, 'keys' for only names, false for no parameters.
'group' => 50, // Group duplicate views. Pass value to auto-group, or true/false to force
'exclude_paths' => [ // Add the paths which you don't want to appear in the views
'vendor/filament' // Exclude Filament components by default
],
],
'route' => [
'label' => true, // show complete route on bar
],
'session' => [
'hiddens' => [], // hides sensitive values using array paths
],
'symfony_request' => [
'hiddens' => [], // hides sensitive values using array paths, example: request_request.password
],
'events' => [
'data' => false, // collect events data, listeners
],
'logs' => [
'file' => null,
],
'cache' => [
'values' => true, // collect cache values
],
],
/*
|--------------------------------------------------------------------------
| Inject Debugbar in Response
|--------------------------------------------------------------------------
|
| Usually, the debugbar is added just before </body>, by listening to the
| Response after the App is done. If you disable this, you have to add them
| in your template yourself. See http://phpdebugbar.com/docs/rendering.html
|
*/
'inject' => true,
/*
|--------------------------------------------------------------------------
| DebugBar route prefix
|--------------------------------------------------------------------------
|
| Sometimes you want to set route prefix to be used by DebugBar to load
| its resources from. Usually the need comes from misconfigured web server or
| from trying to overcome bugs like this: http://trac.nginx.org/nginx/ticket/97
|
*/
'route_prefix' => '_debugbar',
/*
|--------------------------------------------------------------------------
| DebugBar route middleware
|--------------------------------------------------------------------------
|
| Additional middleware to run on the Debugbar routes
*/
'route_middleware' => [],
/*
|--------------------------------------------------------------------------
| DebugBar route domain
|--------------------------------------------------------------------------
|
| By default DebugBar route served from the same domain that request served.
| To override default domain, specify it as a non-empty value.
*/
'route_domain' => null,
/*
|--------------------------------------------------------------------------
| DebugBar theme
|--------------------------------------------------------------------------
|
| Switches between light and dark theme. If set to auto it will respect system preferences
| Possible values: auto, light, dark
*/
'theme' => env('DEBUGBAR_THEME', 'auto'),
/*
|--------------------------------------------------------------------------
| Backtrace stack limit
|--------------------------------------------------------------------------
|
| By default, the DebugBar limits the number of frames returned by the 'debug_backtrace()' function.
| If you need larger stacktraces, you can increase this number. Setting it to 0 will result in no limit.
*/
'debug_backtrace_limit' => 50,
];
+94
View File
@@ -0,0 +1,94 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Filesystem Disk
|--------------------------------------------------------------------------
|
| Here you may specify the default filesystem disk that should be used
| by the framework. The "local" disk, as well as a variety of cloud
| based disks are available to your application for file storage.
|
*/
'default' => env('FILESYSTEM_DISK', 'local'),
/*
|--------------------------------------------------------------------------
| Filesystem Disks
|--------------------------------------------------------------------------
|
| Below you may configure as many filesystem disks as necessary, and you
| may even configure multiple disks for the same driver. Examples for
| most supported storage drivers are configured here for reference.
|
| Supported Drivers: "local", "ftp", "sftp", "s3"
|
*/
'disks' => [
'local' => [
'driver' => 'local',
'root' => storage_path('app'),
'throw' => false,
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL').'/storage',
'visibility' => 'public',
'throw' => false,
],
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
'throw' => false,
],
'oss' => [
"driver" => "oss",
"access_key_id" => env("OSS_ACCESS_KEY_ID"), // Required, yourAccessKeyId
"access_key_secret" => env("OSS_ACCESS_KEY_SECRET"), // Required, yourAccessKeySecret
"bucket" => env("OSS_BUCKET"), // Required, for example: my-bucket
"endpoint" => env("OSS_ENDPOINT"), // Required, for example: oss-cn-shanghai.aliyuncs.com
"internal" => env("OSS_INTERNAL", null), // Optional, for example: oss-cn-shanghai-internal.aliyuncs.com
"domain" => env("OSS_DOMAIN", null), // Optional, for example: oss.my-domain.com
"is_cname" => env("OSS_CNAME", false), // Optional, if the Endpoint is a custom domain name, this must be true, see: https://github.com/aliyun/aliyun-oss-php-sdk/blob/572d0f8e099e8630ae7139ed3fdedb926c7a760f/src/OSS/OssClient.php#L113C1-L122C78
"prefix" => env("OSS_PREFIX", ""), // Optional, the prefix of the store path
"use_ssl" => env("OSS_SSL", false), // Optional, whether to use HTTPS
"throw" => env("OSS_THROW", false), // Optional, whether to throw an exception that causes an error
"signatureVersion" => env("OSS_SIGNATURE_VERSION", "v1"), // Optional, select v1 or v4 as the signature version
"region" => env("OSS_REGION", ""), // Optional, for example: cn-shanghai, used only when v4 signature version is selected
"options" => [], // Optional, add global configuration parameters, For example: [\OSS\OssClient::OSS_CHECK_MD5 => false]
"macros" => [] // Optional, add custom Macro, For example: [\App\Macros\ListBuckets::class, \App\Macros\CreateBucket::class]
],
],
/*
|--------------------------------------------------------------------------
| Symbolic Links
|--------------------------------------------------------------------------
|
| Here you may configure the symbolic links that will be created when the
| `storage:link` Artisan command is executed. The array keys should be
| the locations of the links and the values should be their targets.
|
*/
'links' => [
public_path('storage') => storage_path('app/public'),
],
];
+40
View File
@@ -0,0 +1,40 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
return[
// 扩展自身需要的配置
'protocol' => 'websocket', // 协议 支持 tcp udp unix http websocket text
'host' => '0.0.0.0', // 监听地址
'port' => 2348, // 监听端口
'socket' => '', // 完整监听地址
'context' => [], // socket 上下文选项
'register_deploy' => true, // 是否需要部署register
'businessWorker_deploy' => true, // 是否需要部署businessWorker
'gateway_deploy' => true, // 是否需要部署gateway
// Register配置
'registerAddress' => '127.0.0.1:1236',
// Gateway配置
'name' => 'workerman',
'count' => 1,
'lanIp' => '127.0.0.1',
'startPort' => 2000,
'daemonize' => false,
'pingInterval' => 30,
'pingNotResponseLimit' => 0,
'pingData' => '{"type":"ping"}',
// BusinsessWorker配置
'businessWorker' => [
'name' => 'BusinessWorker',
'count' => 1,
'eventHandler' => '\Modules\Workerman\Events\WorkermanEvent',
],
];
+301
View File
@@ -0,0 +1,301 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) Sean Tymon <tymon148@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [
/*
|--------------------------------------------------------------------------
| JWT Authentication Secret
|--------------------------------------------------------------------------
|
| Don't forget to set this in your .env file, as it will be used to sign
| your tokens. A helper command is provided for this:
| `php artisan jwt:secret`
|
| Note: This will be used for Symmetric algorithms only (HMAC),
| since RSA and ECDSA use a private/public key combo (See below).
|
*/
'secret' => env('JWT_SECRET'),
/*
|--------------------------------------------------------------------------
| JWT Authentication Keys
|--------------------------------------------------------------------------
|
| The algorithm you are using, will determine whether your tokens are
| signed with a random string (defined in `JWT_SECRET`) or using the
| following public & private keys.
|
| Symmetric Algorithms:
| HS256, HS384 & HS512 will use `JWT_SECRET`.
|
| Asymmetric Algorithms:
| RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below.
|
*/
'keys' => [
/*
|--------------------------------------------------------------------------
| Public Key
|--------------------------------------------------------------------------
|
| A path or resource to your public key.
|
| E.g. 'file://path/to/public/key'
|
*/
'public' => env('JWT_PUBLIC_KEY'),
/*
|--------------------------------------------------------------------------
| Private Key
|--------------------------------------------------------------------------
|
| A path or resource to your private key.
|
| E.g. 'file://path/to/private/key'
|
*/
'private' => env('JWT_PRIVATE_KEY'),
/*
|--------------------------------------------------------------------------
| Passphrase
|--------------------------------------------------------------------------
|
| The passphrase for your private key. Can be null if none set.
|
*/
'passphrase' => env('JWT_PASSPHRASE'),
],
/*
|--------------------------------------------------------------------------
| JWT time to live
|--------------------------------------------------------------------------
|
| Specify the length of time (in minutes) that the token will be valid for.
| Defaults to 1 hour.
|
| You can also set this to null, to yield a never expiring token.
| Some people may want this behaviour for e.g. a mobile app.
| This is not particularly recommended, so make sure you have appropriate
| systems in place to revoke the token if necessary.
| Notice: If you set this to null you should remove 'exp' element from 'required_claims' list.
|
*/
'ttl' => env('JWT_TTL', 60 * 60 * 24 * 7 ),
/*
|--------------------------------------------------------------------------
| Refresh time to live
|--------------------------------------------------------------------------
|
| Specify the length of time (in minutes) that the token can be refreshed
| within. I.E. The user can refresh their token within a 2 week window of
| the original token being created until they must re-authenticate.
| Defaults to 2 weeks.
|
| You can also set this to null, to yield an infinite refresh time.
| Some may want this instead of never expiring tokens for e.g. a mobile app.
| This is not particularly recommended, so make sure you have appropriate
| systems in place to revoke the token if necessary.
|
*/
'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),
/*
|--------------------------------------------------------------------------
| JWT hashing algorithm
|--------------------------------------------------------------------------
|
| Specify the hashing algorithm that will be used to sign the token.
|
*/
'algo' => env('JWT_ALGO', Tymon\JWTAuth\Providers\JWT\Provider::ALGO_HS256),
/*
|--------------------------------------------------------------------------
| Required Claims
|--------------------------------------------------------------------------
|
| Specify the required claims that must exist in any token.
| A TokenInvalidException will be thrown if any of these claims are not
| present in the payload.
|
*/
'required_claims' => [
'iss',
'iat',
'exp',
'nbf',
'sub',
'jti',
],
/*
|--------------------------------------------------------------------------
| Persistent Claims
|--------------------------------------------------------------------------
|
| Specify the claim keys to be persisted when refreshing a token.
| `sub` and `iat` will automatically be persisted, in
| addition to the these claims.
|
| Note: If a claim does not exist then it will be ignored.
|
*/
'persistent_claims' => [
// 'foo',
// 'bar',
],
/*
|--------------------------------------------------------------------------
| Lock Subject
|--------------------------------------------------------------------------
|
| This will determine whether a `prv` claim is automatically added to
| the token. The purpose of this is to ensure that if you have multiple
| authentication models e.g. `App\User` & `App\OtherPerson`, then we
| should prevent one authentication request from impersonating another,
| if 2 tokens happen to have the same id across the 2 different models.
|
| Under specific circumstances, you may want to disable this behaviour
| e.g. if you only have one authentication model, then you would save
| a little on token size.
|
*/
'lock_subject' => true,
/*
|--------------------------------------------------------------------------
| Leeway
|--------------------------------------------------------------------------
|
| This property gives the jwt timestamp claims some "leeway".
| Meaning that if you have any unavoidable slight clock skew on
| any of your servers then this will afford you some level of cushioning.
|
| This applies to the claims `iat`, `nbf` and `exp`.
|
| Specify in seconds - only if you know you need it.
|
*/
'leeway' => env('JWT_LEEWAY', 0),
/*
|--------------------------------------------------------------------------
| Blacklist Enabled
|--------------------------------------------------------------------------
|
| In order to invalidate tokens, you must have the blacklist enabled.
| If you do not want or need this functionality, then set this to false.
|
*/
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
/*
| -------------------------------------------------------------------------
| Blacklist Grace Period
| -------------------------------------------------------------------------
|
| When multiple concurrent requests are made with the same JWT,
| it is possible that some of them fail, due to token regeneration
| on every request.
|
| Set grace period in seconds to prevent parallel request failure.
|
*/
'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),
/*
|--------------------------------------------------------------------------
| Cookies encryption
|--------------------------------------------------------------------------
|
| By default Laravel encrypt cookies for security reason.
| If you decide to not decrypt cookies, you will have to configure Laravel
| to not encrypt your cookie token by adding its name into the $except
| array available in the middleware "EncryptCookies" provided by Laravel.
| see https://laravel.com/docs/master/responses#cookies-and-encryption
| for details.
|
| Set it to true if you want to decrypt cookies.
|
*/
'decrypt_cookies' => false,
/*
|--------------------------------------------------------------------------
| Providers
|--------------------------------------------------------------------------
|
| Specify the various providers used throughout the package.
|
*/
'providers' => [
/*
|--------------------------------------------------------------------------
| JWT Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to create and decode the tokens.
|
*/
'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class,
/*
|--------------------------------------------------------------------------
| Authentication Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to authenticate users.
|
*/
'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,
/*
|--------------------------------------------------------------------------
| Storage Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to store tokens in the blacklist.
|
*/
'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,
],
];
+132
View File
@@ -0,0 +1,132 @@
<?php
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;
use Monolog\Processor\PsrLogMessageProcessor;
return [
/*
|--------------------------------------------------------------------------
| Default Log Channel
|--------------------------------------------------------------------------
|
| This option defines the default log channel that is utilized to write
| messages to your logs. The value provided here should match one of
| the channels present in the list of "channels" configured below.
|
*/
'default' => env('LOG_CHANNEL', 'stack'),
/*
|--------------------------------------------------------------------------
| Deprecations Log Channel
|--------------------------------------------------------------------------
|
| This option controls the log channel that should be used to log warnings
| regarding deprecated PHP and library features. This allows you to get
| your application ready for upcoming major versions of dependencies.
|
*/
'deprecations' => [
'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
'trace' => env('LOG_DEPRECATIONS_TRACE', false),
],
/*
|--------------------------------------------------------------------------
| Log Channels
|--------------------------------------------------------------------------
|
| Here you may configure the log channels for your application. Laravel
| utilizes the Monolog PHP logging library, which includes a variety
| of powerful log handlers and formatters that you're free to use.
|
| Available Drivers: "single", "daily", "slack", "syslog",
| "errorlog", "monolog", "custom", "stack"
|
*/
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => explode(',', env('LOG_STACK', 'single')),
'ignore_exceptions' => false,
],
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'replace_placeholders' => true,
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'days' => env('LOG_DAILY_DAYS', 14),
'replace_placeholders' => true,
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'),
'emoji' => env('LOG_SLACK_EMOJI', ':boom:'),
'level' => env('LOG_LEVEL', 'critical'),
'replace_placeholders' => true,
],
'papertrail' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class),
'handler_with' => [
'host' => env('PAPERTRAIL_URL'),
'port' => env('PAPERTRAIL_PORT'),
'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'),
],
'processors' => [PsrLogMessageProcessor::class],
],
'stderr' => [
'driver' => 'monolog',
'level' => env('LOG_LEVEL', 'debug'),
'handler' => StreamHandler::class,
'formatter' => env('LOG_STDERR_FORMATTER'),
'with' => [
'stream' => 'php://stderr',
],
'processors' => [PsrLogMessageProcessor::class],
],
'syslog' => [
'driver' => 'syslog',
'level' => env('LOG_LEVEL', 'debug'),
'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER),
'replace_placeholders' => true,
],
'errorlog' => [
'driver' => 'errorlog',
'level' => env('LOG_LEVEL', 'debug'),
'replace_placeholders' => true,
],
'null' => [
'driver' => 'monolog',
'handler' => NullHandler::class,
],
'emergency' => [
'path' => storage_path('logs/laravel.log'),
],
],
];
+116
View File
@@ -0,0 +1,116 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Mailer
|--------------------------------------------------------------------------
|
| This option controls the default mailer that is used to send all email
| messages unless another mailer is explicitly specified when sending
| the message. All additional mailers can be configured within the
| "mailers" array. Examples of each type of mailer are provided.
|
*/
'default' => env('MAIL_MAILER', 'log'),
/*
|--------------------------------------------------------------------------
| Mailer Configurations
|--------------------------------------------------------------------------
|
| Here you may configure all of the mailers used by your application plus
| their respective settings. Several examples have been configured for
| you and you are free to add your own as your application requires.
|
| Laravel supports a variety of mail "transport" drivers that can be used
| when delivering an email. You may specify which one you're using for
| your mailers below. You may also add additional mailers if needed.
|
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
| "postmark", "resend", "log", "array",
| "failover", "roundrobin"
|
*/
'mailers' => [
'smtp' => [
'transport' => 'smtp',
'url' => env('MAIL_URL'),
'host' => env('MAIL_HOST', '127.0.0.1'),
'port' => env('MAIL_PORT', 2525),
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
'timeout' => null,
'local_domain' => env('MAIL_EHLO_DOMAIN'),
],
'ses' => [
'transport' => 'ses',
],
'postmark' => [
'transport' => 'postmark',
// 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
// 'client' => [
// 'timeout' => 5,
// ],
],
'resend' => [
'transport' => 'resend',
],
'sendmail' => [
'transport' => 'sendmail',
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'),
],
'log' => [
'transport' => 'log',
'channel' => env('MAIL_LOG_CHANNEL'),
],
'array' => [
'transport' => 'array',
],
'failover' => [
'transport' => 'failover',
'mailers' => [
'smtp',
'log',
],
],
'roundrobin' => [
'transport' => 'roundrobin',
'mailers' => [
'ses',
'postmark',
],
],
],
/*
|--------------------------------------------------------------------------
| Global "From" Address
|--------------------------------------------------------------------------
|
| You may wish for all emails sent by your application to be sent from
| the same address. Here you may specify a name and address that is
| used globally for all emails that are sent by your application.
|
*/
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
];
+273
View File
@@ -0,0 +1,273 @@
<?php
use Nwidart\Modules\Activators\FileActivator;
use Nwidart\Modules\Providers\ConsoleServiceProvider;
return [
/*
|--------------------------------------------------------------------------
| Module Namespace
|--------------------------------------------------------------------------
|
| Default module namespace.
|
*/
'namespace' => 'Modules',
/*
|--------------------------------------------------------------------------
| Module Stubs
|--------------------------------------------------------------------------
|
| Default module stubs.
|
*/
'stubs' => [
'enabled' => false,
'path' => base_path('storage/stubs'),
'files' => [
'routes/web' => 'routes/web.php',
'routes/api' => 'routes/api.php',
'routes/admin' => 'routes/admin.php',
// 'views/index' => 'resources/views/index.blade.php',
// 'views/master' => 'resources/views/layouts/master.blade.php',
'scaffold/config' => 'config/config.php',
'composer' => 'composer.json',
// 'assets/js/app' => 'resources/assets/js/app.js',
// 'assets/sass/app' => 'resources/assets/sass/app.scss',
// 'vite' => 'vite.config.js',
// 'package' => 'package.json',
],
'replacements' => [
'routes/web' => ['LOWER_NAME', 'STUDLY_NAME', 'MODULE_NAMESPACE', 'CONTROLLER_NAMESPACE'],
'routes/api' => ['LOWER_NAME', 'STUDLY_NAME', 'MODULE_NAMESPACE', 'CONTROLLER_NAMESPACE'],
'routes/admin' => ['LOWER_NAME', 'STUDLY_NAME', 'MODULE_NAMESPACE', 'CONTROLLER_NAMESPACE'],
'vite' => ['LOWER_NAME', 'STUDLY_NAME'],
'json' => ['LOWER_NAME', 'STUDLY_NAME', 'MODULE_NAMESPACE', 'PROVIDER_NAMESPACE'],
'views/index' => ['LOWER_NAME'],
'views/master' => ['LOWER_NAME', 'STUDLY_NAME'],
'scaffold/config' => ['STUDLY_NAME'],
'composer' => [
'LOWER_NAME',
'STUDLY_NAME',
'VENDOR',
'AUTHOR_NAME',
'AUTHOR_EMAIL',
'MODULE_NAMESPACE',
'PROVIDER_NAMESPACE',
'APP_FOLDER_NAME',
],
],
'gitkeep' => true,
],
'paths' => [
/*
|--------------------------------------------------------------------------
| Modules path
|--------------------------------------------------------------------------
|
| This path is used to save the generated module.
| This path will also be added automatically to the list of scanned folders.
|
*/
'modules' => base_path('modules'),
/*
|--------------------------------------------------------------------------
| Modules assets path
|--------------------------------------------------------------------------
|
| Here you may update the modules' assets path.
|
*/
'assets' => public_path('modules'),
/*
|--------------------------------------------------------------------------
| The migrations' path
|--------------------------------------------------------------------------
|
| Where you run the 'module:publish-migration' command, where do you publish the
| the migration files?
|
*/
'migration' => base_path('database/migrations'),
/*
|--------------------------------------------------------------------------
| The app path
|--------------------------------------------------------------------------
|
| app folder name
| for example can change it to 'src' or 'App'
*/
'app_folder' => 'app/',
/*
|--------------------------------------------------------------------------
| Generator path
|--------------------------------------------------------------------------
| Customise the paths where the folders will be generated.
| Setting the generate key to false will not generate that folder
*/
'generator' => [
// app/
'actions' => ['path' => 'app/Actions', 'generate' => false],
'casts' => ['path' => 'app/Casts', 'generate' => false],
'channels' => ['path' => 'app/Broadcasting', 'generate' => false],
'class' => ['path' => 'app/Classes', 'generate' => false],
'command' => ['path' => 'app/Console', 'generate' => false],
'component-class' => ['path' => 'app/View/Components', 'generate' => false],
'emails' => ['path' => 'app/Emails', 'generate' => false],
'event' => ['path' => 'app/Events', 'generate' => false],
'enums' => ['path' => 'app/Enums', 'generate' => false],
'exceptions' => ['path' => 'app/Exceptions', 'generate' => false],
'jobs' => ['path' => 'app/Jobs', 'generate' => false],
'helpers' => ['path' => 'app/Helpers', 'generate' => false],
'interfaces' => ['path' => 'app/Interfaces', 'generate' => false],
'listener' => ['path' => 'app/Listeners', 'generate' => false],
'model' => ['path' => 'app/Models', 'generate' => true],
'notifications' => ['path' => 'app/Notifications', 'generate' => false],
'observer' => ['path' => 'app/Observers', 'generate' => false],
'policies' => ['path' => 'app/Policies', 'generate' => false],
'provider' => ['path' => 'app/Providers', 'generate' => true],
'repository' => ['path' => 'app/Repositories', 'generate' => false],
'resource' => ['path' => 'app/Transformers', 'generate' => false],
'route-provider' => ['path' => 'app/Providers', 'generate' => true],
'rules' => ['path' => 'app/Rules', 'generate' => false],
'services' => ['path' => 'app/Services', 'generate' => true],
'scopes' => ['path' => 'app/Models/Scopes', 'generate' => false],
'traits' => ['path' => 'app/Traits', 'generate' => false],
// app/Http/
'controller' => ['path' => 'app/Controllers', 'generate' => true],
'filter' => ['path' => 'app/Http/Middleware', 'generate' => false],
'request' => ['path' => 'app/Http/Requests', 'generate' => false],
// config/
'config' => ['path' => 'config', 'generate' => true],
// database/
'factory' => ['path' => 'database/factories', 'generate' => true],
'migration' => ['path' => 'database/migrations', 'generate' => true],
'seeder' => ['path' => 'database/seeders', 'generate' => true],
// lang/
'lang' => ['path' => 'lang', 'generate' => false],
// resource/
'assets' => ['path' => 'resources/assets', 'generate' => false],
'component-view' => ['path' => 'resources/views/components', 'generate' => false],
'views' => ['path' => 'resources/views', 'generate' => false],
// routes/
'routes' => ['path' => 'routes', 'generate' => true],
// tests/
'test-feature' => ['path' => 'tests/Feature', 'generate' => false],
'test-unit' => ['path' => 'tests/Unit', 'generate' => false],
],
],
/*
|--------------------------------------------------------------------------
| Package commands
|--------------------------------------------------------------------------
|
| Here you can define which commands will be visible and used in your
| application. You can add your own commands to merge section.
|
*/
'commands' => ConsoleServiceProvider::defaultCommands()
->merge([
// New commands go here
])->toArray(),
/*
|--------------------------------------------------------------------------
| Scan Path
|--------------------------------------------------------------------------
|
| Here you define which folder will be scanned. By default will scan vendor
| directory. This is useful if you host the package in packagist website.
|
*/
'scan' => [
'enabled' => false,
'paths' => [
base_path('vendor/*/*'),
],
],
/*
|--------------------------------------------------------------------------
| Composer File Template
|--------------------------------------------------------------------------
|
| Here is the config for the composer.json file, generated by this package
|
*/
'composer' => [
'vendor' => env('MODULE_VENDOR', 'tensent'),
'author' => [
'name' => env('MODULE_AUTHOR_NAME', 'molong'),
'email' => env('MODULE_AUTHOR_EMAIL', 'molong@tensent.cn'),
],
'composer-output' => false,
],
/*
|--------------------------------------------------------------------------
| Caching
|--------------------------------------------------------------------------
|
| Here is the config for setting up the caching feature.
|
*/
'cache' => [
'enabled' => env('MODULES_CACHE_ENABLED', false),
'driver' => env('MODULES_CACHE_DRIVER', 'file'),
'key' => env('MODULES_CACHE_KEY', 'laravel-modules'),
'lifetime' => env('MODULES_CACHE_LIFETIME', 60),
],
/*
|--------------------------------------------------------------------------
| Choose what laravel-modules will register as custom namespaces.
| Setting one to false will require you to register that part
| in your own Service Provider class.
|--------------------------------------------------------------------------
*/
'register' => [
'translations' => true,
/**
* load files on boot or register method
*/
'files' => 'register',
],
/*
|--------------------------------------------------------------------------
| Activators
|--------------------------------------------------------------------------
|
| You can define new types of activators here, file, database, etc. The only
| required parameter is 'class'.
| The file activator will store the activation status in storage/installed_modules
*/
'activators' => [
'file' => [
'class' => FileActivator::class,
'statuses-file' => base_path('storage/modules_statuses.json'),
'cache-key' => 'activator.installed',
'cache-lifetime' => 604800,
],
'database' => [
'class' => \App\Services\System\ModulesService::class,
'model' => \App\Models\System\Modules::class
]
],
'activator' => 'file',
];
+112
View File
@@ -0,0 +1,112 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Queue Connection Name
|--------------------------------------------------------------------------
|
| Laravel's queue supports a variety of backends via a single, unified
| API, giving you convenient access to each backend using identical
| syntax for each. The default queue connection is defined below.
|
*/
'default' => env('QUEUE_CONNECTION', 'database'),
/*
|--------------------------------------------------------------------------
| Queue Connections
|--------------------------------------------------------------------------
|
| Here you may configure the connection options for every queue backend
| used by your application. An example configuration is provided for
| each backend supported by Laravel. You're also free to add more.
|
| Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null"
|
*/
'connections' => [
'sync' => [
'driver' => 'sync',
],
'database' => [
'driver' => 'database',
'connection' => env('DB_QUEUE_CONNECTION'),
'table' => env('DB_QUEUE_TABLE', 'jobs'),
'queue' => env('DB_QUEUE', 'default'),
'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90),
'after_commit' => false,
],
'beanstalkd' => [
'driver' => 'beanstalkd',
'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'),
'queue' => env('BEANSTALKD_QUEUE', 'default'),
'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90),
'block_for' => 0,
'after_commit' => false,
],
'sqs' => [
'driver' => 'sqs',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
'queue' => env('SQS_QUEUE', 'default'),
'suffix' => env('SQS_SUFFIX'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'after_commit' => false,
],
'redis' => [
'driver' => 'redis',
'connection' => env('REDIS_QUEUE_CONNECTION', 'default'),
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90),
'block_for' => null,
'after_commit' => false,
],
],
/*
|--------------------------------------------------------------------------
| Job Batching
|--------------------------------------------------------------------------
|
| The following options configure the database and table that store job
| batching information. These options can be updated to any database
| connection and table which has been defined by your application.
|
*/
'batching' => [
'database' => env('DB_CONNECTION', 'sqlite'),
'table' => 'job_batches',
],
/*
|--------------------------------------------------------------------------
| Failed Queue Jobs
|--------------------------------------------------------------------------
|
| These options configure the behavior of failed queue job logging so you
| can control how and where failed jobs are stored. Laravel ships with
| support for storing failed jobs in a simple file or in a database.
|
| Supported drivers: "database-uuids", "dynamodb", "file", "null"
|
*/
'failed' => [
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
'database' => env('DB_CONNECTION', 'sqlite'),
'table' => 'failed_jobs',
],
];
+38
View File
@@ -0,0 +1,38 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Third Party Services
|--------------------------------------------------------------------------
|
| This file is for storing the credentials for third party services such
| as Mailgun, Postmark, AWS and more. This file provides the de facto
| location for this type of information, allowing packages to have
| a conventional file to locate the various service credentials.
|
*/
'postmark' => [
'token' => env('POSTMARK_TOKEN'),
],
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],
'resend' => [
'key' => env('RESEND_KEY'),
],
'slack' => [
'notifications' => [
'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
],
],
];
+217
View File
@@ -0,0 +1,217 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Default Session Driver
|--------------------------------------------------------------------------
|
| This option determines the default session driver that is utilized for
| incoming requests. Laravel supports a variety of storage options to
| persist session data. Database storage is a great default choice.
|
| Supported: "file", "cookie", "database", "apc",
| "memcached", "redis", "dynamodb", "array"
|
*/
'driver' => env('SESSION_DRIVER', 'database'),
/*
|--------------------------------------------------------------------------
| Session Lifetime
|--------------------------------------------------------------------------
|
| Here you may specify the number of minutes that you wish the session
| to be allowed to remain idle before it expires. If you want them
| to expire immediately when the browser is closed then you may
| indicate that via the expire_on_close configuration option.
|
*/
'lifetime' => env('SESSION_LIFETIME', 120),
'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false),
/*
|--------------------------------------------------------------------------
| Session Encryption
|--------------------------------------------------------------------------
|
| This option allows you to easily specify that all of your session data
| should be encrypted before it's stored. All encryption is performed
| automatically by Laravel and you may use the session like normal.
|
*/
'encrypt' => env('SESSION_ENCRYPT', false),
/*
|--------------------------------------------------------------------------
| Session File Location
|--------------------------------------------------------------------------
|
| When utilizing the "file" session driver, the session files are placed
| on disk. The default storage location is defined here; however, you
| are free to provide another location where they should be stored.
|
*/
'files' => storage_path('framework/sessions'),
/*
|--------------------------------------------------------------------------
| Session Database Connection
|--------------------------------------------------------------------------
|
| When using the "database" or "redis" session drivers, you may specify a
| connection that should be used to manage these sessions. This should
| correspond to a connection in your database configuration options.
|
*/
'connection' => env('SESSION_CONNECTION'),
/*
|--------------------------------------------------------------------------
| Session Database Table
|--------------------------------------------------------------------------
|
| When using the "database" session driver, you may specify the table to
| be used to store sessions. Of course, a sensible default is defined
| for you; however, you're welcome to change this to another table.
|
*/
'table' => env('SESSION_TABLE', 'sessions'),
/*
|--------------------------------------------------------------------------
| Session Cache Store
|--------------------------------------------------------------------------
|
| When using one of the framework's cache driven session backends, you may
| define the cache store which should be used to store the session data
| between requests. This must match one of your defined cache stores.
|
| Affects: "apc", "dynamodb", "memcached", "redis"
|
*/
'store' => env('SESSION_STORE'),
/*
|--------------------------------------------------------------------------
| Session Sweeping Lottery
|--------------------------------------------------------------------------
|
| Some session drivers must manually sweep their storage location to get
| rid of old sessions from storage. Here are the chances that it will
| happen on a given request. By default, the odds are 2 out of 100.
|
*/
'lottery' => [2, 100],
/*
|--------------------------------------------------------------------------
| Session Cookie Name
|--------------------------------------------------------------------------
|
| Here you may change the name of the session cookie that is created by
| the framework. Typically, you should not need to change this value
| since doing so does not grant a meaningful security improvement.
|
*/
'cookie' => env(
'SESSION_COOKIE',
Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
),
/*
|--------------------------------------------------------------------------
| Session Cookie Path
|--------------------------------------------------------------------------
|
| The session cookie path determines the path for which the cookie will
| be regarded as available. Typically, this will be the root path of
| your application, but you're free to change this when necessary.
|
*/
'path' => env('SESSION_PATH', '/'),
/*
|--------------------------------------------------------------------------
| Session Cookie Domain
|--------------------------------------------------------------------------
|
| This value determines the domain and subdomains the session cookie is
| available to. By default, the cookie will be available to the root
| domain and all subdomains. Typically, this shouldn't be changed.
|
*/
'domain' => env('SESSION_DOMAIN'),
/*
|--------------------------------------------------------------------------
| HTTPS Only Cookies
|--------------------------------------------------------------------------
|
| By setting this option to true, session cookies will only be sent back
| to the server if the browser has a HTTPS connection. This will keep
| the cookie from being sent to you when it can't be done securely.
|
*/
'secure' => env('SESSION_SECURE_COOKIE'),
/*
|--------------------------------------------------------------------------
| HTTP Access Only
|--------------------------------------------------------------------------
|
| Setting this value to true will prevent JavaScript from accessing the
| value of the cookie and the cookie will only be accessible through
| the HTTP protocol. It's unlikely you should disable this option.
|
*/
'http_only' => env('SESSION_HTTP_ONLY', true),
/*
|--------------------------------------------------------------------------
| Same-Site Cookies
|--------------------------------------------------------------------------
|
| This option determines how your cookies behave when cross-site requests
| take place, and can be used to mitigate CSRF attacks. By default, we
| will set this value to "lax" to permit secure cross-site requests.
|
| See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
|
| Supported: "lax", "strict", "none", null
|
*/
'same_site' => env('SESSION_SAME_SITE', 'lax'),
/*
|--------------------------------------------------------------------------
| Partitioned Cookies
|--------------------------------------------------------------------------
|
| Setting this value to true will tie the cookie to the top-level site for
| a cross-site context. Partitioned cookies are accepted by the browser
| when flagged "secure" and the Same-Site attribute is set to "none".
|
*/
'partitioned' => env('SESSION_PARTITIONED_COOKIE', false),
];
+49
View File
@@ -0,0 +1,49 @@
<?php
// +----------------------------------------------------------------------
// | SentCMS [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2024 http://www.tensent.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: molong <molong@tensent.cn> <http://www.tensent.cn>
// +----------------------------------------------------------------------
return [
'wx' => [
'app_id' => 'wx8729bae07a068c87',
'secret' => 'bd76508353688757296ce1e8c1307758',
'token' => '',
],
/**
* OAuth 配置
*
* scopes:公众平台(snsapi_userinfo / snsapi_base),开放平台:snsapi_login
* callbackOAuth授权完成后的回调页地址
*/
'oauth' => [
'scopes' => ['snsapi_userinfo'],
'callback' => '/examples/oauth_callback.php',
],
/**
* 接口请求相关配置,超时时间等,具体可用参数请参考:
* https://github.com/symfony/symfony/blob/5.3/src/Symfony/Contracts/HttpClient/HttpClientInterface.php
*/
'http' => [
'timeout' => 5.0,
// 'base_uri' => 'https://api.weixin.qq.com/', // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri
'retry' => true, // 使用默认重试配置
// 'retry' => [
// // 仅以下状态码重试
// 'status_codes' => [429, 500]
// // 最大重试次数
// 'max_retries' => 3,
// // 请求间隔 (毫秒)
// 'delay' => 1000,
// // 如果设置,每次重试的等待时间都会增加这个系数
// // (例如. 首次:1000ms; 第二次: 3 * 1000ms; etc.)
// 'multiplier' => 3
// ],
],
];
+1
View File
@@ -0,0 +1 @@
*.sqlite*
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
/**
* The current password being used by the factory.
*/
protected static ?string $password;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}
@@ -0,0 +1,57 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('jobs', function (Blueprint $table) {
$table->id();
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
Schema::create('job_batches', function (Blueprint $table) {
$table->string('id')->primary();
$table->string('name');
$table->integer('total_jobs');
$table->integer('pending_jobs');
$table->integer('failed_jobs');
$table->longText('failed_job_ids');
$table->mediumText('options')->nullable();
$table->integer('cancelled_at')->nullable();
$table->integer('created_at');
$table->integer('finished_at')->nullable();
});
Schema::create('failed_jobs', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('jobs');
Schema::dropIfExists('job_batches');
Schema::dropIfExists('failed_jobs');
}
};
@@ -0,0 +1,132 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void {
Schema::create('auth_admins', function (Blueprint $table) {
$table->id('uid')->unique()->comment('用户ID');
$table->string('username')->unique()->comment('用户名');
$table->string('nickname')->nullable()->comment('昵称');
$table->string('email')->nullable()->comment('邮箱');
$table->string('mobile')->nullable()->comment('手机号码');
$table->string('password')->comment('密码');
$table->string('avatar')->nullable()->comment('头像');
$table->unsignedBigInteger('department_id')->nullable()->comment('部门id');
$table->unsignedTinyInteger('gender')->default(1)->comment('性别 1男 2女');
$table->text('remark')->nullable()->comment('备注,个性签名');
$table->unsignedTinyInteger('status')->default(1)->comment('状态 1正常 2禁用');
$table->string('last_login_ip')->nullable()->comment('最后登录ip');
$table->timestamp('last_login_at')->nullable()->comment('最后登录时间');
$table->timestamp('email_verified_at')->nullable()->comment('邮箱验证时间');
$table->timestamp('mobile_verified_at')->nullable()->comment('手机验证时间');
$table->timestamp('created_at')->nullable()->comment('创建时间');
$table->timestamp('updated_at')->nullable()->comment('更新时间');
$table->timestamp('deleted_at')->nullable()->comment('删除时间');
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('后台管理员表');
});
Schema::create('auth_admins_roles', function (Blueprint $table) {
$table->unsignedBigInteger('role_id')->comment('角色id');
$table->unsignedBigInteger('uid')->comment('用户id');
$table->primary(['role_id', 'uid']);
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('后台管理员角色对应表');
});
Schema::create('auth_roles', function (Blueprint $table) {
$table->id()->uniqid()->comment('主键id');
$table->string('title', 50)->comment('角色名称');
$table->string('name', 50)->comment('角色标识');
$table->string('data_range')->default('all')->comment('数据范围 all全部 department本部门 department_sub本部门及以下 self仅自己');
$table->integer('sort')->default(0)->comment('排序');
$table->unsignedTinyInteger('status')->default(1)->comment('状态 1正常 2禁用');
$table->string('description', 255)->nullable()->comment('角色描述');
$table->timestamp('created_at')->nullable()->comment('创建时间');
$table->timestamp('updated_at')->nullable()->comment('更新时间');
$table->timestamp('deleted_at')->nullable()->comment('删除时间');
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('后台管理员角色表');
});
Schema::create('auth_roles_permissions', function (Blueprint $table) {
$table->bigInteger('role_id')->unsigned()->comment('角色ID');
$table->bigInteger('permission_id')->unsigned()->comment('权限ID');
$table->primary(['role_id', 'permission_id']);
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('后台管理员角色权限表');
});
Schema::create('auth_permissions', function (Blueprint $table) {
$table->id()->uniqid()->comment('主键id');
$table->integer('parent_id')->default(0)->comment('父级id');
$table->string('title', 50)->comment('权限标题');
$table->string('name', 50)->comment('权限名称');
$table->string('icon', 50)->nullable()->comment('图标');
$table->string('path', 100)->nullable()->comment('权限链接');
$table->string('redirect', 100)->nullable()->comment('重定向');
$table->string('component', 200)->nullable()->comment('组件');
$table->string('type', 20)->default('menu')->comment('类型 menu菜单 button按钮 link外链 iframeIframe');
$table->string('color', 20)->nullable()->comment('颜色');
$table->tinyInteger('hidden')->default(0)->comment('是否显示 1显示 2不显示');
$table->tinyInteger('hiddenBreadcrumb')->default(0)->comment('是否显示面包屑 1显示 2不显示');
$table->tinyInteger('affix')->default(0)->comment('是否固定 1固定 2不固定');
$table->tinyInteger('fullpage')->default(0)->comment('是否全屏 1全屏 2不全屏');
$table->text('apiList')->nullable()->comment('接口列表');
$table->integer('sort')->default(0)->comment('排序');
$table->tinyInteger('status')->default(1)->comment('状态 1正常 2禁用');
$table->timestamp('created_at')->nullable()->comment('创建时间');
$table->timestamp('updated_at')->nullable()->comment('更新时间');
$table->timestamp('deleted_at')->nullable()->comment('删除时间');
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('权限表');
});
Schema::create('auth_departments', function (Blueprint $table) {
$table->id()->uniqid()->comment('主键id');
$table->string('parent_id')->default(0)->comment('父级id');
$table->string('title')->comment('部门名称');
$table->string('name')->comment('部门标识');
$table->string('sort')->default(0)->comment('排序');
$table->tinyInteger('status')->default(1)->comment('状态 1正常 2禁用');
$table->string('description', 255)->nullable()->comment('部门描述');
$table->timestamp('created_at')->nullable()->comment('创建时间');
$table->timestamp('updated_at')->nullable()->comment('更新时间');
$table->timestamp('deleted_at')->nullable()->comment('删除时间');
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('部门表');
});
}
/**
* Reverse the migrations.
*/
public function down(): void {
Schema::dropIfExists('auth_admins');
Schema::dropIfExists('auth_admins_roles');
Schema::dropIfExists('auth_roles');
Schema::dropIfExists('auth_roles_permissions');
Schema::dropIfExists('auth_permissions');
Schema::dropIfExists('auth_departments');
}
};
@@ -0,0 +1,203 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void {
Schema::create('system_logs', function (Blueprint $table) {
$table->id()->uniqid()->comment('主键id');
$table->unsignedBigInteger('user_id')->comment('用户ID');
$table->string('title')->comment('日志标题');
$table->string('name')->comment('日志名称');
$table->string('url')->comment('请求地址');
$table->string('method')->comment('请求方法');
$table->string('client_ip')->nullable()->comment('请求IP');
$table->text('data')->nullable()->comment('请求数据');
$table->text('remark')->nullable()->comment('备注');
$table->text('browser')->nullable()->comment('浏览器');
$table->tinyInteger('status')->default(1)->comment('状态码');
$table->timestamp('created_at')->nullable()->comment('创建时间');
$table->timestamp('updated_at')->nullable()->comment('更新时间');
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('系统日志表');
});
Schema::create('system_configs', function (Blueprint $table) {
$table->id()->uniqid()->comment('主键id');
$table->string('title')->comment('配置标题');
$table->string('name')->comment('配置名称');
$table->string('values')->nullable()->comment('配置值');
$table->string('type')->comment('配置类型');
$table->string('group')->comment('配置分组');
$table->jsonb('option')->nullable()->comment('配置选项');
$table->string('remark')->nullable()->comment('配置备注');
$table->string('sort')->default(0)->comment('排序');
$table->tinyInteger('status')->default(1)->comment('状态');
$table->timestamp('created_at')->nullable()->comment('创建时间');
$table->timestamp('updated_at')->nullable()->comment('更新时间');
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('系统配置表');
});
Schema::create('system_dicts', function (Blueprint $table) {
$table->id()->uniqid()->comment('主键id');
$table->string('title')->comment('字典标题');
$table->string('values')->comment('字典值');
$table->string('group_id')->comment('字典分组');
$table->string('group_name')->comment('字典分组标识');
$table->string('sort')->default(0)->comment('排序');
$table->tinyInteger('status')->default(1)->comment('状态');
$table->timestamp('created_at')->nullable()->comment('创建时间');
$table->timestamp('updated_at')->nullable()->comment('更新时间');
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('系统字典表');
});
Schema::create('system_dict_groups', function (Blueprint $table) {
$table->id()->uniqid()->comment('主键id');
$table->string('parent_id')->default(0)->comment('父级id');
$table->string('title')->comment('字典分组标题');
$table->string('name')->comment('字典分组名称');
$table->string('sort')->default(0)->comment('排序');
$table->tinyInteger('status')->default(1)->comment('状态');
$table->timestamp('created_at')->nullable()->comment('创建时间');
$table->timestamp('updated_at')->nullable()->comment('更新时间');
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('系统字典分组表');
});
Schema::create('system_areas', function (Blueprint $table) {
$table->id()->uniqid()->comment('主键id');
$table->string('code')->comment('城市编码');
$table->string('parent_code')->default(0)->comment('父级id');
$table->string('title')->comment('城市名称');
$table->tinyInteger('status')->default(1)->comment('状态');
$table->timestamp('created_at')->nullable()->comment('创建时间');
$table->timestamp('updated_at')->nullable()->comment('更新时间');
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('系统城市数据表');
});
Schema::create('system_messages', function (Blueprint $table) {
$table->id()->uniqid()->comment('主键id');
$table->unsignedBigInteger('send_uid')->comment('发送人uid');
$table->string('receive_uid')->comment('接受人uid');
$table->string('receive_group')->nullable()->comment('接受组');
$table->string('type')->default(1)->comment('消息类型 1系统消息 2用户消息');
$table->string('title')->comment('消息标题');
$table->text('content')->comment('消息内容');
$table->text('params')->nullable()->comment('消息参数');
$table->string('url')->nullable()->comment('消息链接');
$table->string('status')->default(1)->comment('状态 1未读 2已读');
$table->timestamp('created_at')->nullable()->comment('创建时间');
$table->timestamp('updated_at')->nullable()->comment('更新时间');
$table->timestamp('read_at')->nullable()->comment('已读时间');
$table->timestamp('deleted_at')->nullable()->comment('删除时间');
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('系统消息表');
});
Schema::create('system_tasks', function (Blueprint $table) {
$table->id()->uniqid()->comment('主键id');
$table->unsignedBigInteger('user_id')->default(0)->comment('用户id');
$table->string('title')->comment('任务名称');
$table->string('file')->comment('文件路径');
$table->string('type')->default('export')->comment('任务类型 import export');
$table->string('status')->default(1)->comment('状态 1正常 2禁用');
$table->timestamp('created_at')->nullable()->comment('创建时间');
$table->timestamp('updated_at')->nullable()->comment('更新时间');
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('系统excel任务表');
});
Schema::create('system_crontabs', function (Blueprint $table) {
$table->id()->uniqid()->comment('主键id');
$table->string('title')->comment('定时任务标题');
$table->string('command')->comment('定时任务命令');
$table->string('expression')->comment('定时任务表达式');
$table->string('status')->default(1)->comment('状态 1正常 2禁用');
$table->timestamp('created_at')->nullable()->comment('创建时间');
$table->timestamp('updated_at')->nullable()->comment('更新时间');
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('系统定时任务表');
});
Schema::create('system_client', function (Blueprint $table) {
$table->id()->uniqid()->comment('主键id');
$table->string('title')->comment('客户端名称');
$table->string('app_id')->comment('客户端ID');
$table->string('secret')->comment('客户端密钥');
$table->string('redirect')->comment('回调地址');
$table->string('status')->default(1)->comment('状态 1正常 2禁用');
$table->timestamp('created_at')->nullable()->comment('创建时间');
$table->timestamp('updated_at')->nullable()->comment('更新时间');
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('系统客户端表');
});
Schema::create('system_client_menu', function (Blueprint $table) {
$table->id()->uniqid()->comment('主键id');
$table->string('client_id')->comment('客户端ID');
$table->unsignedBigInteger('parent_id')->default(0)->comment('父级id');
$table->string('position')->comment('菜单位置');
$table->string('name')->nullable()->comment('菜单名称标识');
$table->string('title')->comment('菜单标题');
$table->string('url')->comment('菜单链接');
$table->string('cover')->nullable()->comment('菜单图片');
$table->string('icon')->nullable()->comment('菜单图标');
$table->string('sort')->default(0)->comment('排序');
$table->tinyInteger('is_show')->default(1)->comment('是否显示 1显示 2不显示');
$table->tinyInteger('is_blank')->default(1)->comment('是否新窗口打开 1是 2否');
$table->string('status')->default(1)->comment('状态 1正常 2禁用');
$table->timestamp('created_at')->nullable()->comment('创建时间');
$table->timestamp('updated_at')->nullable()->comment('更新时间');
$table->engine = 'InnoDB';
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_unicode_ci';
$table->comment('系统客户端菜单表');
});
}
/**
* Reverse the migrations.
*/
public function down(): void {
Schema::dropIfExists('system_logs');
Schema::dropIfExists('system_configs');
Schema::dropIfExists('system_dicts');
Schema::dropIfExists('system_dict_groups');
Schema::dropIfExists('system_areas');
Schema::dropIfExists('system_messages');
Schema::dropIfExists('system_tasks');
Schema::dropIfExists('system_crontabs');
Schema::dropIfExists('system_client');
Schema::dropIfExists('system_client_menu');
}
};
@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void {
Schema::create('modules', function (Blueprint $table) {
$table->id();
$table->string('title', 50)->nullable()->comment('模块标题');
$table->string('name')->unique()->comment('模块标识');
$table->text('description')->nullable()->comment('模块描述');
$table->boolean('status')->comment('模块状态');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void {
Schema::dropIfExists('modules');
}
};
+64
View File
@@ -0,0 +1,64 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use App\Services\Auth\MenuService;
class AdminSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void {
DB::table('auth_admins')->insert([
'username' => 'admin',
'nickname' => '超级管理员',
'email' => 'admin@admin.com',
'mobile' => '13888888888',
'password' => Hash::make('admin888'),
'department_id' => 1,
'status' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
DB::table('auth_departments')->insert([
'title' => '总部',
'name' => 'root',
'parent_id' => 0,
'sort' => 0,
'description' => '总部',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
DB::table('auth_roles')->insert([
'title' => '超级管理员',
'name' => 'admin',
'description' => '超级管理员',
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
app(MenuService::class)->importMenu($this->getPermissions(), 0);
}
public function getPermissions(){
$permissions = [
['title' => '首页', 'name' => 'home', 'path' => '/home', 'component' => '', 'type' => 'menu', 'sort' => 0, 'children' => [
['title' => '仪表盘', 'name' => 'dashboard', 'path' => '/dashboard', 'component' => 'home', 'type' => 'menu', 'affix' => 1],
['title' => '个人中心', 'name' => 'ucenter', 'path' => '/ucenter', 'component' => 'ucenter', 'type' => 'menu'],
]],
['title' => '权限', 'name' => 'auth', 'path' => '/auth', 'component' => '', 'type' => 'menu', 'sort' => 99, 'children' => [
['title' => '用户管理', 'name' => 'auth.user', 'path' => '/auth/user', 'component' => 'auth/user', 'type' => 'menu'],
['title' => '角色管理', 'name' => 'auth.role', 'path' => '/auth/role', 'component' => 'auth/role', 'type' => 'menu'],
['title' => '权限管理', 'name' => 'auth.permission', 'path' => '/auth/permission', 'component' => 'auth/permission', 'type' => 'menu'],
['title' => '部门管理', 'name' => 'auth.department', 'path' => '/auth/department', 'component' => 'auth/department', 'type' => 'menu'],
]]
];
return $permissions;
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use App\Models\System\Area;
class CitySeeder extends Seeder{
/**
* Run the database seeds.
*/
public function run(): void {
$area = json_decode(file_get_contents(database_path('seeders/area.json')), true);
$data = [];
foreach ($area as $value) {
$value['created_at'] = date('Y-m-d H:i:s');
$value['updated_at'] = date('Y-m-d H:i:s');
$data[] = $value;
}
Area::insert($data);
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace Database\Seeders;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder {
/**
* Seed the application's database.
*/
public function run(): void {
$this->call([
AdminSeeder::class,
SystemSeeder::class,
CitySeeder::class
]);
}
}
+207
View File
@@ -0,0 +1,207 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use App\Services\Auth\MenuService;
class SystemSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void {
$this->insertSettingDict();
$this->insertDataAuthDict();
$this->insertAdsDict();
$this->addModelFieldTypeDict();
$this->insertClientMenuPosition();
$this->insertPermissions();
}
public function insertSettingDict(){
$group_id = DB::table('system_dict_groups')->insertGetId([
'parent_id' => 0,
'title' => '配置分组',
'name' => 'setting_group',
'status' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
if($group_id){
$dict = DB::table('system_dicts')->insert([
['group_id' => $group_id, 'group_name' => 'setting_group', 'title' => '基础配置', 'values' => 'base', 'sort' => 1, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')],
['group_id' => $group_id, 'group_name' => 'setting_group', 'title' => '上传配置', 'values' => 'upload', 'sort' => 1, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')],
]);
DB::table('system_configs')->insert([
['name' => 'system_name', 'title' => '系统名称', 'values' => 'SentOS', 'type' => 'string', 'group' => 'base', 'sort' => 1, 'status' => 1, 'remark' => '系统名称', 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')],
['name' => 'system_logo', 'title' => '系统Logo', 'values' => '', 'type' => 'image', 'group' => 'base', 'sort' => 2, 'status' => 1, 'remark' => '系统Logo', 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')],
['name' => 'system_email', 'title' => '邮箱', 'values' => '', 'type' => 'string', 'group' => 'base', 'sort' => 6, 'status' => 1, 'remark' => '邮箱', 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')],
['name' => 'system_copyright', 'title' => '版权信息', 'values' => 'Copyright © 2019-2024 SentCMS All Rights Reserved.', 'type' => 'string', 'group' => 'base', 'sort' => 3, 'status' => 1, 'remark' => '版权信息', 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')],
['name' => 'system_icp', 'title' => '备案信息', 'values' => '', 'type' => 'string', 'group' =>'base', 'sort' => 4, 'status' => 1, 'remark' => '备案信息', 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')],
['name' => 'upload_way', 'title' => '上传引擎', 'values' => 'public', 'type' => 'radio', 'group' =>'upload', 'sort' => 1, 'status' => 1, 'remark' => '上传引擎', 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')]
]);
}
}
public function insertDataAuthDict(){
$group_id = DB::table('system_dict_groups')->insertGetId([
'parent_id' => 0,
'title' => '数据权限分组',
'name' => 'data_auth',
'sort' => 1,
'status' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
if($group_id){
DB::table('system_dicts')->insert([
[
'group_id' => $group_id,
'group_name' => 'data_auth',
'title' => '全部数据',
'values' => 'all',
'sort' => 1,
'status' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
],
[
'group_id' => $group_id,
'group_name' => 'data_auth',
'title' => '本部门数据',
'values' => 'department',
'sort' => 2,
'status' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
],
[
'group_id' => $group_id,
'group_name' => 'data_auth',
'title' => '本部门及以下数据',
'values' => 'department_sub',
'sort' => 3,
'status' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
],
[
'group_id' => $group_id,
'group_name' => 'data_auth',
'title' => '仅自己数据',
'values' => 'self',
'sort' => 4,
'status' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]
]);
}
}
public function insertAdsDict(){
$group_id = DB::table('system_dict_groups')->insertGetId([
'parent_id' => 0,
'title' => '广告位',
'name' => 'ads_places',
'sort' => 1,
'status' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
if($group_id){
DB::table('system_dicts')->insert([
[
'group_id' => $group_id,
'group_name' => 'ads_places',
'title' => '首页轮播',
'values' => 'index_banner',
'sort' => 2,
'status' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]
]);
}
}
public function insertClientMenuPosition(){
$group_id = DB::table('system_dict_groups')->insertGetId([
'parent_id' => 0,
'title' => '菜单位置',
'name' => 'client_menu_position',
'sort' => 3,
'status' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
if($group_id){
DB::table('system_dicts')->insert([
[
'group_id' => $group_id,
'group_name' => 'client_menu_position',
'title' => '顶部菜单',
'values' => 'top',
'sort' => 1,
'status' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]
]);
}
}
/**
* @title 添加字典
*
* @return void
*/
public function addModelFieldTypeDict(){
$group_id = DB::table('system_dict_groups')->insertGetId([
'parent_id' => 0,
'title' => '字段类型',
'name' => 'field_type',
'status' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
if($group_id){
$dict = DB::table('system_dicts')->insert([
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '单行文本', 'values' => 'string', 'sort' =>1, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '多行文本', 'values' => 'text', 'sort' => 2, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '单选按钮', 'values' => 'radio', 'sort' => 3, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '多选按钮', 'values' => 'checkbox', 'sort' => 4, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '下拉列表', 'values' => 'select', 'sort' => 5, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '字典选择', 'values' => 'sSelect', 'sort' => 5, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' =>date('Y-m-d H:i:s') ],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '树形选择', 'values' => 'sSelectTree', 'sort' => 6, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '日期', 'values' => 'date', 'sort' => 6, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '日期时间', 'values' => 'datetime', 'sort' => 7, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '文件', 'values' => 'file', 'sort' => 8, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '图片', 'values' => 'image', 'sort' => 9, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '多图', 'values' => 'images', 'sort' => 10, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '富文本', 'values' => 'editor', 'sort' => 10, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '视频', 'values' => 'video', 'sort' => 10, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '音频', 'values' => 'audio', 'sort' => 10, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')],
['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '数字', 'values' => 'number', 'sort' => 3, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')]
]);
}
}
public function insertPermissions(){
$permissions = [
['title' => '系统', 'name' => 'system', 'path' => '/system', 'component' => '', 'type' => 'menu', 'sort' => 100, 'children' => [
['title' => '系统设置', 'name' => 'system.setting', 'path' => '/system/setting', 'component' => 'system/setting', 'type' => 'menu'],
['title' => '城市数据', 'name' => 'system.area', 'path' => '/system/area', 'component' => 'system/area', 'type' => 'menu'],
['title' => '字典管理', 'name' => 'system.dic', 'path' => '/system/dic', 'component' => 'system/dic', 'type' => 'menu'],
['title' => '客户端管理', 'name' => 'system.client', 'path' => '/system/client', 'component' => 'system/client', 'type' => 'menu'],
['title' => '模块管理', 'name' => 'system.modules', 'path' => '/system/modules', 'component' => 'system/modules', 'type' => 'menu'],
['title' => '系统日志', 'name' => 'system.log', 'path' => '/system/log', 'component' => 'system/log', 'type' => 'menu'],
['title' => '定时任务', 'name' => 'system.crontab', 'path' => '/system/crontab', 'component' => 'system/crontab', 'type' => 'menu'],
]]
];
app(MenuService::class)->importMenu($permissions, 0);
}
}
File diff suppressed because one or more lines are too long
+960
View File
@@ -0,0 +1,960 @@
# 记账功能模块文档
## 概述
记账功能模块提供了完整的多用户多家庭记账功能,支持:
- 多家庭管理(每个用户可以加入多个家庭)
- 家庭成员管理(owner/admin/member角色)
- 收支记录管理
- 账户管理
- 数据权限控制
## 模块架构
本模块采用Laravel模块化架构,所有代码位于 `modules/Account` 目录下:
```
modules/Account/
├── app/
│ ├── Controllers/
│ │ ├── Api/ # 前端API控制器
│ │ └── Admin/ # 后台管理控制器
│ ├── Models/ # 数据模型
│ ├── Providers/ # 服务提供者
│ └── Services/ # 业务逻辑服务层
├── database/
│ └── migrations/ # 数据库迁移文件
├── routes/
│ ├── api.php # 前端API路由
│ └── admin.php # 后台管理路由
└── module.json # 模块配置文件
```
### 命名空间
- 模型:`Modules\Account\Models\`
- 控制器:`Modules\Account\Controllers\Api\``Modules\Account\Controllers\Admin\`
- 服务:`Modules\Account\Services\`
- 路由:自动加载,无需手动配置
### 路由说明
模块路由已自动注册,无需在主路由文件中配置:
- 前端API路由位于 `modules/Account/routes/api.php`
- 后台管理路由位于 `modules/Account/routes/admin.php`
所有路由由模块的 RouteServiceProvider 自动加载。
## 数据库表结构
### 1. account_families(家庭表)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 主键ID |
| name | varchar(50) | 家庭名称 |
| description | text | 家庭描述 |
| avatar | varchar(255) | 家庭头像 |
| created_at | datetime | 创建时间 |
| updated_at | datetime | 更新时间 |
| deleted_at | datetime | 删除时间(软删除) |
### 2. account_family_members(家庭成员关系表)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 主键ID |
| family_id | bigint | 家庭ID |
| user_id | bigint | 用户ID |
| role | varchar(20) | 角色:owner-拥有者,admin-管理员,member-成员 |
| created_at | datetime | 创建时间 |
| updated_at | datetime | 更新时间 |
| deleted_at | datetime | 删除时间(软删除) |
**唯一索引**: `family_id` + `user_id`(防止重复加入)
### 3. account_records(记账记录表)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 主键ID |
| user_id | bigint | 用户ID |
| family_id | bigint | 家庭ID |
| member_id | bigint | 成员ID |
| account_id | bigint | 账户ID |
| type | varchar(20) | 类型:income-收入,expense-支出 |
| amount | decimal(10,2) | 金额 |
| category | varchar(50) | 分类 |
| date | date | 日期 |
| time | varchar(10) | 时间 |
| remark | text | 备注 |
| created_at | datetime | 创建时间 |
| updated_at | datetime | 更新时间 |
| deleted_at | datetime | 删除时间(软删除) |
### 2. account_members(家庭成员表)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 主键ID |
| user_id | bigint | 用户ID |
| family_id | bigint | 家庭ID |
| name | varchar(50) | 成员名称 |
| avatar | varchar(255) | 头像 |
| created_at | datetime | 创建时间 |
| updated_at | datetime | 更新时间 |
| deleted_at | datetime | 删除时间(软删除) |
### 3. accounts(账户表)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 主键ID |
| user_id | bigint | 用户ID |
| family_id | bigint | 家庭ID |
| name | varchar(50) | 账户名称 |
| type | varchar(20) | 账户类型:cash-现金,bank-银行卡,alipay-支付宝,wechat-微信 |
| balance | decimal(10,2) | 余额 |
| icon | varchar(255) | 图标 |
| created_at | datetime | 创建时间 |
| updated_at | datetime | 更新时间 |
| deleted_at | datetime | 删除时间(软删除) |
## 前端API接口
### 基础URL
`/api/account`
### 记账记录接口
#### 1. 获取记账记录列表
- **路径**: `GET /api/account/records`
- **认证**: 需要
- **参数**:
- `family_id` (可选): 家庭ID,不传则返回当前用户所有家庭的记录
- `member_id` (可选): 成员ID
- `account_id` (可选): 账户ID
- `type` (可选): 类型(income/expense
- `start_date` (可选): 开始日期
- `end_date` (可选): 结束日期
- `page` (可选): 页码
- `limit` (可选): 每页数量,默认30
- **响应**:
```json
{
"code": 1,
"message": "success",
"data": {
"list": [...],
"total": 100
}
}
```
#### 2. 获取记账记录详情
- **路径**: `GET /api/account/records/{id}`
- **认证**: 需要
- **响应**: 单条记录详情
#### 3. 创建记账记录
- **路径**: `POST /api/account/records`
- **认证**: 需要
- **参数**:
- `member_id` (必需): 成员ID
- `account_id` (必需): 账户ID
- `type` (必需): 类型(income/expense
- `amount` (必需): 金额
- `category` (必需): 分类
- `date` (必需): 日期
- `time` (必需): 时间
- `remark` (可选): 备注
- **响应**: 创建的记录
#### 4. 更新记账记录
- **路径**: `PUT /api/account/records/{id}`
- **认证**: 需要
- **参数**: 同创建接口(所有字段可选)
- **响应**: 更新后的记录
#### 5. 删除记账记录
- **路径**: `DELETE /api/account/records/{id}`
- **认证**: 需要
- **响应**: 删除成功
#### 6. 获取统计数据
- **路径**: `GET /api/account/records/statistics`
- **认证**: 需要
- **参数**:
- `start_date` (可选): 开始日期
- `end_date` (可选): 结束日期
- **响应**:
```json
{
"code": 1,
"message": "success",
"data": {
"total_income": 10000.00,
"total_expense": 5000.00,
"balance": 5000.00,
"category_stats": [...],
"date_stats": [...]
}
}
```
### 家庭管理接口
#### 1. 获取用户的家庭列表
- **路径**: `GET /api/account/families`
- **认证**: 需要
- **响应**:
```json
{
"code": 1,
"message": "success",
"data": {
"list": [
{
"id": 1,
"name": "我的家",
"description": "温馨的家",
"avatar": "avatar.jpg",
"user_role": "owner",
"is_owner": true,
"can_manage": true,
"family_members": [...],
"created_at": "2025-01-01 00:00:00"
}
],
"total": 1
}
}
```
#### 2. 获取家庭详情
- **路径**: `GET /api/account/families/{id}`
- **认证**: 需要
- **权限**: 用户必须属于该家庭
- **响应**: 家庭详情(包含成员、账户、记录信息)
#### 3. 创建家庭
- **路径**: `POST /api/account/families`
- **认证**: 需要
- **参数**:
- `name` (必需): 家庭名称(最多50字符)
- `description` (可选): 家庭描述(最多255字符)
- `avatar` (可选): 家庭头像
- **响应**: 创建的家庭(创建者自动成为owner)
#### 4. 更新家庭
- **路径**: `PUT /api/account/families/{id}`
- **认证**: 需要
- **权限**: owner或admin
- **参数**: 同创建接口(所有字段可选)
- **响应**: 更新后的家庭
#### 5. 删除家庭
- **路径**: `DELETE /api/account/families/{id}`
- **认证**: 需要
- **权限**: 仅owner
- **限制**: 家庭下不能有关联账户
- **响应**: 删除成功
#### 6. 邀请成员加入家庭
- **路径**: `POST /api/account/families/{familyId}/invite`
- **认证**: 需要
- **权限**: owner或admin
- **参数**:
- `user_id` (必需): 被邀请用户ID
- **限制**: 用户不能已经在家庭中
- **响应**: 邀请成功
#### 7. 移除家庭成员
- **路径**: `DELETE /api/account/families/{familyId}/members/{memberId}`
- **认证**: 需要
- **权限**: owner或admin
- **限制**: 不能移除owner
- **响应**: 移除成功
#### 8. 更新成员角色
- **路径**: `PUT /api/account/families/{familyId}/members/{memberId}/role`
- **认证**: 需要
- **权限**: 仅owner
- **参数**:
- `role` (必需): 新角色(owner/admin/member
- **限制**: 不能修改owner的角色
- **响应**: 更新成功
#### 9. 退出家庭
- **路径**: `POST /api/account/families/{id}/leave`
- **认证**: 需要
- **限制**: owner不能退出,只能删除家庭
- **响应**: 退出成功
### 家庭成员接口
#### 1. 获取成员列表
- **路径**: `GET /api/account/members`
- **认证**: 需要
- **参数**:
- `family_id` (可选): 家庭ID,不传则返回当前用户所有家庭的成员
- **响应**:
```json
{
"code": 1,
"message": "success",
"data": {
"list": [...],
"total": 10
}
}
```
#### 2. 获取成员详情
- **路径**: `GET /api/account/members/{id}`
- **认证**: 需要
- **响应**: 成员详情
#### 3. 创建成员
- **路径**: `POST /api/account/members`
- **认证**: 需要
- **参数**:
- `name` (必需): 成员名称
- `avatar` (可选): 头像
- **响应**: 创建的成员
#### 4. 更新成员
- **路径**: `PUT /api/account/members/{id}`
- **认证**: 需要
- **参数**: 同创建接口(所有字段可选)
- **响应**: 更新后的成员
#### 5. 删除成员
- **路径**: `DELETE /api/account/members/{id}`
- **认证**: 需要
- **响应**: 删除成功
### 账户管理接口
#### 1. 获取账户列表
- **路径**: `GET /api/account/accounts`
- **认证**: 需要
- **参数**:
- `family_id` (可选): 家庭ID,不传则返回当前用户所有家庭的账户
- **响应**:
```json
{
"code": 1,
"message": "success",
"data": {
"list": [...],
"total": 5,
"total_balance": 10000.00
}
}
```
#### 2. 获取账户详情
- **路径**: `GET /api/account/accounts/{id}`
- **认证**: 需要
- **响应**: 账户详情
#### 3. 创建账户
- **路径**: `POST /api/account/accounts`
- **认证**: 需要
- **参数**:
- `name` (必需): 账户名称
- `type` (必需): 账户类型(cash/bank/alipay/wechat
- `balance` (可选): 初始余额
- `icon` (可选): 图标
- **响应**: 创建的账户
#### 4. 更新账户
- **路径**: `PUT /api/account/accounts/{id}`
- **认证**: 需要
- **参数**: 同创建接口(所有字段可选)
- **响应**: 更新后的账户
#### 5. 删除账户
- **路径**: `DELETE /api/account/accounts/{id}`
- **认证**: 需要
- **响应**: 删除成功
#### 6. 重新计算账户余额
- **路径**: `POST /api/account/accounts/{id}/recalculate`
- **认证**: 需要
- **响应**: 更新后的账户
## 后台管理接口
### 基础URL
`/admin/account`
### 记账记录管理
#### 1. 获取记账记录列表
- **路径**: `GET /admin/account/records/index`
- **认证**: 需要
- **参数**: 同前端API,额外支持
- `user_id` (可选): 用户ID
- **响应**: 记录列表(包含用户、成员、账户信息)
#### 2. 获取记账记录详情
- **路径**: `GET /admin/account/records/show`
- **认证**: 需要
- **响应**: 记录详情
#### 3. 删除记账记录
- **路径**: `DELETE /admin/account/records/delete`
- **认证**: 需要
- **参数**: `id` (必需): 记录ID
- **响应**: 删除成功(软删除)
#### 4. 恢复记账记录
- **路径**: `PUT /admin/account/records/restore`
- **认证**: 需要
- **参数**: `id` (必需): 记录ID
- **响应**: 恢复成功
#### 5. 获取统计数据
- **路径**: `GET /admin/account/records/statistics`
- **认证**: 需要
- **参数**:
- `user_id` (可选): 用户ID
- `start_date` (可选): 开始日期
- `end_date` (可选): 结束日期
- **响应**:
```json
{
"code": 1,
"message": "success",
"data": {
"total_income": 10000.00,
"total_expense": 5000.00,
"balance": 5000.00,
"user_stats": [...],
"category_stats": [...],
"total_records": 100
}
}
```
### 家庭成员管理
#### 1. 获取成员列表
- **路径**: `GET /admin/account/members/index`
- **认证**: 需要
- **参数**:
- `user_id` (可选): 用户ID
- **响应**: 成员列表(包含统计信息)
#### 2. 获取成员详情
- **路径**: `GET /admin/account/members/show`
- **认证**: 需要
- **参数**: `id` (必需): 成员ID
- **响应**: 成员详情(包含关联记录)
#### 3. 创建成员
- **路径**: `POST /admin/account/members/add`
- **认证**: 需要
- **参数**:
- `user_id` (必需): 用户ID
- `name` (必需): 成员名称
- `avatar` (可选): 头像
- **响应**: 创建的成员
#### 4. 更新成员
- **路径**: `PUT /admin/account/members/edit`
- **认证**: 需要
- **参数**:
- `id` (必需): 成员ID
- 其他字段同创建接口
- **响应**: 更新后的成员
#### 5. 删除成员
- **路径**: `DELETE /admin/account/members/delete`
- **认证**: 需要
- **参数**: `id` (必需): 成员ID
- **响应**: 删除成功(软删除)
#### 6. 恢复成员
- **路径**: `PUT /admin/account/members/restore`
- **认证**: 需要
- **参数**: `id` (必需): 成员ID
- **响应**: 恢复成功
### 账户管理
#### 1. 获取账户列表
- **路径**: `GET /admin/account/accounts/index`
- **认证**: 需要
- **参数**:
- `user_id` (可选): 用户ID
- `type` (可选): 账户类型
- **响应**: 账户列表(包含统计信息)
#### 2. 获取账户详情
- **路径**: `GET /admin/account/accounts/show`
- **认证**: 需要
- **参数**: `id` (必需): 账户ID
- **响应**: 账户详情(包含关联记录)
#### 3. 创建账户
- **路径**: `POST /admin/account/accounts/add`
- **认证**: 需要
- **参数**:
- `user_id` (必需): 用户ID
- `name` (必需): 账户名称
- `type` (必需): 账户类型
- `balance` (可选): 初始余额
- `icon` (可选): 图标
- **响应**: 创建的账户
#### 4. 更新账户
- **路径**: `PUT /admin/account/accounts/edit`
- **认证**: 需要
- **参数**:
- `id` (必需): 账户ID
- 其他字段同创建接口
- **响应**: 更新后的账户
#### 5. 删除账户
- **路径**: `DELETE /admin/account/accounts/delete`
- **认证**: 需要
- **参数**: `id` (必需): 账户ID
- **响应**: 删除成功(软删除)
#### 6. 恢复账户
- **路径**: `PUT /admin/account/accounts/restore`
- **认证**: 需要
- **参数**: `id` (必需): 账户ID
- **响应**: 恢复成功
#### 7. 重新计算账户余额
- **路径**: `POST /admin/account/accounts/recalculate`
- **认证**: 需要
- **参数**: `id` (必需): 账户ID
- **响应**: 更新后的账户
#### 8. 获取账户类型列表
- **路径**: `GET /admin/account/accounts/types`
- **认证**: 需要
- **响应**:
```json
{
"code": 1,
"message": "success",
"data": {
"cash": "现金",
"bank": "银行卡",
"alipay": "支付宝",
"wechat": "微信"
}
}
```
## 后台管理系统
### 概述
后台管理系统提供了完整的管理功能,位于 `resources/admin` 目录下,使用 Vue 3 + Element Plus + Vite 技术栈。
### 目录结构
```
resources/admin/src/
├── api/
│ ├── module/
│ │ └── account.js # 记账模块API接口
│ └── index.js # API自动导入
├── pages/
│ └── account/ # 记账模块页面
│ ├── families/ # 家庭管理
│ ├── accounts/ # 账户管理
│ ├── members/ # 成员管理
│ └── records/ # 记录管理
└── router/
├── accountRouter.js # 记账模块路由
└── index.js # 路由主文件
```
### 功能模块
#### 1. 家庭管理 (`/account/families`)
**功能特性**
- 家庭列表展示(支持搜索)
- 查看家庭详情
- 创建/编辑家庭
- 删除家庭(含确认提示)
- 显示成员数量和账户数量统计
**页面组件**
- `index.vue`: 家庭列表页
- `save.vue`: 家庭添加/编辑/查看页
**搜索条件**
- 家庭名称
**操作权限**
- 添加家庭
- 编辑家庭信息
- 查看家庭详情
- 删除家庭
#### 2. 账户管理 (`/account/accounts`)
**功能特性**
- 账户列表展示(支持搜索和筛选)
- 查看账户详情
- 创建/编辑账户
- 删除账户(含确认提示)
- 显示账户余额(正数绿色,负数红色)
- 支持多种账户类型(现金、银行卡、信用卡、支付宝、微信等)
**页面组件**
- `index.vue`: 账户列表页
- `save.vue`: 账户添加/编辑/查看页
**搜索条件**
- 账户名称
- 账户类型
**表单字段**
- 账户名称(必填,2-50字符)
- 账户类型(必填)
- 所属家庭(必填,下拉选择)
- 初始余额(可选,数字)
- 备注(可选,最多200字符)
#### 3. 成员管理 (`/account/members`)
**功能特性**
- 成员列表展示(支持搜索和筛选)
- 查看成员详情
- 添加/编辑成员
- 移除成员(含确认提示)
- 显示成员角色(owner/admin/member
- 显示成员所属家庭
**页面组件**
- `index.vue`: 成员列表页
- `save.vue`: 成员添加/编辑/查看页
**搜索条件**
- 成员昵称
- 成员角色
**表单字段**
- 所属家庭(必填,下拉选择)
- 用户手机号(必填,用于查找用户)
- 角色(必填,下拉选择)
- 自动显示用户详细信息
**角色说明**
- owner(家庭主):最高权限,红色标签
- admin(管理员):管理权限,橙色标签
- member(成员):普通权限,蓝色标签
#### 4. 记录管理 (`/account/records`)
**功能特性**
- 记录列表展示(支持搜索和筛选)
- 查看记录详情
- 创建/编辑记录
- 删除记录(含确认提示)
- 显示金额(收入绿色+号,支出红色-号)
- 显示记录类型(收入/支出标签)
- 支持日期范围筛选
**页面组件**
- `index.vue`: 记录列表页
- `save.vue`: 记录添加/编辑/查看页
**搜索条件**
- 关键词
- 记录类型(收入/支出)
- 日期范围
**表单字段**
- 记录类型(必填,单选:收入/支出)
- 所属家庭(必填,下拉选择)
- 账户(必填,下拉选择,根据家庭筛选)
- 分类(必填,下拉选择,根据记录类型筛选)
- 金额(必填,数字)
- 日期(必填,日期选择器)
- 备注(可选,最多200字符)
**联动功能**
- 选择家庭后,自动加载该家庭的账户列表
- 切换记录类型后,自动筛选对应的分类列表
### API接口封装
所有API接口统一封装在 `resources/admin/src/api/module/account.js` 中:
```javascript
// 家庭管理
this.$API.account.family.list.get(params)
this.$API.account.family.detail.get(params)
this.$API.account.family.add.post(params)
this.$API.account.family.edit.post(params)
this.$API.account.family.delete.post(params)
// 账户管理
this.$API.account.accounts.list.get(params)
this.$API.account.accounts.detail.get(params)
this.$API.account.accounts.add.post(params)
this.$API.account.accounts.edit.post(params)
this.$API.account.accounts.delete.post(params)
// 成员管理
this.$API.account.members.list.get(params)
this.$API.account.members.detail.get(params)
this.$API.account.members.add.post(params)
this.$API.account.members.edit.post(params)
this.$API.account.members.delete.post(params)
// 记录管理
this.$API.account.records.list.get(params)
this.$API.account.records.detail.get(params)
this.$API.account.records.add.post(params)
this.$API.account.records.edit.post(params)
this.$API.account.records.delete.post(params)
```
### 路由配置
记账模块路由已配置在 `resources/admin/src/router/accountRouter.js`
```javascript
{
path: "/account",
meta: {
title: "记账管理",
icon: "el-icon-wallet"
},
children: [
{
path: "/account/families",
meta: { title: "家庭管理", icon: "el-icon-house" }
},
{
path: "/account/accounts",
meta: { title: "账户管理", icon: "el-icon-bank-card" }
},
{
path: "/account/members",
meta: { title: "成员管理", icon: "el-icon-user" }
},
{
path: "/account/records",
meta: { title: "记录管理", icon: "el-icon-notebook-2" }
}
]
}
```
路由已自动注册到主路由文件 `index.js`,无需额外配置。
### 通用功能
#### 表格组件
使用 `scTable` 组件实现统一的表格功能:
- 自动分页
- 自动加载
- 参数搜索
- 行选择
- 固定列
#### 抽屉组件
使用 `el-drawer` 组件实现表单弹窗:
- 三种模式:add(添加)、edit(编辑)、show(查看)
- 表单验证
- 关闭时自动重置
- 成功回调刷新列表
#### 权限控制
所有删除操作都使用 `el-popconfirm` 进行二次确认:
- 防止误删除
- 支持取消操作
- 显示确认提示文字
#### 数据展示
- 金额显示:保留2位小数,根据正负值显示不同颜色
- 日期显示:统一格式化
- 角色标签:使用不同颜色区分
- 头像显示:支持头像上传和展示
### 使用说明
#### 1. 启动开发服务器
```bash
cd resources/admin
npm install
npm run dev
```
#### 2. 访问后台
启动后访问:`http://localhost:5173`
#### 3. 登录系统
使用管理员账号登录后台系统。
#### 4. 使用记账功能
1. 点击左侧菜单"记账管理"
2. 选择子菜单进行操作:
- 家庭管理:管理所有家庭信息
- 账户管理:管理所有账户信息
- 成员管理:管理所有家庭成员
- 记录管理:管理所有收支记录
#### 5. 数据流转
```
创建家庭 → 添加成员 → 创建账户 → 记录收支
```
**注意事项**
- 必须先创建家庭,才能添加成员和账户
- 创建账户时必须选择所属家庭
- 创建记录时必须选择家庭、账户和成员
- 所有删除操作都有确认提示
## 使用说明
### 数据库迁移
运行以下命令创建数据库表:
```bash
php artisan migrate
```
### 多家庭架构
本模块支持多用户多家庭架构:
#### 数据隔离
- 每个用户可以加入多个家庭
- 每个家庭有独立的成员、账户和记录
- 所有数据通过`family_id`字段关联到具体家庭
- 用户只能访问自己所属家庭的数据
#### 家庭角色权限
| 角色 | 创建/编辑家庭 | 邀请成员 | 移除成员 | 修改角色 | 删除家庭 | 退出家庭 |
|------|--------------|---------|---------|---------|---------|---------|
| owner | ✅ | ✅ | ✅(不能移除owner) | ✅(不能修改owner) | ✅ | ❌(只能删除) |
| admin | ✅ | ✅ | ✅(不能移除owner) | ❌ | ❌ | ✅ |
| member | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
#### 权限判断方法
AccountFamilyMember模型提供以下权限判断方法:
```php
// 判断是否是拥有者
$member->isOwner();
// 判断是否是管理员
$member->isAdmin();
// 判断是否有管理权限(owner或admin)
$member->hasManagePermission();
```
### 账户余额自动更新
系统会自动维护账户余额:
- 创建收入记录时,账户余额增加
- 创建支出记录时,账户余额减少
- 更新记录时,自动调整相关账户余额
- 删除记录时,自动回滚账户余额
### 软删除
所有数据表都支持软删除:
- 删除操作不会物理删除数据,只是标记为已删除
- 后台管理可以查看和恢复已删除的数据
- 永久删除需要额外操作
### 数据验证
所有接口都有严格的数据验证:
- 必填字段检查
- 数据类型验证
- 业务逻辑验证(如删除成员前检查是否有关联记录)
## 注意事项
1. 所有接口都需要认证(除登录接口外)
2. 前端API只能操作当前登录用户及其所属家庭的数据
3. 后台API可以操作所有用户的数据
4. 每个请求都应该指定`family_id`来操作特定家庭的数据
5. 账户余额建议使用重新计算接口进行校准
6. 删除成员或账户前,系统会检查是否有关联记录
7. 家庭拥有者不能退出家庭,只能删除家庭
8. 删除家庭前,必须先删除或转移该家庭下的所有账户
9. 修改成员角色需要谨慎操作,可能影响其他成员的权限
## 多家庭使用示例
### 创建家庭并邀请成员
```javascript
// 1. 创建家庭
const family = await familyApi.createFamily({
name: '温馨小家',
description: '我们的家庭记账',
avatar: 'avatar.jpg'
})
// 2. 邀请成员(需要知道对方的用户ID)
await familyApi.inviteMember(family.id, 123) // 123是被邀请用户的ID
```
### 管理家庭成员
```javascript
// 1. 获取家庭详情
const family = await familyApi.getFamilyDetail(familyId)
// 2. 修改成员角色(仅owner可以)
await familyApi.updateMemberRole(familyId, memberId, 'admin')
// 3. 移除成员(需要管理权限)
await familyApi.removeMember(familyId, memberId)
```
### 在记账时使用家庭
```javascript
// 创建记账记录时指定家庭ID
await recordApi.createRecord({
family_id: 1, // 家庭ID
member_id: 2, // 家庭成员ID
account_id: 3, // 家庭账户ID
type: 'expense',
amount: 100,
category: '餐饮',
date: '2025-01-01',
time: '12:00'
})
```
### 切换家庭
```javascript
// 获取用户的所有家庭
const families = await familyApi.getFamilies()
// 在应用中保存当前选择的家庭ID
const currentFamilyId = families.list[0].id
// 后续操作都使用这个family_id
```
+20
View File
@@ -0,0 +1,20 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Language Lines
|--------------------------------------------------------------------------
|
| The following language lines are used during authentication for various
| messages that we need to display to the user. You are free to modify
| these language lines according to your application's requirements.
|
*/
'failed' => 'These credentials do not match our records.',
'password' => 'The provided password is incorrect.',
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
];

Some files were not shown because too many files have changed in this diff Show More