需求:
今天的开发需求是记录web和pc端的访问量。初步思路是在controller入口进行记录。使用redis中的Hash数据类型。key为日期,value为次数,正好Hash提供了累加(hincrby)操作,可以满足要求。目前的时间粒度是按照天数,后期扩展的时候可以将时间粒度进行细分。
编码:
在laravel中的那个地方进行次数的统计呢?有两个地方,一个是BaseController,还有一个中间件,所有的请求均经过这里。在统计的时候需要注意的是进行过滤一些请求,比如接口post请求,来自后台管理系统的请求等等。
//基类controller构造函数
public function __construct(Request $request)
{
$requestParam = request()->route()->getAction(); //获取访问的url
$domain = [env('WECHAT_SUB_ROUTE'), env('FRONTEND_SUB_ROUTE')];//参与次数统计的rul
if (in_array($requestParam['domain'], $domain) && $request->isMethod('get')) {
$hashKey = 'webVisits';
Redis::hincrby($hashKey, date('Y-m-d'), 1);//存入redis的hash
}
}
//getAction方法可以取到的值
array:8 [▼
"domain" => "www.laravelylw.com"
"middleware" => array:5 [▶]
"as" => "xiong.test"
"uses" => "App\Http\Controllers\Home\Main\XiongTestController@index"
"controller" => "App\Http\Controllers\Home\Main\XiongTestController@index"
"namespace" => "App\Http\Controllers\Home\Main"
"prefix" => null
"where" => []
]
根据需求可以看到会按照月和周进行比较。所以初步思路是获取某个时间段的startTime和endTime,然后通过Redis::hgetall($key)获取到哈希表中所有的key-value,以数组的方式返回。
然后遍历数组,将每个值和startTime-endTime比较,是否在该范围,若在就进行累加,即可得到这个时间段的访问量总和。
//本周访问量
$memberVisiyNum_week =$this->getVisit(Time::week());
//date是一个数组包含startTIme和endTIme,获取本周,上周,本月,上个月等时间工具在后面附上。
//还使用到Carbon工具类,用法见https://9iphp.com/web/laravel/php-datetime-package-carbon.html
private function getVisit($date){
$key = 'webVisits';//hash的key
$lists = Redis::hgetall($key);//返回整个hash表元素
$visit = 0;
$Carbon = new Carbon();
$startTime = $Carbon->create(date('Y',$date[0]),date('m',$date[0]),date('d',$date[0]),0,0,0);//用于后面使用Carbon种的日期比较方法
$endTime = $Carbon->create(date('Y',$date[1]),date('m',$date[1]),date('d',$date[1]),0,0,0);
foreach ($lists as $k => $v){
$vTime = $Carbon->create(date('Y',strtotime($k)),date('m',strtotime($k)),date('d',strtotime($k)),0,0,0);//可以兼容细化时间粒度后。
if($vTime->gte($startTime) && $vTime->lte($endTime)){
$visit = $visit + Redis::hget($key,$k);
}
}
return $visit;
}
至此编码结束。
优化
可以优化的地方有以下几个。
1、时间粒度的优化,目前的业务是统计到天,没准哪天运营说我要看看每天那个时间段访问量最多。如果将时间粒度细化了,是否还能兼容现在的getVisit方法呢,是可以的。
2、通过Redis::hgetall($key)取值的时候,我们其实每次不需要都取出来,根据业务需求,最多60天的数据就ok了,如果每次都取出来,foreach的时候很浪费时间。
Time()工具类
class Time
{
/**
* 返回今日开始和结束的时间戳
*
* @return array
*/
public static function today()
{
return [
mktime(0, 0, 0, date('m'), date('d'), date('Y')),
mktime(23, 59, 59, date('m'), date('d'), date('Y'))
];
}
/**
* 返回昨日开始和结束的时间戳
*
* @return array
*/
public static function yesterday()
{
$yesterday = date('d') - 1;
return [
mktime(0, 0, 0, date('m'), $yesterday, date('Y')),
mktime(23, 59, 59, date('m'), $yesterday, date('Y'))
];
}
/**
* 返回本周开始和结束的时间戳
*
* @return array
*/
public static function week()
{
$timestamp = time();
return [
strtotime(date('Y-m-d', strtotime("this week Monday", $timestamp))),
strtotime(date('Y-m-d', strtotime("this week Sunday", $timestamp))) + 24 * 3600 - 1
];
}
/**
* 返回上周开始和结束的时间戳
*
* @return array
*/
public static function lastWeek()
{
$timestamp = time();
return [
strtotime(date('Y-m-d', strtotime("last week Monday", $timestamp))),
strtotime(date('Y-m-d', strtotime("last week Sunday", $timestamp))) + 24 * 3600 - 1
];
}
/**
* 返回本月开始和结束的时间戳
*
* @return array
*/
public static function month($everyDay = false)
{
return [
mktime(0, 0, 0, date('m'), 1, date('Y')),
mktime(23, 59, 59, date('m'), date('t'), date('Y'))
];
}
/**
* 返回上个月开始和结束的时间戳
*
* @return array
*/
public static function lastMonth()
{
$begin = mktime(0, 0, 0, date('m') - 1, 1, date('Y'));
$end = mktime(23, 59, 59, date('m') - 1, date('t', $begin), date('Y'));
return [$begin, $end];
}
/**
* 返回今年开始和结束的时间戳
*
* @return array
*/
public static function year()
{
return [
mktime(0, 0, 0, 1, 1, date('Y')),
mktime(23, 59, 59, 12, 31, date('Y'))
];
}
/**
* 返回去年开始和结束的时间戳
*
* @return array
*/
public static function lastYear()
{
$year = date('Y') - 1;
return [
mktime(0, 0, 0, 1, 1, $year),
mktime(23, 59, 59, 12, 31, $year)
];
}
public static function dayOf()
{
}
/**
* 获取几天前零点到现在/昨日结束的时间戳
*
* @param int $day 天数
* @param bool $now 返回现在或者昨天结束时间戳
* @return array
*/
public static function dayToNow($day = 1, $now = true)
{
$end = time();
if (!$now) {
list($foo, $end) = self::yesterday();
}
return [
mktime(0, 0, 0, date('m'), date('d') - $day, date('Y')),
$end
];
}
/**
* 返回几天前的时间戳
*
* @param int $day
* @return int
*/
public static function daysAgo($day = 1)
{
$nowTime = time();
return $nowTime - self::daysToSecond($day);
}
/**
* 返回几天后的时间戳
*
* @param int $day
* @return int
*/
public static function daysAfter($day = 1)
{
$nowTime = time();
return $nowTime + self::daysToSecond($day);
}
/**
* 天数转换成秒数
*
* @param int $day
* @return int
*/
public static function daysToSecond($day = 1)
{
return $day * 86400;
}
/**
* 周数转换成秒数
*
* @param int $week
* @return int
*/
public static function weekToSecond($week = 1)
{
return self::daysToSecond() * 7 * $week;
}
private static function startTimeToEndTime()
{
}
}