简介:这套价值1.5W的竞技电竞比分源码,主要面向需要搭建电竞比分、LOL赛事预测与竞猜平台的开发者或站长,已修复后台登录报错、比赛采集、推广二维码等常见问题,并支持后台直接设置玩法,免二次买分。包体共2000个文件,以js、css、html前端页面为主,另有php后台逻辑、txt文本说明、json配置、sql数据库及sh部署脚本,压缩包整体约70.47MB,可配合宝塔面板、Nginx、PHP7.3、Mysql8.0环境快速部署。目前已有176人学习下载,适合有一定ThinkPHP基础、希望直接获得可运行源码并二次开发的用户。资源内含安装搭建说明、ThinkPHP伪静态规则、public运行目录设置、后台账号密码及Swoole4等扩展安装指引,可帮助降低环境配置门槛,快速进入功能调试与业务定制。
1. 一套带“免买分”机制的电竞比分源码,为什么会值得拆
收到一套金刚竞技电竞比分源码,我先看它是不是换皮站。资源站里同类包常有三宗罪:后台登录白屏、比赛采集停更、推广二维码扫出 localhost。实际拆下后,这套包和废包的区别在于 LOL 赛事数据入口是通的,后台还能开启免买分,不强制用户先充值买分,直接押预测结果,对测试环境和小规模运营非常友好。下文按宝塔面板 + Tengine + PHP7.3 + MySQL8.0 环境记录部署链路,从数据库配置、伪静态、后台玩法设置到采集修复,是一份可照着改的安装教程。适合快速搭建电竞比分类站点、接手这类 ThinkPHP 老项目的开发者。
2. 部署链路选型:Tengine/PHP7.3/MySQL8.0 与 Swoole4 扩展
2.1 为什么宝塔配 Tengine 最稳妥
这套包进到Application/目录,就能看出是典型 ThinkPHP 3.2 架构。老框架对 PHP 版本很挑剔,PHP 7.4 开始废弃不少语法特性,如果偏离测试环境清单,很可能在后台登录一步就挂掉。宝塔面板里安装 Nginx 时,默认编译的就是 Tengine 分支,配置语法和标准 Nginx 一致,root、rewrite、fastcgi_pass都可以直接抄,不用单独做兼容。Apache 在 PATHINFO 模式下还要解析.htaccess,和这套包的public运行目录配合很容易出现 404,不建议在赛前验证阶段浪费时间。
MySQL 用 8.0 是因为原包就是按 8.0 导的 SQL,如果你本地只有 MySQL 5.7,导入时可能遇到 utf8mb4 排序规则不识别的问题。Tengine、PHP7.3、MySQL8.0 三者的组合,是这个项目最接近生产环境的组合,后续给用户演示赛事预测时也少一些环境差异带来的麻烦。
2.2 安装 fileinfo、redis、Swoole4 扩展
宝塔软件商店先安装 PHP7.3,打开 PHP 设置页,在“安装扩展”列表里勾选fileinfo和redis。fileinfo负责后台头像、比赛图片上传时的 MIME 类型校验;redis供缓存、队列以及 Swoole 协程连接使用。扩展列表里通常没有swoole4,需要到命令行手动编译安装:
# 进入 PHP7.3 的 bin 目录,用 PECL 安装 Swoole4 cd /www/server/php/73/bin ./pecl remote-info swoole ./pecl install swoole-4.8.13逻辑说明:remote-info先看 PECL 仓库里 swoole 的可用版本和依赖要求,避免直接装到只支持 PHP8 的主线版本。swoole-4.8.13是可以和 PHP7.3 配合的 4.x 系列,安装完成后在php.ini里加一行extension=swoole.so,再重启 PHP 服务。注意:宝塔的命令行php可能不是你刚装的 7.3,验证扩展时最好用全路径:
/www/server/php/73/bin/php -m这里有个参数细节:pecl install编译 swoole 时会询问是否开启openssl,如果项目要用wss://推送比分,就选yes;如果只跑 HTTP 定时采集,选no也能跑。安装完后不要忘记看phpinfo里的Loaded Configuration File,确认extension=swoole.so写到了正确的 ini 文件,否则重启后扩展依然不加载。
| 扩展 | 用途 | 安装方式 | 验证命令 |
|---|---|---|---|
| fileinfo | 上传文件类型识别 | 宝塔扩展列表勾选 | php -m | grep fileinfo |
| redis | 缓存、队列、采集去重 | 宝塔扩展列表勾选 | php -m | grep redis |
| swoole4 | 实时推送、定时采集 | PECL 编译安装 | php -m | grep swoole |
2.3 PHP7.3 下的兼容性排查
老代码在 PHP7.3 最典型的坑是create_function被移除。后台登录、验证码、数据导出都可能调用它,一旦调用直接Fatal error,表现就是/admin.php白屏或者 500。拿到源码包后,先全仓搜一遍:
grep -rn "create_function" Application/搜索到之后,按下述方式替换:
// 老代码 $func = create_function('$str', 'return strtoupper($str);'); // PHP7.3 安全写法 $func = function ($str) { return strtoupper($str); };逻辑说明:create_function本质是把字符串当函数体执行,PHP7.3 后彻底移除。匿名函数的行为完全一致,不会改变业务逻辑。替换后顺手清空Application/Runtime里的缓存文件,否则框架可能还在跑旧编译结果。这一步做完,后台登录报错的概率会下降一大截。
3. 数据库配置、伪静态与 public 运行目录:让后台先能登录
3.1 Application/database.php 的五个关键值
打开Application/database.php,这套包用的是 ThinkPHP 3.2 风格配置数组。最小修改示例:
return array( 'DB_HOST' => '127.0.0.1', 'DB_PORT' => '3306', 'DB_NAME' => 'jingang', 'DB_USER' => 'jgjg_user', 'DB_PWD' => '你的数据库密码', 'DB_PREFIX' => 'jg_', );逻辑说明:DB_HOST用127.0.0.1不走 unix socket,连接语义更直观;DB_PREFIX必须和 SQL 导入后的表前缀一致,比如是jg_,后台查询jg_admin时才不会提示数据表不存在。改完配置后,还有一个 MySQL8.0 特有的坑:PHP7.3 的 mysqli 不一定能连上默认使用caching_sha2_password认证插件的账号。建议在 MySQL 里把对应账号改成mysql_native_password:
ALTER USER 'jgjg_user'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的数据库密码'; FLUSH PRIVILEGES;说明:mysql_native_password是老客户端认证方式,改成它之后 PHP7.3 才能稳定连接。如果用的是root测试,也要对 root 执行这条 SQL。导入数据库时,小 SQL 用 phpMyAdmin 就行,大 SQL 推荐命令行:mysql -u root -p jingang < jingang.sql。
3.2 public 运行目录与 Nginx 伪静态
如果站点 root 直接指向项目根目录,访问时可能直接打开ThinkPHP目录下的文件,框架路径容易泄露。宝塔中在站点设置里把运行目录指向/public,再加伪静态规则:
location / { if (!-e $request_filename) { rewrite ^/(.*)$ /index.php?s=$1 last; } } location ~ \.php$ { fastcgi_pass unix:/tmp/php-cgi-73.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; }逻辑说明:if (!-e $request_filename)表示文件或目录不存在时,把请求交给index.php?s=,这是 ThinkPHP 3.2 兼容 PATHINFO 模式的标准写法。fastcgi_pass必须换成宝塔 PHP7.3 的 socket 路径,不同系统可能是unix:/tmp/php-cgi-xx.sock,以 phpinfo 里的信息为准。如果fastcgi_pass没匹配对,首页可能能开,但所有伪静态链接全部 404,后台也进不去。
3.3 后台登录报错的几个修复位置
后台/admin.php登录报错,先看运行日志:Application/Runtime/Logs/Admin/。常见有三种:
| 现象 | 排查位置 | 处理方式 |
|---|---|---|
| 验证码不刷新 | SESSION_OPTIONS中 cookie_domain 写死旧域名 | 改成当前域名或留空 |
| 点击登录无响应 | create_function 引用还在 | 按 2.3 方式替换匿名函数 |
/admin.php404 | admin.php 没放进 public 目录 | 复制入口文件到 public 并绑定模块 |
如果确认是admin.php在项目根目录,而运行目录是public,就需要在public下放一份admin.php,里面显式绑定后台模块:
define('BIND_MODULE', 'Admin'); require __DIR__ . '/ThinkPHP/ThinkPHP.php';逻辑说明:BIND_MODULE告诉框架直接进入后台模块,等价于访问index.php?s=/Admin。require路径按实际结构调整,目标是让入口文件能找到ThinkPHP目录。改完刷新/admin.php,输入 admin / 123456,能进入后台就说明数据库配置和伪静态已经通了。
4. 采集修复与免买分玩法:Swoole 定时任务与后台开关
4.1 采集脚本挂掉,先看字段映射和请求参数
电竞数据源的返回字段经常和数据库不一致,比如接口返回team_a、team_b,表里存的是home_team、away_team。如果直接add,写入的可能是全是空字符串。常见做法是先写一个请求类,统一定义超时、UA、SSL 校验:
public function request($url) { $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 15); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $body = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return array('code' => $code, 'body' => json_decode($body, true)); }逻辑说明:CURLOPT_FOLLOWLOCATION让采集跟随数据源的重定向,CURLOPT_TIMEOUT=15防止接口长时间挂起;CURLOPT_SSL_VERIFYPEER=false只在测试环境使用,如果数据源支持正式 HTTPS 证书,应删除这行并配置 CA 证书。采集挂掉时先看返回的code,如果是 403,一般是 UA 被拦或接口需要签名;如果是 500,检查字段映射。
4.2 用 Swoole Timer 做秒级采集
crontab 最小粒度是分钟,LOL 赛事预测场景里,比赛开始前几分钟的赔率变化很关键,需要秒级更新。常见做法是写一个独立的 Swoole 服务,用tick定时器每 5 秒拉取一次:
require __DIR__ . '/ThinkPHP/ThinkPHP.php'; $server = new Swoole\Server('0.0.0.0', 9503); $server->on('WorkerStart', function ($server, $workerId) { if ($workerId == 0) { $server->tick(5000, function () { $task = new \Admin\Service\MatchCollectService(); $task->sync(); }); } }); $server->start();逻辑说明:tick(5000, ...)以毫秒为单位,每 5000 毫秒执行一次sync()。$workerId == 0防止多进程重复采集,否则同一个比赛会被写入多条记录。CLI 启动时注意在sync()开头加set_time_limit(0),避免执行超过默认 30 秒被掐断。用nohup挂到后台:
nohup php public/collect_server.php start >> runtime/collect.log 2>&1 &collect.log里能看到每次拉取的耗时和返回条数,排查时先看这个日志再动代码。
4.3 免买分竞猜玩法与下注接口
“免买分”实际是后台玩法开关,开启后用户竞猜时不再校验积分余额,直接用免费次数或基础额度下注。后台配置表写入free_bet = 1,下注控制器判断:
$freeBet = M('config')->where(array('name' => 'free_bet'))->getField('value'); $amount = intval(I('post.amount')); if ($freeBet != 1) { $user = M('user')->where(array('id' => $uid))->find(); if ($user['money'] < $amount) { $this->error('积分不足,请先充值'); } M('user')->where(array('id' => $uid))->setDec('money', $amount); } M('bet')->add(array( 'user_id' => $uid, 'match_id' => I('post.match_id'), 'play_type' => I('post.play_type'), 'amount' => $amount, 'create_time' => time(), ));逻辑说明:$amount要用后端intval强转,不能直接相信前端传值,避免提交负数或 0。setDec是数据库原子自减,不会因为并发读到旧值而覆盖。后台玩法设置表可以按赛事类型配置:
| 玩法名称 | 字段 | 说明 |
|---|---|---|
| 胜/负 | win_team | 选择主胜或客胜 |
| 一血 | first_blood | 哪一方先拿一血 |
| 五杀 | five_kill | 是否出现五杀 |
| 大龙数 | dragon_count | 大小龙数量的上下盘 |
4.4 订单写入的幂等与事务
直接用add写订单,高并发下会出现同一用户对同一比赛多次投注。需要在“比赛ID + 用户ID + 玩法ID”上建立唯一索引,插入前先查一次bet表:
$exists = M('bet')->where(array( 'user_id' => $uid, 'match_id' => I('post.match_id'), 'play_type' => I('post.play_type'), ))->find(); if ($exists) { $this->error('本玩法已投注,请勿重复提交'); }同时用事务包裹扣款和下单:
M()->startTrans(); try { M('user')->where(array('id' => $uid))->setDec('money', $amount); M('bet')->add($betData); M()->commit(); } catch (\Exception $e) { M()->rollback(); $this->error('下单失败,请重试'); }说明:startTrans确保扣款和订单写入在同一个事务里,任一失败都回滚。如果后续要接入开奖结算,把bet_id推进 Redis 队列,后台消费队列时统一更新中奖结果,不要在下注接口里直接算奖,否则并发高时 CPU 会被拖垮。
5. 安装验证与推广二维码调试:从日志到扫码的闭环
5.1 用 SQL 验证采集链路
登录 MySQL 后,查询最近三天的比赛分布:
SELECT DATE(FROM_UNIXTIME(start_time)) AS d, COUNT(*) AS cnt FROM jg_match WHERE start_time > UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 3 DAY)) GROUP BY d;如果cnt每天都有值,说明采集链路基本正常;如果某一天断档,回看runtime/collect.log里那段时间的报错。还有一类情况是记录都在但start_time全是 0,通常是采集时把字符串时间直接入库,没有用strtotime转换,修复点在字段映射层。
5.2 推广二维码本地化调试
后台生成推广二维码时,最容易出问题的是链接拼成了http://localhost。这套包通常依赖后台配置的site_url,但老版本会写死。注册页二维码生成代码一般长这样:
$url = C('SITE_URL') . '/index.php?s=/Register/reg&pid=' . $uid; QRcode::png($url, false, QR_ECLEVEL_L, 8);逻辑说明:pid是推荐人用户 ID,QR_ECLEVEL_L是低纠错级别,二维码尺寸更小;如果二维码中间要叠加 Logo,改用QR_ECLEVEL_M,避免扫码扫不出来。验证时用手机扫码,先看落地 URL 是否带pid,注册成功后再查用户表:
SELECT uid, pid, create_time FROM jg_user ORDER BY uid DESC LIMIT 5;如果pid为 0,说明注册控制器没把 URL 参数传给写入逻辑,检查Register/reg方法里是不是把I('pid')写成了I('uid')。
5.3 Swoole 状态与 Redis 命中确认
采集和下注队列都依赖 Swoole 和 Redis,最后确认进程和键值状态:
ps aux | grep collect_server redis-cli -n 0 LLEN bet_queue redis-cli -n 0 TTL collect:lasttimeLLEN返回当前待结算订单数,TTL返回采集时间键的剩余秒数。如果collect:lasttime每隔几秒回弹一次,说明 Swoole 定时器仍存活,采集链路正在向 Redis 写心跳;此时再返回后台比赛列表,应该能看到新同步的场次,整个安装闭环就通了。
本文还有配套的精品资源,点击获取