diff --git a/api/config.sample.toml b/api/config.sample.toml index b89ca439..700b122c 100644 --- a/api/config.sample.toml +++ b/api/config.sample.toml @@ -10,10 +10,6 @@ WeChatBot = false SecretKey = "azyehq3ivunjhbntz78isj00i4hz2mt9xtddysfucxakadq4qbfrt0b7q3lnvg80" # 注意:这个是 JWT Token 授权密钥,生产环境请务必更换 MaxAge = 86400 -[Manager] - Username = "admin" - Password = "admin123" # 如果是生产环境的话,这里管理员的密码记得修改 - [Redis] # redis 配置信息 Host = "localhost" Port = 6379 diff --git a/api/handler/admin/admin_permission_handler.go b/api/handler/admin/admin_permission_handler.go new file mode 100644 index 00000000..ad3bf724 --- /dev/null +++ b/api/handler/admin/admin_permission_handler.go @@ -0,0 +1,127 @@ +package admin + +import ( + "chatplus/core" + "chatplus/core/types" + "chatplus/handler" + "chatplus/store/model" + "chatplus/store/vo" + "chatplus/utils" + "chatplus/utils/resp" + "fmt" + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +type SysPermissionHandler struct { + handler.BaseHandler + db *gorm.DB +} + +func NewSysPermissionHandler(app *core.AppServer, db *gorm.DB) *SysPermissionHandler { + h := SysPermissionHandler{db: db} + h.App = app + return &h +} + +func (h *SysPermissionHandler) List(c *gin.Context) { + var items []model.AdminPermission + var data = make([]vo.AdminPermission, 0) + res := h.db.Find(&items) + if res.Error != nil { + resp.ERROR(c, "暂无数据") + return + } + for _, item := range items { + adminPermissionVo := vo.AdminPermission{} + _ = utils.CopyObject(item, &adminPermissionVo) + data = append(data, adminPermissionVo) + } + + data = ArrayToTree(data) + resp.SUCCESS(c, data) +} + +func ArrayToTree(dates []vo.AdminPermission) []vo.AdminPermission { + group := make(map[int][]vo.AdminPermission, 0) + for _, node := range dates { + group[node.Pid] = append(group[node.Pid], node) + } + // 初始化递归,从根节点开始构建树 + result := FindSiblings(group[0], group) + + return result +} + +func FindSiblings(siblings []vo.AdminPermission, group map[int][]vo.AdminPermission) []vo.AdminPermission { + result := make([]vo.AdminPermission, 0) + for _, sibling := range siblings { + children, ok := group[sibling.Id] + if ok { + sibling.Children = FindSiblings(children, group) + } + + result = append(result, sibling) + } + return result +} + +func (h *SysPermissionHandler) Save(c *gin.Context) { + var data struct { + Id int `json:"id"` + Name string `json:"name"` + Slug string `json:"slug"` + Sort int `json:"sort"` + Pid int `json:"pid"` + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + var permission = model.AdminPermission{} + var res *gorm.DB + if data.Id > 0 { // 更新 + permission.Id = data.Id + // 此处需要用 map 更新,用结构体无法更新 0 值 + res = h.db.Model(&permission).Updates(map[string]interface{}{ + "name": data.Name, + "slug": data.Slug, + "sort": data.Sort, + "pid": data.Pid, + }) + } else { + p := model.AdminPermission{ + Name: data.Name, + Slug: data.Slug, + Sort: data.Sort, + Pid: data.Pid, + } + res = h.db.Create(&p) + } + if res.Error != nil { + fmt.Println(res.Error) + resp.ERROR(c, "更新数据库失败") + return + } + resp.SUCCESS(c) +} + +func (h *SysPermissionHandler) Remove(c *gin.Context) { + var data struct { + Id int + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + if data.Id > 0 { + res := h.db.Where("id = ?", data.Id).Delete(&model.AdminPermission{}) + if res.Error != nil { + resp.ERROR(c, "删除失败") + return + } + } + resp.SUCCESS(c) +} diff --git a/api/handler/admin/admin_role_handler.go b/api/handler/admin/admin_role_handler.go new file mode 100644 index 00000000..9d7605a8 --- /dev/null +++ b/api/handler/admin/admin_role_handler.go @@ -0,0 +1,145 @@ +package admin + +import ( + "chatplus/core" + "chatplus/core/types" + "chatplus/handler" + "chatplus/store/model" + "chatplus/store/vo" + "chatplus/utils" + "chatplus/utils/resp" + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +type SysRoleHandler struct { + handler.BaseHandler + db *gorm.DB +} + +func NewSysRoleHandler(app *core.AppServer, db *gorm.DB) *SysRoleHandler { + h := SysRoleHandler{db: db} + h.App = app + return &h +} + +type permission struct { + Id int `json:"id"` + Name string `json:"name"` +} + +func (h *SysRoleHandler) List(c *gin.Context) { + var items []model.AdminRole + var data = make([]vo.AdminRole, 0) + res := h.db.Find(&items) + if res.Error != nil { + resp.ERROR(c, "暂无数据") + return + } + for _, item := range items { + adminRoleVo := vo.AdminRole{} + err := utils.CopyObject(item, &adminRoleVo) + if err == nil { + var permissions []permission + h.db.Raw("SELECT p.id,p.name "+ + "FROM chatgpt_admin_role_permissions as rp "+ + "LEFT JOIN chatgpt_admin_permissions as p ON rp.permission_id = p.id "+ + "WHERE rp.role_id = ?", item.Id).Scan(&permissions) + adminRoleVo.Permissions = permissions + adminRoleVo.CreatedAt = item.CreatedAt.Format("2006-01-02 15:04:05") + data = append(data, adminRoleVo) + } + } + resp.SUCCESS(c, data) +} + +func (h *SysRoleHandler) Save(c *gin.Context) { + var data struct { + Id int + Name string + Description string + Permissions []int + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + var role = model.AdminRole{} + var res *gorm.DB + tx := h.db.Begin() + if data.Id > 0 { // 更新 + role.Id = data.Id + //删除角色对应的权限 + err := tx.Where("role_id = ?", role.Id).Delete(model.AdminRolePermission{}) + if err.Error != nil { + tx.Rollback() + resp.ERROR(c, "更新数据库失败") + return + } + //更新角色名 + res = tx.Model(&role).Updates(map[string]interface{}{ + "name": data.Name, + "description": data.Description, + }) + } else { + //新建角色 + role.Name = data.Name + role.Description = data.Description + res = tx.Create(&role) + } + + if res.Error != nil { + tx.Rollback() + resp.ERROR(c, "更新数据库失败") + return + } + + rp := make([]model.AdminRolePermission, 0) + if len(data.Permissions) > 0 { + for _, per := range data.Permissions { + rp = append(rp, model.AdminRolePermission{ + RoleId: role.Id, + PermissionId: per, + }) + } + res2 := tx.CreateInBatches(rp, len(rp)) + if res2.Error != nil { + tx.Rollback() + resp.ERROR(c, "更新数据库失败") + return + } + } + + tx.Commit() + + resp.SUCCESS(c) +} + +func (h *SysRoleHandler) Remove(c *gin.Context) { + var data struct { + Id int + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + if data.Id > 0 { + tx := h.db.Begin() + res := tx.Where("id = ?", data.Id).Delete(&model.AdminRole{}) + if res.Error != nil { + tx.Rollback() + resp.ERROR(c, "删除失败") + return + } + res = tx.Where("role_id = ?", data.Id).Delete(&model.AdminRolePermission{}) + if res.Error != nil { + tx.Rollback() + resp.ERROR(c, "删除失败") + return + } + tx.Commit() + } + resp.SUCCESS(c) +} diff --git a/api/handler/admin/admin_user_handler.go b/api/handler/admin/admin_user_handler.go index 4a951ec3..2a50c203 100644 --- a/api/handler/admin/admin_user_handler.go +++ b/api/handler/admin/admin_user_handler.go @@ -23,6 +23,11 @@ func NewSysUserHandler(app *core.AppServer, db *gorm.DB) *SysUserHandler { return &h } +type role struct { + Id int `json:"id"` + Name string `json:"name"` +} + // List 用户列表 func (h *SysUserHandler) List(c *gin.Context) { page := h.GetInt(c, "page", 1) @@ -48,9 +53,16 @@ func (h *SysUserHandler) List(c *gin.Context) { var userVo vo.AdminUser err := utils.CopyObject(item, &userVo) if err == nil { + var roles []role + h.db.Raw("SELECT r.id,r.name "+ + "FROM chatgpt_admin_user_roles as ur "+ + "LEFT JOIN chatgpt_admin_roles as r ON ur.role_id = r.id "+ + "WHERE ur.admin_id = ?", item.Id).Scan(&roles) + userVo.Id = item.Id userVo.CreatedAt = item.CreatedAt.Unix() userVo.UpdatedAt = item.UpdatedAt.Unix() + userVo.RoleIds = roles users = append(users, userVo) } else { logger.Error(err) @@ -68,6 +80,7 @@ func (h *SysUserHandler) Save(c *gin.Context) { Password string `json:"password"` Username string `json:"username"` Status bool `json:"status"` + RoleIds []int `json:"role_ids"` } if err := c.ShouldBindJSON(&data); err != nil { resp.ERROR(c, types.InvalidArgs) @@ -83,31 +96,58 @@ func (h *SysUserHandler) Save(c *gin.Context) { var user = model.AdminUser{} var res *gorm.DB var userVo vo.AdminUser + + tx := h.db.Begin() + if data.Id > 0 { // 更新 user.Id = data.Id + err := tx.Where("admin_id = ?", user.Id).Delete(&model.AdminUserRole{}) + if err.Error != nil { + tx.Rollback() + resp.ERROR(c, "更新数据库失败") + return + } // 此处需要用 map 更新,用结构体无法更新 0 值 - res = h.db.Model(&user).Updates(map[string]interface{}{ + res = tx.Model(&user).Updates(map[string]interface{}{ "username": data.Username, "status": data.Status, }) } else { salt := utils.RandString(8) - u := model.AdminUser{ - Username: data.Username, - Password: utils.GenPassword(data.Password, salt), - Salt: salt, - Status: true, - } - res = h.db.Create(&u) - _ = utils.CopyObject(u, &userVo) - userVo.Id = u.Id - userVo.CreatedAt = u.CreatedAt.Unix() - userVo.UpdatedAt = u.UpdatedAt.Unix() + + user.Username = data.Username + user.Password = utils.GenPassword(data.Password, salt) + user.Salt = salt + user.Status = true + + res = tx.Create(&user) + _ = utils.CopyObject(user, &userVo) + userVo.Id = user.Id + userVo.CreatedAt = user.CreatedAt.Unix() + userVo.UpdatedAt = user.UpdatedAt.Unix() } if res.Error != nil { + tx.Rollback() resp.ERROR(c, "更新数据库失败") return } + // 添加角色 + userRole := make([]model.AdminUserRole, 0) + if len(data.RoleIds) > 0 { + for _, roleId := range data.RoleIds { + userRole = append(userRole, model.AdminUserRole{ + AdminId: user.Id, + RoleId: roleId, + }) + } + err := tx.CreateInBatches(userRole, len(userRole)) + if err.Error != nil { + tx.Rollback() + resp.ERROR(c, "更新数据库失败") + return + } + } + tx.Commit() resp.SUCCESS(c, userVo) } @@ -155,11 +195,20 @@ func (h *SysUserHandler) Remove(c *gin.Context) { return } if data.Id > 0 { - res := h.db.Where("id = ?", data.Id).Delete(&model.AdminUser{}) + tx := h.db.Begin() + res := tx.Where("id = ?", data.Id).Delete(&model.AdminUser{}) if res.Error != nil { + tx.Rollback() resp.ERROR(c, "删除失败") return } + res2 := tx.Where("admin_id = ?", data.Id).Delete(&model.AdminUserRole{}) + if res2.Error != nil { + tx.Rollback() + resp.ERROR(c, "删除失败") + return + } + tx.Commit() } resp.SUCCESS(c) } diff --git a/api/handler/admin/api_key_handler.go b/api/handler/admin/api_key_handler.go index 63a147f3..e4b65d2d 100644 --- a/api/handler/admin/api_key_handler.go +++ b/api/handler/admin/api_key_handler.go @@ -109,10 +109,15 @@ func (h *ApiKeyHandler) Set(c *gin.Context) { } func (h *ApiKeyHandler) Remove(c *gin.Context) { - id := h.GetInt(c, "id", 0) - - if id > 0 { - res := h.db.Where("id = ?", id).Delete(&model.ApiKey{}) + var data struct { + Id uint + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + if data.Id > 0 { + res := h.db.Where("id = ?", data.Id).Delete(&model.ApiKey{}) if res.Error != nil { resp.ERROR(c, "更新数据库失败!") return diff --git a/api/handler/admin/chat_model_handler.go b/api/handler/admin/chat_model_handler.go index 2a9e41b5..1fb9fdc7 100644 --- a/api/handler/admin/chat_model_handler.go +++ b/api/handler/admin/chat_model_handler.go @@ -140,10 +140,15 @@ func (h *ChatModelHandler) Sort(c *gin.Context) { } func (h *ChatModelHandler) Remove(c *gin.Context) { - id := h.GetInt(c, "id", 0) - - if id > 0 { - res := h.db.Where("id = ?", id).Delete(&model.ChatModel{}) + var data struct { + Id uint + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + if data.Id > 0 { + res := h.db.Where("id = ?", data.Id).Delete(&model.ChatModel{}) if res.Error != nil { resp.ERROR(c, "更新数据库失败!") return diff --git a/api/handler/admin/chat_role_handler.go b/api/handler/admin/chat_role_handler.go index f7c5ba1a..233d8434 100644 --- a/api/handler/admin/chat_role_handler.go +++ b/api/handler/admin/chat_role_handler.go @@ -119,13 +119,18 @@ func (h *ChatRoleHandler) Set(c *gin.Context) { } func (h *ChatRoleHandler) Remove(c *gin.Context) { - id := h.GetInt(c, "id", 0) - if id <= 0 { + var data struct { + Id uint + } + if err := c.ShouldBindJSON(&data); err != nil { resp.ERROR(c, types.InvalidArgs) return } - - res := h.db.Where("id = ?", id).Delete(&model.ChatRole{}) + if data.Id <= 0 { + resp.ERROR(c, types.InvalidArgs) + return + } + res := h.db.Where("id = ?", data.Id).Delete(&model.ChatRole{}) if res.Error != nil { resp.ERROR(c, "删除失败!") return diff --git a/api/handler/admin/reward_handler.go b/api/handler/admin/reward_handler.go index def4cf87..a9d05bea 100644 --- a/api/handler/admin/reward_handler.go +++ b/api/handler/admin/reward_handler.go @@ -2,6 +2,7 @@ package admin import ( "chatplus/core" + "chatplus/core/types" "chatplus/handler" "chatplus/store/model" "chatplus/store/vo" @@ -57,10 +58,15 @@ func (h *RewardHandler) List(c *gin.Context) { } func (h *RewardHandler) Remove(c *gin.Context) { - id := h.GetInt(c, "id", 0) - - if id > 0 { - res := h.db.Where("id = ?", id).Delete(&model.Reward{}) + var data struct { + Id uint + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + if data.Id > 0 { + res := h.db.Where("id = ?", data.Id).Delete(&model.Reward{}) if res.Error != nil { resp.ERROR(c, "更新数据库失败!") return diff --git a/api/handler/admin/user_handler.go b/api/handler/admin/user_handler.go index 1366bff4..b84fdf54 100644 --- a/api/handler/admin/user_handler.go +++ b/api/handler/admin/user_handler.go @@ -154,30 +154,36 @@ func (h *UserHandler) ResetPass(c *gin.Context) { } func (h *UserHandler) Remove(c *gin.Context) { - id := h.GetInt(c, "id", 0) - if id > 0 { + var data struct { + Id uint + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + if data.Id > 0 { tx := h.db.Begin() - res := h.db.Where("id = ?", id).Delete(&model.User{}) + res := h.db.Where("id = ?", data.Id).Delete(&model.User{}) if res.Error != nil { resp.ERROR(c, "删除失败") return } // 删除聊天记录 - res = h.db.Where("user_id = ?", id).Delete(&model.ChatItem{}) + res = h.db.Where("user_id = ?", data.Id).Delete(&model.ChatItem{}) if res.Error != nil { tx.Rollback() resp.ERROR(c, "删除失败") return } // 删除聊天历史记录 - res = h.db.Where("user_id = ?", id).Delete(&model.ChatMessage{}) + res = h.db.Where("user_id = ?", data.Id).Delete(&model.ChatMessage{}) if res.Error != nil { tx.Rollback() resp.ERROR(c, "删除失败") return } // 删除登录日志 - res = h.db.Where("user_id = ?", id).Delete(&model.UserLoginLog{}) + res = h.db.Where("user_id = ?", data.Id).Delete(&model.UserLoginLog{}) if res.Error != nil { tx.Rollback() resp.ERROR(c, "删除失败") diff --git a/api/main.go b/api/main.go index 0bf3f6e0..7e628c94 100644 --- a/api/main.go +++ b/api/main.go @@ -273,13 +273,13 @@ func main() { group.POST("save", h.Save) group.GET("list", h.List) group.POST("set", h.Set) - group.GET("remove", h.Remove) + group.POST("remove", h.Remove) }), fx.Invoke(func(s *core.AppServer, h *admin.UserHandler) { group := s.Engine.Group("/api/admin/user/") group.GET("list", h.List) group.POST("save", h.Save) - group.GET("remove", h.Remove) + group.POST("remove", h.Remove) group.GET("loginLog", h.LoginLog) group.POST("resetPass", h.ResetPass) }), @@ -289,12 +289,12 @@ func main() { group.POST("save", h.Save) group.POST("sort", h.Sort) group.POST("set", h.Set) - group.GET("remove", h.Remove) + group.POST("remove", h.Remove) }), fx.Invoke(func(s *core.AppServer, h *admin.RewardHandler) { group := s.Engine.Group("/api/admin/reward/") group.GET("list", h.List) - group.GET("remove", h.Remove) + group.POST("remove", h.Remove) }), fx.Invoke(func(s *core.AppServer, h *admin.DashboardHandler) { group := s.Engine.Group("/api/admin/dashboard/") @@ -310,7 +310,7 @@ func main() { group.GET("list", h.List) group.POST("set", h.Set) group.POST("sort", h.Sort) - group.GET("remove", h.Remove) + group.POST("remove", h.Remove) }), fx.Invoke(func(s *core.AppServer, h *handler.PaymentHandler) { group := s.Engine.Group("/api/payment/") @@ -392,6 +392,22 @@ func main() { group.POST("remove", h.Remove) group.POST("resetPass", h.ResetPass) }), + // 权限 + fx.Provide(admin.NewSysPermissionHandler), + fx.Invoke(func(s *core.AppServer, h *admin.SysPermissionHandler) { + group := s.Engine.Group("/api/admin/sysPermission/") + group.GET("list", h.List) + group.POST("save", h.Save) + group.POST("remove", h.Remove) + }), + // 角色 + fx.Provide(admin.NewSysRoleHandler), + fx.Invoke(func(s *core.AppServer, h *admin.SysRoleHandler) { + group := s.Engine.Group("/api/admin/sysRole/") + group.GET("list", h.List) + group.POST("save", h.Save) + group.POST("remove", h.Remove) + }), fx.Provide(handler.NewFunctionHandler), fx.Invoke(func(s *core.AppServer, h *handler.FunctionHandler) { diff --git a/api/store/model/admin_permission.go b/api/store/model/admin_permission.go new file mode 100644 index 00000000..c535730b --- /dev/null +++ b/api/store/model/admin_permission.go @@ -0,0 +1,13 @@ +package model + +import "time" + +type AdminPermission struct { + Id int `gorm:"primarykey;column:id"` + Name string + Slug string + Sort int + Pid int + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/api/store/model/admin_role.go b/api/store/model/admin_role.go new file mode 100644 index 00000000..ce633545 --- /dev/null +++ b/api/store/model/admin_role.go @@ -0,0 +1,11 @@ +package model + +import "time" + +type AdminRole struct { + Id int `gorm:"primarykey;column:id"` + Name string + Description string + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/api/store/model/admin_role_permission.go b/api/store/model/admin_role_permission.go new file mode 100644 index 00000000..a436e594 --- /dev/null +++ b/api/store/model/admin_role_permission.go @@ -0,0 +1,11 @@ +package model + +import "time" + +type AdminRolePermission struct { + Id int `gorm:"primarykey;column:id"` + RoleId int + PermissionId int + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/api/store/model/admin_user_role.go b/api/store/model/admin_user_role.go new file mode 100644 index 00000000..93b010a4 --- /dev/null +++ b/api/store/model/admin_user_role.go @@ -0,0 +1,11 @@ +package model + +import "time" + +type AdminUserRole struct { + Id int `gorm:"primarykey;column:id"` + AdminId uint + RoleId int + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/api/store/vo/admin_permission.go b/api/store/vo/admin_permission.go new file mode 100644 index 00000000..70dcf27b --- /dev/null +++ b/api/store/vo/admin_permission.go @@ -0,0 +1,10 @@ +package vo + +type AdminPermission struct { + Id int `json:"id"` + Name string `json:"name"` + Slug string `json:"slug"` + Sort int `json:"sort"` + Pid int `json:"pid"` + Children []AdminPermission `json:"children"` +} diff --git a/api/store/vo/admin_role.go b/api/store/vo/admin_role.go new file mode 100644 index 00000000..c378f701 --- /dev/null +++ b/api/store/vo/admin_role.go @@ -0,0 +1,9 @@ +package vo + +type AdminRole struct { + Id int `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Permissions interface{} `json:"permissions"` + CreatedAt string `json:"created_at"` +} diff --git a/api/store/vo/admin_user.go b/api/store/vo/admin_user.go index 70c4a9ea..24403be5 100644 --- a/api/store/vo/admin_user.go +++ b/api/store/vo/admin_user.go @@ -2,8 +2,9 @@ package vo type AdminUser struct { BaseVo - Username string `json:"username"` - Status bool `json:"status"` // 当前状态 - LastLoginAt int64 `json:"last_login_at"` // 最后登录时间 - LastLoginIp string `json:"last_login_ip"` // 最后登录 IP + Username string `json:"username"` + Status bool `json:"status"` // 当前状态 + LastLoginAt int64 `json:"last_login_at"` // 最后登录时间 + LastLoginIp string `json:"last_login_ip"` // 最后登录 IP + RoleIds interface{} `json:"role_ids"` //角色ids } diff --git a/deploy/conf/config.toml b/deploy/conf/config.toml index e3c9fa17..5f89d4c9 100644 --- a/deploy/conf/config.toml +++ b/deploy/conf/config.toml @@ -10,10 +10,6 @@ WeChatBot = false SecretKey = "azyehq3ivunjhbntz78isj00i4hz2mt9xtddysfucxakadq4qbfrt0b7q3lnvg80" # 注意:这个是 JWT Token 授权密钥,生产环境请务必更换 MaxAge = 86400 -[Manager] - Username = "admin" - Password = "admin123" # 如果是生产环境的话,这里管理员的密码记得修改 - [Redis] # redis 配置信息 Host = "localhost" Port = 6379