From a87a4a41cce06249a3f47fcb6bc8bfd95cf9287f Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 18 Apr 2026 14:11:21 +0800 Subject: [PATCH] feat: improve gpt import templates and seo docs --- code/app/admin/controller/Site.php | 79 +++-- .../DomainBatchImportTemplateHelper.php | 278 +++++++++++++----- .../common/helper/DomainSupplyBatchHelper.php | 125 +++++++- .../DomainSupplyBatchTemplateHelper.php | 217 +++++++++++--- .../site-domain-import-template.xlsx | Bin 0 -> 8911 bytes docs/2026-04-18-7天推收录执行主线.md | 70 +++++ .../03-GPT模板每日复盘模板-2026-04-18.md | 59 ++++ .../04-GPT模板A-B组日志跟踪板-2026-04-18.md | 31 ++ ...-GPT模板次日蜘蛛日志判读模板-2026-04-19.md | 219 ++++++++++++++ ...-GPT模板运营导入模板使用说明-2026-04-18.md | 136 +++++++++ ...入模板中文化与自动样本补全交接-2026-04-18.md | 224 ++++++++++++++ ...-GPT模板导入模板改造交付清单-2026-04-18.md | 96 ++++++ docs/gpt-template-seo/README.md | 8 + 13 files changed, 1397 insertions(+), 145 deletions(-) create mode 100644 code/public/_admin_templates/domain-import/site-domain-import-template.xlsx create mode 100644 docs/gpt-template-seo/09-GPT模板次日蜘蛛日志判读模板-2026-04-19.md create mode 100644 docs/gpt-template-seo/10-GPT模板运营导入模板使用说明-2026-04-18.md create mode 100644 docs/gpt-template-seo/11-GPT模板导入模板中文化与自动样本补全交接-2026-04-18.md create mode 100644 docs/gpt-template-seo/12-GPT模板导入模板改造交付清单-2026-04-18.md diff --git a/code/app/admin/controller/Site.php b/code/app/admin/controller/Site.php index 935aaa8..53101c1 100644 --- a/code/app/admin/controller/Site.php +++ b/code/app/admin/controller/Site.php @@ -1230,9 +1230,14 @@ class Site extends BaseController $Spreadsheet = IOFactory::load($FilePath); $Sheet = $Spreadsheet->getActiveSheet(); - $arrData = $Sheet->toArray(); + $arrData = $Sheet->toArray(null, true, true, false); - array_shift($arrData); + $arrHeaders = array_map( + static function ($strHeader): string { + return DomainBatchImportTemplateHelper::normalizeHeader(trim((string)$strHeader)); + }, + (array)array_shift($arrData) + ); $strRunRoot = DomainImportProbeRunHelper::createRunRoot($this->domainImportRunRoot(), 'excel_import'); $strUploadedExcelPath = $strRunRoot . '/uploaded.' . $strExt; @@ -1252,34 +1257,46 @@ class Site extends BaseController foreach ($arrData as $arrRows) { $intRowsTotal++; - $strStrategyProfile = $this->normalizeStrategyProfile($arrRows[19] ?? DomainModel::SEO_STRATEGY_PROFILE_STANDARD); - $strTkdMode = DomainModel::normalizeTkdMode((string)($arrRows[20] ?? '')); - $strTkdProvider = DomainModel::normalizeTkdProvider((string)($arrRows[21] ?? DomainModel::TKD_PROVIDER_LOCAL)); + $arrRowAssoc = []; + foreach ($arrHeaders as $index => $strHeader) { + if ($strHeader === '') { + continue; + } + $arrRowAssoc[$strHeader] = trim((string)($arrRows[$index] ?? '')); + } + + if (empty(array_filter($arrRowAssoc, static fn($value): bool => trim((string)$value) !== ''))) { + continue; + } + + $strStrategyProfile = $this->normalizeStrategyProfile($arrRowAssoc['strategy_profile'] ?? DomainModel::SEO_STRATEGY_PROFILE_STANDARD); + $strTkdMode = DomainModel::normalizeTkdMode((string)($arrRowAssoc['tkd_mode'] ?? '')); + $strTkdProvider = DomainModel::normalizeTkdProvider((string)($arrRowAssoc['tkd_provider'] ?? DomainModel::TKD_PROVIDER_LOCAL)); $arrDomain = [ - 'd_domain' => $this->normalizeStoredDomain($arrRows[0] ?? '', $arrRows[14] ?? DomainModel::MATCH_TYPE_EXACT), - 't_id' => $arrRows[1], - 'd_name' => $arrRows[2], - 'd_keywords' => $arrRows[3], - 'd_index_title' => $arrRows[4], - 'd_index_keywords' => $arrRows[5], - 'd_index_description' => $arrRows[6], - 'd_description' => $arrRows[7], - 'd_statis' => $arrRows[8], - 'd_logo_type' => (int)$arrRows[9], - 'd_text_logo' => $arrRows[10], - 'd_img_logo' => $arrRows[11], - 'd_content_encode' => $arrRows[12], - 'info_id' => $arrRows[13], - 'd_match_type' => $this->normalizeMatchType($arrRows[14] ?? DomainModel::MATCH_TYPE_EXACT), - 'd_parent_domain' => $this->normalizeParentDomain($arrRows[15] ?? ''), - 'd_seed_scope' => $this->normalizeSeedScope($arrRows[16] ?? DomainModel::SEED_SCOPE_DOMAIN), - 'd_baidu_token' => $arrRows[17] ?? null, + 'd_domain' => $this->normalizeStoredDomain($arrRowAssoc['d_domain'] ?? '', $arrRowAssoc['d_match_type'] ?? DomainModel::MATCH_TYPE_EXACT), + 't_id' => $arrRowAssoc['t_id'] ?? 1, + 'd_name' => $arrRowAssoc['d_name'] ?? '', + 'd_keywords' => $arrRowAssoc['d_keywords'] ?? '', + 'd_index_title' => $arrRowAssoc['d_index_title'] ?? '', + 'd_index_keywords' => $arrRowAssoc['d_index_keywords'] ?? '', + 'd_index_description' => $arrRowAssoc['d_index_description'] ?? '', + 'd_description' => $arrRowAssoc['d_description'] ?? '', + 'd_statis' => $arrRowAssoc['d_statis'] ?? '', + 'd_logo_type' => (int)($arrRowAssoc['d_logo_type'] ?? 0), + 'd_text_logo' => $arrRowAssoc['d_text_logo'] ?? '', + 'd_img_logo' => $arrRowAssoc['d_img_logo'] ?? '', + 'd_content_encode' => $arrRowAssoc['d_content_encode'] ?? 0, + 'info_id' => $arrRowAssoc['info_id'] ?? 0, + 'd_match_type' => $this->normalizeMatchType($arrRowAssoc['d_match_type'] ?? DomainModel::MATCH_TYPE_EXACT), + 'd_parent_domain' => $this->normalizeParentDomain($arrRowAssoc['d_parent_domain'] ?? ''), + 'd_seed_scope' => $this->normalizeSeedScope($arrRowAssoc['d_seed_scope'] ?? DomainModel::SEED_SCOPE_DOMAIN), + 'd_baidu_token' => $arrRowAssoc['d_baidu_token'] ?? null, ]; - $arrDomain['d_seo_cfg'] = $this->applySeoStrategyProfileForDomainData($arrRows[18] ?? null, $arrDomain, $strStrategyProfile); + $arrDomain['d_seo_cfg'] = $this->applySeoStrategyProfileForDomainData($arrRowAssoc['d_seo_cfg'] ?? null, $arrDomain, $strStrategyProfile); $arrDomain['d_seo_cfg']['tkd'] = array_merge( (array)($arrDomain['d_seo_cfg']['tkd'] ?? []), [ - 'mode' => $strTkdMode !== '' ? $strTkdMode : ((trim((string)($arrRows[4] ?? '')) !== '' || trim((string)($arrRows[5] ?? '')) !== '' || trim((string)($arrRows[6] ?? '')) !== '') ? DomainModel::TKD_MODE_FORCE_IMPORT : DomainModel::TKD_MODE_AUTO_GENERATE), + 'mode' => $strTkdMode !== '' ? $strTkdMode : ((trim((string)($arrRowAssoc['d_index_title'] ?? '')) !== '' || trim((string)($arrRowAssoc['d_index_keywords'] ?? '')) !== '' || trim((string)($arrRowAssoc['d_index_description'] ?? '')) !== '') ? DomainModel::TKD_MODE_FORCE_IMPORT : DomainModel::TKD_MODE_AUTO_GENERATE), 'provider' => $strTkdProvider, ] ); @@ -1385,11 +1402,15 @@ class Site extends BaseController try { $arrTemplate = DomainBatchImportTemplateHelper::writeTemplate($this->domainImportTemplateRoot()); $strRelativePath = str_replace($this->bootstrapCodeRoot() . '/public/', '', $arrTemplate['file_path']); + $arrSummary = (array)($arrTemplate['summary'] ?? []); return $this->success([ 'excel_template_path' => $strRelativePath, 'template_file_name' => $arrTemplate['file_name'], - 'template_summary' => $arrTemplate['summary'], + 'template_summary' => $arrSummary, + 'template_display_headers' => array_values((array)($arrSummary['display_headers'] ?? [])), + 'template_operator_notice' => array_values((array)($arrSummary['operator_notice'] ?? [])), + 'template_filling_guidance' => (array)($arrSummary['filling_guidance'] ?? []), ]); } catch (\Throwable $Throwable) { return $this->error('9999', $Throwable->getMessage()); @@ -1401,11 +1422,15 @@ class Site extends BaseController try { $arrTemplate = DomainSupplyBatchTemplateHelper::writeTemplate($this->domainSupplyTemplateRoot()); $strRelativePath = $this->publicRelativePath((string)$arrTemplate['file_path']); + $arrSummary = (array)($arrTemplate['summary'] ?? []); return $this->success([ 'excel_template_path' => $strRelativePath, 'template_file_name' => $arrTemplate['file_name'], - 'template_summary' => $arrTemplate['summary'], + 'template_summary' => $arrSummary, + 'template_display_headers' => array_values((array)($arrSummary['display_headers'] ?? [])), + 'template_operator_notice' => array_values((array)($arrSummary['operator_notice'] ?? [])), + 'template_filling_guidance' => (array)($arrSummary['filling_guidance'] ?? []), ]); } catch (\Throwable $Throwable) { return $this->error('9999', '生成一体化供给模板失败:' . $Throwable->getMessage()); diff --git a/code/app/common/helper/DomainBatchImportTemplateHelper.php b/code/app/common/helper/DomainBatchImportTemplateHelper.php index 2f9cb4d..9421cd5 100644 --- a/code/app/common/helper/DomainBatchImportTemplateHelper.php +++ b/code/app/common/helper/DomainBatchImportTemplateHelper.php @@ -10,6 +10,84 @@ use PhpOffice\PhpSpreadsheet\Writer\Xlsx; class DomainBatchImportTemplateHelper { + public static function getHeaderLabels(): array + { + return [ + 'd_domain' => '域名', + 'd_name' => '站点名称', + 't_id' => '模板ID', + 'd_index_title' => '首页标题', + 'd_index_keywords' => '首页关键词', + 'd_index_description' => '首页描述', + 'strategy_profile' => 'SEO策略', + 'd_keywords' => '站点关键词', + 'd_description' => '站点描述', + 'd_baidu_token' => '百度推送Token', + 'd_match_type' => '匹配类型', + 'd_parent_domain' => '父域名', + 'd_seed_scope' => '种子范围', + 'd_logo_type' => 'LOGO类型', + 'd_text_logo' => '文字LOGO', + 'd_img_logo' => '图片LOGO', + 'd_statis' => '统计代码', + 'd_content_encode' => '内容编码', + 'info_id' => '信息ID', + 'd_seo_cfg' => '高级SEO配置JSON', + 'tkd_mode' => 'TKD模式', + 'tkd_provider' => 'TKD来源', + ]; + } + + public static function getHeaderAliases(): array + { + $labels = self::getHeaderLabels(); + $aliases = []; + foreach ($labels as $field => $label) { + $aliases[$field] = $field; + $aliases[mb_strtolower($field)] = $field; + $aliases[$label] = $field; + $aliases[mb_strtolower($label)] = $field; + } + + foreach ([ + '站点域名' => 'd_domain', + '域名host' => 'd_domain', + '首页SEO标题' => 'd_index_title', + '首页SEO关键词' => 'd_index_keywords', + '首页SEO描述' => 'd_index_description', + '策略' => 'strategy_profile', + '高级配置JSON' => 'd_seo_cfg', + '内容编码类型' => 'd_content_encode', + ] as $alias => $field) { + $aliases[$alias] = $field; + $aliases[mb_strtolower($alias)] = $field; + } + + return $aliases; + } + + public static function normalizeHeader(string $header): string + { + $normalized = trim($header); + if ($normalized === '') { + return ''; + } + + $aliases = self::getHeaderAliases(); + + return $aliases[$normalized] ?? $aliases[mb_strtolower($normalized)] ?? ''; + } + + public static function getDisplayHeaders(): array + { + $labels = self::getHeaderLabels(); + + return array_map( + static fn(string $field): string => $labels[$field] ?? $field, + self::getHeaders() + ); + } + public static function writeWorkbook( string $filePath, string $sheetTitle, @@ -65,25 +143,25 @@ class DomainBatchImportTemplateHelper { return [ 'd_domain', - 't_id', 'd_name', - 'd_keywords', + 't_id', 'd_index_title', 'd_index_keywords', 'd_index_description', + 'strategy_profile', + 'd_keywords', 'd_description', + 'd_baidu_token', + 'd_match_type', + 'd_parent_domain', + 'd_seed_scope', 'd_statis', 'd_logo_type', 'd_text_logo', 'd_img_logo', 'd_content_encode', 'info_id', - 'd_match_type', - 'd_parent_domain', - 'd_seed_scope', - 'd_baidu_token', 'd_seo_cfg', - 'strategy_profile', 'tkd_mode', 'tkd_provider', ]; @@ -92,28 +170,28 @@ class DomainBatchImportTemplateHelper public static function getColumnNotes(): array { return [ - 'd_domain' => '必填,站点域名;wildcard 模式可填 *.example.com 或 example.com,由后端归一化。', - 't_id' => '必填,模板 ID。', - 'd_name' => '必填,站点名称。', - 'd_keywords' => '可选,站点关键词。', - 'd_index_title' => '可选,首页标题。', - 'd_index_keywords' => '可选,首页关键词。', - 'd_index_description' => '可选,首页描述。', - 'd_description' => '可选,站点描述。', - 'd_statis' => '可选,统计代码。', - 'd_logo_type' => '必填,0 或 1。', - 'd_text_logo' => '可选,文字 LOGO。', - 'd_img_logo' => '可选,图片 LOGO。', - 'd_content_encode' => '必填,通常填 0。', - 'info_id' => '可选,通常填 0。', - 'd_match_type' => '可选,exact 或 wildcard。', - 'd_parent_domain' => 'wildcard 模式建议填写根域名。', - 'd_seed_scope' => '可选,domain 或 host。', - 'd_baidu_token' => '可选,百度推送 token。', - 'd_seo_cfg' => '可选,JSON;会在 strategy_profile 默认值基础上再做覆盖。', - 'strategy_profile' => '可选,standard / traffic / expand / closed。', - 'tkd_mode' => '可选,1/auto_generate=全自动生成;2/ai_optimize=导入 TKD 后本地/AI 优化;3/force_import=强制使用导入值。', - 'tkd_provider' => '可选,local 或 openai;当前默认 local,本地优化已生效,后续可接 OpenAI API。', + 'd_domain' => '必填。填写站点正式域名,不要带 https:// 。示例:jingxifa.com。只有做泛域名时,才填 *.example.com。', + 'd_name' => '必填。填写站点前台展示名称,会用于页头、页脚和基础 SEO 文案。示例:鲸溪发影视。', + 't_id' => '通常直接填 1。只有明确知道要换模板时才改;不确定就不要改。', + 'd_index_title' => '建议填写。首页给搜索引擎看的标题,尽量自然,不要堆词。建议 28 到 40 字。', + 'd_index_keywords' => '建议填写。首页核心关键词,多个词用逗号分隔。示例:短剧推荐,热播短剧,高清短剧。', + 'd_index_description' => '建议填写。首页一句话简介,建议 40 到 80 字,写自然一点。', + 'strategy_profile' => '通常不用改,默认 standard。只有明确知道要做流量型、扩展型站点时,才改成 traffic 或 expand。', + 'd_keywords' => '可选。不填也能跑;如要补充站点级关键词,可填在这里。', + 'd_description' => '可选。不填时系统会按站点名称自动兜底一版简介。', + 'd_baidu_token' => '可选。只有需要百度主动推送时才填写。', + 'd_match_type' => '可选。默认 exact。普通单域名站保持默认即可;只有泛域名场景才需要改。', + 'd_parent_domain' => '只有 wildcard 泛域名场景才需要填根域名。普通单域名站留空即可。', + 'd_seed_scope' => '可选。默认 domain。普通单域名站不用改;只有特殊 host 级种子策略才改。', + 'd_statis' => '可选。统计代码区,运营通常不用填。', + 'd_logo_type' => '通常填 0。普通站点不用改;如果是纯图片 LOGO 再按技术要求调整。', + 'd_text_logo' => '可选。填写前台显示的文字 LOGO;不填时可按站点名称兜底。', + 'd_img_logo' => '可选。只有要使用图片 LOGO 时才填写。', + 'd_content_encode' => '通常填 0。普通运营不用改。', + 'info_id' => '通常填 0。普通运营不用改。', + 'd_seo_cfg' => '可选。高级配置区,通常给技术同学使用。运营不需要填写;只有明确要覆盖默认 SEO 策略时再填 JSON。', + 'tkd_mode' => '可选。一般不用填。留空时系统会根据首页三要素是否导入自动选择模式。', + 'tkd_provider' => '可选。一般保持 local,不需要运营修改。', ]; } @@ -121,52 +199,52 @@ class DomainBatchImportTemplateHelper { return [ [ - 'demo-example-com', - 1, - 'Demo Example', - 'Demo Example,影视推荐', - 'Demo Example 影视内容推荐', - 'Demo Example,影视推荐', - 'Demo Example 内容整理与推荐。', - 'Demo Example 内容整理与推荐。', - '', - 0, - 'Demo Example', - '', - 0, - 0, - 'exact', - '', - 'domain', - '', - '', - 'standard', - '1', - 'local', + 'd_domain' => 'demo-example-com', + 'd_name' => 'Demo Example', + 't_id' => 1, + 'd_index_title' => 'Demo Example 影视内容推荐', + 'd_index_keywords' => 'Demo Example,影视推荐', + 'd_index_description' => 'Demo Example 内容整理与推荐。', + 'strategy_profile' => 'standard', + 'd_keywords' => 'Demo Example,影视推荐', + 'd_description' => 'Demo Example 内容整理与推荐。', + 'd_baidu_token' => '', + 'd_match_type' => 'exact', + 'd_parent_domain' => '', + 'd_seed_scope' => 'domain', + 'd_statis' => '', + 'd_logo_type' => 0, + 'd_text_logo' => 'Demo Example', + 'd_img_logo' => '', + 'd_content_encode' => 0, + 'info_id' => 0, + 'd_seo_cfg' => '', + 'tkd_mode' => '1', + 'tkd_provider' => 'local', ], [ - '*.traffic-example.com', - 1, - 'Traffic Example', - 'Traffic Example,热门推荐', - 'Traffic Example 热门内容推荐', - 'Traffic Example,热门推荐', - 'Traffic Example 热门内容整理与推荐。', - 'Traffic Example 热门内容整理与推荐。', - '', - 0, - 'Traffic Example', - '', - 0, - 0, - 'wildcard', - 'traffic-example.com', - 'host', - '', - '{"forge":{"route_group":"film"},"structured_data":{"organization_name":"Traffic Example Studio"}}', - 'traffic', - '2', - 'local', + 'd_domain' => '*.traffic-example.com', + 'd_name' => 'Traffic Example', + 't_id' => 1, + 'd_index_title' => 'Traffic Example 热门内容推荐', + 'd_index_keywords' => 'Traffic Example,热门推荐', + 'd_index_description' => 'Traffic Example 热门内容整理与推荐。', + 'strategy_profile' => 'traffic', + 'd_keywords' => 'Traffic Example,热门推荐', + 'd_description' => 'Traffic Example 热门内容整理与推荐。', + 'd_baidu_token' => '', + 'd_match_type' => 'wildcard', + 'd_parent_domain' => 'traffic-example.com', + 'd_seed_scope' => 'host', + 'd_statis' => '', + 'd_logo_type' => 0, + 'd_text_logo' => 'Traffic Example', + 'd_img_logo' => '', + 'd_content_encode' => 0, + 'info_id' => 0, + 'd_seo_cfg' => '{"forge":{"route_group":"film"},"structured_data":{"organization_name":"Traffic Example Studio"}}', + 'tkd_mode' => '2', + 'tkd_provider' => 'local', ], ]; } @@ -175,8 +253,45 @@ class DomainBatchImportTemplateHelper { return [ 'headers' => self::getHeaders(), + 'display_headers' => self::getDisplayHeaders(), + 'header_labels' => self::getHeaderLabels(), 'column_notes' => self::getColumnNotes(), 'example_rows' => self::getExampleRows(), + 'template_type' => 'site_domain_import', + 'filling_guidance' => [ + 'main_fields' => [ + 'd_domain', + 'd_name', + 't_id', + 'd_index_title', + 'd_index_keywords', + 'd_index_description', + 'strategy_profile', + ], + 'optional_fields' => [ + 'd_keywords', + 'd_description', + 'd_baidu_token', + 'd_match_type', + 'd_parent_domain', + 'd_seed_scope', + 'd_statis', + 'd_logo_type', + 'd_text_logo', + 'd_img_logo', + 'd_content_encode', + 'info_id', + 'd_seo_cfg', + 'tkd_mode', + 'tkd_provider', + ], + ], + 'operator_notice' => [ + '基础导入模板只负责站点基础信息,不负责 GPT 启动样本字段。', + '运营通常只需要先填写域名、站点名称、模板ID、首页标题、首页关键词、首页描述、SEO策略。', + '其余字段大多数都有默认值或属于技术配置,可留空。', + '如果要做 GPT 供料与启动包,请改用 domain-supply-batch-template.xlsx。', + ], 'supported_strategy_profiles' => ['standard', 'traffic', 'expand', 'closed'], 'supported_match_types' => ['exact', 'wildcard'], 'supported_seed_scopes' => ['domain', 'host'], @@ -189,19 +304,28 @@ class DomainBatchImportTemplateHelper $fileName = 'site-domain-import-template.xlsx'; $filePath = $outputRoot . '/' . $fileName; $summary = self::buildSummary(); + $headerLabels = self::getHeaderLabels(); $rows = array_map( - static function (array $row): array { - return array_combine(self::getHeaders(), $row) ?: []; + static function (array $row) use ($headerLabels): array { + $displayRow = []; + foreach (self::getHeaders() as $field) { + $displayRow[$headerLabels[$field] ?? $field] = (string)($row[$field] ?? ''); + } + return $displayRow; }, self::getExampleRows() ); + $displayNotes = []; + foreach (self::getColumnNotes() as $field => $note) { + $displayNotes[$headerLabels[$field] ?? $field] = $note; + } self::writeWorkbook( $filePath, 'site-domain-import', - self::getHeaders(), + self::getDisplayHeaders(), $rows, - self::getColumnNotes() + $displayNotes ); return [ diff --git a/code/app/common/helper/DomainSupplyBatchHelper.php b/code/app/common/helper/DomainSupplyBatchHelper.php index af4e571..bb4bb3a 100644 --- a/code/app/common/helper/DomainSupplyBatchHelper.php +++ b/code/app/common/helper/DomainSupplyBatchHelper.php @@ -8,6 +8,92 @@ use PhpOffice\PhpSpreadsheet\IOFactory; class DomainSupplyBatchHelper { + protected static function isSampleField(string $field): bool + { + return in_array($field, [ + 'detail_id', + 'detail_slug', + 'search_keyword', + 'category_parent', + 'category_child', + ], true); + } + + protected static function fillAutoDiscoveredSample(array $row): array + { + $host = self::hostLabelFromRow($row); + if ($host === '') { + return [ + 'row' => $row, + 'status' => 'skipped', + 'message' => 'host_empty', + 'filled_fields' => [], + ]; + } + + $needsSample = false; + foreach ([ + 'detail_id', + 'detail_slug', + 'search_keyword', + 'category_parent', + 'category_child', + ] as $field) { + if (trim((string)($row[$field] ?? '')) === '') { + $needsSample = true; + break; + } + } + if (!$needsSample) { + return [ + 'row' => $row, + 'status' => 'not_needed', + 'message' => 'sample_fields_already_present', + 'filled_fields' => [], + ]; + } + + $sampleResult = DomainAutoSampleHelper::discover($host, ['limit' => 80]); + if (($sampleResult['status'] ?? '') !== 'passed' || empty($sampleResult['sample']) || !is_array($sampleResult['sample'])) { + return [ + 'row' => $row, + 'status' => 'failed', + 'message' => (string)($sampleResult['message'] ?? 'auto_sample_discovery_failed'), + 'filled_fields' => [], + ]; + } + + $sample = (array)$sampleResult['sample']; + $filledFields = []; + $mapping = [ + 'detail_id' => (string)($sample['detail']['detail_id'] ?? ''), + 'detail_slug' => (string)($sample['detail']['detail_slug'] ?? ''), + 'search_keyword' => (string)($sample['search']['search_keyword'] ?? ''), + 'category_parent' => (string)($sample['category']['category_parent'] ?? ''), + 'category_child' => (string)($sample['category']['category_child'] ?? ''), + 'play_type' => (string)($sample['play']['play_type'] ?? ''), + 'play_index' => (string)($sample['play']['play_index'] ?? ''), + ]; + + foreach ($mapping as $field => $value) { + if ($value === '') { + continue; + } + if (trim((string)($row[$field] ?? '')) !== '') { + continue; + } + $row[$field] = $value; + $filledFields[] = $field; + } + + return [ + 'row' => $row, + 'status' => empty($filledFields) ? 'failed' : 'filled', + 'message' => empty($filledFields) ? 'auto_sample_discovery_returned_empty_fields' : 'auto_sample_discovered', + 'filled_fields' => $filledFields, + ]; + } + protected static function publicRelativeToAbsolutePath(string $path): string { $path = str_replace('\\', '/', trim($path)); @@ -122,12 +208,28 @@ class DomainSupplyBatchHelper throw new \RuntimeException('Replay template rows are empty: ' . $summaryPath); } + $headerLabels = DomainSupplyBatchTemplateHelper::getHeaderLabels(); + $displayRows = array_map( + static function (array $row) use ($headerLabels): array { + $displayRow = []; + foreach (DomainSupplyBatchTemplateHelper::getHeaders() as $field) { + $displayRow[$headerLabels[$field] ?? $field] = (string)($row[$field] ?? ''); + } + return $displayRow; + }, + $rows + ); + $displayNotes = []; + foreach (DomainSupplyBatchTemplateHelper::getColumnNotes() as $field => $note) { + $displayNotes[$headerLabels[$field] ?? $field] = $note; + } + DomainBatchImportTemplateHelper::writeWorkbook( $outputPath, 'domain-supply-batch', - DomainSupplyBatchTemplateHelper::getHeaders(), - $rows, - DomainSupplyBatchTemplateHelper::getColumnNotes() + DomainSupplyBatchTemplateHelper::getDisplayHeaders(), + $displayRows, + $displayNotes ); return $outputPath; @@ -164,7 +266,9 @@ class DomainSupplyBatchHelper return []; } - $headers = array_map(static fn ($value): string => trim((string)$value), (array)array_shift($rows)); + $headers = array_map(static function ($value): string { + return DomainSupplyBatchTemplateHelper::normalizeHeader(trim((string)$value)); + }, (array)array_shift($rows)); $normalized = []; foreach ($rows as $row) { $assoc = []; @@ -362,9 +466,19 @@ class DomainSupplyBatchHelper continue; } + $autoSample = self::fillAutoDiscoveredSample($row); + $row = (array)($autoSample['row'] ?? $row); $missing = self::validateRow($row); $status = empty($missing) ? 'ready' : 'failed'; $message = empty($missing) ? 'prepared' : ('missing:' . implode(',', $missing)); + if (($autoSample['status'] ?? '') === 'filled') { + $message = 'auto_sample_filled:' . implode(',', (array)($autoSample['filled_fields'] ?? [])); + } elseif (($autoSample['status'] ?? '') === 'failed') { + $sampleMissing = array_values(array_filter($missing, static fn(string $field): bool => self::isSampleField($field))); + if (!empty($sampleMissing)) { + $message .= ';auto_sample_failed:' . (string)($autoSample['message'] ?? ''); + } + } $siteRow = self::buildSiteImportRow($row); $bootstrapRow = self::buildBootstrapBatchRow($row); @@ -385,6 +499,9 @@ class DomainSupplyBatchHelper 'site_domain' => (string)($siteRow['d_domain'] ?? ''), 'bundle_host' => (string)($bootstrapRow['host'] ?? ''), 'missing_fields' => $missing, + 'auto_sample_status' => (string)($autoSample['status'] ?? ''), + 'auto_sample_message' => (string)($autoSample['message'] ?? ''), + 'auto_sample_filled_fields' => array_values((array)($autoSample['filled_fields'] ?? [])), ]; } diff --git a/code/app/common/helper/DomainSupplyBatchTemplateHelper.php b/code/app/common/helper/DomainSupplyBatchTemplateHelper.php index 323b7be..4233f68 100644 --- a/code/app/common/helper/DomainSupplyBatchTemplateHelper.php +++ b/code/app/common/helper/DomainSupplyBatchTemplateHelper.php @@ -12,61 +12,145 @@ class DomainSupplyBatchTemplateHelper { return [ 'host', - 'template_id', - 'strategy_profile', - 'match_type', - 'parent_domain', - 'seed_scope', 'site_name', - 'site_keywords', - 'site_description', + 'template_id', 'site_index_title', 'site_index_keywords', 'site_index_description', - 'baidu_token', - 'seo_cfg_json', + 'strategy_profile', 'detail_id', 'detail_slug', 'search_keyword', 'category_parent', 'category_child', + 'site_keywords', + 'site_description', + 'baidu_token', + 'match_type', + 'parent_domain', + 'seed_scope', 'play_type', 'play_index', 'forge_index', 'rank_slug', 'base_url', 'bundle_name', + 'seo_cfg_json', ]; } + public static function getHeaderLabels(): array + { + return [ + 'host' => '域名', + 'site_name' => '站点名称', + 'template_id' => '模板ID', + 'site_index_title' => '首页标题', + 'site_index_keywords' => '首页关键词', + 'site_index_description' => '首页描述', + 'strategy_profile' => 'SEO策略', + 'detail_id' => '详情样本ID', + 'detail_slug' => '详情样本Slug', + 'search_keyword' => '搜索样本关键词', + 'category_parent' => '父分类Slug', + 'category_child' => '子分类Slug', + 'site_keywords' => '站点关键词', + 'site_description' => '站点描述', + 'baidu_token' => '百度推送Token', + 'match_type' => '匹配类型', + 'parent_domain' => '父域名', + 'seed_scope' => '种子范围', + 'play_type' => '播放类型', + 'play_index' => '播放序号', + 'forge_index' => '锻造序号', + 'rank_slug' => '榜单Slug', + 'base_url' => '站点基础地址', + 'bundle_name' => '自定义Bundle名称', + 'seo_cfg_json' => '高级SEO配置JSON', + ]; + } + + public static function getHeaderAliases(): array + { + $labels = self::getHeaderLabels(); + $aliases = []; + foreach ($labels as $field => $label) { + $aliases[$field] = $field; + $aliases[mb_strtolower($field)] = $field; + $aliases[$label] = $field; + $aliases[mb_strtolower($label)] = $field; + } + + foreach ([ + '站点域名' => 'host', + '域名host' => 'host', + '策略' => 'strategy_profile', + '首页SEO标题' => 'site_index_title', + '首页SEO关键词' => 'site_index_keywords', + '首页SEO描述' => 'site_index_description', + '搜索关键词' => 'search_keyword', + '父分类' => 'category_parent', + '子分类' => 'category_child', + '基础地址' => 'base_url', + '高级配置JSON' => 'seo_cfg_json', + ] as $alias => $field) { + $aliases[$alias] = $field; + $aliases[mb_strtolower($alias)] = $field; + } + + return $aliases; + } + + public static function normalizeHeader(string $header): string + { + $normalized = trim($header); + if ($normalized === '') { + return ''; + } + + $aliases = self::getHeaderAliases(); + + return $aliases[$normalized] ?? $aliases[mb_strtolower($normalized)] ?? ''; + } + + public static function getDisplayHeaders(): array + { + $labels = self::getHeaderLabels(); + + return array_map( + static fn(string $field): string => $labels[$field] ?? $field, + self::getHeaders() + ); + } + public static function getColumnNotes(): array { return [ - 'host' => '必填,域名或 wildcard 域名;会同时用于站点导入和 bundle 生成。', - 'template_id' => '必填,站点模板 ID,默认 1。', - 'strategy_profile' => '可选,standard / traffic / expand / closed,默认 standard。', - 'match_type' => '可选,exact 或 wildcard,默认 exact。', - 'parent_domain' => 'wildcard 模式建议填写根域名。', - 'seed_scope' => '可选,domain 或 host,默认 domain。', - 'site_name' => '可选,站点名称。', - 'site_keywords' => '可选,站点关键词。', - 'site_description' => '可选,站点描述。', - 'site_index_title' => '可选,首页标题;不填时导入链按站点名称推导。', - 'site_index_keywords' => '可选,首页关键词;不填时导入链按站点关键词推导。', - 'site_index_description' => '可选,首页描述;不填时导入链按站点描述推导。', - 'baidu_token' => '可选,百度推送 token。', - 'seo_cfg_json' => '可选,JSON;会在 strategy_profile 默认值基础上继续覆盖。', - 'detail_id' => '必填,启动包详情样本视频 ID。', - 'detail_slug' => '必填,启动包详情样本 slug。', - 'search_keyword' => '必填,启动包搜索页样本关键词。', - 'category_parent' => '必填,父分类 slug。', - 'category_child' => '必填,子分类 slug。', - 'play_type' => '可选,默认 douban。', - 'play_index' => '可选,默认 1。', - 'forge_index' => '可选,默认 1。', - 'rank_slug' => '可选,默认 daily。', - 'base_url' => '可选,默认 https://。', - 'bundle_name' => '可选,自定义 bundle 目录名。', + 'host' => '必填。填写站点正式域名,不要带 https:// 。示例:jingxifa.com。只有做泛域名时,才填 *.example.com。', + 'site_name' => '必填。填写站点前台展示名称,会用于页头、页脚和基础 SEO 文案。示例:鲸溪发影视。', + 'template_id' => '通常直接填 1。只有确认要换模板时才改;不确定就不要改。', + 'site_index_title' => '建议填写。首页给搜索引擎看的标题,尽量自然,不要堆词。建议 28 到 40 字。', + 'site_index_keywords' => '建议填写。首页核心关键词,多个词用逗号分隔。示例:短剧推荐,热播短剧,高清短剧。', + 'site_index_description' => '建议填写。首页一句话简介,建议 40 到 80 字,写自然一点。', + 'strategy_profile' => '通常不用改,默认 standard。只有明确知道要做流量型、扩展型站点时,才改成 traffic 或 expand。', + 'detail_id' => '通常不用手填。站点部署成功后,系统会优先通过自动样本发现,从当前站点可用内容里自动提取。只有域名还没部署成功、当前只是试跑库、或自动发现失败时,才人工补填真实详情视频 ID。', + 'detail_slug' => '通常不用手填。会和详情样本 ID 一起自动获取。若人工填写,必须和详情样本 ID 对应,不能乱配。', + 'search_keyword' => '通常不用手填。系统会优先从站点名称、视频 SEO 词、视频标题里自动挑一个可搜索关键词。只有自动发现失败时,再人工填写一个能搜出结果的词。', + 'category_parent' => '通常不用手填。系统会从自动样本视频所属分类里提取一级分类 slug。只有自动发现失败时,再人工补填。示例:dian-ying。', + 'category_child' => '通常不用手填。系统会从自动样本视频所属分类里提取二级分类 slug。只有自动发现失败时,再人工补填。示例:ju-qing-pian。', + 'site_keywords' => '可选。不填也能跑;如要补充站点级关键词,可填在这里。', + 'site_description' => '可选。不填时系统会按站点名称自动兜底一版简介。', + 'baidu_token' => '可选。只有需要百度主动推送时才填写。', + 'match_type' => '可选。默认 exact。普通单域名站保持默认即可;只有泛域名场景才需要改。', + 'parent_domain' => '只有 wildcard 泛域名场景才需要填根域名。普通单域名站留空即可。', + 'seed_scope' => '可选。默认 domain。普通单域名站不用改;只有特殊 host 级种子策略才改。', + 'play_type' => '可选。默认 douban。自动样本发现成功时通常也能自动带出,不需要运营关心。', + 'play_index' => '可选。默认 1。通常不用改。', + 'forge_index' => '可选。默认 1。通常不用改。', + 'rank_slug' => '可选。默认 daily。通常不用改。', + 'base_url' => '可选。默认自动按 https://域名 生成。只有特殊环境才手填。', + 'bundle_name' => '可选。一般留空,系统会按域名自动生成 bundle 目录名。', + 'seo_cfg_json' => '可选。高级配置区,通常给技术同学使用。运营不需要填写;只有明确要覆盖默认 SEO 策略时再填 JSON。', ]; } @@ -134,8 +218,50 @@ class DomainSupplyBatchTemplateHelper { return [ 'headers' => self::getHeaders(), + 'display_headers' => self::getDisplayHeaders(), + 'header_labels' => self::getHeaderLabels(), 'column_notes' => self::getColumnNotes(), 'example_rows' => self::getExampleRows(), + 'template_type' => 'gpt_supply_batch', + 'filling_guidance' => [ + 'main_fields' => [ + 'host', + 'site_name', + 'template_id', + 'site_index_title', + 'site_index_keywords', + 'site_index_description', + 'strategy_profile', + ], + 'auto_discovery_fields' => [ + 'detail_id', + 'detail_slug', + 'search_keyword', + 'category_parent', + 'category_child', + ], + 'optional_fields' => [ + 'site_keywords', + 'site_description', + 'baidu_token', + 'match_type', + 'parent_domain', + 'seed_scope', + 'play_type', + 'play_index', + 'forge_index', + 'rank_slug', + 'base_url', + 'bundle_name', + 'seo_cfg_json', + ], + ], + 'operator_notice' => [ + '运营通常只需要先填写主填写区。', + '详情样本ID、详情样本Slug、搜索样本关键词、父分类Slug、子分类Slug 通常可以留空。', + '这些样本字段会在域名部署成功或试跑库有可用内容时,优先自动发现并补全。', + '只有自动发现失败时,才需要人工补填样本字段。', + ], 'supported_strategy_profiles' => [ DomainModel::SEO_STRATEGY_PROFILE_STANDARD, DomainModel::SEO_STRATEGY_PROFILE_TRAFFIC, @@ -165,12 +291,29 @@ class DomainSupplyBatchTemplateHelper $fileName = 'domain-supply-batch-template.xlsx'; $filePath = $outputRoot . '/' . $fileName; + $headerLabels = self::getHeaderLabels(); + $displayHeaders = self::getDisplayHeaders(); + $displayRows = array_map( + static function (array $row) use ($headerLabels): array { + $displayRow = []; + foreach (self::getHeaders() as $field) { + $displayRow[$headerLabels[$field] ?? $field] = (string)($row[$field] ?? ''); + } + return $displayRow; + }, + self::getExampleRows() + ); + $displayNotes = []; + foreach (self::getColumnNotes() as $field => $note) { + $displayNotes[$headerLabels[$field] ?? $field] = $note; + } + DomainBatchImportTemplateHelper::writeWorkbook( $filePath, 'domain-supply-batch', - self::getHeaders(), - self::getExampleRows(), - self::getColumnNotes() + $displayHeaders, + $displayRows, + $displayNotes ); return [ diff --git a/code/public/_admin_templates/domain-import/site-domain-import-template.xlsx b/code/public/_admin_templates/domain-import/site-domain-import-template.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..98ed4ce284f4ad8bf434fa1d1e87819ea4ef7a58 GIT binary patch literal 8911 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 detail` 与 `search -> detail`,现在只完成了一半 + +因此,今天的正式结论定为: + +> SEO 进度总体符合“修复后开始进入正向回访期”的预期,但还不符合“sitemap 与搜索链路快速放量”的理想预期。 + +### 今日最值得继续追的站 + +1. `jingxifa.com` +2. `codohealth.com` +3. `lgyz.net` +4. `jpjdxs.com` + +### 今日最需要警惕的站 + +1. `cnzhenbang.com` +2. `lcdchq.com` +3. `oronorent.com` +4. `jxxgygy.com` +5. `liangzuan.net` + +### 明日优先动作 + +1. 继续盯缓存滞后 4 站: + - `jingxifa.com` + - `lcdchq.com` + - `cnzhenbang.com` + - `oronorent.com` +2. 单独复核 `jxxgygy.com` 的 `/sitemap.xml => 444` +3. 看 `cnzhenbang.com` 是否开始进入 `detail` +4. 看 `lgyz.net`、`jpjdxs.com` 是否继续扩大 `detail -> play` diff --git a/docs/gpt-template-seo/03-GPT模板每日复盘模板-2026-04-18.md b/docs/gpt-template-seo/03-GPT模板每日复盘模板-2026-04-18.md index 2b78543..c908ad9 100644 --- a/docs/gpt-template-seo/03-GPT模板每日复盘模板-2026-04-18.md +++ b/docs/gpt-template-seo/03-GPT模板每日复盘模板-2026-04-18.md @@ -147,3 +147,62 @@ - GPT 模板线今天没有新增结构性故障,重点仍在缓存收口和深页回访扩张。 ``` + +## 2026-04-18 正式复盘 + +- 日期:2026-04-18 +- 复盘人:Codex / 团队 +- 今日处理域名:A组6站 + B组9站 +- 今日查看日志时间窗口:2026-04-17 09:10:03 ~ 2026-04-17 22:20:02 +- 今日是否执行清缓存:部分已列优先执行单,尚未全部收口 +- 今日是否执行 `curl` 对照:已对部分缓存滞后站做过对照 + +## 今日新增日志结论 + +1. GPT 模板线已经出现多站 `detail / play` 正向回访,说明修复方向正确。 +2. `jingxifa.com`、`codohealth.com`、`lgyz.net`、`jpjdxs.com` 是当前最明显的正向样本。 +3. `sitemap` 命中整体仍偏弱,搜索页真实回访不足,链路还没有完全跑通。 +4. `cnzhenbang.com`、`lcdchq.com`、`oronorent.com` 仍受缓存或浅层抓取影响。 +5. `jxxgygy.com` 出现 `/sitemap.xml => 444`,属于需要单独复核的风险项。 + +## 今日缓存复查结论 + +### 已恢复一致 + +1. 暂未把任何站正式降级为“完全恢复一致” + +### 仍需继续跟缓存 + +1. `jingxifa.com` +2. `lcdchq.com` +3. `cnzhenbang.com` +4. `oronorent.com` + +### 疑似不是缓存问题 + +1. `jxxgygy.com` 的 sitemap 异常更像链路或规则问题 +2. `liangzuan.net` 当前窗口无明显蜘蛛信号,不属于缓存结论 + +## 今日新增正向信号 + +1. `jingxifa.com` 已有稳定 `detail + play` +2. `codohealth.com` 已有 `detail + play-like` +3. `lgyz.net` 已有 `detail + play` + +## 今日风险项 + +1. `sitemap` 整体抓取信号仍弱 +2. 搜索页真实回访不足,测试探针仍在干扰判断 +3. 缓存滞后 4 站尚未完成收口 + +## 明日优先处理 + +1. 继续盯 `jingxifa.com` +2. 继续盯 `lcdchq.com` +3. 继续盯 `cnzhenbang.com` +4. 继续盯 `oronorent.com` +5. 复核 `jxxgygy.com` 的 `/sitemap.xml => 444` + +## 一句话复盘 + +- 今天的 SEO 进度总体符合“修复后进入正向回访期”的预期,但还未达到 sitemap 和搜索链路快速放量的理想状态。 diff --git a/docs/gpt-template-seo/04-GPT模板A-B组日志跟踪板-2026-04-18.md b/docs/gpt-template-seo/04-GPT模板A-B组日志跟踪板-2026-04-18.md index 26f5010..8f2c667 100644 --- a/docs/gpt-template-seo/04-GPT模板A-B组日志跟踪板-2026-04-18.md +++ b/docs/gpt-template-seo/04-GPT模板A-B组日志跟踪板-2026-04-18.md @@ -84,6 +84,37 @@ 2. GPT 专属目录负责做节奏管理、跟踪板和阶段汇总 3. 每日复盘优先写到 [03-GPT模板每日复盘模板-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/03-GPT模板每日复盘模板-2026-04-18.md) +## 2026-04-18 当日结论 + +### 总判断 + +今天这轮 GPT 模板蜘蛛进度属于: + +1. 总体正向 +2. 符合第一阶段预期 +3. 但尚未达到理想放量速度 + +### 已确认的正向样本 + +1. `jingxifa.com` +2. `codohealth.com` +3. `lgyz.net` +4. `jpjdxs.com` + +### 当前主要卡点 + +1. `sitemap` 命中偏弱 +2. 搜索页真实回访偏少 +3. 缓存滞后站尚未完全收口 +4. 部分弱站仍停在首页层 / robots 层 + +### 明日执行重点 + +1. P0 继续盯缓存滞后 4 站 +2. P1 继续追 `codohealth.com`、`lgyz.net`、`jpjdxs.com` +3. 单独复核 `jxxgygy.com` 的 sitemap 风险 +4. 观察 `cnzhenbang.com` 是否开始进入 detail 层 + ## 当前全局优先级 ### P0 diff --git a/docs/gpt-template-seo/09-GPT模板次日蜘蛛日志判读模板-2026-04-19.md b/docs/gpt-template-seo/09-GPT模板次日蜘蛛日志判读模板-2026-04-19.md new file mode 100644 index 0000000..61ee151 --- /dev/null +++ b/docs/gpt-template-seo/09-GPT模板次日蜘蛛日志判读模板-2026-04-19.md @@ -0,0 +1,219 @@ +# 09 GPT模板次日蜘蛛日志判读模板 2026-04-19 + +这份文档是给明天继续看 GPT 模板蜘蛛日志时直接套用的。 + +目标很简单: + +1. 不重新发明判读口径 +2. 先看最关键的站,再看其余样本站 +3. 把“是否符合预期”快速落成统一结论 + +建议和下面几份文档一起开着看: + +- [01-GPT模板7天SEO执行主线-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/01-GPT模板7天SEO执行主线-2026-04-18.md) +- [03-GPT模板每日复盘模板-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/03-GPT模板每日复盘模板-2026-04-18.md) +- [04-GPT模板A-B组日志跟踪板-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/04-GPT模板A-B组日志跟踪板-2026-04-18.md) +- [2026-04-18-7天推收录执行主线.md](/www/wwwroot/diff-maccms/SEONexus/docs/2026-04-18-7天推收录执行主线.md) + +## 明日先看什么 + +先看 P0,再看 P1,最后补 P2。 + +### P0 缓存与深页突破优先站 + +1. `jingxifa.com` +2. `lcdchq.com` +3. `cnzhenbang.com` +4. `oronorent.com` + +### P1 已出现正向信号、需要确认是否扩大 + +1. `codohealth.com` +2. `lgyz.net` +3. `jpjdxs.com` + +### P2 风险或弱信号观察站 + +1. `liangzuan.net` +2. `jxxgygy.com` +3. `gxhongzhuang.com` +4. `leici1940.com` +5. `pcslcl.com` +6. `sdxtwnc.com` +7. `gz-yxsw.com` +8. `glae.cc` + +## 明日判读顺序 + +### 第 1 步:先判断整体方向有没有继续向前 + +先回答这 3 个问题: + +1. 今天有没有新增 `detail / play` 命中站点 +2. 昨天已有正向信号的站,今天是否继续重复回访 +3. `sitemap / search` 是否比昨天更强 + +如果这 3 个问题里有 2 个以上是正向,就可以先判为: + +- 总体继续向前 + +如果只有第 1 条或第 2 条成立,但 `sitemap / search` 仍弱,就判为: + +- 仍符合预期,但推进速度一般 + +如果 3 条都没有改善,就判为: + +- 进度偏慢,需要怀疑缓存、入口链路或站点质量问题 + +### 第 2 步:逐站看是不是“浅层”还是“深层” + +优先按这个层级判断: + +1. `robots.txt` / `/` +2. `category` +3. `detail` +4. `play` +5. `search` +6. `sitemap` + +原则: + +1. 只停留在 `robots` / 首页,不算明显进展 +2. 进入 `detail`,才算真正开始吃内容页 +3. 出现 `play`,说明深层链路更进一步 +4. `search` 和 `sitemap` 增强,才代表放量基础变好 + +### 第 3 步:逐站判断是不是缓存问题 + +对 4 个 P0 站,统一问这 3 个问题: + +1. 普通访问是否仍落后于 `?__nocache=1` +2. 普通访问 `title / h1 / canonical` 是否已经追平 +3. 蜘蛛日志里的深页访问,是否和前台当前页输出相互印证 + +如果第 1 条仍成立,就继续归类为: + +- 缓存滞后未收口 + +如果第 1 条不成立,但蜘蛛仍只打浅层,就归类为: + +- 已不主要是缓存问题,转为抓取层问题 + +## 明日快速填写模板 + +- 复盘日期:2026-04-19 +- 日志时间窗口: +- 复盘人: +- 今日先看组别:A组 / B组 / 全量 + +## 一、整体判断 + +- 今日总判断: +- 是否符合预期: +- 是否比 2026-04-18 更进一步: + +可直接三选一: + +1. 明显变好 +2. 基本符合预期,但速度一般 +3. 明显低于预期 + +## 二、P0 四站逐站判读 + +### 1. `jingxifa.com` + +- 今日新增信号: +- 当前层级: +- 是否仍有缓存滞后: +- 明日动作: + +### 2. `lcdchq.com` + +- 今日新增信号: +- 当前层级: +- 是否仍有缓存滞后: +- 明日动作: + +### 3. `cnzhenbang.com` + +- 今日新增信号: +- 当前层级: +- 是否仍停留在浅层: +- 明日动作: + +### 4. `oronorent.com` + +- 今日新增信号: +- 当前层级: +- 是否仍有缓存滞后: +- 明日动作: + +## 三、P1 正向样本站判读 + +### `codohealth.com` + +- 是否继续有 `detail / play-like`: +- 是否扩大: +- 判断: + +### `lgyz.net` + +- 是否继续有 `detail / play`: +- 是否扩大: +- 判断: + +### `jpjdxs.com` + +- 是否继续有 `detail / play`: +- 是否出现 `search`: +- 判断: + +## 四、P2 风险与弱站观察 + +### `liangzuan.net` + +- 是否出现新信号: +- 是否有异常页面: +- 判断: + +### `jxxgygy.com` + +- `/sitemap.xml` 是否恢复: +- 是否仍有 `444`: +- 判断: + +### 其余弱站 + +只写有变化的: + +1. +2. +3. + +## 五、明日最关键的 5 个判断句 + +直接写结论,不写长文。 + +1. +2. +3. +4. +5. + +## 六、一句话总复盘 + +- + +## 建议用词 + +如果明天情况继续温和改善,可以直接用: + +> GPT 模板线今天继续保持正向回访,整体仍符合修复后的收录推进预期,但 `sitemap` 与搜索链路仍未进入理想放量阶段。 + +如果明天出现明显推进,可以直接用: + +> GPT 模板线今天较昨天更进一步,已有更多站点从浅层访问进入 `detail / play`,开始接近第一阶段目标。 + +如果明天表现偏弱,可以直接用: + +> GPT 模板线今天新增有效蜘蛛信号有限,整体低于理想节奏,下一步要优先排除缓存未收口和入口链路偏弱的问题。 diff --git a/docs/gpt-template-seo/10-GPT模板运营导入模板使用说明-2026-04-18.md b/docs/gpt-template-seo/10-GPT模板运营导入模板使用说明-2026-04-18.md new file mode 100644 index 0000000..12068b0 --- /dev/null +++ b/docs/gpt-template-seo/10-GPT模板运营导入模板使用说明-2026-04-18.md @@ -0,0 +1,136 @@ +# 10 GPT模板运营导入模板使用说明 2026-04-18 + +这份文档只解决一个问题: + +运营下载模板后,到底该用哪一份,哪些字段必须填,哪些字段通常可以留空。 + +## 先记结论 + +当前有两类模板: + +1. 基础站点导入模板 +2. GPT 供料模板 + +不要混着用。 + +## 一、基础站点导入模板是干什么的 + +适用场景: + +1. 先把站点基础信息导入系统 +2. 先建站、先落基础 SEO 信息 +3. 还没开始做 GPT 启动包和供料 + +这份模板主要管: + +1. 域名 +2. 站点名称 +3. 模板ID +4. 首页标题 +5. 首页关键词 +6. 首页描述 +7. SEO策略 + +下载接口对应: + +- `getDomainImportTemplate` + +## 二、GPT 供料模板是干什么的 + +适用场景: + +1. 已经要做 GPT 模板启动包 +2. 要生成 bundle +3. 要继续跑 bootstrap / probe / 供料链 + +这份模板除了基础信息,还会带: + +1. 详情样本ID +2. 详情样本Slug +3. 搜索样本关键词 +4. 父分类Slug +5. 子分类Slug + +下载接口对应: + +- `getDomainSupplyTemplate` + +## 三、运营通常该怎么填 + +### 基础站点导入模板 + +运营通常先填这 7 项: + +1. 域名 +2. 站点名称 +3. 模板ID +4. 首页标题 +5. 首页关键词 +6. 首页描述 +7. SEO策略 + +其余字段: + +1. 大多数有默认值 +2. 大多数属于技术配置 +3. 不确定就先留空 + +### GPT 供料模板 + +运营通常先填这 7 项: + +1. 域名 +2. 站点名称 +3. 模板ID +4. 首页标题 +5. 首页关键词 +6. 首页描述 +7. SEO策略 + +下面这 5 项通常可以先留空: + +1. 详情样本ID +2. 详情样本Slug +3. 搜索样本关键词 +4. 父分类Slug +5. 子分类Slug + +原因: + +1. 系统会优先尝试自动样本发现 +2. 域名部署成功后,可从当前站点真实内容自动提取 +3. 如果当前只是试跑库,只要库里有可用视频内容,也会优先自动补全 +4. 只有自动发现失败时,才需要人工补填 + +## 四、什么时候样本字段不能完全指望自动化 + +下面这几种情况,要提前有心理预期: + +1. 域名还没部署成功 +2. 当前站点还没有可访问的首页 / 搜索 / 详情链路 +3. 试跑库里没有合适的视频样本 +4. 分类 slug 数据本身不完整 + +这时系统可能自动补不全,才需要人工补: + +1. 详情样本ID +2. 详情样本Slug +3. 搜索样本关键词 +4. 父分类Slug +5. 子分类Slug + +## 五、给运营的一句话规则 + +可以直接这么记: + +1. 只建站,用基础站点导入模板 +2. 要跑 GPT 启动包和供料,用 GPT 供料模板 +3. 两个模板里,前面主填写区优先填 +4. GPT 供料模板里的样本字段,通常先留空,系统自动补 +5. 自动补失败,再找技术补样本字段 + +## 六、给接手 Codex 的一句话规则 + +1. 不要再要求运营手工理解 `detail_id / detail_slug / search_keyword / category_parent / category_child` +2. 优先让自动样本发现补全 +3. 只有自动发现失败,才让人工介入 diff --git a/docs/gpt-template-seo/11-GPT模板导入模板中文化与自动样本补全交接-2026-04-18.md b/docs/gpt-template-seo/11-GPT模板导入模板中文化与自动样本补全交接-2026-04-18.md new file mode 100644 index 0000000..a20d647 --- /dev/null +++ b/docs/gpt-template-seo/11-GPT模板导入模板中文化与自动样本补全交接-2026-04-18.md @@ -0,0 +1,224 @@ +# 11 GPT模板导入模板中文化与自动样本补全交接 2026-04-18 + +这份文档用于交接本轮“模板中文化 + 上传兼容 + 自动样本补全”改动。 + +目标不是讲所有历史,而是让接手同学快速知道: + +1. 这轮改了什么 +2. 为什么要改 +3. 现在运营该怎么用 +4. 后续如果继续改,应该从哪里接 + +## 一、本轮改动目标 + +本轮主要解决两个问题: + +1. 运营下载的 Excel 模板字段太技术化,英文字段不易理解 +2. GPT 供料模板中的样本字段原本看起来像“必须人工填写”,但系统其实已经具备自动发现能力 + +因此这轮统一做了三件事: + +1. 下载模板中文化 +2. 上传解析兼容中文表头 +3. GPT 供料模板支持样本字段留空后自动补全 + +## 二、已经完成的改动 + +### 1. 基础站点导入模板中文化 + +文件: + +- [DomainBatchImportTemplateHelper.php](/www/wwwroot/diff-maccms/SEONexus/code/app/common/helper/DomainBatchImportTemplateHelper.php) + +已完成: + +1. 模板表头改为中文 +2. 字段顺序重排为“主填写区在前” +3. `notes` 改为运营可理解的人话说明 +4. `template_summary` 补充: + - `template_type` + - `filling_guidance` + - `operator_notice` + +### 2. 基础站点导入上传兼容中文表头 + +文件: + +- [Site.php](/www/wwwroot/diff-maccms/SEONexus/code/app/admin/controller/Site.php) + +已完成: + +1. `uploadDomain` 不再死绑固定列号 +2. 现在会先读取首行表头 +3. 再通过 `normalizeHeader(...)` 映射回内部字段 +4. 因此中文表头和旧英文字段都能兼容 + +### 3. GPT 供料模板中文化 + +文件: + +- [DomainSupplyBatchTemplateHelper.php](/www/wwwroot/diff-maccms/SEONexus/code/app/common/helper/DomainSupplyBatchTemplateHelper.php) + +已完成: + +1. 模板表头改为中文 +2. 字段顺序重排为: + - 运营主填写区 + - 自动样本区 + - 技术参数区 +3. `notes` 明确说明: + - 哪些字段通常先填 + - 哪些字段通常可以留空 + - 哪些字段只有技术同学需要关心 + +### 4. GPT 供料模板支持自动样本补全 + +文件: + +- [DomainSupplyBatchHelper.php](/www/wwwroot/diff-maccms/SEONexus/code/app/common/helper/DomainSupplyBatchHelper.php) + +已完成: + +1. 供料模板上传后,如果以下字段为空: + - `detail_id` + - `detail_slug` + - `search_keyword` + - `category_parent` + - `category_child` +2. 系统会优先调用自动样本发现 +3. 若自动发现成功,则回填并继续处理 +4. 若自动发现失败,才把这些字段继续记为缺失 + +## 三、自动样本补全依赖什么 + +自动样本补全不是凭空猜,它依赖现有自动化能力: + +### 自动样本发现 + +文件: + +- [DomainAutoSampleHelper.php](/www/wwwroot/diff-maccms/SEONexus/code/app/common/helper/DomainAutoSampleHelper.php) + +会自动产出: + +1. `detail_id` +2. `detail_slug` +3. `search_keyword` +4. `category_parent` +5. `category_child` +6. `play_type` +7. `play_index` + +### 轨迹探针 + +文件: + +- [DomainTrajectoryProbeHelper.php](/www/wwwroot/diff-maccms/SEONexus/code/app/common/helper/DomainTrajectoryProbeHelper.php) + +会用自动样本继续验证: + +1. 首页 +2. 搜索 +3. 详情 +4. 播放 + +## 四、现在运营应该怎么用 + +### 1. 只做基础建站 + +使用: + +- 基础站点导入模板 + +优先填写: + +1. 域名 +2. 站点名称 +3. 模板ID +4. 首页标题 +5. 首页关键词 +6. 首页描述 +7. SEO策略 + +### 2. 要做 GPT 启动包和供料 + +使用: + +- GPT 供料模板 + +优先填写: + +1. 域名 +2. 站点名称 +3. 模板ID +4. 首页标题 +5. 首页关键词 +6. 首页描述 +7. SEO策略 + +下面 5 个字段通常先留空: + +1. 详情样本ID +2. 详情样本Slug +3. 搜索样本关键词 +4. 父分类Slug +5. 子分类Slug + +原因: + +1. 域名部署成功后可从真实站点链路自动发现 +2. 若当前只是试跑库,只要库里有可用视频,也会优先自动提取 +3. 只有自动发现失败时才人工补 + +## 五、当前边界 + +这轮虽然已经大幅改善体验,但还要明确边界: + +1. 如果域名未部署成功,自动样本不一定能从前台链路补全 +2. 如果试跑库本身缺少有效视频样本,也可能自动失败 +3. 如果分类 slug 数据本身缺失,`category_parent / category_child` 可能补不全 + +所以正确口径不是“永远不用填”,而是: + +1. 通常先留空 +2. 系统优先自动补 +3. 自动失败再人工补 + +## 六、当前推荐口径 + +以后对运营和接手同学统一这么说: + +1. 基础站点导入模板:先做站点基础信息 +2. GPT 供料模板:做 GPT 启动包与供料 +3. GPT 供料模板里的样本字段通常先留空 +4. 系统会优先自动样本发现 +5. 只有自动发现失败时,才需要人工补样本字段 + +## 七、后续如果继续改,优先从哪里接 + +### 优先级 P1 + +1. 在后台下载模板按钮旁增加一句提示文案 +2. 在上传结果页把 `operator_notice` 更明显展示出来 + +### 优先级 P2 + +1. 让基础导入成功后,直接给出“下一步请使用 GPT 供料模板”的入口 +2. 在供料模板执行结果页,把“自动样本已补全哪些字段”展示得更直观 + +### 优先级 P3 + +1. 如果后续还有别的模板线,也统一改成中文表头 + 人话 notes +2. 把所有模板下载入口的摘要口径统一化 + +## 八、关联文档 + +建议接手时一起看: + +1. [README.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/README.md) +2. [10-GPT模板运营导入模板使用说明-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/10-GPT模板运营导入模板使用说明-2026-04-18.md) +3. [08-GPT模板其它服务器更新交接-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/08-GPT模板其它服务器更新交接-2026-04-18.md) + +## 一句话交接 + +> 本轮已经把 GPT 模板线的导入模板从“技术字段驱动”改成“运营可填写驱动”,并把样本字段从“看起来必须手填”收口成“系统优先自动补全、失败才人工补填”的新口径。 diff --git a/docs/gpt-template-seo/12-GPT模板导入模板改造交付清单-2026-04-18.md b/docs/gpt-template-seo/12-GPT模板导入模板改造交付清单-2026-04-18.md new file mode 100644 index 0000000..1112492 --- /dev/null +++ b/docs/gpt-template-seo/12-GPT模板导入模板改造交付清单-2026-04-18.md @@ -0,0 +1,96 @@ +# 12 GPT模板导入模板改造交付清单 2026-04-18 + +这份文档只做一件事: + +把本轮“导入模板中文化 + 自动样本补全”改造的交付边界一次性钉住。 + +## 一、本轮已交付 + +### 代码 + +1. [DomainBatchImportTemplateHelper.php](/www/wwwroot/diff-maccms/SEONexus/code/app/common/helper/DomainBatchImportTemplateHelper.php) +2. [DomainSupplyBatchTemplateHelper.php](/www/wwwroot/diff-maccms/SEONexus/code/app/common/helper/DomainSupplyBatchTemplateHelper.php) +3. [DomainSupplyBatchHelper.php](/www/wwwroot/diff-maccms/SEONexus/code/app/common/helper/DomainSupplyBatchHelper.php) +4. [Site.php](/www/wwwroot/diff-maccms/SEONexus/code/app/admin/controller/Site.php) + +### 文档 + +1. [10-GPT模板运营导入模板使用说明-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/10-GPT模板运营导入模板使用说明-2026-04-18.md) +2. [11-GPT模板导入模板中文化与自动样本补全交接-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/11-GPT模板导入模板中文化与自动样本补全交接-2026-04-18.md) +3. [README.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/README.md) + +## 二、本轮实现结果 + +### 基础站点导入模板 + +已实现: + +1. 中文表头 +2. 中文 `notes` +3. 主填写区前置 +4. 上传兼容中文表头 +5. 上传兼容旧英文字段表头 + +### GPT 供料模板 + +已实现: + +1. 中文表头 +2. 中文 `notes` +3. 主填写区前置 +4. 样本字段单独收口 +5. 样本字段留空时优先自动补全 + +### 后台接口返回 + +已实现: + +1. `template_summary` +2. `template_display_headers` +3. `template_operator_notice` +4. `template_filling_guidance` + +这意味着后端已经把“模板怎么展示、怎么提示运营”所需的数据准备好了。 + +## 三、当前统一口径 + +以后统一按这句话对外: + +> 基础站点导入模板先做建站基础信息,GPT 供料模板再做启动包和供料;GPT 供料模板中的样本字段通常可留空,系统会优先自动补全,只有自动发现失败时才人工补填。 + +## 四、本轮不再继续做的内容 + +本轮先不继续做这些: + +1. 不继续改 GPT 前台模板 +2. 不继续扩写更多老模板导入逻辑 +3. 不继续做前端页面 UI 大改 +4. 不继续把所有后台页面提示都改成统一样式 + +原因: + +1. 当前最大问题已经不是 GPT 前台模板结构 +2. 本轮目标是把导入模板和执行入口收口 +3. 继续扩大会把当前改动边界搞散 + +## 五、如果下一个人继续接 + +优先顺序建议: + +1. 先确认下载接口返回的 `template_operator_notice` 和 `template_filling_guidance` 是否已在前端页面展示 +2. 若页面还没展示,再补页面提示文案 +3. 若运营仍觉得模板复杂,再继续缩减主填写区,但不要破坏自动样本补全逻辑 + +## 六、验收标准 + +本轮到此为止,满足以下标准即可视为收口完成: + +1. 运营下载模板看到的是中文表头 +2. 运营上传中文表头模板不会报错 +3. GPT 供料模板样本字段留空时能自动补全则自动补全 +4. 自动补全失败时才提示缺字段 +5. 后续接手人能通过文档快速知道怎么继续 + +## 一句话交付结论 + +> 这轮改造已经把“模板难懂、字段太技术化、样本看起来必须手填”的问题,收口成了“中文模板 + 中文说明 + 中文上传兼容 + 样本自动补全优先”的可交付状态。 diff --git a/docs/gpt-template-seo/README.md b/docs/gpt-template-seo/README.md index 6000897..1428b83 100644 --- a/docs/gpt-template-seo/README.md +++ b/docs/gpt-template-seo/README.md @@ -32,6 +32,14 @@ 3. [07-GPT模板交接说明-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/07-GPT模板交接说明-2026-04-18.md) 4. [08-GPT模板其它服务器更新交接-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/08-GPT模板其它服务器更新交接-2026-04-18.md) 作用:给“原本也是 GPT 模板、现在要对齐到最新版”的其它服务器接手使用。 +5. [09-GPT模板次日蜘蛛日志判读模板-2026-04-19.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/09-GPT模板次日蜘蛛日志判读模板-2026-04-19.md) + 作用:给第二天继续看蜘蛛日志时直接套用,快速判断进度是否符合预期。 +6. [10-GPT模板运营导入模板使用说明-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/10-GPT模板运营导入模板使用说明-2026-04-18.md) + 作用:告诉运营和接手同学“基础导入模板”和“GPT 供料模板”分别怎么用、哪些字段通常可留空。 +7. [11-GPT模板导入模板中文化与自动样本补全交接-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/11-GPT模板导入模板中文化与自动样本补全交接-2026-04-18.md) + 作用:给后续 Codex 或技术接手同学,快速说明这轮模板中文化与自动补全到底改了什么。 +8. [12-GPT模板导入模板改造交付清单-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/12-GPT模板导入模板改造交付清单-2026-04-18.md) + 作用:把本轮模板改造的交付边界、统一口径和验收标准一次性钉住。 首批 4 站的执行材料也全部放在本目录: