简介这份资源面向微信平台互动营销开发入门者与中小商家提供一套可直接参考的砸金蛋小游戏实现方案用于公众号活动引流、品牌推广与用户互动场景。压缩包共6个文件约91KB以4张png图片素材、1个php后端脚本和1个html页面为主图片承担金蛋与界面视觉元素html负责游戏页面结构php处理数据交互与奖品逻辑体量轻便便于本地调试与二次修改。目前已有218人学习下载适合作为练手项目参考。内容围绕微信开发者平台配置、公众号AppID与AppSecret调用、JS-SDK分享接口、HTML5与CSS3动画、JavaScript随机抽奖逻辑、后端请求处理与数据库表设计等环节展开并涉及防刷奖与SQL注入等安全校验思路读者可据此理解从页面搭建到后端联调的完整链路并在此基础上调整奖品规则与交互反馈。1. 微信砸金蛋一个被低估的营销互动小项目上周有个做运营的朋友找我说他们公众号想搞个砸金蛋活动预算不多问我能不能直接找个现成的源码改改。我第一反应是这玩意儿不是十年前就烂大街了吗结果翻了一圈发现真正能跑起来、代码结构还说得过去的完整项目并不多。微信砸金蛋这类互动营销页本质上是公众号生态里一个高频复用的活动模板用户关注后进入页面点击金蛋触发动画后端按概率发放奖品同时记录中奖信息和用户 openid。它适合两类人——一类是想快速上线一个活动页的运营团队另一类是想拿它当微信网页开发练手入口的开发者。技术栈通常不复杂前端 HTML/CSS/JS 加动画后端 PHP 或 Node.js 配 MySQL核心难点不在技术深度而在概率控制、防刷和微信授权链路的完整性。下面我按「能干什么、怎么跑、怎么改」的顺序把这个项目的骨架拆开讲。2. 项目结构与技术选型从目录看一个活动页的完整链路2.1 典型目录结构长什么样这类微信砸金蛋项目源码包解压后一般能看到前后端混布的结构常见做法是根目录下分api、static、view三个大块外加一个config放数据库和微信参数。我拿一个典型的 PHP 版本举例目录大致如下egg-smash/ ├── api/ │ ├── auth.php # 微信网页授权回调 │ ├── prize.php # 抽奖核心接口 │ └── record.php # 中奖记录查询 ├── config/ │ ├── db.php # 数据库连接配置 │ └── wechat.php # appid / secret / 活动开关 ├── static/ │ ├── css/ │ ├── js/ │ │ ├── hammer.js # 砸蛋动画与点击逻辑 │ │ └── request.js # 统一请求封装 │ └── images/ # 金蛋、锤子、奖品图 ├── view/ │ ├── index.html # 活动主页面 │ └── result.html # 中奖结果页 └── sql/ └── init.sql # 建表语句与初始奖品数据这个结构不花哨但把「授权—抽奖—记录」三条线分得很清楚。api/auth.php负责拿 code 换 openidapi/prize.php是唯一真正决定给不给奖的入口static/js/hammer.js只管动画和点击节奏不碰业务判断。这种前后端职责切分是这类项目能二次开发的前提——你改概率逻辑不用动前端换皮肤不用碰后端。2.2 技术栈与依赖说明后端以 PHP 5.6/7.x 居多依赖 mysqli 或 PDO不需要框架直接原生写。少数版本会用 ThinkPHP 3.2 这类轻框架但核心逻辑还是裸 SQL。前端不依赖构建工具jQuery 或原生 JS 都行动画靠 CSS3 transform 加 transition砸蛋的碎裂效果一般是预先切好的序列帧图或者 CSS 伪元素模拟。数据库就两张核心表prize存奖品池和库存record存中奖流水。微信侧依赖公众号的网页授权snsapi_base 或 snsapi_userinfo和 JS-SDK 的分享接口这两块需要在公众号后台配置授权域名和 JS 安全域名。提示如果源码里config/wechat.php的 appid 和 secret 是空的说明作者把敏感信息抽走了你需要自己填别指望开箱即用。2.3 一次砸蛋请求的完整链路用户点金蛋到看到结果中间经过四步前端hammer.js捕获点击播放锤子落下动画同时向api/prize.php发一个 POST 请求带上 openid 和活动 id后端先查record表看这个 openid 今天是否已中过奖再查prize表按剩余库存和权重算出一个奖品如果命中扣库存、写流水、返回奖品信息前端拿到结果后跳转result.html展示。这条链路里最容易被忽略的是「先扣库存再返回」的顺序很多翻车案例都是先返回成功再异步扣库存结果并发一上来库存变负数。3. 核心模块拆解抽奖概率、微信授权与防刷3.1 抽奖接口的概率控制逻辑api/prize.php是整个项目的心脏我把它最关键的片段拎出来讲。下面这段是常见的权重抽奖实现?php // api/prize.php require ../config/db.php; $openid $_POST[openid] ?? ; $activityId intval($_POST[activity_id] ?? 1); // 1. 校验当天是否已中奖 $stmt $pdo-prepare(SELECT COUNT(*) FROM record WHERE openid ? AND activity_id ? AND DATE(create_time) CURDATE()); $stmt-execute([$openid, $activityId]); if ($stmt-fetchColumn() 0) { exit(json_encode([code 1, msg 今天已经砸过啦])); } // 2. 取出有库存的奖品按权重排序 $stmt $pdo-query(SELECT id, name, weight, stock FROM prize WHERE stock 0 ORDER BY id); $prizes $stmt-fetchAll(PDO::FETCH_ASSOC); // 3. 权重区间法抽奖 $total array_sum(array_column($prizes, weight)); $rand mt_rand(1, $total); $current 0; $hit null; foreach ($prizes as $p) { $current $p[weight]; if ($rand $current) { $hit $p; break; } } // 4. 扣库存并写流水事务保证一致性 if ($hit) { $pdo-beginTransaction(); $pdo-prepare(UPDATE prize SET stock stock - 1 WHERE id ? AND stock 0)-execute([$hit[id]]); $pdo-prepare(INSERT INTO record (openid, prize_id, activity_id, create_time) VALUES (?, ?, ?, NOW())) -execute([$openid, $hit[id], $activityId]); $pdo-commit(); exit(json_encode([code 0, prize $hit[name]])); } exit(json_encode([code 2, msg 谢谢参与]));这段代码的逻辑说明第一步用DATE(create_time) CURDATE()做每日限次简单但有效第二步只取stock 0的奖品避免抽到空奖第三步是经典的权重区间法weight越大命中概率越高比如一等奖 weight 设 1、二等奖设 10、谢谢参与设 100总权重 111随机数落在 1 到 111 之间落在哪个区间就给哪个奖第四步用事务包住扣库存和写流水UPDATE ... WHERE stock 0这个条件很关键它能在并发下防止库存被扣成负数。参数上weight建议用整数别用小数mt_rand对整数区间更稳activity_id是为了支持同一套代码跑多个活动改活动只改数据不改代码。3.2 微信网页授权的接入点api/auth.php负责把微信回调的 code 换成 openid这是所有后续逻辑的前提。常见写法?php // api/auth.php require ../config/wechat.php; $code $_GET[code] ?? ; if (!$code) { // 第一步跳转到微信授权页 $redirect urlencode(https://你的域名/api/auth.php); $url https://open.weixin.qq.com/connect/oauth2/authorize?appid{$appid}redirect_uri{$redirect}response_typecodescopesnsapi_basestate1#wechat_redirect; header(Location: $url); exit; } // 第二步用 code 换 access_token 和 openid $api https://api.weixin.qq.com/sns/oauth2/access_token?appid{$appid}secret{$secret}code{$code}grant_typeauthorization_code; $resp json_decode(file_get_contents($api), true); if (isset($resp[openid])) { // 把 openid 写入 session 或跳回主页带上参数 session_start(); $_SESSION[openid] $resp[openid]; header(Location: /view/index.html); } else { exit(授权失败 . ($resp[errmsg] ?? 未知错误)); }这里scopesnsapi_base是静默授权用户无感知只能拿到 openid如果要拿昵称头像就换snsapi_userinfo但会弹授权框转化率会掉。redirect_uri必须和公众号后台配置的授权域名一致否则微信会报 redirect_uri 参数错误。state参数建议带上活动 id 或随机串防止 CSRF虽然这类活动页风险不高但养成习惯没坏处。3.3 防刷与并发控制砸金蛋活动最怕两种刷法一种是同一 openid 反复请求接口另一种是脚本批量换 openid 薅羊毛。第一层防线是上面代码里的每日限次查询但它有并发漏洞——两个请求同时进来都查到 0 条记录就都放行了。更稳的做法是在record表上对(openid, activity_id, DATE(create_time))建唯一索引让数据库兜底ALTER TABLE record ADD UNIQUE KEY uk_openid_day (openid, activity_id, create_time);注意这里create_time如果精确到秒唯一索引拦不住同一天不同秒的重复常见做法是额外加一个day字段存YYYY-MM-DD对(openid, activity_id, day)建唯一索引。第二层防线是接口频率限制用 Redis 记openid的请求次数比如 10 秒内超过 5 次直接拒绝。第三层是奖品库存的原子扣减前面UPDATE ... WHERE stock 0已经覆盖。这三层叠起来普通脚本刷不动真遇到专业羊毛党还得加上手机号验证或关注状态校验。4. 跑起来环境配置与本地调试的完整步骤4.1 环境准备与数据库初始化本地跑这个项目最省事的组合是 PHP 7.4 MySQL 5.7 NginxWindows 下用 phpStudy 或 XAMPP 一把梭Mac 下用 Homebrew 装。先把源码放到 web 根目录然后建库导数据# 登录 MySQL 建库 mysql -u root -p -e CREATE DATABASE egg_smash DEFAULT CHARSET utf8mb4; # 导入建表语句和初始奖品 mysql -u root -p egg_smash sql/init.sqlinit.sql里一般会建prize和record两张表并插入几条初始奖品。导完后检查config/db.php里的 host、user、password、dbname 是否和本地一致不一致就改。这里有个血泪经验很多源码包的init.sql用的是utf8而不是utf8mb4如果奖品名里有 emoji 会插入失败建库时统一用utf8mb4能省掉后面改表的麻烦。4.2 微信配置与本地联调技巧config/wechat.php里要填 appid 和 secret这两个在公众号后台「开发—基本配置」里拿。本地调试时微信授权回调没法直接指向 localhost常见做法是用内网穿透工具把本地 80 端口映射到一个临时域名再把该域名填到公众号的授权域名里。如果只是调抽奖逻辑不想碰微信授权可以临时在auth.php里写死一个测试 openid// 本地调试用上线前务必删掉 $_SESSION[openid] test_openid_001; header(Location: /view/index.html);这样跳过微信授权直接进主页能快速验证砸蛋动画和抽奖接口。上线前记得把这行删掉我见过有人忘了删结果线上所有用户都变成同一个 openid中奖记录全串了。4.3 前端页面与静态资源路径view/index.html里引用的 CSS、JS、图片路径通常是相对路径如果你把项目放在子目录下比如http://localhost/egg-smash/要确认static/js/hammer.js里的接口请求地址也带上了子目录前缀否则会出现 404。常见做法是在request.js里统一配一个baseUrl// static/js/request.js const BASE_URL /egg-smash; // 部署在根目录就改成空字符串 function post(url, data) { return fetch(BASE_URL url, { method: POST, headers: { Content-Type: application/x-www-form-urlencoded }, body: new URLSearchParams(data) }).then(res res.json()); }改BASE_URL一处所有接口请求跟着变比在每个页面里硬编码路径靠谱。图片资源建议压缩到 100KB 以内活动页加载速度直接影响参与率尤其是微信里网络波动大首屏超过 3 秒很多人就退了。5. 避坑与常见问题上线前必须过的几道坎5.1 授权域名配置后仍报 redirect_uri 错误现象是用户点进活动页微信直接白屏或提示「redirect_uri 参数错误」。原因通常是公众号后台配置的授权域名和auth.php里拼的redirect_uri不完全一致比如一个带www一个不带或者协议是 http 而配置的是 https。解决方法是把redirect_uri的域名部分单独抽成配置项和公众号后台逐字符核对包括末尾斜杠。另外授权域名不支持端口号本地映射的临时域名别带:8080这种后缀。5.2 并发下库存变负数或超发现象是活动结束后对账发现中奖记录数大于奖品初始库存。原因就是前面说的先查后扣没有原子性两个请求同时读到 stock1 都判断有货。解决办法是把扣减和判断合并成一条 SQLUPDATE prize SET stock stock - 1 WHERE id ? AND stock 0然后检查rowCount()是否为 1为 0 说明没扣到回滚事务返回「谢谢参与」。这个改动能挡住绝大部分并发超发剩下的极端情况靠数据库行锁兜底。5.3 中奖记录查不到但用户说中了现象是用户截图显示中了二等奖但后台record表里没有这条记录。原因多半是前端拿到成功响应后后端写流水的操作在事务提交前就返回了或者事务回滚了但前端已经跳转结果页。解决方法是确保exit(json_encode(...))在commit()之后执行并且前端只在code 0时才跳结果页。另外检查record表的openid字段长度微信 openid 是 28 位如果建表时设成varchar(20)会被截断查记录时对不上。5.4 活动页在 iOS 微信里动画卡顿现象是安卓机上砸蛋动画流畅iOS 上锤子落下卡成幻灯片。原因是用了setTimeout驱动逐帧动画iOS 的渲染节奏和安卓不同。解决办法是改用 CSS3transition或requestAnimationFrame把动画交给合成层处理同时给金蛋元素加will-change: transform提示浏览器提前优化。图片序列帧的话控制在 12 帧以内每帧尺寸不超过 200×200。5.5 分享出去的活动页打开是空白现象是用户从朋友圈或聊天窗口点进来页面白屏但从公众号菜单进正常。原因是分享链接带上了#后面的参数而index.html里的 JS 没有处理 hash 路由或者微信 JS-SDK 的签名域名没配。解决办法是在index.html加载时先解析location.search和location.hash把活动 id 和来源参数取出来同时确认公众号后台的「JS 接口安全域名」和「网页授权域名」都配了当前域名两个缺一不可。6. 二次开发把模板改成你自己的活动系统拿到这套源码大多数人不会只满足于跑通而是想改成能复用的活动系统。我的建议是从数据层动手而不是先改页面。第一步把prize表扩展成activityprize两张表activity存活动名称、起止时间、每日限次、是否强制关注prize加activity_id外键。这样一套代码能同时跑多个活动运营改活动只改数据。第二步把prize.php里的硬编码逻辑抽成函数比如checkLimit($openid, $activityId)、drawPrize($activityId)、writeRecord(...)方便单测和替换。第三步前端把hammer.js里的动画参数抽成配置对象换皮肤只改图片和时长不动逻辑。验证改造是否成功我一般用三个检查点一是用两个不同 openid 并发请求抽奖接口看库存和记录是否一致二是把活动时间改成已过期确认接口返回「活动已结束」而不是继续发奖三是清空prize表库存确认所有请求都落到「谢谢参与」且不报错。这三个点过了基本能上线。有个细节值得单独说微信 JS-SDK 的签名在二次开发时经常被忽略。如果你要加自定义分享文案api/下得补一个jssdk.php生成签名前端wx.config里填的jsApiList要和实际调用的接口对应。签名用的 URL 必须是当前页面去掉#后面的完整地址且每次页面 URL 变化都要重新签名否则 iOS 上分享卡片会丢图丢标题。从那以后我每次接这类活动项目都会先把「授权—抽奖—记录」三条链路的日志打全尤其是 openid 和奖品 id 的对应关系上线后对账全靠它。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?