curlOpen($url); $res = json_decode($res,true); if(isset($res['errmsg'])) return [false, $res['errmsg']]; if(! isset($res['access_token'])) return [false, 'request error']; $token = $res['access_token']; $expire_time = $res['expires_in']-300; Redis::set($token_key,$token); Redis::expire($token_key, $expire_time); return [true,$token]; } return [true, $token]; } public function getPublicWxArticle($data){ list($status, $msg) = $this->rule($data); if(! $status) { file_put_contents('record_ip.txt',date("Y-m-d H:i:s",time()).json_encode($data) . PHP_EOL."来源IP".$msg.PHP_EOL,8); return [false, 'IP未入白名单']; } list($status, $msg) = $this->getToken(); if(! $status) return [false, $msg]; $config = config('qingyaoWx'); $url = sprintf($config['get_article'], $msg); $offset = empty($data['page_index']) ? 0 : $data['page_index'] - 1; $count = empty($data['page_size']) || $data['page_size'] > 10 ? 10 : $data['page_size']; $post = [ 'offset' => $offset, 'count' => $count, 'no_content' => 0, ]; $result = $this->curlOpen($url, ['post' => json_encode($post)]); $result = json_decode($result,true); if(isset($result['errmsg'])) return [false, $result['errmsg']]; Log::channel('apiLog')->info('wxget', ["message" => $result]); return [true, ['data' => $result['item'] ?? [], 'total' => $result['total_count'], 'data_count' => $result['item_count']]]; } public function getPublicWxArticleDetail($data){ list($status, $msg) = $this->rule($data); if(! $status) { file_put_contents('record_ip.txt',date("Y-m-d H:i:s",time()).json_encode($data) . PHP_EOL."来源IP".$msg.PHP_EOL,8); return [false, 'IP未入白名单']; } if(empty($data['article_id'])) return [false, '文章ID不能为空']; list($status, $msg) = $this->getToken(); if(! $status) return [false, $msg]; $config = config('qingyaoWx'); $url = sprintf($config['get_article_detail'], $msg); $post = [ 'article_id' => $data['article_id'], ]; $result = $this->curlOpen($url, ['post' => json_encode($post)]); $result = json_decode($result,true); if(isset($result['errmsg'])) return [false, $result['errmsg']]; return [true, ['data' => $result['news_item'] ?? [] ]]; } public function getPublicWxMaterial($data){ list($status, $msg) = $this->rule($data); if(! $status) { file_put_contents('record_ip.txt',date("Y-m-d H:i:s",time()).json_encode($data) . PHP_EOL."来源IP".$msg.PHP_EOL,8); return [false, 'IP未入白名单']; } list($status, $msg) = $this->getToken(); if(! $status) return [false, $msg]; $config = config('qingyaoWx'); $url = sprintf($config['get_material'], $msg); $offset = empty($data['page_index']) ? 1 : $data['page_index'] - 1; $count = empty($data['page_size']) || $data['page_size'] > 10 ? 10 : $data['page_size']; $post = [ 'offset' => $offset, 'count' => $count, 'type' => 'news', ]; $result = $this->curlOpen($url, ['post' => json_encode($post)]); $result = json_decode($result,true); if(isset($result['errmsg'])) return [false, $result['errmsg']]; return [true, ['data' => $result['item'] ?? [], 'total' => $result['total_count'], 'data_count' => $result['item_count']]]; } public function getPublicWxDraft($data){ list($status, $msg) = $this->rule($data); if(! $status) { file_put_contents('record_ip.txt',date("Y-m-d H:i:s",time()).json_encode($data) . PHP_EOL."来源IP".$msg.PHP_EOL,8); return [false, 'IP未入白名单']; } list($status, $msg) = $this->getToken(); if(! $status) return [false, $msg]; $config = config('qingyaoWx'); $url = sprintf($config['get_draft'], $msg); $offset = empty($data['page_index']) ? 1 : $data['page_index'] - 1; $count = empty($data['page_size']) || $data['page_size'] > 10 ? 10 : $data['page_size']; $post = [ 'offset' => $offset, 'count' => $count, 'no_content' => 0, ]; $result = $this->curlOpen($url, ['post' => json_encode($post)]); $result = json_decode($result,true); if(isset($result['errmsg'])) return [false, $result['errmsg']]; return [true, ['data' => $result['item'] ?? [], 'total' => $result['total_count'], 'data_count' => $result['item_count']]]; } public function getWxFile($data){ list($status, $msg) = $this->rule($data); if(! $status) { file_put_contents('record_ip.txt',date("Y-m-d H:i:s",time()).json_encode($data) . PHP_EOL."来源IP".$msg.PHP_EOL,8); return [false, 'IP未入白名单']; } if(empty($data['wx_url'])) return [false, "URL不存在"]; $header = ['Content-Type:application/json']; list($status,$msg) = $this->get_helper_for_img($data['wx_url'],$header); if(! $status) return [false, $msg]; return [true, $msg]; } public function rule($data){ // 获取用户的IP地址 $userIP = $_SERVER['REMOTE_ADDR']; // 获取设置的IP地址 $allowedIPs = $this->allowedIPs(); if(empty($allowedIPs)) return [false, $userIP]; // 校验用户IP是否在允许的范围内 $isValidIP = false; foreach ($allowedIPs as $allowedIP) { if (strpos($allowedIP, '/') !== false) { // IP段表示法校验 list($subnet, $mask) = explode('/', $allowedIP); if ((ip2long($userIP) & ~((1 << (32 - $mask)) - 1)) == ip2long($subnet)) { $isValidIP = true; break; } } else { // 单个IP地址校验 if ($allowedIP === $userIP) { $isValidIP = true; break; } } } return [$isValidIP, $userIP]; } public function allowedIPs(){ $allowedIPs = Settings::where('setting_name','allowedIPs')->first(); if(empty($allowedIPs) || empty($allowedIPs->setting_value)) return []; return explode(',',$allowedIPs->setting_value); } public function get_helper_for_img($url,$header=[],$timeout = 20){ $ch = curl_init(); curl_setopt_array($ch, array( CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_MAXREDIRS => 10, CURLOPT_TIMEOUT => $timeout, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, CURLOPT_CUSTOMREQUEST => 'GET', CURLOPT_SSL_VERIFYPEER => false, CURLOPT_HTTPHEADER => $header, )); $r = curl_exec($ch); if ($r === false) { // 获取错误号 $errorNumber = curl_errno($ch); // 获取错误信息 $errorMessage = curl_error($ch); $message = "cURL Error #{$errorNumber}: {$errorMessage}"; Log::channel('apiLog')->info('wx', ["message" => $message]); return [false, $message]; } $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); // 检查是否为图片 if (! $this->isImage($r)) return [false, "资源不是图片"]; if ($httpCode == 200) { // 检查是否为图片 list($status, $msg) = $this->getTemporaryUrl($r, $url); if(! $status) return [false, $msg]; $img = $msg; } else { return [false, '']; } return [true, $img]; } function isImage($data) { $imageInfo = getimagesizefromstring($data); return $imageInfo !== false; } //生成临时文件 public function getTemporaryUrl($body,$url) { // 定义本地文件路径 $name = md5($url) . '.jpg'; $localFilePath = storage_path(self::wx_img . $name); // 写入文件前先检查文件是否存在 if (! file_exists($localFilePath)) { // 检查目录是否存在,如果不存在则创建 $directoryPath = dirname($localFilePath); if (!is_dir($directoryPath)) { // 设置目录权限,可以根据需要更改 $mode = 0777; // 使用递归选项创建目录 if (!mkdir($directoryPath, $mode, true)) { return [false, '目录创建失败']; } } file_put_contents($localFilePath, $body); } return [true, $name]; } public function getArticle(){ list($status, $msg) = $this->getToken(); if(! $status) return [false, $msg]; $config = config('qingyaoWx'); $url = sprintf($config['get_article'], $msg); $offset = 0; $count = 20; $get_total = 0; do { $post = [ 'offset' => $offset, 'count' => $count, 'no_content' => 0, ]; $result = $this->curlOpen($url, ['post' => json_encode($post)]); $result = json_decode($result, true); Log::channel('apiLog')->info('wxget', ["message" => $result]); if (isset($result['errmsg'])) return [false, $result['errmsg']]; //保存文章 list($status,$msg) = $this->saveWxArticle($result); if(! $status) return [false, $msg]; // 更新 offset $offset += 1; // 更新 total_count $get_total += $result['total_count']; // 检查是否还有更多数据 $hasMore = isset($result['total_count']) && ($result['total_count'] > $get_total); } while ($hasMore); return [true, '']; } public function saveWxArticle($result){ if(! empty($result['item'])){ try { DB::beginTransaction(); foreach ($result['item'] as $value){ $model = WxArticle::where('del_time',0) ->where('article_id', $value['article_id']) ->first(); if(empty($model)){ $model = new WxArticle(); $model->article_id = $value['article_id']; $model->upd_time = $value['update_time']; $model->save(); $id = $model->id; if(! empty($value['content']['news_item'])){ foreach ($value['content']['news_item'] as $val){ // 获取当前时间的微秒级时间戳 list($micro, $seconds) = explode(' ', microtime()); // 从微秒部分截取全部6位作为唯一标识符 $microSuffix = substr($micro, 2, 6); $content_name = date('YmdHis', $seconds) . $microSuffix; $this->saveContent($val['content'], $content_name); $this->get_helper_for_img($val['thumb_url']); $modelDetail = new WxArticleDetail(); $modelDetail->wx_article_id = $id; $modelDetail->title = $val['title']; $modelDetail->author = $val['author']; $modelDetail->digest = $val['digest']; $modelDetail->content_name = $content_name; $modelDetail->content_source_url = $val['content_source_url']; $modelDetail->thumb_media_id = $val['thumb_media_id']; $modelDetail->show_cover_pic = $val['show_cover_pic']; $modelDetail->url = $val['url']; $modelDetail->thumb_url = $val['thumb_url']; $modelDetail->need_open_comment = $val['need_open_comment']; $modelDetail->only_fans_can_comment = $val['only_fans_can_comment']; $modelDetail->is_deleted = $val['is_deleted']; $modelDetail->upd_time = $value['content']['update_time']; $modelDetail->save(); } } }else{ if($model->upd_time != $value['update_time']){ $model->upd_time = $value['update_time']; $model->save(); $id = $model->id; $detail = WxArticleDetail::where('del_time',0) ->where('wx_article_id',$id) ->select('content_name','thumb_url') ->get()->toArray(); WxArticleDetail::where('del_time',0) ->where('wx_article_id',$id) ->update(['del_time' => time()]); if(! empty($value['content']['news_item'])){ foreach ($value['content']['news_item'] as $val){ // 获取当前时间的微秒级时间戳 list($micro, $seconds) = explode(' ', microtime()); // 从微秒部分截取全部6位作为唯一标识符 $microSuffix = substr($micro, 2, 6); $content_name = date('YmdHis', $seconds) . $microSuffix; $this->saveContent($val['content'], $content_name); $this->get_helper_for_img($val['thumb_url']); $modelDetail = new WxArticleDetail(); $modelDetail->wx_article_id = $id; $modelDetail->title = $val['title']; $modelDetail->author = $val['author']; $modelDetail->digest = $val['digest']; $modelDetail->content_name = $content_name; $modelDetail->content_source_url = $val['content_source_url']; $modelDetail->thumb_media_id = $val['thumb_media_id']; $modelDetail->show_cover_pic = $val['show_cover_pic']; $modelDetail->url = $val['url']; $modelDetail->thumb_url = $val['thumb_url']; $modelDetail->need_open_comment = $val['need_open_comment']; $modelDetail->only_fans_can_comment = $val['only_fans_can_comment']; $modelDetail->is_deleted = $val['is_deleted']; $modelDetail->upd_time = $value['content']['update_time']; $modelDetail->save(); } } $this->delFile($detail); } } } DB::commit(); return [true, '']; }catch (\Throwable $exception){ DB::rollBack(); return [false, $exception->getMessage()]; } } return [false, '暂无数据']; } //生成临时文件 public function saveContent($content, $name) { // 定义本地文件路径 $name = $name . '.txt'; $localFilePath = storage_path(self::wx_img . $name); // 写入文件前先检查文件是否存在 if (! file_exists($localFilePath)) { // 检查目录是否存在,如果不存在则创建 $directoryPath = dirname($localFilePath); if (!is_dir($directoryPath)) { // 设置目录权限,可以根据需要更改 $mode = 0777; // 使用递归选项创建目录 if (!mkdir($directoryPath, $mode, true)) { return [false, '目录创建失败']; } } file_put_contents($localFilePath, $content); } return [true, $name]; } public function delFile($detail){ if(empty($detail)) return; $dir = 'wx_img/'; foreach ($detail as $value){ $dir_content = $dir . $value['content_name'] . '.txt'; if(Storage::disk('public')->exists($dir_content)) Storage::disk('public')->delete($dir_content); if(! empty($value['thumb_url'])){ $dir_thumb = $dir . md5($value['thumb_url']) . '.jpg'; if(Storage::disk('public')->exists($dir_thumb)) Storage::disk('public')->delete($dir_thumb); } } } }