1091 lines
39 KiB
PHP
1091 lines
39 KiB
PHP
|
<?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 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($path,$scene='',$cache = true){
|
|||
|
// 写入到文件
|
|||
|
$file_name = md5( $path.$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($path)) {
|
|||
|
$path = 'pages/index/index';
|
|||
|
}
|
|||
|
$wechat = new \addons\shopro\library\Wechat('wxMiniProgram');
|
|||
|
$content = $wechat->getApp()->app_code->getUnlimit($scene, [
|
|||
|
'page' => $path,
|
|||
|
'is_hyaline' => true,
|
|||
|
]);
|
|||
|
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);
|
|||
|
// 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);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**得到小程序二维码(调用次数上限为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 = " ", $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 = " ", $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 = " ", $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 .= " │";
|
|||
|
}
|
|||
|
}
|
|||
|
if ($v['_level'] != 1) {
|
|||
|
$t = $title ? $v[$title] : "";
|
|||
|
if (isset($arr[$k + 1]) && $arr[$k + 1]['_level'] >= $arr[$k]['_level']) {
|
|||
|
$arr[$k]['_name'] = $str . " ├─ " . $v['_html'] . $t;
|
|||
|
} else {
|
|||
|
$arr[$k]['_name'] = $str . " └─ " . $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;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|