This commit is contained in:
Your Name
2026-04-21 01:02:21 +08:00
parent e568577ce0
commit b943ea2ab7
8 changed files with 1119 additions and 14 deletions

View File

@@ -43,8 +43,8 @@ if ($strTmpCode == 'videoGpt1') {
return view('rss/baidu.xml')->contentType('text/xml');
})->ext('xml');
$routeGetHead('rss/so', function () {
return view('rss/so.xml')->contentType('text/xml');
$routeGetHead('rss/so', function (\think\Request $Request, SiteContext $SiteContext) {
return $SiteContext->getSoSitemapResponse();
})->ext('xml');
$routeGetHead('/sitemap_index', function (\think\Request $Request, SiteContext $SiteContext) {
@@ -413,9 +413,8 @@ if ($strTmpCode == 'videoGpt1') {
/**
* /rss/so
*/
Route::get('rss/so', function () {
return view('rss/so.xml')
->contentType('text/xml');
Route::get('rss/so', function (\think\Request $Request, SiteContext $SiteContext) {
return $SiteContext->getSoSitemapResponse();
})->ext('xml');
/**

View File

@@ -1017,9 +1017,11 @@ class DomainModel extends BaseModel
}
if (!is_array($this->t_cfg) || !array_key_exists($strKey, $this->t_cfg)) {
$strFallbackValue = $this->resolveLegacyTemplateFallback((string)$strKey);
if ($strFallbackValue !== null) {
return $strFallbackValue;
}
return '';
$strError = sprintf("t_cfg [%s] not found! %s ", $strKey , $strDomain . '-' . $strController . '-' . $strAction. '-' . $strDiff. '-' . $boolJoin.'-'.request()->url);
throw new \Exception($strError);
}
$intSfgId = $this->t_cfg[$strKey]['sfg_id'];
@@ -1070,13 +1072,83 @@ class DomainModel extends BaseModel
$strError = sprintf("subject fomart [%s] not found !", $strKey);
throw new \Exception($strError);
}
return $SubjectFomartModel->sf_val;
}
/**
* Undocumented function
*
return $SubjectFomartModel->sf_val;
}
protected function resolveLegacyTemplateFallback(string $strKey): ?string
{
if (!is_array($this->t_cfg) || !isset($this->t_cfg['pages'], $this->t_cfg['url_family'])) {
return null;
}
$arrSeoKeyMap = [
'VIDEO@INDEX@INDEX@TITLE' => ['title', 'home'],
'VIDEO@INDEX@INDEX@KEYWORDS' => ['keywords', 'home'],
'VIDEO@INDEX@INDEX@DESCRIPTION' => ['description', 'home'],
'VIDEO@GETCATEGORYINDEX@TITLE' => ['title', 'category_index'],
'VIDEO@GETCATEGORYINDEX@KEYWORDS' => ['keywords', 'category_index'],
'VIDEO@GETCATEGORYINDEX@DESCRIPTION' => ['description', 'category_index'],
'VIDEO@GETCATEGORY@TITLE' => ['title', 'category_list'],
'VIDEO@GETCATEGORY@KEYWORDS' => ['keywords', 'category_list'],
'VIDEO@GETCATEGORY@DESCRIPTION' => ['description', 'category_list'],
'VIDEO@GETSEARCHVIDEO@TITLE' => ['title', 'search'],
'VIDEO@GETSEARCHVIDEO@KEYWORDS' => ['keywords', 'search'],
'VIDEO@GETSEARCHVIDEO@DESCRIPTION' => ['description', 'search'],
'VIDEO@GETVIDEORANKINDEX@TITLE' => ['title', 'rank_index'],
'VIDEO@GETVIDEORANKINDEX@KEYWORDS' => ['keywords', 'rank_index'],
'VIDEO@GETVIDEORANKINDEX@DESCRIPTION' => ['description', 'rank_index'],
'VIDEO@GETVIDEORANKLIST@TITLE' => ['title', 'rank_list'],
'VIDEO@GETVIDEORANKLIST@KEYWORDS' => ['keywords', 'rank_list'],
'VIDEO@GETVIDEORANKLIST@DESCRIPTION' => ['description', 'rank_list'],
'VIDEO@GETVIDEOINFO@TITLE' => ['title', 'detail'],
'VIDEO@GETVIDEOINFO@KEYWORDS' => ['keywords', 'detail'],
'VIDEO@GETVIDEOINFO@DESCRIPTION' => ['description', 'detail'],
'VIDEO@GETVIDEOPLAY@TITLE' => ['title', 'play'],
'VIDEO@GETVIDEOPLAY@KEYWORDS' => ['keywords', 'play'],
'VIDEO@GETVIDEOPLAY@DESCRIPTION' => ['description', 'play'],
];
if (isset($arrSeoKeyMap[$strKey])) {
[$strSeoCode, $strSeoPage] = $arrSeoKeyMap[$strKey];
try {
$SiteContext = \think\Container::getInstance()->make(\app\services\SiteContext::class);
return (string)$SiteContext->getSeoTkd($strSeoCode, $strSeoPage);
} catch (\Throwable $Throwable) {
return '';
}
}
try {
$arrRuntimeStyle = \app\common\helper\SiteStyle::getConfig($this, (string)($this->d_domain ?? ''));
$UrlBuilder = new \app\common\helper\UrlBuilder($arrRuntimeStyle);
} catch (\Throwable $Throwable) {
return '';
}
$strTemplateCode = (string)(request()->TemplatesModel->t_code ?? '');
if ($strTemplateCode !== '' && $strTemplateCode !== 'videoGpt1') {
return match ($strKey) {
'VIDEO_RANK_INDEX_URL' => '/phb-index',
'VIDEO_SEARCH_LIST_URL' => '/query.html',
'VIDEO_HISTORY_LIST_URL' => '/history.html',
default => null,
};
}
return match ($strKey) {
'VIDEO_CATEGORY_INDEX_URL' => $UrlBuilder->categoryHome(),
'VIDEO_RANK_INDEX_URL' => $UrlBuilder->rankIndex(),
'VIDEO_SEARCH_LIST_URL' => $UrlBuilder->searchEntry(),
'VIDEO_HISTORY_LIST_URL' => $UrlBuilder->history(),
default => null,
};
}
/**
* Undocumented function
*
* @param string $strKey
* @return string
*/

View File

@@ -172,6 +172,53 @@ class VideoModel extends MongoModel
return $this->findManyWithCache($arrFilter, $intCount, $arrSort, $arrOptions, $strKey, $intLifeTime);
}
/**
* 轻量级 sitemap / rss 列表,只取生成 URL 需要的字段,避免大对象缓存撑爆内存。
*/
public function findSitemapItemsWithCache(
array $arrFilter,
int $intCount = 1000,
array $arrSort = [],
string $strKey = '',
int $intLifeTime = 24 * 3600
): array {
try {
$arrResult = $this->checkCacheByKey($strKey);
if ($arrResult !== NULL) {
return is_array($arrResult) ? $arrResult : [];
}
$arrOptions = self::$arrOptions;
$arrOptions['projection'] = [
'_id' => 0,
'v_id' => 1,
'v_name_en' => 1,
'v_publish_date' => 1,
'created_at' => 1,
'updated_at' => 1,
];
$arrOptions['limit'] = max(1, $intCount);
if (!empty($arrSort)) {
$arrOptions['sort'] = $arrSort;
}
$Cursor = $this->getCol()->find($arrFilter, $arrOptions);
$arrResult = iterator_to_array($Cursor);
foreach ($arrResult as &$arrItem) {
applyToKeys($arrItem, ['created_at', 'updated_at'], 'formatMongoDate');
}
unset($arrItem);
$this->setCacheByKey($strKey, $arrResult, $intLifeTime);
return $arrResult;
} catch (\Throwable $Throwable) {
return [];
}
}
/**
* find one Video
*

View File

@@ -13,6 +13,7 @@ use app\model\ChapterModel;
use app\model\SystemConfigModel;
use app\model\NovelClicksModel;
use app\model\NovelModel;
use app\model\VideoModel;
use app\common\helper\SiteStyle;
use app\common\helper\CssBuilder;
use app\common\helper\JsBuilder;
@@ -566,6 +567,72 @@ class SiteContext
exit;
}
/**
* 兼容旧站的 /rss/so.xml。
* 避免走模板标签 video:list 一次性读取/缓存大量完整视频文档导致内存溢出。
*/
public function getSoSitemapResponse(): Response
{
$strDomain = trim((string)($this->DomainModel->d_domain ?? ''));
$strHost = $strDomain === '' ? trim((string)$this->Request->host()) : $strDomain;
$strCacheKey = sprintf('SiteContext:getSoSitemapResponse:%s', $strHost);
$arrRows = VideoModel::getInstance()->findSitemapItemsWithCache(
[],
1000,
['created_at' => -1, 'v_id' => -1],
$strCacheKey,
86400
);
$arrXml = [
'<?xml version="1.0" encoding="UTF-8"?>',
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
];
$VideoService = app(VideoService::class);
foreach ($arrRows as $arrVideo) {
$intVId = (int)($arrVideo['v_id'] ?? 0);
$strSlug = trim((string)($arrVideo['v_name_en'] ?? ''));
if ($intVId <= 0 || $strSlug === '') {
continue;
}
$strPath = $VideoService->getVideoInfoUrl($intVId, $strSlug);
if ($strPath === '') {
continue;
}
$strLoc = 'https://www.' . $strHost . $strPath;
$strLastmod = trim((string)($arrVideo['v_publish_date'] ?? ''));
if ($strLastmod === '') {
$strLastmod = trim((string)($arrVideo['updated_at'] ?? ''));
}
if ($strLastmod === '') {
$strLastmod = trim((string)($arrVideo['created_at'] ?? ''));
}
if ($strLastmod !== '') {
$intTs = strtotime($strLastmod);
if ($intTs !== false) {
$strLastmod = date('Y-m-d', $intTs);
}
}
$arrXml[] = '<url>';
$arrXml[] = '<loc>' . htmlspecialchars($strLoc, ENT_QUOTES | ENT_XML1, 'UTF-8') . '</loc>';
if ($strLastmod !== '') {
$arrXml[] = '<lastmod>' . htmlspecialchars($strLastmod, ENT_QUOTES | ENT_XML1, 'UTF-8') . '</lastmod>';
}
$arrXml[] = '</url>';
}
$arrXml[] = '</urlset>';
return response(implode("\n", $arrXml), 200, [
'Content-Type' => 'application/xml; charset=UTF-8',
]);
}
/**
* 限速访问
*
@@ -817,6 +884,11 @@ class SiteContext
return '';
}
$arrTemplateCfg = is_array($this->DomainModel->t_cfg ?? null) ? $this->DomainModel->t_cfg : [];
if (!array_key_exists($strLegacyKey, $arrTemplateCfg)) {
return '';
}
try {
return $this->normalizeSeoText($this->converterTemplate($strLegacyKey));
} catch (\Throwable $Throwable) {