feat: improve gpt import templates and seo docs

This commit is contained in:
Your Name
2026-04-18 14:11:21 +08:00
parent 5cb11b4476
commit a87a4a41cc
13 changed files with 1397 additions and 145 deletions

View File

@@ -1230,9 +1230,14 @@ class Site extends BaseController
$Spreadsheet = IOFactory::load($FilePath); $Spreadsheet = IOFactory::load($FilePath);
$Sheet = $Spreadsheet->getActiveSheet(); $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'); $strRunRoot = DomainImportProbeRunHelper::createRunRoot($this->domainImportRunRoot(), 'excel_import');
$strUploadedExcelPath = $strRunRoot . '/uploaded.' . $strExt; $strUploadedExcelPath = $strRunRoot . '/uploaded.' . $strExt;
@@ -1252,34 +1257,46 @@ class Site extends BaseController
foreach ($arrData as $arrRows) { foreach ($arrData as $arrRows) {
$intRowsTotal++; $intRowsTotal++;
$strStrategyProfile = $this->normalizeStrategyProfile($arrRows[19] ?? DomainModel::SEO_STRATEGY_PROFILE_STANDARD); $arrRowAssoc = [];
$strTkdMode = DomainModel::normalizeTkdMode((string)($arrRows[20] ?? '')); foreach ($arrHeaders as $index => $strHeader) {
$strTkdProvider = DomainModel::normalizeTkdProvider((string)($arrRows[21] ?? DomainModel::TKD_PROVIDER_LOCAL)); 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 = [ $arrDomain = [
'd_domain' => $this->normalizeStoredDomain($arrRows[0] ?? '', $arrRows[14] ?? DomainModel::MATCH_TYPE_EXACT), 'd_domain' => $this->normalizeStoredDomain($arrRowAssoc['d_domain'] ?? '', $arrRowAssoc['d_match_type'] ?? DomainModel::MATCH_TYPE_EXACT),
't_id' => $arrRows[1], 't_id' => $arrRowAssoc['t_id'] ?? 1,
'd_name' => $arrRows[2], 'd_name' => $arrRowAssoc['d_name'] ?? '',
'd_keywords' => $arrRows[3], 'd_keywords' => $arrRowAssoc['d_keywords'] ?? '',
'd_index_title' => $arrRows[4], 'd_index_title' => $arrRowAssoc['d_index_title'] ?? '',
'd_index_keywords' => $arrRows[5], 'd_index_keywords' => $arrRowAssoc['d_index_keywords'] ?? '',
'd_index_description' => $arrRows[6], 'd_index_description' => $arrRowAssoc['d_index_description'] ?? '',
'd_description' => $arrRows[7], 'd_description' => $arrRowAssoc['d_description'] ?? '',
'd_statis' => $arrRows[8], 'd_statis' => $arrRowAssoc['d_statis'] ?? '',
'd_logo_type' => (int)$arrRows[9], 'd_logo_type' => (int)($arrRowAssoc['d_logo_type'] ?? 0),
'd_text_logo' => $arrRows[10], 'd_text_logo' => $arrRowAssoc['d_text_logo'] ?? '',
'd_img_logo' => $arrRows[11], 'd_img_logo' => $arrRowAssoc['d_img_logo'] ?? '',
'd_content_encode' => $arrRows[12], 'd_content_encode' => $arrRowAssoc['d_content_encode'] ?? 0,
'info_id' => $arrRows[13], 'info_id' => $arrRowAssoc['info_id'] ?? 0,
'd_match_type' => $this->normalizeMatchType($arrRows[14] ?? DomainModel::MATCH_TYPE_EXACT), 'd_match_type' => $this->normalizeMatchType($arrRowAssoc['d_match_type'] ?? DomainModel::MATCH_TYPE_EXACT),
'd_parent_domain' => $this->normalizeParentDomain($arrRows[15] ?? ''), 'd_parent_domain' => $this->normalizeParentDomain($arrRowAssoc['d_parent_domain'] ?? ''),
'd_seed_scope' => $this->normalizeSeedScope($arrRows[16] ?? DomainModel::SEED_SCOPE_DOMAIN), 'd_seed_scope' => $this->normalizeSeedScope($arrRowAssoc['d_seed_scope'] ?? DomainModel::SEED_SCOPE_DOMAIN),
'd_baidu_token' => $arrRows[17] ?? null, '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( $arrDomain['d_seo_cfg']['tkd'] = array_merge(
(array)($arrDomain['d_seo_cfg']['tkd'] ?? []), (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, 'provider' => $strTkdProvider,
] ]
); );
@@ -1385,11 +1402,15 @@ class Site extends BaseController
try { try {
$arrTemplate = DomainBatchImportTemplateHelper::writeTemplate($this->domainImportTemplateRoot()); $arrTemplate = DomainBatchImportTemplateHelper::writeTemplate($this->domainImportTemplateRoot());
$strRelativePath = str_replace($this->bootstrapCodeRoot() . '/public/', '', $arrTemplate['file_path']); $strRelativePath = str_replace($this->bootstrapCodeRoot() . '/public/', '', $arrTemplate['file_path']);
$arrSummary = (array)($arrTemplate['summary'] ?? []);
return $this->success([ return $this->success([
'excel_template_path' => $strRelativePath, 'excel_template_path' => $strRelativePath,
'template_file_name' => $arrTemplate['file_name'], '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) { } catch (\Throwable $Throwable) {
return $this->error('9999', $Throwable->getMessage()); return $this->error('9999', $Throwable->getMessage());
@@ -1401,11 +1422,15 @@ class Site extends BaseController
try { try {
$arrTemplate = DomainSupplyBatchTemplateHelper::writeTemplate($this->domainSupplyTemplateRoot()); $arrTemplate = DomainSupplyBatchTemplateHelper::writeTemplate($this->domainSupplyTemplateRoot());
$strRelativePath = $this->publicRelativePath((string)$arrTemplate['file_path']); $strRelativePath = $this->publicRelativePath((string)$arrTemplate['file_path']);
$arrSummary = (array)($arrTemplate['summary'] ?? []);
return $this->success([ return $this->success([
'excel_template_path' => $strRelativePath, 'excel_template_path' => $strRelativePath,
'template_file_name' => $arrTemplate['file_name'], '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) { } catch (\Throwable $Throwable) {
return $this->error('9999', '生成一体化供给模板失败:' . $Throwable->getMessage()); return $this->error('9999', '生成一体化供给模板失败:' . $Throwable->getMessage());

View File

@@ -10,6 +10,84 @@ use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
class DomainBatchImportTemplateHelper 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( public static function writeWorkbook(
string $filePath, string $filePath,
string $sheetTitle, string $sheetTitle,
@@ -65,25 +143,25 @@ class DomainBatchImportTemplateHelper
{ {
return [ return [
'd_domain', 'd_domain',
't_id',
'd_name', 'd_name',
'd_keywords', 't_id',
'd_index_title', 'd_index_title',
'd_index_keywords', 'd_index_keywords',
'd_index_description', 'd_index_description',
'strategy_profile',
'd_keywords',
'd_description', 'd_description',
'd_baidu_token',
'd_match_type',
'd_parent_domain',
'd_seed_scope',
'd_statis', 'd_statis',
'd_logo_type', 'd_logo_type',
'd_text_logo', 'd_text_logo',
'd_img_logo', 'd_img_logo',
'd_content_encode', 'd_content_encode',
'info_id', 'info_id',
'd_match_type',
'd_parent_domain',
'd_seed_scope',
'd_baidu_token',
'd_seo_cfg', 'd_seo_cfg',
'strategy_profile',
'tkd_mode', 'tkd_mode',
'tkd_provider', 'tkd_provider',
]; ];
@@ -92,28 +170,28 @@ class DomainBatchImportTemplateHelper
public static function getColumnNotes(): array public static function getColumnNotes(): array
{ {
return [ return [
'd_domain' => '必填站点域名wildcard 模式可填 *.example.com 或 example.com,由后端归一化。', 'd_domain' => '必填。填写站点正式域名,不要带 https:// 。示例jingxifa.com。只有做泛域名时才填 *.example.com。',
't_id' => '必填,模板 ID。', 'd_name' => '必填。填写站点前台展示名称,会用于页头、页脚和基础 SEO 文案。示例:鲸溪发影视。',
'd_name' => '必填,站点名称。', 't_id' => '通常直接填 1。只有明确知道要换模板时才改不确定就不要改。',
'd_keywords' => '可选,站点关键词。', 'd_index_title' => '建议填写。首页给搜索引擎看的标题,尽量自然,不要堆词。建议 28 到 40 字。',
'd_index_title' => '可选,首页标题。', 'd_index_keywords' => '建议填写。首页核心关键词,多个词用逗号分隔。示例:短剧推荐,热播短剧,高清短剧。',
'd_index_keywords' => '可选,首页关键词。', 'd_index_description' => '建议填写。首页一句话简介,建议 40 到 80 字,写自然一点。',
'd_index_description' => '可选,首页描述。', 'strategy_profile' => '通常不用改,默认 standard。只有明确知道要做流量型、扩展型站点时才改成 traffic 或 expand。',
'd_description' => '可选,站点描述。', 'd_keywords' => '可选。不填也能跑;如要补充站点级关键词,可填在这里。',
'd_statis' => '可选,统计代码。', 'd_description' => '可选。不填时系统会按站点名称自动兜底一版简介。',
'd_logo_type' => '必填0 或 1。', 'd_baidu_token' => '可选。只有需要百度主动推送时才填写。',
'd_text_logo' => '可选,文字 LOGO。', 'd_match_type' => '可选。默认 exact。普通单域名站保持默认即可只有泛域名场景才需要改。',
'd_img_logo' => '可选,图片 LOGO。', 'd_parent_domain' => '只有 wildcard 泛域名场景才需要填根域名。普通单域名站留空即可。',
'd_content_encode' => '必填,通常填 0。', 'd_seed_scope' => '可选。默认 domain。普通单域名站不用改只有特殊 host 级种子策略才改。',
'info_id' => '可选,通常填 0。', 'd_statis' => '可选。统计代码区,运营通常不用填。',
'd_match_type' => '可选exact 或 wildcard。', 'd_logo_type' => '通常填 0。普通站点不用改如果是纯图片 LOGO 再按技术要求调整。',
'd_parent_domain' => 'wildcard 模式建议填写根域名。', 'd_text_logo' => '可选。填写前台显示的文字 LOGO不填时可按站点名称兜底。',
'd_seed_scope' => '可选domain 或 host。', 'd_img_logo' => '可选。只有要使用图片 LOGO 时才填写。',
'd_baidu_token' => '可选,百度推送 token。', 'd_content_encode' => '通常填 0。普通运营不用改。',
'd_seo_cfg' => '可选JSON会在 strategy_profile 默认值基础上再做覆盖。', 'info_id' => '通常填 0。普通运营不用改。',
'strategy_profile' => '可选standard / traffic / expand / closed。', 'd_seo_cfg' => '可选。高级配置区,通常给技术同学使用。运营不需要填写;只有明确要覆盖默认 SEO 策略时再填 JSON。',
'tkd_mode' => '可选1/auto_generate=全自动生成2/ai_optimize=导入 TKD 后本地/AI 优化3/force_import=强制使用导入值。', 'tkd_mode' => '可选。一般不用填。留空时系统会根据首页三要素是否导入自动选择模式。',
'tkd_provider' => '可选local 或 openai当前默认 local本地优化已生效后续可接 OpenAI API。', 'tkd_provider' => '可选。一般保持 local不需要运营修改。',
]; ];
} }
@@ -121,52 +199,52 @@ class DomainBatchImportTemplateHelper
{ {
return [ return [
[ [
'demo-example-com', 'd_domain' => 'demo-example-com',
1, 'd_name' => 'Demo Example',
'Demo Example', 't_id' => 1,
'Demo Example,影视推荐', 'd_index_title' => 'Demo Example 影视内容推荐',
'Demo Example 影视内容推荐', 'd_index_keywords' => 'Demo Example,影视推荐',
'Demo Example,影视推荐', 'd_index_description' => 'Demo Example 内容整理与推荐',
'Demo Example 内容整理与推荐。', 'strategy_profile' => 'standard',
'Demo Example 内容整理与推荐', 'd_keywords' => 'Demo Example,影视推荐',
'', 'd_description' => 'Demo Example 内容整理与推荐。',
0, 'd_baidu_token' => '',
'Demo Example', 'd_match_type' => 'exact',
'', 'd_parent_domain' => '',
0, 'd_seed_scope' => 'domain',
0, 'd_statis' => '',
'exact', 'd_logo_type' => 0,
'', 'd_text_logo' => 'Demo Example',
'domain', 'd_img_logo' => '',
'', 'd_content_encode' => 0,
'', 'info_id' => 0,
'standard', 'd_seo_cfg' => '',
'1', 'tkd_mode' => '1',
'local', 'tkd_provider' => 'local',
], ],
[ [
'*.traffic-example.com', 'd_domain' => '*.traffic-example.com',
1, 'd_name' => 'Traffic Example',
'Traffic Example', 't_id' => 1,
'Traffic Example,热门推荐', 'd_index_title' => 'Traffic Example 热门内容推荐',
'Traffic Example 热门内容推荐', 'd_index_keywords' => 'Traffic Example,热门推荐',
'Traffic Example,热门推荐', 'd_index_description' => 'Traffic Example 热门内容整理与推荐',
'Traffic Example 热门内容整理与推荐。', 'strategy_profile' => 'traffic',
'Traffic Example 热门内容整理与推荐', 'd_keywords' => 'Traffic Example,热门推荐',
'', 'd_description' => 'Traffic Example 热门内容整理与推荐。',
0, 'd_baidu_token' => '',
'Traffic Example', 'd_match_type' => 'wildcard',
'', 'd_parent_domain' => 'traffic-example.com',
0, 'd_seed_scope' => 'host',
0, 'd_statis' => '',
'wildcard', 'd_logo_type' => 0,
'traffic-example.com', 'd_text_logo' => 'Traffic Example',
'host', 'd_img_logo' => '',
'', 'd_content_encode' => 0,
'{"forge":{"route_group":"film"},"structured_data":{"organization_name":"Traffic Example Studio"}}', 'info_id' => 0,
'traffic', 'd_seo_cfg' => '{"forge":{"route_group":"film"},"structured_data":{"organization_name":"Traffic Example Studio"}}',
'2', 'tkd_mode' => '2',
'local', 'tkd_provider' => 'local',
], ],
]; ];
} }
@@ -175,8 +253,45 @@ class DomainBatchImportTemplateHelper
{ {
return [ return [
'headers' => self::getHeaders(), 'headers' => self::getHeaders(),
'display_headers' => self::getDisplayHeaders(),
'header_labels' => self::getHeaderLabels(),
'column_notes' => self::getColumnNotes(), 'column_notes' => self::getColumnNotes(),
'example_rows' => self::getExampleRows(), '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_strategy_profiles' => ['standard', 'traffic', 'expand', 'closed'],
'supported_match_types' => ['exact', 'wildcard'], 'supported_match_types' => ['exact', 'wildcard'],
'supported_seed_scopes' => ['domain', 'host'], 'supported_seed_scopes' => ['domain', 'host'],
@@ -189,19 +304,28 @@ class DomainBatchImportTemplateHelper
$fileName = 'site-domain-import-template.xlsx'; $fileName = 'site-domain-import-template.xlsx';
$filePath = $outputRoot . '/' . $fileName; $filePath = $outputRoot . '/' . $fileName;
$summary = self::buildSummary(); $summary = self::buildSummary();
$headerLabels = self::getHeaderLabels();
$rows = array_map( $rows = array_map(
static function (array $row): array { static function (array $row) use ($headerLabels): array {
return array_combine(self::getHeaders(), $row) ?: []; $displayRow = [];
foreach (self::getHeaders() as $field) {
$displayRow[$headerLabels[$field] ?? $field] = (string)($row[$field] ?? '');
}
return $displayRow;
}, },
self::getExampleRows() self::getExampleRows()
); );
$displayNotes = [];
foreach (self::getColumnNotes() as $field => $note) {
$displayNotes[$headerLabels[$field] ?? $field] = $note;
}
self::writeWorkbook( self::writeWorkbook(
$filePath, $filePath,
'site-domain-import', 'site-domain-import',
self::getHeaders(), self::getDisplayHeaders(),
$rows, $rows,
self::getColumnNotes() $displayNotes
); );
return [ return [

View File

@@ -8,6 +8,92 @@ use PhpOffice\PhpSpreadsheet\IOFactory;
class DomainSupplyBatchHelper 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 protected static function publicRelativeToAbsolutePath(string $path): string
{ {
$path = str_replace('\\', '/', trim($path)); $path = str_replace('\\', '/', trim($path));
@@ -122,12 +208,28 @@ class DomainSupplyBatchHelper
throw new \RuntimeException('Replay template rows are empty: ' . $summaryPath); 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( DomainBatchImportTemplateHelper::writeWorkbook(
$outputPath, $outputPath,
'domain-supply-batch', 'domain-supply-batch',
DomainSupplyBatchTemplateHelper::getHeaders(), DomainSupplyBatchTemplateHelper::getDisplayHeaders(),
$rows, $displayRows,
DomainSupplyBatchTemplateHelper::getColumnNotes() $displayNotes
); );
return $outputPath; return $outputPath;
@@ -164,7 +266,9 @@ class DomainSupplyBatchHelper
return []; 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 = []; $normalized = [];
foreach ($rows as $row) { foreach ($rows as $row) {
$assoc = []; $assoc = [];
@@ -362,9 +466,19 @@ class DomainSupplyBatchHelper
continue; continue;
} }
$autoSample = self::fillAutoDiscoveredSample($row);
$row = (array)($autoSample['row'] ?? $row);
$missing = self::validateRow($row); $missing = self::validateRow($row);
$status = empty($missing) ? 'ready' : 'failed'; $status = empty($missing) ? 'ready' : 'failed';
$message = empty($missing) ? 'prepared' : ('missing:' . implode(',', $missing)); $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); $siteRow = self::buildSiteImportRow($row);
$bootstrapRow = self::buildBootstrapBatchRow($row); $bootstrapRow = self::buildBootstrapBatchRow($row);
@@ -385,6 +499,9 @@ class DomainSupplyBatchHelper
'site_domain' => (string)($siteRow['d_domain'] ?? ''), 'site_domain' => (string)($siteRow['d_domain'] ?? ''),
'bundle_host' => (string)($bootstrapRow['host'] ?? ''), 'bundle_host' => (string)($bootstrapRow['host'] ?? ''),
'missing_fields' => $missing, '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'] ?? [])),
]; ];
} }

View File

@@ -12,61 +12,145 @@ class DomainSupplyBatchTemplateHelper
{ {
return [ return [
'host', 'host',
'template_id',
'strategy_profile',
'match_type',
'parent_domain',
'seed_scope',
'site_name', 'site_name',
'site_keywords', 'template_id',
'site_description',
'site_index_title', 'site_index_title',
'site_index_keywords', 'site_index_keywords',
'site_index_description', 'site_index_description',
'baidu_token', 'strategy_profile',
'seo_cfg_json',
'detail_id', 'detail_id',
'detail_slug', 'detail_slug',
'search_keyword', 'search_keyword',
'category_parent', 'category_parent',
'category_child', 'category_child',
'site_keywords',
'site_description',
'baidu_token',
'match_type',
'parent_domain',
'seed_scope',
'play_type', 'play_type',
'play_index', 'play_index',
'forge_index', 'forge_index',
'rank_slug', 'rank_slug',
'base_url', 'base_url',
'bundle_name', '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 public static function getColumnNotes(): array
{ {
return [ return [
'host' => '必填,域名或 wildcard 域名;会同时用于站点导入和 bundle 生成。', 'host' => '必填。填写站点正式域名,不要带 https:// 。示例jingxifa.com。只有做泛域名时才填 *.example.com。',
'template_id' => '必填,站点模板 ID默认 1。', 'site_name' => '必填。填写站点前台展示名称,会用于页头、页脚和基础 SEO 文案。示例:鲸溪发影视。',
'strategy_profile' => '可选standard / traffic / expand / closed默认 standard。', 'template_id' => '通常直接填 1。只有确认要换模板时才改不确定就不要改。',
'match_type' => '可选exact 或 wildcard默认 exact。', 'site_index_title' => '建议填写。首页给搜索引擎看的标题,尽量自然,不要堆词。建议 28 到 40 字。',
'parent_domain' => 'wildcard 模式建议填写根域名。', 'site_index_keywords' => '建议填写。首页核心关键词,多个词用逗号分隔。示例:短剧推荐,热播短剧,高清短剧。',
'seed_scope' => '可选domain 或 host默认 domain。', 'site_index_description' => '建议填写。首页一句话简介,建议 40 到 80 字,写自然一点。',
'site_name' => '可选,站点名称。', 'strategy_profile' => '通常不用改,默认 standard。只有明确知道要做流量型、扩展型站点时才改成 traffic 或 expand。',
'site_keywords' => '可选,站点关键词。', 'detail_id' => '通常不用手填。站点部署成功后,系统会优先通过自动样本发现,从当前站点可用内容里自动提取。只有域名还没部署成功、当前只是试跑库、或自动发现失败时,才人工补填真实详情视频 ID。',
'site_description' => '可选,站点描述。', 'detail_slug' => '通常不用手填。会和详情样本 ID 一起自动获取。若人工填写,必须和详情样本 ID 对应,不能乱配。',
'site_index_title' => '可选,首页标题;不填时导入链按站点名称推导。', 'search_keyword' => '通常不用手填。系统会优先从站点名称、视频 SEO 词、视频标题里自动挑一个可搜索关键词。只有自动发现失败时,再人工填写一个能搜出结果的词。',
'site_index_keywords' => '可选,首页关键词;不填时导入链按站点关键词推导。', 'category_parent' => '通常不用手填。系统会从自动样本视频所属分类里提取一级分类 slug。只有自动发现失败时再人工补填。示例dian-ying。',
'site_index_description' => '可选,首页描述;不填时导入链按站点描述推导。', 'category_child' => '通常不用手填。系统会从自动样本视频所属分类里提取二级分类 slug。只有自动发现失败时再人工补填。示例ju-qing-pian。',
'baidu_token' => '可选,百度推送 token。', 'site_keywords' => '可选。不填也能跑;如要补充站点级关键词,可填在这里。',
'seo_cfg_json' => '可选JSON会在 strategy_profile 默认值基础上继续覆盖。', 'site_description' => '可选。不填时系统会按站点名称自动兜底一版简介。',
'detail_id' => '必填,启动包详情样本视频 ID。', 'baidu_token' => '可选。只有需要百度主动推送时才填写。',
'detail_slug' => '必填,启动包详情样本 slug。', 'match_type' => '可选。默认 exact。普通单域名站保持默认即可只有泛域名场景才需要改。',
'search_keyword' => '必填,启动包搜索页样本关键词。', 'parent_domain' => '只有 wildcard 泛域名场景才需要填根域名。普通单域名站留空即可。',
'category_parent' => '必填,父分类 slug。', 'seed_scope' => '可选。默认 domain。普通单域名站不用改只有特殊 host 级种子策略才改。',
'category_child' => '必填,子分类 slug。', 'play_type' => '可选。默认 douban。自动样本发现成功时通常也能自动带出不需要运营关心。',
'play_type' => '可选默认 douban。', 'play_index' => '可选默认 1。通常不用改。',
'play_index' => '可选默认 1。', 'forge_index' => '可选默认 1。通常不用改。',
'forge_index' => '可选默认 1。', 'rank_slug' => '可选默认 daily。通常不用改。',
'rank_slug' => '可选默认 daily。', 'base_url' => '可选默认自动按 https://域名 生成。只有特殊环境才手填。',
'base_url' => '可选,默认 https://<host>。', 'bundle_name' => '可选。一般留空,系统会按域名自动生成 bundle 目录名。',
'bundle_name' => '可选,自定义 bundle 目录名。', 'seo_cfg_json' => '可选。高级配置区,通常给技术同学使用。运营不需要填写;只有明确要覆盖默认 SEO 策略时再填 JSON。',
]; ];
} }
@@ -134,8 +218,50 @@ class DomainSupplyBatchTemplateHelper
{ {
return [ return [
'headers' => self::getHeaders(), 'headers' => self::getHeaders(),
'display_headers' => self::getDisplayHeaders(),
'header_labels' => self::getHeaderLabels(),
'column_notes' => self::getColumnNotes(), 'column_notes' => self::getColumnNotes(),
'example_rows' => self::getExampleRows(), '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' => [ 'supported_strategy_profiles' => [
DomainModel::SEO_STRATEGY_PROFILE_STANDARD, DomainModel::SEO_STRATEGY_PROFILE_STANDARD,
DomainModel::SEO_STRATEGY_PROFILE_TRAFFIC, DomainModel::SEO_STRATEGY_PROFILE_TRAFFIC,
@@ -165,12 +291,29 @@ class DomainSupplyBatchTemplateHelper
$fileName = 'domain-supply-batch-template.xlsx'; $fileName = 'domain-supply-batch-template.xlsx';
$filePath = $outputRoot . '/' . $fileName; $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( DomainBatchImportTemplateHelper::writeWorkbook(
$filePath, $filePath,
'domain-supply-batch', 'domain-supply-batch',
self::getHeaders(), $displayHeaders,
self::getExampleRows(), $displayRows,
self::getColumnNotes() $displayNotes
); );
return [ return [

View File

@@ -299,3 +299,73 @@
2. 再补 B 组 9 站 2. 再补 B 组 9 站
3. 同时单独盯缓存滞后 4 站 3. 同时单独盯缓存滞后 4 站
4. 一旦出现首批已抓 / 已收录样本,立即回写主线文档作为样板 4. 一旦出现首批已抓 / 已收录样本,立即回写主线文档作为样板
## 11. 2026-04-18 当日进度判断
### 今日总判断
今天这轮蜘蛛日志,整体符合“第一阶段起量期预期”,但还没有达到理想推进速度。
更准确地说:
1. 不是偏离预期
2. 不是已经明显爆发
3. 而是“方向正确、已有 deep page 正向信号,但 sitemap 与 search 链路还没真正跑起来”
### 今日符合预期的部分
1. 多个站已经出现真实 `detail / play` 命中
- `jingxifa.com`
- `codohealth.com`
- `lgyz.net`
- `jpjdxs.com`
- `gxhongzhuang.com`
- `leici1940.com`
- `oronorent.com`
2. 当前主问题不再是“大面积基础链路失效”
3. 旧硬 `404` 没有成为这轮主旋律
4. 说明这批 GPT 模板修复已经开始被蜘蛛侧真实吃到
### 今日未达理想预期的部分
1. `sitemap` 命中仍偏弱
2. 搜索页真实回访仍不够,很多还是测试探针
3. 仍有一批站停在首页层 / robots 层
4. 缓存滞后站还没有完全收口
### 当前阶段结论
如果按 7 天主线节奏看:
1. Day 1 到 Day 2 的目标,当前基本达标
2. Day 3 到 Day 4 想看到的 `sitemap -> 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`

View File

@@ -147,3 +147,62 @@
- GPT 模板线今天没有新增结构性故障,重点仍在缓存收口和深页回访扩张。 - 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 和搜索链路快速放量的理想状态。

View File

@@ -84,6 +84,37 @@
2. GPT 专属目录负责做节奏管理、跟踪板和阶段汇总 2. GPT 专属目录负责做节奏管理、跟踪板和阶段汇总
3. 每日复盘优先写到 [03-GPT模板每日复盘模板-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/03-GPT模板每日复盘模板-2026-04-18.md) 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 ### P0

View File

@@ -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 模板线今天新增有效蜘蛛信号有限,整体低于理想节奏,下一步要优先排除缓存未收口和入口链路偏弱的问题。

View File

@@ -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. 只有自动发现失败,才让人工介入

View File

@@ -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 模板线的导入模板从“技术字段驱动”改成“运营可填写驱动”,并把样本字段从“看起来必须手填”收口成“系统优先自动补全、失败才人工补填”的新口径。

View File

@@ -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. 后续接手人能通过文档快速知道怎么继续
## 一句话交付结论
> 这轮改造已经把“模板难懂、字段太技术化、样本看起来必须手填”的问题,收口成了“中文模板 + 中文说明 + 中文上传兼容 + 样本自动补全优先”的可交付状态。

View File

@@ -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) 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) 4. [08-GPT模板其它服务器更新交接-2026-04-18.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/08-GPT模板其它服务器更新交接-2026-04-18.md)
作用:给“原本也是 GPT 模板、现在要对齐到最新版”的其它服务器接手使用。 作用:给“原本也是 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 站的执行材料也全部放在本目录: 首批 4 站的执行材料也全部放在本目录: