作者 刘锟

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

@@ -95,7 +95,7 @@ class RemainDay extends Command @@ -95,7 +95,7 @@ class RemainDay extends Command
95 if($item['type'] != Project::TYPE_THREE){ 95 if($item['type'] != Project::TYPE_THREE){
96 $pause_days = $item['pause_days'] + 1; 96 $pause_days = $item['pause_days'] + 1;
97 } 97 }
98 -// $this->project->edit(['pause_days'=>$pause_days],['id'=>$item['id']]); 98 + $this->project->edit(['pause_days'=>$pause_days],['id'=>$item['id']]);
99 continue; 99 continue;
100 } 100 }
101 //白帽版本的系统 101 //白帽版本的系统
@@ -110,11 +110,11 @@ class RemainDay extends Command @@ -110,11 +110,11 @@ class RemainDay extends Command
110 $seo_remain_day = 0; 110 $seo_remain_day = 0;
111 } 111 }
112 if($deploy_build['plan'] == 0 && $seo_remain_day == 0 && $deploy_build['seo_service_duration'] != 0){//只有白帽版本的项目且剩余服务时常未0,放入未续费中 112 if($deploy_build['plan'] == 0 && $seo_remain_day == 0 && $deploy_build['seo_service_duration'] != 0){//只有白帽版本的项目且剩余服务时常未0,放入未续费中
113 -// $this->project->edit(['seo_remain_day'=>$seo_remain_day,'extend_type'=>Project::TYPE_FIVE],['id'=>$item['id']]); 113 + $this->project->edit(['seo_remain_day'=>$seo_remain_day,'extend_type'=>Project::TYPE_FIVE],['id'=>$item['id']]);
114 continue; 114 continue;
115 } 115 }
116 //同时包括白帽版本+默认版本的项目 116 //同时包括白帽版本+默认版本的项目
117 -// $this->project->edit(['seo_remain_day'=>$seo_remain_day],['id'=>$item['id']]); 117 + $this->project->edit(['seo_remain_day'=>$seo_remain_day],['id'=>$item['id']]);
118 } 118 }
119 //默认版本计算剩余服务时常 119 //默认版本计算剩余服务时常
120 if($item['type'] == Project::TYPE_TWO || $item['type'] == Project::TYPE_FOUR){ 120 if($item['type'] == Project::TYPE_TWO || $item['type'] == Project::TYPE_FOUR){
@@ -144,7 +144,7 @@ class RemainDay extends Command @@ -144,7 +144,7 @@ class RemainDay extends Command
144 $remain_day = 0; 144 $remain_day = 0;
145 $extend_type = Project::TYPE_FIVE; 145 $extend_type = Project::TYPE_FIVE;
146 } 146 }
147 -// $this->project->edit(['remain_day'=>$remain_day,'extend_type'=>$extend_type],['id'=>$item['id']]); 147 + $this->project->edit(['remain_day'=>$remain_day,'extend_type'=>$extend_type],['id'=>$item['id']]);
148 echo 'end->项目id:' . $item['id'] . '执行时间:'. date('Y-m-d H:i:s') . PHP_EOL; 148 echo 'end->项目id:' . $item['id'] . '执行时间:'. date('Y-m-d H:i:s') . PHP_EOL;
149 } 149 }
150 return true; 150 return true;
@@ -14,7 +14,7 @@ use App\Models\Domain\DomainInfo; @@ -14,7 +14,7 @@ use App\Models\Domain\DomainInfo;
14 use App\Models\GoogleSearch\GoogleSearch; 14 use App\Models\GoogleSearch\GoogleSearch;
15 use App\Models\GoogleSearch\GoogleSearchDetail; 15 use App\Models\GoogleSearch\GoogleSearchDetail;
16 use App\Models\Project\Project; 16 use App\Models\Project\Project;
17 -use App\Services\GoogleSearchService; 17 +use App\Services\RapIdApIService;
18 use App\Services\ProjectServer; 18 use App\Services\ProjectServer;
19 use Illuminate\Console\Command; 19 use Illuminate\Console\Command;
20 use Illuminate\Support\Facades\DB; 20 use Illuminate\Support\Facades\DB;
@@ -48,7 +48,7 @@ class GoogleSearchKeyword extends Command @@ -48,7 +48,7 @@ class GoogleSearchKeyword extends Command
48 48
49 public function __construct() 49 public function __construct()
50 { 50 {
51 - $this->googleService = new GoogleSearchService(); 51 + $this->googleService = new RapIdApIService();
52 $this->searchModel = new GoogleSearch(); 52 $this->searchModel = new GoogleSearch();
53 $this->detailModel = new GoogleSearchDetail(); 53 $this->detailModel = new GoogleSearchDetail();
54 parent::__construct(); 54 parent::__construct();
@@ -13,7 +13,9 @@ use App\Helper\Arr; @@ -13,7 +13,9 @@ use App\Helper\Arr;
13 use App\Helper\Translate; 13 use App\Helper\Translate;
14 use App\Models\Ai\AiBlog; 14 use App\Models\Ai\AiBlog;
15 use App\Models\Blog\Blog; 15 use App\Models\Blog\Blog;
  16 +use App\Models\Com\WordCountry;
16 use App\Models\CustomModule\CustomModuleContent; 17 use App\Models\CustomModule\CustomModuleContent;
  18 +use App\Models\GoogleSearch\GoogleCodeCountry;
17 use App\Models\Product\CategoryRelated; 19 use App\Models\Product\CategoryRelated;
18 use App\Models\Product\Keyword; 20 use App\Models\Product\Keyword;
19 use App\Models\Product\Product; 21 use App\Models\Product\Product;
@@ -64,7 +66,7 @@ class UpdateRoute extends Command @@ -64,7 +66,7 @@ class UpdateRoute extends Command
64 */ 66 */
65 public function handle() 67 public function handle()
66 { 68 {
67 - $this->updateProjectOp(); 69 + $this->insertData();
68 } 70 }
69 /** 71 /**
70 * @remark : 72 * @remark :
@@ -543,9 +545,264 @@ class UpdateRoute extends Command @@ -543,9 +545,264 @@ class UpdateRoute extends Command
543 * @method :post 545 * @method :post
544 * @time :2025/3/14 14:39 546 * @time :2025/3/14 14:39
545 */ 547 */
546 - public function updateSeo(){  
547 - $contentModel = new CustomModuleContent();  
548 - $contentModel->edit(['seo_title'=>''],['created_at'=>['<=','2024-11-25 00:00:00']]); 548 + public function insertData(){
  549 + $countries = [
  550 + 'AFG' => 'Afghanistan',
  551 + 'ALB' => 'Albania',
  552 + 'DZA' => 'Algeria',
  553 + 'AND' => 'Andorra',
  554 + 'AGO' => 'Angola',
  555 + 'ATG' => 'Antigua and Barbuda',
  556 + 'ARG' => 'Argentina',
  557 + 'ARM' => 'Armenia',
  558 + 'AUS' => 'Australia',
  559 + 'AUT' => 'Austria',
  560 + 'AZE' => 'Azerbaijan',
  561 + 'BHS' => 'Bahamas',
  562 + 'BHR' => 'Bahrain',
  563 + 'BGD' => 'Bangladesh',
  564 + 'BRB' => 'Barbados',
  565 + 'BLR' => 'Belarus',
  566 + 'BEL' => 'Belgium',
  567 + 'BLZ' => 'Belize',
  568 + 'BEN' => 'Benin',
  569 + 'BTN' => 'Bhutan',
  570 + 'BOL' => 'Bolivia',
  571 + 'BES' => 'Bonaire, Sint Eustatius and Saba',
  572 + 'BIH' => 'Bosnia and Herzegovina',
  573 + 'BWA' => 'Botswana',
  574 + 'BVT' => 'Bouvet Island',
  575 + 'BRA' => 'Brazil',
  576 + 'IOT' => 'British Indian Ocean Territory',
  577 + 'BRN' => 'Brunei Darussalam',
  578 + 'BGR' => 'Bulgaria',
  579 + 'BFA' => 'Burkina Faso',
  580 + 'BDI' => 'Burundi',
  581 + 'CPV' => 'Cabo Verde',
  582 + 'KHM' => 'Cambodia',
  583 + 'CMR' => 'Cameroon',
  584 + 'CAN' => 'Canada',
  585 + 'CYM' => 'Cayman Islands',
  586 + 'CAF' => 'Central African Republic',
  587 + 'TCD' => 'Chad',
  588 + 'CHL' => 'Chile',
  589 + 'CHN' => 'China',
  590 + 'CXR' => 'Christmas Island',
  591 + 'CCK' => 'Cocos (Keeling) Islands',
  592 + 'COL' => 'Colombia',
  593 + 'COM' => 'Comoros',
  594 + 'COD' => 'Congo (Democratic Republic of the)',
  595 + 'COG' => 'Congo',
  596 + 'COK' => 'Cook Islands',
  597 + 'CRI' => 'Costa Rica',
  598 + 'CIV' => 'Côte d\'Ivoire',
  599 + 'HRV' => 'Croatia',
  600 + 'CUB' => 'Cuba',
  601 + 'CUW' => 'Curaçao',
  602 + 'CYP' => 'Cyprus',
  603 + 'CZE' => 'Czech Republic',
  604 + 'DNK' => 'Denmark',
  605 + 'DJI' => 'Djibouti',
  606 + 'DMA' => 'Dominica',
  607 + 'DOM' => 'Dominican Republic',
  608 + 'ECU' => 'Ecuador',
  609 + 'EGY' => 'Egypt',
  610 + 'SLV' => 'El Salvador',
  611 + 'GNQ' => 'Equatorial Guinea',
  612 + 'ERI' => 'Eritrea',
  613 + 'EST' => 'Estonia',
  614 + 'SWZ' => 'Eswatini',
  615 + 'ETH' => 'Ethiopia',
  616 + 'FLK' => 'Falkland Islands (Malvinas)',
  617 + 'FRO' => 'Faroe Islands',
  618 + 'FJI' => 'Fiji',
  619 + 'FIN' => 'Finland',
  620 + 'FRA' => 'France',
  621 + 'GUF' => 'French Guiana',
  622 + 'PYF' => 'French Polynesia',
  623 + 'ATF' => 'French Southern Territories',
  624 + 'GAB' => 'Gabon',
  625 + 'GMB' => 'Gambia',
  626 + 'GEO' => 'Georgia',
  627 + 'DEU' => 'Germany',
  628 + 'GHA' => 'Ghana',
  629 + 'GIB' => 'Gibraltar',
  630 + 'GRC' => 'Greece',
  631 + 'GRL' => 'Greenland',
  632 + 'GRD' => 'Grenada',
  633 + 'GLP' => 'Guadeloupe',
  634 + 'GUM' => 'Guam',
  635 + 'GTM' => 'Guatemala',
  636 + 'GGY' => 'Guernsey',
  637 + 'GIN' => 'Guinea',
  638 + 'GNB' => 'Guinea-Bissau',
  639 + 'GUY' => 'Guyana',
  640 + 'HTI' => 'Haiti',
  641 + 'HMD' => 'Heard Island and McDonald Islands',
  642 + 'VAT' => 'Holy See',
  643 + 'HND' => 'Honduras',
  644 + 'HKG' => 'Hong Kong',
  645 + 'HUN' => 'Hungary',
  646 + 'ISL' => 'Iceland',
  647 + 'IND' => 'India',
  648 + 'IDN' => 'Indonesia',
  649 + 'IRN' => 'Iran (Islamic Republic of)',
  650 + 'IRQ' => 'Iraq',
  651 + 'IRL' => 'Ireland',
  652 + 'IMN' => 'Isle of Man',
  653 + 'ISR' => 'Israel',
  654 + 'ITA' => 'Italy',
  655 + 'JAM' => 'Jamaica',
  656 + 'JPN' => 'Japan',
  657 + 'JEY' => 'Jersey',
  658 + 'JOR' => 'Jordan',
  659 + 'KAZ' => 'Kazakhstan',
  660 + 'KEN' => 'Kenya',
  661 + 'KIR' => 'Kiribati',
  662 + 'KOR' => 'Korea (Republic of)',
  663 + 'KWT' => 'Kuwait',
  664 + 'KGZ' => 'Kyrgyzstan',
  665 + 'LAO' => 'Lao People\'s Democratic Republic',
  666 + 'LVA' => 'Latvia',
  667 + 'LBN' => 'Lebanon',
  668 + 'LSO' => 'Lesotho',
  669 + 'LBR' => 'Liberia',
  670 + 'LBY' => 'Libya',
  671 + 'LIE' => 'Liechtenstein',
  672 + 'LTU' => 'Lithuania',
  673 + 'LUX' => 'Luxembourg',
  674 + 'MAC' => 'Macao',
  675 + 'MDG' => 'Madagascar',
  676 + 'MWI' => 'Malawi',
  677 + 'MYS' => 'Malaysia',
  678 + 'MDV' => 'Maldives',
  679 + 'MLI' => 'Mali',
  680 + 'MLT' => 'Malta',
  681 + 'MHL' => 'Marshall Islands',
  682 + 'MTQ' => 'Martinique',
  683 + 'MRT' => 'Mauritania',
  684 + 'MUS' => 'Mauritius',
  685 + 'MYT' => 'Mayotte',
  686 + 'MEX' => 'Mexico',
  687 + 'FSM' => 'Micronesia (Federated States of)',
  688 + 'MDA' => 'Moldova (Republic of)',
  689 + 'MCO' => 'Monaco',
  690 + 'MNG' => 'Mongolia',
  691 + 'MNE' => 'Montenegro',
  692 + 'MSR' => 'Montserrat',
  693 + 'MAR' => 'Morocco',
  694 + 'MOZ' => 'Mozambique',
  695 + 'MMR' => 'Myanmar',
  696 + 'NAM' => 'Namibia',
  697 + 'NRU' => 'Nauru',
  698 + 'NPL' => 'Nepal',
  699 + 'NLD' => 'Netherlands',
  700 + 'NCL' => 'New Caledonia',
  701 + 'NZL' => 'New Zealand',
  702 + 'NIC' => 'Nicaragua',
  703 + 'NER' => 'Niger',
  704 + 'NGA' => 'Nigeria',
  705 + 'NIU' => 'Niue',
  706 + 'NFK' => 'Norfolk Island',
  707 + 'MNP' => 'Northern Mariana Islands',
  708 + 'NOR' => 'Norway',
  709 + 'OMN' => 'Oman',
  710 + 'PAK' => 'Pakistan',
  711 + 'PLW' => 'Palau',
  712 + 'PSE' => 'Palestine, State of',
  713 + 'PAN' => 'Panama',
  714 + 'PNG' => 'Papua New Guinea',
  715 + 'PRY' => 'Paraguay',
  716 + 'PER' => 'Peru',
  717 + 'PHL' => 'Philippines',
  718 + 'PCN' => 'Pitcairn',
  719 + 'POL' => 'Poland',
  720 + 'PRT' => 'Portugal',
  721 + 'PRI' => 'Puerto Rico',
  722 + 'QAT' => 'Qatar',
  723 + 'REU' => 'Réunion',
  724 + 'ROU' => 'Romania',
  725 + 'RUS' => 'Russian Federation',
  726 + 'RWA' => 'Rwanda',
  727 + 'BLM' => 'Saint Barthélemy',
  728 + 'SHN' => 'Saint Helena, Ascension and Tristan da Cunha',
  729 + 'KNA' => 'Saint Kitts and Nevis',
  730 + 'LCA' => 'Saint Lucia',
  731 + 'MAF' => 'Saint Martin (French part)',
  732 + 'SPM' => 'Saint Pierre and Miquelon',
  733 + 'VCT' => 'Saint Vincent and the Grenadines',
  734 + 'WSM' => 'Samoa',
  735 + 'SMR' => 'San Marino',
  736 + 'STP' => 'Sao Tome and Principe',
  737 + 'SAU' => 'Saudi Arabia',
  738 + 'SEN' => 'Senegal',
  739 + 'SRB' => 'Serbia',
  740 + 'SYC' => 'Seychelles',
  741 + 'SLE' => 'Sierra Leone',
  742 + 'SGP' => 'Singapore',
  743 + 'SXM' => 'Sint Maarten (Dutch part)',
  744 + 'SVK' => 'Slovakia',
  745 + 'SVN' => 'Slovenia',
  746 + 'SLB' => 'Solomon Islands',
  747 + 'SOM' => 'Somalia',
  748 + 'ZAF' => 'South Africa',
  749 + 'SGS' => 'South Georgia and the South Sandwich Islands',
  750 + 'SSD' => 'South Sudan',
  751 + 'ESP' => 'Spain',
  752 + 'LKA' => 'Sri Lanka',
  753 + 'SDN' => 'Sudan',
  754 + 'SUR' => 'Suriname',
  755 + 'SJM' => 'Svalbard and Jan Mayen',
  756 + 'SWE' => 'Sweden',
  757 + 'CHE' => 'Switzerland',
  758 + 'SYR' => 'Syrian Arab Republic',
  759 + 'TWN' => 'Taiwan, Province of China',
  760 + 'TJK' => 'Tajikistan',
  761 + 'TZA' => 'Tanzania, United Republic of',
  762 + 'THA' => 'Thailand',
  763 + 'TLS' => 'Timor-Leste',
  764 + 'TGO' => 'Togo',
  765 + 'TKL' => 'Tokelau',
  766 + 'TON' => 'Tonga',
  767 + 'TTO' => 'Trinidad and Tobago',
  768 + 'TUN' => 'Tunisia',
  769 + 'TUR' => 'Turkey',
  770 + 'TKM' => 'Turkmenistan',
  771 + 'TCA' => 'Turks and Caicos Islands',
  772 + 'TUV' => 'Tuvalu',
  773 + 'UGA' => 'Uganda',
  774 + 'UKR' => 'Ukraine',
  775 + 'ARE' => 'United Arab Emirates',
  776 + 'GBR' => 'United Kingdom',
  777 + 'USA' => 'United States',
  778 + 'URY' => 'Uruguay',
  779 + 'UZB' => 'Uzbekistan',
  780 + 'VUT' => 'Vanuatu',
  781 + 'VEN' => 'Venezuela (Bolivarian Republic of)',
  782 + 'VNM' => 'Viet Nam',
  783 + 'WLF' => 'Wallis and Futuna',
  784 + 'ESH' => 'Western Sahara',
  785 + 'YEM' => 'Yemen',
  786 + 'ZMB' => 'Zambia',
  787 + 'ZWE' => 'Zimbabwe',
  788 + ];
  789 + $codeCountryModel = new GoogleCodeCountry();
  790 + $data = [];
  791 + foreach ($countries as $key =>$val){
  792 + $wordModel = new WordCountry();
  793 + $info = $wordModel->read(['iso3'=>$key]);
  794 + if($info === false){
  795 + $zh = Translate::tran($val,'zh');
  796 + }else{
  797 + $zh = $info['chinese_name'];
  798 + }
  799 + $data[] = [
  800 + 'code'=>$key,
  801 + 'en_country'=>$val,
  802 + 'zh_country'=>$zh,
  803 + ];
  804 + }
  805 + $codeCountryModel->insertAll($data);
549 return true; 806 return true;
550 } 807 }
551 } 808 }
@@ -106,6 +106,7 @@ class SendKeyword extends Command @@ -106,6 +106,7 @@ class SendKeyword extends Command
106 unset($item['main_lang_id']); 106 unset($item['main_lang_id']);
107 unset($item['is_auto_keywords']); 107 unset($item['is_auto_keywords']);
108 $item['day'] = $day; 108 $item['day'] = $day;
  109 + $item['apino'] = $item['api_no'];
109 $this->sendNotice($item); 110 $this->sendNotice($item);
110 } 111 }
111 } 112 }
@@ -1226,7 +1226,7 @@ public static function getCountryNameByAlpha3($alpha3) { @@ -1226,7 +1226,7 @@ public static function getCountryNameByAlpha3($alpha3) {
1226 if (array_key_exists($alpha3, $countries)) { 1226 if (array_key_exists($alpha3, $countries)) {
1227 return $countries[$alpha3]; 1227 return $countries[$alpha3];
1228 } else { 1228 } else {
1229 - return false; 1229 + return '未知国家';
1230 } 1230 }
1231 } 1231 }
1232 1232
  1 +<?php
  2 +
  3 +namespace App\Helper;
  4 +
  5 +
  6 +
  7 +/**
  8 + * api结果处理
  9 + * @author:dc
  10 + * @time 2023/12/16 14:56
  11 + * Class Resource
  12 + * @package GlobalSo\Tool\Gpt
  13 + */
  14 +abstract class Resource {
  15 +
  16 + /**
  17 + * 函数
  18 + * @var array
  19 + */
  20 + protected $func = [];
  21 +
  22 + /**
  23 + * 是否是调试模式
  24 + * @var bool
  25 + */
  26 + public static $debugInfo = false;
  27 +
  28 + /**
  29 + * 获取内容body所有内容
  30 + * @return string
  31 + */
  32 + abstract public function getBody(): string;
  33 +
  34 +
  35 + /**
  36 + * 获取数据的状态
  37 + * @return int
  38 + */
  39 + abstract public function getCode(): int;
  40 +
  41 + /**
  42 + * 验证code 默认200
  43 + * @param int $code
  44 + * @return bool
  45 + * @time 2023/12/16 16:45
  46 + */
  47 + public function checkCode(int $code = 200):bool {
  48 + return $this->getCode() === $code;
  49 + }
  50 +
  51 +
  52 + /**
  53 + * 获取内容 数据
  54 + * @return mixed
  55 + * @author:dc
  56 + * @time 2024/1/2 15:56
  57 + */
  58 + abstract public function getData();
  59 +
  60 +
  61 + /**
  62 + * 获取错误消息
  63 + * @return string
  64 + */
  65 + abstract public function getMessage(): string;
  66 +
  67 +
  68 + /**
  69 + * 是否是函数
  70 + * @return bool
  71 + * @author:dc
  72 + * @time 2024/1/10 12:35
  73 + */
  74 + public function isFun(){
  75 +
  76 + if($this->getBodyFunc()){
  77 + return true;
  78 + }
  79 +
  80 + return false;
  81 + }
  82 +
  83 + /**
  84 + * @return array|mixed
  85 + * @author:dc
  86 + * @time 2024/4/9 15:55
  87 + */
  88 + public function getBodyFunc(){
  89 + $json = @json_decode($this->getBody(),1);
  90 + if(is_array($json) && isset($json['tool_calls'])){
  91 + return $json['tool_calls'];
  92 + }
  93 + return [];
  94 + }
  95 +
  96 + /**
  97 + * 这个是提交了多少token
  98 + * @return int
  99 + * @author:dc
  100 + * @time 2023/12/18 13:37
  101 + */
  102 + public function getPromptToken():int{
  103 + // 这个是老版本
  104 + if(isset($this->getUsage()['prompt_tokens'])){
  105 + return (int) ($this->getUsage()['prompt_tokens']??0);
  106 + }
  107 + // 这个是新版本
  108 + $num = 0;
  109 + foreach ($this->getUsage() as $item){
  110 + $num += (int) ($item['prompt_tokens']??0);
  111 + }
  112 + return $num;
  113 + }
  114 +
  115 + /**
  116 + * 这个是吐出了多少token
  117 + * @return int
  118 + * @author:dc
  119 + * @time 2023/12/18 13:37
  120 + */
  121 + public function getCompletionToken():int{
  122 + // 这个是老版本
  123 + if(isset($this->getUsage()['completion_tokens'])){
  124 + return (int) ($this->getUsage()['completion_tokens']??0);
  125 + }
  126 + // 这个是新版本
  127 + $num = 0;
  128 + foreach ($this->getUsage() as $item){
  129 + $num += (int) ($item['completion_tokens']??0);
  130 + }
  131 + return $num;
  132 + }
  133 +
  134 +
  135 + /**
  136 + * 获取函数
  137 + * @return array|mixed
  138 + */
  139 + public function getFunc($call=null,...$params)
  140 + {
  141 + if(!$this->func){
  142 + $this->func = $this->getBodyFunc();
  143 + }
  144 +
  145 + // {"code":200,"func":{"name":"taocan","arguments":{"attribute":"\u7528\u91cf","usetime":"\u4eca\u5929"}}}
  146 + $function = $this->func;
  147 +
  148 + if(!empty($this->func) && is_array($this->func)) {
  149 + if (!isset($this->func['name'])) {
  150 + $this->func = [$function[0]];
  151 + }
  152 + }
  153 +
  154 + $result = $this->getFuncAll($call,...$params);
  155 +
  156 + $this->func = $function;
  157 +
  158 + return array_values($result)[0]??null;
  159 + }
  160 +
  161 +
  162 + /**
  163 + * 获取所有的函数体
  164 + * @param null $call
  165 + * @param mixed ...$params
  166 + * @return array|false|mixed
  167 + * @author:dc
  168 + * @time 2024/3/11 14:32
  169 + */
  170 + public function getFuncAll($call=null,...$params)
  171 + {
  172 + if(!$this->func){
  173 + $this->func = $this->getBodyFunc();
  174 + }
  175 + // {"code":200,"func":{"name":"taocan","arguments":{"attribute":"\u7528\u91cf","usetime":"\u4eca\u5929"}}}
  176 + $function = [];
  177 + if(!empty($this->func) && is_array($this->func)){
  178 + $function = $this->func;
  179 + }
  180 +
  181 + if($call){
  182 + if($function){
  183 + // 是否是老版本
  184 + if(!empty($function['name'])){
  185 + // 整理参数
  186 + return [$function['name']=> $this->callFunc(
  187 + $call,
  188 + $function['name'],
  189 + $params,
  190 + $function['arguments']??[]
  191 + )];
  192 + }else{
  193 + // 循环 函数
  194 + $result = [];
  195 + foreach ($function as $func){
  196 + $result[$func['name']] = $this->callFunc(
  197 + $call,
  198 + $func['name'],
  199 + $params,
  200 + $func['arguments']??[]
  201 + );
  202 + }
  203 + return $result;
  204 + }
  205 + }
  206 + return [];
  207 + }
  208 +
  209 + return $function;
  210 + }
  211 +
  212 +
  213 +
  214 + /**
  215 + * call 回调
  216 + * @param $call
  217 + * @param $funcName
  218 + * @param $params
  219 + * @return false|mixed
  220 + * @author:dc
  221 + * @time 2024/3/11 11:35
  222 + */
  223 + private function callFunc($call,$funcName,$params,$attr) {
  224 + $params[] = $attr;
  225 + // 匿名函数
  226 + if($call instanceof \Closure){
  227 + return $call($funcName, ...$params);
  228 + }
  229 + // 类名称
  230 + elseif ($call){
  231 + // 定义了类
  232 + if(is_object($call) || class_exists($call)){
  233 + // 掉用类
  234 + return call_user_func(
  235 + [$call,$funcName]
  236 + ,...$params
  237 + );
  238 + }else
  239 + return call_user_func(
  240 + [$call,$funcName]
  241 + ,...$params
  242 + );
  243 + }
  244 + }
  245 +
  246 +
  247 +
  248 +
  249 +
  250 +}
  1 +<?php
  2 +
  3 +namespace App\Helper;
  4 +
  5 +
  6 +
  7 +/**
  8 + * 流输出
  9 + * @author:dc
  10 + * @time 2024/1/2 14:46
  11 + * Class Stream
  12 + * @package GlobalSo\Tool\Gpt\Resource
  13 + */
  14 +class Stream extends Resource{
  15 +
  16 + /**
  17 + * body内容
  18 + * @var string
  19 + */
  20 + private $body = '';
  21 +
  22 + /**
  23 + * 流输出的文本
  24 + * @var string
  25 + */
  26 + private $text = '';
  27 +
  28 + /**
  29 + * http 状态
  30 + * @var int
  31 + */
  32 + private $status = 200;
  33 +
  34 +
  35 + /**
  36 + * @var \Psr\Http\Message\StreamInterface
  37 + */
  38 + private $stream;
  39 +
  40 + /**
  41 + * @var array 使用了多少token
  42 + */
  43 + private $usage = [];
  44 +
  45 +
  46 + /**
  47 + * Resource constructor.
  48 + * @param \Psr\Http\Message\StreamInterface|array $response
  49 + */
  50 + public function __construct($response)
  51 + {
  52 + if($response instanceof \Psr\Http\Message\StreamInterface){
  53 + $this->stream = $response;
  54 + }
  55 + // 数组,带上下文
  56 + elseif(is_array($response)){
  57 + $this->stream = false;
  58 + // 回答的文本
  59 + $this->text = end($response);
  60 + // 计算token
  61 + $this->usage = [
  62 + [
  63 + 'model'=>'',
  64 + ]
  65 + ];
  66 +
  67 + }
  68 +
  69 + }
  70 +
  71 + /**
  72 + * 最后一行
  73 + * @var array
  74 + */
  75 + private $endLine = [];
  76 +
  77 +
  78 + /**
  79 + * 获取流输出内容
  80 + * @return null
  81 + * @author:dc
  82 + * @time 2024/1/2 13:57
  83 + */
  84 + public function getStreamContent(\Closure $call)
  85 + {
  86 + // 文本
  87 + if($this->stream===false){
  88 + $this->body = $this->text;
  89 + $call($this->text);
  90 + }
  91 + // 流输出
  92 + else{
  93 + while (!$this->stream->eof()) {
  94 + // 获取一行数据
  95 + $line = $this->getStreamContentLine();
  96 + // 必须要有数据
  97 + if($line){
  98 + // 解析成数组
  99 + $arr = @json_decode($line,true);
  100 + // 必须是一个数组
  101 + if(is_array($arr)){
  102 +
  103 + // 是否是函数
  104 + if(!empty($arr['func'])){
  105 + $this->func = $arr['func'] ? : ($arr['tool_calls']??[]);
  106 + continue;
  107 + }
  108 +
  109 + // 这里是新版本
  110 + // 文本
  111 + if(isset($arr['text'])){
  112 + // 拼接
  113 + $this->text .= $arr['text'];
  114 + // 调用
  115 + $call($arr['text']);
  116 + }
  117 + // 到了最后一行
  118 + if (isset($arr['usage'])){
  119 + $this->usage = $arr['usage'];
  120 + $this->endLine = $arr;
  121 + }
  122 + }else{
  123 + // 拼接
  124 + $this->text .= $line;
  125 + // 这里兼容下老版本
  126 + $call($line);
  127 + }
  128 +
  129 + }
  130 + }
  131 +
  132 + // 兼容老版本 老版本没办法获取 实际使用了多少token
  133 + if(!$this->usage){
  134 + $this->usage = [
  135 + [
  136 + 'model'=>'',
  137 + ]
  138 + ];
  139 + }
  140 +
  141 + }
  142 +
  143 + }
  144 +
  145 + /**
  146 + * 流 读取一行
  147 + * @return string
  148 + * @author:dc
  149 + * @time 2024/1/2 14:16
  150 + */
  151 + private function getStreamContentLine(){
  152 + $text = '';
  153 + while (!$this->stream->eof()){
  154 + // 读取一个字符串
  155 + $t = $this->stream->read(1);
  156 + $this->body .= $t;
  157 + if($t === "\n"){
  158 + break;
  159 + }
  160 + // 结束了
  161 + if(ord($t)==1){
  162 + break;
  163 + }
  164 + $text .= $t;
  165 +
  166 + }
  167 + return $text;
  168 + }
  169 +
  170 +
  171 + /**
  172 + * 流输出的所有内容
  173 + * @return string
  174 + */
  175 + public function getBody(): string
  176 + {
  177 + return $this->body;
  178 + }
  179 +
  180 +
  181 + /**
  182 + * @return int
  183 + */
  184 + public function getCode(): int
  185 + {
  186 + return 200;
  187 + }
  188 +
  189 +
  190 + /**
  191 + * 这个是文本内容,就是回答的内容
  192 + * @return array|string
  193 + * @author:dc
  194 + * @time 2024/1/2 14:57
  195 + */
  196 + public function getData()
  197 + {
  198 + return $this->text;
  199 + }
  200 +
  201 + /**
  202 + * @return string
  203 + */
  204 + public function getMessage(): string
  205 + {
  206 + return '';
  207 + }
  208 +
  209 +
  210 + /**
  211 + * @return array
  212 + * @author:dc
  213 + * @time 2024/1/2 14:57
  214 + */
  215 + public function getUsage(): array
  216 + {
  217 + return $this->usage;
  218 + }
  219 +
  220 +
  221 + /**
  222 + * 是否已经输出过头部了
  223 + * @var bool
  224 + */
  225 + protected static $isHeader = false;
  226 +
  227 + /**
  228 + * 是否是sse输出
  229 + * @var bool
  230 + */
  231 + public static $echoSse = false;
  232 +
  233 +
  234 + /**
  235 + * 设置头部
  236 + * @param false $sse
  237 + * @author:dc
  238 + * @time 2024/5/31 15:02
  239 + */
  240 + public static function setStreamHeader(array $header=[]){
  241 + // 默认配置的 头信息 输出一次即可
  242 + if(!self::$isHeader){
  243 + // 流输出 必须的 头信息
  244 + if(self::$echoSse) header("Content-Type:event-stream;Charset=UTF-8;");//event-stream 开启这个数据必须是规定格式
  245 + header("cache-control:no-cache;"); // 告诉浏览器不要进行数据缓存
  246 + header('X-Accel-Buffering: no'); // 关键是加了这一行。告诉浏览器不进行输出的缓冲
  247 + header('Access-Control-Expose-Headers: Content-Disposition, Content-Length, X-Content-Range, X-Duration');
  248 + header('Content-Type: application/json'); // json数据头
  249 + header('Access-Control-Allow-Origin:*'); // 这个是 跨域
  250 + self::$isHeader = true;
  251 + }
  252 +
  253 + // 输出其他header
  254 + foreach ($header as $head){
  255 + header($head);
  256 + }
  257 +
  258 + }
  259 +
  260 + /**
  261 + * 其他地方调用,在ai返回前后都可以调用这个
  262 + * @param $data
  263 + * @param string $type 数据类型
  264 + * @author:dc
  265 + * @time 2024/5/31 15:05
  266 + */
  267 + public static function echo_flush($data,string $type='text'){
  268 +
  269 + self::setStreamHeader();
  270 +
  271 + echo self::$echoSse ? en_sse_data($data,$type) : $data;
  272 +
  273 + ob_flush();
  274 + flush();
  275 + }
  276 +
  277 +
  278 + /**
  279 + * 输出 信息到前端
  280 + * @author:dc
  281 + * @time 2024/5/31 10:16
  282 + */
  283 + public function echo(){
  284 +
  285 + // 如果用户断开,继续脚本的运行
  286 + ignore_user_abort(1);
  287 + set_time_limit(400);
  288 +// // 先把之前的内容 也发送到浏览器
  289 +// @ob_implicit_flush(); // 开启隐式刷新 使用 echo函数时会立即发送到浏览器 开启后就不需要flush调用了
  290 + // 输出内容
  291 + $this->getStreamContent(function ($text) {
  292 + self::echo_flush($text);
  293 + });
  294 + if(self::$debugInfo){
  295 + self::echo_flush($this->endLine['debug']??[],'debug');
  296 + }
  297 + }
  298 +
  299 +
  300 +
  301 +
  302 +}
