diff --git a/code/.example.env b/code/.example.env index 9fcd80a..6d5d63c 100644 --- a/code/.example.env +++ b/code/.example.env @@ -52,6 +52,7 @@ STORAGE_LOCAL_DIR = /mnt/gluster VIEW_DISPLAY_CACHE = false VIEW_TPL_CACHE = false VIEW_DATA_CACHE = false +VIEW_SITE_STYLE_CACHE = false VIEW_DATA_CACHE_DRIVER = redis # VIDEO diff --git a/code/.gitignore b/code/.gitignore index 156fa1a..503ab2a 100644 --- a/code/.gitignore +++ b/code/.gitignore @@ -11,4 +11,9 @@ Thumbs.db /.project **/*bbc*/** /public/public/fu/env.js -/public/static/id/* \ No newline at end of file +/public/static/id/* +/public/initdata/video/url_family_pool_dump.php +/public/initdata/video/url_family_bind.php +public/static/css/compiled/* +public/static/js/compiled/* +/storage/* diff --git a/code/app/common.php b/code/app/common.php index 230d58a..df767fc 100644 --- a/code/app/common.php +++ b/code/app/common.php @@ -785,7 +785,7 @@ namespace { if (!is_string($strPinYin) || trim($strPinYin) === '') { return 'k_' . substr(sha1($strZh), 0, 12); } - + return $strPinYin; } @@ -895,4 +895,51 @@ namespace { // 转换为 UTF-8,丢弃无效字符 return mb_convert_encoding($strInput, 'UTF-8', 'UTF-8'); } + + /** + * loadSeoFile function + * 加载 php 文件数组 + * @param string $poolId + * @param string $pageType + * @param string $field + * @return array + */ + function loadSeoFile( + string $poolId, + string $pageType, + string $field + ): array { + static $memo = []; + + $key = "{$poolId}:{$pageType}:{$field}"; + if (isset($memo[$key])) { + return $memo[$key]; + } + + $file = + root_path() . + 'public/initdata/video/pools/' . + $poolId . '/' . + $pageType . '/' . + $field . '.php'; + + if (!is_file($file)) { + return []; + } + + $data = require $file; + if (!is_array($data)) { + return []; + } + + return $memo[$key] = $data; + } + + function buildUrlFromPattern(string $pattern, array $vars): string + { + foreach ($vars as $k => $v) { + $pattern = str_replace('{' . $k . '}', (string)$v, $pattern); + } + return '/' . ltrim($pattern, '/'); + } } diff --git a/code/app/common/CommentPool.php b/code/app/common/CommentPool.php new file mode 100644 index 0000000..8b3640a --- /dev/null +++ b/code/app/common/CommentPool.php @@ -0,0 +1,239 @@ + 0) + ? min($cfgLimit, $videoLimit) + : $videoLimit; + + + $layout = $cfg['layout'] ?? 'A'; + $variant = intval($cfg['variant'] ?? 0); + + $seed = self::stableSeed($domain . '|' . $videoId . '|' . $layout); + + // 生成一个冗余池,再按 layout/variant 取子集 + $pool = self::makePool($tpl, $ctx, $seed, max(12, $limit * 2)); + + return self::pick($pool, $layout, $variant, $limit); + } + + private static function videoLimit(string $domain, int $videoId): int + { + // 基础范围:3–9 + $base = 3; + + // 使用「域名 + 视频」作为冻结源 + $seed = self::stableSeed($domain . '|' . $videoId); + + return $base + ($seed % 7); + } + + + private static function stableSeed(string $s): int + { + return intval(sprintf('%u', crc32($s))); + } + + private static function makePool(array $tpl, array $ctx, int $seed, int $n): array + { + $actors = self::firstName($ctx['v_actor'] ?? null); + $director = self::firstName($ctx['v_director'] ?? null); + $name = strval($ctx['v_name'] ?? ''); + + $blocks = array_keys($tpl); + if (!$blocks) return []; + + $out = []; + $x = $seed ?: 1; + + for ($i = 0; $i < $n; $i++) { + $x = ($x * 1103515245 + 12345) & 0x7fffffff; + + // 每条评论 1–3 个语义块(稳定) + $take = 1 + ($x % 3); + + // 稳定洗牌:用 seed 派生顺序,而不是 php shuffle(shuffle 非稳定) + $pickedBlocks = self::stablePickBlocks($blocks, $x, $take); + + $parts = []; + foreach ($pickedBlocks as $j => $k) { + $arr = $tpl[$k] ?? []; + if (!$arr) continue; + + $txt = $arr[($x + $j) % count($arr)]; + + $txt = str_replace( + ['{actor}', '{director}', '{name}'], + [$actors ?: '主演', $director ?: '导演', $name ?: '本片'], + $txt + ); + + $parts[] = $txt; + } + + $text = implode('', array_values(array_unique(array_filter($parts)))); + if ($text === '') $text = '整体还行,能看完。'; + + $out[] = [ + 'user' => self::fakeUser($x), + 'time' => self::fakeTime($x), + 'text' => $text, + ]; + } + + return $out; + } + + private static function stablePickBlocks(array $blocks, int $seed, int $take): array + { + $cnt = count($blocks); + $take = min($take, $cnt); + + $res = []; + for ($i = 0; $i < $take; $i++) { + $idx = ($seed + $i * 7) % $cnt; + $res[] = $blocks[$idx]; + } + return $res; + } + + private static function pick(array $pool, string $layout, int $variant, int $limit): array + { + $count = count($pool); + if ($count === 0) return []; + + $base = match ($layout) { + 'A' => 0, + 'B' => intval($count * 0.25), + 'C' => intval($count * 0.50), + 'D' => intval($count * 0.75), + default => 0, + }; + + $start = ($base + $variant) % $count; + $res = []; + + for ($i = 0; $i < $limit; $i++) { + $res[] = $pool[($start + $i) % $count]; + } + + return $res; + } + + private static function firstName($maybeArray): string + { + if (is_array($maybeArray) && isset($maybeArray[0])) return strval($maybeArray[0]); + if (is_string($maybeArray)) return $maybeArray; + return ''; + } + + private static function fakeUser(int $x): string + { + $users = [ + '游客', + '影迷', + '路人甲', + '匿名用户', + '追剧党', + '电影控', + '深夜观众', + '随便看看', + '围观群众', + '看片路人', + '普通观众', + '吃瓜群众', + '影像爱好者', + '路过看看', + '夜猫子', + '周末观众', + '随手一评', + '观影路人', + '老影迷', + '普通网友', + '闲着看看', + '刷到就看', + '下班观众', + '影迷路过', + '随缘观影', + '看片群众', + '佛系观众', + '打发时间', + '路过评论', + '轻度影迷', + '周末看片', + '夜间观影', + '普通用户', + '看片一族', + '随手看看', + '观众之一', + '老黄', + '隔壁老黄', + '小明', + '小红', + 'ID0368', + '东方不败', + '黄蓉', + ]; + + return $users[$x % count($users)]; + } + + + private static function fakeTime(int $x): string + { + $times = [ + '刚刚', + '刚看完', + '不久前', + '刚才刷到', + '1 小时前', + '2 小时前', + '今天早些时候', + '今天', + '昨天', + '前两天', + '2 天前', + '3 天前', + '前几天', + '这两天', + '最近', + '最近几天', + '一周内', + '1 周前', + '差不多一周前', + '上周', + '前段时间', + '前些天', + '不久之前', + '前不久', + '最近刷到', + '最近看完', + '这几天', + '前几周', + ]; + + return $times[$x % count($times)]; + } +} diff --git a/code/app/common/PinlunVariant.php b/code/app/common/PinlunVariant.php new file mode 100644 index 0000000..8a97a2c --- /dev/null +++ b/code/app/common/PinlunVariant.php @@ -0,0 +1,24 @@ +> 4) % 20; + } + + + public static function group(int $variant): string + { + if ($variant < 4) return 'G1'; + if ($variant < 8) return 'G2'; + if ($variant < 12) return 'G3'; + if ($variant < 16) return 'G4'; + return 'G5'; + } + + +} diff --git a/code/app/common/helper/CssBuilder.php b/code/app/common/helper/CssBuilder.php index 9b8f827..7505227 100644 --- a/code/app/common/helper/CssBuilder.php +++ b/code/app/common/helper/CssBuilder.php @@ -28,16 +28,16 @@ class CssBuilder } // 输出文件名(作为域名专属缓存) - $targetFile = $targetDir . "dom_{$staticHash}.css"; + $targetFile = $targetDir . "{$staticHash}.css"; // 如果已经生成过,直接返回 if (file_exists($targetFile)) { - return "/static/css/compiled/dom_{$staticHash}.css"; + return "/static/css/compiled/{$staticHash}.css"; } $listCss = [ "list/list_base.css","list/list_cols.css","list/list_rows.css", - "title/title_A.css","list/title/title_B.css","list/title/title_C.css","list/title/title_D.css","list/title/title_E.css", + "list/title/title_A.css","list/title/title_B.css","list/title/title_C.css","list/title/title_D.css","list/title/title_E.css","list/title/title_F.css", "list/shell/shell_A.css","list/shell/shell_B.css","list/shell/shell_C.css","list/shell/shell_D.css", "list/item/_item_media.css","list/item/_item_poster.css","list/item/_item_rank.css","list/item/_item_text.css", "list/item/_item_base.css","list/item/_item_title_clamp.css", @@ -46,6 +46,8 @@ class CssBuilder "pager/pager_base.css", "seowords/seowords.css", "breadcrumb/breadcrumb.css", + "pinlun/_pinlun_base.css", + // "pinlun/_pinlun_base-B.css", // "rank/rank_home.css", ]; @@ -116,6 +118,6 @@ class CssBuilder file_put_contents($targetFile, $allCss); // 返回前端可访问路径 - return "/static/css/compiled/dom_{$staticHash}.css"; + return "/static/css/compiled/{$staticHash}.css"; } } diff --git a/code/app/common/helper/JsBuilder.php b/code/app/common/helper/JsBuilder.php new file mode 100644 index 0000000..7fa58e6 --- /dev/null +++ b/code/app/common/helper/JsBuilder.php @@ -0,0 +1,87 @@ +"; + continue; + } + + $js = (string)file_get_contents($path); + + // 可选:前缀替换(如果你的 JS 里也需要选择器前缀) + $js = str_replace('__PFX__', $domPrefix, $js); + + // 轻量压缩:去 BOM、统一换行、压缩行尾空白(不做激进 minify) + $js = preg_replace('/^\xEF\xBB\xBF/', '', $js); + $js = str_replace(["\r\n", "\r"], "\n", $js); + $js = preg_replace('/[ \t]+\n/', "\n", $js); + $js = trim($js); + + // 拼接:用分隔注释便于你线上排查 + $allJs .= "\n/* ===== {$file} ===== */\n"; + $allJs .= $js . "\n;\n"; + } + + $allJs .= "\n})();\n"; + + // 写入合并后的 JS + file_put_contents($targetFile, $allJs); + + // 返回前端可访问路径 + return "/static/js/compiled/{$staticHash}.js"; + } +} diff --git a/code/app/common/helper/SiteStyle-back.php b/code/app/common/helper/SiteStyle-back.php deleted file mode 100644 index 8f13fe4..0000000 --- a/code/app/common/helper/SiteStyle-back.php +++ /dev/null @@ -1,907 +0,0 @@ - (($seed >> $shift) % 5) + 1; - - // theme 冻结:mode/variant(调色板运行时算,也可冻结存) - $themeMode = ['A', 'B', 'C', 'D'][$seed % 4]; - $themeVariant = ($seed % 4) + 1; - - // grid 冻结(所有页面通用) - $grid = self::buildGridLayout($seed); - - // pages 冻结:home/category - $homePage = self::buildHomePageCfg($seed); - $categoryPage = self::buildCategoryPageCfg($seed); - - // list_layout 冻结:为“会被渲染的模块”提前生成(后续加模块只要补丁即可) - // 说明:这里生成的是“模块级默认布局”,页面渲染时按 module 取用即可 - $listLayout = self::buildDefaultListLayouts($seed, $homePage, $categoryPage); - - return [ - 'meta' => [ - 'host' => $host, - 'seed' => $seed, - 'dom_prefix' => $domPrefix, - 'static_hash' => $staticHash, - 'version' => 4, // 你现在的 cfg 版本号(以后升级用) - ], - - 'global' => [ - 'page_max_width_pc' => self::$WIDTH_POOL[$seed % count(self::$WIDTH_POOL)], - 'grid' => $grid, - 'theme' => [ - 'mode' => $themeMode, - 'variant' => $themeVariant, - ], - ], - - 'pages' => [ - 'home' => $homePage, - 'category' => $categoryPage, - ], - - 'components' => [ - 'templates' => [ - 'head_tpl' => $idx(0), - 'foot_tpl' => $idx(1), - 'banner_tpl' => $idx(2), - 'list_tpl' => $idx(3), - 'detail_tpl' => $idx(9), - 'play_tpl' => $idx(10), - - // 你旧系统里还有 recommend/trending/newest/ranking/category 等独立 tpl,可继续保留 - 'recommend_tpl' => $idx(4), - 'trending_tpl' => $idx(5), - 'newest_tpl' => $idx(6), - 'ranking_tpl' => $idx(7), - 'category_tpl' => $idx(8), - ], - - 'list' => [ - 'item_variants' => [ - '01' => 20, - '02' => 20, - '03' => 20, - '04' => 20, - '05' => 20, - ], - 'semantic_map' => self::$MODULE_ITEM_WEIGHT, - 'shell_rules' => self::$ITEM_SHELL_MAP, - ], - ], - - // 模块布局冻结(核心) - 'list_layout' => $listLayout, - ]; - } - - /** - * 旧 cfg 补齐规则:只要缺字段就补,并标记 patched=true - */ - private static function patchCfgIfNeeded(array $cfg, string $host, int $seed): array - { - $patched = false; - - // meta - if (!isset($cfg['meta'])) { - $cfg['meta'] = []; - $patched = true; - } - if (!isset($cfg['meta']['host'])) { - $cfg['meta']['host'] = $host; - $patched = true; - } - if (!isset($cfg['meta']['seed'])) { - $cfg['meta']['seed'] = $seed; - $patched = true; - } - if (!isset($cfg['meta']['dom_prefix'])) { - $cfg['meta']['dom_prefix'] = substr(md5($host . '_dom'), 0, 6); - $patched = true; - } - if (!isset($cfg['meta']['static_hash'])) { - $cfg['meta']['static_hash'] = substr(md5($host . '_v2025'), 0, 10); - $patched = true; - } - if (!isset($cfg['meta']['version'])) { - $cfg['meta']['version'] = 4; - $patched = true; - } - - // global - if (!isset($cfg['global'])) { - $cfg['global'] = []; - $patched = true; - } - if (!isset($cfg['global']['page_max_width_pc'])) { - $cfg['global']['page_max_width_pc'] = self::$WIDTH_POOL[$seed % count(self::$WIDTH_POOL)]; - $patched = true; - } - if (!isset($cfg['global']['grid'])) { - $cfg['global']['grid'] = self::buildGridLayout($seed); - $patched = true; - } - if (!isset($cfg['global']['theme'])) { - $cfg['global']['theme'] = [ - 'mode' => ['A', 'B', 'C', 'D'][$seed % 4], - 'variant' => ($seed % 4) + 1, - ]; - $patched = true; - } - - // pages - if (!isset($cfg['pages'])) { - $cfg['pages'] = []; - $patched = true; - } - if (!isset($cfg['pages']['home'])) { - $cfg['pages']['home'] = self::buildHomePageCfg($seed); - $patched = true; - } - if (!isset($cfg['pages']['category'])) { - $cfg['pages']['category'] = self::buildCategoryPageCfg($seed); - $patched = true; - } - - // components - if (!isset($cfg['components'])) { - $cfg['components'] = []; - $patched = true; - } - if (!isset($cfg['components']['templates'])) { - $idx = fn($shift) => (($seed >> $shift) % 5) + 1; - $cfg['components']['templates'] = [ - 'head_tpl' => $idx(0), - 'foot_tpl' => $idx(1), - 'banner_tpl' => $idx(2), - 'list_tpl' => $idx(3), - 'detail_tpl' => $idx(9), - 'play_tpl' => $idx(10), - 'recommend_tpl' => $idx(4), - 'trending_tpl' => $idx(5), - 'newest_tpl' => $idx(6), - 'ranking_tpl' => $idx(7), - 'category_tpl' => $idx(8), - ]; - $patched = true; - } - if (!isset($cfg['components']['list'])) { - $cfg['components']['list'] = [ - 'item_variants' => ['01'=>20,'02'=>20,'03'=>20,'04'=>20,'05'=>20], - 'semantic_map' => self::$MODULE_ITEM_WEIGHT, - 'shell_rules' => self::$ITEM_SHELL_MAP, - ]; - $patched = true; - } - - // list_layout(冻结默认模块布局) - if (!isset($cfg['list_layout']) || !is_array($cfg['list_layout'])) { - $cfg['list_layout'] = self::buildDefaultListLayouts( - $seed, - $cfg['pages']['home'], - $cfg['pages']['category'] - ); - $patched = true; - } else { - // 补齐缺少的默认模块布局(升级时很关键:你说的“分类页不写入”就靠这里补) - $needModules = self::collectNeededModules($cfg['pages']['home'], $cfg['pages']['category']); - foreach ($needModules as $m) { - if (!isset($cfg['list_layout'][$m])) { - $cfg['list_layout'][$m] = self::buildListCfgForModule($seed + crc32($m), $m, $cfg); - $patched = true; - } - } - } - - return [$cfg, $patched]; - } - - /* ====================================================== - * Runtime Payload(TpStyle) - * ====================================================== */ - private static function buildRuntimePayload(array $cfg, string $host, int $seed, $domainRow): array - { - $domPrefix = $cfg['meta']['dom_prefix']; - $staticHash = $cfg['meta']['static_hash']; - - // 运行时主题色(从冻结的 mode/variant 计算) - $themeMode = $cfg['global']['theme']['mode'] ?? 'A'; - $themeVariant = (int)($cfg['global']['theme']['variant'] ?? 1); - $themeColors = self::buildTheme($seed, $themeMode, $themeVariant); - - // templates(路径输出保持你旧版结构) - $tpl = $cfg['components']['templates']; - $templates = [ - 'head' => "head/head_" . sprintf("%02d", (int)$tpl['head_tpl']), - 'foot' => "footer/footer_" . sprintf("%02d", (int)$tpl['foot_tpl']), - 'banner' => "module/banner/banner_" . sprintf("%02d", (int)$tpl['banner_tpl']), - 'list' => "module/list/list_" . sprintf("%02d", (int)$tpl['list_tpl']), - 'detail' => "module/detail/detail_" . sprintf("%02d", (int)$tpl['detail_tpl']), - 'play' => "module/play/play_" . sprintf("%02d", (int)$tpl['play_tpl']), - - // 旧系统模块(保留) - 'recommend' => "module/recommend/recommend_" . sprintf("%02d", (int)$tpl['recommend_tpl']), - 'trending' => "module/trending/trending_" . sprintf("%02d", (int)$tpl['trending_tpl']), - 'newest' => "module/newest/newest_" . sprintf("%02d", (int)$tpl['newest_tpl']), - 'ranking' => "module/ranking/ranking_" . sprintf("%02d", (int)$tpl['ranking_tpl']), - 'category' => "module/category/category_" . sprintf("%02d", (int)$tpl['category_tpl']), - ]; - - // css_files(你当前只要 head/footer/banner/list/detail/play + 可选模块 css) - $cssFiles = [ - "head/head_" . sprintf("%02d", (int)$tpl['head_tpl']) . ".css", - "footer/footer_" . sprintf("%02d", (int)$tpl['foot_tpl']) . ".css", - "banner/banner_" . sprintf("%02d", (int)$tpl['banner_tpl']) . ".css", - "list/list_" . sprintf("%02d", (int)$tpl['list_tpl']) . ".css", - "detail/detail_" . sprintf("%02d", (int)$tpl['detail_tpl']) . ".css", - "play/play_" . sprintf("%02d", (int)$tpl['play_tpl']) . ".css", - ]; - $cssFiles = array_values(array_unique($cssFiles)); - - return [ - 'host' => $host, - 'seed' => $seed, - 'dom_prefix' => $domPrefix, - 'static_hash' => $staticHash, - - // cfg 全量给模板(你现在模板一直在用) - 'template_cfg' => $cfg, - - // 兼容你现在模板里直接用的快捷字段 - 'page_max_width_pc' => $cfg['global']['page_max_width_pc'], - 'list_layout' => $cfg['list_layout'], - 'category_page' => $cfg['pages']['category'], - - // 原结构 - 'templates' => $templates, - 'css_files' => $cssFiles, - ] + $themeColors; - } - - /* ====================================================== - * Pages cfg(冻结规则池 + 稳定映射) - * ====================================================== */ - private static function buildHomePageCfg(int $seed): array - { - $modulePool = ['newest', 'hot', 'rank', 'recommend', 'trending']; - $shuffled = self::shuffleStable($modulePool, $seed + 101); - - // 每域名固定 3~5 - $count = 3 + ($seed % 3); - - return [ - 'top_modules' => array_slice($shuffled, 0, $count), - ]; - } - - private static function buildCategoryPageCfg(int $seed): array - { - return [ - 'top_block' => self::buildCategoryTopBlock($seed), - 'sections_pool' => self::buildCategorySectionsPool($seed), - ]; - } - - private static function buildCategoryTopBlock(int $seed): array - { - $modules = ['newest', 'hot', 'rank']; - return ['module' => $modules[$seed % count($modules)]]; - } - - private static function buildCategorySectionsPool(int $seed): array - { - $basePool = [ - ['shell' => 'A', 'item' => '01', 'title' => 'F', 'class' => 'compact'], - ['shell' => 'B', 'item' => '01', 'title' => 'F', 'class' => ''], - ['shell' => 'B', 'item' => '02', 'title' => 'F', 'class' => 'loose'], - ['shell' => 'C', 'item' => '05', 'title' => 'F', 'class' => 'top compact'], - ]; - - $pool = self::shuffleStable($basePool, $seed + 301); - $count = 3 + ($seed % 3); // 3~5(basePool 只有4条,这里实际最大=4;你以后扩展 basePool 即可>5) - - return array_slice($pool, 0, min($count, count($pool))); - } - - /** - * 供模板调用:二级分类块 cfg(不再在模板里写 crc32 逻辑) - * - * 用法(模板里): - * - */ - public static function buildCategorySectionCfg(array $cfg, string $subCategoryEn, int $order = 0): array - { - $seed = (int)($cfg['meta']['seed'] ?? 0) - + crc32($subCategoryEn) - + $order * 13; - - $pool = $cfg['pages']['category']['sections_pool'] ?? []; - if (empty($pool)) { - // 兜底 - $pool = self::buildCategorySectionsPool($seed); - } - - $base = $pool[$seed % count($pool)]; - - $item = (string)$base['item']; - $itemType = self::$ITEM_TYPE_MAP[$item] ?? 'poster'; - - $variantCount = (int)($cfg['components']['list']['item_variants'][$item] ?? 1); - $variant = $variantCount > 0 ? ($seed % $variantCount) : 0; - - return [ - 'shell' => $base['shell'], - 'item' => $item, - 'item_type' => $itemType, - 'item_variant' => $variant, - 'title' => $base['title'], - 'class' => $base['class'], - 'grid' => $cfg['global']['grid'], - ]; - } - - /* ====================================================== - * list_layout(冻结模块默认布局) - * ====================================================== */ - private static function buildDefaultListLayouts(int $seed, array $homePage, array $categoryPage): array - { - $needModules = self::collectNeededModules($homePage, $categoryPage); - - $out = []; - foreach ($needModules as $m) { - $out[$m] = self::buildListCfgForModule($seed + crc32($m), $m, [ - 'global' => ['grid' => self::buildGridLayout($seed)], - 'components' => ['list' => ['item_variants' => ['01'=>20,'02'=>20,'03'=>20,'04'=>20,'05'=>20]]], - ]); - } - return $out; - } - - private static function collectNeededModules(array $homePage, array $categoryPage): array - { - $modules = []; - - // home 顶部模块 - foreach (($homePage['top_modules'] ?? []) as $m) { - $modules[] = $m; - } - - // category 顶部块模块 - if (!empty($categoryPage['top_block']['module'])) { - $modules[] = $categoryPage['top_block']['module']; - } - - // 常见页面:你后面肯定会用到(先冻结) - $modules[] = 'category_list'; - $modules[] = 'search_list'; - - // 去重 - $modules = array_values(array_unique(array_filter($modules))); - return $modules; - } - - private static function buildListCfgForModule(int $seed, string $module, array $cfg): array - { - $layout = self::buildListLayout($seed, $module); - - // Title - $layout['title'] = self::pickTitleTpl($seed + 17, $module); - $layout['title_text'] = self::buildTitleText($seed + 19, $module); - - // Behavior - $behavior = self::buildListBehavior($seed + 23, $module); - $layout['behavior'] = $behavior; - $layout['class'] = trim(implode(' ', array_filter($behavior))); - - // item variant(冻结) - $layout['item_variant'] = self::pickItemVariant( - $seed + 31, - $layout['item'], - $module - ); - - // grid(冻结引用全局) - $layout['grid'] = $cfg['global']['grid'] ?? self::buildGridLayout($seed); - - return $layout; - } - - /** - * 构建 List 布局(带语义权重 + 合法映射) - */ - private static function buildListLayout(int $seed, string $module): array - { - // Item(语义优先) - $itemIdx = self::pickItemBySemantic($module, $seed); - $itemType = self::$ITEM_TYPE_MAP[$itemIdx] ?? 'poster'; - - // Shell(只在合法池中选) - $allowedShells = self::$ITEM_SHELL_MAP[$itemType] ?? ['B']; - $shell = $allowedShells[($seed >> 4) % count($allowedShells)]; - - // Title(只是一个默认值,最终会被 pickTitleTpl 覆盖) - $titlePool = ['A', 'B', 'C', 'D', 'E']; - $title = $titlePool[($seed >> 6) % count($titlePool)]; - - return [ - 'module' => $module, - 'item' => $itemIdx, - 'item_type' => $itemType, - 'shell' => $shell, - 'title' => $title, - 'class' => '', // 最终由 buildListBehavior 生成 - ]; - } - - /** - * item variant:由模块/seed 稳定取值 - */ - private static function pickItemVariant(int $seed, string $item, string $scope = 'list'): int - { - $count = 20; - $salt = crc32($scope); - return abs($seed + $salt) % $count; - } - - /** - * 列表行为(Behavior) - */ - private static function buildListBehavior(int $seed, string $module): array - { - $behavior = []; - - switch ($module) { - case 'newest': - $behavior[] = ($seed % 2 === 0) ? 'compact' : ''; - $behavior[] = ($seed % 2 === 1) ? 'collapsed' : ''; - break; - - case 'hot': - case 'trending': - case 'recommend': - $behavior[] = ($seed % 3 === 0) ? 'loose' : ''; - $behavior[] = ($seed % 2 === 0) ? 'emphasis' : ''; - break; - - case 'rank': - $behavior[] = 'top'; - $behavior[] = 'compact'; - break; - - default: - // category_list / search_list 等兜底 - $behavior[] = ($seed % 2 === 0) ? 'compact' : ''; - } - - return array_values(array_filter($behavior)); - } - - /** - * 根据模块语义 + seed 选择 Item(带权重) - */ - private static function pickItemBySemantic(string $module, int $seed): string - { - $conf = self::$MODULE_ITEM_WEIGHT[$module] ?? null; - - if (!$conf) { - $pool = array_keys(self::$ITEM_TYPE_MAP); - return $pool[$seed % count($pool)]; - } - - // primary 75% - if (($seed & 0b11) !== 0) { - return $conf['primary'][$seed % count($conf['primary'])]; - } - - // secondary - if (!empty($conf['secondary']) && (($seed >> 2) & 1)) { - return $conf['secondary'][$seed % count($conf['secondary'])]; - } - - return $conf['fallback'][0]; - } - - /* ====================================================== - * Grid(冻结) - * ====================================================== */ - private static function pickPcCols(int $seed, string $size): int - { - $pool = self::$PC_COLS_MAP[$size] ?? [3]; - return $pool[$seed % count($pool)]; - } - - private static function buildGridLayout(int $seed): array - { - return [ - // H5:1–3 列 - 'cols_h5' => [1, 2, 3][$seed % 3], - // PC:按断点 - 'cols_pc_sm' => self::pickPcCols($seed + 11, 'sm'), - 'cols_pc_md' => self::pickPcCols($seed + 23, 'md'), - 'cols_pc_lg' => self::pickPcCols($seed + 37, 'lg'), - ]; - } - - /* ====================================================== - * Title 文案池 - * ====================================================== */ - private static function mapTitleType(string $module): string - { - // 你要求:category/search/recommend 可复用 newest 的文案池 - return match ($module) { - 'category_list', 'search_list', 'recommend' => 'newest', - default => $module, - }; - } - - private static function pickStable(array $list, int $seed) - { - if (empty($list)) return null; - $seed = abs($seed); - return $list[$seed % count($list)]; - } - - private static function buildTitleText(int $seed, string $module): array - { - $type = self::mapTitleType($module); - if (!isset(self::$TITLE_POOL[$type])) { - $type = 'newest'; - } - $pool = self::$TITLE_POOL[$type]; - - return [ - 'primary' => self::pickStable($pool['primary'], $seed), - 'secondary' => self::pickStable($pool['secondary'], $seed >> 2), - 'seo' => self::pickStable($pool['seo'], $seed >> 4), - ]; - } - - private static function pickTitleTpl(int $seed, string $module): string - { - $map = [ - 'newest' => ['A', 'B', 'D'], - 'hot' => ['A', 'C', 'D', 'B'], - 'rank' => ['E', 'D'], - 'trending' => ['A', 'C', 'D'], - 'recommend' => ['A', 'B', 'C'], - 'category_list'=> ['F', 'A'], - 'search_list' => ['A', 'B'], - ]; - - $list = $map[$module] ?? ['A']; - return $list[$seed % count($list)]; - } - - /* ====================================================== - * DB / JSON - * ====================================================== */ - private static function readDbConfig($domainRow): ?array - { - try { - if (!$domainRow) return null; - - $raw = is_array($domainRow) - ? ($domainRow['t_cfg'] ?? null) - : ($domainRow->t_cfg ?? null); - - if (empty($raw)) return null; - - return json_decode($raw, true) ?: null; - } catch (\Throwable $e) { - return null; - } - } - - private static function writeDbConfig($domainRow, array $cfg): void - { - if (!$domainRow || is_array($domainRow)) return; - - try { - $domainRow->t_cfg = json_encode($cfg, JSON_UNESCAPED_UNICODE); - $domainRow->save(); - } catch (\Throwable $e) { - } - } - - private static function readLocalJson(string $host): ?array - { - $file = root_path() . "storage/theme_cache/{$host}.json"; - if (!is_file($file)) return null; - - return json_decode(file_get_contents($file), true) ?: null; - } - - private static function writeLocalJson(string $host, array $cfg): void - { - $dir = root_path() . "storage/theme_cache/"; - if (!is_dir($dir)) @mkdir($dir, 0755, true); - - file_put_contents( - $dir . "{$host}.json", - json_encode($cfg, JSON_UNESCAPED_UNICODE) - ); - } - - /* ====================================================== - * 稳定洗牌 - * ====================================================== */ - private static function shuffleStable(array $arr, int $seed): array - { - $result = $arr; - $rand = $seed; - - for ($i = count($result) - 1; $i > 0; $i--) { - $rand = ($rand * 31 + 17) & 0x7fffffff; - $j = $rand % ($i + 1); - [$result[$i], $result[$j]] = [$result[$j], $result[$i]]; - } - return $result; - } - - /* ====================================================== - * 主题色(沿用你原来的 buildTheme + 辅助函数) - * ====================================================== */ - private static function buildTheme(int $seed, string $mode, int $variant): array - { - $base = $seed + $variant * 97; - if ($mode === 'D') $mode = ['A', 'B', 'C'][$base % 3]; - - $h = $base % 360; - - switch ($mode) { - case 'A': - $primary = self::hslToHex($h, 78, 55); - $secondary = self::hslToHex($h + 30, 65, 50); - $accent = self::hslToHex($h + 310, 85, 56); - $bg = '#F8FAFC'; - $bgSoft = '#F1F5F9'; - break; - - case 'B': - $primary = self::hslToHex($h, 55, 60); - $secondary = self::hslToHex($h + 40, 40, 65); - $accent = self::hslToHex($h + 300, 45, 58); - $bg = '#FBFBFE'; - $bgSoft = '#F5F5FA'; - break; - - case 'C': - $primary = self::hslToHex($h, 80, 55); - $secondary = self::hslToHex($h + 200, 60, 50); - $accent = self::hslToHex($h + 40, 75, 60); - $bg = self::hslToHex($h + 190, 30, 12); - $bgSoft = self::hslToHex($h + 190, 25, 16); - break; - - default: - $primary = self::hslToHex($h, 55, 60); - $secondary = self::hslToHex($h + 40, 40, 65); - $accent = self::hslToHex($h + 300, 45, 58); - $bg = '#FBFBFE'; - $bgSoft = '#F5F5FA'; - } - - $dark = self::isDarkColor($bg); - - return [ - 'color_primary' => $primary, - 'color_secondary' => $secondary, - 'color_accent' => $accent, - 'color_bg' => $bg, - 'color_bg_soft' => $bgSoft, - 'color_text_primary' => $dark ? '#FFF' : '#111', - 'color_text_secondary' => $dark ? '#CCC' : '#666', - 'color_border' => $dark ? 'rgba(255,255,255,.1)' : 'rgba(0,0,0,.1)', - 'color_grad_start' => $primary, - 'color_grad_end' => $secondary, - 'shadow_color' => $dark ? 'rgba(0,0,0,.7)' : 'rgba(0,0,0,.12)', - 'is_dark_mode' => $dark, - ]; - } - - private static function hslToHex($h, $s, $l): string - { - $h /= 360; - $s /= 100; - $l /= 100; - $c = (1 - abs(2 * $l - 1)) * $s; - $x = $c * (1 - abs(fmod($h * 6, 2) - 1)); - $m = $l - $c / 2; - - if ($h < 1 / 6) [$r, $g, $b] = [$c, $x, 0]; - elseif ($h < 2 / 6) [$r, $g, $b] = [$x, $c, 0]; - elseif ($h < 3 / 6) [$r, $g, $b] = [0, $c, $x]; - elseif ($h < 4 / 6) [$r, $g, $b] = [0, $x, $c]; - elseif ($h < 5 / 6) [$r, $g, $b] = [$x, 0, $c]; - else [$r, $g, $b] = [$c, 0, $x]; - - $r = round(($r + $m) * 255); - $g = round(($g + $m) * 255); - $b = round(($b + $m) * 255); - return sprintf("#%02X%02X%02X", $r, $g, $b); - } - - private static function isDarkColor(string $hex): bool - { - $hex = ltrim($hex, '#'); - $r = hexdec(substr($hex, 0, 2)); - $g = hexdec(substr($hex, 2, 2)); - $b = hexdec(substr($hex, 4, 2)); - - $lum = (0.2126 * $r + 0.7152 * $g + 0.0722 * $b) / 255; - return $lum < 0.45; - } - - /* ====================================================== - * 常量池 - * ====================================================== */ - private static array $WIDTH_POOL = [860, 900, 960, 1000, 1080, 1140, 1200, 1280, 1360]; - - private static array $PC_COLS_MAP = [ - 'sm' => [3, 4], - 'md' => [4, 5, 6], - 'lg' => [6, 7, 8, 9], - ]; - - /** - * Shell × Item 合法组合映射(poster/media/rank) - */ - private static array $ITEM_SHELL_MAP = [ - 'poster' => ['B', 'D'], - 'media' => ['A', 'D'], - 'rank' => ['C'], - ]; - - /** - * Item 编号 → Item 类型 - */ - private static array $ITEM_TYPE_MAP = [ - '01' => 'poster', - '02' => 'media', - '03' => 'rank', - '04' => 'poster', - '05' => 'rank', - ]; - - /** - * 模块语义 → Item 权重池 - */ - private static array $MODULE_ITEM_WEIGHT = [ - 'newest' => [ - 'primary' => ['01', '04'], - 'secondary' => ['02'], - 'fallback' => ['01'], - ], - 'hot' => [ - 'primary' => ['01', '04'], - 'secondary' => ['02'], - 'fallback' => ['01'], - ], - 'rank' => [ - 'primary' => ['05'], - 'secondary' => ['03'], - 'fallback' => ['05'], - ], - // 兜底模块 - 'trending' => [ - 'primary' => ['01', '04'], - 'secondary' => ['02'], - 'fallback' => ['01'], - ], - 'recommend' => [ - 'primary' => ['01', '04'], - 'secondary' => ['02'], - 'fallback' => ['01'], - ], - 'category_list' => [ - 'primary' => ['01'], - 'secondary' => ['02'], - 'fallback' => ['01'], - ], - 'search_list' => [ - 'primary' => ['02'], - 'secondary' => ['01'], - 'fallback' => ['02'], - ], - ]; - - private static array $TITLE_POOL = [ - 'newest' => [ - 'primary' => ['最近更新','最新上线','新片速递','今日更新','新内容推荐'], - 'secondary' => ['第一时间为你呈现','每日持续更新','不错过任何新片','刚刚上线,抢先观看'], - 'seo' => ['最新电影电视剧更新','今日最新影视资源','新上线影视内容合集'], - ], - 'hot' => [ - 'primary' => ['热门推荐','热播精选','人气必看','大家都在看'], - 'secondary' => ['近期热度持续攀升','高点击率影片推荐','口碑与热度兼具','当前最受欢迎内容'], - 'seo' => ['热门影视作品推荐','高人气电影电视剧合集','热播影视排行榜推荐'], - ], - 'rank' => [ - 'primary' => ['排行榜','热度榜单','人气排行','播放榜'], - 'secondary' => ['数据实时更新','热度排序参考','近期播放趋势','高人气作品排行'], - 'seo' => ['影视排行榜前十名','热门电影电视剧排行','高播放量影视榜单'], - ], - ]; -} diff --git a/code/app/common/helper/SiteStyle.php b/code/app/common/helper/SiteStyle.php index 3df63c0..379167c 100644 --- a/code/app/common/helper/SiteStyle.php +++ b/code/app/common/helper/SiteStyle.php @@ -2,7 +2,8 @@ namespace app\common\helper; -use app\common\helper\style\DetailMainRule; +use app\services\StaticConfig; +use think\facade\Cache; /** * ========================================================= @@ -17,31 +18,55 @@ use app\common\helper\style\DetailMainRule; */ class SiteStyle { + private static function useCache(): bool + { + return (bool) config('view.view_site_style_cache'); + } + + /** + * cfg schema version + * - 用于结构升级时自动重建冻结 cfg(避免手动清缓存) + */ + private const CFG_VERSION = 2; /* ====================================================== * 公共入口 * ====================================================== */ - public static function getConfig($domainRow = null): array + /** + * Undocumented function + * + * @param [type] $domainRow + * @param [type] $strHost + * @return array + */ + public static function getConfig($domainRow = null, $strHost = null): array { - $host = self::resolveHost(); - $seed = crc32($host); + static $arrReqCache = []; - // 冻结 cfg(只生成一次) - $cfg = self::loadOrCreateCfg($domainRow, $host, $seed); + if (empty($strHost)) { + $strHost = self::resolveHost(); + } + + if (self::useCache() && isset($arrReqCache[$strHost])) { + return $arrReqCache[$strHost]; + } + + $intSeed = (int)crc32($strHost); + + $arrCfg = self::loadOrCreateCfg($domainRow, $strHost, $intSeed); // 运行时 payload(给模板用) - return self::buildRuntimePayload($cfg, $host, $seed); + $arrTpStyle = self::buildRuntimePayload($arrCfg, $strHost, $intSeed); + + if (self::useCache()) { + $arrReqCache[$strHost] = $arrTpStyle; + } + + return $arrTpStyle; } /* ====================================================== * Host / Seed * ====================================================== */ - // private static function resolveHost(): string - // { - // $host = $_SERVER['HTTP_HOST'] ?? 'default.com'; - // $host = strtolower(preg_replace('/^www\./i', '', $host)); - // $host = explode(':', $host)[0]; - // return $host ?: 'default.com'; - // } private static function resolveHost(): string { $host = $_SERVER['HTTP_HOST'] ?? ''; @@ -116,28 +141,57 @@ class SiteStyle return $prefix; } - /* ====================================================== * cfg 冻结层(只生成一次) * ====================================================== */ - private static function loadOrCreateCfg($domainRow, string $host, int $seed): array + private static function loadOrCreateCfg($domainRow, string $strHost, int $intSeed): array { - $cfg = self::readDbConfig($domainRow) - ?? self::readLocalJson($host); + $strCacheKey = 'site_cfg:' . $strHost . ':v' . self::CFG_VERSION; - if ($cfg) { - return $cfg; + // 1) Cache(可关闭) + if (self::useCache()) { + $arrCfg = Cache::get($strCacheKey); + if ( + is_array($arrCfg) + && (int)($arrCfg['meta']['version'] ?? 0) === self::CFG_VERSION + ) { + return $arrCfg; + } } - // ===== 新站:完整生成 ===== - $cfg = self::generateFrozenCfg($host, $seed); + // 2) DB + $arrCfg = self::readDbConfig($domainRow); - self::writeDbConfig($domainRow, $cfg); - self::writeLocalJson($host, $cfg); + // 3) local json + if (!$arrCfg) { + $arrCfg = self::readLocalJson($strHost); + } - return $cfg; + // 4) schema 校验 + if ( + is_array($arrCfg) + && (int)($arrCfg['meta']['version'] ?? 0) === self::CFG_VERSION + ) { + if (self::useCache()) { + Cache::set($strCacheKey, $arrCfg, 86400 * 7); + } + return $arrCfg; + } + + // 5) 重建冻结 CFG + $arrCfg = self::generateFrozenCfg($strHost, $intSeed); + + self::writeDbConfig($domainRow, $arrCfg); + self::writeLocalJson($strHost, $arrCfg); + + if (self::useCache()) { + Cache::set($strCacheKey, $arrCfg, 86400 * 30); + } + + return $arrCfg; } + /** * 生成完整冻结 cfg(唯一版本) */ @@ -145,7 +199,8 @@ class SiteStyle { //$domPrefix = substr(md5($host . '_dom'), 0, 6); $domPrefix = self::buildDomPrefix($host); - $staticHash = substr(md5($host . '_v2025'), 0, 10); + // static_hash 用于静态资源缓存隔离(随 cfg schema 升级而变化) + $staticHash = substr(md5($host . '_v2025_schema_' . self::CFG_VERSION), 0, 10); // 模板编号(100 套 head/footer 完全可继续用) $idx = fn($shift) => (($seed >> $shift) % 100) + 1; @@ -154,20 +209,75 @@ class SiteStyle $grid = self::buildGridLayout($seed); // 页面规则 - $homePage = self::buildHomePageCfg($seed); + $homePage = self::buildHomePageCfg($seed); $categoryIndexPage = self::buildCategoryIndexPageCfg($seed); - $searchPage = self::buildSearchPageCfg($seed); - $rankHomePage = self::buildRankHomePageCfg($seed); + $categoryPage = self::buildCategoryPageCfg($seed); + $categoryListPage = self::buildCategoryListPageCfg($seed); + $searchPage = self::buildSearchPageCfg($seed); + $rankHomePage = self::buildRankHomePageCfg($seed); + $rankListIndexPage = self::buildRankListIndexPageCfg($seed); + // list_layout:形态库(纯结构/栅格/壳子/行为),不承载 title 语义 $listLayout = self::buildAllListLayouts($seed, $domPrefix); + self::injectDynamicListLayouts($listLayout, $homePage, $categoryIndexPage, $categoryPage, $rankHomePage, $seed, $domPrefix); + + // ✅ 首页分类块:为每个分类生成独立的 layout(仍然放进 list_layout 形态库) + if (!empty($homePage['categories']) && is_array($homePage['categories'])) { + foreach ($homePage['categories'] as $cat) { + $key = (string)($cat['key'] ?? ''); + $layoutKey = (string)($cat['layout_key'] ?? ''); + $mod = (string)($cat['module'] ?? ''); + if ($key === '' || $layoutKey === '' || $mod === '') continue; + + $s = $seed + crc32('homecat|' . $key); + $listLayout[$layoutKey] = self::buildListLayoutForModule($s, $mod, $seed, $domPrefix); + } + } + // ✅ 追加:category_index / category 页面用的 layout_key(cat1:* / cat2:*) + $catTree = self::getVideoCategoryTree(); + if (!empty($catTree)) { + foreach ($catTree as $c1) { + $k1 = (string)($c1['v_category_en'] ?? ''); + $n1 = (string)($c1['v_category'] ?? $k1); + if ($k1 === '') continue; + + $s1 = $seed + crc32('cat1|' . $k1); + $m1 = self::pickHomeCategoryRenderModule($s1 + 37); + $listLayout['cat1:' . $k1] = self::buildListLayoutForModule($s1, $m1, $seed, $domPrefix); + + $children = $c1['children'] ?? []; + // 某些结构:一级下无二级,则把一级自身当作二级列表入口 + if (empty($children)) { + $children = [[ + 'v_category_en' => $k1, + 'v_category' => $n1, + 'children' => [], + ]]; + } + + foreach ($children as $c2) { + $k2 = (string)($c2['v_category_en'] ?? ''); + $n2 = (string)($c2['v_category'] ?? $k2); + if ($k2 === '') continue; + + $s2 = $seed + crc32('cat2|' . $k1 . '|' . $k2); + $m2 = self::pickHomeCategoryRenderModule($s2 + 37); + $listLayout['cat2:' . $k2] = self::buildListLayoutForModule($s2, $m2, $seed, $domPrefix); + } + } + } + + + // list_layout 只承载“形态库”(item/shell/grid/layout/行为/itv)。 + // title/title_text/更多文案 等“语义”交给 pages.slot/context 决定。 return [ 'meta' => [ 'host' => $host, 'seed' => $seed, 'dom_prefix' => $domPrefix, 'static_hash' => $staticHash, - 'version' => 1, + 'version' => self::CFG_VERSION, ], 'global' => [ @@ -180,11 +290,13 @@ class SiteStyle ], 'pages' => [ - 'home' => $homePage, + 'home' => $homePage, 'category_index' => $categoryIndexPage, - 'category' => [], - 'search' => $searchPage, - 'rank_home' => $rankHomePage, + 'category' => $categoryPage, + 'category_list' => $categoryListPage, + 'search' => $searchPage, + 'rank_home' => $rankHomePage, + 'rank_list_index' => $rankListIndexPage, 'detail' => self::buildDetailPageCfg($seed), 'play' => self::buildPlayPageCfg($seed), ], @@ -229,20 +341,112 @@ class SiteStyle ], 'list_layout' => $listLayout, + + 'url_family' => self::pickUrlFamily($seed), + + 'seo' => self::buildSeoCfg($seed), + ]; } + private static function getUrlFamilyPool(): array + { + return self::loadInitData('public/initdata/video/url_family_pool_gpt.php'); + } + + private static function pickUrlFamily(int $seed): array + { + $pool = self::getUrlFamilyPool(); + if (!$pool) { + return []; + } + + // 1️⃣ pool → map[AID => family] + $map = []; + foreach ($pool as $family) { + if (!empty($family['id'])) { + $map[$family['id']] = $family; + } + } + if (!$map) { + return []; + } + + ksort($map); // A001, A002, ... + + // 2️⃣ 读取 bind(落盘绑定) + $strFileName = 'public/initdata/video/url_family_bind.php'; + $bindFile = root_path() . $strFileName; + $bind = self::loadInitData($strFileName); + + $bindKey = 'seed:' . (string)$seed; + // 👉 如果你要“域名绑定”,改成: + // $bindKey = 'domain:' . ($_SERVER['HTTP_HOST'] ?? 'cli'); + + /** + * ========================= + * 3️⃣ 已有绑定:永远不改 + * ========================= + */ + if (isset($bind[$bindKey])) { + + $aid = $bind[$bindKey]; + + // pool 里还有这个 family → 正常返回 + if (isset($map[$aid])) { + return json_decode(json_encode($map[$aid]), true); + } + + // ⚠️ pool 里暂时没有这个 AID + // ❗ 绝对不能重新绑定 + // 这里你有 2 个选择: + // 1)返回空(推荐,最安全) + // 2)返回一个固定兜底 family(比如 home-only) + + return []; + } + + /** + * ========================= + * 4️⃣ 没有绑定:第一次分配 + * ========================= + */ + $ids = array_keys($map); + $idx = abs($seed) % count($ids); + $aid = $ids[$idx]; + + // 写入绑定(append-only) + $bind[$bindKey] = $aid; + + $content = " "head/head_" . sprintf('%02d', $tpl['head_tpl']), 'foot' => "footer/footer_" . sprintf('%02d', $tpl['foot_tpl']), - 'banner' => "module/banner/banner_" . sprintf('%02d', $tpl['banner_tpl']), - 'list' => "module/list/list_" . sprintf('%02d', $tpl['list_tpl']), - 'detail' => "module/detail/detail_" . sprintf('%02d', $tpl['detail_tpl']), - 'play' => "module/play/play_" . sprintf('%02d', $tpl['play_tpl']), ]; // CSS:只负责声明模块,最终由 CssBuilder 合并 @@ -269,6 +469,7 @@ class SiteStyle "breadcrumb/layout/layout_" . $cfg['pages']['detail']['breadcrumb']['layout'] . ".css", "playline/layout/layout_" . $cfg['pages']['detail']['playline']['layout'] . ".css", "seowords/layout/layout_" . $cfg['pages']['detail']['seowords']['layout'] . ".css", + "pinlun/_pinlun_layout_" . $cfg['pages']['detail']['pinlun']['layout'] . ".css", "banner/banner_" . sprintf('%02d', $tpl['banner_tpl']) . ".css", "list/list_" . sprintf('%02d', $tpl['list_tpl']) . ".css", @@ -276,20 +477,30 @@ class SiteStyle "play/play_" . sprintf('%02d', $tpl['play_tpl']) . ".css", ]; - return [ - 'host' => $host, - 'seed' => $seed, + $arrTpStyle = [ + 'host' => $strHost, + 'seed' => $intSeed, 'dom_prefix' => $domPrefix, 'static_hash' => $staticHash, + 'lazy_img' => self::pickLazyImg([ + 'host' => $strHost, + 'seed' => $intSeed, + ]), + + // 全站统一:标题截断/行数(用于 title 模板,避免把它塞进 list_layout) + 'title_clamp_cls' => self::buildTitleClampClass($intSeed, $domPrefix), + 'templates' => $templates, 'css_files' => $cssFiles, 'template_cfg' => $cfg, - 'page_max_width_pc' => $cfg['global']['page_max_width_pc'], - 'list_layout' => $cfg['list_layout'], - 'category_index' => $cfg['pages']['category_index'], ] + $theme; + + if (self::useCache()) { + Cache::set($strCacheKey, $arrTpStyle, 86400 * 7); + } + return $arrTpStyle; } /* ====================================================== @@ -303,20 +514,192 @@ class SiteStyle // 首页模块数量:1–5(站点级稳定) $count = ($seed % count($pool)) + 1; + $mods = array_slice($pool, 0, $count); + + $slots = []; + $lunliSlots = []; + foreach ($mods as $m) { + $slots[] = self::buildModuleTitleSlot($seed, (string)$m); + } + $lunliSlots[] = self::buildModuleTitleSlot($seed, 'lunli'); + return [ - 'modules' => array_slice($pool, 0, $count), + // 首页顶部固定一栏 这栏只展示图片 放伦理影视,且 item 只有1-2列, + 'lunli' => ['lunli'], + 'lunliSlots' => $lunliSlots, + + // 兼容旧模板(仍保留) + 'modules' => $mods, + + // ✅ 新结构:slot(引用 list_layout 的 key;title 语义也在 slot) + 'slots' => $slots, + + // ✅ 首页分类块(1–5 个) + 'categories' => self::pickHomeCategories($seed), ]; } - private static function buildCategoryIndexPageCfg(int $seed): array { - $pool = ['news', 'piaofang', 'rank', 'tuijian', 'update']; + // 分类首页:顶部随机一个语义 module(域名级稳定) + $topPool = ['news', 'piaofang', 'rank', 'tuijian', 'update']; + $topPool = self::shuffleStable($topPool, $seed + 211); + $topMod = $topPool[0]; + + $tree = self::getVideoCategoryTree(); + $cat1Slots = []; + + foreach ($tree as $c1) { + $strCategoryPinyin = (string)($c1['v_category_en'] ?? ''); + $strCategoryName = (string)($c1['v_category'] ?? $strCategoryPinyin); + if ($strCategoryPinyin === '') continue; + + $s1 = $seed + crc32('catindex|' . $strCategoryPinyin); + $m1 = self::pickHomeCategoryRenderModule($s1 + 37); + + $cat1Slots[] = [ + 'key' => $strCategoryPinyin, + 'name' => $strCategoryName, + 'module' => $m1, + 'layout_key' => 'cat1:' . $strCategoryPinyin, + + // 分类类标题:统一 F(母型更多),子变体 0..19 + 'title_tpl' => self::pickTitleTplSpec($s1 + 17, 'category_list', 'F'), + 'title_text' => self::buildCategoryTitleText($s1 + 19, $strCategoryPinyin, $strCategoryName, 'cat1'), + + // 右侧“更多” + 'more_text' => self::buildMoreText($s1 + 23, 'category'), + ]; + } + return [ + // legacy:保留旧结构,避免模板未改时直接报错 'top_block' => [ - 'module' => $pool[$seed % count($pool)], + 'module' => $topMod, ], 'sections_pool' => self::buildCategorySectionsPool($seed), + // ✅ new:slot 化 + 'top_slot' => self::buildModuleTitleSlot($seed + crc32('catindex|top|' . $topMod), $topMod), + 'cat1_slots' => $cat1Slots, + ]; + } + private static function buildCategoryPageCfg(int $seed): array + { + // 一级分类页面:顶部随机一个语义 module + 当前一级下所有二级分类区块 + $topPool = ['news', 'piaofang', 'rank', 'tuijian', 'update']; + + $tree = self::getVideoCategoryTree(); + $cat1Map = []; + + foreach ($tree as $c1) { + $strPCategoryPinyin = (string)($c1['v_category_en'] ?? ''); + $strPCategoryName = (string)($c1['v_category'] ?? $strPCategoryPinyin); + if ($strPCategoryPinyin === '') continue; + + $s1 = $seed + crc32('cat1page|' . $strPCategoryPinyin); + $topMod = $topPool[$s1 % count($topPool)]; + + $children = $c1['children'] ?? []; + if (empty($children)) { + $children = [[ + 'v_category_en' => $strPCategoryPinyin, + 'v_category' => $strPCategoryName, + 'children' => [], + ]]; + } + + $subSlots = []; + foreach ($children as $c2) { + $strCategoryPinyin = (string)($c2['v_category_en'] ?? ''); + $strCategoryName = (string)($c2['v_category'] ?? $strCategoryPinyin); + if ($strCategoryPinyin === '') continue; + + $s2 = $seed + crc32('cat1page|' . $strPCategoryPinyin . '|cat2|' . $strCategoryPinyin); + $m2 = self::pickHomeCategoryRenderModule($s2 + 37); + + $subSlots[] = [ + 'key' => $strCategoryPinyin, + 'name' => $strCategoryName, + 'module' => $m2, + 'layout_key' => 'cat2:' . $strCategoryPinyin, + 'title_tpl' => self::pickTitleTplSpec($s2 + 17, 'category_list', 'F'), + // 'title_text' => self::buildCategoryTitleText($s2 + 19, $strCategoryName, 'cat2'), + 'title_text' => self::buildCategoryTitleText($s2 + 19, $strCategoryPinyin, $strCategoryName, 'cat2'), + 'more_text' => self::buildMoreText($s2 + 23, 'category'), + ]; + } + + $cat1Map[$strPCategoryPinyin] = [ + 'key' => $strPCategoryPinyin, + 'name' => $strPCategoryName, + 'top_module' => $topMod, + 'top_slot' => self::buildModuleTitleSlot($s1 + crc32('cat1page|top|' . $strPCategoryPinyin . '|' . $topMod), $topMod), + 'subcat_slots' => $subSlots, + ]; + } + + return [ + 'cat1_map' => $cat1Map, + ]; + } + + private static function buildCategoryListPageCfg(int $seed): array + { + // 二级分类列表页:当前二级分类的视频列表 + 分页 + // - layout:固定引用 list_layout['category_list'] + // - title:统一 F,文案按分类语境 + $tree = self::getVideoCategoryTree(); + $cat2Map = []; + + foreach ($tree as $c1) { + $strPCategoryPinyin = (string)($c1['v_category_en'] ?? ''); + $strPCategoryName = (string)($c1['v_category'] ?? $strPCategoryPinyin); + if ($strPCategoryPinyin === '') continue; + + $children = $c1['children'] ?? []; + if (empty($children)) { + $children = [[ + 'v_category_en' => $strPCategoryPinyin, + 'v_category' => $strPCategoryName, + 'children' => [], + ]]; + } + + foreach ($children as $c2) { + $strCategoryPinyin = (string)($c2['v_category_en'] ?? ''); + $strCategoryName = (string)($c2['v_category'] ?? $strCategoryPinyin); + if ($strCategoryPinyin === '') continue; + + $s2 = $seed + crc32('cat2list|' . $strCategoryPinyin); + $cat2Map[$strCategoryPinyin] = [ + 'key' => $strCategoryPinyin, + 'name' => $strCategoryName, + 'layout_key' => 'category_list', + 'title_tpl' => self::pickTitleTplSpec($s2 + 17, 'category_list', 'F'), + 'title_text' => self::buildCategoryTitleText($s2 + 19, $strCategoryPinyin, $strCategoryName, 'cat2_list'), + ]; + } + } + + return [ + 'layout_key' => 'category_list', + 'cat2_map' => $cat2Map, + ]; + } + + private static function buildRankListIndexPageCfg(int $seed): array + { + // 榜单列表页(如果你有 /rank/xxx 这种聚合页) + // layout:引用 list_layout['rank_list_index'] + $s = $seed + crc32('rank_list_index'); + + return [ + 'slot' => [ + 'layout_key' => 'rank_list_index', + 'title_tpl' => self::pickTitleTplSpec($s + 17, 'rank', 'E'), + 'title_text' => self::buildTitleText($s + 19, 'rank'), + 'more_text' => self::buildMoreText($s + 23, 'rank'), + ], ]; } @@ -339,58 +722,116 @@ class SiteStyle // 视觉语气(CSS 层) 'tone' => 1 + (($seed >> 6) % 3), ], + + // ✅ 搜索结果列表:slot 化(layout_key 固定 search_list) + 'list_slot' => [ + 'layout_key' => 'search_list', + 'title_tpl' => self::pickTitleTplSpec($seed + 17, 'search_list'), + 'title_text' => self::buildTitleText($seed + 19, 'search_list'), + ], ]; } private static function buildRankHomePageCfg(int $seed): array { - $out = []; + $slots = []; foreach (self::$RANK_HOME_MODULES as $key => $conf) { - $s = $seed + crc32($key); + $s = $seed + crc32('rankhome|' . $key); - // 1️⃣ item 只能是 rank 类 - $rankItems = ['03', '05']; - $item = $rankItems[$s % count($rankItems)]; + // 语义:rank_home 统一用 rank 语气(取数走 ranklist) + $module = 'rank'; + $layoutKey = 'rankhome:' . $key; - // 2️⃣ shell:只从“适合榜单的 shell 池”里选 - // 你已有 ITEM_SHELL_MAP['rank'] => ['C'] - $shells = self::$ITEM_SHELL_MAP['rank']; - $shell = $shells[$s % count($shells)]; + $titleTpl = self::pickRankHomeTitleTpl($s); + $titleText = self::buildRankHomeTitleText($s + 19, (string)$conf['title'], (string)$conf['sort_type']); - // 3️⃣ 行为(榜单专用) - $class = 'rank compact top'; + $slots[] = [ + 'key' => (string)$key, // rank_daily / rank_weekly ... + 'module' => $module, // 固定 rank + 'sort_type' => (string)$conf['sort_type'], // daily/weekly/monthly/total - $out[] = [ - 'module' => $key, - 'title' => $conf['title'], - 'sort_type' => $conf['sort_type'], + // ✅ slot 引用 list_layout + 'layout_key' => $layoutKey, - // ===== list / item 体系 ===== - 'item' => $item, - 'item_type' => 'rank', - 'item_variant' => $s % 20, - 'shell' => $shell, - 'class' => $class, + // ✅ title 交给 slot(不再进 list_layout) + 'title_tpl' => $titleTpl, + 'title_text' => $titleText, - // ===== 列表布局(完全复用)===== - 'grid' => self::buildGridLayout($s), - 'layout' => self::buildListRowsAndLimit( - $s, - self::buildGridLayout($s), - 'low' // 榜单首页:低密度 - ), - - // ===== 数据层 ===== - 'limit' => 7, - 'more_url' => "/rank/{$conf['sort_type']}", + // ✅ 更多 + 'more_url' => "/rank/{$conf['sort_type']}", // 这个没有使用 + 'more_text' => self::buildMoreText($s + 23, 'rank_home', (string)$conf['title']), ]; } return [ - 'modules' => $out, + 'slots' => $slots, ]; } + private static function pickRankHomeTitleTpl(int $seed): array + { + // 你原来 rank 的 title 家族是 E/D,这里沿用(语义更贴合榜单) + $familyPool = ['E', 'D']; + $family = $familyPool[$seed % count($familyPool)]; + + return [ + 'family' => $family, + 'variant' => ($seed >> 6) % 20, // 0..19(你后续做 20 套子变体) + ]; + } + + private static function buildRankHomeTitleText( + int $seed, + string $titleName, + string $sortType + ): array { + $firelFilele = 'public/initdata/video/text_pool/seo/rank_home.php'; + $file = root_path() . $firelFilele; + + if (!is_file($file)) { + return [ + 'primary' => $titleName, + 'secondary' => '', + 'seo' => '', + ]; + } + + $pool = require $file; + + $primaryPool = $pool['primary'] ?? []; + $secondaryPool = $pool['secondary'] ?? []; + $seoPool = $pool['seo'] ?? []; + + // 池兜底(防止被误删 / 配置异常) + if (!$primaryPool || !$secondaryPool || !$seoPool) { + return [ + 'primary' => $titleName, + 'secondary' => '', + 'seo' => '', + ]; + } + + // 域名级 + 榜单级稳定 + $hP = abs(crc32($seed . '|rkpri|' . $sortType)); + $hS = abs(crc32($seed . '|rksec|' . $sortType)); + $hE = abs(crc32($seed . '|rkseo|' . $sortType)); + + $primaryTpl = $primaryPool[$hP % count($primaryPool)]; + $secondaryTpl = $secondaryPool[$hS % count($secondaryPool)]; + $seoTpl = $seoPool[$hE % count($seoPool)]; + + // 统一占位符替换(rank_home 统一用 {name}) + $replace = [ + '{name}' => $titleName, + ]; + + return [ + 'primary' => strtr($primaryTpl, $replace), + 'secondary' => strtr($secondaryTpl, $replace), + 'seo' => strtr($seoTpl, $replace), + ]; + } + private static function buildDetailPageCfg(int $seed): array { @@ -414,10 +855,12 @@ class SiteStyle 'variant' => ($seed >> 4) % 20, ], - // ===== pinlun 为后面预留 ===== + // ===== pinlun ===== 'pinlun' => [ - 'layout' => ['A', 'B', 'C'][$seed % 3], + 'layout' => ['A', 'B', 'C', 'D'][$seed % 4], 'variant' => ($seed >> 4) % 20, + 'limit' => 6, + 'mode' => 'slice', // slice|rotate(先实现 slice) ], // ===== Breadcrumb ===== @@ -437,7 +880,8 @@ class SiteStyle // ===== 下方列表(复用 list 体系)===== 'list' => [ - 'modules' => self::buildDetailPageListCfg($seed), + 'modules' => ($mods = self::buildDetailPageListCfg($seed)), + 'slots' => self::buildSlotsFromModules($seed + 991, $mods, 'detail'), ], ]; } @@ -452,6 +896,150 @@ class SiteStyle return array_slice($pool, 0, $count); } + private static function pickHomeCategories(int $domainSeed): array + { + $map = StaticConfig::$arrVideoClass ?? []; + if (empty($map) || !is_array($map)) return []; + + $keys = array_keys($map); + + // 稳定打乱(域名级) + $keys = self::shuffleStable($keys, $domainSeed + 901); + + // 1–5(不超过总数) + $max = min(5, count($keys)); + $count = ($domainSeed % $max) + 1; + + $picked = array_slice($keys, 0, $count); + + $out = []; + foreach ($picked as $k) { + $catKey = (string)$k; + $catName = (string)($map[$k] ?? $k); + + // 每个分类自己的稳定 seed(同域名+同分类 => 固定) + $s = $domainSeed + crc32('homecat|' . $catKey); + + // ① 分类块绑定一个“语义 module”(决定 item_type / density / behavior) + $mod = self::pickHomeCategoryRenderModule($s + 37); + + // ② layout_key:指向 list_layout 形态库中的一个 entry(由 generateFrozenCfg 统一注入) + $layoutKey = 'homecat:' . $catKey; + + // ③ title:分类标题必须语义正确,且模板固定 F + $slot = self::buildCategoryTitleSlot($domainSeed, $catKey, $catName); + + $out[] = [ + 'key' => $catKey, + 'name' => $catName, + 'module' => $mod, + 'layout_key' => $layoutKey, + 'title_tpl' => $slot['title_tpl'], + 'title_text' => $slot['title_text'], + 'more_text' => $slot['more_text'], + ]; + } + + return $out; + } + + + private static function pickHomeCategoryRenderModule(int $seed): string + { + // 首页“分类区块”渲染语气:四选一(域名级冻结) + $pool = ['news', 'piaofang', 'tuijian', 'update']; + return $pool[$seed % count($pool)]; + } + + + /** + * 获取所有分类二维结构(1级>2级) + * - 只读 StaticConfig,cfg 生成期调用一次即可 + * - 兜底:若接口不存在 / 返回空,则返回 [] + */ + private static function getVideoCategoryTree(): array + { + if (!class_exists("app\\services\\StaticConfig")) { + return []; + } + if (!method_exists(StaticConfig::class, 'getVideoCategoryTypeFilter')) { + return []; + } + + // 这里 strType 具体取值你项目里可能是 'all' / 'video' / 'vod' + // 为避免上线前因参数不匹配导致空数据,做多策略兜底。 + foreach (['ONE'] as $t) { + try { + $res = StaticConfig::getVideoCategoryTypeFilter((string)$t); + } catch (\Throwable $e) { + $res = []; + } + if (is_array($res) && !empty($res)) { + return $res; + } + } + return []; + } + + /* ====================================================== + * Slot:title/title_text(冻结到 pages.*,但不写进 list_layout) + * ====================================================== */ + + /** + * 普通模块 Slot(如 news/piaofang/tuijian/update/rank) + * - layout_key 指向 list_layout + * - title_tpl / title_text 冻结到 slot + */ + private static function buildModuleTitleSlot(int $domainSeed, string $module, string $scene = 'home'): array + { + $s = $domainSeed + crc32('slot|' . $scene . '|mod|' . $module); + + $moreScene = ($module === 'rank') ? 'rank' : 'default'; + + return [ + 'layout_key' => $module, + 'title_tpl' => self::pickTitleTplSpec($s + 17, $module), + 'title_text' => self::buildTitleText($s + 19, $module), + + // titleF 的更多按钮文案(strMoreText) + 'more_text' => self::buildMoreText($s + 23, $moreScene), + ]; + } + + /** + * 分类 Slot(首页分类块 / 分类列表页都可复用) + * - title 模板固定 F + * - 文案按分类语境(key/name)稳定生成 + */ + private static function buildCategoryTitleSlot(int $domainSeed, string $catKey, string $catName, string $scene = 'homecat'): array + { + $s = $domainSeed + crc32('slot|' . $scene . '|cat|' . $catKey); + + return [ + 'title_tpl' => self::pickTitleTplSpec($s + 17, 'category_list', 'F'), + 'title_text' => self::buildCategoryTitleText($s + 19, $catKey, $catName, $scene), + + // titleF 的更多按钮文案(strMoreText) + 'more_text' => self::buildMoreText($s + 23, 'category', $catName), + ]; + } + + + /** + * 把模块池(module strings)转换成 slot 列表 + * - 仅生成 slot,不生成 layout + * - layout 由 list_layout[layout_key] 提供 + */ + private static function buildSlotsFromModules(int $domainSeed, array $modules, string $scene = 'home'): array + { + $out = []; + foreach ($modules as $m) { + $m = (string)$m; + if ($m === '') continue; + $out[] = self::buildModuleTitleSlot($domainSeed, $m, $scene); + } + return $out; + } private static function buildDetailMainCfg(int $seed): array @@ -546,7 +1134,8 @@ class SiteStyle // ===== 下方列表(复用 list 体系)===== 'list' => [ - 'modules' => self::buildDetailPageListCfg($seed), + 'modules' => ($mods = self::buildDetailPageListCfg($seed)), + 'slots' => self::buildSlotsFromModules($seed + 1991, $mods, 'play'), ], ]; } @@ -598,6 +1187,29 @@ class SiteStyle . ' ' . $domPrefix . '-tclamp-l' . $lines; } + private static function pickItemTypeNum(int $domainSeed, string $module, string $itemType): int + { + // 每种 item_type 的“视觉形态数量” + // poster01..poster08 / media01..media06 / rank01..rank04(你可自行调整) + $pool = [ + 'poster' => 8, + 'media' => 6, + 'rank' => 4, + ]; + $n = $pool[$itemType] ?? 4; + + // 冻结:域名级稳定,同时允许不同 module 也不同(仍然是“域名绑定”) + $h = crc32($domainSeed . '|' . $module . '|itv|' . $itemType); + + return ($h % $n) + 1; // 1..n + } + + private static function buildItemTypeVarClass(string $domPrefix, string $itemType, int $num): string + { + return $domPrefix . '-itv-' . $itemType . sprintf('%02d', $num); + } + + /* ====================================================== * list_layout(一次性冻结) @@ -605,6 +1217,7 @@ class SiteStyle private static function buildAllListLayouts(int $seed, string $domPrefix): array { $modules = [ + 'lunli', 'news', 'piaofang', 'rank', @@ -618,11 +1231,65 @@ class SiteStyle $out = []; foreach ($modules as $m) { - $out[$m] = self::buildListLayoutForModule($seed + crc32($m), $m,$seed, $domPrefix); + $out[$m] = self::buildListLayoutForModule($seed + crc32($m), $m, $seed, $domPrefix); } return $out; } + private static function injectDynamicListLayouts( + array &$listLayout, + array $homePage, + array $categoryIndexPage, + array $categoryPage, + array $rankHomePage, + int $domainSeed, + string $domPrefix + ): void { + + $ensure = function (string $layoutKey, string $module) use (&$listLayout, $domainSeed, $domPrefix) { + if (isset($listLayout[$layoutKey])) return; + + // 用 layoutKey 做 seed:保证同域名下每个动态 key 都稳定且互不相同 + $s = $domainSeed + crc32('lk|' . $layoutKey); + + $listLayout[$layoutKey] = self::buildListLayoutForModule($s, $module, $domainSeed, $domPrefix); + + // 可选:把 module 字段标记成 layoutKey(便于排查) + $listLayout[$layoutKey]['module'] = $layoutKey; + }; + + // 1) 首页分类块 homecat:xxx + foreach (($homePage['categories'] ?? []) as $it) { + $lk = (string)($it['layout_key'] ?? ''); + $md = (string)($it['module'] ?? ''); + if ($lk !== '' && $md !== '') $ensure($lk, $md); + } + + // 2) 分类首页(一级分类列表)cat1:xxx + foreach (($categoryIndexPage['cat1_slots'] ?? []) as $it) { + $lk = (string)($it['layout_key'] ?? ''); + $md = (string)($it['module'] ?? ''); + if ($lk !== '' && $md !== '') $ensure($lk, $md); + } + + // 3) 一级分类页(二级分类 blocks)cat2:xxx + foreach (($categoryPage['cat1_map'] ?? []) as $cat1) { + foreach (($cat1['subcat_slots'] ?? []) as $sub) { + $lk = (string)($sub['layout_key'] ?? ''); + $md = (string)($sub['module'] ?? ''); + if ($lk !== '' && $md !== '') $ensure($lk, $md); + } + } + + // 4) rank_home slots:rankhome:rank_daily ... + foreach (($rankHomePage['slots'] ?? []) as $it) { + $lk = (string)($it['layout_key'] ?? ''); + $md = (string)($it['module'] ?? ''); + if ($lk !== '' && $md !== '') $ensure($lk, $md); + } + } + + private static function buildListLayoutForModule(int $seed, string $module, int $domainSeed, string $domPrefix): array { $item = self::pickItemBySemantic($module, $seed); @@ -630,40 +1297,49 @@ class SiteStyle $shells = self::$ITEM_SHELL_MAP[$itemType]; $shell = $shells[$seed % count($shells)]; + $itemTypeNum = self::pickItemTypeNum($domainSeed, $module, $itemType); + $itvCls = self::buildItemTypeVarClass($domPrefix, $itemType, $itemTypeNum); + + $grid = self::buildGridLayout($seed); - // 页面密度:按模块语义决定 - $density = match ($module) { - 'search_list' => 'high', - 'category_list' => 'high', - 'category_list_index' => 'low', - 'rank_list_index' => 'low', - 'news', - 'piaofang', - 'rank', - 'tuijian', - 'update' => 'low', - default => 'normal', - }; + $grid = self::applyH5ColsRuleByItemType($grid, $itemType); + + // rank_home 动态 key:强制低密度(避免三行四行) + if (str_starts_with($module, 'rankhome:') || str_contains($module, 'rankhome:')) { + $density = 'low'; + } else { + + // 页面密度:按模块语义决定 + $density = match ($module) { + 'search_list' => 'high', + 'category_list' => 'high', + 'category_list_index' => 'normal', + 'rank_list_index' => 'normal', + 'news', + 'piaofang', + 'rank', + 'tuijian', + 'lunli', + 'update' => 'low', + default => 'low', + }; + } $layout = self::buildListRowsAndLimit($seed, $grid, $density); return [ 'module' => $module, 'item' => $item, 'item_type' => $itemType, + 'item_type_num' => $itemTypeNum, 'item_variant' => $seed % 20, 'shell' => $shell, - // ✅ title 母型(A/B/C/F) - 'title' => self::pickTitleTpl($seed + 17, $module), // ✅ 行为 class(冻结) //'class' => implode(' ', self::buildListBehavior($seed, $module)), 'class' => trim( implode(' ', self::buildListBehavior($seed, $module)) - . ' ' . self::buildTitleClampClass($domainSeed, $domPrefix) + . ' ' . $itvCls ), - - // ✅ 标题文案(冻结) - 'title_text' => self::buildTitleText($seed + 19, $module), // ✅ 原有 grid 保留 'grid' => $grid, @@ -676,9 +1352,20 @@ class SiteStyle ]; } + private static function applyH5ColsRuleByItemType(array $grid, string $itemType): array + { + if ($itemType === 'poster') { + $grid['cols_h5'] = max(2, (int)($grid['cols_h5'] ?? 1)); + } + // media 允许 1,不处理 + return $grid; + } + + private static function pickTitleTpl(int $seed, string $module): string { $map = [ + 'lunli' => ['A', 'B', 'D'], 'news' => ['A', 'B', 'D'], 'piaofang' => ['A', 'C', 'D', 'B'], 'rank' => ['E', 'D'], @@ -692,6 +1379,30 @@ class SiteStyle return $list[$seed % count($list)]; } + /** + * title 模板选择(支持母型 + 子变体) + * - family:A-F + * - variant:0..19(每个 family 20 套子变体,结构级差异) + */ + private static function pickTitleTplSpec(int $seed, string $module, ?string $forceFamily = null): array + { + $family = $forceFamily ?: self::pickTitleTpl($seed, $module); + $variant = self::pickTitleVariant($seed, $family); + + return [ + 'family' => $family, + 'variant' => $variant, + ]; + } + + private static function pickTitleVariant(int $seed, string $family): int + { + // 保持稳定:同域名/同场景/同 family => 固定 + $h = crc32($seed . '|titlev|' . $family); + return (abs((int)$h) % 20); // 0..19 + //return (abs((int)$h) % 20) + 1; // 1..20 + } + /** * 列表行为(Behavior) @@ -702,6 +1413,7 @@ class SiteStyle switch ($module) { case 'news': + case 'lunli': $behavior[] = ($seed % 2 === 0) ? 'compact' : ''; $behavior[] = ($seed % 2 === 1) ? 'collapsed' : ''; break; @@ -753,6 +1465,10 @@ class SiteStyle $item = (string)($base['item'] ?? '01'); $itemType = self::$ITEM_TYPE_MAP[$item] ?? 'poster'; + // $itemTypeNum = self::pickItemTypeNum($domainSeed, 'category_section', $itemType); + // $itvCls = self::buildItemTypeVarClass($domPrefix, $itemType, $itemTypeNum); + + $variantCount = (int)($cfg['components']['list']['item_variants'][$item] ?? 1); $variant = $variantCount > 0 ? ($seed % $variantCount) : 0; @@ -993,11 +1709,12 @@ class SiteStyle private static function buildTitleText(int $seed, string $module): array { + $arrTitlePool = self::getChannelTitlePool(); $type = self::mapTitleType($module); - if (!isset(self::$TITLE_POOL[$type])) { + if (!isset($arrTitlePool[$type])) { $type = 'news'; } - $pool = self::$TITLE_POOL[$type]; + $pool = $arrTitlePool[$type]; return [ 'primary' => self::pickStable($pool['primary'], $seed), @@ -1006,6 +1723,449 @@ class SiteStyle ]; } + /** + * 分类标题文案池(独立于模块文案池) + * - primary: 主要标题(适合作为区块 h2) + * - secondary: 副标题/说明(可选) + * - seo: SEO 语境短句(可用于 meta/隐藏 h2 补充) + * + * 冻结策略:同域名 + 同分类 key 永久稳定;不同域名自然差异化。 + */ + private static function buildCategoryTitleText( + int $seed, + string $catKey, + string $catName, + string $scene = 'category' + ): array { + // $pool = self::$CATEGORY_TITLE_POOL; + $pool = self::getCategoryTitlePool(); + + // 首页分类块:主标题必须“语义直给”(按分类 name 渲染),模板固定 F + $p = ($scene === 'homecat') + ? $catName + : self::formatCategoryTitle( + (string)self::pickStable($pool['primary'], $seed), + $catKey, + $catName + ); + $s = self::formatCategoryTitle( + (string)self::pickStable($pool['secondary'], $seed >> 2), + $catKey, + $catName + ); + $seo = self::formatCategoryTitle( + (string)self::pickStable($pool['seo'], $seed >> 4), + $catKey, + $catName + ); + + return [ + 'primary' => $p, + 'secondary' => $s, + 'seo' => $seo, + ]; + } + + /* ====================================================== + * SEO Helpers(canonical / robots / alt / detail addon) + * ====================================================== */ + + /** + * 检测页面类型(尽量不依赖路由名,直接看 path) + * return: home|category_index|category_cat1|category_cat2|search|rank_home|detail|play|other + */ + public static function detectPageType(): string + { + $strPath = (string)(app('request')->pathinfo() ?? ''); + $strPath = trim($strPath, '/'); + + if ($strPath === '' || $strPath === 'index.php') return 'home'; + + if (str_starts_with($strPath, 'voddetail/')) return 'detail'; + if (str_starts_with($strPath, 'vodplay/')) return 'play'; + + if (str_starts_with($strPath, 'search')) return 'search'; + + // 下面几类按你项目实际 URL 规则可继续细化 + if (str_contains($strPath, 'rank')) return 'rank_home'; + + if (str_contains($strPath, 'vodtype') || str_contains($strPath, 'videotype')) { + // 你有:分类首页 / 一级分类 / 二级分类(这里兜底统一认为 category) + return 'category_cat2'; + } + + return 'other'; + } + + /** + * 构建 canonical + robots(模板层只读) + * - play: canonical 指向 detail + * - detail: canonical 指向自身(去 query) + */ + public static function buildSeoMeta(array $TpStyle, array $arrVideo = []): array + { + $strPageType = self::detectPageType(); + + $strDomain = (string)(app('request')->domain() ?? ''); + $strPath = '/' . ltrim((string)(app('request')->pathinfo() ?? ''), '/'); + $strCanonical = rtrim($strDomain, '/') . $strPath; // 去 query + + $strRobots = 'index,follow'; + + if ($strPageType === 'play') { + $strRobots = 'noindex,follow'; + + // play canonical -> detail + $strDetailUrl = self::guessDetailUrlFromVideo($arrVideo); + if (!empty($strDetailUrl)) { + $strCanonical = rtrim($strDomain, '/') . $strDetailUrl; + } + } + + // 可选:深分页统一 noindex(如果你想控制薄分页) + // $intPage = (int)(app('request')->param('page', 1)); + // if ($intPage > 1) $strRobots = 'noindex,follow'; + + return [ + 'page_type' => $strPageType, + 'canonical' => $strCanonical, + 'robots' => $strRobots, + ]; + } + + /** + * 从 video 数据推断详情 URL(你可按实际路由改这一段) + * 兜底规则: + * - 有 v_id + v_name_en(或 slug)优先用 slug + * - 否则用 v_id + */ + private static function guessDetailUrlFromVideo(array $arrVideo): string + { + $strSlug = (string)($arrVideo['v_name_en'] ?? $arrVideo['v_slug'] ?? $arrVideo['slug'] ?? ''); + $intId = (int)($arrVideo['v_id'] ?? 0); + + if (!empty($strSlug) && $intId > 0) { + // 你当前详情 URL 示例:/voddetail/qian-long-zai-tian-247979 + return '/voddetail/' . $strSlug . '-' . $intId; + } + if ($intId > 0) { + return '/voddetail/' . $intId; + } + return ''; + } + + /** + * 图片 alt:稳定文案池(详情封面 / 列表封面) + */ + public static function buildImgAlt(array $TpStyle, $arrVideo, string $strSlotType = 'list_poster', string $strItemType = 'poster'): string + { + if (is_object($arrVideo)) { + if (method_exists($arrVideo, 'getArrayCopy')) { + $arrVideo = $arrVideo->getArrayCopy(); + } elseif ($arrVideo instanceof \JsonSerializable) { + $tmp = $arrVideo->jsonSerialize(); + $arrVideo = is_array($tmp) ? $tmp : (array)$tmp; + } else { + $arrVideo = (array)$arrVideo; + } + } + if (!is_array($arrVideo)) $arrVideo = []; + + $intDomainSeed = (int)($TpStyle['seed'] ?? 0); + + $intVid = (int)($arrVideo['v_id'] ?? 0); + $strTitle = (string)($arrVideo['v_name'] ?? $arrVideo['v_title'] ?? ''); + $strYear = (string)($arrVideo['v_year'] ?? ''); + $strCat = (string)($arrVideo['v_category'] ?? $arrVideo['v_parent_category'] ?? ''); + + $strArea = self::joinArr($arrVideo['v_area'] ?? ''); + $strActor = self::joinArr($arrVideo['v_actor'] ?? ''); + $strDirector = self::joinArr($arrVideo['v_director'] ?? ''); + $strLang = self::joinArr($arrVideo['v_lang'] ?? ''); + + if ($strTitle === '') $strTitle = '影片'; + + $arrPool = self::getAltPool($strSlotType); + + $strKey = (string)($intVid > 0 ? $intVid : ($arrVideo['v_name_en'] ?? $strTitle)); + $intPickSeed = (int)crc32($intDomainSeed . '|alt|' . $strSlotType . '|' . $strItemType . '|' . $strKey); + + $strTpl = $arrPool[abs($intPickSeed) % count($arrPool)]; + + $strAlt = strtr($strTpl, [ + '{title}' => $strTitle, + '{year}' => $strYear, + '{area}' => $strArea, + '{cat}' => $strCat, + ]); + + // 清理空字段残留(避免出现 “2024” 空拼接导致的多余字符) + $strAlt = preg_replace('/\s+/', ' ', trim($strAlt)); + $strAlt = str_replace(['《》', '()', '()'], '', $strAlt); + + return $strAlt ?: $strTitle; + } + + private static function getAltPool(string $strSlotType): array + { + $firelFilele = 'public/initdata/video/text_pool/alt/image.php'; + $file = root_path() . $firelFilele; + + if (!is_file($file)) { + return []; + } + + $pool = require $file; + + // 明确 slot 映射,防止随便传值 + $map = [ + 'detail_cover' => 'detail_cover', + 'list_cover' => 'list_cover', + ]; + + $key = $map[$strSlotType] ?? 'list_cover'; + + return $pool[$key] ?? []; + } + + /** + * 详情页“防薄内容”模块:看点一句话 + 标签 + 提示(稳定、域名级差异、同片稳定) + * 模板只渲染,不做运行期随机 + */ + public static function buildDetailSeoAddon(array $arrTpStyle, array $arrVideo): array + { + $intDomainSeed = (int)($arrTpStyle['seed'] ?? 0); + + $intVid = (int)($arrVideo['v_id'] ?? 0); + $strKey = (string)($intVid > 0 ? $intVid : ($arrVideo['v_name_en'] ?? ($arrVideo['v_name'] ?? '0'))); + + $strTitle = (string)($arrVideo['v_name'] ?? '该片'); + $strYear = (string)($arrVideo['v_year'] ?? ''); + $strParentCategory = (string)($arrVideo['v_parent_category'] ?? ''); + $strCategory = (string)($arrVideo['v_category'] ?? ''); + $strCat = $strCategory !== '' ? $strCategory : ($strParentCategory !== '' ? $strParentCategory : '影视'); + + // 这四个你说是数组:做统一 join(也兼容字符串) + $strArea = self::joinArr($arrVideo['v_area'] ?? []); + $strLang = self::joinArr($arrVideo['v_lang'] ?? []); + $strActor = self::joinArr($arrVideo['v_actor'] ?? []); + $strDirector = self::joinArr($arrVideo['v_director'] ?? []); + + $strArea1 = self::pickFirst($arrVideo['v_area'] ?? []); + $strLang1 = self::pickFirst($arrVideo['v_lang'] ?? []); + $strActor1 = self::pickFirst($arrVideo['v_actor'] ?? []); + $strDirector1 = self::pickFirst($arrVideo['v_director'] ?? []); + + $baseFilele = 'public/initdata/video/text_pool/detail_addon/'; + $baseFilele = root_path() . $baseFilele; + $arrSummaryTpls = is_file($baseFilele . 'summary.php') ? require $baseFilele . 'summary.php' : []; + $arrCommon = is_file($baseFilele . 'tags.php') ? require $baseFilele . 'tags.php' : []; + $arrTipPool = is_file($baseFilele . 'tip.php') ? require $baseFilele . 'tip.php' : []; + + + // ========== A:一句话看点(更像真实编辑;按“是否有主演/导演/地区”选择可用句式) ========== + + $intSeedA = (int)crc32($intDomainSeed . '|synx|a|' . $strKey); + $arrSummaryTpls = self::shuffleStableCompat($arrSummaryTpls, $intSeedA); + + $strA = ''; + foreach ($arrSummaryTpls as $it) { + $arrNeed = $it['need'] ?? []; + $boolOk = true; + + foreach ($arrNeed as $strNeed) { + if ($strNeed === 'year' && $strYear === '') { + $boolOk = false; + break; + } + if ($strNeed === 'area1' && $strArea1 === '') { + $boolOk = false; + break; + } + if ($strNeed === 'actor1' && $strActor1 === '') { + $boolOk = false; + break; + } + if ($strNeed === 'director1' && $strDirector1 === '') { + $boolOk = false; + break; + } + } + + if ($boolOk) { + $strA = (string)($it['tpl'] ?? ''); + break; + } + } + + if ($strA === '') { + $strA = '《{title}》信息密度更高,节奏偏利落,适合想快速进入剧情的观众。'; + } + + // 替换占位符(有值就用,无值用“合理兜底”,避免句子空洞) + $strA = strtr($strA, [ + '{title}' => $strTitle, + '{year}' => $strYear !== '' ? $strYear : '近期', + '{area}' => $strArea1 !== '' ? $strArea1 : '本土', + '{cat}' => $strCat !== '' ? $strCat : '影视', + '{actor}' => $strActor1 !== '' ? $strActor1 : ($strActor !== '' ? $strActor : '主演阵容'), + '{director}' => $strDirector1 !== '' ? $strDirector1 : ($strDirector !== '' ? $strDirector : '主创'), + ]); + + // ========== B:标签(3–6 个稳定;优先“分类/年份/地区/语言/主演/导演”,再补运营短标签) ========== + $arrTags = []; + + if ($strCategory !== '') $arrTags[] = $strCategory; + else if ($strParentCategory) $arrTags[] = $strParentCategory; + + if ($strYear !== '') $arrTags[] = $strYear . '出品'; + + if ($strArea1 !== '') $arrTags[] = $strArea1 . '作品'; + + // 语言标签:不做“语言:xx”这种长标签,直接用最常见的“国语/粤语/英语”等 + if ($strLang1 !== '') $arrTags[] = $strLang1; + + if ($strActor1 !== '') $arrTags[] = $strActor1 . '主演'; + if ($strDirector1 !== '') $arrTags[] = $strDirector1 . '导演'; + + $intSeedB = (int)crc32($intDomainSeed . '|synx|b|' . $strKey); + $intNeed = 3 + (abs($intSeedB) % 4); // 3..6 + + $arrCommon = self::shuffleStableCompat($arrCommon, $intSeedB + 19); + foreach ($arrCommon as $strT) { + if (count($arrTags) >= $intNeed) break; + if (!in_array($strT, $arrTags, true)) $arrTags[] = $strT; + } + + + $intSeedC = (int)crc32($intDomainSeed . '|synx|c|' . $strKey); + $strTip = ((abs($intSeedC) % 2) === 0) ? $arrTipPool[abs($intSeedC) % count($arrTipPool)] : ''; + + // 最终清洗:去空、去重、收敛长度 + $arrTags = array_values(array_unique(array_filter($arrTags, static function ($v) { + $str = trim((string)$v); + return $str !== ''; + }))); + + // 标签上限(防止你后续扩展字段导致溢出) + if (count($arrTags) > 6) { + $arrTags = array_slice($arrTags, 0, 6); + } + + return [ + 'summary' => $strA, + 'tags' => $arrTags, + 'tip' => $strTip, + ]; + } + + /** + * 数组/字符串统一转字符串(你提到 v_area/v_actor/v_director/v_lang 是数组) + */ + private static function joinArr($mixVal, string $strSep = '/'): string + { + if (is_array($mixVal)) { + $arr = array_values(array_filter(array_map(static function ($v) { + return trim((string)$v); + }, $mixVal), static function ($v) { + return $v !== ''; + })); + return implode($strSep, $arr); + } + + $str = trim((string)$mixVal); + if ($str === '') return ''; + + // 兼容 “张三,李四/王五|赵六” 等来源格式 + $arr = preg_split('/[,\s\/\|]+/u', $str); + $arr = array_values(array_filter(array_map(static function ($v) { + return trim((string)$v); + }, $arr), static function ($v) { + return $v !== ''; + })); + + return implode($strSep, $arr); + } + + /** + * 取第一项(数组/字符串都支持),用于“主演/导演/地区/语言”更自然的句式与短标签 + */ + private static function pickFirst($mixVal): string + { + if (is_array($mixVal)) { + $str = trim((string)($mixVal[0] ?? '')); + return $str; + } + + $str = trim((string)$mixVal); + if ($str === '') return ''; + + $arr = preg_split('/[,\s\/\|]+/u', $str); + $str1 = trim((string)($arr[0] ?? '')); + + return $str1; + } + + /** + * 兼容:如果你已有 shuffleStable() 就用你已有的; + * 如果没有,就用这个稳定洗牌(域名 seed 冻结) + */ + private static function shuffleStableCompat(array $arr, int $intSeed): array + { + if (method_exists(__CLASS__, 'shuffleStable')) { + /** @phpstan-ignore-next-line */ + return self::shuffleStable($arr, $intSeed); + } + + // fallback:稳定洗牌 + $arrKeys = array_keys($arr); + $arrRand = []; + foreach ($arrKeys as $k) { + $arrRand[$k] = crc32($intSeed . '|' . (string)$k); + } + uasort($arrRand, static function ($a, $b) { + if ($a === $b) return 0; + return ($a < $b) ? -1 : 1; + }); + + $arrOut = []; + foreach (array_keys($arrRand) as $k) { + $arrOut[] = $arr[$k]; + } + return $arrOut; + } + + + + + private static function buildMoreText(int $seed, string $scene = 'default', ?string $name = null): string + { + $pools = self::getMoreTextPool(); + $list = $pools[$scene] ?? $pools['default']; + + $h = crc32($seed . '|more|' . $scene); + $txt = (string)$list[abs((int)$h) % count($list)]; + + if ($name !== null && $name !== '') { + $txt = str_replace('{name}', $name, $txt); + } else { + // 如果没有 name,去掉占位符残留 + $txt = str_replace('{name}', '', $txt); + } + + return trim($txt); + } + + private static function formatCategoryTitle(string $tpl, string $catKey, string $catName): string + { + // 统一占位符:{name}/{key} + return str_replace( + ['{name}', '{key}'], + [$catName, $catKey], + $tpl + ); + } + /* ====================================================== * Title 文案池 * ====================================================== */ @@ -1052,6 +2212,7 @@ class SiteStyle ]; private static array $MODULE_ITEM_WEIGHT = [ + 'lunli' => ['primary' => ['01', '04'], 'secondary' => ['02'], 'fallback' => ['01']], 'news' => ['primary' => ['01', '04'], 'secondary' => ['02'], 'fallback' => ['01']], 'piaofang' => ['primary' => ['01', '04'], 'secondary' => ['02'], 'fallback' => ['01']], 'rank' => ['primary' => ['05'], 'secondary' => ['03'], 'fallback' => ['05']], @@ -1061,41 +2222,45 @@ class SiteStyle 'search_list' => ['primary' => ['02'], 'secondary' => ['01'], 'fallback' => ['02']], ]; - private static array $TITLE_POOL = [ - 'news' => [ - 'primary' => ['最近更新', '最新上线', '新片速递', '今日更新', '新内容推荐'], - 'secondary' => ['第一时间为你呈现', '每日持续更新', '不错过任何新片', '刚刚上线,抢先观看'], - 'seo' => ['最新电影电视剧更新', '今日最新影视资源', '新上线影视内容合集'], - ], - 'update' => [ - 'primary' => ['最近更新', '最新上线', '新片速递', '今日更新', '新内容推荐'], - 'secondary' => ['第一时间为你呈现', '每日持续更新', '不错过任何新片', '刚刚上线,抢先观看'], - 'seo' => ['最新电影电视剧更新', '今日最新影视资源', '新上线影视内容合集'], - ], - 'piaofang' => [ - 'primary' => ['热门推荐', '热播精选', '人气必看', '大家都在看'], - 'secondary' => ['近期热度持续攀升', '高点击率影片推荐', '口碑与热度兼具', '当前最受欢迎内容'], - 'seo' => ['热门影视作品推荐', '高人气电影电视剧合集', '热播影视排行榜推荐'], - ], - 'tuijian' => [ - 'primary' => ['热门推荐', '热播精选', '人气必看', '大家都在看'], - 'secondary' => ['近期热度持续攀升', '高点击率影片推荐', '口碑与热度兼具', '当前最受欢迎内容'], - 'seo' => ['热门影视作品推荐', '高人气电影电视剧合集', '热播影视排行榜推荐'], - ], - 'rank' => [ - 'primary' => ['排行榜', '热度榜单', '人气排行', '播放榜'], - 'secondary' => ['数据实时更新', '热度排序参考', '近期播放趋势', '高人气作品排行'], - 'seo' => ['影视排行榜前十名', '热门电影电视剧排行', '高播放量影视榜单'], - ], - ]; - // SiteStyle.php + /** + * 分类标题文案池(用于 home.categories / category_list 等 “分类语境” 的标题) + * - {name}:中文分类名(例如“电影”) + * - {key} :拼音 key(例如“dian-ying”) + */ + private static function getCategoryTitlePool(): array + { + $file = 'public/initdata/video/text_pool/seo/category_title.php'; + $file = root_path() . $file; + return is_file($file) ? require $file : []; + } + + /** + * 标题更多文案池(titleF 右侧“更多”文本) + * - {name}:中文分类名 + */ + private static function getMoreTextPool(): array + { + $file = 'public/initdata/video/text_pool/seo/more_text.php'; + $file = root_path() . $file; + return is_file($file) ? require $file : []; + } + + private static function getChannelTitlePool(): array + { + $file = 'public/initdata/video/text_pool/seo/channel_title.php'; + $file = root_path() . $file; + + return is_file($file) ? require $file : []; + } + + // 对应屏幕 行数范围 protected static $LIST_DENSITY_ROW_RANGE = [ 'low' => [ - 'lg' => [2, 3], - 'md' => [2, 3], - 'sm' => [2, 3], - 'h5' => [2, 4], + 'lg' => [1, 2], + 'md' => [1, 3], + 'sm' => [1, 3], + 'h5' => [1, 4], ], 'normal' => [ 'lg' => [2, 4], @@ -1104,16 +2269,316 @@ class SiteStyle 'h5' => [2, 4], ], 'high' => [ - 'lg' => [4, 8], + 'lg' => [4, 6], 'md' => [4, 8], 'sm' => [4, 8], 'h5' => [4, 8], ], ]; private static array $RANK_HOME_MODULES = [ - 'rank_daily' => ['title' => '日排行榜', 'sort_type' => 'daily'], - 'rank_weekly' => ['title' => '周排行榜', 'sort_type' => 'weekly'], - 'rank_monthly' => ['title' => '月排行榜', 'sort_type' => 'monthly'], - 'rank_total' => ['title' => '总排行榜', 'sort_type' => 'total'], + 'rank_daily' => ['title' => '日', 'sort_type' => 'daily'], + 'rank_weekly' => ['title' => '周', 'sort_type' => 'weekly'], + 'rank_monthly' => ['title' => '月', 'sort_type' => 'monthly'], + 'rank_total' => ['title' => '总', 'sort_type' => 'total'], ]; + + // ============================== + // 你只维护这里:手写占位图 + // ============================== + private static array $LAZY_IMG_POOL_MANUAL = [ + '/static/img/lazy/01.webp', + '/static/img/lazy/02.webp', + '/static/img/lazy/03.webp', + '/static/img/lazy/04.webp', + '/static/img/lazy/05.webp', + // 以后新增就继续 append + ]; + + // 默认兜底 + private const LAZY_DEFAULT = '/static/img/lazy/default.webp'; + + // 缓存最终池子(合并后的) + private static ?array $LAZY_IMG_POOL = null; + + /** + * 构建最终懒加载占位图池:手写 + svg(1-100) + webp(01-10) + * - 仅构建一次 + * - 去重并重建索引 + */ + private static function getLazyImgPool(): array + { + if (self::$LAZY_IMG_POOL !== null) { + return self::$LAZY_IMG_POOL; + } + + // 1) SVG 1-100:/static/img/lazy/svg/loding_1.svg ~ loding_100.svg + $svgPool = array_map( + static fn(int $i): string => sprintf('/static/img/loding/loding_%d.svg', $i), + range(1, 100) + ); + + // 2) WEBP 01-10:/static/img/lazy/webp/01.webp ~ 10.webp + // 如目录不同,改这里即可 + $webpPool = array_map( + static fn(int $i): string => sprintf('/static/img/lazy/%02d.webp', $i), + range(1, 10) + ); + + // 3) 合并 + 去重 + 重建索引 + $merged = array_merge(self::$LAZY_IMG_POOL_MANUAL, $svgPool, $webpPool); + $merged = array_values(array_unique(array_filter($merged, 'is_string'))); + + // 最终兜底:保证永远不为空 + self::$LAZY_IMG_POOL = !empty($merged) ? $merged : [self::LAZY_DEFAULT]; + + return self::$LAZY_IMG_POOL; + } + + /** + * 域名级稳定选择一张懒加载占位图 + * - 同域名恒定(host + seed) + * - 池子可扩展(新增图片只改手写数组或扩展范围) + */ + public static function pickLazyImg(array $arrTpStyle): string + { + $pool = self::getLazyImgPool(); + $count = count($pool); + if ($count === 0) { + return self::LAZY_DEFAULT; + } + + $host = (string)($arrTpStyle['host'] ?? ''); + $seedRaw = $arrTpStyle['seed'] ?? 0; + + // seed 允许字符串数字,非数字就当 0 + $seed = is_numeric($seedRaw) ? (int)$seedRaw : 0; + + // host 为空时仍保持稳定(使用 default key) + $key = ($host !== '' ? $host : 'nohost') . '|lazy|' . $seed; + + // crc32 在 PHP 里可能返回 signed int;用 sprintf('%u') 转成无符号再取模更稳 + $hUnsigned = (int)sprintf('%u', crc32($key)); + $idx = $hUnsigned % $count; + + return $pool[$idx] ?? self::LAZY_DEFAULT; + } + + /** + * 基于 seed 生成 SEO phrase pick(域名级冻结) + */ + private static function buildSeoPhrasePick(int $seed, array $slotLen): array + { + $pick = []; + + foreach ($slotLen as $page => $fields) { + foreach ($fields as $field => $slots) { + + // $slots 是 [slotIndex => length] + $slotCount = is_array($slots) ? count($slots) : 0; + + if ($slotCount <= 1) { + $pick[$page][$field] = 0; + } else { + $pick[$page][$field] = abs($seed) % $slotCount; + } + } + } + + return $pick; + } + + /** + * 构建完整 SEO 配置(冻结层) + */ + private static function buildSeoCfg(int $seed): array + { + $poolId = self::pickSeoPoolId($seed); + + // 1️⃣ 计算 slot_len(只算“有几个 slot”,不读句子) + $slotLen = self::buildSeoSlotLenFromFs($poolId); + + // 2️⃣ 基于 slot_len 冻结 slot 选择 + $pick = self::buildSeoPhrasePick($seed, $slotLen); + + // 3️⃣ 冻结 slot 内 idx + $phraseIdx = self::buildSeoPhraseIdx( + $seed, + $slotLen, + $pick + ); + + + return [ + 'pool_id' => $poolId, + 'pool_version' => 1, + 'phrase_pick' => $pick, + 'phrase_idx' => $phraseIdx, + 'slot_len' => $slotLen, + ]; + } + + /** + * 冻结 slot 内的 phrase 索引(终态) + * + * 规则: + * - idx 只和 seed + page + field 有关 + * - 与运行期 path / host 无关 + * - slot 扩容、文案新增都不影响旧 idx + */ + private static function buildSeoPhraseIdx( + int $seed, + array $slotLen, + array $phrasePick + ): array { + $result = []; + + foreach ($slotLen as $page => $fields) { + foreach ($fields as $field => $slots) { + + // 当前页面字段用哪个 slot(已冻结) + $slot = $phrasePick[$page][$field] ?? 0; + + $len = $slots[$slot] ?? 0; + if ($len <= 0) { + $result[$page][$field] = 0; + continue; + } + + /** + * idx 冻结算法: + * - 只依赖 seed + page + field + * - 不依赖 pathinfo / host + * - 永久稳定 + */ + $idx = abs(crc32( + $seed . '|' . $page . '|' . $field + )) % $len; + + $result[$page][$field] = $idx; + } + } + + return $result; + } + + + + private static function buildSeoSlotLenFromFs(string $poolId): array + { + $baseDir = root_path("public/initdata/video/pools/{$poolId}"); + $result = []; + + if (!is_dir($baseDir)) { + return $result; + } + + foreach (glob($baseDir . '/*', GLOB_ONLYDIR) as $pageDir) { + $pageType = basename($pageDir); + + foreach (['title', 'keywords', 'description'] as $field) { + $file = $pageDir . '/' . $field . '.php'; + if (!is_file($file)) { + continue; + } + + $data = require $file; + if (!is_array($data)) { + continue; + } + + // slot_len:每个 slot 里有多少条 + foreach ($data as $slot => $list) { + $result[$pageType][$field][$slot] = is_array($list) + ? count($list) + : 0; + } + } + } + + return $result; + } + + /** + * 根据域名 seed 选择 SEO Pool ID(域名级稳定) + */ + private static function pickSeoPoolId(int $seed): string + { + $baseDir = root_path('public/initdata/video/pools/'); + + $poolIds = []; + foreach (glob($baseDir . 'pool_*', GLOB_ONLYDIR) as $dir) { + $poolIds[] = basename($dir); + } + + // 兜底:一个都没有,给默认 + if (!$poolIds) { + return 'pool_v1'; + } + + // 只有一套,直接用 + if (count($poolIds) === 1) { + return $poolIds[0]; + } + + // 多套:域名级稳定取模 + $idx = abs($seed) % count($poolIds); + return $poolIds[$idx]; + } + + /** + * 构建 SEO slot 冻结长度(用于稳定句子选择) + * + * 规则: + * - 每个 pageType + * - 每个字段(title / keywords / description) + * - 每个 slot + * 记录其“初始句子数量” + * + * ⚠️ 后续 slot 内 append 文案,不影响这里的值 + */ + private static function buildSeoSlotLen(array $seoPhrase): array + { + $slotLen = []; + + foreach ($seoPhrase as $field => $pages) { + foreach ($pages as $pageType => $slots) { + + // slots 必须是二维数组(slot => sentences) + if (!is_array($slots)) { + continue; + } + + foreach ($slots as $slotIdx => $sentences) { + + // 只统计数组型句子池 + if (is_array($sentences)) { + $slotLen[$pageType][$field][$slotIdx] = count($sentences); + } + } + } + } + + return $slotLen; + } + + private static function loadInitData(string $relFile): array + { + static $memo = []; + + $key = $relFile; + + $file = root_path() . $relFile; + + if (!is_file($file)) { + return []; + } + + $data = require $file; + if (!is_array($data)) { + return []; + } + + return $memo[$key] = $data; + return $memo[$relFile] = (is_array($data) ? $data : []); + } } diff --git a/code/app/common/helper/UrlBuilder.php b/code/app/common/helper/UrlBuilder.php new file mode 100644 index 0000000..dab8bbf --- /dev/null +++ b/code/app/common/helper/UrlBuilder.php @@ -0,0 +1,229 @@ +tp = $tpStyle; + } + + /* ========================================================== + * 基础能力 + * ========================================================== */ + + protected function family(): array + { + return $this->tp['template_cfg']['url_family'] ?? []; + } + + protected function trimSlash(string $path): string + { + return '/' . ltrim($path, '/'); + } + + /* ========================================================== + * 首页 + * ========================================================== */ + + public function home(): string + { + return '/'; + } + + /* ========================================================== + * 分类 + * ========================================================== */ + /** + * 分类首页(展示所有一级分类) + * pattern: classes + */ + public function categoryHome(): string + { + $pattern = $this->family()['category_home']['pattern'] + ?? 'classes'; + + return $this->replacePattern($pattern, []); + } + + /** + * 一级分类页 + * pattern: classes/{strParentCategory} + */ + public function categoryParent(string $strParentCategory): string + { + $pattern = $this->family()['category_parent']['pattern'] + ?? 'classes/{strParentCategory}'; + + return $this->replacePattern($pattern, [ + 'strParentCategory' => $strParentCategory, + ]); + } + + /** + * 二级分类页(分页) + * pattern: classes/{strParentCategory}/{strCategory}/page/{intPage} + */ + public function categoryChild( + string $strParentCategory, + string $strCategory, + int|string $intPage = 1 + ): string { + $pattern = $this->family()['category_child']['pattern'] + ?? 'classes/{strParentCategory}/{strCategory}/page/{intPage}'; + + return $this->replacePattern($pattern, [ + 'strParentCategory' => $strParentCategory, + 'strCategory' => $strCategory, + 'intPage' => max(1, (int)$intPage), + ]); + } + + + /* ========================================================== + * 排行榜 + * ========================================================== */ + + /** + * 排行榜首页 + * /rank/index + */ + public function rankIndex(): string + { + $pattern = $this->family()['rank_index']['pattern'] + ?? 'rank/index'; + + return $this->replacePattern($pattern, []); + } + + + /** + * 排行榜列表 + * /rank/day + * /rank/day/action + */ + public function rankList(string $strSortType): string + { + $tpl = $this->family()['rank_list']['pattern'] + ?? 'rank/{strSortType}'; + + return $this->replacePattern($tpl, [ + 'strSortType' => $strSortType, + ]); + } + + /* ========================================================== + * 详情页 + * ========================================================== */ + + /** + * 详情页 + * /voddetail/slug-id + */ + public function detail(string $strPinyin, int $intVId): string + { + $pattern = $this->family()['detail']['pattern'] + ?? 'voddetail/{strPinyin}-{intVId}'; + + return $this->replacePattern($pattern, [ + 'strPinyin' => $strPinyin, + 'intVId' => $intVId, + ]); + } + + /** + * 伪造详情页 + * pattern: video/{strPinyin}-{intVId}-{intVForgeId} + */ + public function detailForge( + string $strPinyin, + int $intVId, + int $intVForgeId + ): string { + $pattern = $this->family()['detail_forge']['pattern'] + ?? 'video/{strPinyin}-{intVId}-{intVForgeId}'; + + return $this->replacePattern($pattern, [ + 'strPinyin' => $strPinyin, + 'intVId' => $intVId, + 'intVForgeId' => $intVForgeId, + ]); + } + + + /** + * 播放页 + * /vodplay/slug-id-line-episode + */ + public function play( + string $strPinyin, + int $intVId, + string $strPlayType, + int $intPlayIndex + ): string { + $pattern = $this->family()['play']['pattern'] + ?? 'vodplay/{strPinyin}-{intVId}-{strPlayType}-{intPlayIndex}'; + + return $this->replacePattern($pattern, [ + 'strPinyin' => $strPinyin, + 'intVId' => $intVId, + 'strPlayType' => $strPlayType, + 'intPlayIndex' => $intPlayIndex, + ]); + } + + + /* ========================================================== + * 搜索 + * ========================================================== */ + + /** + * 搜索入口 + * /search.html + */ + public function searchEntry(): string + { + $entry = $this->family()['search']['pattern'] ?? 'search.html'; + return $this->trimSlash($entry); + } + + /** + * 搜索结果 + * /search.html?keyword=xxx + */ + public function searchResult(string $keyword): string + { + $entry = $this->searchEntry(); + return $entry . '?keyword=' . urlencode($keyword); + } + + + /** + * 历史记录 + */ + public function history(): string + { + $pattern = $this->family()['history']['pattern'] + ?? 'history'; + + return $this->replacePattern($pattern, []); + } + + + + protected function replacePattern(string $pattern, array $vars): string + { + foreach ($vars as $key => $val) { + $pattern = str_replace('{' . $key . '}', (string)$val, $pattern); + } + return $this->trimSlash($pattern); + } +} diff --git a/code/app/common/seo/SeoRenderer.php b/code/app/common/seo/SeoRenderer.php new file mode 100644 index 0000000..07720a2 --- /dev/null +++ b/code/app/common/seo/SeoRenderer.php @@ -0,0 +1,180 @@ +cfg = $tpStyle['template_cfg']['seo'] ?? []; + + // 根据 pool_id 取运行期池(不冻结) + // $poolId = $this->cfg['pool_id'] ?? ''; + // $this->pool = SiteStyle::SEO_POOL[$poolId]['seo_phrase'] ?? []; + } + + /** + * 只负责:选一条模板 + */ + // public function getTemplate(string $field, string $strPage): string + // { + + // // 1️⃣ 取当前字段 + 页面类型的文案池 + // $phrases = loadSeoFile( + // $this->cfg['pool_id'], + // $strPage, + // $field + // ); + + // if (!$phrases) { + // return ''; + // } + + // // 2️⃣ 冻结的 slot + // $slot = $this->cfg['phrase_pick'][$strPage][$field] ?? 0; + + // // slot 是二维 + // if (isset($phrases[$slot]) && is_array($phrases[$slot])) { + // $list = $phrases[$slot]; + // } else { + // $list = $phrases; + // } + + // // 3️⃣ 稳定消费 index(不受扩容影响) + // $len = $this->cfg['slot_len'][$strPage][$field] ?? count($list); + // $len = max(1, min($len, count($list))); + + // // $idx = abs(crc32( + // // Request::pathinfo() ?: Request::host() + // // )) % $len; + // $idx = $this->cfg['phrase_idx'][$strPage][$field] ?? 0; + // return $list[$idx] ?? ''; + + + // return $list[$idx] ?? ''; + // } + // public function getTemplate(string $field, string $strPage): string + // { + // if (empty($this->cfg['pool_id'])) { + // return ''; + // } + + // // 1️⃣ 加载运行期文案池(不冻结) + // $phrases = loadSeoFile( + // $this->cfg['pool_id'], + // $strPage, + // $field + // ); + + // if (!$phrases || !is_array($phrases)) { + // return ''; + // } + + // // 2️⃣ 冻结 slot + // $slot = $this->cfg['phrase_pick'][$strPage][$field] ?? 0; + + // if (isset($phrases[$slot]) && is_array($phrases[$slot])) { + // $list = $phrases[$slot]; + // } else { + // // 兜底:当成单 slot + // $list = $phrases; + // } + + // if (!$list) { + // return ''; + // } + + // // 3️⃣ 冻结 idx(核心) + // $idx = $this->cfg['phrase_idx'][$strPage][$field] ?? 0; + + // return $list[$idx] ?? ''; + // } + + + public function getTemplate(string $field, string $strPage): string + { + if (empty($this->cfg['pool_id'])) { + return ''; + } + + // 1️⃣ 加载运行期文案池 + $phrases = loadSeoFile( + $this->cfg['pool_id'], + $strPage, + $field + ); + + if (!$phrases || !is_array($phrases)) { + return ''; + } + + // 2️⃣ 冻结 slot + $slot = $this->cfg['phrase_pick'][$strPage][$field] ?? 0; + + if (isset($phrases[$slot]) && is_array($phrases[$slot])) { + $list = $phrases[$slot]; + } else { + $list = $phrases; + } + + if (!$list) { + return ''; + } + + // =============================== + // 3️⃣ idx 选择(终态) + // =============================== + + $idx = 0; + + switch ($strPage) { + + case 'detail': + case 'play': + // 视频实体页:seed + videoId + $videoId = (int) Request::param('intVId', 0); + + if ($videoId > 0) { + $len = count($list); + $idx = abs(crc32( + $this->cfg['pool_id'] + . '|detail|' + . $field + . '|' . $videoId + )) % $len; + } + break; + + case 'detail_forge': + // 伪造详情:seed + videoId + forgeId + $videoId = (int) Request::param('intVId', 0); + $forgeId = (int) Request::param('intVForgeId', 0); + + if ($videoId > 0 && $forgeId > 0) { + $len = count($list); + $idx = abs(crc32( + $this->cfg['pool_id'] + . '|detail_forge|' + . $field + . '|' . $videoId + . '|' . $forgeId + )) % $len; + } + break; + + default: + // 非实体页:域名级冻结 idx + $idx = $this->cfg['phrase_idx'][$strPage][$field] ?? 0; + break; + } + + return $list[$idx] ?? ''; + } +} diff --git a/code/app/home/config/router.php b/code/app/home/config/router.php index 2710803..89cbae3 100644 --- a/code/app/home/config/router.php +++ b/code/app/home/config/router.php @@ -6,712 +6,890 @@ declare(strict_types=1); // use app\home\controller\Novel; // use app\home\controller\User; use app\services\SiteContext; +use app\common\helper\SiteStyle; use think\facade\Route; +$siteContext = app(SiteContext::class); +$strTmpCode = $siteContext->getTemplate(); + +/* +|-------------------------------------------------------------------------- +| ENV 开关 +|-------------------------------------------------------------------------- +| true => 新 GPT 模板路由(B 方案) +| false => 旧模板路由(完全不动) +*/ +if ($strTmpCode == 'videoGpt1') { +// if (env('IS_GPT_TMP', false)) { + + // ---------- Common public routes (keep compatible) ---------- + Route::get('/robots', function (\think\Request $Request, SiteContext $SiteContext) { + return view('sitemap/robots.txt'); + })->ext('txt'); + + Route::get('/sitemap', function (\think\Request $Request, SiteContext $SiteContext) { + return view('video/getMap.html'); + })->ext('html'); + + Route::get('rss/baidu', function () { + return view('rss/baidu.xml')->contentType('text/xml'); + })->ext('xml'); + + Route::get('rss/so', function () { + return view('rss/so.xml')->contentType('text/xml'); + })->ext('xml'); + + Route::get('/sitemap_index', function (\think\Request $Request, SiteContext $SiteContext) { + return $SiteContext->getSiteMapByCode('INDEX'); + })->ext('xml'); + + Route::get('/sitemap-main', function (\think\Request $Request, SiteContext $SiteContext) { + return $SiteContext->getSiteMapByCode('MAIN'); + })->ext('xml'); + + Route::get('/sitemap-videos-:page', function (\think\Request $Request, SiteContext $SiteContext) { + return $SiteContext->getSiteMapByCode('VIDEO'); + })->ext('xml'); + + // ========== 1️⃣ 取当前域名冻结的 family ========== + $tpStyle = SiteStyle::getConfig(); + $family = $tpStyle['template_cfg']['url_family']; + + // ========== 2️⃣ 防重复注册 ========== + $registered = []; + + $register = function ( + string $route, + string $view, + array $pattern = [] + ) use (&$registered) { + + if (isset($registered[$route])) { + return; + } + + $r = Route::get($route, fn() => view($view)); + + if ($pattern) { + $r->pattern($pattern); + } + + $registered[$route] = true; + }; + + // ========== 2.5️⃣ 冻结前按 sort 排序 ========== + $sortedFamily = []; + + // 1️⃣ 先保留 id / home(顺序固定) + if (isset($family['id'])) { + $sortedFamily['id'] = $family['id']; + } + if (isset($family['home'])) { + $sortedFamily['home'] = $family['home']; + } + + // 2️⃣ 收集需要排序的页面 + $pagesToSort = []; + + foreach ($family as $page => $cfg) { + if ($page === 'id' || $page === 'home') { + continue; + } + if (isset($cfg['sort'])) { + $pagesToSort[$page] = $cfg; + } + } + + // 3️⃣ 按 sort 升序排序(越小越先注册) + uasort($pagesToSort, function ($a, $b) { + return $a['sort'] <=> $b['sort']; + }); + + // 4️⃣ 合并回最终顺序 + foreach ($pagesToSort as $page => $cfg) { + $sortedFamily[$page] = $cfg; + } + + // ========== 3️⃣ 编译 + 注册 ========== + foreach ($sortedFamily as $page => $cfg) { + + if (empty($cfg['routes'])) { + continue; + } + + foreach ($cfg['routes'] as $route) { + + switch ($page) { + + case 'home': + $register($route, 'index/index.html'); + break; + + case 'category_home': + $register($route, 'video/getCategory.html'); + break; + case 'category_parent': + $register($route, 'video/getCategoryType.html',[ + 'strParentCategory' => '[a-z\-]*', // 允许空值 + ]); + break; + case 'category_child': + $register($route, 'video/getCategory.html',[ + 'strParentCategory' => '[a-z\-]*', // 允许空值 + 'strCategory' => '[a-z\-]*', // 允许空值 + 'intPage' => '\d*', // 允许空值 + ]); + break; + + case 'detail': + $register($route, 'video/getVideoInfo.html', [ + 'intVId' => '\d+', + 'strPinyin' => '[\w-]+' + ]); + break; + case 'detail_forge': + $register($route, 'video/getVideoInfo.html', [ + 'intVId' => '\d+', + 'strPinyin' => '[\w-]+', + 'intVForgeId' => '\d+' + ]); + break; + + case 'play': + $register($route, 'video/getVideoPlayUrl.html', [ + 'intVId' => '\d+', + 'intPlayIndex' => '\d+' + ]); + break; + + case 'rank_index': + $register($route, 'video/getRankIndex.html'); + break; + case 'rank_list': + $register($route, 'video/getRankIndex.html',[ + 'strParentCategory' => '[a-z\-]*', // 允许空值 + 'strCategory' => '[a-z\-]*', // 允许空值 + 'strSortType' => '[a-z\-]*', // 允许空值 + 'intPage' => '\d*', // 允许空值 + ]); + break; + + case 'search': + $register($route, 'video/getSearchVideo.html'); + break; + + case 'history': + $register($route, 'user/getHistory.html'); + break; + } + } + } + return; + +} else { + // Route::get('/article/fenlei/:category', function (\think\Request $Request, SiteContext $SiteContext) { // return view('test/test01/index.html'); // }); -/** - * robots - */ -Route::get('/robots', function (\think\Request $Request, SiteContext $SiteContext) { - return view('sitemap/robots.txt'); -})->ext('txt'); + /** + * robots + */ + Route::get('/robots', function (\think\Request $Request, SiteContext $SiteContext) { + return view('sitemap/robots.txt'); + })->ext('txt'); -/** - * Sitemap - */ -Route::get('/sitemap', function (\think\Request $Request, SiteContext $SiteContext) { - return view('video/getMap.html'); -})->ext('html'); + /** + * Sitemap + */ + Route::get('/sitemap', function (\think\Request $Request, SiteContext $SiteContext) { + return view('video/getMap.html'); + })->ext('html'); -/** - * /rss/baidu - */ -Route::get('rss/baidu', function () { - return view('rss/baidu.xml') - ->contentType('text/xml'); -})->ext('xml'); + /** + * /rss/baidu + */ + Route::get('rss/baidu', function () { + return view('rss/baidu.xml') + ->contentType('text/xml'); + })->ext('xml'); -/** - * /rss/so - */ -Route::get('rss/so', function () { - return view('rss/so.xml') - ->contentType('text/xml'); -})->ext('xml'); + /** + * /rss/so + */ + Route::get('rss/so', function () { + return view('rss/so.xml') + ->contentType('text/xml'); + })->ext('xml'); -/** - * Sitemap 索引文件 - */ -Route::get('/sitemap_index', function (\think\Request $Request, SiteContext $SiteContext) { - return $SiteContext->getSiteMapByCode('INDEX'); - // return view('sitemap/sitemap_index.xml'); -})->ext('xml'); + /** + * Sitemap 索引文件 + */ + Route::get('/sitemap_index', function (\think\Request $Request, SiteContext $SiteContext) { + return $SiteContext->getSiteMapByCode('INDEX'); + // return view('sitemap/sitemap_index.xml'); + })->ext('xml'); -/** - * 首页、分类列表、排行榜、男生/女生频道。 - */ -Route::get('/sitemap-main', function (\think\Request $Request, SiteContext $SiteContext) { - return $SiteContext->getSiteMapByCode('MAIN'); - // return view('sitemap/sitemap-main.xml'); -})->ext('xml'); + /** + * 首页、分类列表、排行榜、男生/女生频道。 + */ + Route::get('/sitemap-main', function (\think\Request $Request, SiteContext $SiteContext) { + return $SiteContext->getSiteMapByCode('MAIN'); + // return view('sitemap/sitemap-main.xml'); + })->ext('xml'); -/** - * 小说目录页面。 - */ -Route::get('/sitemap-books-catalog-:page', function (\think\Request $Request, SiteContext $SiteContext) { - return $SiteContext->getSiteMapByCode('CATALOG'); - // return view('sitemap/sitemap-books-catalog.xml'); -})->ext('xml'); + /** + * 小说目录页面。 + */ + Route::get('/sitemap-books-catalog-:page', function (\think\Request $Request, SiteContext $SiteContext) { + return $SiteContext->getSiteMapByCode('CATALOG'); + // return view('sitemap/sitemap-books-catalog.xml'); + })->ext('xml'); -/** - * 小说详情页面。 - */ -Route::get('/sitemap-books-:page', function (\think\Request $Request, SiteContext $SiteContext) { - return $SiteContext->getSiteMapByCode('BOOK'); - // return view('sitemap/sitemap-books.xml'); -})->ext('xml'); + /** + * 小说详情页面。 + */ + Route::get('/sitemap-books-:page', function (\think\Request $Request, SiteContext $SiteContext) { + return $SiteContext->getSiteMapByCode('BOOK'); + // return view('sitemap/sitemap-books.xml'); + })->ext('xml'); -/** - * 小说详情页面。 - */ -Route::get('/sitemap-forget-books-:page', function (\think\Request $Request, SiteContext $SiteContext) { - return view('sitemap/sitemap-forget-books.xml'); -})->ext('xml'); + /** + * 小说详情页面。 + */ + Route::get('/sitemap-forget-books-:page', function (\think\Request $Request, SiteContext $SiteContext) { + return view('sitemap/sitemap-forget-books.xml'); + })->ext('xml'); -/** - * 章节页面(如果包含) - */ -Route::get('/sitemap-chapters-:page', function (\think\Request $Request, SiteContext $SiteContext) { - return $SiteContext->getSiteMapByCode('CHAPTER'); - // return view('sitemap/sitemap-chapters.xml'); -})->ext('xml'); + /** + * 章节页面(如果包含) + */ + Route::get('/sitemap-chapters-:page', function (\think\Request $Request, SiteContext $SiteContext) { + return $SiteContext->getSiteMapByCode('CHAPTER'); + // return view('sitemap/sitemap-chapters.xml'); + })->ext('xml'); -/** - * 视频详情页面。 - */ -Route::get('/sitemap-videos-:page', function (\think\Request $Request, SiteContext $SiteContext) { - return $SiteContext->getSiteMapByCode('VIDEO'); - // return view('sitemap/sitemap-books.xml'); -})->ext('xml'); + /** + * 视频详情页面。 + */ + Route::get('/sitemap-videos-:page', function (\think\Request $Request, SiteContext $SiteContext) { + return $SiteContext->getSiteMapByCode('VIDEO'); + // return view('sitemap/sitemap-books.xml'); + })->ext('xml'); -/** - * 搜索首页 - */ -Route::get('/searchhome/0', function (\think\Request $Request, SiteContext $SiteContext) { - return view('novel/getSearchIndex.html'); -})->ext('html'); + /** + * 搜索首页 + */ + Route::get('/searchhome/0', function (\think\Request $Request, SiteContext $SiteContext) { + return view('novel/getSearchIndex.html'); + })->ext('html'); -/** - * 导航 - */ + /** + * 导航 + */ // Route::get('/map', function (\think\Request $Request, SiteContext $SiteContext) { // return view('video/getMap.html'); // })->ext('html'); -/** - * 路由配置文件 - */ -$arrRoutes = [ - // H5/PC 端前缀 - 'prefixes' => [ - 'pc' => ['/pc', '/web', '/desktop', '/index', '/shouye', '/zhuomian','/windows'], - 'h5' => ['', '/nindex'], - ], + /** + * 路由配置文件 + */ + $arrRoutes = [ + // H5/PC 端前缀 + 'prefixes' => [ + 'pc' => ['/pc', '/web', '/desktop', '/index', '/shouye', '/zhuomian', '/windows'], + 'h5' => ['', '/nindex'], + ], - 'app_type' => [ - 'novel' => [ - - // 章节相关路径 - 'chapter' => [ - '/xiaoshuo/:name-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/novel/:name-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/book/:name-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/xiaoshuo-:name-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/novel-:name-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/book-:name-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - - // 兼容 没有name 的路径模板 - '/xiaoshuo/-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/novel/-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/book/-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/xiaoshuo--:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/novel--:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/book--:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - - // 兼容 没有name 的路径模板 - '/xiaoshuo/:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/novel/:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/book/:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/xiaoshuo-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/novel-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - '/book-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', - ], - - // 最新章节-特殊页面处理 - 'latest' => [ - '/xiaoshuo/:name-:intNovelId/chapter/latest', - '/novel/:name-:intNovelId/chapter/latest', - '/book/:name-:intNovelId/chapter/latest', - '/xiaoshuo-:name-:intNovelId/chapter/latest', - '/novel-:name-:intNovelId/chapter/latest', - '/book-:name-:intNovelId/chapter/latest', - - // 兼容 没有name 的路径模板 - '/xiaoshuo/-:intNovelId/chapter/latest', - '/novel/-:intNovelId/chapter/latest', - '/book/-:intNovelId/chapter/latest', - '/xiaoshuo--:intNovelId/chapter/latest', - '/novel--:intNovelId/chapter/latest', - '/book--:intNovelId/chapter/latest', - - // 兼容 没有name 的路径模板 - '/xiaoshuo/:intNovelId/chapter/latest', - '/novel/:intNovelId/chapter/latest', - '/book/:intNovelId/chapter/latest', - '/xiaoshuo-:intNovelId/chapter/latest', - '/novel-:intNovelId/chapter/latest', - '/book-:intNovelId/chapter/latest', - ], - - // 目录相关路径 - 'catalog' => [ - '/xiaoshuo/:name-:intNovelId/mulu/:strOrder/[:intPage]', - '/novel/:name-:intNovelId/catalog/:strOrder/[:intPage]', - '/book/:name-:intNovelId/table/:strOrder/[:intPage]', - '/xiaoshuo/:name-:intNovelId/mulu/:strOrder/[:intPage]', - '/novel/:name-:intNovelId/catalog/:strOrder/[:intPage]', - '/book/:name-:intNovelId/table/:strOrder/[:intPage]', - - // 兼容 没有name 的路径模板 - '/xiaoshuo/-:intNovelId/mulu/:strOrder/[:intPage]', - '/novel/-:intNovelId/catalog/:strOrder/[:intPage]', - '/book/-:intNovelId/table/:strOrder/[:intPage]', - '/xiaoshuo/-:intNovelId/mulu/:strOrder/[:intPage]', - '/novel/-:intNovelId/catalog/:strOrder/[:intPage]', - '/book/-:intNovelId/table/:strOrder/[:intPage]', - - // 兼容 没有name 的路径模板 - '/xiaoshuo/:intNovelId/mulu/:strOrder/[:intPage]', - '/novel/:intNovelId/catalog/:strOrder/[:intPage]', - '/book/:intNovelId/table/:strOrder/[:intPage]', - '/xiaoshuo/:intNovelId/mulu/:strOrder/[:intPage]', - '/novel/:intNovelId/catalog/:strOrder/[:intPage]', - '/book/:intNovelId/table/:strOrder/[:intPage]', - ], - - // 伪小说详情相关路径 - 'kan-novel' => [ - '/kan-xiaoshuo/:name-:intNovelId-:intForgeId', - '/kan-book/:name-:intNovelId-:intForgeId', - '/kan-novel/:name-:intNovelId-:intForgeId', - '/show-novel/:name-:intNovelId-:intForgeId', - '/seo-book/:name-:intNovelId-:intForgeId', - '/show-xiaoshuo-:name-:intNovelId-:intForgeId', - '/kan-xiaoshuo-:name-:intNovelId-:intForgeId', - '/kan-novel-:name-:intNovelId-:intForgeId', - '/kan-book-:name-:intNovelId-:intForgeId', - '/see-novel-:name-:intNovelId-:intForgeId', - '/good-book-:name-:intNovelId-:intForgeId', - - // 兼容 没有name 的路径模板 - '/kan-xiaoshuo/-:intNovelId-:intForgeId', - '/kan-book/-:intNovelId-:intForgeId', - '/kan-novel/-:intNovelId-:intForgeId', - '/show-novel/-:intNovelId-:intForgeId', - '/seo-book/-:intNovelId-:intForgeId', - '/show-xiaoshuo--:intNovelId-:intForgeId', - '/kan-xiaoshuo--:intNovelId-:intForgeId', - '/kan-novel--:intNovelId-:intForgeId', - '/kan-book--:intNovelId-:intForgeId', - '/see-novel--:intNovelId-:intForgeId', - '/good-book--:intNovelId-:intForgeId', - - // 兼容 没有name 的路径模板 - '/kan-xiaoshuo/:intNovelId-:intForgeId', - '/kan-book/:intNovelId-:intForgeId', - '/kan-novel/:intNovelId-:intForgeId', - '/show-novel/:intNovelId-:intForgeId', - '/seo-book/:intNovelId-:intForgeId', - '/show-xiaoshuo-:intNovelId-:intForgeId', - '/kan-xiaoshuo-:intNovelId-:intForgeId', - '/kan-novel-:intNovelId-:intForgeId', - '/kan-book-:intNovelId-:intForgeId', - '/see-novel-:intNovelId-:intForgeId', - '/good-book-:intNovelId-:intForgeId', - ], - - // 小说详情相关路径 + 'app_type' => [ 'novel' => [ - '/xiaoshuo/:name-:intNovelId', - '/novel/:name-:intNovelId', - '/book/:name-:intNovelId', - '/xiaoshuo-:name-:intNovelId', - '/novel-:name-:intNovelId', - '/book-:name-:intNovelId', - // 兼容 没有name 的路径模板 - '/xiaoshuo/-:intNovelId', - '/novel/-:intNovelId', - '/book/-:intNovelId', - '/xiaoshuo--:intNovelId', - '/novel--:intNovelId', - '/book--:intNovelId', + // 章节相关路径 + 'chapter' => [ + '/xiaoshuo/:name-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/novel/:name-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/book/:name-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/xiaoshuo-:name-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/novel-:name-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/book-:name-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + + // 兼容 没有name 的路径模板 + '/xiaoshuo/-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/novel/-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/book/-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/xiaoshuo--:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/novel--:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/book--:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + + // 兼容 没有name 的路径模板 + '/xiaoshuo/:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/novel/:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/book/:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/xiaoshuo-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/novel-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + '/book-:intNovelId/zhang-:intChapterSort/[:intChapterPage]', + ], + + // 最新章节-特殊页面处理 + 'latest' => [ + '/xiaoshuo/:name-:intNovelId/chapter/latest', + '/novel/:name-:intNovelId/chapter/latest', + '/book/:name-:intNovelId/chapter/latest', + '/xiaoshuo-:name-:intNovelId/chapter/latest', + '/novel-:name-:intNovelId/chapter/latest', + '/book-:name-:intNovelId/chapter/latest', + + // 兼容 没有name 的路径模板 + '/xiaoshuo/-:intNovelId/chapter/latest', + '/novel/-:intNovelId/chapter/latest', + '/book/-:intNovelId/chapter/latest', + '/xiaoshuo--:intNovelId/chapter/latest', + '/novel--:intNovelId/chapter/latest', + '/book--:intNovelId/chapter/latest', + + // 兼容 没有name 的路径模板 + '/xiaoshuo/:intNovelId/chapter/latest', + '/novel/:intNovelId/chapter/latest', + '/book/:intNovelId/chapter/latest', + '/xiaoshuo-:intNovelId/chapter/latest', + '/novel-:intNovelId/chapter/latest', + '/book-:intNovelId/chapter/latest', + ], + + // 目录相关路径 + 'catalog' => [ + '/xiaoshuo/:name-:intNovelId/mulu/:strOrder/[:intPage]', + '/novel/:name-:intNovelId/catalog/:strOrder/[:intPage]', + '/book/:name-:intNovelId/table/:strOrder/[:intPage]', + '/xiaoshuo/:name-:intNovelId/mulu/:strOrder/[:intPage]', + '/novel/:name-:intNovelId/catalog/:strOrder/[:intPage]', + '/book/:name-:intNovelId/table/:strOrder/[:intPage]', + + // 兼容 没有name 的路径模板 + '/xiaoshuo/-:intNovelId/mulu/:strOrder/[:intPage]', + '/novel/-:intNovelId/catalog/:strOrder/[:intPage]', + '/book/-:intNovelId/table/:strOrder/[:intPage]', + '/xiaoshuo/-:intNovelId/mulu/:strOrder/[:intPage]', + '/novel/-:intNovelId/catalog/:strOrder/[:intPage]', + '/book/-:intNovelId/table/:strOrder/[:intPage]', + + // 兼容 没有name 的路径模板 + '/xiaoshuo/:intNovelId/mulu/:strOrder/[:intPage]', + '/novel/:intNovelId/catalog/:strOrder/[:intPage]', + '/book/:intNovelId/table/:strOrder/[:intPage]', + '/xiaoshuo/:intNovelId/mulu/:strOrder/[:intPage]', + '/novel/:intNovelId/catalog/:strOrder/[:intPage]', + '/book/:intNovelId/table/:strOrder/[:intPage]', + ], + + // 伪小说详情相关路径 + 'kan-novel' => [ + '/kan-xiaoshuo/:name-:intNovelId-:intForgeId', + '/kan-book/:name-:intNovelId-:intForgeId', + '/kan-novel/:name-:intNovelId-:intForgeId', + '/show-novel/:name-:intNovelId-:intForgeId', + '/seo-book/:name-:intNovelId-:intForgeId', + '/show-xiaoshuo-:name-:intNovelId-:intForgeId', + '/kan-xiaoshuo-:name-:intNovelId-:intForgeId', + '/kan-novel-:name-:intNovelId-:intForgeId', + '/kan-book-:name-:intNovelId-:intForgeId', + '/see-novel-:name-:intNovelId-:intForgeId', + '/good-book-:name-:intNovelId-:intForgeId', + + // 兼容 没有name 的路径模板 + '/kan-xiaoshuo/-:intNovelId-:intForgeId', + '/kan-book/-:intNovelId-:intForgeId', + '/kan-novel/-:intNovelId-:intForgeId', + '/show-novel/-:intNovelId-:intForgeId', + '/seo-book/-:intNovelId-:intForgeId', + '/show-xiaoshuo--:intNovelId-:intForgeId', + '/kan-xiaoshuo--:intNovelId-:intForgeId', + '/kan-novel--:intNovelId-:intForgeId', + '/kan-book--:intNovelId-:intForgeId', + '/see-novel--:intNovelId-:intForgeId', + '/good-book--:intNovelId-:intForgeId', + + // 兼容 没有name 的路径模板 + '/kan-xiaoshuo/:intNovelId-:intForgeId', + '/kan-book/:intNovelId-:intForgeId', + '/kan-novel/:intNovelId-:intForgeId', + '/show-novel/:intNovelId-:intForgeId', + '/seo-book/:intNovelId-:intForgeId', + '/show-xiaoshuo-:intNovelId-:intForgeId', + '/kan-xiaoshuo-:intNovelId-:intForgeId', + '/kan-novel-:intNovelId-:intForgeId', + '/kan-book-:intNovelId-:intForgeId', + '/see-novel-:intNovelId-:intForgeId', + '/good-book-:intNovelId-:intForgeId', + ], + + // 小说详情相关路径 + 'novel' => [ + '/xiaoshuo/:name-:intNovelId', + '/novel/:name-:intNovelId', + '/book/:name-:intNovelId', + '/xiaoshuo-:name-:intNovelId', + '/novel-:name-:intNovelId', + '/book-:name-:intNovelId', + + // 兼容 没有name 的路径模板 + '/xiaoshuo/-:intNovelId', + '/novel/-:intNovelId', + '/book/-:intNovelId', + '/xiaoshuo--:intNovelId', + '/novel--:intNovelId', + '/book--:intNovelId', + + // 兼容 没有name 的路径模板 + '/xiaoshuo/:intNovelId', + '/novel/:intNovelId', + '/book/:intNovelId', + '/xiaoshuo-:intNovelId', + '/novel-:intNovelId', + '/book-:intNovelId', + ], + + // 书库 + 'library' => [ + /** + * /shuku/nansheng-xiaoshuo/all/all/all/page1 + * 完整路由 + * 使用: {site:nflurl gender="$strGender" category="$CategoryPinyin" status="all" order="all" page="1"/} + */ + '/shuku/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', + '/books/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', + '/library/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', + '/fenlei/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', + '/class/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', + '/category/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', + + /** + * default 模板 + * strGender 不需要 + * 使用 :gender 传空/或者不传,控制器会默认值是 all + * {site:nflurl gender="" category="$CategoryPinyin" status="all" order="all" page="1"/} + */ + '/shuku/:strCategory/:strStatus/:strOrder/page:intPage', + '/books/:strCategory/:strStatus/:strOrder/page:intPage', + '/library/:strCategory/:strStatus/:strOrder/page:intPage', + '/fenlei/:strCategory/:strStatus/:strOrder/page:intPage', + '/class/:strCategory/:strStatus/:strOrder/page:intPage', + '/category/:strCategory/:strStatus/:strOrder/page:intPage', + + /** + * kuangyu 模板 + * strOrder 不需要 + * 使用 order 传空/或者不传,控制器会默认值是 all + * {site:nflurl gender="$strGender" category="$CategoryPinyin" status="all" page="1"/} + */ + '/shuku/:strGender/:strCategory/:strStatus/page:intPage', + '/books/:strGender/:strCategory/:strStatus/page:intPage', + '/library/:strGender/:strCategory/:strStatus/page:intPage', + '/fenlei/:strGender/:strCategory/:strStatus/page:intPage', + '/class/:strGender/:strCategory/:strStatus/page:intPage', + '/category/:strGender/:strCategory/:strStatus/page:intPage', + + ], + // 书库首页 + 'library-index' => [ + '/shuku/index', + '/books/index', + '/library/index', + '/fenlei/index', + '/class/index', + '/category/index', + ], + // 排行榜列表url + 'rank-info' => [ + '/paihang/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', + '/rank/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', + '/top/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', + '/ranks/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', + '/phb/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', + '/paihangbang/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', + + '/paihang/:strCategory/:strStatus/:strOrder/page:intPage', + '/rank/:strCategory/:strStatus/:strOrder/page:intPage', + '/top/:strCategory/:strStatus/:strOrder/page:intPage', + '/ranks/:strCategory/:strStatus/:strOrder/page:intPage', + '/phb/:strCategory/:strStatus/:strOrder/page:intPage', + '/paihangbang/:strCategory/:strStatus/:strOrder/page:intPage', + + ], + //排行榜 + 'rank' => ['/paihang/all', '/rank/all', '/paihang-quan', '/top/all', '/paihang-bang', '/ranks/all'], + // 男生 + 'boys' => ['/nansheng-xiaoshuo', '/boys-novel', '/nansheng-shu', '/man-read', '/nansheng-book', '/male-novels'], + //女生 + 'girls' => ['/nvsheng-xiaoshuo', '/girls-novel', '/nvsheng-shu', '/woman-read', '/nvsheng-book', '/female-novels'], + //搜索 + 'search' => ['/search', '/explore', '/sousuo', '/find', '/query', '/keywords'], + // 用户中心 + 'user' => ['/user', '/my-account', '/profile', '/yonghu', '/wode-zhongxin', '/personal-center'], + //书架 + 'bookshelf' => ['/bookshelf', '/book-shelf', '/reading-shelf', '/shujia', '/yuedu-shujia', '/book-collection'], + //历史记录 + 'history' => ['/history', '/reading-history', '/read-record', '/lishi', '/yuedu-lishi', '/browse-past'], + // 首页 + 'index' => ['/'], + // 首页 - 如果info_id 》 0 + 'nindex' => ['/nindex'], - // 兼容 没有name 的路径模板 - '/xiaoshuo/:intNovelId', - '/novel/:intNovelId', - '/book/:intNovelId', - '/xiaoshuo-:intNovelId', - '/novel-:intNovelId', - '/book-:intNovelId', ], - - // 书库 - 'library' => [ + + 'video' => [ + // 视频站点url -- start + /** - * /shuku/nansheng-xiaoshuo/all/all/all/page1 - * 完整路由 - * 使用: {site:nflurl gender="$strGender" category="$CategoryPinyin" status="all" order="all" page="1"/} + * 视频分类 分页 + * /目前使用的3种模板: + * /vodtype-dian-ying/dong-zuo-pian-1992-zhong-guo-da-lu-news/guo-yu-page1 + * /vodclass-dong-zuo-pian/dian-ying-1992-zhong-guo-da-lu-news/guo-yu-page1 + * /vodcategory-dian-ying-1992-dong-zuo-pian/zhong-guo-da-lu-news/guo-yu-page1 + * + * /vodshow/电影/动作片/中国大陆/国语/1992/最新/第一页 */ - '/shuku/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', - '/books/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', - '/library/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', - '/fenlei/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', - '/class/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', - '/category/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', - + 'video_category' => [ + + // 现在使用的9套 + '/vodtype-:strParentCategory/:strCategory-:strYear-:strArea-:strOrder/:strLang-page:intPage', + '/vodclass-:strCategory/:strParentCategory-:strYear-:strArea-:strOrder/:strLang-page:intPage', + '/vodcategory-:strParentCategory-:strYear-:strCategory/:strArea-:strOrder/:strLang-page:intPage', + + '/videotype-:strParentCategory/:strCategory-:strYear-:strArea-:strOrder/:strLang-page:intPage', + '/videoclass-:strCategory/:strParentCategory-:strYear-:strArea-:strOrder/:strLang-page:intPage', + '/videocategory-:strParentCategory-:strYear-:strCategory/:strArea-:strOrder/:strLang-page:intPage', + + '/shipinfenlei-:strParentCategory/:strCategory-:strYear-:strArea-:strOrder/:strLang-page:intPage', + '/shipinleixing-:strCategory/:strParentCategory-:strYear-:strArea-:strOrder/:strLang-page:intPage', + '/shipinliebiao-:strParentCategory-:strYear-:strCategory/:strArea-:strOrder/:strLang-page:intPage', + + ], + + // 视频分类 首页 + 'video_category_index' => [ + '/vodtype/:strParentCategory', + '/vodclass/:strParentCategory', + '/vodcategory/:strParentCategory', + + '/video-type/:strParentCategory', + '/video-class/:strParentCategory', + '/video-category/:strParentCategory', + + '/shipinfenlei/:strParentCategory', + '/shipinleixing/:strParentCategory', + '/shipinliebiao/:strParentCategory', + + ], + /** - * default 模板 - * strGender 不需要 - * 使用 :gender 传空/或者不传,控制器会默认值是 all - * {site:nflurl gender="" category="$CategoryPinyin" status="all" order="all" page="1"/} + * 视频详情-伪造 + * /voddetail/strVideoPinyin-1-1 */ - '/shuku/:strCategory/:strStatus/:strOrder/page:intPage', - '/books/:strCategory/:strStatus/:strOrder/page:intPage', - '/library/:strCategory/:strStatus/:strOrder/page:intPage', - '/fenlei/:strCategory/:strStatus/:strOrder/page:intPage', - '/class/:strCategory/:strStatus/:strOrder/page:intPage', - '/category/:strCategory/:strStatus/:strOrder/page:intPage', - + 'video_info_forge' => [ + '/ /:strPinyin-:intVId-:intVForgeId', + '/vodinfo/:strPinyin-:intVId-:intVForgeId', + '/vod/:strPinyin-:intVId-:intVForgeId', + + '/video-info/:strPinyin-:intVId-:intVForgeId', + '/video-detail/:strPinyin-:intVId-:intVForgeId', + '/video/:strPinyin-:intVId-:intVForgeId', + + '/shipin/:strPinyin-:intVId-:intVForgeId', + '/shipin-xiangqing/:strPinyin-:intVId-:intVForgeId', + '/shipin-neiron/:strPinyin-:intVId-:intVForgeId', + + // 兼容seo使用 没有 strPinyin 的路径模板 + '/voddetail/-:intVId-:intVForgeId', + '/vodinfo/-:intVId-:intVForgeId', + '/vod/-:intVId-:intVForgeId', + + '/video-info/-:intVId-:intVForgeId', + '/video-detail/-:intVId-:intVForgeId', + '/video/-:intVId-:intVForgeId', + + '/shipin/-:intVId-:intVForgeId', + '/shipin-xiangqing/-:intVId-:intVForgeId', + '/shipin-neiron/-:intVId-:intVForgeId', + + // 兼容seo使用 没有 strPinyin 的路径模板 + '/voddetail/:intVId-:intVForgeId', + '/vodinfo/:intVId-:intVForgeId', + '/vod/:intVId-:intVForgeId', + + '/video-info/:intVId-:intVForgeId', + '/video-detail/:intVId-:intVForgeId', + '/video/:intVId-:intVForgeId', + + '/shipin/:intVId-:intVForgeId', + '/shipin-xiangqing/:intVId-:intVForgeId', + '/shipin-neiron/:intVId-:intVForgeId', + + + ], + /** - * kuangyu 模板 - * strOrder 不需要 - * 使用 order 传空/或者不传,控制器会默认值是 all - * {site:nflurl gender="$strGender" category="$CategoryPinyin" status="all" page="1"/} + * 视频详情 + * /voddetail/strVideoPinyin-1 */ - '/shuku/:strGender/:strCategory/:strStatus/page:intPage', - '/books/:strGender/:strCategory/:strStatus/page:intPage', - '/library/:strGender/:strCategory/:strStatus/page:intPage', - '/fenlei/:strGender/:strCategory/:strStatus/page:intPage', - '/class/:strGender/:strCategory/:strStatus/page:intPage', - '/category/:strGender/:strCategory/:strStatus/page:intPage', - + 'video_info' => [ + '/voddetail/:strPinyin-:intVId', + '/vodinfo/:strPinyin-:intVId', + '/vod/:strPinyin-:intVId', + + '/video-info/:strPinyin-:intVId', + '/video-detail/:strPinyin-:intVId', + '/video/:strPinyin-:intVId', + + '/shipin/:strPinyin-:intVId', + '/shipin-xiangqing/:strPinyin-:intVId', + '/shipin-neiron/:strPinyin-:intVId', + + // 兼容seo使用 没有 strPinyin 的路径模板 + '/voddetail/-:intVId', + '/vodinfo/-:intVId', + '/vod/-:intVId', + + '/video-info/-:intVId', + '/video-detail/-:intVId', + '/video/-:intVId', + + '/shipin/-:intVId', + '/shipin-xiangqing/-:intVId', + '/shipin-neiron/-:intVId', + + // 兼容seo使用 没有 strPinyin 的路径模板 + '/voddetail/:intVId', + '/vodinfo/:intVId', + '/vod/:intVId', + + '/video-info/:intVId', + '/video-detail/:intVId', + '/video/:intVId', + + '/shipin/:intVId', + '/shipin-xiangqing/:intVId', + '/shipin-neiron/:intVId', + + + ], + + // 视频播放 /vodplay/strPinyin-10-wuxian-1 + 'video_play' => [ + '/vodplay/:strPinyin-:intVId-:strPlayType-:intPlayIndex', + '/vodbf/:strPinyin-:intVId-:strPlayType-:intPlayIndex', + '/vodseed/:strPinyin-:intVId-:strPlayType-:intPlayIndex', + + '/video-play/:strPinyin-:intVId-:strPlayType-:intPlayIndex', + '/video-bofang/:strPinyin-:intVId-:strPlayType-:intPlayIndex', + '/video-show/:strPinyin-:intVId-:strPlayType-:intPlayIndex', + + '/shipin-play/:strPinyin-:intVId-:strPlayType-:intPlayIndex', + '/shipin-bofang/:strPinyin-:intVId-:strPlayType-:intPlayIndex', + '/shipin-kan/:strPinyin-:intVId-:strPlayType-:intPlayIndex', + + // 兼容seo使用 没有 strPinyin 的路径模板 + '/vodplay/-:intVId-:strPlayType-:intPlayIndex', + '/vodbf/-:intVId-:strPlayType-:intPlayIndex', + '/vodseed/-:intVId-:strPlayType-:intPlayIndex', + + '/video-play/-:intVId-:strPlayType-:intPlayIndex', + '/video-bofang/-:intVId-:strPlayType-:intPlayIndex', + '/video-show/-:intVId-:strPlayType-:intPlayIndex', + + '/shipin-play/-:intVId-:strPlayType-:intPlayIndex', + '/shipin-bofang/-:intVId-:strPlayType-:intPlayIndex', + '/shipin-kan/-:intVId-:strPlayType-:intPlayIndex', + + // 兼容seo使用 没有 strPinyin 的路径模板 + '/vodplay/:intVId-:strPlayType-:intPlayIndex', + '/vodbf/:intVId-:strPlayType-:intPlayIndex', + '/vodseed/:intVId-:strPlayType-:intPlayIndex', + + '/video-play/:intVId-:strPlayType-:intPlayIndex', + '/video-bofang/:intVId-:strPlayType-:intPlayIndex', + '/video-show/:intVId-:strPlayType-:intPlayIndex', + + '/shipin-play/:intVId-:strPlayType-:intPlayIndex', + '/shipin-bofang/:intVId-:strPlayType-:intPlayIndex', + '/shipin-kan/:intVId-:strPlayType-:intPlayIndex', + ], + + //排行榜 + 'video_rank_index' => ['/paihang/index', '/rank/index', '/paihang-quan', '/top/index', '/paihang-bang', '/ranks/index', '/sort/index', '/order/index', '/phb/index'], + + //排行榜 + 'video_rank_list' => [ + '/paihang/:strParentCategory/:strCategory/:strSortType', + '/rank:strCategory/:strParentCategory-:strSortType', + '/:strParentCategory/:strCategory/:strSortType-paihang', + + '/:strCategory/:strParentCategory-:strSortType-top', + '/:strParentCategory-:strSortType/paihangbang-:strCategory', + '/:strParentCategory-:strSortType/:strCategory-ranks', + + '/sort:strCategory/:strParentCategory-:strSortType', + '/:strParentCategory/:strCategory/:strSortType-order', + '/:strParentCategory-:strSortType/:strCategory-phb', + + ], + + //搜索 + 'video_search' => ['/search', '/explore', '/sousuo', '/find', '/query', '/keywords', '/get', '/key', '/s'], + + // 历史记录 + 'video_history' => ['/history', '/reading-history', '/seed-record', '/lishi', '/kan-lishi', '/browse-past', '/jilu', '/zuji', '/seed'], + + // 首页 + 'index' => ['/'], + // 首页 - 如果info_id 》 0 + 'vindex' => ['/vindex'], + + // 视频站点url -- end ], - // 书库首页 - 'library-index' => [ - '/shuku/index', - '/books/index', - '/library/index', - '/fenlei/index', - '/class/index', - '/category/index', - ], - // 排行榜列表url - 'rank-info' => [ - '/paihang/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', - '/rank/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', - '/top/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', - '/ranks/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', - '/phb/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', - '/paihangbang/:strGender/:strCategory/:strStatus/:strOrder/page:intPage', - - '/paihang/:strCategory/:strStatus/:strOrder/page:intPage', - '/rank/:strCategory/:strStatus/:strOrder/page:intPage', - '/top/:strCategory/:strStatus/:strOrder/page:intPage', - '/ranks/:strCategory/:strStatus/:strOrder/page:intPage', - '/phb/:strCategory/:strStatus/:strOrder/page:intPage', - '/paihangbang/:strCategory/:strStatus/:strOrder/page:intPage', - - ], - //排行榜 - 'rank' => ['/paihang/all', '/rank/all', '/paihang-quan', '/top/all', '/paihang-bang', '/ranks/all'], - // 男生 - 'boys' => ['/nansheng-xiaoshuo', '/boys-novel', '/nansheng-shu', '/man-read', '/nansheng-book', '/male-novels'], - //女生 - 'girls' => ['/nvsheng-xiaoshuo', '/girls-novel', '/nvsheng-shu', '/woman-read', '/nvsheng-book', '/female-novels'], - //搜索 - 'search' => ['/search', '/explore', '/sousuo', '/find', '/query', '/keywords'], - // 用户中心 - 'user' => ['/user', '/my-account', '/profile', '/yonghu', '/wode-zhongxin', '/personal-center'], - //书架 - 'bookshelf' => ['/bookshelf', '/book-shelf', '/reading-shelf', '/shujia', '/yuedu-shujia', '/book-collection'], - //历史记录 - 'history' => ['/history', '/reading-history', '/read-record', '/lishi', '/yuedu-lishi', '/browse-past'], - // 首页 - 'index' => ['/'], - // 首页 - 如果info_id 》 0 - 'nindex' => ['/nindex'], - ], - 'video' => [ - // 视频站点url -- start + // 通用路径别名 + 'paths' => [], - /** - * 视频分类 分页 - * /目前使用的3种模板: - * /vodtype-dian-ying/dong-zuo-pian-1992-zhong-guo-da-lu-news/guo-yu-page1 - * /vodclass-dong-zuo-pian/dian-ying-1992-zhong-guo-da-lu-news/guo-yu-page1 - * /vodcategory-dian-ying-1992-dong-zuo-pian/zhong-guo-da-lu-news/guo-yu-page1 - * - * /vodshow/电影/动作片/中国大陆/国语/1992/最新/第一页 - */ - 'video_category' => [ + ]; - // 现在使用的9套 - '/vodtype-:strParentCategory/:strCategory-:strYear-:strArea-:strOrder/:strLang-page:intPage', - '/vodclass-:strCategory/:strParentCategory-:strYear-:strArea-:strOrder/:strLang-page:intPage', - '/vodcategory-:strParentCategory-:strYear-:strCategory/:strArea-:strOrder/:strLang-page:intPage', + /** + * 遍历路由配置,动态注册 H5 和 PC 路由 + */ + foreach ($arrRoutes['prefixes'] as $platform => $prefixes) { - '/videotype-:strParentCategory/:strCategory-:strYear-:strArea-:strOrder/:strLang-page:intPage', - '/videoclass-:strCategory/:strParentCategory-:strYear-:strArea-:strOrder/:strLang-page:intPage', - '/videocategory-:strParentCategory-:strYear-:strCategory/:strArea-:strOrder/:strLang-page:intPage', + $prefixes = is_array($prefixes) ? $prefixes : [$prefixes]; + $boolIsMobile = $platform === 'h5'; + foreach ($prefixes as $strPath) { - '/shipinfenlei-:strParentCategory/:strCategory-:strYear-:strArea-:strOrder/:strLang-page:intPage', - '/shipinleixing-:strCategory/:strParentCategory-:strYear-:strArea-:strOrder/:strLang-page:intPage', - '/shipinliebiao-:strParentCategory-:strYear-:strCategory/:strArea-:strOrder/:strLang-page:intPage', - - ], + $arrPathUrl = $arrRoutes['app_type']['novel']; + if (config('app.default_app_type') == 'video') { + $arrPathUrl = $arrRoutes['app_type']['video']; + } - // 视频分类 首页 - 'video_category_index' => [ - '/vodtype/:strParentCategory', - '/vodclass/:strParentCategory', - '/vodcategory/:strParentCategory', + foreach ($arrPathUrl as $key => $arrUrl) { - '/video-type/:strParentCategory', - '/video-class/:strParentCategory', - '/video-category/:strParentCategory', + foreach ($arrUrl as $strUrl) { + $strRoute = $strPath . $strUrl; + switch ($key) { - '/shipinfenlei/:strParentCategory', - '/shipinleixing/:strParentCategory', - '/shipinliebiao/:strParentCategory', + // 视频站点url -- start + case 'video_category_index': + $strView = 'pc/getCategoryType.html'; + if ($platform == 'h5') { + $strView = 'video/getCategoryType.html'; + } + //视频分类 首页 + Route::get($strRoute, function () use ($strView) { + return view($strView); + }) + ->pattern([ + 'strParentCategory' => '[a-z\-]*', // 允许空值 + ]); + break; - ], + case 'video_rank_index': + $strView = 'pc/getRankIndex.html'; + if ($platform == 'h5') { + $strView = 'video/getRankIndex.html'; + } + //视频排行榜 首页 + Route::get($strRoute, function () use ($strView) { + return view($strView); + }) + ->pattern([]); + break; - /** - * 视频详情-伪造 - * /voddetail/strVideoPinyin-1-1 - */ - 'video_info_forge' => [ - '/voddetail/:strPinyin-:intVId-:intVForgeId', - '/vodinfo/:strPinyin-:intVId-:intVForgeId', - '/vod/:strPinyin-:intVId-:intVForgeId', + case 'video_rank_list': + $strView = 'pc/getRankList.html'; + if ($platform == 'h5') { + $strView = 'video/getRankList.html'; + } + //视频分类 分页 + Route::get($strRoute, function () use ($strView) { + return view($strView); + }) + ->pattern([ + 'strParentCategory' => '[a-z\-]*', // 允许空值 + 'strCategory' => '[a-z\-]*', // 允许空值 + 'strSortType' => '[a-z\-]*', // 允许空值 + 'intPage' => '\d*', // 允许空值 + ]); + break; - '/video-info/:strPinyin-:intVId-:intVForgeId', - '/video-detail/:strPinyin-:intVId-:intVForgeId', - '/video/:strPinyin-:intVId-:intVForgeId', + case 'video_category': + $strView = 'pc/getCategory.html'; + if ($platform == 'h5') { + $strView = 'video/getCategory.html'; + } + //视频分类 分页 + Route::get($strRoute, function () use ($strView) { + return view($strView); + }) + ->pattern([ + 'strParentCategory' => '[a-z\-]*', // 允许空值 + 'strCategory' => '[a-z\-]*', // 允许空值 + 'strArea' => '[a-z\-]*', // 允许空值 + 'strLang' => '[a-z\-]*', // 允许空值 + 'strYear' => '([0-9]+|all)?', // 允许空值 + 'strOrder' => '[a-zA-Z]*', // 允许空值 + 'intPage' => '\d*', // 允许空值 + ]); + break; - '/shipin/:strPinyin-:intVId-:intVForgeId', - '/shipin-xiangqing/:strPinyin-:intVId-:intVForgeId', - '/shipin-neiron/:strPinyin-:intVId-:intVForgeId', + case 'video_info_forge': + //详情 + $strView = 'pc/getVideoInfo.html'; + if ($platform == 'h5') { + $strView = 'video/getVideoInfo.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + }) + ->pattern(['intVId' => '\d+', 'strPinyin' => '[\w-]+', 'intVForgeId' => '\d+']); - // 兼容seo使用 没有 strPinyin 的路径模板 - '/voddetail/-:intVId-:intVForgeId', - '/vodinfo/-:intVId-:intVForgeId', - '/vod/-:intVId-:intVForgeId', + break; + case 'video_info': + //详情 + $strView = 'pc/getVideoInfo.html'; + if ($platform == 'h5') { + $strView = 'video/getVideoInfo.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + }) + ->pattern(['intVId' => '\d+', 'strPinyin' => '[\w-]+',]); - '/video-info/-:intVId-:intVForgeId', - '/video-detail/-:intVId-:intVForgeId', - '/video/-:intVId-:intVForgeId', + break; + case 'video_play': + //播放 + $strView = 'pc/getVideoPlayUrl.html'; + if ($platform == 'h5') { + $strView = 'video/getVideoPlayUrl.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + }) + ->pattern(['intVId' => '\d+', 'intPlayIndex' => '\d+', 'strPlayType' => '[\w-]+', 'strPinyin' => '[\w-]+',]); - '/shipin/-:intVId-:intVForgeId', - '/shipin-xiangqing/-:intVId-:intVForgeId', - '/shipin-neiron/-:intVId-:intVForgeId', + break; - // 兼容seo使用 没有 strPinyin 的路径模板 - '/voddetail/:intVId-:intVForgeId', - '/vodinfo/:intVId-:intVForgeId', - '/vod/:intVId-:intVForgeId', - - '/video-info/:intVId-:intVForgeId', - '/video-detail/:intVId-:intVForgeId', - '/video/:intVId-:intVForgeId', - - '/shipin/:intVId-:intVForgeId', - '/shipin-xiangqing/:intVId-:intVForgeId', - '/shipin-neiron/:intVId-:intVForgeId', - - - ], - - /** - * 视频详情 - * /voddetail/strVideoPinyin-1 - */ - 'video_info' => [ - '/voddetail/:strPinyin-:intVId', - '/vodinfo/:strPinyin-:intVId', - '/vod/:strPinyin-:intVId', - - '/video-info/:strPinyin-:intVId', - '/video-detail/:strPinyin-:intVId', - '/video/:strPinyin-:intVId', - - '/shipin/:strPinyin-:intVId', - '/shipin-xiangqing/:strPinyin-:intVId', - '/shipin-neiron/:strPinyin-:intVId', - - // 兼容seo使用 没有 strPinyin 的路径模板 - '/voddetail/-:intVId', - '/vodinfo/-:intVId', - '/vod/-:intVId', - - '/video-info/-:intVId', - '/video-detail/-:intVId', - '/video/-:intVId', - - '/shipin/-:intVId', - '/shipin-xiangqing/-:intVId', - '/shipin-neiron/-:intVId', - - // 兼容seo使用 没有 strPinyin 的路径模板 - '/voddetail/:intVId', - '/vodinfo/:intVId', - '/vod/:intVId', - - '/video-info/:intVId', - '/video-detail/:intVId', - '/video/:intVId', - - '/shipin/:intVId', - '/shipin-xiangqing/:intVId', - '/shipin-neiron/:intVId', - - - ], - - // 视频播放 /vodplay/strPinyin-10-wuxian-1 - 'video_play' => [ - '/vodplay/:strPinyin-:intVId-:strPlayType-:intPlayIndex', - '/vodbf/:strPinyin-:intVId-:strPlayType-:intPlayIndex', - '/vodseed/:strPinyin-:intVId-:strPlayType-:intPlayIndex', - - '/video-play/:strPinyin-:intVId-:strPlayType-:intPlayIndex', - '/video-bofang/:strPinyin-:intVId-:strPlayType-:intPlayIndex', - '/video-show/:strPinyin-:intVId-:strPlayType-:intPlayIndex', - - '/shipin-play/:strPinyin-:intVId-:strPlayType-:intPlayIndex', - '/shipin-bofang/:strPinyin-:intVId-:strPlayType-:intPlayIndex', - '/shipin-kan/:strPinyin-:intVId-:strPlayType-:intPlayIndex', - - // 兼容seo使用 没有 strPinyin 的路径模板 - '/vodplay/-:intVId-:strPlayType-:intPlayIndex', - '/vodbf/-:intVId-:strPlayType-:intPlayIndex', - '/vodseed/-:intVId-:strPlayType-:intPlayIndex', - - '/video-play/-:intVId-:strPlayType-:intPlayIndex', - '/video-bofang/-:intVId-:strPlayType-:intPlayIndex', - '/video-show/-:intVId-:strPlayType-:intPlayIndex', - - '/shipin-play/-:intVId-:strPlayType-:intPlayIndex', - '/shipin-bofang/-:intVId-:strPlayType-:intPlayIndex', - '/shipin-kan/-:intVId-:strPlayType-:intPlayIndex', - - // 兼容seo使用 没有 strPinyin 的路径模板 - '/vodplay/:intVId-:strPlayType-:intPlayIndex', - '/vodbf/:intVId-:strPlayType-:intPlayIndex', - '/vodseed/:intVId-:strPlayType-:intPlayIndex', - - '/video-play/:intVId-:strPlayType-:intPlayIndex', - '/video-bofang/:intVId-:strPlayType-:intPlayIndex', - '/video-show/:intVId-:strPlayType-:intPlayIndex', - - '/shipin-play/:intVId-:strPlayType-:intPlayIndex', - '/shipin-bofang/:intVId-:strPlayType-:intPlayIndex', - '/shipin-kan/:intVId-:strPlayType-:intPlayIndex', - ], - - //排行榜 - 'video_rank_index' => ['/paihang/index', '/rank/index', '/paihang-quan', '/top/index', '/paihang-bang', '/ranks/index', '/sort/index', '/order/index', '/phb/index'], - - //排行榜 - 'video_rank_list' => [ - '/paihang/:strParentCategory/:strCategory/:strSortType', - '/rank:strCategory/:strParentCategory-:strSortType', - '/:strParentCategory/:strCategory/:strSortType-paihang', - - '/:strCategory/:strParentCategory-:strSortType-top', - '/:strParentCategory-:strSortType/paihangbang-:strCategory', - '/:strParentCategory-:strSortType/:strCategory-ranks', - - '/sort:strCategory/:strParentCategory-:strSortType', - '/:strParentCategory/:strCategory/:strSortType-order', - '/:strParentCategory-:strSortType/:strCategory-phb', - - ], - - //搜索 - 'video_search' => ['/search', '/explore', '/sousuo', '/find', '/query', '/keywords', '/get', '/key', '/s'], - - // 历史记录 - 'video_history' => ['/history', '/reading-history', '/seed-record', '/lishi', '/kan-lishi', '/browse-past', '/jilu', '/zuji', '/seed'], - - // 首页 - 'index' => ['/'], - // 首页 - 如果info_id 》 0 - 'vindex' => ['/vindex'], - - // 视频站点url -- end - ], - ], - - // 通用路径别名 - 'paths' => [], - -]; - -/** - * 遍历路由配置,动态注册 H5 和 PC 路由 - */ -foreach ($arrRoutes['prefixes'] as $platform => $prefixes) { - - $prefixes = is_array($prefixes) ? $prefixes : [$prefixes]; - $boolIsMobile = $platform === 'h5'; - foreach ($prefixes as $strPath) { - - $arrPathUrl = $arrRoutes['app_type']['novel']; - if(config('app.default_app_type') == 'video'){ - $arrPathUrl = $arrRoutes['app_type']['video']; - } - - foreach ($arrPathUrl as $key => $arrUrl) { - - foreach ($arrUrl as $strUrl) { - $strRoute = $strPath . $strUrl; - switch ($key) { - - // 视频站点url -- start - case 'video_category_index': - $strView = 'pc/getCategoryType.html'; - if ($platform == 'h5') { - $strView = 'video/getCategoryType.html'; - } - //视频分类 首页 - Route::get($strRoute, function () use ($strView) { - return view($strView); - }) - ->pattern([ - 'strParentCategory' => '[a-z\-]*', // 允许空值 - ]); - break; - - case 'video_rank_index': - $strView = 'pc/getRankIndex.html'; - if ($platform == 'h5') { - $strView = 'video/getRankIndex.html'; - } - //视频排行榜 首页 - Route::get($strRoute, function () use ($strView) { - return view($strView); - }) - ->pattern([ - - ]); - break; - - case 'video_rank_list': - $strView = 'pc/getRankList.html'; - if ($platform == 'h5') { - $strView = 'video/getRankList.html'; - } - //视频分类 分页 - Route::get($strRoute, function () use ($strView) { - return view($strView); - }) - ->pattern([ - 'strParentCategory' => '[a-z\-]*', // 允许空值 - 'strCategory' => '[a-z\-]*', // 允许空值 - 'strSortType' => '[a-z\-]*', // 允许空值 - 'intPage' => '\d*', // 允许空值 - ]); - break; - - case 'video_category': - $strView = 'pc/getCategory.html'; - if ($platform == 'h5') { - $strView = 'video/getCategory.html'; - } - //视频分类 分页 - Route::get($strRoute, function () use ($strView) { - return view($strView); - }) - ->pattern([ - 'strParentCategory' => '[a-z\-]*', // 允许空值 - 'strCategory' => '[a-z\-]*', // 允许空值 - 'strArea' => '[a-z\-]*', // 允许空值 - 'strLang' => '[a-z\-]*', // 允许空值 - 'strYear' => '([0-9]+|all)?', // 允许空值 - 'strOrder' => '[a-zA-Z]*', // 允许空值 - 'intPage' => '\d*', // 允许空值 - ]); - break; - - case 'video_info_forge': - //详情 - $strView = 'pc/getVideoInfo.html'; - if ($platform == 'h5') { - $strView = 'video/getVideoInfo.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }) - ->pattern(['intVId' => '\d+', 'strPinyin' => '[\w-]+','intVForgeId' => '\d+']); - - break; - case 'video_info': - //详情 - $strView = 'pc/getVideoInfo.html'; - if ($platform == 'h5') { - $strView = 'video/getVideoInfo.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }) - ->pattern(['intVId' => '\d+', 'strPinyin' => '[\w-]+',]); - - break; - case 'video_play': - //播放 - $strView = 'pc/getVideoPlayUrl.html'; - if ($platform == 'h5') { - $strView = 'video/getVideoPlayUrl.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }) - ->pattern(['intVId' => '\d+', 'intPlayIndex' => '\d+', 'strPlayType' => '[\w-]+','strPinyin' => '[\w-]+',]); - - break; - - case 'video_search': - //搜素 - $strView = 'pc/getSearchVideo.html'; - if ($platform == 'h5') { - $strView = 'video/getSearchVideo.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - })->ext('html'); - break; + case 'video_search': + //搜素 + $strView = 'pc/getSearchVideo.html'; + if ($platform == 'h5') { + $strView = 'video/getSearchVideo.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + })->ext('html'); + break; // 首页 // if ($platform == 'h5') { @@ -725,257 +903,257 @@ foreach ($arrRoutes['prefixes'] as $platform => $prefixes) { // } // break; - case 'video_history': - //历史记录 - $strView = 'pc/user/getHistory.html'; - if ($platform == 'h5') { - $strView = 'user/getHistory.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }); - break; + case 'video_history': + //历史记录 + $strView = 'pc/user/getHistory.html'; + if ($platform == 'h5') { + $strView = 'user/getHistory.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + }); + break; - // 视频站点url -- end + // 视频站点url -- end - case 'latest': - //最新章节 - $strView = 'pc/getNovelChapter.html'; - if ($platform == 'h5') { - $strView = 'novel/getNovelChapter.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }) - ->pattern(['intNovelId' => '\d+', 'name' => '[\w-]+',]); - break; + case 'latest': + //最新章节 + $strView = 'pc/getNovelChapter.html'; + if ($platform == 'h5') { + $strView = 'novel/getNovelChapter.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + }) + ->pattern(['intNovelId' => '\d+', 'name' => '[\w-]+',]); + break; - case 'chapter': - //章节 - $strView = 'pc/getNovelChapter.html'; - if ($platform == 'h5') { - $strView = 'novel/getNovelChapter.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }) - ->pattern(['intNovelId' => '\d+', 'name' => '[\w-]+', 'intChapterSort' => '\d+', 'intChapterPage' => '\d+']); - break; + case 'chapter': + //章节 + $strView = 'pc/getNovelChapter.html'; + if ($platform == 'h5') { + $strView = 'novel/getNovelChapter.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + }) + ->pattern(['intNovelId' => '\d+', 'name' => '[\w-]+', 'intChapterSort' => '\d+', 'intChapterPage' => '\d+']); + break; - case 'kan-novel': - //伪造小说详情 - // $strView = 'pc/getKanNovelInfo.html'; - // if ($platform == 'h5') { - // $strView = 'novel/getKanNovelInfo.html'; - // } - $strView = 'pc/getNovelInfo.html'; - if ($platform == 'h5') { - $strView = 'novel/getNovelInfo.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }) - ->pattern(['intNovelId' => '\d+', 'intForgeId' => '\d+', 'name' => '[\w-]+',]); - break; + case 'kan-novel': + //伪造小说详情 + // $strView = 'pc/getKanNovelInfo.html'; + // if ($platform == 'h5') { + // $strView = 'novel/getKanNovelInfo.html'; + // } + $strView = 'pc/getNovelInfo.html'; + if ($platform == 'h5') { + $strView = 'novel/getNovelInfo.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + }) + ->pattern(['intNovelId' => '\d+', 'intForgeId' => '\d+', 'name' => '[\w-]+',]); + break; - case 'novel': - //小说详情 - $strView = 'pc/getNovelInfo.html'; - if ($platform == 'h5') { - $strView = 'novel/getNovelInfo.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }) - ->pattern(['intNovelId' => '\d+', 'name' => '[\w-]+',]); + case 'novel': + //小说详情 + $strView = 'pc/getNovelInfo.html'; + if ($platform == 'h5') { + $strView = 'novel/getNovelInfo.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + }) + ->pattern(['intNovelId' => '\d+', 'name' => '[\w-]+',]); - break; - case 'catalog': - //目录 - $strView = 'pc/getNovelCatalog.html'; - if ($platform == 'h5') { - $strView = 'novel/getNovelCatalog.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }) - ->pattern(['intNovelId' => '\d+', 'name' => '[\w-]+', 'strOrder' => 'zheng|dao', 'intPage' => '\d+']); + break; + case 'catalog': + //目录 + $strView = 'pc/getNovelCatalog.html'; + if ($platform == 'h5') { + $strView = 'novel/getNovelCatalog.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + }) + ->pattern(['intNovelId' => '\d+', 'name' => '[\w-]+', 'strOrder' => 'zheng|dao', 'intPage' => '\d+']); - break; - case 'library': - $strView = 'pc/getLibrary.html'; - if ($platform == 'h5') { - $strView = 'novel/getLibrary.html'; - } - //书库/分类 - Route::get($strRoute, function () use ($strView) { - return view($strView); - }) - ->pattern([ + break; + case 'library': + $strView = 'pc/getLibrary.html'; + if ($platform == 'h5') { + $strView = 'novel/getLibrary.html'; + } + //书库/分类 + Route::get($strRoute, function () use ($strView) { + return view($strView); + }) + ->pattern([ + 'strGender' => '[a-z\-]*', // 允许空值 + 'strCategory' => '[a-z\-]*', // 允许空值 + 'strStatus' => '(all|lianzai|wanjie)?', // 允许空值 + 'strOrder' => '[a-z\-]*', // 允许空值 + 'intPage' => '\d*', // 允许空值 + // 'strCategory' => 'all|xuanhuan-xiuzhen|junshi-xiaoshuo|wangyou-xiaoshuo|kehuan-xiaoshuo|hongsheng-chuanyue|dushi-xiaoshuo|lingyi-xiaoshuo|yanqing-xiaoshuo|qita-xiaoshuo', + + ]); + break; + + case 'library-index': + //书库/分类首页 + $strView = 'pc/getLibrary.html'; + if ($platform == 'h5') { + $strView = 'novel/getLibrary.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + }); + + break; + case 'rank': + //排行榜 + $strView = 'pc/getNovelRank.html'; + if ($platform == 'h5') { + $strView = 'novel/getNovelRank.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + }); + break; + case 'rank-info': + //排行榜 + $strView = 'pc/getNovelRank.html'; + if ($platform == 'h5') { + $strView = 'novel/getNovelRank.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + })->pattern([ 'strGender' => '[a-z\-]*', // 允许空值 'strCategory' => '[a-z\-]*', // 允许空值 'strStatus' => '(all|lianzai|wanjie)?', // 允许空值 'strOrder' => '[a-z\-]*', // 允许空值 'intPage' => '\d*', // 允许空值 - // 'strCategory' => 'all|xuanhuan-xiuzhen|junshi-xiaoshuo|wangyou-xiaoshuo|kehuan-xiaoshuo|hongsheng-chuanyue|dushi-xiaoshuo|lingyi-xiaoshuo|yanqing-xiaoshuo|qita-xiaoshuo', ]); - break; + break; - case 'library-index': - //书库/分类首页 - $strView = 'pc/getLibrary.html'; - if ($platform == 'h5') { - $strView = 'novel/getLibrary.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }); + case 'boys': + //男生 + $strView = 'pc/getNovelChannel.html'; + if ($platform == 'h5') { + $strView = 'novel/getNovelChannel.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + })->append(['strChannel' => 'man']); - break; - case 'rank': - //排行榜 - $strView = 'pc/getNovelRank.html'; - if ($platform == 'h5') { - $strView = 'novel/getNovelRank.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }); - break; - case 'rank-info': - //排行榜 - $strView = 'pc/getNovelRank.html'; - if ($platform == 'h5') { - $strView = 'novel/getNovelRank.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - })->pattern([ - 'strGender' => '[a-z\-]*', // 允许空值 - 'strCategory' => '[a-z\-]*', // 允许空值 - 'strStatus' => '(all|lianzai|wanjie)?', // 允许空值 - 'strOrder' => '[a-z\-]*', // 允许空值 - 'intPage' => '\d*', // 允许空值 + //Route::get($strRoute, [Novel::class, 'getNovelChannel'])->append(['boolIsMobile' => $boolIsMobile, 'strChannel' => 'boys']); + break; - ]); - break; + case 'girls': + //女生 + $strView = 'pc/getNovelChannel.html'; + if ($platform == 'h5') { + $strView = 'novel/getNovelChannel.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + })->append(['strChannel' => 'women']); - case 'boys': - //男生 - $strView = 'pc/getNovelChannel.html'; - if ($platform == 'h5') { - $strView = 'novel/getNovelChannel.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - })->append(['strChannel' => 'man']); + break; - //Route::get($strRoute, [Novel::class, 'getNovelChannel'])->append(['boolIsMobile' => $boolIsMobile, 'strChannel' => 'boys']); - break; + case 'search': + //搜素 + // $strView = 'pc/getSearchNovel.html'; + // if ($platform == 'h5') { + // $strView = 'novel/getSearchNovel.html'; + // } + // Route::get($strRoute, function () use ($strView) { + // return view($strView); + // })->ext('html'); + // break; - case 'girls': - //女生 - $strView = 'pc/getNovelChannel.html'; - if ($platform == 'h5') { - $strView = 'novel/getNovelChannel.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - })->append(['strChannel' => 'women']); + // 首页 + if ($platform == 'h5') { + Route::get($strRoute, function (\think\Request $Request, SiteContext $SiteContext) { + return view($SiteContext->getHomeView('h5')); + }); + } else { + Route::get($strRoute, function (\think\Request $Request, SiteContext $SiteContext) { + return view($SiteContext->getHomeView('pc')); + }); + } + break; - break; - - case 'search': - //搜素 - // $strView = 'pc/getSearchNovel.html'; - // if ($platform == 'h5') { - // $strView = 'novel/getSearchNovel.html'; - // } - // Route::get($strRoute, function () use ($strView) { - // return view($strView); - // })->ext('html'); - // break; - - // 首页 - if ($platform == 'h5') { - Route::get($strRoute, function (\think\Request $Request, SiteContext $SiteContext) { - return view($SiteContext->getHomeView('h5')); + case 'user': + //用户中心 + $strView = 'pc/user/index.html'; + if ($platform == 'h5') { + $strView = 'user/index.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); }); - } else { - Route::get($strRoute, function (\think\Request $Request, SiteContext $SiteContext) { - return view($SiteContext->getHomeView('pc')); + break; + + case 'bookshelf': + //书架 + $strView = 'pc/user/getBookShelf.html'; + if ($platform == 'h5') { + $strView = 'user/getBookShelf.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); }); - } - break; + break; - case 'user': - //用户中心 - $strView = 'pc/user/index.html'; - if ($platform == 'h5') { - $strView = 'user/index.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }); - break; - - case 'bookshelf': - //书架 - $strView = 'pc/user/getBookShelf.html'; - if ($platform == 'h5') { - $strView = 'user/getBookShelf.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }); - break; - - case 'history': - //历史记录 - $strView = 'pc/user/getHistory.html'; - if ($platform == 'h5') { - $strView = 'user/getHistory.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }); - break; - - case 'index': - // 首页 - if ($platform == 'h5') { - Route::get($strRoute, function (\think\Request $Request, SiteContext $SiteContext) { - return view($SiteContext->getHomeView('h5')); + case 'history': + //历史记录 + $strView = 'pc/user/getHistory.html'; + if ($platform == 'h5') { + $strView = 'user/getHistory.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); }); - } else { - Route::get($strRoute, function (\think\Request $Request, SiteContext $SiteContext) { - return view($SiteContext->getHomeView('pc')); + break; + + case 'index': + // 首页 + if ($platform == 'h5') { + Route::get($strRoute, function (\think\Request $Request, SiteContext $SiteContext) { + return view($SiteContext->getHomeView('h5')); + }); + } else { + Route::get($strRoute, function (\think\Request $Request, SiteContext $SiteContext) { + return view($SiteContext->getHomeView('pc')); + }); + } + break; + case 'nindex': + // 首页 + $strView = 'pc/index.html'; + if ($platform == 'h5') { + $strView = 'index/index.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); }); - } - break; - case 'nindex': - // 首页 - $strView = 'pc/index.html'; - if ($platform == 'h5') { - $strView = 'index/index.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }); - break; - case 'vindex': - // 首页 - $strView = 'pc/index.html'; - if ($platform == 'h5') { - $strView = 'index/index.html'; - } - Route::get($strRoute, function () use ($strView) { - return view($strView); - }); - break; - + break; + case 'vindex': + // 首页 + $strView = 'pc/index.html'; + if ($platform == 'h5') { + $strView = 'index/index.html'; + } + Route::get($strRoute, function () use ($strView) { + return view($strView); + }); + break; + } } } } diff --git a/code/app/home/view/videoGpt1/base.html b/code/app/home/view/videoGpt1/base.html index 3794831..5c84704 100644 --- a/code/app/home/view/videoGpt1/base.html +++ b/code/app/home/view/videoGpt1/base.html @@ -4,12 +4,10 @@ {block name="get-data"}{/block} - {block name="title"}{/block} - {// 动态主题颜色 } {// 每个域名专属 CSS(合并 + 前缀替换后) } - + {block name="head"}{/block} {block name="head-css"}{/block} @@ -54,19 +51,23 @@ })(); + {block name="strPageCode"} {/block} + + + {// header} - {include file="$TpTpl.head" /} + {include file="$TpStyle.templates.head" /}
{block name="main"}{/block}
{// footer } - {include file="$TpTpl.foot" /} + {include file="$TpStyle.templates.foot" /} {// DOM 干扰节点,站群差异用 }
tpl-{$TpStyle.static_hash}
@@ -79,7 +80,7 @@ {block name="footer-js"}{/block} + src="{site:cfg code='PUBLIC_STATIC_DOMAIN' encode='false'/}{$strTpJs}?v={site:cfg code='STATIC_FILE_VERSION' encode='false'/}"> \ No newline at end of file diff --git a/code/app/home/view/videoGpt1/index/index-back.html b/code/app/home/view/videoGpt1/index/index-back.html deleted file mode 100644 index 9f99f52..0000000 --- a/code/app/home/view/videoGpt1/index/index-back.html +++ /dev/null @@ -1,98 +0,0 @@ -{extend name="base" /} - -{block name="get-data"} - -{video:listexp count="6" sort_type="tuijian" d_key="key" d_val="Video" cache_life="3600" export_name="arrVideoTuijian" -/} -{video:listexp count="6" sort_type="piaofang" d_key="key" d_val="Video" cache_life="3600" -export_name="arrVideoPiaofang" /} - - -{video:listexp -count="12" -sort_type="tuijian" -export_name="arrVideoRecommend" -/} - - -{video:listexp -count="12" -sort_type="zuixin" -export_name="arrVideoNewest" -/} - -{video:listexp -count="12" -sort_type="zuire" -export_name="arrVideoTrending" -/} - - -{video:listexp -count="12" -sort_type="ranking" -export_name="arrVideoRanking" -/} - -{site:grm page_code="h5_index" diff="0" num="80" g_key="arrGrm" /} - -{/block} - - -{block name="title"} -{site:replace code="VIDEO@INDEX@INDEX@TITLE"} -{/block} - -{block name="keywords"} -{site:replace code="VIDEO@INDEX@INDEX@KEYWORDS"} -{/block} - -{block name="description"} -{site:replace code="VIDEO@INDEX@INDEX@DESCRIPTION"} -{/block} - -{block name="main"} - -{// ================= 最近更新 ================= } -{include file="module/list/home_newest" /} - -{// ================= 热门推荐 ================= } -{include file="module/list/home_hot" /} - -{// ================= 排行榜 ================= } -{include file="module/list/home_rank" /} - -
- {foreach $TpStyle.home_modules as $key=>$mod } - - {if $mod == 'banner'} - {include file="$TpTpl.banner" /} - {/if} - - {if $mod == 'recommend'} - {include file="$TpTpl.recommend" /} - {/if} - - {if $mod == 'trending'} - {include file="$TpTpl.trending" /} - {/if} - - {if $mod == 'newest'} - {include file="$TpTpl.newest" /} - {/if} - - {if $mod == 'ranking'} - {include file="$TpTpl.ranking" /} - {/if} - - {if $mod == 'category'} - {include file="$TpTpl.category" /} - {/if} - - {/foreach} - - {include file="$TpTpl.list" /} - -
- -{/block} \ No newline at end of file diff --git a/code/app/home/view/videoGpt1/index/index.html b/code/app/home/view/videoGpt1/index/index.html index b73ca31..52c9154 100644 --- a/code/app/home/view/videoGpt1/index/index.html +++ b/code/app/home/view/videoGpt1/index/index.html @@ -4,25 +4,64 @@ {/block} +{block name="title"}{site:replace code="VIDEO@ @INDEX@TITLE"}{/block} +{block name="keywords"}{site:replace code="VIDEO@INDEX@INDEX@KEYWORDS"}{/block} +{block name="description"}{site:replace code="VIDEO@INDEX@INDEX@DESCRIPTION"}{/block} -{block name="title"} -{site:replace code="VIDEO@INDEX@INDEX@TITLE"} -{/block} +{block name="head"} + + -{block name="keywords"} -{site:replace code="VIDEO@INDEX@INDEX@KEYWORDS"} -{/block} -{block name="description"} -{site:replace code="VIDEO@INDEX@INDEX@DESCRIPTION"} -{/block} +{// 社交媒体标签 } + + + + +{// 结构化数据 } + +{/block} {block name="main"} -
- - {foreach $TpStyle.template_cfg.pages.home.modules as $module} +
+ + {// lunli } + {assign name="Slot" value="$TpStyle.template_cfg.pages.home.lunliSlots[0]"} + {assign name="title" value="$Slot.title_text"} + {include file="module/list/title/_title_router" /} + {assign name="listCfg" value="$TpStyle.template_cfg.list_layout.lunli"} + {assign name="limit" value="$listCfg.layout['max_items']"} + {video:listexp count="$limit" v_parent_category_en="dian-ying" + v_category_en="lun-li-pian" d_key="key" d_val="Video" cache_life="3600" + export_name="__LIST__" /} + {include file="module/list/shell/_shell_router" /} + + + + {// top} + {foreach $TpStyle.template_cfg.pages.home.slots as $Slot} + + {assign name="module" value="$Slot['layout_key']"} + + {assign name="limit" value="$TpStyle.template_cfg.list_layout[$module]['layout']['max_items']"} - {assign name="limit" value="$TpStyle.list_layout[$module]['layout']['max_items']"} {if $module == 'rank'} {video:ranklistexp count="$limit" sort_type="weekly" d_key="key" d_val="Video" cache_life="3600" export_name="__LIST__" /} @@ -30,13 +69,38 @@ {video:listexp count="$limit" sort_type="$module" d_key="key" d_val="Video" cache_life="3600" export_name="__LIST__" /} {/if} - {include file="module/list/home_block" /} {/foreach} + {// 分类} + {foreach $TpStyle.template_cfg.pages.home.categories as $Category} + {assign name="Slot" value="$Category"} + + {site:vciurl parent_category="$Category.key" export_name="strMoreUrl" /} + + {assign name="title" value="$Slot.title_text"} + + {include file="module/list/title/_title_router" /} + + {assign name="Module" value="$Category.layout_key"} + + {assign name="listCfg" value="$TpStyle.template_cfg.list_layout[$module]"} + + {assign name="limit" value="$listCfg.layout['max_items']"} + + {video:listexp count="$limit" + v_parent_category_en="$Category.key" + sort_type="news" + d_key="d_key" d_val="Video" cache_life="3600" + export_name="__LIST__" /} + + {// ===== Shell + Item ===== } + {include file="module/list/shell/_shell_router" /} + + {/foreach}
{/block} diff --git a/code/app/home/view/videoGpt1/module/breadcrumb/breadcrumb_C.html b/code/app/home/view/videoGpt1/module/breadcrumb/breadcrumb_C.html index 95394ea..f19c5de 100644 --- a/code/app/home/view/videoGpt1/module/breadcrumb/breadcrumb_C.html +++ b/code/app/home/view/videoGpt1/module/breadcrumb/breadcrumb_C.html @@ -6,7 +6,12 @@ {if $intVariant == 0} {volist name="arrBreadcrumb" id="node" key="i"} - {$node.title}{if $i < count($arrBreadcrumb)} > {/if} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} + {if $i < count($arrBreadcrumb)} > {/if} {/volist} @@ -27,7 +32,11 @@ {elseif $intVariant == 2} {volist name="arrBreadcrumb" id="node"} - {$node.title} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} {/volist} @@ -36,7 +45,12 @@

当前所在位置: {volist name="arrBreadcrumb" id="node" key="i"} - {$node.title}{if $i < count($arrBreadcrumb)} → {/if} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} + {if $i < count($arrBreadcrumb)} → {/if} {/volist}

@@ -44,7 +58,11 @@ {elseif $intVariant == 4} @@ -52,7 +70,12 @@ {elseif $intVariant == 5} {volist name="arrBreadcrumb" id="node" key="i"} - {$node.title}{if $i < count($arrBreadcrumb)} / {/if} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} + {if $i < count($arrBreadcrumb)} / {/if} {/volist} @@ -60,7 +83,11 @@ {elseif $intVariant == 6} {volist name="arrBreadcrumb" id="node"} - {$node.title} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} {/volist} @@ -68,7 +95,11 @@ {elseif $intVariant == 7}
{volist name="arrBreadcrumb" id="node"} - {$node.title} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} {/volist}
@@ -76,7 +107,11 @@ {elseif $intVariant == 8}
{volist name="arrBreadcrumb" id="node"} - {$node.title} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} {/volist}
@@ -85,7 +120,12 @@

