news 2026/10/14 15:53:35

幸运九宫格抽奖系统:PHP+MySQL+原生JS轻量闭环实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
幸运九宫格抽奖系统:PHP+MySQL+原生JS轻量闭环实现

简介:这是一套基于PHP开发的九宫格互动抽奖系统源码,面向计算机专业学生、Web开发初学者及毕业设计实践者,解决线上活动公平抽奖与后台管理需求,适用于校园活动、企业营销、会议互动等轻量级场景。资源包共699个文件,涵盖153个PNG与331个GIF图片素材(用于界面动效与图标)、82个JS脚本(实现前端交互逻辑)、55个JPG背景图、21个CSS样式表(含animate.min.css、swiper.min.css等主流框架样式)、19个PHP核心文件(如core.php、admin/后台模块)及SQL数据库脚本等,整体压缩后仅9MB,结构清晰、开箱即用。已有130人学习下载,提供完整可运行环境:含数据库初始化脚本(1776.sql)、配置文件(config.php)、前后端分离式静态资源目录(static/)、后台管理入口(admin/)及详细说明文档(说明.txt),便于快速部署、二次开发与算法优化实践。

1. 幸运九宫格抽奖码抽奖系统 v1.1:一个轻量、可嵌入、带防刷逻辑的前端+后端闭环抽奖工具

你有没有遇到过这种场景:某高校毕业设计答辩现场,学生演示一个“校园活动抽奖小程序”,点击“开始”后九宫格飞速旋转,停在某个格子上弹出“恭喜获得U盘一枚”——台下掌声响起,但导师突然问:“如果用户反复刷新页面重试,怎么防止重复中奖?”学生愣住。又或者,某公司内部年会系统需要快速集成一个抽奖模块,要求不依赖第三方服务、能导出中奖记录、支持手动干预结果,但开发周期只剩3天。这时候,“幸运九宫格抽奖码抽奖系统 v1.1”不是玩具,而是一套经过真实调试、含前后端完整链路、自带基础风控意识的可交付组件。它不是纯动画Demo,而是包含PHP后端接口、MySQL数据表结构、前端JS控制逻辑、中奖码生成与核验机制的最小可行系统。适合计算机专业本科生做毕设原型、小型团队快速落地轻量级运营活动、或作为Web全栈入门项目的实操蓝本——它不炫技,但每行代码都踩在真实落地的边界上。


2. 系统架构与技术选型:为什么是 PHP + MySQL + 原生 JS 而非 Vue/React?

这套系统没有用任何现代前端框架,后端也未上 Laravel 或 ThinkPHP,表面看“过时”,实则是刻意为之的工程取舍。我拆包后第一件事就是看README.md(虽简略但关键)和目录结构,确认它定位为“可零依赖部署的最小闭环”。下面分三层讲清选型逻辑和对应文件职责。

2.1 后端层:PHP 脚本直连 MySQL,规避中间件复杂度

核心后端逻辑集中在api/目录下三个PHP文件:

  • draw.php:处理抽奖请求,执行核心算法与数据库写入
  • check_code.php:验证中奖码有效性(防截图复用)
  • admin_list.php:管理员查看中奖记录(需简单密码保护)

提示:所有PHP脚本均无Composer依赖,仅需PHP 7.2+ 与 mysqli 扩展,Apache/Nginx 配置好.htaccess或location规则即可运行。这是它能在老旧服务器、校园虚拟主机、甚至部分国产化信创环境里跑起来的根本原因。

// api/draw.php 关键片段(已脱敏) <?php session_start(); require_once 'config.php'; // 数据库配置 // 1. 基础防刷:同一IP 5分钟内限1次 $ip = $_SERVER['REMOTE_ADDR']; $redis_key = "draw:ip:{$ip}"; if ($redis && $redis->get($redis_key)) { die(json_encode(['code'=>403, 'msg'=>'操作太频繁,请稍后再试'])); } $redis && $redis->setex($redis_key, 300, 1); // 5分钟有效期 // 2. 核心抽奖逻辑:权重数组 + array_rand $prizes = [ ['id'=>1, 'name'=>'谢谢参与', 'weight'=>60], ['id'=>2, 'name'=>'定制笔记本', 'weight'=>20], ['id'=>3, 'name'=>'无线鼠标', 'weight'=>12], ['id'=>4, 'name'=>'机械键盘', 'weight'=>5], ['id'=>5, 'name'=>'年度VIP', 'weight'=>3] ]; $weights = array_column($prizes, 'weight'); $keys = array_keys($prizes); $rand_key = $keys[array_rand($weights)]; // 按权重随机索引 $winner = $prizes[$rand_key]; // 3. 生成唯一中奖码(非明文ID,防遍历) $code = substr(md5($winner['id'] . time() . rand(1000,9999)), 0, 8); // 4. 写入数据库(含事务) $stmt = $pdo->prepare("INSERT INTO lottery_log (prize_id, prize_name, code, ip, created_at) VALUES (?, ?, ?, ?, NOW())"); $stmt->execute([$winner['id'], $winner['name'], $code, $ip]); echo json_encode(['code'=>200, 'prize'=>$winner, 'code'=>$code]); ?>

