19 KiB
草原指南管理、VOD 素材与小程序接口
服务:
hl-user-service、hl-resource-service、hl-mp-service、hl-gateway
PR: #4916、#4917、#4918
Issue: #4914
日期: 2026-07-12
影响范围: 管理后台草原指南内容维护、素材库草原指南分类、小程序草原指南列表/详情/播放
一、前端必须先确认的结论
- 草原指南是独立内容模块,不是“小程序专用素材库列表”;页面不做内容分类筛选。
- 素材库新增固定顶级分类:
- 名称:
草原指南 - 编码:
grassland_guide - 编码固定,不能删除、停用或修改;显示名称和排序可以调整。
- 名称:
- 视频、可选自定义封面、富文本图片都由后台管理员上传;小程序用户没有上传接口。
- 视频文件走阿里云 VOD,确认后自动注册为
grassland_guide分类的视频素材。 - 视频封面规则:
- 传
customCoverMaterialId:使用管理员上传的图片,coverSource=CUSTOM; - 不传或传
null:使用阿里云 VOD 自动截帧封面,coverSource=AUTO。
- 传
- 本次没有新增
/mp/material/miniprogram/page,小程序草原指南只调用本文的/mp/grassland-guide/**。 - 所有业务失败仍可能返回 HTTP 200;前端必须判断响应体
code/success。
二、管理后台菜单与权限
| 配置 | 值 |
|---|---|
| 菜单名 | 草原指南管理 |
| 路由 | /grassland-guide |
| 组件路径 | grassland-guide/GrasslandGuideList |
| 图标 | VideocamOutline |
| 列表权限 | grassland-guide:list |
| 新增权限 | grassland-guide:create |
| 编辑权限 | grassland-guide:edit |
| 发布权限 | grassland-guide:publish |
| 下线权限 | grassland-guide:offline |
| 设置精选权限 | grassland-guide:featured |
| 上传视频权限 | grassland-guide:vod:upload |
菜单及按钮已分配给 SUPER_ADMIN、OPERATOR。
- 所有管理端接口均要求管理员登录。
- 列表、详情没有额外业务角色限制。
- 创建、更新、发布、下线、VOD 上传仅允许
SUPER_ADMIN或OPERATOR。 - 当前没有“删除草原指南内容”接口,只允许下线。
三、接口总表
管理端内容接口
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/admin/grassland-guide/videos |
分页查询 |
GET |
/admin/grassland-guide/videos/{videoId} |
内容详情 |
POST |
/admin/grassland-guide/videos |
创建草稿 |
PUT |
/admin/grassland-guide/videos/{videoId} |
全量更新 |
PUT |
/admin/grassland-guide/videos/{videoId}/publish |
发布/重新发布 |
PUT |
/admin/grassland-guide/videos/{videoId}/offline |
下线 |
管理端 VOD 接口
| 方法 | 路径 | 说明 |
|---|---|---|
POST |
/admin/grassland-guide/vod/upload/create |
创建 VOD 上传会话 |
POST |
/admin/grassland-guide/vod/upload/refresh |
刷新上传凭证 |
POST |
/admin/grassland-guide/vod/upload/confirm |
确认上传并注册素材 |
GET |
/admin/grassland-guide/vod/materials/{materialId}/play-auth |
后台预览播放凭证 |
小程序只读接口
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/mp/grassland-guide/home |
精选区 + 视频分页 |
GET |
/mp/grassland-guide/videos |
视频分页 |
GET |
/mp/grassland-guide/videos/{videoId} |
视频详情 |
GET |
/mp/grassland-guide/videos/{videoId}/play-auth |
短效播放凭证 |
小程序仅放行以上精确 GET 路径;同路径的 POST、PUT、DELETE 不放行。
四、管理端视频内容契约
4.1 分页查询 GET /admin/grassland-guide/videos
Query
| 字段 | 类型 | 必填 | 默认/约束 | 说明 |
|---|---|---|---|---|
keyword |
String | 否 | - | 标题、摘要模糊匹配 |
status |
String | 否 | DRAFT/PUBLISHED/OFFLINE |
状态精确过滤 |
featured |
Boolean | 否 | - | 是否精选 |
page |
Integer | 否 | 默认 1,最小 1 | 页码 |
pageSize |
Integer | 否 | 默认 20,范围 1–100 | 每页条数 |
固定排序:publishTime DESC, videoId DESC。管理列表不按 sortWeight 排序。
PageResult<GrasslandGuideVideoListVO>
分页对象只有 records、total、page、pageSize,没有 totalPages。
| 列表项字段 | 类型 | 说明 |
|---|---|---|
videoId |
String | 内容 ID |
title |
String | 标题 |
summary |
String | 摘要 |
effectiveCoverUrl |
String | 实际生效的封面 |
coverSource |
String | AUTO/CUSTOM |
durationSeconds |
Long | 视频时长(秒) |
featured |
Boolean | 是否精选 |
showProducedBadge |
Boolean | 是否显示“呼籁出品”角标 |
sortWeight |
Integer | 精选排序权重 |
status |
String | DRAFT/PUBLISHED/OFFLINE |
publishTime |
LocalDateTime | 首次发布时间 |
linkedProductId |
String | 预留关联产品 ID,一期不做跳转 |
所有雪花 ID 在响应中按 String 返回,前端不要转成 JavaScript Number。
4.2 详情 GET /admin/grassland-guide/videos/{videoId}
详情包含列表项全部字段,并增加:
| 字段 | 类型 | 说明 |
|---|---|---|
videoMaterialId |
String | VOD 视频素材 ID |
customCoverMaterialId |
String | 自定义封面素材 ID,可空 |
coverMaterialId |
String | 一期兼容字段,值同 customCoverMaterialId |
contentHtml |
String | 白名单清洗后的正文 HTML |
contentImageMaterialIds |
String[] |
正文图片素材 ID |
version |
Integer | 乐观锁版本 |
createdBy |
String | 创建管理员 ID |
updatedBy |
String | 最后更新管理员 ID |
createdAt |
LocalDateTime | 创建时间 |
updatedAt |
LocalDateTime | 更新时间 |
relatedVideos |
GrasslandGuideVideoListVO[] |
按手工顺序返回的相关推荐 |
4.3 创建草稿 POST /admin/grassland-guide/videos
创建允许字段暂不完整,返回的初始状态固定为 DRAFT、version=1。
4.4 全量更新 PUT /admin/grassland-guide/videos/{videoId}
创建和更新共用以下字段;更新时额外必传 version。
| 字段 | 类型 | DTO 必填 | 约束/语义 |
|---|---|---|---|
title |
String | 否 | 最长 200;空白按 null |
summary |
String | 否 | 最长 1000;空白按 null |
videoMaterialId |
Long/String | 否 | grassland_guide 分类的可播放 VOD 素材 |
customCoverMaterialId |
Long/String | 否 | 自定义图片封面;兼容输入别名 coverMaterialId |
contentHtml |
String | 否 | 富文本正文 |
contentImageMaterialIds |
Long[]/String[] | 否 | 正文图片素材 ID,元素不能为 null |
featured |
Boolean | 否 | 未传按 false |
showProducedBadge |
Boolean | 否 | 未传按 false |
sortWeight |
Integer | 否 | 未传按 0 |
publishTime |
LocalDateTime | 否 | 首次发布未传时由服务端补当前时间 |
linkedProductId |
Long/String | 否 | 二期预留,一期不做产品跳转 |
relatedVideoIds |
Long[]/String[] | 否 | 最多 3 条,保持数组顺序 |
version |
Integer | 更新必填 | 必须等于详情返回的当前版本 |
更新是全量覆盖,不是 PATCH。 未传布尔值会写为 false,未传
sortWeight会写为 0,未传正文图片/相关推荐会清空,未传自定义封面会切回AUTO。
示例:
{
"title": "呼伦贝尔草原上的风为什么特别大?",
"summary": "从地形和季节理解草原的风。",
"videoMaterialId": "2030000000000001",
"customCoverMaterialId": null,
"contentHtml": "<p>正文内容</p>",
"contentImageMaterialIds": [],
"featured": true,
"showProducedBadge": true,
"sortWeight": 100,
"publishTime": null,
"linkedProductId": null,
"relatedVideoIds": ["2030000000000002", "2030000000000003"],
"version": 1
}
4.5 发布/下线
请求体一致:
{ "version": 2 }
允许的状态流转只有:
DRAFT -> PUBLISHED -> OFFLINE -> PUBLISHED
- 不允许退回
DRAFT。 - 全量更新、发布、下线成功后都会
version + 1。 - 首次从草稿发布且
publishTime为空时,服务端自动填当前时间。 - 下线后重新发布保留首次发布时间。
4.6 发布门禁
发布前必须同时满足:
- 标题非空;
- 有效 VOD 视频素材,媒体状态为
Normal,时长大于 0; - 有生效封面;
- 正文至少有有效文字或一张图片;
- 有发布时间;
- 有 2–3 条相关推荐。
相关推荐不能为 null、不能重复、不能指向自身,且目标内容必须存在。管理端按请求数组顺序保存;小程序详情只返回其中仍为 PUBLISHED 的内容,不自动补位。
富文本会做白名单清洗;img.src 只允许 HTTP/HTTPS,并且必须与 contentImageMaterialIds 对应素材 URL 集合完全一致。
五、VOD 上传与素材注册
5.1 创建上传会话
POST /admin/grassland-guide/vod/upload/create
{
"title": "草原指南测试视频",
"fileName": "grassland-guide.mp4"
}
| 入参字段 | 类型 | 必填 | 约束 |
|---|---|---|---|
title |
String | 是 | 1–128 字符 |
fileName |
String | 是 | 1–255 字符,保留真实扩展名;支持 mp4/mov/avi/mkv/wmv/flv/webm |
返回 data:
| 字段 | 类型 | 说明 |
|---|---|---|
uploadSessionId |
String | 后续 refresh/confirm 使用 |
vodFileId |
String | 阿里云 VideoId |
uploadAddress |
String | 阿里云上传地址,按不透明字符串处理 |
uploadAuth |
String | 阿里云上传凭证,按不透明敏感字符串处理 |
uploadAddress、uploadAuth 禁止写日志、LocalStorage 或业务数据库。
5.2 刷新上传凭证
POST /admin/grassland-guide/vod/upload/refresh
{ "uploadSessionId": "会话ID" }
返回结构同创建接口。阿里云浏览器上传 SDK 触发凭证过期回调时调用此接口,并把新凭证交回 SDK。
5.3 确认上传并注册素材
POST /admin/grassland-guide/vod/upload/confirm
{
"uploadSessionId": "会话ID",
"materialName": "草原指南测试视频"
}
| 字段 | 类型 | 必填 | 约束 |
|---|---|---|---|
uploadSessionId |
String | 是 | 非空 |
materialName |
String | 否 | 最长 200 |
只有阿里云媒体状态为 Normal 时才会确认成功。若视频仍在转码/截图处理中,前端保留会话 ID,稍后重试 confirm,不要重新创建上传会话。重复 confirm 是幂等的,会返回同一个 fileId/materialId,并刷新异步生成的时长和封面。
成功响应:
{
"code": 200,
"message": "成功",
"data": {
"file": {
"fileId": "...",
"storageProvider": "VOD",
"providerFileId": "阿里云VideoId",
"durationSeconds": 125,
"mediaStatus": "Normal",
"thumbnailUrl": "阿里云自动封面URL"
},
"material": {
"materialId": "...",
"categoryCode": "grassland_guide",
"fileType": "VIDEO",
"storageProvider": "VOD",
"providerFileId": "阿里云VideoId",
"durationSeconds": 125,
"mediaStatus": "Normal",
"thumbnailUrl": "阿里云自动封面URL"
}
},
"success": true
}
前端保存 data.material.materialId,作为内容表单的 videoMaterialId。
5.4 推荐的浏览器上传顺序
选择本地视频
-> create 获取 uploadAddress/uploadAuth/uploadSessionId
-> 使用阿里云 VOD 浏览器上传 SDK 直传
-> 凭证过期时调用 refresh
-> SDK 报告上传完成后调用 confirm
-> confirm 返回 materialId
-> 创建/更新草原指南草稿
不要把视频二进制提交给 HL 后端;后端只签发上传凭证、确认媒体状态并登记素材。
5.5 后台预览播放凭证
GET /admin/grassland-guide/vod/materials/{materialId}/play-auth
返回:
| 字段 | 类型 | 说明 |
|---|---|---|
vodFileId |
String | 阿里云 VideoId |
playAuth |
String | 短效播放凭证 |
expiresInSeconds |
Long | 本次凭证有效期,以返回值为准 |
durationSeconds |
Long | 时长(秒) |
coverUrl |
String | 播放器封面 |
每次打开播放器或凭证即将过期时重新请求;不要缓存、持久化 playAuth。
六、自定义封面与正文图片上传
图片继续复用现有素材库 OSS 上传流程:
POST /admin/material/upload/token- 浏览器按返回凭证直传 OSS
POST /admin/material/upload/confirm
请求上传凭证时固定传:
{
"categoryCode": "grassland_guide",
"materialName": "视频自定义封面",
"filename": "cover.jpg",
"fileSize": 123456,
"md5": "32位十六进制MD5",
"contentType": "image/jpeg"
}
- 自定义封面、正文图片必须是
grassland_guide分类的有效图片素材。 - 有自定义封面时传
customCoverMaterialId;不传/null 时后端使用 VOD 自动封面。 - 自动封面读取顺序为阿里云
coverURL,为空时取 snapshots 中第一张有效图片;后端当前不主动发起截图任务。 - 如果阿里云自动封面仍未生成,草稿可以保存,但发布会因缺少生效封面返回
371004;稍后重试发布会先刷新 VOD 元数据。 - 删除、批量删除或移出分类时,如果素材正在被草原指南引用,返回
371009。 - 固定顶级分类本身不能删除、停用或修改编码,保护错误码为
210405。
七、小程序接口详情
7.1 首页 GET /mp/grassland-guide/home
Query:page 默认 1;pageSize 默认 20、最大 100。
返回:
{
"code": 200,
"message": "成功",
"data": {
"featuredVideos": [],
"videos": {
"records": [],
"total": 0,
"page": 1,
"pageSize": 20
}
},
"success": true
}
featuredVideos最多 5 条,只取已发布且featured=true的内容。- 精选排序:
sortWeight DESC -> publishTime DESC -> videoId DESC。 - 没有精选时返回空数组,不使用普通视频兜底。
videos返回全部已发布内容,排序为publishTime DESC -> videoId DESC。
7.2 视频分页 GET /mp/grassland-guide/videos
Query 同首页:page、pageSize。
卡片字段:
| 字段 | 类型 | 说明 |
|---|---|---|
videoId |
String | 内容 ID |
title |
String | 标题 |
summary |
String | 摘要 |
coverUrl |
String | 生效封面 |
coverSource |
String | AUTO/CUSTOM |
durationSeconds |
Long | 时长(秒) |
精选项在卡片字段基础上增加 showProducedBadge。
7.3 视频详情 GET /mp/grassland-guide/videos/{videoId}
在卡片字段基础上增加:
| 字段 | 类型 | 说明 |
|---|---|---|
publishTime |
LocalDateTime | 发布时间 |
contentHtml |
String | 已清洗正文 |
relatedVideos |
视频卡片数组 | 只包含仍在线的推荐内容,保持手工顺序 |
showProducedBadge |
Boolean | 是否显示“呼籁出品”角标 |
7.4 播放凭证 GET /mp/grassland-guide/videos/{videoId}/play-auth
返回字段与后台预览一致:vodFileId、playAuth、expiresInSeconds、durationSeconds、coverUrl。
- 仅已发布内容可获取。
- 接口不缓存,避免返回过期凭证。
- 前端不得把
playAuth当成长期播放 URL。
八、主要错误码
| code | 说明 | 前端动作 |
|---|---|---|
400 |
DTO 校验、枚举或 JSON 格式错误 | 展示具体 message |
401 |
未登录/小程序写请求被拦截 | 走登录或停止请求 |
403 |
非 SUPER_ADMIN/OPERATOR 执行写操作 |
隐藏操作并提示无权限 |
371001 |
草原指南视频不存在或未发布 | 返回列表/显示已下线 |
371002 |
version 冲突 | 重新拉详情后再编辑 |
371003 |
非法状态流转 | 刷新状态 |
371004 |
发布条件不完整 | 按 message 补齐字段 |
371005 |
视频/封面/正文图片素材无效 | 重新选择素材 |
371006 |
相关推荐无效 | 检查数量、重复、自身和目标 ID |
371007 |
正文图片 URL 与素材声明不一致 | 同步 contentHtml 和图片 ID |
371009 |
素材正被草原指南引用 | 先解除内容引用 |
210405 |
固定素材分类受保护 | 不提供删除/停用/改编码入口 |
250507 |
VOD 媒体尚未处理完成 | 稍后重试 confirm |
250511 |
VOD 确认处理中 | 防重复点击,稍后重试 |
503 |
user/resource 下游暂不可用 | 允许重试,不要当业务空数据 |
九、前端实施检查单
- 新建
grassland-guide/GrasslandGuideList并接入动态菜单。 - 内容列表不增加“内容分类”筛选,只保留关键词、状态、精选筛选。
- ID 全程按 String 保存和回传,避免精度丢失。
- 编辑前先拉详情,更新/发布/下线始终提交最新
version。 - 更新接口按全量表单提交,不使用局部 PATCH 心智。
- VOD 采用 create -> SDK 直传 -> refresh(按需)-> confirm 流程。
- 上传凭证只保存在当前上传任务内,不落 LocalStorage、不打印。
- 图片上传固定使用
categoryCode=grassland_guide。 - 自定义封面可清空;清空后展示后端返回的 VOD 自动封面。
- 小程序播放器按需调用 play-auth,并按
expiresInSeconds管理凭证。 - 小程序实现列表骨架屏、空态、加载失败重试;接口空态为 200 + 空数组/空分页。
- 不给小程序增加任何视频、封面或正文图片上传入口。
十、测试环境已验证
测试分支:dev-v3,合并提交:4d4f090dcc47527e247177277313eec636ec0ada。
hl-user-service 8081 / 8181 Nacos healthy=true ✓
hl-resource-service 8082 / 8182 Nacos healthy=true ✓
hl-mp-service 8085 / 8185 Nacos healthy=true ✓
hl-gateway 8080 / 8180 Nacos healthy=true ✓
GET /mp/grassland-guide/home -> 200,精选空数组 + 空分页 ✓
GET /mp/grassland-guide/videos -> 200,PageResult total=0 ✓
GET /mp/grassland-guide/videos/{不存在ID} -> code=371001 ✓
POST /mp/grassland-guide/home -> code=401 ✓
GET /admin/grassland-guide/videos(未登录) -> code=401 ✓
GET /admin/grassland-guide/videos(管理员) -> 200,total=0 ✓
GET /admin/menu/my -> 草原指南菜单及组件路径存在 ✓
GET /admin/dict/data/material_category -> grassland_guide=ACTIVE ✓
POST /admin/grassland-guide/vod/upload/create(空请求) -> code=400,路由与校验生效 ✓
阿里云 VOD GetCategories -> HTTP 200,当前 AK 具备 VOD 访问权限 ✓
Flyway:
- User
V20260712_002、V20260712_004成功; - Resource
V20260712_001成功; - 菜单 7 条、固定分类 1 条、VOD 会话表、3 张草原指南内容表均已落库。
本轮没有创建真实云端测试视频,以免产生孤儿 VideoId/上传会话;真实大文件上传仍需前端联调时用测试视频执行一次 create -> 直传 -> confirm。
十一、不影响范围
- 不修改现有通用素材列表接口。
- 不增加小程序素材库上传或分页接口。
- 不修改
hl-ui现有代码;本文为前端实施契约。 linkedProductId仅持久化,产品跳转留到二期。