Helpers.php 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852
  1. <?php
  2. use GuzzleHttp\Client;
  3. use Illuminate\Support\Facades\DB;
  4. use Illuminate\Support\Facades\Log;
  5. use Monolog\Handler\StreamHandler;
  6. use Monolog\Logger;
  7. use OSS\OssClient;
  8. use OSS\Core\OssException;
  9. use Tos\TosClient;
  10. use Tos\Exception\TosClientException;
  11. use Tos\Exception\TosServerException;
  12. use Tos\Model\PutObjectInput;
  13. /**
  14. * 截取字符串
  15. *
  16. * @param $string
  17. * @param $length
  18. * @param bool $append
  19. * @return array|string
  20. */
  21. function sysSubStr($string, $length, $append = false)
  22. {
  23. if (strlen($string) <= $length) {
  24. return $string;
  25. }
  26. $i = 0;
  27. $stringLast = [];
  28. while ($i < $length) {
  29. $stringTMP = mb_substr($string, $i, 1);
  30. if (ord($stringTMP) >= 224) {
  31. $stringTMP = mb_substr($string, $i, 3);
  32. $i += 3;
  33. } elseif (ord($stringTMP) >= 192) {
  34. $stringTMP = mb_substr($string, $i, 2);
  35. $i += 2;
  36. } else {
  37. ++$i;
  38. }
  39. $stringLast[] = $stringTMP;
  40. }
  41. $stringLast = implode('', $stringLast);
  42. if ($append) {
  43. $stringLast .= '...';
  44. }
  45. return $stringLast;
  46. }
  47. /**
  48. * 自定义日志
  49. *
  50. * @param $name
  51. * @param string $filename
  52. * @return \Illuminate\Log\Writer
  53. */
  54. function myLog($name, $filename = '')
  55. {
  56. if (!$filename) {
  57. $filename = $name;
  58. }
  59. $filename = $filename . '.log';
  60. $logger = new Logger($name);
  61. $writer = new \Illuminate\Log\Writer($logger);
  62. $writer->useDailyFiles(storage_path('logs/' . $filename));
  63. return $writer;
  64. }
  65. /**
  66. * 业务调试日志
  67. *
  68. * @return Logger
  69. */
  70. function commonLog()
  71. {
  72. $name = 'common';
  73. $filename = $name . '.log';
  74. $logger = new Logger($name);
  75. $logger->pushHandler(new StreamHandler(storage_path('logs/' . $filename), 'debug'));
  76. return $logger;
  77. }
  78. /**
  79. * @param $name
  80. * @return Logger
  81. */
  82. function dLog($name)
  83. {
  84. $filename = $name . '-' . date('Y-m-d') . '.log';
  85. $logger = new Logger($name);
  86. $logger->pushHandler(new StreamHandler(storage_path('logs/' . $filename), 'debug'));
  87. return $logger;
  88. }
  89. /**
  90. * 获取对象或数组的属性值
  91. *
  92. * @param $param
  93. * @param $key
  94. * @param string $default
  95. * @return mixed|string
  96. */
  97. function getProp($param, $key, $default = '')
  98. {
  99. $result = $default;
  100. if (is_object($param) && isset($param->$key)) {
  101. $result = $param->$key;
  102. }
  103. if (is_array($param) && isset($param[$key])) {
  104. $result = $param[$key];
  105. }
  106. return $result;
  107. }
  108. /**
  109. * @param $data
  110. * @param array $trans
  111. * @return array
  112. */
  113. function assetData($data, $trans = []): array
  114. {
  115. $result = [];
  116. if (empty($data)) {
  117. return $result;
  118. }
  119. if (empty($trans)) {
  120. return $data;
  121. }
  122. foreach ($trans as $tran) {
  123. [$originKey, $newKey, $conv, $default] = [$tran['o'], $tran['n'], $tran['conv'], $tran['default']];
  124. if (isset($data[$originKey])) {
  125. $result[$newKey] = $conv(getProp($data, $originKey, $default));
  126. }
  127. }
  128. return $result;
  129. }
  130. /**
  131. * 随机数
  132. *
  133. * @param int $num
  134. * @return string
  135. * @throws Exception
  136. */
  137. function random($num = 16)
  138. {
  139. $bytes = random_bytes($num);
  140. return bin2hex($bytes);
  141. }
  142. /**
  143. * 生成订单
  144. *
  145. * @param string $prefix 订单前缀
  146. * @return string
  147. */
  148. function generateOrderSn($prefix = '')
  149. {
  150. return $prefix . date('YmdHis') . getMillisecond() . rand(1000, 9999);
  151. }
  152. /**
  153. * 分页数据
  154. *
  155. * @param $data
  156. * @return array
  157. */
  158. function getMeta($data)
  159. {
  160. $currentPage = (int)$data->currentPage();
  161. $lastPage = (int)$data->lastPage();
  162. return [
  163. 'current_page' => $currentPage,
  164. 'next_page' => $currentPage >= $lastPage ? $lastPage : ++$currentPage,
  165. 'last_page' => $lastPage,
  166. 'per_page' => (int)$data->perPage(),
  167. 'total' => (int)$data->total(),
  168. 'is_end' => !$data->hasMorePages(),
  169. 'next_page_url' => (string)$data->nextPageUrl(),
  170. 'prev_page_url' => (string)$data->previousPageUrl()
  171. ];
  172. }
  173. /**
  174. * 钉钉通知异常
  175. *
  176. * @param $message
  177. */
  178. function sendNotice($message)
  179. {
  180. $webHook = env('DD_WEB_HOOK');
  181. $data = [
  182. 'msgtype' => 'text',
  183. 'text' => [
  184. 'content' => $message
  185. ],
  186. 'at' => [
  187. 'isAll' => true
  188. ]
  189. ];
  190. // 异步发送
  191. $client = new GuzzleHttp\Client();
  192. $client->post($webHook, ['json' => $data]);
  193. }
  194. /**
  195. * 判断数据是合法的json数据
  196. *
  197. * @param $string
  198. * @return bool
  199. */
  200. function is_json($string)
  201. {
  202. json_decode($string);
  203. return (json_last_error() == JSON_ERROR_NONE);
  204. }
  205. /**
  206. * 获取ip地址
  207. *
  208. * @return mixed|string
  209. */
  210. function getIpAddr()
  211. {
  212. $ipaddress = '';
  213. if (isset($_SERVER['HTTP_CLIENT_IP']))
  214. $ipaddress = $_SERVER['HTTP_CLIENT_IP'];
  215. else if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
  216. $ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
  217. else if (isset($_SERVER['HTTP_X_FORWARDED']))
  218. $ipaddress = $_SERVER['HTTP_X_FORWARDED'];
  219. else if (isset($_SERVER['HTTP_FORWARDED_FOR']))
  220. $ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
  221. else if (isset($_SERVER['HTTP_FORWARDED']))
  222. $ipaddress = $_SERVER['HTTP_FORWARDED'];
  223. else if (isset($_SERVER['REMOTE_ADDR']))
  224. $ipaddress = $_SERVER['REMOTE_ADDR'];
  225. else
  226. $ipaddress = 'UNKNOWN';
  227. return $ipaddress;
  228. }
  229. /**
  230. * @param int $num
  231. * @return bool|string
  232. * @throws Exception
  233. */
  234. function randStr($num = 5)
  235. {
  236. $str = 'QWERTYUIOPASDFGHJKLZXCVBNM1234567890qwertyuiopasdfghjklzxcvbnm';
  237. return substr(str_shuffle($str), random_int(0, strlen($str) - 11), $num);
  238. }
  239. /**
  240. * 上传文件(火山云tos)
  241. *
  242. * @param $prefix 文件夹前缀
  243. * @param $file 文件二进制
  244. * @param $filename 文件名(不带后缀)
  245. * @return mixed
  246. */
  247. function uploadFileByTos($prefix, $file, $filename='')
  248. {
  249. if (!$filename) $filename = randStr(10) . '.' . $file->getClientOriginalExtension();
  250. else $filename = $filename . '.' . $file->getClientOriginalExtension();
  251. try {
  252. $client = new TosClient([
  253. 'region' => env('VOLC_REGION'),
  254. 'endpoint' => env('VOLC_END_POINT'),
  255. 'ak' => env('VOLC_AK'),
  256. 'sk' => env('VOLC_SK'),
  257. ]);
  258. $stream = fopen($file->getRealPath(), 'r');
  259. $input = new PutObjectInput(env('VOLC_BUCKET'), "$prefix/$filename", $stream);
  260. $output = $client->putObject($input);
  261. if ($output->getRequestId()) {
  262. return "https://".env('VOLC_BUCKET').'.'.env('VOLC_END_POINT')."/$prefix/$filename";
  263. }
  264. } catch (TosClientException $ex) {
  265. dLog('exception')->info('saveToTos', [$ex->getMessage()]);
  266. } catch (TosServerException $ex) {
  267. dLog('exception')->info('saveToTos', [$ex->getRequestId(), $ex->getStatusCode(), $ex->getErrorCode()]);
  268. }
  269. return '';
  270. }
  271. /**
  272. * 上传文件流(火山云tos)
  273. *
  274. * @param $prefix 文件夹前缀
  275. * @param $file 文件二进制
  276. * @param $filename 文件名(带后缀)
  277. * @return mixed
  278. */
  279. function uploadStreamByTos($prefix, $stream, $filename)
  280. {
  281. try {
  282. $client = new TosClient([
  283. 'region' => env('VOLC_REGION'),
  284. 'endpoint' => env('VOLC_END_POINT'),
  285. 'ak' => env('VOLC_AK'),
  286. 'sk' => env('VOLC_SK'),
  287. ]);
  288. $input = new PutObjectInput(env('VOLC_BUCKET'), "$prefix/$filename", $stream);
  289. $output = $client->putObject($input);
  290. if ($output->getRequestId()) {
  291. return "https://".env('VOLC_BUCKET').'.'.env('VOLC_END_POINT')."/$prefix/$filename";
  292. }
  293. } catch (TosClientException $ex) {
  294. dLog('exception')->info('saveToTos', [$ex->getMessage()]);
  295. } catch (TosServerException $ex) {
  296. dLog('exception')->info('saveToTos', [$ex->getRequestId(), $ex->getStatusCode(), $ex->getErrorCode()]);
  297. }
  298. return '';
  299. }
  300. /**
  301. * 上传文件(阿里云oss)
  302. *
  303. * @param $prefix 文件夹前缀
  304. * @param $file 文件二进制
  305. * @param $filename 文件名(不带后缀)
  306. * @return mixed
  307. */
  308. function uploadFile($prefix, $file, $filename='')
  309. {
  310. // 阿里云主账号
  311. $accessKeyId = env('OSS_ACCESS_ID');
  312. $accessKeySecret = env('OSS_ACCESS_KEY');
  313. $endpoint = env('OSS_END_POINT');
  314. $bucket = env('OSS_BUCKET');
  315. if (!$filename) $filename = randStr(10) . '.' . $file->getClientOriginalExtension();
  316. else $filename = $filename . '.' . $file->getClientOriginalExtension();
  317. // 设置文件名称。
  318. $object = env('OSS_DIRECTORY') . '/' . $prefix . '/' . $filename;
  319. $provider = new \OSS\Credentials\StaticCredentialsProvider($accessKeyId, $accessKeySecret);
  320. try {
  321. $configs = [
  322. "provider" => $provider,
  323. "endpoint" => $endpoint,
  324. "signatureVersion" => 'v4',
  325. "region" => "cn-hangzhou"
  326. ];
  327. $ossClient = new OssClient($configs);
  328. $uploadRes = $ossClient->uploadFile($bucket, $object, $file->path());
  329. } catch (OssException $e) {
  330. dLog('exception')->info('saveImageToOss', [$e->getMessage()]);
  331. return '';
  332. }
  333. // 替换域名
  334. if (!isset($uploadRes['oss-request-url'])) return '';
  335. $url = str_ireplace('zw-ai.oss-cn-hangzhou.aliyuncs.com', 'cdn-zwai.ycsd.cn', $uploadRes['oss-request-url']);
  336. $url = urldecode($url);
  337. return str_ireplace('http://', 'https://', $url);
  338. }
  339. /**
  340. * 上传文件流(阿里云oss)
  341. *
  342. * @param $prefix 文件夹前缀
  343. * @param $file 文件二进制
  344. * @param $filename 文件名(带后缀)
  345. * @return mixed
  346. */
  347. function uploadStreamToOss($prefix, $stream, $filename) {
  348. $accessKeyId = env('OSS_ACCESS_ID');
  349. $accessKeySecret = env('OSS_ACCESS_KEY');
  350. $endpoint = env('OSS_END_POINT');
  351. $bucket = env('OSS_BUCKET');
  352. // if (!$filename) $filename = randStr(10) . '.' . $file->getClientOriginalExtension();
  353. // 设置文件名称。
  354. $object = env('OSS_DIRECTORY') . '/' . $prefix . '/' . $filename;
  355. $provider = new \OSS\Credentials\StaticCredentialsProvider($accessKeyId, $accessKeySecret);
  356. try{
  357. $configs = [
  358. "provider" => $provider,
  359. "endpoint" => $endpoint,
  360. "signatureVersion" => 'v4',
  361. "region"=> "cn-hangzhou"
  362. ];
  363. $ossClient = new OssClient($configs);
  364. $uploadRes = $ossClient->putObject($bucket, $object, $stream);
  365. } catch (OssException $e) {
  366. dLog('exception')->info('saveImageToOss', [$e->getMessage()]);
  367. return '';
  368. }
  369. // 替换域名
  370. if (!isset($uploadRes['oss-request-url'])) return '';
  371. $url = str_ireplace('zw-ai.oss-cn-hangzhou.aliyuncs.com', 'cdn-zwai.ycsd.cn', $uploadRes['oss-request-url']);
  372. $url = urldecode($url);
  373. return str_ireplace('http://', 'https://', $url);
  374. }
  375. /**
  376. * 下载文件到本地
  377. *
  378. * @param $object
  379. * @param $localFile
  380. * @return string
  381. */
  382. function downloadFile($object, $localFile)
  383. {
  384. // 阿里云主账号
  385. $accessKeyId = env('OSS_ACCESS_ID');
  386. $accessKeySecret = env('OSS_ACCESS_KEY');
  387. $endpoint = env('OSS_END_POINT');
  388. $bucket = env('OSS_BUCKET');
  389. try {
  390. $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
  391. $ossClient->getObject($bucket, $object, [
  392. OssClient::OSS_FILE_DOWNLOAD => $localFile
  393. ]);
  394. } catch (OssException $e) {
  395. printf($e->getMessage());
  396. return '';
  397. }
  398. }
  399. /**
  400. * 上传封面
  401. *
  402. * @param $file
  403. * @return string
  404. * @throws Exception
  405. */
  406. function uploadCoverFile($file)
  407. {
  408. // 阿里云主账号
  409. $accessKeyId = env('OSS_ACCESS_ID');
  410. $accessKeySecret = env('OSS_ACCESS_KEY');
  411. $endpoint = env('OSS_END_POINT');
  412. $bucket = env('OSS_BUCKET');
  413. // 设置文件名称。
  414. $object = 'books/cover/' . randStr(10) . '.' . $file->getClientOriginalExtension();
  415. try {
  416. $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
  417. $ossImgBackData = $ossClient->uploadFile($bucket, $object, $file->path());
  418. } catch (OssException $e) {
  419. printf($e->getMessage());
  420. return '';
  421. }
  422. $urlArr = parse_url($ossImgBackData['oss-request-url']);
  423. return getProp($urlArr, 'path') ? 'http://' . $bucket . '.' . $endpoint . getProp($urlArr, 'path') : '';
  424. }
  425. function uploadAgreementFile($file)
  426. {
  427. // 阿里云主账号
  428. $accessKeyId = env('OSS_ACCESS_ID');
  429. $accessKeySecret = env('OSS_ACCESS_KEY');
  430. $endpoint = env('OSS_END_POINT');
  431. $bucket = env('OSS_BUCKET');
  432. $file_name = $file->getClientOriginalName();
  433. $file_name = str_replace('.' . $file->getClientOriginalExtension(), '', $file_name);
  434. // 设置文件名称。
  435. $object = 'books/contract/' . $file_name . '-' . date('YmdHi') . '.' . $file->getClientOriginalExtension();
  436. try {
  437. $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
  438. $ossImgBackData = $ossClient->uploadFile($bucket, $object, $file);
  439. } catch (OssException $e) {
  440. printf($e->getMessage());
  441. return '';
  442. }
  443. $urlArr = parse_url($ossImgBackData['oss-request-url']);
  444. return getProp($urlArr, 'path') ? 'http://' . $bucket . '.' . $endpoint . urldecode(getProp($urlArr, 'path')) : '';
  445. }
  446. /**
  447. * 上传wap推荐图片
  448. *
  449. * @param $file
  450. * @param $filename
  451. * @return string
  452. * @throws Exception
  453. */
  454. function uploadWapRecommendPic($file, $filename)
  455. {
  456. // 阿里云主账号
  457. $accessKeyId = env('OSS_ACCESS_ID');
  458. $accessKeySecret = env('OSS_ACCESS_KEY');
  459. $endpoint = env('OSS_END_POINT');
  460. $bucket = env('OSS_BUCKET_YCSD');
  461. // 设置文件名称。
  462. $object = 'ycsd_web_3nd/images/homebanners/' . $filename . '.' . $file->getClientOriginalExtension();
  463. try {
  464. $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
  465. $options = array(
  466. OssClient::OSS_HEADERS => array(
  467. 'Content-Type' => 'image/jpeg',
  468. 'Content-Disposition' => 'inline'
  469. ),
  470. );
  471. $ossImgBackData = $ossClient->uploadFile($bucket, $object, $file->path(), $options);
  472. } catch (OssException $e) {
  473. printf($e->getMessage());
  474. return '';
  475. }
  476. $urlArr = parse_url($ossImgBackData['oss-request-url']);
  477. return getProp($urlArr, 'path') ? 'http://' . $bucket . '.' . $endpoint . getProp($urlArr, 'path') : '';
  478. }
  479. /**
  480. * @param $path
  481. * @return Generator
  482. */
  483. function readFileContent($path)
  484. {
  485. if ($handle = fopen($path, 'r')) {
  486. while (!feof($handle)) {
  487. yield trim(fgets($handle));
  488. }
  489. fclose($handle);
  490. }
  491. }
  492. function exportFileCsv(array $headers, array $data, string $filename)
  493. {
  494. header("Content-type:application/vnd.ms-excel");
  495. header("Content-Disposition:attachment;filename=" . $filename . ".csv");
  496. $headers = collect($headers)->map(function ($item) {
  497. return "\"" . mb_convert_encoding($item, "GBK", "UTF-8") . "\"";
  498. })->all();
  499. echo implode(",", $headers);
  500. echo "\r\n";
  501. foreach ($data as $item) {
  502. $rows = collect($item)->map(function ($row) {
  503. return "\"" . mb_convert_encoding(is_numeric($row) && strlen($row) > 12 ? "'" . $row : $row, "GBK", "UTF-8") . "\"";
  504. })->all();
  505. echo implode(",", $rows);
  506. echo "\r\n";
  507. }
  508. exit();
  509. }
  510. //function exportFileCsv(array $header, array $data, string $filename) {
  511. // header('Content-Encoding: UTF-8');
  512. // header("Content-type:application/vnd.ms-excel;charset=UTF-8");
  513. // header('Content-Disposition: attachment;filename="' . $filename . '.csv"');
  514. //
  515. // //打开php标准输出流
  516. // $fp = fopen('php://output', 'a');
  517. //
  518. // //添加BOM头,以UTF8编码导出CSV文件,如果文件头未添加BOM头,打开会出现乱码。
  519. // fwrite($fp, chr(0xEF).chr(0xBB).chr(0xBF));
  520. // //添加导出标题
  521. // fputcsv($fp, $header);
  522. //
  523. // foreach ($data as $k => $item) {
  524. // fputcsv($fp, $item);
  525. // if ($k % 5000 == 0) {
  526. // //每1万条数据就刷新缓冲区
  527. // ob_flush();
  528. // flush();
  529. // }
  530. // }
  531. // exit();
  532. //}
  533. //function exportFileCsv(array $header, array $data, string $filename) {
  534. //
  535. // header('Content-Type: application/vnd.ms-excel');
  536. // header('Content-Disposition: attachment;filename="'.$filename.'.csv"');
  537. // header('Cache-Control: max-age=0');
  538. //
  539. // //打开PHP文件句柄,php://output 表示直接输出到浏览器
  540. // $fp = fopen('php://output', 'a');
  541. //
  542. // //输出Excel列名信息
  543. // foreach ($header as $key => $value) {
  544. // //CSV的Excel支持GBK编码,一定要转换,否则乱码
  545. // $header[$key] = iconv('utf-8', 'gbk', $value);
  546. // }
  547. //
  548. // //将数据通过fputcsv写到文件句柄
  549. // fputcsv($fp, $header);
  550. //
  551. // //计数器
  552. // $num = 0;
  553. //
  554. // //每隔$limit行,刷新一下输出buffer,不要太大,也不要太小
  555. // $limit = 10000;
  556. //
  557. // //逐行取出数据,不浪费内存
  558. // $count = count($data);
  559. // for ($i = 0; $i < $count; $i++) {
  560. //
  561. // $num++;
  562. //
  563. // //刷新一下输出buffer,防止由于数据过多造成问题
  564. // if ($limit == $num) {
  565. // ob_flush();
  566. // flush();
  567. // $num = 0;
  568. // }
  569. //
  570. // $row = $data[$i];
  571. // foreach ($row as $key => $value) {
  572. // $row[$key] = iconv('utf-8', 'gbk', $value);
  573. // }
  574. //
  575. // fputcsv($fp, $row);
  576. // }
  577. // exit();
  578. //}
  579. // 获取除空格外的字数
  580. function getSize(string $content)
  581. {
  582. $content = preg_replace('/\s+/', '', $content);
  583. return mb_strlen($content, 'utf-8');
  584. }
  585. // 获取word字符数(不计空格)
  586. function getChargeSize(string $content)
  587. {
  588. //判断是否存在替换字符
  589. $is_replace_count = substr_count($content, "龘");
  590. try {
  591. //先将回车换行符做特殊处理
  592. $str = preg_replace('/(\r\n+|\s+| +)/', "龘", $content);
  593. //处理英文字符数字,连续字母、数字、英文符号视为一个单词
  594. $str = preg_replace('/[a-z_A-Z0-9-\.!@#\$%\\\^&\*\)\(\+=\{\}\[\]\/",\'<>~`\?:;|]/', "m", $str);
  595. //合并字符m,连续字母、数字、英文符号视为一个单词
  596. $str = preg_replace('/m+/', "*", $str);
  597. //去掉回车换行符
  598. $str = preg_replace('/龘+/', "", $str);
  599. //返回字数
  600. return mb_strlen($str) + $is_replace_count;
  601. } catch (\Exception $e) {
  602. return 0;
  603. }
  604. }
  605. // 将阿拉伯数字转换成中文
  606. function chineseNum($figure, $capital = false, $mode = true)
  607. {
  608. if ($figure == '0') return '零';
  609. $numberChar = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
  610. $unitChar = ['', '十', '百', '千', '', '万', '亿', '兆', '京', '垓', '秭', '穣', '沟', '涧', '正', '载', '极', '恒河沙', '阿僧祇', '那由他', '不可思议', '无量大数'];
  611. if ($capital !== false) {
  612. $numberChar = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
  613. $unitChar = ['', '拾', '佰', '仟', '', '万', '亿', '兆', '京', '垓', '秭', '穣', '沟', '涧', '正', '载', '极', '恒河沙', '阿僧祇', '那由他', '不可思议', '无量大数'];
  614. }
  615. $dec = "点";
  616. $target = '';
  617. $matches = [];
  618. if ($mode) {
  619. preg_match("/^0*(\d*)\.?(\d*)/", $figure, $matches);
  620. } else {
  621. preg_match("/(\d*)\.?(\d*)/", $figure, $matches);
  622. }
  623. list(, $number, $point) = $matches;
  624. if ($point) {
  625. $target = $dec . chineseNum($point, $capital, false);
  626. }
  627. if (!$number) {
  628. return $target;
  629. }
  630. $str = strrev($number);
  631. for ($i = 0; $i < strlen($str); $i++) {
  632. $out[$i] = $numberChar[$str[$i]];
  633. if ($mode === false) {
  634. continue;
  635. }
  636. $out[$i] .= $str[$i] != '0' ? $unitChar[$i % 4] : '';
  637. if ($i > 0 && $str[$i] + $str[$i - 1] == 0) {
  638. $out[$i] = '';
  639. }
  640. if ($i % 4 == 0) {
  641. $temp = substr($str, $i, 4);
  642. $out[$i] = str_replace($numberChar[0], '', $out[$i]);
  643. if (strrev($temp) > 0) {
  644. $out[$i] .= $unitChar[4 + floor($i / 4)];
  645. } else {
  646. $out[$i] .= $numberChar[0];
  647. }
  648. }
  649. }
  650. $result = join('', array_reverse($out)) . $target;
  651. return mb_substr($result, 0, 2) == '一十' ? mb_substr($result, 1) : $result;
  652. }
  653. function addPrefix($str)
  654. {
  655. if (!$str) return '';
  656. if (mb_substr($str, 0, 4) == 'http') return $str;
  657. if (mb_substr($str, 0, 5) == 'https') return $str;
  658. if (mb_substr($str, 0, 7) == '/books/') return 'http://zwcontent.oss-cn-hangzhou.aliyuncs.com' . $str;
  659. if (mb_substr($str, 0, 6) == '/card/') return 'http://zwcontent.oss-cn-hangzhou.aliyuncs.com' . $str;
  660. if (mb_substr($str, 0, 15) == 'uploader/idcard') return 'http://zwcontent.oss-cn-hangzhou.aliyuncs.com/' . $str;
  661. if (mb_substr($str, 0, 6) == '/cover') return 'https://cdn-newyc.ycsd.cn/ycsd_cover/covermiddle' . mb_substr($str, 6);
  662. if (mb_substr($str, 0, 8) == '/images/') return 'https://cdn-newyc.ycsd.cn/ycsd_cover' . $str;
  663. }
  664. /**
  665. * 章节内容排版
  666. *
  667. * @param $content
  668. * @return string
  669. */
  670. function filterContent($content)
  671. {
  672. if (!$content) return '';
  673. $content = str_replace(
  674. ['&nbsp;&nbsp;', '<br /><br />', '<br>', '<br />', '&nbsp;', '<p>', '</p>', '&ldquo;', '&rdquo;', '&hellip;', '&lsquo;', '&rsquo;', '&mdash;'],
  675. [' ', PHP_EOL, PHP_EOL, PHP_EOL, ' ', '', PHP_EOL, '“', '”', '...', '‘', '’', '-'],
  676. $content);
  677. $content = preg_replace('/(\r\n)+/', PHP_EOL, $content);
  678. // 段落首字母前加两个中文空格
  679. $string = explode(PHP_EOL, $content);
  680. foreach ($string as $line => $text) {
  681. $string[$line] = str_replace([' ', "\r\n", "\r", "\n", ' '], '', $string[$line]);
  682. if (!$string[$line]) {
  683. unset($string[$line]);
  684. } else {
  685. $string[$line] = $string[$line] . PHP_EOL;
  686. // if (mb_substr($string[$line], 0, 1) == ' ') {
  687. // $string[$line] = str_replace(' ', '', $string[$line]); // 去除多个空格
  688. // $string[$line] = '  ' . $string[$line].PHP_EOL;
  689. // }
  690. // if (mb_substr($string[$line], 0, 2) != '  ') {
  691. // $string[$line] = '  ' . $string[$line].PHP_EOL;
  692. // }
  693. // if (mb_substr($string[$line], 0, 2) == '  ' && str_replace(' ', '', $string[$line])) {
  694. // $string[$line] .= PHP_EOL;
  695. // }
  696. }
  697. }
  698. $content = implode(PHP_EOL, $string);
  699. return $content;
  700. }
  701. /**
  702. * 书籍简介排版
  703. *
  704. * @param $content
  705. * @return string
  706. */
  707. function filterContent2($content)
  708. {
  709. if (!$content) return '';
  710. $content = str_replace(
  711. ['&nbsp;&nbsp;', '<br /><br />', '<br>', '<br />', '&nbsp;', '<p>', '</p>', '&ldquo;', '&rdquo;', '&hellip;', '&lsquo;', '&rsquo;', '&mdash;'],
  712. [' ', PHP_EOL, PHP_EOL, PHP_EOL, ' ', '', PHP_EOL, '“', '”', '...', '‘', '’', '-'],
  713. $content);
  714. $content = preg_replace('/(\r\n)+/', PHP_EOL, $content);
  715. // 段落首字母前加两个中文空格
  716. $string = explode(PHP_EOL, $content);
  717. $content = '';
  718. foreach ($string as $line => $text) {
  719. $string[$line] = str_replace([' ', "\r\n", "\r", "\n", ' ', '<br />'], '', $string[$line]);
  720. if (!$string[$line]) {
  721. unset($string[$line]);
  722. } else {
  723. $string[$line] = '  ' . $string[$line] . '<br />';
  724. $content .= $string[$line];
  725. }
  726. }
  727. $content = trim($content, '<br />');
  728. return $content;
  729. }
  730. /**
  731. * 书籍简介排版-抖音版
  732. *
  733. * @param $content
  734. * @return string
  735. */
  736. function filterIntro($content)
  737. {
  738. if (!$content) return '';
  739. $content = str_replace(
  740. ['&nbsp;&nbsp;', '<br /><br />', '<br>', '<br />', '&nbsp;', '<p>', '</p>', '&ldquo;', '&rdquo;', '&hellip;', '&lsquo;', '&rsquo;', '&mdash;'],
  741. [' ', ' ', ' ', ' ', ' ', '', ' ', '“', '”', '...', '‘', '’', '-'],
  742. $content);
  743. $content = preg_replace('/(\r\n)+/', PHP_EOL, $content);
  744. // 段落首字母前加两个中文空格
  745. $string = explode(PHP_EOL, $content);
  746. $content = '';
  747. foreach ($string as $line => $text) {
  748. $string[$line] = str_replace([' ', "\r\n", "\r", "\n", ' ', '<br />'], '', $string[$line]);
  749. if (!$string[$line]) {
  750. unset($string[$line]);
  751. } else {
  752. $content .= $string[$line];
  753. }
  754. }
  755. $content = trim($content, '<br />');
  756. return $content;
  757. }
  758. function sensitiveStr($list, $string)
  759. {
  760. $count = 0; //违规词的个数
  761. $sensitiveWord = ''; //违规词
  762. $stringAfter = $string; //替换后的内容
  763. $total = count($list);
  764. $size = 500;
  765. $last = ceil($total / $size);
  766. $patternList = [];
  767. for ($page = 1; $page <= $last; $page++) {
  768. $arr = array_slice($list, $size * ($page - 1), $size);
  769. $filter = [];
  770. foreach ($arr as $v) {
  771. if (preg_match('/[^a-zA-Z0-9\|\p{Han}\·]/u', $v)) continue;
  772. $filter[] = $v;
  773. // $a = preg_replace('/[^a-zA-Z0-9\|\p{Han}]/u', '', $v);
  774. // if ($a) $filter[] = $a;
  775. }
  776. $pattern = "/" . implode("|", $filter) . "/i"; //定义正则表达式
  777. if (preg_match_all($pattern, $string, $matches)) { //匹配到了结果
  778. $patternList = array_merge($patternList, $matches[0]); //匹配到的数组
  779. }
  780. }
  781. $sensitiveWord = '';
  782. if ($patternList) {
  783. $count = count($patternList);
  784. // $sensitiveWord = implode(',', $patternList); //敏感词数组转字符串
  785. $replaceArray = array_combine($patternList, array_fill(0, count($patternList), '*')); //把匹配到的数组进行合并,替换使用
  786. $stringAfter = strtr($string, $replaceArray); //结果替换
  787. // 将敏感词合并
  788. $return_pattern = [];
  789. foreach ($patternList as $v) {
  790. if (!isset($return_pattern[$v])) {
  791. $return_pattern[$v] = [
  792. 'word' => $v,
  793. 'count' => 1,
  794. ];
  795. } else {
  796. $return_pattern[$v]['count'] += 1;
  797. }
  798. }
  799. foreach ($return_pattern as $v) {
  800. $sensitiveWord .= $v['word'] . ',';
  801. }
  802. }
  803. return [
  804. 'count' => $count,
  805. 'sensitive_words' => trim($sensitiveWord, ','),
  806. 'content' => $stringAfter
  807. ];
  808. }
  809. /**
  810. * 转换时间格式
  811. *
  812. * @param $date
  813. * @param string $format
  814. * @return false|string
  815. */
  816. function transDate($date, $format = 'Y-m-d H:i:s')
  817. {
  818. return strtotime($date) > 0 ? date($format, strtotime($date)) : '';
  819. }
  820. /**
  821. * 根据网段获取计算所有IP
  822. *
  823. * @param string $segment 网段 '139.217.0.1/24'
  824. * @return array [网络地址:139.217.0.1 广播地址:139.217.0.255 IP列表: ['139.217.0.2','139.217.0.3'……'139.217.0.254']]
  825. */
  826. function getIpBySegment($segment)
  827. {
  828. $segmentInfo = explode("/", $segment);
  829. $beginIpArray = explode(".", $segmentInfo[0]);
  830. $mask = intval($segmentInfo['1']);
  831. $endIp = array();
  832. foreach ($beginIpArray as $ipKey => $item) {
  833. $beginFlag = 8 * ($ipKey); //0 8 16 24
  834. $endFlag = 8 * ($ipKey + 1);//8 16 24 32
  835. $decbinItem = str_pad(decbin($item), 8, "0", STR_PAD_LEFT);
  836. $endIp[] = $mask >= $endFlag ? $item : ($mask > $beginFlag ? bindec(str_pad(substr($decbinItem, 0, $mask - $beginFlag), 8, "1", STR_PAD_RIGHT)) : ($ipKey <= 2 ? pow(2, 8) - 1 : pow(2, 8) - 1));
  837. }
  838. $ipArray = array();
  839. for ($beginIp[0] = $beginIpArray[0]; $beginIp[0] <= $endIp[0]; $beginIp[0]++) {
  840. for ($beginIp[1] = $beginIpArray[1]; $beginIp[1] <= $endIp[1]; $beginIp[1]++) {
  841. for ($beginIp[2] = $beginIpArray[2]; $beginIp[2] <= $endIp[2]; $beginIp[2]++) {
  842. for ($beginIp[3] = $beginIpArray[3]; $beginIp[3] <= $endIp[3]; $beginIp[3]++) {
  843. $ipArray[] = implode(".", $beginIp);
  844. }
  845. }
  846. }
  847. }
  848. $network_ip_addr = $beginIpArray[0] . '.' . $beginIpArray[1] . '.' . $beginIpArray[2] . '.' . '0'; // 网络地址
  849. $broadcast_ip_addr = end($ipArray); // 广播地址
  850. if ($ipArray[0] == $network_ip_addr) { // 如果是网络地址则删掉
  851. unset($ipArray[0]);
  852. }
  853. $last = count($ipArray);
  854. unset($ipArray[$last]);
  855. return [$network_ip_addr, $broadcast_ip_addr, $ipArray];
  856. }
  857. /**
  858. * 在指定网段中分配子网段
  859. *
  860. * @param string $segment 指定网段
  861. * @param int $ipNum 需要的IP数
  862. * @param array $usedIpArray 不可用(已经使用)的IP,默认为空数组
  863. * @return bool|string 成功则返回分配的网段
  864. */
  865. function allocateSegment($segment, $ipNum, $usedIpArray = [])
  866. {
  867. $usedIpArray = empty($usedIpArray) ? [] : array_flip($usedIpArray);
  868. //计算需要多少个IP
  869. $i = 0;
  870. $ipCount = pow(2, $i);
  871. while ($ipCount < $ipNum) {
  872. $i++;
  873. $ipCount = pow(2, $i);
  874. }
  875. $newMask = 32 - $i;
  876. //大网段的开始和结束IP
  877. $segmentInfo = explode("/", $segment); //['139.217.0.1',24]
  878. $beginIpArray = explode(".", $segmentInfo[0]);//[139,217,0,1]
  879. $mask = intval($segmentInfo['1']); //24
  880. if ($newMask < $mask) {
  881. return false;
  882. }
  883. $endIp = array();
  884. $step = [];
  885. foreach ($beginIpArray as $ipKey => $item) {
  886. $beginFlag = 8 * ($ipKey); //0 8 16 24
  887. $endFlag = 8 * ($ipKey + 1);//8 16 24 32
  888. $step[$ipKey] = $newMask > $endFlag ? 1 : ($endFlag - $newMask < 8 ? pow(2, $endFlag - $newMask) : pow(2, 8));
  889. $decbinItem = str_pad(decbin($item), 8, "0", STR_PAD_LEFT);
  890. $endIp[] = $mask >= $endFlag ? $item : ($mask > $beginFlag ? bindec(str_pad(substr($decbinItem, 0, $mask - $beginFlag), 8, "1", STR_PAD_RIGHT)) : ($ipKey <= 2 ? pow(2, 8) - 1 : pow(2, 8) - 1));
  891. }
  892. //遍历生成网段
  893. for ($beginIp[0] = $beginIpArray[0]; $beginIp[0] <= $endIp[0]; $beginIp[0] += $step[0]) {
  894. for ($beginIp[1] = $beginIpArray[1]; $beginIp[1] <= $endIp[1]; $beginIp[1] += $step[1]) {
  895. for ($beginIp[2] = $beginIpArray[2]; $beginIp[2] <= $endIp[2]; $beginIp[2] += $step[2]) {
  896. for ($beginIp[3] = $beginIpArray[3]; $beginIp[3] <= $endIp[3]; $beginIp[3] += $step[3]) {
  897. $newSegment = implode('.', $beginIp) . '/' . $newMask;
  898. //获取该网段所有的IP
  899. $ipArray = getIpBySegment($newSegment);
  900. $canUse = true;
  901. //判断该网段是否可用
  902. if (!empty($usedIpArray)) {
  903. foreach ($ipArray as $ip) {
  904. if (isset($usedIpArray[$ip])) {
  905. $canUse = false;
  906. break;
  907. }
  908. }
  909. }
  910. if ($canUse) {
  911. return $newSegment;
  912. }
  913. }
  914. }
  915. }
  916. }
  917. return false;
  918. }
  919. function remove_xss($val)
  920. {
  921. // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed
  922. // this prevents some character re-spacing such as <java\0script>
  923. // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs
  924. $val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val);
  925. // straight replacements, the user should never need these since they're normal characters
  926. // this prevents like <IMG SRC=@avascript:alert('XSS')>
  927. $search = 'abcdefghijklmnopqrstuvwxyz';
  928. $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  929. $search .= '1234567890!@#$%^&*()';
  930. $search .= '~`";:?+/={}[]-_|\'\\';
  931. for ($i = 0; $i < strlen($search); $i++) {
  932. // ;? matches the ;, which is optional
  933. // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars
  934. // @ @ search for the hex values
  935. $val = preg_replace('/(&#[xX]0{0,8}' . dechex(ord($search[$i])) . ';?)/i', $search[$i], $val); // with a ;
  936. // @ @ 0{0,7} matches '0' zero to seven times
  937. $val = preg_replace('/(�{0,8}' . ord($search[$i]) . ';?)/', $search[$i], $val); // with a ;
  938. }
  939. // now the only remaining whitespace attacks are \t, \n, and \r
  940. $ra1 = array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
  941. $ra2 = array(
  942. 'onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload'
  943. );
  944. $ra = array_merge($ra1, $ra2);
  945. $found = true; // keep replacing as long as the previous round replaced something
  946. while ($found == true) {
  947. $val_before = $val;
  948. for ($i = 0; $i < sizeof($ra); $i++) {
  949. $pattern = '/';
  950. for ($j = 0; $j < strlen($ra[$i]); $j++) {
  951. if ($j > 0) {
  952. $pattern .= '(';
  953. $pattern .= '(&#[xX]0{0,8}([9ab]);)';
  954. $pattern .= '|';
  955. $pattern .= '|(�{0,8}([9|10|13]);)';
  956. $pattern .= ')*';
  957. }
  958. $pattern .= $ra[$i][$j];
  959. }
  960. $pattern .= '/i';
  961. $replacement = substr($ra[$i], 0, 2) . '<x>' . substr($ra[$i], 2); // add in <> to nerf the tag
  962. $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags
  963. if ($val_before == $val) {
  964. // no replacements were made, so exit the loop
  965. $found = false;
  966. }
  967. }
  968. }
  969. return $val;
  970. }
  971. /**
  972. * 计算作者积分等级
  973. *
  974. * @param $score
  975. */
  976. function calcAuthorLevel($score): int
  977. {
  978. switch (true) {
  979. case $score <= 0:
  980. $level = 0;
  981. break;
  982. case $score <= 5000:
  983. $level = 1;
  984. break;
  985. case $score <= 50000:
  986. $level = 2;
  987. break;
  988. case $score <= 100000:
  989. $level = 3;
  990. break;
  991. case $score <= 300000:
  992. $level = 4;
  993. break;
  994. case $score <= 800000:
  995. $level = 5;
  996. break;
  997. case $score <= 1500000:
  998. $level = 6;
  999. break;
  1000. case $score <= 2500000:
  1001. $level = 7;
  1002. break;
  1003. case $score <= 5000000:
  1004. $level = 8;
  1005. break;
  1006. case $score <= 10000000:
  1007. $level = 9;
  1008. break;
  1009. default:
  1010. $level = 10;
  1011. break;
  1012. }
  1013. return $level;
  1014. }
  1015. /**
  1016. * 运营数据(上传附件)
  1017. *
  1018. * @param $file
  1019. * @return string
  1020. * @throws Exception
  1021. */
  1022. function uploadEnclosureFile($file)
  1023. {
  1024. // 阿里云主账号
  1025. $accessKeyId = env('OSS_ACCESS_ID');
  1026. $accessKeySecret = env('OSS_ACCESS_KEY');
  1027. $endpoint = env('OSS_END_POINT');
  1028. $bucket = env('OSS_BUCKET');
  1029. // 设置文件名称。
  1030. $object = 'books/enclosure/' . randStr(10) . '--' . $file->getClientOriginalName();
  1031. try {
  1032. $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
  1033. $ossImgBackData = $ossClient->uploadFile($bucket, $object, $file->path());
  1034. } catch (OssException $e) {
  1035. printf($e->getMessage());
  1036. return '';
  1037. }
  1038. $urlArr = parse_url($ossImgBackData['oss-request-url']);
  1039. return getProp($urlArr, 'path') ? 'http://' . $bucket . '.' . $endpoint . getProp($urlArr, 'path') : '';
  1040. }
  1041. // 获取当前域名是http还是https
  1042. function getHttpType()
  1043. {
  1044. return ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://';
  1045. }
  1046. // 根据id生成唯一邀请码
  1047. function enCodeId($user_id)
  1048. {
  1049. $key = 'XzeTdSPQc1uYHRBVWmUE6x94q25g3krfCGhb8FjtDZvMNKJpnayw7s';
  1050. $num = strlen($key);
  1051. $code = ''; // 邀请码
  1052. while ($user_id > 0) { // 转进制
  1053. $mod = $user_id % $num; // 求模
  1054. $user_id = ($user_id - $mod) / $num;
  1055. $code = $key[$mod] . $code;
  1056. }
  1057. $code = str_pad($code, 6, 'A', STR_PAD_LEFT); // 不足用0补充
  1058. return $code;
  1059. }
  1060. // 根据邀请码解密为id
  1061. function deCodeId($code)
  1062. {
  1063. $key = 'XzeTdSPQc1uYHRBVWmUE6x94q25g3krfCGhb8FjtDZvMNKJpnayw7s';
  1064. $num = strlen($key);
  1065. if (strrpos($code, '0') !== false) $code = substr($code, strrpos($code, '0') + 1);
  1066. $len = strlen($code);
  1067. $code = strrev($code);
  1068. $user_id = 0;
  1069. for ($i = 0; $i < $len; $i++) {
  1070. $user_id += strpos($key, $code[$i]) * pow($num, $i);
  1071. }
  1072. return $user_id;
  1073. }
  1074. /**
  1075. * 将二维数组按其中的某个数组排序(此方法适用于将数据库数据按数组取出后自动按ID排序的情况 ps:即未按该数组排序)
  1076. *
  1077. * @param array $array 二维数组
  1078. * @param array $sort 排序数组
  1079. * @param string $field 排序字段(二维数组和排序数组相同的字段)
  1080. * @return array
  1081. */
  1082. function sortByArray(array $array, array $sort, string $field): array
  1083. {
  1084. $data = [];
  1085. if (is_array($array) && is_array($sort)) {
  1086. foreach ($sort as $v) {
  1087. foreach ($array as $key => $val) {
  1088. if ($v == $val[$field]) {
  1089. array_push($data, $array[$key]);
  1090. }
  1091. }
  1092. }
  1093. }
  1094. return $data;
  1095. }
  1096. /**
  1097. * 将二维数组按其中的字段排序(正序或倒序)
  1098. *
  1099. * @param array $array 二维数组
  1100. * @param string $field 排序字段
  1101. * @param mixed $type 排序方式(3倒序,4正序)
  1102. * @return array|mixed
  1103. */
  1104. function sortByField(array $array, string $field, $type): array
  1105. {
  1106. if (is_array($array)) {
  1107. array_multisort(array_column($array, $field), $type, $array);
  1108. }
  1109. return $array;
  1110. }
  1111. // 生成用户邀请码
  1112. function setUserInviteCode($id)
  1113. {
  1114. return \Vinkla\Hashids\Facades\Hashids::connection('invite')->encode($id);
  1115. }
  1116. // 解密用户邀请码
  1117. function decodeUserInviteCode($code)
  1118. {
  1119. return \Vinkla\Hashids\Facades\Hashids::connection('invite')->decode($code);
  1120. }
  1121. function getMillisecond()
  1122. {
  1123. list($microsecond, $time) = explode(' ', microtime());
  1124. return (float)sprintf('%.0f', (floatval($microsecond) + floatval($time)) * 1000);
  1125. }
  1126. function get_client_ip($type = 0, $adv = false)
  1127. {
  1128. $type = $type ? 1 : 0;
  1129. static $ip = null;
  1130. if (null !== $ip) {
  1131. return $ip[$type];
  1132. }
  1133. if ($adv) {
  1134. if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  1135. $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
  1136. $pos = array_search('unknown', $arr);
  1137. if (false !== $pos) {
  1138. unset($arr[$pos]);
  1139. }
  1140. $ip = trim($arr[0]);
  1141. } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
  1142. $ip = $_SERVER['HTTP_CLIENT_IP'];
  1143. } elseif (isset($_SERVER['REMOTE_ADDR'])) {
  1144. $ip = $_SERVER['REMOTE_ADDR'];
  1145. }
  1146. } elseif (isset($_SERVER['REMOTE_ADDR'])) {
  1147. $ip = $_SERVER['REMOTE_ADDR'];
  1148. }
  1149. // IP地址合法验证
  1150. $long = sprintf("%u", ip2long($ip));
  1151. $ip = $long ? array($ip, $long) : array('0.0.0.0', 0);
  1152. return $ip[$type];
  1153. }
  1154. /**
  1155. * 获取真实IP
  1156. */
  1157. function _getIp()
  1158. {
  1159. if (getenv('HTTP_X_FORWARDED_FOR')) {
  1160. $ip = getenv('HTTP_X_FORWARDED_FOR');
  1161. } else if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown"))
  1162. $ip = getenv("HTTP_CLIENT_IP");
  1163. else if (getenv("HTTP_X_FORWARD_FOR") && strcasecmp(getenv("HTTP_X_FORWARD_FOR"), "unknown"))
  1164. $ip = getenv("HTTP_X_FORWARD_FOR");
  1165. else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown"))
  1166. $ip = getenv("REMOTE_ADDR");
  1167. else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown"))
  1168. $ip = $_SERVER['REMOTE_ADDR'];
  1169. else
  1170. $ip = "unknown";
  1171. return ($ip);
  1172. }
  1173. /**
  1174. * 数组 转 对象
  1175. *
  1176. * @param array $arr 数组
  1177. * @return object
  1178. */
  1179. function array_to_object($arr)
  1180. {
  1181. if (gettype($arr) != 'array') {
  1182. return;
  1183. }
  1184. foreach ($arr as $k => $v) {
  1185. if (gettype($v) == 'array' || getType($v) == 'object') {
  1186. $arr[$k] = (object)array_to_object($v);
  1187. }
  1188. }
  1189. return (object)$arr;
  1190. }
  1191. /**
  1192. * 对象 转 数组
  1193. *
  1194. * @param object $obj 对象
  1195. * @return array
  1196. */
  1197. function object_to_array($obj)
  1198. {
  1199. $obj = (array)$obj;
  1200. foreach ($obj as $k => $v) {
  1201. if (gettype($v) == 'resource') {
  1202. return;
  1203. }
  1204. if (gettype($v) == 'object' || gettype($v) == 'array') {
  1205. $obj[$k] = (array)object_to_array($v);
  1206. }
  1207. }
  1208. return $obj;
  1209. }
  1210. /**
  1211. * 检查是否为手机号码
  1212. */
  1213. function _isPhone($number)
  1214. {
  1215. return preg_match("/^1[34578][0-9]{9}$/", $number);
  1216. }
  1217. /**
  1218. * 判断所传的参数是否缺少,如果缺少返回渠道的字段,正确返回0
  1219. *
  1220. * @param array $param
  1221. * @param array $must
  1222. * @return int|mixed
  1223. */
  1224. function checkParam(array $param, array $must)
  1225. {
  1226. foreach ($must as $item) {
  1227. if (array_key_exists($item, $param) && $param[$item] != '') {
  1228. } else {
  1229. return $item;
  1230. }
  1231. }
  1232. return 0;
  1233. }
  1234. /**
  1235. * 对象 转 数组
  1236. *
  1237. * @param object $obj 对象
  1238. * @return array
  1239. */
  1240. function ignoreKeyInArray($targetArray, $delete_keys = [], $changes = [])
  1241. {
  1242. $change_keys = array_keys($changes);
  1243. foreach ($targetArray as $key => $value) {
  1244. if (in_array($key, $delete_keys) && isset($targetArray[$key])) unset($targetArray[$key]);
  1245. if (in_array($key, $change_keys) && isset($targetArray[$key])) $targetArray[$key] = $changes[$key];
  1246. if (is_array($value)) ignoreKeyInArray($value, $delete_keys, $change_keys);
  1247. }
  1248. return $targetArray;
  1249. }
  1250. function itemTransform($trans, $data)
  1251. {
  1252. if ($data) {
  1253. return $trans->transform($data);
  1254. } else {
  1255. return [];
  1256. }
  1257. }
  1258. function collectionTransform($trans, $data)
  1259. {
  1260. $ret_data = [];
  1261. if ($data) {
  1262. foreach ($data as $item) {
  1263. $ret_data[] = $trans->transform($item);
  1264. }
  1265. }
  1266. return $ret_data;
  1267. }
  1268. function paginationTransform($trans, $paginator)
  1269. {
  1270. $ret = [];
  1271. $ret['list'] = [];
  1272. if ($paginator) {
  1273. foreach ($paginator as $item) {
  1274. $ret['list'][] = $trans->transform($item);
  1275. }
  1276. $ret['meta'] = [
  1277. 'total' => (int)$paginator->total(),
  1278. 'per_page' => (int)$paginator->perPage(),
  1279. 'current_page' => (int)$paginator->currentPage(),
  1280. 'last_page' => (int)$paginator->lastPage(),
  1281. 'next_page_url' => (string)$paginator->nextPageUrl(),
  1282. 'prev_page_url' => (string)$paginator->previousPageUrl()
  1283. ];
  1284. }
  1285. return $ret;
  1286. }
  1287. /**
  1288. * 加密site id
  1289. */
  1290. function encodeDistributionChannelId($id)
  1291. {
  1292. $encrypt_pool = [
  1293. ];
  1294. if (isset($encrypt_pool[$id])) {
  1295. return $encrypt_pool[$id];
  1296. }
  1297. $hashids = new \Hashids\Hashids('', 16, 'abcdefghjklmnopqrstuvwxyz1234567890');
  1298. return $hashids->encode($id);
  1299. }
  1300. /**
  1301. * 解密密site id
  1302. */
  1303. function decodeDistributionChannelId($code)
  1304. {
  1305. $encrypt_pool = [
  1306. ];
  1307. if (isset($encrypt_pool[$code])) {
  1308. return $encrypt_pool[$code];
  1309. }
  1310. $hashids = new \Hashids\Hashids('', 16, 'abcdefghjklmnopqrstuvwxyz1234567890');
  1311. $res = $hashids->decode($code);
  1312. if ($res && isset($res[0])) {
  1313. return $res[0];
  1314. }
  1315. return null;
  1316. }
  1317. //bid加密
  1318. function book_hash_encode($bid)
  1319. {
  1320. return Vinkla\Hashids\Facades\Hashids::encode($bid);
  1321. }
  1322. function decodeBid($encode_bid)
  1323. {
  1324. $bid = 0;
  1325. try {
  1326. $bid_arr = \Hashids::decode($encode_bid);
  1327. if (isset($bid_arr[0])) {
  1328. $bid = $bid_arr[0];
  1329. }
  1330. } catch (\Exception $e) {
  1331. return null;
  1332. }
  1333. return $bid;
  1334. }
  1335. /**
  1336. * 获取当前域名
  1337. */
  1338. function _domain()
  1339. {
  1340. return str_replace('https://', '', str_replace('http://', '', url('/')));
  1341. }
  1342. /**
  1343. * 字符串转*
  1344. *
  1345. * @param $str // 待转的字符串
  1346. * @param $start // 转*起始位置
  1347. * @param int $end // 转*结束位置
  1348. * @param string $dot // 转换的字符(必须是单字符,默认是*)
  1349. * @param string $charset // 编码方式
  1350. * @param string $end_char // 特殊字符(碰到此字符则确定end位置)
  1351. * @return string
  1352. */
  1353. function trans_pass($str, $start, $end = 0, $dot = "*", $charset = "UTF-8", $end_char = '@'): string
  1354. {
  1355. $len = mb_strlen($str, $charset);
  1356. if ($start == 0 || $start > $len) {
  1357. $start = 1;
  1358. }
  1359. if ($end != 0 && $end > $len) {
  1360. $end = $len - 2;
  1361. }
  1362. if (strstr($str, $end_char)) {
  1363. $end = $len - strrpos($str, $end_char);
  1364. }
  1365. $endStart = $len - $end;
  1366. $top = mb_substr($str, 0, $start, $charset);
  1367. $bottom = "";
  1368. if ($endStart > 0) {
  1369. $bottom = mb_substr($str, $endStart, $end, $charset);
  1370. }
  1371. $len -= mb_strlen($top, $charset);
  1372. $len -= mb_strlen($bottom, $charset);
  1373. $newStr = $top;
  1374. for ($i = 0; $i < $len; $i++) {
  1375. $newStr .= $dot;
  1376. }
  1377. $newStr .= $bottom;
  1378. return $newStr;
  1379. }
  1380. /**
  1381. * 格式化章节内容
  1382. *
  1383. * @param $content
  1384. * @return false|string
  1385. */
  1386. function formatContent($content)
  1387. {
  1388. if (!$content) return '';
  1389. $content = str_replace(
  1390. ['&nbsp;&nbsp;', '<br /><br />', '<br>', '<br />', '&nbsp;', '<p>', '</p >', '&ldquo;', '&rdquo;', '&hellip;'],
  1391. [' ', PHP_EOL, PHP_EOL, PHP_EOL, ' ', '', PHP_EOL, '“', '”', '...'],
  1392. $content);
  1393. $content = str_replace(["&nbsp;", '&ldquo;', '&hellip;', '&rdquo;', '<p>'], '', $content);
  1394. // 段落首字母前加两个中文空格
  1395. $string = explode(PHP_EOL, $content);
  1396. foreach ($string as $line => $text) {
  1397. if (mb_substr($text, 0, 2) != '  ') $string[$line] = '  ' . $text;
  1398. }
  1399. $content = implode(PHP_EOL, $string);
  1400. $content = mb_convert_encoding($content, 'UTF-8', 'UTF-8,GBK,GB2312');
  1401. $content = iconv('UTF-8', 'UTF-8//IGNORE', $content);
  1402. return $content;
  1403. }
  1404. /**
  1405. * 筛选出有效的id集合
  1406. *
  1407. * @param array $ids
  1408. * @return array
  1409. */
  1410. function filterValidIds(array $ids): array
  1411. {
  1412. // 传参
  1413. if (empty($ids)) {
  1414. return [];
  1415. }
  1416. $result = [];
  1417. foreach ($ids as $id) {
  1418. if (in_array($id, $result) || !is_numeric($id) || (int)$id < 1) {
  1419. continue;
  1420. }
  1421. $result[] = (int)$id;
  1422. }
  1423. return $result;
  1424. }
  1425. function arrayToStr($map)
  1426. {
  1427. $isMap = isArrMap($map);
  1428. $result = "";
  1429. if ($isMap) {
  1430. $result = "map[";
  1431. }
  1432. $keyArr = array_keys($map);
  1433. if ($isMap) {
  1434. sort($keyArr);
  1435. }
  1436. $paramsArr = array();
  1437. foreach ($keyArr as $k) {
  1438. $v = $map[$k];
  1439. if ($isMap) {
  1440. if (is_array($v)) {
  1441. $paramsArr[] = sprintf("%s:%s", $k, arrayToStr($v));
  1442. } else {
  1443. $paramsArr[] = sprintf("%s:%s", $k, trim(strval($v)));
  1444. }
  1445. } else {
  1446. if (is_array($v)) {
  1447. $paramsArr[] = arrayToStr($v);
  1448. } else {
  1449. $paramsArr[] = trim(strval($v));
  1450. }
  1451. }
  1452. }
  1453. $result = sprintf("%s%s", $result, join(" ", $paramsArr));
  1454. if (!$isMap) {
  1455. $result = sprintf("[%s]", $result);
  1456. } else {
  1457. $result = sprintf("%s]", $result);
  1458. }
  1459. return $result;
  1460. }
  1461. function isArrMap($map)
  1462. {
  1463. foreach ($map as $k => $v) {
  1464. if (is_string($k)) {
  1465. return true;
  1466. }
  1467. }
  1468. return false;
  1469. }
  1470. /**
  1471. * 随机字符串
  1472. *
  1473. * @param $length
  1474. * @return string
  1475. */
  1476. function makeRandStr($length): string
  1477. {
  1478. // 密码字符集,可任意添加你需要的字符
  1479. $str = [
  1480. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1481. 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
  1482. 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D',
  1483. 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
  1484. 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  1485. '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
  1486. ];
  1487. // 在 $str 中随机取 $length 个数组元素键名
  1488. $keys = array_rand($str, $length);
  1489. $password = '';
  1490. for ($i = 0; $i < $length; $i++) {
  1491. // 将 $length 个数组元素连接成字符串
  1492. $password .= $str[$keys[$i]];
  1493. }
  1494. return $password;
  1495. }
  1496. /**
  1497. * 导出数据为excel表格
  1498. *
  1499. * @param $data 一个二维数组,结构如同从数据库查出来的数组
  1500. * @param $title excel的第一行标题,一个数组,如果为空则没有标题
  1501. * @param $filename 下载的文件名
  1502. * @examlpe10
  1503. */
  1504. function exportExcel($data = [], $title = [], $filename = 'report')
  1505. {
  1506. ob_end_clean();
  1507. ob_start();
  1508. header("Content-type:application/octet-stream");
  1509. header("Accept-Ranges:bytes");
  1510. header("Content-type:application/vnd.ms-excel");
  1511. header("Content-Disposition:attachment;filename=" . $filename . ".xls");
  1512. header("Pragma: no-cache");
  1513. header("Expires: 0");
  1514. //导出xls 开始
  1515. if (!empty($title)) {
  1516. foreach ($title as $k => $v) {
  1517. $title[$k] = iconv("UTF-8", "GB2312", $v);
  1518. }
  1519. $title = implode("\t", $title);
  1520. echo "$title\n";
  1521. }
  1522. if (!empty($data)) {
  1523. foreach ($data as $key => $val) {
  1524. foreach ($val as $ck => $cv) {
  1525. $data[$key][$ck] = iconv("UTF-8", "GB2312", $cv);
  1526. }
  1527. $data[$key] = implode("\t", $data[$key]);
  1528. }
  1529. echo implode("\n", $data);
  1530. }
  1531. }
  1532. /**
  1533. * 导出csv文件
  1534. * @param string $name
  1535. * @param array $headers
  1536. * @param array $data
  1537. * @return void
  1538. */
  1539. function exportCsv(string $name, array $headers, array $data = [])
  1540. {
  1541. header('Content-Description: File Transfer');
  1542. header('Content-Type: application/csv');
  1543. header("Content-Disposition: attachment; filename=".$name.".csv");
  1544. header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
  1545. $handle = fopen('php://output', 'w');
  1546. ob_clean();
  1547. fputcsv($handle, $headers);
  1548. if ($data) {
  1549. foreach ($data as $row) {
  1550. fputcsv($handle, $row);
  1551. }
  1552. }
  1553. ob_flush();
  1554. fclose($handle);
  1555. die();
  1556. }
  1557. // 树状分类
  1558. function buildCategoryTree($categories, $pid = 0) {
  1559. $tree = [];
  1560. foreach ($categories as $category) {
  1561. if ($category['pid'] == $pid) {
  1562. $children = buildCategoryTree($categories, $category['category_id']);
  1563. if ($children) {
  1564. $category['children'] = $children;
  1565. }
  1566. $tree[] = $category;
  1567. }
  1568. }
  1569. return $tree;
  1570. }
  1571. // 获取文件的md5值
  1572. function getFileContentMD5($filePath){
  1573. //获取文件MD5的128位二进制数组
  1574. $md5Bytes = md5_file($filePath,true);
  1575. //计算文件的Content-MD5
  1576. $contentMD5 = base64_encode($md5Bytes);
  1577. return $contentMD5;
  1578. }
  1579. function getTextTokens($text) {
  1580. // 方法1:按空格分词(西文较准)
  1581. $words = preg_split('/\s+/', $text);
  1582. $wordCount = count($words);
  1583. // 方法2:按字符数估算(中文较准:1个汉字 ≈ 1.5-2 tokens)
  1584. $charCount = mb_strlen($text);
  1585. // 综合估算(根据语言调整权重)
  1586. $tokenCount = $wordCount + $charCount * 0.5; // 示例公式
  1587. // 更简单:直接按字符数 * 系数(中文推荐)
  1588. // $tokenCount = $charCount * 1.8; // 经验系数
  1589. return (int)ceil($tokenCount);
  1590. }
  1591. // 处理小说剧本文本
  1592. function handleScriptWords($text, $enable_emotion=1) {
  1593. $text = preg_replace('/[\r\n]+/', PHP_EOL, $text);
  1594. $text_arr = explode(PHP_EOL, $text);
  1595. $roles = [];
  1596. $words = [];
  1597. $role_gender = [];
  1598. // $sequence = 0;
  1599. foreach ($text_arr as $line) {
  1600. $line = trim($line);
  1601. if ($enable_emotion) {
  1602. $match_rule = '/^(.*?)\:(.*?)\{(.*?)\}$/';
  1603. $count = 4;
  1604. } else {
  1605. $match_rule = '/^(.*?)\:(.*?)$/';
  1606. $count = 3;
  1607. }
  1608. preg_match($match_rule, $line, $matches);
  1609. if (count($matches) == $count) {
  1610. $gender = '0';
  1611. // 角色部分拆分
  1612. preg_match('/^(.*?)\((.*?)\)$/', $matches[1], $matches2);
  1613. if (count($matches2) == 3) {
  1614. $role = $matches2[1];
  1615. $gender_arr = ['男'=>'1', '女'=>'2'];
  1616. $gender = isset($gender_arr[$matches2[2]]) ? $gender_arr[$matches2[2]] : '0';
  1617. }else {
  1618. $role = $matches[1];
  1619. }
  1620. if (!in_array($role, $roles)) {
  1621. $roles[] = $role; // 记录角色
  1622. $role_gender[$role] = $gender;
  1623. }
  1624. $words[] = [
  1625. 'role' => $role,
  1626. 'gender' => $gender,
  1627. 'text' => $matches[2],
  1628. 'emotion' => $enable_emotion ? $matches[3] : '中性',
  1629. ];
  1630. }
  1631. }
  1632. $new_words = [];
  1633. $tmp = '';
  1634. $tmp_arr = [];
  1635. $tmp_text = '';
  1636. // 将words数组按照role和emotion合并相邻的text内容,不相邻则跳过合并
  1637. foreach ($words as $word) {
  1638. if(!$tmp) $tmp = $word['role'].'-'.$word['emotion'];
  1639. if($tmp == $word['role'].'-'.$word['emotion']) {
  1640. $tmp_text .= PHP_EOL.$word['text'];
  1641. $tmp_arr = [
  1642. 'role' => $word['role'],
  1643. 'gender' => $word['gender'],
  1644. 'text' => trim($tmp_text, PHP_EOL),
  1645. 'emotion' => $word['emotion'],
  1646. ];
  1647. }else {
  1648. // $sequence++;
  1649. // $tmp_arr['sequence'] = $sequence;
  1650. $new_words[] = $tmp_arr;
  1651. $tmp = $word['role'].'-'.$word['emotion'];
  1652. $tmp_text = $word['text'];
  1653. $tmp_arr = [
  1654. 'role' => $word['role'],
  1655. 'gender' => $word['gender'],
  1656. 'text' => trim($tmp_text, PHP_EOL),
  1657. 'emotion' => $word['emotion'],
  1658. ];
  1659. }
  1660. }
  1661. if ($tmp_arr) {
  1662. // $sequence++;
  1663. // $tmp_arr['sequence'] = $sequence;
  1664. $new_words[] = $tmp_arr;
  1665. }
  1666. return [
  1667. 'roles' => $roles,
  1668. 'role_gender' => $role_gender,
  1669. 'words' => $new_words,
  1670. ];
  1671. }