Files
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

143 lines
4.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ck-rs — Checkpoint 用户状态追踪服务
基于 [Axum](https://github.com/tokio-rs/axum) 的 RESTful API 服务,用于记录用户在不同状态下的持续时间。客户端定时上报检查点(checkpoint),服务端自动计算各状态累计时长。类比手机屏幕使用时间统计。
## 快速开始
```bash
# 启动服务(默认监听 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` 等 |
### 请求示例
```bash
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` 中的注释:
```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`
## 技术栈
- [Axum 0.8](https://crates.io/crates/axum) — Web 框架
- [Tokio](https://crates.io/crates/tokio) — 异步运行时
- [Serde](https://crates.io/crates/serde) — 序列化 / 反序列化
- [Chrono](https://crates.io/crates/chrono) — 日期时间处理
- [Dotenvy](https://crates.io/crates/dotenvy) — .env 文件加载
## License
MIT