我们有个需求就是3人成一个团,发起者开团,也就是剩余2个人可以参与拼团,但实际上会碰到这种情况,这个团如果进入的人过多都购买引起这个团实际超出3人的情况。因此每个用户在进入的时候有个锁定名额的逻辑,一个人进去就会锁定五分钟,我之前做了个版本,但是代码比较复杂,

一、基础实现

下面是V1版本代码:

/**

* 锁定某个拼团,去尝试锁定1,2个名额,都被锁定则则返回失败

* @param $open_id

* @param $active_id

* @param $team_id

* @return int

*/

public function lockTeam1($active_id,$team_id,$open_id,$team_user_count=1){

$seetPosition=[];

//计算还有几个名额剩下

for($i=1;$i<=3;$i++){

if($i<=$team_user_count){

continue;

}

array_push($seetPosition,'seet_'.$i);

}

//返回第一个未锁定的名额

$keyTemplateSeat = SPELL_GROUP_LOCK_TEAM;

$input = ['teamId'=>$team_id];//第一个拼团名额

$teamKey = $this->swtichRedisKey($input, $keyTemplateSeat);

$unlockSeat='';

$lockTimeArr=[];

$firstLockTime=0;

foreach($seetPosition as $val){

//echo $teamKey.$val."\n";

$lockInfo=$this->getRedis()->WYget($teamKey.$val);

if($lockInfo){

array_push($lockTimeArr,$lockInfo);//将所有当时锁定的日期时间戳放入锁定数组

continue;

}

else{

$unlockSeat=$val;

break;

}

}

if(!empty($lockTimeArr)){

sort($lockTimeArr);//按从小到大排序

$firstLockTime=$lockTimeArr[0];//更新为最先锁定时间

}

//读取用户锁定的team

$keyTemplate = SPELL_GROUP_LOCK_TEAM_USER;

$input = ['openId'=>$open_id];

$userLockKey = $this->swtichRedisKey($input, $keyTemplate);

$userLockedTeam=$this->getRedis()->WYhGet($userLockKey,'team_id');//读取用户锁定的Team

//如果用户锁定过这个team则直接返回

if($userLockedTeam==$team_id){

$ret=array('code'=>1,'msg'=>'用户锁定的跟之前锁定的位置是一个团');

return $ret;

}

if($unlockSeat==''){

//团已经满3人了或锁定人数满了

$ret=array('code'=>0,'msg'=>'这个团已经满员了');

if($firstLockTime){

$sUnLockTime=$firstLockTime+self::LOCK_TEAM_EXPIRE;

$ret['data']=array(

'sTeamId'=>$team_id,

'sActiveId'=>$active_id,

'sLockTime'=>$firstLockTime,

'sUnLockTime'=>(string)$sUnLockTime,

);

}

return $ret;//没有空位

}

//开始锁定

$keyTemplate = SPELL_GROUP_LOCK_INCR;

$input = ['teamId'=>$team_id];//

$lockredisKey = $this->swtichRedisKey($input, $keyTemplate);

//这个团的这个位置只能锁定一次,否则就是锁定人数过多

if($this->getRedis()->WYincr($lockredisKey.$unlockSeat)==1){

//锁定过其他团先解锁其他团

if($userLockedTeam){

$userLockPostion=$this->getRedis()->WYhGet($userLockKey,'team_pos');//读取用户锁定的position

$input = ['teamId'=>$userLockedTeam];

$unlockteamKey = $this->swtichRedisKey($input, $keyTemplateSeat);//需要解锁的团的key名

$this->getRedis()->WYdelete($unlockteamKey.$userLockPostion);//解锁用户锁定的团锁定的位置

}

$this->getRedis()->WYset($teamKey.$unlockSeat,time(),self::LOCK_TEAM_EXPIRE);//标识这个团的这个位置被锁定了

$this->getRedis()->WYhMset($userLockKey,array('team_id'=>$team_id,'team_pos'=>$unlockSeat),self::LOCK_TEAM_EXPIRE);//设置用户锁定的团为当前团及锁定位置

$this->getRedis()->WYdelete($lockredisKey.$unlockSeat);//解除INCR

$ret=array('code'=>1,'msg'=>'用户['.$open_id.']已经成功锁定');

return $ret;

}

//同时争抢这个位置的人过多

$ret=array('code'=>2,'msg'=>'锁定的人数已满');

return $ret;

}

一共用了如下的Redis,第一个是避免高并发的string,第二个SPELL_GROUP_LOCK_TEAM是个string,这个是保存了团的某个位置的锁定时间(teamid_seat_1,teamid_seat_2),第三个就是用户的锁定信息,用于解锁团,这个版本操作的Redis比较多,可靠性还可以,就是逻辑比较复杂,一般人看不懂。

