埋点分析系统优化方案
文档用途:可直接作为 AI 编码助手的 Prompt 输入,按优先级逐项实现。
数据截止:2026-07-31
基准对比:ClkLog 埋点分析系统(社区版/PRO版/CDP版)
一、项目上下文
1.1 技术栈
| 层 | 技术 |
|---|---|
| 后端 | Python 3.10 + Flask 3.0 + Gunicorn |
| 数据库 | ClickHouse(TCP 9000 直连,clickhouse-driver 0.2.6) |
| 前端 | React 18 + TypeScript + Vite + Chart.js 4.x + react-chartjs-2 |
| 部署 | Docker Compose |
1.2 数据模型
数据库 clklog,表 log_analysis(MergeTree 引擎),约 291 万行,遵循神策数据模型。
核心字段(用于优化任务):
-- 标识字段
distinct_id String -- 用户唯一标识
event_session_id String -- 会话ID
project_name String -- 项目名
-- 时间字段
stat_date Date -- 统计日期(分区键 PARTITION BY stat_date)
log_time DateTime -- 事件发生时间
-- 事件字段
event String -- 事件名称($AppViewScreen, $AppClick, $SignUp 等)
typeContext String -- 事件类型上下文(track, track_signup)
lib String -- SDK平台(Android, iOS, Web)
lib_method String -- 采集方式(autoTrack, code)
-- 页面/元素字段
screen_name String -- 页面标识
title String -- 页面标题
element_name String -- 元素名称
element_content String -- 元素内容
element_type String -- 元素类型
element_position String -- 元素位置
-- 用户属性
is_first_day String -- 是否首日('true'/'false')
is_first_time String -- 是否首次触发
is_logined String -- 是否已登录
-- 设备/环境
os String -- 操作系统
os_version String -- OS版本
model String -- 设备型号
brand String -- 品牌
network_type String -- 网络类型(WIFI等)
-- 地域
province String -- 省份
city String -- 城市
country String -- 国家
-- 来源/渠道
latest_traffic_source_type String -- 流量来源类型
latest_referrer String -- 来源页面
latest_search_keyword String -- 搜索关键词
url / url_path String -- 页面URL
first_channel_name String -- 首次渠道名称
utm_source / utm_medium String -- UTM参数
-- 事件时长
event_duration Float64 -- 事件持续时间
1.3 现有 API 架构
Blueprint 注册(app.py):
auth_bp → /api/auth (登录/登出/用户信息)
overview_bp → /api/overview (KPI/趋势/事件类型/TOP事件/设备/地域)
events_bp → /api/events (分布/趋势对比/列表/类型枚举)
users_bp → /api/users (KPI/趋势/设备/来源/OS/网络/活跃用户)
screens_bp → /api/screens (流转/列表/入口/出口)
flow_bp → /api/flow (流量概览/新老访客/搜索词/来源网站)
elements_bp → /api/elements (KPI/热门内容/类型分布/热力图/趋势)
detail_bp → /api/detail (明细查询/CSV导出/筛选项)
请求处理链:
before_request → 从 X-Env Header 获取环境 → 切换 ClickHouse 连接
@cached(ttl=300) 装饰器 → 5分钟内存缓存
get_project_filter_params() → 注入 project_name IN (...) 过滤
ClickHouse 参数化查询 (%(param)s 占位符)
1.4 现有前端架构
路由:/login, /, /events, /elements, /users, /screens, /detail
组件:Card, Empty, Header, KpiCard, Loading, ProjectSelector, Sidebar, Toast
图表:Chart.js (Line, Bar, Doughnut, Pie),配置在 config/chart.ts
API:api/index.ts 按模块分组(authApi, flowApi, overviewApi, eventsApi, usersApi, screensApi, detailApi, elementsApi)
二、P0 优化任务(最高优先级)
2.1 留存分析(Retention Analysis)
目标:补齐 users/kpi 中已预留但返回 None 的留存率,新增留存矩阵和留存曲线。
2.1.1 后端实现
新增文件:backend/api/retention.py
# 核心 SQL 逻辑
# 1. 次日/7日/30日留存率(基于 is_first_day 标识)
# 思路:找出某日的新增用户,计算他们在 N 日后仍活跃的比例
# 次日留存率
SELECT
first_day,
count(DISTINCT first_day_users) as new_users,
count(DISTINCT retained_users) as day1_retained,
round(count(DISTINCT retained_users) / count(DISTINCT first_day_users) * 100, 2) as day1_rate
FROM (
SELECT
a.stat_date as first_day,
a.distinct_id as first_day_users,
b.distinct_id as retained_users
FROM (
SELECT DISTINCT stat_date, distinct_id
FROM clklog.log_analysis
WHERE is_first_day = 'true'
AND stat_date BETWEEN %(start_date)s AND %(end_date)s
{project_filter}
) a
LEFT JOIN (
SELECT DISTINCT stat_date, distinct_id
FROM clklog.log_analysis
WHERE stat_date BETWEEN %(start_date)s AND %(end_date_plus_n)s
{project_filter}
) b ON a.distinct_id = b.distinct_id
AND b.stat_date = a.stat_date + INTERVAL %(n_days)s DAY
)
GROUP BY first_day
ORDER BY first_day
新增端点:/api/retention/
| 端点 | 方法 | 功能 |
|---|---|---|
/api/retention/kpi | GET | 次日/7日/30日留存率汇总 |
/api/retention/matrix | GET | 留存矩阵(行=首日,列=第N天) |
/api/retention/curve | GET | 留存曲线(按首日分组的N日留存率趋势) |
参数:startdate, enddate, projects[]
修改文件:backend/app.py — 注册 retention_bp
2.1.2 前端实现
新增页面:frontend/src/pages/Retention.tsx
需求规格:
- 留存率 KPI 卡片:次日留存率、7日留存率、30日留存率,带环比趋势
- 留存矩阵表格:行=首日日期,列=Day1/Day3/Day7/Day14/Day30,单元格=留存率%,颜色渐变(绿→黄→红)
- 留存曲线图:折线图,X轴=第N天,Y轴=留存率,多条线=不同首日群组
- 时间范围筛选:复用全局
startDate/endDate
路由:/retention,导航名:"留存分析",图标 TrendingUp
修改文件:
App.tsx— 添加路由和懒加载Sidebar.tsx— 添加导航项api/index.ts— 添加retentionApi
组件:
RetentionMatrix.tsx— 留存矩阵表格(颜色渐变单元格)- KPI 卡片复用
KpiCard
2.2 漏斗分析(Funnel Analysis)
目标:实现自定义多步骤转化漏斗,计算各步骤转化率与流失率。
2.2.1 后端实现
新增文件:backend/api/funnel.py
# 核心 SQL 逻辑
# 漏斗分析:基于 event_session_id 内的事件序列,计算窗口漏斗
# 思路:在同一 session 中按时间顺序匹配步骤事件
# 示例:注册→登录→浏览→下单 漏斗
WITH step1 AS (
SELECT DISTINCT event_session_id, distinct_id, log_time
FROM clklog.log_analysis
WHERE event = %(step1_event)s
AND stat_date BETWEEN %(start_date)s AND %(end_date)s
{project_filter}
),
step2 AS (
SELECT DISTINCT a.event_session_id
FROM step1 a
INNER JOIN clklog.log_analysis b
ON a.event_session_id = b.event_session_id
AND b.event = %(step2_event)s
AND b.log_time > a.log_time
AND b.log_time <= a.log_time + INTERVAL %(window_hours)s HOUR
AND b.stat_date BETWEEN %(start_date)s AND %(end_date)s
{project_filter}
),
step3 AS (
-- 类似 step2,与 step2 的结果关联
...
)
SELECT
%(step1_name)s as step,
(SELECT count(DISTINCT event_session_id) FROM step1) as users,
(SELECT count(DISTINCT event_session_id) FROM step2) as step2_users,
...
新增端点:/api/funnel/
| 端点 | 方法 | 功能 |
|---|---|---|
/api/funnel/analyze | POST | 执行漏斗分析(接收步骤配置JSON) |
/api/funnel/templates | GET | 预置漏斗模板列表 |
/api/funnel/events | GET | 可用事件列表(供步骤配置下拉选择) |
请求体(POST /api/funnel/analyze):
{
"start_date": "2026-07-01",
"end_date": "2026-07-31",
"projects": ["qianlongapp"],
"window_hours": 24,
"steps": [
{"event": "$SignUp", "name": "注册"},
{"event": "$AppViewScreen", "name": "浏览首页"},
{"event": "$AppClick", "name": "点击元素"}
]
}
修改文件:backend/app.py — 注册 funnel_bp
2.2.2 前端实现
新增页面:frontend/src/pages/Funnel.tsx
需求规格:
- 漏斗配置区:
- 步骤列表(可增删,最少2步,最多10步)
- 每步:事件下拉选择 + 步骤名称输入
- 转化窗口设置(小时数,默认24)
- "分析"按钮 + 重置按钮
- 预置模板快捷选择(注册转化、购买转化、内容浏览等)
- 漏斗可视化(核心):
- 漏斗图(横向条形,宽度递减,标注每步人数和转化率)
- 由于 Chart.js 不原生支持漏斗图,可选方案:
- 方案A(推荐):引入 ECharts + echarts-for-react,用 ECharts 漏斗图
- 方案B:用 CSS/Canvas 自绘漏斗图
- 方案C:用横向堆叠柱状图近似(降低视觉效果)
- 漏斗数据表:步骤名称、通过人数、总体转化率、上一步转化率、流失人数
路由:/funnel,导航名:"漏斗分析",图标 Filter
修改文件:
App.tsx— 添加路由和懒加载Sidebar.tsx— 添加导航项api/index.ts— 添加funnelApi
组件:
FunnelConfig.tsx— 漏斗步骤配置区FunnelChart.tsx— 漏斗图组件FunnelTable.tsx— 漏斗数据明细表
2.3 用户行为序列(User Behavior Timeline)
目标:查看单个用户的完整行为时间线,还原"某用户何时做了什么"。
2.3.1 后端实现
新增端点:在 backend/api/users.py 中添加
| 端点 | 方法 | 功能 |
|---|---|---|
/api/users/behavior | GET | 用户行为序列(按时间排序的事件列表) |
/api/users/sessions | GET | 用户会话列表(按会话分组) |
/api/users/search | GET | 用户搜索(按 distinct_id 模糊搜索) |
参数:
distinct_id(必填)startdate,enddatesession_id(可选,筛选特定会话)event_type(可选,筛选特定事件类型)page,page_size
-- 行为序列查询
SELECT
log_time,
event,
screen_name,
title,
element_name,
element_content,
element_type,
event_duration,
event_session_id,
os,
model,
network_type,
province,
city
FROM clklog.log_analysis
WHERE distinct_id = %(distinct_id)s
AND stat_date BETWEEN %(start_date)s AND %(end_date)s
{project_filter}
{event_filter}
{session_filter}
ORDER BY log_time DESC
LIMIT %(limit)s OFFSET %(offset)s
-- 会话列表
SELECT
event_session_id,
min(log_time) as session_start,
max(log_time) as session_end,
dateDiff('second', min(log_time), max(log_time)) as duration_seconds,
count() as event_count,
count(DISTINCT screen_name) as page_count
FROM clklog.log_analysis
WHERE distinct_id = %(distinct_id)s
AND stat_date BETWEEN %(start_date)s AND %(end_date)s
{project_filter}
GROUP BY event_session_id
ORDER BY session_start DESC
2.3.2 前端实现
新增页面:frontend/src/pages/UserDetail.tsx
需求规格:
- 用户搜索入口:在
Users.tsx的 Top 活跃用户表格中,每行增加"查看详情"按钮 - 用户详情页:
- 用户概览卡片:distinct_id、首次访问日期、最近访问日期、总事件数、总会话数、设备/OS/地域
- 会话列表:按时间倒序的会话卡片,每张卡片显示:起始时间、时长、事件数、页面数
- 行为时间线(核心):
- 时间线布局(垂直时间轴)
- 每条记录:时间戳、事件图标(页面浏览=📄/点击=👆/注册=👤)、事件名称、页面名称、元素名称、停留时长
- 按会话分组,会话间用分隔线
- 支持事件类型筛选(全部/页面浏览/点击/注册)
- 点击某条记录可展开查看完整属性(JSON格式)
路由:/users/:distinctId,导航项在用户分析页内(非顶级导航)
修改文件:
App.tsx— 添加路由Users.tsx— 表格添加"查看详情"按钮api/index.ts— 添加userDetailApi
组件:
UserOverview.tsx— 用户概览卡片SessionList.tsx— 会话列表BehaviorTimeline.tsx— 行为时间线
三、P1 优化任务(重要优先级)
3.1 用户忠诚度分析
目标:分析用户访问深度、访问频次、访问时长分布。
新增端点(在 backend/api/users.py 中):
| 端点 | 方法 | 功能 |
|---|---|---|
/api/users/loyalty | GET | 忠诚度分析(访问页数/深度/时长/频次区间分布) |
SQL 思路:
-- 访问页数区间分布
SELECT
multiIf(
page_count <= 1, '1页',
page_count <= 3, '2-3页',
page_count <= 5, '4-5页',
page_count <= 10, '6-10页',
'10页以上'
) as page_range,
count(DISTINCT distinct_id) as user_count
FROM (
SELECT distinct_id, event_session_id,
count(DISTINCT screen_name) as page_count
FROM clklog.log_analysis
WHERE stat_date BETWEEN %(start_date)s AND %(end_date)s
{project_filter}
GROUP BY distinct_id, event_session_id
)
GROUP BY page_range
ORDER BY
case page_range
when '1页' then 1 when '2-3页' then 2
when '4-5页' then 3 when '6-10页' then 4
else 5
end
类似地实现:访问深度(事件数)区间、访问时长区间、访问频次区间、上次访问时间区间。
前端:在 Users.tsx 用户分析页新增"忠诚度分析"Tab/区块,用柱状图展示各区间分布。
3.2 流失/回流/沉默用户分析
目标:分析用户流失、回流、沉默趋势。
新增端点(在 backend/api/users.py 中):
| 端点 | 方法 | 功能 |
|---|---|---|
/api/users/churn | GET | 流失用户分析(N天未访问的用户趋势) |
/api/users/return | GET | 回流用户分析(流失后又回来的用户) |
/api/users/silent | GET | 沉默用户分析(仅访问1次后再无访问) |
SQL 思路:
-- 流失用户:最近 N 天未活跃的用户
-- 先找出所有历史活跃用户,再排除最近 N 天活跃的用户
SELECT stat_date, count(DISTINCT distinct_id) as churned_users
FROM (
SELECT DISTINCT distinct_id
FROM clklog.log_analysis
WHERE stat_date BETWEEN %(history_start)s AND %(history_end)s
{project_filter}
) all_users
WHERE distinct_id NOT IN (
SELECT DISTINCT distinct_id
FROM clklog.log_analysis
WHERE stat_date BETWEEN %(recent_start)s AND %(recent_end)s
{project_filter}
)
前端:在 Users.tsx 新增"流失/回流/沉默"Tab,折线图展示趋势。
3.3 活跃用户 WAU/MAU
目标:在现有 DAU 基础上增加周活跃(WAU)和月活跃(MAU)。
修改端点:/api/users/kpi — 返回值增加 wau 和 mau 字段
-- WAU: 过去7天活跃用户
SELECT count(DISTINCT distinct_id) as wau
FROM clklog.log_analysis
WHERE stat_date >= %(start_date)s - INTERVAL 7 DAY
AND stat_date <= %(end_date)s
{project_filter}
-- MAU: 过去30天活跃用户
SELECT count(DISTINCT distinct_id) as mau
FROM clklog.log_analysis
WHERE stat_date >= %(start_date)s - INTERVAL 30 DAY
AND stat_date <= %(end_date)s
{project_filter}
前端:Users.tsx 的 KPI 卡片增加 WAU 和 MAU 两栏。
3.4 App 崩溃分析
目标:统计 App 崩溃率、查看崩溃日志。
新增端点:/api/crash/
| 端点 | 方法 | 功能 |
|---|---|---|
/api/crash/overview | GET | 崩溃率、崩溃次数、影响用户数、崩溃趋势 |
/api/crash/list | GET | 崩溃日志列表(分页) |
/api/crash/detail | GET | 单条崩溃详情 |
前提:数据表中需存在 AppCrash 事件(神策 SDK 自动采集)。如果 event = 'AppCrash' 的数据存在,直接查询即可。
-- 崩溃率
SELECT
stat_date,
countIf(event = 'AppCrash') as crash_count,
count(DISTINCT if(event = 'AppCrash', distinct_id, null)) as crash_users,
count(DISTINCT distinct_id) as total_users,
round(crash_users / total_users * 100, 2) as crash_rate
FROM clklog.log_analysis
WHERE stat_date BETWEEN %(start_date)s AND %(end_date)s
{project_filter}
GROUP BY stat_date
前端:新增页面 /crash,导航"崩溃分析",图标 AlertTriangle。
3.5 渠道分析
目标:分析各渠道的流量指标。
新增端点:/api/channel/
| 端点 | 方法 | 功能 |
|---|---|---|
/api/channel/overview | GET | 渠道总览(各渠道 PV/UV/新用户/转化) |
/api/channel/trend | GET | 渠道趋势对比 |
SQL 思路:
SELECT
first_channel_name as channel,
count() as pv,
count(DISTINCT distinct_id) as uv,
count(DISTINCT if(is_first_day = 'true', distinct_id, null)) as new_users,
round(avg(event_duration), 2) as avg_duration
FROM clklog.log_analysis
WHERE stat_date BETWEEN %(start_date)s AND %(end_date)s
AND first_channel_name != ''
{project_filter}
GROUP BY channel
ORDER BY pv DESC
LIMIT 20
前端:在 Users.tsx 或概览页新增"渠道分析"区块。
3.6 用户画像详情
目标:在后台管理端查看用户详细信息。
新增端点:/api/users/profile/
-- 用户画像:汇总该用户的所有关键信息
SELECT
distinct_id,
min(log_time) as first_seen,
max(log_time) as last_seen,
count() as total_events,
count(DISTINCT stat_date) as active_days,
count(DISTINCT event_session_id) as total_sessions,
count(DISTINCT screen_name) as total_pages,
any(os) as os,
any(os_version) as os_version,
any(model) as model,
any(brand) as brand,
any(province) as province,
any(city) as city,
any(network_type) as network_type,
any(first_channel_name) as first_channel,
any(is_logined) as is_logined
FROM clklog.log_analysis
WHERE distinct_id = %(distinct_id)s
{project_filter}
GROUP BY distinct_id
前端:整合到 2.3 的 UserDetail.tsx 用户详情页中。
四、P2 优化任务(增强优先级)
4.1 地图可视化
目标:将地域分布从表格升级为地图热力图。
方案:引入 ECharts + echarts-for-react + 中国地图 GeoJSON。
新增端点:/api/overview/region-map — 返回各省份 PV/UV 数据(含省份编码,用于地图匹配)
SELECT
province,
count() as pv,
count(DISTINCT distinct_id) as uv
FROM clklog.log_analysis
WHERE stat_date BETWEEN %(start_date)s AND %(end_date)s
AND province != ''
{project_filter}
GROUP BY province
前端:在概览页新增"地域热力图"组件,使用 ECharts map 类型 + visualMap。
4.2 自定义分析
目标:用户可自选指标、维度、图表类型生成分析视图。
新增端点:/api/analysis/custom
// 请求体
{
"metrics": [
{"field": "pv", "agg": "count", "alias": "页面浏览"},
{"field": "uv", "agg": "count_distinct", "alias": "访问用户"}
],
"dimensions": ["stat_date", "os"],
"filters": [
{"field": "event", "op": "=", "value": "$AppViewScreen"}
],
"start_date": "2026-07-01",
"end_date": "2026-07-31",
"projects": ["qianlongapp"],
"chart_type": "line"
}
前端:新增页面 /analysis,导航"自定义分析",图标 BarChart3。
- 左侧:指标选择区 + 维度选择区 + 筛选条件区
- 右侧:图表预览区 + 图表类型切换(折线/柱状/饼图/表格)
- 底部:保存/导出按钮
4.3 实时监控大屏
目标:基于 WebSocket 实现实时数据推送。
方案:
- 后端:Flask-SocketIO 或单独 WebSocket 服务
- 每隔 N 秒查询 ClickHouse 最近数据(利用 ClickHouse 实时写入能力)
- 前端:全屏大屏模式,自动刷新
新增端点:WebSocket /ws/realtime 推送实时 KPI。
4.4 下钻交互
目标:图表/表格支持点击下钻到明细。
方案:
- 柱状图点击某省份 → 展示该省份城市级分布
- 饼图点击某事件类型 → 展示该类型事件明细列表
- KPI 卡片点击 → 跳转到对应明细页并预设筛选条件
实现:前端各图表组件添加 onClick 事件处理,通过路由参数传递下钻条件。
4.5 数据库索引优化
目标:解决当前全表扫描问题,提升查询性能。
执行 SQL(在 ClickHouse 中执行):
-- 1. 添加数据跳过索引
ALTER TABLE clklog.log_analysis
ADD INDEX idx_event event TYPE set(100) GRANULARITY 4;
ALTER TABLE clklog.log_analysis
ADD INDEX idx_screen_name screen_name TYPE set(100) GRANULARITY 4;
ALTER TABLE clklog.log_analysis
ADD INDEX idx_province province TYPE set(50) GRANULARITY 4;
ALTER TABLE clklog.log_analysis
ADD INDEX idx_typeContext typeContext TYPE set(20) GRANULARITY 4;
ALTER TABLE clklog.log_analysis
ADD INDEX idx_os os TYPE set(20) GRANULARITY 4;
ALTER TABLE clklog.log_analysis
ADD INDEX idx_city city TYPE set(200) GRANULARITY 4;
ALTER TABLE clklog.log_analysis
ADD INDEX idx_distinct_id distinct_id TYPE bloom_filter GRANULARITY 4;
-- 2. 清理异常分区
ALTER TABLE clklog.log_analysis DROP PARTITION '2030';
ALTER TABLE clklog.log_analysis DROP PARTITION '2031';
-- ... 清理所有 2030-2052 年异常分区
-- 3. 重建表(长期方案,需停机)
-- 建议排序键改为:ORDER BY (project_name, stat_date, event, distinct_id)
五、实施路线图
Phase 1 (第1-2周) — P0 核心补齐
├── 2.1 留存分析(后端3天 + 前端2天)
├── 2.2 漏斗分析(后端5天 + 前端3天)
└── 2.3 用户行为序列(后端3天 + 前端2天)
Phase 2 (第3-4周) — P1 重要补齐
├── 3.1 用户忠诚度分析
├── 3.2 流失/回流/沉默分析
├── 3.3 WAU/MAU
├── 3.4 App崩溃分析
├── 3.5 渠道分析
└── 3.6 用户画像详情
Phase 3 (第5-8周) — P2 增强
├── 4.1 地图可视化
├── 4.2 自定义分析
├── 4.3 实时监控大屏
├── 4.4 下钻交互
└── 4.5 数据库索引优化
六、给 AI 的代码实现规范
6.1 后端规范
- 所有新 Blueprint 注册在
backend/app.py中 - 统一使用
@cached(ttl=300)装饰器做接口缓存 - 统一使用
getprojectfilter_params()注入项目过滤 - 所有 SQL 使用 ClickHouse 参数化查询
%(param)s占位符 - 所有日期参数接收 YYYY-MM-DD 格式
- 返回格式统一为
{"code": 0, "data": {...}} - 在
backend/api/init.py中导出新 Blueprint
6.2 前端规范
- 新页面使用
React.lazy()+Suspense懒加载 - 新路由在
App.tsx中注册 - 新 API 在
api/index.ts中按模块添加 - 新图表使用
react-chartjs-2(P2 前),P2 后引入 ECharts - 新类型在
types/api.ts中定义 - 新导航项在
Sidebar.tsx中添加 - 图标统一使用
lucide-react - 组件命名:PascalCase,文件名与组件名一致
- 样式:复用
index.css中的 CSS 变量体系
6.3 命名规范
| 概念 | 后端端点 | 前端路由 | 前端页面组件 |
|---|---|---|---|
| 留存分析 | /api/retention/* | /retention | Retention.tsx |
| 漏斗分析 | /api/funnel/* | /funnel | Funnel.tsx |
| 用户行为 | /api/users/behavior | /users/:id | UserDetail.tsx |
| 崩溃分析 | /api/crash/* | /crash | Crash.tsx |
| 自定义分析 | /api/analysis/* | /analysis | Analysis.tsx |