CRMEB Pro 秒杀活动二开:时间段配错一次,整场活动为什么都会乱?
摘要秒杀活动最怕什么不是页面不好看而是时间段配置一乱前台“疯抢中”“即将开始”“已结束”全部跟着错商品列表查不到下单校验也会误判。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$pkid;protected$namestore_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)比较逻辑很轻$currentHourdate(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;}真正判断落在 DaopublicfunctionvalStartTime($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;$timeCountcount($seckillTime);$unTimeCunt0;if($timeCount){$todaydate(Y-m-d);$currentHourdate(Hi);foreach($seckillTimeas$key$value){$startstr_replace(:,,$value[start_time]);$endstr_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;$unTimeCunt1;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([status1],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$page0,int$limit0,string$orderstart_time asc,id desc){return$this-search($where)-field($field)-order($order)-select()-toArray();}这说明前台展示顺序天然按开始时间升序。二开时如果增加“权重排序”要非常小心因为秒杀场次不是普通 banner排序会影响默认当前场次判断。7. 小功能怎么扩展加一个“秒杀场次提醒”如果要在现有基础上做一个小功能比如“未开始场次支持预约提醒”建议不要改动原有时间段判断而是新增业务字段is_remind 是否允许预约提醒 remind_before 提前多少分钟提醒后端字段建议放在store_seckill_timeALTERTABLEeb_store_seckill_timeADDCOLUMNis_remindtinyint(1)NOTNULLDEFAULT0COMMENT是否开启秒杀场次提醒,ADDCOLUMNremind_beforeint(10)NOTNULLDEFAULT10COMMENT秒杀开始前提醒分钟数;Service 输出时补字段publicfunctiontime_list(){$list$this-dao-getList([status1],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 商城系统 源码解析 营销活动