feat(mod): add response
This commit is contained in:
97
README.md
97
README.md
@@ -1,15 +1,16 @@
|
||||
# Microservice Common Lib (Rust)
|
||||
|
||||
这是微服务架构的通用基础库 (`common-telemetry`),旨在统一所有服务的错误处理标准、日志格式以及分布式链路追踪。
|
||||
这是微服务架构的通用基础库 (`common-telemetry`),用于统一服务的响应结构(成功/错误)、错误处理标准、日志格式以及分布式链路追踪。
|
||||
|
||||
## ✨ 核心特性
|
||||
|
||||
* **统一错误处理 (Error)**:
|
||||
* 基于 `thiserror` 和 `anyhow` 的最佳实践。
|
||||
* **双 Token 支持**: 明确区分 `AccessTokenExpired` (20001) 和 `RefreshTokenExpired` (20002)。
|
||||
* **Axum 集成**: 实现了 `IntoResponse`,自动将错误转换为标准 JSON 格式并设置正确的 HTTP 状态码。
|
||||
* **第三方库适配**: 提供了 `sqlx`, `redis`, `validator` 的可选集成。
|
||||
* *智能转换*: 例如 `sqlx::Error::RowNotFound` 会自动转换为 **404 Not Found**,而不是 500 Database Error。
|
||||
* **统一 API 响应 (Response)**:
|
||||
* **成功响应**:`AppResponse<T>` 自动映射 HTTP 状态码(200/201/202)并返回统一 JSON 结构 `{ code, message, data?, trace_id? }`。
|
||||
* **错误响应**:`AppError` 实现 `IntoResponse`,自动映射 HTTP 状态码并返回统一 JSON 结构 `{ code, message, details?, trace_id? }`。
|
||||
* **业务码 (BizCode)**:前端可根据 `code` 做稳定分支;成功恒为 `0`。
|
||||
* **双 Token 场景**:明确区分 `AccessTokenExpired` (20001) 与 `RefreshTokenExpired` (20002)。
|
||||
* **第三方库适配**:可选集成 `sqlx` / `redis` / `validator` 的错误转换。
|
||||
* 例如:`sqlx::Error::RowNotFound` 自动转换为 **404 Not Found**。
|
||||
* **可观测性 (Telemetry)**:
|
||||
* 基于 `tracing` 生态。
|
||||
* 支持 **JSON 结构化日志** (适配 ELK/Loki)。
|
||||
@@ -92,12 +93,15 @@ git push -u origin main
|
||||
### 1. 引入依赖
|
||||
|
||||
#### 方式 A: 通过 Kellnr 引入 (如果已发布)
|
||||
在 `user-service` 的 `Cargo.toml` 中,你可以根据需要开启 `sqlx` 或 `redis` 支持:
|
||||
在 `user-service` 的 `Cargo.toml` 中,你可以按需选择“全功能”或“最小依赖”。
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
# 引入基础功能 + SQLX 支持
|
||||
common-telemetry = { version = "0.1", registry = "kellnr", features = ["with-sqlx", "with-validator"] }
|
||||
# 方案 1:默认全功能(等价于 features = ["full"])
|
||||
common-telemetry = { version = "0.1", registry = "kellnr" }
|
||||
|
||||
# 方案 2:只启用响应模型 + SQLX/Validator(更轻量)
|
||||
# common-telemetry = { version = "0.1", registry = "kellnr", default-features = false, features = ["response", "with-sqlx", "with-validator"] }
|
||||
|
||||
# 注意:你需要确保服务本身引用的 sqlx 版本与 common-telemetry 兼容
|
||||
sqlx = { version = "0.7", features = ["postgres", "runtime-tokio"] }
|
||||
@@ -139,6 +143,10 @@ async fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
**重要:环境变量优先级**
|
||||
|
||||
`telemetry::init` 会优先读取 `RUST_LOG` 环境变量;若未设置则使用 `TelemetryConfig.log_level`。
|
||||
|
||||
#### B. 错误处理与第三方库集成 (Handler)
|
||||
|
||||
由于实现了 `From<T>`,你可以直接使用 `?` 操作符,库会自动处理类型转换和 HTTP 映射。
|
||||
@@ -175,6 +183,32 @@ async fn create_user(
|
||||
}
|
||||
```
|
||||
|
||||
#### C. 统一成功响应 (可选)
|
||||
|
||||
如果你希望“成功”和“错误”都返回统一的 JSON 结构(成功包含 `code/message/data/trace_id`;错误包含 `code/message/details/trace_id`),建议让 handler 返回 `AppResponse<T>`:
|
||||
|
||||
```rust
|
||||
use common_telemetry::{AppError, AppResponse};
|
||||
|
||||
async fn get_profile() -> Result<AppResponse<String>, AppError> {
|
||||
Ok(AppResponse::ok("profile data".to_string()))
|
||||
}
|
||||
```
|
||||
|
||||
#### D. 响应体格式(前后端对齐)
|
||||
|
||||
成功响应(`AppResponse<T>`):
|
||||
|
||||
```json
|
||||
{ "code": 0, "message": "Success", "data": { "any": "payload" }, "trace_id": null }
|
||||
```
|
||||
|
||||
错误响应(`AppError`):
|
||||
|
||||
```json
|
||||
{ "code": 30001, "message": "Validation error: ...", "details": "field required", "trace_id": null }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 功能模块说明 (Feature Flags)
|
||||
@@ -184,17 +218,19 @@ async fn create_user(
|
||||
| Feature | 说明 | 包含依赖 |
|
||||
| :--- | :--- | :--- |
|
||||
| **`default`** | 默认开启全功能 | `full` |
|
||||
| **`full`** | 包含基础功能及所有第三方集成 | `error`, `telemetry`, `with-sqlx`, `with-redis`, `with-anyhow`, `with-validator` |
|
||||
| **`error`** | 仅使用基础错误处理 | `thiserror`, `axum`, `serde` |
|
||||
| **`full`** | 包含基础功能及所有第三方集成 | `response`, `telemetry`, `with-sqlx`, `with-redis`, `with-anyhow`, `with-validator` |
|
||||
| **`response`** | 统一成功/错误响应 + 错误处理 | `thiserror`, `axum`, `serde` |
|
||||
| **`telemetry`** | 仅使用日志与链路追踪 | `tracing` 全家桶 |
|
||||
| **`with-sqlx`** | 集成 `sqlx` 错误转换 | `sqlx` (自动处理 RowNotFound) |
|
||||
| **`with-redis`** | 集成 `redis` 错误转换 | `redis` |
|
||||
| **`with-validator`** | 集成 `validator` 错误转换 | `validator` |
|
||||
| **`with-anyhow`** | 集成 `anyhow` 兜底错误 | `anyhow` |
|
||||
|
||||
**示例 (只用错误 + SQLX支持):**
|
||||
兼容性说明:历史上的 `error` feature 已被重命名为 `response`,但仍保留 `error` 作为别名(`error = ["response"]`),以降低已有服务的迁移成本。
|
||||
|
||||
**示例 (只用响应 + SQLX支持):**
|
||||
```toml
|
||||
common-telemetry = { version = "0.1", default-features = false, features = ["error", "with-sqlx"] }
|
||||
common-telemetry = { version = "0.1", default-features = false, features = ["response", "with-sqlx"] }
|
||||
```
|
||||
|
||||
---
|
||||
@@ -210,16 +246,26 @@ common-telemetry = { version = "0.1", default-features = false, features = ["err
|
||||
| `10000` | `ServerError` | 500 | 服务器内部错误 | 提示“系统繁忙” |
|
||||
| `10001` | `DbError` | 500 | 数据库错误 | 提示“系统繁忙” |
|
||||
| `10002` | `CacheError` | 500 | 缓存服务错误 | 提示“系统繁忙” |
|
||||
| `10003` | `SerializationError` | 500 | 序列化/反序列化失败 | 提示“系统繁忙” |
|
||||
| `10004` | `ExternalServiceError` | 500 | 下游/第三方调用失败 | 提示“系统繁忙” |
|
||||
| `10005` | `ConfigError` | 500 | 配置加载失败 | 提示“系统繁忙” |
|
||||
| **20xxx: 认证授权** | | | | |
|
||||
| `20000` | `Unauthorized` | 401 | 未授权/签名无效 | 跳转登录 |
|
||||
| **`20001`** | **`AccessTokenExpired`** | **401** | **Access Token 过期** | **使用 Refresh Token 静默刷新** |
|
||||
| **`20002`** | **`RefreshTokenExpired`** | **401** | **Refresh Token 过期** | **强制登出,跳转登录页** |
|
||||
| `20003` | `PermissionDenied` | 403 | 权限不足 | 提示无权访问 |
|
||||
| `20004` | `AccountDisabled` | 401 | 账号禁用/锁定 | 跳转登录或提示 |
|
||||
| `20005` | `InvalidCredentials` | 401 | 账号或密码错误 | 提示重试 |
|
||||
| `20006` | `MissingHeader` | 401 | 缺少必要 Header | 提示重试 |
|
||||
| **30xxx: 客户端错误** | | | | |
|
||||
| `30000` | `BadRequest` | 400 | 请求参数通用错误 | 提示错误信息 |
|
||||
| `30001` | `ValidationError` | 400 | 表单校验失败 | 提示具体字段错误 |
|
||||
| `30002` | `ResourceNotFound` | 404 | 资源不存在 | 提示“未找到数据” |
|
||||
| `30003` | `ResourceAlreadyExists`| 409 | 资源已存在 | 提示“重复创建” |
|
||||
| `30004` | `MethodNotAllowed` | 405 | HTTP 方法不支持 | 提示“请求方式错误” |
|
||||
| **40xxx: 业务流控/状态** | | | | |
|
||||
| `40000` | `RateLimitExceeded` | 429 | 请求过于频繁 | 退避重试 |
|
||||
| `40001` | `PreconditionFailed` | 400 | 业务前置条件不满足 | 提示原因 |
|
||||
|
||||
---
|
||||
|
||||
@@ -230,4 +276,25 @@ common-telemetry = { version = "0.1", default-features = false, features = ["err
|
||||
```bash
|
||||
# 使用 --nocapture 查看详细步骤打印
|
||||
cargo test -- --nocapture
|
||||
```
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧩 前端 TypeScript 类型 (ApiResponse)
|
||||
|
||||
本仓库提供前后端对齐的 TypeScript 类型定义文件(非 NPM 包形式发布):
|
||||
|
||||
- 入口: [types/index.ts](file:///home/shay/project/backend/common-telemetry/types/index.ts)
|
||||
- 定义: [types/api-response.ts](file:///home/shay/project/backend/common-telemetry/types/api-response.ts)
|
||||
- 便于引用的单文件 re-export: [types.ts](file:///home/shay/project/backend/common-telemetry/types.ts)
|
||||
|
||||
前端可直接在代码库中引用(例如作为 git 子模块/直接拷贝),并使用:
|
||||
|
||||
```ts
|
||||
import { ApiResponse, isSuccessResponse } from "./path/to/common-telemetry/types";
|
||||
|
||||
function handle<T>(res: ApiResponse<T>) {
|
||||
if (isSuccessResponse(res)) return res.data;
|
||||
throw new Error(res.message);
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user