看到“基于微信小程序的在线学习系统springboot(文档+源码)”这种命名,大概率是正在做毕业设计、课程设计,或者想快速接手一个前后端分离实战项目的朋友。这类项目的价值不在于那一堆能跑的代码,而在于把一套在线学习产品从页面交互、后端接口到数据库设计完整串起来的能力。这篇我打算换个讲法,不只带你跑通demo,而是把我在实际开发、部署、演示过程中反复用到的东西都拆开说:小程序端的列表加载更多、顶部导航栏适配、登录态怎么保持,后端Spring Boot的接口分层和JWT认证,以及拿到源码后从建库到真机预览的完整路线。适合正在忙毕设的学生,也适合想用小程序加Spring Boot快速搭学习类产品的开发者做参考。
1. 先看懂这个项目:一套在线学习系统的功能地图
1.1 项目命名里的信息量
“weixin222基于微信小程序的在线学习系统springboot(文档+源码)_kaic”,这个文件名看起来像随机拼凑,实际上信息量不小:
- weixin222:指向微信小程序端,“222”一般是发布者或仓库内部的项目编号,和功能无关。
- 在线学习系统:核心业务领域,说明系统围绕课程展示、学习行为、进度记录这类场景展开。
- springboot:后端技术栈,对应的是Java生态。
- 文档+源码:交付物形式,意味着不只是代码,还配套了数据库脚本、说明文档、部署步骤之类的内容,我经手的同类项目里,文档一般还会包含需求说明和接口清单。
这个命名方式在毕设项目和源码交易平台里很常见。对我这种常年看各种项目的人来说,翻译成人话就是:这是一个前后端分离系统,小程序负责展示和交互,Spring Boot负责业务逻辑与数据存取,两者通过HTTP接口通信。只要你把这条主链路想清楚,后面看代码会快很多。
1.2 核心角色与业务链路
在线学习系统和电商系统不一样,它没有太复杂的订单和库存,核心逻辑可以压缩成一条链:学生登录,浏览课程列表,点进课程详情,看章节内容(视频、文档),系统记录学习进度,学生可以收藏、评论、选课,后台由老师或管理员维护课程内容。
从角色维度看,这类系统一般都拆成三类:
- 学生:浏览课程、选课、学习、提交进度、收藏、评论。
- 教师或内容提供者:后台上传课程、管理章节,偶尔回复评论,很多毕设项目里教师角色会合并到管理员里。
- 管理员:用户管理、课程上下架、数据统计。
拿到项目源码后,我习惯先看三张东西:SQL脚本里的表结构、后端Controller的接口列表、小程序端的页面目录。只要把这三样对应起来,整个系统的功能边界就清楚了。很多同学一上来就埋头看某个类的源码,反而会陷入细节出不来。
1.3 动手前必须想清楚的三个问题
在写任何代码之前,我会先回答三个问题,这三个问题也决定了后面所有表结构和接口的设计:
第一,课程资源是什么形态。视频是放在本地服务器,还是用腾讯云、阿里云的视频服务,还是直接引用第三方视频链接?这决定了资源表怎么设计,小程序播放器怎么接入。常见做法是视频转成MP4格式放服务器或对象存储,课程表里存视频URL。
第二,游客能不能看内容。如果允许游客看课程简介和目录,但不允许看视频,那课程列表和课程详情的接口就要区分是否需要登录;如果所有内容都要登录才能看,那接口统一加上鉴权即可。学习类系统通常选择后者,体验也更合理。
第三,部署环境在哪。本地用localhost调试,和部署到云服务器后通过域名访问,两者的配置差别很大,主要影响小程序的合法域名和HTTPS证书配置。很多项目卡在这一步,不是代码问题,而是环境问题。
2. 技术选型:微信小程序和Spring Boot为什么是绝配
2.1 小程序端的优势与边界
为什么大量在线教育、知识付费、在线学习项目都选择微信小程序而不是原生App或者H5?核心原因有几点:第一,微信生态天然带了流量入口,分享到群里点开就能用,不需要下载安装,用户心理门槛低。第二,小程序开发语法接近前端,上手快,微信开发者工具做调试很顺手。第三,微信官方审核体系成熟,发布时走一遍审核就行。
但小程序也有很明显的边界,做在线学习系统时尤其要留意:
- 包体限制。微信小程序主包默认有大小限制,资源一多很容易超限,这是做视频课程类小程序最常见的坑。
- 网络环境。小程序的请求域名必须是HTTPS,而且要在小程序后台配置合法域名,这在本地开发时可以关掉校验,但一上线就绕不开。
- 页面栈限制。小程序页面层级最多十层,随便跳来跳去会顶到上限,后面我会讲怎么处理。
2.2 Spring Boot在后端扮演的角色
Spring Boot能成为这类系统的默认选择,我理解是三个原因叠在一起:
一是开箱即用,内嵌Tomcat,不用单独装容器,一个java -jar命令就能跑起来,对毕设和中小型项目非常友好。二是生态太丰富,官方Starter和第三方库覆盖了数据库、缓存、安全、文件上传这些常见需求,写代码量大大减少。三是分层结构清晰,Controller负责接口,Service写业务逻辑,Mapper操作数据库,和前端按接口对接时天然顺滑。
从答辩和找工作的角度来说,Java技术栈也是最容易讲清楚的,面试官一问“系统怎么设计的”,你可以从后端分层讲到数据库设计,再讲到前端交互,整个链路非常完整。
2.3 配套组件选型表
这类在线学习系统的常见组合,我按主流做法列一下,你可以根据项目实际情况调整:
| 组件 | 推荐方案 | 用途说明 |
|---|---|---|
| 数据库 | MySQL 5.7或8.0 | 存用户、课程、章节、学习记录等核心数据 |
| ORM | MyBatis-Plus | 简化CRUD,自带分页插件和逻辑删除 |
| 鉴权方案 | JWT + 拦截器 | 前后端分离场景下保持登录态 |
| 文件存储 | 本地目录 / 对象存储 | 存放课程视频、封面图、课件 |
| 接口调试 | Apifox / Postman | 本地启动后端后快速验证接口 |
| 接口文档 | Knife4j(可选) | 生成Swagger风格接口文档 |
需要注意,这些组件不是标题里直接写出来的,而是在我处理过的多个同类项目里最常用的搭配。拿到具体项目后,先看它的pom.xml和application.yml,里面写了什么就用什么,不要上来就换版本。
3. 数据库设计:用表结构还原整个学习业务
3.1 用户表与openid的来龙去脉
在线学习系统既然依托微信小程序,用户表的设计有一个关键字段绕不开:openid。
openid是微信生态里每个用户在某个小程序下的唯一标识,小程序端通过wx.login()拿到临时code,后端拿着code调用微信接口换回openid。所以用户表的设计通常是:id主键、openid、昵称、头像、角色、手机号(可选)、创建时间。
这里有一个我反复强调的点:前端不要直接把openid当用户主键用,也不要随意传给后端接口。正确做法是后端拿到openid后映射成自己系统里的userId,后续所有接口都围绕userId来做,JWT令牌里放userId就行了。这样即使openid泄露,也不会被伪造身份。
角色字段我建议用int类型存,比如0学生、1教师、2管理员,不要用字符串。原因是后续做权限判断时,整型比较比字符串高效,也方便扩展。
3.2 课程、章节与资源的三层结构
在线学习系统的内容部分,我习惯拆成三层:
课程表(course):id、标题、封面图、课程简介、难度、分类id、价格(0表示免费)、状态(上架/下架)、教师id、创建时间。
章节表(chapter):id、课程id、章节标题、排序字段、视频地址、视频时长。一个课程对应多个章节,这是经典的父子关系。
资源表(resource):id、章节id、文件名、文件URL、资源类型(视频/PPT/PDF)。一个章节可以挂多个资源,方便做课件下载和视频播放。
为什么一定要把章节和资源分开?因为课程学习进度通常要精确到章节,如果只有课程没有章节,用户学到第几分钟、看到第几节课都没有办法记录。把粒度拆到章节,后面做学习进度、续播、完成状态都非常好处理。
3.3 学习行为表:进度、选课与评论
用户的实际学习行为比内容表更复杂,我主要讲三张行为表:
选课/收藏表(user_course):id、用户id、课程id、类型(0选课、1收藏)、创建时间。这张表同时承载“我选的课”和“我收藏的课”,用类型字段区分。为了避免重复选课或重复收藏,建议在用户id和课程id上加唯一索引。
学习进度表(study_progress):id、用户id、章节id、视频播放位置、总时长、更新时间。用户在视频播放中暂停或退出时,把当前播放位置上报给后端,下次打开时调接口拿到position,从断点续播。这里唯一索引要建立在user_id加chapter_id上,幂等更新,防止产生重复记录。
评论表(comment):id、用户id、课程id(或章节id)、评论内容、回复的目标评论id、创建时间。评论适合挂在课程粒度,回复可以通过目标评论id形成评论树。
还有一点容易被忽略:如果系统要做打卡、考试或答题,还需要对应的打卡表、试卷表和答题记录表,粒度自己控制即可。核心原则是:所有用户主动产生的数据,都要记录是谁操作的、操作的对象是谁、操作时间是什么,这三要素缺一不可。
3.4 建表时容易踩的坑
我在看别人的毕设项目时,数据库这块问题最多,说几个高频坑:
视频时长字段用varchar存字符串。不要这样存,建议用int类型存秒数,比如“12分30秒”存成750,前端展示时再转成“12:30”,计算进度和剩余时长都方便。
价格字段用float。价格建议用decimal(10,2),浮点数做金额计算会丢失精度,在线付费课程尤其要注意。
所有表没有逻辑删除字段。用MyBatis-Plus时,加一个deleted字段并配合@TableLogic注解,默认0正常1删除,查询自动过滤。管理端“删除课程”时实际上做的是隐藏,不会真删数据,这对内容审核类系统很重要。
时间字段类型不统一。建议全部用datetime,Java实体对应LocalDateTime,前端拿到后统一做格式化,避免时区转换问题。
4. 小程序前端:那些反复被搜索的实现细节
4.1 课程列表分页与“加载更多”
“微信小程序页面列表加载更多”这个搜索词我太有共鸣了,做在线学习系统的人几乎都会遇到。课程列表页最合理的交互方式就是滚动到底部自动加载下一页,核心实现逻辑其实不复杂:
页面数据里维护page、pageSize、hasMore、list四个字段。滚动到底部时触发onReachBottom方法,判断hasMore为true且当前没有请求正在进行,才发起请求。后端返回当前页数据和总条数,前端把返回的新数据append到list里,同时page加1。底部显示状态根据hasMore切换为“上拉加载更多”或“没有更多了”。
这里有两个细节一定要处理好。一是防重复请求,在请求过程中用一个loading标志位,防止手指连续滑动时发出多次请求;二是onReachBottom触发频率实际上比想象高,如果不加标志位,很容易出现数据重复或乱序。实测下来,用标志位加page判断,分页基本就稳了。
另外,如果课程有封面图,列表页图片建议用lazy-load属性做懒加载,课程数量一多,图片加载对滚动性能的影响非常明显。
4.2 自定义导航栏高度适配
在线学习系统的课程详情页和播放页,很多设计师喜欢用自定义导航栏实现沉浸式效果,但自定义导航栏有一个老生常谈的问题:不同机型的状态栏高度和胶囊按钮位置不一样,写死44px在全面屏手机上一定翻车。
我的标准做法是:在页面的onLoad里调用wx.getSystemInfoSync()拿到statusBarHeight,再调用wx.getMenuButtonBoundingClientRect()拿到胶囊按钮的top和height。导航栏高度用这个公式计算:(capsule.top - statusBarHeight) * 2 + capsule.height。
这个公式不是我发明的,是微信生态里验证过很多次的通用方案。算出导航栏高度后,整个页面的顶部占位区域也就确定了,下边内容可以放心往下排。要注意的是,不同基础库版本对getSystemInfoSync的支持略有差异,建议做一个公共的工具函数,全局统一调用。
4.3 登录流程与请求封装
在线学习系统几乎都要登录,尤其是要记录学习进度、收藏、选课这些功能。我推荐的登录流程是:
进入小程序后,先检查本地storage里有没有token;没有就调wx.login()拿code,把code发到后端接口,后端拿code换openid并注册或登录用户,返回token和用户信息;小程序把这个token存下来,之后每次请求都在请求头带上token。
这套流程看起来简单,但有几个细节值得注意。第一,wx.login()拿到的code有效期很短,必须在后端及时换取登录态,不要存到本地留着以后用。第二,后端返回的token要设置过期时间,小程序端要主动处理401状态,出现401时清掉本地token并重新执行登录流程。第三,请求封装建议用一个公共的request方法,统一拼baseURL、统一处理错误码、统一展示错误提示,避免每个页面各自处理。
我在项目里通常会把baseURL单独放在一个config.js里,切换环境时只改一处。这样做有一个直接的好处:本地调试用http://localhost:8080,上线前改成正式HTTPS域名,只需要动一个文件。
4.4 表单、单选与离开监听的细节
学习类小程序里,表单交互看起来不难,实际坑不少。比如选课、支付方式、答题选项这些场景经常用到单选框,微信原生的radio-group和radio样式比较朴素,而且默认样式在不同机型上有差异。我的做法是:数据量少的交互尽量用自定义样式,点击整行触发选中,选中的状态通过data里的字段控制;时机合适时也可以用picker替代单选,交互更接近微信原生习惯,选择题答案也可以用这种方式。
还有两个高频搜索词值得一起说。
“微信小程序如何监听用户离开小程序”:做视频课程时非常关键,用户播放视频中切到后台或退出小程序,必须在onHide生命周期里暂停视频并上报播放位置。如果只在onUnload里上报,很多用户是直接左滑退出的,根本不会触发卸载事件,进度就会丢。
“微信小程序顶部导航栏高度”前面已经讲了,这里再提醒一句:沉浸式页面如果要适配“灵动岛”和不同状态栏高度,最好在页面可见性变化时重新计算一次,不要只算一次就缓存固定值。
5. Spring Boot后端:从接口定义到代码落地
5.1 分层架构与统一返回体
后端代码如果没分层,写到后面一定乱。我处理这类在线学习系统时,用的是几乎成了行业标准的四层结构:Controller接收请求参数,Service处理业务逻辑,Mapper操作数据库,实体类对应表结构。
以课程列表接口为例,请求链路是:前端传pageNum和pageSize,Controller接收后转给Service,Service调用MyBatis-Plus的分页插件查询数据库,最终把列表数据和总数封装成统一返回体返回。
统一返回体我用一个Result<T>类来实现,字段就三个:code、message、data。成功时code为200,业务异常时code为其他值,前端根据code决定是正常渲染还是弹错误提示。这个设计在前后端分离项目里非常关键,否则每个接口返回格式都不一样,小程序端解析逻辑会写得想骂人。
5.2 JWT登录态与拦截器设计
在线学习系统后端最核心的鉴权环节,我用的是JWT加拦截器的组合。登录成功后,后端把用户id放进JWT的payload里,设置过期时间,用密钥签名后返回给小程序。小程序每次请求都在header里带token,后端拦截器统一解析。
拦截器的落地步骤大概是:
定义一个JwtInterceptor实现HandlerInterceptor,在preHandle里从请求头取出token,调用JWT工具类解析。解析失败或过期就直接返回401结果,解析成功就把token里的userId放到ThreadLocal或请求属性里,供后续Controller使用。还需要在WebMvcConfigurer里注册拦截器,并配置哪些路径放行(比如登录接口、公开课程列表接口),哪些路径需要拦截。
这里有一个细节:学习进度上报接口必须带登录态,但课程列表首页如果想允许游客浏览,就可以放行。权限设计要看产品需求,不是所有接口都要锁死。
5.3 核心接口示例:分页列表与学习进度上报
课程分页接口的逻辑很标准,核心代码如下:
@GetMapping("/api/course/page") public Result<PageResult<CourseVO>> page(@RequestParam Integer pageNum, @RequestParam Integer pageSize) { Page<Course> page = new Page<>(pageNum, pageSize); LambdaQueryWrapper<Course> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Course::getStatus, 1); // 只查上架课程 wrapper.orderByDesc(Course::getCreateTime); courseMapper.selectPage(page, wrapper); PageResult<CourseVO> result = new PageResult<>(); result.setList(...); // 转为前端需要的VO result.setTotal(page.getTotal()); result.setPageNum(pageNum); result.setPageSize(pageSize); return Result.success(result); }学习进度上报的接口则要强调幂等:
@PostMapping("/api/progress/report") public Result<ProgressVO> report(@RequestBody ProgressReportDTO dto) { // 从ThreadLocal里取当前用户id Long userId = UserContext.getUserId(); StudyProgress progress = progressMapper.selectOne( new LambdaQueryWrapper<StudyProgress>() .eq(StudyProgress::getUserId, userId) .eq(StudyProgress::getChapterId, dto.getChapterId())); if (progress == null) { // 不存在则插入 } else { // 存在则更新播放位置 } return Result.success(progressVO); }为什么一定要做幂等?因为小程序端在视频暂停、切后台、退出页面等多个时机都可能触发上报,后端如果不处理重复请求,数据库里会攒下一堆脏数据。
5.4 全局异常、参数校验与安全防护
后端还有一个环节经常被忽略,就是全局异常处理。用@RestControllerAdvice加@ExceptionHandler统一捕获异常,把异常信息转成标准返回体,前端就不会看到默认的错误堆栈页面。业务异常可以自定义一个BusinessException,在Service层主动抛出给全局处理器。
参数校验用javax.validation的注解就好,比如@NotNull、@Min、@Size。Controller的方法参数加上@Valid注解后,参数不合法会被框架拦截并抛异常,统一异常处理器会返回格式一致的错误信息。
安全这块,MyBatis-Plus的#{}参数处理已经天然防住了大部分SQL注入;管理端接口建议加上角色校验,可以在拦截器里判断该用户的role是否允许访问,也可以自定义权限注解。在线学习系统里,老师和管理员能调用的接口远多于学生,这个权限边界一定要在接口层控制住。
6. 拿到“文档+源码”后的快速启动路线
6.1 交付包里通常装了哪些东西
这类“文档+源码”的项目,交付物的构成大同小异,我拆给你看:
- database目录:SQL脚本,一般是用数据库管理工具导出的,包含建库建表语句和初始数据。
- server或backend目录:Spring Boot后端源码,Maven工程结构。
- miniapp或weixin目录:微信小程序前端源码。
- 文档:需求说明、数据库设计、部署手册、使用说明,有些还会带答辩PPT。
- README:简短的项目介绍和启动步骤。
拿到项目后,我强烈建议先看文档里的数据库说明和部署手册,再打开SQL脚本确认表结构,最后再让后端跑起来。不要一上来就双击导入IDE,代码是跑不起来的,必须先建库。
6.2 环境版本怎么对齐
Spring Boot项目的版本兼容问题是最常见的启动失败原因,我这里列一个常用对照表给你参考:
| Spring Boot版本 | JDK版本 | MyBatis-Plus建议版本 | 说明 |
|---|---|---|---|
| 2.3.x | JDK8 | 3.3.x | 老项目常见,稳定 |
| 2.7.x | JDK8 / JDK11 | 3.5.x | 市面上大量毕设项目使用 |
| 3.0.x及以上 | JDK17 | 3.5.3.1及以上 | javax换成了jakarta包 |
注意,如果项目用的是Spring Boot 3.x,代码里原来的javax.servlet都会变成jakarta.servlet,自定义拦截器或过滤器可能会编译报错。遇到这种问题,先检查pom.xml里声明的Spring Boot版本,不要盲目升级依赖。
MySQL版本也要和驱动对应,Spring Boot 2.7.x配mysql-connector-java 8.0即可,Spring Boot 3.x建议用新版驱动。这些看似小的问题,实际排查起来会耗掉半天时间,提前对齐是最好的办法。
6.3 最小化启动流程
新手最容易卡住的点是不知道先做什么。我按正常人能理解的最小路径写一遍:
第一步,用Navicat或命令行执行SQL脚本,把数据库建好。修改后端application.yml里的数据库用户名和密码,确保能连上库。
第二步,用IDEA打开后端目录,右下角等Maven把依赖下载完。不要急着点运行,先执行mvn compile看能不能编译通过。编译报错就先解决版本问题。
第三步,启动后端主类,看到Spring Boot的启动日志里出现Tomcat started后就说明后端起来了。用Apifox或Postman先测一个公开接口,比如课程列表接口,确认能返回JSON数据。
第四步,用微信开发者工具导入小程序目录,在config.js或utils/request.js里把baseURL改成http://localhost:8080/api,并在开发者工具右上角“详情”里勾选“不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书”,这是本地联调的标配操作。
第五步,编译小程序,看首页数据能不能加载出来。如果数据正常,整个链路就通了,后续再去调登录、选课、进度上报这些功能。
6.4 把小程序发给别人试用的正确姿势
很多人急着给导师或朋友看demo,但不知道小程序怎么分享。这里有两种方式:
第一种是体验版。登录微信公众平台,把对方微信号加为项目成员(体验者权限),然后在微信开发者工具里点击“上传”,把代码传到后台,再到后台“版本管理”里把该版本设为体验版,生成体验版二维码。扫描后对方就能用了。
第二种是预览模式。开发者工具直接点“预览”,会生成一个预览二维码,但二维码有效期很短,而且需要扫码的人是开发者或体验成员才能打开。临时演示够用,长期试用还是体验版更稳。
这里有一个常见卡点:如果后端跑在你本地电脑,扫码的人手机上是不可能访问到localhost的,所以外部试用必须把后端部署到一台公网服务器上,或者用其他联网方案把本地服务暴露出去。项目文档里如果写了云服务器部署步骤,就按文档来。
7. 实测踩坑记录:这些问题真的会让你卡住
7.1 Spring Boot版本太高导致的兼容性连锁反应
“springboot版本太高”这个搜索词能上榜,我是完全不意外的。很多同学从Maven仓库直接拉最新的Spring Boot版本,比如3.2.x,结果项目里其他依赖全是按2.x写的,启动直接报一堆错。
我遇到过的典型报错链是这样的:Spring Boot 3.2.x要求JDK17,但本机只装了JDK8,项目启动就报UnsupportedClassVersionError;把JDK升上去之后,发现原来代码里import javax.servlet.http.HttpServletRequest编译不过去了,因为3.x换成了jakarta包;再去改MyBatis-Plus版本,又发现分页插件配置类的位置变了,方法签名也不同。
解决思路不是一味升级,而是先看项目文档里标注的版本。如果项目基于2.7.x,就固定用2.7.x,不要动。如果非要升级,那就一次性把所有依赖都对齐到3.x,相当于做一次全面的技术栈迁移,工作量比想象中的大。
7.2 微信小程序2MB包体限制与uniapp打包超限
“uniapp 微信小程序打包 source size 2612kb exceed max limit 2mb”,这个报错信息我闭着眼都能背出来。用uni-app开发的在线学习系统,打包成微信小程序时超过2MB限制是常态,因为uni-app自身运行时库就占了不少体积。
处理办法按优先级排序:第一,把课程详情页、视频播放页这类不常访问的页面拆到分包里,主包只留下首页、列表页、登录页这些核心页面。微信官方对分包总大小的限制比单包宽松很多,正常拆包后压力会大幅下降。第二,压缩图片资源,课程封面图不要放本地,尽量用线上URL。第三,检查项目里有没有误引入的大型第三方组件库,按需引入是基本原则。
如果项目是原生小程序,情况会好一些,但课程视频、PPT资源同样不要放在包内,必须服务器存储加URL访问。
7.3 小程序HTTPS调试:证书与抓包姿势
小程序上线要求request的URL必须是HTTPS且域名备案,不过开发阶段大家基本都是走本地HTTP。本地调试没问题后,联调阶段会遇到一个痛点:有些问题只在真机出现,但你又看不到请求细节。
这时候就需要抓包工具。Charles是开发调试里非常常用的工具,可以用来查看小程序发出的HTTPS请求内容,包括请求头、参数和响应结果。基本节奏是:手机和电脑连同一个局域网,手机设置HTTP代理指向电脑IP和Charles监听端口,安装Charles根证书并开启SSL解密,然后就能在电脑上看到小程序的所有请求了。
这里我要特意提醒一句:抓包只用于调试自己开发的小程序,是一种常规开发手段。联调完成后记得把手机代理关掉,否则手机会一直走代理导致网络异常。
在实际操作中,如果小程序开启了证书校验或者用的是较新的基础库版本,抓包可能会遇到握手失败,常见解法是更新Charles版本并重新安装根证书,同时保证手机和电脑时间一致。
7.4 细碎但致命的几个小问题
除了上面三个大坑,我再把开发过程中容易忽略的小问题列成一张表:
| 问题现象 | 常见原因 | 处理建议 |
|---|---|---|
| 小程序请求返回10002或request fail | 合法域名未配置,或本地关校验后仍报网络错误 | 检查域名配置、HTTP还是HTTPS、证书是否有效 |
| 发布体验版后打开白屏 | 代码上传后域名校验生效 | 在公众平台配置合法域名,确认后端已部署到公网 |
| 视频播放退出后声音仍在 | 页面卸载时未销毁播放器 | 在onHide和onUnload里调用播放器stop |
| 进度条不准确,从0开始 | 未断点续播,用户离开时进度未上报 | 上报播放位置,进入时读取并seek到对应位置 |
| 自定义导航栏在全面屏上错位 | 导航栏高度写死44px | 用胶囊按钮位置加状态栏高度动态计算 |
| 微信开发者工具能跑,真机不行 | 网络环境差异,本地关校验导致 | 上线前部署公网,域名配HTTPS,关闭本地校验 |
这些问题的共同点是:代码层面不难,但是没有人提醒就很容易被卡住。我每做一个类似项目都会把这些问题记进文档里,后面接手的人会省掉大量排查时间。
最后再说一点体会:做这种前后端分离的在线学习系统,最有价值的不是把某个页面做得多炫,而是把“登录到学习到记录进度”这条主链路完整跑通。你只要把用户、课程、章节、进度这四类核心表设计清楚了,再围绕它们把接口补齐,整个项目就立住了。后续如果想扩展,无非是加支付、打卡、试卷、多端等模块,核心骨架不会变。