本页面内容来源于 {volist name="arrBreadcrumb" id="node" key="i"} - {$node.title}{if $i < count($arrBreadcrumb)} · {/if} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} + {if $i < count($arrBreadcrumb)} · {/if} {/volist}

@@ -106,7 +146,11 @@

您当前浏览的是: {volist name="arrBreadcrumb" id="node"} - {$node.title} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} {/volist}

@@ -114,7 +158,11 @@ {elseif $intVariant == 12}
{volist name="arrBreadcrumb" id="node"} - {$node.title} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} {/volist}
@@ -122,7 +170,11 @@ {elseif $intVariant == 13} {volist name="arrBreadcrumb" id="node"} - {$node.title} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} {/volist} @@ -130,7 +182,11 @@ {elseif $intVariant == 14} {volist name="arrBreadcrumb" id="node"} - {$node.title} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} {/volist} @@ -139,7 +195,12 @@

本页面为 {volist name="arrBreadcrumb" id="node" key="i"} - {$node.title}{if $i < count($arrBreadcrumb)} 的 {/if} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} + {if $i < count($arrBreadcrumb)} 的 {/if} {/volist} 相关内容展示。

@@ -148,7 +209,13 @@ {elseif $intVariant == 16}

{volist name="arrBreadcrumb" id="node"} - {$node.title} + + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} + {/volist}

