From 0096805a49e683f926f18f79e8e9d4b30e96d612 Mon Sep 17 00:00:00 2001 From: www Date: Tue, 26 May 2026 17:02:57 +0800 Subject: [PATCH] debug --- code/.gitignore | 1 + code/app/admin/config/router.php | 4 + code/app/admin/controller/ServerNode.php | 210 ++++++++++++++++++ ...omainExternalSeoSnapshotAnalysisHelper.php | 111 ++++++--- code/app/common/helper/JsBuilder.php | 49 +++- code/app/model/ServerNodeModel.php | 18 ++ ...0260422010000_create_server_node_table.php | 88 ++++++++ code/database/schema/mysql-schema.dump | 34 +++ .../site-domain-import-template.xlsx | Bin 10718 -> 10719 bytes .../static/js/modules/player/dplayer.init.js | 82 +++++-- doc/Install.md | 4 +- ...¡æ¿5月12蜘蛛日志与7æ—¥åŽç»­è¿›åº¦-2026-05-12.md | 166 ++++++++++++++ ...5月12åˆç›˜èœ˜è››æ—¥å¿—与7日进度续报-2026-05-12.md | 117 ++++++++++ docs/gpt-template-seo/README.md | 4 + 14 files changed, 830 insertions(+), 58 deletions(-) create mode 100644 code/app/admin/controller/ServerNode.php create mode 100644 code/app/model/ServerNodeModel.php create mode 100644 code/database/migrations/20260422010000_create_server_node_table.php create mode 100644 docs/gpt-template-seo/23-GPT模æ¿5月12蜘蛛日志与7æ—¥åŽç»­è¿›åº¦-2026-05-12.md create mode 100644 docs/gpt-template-seo/24-GPT模æ¿5月12åˆç›˜èœ˜è››æ—¥å¿—与7日进度续报-2026-05-12.md diff --git a/code/.gitignore b/code/.gitignore index 1578b60..bd4b959 100644 --- a/code/.gitignore +++ b/code/.gitignore @@ -21,4 +21,5 @@ public/static/favicon/generated/* /app/public/_admin_templates/video-metadata-missing-task-pool/* /app/public/_admin_templates/video-metadata-missing-workbench/* /data/seo_copy_published/* +/data/seo_resource/keyword_feedback/* /storage/* diff --git a/code/app/admin/config/router.php b/code/app/admin/config/router.php index 9110381..4aa0088 100644 --- a/code/app/admin/config/router.php +++ b/code/app/admin/config/router.php @@ -7,6 +7,7 @@ use app\admin\controller\Index; use app\admin\controller\Domain; use app\admin\controller\SeoSupply; use app\admin\controller\Novel; +use app\admin\controller\ServerNode; use app\admin\controller\Site; use app\admin\controller\SystemConfig; use app\admin\controller\Video; @@ -32,6 +33,9 @@ Route::group("/system", function () { Route::post("/config/save", [SystemConfig::class, "saveSystemConfig"])->name("SystemConfig@saveSystemConfig"); Route::post("/config/seo-ai-rules/reset", [SystemConfig::class, "resetSeoAiRules"])->name("SystemConfig@resetSeoAiRules"); Route::get("/storage/file", [SystemConfig::class, "previewStorageFile"])->name("SystemConfig@previewStorageFile"); + Route::get("/node/list", [ServerNode::class, "getServerNodeList"])->name("ServerNode@getServerNodeList"); + Route::post("/node/save", [ServerNode::class, "saveServerNode"])->name("ServerNode@saveServerNode"); + Route::post("/node/del", [ServerNode::class, "delServerNode"])->name("ServerNode@delServerNode"); Route::get("/cjpz/list", [SystemConfig::class, "getCaiJiPeiZhiList"])->name("SystemConfig@getCaiJiPeiZhiList"); Route::post("/cjpz/save", [SystemConfig::class, "saveCaiJiPeiZhi"])->name("SystemConfig@saveCaiJiPeiZhi"); diff --git a/code/app/admin/controller/ServerNode.php b/code/app/admin/controller/ServerNode.php new file mode 100644 index 0000000..1c6863d --- /dev/null +++ b/code/app/admin/controller/ServerNode.php @@ -0,0 +1,210 @@ + (int)$Request->get('page', 1), + 'limit' => (int)$Request->get('limit', 10), + 'key' => trim((string)$Request->get('key', '')), + ]; + + $this->validate($arrData, [ + 'page' => 'require|number', + 'limit' => 'require|number', + ]); + + $ServerNodeModel = ServerNodeModel::alias('sn') + ->order('sn.sn_status', 'desc') + ->order('sn.updated_at', 'desc') + ->order('sn.sn_id', 'desc'); + + if ($arrData['key'] !== '') { + $strKey = $arrData['key']; + $ServerNodeModel->where(function ($query) use ($strKey) { + $query->whereOr([ + ['sn.sn_name', 'like', '%' . $strKey . '%'], + ['sn.sn_code', 'like', '%' . $strKey . '%'], + ['sn.sn_host', 'like', '%' . $strKey . '%'], + ['sn.sn_username', 'like', '%' . $strKey . '%'], + ['sn.sn_region', 'like', '%' . $strKey . '%'], + ['sn.sn_purpose', 'like', '%' . $strKey . '%'], + ['sn.sn_project_dir', 'like', '%' . $strKey . '%'], + ['sn.sn_log_dir', 'like', '%' . $strKey . '%'], + ['sn.sn_nginx_dir', 'like', '%' . $strKey . '%'], + ]); + }); + } + + $Paginate = $ServerNodeModel->paginate([ + 'list_rows' => $arrData['limit'], + 'page' => $arrData['page'], + ]); + + $arrItems = []; + foreach ($Paginate->items() as $arrItem) { + $arrItems[] = $this->serializeServerNodeRow($arrItem); + } + + return $this->success([ + 'items' => $arrItems, + 'total' => $Paginate->total(), + 'current_page' => $Paginate->currentPage(), + 'total_pages' => $Paginate->lastPage(), + ]); + } + + /** + * ä¿å­˜å‰ç«¯æœåŠ¡å™¨èŠ‚ç‚¹ + */ + public function saveServerNode(Request $Request, ?AdminUserModel $AdminUserModel): Response + { + $arrData = [ + 'sn_id' => $Request->post('sn_id'), + 'sn_name' => trim((string)$Request->post('sn_name', '')), + 'sn_code' => $this->normalizeNullableString($Request->post('sn_code')), + 'sn_host' => trim((string)$Request->post('sn_host', '')), + 'sn_port' => (int)$Request->post('sn_port', 22), + 'sn_username' => trim((string)$Request->post('sn_username', '')), + 'sn_auth_type' => trim((string)$Request->post('sn_auth_type', 'password')), + 'sn_password' => trim((string)$Request->post('sn_password', '')), + 'sn_private_key' => trim((string)$Request->post('sn_private_key', '')), + 'sn_passphrase' => trim((string)$Request->post('sn_passphrase', '')), + 'sn_region' => $this->normalizeNullableString($Request->post('sn_region')), + 'sn_purpose' => $this->normalizeNullableString($Request->post('sn_purpose')), + 'sn_project_dir' => $this->normalizeNullableString($Request->post('sn_project_dir')), + 'sn_log_dir' => $this->normalizeNullableString($Request->post('sn_log_dir')), + 'sn_nginx_dir' => $this->normalizeNullableString($Request->post('sn_nginx_dir')), + 'sn_note' => $this->normalizeNullableString($Request->post('sn_note')), + 'sn_status' => (int)$Request->post('sn_status', 1), + ]; + + $this->validate($arrData, [ + 'sn_name' => 'require|max:255', + 'sn_host' => 'require|max:255', + 'sn_port' => 'require|number|between:1,65535', + 'sn_username' => 'require|max:100', + 'sn_auth_type' => 'require|in:password,private_key', + 'sn_status' => 'require|in:0,1', + ]); + + $boolIsCreate = empty($arrData['sn_id']); + if ($boolIsCreate) { + $ServerNodeModel = new ServerNodeModel(); + } else { + $ServerNodeModel = ServerNodeModel::where('sn_id', (int)$arrData['sn_id'])->find(); + if (empty($ServerNodeModel)) { + return $this->error('9993', 'æœåŠ¡å™¨èŠ‚ç‚¹ä¸å­˜åœ¨'); + } + } + + if ($arrData['sn_code'] !== null) { + $DuplicateModel = ServerNodeModel::where('sn_code', $arrData['sn_code']); + if (!$boolIsCreate) { + $DuplicateModel->where('sn_id', '<>', (int)$arrData['sn_id']); + } + if ($DuplicateModel->find() !== null) { + return $this->error('9993', '节点编ç å·²å­˜åœ¨ï¼Œè¯·æ›´æ¢åŽé‡è¯•'); + } + } + + $strCurrentAuthType = trim((string)($ServerNodeModel->sn_auth_type ?? '')); + $boolHasExistingPassword = trim((string)($ServerNodeModel->sn_password ?? '')) !== ''; + $boolHasExistingPrivateKey = trim((string)($ServerNodeModel->sn_private_key ?? '')) !== ''; + + if ($arrData['sn_auth_type'] === 'password') { + if ($arrData['sn_password'] === '') { + if ($boolIsCreate || $strCurrentAuthType !== 'password' || !$boolHasExistingPassword) { + return $this->error('9993', '当å‰ç™»å½•æ–¹å¼ä¸ºå¯†ç ï¼Œè¯·å¡«å†™ SSH 密ç '); + } + unset($arrData['sn_password']); + } + + $arrData['sn_private_key'] = ''; + $arrData['sn_passphrase'] = ''; + } else { + if ($arrData['sn_private_key'] === '') { + if ($boolIsCreate || $strCurrentAuthType !== 'private_key' || !$boolHasExistingPrivateKey) { + return $this->error('9993', '当å‰ç™»å½•æ–¹å¼ä¸ºç§é’¥ï¼Œè¯·å¡«å†™ SSH ç§é’¥'); + } + unset($arrData['sn_private_key']); + if ($arrData['sn_passphrase'] === '') { + unset($arrData['sn_passphrase']); + } + } + + $arrData['sn_password'] = ''; + } + + $ServerNodeModel->fill($arrData); + $ServerNodeModel->save(); + + return $this->success([ + 'sn_id' => (int)$ServerNodeModel->sn_id, + ], $boolIsCreate ? 'æœåŠ¡å™¨èŠ‚ç‚¹æ–°å¢žæˆåŠŸ' : 'æœåŠ¡å™¨èŠ‚ç‚¹ä¿å­˜æˆåŠŸ'); + } + + /** + * 删除å‰ç«¯æœåŠ¡å™¨èŠ‚ç‚¹ + */ + public function delServerNode(Request $Request, ?AdminUserModel $AdminUserModel): Response + { + $arrData = [ + 'sn_id' => trim((string)$Request->post('sn_id', '')), + ]; + + $this->validate($arrData, [ + 'sn_id' => 'require', + ]); + + $arrIds = array_values(array_unique(array_filter(array_map('intval', explode(',', $arrData['sn_id'])), static function (int $intId): bool { + return $intId > 0; + }))); + + if ($arrIds === []) { + return $this->error('9993', '未识别到å¯åˆ é™¤çš„æœåŠ¡å™¨èŠ‚ç‚¹'); + } + + ServerNodeModel::whereIn('sn_id', $arrIds)->delete(); + + return $this->success(); + } + + /** + * å±è”½åˆ—表接å£é‡Œçš„æ•æ„Ÿ SSH 凭è¯ï¼Œåªè¿”回é…置状æ€ã€‚ + */ + protected function serializeServerNodeRow($arrItem): array + { + $arrRow = is_array($arrItem) ? $arrItem : $arrItem->toArray(); + $arrRow['sn_port'] = (int)($arrRow['sn_port'] ?? 22); + $arrRow['sn_status'] = (int)($arrRow['sn_status'] ?? 1); + $arrRow['sn_password_configured'] = trim((string)($arrRow['sn_password'] ?? '')) !== ''; + $arrRow['sn_private_key_configured'] = trim((string)($arrRow['sn_private_key'] ?? '')) !== ''; + $arrRow['sn_passphrase_configured'] = trim((string)($arrRow['sn_passphrase'] ?? '')) !== ''; + + unset($arrRow['sn_password'], $arrRow['sn_private_key'], $arrRow['sn_passphrase']); + + return $arrRow; + } + + protected function normalizeNullableString($value): ?string + { + $strValue = trim((string)$value); + + return $strValue === '' ? null : $strValue; + } +} diff --git a/code/app/common/helper/DomainExternalSeoSnapshotAnalysisHelper.php b/code/app/common/helper/DomainExternalSeoSnapshotAnalysisHelper.php index 901f0ba..98d94af 100644 --- a/code/app/common/helper/DomainExternalSeoSnapshotAnalysisHelper.php +++ b/code/app/common/helper/DomainExternalSeoSnapshotAnalysisHelper.php @@ -16,7 +16,7 @@ class DomainExternalSeoSnapshotAnalysisHelper $arrProviders = []; $arrHosts = []; - $arrLatestIndexByHost = []; + $arrLatestSignalsByHost = []; $intKeywordSnapshots = (int)SeoExternalSnapshotModel::where('metric_date', '>=', $strStartDate) ->where('scope', 'keyword') ->count(); @@ -28,9 +28,10 @@ class DomainExternalSeoSnapshotAnalysisHelper self::eachSnapshotRow( SeoExternalSnapshotModel::where('metric_date', '>=', $strStartDate) ->where('scope', 'host'), - ['id', 'provider', 'host', 'scope', 'metric_date', 'status', 'indexed_status', 'baidu_pc_ip_range', 'baidu_mobile_ip_range', 'pc_keyword_count', 'mobile_keyword_count', 'queried_at'], - static function (array $arrItem) use (&$arrProviders, &$arrHosts, &$arrLatestIndexByHost, &$strLatestMetricDate): void { + ['id', 'provider', 'snapshot_type', 'host', 'scope', 'metric_date', 'status', 'indexed_status', 'result_count_text', 'baidu_pc_ip_range', 'baidu_mobile_ip_range', 'pc_keyword_count', 'mobile_keyword_count', 'queried_at'], + static function (array $arrItem) use (&$arrProviders, &$arrHosts, &$arrLatestSignalsByHost, &$strLatestMetricDate): void { $strProvider = trim((string)($arrItem['provider'] ?? '')); + $strSnapshotType = trim((string)($arrItem['snapshot_type'] ?? '')); $strHost = trim((string)($arrItem['host'] ?? '')); $strMetricDate = trim((string)($arrItem['metric_date'] ?? '')); $strStatus = trim((string)($arrItem['status'] ?? '')); @@ -48,34 +49,33 @@ class DomainExternalSeoSnapshotAnalysisHelper } if ($strHost !== '') { - $strHostKey = $strHost; - $arrCurrent = $arrLatestIndexByHost[$strHostKey] ?? null; - $boolShouldReplace = false; - if (!$arrCurrent) { - $boolShouldReplace = true; - } else { - $strCurrentDate = (string)($arrCurrent['metric_date'] ?? ''); - $intCurrentQueriedAt = (int)($arrCurrent['queried_at'] ?? 0); - if (strcmp($strCurrentDate, $strMetricDate) < 0) { - $boolShouldReplace = true; - } elseif ($strCurrentDate === $strMetricDate && $intCurrentQueriedAt < $intQueriedAt) { - $boolShouldReplace = true; - } + $arrCandidate = [ + 'host' => $strHost, + 'provider' => $strProvider, + 'snapshot_type' => $strSnapshotType, + 'metric_date' => $strMetricDate, + 'queried_at' => $intQueriedAt, + 'status' => $strStatus, + 'indexed_status' => $strIndexedStatus, + 'result_count_text' => trim((string)($arrItem['result_count_text'] ?? '')), + 'baidu_pc_ip_range' => trim((string)($arrItem['baidu_pc_ip_range'] ?? '')), + 'baidu_mobile_ip_range' => trim((string)($arrItem['baidu_mobile_ip_range'] ?? '')), + 'pc_keyword_count' => (int)($arrItem['pc_keyword_count'] ?? 0), + 'mobile_keyword_count' => (int)($arrItem['mobile_keyword_count'] ?? 0), + ]; + + if (self::shouldReplaceSnapshot($arrLatestSignalsByHost[$strHost]['display'] ?? null, $strMetricDate, $intQueriedAt)) { + $arrLatestSignalsByHost[$strHost]['display'] = $arrCandidate; } - if ($boolShouldReplace) { - $arrLatestIndexByHost[$strHostKey] = [ - 'host' => $strHost, - 'provider' => $strProvider, - 'metric_date' => $strMetricDate, - 'queried_at' => $intQueriedAt, - 'status' => $strStatus, - 'indexed_status' => $strIndexedStatus, - 'baidu_pc_ip_range' => trim((string)($arrItem['baidu_pc_ip_range'] ?? '')), - 'baidu_mobile_ip_range' => trim((string)($arrItem['baidu_mobile_ip_range'] ?? '')), - 'pc_keyword_count' => (int)($arrItem['pc_keyword_count'] ?? 0), - 'mobile_keyword_count' => (int)($arrItem['mobile_keyword_count'] ?? 0), - ]; + if ($strSnapshotType === 'site_query' + && self::shouldReplaceSnapshot($arrLatestSignalsByHost[$strHost]['index'] ?? null, $strMetricDate, $intQueriedAt)) { + $arrLatestSignalsByHost[$strHost]['index'] = $arrCandidate; + } + + if ($strSnapshotType === 'aizhan_summary' + && self::shouldReplaceSnapshot($arrLatestSignalsByHost[$strHost]['keyword'] ?? null, $strMetricDate, $intQueriedAt)) { + $arrLatestSignalsByHost[$strHost]['keyword'] = $arrCandidate; } } }, @@ -85,13 +85,44 @@ class DomainExternalSeoSnapshotAnalysisHelper $intIndexedLikeCount = 0; $intKeywordReadyCount = 0; $intTrafficReadyCount = 0; - $arrHostRows = array_values($arrLatestIndexByHost); + $arrHostRows = []; + + foreach ($arrLatestSignalsByHost as $strHost => $arrSignals) { + $arrDisplay = (array)($arrSignals['display'] ?? []); + $arrIndex = (array)($arrSignals['index'] ?? []); + $arrKeyword = (array)($arrSignals['keyword'] ?? []); + $arrProvidersForHost = []; + + foreach ([$arrIndex, $arrKeyword, $arrDisplay] as $arrSignalRow) { + $strSignalProvider = trim((string)($arrSignalRow['provider'] ?? '')); + if ($strSignalProvider !== '') { + $arrProvidersForHost[$strSignalProvider] = true; + } + } + + $arrHostRows[] = [ + 'host' => $strHost, + 'provider' => implode(',', array_keys($arrProvidersForHost)), + 'index_provider' => (string)($arrIndex['provider'] ?? ''), + 'keyword_provider' => (string)($arrKeyword['provider'] ?? ''), + 'metric_date' => max((string)($arrDisplay['metric_date'] ?? ''), (string)($arrIndex['metric_date'] ?? ''), (string)($arrKeyword['metric_date'] ?? '')), + 'queried_at' => max((int)($arrDisplay['queried_at'] ?? 0), (int)($arrIndex['queried_at'] ?? 0), (int)($arrKeyword['queried_at'] ?? 0)), + 'status' => (string)($arrDisplay['status'] ?? ''), + 'indexed_status' => (string)($arrIndex['indexed_status'] ?? ''), + 'result_count_text' => (string)($arrIndex['result_count_text'] ?? ''), + 'baidu_pc_ip_range' => (string)($arrKeyword['baidu_pc_ip_range'] ?? ''), + 'baidu_mobile_ip_range' => (string)($arrKeyword['baidu_mobile_ip_range'] ?? ''), + 'pc_keyword_count' => (int)($arrKeyword['pc_keyword_count'] ?? 0), + 'mobile_keyword_count' => (int)($arrKeyword['mobile_keyword_count'] ?? 0), + ]; + } foreach ($arrHostRows as &$arrRow) { - $boolIndexedLike = (string)($arrRow['indexed_status'] ?? '') === 'indexed_like'; + $boolIndexedLike = self::normalizeIndexState($arrRow) === 'indexed_like'; $boolKeywordReady = (int)($arrRow['pc_keyword_count'] ?? 0) > 0 || (int)($arrRow['mobile_keyword_count'] ?? 0) > 0; $boolTrafficReady = !self::isZeroRange((string)($arrRow['baidu_pc_ip_range'] ?? '')) || !self::isZeroRange((string)($arrRow['baidu_mobile_ip_range'] ?? '')); + $arrRow['indexed_like'] = $boolIndexedLike; $arrRow['keyword_ready'] = $boolKeywordReady; $arrRow['traffic_ready'] = $boolTrafficReady; @@ -113,6 +144,11 @@ class DomainExternalSeoSnapshotAnalysisHelper if ($intLeftKeyword !== $intRightKeyword) { return $intRightKeyword <=> $intLeftKeyword; } + $intLeftIndexed = !empty($arrLeft['indexed_like']) ? 1 : 0; + $intRightIndexed = !empty($arrRight['indexed_like']) ? 1 : 0; + if ($intLeftIndexed !== $intRightIndexed) { + return $intRightIndexed <=> $intLeftIndexed; + } return strcmp((string)($arrLeft['host'] ?? ''), (string)($arrRight['host'] ?? '')); }); @@ -402,6 +438,19 @@ class DomainExternalSeoSnapshotAnalysisHelper return $strRange === '' || $strRange === '0 ~ 0' || $strRange === '0~0'; } + protected static function shouldReplaceSnapshot(?array $arrCurrent, string $strMetricDate, int $intQueriedAt): bool + { + if (!$arrCurrent) { + return true; + } + + $strCurrentDate = (string)($arrCurrent['metric_date'] ?? ''); + $intCurrentQueriedAt = (int)($arrCurrent['queried_at'] ?? 0); + + return strcmp($strCurrentDate, $strMetricDate) < 0 + || ($strCurrentDate === $strMetricDate && $intCurrentQueriedAt < $intQueriedAt); + } + protected static function normalizeIndexState(array $arrRow): string { $strIndexedStatus = trim((string)($arrRow['indexed_status'] ?? '')); diff --git a/code/app/common/helper/JsBuilder.php b/code/app/common/helper/JsBuilder.php index 7fa58e6..43de770 100644 --- a/code/app/common/helper/JsBuilder.php +++ b/code/app/common/helper/JsBuilder.php @@ -27,14 +27,6 @@ class JsBuilder @mkdir($targetDir, 0755, true); } - // 输出文件å(域å专属缓存) - $targetFile = $targetDir . "{$staticHash}.js"; - - // 如果已ç»ç”Ÿæˆè¿‡ï¼Œç›´æŽ¥è¿”回 - if (file_exists($targetFile)) { - return "/static/js/compiled/{$staticHash}.js"; - } - // ä½ å¯ä»¥åƒ CSS 一样在这里挂一个“全站基础 JS†// 注æ„:如果没有这个文件,就别写入,å¦åˆ™ä¼šè¢« skip $listJs = [ @@ -46,9 +38,46 @@ class JsBuilder $finalJsFiles = array_merge($listJs, $jsFiles); $finalJsFiles = array_values(array_unique(array_filter($finalJsFiles))); + $signatureParts = [ + 'static_hash' => $staticHash, + 'dom_prefix' => $domPrefix, + 'builder' => is_file(__FILE__) ? sha1_file(__FILE__) : (string)@filemtime(__FILE__), + 'files' => [], + ]; + + // 如果 compiled å·²å­˜åœ¨ä¸”æ¯”æ‰€æœ‰æºæ–‡ä»¶éƒ½æ–°ï¼Œç›´æŽ¥è¿”回;å¦åˆ™è‡ªåЍé‡å»ºã€‚ + $intLatestSourceTime = @filemtime(__FILE__) ?: 0; + foreach ($finalJsFiles as $file) { + $file = ltrim((string)$file, '/'); + $path = $baseDir . $file; + if (is_file($path)) { + $intLatestSourceTime = max($intLatestSourceTime, (int)@filemtime($path)); + $signatureParts['files'][] = [ + 'file' => $file, + 'mtime' => (int)@filemtime($path), + 'size' => (int)@filesize($path), + 'sha1' => sha1_file($path), + ]; + } else { + $signatureParts['files'][] = [ + 'file' => $file, + 'missing' => true, + ]; + } + } + + // å‰ç«¯ä»£ç†çš„陿€ç¼“存按 path 命中,query version 无法兜底;文件åå¿…é¡»è·Ÿéšæºç å˜åŒ–。 + $sourceHash = substr(sha1((string)json_encode($signatureParts, JSON_UNESCAPED_SLASHES)), 0, 10); + $targetName = "{$staticHash}-{$sourceHash}.js"; + $targetFile = $targetDir . $targetName; + + if (file_exists($targetFile) && (int)@filemtime($targetFile) >= $intLatestSourceTime) { + return "/static/js/compiled/{$targetName}"; + } + // åˆå¹¶ JS 文件内容 $allJs = ""; - $allJs .= "/*! compiled: {$staticHash}.js */\n"; + $allJs .= "/*! compiled: {$targetName} */\n"; $allJs .= "(function(){\n'use strict';\n"; foreach ($finalJsFiles as $file) { @@ -82,6 +111,6 @@ class JsBuilder file_put_contents($targetFile, $allJs); // 返回å‰ç«¯å¯è®¿é—®è·¯å¾„ - return "/static/js/compiled/{$staticHash}.js"; + return "/static/js/compiled/{$targetName}"; } } diff --git a/code/app/model/ServerNodeModel.php b/code/app/model/ServerNodeModel.php new file mode 100644 index 0000000..481f8d8 --- /dev/null +++ b/code/app/model/ServerNodeModel.php @@ -0,0 +1,18 @@ +hasTable('server_node')) { + $this->table('server_node', ['id' => false]) + ->addColumn(Column::integer('sn_id')->setUnsigned()->setIdentity(true)->setComment('节点ID')) + ->addColumn(Column::string('sn_name', 255)->setDefault('')->setComment('节点åç§°')) + ->addColumn(Column::string('sn_code', 100)->setNullable(true)->setComment('节点编ç ')) + ->addColumn(Column::string('sn_host', 255)->setDefault('')->setComment('SSH 主机/IP')) + ->addColumn(Column::smallInteger('sn_port')->setUnsigned()->setDefault(22)->setComment('SSH 端å£')) + ->addColumn(Column::string('sn_username', 100)->setDefault('')->setComment('SSH 用户å')) + ->addColumn(Column::string('sn_auth_type', 20)->setDefault('password')->setComment('è®¤è¯æ–¹å¼: password/private_key')) + ->addColumn(Column::text('sn_password')->setNullable(true)->setComment('SSH 密ç ')) + ->addColumn(Column::mediumText('sn_private_key')->setNullable(true)->setComment('SSH ç§é’¥')) + ->addColumn(Column::text('sn_passphrase')->setNullable(true)->setComment('ç§é’¥å£ä»¤')) + ->addColumn(Column::string('sn_region', 100)->setNullable(true)->setComment('区域/环境')) + ->addColumn(Column::string('sn_purpose', 255)->setNullable(true)->setComment('机器用途')) + ->addColumn(Column::string('sn_project_dir', 500)->setNullable(true)->setComment('项目目录')) + ->addColumn(Column::string('sn_log_dir', 500)->setNullable(true)->setComment('日志目录')) + ->addColumn(Column::string('sn_nginx_dir', 500)->setNullable(true)->setComment('Nginx é…置目录')) + ->addColumn(Column::text('sn_note')->setNullable(true)->setComment('备注')) + ->addColumn(Column::tinyInteger('sn_status')->setUnsigned()->setDefault(1)->setComment('状æ€:1å¯ç”¨,0ç¦ç”¨')) + ->addTimestamps('created_at', 'updated_at') + ->addIndex(['sn_id'], ['type' => 'primary']) + ->addIndex(['sn_code'], ['unique' => true]) + ->addIndex(['sn_status']) + ->addIndex(['sn_host']) + ->setComment('å‰ç«¯æœåŠ¡å™¨èŠ‚ç‚¹') + ->create(); + + return; + } + + $Table = $this->table('server_node'); + + $this->addColumnIfMissing($Table, 'sn_name', Column::string('sn_name', 255)->setDefault('')->setComment('节点åç§°')); + $this->addColumnIfMissing($Table, 'sn_code', Column::string('sn_code', 100)->setNullable(true)->setComment('节点编ç ')); + $this->addColumnIfMissing($Table, 'sn_host', Column::string('sn_host', 255)->setDefault('')->setComment('SSH 主机/IP')); + $this->addColumnIfMissing($Table, 'sn_port', Column::smallInteger('sn_port')->setUnsigned()->setDefault(22)->setComment('SSH 端å£')); + $this->addColumnIfMissing($Table, 'sn_username', Column::string('sn_username', 100)->setDefault('')->setComment('SSH 用户å')); + $this->addColumnIfMissing($Table, 'sn_auth_type', Column::string('sn_auth_type', 20)->setDefault('password')->setComment('è®¤è¯æ–¹å¼: password/private_key')); + $this->addColumnIfMissing($Table, 'sn_password', Column::text('sn_password')->setNullable(true)->setComment('SSH 密ç ')); + $this->addColumnIfMissing($Table, 'sn_private_key', Column::mediumText('sn_private_key')->setNullable(true)->setComment('SSH ç§é’¥')); + $this->addColumnIfMissing($Table, 'sn_passphrase', Column::text('sn_passphrase')->setNullable(true)->setComment('ç§é’¥å£ä»¤')); + $this->addColumnIfMissing($Table, 'sn_region', Column::string('sn_region', 100)->setNullable(true)->setComment('区域/环境')); + $this->addColumnIfMissing($Table, 'sn_purpose', Column::string('sn_purpose', 255)->setNullable(true)->setComment('机器用途')); + $this->addColumnIfMissing($Table, 'sn_project_dir', Column::string('sn_project_dir', 500)->setNullable(true)->setComment('项目目录')); + $this->addColumnIfMissing($Table, 'sn_log_dir', Column::string('sn_log_dir', 500)->setNullable(true)->setComment('日志目录')); + $this->addColumnIfMissing($Table, 'sn_nginx_dir', Column::string('sn_nginx_dir', 500)->setNullable(true)->setComment('Nginx é…置目录')); + $this->addColumnIfMissing($Table, 'sn_note', Column::text('sn_note')->setNullable(true)->setComment('备注')); + $this->addColumnIfMissing($Table, 'sn_status', Column::tinyInteger('sn_status')->setUnsigned()->setDefault(1)->setComment('状æ€:1å¯ç”¨,0ç¦ç”¨')); + $this->addColumnIfMissing($Table, 'created_at', Column::timestamp('created_at')->setDefault('CURRENT_TIMESTAMP')); + $this->addColumnIfMissing($Table, 'updated_at', Column::timestamp('updated_at')->setDefault('CURRENT_TIMESTAMP')->setUpdate('CURRENT_TIMESTAMP')); + + $this->addIndexIfMissing($Table, ['sn_code'], ['unique' => true]); + $this->addIndexIfMissing($Table, ['sn_status']); + $this->addIndexIfMissing($Table, ['sn_host']); + + $Table->update(); + } + + public function down() + { + if ($this->hasTable('server_node')) { + $this->table('server_node')->drop()->save(); + } + } + + protected function addColumnIfMissing($table, string $columnName, Column $column): void + { + if (!$table->hasColumn($columnName)) { + $table->addColumn($column); + } + } + + protected function addIndexIfMissing($table, array $columns, array $options = []): void + { + if (!$table->hasIndex($columns)) { + $table->addIndex($columns, $options); + } + } +} diff --git a/code/database/schema/mysql-schema.dump b/code/database/schema/mysql-schema.dump index 4d712f0..41512df 100644 --- a/code/database/schema/mysql-schema.dump +++ b/code/database/schema/mysql-schema.dump @@ -179,6 +179,40 @@ CREATE TABLE `plan_task` ( ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `server_node` +-- + +DROP TABLE IF EXISTS `server_node`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `server_node` ( + `sn_id` int unsigned NOT NULL AUTO_INCREMENT, + `sn_name` varchar(255) NOT NULL DEFAULT '' COMMENT '节点åç§°', + `sn_code` varchar(100) DEFAULT NULL COMMENT '节点编ç ', + `sn_host` varchar(255) NOT NULL DEFAULT '' COMMENT 'SSH 主机/IP', + `sn_port` smallint unsigned NOT NULL DEFAULT '22' COMMENT 'SSH 端å£', + `sn_username` varchar(100) NOT NULL DEFAULT '' COMMENT 'SSH 用户å', + `sn_auth_type` varchar(20) NOT NULL DEFAULT 'password' COMMENT 'è®¤è¯æ–¹å¼: password/private_key', + `sn_password` text COMMENT 'SSH 密ç ', + `sn_private_key` mediumtext COMMENT 'SSH ç§é’¥', + `sn_passphrase` text COMMENT 'ç§é’¥å£ä»¤', + `sn_region` varchar(100) DEFAULT NULL COMMENT '区域/环境', + `sn_purpose` varchar(255) DEFAULT NULL COMMENT '机器用途', + `sn_project_dir` varchar(500) DEFAULT NULL COMMENT '项目目录', + `sn_log_dir` varchar(500) DEFAULT NULL COMMENT '日志目录', + `sn_nginx_dir` varchar(500) DEFAULT NULL COMMENT 'Nginx é…置目录', + `sn_note` text COMMENT '备注', + `sn_status` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '状æ€:1å¯ç”¨,0ç¦ç”¨', + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`sn_id`) USING BTREE, + UNIQUE KEY `sn_code` (`sn_code`) USING BTREE, + KEY `sn_status` (`sn_status`) USING BTREE, + KEY `sn_host` (`sn_host`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='å‰ç«¯æœåŠ¡å™¨èŠ‚ç‚¹'; +/*!40101 SET character_set_client = @saved_cs_client */; + -- -- Table structure for table `subject_fomart` -- 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 index 0cd7caef13eb2850433b109aaa3eb5fd9959586b..f9c7cacb6d4ed6ffc1a3b1115527d9d49efaeec3 100644 GIT binary patch delta 774 zcmcZ?d_R~sz?+#xgn@yBgCU}PJR!IHV|-mU*q~j+w1L=rdt}kLJQ_OI4$B>cPuXI@{w(CS|{v3UU+Lu>pctC z$&&W(`(k41uZMnOJ*U0+xr(&mqy{lJt-Z4*KYyw;chV8%>yyIW4$s;0;AClK7{_ex z#}6(_a`TD)P-)n5zIE&T9K%mz?&ykBTP$ z);ZkUQvbMK`Doc$IcB4J&c{#x8d*Po`siEUPKBp4de;Y)&D?a;X!euomurly(=X52 znAf9xVqR2!{xA75_s-Y_hqw-9ebk<;F8yvw9dEh-SF?lg)y%;3OEp(8ZK!UI z6ziqx5ZQI=(#$UG43iVsMJ7K`=K-sH4-_-uWtbemD++WbBNHgfJd|N{hVir*BsRBq$QX9>zn6utNN)-$Y481_C m48#zi0HmHz4%9SK1f@~jYL=)nOg^BhGWmq209&FONG|}|1}{1Q delta 802 zcmcZ~d@qI3}`8Hlvr|10Vpe>Q`?WkTv1mtOx_GZ-#jzZJJ&(^lW=(s%c7mm0WKe$KSC zULOD6HnZ?^X!uEWi}gKm6LOAtFhxnM{8Hw4ZFPHzpHP2{cbp_I_oXIv^QWPTIeIFGKaArB@qT(T1{b1dsHTYjZm_R5`pCnxsee=}huP;9~S~pAUZm4R*lva_Q1qUwfC|djdmWkEQ*QJj26|w!I-+zcm-Pf3~ z{q6l9_dNBsXtUvx&O??%9(fWu9x$$OnV%8YFVaPeCp*ItCKh1%sN|qY?t9V z1@)}j9WU!|D65t=a%pp{KDyp#n%N!AzkCeIvU~5cs#ZLla`c1D>5RF5Id0YO+1q^J zo{Y6d=%I-QRaS(^jd-?D;Z%aNCl8_WojaAX2A zrb&H*L|eB^3JX}|lfr*y5M%O2RjtW#suQ5>Jz%;(%?`p>GXvAF)Lg-|k-9ZfoR_IX zWY?=pGdr_0OipAM;Sca;WMXE6C!on6fO5vX43h(SMYYl7Vk&oh+$qY$z;Kq0fq|C+ z1{8n{m&tw_GGH5XHKZ9kCU 432 -> 292 -> 221 -> 74 åˆç›˜ã€‚ +2. URL è§„èŒƒåŒ–åŽ 301 在 5 月 9 日至 5 月 11 日从 198 é™åˆ° 112,但 5 月 12 æ—¥åˆç›˜å·²åˆ° 51,按全日投影ä»å¯èƒ½æŽ¥è¿‘ 90 至 100,说明 www/non-www 和历å²å…¥å£è¿˜åœ¨æ¶ˆè€—蜘蛛。 +3. 详情+播放页 5 月 11 日为 64,5 月 12 æ—¥åˆç›˜åªæœ‰ 24,深抓没有形æˆç¨³å®šæ”¾å¤§ã€‚ + +## Baidu Site 与爱站进度 + +| 日期 | Baidu indexed_like | Baidu failed | Baidu unknown | 爱站快照站点 | 爱站 PC è¯ | +| --- | ---: | ---: | ---: | ---: | ---: | +| 2026-05-06 | 1 | 28 | 3 | 32 | 2 | +| 2026-05-07 | 3 | 14 | 15 | 16 | 2 | +| 2026-05-08 | 6 | 7 | 19 | 16 | 0 | +| 2026-05-09 | 5 | 4 | 23 | 15 | 0 | +| 2026-05-10 | 4 | 7 | 21 | 17 | 0 | +| 2026-05-11 | 5 | 9 | 18 | 17 | 0 | +| 2026-05-12 | 3 | 18 | 11 | 13 | 0 | + +5 月 12 æ—¥ `indexed_like` 域å:`cnzhenbang.com`ã€`gulenyuzlercocukevi.com`ã€`gz-yxsw.com`。 +5 月 6 日至 5 月 7 日爱站 PC è¯åªå‡ºçŽ°åœ¨ `glae.cc`ï¼Œå„ 2 个;5 月 8 日以åŽå½“剿—¥çˆ±ç«™ PC è¯ä¸º 0。30 æ—¥åé¦ˆé‡Œä»æœ‰åކå²è¯ï¼Œä½†ä¸èƒ½æŒ‰å½“å‰å‡ºè¯è®¡ç®—。 + +## 5 月 12 æ—¥åˆç›˜åŸŸå层表现 + +按去掉 `www.` åŽçš„主域èšåˆï¼Œå–æ ¸å¿ƒèœ˜è››å‰ 15: + +| 域å | 核心蜘蛛 | 百度 | å¤´æ¡ | æœç‹— | 首页 | 详情+播放 | 200 | 301 | 403 | 444 | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| cnzhenbang.com | 24 | 0 | 2 | 22 | 2 | 0 | 0 | 11 | 13 | 0 | +| lcdchq.com | 23 | 17 | 6 | 0 | 17 | 0 | 13 | 7 | 3 | 0 | +| jingxifa.com | 14 | 12 | 0 | 2 | 2 | 12 | 6 | 7 | 1 | 0 | +| sjzyunyang.com | 14 | 12 | 0 | 2 | 2 | 12 | 6 | 7 | 1 | 0 | +| vikau.com | 11 | 2 | 7 | 2 | 3 | 0 | 0 | 1 | 4 | 6 | +| hyjssb.com | 10 | 5 | 5 | 0 | 5 | 0 | 5 | 1 | 1 | 3 | +| sdxtwnc.com | 10 | 10 | 0 | 0 | 10 | 0 | 6 | 4 | 0 | 0 | +| nblssy.com | 7 | 1 | 6 | 0 | 4 | 0 | 1 | 3 | 3 | 0 | +| jxxgygy.com | 6 | 0 | 0 | 6 | 4 | 0 | 0 | 3 | 3 | 0 | +| lmjcg.com | 6 | 0 | 2 | 4 | 2 | 0 | 0 | 2 | 4 | 0 | +| stsgf.com | 6 | 3 | 3 | 0 | 3 | 0 | 3 | 0 | 0 | 3 | +| codohealth.com | 5 | 4 | 1 | 0 | 0 | 0 | 2 | 2 | 1 | 0 | +| lgyz.net | 5 | 1 | 4 | 0 | 1 | 0 | 1 | 0 | 4 | 0 | +| visitsumenep.com | 4 | 0 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 4 | +| caosheninan.com | 3 | 3 | 0 | 0 | 3 | 0 | 2 | 1 | 0 | 0 | + +域å判断: + +1. `jingxifa.com` å’Œ `sjzyunyang.com` 是今天少数有详情+播放深抓的域åï¼Œå„ 12 次,优先ä¿ç•™åœ¨æ”¶å½•转è¯è§‚察层。 +2. `sdxtwnc.com` 仿œ‰ç™¾åº¦é¦–页抓å–,但 `site:` å¿«ç…§ 5 月 12 日为 `failed`,需è¦å…ˆæŸ¥ä¸ºä»€ä¹ˆâ€œæœ‰æŠ“å–但快照ä¸ç¨³â€ã€‚ +3. `cnzhenbang.com` 虽然 5 月 12 日为 `indexed_like`,但åˆç›˜æ ¸å¿ƒèœ˜è››é‡Œæ²¡æœ‰ç™¾åº¦ï¼Œä¸” 301/403 å æ»¡ï¼Œä¸é€‚åˆç›´æŽ¥å½“转è¯ä¸»åŠ›ã€‚ +4. `vikau.com` 403/444 ä»å高,åªåšå¼‚常修å¤ï¼Œä¸è¿›å…¥æ”¾å¤§ã€‚ +5. `codohealth.com` 5 月 11 日有 `indexed_like`,但 5 月 12 æ—¥åˆç›˜æ·±æŠ“为 0,先观察,ä¸åŠ èµ„æºã€‚ + +## leici1940.com å•ç«™å¤æŸ¥ + +| 日期 | 核心蜘蛛 | 百度 | å¤´æ¡ | æœç‹— | 详情+播放 | 200 | 301 | 403 | 444 | Baidu site | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | +| 2026-05-06 | 11 | 7 | 4 | 0 | 0 | 3 | 6 | 2 | 0 | failed | +| 2026-05-07 | 5 | 3 | 2 | 0 | 0 | 2 | 2 | 1 | 0 | failed | +| 2026-05-08 | 23 | 17 | 6 | 0 | 0 | 11 | 9 | 3 | 0 | unknown | +| 2026-05-09 | 8 | 4 | 4 | 0 | 0 | 2 | 4 | 2 | 0 | indexed_like | +| 2026-05-10 | 23 | 12 | 11 | 0 | 0 | 6 | 10 | 6 | 1 | unknown | +| 2026-05-11 | 14 | 12 | 2 | 0 | 0 | 6 | 7 | 1 | 0 | unknown | +| 2026-05-12 | 3 | 0 | 3 | 0 | 0 | 0 | 2 | 1 | 0 | unknown | + +结论: + +1. `leici1940.com` çš„ watch 页颿’­æ”¾å™¨æŠ¥é”™ä¼šå½±å“用户侧体验和页é¢è´¨é‡ä¿¡å·ï¼Œä½†èœ˜è››å±‚颿›´å¤§çš„问题是:7 日没有形æˆè¯¦æƒ…/播放深抓,5 月 12 æ—¥åˆç›˜ä¹Ÿæ²¡æœ‰ç™¾åº¦èœ˜è››ã€‚ +2. 该站 301 å æ¯”å高,且 5 月 12 æ—¥ 3 次核心蜘蛛中 2 次 301ã€1 次 403,没有有效 200 è½ç‚¹ã€‚ +3. å‰ç«¯ JS 文件已ç»é‡‡ç”¨å¸¦ç­¾å的新文件å绕开路径缓存,但线上普通入å£ä»å¯èƒ½å‘½ä¸­å‰ç½®ç¼“存旧 HTMLã€‚éœ€è¦æ¸…å‰ç½®ç¼“å­˜åŽï¼Œå†ç”¨åŒä¸€ watch URL 夿Ÿ¥ console 是å¦è¿˜æŠ¥ `classList` 空指针。 + +## 下一步执行å£å¾„ + +P0:ä¿åކå²è¯ï¼Œä¸æ‰©è¯ã€‚ +ä¿ç•™ `glae.cc`ã€`zbsv3.com`ã€`sdxhtgcl.com`ã€`sdxtwnc.com` 的历å²è¯åé¦ˆé“¾è·¯ï¼Œä½†æŒ‰å½“å‰æ—¥çˆ±ç«™ 0 è¯å¤„ç†ï¼Œä¸åšæ–°å¢žå…³é”®è¯é“ºé‡ã€‚ + +P1:收录转è¯åªçœ‹æœ‰æ·±æŠ“的域å。 +`jingxifa.com`ã€`sjzyunyang.com` 今天有详情+播放抓å–,优先检查详情页和播放页 200 è½ç‚¹ã€æ ‡é¢˜å·®å¼‚ã€canonicalã€sitemap 是å¦ä¸€è‡´ã€‚`codohealth.com`ã€`hyjssb.com`ã€`nblssy.com` 等先等 5 月 13 日回访确认。 + +P2:异常域åå…ˆé™ 301/403/444。 +`cnzhenbang.com`ã€`vikau.com`ã€`jxxgygy.com`ã€`lmjcg.com` ç›®å‰å¼‚常状æ€å æ¯”高,ä¸ä½œä¸ºé¦–页è¯å†²åˆºå¯¹è±¡ã€‚å…ˆå¤„ç† www/non-wwwã€robotsã€WAF/å°ç¦å’Œåކå²å…¥å£ 301。 + +`leici1940.com` 专项: +先清å‰ç½®ç¼“å­˜å¹¶å¤æŸ¥æ’­æ”¾å™¨ï¼›ç„¶åŽè§‚察 5 月 13 æ—¥æ˜¯å¦æ¢å¤ç™¾åº¦èœ˜è››ã€è¯¦æƒ…/播放深抓和 `site:` 稳定状æ€ã€‚未æ¢å¤å‰ï¼Œä¸æŠŠå®ƒåˆ—å…¥å…³é”®è¯æ”¾å¤§ä¸»çº¿ã€‚ + +## 5 月 13 日验收线 + +1. 核心蜘蛛全日大于等于 350,百度大于等于 180。 +2. 详情+播放页全日大于等于 60,且至少 2 ä¸ªå€™é€‰åŸŸåæœ‰ç¨³å®šæ·±æŠ“。 +3. 301 全日控制在 100 以下,403/444 ä¸ç»§ç»­æ‰©å¤§ã€‚ +4. Baidu `indexed_like` 回到 5 个以上。 +5. çˆ±ç«™å½“å‰æ—¥ PC è¯å‡ºçŽ°éž 0,或者历å²è¯åŸŸå釿–°è¢« Baidu `site:` æ•获。 + +如果以上 5 æ¡è¾¾ä¸åˆ°ï¼Œä¸è¿›å…¥æ–°ä¸€è½®å…³é”®è¯æ‰©å¼ ï¼Œåªç»§ç»­åšæ”¶å½•ç¨³å®šã€æ·±æŠ“和异常状æ€ä¿®å¤ã€‚ diff --git a/docs/gpt-template-seo/README.md b/docs/gpt-template-seo/README.md index 2ffa439..badd883 100644 --- a/docs/gpt-template-seo/README.md +++ b/docs/gpt-template-seo/README.md @@ -60,6 +60,10 @@ 作用:把 7 日验收åŽçš„下一步拆æˆä¿åކå²è¯ã€æ”¶å½•转è¯ã€é™å¼‚å¸¸ä¸‰æ¡æ‰§è¡Œçº¿ï¼Œå¹¶æ˜Žç¡® 2026-05-10 验收å£å¾„。 18. [22-GPT模æ¿Sitemap与百度推é€URL规范化修正-2026-05-09.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/22-GPT模æ¿Sitemap与百度推é€URL规范化修正-2026-05-09.md) ä½œç”¨ï¼šè®°å½•å½“å‰ GPT ä¸»æµ‹è¯•æº sitemapã€è¿è¡Œç”Ÿæˆé€»è¾‘ã€ç™¾åº¦æŽ¨é€ URL 从 `https://www.domain` 统一到 `https://domain` 的代ç ä¿®æ­£ã€äº§ç‰©åˆ·æ–°å’Œæ¬¡æ—¥éªŒæ”¶å£å¾„。 +19. [23-GPT模æ¿5月12蜘蛛日志与7æ—¥åŽç»­è¿›åº¦-2026-05-12.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/23-GPT模æ¿5月12蜘蛛日志与7æ—¥åŽç»­è¿›åº¦-2026-05-12.md) + 作用:å¤ç›˜ 2026-05-11 完整日与 5 月 12 日零点蜘蛛ã€ç™¾åº¦æ”¶å½•ã€çˆ±ç«™ä¿¡æ¯ï¼ŒéªŒæ”¶ sitemap/æŽ¨é€ URL 规范化åŽçš„å®žé™…æ•ˆæžœï¼Œå¹¶é‡æŽ’åŽ†å²è¯æ¢å¤ã€æ”¶å½•转è¯å’Œå¼‚常修å¤ä¼˜å…ˆçº§ã€‚ +20. [24-GPT模æ¿5月12åˆç›˜èœ˜è››æ—¥å¿—与7日进度续报-2026-05-12.md](/www/wwwroot/diff-maccms/SEONexus/docs/gpt-template-seo/24-GPT模æ¿5月12åˆç›˜èœ˜è››æ—¥å¿—与7日进度续报-2026-05-12.md) + 作用:按 2026-05-06 至 2026-05-12 åˆç›˜çª—å£å¤ç›˜æ ¸å¿ƒèœ˜è››ã€Baidu siteã€çˆ±ç«™å’Œ leici1940.com å•站状æ€ï¼Œç»™å‡º 5 月 13 日继续验收的域å分层与阈值。 首批 4 ç«™çš„æ‰§è¡Œææ–™ä¹Ÿå…¨éƒ¨æ”¾åœ¨æœ¬ç›®å½•: