houde_web_api/app/common/exception/Tool.php

518 lines
18 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\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 = Request::param();
$con = [];
//处理联表字段
$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 \think\Model $model 模型层对象
* @param array $sortField 排序字段信息[排序字段字段名,当前排序值]
* @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(\think\Model $model, array $sortField, array $extends)
{
//非必传参数初始化
$extra_where = isset($extends['extraWhere']) ? $extends['extraWhere'] : [];
$field = isset($extends['field']) ? $extends['field'] : null;
$type = isset($extends['type']) ? $extends['type'] : 'all';
// 排序字段名
$order_field_name = $sortField[0];
// 排序字段当前值
$order_field_val = $sortField[1];
//闭包查询函数
$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)
->limit(1)
->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 $model 模型层命名空间地址
* @param string $guid 主键
* @param int $order 新排序号
* @param array $extra_wheres 当前指定数据的额外查询条件(tp批量查询数组) 例:["xxx_type" => 1,...]
*/
public static function sortEditProc(string $model, string $guid, int $order, array $extra_wheres = []): void
{
//模型层实例化
$model = new $model;
//表名
$table_name = $model->db()->getTable();
//获取当前的主键字段名
$guld_field = $model->db()->getPk();
//排序字段名
if (!isset($model->order_field)) throwErrorMsg('排序处理失败!模型层未定义排序字段$order_field');
$order_field_name = $model->order_field;
//当前数据原信息
$original_oreder_find = $model->where($guld_field, $guid)->find();
if (!$original_oreder_find) throwErrorMsg('Tool::sortEditProc() : 找不到该数据原排序号', 444);
//查找当前数据所想更换的新排序号是否已有数据占用(排除自己)
//已被占用,则将它们的排序号互换
if ($model->where($order_field_name, $order)->where($guld_field, '<>', $guid)->where($extra_wheres)->find()) {
$update_data = [$order_field_name => $original_oreder_find[$order_field_name]];
if (isset($model->parent_guid_field)) {
$update_data[$model->parent_guid_field] = $original_oreder_find[$model->parent_guid_field];
}
if (isset($model->ancestors_guid_field)) {
$update_data[$model->ancestors_guid_field] = $original_oreder_find[$model->ancestors_guid_field];
}
Db::name($table_name)->where($order_field_name, $order)->where($extra_wheres)->update($update_data);
}
}
/**
* 排序号腾位处理
*
* 注意:使用该方法时候要启动事务!!
* @param string $model 模型层命名空间地址
* @param int $order 当前排序号
* @param array $wheres 当前指定数据的额外查询条件(tp批量查询数组) 例:["xxx_type" => 1,...]
*/
public static function sortInsertProc(string $model, int $order, array $wheres = []): void
{
//模型层实例化
$model = new $model;
//表名
$table_name = $model->db()->getTable();
//排序字段名
if (!isset($model->order_field)) throwErrorMsg('排序处理失败!模型层未定义排序字段$order_field');
$order_field_name = $model->order_field;
//新增数据的所属排序号已有数据占用,则腾位处理(已占用的数据及其后面所有数据一起排序号腾位+1)
if ($model->where($order_field_name, $order)->where($wheres)->value($order_field_name) !== null) {
Db::name($table_name)
->where($wheres)
->where($order_field_name, '>=', $order)
->inc($order_field_name)
->update();
}
}
/**
* 排序号删除处理
* 注意:使用该方法时候要启动事务!!
* @param string $model 模型层命名空间地址
* @param array $guid 主键
* @param array $correl_fields 关联字段 例:["xxx_guid",...] 代表删除排序号时会根据这些字段来关联(例:所属xxx类型、所属xxx系列)来进行排序处理
*/
public static function sortDeleteProc(string $model, string $guid, array $correl_fields = []): void
{
//模型层实例化
$model = new $model;
//获取当前的主键字段名
$guld_field_name = $model->db()->getPk();
//获取当前表排序字段名
if (!isset($model->order_field)) throwErrorMsg('排序处理失败!模型层未定义排序字段$order_field');
$order_field_name = $model->order_field;
//在所删除的数据之后的数据所属排序号将进行减位处理减位-1
if ($find = $model->where($guld_field_name, $guid)->find()) {
$con = [];
foreach ($correl_fields as $correl_field) {
$con[$correl_field] = $find[$correl_field];
}
$model->where($order_field_name, '>', $find[$order_field_name])
->where($con)
->dec($order_field_name)
->save();
}
}
/**
* 处理(树形数据父子)伦理关系
*
* @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;
}
}
}