Files
web_hgcg_h5/docs/sitemap-backend-contract.md
T
谢宇宁andClaude Opus 5.5 8cdbfee08f Initial commit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 16:23:48 +07:00

223 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 后端 Sitemap 生成对接规则
核对时间:2026-10-22 22:53(Asia/Shanghai)。
本文根据当前工作区的前端代码(包含尚未提交的修改)整理固定页面、URL、canonical 和 noindex 规则,供后端实现 sitemap 生成。它描述现有页面行为及生成约定,不表示 sitemap、存储或 Nginx 已完成接入。
当前内容主地址仅优先使用符合英文/拼音字符规则的 slug;中文、已编码中文或其他无效 slug 回退到 ID。搜索关键词仍支持中文和特殊字符,但所有搜索地址均排除在 sitemap 外。
## 1. 域名与通用约定
- `D` 表示正式站点的 origin,即协议、域名和可选端口,不带末尾 `/`。
- 域名优先取 `config.config.foreverUrlAddr`;无有效值时取 `config.newAddressInfo.foreverUrlAddr`。移除配置中的路径、查询参数和锚点,与前端 canonical 保持一致。
- 不使用 API 域名、CDN 域名、请求 Host、临时访问域名或本地地址作为页面域名。
- 生成任务没有有效正式域名时,不猜测域名;暂停本次发布并保留上一份成功结果。
- 页面 URL 以 `/` 结尾;sitemap 中使用绝对地址 `D + 规范路径`。
- sitemap 只提交正常、允许索引的页面及其 canonical 地址;别名、参数变体和重复地址不单独提交。
- API、媒体源、静态资源和健康检查不属于本文的页面 sitemap 范围。
## 2. 固定页面清单
固定地址候选共 12 个。满足条件时,其 canonical 均为 `D + URL`。列表为空或页面异常时,按后续排除规则处理。
| 页面 | URL | 加入 sitemap 的条件 |
| --- | --- | --- |
| 首页 | `/` | 页面正常 |
| 短剧列表 | `/shorts/` | 页面正常且列表非空 |
| 专题列表 | `/topics/` | 页面正常且列表非空 |
| 热播榜 | `/rank/hot/` | 页面正常且列表非空 |
| 新剧榜 | `/rank/new/` | 页面正常且列表非空 |
| 追剧榜 | `/rank/follow/` | 页面正常且列表非空 |
| 关于我们 | `/info/about-us/` | 页面正常 |
| 用户协议 | `/info/user-agreement/` | 页面正常 |
| 隐私政策 | `/info/privacy-policy/` | 页面正常 |
| 常见问题 | `/info/help-center/` | 页面正常 |
| 2257 合规声明 | `/info/2257/` | 页面正常 |
| DMCA 版权声明 | `/info/dmca/` | 页面正常 |
这些是页面地址,不是需要前端预建的 sitemap XML 文件。
## 3. 动态页面 URL 规则
| 类型 | 规范 URL | 加入 sitemap 的条件 |
| --- | --- | --- |
| 作品详情 | `/detail/{key}/` | 作品存在,页面正常且未被标记为需登录或禁止索引 |
| 专题详情 | `/topics/{key}/` | 专题存在,页面正常且列表非空 |
| 标签列表 | `/tag/{key}/` | 标签存在,页面正常且列表非空 |
| 更多栏目 | `/config/{key}/` | 首页栏目存在、已启用且类型为 `1` 或 `3`,并具有自己的标题、正文简介和有效内容卡片 |
| 单集播放 | `/play/{作品key}/{chapterId}/` | 满足下述播放页条件 |
| 内容列表分页 | `{列表基础路径}page/{n}/` | `n >= 2`,分页真实存在且允许索引 |
### 3.1 内容标识
- `{key}` 优先使用内容接口返回的有效英文/拼音 `slug`;无有效 slug 时使用正整数 ID。前端不会自动把中文转成拼音。
- slug 去掉首尾空格后,必须满足 `/^[a-z\d][a-z\d_-]*$/i`:以 ASCII 字母或数字开头,其余只能包含 ASCII 字母、数字、连字符 `-`、下划线 `_`。
- 使用接口返回的规范大小写,作为一个路径段按 `encodeURIComponent` 的语义编码一次,避免重复编码;不得把编码后的中文当作英文 slug。
- 中文、中文英文混合、内含空格、斜杠、点号、百分号、问号等不符合正则的 slug 均回退到 ID。`-city` 和 `_city` 也不符合开头规则。
- 历史中文 slug 地址在能够匹配到真实内容时仍可读取,但 canonical 和新生成链接使用 ID;后端不应继续向 sitemap 写入这些兼容地址。
- 纯数字路径按 ID 查询,不按 slug 查询。当前生成正则本身未单独排除纯数字 slug,因此后端应避免分配此类 slug;存量数据须核对身份,不能直接当作 slug 发布。
- 使用数字 ID 时去除前导零;ID 必须是正整数,且处于 JavaScript 安全整数范围内。
- `{chapterId}` 是真实章节 ID,不是集数,也不支持章节 slug。章节必须属于 URL 中的作品。
以作品 `id=9` 为例:
| 接口返回的 slug | 生成的详情路径 |
| --- | --- |
| `city-story` | `/detail/city-story/` |
| `city_story-2026` | `/detail/city_story-2026/` |
| `dushiaiqing` | `/detail/dushiaiqing/` |
| `City-Story` | `/detail/City-Story/` |
| `都市爱情` | `/detail/9/` |
| `city-爱情` | `/detail/9/` |
| `%E9%83%BD%E5%B8%82` | `/detail/9/` |
| `city story`、`city/story`、`city.story`、空值 | `/detail/9/` |
以上规则同时用于作品、专题、标签和更多栏目;播放页的作品段使用同一规则,章节段仍使用章节 ID。
### 3.2 播放页条件
播放页同时满足以下条件时才可进入公共 sitemap:
1. 作品及章节真实存在,且两者对应。
2. 有作品名和真实的正整数集数。
3. 公开抓取身份有权观看当前章节。
4. 存在有效的当前章节正片地址。
共享预告、继承的作品简介、章节 SEO 标题或纯解锁提示不能单独满足条件。收费类型本身不是排除依据,但不能使用管理员、VIP 或已购用户的个人权限生成公共 sitemap。
sitemap 提交页面 URL,不写入用户凭证或受限媒体源。
### 3.3 列表分页
- 普通内容目录当前每页 24 条,后端根据有效总数计算真实页数。
- 更多栏目当前使用有限首页栏目快照:类型 `1` 取实际作品列表,类型 `3` 取实际专题列表,以实际条目数为 `totalCount`。SEO 层取 `pageSize = max(24, totalCount)`,因此当前只发布栏目基础地址,不生成 `/config/{key}/page/2/` 等额外分页。
- 第一页只提交基础地址,不额外提交 `/page/1/`。
- 第 2 页及以后保留自己的 canonical,例如 `/shorts/page/2/`,不统一归并到第一页。
- 作品详情和播放页的 `/page/{n}/` 是评论分页;`n > 1` 时不进入 sitemap。
- 搜索、收藏、历史、已购和充值即使有分页,也不进入 sitemap。
## 4. Canonical 规则
| 场景 | 规范化规则 |
| --- | --- |
| 正式域名 | 统一使用 `D`,与前端页面 canonical 的域名一致 |
| 尾斜杠 | 页面路径以 `/` 结尾 |
| 查询参数和锚点 | canonical 不含查询参数和锚点;sitemap 同样不提交这些变体 |
| 第一页 | `/shorts/page/1/` 规范化为 `/shorts/`;其他支持分页的页面同理 |
| 有效列表分页 | 第 2 页及以后自引用,保留 `/page/{n}/` |
| 默认榜单 | `/rank/` 规范化为 `/rank/hot/`;`/rank/page/2/` 规范化为 `/rank/hot/page/2/` |
| 专题别名 | `/theme/{key}/` 规范化为 `/topics/{规范key}/`,保留有效分页 |
| ID 与 slug | 有符合英文/拼音字符正则的 slug 时,ID 地址指向 slug 主地址;中文或其他无效 slug 回退到规范 ID |
| 历史中文 slug 地址 | 能匹配到资源时保留兼容读取,canonical 使用该资源的规范 ID 地址 |
| 数字 ID 前导零 | 无 slug 时,`/detail/009/` 规范化为 `/detail/9/`;其他数字内容 ID 同理 |
| noindex 页面 | 当前前端不输出 canonical,不进入 sitemap |
例如作品 `id=9`、`slug=city-story`,章节 ID 为 `90`:
```text
/detail/9/ -> D/detail/city-story/
/detail/city-story/ -> D/detail/city-story/
/play/9/90/ -> D/play/city-story/90/
/play/city-story/90/ -> D/play/city-story/90/
```
例如专题 `id=7`、`slug=city-collection`:
```text
/theme/7/ -> D/topics/city-collection/
/topics/7/ -> D/topics/city-collection/
/theme/7/page/2/ -> D/topics/city-collection/page/2/
```
例如作品 `id=9`、`slug=都市爱情`,章节 ID 为 `90`,中文仅用于展示或兼容读取:
```text
/detail/9/ -> D/detail/9/
/detail/都市爱情/ -> D/detail/9/
/play/9/90/ -> D/play/9/90/
/play/都市爱情/90/ -> D/play/9/90/
```
上述中文路径按可读形式展示,实际请求可使用百分号编码;sitemap 只写入右侧 ID 主地址。
别名页面仍可允许索引,canonical 归并不等于 noindex,也不表示已有重定向。sitemap 只提交最终 canonical 地址,并按完整地址去重。
`/home`、`/playlet`、`/list`、`/topic`、`/application` 等值只用于后台导航值转换,不是额外公开页面,不应据此生成 sitemap 地址。
## 5. 固定 Noindex 页面
| 页面范围 | URL | 当前 robots |
| --- | --- | --- |
| 搜索和搜索分页 | `/search/`、`/search/{关键词}/`、`/search/{关键词}/page/{n}/`、`/search/?q={关键词}`、`/search/page/{n}/?q={关键词}` | `noindex,follow` |
| 独立入口 | `/navigation/` | `noindex,follow` |
| 回家的路 | `/home-road/` | `noindex,follow` |
| 福利应用 | `/apps/` | `noindex,follow` |
| 个人中心 | `/member/center/`、`/info/user-center/` | `noindex,nofollow` |
| 收藏 | `/member/favorites/`,包括分页 | `noindex,nofollow` |
| 历史 | `/member/history/`,包括分页 | `noindex,nofollow` |
| 已购 | `/member/purchased/`,包括分页 | `noindex,nofollow` |
| 账号设置 | `/member/settings/`、`/info/settings/` | `noindex,nofollow` |
| 充值及 VIP | `/info/deposit/`、`/info/deposit/vip/`,包括支持的分页 | `noindex,nofollow` |
| 意见反馈 | `/info/feedback/` | `noindex,nofollow` |
以上页面及其参数变体均不进入 sitemap。
### 5.1 当前搜索地址的排除规则
- 新生成的搜索入口统一使用 `/search/{关键词}/`;关键词先去掉首尾空格,再通过 `encodeURIComponent` 整体编码为一个路径段。
- 关键词可以包含中文、空格、斜杠、问号、百分号等字符。搜索关键词不受内容 slug 的 ASCII 正则限制,也不进行数字 ID 归一。
- `/search/?q={关键词}` 和 `/search/page/{n}/?q={关键词}` 仍可解析;后续分页链接生成路径形式并移除 `q` 参数,不依赖重定向完成转换。
- 路径本身已有关键词时优先使用路径关键词;只有路径没有关键词时才从 `q` 读取。
- 不论入口形式、搜索结果数量或关键词字符,搜索页始终 `noindex,follow`,不输出 canonical,也不进入 sitemap。
- 无关键词的 `/search/` 可以访问;无关键词且页码大于 1 时返回 404。
## 6. 条件性 Noindex 与异常处理
| 情况 | 当前前端行为 | 后端生成处理 |
| --- | --- | --- |
| 空公开列表 | 接口成功确认 `totalCount=0` 时,第一页 `noindex,follow`,仍返回 200 | 不提交 |
| 详情或播放页评论分页 | `/detail/{key}/page/{n}/`、`/play/{key}/{chapterId}/page/{n}/`,`n>1` 时 `noindex,follow` | 不提交 |
| 非默认列表排序 | 除榜单和更多栏目外,公开列表 `sort != 1` 时 noindex | 只生成无参数的默认地址 |
| 非法列表排序 | 公开列表的 `sort` 不属于 `1、2、3` 时 noindex;未传时默认 `1` | 不生成非法变体 |
| 非默认评论排序 | 存在 `commentSort` 且值不等于字符串 `2` 时 noindex | 不生成参数变体 |
| 更多栏目内容不足或无效 | 已存在栏目缺少自己的标题、正文简介或有效卡片时 `noindex,follow`;找不到启用的类型 `1/3` 栏目时按资源缺失处理 | 不提交 |
| 播放页条件不足 | 预告、纯解锁提示、无有效正片或无观看权限时 `noindex,follow` | 不提交 |
| 明确禁止索引或需要登录 | `indexable=false` 或 `loginRequired=true` 时 noindex | 不提交 |
| 无效路由或不存在的资源 | 返回 404,且无 canonical | 不提交 |
| 已确认越界的分页 | 返回 404,且无 canonical | 不提交 |
| 临时取数故障 | `failed` 或 `unavailable` 时返回 500,且无 canonical | 不将故障误判为删除;保留上次成功结果,稍后重试 |
补充约定:
- 榜单和更多栏目可以接受 `sort=2/3`,但 canonical 仍不含参数;sitemap 不重复提交这些参数地址。
- 作品详情的评论数为 0,不等于作品内容为空,不能按空列表规则排除作品详情。
- 不存在的内容与临时故障必须区分;无法可靠判断一批内容时,不发布因取数失败而缺项的新结果。
## 7. 建议的生成与发布约定
1. 合并本文的固定地址与后端数据库枚举的动态内容。
2. 按公开访问时的页面状态判断是否允许索引。
3. 按英文/拼音 slug 白名单和 ID 回退规则生成最终 canonical URL,不输出参数、锚点、别名、中文内容 slug 或非页面资源。
4. 按完整 canonical URL 去重,只发布正常、有效的页面地址。
5. `lastmod` 有可靠数据时使用页面内容实际更新时间;没有时省略,不统一伪造为生成时间。
6. 生成文件保存到前端发布目录之外的持久化存储,不写入前端 `public` 或 `.next`。
7. 完成分片生成和可访问性校验后,再切换 sitemap 索引;失败时保留上一份成功版本。
8. 建议固定入口为 `/sitemap.xml`,分片通过 `/sitemaps/` 下的通用路径提供访问。现有入口不同时,优先保留已提交的地址。
9. `robots.txt` 中声明正式站点的 sitemap 索引地址;前后端 URL 或索引规则变更时同步更新生成逻辑。
首次接入需要部署访问配置;后续内容和分片变化不应要求前端预留文件、重新构建或重启 H5。
## 8. 代码依据
- [路由解析、内容路径和分页支持](../src/lib/routing.ts)
- [域名、canonical、noindex 和页面状态判断](../src/lib/seo.ts)
- [服务端资源核对与 SEO 元数据输出](../src/lib/seo-server.ts)
- [专题解析、首页栏目快照和列表分页](../src/lib/client/channel.ts)
- [章节权限与正片地址判断](../src/lib/client/playback.ts)
- [固定信息页](../src/lib/info-pages.ts)
- [现有页面索引策略](indexation-policy.md)(其“非空 slug 优先”描述需结合当前代码的英文/拼音字符白名单理解)
- [SEO 规则回归用例](../tests/seo.test.mjs)
- [Slug 路由与 canonical 回归用例](../tests/slug-routing.test.mjs)
- [路由与搜索参数兼容回归用例](../tests/routing.test.mjs)
- [分页与搜索路径回归用例](../tests/pagination.test.mjs)