简介:一套2023全新彩虹晴天多功能系统源码,定位为知识付费与虚拟商城一体化平台,也可扩展为实物商城、虚拟卡网,适合站长、开发者直接部署运营或二次开发。源码无需授权,国内外服务器及备案与否均可搭建,支持商业使用,并内置无限代理分销、普通/高级分站、项目投稿、推广发圈、售后工单等功能,业务扩展性较强。包体共951个文件,以346个PHP程序文件、310个PNG图片、78个CSS样式、73个JS脚本为主,另含14个SQL数据库文件及少量视频、字体、音频等素材,压缩包约40.86MB,结构完整便于部署维护。发布前已经作者修复更新,近期已有1173人学习下载,适合具备PHP环境搭建经验的用户快速安装使用,也可作为商城类项目源码参考。
1. 不用再靠手动发卡密:彩虹晴天多功能系统解决的第一件事
知识付费项目只要能卖货,就会遇到一个不上不下的阶段:订单数开始多了,但还没多到值得招一个人专门管发货。这个时候最磨人的不是卖,而是对账和交付——课程卖出去的瞬间,给谁开通、给谁发卡密、钱到没到账,全靠脑子里挂着一根弦。彩虹晴天这套源码就是为这段路准备的。它是一个基于 PHP + MySQL 的虚拟商城系统,把商品展示、下单、支付、自动发货、分销推广全部收到一套后台里,目标就是让你从「人肉发货」变成「系统自动处理」。适合两类人:一类是手里有课程、会员、卡密这类虚拟货品,想快速搭出独立商城的个人开发者;另一类是给客户交付商城项目、需要现成底座做二次开发的程序员。包内预置了 1800+ 条商品和内容数据,安装完不是空架子,前后台立刻能点开看效果。下面我从架构、核心代码、部署到改造,按一套真实商城该有的样子拆开讲。
2. 架构链路:一次点击从 public 入口到订单落库的完整路径
2.1 技术形态:虚拟商品场景下,PHP 单体为什么够用
实体电商要考虑全国库存、物流跟踪、仓储调拨,虚拟商城要简单得多,业务核心只有四件事:商品、订单、支付、发货。彩虹晴天这套源码采用的是经典 PHP 单体 Web 结构,不涉及微服务拆分,也不需要队列中间件,一台常规配置的服务器就能支撑中等日活的商城读写。
它在代码层面做了三层分离:
- 入口层:
public/下的脚本接收所有 HTTP 请求,统一做路由转发; - 业务层:前台、后台、接口按模块划分,各自持有控制器和模型;
- 数据层:全部 SQL 操作收敛到模型与公共函数里,没有散落各处的原生查询。
这种形态的好处是心智负担低。上线一台机器能跑,二次开发时按控制器 → 模型 → 视图的顺序找代码即可。等到订单量真的大了,再把静态资源扔到 CDN、把高频接口改成独立 API 服务,都属于可平滑演进的方向,不存在结构性的死路。
2.2 目录结构:从入口脚本到业务模块的映射
解压源码后,目录关系大致如下:
public/ index.php 前台入口,所有用户请求的起点 admin.php 后台入口,独立于前台 static/ CSS / JS / 图片等静态资源 application/ admin/ 后台管理控制器 api/ 小程序与 H5 的 JSON 接口 index/ 前台商城控制器 common/ 公共函数与基础控制器 model/ 数据模型层 config/ database.php 数据库连接配置 pay.php 支付渠道参数 data/ *.sql 初始数据脚本,含 1800+ 条商品、课程与卡密 runtime/ cache/ 编译缓存 log/ 业务运行日志 media/ upload/ 后台传上来的图片素材 vendor/ autoload.php 第三方依赖加载入口有几个目录值得单独说明。
public/是唯一应该对外暴露的目录。部署时把站点根目录指到public,而不是整个源码根目录,这样才能避免data、application里的文件被直接通过 URL 访问,这是这类系统最常见的安全隐患。admin.php独立于index.php存在,好处是后台入口路径固定,方便在 Nginx 层单独加访问控制;坏处是如果你不想让后台地址被扫描到,部署后最好把它改名,并在代码里同步更新入口文件名。
application/api/是为小程序、H5 预留的接口层。它和前台控制器的区别在于返回值格式,Api 模块的方法统一返回 JSON,前台模块则渲染 HTML 模板。如果你后续要接微信小程序,只需要复用api模块的控制器,再配一套登录鉴权即可。
2.3 数据库主线:四张核心表如何串联一笔订单
从data/*.sql里能看到,整个系统的主数据表可以提炼为四张核心表:
| 表名 | 职责 | 关键字段 | 与其他表的关系 |
|---|---|---|---|
| rg_user | 用户、会员等级、余额 | id, vip_level, balance, openid | 被订单和分销关联 |
| rg_goods | 课程与虚拟商品 | id, title, goods_type, price | 订单引用 goods_id |
| rg_order | 订单流水 | order_sn, uid, goods_id, pay_status | 串联用户与商品 |
| rg_card | 卡密库存 | id, goods_id, code, status | 发货时被订单消费 |
四个表的关系可以这样理解:用户在商城下一笔订单后,数据写入rg_order;支付回调成功,系统根据rg_goods.goods_type判断走哪条发货分支;如果是课程,就往会员开通记录里写一条数据;如果是卡密商品,就取rg_card中status = 0的一条加密卡片标记为已售出,绑定当前订单号。
rg_user.vip_level的妙处在于它支撑了会员体系:用户购买「年度会员」这类虚拟商品时,支付成功会直接把等级字段更新,而不是生成卡密。这也解释了为什么这套系统适合知识付费——它的数据设计从根上就支持「商品即权限」的玩法。
3. 核心模块拆解:下单、支付回调与自动发货的实现细节
3.1 用户端下单:生成订单号的完整过程
前台商城的下单逻辑在application/index/下,核心动作是生成一笔pay_status = 0的待支付订单。以「立即购买」为例,代码如下:
public function createOrder($goodsId) { // 1. 校验商品是否存在且在售 $goods = Goods::where('id', $goodsId)->find(); if (!$goods || $goods['status'] != 1) { return json(['code' => 0, 'msg' => '商品已下架']); } // 2. 生成唯一订单号:日期 + 用户ID + 随机数 $orderSn = date('YmdHis') . str_pad(session('uid'), 4, '0', STR_PAD_LEFT) . str_pad(mt_rand(1, 999), 3, '0', STR_PAD_LEFT); // 3. 写入待支付订单 $orderId = Order::create([ 'order_sn' => $orderSn, 'uid' => session('uid'), 'goods_id' => $goodsId, 'goods_type' => $goods['goods_type'], 'price' => $goods['price'], 'pay_status' => 0 ]); return json(['code' => 1, 'order_sn' => $orderSn]); }这里有两个容易忽视的点。
一是订单号生成规则。日期 + 用户ID + 随机数的组合在并发量不大时足够唯一,还方便后台根据订单号反查用户。如果后续订单量增长,建议改成Redis 自增序列,避免同一秒内撞号。
二是创建订单阶段不扣库存、不发卡密。因为这时候钱还没到,真正的交付动作全部放在支付回调里。这个设计非常重要,它保证了一笔订单不会因为用户关闭页面而丢失,用户随时可以通过订单列表继续支付。
3.2 支付回调:自动发货的最后一道关卡
支付回调是整个系统的核心,也是最容易写错的模块。彩虹晴天的回调逻辑可以抽象为一段伪代码加真代码的组合:
public function notify() { // 1. 取回调参数并验证签名(以微信支付为例) $data = $this->parseXml(file_get_contents('php://input')); // 2. 根据订单号查订单 $order = Order::where('order_sn', $data['out_trade_no'])->find(); if (!$order) { return 'fail'; // 订单不存在,直接返回失败 } // 3. 关键判断:防止重复发货 if ($order['pay_status'] == 1) { return 'success'; // 已处理过,直接告知微信成功 } // 4. 更新订单为已支付 $order->save([ 'pay_status' => 1, 'pay_time' => time(), 'trade_no' => $data['transaction_id'] ]); // 5. 按商品类型自动发货 if ($order['goods_type'] == 'course') { $this->unlockCourse($order['uid'], $order['goods_id']); } elseif ($order['goods_type'] == 'card') { $this->sendCard($order['id'], $order['goods_id']); } return 'success'; }第三步的判断是整个函数的命脉。支付平台在极端情况下会重复推送回调,如果没有pay_status检查,卡密会被重复扣货、课程会被重复开通。我在不少商城源码里见过这行缺失,后果是同一笔订单发了两张卡,售后直接爆炸。
sendCard的实现同样需要注意并发:
private function sendCard($orderId, $goodsId) { // 从库存里取一条未售出的卡密 $card = Card::where('goods_id', $goodsId) ->where('status', 0) ->order('id asc') ->find(); if (!$card) { // 记录缺货日志,通知人工介入 Log::write("订单号{$orderId}缺卡"); return; } $card->save([ 'status' => 1, 'order_id'=> $orderId ]); // 把卡密写入订单详情,用户端可见 OrderGoods::create([ 'order_id' => $orderId, 'card_code'=> $card['code'] ]); }这里有个细节:取卡密用的是order('id asc')而不是随机取,目的是让最早的卡密先发出,避免某批次卡密长期滞留。库存不足时不能抛异常,必须记录日志交给人工补卡,否则用户已经付了钱却拿不到货。
3.3 后台订单管理与分销逻辑
后台控制器在application/admin/下,订单列表、发货管理、提现审核都集中在这一层。它和前台模块共用数据模型,动作逻辑基本是围绕状态字段做流转:订单状态在待支付、已支付、已完成之间切换,后台可以手动补发卡密、调整订单价格、处理退款。
分销模块则是这套系统的另一个亮点,适合卖课程赚佣金的场景。实现上并不复杂:每个用户有专属邀请链接,链接带refer_id参数;新用户通过链接注册后,该关系写入分销绑定表;后续新用户下单,系统按后台设定的佣金比例把金额写入推荐人的「待结算余额」,提现后再冻结转余额。
分销提现的关键代码逻辑:
public function withdraw() { $uid = session('uid'); $user = User::find($uid); $amount = input('post.amount'); // 只能提现可提现余额,不可透支 if ($amount <= 0 || $amount > $user['commission']) { return json(['code' => 0, 'msg' => '可提现余额不足']); } // 冻结金额,避免并发提现超扣 $user->commission -= $amount; $user->freeze += $amount; $user->save(); }先冻结再打款,是处理虚拟资产提现的通用做法。如果用户提现申请被拒绝,再把冻结余额解冻退回即可,这个「冻结中转」设计避免了两笔提现同时扣同一笔佣金的概率。
4. 快速跑起来:环境准备、数据导入与第一笔测试订单
4.1 环境准备:PHP 版本与扩展是最容易踩的隐形门槛
这套源码跑起来不需要特别重的环境,常规的 PHP 集成环境即可,核心要求是 PHP 7.1 - 7.3 版本区间,太新的 PHP 8.x 会出现兼容性报错,后面避坑章节会展开讲。
部署前的环境确认项:
- PHP 版本 7.1 或 7.2 或 7.3,不要用 8.0+;
- 开启
openssl、pdo_mysql、curl扩展; - MySQL 5.6 或 5.7,字符集建议
utf8mb4; - Nginx 或 Apache 均可,Nginx 需要配置伪静态规则。
伪静态规则是很多人忽略的细节。Nginx 环境下需要把请求转发到入口文件:
location / { if (!-e $request_filename){ rewrite ^(.*)$ /index.php?s=$1 last; } }如果不配这条规则,商品详情页、订单页会出现 404 或路由错误,而后台入口是admin.php,不受这条规则影响,表现就是「前台打不开,后台能进」,容易误判成源码损坏。
4.2 导入 1800+ 条数据:两种安装方式
拿到源码后有两种方式初始化数据。第一种是用自带的安装引导:访问http://你的域名/install/,按步骤填写数据库地址、库名、账号密码,系统会自动建表并写入初始数据。
第二种是手动导入,适合安装引导失败时兜底。先把data/目录下的 SQL 文件导入数据库,再手动改配置文件:
mysql -uroot -p --default-character-set=utf8mb4 rainbow_shop < data/install.sql导入完成后修改config/database.php:
return [ 'host' => '127.0.0.1', 'database' => 'rainbow_shop', 'username' => 'root', 'password' => 'your_password', 'prefix' => 'rg_', ];保证data/、runtime/、media/目录有写权限,否则后续上传商品图、生成缓存时会报目录不可写。
4.3 第一笔测试订单:从前台到后台的完整闭环
数据导入成功之后,我建议按下面顺序验证一遍,这个顺序能覆盖系统 80% 的核心链路:
第一步,用后台初始账号登录,进入商品管理,确认 1800+ 条数据里有课程类也有卡密类商品,编辑任一商品确认图片能正常显示。
第二步,打开前台首页,注册一个新用户,选一个虚拟卡密商品加入购物车并下单,在支付页面不要真付款,回后台订单列表查看这笔订单是否出现在待支付列表。
第三步,在后台把该笔订单手动标记为已支付(开发阶段可以用这个方式绕开支付回调),然后检查订单详情是否生成了卡密记录。如果生成了,说明自动发货链路没问题。
第四步,再将支付配置切到测试模式,用测试密钥走一遍真实支付回调,确认pay_status自动更新、库存卡密自动扣减。
这套验证顺序把「页面展示」「订单创建」「发货逻辑」「支付回调」四个环节拆开测试,哪一步断了就定位哪一环,比直接对着真支付调试高效得多。
5. 避坑指南:部署彩虹晴天最容易翻车的 5 个地方
5.1 后台登录直接白屏
现象:安装完成后访问后台登录页,页面一片空白,浏览器控制台也没有明显报错。
原因:当前环境 PHP 版本过高,源码中的一些旧式写法在 PHP 8 下直接抛出致命错误,而部分集成环境默认关闭了错误显示,看起来就是白屏。
解决:把运行环境的 PHP 版本切到 7.3,或者在入口文件顶部临时加error_reporting(E_ALL); ini_set('display_errors', 1);让具体报错显示出来。确认是版本兼容问题后,再调回 7.3 环境。
5.2 支付回调重复触发导致卡密多发
现象:一笔订单在日志里出现了多次回调记录,后台显示卡密被取走两条。
原因:回调接口没有校验pay_status是否已为 1,微信或支付宝重试推送时,系统把同一笔订单当成了新订单再次发货。
解决:在回调处理的最前面判断订单状态,一旦已支付就立即返回成功标志,不再执行任何发货逻辑。这也是我在 3.2 节代码里特意把状态判断放在第三步的原因——它必须是发货动作的前置条件。
5.3 前台链接全部 404,后台却正常
现象:商品点击进去报 404,首页正常,后台各页面都能打开。
原因:Nginx 环境下缺少伪静态规则,路由参数没有被正确解析到index.php。
解决:按 4.1 节补上 rewrite 规则。Apache 环境则确认.htaccess文件存在且被AllowOverride All允许。
5.4 SQL 导入后中文乱码
现象:后台商品标题显示为乱码,数据库里也是问号。
原因:SQL 文件包含中文数据,导入时连接字符集与文件声明不一致,导致 utf8 内容被错误解析。
解决:导入命令显式指定字符集,使用--default-character-set=utf8mb4,同时确认config/database.php中的编码配置为utf8mb4。
5.5 本地环境支付验签总是不通过
现象:订单已支付,但回调地址收到的数据验签失败,订单一直停留待支付。
原因:本地环境没有公网域名,回调地址需要在支付平台侧配置为公网可访问的 HTTPS 地址,而本地地址无法被支付平台主动回访。
解决:开发阶段不建议本地联调支付,更好的方式是部署到测试服务器,或在本地用内网穿透工具把回调地址映射为 HTTPS 地址,再把支付平台的回调 URL 指向映射后的地址。回调日志里记录每一笔请求参数,对比签名逻辑很快能定位问题。
6. 二次开发思路:给彩虹晴天新增一个自定义支付通道
很多场景下默认的支付方式并不够用,比如客户需要挂接某个聚合支付平台,或者内部账户余额支付。新增支付通道的流程可以总结为三步:加配置、加下单方法、加回调处理方法。
以新增一个「余额支付」为例,这是理解这套框架最好的练习。下单接口判断如果用户选择余额支付,直接走内部账户扣款,不需要外部回调:
public function balancePay($orderSn) { $order = Order::where('order_sn', $orderSn)->find(); $user = User::find($order['uid']); // 余额不足则返回错误,由前端引导用户充值 if ($user['balance'] < $order['price']) { return json(['code' => 0, 'msg' => '余额不足']); } // 同一笔订单只允许支付一次 if ($order['pay_status'] == 1) { return json(['code' => 0, 'msg' => '订单已支付']); } // 扣余额、改订单状态、自动发货 $user->balance -= $order['price']; $user->save(); $order->pay_status = 1; $order->pay_type = 'balance'; $order->save(); // 复用原本的自动发货逻辑 $this->deliverGoods($order); return json(['code' => 1, 'msg' => '支付成功']); }改造中的关键点是把deliverGoods逻辑从回调方法里抽出来复用。这个动作看似简单,实际很多源码都没做到,导致新增支付通道时不得不复制一大段发货代码。我建议在二次开发前先完成这个重构,把所有发货动作统一收口到一个公共方法,后续接入任意支付渠道都只需要处理支付确认本身,不需要再关心发货细节。
从那以后我每次拿这套源码做项目,都会强制自己先走一遍「下单 → 回调 → 发货 → 对账」的完整流程,确认状态流转没问题再开始加功能。如果你准备拿它开工,也建议从第 4 章的测试顺序开始,把环境跑顺后,再按自己业务去做支付方式和模板的扩展,希望帮到你。
本文还有配套的精品资源,点击获取