$maxAge || $year == $maxAge && $monthDay > 0) { return false; } return true; } // 将15位身份证升级到18位 function convertIDCard15to18($IDCard) { if (strlen($IDCard) != 15) { return false; } else { // 如果身份证顺序码是996 997 998 999,这些是为百岁以上老人的特殊编码 if (array_search(substr($IDCard, 12, 3), array('996', '997', '998', '999')) !== false) { $IDCard = substr($IDCard, 0, 6) . '18' . substr($IDCard, 6, 9); } else { $IDCard = substr($IDCard, 0, 6) . '19' . substr($IDCard, 6, 9); } } $IDCard = $IDCard . calcIDCardCode($IDCard); return $IDCard; } //计算身份证的最后一位验证码,根据国家标准GB 11643-1999 function calcIDCardCode($IDCardBody) { if (strlen($IDCardBody) != 17) { return false; } //加权因子 $factor = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2); //校验码对应值 $code = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'); $checksum = 0; for ($i = 0; $i < strlen($IDCardBody); $i++) { $checksum += substr($IDCardBody, $i, 1) * $factor[$i]; } return $code[$checksum % 11]; } // 代替each函数 function fun_adm_each(&$array) { $res = array(); $key = key($array); if ($key !== null) { next($array); $res[1] = $res['value'] = $array[$key]; $res[0] = $res['key'] = $key; } else { $res = false; } return $res; } /** * 通用化API数据格式输出 * @param $status * @param string $message * @param array $data * @param int $httpStatus * @return \think\response\Json */ function resultJson($status, $message = "error", $data = [], $httpStatus = 200) { $result = [ "status" => $status, "message" => $message, "data" => $data ]; return json($result, $httpStatus); } // 加密 // $a = strEncrypt("13","Su)sjKsu23G0slow"); function strEncrypt($bytes, $key) { $encrypted = openssl_encrypt($bytes, 'aes-128-cbc', $key, OPENSSL_RAW_DATA, $key); // 加密 return bin2hex($encrypted); } // 解密 // $b = hex2bin($a); // var_dump(str_decrypt($b,"Su)sjKsu23G0slow")); function str_decrypt($encrypted, $key) { $decrypted = openssl_decrypt($encrypted, 'aes-128-cbc', $key, OPENSSL_RAW_DATA, $key); if ($decrypted === false) { return ""; } return $decrypted; } function strDecrypt($encrypted, $key) { $decrypted = openssl_decrypt(hex2bin($encrypted),'aes-128-cbc',$key,OPENSSL_RAW_DATA,$key); if ($decrypted === false) { return ""; } return $decrypted; } function pkcs5UnPadding($origData) { $length = strlen($origData); $unpadding = ord($origData[$length - 1]); return substr($origData, 0, $length - $unpadding); } function pkcs5_padding($ciphertext, $blockSize) { $padding = $blockSize - (strlen($ciphertext) % $blockSize); $padtext = str_repeat(chr($padding), $padding); return $ciphertext . $padtext; } /** * 修复指定游戏的区服ID问题 * @param string $gameid 游戏ID * @param string $serverid 区服ID * * @return string */ function repairRoleServerid($gameid, $serverid) { if(APP_STATUS == 'stable'){ if (in_array($gameid, ['266', '259'])) { if(mb_strlen($serverid) < 10){ $serverid = str_pad($serverid+10010000, 10, "0", STR_PAD_LEFT); } } }else if(APP_STATUS == 'dev'){ if (in_array($gameid, ['266', '259'])) { if(mb_strlen($serverid) < 10){ $serverid = str_pad($serverid+10010000, 10, "0", STR_PAD_LEFT); } } } return $serverid; } /** * 随机获取一个支付方式 * * @param $paymentTypes * * @return false|mixed */ function getRandomPaymentType($paymentTypes) { // 计算所有概率的总和 $totalProbability = array_sum(array_column($paymentTypes, 'probability')); if ($totalProbability == 0) { return []; // throw new Exception("所有的概率值都为0,无法进行随机选择。"); } // 生成一个 0 到 1 之间的随机数 $rand = mt_rand() / mt_getrandmax(); $cumulativeProbability = 0.0; foreach ($paymentTypes as $paymentType) { // 归一化概率 $normalizedProbability = $paymentType['probability'] / $totalProbability; $cumulativeProbability += $normalizedProbability; if ($rand <= $cumulativeProbability) { return $paymentType; } } // 如果没有匹配项,返回最后一个(理论上不应该发生) return end($paymentTypes); } /** * 注册管控 * 每个月,同一个设备号注册上限三个,同一个IP注册上限五个。 * * @param string $ip 访问的IP地址 * @param string $imeil 访问的Imeil标识 * * @return bool true:允许访问 false:超出频率限制 */ function regidterHandle($ip, $imeil){ try { $redis = think\Cache::store('default')->handler(); $monthKey = date('Y-m', time()); $ipKey = "register:ip_limit:{$monthKey}_{$ip}"; $imeilKey = "register:imeil_limit:{$monthKey}_{$imeil}"; $ipLimit = (new Setting())->getSetting('register_month_ip_limit'); $imeilLimit = (new Setting())->getSetting('register_month_imeil_limit'); $expireTime = strtotime(date('Y-m-t 23:59:59')) - time(); $redis->multi(); $redis->incr($ipKey, 1); $redis->expire($ipKey, $expireTime); $redis->incr($imeilKey, 1); $redis->expire($imeilKey, $expireTime); $results = $redis->exec(); $ipCount = $results[0]; $imeilCount = $results[2]; // trace("# regidterHandle: {$ipLimit}, {$imeilLimit} -- {$ipCount}, {$imeilCount}", 'error'); // 检查是否超出限制 if($ipCount > $ipLimit || $imeilCount > $imeilLimit) { return false; } return true; } catch (\Exception $e) { trace('# 注册管控异常: ' . $e->getMessage(), 'error'); // TODO: 钉钉异常通知 } return false; } /** * 请求频率管控 * * @param string $userName 玩家用户名 * @param string $route 请求路由(例如:v1.login/index) * * @return bool true:允许访问 false:超出频率限制 */ function requestFrequencyHandle($userName, $route){ try { $count = Env::get('REQUEST_FREQUENCY_COUNT', 3); // 请求次数 $time = Env::get('REQUEST_FREQUENCY_TIME', 60); // 请求间隔时间(秒) $redis = think\Cache::store('default')->handler(); $key = "request:frequency:{$userName}:{$route}"; $currentCount = $redis->get($key); if ($currentCount === false) { $redis->setex($key, $time, 1); return true; } if ($currentCount < $count) { $redis->incr($key); return true; } return false; } catch (\Exception $e) { trace('# 请求频率管控异常: ' . $e->getMessage(), 'error'); // TODO:钉钉异常通知 } return true; } /** * 判断数组中是否包含数组中键 * * @param array $data 数据数组 * @param array $requiredKeys 需要判断的键数组 * * @return array */ function checkDataHasKeys(array $data, array $requiredKeys) { $missing = []; foreach ($requiredKeys as $key) { $keys = explode('.', $key); $temp = $data; foreach ($keys as $k) { if (!isset($temp[$k])) { $missing[] = $key; break; } $temp = $temp[$k]; } } return $missing; } /** * 判断数组A是否包含数组B中的所有键 * * @param array $arrayA * @param array $keysB * * @return bool */ function hasAllKeys(array $arrayA, array $keysB): bool { foreach ($keysB as $key) { if (!array_key_exists($key, $arrayA)) { return true; } } return false; } /** * 是否虚拟号判断 * * @param $mobile 需要判断的手机号 * * | 号段 | 运营商 | 备注 | * | ----------------------- | ----------- | ---------------------- | * | 1700/1701/1702 | 中国电信虚拟运营商 | 归属电信网络 | * | 1703/1705/1706 | 中国移动虚拟运营商 | 归属移动网络 | * | 1704/1707/1708/1709 | 中国联通虚拟运营商 | 归属联通网络 | * | 171 | 联通专属虚拟运营商号段 | 常见于阿里通信、京东通信等 | * | 162 | 用于物联网和行业专用 | 虚拟运营商/物联网号段(部分归联通) | * | 165 | 联通新型虚拟号段 | 多用于物联网、政企服务 | * | 167 | 移动新型虚拟号段 | 多为企业服务、物联网 | * | 140/141/144/146/148 | 物联网卡号段 | 用于工业/车联网设备等(注意:这不是通话号) | * * @return false|int */ function isVirtualMobile($mobile) { // 清洗手机号 $mobile = preg_replace('/\D/', '', $mobile); // 匹配虚拟号段 return preg_match('/^1(70\d|71\d|65\d|67\d)\d{7}$/', $mobile); } /** * base64封装 * @param $str string 需要加密或解密的字符串 * @param $type string 加密或解密类型,'encode' 为加密,'decode' 为解密 * @param $salt string 加密或解密的盐值,必须与加密和解密时保持一致 * * @return false|string */ function base64_salt($str, $type = 'encode', $salt = 'qmsdk') { if ($type === 'encode') { return base64_encode($str . $salt); } else { $decoded = base64_decode($str); return substr($decoded, 0, -strlen($salt)); } }