ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

FastAdmin短视频系统部署与二次开发:视频知识付费避坑实践指南

FastAdmin短视频系统部署与二次开发:视频知识付费避坑实践指南 简介基于FastAdmin框架的视频知识付费源码包整合短视频系统与小说系统适合内容创业者、在线教育机构快速搭建自有知识变现平台。后台覆盖会员管理、视频包月、单独购买、观影券及小说章节付费等核心商业功能前端需手机验证码登录部署时需较高服务器配置并建议启用VDN加速保证视频播放体验。压缩包共2000个文件其中js脚本1422个、HTML页面209个、CSS样式107个另有SQL数据库、Shell脚本等资源整体约77.75MB目录结构完整便于二次开发与部署调试。已有245人学习下载对希望低成本验证视频知识付费模式的开发者而言源码提供了可直接运行的后台框架与前端交互界面可快速配置商品、用户与订单并扩展小说模块形成多内容形态的知识付费平台。1. 视频知识付费源码到底能拿来干什么先看清这套 FastAdmin 短视频系统的家底收到这样一套源码包很多人第一反应是“有 FastAdmin有短视频还有小说直接扔进宝塔跑起来就能收钱了”。实际上视频知识付费源码这类东西框架只是表面真正值钱的是会员体系、视频权限校验、订单闭环和小说模块怎么共用同一套用户。我会用这套 FastAdminThinkPHP 5.1 内核短视频系统当模板把本地部署、后台初始化、核心购买链路、二次开发“试看 3 分钟”以及我踩过的 5 个坑一次说清。适合想低成本搭建知识付费、短剧或付费小说站并且自己动手改功能的人纯站长不懂代码也能照步骤操作但建议至少会看 PHP 报错日志。2. 本地部署 FastAdmin 短视频系统从 PHP 环境到跑通后台的最小命令2.1 环境选型为什么是 PHP 7.4 Nginx 宝塔而不是其他FastAdmin 基于 ThinkPHP 5.1官方给出的最低要求是 PHP 5.6但实际跑短视频和小说模块5.6 下很多函数和语法会触发废弃警告PHP 7.0 到 7.2 也能跑不过遇到加密过的源码不少二次开发版本用 ionCube 或 SourceGuardian就要额外装扩展。我一般直接上 PHP 7.4因为这是 ThinkPHP 5.1 适配最好的版本也是宝塔面板里切换最方便的一个版本大多数 FastAdmin 源码的命名空间和语法在 7.4 下不会报兼容错误。PHP 8.0 以后不少老插件会直接白屏不建议硬上。数据库方面MySQL 5.7 最稳妥因为很多源码的 SQL 用了旧的 timestamp 默认值写法MySQL 8.0 的 sql_mode 和认证插件不同容易在安装向导最后一步报“数据库连接失败”或“数据表创建失败”。如果要上 MySQL 8得先改application/database.php里的 charset 和 DSN 配置还要给 MySQL 开log_bin_trust_function_creators这套源码包大概率没做适配别自己找麻烦。Web 服务器用 Nginx。Apache 也能跑但 FastAdmin 官方推荐 Nginx很多上传接口、图片压缩、伪静态规则在 Apache 下需要 .htaccessNginx 只靠站点配置文件就能生效性能差距也明显。宝塔面板只是用来装 PHP 和 MySQL 的工具部署逻辑其实和裸环境一样。2.2 解压源码、配伪静态、装扩展一条龙操作假设你已经通过宝塔文件管理器或 FTP 把视频知识付费源码...zip上传到服务器/www/wwwroot目录。先解压注意不要直接解压到已有站点根目录避免覆盖生产环境文件。cd /www/wwwroot mkdir zp-content-pay # 上传 zip 到 zp-content-pay 目录后 unzip -q 视频知识付费源码*.zip -d zp-content-pay cd zp-content-pay ls -la解压后你会看到类似public/、application/、addons/、runtime/这样的目录。FastAdmin 的入口文件在public目录下所以站点根目录不能直接指到解压根目录必须指到public否则application下的 PHP 源码会被 Nginx 当静态文件暴露出来等于把后台代码白送。在宝塔里添加站点时把“网站目录”设置为/www/wwwroot/zp-content-pay/public同时关闭“防跨站攻击”开关否则 PHP 可能没有权限读取上级目录的配置。然后修改 Nginx 伪静态规则内容如下location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; break; } }把这段放到站点配置的location /中保存后去“软件商店”确认 PHP 7.4 的扩展里已经装了fileinfo、opcache、redis、exif。没有fileinfo后台上传视频封面时的尺寸校验会直接报错。装好扩展后在命令行确认一下php -v php -m | grep -E fileinfo|redis|exif输出里有fileinfo就基本过关。如果没装去宝塔 PHP 配置页的“安装扩展”里搜索安装再回到 Nginx 重启 PHP-FPM。2.3 安装向导与后台登录首次初始化的 4 个关键参数浏览器访问站点域名FastAdmin 会自动跳转到/install.php。没跳转就手动访问http://你的域名/install.php。安装向导会检查目录权限常见卡住的是runtime/和public/uploads不可写chmod -R 755 /www/wwwroot/zp-content-pay chmod -R 777 /www/wwwroot/zp-content-pay/runtime chmod -R 777 /www/wwwroot/zp-content-pay/public/uploads安装参数里有 4 个关键点决定你后面少折腾多久。数据库前缀默认是fa_。如果这个数据库里之前装过其他 FastAdmin 项目或者你在同一库里测试过多次安装会报前缀冲突。建议换一个前缀例如z_这样后续装插件、建表都统一带前缀不会和旧表混在一起。管理员账号密码是第一道防线不要用admin/123456。后台入口文件与安全码安装向导会让你填写后台入口文件名源码包里常见的是admin.php为了安全改成别人猜不到的名字比如x8_admin.php。安全码默认是fa务必改成 16 位随机字符串否则别人可以用默认安全码绕过部分接口验证。第三是 URL 模式。安装向导会探测伪静态如果探测失败站点所有链接都会变成index.php?s/video/12这种长链。看起来能用但分享不友好也会影响短视频列表分页。建议先把 2.2 的伪静态配好再进安装向导。第四是时区与语言。挑 Asia/Shanghai 和简体中文别用默认的 UTC否则视频上架时间和订单支付时间全部差 8 小时后面排查支付回调会多一层面障。2.4 部署完成后先验证的三件事安装完成进入后台先别急着传视频做三件验证。先确认后台能正常打开并且界面不白屏然后访问前台首页用 curl 看状态码curl -I http://你的域名/ curl -I http://你的域名/index.php?s/video/index首页返回 200但列表页返回 404说明伪静态的 rewrite 规则没匹配到二级路由回到 2.2 再查 Nginx 配置。如果首页 200列表也 200框架就通了。第二件事是去后台“插件管理”看短视频模块和小说模块有没有启用。很多源码包分两步主程序装好后还要在后台插件管理里安装Shortvideo和Novel插件如果插件在addons目录里但没安装后台左侧菜单会什么都没有。这一步漏了你甚至不知道从哪点开始配置。第三件事是确认日志目录可写。访问一个不存在的页面然后看日志有没有实时滚出来tail -f /www/wwwroot/zp-content-pay/runtime/log/$(date %Y%m)/0.log能滚出 404 或 SQL 错误说明日志正常等了半天没反应多半是 runtime 目录权限不对。这一步不验后面出问题只能黑匣子盲猜。3. 短视频与知识付费的核心链路会员、套餐、视频解析与订单闭环3.1 后台功能地图FastAdmin 的菜单、权限和插件机制FastAdmin 的后台不是传统插件式简单挂菜单而是基于 auth 权限表控制菜单显示和访问。后台左侧菜单来自fa_auth_rule表每一个菜单项对应一个控制器方法和 JS 模块。你在后台“权限管理 - 菜单规则”里会看到“视频管理”“视频分类”“会员套餐”“小说分类”“小说章节”等条目。短视频相关功能一般封装成 addons 模块addons目录下会有shortvideo、novel、vip等目录各自有自己的 controller 和 model。但“附带小说系统”这类源码很多是后来手动融进 FastAdmin 的不一定走标准 addon 机制。进入后台后先去“插件管理”看有没有未安装的本地插件有就先安装并启用。菜单权限要小心。给运营账号开“视频管理”时如果没勾选“视频分类”和“视频上传”实际编辑视频时分类下拉框就是空的。我交付客户时管理员直接给最高权限运营人员则提醒他们把菜单层级勾全这种边界问题几乎每周都能遇到。3.2 视频点播与知识付费的权限校验逻辑知识付费最让人新人翻车的地方在这里以为后台把视频上传好、填好价格播放页就能自动拦截付费。真实逻辑是“付费”发生在接口层而不是前端播放器层。这套源码的视频详情接口通常在api模块下前台播放器通过 AJAX 请求/api/video/info?id12获取播放地址和权限状态。接口内部一般会有类似这样的判断public function info(Request $request) { $id $request-get(id/d); $video Video::get($id); if (!$video) { return json([code 0, msg 视频不存在]); } $user $this-auth-getUser(); $isVip $user $user[vip_expire] time(); // 免费视频只要登录就能看 if ($video[is_free] 1) { return json([code 1, data $video-toArray()]); } $data $video-toArray(); $data[try_duration] $isVip ? 0 : (int) $video[try_time]; $data[is_vip] $isVip; return json([code 1, data $data]); }参数说明$this-auth是 FastAdmin 的登录认证入口通过请求里的 token 换取用户信息。try_time是后台“视频编辑”里填的试看秒数默认 180is_free为 1 表示免费0 表示单独付费购买。注意try_time只能拦游客和非 VIP不能拦已经购买该单集的用户所以后面要有order_status判断。更严谨的做法是视频地址不落库。如果video.url字段直接存了阿里云 OSS 的完整外链等于把最终播放地址发给了前端懂技术的人拿地址就能全片下载。付费视频的播放地址必须由后端临时生成带过期时间的签名 URL不能存什么就返回什么。3.3 小说系统是怎么融进来的一个项目两套业务的常见接法小说系统在这里不是砍掉重写的模块而是另一套独立的业务表。常见做法是短视频吃fa_star_video这类视频表小说吃fa_novel_book、fa_novel_chapter表会员表共用fa_user订单表共用fa_order或者各自有fa_novel_order。登录与 VIP 状态从fa_user统一拉取这是两端能串起来的关键。小说前台详情页请求/api/novel/chapter?id123后端同样检查$user[vip_expire]如果过期就返回本章节付费提示。所以后台把 VIP 套餐加好短视频和小说能直接同时售卖不用各配一套会员。小说模块最常见的坑是数据导入。这套源码要求每一章一条记录存在fa_novel_chapter表。批量导入时如果后台没有自带导入工具我是直接写 SQL 处理的但一定要先备份表INSERT INTO fa_novel_chapter (book_id, title, content, sort, status) SELECT book_id, title, content, sort, status FROM backup_novel_chapter;注意 SQL 里字段要带前缀还要检查book_id在fa_novel_book里确实存在否则前台详情页会空白。这是“小说能开章节但目录翻页白屏”最常见的原因。3.4 支付回调与订单状态机的坑支付是知识付费里最怕出问题的地方。下单前先看订单表结构通常有order_sn、user_id、amount、status、paid_time。状态机建议定成0 待支付1 已支付2 已关闭。统一下单接口生成订单号后发给支付平台用户支付后平台异步通知回调地址。FastAdmin 后台支付配置里的“回调地址”必须能公网访问并且要能被支付平台 POST 请求。本地测试时很多人填 localhost回调永远收不到。回调处理函数里除了验签和改订单状态还要同步更新用户会员到期时间。很多二开源码只update order set status1忘了给用户加会员时长结果就是用户支付成功订单已支付前台仍然提示“会员已过期”。稳妥做法是写一个统一方法public function payNotify($platform) { // 验签通过后 $order Order::where(order_sn, $out_trade_no)-find(); if ($order[status] 1) { // 幂等已经处理过的回调直接跳过 return SUCCESS; } $order-status 1; $order-paid_time time(); $order-save(); $user User::find($order[user_id]); // 根据套餐类型累加会员到期时间 $user-vip_expire max(time(), $user[vip_expire]) $order[vip_days] * 86400; $user-save(); return SUCCESS; }注意第三行如果用户当前会员还没到期新购买的会员应从现有到期时间往后累加而不是直接time() vip_days。很多源码直接覆盖成当前时间加天数等于把用户的剩余会员天数清零。这个“会员时长叠加”的判断是支付闭环里最容易被忽略的一笔。4. 二次开发给前台加一个“试看 3 分钟”功能含代码4.1 找到前台控制器和模板的对应关系FastAdmin 前台不是传统 MVC 的纯页面输出而是路由到控制器。以这套源码为例前台页面在application/index/controller/Video.php模板文件对应application/index/view/video/detail.html。如果源码用了think-view模板引擎那Video::detail方法会渲染detail.html并把视频数据传给模板。怎么确认看浏览器地址栏 URL比如/video/15.html然后在application/route.php里找对应路由规则。FastAdmin 默认路由是module/controller/action所以/video/detail?id15对 Video 控制器的 detail 方法。如果 URL 是/api/video/detail?id15那属于 API 模块下的控制器前台 JS 通过 AJAX 请求它拿数据。给前台加功能优先动 API 层接口不要动最终渲染的 HTML 模板。因为播放器对接的是 JSON 数据模板里改样式只影响自己这一端移动端、小程序端还是要靠接口。下面就从 API 层改起。4.2 试看逻辑的实现在视频接口里加一个 cut 参数需求是非会员播放视频只能看 180 秒到点弹出开通会员弹窗。服务端要返回两个值播放地址和试看时长。播放地址是完整片源地址试看时长由服务端计算前端负责计时。不要在播放器里写死 180因为后台管理员可能把某个视频的试看时长改成 90 秒。打开application/api/controller/Video.php在详情接口中找到返回播放地址的那段加上try_time与pay_status字段public function detail(Request $request) { $id $request-get(id/d); $video VideoModel::where(id, $id) -field(id,title,url,is_free,try_time,price,cover) -find(); if (!$video) { return $this-error(视频不存在); } $user $this-auth-getUser(); $isVip $user $user[vip_expire] time(); $hasBuy false; if ($user) { $hasBuy OrderModel::where(user_id, $user[id]) -where(video_id, $id) -where(status, 1) -count(); } if ($isVip || $hasBuy || $video[is_free] 1) { $duration 0; } else { $duration (int) $video[try_time]; if ($duration 0) { $duration 180; } } $result $video-toArray(); $result[try_time] $duration; $result[pay_status] ($isVip || $hasBuy) ? 1 : 0; return $this-success(ok, $result); }参数说明$isVip判断用户vip_expire是否大于当前时间$hasBuy查询是否已单独购买该视频避免已购买用户看试看is_free1的免费视频不用关心会员。try_time取后台配置值没配置时兜底 180。前端拿到pay_status0时显示价格按钮。注意如果视频 URL 是 OSS 私有地址这里要临时生成带签名的 URL 再放到url字段返回否则攻击者用浏览器调试工具把地址复制出来就能一直看。生成签名地址的逻辑可以在UrlSign::sign()里做常见做法是OSS的signatureUrl($timeout)或腾讯云 COS 的getObjectUrl。4.3 前端播放器对接Video.js 只请求前180秒如果播放器是 Video.js试看秒数不能只靠setTimeout弹窗因为用户暂停时setTimeout还在走时间到了视频明明暂停也弹窗。正确做法是监听timeupdate事件当当前播放时间超过试看值时立即暂停。在detail.html播放器初始化代码后面加入这段 JSvar tryTime res.data.try_time || 0; if (tryTime 0) { var player videojs(my-video); player.one(loadedmetadata, function () { player.on(timeupdate, function () { if (player.currentTime() tryTime) { player.pause(); player.trigger(try-end); if (typeof showVipModal function) { showVipModal(res.data.price); } } }); }); }代码逻辑说明loadedmetadata触发一次后注册timeupdate每次播放时间更新就检查currentTime。到点后pause()暂停同时触发自定义事件try-end页面可以挂监听去做支付弹窗。没有用setInterval因为timeupdate本身由浏览器原生触发播放时每秒 4 次左右足够判断且不耗内存。边界条件用户把进度条拖到 179 秒视频几乎 1 秒后被暂停这是最安全的行为。如果你希望“试看 180 秒”指累计播放时长允许暂停后继续播要改成累计统计var watched 0; player.on(timeupdate, function () { var last player.currentTime(); if (last this._lastTime) { watched last - this._lastTime; } this._lastTime last; if (watched tryTime) { player.pause(); } });注意这里用this._lastTime记录上次播放时间否则用户拖进度条会让watched瞬间增长误暂停。这套源码里我一般只做简单版本因为运营更希望“到点即停”不做累计。4.4 参数说明与兼容性边界参数类型含义默认值try_timeint试看秒数0 表示完整播放180is_freeint0 付费1 免费0pay_statusint0 未购买/非VIP1 已购买/VIP0vip_expiredatetime用户会员到期时间可空null这段代码的兼容性边界有两个。第一如果播放器不是 Video.js 而是 plyr、DPlayertimeupdate事件由原生 video 元素触发把监听从player换成document.querySelector(video)即可。第二如果视频源是 m3u8/HLS走的是 MSE 流currentTime依然有效但如果是分片播放用户暂停后刷新页面重新拿地址又能重新看 180 秒。要想彻底堵住服务端要限制该用户的一次性播放 token 只能拉取前 N 个切片或在转码时生成一个只含前 180 秒的试看专用片源。这个属于转码流程不是改接口能解决的。5. 避坑与常见问题FastAdmin 上传漏洞、伪静态 404、支付不了账5.1 现象后台视频封面上传后预览正常但接口被扫到上传漏洞这是 FastAdmin 用户最容易翻车的问题。旧版 FastAdmin 的ajax.upload接口只校验了图片后缀没限制 php 等可执行文件攻击者构造一个x.php.jpg或改掉 Content-Type 就能绕过。如果你用的源码包是很久没更新的版本风险非常大。原因这套源码可能带着 2019 年左右的老版 FastAdmin后台application/admin/controller/Ajax.php里的上传逻辑只检查了扩展名数组没做 MIME 二次校验。解决先别等漏洞被扫到再处理Nginx 层禁止uploads目录执行 PHP。因为站点根目录是public所以 URL 路径是/uploads/配置如下location ~ ^/uploads/.*\.(php|php5|phtml)$ { deny all; }再到后台把 FastAdmin 内核升级到最新版检查Ajax.php里的upload方法有没有 mime 类型校验。没有就按新版的逻辑补一份。之后上传一个test.php.jpg到 uploads 目录浏览器访问必须返回 403 才算真正防住。注意上述是防御演示不要对别人的线上系统做同样测试。5.2 现象前端页面对视频页 404后台却正常部署完后台可以登录但前台所有二级路由都 404。原因几乎都是 Nginx 伪静态没生效。FastAdmin 的 URL 默认是/index.php?s/video/index如果伪静态没配/video/index会被 Nginx 当成硬盘目录去找找不到就 404。解决按 2.2 的 rewrite 规则再核对一遍重点看location /里的if (!-e $request_filename)。如果你用宝塔伪静态下拉框里选“thinkphp”也行但 ThinkPHP 5.1 的规则和通用规则有区别最好手工粘贴location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; } }不要画蛇添足加$query_string拼接。改完执行nginx -s reload再访问/video/index应该会带s参数正常打开。如果仍然 404打开config/app.php看url_route_on是否为 true——有些源码包为了兼容老版本把它设成 false会导致路由规则全部失效。5.3 现象支付回调成功但订单状态还是待支付微信支付后台已显示成功站内订单状态没变会员也没开通。这是支付集成最常见的坑。原因回调地址没配对或者回调函数里验签失败。这套源码里小程序、H5 各有一份支付调用逻辑有的回调地址写的是http://域名/api/pay/notify但后台支付配置里填的是http://域名/pay/notify少了一段路径支付平台的 POST 请求发到了不存在的接口。解决先开日志定位。FastAdmin 日志在runtime/log下找支付当天日期的日志看有没有处理回调的记录。完全没有记录说明请求没到项目去 Nginx 访问日志查支付平台的 POST 记录。验签时不要自己写 md5 拼串直接用官方 SDK 的验签方法。微信支付通知是 XML 流必须用file_get_contents(php://input)拿原始数据而不是$_POST。5.4 现象小说章节图片不显示抓包显示 referer 防盗链小说系统后台能看前台文字正常但章节里的插图全部裂开浏览器控制台提示 403。原因是小说内容里的图片地址是外链比如https://img.example.com/1.jpg而图片源站或你本机开启了 referer 防盗链。解决优先在“小说导入”时做图片本地化把img srchttps://...替换成img src/uploads/novel/xxx.jpg。内容已经入库写一段命令在本地处理注意用正则处理 http 和 https 混写的情况$content preg_replace(#https?://img\.example\.com/#, , $content);替换后把图片二进制下载到本地目录并确保/uploads/novel可写。如果图片走 CDN那就去 CDN 控制台把站点域名加进 referer 白名单这不是改代码能解决的。5.5 现象安装向导提示“数据表前缀 fa_ 已占用”同一台服务器曾经装过其他 FastAdmin 项目或者上次安装中断fa_user表已经存在。安装向导检测到前缀冲突直接退出。解决进 phpMyAdmin把同名库里的旧表全部改名或删除。要保留老数据可以用RENAME TABLE fa_user TO old_fa_user不保留就DROP TABLE但看清楚库名别把线上库删了。之后在安装向导里把前缀改成fa_v2_或z_新装数据不会和旧表冲突。改前缀后 ThinkPHP 的数据库配置会自动拼接不需要手动改源码里所有表名。但要注意如果源码包里的install.sql硬编码了fa_改前缀还是要手动替换 SQL。一般正式发布的源码包会读取配置这个留个心眼。6. 从部署到上线验证数据安全与会员体系的一体化自测6.1 上线前我用这条命令把 API 全部打了一遍不知道你有没有经历过后台看一切正常用户注册却提示验证码错误支付页面跳转成功但回调失败。我习惯上线前用脚本把关键 API 全走一遍而不是在后台点按钮点点点。# 模拟未登录用户获取视频详情 curl -s http://你的域名/api/video/detail?id1 # 预期返回 try_time180, pay_status0 # 用后端生成的一个测试 token 模拟 VIP 用户 curl -s -H token: 测试token http://你的域名/api/video/detail?id1 # 预期返回 try_time0, pay_status1脚本跑完再检查三点发现try_time0但pay_status0说明会员权限判断写反了发现视频 URL 是完整 CDN 私有地址说明播放地址私有化没做发现未登录也能拿到付费视频完整地址说明is_free开关配错了。这三处过了核心付费链路才算通。上线前还要把config.php里的app_debug关掉改掉管理员默认密码并把后台入口、安全码换成随机值。然后导出一次全库 SQL 作为上线备份。这个动作不花时间但每次都能救回一晚上。我已经因为“回调没做幂等”在线上翻过两次车一次是用户重复支付另一次是微信重试通知把用户会员时间叠加了两倍。后来我把所有支付回调都改成先查状态再累加上线后立刻安静了。希望这个习惯对你有用希望帮到你。本文还有配套的精品资源点击获取
返回列表