'SPELL_GROUP_LOCK_INCR', 'lock_team_incr_{#teamId}');//避免对团的锁定多用户同时

'SPELL_GROUP_LOCK_TEAM', 'user_lock_team_{#teamId}');//团的锁定位置

'SPELL_GROUP_LOCK_TEAM_USER', 'lock_team_user_new_{#openId}');//用户锁定的

二、list版本

下面这个版本算是重构版,代码简洁点,用list结构保存了用户的每次锁团信息,一次性全部读取出来然后根据时间判断,将所有过期的信息移除队列,这个版本已经很优化了,减少了不少KEY,这个版本没有去考虑用户去锁定其他团的时候解锁当前团的问题,需要优化下:

/**

* V2版本锁团,还未验证

* @param $active_id

* @param $team_id

* @param $open_id

* @param int $team_user_count

*/

public function lockTeam($active_id,$team_id,$open_id,$team_user_count=1){

//开始锁定

$keyTemplate = SPELL_GROUP_LOCK_INCR_V2;

$input = ['teamId'=>$team_id];

$lockTeamKey = $this->swtichRedisKey($input, $keyTemplate);

//同一时刻这个团只允许一个人操作,避免人数过多引起错误

if($this->getRedis()->WYincr($lockTeamKey)==1) {

$keyTemplate = SPELL_GROUP_LOCK_TEAM_LIST;

$input = ['teamId'=>$team_id];

$UserLockTeamKey = $this->swtichRedisKey($input, $keyTemplate);

$length = $this->getRedis()->WYlLen($UserLockTeamKey);//读取队列的长度

$time = time();

$flag = false;

if ($length) {

$lockData = $this->getRedis()->WYlRange($UserLockTeamKey);//因为key本身不大,lrange没有多大开销

krsort($lockData);//将取出的数据倒排,便于将过期的key移除

foreach ($lockData as $val) {

$lData = json_decode($val, true);

//当前用户再次锁定并且没有过期则直接返回,如果有未过期的锁定则直接返回

if (($lData['open_id'] == $open_id) && ($time <= $lData['lock_time'] + self::LOCK_TEAM_EXPIRE)) {

$flag = true;

}

//过期的数据清理掉

if ($time > $lData['lock_time'] + self::LOCK_TEAM_EXPIRE) {

$this->getRedis()->WYrPop($UserLockTeamKey);

}

}

$length = $this->getRedis()->WYlLen($UserLockTeamKey);//获取新的队列长度

}

//当前用户存在未过期的锁定,直接可以返回

if ($flag) {

$ret=array('code' => 1, 'msg' => '用户[' . $open_id . ']存在未过期的锁定');

}

else{

$maxListLength = 3 - $team_user_count;//队列允许的最大长度为总数减去剩余未支付人数

$data = json_encode(array('open_id' => $open_id,'lock_time' => $time));

if ($maxListLength > $length) {

$this->getRedis()->WYlPush($UserLockTeamKey, $data);//未满就直接插入

$ret=array('code' => 1, 'msg' => '用户[' . $open_id . ']成功锁定');

} else {

$ret=array('code' => 0, 'msg' => '锁定人数过多');

}

}

$this->getRedis()->WYdelete($lockTeamKey);//解除INCR

return $ret;

}

return array('code' => 0, 'msg' => '同时操作的人太多了');

}

使用了如下Redis,如果加上用户,也是3个Redis

'SPELL_GROUP_LOCK_TEAM_LIST', 'user_lock_team_list_{#teamId}');//团锁定的队列

'SPELL_GROUP_LOCK_INCR_V2', 'lock_team_incr_v2_{#teamId}');//同一时刻一个团只允许一个人操作

三、zset版本

下面这个版本是list版本的优化版,用zset储存了用户的参与时间,利用zset天然的排序功能,不用再次排序,并且删除用户锁定的团也是很容易的事情:

/**

* V2版本锁团,还未验证

* @param $active_id

* @param $team_id

* @param $open_id

* @param int $team_user_count

*/

