From 9be75d53fe7ab898393b270435b0f56800087dbe Mon Sep 17 00:00:00 2001 From: Blizzard Date: Sun, 14 Sep 2025 21:58:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20RBAC=20=E5=9F=BA=E6=9C=AC=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/v1/system/auth.go | 16 ++-- api/v1/system/enter.go | 8 +- api/v1/system/sys_client.go | 10 +-- api/v1/system/sys_menu.go | 100 ++++++++++++++++++++++++ api/v1/system/sys_role.go | 40 ++++++---- api/v1/system/sys_user.go | 100 +++++++++++++++++++++++- config-dev.yaml | 2 +- global/model.go | 8 +- initialize/gorm.go | 2 + initialize/router.go | 2 + model/commom/request/common.go | 10 +-- model/system/request/jwt.go | 2 +- model/system/request/sys_menu.go | 6 ++ model/system/request/sys_role.go | 5 ++ model/system/request/sys_user.go | 10 +++ model/system/response/sys_user.go | 4 - model/system/sys_client.go | 2 +- model/system/sys_menu.go | 17 ++++ model/system/sys_role.go | 7 +- model/system/sys_role_menu.go | 6 ++ model/system/sys_user.go | 6 +- model/system/sys_user_role.go | 6 ++ router/system/enter.go | 2 + router/system/menu_router.go | 19 +++++ router/system/role_router.go | 3 +- router/system/user_router.go | 6 ++ service/system/enter.go | 1 + service/system/sys_client.go | 18 +++-- service/system/sys_menu.go | 126 ++++++++++++++++++++++++++++++ service/system/sys_role.go | 43 +++++++++- service/system/sys_user.go | 43 +++++++++- utils/claims.go | 40 +++++----- utils/hash_test.go | 16 ++-- utils/jwt.go | 3 +- utils/uniqueid/id_generator.go | 13 +++ 35 files changed, 615 insertions(+), 87 deletions(-) create mode 100644 api/v1/system/sys_menu.go create mode 100644 model/system/request/sys_menu.go create mode 100644 model/system/sys_menu.go create mode 100644 model/system/sys_role_menu.go create mode 100644 model/system/sys_user_role.go create mode 100644 router/system/menu_router.go create mode 100644 service/system/sys_menu.go create mode 100644 utils/uniqueid/id_generator.go diff --git a/api/v1/system/auth.go b/api/v1/system/auth.go index 3c063e5..9469281 100644 --- a/api/v1/system/auth.go +++ b/api/v1/system/auth.go @@ -1,15 +1,16 @@ package system import ( - "github.com/gin-gonic/gin" - "github.com/mojocn/base64Captcha" - "go.uber.org/zap" "sundynix-go/global" "sundynix-go/model/commom/response" "sundynix-go/model/system" systemReq "sundynix-go/model/system/request" systemRes "sundynix-go/model/system/response" "sundynix-go/utils" + + "github.com/gin-gonic/gin" + "github.com/mojocn/base64Captcha" + "go.uber.org/zap" ) var store = base64Captcha.DefaultMemStore @@ -26,7 +27,7 @@ func (a *AuthApi) Login(c *gin.Context) { } if l.CaptchaId != "" && l.Captcha != "" && store.Verify(l.CaptchaId, l.Captcha, true) { u := &system.User{Account: l.Account, Password: l.Password} - user, err := UserService.Login(u) + user, err := userService.Login(u) if err != nil { global.Logger.Error("登录失败! 用户名不存在或者密码错误!", zap.Error(err)) response.FailWithMsg("用户名不存在或者密码错误", c) @@ -38,6 +39,11 @@ func (a *AuthApi) Login(c *gin.Context) { response.FailWithMsg("验证码错误", c) } +// 登出 +func (a *AuthApi) Logout(c *gin.Context) { + utils.ClearToken(c) +} + // Captcha api 生成验证码 func (u *AuthApi) Captcha(c *gin.Context) { var driver = base64Captcha.DriverString{ @@ -45,7 +51,7 @@ func (u *AuthApi) Captcha(c *gin.Context) { Width: 240, NoiseCount: 2, ShowLineOptions: 4, - Length: 6, + Length: 4, Source: "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM", } diff --git a/api/v1/system/enter.go b/api/v1/system/enter.go index 1e0b5e9..23d1fa7 100644 --- a/api/v1/system/enter.go +++ b/api/v1/system/enter.go @@ -7,10 +7,12 @@ type ApiGroup struct { UserApi ClientApi RoleApi + MenuApi } var ( - UserService = service.ServiceGroupApp.SystemServiceGroup.UserService - ClientService = service.ServiceGroupApp.SystemServiceGroup.ClientService - RoleService = service.ServiceGroupApp.SystemServiceGroup.RoleService + userService = service.ServiceGroupApp.SystemServiceGroup.UserService + clientService = service.ServiceGroupApp.SystemServiceGroup.ClientService + roleService = service.ServiceGroupApp.SystemServiceGroup.RoleService + menuService = service.ServiceGroupApp.SystemServiceGroup.MenuService ) diff --git a/api/v1/system/sys_client.go b/api/v1/system/sys_client.go index 03e8d66..ee021f7 100644 --- a/api/v1/system/sys_client.go +++ b/api/v1/system/sys_client.go @@ -20,7 +20,7 @@ func (s *ClientApi) SaveClient(c *gin.Context) { response.FailWithMsg(err.Error(), c) return } - err = ClientService.SaveClient(client) + err = clientService.SaveClient(client) if err != nil { global.Logger.Error("保存客户端失败!", zap.Error(err)) response.FailWithMsg(err.Error(), c) @@ -36,7 +36,7 @@ func (s *ClientApi) UpdateClient(c *gin.Context) { response.FailWithMsg(err.Error(), c) return } - err = ClientService.UpdateClient(client) + err = clientService.UpdateClient(client) if err != nil { global.Logger.Error("更新客户端失败!", zap.Error(err)) response.FailWithMsg(err.Error(), c) @@ -52,7 +52,7 @@ func (s *ClientApi) GetClientList(c *gin.Context) { response.FailWithMsg(err.Error(), c) return } - list, total, err := ClientService.GetClientList(pageInfo) + list, total, err := clientService.GetClientList(pageInfo) if err != nil { global.Logger.Error("获取客户端列表失败!", zap.Error(err)) response.FailWithMsg(err.Error(), c) @@ -73,7 +73,7 @@ func (s *ClientApi) Delete(c *gin.Context) { response.FailWithMsg(err.Error(), c) return } - err = ClientService.DeleteClientByIds(ids) + err = clientService.DeleteClientByIds(ids) if err != nil { global.Logger.Error("删除客户端失败!", zap.Error(err)) response.FailWithMsg(err.Error(), c) @@ -89,7 +89,7 @@ func (s *ClientApi) Detail(c *gin.Context) { response.FailWithMsg(err.Error(), c) return } - client, err := ClientService.GetClientById(idInfo.ID) + client, err := clientService.GetClientById(idInfo.ID) if err != nil { global.Logger.Error("获取客户端详情失败!", zap.Error(err)) response.FailWithMsg(err.Error(), c) diff --git a/api/v1/system/sys_menu.go b/api/v1/system/sys_menu.go new file mode 100644 index 0000000..93ab381 --- /dev/null +++ b/api/v1/system/sys_menu.go @@ -0,0 +1,100 @@ +package system + +import ( + "sundynix-go/global" + "sundynix-go/model/commom/response" + "sundynix-go/model/system" + systemReq "sundynix-go/model/system/request" + "sundynix-go/utils" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type MenuApi struct { +} + +func (m *MenuApi) SaveMenu(c *gin.Context) { + var menu system.Menu + err := c.ShouldBindJSON(&menu) + if err != nil { + response.FailWithMsg("参数错误:"+err.Error(), c) + return + } + if err = menuService.SaveMenu(menu); err != nil { + global.Logger.Error("保存菜单失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } else { + response.OkWithMsg("保存菜单成功!", c) + } +} + +func (m *MenuApi) UpdateMenu(c *gin.Context) { + var menu system.Menu + err := c.ShouldBindJSON(&menu) + if err != nil { + response.FailWithMsg("参数错误:"+err.Error(), c) + return + } + if err = menuService.UpdateMenu(&menu); err != nil { + global.Logger.Error("更新菜单失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } else { + response.OkWithMsg("更新菜单成功!", c) + } +} + +func (m *MenuApi) DeleteMenu(c *gin.Context) { + id := c.Query("id") + err := menuService.DeleteMenu(id) + if err != nil { + global.Logger.Error("删除菜单失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + response.OkWithMsg("删除菜单成功!", c) +} + +func (m *MenuApi) Detail(c *gin.Context) { + id := c.Query("id") + menu, err := menuService.GetMenuById(id) + if err != nil { + global.Logger.Error("获取菜单详情失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + response.OkWithData(menu, c) +} + +func (m *MenuApi) GetAllMenuTree(c *gin.Context) { + var param systemReq.GetMenuTree + err := c.ShouldBindJSON(¶m) + if err != nil { + response.FailWithMsg(err.Error(), c) + return + } + menus, err := menuService.GetAllMenuTree(param.Category, param.ParentId) + if err != nil { + global.Logger.Error("获取菜单树结构失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + response.OkWithData(menus, c) +} + +func (m *MenuApi) GetUserMenuTree(c *gin.Context) { + +} + +func (m *MenuApi) Route(c *gin.Context) { + userId := utils.GetUserId(c) + routes, err := menuService.GetUserRoutes(userId) + if err != nil { + global.Logger.Error("获取用户菜单失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + response.OkWithData(routes, c) +} diff --git a/api/v1/system/sys_role.go b/api/v1/system/sys_role.go index c4409cb..0d32d33 100644 --- a/api/v1/system/sys_role.go +++ b/api/v1/system/sys_role.go @@ -1,13 +1,14 @@ package system import ( - "github.com/gin-gonic/gin" - "go.uber.org/zap" "sundynix-go/global" "sundynix-go/model/commom/request" "sundynix-go/model/commom/response" "sundynix-go/model/system" systemreq "sundynix-go/model/system/request" + + "github.com/gin-gonic/gin" + "go.uber.org/zap" ) type RoleApi struct { @@ -17,10 +18,10 @@ func (a *RoleApi) SaveRole(context *gin.Context) { var role system.Role err := context.ShouldBindJSON(&role) if err != nil { - response.FailWithMsg(err.Error(), context) + response.FailWithMsg("参数错误"+err.Error(), context) return } - err = RoleService.SaveRole(role) + err = roleService.SaveRole(role) if err != nil { global.Logger.Error("保存角色失败!", zap.Error(err)) response.FailWithMsg(err.Error(), context) @@ -36,7 +37,7 @@ func (a *RoleApi) UpdateRole(context *gin.Context) { response.FailWithMsg(err.Error(), context) return } - err = RoleService.UpdateRole(role) + err = roleService.UpdateRole(role) if err != nil { global.Logger.Error("更新角色失败!", zap.Error(err)) response.FailWithMsg(err.Error(), context) @@ -52,7 +53,7 @@ func (a *RoleApi) GetRoleList(c *gin.Context) { response.FailWithMsg(err.Error(), c) return } - list, total, err := RoleService.GetRoleList(pageInfo) + list, total, err := roleService.GetRoleList(pageInfo) if err != nil { global.Logger.Error("获取角色列表失败!", zap.Error(err)) response.FailWithMsg(err.Error(), c) @@ -72,7 +73,7 @@ func (a *RoleApi) Delete(context *gin.Context) { response.FailWithMsg(err.Error(), context) return } - err = RoleService.DeleteRoleByIds(ids) + err = roleService.DeleteRoleByIds(ids) if err != nil { global.Logger.Error("删除角色失败!", zap.Error(err)) response.FailWithMsg(err.Error(), context) @@ -82,13 +83,8 @@ func (a *RoleApi) Delete(context *gin.Context) { } func (a *RoleApi) Detail(context *gin.Context) { - var idInfo request.GetById - err := context.ShouldBindJSON(&idInfo) - if err != nil { - response.FailWithMsg(err.Error(), context) - return - } - role, err := RoleService.GetRoleById(idInfo.ID) + id := context.Query("id") + role, err := roleService.GetRoleById(id) if err != nil { global.Logger.Error("获取角色详情失败!", zap.Error(err)) response.FailWithMsg(err.Error(), context) @@ -96,3 +92,19 @@ func (a *RoleApi) Detail(context *gin.Context) { } response.OkWithData(role, context) } + +func (a *RoleApi) GrantMenu(c *gin.Context) { + var grantMenu systemreq.GrantMenu + err := c.ShouldBindJSON(&grantMenu) + if err != nil { + response.FailWithMsg(err.Error(), c) + return + } + err = roleService.GrantMenu(grantMenu.RoleId, grantMenu.MenuIds) + if err != nil { + global.Logger.Error("授权菜单失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + response.OkWithMsg("授权菜单成功!", c) +} diff --git a/api/v1/system/sys_user.go b/api/v1/system/sys_user.go index ef4b8f0..e10eb87 100644 --- a/api/v1/system/sys_user.go +++ b/api/v1/system/sys_user.go @@ -1,16 +1,49 @@ package system import ( + "sundynix-go/global" + "sundynix-go/model/commom/request" + "sundynix-go/model/commom/response" + "sundynix-go/model/system" + systemReq "sundynix-go/model/system/request" + "github.com/gin-gonic/gin" "go.uber.org/zap" - "sundynix-go/global" - "sundynix-go/model/commom/response" - systemReq "sundynix-go/model/system/request" ) type UserApi struct { } +func (u *UserApi) SaveUser(c *gin.Context) { + var user system.User + err := c.ShouldBindJSON(&user) + if err != nil { + response.FailWithMsg("参数错误:"+err.Error(), c) + return + } + if err = userService.SaveUser(user); err != nil { + global.Logger.Error("保存用户失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + } else { + response.OkWithMsg("保存用户成功!", c) + } +} + +func (u *UserApi) UpdateUser(c *gin.Context) { + var user system.User + err := c.ShouldBindJSON(&user) + if err != nil { + response.FailWithMsg("参数错误:"+err.Error(), c) + return + } + if err = userService.UpdateUser(&user); err != nil { + global.Logger.Error("更新用户失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + } else { + response.OkWithMsg("更新用户成功!", c) + } +} + func (u *UserApi) GetUserList(c *gin.Context) { var pageInfo systemReq.GetUserList err := c.ShouldBindJSON(&pageInfo) @@ -18,7 +51,7 @@ func (u *UserApi) GetUserList(c *gin.Context) { response.FailWithMsg(err.Error(), c) return } - list, total, err := UserService.GetUserList(pageInfo) + list, total, err := userService.GetUserList(pageInfo) if err != nil { global.Logger.Error("获取用户列表失败!", zap.Error(err)) response.FailWithMsg(err.Error(), c) @@ -31,3 +64,62 @@ func (u *UserApi) GetUserList(c *gin.Context) { PageSize: pageInfo.PageSize, }, c) } + +func (u *UserApi) Delete(c *gin.Context) { + var ids request.IdsReq + err := c.ShouldBindJSON(&ids) + if err != nil { + response.FailWithMsg(err.Error(), c) + return + } + err = userService.DeleteUserByIds(ids) + if err != nil { + global.Logger.Error("删除用户失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + response.OkWithMsg("删除用户成功!", c) +} + +func (u *UserApi) Detail(c *gin.Context) { + id := c.Query("id") + user, err := userService.GetUserById(id) + if err != nil { + global.Logger.Error("获取用户详情失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + response.OkWithData(user, c) +} + +func (u *UserApi) ChangePassword(c *gin.Context) { + var changePwd systemReq.ChangePwd + err := c.ShouldBindJSON(&changePwd) + if err != nil { + response.FailWithMsg(err.Error(), c) + return + } + err = userService.ChangePassword(changePwd.Id, changePwd.NewPwd) + if err != nil { + global.Logger.Error("修改密码失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + response.OkWithMsg("修改密码成功", c) +} + +func (u *UserApi) GrantRole(c *gin.Context) { + var grantRole systemReq.GrantRole + err := c.ShouldBindJSON(&grantRole) + if err != nil { + response.FailWithMsg(err.Error(), c) + return + } + err = roleService.GrantRole(grantRole.UserId, grantRole.RoleIds) + if err != nil { + global.Logger.Error("授权角色失败!", zap.Error(err)) + response.FailWithMsg(err.Error(), c) + return + } + response.OkWithMsg("授权角色成功!", c) +} diff --git a/config-dev.yaml b/config-dev.yaml index 770f5c4..1ff847b 100644 --- a/config-dev.yaml +++ b/config-dev.yaml @@ -22,7 +22,7 @@ mysql: max-open-conns: 100 host: 127.0.0.1 port: "3306" - prefix: "sundynix-" + prefix: "sundynix_" singular: true user: root password: root diff --git a/global/model.go b/global/model.go index cb3da4f..5ea42cb 100644 --- a/global/model.go +++ b/global/model.go @@ -1,12 +1,14 @@ package global import ( - "gorm.io/gorm" + "sundynix-go/utils/uniqueid" "time" + + "gorm.io/gorm" ) type BaseModel struct { - Id uint `gorm:"primarykey" json:"id"` // 主键ID + Id string `gorm:"size:50;primaryKey" json:"id"` // 主键ID CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` DeletedAt gorm.DeletedAt `gorm:"index" json:"deletedAt"` // 删除时间 @@ -14,6 +16,8 @@ type BaseModel struct { // BeforeCreate 定义一个钩子,在创建之前执行自动插入字段 func (model BaseModel) BeforeCreate(db *gorm.DB) (err error) { + //生成主键的string uniqueid + db.Statement.SetColumn("id", uniqueid.GenerateId()) db.Statement.SetColumn("created_at", time.Now()) db.Statement.SetColumn("updated_at", time.Now()) return diff --git a/initialize/gorm.go b/initialize/gorm.go index 9ff4df3..d31529f 100644 --- a/initialize/gorm.go +++ b/initialize/gorm.go @@ -32,6 +32,8 @@ func MigrateTable() { err := db.AutoMigrate( system.User{}, system.Client{}, + system.Role{}, + system.Menu{}, system.SysOperationRecord{}, ) if err != nil { diff --git a/initialize/router.go b/initialize/router.go index 1b759dd..e4f5e70 100644 --- a/initialize/router.go +++ b/initialize/router.go @@ -29,6 +29,7 @@ func Routers() { { //无须鉴权的路由 systemRouter.InitAuthRouter(PublicGroup) //登录和验证码不需要鉴权 + //systemRouter.InitMenuRouter(PublicGroup) } { @@ -36,6 +37,7 @@ func Routers() { systemRouter.InitUserRouter(NeedAuthGroup) //用户相关 systemRouter.InitClientRouter(NeedAuthGroup) //客户端相关 systemRouter.InitRoleRouter(NeedAuthGroup) //角色相关 + systemRouter.InitMenuRouter(NeedAuthGroup) //菜单相关 } address := fmt.Sprintf(":%d", global.Config.System.Addr) diff --git a/model/commom/request/common.go b/model/commom/request/common.go index af78fed..c8935d8 100644 --- a/model/commom/request/common.go +++ b/model/commom/request/common.go @@ -29,20 +29,20 @@ func (r *PageInfo) Paginate() func(db *gorm.DB) *gorm.DB { // GetById Find by id structure type GetById struct { - ID int `json:"id" form:"id"` // 主键ID + ID string `json:"id" form:"id"` // 主键ID } -func (r *GetById) Uint() uint { - return uint(r.ID) +func (r *GetById) Uint() string { + return string(r.ID) } type IdsReq struct { - Ids []int `json:"ids" form:"ids"` + Ids []string `json:"ids" form:"ids"` } // GetAuthorityId Get role by id structure type GetAuthorityId struct { - AuthorityId uint `json:"authorityId" form:"authorityId"` // 角色ID + AuthorityId string `json:"authorityId" form:"authorityId"` // 角色ID } type Empty struct{} diff --git a/model/system/request/jwt.go b/model/system/request/jwt.go index 6f09007..28c8448 100644 --- a/model/system/request/jwt.go +++ b/model/system/request/jwt.go @@ -13,6 +13,6 @@ type CustomClaims struct { type BaseClaims struct { UUID uuid.UUID - ID uint + ID string Account string } diff --git a/model/system/request/sys_menu.go b/model/system/request/sys_menu.go new file mode 100644 index 0000000..b204b49 --- /dev/null +++ b/model/system/request/sys_menu.go @@ -0,0 +1,6 @@ +package request + +type GetMenuTree struct { + Category int `json:"category" form:"category"` + ParentId string `json:"parentId" form:"parentId"` +} diff --git a/model/system/request/sys_role.go b/model/system/request/sys_role.go index f56c60c..d669b38 100644 --- a/model/system/request/sys_role.go +++ b/model/system/request/sys_role.go @@ -7,3 +7,8 @@ type GetRoleList struct { Code string `json:"code" form:"code"` Name string `json:"name" form:"name"` } + +type GrantMenu struct { + RoleId string `json:"roleId"` + MenuIds []string `json:"menuIds"` +} diff --git a/model/system/request/sys_user.go b/model/system/request/sys_user.go index 781c682..efb5e85 100644 --- a/model/system/request/sys_user.go +++ b/model/system/request/sys_user.go @@ -14,3 +14,13 @@ type GetUserList struct { Account string `json:"account" form:"account"` Phone string `json:"phone" form:"phone"` } + +type ChangePwd struct { + Id string `json:"id"` + NewPwd string `json:"newPwd"` +} + +type GrantRole struct { + UserId string `json:"userId"` + RoleIds []string `json:"roleIds"` +} diff --git a/model/system/response/sys_user.go b/model/system/response/sys_user.go index 78ba53c..d83fb3d 100644 --- a/model/system/response/sys_user.go +++ b/model/system/response/sys_user.go @@ -2,10 +2,6 @@ package response import "sundynix-go/model/system" -type SysUserResponse struct { - User system.User `json:"user"` -} - type LoginResponse struct { User system.User `json:"user"` Token string `json:"token"` diff --git a/model/system/sys_client.go b/model/system/sys_client.go index 1aa7bda..7801e10 100644 --- a/model/system/sys_client.go +++ b/model/system/sys_client.go @@ -8,5 +8,5 @@ type Client struct { Name string `gorm:"size:50;" json:"name"` GrantType string `gorm:"size:50;" json:"grantType"` AdditionalInfo string `gorm:"type:text" json:"additionalInfo"` - ActiveTimeout uint `json:"activeTimeout"` + ActiveTimeout int64 `json:"activeTimeout"` } diff --git a/model/system/sys_menu.go b/model/system/sys_menu.go new file mode 100644 index 0000000..2d014d8 --- /dev/null +++ b/model/system/sys_menu.go @@ -0,0 +1,17 @@ +package system + +import "sundynix-go/global" + +type Menu struct { + global.BaseModel + ParentId string `gorm:"size:100;default:'0'" json:"parentId" form:"parentId"` + Category int `json:"category" form:"category"` + Name string `gorm:"size:20" json:"name" form:"name"` + Title string `gorm:"size:20" json:"title" form:"title"` + Code string `gorm:"size:20" json:"code" form:"code"` + Permission string `gorm:"size:20" json:"permission" form:"permission"` + Locale string `gorm:"size:50" json:"locale" form:"locale"` + Icon string `gorm:"size:20" json:"icon" form:"icon"` + Sort int `json:"sort" form:"sort"` + Children []*Menu `json:"children" gorm:"-"` +} diff --git a/model/system/sys_role.go b/model/system/sys_role.go index eb76ac3..6a7fc33 100644 --- a/model/system/sys_role.go +++ b/model/system/sys_role.go @@ -4,7 +4,8 @@ import "sundynix-go/global" type Role struct { global.BaseModel - Name string `json:"name" form:"name"` - Code string `json:"code" form:"code"` - Sort int `json:"sort" form:"sort"` + Name string `gorm:"size:20" json:"name" form:"name"` + Code string `gorm:"size:20" json:"code" form:"code"` + Sort int `json:"sort" form:"sort"` + Menus []Menu `gorm:"many2many:role_menu;"` } diff --git a/model/system/sys_role_menu.go b/model/system/sys_role_menu.go new file mode 100644 index 0000000..7c9d0e9 --- /dev/null +++ b/model/system/sys_role_menu.go @@ -0,0 +1,6 @@ +package system + +type RoleMenu struct { + RoleId string `json:"roleId" gorm:"size:100;column:role_id;comment:角色id"` + MenuId string `json:"menuId" gorm:"size:100;column:menu_id;comment:菜单id"` +} diff --git a/model/system/sys_user.go b/model/system/sys_user.go index 5a418b6..a02639d 100644 --- a/model/system/sys_user.go +++ b/model/system/sys_user.go @@ -6,21 +6,23 @@ import ( type Login interface { GetAccount() string - GetUserId() uint + GetUserId() string GetUserInfo() any } type User struct { global.BaseModel + TenantId string `gorm:"size:20;" json:"tenantId" form:"tenantId"` ClientId string `gorm:"size:20;" json:"clientId"` Account string `gorm:"size:11;unique;" json:"account" form:"account"` Password string `gorm:"size:100;" json:"-" form:"password"` Phone string `gorm:"size:11;" json:"phone" form:"phone"` + Roles []Role `gorm:"many2many:user_role;" json:"roles"` } func (u *User) GetAccount() string { return u.Account } -func (u *User) GetUserId() uint { +func (u *User) GetUserId() string { return u.Id } diff --git a/model/system/sys_user_role.go b/model/system/sys_user_role.go new file mode 100644 index 0000000..9ff8b92 --- /dev/null +++ b/model/system/sys_user_role.go @@ -0,0 +1,6 @@ +package system + +type UserRole struct { + UserId string `json:"userId" gorm:"size:100;column:user_id;comment:用户id"` + RoleId string `json:"roleId" gorm:"size:100;column:role_id;comment:角色id"` +} diff --git a/router/system/enter.go b/router/system/enter.go index 0e4f27e..2a4a608 100644 --- a/router/system/enter.go +++ b/router/system/enter.go @@ -7,6 +7,7 @@ type RouterGroup struct { UserRouter ClientRouter RoleRouter + MenuRouter } // 初始化路由 @@ -15,4 +16,5 @@ var ( userApi = v1.ApiGroupApp.SystemApiGroup.UserApi clientApi = v1.ApiGroupApp.SystemApiGroup.ClientApi roleApi = v1.ApiGroupApp.SystemApiGroup.RoleApi + menuApi = v1.ApiGroupApp.SystemApiGroup.MenuApi ) diff --git a/router/system/menu_router.go b/router/system/menu_router.go new file mode 100644 index 0000000..d3475dc --- /dev/null +++ b/router/system/menu_router.go @@ -0,0 +1,19 @@ +package system + +import "github.com/gin-gonic/gin" + +type MenuRouter struct { +} + +func (m MenuRouter) InitMenuRouter(Router *gin.RouterGroup) { + menuRouter := Router.Group("menu") + { + menuRouter.GET("route", menuApi.Route) + menuRouter.POST("getAllMenuTree", menuApi.GetAllMenuTree) + menuRouter.POST("getUserMenuTree", menuApi.GetUserMenuTree) + menuRouter.POST("save", menuApi.SaveMenu) + menuRouter.POST("update", menuApi.UpdateMenu) + menuRouter.GET("delete", menuApi.DeleteMenu) + menuRouter.GET("detail", menuApi.Detail) + } +} diff --git a/router/system/role_router.go b/router/system/role_router.go index 87b1901..2b9ef6f 100644 --- a/router/system/role_router.go +++ b/router/system/role_router.go @@ -11,7 +11,8 @@ func (r *RoleRouter) InitRoleRouter(router *gin.RouterGroup) { roleRouter.POST("save", roleApi.SaveRole) roleRouter.POST("update", roleApi.UpdateRole) roleRouter.POST("getRoleList", roleApi.GetRoleList) - roleRouter.GET("delete", roleApi.Delete) + roleRouter.POST("delete", roleApi.Delete) roleRouter.GET("detail", roleApi.Detail) + roleRouter.POST("grantMenu", roleApi.GrantMenu) } } diff --git a/router/system/user_router.go b/router/system/user_router.go index 9767f04..c7fc885 100644 --- a/router/system/user_router.go +++ b/router/system/user_router.go @@ -10,6 +10,12 @@ type UserRouter struct { func (s *UserRouter) InitUserRouter(Router *gin.RouterGroup) { userRouter := Router.Group("user") { + userRouter.POST("save", userApi.SaveUser) + userRouter.POST("update", userApi.UpdateUser) userRouter.POST("getUserList", userApi.GetUserList) + userRouter.POST("delete", userApi.Delete) + userRouter.GET("detail", userApi.Detail) + userRouter.POST("changePassword", userApi.ChangePassword) + userRouter.POST("grantRole", userApi.GrantRole) } } diff --git a/service/system/enter.go b/service/system/enter.go index 87b367b..4474727 100644 --- a/service/system/enter.go +++ b/service/system/enter.go @@ -4,4 +4,5 @@ type ServiceGroup struct { UserService ClientService RoleService + MenuService } diff --git a/service/system/sys_client.go b/service/system/sys_client.go index 108d9f4..a4c76cf 100644 --- a/service/system/sys_client.go +++ b/service/system/sys_client.go @@ -2,11 +2,12 @@ package system import ( "errors" - "gorm.io/gorm" "sundynix-go/global" common "sundynix-go/model/commom/request" "sundynix-go/model/system" systemReq "sundynix-go/model/system/request" + + "gorm.io/gorm" ) type ClientService struct{} @@ -44,10 +45,17 @@ func (s *ClientService) GetClientList(info systemReq.GetClientList) (list interf } func (s *ClientService) DeleteClientByIds(ids common.IdsReq) (err error) { - return global.DB.Delete(&system.Client{}, "id IN ?", ids.Ids).Error + return global.DB.Where("id IN (?)", ids.Ids).Delete(&system.Client{}).Error } -func (s *ClientService) GetClientById(id int) (client system.Client, err error) { - err = global.DB.Where("id = ?", id).First(&client).Error - return client, err +func (s *ClientService) GetClientById(id string) (client *system.Client, err error) { + var c system.Client + err = global.DB.Where("id = ?", id).First(&c).Error + return &c, err +} + +func (s *ClientService) GetClientByClientId(clientId string) (client *system.Client, err error) { + var c system.Client + err = global.DB.Where("client_id = ?", clientId).First(&c).Error + return &c, err } diff --git a/service/system/sys_menu.go b/service/system/sys_menu.go new file mode 100644 index 0000000..7341268 --- /dev/null +++ b/service/system/sys_menu.go @@ -0,0 +1,126 @@ +package system + +import ( + "errors" + "sundynix-go/global" + "sundynix-go/model/system" + + "gorm.io/gorm" +) + +type MenuService struct{} + +var MenuServiceApp = new(MenuService) + +func (s *MenuService) SaveMenu(menu system.Menu) error { + //1.根据code和name查询是否存在重名 + if err := global.DB.Where("code = ? or name = ?", menu.Code, menu.Name).First(&system.Menu{}).Error; err == nil { + return errors.New("菜单已存在") + } + return global.DB.Create(&menu).Error +} + +func (s *MenuService) UpdateMenu(menu *system.Menu) (err error) { + var sysMenu system.Menu + menuMap := map[string]interface{}{ + "Category": menu.Category, + "Name": menu.Name, + "Title": menu.Title, + "Code": menu.Code, + "Permission": menu.Permission, + "Locale": menu.Locale, + "Icon": menu.Icon, + "Sort": menu.Sort, + } + err = global.DB.Where("id = ?", menu.Id).First(&sysMenu).Error + if err != nil { + global.Logger.Debug(err.Error()) + return errors.New("查询菜单失败") + } + err = global.DB.Model(&sysMenu).Updates(menuMap).Error + return err +} + +func (s *MenuService) DeleteMenu(id string) (err error) { + err = global.DB.First(&system.Menu{}, "parent_id = ?", id).Error + if err == nil { + return errors.New("请先删除子菜单") + } + var menu system.Menu + err = global.DB.Where("id = ?", id).First(&menu).Error + if err != nil { + return errors.New("菜单记录不存在") + } + // 同步删除menu表和role-menu表数据 + return global.DB.Transaction(func(tx *gorm.DB) error { + if err = tx.Where("id = ?", id).Delete(&system.Menu{}).Error; err != nil { + return err + } + if err = tx.Where("menu_id = ?", id).Delete(&system.RoleMenu{}).Error; err != nil { + return err + } + return nil + }) +} + +func (s *MenuService) GetMenuById(id string) (menu *system.Menu, err error) { + var m system.Menu + err = global.DB.Where("id = ?", id).First(&m).Error + return &m, err +} + +func (s *MenuService) GetAllMenuTree(category int, parentId string) (menus []*system.Menu, err error) { + //1,先根据category和parentId获取所有菜单 category默认为0,parentId默认为0 + //2.讲查询出的列表构建为树结构 + var menuList []*system.Menu + db := global.DB.Model(&system.Menu{}) + if category != 0 { + db.Where("category = ?", category) + } + if parentId != "0" { + db.Where("parent_id = ?", parentId) + } + err = db.Order("sort asc").Find(&menuList).Error + if err != nil { + return nil, err + } + tree := buildMenuTree(menuList) + return tree, nil +} + +func (s *MenuService) GetUserRoutes(userId string) (menus []*system.Menu, err error) { + //1.根据userId 查询角色 根据角色查询菜单 去重 + //2.构建树结构 + var roleIds []string + err = global.DB.Model(&system.UserRole{}).Where("user_id = ?", userId).Pluck("role_id", &roleIds).Error + var menuIds []string + err = global.DB.Model(&system.RoleMenu{}).Where("role_id in ?", roleIds).Pluck("menu_id", &menuIds).Error + var menuList []*system.Menu + err = global.DB.Model(&system.Menu{}).Where("id in ?", menuIds).Find(&menuList).Error + return buildMenuTree(menuList), nil +} + +func buildMenuTree(list []*system.Menu) []*system.Menu { + //1.定义一个map + menuMap := make(map[string]*system.Menu) + for _, item := range list { + menuMap[item.Id] = item + } + //构建树结构 + var treeList []*system.Menu + for _, item := range list { + if item.ParentId == "0" { + // 如果没有父节点,直接添加到树中 + treeList = append(treeList, item) + } else { + if parent, exists := menuMap[item.ParentId]; exists { + // 如果有父节点,将当前节点添加到父节点的Children中 + parent.Children = append(parent.Children, item) + } else { + // 如果没有父节点,将当前节点添加到树中 + treeList = append(treeList, item) + } + } + } + return treeList +} diff --git a/service/system/sys_role.go b/service/system/sys_role.go index 1b21649..795621e 100644 --- a/service/system/sys_role.go +++ b/service/system/sys_role.go @@ -2,11 +2,12 @@ package system import ( "errors" - "gorm.io/gorm" "sundynix-go/global" common "sundynix-go/model/commom/request" "sundynix-go/model/system" systemReq "sundynix-go/model/system/request" + + "gorm.io/gorm" ) type RoleService struct { @@ -48,7 +49,41 @@ func (s *RoleService) DeleteRoleByIds(ids common.IdsReq) error { return global.DB.Where("id in ?", ids.Ids).Delete(&system.Role{}).Error } -func (s *RoleService) GetRoleById(id int) (role system.Role, err error) { - err = global.DB.Where("id = ?", id).First(&role).Error - return role, err +func (s *RoleService) GetRoleById(id string) (role *system.Role, err error) { + var r system.Role + err = global.DB.Where("id = ?", id).First(&r).Error + return &r, err +} + +func (s *RoleService) GrantRole(userId string, roleIds []string) error { + //1. 检查是否存在userid的授权记录 存在就删除 不存在就插入 + //2. 插入新的数据 + return global.DB.Transaction(func(tx *gorm.DB) error { + if err := tx.Where("user_id = ?", userId).Delete(&system.UserRole{}).Error; err != nil { + return err + } + for _, roleId := range roleIds { + if err := tx.Create(&system.UserRole{UserId: userId, RoleId: roleId}).Error; err != nil { + return err + } + } + return nil + }) + +} + +func (s *RoleService) GrantMenu(roleId string, menuIds []string) error { + //1. 检查是否存在userid的授权记录 存在就删除 不存在就插入 + //2. 插入新的数据 + return global.DB.Transaction(func(tx *gorm.DB) error { + if err := tx.Where("role_id = ?", roleId).Delete(&system.RoleMenu{}).Error; err != nil { + return err + } + for _, menuId := range menuIds { + if err := tx.Create(&system.RoleMenu{RoleId: roleId, MenuId: menuId}).Error; err != nil { + return err + } + } + return nil + }) } diff --git a/service/system/sys_user.go b/service/system/sys_user.go index 342dba0..c8591e1 100644 --- a/service/system/sys_user.go +++ b/service/system/sys_user.go @@ -3,9 +3,12 @@ package system import ( "errors" "sundynix-go/global" + common "sundynix-go/model/commom/request" "sundynix-go/model/system" systemReq "sundynix-go/model/system/request" "sundynix-go/utils" + + "gorm.io/gorm" ) type UserService struct{} @@ -14,7 +17,8 @@ var UserServiceApp = new(UserService) func (userService *UserService) Login(u *system.User) (userInfo *system.User, err error) { var user system.User - err = global.DB.Where("account = ?", u.Account).First(&user).Error + // 查询出用户信息的同时查询出角色信息 + err = global.DB.Model(&system.User{}).Preload("Roles").Where("account = ?", u.Account).First(&user).Error if err == nil { if ok := utils.BcryptCheck(u.Password, user.Password); !ok { return nil, errors.New("密码错误") @@ -23,6 +27,29 @@ func (userService *UserService) Login(u *system.User) (userInfo *system.User, er return &user, err } +func (userService *UserService) SaveUser(user system.User) error { + if !errors.Is(global.DB.Where("account = ?", user.Account).First(&system.User{}).Error, gorm.ErrRecordNotFound) { + return errors.New("存在重复Account,请修改Account") + } + user.Password = utils.BcryptHash(user.Password) + return global.DB.Create(&user).Error +} + +func (userService *UserService) UpdateUser(user *system.User) (err error) { + var sysUser system.User + userMap := map[string]interface{}{ + "Account": user.Account, + "Phone": user.Phone, + } + err = global.DB.Where("id = ?", user.Id).First(&sysUser).Error + if err != nil { + global.Logger.Debug(err.Error()) + return errors.New("查询用户失败") + } + err = global.DB.Model(&sysUser).Updates(userMap).Error + return err +} + func (userService *UserService) GetUserList(info systemReq.GetUserList) (list interface{}, total int64, err error) { limit := info.PageSize offset := info.PageSize * (info.Current - 1) @@ -42,3 +69,17 @@ func (userService *UserService) GetUserList(info systemReq.GetUserList) (list in err = db.Limit(limit).Offset(offset).Find(&userList).Error return userList, total, err } + +func (userService *UserService) DeleteUserByIds(ids common.IdsReq) error { + return global.DB.Where("id IN (?)", ids.Ids).Delete(&system.User{}).Error +} + +func (userService *UserService) GetUserById(id string) (user *system.User, err error) { + var u system.User + err = global.DB.Where("id = ?", id).Preload("Roles").First(&u).Error + return &u, err +} + +func (userService *UserService) ChangePassword(id string, pwd string) (err error) { + return global.DB.Model(&system.User{}).Where("id = ?", id).Update("password", utils.BcryptHash(pwd)).Error +} diff --git a/utils/claims.go b/utils/claims.go index 6e6a6bb..c847c1c 100644 --- a/utils/claims.go +++ b/utils/claims.go @@ -1,12 +1,12 @@ package utils import ( - "github.com/gin-gonic/gin" "net" "sundynix-go/global" "sundynix-go/model/system" systemReq "sundynix-go/model/system/request" - "time" + + "github.com/gin-gonic/gin" ) // GetLoginToken 获取登录token @@ -57,22 +57,22 @@ func GetToken(c *gin.Context) string { // 从请求头中获取Authorization字段的值 token := c.Request.Header.Get("Authorization") - // 如果请求头中没有Authorization字段,则尝试从Cookie中获取token - if token == "" { - j := NewJWT() - token, _ = c.Cookie("x-token") - - // 解析并验证token的有效性 - claims, err := j.ParseToken(token) - if err != nil { - // 如果解析失败,记录错误日志并返回当前token - global.Logger.Error("重新写入cookie token失败,未能成功解析token,请检查请求头是否存在x-token且claims是否为规定结构") - return token - } - - // 如果token有效,则将其重新写入Cookie,并设置过期时间 - SetToken(c, token, int((claims.ExpiresAt.Unix()-time.Now().Unix())/60)) - } + //// 如果请求头中没有Authorization字段,则尝试从Cookie中获取token + //if token == "" { + // j := NewJWT() + // token, _ = c.Cookie("x-token") + // + // // 解析并验证token的有效性 + // claims, err := j.ParseToken(token) + // if err != nil { + // // 如果解析失败,记录错误日志并返回当前token + // global.Logger.Error("重新写入cookie token失败,未能成功解析token,请检查请求头是否存在x-token且claims是否为规定结构") + // return token + // } + // + // // 如果token有效,则将其重新写入Cookie,并设置过期时间 + // SetToken(c, token, int((claims.ExpiresAt.Unix()-time.Now().Unix())/60)) + //} // 返回获取到的token return token @@ -127,10 +127,10 @@ func GetUserInfo(c *gin.Context) *systemReq.CustomClaims { // // 返回值: // - uint: 返回用户的 ID,如果获取失败则返回 0。 -func GetUserId(c *gin.Context) uint { +func GetUserId(c *gin.Context) string { if claims, exists := c.Get("claims"); !exists { if cl, err := GetClaims(c); err != nil { - return 0 + return "0" } else { return cl.BaseClaims.ID } diff --git a/utils/hash_test.go b/utils/hash_test.go index fc4c221..f9d0e55 100644 --- a/utils/hash_test.go +++ b/utils/hash_test.go @@ -1,14 +1,20 @@ package utils import ( + "fmt" + "sundynix-go/utils/uniqueid" "testing" ) func TestHashPwd(t *testing.T) { - //hash := BcryptHash("admin") - //fmt.Println(hash) // $2a$10$QC/zkQ/ohPmvjF/goDyicu7cHgAEj8gHg6OTDHWhbYQMHHn4dwxX2 - // - //check := BcryptCheck("admin", "$2a$10$QC/zkQ/ohPmvjF/goDyicu7cHgAEj8gHg6OTDHWhbYQMHHn4dwxX2") - //fmt.Println(check) + hash := BcryptHash("sundynix") + fmt.Println(hash) // $2a$10$QC/zkQ/ohPmvjF/goDyicu7cHgAEj8gHg6OTDHWhbYQMHHn4dwxX2 + check := BcryptCheck("admin", "$2a$10$QC/zkQ/ohPmvjF/goDyicu7cHgAEj8gHg6OTDHWhbYQMHHn4dwxX2") + fmt.Println(check) +} + +func TestUuid(t *testing.T) { + id := uniqueid.GenerateId() + fmt.Println(id) } diff --git a/utils/jwt.go b/utils/jwt.go index f7b225c..f3787df 100644 --- a/utils/jwt.go +++ b/utils/jwt.go @@ -2,10 +2,11 @@ package utils import ( "errors" - "github.com/golang-jwt/jwt/v5" "sundynix-go/global" "sundynix-go/model/system/request" "time" + + "github.com/golang-jwt/jwt/v5" ) type JWT struct { diff --git a/utils/uniqueid/id_generator.go b/utils/uniqueid/id_generator.go new file mode 100644 index 0000000..1afa5e6 --- /dev/null +++ b/utils/uniqueid/id_generator.go @@ -0,0 +1,13 @@ +package uniqueid + +import ( + "github.com/google/uuid" +) + +func GenerateId() string { + uuidV1, err := uuid.NewUUID() + if err != nil { + panic(err) + } + return uuidV1.String() +}