作者 刘锟

Merge remote-tracking branch 'origin/master' into akun

正在显示 55 个修改的文件 包含 1568 行增加232 行删除
... ... @@ -24,7 +24,7 @@ class RemainDay extends Command
protected $signature = 'remain_day';
/**
* @var 按上线时间统计
* @var 按优化时间统计
*/
protected $projectId = [
1434,1812,276,2414,2974
... ... @@ -86,7 +86,7 @@ class RemainDay extends Command
* @time :2025/4/2 10:48
*/
public function saveRemainDay(){
$list = $this->project->list(['extend_type'=>Project::TYPE_ZERO,'type'=>['in',[Project::TYPE_TWO,Project::TYPE_THREE,Project::TYPE_FOUR]]],'id',['id','type','uptime','remain_day','is_remain_today','pause_days','finish_remain_day']);
$list = $this->project->list(['extend_type'=>Project::TYPE_ZERO,'type'=>['in',[Project::TYPE_TWO,Project::TYPE_THREE,Project::TYPE_FOUR,Project::TYPE_SIX]]],'id',['id','type','uptime','remain_day','is_remain_today','pause_days','finish_remain_day','bm_finish_remain_day']);
foreach ($list as $item){
$deploy_build = $this->deployBuild->read(['project_id'=>$item['id']],['service_duration','seo_service_duration','plan','seo_plan']);
echo 'start->项目id:' . $item['id'] . '执行时间:'. date('Y-m-d H:i:s') . PHP_EOL;
... ... @@ -123,19 +123,15 @@ class RemainDay extends Command
//白帽版本的系统
if($deploy_build['seo_plan'] == 1){
if($deploy_build['seo_service_duration'] != 0){
if($item['uptime']){
$diff = time() - strtotime($item['uptime']);
$compliance_day = floor($diff / (60 * 60 * 24));
if($item['bm_finish_remain_day']){
$compliance_day = (int)$item['bm_finish_remain_day'];
$seo_remain_day = $deploy_build['seo_service_duration'] - $compliance_day;
}else{
$seo_remain_day = $deploy_build['seo_service_duration'];
}
// if($seo_remain_day < 0){
// $seo_remain_day = 0;
// }
if($deploy_build['plan'] == 0 && $seo_remain_day < 0 && $deploy_build['seo_service_duration'] != 0){//只有白帽版本的项目且剩余服务时常为0,放入未续费中
// $this->project->edit(['seo_remain_day'=>$seo_remain_day,'finish_remain_day'=>$compliance_day ?? 0,'extend_type'=>Project::TYPE_FIVE],['id'=>$item['id']]);
$this->project->edit(['seo_remain_day'=>$seo_remain_day,'finish_remain_day'=>$compliance_day ?? 0],['id'=>$item['id']]);
$this->project->edit(['seo_remain_day'=>$seo_remain_day,'bm_finish_remain_day'=>$compliance_day ?? 0],['id'=>$item['id']]);
}else{
//同时包括白帽版本+默认版本的项目
$this->project->edit(['seo_remain_day'=>$seo_remain_day],['id'=>$item['id']]);
... ...
... ... @@ -84,17 +84,27 @@ class GeoQuestionRes extends Command
GET_RESULT:
$error_num++;
try {
echo '执行次数:'.$error_num.PHP_EOL;
$this->output('执行时间:'.date('Y-m-d H:i:s').'->执行次数:'.$error_num.',执行平台:'.$platform.'执行的项目id:'.$taskInfo['project_id'].PHP_EOL);
if ($error_num >= 3) {
continue;
}
echo '执行平台:'.$platform.PHP_EOL;
if ($platform == 'Google AI Overview') {
// overview 数据结构不确定, 需要单独处理数据
$data = $geo_service->getGooglePlatformResult($question);
$result = $this->dealGoogleData($data);
} else {
$result = $geo_service->getAiPlatformResult($question, $platform);
switch ($platform){
case 'google_ai_overview':
// overview 数据结构不确定, 需要单独处理数据
$data = $geo_service->getGooglePlatformResult($question);
$result = $this->dealGoogleData($data);
break;
case 'deep_seek':
$data = $geo_service->getDeepSeekResult($question);
$result = $this->dealDeepSeek($data);
break;
case 'gpt-4o-mini':
$data = $geo_service->getDeepSeekResult($question,'gpt-4o-mini');
$result = $this->dealDeepSeek($data,'gpt-4o-mini');
break;
default:
$result = $geo_service->getAiPlatformResult($question, $platform);
break;
}
if (empty($result['text'])){
goto GET_RESULT;
... ... @@ -110,24 +120,19 @@ class GeoQuestionRes extends Command
$title = array_column(array_column($result['annotations'], 'url_citation'), 'title');
$hit_data = array_merge($url, $title, $hit_data);
}
// 命中关键词和路由
$hit_keyword = $hit_url = [];
$hit = 0;
if (!empty($taskInfo['keywords'])) {
$hit_keyword = $this->getKeywords($taskInfo['keywords'],$hit_data);
if (!empty($hit_keyword)) {
$hit++;
}
$hit_keyword = $this->getKeywords($taskInfo['keywords'],$hit_data);
if (!empty($hit_keyword['keywords'])) {
$hit++;
}
if (!empty($taskInfo['url'])) {
$hit_url = $this->getUrl($taskInfo['url'],$hit_data);
if (!empty($hit_url)) {
$hit++;
}
$keyword_num = json_encode($hit_keyword['keywords_num'] ?? [],true);
$hit_url = $this->getUrl($taskInfo['url'],$hit_data);
if (!empty($hit_url['url'])) {
$hit++;
}
echo 'MZ-url'.json_encode($hit_url).PHP_EOL;
$url_num = json_encode($hit_url['url_num'] ?? [],true);
// 保存数据结果
$geo_result = $geoResultModel->read(['project_id' => $taskInfo['project_id'], 'question_id' => $task_id, 'platform' => $platform, 'question' => $question],['id']);
$geo_result = $geoResultModel->read(['project_id' => $taskInfo['project_id'],'type' => $taskInfo['type'], 'question_id' => $task_id, 'platform' => $platform, 'question' => $question],['id']);
$save_data = [
'project_id' => $taskInfo['project_id'],
'question_id' => $task_id,
... ... @@ -135,17 +140,18 @@ class GeoQuestionRes extends Command
'platform' => $platform,
'question' => $question,
'en_question'=> $en_question,
'keywords' => json_encode($hit_keyword,true),//命中的关键词
'url' => json_encode($hit_url,true),//命中的网址
'text' => json_encode($result,true),
'hit' => $hit,
'keywords' => json_encode($hit_keyword['keywords'] ?? [],true),//命中的关键词
'url' => json_encode($hit_url['url'] ?? [],true),//命中的网址
'text' => json_encode($result ?? [],true),
'hit' => $hit ?? 0,
'keywords_num'=>$keyword_num ?? [],
'url_num'=>$url_num ?? [],
'created_at'=>date('Y-m-d H:i:s'),
'updated_at'=>date('Y-m-d H:i:s'),
];
// echo '当前数据INFO:'.json_encode($save_data,true).PHP_EOL;
if($geo_result === false){
$id= $geoResultModel->insertGetId($save_data);
echo '当前数据id:'.$id.PHP_EOL;
$geoResultModel->insertGetId($save_data);
}else{
$geoResultModel->edit($save_data, ['id' => $geo_result['id']]);
}
... ... @@ -169,15 +175,18 @@ class GeoQuestionRes extends Command
*/
public function getUrl($urlArr = [],$result_annotations = []){
$url = [];
$url_num = [];
if(!empty($urlArr)){
$str = implode(',',$result_annotations);
foreach ($urlArr as $u_item){
$count = substr_count($str, $u_item);
$url_num[$u_item] = $count;
if (str_contains($str, $u_item)) {
$url[] = $u_item;
}
}
}
return array_values(array_unique($url));
return ['url'=>$url,'url_num'=>$url_num];
}
/**
... ... @@ -189,15 +198,43 @@ class GeoQuestionRes extends Command
*/
public function getKeywords($keywordArr = [],$result_text = []){
$keywords = [];
$keywords_num = [];
if(!empty($keywordArr) && !empty($result_text)){
$str = implode(',',$result_text);
foreach ($keywordArr as $k_item){
$count = substr_count($str, $k_item);
$keywords_num[$k_item] = $count;
if (str_contains($str, $k_item)) {
$keywords[] = $k_item;
}
}
}
return $keywords;
return ['keywords'=>$keywords,'keywords_num'=>$keywords_num];
}
/**
* @remark :整合deepSeek
* @name :requestDeepSeek
* @author :lyh
* @method :post
* @time :2025/7/15 10:58
*/
public function dealDeepSeek($data,$model = 'DeepSeek'){
$result = [
'code' => 200,
'model' => $model,
'text' => '',
];
$texts = [];
if(!empty($data['text'])){
array_unshift($texts, $data['text']);
}
if(!empty($data['reasoning_content'])){
array_unshift($texts, $data['reasoning_content']);
}
$text = implode(PHP_EOL, $texts);
$result['text'] = $text;
return $result;
}
/**
... ... @@ -281,6 +318,7 @@ class GeoQuestionRes extends Command
return $task_id;
}
/**
* 输出日志
* @param $message
... ...
... ... @@ -21,6 +21,7 @@ use App\Models\Inquiry\ReInquiryText;
use App\Models\Project\InquiryFilterConfig;
use App\Models\Project\Project;
use App\Models\WebSetting\WebLanguage;
use GuzzleHttp\Exception\ConnectException;
use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
... ... @@ -343,6 +344,12 @@ class RelayInquiry extends Command
}
$val->status = $res ? ReInquiryForm::STATUS_SUCCESS : ReInquiryForm::STATUS_FORGO;
$val->save();
} catch (ConnectException $e) {
$val->status = ReInquiryForm::STATUS_FORGO;
$val->remark = mb_substr($e->getMessage(), 0, 200);
$val->save();
$this->logChannel()->info('执行询盘错误:',[$e->getMessage(), $e->getFile(), $e->getLine()]);
$this->output('执行询盘错误:' . $e->getMessage());
} catch (\Exception $e) {
$this->logChannel()->info('执行询盘错误:',[$e->getMessage(), $e->getFile(), $e->getLine()]);
$this->output('执行询盘错误:' . $e->getMessage());
... ... @@ -469,12 +476,12 @@ class RelayInquiry extends Command
}
//手机号过滤
$phone = $form->phone;
$filter_phone = $this->get_rand($this->filter_phone);
if($filter_phone == 0){
$phone = trim(str_replace("+", '', $phone));
}elseif($filter_phone == 1){
$phone = '';
}
// $filter_phone = $this->get_rand($this->filter_phone);
// if($filter_phone == 0){
// $phone = trim(str_replace("+", '', $phone));
// }elseif($filter_phone == 1){
// $phone = '';
// }
// 推送站点
$domain = $item['url'];
... ... @@ -510,7 +517,7 @@ class RelayInquiry extends Command
$pre = 0;
$start_time = time();
$seconds = rand(300, 7200); // 开始时间 从5-2小时后开始
$seconds = $this->delay_seconds($form->inquiry_date);
$email = '';
if($is_inquiry) {
$exists = ReInquiryDetail::where('re_website', $domain)->where('email', $form->email)->first();
... ... @@ -573,7 +580,7 @@ class RelayInquiry extends Command
$referrer = $this->getReferer($country_name, $lang);
$start_time = time();
$seconds = rand(300, 3000); // 开始时间 从5-50分钟后开始
$seconds = $this->delay_seconds($form->inquiry_date);
$exists = ReInquiryDetail::where('re_website', $domain)->where('email', $form->email)->first();
if($exists){
$this->output('转发站点邮件已存在');
... ... @@ -609,7 +616,7 @@ class RelayInquiry extends Command
$user_agent = $form->email ? Arr::random($this->pc_ua) : Arr::random($this->mobile_ua);
$start_time = time();
$seconds = rand(300, 3000); // 开始时间 从5-50分钟后开始
$seconds = $this->delay_seconds($form->inquiry_date);
$exists = ReInquiryDetail::where('re_website', $postid)->where('email', $form->email)->first();
if($exists){
$this->output('转发站点邮件已存在');
... ... @@ -860,7 +867,7 @@ class RelayInquiry extends Command
function getLinksFromSitemap($sitemapUrl) {
try {
//忽略cert证书 先下载到临时文件
$result = Http::withoutVerifying()->get($sitemapUrl)->body();
$result = Http::withoutVerifying()->timeout(10)->get($sitemapUrl)->body();
$tempFilePath = tempnam(sys_get_temp_dir(), 'remote_file_');
file_put_contents($tempFilePath, $result);
$xml = simplexml_load_file($tempFilePath);
... ... @@ -935,6 +942,23 @@ class RelayInquiry extends Command
}
/**
* @author zbj
* @date 2025/7/16
*/
public function delay_seconds($inquiry_date)
{
$seconds = rand(300, 2 * 3600); // 默认 从5分钟-2小时后开始
$time_diff = time() - strtotime($inquiry_date); //2小时前-24小时内的 当天发完
if ($time_diff > 2 * 3600 && $time_diff <= 24 * 3600) {
$seconds = rand(2 * 3600, 8 * 3600);
}
if ($time_diff > 24 * 3600) { //24小时前的 2天内发完
$seconds = rand(2 * 3600, 48 * 3600);
}
return $seconds;
}
/**
* @return \Psr\Log\LoggerInterface
*/
public function logChannel()
... ...
... ... @@ -79,11 +79,11 @@ class RankDataLog extends BaseCommands
$this->output('保存排名数据:ID'.$log->project_id . ',APINO' . $log->api_no);
if(Str::endsWith($log->api_no, '_bmseo')){
//白帽版
(new RankDataLogic())->save_rank_bmseo($log->project_id, $log->api_no, $res);
$is_compliance = (new RankDataLogic())->save_rank_bmseo($log->project_id, $log->api_no, $res);
}else{
$is_compliance = (new RankDataLogic())->save_rank($log->project_id, $log->api_no, $res, null, $log->lang);
$log->is_compliance = $is_compliance;
}
$log->is_compliance = $is_compliance;
$log->status = 1;
$log->save();
... ...
... ... @@ -49,12 +49,14 @@ class SyncMobile extends Command
}catch (\Exception $e){
echo date('Y-m-d H:i:s').':未拉起到数据'.PHP_EOL;
}
$saveData = [];
if(!empty($data)){
$mobileModel = new Mobile();
$mobileModel->truncate();
$data = json_decode($data, true);
$userModel = new User();
foreach ($data as $mobile){
$saveData[] = $mobile;
$param = [
'mobile'=>$mobile,
'created_at'=>date('Y-m-d H:i:s')
... ... @@ -63,22 +65,21 @@ class SyncMobile extends Command
//查看当前用户是否存在
$info = $userModel->read(['mobile'=>$mobile,'project_id'=>1],['id']);
if($info === false){
$data = [
$userModel->add([
'mobile'=>$mobile,
'password'=>base64_encode(md5('123456')),
'project_id'=>1,
'name'=>$mobile,
'type'=>$userModel::TYPE_ONE
];
$userModel->add($data);
]);
}
}
$data[] = '13083988828';
$saveData[] = '13083988828';
$managerModel = new Manage();
$mobileArr = $managerModel->selectField(['status'=>1],'mobile');
$data = array_values(array_unique(array_merge($data,$mobileArr)));
$userModel->edit(['status'=>1],['project_id'=>1,'mobile'=>['not in',$data]]);
$userModel->edit(['status'=>0],['project_id'=>1,'mobile'=>['in',$data]]);
$saveData = array_values(array_unique(array_merge($saveData,$mobileArr)));
$userModel->edit(['status'=>1],['project_id'=>1,'mobile'=>['not in',$saveData]]);
$userModel->edit(['status'=>0],['project_id'=>1,'mobile'=>['in',$saveData]]);
}
echo 'end.'.PHP_EOL;
return true;
... ...
... ... @@ -47,8 +47,14 @@ class WorkchatMessageSend extends Command
$task->status = MessagePush::STATUS_SUCCESS;
}catch (ConnectionException $e){
$this->output('推送消息' . $task->id . '超时');
$task->status = MessagePush::STATUS_ERROR;
$task->remark = '请求超时';
if ($task->retry < 3) {
$task->status = MessagePush::STATUS_PENDING;
$task->retry = $task->retry + 1;
$task->send_time = date('Y-m-d H:i:s', strtotime('+ ' . $task->retry . ' minute'));
} else {
$task->status = MessagePush::STATUS_ERROR;
$task->remark = '请求超时';
}
}catch (\Exception $e){
$this->output('推送消息' . $task->id . '失败:' . $e->getMessage());
$task->status = MessagePush::STATUS_ERROR;
... ...
... ... @@ -4,12 +4,16 @@ namespace App\Console\Commands\Tdk;
use App\Helper\Arr;
use App\Models\Blog\BlogCategory;
use App\Models\CustomModule\CustomModuleCategory;
use App\Models\News\News;
use App\Models\News\NewsCategory;
use App\Models\Product\Category;
use App\Models\Product\Keyword;
use App\Models\Project\KeywordPrefix;
use App\Models\Project\Project;
use App\Models\Project\ProjectUpdateTdk;
use App\Models\Template\BCustomTemplate;
use App\Services\ProjectServer;
use App\Utils\LogUtils;
use Illuminate\Console\Command;
... ... @@ -55,11 +59,14 @@ class RerunSeoTdk extends Command
*/
public function handle()
{
$project_ids = Project::where('type', Project::TYPE_TWO)->pluck('id')->toArray();
$where = [
'id' => 624
];
$project_ids = Project::where('type', Project::TYPE_TWO)->where($where)->pluck('id')->toArray();
foreach ($project_ids as $project_id){
try {
ProjectServer::useProject($project_id);
$this->judgeAnomalies($project_id);
$this->changeCompanyName($project_id);
DB::disconnect('custom_mysql');
}catch (\Exception $e){
dump($e->getMessage());
... ... @@ -68,6 +75,25 @@ class RerunSeoTdk extends Command
}
/**
* 换了公司英文名的
* @author zbj
* @date 2025/7/18
*/
public function changeCompanyName($project_id){
$row1 = BCustomTemplate::where('description', 'like', '%BlueQ Biotechnology%')->update(['description' => '']);
$row2 = Category::where('seo_des', 'like', '%BlueQ Biotechnology%')->update(['seo_des' => '']);
$row3 = Keyword::where('seo_description', 'like', '%BlueQ Biotechnology%')->update(['seo_description' => '']);
$row4 = Keyword::where('keyword_content', 'like', '%BlueQ Biotechnology%')->update(['keyword_content' => '']);
$row5 = BlogCategory::where('seo_des', 'like', '%BlueQ Biotechnology%')->update(['seo_des' => '']);
$row6 = NewsCategory::where('seo_des', 'like', '%BlueQ Biotechnology%')->update(['seo_des' => '']);
$row7 = CustomModuleCategory::where('seo_description', 'like', '%BlueQ Biotechnology%')->update(['seo_description' => '']);
dump($row1,$row2,$row3,$row4,$row5,$row6,$row7);
}
/**
* 判断seo_title 前缀有wholesale或cheap或buy的词,后缀也有 manufacturer,factory,exporter,company
* 判断关键词最后一个词是前缀的词,前后缀都不拼
* @author zbj
... ...
... ... @@ -56,65 +56,99 @@ class FetchTicketProjects extends Command
*/
public function fetchV5()
{
# pm 项目经理 assm 售后服务经理
$response = Http::get('https://www.quanqiusou.cn/extend_api/webs/globalso_all.php');
if ($response->status() == 200) {
$items = $response->json();
foreach ($items as $item) {
# V5: 版本号+postid
$uuid = md5("V5{$item['postid']}");
$project = TicketProject::where('uuid', $uuid)->first();
$assm_id = Manage::where('name', $item['assm'])->value('id') ?? Manage::where('name', '张鸿飞')->value('id') ?? 0; //售后服务经理
$seom_id = Manage::where('name', $item['yhs'])->value('id') ?? Manage::where('name', '陶婵')->value('id') ?? 0; //优化师
// 如果 $item['cate'] 包含”推广“字符,则 $engineer_name = $item['assm']
/**
* 第一负责人逻即说明:
* 优化推广项目:找售后服务经理??鸿飞
* 建站类项目: 找杨长远
*/
$engineer_id = (strpos($item['cate'], '推广') !== false) ? $assm_id : Manage::where('name', '杨长远')->value('id') ?? 0;
$page = 1;
$postids = [];
$fields = [
'post_id' => $item['postid'],
'company_name' => $item['company'],
'title' => $item['title'],
'engineer_id' => $engineer_id, // 第一负责人
'assm_id' => $assm_id,
'seom_id' => $seom_id,
'website' => $item['main_url'] ?? '',
'test_website' => $item['test_url'] ?? '',
'is_del' => 0,
'plan' => $item['plan'] ?? '',
'project_cate' => 1,
];
if (!$project) {
$new = new TicketProject();
$new->uuid = $uuid;
$new->version = 5;
$new->table_id = 0;
foreach ($fields as $k => $v) {
$new->$k = $v;
}
$new->save();
} else {
$changed = false;
foreach ($fields as $k => $v) {
if ($project->$k != $v) {
$project->$k = $v;
$changed = true;
while (true) {
$response = Http::get('https://www.quanqiusou.cn/extend_api/webs/globalso_all.php?page=' . $page);
if ($response->status() == 200) {
$resp_json = $response->json();
$items = $resp_json['data'] ?? [];
if (empty($items))
{
echo now() . " | INFO | V5: not found items on page $page \n";
break;
}
foreach ($items as $item) {
# V5: 版本号+postid
$uuid = md5("V5{$item['postid']}");
$project = TicketProject::where('uuid', $uuid)->first();
// 项目状态, 根据 $item['cate'] 判断,建站中,建站客户,推广
if (strpos($item['cate'], '推广') !== false)
$status=3; // 推广
elseif ($item['cate'] == "建站客户")
$status=2; // 建站客户
elseif ($item['cate'] == "建站中")
$status=1; // 建站中
$assm_id = Manage::where('name', $item['assm'])->value('id') ?? Manage::where('name', '张鸿飞')->value('id') ?? 0; //售后服务经理
$seom_id = Manage::where('name', $item['yhs'])->value('id') ?? Manage::where('name', '陶婵')->value('id') ?? 0; //优化师
$pm_id = Manage::where('name', $item['pm'])->value('id') ?? Manage::where('name', '李洁玉')->value('id') ?? 0; // 项目经理
/**
* 第一负责人逻即说明:
* 优化推广项目:找售后服务经理??鸿飞
* 建站中:项目经理
* 建站完成:杨长远
*/
if ($status == 3)
$engineer_id = $assm_id; // 推广类项目找售后服务经理
elseif ($status == 2)
$engineer_id = Manage::where('name', '杨长远')->value('id') ?? 0; //建站完成
elseif ($status == 1)
$engineer_id = $pm_id; // 建站中找项目经理
$fields = [
'post_id' => $item['postid'],
'company_name' => $item['company'],
'title' => $item['title'] . " - V5",
'engineer_id' => $engineer_id, // 第一负责人
'assm_id' => $assm_id,
'seom_id' => $seom_id,
'website' => $item['main_url'] ?? '',
'test_website' => $item['test_url'] ?? '',
'is_del' => 0,
'plan' => $item['plan'] ?? '',
'project_cate' => 1,
'pm_id' => $pm_id,
'status' => $status, // 项目状态
'wechat_group_id' => $item['wx_id']
];
if (!$project) {
$new = new TicketProject();
$new->uuid = $uuid;
$new->version = 5;
$new->table_id = 0;
foreach ($fields as $k => $v) {
$new->$k = $v;
}
$new->save();
} else {
$changed = false;
foreach ($fields as $k => $v) {
if ($project->$k != $v) {
$project->$k = $v;
$changed = true;
}
}
if ($changed) {
$project->save();
}
}
if ($changed) {
$project->save();
}
echo now() . " | INFO | V5: {$item['postid']} {$item['company']} fetch ok \n";
}
$page++;
$postids = array_merge($postids, collect($items)->pluck('postid')->toArray());
}
$postids = collect($items)->pluck('postid')->toArray();
}
if ($postids)
{
// 软删除 gl_ticket_projects 中不存在的项目
TicketProject::where('version', 5)
->whereNotIn('post_id', $postids)
->update(['is_del' => 1]);
echo date("Y-m-d H:i:s") . " V5: fetch completed, total " . count($items) . " items\n";
}
}
... ... @@ -142,26 +176,35 @@ class FetchTicketProjects extends Command
foreach ($items as $item) {
$uuid = md5("V6{$item->id}");
$project = TicketProject::where('uuid', $uuid)->first();
// 项目状态
if ($item->type == Project::TYPE_ONE)
$status = 1; // 建站中
elseif ($item->type == Project::TYPE_THREE)
$status = 2; // 建站完成
else
$status = 3; // 推广找售后服务经理
// 售后服务经理
$assm_id = collect([
ManageHr::find($item->deploy_optimize->manager_mid)->manage_id ?? 0,
ManageHr::find($item->deploy_optimize->tech_leader)->manage_id ?? 0,
8, //张鸿飞
])->first(fn($v) => $v !== null && $v !== 0, 0);
// 优化师
$optimist_mid = ManageHr::find($item->deploy_optimize->optimist_mid) ? ManageHr::find($item->deploy_optimize->optimist_mid)->manage_id : 0;
$seom_id = $optimist_mid ? $optimist_mid : $assm_id;
/**
* 第一负责人逻辑
* 建站类项目:找杨长远
* 推广类:找售后
*/
if ($item->type == Project::TYPE_THREE) {
$engineer_id = Manage::where('name', '杨长远')->value('id') ?? 0; // 建站类项目找杨长远
}else {
// 其他找售后服务经理
$engineer_id = $assm_id;
}
$seom_id = ManageHr::find($item->deploy_optimize->optimist_mid) ? ManageHr::find($item->deploy_optimize->optimist_mid)->manage_id : 0;
// 项目经理
$pm_id = ManageHr::find($item->deploy_build->manager_mid)->manage_id ?? ManageHr::where('name', '李洁玉')->value('manage_id') ?? 0;
// 第一负责人
if ($status == 1)
$engineer_id = $pm_id; // 建站中找项目经理
elseif ($status == 2)
$engineer_id = Manage::where('name', '杨长远')->value('id') ?? 0; // 建站完成找杨长远
else
$engineer_id = $assm_id; // 推广找售后服务经理
$is_del = (
$item->extend_type == 5
|| $item->type == 8
... ... @@ -171,7 +214,7 @@ class FetchTicketProjects extends Command
$fields = [
'company_name' => $item->company,
'title' => $item->title,
'title' => $item->title . " - V6",
'assm_id' => $assm_id,
'seom_id' => $seom_id,
'engineer_id' => $engineer_id,
... ... @@ -181,10 +224,12 @@ class FetchTicketProjects extends Command
'version' => empty($item->version) ? 7 : $item->version, // 版本号
'plan' => $item->planMap()[$item->deploy_build->plan] ?? '',
'project_cate' => 2,
'wechat_group_id' => ProjectAssociation::where('project_id', $project->table_id)
'wechat_group_id' => ProjectAssociation::where('project_id', $item->id)
->where('status', ProjectAssociation::STATUS_NORMAL)
->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT)
->value('friend_id')
->value('friend_id'),
'pm_id' => $pm_id,
'status' => $status, // 项目状态
];
if (!$project) {
$project = new TicketProject();
... ... @@ -260,7 +305,13 @@ class FetchTicketProjects extends Command
$seom_id = 0;
// 第一负责人
$engineer_id = $assm_id;
$is_del = 0;
if ($project_cate == 3)
$is_del = !empty($item["go_online"]);
else{
// 域途,以服务时间为准 $item['yutu_service_start_time'] 是开始时间 + 有效天数 yutu_planday
$is_del = !empty($item['yutu_service_start_time']) && !empty($item['yutu_planday'])
&& (strtotime($item['yutu_service_start_time']) + $item['yutu_planday'] * 86400) < time();
}
$fields = [
'company_name' => $item['company'],
... ...
... ... @@ -3,7 +3,9 @@
namespace App\Console\Commands\WorkOrder;
use App\Models\Manage\Manage;
use App\Models\Manage\ManageHr;
use App\Models\WorkOrder\TicketChat;
use App\Models\WorkOrder\TicketDing;
use App\Models\WorkOrder\TicketLog;
use App\Services\DingTalkService;
use Illuminate\Console\Command;
... ... @@ -56,7 +58,7 @@ class WorkOrderDing extends Command
sleep(3);
continue;
}
$mobile = $log->engineer->mobile;
$mobile = ManageHr::where('manage_id', $log->engineer_id)->value('mobile');
$response = Http::withBasicAuth(
env('DINGDING_BASIC_USER'),
env('DINGDING_BASIC_PASS')
... ... @@ -64,8 +66,10 @@ class WorkOrderDing extends Command
if ($response->status() == 200) {
$userid = $response->json()['data']['userid'];
$ding = new DingTalkService();
$created_at = date('m-d H', strtotime($log->ticket->created_at));
$plan_end_at = date('m-d H', strtotime($log->ticket->plan_end_at));
$resp = $ding->danliao(json_encode([
'text' => "您有新的工单(ID: {$log->ticket_id}),请及时处理!",
'text' => "您有新工单{$log->ticket_id}{$created_at}{$plan_end_at} 截止!",
'title' => 'AI协同工单 - ' . $log->ticket->project->title,
'picUrl' => 'https://hub.globalso.com/logocm.png',
'messageUrl' => 'https://oa.quanqiusou.cn/afterorder?project_id=' . $log->ticket->project->uuid,
... ... @@ -142,4 +146,54 @@ class WorkOrderDing extends Command
}
}
}
/**
* 消费 gl_ticket_dings 表的钉钉通知
*/
public function dingSql()
{
while (true) {
$tickDing = TicketDing::where('status', 0)->first();
if (!$tickDing) {
echo now() . " | INFO | 没有通知任务\n";
sleep(3);
continue;
}
try {
$mobiles = ManageHr::whereIn('manage_id', json_decode($tickDing->userIds))
->pluck('mobile')
->toArray();
$response = Http::withBasicAuth(
env('DINGDING_BASIC_USER'),
env('DINGDING_BASIC_PASS')
)->get('https://oa.cmer.com/api/dingding/usersByMobiles', [
'mobiles' => $mobiles,
]);
if ($response->status() == 200) {
$users = $response->json()['data'];
$userIds = [];
foreach ($users as $user) {
$userIds[] = $user['userid'];
}
$ding = new DingTalkService();
$resp = $ding->danliao($tickDing->msgParam, $userIds, $tickDing->msgKey);
$tickDing->status = 1;
$tickDing->save();
echo now() . " | INFO | gl_ticket_dings ID: {$tickDing->id} 通知成功\n";
}else
{
$tickDing->status = 2; // 标记为失败
$tickDing->errorMsg = '钉钉用户查询失败';
$tickDing->save();
echo now() . " | ERROR | gl_ticket_dings ID: {$tickDing->id} 通知失败\n";
}
}catch (\Exception $exception){
echo now() . " | ERROR | gl_ticket_dings ID {$tickDing->id} {$exception->getMessage()} {$exception->getTraceAsString()} \n";
$ding->status = 2; // 标记为失败
$ding->errorMsg = $exception->getMessage();
$ding->save();
}
}
}
}
... ...
... ... @@ -24,6 +24,7 @@ class Kernel extends ConsoleKernel
$schedule->command('sync_channel')->dailyAt('06:00')->withoutOverlapping(1); // 渠道信息,每天执行一次
$schedule->command('inquiry_count')->dailyAt('01:00')->withoutOverlapping(1); // 询盘统计数据,每天凌晨执行一次
$schedule->command('share_user')->dailyAt('01:20')->withoutOverlapping(1);// 每天凌晨1点执行一次
$schedule->command('ai_domain')->dailyAt('01:20')->withoutOverlapping(1);// 每天凌晨1点执行一次(同步ai域名)
$schedule->command('last_inquiry')->dailyAt('04:00')->withoutOverlapping(1);// 最近一次询盘信息
$schedule->command('update_seo_tdk_crontab')->dailyAt('20:00')->withoutOverlapping(1); //更新上线项目TDK
$schedule->command('sync_manager')->dailyAt('01:00')->withoutOverlapping(1); //TODO::手机号码同步 每天执行一次
... ...
... ... @@ -757,6 +757,9 @@ class Translate
return '';
}
$result = self::translate($texts, $tls);
if(!isset($result[0]['texts']) || empty($result[0]['texts'])){
$result = self::translate($texts, $tls);
}
return $result[0]['texts'] ?? '';
}
/**
... ...
... ... @@ -32,12 +32,6 @@ if (!function_exists('generateRoute')) {
$string = $string[0];
}
$sign = str_replace(".", "", trim(strtolower(preg_replace('/[^\w.]+/', '-', trim($string))), '-'));
if (strpos($sign, 'well-known-') === 0) {
$sign = substr($sign, strlen('well-known-'));
}
if(empty($sign)){
$sign = 'u';
}
return $sign;
}
}
... ...
... ... @@ -8,10 +8,8 @@ use App\Http\Requests\Api\WorkOrder\TicketStoreRequest;
use App\Models\WorkOrder\TicketLog;
use App\Models\WorkOrder\TicketProject;
use App\Models\WorkOrder\Tickets;
use Darabonba\GatewaySpi\Models\InterceptorContext\response;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
class TicketController extends BaseController
{
... ... @@ -29,8 +27,11 @@ class TicketController extends BaseController
$size = (int)$request->input('size', 10);
$tickets = Tickets::with([
'project:*',
'logs.engineer:id,name',
'project.pm',
'project.assm',
'project.seom',
'project.first_engineer',
'logs.engineer',
])
->where('project_id', $project->id)
// ->where('submit_side', 2)
... ... @@ -85,9 +86,7 @@ class TicketController extends BaseController
$ticket->submit_side = 2; // 2 for B-side submission
$ticket->submit_username = $request->input('submit_username');
$ticket->save();
$log = new TicketLog();
$log->engineer_id = $project->engineer_id; // 默认第一负责人
$ticket->logs()->save($log);
$ticket->saveEngineers([$project->engineer_id]);
$project->pushWechatGroupMsg("客户新增了工单(ID:{$ticket->id}),请及时处理!");
return $ticket;
});
... ... @@ -143,13 +142,13 @@ class TicketController extends BaseController
public function projectInfo($project_id)
{
$project = TicketProject::with([
'projectV6:id,company',
'assm:id,name',
'seom:id,name',
'first_engineer:id,name',
'pm',
'assm',
'seom',
'first_engineer',
])
->where('uuid', $project_id)->first();
if (!$project) return $this->response('未找到项目', 404);
if (!$project) return response()->json(['message' => '未找到对应的工单项目'], 404);
return response()->json(['data' => $project]);
}
... ...
... ... @@ -6,6 +6,7 @@ use App\Enums\Common\Code;
use App\Http\Controllers\Aside\BaseController;
use App\Http\Logic\Aside\Domain\DomainInfoLogic;
use App\Http\Requests\Aside\Domain\DomainInfoRequest;
use App\Models\Devops\ServersIp;
use App\Models\Domain\CountryCode;
use App\Models\Domain\DomainInfo;
use App\Models\Project\Project;
... ... @@ -34,17 +35,24 @@ class DomainInfoController extends BaseController
$lists = $domainModel->lists($this->map,$this->page,$this->row,$this->order);
if(!empty($lists)){
$project_model = new Project();
$serverIpModel = new ServersIp();
foreach ($lists['list'] as $k=>$v){
$company = '';
$pro_info = $project_model->read(['id'=>$v['project_id']],'company');
if($pro_info){
$company = $pro_info['company'];
$v['company'] = '';
$v['ip_domain'] = [];
$pro_info = $project_model->read(['id'=>$v['project_id']],['company','serve_id']);
if($pro_info !== false){
$v['company'] = $pro_info['company'];
if(!empty($pro_info['serve_id'])){
$serveInfo = $serverIpModel->read(['id'=>$pro_info['serve_id']],['domain','ip']);
$v['ip_domain'] = $serveInfo;
}
}
$lists['list'][$k]['company'] = $company;
$lists['list'][$k] = $v;
}
}
$lists['y_status'] = $domainModel->counts(['status'=>1]);
$lists['n_status'] = $domainModel->counts(['status'=>0]);
$lists['total_count'] = $domainModel->counts();
$lists['y_count'] = $domainModel->counts(['status'=>1]);
$lists['n_count'] = $domainModel->counts(['status'=>0]);
return $this->response('success', Code::SUCCESS, $lists);
}
... ...
<?php
/**
* @remark :
* @name :GeoArticleController.php
* @author :lyh
* @method :post
* @time :2025/7/14 16:19
*/
namespace App\Http\Controllers\Aside\Geo;
use App\Enums\Common\Code;
use App\Http\Controllers\Aside\BaseController;
use App\Http\Logic\Aside\Geo\GeoArticleLogic;
use Illuminate\Http\Request;
/**
* @remark :GEO文章列表数据
* @name :GeoArticleController
* @author :lyh
* @method :post
* @time :2025/7/14 16:19
*/
class GeoArticleController extends BaseController
{
public function __construct(Request $request)
{
parent::__construct($request);
$this->logic = new GeoArticleLogic();
}
/**
* @remark :文章列表数据
* @name :lists
* @author :lyh
* @method :post
* @time :2025/7/14 16:22
*/
public function lists(){
$this->request->validate([
'project_id'=>'required',
],[
'project_id.required' => '项目ID不能为空',
]);
$lists = $this->logic->getArticleList($this->map,$this->page,$this->row,$this->order);
$this->response('success',Code::SUCCESS,$lists);
}
/**
* @remark :获取数据详情
* @name :info
* @author :lyh
* @method :post
* @time :2025/7/14 17:05
*/
public function info(){
$this->request->validate([
'id'=>'required',
],[
'id.required' => 'ID不能为空',
]);
$data = $this->logic->getArticleInfo();
$this->response('success',Code::SUCCESS,$data);
}
/**
* @remark :更新单个数据
* @name :edit
* @author :lyh
* @method :post
* @time :2025/7/15 10:31
*/
public function edit(){
$this->request->validate([
'id'=>'required',
'url'=>'required',
'filename'=>'required',
],[
'id.required' => 'ID不能为空',
'url.required' => '链接不能为空',
'filename.required' => '文件名称不能为空',
]);
$data = $this->logic->editArticle();
$this->response('success',Code::SUCCESS,$data);
}
/**
* @remark :保存数据
* @name :save
* @author :lyh
* @method :post
* @time :2025/7/14 17:05
* @param :data->数组(filename:文件名称 url:链接)
* @json :测试数据 {"project_id": 1,"data": [{"url": "/upload/m/file/2023-09/6513d00870e0821127.pdf","filename": "测试文件1"}]}
*/
public function save(){
$this->request->validate([
'project_id'=>'required',
'data'=>'required|array'
],[
'project_id.required' => '项目ID不能为空',
'data.required' => '数据详情不能为空',
'data.array' => '数据详情为数组',
]);
$data = $this->logic->saveArticle();
$this->response('success',Code::SUCCESS,$data);
}
/**
* @remark :删除数据
* @name :del
* @author :lyh
* @method :post
* @time :2025/7/14 17:05
*/
public function del(){
$this->request->validate([
'ids'=>'required|array',
],[
'ids.required' => 'IDs不能为空',
'ids.array' => '数据详情为数组',
]);
$data = $this->logic->delArticle();
$this->response('success',Code::SUCCESS,$data);
}
}
... ...
<?php
/**
* @remark :
* @name :GeoLinkController.php
* @author :lyh
* @method :post
* @time :2025/7/14 16:17
*/
namespace App\Http\Controllers\Aside\Geo;
use App\Enums\Common\Code;
use App\Http\Controllers\Aside\BaseController;
use App\Http\Logic\Aside\Geo\GeoLinkLogic;
use Illuminate\Http\Request;
/**
* @remark :geo权威新闻(链接数据)
* @name :GeoLinkController
* @author :lyh
* @method :post
* @time :2025/7/14 16:18
*/
class GeoLinkController extends BaseController
{
public function __construct(Request $request)
{
parent::__construct($request);
$this->logic = new GeoLinkLogic();
}
/**
* @remark :获取链接数据列表
* @name :lists
* @author :lyh
* @method :post
* @time :2025/7/15 9:14
*/
public function lists(){
$this->request->validate([
'project_id'=>'required',
],[
'project_id.required' => '项目ID不能为空',
]);
$lists = $this->logic->getLinkList($this->map,$this->page,$this->row,$this->order);
$this->response('success',Code::SUCCESS,$lists);
}
/**
* @remark :获取数据详情
* @name :info
* @author :lyh
* @method :post
* @time :2025/7/15 9:15
*/
public function info(){
$this->request->validate([
'id'=>'required',
],[
'id.required' => 'ID不能为空',
]);
$data = $this->logic->getLinkInfo();
$this->response('success',Code::SUCCESS,$data);
}
/**
* @remark :保存数据
* @name :save
* @author :lyh
* @method :post
* @time :2025/7/15 9:17
*/
public function save(){
$this->request->validate([
'project_id'=>'required',
'data'=>'required|array'
],[
'project_id.required' => '项目ID不能为空',
'data.required' => '数据详情不能为空',
'data.array' => '数据详情为数组',
]);
$data = $this->logic->saveLink();
$this->response('success',Code::SUCCESS,$data);
}
/**
* @remark :删除数据
* @name :del
* @author :lyh
* @method :post
* @time :2025/7/15 9:17
*/
public function del(){
$this->request->validate([
'ids'=>'required|array',
],[
'ids.required' => 'IDs不能为空',
'ids.array' => '数据详情为数组',
]);
$data = $this->logic->delLink();
$this->response('success',Code::SUCCESS,$data);
}
}
... ...
... ... @@ -33,9 +33,11 @@ use App\Models\Project\ProcessRecords;
use App\Models\Project\Project;
use App\Models\Project\ProjectUpdateTdk;
use App\Models\Project\RenewLog;
use App\Models\ProjectAssociation\ProjectAssociation;
use App\Models\RankData\RankData;
use App\Models\Task\Task;
use App\Models\WebSetting\WebLanguage;
use App\Models\WorkOrder\TicketProject;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
... ... @@ -70,23 +72,13 @@ class ProjectController extends BaseController
if(!empty($lists) && !empty($lists['list'])){
foreach ($lists['list'] as $k => $v){
$v = $this->handleParam($v);
// 组装 工单UUID
$ticketProject = null;
$ticketProject = DB::table('gl_ticket_projects')
->where('table_id', $v['id'])
->where('project_cate', 2)
->first();
$v['uuid'] = $ticketProject ? $ticketProject->uuid : null;
// 组装 工单UUID END
$lists['list'][$k] = $v;
}
}
$this->response('success',Code::SUCCESS,$lists);
}
/**
* 需要查询的字段
* @return array
... ... @@ -458,6 +450,8 @@ class ProjectController extends BaseController
$item['task_finish_num'] = Task::getNumByProjectId($item['id'], Task::STATUS_DOWN);
$item['task_pending_num'] = Task::getNumByProjectId($item['id'], [Task::STATUS_DONGING, Task::STATUS_WAIT]);
$item['collect_time'] = $item['is_upgrade'] ? UpdateLog::getProjectUpdate($item['id']) : '';
$item['uuid'] = TicketProject::where('table_id', $item['id'])->where('project_cate', 2)->value('uuid') ?? null;
$item['friend_id'] = ProjectAssociation::where('project_id', $item['id'])->where('status', ProjectAssociation::STATUS_NORMAL)->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT)->value('friend_id') ?? null;
return $item;
}
... ...
... ... @@ -86,9 +86,15 @@ class ProjectAssociationController extends BaseController
$this->response('success', Code::SERVER_ERROR);
}
$cache = isset($cache);
$search = request()->input('search');
try {
$search = request()->input('search');
// $result = ProjectAssociationServices::getInstance()->getAiccWechatLists($isRes, $app, $cache);
$result = ProjectAssociationServices::getInstance()->getWorkChatRoomList($search, $isRes->friend_id);
$result = ProjectAssociationServices::getInstance()->getWorkChatRoomList($search, $isRes->friend_id);
}catch (\Exception $e){
$result = [];
}
$result['info'] = [
'friend_id' => $isRes->friend_id ?? 0
];
... ...
... ... @@ -94,6 +94,13 @@ class AdsController extends BaseController
foreach ($item['target'] as $k=>$target){
$repeat = ReInquiryTask::where('target', 'like', '%"'.$target['url'].'"%')->where('id', '<>', $item['id'])->first();
$item['target'][$k]['is_repeat'] = $repeat ? 1 : 0;
$item['target'][$k]['is_ad_fee'] = 0;
if($item['target'][$k]['is_v6']){
$project = Project::getProjectByDomain($item['target'][$k]['url']);
if($project && $project->deploy_build){
$item['target'][$k]['is_ad_fee'] = $project->deploy_build->ads_price ? 1 : 0;
}
}
}
$item['cost'] = ReInquiryCost::getCostByAdIds($item['ad_id']);
}
... ... @@ -395,10 +402,14 @@ class AdsController extends BaseController
$is_v6 = 1;
}
}
$item['project_id'] = 0;
$item['is_ad_fee'] = 0;
if($is_v6){
$item['project_id'] = DomainInfo::where('domain', $item['domain'])->value('project_id') ?: 0;
}else{
$item['project_id'] = 0;
$project = Project::getProjectByDomain($item['domain']);
if($project && $project->deploy_build){
$item['project_id'] = $project['id'];
$item['is_ad_fee'] = $project->deploy_build->ads_price ? 1 : 0;
}
}
}
... ...
... ... @@ -8,7 +8,7 @@ use App\Http\Requests\Aside\WorkOrder\AsideTicketStoreRequest;
use App\Http\Requests\Aside\WorkOrder\AsideTicketListRequest;
use App\Http\Requests\Aside\WorkOrder\AsideTicketUpdateRequest;
use App\Http\Requests\Aside\WorkOrder\TicketProjectListRequest;
use App\Models\Workchat\MessagePush;
use App\Models\Manage\ManageHr;
use App\Models\WorkOrder\TicketLog;
use App\Models\WorkOrder\TicketProject;
use App\Models\WorkOrder\Tickets;
... ... @@ -26,8 +26,8 @@ class AsideTicketController extends BaseController
{
$validated = $request->validated();
$lists = Tickets::with([
'logs.engineer:id,name',
'project',
'logs.engineer',
'project.pm',
])
->when(!empty($validated['engineer_id']), function ($query) use ($validated) {
// 查 gl_tickets 表 submit_user_id 或 gl_ticket_logs 表 engineer_id
... ... @@ -51,6 +51,10 @@ class AsideTicketController extends BaseController
$status = $request->input('status');
return $query->where('status', $status);
})
->when($request->input('star') !== null, function ($query) use ($request) {
$star = $request->input('star');
return $query->where('star', $star);
})
->when($request->input('search'), function ($query) use ($request) {
// search 查 gl_tickets.title 或 gl_ticket_projects.title 或 gl_ticket_projects.company_name
$search = $request->input('search');
... ... @@ -92,7 +96,31 @@ class AsideTicketController extends BaseController
public function projectList(TicketProjectListRequest $request)
{
$validated = $request->validated();
$dept_id = ManageHr::where('manage_id', $this->manage['id'])
->value('dept_id');
$lists = TicketProject::where('is_del', 0)
->when(($this->manage['role'] != 1 && $dept_id != 5), function ($query) use ($dept_id) {
/**
* 超管看所有项目 $this->manage['role']=1
* 全球搜: 技术部ID 1、售后部ID 2
* 超迹AI: AICC技术部 ID 4
* 域途:域途运营部 ID 17
*/
if (in_array($dept_id, [1, 2])) {
// V5 V6
return $query->whereIn('project_cate', [1, 2]);
}elseif ($dept_id == 4) {
// 超迹AI
return $query->where('project_cate', 3);
}elseif ($dept_id == 17){
// 域途
return $query->where('project_cate', 4);
}else{
// 其他部门,不允许看数据
return $query->where('id', 0); // 返回空结果
}
})
->when(!empty($validated['search']), function ($query) use ($validated) {
// 查找项目名称或公司名称
$search = $validated['search'];
... ... @@ -141,14 +169,15 @@ class AsideTicketController extends BaseController
$ticket->submit_side = 1; // 1 for A-side submission
$ticket->submit_user_id = $this->manage['id'];
$ticket->submit_username = $this->manage['name'];
$ticket->star = $request->input('star', 3);
$ticket->plan_end_at = $request->input('plan_end_at', null);
$ticket->save();
// A 端提工单,都是针对客户提的需求等开发任务;比如翻译,修改页面等。。。
foreach ($request->input('engineer_ids', []) as $engineer_id) {
$log = new TicketLog();
$log->engineer_id = $engineer_id;
$ticket->logs()->save($log);
}
$project->pushWechatGroupMsg("创贸({$ticket->submit_username})新增了工单(ID:{$ticket->id}),请及时处理!");
// 分配工单参与人
$ticket->saveEngineers($request->input('engineer_ids', []));
$nickname = ManageHr::where('manage_id', $this->manage['id'])->value('nickname') ?? mb_substr($ticket->submit_username, 0, 1) . '**';
if ($project->wechat_switch)
$project->pushWechatGroupMsg("创贸({$nickname})新增了工单(ID:{$ticket->id}),请及时处理!");
return $ticket;
});
$this->response('success', Code::SUCCESS, $result->toArray());
... ... @@ -186,27 +215,32 @@ class AsideTicketController extends BaseController
// 开始修改
$result = DB::transaction(function () use ($request, $ticket) {
if ($request->input('engineer_ids'))
{
// 有邀请工程师协同处理
foreach ($request->input('engineer_ids') as $engineer_id)
{
try {
// 利用唯一索引去重
$new_log = new TicketLog();
$new_log->engineer_id = $engineer_id;
$ticket->logs()->save($new_log);
}catch (\Exception $exception){}
}
}
$ticket->saveEngineers($request->input('engineer_ids'));
// 其他字段有提交数据才修改,比如star plan_end_at
if ($request->input('title'))
$ticket->title = $request->input('title');
if ($request->input('content'))
$ticket->content = $request->input('content');
if ($request->input('star'))
$ticket->star = $request->input('star');
if ($request->input('plan_end_at'))
$ticket->plan_end_at = $request->input('plan_end_at');
if ($request->input('status'))
$ticket->status = $request->input('status');
$ticket->reply = $request->input('reply', null);
$ticket->status = $request->input('status', $ticket->status);
if ($ticket->status == Tickets::STATUS_COMPLETED)
{
// 完成工单,把子任务里面未完成的工单改为完成
$ticket->end_at = now();
$ticket->logs()->where('status', '<', TicketLog::STATUS_COMPLETED)
$ticket->logs()->where('status', '<', TicketLog::STATUS_COMPLETED)->where('is_engineer', 1)
->update(['status' => TicketLog::STATUS_COMPLETED, 'end_at' => now()]);
// 推动微信通知
$project = $ticket->project;
if ($project->wechat_switch)
$project->pushWechatGroupMsg("工单(ID:{$ticket->id})已全部完成,请访问查看详情!");
$ticket->pushDing('finish');
}
$ticket->save();
return $ticket;
... ... @@ -241,7 +275,8 @@ class AsideTicketController extends BaseController
if (empty($project->wechat_group_id)) {
$this->response('该工单没有绑定的企微群', Code::USER_MODEL_NOTFOUND_ERROE);
}
$project->pushWechatGroupMsg();
if ($project->wechat_switch)
$project->pushWechatGroupMsg();
$this->response('success', Code::SUCCESS);
}
... ...
... ... @@ -78,17 +78,24 @@ class AsideTicketLogController extends BaseController
// 是否有未完成的子任务
$pending = $ticket->logs()
->where('status', '<', TicketLog::STATUS_COMPLETED)
->where('is_engineer', 1)
->count();
if ($pending)
{
$ticket->status = Tickets::STATUS_PROCESSING;
}else
{
$ticket->status = Tickets::STATUS_COMPLETED;
// 如果所有子任务都完成了,则将工单状态改为已完成
$ticket->end_at = now();
$project = $ticket->project;
$project->pushWechatGroupMsg("工单(ID:{$ticket->id})已全部完成,请访问查看详情!");
// todo 注意:建站期间的工单,所有人都完成后,不自动完成工单,需要项目经理验收修改工单状态
if (!($ticket->project->status == 1 && in_array($ticket->project->project_cate, [1, 2])))
{
$ticket->status = Tickets::STATUS_COMPLETED;
$ticket->end_at = now();
$project = $ticket->project;
if ($project->wechat_switch)
$project->pushWechatGroupMsg("工单(ID:{$ticket->id})已全部完成,请访问查看详情!");
$ticket->pushDing('finish');
}
}
$ticket->save();
return $log;
... ...
... ... @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Aside\WorkOrder;
use App\Enums\Common\Code;
use App\Http\Controllers\Aside\BaseController;
use App\Http\Requests\Api\WorkOrder\TicketChatStoreRequest;
use App\Models\Manage\ManageHr;
use App\Models\WorkOrder\TicketChat;
use App\Models\WorkOrder\Tickets;
use Illuminate\Http\Request;
... ... @@ -64,7 +65,8 @@ class TicketChatController extends BaseController
$chat->manage_id = $this->manage['id'];
$chat->save();
$project = $ticket->project;
$project->pushWechatGroupMsg("{$chat->submit_username}对工单(ID:{$ticket->id})进行了补充,请及时查看处理!");
$nickname = ManageHr::where('manage_id', $this->manage['id'])->value('nickname') ?? mb_substr($ticket->submit_username, 0, 1) . '**';
$project->pushWechatGroupMsg("创贸({$nickname})对工单(ID:{$ticket->id})进行了补充,请及时查看处理!");
$this->response('success', Code::SUCCESS, $chat);
}
... ...
<?php
namespace App\Http\Controllers\Aside\WorkOrder;
use App\Enums\Common\Code;
use App\Http\Controllers\Aside\BaseController;
use App\Http\Controllers\Controller;
use App\Http\Requests\Aside\WorkOrder\TicketProjectUpdateRequest;
use App\Models\WorkOrder\TicketProject;
use Illuminate\Http\Request;
class TicketProjectController extends BaseController
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(TicketProjectUpdateRequest $request, $id)
{
$request->validated();
$project = TicketProject::where('uuid', $id)->first();
if (!$project) $this->response('Project not found', Code::USER_MODEL_NOTFOUND_ERROE);
if ($request->input('subtitle')) $project->subtitle = $request->input('subtitle');
$project->wechat_switch = $request->boolean('wechat_switch', true);
$project->save();
$this->response('Project updated successfully', Code::SUCCESS, $project);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}
... ...
... ... @@ -92,4 +92,46 @@ class GeoQuestionResController extends BaseController
$data = $this->logic->getResultInfo();
$this->response('success',Code::SUCCESS,$data);
}
/**
* @remark :统计数量
* @name :countQuantity
* @author :lyh
* @method :post
* @time :2025/7/21 10:58
*/
public function countQuantity(){
$data = $this->logic->countQuantity();
$data['platform'] = $this->logic->platformHitCount();
$this->response('success',Code::SUCCESS,$data);
}
/**
* @remark :获取搜索时间
* @name :getSearchDate
* @author :lyh
* @method :post
* @time :2025/7/21 16:35
*/
public function getSearchDate(){
$data = $this->logic->getSearchDate();
$this->response('success',Code::SUCCESS,$data);
}
/**
* @remark :获取搜索列表
* @name :getSearchList
* @author :lyh
* @method :post
* @time :2025/7/21 16:47
*/
public function getSearchList(){
$this->request->validate([
'created_at'=>'required',
],[
'created_at.required' => 'created_at不能为空',
]);
$data = $this->logic->getSearchList($this->map,$this->page,$this->row);
$this->response('success',Code::SUCCESS,$data);
}
}
... ...
... ... @@ -320,7 +320,8 @@ class NewsController extends BaseController
],[
'keyword.required' => 'keyword不能为空',
]);
$data = curl_get('http://gnews.globalso.com/gnews_news.php?keyword='.$this->param['keyword'],true);
$data = curl_get('http://gnews.globalso.com/gnews_news.php?keyword='.str_replace(' ', '+', $this->param['keyword']),true);
if(isset($data['data']['p'][0]) && !empty($data['data']['p'][0])){
$data['data']['p'][0] = str_replace('&nbsp' ,' ',$data['data']['p'][0]);
}
... ...
... ... @@ -43,7 +43,8 @@ class TestController extends BaseController
* @time :2025/2/13 16:34
*/
public function ceshi(){
$data = Translate::tran('测试翻译', 'en');
$this->response('success',Code::SUCCESS,['data'=>$data]);
$hrModel = new ManageHr();
$data = $hrModel->accordIdGetLeader($this->param['id']);
$this->response('success',Code::SUCCESS,$data);
}
}
... ...
... ... @@ -230,6 +230,9 @@ class ImageController extends Controller
$suffix = array_pop($nameArr) ?? 'jpg';
$nameStr = implode('-', $nameArr);
$tran_name = Translate::tran($nameStr, 'en');
if(empty($tran_name)){
$tran_name = Translate::tran($nameStr, 'en');
}
if(is_array($tran_name)){
$tran_name = $tran_name[0];
}
... ...
<?php
/**
* @remark :
* @name :GeoArticleLogic.php
* @author :lyh
* @method :post
* @time :2025/7/14 16:21
*/
namespace App\Http\Logic\Aside\Geo;
use App\Http\Logic\Aside\BaseLogic;
use App\Models\Geo\GeoArticle;
/**
* @remark :GEO文章列表
* @name :GeoArticleLogic
* @author :lyh
* @method :post
* @time :2025/7/14 16:22
*/
class GeoArticleLogic extends BaseLogic
{
public function __construct()
{
parent::__construct();
$this->param = $this->requestAll;
$this->model = new GeoArticle();
}
/**
* @remark :列表数据
* @name :getArticleList
* @author :lyh
* @method :post
* @time :2025/7/14 16:24
*/
public function getArticleList($map = [],$page = 1,$row = 20,$order = 'id'){
if(isset($map['filename']) && !empty($map['filename'])){
$map['filename'] = ['like','%'.$map['filename'].'%'];
}
$filed = ['*'];
$lists = $this->model->lists($map,$page,$row,$order,$filed);
if(!empty($lists) && !empty($lists['list'])){
foreach ($lists['list'] as $key => $item){
$item['download_url'] = url('a/download_files?path='.$item['url']);
$item['url_link'] = getFileUrl($item['url']);
$lists['list'][$key] = $item;
}
}
return $this->success($lists);
}
/**
* @remark :获取数据详情
* @name :getArticleInfo
* @author :lyh
* @method :post
* @time :2025/7/14 16:26
*/
public function getArticleInfo(){
$info = $this->model->read($this->param);
if($info === false){
$this->fail('当前数据不存在或者已被删除');
}
$info['url'] = getFileUrl($info['url']);
return $this->success($info);
}
/**
* @remark :更新单个数据
* @name :editArticle
* @author :lyh
* @method :post
* @time :2025/7/15 10:29
*/
public function editArticle(){
$this->param['url'] = str_replace_url($this->param['url']);
$this->model->edit($this->param,['id'=>$this->param['id']]);
return $this->success(['id'=>$this->param['id']]);
}
/**
* @remark :保存数据
* @name :saveArticle
* @author :lyh
* @method :post
* @time :2025/7/14 16:26
*/
public function saveArticle(){
try {
if(!empty($this->param['data'])){
$data = [];
foreach ($this->param['data'] as $item){
$data[] = [
'filename' => $item['filename'],
'url'=> $item['url'],
'project_id'=>$this->param['project_id']
];
}
$this->model->insertAll($data);
}
}catch (\Exception $e){
$this->fail('保存失败,请联系管理员');
}
return $this->success();
}
/**
* @remark :删除数据
* @name :delArticle
* @author :lyh
* @method :post
* @time :2025/7/14 16:28
*/
public function delArticle(){
try {
$this->model->del(['id'=>['in',$this->param['ids']]]);
}catch (\Exception $e){
$this->fail('删除失败,请联系管理员.');
}
return $this->success();
}
}
... ...
<?php
/**
* @remark :
* @name :GeoLinkLogic.php
* @author :lyh
* @method :post
* @time :2025/7/14 16:20
*/
namespace App\Http\Logic\Aside\Geo;
use App\Http\Logic\Aside\BaseLogic;
use App\Models\Geo\GeoLink;
/**
* @remark :geo权威新闻(链接数据)
* @name :GeoLinkLogic
* @author :lyh
* @method :post
* @time :2025/7/14 16:20
*/
class GeoLinkLogic extends BaseLogic
{
public function __construct()
{
parent::__construct();
$this->param = $this->requestAll;
$this->model = new GeoLink();
}
/**
* @remark :获取权威新闻链接数据
* @name :getLinkList
* @author :lyh
* @method :post
* @time :2025/7/14 16:47
*/
public function getLinkList($map = [],$page = 1,$row = 20,$order = 'id'){
$filed = ['*'];
$lists = $this->model->lists($map,$page,$row,$order,$filed);
return $this->success($lists);
}
/**
* @remark :获取链接数据详情
* @name :getLinkInfo
* @author :lyh
* @method :post
* @time :2025/7/14 16:51
*/
public function getLinkInfo(){
$info = $this->model->read($this->param);
if($info === false){
$this->fail('当前数据不存在或者已被删除');
}
return $this->success($info);
}
/**
* @remark :保存链接数据
* @name :saveLink
* @author :lyh
* @method :post
* @time :2025/7/14 16:50
*/
public function saveLink(){
$data = [
'project_id' => 1,
'data' => [
['da'=>'ce_shi1', 'url'=>'www.baidu.com', 'send_time'=>'2021-07-09 11:12:12'],
['da'=>'ce_shi2', 'url'=>'www.baidu1.com', 'send_time'=>'2021-07-10 11:13:12'],
],
];
try {
if(!empty($this->param['data'])){
$data = [];
foreach ($this->param['data'] as $item){
$data[] = [
'project_id'=>$this->param['project_id'],
'da'=>$item['da'],
'url'=>$item['url'],
'send_time'=>$item['send_time']
];
}
$this->model->insertAll($data);
}
}catch (\Exception $e){
$this->fail('保存失败,请联系管理员');
}
return $this->success();
}
/**
* @remark :删除数据
* @name :delLink
* @author :lyh
* @method :post
* @time :2025/7/14 16:51
*/
public function delLink(){
try {
$this->model->del(['id'=>['in',$this->param['ids']]]);
}catch (\Exception $e){
$this->fail('删除失败,请联系管理员.');
}
return $this->success();
}
}
... ...
... ... @@ -427,6 +427,10 @@ class ProjectLogic extends BaseLogic
//上线时间大于当前时间的3天钱,默认不允许关闭推荐供应商
$this->param['deploy_build']['is_supplier'] = 1;
}
//之后上线的项目默认开启新版聚合页
if($param['uptime'] >= '2025-07-16 00:00:00'){
$param['tag_page_version'] = 2;
}
}
//聚合页评论为空时,生成评论
$keywordCommentModel = new AggregateKeywordComment();
... ...
... ... @@ -174,7 +174,7 @@ class CustomTemplateLogic extends BaseLogic
$six_read = $this->param['six_read'] ?? 0;//5.0数据时,是否按6.0显示
if($is_upgrade == 0 || $six_read == 0) {
$this->param['url'] = RouteMap::setRoute($this->param['url'], RouteMap::SOURCE_PAGE, $id, $this->user['project_id']);
if(($this->param['url'] == 'news') || ($this->param['url'] == 'product') || ($this->param['url'] == 'blog')){
if(($this->param['url'] == 'news') || ($this->param['url'] == 'products') || ($this->param['url'] == 'blog')){
$this->fail('不允许创建路由为:'.$this->param['url']);
}
}
... ...
... ... @@ -159,6 +159,7 @@ class CustomModuleCategoryLogic extends BaseLogic
$route = RouteMap::setRoute($this->param['route'], RouteMap::SOURCE_MODULE_CATE,
$this->param['id'], $this->user['project_id']);
$this->editRoute($this->param['id'],$route);
$this->param['route'] = $route;
$rs = $this->model->edit($this->param,['id'=>$this->param['id']]);
if($rs === false){
$this->fail('系统错误,请连续管理员');
... ...
... ... @@ -10,6 +10,9 @@
namespace App\Http\Logic\Bside\Geo;
use App\Http\Logic\Bside\BaseLogic;
use App\Models\Geo\GeoPlatform;
use App\Models\Geo\GeoQuestion;
use App\Models\Geo\GeoQuestionLog;
use App\Models\Geo\GeoQuestionResult;
class GeoQuestionResLogic extends BaseLogic
... ... @@ -44,6 +47,11 @@ class GeoQuestionResLogic extends BaseLogic
*/
public function getResultList($map = [],$page = 1,$row = 20){
$filed = ['id','project_id','question_id','platform','question','en_question','keywords','url','created_at','updated_at'];
if(!empty($map['created_at'])){
$map['project_id'] = $this->user['project_id'];
$map['created_at'] = ['between',[$map['created_at'].' 00:00:00',$map['created_at'].'23:59:59']];
$this->model = new GeoQuestionLog();
}
$query = $this->model->formatQuery($map);
$query = $query->where(function ($q) {
$q->whereRaw('JSON_LENGTH(keywords) > 0')
... ... @@ -64,4 +72,73 @@ class GeoQuestionResLogic extends BaseLogic
$data = $this->model->read($this->param);
return $this->success($data);
}
/**
* @remark :统计数量
* @name :countQuantity
* @author :lyh
* @method :post
* @time :2025/7/21 11:12
*/
public function countQuantity(){
$questionModel = new GeoQuestion();
$list = $questionModel->list(['project_id',$this->user['project_id']],['question','keywords','url']);
$questionTotalCount = $urlTotalCount = $keywordsTotalCount = $keywordUrlCount = 0;
foreach ($list as $item){
$questionTotalCount += count($item['question'] ?? []);
$keywordsTotalCount += count($item['keywords'] ?? []);
$urlTotalCount += count($item['url'] ?? []);
}
$keywordArr = [];
$questionResModel = new GeoQuestionResult();
$resList = $questionResModel->list(['project_id',$this->user['project_id']],['keywords_num','url_num']);
foreach ($resList as $resItem){
$keywordsNumArr = json_decode($resItem['keywords_num'] ?? [], true); // 转为 PHP 关联数组
$keywordUrlCount += array_sum($keywordsNumArr); // 获取值的总和
$urlNumArr = json_decode($resItem['url'] ?? [],true);
$keywordUrlCount += array_sum($urlNumArr); // 获取值的总和
foreach ($resItem['keywords_num'] as $key => $value) {
$keywordArr[$key] = ($keywordArr[$key] ?? 0) + $value;
}
}
$data = [
'keywords_count'=>$keywordsTotalCount,
'url_count'=>$urlTotalCount,
'question_count'=>$questionTotalCount,
'keywords_url_count'=>$keywordUrlCount,
'keywords_arr' => $keywordArr,
];
return $this->success($data);
}
/**
* @remark :按平台统计问题数量
* @name :platformHitCount
* @author :lyh
* @method :post
* @time :2025/7/21 15:00
*/
public function platformHitCount(){
$data = [];
$platformModel = new GeoPlatform();
$list = $platformModel->list(['status'=>1],'id',['name','en_name']);
$questionResModel = new GeoQuestionResult();
foreach ($list as $item){
$data[$item['name']] = $questionResModel->counts(['project_id'=>$this->user['project_id'],'hit'=>['!=',0],'platform'=>$item['en_name']]);
}
return $this->success($data);
}
/**
* @remark :获取搜索时间
* @name :getSearchDate
* @author :lyh
* @method :post
* @time :2025/7/21 16:36
*/
public function getSearchDate(){
$dates = $this->model->select(DB::raw('DATE(created_at) as date_only'))->distinct()->pluck('date_only');
return $this->success($dates);
}
}
... ...
... ... @@ -22,6 +22,7 @@ use App\Models\RankData\IndexedPages;
use App\Models\RankData\IndexedPages as IndexedPagesModel;
use App\Models\RankData\RankData;
use App\Models\RankData\RankDataBmseo;
use App\Models\RankData\RankDataLog;
use App\Models\RankData\RankWeek;
use App\Models\RankData\RankWeek as RankWeekModel;
use App\Models\RankData\RecommDomain;
... ... @@ -709,6 +710,28 @@ class RankDataLogic extends BaseLogic
$first_ten_pages_num ++;
}
}
//保证关键词数
$keyword_num = DeployBuild::where('project_id', $project_id)->value('keyword_num');
if($keyword_num){
$is_compliance = $first_page_num >= $keyword_num;
}else{
$is_compliance = 0;
}
if ($keyword_num && $is_compliance) {
Log::channel('rank_data')->info('项目' . $project_id . '白帽版:关键词达标'. $keyword_num .' - ' . $first_page_num);
$compliance_log = RankDataLog::where('api_no', $api_no)->where('date', date('Y-m-d'))->where('is_compliance', 1)->first();
if (!$compliance_log) {
$compliance_day = Project::where(['id' => $project_id])->value('finish_remain_day') ?: 0;
Project::where('id', $project_id)->update(['bm_is_remain_today' => 1, 'bm_finish_remain_day' => $compliance_day + 1]);
Log::channel('rank_data')->info('项目' . $project_id . '白帽版:达标天数+1:' . ($compliance_day + 1));
}
}else {
Log::channel('rank_data')->info('项目' . $project_id . '白帽版:关键词未达标'. $keyword_num .' - ' . $first_page_num);
}
$where = [
'project_id' => $project_id,
'api_no' => $api_no,
... ... @@ -728,7 +751,7 @@ class RankDataLogic extends BaseLogic
$model->updated_date = date('Y-m-d');
$model->save();
return true;
return $is_compliance;
}
/**
... ...
... ... @@ -26,6 +26,7 @@ class AsideTicketListRequest extends FormRequest
return [
'project_id' => 'nullable|string',
'status' => 'nullable|in:0,1,2,3|integer',
'star' => 'nullable|in:1,2,3|integer',
'search' => 'nullable|string', // 搜索关键词
'engineer_id' => 'nullable|integer', // 工程师ID
'page' => 'nullable|integer',
... ...
... ... @@ -29,6 +29,8 @@ class AsideTicketStoreRequest extends FormRequest
'content' => 'required|string',
'files' => 'nullable|array',
'engineer_ids' => 'array',
'star' => 'nullable|in:1,2,3|integer',
'plan_end_at' => 'nullable|date',
];
}
}
... ...
... ... @@ -24,9 +24,14 @@ class AsideTicketUpdateRequest extends FormRequest
public function rules()
{
return [
'title' => 'nullable|string',
'content' => 'nullable|string',
'files' => 'nullable|array',
'status' => 'nullable|in:0,1,2,3|integer',
'reply' => 'nullable|string',
'engineer_ids' => 'nullable|array',
'star' => 'nullable|in:1,2,3|integer',
'plan_end_at' => 'nullable|date',
'reply' => 'nullable|string', // 弃用
];
}
}
... ...
<?php
namespace App\Http\Requests\Aside\WorkOrder;
use Illuminate\Foundation\Http\FormRequest;
class TicketProjectUpdateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'subtitle' => 'nullable|string',
'wechat_switch' => 'nullable|boolean',
];
}
}
... ...
... ... @@ -162,7 +162,7 @@ class Base extends Model
* @method :post
* @time :2024/9/26 10:52
*/
public function counts($condition){
public function counts($condition = []){
$condition = $this->filterRequestData($condition);
return $this->formatQuery($condition)->count();
}
... ... @@ -362,4 +362,16 @@ class Base extends Model
$lists = $lists->toArray();
return $lists;
}
/**
* @remark :根据条件获取单个值
* @name :getValue
* @author :lyh
* @method :post
* @time :2025/7/22 10:00
*/
public function getValue($data,$field = 'id'){
$data = $this->filterRequestData($data);
return $this->formatQuery($data)->value($field);
}
}
... ...
<?php
/**
* @remark :
* @name :GeoArticle.php
* @author :lyh
* @method :post
* @time :2025/7/14 16:06
*/
namespace App\Models\Geo;
use App\Models\Base;
/**
* @remark :geo文章列表
* @name :GeoArticle
* @author :lyh
* @method :post
* @time :2025/7/14 16:07
*/
class GeoArticle extends Base
{
protected $table = 'gl_geo_article';
}
... ...
<?php
/**
* @remark :
* @name :GeoLink.php
* @author :lyh
* @method :post
* @time :2025/7/14 16:05
*/
namespace App\Models\Geo;
use App\Models\Base;
/**
* @remark :geo权威新闻外链数据
* @name :GeoLink
* @author :lyh
* @method :post
* @time :2025/7/14 16:05
*/
class GeoLink extends Base
{
protected $table = 'gl_geo_link';
}
... ...
... ... @@ -69,4 +69,34 @@ class GeoQuestionResult extends Base
}
return $value;
}
/**
* @remark :命中的关键词数量
* @name :getKeywordsNumAttribute
* @author :lyh
* @method :post
* @time :2025/7/21 11:33
*/
public function getKeywordsNumAttribute($value)
{
if($value){
$value = Arr::s2a($value);
}
return $value;
}
/**
* @remark :命中的url数量
* @name :getUrlNumAttribute
* @author :lyh
* @method :post
* @time :2025/7/21 11:34
*/
public function getUrlNumAttribute($value)
{
if($value){
$value = Arr::s2a($value);
}
return $value;
}
}
... ...
... ... @@ -80,17 +80,23 @@ class ReInquiryCount extends Base
return Arr::setToArr($value);
}
public function getTasksAttribute(){
$tasks = ReInquiryTask::whereIn('id', $this->task_ids)->select(['title', 'industry','target', 'status'])->get()->toArray();
foreach ($tasks as &$task){
$target = collect($task['target'])->where('url', $this->domain)->first();
$task['is_del'] = $target ? 0 : 1;
$task['agent'] = $target['agent'] ?? '';
$task['is_v6'] = $target['is_v6'] ?? '';
$task['agent_group'] = $target['agent_group'] ?? '';
unset($task['target']);
}
return $tasks;
public function getTasksAttribute()
{
$cache_key = 'ReInquiryCountTasks_' . Arr::arrToSet($this->task_ids);
$tasks = Cache::get($cache_key);
if (!$tasks) {
$tasks = ReInquiryTask::whereIn('id', $this->task_ids)->select(['title', 'industry', 'target', 'status'])->get()->toArray();
foreach ($tasks as &$task) {
$target = collect($task['target'])->where('url', $this->domain)->first();
$task['is_del'] = $target ? 0 : 1;
$task['agent'] = $target['agent'] ?? '';
$task['is_v6'] = $target['is_v6'] ?? '';
$task['agent_group'] = $target['agent_group'] ?? '';
unset($task['target']);
}
Cache::put($cache_key, $tasks, 7200);
}
return $tasks;
}
public static function getFobProjects(){
... ...
... ... @@ -11,6 +11,8 @@ class ManageHr extends Base
const GID_ZERO = 0;//超级管理员
const STATUS_ONE = 1;
const IS_LEADER = 1;//组长
/**
* 特殊字段
* @return string[]
... ... @@ -202,4 +204,32 @@ class ManageHr extends Base
}
return $name;
}
/**
* @remark :根据当前用户登录的id获取当前用户的组长
* @name :accordIdGetLeader
* @author :lyh
* @method :post
* @time :2025/7/22 9:42
* @param :id->当前用户的人事id
*/
public function accordIdGetLeader($id = 0){
if(empty($id)){
return 0;
}
//查看当前用户是否为组长
$info = $this->read(['id'=>$id],['belong_group','is_leader','dept_id']);
if($info === false){
return 0;
}
//不是组长:根据小组获取组长
if($info['is_leader'] != self::IS_LEADER){
$id = $this->getValue(['belong_group'=>$info['belong_group'],'dept_id'=>$info['dept_id'],'is_leader'=> self::IS_LEADER]);
if(empty($id)){
//未获取到时,根据当前大组去随机获取一个组长
$id = $this->getValue(['dept_id'=>$info['dept_id'],'is_leader'=> self::IS_LEADER]);
}
}
return $id;
}
}
... ...
... ... @@ -69,6 +69,9 @@ class RouteMap extends Base
}
//过滤特殊字符
$sign = generateRoute($title);
if (strpos($sign, 'well-known-') === 0) {
$sign = substr($sign, strlen('well-known-'));
}
//查看当前数据路由是否存在
$routeInfo = self::where(['project_id' => $project_id, 'source' => $source, 'source_id'=>$source_id])->first();
$suffix = '';//设置路由后缀
... ...
<?php
namespace App\Models\WorkOrder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class TicketDing extends Model
{
use HasFactory;
protected $table = 'gl_ticket_dings';
}
... ...
... ... @@ -4,6 +4,7 @@ namespace App\Models\WorkOrder;
use App\Models\Base;
use App\Models\Manage\Manage;
use App\Models\Manage\ManageHr;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class TicketLog extends Base
... ... @@ -19,8 +20,8 @@ class TicketLog extends Base
public function engineer()
{
return $this->belongsTo(Manage::class, 'engineer_id', 'id')
->select(['id', 'name', 'mobile']);
return $this->belongsTo(ManageHr::class, 'engineer_id', 'manage_id')
->select(['manage_id', 'name', 'nickname']);
}
public function ticket()
... ...
... ... @@ -4,6 +4,7 @@ namespace App\Models\WorkOrder;
use App\Models\Base;
use App\Models\Manage\Manage;
use App\Models\Manage\ManageHr;
use App\Models\Project\Project;
use App\Models\ProjectAssociation\ProjectAssociation;
use App\Models\Workchat\MessagePush;
... ... @@ -21,18 +22,28 @@ class TicketProject extends Base
->where('version', 6);
}
// 项目经理
public function pm()
{
return $this->hasOne(ManageHr::class, 'manage_id', 'pm_id')
->where('manage_id', '>', 0)
->select(['manage_id', 'name', 'nickname']);
}
//售后服务经理
public function assm()
{
return $this->hasOne(Manage::class, 'id', 'assm_id')
->select(['id', 'name']);
return $this->hasOne(ManageHr::class, 'manage_id', 'assm_id')
->where('manage_id', '>', 0)
->select(['manage_id', 'name', 'nickname']);
}
// 优化师
public function seom()
{
return $this->hasOne(Manage::class, 'id', 'seom_id')
->select(['id', 'name']);
return $this->hasOne(ManageHr::class, 'manage_id', 'seom_id')
->where('manage_id', '>', 0)
->select(['manage_id', 'name', 'nickname']);
}
/**
... ... @@ -40,8 +51,9 @@ class TicketProject extends Base
*/
public function first_engineer()
{
return $this->hasOne(Manage::class, 'id', 'engineer_id')
->select(['id', 'name']);
return $this->hasOne(ManageHr::class, 'manage_id', 'engineer_id')
->where('manage_id', '>', 0)
->select(['manage_id', 'name', 'nickname']);
}
/**
... ...
... ... @@ -3,7 +3,9 @@
namespace App\Models\WorkOrder;
use App\Models\Base;
use App\Models\Manage\ManageHr;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Facades\Log;
class Tickets extends Base
{
... ... @@ -14,7 +16,7 @@ class Tickets extends Base
const STATUS_PEDDING = 0; // 待处理
const STATUS_PROCESSING = 1; // 处理中
const STATUS_COMPLETED = 2; // 已完成
const STATUS_CLOSED = 3; // 已关闭
const STATUS_CLOSED = 3; // 已关闭,已失效
/**
* @return void
... ... @@ -32,4 +34,95 @@ class Tickets extends Base
{
return $this->belongsTo(TicketProject::class, 'project_id', 'id');
}
/**
* 当前工单,保存参与的人员到 gl_ticket_logs 表
* 逻辑说明:
* 1. 如果当前项目是超迹,要把徐莹和第一负责人加进去,为参与人
* 2. 若是域途项目,把黄小玉和第一负责人加进去,为参与人
* 3. 若是V5V6的项目,则把组长和第一负责人加进去,为参与人
*/
public function saveEngineers($engineer_ids = [])
{
$canyu = [
$this->project->engineer_id, // 第一负责人
];
if ($this->project->project_cate == 3)
$canyu[] = 20; // 徐莹
elseif ($this->project->project_cate == 4)
$canyu[] = 85; // 黄小玉
else{
// todo 待完善
}
$all_engineer_ids = array_unique(array_merge($canyu, $engineer_ids));
foreach ($all_engineer_ids as $engineer_id)
{
try {
$log = $this->logs()->where('engineer_id', $engineer_id)->first();
if ($log && $log->is_engineer != in_array($engineer_id, $engineer_ids))
{
$log->is_engineer = in_array($engineer_id, $engineer_ids);
$log->save();
}else
{
// 利用唯一索引去重
$log = new TicketLog();
$log->engineer_id = $engineer_id;
$log->is_engineer = in_array($engineer_id, $engineer_ids);
$this->logs()->save($log);
}
}catch (\Exception $exception){
Log::error(" | ERRPR | Ticket saveEngineers {$exception->getMessage()} \n {$exception->getTraceAsString()}");
}
}
// 删除没有参与当前工单的人员(若之前已有)
$this->logs()->whereNotIn('engineer_id', $all_engineer_ids)->delete();
}
/**
* TODO 这个是一个补充功能
* 那些情况需要推送钉钉内部通知?
* 1. 客户提交了工单
* - 通知第一负责人 (gl_ticket_logs 表做了标记)
* 2. 客户补充了工单
* - 通知最近的聊天技术(gl_ticket_chats 表做了标记)
* 3. 技术完成了工单
* - 通知第一负责人 (暂无,所以这里做补充,如果以后要把上面的逻辑也加进来也可以,只是我为了偷懒,暂时只考虑完成工单请款)
*/
public function pushDing($type = 'finish')
{
try {
$ding = new TicketDing();
$ding->msgKey = 'sampleLink';
$ding->table_name = 'gl_tickets';
$ding->table_id = $this->id;
if ($type == 'finish')
{
// 所有技术完成了工单,通知第一负责人 和 工单的提交人
$userIds = [$this->project->engineer_id];
if ($this->submit_side == 1) {
// A 端提的,还要通知提交人
$userIds[] = $this->submit_user_id;
// 去重
$userIds = array_unique($userIds);
}
$ding->userIds = json_encode($userIds);
$ding->msgParam = json_encode([
'text' => "工单(ID: {$this->id}),已经全部完成,请访问查看详情!",
'title' => 'AI协同工单 - ' . $this->project->title,
'picUrl' => 'https://hub.globalso.com/logocm.png',
'messageUrl' => 'https://oa.quanqiusou.cn/afterorder?project_id=' . $this->project->uuid,
], JSON_UNESCAPED_UNICODE);
$ding->save();
}
}catch (\Exception $exception){
Log::error(" | ERRPR | Ticket {$exception->getMessage()} \n {$exception->getTraceAsString()}");
}
}
}
... ...
... ... @@ -168,7 +168,7 @@ class ProjectAssociationServices extends BaseService
}
$param['sign'] = $this->getSign($param);
$url = 'https://hub.ai.cc/api/globalso_ai_customer_service/chatroom_list';
$result = Http::withoutVerifying()->withOptions(['proxy' => env('CURL_PROXY')])->post($url, $param)->json();
$result = Http::withoutVerifying()->withOptions(['proxy' => env('CURL_PROXY')])->timeout(20)->post($url, $param)->json();
if(empty($result) || $result['status'] != 200){
return [];
}
... ...
... ... @@ -11,7 +11,7 @@ namespace App\Services\Geo;
class GeoService
{
public $api_key = '7yn!We6$&NnVA38bpGy*A@4TQ5iYLJcW';
public $api_key = 'UkzZljFv83Z2qBi5YR1o3f2otAVWtug6';
public $api_url = 'https://api.cmer.com/';
... ... @@ -68,4 +68,34 @@ class GeoService
$url = $url . '?' . http_build_query($param);
return http_get($url, $header);
}
/**
* @remark :请求deepSeek数据
* @name :getDeepSeek
* @author :lyh
* @method :post
* @time :2025/7/15 10:59
*/
public function getDeepSeekResult($content,$model = 'deepseek-r1'){
$url = $this->api_url . 'v1/chat';
$header = [
'accept: application/json',
'X-CmerApi-Host: llm-chat.p.cmer.com',
'apikey: '.$this->api_key,
'Content-Type: application/json'
];
$message = [
'messages'=>[
[
'content'=>$content,
'role'=>'user'
],
],
'model' => $model,
"supplier"=> "bailian",
'security_check' => true
];
$data = http_post($url,json_encode($message,true),$header);
return $data;
}
}
... ...
... ... @@ -28,7 +28,7 @@ class ProjectServer
<div class="layout" data-unable="demo01-error404">
<img src="https://ecdn6.globalso.com/upload/m/image_other/2023-10/6528a87e594db30162.png" alt=""/>
</div>
<p style="text-align: center">SORRY. THE PAGE HAS EITHER MOVED OR CANNOT BE FOUND.</p>
<h1 style="text-align: center">SORRY. THE PAGE HAS EITHER MOVED OR CANNOT BE FOUND.</h1>
<style>
.section-block-error404 .layout {
height: 700px;
... ...
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTicketDingsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('gl_ticket_dings', function (Blueprint $table) {
$table->id();
$table->json('userIds')->comment('接收人ID列表');
$table->string('msgKey')->default('sampleMarkdown')->comment('消息模板标识');
$table->json('msgParam')->comment('消息内容参数,JSON格式');
$table->smallInteger('status')->default(0)->index()->comment('发送状态:0-未发送,1-已发送,2-发送失败');
$table->string('errorMsg')->nullable()->comment('错误信息,发送失败时记录');
$table->string('table_name', 50)->nullable()->comment('关联表名称');
$table->string('table_id', 50)->nullable()->comment('关联表ID');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('gl_ticket_dings');
}
}
... ...
... ... @@ -257,6 +257,7 @@ Route::middleware(['aloginauth'])->group(function () {
Route::post('/{id}', [Aside\WorkOrder\AsideTicketController::class, 'update'])->name('admin.tickets.update')->summary('A端更新工单,审核,邀请同事');
Route::get('/pushNotify/{id}', [Aside\WorkOrder\AsideTicketController::class, 'pushNotify'])->name('admin.tickets.pushNotify')->summary('A端工单推送企微群');
Route::get('/projects/{search}', [Aside\WorkOrder\AsideTicketController::class, 'getProjects'])->name('admin.tickets.projects')->summary('A端V5V6项目列表');
Route::patch('/projects/{id}', [Aside\WorkOrder\TicketProjectController::class, 'update'])->name('admin.tickets.projects.update')->summary('A端修改工单项目');
Route::get('/v56_projects/list', [Aside\WorkOrder\AsideTicketController::class, 'projectList'])->name('admin.tickets.projectList')->summary('A端V5V6项目列表')
->description("project_cate[项目分类1]: 1 V5, 2 V6, 3 超迹, 4 域途");
Route::post('/log/{id}', [Aside\WorkOrder\AsideTicketLogController::class, 'update'])->name('admin.tickets.log.update')->summary('A端工单操作日志更新,完成工单');
... ... @@ -563,6 +564,21 @@ Route::middleware(['aloginauth'])->group(function () {
Route::any('/saveGeoQuestion', [Aside\Geo\GeoQuestionController::class, 'saveGeoQuestion'])->name('admin.geo_question_saveGeoQuestion');
Route::any('/delGeoQuestion', [Aside\Geo\GeoQuestionController::class, 'delGeoQuestion'])->name('admin.geo_question_delGeoQuestion');
});
//文章列表
Route::prefix('article')->group(function () {
Route::any('/lists', [Aside\Geo\GeoArticleController::class, 'lists'])->name('admin.geo_article_lists');
Route::any('/save', [Aside\Geo\GeoArticleController::class, 'save'])->name('admin.geo_article_save');
Route::any('/edit', [Aside\Geo\GeoArticleController::class, 'edit'])->name('admin.geo_article_edit');
Route::any('/info', [Aside\Geo\GeoArticleController::class, 'info'])->name('admin.geo_article_info');
Route::any('/del', [Aside\Geo\GeoArticleController::class, 'del'])->name('admin.geo_article_del');
});
//权威文章链接
Route::prefix('link')->group(function () {
Route::any('/lists', [Aside\Geo\GeoLinkController::class, 'lists'])->name('admin.geo_link_lists');
Route::any('/save', [Aside\Geo\GeoLinkController::class, 'save'])->name('admin.geo_link_save');
Route::any('/info', [Aside\Geo\GeoLinkController::class, 'info'])->name('admin.geo_link_info');
Route::any('/del', [Aside\Geo\GeoLinkController::class, 'del'])->name('admin.geo_link_del');
});
});
// 任务相关
Route::prefix('task')->group(function () {
... ...
... ... @@ -755,6 +755,8 @@ Route::middleware(['bloginauth'])->group(function () {
Route::any('/getInfo', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class,'getInfo'])->name('geo_result_getInfo');
Route::any('/getType', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class, 'getType'])->name('geo_result_getType');//geo设置类型
Route::any('/getCount', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class, 'getCount'])->name('geo_result_getCount');//geo设置类型统计数量
Route::any('/countQuantity', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class, 'countQuantity'])->name('geo_result_countQuantity');//geo统计
Route::any('/getSearchDate', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class, 'getSearchDate'])->name('geo_result_getSearchDate');//搜索记录时间
});
});
//无需登录验证的路由组
... ...