DiverseYouthNightSchool/extend/bw/Common.php

1105 lines
40 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// +----------------------------------------------------------------------
// | Bwsaas
// +----------------------------------------------------------------------
// | Copyright (c) 2015~2020 http://www.buwangyun.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Gitee ( https://gitee.com/buwangyun/bwsaas )
// +----------------------------------------------------------------------
// | Author: buwangyun <hnlg666@163.com>
// +----------------------------------------------------------------------
// | Date: 2020-9-28 10:55:00
// +----------------------------------------------------------------------
namespace bw;
use addons\epay\library\Service;
use app\admin\model\Miniqrcode;
use app\admin\model\Xftts;
use app\common\library\Upload;
use think\Cache;
use think\File;
use app\common\model\Attachment;
use addons\xftts\library\Tts;
use addons\xftts\library\WebSocket\Client;
use addons\xftts\library\WebSocket\Exception;
use traits\CacheTrait;
/** 商城工具类
* Class Common
* @package app\bwmall\model
*/
class Common
{
use CacheTrait;
/**
* 获取图片完整连接
*/
public static function getImagesFullUrl($value = '')
{
if (stripos($value, 'http') === 0 || $value === '' || stripos($value, 'data:image') === 0) {
return $value;
} else {
$upload = \think\Config::get('upload');
if (!empty($upload['cdnurl'])) {
return $upload['cdnurl'] . $value;
} else {
return self::getHttpLocation() . $value;
}
}
}
/**
* 获取当前地址
* @return string
*/
public static function getHttpLocation() {
$http_type = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://';
return $http_type . $_SERVER['HTTP_HOST'];
}
/**
* 时间戳 - 精确到毫秒
* @return float
*/
public static function getMillisecond() {
list($t1, $t2) = explode(' ', microtime());
return (float)sprintf('%.0f',(floatval($t1)+floatval($t2))*1000);
}
/**
* 判断文件是否存在,支持本地及远程文件
* @param String $file 文件路径
* @return Boolean
*/
public static function check_file_exists($file){
// 屏蔽域名不存在等访问问题的警告
error_reporting(E_ALL ^ (E_WARNING|E_NOTICE));
// 远程文件
if(strtolower(substr($file, 0, 4))=='http'){
$header = get_headers($file, true);
return isset($header[0]) && (strpos($header[0], '200') || strpos($header[0], '304'));
// 本地文件
}else{
return file_exists($file);
}
}
/**将目录下的文件保存到框架文件系统
* @param $file_path 原文件全路径(物理=>[绝对|相对]
* @param $file_name 源文件名
* @return \app\common\model\attachment|\think\Model
* @throws \app\common\exception\UploadException
*/
public static function setFastAdminFile($file_path,$file_name){
//保存到第三方文件
//name 原文件名 type 文件类型 tmp_name 原目录 error =0 size 文件大小
$fi = new \finfo(FILEINFO_MIME_TYPE);
$mime_type = $fi->file($file_path);
$temp = [
'name'=> $file_name,
'size'=> filesize($file_path),
'tmp_name'=>$file_path,
'error'=>0,
'type'=>$mime_type
];
$file = (new File($file_path))->isTest(true)->setUploadInfo($temp);
$upload = new Upload($file);
return $upload->upload();
}
public $temp_url = 'uploads/qrcode';//临时,目录
public $path = '';
/**将目录下的文件保存到框架文件系统
* @param $file_path 原文件全路径(物理=>[绝对|相对]
* @param $file_name 源文件名
* @return \app\common\model\attachment|\think\Model
* @throws \app\common\exception\UploadException
*/
public function setFastAdminFileByUrl($url,$fileName){
ob_start();
readfile($url);
$res = ob_get_contents();//文件二进制流
ob_end_clean();
$outfile = $this->temp_url . '/'; //本地缓存地址
if (!file_exists('./' . $outfile . $this->path)) mkdir('./' . $outfile . $this->path, 0777, true);
$filepath = './' . $outfile . $this->path . '/' . $fileName;
file_put_contents($filepath, $res);
//保存到fastadmin框架
$attachment = Common::setFastAdminFile($filepath, $fileName);
// TODO: 生成后删除源文件
@unlink($filepath);
return $attachment;
}
/**
* 字符串命名风格转换
* type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格
* @access public
* @param string $name 字符串
* @param integer $type 转换类型
* @param bool $ucfirst 首字母是否大写(驼峰规则)
* @return string
*/
public static function parseName($name, $type = 0, $ucfirst = true)
{
if ($type) {
$name = preg_replace_callback('/_([a-zA-Z])/', function ($match) {
return strtoupper($match[1]);
}, $name);
return $ucfirst ? ucfirst($name) : lcfirst($name);
}
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
}
public static function toreplace($str, $find)//$str是你需要操作的字符串,$find是你指定的字符串
{
if (strpos($str, $find) === false) return false;
$a = explode($find, $str);
return $a[0] . $find;
}
/**
* 无限级归类
*
* @param array $list 归类的数组
* @param string $id 父级ID
* @param string $pid 父级PID
* @param string $child key
* @param string $root 顶级
*
* @return array
*/
public static function tree(array $list, string $pk = 'id', string $pid = 'pid', string $child = 'child', int $root = 0): array
{
$tree = []; //最终得到的树形数据
if (is_array($list)) {
$refer = [];
//基于数组的指针(引用) 并 同步改变数组
foreach ($list as $key => $val) {
$list[$key][$child] = [];
$refer[$val[$pk]] = &$list[$key]; //以主键为下标,值为列表数据的引用
}
foreach ($list as $key => $val) {
//是否存在parent
$parentId = isset($val[$pid]) ? $val[$pid] : $root; //取出父级id
//如果是根节点,直接放入根层级(实际放入的是一个组装好的树分支)
if ($root == $parentId) {
$tree[$val[$pk]] = &$list[$key];
} else { //如果是其他节点,通过引用传入树分支
if (isset($refer[$parentId])) {
$refer[$parentId][$child][] = &$list[$key]; //1 3 4
}
}
}
// die;
}
//var_dump(array_values($tree));
return array_values($tree);
}
/**得到关系树中的全部用户id递归
* @param array $list tree数据来源
* @param string $name 需要获取的数据名
* @param string $child 树子节点名
* @return array
*/
public static function getTreeItem(array $list, string $name = 'pid', string $child = 'child')
{
$item = [];
//遍历关系
foreach ($list as &$value) {
$item[] = $value[$name];
if ($value[$child]) $item = array_merge($item, self::getTreeItem($value[$child], $name, $child));//合并两个数组
}
return $item;
}
/**
* 排列组合
*
* @param array $input 排列的数组
*
* @return array
*/
static public function arrayArrange(array $input): array
{
$temp = [];
$result = array_shift($input); //挤出数组第一个元素
while ($item = array_shift($input)) //循环每次挤出数组的一个元素,直到数组元素全部挤出
{
$temp = $result;
$result = [];
foreach ($temp as $v) {
foreach ($item as $val) {
$result[] = array_merge_recursive($v, $val);
}
}
}
return $result;
}
/**
* 富文本base64解码
*/
public static function r_text_decode($text)
{
$text = base64_decode($text);
return htmlspecialchars_decode(urldecode($text));
}
/**得到小程序太阳码
* @param $path
* @param string $scene
* @param bool $cache
* @return attachment|array|false|\PDOStatement|string|\think\Model
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
* @throws \app\common\exception\UploadException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public static function getMiniappCode($scene, $page, $width = 430, $auto_color = false, $line_color = ["r" => "0", "g" => "0", "b" => "0"], $is_hyaline = true, $outType = null, $check_path = true, $env_version = 'release',$cache = true){
// 写入到文件
$file_name = md5( $page.$scene.'MiniCode') . '.png';
if($cache){
$file_info = Attachment::where('filename',$file_name)->find();
if($file_info){
$file_info['full_url'] = cdnurl($file_info['url'],true);
return $file_info;
}
}
if (empty($page)) {
$page = 'pages/index/index';
}
// $wechat = new \addons\shopro\library\Wechat('wxMiniProgram');
// $content = $wechat->getApp()->app_code->getUnlimit($scene, [
// 'page' => $path,
// 'is_hyaline' => true,
// ]);
// 实例对应的接口对象
$scheme = new \WeMini\Qrcode(Service::wechatConfig());
$content = $scheme->createMiniScene($scene, $page, $width, $auto_color, $line_color ? ( is_array($line_color) ? $line_color : json_decode($line_color, true)) : ["r" => "0", "g" => "0", "b" => "0"], $is_hyaline, null, $check_path, $env_version);
if ($content instanceof \EasyWeChat\Kernel\Http\StreamResponse || is_string($content)) {
$filePath = ROOT_PATH . 'public/uploads/qrcode/' . $file_name;
if(is_string($content)){
file_put_contents(ROOT_PATH . 'public/uploads/qrcode/'.$file_name, $content);
}else{
$content->saveAs(ROOT_PATH . 'public/uploads/qrcode', $file_name);
}
//保存到fastadmin框架
$attachment = Common::setFastAdminFile($filePath, $file_name);
$attachment['full_url'] = cdnurl($attachment['url'],true);
// TODO: 生成后删除源文件
@unlink($filePath);
return $attachment;
} else {
var_dump(gettype($content));
// 小程序码获取失败
$msg = isset($content['errcode']) ? $content['errcode'] : '-';
$msg .= isset($content['errmsg']) ? $content['errmsg'] : '';
\think\Log::write('wxacode-error' . $msg);
throw new \Exception($msg);
}
}
/**得到小程序二维码(调用次数上限为10万)
* @param $path
* @param bool $cache
* @return attachment|array|false|\PDOStatement|string|\think\Model
* @throws \EasyWeChat\Kernel\Exceptions\InvalidArgumentException
* @throws \EasyWeChat\Kernel\Exceptions\RuntimeException
* @throws \app\common\exception\UploadException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public static function getMiniappQrCode($path,$getContent=false,$cache = true){
// 写入到文件
$file_name = md5( $path.'MiniQrCode') . '.png';
if($cache){
$file_info = Attachment::where('filename',$file_name)->find();
if($file_info){
$file_info['full_url'] = cdnurl($file_info['url'],true);
$file_info['common_content'] = null;
if($getContent){
//得到返解析的内容
$qrcode = new \Zxing\QrReader( $file_info['full_url']); //绝对路径
$file_info['common_content'] = $qrcode->text(); //返回二维码的内容
}
return $file_info;
}
}
if (empty($path)) {
$path = 'pages/index/index';
}
$wechat = new \addons\shopro\library\Wechat('wxMiniProgram');
$content = $wechat->getApp()->app_code->getQrCode($path);
if ($content instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
$filePath = ROOT_PATH . 'public/uploads/qrcode/' . $file_name;
$content->saveAs(ROOT_PATH . 'public/uploads/qrcode', $file_name);
//保存到fastadmin框架
$attachment = Common::setFastAdminFile($filePath, $file_name);
$attachment['full_url'] = cdnurl($attachment['url'],true);
$attachment['common_content'] = null;
if($getContent) {
//得到返解析的内容
$qrcode = new \Zxing\QrReader($attachment['full_url']); //绝对路径
$attachment['common_content'] = $qrcode->text(); //返回二维码的内容
}
// TODO: 生成后删除源文件
@unlink($filePath);
return $attachment;
} else {
// 小程序码获取失败
$msg = isset($content['errcode']) ? $content['errcode'] : '-';
$msg .= isset($content['errmsg']) ? $content['errmsg'] : '';
\think\Log::write('wxacode-error' . $msg);
throw new \Exception($msg);
}
}
/**生成二维码
* @param $params
* @throws \Endroid\QrCode\Exception\InvalidPathException
* @throws \app\common\exception\UploadException
*/
public static function getQrcode($params,$stream = false,$cache = true){
$qrCode = \addons\qrcode\library\Service::qrcode($params);
if($stream)return $qrCode;
// 写入到文件
$file_name = md5(implode('', $params).'Qrcode') . '.png';
if($cache){
$file_info = Attachment::where('filename',$file_name)->find();
if($file_info){
$file_info['full_url'] = cdnurl($file_info['url'],true);
return $file_info;
}
}
$filePath = ROOT_PATH . 'public/uploads/qrcode/' . $file_name;
$qrCode->writeFile($filePath);
//保存到fastadmin框架
$attachment = Common::setFastAdminFile($filePath, $file_name);
// TODO: 生成后删除源文件
@unlink($filePath);
$attachment['full_url'] = cdnurl($attachment['url'],true);
return $attachment;
}
/**生成一维码
* @param $params
* @throws \Endroid\QrCode\Exception\InvalidPathException
* @throws \app\common\exception\UploadException
*/
public static function getBarcode($params,$stream = false,$cache = true){
$qrCode = \addons\barcode\library\Service::barcode($params);
if($stream)return $qrCode;
// 写入到文件
$file_name = md5(implode('', $params).'Barcode') . '.png';
if($cache){
$file_info = Attachment::where('filename',$file_name)->find();
if($file_info){
$file_info['full_url'] = cdnurl($file_info['url'],true);
return $file_info;
}
}
$outfile = 'uploads/barcode/';
if (!file_exists('./' . $outfile )) mkdir('./' . $outfile, 0777, true);
$filePath = './' . $outfile . '/' . $file_name;
file_put_contents($filePath, $qrCode);
//保存到fastadmin框架
$attachment = Common::setFastAdminFile($filePath, $file_name);
// TODO: 生成后删除源文件
@unlink($filePath);
$attachment['full_url'] = cdnurl($attachment['url'],true);
return $attachment;
}
/**执行字符串模板替换
* @param $template
* @param array $params
* @param string $expression
*/
public static function parsePrintTemplateString($template,$params=[],$expression = '{{KEYWORD}}'){
foreach ($params as $name => $value)
{
//得到需要替换的字符串
$replace_name = str_replace("KEYWORD",$name,$expression);
//执行模板字符串替换
$template = str_replace($replace_name,$value,$template);
}
return $template;
}
/**
* 返回多层栏目
* @param $data 操作的数组
* @param int $pid 一级PID的值
* @param string $html 栏目名称前缀
* @param string $fieldPri 唯一键名,如果是表则是表的主键
* @param string $fieldPid 父ID键名
* @param int $level 不需要传参数(执行时调用)
* @return array
*/
static public function channelLevel($data, $pid = 0, $html = "&nbsp;", $fieldPri = 'cid', $fieldPid = 'pid', $level = 1)
{
if (empty($data)) {
return array();
}
$arr = array();
foreach ($data as $v) {
if ($v[$fieldPid] == $pid) {
$arr[$v[$fieldPri]] = $v;
$arr[$v[$fieldPri]]['_level'] = $level;
$arr[$v[$fieldPri]]['_html'] = str_repeat($html, $level - 1);
$arr[$v[$fieldPri]]["_data"] = self::channelLevel($data, $v[$fieldPri], $html, $fieldPri, $fieldPid, $level + 1);
}
}
return $arr;
}
/**
* 获得所有子栏目
* @param $data 栏目数据
* @param int $pid 操作的栏目
* @param string $html 栏目名前字符
* @param string $fieldPri 表主键
* @param string $fieldPid 父id
* @param int $level 等级
* @return array
*/
static public function channelList($data, $pid = 0, $html = "&nbsp;", $fieldPri = 'cid', $fieldPid = 'pid', $level = 1)
{
$data = self::_channelList($data, $pid, $html, $fieldPri, $fieldPid, $level);
if (empty($data))
return $data;
foreach ($data as $n => $m) {
if ($m['_level'] == 1)
continue;
$data[$n]['_first'] = false;
$data[$n]['_end'] = false;
if (!isset($data[$n - 1]) || $data[$n - 1]['_level'] != $m['_level']) {
$data[$n]['_first'] = true;
}
if (isset($data[$n + 1]) && $data[$n]['_level'] > $data[$n + 1]['_level']) {
$data[$n]['_end'] = true;
}
}
//更新key为栏目主键
$category = array();
foreach ($data as $d) {
$category[$d[$fieldPri]] = $d;
}
return $category;
}
//只供channelList方法使用
static private function _channelList($data, $pid = 0, $html = "&nbsp;", $fieldPri = 'cid', $fieldPid = 'pid', $level = 1)
{
if (empty($data))
return array();
$arr = array();
foreach ($data as $v) {
$id = $v[$fieldPri];
if ($v[$fieldPid] == $pid) {
$v['_level'] = $level;
$v['_html'] = str_repeat($html, $level - 1);
array_push($arr, $v);
$tmp = self::_channelList($data, $id, $html, $fieldPri, $fieldPid, $level + 1);
$arr = array_merge($arr, $tmp);
}
}
return $arr;
}
/**
* 获得展示状态的树 数据 例如 ├─ xxx
* @param $data 数据
* @param $title 字段名
* @param string $fieldPri 主键id
* @param string $fieldPid 父id
* @return array
*/
static public function exhibition_tree($data, $title, $fieldPri = 'cid', $fieldPid = 'pid')
{
if (!is_array($data) || empty($data))
return array();
$arr = self::channelList($data, 0, '', $fieldPri, $fieldPid);
foreach ($arr as $k => $v) {
$str = "";
if ($v['_level'] > 2) {
for ($i = 1; $i < $v['_level'] - 1; $i++) {
$str .= "&emsp;│";
}
}
if ($v['_level'] != 1) {
$t = $title ? $v[$title] : "";
if (isset($arr[$k + 1]) && $arr[$k + 1]['_level'] >= $arr[$k]['_level']) {
$arr[$k]['_name'] = $str . "&emsp;├─ " . $v['_html'] . $t;
} else {
$arr[$k]['_name'] = $str . "&emsp;└─ " . $v['_html'] . $t;
}
} else {
$arr[$k]['_name'] = $v[$title];
}
}
//设置主键为$fieldPri
$data = array();
foreach ($arr as $d) {
$data[$d[$fieldPri]] = $d;
}
return $data;
}
/**
* 获得所有父级栏目
* @param $data 栏目数据
* @param $sid 子栏目
* @param string $fieldPri 唯一键名,如果是表则是表的主键
* @param string $fieldPid 父ID键名
* @return array
*/
static public function parentChannel($data, $sid, $fieldPri = 'cid', $fieldPid = 'pid')
{
if (empty($data)) {
return $data;
} else {
$arr = array();
foreach ($data as $v) {
if ($v[$fieldPri] == $sid) {
$arr[] = $v;
$_n = self::parentChannel($data, $v[$fieldPid], $fieldPri, $fieldPid);
if (!empty($_n)) {
$arr = array_merge($arr, $_n);
}
}
}
return $arr;
}
}
/**
* 判断$s_cid是否是$d_cid的子栏目
* @param $data 栏目数据
* @param $sid 子栏目id
* @param $pid 父栏目id
* @param string $fieldPri 主键
* @param string $fieldPid 父id字段
* @return bool
*/
static function isChild($data, $sid, $pid, $fieldPri = 'cid', $fieldPid = 'pid')
{
$_data = self::channelList($data, $pid, '', $fieldPri, $fieldPid);
foreach ($_data as $c) {
//目标栏目为源栏目的子栏目
if ($c[$fieldPri] == $sid)
return true;
}
return false;
}
/**
* 检测是不否有子栏目
* @param $data 栏目数据
* @param $cid 要判断的栏目cid
* @param string $fieldPid 父id表字段名
* @return bool
*/
static function hasChild($data, $cid, $fieldPid = 'pid')
{
foreach ($data as $d) {
if ($d[$fieldPid] == $cid) return true;
}
return false;
}
/**
* 递归实现迪卡尔乘积
* @param $arr 操作的数组
* @param array $tmp
* @return array
*/
static function descarte($arr, $tmp = array())
{
static $n_arr = array();
foreach (array_shift($arr) as $v) {
$tmp[] = $v;
if ($arr) {
self::descarte($arr, $tmp);
} else {
$n_arr[] = $tmp;
}
array_pop($tmp);
}
return $n_arr;
}
/**得到可复用的小程序二维码
* @param $link_id
* @param bool $cache
* @return Miniqrcode|array|false|\PDOStatement|string|\think\Model
* @throws \Exception
*/
public static function getLinkMiniQrCode($link_id,$mini_path=null,$type = 'mock',$cache = true){
try{
$miniqrcode = Miniqrcode::where("link_id",$link_id)->where("type",$type)->where("value","not null")->find();
if($miniqrcode && $miniqrcode['value']){
$miniqrcode['status'] = '2';
$miniqrcode->save();
return $miniqrcode;
}
//判断是否有空余小程序二维码
$miniqrcode = Miniqrcode::where("status","1")->where("value","not null")->where("value","<>","")->find();
//如果有,则更新二维码为使用中,保存新的连接信息并返回
if($miniqrcode && $miniqrcode['value']){
$miniqrcode['status'] = '2';
$miniqrcode['link_id'] = $link_id;
$miniqrcode['type'] = $type;
$miniqrcode->save();
return Miniqrcode::where('id',$miniqrcode['id'])->find();
}else{
//如果没有,则
$count = Miniqrcode::count();
$miniqrcode_limit = config("site.miniqrcode_limit");//练车小程序码url
$mock_minicode_url = $mini_path ?: config("site.mock_minicode_url");//练车码生成上限数量
//1,判断是否超出使用数量,如果超出则抛异常
if($count >= $miniqrcode_limit)throw new \Exception("已超出能生成的小程序二维码上限!");
//如果未超出
//1插入小程序二维码表
$miniqrcode = new Miniqrcode;
$miniqrcode->link_id = $link_id;
$miniqrcode['type'] = $type;
$miniqrcode['status'] = '2';
$miniqrcode->save();
// 获取自增ID
$miniqrcode_id = $miniqrcode->id;
//拼接二维码内容
$path = "{$mock_minicode_url}?link_id={$miniqrcode_id}";
//2生成小程序二维码并逆解析内容:找到能解析出内容的二维码
while(true)
{
$attachment = Common::getMiniappQrCode($path,true,$cache);
$image = $attachment['url'];
$value = $attachment['common_content'];
//解析不出二维码,存入码表,循环下一张
if(!$value){
//原码更新成失败码
$error_miniqrcode = Miniqrcode::where("id",$miniqrcode_id)->find();
$error_miniqrcode['link_id'] = 0;
$error_miniqrcode['status'] = '1';
$error_miniqrcode['type'] = 'other';
$error_miniqrcode['path'] = $path;
$error_miniqrcode['image'] = $image;
$error_miniqrcode['value'] = $value;
$error_miniqrcode->save();
//1插入新的小程序二维码
$new_miniqrcode = new Miniqrcode;
$new_miniqrcode->link_id = $link_id;
$new_miniqrcode['type'] = $type;
$new_miniqrcode['status'] = '2';
$new_miniqrcode->save();
//赋值新自增ID
$miniqrcode_id = $new_miniqrcode->id;
//拼接新二维码内容
$path = "{$mock_minicode_url}?link_id={$miniqrcode_id}";
//开始新一轮循环,直到获取到能解析的二维码为止
}else{
//解析得出二维码直接返回
//3返回表信息
$miniqrcode = Miniqrcode::where("id",$miniqrcode_id)->find();
$miniqrcode['path'] = $path;
$miniqrcode['image'] = $image;
$miniqrcode['value'] = $value;
$miniqrcode->save();
break;
}
}
return $miniqrcode;
}
}catch (\Throwable $e){
throw new \Exception($e->getMessage());
}
}
/** 释放被占用的小程序二维码
* @param $link_id
* @param string $type
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public static function freeLinkMiniQrCode($link_id,$type = 'mock'){
$miniqrcode = Miniqrcode::where("link_id",$link_id)->where("type",$type)->find();
if(!$miniqrcode)return true;
$miniqrcode['status'] = '1';
$miniqrcode['link_id'] = 0;
$miniqrcode['type'] = 'other';
$miniqrcode->save();
return true;
}
/**射线与边是否有交点
* @param $poi
* @param $sPoi
* @param $ePoi
*/
public static function isRayIntersectsSegment($poi, $sPoi, $ePoi){
// 排除 与射线平行、重合、是一个点的情况
if($sPoi[0] == $ePoi[0])return false;
//# 排除 线段在射线上边
if($sPoi[0] > $poi[0] && $ePoi[0] > $poi[0])return false;
// # 排除 线段在射线下边
if($sPoi[0] < $poi[0] && $ePoi[0] < $poi[0])return false;
// # 排除 交点为下端点
if($sPoi[0] == $poi[0] && $ePoi[0] > $poi[0])return false;
// # 排除 交点为上端点
if($ePoi[0] == $poi[0] && $sPoi[0] > $poi[0])return false;
// # 排除 线段在射线左边
if($sPoi[1] < $poi[1] && $ePoi[1] < $poi[1])return false;
// # 求交,相似多边形性质
//规避除零异常
$meddle_v = ($sPoi[0] - $ePoi[0]) * ($sPoi[1] - $ePoi[1]);
if($meddle_v == 1)return false;
$xseg = $ePoi[1] + ($poi[0] - $ePoi[0]) / ($sPoi[0] - $ePoi[0]) * ($sPoi[1] - $ePoi[1]);
// # 交点在射线起点的左侧
if($xseg < $poi[1])return false;
// # 排除上述情况之后真正有效的点
return true;
}
/**地理围栏算法实现-单围栏(射线法:多变遍历)
* @param $poi
* @param $poly
* @return bool
*/
public static function poiInPoly($poi, $poly,$count_check=true){
// # 交点个数
$count = 0 ;
// # 逐个二维数组进行判断
$last_index = count($poly)-1; //最后一个数组下标
foreach ($poly as $index=>$epoly)
{
//最后一个节点
if($last_index == $index){
$e_poi = $poly[$index];
$s_poi = $poly[0];
}else{
//正常节点
$e_poi = $poly[$index];
$s_poi = $poly[$index + 1];
}
//刚好压在点上
if($poi[0] == $e_poi[0] && $poi[1] == $e_poi[1])return true;
if($poi[0] == $s_poi[0] && $poi[1] == $s_poi[1])return true;
if(self::isRayIntersectsSegment($poi, $e_poi, $s_poi))$count += 1;
}
if($count_check)return $count % 2 != 0;
return $count;
}
/**地理围栏算法实现-多围栏(射线法:多变遍历)
* @param $poi
* @param $polys
*/
public static function poiInPolyArray($poi, $polys){
// # 交点个数
//记录下包含围栏数
$contain = [];
foreach ($polys as $poly)
{
if(is_string($poly))$poly = json_decode($poly, true);
if(self::poiInPoly($poi, $poly))$contain[] = $poly;
}
return $contain;
}
/**
* 得到时间标识当天6点前算当天
*/
public static function getTimeflag($change=false,$tomorrow_s=""){
//得到当前时间
$time = time();
if($tomorrow_s){
$tomorrow_string = $tomorrow_s;
}else{
$tomorrow_string = config("site.queue_tomorrow_string") ?: "6:0:0";
}
//得到当前六点的时间
$format_six_time = date("Y-m-d {$tomorrow_string}",$time);
//得到当前六点的时间戳
$time_six = strtotime($format_six_time);
//如果小于六点取前一天的六点
if($time < $time_six){
$time_six = strtotime("{$format_six_time} -1 day");
} //如果大于六点取今天的六点的时间戳
//如果是取范围则返回当天时间范围
if($change){
$format_time_end = date("Y-m-d H:i:s",$time_six);
$time_end = strtotime("{$format_time_end} +1 day") - 1; //59分秒时
return ['time_start'=>$time_six,'time_end'=>$time_end];
}
//不是取范围则直接返回
return $time_six;
}
/**
* 下划线转驼峰
* 思路:
* step1.原字符串转小写,原字符串中的分隔符用空格替换,在字符串开头加上分隔符
* step2.将字符串中每个单词的首字母转换为大写,再去空格,去字符串首部附加的分隔符.
*/
public static function camelize($uncamelized_words,$separator='_')
{
$uncamelized_words = $separator. str_replace($separator, " ", strtolower($uncamelized_words));
return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator );
}
/**
* 驼峰命名转下划线命名
* 思路:
* 小写和大写紧挨一起的地方,加上分隔符,然后全部转小写
*/
public static function uncamelize($camelCaps,$separator='_')
{
return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
}
/**讯飞语音合成
* @param $params
* @return array
*/
public static function xftts($params)
{
if (!isset($params['text']) || empty($params['text'])) {
throw new\Exception('请输入合成文字');
}
$config = get_addon_config('xftts');
$tts = new Tts();
$url = $tts->createAuthUrl($config['APIKey'], $config['APISecret'], time());
$client = new Client($url);
$message = $tts->createMsg(
$config['APPID'],
$params['text']."",
isset($params['ent']) ? $params['ent'] : $config['ent'],
isset($params['aue']) ? $params['aue'] : $config['aue'],
isset($params['auf']) ? $params['auf'] : $config['auf'],
isset($params['vcn']) ? $params['vcn'] : $config['vcn'],
isset($params['speed']) ? $params['speed'] : $config['speed'],
isset($params['volume']) ? $params['volume'] : $config['volume'],
isset($params['pitch']) ? $params['pitch'] : $config['pitch'],
isset($params['bgs']) ? $params['bgs'] : $config['bgs'],
isset($params['tte']) ? $params['tte'] : $config['tte'],
isset($params['reg']) ? $params['reg'] : $config['reg'],
isset($params['ram']) ? $params['ram'] : $config['ram'],
isset($params['rdn']) ? $params['rdn'] : $config['rdn']
);
$params = array_merge($config,$params);
try {
$client->send(json_encode($message, true));
$date = date('YmdHis', time());
$file_name = $date . ($params['aue'] === 'raw' ? '.pcm' : '.mp3');
$folder = '/uploads/tts/';
//判断文件夹是否存在
if (!is_dir(ROOT_PATH . 'public' . $folder)) {
@mkdir(ROOT_PATH . 'public' . $folder);
}
$audio_file = fopen(ROOT_PATH . 'public' . $folder . $file_name, 'ab');
$response = $client->receive();
$response = json_decode($response, true);
do {
if ($response['code']) {
throw new \Exception($response['msg']);
}
//返回的音频需要进行base64解码
$audio = base64_decode($response['data']['audio']);
fwrite($audio_file, $audio);
// 第一次消息就收到结束标志的情况
if ($response['data']['status'] == 2) {
fclose($audio_file);
$client->close();
$url = $folder . $file_name;
$filesize = filesize(ROOT_PATH . 'public' . $url);
//保存语音数据
$params['speech_url'] = $url;
$params['filesize'] = $filesize;
$params['admin_id'] = 1;
$result = (new Xftts())->allowField(true)->save($params);
return [
'code' => 0,
'msg' => '合成成功',
'data' => [
'url' => $url,
'full_url' => cdnurl($url,true),
'filesize' => $filesize,
'object' => Xftts::where('speech_url',$url)->find(),
]
];
}
//继续接收消息
$response = $client->receive();
$response = json_decode($response, true);
} while ($response['data']['status'] != 2);
fclose($audio_file);
$url = $folder . $file_name;
$filesize = filesize(ROOT_PATH . 'public' . $url);
//保存语音数据
$params['speech_url'] = $url;
$params['filesize'] = $filesize;
$params['admin_id'] = 1;
$result = (new Xftts())->allowField(true)->save($params);
return [
'code' => 0,
'msg' => '合成成功',
'data' => [
'url' => $url,
'full_url' => cdnurl($url,true),
'filesize' => $filesize,
'object' => Xftts::where('speech_url',$url)->find(),
]
];
} catch (Exception $e) {
throw new \Exception($e->getMessage(),1);
// return [
// 'code' => 1,
// 'msg' => $e->getMessage(),
// ];
} finally {
$client->close();
}
}
/**得到音频信息 composer require james-heinrich/getid3
* @param $localRelativePath
* @return array
* @throws \getid3_exception
*/
public static function getAudioInfo($fullPath){
$remotefilename = $fullPath;
if ($fp_remote = fopen($remotefilename, 'rb')) {
$localtempfilename = tempnam('/tmp', 'getID3');
if ($fp_local = fopen($localtempfilename, 'wb')) {
while ($buffer = fread($fp_remote, 8192)) {
fwrite($fp_local, $buffer);
}
fclose($fp_local);
// 初始化getID3引擎
$getID3 = new \getID3;
$ThisFileInfo = $getID3->analyze($localtempfilename);
// 删除临时文件
unlink($localtempfilename);
}
fclose($fp_remote);
}
return $ThisFileInfo;
}
/**
* 得到语音播报内容(讯飞语音)
*/
public static function getAudioContent($template_content,$params=[],$expression = '{{KEYWORD}}'){
//解析获取文本内容
$content = self::parsePrintTemplateString($template_content,$params,$expression);
//判断是否存在当前文件
$xftts = Xftts::where("text",$content)->find();
//如果存在,则继续往下走
if(!$xftts){
//如果不存在,则生成文件继续往下走
$audio_info = self::xftts(['text'=> $content]);
$xftts = $audio_info['data']['object'];
}
$tts_key = "xftts_{$content}";
//判断是否存在当前播放秒数
$content_seconds = Cache::get($tts_key);
if(!$content_seconds){
//不存在当前秒数则解析当前秒数并存入缓存
$audio_info = self::getAudioInfo(cdnurl($xftts['speech_url'],true));
// var_dump($audio_info['playtime_seconds']);die;
Cache::set($tts_key,$audio_info['playtime_seconds'],3600);
$content_seconds = $audio_info['playtime_seconds'];
}
$xftts['content_seconds'] = $content_seconds;
return $xftts;
}
}