分销系统n级分销的一种设计思路

905 阅读4分钟

在开发中经常会遇到开发分销系统的需求,如何设计用户表,如何存储上下级关系,如何检索指定用户推广的下级,这些都是分销系统很核心的功能,今天就分享一种比较简单的用户表设计思路,也是我经常使用的方案。

1.用户表主要字段

1.jpg

CREATE TABLE `user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `code` char(6) NOT NULL COMMENT '推广码',
  `name` varchar(20) NOT NULL COMMENT '用户名',
  `parent_id` int(10) unsigned DEFAULT NULL COMMENT '父级ID',
  `parent_ids` varchar(255) DEFAULT NULL COMMENT '所有父级',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `user` VALUES (1, 'KJCKWX', '1001', 0, '0');
INSERT INTO `user` VALUES (2, 'PO21XD', '1002', 0, '0');
INSERT INTO `user` VALUES (3, 'QNJO6U', '1003', 0, '0');
INSERT INTO `user` VALUES (4, 'BM1YPD', '1004', 1, '1,0');
INSERT INTO `user` VALUES (5, 'BQSCMU', '1005', 1, '1,0');
INSERT INTO `user` VALUES (6, 'GWYKLC', '1006', 4, '4,1,0');
INSERT INTO `user` VALUES (7, 'CHVZXU', '1007', 6, '6,4,1,0');
INSERT INTO `user` VALUES (8, 'LULW0T', '1008', 7, '7,6,4,1,0');
INSERT INTO `user` VALUES (9, 'WEDCGK', '1009', 4, '4,1,0');
INSERT INTO `user` VALUES (10, 'HIFUCY', '1010', 6, '6,4,1,0');
INSERT INTO `user` VALUES (11, 'I2VKEJ', '1011', 6, '6,4,1,0');
INSERT INTO `user` VALUES (12, 'AVCVUE', '1012', 7, '7,6,4,1,0');
INSERT INTO `user` VALUES (13, '4GOU6S', '1013', 7, '7,6,4,1,0');
INSERT INTO `user` VALUES (14, 'R7QLWY', '1014', 8, '8,7,6,4,1,0');
INSERT INTO `user` VALUES (15, 'HAT2U5', '1015', 14, '14,8,7,6,4,1,0');
INSERT INTO `user` VALUES (16, 'WPGDZH', '1016', 14, '14,8,7,6,4,1,0');
INSERT INTO `user` VALUES (17, 'KIZESN', '1017', 12, '12,7,6,4,1,0');
INSERT INTO `user` VALUES (18, 'LH1CKJ', '1018', 13, '13,7,6,4,1,0');
INSERT INTO `user` VALUES (19, '1PPWTZ', '1019', 18, '18,13,7,6,4,1,0');
INSERT INTO `user` VALUES (20, 'LECH08', '1020', 5, '5,1,0');

根据 parent_id 可简单画出以下的用户关系链

2.jpg

2.推广码字段code

code 为用户推广码,每个用户唯一,系统正是根据 code 为每个用户生成推广链接或推广二维码,分享到社交媒体后,新用户注册时会带上code,在注册逻辑中即可锁定用户的上级。
推广连接格式一般如下:
http://xxx/register?code=xxx

3.用parent_id、parent_ids锁定父级

parent_id 为直接父级ID, 0表示无父级;
parent_ids 为所有父级ID用逗号分隔符连接, '0'表示无父级, 在后端代码中可以按照以下逻辑赋值:
parent_ids = parent_id + 逗号 + 直接父级的parent_ids
这样设计的好处是可以检索所有的父级和推广者(子孙用户), 不限于3级。
为什么用逗号分割,是根据 MySQL 函数 find_in_set( str, strlist ) 的参数 strlist 也是以逗号分割。

计算佣金时需要检索父级用户
此时根据 parent_id 可以查询直接父级,根据 parent_ids 可以查询所有父级。
下面的SQL语句为了方便调试使用了子查询,具体的后端代码肯定要进行要优化处理。

-- 查询1007的直接父级, 用户1007的ID为7
select * from user where id = (select parent_id from user where id=7);

-- 查询1007的所有父级
select * from user where find_in_set(id, (select parent_ids from user where id=7))

用户查看自己的推广成果
考虑到平台的公平,用户肯定需要查看自己的推广用户,而且根据分销机制的不同,用户也需要查看到不同级别的下级用户,具体的SQL语句如下:

-- 1007查看自己的一级推广用户(1)
select * from user where parent_id=7;

-- 1007查看自己的一级推广用户(2)
select * from user where find_in_set(7, parent_ids)=1;

-- 1007查看自己的二级推广用户
select * from user where find_in_set(7, parent_ids)=2;

-- 1007查看自己的三级推广用户
select * from user where find_in_set(7, parent_ids)=3;

-- 1007查看自己的四级推广用户
select * from user where find_in_set(7, parent_ids)=4;

4.ThinkPHP5 部分接口代码演示

   //用户注册接口
    public function register($name, $code){
        //查询父级
        $parent = db('user')->where('code', $code)->find();
        
        //生成自己的推广码
        $code = Random::alnum(6);
        $code = strtoupper($code);

        //注册用户
        $user = [
            'name' => $name,
            'code' => $code,
            'parent_id' => $parent['id'],
            'parent_ids' => $parent['id'].','.$parent['parent_ids'],
        ];
        db('user')->insert($user);

        $this->success('注册成功');
    }

    //查询所有父级
    public function get_parents($user_id){
        $user = db('user')->where('id', $user_id)->find();
        $user_list = db('user')->where('id', 'in', $user['parent_ids'])->select();
        $this->success('success', ['list' => $user_list]);
    }

    //查询指定等级的推广者
    public function get_subs($user_id, $level){
        $where = sprintf('find_in_set(%s, parent_ids)=%s', $user_id, $level);
        $user_list = db('user')->where($where)->select();
        $this->success('success', ['list' => $user_list]);
    }