// +---------------------------------------------------------------------- declare (strict_types = 1); namespace addon\docs\controller; use addon\docs\model\DocsDoc; use addon\docs\model\DocsProject; use addon\docs\model\DocsVersion; /** * 站点地图(前台 SEO 用) * * 输出最简版 sitemap.xml: * - 首页 / 文档中心首页 * - 所有启用项目的入口 * - 所有启用项目下「默认版本」且 status=1 的文档(非默认版本不收录,避免长短 URL 双内容) * * 内容量大可后续改成分片/分项目,本版本供搜索引擎基本覆盖即可。 * * @author ywxapp */ class Sitemap extends DocsFrontend { /** * GET /sitemap.xml */ public function index() { $base = docs_base_url(); $urls = []; // 首页 $urls[] = ['loc' => $base . '/', 'priority' => '1.0']; // 各项目入口 $projects = DocsProject::enabledList(); foreach ($projects as $project) { $urls[] = [ 'loc' => $base . '/' . $project['name'], 'priority' => '0.8', 'lastmod' => $this->dateOf((int) ($project['update_at'] ?? 0)), ]; // 该项目默认版本的可阅读文档(避免短/长地址双内容) $version = DocsVersion::resolve((int) $project['id'], (string) ($project['default_version'] ?? '')); if (!$version) { continue; } $docs = DocsDoc::where('project_id', $project['id']) ->where('version_id', $version->id) ->where('status', 1) ->where('is_dir', 0) ->field('name,update_at') ->order('update_at', 'desc') ->limit(5000) ->select() ->toArray(); foreach ($docs as $doc) { $urls[] = [ 'loc' => $base . '/' . $project['name'] . '/' . rawurlencode($doc['name']), 'priority' => '0.6', 'lastmod' => $this->dateOf((int) ($doc['update_at'] ?? 0)), ]; } } $xml = "\n"; $xml .= ''; foreach ($urls as $u) { $xml .= ''; $xml .= '' . htmlspecialchars((string) $u['loc'], ENT_XML1, 'UTF-8') . ''; if (!empty($u['lastmod'])) { $xml .= '' . $u['lastmod'] . ''; } if (isset($u['priority'])) { $xml .= '' . $u['priority'] . ''; } $xml .= ''; } $xml .= ''; return response($xml, 200, ['Content-Type' => 'application/xml; charset=utf-8']); } /** * 时间戳 → W3C 日期(空值返回 null) */ protected function dateOf(int $ts): ?string { if ($ts <= 0) { return null; } return date('Y-m-d', $ts); } }