当前位置: 首页 > news >正文

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

这是一个系列 Blog,作者将以一个 PHP 全栈工程师的身份,利用 AI 工具(claude code、codex、deepseek、豆包等):从零开始学习 golang 语言,并最终完成 ai-go-admin(github | 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.newIndex>evt.oldIndex ?'down':'up', // 拖拽方向
  • 忽略参考代码中的$dataLimitAdminIds
  • 忽略参考代码中的 “当前是否以权重字段排序” 检查(即:只检查当前排序和默认排序字段,不检查有序保证字段)

功能需求分析

前端拖动排序,如:将权重为1的行(排序行),拖动至权重为5的行(目标权重行目标行

所谓增量重排法,也可以称做区间位移法:实现的是将涉及到的行,权重值全部按算法重设,以达到视觉上的排序调整

比起全量重排,增量重排法影响范围更小,比起交换重排,重排后的顺序更加合理。

难点/注意点如下:

  1. 复用List列表方法的排序和过滤规则,确保执行重新排序时,查得的数据列表和前端的一样。
  2. 只支持以weigh(权重)字段排序时,进行重排操作(字段名是可以自定义的;只使用权重字段,是因为重排是修改权重字段值实现的,假设以创建时间排序,重排功能不可能去修改创建时间)。
  3. 用户当前可能以weigh ascweigh desc两种方式进行排序,并且可能将排序行向上拖或者向下拖,需要全部考虑到位。
  4. 目标行的权重值,不只一行:比如目标行权重为5,但数据表中权重值为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 APISelect方法去选择字段,只是这样又需要反射读取结构体中数据库列名对应的字段值了,没必要。

反转切片代码优化

实现重排时,有一段反转数组的代码:

// 向下拖动时反转,保证等权重区间内相对顺序不变ifdirection=="down"{fori,j:=0,len(weighIDs)-1;i<j;i,j=i+1,j-1{weighIDs[i],weighIDs[j]=weighIDs[j],weighIDs[i]}}

Go 1.25 +提供了slices.Reverse方法,可以直接使用,简化为:

// 向下拖动时反转,保证等权重区间内相对顺序不变ifdirection=="down"{slices.Reverse(weighIDs)}

多余的封装

AI 帮忙封装了一个exprOffset函数,用于组装一些排序用的查询表达式,但其对整体无益,且不被Sort方法以外的地方复用,这里让它直接改为:在Sort方法内封装为闭包函数即可,不要放在Sort方法外边。

BUG

我在测试前,首先让 AI 直接再写一点调试代码,打印出Sort方法内执行的所有SQL,然后前端进行操作,对比我的需求与执行的SQL,发现一处排序错误;

代码我们已经review过了,没有发现明显问题,应该是某处细节上的逻辑错误,这里直接把日志给 AI,并加以描述:

提示词:好像有 BUG,排序后有权重值重复的行,我有权重值为1,2,3的行,入参move=1 target=3时,执行的SQL有以下:

2026/07/25 00:19:34 C:/Users/yang/Desktop/admin/internal/service/base.go:148[0.709ms][rows:1]SELECT"id"FROM"admin_rules"WHERE weigh=3ORDER BY weigh DESC,id DESC2026/07/25 00:19:34 C:/Users/yang/Desktop/admin/internal/service/base.go:171[0.525ms][rows:0]UPDATE"admin_rules"SET"weigh"=weigh -1,"updated_at"='2026-07-25 00:19:34.578'WHERE weigh>3ANDid<>'1'2026/07/25 00:19:34 C:/Users/yang/Desktop/admin/internal/service/base.go:201[0.628ms][rows:1]UPDATE"admin_rules"SET"weigh"=3,"updated_at"='2026-07-25 00:19:34.578'WHEREid='1'2026/07/25 00:19:34 C:/Users/yang/Desktop/admin/internal/service/base.go:213[0.541ms][rows:1]UPDATE"admin_rules"SET"weigh"=2,"updated_at"='2026-07-25 00:19:34.579'WHEREid='3'

最终结果是:有两行的权重值均为2,这与目标不符,应该不出现重复的权重才对,对比参考代码:@../badmin-v2.3.7-full/app/admin/library/traits/Backend.phpsortable方法,找出原因:

找到原因了。对比PHP代码的bulk update逻辑:

PHP(正确):

// updateMethod == 'dec' → WHERE weigh < target// updateMethod == 'inc' → WHERE weigh > target->where(weigh,$updateMethod=='dec'?'<':'>',$weigh)

Go(错误):

// updateOp == "-" → bulkOp = ">" ← 反了!// updateOp == "+" → bulkOp = "<"bulkOp:=">"ifupdateOp=="+"{bulkOp="<"}

dec(权重 −1)应该影响权重比目标小的行(<),让它们再减,给拖动行腾位置。我把 > 和 < 写反了。

嚯,AI 居然还会犯这种逻辑错误。

http://www.jsqmd.com/news/1311397/

相关文章:

  • MiniSpring框架学习笔记-AutoProxyCreator:如何自动添加动态代理?
  • 白转黑维生素 B 深度测评,针对白发的作用一目了然
  • 2026 年现阶段冕宁评价高的食堂订餐带刷脸就餐系统实力厂家深度解析与优选指南,打饭还在掏饭卡?这玩意儿居然让食堂省了一半找零的麻烦 - 企业官方推荐【认证】
  • 计算机文件系统核心概念:目录、文件夹与路径的深度解析与实践指南
  • Depix实测:像素化文字还原原理、部署与实战调优指南
  • 2026年7月杭州代理记账公司哪家靠谱?正规财税机构推荐榜 - 行业深度分析
  • 10平米锦鲤池用六仓还是简化过滤?2026年别再花冤枉钱了
  • 2026 没有公网 IP 也能安全回家:Tailscale 远程访问 AdGuard Home / NAS / SSH 完整指南
  • 2026年8月护坡水泥砖/混凝土水泥砖公司推荐合集_高邑县东玉墙体材料厂 - 行业平台推荐
  • 单片机毕设选题推荐:基于单片机的多按键智能温控报警系统开发 基于物联网的家用热水设备远程监测平台设计(016701)
  • 2026 年现阶段,青海评价高的燃气辐射加热器制造厂选哪家,冬天室外搭帐篷取暖,这玩意儿居然比空调省半幅电费? - 企业推荐官【认证】
  • 2026年8月pph四通管件/江苏pph管帽管件厂家厂家推荐_江苏磊霆精密新材料有限公司 - 品牌宣传支持者
  • Windows原生SSH服务器部署与配置全指南:从原理到实战
  • 显卡驱动清理终极指南:DDU深度卸载工具完全教程 [特殊字符]
  • 《凌微经》的“静态自悖”概念到底是指什么?
  • GetQzonehistory:三步快速备份QQ空间全部历史说说的终极指南
  • 网络安全零基础到实战:从计算机基础到漏洞挖掘的完整学习路径
  • 电力监测系统厂家推荐:2026年靠谱品牌选择指南! - 优质品牌商家
  • 2026年天津活动搭建租赁厂家推荐:舞台搭建、桁架出租、场景布置、桌椅设备租赁选型指南 - 海棠依旧大
  • 2026年南京庭院改造勘测怎么做?排水坡度地下管网一次讲透
  • 如何一键抓取网页视频:猫抓浏览器扩展的终极使用指南
  • ClickHouse十大最佳实践技巧
  • C#程序脱壳实战:使用de4dot与dnSpy逆向分析.NET混淆代码
  • 零基础小白做抖店必看!1688 无货源一件代发实操步骤,搭配抖掌柜单人轻松运营店铺 - 电商分享
  • IP175G引脚图详解:硬件设计、PCB布局与故障排查指南
  • 单片机毕业设计-基于 STM32 与 OLED 的可调时交通灯监控系统设计 基于 STM32 与 TM1650 数码管交通倒计时装置设计(016101)
  • 2026年上海宝山装潢公司推荐:室内外装修、家装设计、旧房翻新、局部改造选型指南 - 海棠依旧大
  • 2026 年 7 月新发布:盐亭热门的弯管生产厂家深度解析,用了十年的老物件,居然是家装里看不见的隐形功臣-江东管道 - 领域鉴赏官
  • 华为Mate60账号锁终极解决方案:官方解锁全流程与风险避坑指南
  • 图解人工智能(92)人工智能前沿-预测新冠病毒传染性