Files
YwxAppThink/addon/haonav/controller/Widget.php
T

172 lines
5.6 KiB
PHP

<?php
// +----------------------------------------------------------------------
// | YwxApp [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: ywxapp<admin@ywxapp.cn>
// +----------------------------------------------------------------------
declare(strict_types=1);
namespace addon\haonav\controller;
use ywxapp\controller\FrontendBase;
use think\facade\Cache;
use addon\haonav\model\Configure as ConfigureModel;
/**
* 首页小组件:实时热搜榜 + 天气(服务端代理 + 缓存,规避跨域并保护第三方接口)
*
* @author ywxapp <admin@ywxapp.cn>
*/
class Widget extends FrontendBase
{
protected $noNeedLogin = ['*'];
protected $noNeedVerify = ['*'];
protected function initialize()
{
}
/**
* 实时热搜榜(缓存 10 分钟)
* GET /haonav/widget/hot?source=baidu|weibo|zhihu
*/
public function hot()
{
if ((int)ConfigureModel::getVal('enable_hot_api', '0') !== 1) {
return $this->result->success([], '未开启');
}
$source = $this->request->param('source', ConfigureModel::getVal('hot_api_source', 'baidu'));
$source = in_array($source, ['baidu', 'weibo', 'zhihu'], true) ? $source : 'baidu';
$cacheKey = 'haonav_hot_' . $source;
$data = Cache::get($cacheKey);
if (is_array($data)) {
return $this->result->success($data, '缓存');
}
$map = [
'baidu' => 'https://api.vvhan.com/api/hotlist/baiduRD',
'weibo' => 'https://api.vvhan.com/api/hotlist/wbHot',
'zhihu' => 'https://api.vvhan.com/api/hotlist/zhihuHot',
];
$list = $this->fetchHot($map[$source]);
// 即便为空也短缓存,避免频繁打第三方
Cache::set($cacheKey, $list, $list ? 600 : 120);
return $this->result->success($list);
}
/**
* 天气(缓存 30 分钟)
* GET /haonav/widget/weather?city=北京
*/
public function weather()
{
if ((int)ConfigureModel::getVal('enable_weather', '0') !== 1) {
return $this->result->success([], '未开启');
}
$city = trim((string)$this->request->param('city', ConfigureModel::getVal('weather_city', '')));
$cacheKey = 'haonav_weather_' . ($city !== '' ? md5($city) : 'auto');
$data = Cache::get($cacheKey);
if (is_array($data) && $data) {
return $this->result->success($data, '缓存');
}
$info = $this->fetchWeather($city);
if ($info) {
Cache::set($cacheKey, $info, 1800);
return $this->result->success($info);
}
return $this->result->success([], '暂无数据');
}
/**
* 拉取并归一化热搜列表 -> [ {title,url,hot}, ... ]
*/
protected function fetchHot(string $api): array
{
$json = $this->httpGet($api);
if (!$json) {
return [];
}
$arr = json_decode($json, true);
if (!is_array($arr)) {
return [];
}
$rows = $arr['data'] ?? ($arr['result'] ?? []);
if (!is_array($rows)) {
return [];
}
$out = [];
foreach ($rows as $r) {
if (!is_array($r)) {
continue;
}
$title = $r['title'] ?? ($r['name'] ?? '');
if ($title === '') {
continue;
}
$out[] = [
'title' => (string)$title,
'url' => (string)($r['url'] ?? ($r['mobil_url'] ?? '')),
'hot' => (string)($r['hot'] ?? ($r['num'] ?? '')),
];
if (count($out) >= 20) {
break;
}
}
return $out;
}
/**
* 拉取并归一化天气 -> {city,type,low,high,tips,...}
*/
protected function fetchWeather(string $city)
{
$api = 'https://api.vvhan.com/api/weather';
if ($city !== '') {
$api .= '?city=' . urlencode($city);
}
$json = $this->httpGet($api);
if (!$json) {
return false;
}
$arr = json_decode($json, true);
if (!is_array($arr) || empty($arr['success'])) {
return false;
}
$today = $arr['data'][0] ?? [];
return [
'city' => (string)($arr['city'] ?? $city),
'date' => (string)($today['date'] ?? ''),
'type' => (string)($today['type'] ?? ''),
'low' => (string)($today['low'] ?? ''),
'high' => (string)($today['high'] ?? ''),
'fx' => (string)($today['fengxiang'] ?? ''),
'tips' => (string)($arr['tip'] ?? ''),
];
}
/**
* 轻量 GET(优先 Guzzle,失败回退 file_get_contents),失败返回 ''
*/
protected function httpGet(string $url): string
{
try {
if (class_exists(\GuzzleHttp\Client::class)) {
$client = new \GuzzleHttp\Client(['timeout' => 5, 'verify' => false]);
$resp = $client->get($url, ['headers' => ['Member-Agent' => 'Mozilla/5.0']]);
return (string)$resp->getBody();
}
$ctx = stream_context_create(['http' => ['timeout' => 5, 'header' => "Member-Agent: Mozilla/5.0\r\n"]]);
$body = @file_get_contents($url, false, $ctx);
return $body === false ? '' : (string)$body;
} catch (\Throwable $e) {
return '';
}
}
}