作者 ZhengBing He

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

正在显示 47 个修改的文件 包含 3318 行增加396 行删除

要显示太多修改。

为保证性能只显示 47 of 47+ 个文件。

@@ -293,6 +293,9 @@ class AiBlogTask extends Command @@ -293,6 +293,9 @@ class AiBlogTask extends Command
293 $domainModel = new DomainInfo(); 293 $domainModel = new DomainInfo();
294 $project_model = new Project(); 294 $project_model = new Project();
295 foreach ($routes as $project_id => $route){ 295 foreach ($routes as $project_id => $route){
  296 + if($project_id == 4339){
  297 + continue;
  298 + }
296 $route[] = 'top-blog'; 299 $route[] = 'top-blog';
297 $domain = $domainModel->getProjectIdDomain($project_id); 300 $domain = $domainModel->getProjectIdDomain($project_id);
298 if (empty($domain)) { 301 if (empty($domain)) {
@@ -113,7 +113,7 @@ class DomainInfo extends Command @@ -113,7 +113,7 @@ class DomainInfo extends Command
113 } else { 113 } else {
114 //除自建站项目外,记录已解析到别的ip的域名 114 //除自建站项目外,记录已解析到别的ip的域名
115 if (!check_domain_record($v['domain'], $servers_ip_info)) { 115 if (!check_domain_record($v['domain'], $servers_ip_info)) {
116 - Log::channel('analyze_other')->error('域名 [' . $v['domain'] . '] 已解析到别的IP'); 116 + Log::channel('analyze_other')->error('服务器ID [' . $servers_ip_info['servers_id'] . '] 域名 [' . $v['domain'] . '] 已解析到别的IP');
117 continue; 117 continue;
118 } 118 }
119 119
@@ -166,7 +166,7 @@ class DomainInfo extends Command @@ -166,7 +166,7 @@ class DomainInfo extends Command
166 if ($servers_ip_info['servers_id'] != ServerConfig::SELF_SITE_ID) { 166 if ($servers_ip_info['servers_id'] != ServerConfig::SELF_SITE_ID) {
167 //过滤已解析到别的ip的AMP域名 167 //过滤已解析到别的ip的AMP域名
168 if (!check_domain_record($amp_domain, $servers_ip_info)) { 168 if (!check_domain_record($amp_domain, $servers_ip_info)) {
169 - Log::channel('analyze_other')->error('AMP域名 [' . $amp_domain . '] 已解析到别的IP'); 169 + Log::channel('analyze_other')->error('服务器ID [' . $servers_ip_info['servers_id'] . '] AMP域名 [' . $amp_domain . '] 已解析到别的IP');
170 continue; 170 continue;
171 } 171 }
172 } 172 }
@@ -324,7 +324,7 @@ class DomainInfo extends Command @@ -324,7 +324,7 @@ class DomainInfo extends Command
324 324
325 //过滤已解析到别的ip的域名 325 //过滤已解析到别的ip的域名
326 if (!check_domain_record($v['custom_domain'], $servers_ip_info)) { 326 if (!check_domain_record($v['custom_domain'], $servers_ip_info)) {
327 - Log::channel('analyze_other')->error('自定义跳转域名 [' . $v['custom_domain'] . '] 已解析到别的IP'); 327 + Log::channel('analyze_other')->error('服务器ID [' . $servers_ip_info['servers_id'] . '] 自定义跳转域名 [' . $v['custom_domain'] . '] 已解析到别的IP');
328 continue; 328 continue;
329 } 329 }
330 330
@@ -43,7 +43,7 @@ class RemainDay extends Command @@ -43,7 +43,7 @@ class RemainDay extends Command
43 * @var 暂停的项目 43 * @var 暂停的项目
44 */ 44 */
45 protected $ceaseProjectId = [ 45 protected $ceaseProjectId = [
46 - 354, 378, 649, 1226, 1283, 1703, 1893, 2066, 2250, 2193, 2399, 1685 46 + 354, 378, 649, 1226, 1283, 1703, 1893, 2066, 2250, 2193, 2399, 1685, 3931
47 ];//需要单独处理的项目 47 ];//需要单独处理的项目
48 /** 48 /**
49 * The console command description. 49 * The console command description.
@@ -118,7 +118,7 @@ class RemainDay extends Command @@ -118,7 +118,7 @@ class RemainDay extends Command
118 */ 118 */
119 public function seoRemainDay($deploy_build,$item){ 119 public function seoRemainDay($deploy_build,$item){
120 //白帽版本的系统 120 //白帽版本的系统
121 - if($deploy_build['seo_plan'] == 1){ 121 + if($deploy_build['seo_plan'] != 0){
122 if($deploy_build['seo_service_duration'] != 0){ 122 if($deploy_build['seo_service_duration'] != 0){
123 if(in_array($item['id'],$this->bm_projectId) || (in_array( 19,$item['level']))){ 123 if(in_array($item['id'],$this->bm_projectId) || (in_array( 19,$item['level']))){
124 $compliance_day = (int)$item['bm_finish_remain_day']; 124 $compliance_day = (int)$item['bm_finish_remain_day'];
@@ -119,24 +119,23 @@ class GeoQuestionRes extends Command @@ -119,24 +119,23 @@ class GeoQuestionRes extends Command
119 $hit_data = array_merge($url, $title, $hit_data); 119 $hit_data = array_merge($url, $title, $hit_data);
120 } 120 }
121 $hit = 0; 121 $hit = 0;
122 - //todo::与预期结果是否复合  
123 - if(!empty($taskInfo['expect_result'])){  
124 - $str = "客户提出的问题:{$question},客户得到的回复:{$result['text']},客户需要预期:{$taskInfo['expect_result']},请分析得到的回复和预期是否一致,仅回复我是或者否";  
125 - $strResult = $geo_service->getChatResult($str, 'gpt-4o-mini');  
126 - if(isset($strResult['text']) && !empty($strResult['text'])){  
127 - switch ($strResult['text']){  
128 - case '是':  
129 - $is_match = 1;  
130 - $hit++;  
131 - break;  
132 - case '否':  
133 - $is_match = 2;  
134 - break;  
135 - default:  
136 - $is_match = 0;  
137 - break;  
138 - } 122 + $is_match = 0;
  123 + $cosine = 0;
  124 + $similarity = [];
  125 + // TODO 有预期结果,分析答案和预期结果
  126 + if(FALSE == empty($taskInfo['expect_result'])){
  127 + $cosine_result = $geo_service->cosineSimilarity($taskInfo['expect_result'], $result['text']);
  128 + // 语义是否一致
  129 + if (FALSE == empty($cosine_result['judgement'])) {
  130 + $is_match = $cosine_result['judgement'] == '语义相近' ? 1 : 2;
  131 + $hit++;
139 } 132 }
  133 + // 余弦相似度
  134 + if (FALSE == empty($cosine_result['similarity']))
  135 + $cosine = intval($cosine_result['similarity'] * 10000) / 100;
  136 + // 语句拆解结果
  137 + if (FALSE == empty($cosine_result['split_results']))
  138 + $similarity = $cosine_result['split_results'];
140 } 139 }
141 $hit_keyword = $this->getKeywords($taskInfo['keywords'],$hit_data); 140 $hit_keyword = $this->getKeywords($taskInfo['keywords'],$hit_data);
142 if (!empty($hit_keyword['keywords'])) { 141 if (!empty($hit_keyword['keywords'])) {
@@ -165,6 +164,8 @@ class GeoQuestionRes extends Command @@ -165,6 +164,8 @@ class GeoQuestionRes extends Command
165 'url_num'=>$url_num ?? [], 164 'url_num'=>$url_num ?? [],
166 'is_match'=>$is_match ?? 0, 165 'is_match'=>$is_match ?? 0,
167 'label'=>$taskInfo['label'] ?? null, 166 'label'=>$taskInfo['label'] ?? null,
  167 + 'cosine' => $cosine,
  168 + 'similarity' => json_encode($similarity, true),
168 'created_at'=>date('Y-m-d H:i:s'), 169 'created_at'=>date('Y-m-d H:i:s'),
169 'updated_at'=>date('Y-m-d H:i:s'), 170 'updated_at'=>date('Y-m-d H:i:s'),
170 ]; 171 ];
@@ -315,17 +316,6 @@ class GeoQuestionRes extends Command @@ -315,17 +316,6 @@ class GeoQuestionRes extends Command
315 } 316 }
316 317
317 /** 318 /**
318 - * @remark :预期结果对比  
319 - * @name :getExpectResult  
320 - * @author :lyh  
321 - * @method :post  
322 - * @time :2025/8/12 13:51  
323 - */  
324 - public function getExpectResult($question,$answer,$expect){  
325 - $str = "客户提出的问题:{$question},客户得到的回复:{$answer},客户需要预期:{$expect},请分析得到的回复和预期是否一致,仅回复我是或者否";  
326 - }  
327 -  
328 - /**  
329 * 获取待执行任务ID 319 * 获取待执行任务ID
330 * @return mixed 320 * @return mixed
331 */ 321 */
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: zhl
  5 + * Date: 2024/09/30
  6 + * Time: 11:51
  7 + */
  8 +namespace App\Console\Commands\Inquiry;
  9 +
  10 +use App\Helper\Common;
  11 +use App\Helper\Gpt;
  12 +use App\Helper\Translate;
  13 +use App\Helper\Validate;
  14 +use App\Models\Ai\AiCommand;
  15 +use App\Models\Inquiry\ReInquiryConfig;
  16 +use App\Models\Inquiry\ReInquiryDetail;
  17 +use App\Models\Inquiry\ReInquiryDetailLog;
  18 +use App\Models\Inquiry\ReInquiryForm;
  19 +use App\Models\Inquiry\ReInquiryTask;
  20 +use App\Models\Inquiry\ReInquiryText;
  21 +use App\Models\Project\InquiryFilterConfig;
  22 +use App\Models\Project\Project;
  23 +use App\Models\WebSetting\WebLanguage;
  24 +use Illuminate\Console\Command;
  25 +use Illuminate\Support\Arr;
  26 +use Illuminate\Support\Facades\Cache;
  27 +use Illuminate\Support\Facades\DB;
  28 +use Illuminate\Support\Facades\Http;
  29 +use Illuminate\Support\Facades\Log;
  30 +use Illuminate\Support\Str;
  31 +
  32 +/**
  33 + * Class RelayInquiry
  34 + * @package App\Console\Commands\Inquiry
  35 + */
  36 +class RelayInquiryTest extends Command
  37 +{
  38 + /**
  39 + * The name and signature of the console command.
  40 + *
  41 + * @var string
  42 + */
  43 + protected $signature = 'relay_inquiry_test';
  44 +
  45 + /**
  46 + * The console command description.
  47 + *
  48 + * @var string
  49 + */
  50 + protected $description = '重启失败的转发询盘';
  51 +
  52 + /**
  53 + * Create a new command instance.
  54 + *
  55 + * @return void
  56 + */
  57 + public function __construct()
  58 + {
  59 + parent::__construct();
  60 + }
  61 +
  62 + /**
  63 + * 模拟访问来源占比
  64 + * @var array
  65 + */
  66 + protected $lyzb = [
  67 + 'https://www.google.com/' => 630,
  68 + 'http://www.google.com/' => 30,
  69 + 'http://www.bing.com/' => 20,
  70 + 'https://www.bing.com/' => 5,
  71 + 'https://www.youtube.com/' => 5,
  72 + 'https://search.yahoo.com/' => 5,
  73 + 'https://www.facebook.com/' => 5,
  74 + ];
  75 +
  76 + /**
  77 + * 俄语站 模拟访问来源占比
  78 + * @var array
  79 + */
  80 + protected $eylyzb = [
  81 + 'https://www.yandex.com/' => 630,
  82 + 'https://www.google.com/' => 30,
  83 + 'http://www.google.com/' => 30,
  84 + 'http://www.bing.com/' => 20,
  85 + 'https://www.bing.com/' => 5,
  86 + 'https://www.youtube.com/' => 5,
  87 + 'https://search.yahoo.com/' => 5,
  88 + 'https://www.facebook.com/' => 5,
  89 + ];
  90 +
  91 + /**
  92 + * PC端访问头信息
  93 + * @var array
  94 + */
  95 + protected $pc_ua = [
  96 + 0 => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11',
  97 + 1 => 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36',
  98 + 2 => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1'
  99 + ];
  100 +
  101 + /**
  102 + * 移动端访问头信息
  103 + * @var array
  104 + */
  105 + protected $mobile_ua = [
  106 + 0 => 'Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko; googleweblight) Chrome/38.0.1025.166 Mobile Safari/535.19',
  107 + ];
  108 +
  109 + /**
  110 + * google域名后缀
  111 + * @var string[]
  112 + */
  113 + protected $suffix = [
  114 + 'co.jp' => '日本',
  115 + 'com.tr' => '土耳其',
  116 + 'nl' => '荷兰',
  117 + 'ru' => '俄罗斯',
  118 + 'fr' => '法国',
  119 + 'co.kr' => '韩国',
  120 + 'fi' => '芬兰',
  121 + 'be' => '比利时',
  122 + 'lt' => '立陶宛',
  123 + 'es' => '西班牙',
  124 + 'it' => '意大利',
  125 + 'com.au' => '澳大利亚',
  126 + 'no' => '挪威',
  127 + 'al' => '阿尔巴尼亚',
  128 + 'pt' => '葡萄牙',
  129 + 'lv' => '拉脱维亚',
  130 + 'hu' => '匈牙利',
  131 + 'cz' => '捷克',
  132 + 'de' => '德国',
  133 + 'ca' => '加拿大',
  134 + 'co.in' => '印度',
  135 + 'co.uk' => '英国',
  136 + 'com.vn' => '越南',
  137 + 'com.br' => '巴西',
  138 + 'co.il' => '以色列',
  139 + 'pl' => '波兰',
  140 + 'com.eg' => '埃及',
  141 + 'co.th' => '泰国',
  142 + 'sk' => '斯洛伐克',
  143 + 'ro' => '罗马尼亚',
  144 + 'com.mx' => '墨西哥',
  145 + 'com.my' => '马来西亚',
  146 + 'com.pk' => '巴基斯坦',
  147 + 'co.nz' => '新西兰',
  148 + 'co.za' => '南非',
  149 + 'com.ar' => '阿根廷',
  150 + 'com.kw' => '科威特',
  151 + 'com.sg' => '新加坡',
  152 + 'com.co' => '哥伦比亚',
  153 + 'co.id' => '印度尼西亚',
  154 + 'gr' => '希腊',
  155 + 'bg' => '保加利亚',
  156 + 'mn' => '蒙古',
  157 + 'dk' => '丹麦',
  158 + 'com.sa' => '沙特阿拉伯',
  159 + 'com.pe' => '秘鲁',
  160 + 'com.ph' => '菲律宾',
  161 + 'com.ua' => '乌克兰',
  162 + 'ge' => '格鲁吉亚',
  163 + 'ae' => '阿拉伯联合酋长国',
  164 + 'tn' => '突尼斯',
  165 + ];
  166 +
  167 + protected $otherzb = [700, 300]; //模拟访问来源占比 (非美国) google.com|google.其他后缀
  168 +
  169 + /**
  170 + * 手机号过滤 0去掉+ 1不转发手机 2不处理
  171 + * @var int[]
  172 + */
  173 + protected $filter_phone = [30,12,58];
  174 +
  175 + /**
  176 + * 需要多个ip访问的国家
  177 + * @var
  178 + */
  179 + protected $multiple_ip_visit_country = [
  180 + '阿尔及利亚',
  181 + '埃及',
  182 + '埃塞俄比亚',
  183 + '安哥拉',
  184 + '贝宁',
  185 + '博茨瓦纳',
  186 + '布基纳法索',
  187 + '布隆迪',
  188 + '赤道几内亚',
  189 + '多哥',
  190 + '厄立特里亚',
  191 + '佛得角',
  192 + '冈比亚',
  193 + '刚果共和国',
  194 + '刚果民主共和国',
  195 + '吉布提',
  196 + '几内亚',
  197 + '几内亚比绍共和国',
  198 + '加纳',
  199 + '加蓬',
  200 + '津巴布韦',
  201 + '喀麦隆',
  202 + '科摩罗',
  203 + '科特迪瓦',
  204 + '肯尼亚',
  205 + '莱索托',
  206 + '利比里亚',
  207 + '利比亞',
  208 + '留尼汪',
  209 + '卢旺达',
  210 + '马达加斯加',
  211 + '马拉维',
  212 + '马里',
  213 + '马约特',
  214 + '毛里求斯',
  215 + '毛里塔尼亚',
  216 + '摩洛哥',
  217 + '莫桑比克',
  218 + '纳米比亚',
  219 + '南非',
  220 + '南苏丹',
  221 + '尼日尔',
  222 + '尼日利亚',
  223 + '塞拉利昂',
  224 + '塞内加尔',
  225 + '塞舌尔',
  226 + '圣多美和普林西比',
  227 + '圣赫勒拿',
  228 + '斯威士兰',
  229 + '索马里',
  230 + '坦桑尼亚',
  231 + '突尼斯',
  232 + '乌干达',
  233 + '西撒哈拉',
  234 + '赞比亚',
  235 + '乍得',
  236 + '中非共和国',
  237 + '阿富汗',
  238 + '阿联酋',
  239 + '阿曼',
  240 + '阿塞拜疆',
  241 + '澳门',
  242 + '巴基斯坦',
  243 + '巴勒斯坦',
  244 + '巴林',
  245 + '不丹',
  246 + '东帝汶',
  247 + '菲律宾',
  248 + '格鲁吉亚',
  249 + '哈萨克斯坦',
  250 + '韩国',
  251 + '吉尔吉斯斯坦',
  252 + '柬埔寨',
  253 + '卡塔尔',
  254 + '科威特',
  255 + '老挝',
  256 + '黎巴嫩',
  257 + '马尔代夫',
  258 + '马来西亚',
  259 + '蒙古',
  260 + '孟加拉国',
  261 + '缅甸',
  262 + '尼泊尔',
  263 + '日本',
  264 + '塞浦路斯',
  265 + '沙特阿拉伯',
  266 + '斯里兰卡',
  267 + '塔吉克斯坦',
  268 + '泰国',
  269 + '土耳其',
  270 + '土库曼斯坦',
  271 + '文莱',
  272 + '乌兹别克斯坦',
  273 + '新加坡',
  274 + '亚美尼亚',
  275 + '也门',
  276 + '伊拉克',
  277 + '以色列',
  278 + '印度尼西亚',
  279 + '约旦',
  280 + '越南',
  281 + ];
  282 +
  283 + /**
  284 + * @return bool
  285 + */
  286 + public function handle()
  287 + {
  288 + while (true) {
  289 + $inquiry = ReInquiryForm::where('created_at', '>', '2025-05-19 00:00:00')
  290 + ->where('created_at', '<', '2025-05-22 18:00:00')
  291 + ->where(['status' => ReInquiryForm::STATUS_FORGO])
  292 + ->where('remark', '超时72小时未处理!')
  293 + ->orderBy('id', 'asc')->get();
  294 +
  295 + if ($inquiry->isEmpty()){
  296 + $this->output('未发现待处理询盘!');
  297 + sleep(60);
  298 + continue;
  299 + }
  300 +
  301 + $this->output('开始处理本轮询盘!');
  302 + foreach ($inquiry as $key=>$val) {
  303 + $this->output('询盘ID:' . $val->id);
  304 +
  305 +
  306 + $detail_ids = ReInquiryDetail::where('form_id', $val->id)->pluck('id')->toArray();
  307 +
  308 + $inquiry_log = ReInquiryDetailLog::whereIn('detail_id', $detail_ids)->where('type', ReInquiryDetailLog::TYPE_INQUIRY)->first();
  309 + //有询盘的了 就跳过
  310 + if($inquiry_log){
  311 + continue;
  312 + }
  313 +
  314 + // 询盘对应广告
  315 + $ad_task = $this->getAdTask($val->ad_id);
  316 + // 没有获取到转发任务
  317 + if (empty($ad_task)) {
  318 + $val->status = ReInquiryForm::STATUS_FORGO;
  319 + $val->remark = '未找到需要转发的广告任务!';
  320 + $val->save();
  321 + continue;
  322 + }
  323 + // 未设置对法对象
  324 + $target_num_arr = array_map(function ($item) {
  325 + return count($item['target']) + count($item['shop_site']) + count($item['fob_pro']);
  326 + }, $ad_task);
  327 + if (!array_sum($target_num_arr)) {
  328 + $val->status = ReInquiryForm::STATUS_FORGO;
  329 + $val->remark = '广告任务转发对象为空!';
  330 + $val->save();
  331 + continue;
  332 + }
  333 + //是否要过滤
  334 + $filter_res = $this->filter($val);
  335 + if($filter_res !== true){
  336 + $val->status = ReInquiryForm::STATUS_FORGO;
  337 + $val->remark = $filter_res;
  338 + $val->save();
  339 + continue;
  340 + }
  341 + //验证手机号 无效 号码不推送
  342 + if(!Validate::phone($val->phone)){
  343 + $val->phone = '';
  344 + }
  345 + try {
  346 + $res = false;
  347 + foreach ($ad_task as $task){
  348 + $res += $this->relayDetail($task, $val);
  349 + $res += $this->relayShopDetail($task, $val);
  350 + $res += $this->relayFobDetail($task, $val);
  351 + }
  352 + $val->status = $res ? ReInquiryForm::STATUS_SUCCESS : ReInquiryForm::STATUS_FORGO;
  353 + $val->save();
  354 + } catch (\Exception $e) {
  355 + $this->logChannel()->info('执行询盘错误:',[$e->getMessage(), $e->getFile(), $e->getLine()]);
  356 + $this->output('执行询盘错误:' . $e->getMessage());
  357 + }
  358 + }
  359 + $this->output('本轮询盘处理结束!');
  360 + }
  361 + return true;
  362 + }
  363 +
  364 +
  365 + public function filter($data)
  366 + {
  367 + //通用过滤规则
  368 + $config = InquiryFilterConfig::getCacheInfoByProjectId(Project::DEMO_PROJECT_ID);
  369 + //FB询盘的全局过滤规则
  370 + $fb_config = ReInquiryConfig::getDefaultConfigCache(ReInquiryConfig::TYPE_FILTER_WORDS);
  371 +
  372 + $fb_config['filter_contents'] = array_filter(explode("\r\n", $fb_config['filter_contents']??''));
  373 + $fb_config['filter_emails'] = array_filter(explode("\r\n", $fb_config['filter_emails']??''));
  374 + $fb_config['filter_mobiles'] = array_filter(explode("\r\n", $fb_config['filter_mobiles']??''));
  375 + $fb_config['filter_names'] = array_filter(explode("\r\n", $fb_config['filter_names']??''));
  376 +
  377 + $config['filter_contents'] = array_unique(array_merge($fb_config['filter_contents'],$config['filter_contents']));
  378 + $config['filter_emails'] = array_unique(array_merge($fb_config['filter_emails'],$config['filter_emails']));
  379 + $config['filter_mobiles'] = array_unique(array_merge($fb_config['filter_mobiles'],$config['filter_mobiles']));
  380 + $config['filter_names'] = array_unique(array_merge($fb_config['filter_names'],$config['filter_names']));
  381 +
  382 + //过滤内容
  383 + if(!empty($data['message']) && !empty($config['filter_contents'])) {
  384 + foreach ($config['filter_contents'] as $filter_content) {
  385 + if (Str::contains(strtolower($data['message']), strtolower($filter_content))) {
  386 + return '过滤内容:' . $filter_content;
  387 + }
  388 + }
  389 + }
  390 + //过滤邮箱
  391 + if(!empty($data['email']) && !empty($config['filter_emails'])){
  392 + foreach ($config['filter_emails'] as $filter_email){
  393 + if(Str::contains(strtolower($data['email']), strtolower($filter_email))){
  394 + return '过滤邮箱:' . $filter_email;
  395 + }
  396 + }
  397 + //邮箱有效性
  398 + if(!Validate::email($data['email'])){
  399 + return '邮箱无效';
  400 + }
  401 + }
  402 + //过滤电话
  403 + if(!empty($data['phone']) && !empty($config['filter_mobiles'])){
  404 + foreach ($config['filter_mobiles'] as $filter_mobile){
  405 + if(Str::contains(strtolower($data['phone']), strtolower($filter_mobile))){
  406 + return '过滤电话:' . $filter_mobile;
  407 + }
  408 + }
  409 + }
  410 + //过滤姓名
  411 + if(!empty($data['full_name'] && !empty($config['filter_names']))){
  412 + foreach ($config['filter_names'] as $filter_name){
  413 + if(Str::contains(strtolower($data['full_name']), strtolower($filter_name))){
  414 + return '过滤姓名:' . $filter_name;
  415 + }
  416 + }
  417 + }
  418 + return true;
  419 + }
  420 +
  421 + /**
  422 + * 创建转发详情
  423 + * TODO 通过任务生成转发对象, 更具转发对象获取对应数据, 写入着陆记录
  424 + * @param $task
  425 + * @param $form
  426 + * @return bool
  427 + */
  428 + public function relayDetail($task, $form)
  429 + {
  430 + $this->output('获取转发对象');
  431 + if(empty($task['target'] )){
  432 + $this->output('没有独立站转发对象');
  433 + return 0;
  434 + }
  435 + //是否有必选的渠道 渠道有一个及以上必选 就在组内随机一个
  436 + $require_agent_group = [];
  437 + foreach ($task['target'] as $item) {
  438 + if (!empty($item['is_require'])) {
  439 + $require_agent_group[] = $item['agent_group'];
  440 + }
  441 + }
  442 + $require_agent_group = array_unique($require_agent_group);
  443 + $require_agent_group_data = collect($task['target'])->whereIn('agent_group', $require_agent_group)->groupBy('agent_group');
  444 + $require_data = $require_agent_group_data->keys()->random(count($require_agent_group))->map(function ($group) use ($require_agent_group_data) {
  445 + return $require_agent_group_data[$group]->random();
  446 + })->all();
  447 + //代理商组 一个组只发一个
  448 + $agent_group = collect($task['target'])->whereNotIn('agent_group', $require_agent_group)->groupBy('agent_group');
  449 + // 获取转发对象 重置num数量, array_rand数量不足会报错
  450 + $task['num'] = $task['num'] - count($require_agent_group);
  451 + $num = $task['num'] > count($agent_group) ? count($agent_group) : $task['num'];
  452 + $random_data = $agent_group->keys()->random($num)->map(function ($group) use ($agent_group) {
  453 + return $agent_group[$group]->random();
  454 + })->all();
  455 + $random_data = array_merge($require_data, $random_data);
  456 +
  457 + if (empty($random_data)) {
  458 + $this->logChannel()->info('当前任务未发现转发对象!', ['广告任务ID:' . $task['id'], '询盘ID:' . $form->id]);
  459 + $form->remark = $form->remark . '当前任务未发现转发对象,广告ID: ' . $form->ad_id . '!';
  460 + return false;
  461 + }
  462 + $this->logChannel()->info('随机域名', array_column($random_data, 'url'));
  463 + foreach ($random_data as $item) {
  464 + $times = 1;
  465 + $inquiry_time = 1;
  466 +
  467 + //需要多个ip访问的国家 随机2-5次访问,只有一次询盘
  468 + if (in_array($form->country_name, $this->multiple_ip_visit_country)) {
  469 + $times = mt_rand(1, 2); //随机次数
  470 + $inquiry_time = mt_rand(1, $times); //第几次询盘
  471 + $this->output('多次访问模拟:' . $times .',第' . $inquiry_time . '次询盘');
  472 + }
  473 + for ($i = 1; $i <= $times; $i++) {
  474 + $is_inquiry = ($inquiry_time == $i);
  475 + if($is_inquiry){
  476 + $this->output('第' . $i . '次询盘');
  477 + }
  478 + //手机号过滤
  479 + $phone = $form->phone;
  480 + $filter_phone = $this->get_rand($this->filter_phone);
  481 + if($filter_phone == 0){
  482 + $phone = trim(str_replace("+", '', $phone));
  483 + }elseif($filter_phone == 1){
  484 + $phone = '';
  485 + }
  486 +
  487 + // 推送站点
  488 + $domain = $item['url'];
  489 + $is_v6 = $item['is_v6'];
  490 + $re_website = 'https://' . $domain . '/';
  491 +
  492 + //urls
  493 + list($urls, $lang, $inquiry_product_url) = $this->getUrls($is_v6, $domain, $re_website, $form, $task);
  494 + if(!$urls){
  495 + continue;
  496 + }
  497 +
  498 + //ip
  499 + $ip_data = $this->getIpData($form->country_name);
  500 + $ip = $ip_data->ip;
  501 + $country_name = $ip_data->ip_area;
  502 +
  503 + //message
  504 + $message = '';
  505 + $message_id = 0;
  506 + $msg_lang = '';
  507 + if($is_inquiry) {
  508 + list($message, $message_id, $msg_lang) = $this->getMessage($task, $form->message, $domain, $inquiry_product_url);
  509 + }
  510 + $lang = $lang ?: $msg_lang;
  511 +
  512 + $this->output('获取转发设备信息');
  513 + // 客户端 头信息 来源
  514 + $device_port = $form->email ? '1' : '2'; //1 pc 2移动端
  515 + $user_agent = $form->email ? Arr::random($this->pc_ua) : Arr::random($this->mobile_ua);
  516 + $referrer = $this->getReferer($country_name, $lang);
  517 + $this->output('写入数据');
  518 +
  519 + $pre = 0;
  520 + $start_time = strtotime($form->inquiry_date);
  521 + if($start_time < time()){
  522 + $start_time = time();
  523 + $seconds = rand(0, 72 * 3600); // 3天内发完
  524 + }else{
  525 + $seconds = rand(100, 2 * 3600);
  526 + }
  527 + $email = '';
  528 + if($is_inquiry) {
  529 + $exists = ReInquiryDetail::where('re_website', $domain)->where('email', $form->email)->first();
  530 + if ($exists) {
  531 + $this->output('转发站点邮件已存在');
  532 + // continue;
  533 + }
  534 + $email = $form->email;
  535 + }
  536 + dump(date('Y-m-d H:i:s', $start_time + $seconds));
  537 + // 写入推送详情
  538 + $re_detail = ReInquiryDetail::createInquiry($task['id'], $form->id, $domain, $country_name, $ip, $form->full_name, $email, $phone, $message, $message_id, $device_port,
  539 + $user_agent, $referrer, $urls, $is_v6, date('Y-m-d H:i:s', $start_time + $seconds));
  540 + foreach ($urls as $k=>$v){
  541 + $pre++;
  542 + $seconds += rand(5,60);
  543 + ReInquiryDetailLog::createInquiryLog($re_detail->id, ReInquiryDetailLog::TYPE_VISIT, $pre, $v, date('Y-m-d H:i:s', $start_time + $seconds));
  544 + // 最后一次访问询盘 加上询盘
  545 + if($is_inquiry && ($k+1) == count($urls)){
  546 + $this->output('第' . ($k+1) . '个链接询盘');
  547 + $seconds += rand(30,120);
  548 + $pre++;
  549 + ReInquiryDetailLog::createInquiryLog($re_detail->id, ReInquiryDetailLog::TYPE_INQUIRY, $pre, $v, date('Y-m-d H:i:s', $start_time + $seconds));
  550 + }
  551 + }
  552 + }
  553 + }
  554 + return true;
  555 + }
  556 +
  557 + public function relayShopDetail($task, $form)
  558 + {
  559 + $this->output('获取商城转发对象');
  560 +
  561 + if(empty($task['shop_site'])){
  562 + $this->output('没有商城转发对象');
  563 + return 0;
  564 + }
  565 +
  566 + $num = $task['num'] > count($task['shop_site']) ? count($task['shop_site']) : $task['num'];
  567 + $shop_site = collect($task['shop_site'])->random($num)->all();
  568 +
  569 + foreach ($shop_site as $item) {
  570 + //手机号过滤
  571 + $phone = $form->phone;
  572 + // 推送站点
  573 + $domain = $item;
  574 + $re_website = 'https://' . $domain . '/';
  575 + $paths = ['', 'contact-us'];
  576 + $url = $re_website . $paths[array_rand($paths)];
  577 + //ip
  578 + $ip_data = $this->getIpData($form->country_name);
  579 + $ip = $ip_data->ip;
  580 + $country_name = $ip_data->ip_area;
  581 +
  582 + //message
  583 + list($message, $message_id, $lang) = $this->getMessage($task, $form->message, $domain);
  584 +
  585 + $device_port = $form->email ? '1' : '2'; //1 pc 2移动端
  586 + $user_agent = $form->email ? Arr::random($this->pc_ua) : Arr::random($this->mobile_ua);
  587 + $referrer = $this->getReferer($country_name, $lang);
  588 +
  589 + $start_time = strtotime($form->inquiry_date);
  590 + if($start_time < time()){
  591 + $start_time = time();
  592 + $seconds = rand(0, 48 * 3600); // 开始时间 从5-2小时后开始
  593 + }else{
  594 + $seconds = rand(100, 2 * 3600);
  595 + }
  596 + dump(date('Y-m-d H:i:s', $start_time + $seconds));
  597 + $exists = ReInquiryDetail::where('re_website', $domain)->where('email', $form->email)->first();
  598 + if($exists){
  599 + $this->output('转发站点邮件已存在');
  600 + continue;
  601 + }
  602 + $re_detail = ReInquiryDetail::createInquiry($task['id'], $form->id, $domain, $country_name, $ip, $form->full_name, $form->email, $phone, $message, $message_id,
  603 + $device_port, $user_agent, $referrer, [$url], 0, date('Y-m-d H:i:s', $start_time + $seconds), ReInquiryDetail::STATUS_INIT, 2);
  604 +
  605 + ReInquiryDetailLog::createInquiryLog($re_detail->id, ReInquiryDetailLog::TYPE_INQUIRY, 1, $url, date('Y-m-d H:i:s', $start_time + $seconds));
  606 + }
  607 + return true;
  608 + }
  609 +
  610 +
  611 + public function relayFobDetail($task, $form)
  612 + {
  613 + $this->output('获取FOB转发对象');
  614 +
  615 + if(empty($task['fob_pro'])){
  616 + $this->output('没有FOB转发对象');
  617 + return 0;
  618 + }
  619 +
  620 + foreach ($task['fob_pro'] as $item) {
  621 + //手机号过滤
  622 + $phone = $form->phone;
  623 + // 推送站点
  624 + $postid = $item;
  625 + //message
  626 + list($message, $message_id, $lang) = $this->getMessage($task, $form->message, $postid);
  627 +
  628 + $device_port = $form->email ? '1' : '2'; //1 pc 2移动端
  629 + $user_agent = $form->email ? Arr::random($this->pc_ua) : Arr::random($this->mobile_ua);
  630 +
  631 + $start_time = strtotime($form->inquiry_date);
  632 + if($start_time < time()){
  633 + $start_time = time();
  634 + $seconds = rand(0, 10 * 3600); // 开始时间 从5-2小时后开始
  635 + }else{
  636 + $seconds = rand(100, 2 * 3600);
  637 + }
  638 + dump(date('Y-m-d H:i:s', $start_time + $seconds));
  639 + $exists = ReInquiryDetail::where('re_website', $postid)->where('email', $form->email)->first();
  640 + if($exists){
  641 + $this->output('转发站点邮件已存在');
  642 + continue;
  643 + }
  644 + $re_detail = ReInquiryDetail::createInquiry($task['id'], $form->id, $postid, $country_name??'', $ip??'', $form->full_name, $form->email, $phone,
  645 + $message, $message_id, $device_port, $user_agent, $referrer??'', [$postid], 0, date('Y-m-d H:i:s', $start_time + $seconds), ReInquiryDetail::STATUS_INIT, 3);
  646 +
  647 + ReInquiryDetailLog::createInquiryLog($re_detail->id, ReInquiryDetailLog::TYPE_INQUIRY, 1, $postid, date('Y-m-d H:i:s', $start_time + $seconds));
  648 + }
  649 + return true;
  650 + }
  651 +
  652 + public function getIpData($country_name){
  653 + $this->output('获取转发ip');
  654 + $country = $country_name;
  655 + // 有国家 通过国家查询, 如果没有获取到就随机获取
  656 + $where = [];
  657 + $country && $where['ip_area'] = $country;
  658 + $ip_data = DB::table('gl_xunpan_ipdata')->where($where)->inRandomOrder()->first();
  659 + if (empty($ip_data)) {
  660 + $ip_data = DB::table('gl_xunpan_ipdata')->inRandomOrder()->first();
  661 + }
  662 + return $ip_data;
  663 + }
  664 +
  665 + public function getMessage($task, $message, $domain, $inquiry_product_url = ''){
  666 + $this->output('转发内容');
  667 + $form_message = $message;
  668 + $message_id = 0;
  669 +
  670 + // TODO 当原始询盘内容长度大于15个字符, 60%几率直接发送原始内容。
  671 + if (strlen($message) >= 15) {
  672 + $not_use_probability = AiCommand::where('key', 'fb_inquiry_text')->value('not_use_probability');
  673 + $randomNumber = rand(0, 100);
  674 + if($randomNumber < $not_use_probability){
  675 + //原内容非英语,转为对应语种
  676 + if (is_numeric($form_message)) { //数字会被识别为中文
  677 + $lang = 'en';
  678 + } else {
  679 + $translateSl = Translate::translateSl($form_message);
  680 + $lang = $translateSl['texts']['sl'] ?? 'en';
  681 + }
  682 + return [$message, $message_id, $lang??''];
  683 + }
  684 + }
  685 +
  686 + //开启文案替换
  687 + if ($task['is_replace_text'] == 2) {
  688 + //AI生成
  689 + $error = 0;
  690 + while ($error<3){
  691 + $message = $this->ai_send($task['ai_param'], $message, $inquiry_product_url);
  692 + if(!$message){
  693 + $this->output('AI文案生成失败');
  694 + $error++;
  695 + if($error==2){
  696 + $task['is_replace_text'] = 1;
  697 + $this->output('AI文案生成失败,使用文案库');
  698 + }
  699 + }else{
  700 + break;
  701 + }
  702 + }
  703 + }
  704 + if ($task['is_replace_text'] == 1 || strlen($message) <= 4) {
  705 + //配置文案库替换或者字符少于4个,直接替换文案
  706 + $use_ids = ReInquiryDetail::where(['re_website' => $domain])->where('status', '<>', ReInquiryDetail::STATUS_FAIL)->pluck('text_id')->toArray();
  707 + $text = ReInquiryText::whereNotIn('id', $use_ids)->where('status', ReInquiryText::STATUS_USABLE)->inRandomOrder()->first();
  708 + $message = $text->content;
  709 + $message_id = $text->id;
  710 + // 获取后,使用次数+1
  711 + $text->use_time += 1;
  712 + $text->save();
  713 +
  714 + //原内容非英语,转为对应语种
  715 + if (is_numeric($form_message)) { //数字会被识别为中文
  716 + $lang = 'en';
  717 + } else {
  718 + $translateSl = Translate::translateSl($form_message);
  719 + $lang = $translateSl['texts']['sl'] ?? 'en';
  720 + }
  721 +
  722 + if ($lang != 'en' && !Str::contains($lang, 'zh')) {
  723 + $message = Translate::tran($message, $lang);
  724 + }
  725 + }
  726 + return [$message, $message_id, $lang??''];
  727 + }
  728 +
  729 + public function getUrls($is_v6, $domain, $re_website, $form, $task){
  730 + $this->output('转发对象:' . $domain);
  731 + $this->output('获取转发链接');
  732 + // v6:有邮箱推送主站,没有邮箱推送AMP站;v5:仅推送有邮箱到主站
  733 + $lang = '';
  734 + if ($is_v6) {
  735 + // 获取语种, 6.0是可以确定语种的
  736 + $project = Project::getProjectByDomain($domain);
  737 + if (empty($project)) {
  738 + $this->logChannel()->info('广告任务ID:' . $task['id'] . ', 转发对象:' . $re_website . '非v6链接,转发失败;', ['广告任务ID:' . $task['id'], '询盘ID:' . $form->id]);
  739 + return [[], $lang];
  740 + }
  741 + $lang = WebLanguage::getLangById($project->main_lang_id ?? 1)['short'];
  742 +
  743 + // 获取访问明细和着陆页
  744 + $product_url = $this->getLinksFromSitemap($re_website . 'product_sitemap.xml');
  745 + $product_cate_url = $this->getLinksFromSitemap($re_website . 'product_category_sitemap.xml');
  746 + $keywords_url = $this->getLinksFromSitemap($re_website . 'product_keywords_sitemap.xml');
  747 + $page_url = $this->getLinksFromSitemap($re_website . 'page_sitemap.xml');
  748 + } else {
  749 + if($form->email){
  750 + //通过sitemap拿访问页面
  751 + $product_url = $this->getLinksFromSitemap($re_website . 'sitemap_post.xml');
  752 + $product_cate_url = $this->getLinksFromSitemap($re_website . 'sitemap_category.xml');
  753 + $keywords_url = $this->getLinksFromSitemap($re_website . 'sitemap_post_tag.xml');
  754 + $page_url = $this->getLinksFromSitemap($re_website . 'sitemap_page.xml');
  755 + }else{
  756 + //m站先就往contact-us着陆
  757 + $product_url = $product_cate_url = $keywords_url = [];
  758 + $page_url = [$re_website . 'contact-us/'];
  759 + }
  760 + }
  761 +
  762 + // 所有可用url
  763 + $urls = $inquiry_urls = [];
  764 + //入口url 首页30%,单页10%,聚合页60%
  765 + $type = getRandByRatio([30,10,60]);
  766 + $inlet = $re_website;
  767 + $type == 1 && $inlet = $page_url ? Arr::random($page_url) : $re_website;
  768 + $type == 2 && $inlet = $keywords_url ? Arr::random($keywords_url) : $re_website;
  769 + $urls[] = $inquiry_urls[] = $inlet;
  770 + $all_urls = array_merge($urls, $product_url, $product_cate_url, $keywords_url, $page_url);
  771 + $inquiry_urls = array_merge($urls, $product_cate_url, $keywords_url, $page_url);
  772 +
  773 + // 随机访问1-6个页面
  774 + $deep = rand(1,6);
  775 + if($deep > 2) {
  776 + $visit_urls = Arr::random($all_urls, rand(1, count($all_urls) > 4 ? 4 : count($all_urls)));
  777 + $urls = array_merge($urls, $visit_urls);
  778 + }
  779 + if($deep > 1) {
  780 + // 推送着落页只能是 首页、产品分类、单页面、聚合页
  781 + if (!in_array(end($urls), $inquiry_urls)) {
  782 + $urls[] = Arr::random($inquiry_urls);
  783 + }
  784 + }
  785 + //着陆页是否是产品页面或产品列表页
  786 + $inquiry_product_url = '';
  787 + if(in_array(Arr::last($urls), $product_url) || in_array(Arr::last($urls), $product_cate_url)){
  788 + $inquiry_product_url = Arr::last($urls);
  789 + }
  790 +
  791 + return [$urls, $lang, $inquiry_product_url];
  792 + }
  793 + /**
  794 + * 获取待处理询盘表单
  795 + * @param int $num
  796 + * @return mixed
  797 + */
  798 + public function getInquiry($num = 10)
  799 + {
  800 + $result = ReInquiryForm::where(['status' => ReInquiryForm::STATUS_INIT])->orderBy('id', 'asc')->limit($num)->get();
  801 + return $result;
  802 + }
  803 +
  804 + /**
  805 + * 获取广告任务配置数据
  806 + * @param $ad_id
  807 + * @return array
  808 + */
  809 + public function getAdTask($ad_id)
  810 + {
  811 + $cache_key = 'inquiry_ads_tasks';
  812 + $ads = Cache::get($cache_key, function () use ($cache_key) {
  813 + $ads = ReInquiryTask::where(['status' => ReInquiryTask::STATUS_OPEN])->get(['id', 'ad_id', 'num', 'target', 'is_replace_text', 'ai_param', 'shop_site', 'fob_pro']);
  814 + $array = [];
  815 + foreach ($ads as $key=>$val) {
  816 + $ad_ids = explode(',', $val['ad_id']);
  817 + foreach ($ad_ids as $ad_id){
  818 + $array[$ad_id][] = $val;
  819 + }
  820 + }
  821 + if ($array)
  822 + Cache::put($cache_key, $array, 60);
  823 + return $array;
  824 + });
  825 + return empty($ads[$ad_id]) ? [] : $ads[$ad_id];
  826 + }
  827 +
  828 + /**
  829 + * 获取头信息
  830 + * @param $ip_area
  831 + * @param $lang
  832 + * @return int|string
  833 + */
  834 + public function getReferer($ip_area, $lang)
  835 + {
  836 + if($lang == 'ru'){
  837 + return $this->get_rand($this->eylyzb);
  838 + }
  839 + if($ip_area == '美国'){
  840 + $referer = $this->get_rand($this->lyzb);
  841 + }else{
  842 + $referer = 'https://www.google.com/';
  843 + $suffix = array_search($ip_area, $this->suffix);
  844 + if($suffix){
  845 + $res_qtzb = $this->get_rand($this->otherzb);
  846 + if($res_qtzb == 1){
  847 + $referer = 'https://www.google.'.$suffix.'/';
  848 + }
  849 + }
  850 + }
  851 + return $referer;
  852 + }
  853 +
  854 + /**
  855 + * 概率算法
  856 + * @param $proArr
  857 + * @return int|string
  858 + */
  859 + protected function get_rand($proArr)
  860 + {
  861 + $result = '';
  862 + $proSum = array_sum($proArr);
  863 + foreach ($proArr as $key => $proCur) {
  864 + $randNum = mt_rand(1, $proSum);
  865 + if ($randNum <= $proCur) {
  866 + $result = $key;
  867 + break;
  868 + } else {
  869 + $proSum -= $proCur;
  870 + }
  871 + }
  872 + unset ($proArr);
  873 + return $result;
  874 + }
  875 +
  876 + /**
  877 + * 获取sitemap内容
  878 + * @param $sitemapUrl
  879 + * @return array|mixed
  880 + */
  881 + function getLinksFromSitemap($sitemapUrl) {
  882 + try {
  883 + //忽略cert证书 先下载到临时文件
  884 + $result = Http::withoutVerifying()->get($sitemapUrl)->body();
  885 + $tempFilePath = tempnam(sys_get_temp_dir(), 'remote_file_');
  886 + file_put_contents($tempFilePath, $result);
  887 + $xml = simplexml_load_file($tempFilePath);
  888 + $links = [];
  889 + foreach ($xml->url as $url) {
  890 + $loc = (string) $url->loc;
  891 + if(!Str::contains($loc, ['404', 'thanks', 'test'])){
  892 + $links[] = $loc;
  893 + }
  894 + }
  895 + //随机取20个
  896 + $total = count($links);
  897 + return Arr::random($links, $total > 20 ? 20 : $total);
  898 + }catch (\Exception $e){
  899 + echo date('Y-m-d H:i:s') . 'sitemap获取失败:' . $e->getMessage() . PHP_EOL;
  900 + return $links??[];
  901 + }
  902 + }
  903 +
  904 + public function ai_send($ai_param, $incontent, $inquiry_product_url)
  905 + {
  906 + $ai_command = AiCommand::where('key', 'fb_inquiry_text')->value('ai');
  907 + if (!$ai_command) {
  908 + return '';
  909 + }
  910 + $translateSl = Translate::translateSl($incontent);
  911 + $lang = $translateSl['texts']['sl'] ?? 'en';
  912 + if ($lang == 'en' || $lang == 'ja' || $lang == 'ko' || Str::contains($lang, 'zh')) {
  913 + $language = '英文';
  914 + $lang = 'en';
  915 + }else{
  916 + $language = Translate::getTls($lang);
  917 + }
  918 +
  919 +
  920 +
  921 + //着陆页是否是产品页面或产品列表页
  922 + if($inquiry_product_url){
  923 + $title = Common::getUrlTitle($inquiry_product_url);
  924 + if($title){
  925 + $ai_command = str_replace('{mkeywords}', $title, $ai_command);
  926 + }
  927 + }
  928 + $ai_command = str_replace('{mkeywords}', Arr::random(explode("\r\n", $ai_param['mkeywords'])), $ai_command);
  929 + $ai_command = str_replace('{incontent}', Arr::random(explode("\r\n", $incontent)), $ai_command);
  930 + $ai_command = str_replace('{characters}', Arr::random(explode("\r\n", $ai_param['characters'])), $ai_command);
  931 +// $ai_command = str_replace('{language}', Arr::random(explode("\r\n", $language)), $ai_command);
  932 + $ai_command = str_replace('{language}', '英语', $ai_command); //输出英文 后面再翻译
  933 + $ai_command = str_replace('{inkeywords}', Arr::random(explode("\r\n", $ai_param['inkeywords'])), $ai_command);
  934 + $ai_command = str_replace('{suoxie}', Arr::random(explode("\r\n", $ai_param['suoxie'])), $ai_command);
  935 + //中括号里的根据概率使用
  936 + preg_match_all("/\[([^\]]+)\]/", $ai_command, $matches);
  937 + foreach ($matches[1] as $k => $match){
  938 + //按比例使用
  939 + $matche_arr = explode('|', $match);
  940 + $percentage = intval(trim($matche_arr[0], "%"));
  941 + if(rand(1,100) <= $percentage){
  942 + //使用
  943 + $ai_command = str_replace($matches[0][$k], $matche_arr[1], $ai_command);
  944 + }else{
  945 + //删除中括号
  946 + $ai_command = str_replace($matches[0][$k], '', $ai_command);
  947 + }
  948 + }
  949 +
  950 + $text = Gpt::instance()->openai_chat_qqs($ai_command);
  951 + if ($lang != 'en' && !Str::contains($lang, 'zh')) {
  952 + $text = Translate::tran($text, $lang);
  953 + }
  954 + $this->logChannel()->info("AI询盘文案", [$ai_command, $text]);
  955 + return Common::deal_str($text);
  956 + }
  957 +
  958 + /**
  959 + * @return \Psr\Log\LoggerInterface
  960 + */
  961 + public function logChannel()
  962 + {
  963 + return Log::channel('inquiry_relay');
  964 + }
  965 +
  966 + public function output($message)
  967 + {
  968 + echo date('Y-m-d H:i:s') . ' | ' . $message . PHP_EOL;
  969 + }
  970 +}
@@ -174,7 +174,7 @@ class VideoTask extends Command @@ -174,7 +174,7 @@ class VideoTask extends Command
174 'is_ytb'=>false, 174 'is_ytb'=>false,
175 'other_language_subtitle'=>false 175 'other_language_subtitle'=>false
176 ]; 176 ];
177 - $result = Http::post('http://216.250.255.116:7866/create_task', $data); 177 + $result = Http::post('http://216.250.255.116:7866/create_task', $data);//todo::小天的接口
178 $res_json = json_decode($result,true); 178 $res_json = json_decode($result,true);
179 $val->task_id = $task_id; 179 $val->task_id = $task_id;
180 $val->status = KeywordVideoTaskLog::STATUS_ERROR; 180 $val->status = KeywordVideoTaskLog::STATUS_ERROR;
@@ -52,9 +52,10 @@ class LyhImportTest extends Command @@ -52,9 +52,10 @@ class LyhImportTest extends Command
52 * @time :2023/11/20 15:13 52 * @time :2023/11/20 15:13
53 */ 53 */
54 public function handle(){ 54 public function handle(){
55 - ProjectServer::useProject(3654);  
56 - echo date('Y-m-d H:i:s') . 'start' . PHP_EOL;  
57 - $this->importProductCategory('https://ecdn6-nc.globalso.com/upload/p/3654/file/2025-06/products-1.csv',3654); 55 + ProjectServer::useProject(3531);
  56 + echo date('Y-m-d H:i:s') . 'start->3531' . PHP_EOL;
  57 +// $this->importProductCategory('https://ecdn6-nc.globalso.com/upload/p/3654/file/2025-06/products-1.csv',3654);
  58 + $this->import3531CustomModule(3531);
58 DB::disconnect('custom_mysql'); 59 DB::disconnect('custom_mysql');
59 echo date('Y-m-d H:i:s') . 'end' . PHP_EOL; 60 echo date('Y-m-d H:i:s') . 'end' . PHP_EOL;
60 } 61 }
@@ -580,4 +581,1094 @@ class LyhImportTest extends Command @@ -580,4 +581,1094 @@ class LyhImportTest extends Command
580 // } 581 // }
581 } 582 }
582 } 583 }
  584 +
  585 + /**
  586 + * @remark :导入扩展模块数据
  587 + * @name :import3153CustomModule
  588 + * @author :lyh
  589 + * @method :post
  590 + * @time :2025/9/10 14:44
  591 + */
  592 + public function import3531CustomModule($project_id = 3531){
  593 + $str = '[
  594 + {
  595 + "release_at": "2024-09-22 10:07:56",
  596 + "name": "300199翰宇药业投资者关系管理信息20240905.pdf",
  597 + "href": "/file_manage/3531/20250522/300199_hanyu-pharmaceutical-2024-09-05_300199-hanyu-pharmaceutical-investor-relations-management-information-20240905_8114.pdf",
  598 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  599 + },
  600 + {
  601 + "release_at": "2024-05-0 01:06:09",
  602 + "name": "300199翰宇药业投资者关系管理信息20240510.pdf",
  603 + "href": "/file_manage/3531/20250522/300199_hanyu-pharmaceutical-2024-05-10_300199-hanyu-pharmaceutical-investor-relations-management-information-20240510_3774.pdf",
  604 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  605 + },
  606 + {
  607 + "release_at": "2023-10-12 10:43:39",
  608 + "name": "300199翰宇药业2023-10-13_2023年10月11-12日投资者关系活动记录表.pdf",
  609 + "href": "/file_manage/3531/20250522/300199_hanyu-pharmaceutical-2023-10-13_2023-october-11-12-investor-relations-activity-record-sheet_7992.pdf",
  610 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  611 + },
  612 + {
  613 + "release_at": "2023-02-22 00:40:24",
  614 + "name": "300199翰宇药业2023年02月03日投资者关系活动记录表.pdf",
  615 + "href": "/file_manage/3531/20250522/300199_hanyu-pharmaceutical-2023-05-29_300199-hanyu-pharmaceutical-research-activity-information-20230529_5609.pdf",
  616 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  617 + },
  618 + {
  619 + "release_at": "2022-06-20 08:09:53",
  620 + "name": "300199翰宇药业调研活动信息20220601.pdf",
  621 + "href": "/file_manage/3531/20250522/300199_hanyu-pharmaceutical-2024-09-05_300199-hanyu-pharmaceutical-investor-relations-management-information-20240905_8114.pdf",
  622 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  623 + },
  624 + {
  625 + "release_at": "2022-05-0 02:58:18",
  626 + "name": "300199翰宇药业调研活动信息20220509.pdf",
  627 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2022-05-09_300199-hanyu-pharmaceutical-research-activity-information-20220509_8714.pdf",
  628 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  629 + },
  630 + {
  631 + "release_at": "2021-11-13 04:07:53",
  632 + "name": "300199翰宇药业调研活动信息20211102.pdf",
  633 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2021-11-02_300199-hanyu-pharmaceutical-research-activity-information-20211102_7796.pdf",
  634 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  635 + },
  636 + {
  637 + "release_at": "2021-09-2 10:36:12",
  638 + "name": "300199翰宇药业调研活动信息20210903.pdf",
  639 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2021-09-03_300199-hanyu-pharmaceutical-research-activity-information-20210903_4654.pdf",
  640 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  641 + },
  642 + {
  643 + "release_at": "2020-12-22 02:06:25",
  644 + "name": "300199翰宇药业调研活动信息20201203.doc",
  645 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2020-12-03_300199-hanyu-pharmaceutical-research-activity-information-20201203_3714.doc",
  646 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  647 + },
  648 + {
  649 + "release_at": "2020-10-16 01:11:15",
  650 + "name": "300199翰宇药业投资者关系管理制度20201016.doc",
  651 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceuticals-2020-10-16_300199-hanyu-pharmaceuticals-investor-relations-management-system-20201016_9487.doc",
  652 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  653 + },
  654 + {
  655 + "release_at": "2020-09-1 05:09:01",
  656 + "name": "300199翰宇药业投资者关系管理档案20200915_3643.doc",
  657 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceuticals-2020-09-15_300199-hanyu-pharmaceuticals-investor-relations-management-file-20200915_3643.doc",
  658 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  659 + },
  660 + {
  661 + "release_at": "2024-09-3 01:32:32",
  662 + "name": "300199翰宇药业投资者关系管理信息20240905_8114.pdf",
  663 + "href": "/file_manage/3531/20250522/300199_hanyu-pharmaceutical-2024-09-05_300199-hanyu-pharmaceutical-investor-relations-management-information-20240905_8114.pdf",
  664 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  665 + },
  666 + {
  667 + "release_at": "2020-08-9 01:15:23",
  668 + "name": "300199翰宇药业2020-08-25_投资者关系管理制度_7744.PDF",
  669 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2020-08-25_investor-relations-management-system_7744.PDF",
  670 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  671 + },
  672 + {
  673 + "release_at": "2024-08-20 11:07:20",
  674 + "name": "300199翰宇药业投资者关系管理档案20200807_9651.doc",
  675 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceuticals-2020-08-07_300199-hanyu-pharmaceuticals-investor-relations-management-file-20200807_9651.doc",
  676 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  677 + },
  678 + {
  679 + "release_at": "2020-07-21 04:43:26",
  680 + "name": "300199翰宇药业调研活动信息20200723_1044.doc",
  681 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2020-07-23_300199-hanyu-pharmaceutical-research-activity-information-20200723_1044.doc",
  682 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  683 + },
  684 + {
  685 + "release_at": "2018-10-4 04:57:34",
  686 + "name": "300199翰宇药业2018-10-12_2018年10月10日投资者关系活动记录表.pdf",
  687 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2018-10-12_october-10-2018-investor-relations-activity-record_6079.pdf",
  688 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  689 + },
  690 + {
  691 + "release_at": "2018-08-1 03:03:44",
  692 + "name": "300199翰宇药业2018-08-16_2018年8月15日投资者关系活动记录表.pdf",
  693 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2018-08-16_2018-august-15-investor-relations-activity-record_1590.pdf",
  694 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  695 + },
  696 + {
  697 + "release_at": "2018-08-3 02:46:31",
  698 + "name": "300199翰宇药业2018-10-12_2018年10月10日投资者关系活动记录表",
  699 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2018-10-12_october-10-2018-investor-relations-activity-record_6079.pdf",
  700 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  701 + },
  702 + {
  703 + "release_at": "2018-07-7 06:43:51",
  704 + "name": "300199翰宇药业2018-07-04_2018年6月28日投资者关系活动记录表.pdf",
  705 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2018-07-04_investor-relations-activity-record-form-on-june-28-2018_7416.pdf",
  706 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  707 + },
  708 + {
  709 + "release_at": "2018-06-4 05:51:21",
  710 + "name": "300199翰宇药业2018-06-30_2018年6月27日投资者关系活动记录表.pdf",
  711 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2018-06-30_investor-relations-activity-record-form-on-june-27-2018_1798.pdf",
  712 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  713 + },
  714 + {
  715 + "release_at": "2018-01-22 05:20:39",
  716 + "name": "300199翰宇药业2018-01-15_2018年1月12日投资者关系活动记录表.pdf",
  717 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2018-01-15_investor-relations-activity-record-form-on-january-12-2018_7983.pdf",
  718 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  719 + },
  720 + {
  721 + "release_at": "2018-01-13 01:01:14",
  722 + "name": "300199翰宇药业投资者关系管理信息20240905_8114.pdf",
  723 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2018-01-11_2018-january-9-investor-relations-activity-record_3126.pdf",
  724 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  725 + },
  726 + {
  727 + "release_at": "2017-12-0 02:48:51",
  728 + "name": "300199翰宇药业2017-12-12_2017年12月8日投资者关系活动记录表.docx",
  729 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-12-12_investor-relations-activity-record-form-on-december-8-2017_4872.docx",
  730 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  731 + },
  732 + {
  733 + "release_at": "2017-12-24 01:34:45",
  734 + "name": "300199翰宇药业2017-12-06_2017年12月4日投资者关系活动记录表.pdf",
  735 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-12-06_investor-relations-activity-record-form-on-december-4-2017_9265.pdf",
  736 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  737 + },
  738 + {
  739 + "release_at": "2017-11-19 10:52:44",
  740 + "name": "300199翰宇药业2017-11-29_2017年11月23日投资者关系活动记录表.pdf",
  741 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-11-29_2017-november-23-investor-relations-activity-record_2045.pdf",
  742 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  743 + },
  744 + {
  745 + "release_at": "2017-11-11 07:31:15",
  746 + "name": "300199翰宇药业2017-11-10_2017年11月8日投资者关系活动记录表.pdf",
  747 + "href": "/file_manage/3531/20250522/300199_hanyu-pharmaceutical-2024-09-05_300199-hanyu-pharmaceutical-investor-relations-management-information-20240905_8114.pdf",
  748 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  749 + },
  750 + {
  751 + "release_at": "2017-11-2 07:22:37",
  752 + "name": "300199翰宇药业2017-11-09_2017年11月6日投资者关系活动记录表.pdf",
  753 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-11-09_november-6-2017-investor-relations-activity-record_4228.pdf",
  754 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  755 + },
  756 + {
  757 + "release_at": "2017-10-4 10:40:44",
  758 + "name": "300199翰宇药业2017-10-31_2017年10月27日投资者关系活动记录表.pdf",
  759 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-10-31_2017-october-27-investor-relations-activity-record_i__6306.pdf",
  760 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  761 + },
  762 + {
  763 + "release_at": "2017-10-15 02:14:30",
  764 + "name": "300199翰宇药业2017-10-31_2017年10月27日投资者关系活动记录表_二.pdf",
  765 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-10-31_2017-october-27-investor-relations-activity-record_ii__4570.pdf",
  766 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  767 + },
  768 + {
  769 + "release_at": "2017-10-11 11:36:58",
  770 + "name": "300199翰宇药业2017-10-25_2017年10月17日投资者关系活动记录表.pdf",
  771 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-10-25_2017-october-17-investor-relations-activity-record_6436.pdf",
  772 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  773 + },
  774 + {
  775 + "release_at": "2017-10-16 09:47:11",
  776 + "name": "300199翰宇药业2017-10-20_2017年10月18日投资者关系活动记录表_一.pdf",
  777 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-10-20_2017-october-18-investor-relations-activity-record-sheet_i__2433.pdf",
  778 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  779 + },
  780 + {
  781 + "release_at": "2017-10-5 09:56:44",
  782 + "name": "300199翰宇药业2017-10-20_2017年10月18日投资者关系活动记录表_二.pdf",
  783 + "href": "/file_manage/3531/20250522/300199_hanyu-pharmaceutical-2024-09-05_300199-hanyu-pharmaceutical-investor-relations-management-information-20240905_8114.pdf",
  784 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  785 + },
  786 + {
  787 + "release_at": "2017-10-5 04:20:08",
  788 + "name": "300199翰宇药业2017-10-11_2017年10月10日投资者关系活动记录表.pdf",
  789 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-10-11_2017-october-10-investor-relations-activity-record_7981.pdf",
  790 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  791 + },
  792 + {
  793 + "release_at": "2017-09-3 06:20:49",
  794 + "name": "300199翰宇药业2017-09-22_2017年9月21日投资者关系活动记录表.pdf",
  795 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-09-22_2017-september-21-investor-relations-activity-record_6296.pdf",
  796 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  797 + },
  798 + {
  799 + "release_at": "2017-09-8 10:35:03",
  800 + "name": "300199翰宇药业2017-09-21_2017年9月19日投资者关系活动记录表.pdf",
  801 + "href": "/file_manage/3531/20250522/300199_hanyu-pharmaceutical-2024-09-05_300199-hanyu-pharmaceutical-investor-relations-management-information-20240905_8114.pdf",
  802 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  803 + },
  804 + {
  805 + "release_at": "2017-08-10 07:09:11",
  806 + "name": "300199翰宇药业2017-08-31_2017年8月30日投资者关系活动记录表_一.pdf",
  807 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-08-31_2017-august-30-investor-relations-activity-record_i__6623.pdf",
  808 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  809 + },
  810 + {
  811 + "release_at": "2017-08-11 00:30:12",
  812 + "name": "300199翰宇药业2017-08-31_2017年8月30日投资者关系活动记录表_二.pdf",
  813 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-08-31_2017-august-30-investor-relations-activity-record_ii__1600.pdf",
  814 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  815 + },
  816 + {
  817 + "release_at": "2017-07-5 05:58:57",
  818 + "name": "300199_翰宇药业2017-07-04_2017年6月30日投资者关系活动记录表.pdf",
  819 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-07-04_investor-relations-activity-record-form-on-june-30-2017_3857.pdf",
  820 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  821 + },
  822 + {
  823 + "release_at": "2017-07-15 10:51:14",
  824 + "name": "300199翰宇药业2017-07-03_2017年6月29日投资者关系活动记录表.pdf",
  825 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-07-03_investor-relations-activity-record-form-on-june-29-2017_6558.pdf",
  826 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  827 + },
  828 + {
  829 + "release_at": "2017-06-0 06:32:30",
  830 + "name": "300199翰宇药业2017-06-30_2017年6月28日投资者关系活动记录表.pdf",
  831 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-06-30_investor-relations-activity-record-form-on-june-28-2017_5867.pdf",
  832 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  833 + },
  834 + {
  835 + "release_at": "2017-06-12 07:32:28",
  836 + "name": "300199翰宇药业2017-06-30_2017年6月28日投资者关系活动记录表.pdf",
  837 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-06-30_investor-relations-activity-record-form-on-june-28-2017_3778.pdf",
  838 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  839 + },
  840 + {
  841 + "release_at": "2017-06-15 01:20:49",
  842 + "name": "300199翰宇药业2017-06-07_2017年5月25日投资者关系活动记录表.pdf",
  843 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-06-07_may-25-2017-investor-relations-activity-record_5392.pdf",
  844 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  845 + },
  846 + {
  847 + "release_at": "2017-05-10 03:34:01",
  848 + "name": "300199翰宇药业2017-05-31_2017年5月26日投资者关系活动记录表.pdf",
  849 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-05-31_2017-may-26-investor-relations-activity-record_4488.pdf",
  850 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  851 + },
  852 + {
  853 + "release_at": "2017-05-15 11:36:44",
  854 + "name": "300199翰宇药业2017-05-26_2017年5月25日投资者关系活动记录表.pdf",
  855 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-05-26_may-25-2017-investor-relations-activity-record_1669.pdf",
  856 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  857 + },
  858 + {
  859 + "release_at": "2017-05-20 10:42:01",
  860 + "name": "300199翰宇药业2017-05-12_2017年5月11日投资者关系活动记录表.pdf",
  861 + "href": "/file_manage/3531/20250724/300199_hanyu-pharmaceutical-2017-05-12_may-11-2017-investor-relations-activity-record_8189.pdf",
  862 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  863 + },
  864 + {
  865 + "release_at": "2017-05-14 00:23:03",
  866 + "name": "300199_翰宇药业2017-05-08_2017年5月5日投资者关系活动记录表.pdf",
  867 + "href": "/file_manage/3531/20250522/300199_hanyu-pharmaceutical-2024-09-05_300199-hanyu-pharmaceutical-investor-relations-management-information-20240905_8114.pdf",
  868 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  869 + },
  870 + {
  871 + "release_at": "2017-05-3 09:15:35",
  872 + "name": "300199_翰宇药业2017-05-05_2017年5月4日投资者关系活动记录表.pdf",
  873 + "href": "/file_manage/3531/20250522/300199_hanyu-pharmaceutical-2024-09-05_300199-hanyu-pharmaceutical-investor-relations-management-information-20240905_8114.pdf",
  874 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  875 + },
  876 + {
  877 + "release_at": "2017-04-7 11:46:44",
  878 + "name": "300199_翰宇药业2017-04-19_2017年4月17日投资者关系活动记录表.pdf",
  879 + "href": "/file_manage/3531/20250522/300199_hanyu-pharmaceutical-2024-09-05_300199-hanyu-pharmaceutical-investor-relations-management-information-20240905_8114.pdf",
  880 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  881 + },
  882 + {
  883 + "release_at": "2017-04-18 03:23:11",
  884 + "name": "300199_翰宇药业2017-04-18_2017年4月14日投资者关系活动记录表.pdf",
  885 + "href": "/file_manage/3531/20250522/300199_hanyu-pharmaceutical-2024-09-05_300199-hanyu-pharmaceutical-investor-relations-management-information-20240905_8114.pdf",
  886 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  887 + },
  888 + {
  889 + "release_at": "2017-03-8 02:36:19",
  890 + "name": "300199_翰宇药业2017-03-28_2017年3月27日投资者关系活动记录表.pdf",
  891 + "href": "/file_manage/3531/20250522/300199_hanyu-pharmaceutical-2024-09-05_300199-hanyu-pharmaceutical-investor-relations-management-information-20240905_8114.pdf",
  892 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  893 + },
  894 + {
  895 + "release_at": "2017-03-24 09:45:46",
  896 + "name": "300199翰宇药业2017-03-10_2017年3月8日投资者关系活动记录表.pdf",
  897 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-10_investor-relations-activity-record-for-march-8-2017_4249.pdf",
  898 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  899 + },
  900 + {
  901 + "release_at": "2017-03-7 05:03:11",
  902 + "name": "300199_翰宇药业2017-03-10_2017年3月8日投资者关系活动记录表.pdf",
  903 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-10_investor-relations-activity-record-for-march-8-2017_4249.pdf",
  904 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  905 + },
  906 + {
  907 + "release_at": "2017-03-15 11:21:32",
  908 + "name": "300199_翰宇药业2017-03-08_2017年3月7日投资者关系活动记录表.pdf",
  909 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-08_investor-relations-activity-record-for-march-7-2017_9347.pdf",
  910 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  911 + },
  912 + {
  913 + "release_at": "2017-02-5 11:09:12",
  914 + "name": "300199翰宇药业2017-02-16_2017年2月14日投资者关系活动记录表_一3396.pdf",
  915 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-02-16_investor-relations-activity-record-sheet-for-february-14-2017_i__3396.pdf",
  916 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  917 + },
  918 + {
  919 + "release_at": "2017-02-15 09:09:14",
  920 + "name": "300199翰宇药业2017-02-14_2017年2月10日投资者关系活动记录表.pdf",
  921 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-02-14_investor-relations-activity-record-for-february-10-2017_9222.pdf",
  922 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  923 + },
  924 + {
  925 + "release_at": "2017-01-6 02:47:09",
  926 + "name": "300199翰宇药业2017-01-06_2017年1月5日投资者关系活动记录表.pdf",
  927 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-01-06_january-5-2017-investor-relations-activity-record-sheet_2137.pdf",
  928 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  929 + },
  930 + {
  931 + "release_at": "2016-12-4 08:44:03",
  932 + "name": "300199翰宇药业2016-12-26_2016年12月22日投资者关系活动记录表.pdf",
  933 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-26_investor-relations-activity-record-for-december-22-2016_4414.pdf",
  934 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  935 + },
  936 + {
  937 + "release_at": "2016-12-4 00:11:24",
  938 + "name": "300199翰宇药业2016-12-21_2016年12月20日投资者关系活动记录表.pdf",
  939 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-21_investor-relations-activity-record-for-december-20-2016_7480.pdf",
  940 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  941 + },
  942 + {
  943 + "release_at": "2016-12-17 11:37:55",
  944 + "name": "300199翰宇药业2016-12-15_2016年12月14日投资者关系活动记录表.pdf",
  945 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-15_investor-relations-activity-record-for-december-14-2016_8735.pdf",
  946 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  947 + },
  948 + {
  949 + "release_at": "2016-12-21 10:12:43",
  950 + "name": "300199翰宇药业2016-12-13_2016年12月12日投资者关系活动记录表.pdf",
  951 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2016-12-13_investor-relations-activity-record-for-december-12-2016_3763.pdf",
  952 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  953 + },
  954 + {
  955 + "release_at": "2017-03-19 00:43:40",
  956 + "name": "300199翰宇药业2017-03-10_2017年3月8日投资者关系活动记录表.pdf",
  957 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-10_investor-relations-activity-record-for-march-8-2017_4249.pdf",
  958 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  959 + },
  960 + {
  961 + "release_at": "2017-03-4 10:23:20",
  962 + "name": "300199_翰宇药业2017-03-10_2017年3月8日投资者关系活动记录表.pdf",
  963 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-10_investor-relations-activity-record-for-march-8-2017_4249.pdf",
  964 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  965 + },
  966 + {
  967 + "release_at": "2017-03-7 04:51:39",
  968 + "name": "300199_翰宇药业2017-03-08_2017年3月7日投资者关系活动记录表.pdf",
  969 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-08_investor-relations-activity-record-for-march-7-2017_9347.pdf",
  970 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  971 + },
  972 + {
  973 + "release_at": "2017-02-14 01:18:23",
  974 + "name": "300199翰宇药业2017-02-16_2017年2月14日投资者关系活动记录表_一3396.pdf",
  975 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-02-16_investor-relations-activity-record-sheet-for-february-14-2017_i__3396.pdf",
  976 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  977 + },
  978 + {
  979 + "release_at": "2017-02-6 10:32:52",
  980 + "name": "300199翰宇药业2017-02-14_2017年2月10日投资者关系活动记录表.pdf",
  981 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-02-14_investor-relations-activity-record-for-february-10-2017_9222.pdf",
  982 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  983 + },
  984 + {
  985 + "release_at": "2017-01-8 02:43:38",
  986 + "name": "300199翰宇药业2017-01-06_2017年1月5日投资者关系活动记录表.pdf",
  987 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-01-06_january-5-2017-investor-relations-activity-record-sheet_2137.pdf",
  988 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  989 + },
  990 + {
  991 + "release_at": "2016-12-21 07:37:24",
  992 + "name": "300199翰宇药业2016-12-26_2016年12月22日投资者关系活动记录表.pdf",
  993 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-26_investor-relations-activity-record-for-december-22-2016_4414.pdf",
  994 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  995 + },
  996 + {
  997 + "release_at": "2016-12-12 07:11:21",
  998 + "name": "300199翰宇药业2016-12-21_2016年12月20日投资者关系活动记录表.pdf",
  999 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-21_investor-relations-activity-record-for-december-20-2016_7480.pdf",
  1000 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1001 + },
  1002 + {
  1003 + "release_at": "2016-12-12 10:08:40",
  1004 + "name": "300199翰宇药业2016-12-15_2016年12月14日投资者关系活动记录表.pdf",
  1005 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-15_investor-relations-activity-record-for-december-14-2016_8735.pdf",
  1006 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1007 + },
  1008 + {
  1009 + "release_at": "2016-12-23 02:23:55",
  1010 + "name": "300199翰宇药业2016-12-13_2016年12月12日投资者关系活动记录表.pdf",
  1011 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2016-12-13_investor-relations-activity-record-for-december-12-2016_3763.pdf",
  1012 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1013 + },
  1014 + {
  1015 + "release_at": "2017-03-5 10:30:23",
  1016 + "name": "300199翰宇药业2017-03-10_2017年3月8日投资者关系活动记录表.pdf",
  1017 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-10_investor-relations-activity-record-for-march-8-2017_4249.pdf",
  1018 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1019 + },
  1020 + {
  1021 + "release_at": "2017-03-4 04:06:45",
  1022 + "name": "300199_翰宇药业2017-03-10_2017年3月8日投资者关系活动记录表.pdf",
  1023 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-10_investor-relations-activity-record-for-march-8-2017_4249.pdf",
  1024 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1025 + },
  1026 + {
  1027 + "release_at": "2017-03-23 02:38:57",
  1028 + "name": "300199_翰宇药业2017-03-08_2017年3月7日投资者关系活动记录表.pdf",
  1029 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-08_investor-relations-activity-record-for-march-7-2017_9347.pdf",
  1030 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1031 + },
  1032 + {
  1033 + "release_at": "2017-02-15 00:09:47",
  1034 + "name": "300199翰宇药业2017-02-16_2017年2月14日投资者关系活动记录表_一3396.pdf",
  1035 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-02-16_investor-relations-activity-record-sheet-for-february-14-2017_i__3396.pdf",
  1036 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1037 + },
  1038 + {
  1039 + "release_at": "2017-02-17 03:25:58",
  1040 + "name": "300199翰宇药业2017-02-14_2017年2月10日投资者关系活动记录表.pdf",
  1041 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-02-14_investor-relations-activity-record-for-february-10-2017_9222.pdf",
  1042 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1043 + },
  1044 + {
  1045 + "release_at": "2017-01-17 05:17:40",
  1046 + "name": "300199翰宇药业2017-01-06_2017年1月5日投资者关系活动记录表.pdf",
  1047 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-01-06_january-5-2017-investor-relations-activity-record-sheet_2137.pdf",
  1048 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1049 + },
  1050 + {
  1051 + "release_at": "2016-12-10 03:33:51",
  1052 + "name": "300199翰宇药业2016-12-26_2016年12月22日投资者关系活动记录表.pdf",
  1053 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-26_investor-relations-activity-record-for-december-22-2016_4414.pdf",
  1054 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1055 + },
  1056 + {
  1057 + "release_at": "2016-12-15 02:51:50",
  1058 + "name": "300199翰宇药业2016-12-21_2016年12月20日投资者关系活动记录表.pdf",
  1059 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-21_investor-relations-activity-record-for-december-20-2016_7480.pdf",
  1060 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1061 + },
  1062 + {
  1063 + "release_at": "2016-12-10 03:24:11",
  1064 + "name": "300199翰宇药业2016-12-15_2016年12月14日投资者关系活动记录表.pdf",
  1065 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-15_investor-relations-activity-record-for-december-14-2016_8735.pdf",
  1066 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1067 + },
  1068 + {
  1069 + "release_at": "2016-12-12 03:34:19",
  1070 + "name": "300199翰宇药业2016-12-13_2016年12月12日投资者关系活动记录表.pdf",
  1071 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2016-12-13_investor-relations-activity-record-for-december-12-2016_3763.pdf",
  1072 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1073 + },
  1074 + {
  1075 + "release_at": "2017-03-24 02:41:28",
  1076 + "name": "300199翰宇药业2017-03-10_2017年3月8日投资者关系活动记录表.pdf",
  1077 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-10_investor-relations-activity-record-for-march-8-2017_4249.pdf",
  1078 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1079 + },
  1080 + {
  1081 + "release_at": "2017-03-1 10:28:18",
  1082 + "name": "300199_翰宇药业2017-03-10_2017年3月8日投资者关系活动记录表.pdf",
  1083 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-10_investor-relations-activity-record-for-march-8-2017_4249.pdf",
  1084 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1085 + },
  1086 + {
  1087 + "release_at": "2017-03-24 00:16:45",
  1088 + "name": "300199_翰宇药业2017-03-08_2017年3月7日投资者关系活动记录表.pdf",
  1089 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-08_investor-relations-activity-record-for-march-7-2017_9347.pdf",
  1090 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1091 + },
  1092 + {
  1093 + "release_at": "2017-02-24 11:59:46",
  1094 + "name": "300199翰宇药业2017-02-16_2017年2月14日投资者关系活动记录表_一3396.pdf",
  1095 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-02-16_investor-relations-activity-record-sheet-for-february-14-2017_i__3396.pdf",
  1096 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1097 + },
  1098 + {
  1099 + "release_at": "2017-02-4 00:03:38",
  1100 + "name": "300199翰宇药业2017-02-14_2017年2月10日投资者关系活动记录表.pdf",
  1101 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-02-14_investor-relations-activity-record-for-february-10-2017_9222.pdf",
  1102 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1103 + },
  1104 + {
  1105 + "release_at": "2017-01-6 06:08:19",
  1106 + "name": "300199翰宇药业2017-01-06_2017年1月5日投资者关系活动记录表.pdf",
  1107 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-01-06_january-5-2017-investor-relations-activity-record-sheet_2137.pdf",
  1108 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1109 + },
  1110 + {
  1111 + "release_at": "2016-12-8 11:38:16",
  1112 + "name": "300199翰宇药业2016-12-26_2016年12月22日投资者关系活动记录表.pdf",
  1113 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-26_investor-relations-activity-record-for-december-22-2016_4414.pdf",
  1114 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1115 + },
  1116 + {
  1117 + "release_at": "2016-12-2 11:40:31",
  1118 + "name": "300199翰宇药业2016-12-21_2016年12月20日投资者关系活动记录表.pdf",
  1119 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-21_investor-relations-activity-record-for-december-20-2016_7480.pdf",
  1120 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1121 + },
  1122 + {
  1123 + "release_at": "2016-12-1 11:12:59",
  1124 + "name": "300199翰宇药业2016-12-15_2016年12月14日投资者关系活动记录表.pdf",
  1125 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-15_investor-relations-activity-record-for-december-14-2016_8735.pdf",
  1126 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1127 + },
  1128 + {
  1129 + "release_at": "2016-12-2 11:14:06",
  1130 + "name": "300199翰宇药业2016-12-13_2016年12月12日投资者关系活动记录表.pdf",
  1131 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2016-12-13_investor-relations-activity-record-for-december-12-2016_3763.pdf",
  1132 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1133 + },
  1134 + {
  1135 + "release_at": "2017-03-22 01:45:15",
  1136 + "name": "300199翰宇药业2017-03-10_2017年3月8日投资者关系活动记录表.pdf",
  1137 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-10_investor-relations-activity-record-for-march-8-2017_4249.pdf",
  1138 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1139 + },
  1140 + {
  1141 + "release_at": "2017-03-20 07:43:24",
  1142 + "name": "300199_翰宇药业2017-03-10_2017年3月8日投资者关系活动记录表.pdf",
  1143 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-10_investor-relations-activity-record-for-march-8-2017_4249.pdf",
  1144 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1145 + },
  1146 + {
  1147 + "release_at": "2017-03-9 10:30:42",
  1148 + "name": "300199_翰宇药业2017-03-08_2017年3月7日投资者关系活动记录表.pdf",
  1149 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2017-03-08_investor-relations-activity-record-for-march-7-2017_9347.pdf",
  1150 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1151 + },
  1152 + {
  1153 + "release_at": "2017-02-6 07:35:38",
  1154 + "name": "300199翰宇药业2017-02-16_2017年2月14日投资者关系活动记录表_一3396.pdf",
  1155 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-02-16_investor-relations-activity-record-sheet-for-february-14-2017_i__3396.pdf",
  1156 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1157 + },
  1158 + {
  1159 + "release_at": "2017-02-18 02:44:23",
  1160 + "name": "300199翰宇药业2017-02-14_2017年2月10日投资者关系活动记录表.pdf",
  1161 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-02-14_investor-relations-activity-record-for-february-10-2017_9222.pdf",
  1162 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1163 + },
  1164 + {
  1165 + "release_at": "2017-01-8 00:45:30",
  1166 + "name": "300199翰宇药业2017-01-06_2017年1月5日投资者关系活动记录表.pdf",
  1167 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2017-01-06_january-5-2017-investor-relations-activity-record-sheet_2137.pdf",
  1168 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1169 + },
  1170 + {
  1171 + "release_at": "2016-12-13 11:48:43",
  1172 + "name": "300199翰宇药业2016-12-26_2016年12月22日投资者关系活动记录表.pdf",
  1173 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-26_investor-relations-activity-record-for-december-22-2016_4414.pdf",
  1174 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1175 + },
  1176 + {
  1177 + "release_at": "2016-12-24 04:09:04",
  1178 + "name": "300199翰宇药业2016-12-21_2016年12月20日投资者关系活动记录表.pdf",
  1179 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-21_investor-relations-activity-record-for-december-20-2016_7480.pdf",
  1180 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1181 + },
  1182 + {
  1183 + "release_at": "2016-12-0 10:30:45",
  1184 + "name": "300199翰宇药业2016-12-15_2016年12月14日投资者关系活动记录表.pdf",
  1185 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-12-15_investor-relations-activity-record-for-december-14-2016_8735.pdf",
  1186 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1187 + },
  1188 + {
  1189 + "release_at": "2016-12-2 03:35:29",
  1190 + "name": "300199翰宇药业2016-12-13_2016年12月12日投资者关系活动记录表.pdf",
  1191 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2016-12-13_investor-relations-activity-record-for-december-12-2016_3763.pdf",
  1192 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1193 + },
  1194 + {
  1195 + "release_at": "2016-11-20 10:07:03",
  1196 + "name": "300199翰宇药业2016-11-18_2016年11月17日投资者关系活动记录表.pdf",
  1197 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2016-11-18_november-17-2016-investor-relations-activity-record_3028.pdf",
  1198 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1199 + },
  1200 + {
  1201 + "release_at": "2016-11-10 10:39:14",
  1202 + "name": "300199翰宇药业2016-11-01_2016年10月26日投资者关系活动记录表.pdf",
  1203 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-11-01_october-26-2016-investor-relations-activity-record_3346.pdf",
  1204 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1205 + },
  1206 + {
  1207 + "release_at": "2016-10-15 05:19:22",
  1208 + "name": "300199翰宇药业2016-10-27_2016年10月25日投资者关系活动记录表.pdf",
  1209 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-10-27_october-25-2016-investor-relations-activity-record-sheet_4404.pdf",
  1210 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1211 + },
  1212 + {
  1213 + "release_at": "2016-10-19 07:08:11",
  1214 + "name": "300199翰宇药业2016-10-20_2016年10月18日投资者关系活动记录表.pdf",
  1215 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-10-20_october-18-2016-investor-relations-activity-record-sheet_7025.pdf",
  1216 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1217 + },
  1218 + {
  1219 + "release_at": "2016-10-3 00:15:17",
  1220 + "name": "300199翰宇药业2016-10-18_2016年10月17日投资者关系活动记录表.pdf",
  1221 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-10-18_october-17-2016-investor-relations-activity-record-sheet_7154.pdf",
  1222 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1223 + },
  1224 + {
  1225 + "release_at": "2016-10-11 10:04:11",
  1226 + "name": "300199翰宇药业2016-10-14_2016年10月13日投资者关系活动记录表.docx",
  1227 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-10-14_october-13-2016-investor-relations-activity-record-sheet_2136.docx",
  1228 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1229 + },
  1230 + {
  1231 + "release_at": "2016-09-4 10:08:25",
  1232 + "name": "300199翰宇药业2016-09-28_2016年9月27日投资者关系活动记录表.pdf",
  1233 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-09-28_investor-relations-activity-record-sheet-for-september-27-2016_5997.pdf",
  1234 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1235 + },
  1236 + {
  1237 + "release_at": "2016-09-7 05:32:27",
  1238 + "name": "300199翰宇药业2016-09-26_2016年9月23日投资者关系活动记录表.pdf",
  1239 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-09-26_investor-relations-activity-record-for-september-23-2016_9551.pdf",
  1240 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1241 + },
  1242 + {
  1243 + "release_at": "2016-09-12 04:55:18",
  1244 + "name": "300199翰宇药业2016-09-14_2016年9月12日投资者关系活动记录表.pdf",
  1245 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-09-14_investor-relations-activity-record-for-september-12-2016_8051.pdf",
  1246 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1247 + },
  1248 + {
  1249 + "release_at": "2016-09-8 05:32:27",
  1250 + "name": "300199_翰宇药业2016-09-12_2016年9月8日投资者关系活动记录表_3358.pdf",
  1251 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-09-12_investor-relations-activity-record-for-september-8-2016_3358.pdf",
  1252 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1253 + },
  1254 + {
  1255 + "release_at": "2016-09-18 11:33:12",
  1256 + "name": "300199翰宇药业2016-09-09_2016年9月6日投资者关系活动记录表.pdf",
  1257 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-09-09_investor-relations-activity-record-sheet-for-september-6-2016_8597.pdf",
  1258 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1259 + },
  1260 + {
  1261 + "release_at": "2016-08-14 10:48:05",
  1262 + "name": "300199翰宇药业2016-08-02_2016年7月28日投资者关系活动记录表_二.pdf",
  1263 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-08-02_investor-relations-activity-record-sheet-for-july-28-2016_ii__5436.pdf",
  1264 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1265 + },
  1266 + {
  1267 + "release_at": "2016-08-1 07:23:09",
  1268 + "name": "300199翰宇药业2016-08-02_2016年7月28日投资者关系活动记录表_一.pdf",
  1269 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-07-12_investor-relations-activity-record-for-july-8-2016_9778.pdf",
  1270 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1271 + },
  1272 + {
  1273 + "release_at": "2016-07-22 11:19:18",
  1274 + "name": "300199翰宇药业2016-07-12_2016年7月8日投资者关系活动记录表.pdf",
  1275 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-07-04_investor-relations-activity-record-for-july-1-2016_3227.pdf",
  1276 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1277 + },
  1278 + {
  1279 + "release_at": "2016-07-6 07:25:13",
  1280 + "name": "300199翰宇药业2016-07-04_2016年7月1日投资者关系活动记录表.pdf",
  1281 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-06-30_investor-relations-activity-record-for-june-27-2016_8256.pdf",
  1282 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1283 + },
  1284 + {
  1285 + "release_at": "2016-06-13 11:16:08",
  1286 + "name": "300199翰宇药业2016-06-30_2016年6月27日投资者关系活动记录表.pdf",
  1287 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-06-30_investor-relations-activity-record-for-june-27-2016_8256.pdf",
  1288 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1289 + },
  1290 + {
  1291 + "release_at": "2016-06-0 01:54:53",
  1292 + "name": "300199翰宇药业2016-06-16_2016年6月14日投资者关系活动记录表.pdf",
  1293 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-06-16_investor-relations-activity-record-sheet-june-14-2016_7707.pdf",
  1294 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1295 + },
  1296 + {
  1297 + "release_at": "2016-03-12 00:19:56",
  1298 + "name": "300199翰宇药业2016-03-18_2016年3月17日投资者关系活动记录表.pdf",
  1299 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-03-18_investor-relations-activity-record-for-march-17-2016_1065.pdf",
  1300 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1301 + },
  1302 + {
  1303 + "release_at": "2016-03-18 06:28:16",
  1304 + "name": "300199翰宇药业2016-03-09_2016年3月4日投资者关系活动记录表.pdf",
  1305 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-03-09_investor-relations-activity-record-for-march-4-2016_2872.pdf",
  1306 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1307 + },
  1308 + {
  1309 + "release_at": "2016-02-0 05:23:16",
  1310 + "name": "300199翰宇药业2016-02-23_2016年2月22日投资者关系活动记录表.pdf",
  1311 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-02-23_investor-relations-activity-record-for-february-22-2016_7179.pdf",
  1312 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1313 + },
  1314 + {
  1315 + "release_at": "2016-02-10 11:07:19",
  1316 + "name": "300199翰宇药业2016-02-19_2016年2月18日投资者关系活动记录表.pdf",
  1317 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2016-02-19_investor-relations-activity-record-for-february-18-2016_6199.pdf",
  1318 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1319 + },
  1320 + {
  1321 + "release_at": "2016-01-4 07:39:04",
  1322 + "name": "300199翰宇药业2016-01-18_2016年1月15日投资者关系活动记录表.pdf",
  1323 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2016-01-18_january-15-2016-investor-relations-activity-record_3283.pdf",
  1324 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1325 + },
  1326 + {
  1327 + "release_at": "2015-12-24 03:50:13",
  1328 + "name": "300199翰宇药业2015-12-07_2015年12月4日投资者关系活动记录表.pdf",
  1329 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2015-12-07_investor-relations-activity-record-for-december-4-2015_1184.pdf",
  1330 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1331 + },
  1332 + {
  1333 + "release_at": "2015-11-7 03:04:24",
  1334 + "name": "300199翰宇药业2015-11-26_2015年11月24日投资者关系活动记录表.pdf",
  1335 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2015-11-26_november-24-2015-investor-relations-activity-record_4876.pdf",
  1336 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1337 + },
  1338 + {
  1339 + "release_at": "2015-11-2 04:52:21",
  1340 + "name": "300199翰宇药业2015-11-10_2015年11月9日投资者关系活动记录表.pdf",
  1341 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2015-11-10_november-9-2015-investor-relations-activity-record_6243.pdf",
  1342 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1343 + },
  1344 + {
  1345 + "release_at": "2015-11-3 10:18:40",
  1346 + "name": "300199翰宇药业2015-11-02_2015年10月30日投资者关系活动记录表.pdf",
  1347 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2015-11-02_october-30-2015-investor-relations-activity-record_6537.pdf",
  1348 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1349 + },
  1350 + {
  1351 + "release_at": "2015-10-8 01:54:07",
  1352 + "name": "300199翰宇药业2015-10-28_2015年10月23日投资者关系活动记录表pdf",
  1353 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2015-10-28_october-23-2015-investor-relations-activity-record_9215.pdf",
  1354 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1355 + },
  1356 + {
  1357 + "release_at": "2015-09-20 09:25:19",
  1358 + "name": "300199翰宇药业2015-09-28_2015年9月25日投资者关系活动记录表.pdf",
  1359 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2015-09-28_investor-relations-activity-record-for-september-25-2015_4371.pdf",
  1360 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1361 + },
  1362 + {
  1363 + "release_at": "2015-09-14 03:22:29",
  1364 + "name": "300199翰宇药业2015-09-25_2015年9月23日投资者关系活动记录表.pdf",
  1365 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2015-09-25_investor-relations-activity-record-for-september-23-2015_1884.pdf",
  1366 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1367 + },
  1368 + {
  1369 + "release_at": "2015-09-0 02:49:54",
  1370 + "name": "300199翰宇药业2015-09-16_2015年9月15日投资者关系活动记录表.pdf",
  1371 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2015-09-16_investor-relations-activity-record-for-september-15-2015_5735.pdf",
  1372 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1373 + },
  1374 + {
  1375 + "release_at": "2015-09-19 08:36:09",
  1376 + "name": "300199翰宇药业2015-09-10_2015年9月9日投资者关系活动记录表.pdf",
  1377 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2015-09-10_investor-relations-activity-record-for-september-9-2015_9112.pdf",
  1378 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1379 + },
  1380 + {
  1381 + "release_at": "2015-09-17 08:37:39",
  1382 + "name": "300199翰宇药业2015-09-02_2015年9月2日投资者关系活动记录表.pdf",
  1383 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2015-09-02_investor-relations-activity-record-for-september-2-2015_4434.pdf",
  1384 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1385 + },
  1386 + {
  1387 + "release_at": "2015-09-15 10:21:34",
  1388 + "name": "300199翰宇药业2015-09-01_2015年8月31日投资者关系活动记录表.pdf",
  1389 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2015-09-01_august-31-2015-investor-relations-activity-record_6139.pdf",
  1390 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1391 + },
  1392 + {
  1393 + "release_at": "2015-08-19 01:28:28",
  1394 + "name": "300199翰宇药业2015-08-26_2015年8月25日投资者关系活动记录表.pdf",
  1395 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2015-08-26_august-25-2015-investor-relations-activity-record_2788.pdf",
  1396 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1397 + },
  1398 + {
  1399 + "release_at": "2015-08-17 01:03:11",
  1400 + "name": "300199翰宇药业2015-08-25_2015年8月24日投资者关系活动记录表.pdf",
  1401 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2015-08-25_investor-relations-activity-record-for-august-24-2015_7740.pdf",
  1402 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1403 + },
  1404 + {
  1405 + "release_at": "2015-07-12 04:15:16",
  1406 + "name": "300199翰宇药业2015-07-15_2015年7月13日投资者关系活动记录表.pdf",
  1407 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceutical-2015-07-15_investor-relations-activity-record-sheet-for-july-13-2015_3770.pdf",
  1408 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1409 + },
  1410 + {
  1411 + "release_at": "2015-07-14 01:17:02",
  1412 + "name": "300199翰宇药业2015-07-03_2015年7月2日投资者关系活动记录表.pdf",
  1413 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2015-07-03_investor-relations-activity-record-for-july-2-2015_2854.pdf",
  1414 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1415 + },
  1416 + {
  1417 + "release_at": "2015-06-20 04:21:54",
  1418 + "name": "300199翰宇药业2015-06-04_2015年6月3日投资者关系活动记录表.pdf",
  1419 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2015-06-04_investor-relations-activity-record-for-june-3-2015_7780.pdf",
  1420 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1421 + },
  1422 + {
  1423 + "release_at": "2015-05-5 02:41:28",
  1424 + "name": "300199翰宇药业2015-05-20_2015年5月19日投资者关系活动记录表.pdf",
  1425 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2015-05-20_may-19-2015-investor-relations-activity-record-sheet_5850.pdf",
  1426 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1427 + },
  1428 + {
  1429 + "release_at": "2015-03-13 02:55:09",
  1430 + "name": "300199翰宇药业2015-03-16_2015年3月13日投资者关系活动记录表.pdf",
  1431 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2015-03-16_investor-relations-activity-record-for-march-13-2015_9640.pdf",
  1432 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1433 + },
  1434 + {
  1435 + "release_at": "2015-01-17 06:16:52",
  1436 + "name": "300199翰宇药业2015-01-12_2015年1月9日投资者关系活动记录表.pdf",
  1437 + "href": "/file_manage/3531/20250901/300199_hanyu-pharmaceuticals-2015-01-12_january-9-2015-investor-relations-activity-record_8915.pdf",
  1438 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1439 + },
  1440 + {
  1441 + "release_at": "2014-12-7 11:27:31",
  1442 + "name": "300199翰宇药业2014-12-18_2014年12月17日投资者关系活动记录表.pdf",
  1443 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2014-12-18_investor-relations-activity-record-for-december-17-2014_1666.pdf",
  1444 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1445 + },
  1446 + {
  1447 + "release_at": "2014-12-12 02:07:13",
  1448 + "name": "300199翰宇药业2014-12-17_2014年12月16日投资者关系活动记录表.pdf",
  1449 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2014-12-17_2014-december-16-investor-relations-activity-record_9100.pdf",
  1450 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1451 + },
  1452 + {
  1453 + "release_at": "2014-12-8 05:34:12",
  1454 + "name": "300199翰宇药业2014-12-15_2014年12月11日投资者关系活动记录表.pdf",
  1455 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2014-12-15_investor-relations-activity-record-for-december-11-2014_3385.pdf",
  1456 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1457 + },
  1458 + {
  1459 + "release_at": "2014-11-11 05:37:37",
  1460 + "name": "300199翰宇药业2014-11-26_2014年11月25日投资者关系活动记录表.pdf",
  1461 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2014-11-26_november-25-2014-investor-relations-activity-record_5012.pdf",
  1462 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1463 + },
  1464 + {
  1465 + "release_at": "2014-11-18 11:22:43",
  1466 + "name": "300199翰宇药业2014-11-21_2014年11月20日投资者关系活动记录表.pdf",
  1467 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2014-11-21_november-20-2014-investor-relations-activity-record_8437.pdf",
  1468 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1469 + },
  1470 + {
  1471 + "release_at": "2014-09-13 01:21:08",
  1472 + "name": "300199翰宇药业2014-09-03_2014年9月2日投资者关系活动记录表.pdf",
  1473 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2014-09-03_investor-relations-activity-record-for-september-2-2014_3385.pdf",
  1474 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1475 + },
  1476 + {
  1477 + "release_at": "2014-09-1 08:16:20",
  1478 + "name": "300199翰宇药业2014-09-01_2014年8月28日投资者关系活动记录表.pdf",
  1479 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2014-09-01_august-28-2014-investor-relations-activity-record_6672.pdf",
  1480 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1481 + },
  1482 + {
  1483 + "release_at": "2014-02-2 08:48:09",
  1484 + "name": "300199翰宇药业2014-02-14_2014年2月12日投资者关系活动记录表.pdf",
  1485 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2014-02-14_investor-relations-activity-record-sheet-for-february-12-2014_5304.pdf",
  1486 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1487 + },
  1488 + {
  1489 + "release_at": "2013-11-11 06:14:21",
  1490 + "name": "300199翰宇药业2013-11-20_2013年11月19日投资者关系活动记录表.pdf",
  1491 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2013-11-20_november-19-2013-investor-relations-activity-record_3561.pdf",
  1492 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1493 + },
  1494 + {
  1495 + "release_at": "2013-11-16 00:45:28",
  1496 + "name": "300199翰宇药业2013-11-07_2013年11月7日投资者关系活动记录表.pdf",
  1497 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2013-11-07_november-7-2013-investor-relations-activity-record-sheet_4976.pdf",
  1498 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1499 + },
  1500 + {
  1501 + "release_at": "2013-10-20 07:21:05",
  1502 + "name": "300199翰宇药业2013-10-28_2013年10月24日投资者关系活动记录表.pdf",
  1503 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2013-10-28_october-24-2013-investor-relations-activity-record_4002.pdf",
  1504 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1505 + },
  1506 + {
  1507 + "release_at": "2013-09-0 01:26:27",
  1508 + "name": "300199翰宇药业2013-09-18_2013年9月16日投资者关系活动记录表.pdf",
  1509 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2013-09-18_investor-relations-activity-record-sheet-for-september-16-2013_8969.pdf",
  1510 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1511 + },
  1512 + {
  1513 + "release_at": "2013-09-4 05:34:37",
  1514 + "name": "300199翰宇药业2013-09-02_2013年8月29日投资者关系活动记录表.pdf",
  1515 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2013-09-02_august-29-2013-investor-relations-activity-record_3890.pdf",
  1516 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1517 + },
  1518 + {
  1519 + "release_at": "2013-08-14 09:42:40",
  1520 + "name": "300199翰宇药业2013-08-26_2013年8月23日投资者关系活动记录表.pdf",
  1521 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2013-08-26_investor-relations-activity-record-for-august-23-2013_8170.pdf",
  1522 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1523 + },
  1524 + {
  1525 + "release_at": "2013-08-16 01:24:11",
  1526 + "name": "300199翰宇药业2013-08-23_2013年8月21日投资者关系活动记录表.pdf",
  1527 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2013-08-23_august-21-2013-investor-relations-activity-record-sheet_4447.pdf",
  1528 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1529 + },
  1530 + {
  1531 + "release_at": "2013-08-11 00:48:03",
  1532 + "name": "300199翰宇药业2013-08-16_2013年8月15日投资者关系活动记录表.pdf",
  1533 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2013-08-16_investor-relations-activity-record-for-august-15-2013_8819.pdf",
  1534 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1535 + },
  1536 + {
  1537 + "release_at": "2013-08-9 07:16:27",
  1538 + "name": "300199翰宇药业2013-08-16_2013年8月14日投资者关系活动记录表.pdf",
  1539 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2013-08-16_august-14-2013-investor-relations-activity-record_6495.pdf",
  1540 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1541 + },
  1542 + {
  1543 + "release_at": "2013-07-17 04:33:50",
  1544 + "name": "300199翰宇药业2013-07-10_2013年7月8日投资者关系活动记录表.pdf",
  1545 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2013-07-10_investor-relations-activity-record-for-july-8-2013_1373.pdf",
  1546 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1547 + },
  1548 + {
  1549 + "release_at": "2013-05-5 08:58:41",
  1550 + "name": "300199翰宇药业2013-05-28_2013年5月24日投资者关系活动记录表.pdf",
  1551 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2013-05-28_may-24-2013-investor-relations-activity-record_6459.pdf",
  1552 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1553 + },
  1554 + {
  1555 + "release_at": "2013-05-22 02:49:11",
  1556 + "name": "300199翰宇药业2013-05-15_2013年5月13日投资者关系活动记录表.pdf",
  1557 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2013-05-15_may-13-2013-investor-relations-activity-record_4417.pdf",
  1558 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1559 + },
  1560 + {
  1561 + "release_at": "2013-05-1 02:00:05",
  1562 + "name": "300199翰宇药业2013-05-06_2013年4月26日投资者关系活动记录表.pdf",
  1563 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2013-05-06_investor-relations-activity-record-sheet-april-26-2013_2242.pdf",
  1564 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1565 + },
  1566 + {
  1567 + "release_at": "2013-04-19 03:49:10",
  1568 + "name": "300199翰宇药业2013-04-16_2013年4月12日投资者关系活动记录表.pdf",
  1569 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2013-04-16_investor-relations-activity-record-for-april-12-2013_8343.pdf",
  1570 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1571 + },
  1572 + {
  1573 + "release_at": "2013-04-12 09:03:47",
  1574 + "name": "300199翰宇药业2013-04-03_2013年4月2日投资者关系活动记录表.docx",
  1575 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2013-04-03_investor-relations-activity-record-for-april-2-2013_2768.docx",
  1576 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1577 + },
  1578 + {
  1579 + "release_at": "2013-03-9 09:45:36",
  1580 + "name": "300199翰宇药业2013-03-29_2013年3月27日投资者关系活动记录表.docx",
  1581 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2013-03-29_investor-relations-activity-record-for-march-27-2013_3656.docx",
  1582 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1583 + },
  1584 + {
  1585 + "release_at": "2013-01-15 07:01:29",
  1586 + "name": "300199翰宇药业2013-01-23_2013年1月21日投资者关系活动记录表.pdf",
  1587 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2013-01-23_january-21-2013-investor-relations-activity-record_2497.pdf",
  1588 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1589 + },
  1590 + {
  1591 + "release_at": "2013-01-1 07:55:57",
  1592 + "name": "300199翰宇药业2013-01-21_2013年1月17日投资者关系活动记录表.pdf",
  1593 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2013-01-21_january-17-2013-investor-relations-activity-record-sheet_8184.pdf",
  1594 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1595 + },
  1596 + {
  1597 + "release_at": "2013-01-5 03:31:25",
  1598 + "name": "300199翰宇药业2013-01-10_2013年1月8日投资者关系活动记录表.pdf",
  1599 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2013-01-10_january-8-2013-investor-relations-activity-record_2914.pdf",
  1600 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1601 + },
  1602 + {
  1603 + "release_at": "2012-12-2 04:20:21",
  1604 + "name": "300199翰宇药业2012-12-14_2012年12月12日投资者关系活动记录表.pdf",
  1605 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2012-12-14_investor-relations-activity-record-for-december-12-2012_9336.pdf",
  1606 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1607 + },
  1608 + {
  1609 + "release_at": "2012-12-24 02:23:36",
  1610 + "name": "300199翰宇药业2012-12-10_2012年12月6日投资者关系活动记录表.pdf",
  1611 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2012-12-10_investor-relations-activity-record-for-december-6-2012_3375.pdf",
  1612 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1613 + },
  1614 + {
  1615 + "release_at": "2012-11-3 00:18:53",
  1616 + "name": "300199翰宇药业2012-11-26_2012年11月23日投资者关系活动记录表.pdf",
  1617 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2012-11-26_november-23-2012-investor-relations-activity-record_2930.pdf",
  1618 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1619 + },
  1620 + {
  1621 + "release_at": "2012-11-8 08:08:33",
  1622 + "name": "300199翰宇药业2012-11-02_2012年10月30日投资者关系活动记录表.pdf",
  1623 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2012-11-02_october-30-2012-investor-relations-activity-record_3969.pdf",
  1624 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1625 + },
  1626 + {
  1627 + "release_at": "2012-10-9 06:57:38",
  1628 + "name": "300199翰宇药业2012-10-29_2012年10月25日投资者关系活动记录表.pdf",
  1629 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2012-10-29_october-25-2012-investor-relations-activity-record_7493.pdf",
  1630 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1631 + },
  1632 + {
  1633 + "release_at": "2012-09-19 09:42:12",
  1634 + "name": "300199翰宇药业2012-09-05_2012年9月3日投资者关系活动记录表.pdf",
  1635 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2012-09-05_investor-relations-activity-record-for-september-3-2012_6262.pdf",
  1636 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1637 + },
  1638 + {
  1639 + "release_at": "2012-08-2 07:20:35",
  1640 + "name": "300199翰宇药业2012-08-30_2012年8月28日投资者关系活动记录表.pdf",
  1641 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2012-08-30_investor-relations-activity-record-for-august-28-2012_7849.pdf",
  1642 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1643 + },
  1644 + {
  1645 + "release_at": "2012-08-6 04:32:07",
  1646 + "name": "300199翰宇药业2012-08-20_2012年8月16日投资者关系活动记录表.pdf",
  1647 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceuticals-2012-08-20_investor-relations-activity-record-for-august-16-2012_7963.pdf",
  1648 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1649 + },
  1650 + {
  1651 + "release_at": "2012-08-20 00:38:33",
  1652 + "name": "300199翰宇药业2012-08-17_2012年8月15日投资者关系活动记录表.pdf",
  1653 + "href": "/file_manage/3531/20250902/300199_hanyu-pharmaceutical-2012-08-17_investor-relations-activity-record-for-august-15-2012_4739.pdf",
  1654 + "image": "/upload/p/3531/image_other/2025-05/downloads.png"
  1655 + }
  1656 +]';
  1657 + $data = json_decode($str,true);
  1658 + $customContentModel = new CustomModuleContent();
  1659 + $customExtendContentTModel = new CustomModuleExtentContent();
  1660 + foreach ($data as $item){
  1661 + $info = $customContentModel->read(['name'=>$item['name'],'project_id'=>$project_id,'module_id'=>5]);
  1662 + if($info === false){
  1663 + $contentId = $customContentModel->addReturnId(['name'=>$item['name'],'image'=>$item['image'],'project_id'=>$project_id,'release_at'=>date('Y-m-d H:i:s'),'module_id'=>5]);
  1664 + $route = RouteMap::setRoute($contentId, RouteMap::SOURCE_MODULE, $contentId, $project_id);
  1665 + $customContentModel->edit(['route'=>$route],['id'=>$contentId]);
  1666 + $saveData = [
  1667 + ['key'=>'pd_extended_field_1', 'type'=>4, 'values'=>$item['href'], 'content_id'=>$contentId, 'project_id'=>$project_id, 'module_id'=>5, 'created_at'=>date('Y-m-d H:i:s'), 'updated_at'=>date('Y-m-d H:i:s')],
  1668 + ];
  1669 + $customExtendContentTModel->insert($saveData);
  1670 + }
  1671 + }
  1672 + return true;
  1673 + }
583 } 1674 }
@@ -33,6 +33,7 @@ class CreateProject extends Command @@ -33,6 +33,7 @@ class CreateProject extends Command
33 protected $description = '创建项目'; 33 protected $description = '创建项目';
34 34
35 public function handle(){ 35 public function handle(){
  36 + dd(1);
36 return $this->sync(); 37 return $this->sync();
37 } 38 }
38 39
@@ -42,10 +43,12 @@ class CreateProject extends Command @@ -42,10 +43,12 @@ class CreateProject extends Command
42 * @throws \Exception 43 * @throws \Exception
43 */ 44 */
44 public function sync($is_update = 0){ 45 public function sync($is_update = 0){
45 - $company = '济南市莱芜凤城铝合金有限公司';  
46 - $mobile = '13806340552';  
47 - $plan = '标准版';  
48 - $cooperate_date = '2019-11-19'; 46 + $company = '山东临磨数控机床装备有限公司(自建站)';
  47 + $mobile = '18663004388';
  48 + $lead_name = '18663004388';
  49 + $plan = '商务版';
  50 + $cooperate_date = '2025-08-21';
  51 +// $channel = '{"user_id": "732", "zone_id": "1", "channel_id": "95"}';
49 $channel = '{"user_id": "1989", "zone_id": "4", "channel_id": "13"}'; 52 $channel = '{"user_id": "1989", "zone_id": "4", "channel_id": "13"}';
50 53
51 $title = date('Ymd') . '-' . $company; 54 $title = date('Ymd') . '-' . $company;
@@ -53,7 +56,7 @@ class CreateProject extends Command @@ -53,7 +56,7 @@ class CreateProject extends Command
53 'project'=>[ 56 'project'=>[
54 'title' => $title, 57 'title' => $title,
55 'company' => $company, 58 'company' => $company,
56 - 'lead_name' => $mobile, 59 + 'lead_name' => $lead_name,
57 'mobile' => $mobile, 60 'mobile' => $mobile,
58 'mysql_id'=>Project::MYSQL_ID, 61 'mysql_id'=>Project::MYSQL_ID,
59 'serve_id'=>9, 62 'serve_id'=>9,
@@ -61,7 +64,7 @@ class CreateProject extends Command @@ -61,7 +64,7 @@ class CreateProject extends Command
61 'channel' => $channel, 64 'channel' => $channel,
62 'requirement' => '', 65 'requirement' => '',
63 'cooperate_date' => $cooperate_date, 66 'cooperate_date' => $cooperate_date,
64 - 'from_order_id' => '', 67 + 'from_order_id' => uniqid(),
65 'type' => $is_update, 68 'type' => $is_update,
66 'is_upgrade'=>$is_update, 69 'is_upgrade'=>$is_update,
67 ], 70 ],
@@ -101,6 +101,19 @@ class SyncProject extends Command @@ -101,6 +101,19 @@ class SyncProject extends Command
101 if($data['data']['order_type'] == '续费'){ 101 if($data['data']['order_type'] == '续费'){
102 $this->renewSync($data['data']); 102 $this->renewSync($data['data']);
103 } 103 }
  104 + // TODO 如果是续费项目 并且有GEO版本,需要处理GEO版本
  105 + if (($data['data']['order_type'] == '续费') && !empty($data['data']['geo_plan']) && ($data['data']['geo_plan'] != '无')) {
  106 + // 续费单,并且有GEO版本, 正常版本不创建 初始化正常版本
  107 + $data['data']['plan_marketing'] = '无';
  108 + //创建对应的GEO版本
  109 + $projectModel = new Project();
  110 + $seo_plan = $this->versionSeoData($data['data']['geo_plan'] ?? '');
  111 + $projectInfo = $projectModel->leftJoin('gl_project_deploy_build', 'gl_project.id', '=', 'gl_project_deploy_build.project_id')->where('gl_project.company', $data['data']['company_name'])->where('gl_project_deploy_build.seo_plan',$seo_plan)->select(['gl_project.id AS id'])->first();
  112 + if (empty($projectInfo)) {
  113 + // 创建对应GEO项目
  114 + $this->sync($data['data'],$is_update);
  115 + }
  116 + }
104 $item->status = NoticeLog::STATUS_SUCCESS; 117 $item->status = NoticeLog::STATUS_SUCCESS;
105 $item->save(); 118 $item->save();
106 echo 'success:' . $item['id'] . '执行时间:' . date('Y-m-d H:i:s') . PHP_EOL; 119 echo 'success:' . $item['id'] . '执行时间:' . date('Y-m-d H:i:s') . PHP_EOL;
@@ -136,7 +149,7 @@ class SyncProject extends Command @@ -136,7 +149,7 @@ class SyncProject extends Command
136 * @time :2023/8/11 15:33 149 * @time :2023/8/11 15:33
137 */ 150 */
138 public function renewSync($param){ 151 public function renewSync($param){
139 - $title = date('Ymd') . '-' . $param['company_name'];; 152 + $title = date('Ymd') . '-' . $param['company_name'];
140 $data = [ 153 $data = [
141 'title' => '【续费单】'.$title, 154 'title' => '【续费单】'.$title,
142 'company' => $param['company_name'], 155 'company' => $param['company_name'],
@@ -146,7 +159,7 @@ class SyncProject extends Command @@ -146,7 +159,7 @@ class SyncProject extends Command
146 'channel' => json_encode(Channel::getProjectChannel($param['company_id'], $param['username_sales'])), 159 'channel' => json_encode(Channel::getProjectChannel($param['company_id'], $param['username_sales'])),
147 'requirement' => $param['remark'], 160 'requirement' => $param['remark'],
148 'cooperate_date' => date('Y-m-d', $param['create_time']), 161 'cooperate_date' => date('Y-m-d', $param['create_time']),
149 -// 'api_no' => $param['id'], //改手动填 162 + 'api_no' => $param['id'], //改手动填
150 'amount' => $param['plan_price'], 163 'amount' => $param['plan_price'],
151 'contract' => json_encode($param['files']), 164 'contract' => json_encode($param['files']),
152 'bill' => json_encode($param['images']), 165 'bill' => json_encode($param['images']),
@@ -203,7 +216,6 @@ class SyncProject extends Command @@ -203,7 +216,6 @@ class SyncProject extends Command
203 */ 216 */
204 public function sync($param,$is_update = 0){ 217 public function sync($param,$is_update = 0){
205 //TODO::4月12日 之前的项目都是v6 218 //TODO::4月12日 之前的项目都是v6
206 -  
207 $version = (($is_update == 1) ? Project::VERSION_SIX : (empty($param['version']) ? Project::VERSION_SEVEN : $param['version'])); 219 $version = (($is_update == 1) ? Project::VERSION_SIX : (empty($param['version']) ? Project::VERSION_SEVEN : $param['version']));
208 $title = date('Ymd') . '-' . $param['company_name']; 220 $title = date('Ymd') . '-' . $param['company_name'];
209 $data = [ 221 $data = [
  1 +<?php
  2 +
  3 +namespace App\Console\Commands\Project;
  4 +
  5 +use App\Helper\Arr;
  6 +use App\Models\Product\Product;
  7 +use App\Models\Project\Project;
  8 +use App\Services\ProjectServer;
  9 +use Illuminate\Console\Command;
  10 +use Illuminate\Support\Facades\DB;
  11 +
  12 +class ThumbProjectImage extends Command
  13 +{
  14 + /**
  15 + * The name and signature of the console command.
  16 + *
  17 + * @var string
  18 + */
  19 + protected $signature = 'thumb_project_image {project_id}';
  20 +
  21 + /**
  22 + * The console command description.
  23 + *
  24 + * @var string
  25 + */
  26 + protected $description = '处理项目产品缩略图';
  27 +
  28 + public function handle()
  29 + {
  30 + $project_id = $this->argument('project_id');
  31 +
  32 + if ($project_id > 0) {
  33 + //指定项目
  34 + $this->output('project_id:' . $project_id . ' | start');
  35 +
  36 + $project_info = ProjectServer::useProject($project_id);
  37 +
  38 + if ($project_info) {
  39 + $thumb_w = $project_info->deploy_build->thumb_w ?? 0;
  40 + Product::select(['id', 'project_id', 'thumb'])->chunk(100, function ($products) use ($thumb_w) {
  41 + foreach ($products as $product) {
  42 + $thumb = $product->thumb;
  43 + if (isset($thumb['url']) && $thumb['url']) {
  44 + $new_thumb = thumbImageByUrl($thumb['url'], $thumb_w);
  45 + if ($new_thumb != $thumb['url']) {
  46 + $thumb['url'] = $new_thumb;
  47 + $json_thumb = Arr::a2s($thumb);
  48 + if (strlen($json_thumb) <= 500) {
  49 + $product->timestamps = false;
  50 + $product->thumb = $json_thumb;
  51 + $product->save();
  52 +
  53 + $this->output('project_id:' . $product->project_id . ',product_id:' . $product->id . ' | success');
  54 + }
  55 + }
  56 + }
  57 + }
  58 + });
  59 +
  60 + DB::disconnect('custom_mysql');
  61 + }
  62 +
  63 + $this->output('project_id:' . $project_id . ' | end');
  64 + } else {
  65 + //所有项目
  66 + $projectModel = new Project();
  67 + $list = $projectModel->list(['delete_status' => 0, 'is_upgrade' => 0, 'type' => ['in', [1, 2, 3, 4, 6]]], 'id', ['id'], 'asc');
  68 + foreach ($list as $k => $v) {
  69 + $project_id = $v['id'];
  70 +
  71 + $this->output('project_id:' . $project_id . ' | start');
  72 +
  73 + $project_info = ProjectServer::useProject($project_id);
  74 +
  75 + if ($project_info) {
  76 + $thumb_w = $project_info->deploy_build->thumb_w ?? 0;
  77 + Product::select(['id', 'project_id', 'thumb'])->chunk(100, function ($products) use ($thumb_w) {
  78 + foreach ($products as $product) {
  79 + $thumb = $product->thumb;
  80 + if (isset($thumb['url']) && $thumb['url']) {
  81 + $new_thumb = thumbImageByUrl($thumb['url'], $thumb_w);
  82 + if ($new_thumb != $thumb['url']) {
  83 + $thumb['url'] = $new_thumb;
  84 + $json_thumb = Arr::a2s($thumb);
  85 + if (strlen($json_thumb) <= 500) {
  86 + $product->timestamps = false;
  87 + $product->thumb = $json_thumb;
  88 + $product->save();
  89 +
  90 + $this->output('project_id:' . $product->project_id . ',product_id:' . $product->id . ' | success');
  91 + }
  92 + }
  93 + }
  94 + }
  95 + });
  96 +
  97 + DB::disconnect('custom_mysql');
  98 + }
  99 +
  100 + $this->output('project_id:' . $project_id . ' | end');
  101 + }
  102 + }
  103 + }
  104 +
  105 +
  106 + /**
  107 + * 输出处理日志
  108 + * @param $message
  109 + * @return bool
  110 + */
  111 + public function output($message)
  112 + {
  113 + echo date('Y-m-d H:i:s') . ' | ' . $message . PHP_EOL;
  114 + return true;
  115 + }
  116 +}
@@ -48,7 +48,7 @@ class IndexedPages extends BaseCommands @@ -48,7 +48,7 @@ class IndexedPages extends BaseCommands
48 $project_ids[] = 2104; 48 $project_ids[] = 2104;
49 } 49 }
50 foreach ($project_ids as $project_id){ 50 foreach ($project_ids as $project_id){
51 - $rank_data = RankData::where('project_id', $project_id)->where('lang', '')->first(); 51 + $rank_data = RankData::where('project_id', $project_id)->where('api_no', $api_no)->where('lang', '')->first();
52 if($rank_data){ 52 if($rank_data){
53 $rank_data->indexed_pages_num = $num; 53 $rank_data->indexed_pages_num = $num;
54 $rank_data->save(); 54 $rank_data->save();
@@ -32,60 +32,29 @@ class SyncTimeFiles extends Command @@ -32,60 +32,29 @@ class SyncTimeFiles extends Command
32 { 32 {
33 $fileModel = new File(); 33 $fileModel = new File();
34 // $imagesModel = new Image(); 34 // $imagesModel = new Image();
35 - $start = '2024-11-07 15:00:00';  
36 - $end = '2024-11-07 15:25:00'; 35 + $start = '2025-08-20 00:00:00';
  36 + $end = '2025-08-28 00:00:00';
37 $lists = $fileModel->list(['created_at'=>['between',[$start,$end]]]); 37 $lists = $fileModel->list(['created_at'=>['between',[$start,$end]]]);
38 foreach ($lists as $v){ 38 foreach ($lists as $v){
39 $path = $v['path']; 39 $path = $v['path'];
40 - $this->param['name'] = basename($path);  
41 - $this->param['path'] = str_replace('/'.$this->param['name'],'',$path);  
42 - $file_path = $this->getUrl($this->param['path'].'/'.$this->param['name'], 0,0);  
43 - $cmd = 'curl -k -F "file_path='.$file_path.'" -F "save_path=/www/wwwroot/cos'.$this->param['path'].'" https://v6-file.globalso.com/upload.php';  
44 - echo date('Y-m-d H:i:s') . ' | ' . $cmd . PHP_EOL;  
45 - $code = shell_exec($cmd); 40 + echo date('Y-m-d H:i:s') . ' | 图片链接:' . $path . PHP_EOL;
  41 + $code = $this->synchronizationFile($path);
46 if(200 != (int)$code){ 42 if(200 != (int)$code){
47 echo date('Y-m-d H:i:s') . ' | 错误状态:' . $code . PHP_EOL; 43 echo date('Y-m-d H:i:s') . ' | 错误状态:' . $code . PHP_EOL;
48 -// $errorFileModel = new ErrorFile();  
49 -// $errorFileModel->add(['path'=>$this->param['path'].'/'.$this->param['name']]); 44 + $errorFileModel = new ErrorFile();
  45 + $errorFileModel->add(['path'=>$this->param['path'].'/'.$this->param['name']]);
50 } 46 }
51 echo date('Y-m-d H:i:s') . ' | ok:' . $code . PHP_EOL; 47 echo date('Y-m-d H:i:s') . ' | ok:' . $code . PHP_EOL;
52 } 48 }
53 return true; 49 return true;
54 } 50 }
55 51
56 - /**  
57 - * @remark :获取图片文件链接  
58 - * @name :getUrl  
59 - * @author :lyh  
60 - * @method :post  
61 - * @time :2024/5/22 11:53  
62 - */  
63 - public function getUrl($path,$storage_type,$location){  
64 - if(is_array($path)){  
65 - $url =[];  
66 - foreach ($path as $v){  
67 - $url[] = $this->getUrl($v,$storage_type,$location);  
68 - }  
69 - }else{  
70 - if(empty($path)){  
71 - return '';  
72 - }  
73 - if((strpos($path,'https://')!== false) || (strpos($path,'http://') !== false)){  
74 - return $path;  
75 - }  
76 - if(substr($path,0,2) == '//'){  
77 - return 'https:'.$path;  
78 - }  
79 - if($location == 0){  
80 - $cos = config('filesystems.disks.cos');  
81 - $cosCdn = ($storage_type == 0) ? $cos['cdn'] : $cos['cdn1'];  
82 - $url = $cosCdn.$path;  
83 - }else{  
84 - $s3 = config('filesystems.disks.s3');  
85 - $cdn = $s3['cdn'];  
86 - $url = $cdn.$path;  
87 - }  
88 - }  
89 - return $url; 52 + public function synchronizationFile($path_name){
  53 + //同步到大文件
  54 + $file_path = config('filesystems.disks.cos')['cdn1'].$path_name;
  55 + $directoryPath = pathinfo($path_name, PATHINFO_DIRNAME);
  56 + $cmd = 'curl -k -F "file_path='.$file_path.'" -F "save_path=/www/wwwroot/cos'.$directoryPath.'" https://v6-file.globalso.com/upload.php';
  57 + return shell_exec($cmd);
90 } 58 }
  59 +
91 } 60 }
@@ -47,16 +47,11 @@ class SyncTimeMinuteFile extends Command @@ -47,16 +47,11 @@ class SyncTimeMinuteFile extends Command
47 } 47 }
48 foreach ($lists as $v){ 48 foreach ($lists as $v){
49 $path = $v['path']; 49 $path = $v['path'];
50 - if (file_exists($dir.$path)) {  
51 - echo date('Y-m-d H:i:s') . ' | file_ok:' . $dir.$path . PHP_EOL;  
52 - continue;  
53 - }  
54 - $this->param['name'] = basename($path);  
55 - $this->param['path'] = str_replace('/'.$this->param['name'],'',$path);  
56 - $file_path = $this->getUrl($this->param['path'].'/'.$this->param['name'], 0,0);  
57 - $cmd = 'curl -k -F "file_path='.$file_path.'" -F "save_path=/www/wwwroot/cos'.$this->param['path'].'" https://v6-file.globalso.com/upload.php';  
58 - echo date('Y-m-d H:i:s') . ' | ' . $cmd . PHP_EOL;  
59 - $code = shell_exec($cmd); 50 +// if (file_exists($dir.$path)) {
  51 +// echo date('Y-m-d H:i:s') . ' | file_ok:' . $dir.$path . PHP_EOL;
  52 +// continue;
  53 +// }
  54 + $code = $this->synchronizationFile($path);
60 if(200 != (int)$code){ 55 if(200 != (int)$code){
61 echo date('Y-m-d H:i:s') . ' | 错误状态:' . $code . PHP_EOL; 56 echo date('Y-m-d H:i:s') . ' | 错误状态:' . $code . PHP_EOL;
62 $errorFileModel = new ErrorFile(); 57 $errorFileModel = new ErrorFile();
@@ -67,6 +62,14 @@ class SyncTimeMinuteFile extends Command @@ -67,6 +62,14 @@ class SyncTimeMinuteFile extends Command
67 return true; 62 return true;
68 } 63 }
69 64
  65 + public function synchronizationFile($path_name){
  66 + //同步到大文件
  67 + $file_path = config('filesystems.disks.cos')['cdn1'].$path_name;
  68 + $directoryPath = pathinfo($path_name, PATHINFO_DIRNAME);
  69 + $cmd = 'curl -k -F "file_path='.$file_path.'" -F "save_path=/www/wwwroot/cos'.$directoryPath.'" https://v6-file.globalso.com/upload.php';
  70 + return shell_exec($cmd);
  71 + }
  72 +
70 /** 73 /**
71 * @remark :获取图片文件链接 74 * @remark :获取图片文件链接
72 * @name :getUrl 75 * @name :getUrl
@@ -30,12 +30,7 @@ class SyncVideo extends Command @@ -30,12 +30,7 @@ class SyncVideo extends Command
30 public function handle() 30 public function handle()
31 { 31 {
32 $path = $this->argument('path'); 32 $path = $this->argument('path');
33 - $this->param['name'] = basename($path);  
34 - $this->param['path'] = str_replace('/'.$this->param['name'],'',$path);  
35 - $file_path = $this->getUrl($this->param['path'].'/'.$this->param['name'], 0,0);  
36 - $cmd = 'curl -k -F "file_path='.$file_path.'" -F "save_path=/www/wwwroot/cos'.$this->param['path'].'" https://v6-file.globalso.com/upload.php';  
37 - echo date('Y-m-d H:i:s') . ' | ' . $cmd . PHP_EOL;  
38 - $code = shell_exec($cmd); 33 + $code = $this->synchronizationFile($path);
39 echo date('Y-m-d H:i:s') . ' | ' . $code . PHP_EOL; 34 echo date('Y-m-d H:i:s') . ' | ' . $code . PHP_EOL;
40 if(200 != (int)$code){ 35 if(200 != (int)$code){
41 echo date('Y-m-d H:i:s') . ' | ' . $code . PHP_EOL; 36 echo date('Y-m-d H:i:s') . ' | ' . $code . PHP_EOL;
@@ -45,6 +40,14 @@ class SyncVideo extends Command @@ -45,6 +40,14 @@ class SyncVideo extends Command
45 return true; 40 return true;
46 } 41 }
47 42
  43 + public function synchronizationFile($path_name){
  44 + //同步到大文件
  45 + $file_path = config('filesystems.disks.cos')['cdn1'].$path_name;
  46 + $directoryPath = pathinfo($path_name, PATHINFO_DIRNAME);
  47 + $cmd = 'curl -k -F "file_path='.$file_path.'" -F "save_path=/www/wwwroot/cos'.$directoryPath.'" https://v6-file.globalso.com/upload.php';
  48 + return shell_exec($cmd);
  49 + }
  50 +
48 /** 51 /**
49 * @remark :获取图片文件链接 52 * @remark :获取图片文件链接
50 * @name :getUrl 53 * @name :getUrl
@@ -85,7 +85,7 @@ class HyStockData extends Command @@ -85,7 +85,7 @@ class HyStockData extends Command
85 // 解析响应结果 85 // 解析响应结果
86 $responseResult = json_decode($response, true); 86 $responseResult = json_decode($response, true);
87 if ($responseResult) { 87 if ($responseResult) {
88 - $data = $responseResult['result'][0]['data']; 88 + $data = $responseResult['result'][0]['data'] ?? [];
89 if ($data) { 89 if ($data) {
90 //获取最近一条 90 //获取最近一条
91 $stock = StockData::orderBy('id', 'desc')->first(); 91 $stock = StockData::orderBy('id', 'desc')->first();
@@ -43,7 +43,34 @@ class Temp extends Command @@ -43,7 +43,34 @@ class Temp extends Command
43 43
44 public function handle() 44 public function handle()
45 { 45 {
46 - $this->specialImport(); 46 +
  47 + }
  48 +
  49 + /**
  50 + * 项目缩略图还原
  51 + * @param $project_id
  52 + * @author Akun
  53 + * @date 2025/09/04 10:48
  54 + */
  55 + public function thumbRollBack($project_id)
  56 + {
  57 + $project_info = ProjectServer::useProject($project_id);
  58 + if ($project_info) {
  59 + Product::select(['id', 'gallery'])->chunk(100, function ($products) {
  60 + foreach ($products as $product) {
  61 + $thumb = $product['gallery'][0] ?? [];
  62 + if (!empty($thumb)) {
  63 + $product->timestamps = false;
  64 + $product->thumb = Arr::a2s($thumb);
  65 + $product->save();
  66 +
  67 + $this->output('product_id:' . $product->id . ' | success');
  68 + }
  69 + }
  70 + });
  71 +
  72 + DB::disconnect('custom_mysql');
  73 + }
47 } 74 }
48 75
49 /** 76 /**
@@ -14,6 +14,10 @@ use App\Models\Manage\ManageHr; @@ -14,6 +14,10 @@ use App\Models\Manage\ManageHr;
14 use App\Models\Ticket\TicketDailyCount; 14 use App\Models\Ticket\TicketDailyCount;
15 use App\Models\Ticket\TicketDailyDeptCount; 15 use App\Models\Ticket\TicketDailyDeptCount;
16 use App\Models\Ticket\TicketDailyManageCount; 16 use App\Models\Ticket\TicketDailyManageCount;
  17 +use App\Models\Ticket\TicketMonthDeptCount;
  18 +use App\Models\Ticket\TicketMonthManageCount;
  19 +use App\Models\Ticket\TicketWeekDeptCount;
  20 +use App\Models\Ticket\TicketWeekManageCount;
17 use App\Models\WorkOrder\TicketLog; 21 use App\Models\WorkOrder\TicketLog;
18 use App\Models\WorkOrder\TicketProject; 22 use App\Models\WorkOrder\TicketProject;
19 use App\Models\WorkOrder\Tickets; 23 use App\Models\WorkOrder\Tickets;
@@ -55,9 +59,29 @@ class TicketCount extends Command @@ -55,9 +59,29 @@ class TicketCount extends Command
55 if($action == 'manage_action'){ 59 if($action == 'manage_action'){
56 $this->manage_action(); 60 $this->manage_action();
57 } 61 }
  62 + if($action == 'manage_week_action'){
  63 + $startOfLastWeek = Carbon::now()->subWeek()->startOfWeek(); // 上周一 00:00:00
  64 + $endOfLastWeek = Carbon::now()->subWeek()->endOfWeek(); // 上周日 23:59:59
  65 + $this->manage_week_month_action($startOfLastWeek,$endOfLastWeek,(new TicketWeekManageCount()));
  66 + }
  67 + if($action == 'manage_month_action'){
  68 + $startOfLastMonth = Carbon::now()->subMonth()->startOfMonth(); // 上个月 1号 00:00:00
  69 + $endOfLastMonth = Carbon::now()->subMonth()->endOfMonth(); // 上个月最后一天 23:59:59
  70 + $this->manage_week_month_action($startOfLastMonth,$endOfLastMonth,(new TicketMonthManageCount()));
  71 + }
58 if($action == 'dept_action'){ 72 if($action == 'dept_action'){
59 $this->dept_action(); 73 $this->dept_action();
60 } 74 }
  75 + if($action == 'dept_week_action'){
  76 + $startOfLastWeek = Carbon::now()->subWeek()->startOfWeek(); // 上周一 00:00:00
  77 + $endOfLastWeek = Carbon::now()->subWeek()->endOfWeek(); // 上周日 23:59:59
  78 + $this->dept_week_month_action($startOfLastWeek,$endOfLastWeek,(new TicketWeekDeptCount()));
  79 + }
  80 + if($action == 'dept_month_action'){
  81 + $startOfLastMonth = Carbon::now()->subMonth()->startOfMonth(); // 上个月 1号 00:00:00
  82 + $endOfLastMonth = Carbon::now()->subMonth()->endOfMonth(); // 上个月最后一天 23:59:59
  83 + $this->dept_week_month_action($startOfLastMonth,$endOfLastMonth,(new TicketMonthDeptCount()));
  84 + }
61 if($action == 'yesterday_daily_action'){ 85 if($action == 'yesterday_daily_action'){
62 $this->yesterday_daily_action(); 86 $this->yesterday_daily_action();
63 } 87 }
@@ -80,24 +104,36 @@ class TicketCount extends Command @@ -80,24 +104,36 @@ class TicketCount extends Command
80 $timeout_ratio = null; 104 $timeout_ratio = null;
81 $this->output('按人员统计:执行的人员名称/id:'.$item['name'].'/'.$item['manage_id']); 105 $this->output('按人员统计:执行的人员名称/id:'.$item['name'].'/'.$item['manage_id']);
82 $ticketLogModel = new TicketLog(); 106 $ticketLogModel = new TicketLog();
83 - $ticket_num = $ticketLogModel->counts(['engineer_id'=>$item['manage_id'],'is_engineer'=>1]); 107 + $ticket_num = $ticketLogModel->counts(['engineer_id'=>$item['manage_id'],'is_engineer'=>1,'status'=>['!=',9]]);
84 //工单总时长 108 //工单总时长
85 - $timeCount = $ticketLogModel->formatQuery(['engineer_id'=>$item['manage_id'],'is_engineer'=>1])->sum('end_time');  
86 - $complete_num = $ticketLogModel->counts(['engineer_id'=>$item['manage_id'],'is_engineer'=>1,'end_at'=>['!=',null]]); 109 + $timeCount = $ticketLogModel->formatQuery(['engineer_id'=>$item['manage_id'],'is_engineer'=>1,'status'=>['!=',9]])->sum('end_time');
  110 + $complete_num = $ticketLogModel->counts(['engineer_id'=>$item['manage_id'],'status'=>['!=',9],'is_engineer'=>1,'end_at'=>['!=',null]]);
87 if(!empty($timeCount)){ 111 if(!empty($timeCount)){
88 $average_time = round($timeCount / $complete_num, 3); 112 $average_time = round($timeCount / $complete_num, 3);
89 } 113 }
90 //最快完成的时间 114 //最快完成的时间
91 - $fastest_time = $ticketLogModel->formatQuery(['engineer_id'=>$item['manage_id'],'is_engineer'=>1,'end_at'=>['!=',null]])->min('end_time'); 115 + $fastest_time = $ticketLogModel->formatQuery(['engineer_id'=>$item['manage_id'],'status'=>['!=',9],'is_engineer'=>1,'end_at'=>['!=',null]])->min('end_time');
92 //最快完成时间 116 //最快完成时间
93 if(!$fastest_time){ 117 if(!$fastest_time){
94 $fastest_time = null; 118 $fastest_time = null;
95 } 119 }
96 //超时工单数量 120 //超时工单数量
97 - $timeout_num = $ticketLogModel->counts(['engineer_id'=>$item['manage_id'],'is_engineer'=>1,'plan_end_at'=>['>',date('Y-m-d H:i:s')]]); 121 + $timeout_num = $ticketLogModel
  122 + ->where('engineer_id', $item['manage_id'])->where('is_engineer', 1)->where('status','!=',9)
  123 + ->where(function ($query) {
  124 + $query->where(function ($q) {
  125 + $q->whereNotNull('end_at')->whereColumn('plan_end_at', '<', 'end_at');
  126 + })->orWhere(function ($q) {
  127 + $q->whereNull('end_at')->where('plan_end_at', '<', now());
  128 + });
  129 + })
  130 + ->count();
98 if(!empty($timeout_num)){ 131 if(!empty($timeout_num)){
99 $timeout_ratio = round($timeout_num / $ticket_num, 3); 132 $timeout_ratio = round($timeout_num / $ticket_num, 3);
100 } 133 }
  134 + if(!empty($complete_num)){
  135 + $complete_ratio = round($complete_num / $ticket_num, 2);
  136 + }
101 $data = [ 137 $data = [
102 'date'=>$date, 138 'date'=>$date,
103 'manage_id'=>$item['id'], 139 'manage_id'=>$item['id'],
@@ -108,7 +144,8 @@ class TicketCount extends Command @@ -108,7 +144,8 @@ class TicketCount extends Command
108 'timeout_num'=>$timeout_num,//超时工单数量 144 'timeout_num'=>$timeout_num,//超时工单数量
109 'complete_num'=>$complete_num,//完成工单数量 145 'complete_num'=>$complete_num,//完成工单数量
110 'dept_id'=>$item['dept_id'], 146 'dept_id'=>$item['dept_id'],
111 - 'timeout_ratio'=>$timeout_ratio ?? null 147 + 'timeout_ratio'=>$timeout_ratio ?? null,
  148 + 'complete_ratio'=>$complete_ratio ?? null
112 ]; 149 ];
113 //查询当前用户是否当日已有记录 150 //查询当前用户是否当日已有记录
114 $ticketManageInfo = $ticketManageCountModel->read(['date'=>$date,'manage_id'=>$item['id']],['id']); 151 $ticketManageInfo = $ticketManageCountModel->read(['date'=>$date,'manage_id'=>$item['id']],['id']);
@@ -124,6 +161,56 @@ class TicketCount extends Command @@ -124,6 +161,56 @@ class TicketCount extends Command
124 } 161 }
125 162
126 /** 163 /**
  164 + * @remark :按周/月统计数据
  165 + * @name :manage_week_action
  166 + * @author :lyh
  167 + * @method :post
  168 + * @time :2025/8/30 9:36
  169 + */
  170 + public function manage_week_month_action($startOfLast,$endOfLast,$model){
  171 + $manageHrModel = new ManageHr();
  172 + $manageList = $manageHrModel->list(['status'=>1,'dept_id'=>['in',[1,2]]],'id',['id','dept_id','manage_id','name','nickname']);
  173 + $ticketLogModel = new TicketLog();
  174 + foreach ($manageList as $item){
  175 + $timeout_ratio = $average_time = null;
  176 + $this->output('按人员统计:执行的人员名称/id:'.$item['name'].'/'.$item['manage_id']);
  177 + //上一周新增工单总数
  178 + $add_num = $ticketLogModel->counts(['created_at'=>['between',[$startOfLast,$endOfLast]],'engineer_id'=>$item['manage_id'],'is_engineer'=>1,'status'=>['!=',9]]);
  179 + //上一周完成工单(创建+完成都在当周,算一条有效数据)
  180 + $complete_num = $ticketLogModel->counts(['created_at'=>['between',[$startOfLast,$endOfLast]],'engineer_id'=>$item['manage_id'],'status'=>['!=',9],'is_engineer'=>1,'end_at'=>['between',[$startOfLast,$endOfLast]]]);
  181 + //上一周最快完成时长
  182 + $fastest_time = $ticketLogModel->formatQuery(['created_at'=>['between',[$startOfLast,$endOfLast]],'engineer_id'=>$item['manage_id'],'status'=>['!=',9],'is_engineer'=>1,'end_at'=>['between',[$startOfLast,$endOfLast]]])->min('end_time');
  183 + //上一周完成工单总时长
  184 + $timeCount = $ticketLogModel->formatQuery(['created_at'=>['between',[$startOfLast,$endOfLast]],'end_at'=>['between',[$startOfLast,$endOfLast]],'engineer_id'=>$item['manage_id'],'is_engineer'=>1,'status'=>['!=',9]])->sum('end_time');
  185 + if(!empty($timeCount)){
  186 + $average_time = round($timeCount / $complete_num, 3);
  187 + }
  188 + $data = [
  189 + 'manage_id'=>$item['id'],
  190 + 'manage_name'=>$item['name'],
  191 + 'add_num'=>$add_num,//上一周新增工单数量
  192 + 'average_time'=>$average_time ?? null,//平均完成工单时长
  193 + 'fastest_time'=>$fastest_time ?? null,//最快完成工单时间
  194 + 'complete_num'=>$complete_num,//完成工单数量
  195 + 'dept_id'=>$item['dept_id'],
  196 + 'timeout_ratio'=>$timeout_ratio ?? null,
  197 + 'start_at'=>$startOfLast,
  198 + 'end_at'=>$endOfLast
  199 + ];
  200 + //查询当前用户是否当日已有记录
  201 + $ticketManageInfo = $model->read(['start_at'=>$startOfLast,'end_at'=>$endOfLast,'manage_id'=>$item['id']],['id']);
  202 + if($ticketManageInfo === false){
  203 + //TODO::执行新增
  204 + $model->addReturnId($data);
  205 + }else{
  206 + //TODO::执行编辑
  207 + $model->edit($data,['id'=>$ticketManageInfo['id']]);
  208 + }
  209 + }
  210 + return true;
  211 + }
  212 +
  213 + /**
127 * @remark :按技术组统计数据 214 * @remark :按技术组统计数据
128 * @name :dept_action 215 * @name :dept_action
129 * @author :lyh 216 * @author :lyh
@@ -138,17 +225,29 @@ class TicketCount extends Command @@ -138,17 +225,29 @@ class TicketCount extends Command
138 $ticketDailyDeptModel = new TicketDailyDeptCount(); 225 $ticketDailyDeptModel = new TicketDailyDeptCount();
139 $date = Carbon::yesterday()->toDateString(); // "2025-08-07" 226 $date = Carbon::yesterday()->toDateString(); // "2025-08-07"
140 foreach ($groupList as $item){ 227 foreach ($groupList as $item){
141 - $average_time = null;  
142 - $timeout_ratio = null; 228 + $timeout_ratio = $average_time = null;
143 $this->output('组统计:执行的组/id:'.$item['name'].'/'.$item['id']); 229 $this->output('组统计:执行的组/id:'.$item['name'].'/'.$item['id']);
144 - $manageIdArr = $manageHrModel->selectField(['belong_group'=>$item['id'],'status'=>1,'dept_id'=>1],'manage_id');  
145 - $ticket_num = $ticketLogModel->counts(['engineer_id'=>['in',$manageIdArr],'is_engineer'=>1]);  
146 - $timeCount = $ticketLogModel->formatQuery(['engineer_id'=>['in',$manageIdArr],'is_engineer'=>1])->sum('end_time'); 230 + $manageIdArr = $manageHrModel->selectField(['belong_group'=>$item['id'],'status'=>['!=',9],'status'=>1,'dept_id'=>1],'manage_id');
  231 + $ticket_num = $ticketLogModel->counts(['engineer_id'=>['in',$manageIdArr],'is_engineer'=>1,'status'=>['!=',9]]);
  232 + $timeCount = $ticketLogModel->formatQuery(['engineer_id'=>['in',$manageIdArr],'status'=>['!=',9],'is_engineer'=>1])->sum('end_time');
147 if(!empty($timeCount)){ 233 if(!empty($timeCount)){
148 $average_time = round($timeCount / $ticket_num, 3); 234 $average_time = round($timeCount / $ticket_num, 3);
149 } 235 }
150 //超期工单数量 236 //超期工单数量
151 - $timeout_num = $ticketLogModel->counts(['engineer_id'=>['in',$manageIdArr],'is_engineer'=>1,'plan_end_at'=>['>',date('Y-m-d H:i:s')]]); 237 + $timeout_num = $ticketLogModel
  238 + ->whereIn('engineer_id', $manageIdArr)
  239 + ->where('is_engineer', 1)
  240 + ->where('status','!=',9)//排除掉作废工单
  241 + ->where(function ($query) {
  242 + $query->where(function ($q) {
  243 + $q->whereNotNull('end_at')
  244 + ->whereColumn('plan_end_at', '<', 'end_at');
  245 + })->orWhere(function ($q) {
  246 + $q->whereNull('end_at')
  247 + ->where('plan_end_at', '<', now());
  248 + });
  249 + })
  250 + ->count();
152 if(!empty($timeout_num)){ 251 if(!empty($timeout_num)){
153 $timeout_ratio = round($timeout_num / $ticket_num, 3); 252 $timeout_ratio = round($timeout_num / $ticket_num, 3);
154 } 253 }
@@ -172,6 +271,70 @@ class TicketCount extends Command @@ -172,6 +271,70 @@ class TicketCount extends Command
172 } 271 }
173 272
174 /** 273 /**
  274 + * @remark :技术组按周统计
  275 + * @name :dept_week_month_action
  276 + * @author :lyh
  277 + * @method :post
  278 + * @time :2025/8/30 14:01
  279 + */
  280 + public function dept_week_month_action($startOfLast,$endOfLast,$model){
  281 + $belongingGroupModel = new BelongingGroup();
  282 + $groupList = $belongingGroupModel->list(['id'=>['in',[1,2,3,4,5,6,7,8,9]]],'id',['id','name']);
  283 + $manageHrModel = new ManageHr();
  284 + $ticketLogModel = new TicketLog();
  285 + foreach ($groupList as $item){
  286 + $timeout_ratio = $average_time = null;
  287 + $this->output('组统计:执行的组/id:'.$item['name'].'/'.$item['id']);
  288 + $manageIdArr = $manageHrModel->selectField(['belong_group'=>$item['id'],'status'=>['!=',9],'status'=>1,'dept_id'=>1],'manage_id');
  289 + //本周新增工单
  290 + $add_num = $ticketLogModel->counts(['created_at'=>['between',[$startOfLast,$endOfLast]],'engineer_id'=>['in',$manageIdArr],'is_engineer'=>1,'status'=>['!=',9]]);
  291 + $complete_num = $ticketLogModel->counts(['end_at'=>['between',[$startOfLast,$endOfLast]],'engineer_id'=>['in',$manageIdArr],'is_engineer'=>1,'status'=>['!=',9]]);
  292 + $timeCount = $ticketLogModel->formatQuery(['end_at'=>['between',[$startOfLast,$endOfLast]],'engineer_id'=>['in',$manageIdArr],'status'=>['!=',9],'is_engineer'=>1])->sum('end_time');
  293 + if(!empty($timeCount)){
  294 + $average_time = round($timeCount / $complete_num, 3);
  295 + }
  296 + //超期工单数量
  297 + $timeout_num = $ticketLogModel
  298 + ->whereIn('engineer_id', $manageIdArr)
  299 + ->where('is_engineer', 1)
  300 + ->where('status','!=',9)//排除掉作废工单
  301 + ->where(function ($query) use ($startOfLast,$endOfLast) {
  302 + $query->where(function ($q) use ($startOfLast,$endOfLast) {
  303 + $q->whereBetween('plan_end_at',[$startOfLast,$endOfLast])->whereNotNull('end_at')->whereColumn('plan_end_at', '<', 'end_at');
  304 + })->orWhere(function ($q) {
  305 + $q->whereNull('end_at')->where('plan_end_at', '<', now());
  306 + });
  307 + })
  308 + ->count();
  309 + //预期结束时间在本周的所有工单
  310 + $ticket_num = $ticketLogModel->counts(['plan_end_at'=>['between',[$startOfLast,$endOfLast]],'engineer_id'=>['in',$manageIdArr],'is_engineer'=>1,'status'=>['!=',9]]);
  311 + if(!empty($timeout_num)){
  312 + $timeout_ratio = round($timeout_num / $ticket_num, 3);
  313 + }
  314 + $data = [
  315 + 'dept_id'=>$item['id'],
  316 + 'dept_name'=>$item['name'],
  317 + 'add_num'=>$add_num ?? 0,
  318 + 'complete_num'=>$complete_num ?? 0,
  319 + 'average_time'=>$average_time ?? null,
  320 + 'timeout_ratio'=>$timeout_ratio ?? null,
  321 + 'timeout_num'=>$timeout_num,
  322 + 'start_at'=>$startOfLast,
  323 + 'end_at'=>$endOfLast
  324 + ];
  325 + //查询当前用户是否当日已有记录
  326 + $ticketManageInfo = $model->read(['start_at'=>$startOfLast,'end_at'=>$endOfLast,'dept_id'=>$item['id']],['id']);
  327 + if($ticketManageInfo === false){
  328 + //TODO::执行新增
  329 + $model->addReturnId($data);
  330 + }else{
  331 + //TODO::执行编辑
  332 + $model->edit($data,['id'=>$ticketManageInfo['id']]);
  333 + }
  334 + }
  335 + return true;
  336 + }
  337 + /**
175 * @remark :技术组所有工单记录 338 * @remark :技术组所有工单记录
176 * @name :daily_action 339 * @name :daily_action
177 * @author :lyh 340 * @author :lyh
@@ -180,6 +343,10 @@ class TicketCount extends Command @@ -180,6 +343,10 @@ class TicketCount extends Command
180 */ 343 */
181 public function yesterday_daily_action(){ 344 public function yesterday_daily_action(){
182 $ticketModel = new Tickets(); 345 $ticketModel = new Tickets();
  346 + $ticketLogModel = new TicketLog();
  347 + $ticketLogModel->whereIn('ticket_id', function ($query) {
  348 + $query->select('id')->from('gl_tickets')->where('status', 9);
  349 + })->update(['status' => 9]);
183 $date = Carbon::yesterday()->toDateString(); // "2025-08-07" 350 $date = Carbon::yesterday()->toDateString(); // "2025-08-07"
184 $ticket_num = $ticketModel->counts(['id'=>['!=',0]]); 351 $ticket_num = $ticketModel->counts(['id'=>['!=',0]]);
185 $time_end_num = $ticketModel->counts(['end_at'=>['!=',null]]);//已完成的工单 352 $time_end_num = $ticketModel->counts(['end_at'=>['!=',null]]);//已完成的工单
@@ -194,12 +361,17 @@ class TicketCount extends Command @@ -194,12 +361,17 @@ class TicketCount extends Command
194 $submit_b_side = $ticketModel->formatQuery(['submit_side'=>2])->sum('submit_side'); 361 $submit_b_side = $ticketModel->formatQuery(['submit_side'=>2])->sum('submit_side');
195 $dbResult = DB::table('gl_ticket_projects as p')->leftJoin('gl_tickets as t', 'p.id', '=', 't.project_id') 362 $dbResult = DB::table('gl_ticket_projects as p')->leftJoin('gl_tickets as t', 'p.id', '=', 't.project_id')
196 ->select( 363 ->select(
197 - 'p.project_cate',  
198 - DB::raw('COUNT(t.id) as ticket_count')  
199 - )  
200 - ->groupBy('p.project_cate')  
201 - ->pluck('ticket_count', 'project_cate');  
202 - $timeout_num = $ticketModel->counts(['end_at'=>null,'plan_end_at'=>['>',date('Y-m-d H:i:s')]]); 364 + 'p.project_cate', DB::raw('COUNT(t.id) as ticket_count')
  365 + )->groupBy('p.project_cate')->pluck('ticket_count', 'project_cate');
  366 + $timeout_num = $ticketModel->where('status','!=',9)
  367 + ->where(function ($query) {
  368 + $query->where(function ($q) {
  369 + $q->whereNotNull('end_at')->whereColumn('plan_end_at', '<', 'end_at');
  370 + })->orWhere(function ($q) {
  371 + $q->whereNull('end_at')->where('plan_end_at', '<', now());
  372 + });
  373 + })
  374 + ->count();
203 $timeout_ratio = null; 375 $timeout_ratio = null;
204 if(!empty($timeout_num)){ 376 if(!empty($timeout_num)){
205 $timeout_ratio = round($timeout_num / $ticket_num, 3); 377 $timeout_ratio = round($timeout_num / $ticket_num, 3);
@@ -138,6 +138,7 @@ class ProjectUpdate extends Command @@ -138,6 +138,7 @@ class ProjectUpdate extends Command
138 //设置数据库 138 //设置数据库
139 $project = ProjectServer::useProject($project_id); 139 $project = ProjectServer::useProject($project_id);
140 if ($project) { 140 if ($project) {
  141 + $thumb_w = $project->deploy_build->thumb_w ?? 0;//缩略图压缩宽度
141 if ($api_type == 'category') { 142 if ($api_type == 'category') {
142 //产品分类 143 //产品分类
143 $url = $api_url . '?' . http_build_query(['w' => 'category']); 144 $url = $api_url . '?' . http_build_query(['w' => 'category']);
@@ -332,6 +333,12 @@ class ProjectUpdate extends Command @@ -332,6 +333,12 @@ class ProjectUpdate extends Command
332 $gallery[] = ['alt' => '', 'url' => $this->source_download($img, $project_id, $domain_arr['host'], $web_url_domain, $home_url)]; 333 $gallery[] = ['alt' => '', 'url' => $this->source_download($img, $project_id, $domain_arr['host'], $web_url_domain, $home_url)];
333 } 334 }
334 } 335 }
  336 + //缩略图
  337 + $thumb = $gallery[0] ?? [];
  338 + if (isset($thumb['url']) && $thumb['url']) {
  339 + //生成缩略图
  340 + $thumb['url'] = thumbImageByUrl($thumb['url'], $thumb_w);
  341 + }
335 //关键词 342 //关键词
336 $keyword_id = ''; 343 $keyword_id = '';
337 if ($item['tags'] ?? []) { 344 if ($item['tags'] ?? []) {
@@ -371,7 +378,7 @@ class ProjectUpdate extends Command @@ -371,7 +378,7 @@ class ProjectUpdate extends Command
371 'content' => $content, 378 'content' => $content,
372 'category_id' => $category_id, 379 'category_id' => $category_id,
373 'keyword_id' => $keyword_id, 380 'keyword_id' => $keyword_id,
374 - 'thumb' => isset($gallery[0]) ? Arr::a2s($gallery[0]) : '', 381 + 'thumb' => Arr::a2s($thumb),
375 'gallery' => Arr::a2s($gallery), 382 'gallery' => Arr::a2s($gallery),
376 'attrs' => Arr::a2s($attrs), 383 'attrs' => Arr::a2s($attrs),
377 'seo_mate' => Arr::a2s([ 384 'seo_mate' => Arr::a2s([
@@ -417,7 +424,7 @@ class ProjectUpdate extends Command @@ -417,7 +424,7 @@ class ProjectUpdate extends Command
417 'content' => $content, 424 'content' => $content,
418 'category_id' => $category_id, 425 'category_id' => $category_id,
419 'keyword_id' => $keyword_id, 426 'keyword_id' => $keyword_id,
420 - 'thumb' => isset($gallery[0]) ? Arr::a2s($gallery[0]) : '', 427 + 'thumb' => Arr::a2s($thumb),
421 'gallery' => Arr::a2s($gallery), 428 'gallery' => Arr::a2s($gallery),
422 'attrs' => Arr::a2s($attrs), 429 'attrs' => Arr::a2s($attrs),
423 'seo_mate' => Arr::a2s([ 430 'seo_mate' => Arr::a2s([
@@ -77,6 +77,9 @@ class UpdateKeyword extends Command @@ -77,6 +77,9 @@ class UpdateKeyword extends Command
77 if($updateObject['type'] == 0){//更新所有关键字 77 if($updateObject['type'] == 0){//更新所有关键字
78 //获取所有关键字的id 78 //获取所有关键字的id
79 $idArr = $keywordModel->selectField(['id'=>['>',0]],'id'); 79 $idArr = $keywordModel->selectField(['id'=>['>',0]],'id');
  80 + if(empty($idArr)){
  81 + return true;
  82 + }
80 if($info['update_method'] != 1){ 83 if($info['update_method'] != 1){
81 $idArr = shuffle($idArr); 84 $idArr = shuffle($idArr);
82 } 85 }
@@ -431,7 +431,7 @@ class WebTraffic extends Command @@ -431,7 +431,7 @@ class WebTraffic extends Command
431 $query->whereIn('ip_area', $main_countries); 431 $query->whereIn('ip_area', $main_countries);
432 } 432 }
433 if($filter_countries){ 433 if($filter_countries){
434 - $query->whereNotIn('ip_area', $main_countries); 434 + $query->whereNotIn('ip_area', $filter_countries);
435 } 435 }
436 })->inRandomOrder()->first(); 436 })->inRandomOrder()->first();
437 if(!$ipdata){ 437 if(!$ipdata){
@@ -229,27 +229,30 @@ class FetchTicketProjects extends Command @@ -229,27 +229,30 @@ class FetchTicketProjects extends Command
229 ManageHr::where('status', 1)->find($item->deploy_optimize->tech_leader)->manage_id ?? 0, 229 ManageHr::where('status', 1)->find($item->deploy_optimize->tech_leader)->manage_id ?? 0,
230 8, //张鸿飞 230 8, //张鸿飞
231 ])->first(fn($v) => $v !== null && $v !== 0, 0); 231 ])->first(fn($v) => $v !== null && $v !== 0, 0);
232 -  
233 // 优化师 232 // 优化师
234 $seom_id = ManageHr::where('status', 1)->find($item->deploy_optimize->optimist_mid) ? ManageHr::where('status', 1)->find($item->deploy_optimize->optimist_mid)->manage_id : 0; 233 $seom_id = ManageHr::where('status', 1)->find($item->deploy_optimize->optimist_mid) ? ManageHr::where('status', 1)->find($item->deploy_optimize->optimist_mid)->manage_id : 0;
235 -  
236 // 项目经理 234 // 项目经理
237 $pm_id = ManageHr::where('status', 1)->find($item->deploy_build->manager_mid)->manage_id ?? ManageHr::where('status', 1)->where('name', '李洁玉')->value('manage_id') ?? 0; 235 $pm_id = ManageHr::where('status', 1)->find($item->deploy_build->manager_mid)->manage_id ?? ManageHr::where('status', 1)->where('name', '李洁玉')->value('manage_id') ?? 0;
238 236
239 // 第一负责人 237 // 第一负责人
240 - /**  
241 - * 5.0升级6.0的项目,白帽SEO, GEO的项目 都划给售后  
242 - * 其他:建站中找项目经理,建站完成找杨长远,推广找售后服务经理 238 + /**单纯的5.0升级6.0的项目,自帽SE0,GE0的项目 部划给售后
  239 + 其他:建站中找项目经理,建站完成找杨长远,推广找售后服务经理
243 */ 240 */
244 - if ($item->is_upgrade || $item->project_type == 1 || $item->deploy_build->seo_plan > 0)  
245 - $engineer_id = $assm_id; // V5升V6,白帽SEO,GEO,找售后服务经理  
246 - elseif ($status == 1)  
247 - $engineer_id = $pm_id; // 建站中找项目经理  
248 - elseif ($status == 2)  
249 - $engineer_id = Manage::where('status', 1)->where('name', '杨长远')->value('id') ?? 0; // 建站完成找杨长远  
250 - else  
251 - $engineer_id = $assm_id; // 推广找售后服务经理  
252 - 241 + //todo::建站或同时为v6版本+GEO的,建站中项目给项目经理
  242 + if(($status == 1) && ($item->deploy_build->plan > 0) && ($item->deploy_build->seo_plan > 0)){
  243 + $engineer_id = $pm_id; //找项目经理
  244 + }else{
  245 + if ($item->is_upgrade || $item->project_type == 1 || $item->deploy_build->seo_plan > 0)
  246 + $engineer_id = $assm_id; // V5升V6,白帽SEO,GEO,找售后服务经理
  247 + elseif ($status == 1)
  248 + $engineer_id = $pm_id; // 建站中找项目经理
  249 + elseif ($status == 2)
  250 + $engineer_id = Manage::where('status', 1)->where('name', '杨长远')->value('id') ?? 0; // 建站完成找杨长远
  251 + elseif ($status == 3)
  252 + $engineer_id = $assm_id; // 推广找售后服务经理
  253 + else
  254 + $engineer_id = $pm_id; //找项目经理
  255 + }
253 $is_del = ( 256 $is_del = (
254 $item->extend_type == 5 257 $item->extend_type == 5
255 || $item->type == 8 258 || $item->type == 8
@@ -13,6 +13,7 @@ use GuzzleHttp\Client; @@ -13,6 +13,7 @@ use GuzzleHttp\Client;
13 use GuzzleHttp\Exception\GuzzleException; 13 use GuzzleHttp\Exception\GuzzleException;
14 use Illuminate\Support\Carbon; 14 use Illuminate\Support\Carbon;
15 use Illuminate\Support\Facades\Cache; 15 use Illuminate\Support\Facades\Cache;
  16 +use Illuminate\Support\Facades\Log;
16 use Illuminate\Support\Facades\Redis; 17 use Illuminate\Support\Facades\Redis;
17 18
18 define('HTTP_OPENAI_URL', 'http://openai.waimaoq.com/'); 19 define('HTTP_OPENAI_URL', 'http://openai.waimaoq.com/');
@@ -28,7 +29,7 @@ define('HTTP_OPENAI_URL', 'http://openai.waimaoq.com/'); @@ -28,7 +29,7 @@ define('HTTP_OPENAI_URL', 'http://openai.waimaoq.com/');
28 if (!function_exists('generateRoute')) { 29 if (!function_exists('generateRoute')) {
29 function generateRoute($string) 30 function generateRoute($string)
30 { 31 {
31 - if(is_array($string)){ 32 + if (is_array($string)) {
32 $string = $string[0]; 33 $string = $string[0];
33 } 34 }
34 $sign = str_replace(".", "", trim(strtolower(preg_replace('/[^\w.]+/', '-', trim($string))), '-')); 35 $sign = str_replace(".", "", trim(strtolower(preg_replace('/[^\w.]+/', '-', trim($string))), '-'));
@@ -61,7 +62,7 @@ if (!function_exists('http_post')) { @@ -61,7 +62,7 @@ if (!function_exists('http_post')) {
61 * @param type $url 62 * @param type $url
62 * @param type $post_data 63 * @param type $post_data
63 */ 64 */
64 - function http_post($url, $post_data, $header = [],$is_json = true,$timeout = 60) 65 + function http_post($url, $post_data, $header = [], $is_json = true, $timeout = 60)
65 { 66 {
66 if (empty($header)) { 67 if (empty($header)) {
67 $header = array( 68 $header = array(
@@ -87,7 +88,7 @@ if (!function_exists('http_post')) { @@ -87,7 +88,7 @@ if (!function_exists('http_post')) {
87 @file_put_contents(storage_path('logs/lyh_error.log'), var_export($error_message, true) . PHP_EOL, FILE_APPEND); 88 @file_put_contents(storage_path('logs/lyh_error.log'), var_export($error_message, true) . PHP_EOL, FILE_APPEND);
88 } 89 }
89 curl_close($ch); 90 curl_close($ch);
90 - if($is_json){ 91 + if ($is_json) {
91 return json_decode($res, true); 92 return json_decode($res, true);
92 } 93 }
93 return trim($res); 94 return trim($res);
@@ -106,7 +107,7 @@ if (!function_exists('http_get')) { @@ -106,7 +107,7 @@ if (!function_exists('http_get')) {
106 if (empty($header)) { 107 if (empty($header)) {
107 $header[] = "content-type: application/json"; 108 $header[] = "content-type: application/json";
108 } 109 }
109 - $ch1 = curl_init(); 110 + $ch1 = curl_init();
110 $timeout = 0; 111 $timeout = 0;
111 curl_setopt($ch1, CURLOPT_URL, $url); 112 curl_setopt($ch1, CURLOPT_URL, $url);
112 curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true); 113 curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
@@ -130,7 +131,7 @@ if (!function_exists('http_get')) { @@ -130,7 +131,7 @@ if (!function_exists('http_get')) {
130 131
131 132
132 if (!function_exists('curl_get')) { 133 if (!function_exists('curl_get')) {
133 - function curl_get($url,$is_array=true) 134 + function curl_get($url, $is_array = true)
134 { 135 {
135 $header = array( 136 $header = array(
136 'Expect:', 137 'Expect:',
@@ -161,7 +162,8 @@ if (!function_exists('curl_get')) { @@ -161,7 +162,8 @@ if (!function_exists('curl_get')) {
161 * @method :post 162 * @method :post
162 * @time :2024/6/5 10:38 163 * @time :2024/6/5 10:38
163 */ 164 */
164 -function contains_russian($text) { 165 +function contains_russian($text)
  166 +{
165 // 使用正则表达式检查是否包含俄语字符 167 // 使用正则表达式检查是否包含俄语字符
166 return preg_match('/[\x{0400}-\x{04FF}]/u', $text) > 0; 168 return preg_match('/[\x{0400}-\x{04FF}]/u', $text) > 0;
167 169
@@ -176,7 +178,8 @@ if (!function_exists('curl_c')) { @@ -176,7 +178,8 @@ if (!function_exists('curl_c')) {
176 * @author Akun 178 * @author Akun
177 * @date 2023/11/22 11:33 179 * @date 2023/11/22 11:33
178 */ 180 */
179 - function curl_c($url,$is_array=true,$replace=[]){ 181 + function curl_c($url, $is_array = true, $replace = [])
  182 + {
180 $header = array( 183 $header = array(
181 'Expect:', 184 'Expect:',
182 'Content-Type: application/json; charset=utf-8' 185 'Content-Type: application/json; charset=utf-8'
@@ -195,16 +198,16 @@ if (!function_exists('curl_c')) { @@ -195,16 +198,16 @@ if (!function_exists('curl_c')) {
195 curl_setopt($ch, CURLOPT_SSLVERSION, 'all'); 198 curl_setopt($ch, CURLOPT_SSLVERSION, 'all');
196 curl_setopt($ch, CURLOPT_HTTPHEADER, $header); 199 curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
197 $content = curl_exec($ch); 200 $content = curl_exec($ch);
198 - $http_code = curl_getinfo($ch,CURLINFO_HTTP_CODE); 201 + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
199 curl_close($ch); 202 curl_close($ch);
200 - if($http_code == 200){  
201 - if(!empty($replace)){  
202 - foreach ($replace as $k=>$v){  
203 - $content = str_replace($k,$v,$content); 203 + if ($http_code == 200) {
  204 + if (!empty($replace)) {
  205 + foreach ($replace as $k => $v) {
  206 + $content = str_replace($k, $v, $content);
204 } 207 }
205 } 208 }
206 return $is_array ? json_decode($content, true) : $content; 209 return $is_array ? json_decode($content, true) : $content;
207 - }else{ 210 + } else {
208 return false; 211 return false;
209 } 212 }
210 } 213 }
@@ -218,7 +221,8 @@ if (!function_exists('curl_code')) { @@ -218,7 +221,8 @@ if (!function_exists('curl_code')) {
218 * @author Akun 221 * @author Akun
219 * @date 2023/11/22 11:33 222 * @date 2023/11/22 11:33
220 */ 223 */
221 - function curl_code($url,$is_array=true){ 224 + function curl_code($url, $is_array = true)
  225 + {
222 $header = array( 226 $header = array(
223 'Expect:', 227 'Expect:',
224 'Content-Type: application/json; charset=utf-8' 228 'Content-Type: application/json; charset=utf-8'
@@ -237,11 +241,11 @@ if (!function_exists('curl_code')) { @@ -237,11 +241,11 @@ if (!function_exists('curl_code')) {
237 curl_setopt($ch, CURLOPT_SSLVERSION, 'all'); 241 curl_setopt($ch, CURLOPT_SSLVERSION, 'all');
238 curl_setopt($ch, CURLOPT_HTTPHEADER, $header); 242 curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
239 $content = curl_exec($ch); 243 $content = curl_exec($ch);
240 - $http_code = curl_getinfo($ch,CURLINFO_HTTP_CODE); 244 + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
241 curl_close($ch); 245 curl_close($ch);
242 - if($http_code == 200){ 246 + if ($http_code == 200) {
243 return $is_array ? json_decode($content, true) : $content; 247 return $is_array ? json_decode($content, true) : $content;
244 - }else{ 248 + } else {
245 return $http_code; 249 return $http_code;
246 } 250 }
247 } 251 }
@@ -260,7 +264,7 @@ if (!function_exists('_get_child')) { @@ -260,7 +264,7 @@ if (!function_exists('_get_child')) {
260 foreach ($arr as $v) { 264 foreach ($arr as $v) {
261 $v = (array)$v; 265 $v = (array)$v;
262 if ($v['pid'] == $my_id) { 266 if ($v['pid'] == $my_id) {
263 - $v['sub'] = _get_child($v['id'], $arr); 267 + $v['sub'] = _get_child($v['id'], $arr);
264 $new_arr[] = $v; 268 $new_arr[] = $v;
265 } 269 }
266 } 270 }
@@ -274,7 +278,7 @@ if (!function_exists('_get_all_sub')) { @@ -274,7 +278,7 @@ if (!function_exists('_get_all_sub')) {
274 * @param int 278 * @param int
275 * @return array 279 * @return array
276 */ 280 */
277 - function _get_all_sub($my_id,$id_Arr) 281 + function _get_all_sub($my_id, $id_Arr)
278 { 282 {
279 $new_arr[] = $my_id; 283 $new_arr[] = $my_id;
280 foreach ($id_Arr as $v) { 284 foreach ($id_Arr as $v) {
@@ -302,7 +306,7 @@ if (!function_exists('checkDomain')) { @@ -302,7 +306,7 @@ if (!function_exists('checkDomain')) {
302 if (empty($urlParts['host'])) { 306 if (empty($urlParts['host'])) {
303 $urlParts = parse_url('https://' . $value); 307 $urlParts = parse_url('https://' . $value);
304 } 308 }
305 - $host = $urlParts['host'] ?? ''; 309 + $host = $urlParts['host'] ?? '';
306 $scheme = $urlParts['scheme'] ?? 'https'; 310 $scheme = $urlParts['scheme'] ?? 'https';
307 if (!in_array($scheme, ['http', 'https'])) { 311 if (!in_array($scheme, ['http', 'https'])) {
308 return false; 312 return false;
@@ -380,7 +384,7 @@ function list_to_tree($list, $pk = 'id', $pid = 'pid', $child = '_child', $root @@ -380,7 +384,7 @@ function list_to_tree($list, $pk = 'id', $pid = 'pid', $child = '_child', $root
380 // 如果是数字,则是root 384 // 如果是数字,则是root
381 if (is_numeric($pk)) { 385 if (is_numeric($pk)) {
382 $root = $pk; 386 $root = $pk;
383 - $pk = 'id'; 387 + $pk = 'id';
384 } 388 }
385 // 创建Tree 389 // 创建Tree
386 $tree = array(); 390 $tree = array();
@@ -505,7 +509,7 @@ if (!function_exists('getPreviousDaysDate')) { @@ -505,7 +509,7 @@ if (!function_exists('getPreviousDaysDate')) {
505 $days = []; 509 $days = [];
506 while ($day > 0) { 510 while ($day > 0) {
507 $days[] = date("Y-m-d", strtotime("-{$day} days")); 511 $days[] = date("Y-m-d", strtotime("-{$day} days"));
508 - $day -= 1; 512 + $day -= 1;
509 } 513 }
510 return $days; 514 return $days;
511 } 515 }
@@ -522,7 +526,7 @@ if (!function_exists('getPreviousMonthsDate')) { @@ -522,7 +526,7 @@ if (!function_exists('getPreviousMonthsDate')) {
522 $months = []; 526 $months = [];
523 while ($month > 0) { 527 while ($month > 0) {
524 $months[] = date("Y-m", strtotime("-{$month} months")); 528 $months[] = date("Y-m", strtotime("-{$month} months"));
525 - $month -= 1; 529 + $month -= 1;
526 } 530 }
527 return $months; 531 return $months;
528 } 532 }
@@ -536,17 +540,17 @@ if (!function_exists('getInquiryInformation')) { @@ -536,17 +540,17 @@ if (!function_exists('getInquiryInformation')) {
536 */ 540 */
537 function getInquiryInformation($domain, $sta_date) 541 function getInquiryInformation($domain, $sta_date)
538 { 542 {
539 - $token = md5($domain . date("Y-m-d")); 543 + $token = md5($domain . date("Y-m-d"));
540 $source = '1,3'; 544 $source = '1,3';
541 - $url = "https://www.globalso.site/api/external-interface/country_con/15243d63ed5a5738?domain={$domain}&token={$token}&source={$source}&sta_date={$sta_date}"; 545 + $url = "https://www.globalso.site/api/external-interface/country_con/15243d63ed5a5738?domain={$domain}&token={$token}&source={$source}&sta_date={$sta_date}";
542 $client = new Client(['verify' => false]); 546 $client = new Client(['verify' => false]);
543 - $http = $client->get($url);  
544 - $data = []; 547 + $http = $client->get($url);
  548 + $data = [];
545 if ($http->getStatusCode() != 200) { 549 if ($http->getStatusCode() != 200) {
546 return $data; 550 return $data;
547 } 551 }
548 $content = $http->getBody()->getContents(); 552 $content = $http->getBody()->getContents();
549 - $json = json_decode($content, true); 553 + $json = json_decode($content, true);
550 if ($json['status'] != 200) { 554 if ($json['status'] != 200) {
551 return $content; 555 return $content;
552 } 556 }
@@ -597,7 +601,7 @@ if (!function_exists('checkIsMonth')) { @@ -597,7 +601,7 @@ if (!function_exists('checkIsMonth')) {
597 $now = time(); 601 $now = time();
598 // 获取当月的起始时间戳和结束时间戳 602 // 获取当月的起始时间戳和结束时间戳
599 $firstDay = strtotime(date('Y-m-01', $now)); 603 $firstDay = strtotime(date('Y-m-01', $now));
600 - $lastDay = strtotime(date('Y-m-t', $now)); 604 + $lastDay = strtotime(date('Y-m-t', $now));
601 // 传入日期的时间戳 605 // 传入日期的时间戳
602 $timestamp = strtotime($date); 606 $timestamp = strtotime($date);
603 // 判断传入日期是否在当月范围内 607 // 判断传入日期是否在当月范围内
@@ -621,8 +625,8 @@ if (!function_exists('getDateDays')) { @@ -621,8 +625,8 @@ if (!function_exists('getDateDays')) {
621 if (!is_null($date)) { 625 if (!is_null($date)) {
622 $dd = explode('-', $date); 626 $dd = explode('-', $date);
623 if (!checkIsGreaterMonth($date) && !checkIsMonth($date)) { 627 if (!checkIsGreaterMonth($date) && !checkIsMonth($date)) {
624 - $year = $dd[0];  
625 - $month = $dd[1]; 628 + $year = $dd[0];
  629 + $month = $dd[1];
626 $first_day_of_month = "{$year}-{$month}-01"; 630 $first_day_of_month = "{$year}-{$month}-01";
627 return getDateArray("{$year}-{$month}-" . date("t", strtotime($first_day_of_month))); 631 return getDateArray("{$year}-{$month}-" . date("t", strtotime($first_day_of_month)));
628 } 632 }
@@ -641,7 +645,7 @@ if (!function_exists('getDateArray')) { @@ -641,7 +645,7 @@ if (!function_exists('getDateArray')) {
641 function getDateArray(string $date) 645 function getDateArray(string $date)
642 { 646 {
643 list($year, $month, $day) = explode('-', date($date)); 647 list($year, $month, $day) = explode('-', date($date));
644 - $i = 1; 648 + $i = 1;
645 $days = []; 649 $days = [];
646 while ($i <= $day) { 650 while ($i <= $day) {
647 $days[] = "{$year}-{$month}-" . str_pad($i, 2, "0", STR_PAD_LEFT); 651 $days[] = "{$year}-{$month}-" . str_pad($i, 2, "0", STR_PAD_LEFT);
@@ -659,34 +663,35 @@ if (!function_exists('getImageUrl')) { @@ -659,34 +663,35 @@ if (!function_exists('getImageUrl')) {
659 * @method :post 663 * @method :post
660 * @time :2023/7/20 16:46 664 * @time :2023/7/20 16:46
661 */ 665 */
662 - function getImageUrl($path,$storage_type = 0,$location = 0,$image_cdn = 1){  
663 - if(is_array($path)){  
664 - $url =[];  
665 - foreach ($path as $v){  
666 - $url[] = getImageUrl($v,$storage_type,$location); 666 + function getImageUrl($path, $storage_type = 0, $location = 0, $image_cdn = 1)
  667 + {
  668 + if (is_array($path)) {
  669 + $url = [];
  670 + foreach ($path as $v) {
  671 + $url[] = getImageUrl($v, $storage_type, $location);
667 } 672 }
668 - }else{  
669 - if(empty($path)){ 673 + } else {
  674 + if (empty($path)) {
670 return ''; 675 return '';
671 } 676 }
672 - if((strpos($path,'https://')!== false) || (strpos($path,'http://') !== false)){  
673 - return $path; 677 + if ((strpos($path, 'https://') !== false) || (strpos($path, 'http://') !== false)) {
  678 + return $path;
674 } 679 }
675 - if(substr($path,0,2) == '//'){  
676 - return 'https:'.$path; 680 + if (substr($path, 0, 2) == '//') {
  681 + return 'https:' . $path;
677 } 682 }
678 - if($location == 0){ 683 + if ($location == 0) {
679 $cos = config('filesystems.disks.cos'); 684 $cos = config('filesystems.disks.cos');
680 - if($image_cdn == 0){//v6链接 685 + if ($image_cdn == 0) {//v6链接
681 $cosCdn = $cos['cdn2']; 686 $cosCdn = $cos['cdn2'];
682 - }else{ 687 + } else {
683 $cosCdn = ($storage_type == 0) ? $cos['cdn'] : $cos['cdn1']; 688 $cosCdn = ($storage_type == 0) ? $cos['cdn'] : $cos['cdn1'];
684 } 689 }
685 - $url = $cosCdn.$path;  
686 - }else{ 690 + $url = $cosCdn . $path;
  691 + } else {
687 $s3 = config('filesystems.disks.s3'); 692 $s3 = config('filesystems.disks.s3');
688 $cdn = $s3['cdn']; 693 $cdn = $s3['cdn'];
689 - $url = $cdn.$path; 694 + $url = $cdn . $path;
690 } 695 }
691 } 696 }
692 return $url; 697 return $url;
@@ -701,36 +706,37 @@ if (!function_exists('getFileUrl')) { @@ -701,36 +706,37 @@ if (!function_exists('getFileUrl')) {
701 * @method :post 706 * @method :post
702 * @time :2023/7/20 16:46 707 * @time :2023/7/20 16:46
703 */ 708 */
704 - function getFileUrl($path,$storage_type = 0,$location = 0,$file_cdn = 0){  
705 - if(is_array($path)){  
706 - $url =[];  
707 - foreach ($path as $v){  
708 - $url[] = getFileUrl($v,$storage_type,$location,$file_cdn); 709 + function getFileUrl($path, $storage_type = 0, $location = 0, $file_cdn = 0)
  710 + {
  711 + if (is_array($path)) {
  712 + $url = [];
  713 + foreach ($path as $v) {
  714 + $url[] = getFileUrl($v, $storage_type, $location, $file_cdn);
709 } 715 }
710 - }else{  
711 - if(empty($path)){ 716 + } else {
  717 + if (empty($path)) {
712 return ''; 718 return '';
713 } 719 }
714 - if((strpos($path,'https://')!== false) || (strpos($path,'http://') !== false)){  
715 - return $path; 720 + if ((strpos($path, 'https://') !== false) || (strpos($path, 'http://') !== false)) {
  721 + return $path;
716 } 722 }
717 - if(substr($path,0,2) == '//'){  
718 - return 'https:'.$path; 723 + if (substr($path, 0, 2) == '//') {
  724 + return 'https:' . $path;
719 } 725 }
720 $file_type = pathinfo($path, PATHINFO_EXTENSION); 726 $file_type = pathinfo($path, PATHINFO_EXTENSION);
721 $fileTypeArr = ['zip', 'pdf', 'mp4', 'doc', 'docx', 'm4v', 'xlsx']; 727 $fileTypeArr = ['zip', 'pdf', 'mp4', 'doc', 'docx', 'm4v', 'xlsx'];
722 - if(in_array(strtolower($file_type),$fileTypeArr) && ($file_cdn == 0)){ 728 + if (in_array(strtolower($file_type), $fileTypeArr) && ($file_cdn == 0)) {
723 $cdn2 = config('filesystems.disks.cos')['cdn2']; 729 $cdn2 = config('filesystems.disks.cos')['cdn2'];
724 - return $cdn2.$path; 730 + return $cdn2 . $path;
725 } 731 }
726 - if($location == 0){ 732 + if ($location == 0) {
727 $cos = config('filesystems.disks.cos'); 733 $cos = config('filesystems.disks.cos');
728 $cosCdn = ($storage_type == 0) ? $cos['cdn'] : $cos['cdn1']; 734 $cosCdn = ($storage_type == 0) ? $cos['cdn'] : $cos['cdn1'];
729 - return $cosCdn.$path;  
730 - }else{ 735 + return $cosCdn . $path;
  736 + } else {
731 $s3 = config('filesystems.disks.s3'); 737 $s3 = config('filesystems.disks.s3');
732 $cdn = $s3['cdn']; 738 $cdn = $s3['cdn'];
733 - return $cdn.$path; 739 + return $cdn . $path;
734 } 740 }
735 } 741 }
736 return $url; 742 return $url;
@@ -744,7 +750,8 @@ if (!function_exists('getFileUrl')) { @@ -744,7 +750,8 @@ if (!function_exists('getFileUrl')) {
744 * @method :post 750 * @method :post
745 * @time :2023/6/28 17:39 751 * @time :2023/6/28 17:39
746 */ 752 */
747 -function characterTruncation($string,$pattern){ 753 +function characterTruncation($string, $pattern)
  754 +{
748 preg_match($pattern, $string, $matches); 755 preg_match($pattern, $string, $matches);
749 if (isset($matches[0])) { 756 if (isset($matches[0])) {
750 $result = $matches[0]; 757 $result = $matches[0];
@@ -761,7 +768,8 @@ function characterTruncation($string,$pattern){ @@ -761,7 +768,8 @@ function characterTruncation($string,$pattern){
761 * @method :post 768 * @method :post
762 * @time :2024/5/14 16:24 769 * @time :2024/5/14 16:24
763 */ 770 */
764 -function characterTruncationStr($string,$startStr,$endStr){ 771 +function characterTruncationStr($string, $startStr, $endStr)
  772 +{
765 $start = strpos($string, $startStr); 773 $start = strpos($string, $startStr);
766 $end = strpos($string, $endStr) + strlen($endStr); 774 $end = strpos($string, $endStr) + strlen($endStr);
767 return substr($string, $start, $end - $start); 775 return substr($string, $start, $end - $start);
@@ -798,7 +806,7 @@ if (!function_exists('str_replace_url')) { @@ -798,7 +806,7 @@ if (!function_exists('str_replace_url')) {
798 $cosCdn1 = $cos['cdn1']; 806 $cosCdn1 = $cos['cdn1'];
799 $cosCdn2 = $cos['cdn2']; 807 $cosCdn2 = $cos['cdn2'];
800 $cosCdn3 = config('filesystems.disks.s3')['cdn']; 808 $cosCdn3 = config('filesystems.disks.s3')['cdn'];
801 - if($url && ((strpos($url,$cosCdn) !== false) || (strpos($url,$cosCdn1) !== false) || (strpos($url,$cosCdn2) !== false) || (strpos($url,$cosCdn3) !== false))){ 809 + if ($url && ((strpos($url, $cosCdn) !== false) || (strpos($url, $cosCdn1) !== false) || (strpos($url, $cosCdn2) !== false) || (strpos($url, $cosCdn3) !== false))) {
802 // 外部URL无需解析 810 // 外部URL无需解析
803 // 使用 parse_url 函数来解析 URL 811 // 使用 parse_url 函数来解析 URL
804 $urlParts = parse_url($url); 812 $urlParts = parse_url($url);
@@ -812,7 +820,7 @@ if (!function_exists('str_replace_url')) { @@ -812,7 +820,7 @@ if (!function_exists('str_replace_url')) {
812 } 820 }
813 } 821 }
814 822
815 -if(!function_exists('curlGet')){ 823 +if (!function_exists('curlGet')) {
816 /** 824 /**
817 * @remark :忽略证书curl请求 825 * @remark :忽略证书curl请求
818 * @name :curlGet 826 * @name :curlGet
@@ -820,8 +828,9 @@ if(!function_exists('curlGet')){ @@ -820,8 +828,9 @@ if(!function_exists('curlGet')){
820 * @method :post 828 * @method :post
821 * @time :2023/9/12 10:10 829 * @time :2023/9/12 10:10
822 */ 830 */
823 - function curlGet($url){  
824 - $ch1 = curl_init(); 831 + function curlGet($url)
  832 + {
  833 + $ch1 = curl_init();
825 $timeout = 60; 834 $timeout = 60;
826 curl_setopt($ch1, CURLOPT_URL, $url); 835 curl_setopt($ch1, CURLOPT_URL, $url);
827 curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true); 836 curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
@@ -852,15 +861,15 @@ function ends_with($string, $suffix) @@ -852,15 +861,15 @@ function ends_with($string, $suffix)
852 * @method :post 861 * @method :post
853 * @time :2025/6/21 9:57 862 * @time :2025/6/21 9:57
854 */ 863 */
855 -function getCustomRouteMap($module_route,$route = '',$is_upgrade = 0) 864 +function getCustomRouteMap($module_route, $route = '', $is_upgrade = 0)
856 { 865 {
857 - if($is_upgrade == 0){ 866 + if ($is_upgrade == 0) {
858 return $route; 867 return $route;
859 } 868 }
860 - if($module_route == $route){ 869 + if ($module_route == $route) {
861 $resultRoute = $route; 870 $resultRoute = $route;
862 - }else{  
863 - $resultRoute = $module_route.'_catalog/'.$route; 871 + } else {
  872 + $resultRoute = $module_route . '_catalog/' . $route;
864 } 873 }
865 return $resultRoute; 874 return $resultRoute;
866 } 875 }
@@ -872,48 +881,53 @@ function getCustomRouteMap($module_route,$route = '',$is_upgrade = 0) @@ -872,48 +881,53 @@ function getCustomRouteMap($module_route,$route = '',$is_upgrade = 0)
872 * @method :post 881 * @method :post
873 * @time :2023/11/10 14:29 882 * @time :2023/11/10 14:29
874 */ 883 */
875 -function getRouteMap($source,$source_id,$is_upgrade = 0, $returnModel = false){ 884 +function getRouteMap($source, $source_id, $is_upgrade = 0, $returnModel = false)
  885 +{
876 $route = ''; 886 $route = '';
877 $routeMapModel = new RouteMap(); 887 $routeMapModel = new RouteMap();
878 - $info = $routeMapModel->read(['source'=>$source,'source_id'=>$source_id]);  
879 - if($info !== false){  
880 - if($is_upgrade == 1){  
881 - if($source == $routeMapModel::SOURCE_NEWS_CATE){  
882 - if($info['route'] != 'news'){  
883 - return $routeMapModel::PATH_NEWS_CATE.'/'.$info['route']; 888 + $info = $routeMapModel->read(['source' => $source, 'source_id' => $source_id]);
  889 + if ($info !== false) {
  890 + if ($is_upgrade == 1) {
  891 + if ($source == $routeMapModel::SOURCE_NEWS_CATE) {
  892 + if ($info['route'] != 'news') {
  893 + return $routeMapModel::PATH_NEWS_CATE . '/' . $info['route'];
884 } 894 }
885 - }elseif ($source == $routeMapModel::SOURCE_BLOG_CATE){  
886 - if($info['route'] != 'blog'){  
887 - return $routeMapModel::PATH_BLOG_CATE.'/'.$info['route']; 895 + } elseif ($source == $routeMapModel::SOURCE_BLOG_CATE) {
  896 + if ($info['route'] != 'blog') {
  897 + return $routeMapModel::PATH_BLOG_CATE . '/' . $info['route'];
888 } 898 }
889 } 899 }
890 $route = $info['route']; 900 $route = $info['route'];
891 return $route; 901 return $route;
892 } 902 }
893 - if(!empty($info['path'])){  
894 - if($info['path'] == 'blog'){  
895 - $info['path'] = $info['path'].'s'; 903 + if (!empty($info['path'])) {
  904 + if ($info['path'] == 'blog') {
  905 + $info['path'] = $info['path'] . 's';
896 } 906 }
897 - $route = $info['path'].'/'.$info['route'];  
898 - }else{ 907 + $route = $info['path'] . '/' . $info['route'];
  908 + } else {
899 $route = $info['route']; 909 $route = $info['route'];
900 } 910 }
901 } 911 }
902 - if($returnModel && ($info !== false)){ 912 + if ($returnModel && ($info !== false)) {
903 return $info; 913 return $info;
904 } 914 }
905 return $route; 915 return $route;
906 } 916 }
907 917
908 -function redis_get($key){ 918 +function redis_get($key)
  919 +{
909 return Redis::connection()->client()->get($key); 920 return Redis::connection()->client()->get($key);
910 } 921 }
911 -function redis_del(...$key){ 922 +
  923 +function redis_del(...$key)
  924 +{
912 return Redis::connection()->client()->del(...$key); 925 return Redis::connection()->client()->del(...$key);
913 } 926 }
914 927
915 -function redis_set($key,$val,$ttl=3600){  
916 - return Redis::connection()->client()->set($key,$val,$ttl); 928 +function redis_set($key, $val, $ttl = 3600)
  929 +{
  930 + return Redis::connection()->client()->set($key, $val, $ttl);
917 } 931 }
918 932
919 /** 933 /**
@@ -925,7 +939,8 @@ function redis_set($key,$val,$ttl=3600){ @@ -925,7 +939,8 @@ function redis_set($key,$val,$ttl=3600){
925 * @author:dc 939 * @author:dc
926 * @time 2023/10/25 9:48 940 * @time 2023/10/25 9:48
927 */ 941 */
928 -function redis_add($key,$val,$ttl=3600){ 942 +function redis_add($key, $val, $ttl = 3600)
  943 +{
929 return Redis::connection()->client()->eval( 944 return Redis::connection()->client()->eval(
930 "return redis.call('exists',KEYS[1])<1 and redis.call('setex',KEYS[1],ARGV[2],ARGV[1])", [$key, $val, $ttl], 1 945 "return redis.call('exists',KEYS[1])<1 and redis.call('setex',KEYS[1],ARGV[2],ARGV[1])", [$key, $val, $ttl], 1
931 ); 946 );
@@ -937,11 +952,12 @@ function redis_add($key,$val,$ttl=3600){ @@ -937,11 +952,12 @@ function redis_add($key,$val,$ttl=3600){
937 * @param $project_id 952 * @param $project_id
938 * @param $domain 953 * @param $domain
939 * @param $is_complete 954 * @param $is_complete
940 - * @author Akun  
941 * @return bool 955 * @return bool
942 * @date 2023/12/08 14:17 956 * @date 2023/12/08 14:17
  957 + * @author Akun
943 */ 958 */
944 -function check_remote_url_down($url,$project_id,$domain,$is_complete=0){ 959 +function check_remote_url_down($url, $project_id, $domain, $is_complete = 0)
  960 +{
945 961
946 if (!$url) { 962 if (!$url) {
947 return ''; 963 return '';
@@ -950,24 +966,24 @@ function check_remote_url_down($url,$project_id,$domain,$is_complete=0){ @@ -950,24 +966,24 @@ function check_remote_url_down($url,$project_id,$domain,$is_complete=0){
950 $arr = parse_url($url); 966 $arr = parse_url($url);
951 $scheme = $arr['scheme'] ?? ''; 967 $scheme = $arr['scheme'] ?? '';
952 $host = $arr['host'] ?? ''; 968 $host = $arr['host'] ?? '';
953 - $host_arr = explode('.',$host); 969 + $host_arr = explode('.', $host);
954 $path = $arr['path'] ?? ''; 970 $path = $arr['path'] ?? '';
955 971
956 - if(strpos($host_arr[0], 'cdn') !== false){ 972 + if (strpos($host_arr[0], 'cdn') !== false) {
957 return $url; 973 return $url;
958 } 974 }
959 975
960 - if($host_arr[0] == 'file' && $host_arr[1] == 'globalso'){ 976 + if ($host_arr[0] == 'file' && $host_arr[1] == 'globalso') {
961 return $url; 977 return $url;
962 } 978 }
963 979
964 //475项目特殊处理 980 //475项目特殊处理
965 - if($project_id == 475 && $host == 'www.ebuyplc.com'){ 981 + if ($project_id == 475 && $host == 'www.ebuyplc.com') {
966 $host = 'g934.goodao.net'; 982 $host = 'g934.goodao.net';
967 } 983 }
968 984
969 - if($path && substr($path,0,1) != '/'){  
970 - $path = '/'.$path; 985 + if ($path && substr($path, 0, 1) != '/') {
  986 + $path = '/' . $path;
971 } 987 }
972 988
973 if ( 989 if (
@@ -978,13 +994,13 @@ function check_remote_url_down($url,$project_id,$domain,$is_complete=0){ @@ -978,13 +994,13 @@ function check_remote_url_down($url,$project_id,$domain,$is_complete=0){
978 ) { 994 ) {
979 $url_complete = ($scheme ?: 'https') . '://' . ($host ?: $domain) . $path; 995 $url_complete = ($scheme ?: 'https') . '://' . ($host ?: $domain) . $path;
980 996
981 - $new_url = CosService::uploadRemote($project_id,'image_product',$url_complete);  
982 - if($new_url){ 997 + $new_url = CosService::uploadRemote($project_id, 'image_product', $url_complete);
  998 + if ($new_url) {
983 return $is_complete ? getImageUrl($new_url) : $new_url; 999 return $is_complete ? getImageUrl($new_url) : $new_url;
984 - }else{ 1000 + } else {
985 return false; 1001 return false;
986 } 1002 }
987 - }else{ 1003 + } else {
988 return false; 1004 return false;
989 } 1005 }
990 } 1006 }
@@ -994,10 +1010,11 @@ function check_remote_url_down($url,$project_id,$domain,$is_complete=0){ @@ -994,10 +1010,11 @@ function check_remote_url_down($url,$project_id,$domain,$is_complete=0){
994 * @author zbj 1010 * @author zbj
995 * @date 2024/3/29 1011 * @date 2024/3/29
996 */ 1012 */
997 -function textareaToArr($content, $separator = ','){  
998 - return array_values(array_filter(array_unique(array_map(function ($v){ 1013 +function textareaToArr($content, $separator = ',')
  1014 +{
  1015 + return array_values(array_filter(array_unique(array_map(function ($v) {
999 return trim($v); 1016 return trim($v);
1000 - },explode($separator, $content))))); 1017 + }, explode($separator, $content)))));
1001 } 1018 }
1002 1019
1003 /** 1020 /**
@@ -1007,7 +1024,8 @@ function textareaToArr($content, $separator = ','){ @@ -1007,7 +1024,8 @@ function textareaToArr($content, $separator = ','){
1007 * @method :post 1024 * @method :post
1008 * @time :2024/6/26 10:46 1025 * @time :2024/6/26 10:46
1009 */ 1026 */
1010 -function ip_to_unique_string($ip) { 1027 +function ip_to_unique_string($ip)
  1028 +{
1011 // 将IP地址转换为数值表示 1029 // 将IP地址转换为数值表示
1012 $ip_number = ip2long($ip); 1030 $ip_number = ip2long($ip);
1013 // 使用哈希函数生成唯一数值 1031 // 使用哈希函数生成唯一数值
@@ -1021,7 +1039,8 @@ function ip_to_unique_string($ip) { @@ -1021,7 +1039,8 @@ function ip_to_unique_string($ip) {
1021 return strtolower($unique_string); 1039 return strtolower($unique_string);
1022 } 1040 }
1023 1041
1024 -function base62_encode($num) { 1042 +function base62_encode($num)
  1043 +{
1025 $characters = '23456789abcdefghijkmnpqrstuvwxyz'; 1044 $characters = '23456789abcdefghijkmnpqrstuvwxyz';
1026 $base = strlen($characters); 1045 $base = strlen($characters);
1027 $result = ''; 1046 $result = '';
@@ -1039,8 +1058,9 @@ function base62_encode($num) { @@ -1039,8 +1058,9 @@ function base62_encode($num) {
1039 * @method :post 1058 * @method :post
1040 * @time :2024/8/19 14:21 1059 * @time :2024/8/19 14:21
1041 */ 1060 */
1042 -function urlSafeBase64Encode($data = '') {  
1043 - if(empty($data)){ 1061 +function urlSafeBase64Encode($data = '')
  1062 +{
  1063 + if (empty($data)) {
1044 return $data; 1064 return $data;
1045 } 1065 }
1046 // 1. 使用标准的 BASE64 编码 1066 // 1. 使用标准的 BASE64 编码
@@ -1061,7 +1081,8 @@ function urlSafeBase64Encode($data = '') { @@ -1061,7 +1081,8 @@ function urlSafeBase64Encode($data = '') {
1061 * @method :post 1081 * @method :post
1062 * @time :2024/9/14 16:45 1082 * @time :2024/9/14 16:45
1063 */ 1083 */
1064 -function generateRandomString($length) { 1084 +function generateRandomString($length)
  1085 +{
1065 return substr(str_shuffle(str_repeat($x = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length / strlen($x)))), 1, $length); 1086 return substr(str_shuffle(str_repeat($x = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length / strlen($x)))), 1, $length);
1066 } 1087 }
1067 1088
@@ -1080,26 +1101,26 @@ if (!function_exists('check_domain_record')) { @@ -1080,26 +1101,26 @@ if (!function_exists('check_domain_record')) {
1080 $is_record = false; 1101 $is_record = false;
1081 1102
1082 //获取域名解析记录 1103 //获取域名解析记录
1083 - $records = dns_get_record($domain,DNS_A);  
1084 - if(count($records) == 1 && ($records[0]['host'] == $server_info['domain'] || $records[0]['ip'] == $server_info['ip'])){ 1104 + $records = dns_get_record($domain, DNS_A);
  1105 + if (count($records) == 1 && ($records[0]['host'] == $server_info['domain'] || $records[0]['ip'] == $server_info['ip'])) {
1085 $is_record = true; 1106 $is_record = true;
1086 } 1107 }
1087 1108
1088 - if(!$is_record){ 1109 + if (!$is_record) {
1089 //解析不正确,再判断是否开启cnd 1110 //解析不正确,再判断是否开启cnd
1090 $top_domain = getTopDomain($domain); 1111 $top_domain = getTopDomain($domain);
1091 $cnd = curlGet('http://sitebak.globalso.com/get_records?domain=' . $top_domain); 1112 $cnd = curlGet('http://sitebak.globalso.com/get_records?domain=' . $top_domain);
1092 if (isset($cnd['data']) && $cnd['data']) { 1113 if (isset($cnd['data']) && $cnd['data']) {
1093 - if($domain == $top_domain || substr($domain,0,4) == 'www.'){ 1114 + if ($domain == $top_domain || substr($domain, 0, 4) == 'www.') {
1094 $check_domain = $domain; 1115 $check_domain = $domain;
1095 - }else{  
1096 - $check_domain = '*.'.$top_domain; 1116 + } else {
  1117 + $check_domain = '*.' . $top_domain;
1097 } 1118 }
1098 foreach ($cnd['data'] as $vc) { 1119 foreach ($cnd['data'] as $vc) {
1099 if ($vc['name'] == $check_domain && $vc['type'] == 'A' && $vc['content'] == $server_info['ip']) { 1120 if ($vc['name'] == $check_domain && $vc['type'] == 'A' && $vc['content'] == $server_info['ip']) {
1100 $is_record = true; 1121 $is_record = true;
1101 break; 1122 break;
1102 - }elseif ($vc['name'] == $check_domain && $vc['type'] == 'CNAME' && $vc['content'] == $server_info['domain']){ 1123 + } elseif ($vc['name'] == $check_domain && $vc['type'] == 'CNAME' && $vc['content'] == $server_info['domain']) {
1103 $is_record = true; 1124 $is_record = true;
1104 break; 1125 break;
1105 } 1126 }
@@ -1108,8 +1129,8 @@ if (!function_exists('check_domain_record')) { @@ -1108,8 +1129,8 @@ if (!function_exists('check_domain_record')) {
1108 } 1129 }
1109 1130
1110 return $is_record; 1131 return $is_record;
1111 - }catch (\Exception $e){  
1112 - errorLog('dns_get_record',['domain'=>$domain],$e); 1132 + } catch (\Exception $e) {
  1133 + errorLog('dns_get_record', ['domain' => $domain], $e);
1113 return false; 1134 return false;
1114 } 1135 }
1115 } 1136 }
@@ -1123,7 +1144,8 @@ if (!function_exists('check_curl_status')) { @@ -1123,7 +1144,8 @@ if (!function_exists('check_curl_status')) {
1123 * @author Akun 1144 * @author Akun
1124 * @date 2024/12/12 15:52 1145 * @date 2024/12/12 15:52
1125 */ 1146 */
1126 - function check_curl_status($url){ 1147 + function check_curl_status($url)
  1148 + {
1127 $header = array( 1149 $header = array(
1128 'Expect:', 1150 'Expect:',
1129 'Content-Type: application/json; charset=utf-8' 1151 'Content-Type: application/json; charset=utf-8'
@@ -1142,7 +1164,7 @@ if (!function_exists('check_curl_status')) { @@ -1142,7 +1164,7 @@ if (!function_exists('check_curl_status')) {
1142 curl_setopt($ch, CURLOPT_SSLVERSION, 'all'); 1164 curl_setopt($ch, CURLOPT_SSLVERSION, 'all');
1143 curl_setopt($ch, CURLOPT_HTTPHEADER, $header); 1165 curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
1144 curl_exec($ch); 1166 curl_exec($ch);
1145 - $http_code = curl_getinfo($ch,CURLINFO_HTTP_CODE); 1167 + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
1146 curl_close($ch); 1168 curl_close($ch);
1147 1169
1148 return $http_code; 1170 return $http_code;
@@ -1154,7 +1176,8 @@ if (!function_exists('check_curl_status')) { @@ -1154,7 +1176,8 @@ if (!function_exists('check_curl_status')) {
1154 * @author zbj 1176 * @author zbj
1155 * @date 2024/10/25 1177 * @date 2024/10/25
1156 */ 1178 */
1157 -function email_desensitize($email){ 1179 +function email_desensitize($email)
  1180 +{
1158 $parts = explode('@', $email); 1181 $parts = explode('@', $email);
1159 $username = $parts[0] ?? ''; 1182 $username = $parts[0] ?? '';
1160 $domain = $parts[1] ?? ''; 1183 $domain = $parts[1] ?? '';
@@ -1168,7 +1191,8 @@ function email_desensitize($email){ @@ -1168,7 +1191,8 @@ function email_desensitize($email){
1168 * @author zbj 1191 * @author zbj
1169 * @date 2024/10/25 1192 * @date 2024/10/25
1170 */ 1193 */
1171 -function getRandByRatio($proArr){ 1194 +function getRandByRatio($proArr)
  1195 +{
1172 $result = ''; 1196 $result = '';
1173 $proSum = array_sum($proArr); 1197 $proSum = array_sum($proArr);
1174 foreach ($proArr as $key => $proCur) { 1198 foreach ($proArr as $key => $proCur) {
@@ -1206,7 +1230,7 @@ function getPrefixKeyword($project_id, $type, $num) @@ -1206,7 +1230,7 @@ function getPrefixKeyword($project_id, $type, $num)
1206 $keyword = array_slice($fix_keyword, 0, $num); 1230 $keyword = array_slice($fix_keyword, 0, $num);
1207 $str = implode(", ", $keyword); 1231 $str = implode(", ", $keyword);
1208 1232
1209 - foreach ($keyword as $k=>$v){ 1233 + foreach ($keyword as $k => $v) {
1210 $tmp = rtrim($v, 's'); 1234 $tmp = rtrim($v, 's');
1211 if (substr_count($str, $tmp) > 1) { 1235 if (substr_count($str, $tmp) > 1) {
1212 unset($keyword[$k]); 1236 unset($keyword[$k]);
@@ -1224,16 +1248,17 @@ function getPrefixKeyword($project_id, $type, $num) @@ -1224,16 +1248,17 @@ function getPrefixKeyword($project_id, $type, $num)
1224 * @method :post 1248 * @method :post
1225 * @time :2025/2/11 14:58 1249 * @time :2025/2/11 14:58
1226 */ 1250 */
1227 -function getDeployOptimize($project_id){ 1251 +function getDeployOptimize($project_id)
  1252 +{
1228 $cache_key = 'project_deploy_optimize_info_' . $project_id; 1253 $cache_key = 'project_deploy_optimize_info_' . $project_id;
1229 $info = Cache::get($cache_key); 1254 $info = Cache::get($cache_key);
1230 - if(!$info){ 1255 + if (!$info) {
1231 $projectOptimizeModel = new DeployOptimize(); 1256 $projectOptimizeModel = new DeployOptimize();
1232 $info = $projectOptimizeModel->read(['project_id' => $project_id], ['id', 'company_en_name', 'company_en_description', 'keyword_prefix', 'keyword_suffix', 'special']); 1257 $info = $projectOptimizeModel->read(['project_id' => $project_id], ['id', 'company_en_name', 'company_en_description', 'keyword_prefix', 'keyword_suffix', 'special']);
1233 $projectKeywordModel = new ProjectKeyword(); 1258 $projectKeywordModel = new ProjectKeyword();
1234 - $keywordInfo = $projectKeywordModel->read(['project_id'=>$project_id]); 1259 + $keywordInfo = $projectKeywordModel->read(['project_id' => $project_id]);
1235 $info['main_keyword'] = ''; 1260 $info['main_keyword'] = '';
1236 - if(!empty($keywordInfo['main_keyword'])){ 1261 + if (!empty($keywordInfo['main_keyword'])) {
1237 $info['main_keyword'] = $keywordInfo['main_keyword']; 1262 $info['main_keyword'] = $keywordInfo['main_keyword'];
1238 } 1263 }
1239 Cache::put($cache_key, $info, 600); 1264 Cache::put($cache_key, $info, 600);
@@ -1249,7 +1274,8 @@ function getDeployOptimize($project_id){ @@ -1249,7 +1274,8 @@ function getDeployOptimize($project_id){
1249 * @method :post 1274 * @method :post
1250 * @time :2025/4/1 9:41 1275 * @time :2025/4/1 9:41
1251 */ 1276 */
1252 -function paginateArray($array, $page = 1, $pageSize = 20) { 1277 +function paginateArray($array, $page = 1, $pageSize = 20)
  1278 +{
1253 $totalItems = count($array); 1279 $totalItems = count($array);
1254 $totalPages = ceil($totalItems / $pageSize); 1280 $totalPages = ceil($totalItems / $pageSize);
1255 // 确保页码有效 1281 // 确保页码有效
@@ -1272,8 +1298,9 @@ function paginateArray($array, $page = 1, $pageSize = 20) { @@ -1272,8 +1298,9 @@ function paginateArray($array, $page = 1, $pageSize = 20) {
1272 * @method :post 1298 * @method :post
1273 * @time :2025/4/3 16:19 1299 * @time :2025/4/3 16:19
1274 */ 1300 */
1275 -function getDomain($url) {  
1276 - if(empty($url)){ 1301 +function getDomain($url)
  1302 +{
  1303 + if (empty($url)) {
1277 return $url; 1304 return $url;
1278 } 1305 }
1279 $parsedUrl = parse_url($url); 1306 $parsedUrl = parse_url($url);
@@ -1394,12 +1421,13 @@ function analysisRoute($pathInfo) @@ -1394,12 +1421,13 @@ function analysisRoute($pathInfo)
1394 return $router; 1421 return $router;
1395 } 1422 }
1396 1423
1397 -function getTopDomain ($url) { 1424 +function getTopDomain($url)
  1425 +{
1398 $url = strtolower($url); //首先转成小写 1426 $url = strtolower($url); //首先转成小写
1399 $url = mb_ereg_replace('^( | )+', '', trim($url)); 1427 $url = mb_ereg_replace('^( | )+', '', trim($url));
1400 $url = mb_ereg_replace('( | )+$', '', $url); 1428 $url = mb_ereg_replace('( | )+$', '', $url);
1401 if (!preg_match('/^(http:\/\/|https)/', $url)) { 1429 if (!preg_match('/^(http:\/\/|https)/', $url)) {
1402 - $url = "https://".$url; 1430 + $url = "https://" . $url;
1403 } 1431 }
1404 $hosts = parse_url($url); 1432 $hosts = parse_url($url);
1405 $host = $hosts['host'] ?? ''; 1433 $host = $hosts['host'] ?? '';
@@ -1413,10 +1441,10 @@ function getTopDomain ($url) { @@ -1413,10 +1441,10 @@ function getTopDomain ($url) {
1413 $preg = '/[\w].+\.(com|net|org|gov|edu|co|ne)\.[\w]/'; 1441 $preg = '/[\w].+\.(com|net|org|gov|edu|co|ne)\.[\w]/';
1414 if (($n > 2) && preg_match($preg, $host)) { 1442 if (($n > 2) && preg_match($preg, $host)) {
1415 //双后缀取后3位 1443 //双后缀取后3位
1416 - $host = $data[$n - 3].'.'.$data[$n - 2].'.'.$data[$n - 1]; 1444 + $host = $data[$n - 3] . '.' . $data[$n - 2] . '.' . $data[$n - 1];
1417 } else { 1445 } else {
1418 //非双后缀取后两位 1446 //非双后缀取后两位
1419 - $host = $data[$n - 2].'.'.$data[$n - 1]; 1447 + $host = $data[$n - 2] . '.' . $data[$n - 1];
1420 } 1448 }
1421 return $host; 1449 return $host;
1422 } 1450 }
@@ -1440,3 +1468,126 @@ function diffInHours($startTime, $endTime) @@ -1440,3 +1468,126 @@ function diffInHours($startTime, $endTime)
1440 return round($hours, 1); 1468 return round($hours, 1);
1441 } 1469 }
1442 1470
  1471 +/**
  1472 + * 通过图片地址压缩图片
  1473 + * @param $url
  1474 + * @param int $width
  1475 + * @return string
  1476 + * @author Akun
  1477 + * @date 2025/09/01 15:18
  1478 + */
  1479 +function thumbImageByUrl($url, $width = 360)
  1480 +{
  1481 + if (empty($url)) {
  1482 + return $url;
  1483 + }
  1484 +
  1485 + if ($width == 0) {
  1486 + return $url;
  1487 + }
  1488 +
  1489 + if (strpos($url, '_thumb') !== false) {
  1490 + return $url;
  1491 + }
  1492 +
  1493 + //获取图片完整访问地址
  1494 + $url_complete = getImageUrl($url, 0, 0, 0);//先用v6-file地址
  1495 + if (strpos($url_complete, 'v6-file') !== false) {
  1496 + $is_exists = checkRemoteFileExists($url_complete);
  1497 + if (!$is_exists) {
  1498 + //不存在,再用cdn地址
  1499 + $url_complete = getImageUrl($url);
  1500 + }
  1501 + }
  1502 +
  1503 + //获取与原图存储路径相同的压缩路径
  1504 + $path = parse_url($url_complete, PHP_URL_PATH);
  1505 + $path_arr = explode('.', $path);
  1506 + if (count($path_arr) != 2) {
  1507 + return $url;
  1508 + }
  1509 + $path_arr[0] = $path_arr[0] . '_thumbW' . $width;
  1510 + $key = implode('.', $path_arr);
  1511 +
  1512 + try {
  1513 + $img = \Intervention\Image\Facades\Image::make($url_complete);
  1514 +
  1515 + //宽度按设定,高度自动调整
  1516 + $img->resize($width, null, function ($constraint) {
  1517 + $constraint->aspectRatio();
  1518 + $constraint->upsize();
  1519 + });
  1520 +
  1521 + //获取处理后的图片二进制资源
  1522 + $resource = $img->stream()->__toString();
  1523 +
  1524 + //上传存储桶
  1525 + $thumb_url = CosService::uploadRemote('', '', '', $key, $resource);
  1526 +
  1527 + $url = $thumb_url ? $thumb_url : $url;
  1528 + } catch (\Exception $e) {
  1529 + Log::channel('thumb_img')->error($e->getMessage(), [$url, $width]);
  1530 + }
  1531 +
  1532 + return $url;
  1533 +}
  1534 +
  1535 +if (!function_exists('checkRemoteFileExists')) {
  1536 + /**
  1537 + * 判断远程文件是否存在
  1538 + * @param $url
  1539 + * @return bool
  1540 + * @author Akun
  1541 + * @date 2025/09/05 9:58
  1542 + */
  1543 + function checkRemoteFileExists($url)
  1544 + {
  1545 + $curl = curl_init($url);
  1546 + curl_setopt($curl, CURLOPT_NOBODY, true);
  1547 + $result = curl_exec($curl);
  1548 + $found = false;
  1549 + if ($result !== false) {
  1550 + $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
  1551 + if ($statusCode == 200) {
  1552 + $found = true;
  1553 + }
  1554 + }
  1555 + curl_close($curl);
  1556 +
  1557 + return $found;
  1558 + }
  1559 +}
  1560 +
  1561 +if (!function_exists('httpGetSsl')) {
  1562 + /**
  1563 + * 获取通配符证书
  1564 + * @param $domain
  1565 + * @return mixed
  1566 + * @author Akun
  1567 + * @date 2025/04/21 16:51
  1568 + */
  1569 + function httpGetSsl($domain)
  1570 + {
  1571 + $header = array(
  1572 + "Accept:application/json",
  1573 + "Content-Type:application/json;charset=utf-8",
  1574 + "X-CmerApi-Host:" . env('GET_SSL_HOST'),
  1575 + "Apikey:" . env('GET_SSL_KEY'),
  1576 + );
  1577 + $ch = curl_init();
  1578 + curl_setopt($ch, CURLOPT_URL, env('GET_SSL_URL') . '?domain=' . $domain);
  1579 + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
  1580 + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  1581 + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  1582 + curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
  1583 + curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
  1584 + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  1585 + curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
  1586 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  1587 + $res = curl_exec($ch);
  1588 + curl_close($ch);
  1589 +
  1590 + $result = json_decode($res, true);
  1591 + return is_array($result) ? $result : $res;
  1592 + }
  1593 +}
@@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
3 namespace App\Http\Controllers\Api; 3 namespace App\Http\Controllers\Api;
4 4
5 use App\Exceptions\InquiryFilterException; 5 use App\Exceptions\InquiryFilterException;
  6 +use App\Models\Domain\DomainInfo;
6 use App\Models\Mail\Mail; 7 use App\Models\Mail\Mail;
7 use App\Models\Project\DeployBuild; 8 use App\Models\Project\DeployBuild;
8 use App\Models\Project\Project; 9 use App\Models\Project\Project;
@@ -69,7 +70,7 @@ class SelfSiteController extends BaseController @@ -69,7 +70,7 @@ class SelfSiteController extends BaseController
69 $cos = new CosService(); 70 $cos = new CosService();
70 $fileName = uniqid() . rand(10000, 99999) . '.' . $file['ext']; 71 $fileName = uniqid() . rand(10000, 99999) . '.' . $file['ext'];
71 $file_data = base64_decode($file['data']); 72 $file_data = base64_decode($file['data']);
72 - $path = $cos->uploadFile($file_data, '/inquiry/' . date('Ymd'), $fileName,true); 73 + $path = $cos->uploadFile($file_data, '/inquiry/' . date('Ymd'), $fileName, true);
73 $data[$key] = [ 74 $data[$key] = [
74 'path' => $path, 75 'path' => $path,
75 'original_name' => $file['name'], 76 'original_name' => $file['name'],
@@ -191,4 +192,50 @@ class SelfSiteController extends BaseController @@ -191,4 +192,50 @@ class SelfSiteController extends BaseController
191 192
192 return $this->success([]); 193 return $this->success([]);
193 } 194 }
  195 +
  196 + /**
  197 + * 自建站获取通配符证书接口
  198 + * @param Request $request
  199 + * @return false|string
  200 + * @author Akun
  201 + * @date 2025/09/02 16:55
  202 + */
  203 + public function selfSiteSsl(Request $request)
  204 + {
  205 + $token = $request->header('token');//token
  206 + $pid = $request->header('pid');//项目id
  207 +
  208 + if (empty($token) || empty($pid)) {
  209 + return $this->error('token无效', 401);
  210 + }
  211 +
  212 + //判断token是否有效
  213 + $project_model = new Project();
  214 + $project_info = $project_model->read(['id' => $pid, 'site_token' => $token]);
  215 + if (!$project_info) {
  216 + return $this->error('token无效', 401);
  217 + }
  218 +
  219 + //获取域名信息
  220 + $domain_model = new DomainInfo();
  221 + $domain_info = $domain_model->read(['project_id' => $pid]);
  222 + if (!$domain_info) {
  223 + return $this->error('获取域名失败', 401);
  224 + }
  225 +
  226 + //获取通配符证书
  227 + $top_domain = getTopDomain($domain_info['domain']);
  228 + $ssl_re = httpGetSsl($top_domain);
  229 + $return = [
  230 + 'ssl_key' => '',
  231 + 'ssl_cert' => ''
  232 + ];
  233 + if (isset($ssl_re['status']) && $ssl_re['status'] == 2) {
  234 + //获取成功
  235 + $return['ssl_key'] = $ssl_re['ssl_key'];
  236 + $return['ssl_cert'] = $ssl_re['ssl_cert'];
  237 + }
  238 +
  239 + return $this->success($return);
  240 + }
194 } 241 }
@@ -2,9 +2,15 @@ @@ -2,9 +2,15 @@
2 2
3 namespace App\Http\Controllers\Api\WorkOrder; 3 namespace App\Http\Controllers\Api\WorkOrder;
4 4
  5 +use App\Enums\Common\Code;
5 use App\Http\Controllers\Api\BaseController; 6 use App\Http\Controllers\Api\BaseController;
  7 +use App\Http\Logic\Aside\Project\ProcessRecordsLogic;
  8 +use App\Http\Logic\Aside\Project\ProjectLogic;
6 use App\Http\Requests\Api\WorkOrder\TicketListRequest; 9 use App\Http\Requests\Api\WorkOrder\TicketListRequest;
7 use App\Http\Requests\Api\WorkOrder\TicketStoreRequest; 10 use App\Http\Requests\Api\WorkOrder\TicketStoreRequest;
  11 +use App\Models\Project\ProcessRecords;
  12 +use App\Models\Project\Project;
  13 +use App\Models\ProjectAssociation\ProjectAssociation;
8 use App\Models\WorkOrder\TicketLog; 14 use App\Models\WorkOrder\TicketLog;
9 use App\Models\WorkOrder\TicketProject; 15 use App\Models\WorkOrder\TicketProject;
10 use App\Models\WorkOrder\Tickets; 16 use App\Models\WorkOrder\Tickets;
@@ -183,4 +189,74 @@ class TicketController extends BaseController @@ -183,4 +189,74 @@ class TicketController extends BaseController
183 $project->pushWechatGroupMsg("您好,我们同事没有及时回复,你可以查看工单进度!"); 189 $project->pushWechatGroupMsg("您好,我们同事没有及时回复,你可以查看工单进度!");
184 return response()->json(['message' => '工单推送成功']); 190 return response()->json(['message' => '工单推送成功']);
185 } 191 }
  192 +
  193 + /**
  194 + * @remark :工单中获取
  195 + * @name :get_process_records
  196 + * @author :lyh
  197 + * @method :post
  198 + * @time :2025/9/9 15:28
  199 + */
  200 + public function get_project_records(Request $request){
  201 + $request->validate([
  202 + 'project_id'=>'required'
  203 + ],[
  204 + 'project_id.required' => '项目ID不能为空'
  205 + ]);
  206 + $data = (new ProcessRecords())->read(['project_id'=>$this->param['project_id']]);
  207 + $project = (new Project())->with(['payment', 'deploy_build', 'deploy_optimize', 'online_check',
  208 + 'project_after','inquiry_filter_config','web_traffic_config','project_keyword'])->where(['id'=>$this->param['project_id']])->first()->toArray();
  209 + if(!$data){
  210 + $data = [
  211 + 'project_id' => $this->param['project_id'],
  212 + 'record' => [],
  213 + 'remark' => '',
  214 + 'project_record'=>0,
  215 + 'optimize_record'=>0,
  216 + 'type'=>$project['type']
  217 + ];
  218 + }else{
  219 + $data['type'] = $project['type'];
  220 + }
  221 + $data['record'] = array_filter($data['record'], function($item) {
  222 + return $item['date'] > '2025-09-01';
  223 + });
  224 + if($project['type'] == 2){
  225 + //优化项目 默认在seo优化中
  226 + if($data['optimize_record'] == 0) {
  227 + $data['optimize_record'] = 1;
  228 + }
  229 + //时间大于优化时间,默认
  230 + if(date('Y-m-d') > $project['deploy_optimize']['start_date']){
  231 + $data['optimize_record'] = 2;
  232 + }
  233 + //首次达标时间
  234 + if(!empty($project['deploy_optimize']['first_compliance_time'])){
  235 + $data['optimize_record'] = 3;
  236 + }
  237 + //无剩余时间
  238 + if($project['project_type'] == 1){
  239 + $remain_day = $project['seo_remain_day'];
  240 + if($project['seo_remain_day'] == 0){$data['optimize_record'] = 4;}
  241 + }else{
  242 + $remain_day = $project['remain_day'];
  243 + if($project['remain_day'] == 0){$data['optimize_record'] = 4;}
  244 + }
  245 + if(in_array(2,$project['level'])){
  246 + $data['date_project_record'] = ['暂停优化'];
  247 + }else{
  248 + $data['date_project_record'] = Project::projectProgress('optimize');
  249 + $data['date_project_record'][4] .= (': '.$remain_day.'天');
  250 + }
  251 + }else{
  252 + //查看是否绑定微信群
  253 + $projectAss = new ProjectAssociation();
  254 + $count = $projectAss->counts(['project_id'=>$this->param['project_id']]);
  255 + if(($count > 0) && ($data['project_record'] == 0)){
  256 + $data['project_record'] = 1;
  257 + }
  258 + $data['date_project_record'] = Project::projectProgress('build');
  259 + }
  260 + $this->response('success',Code::SUCCESS,$data);
  261 + }
186 } 262 }
@@ -200,6 +200,7 @@ class OptimizeController extends BaseController @@ -200,6 +200,7 @@ class OptimizeController extends BaseController
200 'gl_project.is_translate AS is_translate', 200 'gl_project.is_translate AS is_translate',
201 'gl_project.is_translate_tag AS is_translate_tag', 201 'gl_project.is_translate_tag AS is_translate_tag',
202 'gl_project.is_upgrade AS is_upgrade', 202 'gl_project.is_upgrade AS is_upgrade',
  203 + 'gl_project.project_type AS project_type',
203 'gl_project.site_status AS site_status', 204 'gl_project.site_status AS site_status',
204 'gl_project_online_check.id AS online_check_id', 205 'gl_project_online_check.id AS online_check_id',
205 'gl_project_online_check.question AS question', 206 'gl_project_online_check.question AS question',
@@ -322,9 +323,9 @@ class OptimizeController extends BaseController @@ -322,9 +323,9 @@ class OptimizeController extends BaseController
322 $query = $query->where('gl_project_deploy_build.test_domain','like','%'.$this->map['test_domain'].'%'); 323 $query = $query->where('gl_project_deploy_build.test_domain','like','%'.$this->map['test_domain'].'%');
323 } 324 }
324 $query = $query->where('gl_project.type',2)->where('gl_project.extend_type','!=',5);//TODO::2,4代表优化项目; 2024-06-05修改项目中台只显示为类型为2 325 $query = $query->where('gl_project.type',2)->where('gl_project.extend_type','!=',5);//TODO::2,4代表优化项目; 2024-06-05修改项目中台只显示为类型为2
325 - $query->where(function ($subQuery) {  
326 - $subQuery->orwhere('gl_project_online_check.qa_status',1)->orwhere('gl_project.is_upgrade',1);  
327 - }); 326 +// $query->where(function ($subQuery) {
  327 +// $subQuery->where('gl_project.is_upgrade',1);
  328 +// });
328 return $query; 329 return $query;
329 } 330 }
330 331
@@ -547,9 +548,9 @@ class OptimizeController extends BaseController @@ -547,9 +548,9 @@ class OptimizeController extends BaseController
547 ProjectServer::useProject($this->param['project_id']); 548 ProjectServer::useProject($this->param['project_id']);
548 $productKeywordModel = new Keyword(); 549 $productKeywordModel = new Keyword();
549 $data = []; 550 $data = [];
550 - $lists = $productKeywordModel->list(['route'=>['!=',null]],'id',['id','route','seo_title']);  
551 - if (!empty($lists)){  
552 - foreach ($lists as $item){ 551 + $lists = $productKeywordModel->lists(['route'=>['!=',null]],1,500,'id',['id','route','seo_title']);
  552 + if (!empty($lists['list'])){
  553 + foreach ($lists['list'] as $item){
553 $data[] = $domain . $item['route'] . '/{' . $item['seo_title'] . '}'; 554 $data[] = $domain . $item['route'] . '/{' . $item['seo_title'] . '}';
554 } 555 }
555 } 556 }
@@ -598,6 +598,7 @@ class ProjectController extends BaseController @@ -598,6 +598,7 @@ class ProjectController extends BaseController
598 $this->response('success',Code::SUCCESS,$data); 598 $this->response('success',Code::SUCCESS,$data);
599 } 599 }
600 600
  601 +
601 /** 602 /**
602 * 保存进程记录 603 * 保存进程记录
603 * @author zbj 604 * @author zbj
@@ -161,6 +161,7 @@ class RenewProjectController extends BaseController @@ -161,6 +161,7 @@ class RenewProjectController extends BaseController
161 $data = APublicModel::getNumByProjectId($item['id']); 161 $data = APublicModel::getNumByProjectId($item['id']);
162 } 162 }
163 $plan = Project::planMap(); 163 $plan = Project::planMap();
  164 + $seoPlan = Project::seoMap();
164 $item = [ 165 $item = [
165 'id' => $item['id'], 166 'id' => $item['id'],
166 'title' => $item['title'], 167 'title' => $item['title'],
@@ -178,8 +179,10 @@ class RenewProjectController extends BaseController @@ -178,8 +179,10 @@ class RenewProjectController extends BaseController
178 'optimize_tech' => $manageModel->getName($item['deploy_optimize']['tech_mid']), //售后技术 179 'optimize_tech' => $manageModel->getName($item['deploy_optimize']['tech_mid']), //售后技术
179 'type' => $item['type'], 180 'type' => $item['type'],
180 'test_domain' => $item['deploy_build']['test_domain'] ?? 0, 181 'test_domain' => $item['deploy_build']['test_domain'] ?? 0,
181 - 'plan' =>$plan[$item['deploy_build']['plan']] ?? '白帽seo版本', 182 + 'plan' =>$plan[$item['deploy_build']['plan']] ?? '无',
  183 + 'seo_plan' =>$seoPlan[$item['deploy_build']['seo_plan']] ?? '无',
182 'plan_id' =>$item['deploy_build']['plan'], 184 'plan_id' =>$item['deploy_build']['plan'],
  185 + 'seo_plan_id' =>$item['deploy_build']['seo_plan'],
183 'domain' => !empty($item['deploy_optimize']['domain']) ? $domainModel->getDomain($item['deploy_optimize']['domain']) : '', 186 'domain' => !empty($item['deploy_optimize']['domain']) ? $domainModel->getDomain($item['deploy_optimize']['domain']) : '',
184 'created_at' => date('Y年m月d日', strtotime($item['created_at'])), 187 'created_at' => date('Y年m月d日', strtotime($item['created_at'])),
185 'autologin_code' => getAutoLoginCode($item['id']), 188 'autologin_code' => getAutoLoginCode($item['id']),
@@ -6,6 +6,7 @@ use App\Enums\Common\Code; @@ -6,6 +6,7 @@ use App\Enums\Common\Code;
6 use App\Http\Controllers\Aside\BaseController; 6 use App\Http\Controllers\Aside\BaseController;
7 use App\Http\Logic\Aside\Template\ATemplateLogic; 7 use App\Http\Logic\Aside\Template\ATemplateLogic;
8 use App\Http\Requests\Aside\Template\ATemplateRequest; 8 use App\Http\Requests\Aside\Template\ATemplateRequest;
  9 +use App\Models\Manage\Manage;
9 use App\Models\Template\TemplateLabel; 10 use App\Models\Template\TemplateLabel;
10 11
11 /** 12 /**
@@ -26,12 +27,14 @@ class ATemplateController extends BaseController @@ -26,12 +27,14 @@ class ATemplateController extends BaseController
26 public function lists(ATemplateLogic $aTemplateLogic){ 27 public function lists(ATemplateLogic $aTemplateLogic){
27 $templateLabel = new TemplateLabel(); 28 $templateLabel = new TemplateLabel();
28 $this->map = $this->searchLabelName($templateLabel); 29 $this->map = $this->searchLabelName($templateLabel);
29 - $filed = ['id','name','image','url','status','sort','deleted_status','test_model','created_at','project_id']; 30 + $filed = ['id','name','image','url','status','upload_id','sort','deleted_status','test_model','submit_time','design_msg','front_msg','created_at','project_id'];
30 $lists = $aTemplateLogic->aTemplateList($this->map,$this->page,$this->row,$this->order,$filed); 31 $lists = $aTemplateLogic->aTemplateList($this->map,$this->page,$this->row,$this->order,$filed);
31 if(!empty($lists) && !empty($lists['list'])){ 32 if(!empty($lists) && !empty($lists['list'])){
  33 + $manageModel = new Manage();
32 foreach ($lists['list'] as $k => $v){ 34 foreach ($lists['list'] as $k => $v){
33 $v['label'] = $templateLabel->list(['template_id'=>$v['id'],'type'=>1],'id',['id','name'],'desc',5); 35 $v['label'] = $templateLabel->list(['template_id'=>$v['id'],'type'=>1],'id',['id','name'],'desc',5);
34 $v['image_link'] = getImageUrl($v['image']); 36 $v['image_link'] = getImageUrl($v['image']);
  37 + $v['upload_name'] = $manageModel->getName($v['upload_id']);
35 $lists['list'][$k] = $v; 38 $lists['list'][$k] = $v;
36 } 39 }
37 } 40 }
@@ -12,7 +12,12 @@ namespace App\Http\Controllers\Aside\Ticket; @@ -12,7 +12,12 @@ namespace App\Http\Controllers\Aside\Ticket;
12 use App\Enums\Common\Code; 12 use App\Enums\Common\Code;
13 use App\Http\Controllers\Aside\BaseController; 13 use App\Http\Controllers\Aside\BaseController;
14 use App\Http\Logic\Aside\Ticket\TicketLogic; 14 use App\Http\Logic\Aside\Ticket\TicketLogic;
  15 +use App\Models\Ticket\TicketDailyCount;
  16 +use App\Models\Ticket\TicketMonthDeptCount;
  17 +use App\Models\Ticket\TicketWeekDeptCount;
15 use Illuminate\Http\Request; 18 use Illuminate\Http\Request;
  19 +use Illuminate\Support\Carbon;
  20 +use Illuminate\Support\Facades\Cache;
16 21
17 class TicketController extends BaseController 22 class TicketController extends BaseController
18 { 23 {
@@ -30,14 +35,91 @@ class TicketController extends BaseController @@ -30,14 +35,91 @@ class TicketController extends BaseController
30 * @time :2025/8/11 10:47 35 * @time :2025/8/11 10:47
31 */ 36 */
32 public function ticketCount(){ 37 public function ticketCount(){
33 - $data['daily'] = $this->logic->getTicketCount();  
34 - $data['list'] = $this->logic->getDailyTicketCount();  
35 - $data['ticket'] = $this->logic->getTicketList(); 38 + $data = Cache::get('ticket_count');
  39 + if(empty($data)){
  40 + $data = [];
  41 + $data['daily'] = $this->logic->getTicketCount();
  42 + $data['list'] = $this->logic->getDailyTicketCount();
  43 + $data['ticket'] = $this->logic->getTicketList();
  44 + Cache::put('ticket_count',$data,1800);
  45 + }
36 $this->response('success',Code::SUCCESS,$data); 46 $this->response('success',Code::SUCCESS,$data);
37 } 47 }
38 48
39 /** 49 /**
40 - * @remark :技术组 50 + * @remark :获取最近一个月的数据
  51 + * @name :getMonthList
  52 + * @author :lyh
  53 + * @method :post
  54 + * @time :2025/8/30 17:26
  55 + */
  56 + public function getMonthList(){
  57 + $this->request->validate([
  58 + 'start'=>'required',
  59 + 'end'=>'required'
  60 + ],[
  61 + 'start.required' => 'start不能为空',
  62 + 'end.required' => 'end不能为空',
  63 + ]);
  64 + $dailyModel = new TicketDailyCount();
  65 + $dailyList = $dailyModel->list(['date'=>['between',[$this->param['start'],$this->param['end']]]],'date',['*'],'desc',30);//取最近30条数据
  66 + $this->response('success',Code::SUCCESS,$dailyList);
  67 + }
  68 + /**
  69 + * @remark :获取搜索时间
  70 + * @name :getTIme
  71 + * @author :lyh
  72 + * @method :post
  73 + * @time :2025/8/30 15:39
  74 + */
  75 + public function getTime(){
  76 + $weekModel = new TicketWeekDeptCount();
  77 + $weekData = $weekModel->where('dept_id',1)->select('start_at', 'end_at')->distinct()->get()->toArray();
  78 + $monthModel = new TicketMonthDeptCount();
  79 + $monthData = $monthModel->where('dept_id',1)->select('start_at', 'end_at')->distinct()->get()->toArray();
  80 + $this->response('success',Code::SUCCESS,['week_data'=>$weekData,'month_data'=>$monthData]);
  81 + }
  82 +
  83 + /**
  84 + * @remark :周记录数据(默认上一周)
  85 + * @name :weekManageList
  86 + * @author :lyh
  87 + * @method :post
  88 + * @time :2025/8/30 15:31
  89 + */
  90 + public function weekManageList(){
  91 + $this->request->validate([
  92 + 'start'=>'required',
  93 + 'end'=>'required'
  94 + ],[
  95 + 'start.required' => 'start不能为空',
  96 + 'end.required' => 'end不能为空',
  97 + ]);
  98 + $data = $this->logic->getWeekManageList($this->param['start'],$this->param['end'],$this->param['dept_id'] ?? 1,$this->order,$this->param['sort'] ?? 'desc');
  99 + $this->response('success',Code::SUCCESS,$data);
  100 + }
  101 +
  102 + /**
  103 + * @remark :按月统计数据(默认上一月)
  104 + * @name :monthManageList
  105 + * @author :lyh
  106 + * @method :post
  107 + * @time :2025/8/30 15:35
  108 + */
  109 + public function monthManageList(){
  110 + $this->request->validate([
  111 + 'start'=>'required',
  112 + 'end'=>'required'
  113 + ],[
  114 + 'start.required' => 'start不能为空',
  115 + 'end.required' => 'end不能为空',
  116 + ]);
  117 + $data = $this->logic->getMonthManageList($this->param['start'],$this->param['end'],$this->param['dept_id'] ?? 1,$this->order,$this->param['sort'] ?? 'desc');
  118 + $this->response('success',Code::SUCCESS,$data);
  119 + }
  120 +
  121 + /**
  122 + * @remark :技术组总统计
41 * @name :manageTicketCount 123 * @name :manageTicketCount
42 * @author :lyh 124 * @author :lyh
43 * @method :post 125 * @method :post
@@ -50,7 +50,31 @@ class AsideTicketController extends BaseController @@ -50,7 +50,31 @@ class AsideTicketController extends BaseController
50 ->when($request->input('status') !== null, function ($query) use ($request) { 50 ->when($request->input('status') !== null, function ($query) use ($request) {
51 // status 查 gl_tickets.status 51 // status 查 gl_tickets.status
52 $status = $request->input('status'); 52 $status = $request->input('status');
53 - return $query->where('status', $status); 53 + if($status == 10){
  54 + $newTime = date("Y-m-d H:i:s", strtotime("-120 hours"));
  55 + return $query->where('status', 0)->where('plan_end_at','<',$newTime);//超过120个小时未处理的工单
  56 + }else{
  57 + return $query->where('status', $status);
  58 + }
  59 + })
  60 + ->when($request->input('timeout') !== null, function ($query) use ($request) {
  61 + // status 查 gl_tickets.status
  62 + $timeout = $request->input('timeout');
  63 + switch ($timeout) {
  64 + case 1:
  65 + $newTime = date("Y-m-d H:i:s", strtotime("-24 hours"));
  66 + break;
  67 + case 2:
  68 + $newTime = date("Y-m-d H:i:s", strtotime("-48 hours"));
  69 + break;
  70 + case 3:
  71 + $newTime = date("Y-m-d H:i:s", strtotime("-72 hours"));
  72 + break;
  73 + default:
  74 + $newTime = date("Y-m-d H:i:s");
  75 + break;
  76 + }
  77 + return $query->where('status', 0)->where('plan_end_at','<',$newTime);//超过120个小时未处理的工单
54 }) 78 })
55 ->when($request->input('star') !== null, function ($query) use ($request) { 79 ->when($request->input('star') !== null, function ($query) use ($request) {
56 $star = $request->input('star'); 80 $star = $request->input('star');
@@ -84,10 +108,30 @@ class AsideTicketController extends BaseController @@ -84,10 +108,30 @@ class AsideTicketController extends BaseController
84 //TODO::用户部门搜索 108 //TODO::用户部门搜索
85 if(isset($this->param['dept_id']) && !empty($this->param['dept_id'])){ 109 if(isset($this->param['dept_id']) && !empty($this->param['dept_id'])){
86 $manageHrModel = new ManageHr(); 110 $manageHrModel = new ManageHr();
87 - $manageIdArr = $manageHrModel->selectField(['dept_id'=>$this->param['dept_id'],'status'=>1],'manage_id');  
88 - $query->whereHas('logs', function ($q) use ($manageIdArr) {  
89 - $q->whereIn('engineer_id', $manageIdArr);  
90 - }); 111 + //售后优化+技术搜索
  112 + if($this->param['dept_id'] == 2 && !empty($this->param['entry_position'])) {//售后部
  113 + if ($this->param['entry_position'] == 1) {
  114 + $manageIdArr = $manageHrModel->selectField(['entry_position' => ['in', [44, 46, 49]], 'status' => 1], 'manage_id');
  115 + } else {
  116 + //售后技术
  117 + $manageIdArr = $manageHrModel->selectField(['entry_position' => ['in', [42, 43, 45, 48, 51]], 'status' => 1], 'manage_id');
  118 + }
  119 + $query->whereHas('logs', function ($q) use ($manageIdArr) {
  120 + $q->whereIn('engineer_id', $manageIdArr);
  121 + });
  122 + }else{
  123 + $manageIdArr = $manageHrModel->selectField(['dept_id'=>$this->param['dept_id'],'status'=>1],'manage_id');
  124 + $query->whereHas('logs', function ($q) use ($manageIdArr) {
  125 + $q->whereIn('engineer_id', $manageIdArr);
  126 + });
  127 + }
  128 + }
  129 + if(!empty($this->param['start_at']) && !empty($this->param['end_at'])){
  130 + $query->whereBetween('created_at',[$this->param['start_at'],$this->param['end_at']]);
  131 + }
  132 + //搜索提交人姓名
  133 + if(!empty($this->param['submit_username'])){
  134 + $query->where('submit_username','like','%'.$this->param['submit_username'].'%');
91 } 135 }
92 // 添加排序功能 136 // 添加排序功能
93 $query->orderBy('status', 'asc'); 137 $query->orderBy('status', 'asc');
@@ -96,11 +140,24 @@ class AsideTicketController extends BaseController @@ -96,11 +140,24 @@ class AsideTicketController extends BaseController
96 $sortOrder = strtolower($request->input('sort_order', 'asc')); 140 $sortOrder = strtolower($request->input('sort_order', 'asc'));
97 $query->orderBy($sortField, $sortOrder); 141 $query->orderBy($sortField, $sortOrder);
98 if ($sortField != 'plan_end_at') $query->orderBy('plan_end_at', 'asc'); 142 if ($sortField != 'plan_end_at') $query->orderBy('plan_end_at', 'asc');
99 - $lists = $query->paginate($this->row, ['*'], 'page', $this->page); 143 + $lists = $query->paginate($this->row, ['*'], 'page', $this->page)->toArray();
  144 + if(!empty($lists) && !empty($lists['list'])){
  145 + foreach ($lists['list'] as $key => $item){
  146 + //计算超时多少个小时
  147 + if($item['status'] == 0){
  148 + $end = date('Y-m-d H:i:s');
  149 + $start = $item['plan_end_at'];
  150 + if($start < $end){
  151 + $item['plan_ent_time'] = diffInHours($start,$end);
  152 + }
  153 + }
  154 + $lists['list'][$key] = $item;
  155 + }
  156 + }
100 $this->response('success', Code::SUCCESS, $lists); 157 $this->response('success', Code::SUCCESS, $lists);
101 } 158 }
102 159
103 - /** 160 + /**
104 * @param $search 161 * @param $search
105 * @return void 162 * @return void
106 * V5V6所有项目 163 * V5V6所有项目
@@ -201,7 +258,6 @@ class AsideTicketController extends BaseController @@ -201,7 +258,6 @@ class AsideTicketController extends BaseController
201 $ticket->close_wechat = $request->input('close_wechat', false); 258 $ticket->close_wechat = $request->input('close_wechat', false);
202 $ticket->num = $request->input('num', 0); 259 $ticket->num = $request->input('num', 0);
203 $ticket->save(); 260 $ticket->save();
204 -  
205 // 分配工单参与人 261 // 分配工单参与人
206 $ticket->saveEngineers($request->input('engineer_ids', [])); 262 $ticket->saveEngineers($request->input('engineer_ids', []));
207 $nickname = ManageHr::where('manage_id', $this->manage['id'])->value('nickname') ?? mb_substr($ticket->submit_username, 0, 1) . '**'; 263 $nickname = ManageHr::where('manage_id', $this->manage['id'])->value('nickname') ?? mb_substr($ticket->submit_username, 0, 1) . '**';
@@ -259,7 +315,8 @@ class AsideTicketController extends BaseController @@ -259,7 +315,8 @@ class AsideTicketController extends BaseController
259 $ticket->status = $request->input('status'); 315 $ticket->status = $request->input('status');
260 if ($request->input('num')) 316 if ($request->input('num'))
261 $ticket->num = $request->input('num',0); 317 $ticket->num = $request->input('num',0);
262 - 318 + //同步更改工单时间
  319 + $ticket->logs()->where('status', '<', TicketLog::STATUS_COMPLETED)->where('is_engineer', 1)->update(['plan_end_at' => $ticket->plan_end_at]);
263 if ($ticket->status == Tickets::STATUS_COMPLETED) 320 if ($ticket->status == Tickets::STATUS_COMPLETED)
264 { 321 {
265 // 完成工单,把子任务里面未完成的工单改为完成 322 // 完成工单,把子任务里面未完成的工单改为完成
@@ -311,5 +368,4 @@ class AsideTicketController extends BaseController @@ -311,5 +368,4 @@ class AsideTicketController extends BaseController
311 $project->pushWechatGroupMsg(); 368 $project->pushWechatGroupMsg();
312 $this->response('success', Code::SUCCESS); 369 $this->response('success', Code::SUCCESS);
313 } 370 }
314 -  
315 } 371 }
@@ -232,12 +232,12 @@ class CNoticeController extends BaseController @@ -232,12 +232,12 @@ class CNoticeController extends BaseController
232 } 232 }
233 //获取项目所在服务器 233 //获取项目所在服务器
234 $project_model = new Project(); 234 $project_model = new Project();
235 - $project_info = $project_model->read(['id'=>$project_id],['serve_id','is_upgrade', 'main_lang_id']); 235 + $project_info = $project_model->read(['id'=>$project_id],['serve_id','type','is_upgrade', 'main_lang_id']);
236 if(!$project_info){ 236 if(!$project_info){
237 $this->fail('未查询到项目数据'); 237 $this->fail('未查询到项目数据');
238 } 238 }
239 // --------------------------------------------------- 特殊处理通知生成页面 -------------------------------------------------------------- 239 // --------------------------------------------------- 特殊处理通知生成页面 --------------------------------------------------------------
240 - if ($type == 2 && $project_info['main_lang_id'] == 8) { 240 + if ($type == 2 && ($project_id != 4041) && ($project_info['main_lang_id'] == 8)) {
241 $this->fail('申请项目主语种为俄语,禁止翻译小语种,如若需要翻译小语种, 请联系售后人员确认!'); 241 $this->fail('申请项目主语种为俄语,禁止翻译小语种,如若需要翻译小语种, 请联系售后人员确认!');
242 } 242 }
243 243
@@ -72,7 +72,7 @@ class GeoQuestionResController extends BaseController @@ -72,7 +72,7 @@ class GeoQuestionResController extends BaseController
72 'project_id.required' => 'project_id不能为空', 72 'project_id.required' => 'project_id不能为空',
73 'type.required' => '品牌类型不能为空' 73 'type.required' => '品牌类型不能为空'
74 ]); 74 ]);
75 - $data = $this->logic->getResultList($this->map,$this->page,$this->row); 75 + $data = $this->logic->getResultList($this->map,$this->page,$this->row,$this->order,$this->param['sort'] ?? 'desc');
76 $this->response('success',Code::SUCCESS,$data); 76 $this->response('success',Code::SUCCESS,$data);
77 } 77 }
78 78
@@ -398,7 +398,7 @@ class ImageController extends Controller @@ -398,7 +398,7 @@ class ImageController extends Controller
398 foreach ($str_data as $k => $v){ 398 foreach ($str_data as $k => $v){
399 $arr = explode('/',$v); 399 $arr = explode('/',$v);
400 if ($arr[0] == 'text') { 400 if ($arr[0] == 'text') {
401 - $arr[1] = urlSafeBase64Encode($arr[1]); 401 + $arr[1] = urlSafeBase64Encode($arr[1] ?? 'shuiying');
402 $v = implode('/',$arr); 402 $v = implode('/',$arr);
403 } 403 }
404 if ($arr[0] == 'font') { 404 if ($arr[0] == 'font') {
@@ -195,19 +195,25 @@ class DomainInfoLogic extends BaseLogic @@ -195,19 +195,25 @@ class DomainInfoLogic extends BaseLogic
195 $host = $domain_array['host'] ?? $domain_array['path']; 195 $host = $domain_array['host'] ?? $domain_array['path'];
196 $host_array = explode('.',$host); 196 $host_array = explode('.',$host);
197 197
  198 + //已经解析泛域名的项目,更改项目解析状态
  199 + $host_array_any = $host_array;
  200 + if (count($host_array_any) <= 2) {
  201 + array_unshift($host_array_any, '*');
  202 + } else {
  203 + $host_array_any[0] = '*';
  204 + }
  205 + $any_domain = implode('.',$host_array_any);
  206 + $rand_str = generateRandomString(3);
  207 + $any_check_status = check_domain_record(str_replace('*',$rand_str,$any_domain), $serversIpInfo);
  208 + if($any_check_status){
  209 + $project_model->edit(['is_analysis'=>1],['id'=>$info['project_id']]);
  210 + }
  211 +
198 if($this->param['type'] == 3){ 212 if($this->param['type'] == 3){
199 //需要申请通配符证书 213 //需要申请通配符证书
200 214
201 - //判断*是否已经解析  
202 - $host_array_any = $host_array;  
203 - if (count($host_array_any) <= 2) {  
204 - array_unshift($host_array_any, '*');  
205 - } else {  
206 - $host_array_any[0] = '*';  
207 - }  
208 - $any_domain = implode('.',$host_array_any);  
209 - $rand_str = generateRandomString(3);  
210 - if(!check_domain_record(str_replace('*',$rand_str,$any_domain), $serversIpInfo)){ 215 + //判断是否已经解析泛域名
  216 + if(!$any_check_status){
211 $this->fail('域名' . $any_domain . '未解析至目标服务器'); 217 $this->fail('域名' . $any_domain . '未解析至目标服务器');
212 } 218 }
213 219
@@ -73,7 +73,7 @@ class GeoLinkLogic extends BaseLogic @@ -73,7 +73,7 @@ class GeoLinkLogic extends BaseLogic
73 foreach ($this->param['data'] as $item){ 73 foreach ($this->param['data'] as $item){
74 $data[] = [ 74 $data[] = [
75 'project_id'=>$this->param['project_id'], 75 'project_id'=>$this->param['project_id'],
76 - 'da'=>$item['da'], 76 + 'da'=>$item['da'] ?? '',
77 'url'=>$item['url'], 77 'url'=>$item['url'],
78 'send_time'=>$item['send_time'] 78 'send_time'=>$item['send_time']
79 ]; 79 ];
@@ -7,6 +7,7 @@ use App\Http\Logic\Aside\BaseLogic; @@ -7,6 +7,7 @@ use App\Http\Logic\Aside\BaseLogic;
7 use App\Models\Domain\DomainInfo; 7 use App\Models\Domain\DomainInfo;
8 use App\Models\Project\ProcessRecords; 8 use App\Models\Project\ProcessRecords;
9 use App\Models\Project\Project; 9 use App\Models\Project\Project;
  10 +use App\Models\ProjectAssociation\ProjectAssociation;
10 11
11 class ProcessRecordsLogic extends BaseLogic 12 class ProcessRecordsLogic extends BaseLogic
12 { 13 {
@@ -20,14 +21,19 @@ class ProcessRecordsLogic extends BaseLogic @@ -20,14 +21,19 @@ class ProcessRecordsLogic extends BaseLogic
20 public function getInfo($project_id) 21 public function getInfo($project_id)
21 { 22 {
22 $data = $this->model->read(['project_id'=>$project_id]); 23 $data = $this->model->read(['project_id'=>$project_id]);
  24 + $info = $data;
23 $project = ProjectLogic::instance()->getProjectInfo($project_id); 25 $project = ProjectLogic::instance()->getProjectInfo($project_id);
24 if(!$data){ 26 if(!$data){
25 $data = [ 27 $data = [
26 'project_id' => $project_id, 28 'project_id' => $project_id,
27 'record' => [], 29 'record' => [],
28 'remark' => '', 30 'remark' => '',
  31 + 'project_record'=>0,
  32 + 'optimize_record'=>0,
  33 + 'type'=>$project['type']
29 ]; 34 ];
30 } 35 }
  36 + $data['type'] = $project['type'] ?? '';
31 $data['project_company'] = $project['company'] ?? ''; 37 $data['project_company'] = $project['company'] ?? '';
32 $plan = Project::planMap(); 38 $plan = Project::planMap();
33 $seo_plan = Project::seoMap(); 39 $seo_plan = Project::seoMap();
@@ -55,6 +61,45 @@ class ProcessRecordsLogic extends BaseLogic @@ -55,6 +61,45 @@ class ProcessRecordsLogic extends BaseLogic
55 'item' => ['资料上传', '网站修改中', '网站搭建完成,客户确认中', '等待网站品控审核后上线'] 61 'item' => ['资料上传', '网站修改中', '网站搭建完成,客户确认中', '等待网站品控审核后上线']
56 ] 62 ]
57 ]; 63 ];
  64 +// $data['record'] = array_filter($data['record'], function($item) {
  65 +// return $item['date'] > '2025-09-01';
  66 +// });
  67 + if($project['type'] == 2){
  68 + //优化项目 默认在seo优化中
  69 + if($data['optimize_record'] == 0) {
  70 + $data['optimize_record'] = 1;
  71 + }
  72 + //时间大于优化时间,默认
  73 + if(date('Y-m-d') > $project['deploy_optimize']['start_date']){
  74 + $data['optimize_record'] = 2;
  75 + }
  76 + //首次达标时间
  77 + if(!empty($project['deploy_optimize']['first_compliance_time'])){
  78 + $data['optimize_record'] = 3;
  79 + }
  80 + //无剩余时间
  81 + if($project['project_type'] == 1){
  82 + $remain_day = $project['seo_remain_day'];
  83 + if($project['seo_remain_day'] == 0){$data['optimize_record'] = 4;}
  84 + }else{
  85 + $remain_day = $project['remain_day'];
  86 + if($project['remain_day'] == 0){$data['optimize_record'] = 4;}
  87 + }
  88 + if(in_array(2,$project['level'])){
  89 + $data['date_project_record'] = ['暂停优化'];
  90 + }else{
  91 + $data['date_project_record'] = Project::projectProgress('optimize');
  92 + $data['date_project_record'][4] .= (': '.$remain_day.'天');
  93 + }
  94 + }else{
  95 + //查看是否绑定微信群
  96 + $projectAss = new ProjectAssociation();
  97 + $count = $projectAss->counts(['project_id'=>$project_id]);
  98 + if(($count > 0) && ($data['project_record'] == 0)){
  99 + $data['project_record'] = 1;
  100 + }
  101 + $data['date_project_record'] = Project::projectProgress('build');
  102 + }
58 return $this->success($data); 103 return $this->success($data);
59 } 104 }
60 105
@@ -472,7 +472,9 @@ class ProjectLogic extends BaseLogic @@ -472,7 +472,9 @@ class ProjectLogic extends BaseLogic
472 } 472 }
473 $param['confirm_file'] = Arr::a2s($param['confirm_file'] ?? []); 473 $param['confirm_file'] = Arr::a2s($param['confirm_file'] ?? []);
474 $remain_day = $param['deploy_build']['service_duration'] - $param['finish_remain_day']; 474 $remain_day = $param['deploy_build']['service_duration'] - $param['finish_remain_day'];
  475 + $seo_remain_day = $param['deploy_build']['seo_service_duration'] - $param['bm_finish_remain_day'];
475 $param['remain_day'] = ($remain_day > 0) ? $remain_day : 0; 476 $param['remain_day'] = ($remain_day > 0) ? $remain_day : 0;
  477 + $param['seo_remain_day'] = ($seo_remain_day > 0) ? $seo_remain_day : 0;
476 //文件上传默认值 478 //文件上传默认值
477 if($param['is_upload_manage']){ 479 if($param['is_upload_manage']){
478 $param['upload_config'] = [ 480 $param['upload_config'] = [
@@ -15,6 +15,10 @@ use App\Models\Project\Project; @@ -15,6 +15,10 @@ use App\Models\Project\Project;
15 use App\Models\Ticket\TicketDailyCount; 15 use App\Models\Ticket\TicketDailyCount;
16 use App\Models\Ticket\TicketDailyDeptCount; 16 use App\Models\Ticket\TicketDailyDeptCount;
17 use App\Models\Ticket\TicketDailyManageCount; 17 use App\Models\Ticket\TicketDailyManageCount;
  18 +use App\Models\Ticket\TicketMonthDeptCount;
  19 +use App\Models\Ticket\TicketMonthManageCount;
  20 +use App\Models\Ticket\TicketWeekDeptCount;
  21 +use App\Models\Ticket\TicketWeekManageCount;
18 use App\Models\WorkOrder\TicketLog; 22 use App\Models\WorkOrder\TicketLog;
19 use App\Models\WorkOrder\TicketProject; 23 use App\Models\WorkOrder\TicketProject;
20 use App\Models\WorkOrder\Tickets; 24 use App\Models\WorkOrder\Tickets;
@@ -42,7 +46,7 @@ class TicketLogic extends BaseLogic @@ -42,7 +46,7 @@ class TicketLogic extends BaseLogic
42 $date = date('Y-m-d');//今日时间 46 $date = date('Y-m-d');//今日时间
43 $data['add_num'] = $ticketModel->counts(['created_at'=>['between',[$date.' 00:00:00',$date.' 23:59:59']]]);//今日新增工单 47 $data['add_num'] = $ticketModel->counts(['created_at'=>['between',[$date.' 00:00:00',$date.' 23:59:59']]]);//今日新增工单
44 $data['processed_num'] = $ticketModel->counts(['end_at'=>['between',[$date.' 00:00:00',$date.' 23:59:59']]]);//今日已处理工单 48 $data['processed_num'] = $ticketModel->counts(['end_at'=>['between',[$date.' 00:00:00',$date.' 23:59:59']]]);//今日已处理工单
45 - $data['untreated_num'] = $ticketModel->counts(['end_at'=>null]);//今日未处理工单 49 + $data['untreated_num'] = $ticketModel->counts(['end_at'=>null]);//未处理工单
46 $submit_a_side = $ticketModel->formatQuery(['submit_side'=>1])->sum('submit_side'); 50 $submit_a_side = $ticketModel->formatQuery(['submit_side'=>1])->sum('submit_side');
47 $submit_b_side = $ticketModel->formatQuery(['submit_side'=>2])->sum('submit_side'); 51 $submit_b_side = $ticketModel->formatQuery(['submit_side'=>2])->sum('submit_side');
48 $data['source'] = ['a'=>$submit_a_side,'b'=>$submit_b_side]; 52 $data['source'] = ['a'=>$submit_a_side,'b'=>$submit_b_side];
@@ -110,4 +114,41 @@ class TicketLogic extends BaseLogic @@ -110,4 +114,41 @@ class TicketLogic extends BaseLogic
110 return $this->success($manageList); 114 return $this->success($manageList);
111 } 115 }
112 116
  117 + /**
  118 + * @remark :按月统计数据
  119 + * @name :getWeekManageList
  120 + * @author :lyh
  121 + * @method :post
  122 + * @time :2025/8/30 16:02
  123 + */
  124 + public function getWeekManageList($start,$end,$dept_id = 1,$order = 'complete_num',$sort){
  125 + if(empty($start) || empty($end)){
  126 + $start = Carbon::now()->subWeek()->startOfWeek(); // 上周一 00:00:00
  127 + $end = Carbon::now()->subWeek()->endOfWeek(); // 上周日 23:59:59
  128 + }
  129 + $manageWeekModel = new TicketWeekManageCount();
  130 + $manageWeekList = $manageWeekModel->list(['start_at'=>$start,'end_at'=>$end,'dept_id'=>$dept_id],$order,['*'],$sort);
  131 + $deptWeekModel = new TicketWeekDeptCount();
  132 + $deptWeekList = $deptWeekModel->list(['start_at'=>$start,'end_at'=>$end]);
  133 + return $this->success(['manage'=>$manageWeekList,'dept'=>$deptWeekList ?? []]);
  134 + }
  135 +
  136 + /**
  137 + * @remark :按月统计数据
  138 + * @name :getMonthManageList
  139 + * @author :lyh
  140 + * @method :post
  141 + * @time :2025/8/30 16:27
  142 + */
  143 + public function getMonthManageList($start,$end,$dept_id = 1,$order = 'complete_num',$sort = 'desc'){
  144 + if(empty($start) || empty($end)){
  145 + $start = Carbon::now()->subMonth()->startOfMonth(); // 上个月 1号 00:00:00
  146 + $end = Carbon::now()->subMonth()->endOfMonth(); // 上个月最后一天 23:59:59
  147 + }
  148 + $manageWeekModel = new TicketMonthManageCount();
  149 + $manageWeekList = $manageWeekModel->list(['start_at'=>$start,'end_at'=>$end,'dept_id'=>$dept_id],$order,['*'],$sort);
  150 + $deptWeekModel = new TicketMonthDeptCount();
  151 + $deptWeekList = $deptWeekModel->list(['start_at'=>$start,'end_at'=>$end]);
  152 + return $this->success(['manage'=>$manageWeekList,'dept'=>$deptWeekList]);
  153 + }
113 } 154 }
@@ -417,6 +417,8 @@ class CustomModuleContentLogic extends BaseLogic @@ -417,6 +417,8 @@ class CustomModuleContentLogic extends BaseLogic
417 $info = $this->model->read(['id'=>$this->param['id']]); 417 $info = $this->model->read(['id'=>$this->param['id']]);
418 $param = $this->setContentParams($info); 418 $param = $this->setContentParams($info);
419 $save_id = $this->model->insertGetId($param); 419 $save_id = $this->model->insertGetId($param);
  420 + $route = RouteMap::setRoute($param['route'], RouteMap::SOURCE_MODULE, $save_id, $this->user['project_id']);
  421 + $this->edit(['route' => $route], ['id' => $save_id]);
420 $this->copyTemplate($this->param['id'],$info['project_id'],$save_id,$info['module_id']); 422 $this->copyTemplate($this->param['id'],$info['project_id'],$save_id,$info['module_id']);
421 return $this->success(['id'=>$save_id]); 423 return $this->success(['id'=>$save_id]);
422 } 424 }
@@ -46,9 +46,10 @@ class GeoQuestionResLogic extends BaseLogic @@ -46,9 +46,10 @@ class GeoQuestionResLogic extends BaseLogic
46 * @method :post 46 * @method :post
47 * @time :2025/7/4 9:48 47 * @time :2025/7/4 9:48
48 */ 48 */
49 - public function getResultList($map = [],$page = 1,$row = 20){ 49 + public function getResultList($map = [],$page = 1,$row = 20,$order = 'created_at',$sort = 'desc'){
  50 + unset($map['sort']);
50 $map['project_id'] = $this->user['project_id']; 51 $map['project_id'] = $this->user['project_id'];
51 - $filed = ['id','project_id','question_id','platform','is_match','question','en_question','keywords','url','label','created_at','updated_at']; 52 + $filed = ['id','project_id','question_id','platform','is_match','question','en_question','keywords','url','label','cosine','created_at','updated_at'];
52 if(!empty($map['created_at'])){ 53 if(!empty($map['created_at'])){
53 $map['created_at'] = ['between',[$map['created_at'].' 00:00:00',$map['created_at'].' 23:59:59']]; 54 $map['created_at'] = ['between',[$map['created_at'].' 00:00:00',$map['created_at'].' 23:59:59']];
54 $this->model = new GeoQuestionLog(); 55 $this->model = new GeoQuestionLog();
@@ -64,7 +65,7 @@ class GeoQuestionResLogic extends BaseLogic @@ -64,7 +65,7 @@ class GeoQuestionResLogic extends BaseLogic
64 // $q->whereRaw('JSON_LENGTH(keywords) > 0') 65 // $q->whereRaw('JSON_LENGTH(keywords) > 0')
65 // ->orWhereRaw('JSON_LENGTH(url) > 0'); 66 // ->orWhereRaw('JSON_LENGTH(url) > 0');
66 // }); 67 // });
67 - $data = $query->orderByRaw('CHAR_LENGTH(question) ASC')->paginate($row, $filed, 'page', $page);; 68 + $data = $query->orderBy($order,$sort)->orderByRaw('CHAR_LENGTH(question) ASC')->paginate($row, $filed, 'page', $page);
68 return $this->success($data); 69 return $this->success($data);
69 } 70 }
70 71
@@ -93,32 +94,50 @@ class GeoQuestionResLogic extends BaseLogic @@ -93,32 +94,50 @@ class GeoQuestionResLogic extends BaseLogic
93 $core_question_count = $questionTotalCount = $urlTotalCount = $keywordsTotalCount = 0; 94 $core_question_count = $questionTotalCount = $urlTotalCount = $keywordsTotalCount = 0;
94 $keywordArr = []; 95 $keywordArr = [];
95 $questionLogModel = new GeoQuestionLog(); 96 $questionLogModel = new GeoQuestionLog();
96 - $keywordUrlCount = $questionLogModel->counts(['project_id'=>$this->user['project_id'],'hit'=>['!=',0]]);  
97 - $coreKeywordUrlCount = $questionLogModel->counts(['project_id'=>$this->user['project_id'],'label'=>['like','%核心问题%'],'hit'=>['!=',0]]);  
98 - foreach ($list as $item){  
99 - $questionTotalCount += count($item['question'] ?? []);  
100 - //核心问题数  
101 - if(strpos($item['label'],'核心问题') !== false){  
102 - $core_question_count += count($item['question'] ?? []); 97 + if($this->user['project_id'] == 4533){
  98 + $keywordUrlCount = $questionLogModel->counts(['project_id'=>$this->user['project_id'],'is_match'=>1,'hit'=>['!=',0]]);
  99 + foreach ($list as $item){
  100 + $questionTotalCount += count($item['question'] ?? []);
  101 + //核心问题数
  102 + if(strpos($item['label'],'核心问题') !== false){
  103 + $core_question_count += count($item['question'] ?? []);
  104 + }
  105 + foreach ($item['keywords'] as $keyWordItem){
  106 + if (!array_key_exists($keyWordItem, $keywordArr)) {
  107 + $keywordArr[$keyWordItem] = $questionLogModel->counts(['project_id'=>$this->user['project_id'],'is_match'=>1,'keywords'=>['like','%"'.$keyWordItem.'"%']]);
  108 + }
  109 + }
103 } 110 }
104 - $keywordsTotalCount += count($item['keywords'] ?? []);  
105 - $urlTotalCount += count($item['url'] ?? []);  
106 - foreach ($item['keywords'] as $keyWordItem){  
107 - if (!array_key_exists($keyWordItem, $keywordArr)) {  
108 - $keywordArr[$keyWordItem] = $questionLogModel->counts(['project_id'=>$this->user['project_id'],'keywords'=>['like','%"'.$keyWordItem.'"%']]); 111 + $coreKeywordUrlCount = $questionLogModel->counts(['project_id'=>$this->user['project_id'],'label'=>['like','%核心问题%'],'is_match'=>1,'hit'=>['!=',0]]);
  112 + $data = [
  113 + 'question_count'=>$questionTotalCount,//问题总数
  114 + 'core_question_count'=>$core_question_count,//核心问题总数
  115 + 'keywords_url_count'=>$keywordUrlCount,
  116 + 'keywords_arr' => $keywordArr,
  117 + 'core_keyword_url_count'=>$coreKeywordUrlCount ?? 0
  118 + ];
  119 + return $this->success($data);
  120 + }else{
  121 + $keywordUrlCount = $questionLogModel->counts(['project_id'=>$this->user['project_id'],'hit'=>['!=',0]]);
  122 + foreach ($list as $item){
  123 + $questionTotalCount += count($item['question'] ?? []);
  124 + $keywordsTotalCount += count($item['keywords'] ?? []);
  125 + $urlTotalCount += count($item['url'] ?? []);
  126 + foreach ($item['keywords'] as $keyWordItem){
  127 + if (!array_key_exists($keyWordItem, $keywordArr)) {
  128 + $keywordArr[$keyWordItem] = $questionLogModel->counts(['project_id'=>$this->user['project_id'],'keywords'=>['like','%"'.$keyWordItem.'"%']]);
  129 + }
109 } 130 }
110 } 131 }
  132 + $data = [
  133 + 'question_count'=>$questionTotalCount,
  134 + 'keywords_count'=>$keywordsTotalCount,
  135 + 'url_count'=>$urlTotalCount,
  136 + 'keywords_url_count'=>$keywordUrlCount,
  137 + 'keywords_arr' => $keywordArr,
  138 + ];
  139 + return $this->success($data);
111 } 140 }
112 - $data = [  
113 - 'keywords_count'=>$keywordsTotalCount,  
114 - 'url_count'=>$urlTotalCount,  
115 - 'question_count'=>$questionTotalCount,  
116 - 'keywords_url_count'=>$keywordUrlCount,  
117 - 'keywords_arr' => $keywordArr,  
118 - 'core_question_count'=>$core_question_count,  
119 - 'core_keyword_url_count'=>$coreKeywordUrlCount  
120 - ];  
121 - return $this->success($data);  
122 } 141 }
123 142
124 /** 143 /**
@@ -134,7 +153,11 @@ class GeoQuestionResLogic extends BaseLogic @@ -134,7 +153,11 @@ class GeoQuestionResLogic extends BaseLogic
134 $list = $platformModel->list(['status'=>1],'id',['name','en_name']); 153 $list = $platformModel->list(['status'=>1],'id',['name','en_name']);
135 $questionResModel = new GeoQuestionLog(); 154 $questionResModel = new GeoQuestionLog();
136 foreach ($list as $item){ 155 foreach ($list as $item){
137 - $data[$item['name']] = $questionResModel->counts(['project_id'=>$this->user['project_id'],'hit'=>['!=',0],'platform'=>$item['en_name']]); 156 + if($this->user['project_id'] == 4533){
  157 + $data[$item['name']] = $questionResModel->counts(['project_id'=>$this->user['project_id'],'is_match'=>1,'hit'=>['!=',0],'platform'=>$item['en_name']]);
  158 + }else{
  159 + $data[$item['name']] = $questionResModel->counts(['project_id'=>$this->user['project_id'],'hit'=>['!=',0],'platform'=>$item['en_name']]);
  160 + }
138 } 161 }
139 return $this->success($data); 162 return $this->success($data);
140 } 163 }
@@ -356,7 +356,12 @@ class ProductLogic extends BaseLogic @@ -356,7 +356,12 @@ class ProductLogic extends BaseLogic
356 $v['url'] = str_replace_url($v['url']); 356 $v['url'] = str_replace_url($v['url']);
357 $param['gallery'][$k] = $v; 357 $param['gallery'][$k] = $v;
358 } 358 }
359 - $param['thumb'] = Arr::a2s($param['gallery'][0] ?? []); 359 + $thumb = $param['gallery'][0] ?? [];
  360 + if(isset($thumb['url']) && $thumb['url']){
  361 + //生成缩略图
  362 + $thumb['url'] = thumbImageByUrl($thumb['url'],$this->user['thumb_w']??0);
  363 + }
  364 + $param['thumb'] = Arr::a2s($thumb);
360 $param['gallery'] = Arr::a2s($param['gallery'] ?? []); 365 $param['gallery'] = Arr::a2s($param['gallery'] ?? []);
361 }else{ 366 }else{
362 $param['thumb'] = Arr::a2s([]); 367 $param['thumb'] = Arr::a2s([]);
@@ -228,12 +228,12 @@ class RankDataLogic extends BaseLogic @@ -228,12 +228,12 @@ class RankDataLogic extends BaseLogic
228 $api_no = $project['deploy_optimize']['api_no'] ?? 0; 228 $api_no = $project['deploy_optimize']['api_no'] ?? 0;
229 } 229 }
230 230
231 -// if(!$api_no || Str::endsWith($api_no, '_bmseo')){ 231 + if(!$api_no || Str::endsWith($api_no, '_bmseo')){
232 $bm_api_no = RankDataBmseo::where('project_id', $project_id)->value('api_no'); 232 $bm_api_no = RankDataBmseo::where('project_id', $project_id)->value('api_no');
233 if($bm_api_no){ 233 if($bm_api_no){
234 $api_no = $bm_api_no; 234 $api_no = $bm_api_no;
235 } 235 }
236 -// } 236 + }
237 237
238 $domain = (!empty($project['deploy_optimize']['domain']) ? ((new DomainInfo())->getDomain($project['deploy_optimize']['domain'])) : ''); 238 $domain = (!empty($project['deploy_optimize']['domain']) ? ((new DomainInfo())->getDomain($project['deploy_optimize']['domain'])) : '');
239 $domain_arr = parse_url($domain); 239 $domain_arr = parse_url($domain);
@@ -555,7 +555,7 @@ class RankDataLogic extends BaseLogic @@ -555,7 +555,7 @@ class RankDataLogic extends BaseLogic
555 $without_extension_project_ids = [658]; //是否达标只统计主词的 555 $without_extension_project_ids = [658]; //是否达标只统计主词的
556 $extension_project_ids = [354]; //扩展词也到达标的 556 $extension_project_ids = [354]; //扩展词也到达标的
557 $compliance_project_ids = [2163,257,823,1750,497]; //直接达标处理的 557 $compliance_project_ids = [2163,257,823,1750,497]; //直接达标处理的
558 - $ceaseProjectId = [354, 378, 649, 1226, 1283, 1703, 1893, 2066, 2250,2193,2399,1685];//暂停的项目 558 + $ceaseProjectId = [354, 378, 649, 1226, 1283, 1703, 1893, 2066, 2250,2193,2399,1685, 3931];//暂停的项目
559 $uptimeProjectId = [1434,1812,276,2414,2974];//按上线时间统计的项目 559 $uptimeProjectId = [1434,1812,276,2414,2974];//按上线时间统计的项目
560 //一个项目多个api_no 560 //一个项目多个api_no
561 $multiple_api_no_project_ids = [ 561 $multiple_api_no_project_ids = [
@@ -322,6 +322,8 @@ class UserLoginLogic @@ -322,6 +322,8 @@ class UserLoginLogic
322 $info['import_products_url'] = 'https://ecdn6.globalso.com/upload/p/1/file/2024-12/products.csv'; 322 $info['import_products_url'] = 'https://ecdn6.globalso.com/upload/p/1/file/2024-12/products.csv';
323 $info['import_news_url'] = 'https://ecdn6.globalso.com/upload/p/1/file/2024-12/news.csv'; 323 $info['import_news_url'] = 'https://ecdn6.globalso.com/upload/p/1/file/2024-12/news.csv';
324 $info['import_blogs_url'] = 'https://ecdn6.globalso.com/upload/p/1/file/2024-12/blogs.csv'; 324 $info['import_blogs_url'] = 'https://ecdn6.globalso.com/upload/p/1/file/2024-12/blogs.csv';
  325 + //缩略图宽度
  326 + $info['thumb_w'] = $project['deploy_build']['thumb_w'] ?? 0;
325 return $info; 327 return $info;
326 } 328 }
327 329
@@ -25,7 +25,7 @@ class AsideTicketListRequest extends FormRequest @@ -25,7 +25,7 @@ class AsideTicketListRequest extends FormRequest
25 { 25 {
26 return [ 26 return [
27 'project_id' => 'nullable|string', 27 'project_id' => 'nullable|string',
28 - 'status' => 'nullable|in:0,1,2,3,9|integer', 28 + 'status' => 'nullable|in:0,1,2,3,9,10|integer',
29 'star' => 'nullable|in:1,2,3|integer', 29 'star' => 'nullable|in:1,2,3|integer',
30 'search' => 'nullable|string', // 搜索关键词 30 'search' => 'nullable|string', // 搜索关键词
31 'engineer_id' => 'nullable|integer', // 工程师ID 31 'engineer_id' => 'nullable|integer', // 工程师ID
@@ -34,10 +34,8 @@ class SyncImageFileJob implements ShouldQueue @@ -34,10 +34,8 @@ class SyncImageFileJob implements ShouldQueue
34 */ 34 */
35 public function handle() 35 public function handle()
36 { 36 {
37 - $file_path = $this->getUrl($this->param['path'].'/'.$this->param['name'], 0,$this->param['location']);  
38 - $cmd = 'curl -k -F "file_path='.$file_path.'" -F "save_path=/www/wwwroot/cos'.$this->param['path'].'" https://v6-file.globalso.com/upload.php';  
39 - echo date('Y-m-d H:i:s') . ' | ' . $cmd . PHP_EOL;  
40 - $code = shell_exec($cmd); 37 + $code = $this->synchronizationFile($this->param['path'].'/'.$this->param['name']);
  38 + echo date('Y-m-d H:i:s') . ' | ' . $code . PHP_EOL;
41 if(200 != (int)$code){ 39 if(200 != (int)$code){
42 $errorFileModel = new ErrorFile(); 40 $errorFileModel = new ErrorFile();
43 $errorFileModel->add(['path'=>$this->param['path'].'/'.$this->param['name']]); 41 $errorFileModel->add(['path'=>$this->param['path'].'/'.$this->param['name']]);
@@ -45,39 +43,12 @@ class SyncImageFileJob implements ShouldQueue @@ -45,39 +43,12 @@ class SyncImageFileJob implements ShouldQueue
45 return true; 43 return true;
46 } 44 }
47 45
48 - /**  
49 - * @remark :获取图片文件链接  
50 - * @name :getUrl  
51 - * @author :lyh  
52 - * @method :post  
53 - * @time :2024/5/22 11:53  
54 - */  
55 - public function getUrl($path,$storage_type,$location){  
56 - if(is_array($path)){  
57 - $url =[];  
58 - foreach ($path as $v){  
59 - $url[] = $this->getUrl($v,$storage_type,$location);  
60 - }  
61 - }else{  
62 - if(empty($path)){  
63 - return '';  
64 - }  
65 - if((strpos($path,'https://')!== false) || (strpos($path,'http://') !== false)){  
66 - return $path;  
67 - }  
68 - if(substr($path,0,2) == '//'){  
69 - return 'https:'.$path;  
70 - }  
71 - if($location == 0){  
72 - $cos = config('filesystems.disks.cos');  
73 - $cosCdn = ($storage_type == 0) ? $cos['cdn'] : $cos['cdn1'];  
74 - $url = $cosCdn.$path;  
75 - }else{  
76 - $s3 = config('filesystems.disks.s3');  
77 - $cdn = $s3['cdn'];  
78 - $url = $cdn.$path;  
79 - }  
80 - }  
81 - return $url; 46 + public function synchronizationFile($path_name){
  47 + //同步到大文件
  48 + $file_path = config('filesystems.disks.cos')['cdn1'].$path_name;
  49 + $directoryPath = pathinfo($path_name, PATHINFO_DIRNAME);
  50 + $cmd = 'curl -k -F "file_path='.$file_path.'" -F "save_path=/www/wwwroot/cos'.$directoryPath.'" https://v6-file.globalso.com/upload.php';
  51 + echo date('Y-m-d H:i:s') . ' | ' . $cmd . PHP_EOL;
  52 + return shell_exec($cmd);
82 } 53 }
83 } 54 }
@@ -178,6 +178,38 @@ class Project extends Base @@ -178,6 +178,38 @@ class Project extends Base
178 } 178 }
179 179
180 /** 180 /**
  181 + * @remark :项目进度
  182 + * @name :projectProgress
  183 + * @author :lyh
  184 + * @method :post
  185 + * @time :2025/9/3 14:47
  186 + */
  187 + public static function projectProgress($search){
  188 + $data = [
  189 + 'build' => [
  190 + 1 => '建立微信群',
  191 + 2 => '对接资料',
  192 + 3 => '确定风格',
  193 + 4 => '资料上传',
  194 + 5 => '页面调整',
  195 + 6 => '关键词确认',
  196 + 7 => '正式网站上线'
  197 + ],
  198 + 'optimize' => [
  199 + 1 => 'SEO设置',
  200 + 2 => '开始推广',
  201 + 3 => '排名达标',
  202 + 4 => '剩余服务时长'
  203 + ]
  204 + ];
  205 +
  206 + if(!empty($search)){
  207 + return $data[$search];
  208 + }
  209 + return $data;
  210 + }
  211 +
  212 + /**
181 * @remark :新增搜索 213 * @remark :新增搜索
182 * @name :searchParam 214 * @name :searchParam
183 * @author :lyh 215 * @author :lyh
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
2 2
3 namespace App\Models\ProjectAssociation; 3 namespace App\Models\ProjectAssociation;
4 4
  5 +use App\Models\Base;
5 use Illuminate\Database\Eloquent\Builder; 6 use Illuminate\Database\Eloquent\Builder;
6 use Illuminate\Database\Eloquent\Model; 7 use Illuminate\Database\Eloquent\Model;
7 8
@@ -38,7 +39,7 @@ use Illuminate\Database\Eloquent\Model; @@ -38,7 +39,7 @@ use Illuminate\Database\Eloquent\Model;
38 * @method Builder|ProjectAssociation whereUserName($value) 39 * @method Builder|ProjectAssociation whereUserName($value)
39 * @mixin \Eloquent 40 * @mixin \Eloquent
40 */ 41 */
41 -class ProjectAssociation extends Model 42 +class ProjectAssociation extends Base
42 { 43 {
43 44
44 protected $table = 'gl_project_association'; 45 protected $table = 'gl_project_association';