尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

从 PHP 到 AI + Golang,程序员自救转型手记(五十三):管理员日志管理和日志中间件,日志标题读取的极限优化

从 PHP 到 AI + Golang,程序员自救转型手记(五十三):管理员日志管理和日志中间件,日志标题读取的极限优化 这是一个系列 Blog作者将以一个 PHP 全栈工程师的身份利用 AI 工具claude code、codex、deepseek、豆包等从零开始学习 golang 语言并最终完成 ai-go-admingithub | gitee开源项目的制作全程记录分享。在上一期我们进行了 “管理员权限检查中间件”本期将完成管理员日志管理和日志中间件管理员日志中间件一个全局中间件对所有/admin/开头的请求生效之所以不作为分组中间件注册因为我们路由注册时可能会建立多个类似下方的分组group:r.Group(/admin/auth/admin)group:r.Group(/admin/auth/group)即我们的路由都是子模块自行负责注册的不是统一注册所以使用分组级中间件不方便。后续有改动未来会在制作 “自定义后台入口” 功能时统一注册 admin 路由的 group并将日志中间件直接注册到该 group 上就不再需要全局注册了。日志标题读取从权限规则表读取用户请求/admin/auth/admin/update接口日志中不仅要记录请求用户、请求URL、请求体等更重要的是日志标题数据最好能一眼就看出管理员干了啥。我们刚好有admin_rules表管理员的每个请求都经过了验权能验权自然于该表有权限数据/auth/admin从规则表可查出标题为管理员账号管理的权限规则/auth/admin/update从规则表可查出标题为更新的权限规则最终组装为管理员账号管理 - 更新作为日志标题看起来很完美但是一个后台请求中我们已经读了tokens、验权读了admin_rules现在为了个日志标题又加两个 SQL 查询这非常不合适所有这里让 AI 做了个在单条 SQL 查询中准备好标题的办法核心代码是这样的fmt.Sprintf(SELECT c.title, COALESCE(p.title, ) AS parent_title FROM %s AS c LEFT JOIN %s AS p ON p.id c.pid WHERE c.name ?,tableName,tableName,)控制器层、服务层指定标题这一点非常有必要比如管理员登录接口login这个接口压根不在权限规则表有记录而这个接口的日志又不得不记录那么我们可以在控制器里边c.Set(middleware.CtxAdminLogTitleKey,登录)向请求上下文设定一个自定义的日志标题日志中间件里边只需要先调用c.Next()让控制器的代码先跑即可在写入日志时读取到自定义标题数据// 检查控制器层是否已设置自定义日志标题customTitle,hasCustomTitle:c.Get(CtxAdminLogTitleKey)// 异步写入日志// 先在外面把变量提前捕获好在开 goroutine 慢慢处理和入库避免挂在请求上下文的数据随请求结束丢失等gofunc(){// ......}排除日志也不用每条请求都做记录有以下排除规则请求排除仅记录管理后台请求仅记录POST请求排除chunked请求额外排除list请求因为list请求也可能是POST筛选、排序等数据GET不够且GET直接发送json数据不方便敏感字段过滤或排除我们可以利用 GORM 的模型的创建前钩子在日志写入前对password、salt、token等敏感字段进行过滤。日志是异步写入的在模型钩子中处理可以避免解析请求体等动作拖慢用户侧响应速度。非超管只能看自己的日志在控制器和服务层直接通过拼接Where进行限制避免任何可能的越权。// 非超管则只查询自己的日志if!super{opts.Wheresappend(opts.Wheres,service.WhereGroup{Wheres:[]service.Where{{Field:admin_id,Operator:eq,Value:extension.AdminSession.ID,}},})}管理员日志管理前端请求体展示优化到这里一般的后台就是直接在日志详情直接显示JSON了比如{username: admin, password: ****, ......}不过我还是在这里用了个el-tree并写了工具函数将JSON构建为该组件可以合理使用的结构最终效果如下双击打开详情表格的默认设计是双击打开编辑稍微改一下即可另外直接将双击行的数据作为详情弹窗的数据无需额外加载。自定义一个表格行级操作按钮letoptButtons:OptButton[][{render:tip,name:info,title:详情,text:,type:primary,icon:lucide-info,class:table-row-info,click:(row:TableRow){openInfo(row)},},]利用双击单元格前钩子重写双击操作tableManager.opts.before!.columnDblclick({row}){openInfo(row)returnfalse}接下来在openInfo函数打开详情弹窗主要是tableManager.form.operate info详情弹窗那边再Info v-iftableManager.form.operate info /就行了/** * 点击查看详情按钮响应 */constopenInfo(row:TableRow){if(!row)return// 数据来自表格数据未重新请求 API深克隆不然可能会影响表格letrowClonecloneDeep(row)rowClone.datarowClone.data?[{label:点击展开,children:buildJsonToElTreeData(JSON.parse(rowClone.data))}]:[]tableManager.form.extend![info]rowClone tableManager.form.operateinfo}
返回列表