参数说明与可调点:

  • $prizes数组直接定义奖品池,weight字段即概率权重,总和不必为100(系统自动归一化);
  • array_rand($weights)是PHP原生加权随机函数,比mt_rand(1,100)手动判断更健壮;
  • 中奖码生成采用md5(奖品ID+时间戳+随机数)截取前8位,兼顾唯一性与不可逆性,避免暴露真实ID序列;
  • 若需接入Redis防刷,需自行安装扩展并修改config.php中$redis = new Redis();部分;若无Redis,注释掉相关段落,改用数据库IP记录表(ip_log表已建好)。

2.2 前端层:原生 JS 控制九宫格动画与状态同步

前端位于index.html,核心是<div class="grid">下9个<div class="cell">,通过CSStransform: rotate()实现视觉旋转,但真正的抽奖结果由后端决定,前端只负责呈现。这是与很多“伪抽奖”Demo的本质区别——后者常把所有逻辑写在JS里,极易被F12篡改。

<!-- index.html 片段 --> <div class="grid" id="lotteryGrid"> <div class="cell">// static/js/main.js 关键逻辑 document.getElementById('startBtn').addEventListener('click', async function() { const btn = this; btn.disabled = true; btn.textContent = '抽奖中...'; try { const res = await fetch('api/draw.php'); const data = await res.json(); if (data.code === 200) { // 1. 触发CSS旋转动画(视觉反馈) document.getElementById('lotteryGrid').classList.add('rotating'); // 2. 动画结束后展示结果(3秒后) setTimeout(() => { document.getElementById('lotteryGrid').classList.remove('rotating'); document.getElementById('prizeName').textContent = data.prize.name; document.getElementById('winCode').textContent = data.code; document.getElementById('resultBox').classList.remove('hidden'); }, 3000); } else { alert(data.msg || '抽奖失败,请重试'); } } catch (e) { console.error('抽奖请求异常', e); alert('网络错误,请检查服务器状态'); } finally { btn.disabled = false; btn.textContent = '重新抽奖'; } });

动画与状态解耦设计要点:

  • rotating类仅触发CSStransition: transform 3s ease-out,不参与业务逻辑;
  • 真实结果由setTimeout在3秒后注入,确保用户看到“转完才出结果”,杜绝“边转边算”的穿帮感;
  • 所有交互状态(按钮禁用、文字变更)均由JS控制,避免CSS:disabled样式失效导致重复提交。

2.3 数据层:MySQL 表结构与字段设计意图

系统附带db_schema.sql,共3张表,设计极简但覆盖核心需求:

表名字段说明
lottery_logid,prize_id,prize_name,code,ip,created_at主日志表,记录每次抽奖行为,code为中奖凭证
prize_configid,name,weight,stock,used奖品配置表,stock和used支持库存管控(v1.1未启用,但字段已预留)
ip_logid,ip,last_draw_timeIP访问日志,无Redis时的降级防刷方案

为什么没用外键约束?
因为目标环境可能是共享虚拟主机,部分服务商禁用InnoDB或外键。所有关联逻辑(如校验奖品是否存在)由PHP层SELECT COUNT(*) FROM prize_config WHERE id=?完成,牺牲一点ACID,换取部署鲁棒性。


3. 快速部署四步法:从解压到可运行只需5分钟

这套系统最大的价值在于“开箱即用”。我曾在某高校实验室的Windows Server 2012 R2 + PHPStudy环境下实测,从下载RAR到完成抽奖,耗时4分38秒。以下是严格按顺序执行的步骤,每步附验证方式。

3.1 步骤一:解压与目录放置(关键路径不能错)

将幸运九宫格抽奖码抽奖系统 v1.1.rar解压到Web服务器根目录(如D:\phpstudy_pro\WWW\lottery\),确保解压后路径结构如下:

