From 1d1ca218157230bfe0b83b2972f30ed5c2c2df8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=AF=BB=E6=AC=A2?= Date: Fri, 2 Feb 2024 11:44:11 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20=E6=96=B0=E5=A2=9E=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E8=8F=9C=E5=8D=95=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/admin/menu/save.go | 24 +++++++ internal/orm/sort.go | 148 +++++++++++++++++++++++++++++++++++++++ model/param/menu/save.go | 16 +++++ router/admin/menu.go | 3 +- service/menu/save.go | 77 ++++++++++++++++++++ 5 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 api/admin/menu/save.go create mode 100644 internal/orm/sort.go create mode 100644 model/param/menu/save.go create mode 100644 service/menu/save.go diff --git a/api/admin/menu/save.go b/api/admin/menu/save.go new file mode 100644 index 0000000..f2923fc --- /dev/null +++ b/api/admin/menu/save.go @@ -0,0 +1,24 @@ +package menu + +import ( + "github.com/gin-gonic/gin" + menuParam "wechat-robot/model/param/menu" + "wechat-robot/pkg/response" + "wechat-robot/service/menu" +) + +// Save +// @description: 保存菜单 +// @param ctx +func Save(ctx *gin.Context) { + var p menuParam.Save + if err := ctx.ShouldBind(&p); err != nil { + response.New(ctx).SetError(err).Fail() + return + } + if err := menu.Save(p); err != nil { + response.New(ctx).SetError(err).Fail() + return + } + response.New(ctx).Success() +} diff --git a/internal/orm/sort.go b/internal/orm/sort.go new file mode 100644 index 0000000..2519225 --- /dev/null +++ b/internal/orm/sort.go @@ -0,0 +1,148 @@ +package orm + +import ( + "fmt" + "gitee.ltd/lxh/logger/log" + "gorm.io/gorm" + "slices" +) + +// @title checkHasDeletedAt +// @description 检查指定表是否有id_del字段 +// @param tx *gorm.DB "已开启的事务对象" +// @param tableName string "表名" +// @return bool "是否包含" +func checkHasDeletedAt(tx *gorm.DB, tableName string) bool { + var columns []string + // SQL语句 + sql := fmt.Sprintf("select COLUMN_NAME from information_schema.COLUMNS where table_name = '%s'", tableName) + err := tx.Raw(sql).Scan(&columns).Error + if err != nil { + log.Errorf("查询表字段失败: %v", err.Error()) + return false + } + return slices.Contains(columns, "id_del") +} + +// @title checkHasUpdatedAt +// @description 检查指定表是否有updated_at字段 +// @param tx *gorm.DB "已开启的事务对象" +// @param tableName string "表名" +// @return bool "是否包含" +func checkHasUpdatedAt(tx *gorm.DB, tableName string) bool { + var columns []string + // SQL语句 + sql := fmt.Sprintf("select COLUMN_NAME from information_schema.COLUMNS where table_name = '%s'", tableName) + err := tx.Raw(sql).Scan(&columns).Error + if err != nil { + log.Errorf("查询表字段失败: %v", err.Error()) + return false + } + return slices.Contains(columns, "updated_at") +} + +// UpdateSortBefore +// @description 更新之前处理序号 +// @param tx *gorm.DB "已开启的事务对象" +// @param model any "模型对象" +// @return error "错误信息" +func UpdateSortBefore(tx *gorm.DB, tableName, id string, sort int, param string) (err error) { + // 查出原来的排序号 + var oldSort int + err = tx.Table(tableName).Select("`sort`").Where("id = ?", id).Scan(&oldSort).Error + if err != nil { + log.Errorf("查询老数据失败: %v", err.Error()) + return + } + // 如果相等,啥都不干 + if oldSort == sort { + return nil + } + + // 查询是否包含 id_del 字段 + hasDeletedAt := checkHasDeletedAt(tx, tableName) + + // 处理排序 + // 如果老的排序号小于新的,(老, 新]之间的排序号都要-1 + // 如果老的大于新的,[老, 新)排序号-1 + if oldSort < sort { + // 老的小于新的,[老, 新) + 1 + sel := tx.Table(tableName). + Where("sort <= ? AND sort > ?", sort, oldSort) + if hasDeletedAt { + sel.Where("id_del = 0") + } + if param != "" { + sel.Where(param) // 自定义条件 + } + err = sel.Update("sort", gorm.Expr("sort - 1")).Error + } else { + // 老的大于新的,[新, 老) + 1 + sel := tx.Table(tableName). + Where("sort >= ? AND sort < ?", sort, oldSort) + if hasDeletedAt { + sel.Where("id_del = 0") + } + if param != "" { + sel.Where(param) // 自定义条件 + } + err = sel.Update("sort", gorm.Expr("sort + 1")).Error + } + return +} + +// CreateSortBefore +// @description 新建之前处理序号 +// @param tx *gorm.DB "已开启的事务对象" +// @param model any "模型对象" +// @return error "错误信息" +func CreateSortBefore(tx *gorm.DB, tableName string, sort int, param string) (err error) { + // 查询是否包含 id_del 字段 + hasDeletedAt := checkHasDeletedAt(tx, tableName) + + // 处理排序,如果没有传,就会是在最前面 + sel := tx.Table(tableName).Where("sort >= ?", sort) + if hasDeletedAt { + sel.Where("id_del = 0") + } + if param != "" { + sel.Where(param) + } + err = sel.Update("sort", gorm.Expr("sort + 1")).Error + if err != nil { + log.Errorf("处理前置排序失败:%v", err) + } + return +} + +// DealSortAfter +// @description 处理序号之后 +// @param tx *gorm.DB "已开启的事务对象" +// @param modelName string "表名" +// @return error "错误信息" +func DealSortAfter(tx *gorm.DB, modelName, param string) (err error) { + // 保存成功,刷新排序 + if param != "" { + param = " AND " + param + } + + // 查询是否包含 id_del 字段 + hasDeletedAt := checkHasDeletedAt(tx, modelName) + if hasDeletedAt { + param += " AND id_del = 0" + } + + // 如果有更新时间字段,也更新一下值 + updateParam := "" + if checkHasUpdatedAt(tx, modelName) { + updateParam = ",updated_at = NOW()" + } + + sql := fmt.Sprintf("UPDATE %s a, (SELECT (@i := @i + 1) i, id FROM %s WHERE 1=1 %s order by sort ASC) i, "+ + "(SELECT @i := 0) ir SET a.sort = i.i %s WHERE a.id = i.id", modelName, modelName, param, updateParam) + err = tx.Exec(sql).Error + if err != nil { + log.Errorf("刷新排序失败: %v", err.Error()) + } + return +} diff --git a/model/param/menu/save.go b/model/param/menu/save.go new file mode 100644 index 0000000..dfd2729 --- /dev/null +++ b/model/param/menu/save.go @@ -0,0 +1,16 @@ +package menu + +import "wechat-robot/pkg/types" + +// Save +// @description: 保存菜单入参 +type Save struct { + Id string `json:"id" form:"id" comment:"菜单Id"` + Type types.MenuType `json:"type" form:"type" comment:"菜单类型(菜单或按钮)" validate:"oneof=MENU BUTTON"` + Name string `json:"name" form:"name" comment:"页面组件名称"` + Path string `json:"path" form:"path" comment:"访问路径"` + Title string `json:"title" form:"title" comment:"菜单标题"` + Icon string `json:"icon" form:"icon" comment:"菜单图标"` + Sort int `json:"sort" form:"sort" comment:"排序值(数字越小越靠前)"` + ParentId string `json:"parentId" form:"parentId" comment:"父级菜单ID"` +} diff --git a/router/admin/menu.go b/router/admin/menu.go index 4716b7f..c13557b 100644 --- a/router/admin/menu.go +++ b/router/admin/menu.go @@ -9,5 +9,6 @@ import ( // @description: 菜单相关 // @param g func menu(g *gin.RouterGroup) { - g.GET("/self", menuApi.GetUserMenuTree) + g.GET("/self", menuApi.GetUserMenuTree) // 获取当前用户菜单 + g.POST("", menuApi.Save) // 保存菜单 } diff --git a/service/menu/save.go b/service/menu/save.go new file mode 100644 index 0000000..cb5d7c9 --- /dev/null +++ b/service/menu/save.go @@ -0,0 +1,77 @@ +package menu + +import ( + "fmt" + "wechat-robot/internal/database" + "wechat-robot/internal/orm" + "wechat-robot/model/entity" + menuParam "wechat-robot/model/param/menu" +) + +// Save +// @description: 保存菜单 +// @param param menuParam.Save 菜单数据 +// @return err error 错误信息 +func Save(param menuParam.Save) (err error) { + tx := database.Client.Begin() + defer func() { + if err != nil { + tx.Rollback() + return + } + tx.Commit() + }() + + // 提前梳理出参数 + paramSql := "parent_id IS NULL" + if param.ParentId != "" { + paramSql = fmt.Sprintf("parent_id = '%s'", param.ParentId) + } + + if param.Id == "" { + // 新增 + var m entity.Menu + // 预处理排序 + if err = orm.CreateSortBefore(tx, m.TableName(), param.Sort, paramSql); err != nil { + return + } + // 组装参数 + m.Type = param.Type + m.Name = param.Name + m.Path = param.Path + m.Title = param.Title + m.Icon = param.Icon + m.Sort = param.Sort + if param.ParentId != "" { + m.ParentId = ¶m.ParentId + } + err = tx.Create(&m).Error + } else { + // 修改 + // 预处理排序 + if err = orm.UpdateSortBefore(tx, entity.Menu{}.TableName(), param.Id, param.Sort, paramSql); err != nil { + return + } + // 组装参数 + var m = make(map[string]any) + m["type"] = param.Type + m["name"] = param.Name + m["path"] = param.Path + m["title"] = param.Title + m["icon"] = param.Icon + m["sort"] = param.Sort + if param.ParentId != "" { + m["parent_id"] = ¶m.ParentId + } + err = tx.Model(&entity.Menu{}).Where("id = ?", param.Id).Updates(m).Error + } + + if err != nil { + return + } + + // 处理排序 + err = orm.DealSortAfter(tx, entity.Menu{}.TableName(), paramSql) + + return +}