Files
asd-backend/AGENTS.md

194 lines
5.8 KiB
Markdown
Raw 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.
# Agent 文档 — rust-backend
## 项目概述
微信小程序天气数据采集后端。用户通过微信认证,上传天气观测数据,并通过 REST API 管理数据。
## 技术栈
- **语言**: Rust (edition 2024)
- **Web 框架**: actix-web 4.11
- **数据库**: PostgreSQL通过 sqlx 0.8.6 连接
- **认证**: JWT (HS256),使用 jsonwebtoken 9.3
- **HTTP 客户端**: reqwest用于调用微信 API
- **序列化**: serde + serde_json
- **时间处理**: chrono
## 项目结构
```
src/
├── main.rs # 入口文件,所有路由处理器,服务器配置
├── auth.rs # JWT 中间件,令牌生成/验证
├── db.rs # 数据库操作(通过 sqlx 执行原始 SQL
├── models.rs # 数据结构Claims、User、WeatherData 等)
└── config.rs # 配置结构体main.rs 中未使用 — 死代码)
migrations/
└── 001_add_payment_fields.sql # 用于添加支付字段的 ALTER TABLE
tests/
└── integration_test.rs # 基础测试框架
```
## 数据库表结构
### users 表
```sql
id INTEGER PRIMARY KEY
openid VARCHAR UNIQUE ID
name VARCHAR openid 8
type INTEGER 2
is_paid BOOLEAN DEFAULT false
is_admin BOOLEAN DEFAULT false
paid_expires_at TIMESTAMPTZ DEFAULT NULL
```
### weather_data 表
```sql
id INTEGER PRIMARY KEY
user_id INTEGER users
title, date, hour, min, longitude, latitude, ...
-- 30+ 个天气测量字段列
```
## 认证流程
1. **登录**: `POST /api/login` 传入微信 code → 调用微信 API → UPSERT 用户 → 返回 JWT
2. **JWT 声明**: `{exp, iat, user_id, openid, user_type}`24 小时过期)
3. **中间件**: `jwt_middleware` 提取 Bearer 令牌,验证后将 Claims 插入请求扩展
4. **处理器访问**: 通过 `claims: web::ReqData<Claims>` 参数获取
## API 接口
### 公开接口(无需认证)
- `POST /api/login` — 微信登录
- `GET /weather/details?temp_token=...` — 通过临时令牌访问
- `GET /static/{tail:*}` — 静态文件
### 受保护接口(需要 JWT
- `POST /api/post-weather-data` — 上传天气数据(带配额检查)
- `GET /api/user/profile` — 获取当前用户信息(付费状态、管理员标识)
- `GET /weather` — 分页列出用户的天气数据
- `GET /weather/details?id=...` — 获取天气详情JWT 认证)
- `POST /api/generate-temp-token/{resource_id}` — 生成 10 分钟分享令牌
- `DELETE /weather/delete/{id}` — 删除天气记录
### 管理员接口(需要 JWT + is_admin
- `PUT /api/admin/users/{id}/payment` — 更新用户支付状态
- `GET /api/admin/users/{id}` — 获取用户信息
## 支付系统
### 支付状态逻辑
```rust
is_paid_active = is_paid && (paid_expires_at.is_none() || paid_expires_at > Utc::now())
```
### 配额限制
- 未付费用户限制为 `FREE_USER_DATA_LIMIT` 条记录(环境变量,默认 20
- 付费用户(活跃状态)无限制
-`insert_weather_data` 执行 INSERT 前进行检查
### 管理员 API
```json
PUT /api/admin/users/{id}/payment
{
"is_paid": true,
"paid_expires_at": "2026-12-31T23:59:59Z" // 可选null 表示永久
}
```
## 代码模式
### 处理器模式
```rust
#[post("/api/endpoint")]
async fn handler(
pool: web::Data<PgPool>,
claims: web::ReqData<Claims>,
body: web::Json<RequestBody>,
) -> impl Responder {
match db::function(pool.get_ref(), claims.user_id).await {
Ok(data) => HttpResponse::Ok().json(serde_json::json!({"success": true, "data": data})),
Err(e) => HttpResponse::Ok().json(serde_json::json!({"success": false, "errcode": 500, "errmsg": e}))
}
}
```
### 数据库函数模式
```rust
pub async fn function_name(pool: &PgPool, param: i32) -> Result<Type, String> {
match sqlx::query_as::<_, Type>("SELECT ...")
.bind(param)
.fetch_optional(pool)
.await
{
Ok(Some(row)) => Ok(row),
Ok(None) => Err("Not found".to_string()),
Err(e) => Err(format!("Query failed: {}", e)),
}
}
```
### 错误响应格式
```json
{"success": false, "errcode": 403, "errmsg": "Error message"}
```
## 环境变量
```env
DATABASE_URL=postgres://user:pass@host:5432/dbname
WECHAT_APPID=wx...
WECHAT_SECRET=...
JWT_SECRET=your_secret_key
SSL_KEY_PATH=/path/to/key
SSL_CERT_PATH=/path/to/cert
RUST_LOG=info
APP_VERSION=0.2.0
FREE_USER_DATA_LIMIT=20
```
## 约束条件
### 禁止事项
- 在 JWT Claims 中添加 `is_admin`(必须查询数据库)
- 信任 JWT 中的 `is_paid` 来做配额决策(必须查询数据库)
- 使用 `as any``@ts-ignore` 或类型错误抑制
- 修改 config.rs死代码
- 添加支付网关集成
- 添加审计日志
### 必须事项
- 管理员接口必须通过数据库查询验证管理员身份
- 已认证处理器使用 `web::ReqData<Claims>`
- 遵循现有的错误响应格式
- 通过数据库查询检查 `is_paid_active`,而非 JWT
- SQL 中使用 `desc` 等保留关键字时必须加双引号:`"desc"`
## 常见任务
### 添加新接口
1.`main.rs` 中创建处理器函数
2. 如需要,在 `db.rs` 中添加数据库函数
3.`create_server_config` 中注册(如果是受保护接口,放在 JWT 作用域内)
4. 如需要,在 `models.rs` 中添加请求/响应结构体
### 修改数据库
1.`migrations/` 中创建迁移 SQL
2. 在 PostgreSQL 上手动执行迁移
3. 如果表结构变更,更新 `models.rs` 中的 `User` 结构体
4. 更新 `db.rs` 中的数据库函数
## 构建与运行
```bash
cargo build # 编译
cargo run # 启动服务器
cargo test # 运行测试
cargo clippy # 代码检查
```
服务器尝试端口顺序4433、8443、8080、3000、8000、8888