mirror of
https://gitee.com/zoujingli/ThinkAdmin.git
synced 2026-06-08 12:58:11 +08:00
增加 github actions 管理
This commit is contained in:
parent
ce7375ce87
commit
9969a655a2
26
plugin/think-library/.github/workflows/release.yml
vendored
Normal file
26
plugin/think-library/.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
on:
|
||||
push:
|
||||
# Sequence of patterns matched against refs/tags
|
||||
tags:
|
||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
name: Release
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: Release ${{ github.ref }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
8
plugin/think-library/.gitignore
vendored
Normal file
8
plugin/think-library/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
.git
|
||||
.svn
|
||||
.idea
|
||||
*.cache
|
||||
/vendor
|
||||
/composer.lock
|
||||
!.gitignore
|
||||
!composer.json
|
||||
60
plugin/think-library/composer.json
Normal file
60
plugin/think-library/composer.json
Normal file
@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "zoujingli/think-library",
|
||||
"license": "MIT",
|
||||
"homepage": "https://thinkadmin.top",
|
||||
"description": "Basic Library for ThinkAdmin",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Anyon",
|
||||
"email": "zoujingli@qq.com"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"email": "zoujingli@qq.com",
|
||||
"wiki": "https://thinkadmin.top",
|
||||
"forum": "https://thinkadmin.top",
|
||||
"source": "https://gitee.com/zoujingli/ThinkLibrary",
|
||||
"issues": "https://gitee.com/zoujingli/ThinkLibrary/issues"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"ext-gd": "*",
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-zlib": "*",
|
||||
"ext-iconv": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-mbstring": "*",
|
||||
"symfony/process": "^5.4|^6.0|*",
|
||||
"topthink/framework": "^6.0|^8.0|*",
|
||||
"topthink/think-migration": "^3.0|*"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/common.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"think\\admin\\": "src"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"think\\admin\\tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"think": {
|
||||
"services": [
|
||||
"think\\admin\\Library"
|
||||
]
|
||||
}
|
||||
},
|
||||
"prefer-stable": true,
|
||||
"minimum-stability": "dev",
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
||||
21
plugin/think-library/license
Normal file
21
plugin/think-library/license
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014~2024 邹景立 <zoujingli@qq.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
19
plugin/think-library/phpunit.xml.dist
Normal file
19
plugin/think-library/phpunit.xml.dist
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
colors="true"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
stopOnError="false"
|
||||
backupGlobals="false"
|
||||
stopOnFailure="false"
|
||||
processIsolation="false"
|
||||
backupStaticProperties="false"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
beStrictAboutTestsThatDoNotTestAnything="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="ThinkAdmin Test Suite">
|
||||
<directory suffix="Test.php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
397
plugin/think-library/readme.md
Normal file
397
plugin/think-library/readme.md
Normal file
@ -0,0 +1,397 @@
|
||||
# ThinkLibrary for ThinkPHP6
|
||||
|
||||
[](https://packagist.org/packages/zoujingli/think-library)
|
||||
[](https://packagist.org/packages/zoujingli/think-library)
|
||||
[](https://packagist.org/packages/zoujingli/think-library)
|
||||
[](https://packagist.org/packages/zoujingli/think-library)
|
||||
[](https://packagist.org/packages/zoujingli/think-library)
|
||||
[](https://thinkadmin.top)
|
||||
[](https://mit-license.org)
|
||||
|
||||
**ThinkLibrary** 是一个针对 **ThinkPHP 6 & 8** 的封装库,它提供了完整的 **CRUD**(创建、读取、更新、删除)操作和一系列常用工具。
|
||||
该库特别注重多应用支持,为开发者提供便利。前端代码的主仓库位于 **Gitee**,而 **GitHub** 则作为镜像仓库用于发布 **Composer** 包,以方便开发者下载和使用。
|
||||
|
||||
## 功能说明
|
||||
|
||||
1. 数据列表展示组件
|
||||
|
||||
* 功能:展示数据列表,支持分页、排序和高级搜索。
|
||||
* 优化点:提供友好的用户界面和交互体验,确保数据展示的准确性和实时性。
|
||||
* 高级特性:支持多种排序方式、自定义搜索字段和条件。
|
||||
|
||||
2. 表单处理模块
|
||||
|
||||
* 功能:用于创建、展示和提交表单数据。
|
||||
* 优化点:表单验证和错误处理机制,确保数据的有效性和完整性。
|
||||
* 高级特性:支持多种表单元素、表单验证规则和动态表单生成。
|
||||
|
||||
3. 数据状态快速处理模块
|
||||
|
||||
* 功能:根据业务需求快速更新数据状态。
|
||||
* 优化点:提供简洁的接口和操作方式,支持多字段同时更新。
|
||||
* 高级特性:支持条件判断和事务处理,确保数据一致性和完整性。
|
||||
|
||||
4. 数据安全删除模块
|
||||
|
||||
* 功能:根据业务需求安全地删除数据。
|
||||
* 优化点:提供软删除和硬删除两种方式,确保数据彻底消失或标记为已删除。
|
||||
* 高级特性:支持根据条件自动软删除、可配置的软删除标记字段。
|
||||
|
||||
5. 文件存储通用组件
|
||||
|
||||
* 功能:支持多种文件存储方式,包括本地服务存储、云存储等。
|
||||
* 优化点:提供统一的接口和配置方式,方便开发者快速集成和使用。
|
||||
* 高级特性:支持文件上传、下载、删除和版本控制等功能。
|
||||
|
||||
6. 通用数据保存更新模块
|
||||
|
||||
* 功能:根据 key 值及 where 条件判断数据是否存在,进行更新或新增操作。
|
||||
* 优化点:提供简洁的接口和操作方式,减少冗余代码和重复工作量。
|
||||
* 高级特性:支持乐观锁和悲观锁机制,确保并发控制和数据一致性。
|
||||
|
||||
7. 通用网络请求模块
|
||||
|
||||
* 功能:支持 GET、POST 和 PUT 请求,可配置请求参数、证书等。
|
||||
* 优化点:提供统一的接口和配置方式,方便开发者快速发起网络请求。
|
||||
* 高级特性:支持请求重试、超时设置、自动捕获异常等功能。
|
||||
|
||||
8. 系统参数通用 g-k-v 配置模块
|
||||
|
||||
* 功能:快速配置系统参数,支持长久化保存。
|
||||
* 优化点:提供简洁的接口和操作方式,方便开发者管理和维护系统参数。
|
||||
* 高级特性:支持参数加密存储、权限控制和日志记录等功能。
|
||||
|
||||
9. UTF8 加密算法支持模块
|
||||
|
||||
* 功能:提供 UTF8 字符串的加密和解密功能。
|
||||
* 优化点:确保加密过程的安全性和数据的机密性。
|
||||
* 高级特性:支持多种加密算法、密钥管理等功能。
|
||||
|
||||
10. 接口 CORS 跨域默认支持模块
|
||||
|
||||
* 功能:默认支持跨域请求,输出标准化 JSON 数据。
|
||||
* 优化点:减少开发者的工作量,自动处理跨域问题。
|
||||
* 高级特性:支持定制化响应头、跨域请求限制等功能。
|
||||
|
||||
11. 表单 CSRF 安全验证模块
|
||||
|
||||
* 功能:自动为表单添加 CSRF 安全验证字段,防止恶意提交。
|
||||
* 优化点:简化开发者的工作流程,提高表单提交的安全性。
|
||||
* 高级特性:支持自定义验证规则、多种验证方式等功能。
|
||||
|
||||
## 参考项目
|
||||
|
||||
#### ThinkAdmin - V6
|
||||
|
||||
* Gitee 仓库 https://gitee.com/zoujingli/ThinkAdmin
|
||||
* Github 仓库 https://github.com/zoujingli/ThinkAdmin
|
||||
* 体验地址(账号密码都是admin)https://v6.thinkadmin.top
|
||||
|
||||
## 代码仓库
|
||||
|
||||
**ThinkLibrary** 遵循 **MIT** 开源协议发布,并免费提供使用。
|
||||
|
||||
部分代码来自互联网,若有异议可以联系作者进行删除。
|
||||
|
||||
* 在线体验地址:https://v6.thinkadmin.top (账号和密码都是 `admin` )
|
||||
* **Gitee** 仓库地址:https://gitee.com/zoujingli/ThinkLibrary
|
||||
* **Github** 仓库地址:https://github.com/zoujingli/ThinkLibrary
|
||||
|
||||
## 使用说明
|
||||
|
||||
1. **依赖管理**:ThinkLibrary 需要 Composer 支持进行安装和依赖管理。
|
||||
2. **安装指南**:您可以使用以下命令通过 Composer 安装 ThinkLibrary:`composer require zoujingli/think-library`。
|
||||
3. **使用示例**:在使用 ThinkLibrary 时,确保您的控制器类继承自 `think\admin\Controller`。一旦继承完成,您就可以通过 `$this` 对象访问并使用全部功能。
|
||||
|
||||
```php
|
||||
// 定义 MyController 控制器
|
||||
class MyController extend \think\admin\Controller {
|
||||
|
||||
// 指定当前数据表名
|
||||
protected $dbQuery = '数据表名';
|
||||
|
||||
// 显示数据列表
|
||||
public function index(){
|
||||
$this->_page($this->dbQuery);
|
||||
}
|
||||
|
||||
// 当前列表数据处理
|
||||
protected function _index_page_filter(&$data){
|
||||
foreach($data as &$vo){
|
||||
// @todo 修改原列表
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
* 必要数据库表SQL(sysdata 函数需要用这个表)
|
||||
|
||||
```sql
|
||||
CREATE TABLE `system_data`
|
||||
(
|
||||
`id` bigint(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(100) DEFAULT NULL COMMENT '配置名',
|
||||
`value` longtext COMMENT '配置值',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_system_data_name` (`name`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统-数据';
|
||||
```
|
||||
|
||||
* 必要数据库表SQl(sysoplog 函数需要用的这个表)
|
||||
|
||||
```sql
|
||||
CREATE TABLE `system_oplog`
|
||||
(
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`node` varchar(200) NOT NULL DEFAULT '' COMMENT '当前操作节点',
|
||||
`geoip` varchar(15) NOT NULL DEFAULT '' COMMENT '操作者IP地址',
|
||||
`action` varchar(200) NOT NULL DEFAULT '' COMMENT '操作行为名称',
|
||||
`content` varchar(1024) NOT NULL DEFAULT '' COMMENT '操作内容描述',
|
||||
`username` varchar(50) NOT NULL DEFAULT '' COMMENT '操作人用户名',
|
||||
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统-日志';
|
||||
```
|
||||
|
||||
* 必要数据库表SQL(`sysconf`函数需要用到这个表)
|
||||
|
||||
```sql
|
||||
CREATE TABLE `system_config`
|
||||
(
|
||||
`type` varchar(20) DEFAULT '' COMMENT '分类',
|
||||
`name` varchar(100) DEFAULT '' COMMENT '配置名',
|
||||
`value` varchar(500) DEFAULT '' COMMENT '配置值',
|
||||
KEY `idx_system_config_type` (`type`),
|
||||
KEY `idx_system_config_name` (`name`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统-配置';
|
||||
```
|
||||
|
||||
* 系统任务列队支持需要的数据表
|
||||
|
||||
```sql
|
||||
CREATE TABLE `system_queue` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`code` varchar(20) DEFAULT '' COMMENT '任务编号',
|
||||
`title` varchar(50) NOT NULL DEFAULT '' COMMENT '任务名称',
|
||||
`command` varchar(500) DEFAULT '' COMMENT '执行指令',
|
||||
`exec_data` longtext COMMENT '执行参数',
|
||||
`exec_time` bigint(20) unsigned DEFAULT '0' COMMENT '执行时间',
|
||||
`exec_desc` varchar(500) DEFAULT '' COMMENT '状态描述',
|
||||
`enter_time` bigint(20) DEFAULT '0' COMMENT '开始时间',
|
||||
`outer_time` bigint(20) DEFAULT '0' COMMENT '结束时间',
|
||||
`attempts` bigint(20) DEFAULT '0' COMMENT '执行次数',
|
||||
`rscript` tinyint(1) DEFAULT '1' COMMENT '单例模式',
|
||||
`status` tinyint(1) DEFAULT '1' COMMENT '任务状态(1新任务,2处理中,3成功,4失败)',
|
||||
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `idx_system_queue_code` (`code`),
|
||||
KEY `idx_system_queue_title` (`title`) USING BTREE,
|
||||
KEY `idx_system_queue_status` (`status`) USING BTREE,
|
||||
KEY `idx_system_queue_rscript` (`rscript`) USING BTREE,
|
||||
KEY `idx_system_queue_create_at` (`create_at`) USING BTREE,
|
||||
KEY `idx_system_queue_exec_time` (`exec_time`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统-任务';
|
||||
```
|
||||
|
||||
#### 列表处理
|
||||
|
||||
```php
|
||||
// 列表展示
|
||||
$this->_page($dbQuery, $isPage, $isDisplay, $total);
|
||||
|
||||
// 列表展示搜索器(按 name、title 模糊搜索;按 status 精确搜索)
|
||||
$this->_query($dbQuery)->like('name,title')->equal('status')->page();
|
||||
|
||||
// 对列表查询器进行二次处理
|
||||
$query = $this->_query($dbQuery)->like('name, title')->equal('status');
|
||||
$db = $query->db(); // @todo 这里可以对db进行操作
|
||||
$this->_page($db); // 显示列表分页
|
||||
```
|
||||
|
||||
#### 表单处理
|
||||
|
||||
```php
|
||||
// 表单显示及数据更新
|
||||
$this->_form($dbQuery, $tplFile, $pkField , $where, $data);
|
||||
```
|
||||
|
||||
#### 删除处理
|
||||
|
||||
```php
|
||||
// 数据删除处理
|
||||
$this->_deleted($dbQuery);
|
||||
```
|
||||
|
||||
#### 禁用启用处理
|
||||
|
||||
```php
|
||||
// 数据禁用处理
|
||||
$this->_save($dbQuery, ['status'=>'0']);
|
||||
|
||||
// 数据启用处理
|
||||
$this->_save($dbQuery, ['status'=>'1']);
|
||||
```
|
||||
|
||||
#### 文件存储组件( oss 及 qiniu 需要配置参数)
|
||||
|
||||
```php
|
||||
|
||||
// 配置默认存储方式
|
||||
sysconf('storage.type','文件存储类型');
|
||||
|
||||
// 七牛云存储配置
|
||||
sysconf('storage.qiniu_region', '文件存储节点');
|
||||
sysconf('storage.qiniu_domain', '文件访问域名');
|
||||
sysconf('storage.qiniu_bucket', '文件存储空间名称');
|
||||
sysconf('storage.qiniu_is_https', '文件HTTP访问协议');
|
||||
sysconf('storage.qiniu_access_key', '接口授权AccessKey');
|
||||
sysconf('storage.qiniu_secret_key', '接口授权SecretKey');
|
||||
|
||||
|
||||
// 生成文件名称(链接url或文件md5)
|
||||
$filename = \think\admin\Storage::name($url, $ext, $prv, $fun);
|
||||
|
||||
// 获取文件内容(自动存储方式)
|
||||
$result = \think\admin\Storage::get($filename);
|
||||
|
||||
// 保存内容到文件(自动存储方式)
|
||||
$result = \think\admin\Storage::save($filename, $content);
|
||||
|
||||
// 判断文件是否存在
|
||||
boolean \think\admin\Storage::has($filename);
|
||||
|
||||
// 获取文件信息
|
||||
$result = \think\admin\Storage::info($filename);
|
||||
|
||||
//指定存储类型(调用方法)
|
||||
$result = \think\admin\Storage::instance('local')->save($filename, $content);
|
||||
$result = \think\admin\Storage::instance('qiniu')->save($filename, $content);
|
||||
$result = \think\admin\Storage::instance('txcos')->save($filename, $content);
|
||||
$result = \think\admin\Storage::instance('upyun')->save($filename, $content);
|
||||
$result = \think\admin\Storage::instance('alioss')->save($filename, $content);
|
||||
|
||||
// 读取文件内容
|
||||
$result = \think\admin\Storage::instance('local')->get($filename);
|
||||
$result = \think\admin\Storage::instance('qiniu')->get($filename);
|
||||
$result = \think\admin\Storage::instance('txcos')->get($filename);
|
||||
$result = \think\admin\Storage::instance('upyun')->get($filename);
|
||||
$result = \think\admin\Storage::instance('alioss')->get($filename);
|
||||
|
||||
// 生成 URL 访问地址
|
||||
$result = \think\admin\Storage::instance('local')->url($filename);
|
||||
$result = \think\admin\Storage::instance('qiniu')->url($filename);
|
||||
$result = \think\admin\Storage::instance('txcos')->url($filename);
|
||||
$result = \think\admin\Storage::instance('upyun')->url($filename);
|
||||
$result = \think\admin\Storage::instance('alioss')->url($filename);
|
||||
|
||||
// 检查文件是否存在
|
||||
boolean \think\admin\Storage::instance('local')->has($filename);
|
||||
boolean \think\admin\Storage::instance('qiniu')->has($filename);
|
||||
boolean \think\admin\Storage::instance('txcos')->has($filename);
|
||||
boolean \think\admin\Storage::instance('upyun')->has($filename);
|
||||
boolean \think\admin\Storage::instance('alioss')->has($filename);
|
||||
|
||||
// 生成文件信息
|
||||
$resutl = \think\admin\Storage::instance('local')->info($filename);
|
||||
$resutl = \think\admin\Storage::instance('qiniu')->info($filename);
|
||||
$resutl = \think\admin\Storage::instance('txcos')->info($filename);
|
||||
$resutl = \think\admin\Storage::instance('upyun')->info($filename);
|
||||
$resutl = \think\admin\Storage::instance('alioss')->info($filename);
|
||||
```
|
||||
|
||||
#### 通用数据保存
|
||||
|
||||
```php
|
||||
// 指定关键列更新($where 为扩展条件)
|
||||
boolean data_save($dbQuery, $data, 'pkname', $where);
|
||||
```
|
||||
|
||||
#### 通用网络请求
|
||||
|
||||
```php
|
||||
// 发起get请求
|
||||
$result = http_get($url, $query, $options);
|
||||
|
||||
// 发起post请求
|
||||
$result = http_post($url, $data, $options);
|
||||
```
|
||||
|
||||
#### 系统参数配置(基于 system_config 数据表)
|
||||
|
||||
```php
|
||||
// 设置参数
|
||||
sysconf($keyname, $keyvalue);
|
||||
|
||||
// 获取参数
|
||||
$keyvalue = sysconf($kename);
|
||||
```
|
||||
|
||||
### 数据加密
|
||||
|
||||
**自研 UTF8 加密**
|
||||
|
||||
```php
|
||||
// 自研 UTF8 字符串加密操作
|
||||
$string = encode($content);
|
||||
|
||||
// 自研 UTF8 加密字符串解密
|
||||
$content = decode($string);
|
||||
```
|
||||
|
||||
**数据解密**
|
||||
|
||||
```php
|
||||
use think\admin\extend\CodeExtend;
|
||||
|
||||
// 数据 AES-256-CBC 对称加密
|
||||
$encrypt = CodeExtend::encrypt($content, $enckey);
|
||||
|
||||
// 数据 AES-256-CBC 对称解密
|
||||
$content = CodeExtend::decrypt($encrypt, $enckey);
|
||||
```
|
||||
|
||||
**文本转 UTF8 编码**
|
||||
|
||||
```php
|
||||
use think\admin\extend\CodeExtend;
|
||||
|
||||
// 文本转 UTF8 编码
|
||||
$content = CodeExtend::text2utf8($content)
|
||||
```
|
||||
|
||||
**文本 Base64 URL 编码**
|
||||
|
||||
```php
|
||||
use think\admin\extend\CodeExtend;
|
||||
|
||||
// 文本 Base64 URL 编码
|
||||
$safe64 = CodeExtend::enSafe64($content)
|
||||
|
||||
// 文本 Base64 URL 解码
|
||||
$content = CodeExtend::deSafe64($safe64)
|
||||
```
|
||||
|
||||
### 数据压缩处理
|
||||
|
||||
```php
|
||||
use think\admin\extend\CodeExtend;
|
||||
|
||||
// 数据压缩 ( 内容越大效果越好 )
|
||||
$enzip = CodeExtend::enzip($content)
|
||||
|
||||
// 数据解压 ( 内容越大效果越好 )
|
||||
$content = CodeExtend::dezip($enzip)
|
||||
```
|
||||
|
||||
### 数组结构处理
|
||||
|
||||
```php
|
||||
use think\admin\extend\CodeExtend;
|
||||
|
||||
// 二维数组 转为 立体数据结构,需要存在 id 及 pid 关系
|
||||
$tree = CodeExtend::arr2tree($list);
|
||||
|
||||
// 二维数组 转为 扁平数据结构,需要存在 id 及 pid 关系
|
||||
$tree = CodeExtend::arr2table($list);
|
||||
```
|
||||
411
plugin/think-library/src/Builder.php
Normal file
411
plugin/think-library/src/Builder.php
Normal file
@ -0,0 +1,411 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin;
|
||||
|
||||
use think\exception\HttpResponseException;
|
||||
|
||||
/**
|
||||
* 表单模板构建器
|
||||
* 后面会在兼容的基础上慢慢完善
|
||||
* @class Builder
|
||||
* @deprecated 试验中建议不使用
|
||||
* @package think\admin
|
||||
*/
|
||||
class Builder
|
||||
{
|
||||
/**
|
||||
* 生成类型
|
||||
* @var string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* 显示方式
|
||||
* @var string
|
||||
*/
|
||||
private $mode;
|
||||
|
||||
/**
|
||||
* 当前控制器
|
||||
* @var \think\admin\Controller
|
||||
*/
|
||||
private $class;
|
||||
|
||||
/**
|
||||
* 提交地址
|
||||
* @var string
|
||||
*/
|
||||
private $action;
|
||||
|
||||
/**
|
||||
* 表单变量
|
||||
* @var string
|
||||
*/
|
||||
private $variable = '$vo';
|
||||
|
||||
/**
|
||||
* 表单项目
|
||||
* @var array
|
||||
*/
|
||||
private $fields = [];
|
||||
private $buttons = [];
|
||||
|
||||
/**
|
||||
* Constructer
|
||||
* @param string $type 页面类型
|
||||
* @param string $mode 页面模式
|
||||
* @param \think\admin\Controller $class
|
||||
*/
|
||||
public function __construct(string $type, string $mode, Controller $class)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->mode = $mode;
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建表单生成器
|
||||
* @param string $type 页面类型
|
||||
* @param string $mode 页面模式
|
||||
* @return \think\admin\Builder
|
||||
*/
|
||||
public static function mk(string $type = 'form', string $mode = 'modal'): Builder
|
||||
{
|
||||
return Library::$sapp->invokeClass(static::class, ['type' => $type, 'mode' => $mode]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置表单地址
|
||||
* @param string $url
|
||||
* @return $this
|
||||
*/
|
||||
public function setAction(string $url): Builder
|
||||
{
|
||||
$this->action = $url;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置变量名称
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
public function setVariable(string $name): Builder
|
||||
{
|
||||
$this->variable = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加输入表单元素
|
||||
* @param string $name 字段名称
|
||||
* @param string $title 字段标题
|
||||
* @param string $subtitle 字段子标题
|
||||
* @param string $remark 字段备注
|
||||
* @param array $attrs 附加属性
|
||||
* @return $this
|
||||
*/
|
||||
protected function addInput(string $name, string $title, string $subtitle = '', string $remark = '', array $attrs = []): Builder
|
||||
{
|
||||
$html = "\n\t\t" . '<label class="layui-form-item block relative">';
|
||||
$html .= "\n\t\t\t" . sprintf('<span class="help-label %s"><b>%s</b>%s</span>', empty($attrs['required']) ? '' : 'label-required-prev', $title, $subtitle);
|
||||
$html .= "\n\t\t\t" . sprintf('<input name="%s" %s placeholder="请输入%s" value="{%s.%s|default=\'\'}" class="layui-input">', $name, $this->_attrs($attrs), $title, $this->variable, $name);
|
||||
if ($remark) $html .= "\n\t\t\t" . sprintf('<span class="help-block">%s</span>', $remark);
|
||||
$this->fields[] = "{$html}\n\t\t</label>";
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文本输入框架
|
||||
* @param string $name 字段名称
|
||||
* @param string $title 字段标题
|
||||
* @param string $substr 字段子标题
|
||||
* @param array $attrs 附加属性
|
||||
* @return $this
|
||||
*/
|
||||
public function addTextArea(string $name, string $title, string $substr = '', bool $required = false, $remark = '', array $attrs = []): Builder
|
||||
{
|
||||
if ($required) $attrs['required'] = 'required';
|
||||
$html = "\n\t\t" . '<label class="layui-form-item block relative">';
|
||||
$html .= "\n\t\t\t" . sprintf('<span class="help-label %s"><b>%s</b>%s</span>', empty($attrs['required']) ? '' : 'label-required-prev', $title, $substr);
|
||||
$html .= "\n\t\t\t" . sprintf('<textarea name="%s" %s placeholder="请输入%s" class="layui-textarea">{%s.%s|default=\'\'}</textarea>', $name, $this->_attrs($attrs), $title, $this->variable, $name);
|
||||
if ($remark) $html .= "\n\t\t\t" . sprintf('<span class="help-block">%s</span>', $remark);
|
||||
$this->fields[] = "{$html}\n\t\t</lable>";
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段属性转换
|
||||
* @param array $attrs
|
||||
* @param string $html
|
||||
* @return string
|
||||
*/
|
||||
protected function _attrs(array $attrs, string $html = ''): string
|
||||
{
|
||||
foreach ($attrs as $k => $v) $html .= is_null($v) ? sprintf(' %s', $k) : sprintf(' %s="%s"', $k, $v);
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 Text 输入
|
||||
* @param string $name 字段名称
|
||||
* @param string $title 字段标题
|
||||
* @param string $substr 字段子标题
|
||||
* @param string $remark 字段备注
|
||||
* @param boolean $required 是否必填
|
||||
* @param ?string $pattern 验证规则
|
||||
* @param array $attrs 附加属性
|
||||
* @return $this
|
||||
*/
|
||||
public function addTextInput(string $name, string $title, string $substr = '', bool $required = false, string $remark = '', ?string $pattern = null, array $attrs = []): Builder
|
||||
{
|
||||
$attrs['vali-name'] = $title;
|
||||
if ($required) $attrs['required'] = 'required';
|
||||
if (is_string($pattern)) $attrs['pattern'] = $pattern;
|
||||
return $this->addInput($name, $title, $substr, $remark, $attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建密钥输入框
|
||||
* @param string $name 字段名称
|
||||
* @param string $title 字段标题
|
||||
* @param string $substr 字段子标题
|
||||
* @param string $remark 字段备注
|
||||
* @param boolean $required 是否必填
|
||||
* @param ?string $pattern 验证规则
|
||||
* @param array $attrs 附加属性
|
||||
* @return $this
|
||||
*/
|
||||
public function addPassInput(string $name, string $title, string $substr = '', bool $required = false, string $remark = '', ?string $pattern = null, array $attrs = []): Builder
|
||||
{
|
||||
$attrs['type'] = 'password';
|
||||
return $this->addTextInput($name, $title, $substr, $required, $remark, $pattern, $attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加表单按钮
|
||||
* @param string $name 按钮名称
|
||||
* @param string $confirm 确认提示
|
||||
* @param string $type 按钮类型
|
||||
* @param string $class 按钮样式
|
||||
* @param array $attrs 附加属性
|
||||
* @return $this
|
||||
*/
|
||||
protected function addButton(string $name, string $confirm, string $type, string $class = '', array $attrs = []): Builder
|
||||
{
|
||||
$attrs['type'] = $type;
|
||||
if ($confirm) $attrs['data-confirm'] = $confirm;
|
||||
$this->buttons[] = sprintf('<button class="layui-btn %s" %s>%s</button>', $class, $this->_attrs($attrs), $name);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加取消按钮
|
||||
* @param string $name 按钮名称
|
||||
* @param string $confirm 确认提示
|
||||
* @return $this
|
||||
*/
|
||||
public function addCancelButton(string $name = '取消编辑', string $confirm = '确定要取消编辑吗?'): Builder
|
||||
{
|
||||
return $this->addButton($name, $confirm, 'button', 'layui-btn-danger', ['data-close' => null]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加提交按钮
|
||||
* @param string $name 按钮名称
|
||||
* @param string $confirm 确认提示
|
||||
* @return $this
|
||||
*/
|
||||
public function addSubmitButton(string $name = '保存数据', string $confirm = ''): Builder
|
||||
{
|
||||
return $this->addButton($name, $confirm, 'submit');
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加上传单个文件
|
||||
* @param string $name 字段名称
|
||||
* @param string $title 字段标题
|
||||
* @param string $substr 字段子标题
|
||||
* @param array $attrs 附加属性
|
||||
* @param string $type 上传类型
|
||||
* @return $this
|
||||
*/
|
||||
private function _addUploadOneView(string $name, string $title, string $substr = '', array $attrs = [], string $type = 'image'): Builder
|
||||
{
|
||||
$attrs = array_merge($attrs, ['type' => 'text', 'placeholder' => "请上传{$title}", 'vali-name' => $title]);
|
||||
$html = "\n\t\t" . '<div class="layui-form-item">';
|
||||
$html .= "\n\t\t\t" . sprintf('<span class="help-label %s"><b>%s</b>%s</span>', empty($attrs['required']) ? '' : 'label-required-prev', $title, $substr);
|
||||
$html .= "\n\t\t\t" . '<div class="relative block label-required-null">';
|
||||
$html .= "\n\t\t\t\t" . sprintf('<input class="layui-input layui-bg-gray" name="%s" %s value="{%s.%s|default=\'\'}">', $name, $this->_attrs($attrs), $this->variable, $name);
|
||||
if ($type === 'image') {
|
||||
$html .= "\n\t\t\t\t" . sprintf('<a class="layui-icon layui-icon-upload input-right-icon" data-file="image" data-field="%s" data-type="gif,png,jpg,jpeg"></a>', $name);
|
||||
} else {
|
||||
$html .= "\n\t\t\t\t" . sprintf('<a class="layui-icon layui-icon-upload input-right-icon" data-file data-field="%s" data-type="mp4"></a>', $name);
|
||||
}
|
||||
$html .= "\n\t\t\t</div>\n\t\t</div>";
|
||||
if ($type === 'image') {
|
||||
$html .= "\n\t\t" . sprintf('<script>$("input[name=%s]").uploadOneImage()</script>', $name);
|
||||
} else {
|
||||
$html .= "\n\t\t" . sprintf('<script>$("input[name=%s]").uploadOneVideo()</script>', $name);
|
||||
}
|
||||
$this->fields[] = $html;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加上传单图字段
|
||||
* @param string $name 字段名称
|
||||
* @param string $title 字段标题
|
||||
* @param string $substr 字段子标题
|
||||
* @param bool $required 必填字段
|
||||
* @param array $attrs 附加属性
|
||||
* @return $this
|
||||
*/
|
||||
public function addUploadOneImage(string $name, string $title, string $substr = '', bool $required = false, array $attrs = []): Builder
|
||||
{
|
||||
if ($required) $attrs['required'] = 'required';
|
||||
return $this->_addUploadOneView($name, $title, $substr, $attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加上传视频字段
|
||||
* @param string $name 字段名称
|
||||
* @param string $title 字段标题
|
||||
* @param string $substr 字段子标题
|
||||
* @param bool $required 必填字段
|
||||
* @param array $attrs 附加属性
|
||||
* @return $this
|
||||
*/
|
||||
public function addUploadOneVideo(string $name, string $title, string $substr = '', bool $required = false, array $attrs = []): Builder
|
||||
{
|
||||
if ($required) $attrs['required'] = 'required';
|
||||
return $this->_addUploadOneView($name, $title, $substr, $attrs, 'video');
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建上传多图字段
|
||||
* @param string $name 字段名称
|
||||
* @param string $title 字段标题
|
||||
* @param string $substr 字段子标题
|
||||
* @param bool $required 必填字段
|
||||
* @param array $attrs 附加属性
|
||||
* @return $this
|
||||
*/
|
||||
public function addUploadMulImage(string $name, string $title, string $substr = '', bool $required = false, array $attrs = []): Builder
|
||||
{
|
||||
if ($required) $attrs['required'] = 'required';
|
||||
$attrs = array_merge($attrs, ['type' => 'hidden', 'placeholder' => "请上传{$title} ( 多图 )"]);
|
||||
$html = "\n\t\t" . '<div class="layui-form-item">';
|
||||
$html .= "\n\t\t\t" . sprintf('<span class="help-label %s"><b>%s</b>%s</span>', empty($attrs['required']) ? '' : 'label-required-prev ', $title, $substr);
|
||||
$html .= "\n\t\t\t" . '<div class="layui-textarea help-images layui-bg-gray">';
|
||||
$html .= "\n\t\t\t\t" . sprintf('<input name="%s" %s value="{%s.%s|default=\'\'}">', $name, $this->_attrs($attrs), $this->variable, $name);
|
||||
$html .= "\n\t\t\t" . '</div>' . "\n\t\t" . '</div>';
|
||||
$html .= "\n\t\t" . sprintf('<script>$("input[name=%s]").uploadMultipleImage()</script>', $name);
|
||||
$this->fields[] = $html;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建复选框字段
|
||||
* @param string $name 字段名称
|
||||
* @param string $title 字段标题
|
||||
* @param string $substr 字段子标题
|
||||
* @param string $vname 变量名称
|
||||
* @param bool $required 是否必选
|
||||
* @param array $attrs 附加属性
|
||||
* @return $this
|
||||
*/
|
||||
public function addCheckInput(string $name, string $title, string $substr, string $vname, bool $required = false, array $attrs = [], string $type = 'checkbox'): Builder
|
||||
{
|
||||
if ($required) $attrs['required'] = 'required';
|
||||
$attrs = array_merge($attrs, ['type' => $type, 'lay-ignore' => null, 'name' => $name . ($type === 'checkbox' ? '[]' : '')]);
|
||||
$html = "\n\t\t" . '<div class="layui-form-item">';
|
||||
$html .= "\n\t\t\t" . sprintf('<span class="help-label %s"><b>%s</b>%s</span>', empty($attrs['required']) ? '' : ' label-required-prev', $title, $substr);
|
||||
$html .= "\n\t\t\t" . '<div class="layui-textarea help-checks layui-bg-gray">';
|
||||
$html .= "\n\t\t\t\t" . sprintf('<!--{foreach $%s as $k=>$v}item-->', $vname);
|
||||
$html .= "\n\t\t\t\t" . sprintf('<label class="think-%s label-required-null">', $type);
|
||||
$html .= "\n\t\t\t\t\t" . sprintf('<!--if{if isset(%s.types) and is_array(%s.types) and in_array($k,%s.types)}-->', $this->variable, $this->variable, $this->variable);
|
||||
$html .= "\n\t\t\t\t\t" . sprintf('<input value="{$k|default=\'\'}" %s checked> {$v|default=\'\'}', $this->_attrs($attrs));
|
||||
$html .= "\n\t\t\t\t\t" . '<!--{else}else-->';
|
||||
$html .= "\n\t\t\t\t\t" . sprintf('<input value="{$k|default=\'\'}" %s> {$v|default=\'\'}', $this->_attrs($attrs)) . "\n";
|
||||
$html .= "\n\t\t\t\t\t" . '<!--{/if}if-->';
|
||||
$html .= "\n\t\t\t\t" . '</label>';
|
||||
$html .= "\n\t\t\t\t" . '<!--{/foreach}end-->';
|
||||
$this->fields[] = $html . "\n\t\t\t</div>\n\t\t</div>";
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加单选框架字段
|
||||
* @param string $name 字段名称
|
||||
* @param string $title 字段标题
|
||||
* @param string $substr 字段子标题
|
||||
* @param string $vname 变量名称
|
||||
* @param bool $required 是否必选
|
||||
* @param array $attrs 附加属性
|
||||
* @return $this
|
||||
*/
|
||||
public function addRadioInput(string $name, string $title, string $substr, string $vname, bool $required = false, array $attrs = []): Builder
|
||||
{
|
||||
return $this->addCheckInput($name, $title, $substr, $vname, $required, $attrs, 'radio');
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示模板内容
|
||||
* @return mixed
|
||||
*/
|
||||
public function fetch(array $vars = [])
|
||||
{
|
||||
$html = '';
|
||||
$type = "{$this->type}.{$this->mode}";
|
||||
if ($type === 'form.page') {
|
||||
$html = $this->_buildFormPage();
|
||||
} elseif ($type === 'form.modal') {
|
||||
$html = $this->_buildFormModal();
|
||||
}
|
||||
foreach ($this->class as $k => $v) $vars[$k] = $v;
|
||||
throw new HttpResponseException(display($html, $vars));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成弹层表单模板
|
||||
* @return string
|
||||
*/
|
||||
private function _buildFormModal(): string
|
||||
{
|
||||
$html = sprintf('<form action="%s" method="post" data-auto="true" class="layui-form layui-card">', $this->action ?? url()->build());
|
||||
$html .= "\n\t" . '<div class="layui-card-body padding-left-40">' . join("\n", $this->fields);
|
||||
if (count($this->buttons)) {
|
||||
$html .= "\n\n\t\t" . '<div class="hr-line-dashed"></div>';
|
||||
$html .= "\n\t\t" . sprintf('{notempty name="vo.id"}<input type="hidden" value="{%s.id}" name="id">{/notempty}', $this->variable);
|
||||
$html .= "\n\t\t" . sprintf('<div class="layui-form-item text-center">%s</div>', "\n\t\t\t" . join("\n\t\t\t", $this->buttons) . "\n\t\t");
|
||||
$html .= "\n\t" . '</div>';
|
||||
}
|
||||
return $html . "\n</form>";
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成页面表单模板
|
||||
* @return string
|
||||
*/
|
||||
private function _buildFormPage(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
124
plugin/think-library/src/Command.php
Normal file
124
plugin/think-library/src/Command.php
Normal file
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin;
|
||||
|
||||
use think\admin\service\ProcessService;
|
||||
use think\admin\service\QueueService;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
|
||||
/**
|
||||
* 自定义指令基类
|
||||
* @class Command
|
||||
* @package think\admin
|
||||
*/
|
||||
abstract class Command extends \think\console\Command
|
||||
{
|
||||
/**
|
||||
* 任务控制服务
|
||||
* @var QueueService
|
||||
*/
|
||||
protected $queue;
|
||||
|
||||
/**
|
||||
* 进程控制服务
|
||||
* @var ProcessService
|
||||
*/
|
||||
protected $process;
|
||||
|
||||
/**
|
||||
* 初始化指令变量
|
||||
* @param \think\console\Input $input
|
||||
* @param \think\console\Output $output
|
||||
* @return $this
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function initialize(Input $input, Output $output): Command
|
||||
{
|
||||
$this->queue = QueueService::instance();
|
||||
$this->process = ProcessService::instance();
|
||||
if (defined('WorkQueueCode') && $this->queue->code !== WorkQueueCode) {
|
||||
$this->queue->initialize(WorkQueueCode);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置失败消息并结束进程
|
||||
* @param string $message 消息内容
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function setQueueError(string $message)
|
||||
{
|
||||
if (defined('WorkQueueCode')) {
|
||||
$this->queue->error($message);
|
||||
} else {
|
||||
$this->process->message($message);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置成功消息并结束进程
|
||||
* @param string $message 消息内容
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function setQueueSuccess(string $message)
|
||||
{
|
||||
if (defined('WorkQueueCode')) {
|
||||
$this->queue->success($message);
|
||||
} else {
|
||||
$this->process->message($message);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置进度消息并继续执行
|
||||
* @param null|string $message 进度消息
|
||||
* @param null|string $progress 进度数值
|
||||
* @param integer $backline 回退行数
|
||||
* @return static
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function setQueueProgress(?string $message = null, ?string $progress = null, int $backline = 0): Command
|
||||
{
|
||||
if (defined('WorkQueueCode')) {
|
||||
$this->queue->progress(2, $message, $progress, $backline);
|
||||
} elseif (is_string($message)) {
|
||||
$this->process->message($message, $backline);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务进度
|
||||
* @param integer $total 记录总和
|
||||
* @param integer $count 当前记录
|
||||
* @param string $message 文字描述
|
||||
* @param integer $backline 回退行数
|
||||
* @return static
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function setQueueMessage(int $total, int $count, string $message = '', int $backline = 0): Command
|
||||
{
|
||||
$this->queue->message($total, $count, $message, $backline);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
324
plugin/think-library/src/Controller.php
Normal file
324
plugin/think-library/src/Controller.php
Normal file
@ -0,0 +1,324 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin;
|
||||
|
||||
use stdClass;
|
||||
use think\admin\extend\JwtExtend;
|
||||
use think\admin\helper\DeleteHelper;
|
||||
use think\admin\helper\FormHelper;
|
||||
use think\admin\helper\PageHelper;
|
||||
use think\admin\helper\QueryHelper;
|
||||
use think\admin\helper\SaveHelper;
|
||||
use think\admin\helper\TokenHelper;
|
||||
use think\admin\helper\ValidateHelper;
|
||||
use think\admin\service\NodeService;
|
||||
use think\admin\service\QueueService;
|
||||
use think\App;
|
||||
use think\db\BaseQuery;
|
||||
use think\exception\HttpResponseException;
|
||||
use think\Model;
|
||||
use think\Request;
|
||||
|
||||
/**
|
||||
* 标准控制器基类
|
||||
* @class Controller
|
||||
* @package think\admin
|
||||
*/
|
||||
class Controller extends stdClass
|
||||
{
|
||||
|
||||
/**
|
||||
* 应用容器
|
||||
* @var App
|
||||
*/
|
||||
public $app;
|
||||
|
||||
/**
|
||||
* 请求GET参数
|
||||
* @var array
|
||||
*/
|
||||
public $get = [];
|
||||
|
||||
/**
|
||||
* 当前功能节点
|
||||
* @var string
|
||||
*/
|
||||
public $node;
|
||||
|
||||
/**
|
||||
* 请求参数对象
|
||||
* @var Request
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* 表单CSRF验证状态
|
||||
* @var boolean
|
||||
*/
|
||||
public $csrf_state = false;
|
||||
|
||||
/**
|
||||
* 表单CSRF验证消息
|
||||
* @var string
|
||||
*/
|
||||
public $csrf_message;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param App $app
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
if (in_array($app->request->action(), get_class_methods(__CLASS__))) {
|
||||
$this->error('禁止访问内置方法!');
|
||||
}
|
||||
$this->get = $app->request->get();
|
||||
$this->app = $app->bind('think\admin\Controller', $this);
|
||||
$this->node = NodeService::getCurrent();
|
||||
$this->request = $this->app->request;
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制器初始化
|
||||
*/
|
||||
protected function initialize()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回失败的内容
|
||||
* @param mixed $info 消息内容
|
||||
* @param mixed $data 返回数据
|
||||
* @param mixed $code 返回代码
|
||||
*/
|
||||
public function error($info, $data = '{-null-}', $code = 0): void
|
||||
{
|
||||
$this->success($info, $data, $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回成功的内容
|
||||
* @param mixed $info 消息内容
|
||||
* @param mixed $data 返回数据
|
||||
* @param mixed $code 返回代码
|
||||
*/
|
||||
public function success($info, $data = '{-null-}', $code = 1): void
|
||||
{
|
||||
if ($data === '{-null-}') $data = new stdClass();
|
||||
$result = ['code' => $code, 'info' => is_string($info) ? lang($info) : $info, 'data' => $data];
|
||||
if (JwtExtend::isRejwt()) $result['token'] = JwtExtend::token();
|
||||
throw new HttpResponseException(json($result));
|
||||
}
|
||||
|
||||
/**
|
||||
* URL重定向
|
||||
* @param string $url 跳转链接
|
||||
* @param integer $code 跳转代码
|
||||
*/
|
||||
public function redirect(string $url, int $code = 302): void
|
||||
{
|
||||
throw new HttpResponseException(redirect($url, $code));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回视图内容
|
||||
* @param string $tpl 模板名称
|
||||
* @param array $vars 模板变量
|
||||
* @param null|string $node 授权节点
|
||||
*/
|
||||
public function fetch(string $tpl = '', array $vars = [], ?string $node = null): void
|
||||
{
|
||||
if (JwtExtend::$sessionId) {
|
||||
JwtExtend::fetch($this, $vars);
|
||||
} else {
|
||||
foreach ($this as $name => $value) {
|
||||
$vars[$name] = $value;
|
||||
}
|
||||
if ($this->csrf_state) {
|
||||
TokenHelper::fetch($tpl, $vars, $node);
|
||||
} else {
|
||||
throw new HttpResponseException(view($tpl, $vars));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模板变量赋值
|
||||
* @param mixed $name 要显示的模板变量
|
||||
* @param mixed $value 变量的值
|
||||
* @return $this
|
||||
*/
|
||||
public function assign($name, $value = ''): Controller
|
||||
{
|
||||
if (is_string($name)) {
|
||||
$this->$name = $value;
|
||||
} elseif (is_array($name)) {
|
||||
foreach ($name as $k => $v) {
|
||||
if (is_string($k)) $this->$k = $v;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据回调处理机制
|
||||
* @param string $name 回调方法名称
|
||||
* @param mixed $one 回调引用参数1
|
||||
* @param mixed $two 回调引用参数2
|
||||
* @param mixed $thr 回调引用参数3
|
||||
* @return boolean
|
||||
*/
|
||||
public function callback(string $name, &$one = [], &$two = [], &$thr = []): bool
|
||||
{
|
||||
if (is_callable($name)) return call_user_func($name, $this, $one, $two, $thr);
|
||||
foreach (["_{$this->app->request->action()}{$name}", $name] as $method) {
|
||||
if (method_exists($this, $method) && false === $this->$method($one, $two, $thr)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷查询逻辑器
|
||||
* @param BaseQuery|Model|string $dbQuery
|
||||
* @param array|string|null $input
|
||||
* @return QueryHelper
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
protected function _query($dbQuery, $input = null): QueryHelper
|
||||
{
|
||||
return QueryHelper::instance()->init($dbQuery, $input);
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷分页逻辑器
|
||||
* @param BaseQuery|Model|string $dbQuery
|
||||
* @param boolean|integer $page 是否分页或指定分页
|
||||
* @param boolean $display 是否渲染模板
|
||||
* @param boolean|integer $total 集合分页记录数
|
||||
* @param integer $limit 集合每页记录数
|
||||
* @param string $template 模板文件名称
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
protected function _page($dbQuery, $page = true, bool $display = true, $total = false, int $limit = 0, string $template = ''): array
|
||||
{
|
||||
return PageHelper::instance()->init($dbQuery, $page, $display, $total, $limit, $template);
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷表单逻辑器
|
||||
* @param BaseQuery|Model|string $dbQuery
|
||||
* @param string $template 模板名称
|
||||
* @param string $field 指定数据主键
|
||||
* @param mixed $where 额外更新条件
|
||||
* @param array $data 表单扩展数据
|
||||
* @return array|boolean
|
||||
* @throws \think\admin\Exception
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
protected function _form($dbQuery, string $template = '', string $field = '', $where = [], array $data = [])
|
||||
{
|
||||
return FormHelper::instance()->init($dbQuery, $template, $field, $where, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷输入并验证( 支持 规则 # 别名 )
|
||||
* @param array $rules 验证规则( 验证信息数组 )
|
||||
* @param string|array $type 输入方式 ( post. 或 get. )
|
||||
* @param callable|null $callable 异常处理操作
|
||||
* @return array
|
||||
*/
|
||||
protected function _vali(array $rules, $type = '', ?callable $callable = null): array
|
||||
{
|
||||
return ValidateHelper::instance()->init($rules, $type, $callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷更新逻辑器
|
||||
* @param BaseQuery|Model|string $dbQuery
|
||||
* @param array $data 表单扩展数据
|
||||
* @param string $field 数据对象主键
|
||||
* @param mixed $where 额外更新条件
|
||||
* @return boolean
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
protected function _save($dbQuery, array $data = [], string $field = '', $where = []): bool
|
||||
{
|
||||
return SaveHelper::instance()->init($dbQuery, $data, $field, $where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷删除逻辑器
|
||||
* @param BaseQuery|Model|string $dbQuery
|
||||
* @param string $field 数据对象主键
|
||||
* @param mixed $where 额外更新条件
|
||||
* @return boolean
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
protected function _delete($dbQuery, string $field = '', $where = []): bool
|
||||
{
|
||||
return DeleteHelper::instance()->init($dbQuery, $field, $where);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查表单令牌验证
|
||||
* @param boolean $return 是否返回结果
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _applyFormToken(bool $return = false): bool
|
||||
{
|
||||
return TokenHelper::instance()->init($return);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建异步任务并返回任务编号
|
||||
* @param string $title 任务名称
|
||||
* @param string $command 执行内容
|
||||
* @param integer $later 延时执行时间
|
||||
* @param array $data 任务附加数据
|
||||
* @param integer $rscript 任务类型(0单例,1多例)
|
||||
* @param integer $loops 循环等待时间
|
||||
*/
|
||||
protected function _queue(string $title, string $command, int $later = 0, array $data = [], int $rscript = 0, int $loops = 0)
|
||||
{
|
||||
try {
|
||||
$queue = QueueService::register($title, $command, $later, $data, $rscript, $loops);
|
||||
$this->success('创建任务成功!', $queue->code);
|
||||
} catch (Exception $exception) {
|
||||
$code = $exception->getData();
|
||||
if (is_string($code) && stripos($code, 'Q') === 0) {
|
||||
$this->success('任务已经存在,无需再次创建!', $code);
|
||||
} else {
|
||||
$this->error($exception->getMessage());
|
||||
}
|
||||
} catch (HttpResponseException $exception) {
|
||||
throw $exception;
|
||||
} catch (\Exception $exception) {
|
||||
trace_file($exception);
|
||||
$this->error(lang('创建任务失败,%s', [$exception->getMessage()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
65
plugin/think-library/src/Exception.php
Normal file
65
plugin/think-library/src/Exception.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin;
|
||||
|
||||
/**
|
||||
* 自定义数据异常
|
||||
* @class Exception
|
||||
* @package think\admin
|
||||
*/
|
||||
class Exception extends \Exception
|
||||
{
|
||||
/**
|
||||
* 异常数据对象
|
||||
* @var mixed
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* Exception constructor.
|
||||
* @param string $message
|
||||
* @param integer $code
|
||||
* @param mixed $data
|
||||
*/
|
||||
public function __construct($message = "", $code = 0, $data = [])
|
||||
{
|
||||
parent::__construct($message);
|
||||
$this->code = $code;
|
||||
$this->data = $data;
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取异常停止数据
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置异常停止数据
|
||||
* @param mixed $data
|
||||
*/
|
||||
public function setData($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
||||
124
plugin/think-library/src/Helper.php
Normal file
124
plugin/think-library/src/Helper.php
Normal file
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin;
|
||||
|
||||
use think\admin\extend\VirtualModel;
|
||||
use think\App;
|
||||
use think\Container;
|
||||
use think\db\BaseQuery;
|
||||
use think\db\Mongo;
|
||||
use think\db\Query;
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 控制器助手
|
||||
* @class Helper
|
||||
* @package think\admin
|
||||
*/
|
||||
abstract class Helper
|
||||
{
|
||||
/**
|
||||
* 应用容器
|
||||
* @var App
|
||||
*/
|
||||
public $app;
|
||||
|
||||
/**
|
||||
* 控制器实例
|
||||
* @var Controller
|
||||
*/
|
||||
public $class;
|
||||
|
||||
/**
|
||||
* 当前请求方式
|
||||
* @var string
|
||||
*/
|
||||
public $method;
|
||||
|
||||
/**
|
||||
* 自定输出格式
|
||||
* @var string
|
||||
*/
|
||||
public $output;
|
||||
|
||||
/**
|
||||
* Helper constructor.
|
||||
* @param App $app
|
||||
* @param Controller $class
|
||||
*/
|
||||
public function __construct(App $app, Controller $class)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->class = $class;
|
||||
// 计算指定输出格式
|
||||
$output = $app->request->request('output', 'default');
|
||||
$method = $app->request->method() ?: ($app->runningInConsole() ? 'cli' : 'nil');
|
||||
$this->output = strtolower("{$method}.{$output}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例对象反射
|
||||
* @param array $args
|
||||
* @return static
|
||||
*/
|
||||
public static function instance(...$args): Helper
|
||||
{
|
||||
return Container::getInstance()->invokeClass(static::class, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库查询对象
|
||||
* @param BaseQuery|Model|string $query
|
||||
* @return Query|Mongo|BaseQuery
|
||||
*/
|
||||
public static function buildQuery($query)
|
||||
{
|
||||
if (is_string($query)) {
|
||||
return static::buildModel($query)->db();
|
||||
}
|
||||
if ($query instanceof Model) return $query->db();
|
||||
if ($query instanceof BaseQuery && !$query->getModel()) {
|
||||
$name = $query->getConfig('name') ?: '';
|
||||
if (is_string($name) && strlen($name) > 0) {
|
||||
$name = config("database.connections.{$name}") ? $name : '';
|
||||
}
|
||||
$query->model(static::buildModel($query->getName(), [], $name));
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态创建模型对象
|
||||
* @param mixed $name 模型名称
|
||||
* @param array $data 初始数据
|
||||
* @param mixed $conn 指定连接
|
||||
* @return Model
|
||||
*/
|
||||
public static function buildModel(string $name, array $data = [], string $conn = ''): Model
|
||||
{
|
||||
if (strpos($name, '\\') !== false) {
|
||||
if (class_exists($name)) {
|
||||
$model = new $name($data);
|
||||
if ($model instanceof Model) return $model;
|
||||
}
|
||||
$name = basename(str_replace('\\', '/', $name));
|
||||
}
|
||||
return VirtualModel::mk($name, $data, $conn);
|
||||
}
|
||||
}
|
||||
155
plugin/think-library/src/Library.php
Normal file
155
plugin/think-library/src/Library.php
Normal file
@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin;
|
||||
|
||||
use think\admin\service\RuntimeService;
|
||||
use think\admin\support\command\Database;
|
||||
use think\admin\support\command\Package;
|
||||
use think\admin\support\command\Publish;
|
||||
use think\admin\support\command\Queue;
|
||||
use think\admin\support\command\Replace;
|
||||
use think\admin\support\command\Sysmenu;
|
||||
use think\admin\support\middleware\JwtSession;
|
||||
use think\admin\support\middleware\MultAccess;
|
||||
use think\admin\support\middleware\RbacAccess;
|
||||
use think\exception\HttpResponseException;
|
||||
use think\middleware\LoadLangPack;
|
||||
use think\Request;
|
||||
use think\Response;
|
||||
use think\Service;
|
||||
|
||||
/**
|
||||
* 模块注册服务
|
||||
* @class Library
|
||||
* @package think\admin
|
||||
*/
|
||||
class Library extends Service
|
||||
{
|
||||
/** @var \think\App */
|
||||
public static $sapp;
|
||||
|
||||
/**
|
||||
* 启动服务
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
// 静态应用赋值
|
||||
static::$sapp = $this->app;
|
||||
|
||||
// 注册 ThinkAdmin 指令
|
||||
$this->commands([
|
||||
Queue::class,
|
||||
Package::class,
|
||||
Sysmenu::class,
|
||||
Publish::class,
|
||||
Replace::class,
|
||||
Database::class,
|
||||
]);
|
||||
|
||||
// 动态应用运行参数
|
||||
RuntimeService::apply();
|
||||
|
||||
// 请求初始化处理
|
||||
$this->app->event->listen('HttpRun', function (Request $request) {
|
||||
|
||||
// 运行环境配置同步
|
||||
RuntimeService::sync();
|
||||
|
||||
// 配置默认输入过滤
|
||||
$request->filter([static function ($value) {
|
||||
return is_string($value) ? xss_safe($value) : $value;
|
||||
}]);
|
||||
|
||||
// 判断访问模式兼容处理
|
||||
if ($this->app->runningInConsole()) {
|
||||
// 兼容 CLI 访问控制器
|
||||
if (empty($_SERVER['REQUEST_URI']) && isset($_SERVER['argv'][1])) {
|
||||
$request->setPathinfo($_SERVER['argv'][1]);
|
||||
}
|
||||
} else {
|
||||
// 兼容 HTTP 调用 Console 后 URL 问题
|
||||
$request->setHost($request->host());
|
||||
}
|
||||
|
||||
// 注册多应用中间键
|
||||
$this->app->middleware->add(MultAccess::class);
|
||||
});
|
||||
|
||||
// 请求结束后处理
|
||||
$this->app->event->listen('HttpEnd', static function () {
|
||||
function_exists('sysvar') && sysvar('', '');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化服务
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// 动态加载全局配置
|
||||
[$dir, $ext] = [$this->app->getBasePath(), $this->app->getConfigExt()];
|
||||
foreach (glob("{$dir}*/sys{$ext}") as $file) include_once $file;
|
||||
if (is_file($file = "{$dir}common{$ext}")) include_once $file;
|
||||
if (is_file($file = "{$dir}provider{$ext}")) $this->app->bind(include $file);
|
||||
if (is_file($file = "{$dir}event{$ext}")) $this->app->loadEvent(include $file);
|
||||
if (is_file($file = "{$dir}middleware{$ext}")) $this->app->middleware->import(include $file, 'app');
|
||||
|
||||
// 终端 HTTP 访问时特殊处理
|
||||
if (!$this->app->runningInConsole()) {
|
||||
// 动态注释 CORS 跨域处理
|
||||
$this->app->middleware->add(function (Request $request, \Closure $next): Response {
|
||||
$header = ['X-Frame-Options' => $this->app->config->get('app.cors_frame') ?: 'sameorigin'];
|
||||
// HTTP.CORS 跨域规则配置
|
||||
if ($this->app->config->get('app.cors_on', true) && ($origin = $request->header('origin', '-')) !== '-') {
|
||||
if (is_string($hosts = $this->app->config->get('app.cors_host', []))) $hosts = str2arr($hosts);
|
||||
if (empty($hosts) || in_array(parse_url(strtolower($origin), PHP_URL_HOST), $hosts)) {
|
||||
$headers = $this->app->config->get('app.cors_headers', 'Api-Name,Api-Type,Api-Token,Jwt-Token,User-Form-Token,User-Token,Token');
|
||||
$header['Access-Control-Allow-Origin'] = $origin;
|
||||
$header['Access-Control-Allow-Methods'] = $this->app->config->get('app.cors_methods', 'GET,PUT,POST,PATCH,DELETE');
|
||||
$header['Access-Control-Allow-Headers'] = "Authorization,Content-Type,If-Match,If-Modified-Since,If-None-Match,If-Unmodified-Since,X-Requested-With,{$headers}";
|
||||
$header['Access-Control-Allow-Credentials'] = 'true';
|
||||
$header['Access-Control-Expose-Headers'] = $headers;
|
||||
}
|
||||
}
|
||||
// 跨域预请求状态处理
|
||||
if ($request->isOptions()) {
|
||||
throw new HttpResponseException(response()->code(204)->header($header));
|
||||
} else {
|
||||
return $next($request)->header($header);
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化会话和语言包
|
||||
$isapi = $this->app->request->header('api-token') !== null;
|
||||
$agent = preg_replace('|\s+|', '', $this->app->request->header('user-agent', ''));
|
||||
$isrpc = is_numeric(stripos($agent, 'think-admin-jsonrpc')) || is_numeric(stripos($agent, 'PHPYarRPC'));
|
||||
if (empty($isapi) && empty($isrpc) && empty($this->app->request->get('not_init_session'))) {
|
||||
// 非接口模式,注册会话中间键
|
||||
$this->app->middleware->add(JwtSession::class);
|
||||
// 启用会话后,注册语言包中间键
|
||||
$this->app->middleware->add(LoadLangPack::class);
|
||||
}
|
||||
|
||||
// 注册权限验证中间键
|
||||
$this->app->middleware->add(RbacAccess::class, 'route');
|
||||
}
|
||||
}
|
||||
}
|
||||
117
plugin/think-library/src/Model.php
Normal file
117
plugin/think-library/src/Model.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin;
|
||||
|
||||
use think\admin\helper\QueryHelper;
|
||||
|
||||
/**
|
||||
* 基础模型类
|
||||
* @class Model
|
||||
* @mixin \think\db\Query
|
||||
* @package think\admin
|
||||
*
|
||||
* --- 静态助手调用
|
||||
* @method static bool mSave(array $data = [], string $field = '', mixed $where = []) 快捷更新
|
||||
* @method static bool mDelete(string $field = '', mixed $where = []) 快捷删除
|
||||
* @method static bool|array mForm(string $template = '', string $field = '', mixed $where = [], array $data = []) 快捷表单
|
||||
* @method static bool|integer mUpdate(array $data = [], string $field = '', mixed $where = []) 快捷保存
|
||||
* @method static QueryHelper mQuery($input = null, callable $callable = null) 快捷查询
|
||||
*/
|
||||
abstract class Model extends \think\Model
|
||||
{
|
||||
/**
|
||||
* 日志类型
|
||||
* @var string
|
||||
*/
|
||||
protected $oplogType;
|
||||
|
||||
/**
|
||||
* 日志名称
|
||||
* @var string
|
||||
*/
|
||||
protected $oplogName;
|
||||
|
||||
/**
|
||||
* 日志过滤
|
||||
* @var callable
|
||||
*/
|
||||
public static $oplogCall;
|
||||
|
||||
/**
|
||||
* 创建模型实例
|
||||
* @template t of static
|
||||
* @param mixed $data
|
||||
* @return t|static
|
||||
*/
|
||||
public static function mk($data = [])
|
||||
{
|
||||
return new static($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建查询实例
|
||||
* @param array $data
|
||||
* @return \think\db\Query|\think\db\Mongo
|
||||
*/
|
||||
public static function mq(array $data = [])
|
||||
{
|
||||
return static::mk($data)->newQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用魔术方法
|
||||
* @param string $method 方法名称
|
||||
* @param array $args 调用参数
|
||||
* @return $this|false|mixed
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
$oplogs = [
|
||||
'onAdminSave' => "修改%s[%s]状态",
|
||||
'onAdminUpdate' => "更新%s[%s]记录",
|
||||
'onAdminInsert' => "增加%s[%s]成功",
|
||||
"onAdminDelete" => "删除%s[%s]成功",
|
||||
];
|
||||
if (isset($oplogs[$method])) {
|
||||
if ($this->oplogType && $this->oplogName) {
|
||||
$changeIds = $args[0] ?? '';
|
||||
if (is_callable(static::$oplogCall)) {
|
||||
$changeIds = call_user_func(static::$oplogCall, $method, $changeIds, $this);
|
||||
}
|
||||
sysoplog($this->oplogType, lang($oplogs[$method], [lang($this->oplogName), $changeIds]));
|
||||
}
|
||||
return $this;
|
||||
} else {
|
||||
return parent::__call($method, $args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态魔术方法
|
||||
* @param string $method 方法名称
|
||||
* @param array $args 调用参数
|
||||
* @return mixed|false|integer|QueryHelper
|
||||
*/
|
||||
public static function __callStatic($method, $args)
|
||||
{
|
||||
return QueryHelper::make(static::class, $method, $args, function ($method, $args) {
|
||||
return parent::__callStatic($method, $args);
|
||||
});
|
||||
}
|
||||
}
|
||||
235
plugin/think-library/src/Plugin.php
Normal file
235
plugin/think-library/src/Plugin.php
Normal file
@ -0,0 +1,235 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin;
|
||||
|
||||
use think\admin\service\ModuleService;
|
||||
use think\admin\service\NodeService;
|
||||
use think\App;
|
||||
use think\Service;
|
||||
|
||||
/**
|
||||
* 插件注册服务
|
||||
*
|
||||
* @class Plugin
|
||||
* @package think\admin\service
|
||||
*
|
||||
* @method string getAppCode() static 获取插件编号
|
||||
* @method string getAppName() static 获取插件名称
|
||||
* @method string getAppPath() static 获取插件路径
|
||||
* @method string getAppSpace() static 获取插件空间名
|
||||
* @method string getAppPackage() static 获取插件安装包
|
||||
*/
|
||||
abstract class Plugin extends Service
|
||||
{
|
||||
/**
|
||||
* 必填,插件包名
|
||||
* @var string
|
||||
*/
|
||||
protected $package = '';
|
||||
|
||||
/**
|
||||
* 必填,插件编码
|
||||
* @var string
|
||||
*/
|
||||
protected $appCode = '';
|
||||
|
||||
/**
|
||||
* 必填,插件名称
|
||||
* @var string
|
||||
*/
|
||||
protected $appName = '';
|
||||
|
||||
/**
|
||||
* 可选,插件目录
|
||||
* @var string
|
||||
*/
|
||||
protected $appPath = '';
|
||||
|
||||
/**
|
||||
* 可选,插件别名
|
||||
* @var string
|
||||
*/
|
||||
protected $appAlias = '';
|
||||
|
||||
/**
|
||||
* 可选,命名空间
|
||||
* @var string
|
||||
*/
|
||||
protected $appSpace = '';
|
||||
|
||||
/**
|
||||
* 可选,注册服务
|
||||
* @var string
|
||||
*/
|
||||
protected $appService = '';
|
||||
|
||||
/**
|
||||
* 插件配置
|
||||
* @var array
|
||||
*/
|
||||
private static $addons = [];
|
||||
|
||||
/**
|
||||
* 自动注册插件
|
||||
* @param \think\App $app
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
|
||||
// 获取基础服务类
|
||||
$ref = new \ReflectionClass(static::class);
|
||||
|
||||
// 应用服务注册类
|
||||
if (empty($this->appService)) {
|
||||
$this->appService = static::class;
|
||||
}
|
||||
|
||||
// 应用命名空间名
|
||||
if (empty($this->appSpace)) {
|
||||
$this->appSpace = $ref->getNamespaceName();
|
||||
}
|
||||
|
||||
// 应用插件路径计算
|
||||
if (empty($this->appPath) || !is_dir($this->appPath)) {
|
||||
$this->appPath = dirname($ref->getFileName());
|
||||
}
|
||||
|
||||
// 应用插件包名计算
|
||||
if (empty($this->package) && ($path = $ref->getFileName())) {
|
||||
for ($level = 1; $level <= 3; $level++) {
|
||||
if (is_file($file = dirname($path, $level) . '/composer.json')) {
|
||||
$this->package = json_decode(file_get_contents($file), true)['name'] ?? '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 应用插件计算名称及别名
|
||||
$attr = explode('\\', $ref->getNamespaceName());
|
||||
if ($attr[0] === NodeService::space()) array_shift($attr);
|
||||
|
||||
$this->appCode = $this->appCode ?: join('-', $attr);
|
||||
if ($this->appCode === $this->appAlias) $this->appAlias = '';
|
||||
|
||||
if (is_dir($this->appPath)) {
|
||||
// 写入插件参数信息
|
||||
self::$addons[$this->appCode] = [
|
||||
'name' => $this->appName,
|
||||
'path' => realpath($this->appPath) . DIRECTORY_SEPARATOR,
|
||||
'alias' => $this->appAlias,
|
||||
'space' => $this->appSpace ?: NodeService::space($this->appCode),
|
||||
'package' => $this->package,
|
||||
'service' => $this->appService
|
||||
];
|
||||
// 插件别名动态设置
|
||||
if (!empty($this->appAlias) && $this->appCode !== $this->appAlias) {
|
||||
Library::$sapp->config->set([
|
||||
'app_map' => array_merge(Library::$sapp->config->get('app.app_map', []), [
|
||||
$this->appAlias => $this->appCode
|
||||
]),
|
||||
], 'app');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册应用启动
|
||||
* @return void
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件及安装信息
|
||||
* @param ?string $code 指定插件编号
|
||||
* @param boolean $append 关联安装数据
|
||||
* @return ?array
|
||||
*/
|
||||
public static function get(?string $code = null, bool $append = false): ?array
|
||||
{
|
||||
// 读取插件原始信息
|
||||
$data = empty($code) ? self::$addons : (self::$addons[$code] ?? null);
|
||||
if (empty($data) || empty($append)) return $data;
|
||||
// 关联插件安装信息
|
||||
$versions = ModuleService::getLibrarys();
|
||||
return empty($code) ? array_map(static function ($item) use ($versions) {
|
||||
$item['install'] = $versions[$item['package']] ?? [];
|
||||
if (empty($item['name'])) $item['name'] = $item['install']['name'] ?? '';
|
||||
return $item;
|
||||
}, $data) : $data + ['install' => $versions[$data['package']] ?? []];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象内部属性
|
||||
* @param string $name
|
||||
* @return null
|
||||
*/
|
||||
public function __get(string $name)
|
||||
{
|
||||
return $this->$name ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态调用方法兼容
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return array|string|null
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function __callStatic(string $method, array $arguments)
|
||||
{
|
||||
switch (strtolower($method)) {
|
||||
case 'all':
|
||||
return self::get(...$arguments);
|
||||
case 'getappcode':
|
||||
return app(static::class)->appCode;
|
||||
case 'getappname':
|
||||
return app(static::class)->appName;
|
||||
case 'getapppath':
|
||||
return app(static::class)->appPath;
|
||||
case 'getappspace':
|
||||
return app(static::class)->appSpace;
|
||||
case 'getapppackage';
|
||||
return app(static::class)->package;
|
||||
default:
|
||||
$class = basename(str_replace('\\', '/', static::class));
|
||||
throw new Exception("method not exists: {$class}::{$method}()");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 魔术方法调用兼容处理
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return array|null
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function __call(string $method, array $arguments)
|
||||
{
|
||||
return self::__callStatic($method, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义插件菜单
|
||||
* @return array 一级或二级菜单
|
||||
*/
|
||||
abstract public static function menu(): array;
|
||||
}
|
||||
127
plugin/think-library/src/Queue.php
Normal file
127
plugin/think-library/src/Queue.php
Normal file
@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin;
|
||||
|
||||
use think\admin\service\ProcessService;
|
||||
use think\admin\service\QueueService;
|
||||
use think\App;
|
||||
|
||||
/**
|
||||
* 任务基础类
|
||||
* @class Queue
|
||||
* @package think\admin
|
||||
*/
|
||||
abstract class Queue
|
||||
{
|
||||
/**
|
||||
* 应用实例
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 任务控制服务
|
||||
* @var QueueService
|
||||
*/
|
||||
protected $queue;
|
||||
|
||||
/**
|
||||
* 进程控制服务
|
||||
* @var ProcessService
|
||||
*/
|
||||
protected $process;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param App $app
|
||||
* @param ProcessService $process
|
||||
*/
|
||||
public function __construct(App $app, ProcessService $process)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->process = $process;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化任务数据
|
||||
* @param QueueService $queue
|
||||
* @return $this
|
||||
*/
|
||||
public function initialize(QueueService $queue): Queue
|
||||
{
|
||||
$this->queue = $queue;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务处理内容
|
||||
* @param array $data
|
||||
* @return void|string
|
||||
*/
|
||||
abstract public function execute(array $data = []);
|
||||
|
||||
/**
|
||||
* 设置失败的消息
|
||||
* @param string $message 消息内容
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function setQueueError(string $message)
|
||||
{
|
||||
$this->queue->error($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置成功的消息
|
||||
* @param string $message 消息内容
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function setQueueSuccess(string $message)
|
||||
{
|
||||
$this->queue->success($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务进度
|
||||
* @param integer $total 记录总和
|
||||
* @param integer $count 当前记录
|
||||
* @param string $message 文字描述
|
||||
* @param integer $backline 回退行数
|
||||
* @return static
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function setQueueMessage(int $total, int $count, string $message = '', int $backline = 0): Queue
|
||||
{
|
||||
$this->queue->message($total, $count, $message, $backline);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置任务的进度
|
||||
* @param ?string $message 进度消息
|
||||
* @param ?string $progress 进度数值
|
||||
* @param integer $backline 回退行数
|
||||
* @return Queue
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function setQueueProgress(?string $message = null, ?string $progress = null, int $backline = 0): Queue
|
||||
{
|
||||
$this->queue->progress(2, $message, $progress, $backline);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
64
plugin/think-library/src/Service.php
Normal file
64
plugin/think-library/src/Service.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin;
|
||||
|
||||
use think\App;
|
||||
use think\Container;
|
||||
|
||||
/**
|
||||
* 自定义服务基类
|
||||
* @class Service
|
||||
* @package think\admin
|
||||
*/
|
||||
abstract class Service
|
||||
{
|
||||
/**
|
||||
* 应用实例
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param App $app
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化服务
|
||||
*/
|
||||
protected function initialize()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态实例对象
|
||||
* @param array $var 实例参数
|
||||
* @param boolean $new 创建新实例
|
||||
* @return static|mixed
|
||||
*/
|
||||
public static function instance(array $var = [], bool $new = false)
|
||||
{
|
||||
return Container::getInstance()->make(static::class, $var, $new);
|
||||
}
|
||||
}
|
||||
203
plugin/think-library/src/Storage.php
Normal file
203
plugin/think-library/src/Storage.php
Normal file
@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin;
|
||||
|
||||
use think\admin\contract\StorageInterface;
|
||||
use think\admin\storage\LocalStorage;
|
||||
use think\Container;
|
||||
|
||||
/**
|
||||
* 文件存储引擎管理
|
||||
* @class Storage
|
||||
* @package think\admin
|
||||
* @method static array info($name, $safe = false, $attname = null) 文件存储信息
|
||||
* @method static array set($name, $file, $safe = false, $attname = null) 储存文件
|
||||
* @method static string url($name, $safe = false, $attname = null) 获取文件链接
|
||||
* @method static string get($name, $safe = false) 读取文件内容
|
||||
* @method static string path($name, $safe = false) 文件存储路径
|
||||
* @method static boolean del($name, $safe = false) 删除存储文件
|
||||
* @method static boolean has($name, $safe = false) 检查是否存在
|
||||
* @method static string upload() 获取上传地址
|
||||
*/
|
||||
abstract class Storage
|
||||
{
|
||||
|
||||
/**
|
||||
* 实例化存储操作对象
|
||||
* @param ?string $name 驱动名称
|
||||
* @param ?string $class 驱动类名
|
||||
* @return \think\admin\contract\StorageInterface
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function instance(?string $name = null, ?string $class = null): StorageInterface
|
||||
{
|
||||
try {
|
||||
if (is_null($class)) {
|
||||
$type = ucfirst(strtolower($name ?: sysconf('storage.type|raw')));
|
||||
$class = "think\\admin\\storage\\{$type}Storage";
|
||||
}
|
||||
if (class_exists($class)) return Container::getInstance()->make($class);
|
||||
throw new Exception("Storage driver [{$class}] does not exist.");
|
||||
} catch (Exception $exception) {
|
||||
throw $exception;
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception($exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件相对名称
|
||||
* @param string $url 文件访问链接
|
||||
* @param string $ext 文件后缀名称
|
||||
* @param string $pre 文件存储前缀
|
||||
* @param string $fun 名称规则方法
|
||||
* @return string
|
||||
*/
|
||||
public static function name(string $url, string $ext = '', string $pre = '', string $fun = 'md5'): string
|
||||
{
|
||||
[$hah, $ext] = [$fun($url), trim($ext ?: pathinfo($url, 4), '.\\/')];
|
||||
$attr = [trim($pre, '.\\/'), substr($hah, 0, 2), substr($hah, 2, 30)];
|
||||
return trim(join('/', $attr), '/') . '.' . strtolower($ext ?: 'tmp');
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件到本地
|
||||
* @param string $url 文件URL地址
|
||||
* @param boolean $force 是否强制下载
|
||||
* @param integer $expire 文件保留时间
|
||||
* @return array
|
||||
*/
|
||||
public static function down(string $url, bool $force = false, int $expire = 0): array
|
||||
{
|
||||
try {
|
||||
$local = LocalStorage::instance();
|
||||
$filename = static::name($url, '', 'down/');
|
||||
if (empty($force) && $local->has($filename)) {
|
||||
if ($expire < 1 || filemtime($local->path($filename)) + $expire > time()) {
|
||||
return $local->info($filename);
|
||||
}
|
||||
}
|
||||
return $local->set($filename, static::curlGet($url));
|
||||
} catch (\Exception $exception) {
|
||||
return ['url' => $url, 'hash' => md5($url), 'key' => $url, 'file' => $url];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后缀类型
|
||||
* @param array|string $exts 文件后缀
|
||||
* @param array $mime 文件信息
|
||||
* @return string
|
||||
*/
|
||||
public static function mime($exts, array $mime = []): string
|
||||
{
|
||||
$mimes = static::mimes();
|
||||
foreach (is_string($exts) ? explode(',', $exts) : $exts as $ext) {
|
||||
$mime[] = $mimes[strtolower($ext)] ?? 'application/octet-stream';
|
||||
}
|
||||
return join(',', array_unique($mime));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有类型
|
||||
* @return array
|
||||
*/
|
||||
public static function mimes(): array
|
||||
{
|
||||
static $mimes = [];
|
||||
if (count($mimes) > 0) return $mimes;
|
||||
return $mimes = include __DIR__ . '/storage/bin/mimes.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储类型
|
||||
* @return array
|
||||
*/
|
||||
public static function types(): array
|
||||
{
|
||||
return [
|
||||
'local' => lang('本地服务器存储'),
|
||||
'alist' => lang('自建Alist存储'),
|
||||
'qiniu' => lang('七牛云对象存储'),
|
||||
'upyun' => lang('又拍云USS存储'),
|
||||
'txcos' => lang('腾讯云COS存储'),
|
||||
'alioss' => lang('阿里云OSS存储'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用CURL读取网络资源
|
||||
* @param string $url 资源地址
|
||||
* @return string
|
||||
*/
|
||||
public static function curlGet(string $url): string
|
||||
{
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_HEADER, 0);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
$body = curl_exec($ch) ?: '';
|
||||
curl_close($ch);
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态访问启用
|
||||
* @param string $method 方法名称
|
||||
* @param array $arguments 调用参数
|
||||
* @return mixed
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function __callStatic(string $method, array $arguments)
|
||||
{
|
||||
if (method_exists($storage = static::instance(), $method)) {
|
||||
return call_user_func_array([$storage, $method], $arguments);
|
||||
} else {
|
||||
throw new Exception("method not exists: " . get_class($storage) . "->{$method}()");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片数据存储
|
||||
* @param string $base64 图片内容
|
||||
* @param string $prefix 保存前缀
|
||||
* @param boolean $safemode 安全模式
|
||||
* @return array [ url => URL ]
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function saveImage(string $base64, string $prefix = 'image', bool $safemode = false): array
|
||||
{
|
||||
if (preg_match('|^data:image/(.*?);base64,|i', $base64)) {
|
||||
[$ext, $img] = explode('|||', preg_replace('|^data:image/(.*?);base64,|i', '$1|||', $base64));
|
||||
$name = static::name($img, $ext, $prefix);
|
||||
if (empty($ext) || !in_array(strtolower($ext), ['gif', 'png', 'jpg', 'jpeg'])) {
|
||||
throw new Exception('内容格式异常!');
|
||||
} elseif ($safemode) {
|
||||
return LocalStorage::instance()->set($name, base64_decode($img), true);
|
||||
} else {
|
||||
return static::instance()->set($name, base64_decode($img));
|
||||
}
|
||||
} else {
|
||||
return ['url' => $base64];
|
||||
}
|
||||
}
|
||||
}
|
||||
462
plugin/think-library/src/common.php
Normal file
462
plugin/think-library/src/common.php
Normal file
@ -0,0 +1,462 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
use think\admin\extend\CodeExtend;
|
||||
use think\admin\extend\HttpExtend;
|
||||
use think\admin\Helper;
|
||||
use think\admin\helper\TokenHelper;
|
||||
use think\admin\Library;
|
||||
use think\admin\service\AdminService;
|
||||
use think\admin\service\QueueService;
|
||||
use think\admin\service\RuntimeService;
|
||||
use think\admin\service\SystemService;
|
||||
use think\admin\Storage;
|
||||
use think\db\Query;
|
||||
use think\helper\Str;
|
||||
use think\Model;
|
||||
|
||||
if (!function_exists('p')) {
|
||||
/**
|
||||
* 打印输出数据到文件
|
||||
* @param mixed $data 输出的数据
|
||||
* @param boolean $new 强制替换文件
|
||||
* @param ?string $file 保存文件名称
|
||||
* @return false|int
|
||||
*/
|
||||
function p($data, bool $new = false, ?string $file = null)
|
||||
{
|
||||
return SystemService::putDebug($data, $new, $file);
|
||||
}
|
||||
}
|
||||
if (!function_exists('m')) {
|
||||
/**
|
||||
* 动态创建模型对象
|
||||
* @param string $name 模型名称
|
||||
* @param array $data 初始数据
|
||||
* @param string $conn 指定连接
|
||||
* @return Model
|
||||
*/
|
||||
function m(string $name, array $data = [], string $conn = ''): Model
|
||||
{
|
||||
return Helper::buildModel($name, $data, $conn);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('auth')) {
|
||||
/**
|
||||
* 访问权限检查
|
||||
* @param ?string $node
|
||||
* @return boolean
|
||||
*/
|
||||
function auth(?string $node): bool
|
||||
{
|
||||
return AdminService::check($node);
|
||||
}
|
||||
}
|
||||
if (!function_exists('admuri')) {
|
||||
/**
|
||||
* 生成后台 URL 地址
|
||||
* @param string $url 路由地址
|
||||
* @param array $vars PATH 变量
|
||||
* @param boolean|string $suffix 后缀
|
||||
* @param boolean|string $domain 域名
|
||||
* @return string
|
||||
*/
|
||||
function admuri(string $url = '', array $vars = [], $suffix = true, $domain = false): string
|
||||
{
|
||||
return sysuri('admin/index/index') . '#' . url($url, $vars, $suffix, $domain)->build();
|
||||
}
|
||||
}
|
||||
if (!function_exists('sysvar')) {
|
||||
/**
|
||||
* 读写单次请求的内存缓存
|
||||
* @param null|string $name 数据名称
|
||||
* @param null|mixed $value 数据内容
|
||||
* @return null|array|mixed 返回内容
|
||||
*/
|
||||
function sysvar(?string $name = null, $value = null)
|
||||
{
|
||||
static $swap = [];
|
||||
if ($name === '' && $value === '') {
|
||||
return $swap = [];
|
||||
} elseif (is_null($value)) {
|
||||
return is_null($name) ? $swap : ($swap[$name] ?? null);
|
||||
} else {
|
||||
return $swap[$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!function_exists('sysuri')) {
|
||||
/**
|
||||
* 生成最短 URL 地址
|
||||
* @param string $url 路由地址
|
||||
* @param array $vars PATH 变量
|
||||
* @param boolean|string $suffix 后缀
|
||||
* @param boolean|string $domain 域名
|
||||
* @return string
|
||||
*/
|
||||
function sysuri(string $url = '', array $vars = [], $suffix = true, $domain = false): string
|
||||
{
|
||||
if (preg_match('#^(https?://|\\|/|@)#', $url)) {
|
||||
return Library::$sapp->route->buildUrl($url, $vars)->suffix($suffix)->domain($domain)->build();
|
||||
}
|
||||
if (count($attr = $url === '' ? [] : explode('/', rtrim($url, '/'))) < 3) {
|
||||
$map = [Library::$sapp->http->getName(), Library::$sapp->request->controller(), Library::$sapp->request->action(true)];
|
||||
while (count($attr) < 3) array_unshift($attr, $map[2 - count($attr)] ?? 'index');
|
||||
}
|
||||
$attr[1] = Str::snake($attr[1]);
|
||||
[$rcf, $tmp] = [Library::$sapp->config->get('route', []), uniqid('think_admin_replace_temp_vars_')];
|
||||
$map = [Str::lower($rcf['default_app'] ?? ''), Str::snake($rcf['default_controller'] ?? ''), Str::lower($rcf['default_action'] ?? '')];
|
||||
for ($idx = count($attr) - 1; $idx >= 0; $idx--) if ($attr[$idx] == ($map[$idx] ?: 'index')) $attr[$idx] = $tmp; else break;
|
||||
$url = Library::$sapp->route->buildUrl(join('/', $attr), $vars)->suffix($suffix)->domain($domain)->build();
|
||||
$ext = is_string($suffix) ? $suffix : ($rcf['url_html_suffix'] ?? 'html');
|
||||
$new = preg_replace("#/{$tmp}(\.{$ext})?#", '', $old = parse_url($url, PHP_URL_PATH) ?: '', -1, $count);
|
||||
$count > 0 && $suffix && $new && $ext !== '' && $new !== Library::$sapp->request->baseUrl() && $new .= ".{$ext}";
|
||||
return str_replace($old, $new ?: '/', $url);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('encode')) {
|
||||
/**
|
||||
* 加密 UTF8 字符串
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
function encode(string $content): string
|
||||
{
|
||||
[$chars, $length] = ['', strlen($string = CodeExtend::text2utf8($content))];
|
||||
for ($i = 0; $i < $length; $i++) $chars .= str_pad(base_convert(strval(ord($string[$i])), 10, 36), 2, '0', 0);
|
||||
return $chars;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('decode')) {
|
||||
/**
|
||||
* 解密 UTF8 字符串
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
function decode(string $content): string
|
||||
{
|
||||
$chars = '';
|
||||
foreach (str_split($content, 2) as $char) {
|
||||
$chars .= chr(intval(base_convert($char, 36, 10)));
|
||||
}
|
||||
return CodeExtend::text2utf8($chars);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('str2arr')) {
|
||||
/**
|
||||
* 字符串转数组
|
||||
* @param string $text 待转内容
|
||||
* @param string $separ 分隔字符
|
||||
* @param ?array $allow 限定规则
|
||||
* @return array
|
||||
*/
|
||||
function str2arr(string $text, string $separ = ',', ?array $allow = null): array
|
||||
{
|
||||
$items = [];
|
||||
foreach (explode($separ, trim($text, $separ)) as $item) {
|
||||
if ($item !== '' && (!is_array($allow) || in_array($item, $allow))) {
|
||||
$items[] = trim($item);
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
if (!function_exists('arr2str')) {
|
||||
/**
|
||||
* 数组转字符串
|
||||
* @param array $data 待转数组
|
||||
* @param string $separ 分隔字符
|
||||
* @param ?array $allow 限定规则
|
||||
* @return string
|
||||
*/
|
||||
function arr2str(array $data, string $separ = ',', ?array $allow = null): string
|
||||
{
|
||||
foreach ($data as $key => $item) {
|
||||
if ($item === '' || (is_array($allow) && !in_array($item, $allow))) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
return $separ . join($separ, $data) . $separ;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('isDebug')) {
|
||||
/**
|
||||
* 调试模式运行
|
||||
* @return boolean
|
||||
*/
|
||||
function isDebug(): bool
|
||||
{
|
||||
return RuntimeService::isDebug();
|
||||
}
|
||||
}
|
||||
if (!function_exists('isOnline')) {
|
||||
/**
|
||||
* 产品模式运行
|
||||
* @return boolean
|
||||
*/
|
||||
function isOnline(): bool
|
||||
{
|
||||
return RuntimeService::isOnline();
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('sysconf')) {
|
||||
/**
|
||||
* 获取或配置系统参数
|
||||
* @param string $name 参数名称
|
||||
* @param mixed $value 参数内容
|
||||
* @return mixed
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
function sysconf(string $name = '', $value = null)
|
||||
{
|
||||
if (is_null($value) && is_string($name)) {
|
||||
return SystemService::get($name);
|
||||
} else {
|
||||
return SystemService::set($name, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!function_exists('sysdata')) {
|
||||
/**
|
||||
* JSON 数据读取与存储
|
||||
* @param string $name 数据名称
|
||||
* @param mixed $value 数据内容
|
||||
* @return mixed
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
function sysdata(string $name, $value = null)
|
||||
{
|
||||
if (is_null($value)) {
|
||||
return SystemService::getData($name);
|
||||
} else {
|
||||
return SystemService::setData($name, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!function_exists('syspath')) {
|
||||
/**
|
||||
* 获取文件绝对路径
|
||||
* @param string $name 文件路径
|
||||
* @param ?string $root 程序根路径
|
||||
* @return string
|
||||
*/
|
||||
function syspath(string $name = '', ?string $root = null): string
|
||||
{
|
||||
if (is_null($root)) $root = Library::$sapp->getRootPath();
|
||||
$attr = ['/' => DIRECTORY_SEPARATOR, '\\' => DIRECTORY_SEPARATOR];
|
||||
return rtrim($root, '\\/') . DIRECTORY_SEPARATOR . ltrim(strtr($name, $attr), '\\/');
|
||||
}
|
||||
}
|
||||
if (!function_exists('sysoplog')) {
|
||||
/**
|
||||
* 写入系统日志
|
||||
* @param string $action 日志行为
|
||||
* @param string $content 日志内容
|
||||
* @return boolean
|
||||
*/
|
||||
function sysoplog(string $action, string $content): bool
|
||||
{
|
||||
return SystemService::setOplog($action, $content);
|
||||
}
|
||||
}
|
||||
if (!function_exists('systoken')) {
|
||||
/**
|
||||
* 生成 CSRF-TOKEN 参数
|
||||
* @return string
|
||||
*/
|
||||
function systoken(): string
|
||||
{
|
||||
return TokenHelper::token();
|
||||
}
|
||||
}
|
||||
if (!function_exists('sysqueue')) {
|
||||
/**
|
||||
* 注册异步处理任务
|
||||
* @param string $title 任务名称
|
||||
* @param string $command 执行内容
|
||||
* @param integer $later 延时执行时间
|
||||
* @param array $data 任务附加数据
|
||||
* @param integer $rscript 任务类型(0单例,1多例)
|
||||
* @param integer $loops 循环等待时间
|
||||
* @return string
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
function sysqueue(string $title, string $command, int $later = 0, array $data = [], int $rscript = 1, int $loops = 0): string
|
||||
{
|
||||
return QueueService::register($title, $command, $later, $data, $rscript, $loops)->code;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('enbase64url')) {
|
||||
/**
|
||||
* Base64安全URL编码
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
function enbase64url(string $string): string
|
||||
{
|
||||
return CodeExtend::enSafe64($string);
|
||||
}
|
||||
}
|
||||
if (!function_exists('debase64url')) {
|
||||
/**
|
||||
* Base64安全URL解码
|
||||
* @param string $string
|
||||
* @return string
|
||||
*/
|
||||
function debase64url(string $string): string
|
||||
{
|
||||
return CodeExtend::deSafe64($string);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('xss_safe')) {
|
||||
/**
|
||||
* 文本内容XSS过滤
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
function xss_safe(string $text): string
|
||||
{
|
||||
// 将所有 onxxx= 中的字母 o 替换为符号 ο,注意它不是字母
|
||||
$rules = ['#<script.*?<\/script>#is' => '', '#(\s)on(\w+=\S)#i' => '$1οn$2'];
|
||||
return preg_replace(array_keys($rules), array_values($rules), trim($text));
|
||||
}
|
||||
}
|
||||
if (!function_exists('http_get')) {
|
||||
/**
|
||||
* 以 get 模拟网络请求
|
||||
* @param string $url HTTP请求URL地址
|
||||
* @param array|string $query GET请求参数
|
||||
* @param array $options CURL参数
|
||||
* @return boolean|string
|
||||
*/
|
||||
function http_get(string $url, $query = [], array $options = [])
|
||||
{
|
||||
return HttpExtend::get($url, $query, $options);
|
||||
}
|
||||
}
|
||||
if (!function_exists('http_post')) {
|
||||
/**
|
||||
* 以 post 模拟网络请求
|
||||
* @param string $url HTTP请求URL地址
|
||||
* @param array|string $data POST请求数据
|
||||
* @param array $options CURL参数
|
||||
* @return boolean|string
|
||||
*/
|
||||
function http_post(string $url, $data, array $options = [])
|
||||
{
|
||||
return HttpExtend::post($url, $data, $options);
|
||||
}
|
||||
}
|
||||
if (!function_exists('data_save')) {
|
||||
/**
|
||||
* 数据增量保存
|
||||
* @param Model|Query|string $dbQuery
|
||||
* @param array $data 需要保存或更新的数据
|
||||
* @param string $key 条件主键限制
|
||||
* @param mixed $where 其它的where条件
|
||||
* @return boolean|integer
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
function data_save($dbQuery, array $data, string $key = 'id', $where = [])
|
||||
{
|
||||
return SystemService::save($dbQuery, $data, $key, $where);
|
||||
}
|
||||
}
|
||||
if (!function_exists('down_file')) {
|
||||
/**
|
||||
* 下载远程文件到本地
|
||||
* @param string $source 远程文件地址
|
||||
* @param boolean $force 是否强制重新下载
|
||||
* @param integer $expire 强制本地存储时间
|
||||
* @return string
|
||||
*/
|
||||
function down_file(string $source, bool $force = false, int $expire = 0): string
|
||||
{
|
||||
return Storage::down($source, $force, $expire)['url'] ?? $source;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('trace_file')) {
|
||||
/**
|
||||
* 输出异常数据到文件
|
||||
* @param \Exception $exception
|
||||
* @return boolean
|
||||
*/
|
||||
function trace_file(Exception $exception): bool
|
||||
{
|
||||
$path = Library::$sapp->getRuntimePath() . 'trace';
|
||||
if (!is_dir($path)) mkdir($path, 0777, true);
|
||||
$name = substr($exception->getFile(), strlen(syspath()));
|
||||
$file = $path . DIRECTORY_SEPARATOR . date('Ymd_His_') . strtr($name, ['/' => '.', '\\' => '.']);
|
||||
$json = json_encode($exception instanceof \think\admin\Exception ? $exception->getData() : [], 64 | 128 | 256);
|
||||
$class = get_class($exception);
|
||||
return false !== file_put_contents($file,
|
||||
"[CODE] {$exception->getCode()}" . PHP_EOL .
|
||||
"[INFO] {$exception->getMessage()}" . PHP_EOL .
|
||||
($exception instanceof \think\admin\Exception ? "[DATA] {$json}" . PHP_EOL : '') .
|
||||
"[FILE] {$class} in {$name} line {$exception->getLine()}" . PHP_EOL .
|
||||
"[TIME] " . date('Y-m-d H:i:s') . PHP_EOL . PHP_EOL .
|
||||
'[TRACE]' . PHP_EOL . $exception->getTraceAsString()
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!function_exists('format_bytes')) {
|
||||
/**
|
||||
* 文件字节单位转换
|
||||
* @param string|integer $size
|
||||
* @return string
|
||||
*/
|
||||
function format_bytes($size): string
|
||||
{
|
||||
if (is_numeric($size)) {
|
||||
$units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
|
||||
return round($size, 2) . ' ' . $units[$i];
|
||||
} else {
|
||||
return $size;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!function_exists('format_datetime')) {
|
||||
/**
|
||||
* 日期格式标准输出
|
||||
* @param int|string $datetime 输入日期
|
||||
* @param string $format 输出格式
|
||||
* @return string
|
||||
*/
|
||||
function format_datetime($datetime, string $format = 'Y年m月d日 H:i:s'): string
|
||||
{
|
||||
if (empty($datetime)) {
|
||||
return '-';
|
||||
} elseif (is_numeric($datetime)) {
|
||||
return date(lang($format), intval($datetime));
|
||||
} elseif ($timestamp = strtotime($datetime)) {
|
||||
return date(lang($format), $timestamp);
|
||||
} else {
|
||||
return $datetime;
|
||||
}
|
||||
}
|
||||
}
|
||||
99
plugin/think-library/src/contract/StorageInterface.php
Normal file
99
plugin/think-library/src/contract/StorageInterface.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\contract;
|
||||
|
||||
/**
|
||||
* 文件存储标准接口
|
||||
* @class StorageInterface
|
||||
* @package think\admin\contract
|
||||
*/
|
||||
interface StorageInterface
|
||||
{
|
||||
/**
|
||||
* 上传文件内容
|
||||
* @param string $name 文件名称
|
||||
* @param string $file 文件内容
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
*/
|
||||
public function set(string $name, string $file, bool $safe = false, ?string $attname = null): array;
|
||||
|
||||
/**
|
||||
* 读取文件内容
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return string
|
||||
*/
|
||||
public function get(string $name, bool $safe = false): string;
|
||||
|
||||
/**
|
||||
* 删除存储文件
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return boolean
|
||||
*/
|
||||
public function del(string $name, bool $safe = false): bool;
|
||||
|
||||
/**
|
||||
* 判断是否存在
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return boolean
|
||||
*/
|
||||
public function has(string $name, bool $safe = false): bool;
|
||||
|
||||
/**
|
||||
* 获取访问地址
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return string
|
||||
*/
|
||||
public function url(string $name, bool $safe = false, ?string $attname = null): string;
|
||||
|
||||
/**
|
||||
* 获取存储路径
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return string
|
||||
*/
|
||||
public function path(string $name, bool $safe = false): string;
|
||||
|
||||
/**
|
||||
* 获取文件信息
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
*/
|
||||
public function info(string $name, bool $safe = false, ?string $attname = null): array;
|
||||
|
||||
/**
|
||||
* 获取上传地址
|
||||
* @return string
|
||||
*/
|
||||
public function upload(): string;
|
||||
|
||||
/**
|
||||
* 获取存储区域
|
||||
* @return array
|
||||
*/
|
||||
public static function region(): array;
|
||||
}
|
||||
143
plugin/think-library/src/contract/StorageUsageTrait.php
Normal file
143
plugin/think-library/src/contract/StorageUsageTrait.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\contract;
|
||||
|
||||
use think\admin\Exception;
|
||||
use think\App;
|
||||
use think\Container;
|
||||
|
||||
/**
|
||||
* 文件存储公共属性
|
||||
* @class StorageUsageTrait
|
||||
* @package think\admin\contract
|
||||
*/
|
||||
trait StorageUsageTrait
|
||||
{
|
||||
/**
|
||||
* @var \think\App $app
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 链接类型
|
||||
* @var string
|
||||
*/
|
||||
protected $link;
|
||||
|
||||
/**
|
||||
* 链接前缀
|
||||
* @var string
|
||||
*/
|
||||
protected $domain;
|
||||
|
||||
/**
|
||||
* 存储器构造方法
|
||||
* @param \think\App $app
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->link = sysconf('storage.link_type|raw');
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义初始化方法
|
||||
* @return void
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象实例
|
||||
* @return static
|
||||
*/
|
||||
public static function instance()
|
||||
{
|
||||
/** @var \think\admin\contract\StorageInterface */
|
||||
return Container::getInstance()->make(static::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下载链接后缀
|
||||
* @param null|string $attname 下载名称
|
||||
* @param null|string $filename 文件名称
|
||||
* @return string
|
||||
*/
|
||||
protected function getSuffix(?string $attname = null, ?string $filename = null): string
|
||||
{
|
||||
[$class, $suffix] = [class_basename(get_class($this)), ''];
|
||||
if (is_string($filename) && stripos($this->link, 'compress') !== false) {
|
||||
$compress = [
|
||||
'LocalStorage' => '',
|
||||
'QiniuStorage' => '?imageslim',
|
||||
'UpyunStorage' => '!/format/webp',
|
||||
'TxcosStorage' => '?imageMogr2/format/webp',
|
||||
'AliossStorage' => '?x-oss-process=image/format,webp',
|
||||
];
|
||||
$extens = strtolower(pathinfo($this->delSuffix($filename), PATHINFO_EXTENSION));
|
||||
$suffix = in_array($extens, ['png', 'jpg', 'jpeg']) ? ($compress[$class] ?? '') : '';
|
||||
}
|
||||
if (is_string($attname) && strlen($attname) > 0 && stripos($this->link, 'full') !== false) {
|
||||
if ($class === 'UpyunStorage') {
|
||||
$suffix .= ($suffix ? '&' : '?') . '_upd=' . urlencode($attname);
|
||||
} else {
|
||||
$suffix .= ($suffix ? '&' : '?') . 'attname=' . urlencode($attname);
|
||||
}
|
||||
}
|
||||
return $suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件基础名称
|
||||
* @param string $name 文件名称
|
||||
* @return string
|
||||
*/
|
||||
protected function delSuffix(string $name): string
|
||||
{
|
||||
if (strpos($name, '?') !== false) {
|
||||
return strstr($name, '?', true);
|
||||
}
|
||||
if (stripos($name, '!') !== false) {
|
||||
return strstr($name, '!', true);
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重构后兼容处理
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return array|string
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function __call(string $method, array $arguments)
|
||||
{
|
||||
if (strtolower($method) === 'builduploadtoken') {
|
||||
if (method_exists($this, 'token')) {
|
||||
return $this->token(...$arguments);
|
||||
}
|
||||
}
|
||||
// 调用方法异常处理
|
||||
$class = class_basename(static::class);
|
||||
throw new Exception("method not exists: {$class}->{$method}()");
|
||||
}
|
||||
}
|
||||
85
plugin/think-library/src/contract/StreamInterface.php
Normal file
85
plugin/think-library/src/contract/StreamInterface.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\contract;
|
||||
|
||||
/**
|
||||
* 流协议接口
|
||||
* @class StreamInterface
|
||||
* @package think\admin\contract
|
||||
*/
|
||||
interface StreamInterface
|
||||
{
|
||||
public function dir_closedir(): bool;
|
||||
|
||||
public function dir_opendir(string $path, int $options): bool;
|
||||
|
||||
public function dir_readdir(): string;
|
||||
|
||||
public function dir_rewinddir(): bool;
|
||||
|
||||
public function mkdir(string $path, int $mode, int $options): bool;
|
||||
|
||||
public function rename(string $path_from, string $path_to): bool;
|
||||
|
||||
public function rmdir(string $path, int $options): bool;
|
||||
|
||||
public function stream_cast(int $cast_as);
|
||||
|
||||
public function stream_close(): void;
|
||||
|
||||
public function stream_eof(): bool;
|
||||
|
||||
public function stream_flush(): bool;
|
||||
|
||||
public function stream_lock(int $operation): bool;
|
||||
|
||||
public function stream_metadata(string $path, int $option, $value): bool;
|
||||
|
||||
public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool;
|
||||
|
||||
/**
|
||||
* @param int $count
|
||||
* @return string|false
|
||||
*/
|
||||
public function stream_read(int $count);
|
||||
|
||||
public function stream_seek(int $offset, int $whence = SEEK_SET): bool;
|
||||
|
||||
public function stream_set_option(int $option, int $arg1, int $arg2): bool;
|
||||
|
||||
/**
|
||||
* @return array|false
|
||||
*/
|
||||
public function stream_stat();
|
||||
|
||||
public function stream_tell(): int;
|
||||
|
||||
public function stream_truncate(int $new_size): bool;
|
||||
|
||||
public function stream_write(string $data): int;
|
||||
|
||||
public function unlink(string $path): bool;
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param integer $flags
|
||||
* @return array|false
|
||||
*/
|
||||
public function url_stat(string $path, int $flags);
|
||||
}
|
||||
168
plugin/think-library/src/extend/CodeExtend.php
Normal file
168
plugin/think-library/src/extend/CodeExtend.php
Normal file
@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\extend;
|
||||
|
||||
/**
|
||||
* 随机数码管理扩展
|
||||
* @class CodeExtend
|
||||
* @package think\admin\extend
|
||||
*/
|
||||
class CodeExtend
|
||||
{
|
||||
|
||||
/**
|
||||
* 生成 UUID 编码
|
||||
* @return string
|
||||
*/
|
||||
public static function uuid(): string
|
||||
{
|
||||
$chars = md5(uniqid(strval(mt_rand(0, 9999)), true));
|
||||
$value = substr($chars, 0, 8) . '-' . substr($chars, 8, 4) . '-';
|
||||
$value .= substr($chars, 12, 4) . '-' . substr($chars, 16, 4) . '-';
|
||||
return strtoupper($value . substr($chars, 20, 12));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机编码
|
||||
* @param integer $size 编码长度
|
||||
* @param integer $type 编码类型(1纯数字,2纯字母,3数字字母)
|
||||
* @param string $prefix 编码前缀
|
||||
* @return string
|
||||
*/
|
||||
public static function random(int $size = 10, int $type = 1, string $prefix = ''): string
|
||||
{
|
||||
$numbs = '0123456789';
|
||||
$chars = 'abcdefghijklmnopqrstuvwxyz';
|
||||
if ($type === 1) $chars = $numbs;
|
||||
if ($type === 3) $chars = "{$numbs}{$chars}";
|
||||
$code = $prefix . $chars[rand(1, strlen($chars) - 1)];
|
||||
while (strlen($code) < $size) $code .= $chars[rand(0, strlen($chars) - 1)];
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成日期编码
|
||||
* @param integer $size 编码长度
|
||||
* @param string $prefix 编码前缀
|
||||
* @return string
|
||||
*/
|
||||
public static function uniqidDate(int $size = 16, string $prefix = ''): string
|
||||
{
|
||||
if ($size < 14) $size = 14;
|
||||
$code = $prefix . date('Ymd') . (date('H') + date('i')) . date('s');
|
||||
while (strlen($code) < $size) $code .= rand(0, 9);
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成数字编码
|
||||
* @param integer $size 编码长度
|
||||
* @param string $prefix 编码前缀
|
||||
* @return string
|
||||
*/
|
||||
public static function uniqidNumber(int $size = 12, string $prefix = ''): string
|
||||
{
|
||||
$time = strval(time());
|
||||
if ($size < 10) $size = 10;
|
||||
$code = $prefix . (intval($time[0]) + intval($time[1])) . substr($time, 2) . rand(0, 9);
|
||||
while (strlen($code) < $size) $code .= rand(0, 9);
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本转码
|
||||
* @param string $text 文本内容
|
||||
* @param string $target 目标编码
|
||||
* @return string
|
||||
*/
|
||||
public static function text2utf8(string $text, string $target = 'UTF-8'): string
|
||||
{
|
||||
[$first2, $first4] = [substr($text, 0, 2), substr($text, 0, 4)];
|
||||
if ($first4 === chr(0x00) . chr(0x00) . chr(0xFE) . chr(0xFF)) $ft = 'UTF-32BE';
|
||||
elseif ($first4 === chr(0xFF) . chr(0xFE) . chr(0x00) . chr(0x00)) $ft = 'UTF-32LE';
|
||||
elseif ($first2 === chr(0xFE) . chr(0xFF)) $ft = 'UTF-16BE';
|
||||
elseif ($first2 === chr(0xFF) . chr(0xFE)) $ft = 'UTF-16LE';
|
||||
return mb_convert_encoding($text, $target, $ft ?? mb_detect_encoding($text));
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据加密处理
|
||||
* @param mixed $data 加密数据
|
||||
* @param string $skey 安全密钥
|
||||
* @return string
|
||||
*/
|
||||
public static function encrypt($data, string $skey): string
|
||||
{
|
||||
$iv = static::random(16, 3);
|
||||
$value = openssl_encrypt(serialize($data), 'AES-256-CBC', $skey, 0, $iv);
|
||||
return static::enSafe64(json_encode(['iv' => $iv, 'value' => $value]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据解密处理
|
||||
* @param string $data 解密数据
|
||||
* @param string $skey 安全密钥
|
||||
* @return mixed
|
||||
*/
|
||||
public static function decrypt(string $data, string $skey)
|
||||
{
|
||||
$attr = json_decode(static::deSafe64($data), true);
|
||||
return unserialize(openssl_decrypt($attr['value'], 'AES-256-CBC', $skey, 0, $attr['iv']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64Url 安全编码
|
||||
* @param string $text 待加密文本
|
||||
* @return string
|
||||
*/
|
||||
public static function enSafe64(string $text): string
|
||||
{
|
||||
return rtrim(strtr(base64_encode($text), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64Url 安全解码
|
||||
* @param string $text 待解密文本
|
||||
* @return string
|
||||
*/
|
||||
public static function deSafe64(string $text): string
|
||||
{
|
||||
return base64_decode(str_pad(strtr($text, '-_', '+/'), strlen($text) % 4, '='));
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩数据对象
|
||||
* @param mixed $data
|
||||
* @return string
|
||||
*/
|
||||
public static function enzip($data): string
|
||||
{
|
||||
return static::enSafe64(gzcompress(serialize($data)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压数据对象
|
||||
* @param string $string
|
||||
* @return mixed
|
||||
*/
|
||||
public static function dezip(string $string)
|
||||
{
|
||||
return unserialize(gzuncompress(static::deSafe64($string)));
|
||||
}
|
||||
}
|
||||
91
plugin/think-library/src/extend/DataExtend.php
Normal file
91
plugin/think-library/src/extend/DataExtend.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\extend;
|
||||
|
||||
/**
|
||||
* 数据处理扩展
|
||||
* @class DataExtend
|
||||
* @package think\admin\extend
|
||||
*/
|
||||
class DataExtend
|
||||
{
|
||||
/**
|
||||
* 二维数组转多维数据树
|
||||
* @param array $list 待处理数据
|
||||
* @param string $ckey 自己的主键
|
||||
* @param string $pkey 上级的主键
|
||||
* @param string $chil 子数组名称
|
||||
* @return array
|
||||
*/
|
||||
public static function arr2tree(array $list, string $ckey = 'id', string $pkey = 'pid', string $chil = 'sub'): array
|
||||
{
|
||||
[$tree, $list] = [[], array_column($list, null, $ckey)];
|
||||
foreach ($list as $it) isset($list[$it[$pkey]]) ? $list[$it[$pkey]][$chil][] = &$list[$it[$ckey]] : $tree[] = &$list[$it[$ckey]];
|
||||
return $tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 二维数组转数据树表
|
||||
* @param array $list 待处理数据
|
||||
* @param string $ckey 自己的主键
|
||||
* @param string $pkey 上级的主键
|
||||
* @param string $path 当前 PATH
|
||||
* @return array
|
||||
*/
|
||||
public static function arr2table(array $list, string $ckey = 'id', string $pkey = 'pid', string $path = 'path'): array
|
||||
{
|
||||
$build = static function (array $nodes, callable $build, array &$data = [], string $parent = '') use ($ckey, $pkey, $path) {
|
||||
foreach ($nodes as $node) {
|
||||
$subs = $node['sub'] ?? [];
|
||||
unset($node['sub']);
|
||||
$node[$path] = "{$parent}-{$node[$ckey]}";
|
||||
$node['spc'] = count($subs);
|
||||
$node['spt'] = substr_count($parent, '-');
|
||||
$node['spl'] = str_repeat('ㅤ├ㅤ', $node['spt']);
|
||||
$node['sps'] = ",{$node[$ckey]},";
|
||||
array_walk_recursive($subs, static function ($val, $key) use ($ckey, &$node) {
|
||||
if ($key === $ckey) $node['sps'] .= "{$val},";
|
||||
});
|
||||
$node['spp'] = arr2str(str2arr(strtr($parent . $node['sps'], '-', ',')));
|
||||
$data[] = $node;
|
||||
if (empty($subs)) continue;
|
||||
$build($subs, $build, $data, $node[$path]);
|
||||
}
|
||||
return $data;
|
||||
};
|
||||
return $build(static::arr2tree($list, $ckey, $pkey), $build);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据树子ID集合
|
||||
* @param array $list 数据列表
|
||||
* @param mixed $value 起始有效ID值
|
||||
* @param string $ckey 当前主键ID名称
|
||||
* @param string $pkey 上级主键ID名称
|
||||
* @return array
|
||||
*/
|
||||
public static function getArrSubIds(array $list, $value = 0, string $ckey = 'id', string $pkey = 'pid'): array
|
||||
{
|
||||
$ids = [intval($value)];
|
||||
foreach ($list as $vo) if (intval($vo[$pkey]) > 0 && intval($vo[$pkey]) === intval($value)) {
|
||||
$ids = array_merge($ids, static::getArrSubIds($list, intval($vo[$ckey]), $ckey, $pkey));
|
||||
}
|
||||
return $ids;
|
||||
}
|
||||
}
|
||||
81
plugin/think-library/src/extend/ExcelExtend.php
Normal file
81
plugin/think-library/src/extend/ExcelExtend.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\extend;
|
||||
|
||||
/**
|
||||
* 导出 CSV 文件扩展
|
||||
* @class ExcelExtend
|
||||
* @deprecated 改用 JavaScript
|
||||
* @package think\admin\extend
|
||||
*/
|
||||
class ExcelExtend
|
||||
{
|
||||
|
||||
/**
|
||||
* 设置写入 CSV 文件头部
|
||||
* @param string $name 导出文件名称
|
||||
* @param array $headers 表格头部(一维数组)
|
||||
*/
|
||||
public static function header(string $name, array $headers): void
|
||||
{
|
||||
header('Content-Type: application/octet-stream');
|
||||
header("Content-Disposition: attachment; filename=" . iconv('utf-8', 'gbk//TRANSLIT', $name));
|
||||
$handle = fopen('php://output', 'w');
|
||||
foreach ($headers as $key => $value) {
|
||||
$headers[$key] = iconv("utf-8", "gbk//TRANSLIT", $value);
|
||||
}
|
||||
fputcsv($handle, $headers);
|
||||
if (is_resource($handle)) {
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置写入CSV文件内容
|
||||
* @param array $list 数据列表(二维数组)
|
||||
* @param array $rules 数据规则(一维数组)
|
||||
*/
|
||||
public static function body(array $list, array $rules): void
|
||||
{
|
||||
$handle = fopen('php://output', 'w');
|
||||
foreach ($list as $data) {
|
||||
$rows = [];
|
||||
foreach ($rules as $rule) {
|
||||
$rows[] = static::parseKeyDotValue($data, $rule);
|
||||
}
|
||||
fputcsv($handle, $rows);
|
||||
}
|
||||
if (is_resource($handle)) {
|
||||
fclose($handle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据数组key查询(可带点规则)
|
||||
* @param array $data 数据
|
||||
* @param string $rule 规则,如: order.order_no
|
||||
* @return string
|
||||
*/
|
||||
public static function parseKeyDotValue(array $data, string $rule): string
|
||||
{
|
||||
[$temp, $attr] = [$data, explode('.', trim($rule, '.'))];
|
||||
while ($key = array_shift($attr)) $temp = $temp[$key] ?? $temp;
|
||||
return (is_string($temp) || is_numeric($temp)) ? @iconv('utf-8', 'gbk//TRANSLIT', "{$temp}") : '';
|
||||
}
|
||||
}
|
||||
204
plugin/think-library/src/extend/FaviconExtend.php
Normal file
204
plugin/think-library/src/extend/FaviconExtend.php
Normal file
@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\extend;
|
||||
|
||||
use think\admin\Exception;
|
||||
|
||||
/**
|
||||
* 网站 ICO 文件生成工具
|
||||
* @class FaviconExtend
|
||||
* @package think\admin\extend
|
||||
*/
|
||||
class FaviconExtend
|
||||
{
|
||||
/**
|
||||
* 转换后的 BMP 图像
|
||||
* @var array
|
||||
*/
|
||||
private $images = [];
|
||||
|
||||
/**
|
||||
* Constructor - 创建一个新的 ICO 生成器
|
||||
* @param ?string $file 源图像文件的路径
|
||||
* @param array $size 图片文件尺寸 [W1,H1]
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
function __construct(?string $file = null, array $size = [])
|
||||
{
|
||||
$functions = [
|
||||
'imagesx',
|
||||
'imagesy',
|
||||
'getimagesize',
|
||||
'imagesavealpha',
|
||||
'imagecreatefromstring',
|
||||
'imagecreatetruecolor',
|
||||
'imagecolortransparent',
|
||||
'imagecolorallocatealpha',
|
||||
'imagecopyresampled',
|
||||
'imagealphablending',
|
||||
];
|
||||
|
||||
foreach ($functions as $function) if (!function_exists($function)) {
|
||||
throw new Exception(lang('Required %s function not found.', [$function]));
|
||||
}
|
||||
|
||||
if (is_string($file)) {
|
||||
$this->addImage($file, $size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加图像到生成器中
|
||||
*
|
||||
* @param string $file 图像文件路径
|
||||
* @param array $size 图像文件尺寸
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function addImage(string $file, array $size = []): FaviconExtend
|
||||
{
|
||||
if (false === ($im = $this->loadImageFile($file))) {
|
||||
throw new Exception(lang('Read picture file Failed.'));
|
||||
}
|
||||
if (empty($size)) {
|
||||
$size = [imagesx($im), imagesy($im)];
|
||||
}
|
||||
|
||||
[$width, $height] = $size;
|
||||
$image = imagecreatetruecolor($width, $height);
|
||||
imagecolortransparent($image, imagecolorallocatealpha($image, 0, 0, 0, 127));
|
||||
imagealphablending($image, false);
|
||||
imagesavealpha($image, true);
|
||||
|
||||
[$sourceWidth, $sourceHeight] = [imagesx($im), imagesy($im)];
|
||||
if (false === imagecopyresampled($image, $im, 0, 0, 0, 0, $width, $height, $sourceWidth, $sourceHeight)) {
|
||||
throw new Exception(lang('Parse and process picture Failed.'));
|
||||
}
|
||||
$this->addImageData($image);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 ICO 内容写入到文件
|
||||
*
|
||||
* @param string $file 写入文件路径
|
||||
* @return boolean
|
||||
*/
|
||||
public function saveIco(string $file): bool
|
||||
{
|
||||
if (false === ($data = $this->getIcoData())) {
|
||||
return false;
|
||||
}
|
||||
if (false === ($fh = fopen($file, 'w'))) {
|
||||
return false;
|
||||
}
|
||||
if (false === (fwrite($fh, $data))) {
|
||||
fclose($fh);
|
||||
return false;
|
||||
}
|
||||
fclose($fh);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成并获取 ICO 图像数据
|
||||
*/
|
||||
private function getIcoData()
|
||||
{
|
||||
if (!is_array($this->images) || empty($this->images)) {
|
||||
return false;
|
||||
}
|
||||
[$pixelData, $entrySize] = ['', 16];
|
||||
$data = pack('vvv', 0, 1, count($this->images));
|
||||
$offset = 6 + ($entrySize * count($this->images));
|
||||
foreach ($this->images as $image) {
|
||||
$data .= pack('CCCCvvVV', $image['width'], $image['height'], $image['colors'], 0, 1, $image['pixel'], $image['size'], $offset);
|
||||
$pixelData .= $image['data'];
|
||||
$offset += $image['size'];
|
||||
}
|
||||
$data .= $pixelData;
|
||||
unset($pixelData);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 GD 图像转为 BMP 格式
|
||||
*/
|
||||
private function addImageData($im)
|
||||
{
|
||||
[$width, $height] = [imagesx($im), imagesy($im)];
|
||||
[$pixelData, $opacityData, $opacityValue] = [[], [], 0];
|
||||
for ($y = $height - 1; $y >= 0; $y--) {
|
||||
for ($x = 0; $x < $width; $x++) {
|
||||
$color = imagecolorat($im, $x, $y);
|
||||
$alpha = ($color & 0x7F000000) >> 24;
|
||||
$alpha = (1 - ($alpha / 127)) * 255;
|
||||
$color &= 0xFFFFFF;
|
||||
$color |= 0xFF000000 & (intval($alpha) << 24);
|
||||
$pixelData[] = $color;
|
||||
$opacity = ($alpha <= 127) ? 1 : 0;
|
||||
$opacityValue = ($opacityValue << 1) | $opacity;
|
||||
if ((($x + 1) % 32) == 0) {
|
||||
$opacityData[] = $opacityValue;
|
||||
$opacityValue = 0;
|
||||
}
|
||||
}
|
||||
if (($x % 32) > 0) {
|
||||
while (($x++ % 32) > 0) {
|
||||
$opacityValue = $opacityValue << 1;
|
||||
}
|
||||
$opacityData[] = $opacityValue;
|
||||
$opacityValue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$imageHeaderSize = 40;
|
||||
$colorMaskSize = $width * $height * 4;
|
||||
$opacityMaskSize = (ceil($width / 32) * 4) * $height;
|
||||
$data = pack('VVVvvVVVVVV', 40, $width, ($height * 2), 1, 32, 0, 0, 0, 0, 0, 0);
|
||||
foreach ($pixelData as $color) $data .= pack('V', $color);
|
||||
foreach ($opacityData as $opacity) $data .= pack('N', $opacity);
|
||||
|
||||
$this->images[] = [
|
||||
'data' => $data,
|
||||
'size' => $imageHeaderSize + $colorMaskSize + $opacityMaskSize,
|
||||
'width' => $width, 'height' => $height,
|
||||
'pixel' => 32, 'colors' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取图片资源
|
||||
* @param string $file 文件路径
|
||||
* @return false|resource|\GdImage
|
||||
*/
|
||||
private function loadImageFile(string $file)
|
||||
{
|
||||
if (false === getimagesize($file)) {
|
||||
return false;
|
||||
}
|
||||
if (false === ($data = file_get_contents($file))) {
|
||||
return false;
|
||||
}
|
||||
if (false === ($image = @imagecreatefromstring($data))) {
|
||||
return false;
|
||||
}
|
||||
unset($data);
|
||||
return $image;
|
||||
}
|
||||
}
|
||||
176
plugin/think-library/src/extend/HttpExtend.php
Normal file
176
plugin/think-library/src/extend/HttpExtend.php
Normal file
@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\extend;
|
||||
|
||||
/**
|
||||
* CURL模拟请求扩展
|
||||
* @class HttpExtend
|
||||
* @package think\admin\extend
|
||||
*/
|
||||
class HttpExtend
|
||||
{
|
||||
/**
|
||||
* 以 GET 模拟网络请求
|
||||
* @param string $location HTTP请求地址
|
||||
* @param array|string $data GET请求参数
|
||||
* @param array $options CURL请求参数
|
||||
* @return boolean|string
|
||||
*/
|
||||
public static function get(string $location, $data = [], array $options = [])
|
||||
{
|
||||
$options['query'] = $data;
|
||||
return static::request('get', $location, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 以 POST 模拟网络请求
|
||||
* @param string $location HTTP请求地址
|
||||
* @param array|string $data POST请求数据
|
||||
* @param array $options CURL请求参数
|
||||
* @return boolean|string
|
||||
*/
|
||||
public static function post(string $location, $data = [], array $options = [])
|
||||
{
|
||||
$options['data'] = $data;
|
||||
return static::request('post', $location, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 以 FormData 模拟网络请求
|
||||
* @param string $url 模拟请求地址
|
||||
* @param array $data 模拟请求参数数据
|
||||
* @param array $file 提交文件 [field,name,type,content]
|
||||
* @param array $header 请求头部信息,默认带 Content-type
|
||||
* @param string $method 模拟请求的方式 [GET,POST,PUT]
|
||||
* @param boolean $returnHeader 是否返回头部信息
|
||||
* @return boolean|string
|
||||
*/
|
||||
public static function submit(string $url, array $data = [], array $file = [], array $header = [], string $method = 'POST', bool $returnHeader = true)
|
||||
{
|
||||
[$line, $boundary] = [[], CodeExtend::random(18)];
|
||||
foreach ($data as $key => $value) {
|
||||
$line[] = "--{$boundary}";
|
||||
$line[] = "Content-Disposition: form-data; name=\"{$key}\"";
|
||||
$line[] = "";
|
||||
$line[] = $value;
|
||||
}
|
||||
if (is_array($file) && isset($file['field']) && isset($file['name'])) {
|
||||
$line[] = "--{$boundary}";
|
||||
$line[] = "Content-Disposition: form-data; name=\"{$file['field']}\"; filename=\"{$file['name']}\"";
|
||||
if (isset($file['type'])) $line[] = "Content-Type: \"{$file['type']}\"";
|
||||
$line[] = "";
|
||||
$line[] = $file['content'];
|
||||
}
|
||||
$line[] = "--{$boundary}--";
|
||||
$header[] = "Content-type:multipart/form-data;boundary={$boundary}";
|
||||
return static::request($method, $url, ['data' => join("\r\n", $line), 'returnHeader' => $returnHeader, 'headers' => $header]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 以 CURL 模拟网络请求
|
||||
* @param string $method 模拟请求方式
|
||||
* @param string $location 模拟请求地址
|
||||
* @param array $options 请求参数[headers,query,data,cookie,cookie_file,timeout,returnHeader]
|
||||
* @return boolean|string
|
||||
*/
|
||||
public static function request(string $method, string $location, array $options = [])
|
||||
{
|
||||
// GET 参数设置
|
||||
if (!empty($options['query'])) {
|
||||
$location .= strpos($location, '?') !== false ? '&' : '?';
|
||||
if (is_array($options['query'])) {
|
||||
$location .= http_build_query($options['query']);
|
||||
} elseif (is_string($options['query'])) {
|
||||
$location .= $options['query'];
|
||||
}
|
||||
}
|
||||
$curl = curl_init();
|
||||
// Agent 代理设置
|
||||
curl_setopt($curl, CURLOPT_USERAGENT, $options['agent'] ?? static::getUserAgent());
|
||||
// Cookie 信息设置
|
||||
if (!empty($options['cookie'])) {
|
||||
curl_setopt($curl, CURLOPT_COOKIE, $options['cookie']);
|
||||
}
|
||||
// Header 头信息设置
|
||||
if (!empty($options['headers'])) {
|
||||
curl_setopt($curl, CURLOPT_HTTPHEADER, $options['headers']);
|
||||
}
|
||||
if (!empty($options['cookie_file'])) {
|
||||
curl_setopt($curl, CURLOPT_COOKIEJAR, $options['cookie_file']);
|
||||
curl_setopt($curl, CURLOPT_COOKIEFILE, $options['cookie_file']);
|
||||
}
|
||||
// 设置请求方式
|
||||
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, strtoupper($method));
|
||||
if (strtolower($method) === 'head') {
|
||||
curl_setopt($curl, CURLOPT_NOBODY, 1);
|
||||
} elseif (isset($options['data'])) {
|
||||
curl_setopt($curl, CURLOPT_POST, 1);
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, $options['data']);
|
||||
}
|
||||
// 请求超时设置
|
||||
if (isset($options['timeout']) && is_numeric($options['timeout'])) {
|
||||
curl_setopt($curl, CURLOPT_TIMEOUT, $options['timeout']);
|
||||
} else {
|
||||
curl_setopt($curl, CURLOPT_TIMEOUT, 60);
|
||||
}
|
||||
// 是否返回前部内容
|
||||
if (empty($options['returnHeader'])) {
|
||||
curl_setopt($curl, CURLOPT_HEADER, false);
|
||||
} else {
|
||||
curl_setopt($curl, CURLOPT_HEADER, true);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
}
|
||||
// 自定义扩展参数配置,二维数组内每个单元为一个设置语句,格式如下:
|
||||
// $setopt = [ [CURLOPT_URL, $location], [CURLOPT_AUTOREFERER, true], ...];
|
||||
if (isset($options['setopt']) && is_array($options['setopt'])) {
|
||||
foreach ($options['setopt'] as $value) if (is_array($value)) {
|
||||
curl_setopt($curl, ...$value);
|
||||
}
|
||||
}
|
||||
curl_setopt($curl, CURLOPT_URL, $location);
|
||||
curl_setopt($curl, CURLOPT_AUTOREFERER, true);
|
||||
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
|
||||
$content = curl_exec($curl);
|
||||
curl_close($curl);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取浏览器代理信息
|
||||
* @return string
|
||||
*/
|
||||
private static function getUserAgent(): string
|
||||
{
|
||||
$agents = [
|
||||
"Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0",
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; InfoPath.3; rv:11.0) like Gecko",
|
||||
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
|
||||
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)",
|
||||
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
|
||||
];
|
||||
return $agents[array_rand($agents)];
|
||||
}
|
||||
}
|
||||
261
plugin/think-library/src/extend/ImageVerify.php
Normal file
261
plugin/think-library/src/extend/ImageVerify.php
Normal file
@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\extend;
|
||||
|
||||
use think\admin\Library;
|
||||
use think\admin\Storage;
|
||||
use think\admin\storage\LocalStorage;
|
||||
|
||||
/**
|
||||
* 拼图拖拽验证器
|
||||
* @class ImageVerify
|
||||
* @package think\admin\extend
|
||||
*/
|
||||
class ImageVerify
|
||||
{
|
||||
// 浮层圆半径
|
||||
private $r = 10;
|
||||
|
||||
// 图片路径
|
||||
private $srcImage;
|
||||
|
||||
// 浮层图宽高
|
||||
private $picWidth = 100;
|
||||
private $picHeight = 100;
|
||||
|
||||
// 目标图宽高
|
||||
private $dstWidth = 600;
|
||||
private $dstHeight = 300;
|
||||
|
||||
/**
|
||||
* 验证器构造方法
|
||||
* @param string $image 原始图片
|
||||
* @param array $options 配置参数
|
||||
*/
|
||||
public function __construct(string $image, array $options = [])
|
||||
{
|
||||
if (!empty($options)) {
|
||||
foreach ($options as $k => $v) {
|
||||
if (isset($this->$k)) $this->$k = $v;
|
||||
}
|
||||
}
|
||||
$this->srcImage = $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成图片拼图
|
||||
* @param string $image 原始图片
|
||||
* @param integer $time 缓存时间
|
||||
* @param integer $diff 容错数值
|
||||
* @param integer $retry 容错次数
|
||||
* @return array [code, bgimg, water]
|
||||
*/
|
||||
public static function render(string $image, int $time = 1800, int $diff = 10, int $retry = 3): array
|
||||
{
|
||||
$data = (new static($image))->create();
|
||||
$range = [$data['point'] - $diff, $data['point'] + $diff];
|
||||
$result = ['retry' => $retry, 'error' => 0, 'expire' => time() + $time, 'range' => $range];
|
||||
Library::$sapp->cache->set($code = CodeExtend::uniqidNumber(16, 'V'), $result, $time);
|
||||
return ['code' => $code, 'bgimg' => $data['bgimg'], 'water' => $data['water']];
|
||||
}
|
||||
|
||||
/**
|
||||
* 在线验证是否通过
|
||||
* @param string $code 验证码编码
|
||||
* @param string $value 待验证数值
|
||||
* @param boolean $clear 验证成功清理
|
||||
* @return integer [ -1:需要刷新, 0:验证失败, 1:验证成功 ]
|
||||
*/
|
||||
public static function verify(string $code, string $value, bool $clear = false): int
|
||||
{
|
||||
$cache = Library::$sapp->cache->get($code);
|
||||
if (empty($cache['range']) || empty($cache['retry'])) return -1;
|
||||
if ($cache['range'][0] <= $value && $value <= $cache['range'][1]) {
|
||||
$clear && Library::$sapp->cache->delete($code);
|
||||
return 1;
|
||||
}
|
||||
// 验证失败记录次数
|
||||
if (++$cache['error'] < $cache['retry']) {
|
||||
if (($tll = $cache['expire'] - time()) > 0) {
|
||||
Library::$sapp->cache->set($code, $cache, $tll);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// 其他异常直接清空
|
||||
Library::$sapp->cache->delete($code);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 剧中裁剪图片
|
||||
* @param string $image 图片资源
|
||||
* @param integer $width 目标宽度
|
||||
* @param integer $height 目标高度
|
||||
* @return \GdImage|resource
|
||||
*/
|
||||
public static function cover(string $image, int $width, int $height)
|
||||
{
|
||||
// 读取缓存返回图片资源
|
||||
$local = LocalStorage::instance();
|
||||
$name = Storage::name(join('#', func_get_args()), 'png', 'cache');
|
||||
if ($local->has($name, true)) return imageCreateFromString($local->get($name, true));
|
||||
// 计算图片尺寸裁剪坐标
|
||||
[$w, $h] = getimagesize($image);
|
||||
if ($w > $h) {
|
||||
[$_sw, $_sh, $_sx, $_sy] = [$h, $h, intval(($w - $h) / 2), 0];
|
||||
} elseif ($w < $h) {
|
||||
[$_sw, $_sh, $_sx, $_sy] = [$w, $w, 0, intval(($h - $w) / 2)];
|
||||
} else {
|
||||
[$_sw, $_sh, $_sx, $_sy] = [$w, $h, 0, 0];
|
||||
}
|
||||
$newim = imageCreateTrueColor($width, $height);
|
||||
$srcim = imageCreateFromString(file_get_contents($image));
|
||||
imagecopyresampled($newim, $srcim, 0, 0, $_sx, $_sy, $width, $height, $_sw, $_sh);
|
||||
imagedestroy($srcim);
|
||||
// 缓存图片内容
|
||||
$file = $local->path($name, true);
|
||||
is_dir($path = dirname($file)) || mkdir($path, 0755, true);
|
||||
imagepng($newim, $file);
|
||||
// 返回新图片资源
|
||||
return $newim;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建背景图和浮层图、浮层图X坐标
|
||||
* @return array [point, bgimg, water]
|
||||
*/
|
||||
public function create(): array
|
||||
{
|
||||
// 创建目标背景图画布
|
||||
$dstim = $this->cover($this->srcImage, $this->dstWidth, $this->dstHeight);
|
||||
|
||||
// 生成透明底浮层图画布
|
||||
$watim = imageCreateTrueColor($this->picWidth, $this->dstHeight);
|
||||
imageSaveAlpha($watim, true) && imageAlphaBlending($watim, false);
|
||||
imageFill($watim, 0, 0, imageColorAllocateAlpha($watim, 255, 255, 255, 127));
|
||||
|
||||
// 随机位置
|
||||
$srcX1 = mt_rand(150, $this->dstWidth - $this->picWidth); // 水印位于大图X坐标
|
||||
$srcY1 = mt_rand(0, $this->dstHeight - $this->picHeight); // 水印位于大图Y坐标
|
||||
|
||||
// 去除第二个干扰水印
|
||||
// do { // 干扰位置
|
||||
// $srcX2 = mt_rand(100, $this->dstWidth - $this->picWidth);
|
||||
// $srcY2 = mt_rand(0, $this->dstHeight - $this->picHeight);
|
||||
// } while (abs($srcX1 - $srcX2) < $this->picWidth);
|
||||
|
||||
// 水印边框颜色
|
||||
$broders = [
|
||||
imageColorAllocateAlpha($dstim, 250, 100, 0, 50),
|
||||
imageColorAllocateAlpha($dstim, 250, 0, 100, 50),
|
||||
imageColorAllocateAlpha($dstim, 100, 0, 250, 50),
|
||||
imageColorAllocateAlpha($dstim, 100, 250, 0, 50),
|
||||
imageColorAllocateAlpha($dstim, 0, 250, 100, 50),
|
||||
];
|
||||
shuffle($broders);
|
||||
$c1 = array_pop($broders);
|
||||
$c2 = array_pop($broders);
|
||||
$gray = imageColorAllocateAlpha($dstim, 0, 0, 0, 80);
|
||||
$blue = imageColorAllocateAlpha($watim, 0, 100, 250, 50);
|
||||
|
||||
// 取原图像素颜色,生成浮层图
|
||||
$waters = $this->withWaterPoint();
|
||||
for ($i = 0; $i < $this->picHeight; $i++) {
|
||||
for ($j = 0; $j < $this->picWidth; $j++) {
|
||||
if ($waters[$i][$j] === 1) {
|
||||
if (
|
||||
empty($waters[$i - 1][$j - 1]) || empty($waters[$i - 2][$j - 2]) ||
|
||||
empty($waters[$i + 1][$j + 1]) || empty($waters[$i + 2][$j + 2])
|
||||
) {
|
||||
imagesetpixel($watim, $j, $srcY1 + $i, $blue);
|
||||
} else {
|
||||
imagesetpixel($watim, $j, $srcY1 + $i, ImageColorAt($dstim, $srcX1 + $j, $srcY1 + $i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 在原图挖坑,打上灰色水印
|
||||
for ($i = 0; $i < $this->picHeight; $i++) {
|
||||
for ($j = 0; $j < $this->picWidth; $j++) {
|
||||
if ($waters[$i][$j] === 1) {
|
||||
if (
|
||||
empty($waters[$i - 1][$j - 1]) ||
|
||||
empty($waters[$i - 2][$j - 2]) ||
|
||||
empty($waters[$i + 1][$j + 1]) ||
|
||||
empty($waters[$i + 2][$j + 2])
|
||||
) {
|
||||
imagesetpixel($dstim, $srcX1 + $j, $srcY1 + $i, $c1);
|
||||
// 去除第二个干扰水印
|
||||
// imagesetpixel($dstim, $srcX2 + $j, $srcY2 + $i, $c2);
|
||||
} else {
|
||||
imagesetpixel($dstim, $srcX1 + $j, $srcY1 + $i, $gray);
|
||||
// 去除第二个干扰水印
|
||||
// imagesetpixel($dstim, $srcX2 + $j, $srcY2 + $i, $gray);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取背景图及浮层图内容
|
||||
[, , $bgimg] = [ob_start(), imagepng($dstim), ob_get_contents(), ob_end_clean(), imagedestroy($dstim)];
|
||||
[, , $water] = [ob_start(), imagepng($watim), ob_get_contents(), ob_end_clean(), imagedestroy($watim)];
|
||||
|
||||
return [
|
||||
'point' => $srcX1,
|
||||
'bgimg' => 'data:image/png;base64,' . base64_encode($bgimg),
|
||||
'water' => 'data:image/png;base64,' . base64_encode($water)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算水印矩阵坐标
|
||||
* @return void
|
||||
*/
|
||||
private function withWaterPoint(): array
|
||||
{
|
||||
$waters = [];
|
||||
// 半径平方
|
||||
$dr = $this->r * $this->r;
|
||||
$lw = $this->r * 2 - 5;
|
||||
|
||||
// 第一个圆中心点
|
||||
$c_1_x = $lw + ($this->picWidth - $lw * 2) / 2;
|
||||
$c_1_y = $this->r;
|
||||
|
||||
// 第二个圆中心点
|
||||
$c_2_x = $this->picHeight - $this->r;
|
||||
$c_2_y = $lw + ($this->picHeight - ($lw) * 2) / 2;
|
||||
|
||||
for ($i = 0; $i < $this->picHeight; $i++) {
|
||||
for ($j = 0; $j < $this->picWidth; $j++) {
|
||||
// 根据公式(x-a)² + (y-b)² = r² 算出像素是否在圆内
|
||||
$d1 = pow($j - $c_1_x, 2) + pow($i - $c_1_y, 2);
|
||||
$d2 = pow($j - $c_2_x, 2) + pow($i - $c_2_y, 2);
|
||||
if (($i >= $lw && $j >= $lw && $i <= $this->picHeight - $lw && $j <= $this->picWidth - $lw) || $d1 <= $dr || $d2 <= $dr) {
|
||||
$waters[$i][$j] = 1;
|
||||
} else {
|
||||
$waters[$i][$j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $waters;
|
||||
}
|
||||
}
|
||||
105
plugin/think-library/src/extend/JsonRpcClient.php
Normal file
105
plugin/think-library/src/extend/JsonRpcClient.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\extend;
|
||||
|
||||
use think\admin\Exception;
|
||||
|
||||
/**
|
||||
* JsonRpc 客户端
|
||||
* @class JsonRpcClient
|
||||
* @package think\admin\extend
|
||||
*/
|
||||
class JsonRpcClient
|
||||
{
|
||||
/**
|
||||
* 请求ID
|
||||
* @var integer
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* 服务端地址
|
||||
* @var string
|
||||
*/
|
||||
private $proxy;
|
||||
|
||||
/**
|
||||
* 请求头部参数
|
||||
* @var string
|
||||
*/
|
||||
private $header;
|
||||
|
||||
/**
|
||||
* JsonRpcClient constructor.
|
||||
* @param string $proxy
|
||||
* @param array $header
|
||||
*/
|
||||
public function __construct(string $proxy, array $header = [])
|
||||
{
|
||||
$this->id = time();
|
||||
$this->proxy = $proxy;
|
||||
$this->header = $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 JsonRpc 请求
|
||||
* @param string $method
|
||||
* @param array $params
|
||||
* @return mixed
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function __call(string $method, array $params = [])
|
||||
{
|
||||
$options = [
|
||||
'ssl' => [
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false,
|
||||
],
|
||||
'http' => [
|
||||
'method' => 'POST', "timeout" => 60,
|
||||
'header' => join("\r\n", array_merge(['Content-Type:application/json'], $this->header, ['User-Agent:think-admin-jsonrpc', ''])),
|
||||
'content' => json_encode(['jsonrpc' => '2.0', 'method' => $method, 'params' => $params, 'id' => $this->id], JSON_UNESCAPED_UNICODE),
|
||||
],
|
||||
];
|
||||
try {
|
||||
// Performs the HTTP POST
|
||||
if ($fp = fopen($this->proxy, 'r', false, stream_context_create($options))) {
|
||||
$response = '';
|
||||
while ($line = fgets($fp)) $response .= trim($line) . "\n";
|
||||
[, $response] = [fclose($fp), json_decode($response, true)];
|
||||
} else {
|
||||
throw new Exception(lang("Unable connect: %s", [$this->proxy]));
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
throw $exception;
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception($exception->getMessage());
|
||||
}
|
||||
// Compatible with normal
|
||||
if (isset($response['code']) && isset($response['info'])) {
|
||||
throw new Exception($response['info'], intval($response['code']), $response['data'] ?? []);
|
||||
}
|
||||
// Final checks and return
|
||||
if (empty($response['id']) || $response['id'] != $this->id) {
|
||||
throw new Exception(lang("Error flag ( Request tag: %s, Response tag: %s )", [$this->id, $response['id'] ?? '-']), 0, $response);
|
||||
}
|
||||
if (is_null($response['error'])) return $response['result'];
|
||||
throw new Exception($response['error']['message'], intval($response['error']['code']), $response['result'] ?? []);
|
||||
}
|
||||
}
|
||||
125
plugin/think-library/src/extend/JsonRpcServer.php
Normal file
125
plugin/think-library/src/extend/JsonRpcServer.php
Normal file
@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\extend;
|
||||
|
||||
use think\App;
|
||||
use think\Container;
|
||||
use think\exception\HttpResponseException;
|
||||
|
||||
/**
|
||||
* JsonRpc 服务端
|
||||
* @class JsonRpcServer
|
||||
* @package think\admin\extend
|
||||
*/
|
||||
class JsonRpcServer
|
||||
{
|
||||
/**
|
||||
* 当前App对象
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* JsonRpcServer constructor.
|
||||
* @param App $app
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态实例对象
|
||||
* @param array $args
|
||||
* @return static
|
||||
*/
|
||||
public static function instance(...$args): JsonRpcServer
|
||||
{
|
||||
return Container::getInstance()->make(static::class, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置监听对象
|
||||
* @param mixed $object
|
||||
*/
|
||||
public function handle($object)
|
||||
{
|
||||
// Checks if a JSON-RCP request has been received
|
||||
if ($this->app->request->method() !== 'POST' || $this->app->request->contentType() !== 'application/json') {
|
||||
$this->printMethod($object);
|
||||
} else {
|
||||
// Reads the input data
|
||||
$request = json_decode(file_get_contents('php://input'), true) ?: [];
|
||||
if (empty($request)) {
|
||||
$error = ['code' => '-32700', 'message' => lang('Syntax parsing error.'), 'meaning' => lang('Invalid JSON parameter.')];
|
||||
$response = ['jsonrpc' => '2.0', 'id' => '0', 'result' => null, 'error' => $error];
|
||||
} elseif (!isset($request['id']) || !isset($request['method']) || !isset($request['params'])) {
|
||||
$error = ['code' => '-32600', 'message' => lang('Invalid request.'), 'meaning' => lang('Invalid JSON parameter.')];
|
||||
$response = ['jsonrpc' => '2.0', 'id' => $request['id'] ?? '0', 'result' => null, 'error' => $error];
|
||||
} else try {
|
||||
if ($object instanceof \Exception) {
|
||||
throw $object;
|
||||
} elseif (strtolower($request['method']) === '_get_class_name_') {
|
||||
$response = ['jsonrpc' => '2.0', 'id' => $request['id'], 'result' => get_class($object), 'error' => null];
|
||||
} elseif (method_exists($object, $request['method'])) {
|
||||
$result = call_user_func_array([$object, $request['method']], $request['params']);
|
||||
$response = ['jsonrpc' => '2.0', 'id' => $request['id'], 'result' => $result, 'error' => null];
|
||||
} else {
|
||||
$info = lang('method not exists: %s::%s', [class_basename($object), $request['method']]);
|
||||
$error = ['code' => '-32601', 'message' => $info, 'meaning' => lang('The method does not exist or is invalid.')];
|
||||
$response = ['jsonrpc' => '2.0', 'id' => $request['id'], 'result' => null, 'error' => $error];
|
||||
}
|
||||
} catch (\think\admin\Exception $exception) {
|
||||
$error = ['code' => $exception->getCode(), 'message' => lang($exception->getMessage()), 'meaning' => lang('Business Exception.')];
|
||||
$response = ['jsonrpc' => '2.0', 'id' => $request['id'], 'result' => $exception->getData(), 'error' => $error];
|
||||
} catch (\Exception $exception) {
|
||||
$error = ['code' => $exception->getCode(), 'message' => lang($exception->getMessage()), 'meaning' => lang('System Exception.')];
|
||||
$response = ['jsonrpc' => '2.0', 'id' => $request['id'], 'result' => null, 'error' => $error];
|
||||
}
|
||||
// Output the response
|
||||
throw new HttpResponseException(json($response));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印输出对象方法
|
||||
* @param mixed $object
|
||||
*/
|
||||
protected function printMethod($object)
|
||||
{
|
||||
try {
|
||||
$object = new \ReflectionClass($object);
|
||||
echo "<h2>{$object->getName()}</h2><hr>";
|
||||
foreach ($object->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
if (stripos($method->getName(), '_') === 0) continue;
|
||||
$params = [];
|
||||
foreach ($method->getParameters() as $parameter) {
|
||||
$type = $parameter->getType();
|
||||
if ($type instanceof \ReflectionType) $type = $type->getName();
|
||||
$params[] = ($type ? "{$type} $" : '$') . $parameter->getName();
|
||||
}
|
||||
$params = count($params) > 0 ? join(', ', $params) : '';
|
||||
echo '<div style="color:#666">' . nl2br($method->getDocComment() ?: '') . '</div>';
|
||||
echo "<div style='color:#00E'>{$object->getShortName()}::{$method->getName()}({$params})</div><br>";
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
echo "<h3>[{$exception->getCode()}] {$exception->getMessage()}</h3>";
|
||||
}
|
||||
}
|
||||
}
|
||||
244
plugin/think-library/src/extend/JwtExtend.php
Normal file
244
plugin/think-library/src/extend/JwtExtend.php
Normal file
@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\extend;
|
||||
|
||||
use think\admin\Controller;
|
||||
use think\admin\Exception;
|
||||
use think\admin\Library;
|
||||
|
||||
/**
|
||||
* 接口 JWT 接口扩展
|
||||
* @class JwtExtend
|
||||
* @package think\admin\extend
|
||||
* @method static bool isRejwt() 是否输出令牌
|
||||
* @method static array getInData() 获取输入数据
|
||||
*/
|
||||
class JwtExtend
|
||||
{
|
||||
// 头部参数
|
||||
private const header = ['typ' => 'JWT', 'alg' => 'HS256'];
|
||||
|
||||
// 签名配置
|
||||
private const signTypes = [
|
||||
'HS256' => 'sha256',
|
||||
'HS384' => 'sha384',
|
||||
'HS512' => 'sha512'
|
||||
];
|
||||
|
||||
/**
|
||||
* 是否返回令牌
|
||||
* @var boolean
|
||||
*/
|
||||
private static $rejwt = false;
|
||||
|
||||
/**
|
||||
* 当前请求数据
|
||||
* @var array
|
||||
*/
|
||||
private static $input = [];
|
||||
|
||||
/**
|
||||
* 获取原会话标签
|
||||
* @var string
|
||||
*/
|
||||
public static $sessionId = '';
|
||||
|
||||
/**
|
||||
* 生成 jwt token
|
||||
* @param array $data jwt 载荷 格式如下非必须
|
||||
* {
|
||||
* "iss": "http://example.org", // 签发者(Issuer),JWT的签发者
|
||||
* "sub": "1234567890", // 主题(Subject),JWT所面向的用户
|
||||
* "aud": "http://example.com", // 受众(Audience),接收JWT的一方
|
||||
* "exp": 1625174400, // 过期时间(Expiration time),JWT的过期时间戳
|
||||
* "iat": 1625138400, // 签发时间(Issued at),JWT的签发时间戳
|
||||
* "nbf": 1625138400, // 生效时间(Not Before),JWT的生效时间戳
|
||||
* "...": ... // 其他扩展内容
|
||||
* }
|
||||
* @param ?string $jwtkey 签名密钥
|
||||
* @param ?boolean $rejwt 输出令牌
|
||||
* @return string
|
||||
*/
|
||||
public static function token(array $data = [], ?string $jwtkey = null, ?bool $rejwt = null): string
|
||||
{
|
||||
$jwtkey = self::jwtkey($jwtkey);
|
||||
if (is_bool($rejwt)) self::$rejwt = $rejwt;
|
||||
|
||||
// JWT 载荷数据组装
|
||||
[$fields, $payload] = [['iss', 'sub', 'aud', 'exp', 'iat', 'nbf'], ['iat' => time()]];
|
||||
foreach ($data as $k => $v) if (in_array($k, $fields)) {
|
||||
$payload[$k] = $v;
|
||||
unset($data[$k]);
|
||||
}
|
||||
|
||||
// 自定义需要的数据
|
||||
$data['.ssid'] = self::withSess();
|
||||
if (empty($data['.ssid'])) unset($data['.ssid']);
|
||||
$payload['enc'] = CodeExtend::encrypt(json_encode($data, JSON_UNESCAPED_UNICODE), $jwtkey);
|
||||
|
||||
// 组装 JWT 内容格式
|
||||
$two = CodeExtend::enSafe64(json_encode($payload, JSON_UNESCAPED_UNICODE));
|
||||
$one = CodeExtend::enSafe64(json_encode(self::header, JSON_UNESCAPED_UNICODE));
|
||||
return "{$one}.{$two}." . self::withSign("{$one}.{$two}", self::header['alg'], $jwtkey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 token 是否有效, 默认验证 exp,nbf,iat 时间
|
||||
* @param string $token 加密数据
|
||||
* @param ?string $jwtkey 签名密钥
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function verify(string $token, ?string $jwtkey = null): array
|
||||
{
|
||||
$tokens = explode('.', $token);
|
||||
if (count($tokens) != 3) throw new Exception('数据解密失败!', 0, []);
|
||||
|
||||
[$base64header, $base64payload, $signature] = $tokens;
|
||||
|
||||
// 加密算法
|
||||
$header = json_decode(CodeExtend::deSafe64($base64header), true);
|
||||
if (empty($header['alg'])) throw new Exception('数据解密失败!', 0, []);
|
||||
|
||||
// 签名验证
|
||||
$jwtkey = self::jwtkey($jwtkey);
|
||||
if (self::withSign("{$base64header}.{$base64payload}", $header['alg'], $jwtkey) !== $signature) {
|
||||
throw new Exception('验证签名失败!', 0, []);
|
||||
}
|
||||
|
||||
// 获取 Playload 数据
|
||||
$payload = json_decode(CodeExtend::deSafe64($base64payload), true);
|
||||
|
||||
// 签发时间大于当前服务器时间验证失败
|
||||
if (isset($payload['iat']) && $payload['iat'] > time()) {
|
||||
throw new Exception('服务器时间验证失败!', 0, $payload);
|
||||
}
|
||||
|
||||
// 过期时间小于当前服务器时间验证失败
|
||||
if (isset($payload['exp']) && $payload['exp'] < time()) {
|
||||
throw new Exception('服务器时间验证失败!', 0, $payload);
|
||||
}
|
||||
|
||||
// 该 nbf 时间之前不接收处理该 TOKEN
|
||||
if (isset($payload['nbf']) && $payload['nbf'] > time()) {
|
||||
throw new Exception('不接收处理该TOKEN', 0, $payload);
|
||||
}
|
||||
|
||||
// 返回自定义数据字段
|
||||
if (isset($payload['enc'])) {
|
||||
$extra = json_decode(CodeExtend::decrypt($payload['enc'], $jwtkey), true);
|
||||
if (!empty($extra['.ssid'])) self::$sessionId = $extra['.ssid'];
|
||||
unset($payload['enc'], $extra['.ssid']);
|
||||
}
|
||||
|
||||
return self::$input = array_merge($payload, $extra ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 JWT 密钥
|
||||
* @param ?string $jwtkey
|
||||
* @return string
|
||||
*/
|
||||
public static function jwtkey(?string $jwtkey = null): string
|
||||
{
|
||||
try {
|
||||
if (!empty($jwtkey)) return $jwtkey;
|
||||
|
||||
// 优先读取配置文件
|
||||
$jwtkey = config('app.jwtkey');
|
||||
if (!empty($jwtkey)) return $jwtkey;
|
||||
|
||||
// 再次读取数据配置
|
||||
$jwtkey = sysconf('data.jwtkey|raw');
|
||||
if (!empty($jwtkey)) return $jwtkey;
|
||||
|
||||
// 自动生成新的密钥
|
||||
$jwtkey = md5(uniqid(strval(rand(1000, 9999)), true));
|
||||
sysconf('data.jwtkey', $jwtkey);
|
||||
return $jwtkey;
|
||||
|
||||
} catch (\Exception $exception) {
|
||||
trace_file($exception);
|
||||
return 'thinkadmin';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出模板变量
|
||||
* @param \think\admin\Controller $class
|
||||
* @param array $vars
|
||||
* @return void
|
||||
*/
|
||||
public static function fetch(Controller $class, array $vars = [])
|
||||
{
|
||||
$ignore = array_keys(get_class_vars(Controller::class));
|
||||
foreach ($class as $name => $value) if (!in_array($name, $ignore)) {
|
||||
if (is_array($value) || is_numeric($value) || is_string($value) || is_bool($value) || is_null($value)) {
|
||||
$vars[$name] = $value;
|
||||
}
|
||||
}
|
||||
$class->success('获取变量成功!', $vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原会话标识
|
||||
* @return string
|
||||
*/
|
||||
private static function withSess(): string
|
||||
{
|
||||
if (!isset(Library::$sapp->session)) return self::$sessionId = '';
|
||||
return self::$sessionId = Library::$sapp->session->getId();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成数据签名
|
||||
* @param string $input 为 base64UrlEncode(header).".".base64UrlEncode(payload)
|
||||
* @param string $alg 算法方式
|
||||
* @param ?string $key 签名密钥
|
||||
* @return string
|
||||
*/
|
||||
private static function withSign(string $input, string $alg = 'HS256', ?string $key = null): string
|
||||
{
|
||||
return CodeExtend::enSafe64(hash_hmac(self::signTypes[$alg], $input, self::jwtkey($key), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容历史方法
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return array|string
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function __callStatic(string $method, array $arguments)
|
||||
{
|
||||
switch ($method) {
|
||||
case 'isRejwt': // 是否返回令牌
|
||||
return self::$rejwt;
|
||||
case 'getInData': // 获取请求数据
|
||||
return self::$input;
|
||||
case 'getToken': // 生成接口令牌
|
||||
return self::token(...$arguments);
|
||||
case 'verifyToken': // 验证接口令牌
|
||||
return self::verify(...$arguments);
|
||||
default:
|
||||
throw new Exception("method not exists: JwtExtend::{$method}()");
|
||||
}
|
||||
}
|
||||
}
|
||||
309
plugin/think-library/src/extend/PhinxExtend.php
Normal file
309
plugin/think-library/src/extend/PhinxExtend.php
Normal file
@ -0,0 +1,309 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\extend;
|
||||
|
||||
use Exception;
|
||||
use Phinx\Db\Adapter\AdapterInterface;
|
||||
use think\admin\Library;
|
||||
use think\admin\model\SystemMenu;
|
||||
use think\admin\service\ProcessService;
|
||||
use think\helper\Str;
|
||||
|
||||
/**
|
||||
* 数据库迁移扩展
|
||||
* @class PhinxExtend
|
||||
* @package think\admin\extend
|
||||
*/
|
||||
class PhinxExtend
|
||||
{
|
||||
/**
|
||||
* 批量写入菜单
|
||||
* @param array $zdata 菜单数据
|
||||
* @param mixed $exists 检测条件
|
||||
* @return boolean
|
||||
*/
|
||||
public static function write2menu(array $zdata, $exists = []): bool
|
||||
{
|
||||
// 检查是否需要写入菜单
|
||||
try {
|
||||
if (!empty($exists) && SystemMenu::mk()->where($exists)->findOrEmpty()->isExists()) {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
return false;
|
||||
}
|
||||
// 循环写入系统菜单数据
|
||||
foreach ($zdata as $one) {
|
||||
$pid1 = static::write1menu($one);
|
||||
if (!empty($one['subs'])) foreach ($one['subs'] as $two) {
|
||||
$pid2 = static::write1menu($two, $pid1);
|
||||
if (!empty($two['subs'])) foreach ($two['subs'] as $thr) {
|
||||
static::write1menu($thr, $pid2);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个写入菜单
|
||||
* @param array $menu 菜单数据
|
||||
* @param integer $ppid 上级菜单
|
||||
* @return integer
|
||||
*/
|
||||
private static function write1menu(array $menu, int $ppid = 0): int
|
||||
{
|
||||
return (int)SystemMenu::mk()->insertGetId([
|
||||
'pid' => $ppid,
|
||||
'url' => empty($menu['url']) ? (empty($menu['node']) ? '#' : $menu['node']) : $menu['url'],
|
||||
'sort' => $menu['sort'] ?? 0,
|
||||
'icon' => $menu['icon'] ?? '',
|
||||
'node' => empty($menu['node']) ? (empty($menu['url']) ? '' : $menu['url']) : $menu['node'],
|
||||
'title' => $menu['name'] ?? ($menu['title'] ?? ''),
|
||||
'params' => $menu['params'] ?? '',
|
||||
'target' => $menu['target'] ?? '_self',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据库安装脚本
|
||||
* @param array $tables
|
||||
* @param string $class
|
||||
* @return string[]
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function create2table(array $tables = [], string $class = 'InstallTable'): array
|
||||
{
|
||||
$br = "\r\n";
|
||||
$content = static::_build2table($tables, true);
|
||||
$content = substr($content, strpos($content, "\n") + 1);
|
||||
$content = '<?php' . "{$br}{$br}use think\migration\Migrator;{$br}{$br}@set_time_limit(0);{$br}@ini_set('memory_limit', -1);{$br}{$br}class {$class} extends Migrator {{$br}{$content}}{$br}";
|
||||
return ['file' => static::nextFile($class), 'text' => $content];
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据库备份脚本
|
||||
* @param array $tables
|
||||
* @param string $class
|
||||
* @param boolean $progress
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function create2backup(array $tables = [], string $class = 'InstallPackage', bool $progress = true): array
|
||||
{
|
||||
// 处理菜单数据
|
||||
[$menuData, $menuList] = [[], SystemMenu::mk()->where(['status' => 1])->order('sort desc,id asc')->select()->toArray()];
|
||||
foreach (DataExtend::arr2tree($menuList) as $sub1) {
|
||||
$one = ['name' => $sub1['title'], 'icon' => $sub1['icon'], 'url' => $sub1['url'], 'node' => $sub1['node'], 'params' => $sub1['params'], 'subs' => []];
|
||||
if (!empty($sub1['sub'])) foreach ($sub1['sub'] as $sub2) {
|
||||
$two = ['name' => $sub2['title'], 'icon' => $sub2['icon'], 'url' => $sub2['url'], 'node' => $sub2['node'], 'params' => $sub2['params'], 'subs' => []];
|
||||
if (!empty($sub2['sub'])) foreach ($sub2['sub'] as $sub3) {
|
||||
$two['subs'][] = ['name' => $sub3['title'], 'url' => $sub3['url'], 'node' => $sub3['node'], 'icon' => $sub3['icon'], 'params' => $sub3['params']];
|
||||
}
|
||||
if (empty($two['subs'])) unset($two['subs']);
|
||||
$one['subs'][] = $two;
|
||||
}
|
||||
if (empty($one['subs'])) unset($one['subs']);
|
||||
$menuData[] = $one;
|
||||
}
|
||||
|
||||
// 备份数据表
|
||||
[$extra, $version] = [[], strstr($filename = static::nextFile($class), '_', true)];
|
||||
if (count($tables) > 0) foreach ($tables as $table) {
|
||||
if (($count = ($db = Library::$sapp->db->table($table))->count()) > 0) {
|
||||
$dataFileName = "{$version}/{$table}.data";
|
||||
$dataFilePath = syspath("database/migrations/{$dataFileName}");
|
||||
is_dir($dataDirectory = dirname($dataFilePath)) || mkdir($dataDirectory, 0777, true);
|
||||
$progress && ProcessService::message(" -- Starting write {$table}.data ..." . PHP_EOL);
|
||||
[$used, $fp] = [0, fopen($dataFilePath, 'w+')];
|
||||
foreach ($db->cursor() as $item) {
|
||||
fwrite($fp, json_encode($item, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n");
|
||||
if ($progress && ($number = sprintf("%.4f", (++$used / $count) * 100) . '%')) {
|
||||
ProcessService::message(" -- -- write {$table}.data: {$used}/{$count} {$number}", 1);
|
||||
}
|
||||
}
|
||||
fclose($fp);
|
||||
$extra[$table] = $dataFileName;
|
||||
$progress && ProcessService::message(" -- Finished write {$table}.data, Total {$used} rows.", 2);
|
||||
}
|
||||
}
|
||||
|
||||
// 生成迁移脚本
|
||||
$template = file_get_contents(dirname(__DIR__) . '/service/bin/package.stub');
|
||||
$dataJson = json_encode($extra, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$menuJson = json_encode($menuData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$replaces = ['__CLASS__' => $class, '__MENU_JSON__' => $menuJson, '__DATA_JSON__' => $dataJson];
|
||||
return ['file' => $filename, 'text' => str_replace(array_keys($replaces), array_values($replaces), $template)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转代码
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
private static function _arr2str(array $data): string
|
||||
{
|
||||
return preg_replace(['#\s+#', '#, \)$#', '#^array \( #'], [' ', ']', '[',], var_export($data, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成数据库表格创建模板
|
||||
* @param array $tables 指定数据表
|
||||
* @param boolean $rehtml 是否返回内容
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
private static function _build2table(array $tables = [], bool $rehtml = false): string
|
||||
{
|
||||
$br = "\r\n";
|
||||
$connect = Library::$sapp->db->connect();
|
||||
if ($connect->getConfig('type') !== 'mysql') {
|
||||
throw new Exception(' ** Notify: 只支持 MySql 数据库生成数据库脚本');
|
||||
}
|
||||
$schema = $connect->getConfig('database');
|
||||
$content = '<?php' . "{$br}{$br}\t/**{$br}\t * 创建数据库{$br}\t */{$br}\t public function change() {";
|
||||
foreach ($tables as $table) $content .= "{$br}\t\t\$this->_create_{$table}();";
|
||||
$content .= "{$br}{$br}\t}{$br}{$br}";
|
||||
|
||||
// 字段默认长度
|
||||
$sizes = ['tinyint' => 4, 'smallint' => 6, 'mediumint' => 9, 'int' => 11, 'bigint' => 20];
|
||||
|
||||
// 字段类型转换 ( 仅需定义与MySQL不同的配置 )
|
||||
$types = [
|
||||
// 整形数字
|
||||
'tinyint' => AdapterInterface::PHINX_TYPE_TINY_INTEGER,
|
||||
'smallint' => AdapterInterface::PHINX_TYPE_SMALL_INTEGER,
|
||||
'int' => AdapterInterface::PHINX_TYPE_INTEGER,
|
||||
'bigint' => AdapterInterface::PHINX_TYPE_BIG_INTEGER,
|
||||
// 字符类型
|
||||
'varchar' => AdapterInterface::PHINX_TYPE_STRING,
|
||||
'tinytext' => AdapterInterface::PHINX_TYPE_TEXT,
|
||||
'mediumtext' => AdapterInterface::PHINX_TYPE_TEXT,
|
||||
'longtext' => AdapterInterface::PHINX_TYPE_TEXT,
|
||||
// 仅 mysql 有的字段需要单独处理
|
||||
'set' => AdapterInterface::PHINX_TYPE_STRING,
|
||||
'enum' => AdapterInterface::PHINX_TYPE_STRING,
|
||||
'year' => AdapterInterface::PHINX_TYPE_INTEGER,
|
||||
'mediumint' => AdapterInterface::PHINX_TYPE_INTEGER,
|
||||
'tinyblob' => AdapterInterface::PHINX_TYPE_BLOB,
|
||||
'longblob' => AdapterInterface::PHINX_TYPE_BLOB,
|
||||
'mediumblob' => AdapterInterface::PHINX_TYPE_BLOB,
|
||||
];
|
||||
|
||||
foreach ($tables as $table) {
|
||||
|
||||
// 读取数据表 - 备注参数
|
||||
$comment = Library::$sapp->db->table('information_schema.TABLES')->where([
|
||||
'TABLE_SCHEMA' => $schema, 'TABLE_NAME' => $table,
|
||||
])->value('TABLE_COMMENT', '');
|
||||
|
||||
// 读取数据表 - 自动生成结构
|
||||
$class = Str::studly($table);
|
||||
$content .= <<<CODE
|
||||
/**
|
||||
* 创建数据对象
|
||||
* @class {$class}
|
||||
* @table {$table}
|
||||
* @return void
|
||||
*/
|
||||
private function _create_{$table}() {
|
||||
|
||||
// 当前数据表
|
||||
\$table = '{$table}';
|
||||
|
||||
// 存在则跳过
|
||||
if (\$this->hasTable(\$table)) return;
|
||||
|
||||
// 创建数据表
|
||||
\$this->table(\$table, [
|
||||
'engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '{$comment}',
|
||||
])
|
||||
CODE;
|
||||
foreach (Library::$sapp->db->getFields($table) as $field) {
|
||||
if ($field['name'] === 'id') continue;
|
||||
$type = $types[$field['type']] ?? $field['type'];
|
||||
$data = ['default' => $field['default'], 'null' => empty($field['notnull']), 'comment' => $field['comment'] ?? ''];
|
||||
if ($field['type'] === 'enum') {
|
||||
$type = $types[$field['type']] ?? 'string';
|
||||
$data = array_merge(['limit' => 10], $data);
|
||||
} elseif (preg_match('/(tinyblob|blob|mediumblob|longblob|varbinary|bit|binary|varchar|char)\((\d+)\)/', $field['type'], $attr)) {
|
||||
$type = $types[$attr[1]] ?? 'string';
|
||||
$data = array_merge(['limit' => intval($attr[2])], $data);
|
||||
} elseif (preg_match('/(tinyint|smallint|mediumint|int|bigint)\((\d+)\)/', $field['type'], $attr)) {
|
||||
$type = $types[$attr[1]] ?? 'integer';
|
||||
$data = array_merge(['limit' => intval($attr[2])], $data, ['default' => intval($data['default'])]);
|
||||
} elseif (preg_match('/(tinyint|smallint|mediumint|int|bigint)\s+unsigned/i', $field['type'], $attr)) {
|
||||
$type = $types[$attr[1]] ?? 'integer';
|
||||
if (isset($sizes[$attr[1]])) {
|
||||
$data = array_merge(['limit' => $sizes[$attr[1]]], $data);
|
||||
}
|
||||
$data['default'] = intval($data['default']);
|
||||
} elseif (preg_match('/(float|decimal)\((\d+),(\d+)\)/', $field['type'], $attr)) {
|
||||
$type = $types[$attr[1]] ?? 'decimal';
|
||||
$data = array_merge(['precision' => intval($attr[2]), 'scale' => intval($attr[3])], $data);
|
||||
}
|
||||
$params = static::_arr2str($data);
|
||||
$content .= "{$br}\t\t->addColumn('{$field["name"]}','{$type}',{$params})";
|
||||
}
|
||||
// 读取数据表 - 自动生成索引
|
||||
$idxs = [];
|
||||
$indexs = Library::$sapp->db->connect()->query("show index from {$table}");
|
||||
foreach ($indexs as $index) {
|
||||
if ($index['Key_name'] === 'PRIMARY') continue;
|
||||
$short = substr(md5($index['Table']), 0, 9);
|
||||
$params = static::_arr2str(['name' => "i{$short}_{$index['Column_name']}"]);
|
||||
$idxs[] = "{$br}\t\t->addIndex('{$index['Column_name']}', {$params})";
|
||||
}
|
||||
usort($idxs, function ($a, $b) {
|
||||
return strlen($a) <=> strlen($b);
|
||||
});
|
||||
$content .= join('', $idxs);
|
||||
$content .= "{$br}\t\t->create();{$br}{$br}\t\t// 修改主键长度";
|
||||
$content .= "{$br}\t\t\$this->table(\$table)->changeColumn('id', 'integer', ['limit' => 11, 'identity' => true]);";
|
||||
$content .= "{$br}\t}{$br}{$br}";
|
||||
}
|
||||
return $rehtml ? $content : highlight_string($content, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成下一个脚本名称
|
||||
* @param string $class 脚本类名
|
||||
* @return string
|
||||
*/
|
||||
private static function nextFile(string $class): string
|
||||
{
|
||||
[$filename, $versions, $start] = [Str::snake($class), [], 20009999999999];
|
||||
if (count($files = glob(syspath('database/migrations/*.php'))) > 0) {
|
||||
foreach ($files as $file) {
|
||||
$versions[] = $version = intval(substr($bname = pathinfo($file, 8), 0, 14));
|
||||
if ($filename === substr($bname, 15) && unlink($file)) {
|
||||
echo " ** Notify: Class {$class} already exists and has been replaced." . PHP_EOL;
|
||||
if (is_dir($dataPath = dirname($file) . DIRECTORY_SEPARATOR . $version)) {
|
||||
ToolsExtend::removeEmptyDirectory($dataPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
$version = min($versions) - 1;
|
||||
}
|
||||
if (!isset($version) || $version > $start) $version = $start;
|
||||
return "{$version}_{$filename}.php";
|
||||
}
|
||||
}
|
||||
141
plugin/think-library/src/extend/ToolsExtend.php
Normal file
141
plugin/think-library/src/extend/ToolsExtend.php
Normal file
@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\extend;
|
||||
|
||||
use Closure;
|
||||
use FilesystemIterator;
|
||||
use Generator;
|
||||
use SplFileInfo;
|
||||
|
||||
/**
|
||||
* 通用工具扩展
|
||||
* @class ToolsExtend
|
||||
* @package think\admin\extend
|
||||
*/
|
||||
class ToolsExtend
|
||||
{
|
||||
/**
|
||||
* 深度拷贝到指定目录
|
||||
* @param string $frdir 来源目录
|
||||
* @param string $todir 目标目录
|
||||
* @param array $files 指定文件
|
||||
* @param boolean $force 强制替换
|
||||
* @param boolean $remove 删除文件
|
||||
* @return boolean
|
||||
*/
|
||||
public static function copyfile(string $frdir, string $todir, array $files = [], bool $force = true, bool $remove = true): bool
|
||||
{
|
||||
$frdir = rtrim($frdir, '\\/') . DIRECTORY_SEPARATOR;
|
||||
$todir = rtrim($todir, '\\/') . DIRECTORY_SEPARATOR;
|
||||
// 扫描目录文件
|
||||
if (empty($files) && is_dir($frdir)) {
|
||||
$files = static::findFilesArray($frdir, static function (SplFileInfo $info) {
|
||||
return substr($info->getBasename(), 0, 1) !== '.';
|
||||
}, static function (SplFileInfo $info) {
|
||||
return substr($info->getBasename(), 0, 1) !== '.';
|
||||
});
|
||||
}
|
||||
// 复制文件列表
|
||||
foreach ($files as $target) {
|
||||
if ($force || !is_file($todir . $target)) {
|
||||
$dir = dirname($todir . $target);
|
||||
is_dir($dir) or mkdir($dir, 0777, true);
|
||||
copy($frdir . $target, $todir . $target);
|
||||
}
|
||||
// 删除来源文件
|
||||
$remove && unlink($frdir . $target);
|
||||
}
|
||||
// 删除来源目录
|
||||
$remove && static::removeEmptyDirectory($frdir);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描目录列表
|
||||
* @param string $path 扫描目录
|
||||
* @param string $filterExt 筛选后缀
|
||||
* @param boolean $shortPath 相对路径
|
||||
* @return array
|
||||
*/
|
||||
public static function scanDirectory(string $path, string $filterExt = '', bool $shortPath = true): array
|
||||
{
|
||||
return static::findFilesArray($path, static function (SplFileInfo $info) use ($filterExt) {
|
||||
return empty($filterExt) || $info->getExtension() === $filterExt;
|
||||
}, static function (SplFileInfo $info) {
|
||||
return substr($info->getBasename(), 0, 1) !== '.';
|
||||
}, $shortPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描指定目录
|
||||
* @param string $path
|
||||
* @param ?Closure $filterFile
|
||||
* @param ?Closure $filterPath
|
||||
* @param boolean $shortPath
|
||||
* @return array
|
||||
*/
|
||||
public static function findFilesArray(string $path, ?Closure $filterFile = null, ?Closure $filterPath = null, bool $shortPath = true): array
|
||||
{
|
||||
$items = [];
|
||||
if (file_exists($path)) {
|
||||
$files = static::findFilesYield($path, $filterFile, $filterPath);
|
||||
foreach ($files as $file) $items[] = $file->getRealPath();
|
||||
if ($shortPath && ($offset = strlen(realpath($path)) + 1)) {
|
||||
foreach ($items as &$item) $item = substr($item, $offset);
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描指定目录
|
||||
* @param string $path
|
||||
* @param \Closure|null $filterFile
|
||||
* @param \Closure|null $filterPath
|
||||
* @param boolean $fullDirectory
|
||||
* @return \Generator|\SplFileInfo[]
|
||||
*/
|
||||
public static function findFilesYield(string $path, ?Closure $filterFile = null, ?Closure $filterPath = null, bool $fullDirectory = false): Generator
|
||||
{
|
||||
if (file_exists($path)) {
|
||||
$items = is_file($path) ? [new SplFileInfo($path)] : new FilesystemIterator($path);
|
||||
foreach ($items as $item) if ($item->isDir() && !$item->isLink()) {
|
||||
if (is_null($filterPath) || $filterPath($item)) {
|
||||
yield from static::findFilesYield($item->getPathname(), $filterFile, $filterPath, $fullDirectory);
|
||||
}
|
||||
$fullDirectory && yield $item;
|
||||
} elseif (is_null($filterFile) || $filterFile($item)) {
|
||||
yield $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除清空目录
|
||||
* @param string $path
|
||||
* @return boolean
|
||||
*/
|
||||
public static function removeEmptyDirectory(string $path): bool
|
||||
{
|
||||
foreach (self::findFilesYield($path, null, null, true) as $item) {
|
||||
($item->isFile() || $item->isLink()) ? unlink($item->getRealPath()) : rmdir($item->getRealPath());
|
||||
}
|
||||
return is_file($path) ? unlink($path) : (!is_dir($path) || rmdir($path));
|
||||
}
|
||||
}
|
||||
190
plugin/think-library/src/extend/VirtualModel.php
Normal file
190
plugin/think-library/src/extend/VirtualModel.php
Normal file
@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\extend;
|
||||
|
||||
use think\admin\contract\StreamInterface;
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 虚拟模型构建协议
|
||||
* @class VirtualModel
|
||||
* @package think\admin\extend
|
||||
*/
|
||||
class VirtualModel extends \stdClass implements StreamInterface
|
||||
{
|
||||
/**
|
||||
* 虚拟模型模板
|
||||
* @var string
|
||||
*/
|
||||
private $template;
|
||||
|
||||
/**
|
||||
* 读取进度标量
|
||||
* @var integer
|
||||
*/
|
||||
private $position;
|
||||
|
||||
public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool
|
||||
{
|
||||
// 解析链接参数
|
||||
$attr = parse_url($path);
|
||||
if (empty($attr['fragment'])) $attr['fragment'] = '';
|
||||
$type = strtolower($attr['fragment'] ?: 'default');
|
||||
|
||||
// 生成模型代码
|
||||
$this->position = 0;
|
||||
$this->template = '<?php ';
|
||||
$this->template .= "namespace virtual\\model\\_{$type}; ";
|
||||
$this->template .= "class {$attr['host']} extends \\think\\admin\\Model { ";
|
||||
if (!empty($attr['fragment'])) {
|
||||
$this->template .= "protected \$connection='{$attr['fragment']}'; ";
|
||||
}
|
||||
$this->template .= '}';
|
||||
return true;
|
||||
}
|
||||
|
||||
public function stream_read(int $count)
|
||||
{
|
||||
$content = substr($this->template, $this->position, $count);
|
||||
$this->position += strlen($content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
public function stream_eof(): bool
|
||||
{
|
||||
return $this->position >= strlen($this->template);
|
||||
}
|
||||
|
||||
public function stream_cast(int $cast_as)
|
||||
{
|
||||
}
|
||||
|
||||
public function stream_close(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function stream_flush(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function stream_lock(int $operation): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function stream_set_option(int $option, int $arg1, int $arg2): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function stream_metadata(string $path, int $option, $value): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function stream_stat()
|
||||
{
|
||||
return stat(__FILE__);
|
||||
}
|
||||
|
||||
public function stream_tell(): int
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function stream_truncate(int $new_size): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function stream_seek(int $offset, int $whence = SEEK_SET): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function stream_write(string $data): int
|
||||
{
|
||||
return strlen($data);
|
||||
}
|
||||
|
||||
public function dir_opendir(string $path, int $options): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function dir_readdir(): string
|
||||
{
|
||||
return __DIR__;
|
||||
}
|
||||
|
||||
public function dir_closedir(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function dir_rewinddir(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function mkdir(string $path, int $mode, int $options): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rmdir(string $path, int $options): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rename(string $path_from, string $path_to): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function unlink(string $path): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function url_stat(string $path, int $flags)
|
||||
{
|
||||
return stat(__FILE__);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建虚拟模型
|
||||
* @param mixed $name 模型名称
|
||||
* @param array $data 模型数据
|
||||
* @param mixed $conn 默认链接
|
||||
* @return \think\Model
|
||||
*/
|
||||
public static function mk(string $name, array $data = [], string $conn = ''): Model
|
||||
{
|
||||
$type = strtolower($conn ?: 'default');
|
||||
if (!class_exists($class = "\\virtual\\model\\_{$type}\\{$name}")) {
|
||||
if (!in_array('model', stream_get_wrappers())) {
|
||||
stream_wrapper_register('model', static::class);
|
||||
}
|
||||
include "model://{$name}#{$conn}";
|
||||
}
|
||||
return new $class($data);
|
||||
}
|
||||
}
|
||||
95
plugin/think-library/src/helper/DeleteHelper.php
Normal file
95
plugin/think-library/src/helper/DeleteHelper.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\helper;
|
||||
|
||||
use think\admin\Helper;
|
||||
use think\db\BaseQuery;
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 通用删除管理器
|
||||
* @class DeleteHelper
|
||||
* @package think\admin\helper
|
||||
*/
|
||||
class DeleteHelper extends Helper
|
||||
{
|
||||
/**
|
||||
* 逻辑器初始化
|
||||
* @param BaseQuery|Model|string $dbQuery
|
||||
* @param string $field 操作数据主键
|
||||
* @param mixed $where 额外更新条件
|
||||
* @return boolean|void
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public function init($dbQuery, string $field = '', $where = [])
|
||||
{
|
||||
$query = static::buildQuery($dbQuery);
|
||||
$field = $field ?: ($query->getPk() ?: 'id');
|
||||
$value = $this->app->request->post($field);
|
||||
|
||||
// 查询限制处理
|
||||
if (!empty($where)) $query->where($where);
|
||||
if (!isset($where[$field]) && is_string($value)) {
|
||||
$query->whereIn($field, str2arr($value));
|
||||
}
|
||||
|
||||
// 前置回调处理
|
||||
if (false === $this->class->callback('_delete_filter', $query, $where)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 阻止危险操作
|
||||
if (!$query->getOptions('where')) {
|
||||
$this->class->error('数据删除失败!');
|
||||
}
|
||||
|
||||
// 组装执行数据
|
||||
$data = [];
|
||||
if (method_exists($query, 'getTableFields')) {
|
||||
$fields = $query->getTableFields();
|
||||
if (in_array('deleted', $fields)) $data['deleted'] = 1;
|
||||
if (in_array('is_deleted', $fields)) $data['is_deleted'] = 1;
|
||||
if (isset($data['deleted']) || isset($data['is_deleted'])) {
|
||||
if (in_array('deleted_at', $fields)) $data['deleted_at'] = date('Y-m-d H:i:s');
|
||||
if (in_array('deleted_time', $fields)) $data['deleted_time'] = time();
|
||||
}
|
||||
}
|
||||
|
||||
// 执行删除操作
|
||||
if ($result = (empty($data) ? $query->delete() : $query->update($data)) !== false) {
|
||||
// 模型自定义事件回调
|
||||
$model = $query->getModel();
|
||||
if ($model instanceof \think\admin\Model) {
|
||||
$model->onAdminDelete(strval($value));
|
||||
}
|
||||
}
|
||||
|
||||
// 结果回调处理
|
||||
if (false === $this->class->callback('_delete_result', $result)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// 回复返回结果
|
||||
if ($result !== false) {
|
||||
$this->class->success('数据删除成功!', '');
|
||||
} else {
|
||||
$this->class->error('数据删除失败!');
|
||||
}
|
||||
}
|
||||
}
|
||||
79
plugin/think-library/src/helper/FormHelper.php
Normal file
79
plugin/think-library/src/helper/FormHelper.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\helper;
|
||||
|
||||
use think\admin\Helper;
|
||||
use think\admin\service\SystemService;
|
||||
use think\db\BaseQuery;
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 表单视图管理器
|
||||
* @class FormHelper
|
||||
* @package think\admin\helper
|
||||
*/
|
||||
class FormHelper extends Helper
|
||||
{
|
||||
|
||||
/**
|
||||
* 逻辑器初始化
|
||||
* @param BaseQuery|Model|string $dbQuery
|
||||
* @param string $template 视图模板名称
|
||||
* @param string $field 指定数据主键
|
||||
* @param mixed $where 限定更新条件
|
||||
* @param array $edata 表单扩展数据
|
||||
* @return void|array|boolean
|
||||
* @throws \think\admin\Exception
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function init($dbQuery, string $template = '', string $field = '', $where = [], array $edata = [])
|
||||
{
|
||||
$query = static::buildQuery($dbQuery);
|
||||
$field = $field ?: ($query->getPk() ?: 'id');
|
||||
$value = $edata[$field] ?? input($field);
|
||||
if ($this->app->request->isGet()) {
|
||||
if ($value !== null) {
|
||||
$exist = $query->where([$field => $value])->where($where)->find();
|
||||
if ($exist instanceof Model) $exist = $exist->toArray();
|
||||
$edata = array_merge($edata, $exist ?: []);
|
||||
}
|
||||
if (false !== $this->class->callback('_form_filter', $edata)) {
|
||||
$this->class->fetch($template, ['vo' => $edata]);
|
||||
} else {
|
||||
return $edata;
|
||||
}
|
||||
}
|
||||
if ($this->app->request->isPost()) {
|
||||
$edata = array_merge($this->app->request->post(), $edata);
|
||||
if (false !== $this->class->callback('_form_filter', $edata, $where)) {
|
||||
$result = SystemService::save($query, $edata, $field, $where) !== false;
|
||||
if (false !== $this->class->callback('_form_result', $result, $edata)) {
|
||||
if ($result !== false) {
|
||||
$this->class->success('数据保存成功!');
|
||||
} else {
|
||||
$this->class->error('数据保存失败!');
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
194
plugin/think-library/src/helper/PageHelper.php
Normal file
194
plugin/think-library/src/helper/PageHelper.php
Normal file
@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\helper;
|
||||
|
||||
use think\admin\Helper;
|
||||
use think\admin\service\AdminService;
|
||||
use think\db\BaseQuery;
|
||||
use think\db\Query;
|
||||
use think\exception\HttpResponseException;
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 列表处理管理器
|
||||
* @class PageHelper
|
||||
* @package think\admin\helper
|
||||
*/
|
||||
class PageHelper extends Helper
|
||||
{
|
||||
/**
|
||||
* 逻辑器初始化
|
||||
* @param BaseQuery|Model|string $dbQuery
|
||||
* @param boolean|integer $page 是否分页或指定分页
|
||||
* @param boolean $display 是否渲染模板
|
||||
* @param boolean|integer $total 集合分页记录数
|
||||
* @param integer $limit 集合每页记录数
|
||||
* @param string $template 模板文件名称
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function init($dbQuery, $page = true, bool $display = true, $total = false, int $limit = 0, string $template = ''): array
|
||||
{
|
||||
$query = $this->autoSortQuery($dbQuery);
|
||||
if ($page !== false) {
|
||||
$get = $this->app->request->get();
|
||||
$limits = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200];
|
||||
if ($limit <= 1) {
|
||||
$limit = $get['limit'] ?? $this->app->cookie->get('limit', 20);
|
||||
if (in_array($limit, $limits) && ($get['not_cache_limit'] ?? 0) < 1) {
|
||||
$this->app->cookie->set('limit', ($limit = intval($limit >= 5 ? $limit : 20)) . '');
|
||||
}
|
||||
}
|
||||
$inner = strpos($get['spm'] ?? '', 'm-') === 0;
|
||||
$prefix = $inner ? (sysuri('admin/index/index') . '#') : '';
|
||||
// 生成分页数据
|
||||
$config = ['list_rows' => $limit, 'query' => $get];
|
||||
if (is_numeric($page)) $config['page'] = $page;
|
||||
$data = ($paginate = $query->paginate($config, $this->getCount($query, $total)))->toArray();
|
||||
$result = ['page' => ['limit' => $data['per_page'], 'total' => $data['total'], 'pages' => $data['last_page'], 'current' => $data['current_page']], 'list' => $data['data']];
|
||||
// 分页跳转参数
|
||||
$select = "<select onchange='location.href=this.options[this.selectedIndex].value'>";
|
||||
if (in_array($limit, $limits)) foreach ($limits as $num) {
|
||||
$get = array_merge($get, ['limit' => $num, 'page' => 1]);
|
||||
$url = $this->app->request->baseUrl() . '?' . http_build_query($get, '', '&', PHP_QUERY_RFC3986);
|
||||
$select .= sprintf('<option data-num="%d" value="%s" %s>%d</option>', $num, $prefix . $url, $limit === $num ? 'selected' : '', $num);
|
||||
} else {
|
||||
$select .= "<option selected>{$limit}</option>";
|
||||
}
|
||||
$html = lang('共 %s 条记录,每页显示 %s 条,共 %s 页当前显示第 %s 页。', [$data['total'], "{$select}</select>", $data['last_page'], $data['current_page']]);
|
||||
$link = $inner ? str_replace('<a href="', '<a data-open="' . $prefix, $paginate->render() ?: '') : ($paginate->render() ?: '');
|
||||
$this->class->assign('pagehtml', "<div class='pagination-container nowrap'><span>{$html}</span>{$link}</div>");
|
||||
} else {
|
||||
$result = ['list' => $query->select()->toArray()];
|
||||
}
|
||||
if (false !== $this->class->callback('_page_filter', $result['list'], $result) && $display) {
|
||||
if ($this->output === 'get.json') {
|
||||
$this->class->success('JSON-DATA', $result);
|
||||
} else {
|
||||
$this->class->fetch($template, $result);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件 Layui.Table 处理
|
||||
* @param BaseQuery|Model|string $dbQuery
|
||||
* @param string $template
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function layTable($dbQuery, string $template = ''): array
|
||||
{
|
||||
if ($this->output === 'get.json') {
|
||||
$get = $this->app->request->get();
|
||||
$query = static::buildQuery($dbQuery);
|
||||
// 根据参数排序
|
||||
if (isset($get['_field_']) && isset($get['_order_'])) {
|
||||
$dbQuery->order("{$get['_field_']} {$get['_order_']}");
|
||||
}
|
||||
return PageHelper::instance()->init($query);
|
||||
}
|
||||
if ($this->output === 'get.layui.table') {
|
||||
$get = $this->app->request->get();
|
||||
$query = $this->autoSortQuery($dbQuery);
|
||||
// 根据参数排序
|
||||
if (isset($get['_field_']) && isset($get['_order_'])) {
|
||||
$query->order("{$get['_field_']} {$get['_order_']}");
|
||||
}
|
||||
// 数据分页处理
|
||||
if (empty($get['page']) || empty($get['limit'])) {
|
||||
$data = $query->select()->toArray();
|
||||
$result = ['msg' => '', 'code' => 0, 'count' => count($data), 'data' => $data];
|
||||
} else {
|
||||
$cfg = ['list_rows' => $get['limit'], 'query' => $get];
|
||||
$data = $query->paginate($cfg, static::getCount($query))->toArray();
|
||||
$result = ['msg' => '', 'code' => 0, 'count' => $data['total'], 'data' => $data['data']];
|
||||
}
|
||||
if (false !== $this->class->callback('_page_filter', $result['data'], $result)) {
|
||||
static::xssFilter($result['data']);
|
||||
throw new HttpResponseException(json($result));
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
} else {
|
||||
$this->class->fetch($template);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出 XSS 过滤处理
|
||||
* @param array $items
|
||||
*/
|
||||
private static function xssFilter(array &$items)
|
||||
{
|
||||
foreach ($items as &$item) if (is_array($item)) {
|
||||
static::xssFilter($item);
|
||||
} elseif (is_string($item)) {
|
||||
$item = htmlspecialchars($item, ENT_QUOTES);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询对象数量统计
|
||||
* @param BaseQuery|Query $query
|
||||
* @param boolean|integer $total
|
||||
* @return integer|boolean|string
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
private static function getCount($query, $total = false)
|
||||
{
|
||||
if ($total === true || is_numeric($total)) return $total;
|
||||
[$query, $options] = [clone $query, $query->getOptions()];
|
||||
if (isset($options['order'])) $query->removeOption('order');
|
||||
if (empty($options['union'])) return $query->count();
|
||||
$table = [$query->buildSql() => '_union_count_'];
|
||||
return $query->newQuery()->table($table)->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定排序并返回操作对象
|
||||
* @param BaseQuery|Model|string $dbQuery
|
||||
* @param string $field 指定排序字段
|
||||
* @return \think\db\Query
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public function autoSortQuery($dbQuery, string $field = 'sort'): Query
|
||||
{
|
||||
$query = static::buildQuery($dbQuery);
|
||||
if ($this->app->request->isPost() && $this->app->request->post('action') === 'sort') {
|
||||
AdminService::isLogin() or $this->class->error('请重新登录!');
|
||||
if (method_exists($query, 'getTableFields') && in_array($field, $query->getTableFields())) {
|
||||
if ($this->app->request->has($pk = $query->getPk() ?: 'id', 'post')) {
|
||||
$map = [$pk => $this->app->request->post($pk, 0)];
|
||||
$data = [$field => intval($this->app->request->post($field, 0))];
|
||||
$query->newQuery()->where($map)->update($data);
|
||||
$this->class->success('列表排序成功!', '');
|
||||
}
|
||||
}
|
||||
$this->class->error('列表排序失败!');
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
398
plugin/think-library/src/helper/QueryHelper.php
Normal file
398
plugin/think-library/src/helper/QueryHelper.php
Normal file
@ -0,0 +1,398 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\helper;
|
||||
|
||||
use think\admin\Helper;
|
||||
use think\admin\service\SystemService;
|
||||
use think\Container;
|
||||
use think\db\BaseQuery;
|
||||
use think\db\Query;
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 搜索条件处理器
|
||||
* @see \think\db\Query
|
||||
* @mixin \think\db\Query
|
||||
* @class QueryHelper
|
||||
* @package think\admin\helper
|
||||
*
|
||||
* --- 动态方法调用声明
|
||||
* @method bool mSave(array $data = [], string $field = '', mixed $where = []) 快捷更新
|
||||
* @method bool mDelete(string $field = '', mixed $where = []) 快捷删除
|
||||
* @method bool|array mForm(string $tpl = '', string $field = '', mixed $where = [], array $data = []) 快捷表单
|
||||
* @method bool|integer mUpdate(array $data = [], string $field = '', mixed $where = []) 快捷保存
|
||||
*/
|
||||
class QueryHelper extends Helper
|
||||
{
|
||||
/**
|
||||
* 分页助手工具
|
||||
* @var PageHelper
|
||||
*/
|
||||
protected $page;
|
||||
|
||||
/**
|
||||
* 当前数据操作
|
||||
* @var Query
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* 初始化默认数据
|
||||
* @var array
|
||||
*/
|
||||
protected $input;
|
||||
|
||||
/**
|
||||
* 获取当前Db操作对象
|
||||
* @return Query
|
||||
*/
|
||||
public function db(): Query
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 逻辑器初始化
|
||||
* @param BaseQuery|Model|string $dbQuery
|
||||
* @param string|array|null $input 输入数据
|
||||
* @param callable|null $callable 初始回调
|
||||
* @return $this
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public function init($dbQuery, $input = null, ?callable $callable = null): QueryHelper
|
||||
{
|
||||
$this->page = PageHelper::instance();
|
||||
$this->input = $this->getInputData($input);
|
||||
$this->query = $this->page->autoSortQuery($dbQuery);
|
||||
is_callable($callable) && call_user_func($callable, $this, $this->query);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Like 查询条件
|
||||
* @param string|array $fields 查询字段
|
||||
* @param string $split 前后分割符
|
||||
* @param string|array|null $input 输入数据
|
||||
* @param string $alias 别名分割符
|
||||
* @return $this
|
||||
*/
|
||||
public function like($fields, string $split = '', $input = null, string $alias = '#'): QueryHelper
|
||||
{
|
||||
$data = $this->getInputData($input ?: $this->input);
|
||||
foreach (is_array($fields) ? $fields : explode(',', $fields) as $field) {
|
||||
[$dk, $qk] = [$field, $field];
|
||||
if (stripos($field, $alias) !== false) {
|
||||
[$dk, $qk] = explode($alias, $field);
|
||||
}
|
||||
if (isset($data[$qk]) && $data[$qk] !== '') {
|
||||
$this->query->whereLike($dk, "%{$split}{$data[$qk]}{$split}%");
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Equal 查询条件
|
||||
* @param string|array $fields 查询字段
|
||||
* @param string|array|null $input 输入类型
|
||||
* @param string $alias 别名分割符
|
||||
* @return $this
|
||||
*/
|
||||
public function equal($fields, $input = null, string $alias = '#'): QueryHelper
|
||||
{
|
||||
$data = $this->getInputData($input ?: $this->input);
|
||||
foreach (is_array($fields) ? $fields : explode(',', $fields) as $field) {
|
||||
[$dk, $qk] = [$field, $field];
|
||||
if (stripos($field, $alias) !== false) {
|
||||
[$dk, $qk] = explode($alias, $field);
|
||||
}
|
||||
if (isset($data[$qk]) && $data[$qk] !== '') {
|
||||
$this->query->where($dk, strval($data[$qk]));
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 IN 区间查询
|
||||
* @param string|array $fields 查询字段
|
||||
* @param string $split 输入分隔符
|
||||
* @param string|array|null $input 输入数据
|
||||
* @param string $alias 别名分割符
|
||||
* @return $this
|
||||
*/
|
||||
public function in($fields, string $split = ',', $input = null, string $alias = '#'): QueryHelper
|
||||
{
|
||||
$data = $this->getInputData($input ?: $this->input);
|
||||
foreach (is_array($fields) ? $fields : explode(',', $fields) as $field) {
|
||||
[$dk, $qk] = [$field, $field];
|
||||
if (stripos($field, $alias) !== false) {
|
||||
[$dk, $qk] = explode($alias, $field);
|
||||
}
|
||||
if (isset($data[$qk]) && $data[$qk] !== '') {
|
||||
$this->query->whereIn($dk, explode($split, strval($data[$qk])));
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 两字段范围查询
|
||||
* @example field1:field2#field,field11:field22#field00
|
||||
* @param string|array $fields 查询字段
|
||||
* @param string|array|null $input 输入数据
|
||||
* @param string $alias 别名分割符
|
||||
* @return $this
|
||||
*/
|
||||
public function valueRange($fields, $input = null, string $alias = '#'): QueryHelper
|
||||
{
|
||||
$data = $this->getInputData($input ?: $this->input);
|
||||
foreach (is_array($fields) ? $fields : explode(',', $fields) as $field) {
|
||||
if (strpos($field, ':') !== false) {
|
||||
if (stripos($field, $alias) !== false) {
|
||||
[$dk0, $qk0] = explode($alias, $field);
|
||||
[$dk1, $dk2] = explode(':', $dk0);
|
||||
} else {
|
||||
[$qk0] = [$dk1, $dk2] = explode(':', $field, 2);
|
||||
}
|
||||
if (isset($data[$qk0]) && $data[$qk0] !== '') {
|
||||
$this->query->where([[$dk1, '<=', $data[$qk0]], [$dk2, '>=', $data[$qk0]]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置内容区间查询
|
||||
* @param string|array $fields 查询字段
|
||||
* @param string $split 输入分隔符
|
||||
* @param string|array|null $input 输入数据
|
||||
* @param string $alias 别名分割符
|
||||
* @return $this
|
||||
*/
|
||||
public function valueBetween($fields, string $split = ' ', $input = null, string $alias = '#'): QueryHelper
|
||||
{
|
||||
return $this->setBetweenWhere($fields, $split, $input, $alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置日期时间区间查询
|
||||
* @param string|array $fields 查询字段
|
||||
* @param string $split 输入分隔符
|
||||
* @param string|array|null $input 输入数据
|
||||
* @param string $alias 别名分割符
|
||||
* @return $this
|
||||
*/
|
||||
public function dateBetween($fields, string $split = ' - ', $input = null, string $alias = '#'): QueryHelper
|
||||
{
|
||||
return $this->setBetweenWhere($fields, $split, $input, $alias, static function ($value, $type) {
|
||||
if (preg_match('#^\d{4}(-\d\d){2}\s+\d\d(:\d\d){2}$#', $value)) return $value;
|
||||
return $type === 'after' ? "{$value} 23:59:59" : "{$value} 00:00:00";
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置时间戳区间查询
|
||||
* @param string|array $fields 查询字段
|
||||
* @param string $split 输入分隔符
|
||||
* @param string|array|null $input 输入数据
|
||||
* @param string $alias 别名分割符
|
||||
* @return $this
|
||||
*/
|
||||
public function timeBetween($fields, string $split = ' - ', $input = null, string $alias = '#'): QueryHelper
|
||||
{
|
||||
return $this->setBetweenWhere($fields, $split, $input, $alias, static function ($value, $type) {
|
||||
if (preg_match('#^\d{4}(-\d\d){2}\s+\d\d(:\d\d){2}$#', $value)) return strtotime($value);
|
||||
return $type === 'after' ? strtotime("{$value} 23:59:59") : strtotime("{$value} 00:00:00");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化分页管理器
|
||||
* @param boolean|integer $page 是否启用分页
|
||||
* @param boolean $display 是否渲染模板
|
||||
* @param boolean|integer $total 集合分页记录数
|
||||
* @param integer $limit 集合每页记录数
|
||||
* @param string $template 模板文件名称
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function page($page = true, bool $display = true, $total = false, int $limit = 0, string $template = ''): array
|
||||
{
|
||||
return $this->page->init($this->query, $page, $display, $total, $limit, $template);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空数据并保留表结构
|
||||
* @return $this
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public function empty(): QueryHelper
|
||||
{
|
||||
$table = $this->query->getTable();
|
||||
$ctype = strtolower($this->query->getConfig('type'));
|
||||
if ($ctype === 'mysql') {
|
||||
$this->query->getConnection()->execute("truncate table `{$table}`");
|
||||
} elseif (in_array($ctype, ['sqlsrv', 'oracle', 'pgsql'])) {
|
||||
$this->query->getConnection()->execute("truncate table {$table}");
|
||||
} else {
|
||||
$this->query->newQuery()->whereRaw('1=1')->delete();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 中间回调处理
|
||||
* @param callable $after
|
||||
* @return $this
|
||||
*/
|
||||
public function filter(callable $after): QueryHelper
|
||||
{
|
||||
call_user_func($after, $this, $this->query);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Layui.Table 组件数据
|
||||
* @param ?callable $befor 表单前置操作
|
||||
* @param ?callable $after 表单后置操作
|
||||
* @param string $template 视图模板文件
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function layTable(?callable $befor = null, ?callable $after = null, string $template = '')
|
||||
{
|
||||
if (in_array($this->output, ['get.json', 'get.layui.table'])) {
|
||||
if (is_callable($after)) {
|
||||
call_user_func($after, $this, $this->query);
|
||||
}
|
||||
$this->page->layTable($this->query, $template);
|
||||
} else {
|
||||
if (is_callable($befor)) {
|
||||
call_user_func($befor, $this, $this->query);
|
||||
}
|
||||
$this->class->fetch($template);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置区域查询条件
|
||||
* @param string|array $fields 查询字段
|
||||
* @param string $split 输入分隔符
|
||||
* @param string|array|null $input 输入数据
|
||||
* @param string $alias 别名分割符
|
||||
* @param callable|null $callback 回调函数
|
||||
* @return $this
|
||||
*/
|
||||
private function setBetweenWhere($fields, string $split = ' ', $input = null, string $alias = '#', ?callable $callback = null): QueryHelper
|
||||
{
|
||||
$data = $this->getInputData($input ?: $this->input);
|
||||
foreach (is_array($fields) ? $fields : explode(',', $fields) as $field) {
|
||||
[$dk, $qk] = [$field, $field];
|
||||
if (stripos($field, $alias) !== false) {
|
||||
[$dk, $qk] = explode($alias, $field);
|
||||
}
|
||||
if (isset($data[$qk]) && $data[$qk] !== '') {
|
||||
[$begin, $after] = explode($split, strval($data[$qk]));
|
||||
if (is_callable($callback)) {
|
||||
$after = call_user_func($callback, $after, 'after');
|
||||
$begin = call_user_func($callback, $begin, 'begin');
|
||||
}
|
||||
$this->query->whereBetween($dk, [$begin, $after]);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输入数据
|
||||
* @param string|array|null $input
|
||||
* @return array
|
||||
*/
|
||||
private function getInputData($input): array
|
||||
{
|
||||
if (is_array($input)) {
|
||||
return $input;
|
||||
} else {
|
||||
$input = $input ?: 'request';
|
||||
return $this->app->request->$input();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 克隆属性复制
|
||||
* @return void
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->page = clone $this->page;
|
||||
$this->query = clone $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* QueryHelper call.
|
||||
* @param string $name 调用方法名称
|
||||
* @param array $args 调用参数内容
|
||||
* @return $this|mixed
|
||||
*/
|
||||
public function __call(string $name, array $args)
|
||||
{
|
||||
return static::make($this->query, $name, $args, function ($name, $args) {
|
||||
if (is_callable($callable = [$this->query, $name])) {
|
||||
$value = call_user_func_array($callable, $args);
|
||||
if ($name[0] === '_' || $value instanceof $this->query) {
|
||||
return $this;
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
} else {
|
||||
return $this;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 快捷助手调用勾子
|
||||
* @param string|Model|Query $model
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @param callable|null $nohook
|
||||
* @return mixed|false|integer|QueryHelper
|
||||
*/
|
||||
public static function make($model, string $method = 'init', array $args = [], ?callable $nohook = null)
|
||||
{
|
||||
$hooks = [
|
||||
'mForm' => [FormHelper::class, 'init'],
|
||||
'mSave' => [SaveHelper::class, 'init'],
|
||||
'mQuery' => [QueryHelper::class, 'init'],
|
||||
'mDelete' => [DeleteHelper::class, 'init'],
|
||||
'mUpdate' => [SystemService::class, 'save'],
|
||||
];
|
||||
if (isset($hooks[$method])) {
|
||||
[$class, $method] = $hooks[$method];
|
||||
return Container::getInstance()->invokeClass($class)->$method($model, ...$args);
|
||||
} else {
|
||||
return is_callable($nohook) ? $nohook($method, $args) : false;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
plugin/think-library/src/helper/SaveHelper.php
Normal file
78
plugin/think-library/src/helper/SaveHelper.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\helper;
|
||||
|
||||
use think\admin\Helper;
|
||||
use think\db\BaseQuery;
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 数据更新管理器
|
||||
* @class SaveHelper
|
||||
* @package think\admin\helper
|
||||
*/
|
||||
class SaveHelper extends Helper
|
||||
{
|
||||
|
||||
/**
|
||||
* 逻辑器初始化
|
||||
* @param BaseQuery|Model|string $dbQuery
|
||||
* @param array $edata 表单扩展数据
|
||||
* @param string $field 数据对象主键
|
||||
* @param mixed $where 额外更新条件
|
||||
* @return boolean|void
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public function init($dbQuery, array $edata = [], string $field = '', $where = [])
|
||||
{
|
||||
$query = static::buildQuery($dbQuery);
|
||||
$field = $field ?: ($query->getPk() ?: 'id');
|
||||
$edata = $edata ?: $this->app->request->post();
|
||||
$value = $this->app->request->post($field);
|
||||
|
||||
// 主键限制处理
|
||||
if (!isset($where[$field]) && !is_null($value)) {
|
||||
$query->whereIn($field, str2arr(strval($value)));
|
||||
if (isset($edata)) unset($edata[$field]);
|
||||
}
|
||||
|
||||
// 前置回调处理
|
||||
if (false === $this->class->callback('_save_filter', $query, $edata)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查原始数据
|
||||
$query->master()->where($where)->update($edata);
|
||||
|
||||
// 模型自定义事件回调
|
||||
$model = $query->getModel();
|
||||
if ($model instanceof \think\admin\Model) {
|
||||
$model->onAdminSave(strval($value));
|
||||
}
|
||||
|
||||
// 结果回调处理
|
||||
$result = true;
|
||||
if (false === $this->class->callback('_save_result', $result, $model)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// 回复前端结果
|
||||
$this->class->success('数据保存成功!', '');
|
||||
}
|
||||
}
|
||||
71
plugin/think-library/src/helper/TokenHelper.php
Normal file
71
plugin/think-library/src/helper/TokenHelper.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\helper;
|
||||
|
||||
use think\admin\Helper;
|
||||
use think\admin\Library;
|
||||
use think\exception\HttpResponseException;
|
||||
|
||||
/**
|
||||
* 表单令牌验证器
|
||||
* @class TokenHelper
|
||||
* @package think\admin\helper
|
||||
*/
|
||||
class TokenHelper extends Helper
|
||||
{
|
||||
/**
|
||||
* 初始化验证码器
|
||||
* @param boolean $return
|
||||
* @return boolean|void
|
||||
*/
|
||||
public function init(bool $return = false)
|
||||
{
|
||||
$this->class->csrf_state = true;
|
||||
if (!$this->app->request->isPost()) return true;
|
||||
$token = $this->app->request->post('_token_');
|
||||
$extra = ['_token_' => $token ?: $this->app->request->header('User-Form-Token')];
|
||||
if ($this->app->request->checkToken('_token_', $extra)) return true; elseif ($return) return false;
|
||||
$this->class->error($this->class->csrf_message ?: '表单令牌验证失败!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回视图内容
|
||||
* @param string $tpl 模板名称
|
||||
* @param array $vars 模板变量
|
||||
* @param string|null $node 授权节点
|
||||
*/
|
||||
public static function fetch(string $tpl = '', array $vars = [], ?string $node = null)
|
||||
{
|
||||
throw new HttpResponseException(view($tpl, $vars, 200, static function ($html) use ($node) {
|
||||
return preg_replace_callback('/<\/form>/i', static function () use ($node) {
|
||||
return sprintf("<input type='hidden' name='_token_' value='%s'></form>", static::token());
|
||||
}, $html);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回表单令牌数据
|
||||
* 为了兼容JWT模式使用表单令牌
|
||||
* @return string
|
||||
*/
|
||||
public static function token(): string
|
||||
{
|
||||
return Library::$sapp->request->buildToken('_token_');
|
||||
}
|
||||
}
|
||||
78
plugin/think-library/src/helper/ValidateHelper.php
Normal file
78
plugin/think-library/src/helper/ValidateHelper.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\helper;
|
||||
|
||||
use think\admin\Helper;
|
||||
use think\Validate;
|
||||
|
||||
/**
|
||||
* 快捷输入验证器
|
||||
* @class ValidateHelper
|
||||
* @package think\admin\helper
|
||||
*/
|
||||
class ValidateHelper extends Helper
|
||||
{
|
||||
/**
|
||||
* 快捷输入并验证( 支持 规则 # 别名 )
|
||||
* @param array $rules 验证规则( 验证信息数组 )
|
||||
* @param string|array $input 输入内容 ( post. 或 get. )
|
||||
* @param callable|null $callable 异常处理操作
|
||||
* @return array|void
|
||||
*
|
||||
* age.require => message // 最大值限定
|
||||
* age.between:1,120 => message // 范围限定
|
||||
* name.require => message // 必填内容
|
||||
* name.default => 100 // 获取并设置默认值
|
||||
* region.value => value // 固定字段数值内容
|
||||
*
|
||||
* 更多规则参照 ThinkPHP 官方的验证类
|
||||
*/
|
||||
public function init(array $rules, $input = '', ?callable $callable = null)
|
||||
{
|
||||
if (is_string($input)) {
|
||||
$type = trim($input, '.') ?: 'param';
|
||||
$input = $this->app->request->$type();
|
||||
}
|
||||
[$data, $rule, $info] = [[], [], []];
|
||||
foreach ($rules as $name => $message) if (is_numeric($name)) {
|
||||
[$name, $alias] = explode('#', $message . '#');
|
||||
$data[$name] = $input[($alias ?: $name)] ?? null;
|
||||
} elseif (strpos($name, '.') === false) {
|
||||
$data[$name] = $message;
|
||||
} elseif (preg_match('|^(.*?)\.(.*?)#(.*?)#?$|', $name . '#', $matches)) {
|
||||
[, $_key, $_rule, $alias] = $matches;
|
||||
if (in_array($_rule, ['value', 'default'])) {
|
||||
if ($_rule === 'value') $data[$_key] = $message;
|
||||
elseif ($_rule === 'default') $data[$_key] = $input[($alias ?: $_key)] ?? $message;
|
||||
} else {
|
||||
$info[explode(':', $name)[0]] = $message;
|
||||
$data[$_key] = $data[$_key] ?? ($input[($alias ?: $_key)] ?? null);
|
||||
$rule[$_key] = isset($rule[$_key]) ? ($rule[$_key] . '|' . $_rule) : $_rule;
|
||||
}
|
||||
}
|
||||
$validate = new Validate();
|
||||
if ($validate->rule($rule)->message($info)->check($data)) {
|
||||
return $data;
|
||||
} elseif (is_callable($callable)) {
|
||||
return call_user_func($callable, lang($validate->getError()), $data);
|
||||
} else {
|
||||
$this->class->error(lang($validate->getError()));
|
||||
}
|
||||
}
|
||||
}
|
||||
143
plugin/think-library/src/lang/en-us.php
Normal file
143
plugin/think-library/src/lang/en-us.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
use think\admin\Library;
|
||||
use think\admin\model\SystemBase;
|
||||
|
||||
// 动态读取英文数据字典
|
||||
if (count($langs = Library::$sapp->cache->get('lang-en-us', [])) < 1) {
|
||||
$langs = array_column(SystemBase::items('英文字典'), 'name', 'code');
|
||||
$menus = array_column(SystemBase::items('英文菜单'), 'name', 'code');
|
||||
foreach ($menus as $key => $name) $langs["menus_{$key}"] = $name;
|
||||
Library::$sapp->cache->set('lang-en-us', $langs, 360);
|
||||
}
|
||||
|
||||
// 定义菜单专用语言包,使用固定前缀 `menus_` 开头
|
||||
// 数据字典菜单语言包类型为:英文菜单,配置与 英文字典 相同
|
||||
// PS. 使用前缀是方便后缀追加配置,另外历史版本未开启语言分组
|
||||
// PS. 该文件仅在英文模式下才会加载,系统默认使用 `中文` 模式
|
||||
$menus = [
|
||||
// // 系统管理菜单
|
||||
// 'menus_系统管理' => 'System',
|
||||
// 'menus_系统配置' => 'Configuration',
|
||||
// 'menus_系统参数配置' => 'Parameter',
|
||||
// 'menus_系统任务管理' => 'Tasks',
|
||||
// 'menus_系统日志管理' => 'Oplog',
|
||||
// 'menus_数据字典管理' => 'Dictionary',
|
||||
// 'menus_系统文件管理' => 'File',
|
||||
// 'menus_系统菜单管理' => 'Menu',
|
||||
// 'menus_权限管理' => 'Permission',
|
||||
// 'menus_访问权限管理' => 'Role',
|
||||
// 'menus_系统用户管理' => 'User',
|
||||
// // 微信管理菜单
|
||||
// 'menus_微信管理' => 'WeChat',
|
||||
// 'menus_微信接口配置' => 'Configuration',
|
||||
// 'menus_微信支付配置' => 'Pay parameters',
|
||||
// 'menus_微信粉丝管理' => 'Fan User',
|
||||
// 'menus_微信定制' => 'Custom ',
|
||||
// 'menus_微信图文管理' => 'News',
|
||||
// 'menus_微信菜单配置' => 'Menu',
|
||||
// 'menus_回复规则管理' => 'Reply Rule',
|
||||
// 'menus_关注自动回复' => 'Auto Reply',
|
||||
// 'menus_微信支付' => 'Payment',
|
||||
// 'menus_支付行为管理' => 'Action Record',
|
||||
// 'menus_支付退款管理' => 'Refund Record',
|
||||
// // 插件中心菜单
|
||||
// 'menus_插件中心' => 'Plugins'
|
||||
];
|
||||
|
||||
$extra = [];
|
||||
$extra['Y年m月d日 H:i:s'] = 'Y/m/d H:i:s';
|
||||
$extra['请重新登录!'] = 'Invalid login authorization, Please login again.';
|
||||
$extra['共 %s 条记录,每页显示 %s 条,共 %s 页当前显示第 %s 页。'] = 'Total %s records, display %s per page, total %s page current display %s page.';
|
||||
|
||||
return array_merge([
|
||||
// 常规操作翻译
|
||||
// '全部' => 'All',
|
||||
// '添 加' => 'Add',
|
||||
// '编 辑' => 'Edit',
|
||||
// '删 除' => 'Delete',
|
||||
// '搜 索' => 'Search',
|
||||
// '导 出' => 'Export',
|
||||
// '已禁用' => 'Disabled',
|
||||
// '已激活' => 'Activated',
|
||||
// '排序权重' => 'Sort',
|
||||
// '回 收 站' => 'Recycle',
|
||||
// '保存数据' => 'Submit',
|
||||
// '取消编辑' => 'Cancel',
|
||||
// '操作面板' => 'Panel',
|
||||
// '使用状态' => 'Status',
|
||||
// '条件搜索' => 'Search',
|
||||
// '清空数据' => 'Clears Data',
|
||||
// '创建时间' => 'Create Time',
|
||||
// '批量删除' => 'Remove Selected',
|
||||
// '批量禁用' => 'Forbid Selected',
|
||||
// '批量恢复' => 'Resume Selected',
|
||||
// '已禁用记录' => 'Disabled Records',
|
||||
// '已激活记录' => 'Activated Records',
|
||||
// 接口提示内容
|
||||
'数据删除成功!' => 'Successfully deleted.',
|
||||
'数据删除失败!' => 'Sorry, Delete failed.',
|
||||
'数据保存成功!' => 'Successfully saved.',
|
||||
'数据保存失败!' => 'Sorry, Save failed.',
|
||||
'数据排序成功!' => 'Successfully Sorted.',
|
||||
'列表排序失败!' => 'Sorry, Sorting failed.',
|
||||
'请求响应异常!' => 'Sorry, Request response exception.',
|
||||
'请求响应成功!' => 'Sorry, Request response successful.',
|
||||
'未授权禁止访问!' => 'Sorry, Unauthorized access prohibited.',
|
||||
'会话无效或已失效!' => 'The session is invalid or has expired.',
|
||||
'表单令牌验证失败!' => 'The Form token is validation failed.',
|
||||
'接口账号验证失败!' => 'Interface account verification failed.',
|
||||
'接口请求时差过大!' => 'Interface request time difference too large.',
|
||||
'接口签名验证失败!' => 'Interface signature verification failed.',
|
||||
'非JWT访问!' => 'Please use JWT to access.',
|
||||
'请求参数 %s 不能为空!' => 'Request parameter %s cannot be empty.',
|
||||
'接口请求响应格式异常!' => 'Abnormal format of interface request response.',
|
||||
'耗时 %.4f 秒' => 'Time taken %.4f seconds',
|
||||
'创建任务失败,%s' => 'Failed to create task, %s',
|
||||
'已创建请等待处理完成!' => 'Task has been created, please wait for processing to complete.',
|
||||
'删除%s[%s]及授权配置' => 'Delete %s[%s] and authorization configuration',
|
||||
'暂无轨迹信息~' => 'No trajectory information currently available',
|
||||
// 存储引擎翻译
|
||||
'本地服务器存储' => 'Local server storage',
|
||||
'自建Alist存储' => 'Self built Alist storage',
|
||||
'又拍云USS存储' => 'Upyun Cloud USS storage',
|
||||
'阿里云OSS存储' => 'Aliyun Cloud OSS storage',
|
||||
'腾讯云COS存储' => 'Tencent Cloud COS Storage',
|
||||
'七牛云对象存储' => 'Qiniu Cloud Object storage',
|
||||
'未配置又拍云域名' => 'Unconfigured Upyun Cloud domain',
|
||||
'未配置阿里云域名' => 'Unconfigured Aliyun Cloud domain',
|
||||
'未配置七牛云域名' => 'Unconfigured Qiniu Cloud domain',
|
||||
'未配置腾讯云域名' => 'Unconfigured Tencent Cloud domain',
|
||||
'未配置Alist域名' => 'Unconfigured Alist Server domain',
|
||||
// 默认日志翻译
|
||||
'增加%s[%s]成功' => 'Added: %s [ %s ]',
|
||||
'修改%s[%s]状态' => 'Modify: %s [ %s ]',
|
||||
'更新%s[%s]记录' => 'Update: %s [ %s ]',
|
||||
'删除%s[%s]成功' => 'Remove: %s [ %s ]',
|
||||
// 模块管理翻译
|
||||
// '系统任务管理' => 'System Task Management',
|
||||
// '系统菜单管理' => 'System Menu Management',
|
||||
// '系统文件管理' => 'System File Management',
|
||||
// '系统用户管理' => 'System User Management',
|
||||
// '系统日志管理' => 'System Oplog Management',
|
||||
// '系统参数配置' => 'System Parameter Management',
|
||||
// '系统权限管理' => 'System Permission Management',
|
||||
// '数据字典管理' => 'System Dictionary Management',
|
||||
// '系统运维管理' => 'System Maintenance Management',
|
||||
], $extra, $menus, $langs);
|
||||
76
plugin/think-library/src/lang/zh-tw.php
Normal file
76
plugin/think-library/src/lang/zh-tw.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
use think\admin\Library;
|
||||
use think\admin\model\SystemBase;
|
||||
|
||||
// 动态读取繁体数据字典
|
||||
if (count($langs = Library::$sapp->cache->get('lang-zh-tw', [])) < 1) {
|
||||
$langs = array_column(SystemBase::items('繁体中文'), 'name', 'code');
|
||||
$menus = array_column(SystemBase::items('繁体菜单'), 'name', 'code');
|
||||
foreach ($menus as $key => $name) $langs["menus_{$key}"] = $name;
|
||||
Library::$sapp->cache->set('lang-zh-tw', $langs, 360);
|
||||
}
|
||||
|
||||
$extra = [];
|
||||
$extra['Y年m月d日 H:i:s'] = 'Y年m月d日 H:i:s';
|
||||
$extra['请重新登录!'] = '登錄授權無效,請重新登錄!';
|
||||
$extra['共 %s 条记录,每页显示 %s 条,共 %s 页当前显示第 %s 页。'] = '共 %s 條記錄,每頁顯示 %s 條,共 %s 頁當前顯示第 %s 頁。';
|
||||
|
||||
return array_merge([
|
||||
// 接口提示内容
|
||||
'数据删除成功!' => '數據刪除成功!',
|
||||
'数据删除失败!' => '數據刪除失敗!',
|
||||
'数据保存成功!' => '數據保存成功!',
|
||||
'数据保存失败!' => '數據保存失敗!',
|
||||
'数据排序成功!' => '數據排序成功!',
|
||||
'列表排序失败!' => '列表排序失敗!',
|
||||
'请求响应异常!' => '請求響應異常!',
|
||||
'请求响应成功!' => '請求響應成功!',
|
||||
'未授权禁止访问!' => '未授權禁止訪問!',
|
||||
'会话无效或已失效!' => '會話無效或已失效!',
|
||||
'表单令牌验证失败!' => '表單令牌驗證失敗!',
|
||||
'接口账号验证失败!' => '接口账号验证失败!',
|
||||
'接口请求时差过大!' => '接口請求時差過大!',
|
||||
'接口签名验证失败!' => '接口簽名驗證失敗!',
|
||||
'非JWT访问!' => '請使用 JWT 方式訪問!',
|
||||
'请求参数 %s 不能为空!' => '請求參數 %s 不能爲空!',
|
||||
'接口请求响应格式异常!' => '接口請求響應格式異常!',
|
||||
'耗时 %.4f 秒' => '耗時 %.4f 秒',
|
||||
'创建任务失败,%s' => '創建任務失敗,%s',
|
||||
'已创建请等待处理完成!' => '已創建請等待處理完成!',
|
||||
'删除%s[%s]及授权配置' => '刪除%s[%s]及授權配置',
|
||||
'暂无轨迹信息~' => '暫無軌迹信息~',
|
||||
// 存储引擎翻译
|
||||
'本地服务器存储' => '本地服務器存儲',
|
||||
'自建Alist存储' => '自建Alist存儲',
|
||||
'七牛云对象存储' => '七牛雲對象存儲',
|
||||
'又拍云USS存储' => '又拍雲USS存儲',
|
||||
'阿里云OSS存储' => '阿裏雲OSS存儲',
|
||||
'腾讯云COS存储' => '騰訊雲COS存儲',
|
||||
'未配置又拍云域名' => '未配置又拍雲域名',
|
||||
'未配置阿里云域名' => '未配置阿裏雲域名',
|
||||
'未配置七牛云域名' => '未配置七牛雲域名',
|
||||
'未配置腾讯云域名' => '未配置腾讯云域名',
|
||||
'未配置Alist域名' => '未配置Alist域名',
|
||||
// 默认日志翻译
|
||||
'增加%s[%s]成功' => '增加%s[%s]成功',
|
||||
'修改%s[%s]状态' => '修改%s[%s]狀態',
|
||||
'更新%s[%s]记录' => '更新%s[%s]記錄',
|
||||
'删除%s[%s]成功' => '刪除%s[%s]成功',
|
||||
], $extra, $langs);
|
||||
76
plugin/think-library/src/model/SystemAuth.php
Normal file
76
plugin/think-library/src/model/SystemAuth.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\model;
|
||||
|
||||
use think\admin\Model;
|
||||
|
||||
/**
|
||||
* 用户权限模型
|
||||
* @class SystemAuth
|
||||
* @package think\admin\model
|
||||
*/
|
||||
class SystemAuth extends Model
|
||||
{
|
||||
protected $createTime = 'create_at';
|
||||
protected $updateTime = false;
|
||||
|
||||
/**
|
||||
* 日志名称
|
||||
* @var string
|
||||
*/
|
||||
protected $oplogName = '系统权限';
|
||||
|
||||
/**
|
||||
* 日志类型
|
||||
* @var string
|
||||
*/
|
||||
protected $oplogType = '系统权限管理';
|
||||
|
||||
/**
|
||||
* 获取权限数据
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function items(): array
|
||||
{
|
||||
return static::mk()->where(['status' => 1])->order('sort desc,id desc')->select()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除权限事件
|
||||
* @param string $ids
|
||||
*/
|
||||
public function onAdminDelete(string $ids)
|
||||
{
|
||||
if (count($aids = str2arr($ids)) > 0) SystemNode::mk()->whereIn('auth', $aids)->delete();
|
||||
sysoplog($this->oplogType, lang("删除%s[%s]及授权配置", [lang($this->oplogName), $ids]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化创建时间
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getCreateAtAttr($value): string
|
||||
{
|
||||
return format_datetime($value);
|
||||
}
|
||||
}
|
||||
82
plugin/think-library/src/model/SystemBase.php
Normal file
82
plugin/think-library/src/model/SystemBase.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\model;
|
||||
|
||||
use think\admin\Model;
|
||||
|
||||
/**
|
||||
* 数据字典模型
|
||||
* @class SystemBase
|
||||
* @package think\admin\model
|
||||
*/
|
||||
class SystemBase extends Model
|
||||
{
|
||||
protected $createTime = 'create_at';
|
||||
protected $updateTime = false;
|
||||
|
||||
/**
|
||||
* 日志名称
|
||||
* @var string
|
||||
*/
|
||||
protected $oplogName = '数据字典';
|
||||
|
||||
/**
|
||||
* 日志类型
|
||||
* @var string
|
||||
*/
|
||||
protected $oplogType = '数据字典管理';
|
||||
|
||||
/**
|
||||
* 获取指定数据列表
|
||||
* @param string $type 数据类型
|
||||
* @param array $data 外围数据
|
||||
* @param string $field 外链字段
|
||||
* @param string $bind 绑定字段
|
||||
* @return array
|
||||
*/
|
||||
public static function items(string $type, array &$data = [], string $field = 'base_code', string $bind = 'base_info'): array
|
||||
{
|
||||
$map = ['type' => $type, 'status' => 1, 'deleted' => 0];
|
||||
$bases = static::mk()->where($map)->order('sort desc,id asc')->column('code,name,content', 'code');
|
||||
if (count($data) > 0) foreach ($data as &$vo) $vo[$bind] = $bases[$vo[$field]] ?? [];
|
||||
return $bases;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有数据类型
|
||||
* @param boolean $simple 加载默认值
|
||||
* @return array
|
||||
*/
|
||||
public static function types(bool $simple = false): array
|
||||
{
|
||||
$types = static::mk()->where(['deleted' => 0])->distinct()->column('type');
|
||||
if (empty($types) && empty($simple)) $types = ['身份权限'];
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化创建时间
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getCreateAtAttr($value): string
|
||||
{
|
||||
return format_datetime($value);
|
||||
}
|
||||
}
|
||||
30
plugin/think-library/src/model/SystemConfig.php
Normal file
30
plugin/think-library/src/model/SystemConfig.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\model;
|
||||
|
||||
use think\admin\Model;
|
||||
|
||||
/**
|
||||
* 系统配置模型
|
||||
* @class SystemConfig
|
||||
* @package think\admin\model
|
||||
*/
|
||||
class SystemConfig extends Model
|
||||
{
|
||||
}
|
||||
30
plugin/think-library/src/model/SystemData.php
Normal file
30
plugin/think-library/src/model/SystemData.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\model;
|
||||
|
||||
use think\admin\Model;
|
||||
|
||||
/**
|
||||
* 系统数据模型
|
||||
* @class SystemData
|
||||
* @package think\admin\model
|
||||
*/
|
||||
class SystemData extends Model
|
||||
{
|
||||
}
|
||||
71
plugin/think-library/src/model/SystemFile.php
Normal file
71
plugin/think-library/src/model/SystemFile.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\model;
|
||||
|
||||
use think\admin\Model;
|
||||
use think\model\relation\HasOne;
|
||||
|
||||
/**
|
||||
* 文件管理系统
|
||||
* @class SystemFile
|
||||
* @package think\admin\model
|
||||
*/
|
||||
class SystemFile extends Model
|
||||
{
|
||||
/**
|
||||
* 创建字段
|
||||
* @var string
|
||||
*/
|
||||
protected $createTime = 'create_at';
|
||||
|
||||
/**
|
||||
* 更新字段
|
||||
* @var string
|
||||
*/
|
||||
protected $updateTime = 'update_at';
|
||||
|
||||
/**
|
||||
* 关联用户数据
|
||||
* @return \think\model\relation\HasOne
|
||||
*/
|
||||
public function user(): HasOne
|
||||
{
|
||||
return $this->hasOne(SystemUser::class, 'id', 'uuid')->field('id,username,nickname');
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化创建时间
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getCreateAtAttr($value): string
|
||||
{
|
||||
return format_datetime($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化更新时间
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getUpdateAtAttr($value): string
|
||||
{
|
||||
return format_datetime($value);
|
||||
}
|
||||
}
|
||||
54
plugin/think-library/src/model/SystemMenu.php
Normal file
54
plugin/think-library/src/model/SystemMenu.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\model;
|
||||
|
||||
use think\admin\Model;
|
||||
|
||||
/**
|
||||
* 系统菜单模型
|
||||
* @class SystemMenu
|
||||
* @package think\admin\model
|
||||
*/
|
||||
class SystemMenu extends Model
|
||||
{
|
||||
protected $createTime = 'create_at';
|
||||
protected $updateTime = false;
|
||||
|
||||
/**
|
||||
* 日志名称
|
||||
* @var string
|
||||
*/
|
||||
protected $oplogName = '系统菜单';
|
||||
|
||||
/**
|
||||
* 日志类型
|
||||
* @var string
|
||||
*/
|
||||
protected $oplogType = '系统菜单管理';
|
||||
|
||||
/**
|
||||
* 格式化创建时间
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getCreateAtAttr($value): string
|
||||
{
|
||||
return format_datetime($value);
|
||||
}
|
||||
}
|
||||
36
plugin/think-library/src/model/SystemNode.php
Normal file
36
plugin/think-library/src/model/SystemNode.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\model;
|
||||
|
||||
use think\admin\Model;
|
||||
|
||||
/**
|
||||
* 授权节点模型
|
||||
* @class SystemNode
|
||||
* @mixin \think\db\Query
|
||||
* @package think\admin\model
|
||||
*/
|
||||
class SystemNode extends Model
|
||||
{
|
||||
/**
|
||||
* 绑定模型名称
|
||||
* @var string
|
||||
*/
|
||||
protected $name = 'SystemAuthNode';
|
||||
}
|
||||
42
plugin/think-library/src/model/SystemOplog.php
Normal file
42
plugin/think-library/src/model/SystemOplog.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\model;
|
||||
|
||||
use think\admin\Model;
|
||||
|
||||
/**
|
||||
* 系统日志模型
|
||||
* @class SystemOplog
|
||||
* @package think\admin\model
|
||||
*/
|
||||
class SystemOplog extends Model
|
||||
{
|
||||
protected $createTime = 'create_at';
|
||||
protected $updateTime = false;
|
||||
|
||||
/**
|
||||
* 格式化创建时间
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getCreateAtAttr($value): string
|
||||
{
|
||||
return format_datetime($value);
|
||||
}
|
||||
}
|
||||
77
plugin/think-library/src/model/SystemQueue.php
Normal file
77
plugin/think-library/src/model/SystemQueue.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\model;
|
||||
|
||||
use think\admin\Model;
|
||||
|
||||
/**
|
||||
* 系统任务模型
|
||||
* @class SystemQueue
|
||||
* @package think\admin\model
|
||||
*/
|
||||
class SystemQueue extends Model
|
||||
{
|
||||
protected $createTime = 'create_at';
|
||||
protected $updateTime = false;
|
||||
|
||||
/**
|
||||
* 格式化计划时间
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getExecTimeAttr($value): string
|
||||
{
|
||||
return format_datetime($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行开始时间处理
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getEnterTimeAttr($value): string
|
||||
{
|
||||
return floatval($value) > 0 ? format_datetime(intval($value)) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行结束时间处理
|
||||
* @param mixed $value
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function getOuterTimeAttr($value, array $data): string
|
||||
{
|
||||
if ($value > 0 && $value > $data['enter_time']) {
|
||||
return lang("耗时 %.4f 秒", [$data['outer_time'] - $data['enter_time']]);
|
||||
} else {
|
||||
return ' - ';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化创建时间
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getCreateAtAttr($value): string
|
||||
{
|
||||
return format_datetime($value);
|
||||
}
|
||||
}
|
||||
114
plugin/think-library/src/model/SystemUser.php
Normal file
114
plugin/think-library/src/model/SystemUser.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\model;
|
||||
|
||||
use think\admin\Model;
|
||||
use think\model\relation\HasOne;
|
||||
|
||||
/**
|
||||
* 系统用户模型
|
||||
* @class SystemUser
|
||||
* @package think\admin\model
|
||||
*/
|
||||
class SystemUser extends Model
|
||||
{
|
||||
protected $createTime = 'create_at';
|
||||
protected $updateTime = false;
|
||||
|
||||
/**
|
||||
* 日志名称
|
||||
* @var string
|
||||
*/
|
||||
protected $oplogName = '系统用户';
|
||||
|
||||
/**
|
||||
* 日志类型
|
||||
* @var string
|
||||
*/
|
||||
protected $oplogType = '系统用户管理';
|
||||
|
||||
/**
|
||||
* 获取用户数据
|
||||
* @param mixed $map 数据查询规则
|
||||
* @param array $data 用户数据集合
|
||||
* @param string $field 原外连字段
|
||||
* @param string $target 关联目标字段
|
||||
* @param string $fields 关联数据字段
|
||||
* @return array
|
||||
*/
|
||||
public static function items($map, array &$data = [], string $field = 'uuid', string $target = 'user_info', string $fields = 'username,nickname,headimg,status,is_deleted'): array
|
||||
{
|
||||
$query = static::mk()->where($map)->order('sort desc,id desc');
|
||||
if (count($data) > 0) {
|
||||
$users = $query->whereIn('id', array_unique(array_column($data, $field)))->column($fields, 'id');
|
||||
foreach ($data as &$vo) $vo[$target] = $users[$vo[$field]] ?? [];
|
||||
return $users;
|
||||
} else {
|
||||
return $query->column($fields, 'id');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联身份权限
|
||||
* @return HasOne
|
||||
*/
|
||||
public function userinfo(): HasOne
|
||||
{
|
||||
return $this->hasOne(SystemBase::class, 'code', 'usertype')->where([
|
||||
'type' => '身份权限', 'status' => 1, 'deleted' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认头像处理
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getHeadimgAttr($value): string
|
||||
{
|
||||
if (empty($value)) try {
|
||||
$host = sysconf('base.site_host|raw') ?: 'https://v6.thinkadmin.top';
|
||||
return "{$host}/static/theme/img/headimg.png";
|
||||
} catch (\Exception $exception) {
|
||||
return "https://v6.thinkadmin.top/static/theme/img/headimg.png";
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化登录时间
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginAtAttr(string $value): string
|
||||
{
|
||||
return format_datetime($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化创建时间
|
||||
* @param mixed $value
|
||||
* @return string
|
||||
*/
|
||||
public function getCreateAtAttr($value): string
|
||||
{
|
||||
return format_datetime($value);
|
||||
}
|
||||
}
|
||||
334
plugin/think-library/src/service/AdminService.php
Normal file
334
plugin/think-library/src/service/AdminService.php
Normal file
@ -0,0 +1,334 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use think\admin\Exception;
|
||||
use think\admin\extend\CodeExtend;
|
||||
use think\admin\extend\DataExtend;
|
||||
use think\admin\Library;
|
||||
use think\admin\model\SystemAuth;
|
||||
use think\admin\model\SystemNode;
|
||||
use think\admin\model\SystemUser;
|
||||
use think\admin\Service;
|
||||
use think\helper\Str;
|
||||
use think\Session;
|
||||
|
||||
/**
|
||||
* 系统权限管理服务
|
||||
* @class AdminService
|
||||
* @package think\admin\service
|
||||
*/
|
||||
class AdminService extends Service
|
||||
{
|
||||
/**
|
||||
* 自定义回调处理
|
||||
* @var array
|
||||
*/
|
||||
private static $checkCallables = [];
|
||||
|
||||
/**
|
||||
* 是否已经登录
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isLogin(): bool
|
||||
{
|
||||
return static::getUserId() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为超级用户
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isSuper(): bool
|
||||
{
|
||||
return static::getUserName() === static::getSuperName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取超级用户账号
|
||||
* @return string
|
||||
*/
|
||||
public static function getSuperName(): string
|
||||
{
|
||||
return Library::$sapp->config->get('app.super_user', 'admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后台用户ID
|
||||
* @return integer
|
||||
*/
|
||||
public static function getUserId(): int
|
||||
{
|
||||
return intval(Library::$sapp->session->get('user.id', 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取后台用户名称
|
||||
* @return string
|
||||
*/
|
||||
public static function getUserName(): string
|
||||
{
|
||||
return Library::$sapp->session->get('user.username', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户扩展数据
|
||||
* @param null|string $field
|
||||
* @param null|mixed $default
|
||||
* @return array|mixed
|
||||
*/
|
||||
public static function getUserData(?string $field = null, $default = null)
|
||||
{
|
||||
$data = SystemService::getData('UserData_' . static::getUserId());
|
||||
return is_null($field) ? $data : ($data[$field] ?? $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户扩展数据
|
||||
* @param array $data
|
||||
* @param boolean $replace
|
||||
* @return boolean
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function setUserData(array $data, bool $replace = false): bool
|
||||
{
|
||||
$data = $replace ? $data : array_merge(static::getUserData(), $data);
|
||||
return SystemService::setData('UserData_' . static::getUserId(), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户主题名称
|
||||
* @return string
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function getUserTheme(): string
|
||||
{
|
||||
$default = sysconf('base.site_theme|raw') ?: 'default';
|
||||
return static::getUserData('site_theme', $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户主题名称
|
||||
* @param string $theme 主题名称
|
||||
* @return boolean
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function setUserTheme(string $theme): bool
|
||||
{
|
||||
return static::setUserData(['site_theme' => $theme]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册权限检查函数
|
||||
* @param callable $callable
|
||||
* @return integer
|
||||
*/
|
||||
public static function registerCheckCallable(callable $callable): int
|
||||
{
|
||||
self::$checkCallables[] = $callable;
|
||||
return count(self::$checkCallables) - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除权限检查函数
|
||||
* @param ?integer $index
|
||||
* @return boolean
|
||||
*/
|
||||
public static function removeCheckCallable(?int $index): bool
|
||||
{
|
||||
if (is_null($index)) {
|
||||
self::$checkCallables = [];
|
||||
return true;
|
||||
} elseif (isset(self::$checkCallables[$index])) {
|
||||
unset(self::$checkCallables[$index]);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查指定节点授权
|
||||
* --- 需要读取缓存或扫描所有节点
|
||||
* @param null|string $node
|
||||
* @return boolean
|
||||
*/
|
||||
public static function check(?string $node = ''): bool
|
||||
{
|
||||
$skey1 = 'think.admin.methods';
|
||||
$current = NodeService::fullNode($node);
|
||||
$methods = sysvar($skey1) ?: sysvar($skey1, NodeService::getMethods());
|
||||
$userNodes = Library::$sapp->session->get('user.nodes', []);
|
||||
// 自定义权限检查回调
|
||||
if (count(self::$checkCallables) > 0) {
|
||||
foreach (self::$checkCallables as $callable) {
|
||||
if ($callable($current, $methods, $userNodes) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// 自定义权限检查方法
|
||||
if (function_exists('admin_check_filter')) {
|
||||
return call_user_func('admin_check_filter', $current, $methods, $userNodes);
|
||||
}
|
||||
// 超级用户不需要检查权限
|
||||
if (static::isSuper()) return true;
|
||||
// 节点权限检查,需要兼容 windows 控制器不区分大小写,统一去除节点下划线再检查权限
|
||||
if (empty($simples = sysvar($skey2 = 'think.admin.fulls') ?: [])) {
|
||||
foreach ($methods as $k => $v) $simples[strtr($k, ['_' => ''])] = $v;
|
||||
sysvar($skey2, $simples);
|
||||
}
|
||||
if (empty($simples[$simple = strtr($current, ['_' => ''])]['isauth'])) {
|
||||
return !(!empty($simples[$simple]['islogin']) && !static::isLogin());
|
||||
} else {
|
||||
return in_array($current, $userNodes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取授权节点列表
|
||||
* @param array $checkeds
|
||||
* @return array
|
||||
*/
|
||||
public static function getTree(array $checkeds = []): array
|
||||
{
|
||||
[$nodes, $pnodes, $methods] = [[], [], array_reverse(NodeService::getMethods())];
|
||||
foreach ($methods as $node => $method) {
|
||||
[$count, $pnode] = [substr_count($node, '/'), substr($node, 0, strripos($node, '/'))];
|
||||
if ($count === 2 && !empty($method['isauth'])) {
|
||||
in_array($pnode, $pnodes) or array_push($pnodes, $pnode);
|
||||
$nodes[$node] = ['node' => $node, 'title' => lang($method['title']), 'pnode' => $pnode, 'checked' => in_array($node, $checkeds)];
|
||||
} elseif ($count === 1 && in_array($pnode, $pnodes)) {
|
||||
$nodes[$node] = ['node' => $node, 'title' => lang($method['title']), 'pnode' => $pnode, 'checked' => in_array($node, $checkeds)];
|
||||
}
|
||||
}
|
||||
foreach (array_keys($nodes) as $key) foreach ($methods as $node => $method) if (stripos($key, $node . '/') !== false) {
|
||||
$pnode = substr($node, 0, strripos($node, '/'));
|
||||
$nodes[$node] = ['node' => $node, 'title' => lang($method['title']), 'pnode' => $pnode, 'checked' => in_array($node, $checkeds)];
|
||||
$nodes[$pnode] = ['node' => $pnode, 'title' => Str::studly($pnode), 'pnode' => '', 'checked' => in_array($pnode, $checkeds)];
|
||||
}
|
||||
return DataExtend::arr2tree(array_reverse($nodes), 'node', 'pnode', '_sub_');
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化用户权限
|
||||
* @param boolean $force 强刷权限
|
||||
* @return array
|
||||
*/
|
||||
public static function apply(bool $force = false): array
|
||||
{
|
||||
if ($force) static::clear();
|
||||
if (($uuid = static::getUserId()) <= 0) return [];
|
||||
$user = SystemUser::mk()->where(['id' => $uuid])->findOrEmpty()->toArray();
|
||||
if (!static::isSuper() && count($aids = str2arr($user['authorize'])) > 0) {
|
||||
$aids = SystemAuth::mk()->where(['status' => 1])->whereIn('id', $aids)->column('id');
|
||||
if (!empty($aids)) $nodes = SystemNode::mk()->distinct()->whereIn('auth', $aids)->column('node');
|
||||
}
|
||||
$user['nodes'] = $nodes ?? [];
|
||||
Library::$sapp->session->set('user', $user);
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理节点缓存
|
||||
* @return bool
|
||||
*/
|
||||
public static function clear(): bool
|
||||
{
|
||||
Library::$sapp->cache->delete('SystemAuthNode');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员上传配置
|
||||
* @param ?string $uptoken
|
||||
* @return array [unid,exts]
|
||||
*/
|
||||
public static function withUploadUnid(?string $uptoken = null): array
|
||||
{
|
||||
try {
|
||||
if ($uptoken === '') return [0, []];
|
||||
$session = Library::$sapp->session;
|
||||
if (is_null($uptoken)) {
|
||||
$sesskey = $session->get('UploadSessionKey');
|
||||
if (empty($sesskey)) return [0, []];
|
||||
if ($session->getId() !== $sesskey) {
|
||||
$session = Library::$sapp->invokeClass(Session::class);
|
||||
$session->setId($sesskey);
|
||||
$session->init();
|
||||
}
|
||||
$unid = intval($session->get('AdminUploadUnid', 0));
|
||||
} else {
|
||||
$sesskey = CodeExtend::decrypt($uptoken, sysconf('data.jwtkey'));
|
||||
if (empty($sesskey)) return [0, []];
|
||||
if ($session->getId() !== $sesskey) {
|
||||
$session = Library::$sapp->invokeClass(Session::class);
|
||||
$session->setId($sesskey);
|
||||
$session->init();
|
||||
}
|
||||
if ($unid = intval($session->get('AdminUploadUnid', 0))) {
|
||||
$session->set('UploadSessionKey', $session->getId());
|
||||
}
|
||||
}
|
||||
return [$unid, $session->get('AdminUploadExts', [])];
|
||||
} catch (\Error|\Exception $exception) {
|
||||
return [0, []];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成上传入口令牌
|
||||
* @param integer $unid 会员编号
|
||||
* @param string $exts 允许后缀(多个以英文逗号隔开)
|
||||
* @return string
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function withUploadToken(int $unid, string $exts = ''): string
|
||||
{
|
||||
Library::$sapp->session->set('AdminUploadUnid', $unid);
|
||||
Library::$sapp->session->set('AdminUploadExts', str2arr(strtolower($exts)));
|
||||
return CodeExtend::encrypt(Library::$sapp->session->getId(), sysconf('data.jwtkey'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法兼容(临时)
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return bool
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function __callStatic(string $method, array $arguments)
|
||||
{
|
||||
if (strtolower($method) === 'clearcache') return static::clear();
|
||||
throw new Exception("method not exists: AdminService::{$method}()");
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象方法兼容(临时)
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return bool
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function __call(string $method, array $arguments)
|
||||
{
|
||||
return static::__callStatic($method, $arguments);
|
||||
}
|
||||
}
|
||||
185
plugin/think-library/src/service/CaptchaService.php
Normal file
185
plugin/think-library/src/service/CaptchaService.php
Normal file
@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use think\admin\Library;
|
||||
use think\admin\Service;
|
||||
|
||||
/**
|
||||
* 图形验证码服务
|
||||
* @class CaptchaService
|
||||
* @package think\admin\service
|
||||
*/
|
||||
class CaptchaService extends Service
|
||||
{
|
||||
private $code; // 验证码
|
||||
private $uniqid; // 唯一序号
|
||||
private $charset = 'ABCDEFGHKMNPRSTUVWXYZ23456789'; // 随机因子
|
||||
private $width = 130; // 图片宽度
|
||||
private $height = 50; // 图片高度
|
||||
private $length = 4; // 验证码长度
|
||||
private $fontsize = 20; // 指定字体大小
|
||||
|
||||
/**
|
||||
* 验证码服务初始化
|
||||
* @param array $config
|
||||
* @return static
|
||||
*/
|
||||
public function initialize(array $config = []): CaptchaService
|
||||
{
|
||||
// 动态配置属性
|
||||
foreach ($config as $k => $v) if (isset($this->$k)) $this->$k = $v;
|
||||
// 生成验证码序号
|
||||
$this->uniqid = uniqid('captcha') . mt_rand(1000, 9999);
|
||||
// 生成验证码字符串
|
||||
[$this->code, $length] = ['', strlen($this->charset) - 1];
|
||||
for ($i = 0; $i < $this->length; $i++) {
|
||||
$this->code .= $this->charset[mt_rand(0, $length)];
|
||||
}
|
||||
// 缓存验证码字符串
|
||||
$this->app->cache->set($this->uniqid, $this->code, 360);
|
||||
// 返回当前对象
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态切换配置
|
||||
* @param array $config
|
||||
* @return $this
|
||||
*/
|
||||
public function config(array $config = []): CaptchaService
|
||||
{
|
||||
return $this->initialize($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码值
|
||||
* @return string
|
||||
*/
|
||||
public function getCode(): string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片的内容
|
||||
* @return string
|
||||
*/
|
||||
public function getData(): string
|
||||
{
|
||||
return "data:image/png;base64,{$this->_image()}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码编号
|
||||
* @return string
|
||||
*/
|
||||
public function getUniqid(): string
|
||||
{
|
||||
return $this->uniqid;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证码数据
|
||||
* @return array
|
||||
*/
|
||||
public function getAttrs(): array
|
||||
{
|
||||
return [
|
||||
'code' => $this->getCode(),
|
||||
'data' => $this->getData(),
|
||||
'uniqid' => $this->getUniqid(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出图形验证码
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建验证码图片
|
||||
* @return string
|
||||
*/
|
||||
private function _image(): string
|
||||
{
|
||||
// 生成背景
|
||||
$img = imagecreatetruecolor($this->width, $this->height);
|
||||
$color = imagecolorallocate($img, mt_rand(220, 255), mt_rand(220, 255), mt_rand(220, 255));
|
||||
imagefilledrectangle($img, 0, $this->height, $this->width, 0, $color);
|
||||
// 生成线条
|
||||
for ($i = 0; $i < 6; $i++) {
|
||||
$color = imagecolorallocate($img, mt_rand(0, 50), mt_rand(0, 50), mt_rand(0, 50));
|
||||
imageline($img, mt_rand(0, $this->width), mt_rand(0, $this->height), mt_rand(0, $this->width), mt_rand(0, $this->height), $color);
|
||||
}
|
||||
// 生成雪花
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$color = imagecolorallocate($img, mt_rand(200, 255), mt_rand(200, 255), mt_rand(200, 255));
|
||||
imagestring($img, mt_rand(1, 5), mt_rand(0, $this->width), mt_rand(0, $this->height), '*', $color);
|
||||
}
|
||||
// 生成文字
|
||||
$_x = $this->width / $this->length;
|
||||
$fontfile = self::font();
|
||||
for ($i = 0; $i < $this->length; $i++) {
|
||||
$fontcolor = imagecolorallocate($img, mt_rand(0, 156), mt_rand(0, 156), mt_rand(0, 156));
|
||||
if (function_exists('imagettftext')) {
|
||||
imagettftext($img, $this->fontsize, mt_rand(-30, 30), intval($_x * $i + mt_rand(1, 5)), intval($this->height / 1.4), $fontcolor, $fontfile, $this->code[$i]);
|
||||
} else {
|
||||
imagestring($img, 15, intval($_x * $i + mt_rand(10, 15)), mt_rand(10, 30), $this->code[$i], $fontcolor);
|
||||
}
|
||||
}
|
||||
ob_start();
|
||||
imagepng($img);
|
||||
$data = ob_get_contents();
|
||||
ob_end_clean();
|
||||
imagedestroy($img);
|
||||
return base64_encode($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字体文件
|
||||
* @return string
|
||||
*/
|
||||
public static function font(): string
|
||||
{
|
||||
return __DIR__ . '/bin/captcha.ttf';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查验证码是否正确
|
||||
* @param string $code 需要验证的值
|
||||
* @param null|string $uniqid 验证码编号
|
||||
* @return boolean
|
||||
*/
|
||||
public static function check(string $code, ?string $uniqid = null): bool
|
||||
{
|
||||
$_uni = is_string($uniqid) ? $uniqid : input('uniqid', '-');
|
||||
$_val = Library::$sapp->cache->get($_uni, '');
|
||||
if (is_string($_val) && strtolower($_val) === strtolower($code)) {
|
||||
Library::$sapp->cache->delete($_uni);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
162
plugin/think-library/src/service/ExpressService.php
Normal file
162
plugin/think-library/src/service/ExpressService.php
Normal file
@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use think\admin\extend\CodeExtend;
|
||||
use think\admin\extend\HttpExtend;
|
||||
use think\admin\Service;
|
||||
|
||||
/**
|
||||
* 百度快递100物流查询
|
||||
* @class ExpressService
|
||||
* @deprecated 独立封装为插件
|
||||
* @package think\admin\service
|
||||
*/
|
||||
class ExpressService extends Service
|
||||
{
|
||||
|
||||
/**
|
||||
* 网络请求参数
|
||||
* @var array
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* 公司编码别名
|
||||
* @var array
|
||||
*/
|
||||
protected $codes = [
|
||||
'YD' => 'yunda',
|
||||
'SF' => 'shunfeng',
|
||||
'UC' => 'youshuwuliu',
|
||||
'YTO' => 'yuantong',
|
||||
'STO' => 'shentong',
|
||||
'ZTO' => 'zhongtong',
|
||||
'ZJS' => 'zhaijisong',
|
||||
'DBL' => 'debangwuliu',
|
||||
'HHTT' => 'tiantian',
|
||||
'HTKY' => 'huitongkuaidi',
|
||||
'YZPY' => 'youzhengguonei',
|
||||
];
|
||||
|
||||
/**
|
||||
* 快递服务初始化
|
||||
* @return $this
|
||||
*/
|
||||
protected function initialize(): ExpressService
|
||||
{
|
||||
// 获取当前请求 IP 地址
|
||||
$clentip = $this->app->request->ip();
|
||||
if (empty($clentip) || $clentip === '0.0.0.0') {
|
||||
$clentip = join('.', [rand(1, 254), rand(1, 254), rand(1, 254), rand(1, 254)]);
|
||||
}
|
||||
// 创建 CURL 请求模拟参数
|
||||
$this->options['cookie_file'] = syspath('runtime/.cok');
|
||||
$this->options['headers'] = ['Host:express.baidu.com', "CLIENT-IP:{$clentip}", "X-FORWARDED-FOR:{$clentip}"];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过百度快递100应用查询物流信息
|
||||
* @param string $code 快递公司编辑
|
||||
* @param string $number 快递物流编号
|
||||
* @param array $list 快递路径列表
|
||||
* @return array
|
||||
*/
|
||||
public function express(string $code, string $number, array $list = []): array
|
||||
{
|
||||
// 新状态:1-新订单,2-在途中,3-签收,4-问题件
|
||||
// 原状态:0-在途,1-揽收,2-疑难,3-签收,4-退签,5-派件,6-退回,7-转投,8-清关,14-拒签
|
||||
$ckey = md5("{$code}{$number}");
|
||||
$cache = $this->app->cache->get($ckey, []);
|
||||
$message = [1 => '新订单', 2 => '在途中', 3 => '签收', 4 => '问题件'];
|
||||
if (!empty($cache)) return $cache;
|
||||
for ($i = 0; $i < 6; $i++) if (is_array($result = $this->doExpress($code, $number))) {
|
||||
if (isset($result['data']['info']['context']) && isset($result['data']['info']['state'])) {
|
||||
$state = intval($result['data']['info']['state']);
|
||||
$status = in_array($state, [0, 1, 5, 7, 8]) ? 2 : ($state === 3 ? 3 : 4);
|
||||
foreach ($result['data']['info']['context'] as $vo) $list[] = ['time' => date('Y-m-d H:i:s', intval($vo['time'])), 'context' => $vo['desc']];
|
||||
$result = ['message' => lang($message[$status] ?? $result['msg']), 'status' => $status, 'express' => $code, 'number' => $number, 'data' => $list];
|
||||
$this->app->cache->set($ckey, $result, 30);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
return ['message' => lang('暂无轨迹信息~'), 'status' => 1, 'express' => $code, 'number' => $number, 'data' => $list];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取快递公司列表
|
||||
* @return array
|
||||
*/
|
||||
public function getExpressList(): array
|
||||
{
|
||||
return $this->getQueryData(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行百度快递100应用查询请求
|
||||
* @param string $code 快递公司编号
|
||||
* @param string $number 快递单单号
|
||||
* @return mixed
|
||||
*/
|
||||
private function doExpress(string $code, string $number)
|
||||
{
|
||||
[$code, $qqid] = [$this->codes[$code] ?? $code, CodeExtend::uniqidNumber(19, '7740')];
|
||||
$url = "{$this->getQueryData(1)}&appid=4001&nu={$number}&com={$code}&qid={$qqid}&new_need_di=1&source_xcx=0&vcode=&token=&sourceId=4155";
|
||||
$result = json_decode(trim(HttpExtend::get($url, [], $this->options)), true);
|
||||
if (!empty($result['status']) || !empty($result['error_code'])) {
|
||||
@unlink($this->options['cookie_file']);
|
||||
$this->app->cache->delete('express_kuaidi_uri');
|
||||
$this->app->cache->delete('express_kuaidi_com');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取快递查询接口
|
||||
* @param integer $type 类型数据
|
||||
* @return string|array
|
||||
*/
|
||||
private function getQueryData(int $type)
|
||||
{
|
||||
$times = 0;
|
||||
$expressUri = $this->app->cache->get('express_kuaidi_uri', '');
|
||||
if ($type == 1 && !empty($expressUri)) return $expressUri;
|
||||
$expressCom = $this->app->cache->get('express_kuaidi_com', []);
|
||||
if ($type === 2 && !empty($expressCom)) return $expressCom;
|
||||
while (true) {
|
||||
if ($times++ >= 10) {
|
||||
$times = 0;
|
||||
@unlink($this->options['cookie_file']);
|
||||
}
|
||||
[$ts, $input] = [mt_rand(2000000, 2900000), CodeExtend::random(5)];
|
||||
$content = HttpExtend::get("https://m.baidu.com/s?word=快递查询&ts={$ts}&t_kt=0&ie=utf-8&rsv_iqid=&rsv_t=&sa=&rsv_pq=&rsv_sug4=&tj=1&inputT={$input}&sugid=&ss=", [], $this->options);
|
||||
if (preg_match('#"(expSearchApi|checkExpUrl)":"(.*?)"#i', $content, $matches)) {
|
||||
$this->app->cache->set('express_kuaidi_uri', $expressUri = $matches[2], 3600);
|
||||
if (preg_match('#"text":"快递查询","option":.*?(\[.*?]).*?#i', $content, $items)) {
|
||||
$attr = json_decode($items[1], true);
|
||||
$expressCom = array_combine(array_column($attr, 'value'), array_column($attr, 'text'));
|
||||
$this->app->cache->set('express_kuaidi_com', $expressCom, 3600);
|
||||
if ($type === 2) return $expressCom;
|
||||
}
|
||||
if ($type === 1) return $expressUri;
|
||||
} else usleep(100000);
|
||||
}
|
||||
}
|
||||
}
|
||||
275
plugin/think-library/src/service/InterfaceService.php
Normal file
275
plugin/think-library/src/service/InterfaceService.php
Normal file
@ -0,0 +1,275 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use stdClass;
|
||||
use think\admin\Exception;
|
||||
use think\admin\extend\HttpExtend;
|
||||
use think\admin\helper\ValidateHelper;
|
||||
use think\admin\Service;
|
||||
use think\App;
|
||||
use think\exception\HttpResponseException;
|
||||
|
||||
/**
|
||||
* 通用接口基础服务
|
||||
* @class InterfaceService
|
||||
* @package think\admin\service
|
||||
*/
|
||||
class InterfaceService extends Service
|
||||
{
|
||||
/**
|
||||
* 输出格式
|
||||
* @var string
|
||||
*/
|
||||
private $type = 'json';
|
||||
|
||||
/**
|
||||
* 接口认证账号
|
||||
* @var string
|
||||
*/
|
||||
private $appid;
|
||||
|
||||
/**
|
||||
* 接口认证密钥
|
||||
* @var string
|
||||
*/
|
||||
private $appkey;
|
||||
|
||||
/**
|
||||
* 接口请求地址
|
||||
* @var string
|
||||
*/
|
||||
private $getway;
|
||||
|
||||
/**
|
||||
* 接口服务初始化
|
||||
* InterfaceService constructor.
|
||||
* @param App $app
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
parent::__construct($app);
|
||||
$this->appid = sysconf('data.interface_appid|raw') ?: '';
|
||||
$this->appkey = sysconf('data.interface_appkey|raw') ?: '';
|
||||
$this->getway = sysconf('data.interface_getway|raw') ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置接口网关
|
||||
* @param string $getway 接口网关
|
||||
* @return $this
|
||||
*/
|
||||
public function getway(string $getway): InterfaceService
|
||||
{
|
||||
$this->getway = $getway;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置授权账号
|
||||
* @param string $appid 接口账号
|
||||
* @param string $appkey 接口密钥
|
||||
* @return $this
|
||||
*/
|
||||
public function setAuth(string $appid, string $appkey): InterfaceService
|
||||
{
|
||||
$this->appid = $appid;
|
||||
$this->appkey = $appkey;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输出类型为 JSON
|
||||
* @return $this
|
||||
*/
|
||||
public function setOutTypeJson(): InterfaceService
|
||||
{
|
||||
$this->type = 'json';
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输出类型为 Array
|
||||
* @return $this
|
||||
*/
|
||||
public function setOutTypeArray(): InterfaceService
|
||||
{
|
||||
$this->type = 'array';
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前APPID
|
||||
* @return string
|
||||
*/
|
||||
public function getAppid(): string
|
||||
{
|
||||
return $this->appid ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求参数
|
||||
* @return array
|
||||
*/
|
||||
public function getData(): array
|
||||
{
|
||||
// 基础参数获取
|
||||
$input = ValidateHelper::instance()->init([
|
||||
'time.require' => lang('请求参数 %s 不能为空!', ['time']),
|
||||
'sign.require' => lang('请求参数 %s 不能为空!', ['sign']),
|
||||
'data.require' => lang('请求参数 %s 不能为空!', ['data']),
|
||||
'appid.require' => lang('请求参数 %s 不能为空!', ['appid']),
|
||||
'nostr.require' => lang('请求参数 %s 不能为空!', ['nostr']),
|
||||
], 'post', [$this, 'baseError']);
|
||||
|
||||
// 检查请求签名,使用通用签名方式
|
||||
$build = $this->signString($input['data'], $input['time'], $input['nostr']);
|
||||
if ($build['sign'] !== $input['sign']) {
|
||||
$this->baseError(lang('接口签名验证失败!'));
|
||||
}
|
||||
|
||||
// 检查请求时间,与服务差不能超过 30 秒
|
||||
if (abs(intval($input['time']) - time()) > 30) {
|
||||
$this->baseError(lang('接口请求时差过大!'));
|
||||
}
|
||||
|
||||
// 返回并解析数据内容,如果解析失败返回空数组
|
||||
return json_decode($input['data'], true) ?: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复业务处理失败的消息
|
||||
* @param mixed $info 消息内容
|
||||
* @param mixed $data 返回数据
|
||||
* @param mixed $code 返回状态码
|
||||
*/
|
||||
public function error($info, $data = '{-null-}', $code = 0): void
|
||||
{
|
||||
if ($data === '{-null-}') $data = new stdClass();
|
||||
$this->baseResponse(lang('请求响应异常!'), [
|
||||
'code' => $code, 'info' => $info, 'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复业务处理成功的消息
|
||||
* @param mixed $info 消息内容
|
||||
* @param mixed $data 返回数据
|
||||
* @param mixed $code 返回状态码
|
||||
*/
|
||||
public function success($info, $data = '{-null-}', $code = 1): void
|
||||
{
|
||||
if ($data === '{-null-}') $data = new stdClass();
|
||||
$this->baseResponse(lang('请求响应成功!'), [
|
||||
'code' => $code, 'info' => is_string($info) ? lang($info) : $info, 'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复根失败消息
|
||||
* @param mixed $info 消息内容
|
||||
* @param mixed $data 返回数据
|
||||
* @param mixed $code 根状态码
|
||||
*/
|
||||
public function baseError($info, $data = [], $code = 0): void
|
||||
{
|
||||
$this->baseResponse($info, $data, $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复根成功消息
|
||||
* @param mixed $info 消息内容
|
||||
* @param mixed $data 返回数据
|
||||
* @param mixed $code 根状态码
|
||||
*/
|
||||
public function baseSuccess($info, $data = [], $code = 1): void
|
||||
{
|
||||
$this->baseResponse($info, $data, $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回复根签名消息
|
||||
* @param mixed $info 消息内容
|
||||
* @param mixed $data 返回数据
|
||||
* @param mixed $code 根状态码
|
||||
*/
|
||||
public function baseResponse($info, $data = [], $code = 1): void
|
||||
{
|
||||
$array = $this->signData($data);
|
||||
throw new HttpResponseException(json([
|
||||
'code' => $code, 'info' => $info, 'time' => $array['time'],
|
||||
'sign' => $array['sign'], 'appid' => $array['appid'], 'nostr' => $array['nostr'],
|
||||
'data' => $this->type !== 'json' ? json_decode($array['data'], true) : $array['data'],
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口数据模拟请求
|
||||
* @param string $uri 接口地址
|
||||
* @param array $data 请求数据
|
||||
* @param boolean $check 验证结果
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function doRequest(string $uri, array $data = [], bool $check = true): array
|
||||
{
|
||||
$url = rtrim($this->getway, '/') . '/' . ltrim($uri, '/');
|
||||
$content = HttpExtend::post($url, $this->signData($data)) ?: '';
|
||||
// 解析返回的结果
|
||||
if (!($result = json_decode($content, true)) || empty($result)) {
|
||||
throw new Exception(lang('接口请求响应格式异常!'));
|
||||
}
|
||||
// 返回业务异常结果
|
||||
if (empty($result['code'])) throw new Exception($result['info']);
|
||||
$array = is_array($result['data']) ? $result['data'] : json_decode($result['data'], true);
|
||||
// 无需验证直接返回
|
||||
if (empty($check)) return $array;
|
||||
// 返回结果签名验证
|
||||
$json = is_string($result['data']) ? $result['data'] : json_encode($result['data'], JSON_UNESCAPED_UNICODE);
|
||||
$build = $this->signString($json, $result['time'], $result['nostr']);
|
||||
if ($build['sign'] === $result['sign']) return $array ?: [];
|
||||
throw new Exception(lang('返回结果签名验证失败!'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口响应数据签名
|
||||
* @param array $data ['appid','nostr','time','sign','data']
|
||||
* @return array
|
||||
*/
|
||||
private function signData(array $data): array
|
||||
{
|
||||
return $this->signString(json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据字符串数据签名
|
||||
* @param string $json 待签名的数据
|
||||
* @param mixed $time 签名的时间戳
|
||||
* @param mixed $rand 签名随机字符
|
||||
* @return array
|
||||
*/
|
||||
private function signString(string $json, $time = null, $rand = null): array
|
||||
{
|
||||
$time = strval($time ?: time());
|
||||
$rand = strval($rand ?: md5(uniqid('', true)));
|
||||
$sign = md5("{$this->appid}#{$json}#{$time}#{$this->appkey}#{$rand}");
|
||||
return ['appid' => $this->appid, 'nostr' => $rand, 'time' => $time, 'sign' => $sign, 'data' => $json];
|
||||
}
|
||||
}
|
||||
103
plugin/think-library/src/service/MenuService.php
Normal file
103
plugin/think-library/src/service/MenuService.php
Normal file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use think\admin\extend\DataExtend;
|
||||
use think\admin\model\SystemMenu;
|
||||
use think\admin\Service;
|
||||
|
||||
/**
|
||||
* 系统菜单管理服务
|
||||
* @class MenuService
|
||||
* @package app\admin\service
|
||||
*/
|
||||
class MenuService extends Service
|
||||
{
|
||||
|
||||
/**
|
||||
* 菜单分组语言包
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
private static function lang(string $name): string
|
||||
{
|
||||
$lang = lang("menus_{$name}");
|
||||
if (stripos($lang, 'menus_') === 0) {
|
||||
return lang(substr($lang, 6));
|
||||
} else {
|
||||
return $lang;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可选菜单节点
|
||||
* @param boolean $force 强制刷新
|
||||
* @return array
|
||||
*/
|
||||
public static function getList(bool $force = false): array
|
||||
{
|
||||
$nodes = sysvar($keys = 'think.admin.menus') ?: [];
|
||||
if (empty($force) && count($nodes) > 0) return $nodes; else $nodes = [];
|
||||
foreach (NodeService::getMethods($force) as $node => $method) {
|
||||
if ($method['ismenu']) $nodes[] = ['node' => $node, 'title' => self::lang($method['title'])];
|
||||
}
|
||||
return sysvar($keys, $nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统菜单树数据
|
||||
* @return array
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public static function getTree(): array
|
||||
{
|
||||
$menus = SystemMenu::mk()->where(['status' => 1])->order('sort desc,id asc')->select()->toArray();
|
||||
if (function_exists('admin_menu_filter')) $menus = call_user_func('admin_menu_filter', $menus);
|
||||
foreach ($menus as &$menu) $menu['title'] = self::lang($menu['title']);
|
||||
return static::filter(DataExtend::arr2tree($menus));
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台主菜单权限过滤
|
||||
* @param array $menus 当前菜单列表
|
||||
* @return array
|
||||
*/
|
||||
private static function filter(array $menus): array
|
||||
{
|
||||
foreach ($menus as $key => &$menu) {
|
||||
if (!empty($menu['sub'])) {
|
||||
$menu['sub'] = static::filter($menu['sub']);
|
||||
}
|
||||
if (!empty($menu['sub'])) {
|
||||
$menu['url'] = '#';
|
||||
} elseif (empty($menu['url']) || $menu['url'] === '#' || !(empty($menu['node']) || AdminService::check($menu['node']))) {
|
||||
unset($menus[$key]);
|
||||
} elseif (preg_match('#^(https?:)?//\w+#i', $menu['url'])) {
|
||||
if ($menu['params']) $menu['url'] .= (strpos($menu['url'], '?') === false ? '?' : '&') . $menu['params'];
|
||||
} else {
|
||||
$node = join('/', array_slice(str2arr($menu['url'], '/'), 0, 3));
|
||||
$menu['url'] = admuri($menu['url']) . ($menu['params'] ? '?' . $menu['params'] : '');
|
||||
if (!AdminService::check($node)) unset($menus[$key]);
|
||||
}
|
||||
}
|
||||
return $menus;
|
||||
}
|
||||
}
|
||||
505
plugin/think-library/src/service/MessageService.php
Normal file
505
plugin/think-library/src/service/MessageService.php
Normal file
@ -0,0 +1,505 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use think\admin\extend\HttpExtend;
|
||||
use think\admin\Service;
|
||||
|
||||
/**
|
||||
* 旧助通短信接口服务
|
||||
* @class MessageService
|
||||
* @package app\store\service
|
||||
* @deprecated 建议使用云平台服务
|
||||
* =================================
|
||||
*
|
||||
* CREATE TABLE `system_message_history` (
|
||||
* `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
* `phone` varchar(100) DEFAULT '' COMMENT '目标手机',
|
||||
* `region` varchar(100) DEFAULT '' COMMENT '国家编号',
|
||||
* `result` varchar(100) DEFAULT '' COMMENT '返回结果',
|
||||
* `content` varchar(512) DEFAULT '' COMMENT '短信内容',
|
||||
* `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
* PRIMARY KEY (`id`) USING BTREE,
|
||||
* KEY `idx_system_message_history_phone` (`phone`) USING BTREE
|
||||
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统-短信';
|
||||
*
|
||||
* =================================
|
||||
* 发送国内短信需要给产品码 [productid]
|
||||
* --- 验证短信的产品码为:676767
|
||||
* --- 营销短信的产品码为:333333
|
||||
* ---------------------------------
|
||||
* ---------------------------------
|
||||
* 发送国际短信需要给国家代码 [code]
|
||||
* --- 国家代码见 getGlobeRegionMap
|
||||
* ---------------------------------
|
||||
* ---------------------------------
|
||||
* 需要开通短信账号请联系客服
|
||||
* --- 客服电话:18122377655
|
||||
* =================================
|
||||
*/
|
||||
class MessageService extends Service
|
||||
{
|
||||
|
||||
private $table;
|
||||
|
||||
private $chinaUsername;
|
||||
private $chinaPassword;
|
||||
|
||||
private $globeUsername;
|
||||
private $globePassword;
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function initialize(): MessageService
|
||||
{
|
||||
$this->table = 'SystemMessageHistory';
|
||||
$this->chinaUsername = sysconf('sms_zt.china_username|raw');
|
||||
$this->chinaPassword = sysconf('sms_zt.china_password|raw');
|
||||
$this->globeUsername = sysconf('sms_zt.globe_username|raw');
|
||||
$this->globePassword = sysconf('sms_zt.globe_password|raw');
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置内陆短信认证
|
||||
* @param string $username 账号名称
|
||||
* @param string $password 账号密码
|
||||
* @return $this
|
||||
*/
|
||||
public function configChina(string $username, string $password): MessageService
|
||||
{
|
||||
$this->chinaUsername = $username;
|
||||
$this->chinaPassword = $password;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置国际短信认证
|
||||
* @param string $username 账号名称
|
||||
* @param string $password 账号密码
|
||||
* @return $this
|
||||
*/
|
||||
public function configGlobe(string $username, string $password): MessageService
|
||||
{
|
||||
$this->globeUsername = $username;
|
||||
$this->globePassword = $password;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置存储数据表
|
||||
* @param string $table
|
||||
* @return $this
|
||||
*/
|
||||
public function setSaveTable(string $table): MessageService
|
||||
{
|
||||
$this->table = $table;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成短信内容
|
||||
* @param string $content
|
||||
* @param array $params
|
||||
* @return string
|
||||
*/
|
||||
public function buildContent(string $content, array $params = []): string
|
||||
{
|
||||
foreach ($params as $key => $value) {
|
||||
$content = str_replace("{{$key}}", $value, $content);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送国内短信验证码
|
||||
* @param integer|string $phone 手机号
|
||||
* @param integer|string $content 短信内容
|
||||
* @param integer|string $productid 短信通道
|
||||
* @return boolean
|
||||
*/
|
||||
public function sendChinaSms($phone, $content, $productid = '676767'): bool
|
||||
{
|
||||
$tkey = date("YmdHis");
|
||||
$result = HttpExtend::get('http' . '://www.ztsms.cn/sendNSms.do', [
|
||||
'tkey' => $tkey,
|
||||
'mobile' => $phone,
|
||||
'content' => $content,
|
||||
'username' => $this->chinaUsername,
|
||||
'productid' => $productid,
|
||||
'password' => md5(md5($this->chinaPassword) . $tkey),
|
||||
]);
|
||||
[$code] = explode(',', $result . ',');
|
||||
$this->app->db->name($this->table)->insert([
|
||||
'phone' => $phone, 'region' => '860',
|
||||
'content' => $content, 'result' => $result,
|
||||
]);
|
||||
return intval($code) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送国内短信验证码
|
||||
* @param integer|string $phone 目标手机
|
||||
* @param integer $wait 等待时间
|
||||
* @param string $type 短信模板
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function sendChinaSmsByCode($phone, int $wait = 120, string $type = 'sms_reg_template'): array
|
||||
{
|
||||
$cache = $this->app->cache->get($ckey = "{$type}_{$phone}", []);
|
||||
if (is_array($cache) && isset($cache['time']) && $cache['time'] > time() - $wait) {
|
||||
$dtime = ($cache['time'] + $wait < time()) ? 0 : ($wait - time() + $cache['time']);
|
||||
return [1, lang('短信验证码已经发送!'), ['time' => $dtime]];
|
||||
}
|
||||
[$code, $content] = [rand(1000, 9999) . '', sysconf("{$type}|raw")];
|
||||
if (empty($content) || stripos($content, '{code}') === false) {
|
||||
$content = lang('您的验证码为{code},请在十分钟内完成操作!');
|
||||
}
|
||||
$this->app->cache->set($ckey, $cache = ['code' => $code, 'time' => time()], 600);
|
||||
if ($this->sendChinaSms($phone, str_replace('{code}', $code, $content))) {
|
||||
$dtime = ($cache['time'] + $wait < time()) ? 0 : ($wait - time() + $cache['time']);
|
||||
return [1, lang('短信验证码发送成功!'), ['time' => $dtime]];
|
||||
} else {
|
||||
return [0, lang('短信发送失败,请稍候再试!'), []];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证手机短信验证码
|
||||
* @param integer|string $phone 目标手机
|
||||
* @param integer|string $code 短信验证码
|
||||
* @param string $type 短信模板
|
||||
* @return boolean
|
||||
*/
|
||||
public function check($phone, $code, string $type = 'sms_reg_template'): bool
|
||||
{
|
||||
$cache = $this->app->cache->get("{$type}_{$phone}", []);
|
||||
return is_array($cache) && isset($cache['code']) && $cache['code'] == $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询国内短信余额
|
||||
* @return array
|
||||
*/
|
||||
public function queryChinaSmsBalance(): array
|
||||
{
|
||||
$tkey = date("YmdHis");
|
||||
$result = HttpExtend::get('http' . '://www.ztsms.cn/balanceN.do', [
|
||||
'username' => $this->chinaUsername, 'tkey' => $tkey,
|
||||
'password' => md5(md5($this->chinaPassword) . $tkey),
|
||||
]);
|
||||
if ($result > -1) {
|
||||
return ['code' => 1, 'num' => $result, 'msg' => lang('获取短信剩余条数成功!')];
|
||||
} elseif ($result > -2) {
|
||||
return ['code' => 0, 'num' => '0', 'msg' => lang('用户名或者密码不正确!')];
|
||||
} elseif ($result > -3) {
|
||||
return ['code' => 0, 'num' => '0', 'msg' => lang('tkey不正确!')];
|
||||
} elseif ($result > -4) {
|
||||
return ['code' => 0, 'num' => '0', 'msg' => lang('用户不存在或用户停用!')];
|
||||
} else {
|
||||
return ['code' => 0, 'num' => '0', 'msg' => lang('未知错误原因!')];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误消息处理
|
||||
* @var array
|
||||
*/
|
||||
private $globeMessageMap = [
|
||||
2 => '用户账号为空',
|
||||
3 => '用户账号错误',
|
||||
4 => '授权密码为空',
|
||||
5 => '授权密码错误',
|
||||
6 => '当前时间为空',
|
||||
7 => '当前时间错误',
|
||||
8 => '用户类型错误',
|
||||
9 => '用户鉴权错误',
|
||||
10 => '请求IP已被列入黑名单',
|
||||
];
|
||||
|
||||
/**
|
||||
* 发送国际短信内容
|
||||
* @param integer|string $code 国家代码
|
||||
* @param integer|string $mobile 手机号码
|
||||
* @param string $content 发送内容
|
||||
* @return boolean
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function sendGlobeSms($code, $mobile, string $content): bool
|
||||
{
|
||||
$tkey = date("YmdHis");
|
||||
$result = HttpExtend::get('http' . '://intl.zthysms.com/intSendSms.do', [
|
||||
'tkey' => $tkey, 'code' => $code, 'mobile' => $mobile,
|
||||
'content' => $content, 'username' => sysconf('sms_zt_username2|raw'),
|
||||
'password' => md5(md5(sysconf('sms_zt_password2|raw')) . $tkey),
|
||||
]);
|
||||
$this->app->db->name($this->table)->insert([
|
||||
'region' => $code, 'phone' => $mobile, 'content' => $content, 'result' => $result,
|
||||
]);
|
||||
return intval($result) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询国际短信余额
|
||||
* @return array
|
||||
*/
|
||||
public function queryGlobeSmsBalance(): array
|
||||
{
|
||||
$tkey = date("YmdHis");
|
||||
$result = HttpExtend::get('http' . '://intl.zthysms.com/intBalance.do', [
|
||||
'username' => $this->globeUsername, 'tkey' => $tkey, 'password' => md5(md5($this->globePassword) . $tkey),
|
||||
]);
|
||||
if (!is_numeric($result) && ($state = intval($result)) && isset($this->globeMessageMap[$state])) {
|
||||
return ['code' => 0, 'num' => 0, 'msg' => lang($this->globeMessageMap[$state])];
|
||||
} else {
|
||||
return ['code' => 1, 'num' => $result, 'msg' => lang('查询成功')];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取国际地域编号
|
||||
* @return array
|
||||
*/
|
||||
public function getGlobeRegionMap(): array
|
||||
{
|
||||
return [
|
||||
['title' => '中国 台湾', 'english' => 'Taiwan', 'code' => 886],
|
||||
['title' => '东帝汶民主共和国', 'english' => 'DEMOCRATIC REPUBLIC OF TIMORLESTE', 'code' => 670],
|
||||
['title' => '中非共和国', 'english' => 'Central African Republic', 'code' => 236],
|
||||
['title' => '丹麦', 'english' => 'Denmark', 'code' => 45],
|
||||
['title' => '乌克兰', 'english' => 'Ukraine', 'code' => 380],
|
||||
['title' => '乌兹别克斯坦', 'english' => 'Uzbekistan', 'code' => 998],
|
||||
['title' => '乌干达', 'english' => 'Uganda', 'code' => 256],
|
||||
['title' => '乌拉圭', 'english' => 'Uruguay', 'code' => 598],
|
||||
['title' => '乍得', 'english' => 'Chad', 'code' => 235],
|
||||
['title' => '也门', 'english' => 'Yemen', 'code' => 967],
|
||||
['title' => '亚美尼亚', 'english' => 'Armenia', 'code' => 374],
|
||||
['title' => '以色列', 'english' => 'Israel', 'code' => 972],
|
||||
['title' => '伊拉克', 'english' => 'Iraq', 'code' => 964],
|
||||
['title' => '伊朗', 'english' => 'Iran', 'code' => 98],
|
||||
['title' => '伯利兹', 'english' => 'Belize', 'code' => 501],
|
||||
['title' => '佛得角', 'english' => 'Cape Verde', 'code' => 238],
|
||||
['title' => '俄罗斯', 'english' => 'Russia', 'code' => 7],
|
||||
['title' => '保加利亚', 'english' => 'Bulgaria', 'code' => 359],
|
||||
['title' => '克罗地亚', 'english' => 'Croatia', 'code' => 385],
|
||||
['title' => '关岛', 'english' => 'Guam', 'code' => 1671],
|
||||
['title' => '冈比亚', 'english' => 'The Gambia', 'code' => 220],
|
||||
['title' => '冰岛', 'english' => 'Iceland', 'code' => 354],
|
||||
['title' => '几内亚', 'english' => 'Guinea', 'code' => 224],
|
||||
['title' => '几内亚比绍', 'english' => 'Guinea - Bissau', 'code' => 245],
|
||||
['title' => '列支敦士登', 'english' => 'Liechtenstein', 'code' => 423],
|
||||
['title' => '刚果共和国', 'english' => 'The Republic of Congo', 'code' => 242],
|
||||
['title' => '刚果民主共和国', 'english' => 'Democratic Republic of the Congo', 'code' => 243],
|
||||
['title' => '利比亚', 'english' => 'Libya', 'code' => 218],
|
||||
['title' => '利比里亚', 'english' => 'Liberia', 'code' => 231],
|
||||
['title' => '加拿大', 'english' => 'Canada', 'code' => 1],
|
||||
['title' => '加纳', 'english' => 'Ghana', 'code' => 233],
|
||||
['title' => '加蓬', 'english' => 'Gabon', 'code' => 241],
|
||||
['title' => '匈牙利', 'english' => 'Hungary', 'code' => 36],
|
||||
['title' => '南非', 'english' => 'South Africa', 'code' => 27],
|
||||
['title' => '博茨瓦纳', 'english' => 'Botswana', 'code' => 267],
|
||||
['title' => '卡塔尔', 'english' => 'Qatar', 'code' => 974],
|
||||
['title' => '卢旺达', 'english' => 'Rwanda', 'code' => 250],
|
||||
['title' => '卢森堡', 'english' => 'Luxembourg', 'code' => 352],
|
||||
['title' => '印尼', 'english' => 'Indonesia', 'code' => 62],
|
||||
['title' => '印度', 'english' => 'India', 'code' => 91918919],
|
||||
['title' => '危地马拉', 'english' => 'Guatemala', 'code' => 502],
|
||||
['title' => '厄瓜多尔', 'english' => 'Ecuador', 'code' => 593],
|
||||
['title' => '厄立特里亚', 'english' => 'Eritrea', 'code' => 291],
|
||||
['title' => '叙利亚', 'english' => 'Syria', 'code' => 963],
|
||||
['title' => '古巴', 'english' => 'Cuba', 'code' => 53],
|
||||
['title' => '吉尔吉斯斯坦', 'english' => 'Kyrgyzstan', 'code' => 996],
|
||||
['title' => '吉布提', 'english' => 'Djibouti', 'code' => 253],
|
||||
['title' => '哥伦比亚', 'english' => 'Colombia', 'code' => 57],
|
||||
['title' => '哥斯达黎加', 'english' => 'Costa Rica', 'code' => 506],
|
||||
['title' => '喀麦隆', 'english' => 'Cameroon', 'code' => 237],
|
||||
['title' => '图瓦卢', 'english' => 'Tuvalu', 'code' => 688],
|
||||
['title' => '土库曼斯坦', 'english' => 'Turkmenistan', 'code' => 993],
|
||||
['title' => '土耳其', 'english' => 'Turkey', 'code' => 90],
|
||||
['title' => '圣卢西亚', 'english' => 'Saint Lucia', 'code' => 1758],
|
||||
['title' => '圣基茨和尼维斯', 'english' => 'Saint Kitts and Nevis', 'code' => 1869],
|
||||
['title' => '圣多美和普林西比', 'english' => 'Sao Tome and Principe', 'code' => 239],
|
||||
['title' => '圣文森特和格林纳丁斯', 'english' => 'Saint Vincent and the Grenadines', 'code' => 1784],
|
||||
['title' => '圣皮埃尔和密克隆群岛', 'english' => 'Saint Pierre and Miquelon', 'code' => 508],
|
||||
['title' => '圣赫勒拿岛', 'english' => 'Saint Helena', 'code' => 290],
|
||||
['title' => '圣马力诺', 'english' => 'San Marino', 'code' => 378],
|
||||
['title' => '圭亚那', 'english' => 'Guyana', 'code' => 592],
|
||||
['title' => '坦桑尼亚', 'english' => 'Tanzania', 'code' => 255],
|
||||
['title' => '埃及', 'english' => 'Egypt', 'code' => 20],
|
||||
['title' => '埃塞俄比亚', 'english' => 'Ethiopia', 'code' => 251],
|
||||
['title' => '基里巴斯', 'english' => 'Kiribati', 'code' => 686],
|
||||
['title' => '塔吉克斯坦', 'english' => 'Tajikistan', 'code' => 992],
|
||||
['title' => '塞内加尔', 'english' => 'Senegal', 'code' => 221],
|
||||
['title' => '塞尔维亚', 'english' => 'Serbia and Montenegro', 'code' => 381],
|
||||
['title' => '塞拉利昂', 'english' => 'Sierra Leone', 'code' => 232],
|
||||
['title' => '塞浦路斯', 'english' => 'Cyprus', 'code' => 357],
|
||||
['title' => '塞舌尔', 'english' => 'Seychelles', 'code' => 248],
|
||||
['title' => '墨西哥', 'english' => 'Mexico', 'code' => 52],
|
||||
['title' => '多哥', 'english' => 'Togo', 'code' => 228],
|
||||
['title' => '多米尼克', 'english' => 'Dominica', 'code' => 1767],
|
||||
['title' => '奥地利', 'english' => 'Austria', 'code' => 43],
|
||||
['title' => '委内瑞拉', 'english' => 'Venezuela', 'code' => 58],
|
||||
['title' => '孟加拉', 'english' => 'Bangladesh', 'code' => 880],
|
||||
['title' => '安哥拉', 'english' => 'Angola', 'code' => 244],
|
||||
['title' => '安圭拉岛', 'english' => 'Anguilla', 'code' => 1264],
|
||||
['title' => '安道尔', 'english' => 'Andorra', 'code' => 376],
|
||||
['title' => '密克罗尼西亚', 'english' => 'Federated States of Micronesia', 'code' => 691],
|
||||
['title' => '尼加拉瓜', 'english' => 'Nicaragua', 'code' => 505],
|
||||
['title' => '尼日利亚', 'english' => 'Nigeria', 'code' => 234],
|
||||
['title' => '尼日尔', 'english' => 'Niger', 'code' => 227],
|
||||
['title' => '尼泊尔', 'english' => 'Nepal', 'code' => 977],
|
||||
['title' => '巴勒斯坦', 'english' => 'Palestine', 'code' => 970],
|
||||
['title' => '巴哈马', 'english' => 'The Bahamas', 'code' => 1242],
|
||||
['title' => '巴基斯坦', 'english' => 'Pakistan', 'code' => 92],
|
||||
['title' => '巴巴多斯', 'english' => 'Barbados', 'code' => 1246],
|
||||
['title' => '巴布亚新几内亚', 'english' => 'Papua New Guinea', 'code' => 675],
|
||||
['title' => '巴拉圭', 'english' => 'Paraguay', 'code' => 595],
|
||||
['title' => '巴拿马', 'english' => 'Panama', 'code' => 507],
|
||||
['title' => '巴林', 'english' => 'Bahrain', 'code' => 973],
|
||||
['title' => '巴西', 'english' => 'Brazil', 'code' => 55],
|
||||
['title' => '布基纳法索', 'english' => ' Burkina Faso', 'code' => 226],
|
||||
['title' => '布隆迪', 'english' => 'Burundi', 'code' => 257],
|
||||
['title' => '希腊', 'english' => ' Greece', 'code' => 30],
|
||||
['title' => '帕劳', 'english' => 'Palau', 'code' => 680],
|
||||
['title' => '库克群岛', 'english' => ' Cook Islands', 'code' => 682],
|
||||
['title' => '开曼群岛', 'english' => 'Cayman Islands', 'code' => 1345],
|
||||
['title' => '德国', 'english' => ' Germany', 'code' => 49],
|
||||
['title' => '意大利', 'english' => 'Italy', 'code' => 39],
|
||||
['title' => '所罗门群岛', 'english' => ' Solomon Islands', 'code' => 677],
|
||||
['title' => '托克劳', 'english' => 'Tokelau', 'code' => 690],
|
||||
['title' => '拉脱维亚', 'english' => 'Latvia', 'code' => 371],
|
||||
['title' => '挪威', 'english' => 'Norway', 'code' => 47],
|
||||
['title' => '捷克共和国', 'english' => 'Czech Republic', 'code' => 420],
|
||||
['title' => '摩尔多瓦', 'english' => 'Moldova', 'code' => 373],
|
||||
['title' => '摩洛哥', 'english' => 'Morocco', 'code' => 212],
|
||||
['title' => '摩纳哥', 'english' => 'Monaco', 'code' => 377],
|
||||
['title' => '文莱', 'english' => 'Brunei Darussalam', 'code' => 673],
|
||||
['title' => '斐济', 'english' => 'Fiji', 'code' => 679],
|
||||
['title' => '斯威士兰王国', 'english' => 'The Kingdom of Swaziland', 'code' => 268],
|
||||
['title' => '斯洛伐克', 'english' => 'Slovakia', 'code' => 421],
|
||||
['title' => '斯洛文尼亚', 'english' => 'Slovenia', 'code' => 386],
|
||||
['title' => '斯里兰卡', 'english' => 'Sri Lanka', 'code' => 94],
|
||||
['title' => '新加坡', 'english' => 'Singapore ', 'code' => 65],
|
||||
['title' => '新喀里多尼亚', 'english' => 'New Caledonia', 'code' => 687],
|
||||
['title' => '新西兰', 'english' => 'New Zealand', 'code' => 64],
|
||||
['title' => '日本', 'english' => 'Japan', 'code' => 81],
|
||||
['title' => '智利', 'english' => 'Chile', 'code' => 56],
|
||||
['title' => '朝鲜', 'english' => 'Korea, North', 'code' => 850],
|
||||
['title' => '柬埔寨 ', 'english' => 'Cambodia', 'code' => 855],
|
||||
['title' => '格林纳达', 'english' => 'Grenada', 'code' => 1473],
|
||||
['title' => '格陵兰', 'english' => 'Greenland', 'code' => 299],
|
||||
['title' => '格鲁吉亚', 'english' => 'Georgia', 'code' => 995],
|
||||
['title' => '比利时', 'english' => 'Belgium', 'code' => 32],
|
||||
['title' => '毛里塔尼亚', 'english' => 'Mauritania', 'code' => 222],
|
||||
['title' => '毛里求斯', 'english' => 'Mauritius', 'code' => 230],
|
||||
['title' => '汤加', 'english' => 'Tonga', 'code' => 676],
|
||||
['title' => '沙特阿拉伯', 'english' => 'Saudi Arabia', 'code' => 966],
|
||||
['title' => '法国', 'english' => 'France', 'code' => 33],
|
||||
['title' => '法属圭亚那', 'english' => 'French Guiana', 'code' => 594],
|
||||
['title' => '法属波利尼西亚', 'english' => 'French Polynesia', 'code' => 689],
|
||||
['title' => '法属西印度群岛', 'english' => 'french west indies', 'code' => 596],
|
||||
['title' => '法罗群岛', 'english' => 'Faroe Islands', 'code' => 298],
|
||||
['title' => '波兰', 'english' => 'Poland', 'code' => 48],
|
||||
['title' => '波多黎各', 'english' => 'The Commonwealth of Puerto Rico', 'code' => 17871939],
|
||||
['title' => '波黑', 'english' => 'Bosnia and Herzegovina ', 'code' => 387],
|
||||
['title' => '泰国', 'english' => 'Thailand', 'code' => 66],
|
||||
['title' => '津巴布韦', 'english' => 'Zimbabwe', 'code' => 263],
|
||||
['title' => '洪都拉斯', 'english' => 'Honduras', 'code' => 504],
|
||||
['title' => '海地', 'english' => 'Haiti', 'code' => 509],
|
||||
['title' => '澳大利亚', 'english' => 'Australia', 'code' => 61],
|
||||
['title' => '澳门', 'english' => 'Macao', 'code' => 853],
|
||||
['title' => '爱尔兰', 'english' => 'Ireland', 'code' => 353],
|
||||
['title' => '爱沙尼亚', 'english' => 'Estonia', 'code' => 372],
|
||||
['title' => '牙买加 ', 'english' => 'Jamaica', 'code' => 1876],
|
||||
['title' => '特克斯和凯科斯群岛', 'english' => 'Turks and Caicos Islands', 'code' => 1649],
|
||||
['title' => '特立尼达和多巴哥', 'english' => 'Trinidad and Tobago', 'code' => 1868],
|
||||
['title' => '玻利维亚', 'english' => 'Bolivia', 'code' => 591],
|
||||
['title' => '瑙鲁', 'english' => 'Nauru', 'code' => 674],
|
||||
['title' => '瑞典', 'english' => 'Sweden', 'code' => 46],
|
||||
['title' => '瑞士', 'english' => 'Switzerland', 'code' => 41],
|
||||
['title' => '瓜德罗普', 'english' => 'Guadeloupe', 'code' => 590],
|
||||
['title' => '瓦利斯和富图纳群岛', 'english' => 'Wallis et Futuna', 'code' => 681],
|
||||
['title' => '瓦努阿图', 'english' => 'Vanuatu', 'code' => 678],
|
||||
['title' => '留尼汪 ', 'english' => 'Reunion', 'code' => 262],
|
||||
['title' => '白俄罗斯', 'english' => 'Belarus', 'code' => 375],
|
||||
['title' => '百慕大', 'english' => 'Bermuda', 'code' => 1441],
|
||||
['title' => '直布罗陀', 'english' => 'Gibraltar', 'code' => 350],
|
||||
['title' => '福克兰群岛', 'english' => 'Falkland', 'code' => 500],
|
||||
['title' => '科威特', 'english' => 'Kuwait', 'code' => 965],
|
||||
['title' => '科摩罗和马约特', 'english' => 'Comoros', 'code' => 269],
|
||||
['title' => '科特迪瓦', 'english' => 'Cote d’Ivoire', 'code' => 225],
|
||||
['title' => '秘鲁', 'english' => 'Peru', 'code' => 51],
|
||||
['title' => '突尼斯', 'english' => 'Tunisia', 'code' => 216],
|
||||
['title' => '立陶宛', 'english' => 'Lithuania', 'code' => 370],
|
||||
['title' => '索马里', 'english' => 'Somalia', 'code' => 252],
|
||||
['title' => '约旦', 'english' => 'Jordan', 'code' => 962],
|
||||
['title' => '纳米比亚', 'english' => 'Namibia', 'code' => 264],
|
||||
['title' => '纽埃岛', 'english' => 'Island of Niue', 'code' => 683],
|
||||
['title' => '缅甸 ', 'english' => 'Burma', 'code' => 95],
|
||||
['title' => '罗马尼亚', 'english' => 'Romania', 'code' => 40],
|
||||
['title' => '美国', 'english' => 'United States of America', 'code' => 1],
|
||||
['title' => '美属维京群岛', 'english' => 'Virgin Islands', 'code' => 1340],
|
||||
['title' => '美属萨摩亚', 'english' => 'American Samoa', 'code' => 1684],
|
||||
['title' => '老挝', 'english' => 'Laos', 'code' => 856],
|
||||
['title' => '肯尼亚', 'english' => 'Kenya', 'code' => 254],
|
||||
['title' => '芬兰', 'english' => 'Finland', 'code' => 358],
|
||||
['title' => '苏丹', 'english' => 'Sudan', 'code' => 249],
|
||||
['title' => '苏里南', 'english' => 'Suriname', 'code' => 597],
|
||||
['title' => '英国', 'english' => 'United Kingdom', 'code' => 44],
|
||||
['title' => '英属维京群岛', 'english' => 'British Virgin Islands', 'code' => 1284],
|
||||
['title' => '荷兰', 'english' => 'Netherlands', 'code' => 31],
|
||||
['title' => '荷属安的列斯', 'english' => 'Netherlands Antilles', 'code' => 599],
|
||||
['title' => '莫桑比克', 'english' => 'Mozambique', 'code' => 258],
|
||||
['title' => '莱索托', 'english' => 'Lesotho', 'code' => 266],
|
||||
['title' => '菲律宾', 'english' => 'Philippines', 'code' => 63],
|
||||
['title' => '萨尔瓦多', 'english' => 'El Salvador', 'code' => 503],
|
||||
['title' => '萨摩亚', 'english' => 'Samoa', 'code' => 685],
|
||||
['title' => '葡萄牙', 'english' => 'Portugal', 'code' => 351],
|
||||
['title' => '蒙古', 'english' => 'Mongolia', 'code' => 976],
|
||||
['title' => '西班牙', 'english' => 'Spain', 'code' => 34],
|
||||
['title' => '贝宁', 'english' => 'Benin', 'code' => 229],
|
||||
['title' => '赞比亚', 'english' => 'Zambia', 'code' => 260],
|
||||
['title' => '赤道几内亚', 'english' => 'Equatorial Guinea', 'code' => 240],
|
||||
['title' => '越南', 'english' => 'Vietnam', 'code' => 84],
|
||||
['title' => '阿塞拜疆', 'english' => 'Azerbaijan', 'code' => 994],
|
||||
['title' => '阿富汗', 'english' => 'Afghanistan', 'code' => 93],
|
||||
['title' => '阿尔及利亚', 'english' => 'Algeria', 'code' => 213],
|
||||
['title' => '阿尔巴尼亚', 'english' => 'Albania', 'code' => 355],
|
||||
['title' => '阿拉伯联合酋长国', 'english' => 'United Arab Emirates', 'code' => 971],
|
||||
['title' => '阿曼', 'english' => 'Oman', 'code' => 968],
|
||||
['title' => '阿根廷', 'english' => 'Argentina', 'code' => 54],
|
||||
['title' => '阿鲁巴', 'english' => 'Aruba', 'code' => 297],
|
||||
['title' => '韩国', 'english' => 'Korea, South)', 'code' => 82],
|
||||
['title' => '香港', 'english' => 'Hong Kong(SAR)', 'code' => 852],
|
||||
['title' => '马其顿', 'english' => 'Macedonia', 'code' => 389],
|
||||
['title' => '马尔代夫', 'english' => 'Maldives ', 'code' => 960],
|
||||
['title' => '马拉维', 'english' => ' Malawi', 'code' => 265],
|
||||
['title' => '马来西亚', 'english' => 'Malaysia', 'code' => 60],
|
||||
['title' => '马绍尔群岛', 'english' => 'Marshall Islands', 'code' => 692],
|
||||
['title' => '马耳他', 'english' => 'Malta', 'code' => 356],
|
||||
['title' => '马达加斯加', 'english' => 'Madagascar', 'code' => 261],
|
||||
['title' => '马里', 'english' => 'Mali', 'code' => 223],
|
||||
['title' => '黎巴嫩', 'english' => 'Lebanon', 'code' => 961],
|
||||
['title' => '黑山共和国', 'english' => 'The Republic of Montenegro', 'code' => 382],
|
||||
];
|
||||
}
|
||||
}
|
||||
100
plugin/think-library/src/service/ModuleService.php
Normal file
100
plugin/think-library/src/service/ModuleService.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use think\admin\Library;
|
||||
use think\admin\Service;
|
||||
|
||||
/**
|
||||
* 系统模块管理
|
||||
* @class ModuleService
|
||||
* @package think\admin\service
|
||||
*/
|
||||
class ModuleService extends Service
|
||||
{
|
||||
/**
|
||||
* 获取版本号信息
|
||||
* @return string
|
||||
*/
|
||||
public static function getVersion(): string
|
||||
{
|
||||
$libray = self::getLibrarys('zoujingli/think-library');
|
||||
return trim($libray['version'] ?? 'v6.0.0', 'v');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取运行参数变量
|
||||
* @param string $field 指定字段
|
||||
* @return string
|
||||
*/
|
||||
public static function getRunVar(string $field): string
|
||||
{
|
||||
$file = syspath('vendor/binarys.php');
|
||||
if (is_file($file) && is_array($binarys = include $file)) {
|
||||
return $binarys[$field] ?? '';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 PHP 执行路径
|
||||
* @return string
|
||||
*/
|
||||
public static function getPhpExec(): string
|
||||
{
|
||||
if ($phpExec = sysvar($keys = 'phpBinary')) return $phpExec;
|
||||
if (ProcessService::isFile($phpExec = self::getRunVar('php'))) {
|
||||
return sysvar($keys, $phpExec);
|
||||
} else {
|
||||
$phpExec = str_replace('/sbin/php-fpm', '/bin/php', PHP_BINARY);
|
||||
$phpExec = preg_replace('#-(cgi|fpm)(\.exe)?$#', '$2', $phpExec);
|
||||
return sysvar($keys, ProcessService::isFile($phpExec) ? $phpExec : 'php');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用模块
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
public static function getModules(array $data = []): array
|
||||
{
|
||||
$path = Library::$sapp->getBasePath();
|
||||
foreach (scandir($path) as $item) if ($item[0] !== '.') {
|
||||
if (is_dir(realpath($path . $item))) $data[] = $item;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地组件
|
||||
* @param ?string $package 指定包名
|
||||
* @param boolean $force 强制刷新
|
||||
* @return array|string|null
|
||||
*/
|
||||
public static function getLibrarys(?string $package = null, bool $force = false)
|
||||
{
|
||||
$plugs = sysvar($keys = 'think.admin.version');
|
||||
if ((empty($plugs) || $force) && is_file($file = syspath('vendor/versions.php'))) {
|
||||
$plugs = sysvar($keys, include $file);
|
||||
}
|
||||
return empty($package) ? $plugs : ($plugs[$package] ?? null);
|
||||
}
|
||||
}
|
||||
218
plugin/think-library/src/service/NodeService.php
Normal file
218
plugin/think-library/src/service/NodeService.php
Normal file
@ -0,0 +1,218 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use think\admin\Exception;
|
||||
use think\admin\extend\ToolsExtend;
|
||||
use think\admin\Library;
|
||||
use think\admin\Plugin;
|
||||
use think\admin\Service;
|
||||
|
||||
/**
|
||||
* 应用节点服务管理
|
||||
* @class NodeService
|
||||
* @method static array getModules() 获取应用列表
|
||||
* @method static array scanDirectory() 扫描目录列表
|
||||
* @package think\admin\service
|
||||
*/
|
||||
class NodeService extends Service
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取默认应用空间名
|
||||
* @param string $suffix 后缀路径
|
||||
* @return string
|
||||
*/
|
||||
public static function space(string $suffix = ''): string
|
||||
{
|
||||
$default = Library::$sapp->config->get('app.app_namespace') ?: 'app';
|
||||
return empty($suffix) ? $default : trim($default . '\\' . trim($suffix, '\\/'), '\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰转下划线规则
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
public static function nameTolower(string $name): string
|
||||
{
|
||||
$dots = [];
|
||||
foreach (explode('.', strtr($name, '/', '.')) as $dot) {
|
||||
$dots[] = trim(preg_replace("/[A-Z]/", "_\\0", $dot), '_');
|
||||
}
|
||||
return strtolower(join('.', $dots));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前节点内容
|
||||
* @param string $type app|module|controller|action
|
||||
* @return string
|
||||
*/
|
||||
public static function getCurrent(string $type = ''): string
|
||||
{
|
||||
// 获取应用节点
|
||||
$appname = strtolower(Library::$sapp->http->getName());
|
||||
if (in_array($type, ['app', 'module'])) return $appname;
|
||||
|
||||
// 获取控制器节点
|
||||
$controller = static::nameTolower(Library::$sapp->request->controller());
|
||||
if ($type === 'controller') return "{$appname}/{$controller}";
|
||||
|
||||
// 获取方法权限节点
|
||||
$method = strtolower(Library::$sapp->request->action());
|
||||
return "{$appname}/{$controller}/{$method}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查并完整节点内容
|
||||
* @param ?string $node
|
||||
* @return string
|
||||
*/
|
||||
public static function fullNode(?string $node = ''): string
|
||||
{
|
||||
if (empty($node)) return static::getCurrent();
|
||||
switch (count($attrs = explode('/', $node))) {
|
||||
case 1: # 方法名
|
||||
return static::getCurrent('controller') . '/' . strtolower($node);
|
||||
case 2: # 控制器/方法名
|
||||
$suffix = static::nameTolower($attrs[0]) . '/' . $attrs[1];
|
||||
return static::getCurrent('module') . '/' . strtolower($suffix);
|
||||
default: # 应用名/控制器/方法名?[其他参数]
|
||||
$attrs[1] = static::nameTolower($attrs[1]);
|
||||
return strtolower(join('/', $attrs));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有控制器入口
|
||||
* @param boolean $force 强制更新
|
||||
* @return array
|
||||
*/
|
||||
public static function getMethods(bool $force = false): array
|
||||
{
|
||||
$skey = 'think.admin.methods';
|
||||
if (empty($force)) {
|
||||
$data = sysvar($skey) ?: Library::$sapp->cache->get('SystemAuthNode', []);
|
||||
if (count($data) > 0) return sysvar($skey, $data);
|
||||
} else {
|
||||
$data = [];
|
||||
}
|
||||
// 排除内置方法,禁止访问内置方法及忽略的应用模块配置
|
||||
$ignoreMethods = get_class_methods('\think\admin\Controller');
|
||||
$ignoreAppNames = Library::$sapp->config->get('app.rbac_ignore', []);
|
||||
// 扫描所有代码控制器节点,更新节点缓存
|
||||
foreach (ToolsExtend::scanDirectory(Library::$sapp->getBasePath(), 'php') as $name) {
|
||||
if (preg_match("|^(\w+)/controller/(.+)\.php$|i", strtr($name, '\\', '/'), $matches)) {
|
||||
[, $appName, $className] = $matches;
|
||||
if (in_array($appName, $ignoreAppNames)) continue;
|
||||
static::_parseClass($appName, self::space($appName), $className, $ignoreMethods, $data);
|
||||
}
|
||||
}
|
||||
// 扫描所有插件代码
|
||||
foreach (Plugin::get() as $appName => $plugin) {
|
||||
if (in_array($appName, $ignoreAppNames)) continue;
|
||||
[$appPath, $appSpace] = [$plugin['path'], $plugin['space']];
|
||||
foreach (ToolsExtend::scanDirectory($appPath, 'php') as $name) {
|
||||
if (preg_match("|^.*?controller/(.+)\.php$|i", strtr($name, '\\', '/'), $matches)) {
|
||||
static::_parseClass($appName, $appSpace, $matches[1], $ignoreMethods, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 节点数据回调处理
|
||||
if (function_exists('admin_node_filter')) {
|
||||
$data = call_user_func('admin_node_filter', $data);
|
||||
}
|
||||
// 缓存系统节点数据
|
||||
Library::$sapp->cache->set('SystemAuthNode', $data);
|
||||
return sysvar($skey, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析节点数据
|
||||
* @param string $appName 应用名称
|
||||
* @param string $appSpace 应用空间
|
||||
* @param string $className 应用类型
|
||||
* @param array $ignoreNode 忽略节点
|
||||
* @param array $data 绑定节点的数据
|
||||
* @return void
|
||||
*/
|
||||
private static function _parseClass(string $appName, string $appSpace, string $className, array $ignoreNode, array &$data)
|
||||
{
|
||||
$classfull = strtr("{$appSpace}/controller/{$className}", '/', '\\');
|
||||
if (class_exists($classfull) && ($class = new ReflectionClass($classfull))) {
|
||||
$prefix = strtolower(strtr("{$appName}/" . static::nameTolower($className), '\\', '/'));
|
||||
$data[$prefix] = static::_parseComment($class->getDocComment() ?: '', $className);
|
||||
foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
if (in_array($metname = $method->getName(), $ignoreNode)) continue;
|
||||
$data[strtolower("{$prefix}/{$metname}")] = static::_parseComment($method->getDocComment() ?: '', $metname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析硬节点属性
|
||||
* @param string $comment 备注内容
|
||||
* @param string $default 默认标题
|
||||
* @return array
|
||||
*/
|
||||
private static function _parseComment(string $comment, string $default = ''): array
|
||||
{
|
||||
$text = strtr($comment, "\n", ' ');
|
||||
$title = preg_replace('/^\/\*\s*\*\s*\*\s*(.*?)\s*\*.*?$/', '$1', $text);
|
||||
if (in_array(substr($title, 0, 5), ['@auth', '@menu', '@logi'])) $title = $default;
|
||||
return [
|
||||
'title' => $title ?: $default,
|
||||
'isauth' => intval(preg_match('/@auth\s*true/i', $text)),
|
||||
'ismenu' => intval(preg_match('/@menu\s*true/i', $text)),
|
||||
'islogin' => intval(preg_match('/@login\s*true/i', $text)),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 重构兼容处理
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function __call(string $name, array $arguments)
|
||||
{
|
||||
return static::__callStatic($name, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重构兼容处理
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function __callStatic(string $name, array $arguments)
|
||||
{
|
||||
if ($name === 'scanDirectory') {
|
||||
return ToolsExtend::scanDirectory(...$arguments);
|
||||
} elseif ($name === 'getModules') {
|
||||
return ModuleService::getModules(...$arguments);
|
||||
} else {
|
||||
throw new Exception("method not exists: NodeService::{$name}()");
|
||||
}
|
||||
}
|
||||
}
|
||||
236
plugin/think-library/src/service/ProcessService.php
Normal file
236
plugin/think-library/src/service/ProcessService.php
Normal file
@ -0,0 +1,236 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
use think\admin\Exception;
|
||||
use think\admin\extend\CodeExtend;
|
||||
use think\admin\Library;
|
||||
use think\admin\Service;
|
||||
|
||||
/**
|
||||
* 系统进程管理服务
|
||||
* @class ProcessService
|
||||
* @package think\admin\service
|
||||
*/
|
||||
class ProcessService extends Service
|
||||
{
|
||||
|
||||
/**
|
||||
* 生成 PHP 指令
|
||||
* @param string $args
|
||||
* @return string
|
||||
*/
|
||||
public static function php(string $args = ''): string
|
||||
{
|
||||
return ModuleService::getPhpExec() . ' ' . $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 Think 指令
|
||||
* @param string $args 指令参数
|
||||
* @param boolean $simple 仅返回内容
|
||||
* @return string
|
||||
*/
|
||||
public static function think(string $args = '', bool $simple = false): string
|
||||
{
|
||||
$command = syspath('think') . ' ' . $args;
|
||||
return $simple ? $command : self::php($command);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 Composer 指令
|
||||
* @param string $args 参数
|
||||
* @return string
|
||||
*/
|
||||
public static function composer(string $args = ''): string
|
||||
{
|
||||
static $comExec;
|
||||
if (empty($comExec)) {
|
||||
$comExec = ModuleService::getRunVar('com');
|
||||
$comExec = self::isFile($comExec) ? self::php($comExec) : 'composer';
|
||||
}
|
||||
$root = Library::$sapp->getRootPath();
|
||||
return "{$comExec} -d {$root} {$args}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 Think 进程
|
||||
* @param string $args 执行参数
|
||||
* @param integer $usleep 延时等待
|
||||
* @param boolean $doQuery 查询进程
|
||||
* @return array
|
||||
*/
|
||||
public static function thinkExec(string $args, int $usleep = 0, bool $doQuery = false): array
|
||||
{
|
||||
static::create(static::think($args), $usleep);
|
||||
return $doQuery ? static::query(static::think($args, true)) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 Think 进程
|
||||
* @param string $args 执行参数
|
||||
* @return array
|
||||
*/
|
||||
public static function thinkQuery(string $args): array
|
||||
{
|
||||
return static::query(static::think($args, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建异步进程
|
||||
* @param string $command 任务指令
|
||||
* @param integer $usleep 延时毫米
|
||||
*/
|
||||
public static function create(string $command, int $usleep = 0)
|
||||
{
|
||||
if (static::isWin()) {
|
||||
static::exec(__DIR__ . "/bin/console.exe {$command}");
|
||||
} else {
|
||||
static::exec("{$command} > /dev/null 2>&1 &");
|
||||
}
|
||||
$usleep > 0 && usleep($usleep);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询进程列表
|
||||
* @param string $cmd 任务指令
|
||||
* @param string $name 进程名称
|
||||
* @return array
|
||||
*/
|
||||
public static function query(string $cmd, string $name = 'php.exe'): array
|
||||
{
|
||||
$list = [];
|
||||
if (static::isWin()) {
|
||||
$lines = static::exec("wmic process where name=\"{$name}\" get processid,CommandLine", true);
|
||||
foreach ($lines as $line) if (is_numeric(stripos($line, $cmd))) {
|
||||
$attr = explode(' ', trim(preg_replace('#\s+#', ' ', $line)));
|
||||
$list[] = ['pid' => array_pop($attr), 'cmd' => join(' ', $attr)];
|
||||
}
|
||||
} else {
|
||||
$lines = static::exec("ps ax|grep -v grep|grep \"{$cmd}\"", true);
|
||||
foreach ($lines as $line) if (is_numeric(stripos($line, $cmd))) {
|
||||
$attr = explode(' ', trim(preg_replace('#\s+#', ' ', $line)));
|
||||
[$pid] = [array_shift($attr), array_shift($attr), array_shift($attr), array_shift($attr)];
|
||||
$list[] = ['pid' => $pid, 'cmd' => join(' ', $attr)];
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭指定进程
|
||||
* @param integer $pid 进程号
|
||||
* @return boolean
|
||||
*/
|
||||
public static function close(int $pid): bool
|
||||
{
|
||||
if (static::isWin()) {
|
||||
static::exec("wmic process {$pid} call terminate");
|
||||
} else {
|
||||
static::exec("kill -9 {$pid}");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 立即执行指令
|
||||
* @param string $command 执行指令
|
||||
* @param boolean $outarr 返回数组
|
||||
* @param ?callable $callable 逐行处理
|
||||
* @return string|array
|
||||
*/
|
||||
public static function exec(string $command, bool $outarr = false, ?callable $callable = null)
|
||||
{
|
||||
$process = Process::fromShellCommandline($command)->setWorkingDirectory(Library::$sapp->getRootPath());
|
||||
$process->run(is_callable($callable) ? static function ($type, $text) use ($callable, $process) {
|
||||
call_user_func($callable, $process, $type, trim(CodeExtend::text2utf8($text))) === true && $process->stop();
|
||||
} : null);
|
||||
$output = str_replace("\r\n", "\n", CodeExtend::text2utf8($process->getOutput()));
|
||||
return $outarr ? explode("\n", $output) : trim($output);
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出命令行消息
|
||||
* @param string $message 输出内容
|
||||
* @param integer $backline 回退行数
|
||||
* @return void
|
||||
*/
|
||||
public static function message(string $message, int $backline = 0)
|
||||
{
|
||||
while ($backline-- > 0) $message = "\033[1A\r\033[K{$message}";
|
||||
print_r($message . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断系统类型 WINDOWS
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isWin(): bool
|
||||
{
|
||||
return PATH_SEPARATOR === ';';
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断系统类型 UNIX
|
||||
* @return bool
|
||||
*/
|
||||
public static function isUnix(): bool
|
||||
{
|
||||
return PATH_SEPARATOR !== ';';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否存在
|
||||
* @param string $file 文件路径
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isFile(string $file): bool
|
||||
{
|
||||
try {
|
||||
return $file !== '' && is_file($file);
|
||||
} catch (\Error|\Exception $exception) {
|
||||
try {
|
||||
if (self::isWin()) {
|
||||
return self::exec("if exist \"{$file}\" echo 1") === '1';
|
||||
} else {
|
||||
return self::exec("if [ -f \"{$file}\" ];then echo 1;fi") === '1';
|
||||
}
|
||||
} catch (\Error|\Exception $exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态兼容处理
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function __callStatic(string $method, array $arguments)
|
||||
{
|
||||
if ($method === 'thinkCreate') {
|
||||
return self::thinkExec(...$arguments);
|
||||
} else {
|
||||
throw new Exception("method not exists: ProcessService::{$method}()");
|
||||
}
|
||||
}
|
||||
}
|
||||
299
plugin/think-library/src/service/QueueService.php
Normal file
299
plugin/think-library/src/service/QueueService.php
Normal file
@ -0,0 +1,299 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use think\admin\Exception;
|
||||
use think\admin\extend\CodeExtend;
|
||||
use think\admin\model\SystemQueue;
|
||||
use think\admin\Service;
|
||||
|
||||
/**
|
||||
* 任务基础服务
|
||||
* @class QueueService
|
||||
* @package think\admin\service
|
||||
*/
|
||||
class QueueService extends Service
|
||||
{
|
||||
|
||||
/**
|
||||
* 当前任务编号
|
||||
* @var string
|
||||
*/
|
||||
public $code = '';
|
||||
|
||||
/**
|
||||
* 当前任务标题
|
||||
* @var string
|
||||
*/
|
||||
public $title = '';
|
||||
|
||||
/**
|
||||
* 当前任务参数
|
||||
* @var array
|
||||
*/
|
||||
public $data = [];
|
||||
|
||||
/**
|
||||
* 当前任务数据
|
||||
* @var SystemQueue
|
||||
*/
|
||||
public $record;
|
||||
|
||||
/**
|
||||
* 运行消息记录
|
||||
* @var array
|
||||
*/
|
||||
private $msgs = [];
|
||||
|
||||
/**
|
||||
* 运行消息写库
|
||||
* @var boolean
|
||||
*/
|
||||
private $msgsWriteDb = false;
|
||||
|
||||
/**
|
||||
* 异常尝试次数
|
||||
* @var integer
|
||||
*/
|
||||
private $tryTimes = 0;
|
||||
|
||||
/**
|
||||
* 数据初始化
|
||||
* @param string $code
|
||||
* @return static
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function initialize(string $code = ''): QueueService
|
||||
{
|
||||
// 重置消息内容
|
||||
if (!empty($this->code) && $this->code !== $code) {
|
||||
$this->_lazyWrite(true);
|
||||
$this->msgs = [];
|
||||
}
|
||||
// 初始化新任务数据
|
||||
if (!empty($code)) {
|
||||
$this->record = SystemQueue::mk()->master()->where(['code' => $code])->findOrEmpty();
|
||||
if ($this->record->isEmpty()) {
|
||||
$message = sprintf("Qeueu initialize failed, Queue %s not found.", $code);
|
||||
$this->app->log->error($message);
|
||||
throw new Exception($message);
|
||||
}
|
||||
$this->code = $code;
|
||||
$this->data = json_decode($this->record['exec_data'], true) ?: [];
|
||||
$this->title = $this->record['title'];
|
||||
}
|
||||
// 消息写入数据库
|
||||
$this->msgsWriteDb = in_array('message', SystemQueue::mk()->getTableFields());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重发异步任务
|
||||
* @param integer $wait 等待时间
|
||||
* @return $this
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function reset(int $wait = 0): QueueService
|
||||
{
|
||||
if ($this->record->isEmpty()) {
|
||||
$message = "Qeueu reset failed, Queue {$this->code} data cannot be empty!";
|
||||
$this->app->log->error($message);
|
||||
throw new Exception($message);
|
||||
}
|
||||
$this->record->save(['exec_pid' => 0, 'exec_time' => time() + $wait, 'status' => 1]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加定时清理任务
|
||||
* @param integer $loops 循环时间
|
||||
* @return $this
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function addCleanQueue(int $loops = 3600): QueueService
|
||||
{
|
||||
return static::register('定时清理系统任务数据', "xadmin:service clean", 0, [], 0, $loops);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册异步处理任务
|
||||
* @param string $title 任务名称
|
||||
* @param string $command 执行脚本
|
||||
* @param integer $later 延时时间
|
||||
* @param array $data 任务附加数据
|
||||
* @param integer $rscript 任务类型(0单例,1多例)
|
||||
* @param integer $loops 循环等待时间
|
||||
* @return $this
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function register(string $title, string $command, int $later = 0, array $data = [], int $rscript = 0, int $loops = 0): QueueService
|
||||
{
|
||||
try {
|
||||
$map = [['title', '=', $title], ['status', 'in', [1, 2]]];
|
||||
if (empty($rscript) && ($queue = SystemQueue::mk()->master()->where($map)->findOrEmpty())->isExists()) {
|
||||
throw new Exception(lang('已创建请等待处理完成!'), 0, $queue['code']);
|
||||
}
|
||||
// 生成唯一编号
|
||||
do $map = ['code' => $code = CodeExtend::uniqidDate(16, 'Q')];
|
||||
while (($queue = SystemQueue::mk()->master()->where($map)->findOrEmpty())->isExists());
|
||||
// 写入任务数据
|
||||
$queue->save([
|
||||
'code' => $code,
|
||||
'title' => $title,
|
||||
'command' => $command,
|
||||
'attempts' => 0,
|
||||
'rscript' => intval(boolval($rscript)),
|
||||
'exec_data' => json_encode($data, JSON_UNESCAPED_UNICODE),
|
||||
'exec_time' => $later > 0 ? time() + $later : time(),
|
||||
'enter_time' => 0,
|
||||
'outer_time' => 0,
|
||||
'loops_time' => $loops,
|
||||
'create_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
$that = static::instance([], true)->initialize($code);
|
||||
$that->progress(1, '>>> 任务创建成功 <<<', '0.00');
|
||||
return $that;
|
||||
} catch (Exception $exception) {
|
||||
throw $exception;
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception($exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置任务进度信息
|
||||
* @param ?integer $status 任务状态
|
||||
* @param ?string $message 进度消息
|
||||
* @param ?string $progress 进度数值
|
||||
* @param integer $backline 回退信息行
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function progress(?int $status = null, ?string $message = null, ?string $progress = null, int $backline = 0): array
|
||||
{
|
||||
if (is_numeric($status) && intval($status) === 3) {
|
||||
if (!is_numeric($progress)) $progress = '100.00';
|
||||
if (is_null($message)) $message = '>>> 任务已经完成 <<<';
|
||||
}
|
||||
if (is_numeric($status) && intval($status) === 4) {
|
||||
if (!is_numeric($progress)) $progress = '0.00';
|
||||
if (is_null($message)) $message = '>>> 任务执行失败 <<<';
|
||||
}
|
||||
try {
|
||||
if (empty($this->msgs)) $this->msgs = $this->app->cache->get("queue_{$this->code}_progress", [
|
||||
'code' => $this->code, 'status' => $status, 'sctime' => 0, 'message' => $message, 'progress' => $progress, 'history' => []
|
||||
]);
|
||||
$this->tryTimes = 0;
|
||||
} catch (\Exception|\Error $exception) {
|
||||
if ($this->tryTimes++ > 10) throw new Exception('读取进程缓存异常!');
|
||||
return $this->progress($status, $message, $progress, $backline);
|
||||
}
|
||||
while (--$backline > -1 && count($this->msgs['history']) > 0) array_pop($this->msgs['history']);
|
||||
if (is_numeric($status)) $this->msgs['status'] = intval($status);
|
||||
if (is_numeric($progress)) $progress = str_pad(sprintf('%.2f', $progress), 6, '0', STR_PAD_LEFT);
|
||||
if (is_string($message) && is_null($progress)) {
|
||||
$this->msgs['swrite'] = 0;
|
||||
$this->msgs['message'] = $message;
|
||||
$this->msgs['history'][] = ['message' => $message, 'progress' => $this->msgs['progress'], 'datetime' => date('Y-m-d H:i:s')];
|
||||
} elseif (is_null($message) && is_numeric($progress)) {
|
||||
$this->msgs['swrite'] = 0;
|
||||
$this->msgs['progress'] = $progress;
|
||||
$this->msgs['history'][] = ['message' => $this->msgs['message'], 'progress' => $progress, 'datetime' => date('Y-m-d H:i:s')];
|
||||
} elseif (is_string($message) && is_numeric($progress)) {
|
||||
$this->msgs['swrite'] = 0;
|
||||
$this->msgs['message'] = $message;
|
||||
$this->msgs['progress'] = $progress;
|
||||
$this->msgs['history'][] = ['message' => $message, 'progress' => $progress, 'datetime' => date('Y-m-d H:i:s')];
|
||||
}
|
||||
if (is_string($message) || is_numeric($progress)) if (count($this->msgs['history']) > 10) {
|
||||
$this->msgs['history'] = array_slice($this->msgs['history'], -10);
|
||||
}
|
||||
// 延时写入并返回内容
|
||||
return $this->_lazyWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* 延时写入记录
|
||||
* @param boolean $force 强制更新
|
||||
* @return array
|
||||
*/
|
||||
private function _lazyWrite(bool $force = false): array
|
||||
{
|
||||
// 无消息状态
|
||||
if (!isset($this->msgs['status'])) return $this->msgs;
|
||||
// 消息延时写数据库
|
||||
if ($force || empty($this->msgs['sctime']) || in_array($this->msgs['status'], [3, 4]) || microtime(true) - $this->msgs['sctime'] > 1) {
|
||||
if (empty($this->msgs['swrite']) && $this->record->isExists()) {
|
||||
[$this->msgs['swrite'], $this->msgs['sctime']] = [1, microtime(true)];
|
||||
$this->app->cache->set("queue_{$this->code}_progress", $this->msgs, 864000);
|
||||
if ($this->msgsWriteDb) {
|
||||
$this->record->save(['message' => json_encode($this->msgs, JSON_UNESCAPED_UNICODE)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务进度
|
||||
* @param integer $total 记录总和
|
||||
* @param integer $count 当前记录
|
||||
* @param string $message 文字描述
|
||||
* @param integer $backline 回退行数
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function message(int $total, int $count, string $message = '', int $backline = 0): void
|
||||
{
|
||||
$prefix = str_pad("{$count}", strlen(strval($total)), '0', STR_PAD_LEFT);
|
||||
if (defined('WorkQueueCode')) {
|
||||
$this->progress(2, "[{$prefix}/{$total}] {$message}", sprintf("%.2f", $count / max($total, 1) * 100), $backline);
|
||||
} else {
|
||||
ProcessService::message("[{$prefix}/{$total}] {$message}", $backline);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务执行成功
|
||||
* @param string $message 消息内容
|
||||
* @throws Exception
|
||||
*/
|
||||
public function success(string $message): void
|
||||
{
|
||||
throw new Exception($message, 3, $this->code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务执行失败
|
||||
* @param string $message 消息内容
|
||||
* @throws Exception
|
||||
*/
|
||||
public function error(string $message): void
|
||||
{
|
||||
throw new Exception($message, 4, $this->code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务处理
|
||||
* @param array $data 任务参数
|
||||
* @return void
|
||||
*/
|
||||
public function execute(array $data = [])
|
||||
{
|
||||
}
|
||||
}
|
||||
263
plugin/think-library/src/service/RuntimeService.php
Normal file
263
plugin/think-library/src/service/RuntimeService.php
Normal file
@ -0,0 +1,263 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use think\admin\Library;
|
||||
use think\admin\support\Route;
|
||||
use think\admin\support\Url;
|
||||
use think\App;
|
||||
use think\Container;
|
||||
use think\Request;
|
||||
use think\Response;
|
||||
|
||||
/**
|
||||
* 系统运行服务
|
||||
* @class RuntimeService
|
||||
* @package think\admin\service
|
||||
*/
|
||||
class RuntimeService
|
||||
{
|
||||
|
||||
/**
|
||||
* 开发运行模式
|
||||
* @var string
|
||||
*/
|
||||
public const MODE_DEVE = 'dev';
|
||||
|
||||
/**
|
||||
* 演示运行模式
|
||||
* @var string
|
||||
*/
|
||||
public const MODE_DEMO = 'demo';
|
||||
|
||||
/**
|
||||
* 本地运行模式
|
||||
* @var string
|
||||
*/
|
||||
public const MODE_LOCAL = 'local';
|
||||
|
||||
/**
|
||||
* 环境配置文件位置
|
||||
* @var string
|
||||
*/
|
||||
private static $envFile = './runtime/.env';
|
||||
|
||||
/**
|
||||
* 初始化文件哈希值
|
||||
* @var string
|
||||
*/
|
||||
private static $evnHash = '';
|
||||
|
||||
/**
|
||||
* 系统服务初始化
|
||||
* @param ?\think\App $app
|
||||
* @return App
|
||||
*/
|
||||
public static function init(?App $app = null): App
|
||||
{
|
||||
// 初始化运行环境
|
||||
Library::$sapp = $app ?: Container::getInstance()->make(App::class);
|
||||
Library::$sapp->bind('think\Route', Route::class);
|
||||
Library::$sapp->bind('think\route\Url', Url::class);
|
||||
// 初始化运行配置位置
|
||||
self::$envFile = syspath('runtime/.env');
|
||||
return Library::$sapp->debug(static::isDebug());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取动态配置
|
||||
* @param null|string $name 配置名称
|
||||
* @param array $default 配置内容
|
||||
* @return array|string
|
||||
*/
|
||||
public static function get(?string $name = null, array $default = [])
|
||||
{
|
||||
$keys = 'think.admin.runtime';
|
||||
if (empty($envs = sysvar($keys) ?: [])) {
|
||||
// 读取默认配置
|
||||
clearstatcache(true, self::$envFile);
|
||||
is_file(self::$envFile) && Library::$sapp->env->load(self::$envFile);
|
||||
// 动态判断赋值
|
||||
$envs['mode'] = Library::$sapp->env->get('RUNTIME_MODE') ?: 'debug';
|
||||
$envs['appmap'] = Library::$sapp->env->get('RUNTIME_APPMAP') ?: [];
|
||||
$envs['domain'] = Library::$sapp->env->get('RUNTIME_DOMAIN') ?: [];
|
||||
sysvar($keys, $envs);
|
||||
}
|
||||
return is_null($name) ? $envs : ($envs[$name] ?? $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置动态配置
|
||||
* @param null|mixed $mode 支持模式
|
||||
* @param null|array $appmap 应用映射
|
||||
* @param null|array $domain 域名映射
|
||||
* @return boolean 是否调试模式
|
||||
*/
|
||||
public static function set(?string $mode = null, ?array $appmap = [], ?array $domain = []): bool
|
||||
{
|
||||
$envs = self::get();
|
||||
$envs['mode'] = is_null($mode) ? $envs['mode'] : $mode;
|
||||
$envs['appmap'] = static::uniqueMergeArray($envs['appmap'], $appmap);
|
||||
$envs['domain'] = static::uniqueMergeArray($envs['domain'], $domain);
|
||||
|
||||
// 组装配置文件格式
|
||||
$rows[] = "mode = {$envs['mode']}";
|
||||
foreach ($envs['appmap'] as $key => $item) $rows[] = "appmap[{$key}] = {$item}";
|
||||
foreach ($envs['domain'] as $key => $item) $rows[] = "domain[{$key}] = {$item}";
|
||||
|
||||
// 写入并刷新文件希值
|
||||
@file_put_contents(self::$envFile, "[RUNTIME]\n" . join("\n", $rows));
|
||||
|
||||
// 同步更新当前环境
|
||||
sysvar('think.admin.runtime', $envs);
|
||||
|
||||
// 应用当前的配置文件
|
||||
return static::apply($envs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步运行配置
|
||||
* @return void
|
||||
*/
|
||||
public static function sync()
|
||||
{
|
||||
clearstatcache(true, self::$envFile);
|
||||
is_file(self::$envFile) && md5_file(self::$envFile) !== self::$evnHash && self::apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定动态配置
|
||||
* @param array $data 配置数据
|
||||
* @return boolean 是否调试模式
|
||||
*/
|
||||
public static function apply(array $data = []): bool
|
||||
{
|
||||
// 设置模块绑定
|
||||
$data = array_merge(static::get(), $data);
|
||||
$appmap = static::uniqueMergeArray(Library::$sapp->config->get('app.app_map', []), $data['appmap']);
|
||||
$domain = static::uniqueMergeArray(Library::$sapp->config->get('app.domain_bind', []), $data['domain']);
|
||||
Library::$sapp->config->set(['app_map' => $appmap, 'domain_bind' => $domain], 'app');
|
||||
|
||||
// 记录配置文件
|
||||
is_file(self::$envFile) && self::$evnHash = md5_file(self::$envFile);
|
||||
|
||||
// 初始化调试配置
|
||||
return Library::$sapp->debug($data['mode'] !== 'product')->isDebug();
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩发布项目
|
||||
* @return string
|
||||
*/
|
||||
public static function push(): string
|
||||
{
|
||||
self::set('product');
|
||||
$connection = Library::$sapp->db->getConfig('default');
|
||||
Library::$sapp->console->call('optimize:schema', ["--connection={$connection}"]);
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断运行环境
|
||||
* @param string $type 运行模式(dev|demo|local)
|
||||
* @return boolean
|
||||
*/
|
||||
public static function check(string $type = 'dev'): bool
|
||||
{
|
||||
$domain = Library::$sapp->request->host(true);
|
||||
$isDemo = boolval(preg_match('|v\d+\.thinkadmin\.top|', $domain));
|
||||
$isLocal = $domain === '127.0.0.1' || is_numeric(stripos($domain, 'local'));
|
||||
if ($type === static::MODE_DEVE) return $isLocal || $isDemo;
|
||||
if ($type === static::MODE_DEMO) return $isDemo;
|
||||
if ($type === static::MODE_LOCAL) return $isLocal;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理运行缓存
|
||||
* @param boolean $force 清理目录
|
||||
* @return boolean
|
||||
*/
|
||||
public static function clear(bool $force = true): bool
|
||||
{
|
||||
$data = static::get();
|
||||
AdminService::clear() && Library::$sapp->cache->clear();
|
||||
$force && Library::$sapp->console->call('clear', ['--dir']);
|
||||
return static::set($data['mode'], $data['appmap'], $data['domain']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开发模式运行
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isDebug(): bool
|
||||
{
|
||||
return static::get('mode') !== 'product';
|
||||
}
|
||||
|
||||
/**
|
||||
* 生产模式运行
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isOnline(): bool
|
||||
{
|
||||
return static::get('mode') === 'product';
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化主程序
|
||||
* @param ?\think\App $app
|
||||
* @param ?\think\Request $request
|
||||
* @return \think\Response
|
||||
*/
|
||||
public static function doWebsiteInit(?App $app = null, ?Request $request = null): Response
|
||||
{
|
||||
$http = static::init($app)->http;
|
||||
$request = $request ?: Library::$sapp->make(Request::class);
|
||||
Library::$sapp->instance('request', $request);
|
||||
($response = $http->run($request))->send();
|
||||
$http->end($response);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化命令行
|
||||
* @param ?\think\App $app
|
||||
* @return integer
|
||||
*/
|
||||
public static function doConsoleInit(?App $app = null): int
|
||||
{
|
||||
try {
|
||||
return static::init($app)->console->run();
|
||||
} catch (\Exception $exception) {
|
||||
ProcessService::message($exception->getMessage());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一数组
|
||||
* @param array ...$args
|
||||
* @return array
|
||||
*/
|
||||
private static function uniqueMergeArray(...$args): array
|
||||
{
|
||||
return array_unique(array_reverse(array_merge(...$args)));
|
||||
}
|
||||
}
|
||||
411
plugin/think-library/src/service/SystemService.php
Normal file
411
plugin/think-library/src/service/SystemService.php
Normal file
@ -0,0 +1,411 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use think\admin\Exception;
|
||||
use think\admin\extend\FaviconExtend;
|
||||
use think\admin\Helper;
|
||||
use think\admin\Library;
|
||||
use think\admin\model\SystemConfig;
|
||||
use think\admin\model\SystemData;
|
||||
use think\admin\model\SystemOplog;
|
||||
use think\admin\Service;
|
||||
use think\admin\Storage;
|
||||
use think\admin\storage\LocalStorage;
|
||||
use think\App;
|
||||
use think\db\Query;
|
||||
use think\Model;
|
||||
|
||||
/**
|
||||
* 系统参数管理服务
|
||||
* @class SystemService
|
||||
* @package think\admin\service
|
||||
*
|
||||
* @method static bool isDebug() 调式模式运行
|
||||
* @method static bool isOnline() 产品模式运行
|
||||
*
|
||||
* 运行环境配置
|
||||
* @method static array getRuntime(?string $name = null, array $default = []) 获取动态配置
|
||||
* @method static bool setRuntime(?string $mode = null, ?array $appmap = [], ?array $domain = []) 设置动态配置
|
||||
* @method static bool bindRuntime(array $data = []) 绑定动态配置
|
||||
*
|
||||
* 运行缓存管理
|
||||
* @method static bool pushRuntime() 压缩发布项目
|
||||
* @method static bool clearRuntime() 清理运行缓存
|
||||
* @method static bool checkRunMode(string $type = 'dev') 判断运行环境
|
||||
*
|
||||
* 初始化启动系统
|
||||
* @method static mixed doInit(?App $app = null) 初始化主程序
|
||||
* @method static mixed doConsoleInit(?App $app = null) 初始化命令行
|
||||
*/
|
||||
class SystemService extends Service
|
||||
{
|
||||
/**
|
||||
* 生成静态路径链接
|
||||
* @param string $path 后缀路径
|
||||
* @param ?string $type 路径类型
|
||||
* @param mixed $default 默认数据
|
||||
* @return string|array
|
||||
*/
|
||||
public static function uri(string $path = '', ?string $type = '__ROOT__', $default = '')
|
||||
{
|
||||
$plugin = Library::$sapp->http->getName();
|
||||
if (strlen($path)) $path = '/' . ltrim($path, '/');
|
||||
$prefix = rtrim(dirname(Library::$sapp->request->basefile()), '\\/');
|
||||
$data = [
|
||||
'__APP__' => rtrim(url('@')->build(), '\\/') . $path,
|
||||
'__ROOT__' => $prefix . $path,
|
||||
'__PLUG__' => "{$prefix}/static/extra/{$plugin}{$path}",
|
||||
'__FULL__' => Library::$sapp->request->domain() . $prefix . $path
|
||||
];
|
||||
return is_null($type) ? $data : ($data[$type] ?? $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成全部静态路径
|
||||
* @param string $path
|
||||
* @return string[]
|
||||
*/
|
||||
public static function uris(string $path = ''): array
|
||||
{
|
||||
return static::uri($path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置数据
|
||||
* @param string $name 配置名称
|
||||
* @param mixed $value 配置内容
|
||||
* @return integer|string
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function set(string $name, $value = '')
|
||||
{
|
||||
[$type, $field] = static::_parse($name);
|
||||
if (is_array($value)) {
|
||||
$count = 0;
|
||||
foreach ($value as $kk => $vv) {
|
||||
$count += static::set("{$field}.{$kk}", $vv);
|
||||
}
|
||||
return $count;
|
||||
} else try {
|
||||
$map = ['type' => $type, 'name' => $field];
|
||||
SystemConfig::mk()->master()->where($map)->findOrEmpty()->save(array_merge($map, ['value' => $value]));
|
||||
sysvar('think.admin.config', []);
|
||||
Library::$sapp->cache->delete('SystemConfig');
|
||||
return 1;
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception($exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取配置数据
|
||||
* @param string $name
|
||||
* @param string $default
|
||||
* @return array|mixed|string
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function get(string $name = '', string $default = '')
|
||||
{
|
||||
try {
|
||||
if (empty($config = sysvar($keys = 'think.admin.config') ?: [])) {
|
||||
SystemConfig::mk()->cache('SystemConfig')->select()->map(function ($item) use (&$config) {
|
||||
$config[$item['type']][$item['name']] = $item['value'];
|
||||
});
|
||||
sysvar($keys, $config);
|
||||
}
|
||||
[$type, $field, $outer] = static::_parse($name);
|
||||
if (empty($name)) {
|
||||
return $config;
|
||||
} elseif (isset($config[$type])) {
|
||||
$group = $config[$type];
|
||||
if ($outer !== 'raw') foreach ($group as $kk => $vo) {
|
||||
$group[$kk] = htmlspecialchars(strval($vo));
|
||||
}
|
||||
return $field ? ($group[$field] ?? $default) : $group;
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception($exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据增量保存
|
||||
* @param Model|Query|string $query 数据查询对象
|
||||
* @param array $data 需要保存的数据,成功返回对应模型
|
||||
* @param string $key 更新条件查询主键
|
||||
* @param mixed $map 额外更新查询条件
|
||||
* @return boolean|integer 失败返回 false, 成功返回主键值或 true
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function save($query, array &$data, string $key = 'id', $map = [])
|
||||
{
|
||||
try {
|
||||
$query = Helper::buildQuery($query)->master()->strict(false);
|
||||
if (empty($map[$key])) $query->where([$key => $data[$key] ?? null]);
|
||||
$model = $query->where($map)->findOrEmpty();
|
||||
// 当前操作方法描述
|
||||
$action = $model->isExists() ? 'onAdminUpdate' : 'onAdminInsert';
|
||||
// 写入或更新模型数据
|
||||
if ($model->save($data) === false) return false;
|
||||
// 模型自定义事件回调
|
||||
if ($model instanceof \think\admin\Model) {
|
||||
$model->$action(strval($model->getAttr($key)));
|
||||
}
|
||||
$data = $model->toArray();
|
||||
return $model[$key] ?? true;
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception($exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析缓存名称
|
||||
* @param string $rule 配置名称
|
||||
* @return array
|
||||
*/
|
||||
private static function _parse(string $rule): array
|
||||
{
|
||||
$type = 'base';
|
||||
if (stripos($rule, '.') !== false) {
|
||||
[$type, $rule] = explode('.', $rule, 2);
|
||||
}
|
||||
[$field, $outer] = explode('|', "{$rule}|");
|
||||
return [$type, $field, strtolower($outer)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库所有数据表
|
||||
* @return array [table, total, count]
|
||||
*/
|
||||
public static function getTables(): array
|
||||
{
|
||||
$tables = Library::$sapp->db->getTables();
|
||||
return [$tables, count($tables), 0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制并创建表结构
|
||||
* @param string $from 来源表名
|
||||
* @param string $create 创建表名
|
||||
* @param array $tables 现有表集合
|
||||
* @param boolean $copy 是否复制
|
||||
* @param mixed $where 复制条件
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function copyTableStruct(string $from, string $create, array $tables = [], bool $copy = false, $where = [])
|
||||
{
|
||||
try {
|
||||
if (empty($tables)) [$tables] = static::getTables();
|
||||
if (!in_array($from, $tables)) {
|
||||
throw new Exception("待复制的数据表 {$from} 不存在!");
|
||||
}
|
||||
if (!in_array($create, $tables)) {
|
||||
Library::$sapp->db->connect()->query("CREATE TABLE IF NOT EXISTS {$create} (LIKE {$from})");
|
||||
if ($copy) {
|
||||
$sql1 = Library::$sapp->db->name($from)->where($where)->buildSql(false);
|
||||
Library::$sapp->db->connect()->query("INSERT INTO {$create} {$sql1}");
|
||||
}
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception($exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据内容
|
||||
* @param string $name 数据名称
|
||||
* @param mixed $value 数据内容
|
||||
* @return boolean
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function setData(string $name, $value): bool
|
||||
{
|
||||
try {
|
||||
$data = ['name' => $name, 'value' => json_encode([$value], 64 | 256)];
|
||||
return SystemData::mk()->where(['name' => $name])->findOrEmpty()->save($data);
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception($exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取数据内容
|
||||
* @param string $name 数据名称
|
||||
* @param mixed $default 默认内容
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getData(string $name, $default = [])
|
||||
{
|
||||
try {
|
||||
// 读取原始序列化或JSON数据
|
||||
$value = SystemData::mk()->where(['name' => $name])->value('value');
|
||||
if (is_null($value)) return $default;
|
||||
if (is_string($value) && strpos($value, '[') === 0) {
|
||||
return json_decode($value, true)[0];
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
trace_file($exception);
|
||||
return $default;
|
||||
}
|
||||
try {
|
||||
// 尝试正常反序列解析
|
||||
return unserialize($value);
|
||||
} catch (\Exception $exception) {
|
||||
trace_file($exception);
|
||||
}
|
||||
try {
|
||||
// 尝试修复反序列解析
|
||||
$unit = 'i:\d+;|b:[01];|s:\d+:".*?";|O:\d+:".*?":\d+:\{';
|
||||
$preg = '/(?=^|' . $unit . ')s:(\d+):"(.*?)";(?=' . $unit . '|}+$)/';
|
||||
return unserialize(preg_replace_callback($preg, static function ($attr) {
|
||||
return sprintf('s:%d:"%s";', strlen($attr[2]), $attr[2]);
|
||||
}, $value));
|
||||
} catch (\Exception $exception) {
|
||||
trace_file($exception);
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入系统日志内容
|
||||
* @param string $action
|
||||
* @param string $content
|
||||
* @return boolean
|
||||
*/
|
||||
public static function setOplog(string $action, string $content): bool
|
||||
{
|
||||
return SystemOplog::mk()->save(static::getOplog($action, $content)) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统日志内容
|
||||
* @param string $action
|
||||
* @param string $content
|
||||
* @return array
|
||||
*/
|
||||
public static function getOplog(string $action, string $content): array
|
||||
{
|
||||
return [
|
||||
'node' => NodeService::getCurrent(),
|
||||
'action' => lang($action), 'content' => lang($content),
|
||||
'geoip' => Library::$sapp->request->ip() ?: '127.0.0.1',
|
||||
'username' => AdminService::getUserName() ?: '-',
|
||||
'create_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印输出数据到文件
|
||||
* @param mixed $data 输出的数据
|
||||
* @param boolean $new 强制替换文件
|
||||
* @param string|null $file 文件名称
|
||||
* @return false|int
|
||||
*/
|
||||
public static function putDebug($data, bool $new = false, ?string $file = null)
|
||||
{
|
||||
ob_start();
|
||||
var_dump($data);
|
||||
$output = preg_replace('/]=>\n(\s+)/m', '] => ', ob_get_clean());
|
||||
if (is_null($file)) $file = syspath('runtime/' . date('Ymd') . '.log');
|
||||
else if (!preg_match('#[/\\\\]+#', $file)) $file = syspath("runtime/{$file}.log");
|
||||
is_dir($dir = dirname($file)) or mkdir($dir, 0777, true);
|
||||
return $new ? file_put_contents($file, $output) : file_put_contents($file, $output, FILE_APPEND);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置网页标签图标
|
||||
* @param ?string $icon 网页标签图标
|
||||
* @return boolean
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function setFavicon(?string $icon = null): bool
|
||||
{
|
||||
try {
|
||||
$icon = $icon ?: sysconf('base.site_icon|raw');
|
||||
if (!preg_match('#^https?://#i', $icon)) {
|
||||
throw new Exception(lang('无效的原文件地址!'));
|
||||
}
|
||||
if (preg_match('#/upload/(\w{2}/\w{30}.\w+)$#i', $icon, $vars)) {
|
||||
$info = LocalStorage::instance()->info($vars[1]);
|
||||
}
|
||||
if (empty($info) || empty($info['file'])) {
|
||||
$name = Storage::name($icon, 'tmp', 'icon');
|
||||
$info = LocalStorage::instance()->set($name, Storage::curlGet($icon), true);
|
||||
}
|
||||
if (empty($info) || empty($info['file'])) return false;
|
||||
$favicon = new FaviconExtend($info['file'], [48, 48]);
|
||||
return $favicon->saveIco(syspath('public/favicon.ico'));
|
||||
} catch (Exception $exception) {
|
||||
throw $exception;
|
||||
} catch (\Exception $exception) {
|
||||
trace_file($exception);
|
||||
throw new Exception($exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 魔术方法调用(临时)
|
||||
* @param string $method 方法名称
|
||||
* @param array $arguments 调用参数
|
||||
* @return mixed
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function __call(string $method, array $arguments)
|
||||
{
|
||||
return static::__callStatic($method, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态方法兼容(临时)
|
||||
* @param string $method 方法名称
|
||||
* @param array $arguments 调用参数
|
||||
* @return mixed
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public static function __callStatic(string $method, array $arguments)
|
||||
{
|
||||
$map = [
|
||||
'setRuntime' => 'set',
|
||||
'getRuntime' => 'get',
|
||||
'bindRuntime' => 'apply',
|
||||
'isDebug' => 'isDebug',
|
||||
'isOnline' => 'isOnline',
|
||||
'doInit' => 'doWebsiteInit',
|
||||
'doConsoleInit' => 'doConsoleInit',
|
||||
'pushRuntime' => 'push',
|
||||
'clearRuntime' => 'clear',
|
||||
'checkRunMode' => 'check',
|
||||
];
|
||||
switch (strtolower($method)) {
|
||||
case 'setconfig':
|
||||
return self::setData(...$arguments);
|
||||
case 'getconfig':
|
||||
return self::getData(...$arguments);
|
||||
}
|
||||
if (isset($map[$method])) {
|
||||
return RuntimeService::{$map[$method]}(...$arguments);
|
||||
} else {
|
||||
throw new Exception("method not exists: RuntimeService::{$method}()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
296
plugin/think-library/src/service/ZtSmsService.php
Normal file
296
plugin/think-library/src/service/ZtSmsService.php
Normal file
@ -0,0 +1,296 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\service;
|
||||
|
||||
use think\admin\extend\HttpExtend;
|
||||
use think\admin\Service;
|
||||
|
||||
/**
|
||||
* 新助通短信接口服务
|
||||
* @class ZtSmsService
|
||||
* @deprecated 独立封装为插件
|
||||
* @package think\admin\service
|
||||
*/
|
||||
class ZtSmsService extends Service
|
||||
{
|
||||
/**
|
||||
* 接口地址
|
||||
* @var string
|
||||
*/
|
||||
private $api = 'https://api.mix2.zthysms.com';
|
||||
|
||||
/**
|
||||
* 子账号名称
|
||||
* @var string
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* 子账号密码
|
||||
* @var string
|
||||
*/
|
||||
protected $password;
|
||||
|
||||
/**
|
||||
* 短信服务初始化
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function initialize()
|
||||
{
|
||||
$this->username = sysconf('ztsms.username|raw') ?: '';
|
||||
$this->password = sysconf('ztsms.password|raw') ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 短信服务初始化
|
||||
* @param string $username 账号名称
|
||||
* @param string $password 账号密码
|
||||
* @return static
|
||||
*/
|
||||
public function setAuth(string $username, string $password): ZtSmsService
|
||||
{
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证手机短信验证码
|
||||
* @param string $code 验证码
|
||||
* @param string $phone 手机号验证
|
||||
* @param string $template 模板编码
|
||||
* @return boolean
|
||||
*/
|
||||
public function checkVerifyCode(string $code, string $phone, string $template = 'ztsms.register_verify'): bool
|
||||
{
|
||||
$cache = $this->app->cache->get($ckey = md5("code-{$template}-{$phone}"), []);
|
||||
if (is_array($cache) && isset($cache['code']) && $cache['code'] == $code) {
|
||||
$this->app->cache->delete($ckey);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证手机短信验证码
|
||||
* @param string $phone 手机号码
|
||||
* @param integer $wait 等待时间
|
||||
* @param string $template 模板编码
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function sendVerifyCode(string $phone, int $wait = 120, string $template = 'ztsms.register_verify'): array
|
||||
{
|
||||
$time = time();
|
||||
// 检查是否已经发送
|
||||
$cache = $this->app->cache->get($ckey = md5("code-{$template}-{$phone}"), []);
|
||||
if (is_array($cache) && isset($cache['time']) && $cache['time'] + $wait > $time) {
|
||||
$dtime = $cache['time'] + $wait < $time ? 0 : $cache['time'] + $wait - $time;
|
||||
return [1, lang('短信验证码已经发送!'), ['time' => $dtime]];
|
||||
}
|
||||
// 生成新的验证码
|
||||
$code = (string)rand(100000, 999999);
|
||||
$this->app->cache->set($ckey, ['code' => $code, 'time' => $time], 600);
|
||||
// 尝试发送短信内容
|
||||
$content = sysconf("{$template}|raw") ?: lang('您的验证码为{code},请在十分钟内完成操作!');
|
||||
[$state] = $this->timeSend($phone, str_replace('{code}', $code, $content));
|
||||
if ($state) {
|
||||
return [1, lang('短信验证码发送成功!'), ['time' => $wait]];
|
||||
} else {
|
||||
$this->app->cache->delete($ckey);
|
||||
return [0, lang('短信发送失败,请稍候再试!'), ['time' => 0]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建短信签名
|
||||
* @param array $signs 签名列表
|
||||
* @param string $remark 签名备注
|
||||
* @return array
|
||||
*/
|
||||
public function signAdd(array $signs = [], string $remark = ''): array
|
||||
{
|
||||
foreach ($signs as $key => $name) $signs[$key] = $this->_singName($name);
|
||||
return $this->doRequest('/sms/v1/sign', ['sign' => $signs, 'remark' => $remark]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询短信签名
|
||||
* @param string $name 短信签名
|
||||
* @return array
|
||||
*/
|
||||
public function signGet(string $name): array
|
||||
{
|
||||
return $this->doRequest('/sms/v1/sign/query', [
|
||||
'sign' => $this->_singName($name),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 报备短信模板
|
||||
* @param string $name 模板名称
|
||||
* @param integer $type 模板类型(1验证码, 2行业通知, 3营销推广)
|
||||
* @param string $content 模板内容
|
||||
* @param array $params 模板变量
|
||||
* @param string $remark 模板备注
|
||||
* @return array
|
||||
*/
|
||||
public function tplAdd(string $name, int $type, string $content, array $params = [], string $remark = ''): array
|
||||
{
|
||||
return $this->doRequest('/sms/v2/template', [
|
||||
'temName' => $name, 'temType' => $type, 'temContent' => $content, 'paramJson' => $params, 'remark' => $remark,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询模板状态
|
||||
* @param string $temId 短信模板
|
||||
* @return array
|
||||
*/
|
||||
public function tplGet(string $temId): array
|
||||
{
|
||||
return $this->doRequest('/sms/v2/template/query', ['temId' => $temId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送模板短信
|
||||
* @param string $tpId 短信模板
|
||||
* @param string $sign 短信签名
|
||||
* @param array $records 发送记录
|
||||
* @return array
|
||||
*/
|
||||
public function tplSend(string $tpId, string $sign, array $records): array
|
||||
{
|
||||
return $this->doRequest('/v2/sendSmsTp', [
|
||||
'tpId' => $tpId, 'records' => $records, 'signature' => $this->_singName($sign),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送定时短信
|
||||
* @param string $mobile 发送手机号码
|
||||
* @param string $content 发送短信内容
|
||||
* @param integer $time 定时发送时间(为 0 立即发送)
|
||||
* @return array
|
||||
*/
|
||||
public function timeSend(string $mobile, string $content, int $time = 0): array
|
||||
{
|
||||
$data = ['mobile' => $mobile, 'content' => $content];
|
||||
if ($time > 0) $data['time'] = $time;
|
||||
return $this->doRequest('/v2/sendSms', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量发送短信
|
||||
* @param array $records
|
||||
* @return array
|
||||
*/
|
||||
public function batchSend(array $records): array
|
||||
{
|
||||
return $this->doRequest('/v2/sendSmsPa', ['records' => $records]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 短信条数查询
|
||||
*/
|
||||
public function balance(): array
|
||||
{
|
||||
[$state, $result, $message] = $this->doRequest('/v2/balance', []);
|
||||
return [$state, $state ? $result['sumSms'] : 0, $message];
|
||||
}
|
||||
|
||||
/**
|
||||
* 短信签名内容处理
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
private function _singName(string $name): string
|
||||
{
|
||||
if (strpos($name, '】') === false) $name = $name . '】';
|
||||
if (strpos($name, '【') === false) $name = '【' . $name;
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行网络请求
|
||||
* @param string $uri 接口请求地址
|
||||
* @param array $data 接口请求参数
|
||||
* @return array
|
||||
*/
|
||||
private function doRequest(string $uri, array $data): array
|
||||
{
|
||||
$encode = md5(md5($this->password) . ($tkey = time()));
|
||||
$options = ['headers' => ['Content-Type:application/json;charset="UTF-8"']];
|
||||
$extends = ['username' => $this->username, 'password' => $encode, 'tKey' => $tkey];
|
||||
$result = json_decode(HttpExtend::post($this->api . $uri, json_encode(array_merge($data, $extends)), $options), true);
|
||||
if (empty($result['code'])) {
|
||||
return [0, [], lang('接口请求网络异常')];
|
||||
} elseif (intval($result['code']) === 200) {
|
||||
return [1, $result, lang($this->error($result['code']))];
|
||||
} else {
|
||||
return [0, $result, lang($this->error($result['code']))];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态描述
|
||||
* @param integer $code 异常编号
|
||||
* @return string
|
||||
*/
|
||||
private function error(int $code): string
|
||||
{
|
||||
$arrs = [
|
||||
200 => '提交成功',
|
||||
4001 => '用户名错误',
|
||||
4002 => '密码不能为空',
|
||||
4003 => '短信内容不能为空',
|
||||
4004 => '手机号码错误',
|
||||
4006 => 'IP鉴权错误',
|
||||
4007 => '用户禁用',
|
||||
4008 => 'tKey错误',
|
||||
4009 => '密码错误',
|
||||
4011 => '请求错误',
|
||||
4013 => '定时时间错误',
|
||||
4014 => '模板错误',
|
||||
4015 => '扩展号错误',
|
||||
4019 => '用户类型错误',
|
||||
4023 => '签名错误',
|
||||
4025 => '模板变量内容为空',
|
||||
4026 => '手机号码数最大2000个',
|
||||
4027 => '模板变量内容最大200组',
|
||||
4029 => '请使用 POST 请求',
|
||||
4030 => 'Content-Type 请使用 application/json',
|
||||
4031 => '模板名称不能为空',
|
||||
4032 => '模板类型不正确',
|
||||
4034 => '模板内容不能为空',
|
||||
4035 => '模板名称已经存在',
|
||||
4036 => '添加模板信息失败',
|
||||
4037 => '模板名称最大20字符',
|
||||
4038 => '模板内容超过最大字符数',
|
||||
4040 => '模板内容缺少变量值或规则错误',
|
||||
4041 => '模板内容中变量规范错误',
|
||||
4042 => '模板变量个数超限',
|
||||
4044 => '接口24小时限制提交次数超限',
|
||||
9998 => 'JSON解析错误',
|
||||
9999 => '非法请求',
|
||||
];
|
||||
return $arrs[$code] ?? "{$code}";
|
||||
}
|
||||
}
|
||||
BIN
plugin/think-library/src/service/bin/captcha.ttf
Normal file
BIN
plugin/think-library/src/service/bin/captcha.ttf
Normal file
Binary file not shown.
BIN
plugin/think-library/src/service/bin/console.exe
Normal file
BIN
plugin/think-library/src/service/bin/console.exe
Normal file
Binary file not shown.
110
plugin/think-library/src/service/bin/package.stub
Normal file
110
plugin/think-library/src/service/bin/package.stub
Normal file
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
use think\admin\extend\PhinxExtend;
|
||||
use think\admin\model\SystemConfig;
|
||||
use think\admin\model\SystemMenu;
|
||||
use think\admin\model\SystemUser;
|
||||
use think\admin\service\ProcessService;
|
||||
use think\helper\Str;
|
||||
use think\migration\Migrator;
|
||||
|
||||
@set_time_limit(0);
|
||||
@ini_set('memory_limit', -1);
|
||||
|
||||
/**
|
||||
* 数据安装包
|
||||
* @class __CLASS__
|
||||
*/
|
||||
class __CLASS__ extends Migrator
|
||||
{
|
||||
/**
|
||||
* 数据库初始化
|
||||
* @return void
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public function change()
|
||||
{
|
||||
$this->inserData();
|
||||
$this->insertConf();
|
||||
$this->insertUser();
|
||||
$this->insertMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装扩展数据
|
||||
* @return void
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
private function inserData()
|
||||
{
|
||||
// 待解析处理数据
|
||||
$json = '__DATA_JSON__';
|
||||
// 解析并写入扩展数据
|
||||
if (is_array($tables = json_decode($json, true)) && count($tables) > 0) {
|
||||
foreach ($tables as $table => $path) if (($model = m($table))->count() < 1) {
|
||||
$name = Str::studly($table);
|
||||
ProcessService::message(" -- Starting write {$table} table ..." . PHP_EOL);
|
||||
[$ls, $rs, $fp] = [0, [], fopen(__DIR__ . DIRECTORY_SEPARATOR . $path, 'r+')];
|
||||
while (!feof($fp)) {
|
||||
if (empty($text = trim(fgets($fp)))) continue; else $ls++;
|
||||
if (is_array($rw = json_decode($text, true))) $rs[] = $rw;
|
||||
if (count($rs) > 100) [, $rs] = [$model->strict(false)->insertAll($rs), []];
|
||||
ProcessService::message(" -- -- {$name}:{$ls}", 1);
|
||||
}
|
||||
count($rs) > 0 && $model->strict(false)->insertAll($rs);
|
||||
ProcessService::message(" -- Finished write {$table} table, Total {$ls} rows.", 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化配置参数
|
||||
* @return void
|
||||
*/
|
||||
private function insertConf()
|
||||
{
|
||||
$modal = SystemConfig::mk()->whereRaw('1=1')->findOrEmpty();
|
||||
$modal->isEmpty() && $modal->insertAll([
|
||||
['type' => 'base', 'name' => 'app_name', 'value' => 'ThinkAdmin'],
|
||||
['type' => 'base', 'name' => 'app_version', 'value' => 'v6'],
|
||||
['type' => 'base', 'name' => 'editor', 'value' => 'ckeditor5'],
|
||||
['type' => 'base', 'name' => 'login_name', 'value' => '系统管理'],
|
||||
['type' => 'base', 'name' => 'site_copy', 'value' => '©版权所有 2014-' . date('Y') . ' ThinkAdmin'],
|
||||
['type' => 'base', 'name' => 'site_icon', 'value' => 'https://thinkadmin.top/static/img/logo.png'],
|
||||
['type' => 'base', 'name' => 'site_name', 'value' => 'ThinkAdmin'],
|
||||
['type' => 'base', 'name' => 'site_theme', 'value' => 'default'],
|
||||
['type' => 'wechat', 'name' => 'type', 'value' => 'api'],
|
||||
['type' => 'storage', 'name' => 'type', 'value' => 'local'],
|
||||
['type' => 'storage', 'name' => 'allow_exts', 'value' => 'doc,gif,ico,jpg,mp3,mp4,p12,pem,png,zip,rar,xls,xlsx'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化用户数据
|
||||
* @return void
|
||||
*/
|
||||
private function insertUser()
|
||||
{
|
||||
$modal = SystemUser::mk()->whereRaw('1=1')->findOrEmpty();
|
||||
$modal->isEmpty() && $modal->insert([
|
||||
'id' => '10000',
|
||||
'username' => 'admin',
|
||||
'nickname' => '超级管理员',
|
||||
'password' => '21232f297a57a5a743894a0e4a801fc3',
|
||||
'headimg' => 'https://thinkadmin.top/static/img/head.png',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化系统菜单
|
||||
* @return void
|
||||
*/
|
||||
private function insertMenu()
|
||||
{
|
||||
if (SystemMenu::mk()->whereRaw('1=1')->findOrEmpty()->isEmpty()) {
|
||||
// 解析并初始化菜单数据
|
||||
$json = '__MENU_JSON__';
|
||||
PhinxExtend::write2menu(json_decode($json, true) ?: []);
|
||||
}
|
||||
}
|
||||
}
|
||||
286
plugin/think-library/src/storage/AliossStorage.php
Normal file
286
plugin/think-library/src/storage/AliossStorage.php
Normal file
@ -0,0 +1,286 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\storage;
|
||||
|
||||
use think\admin\contract\StorageInterface;
|
||||
use think\admin\contract\StorageUsageTrait;
|
||||
use think\admin\Exception;
|
||||
use think\admin\extend\HttpExtend;
|
||||
use think\admin\Storage;
|
||||
|
||||
/**
|
||||
* 阿里云OSS存储支持
|
||||
* @class AliossStorage
|
||||
* @package think\admin\storage
|
||||
*/
|
||||
class AliossStorage implements StorageInterface
|
||||
{
|
||||
use StorageUsageTrait;
|
||||
|
||||
/**
|
||||
* 数据中心
|
||||
* @var string
|
||||
*/
|
||||
private $point;
|
||||
|
||||
/**
|
||||
* 存储空间名称
|
||||
* @var string
|
||||
*/
|
||||
private $bucket;
|
||||
|
||||
/**
|
||||
* AccessId
|
||||
* @var string
|
||||
*/
|
||||
private $accessKey;
|
||||
|
||||
/**
|
||||
* AccessSecret
|
||||
* @var string
|
||||
*/
|
||||
private $secretKey;
|
||||
|
||||
/**
|
||||
* 初始化入口
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
// 读取配置文件
|
||||
$this->point = sysconf('storage.alioss_point|raw');
|
||||
$this->bucket = sysconf('storage.alioss_bucket|raw');
|
||||
$this->accessKey = sysconf('storage.alioss_access_key|raw');
|
||||
$this->secretKey = sysconf('storage.alioss_secret_key|raw');
|
||||
// 计算链接前缀
|
||||
$host = strtolower(sysconf('storage.alioss_http_domain|raw'));
|
||||
$type = strtolower(sysconf('storage.alioss_http_protocol|raw'));
|
||||
if ($type === 'auto') {
|
||||
$this->domain = "//{$host}";
|
||||
} elseif (in_array($type, ['http', 'https'])) {
|
||||
$this->domain = "{$type}://{$host}";
|
||||
} else {
|
||||
throw new Exception(lang('未配置阿里云域名'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件内容
|
||||
* @param string $name 文件名称
|
||||
* @param string $file 文件内容
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
*/
|
||||
public function set(string $name, string $file, bool $safe = false, ?string $attname = null): array
|
||||
{
|
||||
$token = $this->token($name);
|
||||
$data = ['key' => $name];
|
||||
$data['policy'] = $token['policy'];
|
||||
$data['Signature'] = $token['signature'];
|
||||
$data['OSSAccessKeyId'] = $this->accessKey;
|
||||
$data['success_action_status'] = '200';
|
||||
if (is_string($attname) && strlen($attname) > 0) {
|
||||
$data['Content-Disposition'] = 'inline;filename=' . urlencode($attname);
|
||||
}
|
||||
$file = ['field' => 'file', 'name' => $name, 'content' => $file];
|
||||
if (is_numeric(stripos(HttpExtend::submit($this->upload(), $data, $file), '200 OK'))) {
|
||||
return ['file' => $this->path($name, $safe), 'url' => $this->url($name, $safe, $attname), 'key' => $name];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件内容
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return string
|
||||
*/
|
||||
public function get(string $name, bool $safe = false): string
|
||||
{
|
||||
return Storage::curlGet($this->url($name, $safe));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除存储文件
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return boolean
|
||||
*/
|
||||
public function del(string $name, bool $safe = false): bool
|
||||
{
|
||||
[$file] = explode('?', $name);
|
||||
$result = HttpExtend::request('DELETE', "https://{$this->bucket}.{$this->point}/{$file}", [
|
||||
'returnHeader' => true, 'headers' => $this->_sign('DELETE', $file),
|
||||
]);
|
||||
return is_numeric(stripos($result, '204 No Content'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否存在
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return boolean
|
||||
*/
|
||||
public function has(string $name, bool $safe = false): bool
|
||||
{
|
||||
$file = $this->delSuffix($name);
|
||||
$result = HttpExtend::request('HEAD', "https://{$this->bucket}.{$this->point}/{$file}", [
|
||||
'returnHeader' => true, 'headers' => $this->_sign('HEAD', $file),
|
||||
]);
|
||||
return is_numeric(stripos($result, 'HTTP/1.1 200 OK'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取访问地址
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return string
|
||||
*/
|
||||
public function url(string $name, bool $safe = false, ?string $attname = null): string
|
||||
{
|
||||
return "{$this->domain}/{$this->delSuffix($name)}{$this->getSuffix($attname,$name)}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储路径
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return string
|
||||
*/
|
||||
public function path(string $name, bool $safe = false): string
|
||||
{
|
||||
return $this->url($name, $safe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件信息
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
*/
|
||||
public function info(string $name, bool $safe = false, ?string $attname = null): array
|
||||
{
|
||||
return $this->has($name, $safe) ? [
|
||||
'url' => $this->url($name, $safe, $attname),
|
||||
'key' => $name, 'file' => $this->path($name, $safe),
|
||||
] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传地址
|
||||
* @return string
|
||||
*/
|
||||
public function upload(): string
|
||||
{
|
||||
$proc = $this->app->request->isSsl() ? 'https' : 'http';
|
||||
return "{$proc}://{$this->bucket}.{$this->point}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传令牌
|
||||
* @param string $name 文件名称
|
||||
* @param integer $expires 有效时间
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
*/
|
||||
public function token(string $name, int $expires = 3600, ?string $attname = null): array
|
||||
{
|
||||
$data = [
|
||||
'policy' => base64_encode(json_encode([
|
||||
'conditions' => [['content-length-range', 0, 1048576000]],
|
||||
'expiration' => date('Y-m-d\TH:i:s.000\Z', time() + $expires),
|
||||
])),
|
||||
'keyid' => $this->accessKey,
|
||||
'siteurl' => $this->url($name, false, $attname),
|
||||
];
|
||||
$data['signature'] = base64_encode(hash_hmac('sha1', $data['policy'], $this->secretKey, true));
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求数据签名
|
||||
* @param string $method 请求方式
|
||||
* @param string $soruce 资源名称
|
||||
* @return array
|
||||
*/
|
||||
private function _sign(string $method, string $soruce): array
|
||||
{
|
||||
$header = [];
|
||||
$header['Date'] = gmdate('D, d M Y H:i:s \G\M\T');
|
||||
$header['Content-Type'] = 'application/xml';
|
||||
uksort($header, 'strnatcasecmp');
|
||||
$content = "{$method}\n\n";
|
||||
foreach ($header as $key => $value) {
|
||||
$value = str_replace(["\r", "\n"], '', $value);
|
||||
if (in_array(strtolower($key), ['content-md5', 'content-type', 'date'])) {
|
||||
$content .= "{$value}\n";
|
||||
} elseif (stripos($key, 'x-oss-') === 0) {
|
||||
$content .= strtolower($key) . ":{$value}\n";
|
||||
}
|
||||
}
|
||||
$content = rawurldecode($content) . "/{$this->bucket}/{$soruce}";
|
||||
$signature = base64_encode(hash_hmac('sha1', $content, $this->secretKey, true));
|
||||
$header['Authorization'] = "OSS {$this->accessKey}:{$signature}";
|
||||
foreach ($header as $key => $value) $header[$key] = "{$key}: {$value}";
|
||||
return array_values($header);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储区域
|
||||
* @return array
|
||||
*/
|
||||
public static function region(): array
|
||||
{
|
||||
return [
|
||||
'oss-cn-hangzhou.aliyuncs.com' => lang('华东 1(杭州)'),
|
||||
'oss-cn-shanghai.aliyuncs.com' => lang('华东 2(上海)'),
|
||||
'oss-cn-nanjing.aliyuncs.com' => lang('华东 5(南京本地地域)'),
|
||||
'oss-cn-fuzhou.aliyuncs.com' => lang('华东 6(福州本地地域)'),
|
||||
'oss-cn-qingdao.aliyuncs.com' => lang('华北 1(青岛)'),
|
||||
'oss-cn-beijing.aliyuncs.com' => lang('华北 2(北京)'),
|
||||
'oss-cn-zhangjiakou.aliyuncs.com' => lang('华北 3(张家口)'),
|
||||
'oss-cn-huhehaote.aliyuncs.com' => lang('华北 5(呼和浩特)'),
|
||||
'oss-cn-wulanchabu.aliyuncs.com' => lang('华北 6(乌兰察布)'),
|
||||
'oss-cn-shenzhen.aliyuncs.com' => lang('华南 1(深圳)'),
|
||||
'oss-cn-heyuan.aliyuncs.com' => lang('华南 2(河源)'),
|
||||
'oss-cn-guangzhou.aliyuncs.com' => lang('华南 3(广州)'),
|
||||
'oss-cn-chengdu.aliyuncs.com' => lang('西南 1(成都)'),
|
||||
'oss-cn-hongkong.aliyuncs.com' => lang('中国(香港)'),
|
||||
'oss-us-west-1.aliyuncs.com' => lang('美国(硅谷)'),
|
||||
'oss-us-east-1.aliyuncs.com' => lang('美国(弗吉尼亚)'),
|
||||
'oss-ap-northeast-1.aliyuncs.com' => lang('日本(东京)'),
|
||||
'oss-ap-northeast-2.aliyuncs.com' => lang('韩国(首尔)'),
|
||||
'oss-ap-southeast-1.aliyuncs.com' => lang('新加坡'),
|
||||
'oss-ap-southeast-2.aliyuncs.com' => lang('澳大利亚(悉尼)'),
|
||||
'oss-ap-southeast-3.aliyuncs.com' => lang('马来西亚(吉隆坡)'),
|
||||
'oss-ap-southeast-5.aliyuncs.com' => lang('印度尼西亚(雅加达)'),
|
||||
'oss-ap-southeast-6.aliyuncs.com' => lang('菲律宾(马尼拉)'),
|
||||
'oss-ap-southeast-7.aliyuncs.com' => lang('泰国(曼谷)'),
|
||||
'oss-ap-south-1.aliyuncs.com' => lang('印度(孟买)'),
|
||||
'oss-eu-central-1.aliyuncs.com' => lang('德国(法兰克福)'),
|
||||
'oss-eu-west-1.aliyuncs.com' => lang('英国(伦敦)'),
|
||||
'oss-me-east-1.aliyuncs.com' => lang('阿联酋(迪拜)'),
|
||||
'oss-rg-china-mainland.aliyuncs.com' => lang('无地域属性(中国内地)')
|
||||
];
|
||||
}
|
||||
}
|
||||
322
plugin/think-library/src/storage/AlistStorage.php
Normal file
322
plugin/think-library/src/storage/AlistStorage.php
Normal file
@ -0,0 +1,322 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\storage;
|
||||
|
||||
use think\admin\contract\StorageInterface;
|
||||
use think\admin\contract\StorageUsageTrait;
|
||||
use think\admin\Exception;
|
||||
use think\admin\extend\HttpExtend;
|
||||
use think\admin\Storage;
|
||||
|
||||
/**
|
||||
* Alist 自建存储支持
|
||||
* @class AlistStorage
|
||||
* @package think\admin\storage
|
||||
*/
|
||||
class AlistStorage implements StorageInterface
|
||||
{
|
||||
use StorageUsageTrait;
|
||||
|
||||
/**
|
||||
* 用户账号
|
||||
* @var string
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* 用户密码
|
||||
* @var string
|
||||
*/
|
||||
protected $password;
|
||||
|
||||
/**
|
||||
* 保存路径
|
||||
* @var string
|
||||
*/
|
||||
protected $savepath;
|
||||
|
||||
/**
|
||||
* 缓存前缀
|
||||
* @var string
|
||||
*/
|
||||
protected $cachekey;
|
||||
|
||||
/**
|
||||
* 存储引擎初始化
|
||||
* @return void
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
$host = strtolower(sysconf('storage.alist_http_domain|raw'));
|
||||
$type = strtolower(sysconf('storage.alist_http_protocol|raw'));
|
||||
if (!empty($host) && $type === 'auto') {
|
||||
$this->domain = "//{$host}";
|
||||
} elseif (!empty($host) && in_array($type, ['http', 'https'])) {
|
||||
$this->domain = "{$type}://{$host}";
|
||||
} else {
|
||||
throw new Exception(lang('未配置Alist域名'));
|
||||
}
|
||||
$this->username = sysconf('storage.alist_username|raw') ?: '';
|
||||
$this->password = sysconf('storage.alist_password|raw') ?: '';
|
||||
$this->savepath = trim(sysconf('storage.alist_savepath|raw') ?: '', '\\/');
|
||||
$this->savepath = $this->savepath ? "{$this->savepath}/" : '';
|
||||
$this->cachekey = md5($this->domain . $this->username . $this->password);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件内容
|
||||
* @param string $name 文件名称
|
||||
* @param string $file 文件内容
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function set(string $name, string $file, bool $safe = false, ?string $attname = null): array
|
||||
{
|
||||
$file = ['field' => 'file', 'name' => $name, 'content' => $file];
|
||||
$header = ["Authorization: {$this->token()}", "file-path:" . urlencode($this->real($name))];
|
||||
$result = HttpExtend::submit("{$this->domain}/api/fs/form", [], $file, $header, 'PUT', false);
|
||||
if (is_array($data = json_decode($result, true))) {
|
||||
if ($data['code'] === 200 && $data['message'] === 'success') {
|
||||
return $this->info($name, $safe);
|
||||
} else {
|
||||
throw new Exception($data['message'] ?? '接口请求失败!', intval($data['code'] ?? 0));
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件内容
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return string
|
||||
*/
|
||||
public function get(string $name, bool $safe = false): string
|
||||
{
|
||||
return Storage::curlGet($this->url($name, $safe));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除存储文件
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return boolean
|
||||
*/
|
||||
public function del(string $name, bool $safe = false): bool
|
||||
{
|
||||
try {
|
||||
$path = $this->real($this->delSuffix($name));
|
||||
$data = ['dir' => dirname($path) ?: '/', 'names' => [basename($path)]];
|
||||
$this->httpPost('/api/fs/remove', $data);
|
||||
return true;
|
||||
} catch (\Exception $exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否存在
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return boolean
|
||||
*/
|
||||
public function has(string $name, bool $safe = false): bool
|
||||
{
|
||||
try {
|
||||
$this->httpPost('/api/fs/get', [
|
||||
'path' => $this->real($name)
|
||||
]);
|
||||
return true;
|
||||
} catch (\Exception $exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件下载链接
|
||||
* @param string $name
|
||||
* @param bool $safe
|
||||
* @param string|null $attname
|
||||
* @return string
|
||||
*/
|
||||
public function url(string $name, bool $safe = false, ?string $attname = null): string
|
||||
{
|
||||
$path = rtrim($this->userPath(), '\\/') . $this->real($name);
|
||||
return "{$this->domain}/d{$this->delSuffix($path)}{$this->getSuffix($attname,$path)}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储路径
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return string
|
||||
*/
|
||||
public function path(string $name, bool $safe = false): string
|
||||
{
|
||||
return $this->url($name, $safe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件信息
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
*/
|
||||
public function info(string $name, bool $safe = false, ?string $attname = null): array
|
||||
{
|
||||
return $this->has($name, $safe) ? [
|
||||
'key' => $name,
|
||||
'url' => $this->url($name, $safe, $attname),
|
||||
'file' => $this->path($name, $safe),
|
||||
] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传地址
|
||||
* @return string
|
||||
*/
|
||||
public function upload(): string
|
||||
{
|
||||
return "{$this->domain}/api/fs/form";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储区域
|
||||
* @return array
|
||||
*/
|
||||
public static function region(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建目录
|
||||
* @param string $path
|
||||
* @return boolean
|
||||
*/
|
||||
protected function mkdir(string $path): bool
|
||||
{
|
||||
try {
|
||||
$this->httpPost('/api/fs/mkdir', [
|
||||
'path' => $this->real($path)
|
||||
]);
|
||||
return true;
|
||||
} catch (\Exception $exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为绝对路径
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public function real(string $path): string
|
||||
{
|
||||
return "/{$this->savepath}" . trim($path, '\\/');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户 Token 信息
|
||||
* @param boolean $force
|
||||
* @return string
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function token(bool $force = false): string
|
||||
{
|
||||
try {
|
||||
$skey = "{$this->cachekey}.token";
|
||||
if (empty($force) && ($token = $this->app->cache->get($skey))) return $token;
|
||||
$data = ['Password' => $this->password, 'Username' => $this->username];
|
||||
$body = $this->httpPost("/api/auth/login", $data, false);
|
||||
if (!empty($body['data']['token'])) {
|
||||
$this->app->cache->set($skey, $body['data']['token'], 60);
|
||||
return $body['data']['token'];
|
||||
} else {
|
||||
throw new Exception('获取用户 Token 失败!');
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception($exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取基础路径
|
||||
* @return string
|
||||
*/
|
||||
private function userPath(): string
|
||||
{
|
||||
try {
|
||||
$skey = "{$this->cachekey}.path";
|
||||
if ($path = $this->app->cache->get($skey)) return $path;
|
||||
$data = $this->httGet('/api/me');
|
||||
if (empty($data['data']['base_path'])) return '/';
|
||||
$path = trim($data['data']['base_path'], '\\/');
|
||||
$this->app->cache->set($skey, $path = $path ? "/{$path}/" : '/', 60);
|
||||
return $path;
|
||||
} catch (\Exception $exception) {
|
||||
return "/{$this->savepath}";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 提交数据
|
||||
* @param string $uri
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
private function httGet(string $uri): array
|
||||
{
|
||||
$header = ["Authorization: {$this->token()}"];
|
||||
$header[] = "Content-Type: application/json;charset=UTF-8";
|
||||
$result = HttpExtend::get($this->domain . $uri, [], ['headers' => $header]);
|
||||
if (is_array($data = json_decode($result, true))) {
|
||||
if ($data['code'] === 200 && $data['message'] === 'success') return $data;
|
||||
throw new Exception($data['message'] ?? '接口请求失败!', intval($data['code'] ?? 0));
|
||||
} else {
|
||||
throw new Exception('接口请求失败!');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST 提交数据
|
||||
* @param string $uri
|
||||
* @param array $body
|
||||
* @param boolean $auth
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
private function httpPost(string $uri, array $body = [], bool $auth = true): array
|
||||
{
|
||||
$body = json_encode($body, JSON_UNESCAPED_UNICODE);
|
||||
$header = $auth ? ["Authorization: {$this->token()}"] : [];
|
||||
$header[] = "Content-Type: application/json;charset=UTF-8";
|
||||
$result = HttpExtend::post($this->domain . $uri, $body, ['headers' => $header]);
|
||||
if (is_array($data = json_decode($result, true))) {
|
||||
if ($data['code'] === 200 && $data['message'] === 'success') return $data;
|
||||
throw new Exception($data['message'] ?? '接口请求失败!', intval($data['code'] ?? 0));
|
||||
} else {
|
||||
throw new Exception('接口请求失败!');
|
||||
}
|
||||
}
|
||||
}
|
||||
167
plugin/think-library/src/storage/LocalStorage.php
Normal file
167
plugin/think-library/src/storage/LocalStorage.php
Normal file
@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\storage;
|
||||
|
||||
use think\admin\contract\StorageInterface;
|
||||
use think\admin\contract\StorageUsageTrait;
|
||||
|
||||
/**
|
||||
* 本地存储支持
|
||||
* @class LocalStorage
|
||||
* @package think\admin\storage
|
||||
*/
|
||||
class LocalStorage implements StorageInterface
|
||||
{
|
||||
use StorageUsageTrait;
|
||||
|
||||
/**
|
||||
* 初始化入口
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
$type = sysconf('storage.local_http_protocol|raw') ?: 'follow';
|
||||
if ($type === 'follow') $type = $this->app->request->scheme();
|
||||
$this->domain = trim(dirname($this->app->request->baseFile()), '\\/');
|
||||
if ($type !== 'path') {
|
||||
$domain = sysconf('storage.local_http_domain|raw') ?: $this->app->request->host();
|
||||
if ($type === 'auto') {
|
||||
$this->domain = "//{$domain}";
|
||||
} elseif (in_array($type, ['http', 'https'])) {
|
||||
$this->domain = "{$type}://{$domain}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件内容
|
||||
* @param string $name 文件名称
|
||||
* @param string $file 文件内容
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
*/
|
||||
public function set(string $name, string $file, bool $safe = false, ?string $attname = null): array
|
||||
{
|
||||
try {
|
||||
$path = $this->path($name, $safe);
|
||||
is_dir($dir = dirname($path)) || mkdir($dir, 0777, true);
|
||||
if (file_put_contents($path, $file)) {
|
||||
return $this->info($name, $safe, $attname);
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件内容
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return string
|
||||
*/
|
||||
public function get(string $name, bool $safe = false): string
|
||||
{
|
||||
if (!$this->has($name, $safe)) return '';
|
||||
return file_get_contents($this->path($name, $safe));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除存储文件
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return boolean
|
||||
*/
|
||||
public function del(string $name, bool $safe = false): bool
|
||||
{
|
||||
if ($this->has($name, $safe)) {
|
||||
return @unlink($this->path($name, $safe));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否存在
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return boolean
|
||||
*/
|
||||
public function has(string $name, bool $safe = false): bool
|
||||
{
|
||||
return is_file($this->path($name, $safe));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取访问地址
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return string
|
||||
*/
|
||||
public function url(string $name, bool $safe = false, ?string $attname = null): string
|
||||
{
|
||||
return $safe ? $name : "{$this->domain}/upload/{$this->delSuffix($name)}{$this->getSuffix($attname,$name)}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储路径
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return string
|
||||
*/
|
||||
public function path(string $name, bool $safe = false): string
|
||||
{
|
||||
$path = $safe ? 'safefile' : 'public/upload';
|
||||
return strtr(syspath("{$path}/{$this->delSuffix($name)}"), '\\', '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件信息
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
*/
|
||||
public function info(string $name, bool $safe = false, ?string $attname = null): array
|
||||
{
|
||||
return $this->has($name, $safe) ? [
|
||||
'url' => $this->url($name, $safe, $attname),
|
||||
'key' => "upload/{$name}", 'file' => $this->path($name, $safe),
|
||||
] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传地址
|
||||
* @return string
|
||||
*/
|
||||
public function upload(): string
|
||||
{
|
||||
return url('admin/api.upload/file', [], false, true)->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储区域
|
||||
* @return array
|
||||
*/
|
||||
public static function region(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
252
plugin/think-library/src/storage/QiniuStorage.php
Normal file
252
plugin/think-library/src/storage/QiniuStorage.php
Normal file
@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\storage;
|
||||
|
||||
use think\admin\contract\StorageInterface;
|
||||
use think\admin\contract\StorageUsageTrait;
|
||||
use think\admin\Exception;
|
||||
use think\admin\extend\HttpExtend;
|
||||
use think\admin\Storage;
|
||||
|
||||
/**
|
||||
* 七牛云存储支持
|
||||
* @class QiniuStorage
|
||||
* @package think\admin\storage
|
||||
*/
|
||||
class QiniuStorage implements StorageInterface
|
||||
{
|
||||
use StorageUsageTrait;
|
||||
|
||||
private $bucket;
|
||||
private $accessKey;
|
||||
private $secretKey;
|
||||
|
||||
/**
|
||||
* 初始化入口
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
// 读取配置文件
|
||||
$this->bucket = sysconf('storage.qiniu_bucket|raw');
|
||||
$this->accessKey = sysconf('storage.qiniu_access_key|raw');
|
||||
$this->secretKey = sysconf('storage.qiniu_secret_key|raw');
|
||||
// 计算链接前缀
|
||||
$host = strtolower(sysconf('storage.qiniu_http_domain|raw'));
|
||||
$type = strtolower(sysconf('storage.qiniu_http_protocol|raw'));
|
||||
if ($type === 'auto') {
|
||||
$this->domain = "//{$host}";
|
||||
} elseif (in_array($type, ['http', 'https'])) {
|
||||
$this->domain = "{$type}://{$host}";
|
||||
} else {
|
||||
throw new Exception(lang('未配置七牛云域名'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件内容
|
||||
* @param string $name 文件名称
|
||||
* @param string $file 文件内容
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function set(string $name, string $file, bool $safe = false, ?string $attname = null): array
|
||||
{
|
||||
$token = $this->token($name, 3600, $attname);
|
||||
$data = ['key' => $name, 'token' => $token, 'fileName' => $name];
|
||||
$file = ['field' => "file", 'name' => $name, 'content' => $file];
|
||||
$result = HttpExtend::submit($this->upload(), $data, $file, [], 'POST', false);
|
||||
return json_decode($result, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件内容
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return string
|
||||
*/
|
||||
public function get(string $name, bool $safe = false): string
|
||||
{
|
||||
$url = $this->url($name, $safe) . "?e=" . time();
|
||||
$token = "{$this->accessKey}:{$this->safeBase64(hash_hmac('sha1', $url, $this->secretKey, true))}";
|
||||
return Storage::curlGet("{$url}&token={$token}");
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除存储文件
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return boolean
|
||||
*/
|
||||
public function del(string $name, bool $safe = false): bool
|
||||
{
|
||||
[$EncodedEntryURI, $AccessToken] = $this->getAccessToken($name, 'delete');
|
||||
$data = json_decode(HttpExtend::post("https://rs.qiniu.com/delete/{$EncodedEntryURI}", [], [
|
||||
'headers' => ["Authorization:QBox {$AccessToken}"],
|
||||
]), true);
|
||||
return empty($data['error']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否存在
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return boolean
|
||||
*/
|
||||
public function has(string $name, bool $safe = false): bool
|
||||
{
|
||||
return !empty($this->info($name, $safe));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取访问地址
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return string
|
||||
*/
|
||||
public function url(string $name, bool $safe = false, ?string $attname = null): string
|
||||
{
|
||||
return "{$this->domain}/{$this->delSuffix($name)}{$this->getSuffix($attname,$name)}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储路径
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return string
|
||||
*/
|
||||
public function path(string $name, bool $safe = false): string
|
||||
{
|
||||
return $this->url($name, $safe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件信息
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
*/
|
||||
public function info(string $name, bool $safe = false, ?string $attname = null): array
|
||||
{
|
||||
[$entry, $token] = $this->getAccessToken($name);
|
||||
$data = json_decode(HttpExtend::get("https://rs.qiniu.com/stat/{$entry}", [], ['headers' => ["Authorization: QBox {$token}"]]), true);
|
||||
return isset($data['md5']) ? ['file' => $name, 'url' => $this->url($name, $safe, $attname), 'key' => $name] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传地址
|
||||
* @return string
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function upload(): string
|
||||
{
|
||||
try {
|
||||
$proc = $this->app->request->isSsl() ? 'https' : 'http';
|
||||
$region = sysconf('storage.qiniu_region|raw');
|
||||
} catch (\Exception $exception) {
|
||||
throw new Exception($exception->getMessage());
|
||||
}
|
||||
// 注:汉字为兼容旧版本区域配置
|
||||
switch ($region) {
|
||||
case '华东':
|
||||
case 'up.qiniup.com':
|
||||
return "{$proc}://up.qiniup.com";
|
||||
case 'up-cn-east-2.qiniup.com':
|
||||
return "{$proc}://up-cn-east-2.qiniup.com";
|
||||
case '华北':
|
||||
case 'up-z1.qiniup.com':
|
||||
return "{$proc}://up-z1.qiniup.com";
|
||||
case '华南':
|
||||
case 'up-z2.qiniup.com':
|
||||
return "{$proc}://up-z2.qiniup.com";
|
||||
case '北美':
|
||||
case 'up-na0.qiniup.com':
|
||||
return "{$proc}://up-na0.qiniup.com";
|
||||
case '东南亚':
|
||||
case 'up-as0.qiniup.com':
|
||||
return "{$proc}://up-as0.qiniup.com";
|
||||
case 'up-ap-northeast-1.qiniup.com':
|
||||
return "{$proc}://up-ap-northeast-1.qiniup.com";
|
||||
default:
|
||||
throw new Exception(lang('未配置七牛云空间区域哦'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成上传令牌
|
||||
* @param ?string $name 文件名称
|
||||
* @param integer $expires 有效时间
|
||||
* @param ?string $attname 下载名称
|
||||
* @return string
|
||||
*/
|
||||
public function token(?string $name = null, int $expires = 3600, ?string $attname = null): string
|
||||
{
|
||||
$key = is_null($name) ? '$(key)' : $name;
|
||||
$url = "{$this->domain}/$(key){$this->getSuffix($attname,$name)}";
|
||||
$policy = $this->safeBase64(json_encode([
|
||||
"deadline" => time() + $expires, "scope" => is_null($name) ? $this->bucket : "{$this->bucket}:{$name}",
|
||||
'returnBody' => json_encode(['uploaded' => true, 'filename' => '$(key)', 'url' => $url, 'key' => $key, 'file' => $key], JSON_UNESCAPED_UNICODE),
|
||||
]));
|
||||
return "{$this->accessKey}:{$this->safeBase64(hash_hmac('sha1', $policy, $this->secretKey, true))}:{$policy}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全BASE64编码
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
private function safeBase64(string $content): string
|
||||
{
|
||||
return str_replace(['+', '/'], ['-', '_'], base64_encode($content));
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成管理凭证
|
||||
* @param string $name 文件名称
|
||||
* @param string $type 操作类型
|
||||
* @return array
|
||||
*/
|
||||
private function getAccessToken(string $name, string $type = 'stat'): array
|
||||
{
|
||||
$entry = $this->safeBase64("{$this->bucket}:{$name}");
|
||||
$sign = hash_hmac('sha1', "/{$type}/{$entry}\n", $this->secretKey, true);
|
||||
return [$entry, "{$this->accessKey}:{$this->safeBase64($sign)}"];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储区域
|
||||
* @return array
|
||||
*/
|
||||
public static function region(): array
|
||||
{
|
||||
return [
|
||||
'up.qiniup.com' => lang('华东-浙江'),
|
||||
'up-cn-east-2.qiniup.com' => lang('华东-浙江2'),
|
||||
'up-z1.qiniup.com' => lang('华北-河北'),
|
||||
'up-z2.qiniup.com' => lang('华南-广东'),
|
||||
'up-na0.qiniup.com' => lang('北美-洛杉矶'),
|
||||
'up-as0.qiniup.com' => lang('亚太-新加坡'),
|
||||
'up-ap-northeast-1.qiniup.com' => lang('亚太-首尔'),
|
||||
];
|
||||
}
|
||||
}
|
||||
304
plugin/think-library/src/storage/TxcosStorage.php
Normal file
304
plugin/think-library/src/storage/TxcosStorage.php
Normal file
@ -0,0 +1,304 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\storage;
|
||||
|
||||
use think\admin\contract\StorageInterface;
|
||||
use think\admin\contract\StorageUsageTrait;
|
||||
use think\admin\Exception;
|
||||
use think\admin\extend\HttpExtend;
|
||||
use think\admin\Storage;
|
||||
|
||||
/**
|
||||
* 腾讯云COS存储支持
|
||||
* @class TxcosStorage
|
||||
* @package think\admin\storage
|
||||
*/
|
||||
class TxcosStorage implements StorageInterface
|
||||
{
|
||||
|
||||
use StorageUsageTrait;
|
||||
|
||||
/**
|
||||
* 数据中心
|
||||
* @var string
|
||||
*/
|
||||
private $point;
|
||||
|
||||
/**
|
||||
* 存储空间名称
|
||||
* @var string
|
||||
*/
|
||||
private $bucket;
|
||||
|
||||
/**
|
||||
* $secretId
|
||||
* @var string
|
||||
*/
|
||||
private $secretId;
|
||||
|
||||
/**
|
||||
* secretKey
|
||||
* @var string
|
||||
*/
|
||||
private $secretKey;
|
||||
|
||||
/**
|
||||
* 初始化入口
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
// 读取配置文件
|
||||
$this->point = sysconf('storage.txcos_point|raw');
|
||||
$this->bucket = sysconf('storage.txcos_bucket|raw');
|
||||
$this->secretId = sysconf('storage.txcos_access_key|raw');
|
||||
$this->secretKey = sysconf('storage.txcos_secret_key|raw');
|
||||
// 计算链接前缀
|
||||
$host = strtolower(sysconf('storage.txcos_http_domain|raw'));
|
||||
$type = strtolower(sysconf('storage.txcos_http_protocol|raw'));
|
||||
if ($type === 'auto') {
|
||||
$this->domain = "//{$host}";
|
||||
} elseif (in_array($type, ['http', 'https'])) {
|
||||
$this->domain = "{$type}://{$host}";
|
||||
} else {
|
||||
throw new Exception(lang('未配置腾讯云域名'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件内容
|
||||
* @param string $name 文件名称
|
||||
* @param string $file 文件内容
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
*/
|
||||
public function set(string $name, string $file, bool $safe = false, ?string $attname = null): array
|
||||
{
|
||||
$data = $this->token($name) + ['key' => $name];
|
||||
if (is_string($attname) && strlen($attname) > 0) {
|
||||
$data['Content-Disposition'] = urlencode($attname);
|
||||
}
|
||||
$data['success_action_status'] = '200';
|
||||
$file = ['field' => 'file', 'name' => $name, 'content' => $file];
|
||||
if (is_numeric(stripos(HttpExtend::submit($this->upload(), $data, $file), '200 OK'))) {
|
||||
return ['file' => $this->path($name, $safe), 'url' => $this->url($name, $safe, $attname), 'key' => $name];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件内容
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return string
|
||||
*/
|
||||
public function get(string $name, bool $safe = false): string
|
||||
{
|
||||
return Storage::curlGet($this->url($name, $safe));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除存储文件
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return boolean
|
||||
*/
|
||||
public function del(string $name, bool $safe = false): bool
|
||||
{
|
||||
[$file] = explode('?', $name);
|
||||
$result = HttpExtend::request('DELETE', "https://{$this->bucket}.{$this->point}/{$file}", [
|
||||
'returnHeader' => true, 'headers' => $this->_sign('DELETE', $file),
|
||||
]);
|
||||
return is_numeric(stripos($result, '204 No Content'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否存在
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return boolean
|
||||
*/
|
||||
public function has(string $name, bool $safe = false): bool
|
||||
{
|
||||
$file = $this->delSuffix($name);
|
||||
$result = HttpExtend::request('HEAD', "https://{$this->bucket}.{$this->point}/{$file}", [
|
||||
'returnHeader' => true, 'headers' => $this->_sign('HEAD', $name),
|
||||
]);
|
||||
return is_numeric(stripos($result, 'HTTP/1.1 200 OK'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取访问地址
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return string
|
||||
*/
|
||||
public function url(string $name, bool $safe = false, ?string $attname = null): string
|
||||
{
|
||||
return "{$this->domain}/{$this->delSuffix($name)}{$this->getSuffix($attname,$name)}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储路径
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return string
|
||||
*/
|
||||
public function path(string $name, bool $safe = false): string
|
||||
{
|
||||
return $this->url($name, $safe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件信息
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
*/
|
||||
public function info(string $name, bool $safe = false, ?string $attname = null): array
|
||||
{
|
||||
return $this->has($name, $safe) ? [
|
||||
'url' => $this->url($name, $safe, $attname),
|
||||
'key' => $name, 'file' => $this->path($name, $safe),
|
||||
] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传地址
|
||||
* @return string
|
||||
*/
|
||||
public function upload(): string
|
||||
{
|
||||
$proc = $this->app->request->isSsl() ? 'https' : 'http';
|
||||
return "{$proc}://{$this->bucket}.{$this->point}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成上传令牌
|
||||
* @param string $name 文件名称
|
||||
* @param integer $expires 有效时间
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
*/
|
||||
public function token(string $name, int $expires = 3600, ?string $attname = null): array
|
||||
{
|
||||
$startTimestamp = time();
|
||||
$endTimestamp = $startTimestamp + $expires;
|
||||
$keyTime = "{$startTimestamp};{$endTimestamp}";
|
||||
$siteurl = $this->url($name, false, $attname);
|
||||
$policy = json_encode([
|
||||
'expiration' => date('Y-m-d\TH:i:s.000\Z', $endTimestamp),
|
||||
'conditions' => [['q-ak' => $this->secretId], ['q-sign-time' => $keyTime], ['q-sign-algorithm' => 'sha1']],
|
||||
]);
|
||||
return [
|
||||
'policy' => base64_encode($policy), 'q-ak' => $this->secretId,
|
||||
'siteurl' => $siteurl, 'q-key-time' => $keyTime, 'q-sign-algorithm' => 'sha1',
|
||||
'q-signature' => hash_hmac('sha1', sha1($policy), hash_hmac('sha1', $keyTime, $this->secretKey)),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成请求签名
|
||||
* @param string $method 请求方式
|
||||
* @param string $soruce 资源名称
|
||||
* @return array
|
||||
*/
|
||||
private function _sign(string $method, string $soruce): array
|
||||
{
|
||||
$header = [];
|
||||
// 1.生成 KeyTime
|
||||
$startTimestamp = time();
|
||||
$endTimestamp = $startTimestamp + 3600;
|
||||
$keyTime = "{$startTimestamp};{$endTimestamp}";
|
||||
// 2.生成 SignKey
|
||||
$signKey = hash_hmac('sha1', $keyTime, $this->secretKey);
|
||||
// 3.生成 UrlParamList, HttpParameters
|
||||
[$parse_url, $urlParamList, $httpParameters] = [parse_url($soruce), '', ''];
|
||||
if (!empty($parse_url['query'])) {
|
||||
parse_str($parse_url['query'], $params);
|
||||
uksort($params, 'strnatcasecmp');
|
||||
$urlParamList = join(';', array_keys($params));
|
||||
$httpParameters = http_build_query($params);
|
||||
}
|
||||
// 4.生成 HeaderList, HttpHeaders
|
||||
[$headerList, $httpHeaders] = ['', ''];
|
||||
if (!empty($header)) {
|
||||
uksort($header, 'strnatcasecmp');
|
||||
$headerList = join(';', array_keys($header));
|
||||
$httpHeaders = http_build_query($header);
|
||||
}
|
||||
// 5.生成 HttpString
|
||||
$httpString = strtolower($method) . "\n/{$parse_url['path']}\n{$httpParameters}\n{$httpHeaders}\n";
|
||||
// 6.生成 StringToSign
|
||||
$httpStringSha1 = sha1($httpString);
|
||||
$stringToSign = "sha1\n{$keyTime}\n{$httpStringSha1}\n";
|
||||
// 7.生成 Signature
|
||||
$signature = hash_hmac('sha1', $stringToSign, $signKey);
|
||||
// 8.生成签名
|
||||
$signArray = [
|
||||
'q-sign-algorithm' => 'sha1',
|
||||
'q-ak' => $this->secretId,
|
||||
'q-sign-time' => $keyTime,
|
||||
'q-key-time' => $keyTime,
|
||||
'q-header-list' => $headerList,
|
||||
'q-url-param-list' => $urlParamList,
|
||||
'q-signature' => $signature,
|
||||
];
|
||||
$header['Authorization'] = urldecode(http_build_query($signArray));
|
||||
foreach ($header as $key => $value) $header[$key] = ucfirst($key) . ": {$value}";
|
||||
return array_values($header);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储区域
|
||||
* @return array
|
||||
*/
|
||||
public static function region(): array
|
||||
{
|
||||
return [
|
||||
'cos.ap-beijing-1.myqcloud.com' => lang('中国大陆 公有云地域 北京一区'),
|
||||
'cos.ap-beijing.myqcloud.com' => lang('中国大陆 公有云地域 北京'),
|
||||
'cos.ap-nanjing.myqcloud.com' => lang('中国大陆 公有云地域 南京'),
|
||||
'cos.ap-shanghai.myqcloud.com' => lang('中国大陆 公有云地域 上海'),
|
||||
'cos.ap-guangzhou.myqcloud.com' => lang('中国大陆 公有云地域 广州'),
|
||||
'cos.ap-chengdu.myqcloud.com' => lang('中国大陆 公有云地域 成都'),
|
||||
'cos.ap-chongqing.myqcloud.com' => lang('中国大陆 公有云地域 重庆'),
|
||||
'cos.ap-shenzhen-fsi.myqcloud.com' => lang('中国大陆 金融云地域 深圳金融'),
|
||||
'cos.ap-shanghai-fsi.myqcloud.com' => lang('中国大陆 金融云地域 上海金融'),
|
||||
'cos.ap-beijing-fsi.myqcloud.com' => lang('中国大陆 金融云地域 北京金融'),
|
||||
'cos.ap-hongkong.myqcloud.com' => lang('亚太地区 公有云地域 中国香港'),
|
||||
'cos.ap-singapore.myqcloud.com' => lang('亚太地区 公有云地域 新加坡'),
|
||||
'cos.ap-mumbai.myqcloud.com' => lang('亚太地区 公有云地域 孟买'),
|
||||
'cos.ap-jakarta.myqcloud.com' => lang('亚太地区 公有云地域 雅加达'),
|
||||
'cos.ap-seoul.myqcloud.com' => lang('亚太地区 公有云地域 首尔'),
|
||||
'cos.ap-bangkok.myqcloud.com' => lang('亚太地区 公有云地域 曼谷'),
|
||||
'cos.ap-tokyo.myqcloud.com' => lang('亚太地区 公有云地域 东京'),
|
||||
'cos.na-siliconvalley.myqcloud.com' => lang('北美地区 公有云地域 硅谷'),
|
||||
'cos.na-ashburn.myqcloud.com' => lang('北美地区 公有云地域 弗吉尼亚'),
|
||||
'cos.na-toronto.myqcloud.com' => lang('北美地区 公有云地域 多伦多'),
|
||||
'cos.sa-saopaulo.myqcloud.com' => lang('北美地区 公有云地域 圣保罗'),
|
||||
'cos.eu-frankfurt.myqcloud.com' => lang('欧洲地区 公有云地域 法兰克福'),
|
||||
'cos.eu-moscow.myqcloud.com' => lang('欧洲地区 公有云地域 莫斯科'),
|
||||
];
|
||||
}
|
||||
}
|
||||
232
plugin/think-library/src/storage/UpyunStorage.php
Normal file
232
plugin/think-library/src/storage/UpyunStorage.php
Normal file
@ -0,0 +1,232 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\storage;
|
||||
|
||||
use think\admin\contract\StorageInterface;
|
||||
use think\admin\contract\StorageUsageTrait;
|
||||
use think\admin\Exception;
|
||||
use think\admin\extend\HttpExtend;
|
||||
use think\admin\Storage;
|
||||
|
||||
/**
|
||||
* 又拍云存储支持
|
||||
* @class UpyunStorage
|
||||
* @package think\admin\storage
|
||||
*/
|
||||
class UpyunStorage implements StorageInterface
|
||||
{
|
||||
use StorageUsageTrait;
|
||||
|
||||
/**
|
||||
* 存储空间名称
|
||||
* @var string
|
||||
*/
|
||||
private $bucket;
|
||||
|
||||
/**
|
||||
* AccessId
|
||||
* @var string
|
||||
*/
|
||||
private $accessKey;
|
||||
|
||||
/**
|
||||
* AccessSecret
|
||||
* @var string
|
||||
*/
|
||||
private $secretKey;
|
||||
|
||||
/**
|
||||
* 初始化入口
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
// 读取配置文件
|
||||
$this->bucket = sysconf('storage.upyun_bucket|raw');
|
||||
$this->accessKey = sysconf('storage.upyun_access_key|raw');
|
||||
$this->secretKey = sysconf('storage.upyun_secret_key|raw');
|
||||
// 计算链接前缀
|
||||
$host = strtolower(sysconf('storage.upyun_http_domain|raw'));
|
||||
$type = strtolower(sysconf('storage.upyun_http_protocol|raw'));
|
||||
if ($type === 'auto') {
|
||||
$this->domain = "//{$host}";
|
||||
} elseif (in_array($type, ['http', 'https'])) {
|
||||
$this->domain = "{$type}://{$host}";
|
||||
} else {
|
||||
throw new Exception(lang('未配置又拍云域名'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件内容
|
||||
* @param string $name 文件名称
|
||||
* @param string $file 文件内容
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
*/
|
||||
public function set(string $name, string $file, bool $safe = false, ?string $attname = null): array
|
||||
{
|
||||
$data = [];
|
||||
$token = $this->token($name, 3600, $attname, md5($file));
|
||||
$data['policy'] = $token['policy'];
|
||||
$data['authorization'] = $token['authorization'];
|
||||
$file = ['field' => 'file', 'name' => $name, 'content' => $file];
|
||||
if (is_numeric(stripos(HttpExtend::submit($this->upload(), $data, $file), '200 OK'))) {
|
||||
return ['file' => $this->path($name, $safe), 'url' => $this->url($name, $safe, $attname), 'key' => $name];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件内容
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return string
|
||||
*/
|
||||
public function get(string $name, bool $safe = false): string
|
||||
{
|
||||
return Storage::curlGet($this->url($name, $safe));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除存储文件
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return boolean
|
||||
*/
|
||||
public function del(string $name, bool $safe = false): bool
|
||||
{
|
||||
[$file] = explode('?', $name);
|
||||
$result = HttpExtend::request('DELETE', "{$this->upload()}/{$file}", [
|
||||
'returnHeader' => true, 'headers' => $this->_sign('DELETE', $file),
|
||||
]);
|
||||
return is_numeric(stripos($result, 'HTTP/1.1 200 OK'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否存在
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return boolean
|
||||
*/
|
||||
public function has(string $name, bool $safe = false): bool
|
||||
{
|
||||
$file = $this->delSuffix($name);
|
||||
$result = HttpExtend::request('HEAD', "{$this->upload()}/{$file}", [
|
||||
'returnHeader' => true, 'headers' => $this->_sign('HEAD', $file),
|
||||
]);
|
||||
return is_numeric(stripos($result, 'HTTP/1.1 200 OK'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取访问地址
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return string
|
||||
*/
|
||||
public function url(string $name, bool $safe = false, ?string $attname = null): string
|
||||
{
|
||||
return "{$this->domain}/{$this->delSuffix($name)}{$this->getSuffix($attname,$name)}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储路径
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @return string
|
||||
*/
|
||||
public function path(string $name, bool $safe = false): string
|
||||
{
|
||||
return $this->url($name, $safe);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件信息
|
||||
* @param string $name 文件名称
|
||||
* @param boolean $safe 安全模式
|
||||
* @param ?string $attname 下载名称
|
||||
* @return array
|
||||
*/
|
||||
public function info(string $name, bool $safe = false, ?string $attname = null): array
|
||||
{
|
||||
return $this->has($name, $safe) ? [
|
||||
'url' => $this->url($name, $safe, $attname),
|
||||
'key' => $name, 'file' => $this->path($name, $safe),
|
||||
] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传地址
|
||||
* @return string
|
||||
*/
|
||||
public function upload(): string
|
||||
{
|
||||
$protocol = $this->app->request->isSsl() ? 'https' : 'http';
|
||||
return "{$protocol}://v0.api.upyun.com/{$this->bucket}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成上传令牌
|
||||
* @param string $name 文件名称
|
||||
* @param integer $expires 有效时间
|
||||
* @param ?string $attname 下载名称
|
||||
* @param ?string $fileHash 文件哈希
|
||||
* @return array
|
||||
*/
|
||||
public function token(string $name, int $expires = 3600, ?string $attname = null, ?string $fileHash = ''): array
|
||||
{
|
||||
$policy = ['save-key' => $name];
|
||||
$policy['date'] = gmdate('D, d M Y H:i:s \G\M\T');
|
||||
$policy['bucket'] = $this->bucket;
|
||||
$policy['expiration'] = time() + $expires;
|
||||
if ($fileHash) $policy['content-md5'] = $fileHash;
|
||||
$data = ['keyid' => $this->accessKey, 'siteurl' => $this->url($name, false, $attname)];
|
||||
$data['policy'] = base64_encode(json_encode($policy, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
$content = "POST&/{$this->bucket}&{$policy['date']}&{$data['policy']}";
|
||||
if ($fileHash) $content .= "&{$fileHash}";
|
||||
$data['signature'] = base64_encode(hash_hmac('sha1', $content, md5($this->secretKey), true));
|
||||
$data['authorization'] = "UPYUN {$this->accessKey}:{$data['signature']}";
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成请求签名
|
||||
* @param string $method 请求方式
|
||||
* @param string $name 资源名称
|
||||
* @return array
|
||||
*/
|
||||
private function _sign(string $method, string $name): array
|
||||
{
|
||||
$data = [$method, "/{$this->bucket}/{$name}", $date = gmdate('D, d M Y H:i:s \G\M\T')];
|
||||
$signature = base64_encode(hash_hmac('sha1', join('&', $data), md5($this->secretKey), true));
|
||||
return ["Authorization:UPYUN {$this->accessKey}:{$signature}", "Date:{$date}"];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储区域
|
||||
* @return array
|
||||
*/
|
||||
public static function region(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
1015
plugin/think-library/src/storage/bin/mimes.php
Normal file
1015
plugin/think-library/src/storage/bin/mimes.php
Normal file
File diff suppressed because it is too large
Load Diff
39
plugin/think-library/src/support/Route.php
Normal file
39
plugin/think-library/src/support/Route.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\support;
|
||||
|
||||
use think\Route as ThinkRoute;
|
||||
|
||||
/**
|
||||
* 自定义路由对象
|
||||
* @class Route
|
||||
* @package think\admin\support
|
||||
*/
|
||||
class Route extends ThinkRoute
|
||||
{
|
||||
/**
|
||||
* 重载路由配置
|
||||
* @return $this
|
||||
*/
|
||||
public function reload(): Route
|
||||
{
|
||||
$this->config = array_merge($this->config, $this->app->config->get('route'));
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
203
plugin/think-library/src/support/Url.php
Normal file
203
plugin/think-library/src/support/Url.php
Normal file
@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: liu21st <liu21st@gmail.com>
|
||||
// +----------------------------------------------------------------------
|
||||
// 以下代码来自 topthink/think-multi-app,有部分修改以兼容 ThinkAdmin 的需求
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\support;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use think\admin\service\NodeService;
|
||||
use think\route\Url as ThinkUrl;
|
||||
|
||||
/**
|
||||
* 多应用 URL 生成与解析
|
||||
* @class Url
|
||||
* @package think\admin\support
|
||||
*/
|
||||
class Url extends ThinkUrl
|
||||
{
|
||||
/**
|
||||
* 直接解析 URL 地址
|
||||
* @param string $url URL
|
||||
* @param string|boolean $domain Domain
|
||||
* @return string
|
||||
*/
|
||||
protected function parseUrl(string $url, &$domain): string
|
||||
{
|
||||
$request = $this->app->request;
|
||||
if (0 === strpos($url, '/')) {
|
||||
$url = substr($url, 1);
|
||||
} elseif (false !== strpos($url, '\\')) {
|
||||
$url = ltrim(str_replace('\\', '/', $url), '/');
|
||||
} elseif (0 === strpos($url, '@')) {
|
||||
$url = substr($url, 1);
|
||||
} else {
|
||||
$attrs = str2arr($url, '/');
|
||||
$action = empty($attrs) ? $request->action() : array_pop($attrs);
|
||||
$contrl = empty($attrs) ? $request->controller() : array_pop($attrs);
|
||||
$module = empty($attrs) ? $this->app->http->getName() : array_pop($attrs);
|
||||
// 拼装新的链接地址
|
||||
$url = NodeService::nameTolower($contrl) . '/' . $action;
|
||||
$bind = $this->app->config->get('app.domain_bind', []);
|
||||
if ($key = array_search($module, $bind)) {
|
||||
if (isset($bind[$_SERVER['SERVER_NAME']])) $domain = $_SERVER['SERVER_NAME'];
|
||||
$domain = is_bool($domain) ? $key : $domain;
|
||||
} elseif ($key = array_search($module, $this->app->config->get('app.app_map', []))) {
|
||||
$url = $key . '/' . $url;
|
||||
} else {
|
||||
$url = $module . '/' . $url;
|
||||
}
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build URL
|
||||
* @return string
|
||||
*/
|
||||
public function build(): string
|
||||
{
|
||||
$url = $this->url;
|
||||
$vars = $this->vars;
|
||||
$domain = $this->domain;
|
||||
$suffix = $this->suffix;
|
||||
$request = $this->app->request;
|
||||
if (0 === strpos($url, '[') && $pos = strpos($url, ']')) {
|
||||
// [name] 表示使用路由命名标识生成URL
|
||||
$name = substr($url, 1, $pos - 1);
|
||||
$url = 'name' . substr($url, $pos + 1);
|
||||
}
|
||||
if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
|
||||
$info = parse_url($url);
|
||||
$url = !empty($info['path']) ? $info['path'] : '';
|
||||
if (isset($info['fragment'])) {
|
||||
// 解析锚点
|
||||
$anchor = $info['fragment'];
|
||||
if (false !== strpos($anchor, '?')) {
|
||||
// 解析参数
|
||||
[$anchor, $info['query']] = explode('?', $anchor, 2);
|
||||
}
|
||||
if (false !== strpos($anchor, '@')) {
|
||||
// 解析域名
|
||||
[$anchor, $domain] = explode('@', $anchor, 2);
|
||||
}
|
||||
} elseif (strpos($url, '@') && false === strpos($url, '\\')) {
|
||||
// 解析域名
|
||||
[$url, $domain] = explode('@', $url, 2);
|
||||
}
|
||||
}
|
||||
if ($url) {
|
||||
$checkDomain = $domain && is_string($domain) ? $domain : null;
|
||||
$checkName = $name ?? $url . (isset($info['query']) ? '?' . $info['query'] : '');
|
||||
$rule = $this->route->getName($checkName, $checkDomain);
|
||||
if (empty($rule) && isset($info['query'])) {
|
||||
$rule = $this->route->getName($url, $checkDomain);
|
||||
parse_str($info['query'], $params);
|
||||
$vars = array_merge($params, $vars);
|
||||
unset($info['query']);
|
||||
}
|
||||
}
|
||||
if (!empty($rule) && $match = $this->getRuleUrl($rule, $vars, $domain)) {
|
||||
$url = $match[0];
|
||||
if ($domain && !empty($match[1])) $domain = $match[1];
|
||||
if (!is_null($match[2])) $suffix = $match[2];
|
||||
if (!$this->app->http->isBind()) {
|
||||
$url = $this->app->http->getName() . '/' . $url;
|
||||
}
|
||||
} elseif (!empty($rule) && isset($name)) {
|
||||
throw new InvalidArgumentException('route name not exists:' . $name);
|
||||
} else {
|
||||
// 检测URL绑定
|
||||
$bind = $this->route->getDomainBind($domain && is_string($domain) ? $domain : null);
|
||||
if ($bind && 0 === strpos($url, $bind)) {
|
||||
$url = substr($url, strlen($bind) + 1);
|
||||
} else {
|
||||
$binds = $this->route->getBind();
|
||||
foreach ($binds as $key => $val) {
|
||||
if (is_string($val) && 0 === strpos($url, $val) && substr_count($val, '/') > 1) {
|
||||
$url = substr($url, strlen($val) + 1);
|
||||
$domain = $key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 路由标识不存在 直接解析
|
||||
$url = $this->parseUrl($url, $domain);
|
||||
if (isset($info['query'])) {
|
||||
// 解析地址里面参数 合并到vars
|
||||
parse_str($info['query'], $params);
|
||||
$vars = array_merge($params, $vars);
|
||||
}
|
||||
}
|
||||
// 还原 URL 分隔符
|
||||
$file = $request->baseFile();
|
||||
$depr = $this->route->config('pathinfo_depr');
|
||||
[$uri, $url] = [$request->url(), str_replace('/', $depr, $url)];
|
||||
if ($file && 0 !== strpos($uri, $file)) {
|
||||
$file = str_replace('\\', '/', dirname($file));
|
||||
}
|
||||
/*=====- 多应用绑定 URL 生成处理 -=====*/
|
||||
$app = $this->app->http->getName();
|
||||
if ($this->app->http->isBind()) {
|
||||
if (preg_match("#^{$app}({$depr}|\.|$)#i", $url)) {
|
||||
$url = trim(substr($url, strlen($app)), $depr);
|
||||
} elseif (substr_count($url, $depr) >= 2) {
|
||||
$file = 'index.php';
|
||||
}
|
||||
}
|
||||
$url = rtrim($file, '/') . '/' . ltrim($url, '/');
|
||||
// URL后缀
|
||||
if ('/' == substr($url, -1) || '' == $url) {
|
||||
$suffix = '';
|
||||
} else {
|
||||
$suffix = $this->parseSuffix($suffix);
|
||||
}
|
||||
// 锚点
|
||||
$anchor = !empty($anchor) ? '#' . $anchor : '';
|
||||
// 参数组装
|
||||
if (!empty($vars)) {
|
||||
// 添加参数
|
||||
if ($this->route->config('url_common_param')) {
|
||||
$vars = http_build_query($vars);
|
||||
$url .= $suffix . '?' . $vars . $anchor;
|
||||
} else {
|
||||
foreach ($vars as $var => $val) {
|
||||
$val = (string)$val;
|
||||
if ('' !== $val) {
|
||||
$url .= $depr . $var . $depr . urlencode($val);
|
||||
}
|
||||
}
|
||||
$url .= $suffix . $anchor;
|
||||
}
|
||||
} else {
|
||||
$url .= $suffix . $anchor;
|
||||
}
|
||||
// 检测域名
|
||||
$domain = $this->parseDomain($url, $domain);
|
||||
// URL 组装
|
||||
return $domain . rtrim($this->root, '/') . '/' . ltrim($url, '/');
|
||||
}
|
||||
}
|
||||
96
plugin/think-library/src/support/command/Database.php
Normal file
96
plugin/think-library/src/support/command/Database.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\support\command;
|
||||
|
||||
use think\admin\Command;
|
||||
use think\admin\service\SystemService;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\Output;
|
||||
|
||||
/**
|
||||
* 数据库修复优化指令
|
||||
* @class Database
|
||||
* @package think\admin\support\command
|
||||
*/
|
||||
class Database extends Command
|
||||
{
|
||||
/**
|
||||
* 指令任务配置
|
||||
*/
|
||||
public function configure()
|
||||
{
|
||||
$this->setName('xadmin:database');
|
||||
$this->addArgument('action', Argument::OPTIONAL, 'repair|optimize', 'optimize');
|
||||
$this->setDescription('Database Optimize and Repair for ThinkAdmin');
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务执行入口
|
||||
* @param \think\console\Input $input
|
||||
* @param \think\console\Output $output
|
||||
* @return void
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function execute(Input $input, Output $output): void
|
||||
{
|
||||
if ($this->app->db->connect()->getConfig('type') === 'sqlite') {
|
||||
$this->setQueueError("Sqlite 数据库不支持 REPAIR 和 OPTIMIZE 操作!");
|
||||
}
|
||||
$action = $input->getArgument('action');
|
||||
if (method_exists($this, $method = "_{$action}")) $this->$method();
|
||||
else $this->output->error('Wrong operation, currently allow repair|optimize');
|
||||
}
|
||||
|
||||
/**
|
||||
* 修复所有数据表
|
||||
* @return void
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function _repair(): void
|
||||
{
|
||||
$this->setQueueProgress("正在获取需要修复的数据表", '0');
|
||||
[$tables, $total, $count] = SystemService::getTables();
|
||||
$this->setQueueProgress("总共需要修复 {$total} 张数据表", '0');
|
||||
foreach ($tables as $table) {
|
||||
$this->setQueueMessage($total, ++$count, "正在修复数据表 {$table}");
|
||||
$this->app->db->connect()->query("REPAIR TABLE `{$table}`");
|
||||
$this->setQueueMessage($total, $count, "完成修复数据表 {$table}", 1);
|
||||
}
|
||||
$this->setQueueSuccess("已完成对 {$total} 张数据表修复操作");
|
||||
}
|
||||
|
||||
/**
|
||||
* 优化所有数据表
|
||||
* @return void
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function _optimize(): void
|
||||
{
|
||||
$this->setQueueProgress("正在获取需要优化的数据表", '0');
|
||||
[$tables, $total, $count] = SystemService::getTables();
|
||||
$this->setQueueProgress("总共需要优化 {$total} 张数据表", '0');
|
||||
foreach ($tables as $table) {
|
||||
$this->setQueueMessage($total, ++$count, "正在优化数据表 {$table}");
|
||||
$this->app->db->connect()->query("OPTIMIZE TABLE `{$table}`");
|
||||
$this->setQueueMessage($total, $count, "完成优化数据表 {$table}", 1);
|
||||
}
|
||||
$this->setQueueSuccess("已完成对 {$total} 张数据表优化操作");
|
||||
}
|
||||
}
|
||||
158
plugin/think-library/src/support/command/Package.php
Normal file
158
plugin/think-library/src/support/command/Package.php
Normal file
@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\support\command;
|
||||
|
||||
use think\admin\Command;
|
||||
use think\admin\Exception;
|
||||
use think\admin\extend\PhinxExtend;
|
||||
use think\admin\Library;
|
||||
use think\admin\service\SystemService;
|
||||
use think\console\input\Option;
|
||||
|
||||
/**
|
||||
* 生成数据安装包
|
||||
* @class Package
|
||||
* @package think\admin\support\command
|
||||
*/
|
||||
class Package extends Command
|
||||
{
|
||||
/**
|
||||
* 系统指定配置
|
||||
* @return void
|
||||
*/
|
||||
public function configure()
|
||||
{
|
||||
$this->setName('xadmin:package');
|
||||
$this->addOption('all', 'a', Option::VALUE_NONE, 'Backup All Tables');
|
||||
$this->addOption('table', 't', Option::VALUE_OPTIONAL, 'Package Tables Scheme', '');
|
||||
$this->addOption('backup', 'b', Option::VALUE_OPTIONAL, 'Package Tables Backup', '');
|
||||
$this->setDescription('Generate System Install Package for ThinkAdmin');
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成系统安装数据包
|
||||
* @return void
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
// 创建数据库迁移脚本目录
|
||||
$dirname = syspath('database/migrations');
|
||||
is_dir($dirname) or mkdir($dirname, 0777, true);
|
||||
// 开始创建数据库迁移脚本
|
||||
$this->output->writeln('--- 开始创建数据库迁移脚本 ---');
|
||||
if ($this->createBackup() && $this->createScheme()) {
|
||||
$this->setQueueSuccess('--- 数据库迁移脚本创建成功 ---');
|
||||
} else {
|
||||
$this->setQueueError('--- 数据库迁移脚本创建失败 ---');
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
throw $exception;
|
||||
} catch (\Exception $exception) {
|
||||
trace_file($exception);
|
||||
$this->setQueueError($exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据表
|
||||
* @return boolean
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function createScheme(): bool
|
||||
{
|
||||
// 接收指定打包数据表
|
||||
if ($this->input->hasOption('table')) {
|
||||
$tables = str2arr(strtr($this->input->getOption('table'), '|', ','));
|
||||
} elseif ($this->input->hasOption('all')) {
|
||||
[$tables] = SystemService::getTables();
|
||||
} else {
|
||||
$tables = Library::$sapp->config->get('phinx.tables', []);
|
||||
if (empty($tables)) [$tables] = SystemService::getTables();
|
||||
}
|
||||
|
||||
// 去除忽略的数据表
|
||||
$ignore = Library::$sapp->config->get('phinx.ignore', []);
|
||||
$tables = array_unique(array_diff($tables, $ignore, ['migrations']));
|
||||
|
||||
// 创建数据库结构安装脚本
|
||||
[$prefix, $groups] = ['', []];
|
||||
foreach ($tables as $table) {
|
||||
$attr = explode('_', $table);
|
||||
if ($attr[0] === 'plugin') array_shift($attr);
|
||||
if (empty($prefix) || $prefix !== $attr[0]) {
|
||||
$prefix = $attr[0];
|
||||
}
|
||||
$groups[$prefix][] = $table;
|
||||
}
|
||||
[$total, $count] = [count($groups), 0];
|
||||
$this->setQueueMessage($total, 0, '开始创建数据表创建脚本!');
|
||||
foreach ($groups as $key => $tbs) {
|
||||
$name = 'Install' . ucfirst($key) . 'Table';
|
||||
$phinx = PhinxExtend::create2table($tbs, $name);
|
||||
$target = syspath("database/migrations/{$phinx['file']}");
|
||||
if (file_put_contents($target, $phinx['text']) !== false) {
|
||||
$this->setQueueMessage($total, ++$count, "创建数据库 {$name} 安装脚本成功!");
|
||||
} else {
|
||||
$this->setQueueMessage($total, ++$count, "创建数据库 {$name} 安装脚本失败!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建数据包
|
||||
* @return boolean
|
||||
* @throws \think\admin\Exception
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function createBackup(): bool
|
||||
{
|
||||
// 接收指定打包数据表
|
||||
if ($this->input->hasOption('backup')) {
|
||||
$tables = str2arr(strtr($this->input->getOption('backup'), '|', ','));
|
||||
} elseif ($this->input->hasOption('all')) {
|
||||
[$tables] = SystemService::getTables();
|
||||
} else {
|
||||
[$tables] = SystemService::getTables();
|
||||
$tables = array_intersect($tables, Library::$sapp->config->get('phinx.backup', []));
|
||||
}
|
||||
|
||||
// 去除忽略的数据表
|
||||
$ignore = Library::$sapp->config->get('phinx.ignore', []);
|
||||
if (empty($ignore)) $ignore = ['system_queue', 'system_oplog'];
|
||||
$tables = array_unique(array_diff($tables, $ignore, ['migrations']));
|
||||
|
||||
// 创建数据库记录安装脚本
|
||||
$this->setQueueMessage(4, 1, '开始创建数据包安装脚本!');
|
||||
$phinx = PhinxExtend::create2backup($tables);
|
||||
$target = syspath("database/migrations/{$phinx['file']}");
|
||||
if (file_put_contents($target, $phinx['text']) !== false) {
|
||||
$this->setQueueMessage(4, 2, '成功创建数据包安装脚本!');
|
||||
return true;
|
||||
} else {
|
||||
$this->setQueueMessage(4, 2, '创建数据包安装脚本失败!');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
160
plugin/think-library/src/support/command/Publish.php
Normal file
160
plugin/think-library/src/support/command/Publish.php
Normal file
@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\support\command;
|
||||
|
||||
use think\admin\extend\ToolsExtend;
|
||||
use think\admin\service\ModuleService;
|
||||
use think\admin\service\RuntimeService;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
|
||||
/**
|
||||
* 组件安装指令
|
||||
* @class Publish
|
||||
* @package think\admin\support\command
|
||||
*/
|
||||
class Publish extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* 任务参数配置
|
||||
* @return void
|
||||
*/
|
||||
public function configure()
|
||||
{
|
||||
$this->setName('xadmin:publish');
|
||||
$this->addOption('force', 'f', Option::VALUE_NONE, 'Overwrite any existing files');
|
||||
$this->addOption('migrate', 'm', Option::VALUE_NONE, 'Execute phinx database script');
|
||||
$this->setDescription('Publish Plugs and Config Assets for ThinkAdmin');
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务合并执行
|
||||
* @param \think\console\Input $input
|
||||
* @param \think\console\Output $output
|
||||
* @return null|void
|
||||
*/
|
||||
public function execute(Input $input, Output $output)
|
||||
{
|
||||
RuntimeService::clear(false);
|
||||
$this->parse()->plugin()->output->writeln('<info>Succeed!</info>');
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装数据库
|
||||
* @return $this
|
||||
*/
|
||||
private function plugin(): Publish
|
||||
{
|
||||
// 执行子应用安装
|
||||
$force = boolval($this->input->getOption('force'));
|
||||
foreach (ModuleService::getModules() as $appName) {
|
||||
$appPath = $this->app->getBasePath() . $appName;
|
||||
is_dir($appPath) && $this->copy($appPath, $force);
|
||||
}
|
||||
// 执行数据库脚本
|
||||
if ($this->input->getOption('migrate')) {
|
||||
$this->app->console->call('migrate:run', [], 'console');
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化组件文件
|
||||
* @param string $copy 应用资源目录
|
||||
* @param boolean $force 是否强制替换
|
||||
* @return void
|
||||
*/
|
||||
private function copy(string $copy, bool $force = false)
|
||||
{
|
||||
// 复制系统配置文件
|
||||
$frdir = rtrim($copy, '\\/') . DIRECTORY_SEPARATOR . 'config';
|
||||
ToolsExtend::copyfile($frdir, syspath('config'), [], $force, false);
|
||||
|
||||
// 复制静态资料文件
|
||||
$frdir = rtrim($copy, '\\/') . DIRECTORY_SEPARATOR . 'public';
|
||||
ToolsExtend::copyfile($frdir, syspath('public'), [], true, false);
|
||||
|
||||
// 复制数据库脚本
|
||||
$frdir = rtrim($copy, '\\/') . DIRECTORY_SEPARATOR . 'database';
|
||||
ToolsExtend::copyfile($frdir, syspath('database/migrations'), [], $force, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 json 包
|
||||
* @return $this
|
||||
*/
|
||||
private function parse(): Publish
|
||||
{
|
||||
[$services, $versions] = [[], []];
|
||||
if (is_file($file = syspath('vendor/composer/installed.json'))) {
|
||||
$packages = json_decode(@file_get_contents($file), true);
|
||||
foreach ($packages['packages'] ?? $packages as $package) {
|
||||
// 生成组件版本
|
||||
$type = $package['type'] ?? '';
|
||||
$config = $package['extra']['config'] ?? [];
|
||||
$versions[$package['name']] = [
|
||||
'type' => $config['type'] ?? ($type === 'think-admin-plugin' ? 'plugin' : 'library'),
|
||||
'name' => $config['name'] ?? ($package['name'] ?? ''),
|
||||
'icon' => $config['icon'] ?? '',
|
||||
'cover' => $config['cover'] ?? '',
|
||||
'license' => (array)($config['license'] ?? ($package['license'] ?? [])),
|
||||
'version' => $config['version'] ?? ($package['version'] ?? ''),
|
||||
'homepage' => $config['homepage'] ?? ($package['homepage'] ?? ''),
|
||||
'document' => $config['document'] ?? ($package['document'] ?? ''),
|
||||
'platforms' => $config['platforms'] ?? [],
|
||||
'description' => $config['description'] ?? ($package['description'] ?? ''),
|
||||
];
|
||||
// 生成服务配置
|
||||
if (!empty($package['extra']['think']['services'])) {
|
||||
$services = array_merge($services, (array)$package['extra']['think']['services']);
|
||||
}
|
||||
// 复制配置文件
|
||||
if (!empty($package['extra']['think']['config'])) {
|
||||
$configPath = $this->app->getConfigPath();
|
||||
$installPath = syspath("vendor/{$package['name']}/");
|
||||
foreach ((array)$package['extra']['think']['config'] as $name => $file) {
|
||||
if (is_file($target = $configPath . $name . '.php')) {
|
||||
$this->output->info("File {$target} exist!");
|
||||
continue;
|
||||
}
|
||||
if (!is_file($source = $installPath . $file)) {
|
||||
$this->output->info("File {$source} not exist!");
|
||||
continue;
|
||||
}
|
||||
copy($source, $target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 写入服务配置
|
||||
$header = "// Automatically Generated At: " . date('Y-m-d H:i:s') . PHP_EOL . 'declare(strict_types=1);';
|
||||
$content = '<?php' . PHP_EOL . $header . PHP_EOL . 'return ' . var_export($services, true) . ';';
|
||||
@file_put_contents(syspath('vendor/services.php'), $content);
|
||||
|
||||
// 写入组件版本
|
||||
$content = '<?php' . PHP_EOL . $header . PHP_EOL . 'return ' . var_export($versions, true) . ';';
|
||||
@file_put_contents(syspath('vendor/versions.php'), preg_replace('#\s+=>\s+array\s+\(#m', ' => array (', $content));
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
378
plugin/think-library/src/support/command/Queue.php
Normal file
378
plugin/think-library/src/support/command/Queue.php
Normal file
@ -0,0 +1,378 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\support\command;
|
||||
|
||||
use Error;
|
||||
use Exception;
|
||||
use Psr\Log\NullLogger;
|
||||
use think\admin\Command;
|
||||
use think\admin\model\SystemQueue;
|
||||
use think\admin\service\QueueService;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 异步任务管理指令
|
||||
* @class Queue
|
||||
* @package think\admin\support\command
|
||||
*/
|
||||
class Queue extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* 任务等待处理
|
||||
* @var integer
|
||||
*/
|
||||
const STATE_WAIT = 1;
|
||||
|
||||
/**
|
||||
* 任务正在处理
|
||||
* @var integer
|
||||
*/
|
||||
const STATE_LOCK = 2;
|
||||
|
||||
/**
|
||||
* 任务处理完成
|
||||
* @var integer
|
||||
*/
|
||||
const STATE_DONE = 3;
|
||||
|
||||
/**
|
||||
* 任务处理失败
|
||||
* @var integer
|
||||
*/
|
||||
const STATE_ERROR = 4;
|
||||
|
||||
/**
|
||||
* 监听进程指令
|
||||
* @var string
|
||||
*/
|
||||
const QUEUE_LISTEN = 'xadmin:queue listen';
|
||||
|
||||
/**
|
||||
* 当前任务编号
|
||||
* @var string
|
||||
*/
|
||||
protected $code;
|
||||
|
||||
/**
|
||||
* 指令任务配置
|
||||
*/
|
||||
public function configure()
|
||||
{
|
||||
$this->setName('xadmin:queue');
|
||||
$this->addOption('host', '-H', Option::VALUE_OPTIONAL, 'The host of WebServer.');
|
||||
$this->addOption('port', '-p', Option::VALUE_OPTIONAL, 'The port of WebServer.');
|
||||
$this->addOption('daemon', 'd', Option::VALUE_NONE, 'The queue listen in daemon mode');
|
||||
$this->addArgument('action', Argument::OPTIONAL, 'stop|start|status|query|listen|clean|dorun|webstop|webstart|webstatus', 'listen');
|
||||
$this->addArgument('code', Argument::OPTIONAL, 'Taskcode');
|
||||
$this->addArgument('spts', Argument::OPTIONAL, 'Separator');
|
||||
$this->setDescription('Asynchronous Command Queue Task for ThinkAdmin');
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务执行入口
|
||||
* @param Input $input
|
||||
* @param Output $output
|
||||
* @return void
|
||||
*/
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$action = $input->hasOption('daemon') ? 'start' : $input->getArgument('action');
|
||||
if (method_exists($this, $method = "{$action}Action")) return $this->$method();
|
||||
$this->output->error("># Wrong operation, Allow stop|start|status|query|listen|clean|dorun|webstop|webstart|webstatus");
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止 WebServer 调试进程
|
||||
*/
|
||||
protected function webStopAction()
|
||||
{
|
||||
$root = syspath('public/');
|
||||
if (count($result = $this->process->query("{$root} {$root}router.php")) < 1) {
|
||||
$this->output->writeln("># There are no WebServer processes to stop");
|
||||
} else foreach ($result as $item) {
|
||||
$this->process->close(intval($item['pid']));
|
||||
$this->output->writeln("># Successfully sent end signal to process {$item['pid']}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动 WebServer 调试进程
|
||||
*/
|
||||
protected function webStartAction()
|
||||
{
|
||||
$prot = 'http';
|
||||
$port = $this->input->getOption('port') ?: '80';
|
||||
$host = $this->input->getOption('host') ?: '127.0.0.1';
|
||||
$root = syspath('public' . DIRECTORY_SEPARATOR);
|
||||
$command = "php -S {$host}:{$port} -t {$root} {$root}router.php";
|
||||
$this->output->comment(">$ {$command}");
|
||||
if (count($result = $this->process->query($command)) > 0) {
|
||||
if ($this->process->isWin()) $this->process->exec("start {$prot}://{$host}:{$port}");
|
||||
$this->output->writeln("># WebServer process already exist for pid {$result[0]['pid']}");
|
||||
} else {
|
||||
$this->process->create($command, 2000);
|
||||
if (count($result = $this->process->query($command)) > 0) {
|
||||
$this->output->writeln("># WebServer process started successfully for pid {$result[0]['pid']}");
|
||||
if ($this->process->isWin()) $this->process->exec("start {$prot}://{$host}:{$port}");
|
||||
} else {
|
||||
$this->output->writeln('># WebServer process failed to start');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看 WebServer 调试进程
|
||||
*/
|
||||
protected function webStatusAction()
|
||||
{
|
||||
$root = syspath('public' . DIRECTORY_SEPARATOR);
|
||||
if (count($result = $this->process->query("{$root} {$root}router.php")) > 0) {
|
||||
$this->output->comment(">$ {$result[0]['cmd']}");
|
||||
$this->output->writeln("># WebServer process {$result[0]['pid']} running");
|
||||
} else {
|
||||
$this->output->writeln("># The WebServer process is not running");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止所有任务
|
||||
*/
|
||||
protected function stopAction()
|
||||
{
|
||||
if (count($result = $this->process->thinkQuery('xadmin:queue')) < 1) {
|
||||
$this->output->writeln("># There are no task processes to stop");
|
||||
} else foreach ($result as $item) {
|
||||
$this->process->close(intval($item['pid']));
|
||||
$this->output->writeln("># Successfully sent end signal to process {$item['pid']}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动后台任务
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
protected function startAction()
|
||||
{
|
||||
SystemQueue::mk()->count();
|
||||
$this->output->comment(">$ {$this->process->think(static::QUEUE_LISTEN)}");
|
||||
if (count($result = $this->process->thinkQuery(static::QUEUE_LISTEN)) > 0) {
|
||||
if (is_file($lock = syspath('runtime/cache/time.queue')) && intval(file_get_contents($lock)) + 60 < time()) {
|
||||
$this->output->writeln("># The task monitoring delay has exceeded 60 seconds, and the monitoring will be restarted.");
|
||||
$this->process->close(intval($result[0]['pid'])) && $this->process->thinkExec(static::QUEUE_LISTEN, 1000);
|
||||
} else {
|
||||
$this->output->writeln("># Queue daemons already exist for pid {$result[0]['pid']}");
|
||||
}
|
||||
} else {
|
||||
$this->process->thinkExec(static::QUEUE_LISTEN, 1000);
|
||||
if (count($result = $this->process->thinkQuery(static::QUEUE_LISTEN)) > 0) {
|
||||
$this->output->writeln("># Queue daemons started successfully for pid {$result[0]['pid']}");
|
||||
} else {
|
||||
$this->output->writeln("># Queue daemons failed to start");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有任务
|
||||
*/
|
||||
protected function queryAction()
|
||||
{
|
||||
$items = $this->process->thinkQuery('xadmin:queue');
|
||||
if (count($items) > 0) foreach ($items as $item) {
|
||||
$this->output->writeln("># {$item['pid']}\t{$item['cmd']}");
|
||||
} else {
|
||||
$this->output->writeln('># No related task process found');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理所有任务
|
||||
* @throws \think\admin\Exception
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
protected function cleanAction()
|
||||
{
|
||||
// 清理任务历史记录
|
||||
$days = intval(sysconf('base.queue_clean_days|raw') ?: 7);
|
||||
$clean = SystemQueue::mk()->where('exec_time', '<', time() - $days * 24 * 3600)->delete();
|
||||
// 标记超过 1 小时未完成的任务为失败状态,循环任务失败重置
|
||||
$map1 = [['loops_time', '>', 0], ['status', '=', static::STATE_ERROR]]; // 执行失败的循环任务
|
||||
$map2 = [['exec_time', '<', time() - 3600], ['status', '=', static::STATE_LOCK]]; // 执行超时的任务
|
||||
[$timeout, $loops, $total] = [0, 0, SystemQueue::mk()->whereOr([$map1, $map2])->count()];
|
||||
foreach (SystemQueue::mk()->whereOr([$map1, $map2])->cursor() as $queue) {
|
||||
$queue['loops_time'] > 0 ? $loops++ : $timeout++;
|
||||
if ($queue['loops_time'] > 0) {
|
||||
$this->queue->message($total, $timeout + $loops, "正在重置任务 {$queue['code']} 为运行");
|
||||
[$status, $message] = [static::STATE_WAIT, $queue['status'] === static::STATE_ERROR ? '任务执行失败,已自动重置任务!' : '任务执行超时,已自动重置任务!'];
|
||||
} else {
|
||||
$this->queue->message($total, $timeout + $loops, "正在标记任务 {$queue['code']} 为超时");
|
||||
[$status, $message] = [static::STATE_ERROR, '任务执行超时,已自动标识为失败!'];
|
||||
}
|
||||
$queue->save(['status' => $status, 'exec_desc' => $message]);
|
||||
}
|
||||
$this->setQueueSuccess("清理 {$clean} 条历史任务,关闭 {$timeout} 条超时任务,重置 {$loops} 条循环任务");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询兼听状态
|
||||
*/
|
||||
protected function statusAction()
|
||||
{
|
||||
if (count($result = $this->process->thinkQuery(static::QUEUE_LISTEN)) > 0) {
|
||||
$this->output->writeln("Listening for main process {$result[0]['pid']} running");
|
||||
} else {
|
||||
$this->output->writeln("The Listening main process is not running");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动任务监听
|
||||
* @return void
|
||||
*/
|
||||
protected function listenAction()
|
||||
{
|
||||
try {
|
||||
set_time_limit(0) && PHP_SAPI !== 'cli' && ignore_user_abort(true);
|
||||
$this->app->db->setLog(new NullLogger());
|
||||
$this->createListenProcess();
|
||||
} catch (Exception $exception) {
|
||||
trace_file($exception) && usleep(3000000);
|
||||
$this->output->write('=============== EXCEPTION ===============');
|
||||
$this->output->write($exception->getMessage());
|
||||
$this->output->writeln('=============== TRY-REBOOT ===============');
|
||||
$this->createListenProcess();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务监听
|
||||
* @return void
|
||||
*/
|
||||
private function createListenProcess()
|
||||
{
|
||||
$this->output->writeln("\n\tYou can exit with <info>`CTRL-C`</info>");
|
||||
$this->output->writeln('=============== LISTENING ===============');
|
||||
while (true) {
|
||||
@file_put_contents(syspath('runtime/cache/time.queue'), strval(time()));
|
||||
[$map, $start] = [[['status', '=', static::STATE_WAIT], ['exec_time', '<=', time()]], microtime(true)];
|
||||
foreach (SystemQueue::mk()->where($map)->order('exec_time asc')->cursor() as $queue) try {
|
||||
$args = "xadmin:queue dorun {$queue['code']} -";
|
||||
$this->output->comment(">$ {$this->process->think($args)}");
|
||||
if (count($this->process->thinkQuery($args)) > 0) {
|
||||
$this->output->writeln("># Already in progress -> [{$queue['code']}] {$queue['title']}");
|
||||
} else {
|
||||
$this->process->thinkExec($args);
|
||||
$this->output->writeln("># Created new process -> [{$queue['code']}] {$queue['title']}");
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
$queue->save(['status' => static::STATE_ERROR, 'outer_time' => time(), 'exec_desc' => $exception->getMessage()]);
|
||||
$this->output->error("># Execution failed -> [{$queue['code']}] {$queue['title']},{$exception->getMessage()}");
|
||||
}
|
||||
if (microtime(true) < $start + 1) usleep(1000000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行指定任务
|
||||
* @return void
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
protected function doRunAction()
|
||||
{
|
||||
$this->code = trim($this->input->getArgument('code'));
|
||||
if (empty($this->code)) {
|
||||
$this->output->error('Task number needs to be specified for task execution');
|
||||
} else try {
|
||||
set_time_limit(0) && PHP_SAPI !== 'cli' && ignore_user_abort(true);
|
||||
$this->queue->initialize($this->code);
|
||||
if (empty($this->queue->record) || intval($this->queue->record->getAttr('status')) !== static::STATE_WAIT) {
|
||||
// 这里不做任何处理(该任务可能在其它地方已经在执行)
|
||||
$this->output->warning("The or status of task {$this->code} is abnormal");
|
||||
} else {
|
||||
// 锁定任务状态,防止任务再次被执行
|
||||
SystemQueue::mk()->strict(false)->where(['code' => $this->code])->inc('attempts')->update([
|
||||
'enter_time' => microtime(true), 'outer_time' => 0, 'exec_pid' => getmypid(), 'exec_desc' => '', 'status' => static::STATE_LOCK,
|
||||
]);
|
||||
$this->queue->progress(2, '>>> 任务处理开始 <<<', '0');
|
||||
// 执行任务内容
|
||||
defined('WorkQueueCall') or define('WorkQueueCall', true);
|
||||
defined('WorkQueueCode') or define('WorkQueueCode', $this->code);
|
||||
if (class_exists($command = $this->queue->record->getAttr('command'))) {
|
||||
// 自定义任务,支持返回消息(支持异常结束,异常码可选择 3|4 设置任务状态)
|
||||
/**@var \think\admin\Queue|QueueService $class */
|
||||
$class = $this->app->make($command, [], true);
|
||||
if ($class instanceof \think\admin\Queue) {
|
||||
$this->updateQueue(static::STATE_DONE, $class->initialize($this->queue)->execute($this->queue->data) ?: '');
|
||||
} elseif ($class instanceof QueueService) {
|
||||
$this->updateQueue(static::STATE_DONE, $class->initialize($this->queue->code)->execute($this->queue->data) ?: '');
|
||||
} else {
|
||||
throw new \think\admin\Exception("自定义 {$command} 未继承 think\admin\Queue 或 think\admin\service\QueueService");
|
||||
}
|
||||
} else {
|
||||
// 自定义指令,不支持返回消息(支持异常结束,异常码可选择 3|4 设置任务状态)
|
||||
$attr = explode(' ', trim(preg_replace('|\s+|', ' ', $command)));
|
||||
$this->updateQueue(static::STATE_DONE, $this->app->console->call(array_shift($attr), $attr)->fetch(), false);
|
||||
}
|
||||
}
|
||||
} catch (Exception|Throwable|Error $exception) {
|
||||
$isDone = intval($exception->getCode()) === static::STATE_DONE;
|
||||
$this->updateQueue($isDone ? static::STATE_DONE : static::STATE_ERROR, $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改当前任务状态
|
||||
* @param integer $status 任务状态
|
||||
* @param string $message 消息内容
|
||||
* @param boolean $isSplit 是否分隔
|
||||
* @throws \think\admin\Exception
|
||||
*/
|
||||
private function updateQueue(int $status, string $message, bool $isSplit = true)
|
||||
{
|
||||
// 更新当前任务
|
||||
$desc = $isSplit ? explode("\n", trim($message)) : [$message];
|
||||
SystemQueue::mk()->strict(false)->where(['code' => $this->code])->update([
|
||||
'status' => $status, 'outer_time' => microtime(true), 'exec_pid' => getmypid(), 'exec_desc' => $desc[0],
|
||||
]);
|
||||
$this->process->message($message);
|
||||
// 任务进度标记
|
||||
if (!empty($desc[0])) {
|
||||
$this->queue->progress($status, ">>> {$desc[0]} <<<");
|
||||
}
|
||||
// 任务状态标记
|
||||
if ($status === static::STATE_DONE) {
|
||||
$this->queue->progress($status, '>>> 任务处理完成 <<<', '100.00');
|
||||
} elseif ($status === static::STATE_ERROR) {
|
||||
$this->queue->progress($status, '>>> 任务处理失败 <<<');
|
||||
}
|
||||
// 注册循环任务
|
||||
if (($time = intval($this->queue->record->getAttr('loops_time'))) > 0) try {
|
||||
$this->queue->initialize($this->code)->reset($time);
|
||||
} catch (Exception|Throwable|Error $exception) {
|
||||
$this->app->log->error("Queue {$this->queue->record->getAttr('code')} Loops Failed. {$exception->getMessage()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
78
plugin/think-library/src/support/command/Replace.php
Normal file
78
plugin/think-library/src/support/command/Replace.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\support\command;
|
||||
|
||||
use think\admin\Command;
|
||||
use think\admin\service\SystemService;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\Output;
|
||||
use think\helper\Str;
|
||||
|
||||
/**
|
||||
* 数据库字符替换
|
||||
* @class Replace
|
||||
* @package think\admin\support\command
|
||||
*/
|
||||
class Replace extends Command
|
||||
{
|
||||
/**
|
||||
* 指令任务配置
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('xadmin:replace');
|
||||
$this->addArgument('search', Argument::OPTIONAL, '查找替换的字符内容', '');
|
||||
$this->addArgument('replace', Argument::OPTIONAL, '目标替换的字符内容', '');
|
||||
$this->setDescription('Database Character Field Replace for ThinkAdmin');
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务执行入口
|
||||
* @param \think\console\Input $input
|
||||
* @param \think\console\Output $output
|
||||
* @return void
|
||||
* @throws \think\admin\Exception
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$search = $input->getArgument('search');
|
||||
$repalce = $input->getArgument('replace');
|
||||
if ($search === '') $this->setQueueError('查找替换字符内容不能为空!');
|
||||
if ($repalce === '') $this->setQueueError('目标替换字符内容不能为空!');
|
||||
[$tables, $total, $count] = SystemService::getTables();
|
||||
foreach ($tables as $table) {
|
||||
$data = [];
|
||||
$this->setQueueMessage($total, ++$count, sprintf("准备替换数据表 %s", Str::studly($table)));
|
||||
foreach ($this->app->db->table($table)->getFields() as $field => $attrs) {
|
||||
if (preg_match('/char|text/', $attrs['type'])) {
|
||||
$data[$field] = $this->app->db->raw(sprintf('REPLACE(`%s`,"%s","%s")', $field, $search, $repalce));
|
||||
}
|
||||
}
|
||||
if (count($data) > 0) {
|
||||
$this->app->db->table($table)->master()->where('1=1')->update($data);
|
||||
$this->setQueueMessage($total, $count, sprintf("成功替换数据表 %s", Str::studly($table)), 1);
|
||||
} else {
|
||||
$this->setQueueMessage($total, $count, sprintf("无需替换数据表 %s", Str::studly($table)), 1);
|
||||
}
|
||||
}
|
||||
$this->setQueueSuccess("批量替换 {$total} 张数据表成功");
|
||||
}
|
||||
}
|
||||
88
plugin/think-library/src/support/command/Sysmenu.php
Normal file
88
plugin/think-library/src/support/command/Sysmenu.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\support\command;
|
||||
|
||||
use think\admin\Command;
|
||||
use think\admin\extend\DataExtend;
|
||||
use think\admin\model\SystemMenu;
|
||||
|
||||
/**
|
||||
* 重置并清理系统菜单
|
||||
* @class Sysmenu
|
||||
* @package think\admin\support\command
|
||||
*/
|
||||
class Sysmenu extends Command
|
||||
{
|
||||
/**
|
||||
* 指令任务配置
|
||||
*/
|
||||
public function configure()
|
||||
{
|
||||
$this->setName('xadmin:sysmenu');
|
||||
$this->setDescription('Clean and Reset System Menu Data for ThinkAdmin');
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务执行入口
|
||||
* @return void
|
||||
* @throws \think\admin\Exception
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$query = SystemMenu::mQuery()->where(['status' => 1]);
|
||||
$menus = $query->db()->order('sort desc,id asc')->select()->toArray();
|
||||
[$total, $count] = [count($menus), 0, $query->empty()];
|
||||
$this->setQueueMessage($total, 0, '开始重置系统菜单编号...');
|
||||
foreach (DataExtend::arr2tree($menus) as $sub1) {
|
||||
$pid1 = $this->write($sub1);
|
||||
$this->setQueueMessage($total, ++$count, "重写1级菜单:{$sub1['title']}");
|
||||
if (!empty($sub1['sub'])) foreach ($sub1['sub'] as $sub2) {
|
||||
$pid2 = $this->write($sub2, $pid1);
|
||||
$this->setQueueMessage($total, ++$count, "重写2级菜单:-> {$sub2['title']}");
|
||||
if (!empty($sub2['sub'])) foreach ($sub2['sub'] as $sub3) {
|
||||
$this->write($sub3, $pid2);
|
||||
$this->setQueueMessage($total, ++$count, "重写3级菜单:-> -> {$sub3['title']}");
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->setQueueMessage($total, $count, "完成重置系统菜单编号!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入单项菜单数据
|
||||
* @param array $arr 单项菜单数据
|
||||
* @param mixed $pid 上级菜单编号
|
||||
* @return int|string
|
||||
*/
|
||||
private function write(array $arr, $pid = 0)
|
||||
{
|
||||
return SystemMenu::mk()->insertGetId([
|
||||
'pid' => $pid,
|
||||
'url' => $arr['url'],
|
||||
'icon' => $arr['icon'],
|
||||
'node' => $arr['node'],
|
||||
'title' => $arr['title'],
|
||||
'params' => $arr['params'],
|
||||
'target' => $arr['target'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
113
plugin/think-library/src/support/middleware/JwtSession.php
Normal file
113
plugin/think-library/src/support/middleware/JwtSession.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\support\middleware;
|
||||
|
||||
use Closure;
|
||||
use think\admin\Exception;
|
||||
use think\admin\extend\JwtExtend;
|
||||
use think\App;
|
||||
use think\exception\HttpResponseException;
|
||||
use think\Request;
|
||||
use think\Response;
|
||||
|
||||
/**
|
||||
* 兼容会话中间键
|
||||
* @class JwtSession
|
||||
* @package think\admin\support\middleware
|
||||
*/
|
||||
class JwtSession
|
||||
{
|
||||
/**
|
||||
* 当前 App 对象
|
||||
* @var \think\App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 当前 Session 对象
|
||||
* @var \think\Session
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
* @param \think\App $app
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->session = $app->session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 中间键处理
|
||||
* @param \think\Request $request
|
||||
* @param \Closure $next
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
// 处理 Jwt 请求,请求头存在 jwt-token 字段
|
||||
if (($token = $request->header('jwt-token', ''))) try {
|
||||
if (preg_match('#^\s*([\w\-]+\.[\w\-]+\.[\w\-]+)\s*$#', $token, $match)) {
|
||||
JwtExtend::verify($match[1]);
|
||||
$sessionId = JwtExtend::$sessionId;
|
||||
} else {
|
||||
throw new Exception('令牌格式错误!', 401);
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
throw new HttpResponseException(json(['code' => $exception->getCode(), 'info' => lang($exception->getMessage())]));
|
||||
}
|
||||
|
||||
if (empty($sessionId)) {
|
||||
$varSessionId = $this->app->config->get('session.var_session_id');
|
||||
if ($varSessionId && $request->request($varSessionId)) {
|
||||
$sessionId = $request->request($varSessionId);
|
||||
} else {
|
||||
$sessionId = $request->cookie($this->session->getName());
|
||||
}
|
||||
}
|
||||
|
||||
if ($sessionId) {
|
||||
$this->session->setId($sessionId);
|
||||
}
|
||||
|
||||
// 基础 Session 初始化
|
||||
$this->session->init();
|
||||
$request->withSession($this->session);
|
||||
|
||||
if (empty(JwtExtend::$sessionId)) {
|
||||
// 非 Jwt 会话需写入 Cookie 记录 SessionID
|
||||
$this->app->cookie->set($this->session->getName(), $this->session->getId());
|
||||
}
|
||||
|
||||
// 执行下一步控制器操作
|
||||
return $next($request)->setSession($this->session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存会话数据
|
||||
* @return void
|
||||
*/
|
||||
public function end()
|
||||
{
|
||||
$this->session->save();
|
||||
JwtExtend::$sessionId = '';
|
||||
}
|
||||
}
|
||||
189
plugin/think-library/src/support/middleware/MultAccess.php
Normal file
189
plugin/think-library/src/support/middleware/MultAccess.php
Normal file
@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\support\middleware;
|
||||
|
||||
use Closure;
|
||||
use think\admin\Plugin;
|
||||
use think\admin\service\NodeService;
|
||||
use think\admin\service\SystemService;
|
||||
use think\App;
|
||||
use think\exception\HttpException;
|
||||
use think\Request;
|
||||
use think\Response;
|
||||
|
||||
/**
|
||||
* 多应用调度中间键
|
||||
* @class MultAccess
|
||||
* @package think\admin\support\middleware
|
||||
*/
|
||||
class MultAccess
|
||||
{
|
||||
/**
|
||||
* 应用实例
|
||||
* @var App
|
||||
*/
|
||||
private $app;
|
||||
|
||||
/**
|
||||
* 应用路径
|
||||
* @var string
|
||||
*/
|
||||
private $appPath;
|
||||
|
||||
/**
|
||||
* 应用空间
|
||||
* @var string
|
||||
*/
|
||||
private $appSpace;
|
||||
|
||||
/**
|
||||
* App constructor.
|
||||
* @param App $app
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* 多应用解析
|
||||
* @param Request $request
|
||||
* @param Closure $next
|
||||
* @return Response
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
[$this->appPath, $this->appSpace] = ['', ''];
|
||||
if (!$this->parseMultiApp()) return $next($request);
|
||||
return $this->app->middleware->pipeline('app')->send($request)->then(function ($request) use ($next) {
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析多应用
|
||||
* @return bool
|
||||
*/
|
||||
private function parseMultiApp(): bool
|
||||
{
|
||||
$defaultApp = $this->app->config->get('route.default_app') ?: 'index';
|
||||
[$script, $pathinfo] = [$this->scriptName(), $this->app->request->pathinfo()];
|
||||
if ($script && !in_array($script, ['index', 'router', 'think'])) {
|
||||
$this->app->request->setPathinfo(preg_replace("#^{$script}\.php(/|\.|$)#i", '', $pathinfo) ?: '/');
|
||||
return $this->setMultiApp($script, true);
|
||||
} else {
|
||||
// 域名绑定处理
|
||||
$domains = $this->app->config->get('app.domain_bind', []);
|
||||
if (!empty($domains)) foreach ([$this->app->request->host(true), $this->app->request->subDomain(), '*'] as $key) {
|
||||
if (isset($domains[$key])) return $this->setMultiApp($domains[$key], true);
|
||||
}
|
||||
$name = current(explode('/', $pathinfo));
|
||||
if (strpos($name, '.')) $name = strstr($name, '.', true);
|
||||
// 应用绑定与插件处理
|
||||
$addons = Plugin::get();
|
||||
$appmap = $this->app->config->get('app.app_map', []);
|
||||
if (isset($appmap[$name])) {
|
||||
$appName = $appmap[$name] instanceof Closure ? (call_user_func_array($appmap[$name], [$this->app]) ?: $name) : $appmap[$name];
|
||||
} elseif ($name && (in_array($name, $appmap) || in_array($name, $this->app->config->get('app.deny_app_list', [])))) {
|
||||
throw new HttpException(404, "app not exists: {$name}");
|
||||
} elseif ($name && isset($appmap['*'])) {
|
||||
$appName = $appmap['*'];
|
||||
} else {
|
||||
$appName = $name ?: $defaultApp;
|
||||
if (!isset($addons[$appName]) && !is_dir($this->app->getBasePath() . $appName)) {
|
||||
return $this->app->config->get('app.app_express', false) && $this->setMultiApp($defaultApp, false);
|
||||
}
|
||||
}
|
||||
// 插件绑定处理
|
||||
if (isset($addons[$appName])) {
|
||||
[$this->appPath, $this->appSpace] = [$addons[$appName]['path'], $addons[$appName]['space']];
|
||||
}
|
||||
if ($name) {
|
||||
$this->app->request->setRoot('/' . $name);
|
||||
$this->app->request->setPathinfo(strpos($pathinfo, '/') ? ltrim(strstr($pathinfo, '/'), '/') : '');
|
||||
}
|
||||
}
|
||||
return $this->setMultiApp($appName ?? $defaultApp, $this->app->http->isBind());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前运行入口名称
|
||||
* @codeCoverageIgnore
|
||||
* @return string
|
||||
*/
|
||||
private function scriptName(): string
|
||||
{
|
||||
$file = $_SERVER['SCRIPT_FILENAME'] ?? ($_SERVER['argv'][0] ?? '');
|
||||
return empty($file) ? '' : pathinfo($file, PATHINFO_FILENAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置应用参数
|
||||
* @param string $appName 应用名称
|
||||
* @param boolean $appBind 应用绑定
|
||||
* @return boolean
|
||||
*/
|
||||
private function setMultiApp(string $appName, bool $appBind): bool
|
||||
{
|
||||
if (is_dir($this->appPath = $this->appPath ?: syspath("app/{$appName}/"))) {
|
||||
// 设置多应用模式
|
||||
$this->app->setNamespace($this->appSpace ?: NodeService::space($appName))->setAppPath($this->appPath);
|
||||
$this->app->http->setBind($appBind)->name($appName)->path($this->appPath)->setRoutePath($this->appPath . 'route' . DIRECTORY_SEPARATOR);
|
||||
// 修改模板参数配置
|
||||
$uris = array_merge($this->app->config->get('view.tpl_replace_string', []), SystemService::uris());
|
||||
$this->app->config->set(['view_path' => $this->appPath . 'view' . DIRECTORY_SEPARATOR, 'tpl_replace_string' => $uris], 'view');
|
||||
// 初始化多应用文件
|
||||
return $this->loadMultiApp($this->appPath);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载应用文件
|
||||
* @param string $appPath 应用路径
|
||||
* @codeCoverageIgnore
|
||||
* @return boolean
|
||||
*/
|
||||
private function loadMultiApp(string $appPath): bool
|
||||
{
|
||||
[$ext, $fmaps] = [$this->app->getConfigExt(), []];
|
||||
if (is_file($file = "{$appPath}common{$ext}")) include_once $file;
|
||||
foreach (glob($appPath . 'config' . DIRECTORY_SEPARATOR . '*' . $ext) as $file) {
|
||||
$this->app->config->load($file, $fmaps[] = pathinfo($file, PATHINFO_FILENAME));
|
||||
}
|
||||
if (in_array('route', $fmaps) && method_exists($this->app->route, 'reload')) {
|
||||
$this->app->route->reload();
|
||||
}
|
||||
if (is_file($file = "{$appPath}provider{$ext}")) {
|
||||
$this->app->bind(include $file);
|
||||
}
|
||||
if (is_file($file = "{$appPath}event{$ext}")) {
|
||||
$this->app->loadEvent(include $file);
|
||||
}
|
||||
if (is_file($file = "{$appPath}middleware{$ext}")) {
|
||||
$this->app->middleware->import(include $file, 'app');
|
||||
}
|
||||
// 重新加载应用语言包
|
||||
if (method_exists($this->app->lang, 'switchLangSet')) {
|
||||
$this->app->lang->switchLangSet($this->app->lang->getLangSet());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
84
plugin/think-library/src/support/middleware/RbacAccess.php
Normal file
84
plugin/think-library/src/support/middleware/RbacAccess.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
// +----------------------------------------------------------------------
|
||||
// | Library for ThinkAdmin
|
||||
// +----------------------------------------------------------------------
|
||||
// | 版权所有 2014~2024 ThinkAdmin [ thinkadmin.top ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | 官方网站: https://thinkadmin.top
|
||||
// +----------------------------------------------------------------------
|
||||
// | 开源协议 ( https://mit-license.org )
|
||||
// | 免费声明 ( https://thinkadmin.top/disclaimer )
|
||||
// +----------------------------------------------------------------------
|
||||
// | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
|
||||
// | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace think\admin\support\middleware;
|
||||
|
||||
use think\admin\service\AdminService;
|
||||
use think\App;
|
||||
use think\exception\HttpResponseException;
|
||||
use think\Request;
|
||||
use think\Response;
|
||||
|
||||
/**
|
||||
* 后台权限中间键
|
||||
* @class RbacAccess
|
||||
* @package think\admin\support\middleware
|
||||
*/
|
||||
class RbacAccess
|
||||
{
|
||||
/**
|
||||
* 当前 App 对象
|
||||
* @var \think\App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
* @param \think\App $app
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* 中间键处理
|
||||
* @param \think\Request $request
|
||||
* @param \Closure $next
|
||||
* @return \think\Response
|
||||
*/
|
||||
public function handle(Request $request, \Closure $next): Response
|
||||
{
|
||||
// HTTP.LANG 语言包处理
|
||||
$langSet = $this->app->lang->getLangSet();
|
||||
if (is_file($file = dirname(__DIR__, 2) . "/lang/{$langSet}.php")) {
|
||||
$this->app->lang->load($file, $langSet);
|
||||
}
|
||||
|
||||
// 动态加载全局语言包
|
||||
if (is_file($file = syspath("lang/{$langSet}.php"))) {
|
||||
$this->app->lang->load($file, $langSet);
|
||||
}
|
||||
|
||||
// 跳过忽略配置应用 或 有权限访问
|
||||
$ignore = $this->app->config->get('app.rbac_ignore', []);
|
||||
if (in_array($this->app->http->getName(), $ignore) || AdminService::check()) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
// 无权限已登录,提示异常
|
||||
if (AdminService::isLogin()) {
|
||||
throw new HttpResponseException(json(['code' => 0, 'info' => lang('禁用访问!')]));
|
||||
}
|
||||
|
||||
// 无权限未登录,跳转登录
|
||||
$loginUrl = $this->app->config->get('app.rbac_login') ?: 'admin/login/index';
|
||||
$loginPage = preg_match('#^(/|https?://)#', $loginUrl) ? $loginUrl : sysuri($loginUrl);
|
||||
throw new HttpResponseException(json(['code' => 0, 'info' => lang('请重新登录!'), 'url' => $loginPage]));
|
||||
}
|
||||
}
|
||||
22
plugin/think-library/tests/CodeTest.php
Normal file
22
plugin/think-library/tests/CodeTest.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace think\admin\tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use think\admin\extend\CodeExtend;
|
||||
|
||||
class CodeTest extends TestCase
|
||||
{
|
||||
public function testUuidCreate()
|
||||
{
|
||||
$uuid = CodeExtend::uuid();
|
||||
$this->assertNotEmpty(preg_match('|^[a-z0-9]{8}-([a-z0-9]{4}-){3}[a-z0-9]{12}$|i', $uuid));
|
||||
}
|
||||
|
||||
public function testEncode()
|
||||
{
|
||||
$value = '235215321351235123dasfdasfasdfas';
|
||||
$encode = CodeExtend::encrypt($value, 'thinkadmin');
|
||||
$this->assertEquals($value, CodeExtend::decrypt($encode, 'thinkadmin'), '验证加密解密');
|
||||
}
|
||||
}
|
||||
18
plugin/think-library/tests/JwtTest.php
Normal file
18
plugin/think-library/tests/JwtTest.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace think\admin\tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use think\admin\extend\JwtExtend;
|
||||
|
||||
class JwtTest extends TestCase
|
||||
{
|
||||
public function testJwtCreateAndVerify()
|
||||
{
|
||||
$jwtkey = 'thinkadmin';
|
||||
$testdata = ['user' => 'admin' . mt_rand(0, 1000), 'iss' => 'thinkadmin.top', 'exp' => time() + 30];
|
||||
$token = JwtExtend::token($testdata, $jwtkey);
|
||||
$result = JwtExtend::verify($token, $jwtkey);
|
||||
$this->assertEquals($testdata['user'], $result['user']);
|
||||
}
|
||||
}
|
||||
14
plugin/think-library/tests/ModelTest.php
Normal file
14
plugin/think-library/tests/ModelTest.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace think\admin\tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use think\admin\model\SystemUser;
|
||||
|
||||
class ModelTest extends TestCase
|
||||
{
|
||||
public function testVirtualModel()
|
||||
{
|
||||
$this->assertEquals(m('SystemUser')->getTable(), SystemUser::mk()->getTable(), '动态模型测试');
|
||||
}
|
||||
}
|
||||
19
plugin/think-library/tests/StorageTest.php
Normal file
19
plugin/think-library/tests/StorageTest.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace think\admin\tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class StorageTest extends TestCase
|
||||
{
|
||||
public function testInit()
|
||||
{
|
||||
$this->assertEquals(1, 1);
|
||||
}
|
||||
// public function testAlist()
|
||||
// {
|
||||
// $alist = AlistStorage::instance();
|
||||
// $alist->set('test.tt', $content = uniqid());
|
||||
// $this->assertEquals($alist->get('test.tt'), $content);
|
||||
// }
|
||||
}
|
||||
22
plugin/think-library/tests/bootstrap.php
Normal file
22
plugin/think-library/tests/bootstrap.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use think\facade\Db;
|
||||
|
||||
include_once dirname(__DIR__) . '/vendor/autoload.php';
|
||||
include_once dirname(__DIR__) . '/vendor/topthink/framework/src/helper.php';
|
||||
|
||||
Db::setConfig([
|
||||
'default' => 'mysql',
|
||||
'connections' => [
|
||||
'mysql' => [
|
||||
'type' => 'mysql',
|
||||
'hostname' => '127.0.0.1',
|
||||
'database' => 'admin_v6',
|
||||
'username' => 'admin_v6',
|
||||
'password' => 'FbYBHcWKr2',
|
||||
'hostport' => '3306',
|
||||
'charset' => 'utf8mb4',
|
||||
'debug' => true,
|
||||
],
|
||||
],
|
||||
]);
|
||||
Loading…
x
Reference in New Issue
Block a user