540 lines
17 KiB
PHP
540 lines
17 KiB
PHP
<?php
|
||
|
||
namespace app\common\exception;
|
||
|
||
use DateTime;
|
||
use think\facade\Request;
|
||
use think\facade\Log;
|
||
use think\helper\Arr;
|
||
use think\facade\Db;
|
||
use app\common\exception\Sort;
|
||
use app\common\arw\adjfut\src\Exception\ErrorMsg;
|
||
|
||
class Tool
|
||
{
|
||
/**
|
||
* 生成配置项
|
||
*
|
||
* @param array $config
|
||
* @param callable|string $cb 自定义合并策略
|
||
* @return callable
|
||
* @date 2022-07-29
|
||
* @example
|
||
* @author arw
|
||
* @since 1.0.0
|
||
*/
|
||
public static function generateConfig(array $config, $cb = 'array_merge'): callable
|
||
{
|
||
return function (...$argv) use ($cb, $config) {
|
||
array_unshift($argv, $config);
|
||
return call_user_func_array($cb, $argv);
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 构建url
|
||
*
|
||
* @param string $url
|
||
* @param array $query
|
||
* @param boolean $decode
|
||
* @return string
|
||
* @date 2023-01-04
|
||
* @example
|
||
* @author arw
|
||
* @since 1.0.0
|
||
*/
|
||
public static function buildUrl(string $url, array $query = [], bool $decode = true): string
|
||
{
|
||
$parse = parse_url($url);
|
||
$parse_query = Arr::get($parse, 'query', '');
|
||
$_query = [];
|
||
parse_str($parse_query, $_query);
|
||
if (!is_array($_query)) {
|
||
$_query = [];
|
||
}
|
||
$_query = array_merge($_query, $query);
|
||
$_url = str_replace('?' . $parse_query, '', $url);
|
||
$_url .= '?';
|
||
$_url .= http_build_query($_query);
|
||
if ($decode) {
|
||
$_url = urldecode($_url);
|
||
}
|
||
return $_url;
|
||
}
|
||
|
||
/**
|
||
* 写入通道日志
|
||
*
|
||
* @param string $channel
|
||
* @param string $type
|
||
* @param array $data
|
||
* @return void
|
||
* @date 2022-07-29
|
||
* @example
|
||
* @author arw
|
||
* @since 1.0.0
|
||
*/
|
||
public static function writeChannelLog(string $channel, string $type, array $data): void
|
||
{
|
||
Log::channel($channel)->$type(json_encode($data, JSON_UNESCAPED_UNICODE));
|
||
}
|
||
|
||
/**
|
||
* 字段映射
|
||
*
|
||
* @param array $data
|
||
* @param array $map
|
||
* @param array $options
|
||
* @return array
|
||
* @date 2022-06-23
|
||
* @example
|
||
* @author arw
|
||
* @since 1.0.0
|
||
*/
|
||
public static function fieldMap(array $data, array $map, array $options = []): array
|
||
{
|
||
$prefix = Arr::get($options, 'prefix', '');
|
||
$result = [];
|
||
foreach ($data as $key => $value) {
|
||
if (isset($map[$key])) {
|
||
$key = $map[$key];
|
||
}
|
||
if ($prefix) {
|
||
$key = $prefix . $key;
|
||
}
|
||
$result[$key] = $value;
|
||
}
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 转换日期格式
|
||
*
|
||
* @param string $datetime
|
||
* @param string $format
|
||
* @return string
|
||
* @date 2022-04-20
|
||
* @example
|
||
* @author arw
|
||
* @since 1.0.0
|
||
*/
|
||
public static function conversionDateTime($datetime, string $format): string
|
||
{
|
||
try {
|
||
$date = new DateTime($datetime);
|
||
return $date->format($format);
|
||
} catch (\Throwable $th) {
|
||
throw new ErrorMsg('日期格式非法', 1);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成唯一id
|
||
*
|
||
* @param string|array $value
|
||
* @return string
|
||
* @date 2022-03-24
|
||
* @example
|
||
* @author arw
|
||
* @since 1.0.0
|
||
*/
|
||
public static function generateGuid($value = ''): string
|
||
{
|
||
$str = '';
|
||
if (is_array($value)) {
|
||
$str = json_encode($value);
|
||
}
|
||
$charid = strtolower(md5($str . uniqid('', true)));
|
||
$hyphen = chr(45); // '-'
|
||
$uuid = //chr(123)// '{'
|
||
substr($charid, 0, 8) . $hyphen
|
||
. substr($charid, 8, 4) . $hyphen
|
||
. substr($charid, 12, 4) . $hyphen
|
||
. substr($charid, 16, 4) . $hyphen
|
||
. substr($charid, 20, 12);
|
||
//.chr(125);// '}'
|
||
return $uuid;
|
||
}
|
||
|
||
|
||
/**
|
||
* 清空(擦除)缓冲区并关闭输出缓冲
|
||
*
|
||
* @return void
|
||
*/
|
||
public static function obEndClean(): void
|
||
{
|
||
try {
|
||
while (ob_get_level() > 0) {
|
||
ob_end_clean();
|
||
}
|
||
} catch (\Throwable $th) {
|
||
//throw $th;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 自动创建文件夹
|
||
*
|
||
* @param string $path 文件夹路径
|
||
* @param integer $mode 权限
|
||
* @return boolean
|
||
*/
|
||
public static function mkdir(string $path, int $mode = 0755): bool
|
||
{
|
||
$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
|
||
$temp = explode(DIRECTORY_SEPARATOR, $path);
|
||
if (preg_match("/\./", end($temp))) {
|
||
// pathinfo 不支持中文
|
||
// $path = pathinfo($path, PATHINFO_DIRNAME);
|
||
array_splice($temp, count($temp) - 1, 1);
|
||
$path = join(DIRECTORY_SEPARATOR, $temp);
|
||
}
|
||
if (!file_exists($path)) {
|
||
mkdir($path, $mode, true);
|
||
}
|
||
chmod($path, $mode);
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 下划线转驼峰
|
||
* @param string $uncamelized_words 下划线单词
|
||
* @param bool $size true:大驼峰 false:小驼峰 默认为false
|
||
*/
|
||
public static function camelize(string $uncamelized_words, bool $size = false): string
|
||
{
|
||
//下划线替换为空格
|
||
$uncamelized_words = str_replace('_', ' ', $uncamelized_words);
|
||
|
||
if ($size) {
|
||
//大驼峰: 1:进行单词首字母大写转换 2:将空格替换为空
|
||
return str_replace(" ", "", ucwords($uncamelized_words));
|
||
} else {
|
||
//小驼峰: 1:下划线替换为空格,并在单词首部添加下划线(防止被首单词首字母后续被转为大写)
|
||
// 2:进行单词首字母大写转换
|
||
// 3:将空格替换为空
|
||
// 4:将首单词多余的下划线去除
|
||
$uncamelized_words = '_' . $uncamelized_words;
|
||
return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), '_');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 驼峰命名转下划线命名
|
||
* @param string $uncamelize 驼峰单词
|
||
*/
|
||
public static function uncamelize($camelCaps): string
|
||
{
|
||
return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . '_' . "$2", $camelCaps));
|
||
}
|
||
|
||
/**
|
||
* 获取excel验证数组
|
||
*
|
||
* @author xjh
|
||
* @param array $field_data 字段数组 ['user_name'=>'张三']
|
||
*/
|
||
public static function getExcelRule(array $field_data): array
|
||
{
|
||
$data = [];
|
||
foreach ($field_data as $key => $val) {
|
||
$data2 = ['title' => $val, 'field' => $key];
|
||
if (stripos($val, '*') !== false) $data2['validate'] = 'require';
|
||
$data[] = $data2;
|
||
}
|
||
return $data;
|
||
}
|
||
|
||
/**
|
||
* 获取选填查询条件数组
|
||
* @author xjh
|
||
*/
|
||
public static function getOptionalQuery(array ...$data)
|
||
{
|
||
$params = [];
|
||
$con = [];
|
||
|
||
//请求参数获取
|
||
if (isset($data[0]['params']) && $data[0]['params']) {
|
||
//自定义
|
||
$params = $data[0]['params'];
|
||
unset($data[0]);
|
||
$data = array_merge($data);
|
||
} else {
|
||
//自动获取
|
||
$params = Request::param();
|
||
}
|
||
|
||
//处理联表字段
|
||
$dealJoinField = function (string $field_name): string {
|
||
$stripos_val = stripos($field_name, '.');
|
||
return $stripos_val !== false ? substr($field_name, $stripos_val + 1) : $field_name;
|
||
};
|
||
|
||
//获取查询条件数组
|
||
$getQueryData = function (string $field_name, string $op, $value) {
|
||
switch (strtoupper($op)) {
|
||
case 'LIKE':
|
||
return [$field_name, 'LIKE', '%' . $value . '%'];
|
||
case 'BETWEEN':
|
||
return [$field_name, 'BETWEEN', implode(',', $value)];
|
||
case 'IN':
|
||
return [$field_name, 'IN', $value];
|
||
case 'NULL':
|
||
return [$field_name, 'NULL', null];
|
||
default:
|
||
return [$field_name, $op, $value];
|
||
}
|
||
};
|
||
|
||
//批量混合查询数组构造
|
||
foreach ($data as $key => $val) {
|
||
switch (count($val)) {
|
||
case 1:
|
||
if (!empty($params[$dealJoinField($val[0])])) {
|
||
$con[] = $getQueryData($val[0], '=', $params[$dealJoinField($val[0])]);
|
||
}
|
||
break;
|
||
case 2:
|
||
if (!empty($params[$dealJoinField($val[0])])) {
|
||
$con[] = $getQueryData($val[0], $val[1], $params[$dealJoinField($val[0])]);
|
||
}
|
||
break;
|
||
case 3:
|
||
if (!empty($params[$dealJoinField($val[2])])) {
|
||
$con[] = $getQueryData($val[0], $val[1], $params[$val[2]]);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
return $con;
|
||
}
|
||
|
||
/**
|
||
* 上下个数据返回
|
||
*
|
||
* @author xjh
|
||
* @param string $model 模型层命名空间地址
|
||
* @param int|array $sort_field_info 排序字段信息 例:当前排序值(排序字段默认走模型层定义好的) | [排序字段字段名,当前排序值]
|
||
* @param array $extends 扩展
|
||
* @param array $extends["field"] 数据结果集标识要返回的字段(默认全部字段返回)
|
||
* @param string $extends["type"] 返回类型 all(默认):[上个数据,下个数据] | last:上个数据 | next:下个数据
|
||
* @param array $extends["extraWhere"] 额外查询条件(批量混合查询格式: [ ['user_name','=','name'],... ])
|
||
* @return string|array
|
||
*/
|
||
public static function getLastNextData(string $model, $sort_field_info, array $extends = [])
|
||
{
|
||
//模型对象初始化
|
||
$model = new $model();
|
||
|
||
//排序字段信息初始化
|
||
$order_field_name = null;
|
||
$order_field_val = null;
|
||
if (is_array($sort_field_info)) {
|
||
$order_field_name = $sort_field_info[0];
|
||
$order_field_val = $sort_field_info[1];
|
||
} else if (is_int($sort_field_info)) {
|
||
if (!isset($model->order_field)) {
|
||
throwErrorMsg(__METHOD__ . "若排序字段信息为字符串,则必须要在模型层定义排序字段");
|
||
}
|
||
$order_field_name = $model->order_field;
|
||
$order_field_val = $sort_field_info;
|
||
}
|
||
|
||
//非必传参数初始化
|
||
$extra_where = isset($extends['extraWhere']) ? $extends['extraWhere'] : [];
|
||
$field = isset($extends['field']) ? $extends['field'] : null;
|
||
$type = isset($extends['type']) ? $extends['type'] : 'all';
|
||
|
||
|
||
//闭包查询函数
|
||
$query = function ($op) use ($model, $order_field_name, $order_field_val, $field, $extra_where) {
|
||
$order = $op == '<' ? 'desc' : 'asc';
|
||
if ($field) $model = $model->field($field);
|
||
return $model->where($order_field_name, $op, $order_field_val)
|
||
->where($extra_where)
|
||
->order($order_field_name, $order)
|
||
->find();
|
||
};
|
||
|
||
//获取指定类型数据
|
||
switch ($type) {
|
||
case 'all':
|
||
return [$query('<'), $query('>')];
|
||
case 'last':
|
||
return $query('<');
|
||
case 'next':
|
||
return $query('>');
|
||
default:
|
||
throwErrorMsg('Tool::getLastNextId() : type无此类型', 444);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 后台接口锁表(排他锁/写锁/独占锁)操作
|
||
*
|
||
* @param string|array $table 被锁表名(可批量)
|
||
*/
|
||
public static function adminLockTableWrite($table)
|
||
{
|
||
if(is_string($table)){
|
||
$table = [$table];
|
||
}
|
||
|
||
$lock_table =[];
|
||
foreach ($table as $table_name)
|
||
{
|
||
$lock_table[] = "{$table_name} WRITE";
|
||
};
|
||
$lock_table = implode(',',$lock_table);
|
||
|
||
Db::execute("LOCK TABLES {$lock_table},token WRITE,user WRITE");
|
||
}
|
||
|
||
/**
|
||
* 解除表锁
|
||
*/
|
||
public static function unlockTable()
|
||
{
|
||
Db::execute("UNLOCK TABLES");
|
||
}
|
||
|
||
/**
|
||
* 数据修改前排序处理
|
||
*
|
||
* 注意:(调用此类的方法需要进行事务与锁表操作)
|
||
* @param \think\model &$model 模型实例引用
|
||
* @param array $extra_wheres 关联类型 例:["xxx_type" => 1]
|
||
*/
|
||
public static function dataEditSortProc(\think\model &$model, array $extra_wheres = []): void
|
||
{
|
||
$sort = new Sort(get_class($model));
|
||
$sort->swap($model[$sort->getPk()], $model[$sort->getSortField()], $extra_wheres);
|
||
}
|
||
|
||
/**
|
||
* 数据新增前排序处理
|
||
*
|
||
* 注意:(调用此类的方法需要进行事务与锁表操作)
|
||
* @param \think\model &$model 模型实例引用
|
||
* @param array $wheres 当前指定数据的额外查询条件(tp批量查询数组) 例:["xxx_type" => 1,...]
|
||
*/
|
||
public static function dataAddSortProc(\think\model &$model, array $wheres = []): void
|
||
{
|
||
$sort = new Sort(get_class($model));
|
||
if ($model[$sort->getSortField()]) {
|
||
$sort->vacate($model[$sort->getSortField()], $wheres);
|
||
} else {
|
||
$model[$sort->getSortField()] = $sort->add($wheres);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 数据删除前排序处理
|
||
*
|
||
* 注意:(调用此类的方法需要进行事务与锁表操作)
|
||
* @param \think\model &$model 模型实例引用
|
||
* @param array $correl_fields 关联类型字段 例:["xxx_guid",...] 代表删除排序号时会根据这些字段来关联(例:所属xxx类型、所属xxx系列)来进行排序处理
|
||
*/
|
||
public static function dataDeleteSortProc(\think\model &$model, array $correl_fields = []): void
|
||
{
|
||
$sort = new Sort(get_class($model));
|
||
$sort->back($model[$sort->getPk()], $correl_fields);
|
||
}
|
||
|
||
/**
|
||
* 处理(树形数据父子)伦理关系
|
||
*
|
||
* @param \think\Model $model 模型层对象
|
||
*/
|
||
public static function handleEthicalRel(\think\Model $model): void
|
||
{
|
||
if (!isset($model->parent_guid_field) || !isset($model->ancestors_guid_field)) {
|
||
throwErrorMsg(__METHOD__
|
||
. "方法:"
|
||
. get_class($model)
|
||
. "模型层必须定义public \$parent_guid_field,public \$works_type_ancestors_guid");
|
||
}
|
||
//获取当前的主键字段名
|
||
$guld_field = $model->db()->getPk();
|
||
//当前父级主键字段名
|
||
$parent_guid_field = $model->parent_guid_field;
|
||
//当前祖级级主键字段名
|
||
$ancestors_guid_field = $model->ancestors_guid_field;
|
||
//处理一
|
||
if ($model[$guld_field] == $model[$parent_guid_field]) {
|
||
throwErrorMsg("不可以当自己的子级!");
|
||
}
|
||
//处理二
|
||
$is_children = $model->where([
|
||
[$guld_field, '=', $model[$parent_guid_field]],
|
||
[$ancestors_guid_field, 'REGEXP', $model[$guld_field]],
|
||
])->find();
|
||
if ($is_children) {
|
||
throwErrorMsg("不可以当自己孩子们的子级!");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 祖级guid构建
|
||
* @param \think\Model $model 模型层命名空间地址
|
||
* @param bool $is_unipolar 是否为单极结构
|
||
*/
|
||
public static function buildAncestorsGuid(\think\Model &$model, bool $is_unipolar = false): void
|
||
{
|
||
//获取最大父级guid
|
||
$first_parent_guid = isset($model->first_parent_guid) ? $model->first_parent_guid : "0";
|
||
|
||
//获取当前的主键字段名
|
||
$guld_field_name = $model->db()->getPk();
|
||
//获取当前父级主键集字段名
|
||
$parent_guid_field = $model->parent_guid_field;
|
||
$parent_guid = $model[$parent_guid_field];
|
||
//获取当前祖级主键集字段名
|
||
$ancestors_guid_field = $model->ancestors_guid_field;
|
||
|
||
//单极结构或父级guid已经为最大父级guid时直接返回最大父级guid
|
||
if ($is_unipolar || $parent_guid == $first_parent_guid) {
|
||
$model[$ancestors_guid_field] = $first_parent_guid;
|
||
return;
|
||
}
|
||
|
||
//开始构建祖级guid
|
||
$parent = $model->where($guld_field_name, $parent_guid)->find();
|
||
if (!$parent) throwErrorMsg('该父级数据不存在!');
|
||
$model[$ancestors_guid_field] = $parent[$ancestors_guid_field] . ',' . $parent_guid;
|
||
}
|
||
|
||
/**
|
||
* 初始化模型字段值
|
||
*
|
||
* @param \think\Model &$model 模型层对象(引用传递)
|
||
* @param array $field_values 初始化字段信息 例: ['user_name'=>'张三',...]
|
||
*/
|
||
public static function initModelFieldValue(\think\Model &$model, array $field_values): void
|
||
{
|
||
foreach ($field_values as $field => $value) {
|
||
$model[$field] = $value;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 字符串替换优化版
|
||
*
|
||
* @param string $subject 执行替换字符串
|
||
* @param array $search_and_replace 替换信息 例:[替换目标=>替换值,替换目标=>替换值,...]
|
||
* @return string 该函数返回替换后的字符串。
|
||
*/
|
||
public static function strReplacePlus(string $subject, array $search_and_replace): string
|
||
{
|
||
$search = [];
|
||
$replace = [];
|
||
foreach ($search_and_replace as $key => $val) {
|
||
$search[] = $key;
|
||
$replace[] = $val;
|
||
};
|
||
return str_replace($search, $replace, $subject);
|
||
}
|
||
}
|