简介:2024最新万岳开源网校源码,是一套面向教育培训机构、职业院校及独立讲师的开源在线教学系统。采用原生语言开发、多端互通,集教学、学习、管理、互动、营销于一体,支持视频直播、语音直播、PPT直播,覆盖大班课、小班课、公开课、双师教学等模式,适用于快速搭建自有网校或进行二次开发。压缩包内共2000个文件,整体约52.57MB,以1015个JavaScript、368个HTML为核心,配合111个CSS样式文件、129个JSON配置及8个SQL数据库脚本,另有Markdown与TXT说明文档,便于理解项目结构与部署方式。目前已有281人学习下载。源码自带web学生端、讲师端、管理后台等清晰模块,无需搭建即可注册使用,也支持灵活改造,可作为教育产品研发或在线课堂项目的重要参考。
1. 万岳开源网校源码:从直播课堂到多端交付的工程拆解
与其把万岳网校当成一套“装完就能招生”的成品系统,不如把它看作一套已经跑通教学闭环的代码底座。它覆盖了web学生端、app学生端、web管理后台、web讲师端四个终端,核心场景集中在互动大班课、精品小班课、双师教学和内容付费四类业务上。对于技术团队来说,这套源码的价值在于:你不用从零设计课程表、直播间信令、讲师端白板权限这些通用模块,而是可以直接在已有骨架上做二开、改样式、接自己的直播服务。适合的对象也很明确:有研发能力的教培机构、做在线教育SaaS的创业团队、以及需要快速搭POC演示给客户看的售前工程师。如果你只是想要一套零代码运营的SaaS账号,那直接用官方云服务即可,源码反而是多余的成本。
2. 前端静态资源与页面骨架:先从bootstrap和layui说起
拿到源码压缩包后,第一眼看到的是bootstrap.min.css、layui.css、white.css、icon.css、ueditor.css、liveback.css、admin.css这一批静态文件。这里有个容易误判的点:万岳网校并不是前后端分离的SPA应用,而是典型的服务端渲染 + 多页面应用结构,前端以jQuery、layui、Bootstrap为主,直播管理后台大量使用layui的表格和弹层组件,学生端门户则用Bootstrap做栅格布局。这意味着你不需要引入webpack或vite构建链,改完CSS直接刷新就能看到效果,二开门槛比想象中低很多。
2.1 登录态与页面渲染:临时令牌在URL里的流转
学生端页面大多通过URL携带临时令牌来识别身份,具体形式是在地址栏拼接类似?token=xxxx的参数。我在本地复现时,第一次打开首页就发现没有登录态时页面仍能正常渲染课程列表,但进入直播间时会强制跳转登录页。核心逻辑在/app/student/目录下的公共方法里,通过读取token后调用用户信息接口,再把返回值写入cookie或localStorage。
// 伪代码:学生端进入直播间前的token校验 function checkLogin() { var token = getQueryParam('token') || localStorage.getItem('student_token'); if (!token) { location.href = '/login.html?redirect=' + encodeURIComponent(location.href); return false; } return token; }这个函数的执行时机是所有直播页面加载时的第一行逻辑,token有效期由服务端统一控制。实际部署时需要注意redirect参数必须做白名单校验,否则容易被拼上外部链接变成开放重定向漏洞。另一个细节是token在不同子域名间不共享,如果你把静态资源放在static.yourdomain.com,接口放在api.yourdomain.com,跨域请求要处理好CORS头。
2.2 样式定制:不重新编译,直接覆盖
万岳的前端样式变量没有走LESS或SASS,直接用原生CSS编写,所以定制主题色的常规做法是在HTML尾部追加一个自定义CSS文件,覆盖Bootstrap和layui的默认变量。
/* custom.css:覆盖layui主色 */ .layui-btn-primary { background-color: #ff6a00; border-color: #ff6a00; color: #fff; } .layui-table-tool { background-color: #f8f9fa; }这里有个坑:liveback.css和white.css都定义了直播控制栏的样式,liveback.css作用于讲师端直播回显区域,white.css作用于学生端看课页面,两者权重相同,后加载的会覆盖先加载的。建议在公共头部统一调整引入顺序,而不是在不同页面单独改样式,否则直播页和控制台会出现按钮颜色不一致的问题。改完样式后按Ctrl+F5强制刷新,因为多数浏览器会因强缓存忽略CSS更新。
2.3 富文本与白板:ueditor在直播场景里的边界
源码中集成了ueditor.css,说明课程介绍、公告、内容付费详情页都通过ueditor编辑器生成HTML。讲师端在直播中上传的课件如果能解析为HTML,也可以直接回显到ueditor容器里。
实际使用中ueditor主要暴露两个问题:一是上传图片默认走独立接口,没有与万岳的用户鉴权体系打通,需要在config.json里配置serverUrl指向自己的上传接口,同时携带token;二是ueditor生成的HTML包含大量style属性,存入数据库前建议做标签过滤,防止XSS注入。
// ueditor配置片段:对接自有上传接口 window.UEDITOR_CONFIG = { serverUrl: '/api/upload/ueditor', toolbars: [[ 'bold', 'italic', 'underline', 'insertimage', 'insertvideo', 'attachment' ]], initialFrameHeight: 300 };参数initialFrameHeight控制默认编辑高度,课程简介通常设为300px足够;如果内容较长,讲师可在编辑区内自行拉伸,这个高度不会截断内容。图片上传返回的数据结构必须是{url: '...', originalName: '...'},否则ueditor无法回显缩略图。调试上传接口时注意查看Network面板中responseText字段,而不是只看HTTP状态码。
3. 直播课堂的承载结构:大班课、小班课与双师模式的底层差异
万岳网校的直播模块不是单一路由,而是按班型拆分了不同的接口和数据模型。大班课是一对多广播模式,讲师端推流到CDN,学生端拉流观看,做万人并发时主要考验CDN带宽和分发策略;小班课则是一对一或一对多互动模式,依赖WebRTC或低延迟音视频服务,每个学生端与讲师端之间需要维持信令连接;双师教学实际上是上述两种模式的组合,主讲端走广播推流,辅导端走互动连麦通道。
3.1 直播间数据表设计与关联关系
从源码的SQL文件里可以看到直播间相关的核心数据表结构,包括live_room、live_user_auth、live_record和live_gift_log。其中live_room表存储直播间基础信息,room_type字段区分大班课、小班课和双师直播间。
CREATE TABLE `live_room` ( `id` int(11) NOT NULL AUTO_INCREMENT, `room_name` varchar(255) NOT NULL COMMENT '直播间名称', `room_type` tinyint(1) DEFAULT '1' COMMENT '1大班课 2小班课 3双师', `teacher_id` int(11) DEFAULT NULL COMMENT '讲师ID', `assistant_id` int(11) DEFAULT NULL COMMENT '辅导老师ID', `live_status` tinyint(1) DEFAULT '0' COMMENT '0待开课 1直播中 2已结束', `start_time` datetime DEFAULT NULL, `end_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_teacher` (`teacher_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;该表的核心设计在于assistant_id字段单独列出来,而不是统一放在讲师维度,说明双师模式中辅导老师是独立角色,拥有独立的连麦权限和禁言权限,不能和讲师混用一套权限控制。live_status通过定时任务或回调更新,避免学生端不断轮询接口打到数据库。
3.2 直播协议选型与延迟参数参考
开源版默认的直播方案是HLS拉流 + HTTP-FLV推流组合,HLS用于学生端观看(延迟约5到10秒),HTTP-FLV用于讲师端推流预览(延迟约1到3秒)。在/config/live.php中能看到直播配置参数,我一般会按下面这个模板调整:
return [ 'push_protocol' => 'rtmp', 'play_protocol' => 'hls', 'hls_time' => 4, // HLS切片长度,单位秒,值越小延迟越低但增加回源压力 'hls_list_size' => 6, // 点播列表保留切片数量 'low_latency' => true, // 开启低延迟模式 'record_enabled' => true // 直播自动录制 ];hls_time设成2到4秒之间体验较优,设成1秒会导致切片文件过多,CDN回源次数指数上升。hls_list_size控制在6个以内即可,过大只会占用存储且对播放体验没有提升。如果小班课互动要求延迟低于500毫秒,那HLS方案不适用,需要单独接入WebRTC网关,并在配置里把该房间的play_protocol改为webrtc,源码中这一部分是预留了接口但默认未启用。
3.3 万人并发压力点排查:先看CDN再看数据库
大班课支撑万人的前提是直播流走CDN分发,而不是服务端直接转发。所以首先确认推流地址是rtmp://your-cdn-domain/live/开头,而不是服务器公网IP。代码层面需要关注的是socket连接数,万岳的聊天室和上麦逻辑使用WebSocket维持长连接,默认配置下PHP-FPM进程数很快会打满。
netstat -an | grep :9501 | wc -l端口9501是万岳默认的WebSocket服务端口,如果连接数超过10万,就需要在/etc/sysctl.conf中调整fs.file-max和net.ipv4.ip_local_port_range参数。实操时先把本地文件句柄限制调大:
ulimit -n 1048576 echo "ulimit -n 1048576" >> /etc/profile另一个压力点是聊天室消息广播,若全员发送的消息都回源到数据库,必然造成锁等待。万岳的做法是聊天消息先写入Redis的List结构,再由定时任务异步落库。开发者若二次开发扩展直播间礼物、弹幕功能时,建议沿用这个队列模型而不要直接写表。
3.4 直播录制与回放:ffmpeg命令参数表
源码管理后台自带直播录制功能,底层调用ffmpeg对直播流进行转存,生成MP4回放文件。录制任务由独立脚本控制,核心是一个定时轮询的shell脚本。
| 参数 | 值 | 用途说明 |
|---|---|---|
-i | rtmp://127.0.0.1/live/{room_id} | 输入直播流地址 |
-c:v | libx264 | 视频编码,兼容性最好 |
-preset | veryfast | 编码速度优先,减少CPU开销 |
-c:a | aac | 音频编码,适用于直播录制 |
-f | mp4 | 输出容器格式 |
-movflags | faststart | 将MP4索引移到文件头部,边下边播 |
ffmpeg -i rtmp://127.0.0.1/live/123456 \ -c:v libx264 -preset veryfast -c:a aac \ -f mp4 -movflags faststart \ /data/recordings/123456_$(date +%Y%m%d%H%M).mp4录制脚本需要常驻后台执行,通常配合nohup和&启动,并添加任务锁防止同一直播间被重复拉起录制进程。判断任务是否重复,建议检查/tmp/record_123456.lock文件是否存在,存在则直接退出。录制出来的MP4文件如果要在小程序端播放,还需要额外转码出H.264编码和AAC音频的版本。
4. 部署落地:Nginx与PHP环境的搭建和踩坑记录
万岳网校基于PHP开发,官方推荐使用Nginx + PHP 7.x + MySQL 5.7/8.0的组合。整体部署流程不算复杂,核心在于伪静态配置、PHP上传限制和HTTPS证书三个环节。由于源码中bootstrap.min.css等静态资源文件较大,Nginx的Gzip压缩建议开启,否则首屏加载时间可能突破3秒。
4.1 Nginx站点配置与pathinfo模式
server { listen 80; server_name edu.yourdomain.com; root /var/www/wanyue/student; index index.php index.html; location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s=$1 last; } } location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } location ~* \.(css|js|png|jpg|jpeg|gif|ico)$ { expires 7d; add_header Cache-Control "public"; } # 直播流文件不缓存 location ~* \.(m3u8|ts|mp4)$ { expires -1; add_header Cache-Control "no-cache"; } }伪静态规则主要解决学生端URL路由问题。rewrite规则把不存在的文件路径统一交给index.php解析,交给ThinkPHP框架处理控制器。配置时最容易出问题的点是把root指到了/public目录,导致CSS和JS文件全部404。万岳学生端的入口在/student/目录下,管理后台在/admin/目录下,两个站点需要分别配置server块或者通过子路径区分。
4.2 PHP环境关键参数调整
直播过程中学生端要上传头像、提交作业、讲师要上传课件,PHP的上传限制必须改大,否则会看到“文件过大”的错误提示。修改php.ini中的以下参数:
file_uploads = On upload_max_filesize = 2048M post_max_size = 2048M max_execution_time = 300 max_input_time = 300 memory_limit = 256Mupload_max_filesize同时影响图片、视频课件的上传大小,建议设置成2G以兼容录播课上传。这个数值不是无脑往上调,如果超过Nginx的client_max_body_size配置,Nginx会先返回413错误,所以Nginx配置里也要同步加上client_max_body_size 2048m;。post_max_size必须大于upload_max_filesize,否则多个文件同时上传时会失败。另外源码中使用fileinfo扩展来检测上传文件的MIME类型,这个扩展在某些精简版PHP中没有默认安装,出现文件上传成功后无法访问的情况时优先检查php -m | grep fileinfo。
4.3 数据库初始化与账号权限配置
源码包中的sql目录存放有初始化脚本,导入后默认管理员账号是admin、密码经过MD5加密存储。首次登录后应立即修改默认密码,否则存在被扫描工具直接命中后台的风险。
-- 修改管理员密码:MD5加密后的字符串 UPDATE `users` SET `password` = MD5('新密码') WHERE `username` = 'admin';MySQL的排序规则建议使用utf8mb4_general_ci,因为学生姓名、课程标题中可能会有Emoji表情,utf8mb4能完整存储四字节字符。如果建库时误选了utf8,后续插入带表情符号的数据会报Incorrect string value错误,解决起来比较麻烦。初始化脚本执行后还需要创建專用的数据库账号,避免用root账号连接业务库,最小权限原则只授予SELECT/INSERT/UPDATE/DELETE即可。
5. 从资源到项目:模板二开的实用路径
万岳源码的目录结构适合团队协作,学生端、讲师端、管理后台各自独立。对于想要快速跑通demo用于招商或内部培训的场景,建议直接用原生代码跑通默认流程,对需要深度二开业务逻辑的代码则要重点关注订单支付和课程版权保护两部分。
5.1 二开工作流:确定需求边界、剥离冗余模块
在动手之前,第一步是梳理哪些功能是必须的,哪些是演示用而线上应该隐藏的。万岳默认带营销插件,这些模块会引入大量额外表结构和定时任务。如果不需要这些功能,直接删除对应的控制器和方法会省下不少维护成本。我通常会做一次全量代码扫描,按“直播、支付、用户、课程、聊天”五个维度拆分模块,再决定留哪些。
# 扫描控制器中引用到的所有数据库表 grep -rh "db('" app/ | awk -F"'" '{print $2}' | sort | uniq > tables.txt # 对比该文件与初始化SQL中的table定义,找出多余模块 grep "^CREATE TABLE" sql/install.sql | awk '{print $3}' > all_tables.txt comm -23 tables.txt all_tables.txtgrep命令通过匹配db('表名')的调用模式,找出代码中实际用到的数据表名称,再和SQL安装文件中定义的全部表做差集,差集中出现的就是可能被冗余模块引用但又未在主安装包中建表的表格。识别出这些表之后再决定需要新增建表语句还是删掉对应代码,避免上线后因为缺表导致白屏。
5.2 多端接口权限与鉴权放行策略
web端与app端共用大部分接口,只是入口域名不同。讲师端的操作接口权限校验在中间件中统一处理,学生端和管理后台的权限模型则通过RBAC表控制。二开中新增一个讲师端接口时,需要在权限表中插入对应的权限节点。
-- 新增讲师端“查看学员列表”权限节点 INSERT INTO `auth_rule` (`name`, `title`, `status`) VALUES ('/live/studentList', '查看学员列表', 1); INSERT INTO `auth_group` (`rules`) VALUES (CONCAT('1,2,3,', LAST_INSERT_ID()));auth_rule表存储权限规则的唯一标识名称,框架根据当前登录用户的角色ID去auth_group表中匹配rules字段是否包含当前请求的节点名称。status字段控制节点是否启用,置为0时该权限暂停生效,无需修改代码。新增接口后如果没有同步插入权限节点,访问时会返回“无权限”提示,这个排查步骤容易被新手忽略。
5.3 课程保护与防盗链:HLS鉴权
网校系统最怕课程资源被批量下载后转卖。默认配置下直播回放地址是明文的M3U8链接,很容易被爬虫抓取并二次分发。实操中建议在Nginx层面对HLS文件做鉴权,只有携带有效签名参数的请求才允许拉流。
# 签名URL格式 https://edu.yourdomain.com/live/123456.m3u8?sign=b8c37e33defde51cf91e1e03e51657da&expire=1735689600Nginx通过secure_link_module模块校验sign参数,签名值由expire时间戳和访问路径拼接后取MD5生成。讲师端直播推流时自动生成带签名的播放地址,到期后URL自动失效,无需在应用代码中做额外判断。需要注意前端播放器在HLS切片请求时会自动带上M3U8的URL参数,所以要给location块配置secure_link指令,而不是仅校验M3U8文件的请求参数。如果开启了CDN加速,还要把鉴权参数透传给源站,否则CDN节点上会缓存到过期URL导致播放失败。
本文还有配套的精品资源,点击获取