@@ -156,7 +223,12 @@ {elseif $intVariant == 17} {volist name="arrBreadcrumb" id="node" key="i"} - {$node.title}{if $i < count($arrBreadcrumb)} , {/if} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} + {if $i < count($arrBreadcrumb)} , {/if} {/volist} @@ -164,7 +236,11 @@ {elseif $intVariant == 18} @@ -172,7 +248,11 @@ {else}
{volist name="arrBreadcrumb" id="node"} - {$node.title} + {if !empty($node.url)} + {$node.title} + {else /} + {$node.title} + {/if} {/volist}
{/if} diff --git a/code/app/home/view/videoGpt1/module/detail/_seo_addon.html b/code/app/home/view/videoGpt1/module/detail/_seo_addon.html new file mode 100644 index 0000000..5b359a9 --- /dev/null +++ b/code/app/home/view/videoGpt1/module/detail/_seo_addon.html @@ -0,0 +1,21 @@ +{if !empty($arrAddon.summary) || !empty($arrAddon.tags) || !empty($arrAddon.tip)} +
+ + {if !empty($arrAddon.summary)} +

{$arrAddon.summary}

+ {/if} + + {if !empty($arrAddon.tags)} +
+ {volist name="$arrAddon.tags" id="t"} + {$t} + {/volist} +
+ {/if} + + {if !empty($arrAddon.tip)} +

