【Go】基于GoFiber从零开始搭建一个GoWeb后台管理系统(四)用户管理、部门管理模块

雨点打透心脏的1/2处 2024-02-05 14:25 73阅读 0赞

第一篇:【Go】基于GoFiber从零开始搭建一个GoWeb后台管理系统(一)搭建项目

第二篇:【Go】基于GoFiber从零开始搭建一个GoWeb后台管理系统(二)日志输出中间件、校验token中间件、配置路由、基础工具函数。

第三篇:【Go】基于GoFiber从零开始搭建一个GoWeb后台管理系统(三)日志管理(登录日志、操作日志)、用户登录模块

碎碎念:最近脖子一直好痛啊,已经持续有一段时间了,从早痛到晚,早上起床就痛,到晚上睡觉都还在痛,导致我经常睡不着觉。之前也会脖子痛,但都是痛一两天就好了。真的坐着痛、躺着痛,普通枕头痛、U型枕痛、不枕枕头也痛。。。大家有什么办法可以缓解一下吗?是要去按摩?

接收参数

gofiber中,接收前端传过来的参数,有以下几种传参方式及接收方式:

  • 用问号拼接在地址后面的参数,我们一般用 c.Query("参数名") 获取;
  • 路径参数(直接拼接在地址后面,比如:/sys/user/getById/1,这里的 1 就是路径参数),用 c.Params("参数名")
  • FormData参数,用 c.FormValue("参数名") 接收;
  • FormData传的文件参数,用 c.MultipartForm() 获取;
  • JSON参数,用 c.BodyParser(结构体指针) 接收,就是将json转为具体的某个结构体。

数据库操作

  • 新增:用 Create() ,新增单条数据和批量新增都是用这个方法:db.Create(&user)、db.Create(&list)
  • 修改:
    • Update() 、UpdateColumn() :更新指定的列,每次只更新一个字段。
    • Updates() 、UpdateColumns():更新整个结构体或map,但是注意这两个方法不会更新零值。
    • Save() :插入或更新,传结构体或map,可以插入、更新零值。根据主键进行更新,如果主键不存在则插入。
  • 删除:Delete() 默认根据主键删除或批量删除,也可以在 Delete() 前面使用 Where() ,指定条件删除。
  • 查询:可以使用 Find() 查询单条数据或多条数据。

Update() 、UpdateColumn() 和 Updates() 、UpdateColumns() 其实是有一点区别的,具体我们可以看看源码:

在这里插入图片描述

tx.Statement.SkipHooks = true 这句代码是关于事务(transaction)的设置。当这个设置被设置为 true 时,GORM 将跳过与该事务相关的所有钩子。

然后更新时,可以使用 Select() 指定要更新的字段,更新或新增时,可以使用 Omit() 忽略指定字段。

具体怎么操作数据库,我们直接看下面的代码。

用户管理

首先是用户管理模块,有这几个接口:

在这里插入图片描述

废话不多说,我们直接上代码

controller层:sys_user.go

  1. package sys
  2. import (
  3. "fmt"
  4. "github.com/gofiber/fiber/v2"
  5. "go-web2/app/common/config"
  6. "go-web2/app/common/util"
  7. "go-web2/app/model/sys"
  8. "path/filepath"
  9. "strings"
  10. "time"
  11. )
  12. type UserController struct{
  13. }
  14. // 获取当前登录的用户
  15. func (UserController) GetLoginUser(c *fiber.Ctx) error {
  16. // 获取请求中的 Token
  17. token := c.Get(config.TokenHeader)
  18. user := sys.GetLoginUser(token)
  19. result := map[string]any{
  20. }
  21. result["user"] = user
  22. result["roles"] = user.RoleId
  23. result["permissions"] = sys.GetPermsMenuByRoleId(user.RoleId)
  24. return c.Status(200).JSON(config.Success(result))
  25. }
  26. // 用户列表
  27. func (UserController) GetPage(c *fiber.Ctx) error {
  28. user := sys.SysUserView{
  29. }
  30. user.UserName = c.Query("code")
  31. user.RealName = c.Query("name")
  32. user.AncestorId = c.Query("parentId")
  33. user.Token = c.Get(config.TokenHeader)
  34. pageSize := c.QueryInt("pageSize", 10)
  35. pageNum := c.QueryInt("pageNum", 1)
  36. return c.Status(200).JSON(config.Success(user.GetPage(pageSize, pageNum)))
  37. }
  38. // 根据id获取用户
  39. func (UserController) GetById(c *fiber.Ctx) error {
  40. user := sys.SysUser{
  41. }
  42. user.Id = c.Params("id")
  43. user.Token = c.Get(config.TokenHeader)
  44. err := user.GetUser()
  45. if err != nil {
  46. return c.Status(200).JSON(config.Error("获取用户失败,或没有查看权限"))
  47. }
  48. return c.Status(200).JSON(config.Success(user))
  49. }
  50. // 新增用户
  51. func (UserController) Insert(c *fiber.Ctx) error {
  52. var user sys.SysUser
  53. if err := c.BodyParser(&user); err != nil {
  54. return c.Status(200).JSON(config.Error(err.Error()))
  55. }
  56. pwd, err := util.GetEncryptedPassword(user.Password)
  57. if err != nil {
  58. return c.Status(200).JSON(config.Error("密码加密失败"))
  59. }
  60. user.Password = pwd
  61. user.Token = c.Get(config.TokenHeader)
  62. err = user.Insert()
  63. if err != nil {
  64. return c.Status(200).JSON(config.Error(err.Error()))
  65. }
  66. return c.Status(200).JSON(config.Success(nil))
  67. }
  68. // 修改用户
  69. func (UserController) Update(c *fiber.Ctx) error {
  70. var user sys.SysUser
  71. if err := c.BodyParser(&user); err != nil {
  72. return c.Status(200).JSON(config.Error(err.Error()))
  73. }
  74. user.Token = c.Get(config.TokenHeader)
  75. err := user.Update()
  76. if err != nil {
  77. return c.Status(200).JSON(config.Error(err.Error()))
  78. }
  79. return c.Status(200).JSON(config.Success(nil))
  80. }
  81. // 删除用户
  82. func (UserController) Delete(c *fiber.Ctx) error {
  83. var user sys.SysUser
  84. if err := c.BodyParser(&user); err != nil {
  85. return c.Status(200).JSON(config.Error(err.Error()))
  86. }
  87. user.Token = c.Get(config.TokenHeader)
  88. ids := strings.Split(user.Id, ",")
  89. if err := user.Delete(ids); err != nil {
  90. return c.Status(200).JSON(config.Error("删除用户失败"))
  91. }
  92. return c.Status(200).JSON(config.Success(nil))
  93. }
  94. // 修改密码
  95. func (UserController) UpdatePassword(c *fiber.Ctx) error {
  96. // 正常是需要加解密的(没有前端就暂时不先加密解密)
  97. //newPassword := util.RSADecrypt(c.FormValue("newPassword"))
  98. //oldPassword := util.RSADecrypt(c.FormValue("oldPassword"))
  99. //id := util.RSADecrypt(c.FormValue("id"))
  100. newPassword := c.FormValue("newPassword")
  101. oldPassword := c.FormValue("oldPassword")
  102. id := c.FormValue("id")
  103. var password sys.Password
  104. password.Id = id
  105. password.OldPassword = oldPassword
  106. password.NewPassword = newPassword
  107. password.Token = c.Get(config.TokenHeader)
  108. err := password.UpdatePassword()
  109. if err != nil {
  110. return c.Status(200).JSON(config.Error(err.Error()))
  111. }
  112. return c.Status(200).JSON(config.Success(nil))
  113. }
  114. // 重置密码
  115. func (UserController) ResetPassword(c *fiber.Ctx) error {
  116. var user sys.SysUser
  117. user.Id = c.FormValue("id")
  118. if user.Id == "" {
  119. return c.Status(200).JSON(config.Error("用户id不可为空"))
  120. }
  121. user.Token = c.Get(config.TokenHeader)
  122. err := user.ResetPassword()
  123. if err != nil {
  124. return c.Status(200).JSON(config.Error(err.Error()))
  125. }
  126. return c.Status(200).JSON(config.Success(nil))
  127. }
  128. // 上传头像
  129. func (UserController) Upload(c *fiber.Ctx) error {
  130. form, err := c.MultipartForm()
  131. if err != nil {
  132. fmt.Println(err)
  133. return c.Status(200).JSON(config.Error("头像上传失败"))
  134. }
  135. // 获取新的文件名
  136. name := form.File["file"][0].Filename // 获取文件名
  137. suffix := name[strings.LastIndex(name, "."):] // 文件后缀:.jpg
  138. fileName := fmt.Sprintf("%d%s", time.Now().UnixMilli(), suffix) // 新文件名
  139. // 相对路径:/upload/20231208
  140. relative := filepath.Join(config.FilePath, time.Now().Format("20060102"))
  141. // 保存文件
  142. err = util.SaveFile(form, relative, fileName)
  143. if err != nil {
  144. return c.Status(200).JSON(config.Error(err.Error()))
  145. }
  146. var user sys.SysUser
  147. user.Token = c.Get(config.TokenHeader)
  148. relative = filepath.Join(relative, fileName)
  149. user.Picture = &relative // 数据库保存相对路径
  150. user.Upload() // 保存头像到数据库
  151. return c.Status(200).JSON(config.Success(relative))
  152. }

