| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136 |
- <?php
- // +----------------------------------------------------------------------
- // | ThinkPHP [ WE CAN DO IT JUST THINK ]
- // +----------------------------------------------------------------------
- // | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
- // +----------------------------------------------------------------------
- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
- // +----------------------------------------------------------------------
- // | Author: 流年 <liu21st@gmail.com>
- // +----------------------------------------------------------------------
- use app\common\model\Setting;
- use GuzzleHttp\Client;
- use think\Cache;
- use think\Config;
- use think\Db;
- use think\db\exception\DataNotFoundException;
- use think\db\exception\ModelNotFoundException;
- use think\Env;
- use think\Exception;
- use think\exception\DbException;
- /**
- * 取得随机数
- *
- * @param int $length 生成随机数的长度
- * @param int $numeric 是否只产生数字随机数 1是0否
- * @return string
- */
- function random($length, $numeric = 0)
- {
- $seed = base_convert(md5(microtime() . $_SERVER['DOCUMENT_ROOT']), 16, $numeric ? 10 : 35);
- $seed = $numeric ? (str_replace('0', '', $seed) . '012340567890') : ($seed . 'zZ' . strtoupper($seed));
- $hash = '';
- $max = strlen($seed) - 1;
- for ($i = 0; $i < $length; $i++) {
- $hash .= $seed[mt_rand(0, $max)];
- }
- return $hash;
- }
- /**
- * 二维数组根据字段进行排序
- * @params array $array 需要排序的数组
- * @params string $field 排序的字段
- * @params string $sort 排序顺序标志 SORT_DESC 降序;SORT_ASC 升序
- *
- */
- function arraySequence($array, $field, $sort = 'SORT_DESC')
- {
- $arrSort = array();
- if (empty($array)) {
- return $arrSort;
- }
- foreach ($array as $uniqid => $row) {
- foreach ($row as $key => $value) {
- $arrSort[$key][$uniqid] = $value;
- }
- }
- array_multisort($arrSort[$field], constant($sort), $array);
- return $array;
- }
- /**
- * 因为 < php7.0自带的array_column函数无法作用于对象数组,该函数用于返回对象数组中指定的一列
- *
- * @param $arr : array 需要取出数组列的二维数组
- * @param $column_key :数组的列的键
- * @return array :一维数组
- *
- */
- function arrayColumnByObject($arr, $column_key, $index_key = null)
- {
- $result = array();
- if (!empty($arr)) {
- foreach ($arr as $value) {
- if (!empty($index_key)) {
- $result[$value[$index_key]] = $value[$column_key];
- } else
- $result[] = $value[$column_key];
- }
- }
- return $result;
- }
- /**
- * 转换字符串中的HTML、js等XSS字符
- * @param string $value 需要转义的值
- *
- * @return string
- */
- function escape($value)
- {
- if (empty($value)) {
- return $value;
- }
- return htmlspecialchars($value, ENT_COMPAT, 'UTF-8');
- }
- /**
- * HTML过滤XSS字符
- * @param mixed $value 需要转义的值
- * @return mixed
- */
- function removeXss($value)
- {
- if (empty($value)) {
- return $value;
- }
- vendor('htmlpurifier.library.HTMLPurifier#auto');
- // 实例化 HTMLPurifier对象
- $purifier = new HTMLPurifier();
- // 返回过滤后的数据
- return $purifier->purify($value);
- }
- /**
- * 生成返回跳转的url
- *
- * @param string $url 生成的URL地址或路由地址
- * @param string|array $params 参数
- * @param bool|string $suffix 生成的URL后缀
- * @param bool|string $domain 域名
- *
- * @return string
- */
- function createRefererUrl($url = null, $params = '', $suffix = true, $domain = false)
- {
- if (is_null($url) && isset($_SERVER["HTTP_REFERER"])) {
- $url = $_SERVER["HTTP_REFERER"];
- } else {
- $url = empty($params) ? url($url, '', $suffix, $domain) :
- url($url, '', $suffix, $domain) . '?' . http_build_query($params, null, '&');
- }
- return urlencode($url);
- }
- /**
- * 旧版SDK加密解密函数(来自discuz)
- * @param $string string 明文或密文
- * @param $operation string DECODE为解密,ENCODE为加密
- * @param $key string 密钥
- * @param $expiry int 有效期,秒为单位,0为永久
- */
- function auth_code($string, $operation = 'DECODE', $key = '', $expiry = 0)
- {
- $ckey_length = 0;
- $key = md5($key ? $key : Env::get('auth_key'));
- $keya = md5(substr($key, 0, 16));
- $keyb = md5(substr($key, 16, 16));
- $keyc = $ckey_length ?
- ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) : '';
- $cryptkey = $keya . md5($keya . $keyc);
- $key_length = strlen($cryptkey);
- $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) :
- sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
- $string_length = strlen($string);
- $result = '';
- $box = range(0, 255);
- // $box = 100;
- $rndkey = array();
- for ($i = 0; $i <= 255; $i++) {
- $rndkey[$i] = ord($cryptkey[$i % $key_length]);
- }
- for ($j = $i = 0; $i < 256; $i++) {
- $j = ($j + $box[$i] + $rndkey[$i]) % 256;
- $tmp = $box[$i];
- $box[$i] = $box[$j];
- $box[$j] = $tmp;
- }
- for ($a = $j = $i = 0; $i < $string_length; $i++) {
- $a = ($a + 1) % 256;
- $j = ($j + $box[$a]) % 256;
- $tmp = $box[$a];
- $box[$a] = $box[$j];
- $box[$j] = $tmp;
- $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
- }
- if ($operation == 'DECODE') {
- if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
- return substr($result, 26);
- } else {
- return '';
- }
- } else {
- return $keyc . str_replace('=', '', base64_encode($result));
- }
- }
- /**
- * 字符串模糊处理,中间4位以*代替
- * @param string $str 需要模糊处理的字符串
- * @param int $startlen 开头明文显示的长度
- *
- * @return string
- */
- function stringObfuscation($str, $startlen = 3)
- {
- if (!$str) {
- return '***';
- }
- $len = mb_strlen($str, 'utf-8');
- if ($len <= $startlen) {
- return mb_substr($str, 0, 1) . str_repeat('*', $len - 1);
- } elseif ($len <= ($startlen + 4)) {
- return mb_substr($str, 0, $startlen) . str_repeat('*', $len - $startlen);
- } else {
- return mb_substr($str, 0, $startlen) . str_repeat('*', 4) . mb_substr($str, $startlen + 4);
- }
- return $str;
- }
- /**
- * 身份证模糊处理,中间以*代替
- * @param string $str
- * @return string
- */
- function idcardObfuscation($str)
- {
- if (!$str || $str == '-1') {
- return $str;
- }
- $len = mb_strlen($str, 'utf-8');
- return mb_substr($str, 0, 4) . str_repeat('*', $len - 8) . mb_substr($str, -4);
- }
- /**
- * 密码模糊处理,中间以*代替
- * @param string $str
- * @return string
- */
- function passwordObfuscation($str)
- {
- if (!$str) {
- return '***';
- }
- $len = mb_strlen($str, 'utf-8');
- return mb_substr($str, 0, 3) . str_repeat('*', $len - 6) . mb_substr($str, -3);
- }
- /**
- * 手机号模糊处理,中间4位以*代替
- *
- * @param string $mobile
- * @return string 处理过的手机号
- */
- function mobileObfuscation($mobile)
- {
- if (empty($mobile)) {
- return null;
- }
- return substr($mobile, 0, 3) . '****' . substr($mobile, 7);
- }
- /**
- * 用户名模糊处理
- * @param $username
- * @return string
- */
- function usernameObfuscation($username)
- {
- $len = mb_strlen($username, 'utf-8');
- if ($len == 2) {
- return mb_substr($username, 0, 1) . '*';
- } else if ($len > 2) {
- return mb_substr($username, 0, 1) . str_repeat('*', $len - 2) . mb_substr($username, -1);
- }
- return $username;
- }
- /**
- * 邮箱地址模糊处理,显示头两位及@之后的
- * @param string $mail
- * @return string 处理过的邮箱地址
- */
- function mailObfuscation($mail)
- {
- if (empty($mail)) {
- return null;
- }
- return substr($mail, 0, 3) . '****' . strstr($mail, '@');
- }
- if (!function_exists('filterAndTrimInput')) {
- function filterAndTrimInput($val)
- {
- return escape(trim($val));
- }
- }
- /**
- * 根据传来的日期,生成时间戳区间查询条件,日期格式:2016-01-01
- * @param string $start_time 开始时间
- * @param string $end_time 结束时间
- * @return array 时间戳查询条件
- */
- function makeTimeCondition($start_time = '', $end_time = '')
- {
- $start_time = !empty($start_time) ? strtotime($start_time) : '';
- $end_time = !empty($end_time) ? strtotime($end_time . ' 23:59:59') : '';
- $condition = array();
- if (!empty($start_time)) {
- $condition = array('EGT', $start_time);
- }
- if (!empty($end_time)) {
- $condition = array('ELT', $end_time);
- }
- if (!empty($start_time) && !empty($end_time)) {
- $condition = array(array('EGT', $start_time), array('ELT', $end_time));
- }
- return $condition;
- }
- /**
- * 写入日志
- *
- * @param string $path 日志路径
- * @param string $level
- * log 常规日志,用于记录日志
- * error 错误,一般会导致程序的终止
- * notice 警告,程序可以运行但是还不够完美的错误
- * info 信息,程序输出信息
- * debug 调试,用于调试信息
- * sql SQL语句,用于SQL记录,只在数据库的调试模式开启时有效
- */
- function log_message($msg, $level = 'info', $path = LOG_PATH)
- {
- \think\Log::init([
- 'type' => 'File',
- 'path' => $path,
- 'file_size' => 10485760
- ]);
- \think\Log::write($msg, $level);
- }
- /**
- * 获得cURL请求数据
- *
- * @param string $url 请求的URL
- * @param array $post_fields “POST”请求的数据
- * @return mixed
- */
- function getCurlContent($url, $post_fields = array())
- {
- $h_curl = curl_init();
- curl_setopt($h_curl, CURLOPT_URL, $url);
- curl_setopt($h_curl, CURLOPT_HEADER, false);
- curl_setopt($h_curl, CURLOPT_FOLLOWLOCATION, 1);
- curl_setopt($h_curl, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($h_curl, CURLOPT_TIMEOUT, 60);
- curl_setopt($h_curl, CURLOPT_SSL_VERIFYPEER, false); // 阻止对证书的合法性的检查
- curl_setopt($h_curl, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在
- if (!empty($post_fields)) {
- curl_setopt($h_curl, CURLOPT_POST, 1);
- curl_setopt($h_curl, CURLOPT_POSTFIELDS, $post_fields);
- if (is_string($post_fields)) {
- curl_setopt($h_curl, CURLOPT_HTTPHEADER, array(
- 'Content-Type: application/json; charset=utf-8',
- 'Content-Length: ' . strlen($post_fields))
- );
- }
- }
- $contents = curl_exec($h_curl);
- curl_close($h_curl);
- return $contents;
- }
- /**
- * 生成订单号,每秒可产生9999个不重复的订单号
- * @return string
- */
- function makeOrderid($head = 'WL')
- {
- $redis = think\Cache::store('default');
- $orderid = (microtime(true) * 10000) . substr(str_pad(mt_rand(), 6, "0", STR_PAD_BOTH), 0, 6);
- // 判断是否有同个订单号的请求还没处理完,防止因为网络问题,导致同个订单并发
- $cache_key = 'orderid:' . $orderid;
- if ($redis->has($cache_key)) {
- return makeOrderid();
- } else {
- $redis->set($cache_key, $orderid, 86400);
- return $head . $orderid;
- }
- }
- /**
- * 生成平台币订单号
- *
- */
- function makeCoinSn()
- {
- return 'sn' . makeOrderid();
- }
- /**
- * 获取渠道顶级信息
- *
- * @param $channelId int 当前渠道ID
- *
- * @return array 渠道信息
- */
- function get_top_channel($channelId)
- {
- $channelModel = \Think\Db::name('nw_channel');
- // $channelLevel = get_channel_level($channelId);
- $i = 0;
- while ($info = $channelModel->where(['id' => $channelId])->find()) {
- $i++;
- if ($info['level'] == 1 || $info['parent_id'] == $info['id'] || $i > 5) {
- return $info;
- }
- $channelId = $info['parent_id'];
- }
- }
- /**
- * 获取渠道联盟信息
- *
- * @param $channelId int 当前渠道ID
- *
- * @return array 渠道信息
- */
- function get_union_channel($channelId)
- {
- $data = \think\Cache::get('get_union_channel'.$channelId);
- if(!empty($data)){
- return json_decode($data,true);
- }
- $channelModel = \Think\Db::name('nw_channel');
- // $channelLevel = get_channel_level($channelId);
- $i = 0;
- while ($info = $channelModel->where(['id' => $channelId])->find()) {
- $i++;
- if ($info['level'] == 0 || $info['parent_id'] == $info['id'] || $i > 5) {
- \think\Cache::set('get_union_channel'.$channelId,json_encode($info),60);
- return $info;
- }
- $channelId = $info['parent_id'];
- }
- }
- /**
- * 获取渠道等级
- */
- function get_channel_level($channelId)
- {
- $level = 3;
- $channelModel = \Think\Db::name('nw_channel');
- while ($info = $channelModel->where(['id' => $channelId])->find()) {
- return $info['level'];
- /*
- if ($info['level'] == 1) {
- return $level;
- }
- $channelId = $info['parent_id'];
- $level++;
- */
- }
- }
- /**
- * 获取顶级二级渠道
- * 获取渠道信息
- */
- function get_top_second_channel_name($channelId)
- {
- $keyName = 'get_top_second_channel_name:'.$channelId;
- $data = \think\Cache::get($keyName);
- if(!empty($data)){
- return json_decode($data,true);
- }
- $channel = [];
- $channelModel = \think\Db::name('nw_channel');
- $level = 0;
- $newChannelId = $channelId;
- while ($info = $channelModel->field('id,name,parent_id,level')->where(['id' => $newChannelId, 'flag' => ['in', '1,2,3']])->find()) {
- $channel[$level] = $info['name'];
- $level++;
- //如果上级是最高级渠道退出循环
- if ($info['level'] == 1) {
- break;
- }
- //测试数据存在 多个parent_id = 0的情况 后续不需要去掉
- if (!$info['parent_id']) {
- break;
- }
- $newChannelId = $info['parent_id'];
- }
- //反转数组
- $channel = array_reverse($channel);
- if (!$level) {
- $res = [
- 'channel_name' => '',
- 'second_name' => '',
- 'top_name' => '',
- ];
- }
- //二级渠道情况
- if ($level == 2) {
- $res = [
- 'channel_name' => isset($channel[1]) ? $channel[1] : '',
- 'second_name' => '',
- 'top_name' => isset($channel[0]) ? $channel[0] : '',
- ];
- } elseif ($level > 2) { //三级渠道情况
- $res = [
- 'channel_name' => isset($channel[count($channel) - 1]) ? $channel[count($channel) - 1] : "",
- 'second_name' => isset($channel[1]) ? $channel[1] : '',
- 'top_name' => isset($channel[0]) ? $channel[0] : '',
- ];
- } else {
- //最高级渠道情况
- $res = [
- 'channel_name' => isset($channel[0]) ? $channel[0] : '',
- 'second_name' => '',
- 'top_name' => '',
- ];
- }
- \think\Cache::set($keyName, json_encode($res),60);
- return $res;
- }
- /**
- * 获取顶级二级渠道名称 - v2 优化版
- *
- * 原版问题:while 循环逐级向上查 parent_id,N 级渠道就打 N 次 SQL。
- * 优化思路:nw_channel.id_path 已存储完整祖先 ID 链(逗号分隔),
- * 只需 1 次查询取出当前渠道的 id_path + level,
- * 再用 1 次 IN 查询批量拿到所有祖先名称,合计固定 2 次 SQL。
- *
- * id_path 格式示例(自身 id 也包含在内):
- * level=0(B+): "10"
- * level=1(B): "10,20"
- * level=2(B-): "10,20,300"
- * level=3(推广员): "10,20,300,500"
- *
- * 返回结构与原版保持一致:
- * [
- * 'channel_name' => 当前渠道名(最末级),
- * 'second_name' => 二级渠道名(level=1,B账号),无则空字符串,
- * 'top_name' => 顶级渠道名(level=0,B+账号),
- * ]
- *
- * @param int $channelId
- * @return array
- */
- function get_top_second_channel_name_v2($channelId)
- {
- // ---------- 0. 空值快速返回 ----------
- $emptyRes = ['channel_name' => '', 'second_name' => '', 'top_name' => '', 'union_name' => ''];
- if (empty($channelId)) {
- return $emptyRes;
- }
- // ---------- 1. 缓存读取 ----------
- $keyName = 'get_top_second_channel_name_v2:' . $channelId;
- $cached = \think\Cache::get($keyName);
- if (!empty($cached)) {
- return json_decode($cached, true);
- }
- // ---------- 2. 查询当前渠道的 id_path ----------
- $self = \think\Db::name('nw_channel')
- ->field('id, name, level, id_path')
- ->where(['id' => $channelId])
- ->find();
- if (empty($self)) {
- \think\Cache::set($keyName, json_encode($emptyRes), 60);
- return $emptyRes;
- }
- // ---------- 3. 解析 id_path,获取所有上级渠道 ID ----------
- // id_path 格式:",10,5213,5214,"
- $idPath = trim($self['id_path'], ',');
-
- // 当前渠道名称
- $channelName = $self['name'];
- $topName = $twoName = $threeName ='';
- if (!empty($idPath)) {
- // 解析上级 ID 列表
- $parentIds = array_filter(array_map('intval', explode(',', $idPath)));
-
- if (!empty($parentIds)) {
- // ---------- 4. 批量查询所有上级渠道的 id/name/level ----------
- $parents = \think\Db::name('nw_channel')
- ->field('id, name, level')
- ->where(['id' => ['in', $parentIds]])
- ->select();
- // 以 level 为键建立映射
- $levelMap = [];
- foreach ($parents as $row) {
- $levelMap[(int)$row['level']] = $row['name'];
- }
- // 根据层级组装返回
- // level: 0=B+(顶级), 1=B(二级), 2=B-(三级), 3=推广员(四级)
- $topName = $levelMap[0] ?? ''; // 顶级渠道(B+)
- $twoName = $levelMap[1] ?? ''; // 二级渠道(B)
- $threeName = $levelMap[2] ?? ''; // 三级渠道(B-)
- }
- }
- // ---------- 5. 组装返回结构 ----------
- $res = [
- 'channel_name' => $channelName,
- 'second_name' => $threeName,
- 'top_name' => $twoName,
- 'union_name' => $topName,
- ];
- // ---------- 6. 写缓存并返回 ----------
- \think\Cache::set($keyName, json_encode($res), 60);
- return $res;
- }
- /**
- * 根据渠道ID,获取四级渠道,并根据渠道等级为键
- * 四级渠道=层级:0(B+账号),1(B账号),2(B-账号),3(推广员)
- * @param $channelId
- *
- * @return array
- * @throws DataNotFoundException
- * @throws ModelNotFoundException
- * @throws DbException
- */
- function get_channel_top_by_aggregate($channelId)
- {
- $id_path = Db::table('nw_channel')->field('id,name,level,id_path')->where('id', $channelId)->value('id_path');
- if(!$id_path) return [];
- $id_path = $channelId . ',' . $id_path;
- $id_path = array_filter(explode(',', $id_path));
- $list = Db::table('nw_channel')->field('id,name,level')->where('id', 'in', $id_path)->select();
- if(!$list) return [];
- $result = array_column($list, null, 'level');
- return $result;
- }
- /**
- * 获取渠道名称
- */
- function get_channel_name($id)
- {
- $info = \Think\Db::name('nw_channel')->field('name')->where(['id' => $id])->find();
- return $info['name'];
- }
- /**
- * 获取聚合渠道名称
- */
- function get_complex_channel_name($id)
- {
- $info = \Think\Db::name('nw_complex_channel')->field('name')->where(['id' => $id])->find();
- return $info['name'];
- }
- /**
- * 获取子渠道
- *
- * @param $channelId integer 当前渠道ID
- * @param $deep 层级深度(默认不传值时,取所有子渠道)
- *
- */
- function get_child_channel_arr($channelId, $deep = '')
- {
- $result = [];
- $channelModel = \think\Db::name('nw_channel');
- //所有子渠道
- if (empty($deep)) {
- while ($info = $channelModel->field('id')->whereIn('parent_id', $channelId)->select()) {
- $tmp = [];
- if (!empty($info)) {
- foreach ($info as $v) {
- $result[] = $v['id'];
- $tmp[] = $v['id'];
- }
- $channelId = $tmp;
- }
- }
- } //层级深度以内的子渠道
- else {
- while ($info = $channelModel->field('id')->whereIn('parent_id', $channelId)->select()) {
- $deep--;
- $tmp = [];
- if (!empty($info)) {
- foreach ($info as $v) {
- $result[] = $v['id'];
- $tmp[] = $v['id'];
- }
- $channelId = $tmp;
- }
- if ($deep == 0) break;
- }
- }
- return $result;
- }
- /**
- * 获取游戏名称
- */
- function get_game_name($id)
- {
- $info = \Think\Db::name('cy_game')->field('name')->where(['id' => $id])->find();
- return $info['name'];
- }
- /**
- * 清理游戏名称末尾的平台标识,得到公共游戏组名称。
- *
- * @param string $name
- * @return string
- */
- function cleanGameName(string $name): string
- {
- $patterns = [
- '/\s*((?:安卓|IOS|ios|H5|h5))\s*$/iu',
- '/\s*[-—]?\s*(安卓|IOS|ios|H5|h5)\s*$/iu',
- '/\s*\(?(?:安卓|IOS|ios|H5|h5)\)?\s*$/iu',
- ];
- foreach ($patterns as $pattern) {
- $name = preg_replace($pattern, '', $name);
- }
- return trim($name);
- }
- /**
- * 获取后台管理员名称
- */
- function get_admin_name($id)
- {
- $info = \Think\Db::name('cy_admin')->field('id,username')->where(['id' => $id])->find();
- return $info['username'];
- }
- /**
- * 后台及推广平台用户密码加密方法
- * @param string $pw 要加密的原始密码
- * @return string
- */
- function mg_password($pw)
- {
- return md5(Env::get('ADMIN_PASS_PRE') . $pw);
- }
- /**
- * 判断游戏母包是否存在,查询结果做10秒缓存,减轻接口的压力
- * @param $pinyin string
- * @param $platform integer 游戏平台 0:安卓; 1:IOS
- * @param $apktype integer 游戏包类型 0:普通包,1:光环包
- *
- * @return bool | string 返回false或者'False'可判断为母包不存在
- */
- function isRawGameApkExistBak($pinyin, $platform, $apktype)
- {
- $cache_key = 'subpackage:apkexist:' . $pinyin . ':apktype:' . $apktype;
- $redis = \think\Cache::store('default');
- if ($redis->has($cache_key)) {
- $redisInfo = $redis->get($cache_key);
- //日志记录
- if (is_array($redisInfo)) {
- log_message('请求母包是否存在redis:' . print_r($redisInfo, true), 'log', LOG_PATH . 'subpackage/');
- } else {
- log_message('请求母包是否存在redis:' . $redisInfo, 'log', LOG_PATH . 'subpackage/');
- }
- return $redisInfo;
- }
- $apk_verify_url = Env::get('mubao.upload_url', 'http://127.0.0.1:9998') . '/apk_exists?gamename=' . $pinyin . '&platform=' . ($platform == 1 ? 'ios' : 'android') . '&apktype=' . $apktype;
- try {
- $request_result = (new Client(['timeout' => 30]))->get($apk_verify_url);
- $response = $request_result->getBody(); //返回的是个对象
- } catch (Exception $e) {
- log_message('请求母包是否存在接口失败: gamename=' . $pinyin . '; ' . $e->getMessage(). " -- url:". $apk_verify_url, 'error', LOG_PATH . 'subpackage/');
- return false;
- }
- $json = json_decode($response, true);
- $error = json_last_error();
- //没有json错误时
- if (json_last_error() == JSON_ERROR_NONE) {
- $result = $json;
- } else {
- $result = 'False';
- }
- //日志记录
- if (is_array($result)) {
- log_message('请求母包是否存在接口: ' . $apk_verify_url . ' data: ' . print_r($result, true), 'log', LOG_PATH . 'subpackage/');
- } else {
- log_message('请求母包是否存在接口: ' . $apk_verify_url . ' data: ' . $result, 'log', LOG_PATH . 'subpackage/');
- }
- $redis->set($cache_key, $result, 300);
- return $result;
- }
- // 判断母包是否存在
- function isRawGameApkExist($pinyin, $platform, $apktype)
- {
- $apk_verify_url = Env::get('mubao.upload_url', 'http://127.0.0.1:9998') . '/apk_exists?gamename=' . $pinyin . '&platform=' . ($platform == 1 ? 'ios' : 'android') . '&apktype=' . $apktype;
- try {
- $request_result = (new Client(['timeout' => 30]))->get($apk_verify_url);
- $response = $request_result->getBody(); //返回的是个对象
- return $response;
- } catch (Exception $e) {
- log_message('请求母包是否存在接口失败: gamename=' . $pinyin . '; ' . $e->getMessage() . " -- url:" . $apk_verify_url, 'error', LOG_PATH . 'subpackage/');
- return false;
- }
- }
- // 同步分包请求
- function gameApkPack($data)
- {
- $url = Env::get('mubao.upload_url', 'http://127.0.0.1:9998') . '/pack';
- try {
- // $request_result = (new Client(['timeout' => 60]))->post($url, $data);
- // $response = $request_result->getBody(); //返回的是个对象
- $response = getHttpPost($url, $data);
- return $response;
- } catch (Exception $e) {
- log_message("同步分包: channel_id={$data['channel_id']}, filename=" . $data['filename'] . '; error=' . $e->getMessage() . " -- url:" . $url, 'error', LOG_PATH . 'subpackage/');
- return false;
- }
- }
- /**
- * 获取渠道的指定冻结选项的状态
- * 此方法除了判断自身渠道的状态, 也将追溯所有层级的父渠道相应的状态
- * 如果有被禁止的就会返回被禁止状态
- *
- * @param <int> $p_depId 渠道ID
- * @param <int> $p_gameId 游戏ID
- * @param <string> $p_field 选项的数据库字段名
- *
- * @return <bool> true: 被禁止; false: 未禁止
- */
- function isFrozenOption($p_depId, $p_gameId, $p_field = 'subpackage')
- {
- $isFrozen = false;
- $depId = (int)$p_depId;
- $gameId = (int)$p_gameId;
- $field = trim($p_field);
- $i = 0;
- $findFrozenStatus = true; //循环控制标识
- do {
- $channelFrozenModel = \think\Db::name('nw_channel_frozen');
- $frozenStatus = $channelFrozenModel->cache('common:isFrozenOption:' . $p_field . ':' . $depId . ':' . $gameId, 300)->field($field)->where(['channel_id' => $depId, 'game_id' => $gameId])->find();
- if (!empty($frozenStatus) && $frozenStatus[$p_field] == 1) {
- $isFrozen = true;
- $findFrozenStatus = false;
- }
- if ($depId == config('TOP_CHANNEL_ID')) {
- $findFrozenStatus = false;
- }
- if ($findFrozenStatus) {
- $channelModel = \think\Db::name('nw_channel');
- $depId = (int)$channelModel->cache('common:isFrozenOptionChannel:' . $depId, 300)->where(['id' => $depId])->value('parent_id');
- if (empty($depId)) {
- $findFrozenStatus = false;
- }
- }
- if ($i > 10) {
- $findFrozenStatus = false;
- }
- $i++;
- } while ($findFrozenStatus);
- return $isFrozen;
- }
- /**
- * 调用钉钉机器人函数
- *
- */
- function curlDD($msg = '', $url, $all = false)
- {
- // return false;
-
- // 30分钟只提醒一次
- $cacheName = md5($msg);
- $redis = think\Cache::store('default')->handler();
- $cacheKey = "dd:message:".$cacheName;
- $cacheNum = $redis->get($cacheKey);
- if($cacheNum){
- $redis->incr($cacheKey);
- if(!in_array($cacheNum, [1,5,10, 30, 50, 100])){
- return true;
- }
- }else{
- $cacheNum = 1;
- $redis->setex($cacheKey, 1800, 1);
- }
- $msg .= ' - 循环次数:'.$cacheNum;
- if($msg && Env::get('dingtalk.server_name', '')){
- $server_name = Env::get('dingtalk.server_name', '');
- $msg = "[{$server_name}]: ".$msg;
- }
- if ($all) {
- $postData = [
- 'msgtype' => 'text',
- 'text' => [
- 'content' => mb_convert_encoding($msg, "UTF-8", "auto"),
- 'mentioned_list' => array('@all'),
- ],
- 'at' => [
- "atMobiles" => [Config::get('dd_message')['develop']],
- ]
- ];
- } else {
- $postData = [
- 'msgtype' => 'text',
- 'text' => [
- 'content' => mb_convert_encoding($msg, "UTF-8", "auto"),
- ]
- ];
- }
- $postString = json_encode($postData);
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 对认证证书来源的检查
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_POST, true);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $postString);
- curl_setopt($ch, CURLOPT_HTTPHEADER, array(
- "Content-Type: application/json"
- ));
- $result = curl_exec($ch);
- $HTTP_CODE = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- curl_close($ch);
- // if($HTTP_CODE<>200){
- // $arr=array("state"=>-100);
- // echo json_encode($arr);
- // exit;
- // }elseif($result==""){
- // $arr=array("state"=>0,"errMsg"=>"接口返回为空");
- // echo json_encode($arr);
- // exit;
- // }
- return $result;
- }
- /**
- * 调用钉钉机器人函数-markdown
- *
- * @param $msg 消息内容字符串
- * @param $url 机器人地址
- * @param $devalop 是否@负责人
- *
- * @return bool|string
- */
- function curlDDMarkdown($msg, $url, $devalop = false)
- {
- // 30分钟只提醒一次
- $cacheName = md5($msg);
- $redis = think\Cache::store('default')->handler();
- $cacheKey = "dd:message:".$cacheName;
- $cacheNum = $redis->get($cacheKey);
- if($cacheNum){
- $redis->incr($cacheKey);
- if(!in_array($cacheNum, [1,5,10, 30, 50, 100])){
- return true;
- }
- }else{
- $cacheNum = 1;
- $redis->setex($cacheKey, 1800, 1);
- }
- $msg .= ' - 循环次数:'.$cacheNum;
- $title = config('dd_notice.server_name')??'祁盟';
- if($devalop){
- $msg = $title." @".config('dd_notice.develop')." \n ". $msg;
- $postData = [
- 'msgtype' => 'text',
- 'text' => [
- // 'content' => mb_convert_encoding($msg, "UTF-8", "auto"),
- 'content' => $msg,
- 'mentioned_list' => array('@all'),
- ],
- 'at' => [
- "atMobiles" => [Config::get('dd_notice')['develop']],
- ]
- ];
- }else{
- $postData = [
- 'msgtype' => 'markdown',
- "markdown" => [
- 'title' => $title,
- 'text' => $title.' '.$msg,
- ],
- ];
- }
- $postString = json_encode($postData);
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 对认证证书来源的检查
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_POST, true);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $postString);
- curl_setopt($ch, CURLOPT_HTTPHEADER, array(
- "Content-Type: application/json"
- ));
- $result = curl_exec($ch);
- $HTTP_CODE = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- curl_close($ch);
- return $result;
- }
- /**
- * 钉钉markdown通知管理
- *
- * @param string $notice_type 通知类型: warning/weelfar/notic/operat
- * @param string $msg_template 消息模板: 使用默认要为空
- * @param string $msg_data 通知数据: 默认=标题、报错说明、相关标识
- * @param bool $develop 是否呼叫管理员
- *
- * @return false|void
- */
- function ddMsg($notice_type, $msg_template = 'defaults', $msg_data = [], $develop = false){
- if(!array_key_exists($notice_type, Config::get('dd_notice.notice_type'))){
- return false;
- }
- $url = Config::get('dd_notice.notice_type')[$notice_type];
- if(array_key_exists($msg_template, Config::get('dd_notice.msg_template'))){
- // TODO: 不同模板多个 data 判断,未完善。
- $warning_msg = sprintf(Config::get('dd_notice')['msg_template'][$msg_template],$msg_data[0], $msg_data[1], $msg_data[2], $msg_data[3], date('Y-m-d H:i:s', time()));
- }else{
- $warning_msg = sprintf(Config::get('dd_notice')['msg_template']['defaults'],$msg_data[0], $msg_data[1], $msg_data[2], date('Y-m-d H:i:s', time()));
- }
- if($notice_type == 'warning'){
- $develop = true;
- }
- curlDDMarkdown($warning_msg, $url, $develop);
- }
- /**
- * 发送飞书机器人消息
- *
- * @param string $webhook 飞书机器人的 Webhook URL
- * @param string $content 消息内容
- * @param string $msgType 消息类型:text 或 post(富文本,支持格式化)
- * @param string $title 富文本标题(msgType=post 时有效)
- *
- * @return bool|string
- *
- * 范例:
- * **加粗文本**
- * ~~删除线~~
- * [超链接](https://example.com)
- * <at id=user_id></at> @人
- *
- *
- * // 纯文本
- * sendFeishuMessage('https://open.feishu.cn/open-apis/bot/v2/hook/你的webhook', '这是一条测试消息');
- *
- * // 富文本(支持格式化)
- * $message = "任务执行完成!\n耗时:5.2秒\n状态:成功";
- * sendFeishuMessage('https://open.feishu.cn/...', $message, 'post', '✅❌⚠️ 通知标题');
- *
- */
- function sendFeishuMessage($webhook, $content, $msgType = 'text', $title = '') {
- if (empty($webhook) || empty($content)) {
- return ['success' => false, 'error' => 'webhook or content is empty'];
- }
- $data = [];
- if ($msgType === 'interactive') {
- // 卡片消息 - 支持 Markdown 格式(推荐)
- $data = [
- 'msg_type' => 'interactive',
- 'card' => [
- 'header' => [
- 'title' => [
- 'tag' => 'plain_text',
- 'content' => $title ?: '祈盟-系统通知'
- ],
- 'template' => 'red' // 红色警示
- ],
- 'elements' => [
- [
- 'tag' => 'div',
- 'text' => [
- 'tag' => 'lark_md',
- 'content' => $content
- ]
- ]
- ]
- ]
- ];
- } elseif ($msgType === 'post') {
- // 富文本格式(飞书原生格式,不是 Markdown)
- $data = [
- 'msg_type' => 'post',
- 'content' => [
- 'post' => [
- 'zh_cn' => [
- 'title' => $title ?: '祈盟-系统通知',
- 'content' => [
- [
- [
- 'tag' => 'text',
- 'text' => $content
- ]
- ]
- ]
- ]
- ]
- ]
- ];
- } else {
- // 纯文本
- $data = [
- 'msg_type' => 'text',
- 'content' => [
- 'text' => "祈盟-".$content
- ]
- ];
- }
- $ch = curl_init($webhook);
- curl_setopt($ch, CURLOPT_POST, true);
- curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_UNESCAPED_UNICODE));
- curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_TIMEOUT, 10);
- // SSL 配置(飞书是 HTTPS)
- if (substr($webhook, 0, 5) == 'https') {
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
- }
- $result = curl_exec($ch);
- // 检查 curl 是否执行成功
- if ($result === false) {
- $error = curl_error($ch);
- $errno = curl_errno($ch);
- curl_close($ch);
- return ['success' => false, 'error' => "CURL_ERROR [{$errno}]: {$error}"];
- }
- $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- curl_close($ch);
- if ($httpCode !== 200) {
- return ['success' => false, 'error' => "HTTP_ERROR: status={$httpCode}, body={$result}"];
- }
- $response = json_decode($result, true);
- if ($response === null) {
- return ['success' => false, 'error' => "JSON_DECODE_ERROR: {$result}"];
- }
- if (isset($response['code']) && $response['code'] == 0) {
- return ['success' => true, 'data' => $response];
- }
- return ['success' => false, 'error' => "FEISHU_ERROR: code=" . ($response['code'] ?? 'unknown') . ", msg=" . ($response['msg'] ?? 'unknown')];
- }
- /**
- * 输出xml字符
- * @param $params array 参数
- *
- * @return string 返回组装的xml
- **/
- function dataToXml($params)
- {
- if (!is_array($params) || count($params) <= 0) {
- return false;
- }
- $xml = "<xml>";
- foreach ($params as $key => $val) {
- if (is_numeric($val)) {
- $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
- } else {
- $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
- }
- }
- $xml .= "</xml>";
- return $xml;
- }
- /**
- * 防止重复提交的并发控制
- *
- * @param $cache_key string redis的key
- * @param $time_limit int 限制时间
- *
- * @return boolean true:允许 false:禁止
- */
- function requestDuplicateCheck($cache_key, $time_limit)
- {
- $redis = \think\Cache::store('default')->handler(); //redis的句柄对象
- //redis系统的当前时间
- $redis_time = $redis->time()[0];
- //初步加锁
- $isLock = $redis->setnx($cache_key, $redis_time + $time_limit);
- if ($isLock) {
- $redis->expire($cache_key, $time_limit);
- return true;
- } else {
- //加锁失败的情况下。判断锁是否已经存在,如果锁存在切已经过期,那么删除锁。进行重新加锁
- $val = $redis->get($cache_key);
- if ($val && $val < $redis_time) {
- $redis->del($cache_key);
- }
- $result = $redis->setnx($cache_key, $redis_time + $time_limit);
- if ($result) {
- $redis->expire($cache_key, $time_limit);
- }
- return $result;
- }
- }
- /**
- * 并发解锁
- * @param $cache_key
- */
- function requestDuplicateCheckUnlock($cache_key)
- {
- $redis = \think\Cache::store('default')->handler(); //redis的句柄对象
- $redis->del($cache_key);
- }
- /**
- * 如果是当月7日之前(包括7日),则可以导出上个月和这个月的报表; 如果是7日之后,则只能导出这个月报表
- * @param $start 开始时间戳
- * @param $end 结束时间戳
- * @param int $day 日期分界(例:7号填 7 )
- * @return array
- */
- function judgeReportTime($start, $end, $day = 7)
- {
- $data = ['code' => 0, 'msg' => '违规操作,禁止导出报表'];
- $nowDay = date('d');
- $lastMonth = strtotime(date('Y-m-01 00:00:00', strtotime('-1 month'))); // 上个月1号时间戳
- $beginThismonth = mktime(0, 0, 0, date('m'), 1, date('Y')); // 这个月的开始时间
- $endThismonth = mktime(23, 59, 59, date('m'), date('t'), date('Y')); // 这个月的结束时间
- if ($end < $start || $lastMonth > $start || $end > $endThismonth) {
- return $data;
- }
- if ($nowDay <= $day && $lastMonth >= $start) {
- $data['code'] = 1;
- } elseif ($nowDay <= $day && date("Y-m", $start) == date("Y-m", $end)) {
- $data['code'] = 1;
- } elseif ($nowDay > $day && date("Y-m", $start) == date("Y-m", $end) && $start >= $beginThismonth) {
- $data['code'] = 1;
- }
- return $data;
- }
- //以下微信公众号使用
- function apipost($url, $arr = array(), $parse = true, $authentication = false)
- {
- $param = "";
- if ($parse) {
- foreach ($arr as $key => $value) {
- $param = $param . $key . "=" . $value . "&";
- }
- $len = strlen($param) - 1;
- $param = substr($param, 0, $len);
- } else {
- $param = $arr;
- }
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_URL, $url);
- if ($authentication) {
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
- }
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
- curl_setopt($ch, CURLOPT_POST, 1); //启用POST提交
- curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
- $file_contents = curl_exec($ch);
- curl_close($ch);
- return $file_contents;
- }
- function dd()
- {
- echo Db::getLastSql();
- die;
- }
- function priceFormat($number, $decimals = 2, $dec_point = '.', $thousands_sep = '')
- {
- return number_format($number, $decimals, $dec_point, $thousands_sep);
- }
- function getCurl($url, $data = [], $timeout = 30)
- {
- if ($url == "" || $timeout <= 0) {
- return false;
- }
- if ($data) {
- $url = $url . '?' . http_build_query($data);
- }
- $con = curl_init((string)$url);
- curl_setopt($con, CURLOPT_HEADER, false);
- curl_setopt($con, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($con, CURLOPT_TIMEOUT, (int)$timeout);
- return curl_exec($con);
- }
- function opensslEncrypt($data)
- {
- $key = 'ukN3KYVdI2sPtjpL';
- return base64_encode(openssl_encrypt($data, 'AES-128-ECB', $key, 0));
- }
- function opensslDecrypt($data)
- {
- $key = 'ukN3KYVdI2sPtjpL';
- return openssl_decrypt(base64_decode($data), 'AES-128-ECB', $key, 0);
- }
- function curl($url, $params = [], $returnJson = false)
- {
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, $url);
- curl_setopt($curl, CURLOPT_HEADER, 0); // 过滤HTTP头
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // 获取数据返回
- curl_setopt($curl, CURLOPT_TIMEOUT, 30); //超时时长,单位秒
- if ($params) {
- curl_setopt($curl, CURLOPT_POST, true); // post传输数据
- curl_setopt($curl, CURLOPT_POSTFIELDS, $params); // post传输数据
- }
- // curl_setopt($curl, CURLOPT_HTTPHEADER, $header); // 设置请求头
- $result = curl_exec($curl);
- if ($result === false) {
- $result = '接口超时';
- }
- curl_close($curl);
- if ($returnJson) {
- $result = json_decode($result, true);
- }
- return $result;
- }
- function is_mobile_request()
- {
- $_SERVER['ALL_HTTP'] = isset($_SERVER['ALL_HTTP']) ? $_SERVER['ALL_HTTP'] : '';
- $mobile_browser = '0';
- if (preg_match('/(up.browser|up.link|mmp|symbian|smartphone|midp|wap|phone|iphone|ipad|ipod|android|xoom)/i', strtolower($_SERVER['HTTP_USER_AGENT'])))
- $mobile_browser++;
- if ((isset($_SERVER['HTTP_ACCEPT'])) and (strpos(strtolower($_SERVER['HTTP_ACCEPT']), 'application/vnd.wap.xhtml+xml') !== false))
- $mobile_browser++;
- if (isset($_SERVER['HTTP_X_WAP_PROFILE']))
- $mobile_browser++;
- if (isset($_SERVER['HTTP_PROFILE']))
- $mobile_browser++;
- $mobile_ua = strtolower(substr($_SERVER['HTTP_USER_AGENT'], 0, 4));
- $mobile_agents = array(
- 'w3c ', 'acs-', 'alav', 'alca', 'amoi', 'audi', 'avan', 'benq', 'bird', 'blac',
- 'blaz', 'brew', 'cell', 'cldc', 'cmd-', 'dang', 'doco', 'eric', 'hipt', 'inno',
- 'ipaq', 'java', 'jigs', 'kddi', 'keji', 'leno', 'lg-c', 'lg-d', 'lg-g', 'lge-',
- 'maui', 'maxo', 'midp', 'mits', 'mmef', 'mobi', 'mot-', 'moto', 'mwbp', 'nec-',
- 'newt', 'noki', 'oper', 'palm', 'pana', 'pant', 'phil', 'play', 'port', 'prox',
- 'qwap', 'sage', 'sams', 'sany', 'sch-', 'sec-', 'send', 'seri', 'sgh-', 'shar',
- 'sie-', 'siem', 'smal', 'smar', 'sony', 'sph-', 'symb', 't-mo', 'teli', 'tim-',
- 'tosh', 'tsm-', 'upg1', 'upsi', 'vk-v', 'voda', 'wap-', 'wapa', 'wapi', 'wapp',
- 'wapr', 'webc', 'winw', 'winw', 'xda', 'xda-'
- );
- if (in_array($mobile_ua, $mobile_agents))
- $mobile_browser++;
- if (strpos(strtolower($_SERVER['ALL_HTTP']), 'operamini') !== false)
- $mobile_browser++;
- // Pre-final check to reset everything if the user is on Windows
- if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows') !== false)
- $mobile_browser = 0;
- // But WP7 is also Windows, with a slightly different characteristic
- if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows phone') !== false)
- $mobile_browser++;
- if ($mobile_browser > 0)
- return true;
- else
- return false;
- }
- function timingCurl($url)
- {
- $ch = curl_init();
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_HEADER, 0);
- curl_exec($ch);
- curl_close($ch);
- }
- function getMonthRange($date, $returnFirstDay = true)
- {
- $timestamp = strtotime($date);
- if ($returnFirstDay) {
- $monthFirstDay = date('Y-m-1 00:00:00', $timestamp);
- return $monthFirstDay;
- } else {
- $mdays = date('t', $timestamp);
- $monthLastDay = date('Y-m-' . $mdays . ' 23:59:59', $timestamp);
- return $monthLastDay;
- }
- }
- /**
- * @return float 获取当前毫秒时间戳
- */
- function getMillisecond()
- {
- list($microsecond, $time) = explode(' ', microtime()); //' '中间是一个空格
- return (float)sprintf('%.0f', (floatval($microsecond) + floatval($time)) * 1000);
- }
- /**
- * 是否json字符串
- *
- * @param $string
- *
- * @return bool
- */
- function is_json($string)
- {
- if (!is_string($string) || $string === '') {
- return false;
- }
-
- $decoded = json_decode($string);
- // 检查是否为对象或数组,且没有错误
- return (json_last_error() === JSON_ERROR_NONE) &&
- ($decoded !== null) &&
- (is_object($decoded) || is_array($decoded));
- }
- function curlHeader($url, $params = '', $header = [], $returnJson = false)
- {
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, $url);
- curl_setopt($curl, CURLOPT_HEADER, 0); // 过滤HTTP头
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // 获取数据返回
- curl_setopt($curl, CURLOPT_TIMEOUT, 30); //超时时长,单位秒
- curl_setopt($curl, CURLOPT_POST, true); // post传输数据
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
- curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
- if ($params) {
- curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
- }
- if (!empty($header)) {
- curl_setopt($curl, CURLOPT_HTTPHEADER, $header); // 设置请求头
- }
- $result = curl_exec($curl);
- if ($result === false) {
- $result = '接口超时';
- }
- $code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- curl_close($curl);
- if ($returnJson) {
- $result = json_decode($result, true);
- $result['code_qf'] = $code;
- }
- return $result;
- }
- function temporary()
- {
- $time = time();
- $start_time = '1670256000';
- $end_time = '1670342400';
- if($start_time<$time && $end_time>time()){
- return true;
- }else{
- return false;
- }
- }
- /**
- * 生成唯一值
- * @return string
- */
- function makeUniqueid($head = 'FL')
- {
- $redis = think\Cache::store('default');
- $uniqueid = (date('YmdHis').getRandomStr(8, false) . substr(str_pad(mt_rand(), 6, "0", STR_PAD_BOTH), 0, 6));
- // 判断是否有同个唯一值的请求还没处理完,防止因为网络问题,导致同个订单并发
- $cache_key = 'orderid:' . $uniqueid;
- if ($redis->has($cache_key)) {
- return makeUniqueid();
- } else {
- $redis->set($cache_key, $uniqueid, 86400);
- return $head . $uniqueid;
- }
- }
- /**
- * 获得随机字符串
- * @param $len 需要的长度
- * @param $special 是否需要特殊符号
- * @return string 返回随机字符串
- */
- function getRandomStr($len, $special=true){
- $chars = array(
- "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
- "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
- "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G",
- "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
- "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2",
- "3", "4", "5", "6", "7", "8", "9"
- );
- if($special){
- $chars = array_merge($chars, array(
- "!", "@", "#", "$", "?", "|", "{", "/", ":", ";",
- "%", "^", "&", "*", "(", ")", "-", "_", "[", "]",
- "}", "<", ">", "~", "+", "=", ",", "."
- ));
- }
- $charsLen = count($chars) - 1;
- shuffle($chars); //打乱数组顺序
- $str = '';
- for($i=0; $i<$len; $i++){
- $str .= $chars[mt_rand(0, $charsLen)]; //随机取出一位
- }
- return $str;
- }
- function setChannelId($userid,$channelId){
- // \think\Cache::store('default')->set('token:channelId:'.$userid, $channelId, 60 * 60 * 24 * 30);
- }
- function getChannelId($userid, $gameid = ''){
- // $channelId = \think\Cache::store('default')->get('token:channelId:'.$userid);
- // ->cache(true, 86400)
- $channelId = Db::table('nw_subaccount')->where(['member_id' => $userid, 'game_id' => $gameid])->value('channel_id');
- if($channelId > 0){
- return $channelId;
- }else{
- return 0;
- }
- }
- // 获取折扣比例
- function getDiscount($userid = 0,$gameId = 0,$channelId = 0){
- $proportion = \Think\Db::name('cy_game_pay_discount')->where(['game_id' => $gameId])->value('proportion');
- return $proportion??"1";
- }
- /**
- * 金额分转元
- * @param int $fen
- * @return string
- */
- function formatFenToYuan($fen, $long = 2)
- {
- return number_format($fen / 100, $long, '.', '');
- }
- /**
- * 金额元转分
- *
- * @param int $fen
- *
- * @return string
- */
- function formatYuanToFen($yuan, $long = 0)
- {
- return number_format($yuan * 100, $long, '.', '');
- }
- /**
- * 获取渠道游戏支付金额的产品标识
- *
- * @param string $money 获取金额(元)(支付金额最大为9w元)
- * @param string $mark 前缀标识
- *
- * @return string
- */
- function formatComplexProductMark($mark, $game_id, $money)
- {
- $money = formatYuanToFen($money);
- $cpMark = $mark . '_' . str_pad($money, 7, "0", STR_PAD_LEFT); // 拼接标识(0.06 = kdzz_0000006)
- if (!isset(config('config_complex_goods')[$mark][$cpMark])) {
- return false;
- }
- return $cpMark;
- }
- /**
- * 获取游戏所属渠道的标识
- *
- * @param $jh_mark 渠道标识
- * @param $game_id 游戏ID
- *
- * @return array
- */
- function isComplexProduct($jh_mark, $game_id)
- {
- $ids = Env::get('complex.sj_game_ids', '');
- $ids = explode(',', $ids);
- $channelList = [];
- foreach ($ids as $v) {
- $channel = explode('_', $v);
- $channelList[$channel[0]] = $channel[1];
- }
- if(!in_array($game_id, array_keys($channelList))){
- return "";
- }
- return $jh_mark.'_'.$channelList[$game_id];
- }
- function dump($data, $type = true)
- {
- $params = func_get_args();
- if ($type == true) {
- echo "<pre>";
- }
- var_dump(...$params);
- die;
- }
- /**
- * 发起请求提交
- *
- * @param string $type 请求类型 GET/POST
- * @param string $url 请求URL
- * @param array $data POST请求数据
- * @param array $header Header头数据
- *
- * @return
- */
- function getHttp($type = 'GET', $url, $data = [], $header = ['Content-Type: application/json'], $headerStatus = false)
- {
- $curl = curl_init($url);
- // curl_setopt($curl, CURLOPT_URL, $url); // 设置抓取的url
- // curl_setopt($curl, CURLOPT_HEADER, 0); // 设置头文件的信息作为数据流输出
- curl_setopt($curl, CURLOPT_TIMEOUT, 30); // 超时设置,以秒为单位
- // 超时设置,以毫秒为单位
- // curl_setopt($curl, CURLOPT_TIMEOUT_MS, 300);
- if ($header) {
- curl_setopt($curl, CURLOPT_HTTPHEADER, $header); // 设置请求头
- }
- //设置获取的信息以文件流的形式返回,而不是直接输出。
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // 执行后不直接打印出来
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); // 跳过证书检查
- curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); // 不从证书中检查SSL加密算法是否存在
- if ($type == 'POST') {
- // curl_setopt($curl, CURLOPT_POST, 1); // 设置请求方式为post
- curl_setopt($curl, CURLOPT_POSTFIELDS, $data); // post的变量
- // curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data)); // post的变量
- }
- if ($headerStatus == true) {
- $header = curl_getinfo($curl);
- }
- $result = curl_exec($curl); // 执行并获取內容
- curl_close($curl); // 释放curl句柄
- if (!is_json($result)) {
- $result = json_encode($result, true);
- }
- // trace("## common@getHttp: ".$type . " - URL: " . $url . " - Data: " . json_encode($data) . ' - Result: ' . $result, 'common@getHttp.$result');
- log_message("## common@getHttp - ".date("Y-m-d H:i:s", time()).": ".$type . " - URL: " . $url . " - Data: " . json_encode($data) . ' - Result: ' . $result, 'log', LOG_PATH . 'httpLog/'); // ## aplog log
- return $result;
- }
- /**
- * 模拟 GET 请求
- *
- * @param string $url 请求地址
- * @param array $header Header头数据
- *
- * @return
- */
- function getHttpGet(string $url, $query = [], $header = [], $json = true)
- {
- // $res = getHttp('GET', $url . '?' . assemblyByQuery($query), [], $header);
- $query = http_build_query($query);
- if (!empty($query)) {
- $query = "?" . $query;
- }
- return getHttp('GET', $url . $query, [], $header);
- }
- /**
- * 模拟 POST 请求
- *
- * @param string $url 请求地址
- * @param array $data 请求数据
- * @param array $header Header头数据
- * @param array $headerStatus 是否返回请求返回的Header数据
- *
- * @return array
- */
- function getHttpPost(string $url, $data = [], $header = [], $json = true, $headerStatus = false)
- {
- return getHttp('POST', $url, $data, $header, $headerStatus);
- }
- /**
- * GET 数组数据处理为拼接字符串
- *
- * @param array $query 处理的数据数组
- * @param boolean $isUrl 是否是URL参数标识(是的话,返回的字符串前缀加?标识)
- *
- * @return string
- */
- function assemblyByQuery($query, $isUrl = false)
- {
- ksort($query);
- $queryData = '';
- foreach ($query as $key => $value) {
- if (!$queryData) {
- if ($isUrl) {
- $queryData = '?';
- }
- $queryData .= $key . '=' . $value;
- } else {
- $queryData .= '&' . $key . '=' . $value;
- }
- }
- return $queryData;
- }
- //获取真实IP,可以根据实际情况自行获取
- function getUserIp()
- {
- try {
- $info = getCurl('https://myip.ipip.net');
- if (!$info) {
- $info = file_get_contents('http://myip.ipip.net');
- }
- $ipstr = explode(':', $info);
- $ip = explode(' ', $ipstr[1]);
- return $ip[0];
- } catch (Exception $e) {
- return request()->ip();
- }
- }
- /**
- * 为支付方式增加其他附加支付类型
- * conin- = 代金卷
- * mix- = 专属平台币(目前好像没用)
- *
- * @param $list
- *
- * @return array
- */
- function handlePayType($list)
- {
- $newList = [];
- $markList = ['coin-', 'mix-'];
- foreach ($list as $key => $value) {
- $newPayType = [];
- foreach ($value as $value1) {
- $newPayType[] = $value1;
- foreach ($markList as $value2) {
- $newPayType[] = $value2 . $value1;
- }
- }
- $newList[$key] = $newPayType;
- }
- return $newList;
- }
- /**
- * 判断二维数组中是否包含某个字符串
- *
- * @param $arr array 二维数组
- * @param $searchValue string 包含值
- *
- * @return bool
- */
- function isInArrType($arr, $searchValue)
- {
- foreach ($arr as $key => $values) {
- if (in_array($searchValue, $values)) {
- return true;
- }
- }
- return false;
- }
- /**
- * 获取游戏的分包域名地址
- * 配置了则走CDN域名,没有则走默认分包服务器
- *
- * @param $game_id
- *
- * @return string
- */
- function getHandleCdnGamesByIds($game_id)
- {
- $result = (new Setting())->getSetting('handle_cdn_games');
- if ($result) {
- if ($result == 'all') {
- return APK_DOWN_DOMAIN_NEW;
- }
- $res = json_decode($result, true);
- if (in_array($game_id, $res)) {
- return APK_DOWN_DOMAIN_NEW;
- }
- }
- return APK_DOWN_DOMAIN;
- }
- function getChannelH5Url($code, $gameId){
- $h5_url = (new Setting)::getSetting('HTTP_HOST_URL_H5');
- return $h5_url.'/?c='. $code.'&g='.$gameId;
- }
- if (!function_exists('definition_log')) {
- function definition_log($pathType, $message, $level = 'info') {
- $customLogPath = LOG_PATH . "definition/{$pathType}/";
- // dump($customLogPath);
- static $logInited = false;
- if (!$logInited) {
- \think\Log::init([
- // 日志记录方式,内置 file socket 支持扩展
- 'type' => 'File',
- // 日志保存目录
- 'path' => $customLogPath,
- // 日志记录级别
- 'level' => ['log', 'error', 'notice', 'info', 'debug', 'sql'],
- 'max_files' => 30,
- 'realtime_write' => true,
- ]);
- $logInited = true;
- }
- \think\Log::write($message, $level);
- }
- }
- /**
- * 获取游戏绑定的其他端游戏ID
- *
- * @param $gameId
- *
- * @return array|mixed
- * @throws DataNotFoundException
- * @throws DbException
- * @throws ModelNotFoundException
- */
- function getBandGameId($gameId){
- $ameBandList = Db::table('nw_game_band')->select();
- $newBandGameList = [];
- foreach($ameBandList as $v){
- if(!empty($v['android_game_id'])){
- $newBandGameList[$v['android_game_id']] = [$v['ios_game_id'], $v['h5_game_id']];
- }
- if(!empty($v['ios_game_id'])){
- $newBandGameList[$v['ios_game_id']] = [$v['android_game_id'], $v['h5_game_id']];
- }
- if(!empty($v['h5_game_id'])){
- $newBandGameList[$v['h5_game_id']] = [$v['android_game_id'], $v['ios_game_id']];
- }
- }
- if(empty($newBandGameList[$gameId])){
- return [];
- }
- return $newBandGameList[$gameId]; // [20, 30]
- }
- /**
- * 清理金额输入,保留两位小数
- *
- * @param $value
- *
- * @return float
- */
- function cleanAmount($value) {
- /**
- * ¥1,000.00 => 1000.00
- * $2,345.67 => 2345.67
- * 1 234.50元 => 1234.50
- * 3.000,50 => 3000.50
- * abc1234.56def => 1234.56
- */
- // 1. 转为字符串,防止传入非字符串类型
- $value = (string)$value;
- // 2. 去掉所有非数字、小数点、负号
- $value = preg_replace('/[^\d.\-]/', '', $value);
- // 3. 防止多个小数点导致格式错误,只保留第一个
- if (substr_count($value, '.') > 1) {
- $parts = explode('.', $value, 2);
- $value = $parts[0] . '.' . preg_replace('/\./', '', $parts[1]);
- }
- return (float)$value;
- // 4. 转为浮点数并保留两位小数(或根据需求改为字符串)
- // return number_format((float)$value, 2, '.', '');
- }
- // 根据渠道ID获取渠道所属的上级渠道ID
- function getChannel($channel_id)
- {
- // 进程级静态缓存,一次请求只读一次文件
- static $channelList = null;
- if ($channelList === null) {
- $channelList = Cache::store('File')->get('cache_retaine_channel_list');
- if (!$channelList) {
- $channelList = model('common/Channel')->column('id,name,id_path,level', 'id');
- Cache::store('File')->set('cache_retaine_channel_list', $channelList, 3600);
- }
- }
- if (!isset($channelList[$channel_id])) {
- return [];
- }
- $data = [];
- $id_path = explode(',', $channelList[$channel_id]['id_path']);
- foreach ($id_path as $vv) {
- if ($vv == $channel_id || !isset($channelList[$vv])) {
- continue;
- }
- $ch = $channelList[$vv];
- if ($ch['level'] == 0) {
- $data['s_channel_id'] = $ch['id'];
- } elseif ($ch['level'] == 1) {
- $data['b_channel_id'] = $ch['id'];
- } elseif ($ch['level'] == 2) {
- $data['bz_channel_id'] = $ch['id'];
- }
- }
- return $data;
- }
- /**
- * 解析 URL 参数,保留 + 号不转换成空格
- * PHP 的 parse_str() 和 $_POST 会自动把 + 转成空格,导致签名验证失败
- *
- * @param string $queryString 原始查询字符串
- * @return array 解析后的参数数组
- */
- function parseParamsKeepPlus($queryString)
- {
- $params = [];
- if (empty($queryString)) {
- return $params;
- }
- $pairs = explode('&', $queryString);
- foreach ($pairs as $pair) {
- $parts = explode('=', $pair, 2);
- if (count($parts) === 2) {
- // 只解码 %XX 格式的编码,保留 + 号
- $key = rawurldecode($parts[0]);
- $value = rawurldecode($parts[1]);
- $params[$key] = $value;
- }
- }
- return $params;
- }
|