public function lockTeam($active_id,$team_id,$open_id,$team_user_count=1){

//开始锁定

$keyTemplate = SPELL_GROUP_LOCK_INCR_V2;

$input = ['teamId'=>$team_id];

$lockTeamKey = $this->swtichRedisKey($input, $keyTemplate);

$firstLockTime='';//第一个锁定人的锁定时间

//同一时刻这个团只允许一个人操作,避免人数过多引起错误

if($this->getRedis()->WYincr($lockTeamKey)==1) {

//读取用户锁定的团,如果存在则删除

$keyTemplate = SPELL_GROUP_LOCK_TEAM_USER_V2;

$input = ['openId'=>$open_id];

$userLockTeamKey = $this->swtichRedisKey($input, $keyTemplate);

$userLockTeam = $this->getRedis()->WYget($userLockTeamKey);//读取用户锁定的团

$keyTemplate = SPELL_GROUP_LOCK_TEAM_ZSETS;

$input = ['teamId'=>$team_id];

$LockTeamSetsKey = $this->swtichRedisKey($input, $keyTemplate);

//当用户已经锁定过并且锁定的不是当前的团的时候,将之前锁定的删除掉

if($userLockTeam && $userLockTeam!=$team_id){

$this->getRedis()->WYzRem($LockTeamSetsKey,$open_id);//将用户锁定的其他团解锁

}

$length = $this->getRedis()->WYzCard($LockTeamSetsKey);//读取队列的长度

$time = time();

$flag = false;

if ($length) {

$lockData = $this->getRedis()->WYzRange($LockTeamSetsKey,0,-1,1);//查询score

foreach ($lockData as $key=>$val) {

//读取并设定第一个锁定人的锁定时间

if($firstLockTime==''){

$firstLockTime=$val;

}

//当前用户再次锁定并且没有过期则直接返回,如果有未过期的锁定则直接返回

if (($key == $open_id) && ($time <= $val + self::LOCK_TEAM_EXPIRE)) {

$flag = true;

}

//过期的数据清理掉

if ($time > $val + self::LOCK_TEAM_EXPIRE) {

$this->getRedis()->WYzRem($LockTeamSetsKey,$key);

}

}

$length = $this->getRedis()->WYzCard($LockTeamSetsKey);//获取新的队列长度

}

//当前用户存在未过期的锁定,直接可以返回

if ($flag) {

$ret=array('code' => 1, 'msg' => '用户[' . $open_id . ']存在未过期的锁定');

}

else{

$maxListLength = 3 - $team_user_count;//队列允许的最大长度为总数减去剩余未支付人数

if ($maxListLength > $length) {

$this->getRedis()->WYzAdd($LockTeamSetsKey, $time,$open_id);//未满就直接插入

$this->getRedis()->WYexpire($LockTeamSetsKey,self::TEAM_EXPIRE);//设置过期时间,有人操作会自动延时,否则会过期

$this->getRedis()->WYset($userLockTeamKey,$team_id,self::LOCK_TEAM_EXPIRE);//设置用户当前锁定的团,有效期跟锁定团的有效期相同

$ret=array('code' => 1, 'msg' => '用户[' . $open_id . ']成功锁定');

} else {

//print_r($this->getRedis()->WYzRange($LockTeamSetsKey,0,-1,1));

$ret=array('code' => 0, 'msg' => '锁定人数过多');

if($firstLockTime){

$sUnLockTime=$firstLockTime+self::LOCK_TEAM_EXPIRE;

$ret['data']=array(

'sTeamId'=>$team_id,

'sActiveId'=>$active_id,

'sLockTime'=>(string)$firstLockTime,

'sUnLockTime'=>(string)$sUnLockTime,

);

}

}

}

$this->getRedis()->WYdelete($lockTeamKey);//解除INCR

return $ret;

}

return array('code' => 0, 'msg' => '同时操作的人太多了');

}

使用了如下redis的:

'SPELL_GROUP_LOCK_TEAM_ZSETS', 'user_lock_team_zset_{#teamId}');//团锁定的有序集合

'SPELL_GROUP_LOCK_TEAM_USER_V2', 'lock_team_user_v2_{#openId}');//用户当前锁定的团

'SPELL_GROUP_LOCK_INCR_V2', 'lock_team_incr_v2_{#teamId}');//同一时刻一个团只允许一个人操作

