当前位置:网站首页 >  教程

从零构建付费课程裂变分销系统的完整技术方案

时间:2026年05月24日 06:27:29 来源:易频IT社区

技术栈选型与环境准备

本方案采用 Node.js 作为后端运行环境,MySQL 作为核心数据存储,Redis 用于处理高并发下的分布式锁与缓存。这种组合在处理订单状态机与裂变计算时具有极高的稳定性。请确保你的开发环境已安装 Node.js v14+ 及 MySQL 5.7+。

我们需要创建项目目录并安装必要的依赖包。请在终端执行以下命令:

mkdir course-fission-system && cd course-fission-system

npm init -y

npm install express mysql2 ioredis uuid crypto-js body-parser --save

这里我们使用 mysql2 因为它支持 Promise 且性能优于 mysql 驱动,ioredis 是 Redis 的优秀客户端。安装完成后,我们将进入数据库结构设计阶段,这是裂变系统的地基。

数据库表结构设计

我们需要设计三张核心表:用户表(存储邀请码与层级关系)、订单表(记录购买状态)、佣金记录表(记录收益流向)。请在你的 MySQL 数据库中执行以下 SQL 语句,确保字段与索引设置完全一致,否则后续代码无法运行。

```sql CREATE DATABASE IF NOT EXISTS fission_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE fission_db; -- 用户表 CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `openid` varchar(64) NOT NULL COMMENT '用户唯一标识', `invite_code` varchar(10) NOT NULL COMMENT '用户的邀请码', `inviter_id` int(11) DEFAULT 0 COMMENT '直接邀请人的ID,0表示无邀请人', `balance` decimal(10,2) DEFAULT 0.00 COMMENT '可提现余额', `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_openid` (`openid`), UNIQUE KEY `uk_invite_code` (`invite_code`), KEY `idx_inviter` (`inviter_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 课程订单表 CREATE TABLE `orders` ( `id` int(11) NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL COMMENT '订单号', `user_id` int(11) NOT NULL COMMENT '购买者ID', `course_id` int(11) NOT NULL COMMENT '课程ID', `amount` decimal(10,2) NOT NULL COMMENT '支付金额', `status` tinyint(1) DEFAULT 0 COMMENT '0:待支付, 1:已支付, 2:已结算', `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 佣金流水表 CREATE TABLE `commissions` ( `id` int(11) NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL COMMENT '关联订单号', `ancestor_id` int(11) NOT NULL COMMENT '获益人ID', `descendant_id` int(11) NOT NULL COMMENT '购买者ID', `level` tinyint(1) NOT NULL COMMENT '层级: 1一级, 2二级', `amount` decimal(10,2) NOT NULL COMMENT '佣金金额', `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_order_no` (`order_no`), KEY `idx_ancestor` (`ancestor_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ```

项目核心配置与连接封装

从零构建付费课程裂变分销系统的完整技术方案

在项目根目录下创建 db.js 文件,用于封装数据库和 Redis 连接。请直接替换以下配置中的 host、user 和 password 为你本地的数据库信息。

```javascript // db.js const mysql = require('mysql2/promise'); const Redis = require('ioredis'); // MySQL 连接池配置 const pool = mysql.createPool({ host: '127.0.0.1', user: 'root', password: 'your_password', database: 'fission_db', waitForConnections: true, connectionLimit: 10, queueLimit: 0 }); // Redis 连接配置 const redis = new Redis({ host: '127.0.0.1', port: 6379, password: '' // 如果Redis设置了密码,在此填写 }); module.exports = { pool, redis }; ```

核心功能:用户注册与邀请关系绑定

裂变的基础是绑定上下级关系。我们需要在用户注册或首次访问时,通过 URL 参数中的 invite_code 确定其上级。创建 app.js,引入依赖并编写注册逻辑。

这里我们需要一个生成随机邀请码的辅助函数,以及处理注册的 API 接口。为了保证数据一致性,注册逻辑中必须包含事务处理。

```javascript // app.js (部分代码) const express = require('express'); const bodyParser = require('body-parser'); const { pool, redis } = require('./db'); const app = express(); app.use(bodyParser.json()); // 生成6位随机邀请码 const generateInviteCode = () => { return Math.random().toString(36).substring(2, 8).toUpperCase(); }; // 用户注册/登录接口 app.post('/api/register', async (req, res) => { const { openid, invite_code } = req.body; const connection = await pool.getConnection(); try { await connection.beginTransaction(); // 检查用户是否存在 const [users] = await connection.execute('SELECT id FROM users WHERE openid = ?', [openid]); if (users.length > 0) { await connection.rollback(); return res.json({ code: 0, msg: '用户已存在', data: { userId: users[0].id } }); } // 解析邀请人ID let inviterId = 0; if (invite_code) { const [inviters] = await connection.execute('SELECT id FROM users WHERE invite_code = ?', [invite_code]); if (inviters.length > 0) { inviterId = inviters[0].id; } } const myInviteCode = generateInviteCode(); // 插入新用户 const [result] = await connection.execute( 'INSERT INTO users (openid, invite_code, inviter_id) VALUES (?, ?, ?)', [openid, myInviteCode, inviterId] ); await connection.commit(); res.json({ code: 1, msg: '注册成功', data: { userId: result.insertId, inviteCode: myInviteCode } }); } catch (err) { await connection.rollback(); res.json({ code: -1, msg: '注册失败: ' + err.message }); } finally { connection.release(); } }); ```

核心功能:支付回调与佣金裂变逻辑

这是整个系统的核心。当用户支付成功后,系统必须执行以下原子操作:

  • 更新订单状态:防止重复发货。
  • 查找上级关系:追溯直接邀请人和间接邀请人。
  • 计算并发放佣金:一级分销 20%,二级分销 10%。
  • 增加用户余额:将佣金写入用户表。

为了防止高并发下的重复发放(如支付回调重试),我们引入 Redis 分布式锁。请在 app.js 中继续添加以下代码:

```javascript // 模拟支付回调处理接口 app.post('/api/payment/callback', async (req, res) => { const { order_no, total_fee } = req.body; // 假设这是支付平台传回的数据 const lockKey = `lock:order:${order_no}`; try { // 1. 获取分布式锁,防止并发重复处理,设置锁过期时间为10秒 const lock = await redis.set(lockKey, '1', 'NX', 'EX', 10); if (!lock) { return res.json({ code: 0, msg: '订单处理中,请勿重复提交' }); } const connection = await pool.getConnection(); await connection.beginTransaction(); // 2. 校验订单状态 const [orders] = await connection.execute('SELECT FROM orders WHERE order_no = ?', [order_no]); if (orders.length === 0) { await connection.rollback(); return res.json({ code: -1, msg: '订单不存在' }); } const order = orders[0]; if (order.status === 1) { await connection.rollback(); // 实际业务中可能直接返回成功 return res.json({ code: 1, msg: '订单已处理' }); } // 3. 更新订单状态为已支付 await connection.execute('UPDATE orders SET status = 1 WHERE order_no = ?', [order_no]); // 4. 获取购买者信息(查找邀请人) const [buyers] = await connection.execute('SELECT inviter_id FROM users WHERE id = ?', [order.user_id]); if (buyers.length === 0) { await connection.commit(); return res.json({ code: 1, msg: '支付成功,无佣金' }); } const buyer = buyers[0]; let currentInviterId = buyer.inviter_id; const commissionRules = [0.20, 0.10]; // 一级20%,二级10% // 5. 循环计算佣金(最多向上追溯2级) for (let level = 0; level < commissionRules.length; level++) { if (currentInviterId === 0) break; // 没有上级了 const commissionAmount = order.amount commissionRules[level]; // 增加上级余额 await connection.execute( 'UPDATE users SET balance = balance + ? WHERE id = ?', [commissionAmount, currentInviterId] ); // 记录佣金流水 await connection.execute( 'INSERT INTO commissions (order_no, ancestor_id, descendant_id, level, amount) VALUES (?, ?, ?, ?, ?)', [order_no, currentInviterId, order.user_id, level + 1, commissionAmount] ); // 查找上一级的邀请人,用于下一次循环 const [parents] = await connection.execute('SELECT inviter_id FROM users WHERE id = ?', [currentInviterId]); if (parents.length > 0) { currentInviterId = parents[0].inviter_id; } else { currentInviterId = 0; } } await connection.commit(); res.json({ code: 1, msg: '支付成功,佣金已结算' }); } catch (err) { console.error(err); res.json({ code: -1, msg: '系统错误' }); } finally { // 释放锁 await redis.del(lockKey); } }); // 启动服务 app.listen(3000, () => { console.log('Server running on http://localhost:3000'); }); ```

防刷与安全细节补充

在生产环境中,上述代码还需要配合 IP 限流和签名校验。请在 app.js 头部添加以下中间件,防止恶意请求伪造支付回调。

```javascript // 简单的IP限流中间件 const ipMap = new Map(); app.use((req, res, next) => { const ip = req.ip; const now = Date.now(); if (ipMap.has(ip)) { const lastTime = ipMap.get(ip); if (now - lastTime < 100) { // 100ms内同一IP只能请求一次 return res.status(429).json({ code: -1, msg: '请求过于频繁' }); } } ipMap.set(ip, now); next(); }); ```

至此,一个完整的付费课程裂变分销系统已经搭建完成。你可以通过 Postman 向 http://localhost:3000/api/register 发送带有 invite_code 的请求来模拟用户注册,然后创建订单并调用 /api/payment/callback 来验证佣金是否按照预期比例自动分配到上级用户的余额中。所有逻辑均基于事务与锁,确保了金融数据的准确性与高并发下的安全性。

相关推荐

最新

热门

推荐

精选

标签

易频IT社区是综合性互联网IT技术门户网站,专注分享网络技术、服务器运维、网络安全、编程开发、系统架构、云计算、大数据等行业干货,实时更新IT行业资讯、零基础教程、实战案例,为IT从业者、技术爱好者提供专业的学习交流平台。

Copyright © 2021-2026 易频IT社区. All Rights Reserved. 备案号:闽ICP备2023013482号 网站地图