码桶

发现社区成员的开源项目

geekssl /

wstats

公开
main
README.md

网站统计系统(WebStats)

自建网站统计平台:原生 PHP + MySQL + Redis 后端,React + Semi Design 管理端,零依赖 JS SDK 前端采集。支持多站点、多用户(注册登录 + 文件验证)、多语言(中/EN)。

当前交付:概览看板(PV/UV/IP/跳出率/平均时长/活跃趋势/今日实时分时)、来源分析(渠道构成/趋势/外链/UTM 系列/广告平台)、
会话列表 + 单会话完整行为日志、注册登录、多站点管理 + 两步文件验证、SDK 安装引导(已验证站点不可修改,仅验证后可用 SDK)。


1. 架构总览

┌──────────── 被统计站点 ────────────┐      ┌────────────── WebStats 服务端 ──────────────┐
│ <script src="…/sdk/wstat.js"      │      │  入口:/collect.php(采集)                  │
│        data-site-key="xxx">       │  ──► │      ├ 站点校验(Redis缓存)                  │
│  · Cookie: uid(2y)/sid(30min滑动) │      │      ├ Redis 实时计数(PV/在线/分时)         │
│  · UTM 五参 + ClickID 首触留存     │      │      └ 事件 JSON → Redis 队列                │
│  · Web Vitals(采集)                │      │                    │                        │
│  · SPA 路由自动上报 / sendBeacon   │      │  常驻 worker(批量100/条) ──► events 明细表   │
└────────────────────────────────────┘      │        · 会话聚合(Redis ssn hash)          │
                                            │        · 空闲30min → sessions 表           │
                                            │ cron: session/rollup/partition/clean       │
                                            │        ↓ site_daily 日汇总                  │
                                            │ 管理API:/api/*(token 认证,多用户多站点) │
                                            │      └── React + Semi Design 前端          │
                                            └──────────────────────────────────────────┘
高流量瓶颈对策(核心设计) 瓶颈 对策
埋点写放大 采集端只写 Redis(毫秒级);worker 每 100 行/条 SQL 批量写库,DB 峰值写量降 2 个数量级
events 表膨胀 按月 RANGE(TO_DAYS(day)) 分区,cron partition 自动预建分区,cron clean 直接 DROP 过期分区
明细读报表慢 cron rollup 每5分钟刷新 site_daily 汇总,概览读汇总表(缺失天自动回填)
高并发唯一统计 UV/IP 走事件明细精确去重(一次扫描同时取 UV 与独立 IP,口径全站统一);Redis 只承担 PV/在线/进行中会话等精确实时计数
Redis 故障 采集端自动降级直写 MySQL(不丢点,会话退化为单页近似),恢复后自动切回
Redis 无扩展 自带纯 PHP RESP 协议客户端(Support/RedisClient),无需 phpredis/Predis

2. 目录结构

发布包布局(v1.0.1 起统一,不再区分普通版 / 宝塔版):包根 = 站点目录,public/ 为网站运行目录

<包根>/
├── public/                   # ← Web 根(运行目录):只有这个目录能被访问
│   ├── index.php             # 入口 + 管理 API(未安装时自动 302 → /install/)
│   ├── collect.php           # 采集入口
│   ├── index.html  assets/   # 前端产物(SPA)
│   ├── geo/                  # 地图数据(世界 / 中国)
│   ├── sdk/wstat.js          # 对外提供的采集 SDK
│   └── install/              # 安装向导(Web 四步向导 + CLI + 自带 install.sql),装完请删除
├── app/                      # 后端代码(无 Composer 依赖)
│   ├── bootstrap.php         # 自动加载 / 全局异常 / 未安装守卫
│   ├── Http/                 # Router / Request
│   ├── Support/              # Db / RedisClient(RESP) / Rds / Auth / Util / Settings / Updater …
│   └── Controllers/          # Auth / Site / Stats / Collect / Setting / Alert / Export / Open
├── config.php                # 出厂默认配置(凭据在 data/installed.php,WSTAT_* 环境变量优先级最高)
├── scripts/                  # worker.php(常驻消费)· cron.php(定时)· doctor.php / geo-doctor.php(诊断)
│                             # · selfcheck.php(离线自检)· fetch-geo.php(更新 IP 库)
├── data/                     # 运行期数据:installed.php / install.lock / ip2region.xdb + ip2region_v6.xdb / verify/ / backup/
├── deploy/                   # Nginx / Apache 配置样例
├── docs/deploy.md            # 部署与运维手册(含排查速查表)
└── README.md  RELEASE.md  CHANGELOG.md

app/ scripts/ data/ sql/ config.php 全在 Web 根之外 → 天然不可直访,无需任何屏蔽规则
data/installed.php(含库 / Redis 凭据)也不可能被下载。

完整部署手册见 docs/deploy.md

3. 部署(安装向导)

3.1 上传与目录权限

把发布包解压到服务器(如 /www/wwwroot/webstats),站点目录 = 包根、运行目录 = /public
(宝塔:网站 → 设置 → 网站目录选包根,运行目录选 /public;自建 Nginx:root <包根>/public;),
并确保 PHP 进程可写 data/

chown -R www:www /www/wwwroot/webstats/data

环境要求:PHP ≥ 7.4(推荐 8.1/8.2,扩展 pdo_mysql/json/mbstring无需 redis 扩展)、
MySQL ≥ 5.7、Redis(可选,缺失自动降级)、Node 18+(仅自行构建前端时需要)。

3.2 安装(Web 向导 / 命令行二选一)

A. Web 向导(推荐):访问 http://你的域名/install/,四步完成 —— 环境检测 → 数据库与 Redis(自动实测连接)→ 创建管理员 → 完成页给出后续命令与站点接入代码。向导会写入 data/installed.php 并加安装锁,防止被他人重装。安装后请删除 public/install 目录。

B. 命令行(SSH / 自动化)

php public/install/cli.php --check                     # 环境自检
php public/install/cli.php \                           # 一键安装(不带参数则为交互问答)
  --db-name=webstats --db-user=webstats --db-pass='密码' --create-db \
  --redis-host=127.0.0.1 --redis-port=6379 --redis-auth='密码' \
  [email protected] --admin-pass='Admin@12345'
php public/install/cli.php --status                    # 查看安装状态
php public/install/cli.php --unlock                    # 解除安装锁(重装/迁移用)

配置优先级:config.php 默认值 ← data/installed.php(向导生成)← WSTAT_* 环境变量。
因此也支持「不写配置文件、纯环境变量部署」(容器/K8s 场景):
WSTAT_DB_HOST/WSTAT_DB_PORT/WSTAT_DB_NAME/WSTAT_DB_USER/WSTAT_DB_PASS
WSTAT_REDIS_HOST/WSTAT_REDIS_PORT/WSTAT_REDIS_AUTH/WSTAT_REDIS_DBWSTAT_DEBUG=0

3.3 启动后台进程(必须)

cd /www/wwwroot/webstats
nohup php scripts/worker.php >> data/worker.log 2>&1 &   # 常驻,建议 supervisor/宝塔守护
# crontab(在项目根目录下执行):
# * * * * *   php {path}/scripts/cron.php session      # 回收空闲会话
# 0 * * * *   php {path}/scripts/cron.php rollup       # 生成 site_daily
# 0 3 * * *   php {path}/scripts/cron.php partition    # 预建未来分区
# 0 4 * * *   php {path}/scripts/cron.php clean        # 清理过期明细

⚠️ 没有 worker:events 明细不入库、会话/停留时长/实时数据永远为空。
⚠️ 更新代码后必须重启 worker(PHP 常驻进程不会热加载);请从项目根目录启动,避免误加载其它副本的旧代码。

3.4 Nginx 站点配置(管理端)

完整样例见 deploy/nginx.conf.sample(Apache 用户见 deploy/apache.htaccess.sample):

root {包根}/public;                                # ← 关键:根目录指向包根下的 public
index index.php index.html;                        # 须含 index.php(/install/ 与 SPA 回退都靠它)

location / {                                       # SPA 前端 + 未安装 302 /install/
    try_files $uri $uri/ /index.php$is_args$args;
}
location ^~ /api/ {
    try_files $uri /index.php$is_args$args;        # 管理 API
}
# 采集入口 /collect.php 不要单独写 location:POST 会被当静态文件拦成 405,
# 交给 PHP-FPM 处理即可(宝塔站点配置里已自带 location ~ [^/]\.php(/|$) 块)。
location /sdk/ { try_files $uri =404; }            # SDK 静态直出
# 安装完成后建议:location ^~ /install/ { deny all; }

app/ scripts/ data/ sql/ config.php 都在 Web 根之外,不需要任何 deny 规则
对外提供的 SDK 就是 public/sdk/wstat.js(源码改完后重新打包即可,无需手工同步)。
站点安装代码中的 data-host 填本系统域名(如 https://stats.your.com)。
若安装了统计脚本但无数据,先跑 php scripts/doctor.php 一键诊断(常见:SDK 404、站点未验证、worker 未启动)。

events 有数据但看板为空:概览读 site_daily 汇总(缺失天自动按 events 回填,接口含 0 值行兜底校正)。
依次执行 php scripts/cron.php session rollup → 刷新概览;仍无则用 doctor 的 [4b] 数据链路快照
核对:events 的 site_id 与所选站点一致?day(站点本地日 YYYY-MM-DD)在所选区间?type='pageview'
PV 正常但 visits/跳出率/时长为 0 → sessions 表为空,需 worker 常驻 + cron session 回收(Redis 可用时会话先驻留 Redis)。

4. 使用流程(一次闭环)

  1. 注册/登录 → 2. 「站点管理」添加站点(域名仅主机名)→ 3. 文件验证:两步 —— 打开验证弹窗自动生成 wstat_verify_{token}.txt(令牌落库)→ 将该文件放到站点根目录(内容为 token)→ 点击「我已上传,立即校验」(统计服务器回源拉取比对,通过后状态变绿)→ 4. 验证通过后 SDK 按钮才可用,复制安装代码到目标站 <head> → 5. 「概览看板」看总量与趋势,「来源分析」看渠道构成/外链/UTM/广告,「会话列表」查看每次访问与逐条行为日志。

    验证后站点即锁定:不可再编辑(名称/域名/时区),如需变更删除后重建。数据采集中断排查:php scripts/doctor.php

采集端默认拒绝未验证站点的上报(HTTP 403)。本地联调需放开时:启动环境加 WSTAT_ALLOW_UNVERIFIED=1

5. API 一览

方法 路径 说明
POST /api/auth/register · login · logout 注册 / 登录 / 退出
GET /api/auth/me 当前用户
GET/PATCH /api/profile 个人中心:资料查看 / 修改昵称与界面语言(邮箱为登录凭证,只读)
POST /api/profile/password 修改密码(需当前密码;成功后吊销全部旧会话并换发新令牌)
GET /api/profile/logs 我的操作日志(登录/改资料/改密 + 管理员对本账号的操作留痕)
GET/POST /api/users 用户管理(仅管理员):分页列表(关键词/状态筛选、站点数、最近登录)/ 新建
PATCH /api/users/{id} 编辑昵称/语言/管理员标记/重置密码(重置后该用户全部会话失效)
POST /api/users/{id}/status 禁用(立即踢下线)/ 启用
DELETE /api/users/{id} 删除(不能删自己/最后一个管理员;名下有站点时拒绝)
GET /api/users/{id}/sites 查看某用户的站点(自有 owner + 被授权 editor/viewer)
GET/POST /api/sites 站点列表 / 新增
GET/PATCH/DELETE /api/sites/{id} 详情 / 修改 / 删除(已验证站点拒绝修改,需删除重建)
GET /api/sites/{id}/verify-file 生成/复用验证文件(返回文件名与令牌,不联网)
POST /api/sites/{id}/verify 远端校验(服务端回源拉取验证文件比对)
GET /api/stats/overview?site_id&start&end 概览(totals/trend/live,site_daily 快路径)
GET /api/stats/sources?site_id&start&end 来源分析(渠道/外链/UTM/广告平台)
GET /api/stats/sessions?site_id&page&size&q 会话分页
GET /api/stats/sessions/{id}?site_id 会话详情 + 行为日志
GET /api/stats/online?site_id 实时在线访客
POST /collect.php 采集上报(任意 Origin;仅已验证站点接收,未验证 403)

响应统一 {code,msg,data};认证 Authorization: Bearer {token}