php拼团逻辑,拼团3人团避免人数过多的一个算法相关推荐

  1. 揭露丑恶:邹涛现在就搞深圳万人团购房?

    据媒体报道:深圳那个原来在2006倡议大家联合起来不要买房的邹涛最近又在搞什么万人团购房. 搞万人团购还是有意义的,当然不是现在!现在房价高企,所谓的万人团购和砍价都是噱头. 当深圳房价回到6000, ...

  2. 成都拓嘉启远:拼多多万人团要不要去参加

    拼多多万人团顾名思义就有很多人参与进来拼团,拼多多万人团不仅仅上面的拼团人数多,而且商品类型也是各种各样的,比较丰富,买家在万人团进行拼团购买是可以省钱的,那么对于拼多多商家来说,值得去参加拼多多万人 ...

  3. 海康威视 0day_某拼又有车了,海康威视固态万人团,不管好不好听我别急着上车...

    Hello大家好,我是兼容机之家的咖啡. 海康威视是全球领先的以视频为核心的物联网解决方案提供商,致力于不断提升视频处理技术和视频分析技术,面向全球提供领先的监控产品和技术解决方案. 但是这么一个做监 ...

  4. 下单接口剥离秒杀和拼团逻辑

    概述 之前在一次判断失误的反思一文提到,从下单接口中,一次性剥离所有营销逻辑,难度和风险都是非常大的.更好的方式是先剥离其中一到两个,慢慢击破.但是这种方式实施起来,难度还仍然还是很大的,下面将剥离的 ...

  5. 鑫缘聚禾:拼多多拼团怎么拼

    拼多多是一家专心于C2B拼团的第三方社交电商平台,将交流共享与社交的理念融于电商的参团拼团过程中,构成属于拼多多的新社交电商思想.要知道最近对拼多多感兴趣的人越来越多,首要是因为顾客能够通过拼单的方法 ...

  6. 拼多多待拼团,大家一起拼数据接口

    拼多多待拼团,大家一起拼数据接口 http://www.youkezhushou .com/apiMarket/index Q137371158 {"code": 1," ...

  7. 课程拼团超级拼团公众号小程序制作

    随着教育培训机构越来越多,竞争也越来越激烈,传统简单的售卖课程地推招生已经无法满足市场竞争的需求,越来越多的机构尝试通过线上教育来构建多样化的场景来锁住用户. 微信小程序公众号H5网页链接课程拼团活动 ...

  8. 链团爱拼董事长欧阳胜:敢想敢拼 再创辉煌

    服务定天下,套路得人心,而真诚才是唯一的销售技巧.这是链团爱拼公司董事长欧阳胜一直信奉的工作原则.自爱拼APP上线以来,新成立的整个团队以最快的速度,加入到爱拼的学习推广之路,用爱拼的方式使团队快速定 ...

  9. PHP商城源码|好看的团购拼购商城源码

    简介: PHP商城源码|团购拼购商城|亲测完美运营. 测试环境:php7.0+mysql5.6+Redis 1.修改数据库配置文件 \application\database.php 2.导入数据库 ...

最新文章

  1. java+++多数据源配置,Spring Cloud + Mybatis 多数据源配置
  2. Windows Hyper-V远程信息泄露漏洞CVE-2017-8712 影响Win2016和win10
  3. Packet Capture -- android 手机抓包利器
  4. Centos 6.5安装MySQL-python
  5. HBase架构设计及原理分析
  6. 伯克利人工智能导论课开放:视频、PPT和练习都在这 | 资源
  7. 【转】Printf()输出格式控制
  8. java版电子商务spring cloud分布式微服务b2b2c社交电商(十一)springboot集成swagger2,构建优雅的Restful API...
  9. soapui使用教程2-属性与脚本
  10. 【Modelsim】下载安装教程
  11. js 实现文件上传 php,JS+php后台实现文件上传功能详解
  12. 我国家庭计算机最快方式,选择哪种方式上网对家庭电脑更实惠,更快速?
  13. EXCEL柱状图制作(三)
  14. Python 散点图的数据分析
  15. 没有oracle 连接kettle,Kettle 连接 oracle
  16. 360全景倒车影像怎么看_360度全景倒车影像应该装么?告诉大家千万要冷静,一招对付所有...
  17. 软考系统集成项目管理工程师模拟题
  18. untiy游戏接入之uc_sdk(九游)
  19. 知道焊缝长度如何确定节点板尺寸_钢结构焊缝中焊脚尺寸怎么确定?有没有标准规定或计算公式?...
  20. 佛祖保佑永无BUG python版本

热门文章

  1. 计算机主板供电故障,电脑主板内部电池供电出现问题,会出现哪些故障问题
  2. php数组竖转横,数组纵向转横向怎么实现
  3. 闪存联盟启动“百强架构师”行动 迎接认知时代架构挑战
  4. log日志中不打印异常栈的具体信息
  5. Redis应用之限制访问频率
  6. 跟了老板10年被劝退,“忠诚”就换来这个?
  7. java斗地主代码花色,集合经典案例:斗地主发牌功能实现
  8. 零基础如何学Java?一系列教程带你从小白到大神进阶
  9. SAS对决Ultra320(下):MAX3147RC小胜Ultrastar 15K147
  10. 搜索引擎的网址收录链接