drag-create-api/app/common/exception/Tool.php
2023-06-25 08:51:24 +08:00

540 lines
17 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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);
}
}