摘要
秒杀活动最怕什么?不是页面不好看,而是时间段配置一乱,前台“疯抢中”“即将开始”“已结束”全部跟着错,商品列表查不到,下单校验也会误判。CRMEB Pro 的秒杀不是单纯按商品开始时间判断,它还叠加了“秒杀时间段”“活动日期”“商品状态”“活动状态”几个维度。
这篇先拆秒杀小功能的第一部分:后台如何配置秒杀时间段,为什么时间段不能重叠,前台又是怎么根据时间段显示活动状态。二开时如果要加“每天多场次”“预约提醒”“跨天秒杀”,这块一定要先看懂。
1. 先把秒杀时间段链路找出来
后台秒杀时间段入口在营销模块:
app/controller/admin/v1/marketing/seckill/StoreSeckillTime.php app/services/activity/seckill/StoreSeckillTimeServices.php app/dao/activity/seckill/StoreSeckillTimeDao.php app/model/activity/seckill/StoreSeckillTime.php app/validate/admin/marketing/StoreSeckillTimeValidate.php前台读取时间段的入口在:
app/controller/api/v1/activity/StoreSeckill.php app/services/activity/seckill/StoreSeckillTimeServices.php核心数据表模型是:
classStoreSeckillTimeextendsBaseModel{useModelTrait;protected$pk='id';protected$name='store_seckill_time';}这里的start_time、end_time不是完整日期时间,而是类似0900、1200这样的日内时间段。它决定“今天几点到几点是秒杀场次”。
2. 后台保存时先把 HH:mm 转成 4 位数字
后台 Controller 保存时间段时,会接收表单里的time数组:
publicfunctionsave($id){$data=$this->request->postMore([[['title','s'],''],[['describe','s'],''],['time',[]],['pic',''],[['status','d'],0],]);$this->validate($data,\app\validate\admin\marketing\StoreSeckillTimeValidate::class,'save');$data['start_time']=str_replace(':','',$data['time'][0]);$data['end_time']=str_replace(':','',$data['time'][1]);unset($data['time']);}举个例子:
09:00 -> 0900 12:00 -> 1200这样做的好处是后面比较当前时间时可以直接用date('Hi')比较,逻辑很轻:
$currentHour=date('Hi');if($currentHour>=$start&&$currentHour<$end){// 当前时间命中这个秒杀场次}二开注意:如果你要支持跨天场次,比如23:00 - 01:00,这个存储方式就不够用了。因为现有逻辑要求结束时间大于开始时间。
3. 结束时间必须大于开始时间
当前 Controller 里有第一层硬校验:
if($data['end_time']<=$data['start_time']){returnapp('json')->fail('时间段结束时间要大于开始时间');}也就是说,下面这些配置不允许:
12:00 - 12:00 18:00 - 09:00 23:00 - 01:00如果业务只是“每天固定几场秒杀”,这个规则很合理。它能避免一个场次同时覆盖今天和明天,导致前台状态计算变复杂。
如果二开需求是“跨天夜场秒杀”,不要直接删掉这段判断。更稳的方式是新增一个场次类型或日期维度,例如:
start_day_offset 0 今天,1 明天 end_day_offset 0 今天,1 明天然后单独改前台状态判断、商品列表查询和下单校验。否则页面可能显示“疯抢中”,下单时却提示“活动已结束”。
4. 时间段为什么不能重叠
保存前还会调用:
if(!$this->services->checkTime($data,$id)){returnapp('json')->fail('时间段不可重叠');}Service 层只是编排:
publicfunctioncheckTime(array$where,int$id){if(!$this->dao->valStartTime($where['start_time'],$id)&&!$this->dao->valEndTime($where['end_time'],$id)&&!$this->dao->valAllTime($where,$id)){returntrue;}returnfalse;}真正判断落在 Dao:
publicfunctionvalStartTime($time,$id){return$this->getModel()->when($id,function($query)use($id){$query->where('id','<>',$id);})->where('start_time','<=',$time)->where('end_time','>',$time)->count();}publicfunctionvalEndTime($time,$id){return$this->getModel()->when($id,function($query)use($id){$query->where('id','<>',$id);})->where('start_time','<',$time)->where('end_time','>=',$time)->count();}publicfunctionvalAllTime(array$data,$id){return$this->getModel()->when($id,function($query)use($id){$query->where('id','<>',$id);})->where('start_time','>',$data['start_time'])->where('end_time','<=',$data['end_time'])->count();}这三段分别拦截三种情况:
1. 新开始时间落在旧时间段中 2. 新结束时间落在旧时间段中 3. 新时间段整个包住旧时间段例如已有:
10:00 - 12:00下面都应该被拦:
09:00 - 11:00 结束时间落进旧场次 11:00 - 13:00 开始时间落进旧场次 09:00 - 13:00 整个包住旧场次为什么要这么严?因为前台只会选一个当前场次。如果两个场次重叠,商品列表、倒计时、活动状态都可能取错场。
5. 前台怎么判断“疯抢中、即将开始、已结束”
用户端秒杀时间区间接口在:
app/controller/api/v1/activity/StoreSeckill.php index()核心代码如下:
publicfunctionindex(StoreSeckillTimeServices$seckillTimeServices){$seckillTime=$seckillTimeServices->time_list();$seckillTimeIndex=$seckillTimeNext=-1;$timeCount=count($seckillTime);$unTimeCunt=0;if($timeCount){$today=date('Y-m-d');$currentHour=date('Hi');foreach($seckillTimeas$key=>&$value){$start=str_replace(':','',$value['start_time']);$end=str_replace(':','',$value['end_time']);if($currentHour>=$start&&$currentHour<=$end){$value['state']='疯抢中';$value['status']=1;if($seckillTimeIndex==-1){$seckillTimeIndex=$key;}}elseif($currentHour<$start){$value['state']='即将开始';$value['status']=2;$unTimeCunt+=1;if($seckillTimeNext==-1){$seckillTimeNext=$key;}}elseif($currentHour>=$end){$value['state']='已结束';$value['status']=0;}$value['time']=$value['start_time'];$value['stop']=strtotime($today.' '.$value['end_time']);}}}这里有两个容易踩坑的点:
seckillTimeIndex 当前选中展示的场次下标 seckillTimeNext 下一场即将开始的场次下标移动端进入秒杀页,通常会用这两个字段决定默认 tab 和倒计时。时间段一旦重叠,默认 tab 可能不是运营想展示的那场。
6. time_list 只返回启用场次
Service 里time_list()只查状态为启用的时间段:
publicfunctiontime_list(){$list=$this->dao->getList(['status'=>1],'id,title,start_time,end_time,pic');foreach($listas&$item){$item['start_time']=substr_replace($item['start_time'],':',2,0);$item['end_time']=substr_replace($item['end_time'],':',2,0);$item['slide']=$item['pic'];}return$list;}Dao 默认排序:
publicfunctiongetList(array$where,string$field='*',int$page=0,int$limit=0,string$order='start_time asc,id desc'){return$this->search($where)->field($field)->order($order)->select()->toArray();}这说明前台展示顺序天然按开始时间升序。二开时如果增加“权重排序”,要非常小心,因为秒杀场次不是普通 banner,排序会影响默认当前场次判断。
7. 小功能怎么扩展:加一个“秒杀场次提醒”
如果要在现有基础上做一个小功能,比如“未开始场次支持预约提醒”,建议不要改动原有时间段判断,而是新增业务字段:
is_remind 是否允许预约提醒 remind_before 提前多少分钟提醒后端字段建议放在store_seckill_time:
ALTERTABLE`eb_store_seckill_time`ADDCOLUMN`is_remind`tinyint(1)NOTNULLDEFAULT0COMMENT'是否开启秒杀场次提醒',ADDCOLUMN`remind_before`int(10)NOTNULLDEFAULT10COMMENT'秒杀开始前提醒分钟数';Service 输出时补字段:
publicfunctiontime_list(){$list=$this->dao->getList(['status'=>1],'id,title,start_time,end_time,pic,is_remind,remind_before');foreach($listas&$item){$item['start_time']=substr_replace($item['start_time'],':',2,0);$item['end_time']=substr_replace($item['end_time'],':',2,0);$item['slide']=$item['pic'];$item['can_remind']=$item['is_remind']&&$item['start_time']>date('H:i');}return$list;}前台看到status = 2且can_remind = true时,再展示“提醒我”。这样不会影响原来的疯抢状态。
8. 关键代码/目录说明
app/controller/admin/v1/marketing/seckill/StoreSeckillTime.php 后台时间段列表、表单、保存、状态修改、删除。 app/services/activity/seckill/StoreSeckillTimeServices.php 时间段列表格式化、当前时间段计算、时间冲突校验入口。 app/dao/activity/seckill/StoreSeckillTimeDao.php 时间段查询、开始时间冲突、结束时间冲突、包裹冲突判断。 app/model/activity/seckill/StoreSeckillTime.php store_seckill_time 表模型和搜索器。 app/controller/api/v1/activity/StoreSeckill.php 前台秒杀首页时间段状态输出。9. 二开注意事项
- 不要让秒杀时间段重叠,否则前台默认场次和商品列表会失真。
- 不要直接支持跨天场次,除非同步改商品查询、详情状态、下单校验和倒计时。
- 不要只改前端时间选择器,后端
checkTime()和 Dao 冲突判断必须一起看。 - 不要用排序权重打乱时间段顺序,除非前台当前场次判断也一起改。
- 新增提醒、预约、订阅消息时,建议挂在“即将开始”状态,不要改“疯抢中”的判断。
标签建议
CRMEB Pro CRMEB 二开 秒杀活动 ThinkPHP 商城系统 源码解析 营销活动