上面这几个接口,就用到了 c.Query()c.Params()c.FormValue()c.MultipartForm()c.BodyParser() 这几种方式。

model层:sys_user.go

接口有了,那接下来就是获取数据了,数据从数据库来的,所以下面就是来操作数据库了。

  1. package sys
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/google/uuid"
  6. "go-web2/app/common/config"
  7. "go-web2/app/common/util"
  8. "strings"
  9. "time"
  10. )
  11. // 用户信息model,对应数据库的 sys_user 表
  12. type SysUser struct {
  13. config.BaseModel // 嵌套公共的model,这样就可以使用 BaseModel 的字段了
  14. SysUserView
  15. Password string `gorm:"password" json:"password" form:"password"` // 加密密码
  16. }
  17. // 用户信息model,用于展示给前端
  18. type SysUserView struct {
  19. config.BaseModel // 嵌套公共的model,这样就可以使用 BaseModel 的字段了
  20. UserName string `json:"userName" form:"userName"` // 用户名称
  21. RealName string `json:"realName" form:"realName"` // 真实姓名
  22. DeptId string `json:"deptId" form:"deptId"` // 部门id
  23. DeptName string `json:"deptName" form:"deptName"` // 部门名称
  24. AncestorId string `json:"ancestorId" form:"ancestorId"` // 祖级id
  25. AncestorName string `json:"ancestorName" form:"ancestorName"` // 祖级名称
  26. ChildId string `json:"childId" form:"childId"` // 部门id及子级id
  27. ChildName string `json:"childName" form:"childName"` // 部门名称及子级名称
  28. RoleId string `json:"roleId" form:"roleId"` // 角色id
  29. RoleKey string `json:"roleKey" form:"roleKey"` // 角色代码
  30. RoleName string `json:"roleName" form:"roleName"` // 角色名称
  31. Phone *string `json:"phone" form:"phone"` // 联系电话 这里用指针,是因为可以传空(这个空不是指空字符串,而是null)
  32. State int `json:"state" form:"state"` // 状态(1 启用 2 停用)
  33. Picture *string `json:"picture" form:"picture"` // 头像地址
  34. }
  35. // 密码结构体,用于修改密码
  36. type Password struct {
  37. config.BaseModel // 嵌套公共的model,这样就可以使用 BaseModel 的字段了
  38. OldPassword string `json:"oldPassword" form:"oldPassword"` // 旧密码
  39. NewPassword string `json:"newPassword" form:"newPassword"` // 新密码
  40. }
  41. // 新增、更新用户信息时,要忽略的字段
  42. var omit = "dept_name,ancestor_id,ancestor_name,child_id,child_name,role_key,role_name"
  43. // 获取用户管理的表名
  44. func (SysUserView) TableName() string {
  45. return "sys_user"
  46. }
  47. // 列表
  48. func (e *SysUserView) GetPage(pageSize int, pageNum int) config.PageInfo {
  49. var list []SysUserView // 查询结果
  50. var total int64 // 总数
  51. query := config.DB.Table(e.TableName())
  52. if e.UserName != "" {
  53. query.Where("user_name like ?", fmt.Sprintf("%%%s%%", e.UserName))
  54. }
  55. if e.RealName != "" {
  56. query.Where("real_name like ?", fmt.Sprintf("%%%s%%", e.RealName))
  57. }
  58. if e.AncestorId != "" {
  59. ids, _ := GetDeptChild(e.AncestorId) // 获取当前 parentId 的所有子节点包括它本身
  60. query.Where("FIND_IN_SET(dept_id,?)", ids)
  61. }
  62. // 数据过滤
  63. scope := AppendQueryDataScope(e.Token, "dept_id", "2", false, true)
  64. if scope != "" {
  65. query.Where(scope)
  66. }
  67. // 关联部门和角色表查询
  68. offset := (pageNum - 1) * pageSize // 计算跳过的记录数
  69. query.Debug().Order("sys_user.create_time desc").
  70. Select("sys_user.*, b.name as dept_name,role_key,role_name").
  71. Joins("left join sys_dept b on b.id = sys_user.dept_id").
  72. Joins("left join sys_role c on c.id = sys_user.role_id").
  73. Offset(offset).Limit(pageSize).Find(&list) // 分页查询,根据offset和limit来查询
  74. query.Count(&total) // 查询总数用Count,注意查总数不能直接连在Find()后面,需要分开来单独一句才能正确查询到总数。
  75. return config.PageInfo{
  76. list, total}
  77. }
  78. // 详情
  79. func (e *SysUser) GetUser() (err error) {
  80. query := config.DB.Table(e.TableName())
  81. sql := `
  82. SELECT a.*,b.name dept_name,role_key,role_name
  83. FROM sys_user a
  84. LEFT JOIN sys_dept b on FIND_IN_SET(b.id,a.dept_id)
  85. LEFT JOIN sys_role c on a.role_id=c.id
  86. `
  87. args := []interface{
  88. }{
  89. }
  90. if e.Id != "" {
  91. sql = sql + "WHERE a.id = ?"
  92. args = append(args, e.Id)
  93. }
  94. if e.UserName != "" {
  95. sql = sql + "WHERE user_name = ?"
  96. args = append(args, e.UserName)
  97. }
  98. // 数据过滤
  99. scope := AppendQueryDataScope(e.Token, "dept_id", "2", false, true)
  100. if scope != "" {
  101. sql = sql + " AND " + scope
  102. }
  103. if err = query.Raw(sql, args...).Find(e).Error; err != nil {
  104. return
  105. }
  106. if e.Id == "" || e.UserName == "" {
  107. err = errors.New("没有查看权限!")
  108. return
  109. }
  110. return
  111. }
  112. // 新增
  113. func (e *SysUser) Insert() (err error) {
  114. // 校验新增的用户和当前用户是否是同一部门或子部门
  115. if !CheckDataScope(e.Token, e.DeptId, false, true) {
  116. err = errors.New("没有操作权限!")
  117. return
  118. }
  119. // 校验用户名和手机号码
  120. var count int64
  121. db := config.DB.Table(e.TableName())
  122. db.Where("user_name = ?", e.UserName).Count(&count)
  123. if count > 0 {
  124. err = errors.New("用户名称已存在!")
  125. return
  126. }
  127. if e.Phone != nil {
  128. db.Where("phone = ?", e.Phone).Count(&count)
  129. if count > 0 {
  130. err = errors.New("手机号码已存在!")
  131. return
  132. }
  133. }
  134. e.Id = strings.ReplaceAll(uuid.NewString(), "-", "")
  135. e.CreatorId = GetLoginId(e.Token)
  136. e.CreateTime = time.Now()
  137. config.DB.Table(e.TableName()).Omit(omit).Create(e)
  138. return
  139. }
  140. // 修改
  141. func (e *SysUser) Update() (err error) {
  142. // 校验修改的用户和当前用户是否是同一部门或子部门
  143. if !CheckDataScope(e.Token, e.DeptId, false, true) {
  144. err = errors.New("没有操作权限!")
  145. return
  146. }
  147. var byId SysUser
  148. config.DB.Table(e.TableName()).Where("id = ?", e.Id).Find(&byId)
  149. if byId.DeptId != e.DeptId && !CheckDataScope(e.Token, byId.DeptId, false, true) {
  150. err = errors.New("没有操作权限!")
  151. return
  152. }
  153. // 校验用户名和手机号码
  154. var count int64
  155. db := config.DB.Table(e.TableName())
  156. db.Where("user_name = ? and id <> ?", e.UserName, e.Id).Count(&count)
  157. if count > 0 {
  158. err = errors.New("用户名称已存在!")
  159. return
  160. }
  161. if e.Phone != nil {
  162. db.Where("phone = ? and id <> ?", e.Phone, e.Id).Count(&count)
  163. if count > 0 {
  164. err = errors.New("手机号码已存在!")
  165. return
  166. }
  167. }
  168. config.DB.Table(e.TableName()).Omit(omit).Model(&SysUser{
  169. }).Where("id = ?", e.Id).Updates(e)
  170. return
  171. }
  172. // 删除
  173. func (e *SysUser) Delete(ids []string) (err error) {
  174. // 先查询要删除的用户
  175. scope := GetDataScope(e.Token, false, true)
  176. if scope != "" {
  177. var list []SysUser
  178. config.DB.Table(e.TableName()).Where("id in (?)", ids).Find(&list)
  179. split := strings.Split(scope, ",")
  180. for _, user := range list {
  181. if !util.IsContain(split, user.DeptId) {
  182. err = errors.New("没有操作权限!")
  183. return
  184. }
  185. }
  186. }
  187. config.DB.Table(e.TableName()).Delete(&SysUser{
  188. }, ids)
  189. return
  190. }
  191. // 修改密码
  192. func (e *Password) UpdatePassword() (err error) {
  193. if e.NewPassword == "" || e.OldPassword == "" || e.Id == "" {
  194. err = errors.New("数据解密失败")
  195. return
  196. }
  197. var user SysUser
  198. config.DB.Table(user.TableName()).Where("id = ?", e.Id).Find(&user)
  199. if !CheckDataScope(e.Token, user.DeptId, false, true) {
  200. err = errors.New("没有操作权限!")
  201. return
  202. }
  203. if e.NewPassword == e.OldPassword {
  204. err = errors.New("新密码不可于旧密码相同")
  205. return
  206. }
  207. if e.NewPassword == config.InitPassword {
  208. err = errors.New("新密码不可于初始密码相同")
  209. return
  210. }
  211. // 正则表达式我去你大爷!!!不校验了,我校验你大爷!!!从别的项目复制过来的正则,为什么你就死活校验不上!!
  212. // 同样的正则,我一模一样复制到校验工具去验证都可以匹配上,就你不行,我去你大爷,不校验了,校验你爹!!!
  213. /*reg := "^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[_#?!@$%^&*-]).{8,}$"
  214. m, _ := regexp.MatchString(reg, e.NewPassword)
  215. if !m {
  216. err = errors.New("密码长度大于7,且必须由数字、大小写字母、特殊字符组成")
  217. return
  218. }*/
  219. b := util.AuthenticatePassword(e.OldPassword, user.Password)
  220. if !b {
  221. err = errors.New("旧密码错误")
  222. return
  223. }
  224. newPassword, err := util.GetEncryptedPassword(e.NewPassword)
  225. if err = config.DB.Table(user.TableName()).Where("id = ?", e.Id).Update("password", newPassword).Error; err != nil {
  226. err = errors.New("密码修改失败")
  227. return
  228. }
  229. return
  230. }
  231. // 重置密码为初始密码
  232. func (e *SysUser) ResetPassword() (err error) {
  233. var user SysUser
  234. config.DB.Table(e.TableName()).Where("id = ?", e.Id).Find(&user)
  235. if !CheckDataScope(e.Token, user.DeptId, false, true) {
  236. err = errors.New("没有操作权限!")
  237. return
  238. }
  239. password, err := util.GetEncryptedPassword(config.InitPassword)
  240. if err = config.DB.Table(e.TableName()).Where("id = ?", e.Id).Update("password", password).Error; err != nil {
  241. err = errors.New("密码重置失败")
  242. return
  243. }
  244. return
  245. }
  246. // 上传头像
  247. func (e *SysUser) Upload() {
  248. id := GetLoginId(e.Token)
  249. config.DB.Table(e.TableName()).Where("id = ?", id).Update("picture", e.Picture)
  250. }
  251. // 根据部门id校验是否存在用户
  252. func CheckDeptExistUser(deptId string) bool {
  253. var count int64
  254. query := config.DB.Table(SysUserView{
  255. }.TableName())
  256. query.Where("dept_id = ?", deptId).Count(&count)
  257. return count > 0
  258. }
  259. // 根据角色id校验是否存在用户
  260. func CheckRoleExistUser(roleId string) bool {
  261. var count int64
  262. query := config.DB.Table(SysUserView{
  263. }.TableName())
  264. query.Where("role_id = ?", roleId).Count(&count)
  265. return count > 0
  266. }

