作者 zhl

merge origin/master

... ... @@ -59,15 +59,30 @@ class MonthProjectCount extends Command
* @time :2024/1/8 9:05
*/
public function count($project_id,$url,$is_upgrade = 0){
$data = [];
$list = DB::connection('custom_mysql')->table('gl_customer_visit')
->select(DB::raw('DATE_FORMAT(updated_date, "%Y-%m") as month'))
->orderBy('month', 'asc')
->groupBy('month')->get()->toArray();
foreach ($list as $v){
$data[] = $v->month;
$data = [];
if (!empty($list)) {
// 提取已有月份
foreach ($list as $v) {
$data[] = $v->month;
}
// 生成连续月份
$startMonth = $data[0];
$endMonth = end($data);
$fullMonths = [];
$current = $startMonth;
while ($current <= $endMonth) {
$fullMonths[] = $current;
$current = date('Y-m', strtotime($current . ' +1 month'));
}
$data = $fullMonths;
} else {
$data = [];
}
$list = $this->fillMissingMonths($data);
$list = $data;
foreach ($list as $v){
$arr = [];
$monthCountModel = new MonthCount();
... ... @@ -179,38 +194,4 @@ class MonthProjectCount extends Command
$arr['referrer_port'] = json_encode($referrer_port);
return $arr;
}
/**
* @remark :补齐月份
* @name :fillMissingMonths
* @author :lyh
* @method :post
* @time :2024/8/14 11:11
*/
public function fillMissingMonths($dates) {
// 将字符串日期转换为 Carbon 对象
$carbonDates = array_map(function($date) {
return Carbon::createFromFormat('Y-m', $date);
}, $dates);
// 排序日期,确保列表按时间顺序排列
usort($carbonDates, function($a, $b) {
return $a->gt($b);
});
// 用于存储完整日期的数组
$completeDates = [];
// 遍历日期列表,补齐中间缺失的月份
for ($i = 0; $i < count($carbonDates) - 1; $i++) {
$current = $carbonDates[$i];
$next = $carbonDates[$i + 1];
// 将当前月份加入完整日期数组
array_push($completeDates, $current->format('Y-m'));
// 循环补齐中间缺失的月份
while ($current->addMonth()->lt($next)) {
array_push($completeDates, $current->format('Y-m'));
}
}
// 加入最后一个月份
array_push($completeDates, $carbonDates[count($carbonDates) - 1]->format('Y-m'));
return $completeDates;
}
}
... ...
... ... @@ -5,14 +5,20 @@
* Date: 2025/10/27
* Time: 13:42
*/
namespace App\Console\Commands\Product;
use App\Console\Commands\Tdk\UpdateSeoTdk;
use App\Models\Com\NoticeLog;
use App\Models\Com\UpdateNotify;
use App\Models\Domain\DomainInfo;
use App\Models\Product\Keyword;
use App\Models\Project\DeployBuild;
use App\Models\Project\Project;
use App\Services\ProjectServer;
use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
class SplicePrefix extends Command
... ... @@ -36,20 +42,36 @@ class SplicePrefix extends Command
*/
public function handle()
{
$this->output('project start: ' . 4410); #6 23 38 41 46 47 49
$this->bind(4410);
dd('end');
#TODO 获取当日不达标项目, 检查关键词前缀拼接
//获取当日不达标项目, 检查关键词前缀拼接
$project_ids = $this->getProject();
if (empty($project_ids))
return true;
//获取已经拼接的项目id
$key = 'splice_prefix_project_ids';
$has_splice_ids = Cache::get($key) ?? [];
foreach ($project_ids as $project_id) {
if ($project_id == 1)
continue;
if (in_array($project_id, $has_splice_ids)) {
continue;
}
$this->output('project start: ' . $project_id);
$this->bind($project_id);
//处理完后加入已拼接项目id集
array_push($has_splice_ids, $project_id);
$this->output('project end: ' . $project_id);
}
//更新已拼接项目id缓存
Cache::put($key, $has_splice_ids);
return true;
}
... ... @@ -59,129 +81,147 @@ class SplicePrefix extends Command
*/
public function bind($project_id)
{
$project = ProjectServer::useProject($project_id);
// 客户前缀
$tdk_class = new UpdateSeoTdk();
$info = $tdk_class->getDeployOptimize($project_id);
$fix_keyword = explode(",", $info['keyword_prefix']);
$fix_keyword = array_filter($fix_keyword);
// 所有前缀
$all_prefixes = $tdk_class->getAllPrefix(1, $project_id);
$all_prefixes = array_map('strtolower', $all_prefixes);
$keywords = Keyword::select(['id', 'title', 'seo_title'])->get();
foreach ($keywords as $item) {
$this_fix_keyword = $fix_keyword;
if (empty($item->title))
continue;
$notify_master = false;
if (ProjectServer::useProject($project_id)) {
$this->output('keyword id:' . $item->id . ' | title: ' . $item->title . ' | old seo title: ' . $item->seo_title);
// 客户前缀
$tdk_class = new UpdateSeoTdk();
$info = $tdk_class->getDeployOptimize($project_id);
$fix_keyword = explode(",", $info['keyword_prefix']);
$fix_keyword = array_filter($fix_keyword);
// 没有 SEO Title 直接生成
if (empty($item->seo_title)) {
$prefix = $tdk_class->getPrefixKeyword($project_id, 'prefix', 2, $item->title);
$suffix = $tdk_class->getPrefixKeyword($project_id, 'suffix', 2, trim($prefix . ' ' . $item->title));
if(Str::startsWith($suffix, ', ')){
$seo_title = $prefix . ' ' . $item->title . $suffix;
}else{
$seo_title = $prefix . ' ' . $item->title . ' ' . $suffix;
}
// $item->seo_title = trim($seo_title);
// $item->save();
$this->output('new seo title: ' . $seo_title);
continue;
}
// 所有前缀
$all_prefixes = $tdk_class->getAllPrefix(1, $project_id);
$all_prefixes = array_map('strtolower', $all_prefixes);
// 有 SEO Title 需要分析前后缀
$start = strpos($item->seo_title, $item->title);
$keywords = Keyword::select(['id', 'title', 'seo_title', 'route'])->get();
foreach ($keywords as $item) {
$this_fix_keyword = $fix_keyword;
if (empty($item->title))
continue;
// Title 和 SEO Title 不存在包含关系
if ($start === FALSE) {
$this->output('Title 和 SEO Title 不存在包含关系');
continue;
}
$this->output('keyword id:' . $item->id . ' | title: ' . $item->title . ' | old seo title: ' . $item->seo_title);
$prefix = $start == 0 ? '' : trim(substr($item->seo_title, 0, $start));
$prefix_array = explode(' ', $prefix);
$prefix_array = array_filter($prefix_array);
$need_num = 2 - count($prefix_array);
// 已经有两个前缀, 不在处理
if ($need_num <= 0) {
$this->output('已经有两个前缀, 不在处理');
continue;
}
// 没有 SEO Title 直接生成
if (empty($item->seo_title)) {
$prefix = $tdk_class->getPrefixKeyword($project_id, 'prefix', 2, $item->title);
$suffix = $tdk_class->getPrefixKeyword($project_id, 'suffix', 2, trim($prefix . ' ' . $item->title));
if (Str::startsWith($suffix, ', ')) {
$seo_title = $prefix . ' ' . $item->title . $suffix;
} else {
$seo_title = $prefix . ' ' . $item->title . ' ' . $suffix;
}
$item->seo_title = trim($seo_title);
$item->save();
// 关键词最后一个词是前缀的词,前后缀都不拼
$title_words = explode(' ', strtolower($item->title));
//存入按需更新表
UpdateNotify::addUpdateItem($project_id, 'product_keyword', $item->route);
$notify_master = true;
// 关键词最后一个词是前缀的词,前后缀都不拼
if(in_array(Arr::last($title_words), $all_prefixes)) {
$this->output('关键词最后一个词是前缀的词, 前后缀都不拼');
continue;
}
$this->output('new seo title: ' . $seo_title);
continue;
}
// in,for,with,to,near,from 这些介词 只拼前缀,不拼后缀
$ban = [];
// 有 SEO Title 需要分析前后缀
$start = strpos($item->seo_title, $item->title);
// 关键词本身包含了前缀,也可以再拼一个不重复的前缀, 包含两个前缀就不拼前缀了
foreach ($title_words as $title_word) {
if(in_array($title_word, $all_prefixes)){
$ban[] = $title_word;
// Title 和 SEO Title 不存在包含关系
if ($start === FALSE) {
$this->output('Title 和 SEO Title 不存在包含关系');
continue;
}
}
$need_num = $need_num - count($ban);
// 关键词本身包含前缀,包含关键词大于等于需要的前缀,当前关键词不需要处理
if ($need_num <= 0) {
$this->output('关键词本身包含前缀,包含关键词大于等于需要的前缀,当前关键词不需要处理');
continue;
}
$prefix = $start == 0 ? '' : trim(substr($item->seo_title, 0, $start));
$prefix_array = explode(' ', $prefix);
$prefix_array = array_filter($prefix_array);
$need_num = 2 - count($prefix_array);
// 已经有两个前缀, 不在处理
if ($need_num <= 0) {
$this->output('已经有两个前缀, 不在处理');
continue;
}
// 关键词最后一个词是前缀的词,前后缀都不拼
$title_words = explode(' ', strtolower($item->title));
// services/service 结尾的词,后缀不拼manufacturer,factory
// manufacturer,factory 结尾的词,后缀不拼 services/service
// 有wholesale或cheap的词,后缀不拼 manufacturer,factory,exporter,company
// 关键词最后一个词是前缀的词,前后缀都不拼
if (in_array(Arr::last($title_words), $all_prefixes)) {
$this->output('关键词最后一个词是前缀的词, 前后缀都不拼');
continue;
}
// 关键词以manufacturer,factory,exporter,company结尾, 前缀不拼wholesale或cheap的词
if (Str::endsWith(strtolower($item->title), ['manufacturer', 'manufacturers', 'factory', 'factories', 'exporter', 'exporters', 'company', 'companies', 'supplier', 'suppliers']))
$ban = array_merge($ban, ['wholesale', 'cheap', 'buy']);
// in,for,with,to,near,from 这些介词 只拼前缀,不拼后缀
$ban = [];
// 关键词本身包含了前缀,也可以再拼一个不重复的前缀, 包含两个前缀就不拼前缀了
foreach ($title_words as $title_word) {
if (in_array($title_word, $all_prefixes)) {
$ban[] = $title_word;
}
}
$need_num = $need_num - count($ban);
foreach ($this_fix_keyword as $k => $keyword) {
// 被禁用的关键词
if (in_array(strtolower(Str::plural($keyword)), $ban)) {
unset($this_fix_keyword[$k]);
// 关键词本身包含前缀,包含关键词大于等于需要的前缀,当前关键词不需要处理
if ($need_num <= 0) {
$this->output('关键词本身包含前缀,包含关键词大于等于需要的前缀,当前关键词不需要处理');
continue;
}
if (in_array(strtolower(Str::singular($keyword)), $ban)) {
unset($this_fix_keyword[$k]);
// services/service 结尾的词,后缀不拼manufacturer,factory
// manufacturer,factory 结尾的词,后缀不拼 services/service
// 有wholesale或cheap的词,后缀不拼 manufacturer,factory,exporter,company
// 关键词以manufacturer,factory,exporter,company结尾, 前缀不拼wholesale或cheap的词
if (Str::endsWith(strtolower($item->title), ['manufacturer', 'manufacturers', 'factory', 'factories', 'exporter', 'exporters', 'company', 'companies', 'supplier', 'suppliers']))
$ban = array_merge($ban, ['wholesale', 'cheap', 'buy']);
foreach ($this_fix_keyword as $k => $keyword) {
// 被禁用的关键词
if (in_array(strtolower(Str::plural($keyword)), $ban)) {
unset($this_fix_keyword[$k]);
}
if (in_array(strtolower(Str::singular($keyword)), $ban)) {
unset($this_fix_keyword[$k]);
}
}
}
$this_fix_keyword = array_diff($this_fix_keyword, $prefix_array);
shuffle($this_fix_keyword);
$need_keyword = [];
foreach ($this_fix_keyword as $v) {
if ($need_num == 0)
break;
$is_repeat = false;
foreach ($need_keyword as $keyword) {
if (Str::singular($keyword) == Str::singular($v)) {
$is_repeat = true;
$this_fix_keyword = array_diff($this_fix_keyword, $prefix_array);
shuffle($this_fix_keyword);
$need_keyword = [];
foreach ($this_fix_keyword as $v) {
if ($need_num == 0)
break;
$is_repeat = false;
foreach ($need_keyword as $keyword) {
if (Str::singular($keyword) == Str::singular($v)) {
$is_repeat = true;
break;
}
}
if ($is_repeat)
continue;
$need_keyword[] = $v;
$need_num--;
}
if ($is_repeat)
continue;
$item->seo_title = trim(implode(' ', $need_keyword) . ' ' . trim($item->seo_title));
$item->save();
$need_keyword[] = $v;
$need_num--;
//存入按需更新表
UpdateNotify::addUpdateItem($project_id, 'product_keyword', $item->route);
$notify_master = true;
$this->output('new seo title: ' . implode(' ', $need_keyword) . ' ' . trim($item->seo_title));
}
// $item->seo_title = trim(implode(' ', $need_keyword) . ' ' . trim($item->seo_title));
// $item->save();
$this->output('new seo title: ' . implode(' ', $need_keyword) . ' ' . trim($item->seo_title));
}
if ($notify_master) {
//通知主站按需更新
$this->sendNotify($project_id, 2);
}
return true;
}
... ... @@ -191,8 +231,40 @@ class SplicePrefix extends Command
*/
public function getProject()
{
$project_ids = Project::where(['type' => Project::TYPE_TWO, 'project_type' => Project::TYPE_ZERO, 'delete_status' => Project::IS_DEL_FALSE, 'is_remain_today' => 0])->pluck('id')->toArray();
return $project_ids;
return Project::where(['type' => Project::TYPE_TWO, 'project_type' => Project::TYPE_ZERO, 'delete_status' => Project::IS_DEL_FALSE, 'is_remain_today' => 0])->pluck('id')->toArray();
}
/**
* 页面更新
* @param $project_id
* @param $route
* @author Akun
* @date 2025/10/30 14:33
*/
public function sendNotify($project_id, $route)
{
//获取当前项目的域名
$domainModel = new DomainInfo();
$domainInfo = $domainModel->read(['project_id' => $project_id]);
if ($domainInfo === false) {
//获取测试域名
$deployBuildModel = new DeployBuild();
$buildInfo = $deployBuildModel->read(['project_id' => $project_id]);
$domain = $buildInfo['test_domain'];
} else {
$domain = 'https://' . $domainInfo['domain'] . '/';
}
$url = $domain . 'api/update_page/';
$param = [
'project_id' => $project_id,
'type' => 1,
'route' => $route,
'url' => [],
'language' => [],
];
NoticeLog::createLog(NoticeLog::GENERATE_PAGE, ['c_url' => $url, 'c_params' => $param], date('Y-m-d H:i:s', time() + 300));
$this->output('更新中请稍后, 更新完成将会发送站内信通知更新结果!');
}
/**
... ... @@ -207,4 +279,4 @@ class SplicePrefix extends Command
echo $message;
return true;
}
}
\ No newline at end of file
}
... ...
... ... @@ -171,6 +171,10 @@ class HtmlCollect extends Command
$html = str_replace('Broflanilide', '', $html);
$html = str_replace('broflanilide', '', $html);
}
if ($project_id == 587) {
//437项目单词替换
$html = str_replace('Horny Goat Weed', 'Icariin', $html);
}
$collect_info->html = $html;
$collect_info->status = CollectTask::STATUS_COM;
$collect_info->save();
... ...
... ... @@ -138,6 +138,10 @@ class ProjectUpdate extends Command
//2078项目单词替换
$replace = ['Broflanilide' => '', 'broflanilide' => ''];
}
if ($project_id == 587) {
//587项目单词替换
$replace = ['Horny Goat Weed' => 'Icariin'];
}
//设置数据库
$project = ProjectServer::useProject($project_id);
... ...
... ... @@ -41,7 +41,7 @@ class GeoController extends BaseController
$projectModel = new Project();
$projectInfo = $projectModel->read(['id' => $project_id],['title','version']);
$geoWritingsModel = new GeoWritings();
$lists = $geoWritingsModel->list(['project_id' => $project_id, 'status' => 2 ,'is_del' => GeoWritings::IS_DEL_FALSE],'id',['title', 'status', 'uniqid', 'confirm_at']);
$lists = $geoWritingsModel->list(['project_id' => $project_id, 'status' => ['in',[GeoWritings::STATUS_RUNNING,GeoWritings::STATUS_FINISH]] ,'is_del' => GeoWritings::IS_DEL_FALSE],'id',['title', 'status', 'uniqid', 'confirm_at']);
$result = [
'project' => $projectInfo,
'list' => $lists
... ... @@ -108,13 +108,16 @@ class GeoController extends BaseController
$token = trim($this->param['token']);
$data = GeoConfirm::where(['uniqid' => $token])->first();
if (empty($data)){
return $this->error('当前授权已失效');
return $this->error('确认链接已重置,请查看最新推送链接!');
}
$content = explode("\n", $data->content);
$confirm = explode("\n", $data->confirm);
$type = $data->type;
$status = $data->status;
$result = compact('content', 'confirm', 'type', 'status');
$projectModel = new Project();
$projectInfo = $projectModel->read(['id' => $data->project_id],['title','version']);
$project_title = $projectInfo['title'] ?? '';
$result = compact('content', 'confirm', 'type', 'status', 'project_title');
$this->response('success',Code::SUCCESS,$result);
}
... ...
... ... @@ -205,4 +205,22 @@ class NoticeController extends BaseController
MessagePush::addInquiryMessage(0, $project->id, $country, $name, $submit);
return $this->success();
}
/**
* 获取6.0所有使用域名
* @return false|string
* @author Akun
* @date 2025/10/30 10:42
*/
public function getAllDomain(){
$domainModel = new DomainInfo();
$lists = $domainModel->list(['status'=>DomainInfo::STATUS_ONE],'id',['domain','project_id'],'asc');
$project_model = new Project();
foreach ($lists as &$v){
$pro_info = $project_model->read(['id'=>$v['project_id']],['company']);
$v['company'] = $pro_info ? $pro_info['company'] : '';
}
return $this->success($lists);
}
}
... ...
... ... @@ -71,4 +71,26 @@ class GeoConfirmController extends BaseController
$data = $this->logic->saveConfirmContent($this->param);
$this->response('success', Code::SUCCESS, $data);
}
/**
* @remark :保存确认信息
* @name :saveInfo
* @author :lyh
* @method :post
* @time :2025/10/30 11:30
*/
public function saveInfo()
{
$this->request->validate([
'id' => 'required',
'confirm' => 'required',
'confirm_num' => 'required',
], [
'id.required' => '主键id不能为空',
'confirm.required' => '客户确认内容不能为空',
'confirm_num.max' => '客户确认数量不能为空',
]);
$data = $this->logic->saveConfirmInfo();
$this->response('success', Code::SUCCESS, $data);
}
}
... ...
... ... @@ -68,4 +68,22 @@ class GeoController extends BaseController
$data = $this->logic->saveConfig($this->param);
$this->response('success', Code::SUCCESS, $data);
}
/**
* @remark :统计数据
* @name :getCount
* @author :lyh
* @method :post
* @time :2025/10/30 10:37
*/
public function getCount()
{
$this->request->validate([
'project_id' => 'required',
], [
'project_id.required' => '项目ID不能为空',
]);
$data = $this->logic->getCount();
$this->response('success', Code::SUCCESS, $data);
}
}
... ...
... ... @@ -78,8 +78,10 @@ class SettingFaqController extends BaseController
{
$this->request->validate([
'route'=>'required',
'qa'=>'required',
],[
'route.required' => '路由不能为空',
'qa.required' => 'qa不能为空',
]);
$data = $this->logic->saveFaq();
$this->response('success',Code::SUCCESS,$data);
... ...
... ... @@ -40,7 +40,7 @@ class GeoConfirmLogic extends BaseLogic
public function saveConfirmContent($param)
{
try {
$info = $this->model->read(['project_id' => $param['project_id']]);
$info = $this->model->read(['project_id' => $param['project_id'],'type' => $param['type']]);
if($info === false){
$id = $this->model->addReturnId($param);
}else{
... ... @@ -68,6 +68,24 @@ class GeoConfirmLogic extends BaseLogic
public function getConfirmInfo()
{
$data = $this->model->read($this->param);
if($data === false){
$this->fail('当前数据不存在或者已被删除');
}
$data['confirm'] = $data['content'];
return $this->success($data);
}
/**
* @remark :保存确认信息
* @name :saveConfirmInfo
* @author :lyh
* @method :post
* @time :2025/10/30 11:41
*/
public function saveConfirmInfo()
{
$this->param['status'] = GeoConfirm::STATUS_FINISH;
$this->model->edit($this->param,['id'=>$this->param['id']]);
return $this->success(['id'=>$this->param['id']]);
}
}
... ...
... ... @@ -9,8 +9,12 @@
namespace App\Http\Logic\Aside\Geo;
use App\Console\Commands\Geo\GeoQuestionRes;
use App\Http\Logic\Aside\BaseLogic;
use App\Models\Geo\GeoConf;
use App\Models\Geo\GeoLink;
use App\Models\Geo\GeoQuestion;
use App\Models\Geo\GeoWritings;
use App\Models\Manage\ManageHr;
use App\Models\Project\KeywordPrefix;
use App\Models\Project\Project;
... ... @@ -97,4 +101,20 @@ class GeoLogic extends BaseLogic
return $this->success(['id'=>$id]);
}
/**
* @remark :获取统计数据
* @name :getCount
* @author :lyh
* @method :post
* @time :2025/10/30 10:39
*/
public function getCount()
{
//获取问题数量
$geo_question_count = GeoQuestion::selectRaw('SUM(JSON_LENGTH(question)) as total_count')->where('project_id',$this->param['project_id'])->value('total_count');
$geo_pr_count = GeoLink::where('project_id',$this->param['project_id'])->count();
$geo_writings_count = GeoWritings::where('project_id',$this->param['project_id'])->count();
return $this->success(['geo_writings_count'=>$geo_writings_count,'geo_pr_count'=>$geo_pr_count,'geo_question_count'=>$geo_question_count]);
}
}
... ...
... ... @@ -479,6 +479,14 @@ class SyncSubmitTaskService
){
throw new InquiryFilterException( '全局过滤');
}
if(
empty($data['data']['name']) &&
strlen($data['data']['message']??'') >= 8 &&
preg_match('/^[a-zA-Z]+$/', $data['data']['message']??'') &&
in_array($data['country']??'', ['荷兰', '俄罗斯'])
){
throw new InquiryFilterException( '全局过滤');
}
//全局过滤 ip 荷兰 有name、phone、email字段,但都是空
if(
in_array($data['country']??'', ['荷兰', '俄罗斯']) &&
... ...
... ... @@ -74,6 +74,9 @@ Route::post('selfSiteSsl', [\App\Http\Controllers\Api\SelfSiteController::class,
//创建301跳转任务
Route::any('/addRedirect',[\App\Http\Controllers\Api\NoticeController::class,'addRedirect']);
//获取所有有效域名
Route::any('/getAllDomain',[\App\Http\Controllers\Api\NoticeController::class,'getAllDomain']);
//关联域名
Route::post('/inquiry_relate_domain', [\App\Http\Controllers\Api\PrivateController::class, 'inquiry_relate_domain']);
// 通过域名获取项目人员配置
... ...
... ... @@ -591,11 +591,13 @@ Route::middleware(['aloginauth'])->group(function () {
Route::prefix('conf')->group(function () {
Route::any('/getConfig', [Aside\Geo\GeoController::class, 'getConfig'])->name('admin.geo_conf_getConfig');
Route::any('/saveConfig', [Aside\Geo\GeoController::class, 'saveConfig'])->name('admin.geo_conf_saveConfig');
Route::any('/getCount', [Aside\Geo\GeoController::class, 'getCount'])->name('admin.geo_conf_getCount');
});
//geo客户确认信息
Route::prefix('confirm')->group(function () {
Route::any('/saveConfirmContent', [Aside\Geo\GeoConfirmController::class, 'saveConfirmContent'])->name('admin.geo_confirm_saveConfirmContent');
Route::any('/getInfo', [Aside\Geo\GeoConfirmController::class, 'getInfo'])->name('admin.geo_confirm_getInfo');
Route::any('/saveInfo', [Aside\Geo\GeoConfirmController::class, 'saveInfo'])->name('admin.geo_confirm_saveInfo');
});
//geoai文章任务管理
Route::prefix('writing_task')->group(function () {
... ...