{$arrAddon.tip}

+ {/if} + +
+{/if} diff --git a/code/app/home/view/videoGpt1/module/detail_main/cover.html b/code/app/home/view/videoGpt1/module/detail_main/cover.html index 8d62268..a82b1d5 100644 --- a/code/app/home/view/videoGpt1/module/detail_main/cover.html +++ b/code/app/home/view/videoGpt1/module/detail_main/cover.html @@ -1,17 +1,18 @@ {// ===================== Cover Variants ===================== } +{video:imgalt video="$arrVideo" slot="detail_cover" item_type="poster" export_name="strAlt" /} {if $variant == 0}
{$arrVideo.v_name} + alt="{$strAlt}">
{elseif $variant == 1}
{$arrVideo.v_name} + alt="{$strAlt}">
{$arrVideo.v_remarks}
@@ -22,19 +23,19 @@ href='{site:vpurl v_id="$arrVideo.v_id" v_py="$arrVideo.v_name_en" play_type="default" play_index="1" /}'> {$arrVideo.v_name} + alt="{$strAlt}"> {elseif $variant == 3}
-
+ {$strAlt}
{elseif $variant == 4}
+ src="{site:cfg code='PUBLIC_COVER_DOMAIN' encode='false'/}{$arrVideo.v_pic}" alt="{$strAlt}"> {$arrVideo.v_year} @@ -43,7 +44,7 @@ {elseif $variant == 5}
+ src="{site:cfg code='PUBLIC_COVER_DOMAIN' encode='false'/}{$arrVideo.v_pic}" alt="{$strAlt}"> {$arrVideo.v_score} @@ -51,29 +52,29 @@ {elseif $variant == 6}
-
+ {$strAlt}
{elseif $variant == 7}
+ src="{site:cfg code='PUBLIC_COVER_DOMAIN' encode='false'/}{$arrVideo.v_pic}" alt="{$strAlt}">
{elseif $variant == 8}
+ src="{site:cfg code='PUBLIC_COVER_DOMAIN' encode='false'/}{$arrVideo.v_pic}" alt="{$strAlt}">
{elseif $variant == 9}
+ src="{site:cfg code='PUBLIC_COVER_DOMAIN' encode='false'/}{$arrVideo.v_pic}" alt="{$strAlt}"> {$arrVideo.v_remarks} @@ -81,36 +82,36 @@ {elseif $variant == 10}
-
+ {$strAlt} HD
{elseif $variant == 11} -
+ {$strAlt}
{elseif $variant == 12}
+ src="{site:cfg code='PUBLIC_COVER_DOMAIN' encode='false'/}{$arrVideo.v_pic}" alt="{$strAlt}"> 更新中
{elseif $variant == 13}
+ src="{site:cfg code='PUBLIC_COVER_DOMAIN' encode='false'/}{$arrVideo.v_pic}" alt="{$strAlt}">
{elseif $variant == 14}
-
+ {$strAlt} {$arrVideo.v_year} @@ -119,7 +120,7 @@ {elseif $variant == 15}
+ src="{site:cfg code='PUBLIC_COVER_DOMAIN' encode='false'/}{$arrVideo.v_pic}" alt="{$strAlt}"> 免费 @@ -128,24 +129,24 @@ {elseif $variant == 16}
+ src="{site:cfg code='PUBLIC_COVER_DOMAIN' encode='false'/}{$arrVideo.v_pic}" alt="{$strAlt}">
{elseif $variant == 17} {elseif $variant == 18} -
-
+{$strAlt} + {elseif $variant == 19}
- {$strAlt}
diff --git a/code/app/home/view/videoGpt1/module/detail_main/desc.html b/code/app/home/view/videoGpt1/module/detail_main/desc.html index 9d365db..670e1e0 100644 --- a/code/app/home/view/videoGpt1/module/detail_main/desc.html +++ b/code/app/home/view/videoGpt1/module/detail_main/desc.html @@ -132,3 +132,7 @@ {/if} + +{video:seoaddon video="$arrVideo" export_name="arrAddon" /} +{include file="module/detail/_seo_addon" /} + diff --git a/code/app/home/view/videoGpt1/module/detail_main/meta.html b/code/app/home/view/videoGpt1/module/detail_main/meta.html index fde0223..93a09a9 100644 --- a/code/app/home/view/videoGpt1/module/detail_main/meta.html +++ b/code/app/home/view/videoGpt1/module/detail_main/meta.html @@ -1,6 +1,6 @@ {// ===================== Meta Variants (Array-aware, no i_last) ===================== } -{// 预计算长度(避免 i_last) } +{// 预计算长度(避免 i_last) } {assign name="len_area" value="is_array($arrVideo.v_area)?count($arrVideo.v_area):0"} {assign name="len_actor" value="is_array($arrVideo.v_actor)?count($arrVideo.v_actor):0"} {assign name="len_director" value="is_array($arrVideo.v_director)?count($arrVideo.v_director):0"} diff --git a/code/app/home/view/videoGpt1/module/list/home_block.html b/code/app/home/view/videoGpt1/module/list/home_block.html index 42e4f90..b78805e 100644 --- a/code/app/home/view/videoGpt1/module/list/home_block.html +++ b/code/app/home/view/videoGpt1/module/list/home_block.html @@ -3,13 +3,12 @@ $mod = $module; // 2. 读取该模块的 layout 配置(冻结) -$cfg = $TpStyle['list_layout'][$mod] ?? null; -$listCfg = $TpStyle['list_layout'][$mod] ?? null; +$cfg = $TpStyle['template_cfg']['list_layout'][$mod] ?? null; +$listCfg = $TpStyle['template_cfg']['list_layout'][$mod] ?? null; if (!$cfg) return; // 3. 绑定数据变量(只在这里做一次映射) - -$title = $cfg['title_text'] ?? null; +$title = $Slot['title_text'] ?? null; ?>
diff --git a/code/app/home/view/videoGpt1/module/list/item/_item_router.html b/code/app/home/view/videoGpt1/module/list/item/_item_router.html index 2445dd5..bdfe64e 100644 --- a/code/app/home/view/videoGpt1/module/list/item/_item_router.html +++ b/code/app/home/view/videoGpt1/module/list/item/_item_router.html @@ -1,10 +1,11 @@ {php} -// 统一从 cfg 下发(冻结) -$variant = isset($cfg['item_variant']) ? intval($cfg['item_variant']) : 0; +// 统一从 listCfg 下发(冻结) +$variant = isset($listCfg['item_variant']) ? intval($listCfg['item_variant']) : 0; {/php} +{video:imgalt video="$vo" slot="list_poster" item_type="$listCfg.item_type" export_name="strAlt" /} -{switch $cfg.item} +{switch $listCfg.item} {case 01} {include file="module/list/item/item_01" /} diff --git a/code/app/home/view/videoGpt1/module/list/item/item_01.html b/code/app/home/view/videoGpt1/module/list/item/item_01.html index a17d595..cf82a21 100644 --- a/code/app/home/view/videoGpt1/module/list/item/item_01.html +++ b/code/app/home/view/videoGpt1/module/list/item/item_01.html @@ -5,7 +5,7 @@
{elseif $variant == 3} @@ -50,7 +50,7 @@
- + {$strAlt}

{$vo.v_name}

@@ -63,23 +63,24 @@

{$vo.v_name}

-
- -
+ + + {elseif $variant == 5} {// Variant 5:span + div 混合 }
- - {$vo.v_name ?? ''} - {$vo.v_parent_category ?? ''}{$vo.v_category ?? ''}免费高清电影在线观看 - -
- {$vo.v_name} - {$vo.v_year} -
+ + + {$vo.v_name ?? ''} - {$vo.v_parent_category ?? ''}{$vo.v_category ?? ''}免费高清电影在线观看 + +
+ {$vo.v_name} + {$vo.v_year} +
+
{elseif $variant == 6} @@ -89,7 +90,7 @@

{$vo.v_name}

- + @@ -97,12 +98,12 @@ {// Variant 7:反向嵌套 }
- @@ -111,31 +112,31 @@ {// Variant 8:极简 } {elseif $variant == 9} {// Variant 9:多层包裹 }
- {elseif $variant == 10} {// Variant 10:aside 结构 } @@ -143,19 +144,19 @@ {// Variant 11:dl / dt / dd }
- - + +
-
{$vo.v_name}
+
{$vo.v_name}
{elseif $variant == 12} {// Variant 12:section + header }
-
{$vo.v_name}
+
{$vo.v_name}
- + {$strAlt}
@@ -163,7 +164,7 @@ {// Variant 13:figure 无 figcaption }
- +
@@ -172,7 +173,7 @@
{$vo.v_name} - +
@@ -181,7 +182,7 @@

- + {$vo.v_name}

@@ -191,24 +192,24 @@ {// Variant 16:div + data } {elseif $variant == 17} {// Variant 17:h4 }
-

{$vo.v_name}

+

{$vo.v_name}

- +
{elseif $variant == 18} {// Variant 18:纯链接卡 } - + {$vo.v_name} @@ -216,8 +217,8 @@ {// Variant 19:极端自由结构 } {/if} \ No newline at end of file diff --git a/code/app/home/view/videoGpt1/module/list/item/item_02.html b/code/app/home/view/videoGpt1/module/list/item/item_02.html index d3b2ef7..7cbd675 100644 --- a/code/app/home/view/videoGpt1/module/list/item/item_02.html +++ b/code/app/home/view/videoGpt1/module/list/item/item_02.html @@ -2,10 +2,10 @@ {if $variant == 0} {// V0 标准(你给的母型) } -
+
- {$vo.v_name} + {$strAlt} {notempty name="vo.v_score"} {$vo.v_score} {/notempty} @@ -32,10 +32,10 @@ {elseif $variant == 1} {// V1 figure/figcaption 语义化 } -
  • +
  • - {$vo.v_name} + {$strAlt} {notempty name="vo.v_score"} {$vo.v_score} {/notempty} @@ -59,7 +59,17 @@ {elseif $variant == 2} {// V2 信息前置 + 封面后置(结构反转) } -
    + {elseif $variant == 3} {// V3 多层包裹:inner / media / text } -
    +