埋点分析系统 - 开发记忆文档

📑 目录
  1. 一、系统架构概览
  2. 二、已实现功能清单
  3. 三、关键配置与规范
  4. 四、已解决的疑难问题
  5. 五、启动与构建
  6. 六、数据库表结构要点
  7. 七、后续优化建议(未实施)
  8. 八、修改注意事项
  9. 九、近期变更记录(2026-07-30)
  10. 十、近期变更记录(2026-07-31)
  11. 十一、新增注意事项(2026-07-31)

埋点分析系统 - 开发记忆文档

本文档记录埋点分析系统的完整架构、已实现功能、关键文件、已知问题和开发规范,供后续优化升级时参考,避免破坏已完成的功能。


一、系统架构概览

埋点分析系统/
├── backend/                    # Flask 后端
│   ├── app.py                  # 主应用入口,蓝图注册 + 全局鉴权拦截
│   ├── config.py               # 多环境配置(生产/测试)
│   ├── database.py             # ClickHouse 连接池 + 线程锁
│   ├── utils.py                # 工具函数(日期、项目过滤、响应格式)
│   └── api/                    # API 蓝图模块
│       ├── auth.py             # 登录认证(Token 管理)
│       ├── overview.py         # 概览看板
│       ├── events.py           # 事件分析
│       ├── elements.py         # 元素内容分析
│       ├── flow.py             # 流量分析(环比/同比)
│       ├── users.py            # 用户分析
│       ├── screens.py          # 页面路径分析
│       └── detail.py           # 数据明细
├── frontend/                   # React + TypeScript + Vite 前端
│   ├── src/
│   │   ├── App.tsx             # 主应用,路由 + 状态管理
│   │   ├── api/index.ts        # Axios 实例 + API 封装
│   │   ├── contexts/AuthContext.tsx  # 认证上下文
│   │   ├── components/         # 通用组件
│   │   │   ├── Sidebar.tsx     # 侧边栏(环境切换 + 项目选择 + 菜单)
│   │   │   ├── Header.tsx      # 顶栏(日期选择 + 用户菜单)
│   │   │   ├── ProjectSelector.tsx  # 项目下拉多选
│   │   │   ├── ProtectedRoute.tsx   # 路由守卫
│   │   │   ├── Card.tsx        # 卡片容器
│   │   │   ├── KpiCard.tsx     # KPI 指标卡
│   │   │   └── Loading.tsx     # 加载/空状态
│   │   ├── pages/              # 页面组件
│   │   │   ├── Login.tsx       # 登录页
│   │   │   ├── Overview.tsx    # 概览看板
│   │   │   ├── Events.tsx      # 事件分析
│   │   │   ├── ElementsContent.tsx  # 元素内容分析
│   │   │   ├── Users.tsx       # 用户分析
│   │   │   ├── Screens.tsx     # 页面路径
│   │   │   └── Detail.tsx      # 数据明细
│   │   ├── config/chart.ts     # Chart.js 配置
│   │   ├── utils/index.ts      # 工具函数
│   │   └── index.css           # 全局样式 + CSS 变量
│   └── dist/                   # 构建产物(Flask 直接 serve)
├── tools/node/                 # 便携版 Node.js v24.18.1
└── 生产优化文档.md

技术栈

  • 后端: Flask + clickhouse-driver + Flask-CORS
  • 前端: React 19 + TypeScript 6 + Vite 8 + Chart.js + lucide-react
  • 数据库: ClickHouse (TCP 9000)
  • 认证: Token-based(内存存储,12 小时 TTL)

二、已实现功能清单

