尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

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

从 PHP 到 AI + Golang,程序员自救转型手记(四十七):列表通用排序接口实现(增量重排法)
📅 发布时间:2026/8/2 5:21:39

这是一个系列 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 asc和weigh 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 API的Select方法去选择字段,只是这样又需要反射读取结构体中数据库列名对应的字段值了,没必要。

反转切片代码优化

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

// 向下拖动时反转,保证等权重区间内相对顺序不变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.php的sortable方法,找出原因:

找到原因了。对比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 居然还会犯这种逻辑错误。

相关新闻

  • 2026 年现阶段,青海评价高的燃气辐射加热器制造厂选哪家,冬天室外搭帐篷取暖,这玩意儿居然比空调省半幅电费? - 企业推荐官【认证】
  • 2026年8月护坡水泥砖/混凝土水泥砖公司推荐合集_高邑县东玉墙体材料厂 - 行业平台推荐
  • Depix实测:像素化文字还原原理、部署与实战调优指南

最新新闻

  • 2026男生自我养发指南:不用折腾网红款,28年老国货莹朴专业男生防脱 - 中媒介
  • 基于OpenCV与C++的机器视觉运动控制系统实战解析
  • 微信小程序Canvas图片裁剪:从原理到实战实现
  • 机器学习损失函数全解析:从MSE到Focal Loss的原理与应用实战
  • Linux网络管理利器nmcli:从基础操作到网卡绑定实战指南
  • 装修公司哪家全案做得好? - 中媒介

日新闻

  • 怀化母婴除甲醛公司测甲醛中心怎么选:康之居母婴除甲醛标准、流程、避坑指南 - 信誉隆金银铂奢回收
  • 三步打造你的终极音乐中心:foobox-cn网络电台功能完整指南
  • Lance湖仓格式:为多模态AI工作流设计的终极数据存储方案

周新闻

  • 怀化母婴除甲醛公司测甲醛中心怎么选:康之居母婴除甲醛标准、流程、避坑指南 - 信誉隆金银铂奢回收
  • 三步打造你的终极音乐中心:foobox-cn网络电台功能完整指南
  • Lance湖仓格式:为多模态AI工作流设计的终极数据存储方案

月新闻

  • ClickHouse版本管理深度实战:4步构建零风险升级与回滚体系
  • Java 23 种设计模式:从踩坑到精通 | 番外:责任链模式 —— 物流审批流程实战
  • 华硕笔记本性能解放指南:G-Helper轻量级控制工具全面解析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号