通过上面controller、model层的方法、函数,不难看出,我很多函数都绑定了结构体,controller层绑定的结构体是 UserController ,model层也绑定了对应的结构体。像这种绑定了具体的数据类型的函数我们称为方法。

在这里插入图片描述

将函数绑定数据类型,这样子就可以让函数只属于这个数据类型了。我们知道,在go中,同一个包下函数名是不可以重复的,但我们给那些重名的函数绑定不同数据类型,就可以了。这样可以省去好多命名的烦恼。

假如某个函数是专门用来处理某个结构体的业务时,那就可以给这个函数绑定对应的结构体(不绑定也可以,就是函数命名的时候需要注意)。

部门管理

接下来是部门管理模块,有以下几个接口:

在这里插入图片描述

controller层:sys_dept.go

  1. package sys
  2. import (
  3. "github.com/gofiber/fiber/v2"
  4. "go-web2/app/common/config"
  5. "go-web2/app/model/sys"
  6. )
  7. type DeptController struct{
  8. }
  9. // 部门树列表
  10. func (DeptController) GetList(c *fiber.Ctx) error {
  11. dept := sys.SysDept{
  12. }
  13. dept.Token = c.Get(config.TokenHeader)
  14. return c.Status(200).JSON(config.Success(dept.GetListTree()))
  15. }
  16. // 根据id获取部门
  17. func (DeptController) GetById(c *fiber.Ctx) error {
  18. dept := sys.SysDept{
  19. }
  20. dept.Id = c.Params("id")
  21. dept.Token = c.Get(config.TokenHeader)
  22. err := dept.GetById()
  23. if err != nil {
  24. return c.Status(200).JSON(config.Error(err.Error()))
  25. }
  26. return c.Status(200).JSON(config.Success(dept))
  27. }
  28. // 新增部门
  29. func (DeptController) Insert(c *fiber.Ctx) error {
  30. var dept sys.SysDept
  31. if err := c.BodyParser(&dept); err != nil {
  32. //log.Error(err.Error())
  33. return c.Status(200).JSON(config.Error(err.Error()))
  34. }
  35. dept.Token = c.Get(config.TokenHeader)
  36. err := dept.Insert()
  37. if err != nil {
  38. return c.Status(200).JSON(config.Error(err.Error()))
  39. }
  40. return c.Status(200).JSON(config.Success(nil))
  41. }
  42. // 修改部门
  43. func (DeptController) Update(c *fiber.Ctx) error {
  44. var dept sys.SysDept
  45. if err := c.BodyParser(&dept); err != nil {
  46. //log.Error(err.Error())
  47. return c.Status(200).JSON(config.Error(err.Error()))
  48. }
  49. dept.Token = c.Get(config.TokenHeader)
  50. err := dept.Update()
  51. if err != nil {
  52. return c.Status(200).JSON(config.Error(err.Error()))
  53. }
  54. return c.Status(200).JSON(config.Success(nil))
  55. }
  56. // 删除部门
  57. func (DeptController) Delete(c *fiber.Ctx) error {
  58. var dept sys.SysDept
  59. dept.Id = c.Params("id")
  60. dept.Token = c.Get(config.TokenHeader)
  61. if err := dept.Delete(); err != nil {
  62. return c.Status(200).JSON(config.Error(err.Error()))
  63. }
  64. return c.Status(200).JSON(config.Success(nil))
  65. }
  66. // 部门下拉树列表
  67. func (DeptController) GetSelectList(c *fiber.Ctx) error {
  68. dept := sys.SysDept{
  69. }
  70. dept.Token = c.Get(config.TokenHeader)
  71. return c.Status(200).JSON(config.Success(dept.GetListTree()))
  72. }

