php拼团逻辑,拼团3人团避免人数过多的一个算法
我们有个需求就是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人团避免人数过多的一个算法相关推荐
- 揭露丑恶:邹涛现在就搞深圳万人团购房?
据媒体报道:深圳那个原来在2006倡议大家联合起来不要买房的邹涛最近又在搞什么万人团购房. 搞万人团购还是有意义的,当然不是现在!现在房价高企,所谓的万人团购和砍价都是噱头. 当深圳房价回到6000, ...
- 成都拓嘉启远:拼多多万人团要不要去参加
拼多多万人团顾名思义就有很多人参与进来拼团,拼多多万人团不仅仅上面的拼团人数多,而且商品类型也是各种各样的,比较丰富,买家在万人团进行拼团购买是可以省钱的,那么对于拼多多商家来说,值得去参加拼多多万人 ...
- 海康威视 0day_某拼又有车了,海康威视固态万人团,不管好不好听我别急着上车...
Hello大家好,我是兼容机之家的咖啡. 海康威视是全球领先的以视频为核心的物联网解决方案提供商,致力于不断提升视频处理技术和视频分析技术,面向全球提供领先的监控产品和技术解决方案. 但是这么一个做监 ...
- 下单接口剥离秒杀和拼团逻辑
概述 之前在一次判断失误的反思一文提到,从下单接口中,一次性剥离所有营销逻辑,难度和风险都是非常大的.更好的方式是先剥离其中一到两个,慢慢击破.但是这种方式实施起来,难度还仍然还是很大的,下面将剥离的 ...
- 鑫缘聚禾:拼多多拼团怎么拼
拼多多是一家专心于C2B拼团的第三方社交电商平台,将交流共享与社交的理念融于电商的参团拼团过程中,构成属于拼多多的新社交电商思想.要知道最近对拼多多感兴趣的人越来越多,首要是因为顾客能够通过拼单的方法 ...
- 拼多多待拼团,大家一起拼数据接口
拼多多待拼团,大家一起拼数据接口 http://www.youkezhushou .com/apiMarket/index Q137371158 {"code": 1," ...
- 课程拼团超级拼团公众号小程序制作
随着教育培训机构越来越多,竞争也越来越激烈,传统简单的售卖课程地推招生已经无法满足市场竞争的需求,越来越多的机构尝试通过线上教育来构建多样化的场景来锁住用户. 微信小程序公众号H5网页链接课程拼团活动 ...
- 链团爱拼董事长欧阳胜:敢想敢拼 再创辉煌
服务定天下,套路得人心,而真诚才是唯一的销售技巧.这是链团爱拼公司董事长欧阳胜一直信奉的工作原则.自爱拼APP上线以来,新成立的整个团队以最快的速度,加入到爱拼的学习推广之路,用爱拼的方式使团队快速定 ...
- PHP商城源码|好看的团购拼购商城源码
简介: PHP商城源码|团购拼购商城|亲测完美运营. 测试环境:php7.0+mysql5.6+Redis 1.修改数据库配置文件 \application\database.php 2.导入数据库 ...
最新文章
- java+++多数据源配置,Spring Cloud + Mybatis 多数据源配置
- Windows Hyper-V远程信息泄露漏洞CVE-2017-8712 影响Win2016和win10
- Packet Capture -- android 手机抓包利器
- Centos 6.5安装MySQL-python
- HBase架构设计及原理分析
- 伯克利人工智能导论课开放:视频、PPT和练习都在这 | 资源
- 【转】Printf()输出格式控制
- java版电子商务spring cloud分布式微服务b2b2c社交电商(十一)springboot集成swagger2,构建优雅的Restful API...
- soapui使用教程2-属性与脚本
- 【Modelsim】下载安装教程
- js 实现文件上传 php,JS+php后台实现文件上传功能详解
- 我国家庭计算机最快方式,选择哪种方式上网对家庭电脑更实惠,更快速?
- EXCEL柱状图制作(三)
- Python 散点图的数据分析
- 没有oracle 连接kettle,Kettle 连接 oracle
- 360全景倒车影像怎么看_360度全景倒车影像应该装么?告诉大家千万要冷静,一招对付所有...
- 软考系统集成项目管理工程师模拟题
- untiy游戏接入之uc_sdk(九游)
- 知道焊缝长度如何确定节点板尺寸_钢结构焊缝中焊脚尺寸怎么确定?有没有标准规定或计算公式?...
- 佛祖保佑永无BUG python版本
热门文章
- 计算机主板供电故障,电脑主板内部电池供电出现问题,会出现哪些故障问题
- php数组竖转横,数组纵向转横向怎么实现
- 闪存联盟启动“百强架构师”行动 迎接认知时代架构挑战
- log日志中不打印异常栈的具体信息
- Redis应用之限制访问频率
- 跟了老板10年被劝退,“忠诚”就换来这个?
- java斗地主代码花色,集合经典案例:斗地主发牌功能实现
- 零基础如何学Java?一系列教程带你从小白到大神进阶
- SAS对决Ultra320(下):MAX3147RC小胜Ultrastar 15K147
- 搜索引擎的网址收录链接