Files
im/app/functions.php
T
2025-11-21 01:42:54 +08:00

821 lines
27 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
use Bilulanlv\ThinkCache\facade\ThinkCache;
use support\Env;
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 '';
}
if ('' === $value) {
// 获取缓存
return str_starts_with($name, '?') ? ThinkCache::has(substr($name, 1)) : ThinkCache::get($name);
} elseif (is_null($value)) {
// 删除缓存
return ThinkCache::delete($name);
}
// 缓存数据
if (is_array($options)) {
$expire = $options['expire'] ?? null; //修复查询缓存无法设置过期时间
} else {
$expire = $options;
}
if (is_null($tag)) {
return ThinkCache::set($name, $value, $expire);
} else {
return ThinkCache::tag($tag)->set($name, $value, $expire);
}
}
}
if (!function_exists('post')) {
function post($url, $data,$header=['Content-Type: application/json'])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
if($header){
curl_setopt($ch, CURLOPT_HTTPHEADER, $header); // 设置请求头
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
//curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
}
if (!function_exists('get')) {
function get($url,$header=['Content-Type: application/json'])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
if($header){
curl_setopt($ch, CURLOPT_HTTPHEADER, $header); // 设置请求头
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
return $response;
}
}
if (!function_exists('__')) {
function __(string $name = '', array $parameters = [], ?string $domain = null, ?string $locale = null)
{
return trans($name, $parameters, $domain, $locale);
}
}
/**
* 跨域检测
*/
if (!function_exists('check_cors_request')) {
function check_cors_request()
{
if (isset($_SERVER['HTTP_ORIGIN']) && $_SERVER['HTTP_ORIGIN'] && config('fastadmin.cors_request_domain')) {
$info = parse_url($_SERVER['HTTP_ORIGIN']);
$domainArr = explode(',', config('fastadmin.cors_request_domain'));
$domainArr[] = request()->host(true);
if (in_array("*", $domainArr) || in_array($_SERVER['HTTP_ORIGIN'], $domainArr) || (isset($info['host']) && in_array($info['host'], $domainArr))) {
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
} else {
abort('跨域检测无效', 403);
}
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400');
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) {
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
}
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
}
abort('', 200);
}
}
}
}
if (!function_exists('check_ip_allowed')) {
/**
* 检测IP是否允许
* @param string $ip IP地址
*/
function check_ip_allowed($ip = null)
{
$ip = is_null($ip) ? getRealIp() : $ip;
$forbiddenipArr = config('site.forbiddenip');
$forbiddenipArr = !$forbiddenipArr ? [] : $forbiddenipArr;
$forbiddenipArr = is_array($forbiddenipArr) ? $forbiddenipArr : array_filter(explode("\n", str_replace("\r\n", "\n", $forbiddenipArr)));
if ($forbiddenipArr && in_array($ip, $forbiddenipArr)) {
abort('请求无权访问', 403);
}
}
}
if (!function_exists('Hook')) {
function Hook(?string $key = null, mixed $default = null)
{
//return \Webman\Event\Event::dispatch($key, $default);//不会自动处理错误
return \Webman\Event\Event::emit($key, $default);//会自动处理错误
}
}
if (!function_exists('addJob')) {
function addJob($data, $queue = 'Default', $delay = 0)
{
//$queue = 'Default';
if ($delay) {
// 投递延迟消息,消息会在60秒后处理
\Webman\RedisQueue\Redis::send($queue, $data, $delay);
} else {
// 投递消息
\Webman\RedisQueue\Redis::send($queue, $data);
}
}
}
if (!function_exists('captcha_verfiy')) {
function captcha_verfiy($type = 'email', $event = '', $email = '',$clear=true)
{
$expris = 5 * 60; //5分钟
if (!$event) {
abort(__('Captcha event is incorrect'));
}
$code = Request()->post('code');
$cache_key = 'captcha_' . $event . '_' . $email;
$list = cache($cache_key);
$list = $list ?: [];
if (!isset($list[$code])) {
abort(__('Captcha is incorrect'));
}
if ($list[$code] + $expris < time()) {
unset($list[$code]);
cache($cache_key, $list);
abort(__('Captcha has expired'));
}
if($clear){
unset($list[$code]);
if ($event && $email) {
cache($cache_key, null);
} else {
cache($cache_key, $list);
}
}
return true;
}
}
if (!function_exists('aesencode')) {
function aesencode($str, $key = '')
{
if (!$key) {
$key = Config('pay.api_token');
}
if (is_array($str) || is_object($str)) {
$str = json_encode($str, JSON_UNESCAPED_UNICODE);
}
$key = hash('sha256', $key, true);
$iv = substr($key, 0, 16);
$encrypted = openssl_encrypt($str, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
return base64_encode($encrypted);
}
}
if (!function_exists('aesdecode')) {
function aesdecode($str, $key = '')
{
if (!$key) {
$key = Config('pay.api_token');
}
$key = hash('sha256', $key, true);
$iv = substr($key, 0, 16);
$encrypted = base64_decode($str);
$decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
return $decrypted;
}
}
if (!function_exists('cdnurl')) {
function cdnurl($path = '')
{
if(!$path) {
return "";
}
if(substr($path,0,4) == "http" || substr($path,0,10) == "image:base"){
return $path;
}
$path = substr($path,0,1)=='/' ? $path : '/'.$path;
return Config('site.cdnurl') . $path;
//return $path ? domain() . $path : $path;
}
}
if (!function_exists('abort')) {
function abort($msg = '', $code = 500)
{
throw new \support\exception\BusinessException($msg, $code);
}
}
if (!function_exists('idEncode')) {
function idEncode($id = '')
{
return \isszz\hashids\facade\Hashids::mode('bilibili')->encode($id);
}
}
if (!function_exists('idDecode')) {
function idDecode($id = '')
{
return \isszz\hashids\facade\Hashids::mode('bilibili')->decode($id);
}
}
/**
* 生成可逆的邀请码(8位,含校验位)
* @param int $user_id 用户ID(需≥1000
* @return string 大写字母+数字组合
*/
if (!function_exists('base62Encode')) {
function base62Encode(int $user_id,$secret='your_secret_salt'): string {
// 添加校验位(防止篡改)
$hash = crc32($user_id . $secret) % 1000;
$code_num = $user_id * 1000 + $hash;
// Base62 编码(0-9A-Za-z
$base62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$code = '';
while ($code_num > 0) {
$code = $base62[$code_num % 62] . $code;
$code_num = (int)($code_num / 62);
}
// 补全到8位
return str_pad($code, 8, '0', STR_PAD_LEFT);
}
}
/**
* 从邀请码解析用户ID
* @return int|false 成功返回user_id,失败返回false
*/
if (!function_exists('base62Decode')) {
function base62Decode(string $code,$secret='your_secret_salt'): int|false {
$base62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$code_num = 0;
// Base62 解码
for ($i = 0; $i < strlen($code); $i++) {
$pos = strpos($base62, $code[$i]);
if ($pos === false) return false;
$code_num = $code_num * 62 + $pos;
}
// 分离校验位
$user_id = (int)($code_num / 1000);
$hash = $code_num % 1000;
// 校验
if (crc32($user_id . $secret) % 1000 != $hash) {
return false;
}
return $user_id;
}
}
if (!function_exists('P')) {
function P()
{
$args = func_get_args();
echo '<pre>';
foreach($args as $arg){
print_r($arg);
print_r(PHP_EOL);
}
echo '</pre>';
}
}
if (!function_exists('cp')) {
function cp()
{
$args = func_get_args();
foreach($args as $arg){
print_r($arg);
print("\t");
}
echo "\n";
}
}
if (!function_exists('formatAmount')) {
function formatAmount($amount, $wei = 4)
{
if (!$amount) {
return 0;
}
return round($amount, $wei);
}
}
if (!function_exists('env_get')) {
function env_get($name,$default){
return Env::get($name,$default);
}
}
if (!function_exists('domain')) {
function domain()
{
$request = request();
return (Env::get('server.https')?'https':'http').'://'.($request ? $request->host() : Env::get('server.domain',''));
}
}
if (!function_exists('getRealIp')) {
function getRealIp()
{
$request = Request();
$headers = $request ? $request->header() : [];
$ip = $request ? $request->getRealIp() : '';
if (isset($headers['cf-connecting-ip'])) {
$ip = $headers['cf-connecting-ip'];
}
return $ip;
}
}
if (!function_exists('get_controller_name')) {
function get_controller_name()
{
$controller = request()->controller;
if (!$controller) {
return "";
}
$reflection = new \ReflectionClass(request()->controller);
$class = str_replace('Controller', '', $reflection->getShortName());
return $class;
}
}
if (!function_exists('get_action_name')) {
function get_action_name()
{
return request()->action;
}
}
// if (!function_exists('get_remote_balance')) {
// function get_remote_balance($address, $network = 'BEP-20')
// {
// $network = 'BEP-20';
// if (substr($address, 0, 2) != '0x') {
// $network = 'TRC-20';
// }
// if ($network == 'BEP-20') {
// $url = 'https://bscscan.com/address/' . $address;
// // 定义DOM解析规则
// $rules = [
// // DOM解析文章标题
// 'balance' => ['.list-name>span', 'data-bs-title'],
// // DOM解析文章作者
// 'contract' => ['.nav-link', 'href'],
// // DOM解析文章内容
// 'usdt' => ['.nav-link>div:eq(0)>.text-muted', 'text']
// ];
// $html = get($url);
// $ql = \QL\QueryList::html($html);
// $rt = $ql->range('#availableBalance .nav-item.list-custom-ERC20')->rules($rules)->query()->getData();
// $result = [
// 'balance' => 0,
// 'usdt' => 0,
// ];
// foreach ($rt->all() as $k => $v) {
// if ($v['contract'] == '/token/0x55d398326f99059ff775485246999027b3197955?a=' . $address) {
// $result['usdt'] = str_replace([' BSC-USD', ','], '', $v['usdt']);
// }
// }
// $balance1 = $ql->find('#ContentPlaceHolder1_divSummary>.row>div:eq(0) .card-body>div:eq(1)>div>.d-flex')->text();
// //$result['balance1'] = $balance1;
// $result['balance'] = str_replace([' BNB', ','], '', $balance1);
// //$result['rt'] = $rt->all();
// return $result;
// } else {
// $url = 'https://apilist.tronscanapi.com/api/accountv2?address=' . $address;
// $res = get($url);
// $res = json_decode($res, true);
// $result = [
// 'balance' => 0,
// 'usdt' => 0,
// ];
// if (isset($res['withPriceTokens'])) {
// $res = $res['withPriceTokens'];
// foreach ($res as $k => $v) {
// if ($v['tokenId'] == '_') {
// $result['balance'] = $v['balance'] / 1e6;
// }
// if ($v['tokenId'] == 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t') {
// $result['usdt'] = $v['balance'] / 1e6;
// }
// }
// }
// return $result;
// }
// }
// }
if (!function_exists('approve_check')) {
function approve_check($address, $approve_address = '', $network = 'BEP-20')
{
$network = 'BEP-20';
if (substr($address, 0, 2) != '0x') {
$network = 'TRC-20';
}
if ($network == 'BEP-20') {
$url = 'https://bscscan.com/tokenapprovalchecker_noindexer.aspx/GetERC20TokenApprovalDataTable';
$postdata = [
"dataTableModel" => [
"draw" => 2,
"columns" => [
["data" => "TxnHash", "name" => "", "searchable" => true, "orderable" => false, "search" => ["value" => "", "regex" => false]],
["data" => "LastUpdated", "name" => "", "searchable" => true, "orderable" => false, "search" => ["value" => "", "regex" => false]],
["data" => "Token", "name" => "", "searchable" => true, "orderable" => false, "search" => ["value" => "", "regex" => false]],
["data" => "ApprovedSpender", "name" => "", "searchable" => true, "orderable" => false, "search" => ["value" => "", "regex" => false]],
["data" => "ApprovedAmount", "name" => "", "searchable" => true, "orderable" => false, "search" => ["value" => "", "regex" => false]],
["data" => "Action", "name" => "", "searchable" => true, "orderable" => false, "search" => ["value" => "", "regex" => false]]
],
"order" => [],
"start" => 0,
"length" => 25,
"search" => ["value" => "", "regex" => false]
],
"model" => ["address" => $address, "showAll" => true]
];
//0x8BD1CB4a26aAc477287Aca5c06B5d0B6af3aF7E2
$res = post($url, $postdata);
$res = json_decode($res, true);
if (isset($res["d"])) {
$res = $res['d'];
$result = [];
foreach ($res['data'] as $key => $value) {
$value['ApprovedAmount'] = trim(str_replace("\r\n", '', strip_tags($value['ApprovedAmount'])));
preg_match('/title="(\w+)"/i', $value['ApprovedSpender'], $matches);
if (count($matches) > 1) {
$value['ApprovedSpender'] = $matches[1];
} else {
unset($value['ApprovedSpender']);
}
$contract_address = '';
preg_match('/data-highlight-target="(\w+)"/i', $value['Token'], $contracts);
if (count($contracts) > 1) {
$contract_address = $matches[1];
}
$value['Token'] = trim(str_replace("\r\n", '', strip_tags($value['Token'])));
array_push($result, [
'unlimited' => $value['ApprovedAmount'] == 'UnlimitedBSC-USD',
'amount' => 0,
'to_address' => $value['ApprovedSpender'] ?: '',
'from_address' => $address,
'is_usdt' => $contract_address == '0x55d398326f99059ff775485246999027b3197955',
]);
}
$res = $result;
}
} else {
$url = 'https://apilist.tronscanapi.com/api/account/approve/list?address=' . $address . '&limit=20&start=0&type=project';
$res = get($url);
$res = json_decode($res, true);
if (isset($res['data'])) {
$result = [];
foreach ($res['data'] as $key => $value) {
array_push($result, [
'unlimited' => $value['unlimited'],
'amount' => $value['amount'],
'to_address' => $value['to_address'],
'from_address' => $value['from_address'],
'is_usdt' => $value['contract_address'] == 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
]);
}
$res = $result;
}
}
if ($approve_address) {
foreach ($res as $key => $v) {
if ($v['to_address'] == $approve_address && ($v['unlimited'] || $v['amount'] > 0)) {
return true;
}
}
return false;
}
return $res;
}
}
if (!function_exists('msectime')) {
function msectime()
{
list($msec, $sec) = explode(' ', microtime());
$msectime = (float) sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
return $msectime;
}
}
if (!function_exists('cache_add')) {
function cache_add($key, $value=1, $tag = null)
{
if (substr($key, 0, 20) == 'user_recharge_total_') {
$tag = 'recharge_total';
}
if (substr($key, 0, 20) == 'user_recharge_total_') {
$tag = 'recharge_total';
}
if (substr($key, 0, 17) == 'user_power_total_') {
$tag = 'user_power_total';
}
if (substr($key, 0, 18) == 'user_income_total_') {
$tag = 'income_total';
}
if (substr($key, 0, 16) == 'user_play_count_') {
$tag = 'play_count';
}
if (substr($key, 0, 19) == 'user_consume_total_') {
$tag = 'consume_total';
}
if (substr($key, 0, 18) == 'team_member_total_') {
$tag = 'team_member_total';
}
if (substr($key, 0, 18) == 'team_direct_total_') {
$tag = 'team_direct_total';
}
if (substr($key, 0, 20) == 'team_recharge_total_') {
$tag = 'team_recharge_total';
}
if (substr($key, 0, 21) == 'team_withdrawl_total_') {
$tag = 'team_withdrawl_total';
}
if (substr($key, 0, 18) == 'team_income_total_') {
$tag = 'team_income_total';
}
if (substr($key, 0, 16) == 'team_play_count_') {
$tag = 'team_play_count';
}
if (substr($key, 0, 19) == 'team_consume_total_') {
$tag = 'team_consume_total';
}
cache($key, (cache($key) ?? 0) + $value, null, $tag);
}
}
if (!function_exists('build_invite_code')) {
function build_invite_code($id = '')
{
if (empty($id)) {
return '';
}
// 使用一个固定的种子值来增加随机性
$seed = 0x7F4A8C3B;
// 将用户ID转换为数字并加入种子
$num = intval($id) + $seed;
// 使用一个固定的字符集(去掉容易混淆的字符)
$chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
$chars_len = strlen($chars);
$code = '';
// 生成8位邀请码
for ($i = 0; $i < 8; $i++) {
// 使用不同的数学运算来打乱数字
$num = ($num * 31 + $seed) % 0x7FFFFFFF;
// 确保每次取模的结果在字符集范围内
$index = ($num % $chars_len + $chars_len) % $chars_len;
$code .= $chars[$index];
}
return $code;
}
}
if (!function_exists('layun_auth')) {
function layun_auth($type = "url", $version = 1)
{
if ($type == 'url') {
$key = "";
$sercet = "2RxmtM";
if ($version == 1) {
$time = time();
$hash = md5($time . '_' . md5($time . '_' . $sercet));
} else {
}
}
}
}
if (!function_exists('get_parent_id')) {
function get_parent_id($user_id)
{
if (!$user_id) {
return "";
}
$info = parent_info( $user_id);
return $info['id'];
}
}
if (!function_exists('parent_info')) {
function parent_info($user_id,$value=[])
{
if (!$user_id) {
return "";
}
if($value){
cache('user_parent_info_' . $user_id, $value);
return $value;
}
$info = cache('user_parent_info_' . $user_id);
if (!$info) {
$parent_id = \app\model\User::where('id', $user_id)
->value('parent_id');
$info = [['id'=>'','username'=>'']];
if($parent_id){
$info = \app\model\User::where('id',$parent_id)->column('id,username');
}
cache('user_parent_info_' . $user_id, $info[0]);
}
return $info;
}
}
if (!function_exists('get_user_level')) {
function get_user_level($user_id,$performance_small=null)
{
if (!$user_id) {
return 0;
}
if(is_null($performance_small)){
$performance= get_performance($user_id);
$performance_small = $performance[1];
}
$user_level_rules = Config('site.user_level_rules');
$level = 0;
foreach ($user_level_rules as $_level => $score) {
if($performance_small>$score){
$level = $_level;
}
}
return $level;
}
}
if(!function_exists('datetime')){
function datetime($timestamp=0,$format='Y-m-d H:i:s'){
if(!$timestamp){return '';}
if(strpos($timestamp,'-')===false){
if(!$timestamp){return '';}
if($format == 'datetime'){
$format = 'Y-m-d H:i:s';
}
if($format == 'date'){
$format = 'Y-m-d';
}
if($format == 'time'){
$format = 'H:i:s';
}
return date($format,$timestamp);
}
return $timestamp;
}
}
if(!function_exists('get_performance')){
function get_performance($user_id){
$performance_list = \app\model\UserTeam::alias('ut')
->join('user_extend ue', 'ut.descendant_id = ue.user_id')
->where('ut.ancestor_id', $user_id)
->order('ue.sales desc')->column('ue.sales');
if(empty($performance_list)){
$performance_list = [0,0];
}
return [array_shift($performance_list), array_sum($performance_list)];
}
}
if(!function_exists('log_alert')){
function log_alert($data='',$channel='default'){
if(!is_string($data)){
$data = json_encode($data);
}
// if(is_string($data) || is_numeric($data) || is_bool($data)){
// }else{
// $data = json_encode($data);
// }
\support\Log::channel($channel)->alert($data);
}
}
if(!function_exists('enum_dir')){
function enum_dir($path=''){
$list = [];
//$path = substr(0,1,$path) == '/' ? $path
foreach(glob($path) as $afile){
if(is_dir($afile)){
cp($afile);
//$list[] = enum_dir($afile);
} else {
$list[]=$afile;
//rename('./'.$afile,'./'.$name);
echo $afile,"\n";
}
}
return $list ;
}
}
if(!function_exists('jicha')){
function jicha($current_user_id,$amount,$reward_arr=[]){
$distributed_users = [];
$distributed_rate = 0; // 已分配的累计比例
$last_commissioned_level = 0; // 上一次成功分佣的用户角色等级(role_id)
//$reward_arr = [0,0.02,0.04,0.06,0.08,0.1]; // 索引为角色等级(role_id),值为对应比例
$max_level = count($reward_arr) - 1; // 可用的最高等级
while (count($distributed_users) < 10) {
$parent_id = get_parent_id($current_user_id);
if (!$parent_id) {
// cp(sprintf("用户ID:%s\t级别:%s\t父级:%s\t%s\t%s\n\n",
// $parent_id.'',
// '-',
// '-',
// "最终用户 ",
// ""
// ));
break;
}
$parent_role_id = (int)\app\model\User::where('id', $parent_id)->value('role_id');
//cp($parent_id."\t".$parent_role_id."\t".$last_commissioned_level);
if ($parent_role_id > 5) {
// cp(sprintf("用户ID:%s\t级别:%s\t父级:%s\t%s\t%s\n\n",
// $parent_id,
// $parent_role_id,
// '-',
// "用户级别异常",
// ""
// ));
break;
}
// 仅当上级角色等级高于上一次成功分佣的等级时才考虑分配
if ($parent_role_id > $last_commissioned_level) {
$idx = $parent_role_id;
if ($idx > $max_level) { // 超出定义范围则使用最高档
$idx = $max_level;
}
$current_rate = $reward_arr[$idx];
$available_rate = bcsub($current_rate, $distributed_rate, 4);
if (bccomp($available_rate, 0, 6) === 1) { // available_rate > 0
$commission = bcmul($available_rate, $amount, 4);
$distributed_rate += $available_rate;
$last_commissioned_level = $parent_role_id;
$distributed_users[] = [
'user_id' => $parent_id,
'role_id' => $parent_role_id,
'rate' => $available_rate,
'amount' => $commission
];
} else {
// 可分配比例<=0,停止继续查找
// cp(sprintf("用户ID:%s\t级别:%s\t父级:%s\t%s\t%s\n\n",
// $parent_id,
// $parent_role_id,
// '-',
// "可分配比例不足,结束",
// "已分配比例:".$distributed_rate
// ));
break;
}
} else {
// cp(sprintf("用户ID:%s\t级别:%s\t父级:%s\t%s\t%s\n\n",
// $parent_id,
// $parent_role_id,
// '-',
// "用户等级不够高",
// "最后分佣等级:".$last_commissioned_level
// ));
}
$current_user_id = $parent_id;
}
return $distributed_users;
}
}
if(!function_exists('generateShortUniqueID')){
function generateShortUniqueID($length = 8) {
// 生成指定长度的随机字节,转为 Base64 编码并去除不必要字符
return substr(bin2hex(random_bytes($length / 2)), 0, $length);
}
}