需求:
今天的開發(fā)需求是記錄web和pc端的訪問量腋颠。初步思路是在controller入口進(jìn)行記錄繁成。使用redis中的Hash數(shù)據(jù)類型。key為日期淑玫,value為次數(shù)巾腕,正好Hash提供了累加(hincrby)操作,可以滿足要求絮蒿。目前的時(shí)間粒度是按照天數(shù)尊搬,后期擴(kuò)展的時(shí)候可以將時(shí)間粒度進(jìn)行細(xì)分。
編碼:
在laravel中的那個(gè)地方進(jìn)行次數(shù)的統(tǒng)計(jì)呢土涝?有兩個(gè)地方佛寿,一個(gè)是BaseController,還有一個(gè)中間件但壮,所有的請(qǐng)求均經(jīng)過這里冀泻。在統(tǒng)計(jì)的時(shí)候需要注意的是進(jìn)行過濾一些請(qǐng)求,比如接口post請(qǐng)求蜡饵,來自后臺(tái)管理系統(tǒng)的請(qǐng)求等等弹渔。
//基類controller構(gòu)造函數(shù)
public function __construct(Request $request)
{
$requestParam = request()->route()->getAction(); //獲取訪問的url
$domain = [env('WECHAT_SUB_ROUTE'), env('FRONTEND_SUB_ROUTE')];//參與次數(shù)統(tǒng)計(jì)的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" => []
]
根據(jù)需求可以看到會(huì)按照月和周進(jìn)行比較。所以初步思路是獲取某個(gè)時(shí)間段的startTime和endTime溯祸,然后通過Redis::hgetall($key)獲取到哈希表中所有的key-value肢专,以數(shù)組的方式返回。
然后遍歷數(shù)組焦辅,將每個(gè)值和startTime-endTime比較鸟召,是否在該范圍,若在就進(jìn)行累加氨鹏,即可得到這個(gè)時(shí)間段的訪問量總和。
//本周訪問量
$memberVisiyNum_week =$this->getVisit(Time::week());
//date是一個(gè)數(shù)組包含startTIme和endTIme压状,獲取本周仆抵,上周跟继,本月,上個(gè)月等時(shí)間工具在后面附上镣丑。
//還使用到Carbon工具類舔糖,用法見https://9iphp.com/web/laravel/php-datetime-package-carbon.html
private function getVisit($date){
$key = 'webVisits';//hash的key
$lists = Redis::hgetall($key);//返回整個(gè)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);//可以兼容細(xì)化時(shí)間粒度后。
if($vTime->gte($startTime) && $vTime->lte($endTime)){
$visit = $visit + Redis::hget($key,$k);
}
}
return $visit;
}
至此編碼結(jié)束莺匠。
優(yōu)化
可以優(yōu)化的地方有以下幾個(gè)金吗。
1、時(shí)間粒度的優(yōu)化趣竣,目前的業(yè)務(wù)是統(tǒng)計(jì)到天摇庙,沒準(zhǔn)哪天運(yùn)營(yíng)說我要看看每天那個(gè)時(shí)間段訪問量最多。如果將時(shí)間粒度細(xì)化了遥缕,是否還能兼容現(xiàn)在的getVisit方法呢卫袒,是可以的。
2单匣、通過Redis::hgetall($key)取值的時(shí)候夕凝,我們其實(shí)每次不需要都取出來,根據(jù)業(yè)務(wù)需求户秤,最多60天的數(shù)據(jù)就ok了码秉,如果每次都取出來,foreach的時(shí)候很浪費(fèi)時(shí)間鸡号。
Time()工具類
class Time
{
/**
* 返回今日開始和結(jié)束的時(shí)間戳
*
* @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'))
];
}
/**
* 返回昨日開始和結(jié)束的時(shí)間戳
*
* @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'))
];
}
/**
* 返回本周開始和結(jié)束的時(shí)間戳
*
* @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
];
}
/**
* 返回上周開始和結(jié)束的時(shí)間戳
*
* @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
];
}
/**
* 返回本月開始和結(jié)束的時(shí)間戳
*
* @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'))
];
}
/**
* 返回上個(gè)月開始和結(jié)束的時(shí)間戳
*
* @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];
}
/**
* 返回今年開始和結(jié)束的時(shí)間戳
*
* @return array
*/
public static function year()
{
return [
mktime(0, 0, 0, 1, 1, date('Y')),
mktime(23, 59, 59, 12, 31, date('Y'))
];
}
/**
* 返回去年開始和結(jié)束的時(shí)間戳
*
* @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()
{
}
/**
* 獲取幾天前零點(diǎn)到現(xiàn)在/昨日結(jié)束的時(shí)間戳
*
* @param int $day 天數(shù)
* @param bool $now 返回現(xiàn)在或者昨天結(jié)束時(shí)間戳
* @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
];
}
/**
* 返回幾天前的時(shí)間戳
*
* @param int $day
* @return int
*/
public static function daysAgo($day = 1)
{
$nowTime = time();
return $nowTime - self::daysToSecond($day);
}
/**
* 返回幾天后的時(shí)間戳
*
* @param int $day
* @return int
*/
public static function daysAfter($day = 1)
{
$nowTime = time();
return $nowTime + self::daysToSecond($day);
}
/**
* 天數(shù)轉(zhuǎn)換成秒數(shù)
*
* @param int $day
* @return int
*/
public static function daysToSecond($day = 1)
{
return $day * 86400;
}
/**
* 周數(shù)轉(zhuǎn)換成秒數(shù)
*
* @param int $week
* @return int
*/
public static function weekToSecond($week = 1)
{
return self::daysToSecond() * 7 * $week;
}
private static function startTimeToEndTime()
{
}
}