@@ -215,7 +215,7 @@ class CNoticeController extends BaseController @@ -215,7 +215,7 @@ class CNoticeController extends BaseController
215 $project_id = $this->user['project_id']; 215 $project_id = $this->user['project_id'];
216 $type = intval($request->input('type', 1)); 216 $type = intval($request->input('type', 1));
217 $route = intval($request->input('page', 1)); 217 $route = intval($request->input('page', 1));
218 - if(in_array($route,[4,6])){ 218 + if($type == 2 && in_array($route,[4,6])){
219 $this->fail('聚合页翻译请联系管理员'); 219 $this->fail('聚合页翻译请联系管理员');
220 } 220 }
221 $url = $request->input('url', []); 221 $url = $request->input('url', []);
@@ -51,14 +51,17 @@ class GoogleKeywordInsightController extends BaseController @@ -51,14 +51,17 @@ class GoogleKeywordInsightController extends BaseController
51 * @time :2025/4/1 9:12 51 * @time :2025/4/1 9:12
52 */ 52 */
53 public function getOptimizeList(){ 53 public function getOptimizeList(){
  54 + $this->request->validate([
  55 + 'field' => 'required'
  56 + ],[
  57 + 'field.required' => 'field不能为空',
  58 + ]);
54 $projectKeywordModel = new ProjectKeyword(); 59 $projectKeywordModel = new ProjectKeyword();
55 - $info = $projectKeywordModel->read(['project_id'=>$this->user['project_id']],['main_keyword','customer_keywords']); 60 + $info = $projectKeywordModel->read(['project_id'=>$this->user['project_id']],[$this->param['field']]);
56 if($info === false){ 61 if($info === false){
57 $this->response('success'); 62 $this->response('success');
58 } 63 }
59 - $main_keyword = explode("\r\n", $info['main_keyword']);  
60 - $customer_keywords = explode("\r\n", $info['customer_keywords']);  
61 - $array = array_merge($main_keyword, $customer_keywords); 64 + $array = explode("\r\n", $info[$this->param['field']]);
62 $detailModel = new GoogleKeywordInsightDetail(); 65 $detailModel = new GoogleKeywordInsightDetail();
63 $resultData = []; 66 $resultData = [];
64 if(!empty($array)){ 67 if(!empty($array)){
@@ -10,11 +10,14 @@ @@ -10,11 +10,14 @@
10 namespace App\Http\Controllers\Bside\GoogleKeyword; 10 namespace App\Http\Controllers\Bside\GoogleKeyword;
11 11
12 use App\Enums\Common\Code; 12 use App\Enums\Common\Code;
  13 +use App\Helper\Country;
  14 +use App\Helper\Translate;
13 use App\Http\Controllers\Bside\BaseController; 15 use App\Http\Controllers\Bside\BaseController;
14 use App\Models\Com\NoticeLog; 16 use App\Models\Com\NoticeLog;
  17 +use App\Models\GoogleSearch\GoogleCodeCountry;
15 use App\Models\GoogleSearch\GoogleSearch; 18 use App\Models\GoogleSearch\GoogleSearch;
16 use App\Models\GoogleSearch\GoogleSearchDetail; 19 use App\Models\GoogleSearch\GoogleSearchDetail;
17 -use App\Services\GoogleSearchService; 20 +use App\Services\RapIdApIService;
18 21
19 class GoogleSearchController extends BaseController 22 class GoogleSearchController extends BaseController
20 { 23 {
@@ -33,8 +36,25 @@ class GoogleSearchController extends BaseController @@ -33,8 +36,25 @@ class GoogleSearchController extends BaseController
33 ]); 36 ]);
34 //查询详情数据 37 //查询详情数据
35 $searchDetailModel = new GoogleSearchDetail(); 38 $searchDetailModel = new GoogleSearchDetail();
36 - $this->map['project_id']= $this->user['project_id'];  
37 - $data = $searchDetailModel->lists($this->map,$this->page,$this->row); 39 + $this->map['project_id']= 711;
  40 + $data = $searchDetailModel->lists($this->map,$this->page,$this->row,'impressions');
  41 + if(!empty($data)){
  42 + if($this->param['type'] == 'country'){
  43 + $codeCountryModel = new GoogleCodeCountry();
  44 + foreach ($data['list'] as $key => $val){
  45 + $val['zh_country'] = $codeCountryModel->getCodeCountry($val['keys']);
  46 + $val['click_rate'] = number_format($val['click_rate'] * 100, 2); // 保留 2 位小数
  47 + $val['impressions_rate'] = number_format($val['impressions_rate'] * 100, 2);
  48 + $data['list'][$key] = $val;
  49 + }
  50 + }else{
  51 + foreach ($data['list'] as $key => $val){
  52 + $val['click_rate'] = number_format($val['click_rate'] * 100, 2); // 保留 2 位小数
  53 + $val['impressions_rate'] = number_format($val['impressions_rate'] * 100, 2);
  54 + $data['list'][$key] = $val;
  55 + }
  56 + }
  57 + }
38 $this->response('success',Code::SUCCESS,$data); 58 $this->response('success',Code::SUCCESS,$data);
39 } 59 }
40 } 60 }
@@ -12,11 +12,42 @@ namespace App\Http\Controllers\Bside\Gpt; @@ -12,11 +12,42 @@ namespace App\Http\Controllers\Bside\Gpt;
12 use App\Enums\Common\Code; 12 use App\Enums\Common\Code;
13 use App\Http\Controllers\Bside\BaseController; 13 use App\Http\Controllers\Bside\BaseController;
14 use App\Http\Logic\Bside\Gpt\ChatLogic; 14 use App\Http\Logic\Bside\Gpt\ChatLogic;
  15 +use App\Models\Gpt\Chat;
  16 +use App\Models\Gpt\ChatItem;
