removeEvilAttributes(['style']); // 检查到 xss 代码之后使用 cleanXss 替换它 $antiXss->setReplacement('cleanXss'); return $antiXss->xss_clean($string); } } if (!function_exists('htmlspecialchars_decode_improve')) { /** * html解码增强 * 被 filter函数 内的 htmlspecialchars 编码的字符串,需要用此函数才能完全解码 * @param string $string * @param int $flags * @return string */ function htmlspecialchars_decode_improve(string $string, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401): string { return htmlspecialchars_decode($string, $flags); } } if (!function_exists('get_sys_config')) { /** * 获取站点的系统配置,不传递参数则获取所有配置项 * @param string $name 变量名 * @param string $group 变量分组,传递此参数来获取某个分组的所有配置项 * @param bool $concise 是否开启简洁模式,简洁模式下,获取多项配置时只返回配置的键值对 * @return mixed * @throws Throwable */ function get_sys_config(string $name = '', string $group = '', bool $concise = true): mixed { if ($name) { // 直接使用->value('value')不能使用到模型的类型格式化 $config = configModel::cache($name, null, configModel::$cacheTag)->where('name', $name)->find(); if ($config) $config = $config['value']; } else { if ($group) { $temp = configModel::cache('group' . $group, null, configModel::$cacheTag)->where('group', $group)->select()->toArray(); } else { $temp = configModel::cache('sys_config_all', null, configModel::$cacheTag)->order('weigh desc')->select()->toArray(); } if ($concise) { $config = []; foreach ($temp as $item) { $config[$item['name']] = $item['value']; } } else { $config = $temp; } } return $config; } } if (!function_exists('get_route_remark')) { /** * 获取当前路由后台菜单规则的备注信息 * @return string */ function get_route_remark(): string { $controllerName = request()->controller; $actionName = request()->action; $path = str_replace('.', '/', $controllerName); $remark = Db::name('admin_rule') ->where('name', $path) ->whereOr('name', $path . '/' . $actionName) ->value('remark'); return __((string)$remark); } } if (!function_exists('full_url')) { /** * 获取资源完整url地址;若安装了云存储或 config/buildadmin.php 配置了CdnUrl,则自动使用对应的CdnUrl * @param string $relativeUrl 资源相对地址 不传入则获取域名 * @param string|bool $domain 是否携带域名 或者直接传入域名 * @param string $default 默认值 * @return string */ function full_url(string $relativeUrl = '', string|bool $domain = true, string $default = ''): string { // 存储/上传资料配置 Event::trigger('uploadConfigInit', App::getInstance()); $cdnUrl = Config::get('buildadmin.cdn_url'); if (!$cdnUrl) { $cdnUrl = request()->upload['cdn'] ?? '//' . request()->host(); } if ($domain === true) { $domain = $cdnUrl; } elseif ($domain === false) { $domain = ''; } $relativeUrl = $relativeUrl ?: $default; if (!$relativeUrl) return $domain; $regex = "/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i"; if (preg_match('/^http(s)?:\/\//', $relativeUrl) || preg_match($regex, $relativeUrl) || $domain === false) { return $relativeUrl; } $url = $domain . $relativeUrl; $cdnUrlParams = Config::get('buildadmin.cdn_url_params'); if ($domain === $cdnUrl && $cdnUrlParams) { $separator = str_contains($url, '?') ? '&' : '?'; $url .= $separator . $cdnUrlParams; } return $url; } } if (!function_exists('encrypt_password')) { /** * 加密密码 * @deprecated 使用 hash_password 代替 */ function encrypt_password($password, $salt = '', $encrypt = 'md5') { return $encrypt($encrypt($password) . $salt); } } if (!function_exists('hash_password')) { /** * 创建密码散列(hash) */ function hash_password(string $password): string { return password_hash($password, PASSWORD_DEFAULT); } } if (!function_exists('verify_password')) { /** * 验证密码是否和散列值匹配 * @param string $password 密码 * @param string $hash 散列值 * @param array $extend 扩展数据 */ function verify_password(string $password, string $hash, array $extend = []): bool { // 第一个表达式直接检查是否为 password_hash 函数创建的 hash 的典型格式,即:$algo$cost$salt.hash if (str_starts_with($hash, '$') || password_get_info($hash)['algoName'] != 'unknown') { return password_verify($password, $hash); } else { // 兼容旧版 md5 加密的密码 return encrypt_password($password, $extend['salt'] ?? '') === $hash; } } } if (!function_exists('str_attr_to_array')) { /** * 将字符串属性列表转为数组 * @param string $attr 属性,一行一个,无需引号,比如:class=input-class * @return array */ function str_attr_to_array(string $attr): array { if (!$attr) return []; $attr = explode("\n", trim(str_replace("\r\n", "\n", $attr))); $attrTemp = []; foreach ($attr as $item) { $item = explode('=', $item); if (isset($item[0]) && isset($item[1])) { $attrVal = $item[1]; if ($item[1] === 'false' || $item[1] === 'true') { $attrVal = !($item[1] === 'false'); } elseif (is_numeric($item[1])) { $attrVal = (float)$item[1]; } if (strpos($item[0], '.')) { $attrKey = explode('.', $item[0]); if (isset($attrKey[0]) && isset($attrKey[1])) { $attrTemp[$attrKey[0]][$attrKey[1]] = $attrVal; continue; } } $attrTemp[$item[0]] = $attrVal; } } return $attrTemp; } } if (!function_exists('action_in_arr')) { /** * 检测一个方法是否在传递的数组内 * @param array $arr * @return bool */ function action_in_arr(array $arr = []): bool { $arr = is_array($arr) ? $arr : explode(',', $arr); if (!$arr) { return false; } $arr = array_map('strtolower', $arr); if (in_array(strtolower(request()->action), $arr) || in_array('*', $arr)) { return true; } return false; } } if (!function_exists('build_suffix_svg')) { /** * 构建文件后缀的svg图片 * @param string $suffix 文件后缀 * @param ?string $background 背景颜色,如:rgb(255,255,255) * @return string */ function build_suffix_svg(string $suffix = 'file', string $background = null): string { $suffix = mb_substr(strtoupper($suffix), 0, 4); $total = unpack('L', hash('adler32', $suffix, true))[1]; $hue = $total % 360; [$r, $g, $b] = hsv2rgb($hue / 360, 0.3, 0.9); $background = $background ?: "rgb($r,$g,$b)"; return ' ' . $suffix . ' '; } } if (!function_exists('get_area')) { /** * 获取省份地区数据 * @throws Throwable */ function get_area(): array { $province = request()->get('province', ''); $city = request()->get('city', ''); $where = ['pid' => 0, 'level' => 1]; if ($province !== '') { $where['pid'] = $province; $where['level'] = 2; if ($city !== '') { $where['pid'] = $city; $where['level'] = 3; } } return Db::name('area') ->where($where) ->field('id as value,name as label') ->select() ->toArray(); } } if (!function_exists('hsv2rgb')) { function hsv2rgb($h, $s, $v): array { $r = $g = $b = 0; $i = floor($h * 6); $f = $h * 6 - $i; $p = $v * (1 - $s); $q = $v * (1 - $f * $s); $t = $v * (1 - (1 - $f) * $s); switch ($i % 6) { case 0: $r = $v; $g = $t; $b = $p; break; case 1: $r = $q; $g = $v; $b = $p; break; case 2: $r = $p; $g = $v; $b = $t; break; case 3: $r = $p; $g = $q; $b = $v; break; case 4: $r = $t; $g = $p; $b = $v; break; case 5: $r = $v; $g = $p; $b = $q; break; } return [ floor($r * 255), floor($g * 255), floor($b * 255) ]; } } if (!function_exists('ip_check')) { /** * IP检查 * @throws Throwable */ function ip_check($ip = null): void { $ip = is_null($ip) ? request()->ip : $ip; $noAccess = get_sys_config('no_access_ip'); $noAccess = !$noAccess ? [] : array_filter(explode("\n", str_replace("\r\n", "\n", $noAccess))); if ($noAccess && IpUtils::checkIp($ip, $noAccess)) { $response = Response::create(['msg' => 'No permission request'], 'json', 403); throw new HttpResponseException($response); } } } if (!function_exists('set_timezone')) { /** * 设置时区 * @throws Throwable */ function set_timezone($timezone = null): void { // $defaultTimezone = Config::get('app.default_timezone'); // $timezone = is_null($timezone) ? get_sys_config('time_zone') : $timezone; // if ($timezone && $defaultTimezone != $timezone) { // Config::set([ // 'app.default_timezone' => $timezone // ]); // date_default_timezone_set($timezone); // } date_default_timezone_set('Asia/Shanghai'); } } if (!function_exists('env')) { /** * 获取环境变量值 * @access public * @param string $name 环境变量名(支持二级 .号分割) * @param string $default 默认值 * @return mixed */ function env(?string $name = null, $default = null) { return getenv($name, $default); } } if (!function_exists('get_upload_config')) { /** * 获取上传配置 * @return array */ function get_upload_config(): array { // 存储/上传资料配置 Event::trigger('uploadConfigInit', App::getInstance()); $uploadConfig = Config::get('upload'); $uploadConfig['max_size'] = Filesystem::fileUnitToByte($uploadConfig['max_size']); $upload = request()->upload; if (!$upload) { $uploadConfig['mode'] = 'local'; return $uploadConfig; } unset($upload['cdn']); return array_merge($upload, $uploadConfig); } } if (!function_exists('get_auth_token')) { /** * 获取鉴权 token * @param array $names * @return string */ function get_auth_token(array $names = ['ba', 'token']): string { $separators = [ 'header' => ['', '-'], // batoken、ba-token【ba_token 不在 header 的接受列表内因为兼容性不高,改用 http_ba_token】 'param' => ['', '-', '_'], // batoken、ba-token、ba_token 'server' => ['_'], // http_ba_token ]; $tokens = []; $request = request(); foreach ($separators as $fun => $sps) { foreach ($sps as $sp) { $tokens[] = $request->$fun(($fun == 'server' ? 'http_' : '') . implode($sp, $names)); } } $tokens = array_filter($tokens); return array_values($tokens)[0] ?? ''; } } if (!function_exists('keys_to_camel_case')) { /** * 将数组 key 的命名方式转换为小写驼峰 * @param array $array 被转换的数组 * @param array $keys 要转换的 key,默认所有 * @return array */ function keys_to_camel_case(array $array, array $keys = []): array { $result = []; foreach ($array as $key => $value) { // 将键名转换为驼峰命名 $camelCaseKey = $keys && in_array($key, $keys) ? parse_name($key, 1, false) : $key; if (is_array($value)) { // 如果值是数组,递归转换 $result[$camelCaseKey] = keys_to_camel_case($value); } else { $result[$camelCaseKey] = $value; } } return $result; } } if (!function_exists('p')) { /** * 将数组 key 的命名方式转换为小写驼峰 * @param array $array 被转换的数组 * @param array $keys 要转换的 key,默认所有 * @return array */ function p(...$p) { if(count($p) > 1) { foreach ($p as $k => $v) { print_r($v); print_r('---'); } }else { print_r($p[0]); } die; } } if (!function_exists('returnErrorData')) { function returnErrorData($msg) { return ['code' => -1, 'message' => $msg, 'data' => []]; } } function getYesterdayFiles($dirPath) { // 1. 获取昨天的日期前缀(格式:两位数日期,如 08) $prefix = date('d', strtotime('-1 day')); // 2. 定义要读取的文件夹路径 // 示例:读取 public 目录下的 files 文件夹 // 若读取其他目录,可使用 app_path()、root_path() 等助手函数 // $dirPath = app_path() . 'data/'; // app/data/ 目录 // 3. 验证文件夹是否存在 if (!is_dir($dirPath)) { return "文件夹不存在:{$dirPath}"; } // 4. 读取文件夹中的所有文件,并筛选以指定前缀开头的文件 $fileNames = []; // 打开目录 $dirHandle = opendir($dirPath); if ($dirHandle) { // 循环读取目录中的文件 while (($fileName = readdir($dirHandle)) !== false) { // 排除 . 和 .. 目录 if ($fileName != '.' && $fileName != '..') { // 检查文件名是否以昨天日期前缀开头 if (strpos($fileName, $prefix) === 0) { $fileNames[] = $fileName; } } } closedir($dirHandle); // 关闭目录句柄 } // 5. 输出结果 return [ 'prefix' => $prefix, 'dir' => $dirPath, 'files' => $fileNames, 'count' => count($fileNames) ]; } if (!function_exists('returnSuccessData')) { function returnSuccessData($data = [], $arr_data = []) { if(!empty($arr_data)) { $return = $arr_data; $return['code'] = 0; $return['message'] = 'ok'; return $return; }else { return ['code' => 0, 'message' => 'ok', 'data' => $data]; } } } if (!function_exists('maskPhoneNumber')) { /** * 隐藏手机号中间部分 * @param string $phone 手机号 * @param int $start 开始位置 * @param int $length 替换长度 * @return string 处理后的手机号 */ function maskPhoneNumber(string $phone, int $start = 3, int $length = 4): string { if (strlen($phone) < $start + $length) { return $phone; // 长度不足,直接返回原号码 } return substr_replace($phone, str_repeat('*', $length), $start, $length); } } if (!function_exists('sha256Hex')) { function sha256Hex($data) { return hash('sha256', $data); } } if (!function_exists('toSerialCode')) { function toSerialCode($id) { $r = str_split("0123456789ABCDEFGHIJKLMNOPQRSTUV"); $binLen = count($r); // 进制长度,通常为32 $s = 8; // 目标字符串长度 $e = "00000000"; // 补全字符,通常为8个'0' $buf = array_fill(0, 32, ''); $charPos = 32; $id = (string) $id; while (intdiv($id, $binLen) > 0) { $ind = (int) ($id % $binLen); $buf[--$charPos] = $r[$ind]; $id = intdiv($id, $binLen); } $buf[--$charPos] = $r[(int) ($id % $binLen)]; $str = implode(array_slice($buf, $charPos)); // 长度不足时补全 if (strlen($str) < $s) { $str = substr($e, 0, $s - strlen($str)) . $str; } return strtoupper($str); } } function toSnakeCase($string) { // 将驼峰或帕斯卡命名法转为下划线命名 $string = preg_replace('/([a-z])([A-Z])/', '$1_$2', $string); $string = preg_replace('/([A-Z])([A-Z][a-z])/', '$1_$2', $string); return strtolower($string); } function arrayKeysToSnakeCase($array) { $result = []; foreach ($array as $key => $value) { $newKey = is_string($key) ? toSnakeCase($key) : $key; if (is_array($value)) { $value = arrayKeysToSnakeCase($value); // 递归处理 } $result[$newKey] = $value; } return $result; } function getNormalDate(string $time = '') { return $time ? date($time) : date('Y-m-d H:i:s'); } /** * ThinkPHP 防抖函数,基于 cache() 助手函数实现 * * @param string $key 唯一操作标识符(例如 user_id_action) * @param int $waitMs 等待毫秒数(支持毫秒) * @param callable $callback 要执行的操作 * @return mixed|null 等待时间内重复调用将返回 null */ function debounceAndRun(string $key, callable $callback,int $waitMs = 2000) { // cache() 默认单位是秒,我们转成浮点秒数 $ttl = $waitMs / 1000; // 尝试设置缓存,存在时不执行 if (!cache($key)) { cache($key, 1, $ttl); // 设置一个短暂缓存用于防抖 return $callback(); } throw new Exception("操作过于频繁,请稍后再试"); } function debounce(string $key,int $waitMs = 20) { // cache() 默认单位是秒,我们转成浮点秒数 $ttl = $waitMs; // 尝试设置缓存,存在时不执行 if (!cache($key)) { cache($key, 1, $ttl); // 设置一个短暂缓存用于防抖 }else{ throw new SysException("操作过于频繁,请稍后再试"); } } /** * 类似 Java 的 StrUtil.format("{}, {}") 占位符替换函数 * * @param string $template 模板字符串 * @param mixed ...$args 依次替换的值 * @return string */ function format(string $template, ...$args): string { foreach ($args as $value) { if (!is_string($value)) { $value = json_encode($value); } $template = preg_replace('/\{}/', (string)$value, $template, 1); } return $template; } if (!function_exists('uuid')) { function uuid():string { $data = random_bytes(16); $data[6] = chr((ord($data[6]) & 0x0f) | 0x40); $data[8] = chr((ord($data[8]) & 0x3f) | 0x80); return vsprintf('%s%s%s%s%s%s%s%s', str_split(bin2hex($data), 4)); } } if (!function_exists('formatTo4Decimal')) { /** * 使用sprintf保留4位小数 * @param float $number 输入的浮点数 * @return string 格式化后的字符串 */ function formatTo4Decimal(float $number): string { // 先乘以10000取整,再除以10000,最后格式化为4位小数 $truncated = (int)($number * 10000) / 10000; return sprintf('%.4f', $truncated); } } function hasEmpty(...$args): bool { foreach ($args as $arg) { if (empty($arg)) { return true; } } return false; } if(!function_exists('shuffleMultiArray')) { /** * 打乱多维数组的顶层元素 * @param array $array 输入的多维数组 * @return array 打乱顺序后的数组 */ function shuffleMultiArray(array $array): array { $keys = array_keys($array); shuffle($keys); // 随机打乱键名顺序 $shuffled = []; foreach ($keys as $key) { $shuffled[$key] = $array[$key]; } return $shuffled; } } if(!function_exists('convertToCamelCase')) { /** * 下划线转驼峰 */ function convertToCamelCase(array | null $data): array | null { if (!$data) { return $data; } $result = []; foreach ($data as $k => $item) { $camelItem = []; if(is_array($item)) { foreach ($item as $key => $value) { $camelKey = lcfirst(preg_replace_callback('/_([a-z])/', function ($match) { return strtoupper($match[1]); }, $key)); $camelItem[$camelKey] = $value; } $result[] = $camelItem; }else { $camelKey = lcfirst(preg_replace_callback('/_([a-z])/', function ($match) { return strtoupper($match[1]); }, $k)); $result[$camelKey] = $item; } } return $result; } } if(!function_exists('apiconvertToCamelCase')) { /** * 下划线转驼峰 */ function apiconvertToCamelCase(array $data): array { $result = []; foreach ($data as $k => $item) { $camelItem = []; if(is_array($item)) { foreach ($item as $key => $value) { $camelKey = lcfirst(preg_replace_callback('/_([a-z])/', function ($match) { return strtoupper($match[1]); }, $key)); $camelItem[$camelKey] = $value; } $result[] = $camelItem; }else { $camelKey = lcfirst(preg_replace_callback('/_([a-z])/', function ($match) { return strtoupper($match[1]); }, $k)); $result[$camelKey] = $item; } } $result = convertUserIdToString($result); return $result; } } if(!function_exists('page')) { /** * 分页计算 */ function page($page = 1, $limit = 10) { return ($page - 1) * $limit; } } /** * 自动加锁执行回调逻辑,结束后自动释放锁 * * @param string $key 锁的键名 * @param int $expire 锁过期时间(秒) * @param \Closure $callback 要执行的回调函数 * @return mixed 回调函数返回值,失败返回 false */ function runWithLock(string $key, int $expire, \Closure $callback) { $lockValue = uniqid(); $val = cache($key); if ($val) { return false; } // 加锁(NX:不存在时设置,EX:过期时间) $acquired = cache($key, $lockValue, $expire); if (!$acquired) { return false; // 获取锁失败 } try { return $callback(); // 执行传入的方法 } finally { release($key, $lockValue); } } if (!function_exists('cache')) { /** * 缓存管理 * @param string $name 缓存名称 * @param mixed $value 缓存值 * @param mixed $options 缓存参数 * @param string $tag 缓存标签 * @return mixed */ function cache(?string $name = null, $value = '', $options = null, $tag = null) { if (is_null($name)) { return app('cache'); } if ('' === $value) { // 获取缓存 return str_starts_with($name, '?') ? Cache::has(substr($name, 1)) : Cache::get($name); } elseif (is_null($value)) { // 删除缓存 return Cache::delete($name); } // 缓存数据 if (is_array($options)) { $expire = $options['expire'] ?? null; //修复查询缓存无法设置过期时间 } else { $expire = $options; } if (is_null($tag)) { return Cache::set($name, $value, $expire); } else { return Cache::tag($tag)->set($name, $value, $expire); } } } /** * 解锁(用Lua防止误删) */ function release(string $key, string $value) { $lua = << uuid(), ]; Log::info("消息队列发送消息,对列名: $class, 携带数据: ".json_encode($data).', 延时时间: '.$seconds); if ($seconds > 0) { Queue::later($seconds, $class, $data); }else{ Queue::push($class, $data); } } if(!function_exists('daysBetween')) { /** * 计算两个日期之间的天数差(纯原生函数实现) * @param string $startDate 开始日期(格式:YYYY-MM-DD) * @param string $endDate 结束日期(格式:YYYY-MM-DD,默认当前日期) * @return int 天数差(绝对值) */ function daysBetween(string $startDate, string $endDate = null): int { // 解析开始日期 $startTimestamp = strtotime($startDate); if ($startTimestamp === false) { throw new InvalidArgumentException("无效的开始日期格式: $startDate"); } // 处理结束日期(默认为当前日期) if ($endDate === null) { $endTimestamp = time(); } else { $endTimestamp = strtotime($endDate); if ($endTimestamp === false) { throw new InvalidArgumentException("无效的结束日期格式: $endDate"); } } // 计算天数差(忽略时区影响,仅计算日期差) $startDay = strtotime(date('Y-m-d', $startTimestamp)); $endDay = strtotime(date('Y-m-d', $endTimestamp)); $daysDiff = ($endDay - $startDay) / (24 * 60 * 60); // 86400秒/天 return (int)$daysDiff; } } if(!function_exists('buildFlowDays')) { /** * 生成从开始日期起的连续日期列表 * @param string $beginDay 开始日期(格式:YYYY-MM-DD) * @param int $activeDays 连续天数 * @return array 日期字符串列表(格式:YYYY-MM-DD) */ function buildFlowDays(string $beginDay, int $activeDays) { $flowDays = []; $timestamp = strtotime($beginDay); // 转换为时间戳 for ($i = 0; $i < $activeDays; $i++) { // 计算偏移后的时间戳(每天86400秒) $currentTimestamp = $timestamp + ($i * 86400); // 格式化为YYYY-MM-DD并添加到列表 $flowDays[] = date('Y-m-d', $currentTimestamp); } return $flowDays; } } if(!function_exists('buildFlowDaysTwo')) { /** * 生成从开始日期到结束日期的连续日期列表(纯原生函数实现) * @param string $beginDay 开始日期(格式:YYYY-MM-DD) * @param string $endDay 结束日期(格式:YYYY-MM-DD) * @return array 日期字符串列表(格式:YYYY-MM-DD) */ function buildFlowDaysTwo(string $beginDay, string $endDay): array { $flowDays = []; $currentTimestamp = strtotime($beginDay); $endTimestamp = strtotime($endDay); while (true) { // 将当前日期添加到列表(每次循环开始时添加) $flowDays[] = date('Y-m-d', $currentTimestamp); // 判断是否达到结束日期 if ($currentTimestamp === $endTimestamp) { break; } // 未达到结束日期,增加一天 $currentTimestamp += 86400; // 86400秒 = 1天 } return $flowDays; } } if(!function_exists('todayAfterSecond')) { /** * 获取当日剩余秒数 * @return int 剩余秒数 */ function todayAfterSecond() { // 获取当前时间戳 $now = time(); // 获取当天结束时间(23:59:59)的时间戳 $endOfDay = strtotime('tomorrow -1 second'); // 计算剩余秒数 $diffSeconds = $endOfDay - $now; return $diffSeconds; } } if(!function_exists('generateRedisKey')) { function generateRedisKey($key, $id) { return 'sys:limit:' . $key . ':' . $id; } } if(!function_exists('bankCard')) { function bankCard($bankCardNo) { if (empty($bankCardNo)) { return $bankCardNo; } $bankCardNo = trim($bankCardNo); $length = strlen($bankCardNo); if ($length < 9) { return $bankCardNo; } $midLength = $length - 8; // 中间需要脱敏的长度 $buf = ''; // 保留前4位 $buf .= substr($bankCardNo, 0, 4); // 中间部分用*替换,每4位加一个空格 for ($i = 0; $i < $midLength; $i++) { if ($i % 4 === 0) { $buf .= ' '; } $buf .= '*'; } // 保留后4位,前面加一个空格 $buf .= ' ' . substr($bankCardNo, -4); return $buf; } } if(!function_exists('email')) { function email($email) { if (empty($email)) { return ''; } $index = strpos($email, '@'); if ($index <= 1) { return $email; } // 保留第一个字符,中间部分用*替换,保留@及后面的域名 $prefix = substr($email, 0, 1); $suffix = substr($email, $index); $maskLength = $index - 1; $maskedPart = str_repeat('*', $maskLength); return $prefix . $maskedPart . $suffix; } } if(!function_exists('idCardNum')) { /** * 对身份证号进行脱敏处理 * @param string $idCardNum 身份证号码 * @param int $front 保留前几位 * @param int $end 保留后几位 * @return string 脱敏后的身份证号 */ function idCardNum($idCardNum, $front, $end) { // 身份证不能为空 if (empty($idCardNum)) { return ''; } // 需要截取的长度不能大于身份证号长度 if (($front + $end) > strlen($idCardNum)) { return ''; } // 需要截取的位数不能小于0 if ($front < 0 || $end < 0) { return ''; } // 保留前$front位和后$end位,中间用*替换 $maskLength = strlen($idCardNum) - $front - $end; $maskedPart = str_repeat('*', $maskLength); return substr($idCardNum, 0, $front) . $maskedPart . substr($idCardNum, -$end); } } if(!function_exists('http_post')) { function http_post($url, $data, $headers = [], $timeout = 30, $contentType = 'application/json') { // 初始化cURL $ch = curl_init(); // 处理请求数据 if ($contentType === 'application/json') { $data = json_encode($data); } elseif (is_array($data)) { $data = http_build_query($data); } // 设置请求头 $defaultHeaders = [ "Content-Type: $contentType", "Content-Length: " . strlen($data) ]; $headers = array_merge($defaultHeaders, $headers); // 设置cURL选项 curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 执行请求 $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); // 关闭cURL curl_close($ch); // 处理错误 if ($response === false) { return $error; } return $response; } } function shiro_simple_hash_hex_salt(string $algorithm, string $source, ?string $salt = null, int $iterations = 1): string { if ($iterations < 1) { $iterations = 1; } $salt = $salt ?? ''; // 将 null 转为空字符串 $hexSalt = bin2hex($salt); // 转为字节 $sourceBytes = is_string($source) ? $source : strval($source); $saltBytes = $hexSalt !== '' ? hex2bin($hexSalt) : ''; if ($saltBytes === false && $hexSalt !== '') { throw new InvalidArgumentException("Invalid hex salt: $hexSalt"); } // 初始 hash(含 salt) $digest = hash_init($algorithm); if ($saltBytes !== '') { hash_update($digest, $saltBytes); } hash_update($digest, $sourceBytes); $result = hash_final($digest, true); // 后续迭代(不加盐) for ($i = 1; $i < $iterations; $i++) { $result = hash($algorithm, $result, true); // binary } return bin2hex($result); } if(!function_exists('get_master_connect_name')) { function get_master_connect_name() { return config('database.z_library'); } } if(!function_exists('get_slave_connect_name')) { function get_slave_connect_name() { return config('database.search_library'); } } if(!function_exists('get_file_info')) { function get_file_info($file) { return file_get_contents($file); } } function buildPageInfo(array $info, bool $isRecord=false) { $size = count($info); return [ 'totalCount' => $size, 'pageSize' => $size, 'totalPage' => 1, 'currPage' => 1, $isRecord ? 'records' : 'list' => $info ]; } /** * 将日期补全为当天的开始时间(00:00:00) * @param string $date 输入日期(支持格式:'2023-10-05'、'2023/10/05'、'2023-10-05 12:30:45' 等) * @return string 补全后的时间字符串(格式:'Y-m-d 00:00:00') * @throws \Exception 若日期格式无效则抛出异常 */ function completeStartTime($date) { // 将输入日期转换为时间戳(支持多种格式) $timestamp = strtotime($date); if ($timestamp === false) { throw new \Exception("无效的日期格式:{$date},请使用类似 'YYYY-MM-DD' 的格式"); } // 格式化时间戳为 "YYYY-MM-DD 00:00:00" return date('Y-m-d 00:00:00', $timestamp); } /** * 模糊删除 Redis 中匹配指定模式的所有 key(使用 SCAN) * * @param string $pattern 匹配模式,如 "user_*" * @param int $count 每次扫描数量,默认 100 * @return int 删除的 key 数量 */ function deleteRedisKeysByPattern(string $pattern, int $count = 100): int { $redis = Cache::store('redis')->handler(); $iterator = null; $deleted = 0; do { $keys = $redis->scan($iterator, $pattern, $count); if (!empty($keys)) { $redis->del(...$keys); $deleted += count($keys); } } while ($iterator > 0); return $deleted; } /** * 将多维数组中的user_id或userId键的值转换为字符串类型 * @param array $data 待处理的多维数组 * @return array 处理后的数组 */ function convertUserIdToString(array $data): array { $result = []; foreach ($data as $key => $value) { // 处理键为user_id或userId的情况 $id_list = [ 'user_id', 'userId', 'id', 'roleId', 'courseDetailsId', 'courseId', 'ordersId', ]; if (in_array($key, $id_list)) { $result[$key] = (string)$value; continue; } // 递归处理子数组 if (is_array($value)) { $result[$key] = convertUserIdToString($value); } else { // 其他类型的值保持不变 $result[$key] = $value; } } return $result; }