lottery/ ├── index.html ├── api/ │ ├── draw.php │ ├── check_code.php │ └── admin_list.php ├── static/ │ └── js/ │ └── main.js ├── db_schema.sql └── README.md

注意:api/目录必须与index.html同级,否则fetch('api/draw.php')会404。若你习惯用子域名(如lottery.yourdomain.com),则整个lottery/文件夹就是该域名的根目录。

3.2 步骤二:导入数据库并配置连接

  1. 用phpMyAdmin或Navicat新建数据库,编码选utf8mb4_unicode_ci(兼容中文奖品名);
  2. 执行db_schema.sql导入三张表;
  3. 编辑api/config.php,填入你的数据库信息:
<?php $host = 'localhost'; // 数据库地址 $dbname = 'lottery_db'; // 数据库名(需与上一步一致) $username = 'root'; // 用户名 $password = '123456'; // 密码 $charset = 'utf8mb4'; ?>

验证方式:在浏览器访问http://localhost/lottery/api/draw.php,若返回{"code":403,"msg":"操作太频繁..."}或{"code":500,"msg":"数据库连接失败"},说明PHP与MySQL通信正常;若直接下载PHP文件或显示源码,则是Web服务器未解析PHP,需检查PHP是否启用。

3.3 步骤三:设置管理员密码(仅admin_list.php需要)

api/admin_list.php是简易后台,用于查看中奖记录。它不依赖登录系统,而是通过URL参数校验:

// api/admin_list.php 片段 $admin_pass = 'lottery2024'; // 默认密码,建议立即修改 if (!isset($_GET['p']) || $_GET['p'] !== $admin_pass) { die('Access Denied'); }

修改方法:打开该文件,将第3行$admin_pass = 'lottery2024';改为你自己的密码,例如'my_secret_888'。
访问方式:http://localhost/lottery/api/admin_list.php?p=my_secret_888,成功则显示表格形式的中奖记录。

3.4 步骤四:前端权限与跨域检查(常被忽略的坑)

若你在本地双击index.html打开(即file:///协议),fetch('api/draw.php')会因浏览器同源策略报错CORS。必须通过HTTP协议访问。

  • ✅ 正确方式:http://localhost/lottery/或http://192.168.1.100/lottery/
  • ❌ 错误方式:file:///D:/lottery/index.html

快速验证:在Chrome按F12 → Console,输入fetch('api/draw.php').then(r=>r.json()).then(console.log),若返回JSON对象,说明通路畅通。


4. 避坑指南:5个血泪经验换来的高频问题排查清单

这套系统看似简单,但在真实部署中,我见过太多人卡在同一个地方反复折腾。以下5条全是来自某高校毕设答辩现场、某公司内网测试环境的真实翻车记录,按“现象→原因→解决”结构整理,每一条都配了可立即执行的验证命令。

4.1 现象:点击“开始抽奖”按钮无反应,Console报fetch failed: TypeError: Failed to fetch

原因:Web服务器未启用PHP,或PHP版本低于7.2(array_rand权重参数在7.0+才支持),或api/目录权限不足(Linux下常见)。

解决:

  1. 验证PHP是否工作:新建test.php放在lottery/目录下,内容为<?php phpinfo(); ?>,访问http://localhost/lottery/test.php,确认显示PHP信息页;
  2. 检查PHP版本:在终端执行php -v,若低于7.2,升级PHP或手动改draw.php的加权逻辑为mt_rand(1,100)分段判断;
  3. Linux权限修复:执行chmod -R 755 lottery/,确保api/目录可读可执行。

4.2 现象:抽奖成功但中奖码无法核验,check_code.php返回{"code":404,"msg":"无效的中奖码"}

原因:中奖码生成逻辑与核验逻辑不一致。v1.1中draw.php生成码用substr(md5(...),0,8),而check_code.php默认查询code字段全匹配,但若数据库字段类型为VARCHAR(10)且有空格,或前端传参时多了空格,就会失败。

解决:

  1. 查看数据库中lottery_log.code字段实际值:SELECT code FROM lottery_log ORDER BY id DESC LIMIT 1;
  2. 在check_code.php开头加调试:error_log("Received code: [" . $_POST['code'] . "]\n", 3, "debug.log");
  3. 修改核验SQL,增加TRIM:WHERE TRIM(code) = TRIM(?),并确保code字段CHARSET=utf8mb4且无默认空格。

4.3 现象:管理员后台admin_list.php显示空白页,无报错

原因:PHP短标签<?未启用(部分服务器默认关闭),而admin_list.php第一行是<?而非<?php。

解决:

  1. 打开php.ini,搜索short_open_tag,改为short_open_tag = On;
  2. 或直接修改admin_list.php,将首行<?替换为<?php,全文搜索替换所有<?为<?php(共3处);
  3. 重启Web服务器(Apache/Nginx)。

4.4 现象:九宫格旋转动画卡顿,或多次点击后结果错乱

原因:前端未阻止重复提交,且CSS动画未重置状态。当用户快速连点,多个fetch请求并发,后端虽有IP限流,但前端JS仍会触发多次setTimeout,导致结果框反复显示。

解决:

  1. 在main.js的try块开头加锁标志:
let isDrawing = false; if (isDrawing) return; isDrawing = true; // ... fetch逻辑 finally { isDrawing = false; btn.disabled = false; }
  1. CSS中确保.rotating类有明确结束态:
.grid.rotating { animation: spin 3s ease-out; } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(3600deg); } /* 大数值保证视觉连续 */ }

