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

@@ -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 [

View File

@@ -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'] ?? [])),
];
}

View File

@@ -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://<host>。',
'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 [