上线指南
This commit is contained in:
1 parent
a81c637a98
commit
6b263af74f
1 file changed
+247
+247
@@ -0,0 +1,247 @@
|
||||
# 课程上线指南
|
||||
|
||||
面向「要往小程序里上一门新课」的人。按第 7 节的顺序做,每一步都有验证方法。
|
||||
|
||||
---
|
||||
|
||||
## 0. 先理解三件事
|
||||
|
||||
1. **数据分两处**:课程 / 课时的**文字信息**在数据库里(管理后台改的就是它);
|
||||
**视频和图片**在数据端,是磁盘上的文件。
|
||||
2. **付费课时不是 mp4**,是 HLS 目录(一个 `playlist.m3u8` + 一堆 `.ts` 分片)。
|
||||
3. **媒体文件不在云服务器上**。数据域名整站反代到隧道另一端(NAS),服务端播放视频也走同一条路。
|
||||
所以文件最终要落到 NAS 上对应的目录里,放对位置就立刻生效。
|
||||
|
||||
> ⚠️ 目前**还没有后台上传功能**。文件要手工(或打包成 zip)放到 NAS 的对应目录,
|
||||
> 再回管理后台填表单。
|
||||
|
||||
---
|
||||
|
||||
## 1. 三个不能随便起的名字
|
||||
|
||||
这三个名字一旦定下来就**不要改**,它们是文件路径的一部分:
|
||||
|
||||
| 名字 | 同时还是 |
|
||||
|---|---|
|
||||
| `courses.code` | 视频目录名 `Course/{code}/` 和封面名 `Course/{code}.png` |
|
||||
| `lessons.chapter_id` | 目录中的一级:`Course/{code}/{chapter_id}/` |
|
||||
| `lessons.play_id` | 目录中的二级:`Course/{code}/{chapter_id}/{play_id}/` |
|
||||
|
||||
改其中任何一个,已经发出去的播放地址、封面、以及用户端的缓存全部失效。
|
||||
|
||||
另外一批名字是**拼出来的**,由后台填的文字决定,同样不能随手改:
|
||||
|
||||
| 小程序里的地址 | 由什么拼出来 |
|
||||
|---|---|
|
||||
| `Course/{code}.png` | 课程 `code` |
|
||||
| `Images/NewCourse/{条目名}.png` | 首页新课的 `course_name` |
|
||||
| `Images/FreeCourse/{条目名}.png` | 首页试听的 `course_name` |
|
||||
| `Images/Banner/{文件名}` | 轮播图的 `filename`(含扩展名) |
|
||||
| `WebPages/{网页id}.png` | 文章的 `legacy_page_id` |
|
||||
|
||||
现网示例:`code` 是 64 位十六进制,`chapter_id` 是 `1`、`2`、`3`,
|
||||
`play_id` 是 13 位十六进制(如 `65eefb35e29da`)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 要准备的文件
|
||||
|
||||
假设新课的 `code` 是 `AbC...`(64 位),有三个章节、每章两个课时:
|
||||
|
||||
```
|
||||
Course/AbC....png ← 课程封面,1 张
|
||||
Course/AbC.../1/65eefb35e29da/playlist.m3u8 ← 课时 1
|
||||
Course/AbC.../1/65eefb35e29da/0000.ts 0001.ts ... ← 该课时的分片
|
||||
Course/AbC.../1/65eefb35e29db/playlist.m3u8 ← 课时 2
|
||||
Course/AbC.../2/65eefb50f02b5/playlist.m3u8 ← 课时 3
|
||||
...
|
||||
```
|
||||
|
||||
如果这门课还要上首页,另外准备:
|
||||
|
||||
| 用途 | 文件 | 引用方式 |
|
||||
|---|---|---|
|
||||
| 首页「新课上线」列表图 | `Images/NewCourse/{条目名}.png` | 按 `course_name` 拼 |
|
||||
| 首页「新课上线」长图内页 | `Images/NewCourseWebPages/{分组名}/{n}.png` | 写在长图 HTML 里 |
|
||||
| 首页「试听」列表图 | `Images/FreeCourse/{条目名}.png` | 按 `course_name` 拼 |
|
||||
| 免费试听视频 | `FreeVideos/{任意目录}/{任意名}.m3u8` + 分片 | 路径存在数据库字段里 |
|
||||
|
||||
**图片规格参考**
|
||||
|
||||
- 课程封面:现网是 **900×500**(后台卡片按 9:5 显示,整张完整展示不裁剪)。
|
||||
- 新课长图:现网每张 **800px 宽**,高度按内容(实测一张 800×564)。所有图宽度保持一致。
|
||||
- 首页列表图:按名称拼 `.png`,建议和长图同宽或按 2 倍图准备。
|
||||
|
||||
---
|
||||
|
||||
## 3. HLS 打包要求
|
||||
|
||||
现网一个真实的 `playlist.m3u8`:
|
||||
|
||||
```
|
||||
#EXTM3U
|
||||
#EXT-X-VERSION:3
|
||||
#EXT-X-TARGETDURATION:4
|
||||
#EXT-X-MEDIA-SEQUENCE:0
|
||||
#EXTINF:4.166667,
|
||||
0000.ts
|
||||
#EXTINF:4.166667,
|
||||
0001.ts
|
||||
```
|
||||
|
||||
四条硬要求:
|
||||
|
||||
1. **分片引用必须是相对路径**(`0000.ts`),不能写 `http://...` 绝对地址。
|
||||
服务端会给每一行分片单独签名,绝对地址会被跳过——防盗链直接失效,播放器还会绕过服务端直连。
|
||||
2. **文件名必须叫 `playlist.m3u8`**。这个名是服务端写死的,不是数据库里的字段。
|
||||
3. **目录名只用字母数字**。`chapter_id` / `play_id` 参与播放地址的拼接与签名,中文和空格容易踩坑。
|
||||
4. **目录里不要放无关文件**,整棵目录树都在签名保护下。
|
||||
|
||||
其余参数(切片时长、编码、加密)没有强制要求,按上面的形状切即可。
|
||||
如果做了 HLS 加密,`.key` 文件要放在同一目录树内,`EXT-X-KEY` 的 URI 也用相对路径。
|
||||
|
||||
---
|
||||
|
||||
## 4. 首页新课长图的内容要求
|
||||
|
||||
「长图」不是一张图片文件,而是 `home_courses.content` 里的一段 **HTML**,
|
||||
服务端原样返回,小程序用内置的 Parser 组件渲染。现网真实内容:
|
||||
|
||||
```html
|
||||
<p style="font-size: 0; margin: 0; padding: 0;">
|
||||
<img src="https://<数据域名>/Images/NewCourseWebPages/几何模型/1.png"/>
|
||||
<img src="https://<数据域名>/Images/NewCourseWebPages/几何模型/2.png"/>
|
||||
<img src="https://<数据域名>/Images/NewCourseWebPages/几何模型/3.png"/>
|
||||
</p>
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
1. **图片必须是绝对 URL**(带 `https://<数据域名>`)。写相对路径会裂图——小程序会把 `/xxx.png` 当成包内的本地文件。
|
||||
2. **外层 `<p>` 必须带 `font-size: 0`**(通常还带 `margin:0; padding:0`)。少了它,图与图之间会露出白缝,长图就断了。
|
||||
3. **不要给 `<img>` 写 width / height**。渲染时会自动补 `width:100%` 按屏宽缩放;写死像素宽度会溢出屏幕。
|
||||
4. **底部要留白**。长图页底部钉着两个悬浮按钮(购买 / 咨询客服),末尾留一段空白,否则按钮压住内容。
|
||||
5. **只能放静态标签**。`<script>`、`<iframe>`、`<video>` 不会执行。
|
||||
6. 长图里的图片路径是**手写在 HTML 里**的,改名或挪目录内容不会跟着变,会直接 404。所以图片一次定稿再填。
|
||||
|
||||
---
|
||||
|
||||
## 5. 后台要填的表单
|
||||
|
||||
### 课程(管理后台 → 课程)
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| code | 64 位十六进制串,**创建后不可改** |
|
||||
| title | 课程名 |
|
||||
| intro | 一句话介绍 |
|
||||
| price_fen | 价格,单位**分**(1.10 元填 110) |
|
||||
| status / sort_order | 上架状态、排序 |
|
||||
|
||||
### 课时(管理后台 → 课时,每个视频一行)
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| course_id | 所属课程 |
|
||||
| chapter_id / play_id | 必须和磁盘目录**逐字对应** |
|
||||
| chapter_title / lesson_title | 章节名、视频名 |
|
||||
| duration | 现网存的是分钟数,如 `14`、`21` |
|
||||
| published_at / sort_order | 发布日期、排序 |
|
||||
|
||||
约束:`(course_id, chapter_id, play_id)` 唯一——同一章里不能有重复的 `play_id`。
|
||||
|
||||
### 支付商品(管理后台 → 支付商品,要卖就得填)
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| course_id | 对应课程 |
|
||||
| wx_product_id | 微信后台的道具 ID |
|
||||
| price_fen | **必须和微信后台道具价格完全一致**,否则下单会被拒 |
|
||||
| name / status | 商品名、是否启用 |
|
||||
|
||||
### 首页新课(管理后台 → 首页新课,要上首页才填)
|
||||
|
||||
| 字段 | 说明 |
|
||||
|---|---|
|
||||
| course_name | 条目名,**决定列表缩略图** `Images/NewCourse/{条目名}.png` |
|
||||
| content | 第 4 节那段长图 HTML |
|
||||
| course_id | 关联到可购买的课程;**留空则长图页不显示购买按钮**,只剩客服按钮 |
|
||||
| intro / published_at / sort_order / status | 列表展示用 |
|
||||
|
||||
### 首页试听(管理后台 → 首页试听 + 免费试听课)
|
||||
|
||||
- 首页试听:`series_no`(要和免费试听课的序号对上)、`course_name`(决定 `Images/FreeCourse/{条目名}.png`)、排序、状态。
|
||||
- 免费试听课:`series_no`、`course_name`、`video_title`、`duration`、`video_m3u8`(如 `FreeVideos/9102/9102.m3u8`)、`cover_image`(如 `Images/FreeCourse/xxx.png`)。
|
||||
**这两个字段是数据库里存的全路径**,不是拼出来的,可以自由命名。
|
||||
|
||||
> 注意:课程表 / 首页新课 / 课时表里都有一个「封面文件 / cover_image」字段,
|
||||
> **接口目前不使用它们**,真正生效的是第 1 节里「拼出来的」那些地址。
|
||||
> 只有免费试听课的 `cover_image` 是真正生效的。
|
||||
|
||||
---
|
||||
|
||||
## 6. 微信小程序后台要先做的
|
||||
|
||||
要在小程序里卖这门课,必须先建支付道具:
|
||||
|
||||
1. 【虚拟支付 → 道具管理】新建道具,拿到**道具 ID** 与价格。
|
||||
2. 价格记下来,后台「支付商品」里要填一模一样的分值。
|
||||
3. 道具建好后要**发布**,未发布的道具下不了单。
|
||||
|
||||
---
|
||||
|
||||
## 7. 操作顺序
|
||||
|
||||
| 步骤 | 做什么 | 怎么验证 |
|
||||
|---|---|---|
|
||||
| 1 | 微信后台建支付道具,记下道具 ID 和价格 | 道具状态是已发布 |
|
||||
| 2 | 定下课程的 `code`(64 位十六进制) | —— |
|
||||
| 3 | 把封面和所有课时目录放到数据端对应路径 | 浏览器直接打开 `Course/{code}.png` 能显示 |
|
||||
| 4 | 后台建**课程**行,`code` 填上面那个 | 小程序「知识」页能看到课程、封面正常 |
|
||||
| 5 | 后台建**课时**行,`chapter_id` / `play_id` 和目录逐字对上 | 买过课之后能播放;没买时报「尚未拥有该课程」 |
|
||||
| 6 | 后台建**支付商品**行,价格与微信一致 | 走一笔小额真实订单,能下单成功 |
|
||||
| 7 | 首页要展示的话,建**首页新课 / 首页试听**并填长图 | 首页能看到条目、点进去长图完整、底部按钮不挡内容 |
|
||||
| 8 | 课程 `status` 改为 1(上架) | 小程序下拉刷新后可见 |
|
||||
|
||||
> ⚠️ **上架前先确认后台左上角的「查看的数据源」是生产库。**
|
||||
> 后台有两个概念:「查看的数据源」(只影响你自己看到什么)和「线上小程序数据源」(影响所有用户)。
|
||||
> 在开发库建的课程,用户在线上是看不到的。
|
||||
|
||||
---
|
||||
|
||||
## 8. 上线后验证清单
|
||||
|
||||
- [ ] 首页能看到新课条目,缩略图正常
|
||||
- [ ] 点进长图页:图片连成一张、没有白缝、底部按钮不挡住内容
|
||||
- [ ] 长图页的「立即支付」按钮存在(说明 `course_id` 关联上了)
|
||||
- [ ] 「知识」页能看到这门课的封面
|
||||
- [ ] 未购买的账号点课程,看不到课时列表
|
||||
- [ ] 走一笔小额真实支付:能拉起支付 → 支付成功后课程出现在「已购」里
|
||||
- [ ] 已购账号能播放每一个课时(不是只测第一个)
|
||||
- [ ] 后台「订单」页能看到这笔订单,状态「已开通」
|
||||
- [ ] 后台「数据看板」的累计订单 +1、成交金额增加
|
||||
|
||||
---
|
||||
|
||||
## 9. 常见坑
|
||||
|
||||
| 现象 | 原因 | 处理 |
|
||||
|---|---|---|
|
||||
| 封面 404 | 文件名和 `code` 对不上,或图片没放上去 | 地址必须是 `Course/{code}.png`,逐字核对 |
|
||||
| 播放地址 404 / 播不了 | `chapter_id`、`play_id` 和目录名不一致 | 三处(数据库、目录、表单)必须逐字相同 |
|
||||
| 长图断了、有白缝 | 外层 `<p>` 少了 `font-size: 0` | 见第 4 节 |
|
||||
| 长图裂图 | 图片用了相对路径 | 必须写完整的 `https://<数据域名>/...` |
|
||||
| 换了同名图片但没变 | 数据端对图片有 1 分钟缓存 | 等 1 分钟,或清 Nginx 缓存后 reload |
|
||||
| 换了同名视频但播还是旧的 | 播放地址响应头带 5 分钟缓存 | 等 5 分钟;播放地址本身有效期是 30 分钟 |
|
||||
| 下单提示「该课程尚未配置支付商品」 | `products` 里没有启用的商品行 | 见第 5 节 |
|
||||
| 下单后拉不起支付 | 微信道具没发布,或价格与后台不一致 | 见第 6 节 |
|
||||
| 长图页没有购买按钮 | `home_courses.course_id` 为空 | 填上关联课程 |
|
||||
| 改完没生效 | 后台看的是开发库 | 切回生产库再操作 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 相关文档
|
||||
|
||||
- [部署指南.md](部署指南.md):服务端 / 后台 / 数据端怎么部署
|
||||
- [重构开发文档.md](重构开发文档.md):整体设计与数据模型
|
||||
- [数据端 README](../yelaymaths_data.yeomai.site/README.md):数据端目录结构与路径约定
|
||||
Reference in new issue
Block a user