getHook();
parent::initialize();
}
public static function getHook()
{
//需要使用静态方法Facade 不然会报错
\think\Facade\Hook::listen('response_send');
}
/*
* 业务控制器
* 上传csv关键词数据
* https://console.zx2049.com/api/ai/uploadCsv?file=20240504/eebcb4ca6a8a69fdd9e0f60c16b55746.csv
*/
public function uploadCsv(){
//测试
$url = request()->get('file',0);
if($url !== 0){
$this->csvHandle('./uploads/'.$url);
return json([]);
}
$file = request()->file('file'); //$file = $_FILES['file'];
$info = $file->move( './uploads');
if($info){
// 成功上传后 获取上传信息
// 输出 jpg
//$url = $info->getExtension();
// 输出 20160820/42a79759f284b767dfcb2a0197904287.jpg
$url = $info->getSaveName();
// 输出 42a79759f284b767dfcb2a0197904287.jpg
//echo $info->getFilename();
$data = ['code' => 0, 'msg' => '文件上传成功', 'data' =>['src'=>$url]];
}else{
// 上传失败获取错误信息
$info = $file->getError();
$data = ['code' => 1, 'msg' => '文件上传失败', 'data' =>$info];
}
return json($data);
}
/*
* 业务控制器
* 处理表单
*/
public function uploadCsvFrom(){
$post = request()->post();
/*
* rule:
0
title:
建筑职称
file:
url:
20240505/2aff80b14c861480183278bbc9442f56.csv
*/
//判断关键词是否存在
$one = Db::name('options')->where('title','=',$post['title'])->where('status','=',1)->find();
if(empty($one)){
$data = ['code' => 1, 'msg' => '关键词不存在', 'data' => []];
return json($data);
}
if(!empty($post['url'])){
$this->csvHandle('./uploads/'.$post['url'],$post['rule'],$post['title']);
$data = ['code' => 0, 'msg' => '提交成功', 'data' =>$post];
}else{
$data = ['code' => 1, 'msg' => '提交失败', 'data' => []];
}
return json($data);
}
/*
* 逻辑控制器
* 规则的指令集和创意 如果为空 调用全局的即可 同样优先调用自身规则
*/
public function csvHandle($url,$rule,$title){
// 读取CSV文件
$filename = $url;
if (file_exists($filename)) {
$file = fopen($filename, 'r');
//任务批次
$task_no = date('YmdHis',time()).'-'.rand(1,10000);
//插入 重复也无所谓了
Db::name('pc')->insert(['catid'=>31,'title'=>$task_no,'create_time' => time(),'update_time' => time()]);
$pid = time(); //不考虑并发提交多次间隔也是秒级
$num = 1;
$log_content = '';
$log_content_error = '';
$s_num = 0;
$f_num = 0;
while (($line = fgetcsv($file)) !== FALSE) {
// $line是一个数组,包含CSV的一行数据
//print_r($line);
if($num === 1){
$row = $line;
}else{
if(!empty($line[0])) {
//关键词步骤省略 需要去站长之家搜索并导出 这个步骤由人工操作
//判断是否存在短标题 重复性检测
//相同规则 短标题只能存在一个
$one = Db::name('cms_zl')
->where('title', '=', $line[0])
->where('catid', '=', 18) //分类id
->where('zc', '=', $title) //主词
->where('gzxh', '=', $rule) //规则id
->where('sj','=',$task_no) //时间维度 批次
->find();
if (empty($one)) {
//过滤标题是否合法的函数 这个可以用定时任务处理
[$is_use, $desc, $rule_id] = $this->titleHandle($line[0], $rule, $title);
//构建详情
$html = '';
for ($i = 1; $i < 10; $i++) {
$html .= $row[$i] . ':' . $line[$i] . '
';
}
$data = [
'catid' => 18, //短标题
'title' => $line[0], //短标题
'content' => $html,
'zc' => $title, //增加主词字段
'create_time' => time(),
'update_time' => time(),
'status' => 0, //默认禁用状态 定时手动开启 自动化再读取即可
'pid' => $pid, //关联日志的aid,可查询到规则序号 主词 文件地址 执行情况
'ptlx' => 0,
'is_use' => $is_use, //是否合法
'desc' => $desc, //非法原因 或者 合法匹配结果
'gzxh' => $rule,
'sj' => $task_no, //写入时间维度 批次
'is_use_id' => json_encode($rule_id,JSON_UNESCAPED_UNICODE), //写入具体规则id 可查询到对应规则是什么 判断是否合法
];
//dump($data);
//插入记录
$doc_id = Db::name('cms_zl')->insertGetId($data);
//写入日志
$log_content .= '指令id-' . $doc_id . '-' . json_encode($line, JSON_UNESCAPED_UNICODE) . '
';
$s_num++;
//通知前端
$is_use_desc = $is_use === 1?'合法':'不匹配';
$msg = [
'info' => '['.$line[0].']成功新增记录!匹配结果:'.$is_use_desc
];
$this->curlPost($msg);
} else {
//写日志
$log_content_error .= '
短标题-' . $line[0] . '-已存在-指令短标题id:' . $one['id'] . '
'; $f_num++; //通知前端 $msg = [ 'info' => '相同规则与相同主词下,['.$line[0].']已存在-不可重复导入!' ]; $this->curlPost($msg); } } } $num++; } //每次操作 1-2条 日志记录 if(!empty($log_content)){ Db::name('log')->insert([ 'catid' => 27, 'title' => '批次'.$task_no.'成功导入处理-'.$s_num.'条数据', 'keywords' => $title, //主词 'description' => $filename,//对应文件地址 'create_time' => time(), 'update_time' => time(), 'status' => 1, 'site_id'=> 1, 'cid' => (int)$rule, //规则序号 'aid' => $pid, //对应记录id 'content' => ''.$log_content.'
', ]); } if(!empty($log_content_error)){ Db::name('log')->insert([ 'catid' => 27, 'title' => '批次'.$task_no.'导入处理已存在-'.$f_num.'条数据', 'keywords' => $title, //主词 'description' => $filename,//对应文件地址 'create_time' => time(), 'update_time' => time(), 'status' => 1, 'site_id'=> 1, 'cid' => (int)$rule, //规则序号 'aid' => $pid, //对应记录id 'content' => ''.$log_content_error.'
', ]); } fclose($file); } /*// 写入CSV文件 $data = [ ['Name', 'Age', 'Email'], ['Alice', 25, 'alice@example.com'], ['Bob', 30, 'bob@example.com'] ]; $filename = 'output.csv'; $file = fopen($filename, 'w'); foreach ($data as $row) { fputcsv($file, $row); } fclose($file);*/ } /* * 模型控制器 操作数据 */ /* * http://console.zx2049.com/api/ai/titleHandleApi * 调试 */ public function titleHandleApi(){ $t = $this->titleHandle('建筑职称评审',0,'建筑职称'); var_dump($t); } /* * 处理函数 * $title 主词 */ public function titleHandle($line,$rule,$title){ //规则1 $desc = ''; //是否完全匹配主词 if (strpos($line, $title) !== false) { //包含主词 }else{ return [0,'短标题不包含主词['.$title.']',0]; } //1.查询是否存在主词规则 $one = Db::name('cms_zl') ->where('catid','=',17) ->where('title','=',$title) //只能一条 已做标题重复校验 ->where('is_qj','=',0) //非全局 ->where('zllx','=',1) //指令类型是 主词 ->where('gzxh','=',$rule) ->where('status','=',1) //多条需要控制开关 ->order('id asc') //可能添加多条 以第一条为主 理论每个规则只添加一条 ->find(); //2.查询全局主词规则 if(empty($one)){ $one = Db::name('cms_zl') ->where('catid','=',17) ->where('title','=','全局-主词类型') //只能一条 已做标题重复校验 ->where('is_qj','=',1) //是全局 ->where('zllx','=',1) //指令类型是 主词 ->where('gzxh','=',$rule) ->where('status','=',1) //多条需要控制开关 ->order('id asc') //可能添加多条 以第一条为主 理论每个规则只添加一条 ->find(); } //3.查询全局规则 if(empty($one)){ $one = Db::name('cms_zl') ->where('catid','=',17) ->where('title','=','全局-非主非副') //只能一条 已做标题重复校验 ->where('is_qj','=',1) //是全局 ->where('zllx','=',0) //指令类型是 非主非副 ->where('gzxh','=',$rule) ->where('status','=',1) //多条需要控制开关 ->order('id asc') //可能添加多条 以第一条为主 理论每个规则只添加一条 ->find(); } if(empty($one)){ return [0,'不存在全局通用规则,全局主词规则,当前主词规则',0]; } //4.获取到全局或主词的副词 $two = []; if(empty($one['fclb']) || (string)$one['fclb'] === '0'){ //不存在副词规则 快照 表示这条规则 没有副词匹配要求 //即使标题里面存在 主词设置的副词列表 //即使存在某些副词规则 不存在副词规则 确定不了哪个是副词 故二无法匹配 $desc .= '不存在副词规则-|-'; }else{ //存在副词规则 $one['fclb'] = $one['fclb'].','; $fclb = explode(',',$one['fclb']); $line_sor = str_replace($title, "", $line); //短标题去除主词 $is_bh = 0; $fc = ''; $fc_num = 0; foreach ($fclb as $k => $v) { //该判断 主词和副词 不一定是连在一起的 if(!empty($v)) { if (strpos($line_sor, $v) !== false) { //包含 $is_bh = 1; $fc = $v; //break; //跳出 目前只匹配一个 存在多个不做处理 以第一个为主 $fc_num++; } } } //如果副词快照存在,那么以副词快照里面找不到,那么再去找主词设置是否存在 //这样判断是不真实的 因为主词设置是会修改的 if($is_bh === 0){ //$fc 无法确认值 所以无法匹配副词规则 //当主词规则 都没有填写副词列表 就表示不匹配副词了 } //存在多个副词 不合理 if($fc_num > 1){ return [0,'存在多个副词,不合法',$one['id']]; } //5.判断是否存在副词 if($is_bh === 1){ //6.查询主词+副词是否存在规则 $two = Db::name('cms_zl') ->where('catid','=',17) ->where('title','=',$title.$fc) //查询设置 按照主词+副词 格式查询 //只能一条 已做标题重复校验 ->where('is_qj','=',0) //非全局 ->where('zllx','=',2) //指令类型是 副词 ->where('gzxh','=',$rule) ->where('status','=',1) //多条需要控制开关 ->order('id asc') //可能添加多条 以第一条为主 理论每个规则只添加一条 ->find(); //7.查询副词通用全局规则 if(empty($two)){ $two = Db::name('cms_zl') ->where('catid','=',17) ->where('title','=','全局-副词类型') //查询设置 按照主词+副词 格式查询 //只能一条 已做标题重复校验 ->where('is_qj','=',1) //非全局 ->where('zllx','=',2) //指令类型是 副词 ->where('gzxh','=',$rule) ->where('status','=',1) //多条需要控制开关 ->order('id asc') //可能添加多条 以第一条为主 理论每个规则只添加一条 ->find(); } }else{ //短标题不包含副词 $desc .= '短标题不包含副词-|-'; } } //短标题的主词和副词 不一定是连在一起的 if(!empty($two)){ //存在副词规则 按照此规则执行 $data = $two; }else{ //按照$one规则记录执行 $data = $one; } //8.判断是否合法 地区词 [$is_bh,$fc] = $this->isInRule($data['dqc'],$line); if($is_bh > 0){ $desc .= '存在地区词'.$fc.',不合法-|-'; return [0,$desc,$data]; } //9.判断是否合法 分类词 [$is_bh,$fc] = $this->isInRule($data['flc'],$line); if($is_bh > 0){ $desc .= '存在分类词'.$fc.',不合法-|-'; return [0,$desc,$data]; } //10.判断是否合法 时间词 [$is_bh,$fc] = $this->isInRule($data['sjc'],$line); if($is_bh > 0){ $desc .= '存在时间词'.$fc.',不合法-|-'; return [0,$desc,$data]; } //11.判断是否合法 屏蔽词 [$is_bh,$fc] = $this->isInRule($data['pbc'],$line); if($is_bh > 0){ $desc .= '存在屏蔽词'.$fc.',不合法-|-'; return [0,$desc,$data]; } //12.替换指令的变量 //var_dump($data['zlj']);die; if(!empty($data['zlj'])){ $zlj = json_decode($data['zlj'],true); $zlj_new = []; foreach ($zlj as $k => $v) { $key = ''.(string)$k.''; if (strpos($v,'K') !== false) { $v_str = str_replace("K", $line, $v); $zlj_new[$key] = $v_str; }else{ $zlj_new[$key] = $v; } } $zlj_new_json = json_encode($zlj_new,JSON_UNESCAPED_UNICODE); $data['zlj'] = $zlj_new_json; //var_dump($zlj_new_json);die; } //13.判断是否合法 同时存在触发词和并发词 //增加判断可以不存在并发词和触发词 //只需要匹配 包含主词即可通过 if(!empty($data['cfc']) && (string)$data['cfc'] != '0' || !empty($data['bfc']) && (string)$data['bfc'] != '0'){ [$is_bh1,$fc1] = $this->isInRule($data['cfc'],$line); [$is_bh2,$fc2] = $this->isInRule($data['bfc'],$line); }else{ $desc .= '不需要验证并发词和触发词,合法'; return [1,$desc,$data]; } //var_dump($data['cfc']); //var_dump($is_bh1); //var_dump($is_bh2); //die; if($is_bh1 > 0 && $is_bh2 > 0){ //同时存在 $desc .= '合法,同时存在并发词['.$fc2.']和触发词['.$fc1.']'; return [1,$desc,$data]; }else if($is_bh1 > 0 || $is_bh2 > 0){ $desc .= '合法,不同时存在并发词['.$fc2.']和触发词['.$fc1.']'; return [1,$desc,$data]; }else{ $desc .= '不合法,同时不存在并发词['.$fc2.']和触发词['.$fc1.']'; return [0,$desc,$data]; } } /* * 规则设定 */ public function setRule(){ //方案1: 定义config配置文件 //方案2: 直接代码写死 //方案3: 后台设置 -- 采用这个 $json = [ '' ]; } /* * 函数 是否包含 */ public function isInRule($data,$str){ $data = $data.','; $data = explode(',',$data); $is_bh = 0; $fc = ''; foreach ($data as $k => $v) { //该判断 主词和副词 不一定是连在一起的 if(!empty($v)) { if (strpos($str, $v) !== false) { //包含 $is_bh = 1; $fc = $v; break; //跳出 目前只匹配一个 存在多个不做处理 以第一个为主 } } } return [$is_bh,$fc]; } /* * 导出csv * http://console.zx2049.com/api/ai/exportCsv */ public function exportCsv(){ $list = Db::name('cms_zl')->field('id,title,desc')->where('catid','=',18)->select(); /* $data = [ ['Name', 'Age', 'Email'], ['Alice', 25, 'alice@example.com'], ['Bob', 30, 'bob@example.com'] ];*/ $filename = 'output.csv'; $file = fopen($filename, 'w'); foreach ($list as $row) { fputcsv($file, $row); } fclose($file); //执行下载 } /* * curl - post 通知 */ public function curlPost($data){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://workerman.dev.zx2049.com/work/push/test"); curl_setopt($ch, CURLOPT_POST, 1); //The number of seconds to wait while trying to connect. Use 0 to wait indefinitely. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60); curl_setopt($ch, CURLOPT_POSTFIELDS , http_build_query($data)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($ch); curl_close($ch); return $output; } /* * 定时自动通过合法的短标题 * http://console.zx2049.com/api/ai/dsTitle */ public function dsTitle() { $id_list = Db::name('cms_zl')->where([ 'catid'=>18, 'is_use' => 1, 'status' => 0 ])->limit(10)->column('id'); dump($id_list); Db::name('cms_zl')->where('id','in',$id_list)->update([ 'status' => 1 ]); } /* * 定时同步通过审核的短标题到自动发文 * http://console.zx2049.com/api/ai/dsTitlelist * 必须判断唯一 如果标题一样 就不写入了 */ public function dsTitlelist() { //得到最后时间 $row = Db::name('cms_ai')->where([ 'catid' => 13 ]) ->order('tb_time desc') ->limit(1) ->find(); //var_dump($row); if(isset($row['tb_time'])){ $lasttime = $row['tb_time']; }else{ $lasttime = 0; } $list = Db::name('cms_zl')->where([ 'catid'=>18, 'is_use' => 1, //合法 'status' => 1, 'is_cf' => 0, //是否重复 //'id'=>1199 ]) ->field('id,title,create_time,is_use_id') ->where('create_time','>',$lasttime) //->limit(10) //不能限制数量 不然时间查询 下次会漏掉某些时间相同的剩余数量 //要么使用状态字段 //要么使用自增id判断 ->order('create_time asc') //必须asc排序 先入库 ->select(); //dump($list);die; foreach ($list as $index=>$row) { $one = Db::name('cms_ai')->field('id,title')->where(['catid' => 13])->where('title','=',$row['title'])->find(); if(empty($one)){ //$json = strip_tags(htmlspecialchars_decode($row['is_use_id'])); //echo $json; $json = $row['is_use_id']; $json = json_decode($json,true); //dump($json);die; //读取指令集 $zlj = $json['zlj']; $zlj_arr = json_decode($zlj,true); $zlj_end = end($zlj_arr); $key = count($zlj_arr)-1; unset($zlj_arr[$key]); $zlj = json_encode($zlj_arr,JSON_UNESCAPED_UNICODE); //获取创意 if((string)$json['is_sj'] === '-1'){ //不需要加创意 $cy = '0'; }else{ if((int)$json['is_sj'] > 0){ //既然指定 不管管是否为空 $cy = $json['cy'.$json['is_sj']]; }else{ //随机 $sj_num = []; for($i=1;$i<=6;$i++){ if(!empty($json['cy'.$i])){ $sj_num[] = $i; } } if(!empty($sj_num)){ $randomKey = array_rand($sj_num); $num = $sj_num[$randomKey]; $cy = $json['cy'.$num]; }else{ $cy = '0'; } } } //不存在于ai中 $data = [ 'catid' => 13, 'title' => $row['title'], 'content' => '0', 'topic_1' => '0', 'topic_2' => '0', 'topic_3' => '0', 'didian' => '0', 'dssj' => '0', 'fbsj' => '0', 'tb_time' => $row['create_time'], 'create_time' => time(), 'update_time' => time(), 'status' => 0, //默认手动开启 'cy' => $cy, //创意 'zlj' => $zlj, //指令集 json字符串 'tpzl' => $zlj_end, 'sjcy' => $json['is_sj'], //随机创意 'lyjlid' => $row['id'], //来源id 'sblx' => 3,//设别类型 电脑A 'is_sc' => 0, //是否生成文章 'pic_html' => '0', 'imgs' => '{}' ]; $run = Db::name('cms_ai')->insert($data); Db::name('cms_zl')->where('id',$row['id'])->update(['is_cf'=>2]); //已检测 同步 echo $run; }else{ //更新为重复记录 Db::name('cms_zl')->where('id',$row['id'])->update(['is_cf'=>1]); echo $row['title']."已存在,更新为重复记录"; } } } /* * 定时自动通过合法的发文标题 * http://console.zx2049.com/api/ai/dsTitle13 */ public function dsTitle13() { $id_list = Db::name('cms_ai')->where([ 'catid'=>13, 'status' => 0 ])->limit(10)->column('id'); dump($id_list); Db::name('cms_ai')->where('id','in',$id_list)->update([ 'status' => 1 ]); } /* * 处理百度的的ai图片 变成本地的 * 定时任务处理 * http://console.zx2049.com/api/ai/bdPicHandle */ public function bdPicHandle(){ //查找是否已处理图片 本地化 $id_list = Db::name('cms_ai')->where([ 'catid'=>13, 'is_pic' => 0, 'is_sc' => 1, //已生成文章 ])->find(); if(!empty($id_list['pic_html'])){ //提取图片 //var_dump($id_list['pic_html']); $str = htmlspecialchars_decode($id_list['pic_html']); $pattern="/<[img|IMG].*?src=[\'|\"](.*?(?:[\.gif|\.jpg|\.png?]))[\'|\"].*?[\/]?>/"; preg_match_all($pattern,$str,$match); print_r($match[1]); //下载文件到本地服务器 $imgs = $match[1]; if(!empty($imgs)){ $dir_name = '/ai/images/'.date('Y-m-d',time()).'/'; $dir = '.'.$dir_name; if(!is_dir($dir)) { mkdir($dir, 0777, true); } $new_img = []; foreach ($imgs as $k => $v){ //去除字符 $v = str_replace("?", "", $v); $this->downloadImage($v, $dir); $filename = pathinfo($v, PATHINFO_BASENAME); $new_img[] = $dir_name.$filename; } //保存图片信息 $new_img = json_encode($new_img); Db::name('cms_ai')->where([ 'id' => $id_list['id'] ])->update([ 'is_pic' => 1, 'imgs' => $new_img, 'username' => 'admin', 'sysadd' => 1, 'hits' => 1 ]); } } } //保存文件方法 public function saveAsImage($url, $file, $path) { $filename = pathinfo($url, PATHINFO_BASENAME); $resource = fopen($path . $filename, 'a'); fwrite($resource, $file); fclose($resource); } //下载文件方法 public function downloadImage($url, $path) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); //避免https 的ssl验证 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_SSLVERSION, false); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $file = curl_exec($ch); if(curl_exec($ch) === false) echo 'Curl error: ' . curl_error($ch); curl_close($ch); $this->saveAsImage($url, $file, $path); } }