mirror of
https://github.com/kokororin/typecho-plugin-Access.git
synced 2024-12-27 13:20:54 +08:00
328 lines
11 KiB
PHP
328 lines
11 KiB
PHP
<?php
|
|
if (!defined('__ACCESS_PLUGIN_ROOT__')) {
|
|
throw new Exception('Boostrap file not found');
|
|
}
|
|
|
|
class Access_Statistic {
|
|
private $db;
|
|
private $request;
|
|
|
|
public function __construct($request) {
|
|
$this->db = Typecho_Db::get();
|
|
$this->request = $request;
|
|
}
|
|
|
|
/**
|
|
* 获取计数分析
|
|
*
|
|
* @access private
|
|
* @return ?array
|
|
* @throws Exception
|
|
*/
|
|
private function count(): ?array {
|
|
$resp = [];
|
|
$type = $this->request->get('type', 'total'); # 统计类型
|
|
$dstTime = $this->request->get('time'); # 目标时间
|
|
switch($type) {
|
|
# 总计数据
|
|
case 'total':
|
|
$startTime = 0;
|
|
$endTime = 0;
|
|
break;
|
|
# 按天统计
|
|
case 'day':
|
|
if(!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dstTime))
|
|
throw new Exception('Bad Request', 400);
|
|
$startTime = strtotime(date("{$dstTime} 00:00:00"));
|
|
$endTime = strtotime(date("{$dstTime} 23:59:59"));
|
|
break;
|
|
# 按月统计
|
|
case 'month':
|
|
if(!preg_match('/^\d{4}-\d{2}$/', $dstTime))
|
|
throw new Exception('Bad Request', 400);
|
|
[$year, $month] = explode('-', $dstTime);
|
|
$monthDays = date('t', mktime(0, 0, 0, intval($month), 1, intval($year))); # 计算当月天数
|
|
$startTime = strtotime(date("{$dstTime}-01 00:00:00"));
|
|
$endTime = strtotime(date("{$dstTime}-{$monthDays} 23:59:59"));
|
|
break;
|
|
default:
|
|
throw new Exception('Bad Request', 400);
|
|
}
|
|
$resp['type'] = $type;
|
|
$resp['dst'] = $dstTime;
|
|
# ip数
|
|
$subQuery = $this->db
|
|
->select('DISTINCT ip')
|
|
->from('table.access_logs');
|
|
if ($endTime > 0) {
|
|
$subQuery->where("time >= ? AND time <= ?", $startTime, $endTime);
|
|
}
|
|
if(method_exists($subQuery, 'prepare'))
|
|
$subQuery = $subQuery->prepare($subQuery);
|
|
$resp['count']['ip'] = intval($this->db->fetchRow(
|
|
$this->db
|
|
->select('COUNT(1) AS cnt')
|
|
->from('(' . $subQuery . ') AS tmp')
|
|
)['cnt']);
|
|
# 访客数
|
|
$subQuery = $this->db
|
|
->select('DISTINCT ip, ua')
|
|
->from('table.access_logs');
|
|
if ($endTime > 0) {
|
|
$subQuery->where("time >= ? AND time <= ?", $startTime, $endTime);
|
|
}
|
|
if(method_exists($subQuery, 'prepare'))
|
|
$subQuery = $subQuery->prepare($subQuery);
|
|
$resp['count']['uv'] = intval($this->db->fetchRow(
|
|
$this->db
|
|
->select('COUNT(1) AS cnt')
|
|
->from('(' . $subQuery . ') AS tmp')
|
|
)['cnt']);
|
|
# 浏览数
|
|
$subQuery = $this->db
|
|
->select('COUNT(1) AS cnt')
|
|
->from('table.access_logs');
|
|
if ($endTime > 0) {
|
|
$subQuery->where("time >= ? AND time <= ?", $startTime, $endTime);
|
|
}
|
|
$resp['count']['pv'] = intval($this->db->fetchRow($subQuery)['cnt']);
|
|
return $resp;
|
|
}
|
|
|
|
/**
|
|
* 获取文章访问统计
|
|
*
|
|
* @access private
|
|
* @return ?array
|
|
* @throws Exception
|
|
*/
|
|
private function article(): ?array {
|
|
$resp = [];
|
|
$ps = $this->request->get('ps', 10); # 页大小
|
|
# 统计文章浏览比例
|
|
foreach(
|
|
$this->db->fetchAll(
|
|
$this->db
|
|
->select('content_id AS cid, table.contents.title AS title, COUNT(1) AS cnt')
|
|
->from('table.access_logs')
|
|
->join('table.contents', 'content_id = table.contents.cid', Typecho_Db::INNER_JOIN)
|
|
->where('IFNULL(content_id, 0)')
|
|
->group('content_id')
|
|
->order('cnt', Typecho_Db::SORT_DESC)
|
|
->limit($ps)
|
|
) as $i
|
|
) {
|
|
$resp[] = [
|
|
'cid' => intval($i['cid']),
|
|
'title' => $i['title'],
|
|
'count' => intval($i['cnt'])
|
|
];
|
|
}
|
|
return $resp;
|
|
}
|
|
|
|
/**
|
|
* 获取访问地域统计
|
|
*
|
|
* @access private
|
|
* @return ?array
|
|
* @throws Exception
|
|
*/
|
|
private function location(): ?array {
|
|
$resp = [];
|
|
$ps = $this->request->get('ps', 10); # 页大小
|
|
$cate = $this->request->get('cate'); # 类型
|
|
switch($cate) {
|
|
case 'china':
|
|
# 国内
|
|
$fetchData = $this->db->fetchAll(
|
|
$this->db
|
|
->select("IF(ip_province = '中国', '国内未明确', ip_province) AS area, COUNT(1) AS cnt")
|
|
->from('table.access_logs')
|
|
->where("ip_country = '中国'")
|
|
->group('area')
|
|
->order('cnt', Typecho_Db::SORT_DESC)
|
|
->limit($ps)
|
|
);
|
|
break;
|
|
case 'inter':
|
|
# 国际
|
|
$fetchData = $this->db->fetchAll(
|
|
$this->db
|
|
->select("ip_country AS area, COUNT(1) AS cnt")
|
|
->from('table.access_logs')
|
|
->group('area')
|
|
->order('cnt', Typecho_Db::SORT_DESC)
|
|
->limit(15)
|
|
);
|
|
break;
|
|
default:
|
|
throw new Exception('Bad Request', 400);
|
|
}
|
|
foreach($fetchData as $row) {
|
|
$resp[] = [
|
|
'area' => $row['area'],
|
|
'count' => intval($row['cnt'])
|
|
];
|
|
}
|
|
return $resp;
|
|
}
|
|
|
|
/**
|
|
* 获取访问量图表数据
|
|
*
|
|
* @access private
|
|
* @return ?array
|
|
* @throws Exception
|
|
*/
|
|
private function chart(): ?array {
|
|
$resp = [];
|
|
$type = $this->request->get('type'); # 统计类型
|
|
$dstTime = $this->request->get('time'); # 目标时间
|
|
switch($type) {
|
|
# 按天统计
|
|
case 'day':
|
|
if(!preg_match('/^\d{4}-\d{2}-\d{2}$/', $dstTime))
|
|
throw new Exception('Bad Request', 400);
|
|
$loopStart = 0;
|
|
$loopEnd = 23;
|
|
break;
|
|
# 按月统计
|
|
case 'month':
|
|
if(!preg_match('/^\d{4}-\d{2}$/', $dstTime))
|
|
throw new Exception('Bad Request', 400);
|
|
[$year, $month] = explode('-', $dstTime);
|
|
$loopStart = 1;
|
|
$loopEnd = date('t', mktime(0, 0, 0, intval($month), 1, intval($year))); # 计算当月天数
|
|
break;
|
|
default:
|
|
throw new Exception('Bad Request', 400);
|
|
}
|
|
$resp['type'] = $type;
|
|
$resp['dst'] = $dstTime;
|
|
|
|
foreach(range($loopStart, $loopEnd) as $i) {
|
|
$chart = [];
|
|
switch($type) {
|
|
case 'day':
|
|
$startTime = strtotime(date("{$dstTime} {$i}:00:00"));
|
|
$endTime = strtotime(date("{$dstTime} {$i}:59:59"));
|
|
break;
|
|
case 'month':
|
|
$startTime = strtotime(date("{$dstTime}-{$i} 00:00:00"));
|
|
$endTime = strtotime(date("{$dstTime}-{$i} 23:59:59"));
|
|
break;
|
|
}
|
|
$chart['time'] = $i;
|
|
# ip数
|
|
$subQuery = $this->db
|
|
->select('DISTINCT ip')
|
|
->from('table.access_logs')
|
|
->where('time >= ? AND time <= ?', $startTime, $endTime);
|
|
if(method_exists($subQuery, 'prepare'))
|
|
$subQuery = $subQuery->prepare($subQuery);
|
|
$chart['ip'] = intval($this->db->fetchRow(
|
|
$this->db
|
|
->select('COUNT(1) AS count')
|
|
->from('(' . $subQuery . ') AS tmp')
|
|
)['count']);
|
|
# 访客数
|
|
$subQuery = $this->db
|
|
->select('DISTINCT ip,ua')
|
|
->from('table.access_logs')
|
|
->where('time >= ? AND time <= ?', $startTime, $endTime);
|
|
if(method_exists($subQuery, 'prepare'))
|
|
$subQuery = $subQuery->prepare($subQuery);
|
|
$chart['uv'] = intval($this->db->fetchRow(
|
|
$this->db
|
|
->select('COUNT(1) AS count')
|
|
->from('(' . $subQuery . ') AS tmp')
|
|
)['count']);
|
|
# 浏览数
|
|
$chart['pv'] = intval($this->db->fetchRow(
|
|
$this->db
|
|
->select('COUNT(1) AS count')
|
|
->from('table.access_logs')
|
|
->where('time >= ? AND time <= ?', $startTime, $endTime)
|
|
)['count']);
|
|
$resp['chart'][] = $chart;
|
|
}
|
|
|
|
# 计算各平均值
|
|
$pvSum = 0;
|
|
$uvSum = 0;
|
|
$ipSum = 0;
|
|
$cnt = count($resp['chart']);
|
|
foreach($resp['chart'] as $i) {
|
|
$pvSum += $i['pv'];
|
|
$uvSum += $i['uv'];
|
|
$ipSum += $i['ip'];
|
|
}
|
|
$resp['avg']['pv'] = round($pvSum / $cnt, 2);
|
|
$resp['avg']['uv'] = round($uvSum / $cnt, 2);
|
|
$resp['avg']['ip'] = round($ipSum / $cnt, 2);
|
|
|
|
return $resp;
|
|
}
|
|
|
|
/**
|
|
* 获取来源统计
|
|
*
|
|
* @access private
|
|
* @return ?array
|
|
* @throws Exception
|
|
*/
|
|
private function referer(): ?array {
|
|
$resp = [];
|
|
$type = $this->request->get('type', 'url'); # 统计类型
|
|
$ps = $this->request->get('ps', 10);
|
|
$pn = $this->request->get('pn', 1);
|
|
switch($type) {
|
|
case 'url':
|
|
$fetchData = $this->db->fetchAll(
|
|
$this->db
|
|
->select('DISTINCT entrypoint AS value, COUNT(1) as cnt')
|
|
->from('table.access_logs')
|
|
->where("entrypoint != ''")
|
|
->group('entrypoint')
|
|
->order('cnt', Typecho_Db::SORT_DESC)
|
|
->page($pn, $ps)
|
|
);
|
|
break;
|
|
case 'domain':
|
|
$fetchData = $this->db->fetchAll($this->db
|
|
->select('DISTINCT entrypoint_domain AS value, COUNT(1) as cnt')
|
|
->from('table.access_logs')
|
|
->where("entrypoint_domain != ''")
|
|
->group('entrypoint_domain')
|
|
->order('cnt', Typecho_Db::SORT_DESC)
|
|
->page($pn, $ps)
|
|
);
|
|
break;
|
|
default:
|
|
throw new Exception('Bad Request', 400);
|
|
}
|
|
foreach($fetchData as $i) {
|
|
$resp[] = [
|
|
'value' => ($type == 'url') ? urldecode($i['value']) : $i['value'],
|
|
'count' => $i['cnt']
|
|
];
|
|
}
|
|
return $resp;
|
|
}
|
|
|
|
/**
|
|
* 业务调度入口
|
|
*
|
|
* @access public
|
|
* @param string rpcType 调用过程类型
|
|
* @return ?array
|
|
* @throws Exception
|
|
*/
|
|
public function invoke(string $rpcType): ?array {
|
|
if(!method_exists($this, $rpcType) || in_array($rpcType, ['__construct', 'invoke']))
|
|
throw new Exception('Bad Request', 400);
|
|
return $this->$rpcType(); # 方法指针
|
|
}
|
|
}
|