Commit 8da5c178 authored by 刘小敏's avatar 刘小敏

数据统计中心计划任务脚本,页面,接口

parent e7e8b184
...@@ -7,6 +7,10 @@ use think\queue\Job; ...@@ -7,6 +7,10 @@ use think\queue\Job;
use think\Db; use think\Db;
use think\Queue; use think\Queue;
/**
* 埋点数据处理任务
* PV UV 等统计
*/
class TrackProcessJob class TrackProcessJob
{ {
/** /**
...@@ -16,6 +20,7 @@ class TrackProcessJob ...@@ -16,6 +20,7 @@ class TrackProcessJob
try { try {
$trackData = $data['data'] ?? []; $trackData = $data['data'] ?? [];
$clientIp = $data['ip'] ?? ''; $clientIp = $data['ip'] ?? '';
$jobTimestamp = isset($data['timestamp']) ? (int)$data['timestamp'] : time();
if (empty($trackData)) { if (empty($trackData)) {
$job->delete(); $job->delete();
...@@ -23,7 +28,7 @@ class TrackProcessJob ...@@ -23,7 +28,7 @@ class TrackProcessJob
} }
// 批量插入埋点数据 // 批量插入埋点数据
$this->insertTracks($trackData, $clientIp); $this->insertTracks($trackData, $clientIp, $jobTimestamp);
// 删除任务 // 删除任务
$job->delete(); $job->delete();
...@@ -43,22 +48,39 @@ class TrackProcessJob ...@@ -43,22 +48,39 @@ class TrackProcessJob
/** /**
* 批量插入埋点数据 * 批量插入埋点数据
*/ */
protected function insertTracks($trackData, $clientIp) { protected function insertTracks($trackData, $clientIp, $jobTimestamp) {
$insertData = []; $insertData = [];
$now = time(); $seen = [];
foreach ($trackData as $item) { foreach ($trackData as $item) {
$insertData[] = [ $trackRow = [
'user_id' => $item['user_id'] ?? 0, 'user_id' => $item['user_id'] ?? 0,
'openid' => $item['openid'] ?? '', 'openid' => $item['openid'] ?? '',
'session_id' => $item['session_id'] ?? '', 'session_id' => $item['session_id'] ?? '',
'event_type' => $item['event_type'] ?? '', 'event_type' => $item['event_type'] ?? '',
'event_code' => $item['event_code'] ?? '',
'page_path' => $item['page_path'] ?? '', 'page_path' => $item['page_path'] ?? '',
'page_title' => $item['page_title'] ?? '', 'page_title' => $item['page_title'] ?? '',
'params' => !empty($item['params']) ? json_encode($item['params']) : '', 'params' => !empty($item['params']) ? json_encode($item['params']) : '',
'ip' => $clientIp, 'ip' => $clientIp,
'create_time' => isset($item['create_time']) ? (int)($item['create_time'] / 1000) : $now 'create_time' => isset($item['create_time']) ? (int)($item['create_time'] / 1000) : $jobTimestamp
]; ];
$dedupKey = $this->buildTrackDedupKey($trackRow);
if (isset($seen[$dedupKey])) {
continue;
}
$seen[$dedupKey] = true;
if ($this->trackExists($trackRow)) {
continue;
}
$insertData[] = $trackRow;
}
if (empty($insertData)) {
return;
} }
// 分批插入(每批500条) // 分批插入(每批500条)
...@@ -69,10 +91,48 @@ class TrackProcessJob ...@@ -69,10 +91,48 @@ class TrackProcessJob
} }
/** /**
* 构建埋点判重键
*/
protected function buildTrackDedupKey($trackRow)
{
return md5(implode('|', [
(string)$trackRow['user_id'],
(string)$trackRow['openid'],
(string)$trackRow['session_id'],
(string)$trackRow['event_type'],
(string)$trackRow['event_code'],
(string)$trackRow['page_path'],
(string)$trackRow['page_title'],
(string)$trackRow['params'],
(string)$trackRow['ip'],
(string)$trackRow['create_time'],
]));
}
/**
* 判断埋点是否已存在
*/
protected function trackExists($trackRow)
{
return Db::name('shopro_miniprogram_track')
->where('user_id', $trackRow['user_id'])
->where('openid', $trackRow['openid'])
->where('session_id', $trackRow['session_id'])
->where('event_type', $trackRow['event_type'])
->where('event_code', $trackRow['event_code'])
->where('page_path', $trackRow['page_path'])
->where('page_title', $trackRow['page_title'])
->where('params', $trackRow['params'])
->where('ip', $trackRow['ip'])
->where('create_time', $trackRow['create_time'])
->find() ? true : false;
}
/**
* 保存失败数据 * 保存失败数据
*/ */
protected function saveFailed($data, $error) { protected function saveFailed($data, $error) {
Db::name('shopro_track_failed')->insert([ Db::name('shopro_miniprogram_track_failed')->insert([
'data' => json_encode($data), 'data' => json_encode($data),
'error' => $error, 'error' => $error,
'attempts' => 3, 'attempts' => 3,
...@@ -81,4 +141,4 @@ class TrackProcessJob ...@@ -81,4 +141,4 @@ class TrackProcessJob
} }
} }
\ No newline at end of file
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_2385137_b8qygb2jne.css?v={$site.version|htmlentities}"> <link rel="stylesheet" href="//at.alicdn.com/t/c/font_2385137_b8qygb2jne.css?v={$site.version|htmlentities}">
<link rel="stylesheet" href="__CDN__/assets/addons/shopro/css/index.css?v={$site.version|htmlentities}"> <link rel="stylesheet" href="__CDN__/assets/addons/shopro/css/index.css?v={$site.version|htmlentities}">
<script src="__CDN__/assets/libs/jquery/dist/jquery.min.js?v={$site.version|htmlentities}"></script>
{if condition="$DARK_TYPE neq 'none'" } {if condition="$DARK_TYPE neq 'none'" }
<link rel="stylesheet" href="__CDN__/assets/addons/shopro/css/dark.css?v={$site.version|htmlentities}" data-render="darktheme" /> <link rel="stylesheet" href="__CDN__/assets/addons/shopro/css/dark.css?v={$site.version|htmlentities}" data-render="darktheme" />
{/if} {/if}
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
// | Author: yunwuxin <448901948@qq.com> // | Author: yunwuxin <448901948@qq.com>
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
use Dom\DtdNamedNodeMap;
return [ return [
'app\admin\command\Crud', 'app\admin\command\Crud',
'app\admin\command\Menu', 'app\admin\command\Menu',
...@@ -21,4 +23,6 @@ return [ ...@@ -21,4 +23,6 @@ return [
'app\command\StatMemberTag', 'app\command\StatMemberTag',
'app\command\StatsDaily', 'app\command\StatsDaily',
'app\command\TrackFailedRetry', 'app\command\TrackFailedRetry',
'app\command\DataStatsCenter',
]; ];
This diff is collapsed.
...@@ -36,6 +36,7 @@ class TrackFailedRetry extends Command ...@@ -36,6 +36,7 @@ class TrackFailedRetry extends Command
} }
protected function execute(Input $input, Output $output) { protected function execute(Input $input, Output $output) {
$this->output = $output;
$this->customLog('info', '========== [' . date('Y-m-d H:i:s') . '] 开始执行重试失败埋埋点数据任务 =========='); $this->customLog('info', '========== [' . date('Y-m-d H:i:s') . '] 开始执行重试失败埋埋点数据任务 ==========');
...@@ -78,8 +79,24 @@ class TrackFailedRetry extends Command ...@@ -78,8 +79,24 @@ class TrackFailedRetry extends Command
throw new \Exception('数据格式错误'); throw new \Exception('数据格式错误');
} }
$retryPayload = $this->filterRetryPayload($data);
if (empty($retryPayload['data'])) {
Db::name('shopro_miniprogram_track_failed')
->where('id', $item['id'])
->update([
'status' => 1,
'error' => '同一操作已入库,跳过重试',
'update_time' => time()
]);
$totalSuccess++;
$totalProcessed++;
continue;
}
// 重新推入队列 // 重新推入队列
Queue::push('\addons\shopro\job\TrackProcessJob@fire', $data['data'], 'shopro-high'); Queue::push('\addons\shopro\job\TrackProcessJob@fire', $retryPayload, 'shopro-high');
// 更新状态为已处理 // 更新状态为已处理
Db::name('shopro_miniprogram_track_failed') Db::name('shopro_miniprogram_track_failed')
...@@ -114,6 +131,94 @@ class TrackFailedRetry extends Command ...@@ -114,6 +131,94 @@ class TrackFailedRetry extends Command
$this->customLog('info', "重试完成!总计处理: {$totalProcessed} | 成功: {$totalSuccess} | 失败: {$totalFailed}"); $this->customLog('info', "重试完成!总计处理: {$totalProcessed} | 成功: {$totalSuccess} | 失败: {$totalFailed}");
} }
/**
* 过滤已经入库的埋点,仅重试未入库数据
*/
protected function filterRetryPayload($payload)
{
$trackList = isset($payload['data']) && is_array($payload['data']) ? $payload['data'] : [];
$clientIp = $payload['ip'] ?? '';
$jobTimestamp = isset($payload['timestamp']) ? (int)$payload['timestamp'] : time();
$pendingList = [];
$seen = [];
foreach ($trackList as $trackItem) {
$trackRow = $this->buildTrackRow($trackItem, $clientIp, $jobTimestamp);
$dedupKey = $this->buildTrackDedupKey($trackRow);
if (isset($seen[$dedupKey])) {
continue;
}
$seen[$dedupKey] = true;
if ($this->trackExists($trackRow)) {
continue;
}
$pendingList[] = $trackItem;
}
$payload['data'] = $pendingList;
return $payload;
}
/**
* 构建埋点行数据
*/
protected function buildTrackRow($trackItem, $clientIp, $jobTimestamp)
{
return [
'user_id' => $trackItem['user_id'] ?? 0,
'openid' => $trackItem['openid'] ?? '',
'session_id' => $trackItem['session_id'] ?? '',
'event_type' => $trackItem['event_type'] ?? '',
'event_code' => $trackItem['event_code'] ?? '',
'page_path' => $trackItem['page_path'] ?? '',
'page_title' => $trackItem['page_title'] ?? '',
'params' => !empty($trackItem['params']) ? json_encode($trackItem['params']) : '',
'ip' => $clientIp,
'create_time' => isset($trackItem['create_time']) ? (int)($trackItem['create_time'] / 1000) : $jobTimestamp
];
}
/**
* 构建埋点判重键
*/
protected function buildTrackDedupKey($trackRow)
{
return md5(implode('|', [
(string)$trackRow['user_id'],
(string)$trackRow['openid'],
(string)$trackRow['session_id'],
(string)$trackRow['event_type'],
(string)$trackRow['event_code'],
(string)$trackRow['page_path'],
(string)$trackRow['page_title'],
(string)$trackRow['params'],
(string)$trackRow['ip'],
(string)$trackRow['create_time'],
]));
}
/**
* 判断埋点是否已存在
*/
protected function trackExists($trackRow)
{
return Db::name('shopro_miniprogram_track')
->where('user_id', $trackRow['user_id'])
->where('openid', $trackRow['openid'])
->where('session_id', $trackRow['session_id'])
->where('event_type', $trackRow['event_type'])
->where('event_code', $trackRow['event_code'])
->where('page_path', $trackRow['page_path'])
->where('page_title', $trackRow['page_title'])
->where('params', $trackRow['params'])
->where('ip', $trackRow['ip'])
->where('create_time', $trackRow['create_time'])
->find() ? true : false;
}
} }
?> ?>
\ No newline at end of file
...@@ -54,7 +54,9 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -54,7 +54,9 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
}, function (ret, res) { }) }, function (ret, res) { })
} }
function initChartTotal(key) { function initChartTotal(key) {
var myChart = echarts.init(document.getElementById(`${key}Total`)); var chartEl = document.getElementById(`${key}Total`);
if (!chartEl) return;
var myChart = echarts.init(chartEl);
window.onresize = () => { window.onresize = () => {
myChart.resize() myChart.resize()
} }
...@@ -370,14 +372,15 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -370,14 +372,15 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
} }
drawX(interval, kld); drawX(interval, kld);
console.log(chartOption, 'chartOption') console.log(chartOption, 'chartOption')
var myChart2 = echarts.init(document.getElementById(`chartContent`)); var chartContentEl = document.getElementById(`chartContent`);
window.onresize = () => { if (chartContentEl) {
myChart2.resize() var myChart2 = echarts.init(chartContentEl);
window.onresize = () => {
myChart2.resize()
}
myChart2.setOption(chartOption);
} }
myChart2.setOption(chartOption);
} else { } else {
chartOption.xAxis.data = []; chartOption.xAxis.data = [];
chartOption.series[0].data = []; chartOption.series[0].data = [];
...@@ -495,13 +498,15 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -495,13 +498,15 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
}); });
}) })
var myChart3 = echarts.init(document.getElementById(`rankingContent`)); var rankingContentEl = document.getElementById(`rankingContent`);
window.onresize = () => { if (rankingContentEl) {
myChart3.resize() var myChart3 = echarts.init(rankingContentEl);
window.onresize = () => {
myChart3.resize()
}
myChart3.setOption(pieOption);
} }
myChart3.setOption(pieOption);
return false return false
}, function (ret, res) { }) }, function (ret, res) { })
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment