从 PHP 到 AI + Golang,程序员自救转型手记(四十七):列表通用排序接口实现(增量重排法)

发布时间:2026/8/2 5:15:05

从 PHP 到 AI + Golang,程序员自救转型手记(四十七):列表通用排序接口实现(增量重排法) 这是一个系列 Blog作者将以一个 PHP 全栈工程师的身份利用 AI 工具claude code、codex、deepseek、豆包等从零开始学习 golang 语言并最终完成 ai-go-admingithub | gitee开源项目的制作全程记录分享。在上一期我们进行了 “再次优化基类”本期将完成列表通用排序接口实现增量重排法列表通用排序接口实现增量重排法实现初版的提示词继续啃BuildAdmin的老本参考../badmin-v2.3.7-full/app/admin/library/traits/Backend.php中的sortable方法在当前项目的基类internal/handler/base.go和internal/repository/base.go和internal/service/base.go中实现sort方法并注册路由前端传递的参数有move: moveRow[table.pk!], // 移动行 target: targetRow[table.pk!], // 目标行 sort: table.filter?.sort, // 排序字段权重字段 order: table.filter?.order, // 排序方式 direction: evt.newIndexevt.oldIndex ?down:up, // 拖拽方向忽略参考代码中的$dataLimitAdminIds忽略参考代码中的 “当前是否以权重字段排序” 检查即只检查当前排序和默认排序字段不检查有序保证字段功能需求分析前端拖动排序如将权重为1的行排序行拖动至权重为5的行目标权重行或目标行所谓增量重排法也可以称做区间位移法实现的是将涉及到的行权重值全部按算法重设以达到视觉上的排序调整比起全量重排增量重排法影响范围更小比起交换重排重排后的顺序更加合理。难点/注意点如下复用List列表方法的排序和过滤规则确保执行重新排序时查得的数据列表和前端的一样。只支持以weigh权重字段排序时进行重排操作字段名是可以自定义的只使用权重字段是因为重排是修改权重字段值实现的假设以创建时间排序重排功能不可能去修改创建时间。用户当前可能以weigh asc和weigh desc两种方式进行排序并且可能将排序行向上拖或者向下拖需要全部考虑到位。目标行的权重值不只一行比如目标行权重为5但数据表中权重值为5的行有很多。不要一个 SQL 修改一行而是尽量一个 SQL 完成一类权重值的全部修改比如权重值 目标权重的行全部1不能一条一条的遍历去改。AI 实现后控制器层的代码很简单主要是新增了Sort方法及其请求体结构体定义并注册了sort路由。主要功能实现是在服务层所以服务层问题也最多weigh 字段名不固定实现排序功能期间需要读取目标行的权重值但字段名是不固定的go 里边没有$row[$weigh]这种写法所以 AI 使用了反射的方式去读取权重值反射首先是实现复杂二是性能一般拖拽排序其实无需考虑性能但我们还是直接改为从前端传递目标行权重值即可服务端直接读取并使用去掉反射相关代码。AI 根据需求实现后接受的前端变量名为target_weigh由于我们服务端不需要使用排序行的权重值只需要目标行的权重值这里将target_weigh改名为更简洁的weigh即可。并且AI 将weigh的类型定义为any这里直接固定为int64。改为 Generics API在这种需要比较复杂的 update 语句时AI 又开始忘记AGENTS.md中的规则了这里强行要求它改为Generics API// 原来的iferr:tx.Model(new(T)).Where(weighField bulkOp ?,weigh).Where(pkField ?,move).UpdateColumn(weighField,gorm.Expr(weighField updateOp ?,weighRowsCount)).Error;err!nil{returnerr}// 改为gtx:gorm.G[T](tx)iferr:gtx.Where(weighField bulkOp ?,weigh).Where(pkField ?,move).UpdateColumn(weighField,gorm.Expr(weighField updateOp ?,weighRowsCount));err!nil{returnerr}// 且 gtx 后续复用另外重排实现内必需使用一下传统 API 的Pluck方法当然也可以使用Generics API的Select方法去选择字段只是这样又需要反射读取结构体中数据库列名对应的字段值了没必要。反转切片代码优化实现重排时有一段反转数组的代码// 向下拖动时反转保证等权重区间内相对顺序不变ifdirectiondown{fori,j:0,len(weighIDs)-1;ij;i,ji1,j-1{weighIDs[i],weighIDs[j]weighIDs[j],weighIDs[i]}}在Go 1.25 提供了slices.Reverse方法可以直接使用简化为// 向下拖动时反转保证等权重区间内相对顺序不变ifdirectiondown{slices.Reverse(weighIDs)}多余的封装AI 帮忙封装了一个exprOffset函数用于组装一些排序用的查询表达式但其对整体无益且不被Sort方法以外的地方复用这里让它直接改为在Sort方法内封装为闭包函数即可不要放在Sort方法外边。BUG我在测试前首先让 AI 直接再写一点调试代码打印出Sort方法内执行的所有SQL然后前端进行操作对比我的需求与执行的SQL发现一处排序错误代码我们已经review过了没有发现明显问题应该是某处细节上的逻辑错误这里直接把日志给 AI并加以描述提示词好像有 BUG排序后有权重值重复的行我有权重值为1,2,3的行入参move1 target3时执行的SQL有以下2026/07/25 00:19:34 C:/Users/yang/Desktop/admin/internal/service/base.go:148[0.709ms][rows:1]SELECTidFROMadmin_rulesWHERE weigh3ORDER BY weigh DESC,id DESC2026/07/25 00:19:34 C:/Users/yang/Desktop/admin/internal/service/base.go:171[0.525ms][rows:0]UPDATEadmin_rulesSETweighweigh -1,updated_at2026-07-25 00:19:34.578WHERE weigh3ANDid12026/07/25 00:19:34 C:/Users/yang/Desktop/admin/internal/service/base.go:201[0.628ms][rows:1]UPDATEadmin_rulesSETweigh3,updated_at2026-07-25 00:19:34.578WHEREid12026/07/25 00:19:34 C:/Users/yang/Desktop/admin/internal/service/base.go:213[0.541ms][rows:1]UPDATEadmin_rulesSETweigh2,updated_at2026-07-25 00:19:34.579WHEREid3最终结果是有两行的权重值均为2这与目标不符应该不出现重复的权重才对对比参考代码../badmin-v2.3.7-full/app/admin/library/traits/Backend.php的sortable方法找出原因找到原因了。对比PHP代码的bulk update逻辑PHP正确// updateMethod dec → WHERE weigh target// updateMethod inc → WHERE weigh target-where(weigh,$updateMethoddec?:,$weigh)Go错误// updateOp - → bulkOp ← 反了// updateOp → bulkOp bulkOp:ifupdateOp{bulkOp}dec权重 −1应该影响权重比目标小的行让它们再减给拖动行腾位置。我把 和 写反了。嚯AI 居然还会犯这种逻辑错误。

相关新闻