作者 刘锟

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

正在显示 39 个修改的文件 包含 1066 行增加292 行删除
... ... @@ -48,8 +48,9 @@ class AuthorityScore extends Command
continue;
}
echo '执行的项目id'.$item.PHP_EOL;
$url = $url.$domainInfo['domain'];
$data = http_get($url);
$urls = $url.$domainInfo['domain'];
echo $urls.PHP_EOL;
$data = http_get($urls);
if(!empty($data) && !empty($data['data'])){
echo json_encode($data).PHP_EOL;
$data = $data['data'];
... ...
... ... @@ -34,87 +34,127 @@ class GeoQuestionRes extends Command
*/
protected $description = 'geo设置请求获取结果';
public function handle(){
while (true){
/**
* @return bool
*/
public function handle()
{
while (true) {
$task_id = $this->getTaskId();
if(empty($task_id)){
echo '无数据'.PHP_EOL;
sleep(30);
if (empty($task_id)) {
sleep(300);
continue;
}
echo date('Y-m-d H:i:s').'执行的任务id:'.$task_id.PHP_EOL;
$geoQuestionModel = new GeoQuestion();
$taskInfo = $geoQuestionModel->read(['id'=>$task_id]);
if ($taskInfo === false) {
$this->output('当前任务详情为空!');
continue;
}
$questionModel = new GeoQuestion();//问题
$info = $questionModel->read(['id'=>$task_id]);
//获取当前项目的执行频率
$projectModel = new Project();
$projectInfo = $projectModel->read(['id'=>$info['project_id']],['geo_status','geo_frequency']);
if($projectInfo['geo_status'] == 0){
$questionModel->edit(['status'=>0],['id'=>$task_id]);
$projectInfo = $projectModel->read(['id' => $taskInfo['project_id']],['geo_status', 'geo_frequency']);
if ($projectInfo === false) {
$this->output('未获取到项目详情!');
$geoQuestionModel->edit(['status'=>$geoQuestionModel::STATUS_CLOSE],['id'=>$task_id]);
continue;
}
$questionArr = $info['question'];
if(empty($questionArr)){
echo date('Y-m-d H:i:s').'当前任务不存在问题。'.PHP_EOL;
$questionModel->edit(['status'=>0],['id'=>$task_id]);
if(empty($taskInfo['question']) || empty($taskInfo['keywords']) || empty($taskInfo['url'])){
$this->output('task id: ' . $task_id . ', error: 任务数据缺失, continue!');
$geoQuestionModel->edit(['status'=>$geoQuestionModel::STATUS_CLOSE],['id'=>$task_id]);
continue;
}
//获取平台信息
$platformModel = new GeoPlatform();//平台
$platformArr = $platformModel->selectField(['status'=>$platformModel::STATUS_ON],'en_name');
if(empty($platformArr)){
echo date('Y-m-d H:i:s').'请求平台为空。'.PHP_EOL;
$geoPlatformModel = new GeoPlatform();
$platformsArr = $geoPlatformModel->selectField(['status' => GeoPlatform::STATUS_ON],'en_name');
if (empty($platformsArr)) {
$this->output('未设置AI模型!');
continue;
}
$geoService = new GeoService();
$keywordArr = $info['keywords'] ?? [];
$urlArr = $info['url'] ?? [];
$geo_service = new GeoService();
$geoResultModel = new GeoQuestionResult();
foreach ($questionArr as $q_item){
foreach ($platformArr as $p_item){
$keywords = [];//命中的关键词
$urls = [];//命中的网址
$geoLogModel = new GeoQuestionLog();
foreach ($taskInfo['question'] as $question) {
foreach ($platformsArr as $platform) {
$data = $hit_data = [];
$error_num = 0;
// 设置重试, 有的平台不一定能正常获取到数据
GET_RESULT:
$error_num++;
try {
$result_data = $geoService->setWebSearchChatAction($q_item,$p_item);
echo 'success:'.$result_data['code'].PHP_EOL;
if(isset($result_data) && $result_data['code'] == 200){
$keywords = $this->getKeywords($keywordArr,$result_data['text'] ?? []);
$urls = $this->getUrl($urlArr,$result_data['annotations'] ?? []);
echo '执行次数:'.$error_num.PHP_EOL;
if ($error_num >= 5) {
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);
}
if (empty($result['text'])){
goto GET_RESULT;
}
}catch (\Exception $e){
echo $e->getMessage().PHP_EOL;
continue;
} catch (\Exception $e) {
$this->output('task id:' . $task_id . ', question: ' . $question . ', platform: ' . $platform . ', error: ' . $e->getMessage());
goto GET_RESULT;
}
//查询当前是否已有执行保存记录
$resultInfo = $geoResultModel->read(['project_id'=>$info['project_id'],'question_id'=>$info['id'],'platform'=>$p_item,'question'=>$q_item],['id']);
//保存一条结果记录
$data = [
'project_id'=>$info['project_id'],
'question_id'=>$info['id'],
'platform'=>$p_item,
'question'=>$q_item,
'keywords'=>json_encode($keywords ?? [],true),//命中的关键词
'text'=>json_encode($result_data ?? [],true),
'url'=>json_encode($urls ?? [],true),//命中的网址
'type'=>$info['type'] ?? 1
// 命中文案
$hit_data[] = $result['text'];
if(!empty($result['annotations'])){
$url = array_column(array_column($result['annotations'], 'url_citation'), 'url');
$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++;
}
}
if (!empty($taskInfo['url'])) {
$hit_url = $this->getUrl($taskInfo['url'],$hit_data);
if (!empty($hit_url)) {
$hit++;
}
}
echo 'MZ-url'.json_encode($hit_url).PHP_EOL;
// 保存数据结果
$geo_result = $geoResultModel->read(['project_id' => $taskInfo['project_id'], 'question_id' => $task_id, 'platform' => $platform, 'question' => $question],['id']);
$save_data = [
'project_id' => $taskInfo['project_id'],
'question_id' => $task_id,
'type' => $taskInfo['type'] ?? $geoQuestionModel::TYPE_BRAND,
'platform' => $platform,
'question' => $question,
'keywords' => json_encode($hit_keyword,true),//命中的关键词
'url' => json_encode($hit_url,true),//命中的网址
'text' => json_encode($result,true),
'hit' => $hit,
'created_at'=>date('Y-m-d H:i:s'),
'updated_at'=>date('Y-m-d H:i:s'),
];
if($resultInfo === false){
$geoResultModel->addReturnId($data);
// echo '当前数据INFO:'.json_encode($save_data,true).PHP_EOL;
if($geo_result === false){
$id= $geoResultModel->insertGetId($save_data);
echo '当前数据id:'.$id.PHP_EOL;
}else{
$geoResultModel->edit($data,['id'=>$resultInfo['id']]);
$geoResultModel->edit($save_data, ['id' => $geo_result['id']]);
}
$data_log = [
'project_id'=>$info['project_id'],
'question_id'=>$info['id'],
'platform'=>$p_item,
'question'=>$q_item,
'text'=>json_encode($result_data ?? [],true),
'type'=>$info['type'] ?? 1
];
$geoLogModel = new GeoQuestionLog();
$geoLogModel->addReturnId($data_log);
$save_data['text'] = json_encode(!empty($data) ? $data : $result,true);
$geoLogModel->addReturnId($save_data);
echo '执行结束:'.$platform.PHP_EOL;
}
}
//更新下次执行时间
$questionModel->edit(['current_time'=>date('Y-m-d'),'next_time'=>date('Y-m-d', strtotime(date('Y-m-d') . ' +'.(int)$projectInfo['geo_frequency'].' days'))],['id'=>$info['id']]);
$next_time = date('Y-m-d', strtotime('+' . ($projectInfo['geo_frequency'] ?? 3) . ' days'));
$geoQuestionModel->edit(['current_time'=>date('Y-m-d'),'next_time'=>$next_time],['id'=>$task_id]);
}
return true;
}
/**
... ... @@ -124,22 +164,13 @@ class GeoQuestionRes extends Command
* @method :post
* @time :2025/7/3 16:38
*/
public function getUrl($urlArr = [],$result_annotations = [],$result_text = []){
public function getUrl($urlArr = [],$result_annotations = []){
$url = [];
if(!empty($urlArr)){
$str = implode(',',$result_annotations);
foreach ($urlArr as $u_item){
if(!empty($result_text)){
if (str_contains($result_text, $u_item)) {
$url[] = $u_item;
}
}
if(!empty($result_annotations)){
foreach ($result_annotations as $a_item){
echo 'url'.$a_item['url_citation']['url'].PHP_EOL.'当前的url:'.$u_item;
if (str_contains($a_item['url_citation']['url'], $u_item)) {
$url[] = $u_item;
}
}
if (str_contains($str, $u_item)) {
$url[] = $u_item;
}
}
}
... ... @@ -156,8 +187,9 @@ class GeoQuestionRes extends Command
public function getKeywords($keywordArr = [],$result_text = []){
$keywords = [];
if(!empty($keywordArr) && !empty($result_text)){
$str = implode(',',$result_text);
foreach ($keywordArr as $k_item){
if (str_contains($result_text, $k_item)) {
if (str_contains($str, $k_item)) {
$keywords[] = $k_item;
}
}
... ... @@ -166,24 +198,94 @@ class GeoQuestionRes extends Command
}
/**
* @remark :拉取任务id
* @name :getTaskId
* @author :lyh
* @method :post
* @time :2025/7/3 15:15
* 整合Google平台数据
* @param $data
* @return array
*/
public function dealGoogleData($data)
{
$result = [
'code' => 200,
'model' => 'Google AI Overview',
'text' => '',
];
$texts = [];
if(!empty($data['data']['text_parts']) && is_array($data['data']['text_parts'])){
foreach ($data['data']['text_parts'] as $item){
switch ($item['type']){
case 'paragraph':
if(isset($item['text']) && !empty($item['text'])){
array_push($texts, $item['text']);
}
break;
case 'title':
if(isset($item['text']) && !empty($item['text'])) {
array_unshift($texts, $item['text']);
}
break;
case 'list':
if(!empty($item['list'])){
foreach ($item['list'] as $sonItem){
if(isset($sonItem['text']) && !empty($sonItem['text'])) {
array_push($texts, $sonItem['text']);
}
if(isset($item['title']) && !empty($item['title'])) {
array_push($texts, $sonItem['title']);
}
}
}
break;
default:
break;
}
}
}
if(!empty($data['data']['reference_links']) && is_array($data['data']['reference_links'])){
foreach ($data['data']['reference_links'] as $link) {
if (isset($link['title']) && !empty($link['title']) && isset($link['link']) && !empty($link['link'])) {
$result['annotations'][] = [
'type' => 'url_citation',
'url_citation' => [
'url' => $link['link'],
'title' => $link['title']
],
];
}
}
}
$text = implode(PHP_EOL, $texts);
$result['text'] = $text;
return $result;
}
/**
* 获取待执行任务ID
* @return mixed
*/
public function getTaskId(){
$task_id = Redis::rpop('geo_question_result');
$key = 'geo_task_list';
$task_id = Redis::rpop($key);
if(empty($task_id)){
$questionModel = new GeoQuestion();
$ids = $questionModel->selectField(['status'=>1,'next_time'=>['<=',date('Y-m-d')]],'id');
$ids = $questionModel->selectField(['status'=>$questionModel::STATUS_OPEN,'next_time'=>['<=',date('Y-m-d')]],'id');
if(!empty($ids)){
foreach ($ids as $id) {
Redis::lpush('geo_question_result', $id);
Redis::lpush($key, $id);
}
}
$task_id = Redis::rpop('geo_question_result');
$task_id = Redis::rpop($key);
}
return $task_id;
}
/**
* 输出日志
* @param $message
* @return bool
*/
public function output($message)
{
echo date('Y-m-d H:i:s') . ' ' . $message . PHP_EOL;
return true;
}
}
... ...
... ... @@ -10,6 +10,7 @@
namespace App\Console\Commands\LyhTest;
use App\Models\Ai\AiBlog;
use App\Models\News\News;
use App\Models\Product\Category;
use App\Models\Project\AggregateKeywordAffix;
use App\Models\Project\AiBlogTask;
... ... @@ -41,6 +42,15 @@ class lyhDemo extends Command
protected $description = '更新路由';
public function handle(){
ProjectServer::useProject(3531);
$newsModel = new News();
$rows = $newsModel->select('id', 'text')->get();
foreach ($rows as $row) {
echo '执行数据id:'.$row->id.PHP_EOL;
$newText = preg_replace('/<h1 class="t">.*?<\/h1>/is', '', $row->text);
$newsModel->where('id', $row->id)->update(['text' => $newText]);
}
DB::disconnect('custom_mysql');
return true;
}
... ...
<?php
/**
* @remark :
* @name :InitKeywordComment.php
* @author :lyh
* @method :post
* @time :2025/6/3 15:38
*/
namespace App\Console\Commands\Project;
use App\Helper\Common;
use App\Helper\Gpt;
use App\Models\Ai\AiCommand;
use App\Models\Com\NoticeLog;
use App\Models\Project\AggregateKeywordComment;
use App\Models\Project\Project;
use Illuminate\Console\Command;
class ProjectComment extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'project_comment';
/**
* The console command description.
*
* @var string
*/
protected $description = '初始化关键字评论';
public $number = 100;
public function handle(){
$projectModel = new Project();
$ids = $projectModel->selectField(['delete_status' => 0,'project_type'=>0,'extend_type'=>0,'type'=>['in',[1,2,3,4,6]]], 'id');
foreach ($ids as $id){
echo date('Y-m-d H:i:s').'start:' . $id . PHP_EOL;
$project_id = $id;
echo date('Y-m-d H:i:s').'执行的项目id:' . $project_id . PHP_EOL;
try {
$this->_action($project_id);
}catch (\Exception $e){
echo date('Y-m-d H:i:s').'错误信息:'.$e->getMessage().PHP_EOL;
continue;
}
echo date('Y-m-d H:i:s').'end:' . $id . PHP_EOL;
}
return true;
}
/**
* @remark :执行的方法
* @name :_action
* @author :lyh
* @method :post
* @time :2025/6/3 15:42
*/
public function _action($project_id){
$aiCommonModel = new AiCommand();
$info = $aiCommonModel->read(['key'=>'tag_comment']);
$info['ai'] = str_replace('50', '100', $info['ai']);
$text = Gpt::instance()->openai_chat_qqs($info['ai']);
$text = Common::deal_keywords($text);
preg_match_all('/\{[^{}]*\}/', $text, $matches);
if(!empty($text)){
$data = [];
foreach ($matches[0] as $item){
$item = str_replace("'", '"', $item);
// 解码成 PHP 关联数组
$item = json_decode($item, true);
if(!isset($item['name']) || !isset($item['comment'])){
continue;
}
$twoMonthsAgo = strtotime('-2 months');
$randomTimestamp = rand($twoMonthsAgo, time());
$randomDateTime = date('Y-m-d H:i:s', $randomTimestamp);
$data[] = [
'nickname'=>$item['name'],
'text'=>$item['comment'],
'project_id'=>$project_id,
'type'=>1,
'uid'=>0,
'start_time'=>$randomDateTime,
'created_at'=>date('Y-m-d H:i:s'),
'updated_at'=>date('Y-m-d H:i:s')
];
}
$keywordCommentModel = new AggregateKeywordComment();
$keywordCommentModel->insertAll($data);
}
return true;
}
}
... ...
<?php
/**
* Created by PhpStorm.
* User: zhl
* Date: 2025/7/4
* Time: 11:14
*/
namespace App\Console\Commands\Statistics;
use App\Models\Project\ProjectFlow;
use App\Services\UpyunService;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
class Flow extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'project_flow_statistics';
/**
* The console command description.
*
* @var string
*/
protected $description = '项目流量统计';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct(); // 确保调用父类构造函数
}
public function handle()
{
$this->cdnStatistics();
return true;
}
public function cdnStatistics()
{
$class = new UpyunService();
list($start_at, $end_at) = $this->getTime();
$date = date('Y-m-d', strtotime($end_at));
echo 'start_at: ' . $start_at . PHP_EOL;
echo 'end_at: ' . $end_at . PHP_EOL;
$result = $class->realTimeStatistics($start_at, $end_at);
file_put_contents(storage_path('logs/flow/' . date('YmdHis', strtotime($start_at)) . '.json'), $result);
// $result = file_get_contents(storage_path('logs/flow/' . date('YmdHis', strtotime($start_at)) . '.json'));
$result = json_decode($result, true);
$flow = [];
if (FALSE == empty($result['data']) && is_array($result['data'])) {
// 结算 所有项目 流量
foreach ($result['data'] as $item) {
if (Str::startsWith($item['key'], '/upload/p/')) {
$tmp = explode('/', $item['key']);
$flow[$tmp[3]] = FALSE == empty($flow[$tmp[3]]) ? $flow[$tmp[3]] + $item['val'] : $item['val'];
}
}
}
ksort($flow);
$total_flow = 0;
foreach ($flow as $project_id=>$val) {
ProjectFlow::flowInsert($project_id, $date, $val, $end_at);
$total_flow += $val;
}
echo 'total project: ' . count($flow) . PHP_EOL;
echo 'total flow: ' . $total_flow . PHP_EOL;
return true;
}
/**
* @return array
*/
public function getTime()
{
$last_at_log = ProjectFlow::orderBy('last_at', 'desc')->first();
$today = date('Y-m-d 00:00:00');
$start_at = $last_at_log ? $last_at_log->last_at : $today;
if (strtotime($start_at) < strtotime($today))
$start_at = $today;
$end_at = date('Y-m-d H:i:s', time() - 1);
return [$start_at, $end_at];
}
}
\ No newline at end of file
... ...
... ... @@ -9,6 +9,7 @@
namespace App\Console\Commands\Sync;
use App\Models\Manage\Mobile;
use App\Models\User\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
... ... @@ -40,30 +41,42 @@ class SyncMobile extends Command
$url = 'https://www.quanqiusou.cn/extend_api/saas/get_phone.php';
// $data = curlGet($url);//TODO::获取号码库
$client = new \GuzzleHttp\Client();
$data = $client->request('GET', $url, [
'proxy' => env('CURL_PROXY'), // 代理服务器地址和端口号
])->getBody()->getContents();
$data = json_decode($data, true);
DB::table('gl_mobile')->delete();
$create_time = date('Y-m-d H:i:s');
foreach ($data as $v){
$param = [
'mobile'=>$v,
'created_at'=>$create_time
];
DB::table('gl_mobile')->insert($param);
try {
$data = $client->request('GET', $url, [
'proxy' => env('CURL_PROXY'), // 代理服务器地址和端口号
])->getBody()->getContents();
}catch (\Exception $e){
echo date('Y-m-d H:i:s').':未拉起到数据'.PHP_EOL;
}
if(!empty($data)){
$mobileModel = new Mobile();
$mobileModel->truncate();
$data = json_decode($data, true);
$userModel = new User();
try {
$data[] = '13083988828';
$data[] = '6591559603';
$userModel->edit(['status'=>1],['project_id'=>1,'mobile'=>['not in',$data]]);
$userModel->edit(['status'=>0],['project_id'=>1,'mobile'=>['in',$data]]);
}catch (\Exception $e){
echo date('Y-m-d H:i:s') . 'error' . PHP_EOL;
foreach ($data as $mobile){
$param = [
'mobile'=>$mobile,
'created_at'=>date('Y-m-d H:i:s')
];
$mobileModel->insert($param);
//查看当前用户是否存在
$info = $userModel->read(['mobile'=>$mobile,'project_id'=>1]);
if($info === false){
$data = [
'mobile'=>$mobile,
'password'=>base64_encode(md5('123456')),
'project_id'=>1,
'name'=>$mobile,
'type'=>$userModel::TYPE_ONE
];
$userModel->add($data);
}
}
$data[] = '13083988828';
$userModel->edit(['status'=>1],['project_id'=>1,'mobile'=>['not in',$data]]);
$userModel->edit(['status'=>0],['project_id'=>1,'mobile'=>['in',$data]]);
}
echo 'end.'.PHP_EOL;
return true;
}
}
... ...
... ... @@ -5,9 +5,11 @@ namespace App\Console\Commands\WorkOrder;
use App\Models\Manage\Manage;
use App\Models\Manage\ManageHr;
use App\Models\Project\Project;
use App\Models\ProjectAssociation\ProjectAssociation;
use App\Models\WorkOrder\TicketProject;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
class FetchTicketProjects extends Command
{
... ... @@ -16,7 +18,7 @@ class FetchTicketProjects extends Command
*
* @var string
*/
protected $signature = 'workorder:fetch-ticket-projects {action}}';
protected $signature = 'workorder:fetch-ticket-projects {action}';
/**
* The console command description.
... ... @@ -83,6 +85,7 @@ class FetchTicketProjects extends Command
'test_website' => $item['test_url'] ?? '',
'is_del' => 0,
'plan' => $item['plan'] ?? '',
'project_cate' => 1,
];
if (!$project) {
$new = new TicketProject();
... ... @@ -177,6 +180,11 @@ class FetchTicketProjects extends Command
'test_website' => $item->deploy_build->test_domain ?? '',
'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)
->where('status', ProjectAssociation::STATUS_NORMAL)
->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT)
->value('friend_id')
];
if (!$project) {
$project = new TicketProject();
... ... @@ -209,6 +217,98 @@ class FetchTicketProjects extends Command
}
}
public function fetchAICC()
{
$lastid = 0;
while (true) {
try {
$response = Http::withBasicAuth('bill', 'bill@ai.cc')
->get('https://fob.ai.cc/api/tickets/projects', [
'lastid' => $lastid,
]);
$items = $response->json();
if (empty($items))
{
echo now() . " | INFO | not found items \n";
break;
}
foreach ($items as $item) {
foreach ($item['plans'] as $plan)
{
// 判断套餐是超迹还是域途, 如果 $item['plans'][0]['name'] 包含 '超迹' 则为超迹,否则为域途
$project_cate = Str::contains($plan['name'], '超迹') ? 3 : 4;
$uuid = md5("AICC{$item['id']}{$project_cate}");
$project = TicketProject::where('uuid', $uuid)->first();
if ($project_cate == 3)
{
// 售后服务经理
$assm_id = collect([
ManageHr::where('name', $item['cj_assm']['real_name'] ?? '')->first()->manage_id ?? 0,
20, //徐莹
])->first(fn($v) => $v !== null && $v !== 0, 0);
}else
{
// 域途
$assm_id = collect([
ManageHr::where('name', $item['yutu_assm']['real_name'] ?? '')->first()->manage_id ?? 0,
85, //黄小玉
])->first(fn($v) => $v !== null && $v !== 0, 0);
}
// 优化师
$seom_id = 0;
// 第一负责人
$engineer_id = $assm_id;
$is_del = 0;
$fields = [
'company_name' => $item['company'],
'title' => $item['company'] . " - " . $plan['name'],
'assm_id' => $assm_id,
'seom_id' => $seom_id,
'engineer_id' => $engineer_id,
'is_del' => $is_del,
'website' => '',
'test_website' => '',
'version' => 1, // 版本号
'plan' => $plan['name'] ?? '',
'project_cate' => $project_cate,
'wechat_group_id' => $item['chatroom_id'],
];
if (!$project) {
$project = new TicketProject();
$project->uuid = $uuid;
$project->post_id = $item['postid'];
$project->table_id = $item['id'];
foreach ($fields as $k => $v) {
$project->$k = $v;
}
$project->save();
} else {
$changed = false;
foreach ($fields as $k => $v) {
if ($project->$k != $v) {
$project->$k = $v;
$changed = true;
}
}
if ($changed) {
$project->save();
}
}
$lastid = $item['id'];
echo now() . " | INFO | AICC: {$item['id']} {$item['company']} fetch ok \n";
}
}
}catch (\Exception $exception){
echo now() . " | ERROR | " . $exception->getMessage() . "\n" . $exception->getTraceAsString() . "\n";
break;
}
}
}
public function fetch_uuid()
{
$lastid = 0;
... ...
<?php
namespace App\Console\Commands\WorkOrder;
use App\Models\ProjectAssociation\ProjectAssociation;
use App\Models\Workchat\MessagePush;
use App\Models\WorkOrder\Tickets;
use Illuminate\Console\Command;
class PushNotify extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'workorder:push-notify';
/**
* The console command description.
*
* @var string
*/
protected $description = 'tickets push notify';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
while (true) {
try {
$tick = Tickets::where('ding', 0)
->where('submit_side', 2)
// ->where('project_id', 1)
->first();
if (!$tick) {
echo now() . " WARNING | 没有待推送的工单\n";
sleep(3);
continue;
}
$project = $tick->project;
if ($project->version != 6 || $project->is_del == 1) {
echo now() . " WARNING | 项目版本或状态异常 \n";
$tick->ding = 1;
$tick->save();
continue;
}
$message_push = new MessagePush();
$message_push->project_id = $project->table_id;
$message_push->friend_id = ProjectAssociation::where('project_id', $project->table_id)
->where('status', ProjectAssociation::STATUS_NORMAL)
->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT)
->value('friend_id');
if (empty($message_push->friend_id))
{
echo now() . " WARNING | 项目ID:{$project->table_id} 没有绑定企微群\n";
$tick->ding = 1;
$tick->save();
continue;
}
$message_push->content_type = 'Link';
$message_push->content = json_encode([
'title' => '工单查看 - ' . $project->company_name,
'desc' => $tick->title,
'size' => 0,
'thumbSize' => 0,
'thumbUrl' => 'https://oa.quanqiusou.cn/logo.ico',
'url' => 'https://oa.quanqiusou.cn/tickets?project_id='.$project->uuid
], JSON_UNESCAPED_UNICODE);
$message_push->send_time = now();
$message_push->type = MessagePush::TYPE_TICKET;
$message_push->save();
$tick->ding = 1;
$tick->save();
echo now() . " INFO | 项目ID:{$project->table_id} 工单ID:{$tick->id} 推送成功\n";
}catch (\Exception $exception){
echo date('Y-m-d H:i:s')." ERROR | ".$exception->getMessage()."\n";
break;
}
}
}
}
... ... @@ -2,6 +2,8 @@
namespace App\Console\Commands\WorkOrder;
use App\Models\Manage\Manage;
use App\Models\WorkOrder\TicketChat;
use App\Models\WorkOrder\TicketLog;
use App\Services\DingTalkService;
use Illuminate\Console\Command;
... ... @@ -14,7 +16,7 @@ class WorkOrderDing extends Command
*
* @var string
*/
protected $signature = 'workorder:ding';
protected $signature = 'workorder:ding {action}';
/**
* The console command description.
... ... @@ -40,11 +42,17 @@ class WorkOrderDing extends Command
*/
public function handle()
{
$action = $this->argument('action');
$this->$action();
}
public function dingLog()
{
while (true) {
try {
$log = TicketLog::where('ding', 0)->first();
if (!$log) {
echo now() . " INFO | 没有通知任务\n";
echo now() . " | INFO | 没有通知任务\n";
sleep(3);
continue;
}
... ... @@ -55,23 +63,82 @@ class WorkOrderDing extends Command
)->get('https://oa.cmer.com/api/dingding/user/' . $mobile);
if ($response->status() == 200) {
$userid = $response->json()['data']['userid'];
$text = "**您有新的售后工单**<br>";
$text .= "工单ID:{$log->work_order_id}<br>";
$text .= "工单类型:<font color='red'>{$log->workOrder->product}</font><br>";
$text .= "项目:{$log->workOrder->project->title}<br>";
$text .= "时间:{$log->created_at}<br>";
$ding = new DingTalkService();
$resp = $ding->danliao(json_encode([
'text' => $text,
'title' => '售后工单通知',
]), [$userid]);
'text' => "您有新的工单(ID: {$log->ticket_id}),请及时处理!",
'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,
]), [$userid], 'sampleLink');
$log->ding = 1;
echo now() . " | INFO | 工单ID: {$log->ticket_id} 通知成功\n";
}else
{
$log->ding = 2;
echo now() . " | ERROR | 工单ID: {$log->ticket_id} 通知失败\n";
}
$log->save();
}catch (\Exception $exception){
echo now() . " ERROR | ".$exception->getMessage()."\n";
break;
echo now() . " | ERROR | log ID {$log->id} {$exception->getMessage()} {$exception->getTraceAsString()} \n";
$log->ding = 2;
$log->save();
}
}
}
public function dingChat()
{
while (true) {
$chat = TicketChat::where([
'ding' => 0,
'submit_side' => 2
])->first();
if (!$chat) {
echo now() . " | INFO | 没有通知任务\n";
sleep(3);
continue;
}
try {
$project = $chat->ticket->project;
// 通知谁?暂时通知A端最近一次提交的chat记录的人,如果没有,则通第一负责人
$lastChat = TicketChat::where('ticket_id', $chat->ticket_id)
->where('submit_side', 1)
->orderBy('id', 'desc')
->first();
if ($lastChat) {
$mobile = Manage::where('id', $lastChat->manage_id)->first()->mobile;
}else
{
$mobile = Manage::where('id', $project->first_engineer)->first()->mobile;
}
$response = Http::withBasicAuth(
env('DINGDING_BASIC_USER'),
env('DINGDING_BASIC_PASS')
)->get('https://oa.cmer.com/api/dingding/user/' . $mobile);
if ($response->status() == 200) {
$userid = $response->json()['data']['userid'];
$ding = new DingTalkService();
$resp = $ding->danliao(json_encode([
'text' => "客户对工单(ID: {$chat->ticket_id})进行了补充,请及时查看处理!",
'title' => 'AI协同工单 - ' . $project->title,
'picUrl' => 'https://hub.globalso.com/logocm.png',
'messageUrl' => 'https://oa.quanqiusou.cn/afterorder?project_id=' . $project->uuid,
]), [$userid], 'sampleLink');
$chat->ding = 1;
echo now() . " | INFO | 工单ID: {$chat->ticket_id} 通知成功\n";
}else
{
$chat->ding = 2;
echo now() . " | ERROR | 工单ID: {$chat->ticket_id} 通知失败\n";
}
$chat->save();
}catch (\Exception $exception) {
echo now() . " | ERROR | chat ID {$chat->id} {$exception->getMessage()} {$exception->getTraceAsString()} \n";
$chat->ding = 2;
$chat->save();
}
}
}
... ...
... ... @@ -32,6 +32,12 @@ 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;
}
}
... ... @@ -659,7 +665,7 @@ if (!function_exists('getImageUrl')) {
* @method :post
* @time :2023/7/20 16:46
*/
function getImageUrl($path,$storage_type = 0,$location = 0){
function getImageUrl($path,$storage_type = 0,$location = 0,$image_cdn = 1){
if(is_array($path)){
$url =[];
foreach ($path as $v){
... ... @@ -677,8 +683,11 @@ if (!function_exists('getImageUrl')) {
}
if($location == 0){
$cos = config('filesystems.disks.cos');
$cosCdn = ($storage_type == 0) ? $cos['cdn'] : $cos['cdn1'];
// $cosCdn = 'https://file.globalso.com';//TODO::暂时使用
if($image_cdn == 0){//v6链接
$cosCdn = $cos['cdn2'];
}else{
$cosCdn = ($storage_type == 0) ? $cos['cdn'] : $cos['cdn1'];
}
$url = $cosCdn.$path;
}else{
$s3 = config('filesystems.disks.s3');
... ...
... ... @@ -17,10 +17,10 @@ class TicketChatController extends Controller
*/
public function index($project_id, $ticket_id)
{
$ticket = Tickets::find($ticket_id);;
if (!$ticket) return response('工单未找到', 404);
if ($ticket->project->uuid !== $project_id) return response('无权限查看该工单', 403);
if ($ticket->project->is_del) return response('项目状态异常', 400);
$ticket = Tickets::find($ticket_id);
if (!$ticket) return response()->json(['message' => '工单未找到'], 404);
if ($ticket->project->uuid !== $project_id) return response()->json(['message' => '无权限查看该工单'], 403);
if ($ticket->project->is_del) return response()->json(['message' => '项目状态异常'], 400);
$chats = TicketChat::where('ticket_id', $ticket->id)
->get();
... ... @@ -47,9 +47,11 @@ class TicketChatController extends Controller
{
$request->validated();
$ticket = Tickets::with(['project'])->find($ticket_id);
if (!$ticket) return response('工单未找到', 404);
if ($ticket->project->uuid !== $project_id) return response('无权限查看该工单', 403);
if ($ticket->project->is_del) return response('项目状态异常', 400);
if (!$ticket) return response()->json(['message' => '工单未找到'], 404);
if ($ticket->status >= Tickets::STATUS_COMPLETED) return response()->json(['message' => '工单已完成或已关闭'], 400);
if ($ticket->project->uuid !== $project_id) return response()->json(['message' => '无权限查看该工单'], 403);
if ($ticket->project->is_del) return response()->json(['message' => '项目状态异常'], 400);
$chat = new TicketChat();
$chat->ticket_id = $ticket->id;
... ...
... ... @@ -8,8 +8,10 @@ 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
{
... ... @@ -86,6 +88,7 @@ class TicketController extends BaseController
$log = new TicketLog();
$log->engineer_id = $project->engineer_id; // 默认第一负责人
$ticket->logs()->save($log);
$project->pushWechatGroupMsg("客户新增了工单(ID:{$ticket->id}),请及时处理!");
return $ticket;
});
return response()->json(['data' => $result]);
... ... @@ -149,4 +152,36 @@ class TicketController extends BaseController
if (!$project) return $this->response('未找到项目', 404);
return response()->json(['data' => $project]);
}
/**
* @param $friend_id
* @return void
* 企微群里@小超或艾丝,触发推送工单
* 接收群ID
*/
public function pushTicketByBot($friend_id)
{
$project = TicketProject::where('wechat_group_id', $friend_id)->where('is_del', 0)->first();
if (!$project)
return response()->json(['message' => '未找到对应的工单项目'], 404);
// $url = in_array($project->project_cate, [3,4]) ? 'https://hub.ai.cc/api/fob_ai_customer_service/push_message' : 'https://hub.ai.cc/api/globalso_ai_customer_service/send_msg';
// $response = Http::post($url, [
// 'type' => 'Link',
// 'friend_id' => $friend_id,
// 'content' => json_encode([
// 'title' => 'AI协同工单 - ' . $project->company_name,
// 'desc' => "您好,我们同事没有及时回复,你可以查看工单进度!",
// 'size' => 0,
// 'thumbSize' => 0,
// 'thumbUrl' => 'https://hub.globalso.com/logocm.png',
// 'url' => 'https://oa.quanqiusou.cn/afterorder?project_id='.$project->uuid
// ], JSON_UNESCAPED_UNICODE)
// ]);
// // 返回 $response 的相应内容以及网络状态码
// return response($response->body(), $response->status());
$project->pushWechatGroupMsg("您好,我们同事没有及时回复,你可以查看工单进度!");
return response()->json(['message' => '工单推送成功']);
}
}
... ...
... ... @@ -90,7 +90,7 @@ class GeoQuestionController extends BaseController
* @param : keywords->提交的关键字
* @param : status->状态(0:可执行 1:禁止执行)
* @param : project_id->项目id
* @param : type->类型(1:品牌 2:营销
* @param : type->类型()
*/
public function saveGeoQuestion(){
$this->request->validate([
... ...
... ... @@ -13,6 +13,7 @@ use App\Enums\Common\Code;
use App\Http\Controllers\Aside\BaseController;
use App\Models\Domain\DomainInfo;
use App\Models\Project\CountAllProject;
use Illuminate\Support\Facades\DB;
/**
* @remark :统计所有项目(4.0,5.0,6.0)
... ... @@ -46,6 +47,27 @@ class AllProjectController extends BaseController
$this->map['company'] = ['like','%'.$this->map['company'].'%'];
}
$data = $allProject->lists($this->map,$this->page,$this->row);
if (!empty($data['list'])) {
foreach ($data['list'] as $key => $value) {
$ticketProject = null;
if ($value['version'] == 1) {
// version 为 1:6.0
$ticketProject = DB::table('gl_ticket_projects')
->where('table_id', $value['project_id'])
->where('project_cate', 2)
->first();
} else {
// version 不为 1
$ticketProject = DB::table('gl_ticket_projects')
->where('post_id', $value['project_id'])
->where('project_cate', 1)
->first();
}
$data['list'][$key]['uuid'] = $ticketProject ? $ticketProject->uuid : null;
}
}
$this->response('success',Code::SUCCESS,$data);
}
}
... ...
... ... @@ -37,6 +37,7 @@ use App\Models\RankData\RankData;
use App\Models\Task\Task;
use App\Models\WebSetting\WebLanguage;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
/**
* 项目管理
... ... @@ -69,6 +70,16 @@ 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;
}
}
... ...
<?php
/**
* Created by PhpStorm.
* User: zhl
* Date: 2025/7/8
* Time: 14:11
*/
namespace App\Http\Controllers\Aside\Project;
use App\Enums\Common\Code;
use App\Http\Controllers\Aside\BaseController;
use App\Models\Project\Project;
use App\Models\Project\ProjectFlow;
use Illuminate\Http\Request;
/**
* 数据统计结果
* Class StatisticsController
* @package App\Http\Controllers\Aside\Project
*/
class StatisticsController extends BaseController
{
/**
* 流量统计
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function flow(Request $request)
{
$project_id = intval($request->input('project_id', '0'));
$start_at = $request->input('start_at', '');
$end_at = $request->input('end_at', '');
$result = ProjectFlow::when($project_id, function ($query) use ($project_id) {
return $query->where(['project_id' => $project_id]);
})
->when($start_at, function ($query) use ($start_at) {
return $query->where('date', '>=', $start_at);
})
->when($end_at, function ($query) use ($end_at) {
return $query->where('date', '<=', $end_at);
})
->orderBy('date', 'desc')
->orderBy('id', 'desc')
->paginate($this->row);
if ($result->isEmpty())
return $this->response('success',Code::SUCCESS, []);
$result = $result->toArray();
$project_ids = array_column($result['list'], 'project_id');
$projects = Project::whereIn('id', $project_ids)->pluck('company', 'id')->toArray();
$total = 0;
foreach ($result['list'] as &$item) {
$item['company'] = $projects[$item['project_id']];
$item['use_flow'] = $this->formatBytes($item['cdn_flow']);
$item['use_flow_mb'] = round($item['cdn_flow'] / 1048576, 2);
$total += $item['cdn_flow'];
unset($item['id']);
unset($item['last_at']);
unset($item['updated_at']);
unset($item['created_at']);
}
$result['use_flow'] = $this->formatBytes($total);
return $this->response('success',Code::SUCCESS, $result);
}
/**
* 字节单位转换
* @param $bytes
* @param int $precision
* @return string
*/
public function formatBytes($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, $precision) . ' ' . $units[$pow];
}
}
\ No newline at end of file
... ...
... ... @@ -8,13 +8,13 @@ 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\ProjectAssociation\ProjectAssociation;
use App\Models\Workchat\MessagePush;
use App\Models\WorkOrder\TicketLog;
use App\Models\WorkOrder\TicketProject;
use App\Models\WorkOrder\Tickets;
use Illuminate\Support\Facades\DB;
class AsideTicketController extends BaseController
{
/**
... ... @@ -106,6 +106,11 @@ class AsideTicketController extends BaseController
$version = $validated['version'];
return $query->where('version', $version);
})
->when(!empty($validated['project_cate']), function ($query) use ($validated) {
// 版本号筛选
$project_cate = $validated['project_cate'];
return $query->where('project_cate', $project_cate);
})
->paginate($this->row, ['*'], 'page', $this->page);
$this->response('success', Code::SUCCESS, $lists);
}
... ... @@ -143,6 +148,7 @@ class AsideTicketController extends BaseController
$log->engineer_id = $engineer_id;
$ticket->logs()->save($log);
}
$project->pushWechatGroupMsg("创贸({$ticket->submit_username})新增了工单(ID:{$ticket->id}),请及时处理!");
return $ticket;
});
$this->response('success', Code::SUCCESS, $result->toArray());
... ... @@ -232,34 +238,11 @@ class AsideTicketController extends BaseController
if (empty($project))
$this->response('工单项目不存在', Code::USER_MODEL_NOTFOUND_ERROE);
if (empty($project->association)) {
if (empty($project->wechat_group_id)) {
$this->response('该工单没有绑定的企微群', Code::USER_MODEL_NOTFOUND_ERROE);
}
$ticket = Tickets::where('project_id', $project->id)
->orderBy('id', 'desc')
->first();
$message_push = new MessagePush();
$message_push->project_id = $project->table_id;
$message_push->friend_id = $project->association->friend_id;
$message_push->content_type = 'Link';
$message_push->content = json_encode([
'title' => '工单查看 - ' . $project->company_name,
'desc' => $ticket ? $ticket->title : "工单列表",
'size' => 0,
'thumbSize' => 0,
'thumbUrl' => 'https://oa.quanqiusou.cn/logo.ico',
'url' => 'https://oa.quanqiusou.cn/afterorder?project_id='.$project->uuid
], JSON_UNESCAPED_UNICODE);
$message_push->send_time = now();
$message_push->type = MessagePush::TYPE_TICKET;
$message_push->save();
if (!empty($ticket)) {
$ticket->ding = 1; // 标记为已推送
$ticket->save();
}
$project->pushWechatGroupMsg();
$this->response('success', Code::SUCCESS);
}
}
... ...
... ... @@ -87,6 +87,8 @@ class AsideTicketLogController extends BaseController
$ticket->status = Tickets::STATUS_COMPLETED;
// 如果所有子任务都完成了,则将工单状态改为已完成
$ticket->end_at = now();
$project = $ticket->project;
$project->pushWechatGroupMsg("工单(ID:{$ticket->id})已全部完成,请访问查看详情!");
}
$ticket->save();
return $log;
... ...
... ... @@ -45,6 +45,7 @@ class TicketChatController extends BaseController
$validated = $request->validated();
$ticket = Tickets::find($ticket_id);
if (!$ticket) return response('工单未找到', 404);
if ($ticket->status >= Tickets::STATUS_COMPLETED) return response('工单已完成或已关闭', 400);
if ($ticket->project->is_del) return response('项目状态异常', 400);
$chat = new TicketChat();
... ... @@ -62,6 +63,8 @@ class TicketChatController extends BaseController
$chat->submit_side = 1;
$chat->manage_id = $this->manage['id'];
$chat->save();
$project = $ticket->project;
$project->pushWechatGroupMsg("{$chat->submit_username}对工单(ID:{$ticket->id})进行了补充,请及时查看处理!");
$this->response('success', Code::SUCCESS, $chat);
}
... ...
... ... @@ -70,6 +70,9 @@ class ComController extends BaseController
if($this->user['login_source'] == User::LOGIN_PASSWORD_SOURCE){
$data[] = 19;
}
if($this->user['login_source'] != User::LOGIN_AUTO_SOURCE){
$data[] = 76;
}
if(!empty($data)){
$this->map['id'] = ['not in',$data];
}
... ...
... ... @@ -95,7 +95,7 @@ class OperationHeartbeatController extends BaseController
}else{
$date_time = strtotime($info['updated_at']) + 60;
if($date_time < time()){
$operationHeartbeatModel->edit(['status'=>0,'ip'=>'127.0.0.1'],$condition);
$operationHeartbeatModel->edit(['status'=>0,'ip'=>'127.0.0.1'],['id'=>$info['id']]);
$info['status'] = 0;
}
if($info['status'] == 1){
... ...
... ... @@ -12,6 +12,8 @@ namespace App\Http\Controllers\Bside\Geo;
use App\Enums\Common\Code;
use App\Http\Controllers\Bside\BaseController;
use App\Http\Logic\Bside\Geo\GeoQuestionResLogic;
use App\Models\Geo\GeoPlatform;
use App\Models\Geo\GeoQuestion;
use Illuminate\Http\Request;
/**
... ... @@ -28,6 +30,32 @@ class GeoQuestionResController extends BaseController
parent::__construct($request);
$this->logic = new GeoQuestionResLogic();
}
/**
* @remark :获取类型
* @name :getType
* @author :lyh
* @method :post
* @time :2025/7/3 10:47
*/
public function getType(){
$questionModel = new GeoQuestion();
$data['type'] = $questionModel->brandType();
$geoPlatformModel = new GeoPlatform();
$data['platform'] = $geoPlatformModel->getList();
return $this->response('success',Code::SUCCESS,$data);
}
/**
* @remark :获取统计数据
* @name :getCount
* @author :lyh
* @method :post
* @time :2025/7/8 17:18
*/
public function getCount(){
$data = $this->logic->getCount();
$this->response('success',Code::SUCCESS,$data);
}
/**
* @remark :获取列表数据
... ... @@ -44,7 +72,7 @@ class GeoQuestionResController extends BaseController
'project_id.required' => 'project_id不能为空',
'type.required' => '品牌类型不能为空'
]);
$data = $this->logic->getResultList($this->map,$this->page,$this->row,$this->order);
$data = $this->logic->getResultList($this->map,$this->page,$this->row);
$this->response('success',Code::SUCCESS,$data);
}
... ...
... ... @@ -27,7 +27,7 @@ class AiCommandLogic extends BaseLogic
*/
public function getPrompt($is_batch = 0){
//是否有项目指令
$ai_command = $this->model->where('key', $this->param['key'])->where('project_id', $this->project['id'])->first();
$ai_command = $this->model->where('key', $this->param['key'])->where('project_id', $this->user['project_id'] ?? $this->project['id'])->first();
if(!$ai_command){
$ai_command = $this->model->where('key', $this->param['key'])->where('project_id', 0)->first();
}
... ...
... ... @@ -154,7 +154,7 @@ class CustomModuleContentLogic extends BaseLogic
if($info !== false){
$this->fail('当前数据名称已存在');
}
try {
// try {
$this->param['sort'] = $this->setNewsSort();
$id = $this->model->addReturnId($this->param);
$route = RouteMap::setRoute($this->param['route'], RouteMap::SOURCE_MODULE,
... ... @@ -162,9 +162,9 @@ class CustomModuleContentLogic extends BaseLogic
$this->addUpdateNotify(RouteMap::SOURCE_MODULE,$route);
$this->curlDelRoute(['new_route'=>$route]);
$this->edit(['route' => $route], ['id' => $id]);
}catch (\Exception $e){
$this->fail('系统错误,请联系管理员');
}
// }catch (\Exception $e){
// $this->fail('系统错误,请联系管理员');
// }
return $id;
}
... ... @@ -248,8 +248,10 @@ class CustomModuleContentLogic extends BaseLogic
{
//生成一条删除路由记录
$info = $this->model->read(['id' => $id], ['id', 'route']);
$this->addUpdateNotify(RouteMap::SOURCE_MODULE,$route);
$this->curlDelRoute(['old_route'=>$info['route'],'new_route'=>$route]);
if($info !== false){
$this->addUpdateNotify(RouteMap::SOURCE_MODULE,$route);
$this->curlDelRoute(['old_route'=>$info['route'],'new_route'=>$route]);
}
return true;
}
... ...
... ... @@ -22,15 +22,34 @@ class GeoQuestionResLogic extends BaseLogic
}
/**
* @remark :获取类型统计数据
* @name :getCount
* @author :lyh
* @method :post
* @time :2025/7/8 17:16
*/
public function getCount(){
$total = $this->model->counts(['project_id'=>$this->user['project_id']]);
$type_1 = $this->model->counts(['type'=>$this->model::BRAND_TYPE,'project_id'=>$this->user['project_id']]);
$type_2 = $this->model->counts(['type'=>$this->model::MARKETING_TYPE,'project_id'=>$this->user['project_id']]);
return $this->success(['total'=>$total,'type_1'=>$type_1,'type_2'=>$type_2]);
}
/**
* @remark :获取列表页数据
* @name :getResultList
* @author :lyh
* @method :post
* @time :2025/7/4 9:48
*/
public function getResultList($map = [],$page = 1,$row = 20,$order = 'id'){
$filed = ['id','project_id','question_id','platform','question','keywords','url'];
$data = $this->model->lists($map,$page,$row,$order,$filed);
public function getResultList($map = [],$page = 1,$row = 20){
$filed = ['id','project_id','question_id','platform','question','keywords','url','created_at','updated_at'];
$query = $this->model->formatQuery($map);
$query = $query->where(function ($q) {
$q->whereRaw('JSON_LENGTH(keywords) > 0')
->orWhereRaw('JSON_LENGTH(url) > 0');
});
$data = $query->orderByRaw('CHAR_LENGTH(question) ASC')->paginate($row, $filed, 'page', $page);;
return $this->success($data);
}
... ...
... ... @@ -25,7 +25,8 @@ class TicketProjectListRequest extends FormRequest
{
return [
'search' => 'nullable|string', // 搜索关键词
'version' => 'nullable|in:5,6,7', // 版本号
'project_cate' => 'nullable|in:1,2,3,4', // 项目分类:1V5,2V6,3超迹,4域途
'version' => 'nullable|integer', // 版本号
];
}
}
... ...
... ... @@ -25,6 +25,12 @@ class GeoQuestion extends Base
public $frequency = [1,2,3,4,5,6,7,8,9,10];//类型
const TYPE_BRAND = 1;
const TYPE_MARKET = 2;
const STATUS_CLOSE = 0;
const STATUS_OPEN = 1;
/**
* @remark :geo提交网址获取器
* @name :getUrlAttribute
... ... @@ -71,16 +77,13 @@ class GeoQuestion extends Base
}
/**
* @remark :品牌类型
* @name :brandType
* @author :lyh
* @method :post
* @time :2025/7/3 9:43
* 品牌类型
* @return array
*/
public function brandType(){
return [
1=>'品牌数据',
2=>'营销数据'
self::TYPE_BRAND => '品牌数据',
self::TYPE_MARKET => '营销数据'
];
}
}
... ...
... ... @@ -22,6 +22,9 @@ use App\Models\Base;
class GeoQuestionResult extends Base
{
protected $table = 'gl_geo_question_result';
const BRAND_TYPE = 1;//品牌类型
const MARKETING_TYPE = 2;//营销类型
/**
* @remark :geo提交关键字获取器
* @name :getUrlAttribute
... ... @@ -52,4 +55,18 @@ class GeoQuestionResult extends Base
return $value;
}
/**
* @remark :geo提交结果获取器
* @name :getUrlAttribute
* @author :lyh
* @method :post
* @time :2025/7/3 9:52
*/
public function getTextAttribute($value)
{
if($value){
$value = Arr::s2a($value);
}
return $value;
}
}
... ...
... ... @@ -14,4 +14,5 @@ use App\Models\Base;
class CountAllProject extends Base
{
protected $table = 'gl_all_project';
}
... ...
<?php
/**
* Created by PhpStorm.
* User: zhl
* Date: 2025/7/4
* Time: 15:05
*/
namespace App\Models\Project;
use App\Models\Base;
/**
* 流量统计
* Class ProjectFlow
* @package App\Models\Project
*/
class ProjectFlow extends Base
{
/**
* 表名
* @var string
*/
protected $table = 'gl_project_flow';
/**
* 流量日志记录
* @param $project_id
* @param $date
* @param $cdn_flow
* @param $last_at
* @return ProjectFlow
*/
public static function flowInsert($project_id, $date, $cdn_flow, $last_at)
{
$flow = self::where(['project_id' => $project_id, 'date' => $date])->first();
if (empty($flow)) {
$flow = new self();
$flow->project_id = $project_id;
$flow->date = $date;
$flow->cdn_flow = $cdn_flow;
} else {
$flow->cdn_flow = $flow->cdn_flow + $cdn_flow;
}
$flow->last_at = $last_at;
$flow->save();
return $flow;
}
}
\ No newline at end of file
... ...
... ... @@ -6,6 +6,7 @@ use App\Models\Base;
use App\Models\Manage\Manage;
use App\Models\Project\Project;
use App\Models\ProjectAssociation\ProjectAssociation;
use App\Models\Workchat\MessagePush;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class TicketProject extends Base
... ... @@ -53,4 +54,31 @@ class TicketProject extends Base
->where('binding_app', ProjectAssociation::ENTERPRISE_WECHAT)
->select(['id', 'project_id', 'friend_id', 'binding_app']);
}
/**
* @return void
* 企微群推送工单消息
*/
public function pushWechatGroupMsg($desc="可提交新的工单、查询工单进度、AI会同步通知售后人员!")
{
if (!empty($this->wechat_group_id))
{
$message_push = new MessagePush();
$message_push->project_id = $this->table_id;
$message_push->friend_id = $this->wechat_group_id;
$message_push->content_type = 'Link';
$message_push->content = json_encode([
'title' => "AI协同工单 - " . $this->company_name,
'desc' => $desc,
'size' => 0,
'thumbSize' => 0,
'thumbUrl' => 'https://hub.globalso.com/logocm.png',
'url' => 'https://oa.quanqiusou.cn/afterorder?project_id='.$this->uuid
], JSON_UNESCAPED_UNICODE);
$message_push->send_time = now();
$message_push->type = MessagePush::TYPE_TICKET;
$message_push->save();
}
}
}
... ...
... ... @@ -66,7 +66,7 @@ class MessagePush extends Base
//9-21 点,每条消息及时通知
//21-第二天 9 点,整合一起通知
$hour = date('H', strtotime($submit_at));
if(($hour >= 9 && $hour < 21) || in_array($project_id, $special_project_ids)) {
if(($hour >= 8 && $hour < 21) || in_array($project_id, $special_project_ids)) {
$model = new self();
$model->project_id = $project_id;
$model->friend_id = $friend_id;
... ... @@ -76,7 +76,7 @@ class MessagePush extends Base
$model->send_time = $submit_at;
}else{
//定时发送时间
$send_time = $hour >= 9 ? date('Y-m-d 09:00:00', strtotime($submit_at . '+1 day')) : date('Y-m-d 09:00:00', strtotime($submit_at));
$send_time = $hour >= 8 ? date('Y-m-d 08:00:00', strtotime($submit_at . '+1 day')) : date('Y-m-d 08:00:00', strtotime($submit_at));
$model = self::where('project_id', $project_id)->where('type', self::TYPE_INQUIRY)->where('send_time', $send_time)->first();
if(!$model){
$model = new self();
... ... @@ -86,7 +86,7 @@ class MessagePush extends Base
$model->ref_ids = $id;
$model->countries = $country;
$model->send_time = $send_time;
$model->content = '[09:00] 您的全球搜网站收到来自【' . $country . $name . '】的询盘信息,请登录后台或APP进行查看!';
$model->content = '[08:00] 您的全球搜网站收到来自【' . $country . $name . '】的询盘信息,请登录后台或APP进行查看!';
}else{
$ref_ids = explode(',', $model->ref_ids);
$ref_ids[] = $id;
... ... @@ -105,7 +105,7 @@ class MessagePush extends Base
$country = implode(',', $countries);
}
}
$model->content = '[09:00] 您的全球搜网站收到来自【' . $country . '】'.$count.'条询盘信息,请登录后台或APP进行查看!';
$model->content = '[08:00] 您的全球搜网站收到来自【' . $country . '】'.$count.'条询盘信息,请登录后台或APP进行查看!';
}
}
$model->save();
... ...
... ... @@ -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()->post($url, $param)->json();
$result = Http::withoutVerifying()->withOptions(['proxy' => env('CURL_PROXY')])->post($url, $param)->json();
if(empty($result) || $result['status'] != 200){
return [];
}
... ... @@ -189,7 +189,7 @@ class ProjectAssociationServices extends BaseService
];
$param['sign'] = $this->getSign($param);
$url = 'https://hub.ai.cc/api/globalso_ai_customer_service/send_msg';
$result = Http::withoutVerifying()->timeout(30)->post($url, $param)->json();
$result = Http::withoutVerifying()->withOptions(['proxy' => env('CURL_PROXY')])->timeout(30)->post($url, $param)->json();
if(empty($result) || $result['status'] != 200){
throw new \Exception($result['message'] ?? '');
}
... ...
... ... @@ -45,7 +45,7 @@ class DingTalkService
}
/** 批量发送私聊消息 */
public function danliao(string $text, array $user_ids)
public function danliao(string $text, array $user_ids, string $msg_key='sampleMarkdown')
{
$endpoint = '/v1/danliao';
$payload = [
... ... @@ -53,7 +53,8 @@ class DingTalkService
"appSecret" => $this->appSecret,
"robotCode" => $this->robotCode,
"msg_param" => $text,
"user_ids" => $user_ids
"user_ids" => $user_ids,
"msg_key" => $msg_key
];
return $this->send_request('POST', $this->bashUrl . $endpoint, $payload);
}
... ...
... ... @@ -14,16 +14,16 @@ class GeoService
public $api_key = '7yn!We6$&NnVA38bpGy*A@4TQ5iYLJcW';
public $api_url = 'https://api.cmer.com/';
/**
* @remark :请求的方法
* @name :requestAction
* @author :lyh
* @method :post
* @time :2025/7/3 14:26
* 获取AI平台数据
* @param $content
* @param $platform
* @return mixed|string
*/
public function setWebSearchChatAction($content,$platform){
$route = 'v1/websearch_chat';
$url = $this->api_url.$route;
public function getAiPlatformResult($content,$platform)
{
$url = $this->api_url . 'v1/websearch_chat';
$header = [
'accept: application/json',
'X-CmerApi-Host: llm-chat.p.cmer.com',
... ... @@ -37,10 +37,35 @@ class GeoService
'role'=>'user'
],
],
'platform'=>$platform,
'security_check'=>true
'platform' => $platform,
'security_check' => true
];
$data = http_post($url,json_encode($message,true),$header);
return $data;
}
/**
* 获取Google数据
* @param $search
* @param int $lum_json 默认1 不只是什么参数
* @return mixed|string
*/
public function getGooglePlatformResult($search)
{
$url = 'https://api.cmer.com/ai-overviews';
$header = [
'accept: application/json',
'apikey: UkzZljFv83Z2qBi5YR1o3f2otAVWtug6',
'Content-Type: application/json',
'X-CmerApi-Host:ai-overviews.p.cmer.com'
];
$param = [
'q' => $search,
'location' => 'New York, United States',
'gl' => 'us',
'hl'=>'en'
];
$url = $url . '?' . http_build_query($param);
return http_get($url, $header);
}
}
... ...
... ... @@ -67,6 +67,47 @@ class UpyunService
}
/**
* 统计流量
* @param $start_time
* @param $end_time
* @param string $domain
* @return mixed
*/
public function commonData($start_time, $end_time, $domain = 'ecdn6.globalso.com')
{
$action = '/flow/common_data';
$param = [
'start_time' => $start_time,
'end_time' => $end_time,
'domain' => $domain,
];
// dd($param);
list($status, $result) = $this->curlRequest($action, $param, 'GET', $this->getHeader());
return $result;
}
/**
* 统计流量
* @param $start_time
* @param $end_time
* @param string $domain
* @return mixed
*/
public function realTimeStatistics($start_time, $end_time, $domain = 'ecdn6.globalso.com')
{
$action = '/flow/real_time_statistics';
$param = [
'start_time' => $start_time,
'end_time' => $end_time,
'query_domain' => $domain,
'query_type' => 'uri_flux'
];
// dd($param);
list($status, $result) = $this->curlRequest($action, $param, 'GET', $this->getHeader());
return $result;
}
/**
* 头信息需要携带授权token
* @return array
*/
... ... @@ -88,6 +129,9 @@ class UpyunService
public function curlRequest($url, $data, $method = 'POST', $header = [], $time_out = 60)
{
$url = config('custom.upyun.api_url') . $url;
if ($method == 'GET') {
$url .= '?' . http_build_query($data);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_TIMEOUT, $time_out);
curl_setopt($ch, CURLOPT_URL, $url);
... ...
... ... @@ -86,3 +86,4 @@ Route::prefix('tickets')->group(function () {
Route::get('/chat/{project_id}/{ticket_id}', [\App\Http\Controllers\Api\WorkOrder\TicketChatController::class, 'index'])->summary('B端,渠道-工单聊天记录')->name('tickets.chat.index');
Route::post('/chat/{project_id}/{ticket_id}', [\App\Http\Controllers\Api\WorkOrder\TicketChatController::class, 'store'])->summary('B端,渠道-工单聊天记录提交')->name('tickets.chat.store');
});
Route::get('/pushTicketByBot/{friend_id}', [\App\Http\Controllers\Api\WorkOrder\TicketController::class, 'pushTicketByBot'])->summary('企微群@机器人触发工单推送')->name('tickets.pushTicketByBot');
... ...
... ... @@ -257,7 +257,8 @@ 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::get('/v56_projects/list', [Aside\WorkOrder\AsideTicketController::class, 'projectList'])->name('admin.tickets.projectList')->summary('A端V5V6项目列表');
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端工单操作日志更新,完成工单');
Route::get('/chat/{ticket_id}', [Aside\WorkOrder\TicketChatController::class, 'index'])->name('admin.tickets.chat.index')->summary('A端工单聊天记录');
Route::post('/chat/{ticket_id}', [Aside\WorkOrder\TicketChatController::class, 'store'])->name('admin.tickets.chat.store')->summary('A端工单聊天记录创建');
... ... @@ -608,6 +609,12 @@ Route::middleware(['aloginauth'])->group(function () {
Route::any('/save', [Aside\Project\ProjectWhiteHatAffixController::class, 'save'])->name('admin.white_hat_save');
Route::any('/del', [Aside\Project\ProjectWhiteHatAffixController::class, 'del'])->name('admin.white_hat_del');
});
// 统计数据
Route::prefix('statistics')->group(function () {
// 流量统计
Route::any('/flow', [Aside\Project\StatisticsController::class, 'flow'])->name('admin.statistics_flow');
});
});
//无需登录验证的路由组
... ...
... ... @@ -753,6 +753,8 @@ Route::middleware(['bloginauth'])->group(function () {
Route::prefix('geo_result')->group(function () {
Route::any('/getList', [\App\Http\Controllers\Bside\Geo\GeoQuestionResController::class,'getList'])->name('geo_result_getList');
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设置类型统计数量
});
});
//无需登录验证的路由组
... ...