从 PHP 到 AI + Golang,程序员自救转型手记(四十六):再次优化基类 这是一个系列 Blog作者将以一个 PHP 全栈工程师的身份利用 AI 工具claude code、codex、deepseek、豆包等从零开始学习 golang 语言并最终完成 ai-go-admingithub | gitee开源项目的制作全程记录分享。在上一期我们进行了 “前端远程下拉输入组件”本期将完成再次优化基类再次优化基类基类严格来讲不是 go 里边的概念在本 blog 中使用它范指 基控制器、基服务、基仓储列表数据有序保证很多项目都没有做这个甚至都不知道这个概念这其实是一个数据库含 PostgreSQL、MySQL使用的小坑在未指定排序子句ORDER BY时数据库并不保证排序即先查到哪行就输出哪行且不保证多次相同查询中的数据输出顺序也就是说只要是列表查询开发者必需传递ORDER BY子句如果不传递你看的有序只是临时的下次未必还是这个顺序这在分页等场景可能是灾难性的。实践中我们以用户传递的排序字段优先如果没有传递默认读取主键字段做有序保证且就算有传递排序字段依旧以主键字段托底即可防止用户排序字段值有一样的此时又会丢失有序保证。基控制器封装 BuildSerOpts 方法用于构建服务层选项数据比如Get方法现在组装服务层选项是这样写的entity,err:h.svc.Get(c,service.Options{Omit:h.cfg.Omit.Get,Select:h.cfg.Select.Get,PrimaryKey:c.Param(pk),})增加BuildSerOpts方法后可以简化为opts:h.BuildSerOpts(c,Get,Request{PrimaryKeyValue:c.Param(pk),})entity,err:h.svc.Get(c,opts)即无需再单独传递Omit和Select选项而且在List方法中收益更大代码可以由原来的7行改为单行opts : h.BuildSerOpts(c, List, req.Request)。基类增加自定义扩展参数解析函数目前我们可以为基类配置OmitFields各动作的 Omit 黑名单字段、SelectFields设置各动作的 Select 白名单字段、Adapter数据适配器。以上都是固定的参数但在实际业务场景中很多时候需要能够向底层传递一些自定义的扩展参数规划如下仓储层与自定义扩展参数无关最多传递到服务层即可服务层额外接受一个Extension的Options类型为any增加参数后完整的Options如下// Options 通用服务操作选项// 每个方法所需要的选项都可以在此找到但并非每个方法都会使用全部的选项typeOptionsstruct{OmitFields[]string// 排除出入库字段会传递给仓储层的 Omit 方法SelectFields[]string// 选择出入库字段会传递给仓储层的 Select 方法Wheres[]Where// 查询条件用于构建 WhereScopes然后传递给仓储层的 Scopes 方法SortFieldstring// 排序字段用于构建 OrderScopeSortOrderstring// 排序方式Pageint// 页码用于构建 PaginateScopeLimitint// 每页条数PrimaryKeyValuestring// 主键值目前可供 Get、Update 方法获取数据行PrimaryKeyValues[]string// 主键切片目前可供 Delete 方法批量删除行Extension any// 任意自定义扩展参数}控制器层不仅仅是接受一个Extension any更合理的方式是接受一个函数ExtensionResolver func(c *gin.Context) any可以将它称之为扩展数据解析器函数返回值将被赋值给服务层的Extension any直接于之前增加的BuildSerOpts构建服务层选项数据方法中调用一下解析器再赋值即可。以上是需求描述也是给 AI 的提示词Blog 会比实际发送的更加详细很多AI 帮忙实现以上需求后我特意实际使用了一下比如我们需要向服务层传递一个自定义的AdminSession参数又不想重写控制器层的方法首先在服务层定义好结构体字段列表和类型完全自定义// AuthAdminRuleExtension 规则列表扩展参数typeAuthAdminRuleExtensionstruct{AdminSession*dto.AdminSession}控制器handler.NewHandler时额外使用WithExtension传递一个扩展数据解析器即可// NewAuthAdminRuleHandler 创建菜单和权限规则管理控制器实例funcNewAuthAdminRuleHandler(svc*svcAuth.AuthAdminRuleService)*AuthAdminRuleHandler{returnAuthAdminRuleHandler{Handler:handler.NewHandler(svc,// 传递 扩展数据解析器handler.WithExtension(func(c*gin.Context)any{returnsvcAuth.AuthAdminRuleExtension{// 传递 AdminSessionAdminSession:middleware.GetAdmin(c),}}),),svc:svc,}}服务层读取和使用扩展数据// 从控制器传来的 管理员信息 扩展数据extension,ok:opts.Extension.(*AuthAdminRuleExtension)if!ok||extension.AdminSessionnil{returnnil,errors.New(参数错误缺少 AdminSession 扩展数据)}// opts.xxxx 参数基本每个方法都有接受// 此时就可以使用 extension.AdminSession 了Create 默认忽略传递的主键字段入库我们已经设计了OmitFields选项用于配置忽略入库的字段但是执行创建动作时主键字段总是应该被忽略我们在很早之前就写好了获取当前模型主键的方法这里直接配合它在未设定 OmitFields 选项时默认忽略掉主键字段。