ciyon_ai/web/admin/www/keywordManage.php
2026-04-27 16:46:44 +08:00

749 lines
28 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
namespace web\admin\www;
class keywordManage
{
static function setwhere($db, $post, $rsuser)
{
$query = $post->get('query', array());
$csql = new \ciy\sql('www_keyword');
$val = objstr($query, 'cateid');
if (!empty($val)) {
$csqlt = new \ciy\sql('www_list_cate');
$csqlt->where('name like', $val);
$trow = $db->getone($csqlt);
if (is_array($trow)) {
$csql->where('cateid', $trow['id']);
$query['cateid'] = $trow['name'];
} else {
$csql->where('cateid=0');
}
} else
$csql->where('cateid', get('_cateid'));
$csql->where('name like', objstr($query, 'name'));
$csql->where('usetime like', objstr($query, 'usetime'));
$csql->wheredaterange('addtimes', objstr($query, 'addtimes'));
$order = objstr($query, 'order', 'id desc');
$csql->order($order);
$query['order'] = $order;
return [$query, $csql];
}
public static function json_list()
{
global $db;
$rsuser = verifyfast();
$post = new \ciy\post();
list($where, $csql) = self::setwhere($db, $post, $rsuser);
$pageno = $post->getint('pageno', 1);
$pagecount = $post->getint('pagecount', 10);
$csql->limit($pageno, $pagecount);
$mainrowcount = $post->getint('count');
$mrows = $db->get($csql, $mainrowcount);
if ($mrows === false)
return errjson($db->error);
$ret = array('searchwhere' => $where, 'pageno' => $pageno, 'pagecount' => $pagecount, 'count' => $mainrowcount, 'list' => $mrows);
if ($post->getbool('field')) {
$field = array();
$fshow = $db->getfield($field, 'www_keyword');
foreach ($field as $fr => $v) {
if (get('_' . $fr))
$field[$fr]['c'] = ',' . $field[$fr]['c'];
}
$fshow = fieldadd($fshow, $field, 0, '_btn', '操作');
$ret['field'] = $field;
$ret['fshow'] = $fshow;
}
if ($post->getbool('once')) {
$ret['once'] = true;
$input = array();
if (!get('_cateid'))
$input[] = array('type' => 'input', 'form' => 'cateid', 'name' => '所属栏目', 'prop' => ' style="width:8em;"');
$input[] = array('type' => 'input', 'form' => 'name', 'name' => '关键词', 'prop' => ' style="width:8em;"');
$input[] = array('type' => 'input', 'form' => 'usetime', 'name' => '使用次数', 'prop' => ' style="width:8em;"');
$input[] = array('type' => 'daterange', 'form' => 'addtimes', 'name' => '添加时间');
$ret['searchinput'] = $input;
$csql = (new \ciy\sql('www_list_cate'))->column('id,name');
$ret['www_list_cate'] = $db->get($csql);
}
return succjson($ret);
}
public static function json_update()
{
global $db;
$rsuser = verifyfast();
//if (nopower($db, $rsuser['id'], 'p u'))
// return errjson('您未被授权操作');
$post = new \ciy\post();
//$db->trace($post->get('_pf'), $rsuser);
$id = $post->getint('id');
$cateid = $post->getint('cateid');
$name = $post->get('name');
if ($cateid == 0)
return errjson('请填写所属栏目');
if (empty($name))
return errjson('请填写关键词');
$datarow = null;
if ($id > 0) {
$csql = new \ciy\sql('www_keyword');
$csql->where('id', $id);
$datarow = $db->getone($csql);
if (!is_array($datarow))
return errjson('数据不存在');
}
try {
$db->begin();
$updata = array();
$updata['cateid'] = $cateid;
$updata['name'] = $name;
$csql = new \ciy\sql('www_keyword'); //auto
if ($id > 0) {
$csql->where('id', $id);
if ($db->update($csql, $updata) === false)
throw new \Exception('更新失败:' . $db->error);
} else {
$updata['usetime'] = 0;
$updata['addtimes'] = tostamp();
if ($db->insert($csql, $updata) === false)
throw new \Exception('新增失败:' . $db->error);
$id = $db->insert_id();
}
$updata['id'] = $id;
//savelogdb($db, $rsuser['id'], 'www_keyword', $datarow, $updata);
$db->commit();
} catch (\Exception $ex) {
$db->rollback();
savelogfile('err_db', $ex->getMessage());
return errjson($ex->getMessage());
}
$ret['data'] = $updata;
return succjson($ret);
}
public static function json_del()
{
global $db;
$rsuser = verifyfast();
//if (nopower($db, $rsuser['id'], 'p d'))
// return errjson('您未被授权操作');
$post = new \ciy\post();
//$db->trace($post->get('_pf'), $rsuser);
$ids = $post->get('ids');
if (empty($ids))
return errjson('请选择至少一条');
$csql = new \ciy\sql('www_keyword');
$csql->where('id in', $ids);
$mrows = $db->get($csql);
$vids = array();
try {
$db->begin();
foreach ($mrows as $mrow) {
$delid = $mrow['id'];
//delcheck($db, $delid, 'tablexx', 'xxid', '管理员');
//delall($db, $delid, 'tablexx', 'xxid', '运动员'); //deltimeall
delme($db, $delid, 'www_keyword');
savelogdb($db, $rsuser['id'], 'www_keyword', $mrow, null);
$vids[] = $delid;
}
$db->commit();
} catch (\Exception $ex) {
$db->rollback();
savelogfile('err_db', $ex->getMessage());
return errjson($ex->getMessage());
}
$ret['ids'] = $vids;
return succjson($ret);
}
public static function json_exportxls()
{
global $db;
$rsuser = verifyfast();
//if (nopower($db, $rsuser['id'], 'p e'))a
// return errjson('您未被授权操作');
$post = new \ciy\post();
list($where, $csql) = self::setwhere($db, $post, $rsuser);
$mrows = $db->get($csql);
if (count($mrows) > 10000)
return errjson('将导出' . count($mrows) . '条不建议超过1万条请筛选缩小范围');
$fields = array();
$fields[] = array('style' => 'l', 'width' => 60, 'field' => 'id', 'name' => '行码');
$fields[] = array('style' => 'l', 'width' => 100, 'field' => 'cateid', 'name' => '所属栏目');
$fields[] = array('style' => 'l', 'width' => 150, 'field' => 'name', 'name' => '关键词');
$fields[] = array('style' => 'r', 'width' => 100, 'field' => 'usetime', 'name' => '使用次数');
$fields[] = array('style' => 'l', 'width' => 100, 'field' => 'addtimes', 'name' => '添加时间');
$code_cateid = $db->get((new \ciy\sql('www_list_cate'))->column('id,name'));
$datas = array();
foreach ($mrows as $mrow) {
$dat = array();
foreach ($fields as $field) {
$field = $field['field'];
$val = isset($mrow[$field]) ? $mrow[$field] : '';
if ($field == 'id')
$val = enid($val);
if ($field == 'cateid')
$val = ccode($code_cateid, $val);
if ($field == 'addtimes')
$val = ($val <= 0 ? '--' : date('Y-m-d H:i', $val));
$dat[] = $val;
}
$datas[] = $dat;
}
$param = array();
$param['field'] = $fields;
$param['data'] = $datas;
$param['sheetname'] = '数据报表';
$param['titleheight'] = '25'; //列头高度
$param['landscape'] = true; //横向打印
$param['fixtopage'] = true; //打印整个工作表
$param['toptitle'] = '关键词库数据报表';
$str = \ciy\excel::general_excel_xml($param);
$filename = '';
if (empty($filename))
$filename = date('Y-m-d_H-i-s') . rand(1000, 9999);
$filename .= '.xls';
file_put_contents(PATH_WEB . 'ud/tmp/' . $filename, $str);
return succjson(array('url' => '/ud/tmp/' . $filename));
}
public static function json_importxls_in()
{
global $db;
$rsuser = verifyfast();
//if (nopower($db, $rsuser['id'], 'p i'))
// return errjson('您未被授权操作');
$post = new \ciy\post();
$file = PATH_WEB . '/ud' . $post->get('file');
if (!file_exists($file))
return errjson('文件不存在');
require_once PATH_ROOT . '../libs/phpoffice/autoload.php';
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($file);
$sheet = $spreadsheet->getActiveSheet();
$datas = $sheet->toArray('', true, true, false);
$datacnt = count($datas);
if ($datacnt < 2)
return errjson('数据为空');
$html = '';
$headsn = array();
$headsn[] = '行码.id';
$headsn[] = '所属栏目.cateid';
$headsn[] = '关键词.name';
$headsn[] = '使用次数.usetime';
$xlsidx = 1;
if (empty($datas[0][count($headsn) - 1]))
$xlsidx = 2;
$heads = array();
foreach ($headsn as $_head) {
$hd = explode('.', $_head);
if (count($hd) < 2)
continue;
$heads[] = array(
'idx' => array_search($hd[0], $datas[$xlsidx - 1]),
'fld' => $hd[1],
'name' => $hd[0]
);
}
$code_cateid = $db->get((new \ciy\sql('www_list_cate'))->column('id,name'));
$html .= '<div class="table" style="width: 100%;height: calc(100% - 2.2em);overflow: auto;">';
$html .= '<table><tbody><tr>';
$html .= '<th>#</th>';
foreach ($heads as $arr) {
$html .= '<th>' . $arr['name'] . '</th>';
}
$html .= '</tr>';
$cnt = 0;
$uniques = array();
$id = 0;
for ($rowidx = $xlsidx; $rowidx < $datacnt; $rowidx++) {
$bfull = false;
foreach ($datas[$rowidx] as $dat) {
if (empty($dat))
continue;
$bfull = true;
break;
}
if (!$bfull)
continue;
$lineidx = $rowidx - $xlsidx + 1;
$hrhtml = '';
$firsthtml = '<td><div>' . $lineidx . '</div></td>';
$bempty = true;
$unqs = array();
$csql = new \ciy\sql('www_keyword');
foreach ($heads as $arr) {
$name = $arr['name'];
$errmsg = ''; //数据有误,显示红色说明
$showdat = ''; //显示在表格中的数据
if ($arr['idx'] > -1)
$showdat = trim($datas[$rowidx][$arr['idx']]);
if ($showdat == '--')
$showdat = '';
$value = $showdat; //在表单中的数据(转换后)
$ext = ''; //扩展表单
if ($name == '行码') {
if (empty($showdat)) {
$value = 0;
$showdat = '<kbd>新增</kbd>';
} else {
$id = deid($showdat);
if ($id == 0)
$errmsg = $name . '解析错误';
else {
$csqlchk = new \ciy\sql('www_keyword');
$csqlchk->where('id', $id)->column('id');
$chkid = toint($db->get1($csqlchk));
if ($chkid != $id)
$errmsg = $name . '在数据库中不存在';
$value = $id;
}
}
} else if ($name == '所属栏目') {
if (empty($showdat)) {
$errmsg = $name . '为必填项';
} else {
$value = dcode($code_cateid, $showdat);
if ($value == -1)
$errmsg = $name . '文字与系统数据不匹配';
}
} else if ($name == '关键词') {
if (empty($showdat)) {
$errmsg = $name . '为必填项';
} else {
}
} else if ($name == '使用次数') {
if (empty($showdat)) {
$value = 0;
} else {
$showdat = str_replace(',', '', $showdat);
if (!is_numeric($showdat))
$errmsg = $name . '不是数字';
else
$value = toint($showdat);
}
}
if (!empty($showdat))
$bempty = false;
if (empty($errmsg))
$hrhtml .= '<td><div>' . $showdat . '<input type="hidden" name="' . $arr['fld'] . '_' . $lineidx . '" value="' . $value . '"/>' . $ext . '</div></td>';
else
$hrhtml .= '<td style="background:#ffe8c5;" title="#' . $lineidx . ':' . $errmsg . '"><div>' . $showdat . '</div></td>';
}
if ($bempty)
continue;
if (count($unqs) > 0) {
$unq = implode('|', $unqs);
if (in_array($unq, $uniques))
$firsthtml = '<td style="background:#ffe8c5;" title="#' . $lineidx . ':该行与待导入数据有重复"><div data-lang>重复</div></td>';
else {
$uniques[] = $unq;
$csql->column('id');
$chkid = toint($db->get1($csql));
if ($chkid > 0 && (($id > 0 && $chkid != $id) || $id == 0))
$firsthtml = '<td style="background:#ffe8c5;" title="#' . $lineidx . ':该行与数据库数据有重复"><div data-lang>重复</div></td>';
}
}
$html .= '<tr>' . $firsthtml . $hrhtml . '</tr>';
$cnt++;
}
$html .= '</tbody></table></div>';
$html .= '<input type="hidden" name="total" value="' . $cnt . '"/>';
$html .= '<code>共' . $cnt . '条数据</code>';
return succjson(array('html' => $html, 'count' => $cnt));
}
public static function json_importxls_data()
{
global $db;
$rsuser = verifyfast();
//if (nopower($db, $rsuser['id'], 'p i'))
// return errjson('您未被授权操作');
$post = new \ciy\post();
//$db->trace($post->get('_pf'), $rsuser);
$total = $post->getint('total');
try {
$db->begin();
for ($i = 1; $i <= $total; $i++) {
$id = $post->getint('id_' . $i);
$updata = array();
$updata['cateid'] = $post->get('cateid_' . $i);
$updata['name'] = $post->get('name_' . $i);
$updata['usetime'] = $post->get('usetime_' . $i);
$csql = new \ciy\sql('www_keyword');
if ($id == 0) {
$updata['addtimes'] = tostamp();
if ($db->insert($csql, $updata) === false)
throw new \Exception('新增导入失败:' . $db->error);
} else {
$csql->where('id', $id);
if ($db->update($csql, $updata) === false)
throw new \Exception('更新导入失败:' . $db->error);
}
}
$db->commit();
} catch (\Exception $ex) {
$db->rollback();
savelogfile('err_db', $ex->getMessage());
return errjson($ex->getMessage());
}
return succjson();
}
// 后端批量导入接口
public static function json_BatchAdd()
{
global $db;
$rsuser = verifyfast();
$post = new \ciy\post();
$font_cateid = $post->getint('cateid');
$font_names = trim($post->get('names'));
$Array_name = explode("\n", $font_names);
try {
$db->begin();
foreach ($Array_name as $name) {
$name = trim($name);
if ($name === '') continue;
$csql = new \ciy\sql('www_keyword');
$csql->where('name', $name);
$isExist = $db->get1($csql);
if ($isExist === '') {
$insertData = [
'cateid' => $font_cateid,
'name' => $name,
'usetime' => 0,
'addtimes' => tostamp()
];
$csql_insert = new \ciy\sql('www_keyword');
$db->insert($csql_insert, $insertData);
}
}
$db->commit();
return succjson('批量添加成功');
// return succjson([
// 'data'=> $font_names,
// 'cateid'=>$font_cateid
// ]);
} catch (\Exception $ex) {
$db->rollback();
return errjson('错误:' . $ex->getMessage());
}
}
// 交叉生成关键词
public static function json_GenerateKeywords()
{
global $db;
$rsuser = verifyfast();
$post = new \ciy\post();
$cateid = $post->getint('cateid');
$dim1 = trim($post->get('dim1'));
$dim2 = trim($post->get('dim2'));
$dim3 = trim($post->get('dim3'));
if ($cateid == 0) {
return errjson('请选择所属栏目');
}
if (empty($dim2)) {
return errjson('核心词不能为空');
}
$toArray = function ($text) {
$arr = explode("\n", $text);
$arr = array_map('trim', $arr);
$arr = array_filter($arr);
return array_unique($arr);
};
$arr1 = $toArray($dim1);
$arr2 = $toArray($dim2);
$arr3 = $toArray($dim3);
$keywords = [];
if (!empty($arr3) && !empty($arr1)) {
foreach ($arr1 as $a) {
foreach ($arr2 as $b) {
foreach ($arr3 as $c) {
$keywords[] = $a . $b . $c;
}
}
}
} else if(empty($arr3) && !empty($arr2)){
foreach ($arr1 as $a) {
foreach ($arr2 as $b) {
$keywords[] = $a . $b;
}
}
} else if(empty($arr1) && !empty($arr2)){
foreach ($arr2 as $b) {
foreach($arr3 as $c){
$keywords[] = $b . $c;
}
}
}
$keywords = array_unique($keywords);
if (empty($keywords)) {
return errjson('未生成有效关键词');
}
// 返回数组给前端编辑
return succjson([
'list' => $keywords
]);
}
// 确认后入库
public static function json_ConfirmSaveKeywords()
{
global $db;
$rsuser = verifyfast();
$post = new \ciy\post();
$cateid = $post->getint('cateid');
$text = trim($post->get('text'));
clog($text);
if (empty($cateid) || empty($text)) {
return errjson('请填写相应的信息');
}
$lines = explode("\n", $text);
$lines = array_map('trim', $lines);
$lines = array_filter($lines);
$lines = array_unique($lines);
$addNum = 0;
try {
$db->begin();
foreach ($lines as $name) {
$sql = new \ciy\sql('www_keyword');
$sql->where('name', $name);
if ($db->get1($sql)) continue;
$ins = [
'cateid' => $cateid,
'name' => $name,
'usetime' => 0,
'addtimes' => tostamp()
];
$db->insert(new \ciy\sql('www_keyword'), $ins);
$addNum++;
}
$db->commit();
return succjson(['msg' => "保存完成,新增 {$addNum}"]);
} catch (\Exception $e) {
$db->rollback();
return errjson($e->getMessage());
}
}
//AI相关功能
// public static function json_AIFunction()
// {
// global $db;
// $rsuser = verifyfast();
// $post = new \ciy\post();
// $function_type = trim($post->get('param'));
// // AI配置 阿里云通义千问
// $aiConfig = [
// // https://dashscope-inner.aliyuncs.com
// 'baseurl' => 'https://dashscope.aliyuncs.com/compatible-mode/v1',
// 'aikey' => 'sk-0cd7b8c72ae04d049306e345f17c6790',
// 'model' => 'qwen-turbo',
// 'maxtoken' => 2000
// ];
// try {
// $openai = new \ciy\openai($aiConfig);
// // 角色定位
// if ($function_type === '修改') {
// $content = trim($post->get('content'));
// if (empty($content)) {
// return errjson('请先等待生成关键词后再操作');
// }
// $system = '你是SEO关键词清洗优化助手严格遵守
// 1. 核心词绝对不能修改、删除、替换;
// 2. 只剔除不通顺、重复、无意义、垃圾关键词;
// 3. 可优化前后修饰词,不改变核心;
// 4. 输出严格一行一个,无解释、无符号、无序号;
// 5. 只净化,不新增。';
// $prompt = "请清洗优化以下关键词:\n{$content}";
// } else {
// $cateid = trim($post->get('cateid'));
// $dim1 = trim($post->get('dim1'));
// $new_dim1 = empty($dim1) ? "自行补充,与核心词高度关联" : $dim1;
// $dim2 = trim($post->get('dim2'));
// $core_words = explode("\n", $dim2);
// $core_words = array_filter(array_map('trim', $core_words));
// $filter_str = implode('、', $core_words);
// $dim3 = trim($post->get('dim3'));
// $new_dim3 = empty($dim3) ? "自行补充,与核心词高度关联" : $dim3;
// if (empty($cateid)) {
// return errjson('请选择所属栏目');
// }
// if(empty($dim2)){
// return errjson('请填写核心关键词');
// }
// // AI 联想
// $system = '你是专业的SEO关键词生成助手严格遵守
// 1. 核心词绝对不变;
// 2. 根据核心词自动生成合理的前缀词+核心词+后缀词组合的关键词;
// 3. 剔除不通顺、重复、无意义内容;
// 4. 输出一行一个,无多余内容;
// 5. 生成数量 7-12 个。';
// $prompt = "前缀词:{$new_dim1} ,核心词:{$filter_str},后缀词:{$new_dim3}。请根据提供的前缀词、核心词、后缀词生成优质关键词";
// }
// // 发送请求
// $openai->newsystem($system);
// $res = $openai->completion($prompt);
// // 修复:统一处理返回值(字符串/数组都兼容)
// $resultContent = '';
// if (is_array($res)) {
// $resultContent = isset($res['content']) ? trim($res['content']) : '';
// } else {
// $resultContent = trim($res);
// }
// if (!empty($resultContent)) {
// return succjson(['data' => $resultContent]);
// } else {
// return errjson('AI处理失败未返回有效内容');
// }
// } catch (\Exception $e) {
// return errjson('接口异常:' . $e->getMessage());
// }
// }
// }
public static function json_AIFunction()
{
global $db;
$rsuser = verifyfast();
$post = new \ciy\post();
$function_type = trim($post->get('param'));
// 日志函数:把信息写到文件里
$logPath = '/var/log/ai_debug.log';
$writeLog = function ($msg) use ($logPath) {
$time = date('Y-m-d H:i:s');
file_put_contents($logPath, "[{$time}] {$msg}\n", FILE_APPEND);
};
// 记录开始
$writeLog("=== 开始执行AI联想 ===");
$aiConfig = [
'baseurl' => 'https://dashscope.aliyuncs.com/compatible-mode/v1',
'aikey' => 'sk-0cd7b8c72ae04d049306e345f17c6790',
'model' => 'qwen-turbo',
'maxtoken' => 2000
];
// ==========================================================
$writeLog("配置加载完成baseurl: " . $aiConfig['baseurl']);
try {
// 1. 检查openai类是否存在
if (!class_exists('\ciy\openai')) {
throw new \Exception("关键错误:\ciy\openai 类不存在!请检查文件是否加载");
}
$writeLog("openai类存在准备实例化");
// 2. 实例化
$openai = new \ciy\openai($aiConfig);
$writeLog("openai实例化成功");
// 3. 处理参数
if ($function_type === '修改') {
$content = trim($post->get('content'));
if (empty($content)) {
return errjson('请先等待生成关键词后再操作');
}
$system = '你是SEO关键词清洗优化助手严格遵守
1. 核心词绝对不能修改、删除、替换;
2. 只剔除不通顺、重复、无意义、垃圾关键词;
3. 可优化前后修饰词,不改变核心;
4. 输出严格一行一个,无解释、无符号、无序号;
5. 只净化,不新增。';
$prompt = "请清洗优化以下关键词:\n{$content}";
} else {
$cateid = trim($post->get('cateid'));
$dim1 = trim($post->get('dim1'));
$dim2 = trim($post->get('dim2'));
$dim3 = trim($post->get('dim3'));
$new_dim1 = empty($dim1) ? "自行补充,与核心词高度关联" : $dim1;
$new_dim3 = empty($dim3) ? "自行补充,与核心词高度关联" : $dim3;
$core_words = explode("\n", $dim2);
$core_words = array_filter(array_map('trim', $core_words));
$filter_str = implode('、', $core_words);
if (empty($cateid)) {
return errjson('请选择所属栏目');
}
if (empty($dim2)) {
return errjson('请填写核心关键词');
}
$system = '你是专业的SEO关键词生成助手严格遵守
1. 核心词绝对不变;
2. 根据核心词自动生成合理的前缀词+核心词+后缀词组合的关键词;
3. 剔除不通顺、重复、无意义内容;
4. 输出一行一个,无多余内容;
5. 生成数量 7-12 个。';
$prompt = "前缀词:{$new_dim1},核心词:{$filter_str},后缀词:{$new_dim3}。请根据提供的内容生成优质关键词";
}
$writeLog("参数处理完成prompt长度" . strlen($prompt));
// 4. 设置system并调用
$openai->newsystem($system);
$writeLog("system设置完成");
$res = $openai->completion($prompt);
$writeLog("completion调用返回结果类型" . gettype($res) . ",内容:" . json_encode($res));
// 5. 处理返回
$resultContent = '';
if (is_array($res)) {
$resultContent = isset($res['content']) ? trim($res['content']) : '';
} elseif (is_string($res)) {
$resultContent = trim($res);
}
if (!empty($resultContent)) {
$writeLog("成功获取内容,长度:" . strlen($resultContent));
return succjson(['data' => $resultContent]);
} else {
throw new \Exception("AI处理失败返回内容为空原始结果" . json_encode($res));
}
} catch (\Exception $e) {
// 把错误完整写入日志
$errMsg = "异常捕获:" . $e->getMessage() . " 堆栈:" . $e->getTraceAsString();
$writeLog($errMsg);
return errjson('接口异常:' . $e->getMessage());
}
}
}