一、场景
之前做的电商平台,用户在收到货之后,大部分都不会主动的点击确认收货,导致给商家结款的时候,商家各种投诉,于是就根据需求,要做一个订单在发货之后的x天自动确认收货。所谓的订单自动确认收货,就是在在特定的时间,执行一条update语句,改变订单的状态。
二、思路
最笨重的做法,通过linux后台定时任务,查询符合条件的订单,然后update。最理想情况下,如果每分钟都有需要update的订单,这种方式也还行。奈何平台太小,以及卖家发货时间大部分也是密集的,不会分散在24小时的每分钟。那么,定时任务的话,查询过多,不适合。这里可以先把将要自动确认收货的订单信息存储到其他介质上,比如redis,memcache,rabbitmq,然后执行的脚本从前面的介质获取到订单信息来判断,这里可以大大的减少数据库的查询压力。
redis队列的生产者
对此,我们选择每天在凌晨两点的时候,通过linux的定时任务把即将要确认收货的订单信息查询出来,然后存储在redis上,redis上我们选择的队列,队列处理的特点就是先进先出,前面的数据在查询订单时,通过发货时间排序,所以最先出队列的肯定是距离规定的自动收货时间最近的订单。代码如下

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/21
 * Time: 9:30
 */
$successCount = 0;
$failCount = 0;
$screen_time = 3600 * 24 * 9;//设置筛选天数
$data = array();
$now_time = time();
$con = mysqli_connect('127.0.0.1', 'root', '123456', 'test');
$redis = new \Redis();
$redis->connect('127.0.0.1');
//查询符合要求的数据
$sql = "select id,send_time as deliver_time from `order` where is_send=1 and is_del=0 and is_cancel=0 and is_token=0 and send_time>0 and send_time + {$screen_time} < $now_time
order by send_time asc";
$res = $con->query($sql);
//当队列还有数据时将数据记录并清除
while ($redis->LLEN('auto_recevice_order')) {
    $txt = '执行时间:' . date('Y-m-d H:i:s') . ',信息:' . $redis->RPOP('auto_recevice_order');
    file_put_contents('./autoToken/fail_log.txt', $txt . "\r\n" . PHP_EOL, FILE_APPEND);
    $failCount++;
}
//重新填充数据进队列
while ($row = $res->fetch_assoc()) {
    $successCount++;
    $redis->LPUSH('auto_recevice_order', json_encode($row));
}
$con->close();
$success = date('Y-m-d H:i:s') . ':[推送成功]:本次成功推送数据:' . $successCount . '条;记录上次处理失败数据:' . $failCount . "条\r\n";
file_put_contents('./success_log.txt', $success . "\r\n" . PHP_EOL, FILE_APPEND);

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

<?php
/**
 * Created by ZhengNiu.
 * User: admin
 * Date: 2019/8/21
 * Time: 9:41
 */
$set_time = 3600 * 24 * 10;//设置几天后自动收货
$con = mysqli_connect('127.0.0.1', 'root', '123456', 'test');
$redis = new \Redis();
$redis->connect('127.0.0.1');
while (true) {
    if ($i % 30 == 0) {
        sleep(10);//防止while 循环使CPU使用率过高
    }
    if ($redis->LLEN('auto_recevice_order')) {
        $data = json_decode($redis->RPOP('auto_recevice_order'));
        $id = (int)$data->id;//将数据转化为整形
        $deliver_time = (int)$data->deliver_time;//将数据转化为整形
        $res1 = $res2 = false;
        $now_time = time();
        if (($deliver_time + $set_time) < $now_time) {
            $sql1 = "update `order` set `is_token`='1',`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";
            $res1 = $con->query($sql1);//更新数据
            $rows = mysqli_affected_rows($con);
            if ($rows) {
                $ip = getIp();
                $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)";//写入订单日志
                $res2 = $con->query($sql2);//添加日志数据
            }
        }
        if ($res1 == false) {//将没达到条件的数据重新插入队列中
            $redis->RPUSH('auto_recevice_order', json_encode(array('id' => $id, 'deliver_time' => $deliver_time)));
        }
    }
    $i++;
}
function getIp()
{
    $ip = $_SERVER['REMOTE_ADDR'];
    if (isset($_SERVER['HTTP_CDN_SRC_IP'])) {
        $ip = $_SERVER['HTTP_CDN_SRC_IP'];
    } elseif (isset($_SERVER['HTTP_CLIENT_IP']) && preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $_SERVER['HTTP_CLIENT_IP'])) {
        $ip = $_SERVER['HTTP_CLIENT_IP'];
    } elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
        foreach ($matches[0] AS $xip) {
            if (!preg_match('#^(10|172\.16|192\.168)\.#', $xip)) {
                $ip = $xip;
                break;
            }
        }
    }
    return $ip;
}

数据库结果:
order表:
1.png
order_log表:
2.png

涉及到的创建表的sql

CREATE TABLE `order` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `is_del` tinyint(1) NOT NULL DEFAULT '0',
  `send_time` int(11) NOT NULL DEFAULT '0',
  `is_cancel` tinyint(1) NOT NULL DEFAULT '0',
  `is_send` tinyint(1) NOT NULL DEFAULT '0',
  `is_token` tinyint(1) NOT NULL DEFAULT '0',
  `token_time` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=122 DEFAULT CHARSET=latin1;
CREATE TABLE `order_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_id` int(11) DEFAULT NULL,
  `log_msg` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
  `log_ip` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
  `log_role` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
  `log_user` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
  `log_order_state` tinyint(255) DEFAULT NULL,
  `log_time` int(11) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=113 DEFAULT CHARSET=latin1;

redis的相关文章

Last modification:September 19, 2020
如果觉得我的文章对你有用,请随意赞赏