Files
ck-rs/README.md
T
Mplan 13f7c1326a feat: checkpoint user state tracking service with PostgreSQL
- 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.
2026-05-31 22:36:20 +08:00

4.6 KiB
Raw Blame History

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 睡眠
"任意字符串" 自定义状态,如 GamingMeetingDriving

请求示例

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