// Package router ... // // Description : router ... // // Author : go_developer@163.com<白茶清欢> // // Date : 2025-01-27 19:42 package router import ( "errors" "fmt" "net/http" "reflect" "sync" "time" "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/exception" "git.zhangdeman.cn/zhangdeman/gin/define" "git.zhangdeman.cn/zhangdeman/gin/logger" "git.zhangdeman.cn/zhangdeman/gin/request" "git.zhangdeman.cn/zhangdeman/gin/response" "git.zhangdeman.cn/zhangdeman/gin/util" loggerPkg "git.zhangdeman.cn/zhangdeman/logger" "git.zhangdeman.cn/zhangdeman/serialize" "git.zhangdeman.cn/zhangdeman/wrapper/op_string" "github.com/gin-gonic/gin" "github.com/mcuadros/go-defaults" ) func (s *server) getFormInitValue(ctx *gin.Context, uriCfg UriConfig) (any, error) { var ( formParam reflect.Value formValue any err error ) 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) logger.Instance.Error("参数解析出现异常", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeParamValidateFailure, map[string]any{ "err_msg": err.Error(), }).ToFieldList()...) return nil, err } // 非必传参数设置默认值 defaults.SetDefaults(formValue) return formValue, nil } func (s *server) initRequest(ctx *gin.Context, uriCfg UriConfig) (any, reflect.Value, exception.IException) { var ( err error formValue any inputValue reflect.Value ) if formValue, err = s.getFormInitValue(ctx, uriCfg); nil != err { return nil, inputValue, exception.NewFromError(http.StatusBadRequest, err) } // 表单数据 inputValue = reflect.ValueOf(formValue) if !uriCfg.ParamIsPtr { inputValue = inputValue.Elem() } // 注入公共参数 if err = s.injectCommonParam(ctx, inputValue); nil != err { return nil, inputValue, exception.NewFromError(500, err) } // 默认请求失败 ctx.Set(consts.GinRequestSuccess, false) // 初始化响应之后 logic logicAfterResponse := &define.LogicAfterResponse{ SuccessHookFuncList: make([]func(ctx *gin.Context), 0), FailureHookFuncList: make([]func(ctx *gin.Context), 0), Lock: &sync.RWMutex{}, } // 此处暴露出去,是为了使用方可以获取到对应数据 ctx.Set(consts.GinLogicAfterResponseKey, logicAfterResponse) return formValue, inputValue, nil } // callFunc 调用方法 func (s *server) callFunc(ctx *gin.Context, uriCfg UriConfig, inputValue reflect.Value) (any, exception.IException) { var ( firstParam = reflect.ValueOf(ctx) ) resList := uriCfg.ApiLogicFunc.Func.Call([]reflect.Value{uriCfg.ApiStructValue, firstParam, inputValue}) if resList[1].IsNil() { // 请求成功, 更新标识 ctx.Set(consts.GinRequestSuccess, true) return resList[0].Interface(), nil } return nil, s.formatError(ctx, resList[1].Interface()) } // formatError 格式化错误 func (s *server) formatError(ctx *gin.Context, err any) exception.IException { if nil == err { return nil } var ( ok bool e exception.IException innerErr error ) // 请求失败 if ok = errors.As(err.(error), &e); ok { // 本身就是exception.IException logger.Instance.Debug("请求结果err类型为 exception.IException, 无需特殊处理", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeLogicErrorWrapper, map[string]any{}).ToFieldList()...) } else if innerErr, ok = err.(error); ok { logger.Instance.Debug("请求结果err类型为 error, 包装为 exception.IException", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeLogicErrorWrapper, map[string]any{}).ToFieldList()...) e = exception.NewFromError(-1, innerErr) } else { logger.Instance.Debug("请求结果err类型 既不是 error 也不是 exception.IException, 包装为 exception.IException", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeLogicErrorWrapper, map[string]any{}).ToFieldList()...) e = exception.NewWithCodeAndData(-1, map[string]any{ "err": err, }) } return e } // RequestHandler 获取请求处理方法 func (s *server) RequestHandler(uriCfg UriConfig) gin.HandlerFunc { return func(ctx *gin.Context) { var ( err error e exception.IException handleResult any inputValue reflect.Value ) if _, inputValue, e = s.initRequest(ctx, uriCfg); nil != err { response.SendWithException(ctx, e, &define.ResponseOption{ ContentType: consts.MimeTypeJson, }) ctx.Abort() return } defer s.hook(ctx, uriCfg) // 执行 Logic 之后的相关逻辑 if handleResult, e = s.callFunc(ctx, uriCfg, inputValue); nil != e { response.SuccessWithExtension(ctx, handleResult, &define.ResponseOption{ContentType: consts.MimeTypeJson}) return } response.SendWithException(ctx, e, &define.ResponseOption{ ContentType: consts.MimeTypeJson, }) } } // SseHandler sse连接请求 func (s *server) SseHandler(uriCfg UriConfig) gin.HandlerFunc { return func(ctx *gin.Context) { var ( err error e exception.IException inputValue reflect.Value firstParam reflect.Value ) if _, inputValue, err = s.initRequest(ctx, uriCfg); nil != err { e = exception.NewFromError(http.StatusBadRequest, err) response.SendWithException(ctx, e, &define.ResponseOption{ ContentType: consts.MimeTypeJson, }) ctx.Abort() return } defer s.hook(ctx, uriCfg) // 执行 Logic 之后的相关逻辑 ctx.Writer.Header().Set(consts.HeaderKeyContentType.String(), "text/event-stream") ctx.Writer.Header().Set(consts.HeaderKeyCacheControl.String(), "no-cache") ctx.Writer.Header().Set(consts.HeaderKeyConnection.String(), "keep-alive") ctx.Writer.Header().Set(consts.HeaderKeyXAccelBuffering.String(), "no") flusher, _ := ctx.Writer.(http.Flusher) // 发送连接就绪消息 if _, err = fmt.Fprintf(ctx.Writer, define.SseMsgFormat, -1, "system", serialize.JSON.MarshalForStringIgnoreError(define.SseData{ ID: op_string.Random(8, ""), Object: "system", Created: time.Now().Unix(), Choices: []map[string]any{}, EventType: "connected", })); nil != err { // 无法推送数据, 等价于结束, 如有必要, 让客户端发起重连 logger.Instance.Error("SSE 连接建立成功后, 发送链接成功消息出现异常", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeLogicSseInitError, map[string]any{ "err_msg": err.Error(), }).ToFieldList()...) return } flusher.Flush() defer func() { // 发送连接关闭消息 if _, err = fmt.Fprintf(ctx.Writer, define.SseMsgFormat, -3, "system", serialize.JSON.MarshalForStringIgnoreError(define.SseData{ ID: op_string.Random(8, ""), Object: "system", Created: time.Now().Unix(), Choices: []map[string]any{}, EventType: "closed", })); nil != err { // 无法推送数据, 等价于结束, 如有必要, 让客户端发起重连 logger.Instance.Error("SSE 连接断开前, 发送链接断开消息出现异常", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeLogicSseClosedError, map[string]any{ "err_msg": err.Error(), }).ToFieldList()...) return } flusher.Flush() }() firstParam = reflect.ValueOf(ctx) resList := uriCfg.ApiLogicFunc.Func.Call([]reflect.Value{uriCfg.ApiStructValue, firstParam, inputValue}) if resList[1].IsNil() { // 请求成功, 更新标识 ctx.Set(consts.GinRequestSuccess, true) response.SuccessWithExtension(ctx, resList[0].Interface(), &define.ResponseOption{ContentType: consts.MimeTypeJson}) return } // 请求失败 e = s.formatError(ctx, resList[1].Interface()) if nil != e { // 异常终止 if _, err = fmt.Fprintf(ctx.Writer, define.SseMsgFormat, -3, "system", serialize.JSON.MarshalForStringIgnoreError(define.SseData{ ID: op_string.Random(8, ""), Object: "system", Created: time.Now().Unix(), Choices: []map[string]any{ { "err_msg": e.Message(), "err_code": e.Code(), "err_data": e.Data(), }, }, EventType: "failure", })); nil != err { // 无法推送数据, 等价于结束, 如有必要, 让客户端发起重连 logger.Instance.Error("SSE 业务处理完成, 发送业务处理失败消息出现异常", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeLogicSseClosedError, map[string]any{ "err_msg": err.Error(), }).ToFieldList()...) return } } else { // 正常终止 if _, err = fmt.Fprintf(ctx.Writer, define.SseMsgFormat, -3, "system", serialize.JSON.MarshalForStringIgnoreError(define.SseData{ ID: op_string.Random(8, ""), Object: "system", Created: time.Now().Unix(), Choices: []map[string]any{}, EventType: "success", })); nil != err { // 无法推送数据, 等价于结束, 如有必要, 让客户端发起重连 logger.Instance.Error("SSE 业务处理完成, 发送业务处理成功消息出现异常", loggerPkg.NewLogData(util.GinCtxToContext(ctx), logger.RecordType, logger.CodeLogicSseClosedError, map[string]any{ "err_msg": err.Error(), }).ToFieldList()...) return } } } }