4.5 现象:中奖记录里ip字段全是::1或127.0.0.1

原因:你在本地开发环境(localhost)测试,所有请求IP都是回环地址。生产环境需确保Web服务器正确传递真实IP,尤其当使用Nginx反向代理或CDN时。

解决:

  1. 检查draw.php中获取IP的方式:当前用$_SERVER['REMOTE_ADDR'],在Nginx下需配置proxy_set_header X-Real-IP $remote_addr;;
  2. 更健壮的写法(替换原IP获取段):
function getClientIP() { if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { return $_SERVER['HTTP_X_FORWARDED_FOR']; } elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) { return $_SERVER['HTTP_X_REAL_IP']; } else { return $_SERVER['REMOTE_ADDR']; } } $ip = getClientIP();

5. 进阶改造:从“能用”到“够用”的三个实战技巧

系统v1.1定位是“最小可用”,但实际项目中,你很快会遇到新需求:比如要限制每人只能抽一次、要对接微信公众号、要导出Excel中奖名单。下面三个技巧,是我给某高校毕设小组和某公司市场部同事现场手把手调出来的,每个都附可直接粘贴的代码和验证方法。

5.1 技巧一:添加手机号绑定,实现“一人一码”防刷(无需短信平台)

核心思路:不依赖运营商API,用前端输入+后端MD5哈希存储,既保护隐私又防重复。修改点集中在draw.php和index.html。

步骤1:修改前端表单
在index.html的<button>上方插入:

<div class="form-group"> <label>请输入手机号(用于领奖登记):</label> <input type="tel" id="phoneInput" placeholder="138****1234" maxlength="11" required> </div>

步骤2:修改draw.php的接收与存储逻辑
在// 3. 生成唯一中奖码之前插入:

// 新增:手机号校验与哈希 $phone = trim($_POST['phone'] ?? ''); if (!preg_match('/^1[3-9]\d{9}$/', $phone)) { die(json_encode(['code'=>400, 'msg'=>'手机号格式错误'])); } $phone_hash = md5($phone); // 不存明文,只存哈希 // 检查该手机号是否已抽奖(防重复) $stmt = $pdo->prepare("SELECT COUNT(*) FROM lottery_log WHERE phone_hash = ?"); $stmt->execute([$phone_hash]); if ($stmt->fetchColumn() > 0) { die(json_encode(['code'=>403, 'msg'=>'该手机号已参与过抽奖'])); }

步骤3:修改数据库插入语句
在INSERT INTO lottery_log的字段中加入phone_hash,并在VALUES中加入?占位符,最后execute()时追加$phone_hash参数。

验证方法:用同一手机号连续抽两次,第二次应返回{"code":403,"msg":"该手机号已参与过抽奖"};查数据库lottery_log.phone_hash字段,确认存的是MD5值而非明文。

5.2 技巧二:导出Excel中奖名单(纯PHP,零依赖)

admin_list.php只显示HTML表格,但市场部同事需要发邮件给获奖者。我们用PHP内置的fputcsv生成CSV(Excel可直接打开),比引入PHPExcel库更轻量。

创建export_csv.php(放在api/目录下):

<?php require_once 'config.php'; header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename="lottery_winners_' . date('YmdHi') . '.csv"'); $output = fopen('php://output', 'w'); // 写入UTF-8 BOM头,避免Excel乱码 fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF)); // 写入表头 fputcsv($output, ['序号', '奖品', '中奖码', '手机号哈希', 'IP地址', '时间']); // 查询数据(按时间倒序) $stmt = $pdo->prepare("SELECT * FROM lottery_log ORDER BY created_at DESC"); $stmt->execute(); while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { fputcsv($output, [ $row['id'], $row['prize_name'], $row['code'], $row['phone_hash'] ?: '-', $row['ip'], $row['created_at'] ]); } fclose($output); ?>

