Compare commits
9 Commits
4eea5a5167
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 146d9df639 | |||
| 9143bf84f8 | |||
| 655a4f5021 | |||
| d4052bcc4f | |||
| 954f6fef8a | |||
| 36dee32db8 | |||
| 645876e316 | |||
| 2deff38f9d | |||
| 463523f0b8 |
@ -1,12 +0,0 @@
|
|||||||
// Package define ...
|
|
||||||
//
|
|
||||||
// Description : define ...
|
|
||||||
//
|
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
|
||||||
//
|
|
||||||
// Date : 2025-04-12 20:18
|
|
||||||
package define
|
|
||||||
|
|
||||||
const (
|
|
||||||
CustomContextKey = "_CUSTOM_CONTEXT" // 自定义context
|
|
||||||
)
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
// 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/op_string"
|
|
||||||
|
|
||||||
"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), ".", ""),
|
|
||||||
op_string.Md5(op_string.Random(32, "")),
|
|
||||||
)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
"git.zhangdeman.cn/zhangdeman/consts"
|
"git.zhangdeman.cn/zhangdeman/consts"
|
||||||
"git.zhangdeman.cn/zhangdeman/wrapper/op_ternary"
|
"git.zhangdeman.cn/zhangdeman/wrapper/op_ternary"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -127,22 +128,18 @@ func GetHttpHandleConfig() *HttpHandleConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
LogicAfterResponseKey = "__logic_after_response__"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LogicAfterResponse struct {
|
type LogicAfterResponse struct {
|
||||||
SuccessHookFuncList []func() `json:"-"` // 请求最后需要执行的成功hook函数
|
SuccessHookFuncList []func(ctx *gin.Context) `json:"-"` // 请求最后需要执行的成功hook函数
|
||||||
FailureHookFuncList []func() `json:"-"` // 请求最后需要执行的失败hook函数
|
FailureHookFuncList []func(ctx *gin.Context) `json:"-"` // 请求最后需要执行的失败hook函数
|
||||||
Lock *sync.RWMutex `json:"-"` // 逻辑锁
|
Lock *sync.RWMutex `json:"-"` // 逻辑锁
|
||||||
}
|
}
|
||||||
|
|
||||||
func (logic *LogicAfterResponse) AddSuccessHook(f func()) {
|
func (logic *LogicAfterResponse) AddSuccessHook(f func(ctx *gin.Context)) {
|
||||||
logic.Lock.Lock()
|
logic.Lock.Lock()
|
||||||
defer logic.Lock.Unlock()
|
defer logic.Lock.Unlock()
|
||||||
logic.SuccessHookFuncList = append(logic.SuccessHookFuncList, f)
|
logic.SuccessHookFuncList = append(logic.SuccessHookFuncList, f)
|
||||||
}
|
}
|
||||||
func (logic *LogicAfterResponse) AddFailureHook(f func()) {
|
func (logic *LogicAfterResponse) AddFailureHook(f func(ctx *gin.Context)) {
|
||||||
logic.Lock.Lock()
|
logic.Lock.Lock()
|
||||||
defer logic.Lock.Unlock()
|
defer logic.Lock.Unlock()
|
||||||
logic.FailureHookFuncList = append(logic.FailureHookFuncList, f)
|
logic.FailureHookFuncList = append(logic.FailureHookFuncList, f)
|
||||||
|
|||||||
2
go.mod
2
go.mod
@ -6,7 +6,7 @@ toolchain go1.24.2
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
git.zhangdeman.cn/zhangdeman/api-doc v1.0.3-0.20251013152001-868ee8955623
|
git.zhangdeman.cn/zhangdeman/api-doc v1.0.3-0.20251013152001-868ee8955623
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250916024308-d378e6c57772
|
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20251101092813-3dd9fb807f1c
|
||||||
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251013092857-dcf591d4e8a8
|
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251013092857-dcf591d4e8a8
|
||||||
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250510123912-a0d52fc093ab
|
git.zhangdeman.cn/zhangdeman/exception v0.0.0-20250510123912-a0d52fc093ab
|
||||||
git.zhangdeman.cn/zhangdeman/graceful v0.0.0-20250529070945-92833db6f3a4
|
git.zhangdeman.cn/zhangdeman/graceful v0.0.0-20250529070945-92833db6f3a4
|
||||||
|
|||||||
6
go.sum
6
go.sum
@ -1,7 +1,9 @@
|
|||||||
git.zhangdeman.cn/zhangdeman/api-doc v1.0.3-0.20251013152001-868ee8955623 h1:QiqETEQx2PBv2fF3UwPS11dsbVDcF3WNCmRRA8zrqAk=
|
git.zhangdeman.cn/zhangdeman/api-doc v1.0.3-0.20251013152001-868ee8955623 h1:QiqETEQx2PBv2fF3UwPS11dsbVDcF3WNCmRRA8zrqAk=
|
||||||
git.zhangdeman.cn/zhangdeman/api-doc v1.0.3-0.20251013152001-868ee8955623/go.mod h1:ryyMI2gPgotFD1lZC50p2vRyX8bf3MRwO9tos41z4WU=
|
git.zhangdeman.cn/zhangdeman/api-doc v1.0.3-0.20251013152001-868ee8955623/go.mod h1:ryyMI2gPgotFD1lZC50p2vRyX8bf3MRwO9tos41z4WU=
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250916024308-d378e6c57772 h1:Yo1ur3LnDF5s7F7tpJsNrdUSF8LwYKnN9TdQU32F3eU=
|
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20251101052451-27b111113aa3 h1:MQJCu5ZIDUYJSQ7rGZni4gsxgGmpXgWgNBHJzHSOvxw=
|
||||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250916024308-d378e6c57772/go.mod h1:5p8CEKGBxi7qPtTXDI3HDmqKAfIm5i/aBWdrbkbdNjc=
|
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20251101052451-27b111113aa3/go.mod h1:5p8CEKGBxi7qPtTXDI3HDmqKAfIm5i/aBWdrbkbdNjc=
|
||||||
|
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20251101092813-3dd9fb807f1c h1:H0Tlh7i5UtCLwwhPFEqIO7V+vydQ7YgMtPQI7iCxiSI=
|
||||||
|
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20251101092813-3dd9fb807f1c/go.mod h1:5p8CEKGBxi7qPtTXDI3HDmqKAfIm5i/aBWdrbkbdNjc=
|
||||||
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251013092857-dcf591d4e8a8 h1:Pw981jG3hH9ZHrB3s1xPpsZafgX3USuMAjnGi2GEP9Y=
|
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251013092857-dcf591d4e8a8 h1:Pw981jG3hH9ZHrB3s1xPpsZafgX3USuMAjnGi2GEP9Y=
|
||||||
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251013092857-dcf591d4e8a8/go.mod h1:xtCw3om5DRrG30EfQd/lfQPXgptfK7l9oBSt4Kdhjok=
|
git.zhangdeman.cn/zhangdeman/dynamic-struct v0.0.0-20251013092857-dcf591d4e8a8/go.mod h1:xtCw3om5DRrG30EfQd/lfQPXgptfK7l9oBSt4Kdhjok=
|
||||||
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda h1:bMD6r9gjRy7cO+T4zRQVYAesgIblBdTnhzT1vN5wjvI=
|
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda h1:bMD6r9gjRy7cO+T4zRQVYAesgIblBdTnhzT1vN5wjvI=
|
||||||
|
|||||||
@ -1,17 +0,0 @@
|
|||||||
// Package logger ...
|
|
||||||
//
|
|
||||||
// Description : logger ...
|
|
||||||
//
|
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
|
||||||
//
|
|
||||||
// Date : 2025-10-30 16:56
|
|
||||||
package logger
|
|
||||||
|
|
||||||
// Data 日志数据
|
|
||||||
type Data struct {
|
|
||||||
TraceID string `json:"trace_id" dc:"trace_id"`
|
|
||||||
RequestID string `json:"request_id" dc:"请求ID"`
|
|
||||||
UserID any `json:"user_id" dc:"用户IP"`
|
|
||||||
Hostname string `json:"hostname" dc:"服务器主机名称"`
|
|
||||||
HostIp string `json:"host_ip" dc:"主机IP"`
|
|
||||||
}
|
|
||||||
@ -10,7 +10,7 @@ package logger
|
|||||||
import "go.uber.org/zap"
|
import "go.uber.org/zap"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Instance *zap.Logger = zap.NewNop()
|
Instance = zap.NewNop()
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetInstance 设置日志实例
|
// SetInstance 设置日志实例
|
||||||
@ -20,3 +20,12 @@ func SetInstance(l *zap.Logger) {
|
|||||||
}
|
}
|
||||||
Instance = l
|
Instance = l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
RecordType = "GIN_LOG"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CodeInjectCommonParam = "inject-common-param"
|
||||||
|
CodeLogicHook = "logic-hook"
|
||||||
|
)
|
||||||
|
|||||||
@ -11,11 +11,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"git.zhangdeman.cn/zhangdeman/trace"
|
"git.zhangdeman.cn/zhangdeman/consts"
|
||||||
"git.zhangdeman.cn/zhangdeman/wrapper/op_ternary"
|
|
||||||
|
|
||||||
"git.zhangdeman.cn/zhangdeman/gin/define"
|
"git.zhangdeman.cn/zhangdeman/gin/define"
|
||||||
"git.zhangdeman.cn/zhangdeman/gin/request/parse_body"
|
"git.zhangdeman.cn/zhangdeman/gin/request/parse_body"
|
||||||
|
"git.zhangdeman.cn/zhangdeman/trace"
|
||||||
|
"git.zhangdeman.cn/zhangdeman/wrapper/op_ternary"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -242,33 +242,29 @@ func (wh *wrapperHandle) ParseCookie(ctx *gin.Context) map[string]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetLogicAfterResponse ...
|
// GetLogicAfterResponse ...
|
||||||
//
|
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
|
||||||
//
|
|
||||||
// Date : 16:54 2025/2/28
|
|
||||||
func (wh *wrapperHandle) GetLogicAfterResponse(ctx *gin.Context) *define.LogicAfterResponse {
|
func (wh *wrapperHandle) GetLogicAfterResponse(ctx *gin.Context) *define.LogicAfterResponse {
|
||||||
if nil == ctx || nil == ctx.Request {
|
if nil == ctx || nil == ctx.Request {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
l, exist := ctx.Get(define.LogicAfterResponseKey)
|
l, exist := ctx.Get(consts.GinLogicAfterResponseKey)
|
||||||
if !exist || nil == l {
|
if !exist || nil == l {
|
||||||
l = &define.LogicAfterResponse{
|
l = &define.LogicAfterResponse{
|
||||||
SuccessHookFuncList: make([]func(), 0),
|
SuccessHookFuncList: make([]func(ctx *gin.Context), 0),
|
||||||
FailureHookFuncList: make([]func(), 0),
|
FailureHookFuncList: make([]func(ctx *gin.Context), 0),
|
||||||
Lock: &sync.RWMutex{},
|
Lock: &sync.RWMutex{},
|
||||||
}
|
}
|
||||||
ctx.Set(define.LogicAfterResponseKey, l)
|
ctx.Set(consts.GinLogicAfterResponseKey, l)
|
||||||
}
|
}
|
||||||
// 就这么写, key值如果被其他人覆盖成非法值, 此处会直接panic
|
// 就这么写, key值如果被其他人覆盖成非法值, 此处会直接panic
|
||||||
return l.(*define.LogicAfterResponse)
|
return l.(*define.LogicAfterResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCustomContext 获取自定义context
|
|
||||||
func (wh *wrapperHandle) GetCustomContext(ctx *gin.Context) *define.Context {
|
|
||||||
return define.NewContext(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTraceInstance 获取trace实例
|
// GetTraceInstance 获取trace实例
|
||||||
func (wh *wrapperHandle) GetTraceInstance(ctx *gin.Context) *trace.Runtime {
|
func (wh *wrapperHandle) GetTraceInstance(ctx *gin.Context) *trace.Runtime {
|
||||||
return define.NewContext(ctx).Trace
|
i, exist := ctx.Get(consts.GinTraceInstanceField)
|
||||||
|
if !exist || nil == i {
|
||||||
|
i = trace.NewRuntime(wh.GetCtxStringData(ctx, consts.GinTraceIDField, ""), 2)
|
||||||
|
ctx.Set(consts.GinTraceInstanceField, i)
|
||||||
|
}
|
||||||
|
return i.(*trace.Runtime)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,9 +8,12 @@
|
|||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"git.zhangdeman.cn/zhangdeman/gin/logger"
|
"git.zhangdeman.cn/zhangdeman/gin/logger"
|
||||||
|
"git.zhangdeman.cn/zhangdeman/gin/util"
|
||||||
|
pkgLogger "git.zhangdeman.cn/zhangdeman/logger"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,23 +34,86 @@ func (s *server) AddCommonParamRules(rules map[string]GetCommonParam) {
|
|||||||
|
|
||||||
// injectCommonParam 注入公共参数
|
// injectCommonParam 注入公共参数
|
||||||
func (s *server) injectCommonParam(ctx *gin.Context, formValue any) error {
|
func (s *server) injectCommonParam(ctx *gin.Context, formValue any) error {
|
||||||
reflectType := reflect.TypeOf(formValue)
|
innerCtx := util.GinCtxToContext(ctx)
|
||||||
|
var (
|
||||||
|
val any
|
||||||
|
err error
|
||||||
|
reflectFormValue reflect.Value
|
||||||
|
reflectType reflect.Type
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
|
||||||
|
if reflectFormValue, ok = formValue.(reflect.Value); !ok {
|
||||||
|
reflectFormValue = reflect.ValueOf(formValue)
|
||||||
|
reflectType = reflect.TypeOf(formValue)
|
||||||
|
} else {
|
||||||
|
reflectType = reflectFormValue.Type()
|
||||||
|
}
|
||||||
|
|
||||||
fieldTable := map[string]bool{}
|
fieldTable := map[string]bool{}
|
||||||
fieldNum := reflectType.Elem().NumField()
|
fieldNum := reflectType.Elem().NumField()
|
||||||
for i := 0; i < fieldNum; i++ {
|
for i := 0; i < fieldNum; i++ {
|
||||||
|
if reflectType.Elem().Field(i).Anonymous && // 是匿名字段, 再做一次解析
|
||||||
|
((reflectType.Elem().Field(i).Type.Kind() == reflect.Ptr && reflectType.Elem().Field(i).Type.Kind() == reflect.Struct) || // 结构体指针
|
||||||
|
reflectType.Elem().Field(i).Type.Kind() == reflect.Struct) { // 结构体
|
||||||
|
anonymousFieldType := reflectType.Elem().Field(i).Type
|
||||||
|
if anonymousFieldType.Kind() == reflect.Ptr {
|
||||||
|
anonymousFieldType = anonymousFieldType.Elem()
|
||||||
|
}
|
||||||
|
for j := 0; j < anonymousFieldType.NumField(); j++ {
|
||||||
|
fieldTable[anonymousFieldType.Field(j).Name] = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// 提取全部结构体字段
|
// 提取全部结构体字段
|
||||||
fieldTable[reflectType.Elem().Field(i).Name] = true
|
fieldTable[reflectType.Elem().Field(i).Name] = true
|
||||||
}
|
}
|
||||||
reflectValue := reflect.ValueOf(formValue)
|
}
|
||||||
for fieldName, getParamFunc := range s.commonParam {
|
for fieldName, getParamFunc := range s.commonParam {
|
||||||
if _, ok := fieldTable[fieldName]; !ok {
|
if _, ok = fieldTable[fieldName]; !ok {
|
||||||
// 结构体字段未配置自动注入
|
// 结构体字段未配置自动注入
|
||||||
|
logger.Instance.Debug("当前结构体不包含指定字段, 忽略执行", pkgLogger.NewLogData(innerCtx, logger.RecordType, logger.CodeInjectCommonParam, map[string]any{
|
||||||
|
"field_name": fieldName,
|
||||||
|
"struct": reflectFormValue.Elem().Type().String(),
|
||||||
|
}).ToFieldList()...)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if val, err := getParamFunc(ctx); nil != err {
|
fieldValue := reflectFormValue.Elem().FieldByName(fieldName)
|
||||||
logger.Instance.Error("inject common param failed")
|
if !fieldValue.IsZero() {
|
||||||
|
// 表单次数不为空, 说明从参数中传递, 不做覆盖填充
|
||||||
|
logger.Instance.Debug("指定字段已赋值, 不做重新覆盖填充处理, 跳过", pkgLogger.NewLogData(innerCtx, logger.RecordType, logger.CodeInjectCommonParam, map[string]any{
|
||||||
|
"field_name": fieldName,
|
||||||
|
"struct": reflectFormValue.Elem().Type().String(),
|
||||||
|
"input_form_value": fieldValue.Interface(),
|
||||||
|
}).ToFieldList()...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if val, err = getParamFunc(ctx); nil != err {
|
||||||
|
logger.Instance.Error("获取公共结构体字段参数值失败", pkgLogger.NewLogData(innerCtx, logger.RecordType, logger.CodeInjectCommonParam, map[string]any{
|
||||||
|
"field_name": fieldName,
|
||||||
|
"err_msg": err.Error(),
|
||||||
|
}).ToFieldList()...)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
reflectValue.Elem().FieldByName(fieldName)
|
if !fieldValue.CanSet() {
|
||||||
|
logDataList := pkgLogger.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeInjectCommonParam, map[string]any{
|
||||||
|
"field_name": fieldName,
|
||||||
|
"field_type": fieldValue.Type().String(),
|
||||||
|
})
|
||||||
|
logger.Instance.Error("结构体字段不可设置值", logDataList.ToFieldList()...)
|
||||||
|
return errors.New(fieldName + ": 结构体字段不可设置值")
|
||||||
}
|
}
|
||||||
|
reflectVal := reflect.ValueOf(val)
|
||||||
|
if reflectVal.Type() != fieldValue.Type() {
|
||||||
|
logDataList := pkgLogger.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeInjectCommonParam, map[string]any{
|
||||||
|
"field_name": fieldName,
|
||||||
|
"field_type": fieldValue.Type().String(),
|
||||||
|
"value_type": reflectVal.Type().String(),
|
||||||
|
})
|
||||||
|
logger.Instance.Error("返回数据类型与字段类型不一致", logDataList.ToFieldList()...)
|
||||||
|
return errors.New(fieldName + ": 字段自动注入, 返回数据类型与字段类型不一致")
|
||||||
|
}
|
||||||
|
// 设置值
|
||||||
|
fieldValue.Set(reflectVal)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,10 +12,6 @@ import "strings"
|
|||||||
var defaultValidateErrTag = "err"
|
var defaultValidateErrTag = "err"
|
||||||
|
|
||||||
// SetValidateErrTag 设置验证失败时, 获取错误信息的tag字段
|
// SetValidateErrTag 设置验证失败时, 获取错误信息的tag字段
|
||||||
//
|
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
|
||||||
//
|
|
||||||
// Date : 17:42 2025/2/7
|
|
||||||
func SetValidateErrTag(tagName string) {
|
func SetValidateErrTag(tagName string) {
|
||||||
tagName = strings.TrimSpace(tagName)
|
tagName = strings.TrimSpace(tagName)
|
||||||
if tagName == "" {
|
if tagName == "" {
|
||||||
|
|||||||
@ -13,10 +13,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// controller 解析controller有哪些方法要注册为接口
|
// controller 解析controller有哪些方法要注册为接口
|
||||||
//
|
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
|
||||||
//
|
|
||||||
// Date : 15:30 2025/1/27
|
|
||||||
type controller struct {
|
type controller struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +67,7 @@ func (c controller) methodConfig(reflectMethod reflect.Method) (cfg UriConfig, n
|
|||||||
}
|
}
|
||||||
// 第一个参数必须是 *gin.Context 或者 *define.Context
|
// 第一个参数必须是 *gin.Context 或者 *define.Context
|
||||||
paramOne := methodType.In(1).String()
|
paramOne := methodType.In(1).String()
|
||||||
if paramOne != GinContextType && paramOne != CustomContextType {
|
if paramOne != GinContextType {
|
||||||
needRegister = false
|
needRegister = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -107,13 +103,14 @@ func (c controller) methodConfig(reflectMethod reflect.Method) (cfg UriConfig, n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 解析meta信息
|
// 解析meta信息
|
||||||
cfg.CtxType = paramOne
|
|
||||||
cfg.Path = metaField.Tag.Get(TagNamePath)
|
cfg.Path = metaField.Tag.Get(TagNamePath)
|
||||||
cfg.RequestMethod = metaField.Tag.Get(TagNameMethod)
|
cfg.RequestMethod = metaField.Tag.Get(TagNameMethod)
|
||||||
cfg.Desc = metaField.Tag.Get(TagNameDesc)
|
cfg.Desc = metaField.Tag.Get(TagNameDesc)
|
||||||
cfg.TagList = strings.Split(metaField.Tag.Get(TagNameUriTag), ",")
|
cfg.TagList = strings.Split(metaField.Tag.Get(TagNameUriTag), ",")
|
||||||
// 解析第一个返回值, 要求必须是结构体或者是map
|
// 解析第一个返回值, 要求必须是结构体或者是map
|
||||||
outputStrictModel := metaField.Tag.Get(TagNameOutputStrict)
|
outputStrictModel := metaField.Tag.Get(TagNameOutputStrict)
|
||||||
|
hookSync := strings.ToLower(metaField.Tag.Get(TagNameHookSync))
|
||||||
|
cfg.HookSync = hookSync == "1" || hookSync == "true" // 同步执行判断
|
||||||
cfg.OutputStrict = outputStrictModel == "1" || outputStrictModel == "true"
|
cfg.OutputStrict = outputStrictModel == "1" || outputStrictModel == "true"
|
||||||
if cfg.OutputStrict {
|
if cfg.OutputStrict {
|
||||||
// 开启输出严格模式校验
|
// 开启输出严格模式校验
|
||||||
|
|||||||
@ -15,7 +15,6 @@ const (
|
|||||||
PrefixFuncName = "RouterPrefix" // 路由前缀函数名称
|
PrefixFuncName = "RouterPrefix" // 路由前缀函数名称
|
||||||
MiddlewareFuncName = "RouterMiddleware" // 路由中间件函数名称
|
MiddlewareFuncName = "RouterMiddleware" // 路由中间件函数名称
|
||||||
GinContextType = "*gin.Context" // gin context 类型名称
|
GinContextType = "*gin.Context" // gin context 类型名称
|
||||||
CustomContextType = "*define.Context" // custom context 类型名称
|
|
||||||
ErrorType = "error" // error类型
|
ErrorType = "error" // error类型
|
||||||
ErrorInterfaceFuncName = "Error" // error接口需要实现的方法名称
|
ErrorInterfaceFuncName = "Error" // error接口需要实现的方法名称
|
||||||
)
|
)
|
||||||
@ -26,6 +25,7 @@ const (
|
|||||||
TagNameUriTag = "tag" // 接口的tag
|
TagNameUriTag = "tag" // 接口的tag
|
||||||
TagNameDesc = "desc" // 接口的描述
|
TagNameDesc = "desc" // 接口的描述
|
||||||
TagNameOutputStrict = "output_strict" // 接口数据是否为严格模式 : 严格模式, 响应数据必须是结构体/map,非严格模式返回任意值
|
TagNameOutputStrict = "output_strict" // 接口数据是否为严格模式 : 严格模式, 响应数据必须是结构体/map,非严格模式返回任意值
|
||||||
|
TagNameHookSync = "hook-sync" // hook同步执行
|
||||||
TagNameBinding = "binding" // gin 内置的验证规则tag
|
TagNameBinding = "binding" // gin 内置的验证规则tag
|
||||||
TagNameValidate = "validate" // validator v10 默认的验证规则tag
|
TagNameValidate = "validate" // validator v10 默认的验证规则tag
|
||||||
TagNameErrMsg = "err" // 验证失败错误信息tag
|
TagNameErrMsg = "err" // 验证失败错误信息tag
|
||||||
@ -42,7 +42,7 @@ type UriConfig struct {
|
|||||||
TagList []string `json:"tag_list"` // 接口分组
|
TagList []string `json:"tag_list"` // 接口分组
|
||||||
Desc string `json:"desc"` // 接口描述
|
Desc string `json:"desc"` // 接口描述
|
||||||
OutputStrict bool `json:"output_strict"` // 接口是否为严格模式 : 不配置,可返回任意类型, 配置, 必须返回结构体或者map
|
OutputStrict bool `json:"output_strict"` // 接口是否为严格模式 : 不配置,可返回任意类型, 配置, 必须返回结构体或者map
|
||||||
CtxType string `json:"ctx_type"` // ctx参数类型
|
HookSync bool `json:"hook_sync"` // 接口主逻辑执行完成之后,hook是否同步执行, 默认异步执行
|
||||||
FormDataType reflect.Type `json:"-"` // 表单数据类型
|
FormDataType reflect.Type `json:"-"` // 表单数据类型
|
||||||
ResultDataType reflect.Type `json:"-"` // 返回值数据类型
|
ResultDataType reflect.Type `json:"-"` // 返回值数据类型
|
||||||
ApiStructValue reflect.Value `json:"-"` // 逻辑函数所属结构体取值
|
ApiStructValue reflect.Value `json:"-"` // 逻辑函数所属结构体取值
|
||||||
@ -50,10 +50,6 @@ type UriConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UriParam 接口参数配置
|
// UriParam 接口参数配置
|
||||||
//
|
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
|
||||||
//
|
|
||||||
// Date : 15:40 2025/1/27
|
|
||||||
type UriParam struct {
|
type UriParam struct {
|
||||||
Field string `json:"field"` // 结构体字段
|
Field string `json:"field"` // 结构体字段
|
||||||
Name string `json:"name"` // 参数名称
|
Name string `json:"name"` // 参数名称
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import (
|
|||||||
"git.zhangdeman.cn/zhangdeman/gin/request"
|
"git.zhangdeman.cn/zhangdeman/gin/request"
|
||||||
"git.zhangdeman.cn/zhangdeman/gin/response"
|
"git.zhangdeman.cn/zhangdeman/gin/response"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/mcuadros/go-defaults"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *server) getFormInitValue(ctx *gin.Context, uriCfg UriConfig) (any, error) {
|
func (s *server) getFormInitValue(ctx *gin.Context, uriCfg UriConfig) (any, error) {
|
||||||
@ -50,6 +51,7 @@ func (s *server) RequestHandler(uriCfg UriConfig) gin.HandlerFunc {
|
|||||||
ok bool
|
ok bool
|
||||||
e exception.IException
|
e exception.IException
|
||||||
formValue any
|
formValue any
|
||||||
|
firstParam reflect.Value
|
||||||
)
|
)
|
||||||
|
|
||||||
if formValue, err = s.getFormInitValue(ctx, uriCfg); nil != err {
|
if formValue, err = s.getFormInitValue(ctx, uriCfg); nil != err {
|
||||||
@ -60,56 +62,44 @@ func (s *server) RequestHandler(uriCfg UriConfig) gin.HandlerFunc {
|
|||||||
ctx.Abort()
|
ctx.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 表单数据
|
||||||
|
inputValue := reflect.ValueOf(formValue)
|
||||||
|
|
||||||
isSuccess := false
|
// 注入公共参数
|
||||||
|
if err = s.injectCommonParam(ctx, inputValue); nil != err {
|
||||||
|
e = exception.NewFromError(500, err)
|
||||||
|
response.SendWithException(ctx, e, &define.ResponseOption{
|
||||||
|
ContentType: consts.MimeTypeJson,
|
||||||
|
})
|
||||||
|
ctx.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 非必传参数设置默认值
|
||||||
|
defaults.SetDefaults(formValue)
|
||||||
|
|
||||||
|
// 默认请求失败
|
||||||
|
ctx.Set(consts.GinRequestSuccess, false)
|
||||||
// 初始化响应之后logic
|
// 初始化响应之后logic
|
||||||
logicAfterResponse := &define.LogicAfterResponse{
|
logicAfterResponse := &define.LogicAfterResponse{
|
||||||
SuccessHookFuncList: make([]func(), 0),
|
SuccessHookFuncList: make([]func(ctx *gin.Context), 0),
|
||||||
FailureHookFuncList: make([]func(), 0),
|
FailureHookFuncList: make([]func(ctx *gin.Context), 0),
|
||||||
Lock: &sync.RWMutex{},
|
Lock: &sync.RWMutex{},
|
||||||
}
|
}
|
||||||
// 此处暴露出去,是为了使用方可以获取到对应数据
|
// 此处暴露出去,是为了使用方可以获取到对应数据
|
||||||
ctx.Set(define.LogicAfterResponseKey, logicAfterResponse)
|
ctx.Set(consts.GinLogicAfterResponseKey, logicAfterResponse)
|
||||||
defer func() {
|
defer s.hook(ctx, uriCfg) // 执行Logic之后的相关逻辑
|
||||||
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 {
|
if uriCfg.FormDataType.Kind() != reflect.Ptr {
|
||||||
inputValue = inputValue.Elem()
|
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)
|
firstParam = reflect.ValueOf(ctx)
|
||||||
}
|
|
||||||
resList := uriCfg.ApiLogicFunc.Func.Call([]reflect.Value{uriCfg.ApiStructValue, firstParam, inputValue})
|
resList := uriCfg.ApiLogicFunc.Func.Call([]reflect.Value{uriCfg.ApiStructValue, firstParam, inputValue})
|
||||||
if resList[1].IsNil() {
|
if resList[1].IsNil() {
|
||||||
// 请求成功
|
// 请求成功, 更新标识
|
||||||
isSuccess = true
|
ctx.Set(consts.GinRequestSuccess, true)
|
||||||
response.SuccessWithExtension(ctx, resList[0].Interface(), &define.ResponseOption{ContentType: "application/json;charset=utf-8"})
|
response.SuccessWithExtension(ctx, resList[0].Interface(), &define.ResponseOption{ContentType: consts.MimeTypeJson})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 请求失败
|
// 请求失败
|
||||||
@ -123,7 +113,7 @@ func (s *server) RequestHandler(uriCfg UriConfig) gin.HandlerFunc {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
response.SendWithException(ctx, e, &define.ResponseOption{
|
response.SendWithException(ctx, e, &define.ResponseOption{
|
||||||
ContentType: "application/json;charset=utf-8",
|
ContentType: consts.MimeTypeJson,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
74
router/hook.go
Normal file
74
router/hook.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Package router ...
|
||||||
|
//
|
||||||
|
// Description : router ...
|
||||||
|
//
|
||||||
|
// Author : go_developer@163.com<白茶清欢>
|
||||||
|
//
|
||||||
|
// Date : 2025-11-01 12:34
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.zhangdeman.cn/zhangdeman/consts"
|
||||||
|
"git.zhangdeman.cn/zhangdeman/gin/define"
|
||||||
|
"git.zhangdeman.cn/zhangdeman/gin/logger"
|
||||||
|
"git.zhangdeman.cn/zhangdeman/gin/util"
|
||||||
|
pkgLogger "git.zhangdeman.cn/zhangdeman/logger"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// hook 执行hook逻辑
|
||||||
|
func (s *server) hook(ctx *gin.Context, uriCfg UriConfig) {
|
||||||
|
responseAfter, exist := ctx.Get(consts.GinLogicAfterResponseKey)
|
||||||
|
innerContext := util.GinCtxToContext(ctx)
|
||||||
|
if !exist || nil != responseAfter {
|
||||||
|
// 未配置
|
||||||
|
logger.Instance.Debug("未配置Logic执行后的hook逻辑", pkgLogger.NewLogData(innerContext, logger.RecordType, logger.CodeLogicHook, map[string]any{
|
||||||
|
"uri": uriCfg.Path,
|
||||||
|
"logic_after_response_key": consts.GinLogicAfterResponseKey,
|
||||||
|
}).ToFieldList()...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isSuccess, exist := ctx.Get(consts.GinRequestSuccess)
|
||||||
|
success := false
|
||||||
|
if nil != isSuccess && (isSuccess == "1" || isSuccess == "true" || isSuccess.(bool)) {
|
||||||
|
success = true
|
||||||
|
}
|
||||||
|
hookInstance := responseAfter.(*define.LogicAfterResponse)
|
||||||
|
if uriCfg.HookSync {
|
||||||
|
// 同步执行
|
||||||
|
s.hookAfter(ctx, uriCfg, hookInstance, success)
|
||||||
|
} else {
|
||||||
|
// 异步执行
|
||||||
|
go s.hookAfter(ctx, uriCfg, hookInstance, success)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *server) hookAfter(ctx *gin.Context, uriCfg UriConfig, hookInstance *define.LogicAfterResponse, success bool) {
|
||||||
|
innerContext := util.GinCtxToContext(ctx)
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
logger.Instance.Error("hook执行异常", pkgLogger.NewLogData(innerContext, logger.RecordType, logger.CodeLogicHook, map[string]any{
|
||||||
|
"uri": uriCfg.Path,
|
||||||
|
"logic_after_response_key": consts.GinLogicAfterResponseKey,
|
||||||
|
"error": err.(error).Error(),
|
||||||
|
"logic_success": success,
|
||||||
|
}).ToFieldList()...)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if success {
|
||||||
|
logger.Instance.Debug("接口Logic执行成功, 执行Hook逻辑")
|
||||||
|
for _, itemFunc := range hookInstance.SuccessHookFuncList {
|
||||||
|
if nil != itemFunc {
|
||||||
|
itemFunc(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, itemFunc := range hookInstance.FailureHookFuncList {
|
||||||
|
if nil != itemFunc {
|
||||||
|
itemFunc(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,7 +13,8 @@ package router
|
|||||||
//
|
//
|
||||||
// method: 请求方法: get/post 等
|
// method: 请求方法: get/post 等
|
||||||
//
|
//
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
// 使用示例如下:
|
||||||
//
|
// type RequestForm struct {
|
||||||
// Date : 21:40 2024/7/20
|
// Meta `json:"-" path:"接口路由" method:"get,post" sse:"1" tag:"接口分组标签" summary:"接口描述"`
|
||||||
|
// }
|
||||||
type Meta struct{}
|
type Meta struct{}
|
||||||
|
|||||||
@ -15,8 +15,6 @@ import (
|
|||||||
|
|
||||||
"git.zhangdeman.cn/zhangdeman/graceful"
|
"git.zhangdeman.cn/zhangdeman/graceful"
|
||||||
|
|
||||||
"git.zhangdeman.cn/zhangdeman/gin/define"
|
|
||||||
|
|
||||||
apiDoc "git.zhangdeman.cn/zhangdeman/api-doc"
|
apiDoc "git.zhangdeman.cn/zhangdeman/api-doc"
|
||||||
"git.zhangdeman.cn/zhangdeman/consts"
|
"git.zhangdeman.cn/zhangdeman/consts"
|
||||||
"git.zhangdeman.cn/zhangdeman/gin/middleware"
|
"git.zhangdeman.cn/zhangdeman/gin/middleware"
|
||||||
@ -67,10 +65,6 @@ func newServerOption(port int, optionList ...SetServerOptionFunc) *serverOption
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewServer server实例
|
// NewServer server实例
|
||||||
//
|
|
||||||
// Author : go_developer@163.com<白茶清欢>
|
|
||||||
//
|
|
||||||
// Date : 18:20 2025/2/7
|
|
||||||
func NewServer(port int, optionList ...SetServerOptionFunc) *server {
|
func NewServer(port int, optionList ...SetServerOptionFunc) *server {
|
||||||
if port < 80 {
|
if port < 80 {
|
||||||
panic("port should be greater than 80")
|
panic("port should be greater than 80")
|
||||||
@ -86,10 +80,6 @@ func NewServer(port int, optionList ...SetServerOptionFunc) *server {
|
|||||||
// CustomContext 必须在第一个, 并且进行初始化
|
// CustomContext 必须在第一个, 并且进行初始化
|
||||||
globalMiddlewareList = append(
|
globalMiddlewareList = append(
|
||||||
globalMiddlewareList,
|
globalMiddlewareList,
|
||||||
func(ctx *gin.Context) {
|
|
||||||
// 初始化上下文以及基础信息
|
|
||||||
_ = define.NewContext(ctx)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
if nil != option.loggerCfg {
|
if nil != option.loggerCfg {
|
||||||
// 请求日志记录中间件
|
// 请求日志记录中间件
|
||||||
@ -131,6 +121,7 @@ func NewServer(port int, optionList ...SetServerOptionFunc) *server {
|
|||||||
port: port,
|
port: port,
|
||||||
option: option,
|
option: option,
|
||||||
lock: &sync.RWMutex{},
|
lock: &sync.RWMutex{},
|
||||||
|
commonParam: map[string]GetCommonParam{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,8 +13,28 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type testCommon struct {
|
||||||
|
UserID uint `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type testForm struct {
|
||||||
|
Meta `json:"-" method:"get" path:"test"`
|
||||||
|
testCommon
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewServer(t *testing.T) {
|
func TestNewServer(t *testing.T) {
|
||||||
s := NewServer(9087)
|
s := NewServer(9087)
|
||||||
s.Router().GET("/ping", func(c *gin.Context) {})
|
s.AddCommonParamRule("UserID", func(ctx *gin.Context) (any, error) {
|
||||||
|
return uint(123456), nil
|
||||||
|
})
|
||||||
|
s.Group("", nil, testController{})
|
||||||
s.Start()
|
s.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type testController struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc testController) Test(ctx *gin.Context, requestData *testForm) (*testCommon, error) {
|
||||||
|
return &testCommon{UserID: requestData.UserID}, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user