Files
as-daycard-openapi/FE-RECOMMEND-GUIDE.md

249 lines
9.1 KiB
Markdown
Raw Normal View History

# 推荐页配置 · 前端对接文档
> 功能:设备厂商 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 # 排版与拉起规则说明
```