PHP如何实现电商订单自动确认收货

五月 02, 2019 | views
Comments 0

一、场景

之前做的电商平台,用户在收到货之后,大部分都不会主动的点击确认收货,导致给商家结款的时候,商家各种投诉,于是就根据需求,要做一个订单在发货之后的x天自动确认收货。所谓的订单自动确认收货,就是在在特定的时间,执行一条update语句,改变订单的状态。

二、思路

最笨重的做法,通过linux后台定时任务,查询符合条件的订单,然后update。最理想情况下,如果每分钟都有需要update的订单,这种方式也还行。奈何平台太小,以及卖家发货时间大部分也是密集的,不会分散在24小时的每分钟。那么,定时任务的话,查询过多,不适合。这里可以先把将要自动确认收货的订单信息存储到其他介质上,比如redis,memcache,rabbitmq,然后执行的脚本从前面的介质获取到订单信息来判断,这里可以大大的减少数据库的查询压力。

redis队列的生产者

对此,我们选择每天在凌晨两点的时候,通过linux的定时任务把即将要确认收货的订单信息查询出来,然后存储在redis上,redis上我们选择的队列,队列处理的特点就是先进先出,前面的数据在查询订单时,通过发货时间排序,所以最先出队列的肯定是距离规定的自动收货时间最近的订单。代码如下:

  1. $successCount=0; 
  2.  
  3. $failCount=0; 
  4.  
  5. $screen_time= 3600*24*9;//设置筛选天数 
  6.  
  7. $data=array(); 
  8.  
  9. $now_time= time(); 
  10.  
  11. //查询符合要求的数据 
  12.  
  13. $sql="select id,send_timeasdeliver_time from `order` where is_send=1andis_del=0andis_cancel=0andis_token=0andsend_time>0andsend_time + {$screen_time} <$now_time 
  14.  
  15. order by send_time asc"; 
  16.  
  17. $res=$con->query($sql); 
  18.  
  19. //当队列还有数据时将数据记录并清除 
  20.  
  21. while($redis->LLEN('auto_recevice_order')){ 
  22.  
  23. $txt='执行时间:'.date('Y-m-d H:i:s').',信息:'.$redis->RPOP('auto_recevice_order'); 
  24.  
  25. file_put_contents('./autoToken/fail_log.txt',$txt."\r\n".PHP_EOL,FILE_APPEND); 
  26.  
  27. $failCount++; 
  28.  
  29.  
  30. //重新填充数据进队列 
  31.  
  32. while($row=$res->fetch_assoc()) { 
  33.  
  34.  $successCount++; 
  35.  
  36.  $redis->LPUSH('auto_recevice_order',json_encode($row1)); 
  37.  
  38. //phpfensi.com 
  39.  $con->close(); 
  40.  
  41.  $success=date('Y-m-d H:i:s').':[推送成功]:本次成功推送数据:'.$successCount.'条;记录上次处理失败数据:'.$failCount."条\r\n"
  42.  
  43.  file_put_contents('./success_log.txt',$success."\r\n".PHP_EOL,FILE_APPEND); 

redis队列的消费者

队列的消费者没有通过linux的定时任务去做,用linux的screen+php cli模式执行php脚本,消费者只需要不断的从队列中读取订单信息,然后判断订单信息中的发货时间,如果达到自动收货的要求,就执行update语句。同时如果没有达到收货的时间,而且与收货时间间距比较大的时候,可以让php脚本休眠sleep一定的时间数,这个时间数自己调节设计,获取出来的未达到时间要求的订单,需要重新推送到redis队列中去,而且还是队列的顶端。以便下次获取。代码如下:

  1. $set_time= 3600*24*10;//设置几天后自动收货 
  2.  
  3. while(true){ 
  4.  
  5. if($i%30==0){ 
  6.  
  7. usleep(10);//防止while 循环使CPU使用率过高 
  8.  
  9.  
  10. if($redis->LLEN('auto_recevice_order')){ 
  11.  
  12. $data= json_decode($redis->RPOP('auto_recevice_order')); 
  13.  
  14. $id= (int)$data->id;//将数据转化为整形 
  15.  
  16. $deliver_time= (int)$data->deliver_time;//将数据转化为整形 
  17.  
  18. $res1=$res2=false; 
  19.  
  20. $now_time= time(); 
  21.  
  22. if(($deliver_time+$set_time)<$now_time){ 
  23.  
  24.  $sql1="update `order` set `is_token`=Ƈ',`token_time` = $now_time where id=$id and is_send=1 and is_del=0 and is_cancel=0 and is_token=0 and send_time + {$set_time} < $now_time"
  25.  
  26.  $res1=$con->query($sql1);//更新数据 
  27.  
  28. $rows= mysqli_affected_rows($con); 
  29.  
  30. if($rows){ 
  31.  
  32.  $ip=$this->getIp(); 
  33.  
  34.  $sql2="insert into `order_log`(`order_id`,`log_msg`,`log_ip`,`log_role`,`log_user`,`log_order_state`,`log_time`) VALUES($id,'系统自动收货','$ip','系统','服务器','收货',$now_time)";//写入订单日志 
  35.  
  36.  $res2=$con->query($sql2);//添加日志数据 
  37.  
  38.  } 
  39.  
  40.  } 
  41.  
  42.  if($res1==false){//将没达到条件的数据重新插入队列中 
  43.  
  44.   $redis->RPUSH('auto_recevice_order',json_encode(array('id'=>$id,'deliver_time'=>$deliver_time))); 
  45.  
  46.  } 
  47.  
  48.  
  49.  $i++; 
  50.  

这里执行php脚本,需要用到linux的screen或者supervisor、nohup守护进程。具体用法可自行百度.同样脚本里面最好有必须的日志记录。

三、思考

随着业务的增长,在队列中同一秒内,存在的多个需要处理的订单,而一次只能从队列中取出一个相关订单信息的时候,可以采用一个生产者多个消费者的模式,这种情况下,可以用到锁机制,保证一条消息只能到达一个消费者。当redis数据达到一定的量之后,也可以适当的调整生产者的执行频率和对应的条件。

以上这篇PHP实现电商订单自动确认收货redis队列就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本网站。



zend