1. 登录认证体系

  • 后端: api/auth.py
  • /api/auth/login POST — 登录,返回 token
  • /api/auth/info GET — 获取用户信息
  • /api/auth/logout POST — 登出
  • Token 生成: MD5(username + 随机盐),存内存字典 + 线程锁
  • 全局鉴权: app.py before_request 拦截所有 /api/*,白名单: login/health/info
  • 前端:
  • AuthContext.tsx — 全局认证状态
  • Login.tsx — 分屏式登录页
  • ProtectedRoute.tsx — 路由守卫
  • api/index.ts — 请求拦截器自动注入 Authorization: Bearer
  • 登录账号: admin / clklog(环境变量 AUTHADMINUSER / AUTHADMINPASSWORD 可覆盖)

2. 环境切换(左上角)

  • 位置: Sidebar 顶部,Logo 下方,项目选择上方
  • 后端: app.py /api/env/list/api/env/switch
  • 前端: Sidebar.tsx 按钮组切换
  • 机制: 前端 localStorage 存 X-Env header,后端 before_request 读取并设到 thread-local
  • 环境配置: config.py
  • 生产: 114.80.38.24:9000 / clklog / log_analysis
  • 测试: 192.168.20.147:9000 / clklog / log_analysis
  • 关键: 切换环境后调用 loadProjects({ resetSelection: true }) 强制刷新项目列表

3. 项目选择

  • 位置: Sidebar,环境切换下方
  • 后端: /api/projects 返回项目列表(按 event_count 降序)
  • 前端: ProjectSelector.tsx 多选下拉
  • 使用 createPortal 渲染到 document.body,避免父容器裁切
  • position: fixed + 动态计算坐标,彻底避免层叠上下文问题
  • 支持全选/取消全选/搜索
  • 关键: withProjects() 工具函数将数组转为逗号分隔字符串传给后端

4. 概览看板

  • 路由: /
  • 后端: api/overview.py + api/flow.py
  • 模块: KPI 卡片、流量概览(本期/上期/环比/同比对比表)、事件趋势、事件类型、Top 事件、设备分布、地域分布、新老访客、搜索词、来源网站
  • 前端: Overview.tsx 使用 Promise.allSettled 并行加载 9 个接口

5. 事件分析

  • 路由: /events
  • 后端: api/events.py
  • 模块: 事件分布、趋势对比、事件列表(分页+搜索)

6. 元素内容分析 ★新增

  • 路由: /elements
  • 后端: api/elements.py
  • 接口:
  • /api/elements/kpi — 4 大指标(总点击/独立元素/用户数/页面数)
  • /api/elements/top-contents — 元素内容排行(分页+搜索)
  • /api/elements/types — 元素类型分布
  • /api/elements/screen-heatmap — 各页面元素热区
  • /api/elements/trend — 点击趋势
  • 前端: ElementsContent.tsx
  • 数据字段: elementcontent(元素文本,如"返回"、"全部A股")、elementtype(如 UIButton、button)、elementname(通常为空,用 elementcontent 代替)

7. 用户分析

8. 页面路径分析

  • 路由: /screens
  • 后端: api/screens.py
  • 注意: 使用 elementcontentelementtype 替代空的 element_name

9. 数据明细


三、关键配置与规范

数据库连接

  • 协议: clickhouse-driver 仅支持 TCP (端口 9000),不支持 HTTP (8123)
  • 连接池: database.py 5 分钟自动刷新
  • 线程锁: threading.Lock() 防止并发查询报错
  • 密码: 环境变量 CLICKHOUSE_PASSWORD,生产密码 Ql@clklog2026
  • Monkey-patch: Connection.disconnect 可能抛 AttributeError(socket 为 None),已打补丁

后端 API 规范

  • 所有 SQL 使用参数化查询 %(param)s,禁止 f-string 拼接用户输入
  • LIMIT/OFFSET 可直接插入 SQL(老版本 clickhouse-driver 不支持参数化)
  • 日期/项目过滤: 使用 getdaterange() + getprojectfilter_params() 装饰器
  • 响应格式: successresponse(data) / errorresponse(msg, code)
  • CORS: 限制为 http://localhost:3000(环境变量 CORS_ORIGINS 可覆盖)
  • Flask debug 模式必须关闭(防止多进程监听同端口)

前端规范

  • 构建: npm run build(tsc + vite build),需要 Node.js 18+
  • 便携 Node: tools/node/node-v24.18.1-win-x64/(系统未装 Node.js 时的备用方案)
  • 构建命令: $env:Path = 'E:\AI工作台\埋点分析系统\tools\node\node-v24.18.1-win-x64;' + $env:Path; npm run build
  • 类型导入: verbatimModuleSyntax 开启,类型必须用 import type
  • Chart.js: drawBorder 已废弃,改用 border: { display: false }
  • 多接口并行: 使用 Promise.allSettled 而非 Promise.all
  • 全局状态消除: selectedProjects 通过 props 传递,不用模块级变量
  • KpiCard icon: 传 JSX 节点 ,不能传组件引用 Icon

CSS 关键约束

  • .glass-card:hover 不可使用 transform(会创建层叠上下文,破坏下拉菜单)
  • .app-sidebar 需要 z-index: 50 + overflow: visible(backdrop-filter 会创建层叠上下文)
  • 环境切换和项目选择容器需要 shrink-0(防止 flex 压缩)
  • ProjectSelector 下拉使用 createPortal + position: fixed(彻底避免裁切)

四、已解决的疑难问题

问题根因解决方案
环境切换/项目选择不可用(第1次).glass-card:hovertransform: translateY(-1px) 创建层叠上下文移除 transform
环境切换/项目选择不可用(第2次)backdrop-filter 创建层叠上下文 + 下拉被父容器裁切sidebar 加 z-index:50 + overflow:visible;下拉用 Portal+fixed
切换环境后项目不刷新setSelectedProjects 异步,loadProjects 读到旧 state改用 loadProjects({ resetSelection: true })
端口被旧进程占用Flask debug 模式启动多进程关闭 debug,手动 kill 旧进程
ClickHouse 连接失败 (516)缺少密码环境变量设置 CLICKHOUSE_PASSWORD
ClickHouse 协议错误用 HTTP 8123 连 TCP-only 驱动改用 9000 端口
element_name 字段为空手机端不采集此字段elementcontent + elementtype 替代
LAG 窗口函数不支持ClickHouse 老版本改用 groupArray + 数组函数
LIMIT 参数化失败老版本 clickhouse-driver直接插入 SQL 字符串
CSV 逗号未转义无 escape 函数新增 csv_escape
N+1 查询每个事件类型单独查询合并为 GROUP BY stat_date, event
TypeScript JSX 报错缺少 @types/react + tsconfig 无 jsx安装类型包 + 配置 "jsx": "react-jsx"
Top 活跃用户无数据生产库 eventsessionid 全部为空,AND eventsessionid != '' 过滤掉所有数据移除 session 过滤,改按 distinct_id 分组,单独查询 model 和注册日期
留存率显示 null后端 API 硬编码返回 None,未实现计算实现7日留存:7天前新增用户队列与今日活跃用户 LEFT JOIN 计算
页面路径搜索无结果SQL 别名 any(title) as title 与 WHERE 的 title LIKE 冲突,ClickHouse 报错别名改为 screen_title,避免别名冲突
页面路径搜索大小写不匹配ClickHouse LIKE 默认大小写敏感使用 lower() 函数实现大小写不敏感匹配
各页面元素热区布局混乱2列网格布局导致卡片高度不统一改为单列表格布局,4列:页面名称/总点击/元素数/热门元素Top5

五、启动与构建

启动后端

cd E:\AI工作台\埋点分析系统\backend
$env:AUTH_ADMIN_USER='admin'; $env:AUTH_ADMIN_PASSWORD='clklog'
python app.py
# 服务运行于 http://127.0.0.1:5000

构建前端

$env:Path = 'E:\AI工作台\埋点分析系统\tools\node\node-v24.18.1-win-x64;' + $env:Path
cd E:\AI工作台\埋点分析系统\frontend
npm run build
# 产物输出到 dist/,Flask 直接 serve

验证后端接口

# 登录获取 token
curl.exe -s --noproxy '127.0.0.1' -X POST 'http://127.0.0.1:5000/api/auth/login' -H 'Content-Type: application/json' -d '{"username":"admin","password":"clklog"}'

# 用 token 访问接口
curl.exe -s --noproxy '127.0.0.1' 'http://127.0.0.1:5000/api/elements/kpi?start_date=2026-07-23&end_date=2026-07-29' -H 'Authorization: Bearer <TOKEN>'

六、数据库表结构要点

: clklog.log_analysis

关键字段:

字段说明备注
stat_date分区日期用于日期过滤
log_time日志时间用于排序
project_name项目名项目过滤
event事件类型$AppClick / $AppViewScreen / $SignUp 等
distinct_id用户 ID去重计数
screen_name页面名页面分析
element_content元素文本核心字段,如"返回"、"全部A股"
element_type元素类型如 UIButton / button / QLOtherButton
element_name元素名通常为空,用 element_content 代替
eventsessionid会话 ID流量分析用(生产库全为空字符串,不可作为过滤条件)
client_ip客户端 IPIP 去重
device, os, province, city设备/系统/地域用户分析用

已知数据问题:

  • 存在 2030-2052 年的异常分区
  • 排序键为 distinct_id(不合理,与高频查询条件不匹配)
  • 缺少数据跳过索引

七、后续优化建议(未实施)

  1. 数据库索引优化: 添加 7 个数据跳过索引(event/screenname/province 用 set,city/distinctid 用 bloom_filter)
  2. 排序键优化: 改为 (projectname, statdate, event, distinct_id) 复合排序键
  3. 清理异常分区: 删除 2030 年及以后的分区
  4. 生产部署: 使用 Gunicorn WSGI 服务器,配置 Dockerfile
  5. 安全加固: 密码加密存储(当前明文),JWT 替代内存 Token
  6. 监控告警: 接入 Prometheus + Grafana

八、修改注意事项

修改任何功能前,务必阅读本节,避免破坏已有功能。

  1. 修改 Sidebar 时: 不要给 .glass-card:hovertransform;环境/项目容器必须有 shrink-0
  2. 修改 ProjectSelector 时: 下拉必须用 createPortal + position: fixed,不要改回 position: absolute
  3. 修改 API 时: 所有用户输入必须参数化,LIMIT/OFFSET 例外可直接插入
  4. 修改 app.py 时: 新蓝图必须注册;before_request 鉴权白名单要同步更新
  5. 修改 tsconfig 时: 必须有 "jsx": "react-jsx",否则所有 .tsx 报错
  6. 修改 CSS 时: .app-sidebar 必须有 z-index: 50 + overflow: visible
  7. 新增页面时: 在 App.tsx 添加 lazy import + pageTitles + Route;在 Sidebar.tsx navItems 添加菜单项
  8. 新增 API 模块时: 在 api/index.ts 添加 elementsApi 风格的封装;在 backend/api/ 创建蓝图;在 app.py 注册
  9. 构建前: 确保便携 Node.js 在 PATH 中,或系统已安装 Node.js 18+
  10. 启动前: 确保端口 5000 无旧进程占用(netstat -ano | findstr ':5000'
  11. ClickHouse SQL: 聚合函数别名不要与 WHERE 字段同名(如 any(title) as titletitle LIKE 冲突)
  12. ClickHouse LIKE: 默认大小写敏感,需用 lower() 实现不敏感匹配
  13. 搜索功能: 所有页面搜索统一为点击按钮/回车触发,使用 keywordInput/keyword 双状态分离

九、近期变更记录(2026-07-30)

9.1 元素分析页面优化

  • 菜单重命名: 「元素内容分析」→「元素分析」(Sidebar.tsx)
  • 热区布局: 各页面元素热区从 2 列网格改为单列表格 (ElementsContent.tsx)

9.2 用户分析修复

  • Top 活跃用户 (users.py):
  • 移除了 eventsessionid != '' 过滤条件(生产库该字段全空)
  • 分组维度从 distinctid, model 改为仅 distinctid(避免同用户多设备拆分)
  • model 改为单独查询取出现次数最多的值
  • 注册日期改为只查 Top 20 用户(IN 条件),不再全表扫描
  • 返回条数从 10 条改为 20 条
  • 7日留存率 (users.py):
  • 算法: 7天前新增用户中今天仍活跃的人数 / 7天前新增用户总数 × 100%
  • 队列日期 = enddate - 7天,基于 isfirst_day = 'true' 筛选
  • 上期对比: 再往前7天的队列,上期队列为0时 trend 返回 null

9.3 全局搜索功能优化

  • 4个页面统一改为点击搜索按钮/回车执行(原先输入即触发):
  • Events.tsx — 搜索事件名称
  • Screens.tsx — 搜索页面名称
  • Detail.tsx — 搜索事件/页面
  • ElementsContent.tsx — 搜索元素内容
  • 实现方式: 分离 keywordInput(输入框绑定值)和 keyword(提交后的搜索值),只有点击按钮或按回车才将 input 同步到 keyword 触发 API 请求

9.4 页面路径搜索修复

  • 文件: screens.py
  • Bug 1: SQL 别名 any(title) as title 与 WHERE 子句 title LIKE 冲突 → 改为 screen_title
  • Bug 2: ClickHouse LIKE 大小写敏感 → 使用 lower(screen_name) LIKE lower(%(keyword)s)

十、近期变更记录(2026-07-31)

10.1 用户分析顶部看板样式统一

  • 文件: Users.tsx
  • 变更: 将 grid-cols-6 的 6 卡片改为 grid-cols-4 的 4 个 KPI 卡片(累计/新增/活跃/7日留存),WAU/MAU 移至趋势卡片标题栏右侧,加载态改为骨架屏

10.2 数据明细页增加 app_name 和元素下拉筛选

  • 后端: detail.py 的 /events、/export、/filters 接口新增 appname、elementcontent 过滤参数
  • 前端: Detail.tsx 新增"全部应用""全部元素"下拉框

10.3 数据明细下拉框优化

  • /filters 所有查询改用 GROUP BY + ORDER BY count(*) DESC 降序,LIMIT 30
  • 前端下拉框统一使用 flex: 1 + minWidth: '120px' 实现等宽平均分配

10.4 留存 KPI 修复

  • 后端: retention.py
  • 新增 getdate_range 统一返回 datetime.date 类型
  • getmaxstatdate 改用 maxIf(statdate, statdate <= today()) 排除未来脏数据(最远到 2050 年)
  • KPI 改为周期内所有有效 cohort 的平均留存率(旧逻辑只用 effectiveend - N 天的单个 cohort,与 startdate 无关,导致切换统计周期时数值不变)
  • 前端: Retention.tsx 增加 formatRetention 函数将 null 显示为 '-'

10.5 项目下拉框修复

  • 文件: ProjectSelector.tsx
  • Bug: 下拉框通过 createPortal 渲染到 document.body,点击外部检测仅检查 dropdownRef,导致点击下拉项被误判为外部点击而关闭
  • 修复: 新增 portalRef 跟踪 portal 容器,handleClickOutside 同时检查 buttonRef、dropdownRef、portalRef 三个 ref,仅当点击目标不在三者内时才关闭下拉框
  • 增强: 新增确定按钮和 tempSelected 临时状态,选择后点击确定按钮主动刷新页面

10.6 修改密码功能 ★新增

  • 前端: Header.tsx 用户下拉菜单新增"修改密码"项;新增 ChangePasswordModal.tsx 组件(旧密码/新密码/确认密码,含长度和一致性校验)
  • 后端: auth.py 新增 /api/auth/change-password 接口(@login_required 保护)

10.7 系统用户管理模块 ★新增

  • 后端:
  • userstore.py — SQLite 存储,SHA-256 密码哈希,首次启动自动初始化 admin 账号
  • api/sysusers.py — CRUD/权限/角色/重置密码/启禁用,全部 @admin_required 保护
  • auth.py — 登录改为查 SQLite,兼容旧环境变量方式
  • 前端:
  • Sidebar.tsx — 新增"系统用户"菜单(仅 admin 可见)
  • SysUsers.tsx — 管理页面(搜索/新建/编辑/权限设置/重置密码/启禁用/删除)
  • 功能: 用户名(非必填)、登录名(必填,仅字母/数字/下划线)、手机号(非必填)、密码(可快捷选择默认 123456)、角色(admin/user)、项目权限、环境权限
  • 安全限制: 不能删除/禁用最后一个启用的管理员,不能对自身账号执行删除/禁用/重置密码
  • Bug 修复: status 字段是字符串 'active'/'disabled',前端不能用 number === 1 比较;非管理员直接访问 /sysusers 路由需重定向

10.8 部署打包 ★新增

  • 打包脚本: build-package.ps1 — 一键打包,排除 pycache、venv、users.db
  • 部署配置: deploy/ 目录
  • Dockerfile — 单镜像方案(前后端 dist 一体,gunicorn 启动)
  • docker-compose.yml — 编排文件(含数据卷持久化 + 健康检查)
  • .env.example — 环境变量模板(已修复端口 8123→9000)
  • scripts/start.sh / stop.sh — Linux 启动/停止脚本
  • scripts/start.bat / stop.bat — Windows 启动/停止脚本
  • DEPLOY.md — 详细部署指南
  • UPGRADE.md — 升级指南(含回滚方案)
  • 交付包: dist/tracker-analytics-v1.1.0.zip(57 个文件,0.25 MB)
  • 修复: .env.example 端口错误(8123→9000)、Dockerfile 未包含前端 dist、启动方式从 python app.py 改为 gunicorn

十一、新增注意事项(2026-07-31)

修改任何功能前,务必阅读本节,避免破坏已有功能。

  1. 系统用户管理 API: 所有 /api/sysusers/* 接口必须使用 @adminrequired 装饰器,不能用 @loginrequired
  2. sys_users 表 status 字段: 是字符串 'active'/'disabled',前端不能用 number === 1 比较
  3. createPortal 下拉框: 点击外部检测必须同时检查 portalRef,否则点击下拉项会被误判为外部点击而关闭
  4. 留存 KPI 计算: 必须按选定周期内所有有效 cohort 的平均留存率计算,不能只取结束日的单个 cohort
  5. 生产库 statdate 脏数据: 存在未来日期(最远到 2050 年),必须用 maxIf(statdate, stat_date <= today()) 排除
  6. PowerShell 脚本编码: 含中文和特殊字符(如树形图 ├ │ └)的 here-string 会因编码问题解析失败,应改用数组拼接或独立文件
  7. Docker 部署: 必须使用包含前后端的单镜像 Dockerfile,不能只打包后端(Flask 需要托管 frontend/dist)
  8. .env.example 端口: CLICKHOUSE_PORT 必须是 9000(TCP 原生协议),不是 8123(HTTP)
  9. Token 内存存储: Flask 重启后所有 token 失效,用户需重新登录