From dc20a86b338b8f87cdbde852558a998fb2c5da38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=9F=E5=B8=85?= <133814250@qq.com> Date: Wed, 24 Apr 2024 23:25:29 +0800 Subject: [PATCH] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8Dwebsocket=E5=9C=A8?= =?UTF-8?q?=E6=9F=90=E4=BA=9B=E6=83=85=E5=86=B5=E4=B8=8B=E4=B8=8D=E9=87=8D?= =?UTF-8?q?=E8=BF=9E=E9=97=AE=E9=A2=98=20fix=20=E4=BF=AE=E5=A4=8D=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=97=A5=E5=BF=97=E6=9F=A5=E7=9C=8B=E6=9D=83=E9=99=90?= =?UTF-8?q?=20feat=20=E8=AE=BF=E9=97=AE=E6=97=A5=E5=BF=97=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=8E=A5=E5=8F=A3=E4=BF=A1=E6=81=AF=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=20perf=20=E4=B8=BA=E6=89=80=E6=9C=89orm=E7=9A=84Insert?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E5=A2=9E=E5=8A=A0OmitEmptyData=E9=80=89?= =?UTF-8?q?=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/addons/hgexample/logic/sys/table.go | 2 +- .../addons/hgexample/logic/sys/tree_table.go | 2 +- server/api/admin/loginlog/loginlog.go | 10 - server/internal/consts/http.go | 47 +++++ .../controller/admin/sys/login_log.go | 12 -- server/internal/global/httproutes.go | 125 ++++++++++++ server/internal/library/addons/install.go | 2 +- server/internal/library/casbin/adapter.go | 6 +- .../hggen/views/curd_generate_logic.go | 2 +- .../library/hgorm/hook/tenant_test.go | 2 +- server/internal/library/storager/upload.go | 2 +- server/internal/logic/admin/cash.go | 2 +- server/internal/logic/admin/credits_log.go | 4 +- server/internal/logic/admin/dept.go | 2 +- server/internal/logic/admin/member.go | 2 +- server/internal/logic/admin/member_post.go | 2 +- server/internal/logic/admin/menu.go | 2 +- server/internal/logic/admin/notice.go | 4 +- server/internal/logic/admin/order.go | 4 +- server/internal/logic/admin/post.go | 2 +- server/internal/logic/admin/role.go | 4 +- server/internal/logic/admin/site.go | 2 +- server/internal/logic/middleware/init.go | 9 +- .../internal/logic/middleware/pre_filter.go | 49 +---- server/internal/logic/pay/create.go | 2 +- server/internal/logic/pay/pay.go | 2 +- server/internal/logic/pay/refund.go | 2 +- server/internal/logic/sys/blacklist.go | 2 +- server/internal/logic/sys/cron.go | 2 +- server/internal/logic/sys/cron_group.go | 2 +- server/internal/logic/sys/curd_demo.go | 2 +- server/internal/logic/sys/dict_data.go | 2 +- server/internal/logic/sys/dict_type.go | 2 +- server/internal/logic/sys/ems_log.go | 4 +- server/internal/logic/sys/gen_codes.go | 2 +- server/internal/logic/sys/log.go | 95 ++++++--- server/internal/logic/sys/login_log.go | 18 +- server/internal/logic/sys/normal_tree_demo.go | 4 +- server/internal/logic/sys/option_tree_demo.go | 4 +- server/internal/logic/sys/provinces.go | 2 +- server/internal/logic/sys/serve_license.go | 2 +- server/internal/logic/sys/serve_log.go | 2 +- server/internal/logic/sys/sms_log.go | 2 +- server/internal/logic/sys/test_category.go | 4 +- server/internal/model/input/adminin/member.go | 2 +- server/internal/model/input/sysin/log.go | 22 ++- server/internal/service/middleware.go | 8 - server/internal/service/sys.go | 12 +- web/package.json | 2 +- web/src/api/loginLog/index.ts | 9 - web/src/utils/websocket/index.ts | 16 +- web/src/views/log/ems-log/index.vue | 4 +- web/src/views/log/log/columns.ts | 183 ++++++++++++------ web/src/views/log/log/index.vue | 142 +------------- web/src/views/log/log/model.ts | 134 +++++++++++++ web/src/views/log/log/view.vue | 30 +-- web/vite.config.ts | 2 +- 57 files changed, 622 insertions(+), 401 deletions(-) create mode 100644 server/internal/global/httproutes.go create mode 100644 web/src/views/log/log/model.ts diff --git a/server/addons/hgexample/logic/sys/table.go b/server/addons/hgexample/logic/sys/table.go index 71b58c5..41d0e32 100644 --- a/server/addons/hgexample/logic/sys/table.go +++ b/server/addons/hgexample/logic/sys/table.go @@ -153,7 +153,7 @@ func (s *sSysTable) Edit(ctx context.Context, in *sysin.TableEditInp) (err error // 新增 in.CreatedBy = contexts.GetUserId(ctx) - if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Data(in).Insert(); err != nil { + if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Data(in).OmitEmptyData().Insert(); err != nil { err = gerror.Wrap(err, "新增表格失败,请稍后重试!") return } diff --git a/server/addons/hgexample/logic/sys/tree_table.go b/server/addons/hgexample/logic/sys/tree_table.go index b2f417b..06562a4 100644 --- a/server/addons/hgexample/logic/sys/tree_table.go +++ b/server/addons/hgexample/logic/sys/tree_table.go @@ -125,7 +125,7 @@ func (s *sSysTreeTable) Edit(ctx context.Context, in *sysin.TableEditInp) (err e } } else { in.CreatedBy = contexts.GetUserId(ctx) - if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Data(in).Insert(); err != nil { + if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Data(in).OmitEmptyData().Insert(); err != nil { err = gerror.Wrap(err, "新增表格失败,请稍后重试!") return err } diff --git a/server/api/admin/loginlog/loginlog.go b/server/api/admin/loginlog/loginlog.go index 903ad8f..6fd3710 100644 --- a/server/api/admin/loginlog/loginlog.go +++ b/server/api/admin/loginlog/loginlog.go @@ -30,16 +30,6 @@ type ExportReq struct { type ExportRes struct{} -// ViewReq 获取登录日志指定信息 -type ViewReq struct { - g.Meta `path:"/loginLog/view" method:"get" tags:"登录日志" summary:"获取登录日志指定信息"` - sysin.LoginLogViewInp -} - -type ViewRes struct { - *sysin.LoginLogViewModel -} - // DeleteReq 删除登录日志 type DeleteReq struct { g.Meta `path:"/loginLog/delete" method:"post" tags:"登录日志" summary:"删除登录日志"` diff --git a/server/internal/consts/http.go b/server/internal/consts/http.go index 27fdae8..1bb4380 100644 --- a/server/internal/consts/http.go +++ b/server/internal/consts/http.go @@ -5,9 +5,56 @@ // @License https://github.com/bufanyun/hotgo/blob/master/LICENSE package consts +import ( + "fmt" + "github.com/gogf/gf/v2/errors/gcode" + "hotgo/internal/library/dict" + "hotgo/internal/model" + "net/http" +) + +func init() { + dict.RegisterEnums("HTTPMethod", "HTTP请求方式选项", HTTPMethodOptions) + dict.RegisterEnums("HTTPHandlerTime", "HTTP处理耗时选项", HTTPHandlerTimeOptions) + dict.RegisterEnums("HTTPApiCode", "HTTP接口状态码选项", HTTPApiCodeOptions) +} + const ( HTTPContentTypeXml = "text/xml" HTTPContentTypeHtml = "text/html" HTTPContentTypeStream = "text/event-stream" HTTPContentTypeJson = "application/json" ) + +// HTTPMethodOptions HTTP请求方式选项 +var HTTPMethodOptions = []*model.Option{ + dict.GenSuccessOption(http.MethodGet, "GET"), + dict.GenInfoOption(http.MethodPost, "POST"), + dict.GenSuccessOption(http.MethodPut, "PUT"), + dict.GenInfoOption(http.MethodDelete, "DELETE"), +} + +const ( + HTTPHandlerTime50 = "< 50" + HTTPHandlerTime200 = "< 200" + HTTPHandlerTime200To500 = "BETWEEN 200 AND 500" + HTTPHandlerTime500To1000 = "BETWEEN 500 AND 1000" + HTTPHandlerTime1000To10000 = "BETWEEN 1000 AND 10000" + HTTPHandlerTime10000UP = "> 10000" +) + +// HTTPHandlerTimeOptions HTTP处理耗时选项 +var HTTPHandlerTimeOptions = []*model.Option{ + dict.GenSuccessOption(HTTPHandlerTime50, "50ms以内"), + dict.GenInfoOption(HTTPHandlerTime200, "200ms以内"), + dict.GenSuccessOption(HTTPHandlerTime200To500, "200~500ms"), + dict.GenSuccessOption(HTTPHandlerTime500To1000, "500~1000ms"), + dict.GenInfoOption(HTTPHandlerTime1000To10000, "1000~10000ms"), + dict.GenInfoOption(HTTPHandlerTime10000UP, "10000ms以上"), +} + +// HTTPApiCodeOptions HTTP接口状态码选项 +var HTTPApiCodeOptions = []*model.Option{ + dict.GenSuccessOption(gcode.CodeOK.Code(), fmt.Sprintf("%v %v", gcode.CodeOK.Code(), "成功")), + dict.GenWarningOption(gcode.CodeNil.Code(), fmt.Sprintf("%v %v", gcode.CodeNil.Code(), "失败")), +} diff --git a/server/internal/controller/admin/sys/login_log.go b/server/internal/controller/admin/sys/login_log.go index 50fa374..6123c58 100644 --- a/server/internal/controller/admin/sys/login_log.go +++ b/server/internal/controller/admin/sys/login_log.go @@ -38,18 +38,6 @@ func (c *cLoginLog) Export(ctx context.Context, req *loginlog.ExportReq) (res *l return } -// View 获取指定登录日志信息 -func (c *cLoginLog) View(ctx context.Context, req *loginlog.ViewReq) (res *loginlog.ViewRes, err error) { - data, err := service.SysLoginLog().View(ctx, &req.LoginLogViewInp) - if err != nil { - return - } - - res = new(loginlog.ViewRes) - res.LoginLogViewModel = data - return -} - // Delete 删除登录日志 func (c *cLoginLog) Delete(ctx context.Context, req *loginlog.DeleteReq) (res *loginlog.DeleteRes, err error) { err = service.SysLoginLog().Delete(ctx, &req.LoginLogDeleteInp) diff --git a/server/internal/global/httproutes.go b/server/internal/global/httproutes.go new file mode 100644 index 0000000..23952ea --- /dev/null +++ b/server/internal/global/httproutes.go @@ -0,0 +1,125 @@ +// Package global +// @Link https://github.com/bufanyun/hotgo +// @Copyright Copyright (c) 2024 HotGo CLI +// @Author Ms <133814250@qq.com> +// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE +package global + +import ( + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/util/gmeta" + "github.com/gogf/gf/v2/util/gtag" + "reflect" + "strings" + "sync" +) + +// HTTPRouter http路由 +type HTTPRouter struct { + ghttp.RouterItem + Tags string `json:"tags" dc:"接口所属的标签,用于接口分类"` + Summary string `json:"summary" dc:"接口/参数概要描述"` + Description string `json:"description" dc:"接口/参数详细描述"` +} + +var ( + httpRoutes map[string]*HTTPRouter + routeMutex sync.Mutex + shortTypeMapForTag = map[string]string{ + gtag.SummaryShort: gtag.Summary, + gtag.SummaryShort2: gtag.Summary, + gtag.DescriptionShort: gtag.Description, + gtag.DescriptionShort2: gtag.Description, + } +) + +// GetRequestRoute 获取当前请求路由属性 +func GetRequestRoute(r *ghttp.Request) *HTTPRouter { + key := GenFilterRequestKey(r) + routes := LoadHTTPRoutes(r) + router, ok := routes[key] + if !ok { + return nil + } + return router +} + +// GenFilterRequestKey 根据请求生成唯一key +func GenFilterRequestKey(r *ghttp.Request) string { + return GenRouteKey(r.Method, r.Request.URL.Path) +} + +// GenFilterRouteKey 根据路由生成唯一key +func GenFilterRouteKey(r *ghttp.Router) string { + return GenRouteKey(r.Method, r.Uri) +} + +// GenRouteKey 生成唯一key +func GenRouteKey(method, path string) string { + return strings.ToUpper(method) + " " + path +} + +func LoadHTTPRoutes(r *ghttp.Request) map[string]*HTTPRouter { + if httpRoutes == nil { + routeMutex.Lock() + defer routeMutex.Unlock() + + if httpRoutes != nil { + return httpRoutes + } + + httpRoutes = make(map[string]*HTTPRouter, len(r.Server.GetRoutes())) + for _, v := range r.Server.GetRoutes() { + key := GenFilterRouteKey(v.Handler.Router) + if _, ok := httpRoutes[key]; !ok { + router := new(HTTPRouter) + router.RouterItem = v + httpRoutes[key] = setRouterMeta(router) + } + } + + } + return httpRoutes +} + +func setRouterMeta(router *HTTPRouter) *HTTPRouter { + if !router.RouterItem.Handler.Info.IsStrictRoute { + return router + } + + var reflectValue = reflect.ValueOf(router.Handler.Info.Value.Interface()) + for reflectValue.Kind() == reflect.Ptr { + reflectValue = reflectValue.Elem() + } + + if reflectValue.Kind() != reflect.Func { + return router + } + + var reflectType = reflect.TypeOf(router.Handler.Info.Value.Interface()) + if reflectType.NumIn() != 2 || reflectType.NumOut() != 2 { + return router + } + + var inputObject reflect.Value + if reflectType.In(1).Kind() == reflect.Ptr { + inputObject = reflect.New(reflectType.In(1).Elem()).Elem() + } else { + inputObject = reflect.New(reflectType.In(1)).Elem() + } + + inputMetaMap := fillMapWithShortTags(gmeta.Data(inputObject.Interface())) + router.Tags = inputMetaMap["tags"] + router.Summary = inputMetaMap[gtag.Summary] + router.Description = inputMetaMap[gtag.Description] + return router +} + +func fillMapWithShortTags(m map[string]string) map[string]string { + for k, v := range shortTypeMapForTag { + if m[v] == "" && m[k] != "" { + m[v] = m[k] + } + } + return m +} diff --git a/server/internal/library/addons/install.go b/server/internal/library/addons/install.go index 2614af5..1913fc9 100644 --- a/server/internal/library/addons/install.go +++ b/server/internal/library/addons/install.go @@ -65,7 +65,7 @@ func Install(m Module) (err error) { _, _ = GetModel(m.Ctx()).Where("id", record.Id).Delete() } - if _, err = GetModel(m.Ctx()).Data(data).Insert(); err != nil { + if _, err = GetModel(m.Ctx()).Data(data).OmitEmptyData().Insert(); err != nil { return err } return m.Install(ctx) diff --git a/server/internal/library/casbin/adapter.go b/server/internal/library/casbin/adapter.go index afdd42a..b6cd69c 100644 --- a/server/internal/library/casbin/adapter.go +++ b/server/internal/library/casbin/adapter.go @@ -151,7 +151,7 @@ func (a *adapter) SavePolicy(model model.Model) (err error) { } if count := len(policyRules); count > 0 { - if _, err = a.model().Insert(policyRules); err != nil { + if _, err = a.model().OmitEmptyData().Insert(policyRules); err != nil { return } } @@ -160,7 +160,7 @@ func (a *adapter) SavePolicy(model model.Model) (err error) { // AddPolicy adds a policy rule to the storage. func (a *adapter) AddPolicy(sec string, ptype string, rule []string) (err error) { - _, err = a.model().Insert(a.buildPolicyRule(ptype, rule)) + _, err = a.model().OmitEmptyData().Insert(a.buildPolicyRule(ptype, rule)) return } @@ -176,7 +176,7 @@ func (a *adapter) AddPolicies(sec string, ptype string, rules [][]string) (err e policyRules = append(policyRules, a.buildPolicyRule(ptype, rule)) } - _, err = a.model().Insert(policyRules) + _, err = a.model().OmitEmptyData().Insert(policyRules) return } diff --git a/server/internal/library/hggen/views/curd_generate_logic.go b/server/internal/library/hggen/views/curd_generate_logic.go index 08db2de..c2bdb33 100644 --- a/server/internal/library/hggen/views/curd_generate_logic.go +++ b/server/internal/library/hggen/views/curd_generate_logic.go @@ -19,7 +19,7 @@ const ( LogicWhereComments = "\n\t// 查询%s\n" LogicWhereNoSupport = "\t// TODO 暂不支持生成[ %s ]查询方式,请自行补充此处代码!" LogicEditUpdate = "\tif _, err = s.Model(ctx%s).\n\t\t\tFields(%sin.%sUpdateFields{}).\n\t\t\tWherePri(in.%s).Data(in).Update(); err != nil {\n\t\t\terr = gerror.Wrap(err, \"修改%s失败,请稍后重试!\")\n\t\t}\n\t\treturn" - LogicEditInsert = "\tif _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).\n\t\tFields(%sin.%sInsertFields{}).\n\t\tData(in).Insert(); err != nil {\n\t\terr = gerror.Wrap(err, \"新增%s失败,请稍后重试!\")\n\t}" + LogicEditInsert = "\tif _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).\n\t\tFields(%sin.%sInsertFields{}).\n\t\tData(in).OmitEmptyData().Insert(); err != nil {\n\t\terr = gerror.Wrap(err, \"新增%s失败,请稍后重试!\")\n\t}" LogicEditUnique = "\t// 验证'%s'唯一\n\tif err = hgorm.IsUnique(ctx, &dao.%s, g.Map{dao.%s.Columns().%s: in.%s}, \"%s已存在\", in.Id); err != nil {\n\t\treturn\n\t}\n" LogicSwitchUpdate = "g.Map{\n\t\tin.Key: in.Value,\n%s}" LogicStatusUpdate = "g.Map{\n\t\tdao.%s.Columns().Status: in.Status,\n%s}" diff --git a/server/internal/library/hgorm/hook/tenant_test.go b/server/internal/library/hgorm/hook/tenant_test.go index cc8aac5..12e9c7c 100644 --- a/server/internal/library/hgorm/hook/tenant_test.go +++ b/server/internal/library/hgorm/hook/tenant_test.go @@ -50,7 +50,7 @@ func TestSaveTenant_User(t *testing.T) { cols.Status: consts.PayStatusWait, } - _, err := dao.AddonHgexampleTenantOrder.Ctx(ctx).Data(data).Hook(hook.SaveTenant).Insert() + _, err := dao.AddonHgexampleTenantOrder.Ctx(ctx).Data(data).Hook(hook.SaveTenant).OmitEmptyData().Insert() if err != nil { panic(err) } diff --git a/server/internal/library/storager/upload.go b/server/internal/library/storager/upload.go index e93907b..78f9761 100644 --- a/server/internal/library/storager/upload.go +++ b/server/internal/library/storager/upload.go @@ -233,7 +233,7 @@ func write(ctx context.Context, meta *FileMeta, fullPath string) (models *entity Status: consts.StatusEnabled, } - id, err := GetModel(ctx).Data(models).InsertAndGetId() + id, err := GetModel(ctx).Data(models).OmitEmptyData().InsertAndGetId() if err != nil { return } diff --git a/server/internal/logic/admin/cash.go b/server/internal/logic/admin/cash.go index 15efa4f..9e6faba 100644 --- a/server/internal/logic/admin/cash.go +++ b/server/internal/logic/admin/cash.go @@ -225,7 +225,7 @@ func (s *sAdminCash) Apply(ctx context.Context, in *adminin.CashApplyInp) (err e "status": consts.CashStatusWait, "msg": "", "ip": location.GetClientIp(ghttp.RequestFromCtx(ctx)), - }).InsertAndGetId() + }).OmitEmptyData().InsertAndGetId() if err != nil { return } diff --git a/server/internal/logic/admin/credits_log.go b/server/internal/logic/admin/credits_log.go index 327ddc4..3f5c61f 100644 --- a/server/internal/logic/admin/credits_log.go +++ b/server/internal/logic/admin/credits_log.go @@ -97,7 +97,7 @@ func (s *sAdminCreditsLog) SaveBalance(ctx context.Context, in *adminin.CreditsL data.Ip = in.Ip data.MapId = in.MapId - _, err = dao.AdminCreditsLog.Ctx(ctx).Data(data).Insert() + _, err = dao.AdminCreditsLog.Ctx(ctx).Data(data).OmitEmptyData().Insert() return } @@ -156,7 +156,7 @@ func (s *sAdminCreditsLog) SaveIntegral(ctx context.Context, in *adminin.Credits data.Ip = in.Ip data.MapId = in.MapId - _, err = dao.AdminCreditsLog.Ctx(ctx).Data(data).Insert() + _, err = dao.AdminCreditsLog.Ctx(ctx).Data(data).OmitEmptyData().Insert() return } diff --git a/server/internal/logic/admin/dept.go b/server/internal/logic/admin/dept.go index 09622a6..105d331 100644 --- a/server/internal/logic/admin/dept.go +++ b/server/internal/logic/admin/dept.go @@ -117,7 +117,7 @@ func (s *sAdminDept) Edit(ctx context.Context, in *adminin.DeptEditInp) (err err } // 新增 - if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Data(in).Insert(); err != nil { + if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Data(in).OmitEmptyData().Insert(); err != nil { err = gerror.Wrap(err, "新增部门管理失败,请稍后重试!") } return diff --git a/server/internal/logic/admin/member.go b/server/internal/logic/admin/member.go index 0f8d3b5..1104bc3 100644 --- a/server/internal/logic/admin/member.go +++ b/server/internal/logic/admin/member.go @@ -564,7 +564,7 @@ func (s *sAdminMember) Edit(ctx context.Context, in *adminin.MemberEditInp) (err } return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { - id, err := dao.AdminMember.Ctx(ctx).Data(data).InsertAndGetId() + id, err := dao.AdminMember.Ctx(ctx).Data(data).OmitEmptyData().InsertAndGetId() if err != nil { err = gerror.Wrap(err, "新增用户失败,请稍后重试!") return diff --git a/server/internal/logic/admin/member_post.go b/server/internal/logic/admin/member_post.go index d916f8e..7ed8f3f 100644 --- a/server/internal/logic/admin/member_post.go +++ b/server/internal/logic/admin/member_post.go @@ -31,7 +31,7 @@ func (s *sAdminMemberPost) UpdatePostIds(ctx context.Context, memberId int64, po } for i := 0; i < len(postIds); i++ { - _, err = dao.AdminMemberPost.Ctx(ctx).Insert(entity.AdminMemberPost{ + _, err = dao.AdminMemberPost.Ctx(ctx).OmitEmptyData().Insert(entity.AdminMemberPost{ MemberId: memberId, PostId: postIds[i], }) diff --git a/server/internal/logic/admin/menu.go b/server/internal/logic/admin/menu.go index e0159bd..47cde76 100644 --- a/server/internal/logic/admin/menu.go +++ b/server/internal/logic/admin/menu.go @@ -110,7 +110,7 @@ func (s *sAdminMenu) Edit(ctx context.Context, in *adminin.MenuEditInp) (err err return err } } else { - if _, err = dao.AdminMenu.Ctx(ctx).Data(in).Insert(); err != nil { + if _, err = dao.AdminMenu.Ctx(ctx).Data(in).OmitEmptyData().Insert(); err != nil { err = gerror.Wrap(err, "新增菜单失败!") return err } diff --git a/server/internal/logic/admin/notice.go b/server/internal/logic/admin/notice.go index 5c61954..e439d87 100644 --- a/server/internal/logic/admin/notice.go +++ b/server/internal/logic/admin/notice.go @@ -88,7 +88,7 @@ func (s *sAdminNotice) Edit(ctx context.Context, in *adminin.NoticeEditInp) (err // 新增 in.CreatedBy = member.Id in.CreatedAt = gtime.Now() - in.Id, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Data(in).InsertAndGetId() + in.Id, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Data(in).OmitEmptyData().InsertAndGetId() if err != nil { err = gerror.Wrap(err, consts.ErrorORM) return err @@ -426,7 +426,7 @@ func (s *sAdminNotice) updatedReadClicks(ctx context.Context, noticeId, memberId } if models == nil { - _, err = dao.AdminNoticeRead.Ctx(ctx).Data(entity.AdminNoticeRead{NoticeId: noticeId, MemberId: memberId}).Insert() + _, err = dao.AdminNoticeRead.Ctx(ctx).Data(entity.AdminNoticeRead{NoticeId: noticeId, MemberId: memberId}).OmitEmptyData().Insert() return } _, err = dao.AdminNoticeRead.Ctx(ctx).Where(dao.AdminNoticeRead.Columns().Id, models.Id).Increment(dao.AdminNoticeRead.Columns().Clicks, 1) diff --git a/server/internal/logic/admin/order.go b/server/internal/logic/admin/order.go index 510053e..e0edd5e 100644 --- a/server/internal/logic/admin/order.go +++ b/server/internal/logic/admin/order.go @@ -224,7 +224,7 @@ func (s *sAdminOrder) Create(ctx context.Context, in *adminin.OrderCreateInp) (r Money: in.Money, Remark: in.Remark, Status: consts.OrderStatusNotPay, - }).Insert() + }).OmitEmptyData().Insert() if err != nil { return } @@ -348,7 +348,7 @@ func (s *sAdminOrder) Edit(ctx context.Context, in *adminin.OrderEditInp) (err e FieldsEx( dao.AdminOrder.Columns().Id, ). - Data(in).Insert() + Data(in).OmitEmptyData().Insert() return } diff --git a/server/internal/logic/admin/post.go b/server/internal/logic/admin/post.go index d0b80d5..67ae7d2 100644 --- a/server/internal/logic/admin/post.go +++ b/server/internal/logic/admin/post.go @@ -96,7 +96,7 @@ func (s *sAdminPost) Edit(ctx context.Context, in *adminin.PostEditInp) (err err } // 新增 - _, err = dao.AdminPost.Ctx(ctx).Data(in).Insert() + _, err = dao.AdminPost.Ctx(ctx).Data(in).OmitEmptyData().Insert() return } diff --git a/server/internal/logic/admin/role.go b/server/internal/logic/admin/role.go index ad02cfb..32d6799 100644 --- a/server/internal/logic/admin/role.go +++ b/server/internal/logic/admin/role.go @@ -146,7 +146,7 @@ func (s *sAdminRole) UpdatePermissions(ctx context.Context, in *adminin.UpdatePe }) } - if _, err = dao.AdminRoleMenu.Ctx(ctx).Data(list).Insert(); err != nil { + if _, err = dao.AdminRoleMenu.Ctx(ctx).Data(list).OmitEmptyData().Insert(); err != nil { err = gerror.Wrap(err, consts.ErrorORM) return } @@ -189,7 +189,7 @@ func (s *sAdminRole) Edit(ctx context.Context, in *adminin.RoleEditInp) (err err } // 新增 - if _, err = dao.AdminRole.Ctx(ctx).Fields(adminin.RoleInsertFields{}).Data(in).Insert(); err != nil { + if _, err = dao.AdminRole.Ctx(ctx).Fields(adminin.RoleInsertFields{}).Data(in).OmitEmptyData().Insert(); err != nil { err = gerror.Wrap(err, consts.ErrorORM) return } diff --git a/server/internal/logic/admin/site.go b/server/internal/logic/admin/site.go index e1f3e10..d22948c 100644 --- a/server/internal/logic/admin/site.go +++ b/server/internal/logic/admin/site.go @@ -126,7 +126,7 @@ func (s *sAdminSite) Register(ctx context.Context, in *adminin.RegisterInp) (err // 提交注册信息 return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { - id, err := dao.AdminMember.Ctx(ctx).Data(data).InsertAndGetId() + id, err := dao.AdminMember.Ctx(ctx).Data(data).OmitEmptyData().InsertAndGetId() if err != nil { err = gerror.Wrap(err, consts.ErrorORM) return diff --git a/server/internal/logic/middleware/init.go b/server/internal/logic/middleware/init.go index 07c1834..0e6a2a9 100644 --- a/server/internal/logic/middleware/init.go +++ b/server/internal/logic/middleware/init.go @@ -27,15 +27,12 @@ import ( "hotgo/utility/validate" "net/http" "strings" - "sync" ) type sMiddleware struct { - LoginUrl string // 登录路由地址 - DemoWhiteList g.Map // 演示模式放行的路由白名单 - NotRecordRequest g.Map // 不记录请求数据的路由(当前请求数据过大时会影响响应效率,可以将路径放到该选项中改善) - FilterRoutes map[string]ghttp.RouterItem // 支持预处理的web路由 - routeMutex sync.Mutex + LoginUrl string // 登录路由地址 + DemoWhiteList g.Map // 演示模式放行的路由白名单 + NotRecordRequest g.Map // 不记录请求数据的路由(当前请求数据过大时会影响响应效率,可以将路径放到该选项中改善) } func init() { diff --git a/server/internal/logic/middleware/pre_filter.go b/server/internal/logic/middleware/pre_filter.go index 2f8e42d..d1e346c 100644 --- a/server/internal/logic/middleware/pre_filter.go +++ b/server/internal/logic/middleware/pre_filter.go @@ -9,58 +9,17 @@ import ( "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/util/gconv" + "hotgo/internal/global" "hotgo/internal/library/response" "hotgo/utility/validate" "reflect" ) -// GetFilterRoutes 获取支持预处理的web路由 -func (s *sMiddleware) GetFilterRoutes(r *ghttp.Request) map[string]ghttp.RouterItem { - // 首次访问时加载 - if s.FilterRoutes == nil { - s.routeMutex.Lock() - defer s.routeMutex.Unlock() - - if s.FilterRoutes != nil { - return s.FilterRoutes - } - - s.FilterRoutes = make(map[string]ghttp.RouterItem) - for _, v := range r.Server.GetRoutes() { - // 非规范路由不加载 - if v.Handler.Info.Type.NumIn() != 2 { - continue - } - - key := s.GenFilterRouteKey(v.Handler.Router) - if _, ok := s.FilterRoutes[key]; !ok { - s.FilterRoutes[key] = v - } - } - } - return s.FilterRoutes -} - -// GenFilterRequestKey 根据请求生成唯一key -func (s *sMiddleware) GenFilterRequestKey(r *ghttp.Request) string { - return s.GenRouteKey(r.Method, r.Request.URL.Path) -} - -// GenFilterRouteKey 根据路由生成唯一key -func (s *sMiddleware) GenFilterRouteKey(r *ghttp.Router) string { - return s.GenRouteKey(r.Method, r.Uri) -} - -// GenRouteKey 生成唯一key -func (s *sMiddleware) GenRouteKey(method, path string) string { - return method + " " + path -} - // PreFilter 请求输入预处理 // api使用gf规范路由并且XxxReq结构体实现了validate.Filter接口即可 func (s *sMiddleware) PreFilter(r *ghttp.Request) { - router, ok := s.GetFilterRoutes(r)[s.GenFilterRequestKey(r)] - if !ok { + router := global.GetRequestRoute(r) + if router == nil { r.Middleware.Next() return } @@ -89,7 +48,7 @@ func (s *sMiddleware) PreFilter(r *ghttp.Request) { } // 没有实现预处理 - if _, ok = inputObject.Interface().(validate.Filter); !ok { + if _, ok := inputObject.Interface().(validate.Filter); !ok { r.Middleware.Next() return } diff --git a/server/internal/logic/pay/create.go b/server/internal/logic/pay/create.go index 58da20d..ded2fa5 100644 --- a/server/internal/logic/pay/create.go +++ b/server/internal/logic/pay/create.go @@ -92,7 +92,7 @@ func (s *sPay) Create(ctx context.Context, in payin.PayCreateInp) (res *payin.Pa } // 创建支付记录 - if _, err = s.Model(ctx).Data(data).Insert(); err != nil { + if _, err = s.Model(ctx).Data(data).OmitEmptyData().Insert(); err != nil { return } diff --git a/server/internal/logic/pay/pay.go b/server/internal/logic/pay/pay.go index 68eb8de..68aeec7 100644 --- a/server/internal/logic/pay/pay.go +++ b/server/internal/logic/pay/pay.go @@ -110,7 +110,7 @@ func (s *sPay) Edit(ctx context.Context, in payin.PayEditInp) (err error) { } // 新增 - _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Data(in).Insert() + _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Data(in).OmitEmptyData().Insert() return } diff --git a/server/internal/logic/pay/refund.go b/server/internal/logic/pay/refund.go index 5bd820e..17a58d8 100644 --- a/server/internal/logic/pay/refund.go +++ b/server/internal/logic/pay/refund.go @@ -132,7 +132,7 @@ func (s *sPayRefund) Refund(ctx context.Context, in *payin.PayRefundInp) (res *p } // 创建退款记录 - if _, err = s.Model(ctx).Data(data).Insert(); err != nil { + if _, err = s.Model(ctx).Data(data).OmitEmptyData().Insert(); err != nil { return } return diff --git a/server/internal/logic/sys/blacklist.go b/server/internal/logic/sys/blacklist.go index d3637ac..808709c 100644 --- a/server/internal/logic/sys/blacklist.go +++ b/server/internal/logic/sys/blacklist.go @@ -60,7 +60,7 @@ func (s *sSysBlacklist) Edit(ctx context.Context, in *sysin.BlacklistEditInp) (e } // 新增 - _, err = dao.SysBlacklist.Ctx(ctx).Data(in).Insert() + _, err = dao.SysBlacklist.Ctx(ctx).Data(in).OmitEmptyData().Insert() return } diff --git a/server/internal/logic/sys/cron.go b/server/internal/logic/sys/cron.go index 86b0738..44d3d8a 100644 --- a/server/internal/logic/sys/cron.go +++ b/server/internal/logic/sys/cron.go @@ -89,7 +89,7 @@ func (s *sSysCron) Edit(ctx context.Context, in *sysin.CronEditInp) (err error) } // 新增 - in.SysCron.Id, err = dao.SysCron.Ctx(ctx).Data(in).InsertAndGetId() + in.SysCron.Id, err = dao.SysCron.Ctx(ctx).Data(in).OmitEmptyData().InsertAndGetId() if err != nil || in.SysCron.Id < 1 { return } diff --git a/server/internal/logic/sys/cron_group.go b/server/internal/logic/sys/cron_group.go index ebd97cc..3cc63eb 100644 --- a/server/internal/logic/sys/cron_group.go +++ b/server/internal/logic/sys/cron_group.go @@ -43,7 +43,7 @@ func (s *sSysCronGroup) Edit(ctx context.Context, in *sysin.CronGroupEditInp) (e } // 新增 - if _, err = dao.SysCronGroup.Ctx(ctx).Fields(sysin.CronGroupInsertFields{}).Data(in).Insert(); err != nil { + if _, err = dao.SysCronGroup.Ctx(ctx).Fields(sysin.CronGroupInsertFields{}).Data(in).OmitEmptyData().Insert(); err != nil { err = gerror.Wrap(err, consts.ErrorORM) } return diff --git a/server/internal/logic/sys/curd_demo.go b/server/internal/logic/sys/curd_demo.go index 29da24c..3f601b4 100644 --- a/server/internal/logic/sys/curd_demo.go +++ b/server/internal/logic/sys/curd_demo.go @@ -161,7 +161,7 @@ func (s *sSysCurdDemo) Edit(ctx context.Context, in *sysin.CurdDemoEditInp) (err in.CreatedBy = contexts.GetUserId(ctx) if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}). Fields(sysin.CurdDemoInsertFields{}). - Data(in).Insert(); err != nil { + Data(in).OmitEmptyData().Insert(); err != nil { err = gerror.Wrap(err, "新增CURD列表失败,请稍后重试!") } return diff --git a/server/internal/logic/sys/dict_data.go b/server/internal/logic/sys/dict_data.go index 88f94a8..c739b0f 100644 --- a/server/internal/logic/sys/dict_data.go +++ b/server/internal/logic/sys/dict_data.go @@ -60,7 +60,7 @@ func (s *sSysDictData) Edit(ctx context.Context, in *sysin.DictDataEditInp) (err return gerror.Wrap(err, "类型选择无效,请检查") } - _, err = dao.SysDictData.Ctx(ctx).Fields(sysin.DictDataInsertFields{}).Data(in).Insert() + _, err = dao.SysDictData.Ctx(ctx).Fields(sysin.DictDataInsertFields{}).Data(in).OmitEmptyData().Insert() if err != nil { err = gerror.Wrap(err, consts.ErrorORM) return err diff --git a/server/internal/logic/sys/dict_type.go b/server/internal/logic/sys/dict_type.go index fe84766..c45154d 100644 --- a/server/internal/logic/sys/dict_type.go +++ b/server/internal/logic/sys/dict_type.go @@ -96,7 +96,7 @@ func (s *sSysDictType) Edit(ctx context.Context, in *sysin.DictTypeEditInp) (err } // 新增 - if _, err = dao.SysDictType.Ctx(ctx).Fields(sysin.DictTypeInsertFields{}).Data(in).Insert(); err != nil { + if _, err = dao.SysDictType.Ctx(ctx).Fields(sysin.DictTypeInsertFields{}).Data(in).OmitEmptyData().Insert(); err != nil { err = gerror.Wrap(err, consts.ErrorORM) } return diff --git a/server/internal/logic/sys/ems_log.go b/server/internal/logic/sys/ems_log.go index e4e8fd8..d75128d 100644 --- a/server/internal/logic/sys/ems_log.go +++ b/server/internal/logic/sys/ems_log.go @@ -61,7 +61,7 @@ func (s *sSysEmsLog) Edit(ctx context.Context, in *sysin.EmsLogEditInp) (err err } // 新增 - _, err = dao.SysEmsLog.Ctx(ctx).Data(in).Insert() + _, err = dao.SysEmsLog.Ctx(ctx).Data(in).OmitEmptyData().Insert() return } @@ -190,7 +190,7 @@ func (s *sSysEmsLog) Send(ctx context.Context, in *sysin.SendEmsInp) (err error) data.CreatedAt = gtime.Now() data.UpdatedAt = gtime.Now() - _, err = dao.SysEmsLog.Ctx(ctx).Data(data).Insert() + _, err = dao.SysEmsLog.Ctx(ctx).Data(data).OmitEmptyData().Insert() return } diff --git a/server/internal/logic/sys/gen_codes.go b/server/internal/logic/sys/gen_codes.go index 8dfdc93..bd76a08 100644 --- a/server/internal/logic/sys/gen_codes.go +++ b/server/internal/logic/sys/gen_codes.go @@ -88,7 +88,7 @@ func (s *sSysGenCodes) Edit(ctx context.Context, in *sysin.GenCodesEditInp) (res in.MasterColumns = gjson.New("[]") in.Status = consts.GenCodesStatusWait in.CreatedAt = gtime.Now() - id, err := dao.SysGenCodes.Ctx(ctx).Data(in).InsertAndGetId() + id, err := dao.SysGenCodes.Ctx(ctx).Data(in).OmitEmptyData().InsertAndGetId() if err != nil { err = gerror.Wrap(err, consts.ErrorORM) return diff --git a/server/internal/logic/sys/log.go b/server/internal/logic/sys/log.go index 61b99c2..302368b 100644 --- a/server/internal/logic/sys/log.go +++ b/server/internal/logic/sys/log.go @@ -7,6 +7,8 @@ package sys import ( "context" + "fmt" + "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" @@ -17,7 +19,9 @@ import ( "github.com/gogf/gf/v2/util/gconv" "hotgo/internal/consts" "hotgo/internal/dao" + "hotgo/internal/global" "hotgo/internal/library/contexts" + "hotgo/internal/library/dict" "hotgo/internal/library/hgorm/handler" "hotgo/internal/library/hgorm/hook" "hotgo/internal/library/location" @@ -40,6 +44,11 @@ func init() { service.RegisterSysLog(NewSysLog()) } +// Model 请求日志Orm模型 +func (s *sSysLog) Model(ctx context.Context, option ...*handler.Option) *gdb.Model { + return handler.Model(dao.SysLog.Ctx(ctx), option...) +} + // Export 导出 func (s *sSysLog) Export(ctx context.Context, in *sysin.LogListInp) (err error) { // 导出格式 @@ -96,7 +105,7 @@ func (s *sSysLog) Export(ctx context.Context, in *sysin.LogListInp) (err error) // RealWrite 真实写入 func (s *sSysLog) RealWrite(ctx context.Context, log entity.SysLog) (err error) { - _, err = dao.SysLog.Ctx(ctx).FieldsEx(dao.SysLog.Columns().Id).Data(log).Insert() + _, err = dao.SysLog.Ctx(ctx).FieldsEx(dao.SysLog.Columns().Id).Data(log).Unscoped().OmitEmptyData().Insert() return } @@ -171,6 +180,10 @@ func (s *sSysLog) AnalysisLog(ctx context.Context) entity.SysLog { } } + if timestamp == 0 { + timestamp = gtime.Timestamp() + } + // 请求头 if reqHeadersBytes, _ := gjson.New(request.Header).MarshalJSON(); len(reqHeadersBytes) > 0 { headerData = gjson.New(reqHeadersBytes) @@ -213,6 +226,8 @@ func (s *sSysLog) AnalysisLog(ctx context.Context) entity.SysLog { takeUpTime = tt } + headerData.MustSet("qqq", request.EnterTime.String()) + data = entity.SysLog{ AppId: appId, MerchantId: 0, @@ -234,13 +249,28 @@ func (s *sSysLog) AnalysisLog(ctx context.Context) entity.SysLog { UserAgent: request.Header.Get("User-Agent"), Status: consts.StatusEnabled, TakeUpTime: takeUpTime, + UpdatedAt: gtime.Now(), + CreatedAt: request.EnterTime, } return data } -// View 获取指定字典类型信息 +// View 获取指定请求日志信息 func (s *sSysLog) View(ctx context.Context, in *sysin.LogViewInp) (res *sysin.LogViewModel, err error) { - if err = dao.SysLog.Ctx(ctx).Handler(handler.FilterAuth).Hook(hook.CityLabel).Where("id", in.Id).Scan(&res); err != nil { + mod := s.Model(ctx) + + count, err := service.SysLoginLog().Model(ctx). + LeftJoinOnFields(dao.SysLog.Table(), dao.SysLoginLog.Columns().ReqId, "=", dao.SysLog.Columns().ReqId). + WherePrefix(dao.SysLog.Table(), dao.SysLog.Columns().Id, in.Id).Count() + if err != nil { + return nil, err + } + + if count > 0 { + mod = dao.SysLog.Ctx(ctx) + } + + if err = mod.Hook(hook.CityLabel).WherePri(in.Id).Scan(&res); err != nil { err = gerror.Wrap(err, consts.ErrorORM) return } @@ -249,6 +279,15 @@ func (s *sSysLog) View(ctx context.Context, in *sysin.LogViewInp) (res *sysin.Lo return } + routes := global.LoadHTTPRoutes(ghttp.RequestFromCtx(ctx)) + key := global.GenRouteKey(res.Method, res.Url) + route, ok := routes[key] + if ok { + res.Tags = route.Tags + res.Summary = route.Summary + res.Description = route.Description + } + if simple.IsDemo(ctx) { res.HeaderData = gjson.New(`{ "none": [ @@ -259,15 +298,15 @@ func (s *sSysLog) View(ctx context.Context, in *sysin.LogViewInp) (res *sysin.Lo return } -// Delete 删除 +// Delete 删除请求日志 func (s *sSysLog) Delete(ctx context.Context, in *sysin.LogDeleteInp) (err error) { - _, err = dao.SysLog.Ctx(ctx).Handler(handler.FilterAuth).Where("id", in.Id).Delete() + _, err = s.Model(ctx).WherePri(in.Id).Delete() return } -// List 列表 +// List 请求日志列表 func (s *sSysLog) List(ctx context.Context, in *sysin.LogListInp) (list []*sysin.LogListModel, totalCount int, err error) { - mod := dao.SysLog.Ctx(ctx).Handler(handler.FilterAuth).FieldsEx("get_data", "header_data", "post_data") + mod := s.Model(ctx).FieldsEx("get_data", "header_data", "post_data") // 访问路径 if in.Url != "" { @@ -279,6 +318,11 @@ func (s *sSysLog) List(ctx context.Context, in *sysin.LogListInp) (list []*sysin mod = mod.Where("module", in.Module) } + // 链路ID + if in.ReqId != "" { + mod = mod.Where("req_id", in.ReqId) + } + // 请求方式 if in.Method != "" { mod = mod.Where("method", in.Method) @@ -305,8 +349,8 @@ func (s *sSysLog) List(ctx context.Context, in *sysin.LogListInp) (list []*sysin } // 请求耗时 - if in.TakeUpTime > 0 { - mod = mod.WhereGTE("take_up_time", in.TakeUpTime) + if dict.HasOptionKey(consts.HTTPHandlerTimeOptions, in.TakeUpTime) { + mod = mod.Where(fmt.Sprintf("`take_up_time` %v", in.TakeUpTime)) } totalCount, err = mod.Count() @@ -314,35 +358,40 @@ func (s *sSysLog) List(ctx context.Context, in *sysin.LogListInp) (list []*sysin return } - if err = mod.Page(in.Page, in.PerPage).Order("id desc").Scan(&list); err != nil { + if err = mod.Page(in.Page, in.PerPage).Hook(hook.CityLabel).Order("id desc").Scan(&list); err != nil { return } - for i := 0; i < len(list); i++ { - // 管理员 - if list[i].AppId == consts.AppAdmin { - memberName, err := dao.AdminMember.Ctx(ctx).Fields("realname").Where("id", list[i].MemberId).Value() + routes := global.LoadHTTPRoutes(ghttp.RequestFromCtx(ctx)) + for _, v := range list { + if v.AppId == consts.AppAdmin { + memberName, err := dao.AdminMember.Ctx(ctx).Fields("realname").WherePri(v.MemberId).Value() if err != nil { err = gerror.Wrap(err, consts.ErrorORM) return list, totalCount, err } - list[i].MemberName = memberName.String() + v.MemberName = memberName.String() } - // 接口 - // ... - - if list[i].MemberName == "" { - list[i].MemberName = "游客" + if v.MemberName == "" { + v.MemberName = "游客" } // 截取请求url路径 - if gstr.Contains(list[i].Url, "?") { - list[i].Url = gstr.StrTillEx(list[i].Url, "?") + if gstr.Contains(v.Url, "?") { + v.Url = gstr.StrTillEx(v.Url, "?") + } + + key := global.GenRouteKey(v.Method, v.Url) + route, ok := routes[key] + if ok { + v.Tags = route.Tags + v.Summary = route.Summary + v.Description = route.Description } if simple.IsDemo(ctx) { - list[i].HeaderData = gjson.New(`{ + v.HeaderData = gjson.New(`{ "none": [ "` + consts.DemoTips + `" ] diff --git a/server/internal/logic/sys/login_log.go b/server/internal/logic/sys/login_log.go index 6a072a8..c83c4c3 100644 --- a/server/internal/logic/sys/login_log.go +++ b/server/internal/logic/sys/login_log.go @@ -43,13 +43,13 @@ func init() { } // Model 登录日志Orm模型 -func (s *sSysLoginLog) Model(ctx context.Context) *gdb.Model { - return dao.SysLoginLog.Ctx(ctx) +func (s *sSysLoginLog) Model(ctx context.Context, option ...*handler.Option) *gdb.Model { + return handler.Model(dao.SysLoginLog.Ctx(ctx), option...) } // List 获取登录日志列表 func (s *sSysLoginLog) List(ctx context.Context, in *sysin.LoginLogListInp) (list []*sysin.LoginLogListModel, totalCount int, err error) { - mod := dao.SysLoginLog.Ctx(ctx) + mod := s.Model(ctx) // 查询状态 if in.Status > 0 { @@ -76,7 +76,7 @@ func (s *sSysLoginLog) List(ctx context.Context, in *sysin.LoginLogListInp) (lis return } - if err = mod.Fields(sysin.LoginLogListModel{}).Hook(hook.CityLabel).Handler(handler.FilterAuth).Page(in.Page, in.PerPage).OrderDesc(dao.SysLoginLog.Columns().Id).Scan(&list); err != nil { + if err = mod.Fields(sysin.LoginLogListModel{}).Hook(hook.CityLabel).Page(in.Page, in.PerPage).OrderDesc(dao.SysLoginLog.Columns().Id).Scan(&list); err != nil { err = gerror.Wrap(err, consts.ErrorORM) return list, totalCount, err } @@ -121,13 +121,7 @@ func (s *sSysLoginLog) Export(ctx context.Context, in *sysin.LoginLogListInp) (e // Delete 删除登录日志 func (s *sSysLoginLog) Delete(ctx context.Context, in *sysin.LoginLogDeleteInp) (err error) { - _, err = dao.SysLoginLog.Ctx(ctx).Where(dao.SysLoginLog.Columns().Id, in.Id).Delete() - return -} - -// View 获取登录日志指定信息 -func (s *sSysLoginLog) View(ctx context.Context, in *sysin.LoginLogViewInp) (res *sysin.LoginLogViewModel, err error) { - err = dao.SysLoginLog.Ctx(ctx).Where(dao.SysLoginLog.Columns().Id, in.Id).Scan(&res) + _, err = s.Model(ctx).WherePri(in.Id).Delete() return } @@ -181,6 +175,6 @@ func (s *sSysLoginLog) Push(ctx context.Context, in *sysin.LoginLogPushInp) { // RealWrite 真实写入 func (s *sSysLoginLog) RealWrite(ctx context.Context, models entity.SysLoginLog) (err error) { - _, err = dao.SysLoginLog.Ctx(ctx).FieldsEx(dao.SysLog.Columns().Id).Data(models).Insert() + _, err = dao.SysLoginLog.Ctx(ctx).Data(models).OmitEmptyData().Insert() return } diff --git a/server/internal/logic/sys/normal_tree_demo.go b/server/internal/logic/sys/normal_tree_demo.go index fde7857..6fd66a6 100644 --- a/server/internal/logic/sys/normal_tree_demo.go +++ b/server/internal/logic/sys/normal_tree_demo.go @@ -113,7 +113,7 @@ func (s *sSysNormalTreeDemo) Edit(ctx context.Context, in *sysin.NormalTreeDemoE in.CreatedBy = contexts.GetUserId(ctx) if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}). Fields(sysin.NormalTreeDemoInsertFields{}). - Data(in).Insert(); err != nil { + Data(in).OmitEmptyData().Insert(); err != nil { err = gerror.Wrap(err, "新增普通树表失败,请稍后重试!") } return @@ -173,4 +173,4 @@ func (s *sSysNormalTreeDemo) TreeOption(ctx context.Context) (nodes []tree.Node, nodes[i] = v } return tree.ListToTree(0, nodes) -} \ No newline at end of file +} diff --git a/server/internal/logic/sys/option_tree_demo.go b/server/internal/logic/sys/option_tree_demo.go index 8761208..3f4ec51 100644 --- a/server/internal/logic/sys/option_tree_demo.go +++ b/server/internal/logic/sys/option_tree_demo.go @@ -113,7 +113,7 @@ func (s *sSysOptionTreeDemo) Edit(ctx context.Context, in *sysin.OptionTreeDemoE in.CreatedBy = contexts.GetUserId(ctx) if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}). Fields(sysin.OptionTreeDemoInsertFields{}). - Data(in).Insert(); err != nil { + Data(in).OmitEmptyData().Insert(); err != nil { err = gerror.Wrap(err, "新增选项树表失败,请稍后重试!") } return @@ -173,4 +173,4 @@ func (s *sSysOptionTreeDemo) TreeOption(ctx context.Context) (nodes []tree.Node, nodes[i] = v } return tree.ListToTree(0, nodes) -} \ No newline at end of file +} diff --git a/server/internal/logic/sys/provinces.go b/server/internal/logic/sys/provinces.go index 54a7f3d..ab01d5d 100644 --- a/server/internal/logic/sys/provinces.go +++ b/server/internal/logic/sys/provinces.go @@ -92,7 +92,7 @@ func (s *sSysProvinces) Edit(ctx context.Context, in *sysin.ProvincesEditInp) (e } // 新增 - if _, err = dao.SysProvinces.Ctx(ctx).Fields(sysin.ProvincesInsertFields{}).Data(in).Insert(); err != nil { + if _, err = dao.SysProvinces.Ctx(ctx).Fields(sysin.ProvincesInsertFields{}).Data(in).OmitEmptyData().Insert(); err != nil { err = gerror.Wrap(err, "新增省市区数据失败!") } return diff --git a/server/internal/logic/sys/serve_license.go b/server/internal/logic/sys/serve_license.go index 5be43f3..29245c7 100644 --- a/server/internal/logic/sys/serve_license.go +++ b/server/internal/logic/sys/serve_license.go @@ -142,7 +142,7 @@ func (s *sSysServeLicense) Edit(ctx context.Context, in *sysin.ServeLicenseEditI } // 新增 - if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Fields(sysin.ServeLicenseInsertFields{}).Data(in).Insert(); err != nil { + if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}).Fields(sysin.ServeLicenseInsertFields{}).Data(in).OmitEmptyData().Insert(); err != nil { err = gerror.Wrap(err, "新增服务许可证失败,请稍后重试!") } return diff --git a/server/internal/logic/sys/serve_log.go b/server/internal/logic/sys/serve_log.go index 9c4e0b2..e19c6cd 100644 --- a/server/internal/logic/sys/serve_log.go +++ b/server/internal/logic/sys/serve_log.go @@ -126,6 +126,6 @@ func (s *sSysServeLog) View(ctx context.Context, in *sysin.ServeLogViewInp) (res // RealWrite 真实写入 func (s *sSysServeLog) RealWrite(ctx context.Context, models entity.SysServeLog) (err error) { - _, err = dao.SysServeLog.Ctx(ctx).FieldsEx(dao.SysLog.Columns().Id).Data(models).Insert() + _, err = dao.SysServeLog.Ctx(ctx).FieldsEx(dao.SysLog.Columns().Id).Data(models).OmitEmptyData().Insert() return } diff --git a/server/internal/logic/sys/sms_log.go b/server/internal/logic/sys/sms_log.go index 5452656..02e153a 100644 --- a/server/internal/logic/sys/sms_log.go +++ b/server/internal/logic/sys/sms_log.go @@ -138,7 +138,7 @@ func (s *sSysSmsLog) SendCode(ctx context.Context, in *sysin.SendCodeInp) (err e data.CreatedAt = gtime.Now() data.UpdatedAt = gtime.Now() - _, err = dao.SysSmsLog.Ctx(ctx).Data(data).Insert() + _, err = dao.SysSmsLog.Ctx(ctx).Data(data).OmitEmptyData().Insert() return } diff --git a/server/internal/logic/sys/test_category.go b/server/internal/logic/sys/test_category.go index 0642814..aedf8a4 100644 --- a/server/internal/logic/sys/test_category.go +++ b/server/internal/logic/sys/test_category.go @@ -98,7 +98,7 @@ func (s *sSysTestCategory) Edit(ctx context.Context, in *sysin.TestCategoryEditI // 新增 if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}). Fields(sysin.TestCategoryInsertFields{}). - Data(in).Insert(); err != nil { + Data(in).OmitEmptyData().Insert(); err != nil { err = gerror.Wrap(err, "新增测试分类失败,请稍后重试!") } return @@ -164,4 +164,4 @@ func (s *sSysTestCategory) Option(ctx context.Context) (opts []*model.Option, er opts[k] = dict.GenHashOption(v.Id, gconv.String(v.Name)) } return -} \ No newline at end of file +} diff --git a/server/internal/model/input/adminin/member.go b/server/internal/model/input/adminin/member.go index 8e57362..72a512d 100644 --- a/server/internal/model/input/adminin/member.go +++ b/server/internal/model/input/adminin/member.go @@ -147,7 +147,7 @@ type MemberAddInp struct { } func (in *MemberEditInp) Filter(ctx context.Context) (err error) { - if in.Password != "" { + if in.Id < 1 || in.Password != "" { if err := g.Validator(). Rules("length:6,16"). Messages("新密码不能为空#新密码需在6~16之间"). diff --git a/server/internal/model/input/sysin/log.go b/server/internal/model/input/sysin/log.go index 35dc6b0..ce2fa80 100644 --- a/server/internal/model/input/sysin/log.go +++ b/server/internal/model/input/sysin/log.go @@ -14,20 +14,25 @@ import ( type LogListInp struct { form.PageReq form.StatusReq + ReqId string `json:"reqId" dc:"对外ID"` Module string `json:"module" dc:"应用端口"` - MemberId int `json:"member_id" dc:"用户ID"` - TakeUpTime int `json:"take_up_time" dc:"请求耗时"` + MemberId int `json:"memberId" dc:"用户ID"` + TakeUpTime string `json:"takeUpTime" dc:"请求耗时"` Method string `json:"method" dc:"请求方式"` Url string `json:"url" dc:"请求路径"` Ip string `json:"ip" dc:"访问IP"` - ErrorCode string `json:"error_code" dc:"状态码"` - CreatedAt []int64 `json:"created_at" dc:"创建时间"` + ErrorCode string `json:"errorCode" dc:"状态码"` + CreatedAt []int64 `json:"createdAt" dc:"创建时间"` } type LogListModel struct { entity.SysLog - MemberName string `json:"memberName"` - Region string `json:"region"` + MemberName string `json:"memberName"` + Region string `json:"region"` + CityLabel string `json:"cityLabel" dc:"城市标签"` + Tags string `json:"tags" dc:"接口所属的标签,用于接口分类"` + Summary string `json:"summary" dc:"接口/参数概要描述"` + Description string `json:"description" dc:"接口/参数详细描述"` } // LogViewInp 获取信息 @@ -37,7 +42,10 @@ type LogViewInp struct { type LogViewModel struct { entity.SysLog - CityLabel string `json:"cityLabel" description:"城市标签"` + CityLabel string `json:"cityLabel" dc:"城市标签"` + Tags string `json:"tags" dc:"接口所属的标签,用于接口分类"` + Summary string `json:"summary" dc:"接口/参数概要描述"` + Description string `json:"description" dc:"接口/参数详细描述"` } // LogDeleteInp 删除 diff --git a/server/internal/service/middleware.go b/server/internal/service/middleware.go index 49e6fb9..526504a 100644 --- a/server/internal/service/middleware.go +++ b/server/internal/service/middleware.go @@ -37,14 +37,6 @@ type ( Blacklist(r *ghttp.Request) // Develop 开发工具白名单过滤 Develop(r *ghttp.Request) - // GetFilterRoutes 获取支持预处理的web路由 - GetFilterRoutes(r *ghttp.Request) map[string]ghttp.RouterItem - // GenFilterRequestKey 根据请求生成唯一key - GenFilterRequestKey(r *ghttp.Request) string - // GenFilterRouteKey 根据路由生成唯一key - GenFilterRouteKey(r *ghttp.Router) string - // GenRouteKey 生成唯一key - GenRouteKey(method, path string) string // PreFilter 请求输入预处理 // api使用gf规范路由并且XxxReq结构体实现了validate.Filter接口即可 PreFilter(r *ghttp.Request) diff --git a/server/internal/service/sys.go b/server/internal/service/sys.go index c5f14e2..53ce9b8 100644 --- a/server/internal/service/sys.go +++ b/server/internal/service/sys.go @@ -243,6 +243,8 @@ type ( Build(ctx context.Context, in *sysin.GenCodesBuildInp) (err error) } ISysLog interface { + // Model 请求日志Orm模型 + Model(ctx context.Context, option ...*handler.Option) *gdb.Model // Export 导出 Export(ctx context.Context, in *sysin.LogListInp) (err error) // RealWrite 真实写入 @@ -251,24 +253,22 @@ type ( AutoLog(ctx context.Context) error // AnalysisLog 解析日志数据 AnalysisLog(ctx context.Context) entity.SysLog - // View 获取指定字典类型信息 + // View 获取指定请求日志信息 View(ctx context.Context, in *sysin.LogViewInp) (res *sysin.LogViewModel, err error) - // Delete 删除 + // Delete 删除请求日志 Delete(ctx context.Context, in *sysin.LogDeleteInp) (err error) - // List 列表 + // List 请求日志列表 List(ctx context.Context, in *sysin.LogListInp) (list []*sysin.LogListModel, totalCount int, err error) } ISysLoginLog interface { // Model 登录日志Orm模型 - Model(ctx context.Context) *gdb.Model + Model(ctx context.Context, option ...*handler.Option) *gdb.Model // List 获取登录日志列表 List(ctx context.Context, in *sysin.LoginLogListInp) (list []*sysin.LoginLogListModel, totalCount int, err error) // Export 导出登录日志 Export(ctx context.Context, in *sysin.LoginLogListInp) (err error) // Delete 删除登录日志 Delete(ctx context.Context, in *sysin.LoginLogDeleteInp) (err error) - // View 获取登录日志指定信息 - View(ctx context.Context, in *sysin.LoginLogViewInp) (res *sysin.LoginLogViewModel, err error) // Push 推送登录日志 Push(ctx context.Context, in *sysin.LoginLogPushInp) // RealWrite 真实写入 diff --git a/web/package.json b/web/package.json index 5c112a4..b5cebe6 100644 --- a/web/package.json +++ b/web/package.json @@ -97,7 +97,7 @@ "stylelint-config-standard": "^29.0.0", "stylelint-order": "^5.0.0", "stylelint-scss": "^4.7.0", - "tailwindcss": "^3.4.3", + "tailwindcss": "^2.2.19", "typescript": "^5.3.0", "unplugin-vue-components": "^0.22.12", "vite": "^4.5.3", diff --git a/web/src/api/loginLog/index.ts b/web/src/api/loginLog/index.ts index 8209308..3f50101 100644 --- a/web/src/api/loginLog/index.ts +++ b/web/src/api/loginLog/index.ts @@ -18,15 +18,6 @@ export function Delete(params) { }); } -// 获取登录日志指定详情 -export function View(params) { - return http.request({ - url: '/loginLog/view', - method: 'GET', - params, - }); -} - // 导出登录日志 export function Export(params) { jumpExport('/loginLog/export', params); diff --git a/web/src/utils/websocket/index.ts b/web/src/utils/websocket/index.ts index 49ef48e..4ecd535 100644 --- a/web/src/utils/websocket/index.ts +++ b/web/src/utils/websocket/index.ts @@ -49,21 +49,29 @@ export default () => { let timer: ReturnType; const createSocket = () => { console.log('[WebSocket] createSocket...'); - if (useUserStore.token === '') { + if (useUserStore.token === '' || useUserStore.config?.wsAddr == '') { console.error('[WebSocket] 用户未登录,稍后重试...'); - reconnect(); + resetReconnect(); return; } try { socket = new WebSocket(`${useUserStore.config?.wsAddr}?authorization=${useUserStore.token}`); init(); + if (lockReconnect) { + lockReconnect = false; + } } catch (e) { console.error(`[WebSocket] createSocket err: ${e}`); - reconnect(); + resetReconnect(); + return; } + }; + + const resetReconnect = () => { if (lockReconnect) { lockReconnect = false; } + reconnect(); }; const reconnect = () => { @@ -73,7 +81,7 @@ export default () => { clearTimeout(timer); timer = setTimeout(() => { createSocket(); - }, SocketEnum.HeartBeatInterval); + }, 2000); }; const init = () => { diff --git a/web/src/views/log/ems-log/index.vue b/web/src/views/log/ems-log/index.vue index a105e8f..b6e846a 100644 --- a/web/src/views/log/ems-log/index.vue +++ b/web/src/views/log/ems-log/index.vue @@ -64,9 +64,9 @@ { field: 'url', component: 'NInput', - label: '访问路径', + label: '接口路径', componentProps: { - placeholder: '请输入访问路径', + placeholder: '请输入接口路径', onInput: (e: any) => { console.log(e); }, diff --git a/web/src/views/log/log/columns.ts b/web/src/views/log/log/columns.ts index 2e246cc..a21e230 100644 --- a/web/src/views/log/log/columns.ts +++ b/web/src/views/log/log/columns.ts @@ -1,84 +1,147 @@ import { h } from 'vue'; -import { NTag } from 'naive-ui'; +import { NTag, NEllipsis, NSpace } from 'naive-ui'; +import { timestampToTime } from '@/utils/dateUtil'; +import { renderHtmlTooltip } from '@/utils'; export const columns = [ { - title: '模块', - key: 'module', - render(row) { - return h( - NTag, - { - style: { - marginRight: '6px', - }, - type: row.module == 'admin' ? 'info' : 'success', - bordered: false, - }, - { - default: () => row.module, - } - ); - }, + title: '记录ID', + key: 'id', width: 100, }, { - title: '操作人', - key: 'memberName', - render(row) { - if (row.memberId === 0) { - return row.memberName; - } - return row.memberName + '(' + row.memberId + ')'; - }, - width: 150, - }, - { - title: '请求方式', - key: 'method', - width: 80, - }, - { - title: '请求路径', - key: 'url', - width: 200, - }, - { - title: '访问IP', - key: 'ip', - width: 150, - }, - // { - // title: 'IP地区', - // key: 'region', - // }, - { - title: '状态码', - key: 'errorCode', + title: '访客', + key: 'name', + width: 180, render(row) { + const operator = + row.memberId === 0 ? row.memberName : row.memberName + '(' + row.memberId + ')'; + return h( - NTag, + NEllipsis, { style: { - marginRight: '6px', + maxWidth: '180px', }, - type: row.errorCode == 0 ? 'success' : 'warning', - bordered: false, }, { - default: () => row.errorMsg + '(' + row.errorCode + ')', + default: () => + h( + NSpace, + { vertical: true }, + { + default: () => [ + h('div', { + innerHTML: '

' + operator + '

', + }), + h('div', { + innerHTML: '

IP:' + row.ip + '

', + }), + row.cityLabel != '' + ? h( + NTag, + { + style: { + marginRight: '6px', + }, + type: 'primary', + bordered: false, + }, + { + default: () => row.cityLabel, + } + ) + : null, + ], + } + ), } ); }, - width: 150, }, { - title: '处理耗时', - key: 'takeUpTime', + title: '请求接口', + key: 'name', + width: 260, render(row) { - return row.takeUpTime + ' ms'; + return h( + NEllipsis, + { + style: { + maxWidth: '260px', + }, + }, + { + default: () => + h( + NSpace, + { vertical: true }, + { + default: () => [ + h( + NTag, + { + style: { + marginRight: '6px', + }, + bordered: false, + }, + { + default: () => row.method, + } + ), + h('div', { + innerHTML: '

接口:' + row.url + '

', + }), + h('div', { + innerHTML: '

名称:' + row.tags + ' / ' + row.summary + '

', + }), + ], + } + ), + } + ); + }, + }, + { + title: '接口响应', + key: 'name', + width: 260, + render(row) { + return h( + NEllipsis, + { + style: { + maxWidth: '260px', + }, + }, + { + default: () => + h( + NSpace, + { vertical: true }, + { + default: () => [ + renderHtmlTooltip( + '

状态码:' + + row.errorMsg + + '(' + + row.errorCode + + ')' + + '

' + ), + h('div', { + innerHTML: '

处理耗时:' + row.takeUpTime + 'ms

', + }), + h('div', { + innerHTML: '

响应时间:' + timestampToTime(row.timestamp) + '

', + }), + ], + } + ), + } + ); }, - width: 120, }, { title: '访问时间', diff --git a/web/src/views/log/log/index.vue b/web/src/views/log/log/index.vue index 515e1c7..5172646 100644 --- a/web/src/views/log/log/index.vue +++ b/web/src/views/log/log/index.vue @@ -39,150 +39,26 @@ diff --git a/web/src/views/log/log/model.ts b/web/src/views/log/log/model.ts new file mode 100644 index 0000000..e0d77f1 --- /dev/null +++ b/web/src/views/log/log/model.ts @@ -0,0 +1,134 @@ +import { FormSchema } from '@/components/Form'; +import { ref } from 'vue'; +import { defRangeShortcuts } from '@/utils/dateUtil'; +import { Option } from '@/utils/hotgo'; +import { Dicts } from '@/api/dict/dict'; + +export const schemas = ref([ + { + field: 'reqId', + component: 'NInput', + label: '链路ID', + componentProps: { + placeholder: '请输入链路ID', + onInput: (e: any) => { + console.log(e); + }, + }, + }, + { + field: 'memberId', + component: 'NInput', + label: '操作人', + componentProps: { + placeholder: '请输入操作人ID', + onInput: (e: any) => { + console.log(e); + }, + }, + rules: [{ trigger: ['blur'] }], + }, + { + field: 'url', + component: 'NInput', + label: '接口路径', + componentProps: { + placeholder: '请输入接口路径', + onInput: (e: any) => { + console.log(e); + }, + }, + }, + { + field: 'ip', + component: 'NInput', + label: '访问IP', + componentProps: { + placeholder: '请输入IP地址', + onInput: (e: any) => { + console.log(e); + }, + }, + }, + { + field: 'method', + component: 'NSelect', + label: '请求方式', + componentProps: { + placeholder: '请选择请求方式', + options: [], + onUpdateValue: (e: any) => { + console.log(e); + }, + }, + }, + { + field: 'createdAt', + component: 'NDatePicker', + label: '访问时间', + componentProps: { + type: 'datetimerange', + clearable: true, + shortcuts: defRangeShortcuts(), + onUpdateValue: (e: any) => { + console.log(e); + }, + }, + }, + { + field: 'takeUpTime', + component: 'NSelect', + label: '请求耗时', + componentProps: { + placeholder: '请选择请求耗时', + options: [], + onUpdateValue: (e: any) => { + console.log(e); + }, + }, + }, + { + field: 'errorCode', + component: 'NSelect', + label: '状态码', + labelMessage: '支持填入自定义状态码', + componentProps: { + placeholder: '请选择状态码', + options: [], + filterable: true, + tag: true, + onUpdateValue: (e: any) => { + console.log(e); + }, + }, + }, +]); + +// 字典数据选项 +export const options = ref({ + HTTPMethod: [] as Option[], + HTTPHandlerTime: [] as Option[], + HTTPApiCode: [] as Option[], +}); + +// 加载字典数据选项 +export function loadOptions() { + Dicts({ + types: ['HTTPMethod', 'HTTPHandlerTime', 'HTTPApiCode'], + }).then((res) => { + options.value = res; + for (const item of schemas.value) { + switch (item.field) { + case 'method': + item.componentProps.options = options.value.HTTPMethod; + break; + case 'takeUpTime': + item.componentProps.options = options.value.HTTPHandlerTime; + break; + case 'errorCode': + item.componentProps.options = options.value.HTTPApiCode; + break; + } + } + }); +} diff --git a/web/src/views/log/log/view.vue b/web/src/views/log/log/view.vue index 0d67075..8e4c441 100644 --- a/web/src/views/log/log/view.vue +++ b/web/src/views/log/log/view.vue @@ -14,15 +14,18 @@ {{ data.url }} - {{ data.takeUpTime }} ms + {{ data.tags }} / {{ data.summary }} {{ data.ip }} {{ data.cityLabel }} {{ data.reqId }} + {{ data.takeUpTime }} ms {{ - timestampToTime(data.timestamp) + data.timestamp > 0 ? timestampToTime(data.timestamp) : '--' }} - {{ data.createdAt }} + {{ data.createdAt }} { - if (logId === undefined || logId < 1) { - message.error('ID不正确,请检查!'); - return; - } - getInfo(); - }); - const data = ref({}); const getInfo = () => { loading.value = true; - View({ id: logId }) + View(params) .then((res) => { data.value = res; }) @@ -154,6 +148,14 @@ loading.value = false; }); }; + + onMounted(() => { + if (!params.id) { + message.error('日志ID不正确,请检查!'); + return; + } + getInfo(); + });