Compare commits
162 Commits
8da2c61aa8
...
master
Author | SHA1 | Date | |
---|---|---|---|
f4c58d473e | |||
a677a8312b | |||
c5fa764807 | |||
7a3da3614e | |||
a1372933c0 | |||
1da666d6fc | |||
558587ec68 | |||
66c2ddad1c | |||
fcfba9c50b | |||
18dd156718 | |||
d725815ce7 | |||
ecaf448100 | |||
927870b11a | |||
2aa8e86917 | |||
146fbaf878 | |||
0bd02dbd08 | |||
03963ce8e3 | |||
8a21c7eb52 | |||
24f33309b9 | |||
54a8fa91c7 | |||
e0d5e9c94b | |||
a7fed88714 | |||
5df4fbab16 | |||
91a1d34474 | |||
53ecd0c267 | |||
2fd9195b77 | |||
c826166256 | |||
84f0e1a4f6 | |||
9ae310782b | |||
0d981b32c6 | |||
9decd12ee8 | |||
b95d464c89 | |||
3432087fbd | |||
2c201ae226 | |||
12d2735663 | |||
01b62b6eee | |||
1de0715637 | |||
93cef0cf7f | |||
b9a7f97342 | |||
75cc21c494 | |||
4909e75928 | |||
bc94242d12 | |||
11d90566ae | |||
113b1e5a05 | |||
7530625baa | |||
8cb98e4817 | |||
cfcb05f4da | |||
f11540fa6e | |||
c3e42b944c | |||
bb54978dc5 | |||
de34f57a5f | |||
793fefbcd2 | |||
26476ee23b | |||
1c2c5aa4ce | |||
74dde8b41e | |||
1e53d899dc | |||
a80c559a7c | |||
02675d6c40 | |||
681bd3fe95 | |||
7ef8bf2676 | |||
293f829945 | |||
74f0ae2069 | |||
d237b3b651 | |||
fca399fdc3 | |||
593df47884 | |||
3774bda0ff | |||
4ddf55d371 | |||
d832eef5a0 | |||
c3df76e94d | |||
99df73e50e | |||
ff91efd1c8 | |||
e1f597ae50 | |||
2f7d1438b2 | |||
a50883b734 | |||
5d19efa8cf | |||
7c67160c65 | |||
a338713c77 | |||
cb4718a269 | |||
1b1964881f | |||
e95061a1a8 | |||
77ea723e86 | |||
196c437cc5 | |||
b408076fa7 | |||
851de1b3ef | |||
e40475cdb1 | |||
ec04a023cd | |||
18988c4d46 | |||
196c420fcc | |||
6a917d338a | |||
f6db9e8edb | |||
a0b4cdb414 | |||
cffb0c8779 | |||
56441151cf | |||
2c99eb9656 | |||
a72733d81a | |||
0636dd1b11 | |||
e52d67cfe3 | |||
02cdc3c792 | |||
36d4ca844a | |||
99ea9ba111 | |||
8ceb818a24 | |||
22bf77019a | |||
01c2fdf5a2 | |||
29b0eaa6b3 | |||
5ae2e3fae1 | |||
9d0f74b19a | |||
c4d41a9d4e | |||
5abd91f947 | |||
4f1e0e2649 | |||
df5a20ea87 | |||
195e391235 | |||
ce8feaa98b | |||
29bcf44ec9 | |||
c420abfed9 | |||
fce6043dca | |||
df16e63e85 | |||
9b7e714b29 | |||
b9c0389e7b | |||
b2fe05111c | |||
3c29aa847c | |||
3368418b3b | |||
86cecc4eb7 | |||
a1418c290c | |||
8f533f5b04 | |||
20d655973a | |||
ca0cb31235 | |||
9eb3b92c41 | |||
60ef099fdf | |||
8997577181 | |||
cc98043aff | |||
0b7df330f7 | |||
8a6182d0de | |||
deead87688 | |||
f8f63691b7 | |||
455f74ad89 | |||
f55dee577d | |||
b14b1ef345 | |||
cbfe92597e | |||
0a4a8a3c67 | |||
567f8794de | |||
0ffe074179 | |||
c4edba172b | |||
a6c94d55b1 | |||
3298adb55a | |||
d94d4b0e9f | |||
cc6975ec98 | |||
f787899725 | |||
80ce64f8e1 | |||
4dc70ee96b | |||
b7abf88670 | |||
782c3afffe | |||
d176a23c53 | |||
e7c13b6089 | |||
817a4a8310 | |||
c93cf01fe0 | |||
5e1aaaf64e | |||
ec91bb7a44 | |||
f68f691dd1 | |||
25aab2e8db | |||
77d2310b27 | |||
7678ee10ba | |||
6d2ea0ce59 |
12
define/consts.go
Normal file
12
define/consts.go
Normal file
@ -0,0 +1,12 @@
|
||||
// Package define ...
|
||||
//
|
||||
// Description : define ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-04-12 20:18
|
||||
package define
|
||||
|
||||
const (
|
||||
CustomContextKey = "_CUSTOM_CONTEXT" // 自定义context
|
||||
)
|
68
define/context.go
Normal file
68
define/context.go
Normal file
@ -0,0 +1,68 @@
|
||||
// Package define ...
|
||||
//
|
||||
// Description : define ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-04-12 20:57
|
||||
package define
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
networkUtil "git.zhangdeman.cn/zhangdeman/network/util"
|
||||
"git.zhangdeman.cn/zhangdeman/wrapper"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/trace"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
Context *gin.Context // 继承 gin context
|
||||
Trace *trace.Runtime // trace 实例
|
||||
TraceID string
|
||||
RequestID string
|
||||
RequestTime time.Time
|
||||
}
|
||||
|
||||
// NewContext 创建context
|
||||
func NewContext(ginCtx *gin.Context) *Context {
|
||||
existCtx, exist := ginCtx.Get(CustomContextKey)
|
||||
if exist && existCtx != nil {
|
||||
if c, ok := existCtx.(*Context); ok {
|
||||
return c
|
||||
}
|
||||
}
|
||||
traceID := fmt.Sprintf(
|
||||
"%v-%v-%v-%v",
|
||||
time.Now().UnixNano()/1e6,
|
||||
strings.ReplaceAll(networkUtil.IP.GetHostIP(), ".", ""),
|
||||
strings.ReplaceAll(networkUtil.IP.GetRemoteIP(ginCtx.Request), ".", ""),
|
||||
wrapper.StringFromRandom(32, "").Md5().Value,
|
||||
)
|
||||
getRequestID := func(ctx *gin.Context, traceID string) string {
|
||||
requestID := ctx.GetHeader("X-Forward-Request-Id")
|
||||
if len(requestID) > 0 {
|
||||
return requestID
|
||||
}
|
||||
if len(traceID) > 0 {
|
||||
return traceID
|
||||
}
|
||||
return traceID
|
||||
}
|
||||
ctx := &Context{
|
||||
Context: ginCtx,
|
||||
Trace: trace.NewRuntime(traceID, 1),
|
||||
TraceID: traceID,
|
||||
RequestID: getRequestID(ginCtx, traceID),
|
||||
RequestTime: time.Now(),
|
||||
}
|
||||
httpHandleConfig := GetHttpHandleConfig()
|
||||
ginCtx.Set(CustomContextKey, ctx)
|
||||
ginCtx.Set(httpHandleConfig.TraceIDField, traceID)
|
||||
ginCtx.Set(httpHandleConfig.RequestIDField, ctx.RequestID)
|
||||
ginCtx.Set(httpHandleConfig.StartRequestTimeField, ctx.RequestTime.UnixMilli())
|
||||
return ctx
|
||||
}
|
@ -7,31 +7,14 @@
|
||||
// Date : 2022-06-25 20:33
|
||||
package define
|
||||
|
||||
import (
|
||||
"git.zhangdeman.cn/zhangdeman/consts"
|
||||
"git.zhangdeman.cn/zhangdeman/wrapper"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// RequestIDField 请求ID
|
||||
RequestIDField = "request_id"
|
||||
// TraceIDField 追踪ID
|
||||
TraceIDField = "trace_id"
|
||||
// StartRequestTimeField 开始请求时间字段
|
||||
StartRequestTimeField = "start_request_time"
|
||||
// FinishRequestTimeField 完成请求时间
|
||||
FinishRequestTimeField = "finish_request_time"
|
||||
// ResponseCodeField 响应状态码字段
|
||||
ResponseCodeField = "code"
|
||||
// ResponseMessageField 响应信息字段
|
||||
ResponseMessageField = "message"
|
||||
// HandleRequestCostField 处理请求耗时
|
||||
HandleRequestCostField = "cost"
|
||||
// ResponseDataField 响应数据字段
|
||||
ResponseDataField = "data"
|
||||
// ResponseTraceIDField 响应TraceID字段
|
||||
ResponseTraceIDField = "trace_id"
|
||||
// ResponseRequestIDField 响应的请求ID字段
|
||||
ResponseRequestIDField = "request_id"
|
||||
// RecordResponseDataField 记录响应数据到上下文
|
||||
RecordResponseDataField = "pkg_gin_record_response_data"
|
||||
// RecordRequestDataField 记录请求数据到上下文
|
||||
RecordRequestDataField = "pkg_gin_record_request_data"
|
||||
inputHttpHandleConfig = &HttpHandleConfig{}
|
||||
)
|
||||
|
||||
// HttpHandleConfig 请求处理配置
|
||||
@ -40,15 +23,21 @@ var (
|
||||
//
|
||||
// Date : 20:41 2022/6/25
|
||||
type HttpHandleConfig struct {
|
||||
RequestIDField string
|
||||
TraceIDField string
|
||||
ResponseCodeField string
|
||||
ResponseMessageField string
|
||||
HandleRequestCostField string
|
||||
ResponseDataField string
|
||||
ResponseTraceIDField string
|
||||
ResponseRequestIDField string
|
||||
StartRequestTimeField string
|
||||
RecordRequestDataField string
|
||||
RecordResponseDataField string
|
||||
RequestIDField string
|
||||
TraceIDField string
|
||||
ResponseCodeField string
|
||||
ResponseMessageField string
|
||||
HandleRequestCostField string
|
||||
ResponseDataField string
|
||||
ResponseTraceIDField string
|
||||
StartRequestTimeField string
|
||||
FinishRequestTimeField string
|
||||
RequestIsSuccessField string // 请求处理是否成功的标识
|
||||
ExtensionOutputField string // 扩展信息对外输出字段
|
||||
EnableExtensionOutput bool
|
||||
DisableDebugStackOutput bool // 禁用异常堆栈打印
|
||||
}
|
||||
|
||||
// ConvertDefaultConfig 覆盖默认配置
|
||||
@ -57,39 +46,110 @@ type HttpHandleConfig struct {
|
||||
//
|
||||
// Date : 20:41 2022/6/25
|
||||
func ConvertDefaultConfig(cfg *HttpHandleConfig) {
|
||||
if len(cfg.RequestIDField) > 0 {
|
||||
RequestIDField = cfg.ResponseRequestIDField
|
||||
}
|
||||
inputHttpHandleConfig = cfg
|
||||
}
|
||||
|
||||
if len(cfg.TraceIDField) > 0 {
|
||||
TraceIDField = cfg.TraceIDField
|
||||
}
|
||||
|
||||
if len(cfg.ResponseCodeField) > 0 {
|
||||
ResponseCodeField = cfg.ResponseCodeField
|
||||
}
|
||||
|
||||
if len(cfg.ResponseMessageField) > 0 {
|
||||
ResponseMessageField = cfg.ResponseMessageField
|
||||
}
|
||||
|
||||
if len(cfg.ResponseDataField) > 0 {
|
||||
ResponseDataField = cfg.ResponseDataField
|
||||
}
|
||||
|
||||
if len(cfg.ResponseRequestIDField) > 0 {
|
||||
ResponseRequestIDField = cfg.ResponseRequestIDField
|
||||
}
|
||||
|
||||
if len(cfg.ResponseTraceIDField) > 0 {
|
||||
ResponseTraceIDField = cfg.ResponseTraceIDField
|
||||
}
|
||||
|
||||
if len(cfg.HandleRequestCostField) > 0 {
|
||||
HandleRequestCostField = cfg.HandleRequestCostField
|
||||
}
|
||||
|
||||
if len(cfg.StartRequestTimeField) > 0 {
|
||||
StartRequestTimeField = cfg.StartRequestTimeField
|
||||
// GetHttpHandleConfig 获取http配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:55 2024/7/23
|
||||
func GetHttpHandleConfig() *HttpHandleConfig {
|
||||
return &HttpHandleConfig{
|
||||
EnableExtensionOutput: inputHttpHandleConfig.EnableExtensionOutput,
|
||||
DisableDebugStackOutput: inputHttpHandleConfig.DisableDebugStackOutput,
|
||||
RequestIDField: wrapper.TernaryOperator.String(
|
||||
nil == inputHttpHandleConfig || inputHttpHandleConfig.RequestIDField == "",
|
||||
consts.GinRequestIDField,
|
||||
wrapper.String(inputHttpHandleConfig.RequestIDField),
|
||||
).Value(),
|
||||
TraceIDField: wrapper.TernaryOperator.String(
|
||||
nil == inputHttpHandleConfig || inputHttpHandleConfig.TraceIDField == "",
|
||||
consts.GinTraceIDField,
|
||||
wrapper.String(inputHttpHandleConfig.TraceIDField),
|
||||
).Value(),
|
||||
ResponseCodeField: wrapper.TernaryOperator.String(
|
||||
nil == inputHttpHandleConfig || inputHttpHandleConfig.ResponseCodeField == "",
|
||||
consts.GinResponseCodeField,
|
||||
wrapper.String(inputHttpHandleConfig.ResponseCodeField),
|
||||
).Value(),
|
||||
ResponseMessageField: wrapper.TernaryOperator.String(
|
||||
nil == inputHttpHandleConfig || inputHttpHandleConfig.ResponseMessageField == "",
|
||||
consts.GinResponseMessageField,
|
||||
wrapper.String(inputHttpHandleConfig.ResponseMessageField),
|
||||
).Value(),
|
||||
HandleRequestCostField: wrapper.TernaryOperator.String(
|
||||
nil == inputHttpHandleConfig || inputHttpHandleConfig.HandleRequestCostField == "",
|
||||
consts.GinHandleRequestCostField,
|
||||
wrapper.String(inputHttpHandleConfig.HandleRequestCostField),
|
||||
).Value(),
|
||||
ResponseDataField: wrapper.TernaryOperator.String(
|
||||
nil == inputHttpHandleConfig || inputHttpHandleConfig.ResponseDataField == "",
|
||||
consts.GinResponseDataField,
|
||||
wrapper.String(inputHttpHandleConfig.ResponseDataField),
|
||||
).Value(),
|
||||
ResponseTraceIDField: wrapper.TernaryOperator.String(
|
||||
nil == inputHttpHandleConfig || inputHttpHandleConfig.ResponseTraceIDField == "",
|
||||
consts.GinResponseTraceIDField,
|
||||
wrapper.String(inputHttpHandleConfig.ResponseTraceIDField),
|
||||
).Value(),
|
||||
StartRequestTimeField: wrapper.TernaryOperator.String(
|
||||
nil == inputHttpHandleConfig || inputHttpHandleConfig.StartRequestTimeField == "",
|
||||
consts.GinStartRequestTimeField,
|
||||
wrapper.String(inputHttpHandleConfig.StartRequestTimeField),
|
||||
).Value(),
|
||||
FinishRequestTimeField: wrapper.TernaryOperator.String(
|
||||
nil == inputHttpHandleConfig || inputHttpHandleConfig.FinishRequestTimeField == "",
|
||||
consts.GinFinishRequestTimeField,
|
||||
wrapper.String(inputHttpHandleConfig.FinishRequestTimeField),
|
||||
).Value(),
|
||||
RecordRequestDataField: wrapper.TernaryOperator.String(
|
||||
nil == inputHttpHandleConfig || inputHttpHandleConfig.RecordRequestDataField == "",
|
||||
consts.GinRecordRequestDataField,
|
||||
wrapper.String(inputHttpHandleConfig.RecordRequestDataField),
|
||||
).Value(),
|
||||
RecordResponseDataField: wrapper.TernaryOperator.String(
|
||||
nil == inputHttpHandleConfig || inputHttpHandleConfig.RecordResponseDataField == "",
|
||||
consts.GinRecordResponseDataField,
|
||||
wrapper.String(inputHttpHandleConfig.RecordResponseDataField),
|
||||
).Value(),
|
||||
RequestIsSuccessField: wrapper.TernaryOperator.String(
|
||||
nil == inputHttpHandleConfig || inputHttpHandleConfig.RequestIsSuccessField == "",
|
||||
consts.GinRequestIsSuccessField,
|
||||
wrapper.String(inputHttpHandleConfig.RecordResponseDataField),
|
||||
).Value(),
|
||||
ExtensionOutputField: wrapper.TernaryOperator.String(
|
||||
nil == inputHttpHandleConfig || inputHttpHandleConfig.ExtensionOutputField == "",
|
||||
consts.GinResponseExtensionField,
|
||||
wrapper.String(inputHttpHandleConfig.RecordResponseDataField),
|
||||
).Value(),
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
LogicAfterResponseKey = "__logic_after_response__"
|
||||
)
|
||||
|
||||
type LogicAfterResponse struct {
|
||||
SuccessHookFuncList []func() `json:"-"` // 请求最后需要执行的成功hook函数
|
||||
FailureHookFuncList []func() `json:"-"` // 请求最后需要执行的失败hook函数
|
||||
Lock *sync.RWMutex `json:"-"` // 逻辑锁
|
||||
}
|
||||
|
||||
func (logic *LogicAfterResponse) AddSuccessHook(f func()) {
|
||||
logic.Lock.Lock()
|
||||
defer logic.Lock.Unlock()
|
||||
logic.SuccessHookFuncList = append(logic.SuccessHookFuncList, f)
|
||||
}
|
||||
func (logic *LogicAfterResponse) AddFailureHook(f func()) {
|
||||
logic.Lock.Lock()
|
||||
defer logic.Lock.Unlock()
|
||||
logic.FailureHookFuncList = append(logic.FailureHookFuncList, f)
|
||||
}
|
||||
|
||||
// ResponseOption 响应的可用选项
|
||||
type ResponseOption struct {
|
||||
ContentType string `json:"content_type"` // 响应的contentType
|
||||
Extension map[string]any `json:"extension"` // 扩展数据
|
||||
XmlName string `json:"xml_name"` // 以xml文件格式响应数据时, Xml文件名(根节点)
|
||||
}
|
||||
|
98
go.mod
98
go.mod
@ -1,43 +1,85 @@
|
||||
module git.zhangdeman.cn/zhangdeman/gin
|
||||
|
||||
go 1.17
|
||||
go 1.24.1
|
||||
|
||||
toolchain go1.24.2
|
||||
|
||||
require (
|
||||
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20220703050824-0679ce509241
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20220714054940-3cfcad0c6547
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
git.zhangdeman.cn/gateway/api-doc v0.0.0-20250528130517-e02098e64392
|
||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250425024726-cc17224cb995
|
||||
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20250429065800-fc340b9417cf
|
||||
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250510123912-a0d52fc093ab
|
||||
git.zhangdeman.cn/zhangdeman/graceful v0.0.0-20250529070945-92833db6f3a4
|
||||
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20250427065227-163236205af5
|
||||
git.zhangdeman.cn/zhangdeman/network v0.0.0-20250509030820-7b1a36a7c38c
|
||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20250504055908-8d68e6106ea9
|
||||
git.zhangdeman.cn/zhangdeman/trace v0.0.0-20250412104923-c1ecb1bfe8d5
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250321102712-1cbfbe959740
|
||||
github.com/gin-contrib/pprof v1.5.3
|
||||
github.com/gin-gonic/gin v1.10.1
|
||||
github.com/go-playground/validator/v10 v10.26.0
|
||||
github.com/mcuadros/go-defaults v1.2.0
|
||||
go.uber.org/zap v1.27.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Jeffail/gabs v1.4.0 // indirect
|
||||
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda // indirect
|
||||
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20241101082529-28a6c68e38a4 // indirect
|
||||
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 // indirect
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e // indirect
|
||||
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20241125101541-c5ea194c9c1e // indirect
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-ini/ini v1.66.6 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.10.0 // indirect
|
||||
github.com/goccy/go-json v0.9.7 // indirect
|
||||
github.com/bytedance/sonic v1.13.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/spec v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.1 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-webtools/knife4go v1.0.4 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect
|
||||
github.com/lestrrat-go/strftime v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
||||
github.com/mozillazg/go-pinyin v0.20.0 // indirect
|
||||
github.com/mssola/user_agent v0.6.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/sbabiv/xml2map v1.2.1 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/stretchr/testify v1.8.0 // indirect
|
||||
github.com/tidwall/gjson v1.14.1 // indirect
|
||||
github.com/swaggo/files v1.0.1 // indirect
|
||||
github.com/swaggo/gin-swagger v1.6.0 // indirect
|
||||
github.com/swaggo/swag v1.16.4 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.18.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
284
go.sum
284
go.sum
@ -1,151 +1,241 @@
|
||||
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20220703050824-0679ce509241 h1:tBaET7SL1ElDQ8mI3A0sHNonmycB0fFp3n8vWo410YM=
|
||||
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20220703050824-0679ce509241/go.mod h1:mIMM/t9BkrKHAcDCmarLCHQhHfWf0/ZjtcqJPboqmSA=
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20220625151616-cfe1f4c04db4 h1:uHYTRztH/XEVtr3FLykCf/3LhFQ7zQHnzVAeDyC1huQ=
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20220625151616-cfe1f4c04db4/go.mod h1:YI/XeTmrr9+8dxa4ThPkmNcEE8WHG5pZkKujpSWwIxM=
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20220714054940-3cfcad0c6547 h1:ieuPrfZDDgwz9ANQdXQrGNWlKWN6bK3qOUC+XUAITrk=
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20220714054940-3cfcad0c6547/go.mod h1:zTir/0IWdK3E7n0GiaogyWHADAQnBtTdl2I6Z2/OPqw=
|
||||
github.com/Jeffail/gabs v1.4.0 h1://5fYRRTq1edjfIrQGvdkcd22pkYUrHZ5YC/H2GJVAo=
|
||||
github.com/Jeffail/gabs v1.4.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
|
||||
git.zhangdeman.cn/gateway/api-doc v0.0.0-20250528130517-e02098e64392 h1:1HALMZC54/krtdGKnRlhATMzCT5sZdvgVIxt4zZIrTk=
|
||||
git.zhangdeman.cn/gateway/api-doc v0.0.0-20250528130517-e02098e64392/go.mod h1:ypwNuZTQHxynZwm+l5Z8ZYFd5YrLrwnSWy+FxtXLqJw=
|
||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250425024726-cc17224cb995 h1:LmPRAf0AsxRVFPibdpZR89ajlsz8hof2IvMMyTqiEq4=
|
||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250425024726-cc17224cb995/go.mod h1:5p8CEKGBxi7qPtTXDI3HDmqKAfIm5i/aBWdrbkbdNjc=
|
||||
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20250429065800-fc340b9417cf h1:xCPM3U6i62UvLo9VNvDP45Ue3dPl7ratHu1rSEJRE2k=
|
||||
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20250429065800-fc340b9417cf/go.mod h1:onY+qrB+Uwfuv75JlgHlGdkirAfYcINrvCashtVoBX0=
|
||||
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda h1:bMD6r9gjRy7cO+T4zRQVYAesgIblBdTnhzT1vN5wjvI=
|
||||
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda/go.mod h1:dT0rmHcJ9Z9IqWeMIt7YzR88nKkNV2V3dfG0j9Q6lK0=
|
||||
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20241101082529-28a6c68e38a4 h1:s6d4b6yY+NaK1AzoBD1pxqsuygEHQz0Oie86c45geDw=
|
||||
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20241101082529-28a6c68e38a4/go.mod h1:V4Dfg1v/JVIZGEKCm6/aehs8hK+Xow1dkL1yiQymXlQ=
|
||||
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250510123912-a0d52fc093ab h1:O0XaAKKb8qrjcjewonmKfnRsMFoCfJF+tUv6RfhRe94=
|
||||
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250510123912-a0d52fc093ab/go.mod h1:Voc8J4ordx7nuMWpgACXXZULQy7ZIuBzcEIoS8VnDIw=
|
||||
git.zhangdeman.cn/zhangdeman/graceful v0.0.0-20250529070945-92833db6f3a4 h1:d1B3IXRitiMlY5ssEWRa//RQD24SqRPk/bCpL4/WyOA=
|
||||
git.zhangdeman.cn/zhangdeman/graceful v0.0.0-20250529070945-92833db6f3a4/go.mod h1:faaKb5d5tz3NmQQ+NrTnBDa1G/tN9/CVuKun1V4WBIE=
|
||||
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20250427065227-163236205af5 h1:e3N+ODeTBwBOQ4iIzXm9gO3X6Htgc/YmPBImSk7o2mw=
|
||||
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20250427065227-163236205af5/go.mod h1:lTxYyi7IodBefXSdSZwPFs3Izggg5Wxa2RkHlFVwKLg=
|
||||
git.zhangdeman.cn/zhangdeman/network v0.0.0-20250509030820-7b1a36a7c38c h1:4Yn7U1TkurpNizrjbIyJDYTRSlD4zp1ypkREyCSBKIM=
|
||||
git.zhangdeman.cn/zhangdeman/network v0.0.0-20250509030820-7b1a36a7c38c/go.mod h1:uRg3BnUimmrJiv8N61P6Ir4Im2EqxXZe62m4WBw3sec=
|
||||
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 h1:gUDlQMuJ4xNfP2Abl1Msmpa3fASLWYkNlqDFF/6GN0Y=
|
||||
git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0/go.mod h1:VHb9qmhaPDAQDcS6vUiDCamYjZ4R5lD1XtVsh55KsMI=
|
||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20250504055908-8d68e6106ea9 h1:/GLQaFoLb+ciHOtAS2BIyPNnf4O5ME3AC5PUaJY9kfs=
|
||||
git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20250504055908-8d68e6106ea9/go.mod h1:ABJ655C5QenQNOzf7LjCe4sSB52CXvaWLX2Zg4uwDJY=
|
||||
git.zhangdeman.cn/zhangdeman/trace v0.0.0-20250412104923-c1ecb1bfe8d5 h1:dD1Q/MIrRmIhKqfYPH+y167ca9CKwTPuQt3c1hXWGJ8=
|
||||
git.zhangdeman.cn/zhangdeman/trace v0.0.0-20250412104923-c1ecb1bfe8d5/go.mod h1:PB486NC82nuvn5yi+U2i48ogX/9EAETWAHd8O9TwY9k=
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e h1:Q973S6CcWr1ICZhFI1STFOJ+KUImCl2BaIXm6YppBqI=
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e/go.mod h1:VpPjBlwz8U+OxZuxzHQBv1aEEZ3pStH6bZvT21ADEbI=
|
||||
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20241125101541-c5ea194c9c1e h1:YE2Gi+M03UDImIpWa3I7jzSesyfu2RL8x/4ONs5v0oE=
|
||||
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20241125101541-c5ea194c9c1e/go.mod h1:L/7JugxKZL3JP9JP/XDvPAPz0FQXG1u181Su1+u/d1c=
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250321102712-1cbfbe959740 h1:zPUoylfJTbc0EcxW+NEzOTBmoeFZ2I/rLFBnEzxb4Wk=
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250321102712-1cbfbe959740/go.mod h1:1ct92dbVc49pmXusA/iGfcQUJzcYmJ+cjAhgc3sDv1I=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
|
||||
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
|
||||
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
|
||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||
github.com/go-ini/ini v1.66.6 h1:h6k2Bb0HWS/BXXHCXj4QHjxPmlIU4NK+7MuLp9SD+4k=
|
||||
github.com/go-ini/ini v1.66.6/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0=
|
||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU=
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
||||
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
||||
github.com/gin-contrib/pprof v1.5.3 h1:Bj5SxJ3kQDVez/s/+f9+meedJIqLS+xlkIVDe/lcvgM=
|
||||
github.com/gin-contrib/pprof v1.5.3/go.mod h1:0+LQSZ4SLO0B6+2n6JBzaEygpTBxe/nI+YEYpfQQ6xY=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
|
||||
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
|
||||
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
||||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||
github.com/go-webtools/knife4go v1.0.4 h1:p32SApmM0sx2/Y5p0QfeaGv5KD96R1mj2CaHdyH8jy8=
|
||||
github.com/go-webtools/knife4go v1.0.4/go.mod h1:trOlXN1tqBJ7R44sHON3exGvzCwjbsVriIHEenry3d8=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
|
||||
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg=
|
||||
github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
|
||||
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
|
||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
|
||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
|
||||
github.com/lestrrat-go/strftime v1.1.0 h1:gMESpZy44/4pXLO/m+sL0yBd1W6LjgjrrD4a68Gapyg=
|
||||
github.com/lestrrat-go/strftime v1.1.0/go.mod h1:uzeIB52CeUJenCo1syghlugshMysrqUT51HlxphXVeI=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc=
|
||||
github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/mozillazg/go-pinyin v0.20.0 h1:BtR3DsxpApHfKReaPO1fCqF4pThRwH9uwvXzm+GnMFQ=
|
||||
github.com/mozillazg/go-pinyin v0.20.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
|
||||
github.com/mssola/user_agent v0.6.0 h1:uwPR4rtWlCHRFyyP9u2KOV0u8iQXmS7Z7feTrstQwk4=
|
||||
github.com/mssola/user_agent v0.6.0/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/sbabiv/xml2map v1.2.1 h1:1lT7t0hhUvXZCkdxqtq4n8/ZCnwLWGq4rDuDv5XOoFE=
|
||||
github.com/sbabiv/xml2map v1.2.1/go.mod h1:2TPoAfcaM7+Sd4iriPvzyntb2mx7GY+kkQpB/GQa/eo=
|
||||
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
||||
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
|
||||
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
|
||||
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q=
|
||||
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo=
|
||||
github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
|
||||
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
|
||||
github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
|
||||
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
|
||||
github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A=
|
||||
github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
|
||||
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
||||
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
|
||||
golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 h1:sY2a+y0j4iDrajJcorb+a0hJIQ6uakU5gybjfLWHlXo=
|
||||
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376/go.mod h1:BHKOc1m5wm8WwQkMqYBoo4vNxhmF7xg8+xhG8L+Cy3M=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
|
12
init.go
Normal file
12
init.go
Normal file
@ -0,0 +1,12 @@
|
||||
// Package gin ...
|
||||
//
|
||||
// Description : gin ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-10-22 17:01
|
||||
package gin
|
||||
|
||||
import (
|
||||
_ "git.zhangdeman.cn/zhangdeman/gin/request/parse_body"
|
||||
)
|
@ -8,90 +8,120 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"git.zhangdeman.cn/zhangdeman/gin/request/parse_body"
|
||||
"strings"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/util"
|
||||
"git.zhangdeman.cn/zhangdeman/consts"
|
||||
"git.zhangdeman.cn/zhangdeman/gin/request"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/gin/define"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/logger"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Access 记录请求日志
|
||||
// fillCfg 填充默认配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 10:55 2022/7/14
|
||||
func Access(cfg *AccessConfig) gin.HandlerFunc {
|
||||
// 未传入配置或者未传入日志实例
|
||||
if nil == cfg || nil == cfg.Logger {
|
||||
return func(ctx *gin.Context) {
|
||||
ctx.Next()
|
||||
}
|
||||
// Date : 15:37 2023/12/29
|
||||
func fillCfg(cfg *AccessConfig) {
|
||||
if nil == cfg {
|
||||
return
|
||||
}
|
||||
if nil == cfg.IsRecordLog {
|
||||
cfg.IsRecordLog = defaultIsRecordLog
|
||||
}
|
||||
if nil == cfg.ExtraFieldList {
|
||||
cfg.ExtraFieldList = make([]string, 0)
|
||||
if nil == cfg.RequestHeaderList {
|
||||
cfg.RequestHeaderList = make([]string, 0)
|
||||
}
|
||||
cfg.ExtraFieldList = append(cfg.ExtraFieldList, define.RecordRequestDataField, define.RecordResponseDataField)
|
||||
if nil == cfg.ResponseHeaderList {
|
||||
cfg.ResponseHeaderList = make([]string, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// getLogRequestHeader 获取记录的请求header
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:59 2023/12/29
|
||||
func getLogRequestHeader(ctx *gin.Context, cfg *AccessConfig) map[string][]string {
|
||||
// 请求header
|
||||
headerTable := make(map[string][]string)
|
||||
if len(cfg.RequestHeaderList) == 0 {
|
||||
// 全部记录
|
||||
for headerKey := range ctx.Request.Header {
|
||||
cfg.RequestHeaderList = append(cfg.RequestHeaderList, headerKey)
|
||||
}
|
||||
}
|
||||
for _, key := range cfg.RequestHeaderList {
|
||||
headerTable[key] = ctx.Request.Header.Values(key)
|
||||
}
|
||||
return headerTable
|
||||
}
|
||||
|
||||
// getLogResponseHeader 记录相应header
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:01 2023/12/29
|
||||
func getLogResponseHeader(ctx *gin.Context, cfg *AccessConfig) map[string][]string {
|
||||
// 响应header
|
||||
responseHeaderTable := make(map[string][]string)
|
||||
if len(cfg.ResponseHeaderList) == 0 {
|
||||
// 全部记录
|
||||
for headerKey := range ctx.Writer.Header() {
|
||||
cfg.ResponseHeaderList = append(cfg.ResponseHeaderList, headerKey)
|
||||
}
|
||||
zap.Any("pkg_gin_response_header", ctx.Writer.Header())
|
||||
}
|
||||
for _, key := range cfg.ResponseHeaderList {
|
||||
responseHeaderTable[key] = ctx.Writer.Header().Values(key)
|
||||
}
|
||||
return responseHeaderTable
|
||||
}
|
||||
|
||||
// LogRequest 记录请求日志
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 10:55 2022/7/14
|
||||
func LogRequest(cfg *AccessConfig) gin.HandlerFunc {
|
||||
fillCfg(cfg)
|
||||
handleConfig := define.GetHttpHandleConfig()
|
||||
return func(ctx *gin.Context) {
|
||||
ctx.Next()
|
||||
if !cfg.IsRecordLog(ctx) {
|
||||
// 不记录日志
|
||||
// 未传入配置或者未传入日志实例
|
||||
if nil == cfg || nil == cfg.Logger || (nil != cfg.IsRecordLog && !cfg.IsRecordLog(ctx)) {
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
startRequestTime := ctx.GetInt64(define.StartRequestTimeField)
|
||||
logDataList := []zap.Field{
|
||||
// 开始请求时间
|
||||
zap.Any(define.StartRequestTimeField, util.Time.FormatUnixNano(startRequestTime)),
|
||||
startRequestTime := request.WrapperHandle.GetCtxIntData(ctx, handleConfig.StartRequestTimeField, 0)
|
||||
// 记录请求日志
|
||||
data := map[string]any{
|
||||
handleConfig.StartRequestTimeField: startRequestTime, // 开始请求时间
|
||||
"request_header": getLogRequestHeader(ctx, cfg), // 请求header
|
||||
"request_query": request.WrapperHandle.ParseQuery(ctx), // 获取请求query
|
||||
"request_body": map[string]any{}, // 请求body
|
||||
}
|
||||
data["request_body"], _ = parse_body.ExecuteForMap(ctx)
|
||||
logData := logger.NewLogData(ctx, consts.LogTypeRequest, "", data)
|
||||
cfg.Logger.Info("接口请求日志记录", logger.ZapLogDataList(logData)...)
|
||||
ctx.Next()
|
||||
// 结束时间
|
||||
finishRequestTime := ctx.GetInt64(define.FinishRequestTimeField)
|
||||
logDataList = append(
|
||||
logDataList,
|
||||
zap.Any(define.FinishRequestTimeField, util.Time.FormatUnixNano(startRequestTime)),
|
||||
zap.Int64("pkg_gin_request_cost", finishRequestTime-startRequestTime),
|
||||
)
|
||||
|
||||
// 请求header
|
||||
if len(cfg.RequestHeaderList) == 0 {
|
||||
// 全部记录
|
||||
zap.Any("pkg_gin_request_header", ctx.Request.Header)
|
||||
finishRequestTime := request.WrapperHandle.GetCtxIntData(ctx, handleConfig.FinishRequestTimeField, 0)
|
||||
ctx.Set(handleConfig.FinishRequestTimeField, finishRequestTime)
|
||||
// 记录相应日志
|
||||
logResponseData := logger.NewLogData(ctx, consts.LogTypeOutput, "", map[string]any{
|
||||
handleConfig.FinishRequestTimeField: finishRequestTime, // 完成请求时间
|
||||
"request_cost": finishRequestTime - startRequestTime, // 请求耗时
|
||||
"response_body": request.WrapperHandle.GetResponseBody(ctx, handleConfig.ResponseDataField, map[string]any{}), // 响应body
|
||||
"response_header": getLogResponseHeader(ctx, cfg), // 响应header
|
||||
})
|
||||
if ctx.GetBool(define.GetHttpHandleConfig().RequestIsSuccessField) {
|
||||
cfg.Logger.Info("接口响应日志记录", logger.ZapLogDataList(logResponseData)...)
|
||||
} else {
|
||||
headerTable := make(map[string][]string, 0)
|
||||
for _, key := range cfg.RequestHeaderList {
|
||||
headerTable[key] = ctx.Request.Header.Values(key)
|
||||
}
|
||||
zap.Any("pkg_gin_request_header", headerTable)
|
||||
}
|
||||
|
||||
// 响应header
|
||||
if len(cfg.ResponseHeaderList) == 0 {
|
||||
// 全部记录
|
||||
zap.Any("pkg_gin_response_header", ctx.Writer.Header())
|
||||
} else {
|
||||
headerTable := make(map[string][]string, 0)
|
||||
for _, key := range cfg.ResponseHeaderList {
|
||||
headerTable[key] = ctx.Writer.Header().Values(key)
|
||||
}
|
||||
zap.Any("pkg_gin_response_header", headerTable)
|
||||
}
|
||||
|
||||
// 扩展数据
|
||||
for _, field := range cfg.ExtraFieldList {
|
||||
val, _ := ctx.Get(field)
|
||||
logDataList = append(logDataList, zap.Any(field, val))
|
||||
}
|
||||
cfg.Logger.Info("请求日志记录", logDataList...)
|
||||
if nil != cfg.FinishHook {
|
||||
// hook 不为nil, 自动触发
|
||||
cfg.FinishHook(ctx,
|
||||
[]byte(ctx.GetString(define.RecordRequestDataField)),
|
||||
[]byte(ctx.GetString(define.RecordResponseDataField)),
|
||||
finishRequestTime-startRequestTime)
|
||||
cfg.Logger.Error("接口响应日志记录", logger.ZapLogDataList(logResponseData)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -102,12 +132,10 @@ func Access(cfg *AccessConfig) gin.HandlerFunc {
|
||||
//
|
||||
// Date : 11:26 2022/7/14
|
||||
type AccessConfig struct {
|
||||
Logger *zap.Logger // 日志实例
|
||||
RequestHeaderList []string // 要记录哪些header , 不传全部记录
|
||||
ResponseHeaderList []string // 要记录哪些响应header, 不传全部记录
|
||||
IsRecordLog func(ctx *gin.Context) bool // 验证当前请求是否记录日志
|
||||
ExtraFieldList []string // 记录的扩展字段列表,请将相关数据使用 ctx.Set 写入上下文中, 日志会自动记录
|
||||
FinishHook func(ctx *gin.Context, requestData []byte, responseData []byte, cost int64) // 请求处理完成之后, 触发的hook函数
|
||||
Logger *zap.Logger // 日志实例
|
||||
RequestHeaderList []string // 要记录哪些header , 不传全部记录
|
||||
ResponseHeaderList []string // 要记录哪些响应header, 不传全部记录
|
||||
IsRecordLog func(ctx *gin.Context) bool // 验证当前请求是否记录日志
|
||||
}
|
||||
|
||||
// defaultIsRecordLog 默认仅记录 json api 日志
|
||||
|
38
middleware/hook_after_response.go
Normal file
38
middleware/hook_after_response.go
Normal file
@ -0,0 +1,38 @@
|
||||
// Package middleware ...
|
||||
//
|
||||
// Description : middleware ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-07-26 11:54
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"git.zhangdeman.cn/zhangdeman/gin/define"
|
||||
"git.zhangdeman.cn/zhangdeman/gin/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type HookFunc func(ctx *gin.Context, requestData []byte, responseData map[string]interface{}, cost int64)
|
||||
|
||||
// HookAfterResponseMiddleware 请求最终处理完成之后执行的中间件
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 11:55 2024/7/26
|
||||
func HookAfterResponseMiddleware(hookFunc HookFunc) gin.HandlerFunc {
|
||||
handleConfig := define.GetHttpHandleConfig()
|
||||
return func(ctx *gin.Context) {
|
||||
ctx.Next()
|
||||
if nil == hookFunc {
|
||||
return
|
||||
}
|
||||
// hook 不为nil, 自动触发
|
||||
hookFunc(
|
||||
ctx,
|
||||
[]byte(ctx.GetString(handleConfig.RecordRequestDataField)),
|
||||
ctx.GetStringMap(handleConfig.RecordResponseDataField),
|
||||
request.WrapperHandle.GetCtxIntData(ctx, handleConfig.StartRequestTimeField, 0)-request.WrapperHandle.GetCtxIntData(ctx, handleConfig.StartRequestTimeField, 0),
|
||||
)
|
||||
}
|
||||
}
|
@ -12,9 +12,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/wrapper"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/gin/define"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/util"
|
||||
networkUtil "git.zhangdeman.cn/zhangdeman/network/util"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@ -25,11 +27,12 @@ import (
|
||||
// Date : 23:08 2022/6/25
|
||||
func InitRequest() gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
httpHandleConfig := define.GetHttpHandleConfig()
|
||||
traceID := getTraceID(ctx)
|
||||
requestID := getRequestID(ctx, traceID)
|
||||
ctx.Set(define.TraceIDField, traceID)
|
||||
ctx.Set(define.RequestIDField, requestID)
|
||||
ctx.Set(define.StartRequestTimeField, time.Now().UnixNano())
|
||||
ctx.Set(httpHandleConfig.TraceIDField, traceID)
|
||||
ctx.Set(httpHandleConfig.RequestIDField, requestID)
|
||||
ctx.Set(httpHandleConfig.StartRequestTimeField, time.Now().UnixMilli())
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
@ -43,9 +46,9 @@ func getTraceID(ctx *gin.Context) string {
|
||||
return fmt.Sprintf(
|
||||
"%v-%v-%v-%v",
|
||||
time.Now().UnixNano()/1e6,
|
||||
strings.ReplaceAll(util.IP.GetHostIP(), ".", ""),
|
||||
strings.ReplaceAll(util.IP.GetRemoteIP(ctx.Request), ".", ""),
|
||||
util.String.Md5(util.String.GenRandom("", 32)),
|
||||
strings.ReplaceAll(networkUtil.IP.GetHostIP(), ".", ""),
|
||||
strings.ReplaceAll(networkUtil.IP.GetRemoteIP(ctx.Request), ".", ""),
|
||||
wrapper.StringFromRandom(32, "").Md5().Value,
|
||||
)
|
||||
}
|
||||
|
||||
@ -55,7 +58,7 @@ func getTraceID(ctx *gin.Context) string {
|
||||
//
|
||||
// Date : 23:12 2022/6/25
|
||||
func getRequestID(ctx *gin.Context, traceID string) string {
|
||||
requestID := ctx.GetHeader("X-Forward-Request-ID")
|
||||
requestID := ctx.GetHeader("X-Forward-Request-Id")
|
||||
if len(requestID) > 0 {
|
||||
return requestID
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ package middleware
|
||||
import (
|
||||
"git.zhangdeman.cn/zhangdeman/gin/define"
|
||||
"git.zhangdeman.cn/zhangdeman/gin/response"
|
||||
"git.zhangdeman.cn/zhangdeman/util"
|
||||
networkUtil "git.zhangdeman.cn/zhangdeman/network/util"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@ -26,9 +26,12 @@ func ValidateBlackIPMiddleware(code interface{}, httpCode int, validateFunc defi
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
if validateFunc(ctx, util.IP.GetRemoteIP(ctx.Request)) {
|
||||
remoteIp := networkUtil.IP.GetRemoteIP(ctx.Request)
|
||||
if validateFunc(ctx, remoteIp) {
|
||||
// 命中黑名单
|
||||
response.Send(ctx, code, httpCode, nil)
|
||||
response.Send(ctx, code, httpCode, map[string]any{
|
||||
"remote_ip": remoteIp,
|
||||
}, nil)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
@ -48,9 +51,12 @@ func ValidateWhiteIPMiddleware(code interface{}, httpCode int, validateFunc defi
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
if !validateFunc(ctx, util.IP.GetRemoteIP(ctx.Request)) {
|
||||
remoteIp := networkUtil.IP.GetRemoteIP(ctx.Request)
|
||||
if !validateFunc(ctx, remoteIp) {
|
||||
// 非名单
|
||||
response.Send(ctx, code, httpCode, nil)
|
||||
response.Send(ctx, code, httpCode, map[string]any{
|
||||
"remote_ip": remoteIp,
|
||||
}, nil)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
|
18
request/abstract/parse_body.go
Normal file
18
request/abstract/parse_body.go
Normal file
@ -0,0 +1,18 @@
|
||||
// Package abstract ...
|
||||
//
|
||||
// Description : abstract ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-10-22 16:38
|
||||
package abstract
|
||||
|
||||
// RequestBodyParseAdaptor 解析请求body的接口适配器约束
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:39 2024/10/22
|
||||
type RequestBodyParseAdaptor interface {
|
||||
// Unmarshal 自定义反序列化的方法, 为 nil 则使用内置的序列化方式
|
||||
Unmarshal(sourceData []byte, receiver any) error
|
||||
}
|
@ -27,7 +27,7 @@ type contentType struct {
|
||||
//
|
||||
// Date : 00:35 2022/7/3
|
||||
func (ct *contentType) IsJson(ctx *gin.Context) bool {
|
||||
return strings.Contains(ct.Get(ctx), "json")
|
||||
return strings.Contains(WrapperHandle.GetContentType(ctx, "application/x-www-form-urlencoded"), "json")
|
||||
}
|
||||
|
||||
// IsFormURLEncoded 请求方式是否为 x-www-form-urlencoded
|
||||
@ -36,19 +36,5 @@ func (ct *contentType) IsJson(ctx *gin.Context) bool {
|
||||
//
|
||||
// Date : 00:36 2022/7/3
|
||||
func (ct *contentType) IsFormURLEncoded(ctx *gin.Context) bool {
|
||||
return strings.Contains(ct.Get(ctx), "x-www-form-urlencoded")
|
||||
}
|
||||
|
||||
// Get 获取请求类型
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 00:39 2022/7/3
|
||||
func (ct *contentType) Get(ctx *gin.Context) string {
|
||||
contentType := strings.ToLower(ctx.ContentType())
|
||||
if len(contentType) == 0 {
|
||||
// 请求不传type默认为 x-www-form-urlencoded
|
||||
contentType = "application/x-www-form-urlencoded"
|
||||
}
|
||||
return contentType
|
||||
return strings.Contains(WrapperHandle.GetContentType(ctx, "application/x-www-form-urlencoded"), "x-www-form-urlencoded")
|
||||
}
|
||||
|
@ -10,10 +10,13 @@ package request
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"github.com/mcuadros/go-defaults"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/gin/define"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@ -25,6 +28,7 @@ import (
|
||||
//
|
||||
// Date : 00:51 2022/7/3
|
||||
type form struct {
|
||||
checkInstance *validator.Validate
|
||||
}
|
||||
|
||||
// Parse 解析请求表单
|
||||
@ -33,32 +37,39 @@ type form struct {
|
||||
//
|
||||
// Date : 00:34 2022/7/3
|
||||
func (f *form) Parse(ctx *gin.Context, receiver interface{}) error {
|
||||
requestBody, _ := ioutil.ReadAll(ctx.Request.Body)
|
||||
handleConfig := define.GetHttpHandleConfig()
|
||||
requestBody, _ := io.ReadAll(ctx.Request.Body)
|
||||
// 请求信息写入上下文
|
||||
ctx.Set(define.RecordRequestDataField, string(requestBody))
|
||||
ctx.Set(handleConfig.RecordRequestDataField, string(requestBody))
|
||||
// 因为请求体被读一遍之后就没了,重新赋值 requestBody
|
||||
ctx.Request.Body = ioutil.NopCloser(bytes.NewReader(requestBody))
|
||||
ctx.Request.Body = io.NopCloser(bytes.NewReader(requestBody))
|
||||
method := strings.ToUpper(ctx.Request.Method)
|
||||
if method == http.MethodGet ||
|
||||
method == http.MethodPatch ||
|
||||
method == http.MethodTrace ||
|
||||
method == http.MethodConnect ||
|
||||
method == http.MethodOptions {
|
||||
return ctx.ShouldBindQuery(receiver)
|
||||
}
|
||||
|
||||
if method == http.MethodPost ||
|
||||
if err := ctx.ShouldBindQuery(receiver); nil != err {
|
||||
return err
|
||||
}
|
||||
} else if method == http.MethodPost ||
|
||||
method == http.MethodPut ||
|
||||
method == http.MethodDelete {
|
||||
if ContentType.IsJson(ctx) {
|
||||
return ctx.ShouldBindJSON(receiver)
|
||||
if err := ctx.ShouldBindJSON(receiver); nil != err {
|
||||
return err
|
||||
}
|
||||
} else if ContentType.IsFormURLEncoded(ctx) {
|
||||
if err := ctx.ShouldBind(receiver); nil != err {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return errors.New(ctx.ContentType() + " is not support")
|
||||
}
|
||||
|
||||
if ContentType.IsFormURLEncoded(ctx) {
|
||||
return ctx.ShouldBind(receiver)
|
||||
}
|
||||
|
||||
return errors.New(ctx.ContentType() + " is not support")
|
||||
} else {
|
||||
return errors.New(method + " : request method is not support")
|
||||
}
|
||||
return errors.New(method + " : request method is not support")
|
||||
// 设置默认值
|
||||
defaults.SetDefaults(receiver)
|
||||
return nil
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
// Date : 2022-07-03 00:52
|
||||
package request
|
||||
|
||||
import "github.com/go-playground/validator/v10"
|
||||
|
||||
var (
|
||||
// Form 表单相关操作
|
||||
Form *form
|
||||
@ -15,6 +17,8 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
Form = &form{}
|
||||
Form = &form{
|
||||
checkInstance: validator.New(),
|
||||
}
|
||||
ContentType = &contentType{}
|
||||
}
|
||||
|
128
request/parse_body/execute.go
Normal file
128
request/parse_body/execute.go
Normal file
@ -0,0 +1,128 @@
|
||||
// Package parse_body ...
|
||||
//
|
||||
// Description : parse_body ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-10-22 16:43
|
||||
package parse_body
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"git.zhangdeman.cn/zhangdeman/gin/request/abstract"
|
||||
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||
"github.com/gin-gonic/gin"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
requestBodyParseAdaptorTable = map[string]abstract.RequestBodyParseAdaptor{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
requestBodyParseAdaptorTable["xml"] = serialize.Xml
|
||||
requestBodyParseAdaptorTable["ini"] = serialize.Ini
|
||||
requestBodyParseAdaptorTable["yml"] = serialize.Yml
|
||||
requestBodyParseAdaptorTable["yaml"] = serialize.Yml
|
||||
requestBodyParseAdaptorTable["json"] = serialize.JSON
|
||||
requestBodyParseAdaptorTable["x-www-form-urlencoded"] = serialize.JSON
|
||||
}
|
||||
|
||||
// Register 注册适配器实例
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:45 2024/10/22
|
||||
func Register(requestType string, adaptor abstract.RequestBodyParseAdaptor) {
|
||||
if nil == adaptor {
|
||||
return
|
||||
}
|
||||
requestBodyParseAdaptorTable[requestType] = adaptor
|
||||
}
|
||||
|
||||
// Execute 解析请求BODY数据
|
||||
func Execute(ctx *gin.Context, receiver any) ([]byte, error) {
|
||||
contentType := strings.ToLower(strings.ReplaceAll(ctx.ContentType(), " ", ""))
|
||||
if len(contentType) == 0 {
|
||||
return []byte("{}"), nil
|
||||
}
|
||||
// 裁剪出真实的类型,之所以截取,是因为 content_type 中可能还包含编码信息, 如 : application/json;chaset=utf8
|
||||
contentTypeArr := strings.Split(contentType, ";")
|
||||
contentTypeFormatArr := strings.Split(contentTypeArr[0], "/")
|
||||
if len(contentTypeFormatArr) != 2 {
|
||||
return nil, errors.New(contentType + " : content_type format error")
|
||||
}
|
||||
// 裁剪出真实的类型,之所以截取,是因为 content_type 中可能还包含编码信息, 如 : application/json;charset=utf8
|
||||
contentType = contentTypeFormatArr[1]
|
||||
if _, exist := requestBodyParseAdaptorTable[contentType]; !exist {
|
||||
return nil, errors.New(contentType + " : adaptor not found")
|
||||
}
|
||||
var (
|
||||
bodyData []byte
|
||||
err error
|
||||
exist bool
|
||||
body any
|
||||
)
|
||||
|
||||
bodyKey := "_body_read_result"
|
||||
if body, exist = ctx.Get(bodyKey); exist && nil != body {
|
||||
bodyData = body.([]byte)
|
||||
} else {
|
||||
if bodyData, err = ReadBody(ctx); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
// 设置读取结果
|
||||
ctx.Set(bodyKey, bodyData)
|
||||
}
|
||||
|
||||
if err = requestBodyParseAdaptorTable[contentType].Unmarshal(bodyData, receiver); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
byteData := serialize.JSON.MarshalForByteIgnoreError(receiver)
|
||||
return byteData, nil
|
||||
}
|
||||
|
||||
// ExecuteForMap 高层级包装,表单解析为map
|
||||
func ExecuteForMap(ctx *gin.Context) (map[string]any, error) {
|
||||
var (
|
||||
err error
|
||||
result map[string]any
|
||||
)
|
||||
if _, err = Execute(ctx, &result); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ReadBody 读取请求Body
|
||||
func ReadBody(ctx *gin.Context) ([]byte, error) {
|
||||
var (
|
||||
data []byte
|
||||
err error
|
||||
)
|
||||
// 判断form url encode
|
||||
if strings.Contains(ctx.ContentType(), "x-www-form-urlencoded") {
|
||||
if err = ctx.Request.ParseForm(); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
body := map[string]any{}
|
||||
for paramName, itemParam := range ctx.Request.PostForm {
|
||||
if len(itemParam) > 0 {
|
||||
body[paramName] = itemParam[0]
|
||||
} else {
|
||||
body[paramName] = ""
|
||||
}
|
||||
}
|
||||
return serialize.JSON.MarshalForByteIgnoreError(body), nil
|
||||
}
|
||||
|
||||
// 读取Body信息
|
||||
if data, err = io.ReadAll(ctx.Request.Body); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
// 重置Body信息
|
||||
ctx.Request.Body = io.NopCloser(bytes.NewBuffer(data))
|
||||
return data, nil
|
||||
}
|
302
request/wrapper.go
Normal file
302
request/wrapper.go
Normal file
@ -0,0 +1,302 @@
|
||||
// Package request ...
|
||||
//
|
||||
// Description : gin ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2023-12-29 15:10
|
||||
package request
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/trace"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/gin/define"
|
||||
"git.zhangdeman.cn/zhangdeman/gin/request/parse_body"
|
||||
"git.zhangdeman.cn/zhangdeman/wrapper"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var (
|
||||
WrapperHandle = &wrapperHandle{}
|
||||
)
|
||||
|
||||
type wrapperHandle struct {
|
||||
}
|
||||
|
||||
// GetHeader 读取header
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:22 2023/12/25
|
||||
func (wh *wrapperHandle) GetHeader(ctx *gin.Context, headerKey string, defaultVal string) string {
|
||||
if nil == ctx {
|
||||
return defaultVal
|
||||
}
|
||||
val := ctx.GetHeader(headerKey)
|
||||
return wrapper.TernaryOperator.String(len(val) > 0, wrapper.String(val), wrapper.String(defaultVal)).Value()
|
||||
}
|
||||
|
||||
// GetCookie 读取cookie
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:23 2023/12/25
|
||||
func (wh *wrapperHandle) GetCookie(ctx *gin.Context, cookieName string, defaultVal string) string {
|
||||
if nil == ctx {
|
||||
return ""
|
||||
}
|
||||
val, err := ctx.Cookie(cookieName)
|
||||
if nil != err {
|
||||
return defaultVal
|
||||
}
|
||||
return wrapper.TernaryOperator.String(len(val) > 0, wrapper.String(val), wrapper.String(defaultVal)).Value()
|
||||
}
|
||||
|
||||
// GetUri 获取请求URI
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:13 2023/12/29
|
||||
func (wh *wrapperHandle) GetUri(ctx *gin.Context, defaultVal string) string {
|
||||
if nil != ctx && nil != ctx.Request && nil != ctx.Request.URL {
|
||||
return ctx.Request.URL.Path
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
// GetScheme 获取scheme
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:16 2024/1/2
|
||||
func (wh *wrapperHandle) GetScheme(ctx *gin.Context, defaultVal string) string {
|
||||
if nil != ctx && nil != ctx.Request && nil != ctx.Request.URL {
|
||||
return ctx.Request.URL.Scheme
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
// ParseQuery 获取query参数
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:03 2024/1/2
|
||||
func (wh *wrapperHandle) ParseQuery(ctx *gin.Context) map[string]string {
|
||||
query := make(map[string]string)
|
||||
if nil != ctx && nil != ctx.Request && nil != ctx.Request.URL {
|
||||
for paramName, valueList := range ctx.Request.URL.Query() {
|
||||
if len(valueList) == 0 {
|
||||
query[paramName] = ""
|
||||
} else {
|
||||
query[paramName] = valueList[0]
|
||||
}
|
||||
}
|
||||
return query
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
// GetMethod 获取请求方法
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:19 2024/1/2
|
||||
func (wh *wrapperHandle) GetMethod(ctx *gin.Context) string {
|
||||
if nil != ctx && nil != ctx.Request {
|
||||
return ctx.Request.Method
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetContentType 获取请求方式
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:24 2023/12/29
|
||||
func (wh *wrapperHandle) GetContentType(ctx *gin.Context, defaultVal string) string {
|
||||
if nil == ctx {
|
||||
return defaultVal
|
||||
}
|
||||
contentType := strings.ToLower(ctx.ContentType())
|
||||
return wrapper.TernaryOperator.String(len(contentType) > 0, wrapper.String(contentType), wrapper.String(defaultVal)).Value()
|
||||
}
|
||||
|
||||
// GetDomain 获取请求Domain
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:12 2024/9/19
|
||||
func (wh *wrapperHandle) GetDomain(ctx *gin.Context) string {
|
||||
if nil == ctx {
|
||||
return ""
|
||||
}
|
||||
return ctx.Request.Host
|
||||
}
|
||||
|
||||
// ParseBody 获取请求body
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 11:25 2024/7/26
|
||||
func (wh *wrapperHandle) ParseBody(ctx *gin.Context) (map[string]any, error) {
|
||||
return parse_body.ExecuteForMap(ctx)
|
||||
}
|
||||
|
||||
// GetResponseBody 获取响应body
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:18 2024/1/2
|
||||
func (wh *wrapperHandle) GetResponseBody(ctx *gin.Context, key string, defaultVal any) any {
|
||||
if nil == ctx {
|
||||
return defaultVal
|
||||
}
|
||||
if val, exist := ctx.Get(key); !exist || nil == val {
|
||||
return defaultVal
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
// GetClientIp 获取请求客户端IP
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:31 2024/7/23
|
||||
func (wh *wrapperHandle) GetClientIp(ctx *gin.Context, defaultVal string) string {
|
||||
if nil == ctx {
|
||||
return defaultVal
|
||||
}
|
||||
return ctx.ClientIP()
|
||||
}
|
||||
|
||||
// GetUserAgent 获取user_agent
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:32 2024/7/23
|
||||
func (wh *wrapperHandle) GetUserAgent(ctx *gin.Context, defaultVal string) string {
|
||||
if nil == ctx {
|
||||
return defaultVal
|
||||
}
|
||||
return ctx.Request.UserAgent()
|
||||
}
|
||||
|
||||
// GetCtxData 获取请求上下文数据
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:35 2024/7/23
|
||||
func (wh *wrapperHandle) GetCtxData(ctx *gin.Context, key string, defaultVal any) any {
|
||||
if nil == ctx {
|
||||
return defaultVal
|
||||
}
|
||||
if val, exist := ctx.Get(key); !exist || nil == val {
|
||||
return defaultVal
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
// GetCtxStringData 获取字符串数据
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:37 2024/7/23
|
||||
func (wh *wrapperHandle) GetCtxStringData(ctx *gin.Context, key string, defaultVal string) string {
|
||||
if nil == ctx {
|
||||
return defaultVal
|
||||
}
|
||||
val := ctx.GetString(key)
|
||||
if len(val) == 0 {
|
||||
return defaultVal
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// GetCtxIntData 获取int数据
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:39 2024/7/23
|
||||
func (wh *wrapperHandle) GetCtxIntData(ctx *gin.Context, key string, defaultVal int64) int64 {
|
||||
if nil == ctx {
|
||||
return defaultVal
|
||||
}
|
||||
val := wh.GetCtxData(ctx, key, nil)
|
||||
if nil == val {
|
||||
return defaultVal
|
||||
}
|
||||
return ctx.GetInt64(key)
|
||||
}
|
||||
|
||||
// ParseHeader 解析header数据
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:04 2024/9/23
|
||||
func (wh *wrapperHandle) ParseHeader(ctx *gin.Context) map[string]string {
|
||||
headerData := map[string]string{}
|
||||
if nil == ctx || nil == ctx.Request {
|
||||
return headerData
|
||||
}
|
||||
for headerName, headerVal := range ctx.Request.Header {
|
||||
if len(headerVal) > 0 {
|
||||
headerData[headerName] = headerVal[0]
|
||||
} else {
|
||||
headerData[headerName] = ""
|
||||
}
|
||||
}
|
||||
return headerData
|
||||
}
|
||||
|
||||
// ParseCookie 解析cookie数据
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:06 2024/9/23
|
||||
func (wh *wrapperHandle) ParseCookie(ctx *gin.Context) map[string]string {
|
||||
cookieData := map[string]string{}
|
||||
if nil == ctx || nil == ctx.Request {
|
||||
return cookieData
|
||||
}
|
||||
for _, itemCookie := range ctx.Request.Cookies() {
|
||||
cookieData[itemCookie.Name] = itemCookie.Value
|
||||
}
|
||||
return cookieData
|
||||
}
|
||||
|
||||
// GetLogicAfterResponse ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:54 2025/2/28
|
||||
func (wh *wrapperHandle) GetLogicAfterResponse(ctx *gin.Context) *define.LogicAfterResponse {
|
||||
if nil == ctx || nil == ctx.Request {
|
||||
return nil
|
||||
}
|
||||
l, exist := ctx.Get(define.LogicAfterResponseKey)
|
||||
if !exist || nil == l {
|
||||
l = &define.LogicAfterResponse{
|
||||
SuccessHookFuncList: make([]func(), 0),
|
||||
FailureHookFuncList: make([]func(), 0),
|
||||
Lock: &sync.RWMutex{},
|
||||
}
|
||||
ctx.Set(define.LogicAfterResponseKey, l)
|
||||
}
|
||||
// 就这么写, key值如果被其他人覆盖成非法值, 此处会直接panic
|
||||
return l.(*define.LogicAfterResponse)
|
||||
}
|
||||
|
||||
// GetCustomContext 获取自定义context
|
||||
func (wh *wrapperHandle) GetCustomContext(ctx *gin.Context) *define.Context {
|
||||
return define.NewContext(ctx)
|
||||
}
|
||||
|
||||
// GetTraceInstance 获取trace实例
|
||||
func (wh *wrapperHandle) GetTraceInstance(ctx *gin.Context) *trace.Runtime {
|
||||
return define.NewContext(ctx).Trace
|
||||
}
|
@ -8,8 +8,11 @@
|
||||
package response
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.zhangdeman.cn/zhangdeman/dynamic-struct/wrapper"
|
||||
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/exception"
|
||||
@ -17,14 +20,43 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
// hasSendResponseFlag 已经发送响应数据的标识, 一旦识别到, 重复调用不会执行
|
||||
hasSendResponseFlag = "GIN_PKG_HAS_SEND_RESPONSE"
|
||||
)
|
||||
|
||||
var (
|
||||
// 成功的业务状态码
|
||||
successBusinessCode any = "200"
|
||||
)
|
||||
|
||||
// SetBusinessSuccessCode 设置成状态码
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:01 2024/8/17
|
||||
func SetBusinessSuccessCode(code any) {
|
||||
successBusinessCode = code
|
||||
}
|
||||
|
||||
// Success 成功的响应
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 22:37 2022/6/25
|
||||
func Success(ctx *gin.Context, data interface{}) {
|
||||
func Success(ctx *gin.Context, data any) {
|
||||
successException := exception.NewSuccess(data)
|
||||
Send(ctx, successException.GetCode(), successException.GetHttpCode(), successException.GetData())
|
||||
Send(ctx, successException.Code(), http.StatusOK, successException.Data(), nil)
|
||||
}
|
||||
|
||||
// SuccessWithExtension 返回扩展数据
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 14:52 2024/9/24
|
||||
func SuccessWithExtension(ctx *gin.Context, data any, responseOption *define.ResponseOption) {
|
||||
successException := exception.NewSuccess(data)
|
||||
Send(ctx, successException.Code(), http.StatusOK, successException.Data(), responseOption)
|
||||
}
|
||||
|
||||
// Send 基础的发送数据
|
||||
@ -32,23 +64,69 @@ func Success(ctx *gin.Context, data interface{}) {
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 22:40 2022/6/25
|
||||
func Send(ctx *gin.Context, code interface{}, httpCode int, data interface{}) {
|
||||
finishRequestTime := time.Now().UnixNano()
|
||||
responseData := map[string]interface{}{
|
||||
define.ResponseCodeField: code,
|
||||
define.ResponseMessageField: exception.GetMessage(code),
|
||||
define.ResponseTraceIDField: ctx.GetString(define.TraceIDField),
|
||||
define.ResponseRequestIDField: ctx.GetString(define.RequestIDField),
|
||||
define.ResponseDataField: data,
|
||||
define.HandleRequestCostField: (finishRequestTime - ctx.GetInt64(define.StartRequestTimeField)) / 1e6,
|
||||
func Send(ctx *gin.Context, code any, httpCode int, data any, responseOption *define.ResponseOption) {
|
||||
if nil == responseOption {
|
||||
responseOption = &define.ResponseOption{
|
||||
ContentType: "application/json; charset=utf-8",
|
||||
Extension: make(map[string]any),
|
||||
}
|
||||
}
|
||||
// 记录响应数据
|
||||
recordData, _ := json.Marshal(responseData)
|
||||
if len(responseOption.ContentType) == 0 {
|
||||
responseOption.ContentType = "application/json; charset=utf-8"
|
||||
}
|
||||
if len(responseOption.XmlName) == 0 {
|
||||
responseOption.XmlName = "ResponseData"
|
||||
}
|
||||
// 设置请求是否成功的标识
|
||||
ctx.Set(define.GetHttpHandleConfig().RequestIsSuccessField, fmt.Sprintf("%v", code) == fmt.Sprintf("%v", successBusinessCode))
|
||||
if ctx.GetBool(hasSendResponseFlag) {
|
||||
// 已经发送过数据, 后面在发送数据, 不执行
|
||||
return
|
||||
}
|
||||
// 设置数据已发送的标识
|
||||
defer ctx.Set(hasSendResponseFlag, true)
|
||||
finishRequestTime := time.Now()
|
||||
responseData := BuildResponseData(ctx, finishRequestTime, code, data, responseOption.Extension)
|
||||
// 记录完成时间
|
||||
ctx.Set(define.FinishRequestTimeField, finishRequestTime)
|
||||
ctx.Set(define.RecordResponseDataField, string(recordData))
|
||||
responseException := exception.New(code, httpCode, responseData)
|
||||
ctx.JSON(responseException.GetHttpCode(), responseException.GetData())
|
||||
responseConfig := define.GetHttpHandleConfig()
|
||||
ctx.Set(responseConfig.FinishRequestTimeField, finishRequestTime.UnixMilli())
|
||||
ctx.Set(responseConfig.ResponseDataField, responseData)
|
||||
responseException := exception.New(code, responseData)
|
||||
responseContentType := getResponseDataType(responseOption.ContentType)
|
||||
responseStr := serialize.JSON.MarshalForStringIgnoreError(responseException.Data())
|
||||
if responseInstance, err := wrapper.NewJson(responseStr, &wrapper.Option{XmlName: responseOption.XmlName}); nil != err {
|
||||
// 接口处理成功, 数据序列化出现异常
|
||||
panic(err.Error()) // 直接panic, 交给gin处理
|
||||
} else {
|
||||
finalResponseData, _ := responseInstance.Marshal(responseContentType)
|
||||
ctx.Data(http.StatusOK, responseOption.ContentType, finalResponseData)
|
||||
}
|
||||
}
|
||||
|
||||
// getResponseDataType 获取相应数据类型
|
||||
func getResponseDataType(contentType string) string {
|
||||
if contentType == "" {
|
||||
return "json"
|
||||
}
|
||||
applicationInfo := strings.Split(contentType, ";")[0]
|
||||
applicationInfoArr := strings.Split(applicationInfo, "/")
|
||||
return applicationInfoArr[len(applicationInfoArr)-1]
|
||||
}
|
||||
|
||||
// BuildResponseData 构建响应数据
|
||||
func BuildResponseData(ctx *gin.Context, finishTime time.Time, code any, data any, extension map[string]any) map[string]any {
|
||||
responseConfig := define.GetHttpHandleConfig()
|
||||
responseData := map[string]any{
|
||||
responseConfig.ResponseCodeField: code,
|
||||
responseConfig.ResponseMessageField: exception.GetMessage(code),
|
||||
responseConfig.ResponseTraceIDField: ctx.GetString(responseConfig.ResponseTraceIDField),
|
||||
responseConfig.ResponseDataField: data,
|
||||
responseConfig.HandleRequestCostField: finishTime.UnixMilli() - ctx.GetInt64(responseConfig.StartRequestTimeField),
|
||||
}
|
||||
if responseConfig.EnableExtensionOutput && nil != extension {
|
||||
responseData[responseConfig.ExtensionOutputField] = extension
|
||||
}
|
||||
return responseData
|
||||
}
|
||||
|
||||
// SendWithStatusOK ...
|
||||
@ -56,8 +134,8 @@ func Send(ctx *gin.Context, code interface{}, httpCode int, data interface{}) {
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 22:41 2022/6/25
|
||||
func SendWithStatusOK(ctx *gin.Context, code interface{}, data map[string]interface{}) {
|
||||
Send(ctx, code, http.StatusOK, data)
|
||||
func SendWithStatusOK(ctx *gin.Context, code any, data any) {
|
||||
Send(ctx, code, http.StatusOK, data, nil)
|
||||
}
|
||||
|
||||
// SendWithException 使用exception发送数据
|
||||
@ -65,13 +143,30 @@ func SendWithStatusOK(ctx *gin.Context, code interface{}, data map[string]interf
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 13:08 2022/6/26
|
||||
func SendWithException(ctx *gin.Context, e exception.IException, data interface{}) {
|
||||
func SendWithException(ctx *gin.Context, e exception.IException, responseOption *define.ResponseOption) {
|
||||
if nil == e {
|
||||
e = exception.NewSuccess(data)
|
||||
e = exception.NewSuccess(map[string]any{})
|
||||
}
|
||||
if nil != e.GetData() {
|
||||
Send(ctx, e.GetCode(), e.GetHttpCode(), e.GetData())
|
||||
} else {
|
||||
Send(ctx, e.GetCode(), e.GetHttpCode(), data)
|
||||
if !define.GetHttpHandleConfig().DisableDebugStackOutput && nil != e {
|
||||
stack := e.GetStack()
|
||||
if len(stack) > 0 {
|
||||
fmt.Println(stack)
|
||||
}
|
||||
}
|
||||
Send(ctx, e.Code(), http.StatusOK, e.Data(), responseOption)
|
||||
}
|
||||
|
||||
// JSON ctx.JSON 的平替, 增加了数据是否已相应的标识
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 14:51 2023/2/15
|
||||
func JSON(ctx *gin.Context, httpCode int, data any) {
|
||||
if ctx.GetBool(hasSendResponseFlag) {
|
||||
// 已经发送过数据, 后面在发送数据, 不执行
|
||||
return
|
||||
}
|
||||
// 设置数据已发送的标识
|
||||
defer ctx.Set(hasSendResponseFlag, true)
|
||||
ctx.JSON(httpCode, data)
|
||||
}
|
||||
|
110
router/README.md
Normal file
110
router/README.md
Normal file
@ -0,0 +1,110 @@
|
||||
# 路由包使用说明
|
||||
|
||||
## 基础说明
|
||||
|
||||
- 基于 [web框架GIN](https://github.com/gin-gonic/gin) 的二次封装
|
||||
|
||||
## 为什么要二次封装
|
||||
|
||||
二次封装的想法来源于 [web框架GoFrame](https://github.com/gogf/gf)。gf框架本身内置集成了大量组件, 对于轻量应用开发, 相比较而言比较笨重, 但是启根据请求数据结构、响应数据结构自动生成表单的能力十分实用。
|
||||
在采用gin开发时,一般接口文档有两种选择 :
|
||||
- 在内部文档平台手搓文档, 并人工维护, 此方案最终演变方式大概率为文档主键滞后,与接口实现并不一致
|
||||
- 利用golang的文档生成工具[swag]() 自动生成swagger文档, 此种方案需要在代码中引入大量swag工具解析时需要的注释, 且当输入/输出发生变化时需要维护相关注释
|
||||
基于以上问题, 对gin的路由注册进行二次封装
|
||||
|
||||
## 二次封装解决哪些问题
|
||||
|
||||
- 文档维护的繁琐性 : 会通过反射自动基于 **`请求的数据结构 + 返回的数据结构`** , 生成接口文档, 专注于数据结构设计即可, 无需关注文档相关内容, 程序接管, 自动生成
|
||||
- 做接口设计的统一性规范约束, 如 :
|
||||
- 入参数据类型不能为 any
|
||||
- 入参类型不能为 map , 但是特定字段特定场景, 可以强行禁用此规则等
|
||||
- 返回值必须为结构体等
|
||||
|
||||
## 设计方案
|
||||
|
||||
### 【可选】路由组定义
|
||||
|
||||
路由组可以通过函数进行定义, 函数名必须需为 : **`RouterPrefix`** , 函数无任何参数, 返回路由组名称, 示例 :
|
||||
|
||||
```go
|
||||
type TestController struct{}
|
||||
|
||||
func (t *TestController) RouterPrefix() string {
|
||||
return "/uri/prefix"
|
||||
}
|
||||
```
|
||||
|
||||
### 【可选】路由组中间件定义
|
||||
|
||||
路由组中间件可以通过函数进行定义, 函数名必须需为 : **`RouterMiddleware`** , 函数无任何参数, 返回路由组中间件列表 **`[]gin.HandlerFunc`**, 示例 :
|
||||
|
||||
```go
|
||||
type TestController struct{}
|
||||
|
||||
func (t *TestController) RouterPrefix() string {
|
||||
return "/uri/prefix"
|
||||
}
|
||||
|
||||
func (t *TestController) RouterMiddleware() []gin.HandlerFunc {
|
||||
return []gin.HandlerFunc{
|
||||
func(ctx *gin.Context) {
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 接口逻辑处理定义
|
||||
|
||||
接口逻辑处理函数, 函数名称自定义, 必须是个 **`可导出函数`** , 函数接收两个参数, 分别是 :
|
||||
|
||||
- *gin.Context : gin框架的上下文信息
|
||||
- any: 表单参数 **结构体指针** , 注意 : 类型必须为结构体指针
|
||||
|
||||
函数返回值有两个 :
|
||||
- any : 返回的业务数据, 必须是个 **`结构体指针`**
|
||||
- error : 可以是内置的 **error** , 也可以是 **exception.IException** , 建议使用 exception.IException , 可承载更多的异常信息
|
||||
|
||||
## 使用方式
|
||||
|
||||
```go
|
||||
type TestController struct{}
|
||||
|
||||
func (t *TestController) RouterPrefix() string {
|
||||
return "/uri/prefix"
|
||||
}
|
||||
|
||||
func (t *TestController) RouterMiddleware() []gin.HandlerFunc {
|
||||
return []gin.HandlerFunc{
|
||||
func(ctx *gin.Context) {
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
func (t *TestController) Uri(ctx *gin.Context, formData *TestForm) (any, error) {
|
||||
return formData, nil
|
||||
}
|
||||
|
||||
type TestForm struct {
|
||||
Meta `tag:"测试表单" path:"/a/b/c/d" desc:"测试接口" method:"get" strict:"true"`
|
||||
Age int `json:"age" form:"age"`
|
||||
Name string `json:"name" form:"name"`
|
||||
Test *Test `json:"test" form:"test"`
|
||||
Num *int64 `json:"num" form:"num"`
|
||||
}
|
||||
type Test struct {
|
||||
L string `json:"l"`
|
||||
}
|
||||
|
||||
func Test_parseController(t *testing.T) {
|
||||
type args struct {
|
||||
controller any
|
||||
}
|
||||
Register(8080, &TestController{})
|
||||
}
|
||||
```
|
||||
|
||||
注意事项:
|
||||
|
||||
- Register 方法第一个入参是监听的端口
|
||||
- 后面若干个controller实例, 朱一必须以指针的方式传入
|
25
router/config.go
Normal file
25
router/config.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Package router ...
|
||||
//
|
||||
// Description : router ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-02-07 17:41
|
||||
package router
|
||||
|
||||
import "strings"
|
||||
|
||||
var defaultValidateErrTag = "err"
|
||||
|
||||
// SetValidateErrTag 设置验证失败时, 获取错误信息的tag字段
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:42 2025/2/7
|
||||
func SetValidateErrTag(tagName string) {
|
||||
tagName = strings.TrimSpace(tagName)
|
||||
if tagName == "" {
|
||||
return
|
||||
}
|
||||
defaultValidateErrTag = tagName
|
||||
}
|
152
router/controller.go
Normal file
152
router/controller.go
Normal file
@ -0,0 +1,152 @@
|
||||
// Package router ...
|
||||
//
|
||||
// Description : router ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-01-27 15:29
|
||||
package router
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// controller 解析controller有哪些方法要注册为接口
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:30 2025/1/27
|
||||
type controller struct {
|
||||
}
|
||||
|
||||
// Parse 执行解析
|
||||
//
|
||||
// 符合注册为借口的方法具有如下特征:
|
||||
//
|
||||
// 1. 函数接受两个入参, 第一个参数为 gin.Context , 第二个参数为 任意结构体指针, 但是必须声明 Meta 相关信息, 否则会报错
|
||||
//
|
||||
// 2. 函数有两个返回值, 第一个返回值为任意结构体/结构体指针(限制死不能为map/slice, 方便后续统一标准化) , 第二个返回值为 error , 代表处理的异常, 会自动适配 exception.IException 类型
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:31 2025/1/27
|
||||
func (c controller) Parse(inputController any) map[string]UriConfig {
|
||||
parseRes := make(map[string]UriConfig)
|
||||
if nil == inputController {
|
||||
return parseRes
|
||||
}
|
||||
controllerType := reflect.TypeOf(inputController)
|
||||
inputController = reflect.New(controllerType).Interface()
|
||||
controllerType = reflect.TypeOf(inputController)
|
||||
controllerValue := reflect.ValueOf(inputController)
|
||||
for methodIdx := 0; methodIdx < controllerType.NumMethod(); methodIdx++ {
|
||||
uriCfg, needRegister := c.methodConfig(controllerType.Method(methodIdx))
|
||||
if !needRegister {
|
||||
continue
|
||||
}
|
||||
uriCfg.ApiStructValue = controllerValue
|
||||
parseRes[uriCfg.Path] = uriCfg
|
||||
}
|
||||
return parseRes
|
||||
}
|
||||
|
||||
// methodConfig 解析方法配置, 要求函数格式, 两个参数, 两个返回值, 格式 : func(ctx *gin.Context, formData anyStruct[组合Meta]) (anyStruct|map[response], error)
|
||||
//
|
||||
// 参数 : 方法反射结果
|
||||
//
|
||||
// 返回值 : 第一个 -> 解析出的接口配置 第二个 -> 是否要注册为接口
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:05 2025/1/27
|
||||
func (c controller) methodConfig(reflectMethod reflect.Method) (cfg UriConfig, needRegister bool) {
|
||||
methodType := reflectMethod.Type
|
||||
// num0: 函数声明
|
||||
// num1: 第一个参数
|
||||
// num2: 第二个参数
|
||||
if methodType.NumIn() != 3 {
|
||||
needRegister = false
|
||||
return
|
||||
}
|
||||
// 第一个参数必须是 *gin.Context 或者 *define.Context
|
||||
paramOne := methodType.In(1).String()
|
||||
if paramOne != GinContextType && paramOne != CustomContextType {
|
||||
needRegister = false
|
||||
return
|
||||
}
|
||||
// 解析第二个参数是组合Meta的form表单
|
||||
formType := methodType.In(2)
|
||||
cfg.FormDataType = formType
|
||||
if formType.Kind() == reflect.Ptr {
|
||||
formType = methodType.In(2).Elem()
|
||||
}
|
||||
metaField, metaFieldExist := formType.FieldByName(FieldNameMeta)
|
||||
if !metaFieldExist {
|
||||
needRegister = false
|
||||
return
|
||||
}
|
||||
cfg.ResultDataType = methodType.Out(0)
|
||||
if cfg.ResultDataType == nil {
|
||||
}
|
||||
if methodType.Out(1).Kind().String() != ErrorType {
|
||||
// 判断是否是实现 error接口的方法
|
||||
outputErrParse := false
|
||||
for j := 0; j < methodType.Out(1).NumMethod(); j++ {
|
||||
if methodType.Out(1).Method(j).Name == ErrorInterfaceFuncName && // 实现Error方法
|
||||
methodType.Out(1).Method(j).Type.NumIn() == 0 && // 没有任何参数
|
||||
methodType.Out(1).Method(j).Type.NumOut() == 1 && // 一个返回值
|
||||
methodType.Out(1).Method(j).Type.Out(0).Kind().String() == reflect.String.String() {
|
||||
outputErrParse = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !outputErrParse {
|
||||
needRegister = false
|
||||
return
|
||||
}
|
||||
}
|
||||
// 解析meta信息
|
||||
cfg.CtxType = paramOne
|
||||
cfg.Path = metaField.Tag.Get(TagNamePath)
|
||||
cfg.RequestMethod = metaField.Tag.Get(TagNameMethod)
|
||||
cfg.Desc = metaField.Tag.Get(TagNameDesc)
|
||||
cfg.TagList = strings.Split(metaField.Tag.Get(TagNameUriTag), ",")
|
||||
// 解析第一个返回值, 要求必须是结构体或者是map
|
||||
outputStrictModel := metaField.Tag.Get(TagNameOutputStrict)
|
||||
cfg.OutputStrict = outputStrictModel == "1" || outputStrictModel == "true"
|
||||
if cfg.OutputStrict {
|
||||
// 开启输出严格模式校验
|
||||
if methodType.Out(0).Kind() != reflect.Struct && methodType.Out(0).Kind() != reflect.Map {
|
||||
panic(cfg.Path + " : 接口配置输出严格校验, 输出数据类型必须为 struct 或 *struct 或 map, 实际返回数据类型 : " + methodType.Out(0).Kind().String())
|
||||
return
|
||||
}
|
||||
}
|
||||
// 解析参数配置
|
||||
//cfg.ParamList = c.parseParamConfig(formType)
|
||||
cfg.ApiLogicFunc = reflectMethod
|
||||
needRegister = true
|
||||
return
|
||||
}
|
||||
|
||||
// parseParamConfig 解析参数配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 14:35 2025/2/7
|
||||
func (c controller) parseParamConfig(formDataType reflect.Type) []UriParam {
|
||||
res := make([]UriParam, 0)
|
||||
for i := 0; i < formDataType.NumField(); i++ {
|
||||
structField := formDataType.Field(i)
|
||||
if structField.Name == FieldNameMeta {
|
||||
// Meta 字段, 忽略
|
||||
continue
|
||||
}
|
||||
jsonTag := structField.Tag.Get("json")
|
||||
if jsonTag == "" {
|
||||
jsonTag = structField.Name
|
||||
}
|
||||
|
||||
}
|
||||
return res
|
||||
}
|
69
router/define.go
Normal file
69
router/define.go
Normal file
@ -0,0 +1,69 @@
|
||||
// Package router ...
|
||||
//
|
||||
// Description : router ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-07-20 22:57
|
||||
package router
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const (
|
||||
PrefixFuncName = "RouterPrefix" // 路由前缀函数名称
|
||||
MiddlewareFuncName = "RouterMiddleware" // 路由中间件函数名称
|
||||
GinContextType = "*gin.Context" // gin context 类型名称
|
||||
CustomContextType = "*define.Context" // custom context 类型名称
|
||||
ErrorType = "error" // error类型
|
||||
ErrorInterfaceFuncName = "Error" // error接口需要实现的方法名称
|
||||
)
|
||||
|
||||
const (
|
||||
TagNamePath = "path" // 接口的请求路径
|
||||
TagNameMethod = "method" // 接口的请求方法
|
||||
TagNameUriTag = "tag" // 接口的tag
|
||||
TagNameDesc = "desc" // 接口的描述
|
||||
TagNameOutputStrict = "output_strict" // 接口数据是否为严格模式 : 严格模式, 响应数据必须是结构体/map,非严格模式返回任意值
|
||||
TagNameBinding = "binding" // gin 内置的验证规则tag
|
||||
TagNameValidate = "validate" // validator v10 默认的验证规则tag
|
||||
TagNameErrMsg = "err" // 验证失败错误信息tag
|
||||
)
|
||||
|
||||
// UriConfig 接口配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:41 2024/7/21
|
||||
type UriConfig struct {
|
||||
Path string `json:"path"` // 接口路由, 必须配置
|
||||
RequestMethod string `json:"request_method"` // 接口请求方法, 必须配置
|
||||
TagList []string `json:"tag_list"` // 接口分组
|
||||
Desc string `json:"desc"` // 接口描述
|
||||
OutputStrict bool `json:"output_strict"` // 接口是否为严格模式 : 不配置,可返回任意类型, 配置, 必须返回结构体或者map
|
||||
CtxType string `json:"ctx_type"` // ctx参数类型
|
||||
FormDataType reflect.Type `json:"-"` // 表单数据类型
|
||||
ResultDataType reflect.Type `json:"-"` // 返回值数据类型
|
||||
ApiStructValue reflect.Value `json:"-"` // 逻辑函数所属结构体取值
|
||||
ApiLogicFunc reflect.Method `json:"-"` // 自定义的接口逻辑
|
||||
}
|
||||
|
||||
// UriParam 接口参数配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:40 2025/1/27
|
||||
type UriParam struct {
|
||||
Field string `json:"field"` // 结构体字段
|
||||
Name string `json:"name"` // 参数名称
|
||||
Type string `json:"type"` // 参数类型
|
||||
Validate string `json:"validate"` // 验证规则: validator/v10 库
|
||||
ErrorMsg string `json:"error_msg"` // 验证失败的错误信息
|
||||
DisableAutoType bool `json:"disable_auto_type"` // 禁用自动类型转换
|
||||
Sort string `json:"sort"` // 参数读取顺序: 默认 POST : body > query > path GET : query > path > body
|
||||
}
|
||||
|
||||
const (
|
||||
FieldNameMeta = "Meta" // 元信息字段
|
||||
)
|
43
router/doc.go
Normal file
43
router/doc.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Package router ...
|
||||
//
|
||||
// Description : router ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-02-14 21:48
|
||||
package router
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
apiDoc "git.zhangdeman.cn/gateway/api-doc"
|
||||
"git.zhangdeman.cn/gateway/api-doc/define"
|
||||
)
|
||||
|
||||
func NewDoc(info *define.Info, servers []*define.ServerItem) *Doc {
|
||||
return &Doc{
|
||||
instance: apiDoc.NewOpenapiDoc(info, servers),
|
||||
}
|
||||
}
|
||||
|
||||
type Doc struct {
|
||||
instance *apiDoc.Generate
|
||||
}
|
||||
|
||||
// Add 增加接口文档测试
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:55 2025/2/14
|
||||
func (d *Doc) Add(routePrefix string, paramType reflect.Type, resultType reflect.Type) {
|
||||
_ = d.instance.AddApiFromInAndOut(routePrefix, paramType, resultType)
|
||||
}
|
||||
|
||||
// Data 文档数据
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:59 2025/2/14
|
||||
func (d *Doc) Data() *define.OpenapiDoc {
|
||||
return d.instance.Doc()
|
||||
}
|
120
router/handler.go
Normal file
120
router/handler.go
Normal file
@ -0,0 +1,120 @@
|
||||
// Package router ...
|
||||
//
|
||||
// Description : router ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-01-27 19:42
|
||||
package router
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/exception"
|
||||
"git.zhangdeman.cn/zhangdeman/gin/define"
|
||||
"git.zhangdeman.cn/zhangdeman/gin/request"
|
||||
"git.zhangdeman.cn/zhangdeman/gin/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// RequestHandler 获取请求处理方法
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 19:44 2025/1/27
|
||||
func RequestHandler(uriCfg UriConfig) gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
var (
|
||||
err error
|
||||
ok bool
|
||||
e exception.IException
|
||||
)
|
||||
|
||||
var formParam reflect.Value
|
||||
if uriCfg.FormDataType.Kind() == reflect.Ptr {
|
||||
formParam = reflect.New(uriCfg.FormDataType.Elem())
|
||||
} else {
|
||||
// 表单解析
|
||||
formParam = reflect.New(uriCfg.FormDataType)
|
||||
}
|
||||
formValue := formParam.Interface()
|
||||
if err = request.Form.Parse(ctx, formValue); nil != err {
|
||||
// 格式化验证错误的信息
|
||||
err = GetValidateErr(formValue, err)
|
||||
e = exception.NewFromError(400, err)
|
||||
response.SendWithException(ctx, e, &define.ResponseOption{
|
||||
ContentType: "application/json;charset=utf-8",
|
||||
})
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
isSuccess := false
|
||||
// 初始化响应之后logic
|
||||
logicAfterResponse := &define.LogicAfterResponse{
|
||||
SuccessHookFuncList: make([]func(), 0),
|
||||
FailureHookFuncList: make([]func(), 0),
|
||||
Lock: &sync.RWMutex{},
|
||||
}
|
||||
// 此处暴露出去,是为了使用方可以获取到对应数据
|
||||
ctx.Set(define.LogicAfterResponseKey, logicAfterResponse)
|
||||
defer func() {
|
||||
go func() {
|
||||
// 执行响应之后的相关逻辑
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
||||
}
|
||||
}()
|
||||
if isSuccess {
|
||||
for _, itemFunc := range logicAfterResponse.SuccessHookFuncList {
|
||||
if nil != itemFunc {
|
||||
itemFunc()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, itemFunc := range logicAfterResponse.FailureHookFuncList {
|
||||
if nil != itemFunc {
|
||||
itemFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}()
|
||||
// 执行逻辑
|
||||
inputValue := reflect.ValueOf(formValue)
|
||||
if uriCfg.FormDataType.Kind() != reflect.Ptr {
|
||||
inputValue = inputValue.Elem()
|
||||
}
|
||||
var firstParam reflect.Value
|
||||
if uriCfg.CtxType == CustomContextType {
|
||||
customCtx := ctx.MustGet(define.CustomContextKey)
|
||||
firstParam = reflect.ValueOf(customCtx)
|
||||
} else {
|
||||
firstParam = reflect.ValueOf(ctx)
|
||||
}
|
||||
resList := uriCfg.ApiLogicFunc.Func.Call([]reflect.Value{uriCfg.ApiStructValue, firstParam, inputValue})
|
||||
if resList[1].IsNil() {
|
||||
// 请求成功
|
||||
isSuccess = true
|
||||
response.SuccessWithExtension(ctx, resList[0].Interface(), &define.ResponseOption{ContentType: "application/json;charset=utf-8"})
|
||||
return
|
||||
}
|
||||
// 请求失败
|
||||
if ok = errors.As(resList[1].Interface().(error), &e); ok {
|
||||
// 本身就是exception.IException
|
||||
} else if err, ok = resList[1].Interface().(error); ok {
|
||||
e = exception.NewFromError(-1, err)
|
||||
} else {
|
||||
e = exception.NewWithCodeAndData(-1, map[string]any{
|
||||
"err": resList[1].Interface(),
|
||||
})
|
||||
}
|
||||
response.SendWithException(ctx, e, &define.ResponseOption{
|
||||
ContentType: "application/json;charset=utf-8",
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
19
router/meta.go
Normal file
19
router/meta.go
Normal file
@ -0,0 +1,19 @@
|
||||
// Package router ...
|
||||
//
|
||||
// Description : router ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-07-20 21:40
|
||||
package router
|
||||
|
||||
// Meta 接口的元信息, 主要包含如下信息:
|
||||
//
|
||||
// uri: 接口路由(不包含group前缀)
|
||||
//
|
||||
// method: 请求方法: get/post 等
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:40 2024/7/20
|
||||
type Meta struct{}
|
151
router/option.go
Normal file
151
router/option.go
Normal file
@ -0,0 +1,151 @@
|
||||
// Package router ...
|
||||
//
|
||||
// Description : router ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-02-18 17:26
|
||||
package router
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
apiDocDefine "git.zhangdeman.cn/gateway/api-doc/define"
|
||||
"git.zhangdeman.cn/zhangdeman/gin/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type SetServerOptionFunc func(so *serverOption)
|
||||
|
||||
// serverOption 获取server实例的选项
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:28 2025/2/18
|
||||
type serverOption struct {
|
||||
swaggerUiTheme string // swagger 主题
|
||||
swaggerBaseUri string // swagger基础path
|
||||
globalMiddlewareList []gin.HandlerFunc // 全局中间件列表
|
||||
disableSwaggerDoc bool // 禁用swagger文档, 特定环境不想展示文档, 可通过次方式禁用
|
||||
serverInfo *apiDocDefine.Info // 服务器信息
|
||||
serverList []*apiDocDefine.ServerItem // 服务器环境列表
|
||||
enablePprof bool // 启用pprof
|
||||
enableCors bool // 启动跨域支持
|
||||
loggerCfg *middleware.AccessConfig // 日志配置
|
||||
}
|
||||
|
||||
// WithSwaggerUITheme 设置swaggerUI主题
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:29 2025/2/18
|
||||
func WithSwaggerUITheme(uiTheme string) SetServerOptionFunc {
|
||||
return func(so *serverOption) {
|
||||
uiTheme = strings.TrimSpace(uiTheme)
|
||||
if len(uiTheme) == 0 {
|
||||
return
|
||||
}
|
||||
so.swaggerUiTheme = uiTheme
|
||||
}
|
||||
}
|
||||
|
||||
// WithGlobalMiddlewareList 设置全局中间件
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:33 2025/2/18
|
||||
func WithGlobalMiddlewareList(middlewareList ...gin.HandlerFunc) SetServerOptionFunc {
|
||||
return func(so *serverOption) {
|
||||
so.globalMiddlewareList = middlewareList
|
||||
}
|
||||
}
|
||||
|
||||
// WithSwaggerBaseUri ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:05 2025/2/18
|
||||
func WithSwaggerBaseUri(baseUri string) SetServerOptionFunc {
|
||||
return func(so *serverOption) {
|
||||
baseUri = strings.TrimSpace(baseUri)
|
||||
if len(baseUri) == 0 {
|
||||
return
|
||||
}
|
||||
baseUri = "/" + strings.TrimLeft(baseUri, "/")
|
||||
so.swaggerBaseUri = baseUri
|
||||
}
|
||||
}
|
||||
|
||||
// WithDisableSwaggerDoc 禁用swagger文档
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:35 2025/2/18
|
||||
func WithDisableSwaggerDoc() SetServerOptionFunc {
|
||||
return func(so *serverOption) {
|
||||
so.disableSwaggerDoc = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithServerInfo 设置serverInfo
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:51 2025/2/18
|
||||
func WithServerInfo(serverInfo *apiDocDefine.Info) SetServerOptionFunc {
|
||||
return func(so *serverOption) {
|
||||
if nil == serverInfo {
|
||||
return
|
||||
}
|
||||
so.serverInfo = serverInfo
|
||||
}
|
||||
}
|
||||
|
||||
// WithServerList 设置服务器列表
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:52 2025/2/18
|
||||
func WithServerList(serverList []*apiDocDefine.ServerItem) SetServerOptionFunc {
|
||||
return func(so *serverOption) {
|
||||
if len(serverList) == 0 {
|
||||
return
|
||||
}
|
||||
so.serverList = serverList
|
||||
}
|
||||
}
|
||||
|
||||
// WithPprofEnable 启用pprof
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:10 2025/2/21
|
||||
func WithPprofEnable() SetServerOptionFunc {
|
||||
return func(so *serverOption) {
|
||||
so.enablePprof = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithEnableCors 启用全局跨域
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 14:56 2025/2/22
|
||||
func WithEnableCors() SetServerOptionFunc {
|
||||
return func(so *serverOption) {
|
||||
so.enableCors = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithLoggerCfg ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:25 2025/2/22
|
||||
func WithLoggerCfg(loggerCfg *middleware.AccessConfig) SetServerOptionFunc {
|
||||
return func(so *serverOption) {
|
||||
if nil != loggerCfg || nil != loggerCfg.Logger {
|
||||
so.loggerCfg = loggerCfg
|
||||
}
|
||||
}
|
||||
}
|
218
router/server.go
Normal file
218
router/server.go
Normal file
@ -0,0 +1,218 @@
|
||||
// Package router ...
|
||||
//
|
||||
// Description : router ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-02-07 18:19
|
||||
package router
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.zhangdeman.cn/zhangdeman/graceful"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/gin/define"
|
||||
|
||||
apiDoc "git.zhangdeman.cn/gateway/api-doc"
|
||||
"git.zhangdeman.cn/zhangdeman/consts"
|
||||
"git.zhangdeman.cn/zhangdeman/gin/middleware"
|
||||
"git.zhangdeman.cn/zhangdeman/gin/middleware/request_cors"
|
||||
"github.com/gin-contrib/pprof"
|
||||
|
||||
apiDocDefine "git.zhangdeman.cn/gateway/api-doc/define"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func newServerOption(port int, optionList ...SetServerOptionFunc) *serverOption {
|
||||
option := &serverOption{
|
||||
swaggerUiTheme: apiDocDefine.SwaggerUIThemeRedocFree,
|
||||
swaggerBaseUri: "/doc/swagger",
|
||||
globalMiddlewareList: nil,
|
||||
disableSwaggerDoc: false,
|
||||
serverInfo: &apiDocDefine.Info{
|
||||
Description: "这是一个微服务,提供一些必要的的数据接口功能",
|
||||
Title: "微服务接口文档",
|
||||
TermsOfService: "",
|
||||
Contact: &apiDocDefine.Contact{
|
||||
Name: "开发人员",
|
||||
Url: "",
|
||||
Email: "developer@example.com",
|
||||
},
|
||||
License: &apiDocDefine.License{
|
||||
Name: consts.LicenseApache20,
|
||||
Url: consts.LicenseUrlTable[consts.LicenseApache20],
|
||||
},
|
||||
Version: "0.0.1",
|
||||
},
|
||||
serverList: []*apiDocDefine.ServerItem{
|
||||
{
|
||||
Url: fmt.Sprintf("http://127.0.0.1:%d", port),
|
||||
Description: "测试服务器",
|
||||
Variables: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, opt := range optionList {
|
||||
if nil == opt {
|
||||
continue
|
||||
}
|
||||
opt(option)
|
||||
}
|
||||
return option
|
||||
}
|
||||
|
||||
// NewServer server实例
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:20 2025/2/7
|
||||
func NewServer(port int, optionList ...SetServerOptionFunc) *server {
|
||||
if port < 80 {
|
||||
panic("port should be greater than 80")
|
||||
}
|
||||
option := newServerOption(port, optionList...)
|
||||
globalMiddlewareList := make([]gin.HandlerFunc, 0)
|
||||
// CustomContext 必须在第一个, 并且进行初始化
|
||||
globalMiddlewareList = append(
|
||||
globalMiddlewareList,
|
||||
func(ctx *gin.Context) {
|
||||
// 初始化上下文以及基础信息
|
||||
_ = define.NewContext(ctx)
|
||||
},
|
||||
)
|
||||
if nil != option.loggerCfg {
|
||||
// 请求日志记录中间件
|
||||
globalMiddlewareList = append(globalMiddlewareList, middleware.LogRequest(option.loggerCfg))
|
||||
}
|
||||
if option.enableCors {
|
||||
// 跨域中间件
|
||||
globalMiddlewareList = append(globalMiddlewareList, request_cors.New(request_cors.Config{
|
||||
AllowAllOrigins: true,
|
||||
AllowOrigins: nil,
|
||||
AllowOriginFunc: nil,
|
||||
AllowMethods: []string{"*"},
|
||||
AllowHeaders: []string{"*"},
|
||||
AllowCredentials: true,
|
||||
ExposeHeaders: nil,
|
||||
MaxAge: 0,
|
||||
AllowWildcard: true,
|
||||
AllowBrowserExtensions: true,
|
||||
AllowWebSockets: true,
|
||||
AllowFiles: true,
|
||||
}))
|
||||
}
|
||||
if len(option.globalMiddlewareList) > 0 {
|
||||
// 自定义全局中间件追加
|
||||
globalMiddlewareList = append(globalMiddlewareList, option.globalMiddlewareList...)
|
||||
}
|
||||
r := gin.Default()
|
||||
|
||||
// 注册全局中间件
|
||||
r.Use(globalMiddlewareList...)
|
||||
|
||||
// 启用pprof, 注册相关路由
|
||||
if option.enablePprof {
|
||||
pprof.Register(r)
|
||||
}
|
||||
return &server{
|
||||
router: r,
|
||||
uiInstance: apiDoc.NewSwaggerUI(option.serverInfo, option.serverList, option.swaggerUiTheme),
|
||||
port: port,
|
||||
option: option,
|
||||
}
|
||||
}
|
||||
|
||||
type server struct {
|
||||
router *gin.Engine
|
||||
port int
|
||||
uiInstance *apiDoc.SwaggerUI
|
||||
option *serverOption
|
||||
}
|
||||
|
||||
// Start 启动服务
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:31 2025/2/7
|
||||
func (s *server) Start() {
|
||||
// 注册文档
|
||||
s.uiInstance.RegisterHandler(s.router, s.option.swaggerBaseUri)
|
||||
gracefulServer := graceful.NewServer(fmt.Sprintf(":%d", s.port), s.Router())
|
||||
if err := gracefulServer.ListenAndServe(); err != nil {
|
||||
if strings.Contains(err.Error(), "use of closed network connection") {
|
||||
fmt.Println("接收到退出指令, 服务平滑关闭")
|
||||
return
|
||||
}
|
||||
panic("服务启动监听失败" + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Router 对外访问路由实例, 不建议直接用
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:35 2025/2/18
|
||||
func (s *server) Router() *gin.Engine {
|
||||
return s.router
|
||||
}
|
||||
|
||||
// Handler404 注册404的处理方法
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:01 2025/2/22
|
||||
func (s *server) Handler404(f gin.HandlerFunc) {
|
||||
s.router.NoRoute(f)
|
||||
}
|
||||
|
||||
// SetCustomRouter 自定义路由处理
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:09 2025/2/22
|
||||
func (s *server) SetCustomRouter(f func(r *gin.Engine)) {
|
||||
if nil == f {
|
||||
return
|
||||
}
|
||||
f(s.router)
|
||||
}
|
||||
|
||||
// Group 注册接口路由
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 19:35 2025/1/27
|
||||
func (s *server) Group(routerPrefix string, middlewareList []gin.HandlerFunc, cList ...any) {
|
||||
g := s.router.Group(routerPrefix)
|
||||
g.Use(middlewareList...)
|
||||
cParser := controller{}
|
||||
for _, c := range cList {
|
||||
urlTable := cParser.Parse(c)
|
||||
for _, itemUriCfg := range urlTable {
|
||||
_ = s.uiInstance.DocInstance().AddApiFromInAndOut(routerPrefix, itemUriCfg.FormDataType, itemUriCfg.ResultDataType)
|
||||
method := strings.ToUpper(itemUriCfg.RequestMethod)
|
||||
switch method {
|
||||
case http.MethodGet:
|
||||
g.GET(itemUriCfg.Path, RequestHandler(itemUriCfg))
|
||||
case http.MethodHead:
|
||||
g.HEAD(itemUriCfg.Path, RequestHandler(itemUriCfg))
|
||||
case http.MethodPost:
|
||||
g.POST(itemUriCfg.Path, RequestHandler(itemUriCfg))
|
||||
case http.MethodPut:
|
||||
g.PUT(itemUriCfg.Path, RequestHandler(itemUriCfg))
|
||||
case http.MethodPatch:
|
||||
g.PATCH(itemUriCfg.Path, RequestHandler(itemUriCfg))
|
||||
case http.MethodDelete:
|
||||
g.DELETE(itemUriCfg.Path, RequestHandler(itemUriCfg))
|
||||
case http.MethodOptions:
|
||||
g.OPTIONS(itemUriCfg.Path, RequestHandler(itemUriCfg))
|
||||
case http.MethodTrace:
|
||||
panic(`method Trace is not supported`)
|
||||
default:
|
||||
panic("method " + itemUriCfg.RequestMethod + " is not support")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
router/server_test.go
Normal file
19
router/server_test.go
Normal file
@ -0,0 +1,19 @@
|
||||
// Package router ...
|
||||
//
|
||||
// Description : router ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-05-28 20:58
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewServer(t *testing.T) {
|
||||
s := NewServer(9087)
|
||||
s.Router().GET("/ping", func(c *gin.Context) {})
|
||||
s.Start()
|
||||
}
|
55
router/validator.go
Normal file
55
router/validator.go
Normal file
@ -0,0 +1,55 @@
|
||||
// Package router ...
|
||||
//
|
||||
// Description : router ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2025-02-07 17:36
|
||||
package router
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GetValidateErr 格式化错误信息
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 22:19 2025/1/15
|
||||
func GetValidateErr(obj any, rawErr error) error {
|
||||
if nil == rawErr {
|
||||
return nil
|
||||
}
|
||||
if nil == obj {
|
||||
return rawErr
|
||||
}
|
||||
var (
|
||||
ok bool
|
||||
validationErrs validator.ValidationErrors
|
||||
errString []string
|
||||
field reflect.StructField
|
||||
)
|
||||
if ok = errors.As(rawErr, &validationErrs); !ok {
|
||||
return rawErr
|
||||
}
|
||||
objType := reflect.TypeOf(obj)
|
||||
if objType.Kind() == reflect.Ptr {
|
||||
objType = objType.Elem()
|
||||
}
|
||||
for _, validationErr := range validationErrs {
|
||||
if field, ok = objType.FieldByName(validationErr.Field()); ok {
|
||||
if e := field.Tag.Get(defaultValidateErrTag); e != "" {
|
||||
errString = append(errString, fmt.Sprintf("%s: %s", field.Tag.Get("json"), e))
|
||||
continue
|
||||
} else {
|
||||
errString = append(errString, fmt.Sprintf("%s: %v", field.Tag.Get("json"), validationErr.Value()))
|
||||
}
|
||||
}
|
||||
errString = append(errString, validationErr.Error())
|
||||
}
|
||||
return errors.New(strings.Join(errString, "\n"))
|
||||
}
|
Reference in New Issue
Block a user