model层:sys_dept.go

  1. package sys
  2. import (
  3. "github.com/google/uuid"
  4. "github.com/pkg/errors"
  5. "go-web2/app/common/config"
  6. "strings"
  7. "time"
  8. )
  9. // 部门管理
  10. type SysDept struct {
  11. config.BaseModel
  12. Name string `json:"name" form:"name"` // 名称
  13. ParentId string `json:"parentId" form:"parentId"` // 上级部门id
  14. Level int `json:"level" form:"level"` // 层级(1 根目录 2 单位 3 部门 4 小组)
  15. Sort int `json:"sort" form:"sort"` // 序号
  16. Children []SysDept `gorm:"-" json:"children"` // 子级数据
  17. }
  18. // 获取表名
  19. func (SysDept) TableName() string {
  20. return "sys_dept"
  21. }
  22. // 递归所有子节点
  23. func (e *SysDept) ChildList() []SysDept {
  24. // 获取所以子节点包括它本身
  25. sql := `
  26. WITH RECURSIVE temp_dept AS (
  27. SELECT id,name,parent_id FROM sys_dept WHERE id = ?
  28. UNION ALL
  29. SELECT d.id,d.name,d.parent_id FROM sys_dept d
  30. JOIN temp_dept sd ON sd.id = d.parent_id
  31. )
  32. SELECT * FROM temp_dept
  33. `
  34. var childList []SysDept
  35. config.DB.Raw(sql, e.ParentId).Find(&childList)
  36. return childList
  37. }
  38. // 根据parentId得到所有子节点的id和name(包括它本身),以逗号分隔
  39. func GetDeptChild(parentId string) (string, string) {
  40. dept := SysDept{
  41. }
  42. dept.ParentId = parentId
  43. list := dept.ChildList()
  44. if len(list) > 0 {
  45. idList := []string{
  46. }
  47. nameList := []string{
  48. }
  49. for _, t := range list {
  50. idList = append(idList, t.Id)
  51. nameList = append(nameList, t.Name)
  52. }
  53. return strings.Join(idList, ","), strings.Join(nameList, ",")
  54. }
  55. return "", ""
  56. }
  57. // 递归所有父节点,获取获取祖级列表(不包括它本身)
  58. func (e *SysDept) GetAncestor() (string, string) {
  59. sql := `
  60. WITH RECURSIVE temp_dept(id,name,parent_id,level) AS (
  61. SELECT id,name,parent_id,level
  62. FROM sys_dept
  63. WHERE id = ?
  64. UNION ALL
  65. SELECT d.id,d.name,d.parent_id,d.level
  66. FROM sys_dept d
  67. JOIN temp_dept c ON d.id = c.parent_id
  68. )
  69. SELECT id,name,parent_id,level
  70. FROM temp_dept
  71. WHERE id != ?
  72. ORDER BY level,parent_id
  73. `
  74. var ancestorList []SysDept
  75. config.DB.Raw(sql, e.Id, e.Id).Find(&ancestorList)
  76. idList := []string{
  77. }
  78. nameList := []string{
  79. }
  80. for _, t := range ancestorList {
  81. idList = append(idList, t.Id)
  82. nameList = append(nameList, t.Name)
  83. }
  84. return strings.Join(idList, ","), strings.Join(nameList, ",")
  85. }
  86. // 树形列表
  87. func (e *SysDept) GetListTree() []SysDept {
  88. var list []SysDept // 查询结果
  89. sql := ""
  90. args := []interface{
  91. }{
  92. }
  93. loginUser := GetLoginUser(e.Token)
  94. e.Id = loginUser.DeptId
  95. if e.Id != "" {
  96. // 这里用于递归 e.Id 的父级根节点,因为转为树结构时,是从根节点开始递归的(不包括e.Id本身)
  97. sql = `WITH RECURSIVE temp_dept(id,parent_id,name,level,sort) AS (
  98. SELECT id,parent_id,name,level,sort
  99. FROM sys_dept
  100. WHERE id = ?
  101. UNION ALL
  102. SELECT d.id,d.parent_id,d.name,d.level,d.sort
  103. FROM sys_dept d
  104. JOIN temp_dept c ON d.id = c.parent_id
  105. )
  106. SELECT id,parent_id,name,level,sort
  107. FROM temp_dept
  108. WHERE id != ?
  109. UNION ALL`
  110. args = append(args, e.Id, e.Id)
  111. }
  112. sql += ` SELECT id,parent_id,name,level,sort FROM sys_dept`
  113. // 设置数据范围查询
  114. scope := AppendQueryDataScope(e.Token, "id", "2", false, true)
  115. if scope != "" {
  116. sql = sql + " WHERE " + scope
  117. }
  118. config.DB.Table(e.TableName()).Debug().Order("`level`,parent_id,sort asc").Raw(sql, args...).Find(&list)
  119. return buildDeptTree(list, "ROOT")
  120. }
  121. // 获取详情
  122. func (e *SysDept) GetById() (err error) {
  123. if !CheckDataScope(e.Token, e.Id, false, true) {
  124. err = errors.New("没有操作权限!")
  125. return
  126. }
  127. config.DB.Table(e.TableName()).Where("id = ?", e.Id).Find(e)
  128. return
  129. }
  130. // 新增
  131. func (e *SysDept) Insert() (err error) {
  132. // 新增部门时,只允许新增子部门(也就是只允许给当前用户所在部门新增子部门)
  133. if !CheckDataScope(e.Token, e.ParentId, false, true) {
  134. err = errors.New("没有操作权限!")
  135. return
  136. }
  137. // 校验用户名和手机号码
  138. var count int64
  139. query := config.DB.Table(e.TableName())
  140. query.Where("name = ? and parent_id = ?", e.Name, e.ParentId).Count(&count)
  141. if count > 0 {
  142. err = errors.New("名称已存在!")
  143. return
  144. }
  145. err = e.getLevel()
  146. if err != nil {
  147. return err
  148. }
  149. e.Id = strings.ReplaceAll(uuid.NewString(), "-", "")
  150. e.CreatorId = GetLoginId(e.Token)
  151. e.CreateTime = time.Now()
  152. config.DB.Table(e.TableName()).Create(e)
  153. // 新增成功,更新数据权限缓存
  154. exists, _ := config.RedisConn.Exists(config.DATA_SCOPE + e.ParentId).Result()
  155. if exists > 0 {
  156. childId := config.RedisConn.HGet(config.DATA_SCOPE+e.ParentId, "childId").Val()
  157. childName := config.RedisConn.HGet(config.DATA_SCOPE+e.ParentId, "childName").Val()
  158. config.RedisConn.HSet(config.DATA_SCOPE+e.ParentId, "childId", childId+","+e.Id)
  159. config.RedisConn.HSet(config.DATA_SCOPE+e.ParentId, "childName", childName+","+e.Name)
  160. }
  161. return
  162. }
  163. // 修改
  164. func (e *SysDept) Update() (err error) {
  165. // 修改部门时,只允许修改当前部门和子部门数据
  166. if !CheckDataScope(e.Token, e.Id, false, true) {
  167. err = errors.New("没有操作权限!")
  168. return
  169. }
  170. // 校验用户名和手机号码
  171. var count int64
  172. query := config.DB.Table(e.TableName())
  173. query.Where("name = ? and parent_id = ? and id <> ?", e.Name, e.ParentId, e.Id).Count(&count)
  174. if count > 0 {
  175. err = errors.New("名称已存在!")
  176. return
  177. }
  178. err = e.getLevel()
  179. if err != nil {
  180. return err
  181. }
  182. config.DB.Table(e.TableName()).Model(&SysDept{
  183. }).Where("id = ?", e.Id).Updates(e)
  184. return
  185. }
  186. // 删除
  187. func (e *SysDept) Delete() (err error) {
  188. // 修改部门时,只允许修改当前部门和子部门数据
  189. if !CheckDataScope(e.Token, e.Id, false, true) {
  190. err = errors.New("没有操作权限!")
  191. return
  192. }
  193. // 1、校验是否存在下级
  194. var count int64
  195. query := config.DB.Table(e.TableName())
  196. query.Where("parent_id = ?", e.Id).Count(&count)
  197. if count > 0 {
  198. err = errors.New("存在下级,不允许删除")
  199. return
  200. }
  201. // 2、校验是否存在用户
  202. if CheckDeptExistUser(e.Id) {
  203. err = errors.New("该组织存在用户,不允许删除")
  204. return
  205. }
  206. if err = config.DB.Table(e.TableName()).Delete(e).Error; err != nil {
  207. return
  208. }
  209. return
  210. }
  211. // 新增或修改部门时,根据父级的level获取当前部门的level
  212. func (e *SysDept) getLevel() (err error) {
  213. if e.ParentId == "ROOT" {
  214. e.Level = 1
  215. } else {
  216. var parent SysDept
  217. parent.Id = e.ParentId
  218. parent.GetById()
  219. if parent.Name == "" {
  220. err = errors.New("上级不存在!")
  221. return
  222. }
  223. e.Level = parent.Level + 1
  224. }
  225. return
  226. }
  227. // 构建树结构
  228. func (e *SysDept) BuildTree(list []SysDept, parentId string) []SysDept {
  229. var tree []SysDept
  230. for _, item := range list {
  231. if item.ParentId == parentId {
  232. children := e.BuildTree(list, item.Id)
  233. if len(children) > 0 {
  234. item.Children = children
  235. }
  236. tree = append(tree, item)
  237. }
  238. }
  239. return tree
  240. }

最后

其实上面的代码都没有什么特别需要注意的地方,都是一些业务代码来的。对新手来说,需要注意的地方,可能就是查询数据的时候,特别是用原生sql拼接查询条件时,要用占位符的形式(也就是用 “?” 表示)拼接,那些查询条件不要用加号或者是fmt.Sprintf() 去拼接,不然容易有sql注入的问题。

还有就是分页查询,需要查询总数时,Count() 方法需要分开单独一句写,不然查的就不是总数,而是每一页的数量。

好啦,以上就是本篇文章的全部内容辣,等我更完这个项目的全部文章,我会放出完整代码的地址,欢迎大家多多点赞支持下,最后可以关注我不迷路~

发表评论

表情:
评论列表 (有 0 条评论,73人围观)

还没有评论,来说两句吧...

相关阅读