15 17
16 class ChatController extends BaseController 18 class ChatController extends BaseController
17 { 19 {
18 /** 20 /**
19 - * @remark :发送消息 21 + * @remark :获取消息列表
  22 + * @name :list
  23 + * @author :lyh
  24 + * @method :post
  25 + * @time :2025/4/2 15:51
  26 + */
  27 + public function list(Chat $chat){
  28 + $list = $chat->lists(['user_id'=>$this->user['id'],'status'=>1],$this->page,$this->row);
  29 + $this->response('success',Code::SUCCESS,$list);
  30 + }
  31 +
  32 + /**
  33 + * @remark :获取所有子消息
  34 + * @name :itemList
  35 + * @author :lyh
  36 + * @method :post
  37 + * @time :2025/4/2 15:55
  38 + */
  39 + public function itemList(ChatItem $chatItem){
  40 + $this->request->validate([
  41 + 'chat_id'=>['required'],
  42 + ],[
  43 + 'chat_id.required' => 'chat_id不能为空',
  44 + ]);
  45 + $list = $chatItem->list(['user_id'=>$this->user['id'],'chat_id'=>$this->map['chat_id']],'id',['*'],'asc');
  46 + $this->response('success',Code::SUCCESS,$list);
  47 + }
  48 +
  49 + /**
  50 + * @remark :发送消息(流返回)
20 * @name :sendMessage 51 * @name :sendMessage
21 * @author :lyh 52 * @author :lyh
22 * @method :post 53 * @method :post
@@ -28,7 +59,23 @@ class ChatController extends BaseController @@ -28,7 +59,23 @@ class ChatController extends BaseController
28 ],[ 59 ],[
29 'message.required' => '消息内容不能为空', 60 'message.required' => '消息内容不能为空',
30 ]); 61 ]);
31 - $data = $logic->sendMessage(); 62 + return $logic->sendMessage();
  63 + }
  64 +
  65 + /**
  66 + * @remark :删除消息
  67 + * @name :del
  68 + * @author :lyh
  69 + * @method :post
  70 + * @time :2025/4/2 15:54
  71 + */
  72 + public function del(Chat $chat){
  73 + $this->request->validate([
  74 + 'id'=>['required'],
  75 + ],[
  76 + 'id.required' => 'id不能为空',
  77 + ]);
  78 + $data = $chat->edit(['status'=>0],['id'=>$this->param['id']]);
32 $this->response('success',Code::SUCCESS,$data); 79 $this->response('success',Code::SUCCESS,$data);
33 } 80 }
34 } 81 }
@@ -137,12 +137,8 @@ class SuppliersController extends BaseController @@ -137,12 +137,8 @@ class SuppliersController extends BaseController
137 if(isset($this->param['position'])){ 137 if(isset($this->param['position'])){
138 $param['position'] = $this->param['position']; 138 $param['position'] = $this->param['position'];
139 } 139 }
140 - try {  
141 $res = $this->_action($api_url,$action_name,$param); 140 $res = $this->_action($api_url,$action_name,$param);
142 $this->response('success',Code::SUCCESS,$res); 141 $this->response('success',Code::SUCCESS,$res);
143 - }catch (\Exception $e){  
144 - $this->fail('请求失败,请联系管理员');  
145 - }  
146 } 142 }
147 143
148 /** 144 /**
@@ -28,7 +28,7 @@ use App\Models\Project\Project; @@ -28,7 +28,7 @@ use App\Models\Project\Project;
28 use App\Models\Project\ProjectAiSetting; 28 use App\Models\Project\ProjectAiSetting;
29 use App\Models\RouteMap\RouteMap; 29 use App\Models\RouteMap\RouteMap;
30 use App\Services\AiBlogService; 30 use App\Services\AiBlogService;
31 -use App\Services\GoogleSearchService; 31 +use App\Services\RapIdApIService;
32 use App\Services\ProjectServer; 32 use App\Services\ProjectServer;
33 use Illuminate\Support\Facades\DB; 33 use Illuminate\Support\Facades\DB;
34 34
@@ -13,7 +13,7 @@ use App\Helper\Translate; @@ -13,7 +13,7 @@ use App\Helper\Translate;
13 use App\Http\Logic\Bside\BaseLogic; 13 use App\Http\Logic\Bside\BaseLogic;
14 use App\Models\GoogleKeywordInsight\GoogleKeywordInsight; 14 use App\Models\GoogleKeywordInsight\GoogleKeywordInsight;
15 use App\Models\GoogleKeywordInsight\GoogleKeywordInsightDetail; 15 use App\Models\GoogleKeywordInsight\GoogleKeywordInsightDetail;
16 -use App\Services\GoogleSearchService; 16 +use App\Services\RapIdApIService;
17 use Illuminate\Support\Facades\DB; 17 use Illuminate\Support\Facades\DB;
18 18
19 class GoogleKeywordInsightLogic extends BaseLogic 19 class GoogleKeywordInsightLogic extends BaseLogic
@@ -37,7 +37,7 @@ class GoogleKeywordInsightLogic extends BaseLogic @@ -37,7 +37,7 @@ class GoogleKeywordInsightLogic extends BaseLogic
37 public function getGoogleInsight(){ 37 public function getGoogleInsight(){
38 $data = $this->model->read(['search'=>$this->param['keyword']],['id']); 38 $data = $this->model->read(['search'=>$this->param['keyword']],['id']);
39 if($data === false){ 39 if($data === false){
40 - $this->service = new GoogleSearchService(); 40 + $this->service = new RapIdApIService();
41 $data = $this->service->requestUrl($this->param['keyword']); 41 $data = $this->service->requestUrl($this->param['keyword']);
42 if(!empty($data)){ 42 if(!empty($data)){
43 DB::beginTransaction(); 43 DB::beginTransaction();
@@ -64,7 +64,7 @@ class GoogleKeywordInsightLogic extends BaseLogic @@ -64,7 +64,7 @@ class GoogleKeywordInsightLogic extends BaseLogic
64 * @time :2025/3/25 14:36 64 * @time :2025/3/25 14:36
65 */ 65 */
66 public function getGoogleInsightDetail(){ 66 public function getGoogleInsightDetail(){
67 - $this->service = new GoogleSearchService(); 67 + $this->service = new RapIdApIService();
68 $data = $this->service->requestUrl($this->param['keyword']); 68 $data = $this->service->requestUrl($this->param['keyword']);
69 if(!empty($data)){ 69 if(!empty($data)){
70 DB::beginTransaction(); 70 DB::beginTransaction();
@@ -9,15 +9,20 @@ @@ -9,15 +9,20 @@
9 9
10 namespace App\Http\Logic\Bside\Gpt; 10 namespace App\Http\Logic\Bside\Gpt;
11 11
  12 +use App\Helper\Stream;
12 use App\Http\Logic\Bside\BaseLogic; 13 use App\Http\Logic\Bside\BaseLogic;
13 use App\Models\Gpt\Chat; 14 use App\Models\Gpt\Chat;
  15 +use App\Models\Gpt\ChatItem;
  16 +use App\Services\GptService;
14 17
15 class ChatLogic extends BaseLogic 18 class ChatLogic extends BaseLogic
16 { 19 {
17 public function __construct() 20 public function __construct()
18 { 21 {
19 parent::__construct(); 22 parent::__construct();
  23 + $this->param = $this->requestAll;
20 $this->model = new Chat(); 24 $this->model = new Chat();
  25 + $this->itemModel = new ChatItem();
21 } 26 }
22 27
23 /** 28 /**
@@ -27,19 +32,77 @@ class ChatLogic extends BaseLogic @@ -27,19 +32,77 @@ class ChatLogic extends BaseLogic
27 * @method :post 32 * @method :post
28 * @time :2025/4/2 10:01 33 * @time :2025/4/2 10:01
29 */ 34 */
30 - public function sendMessage(){  
31 - if(isset($this->param['chat_id'])){  
32 - $chatInfo = $this->model->read(['id'=>$this->param['chat_id']]);  
33 - if($chatInfo === false){  
34 - $id = $this->saveChat($this->param['message']);  
35 - }else{  
36 - $id = $chatInfo['id']; 35 + public function sendMessage()
  36 + {
  37 + $gptService = new GptService();
  38 + $message = [];
  39 + if (isset($this->param['chat_id'])) {
  40 + $chatInfo = $this->model->read(['id' => $this->param['chat_id']]);
  41 + if ($chatInfo !== false) {
  42 + $this->saveChatItem($chatInfo['id'], $this->param['message']);
  43 + // 获取最近 2 条对话记录
  44 + $list = $this->itemModel->list(['chat_id' => $chatInfo['id']], 'id', ['*'], 'desc', 2);
  45 + $message[] = ['role' => 'system', 'content' => "You are now the marketing customer service of 深圳创贸集团"];
  46 + foreach ($list as $val) {
  47 + if ($val['is_reply'] == 2) {
  48 + $message[] = ['role' => 'user', 'content' => $val['content']];
  49 + } else {
  50 + $message[] = ['role' => 'assistant', 'content' => $val['content']];
  51 + }
37 } 52 }
  53 + $message[] = ['role' => 'user', 'content' => $this->param['message']];
  54 + $chatId = $chatInfo['id'];
38 }else{ 55 }else{
39 - $id = $this->saveChat($this->param['message']); 56 + $chatId = $this->saveChat($this->param['message']);
  57 + $this->saveChatItem($chatId, $this->param['message']);
  58 + $message = [
  59 + ['role' => 'system', 'content' => "You are now the marketing customer service of 深圳创贸集团"],
  60 + ['role' => 'user', 'content' => $this->param['message']],
  61 + ];
  62 + }
  63 + } else {
  64 + $chatId = $this->saveChat($this->param['message']);
  65 + $this->saveChatItem($chatId, $this->param['message']);
  66 + $message = [
  67 + ['role' => 'system', 'content' => "You are now the marketing customer service of 深圳创贸集团"],
  68 + ['role' => 'user', 'content' => $this->param['message']],
  69 + ];
  70 + }
  71 + $data = ['message' => $message];
  72 + $stream = $gptService->get_ai_chat($data); // 获取流
  73 + header('Content-Type: text/event-stream');
  74 + header('Cache-Control: no-cache');
  75 + header('Connection: keep-alive');
  76 + $aiResponse = '';
  77 + $buffer = '';
  78 + while (!$stream->eof()) {
  79 + $chunk = $stream->read(1024);
  80 + $chunk = str_replace(chr(1), '', $chunk);
  81 + if ($chunk !== false) {
  82 + // 累积数据
  83 + $buffer .= $chunk;
  84 + // 持续解析完整的 JSON
  85 + while (preg_match('/^\{[^{}]*\}/', $buffer, $match)) {
  86 + $jsonStr = $match[0];
  87 + $jsonData = json_decode($jsonStr, true);
  88 + // 确保 JSON 解析成功
  89 + if (json_last_error() === JSON_ERROR_NONE) {
  90 + if (isset($jsonData['text'])) {
  91 + $aiResponse .= $jsonData['text'];
  92 + echo $gptService->en_sse_data(trim($jsonData['text']));
  93 + ob_flush();
  94 + flush();
  95 + }
  96 + // 移除已解析的 JSON,保留未完成的部分
  97 + $buffer = substr($buffer, strlen($jsonStr));
  98 + } else {
  99 + break;
  100 + }
  101 + }
40 } 102 }
41 -  
42 - 103 + }
  104 + $this->saveChatItem($chatId, $aiResponse, 1);
  105 + return true;
43 } 106 }
44 107
45 /** 108 /**
@@ -57,4 +120,22 @@ class ChatLogic extends BaseLogic @@ -57,4 +120,22 @@ class ChatLogic extends BaseLogic
57 ]; 120 ];
58 return $this->model->addReturnId($saveData); 121 return $this->model->addReturnId($saveData);
59 } 122 }
  123 +
  124 + /**
  125 + * @remark :消息详情表保存一条记录
  126 + * @name :saveChatItem
  127 + * @author :lyh
  128 + * @method :post
  129 + * @time :2025/4/2 13:45
  130 + */
  131 + public function saveChatItem($id,$message,$is_reply = 2){
  132 + //创建一个会话
  133 + $saveData = [
  134 + 'user_id'=>$this->user['id'],
  135 + 'is_reply'=>$is_reply,
  136 + 'chat_id'=>$id,
  137 + 'content'=>$message,
  138 + ];
  139 + return $this->itemModel->addReturnId($saveData);
  140 + }
60 } 141 }
  1 +<?php
  2 +/**
  3 + * @remark :
  4 + * @name :GoogleCodeCountry.php
  5 + * @author :lyh
  6 + * @method :post
  7 + * @time :2025/4/3 10:13
  8 + */
  9 +
  10 +namespace App\Models\GoogleSearch;
  11 +
  12 +use App\Models\Base;
  13 +
  14 +class GoogleCodeCountry extends Base
  15 +{
  16 + protected $table = 'gl_google_code_country';
  17 +
  18 + /**
  19 + * @remark :获取国家中文
  20 + * @name :getCodeCountry
  21 + * @author :lyh
  22 + * @method :post
  23 + * @time :2025/4/3 10:26
  24 + */
  25 + public function getCodeCountry($code){
  26 + $info = $this->read(['code'=>$code],['zh_country']);
  27 + if($info !== false){
  28 + return $info['zh_country'];
  29 + }
  30 + return '未知国家';
  31 + }
  32 +}
@@ -23,37 +23,46 @@ class GptService @@ -23,37 +23,46 @@ class GptService
23 * @time :2025/4/2 9:38 23 * @time :2025/4/2 9:38
24 * 24 *
25 */ 25 */
26 - public function get_ai_chat($data,$type = 1){  
27 - // 组装请求体参数  
28 - // $data['message'] = [  
29 - // ['role' => 'system', 'content' => "You are now the marketing customer service of 深圳创贸集团"],  
30 - // ['role' => 'user', 'content' => '创贸集团有多少技术?'],  
31 - // ['role' => 'assistant', 'content' => '创贸集团有200+技术。'],  
32 - // ['role' => 'user', 'content' => '今天天气怎么样']  
33 - // ]; 26 + public function get_ai_chat($data,$type = 0){
34 $apikey = env('AI_CREATE_KEY')??'7yn!We6$&NnVA38bpGy*A@4TQ5iYLJcW'; 27 $apikey = env('AI_CREATE_KEY')??'7yn!We6$&NnVA38bpGy*A@4TQ5iYLJcW';
35 $client = new CmerClient($apikey); 28 $client = new CmerClient($apikey);
36 // 修改超时时间,默认60秒 29 // 修改超时时间,默认60秒
37 - $client->timeout=300;  
38 - $payload = new ChatModel($data['messages']); 30 + $client->timeout = 300;
  31 + $payload = new ChatModel($data['message']);
39 // 修改模型名称,豆包,Gpt,Claude 32 // 修改模型名称,豆包,Gpt,Claude
40 $payload->model = env('CHAT_GTP_MODEL','gpt-4o-mini');; 33 $payload->model = env('CHAT_GTP_MODEL','gpt-4o-mini');;
41 - $payload->supplier = isset($data['supplier'])?$data['supplier']:"openai"; 34 + $payload->supplier = isset($data['supplier'])?$data['supplier']:"azure";
42 //发送请求 35 //发送请求
43 if($type == 1){//返回数据 36 if($type == 1){//返回数据
44 $response = $client->chat($payload); 37 $response = $client->chat($payload);
45 - $result=$response->getBody()->getContents(); 38 + $result = $response->getBody()->getContents();
  39 + @file_put_contents(storage_path('logs/lyh_error.log'), var_export($result, true) . PHP_EOL, FILE_APPEND);
46 if(!$result){ 40 if(!$result){
47 Log::info('ai接口返回错误信息:'.$result. PHP_EOL); 41 Log::info('ai接口返回错误信息:'.$result. PHP_EOL);
48 return false; 42 return false;
49 } 43 }
50 return json_decode($result,true); 44 return json_decode($result,true);
51 }else { 45 }else {
52 - //发送流式请求 46 + // **流式请求**
53 $payload->stream = true; 47 $payload->stream = true;
54 $response = $client->chat($payload); 48 $response = $client->chat($payload);
55 - $body = $response->getBody();  
56 - return $body; 49 + $stream = $response->getBody();
  50 + return $stream;
57 } 51 }
58 } 52 }
  53 +
  54 + /**
  55 + * @remark :返回格式
  56 + * @name :en_sse_data
  57 + * @author :lyh
  58 + * @method :post
  59 + * @time :2025/4/2 16:56
  60 + */
  61 + public function en_sse_data($body, string $type='text'){
  62 + return 'data:'.json_encode([
  63 + 'id' => md5(is_array($body) ? json_encode($body) : $body),
  64 + 'data' => $body,
  65 + 'type' => $type
  66 + ],JSON_UNESCAPED_UNICODE)."\n\n";
  67 + }
59 } 68 }
1 <?php 1 <?php
2 /** 2 /**
3 * @remark : 3 * @remark :
4 - * @name :GoogleSearchService.php 4 + * @name :RapIdApIService.php
5 * @author :lyh 5 * @author :lyh
6 * @method :post 6 * @method :post
7 * @time :2025/3/25 11:36 7 * @time :2025/3/25 11:36
@@ -13,15 +13,17 @@ use App\Helper\Country; @@ -13,15 +13,17 @@ use App\Helper\Country;
13 13
14 /** 14 /**
15 * @remark :google关键字扩展 15 * @remark :google关键字扩展
16 - * @name :GoogleSearchService 16 + * @name :RapIdApIService
17 * @author :lyh 17 * @author :lyh
18 * @method :post 18 * @method :post
19 * @time :2025/3/25 11:38 19 * @time :2025/3/25 11:38
20 */ 20 */
21 -class GoogleSearchService 21 +class RapIdApIService
22 { 22 {
23 public $url = ""; 23 public $url = "";
24 24
  25 + public $key = '3eba1ba999msh3a7c11101a7e298p19924bjsn5089487f7c37';
  26 +
25 /** 27 /**
26 * @remark :扩展关键词请求数据 28 * @remark :扩展关键词请求数据
27 * @name :requestUrl 29 * @name :requestUrl
@@ -67,7 +69,7 @@ class GoogleSearchService @@ -67,7 +69,7 @@ class GoogleSearchService
67 CURLOPT_CUSTOMREQUEST => "GET", 69 CURLOPT_CUSTOMREQUEST => "GET",
68 CURLOPT_HTTPHEADER => [ 70 CURLOPT_HTTPHEADER => [
69 "x-rapidapi-host: google-keyword-insight1.p.rapidapi.com", 71 "x-rapidapi-host: google-keyword-insight1.p.rapidapi.com",
70 - "x-rapidapi-key: d246239565mshc29088b58ff484dp17c0bdjsn2d28d03622c7" 72 + "x-rapidapi-key: $this->key"
71 ], 73 ],
72 ]); 74 ]);
73 $response = curl_exec($curl); 75 $response = curl_exec($curl);
@@ -712,6 +712,13 @@ Route::middleware(['bloginauth'])->group(function () { @@ -712,6 +712,13 @@ Route::middleware(['bloginauth'])->group(function () {
712 Route::any('/getEnterProduct', [\App\Http\Controllers\Bside\SeoSetting\EnterpriseProductController::class, 'getEnterProduct'])->name('enterprise_product_getEnterProduct'); 712 Route::any('/getEnterProduct', [\App\Http\Controllers\Bside\SeoSetting\EnterpriseProductController::class, 'getEnterProduct'])->name('enterprise_product_getEnterProduct');
713 Route::any('/del', [\App\Http\Controllers\Bside\SeoSetting\EnterpriseProductController::class, 'del'])->name('enterprise_product_del'); 713 Route::any('/del', [\App\Http\Controllers\Bside\SeoSetting\EnterpriseProductController::class, 'del'])->name('enterprise_product_del');
714 }); 714 });
  715 + //大模型会话
  716 + Route::prefix('gpt')->group(function () {
  717 + Route::any('/', [\App\Http\Controllers\Bside\Gpt\ChatController::class, 'list'])->name('gpt_list');
  718 + Route::any('/itemList', [\App\Http\Controllers\Bside\Gpt\ChatController::class, 'itemList'])->name('gpt_itemList');
  719 + Route::any('/del', [\App\Http\Controllers\Bside\Gpt\ChatController::class, 'del'])->name('gpt_del');
  720 + Route::any('/sendMessage', [\App\Http\Controllers\Bside\Gpt\ChatController::class, 'sendMessage'])->name('gpt_sendMessage');
  721 + });
715 }); 722 });
716 //无需登录验证的路由组 723 //无需登录验证的路由组
717 Route::group([], function () { 724 Route::group([], function () {