From d4052bcc4ffa52c5f4539ce8a818853ee52deada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Sat, 1 Nov 2025 17:16:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=8D=87=E7=BA=A7hook=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- define/response.go | 15 ++++----- go.mod | 2 +- go.sum | 2 ++ logger/instance.go | 3 +- router/controller.go | 2 ++ router/define.go | 6 ++-- router/handler.go | 33 ++++---------------- router/hook.go | 74 ++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 95 insertions(+), 42 deletions(-) create mode 100644 router/hook.go diff --git a/define/response.go b/define/response.go index 4cfbfea..a995dad 100644 --- a/define/response.go +++ b/define/response.go @@ -12,6 +12,7 @@ import ( "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/wrapper/op_ternary" + "github.com/gin-gonic/gin" ) var ( @@ -127,22 +128,18 @@ func GetHttpHandleConfig() *HttpHandleConfig { } } -const ( - LogicAfterResponseKey = "__logic_after_response__" -) - type LogicAfterResponse struct { - SuccessHookFuncList []func() `json:"-"` // 请求最后需要执行的成功hook函数 - FailureHookFuncList []func() `json:"-"` // 请求最后需要执行的失败hook函数 - Lock *sync.RWMutex `json:"-"` // 逻辑锁 + SuccessHookFuncList []func(ctx *gin.Context) `json:"-"` // 请求最后需要执行的成功hook函数 + FailureHookFuncList []func(ctx *gin.Context) `json:"-"` // 请求最后需要执行的失败hook函数 + Lock *sync.RWMutex `json:"-"` // 逻辑锁 } -func (logic *LogicAfterResponse) AddSuccessHook(f func()) { +func (logic *LogicAfterResponse) AddSuccessHook(f func(ctx *gin.Context)) { logic.Lock.Lock() defer logic.Lock.Unlock() logic.SuccessHookFuncList = append(logic.SuccessHookFuncList, f) } -func (logic *LogicAfterResponse) AddFailureHook(f func()) { +func (logic *LogicAfterResponse) AddFailureHook(f func(ctx *gin.Context)) { logic.Lock.Lock() defer logic.Lock.Unlock() logic.FailureHookFuncList = append(logic.FailureHookFuncList, f) diff --git a/go.mod b/go.mod index df6ea75..de0350c 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.24.2 require ( 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-20251101052451-27b111113aa3 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/graceful v0.0.0-20250529070945-92833db6f3a4 diff --git a/go.sum b/go.sum index 717d8b1..f717607 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ git.zhangdeman.cn/zhangdeman/api-doc v1.0.3-0.20251013152001-868ee8955623 h1:Qiq 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-20250916024308-d378e6c57772/go.mod h1:5p8CEKGBxi7qPtTXDI3HDmqKAfIm5i/aBWdrbkbdNjc= +git.zhangdeman.cn/zhangdeman/consts v0.0.0-20251101052451-27b111113aa3 h1:MQJCu5ZIDUYJSQ7rGZni4gsxgGmpXgWgNBHJzHSOvxw= +git.zhangdeman.cn/zhangdeman/consts v0.0.0-20251101052451-27b111113aa3/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/go.mod h1:xtCw3om5DRrG30EfQd/lfQPXgptfK7l9oBSt4Kdhjok= git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda h1:bMD6r9gjRy7cO+T4zRQVYAesgIblBdTnhzT1vN5wjvI= diff --git a/logger/instance.go b/logger/instance.go index 5c9263b..6afb4a8 100644 --- a/logger/instance.go +++ b/logger/instance.go @@ -10,7 +10,7 @@ package logger import "go.uber.org/zap" var ( - Instance *zap.Logger = zap.NewNop() + Instance = zap.NewNop() ) // SetInstance 设置日志实例 @@ -27,4 +27,5 @@ const ( const ( CodeInjectCommonParam = "inject-common-param" + CodeLogicHook = "logic-hook" ) diff --git a/router/controller.go b/router/controller.go index 1b2e11c..97e1da1 100644 --- a/router/controller.go +++ b/router/controller.go @@ -109,6 +109,8 @@ func (c controller) methodConfig(reflectMethod reflect.Method) (cfg UriConfig, n cfg.TagList = strings.Split(metaField.Tag.Get(TagNameUriTag), ",") // 解析第一个返回值, 要求必须是结构体或者是map outputStrictModel := metaField.Tag.Get(TagNameOutputStrict) + hookSync := strings.ToLower(metaField.Tag.Get(TagNameHookSync)) + cfg.HookSync = hookSync == "1" || hookSync == "true" // 同步执行判断 cfg.OutputStrict = outputStrictModel == "1" || outputStrictModel == "true" if cfg.OutputStrict { // 开启输出严格模式校验 diff --git a/router/define.go b/router/define.go index bc49eb7..f629ce7 100644 --- a/router/define.go +++ b/router/define.go @@ -25,6 +25,7 @@ const ( TagNameUriTag = "tag" // 接口的tag TagNameDesc = "desc" // 接口的描述 TagNameOutputStrict = "output_strict" // 接口数据是否为严格模式 : 严格模式, 响应数据必须是结构体/map,非严格模式返回任意值 + TagNameHookSync = "hook-sync" // hook同步执行 TagNameBinding = "binding" // gin 内置的验证规则tag TagNameValidate = "validate" // validator v10 默认的验证规则tag TagNameErrMsg = "err" // 验证失败错误信息tag @@ -41,6 +42,7 @@ type UriConfig struct { TagList []string `json:"tag_list"` // 接口分组 Desc string `json:"desc"` // 接口描述 OutputStrict bool `json:"output_strict"` // 接口是否为严格模式 : 不配置,可返回任意类型, 配置, 必须返回结构体或者map + HookSync bool `json:"hook_sync"` // 接口主逻辑执行完成之后,hook是否同步执行, 默认异步执行 FormDataType reflect.Type `json:"-"` // 表单数据类型 ResultDataType reflect.Type `json:"-"` // 返回值数据类型 ApiStructValue reflect.Value `json:"-"` // 逻辑函数所属结构体取值 @@ -48,10 +50,6 @@ type UriConfig struct { } // UriParam 接口参数配置 -// -// Author : go_developer@163.com<白茶清欢> -// -// Date : 15:40 2025/1/27 type UriParam struct { Field string `json:"field"` // 结构体字段 Name string `json:"name"` // 参数名称 diff --git a/router/handler.go b/router/handler.go index 29a2d60..437e825 100644 --- a/router/handler.go +++ b/router/handler.go @@ -78,7 +78,8 @@ func (s *server) RequestHandler(uriCfg UriConfig) gin.HandlerFunc { // 非必传参数设置默认值 defaults.SetDefaults(formValue) - isSuccess := false + // 默认请求失败 + ctx.Set(consts.GinRequestSuccess, false) // 初始化响应之后logic logicAfterResponse := &define.LogicAfterResponse{ SuccessHookFuncList: make([]func(), 0), @@ -86,30 +87,8 @@ func (s *server) RequestHandler(uriCfg UriConfig) gin.HandlerFunc { 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() - } - } - } - }() - }() + ctx.Set(consts.GinLogicAfterResponseKey, logicAfterResponse) + defer s.hook(ctx, uriCfg) // 执行Logic之后的相关逻辑 // 执行逻辑 if uriCfg.FormDataType.Kind() != reflect.Ptr { inputValue = inputValue.Elem() @@ -118,8 +97,8 @@ func (s *server) RequestHandler(uriCfg UriConfig) gin.HandlerFunc { firstParam = reflect.ValueOf(ctx) resList := uriCfg.ApiLogicFunc.Func.Call([]reflect.Value{uriCfg.ApiStructValue, firstParam, inputValue}) if resList[1].IsNil() { - // 请求成功 - isSuccess = true + // 请求成功, 更新标识 + ctx.Set(consts.GinRequestSuccess, true) response.SuccessWithExtension(ctx, resList[0].Interface(), &define.ResponseOption{ContentType: consts.MimeTypeJson}) return } diff --git a/router/hook.go b/router/hook.go new file mode 100644 index 0000000..470999b --- /dev/null +++ b/router/hook.go @@ -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) + } + } + } +}