优化方法的使用

This commit is contained in:
yun
2026-09-19 01:31:34 +08:00
parent 00eba5cc75
commit 407fc49e0c
20 changed files with 1710 additions and 376 deletions
+220 -5
View File
@@ -1,6 +1,221 @@
# 功能说明
# responsex
1. 这个是API响应封装组件
2. 支持多多语言的错误码和错误提示
3. 优化了gin框架的响应体验
4. 支持gozero框架的traceid
API 响应封装组件,**按需加载**:核心包不依赖任何 Web/ORM/追踪框架,
用到哪个框架才 import 哪个适配包。
## 功能
1. API 统一响应封装(code / message / data / pagination / trace_id)
2. 支持多语言的错误码与错误提示(基于 [langx](https://github.com/yuninks/langx))
3. gin 响应体验优化(`responsex/gin`)
4. go-zero trace_id 接入(`responsex/gozerotrace`,可选)
5. GORM 分页辅助(`responsex/gormx`,可选)
## 模块与按需加载
本仓库是**多模块**结构,每个模块独立 `go.mod`,依赖互不牵扯:
| 模块 | import 路径 | 依赖 | 适用场景 |
|---|---|---|---|
| 核心 | `code.yun.ink/pkg/responsex` | langx、errorx | 任意 HTTP 框架 |
| gin 适配 | `code.yun.ink/pkg/responsex/gin` | gin、核心 | gin 项目 |
| go-zero trace | `code.yun.ink/pkg/responsex/gozerotrace` | go-zero、核心 | 需要 go-zero trace_id |
| GORM 分页 | `code.yun.ink/pkg/responsex/gormx` | gorm | 需要分页偏移 |
实测依赖隔离效果(`go list -deps`,外部包数 / 是否引入 go-zero):
| 消费场景 | 模块图 | 外部包 | go-zero | gin | gorm |
|---|---|---|---|---|---|
| 只用核心包 | 4 | 4 | ✗ | ✗ | ✗ |
| 核心 + gin | 69 | 57 | ✗ | ✓ | ✗ |
| 核心 + gin + gorm | 82 | 69 | ✗ | ✓ | ✓ |
| 核心 + gin + gozerotrace | 174 | 210 | ✓ | ✓ | ✗ |
关键点:**不 import `gozerotrace` 就不会下载、不会链接 go-zero**
(含 OpenTelemetry / gRPC / zipkin / jaeger 等一整棵依赖树)。
## 快速开始
### 纯 gin(推荐)
```go
import (
"code.yun.ink/pkg/responsex"
ginx "code.yun.ink/pkg/responsex/gin"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.New()
r.Use(ginx.Middleware()) // 桥接 trace_id 与 Accept-Language
r.GET("/ping", func(c *gin.Context) {
c.Set("trace_id", "xxx") // 有就自动出现在响应里
ginx.Success(c, gin.H{"pong": true})
})
r.GET("/fail", func(c *gin.Context) {
ginx.ErrorStr(c, "参数异常,请检查参数")
})
r.Run()
}
```
`ginx.Middleware()` 做了两件事:
1. 把 gin 的 `c.Set("trace_id", ...)`(或 `X-Request-Id` 请求头)桥接到标准 context;
没有它,`trace_id` 不会出现在响应里(gin 的 `c.Value()` 读不到 `c.Set()` 的值)。
2. 把 `Accept-Language` 写入语言 context,核心包据此翻译文案。
### 接入 go-zero 的 trace_id
```go
import _ "code.yun.ink/pkg/responsex/gozerotrace" // 只需这一行
```
导入后 go-zero 的 OpenTelemetry trace_id 会被自动读取,业务代码无需改动。
(纯 gin 项目如果自己用 `c.Set("trace_id", ...)` 注入,可以不要这个包。)
### GORM 分页
```go
import "code.yun.ink/pkg/responsex/gormx"
var list []User
gormx.GetDbOffset(db, page, size).Find(&list)
respgin.SuccessWithPage(c, list, responsex.GetPage(page, size, total))
```
### 不使用 gin 的其它框架
核心包只依赖标准库的 `context` 与 `http.ResponseWriter`:
```go
responsex.Success(r.Context(), w, data)
responsex.Error(r.Context(), w, err)
```
## 多语言
1. 错误码/文案注册(`langx` / `errorx`):
```go
var ErrParamsInvalid = errorx.NewCode("params_invalid", 40001, "参数异常")
// 或按文件注册:code.json 为 map[string]int,其它 json 文件名为语言名
langx.RegisterDir("./langs")
```
2. 输出:
```go
responsex.Error(ctx, w, ErrParamsInvalid.New(ctx)) // 自动带业务码与译文
responsex.FormatMessage(ctx, w, "welcome", map[string]string{"name": "Yun"}, data)
ginx.MsgKV(c, "welcome", "name", "Yun")
```
3. 成功文案:默认 `请求成功`,可用两种方式覆盖:
```go
// 方式一:全局/单次指定
responsex.InitOptions(responsex.SetDefaultSuccessMessage("操作成功"))
// 方式二:注册专用 key "responsex_success",按请求语言输出
langx.AppendTrans("zh", map[string]string{"responsex_success": "请求成功"})
langx.AppendTrans("en", map[string]string{"responsex_success": "success"})
```
> 注意:`responsex_success` 是专用 key。不要用 `success`,该 key 已被 errorx 预注册。
## trace_id 解析顺序
1. 已注册的驱动(如 go-zero 适配包);
2. 标准 ctx key `responsex.CtxTraceIDKey`(值为 `"trace_id"`);
3. 取不到则响应中不出现 `trace_id` 字段。
框架适配包通过 `responsex.RegisterTraceDriver` 注册,业务代码一般无需调用。
需要自定义来源时:
```go
responsex.RegisterTraceDriver(func(ctx context.Context) string {
return myTracer.IDFrom(ctx)
})
```
## 主要 API
核心(`responsex`):
- 响应:`Success`、`SuccessWithPage`、`Error`、`ErrorWithData`、`ErrorStr`、`FormatMessage`、`ResponseCtx`
- 选项:`SetCode`、`SetMessage`、`SetData`、`SetPage`、`SetHttpStatusCode`、`SetHeader(s)`、`SetIgnoreLog`、`SetLogger`、`SetIgnoreTrace`、`SetDefaultSuccessCode`、`SetDefaultErrorCode`、`SetDefaultSuccessMessage`
- 分页:`GetOffset`、`GetPage`、`Pagination`
- trace:`SetTraceID`、`TraceID`、`RegisterTraceDriver`、`CtxTraceIDKey`、`SuccessMessageCtx`
gin(`ginx`):`Middleware`、`Success`、`SuccessWithPage`、`Error`、`ErrorWithData`、`ErrorStr`、`Custom`、`MsgMapData`、`MsgKV`
gormx:`GetDbOffset`
gozerotrace:空导入即注册
## 开发
本仓库是多模块,本地开发建议在根目录建 `go.work`(已 gitignore,勿提交):
```
go 1.22.4
use (
.
./gin
./gormx
./gozerotrace
)
```
运行测试:
```bash
go test ./... # 需要 go.work;否则分别进入各模块执行
cd gin && go test ./...
cd gormx && go test ./...
cd gozerotrace && go test ./...
```
校验依赖隔离(在核心模块根目录执行,应无任何框架包):
```bash
go list -deps code.yun.ink/pkg/responsex | grep -E 'gin-gonic|gorm.io|zeromicro|opentelemetry'
```
## 发布(tag 规则)
子模块必须带目录前缀的 tag,否则 proxy 不认:
| 模块 | tag | 说明 |
|---|---|---|
| 核心 | `v1.0.2` | 根目录 |
| gin | `gin/v1.0.2` | 子模块 |
| gormx | `gormx/v1.0.2` | 子模块 |
| gozerotrace | `gozerotrace/v1.0.2` | 子模块 |
**发布顺序**(重要):Go 无法 require 本地未发布的模块,所以必须
**先推子模块 tag,最后再改根 go.mod 并发布根版本**。
## v0.0.4 → v1.x 迁移
gin 适配层从根包移到了子模块,并改名为 `ginx`:
| 旧(v0.0.4,根包) | 新(v1.x) |
|---|---|
| `responsex.GinError` | `ginx.Error` |
| `responsex.GinErrorData` | `ginx.ErrorWithData` |
| `responsex.GinErrorStr` | `ginx.ErrorStr` |
| `responsex.GinSuccess` | `ginx.Success` |
| `responsex.GinSuccessWithPage` | `ginx.SuccessWithPage` |
| `responsex.GinOptions` | `ginx.Custom` |
| `responsex.GinMsgMapData` | `ginx.MsgMapData` |
| `responsex.GinMsgKV` | `ginx.MsgKV` |
新增的 `ops ...Option` 是变参,旧调用点语义不变;只需把 import 与函数名改掉,
并在路由上挂一次 `ginx.Middleware()`。