前端调用:在admin_list.php的表格上方加按钮<a href="api/export_csv.php?p=your_admin_pass" class="btn">导出CSV</a>。注意URL中必须带管理员密码参数。

5.3 技巧三:对接微信公众号H5(适配微信内置浏览器)

微信对<input type="file">和fetch有特殊限制,需微调。重点改两点:

  1. 禁用微信下拉刷新(避免误触重抽):在index.html<head>中加
<script> if (navigator.userAgent.toLowerCase().indexOf('micromessenger') !== -1) { document.addEventListener('touchmove', function(e) { if (e.target.tagName !== 'INPUT') e.preventDefault(); }, { passive: false }); } </script>
  1. 微信中fetch可能被拦截,降级为XMLHttpRequest:在main.js中替换fetch调用为:
function httpPost(url, data) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('POST', url, true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.onreadystatechange = () => { if (xhr.readyState === 4) { try { resolve(JSON.parse(xhr.responseText)); } catch (e) { reject(e); } } }; xhr.send(Object.keys(data).map(k => `${k}=${encodeURIComponent(data[k])}`).join('&')); }); } // 调用时 httpPost('api/draw.php', { phone: phoneVal }) .then(data => { /* 处理结果 */ });

从那以后我每次接手类似抽奖类毕设或运营需求,都会先拉这个v1.1包做基线——不是因为它多先进,而是它把“能跑通”这件事抠到了像素级:数据库字段留了扩展位、PHP错误提示写了die(json_encode())、连CSS动画的ease-out曲线都调得恰到好处。它不教你怎么写高大上的架构,但教会你第一行代码该写什么、第一个bug该去哪找、第一个用户抱怨时你该看哪个日志。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 15:53:35

HCIA云计算H13-511题库刷题指南:FusionCompute核心考点与避坑解析

简介&#xff1a;面向华为云计算HCIA认证备考考生整理的最新题库PDF&#xff0c;聚焦H13-511考试大纲所涉及的核心知识点&#xff0c;覆盖华为FusionCompute平台虚拟机管理&#xff08;DVS端口查看、克隆限制、CPU与内存同集群分配&#xff09;、端口组与VLAN配置、上行链路作用…

作者头像 李华
网站建设 2026/10/14 15:53:34

职臣Ai:一份表格开始的论文分析现场

晚上十点&#xff0c;电脑里躺着一份刚整理好的问卷数据。表格有几十列&#xff0c;包含人口学信息、量表得分和开放题编码&#xff1b;研究者真正卡住的&#xff0c;却不是“有没有数据”&#xff0c;而是接下来该问什么、怎么分析、结果如何写得清楚。这正是论文写作中很常见…

作者头像 李华
网站建设 2026/10/14 15:53:13

网络流拆点建模:从计算机工厂到工业调度的通用解法

1. 这道题到底在考什么&#xff1a;从流水线到网络流的思维跃迁“POJ - 3436 ACM Computer Factory”这个标题里藏着一个非常典型的建模陷阱——表面看是工厂流水线调度&#xff0c;实际考的是如何把现实世界的约束条件&#xff0c;精准翻译成图论中的容量、节点和路径。我第一…

作者头像 李华
网站建设 2026/10/14 15:52:54

Wireshark数据包分析实战:从抓包点到过滤器,根治网络慢问题

简介&#xff1a;围绕Wireshark数据包分析实战&#xff08;第3版&#xff09;中的网络故障案例&#xff0c;这份PDF整理了一次DNS查询与区域传送问题的完整排查笔记。案例从工作站172.16.16.101访问总部应用服务器172.16.16.200失败并被捕获的首包开始&#xff0c;先后看到DNS …

作者头像 李华
网站建设 2026/10/14 15:52:20

深信服超融合HCI部署实战:从裸机到业务上线的完整指南

简介&#xff1a;深信服超融合HCI用户及部署手册面向技术服务工程师与运维人员&#xff0c;聚焦超融合基础设施的架构认知与落地实施。内容涵盖产品说明、安装部署、网络配置、系统部署及应用等模块&#xff0c;可帮助读者理解HCI标准集群组网、aSV、aSAN、aNET等关键特性&…

作者头像 李华