Files
as-daycard-openapi/FE-RECOMMEND-GUIDE.md
zhaogd b537168895 feat: 新增整屏推荐页配置功能
- 7 个固定槽位(1920×1080 两行网格),槽位定义/校验/橘视厅元数据同步
- 海报按槽位自动选:竖图槽取 posterimg、横图槽取 thumbimg,支持自定义覆盖
- 一键导出 zip 交付包:海报按槽位精确尺寸居中裁剪 + config.json + README(am start 拉起命令)
- 管理后台接口 6 个(/manage/recommend-page/*),前端对接文档 FE-RECOMMEND-GUIDE.md
2026-09-26 14:28:19 +08:00

249 lines
9.1 KiB
Markdown
Raw Permalink 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.

# 推荐页配置 · 前端对接文档
> 功能:设备厂商 1920×1080 整屏电影推荐页,运营在后台选片配置 7 个固定槽位,一键导出 zip 交付包发给厂商。
> 后端已上线,本文档为前端实施所需全部信息。
---
## 1. 通用约定
- **鉴权**:所有接口走管理后台 JWT,请求头带 `token`(与现有 /manage 接口一致)
- **返回格式**:`{ code: 0, msg: "", data: {...} }`,code=0 成功,非 0 失败(msg 为错误信息)
- **新增菜单**:「媒资管理 → 推荐页配置」,路由 `manage/recommend-page`(后台 sys_menu 已配置 id=126,超管自动可见,其他角色需在角色管理勾选)
---
## 2. 槽位定义(固定 7 个,不可增减)
整屏排版(1920×1080 两行网格,槽位间距 20px,左右边距 70px):
```
┌────────────────┐ ┌─────┐ ┌─────┐ ┌─────┐
│ slot_01 │ │slot_02│ │slot_03│ │slot_04│ ← 上行
│ 700×400 │ │340×440│ │340×440│ │340×440│
└────────────────┘ └─────┘ └─────┘ └─────┘
┌──────────────┐ ┌──────────────┐ ┌─────────┐
│ slot_05 │ │ slot_06 │ │ slot_07 │ ← 下行
│ 700×300 │ │ 700×300 │ │ 340×300 │
└──────────────┘ └──────────────┘ └─────────┘
```
| 槽位 | 类型 | 尺寸 | 说明 | 影片 |
|---|---|---|---|---|
| slot_01 | MAIN 主推 | 700×400 | 横图 | 必填 contentcode |
| slot_02 | PORTRAIT 竖图 | 340×440 | 竖图 | 必填 |
| slot_03 | PORTRAIT 竖图 | 340×440 | 竖图 | 必填 |
| slot_04 | PORTRAIT 竖图 | 340×440 | 竖图 | 必填 |
| slot_05 | LANDSCAPE 横图 | 700×300 | 横图 | 必填 |
| slot_06 | LANDSCAPE 横图 | 700×300 | 横图 | 必填 |
| slot_07 | MORE 更多入口 | 340×300 | 无影片,点击拉起橘视厅 | 不填,必填海报 URL |
**编辑页建议**:按 0.25 倍缩放渲染线框(175×100 / 85×110 / 175×75 / 85×75),每槽位显示海报缩略图 + 影片名 + 尺寸标注。
---
## 3. 接口清单
Base:`/manage/recommend-page`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/list` | 分页列表,参数 `page`、`limit`、`name`(可选) |
| GET | `/info/{id}` | 详情(slots 已解析为数组) |
| GET | `/slot-definitions` | 槽位定义(渲染线框用,见 §3.1) |
| POST | `/save` | 新建或更新(有 id 更新,无 id 新建) |
| POST | `/delete` | 软删除,body: `[1, 2]`(id 数组) |
| GET | `/export/{id}` | 导出 zip 交付包(**必须 blob 方式下载,见 §6**) |
### 3.1 槽位定义 GET `/slot-definitions`
```json
{
"code": 0,
"data": {
"slots": {
"slot_01": { "slotCode": "slot_01", "slotType": "MAIN", "width": 700, "height": 400,
"defaultActionType": "DETAIL", "mediaRequired": true, "posterField": "thumbimg" },
"slot_02": { "slotCode": "slot_02", "slotType": "PORTRAIT", "width": 340, "height": 440,
"defaultActionType": "DETAIL", "mediaRequired": true, "posterField": "posterimg" }
// ... slot_03~07
}
}
}
```
### 3.2 列表 GET `/list?page=1&limit=10`
```json
{
"code": 0,
"data": {
"page": {
"totalCount": 5, "pageSize": 10, "currPage": 1, "totalPage": 1,
"list": [
{ "id": 1, "name": "2026年10月推荐", "remark": "备注", "deleteFlag": "UNDEL",
"createdAt": "2026-09-26 15:00:00", "updatedAt": "2026-09-26 15:00:00" }
]
}
}
}
```
### 3.3 详情 GET `/info/{id}`
```json
{
"code": 0,
"data": {
"recommendPage": { "id": 1, "name": "2026年10月推荐", "remark": "", "slots": "[...原始JSON字符串...]", "createdAt": "..." },
"slots": [
{
"slotCode": "slot_01",
"slotType": "MAIN",
"width": 700, "height": 400,
"contentcode": "23010012026061709581101832",
"mediaName": "影片名(后端从橘视厅同步)",
"posterUrl": "https://imgcms.jetsenx.huashi.tv/.../448x252_xxx.png",
"customPoster": false,
"actionType": "DETAIL",
"actionParam": "am start -n tv.huashi.jetsenx.jetsenfilx.jsttv.home/com.pukka.jiecheng.MainActivity --es media_id \"{contentcode}\"",
"rawMedia": { "...橘视厅完整元数据(posterimg/thumbimg/description等)..." }
},
{
"slotCode": "slot_07",
"slotType": "MORE",
"width": 340, "height": 300,
"contentcode": null,
"mediaName": "更多影片",
"posterUrl": "https://.../more.jpg",
"customPoster": true,
"actionType": "LAUNCH",
"actionParam": "am start -n tv.huashi.jetsenx.jetsenfilx.jsttv.home/com.pukka.jiecheng.MainActivity",
"rawMedia": null
}
]
}
}
```
> 前端渲染用 `data.slots`(已解析),按 slotCode 匹配到线框位置。
### 3.4 保存 POST `/save`
**前端只需传必要字段,影片名/海报由后端从橘视厅自动同步:**
```json
{
"id": null,
"name": "2026年10月推荐",
"remark": "备注(可选)",
"slots": [
{ "slotCode": "slot_01", "contentcode": "23010012026061709581101832" },
{ "slotCode": "slot_02", "contentcode": "..." },
{ "slotCode": "slot_03", "contentcode": "..." },
{ "slotCode": "slot_04", "contentcode": "..." },
{ "slotCode": "slot_05", "contentcode": "..." },
{ "slotCode": "slot_06", "contentcode": "..." },
{ "slotCode": "slot_07", "posterUrl": "https://.../more.jpg" }
]
}
```
**槽位可选字段:**
| 字段 | 说明 |
|---|---|
| `posterUrl` | 自定义海报 URL。横图槽位(slot_01/05/06/07)默认用橘视厅横版图,效果不佳时运营可手动填横图 URL 覆盖 |
| `actionParam` | 自定义拉起命令(一般不填,用后端默认值) |
**后端校验规则(错误信息会原样返回 msg):**
- 期名 name 必填
- slots 必须 7 个、slotCode 齐全不重复
- slot_01~06 contentcode 必填;无效 contentcode 报错 `contentcode 无效: xxx`(保存时后端实时调橘视厅校验)
- slot_07 必填 posterUrl
- 影片无海报时报错 `影片 xxx 无可用海报`
成功返回:`{ code: 0, data: { recommendPage: { id: 1, ... } } }`
---
## 4. 选片弹窗(复用已有媒资接口)
**搜索**:`GET /manage/jetsen-media/search?keyword=xxx&pageindex=1&pagesize=20`(已存在的接口)
```json
{
"code": 0,
"data": {
"list": [
{ "contentcode": "23010022026090109192132084", "name": "后悔没有好好爱过你",
"posterimg": "https://...", "categoryname": "电影", "score": "8.0",
"releaseyear": "2026", "originalcountry": "中国大陆" }
],
"totalCount": 100, "currPage": 1, "pageSize": 20, "totalPage": 5
}
}
```
弹窗交互:搜索框 → 结果列表(海报缩略图 + 片名 + contentcode)→ 选中回填槽位。
槽位预览图可直接用选中结果的 `posterimg`(保存后以 info 接口返回的 posterUrl 为准)。
---
## 5. 页面结构
### 5.1 列表页 `/recommend`(路由建议)
| 列 | 说明 |
|---|---|
| 期名 | name |
| 备注 | remark |
| 创建时间 | createdAt |
| 操作 | 编辑 / **复制新一期** / 导出zip / 删除 |
**复制新一期**:调 `info/{id}` 取 slots 预填新建表单(id 置 null),运营只改差异影片。
### 5.2 编辑页
- 顶部表单:期名(必填)、备注
- 槽位线框区(§2 尺寸 × 0.25 缩放)
- slot_01~06:点槽位 → 打开选片弹窗 → 回填 contentcode + 预览图
- 每个槽位可展开「自定义海报 URL」输入框(可选,横图槽位补横版图用)
- slot_07:仅海报 URL 输入(必填)+ 可选拉起参数
- 工具栏:保存、导出 zip(保存后才可导出,需有 id)
**前端预校验**(7 槽位齐、slot_01~06 contentcode 齐、slot_07 海报齐),后端保存时再校验一遍。
---
## 6. 导出下载(重要:不能用 window.open)
导出接口走 JWT 请求头,`window.open` / `<a href>` 无法带 token,**必须用 axios blob 方式**:
```javascript
axios.get(`/manage/recommend-page/export/${id}`, {
responseType: 'blob',
headers: { token: getToken() }
}).then(res => {
const url = window.URL.createObjectURL(new Blob([res.data]))
const link = document.createElement('a')
link.href = url
link.download = `推荐页_${name}.zip`
link.click()
window.URL.revokeObjectURL(url)
})
```
注意:导出需实时下载 7 张海报并裁剪,耗时约 5~15 秒,建议按钮加 loading 态。失败时(如某海报下载失败)返回 JSON 错误而非 zip,前端可判断 `res.data.type === 'application/json'` 提示错误。
---
## 7. zip 交付包结构(导出结果,无需前端处理,仅供了解)
```
推荐页_2026年10月推荐.zip
├── images/slot_01.png ~ slot_07.png # 已按槽位精确尺寸居中裁剪
├── recommend_config.json # contentcode + actionType + actionParam(占位符已替换真实值)
└── README.txt # 排版与拉起规则说明
```