13f7c1326a
- RESTful API: POST /heartbeat, POST /checkpoints, GET /status, GET /summaries - State-change-only checkpoint model with extensible StateType enum - PostgreSQL backend with sqlx, auto-migration on startup - pg_cron scheduled aggregation (state_summaries) and offline detection - Heartbeat-based liveness with 60s timeout auto-offline - LEAD() window function for state duration calculation - JSONB content field for extensible checkpoint metadata BREAKING CHANGE: Complete rewrite from Hello World to full API service.
4.6 KiB
4.6 KiB
ck-rs — Checkpoint 用户状态追踪服务
基于 Axum 的 RESTful API 服务,用于记录用户在不同状态下的持续时间。客户端定时上报检查点(checkpoint),服务端自动计算各状态累计时长。类比手机屏幕使用时间统计。
快速开始
# 启动服务(默认监听 127.0.0.1:3000,可通过 .env 中 LISTEN_ADDR 修改)
cargo run
API 端点
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/health |
服务健康检查 |
POST |
/users/{user_id}/checkpoints |
上报当前状态(心跳) |
GET |
/users/{user_id}/checkpoints |
查询检查点历史(?from=&to=&limit=) |
GET |
/users/{user_id}/checkpoints/{id} |
查询单个检查点 |
GET |
/users/{user_id}/status |
当前状态 + 各状态累计时长 |
状态类型
| 内置状态 | 说明 |
|---|---|
Online |
在线 |
Offline |
离线 |
Idle |
空闲 |
Working |
工作中 |
Sleeping |
睡眠 |
"任意字符串" |
自定义状态,如 Gaming、Meeting、Driving 等 |
请求示例
BASE=http://localhost:3000
# 健康检查
curl $BASE/health
# 上报状态(自动记录服务端当前时间)
curl -X POST $BASE/users/alice/checkpoints \
-H "Content-Type: application/json" \
-d '{"state":"Working"}'
# 上报状态 + 附带元数据(设备、坐标等)
curl -X POST $BASE/users/alice/checkpoints \
-H "Content-Type: application/json" \
-d '{"state":"Idle","content":{"device":"MacBook","battery":85}}'
# 上报自定义状态
curl -X POST $BASE/users/alice/checkpoints \
-H "Content-Type: application/json" \
-d '{"state":"Gaming"}'
# 等待几秒后切换状态(产生时长数据)
sleep 3
curl -X POST $BASE/users/alice/checkpoints \
-H "Content-Type: application/json" \
-d '{"state":"Offline"}'
# 查询检查点历史
curl $BASE/users/alice/checkpoints
# 按时间范围查询
curl "$BASE/users/alice/checkpoints?from=1717161600&to=1717248000&limit=10"
# 查询单个检查点
curl $BASE/users/alice/checkpoints/1
# 查询状态汇总(当前状态 + 各状态累计时长)
curl $BASE/users/alice/status
# → {"user_id":"alice","current_state":"Offline","since":1717248000,
# "durations":[{"state":"Working","duration_secs":3}]}
环境变量
| 变量 | 默认值 | 说明 |
|---|---|---|
LISTEN_ADDR |
127.0.0.1:3000 |
监听地址 |
DATABASE_URL |
(无) | 数据库连接字符串(接入真实 DB 时设置) |
项目结构
src/
├── main.rs # 入口:加载配置 → 初始化 DB → 构建路由 → 启动
├── config.rs # 配置层(环境变量 + 默认值)
├── state.rs # AppState(全局共享状态,持有 DB)
├── error.rs # 统一错误类型 AppError(实现 IntoResponse)
├── router.rs # 路由组装
├── models/
│ ├── mod.rs
│ └── checkpoint.rs # StateType / Checkpoint / UserStatusResponse 等
├── handlers/
│ ├── mod.rs
│ ├── health.rs # GET /health
│ └── checkpoints.rs # POST/GET /users/{id}/checkpoints
└── db/
├── mod.rs # Db trait(数据库抽象接口)
└── memory.rs # MemoryDb(内存模拟,开发期使用)
核心设计
- 状态可自由扩充:
StateType内置 5 种状态 +Custom(String)变体,传入任意字符串自动作为新状态 - content 可扩展:每个检查点可附带
Option<serde_json::Value>元数据 - 时长自动计算:相邻检查点的时间差归属于前一个状态,
/status返回各状态累计秒数 - timestamp 可选:请求可带时间戳,不传则服务端取当前时间
接入真实数据库
当前使用内存模拟存储(MemoryDb),切换为 PostgreSQL 仅需 3 步:
1. 添加依赖
取消 Cargo.toml 中的注释:
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres"] }
2. 实现 Db trait
新建 src/db/postgres.rs,对 PgPool 实现 Db trait。
3. 修改入口
src/main.rs 中将 MemoryDb::new() 替换为 PgPool::connect(...).await。
技术栈
License
MIT