# 应用下载页 PC 推广列表设计 ## 目标与范围 只在 `app.html` 现有“应用推广”区域接入: `GET /genealogy/pc/promotions` 本批不修改首页广告位,不接入官网文章、帮助文章或公开家谱,不新增推广详情、编辑、发布和统计功能。 ## 契约来源与冲突 后端 `PcPromotionApiController` 继承 `BusinessPromotionControllerSupport`,实际支持可选 Query `platform`,返回直接数组 `List`。服务端只返回 `status=0` 的记录,并按 `sortOrder` 升序、`promotionId` 降序排列。 Apifox YAML 将接口标记为 `security: []` 且未导出 `platform`;后端控制器没有 `@SaIgnore`,全局安全拦截器实际要求 PC 登录态和匹配的 `clientid`。本批以后端代码和部署行为为准: - 请求携带当前 PC 登录 token; - 不发送 `platform`,因为后端没有提供可核验的枚举和值说明; - 页面不自行排序或筛选。 ## 页面行为 `app.html` 本身保持公开,不因推广接口需要登录而强制跳转: - 未登录:不发请求,推广区域显示“登录后查看应用推广”和登录链接; - 已登录:初始化时读取一次推广列表; - 返回空数组:显示“当前暂无应用推广”; - 返回有效记录:渲染现有三列推广卡片; - 401:清理失效登录态,推广区域改为登录入口; - 403、网络失败或响应非法:只在推广区域显示错误,不影响下载页其它内容; - 页面不提供手动刷新、筛选、编辑或业务 ID 输入。 ## 字段边界 | 字段 | 分类 | 页面行为 | | --- | --- | --- | | `promotionId` | I | 要求为稳定非零十进制字符串,用作节点标识,不展示 | | `promotionKey` | I | 后台键,不展示 | | `promotionTitle` | R | 必填,转义后作为卡片标题 | | `promotionDesc` | R | 可空,转义后作为说明 | | `coverOssId` | I | 没有文件 URL 契约,不拼接、不展示、不手填 | | `targetUrl` | R | 可空;只允许绝对 `http`/`https` URL,合法时整张卡片可点击,并使用 `target="_blank"`、`rel="noopener noreferrer"` | | `platform` | I | 响应字段只用于边界保留,不显示、不作为前端筛选依据 | | `sortOrder` | I | 服务端排序 owner,前端不重新排序 | | `status` | I | 只接受 `0`;其它值使整条响应非法 | | `remark` | I | 后台备注,不展示 | 列表必须是直接数组。任一元素缺少稳定 `promotionId`、标题或正常状态时,整批响应失败,避免部分错误数据被误当成完整结果。 ## 文件与所有权 - `utils/ApiClient.js`:method、完整 path 和 Query 白名单唯一 owner,新增 `promotions(query)`;虽然当前页面省略 Query,但客户端只允许 `platform`。 - `public/js/app-promotion-pages.js`:响应规范化、URL 安全校验、卡片渲染、登录态和区域状态 owner。 - `app.html`:保留现有 `data-promotion-list` 容器,加载推广脚本。 - `public/css/app.css`:只在现有样式确实无法支持无封面卡片时做最小调整。 - `tests/api-client-contract.test.js`、`tests/app-promotion-pages.test.js`、`tests/pc-scope.test.js`:契约、渲染、安全和页面开放状态。 - `docs/PC接口对接规划.md`:记录字段来源、提交时机、鉴权冲突和阶段进度。 旧的 `promotion-pages.js` 不恢复;新的 owner 只服务应用下载页,避免与历史未定义脚本形成兼容分支。 ## 测试与验收 必须按 RED → GREEN 验证: 1. 客户端只调用 `GET /genealogy/pc/promotions`,可选 Query 仅保留 `platform`,并携带登录 token。 2. 长 ID 始终按字符串处理;不安全数字、停用记录、空标题和非法列表结构失败。 3. 标题、说明和 URL 转义;`javascript:`、相对 URL 和非 HTTP(S) URL 不生成链接。 4. 页面不显示 `coverOssId`、`promotionKey`、`sortOrder`、`status`、`remark` 或原始 JSON。 5. 未登录不发推广请求;401、403、空列表和成功列表均有独立区域状态。 6. 专项测试、全量测试、语法检查和 `git diff --check` 通过。 7. 浏览器使用测试账号验证真实列表或空状态;没有推广数据时,不制造详情或示例推广。