mirror of
https://gitee.com/goflyfox/gmanager.git
synced 2025-12-06 17:19:26 +08:00
feat(admin): 新增个人中心功能并优化用户相关逻辑
- 新增个人中心相关接口和页面,包括用户信息获取、修改密码、更新手机号等 - 重构用户相关逻辑,增加缓存支持,优化权限验证和菜单获取 - 更新数据库连接配置,调整表前缀和连接信息
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,7 +1,6 @@
|
||||
# Vue 项目忽略项
|
||||
node_modules
|
||||
.idea
|
||||
.vscode
|
||||
.dist
|
||||
.cache
|
||||
|
||||
@@ -19,4 +18,6 @@ go.sum
|
||||
*.tmp
|
||||
|
||||
# 本地配置文件
|
||||
**/config/config.yaml
|
||||
**/config/config.yaml
|
||||
**/attachment/**
|
||||
**/temp/**
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
type ConfigListReq struct {
|
||||
g.Meta `path:"/config/list" method:"POST" tags:"配置管理" summary:"配置列表"`
|
||||
g.Meta `path:"/config/list" method:"POST" perms:"admin:config:query" tags:"配置管理" summary:"配置列表"`
|
||||
Keywords string `json:"keywords" dc:"名称"`
|
||||
DataType int `json:"dataType" dc:"数据类型"`
|
||||
Enable int `json:"enable" dc:"是否启用"`
|
||||
@@ -20,14 +20,14 @@ type ConfigListRes struct {
|
||||
}
|
||||
|
||||
type ConfigGetReq struct {
|
||||
g.Meta `path:"/config/get/:id" method:"get" tags:"配置管理" summary:"配置获取"`
|
||||
g.Meta `path:"/config/get/:id" method:"get" perms:"admin:config:query" tags:"配置管理" summary:"配置获取"`
|
||||
Id int64 `json:"id" dc:"ID"`
|
||||
}
|
||||
|
||||
type ConfigGetRes = entity.Config
|
||||
|
||||
type ConfigSaveReq struct {
|
||||
g.Meta `path:"/config/save/:id" method:"post" tags:"配置管理" summary:"配置保存"`
|
||||
g.Meta `path:"/config/save/:id" method:"post" perms:"admin:config:save" tags:"配置管理" summary:"配置保存"`
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name" dc:"配置名称" v:"required#配置名称不能为空"`
|
||||
Key string `json:"key" dc:"配置键" v:"required#键不能为空"`
|
||||
@@ -46,7 +46,7 @@ type ConfigSaveRes struct {
|
||||
}
|
||||
|
||||
type ConfigDeleteReq struct {
|
||||
g.Meta `path:"/config/delete/:ids" method:"post" tags:"配置管理" summary:"配置删除"`
|
||||
g.Meta `path:"/config/delete/:ids" method:"post" perms:"admin:config:delete" tags:"配置管理" summary:"配置删除"`
|
||||
Ids string `json:"ids" dc:"删除id列表"`
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
type DeptListReq struct {
|
||||
g.Meta `path:"/dept/list" method:"POST" tags:"部门管理" summary:"部门列表"`
|
||||
g.Meta `path:"/dept/list" method:"POST" perms:"admin:dept:query" tags:"部门管理" summary:"部门列表"`
|
||||
Keywords string `json:"keywords" dc:"名称"`
|
||||
Code string `json:"code" dc:"部门编码"`
|
||||
Enable int `json:"enable" dc:"是否启用"`
|
||||
@@ -27,14 +27,14 @@ type DeptOptionsReq struct {
|
||||
type DeptOptionsRes = []*input2.OptionVal
|
||||
|
||||
type DeptGetReq struct {
|
||||
g.Meta `path:"/dept/get/:id" method:"get" tags:"部门管理" summary:"部门获取"`
|
||||
g.Meta `path:"/dept/get/:id" method:"get" perms:"admin:dept:query" tags:"部门管理" summary:"部门获取"`
|
||||
Id int64 `json:"id" dc:"ID"`
|
||||
}
|
||||
|
||||
type DeptGetRes = entity.Dept
|
||||
|
||||
type DeptSaveReq struct {
|
||||
g.Meta `path:"/dept/save/:id" method:"post" tags:"部门管理" summary:"部门保存"`
|
||||
g.Meta `path:"/dept/save/:id" method:"post" perms:"admin:dept:save" tags:"部门管理" summary:"部门保存"`
|
||||
Id int64 `json:"id"`
|
||||
ParentId int64 `json:"parentId" v:"required#父级不能为空"`
|
||||
Name string `json:"name" dc:"部门名称" v:"required#部门名称不能为空"`
|
||||
@@ -50,7 +50,7 @@ type DeptSaveRes struct {
|
||||
}
|
||||
|
||||
type DeptDeleteReq struct {
|
||||
g.Meta `path:"/dept/delete/:ids" method:"post" tags:"部门管理" summary:"部门删除"`
|
||||
g.Meta `path:"/dept/delete/:ids" method:"post" perms:"admin:dept:delete" tags:"部门管理" summary:"部门删除"`
|
||||
Ids string `json:"ids" dc:"删除id列表"`
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
type MenuListReq struct {
|
||||
g.Meta `path:"/menu/list" method:"post" tags:"菜单管理" summary:"菜单列表"`
|
||||
g.Meta `path:"/menu/list" method:"post" perms:"admin:menu:query" tags:"菜单管理" summary:"菜单列表"`
|
||||
Name string `json:"name" dc:"菜单名称"`
|
||||
Enable int `json:"enable" dc:"是否启用"`
|
||||
input2.PageReq
|
||||
@@ -26,14 +26,14 @@ type MenuOptionsReq struct {
|
||||
type MenuOptionsRes = []*input2.OptionVal
|
||||
|
||||
type MenuGetReq struct {
|
||||
g.Meta `path:"/menu/get/:id" method:"get" tags:"菜单管理" summary:"菜单获取"`
|
||||
g.Meta `path:"/menu/get/:id" method:"get" perms:"admin:menu:query" tags:"菜单管理" summary:"菜单获取"`
|
||||
Id int `json:"id" dc:"ID"`
|
||||
}
|
||||
|
||||
type MenuGetRes = input2.Menu
|
||||
|
||||
type MenuSaveReq struct {
|
||||
g.Meta `path:"/menu/save/:id" method:"post" tags:"菜单管理" summary:"菜单保存"`
|
||||
g.Meta `path:"/menu/save/:id" method:"post" perms:"admin:menu:save" tags:"菜单管理" summary:"菜单保存"`
|
||||
Id int `json:"id"`
|
||||
ParentId int `json:"parentId" v:"required#父级不能为空"`
|
||||
Name string `json:"name" dc:"菜单名称" v:"required#菜单名称不能为空"`
|
||||
@@ -57,7 +57,7 @@ type MenuSaveRes struct {
|
||||
}
|
||||
|
||||
type MenuDeleteReq struct {
|
||||
g.Meta `path:"/menu/delete/:ids" method:"post" tags:"菜单管理" summary:"菜单删除"`
|
||||
g.Meta `path:"/menu/delete/:ids" method:"post" perms:"admin:menu:delete" tags:"菜单管理" summary:"菜单删除"`
|
||||
Ids []int `json:"ids" dc:"删除id列表"`
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
type RoleListReq struct {
|
||||
g.Meta `path:"/role/list" method:"post" tags:"角色管理" summary:"角色列表"`
|
||||
g.Meta `path:"/role/list" method:"post" perms:"admin:role:query" tags:"角色管理" summary:"角色列表"`
|
||||
Keywords string `json:"keywords" dc:"角色或编码名称"`
|
||||
Name string `json:"name" dc:"角色名称"`
|
||||
Enable int `json:"enable" dc:"是否启用"`
|
||||
@@ -27,7 +27,7 @@ type RoleOptionsReq struct {
|
||||
type RoleOptionsRes = []*input2.OptionVal
|
||||
|
||||
type RoleSaveReq struct {
|
||||
g.Meta `path:"/role/save/:id" method:"post" tags:"角色管理" summary:"角色保存"`
|
||||
g.Meta `path:"/role/save/:id" method:"post" perms:"admin:role:save" tags:"角色管理" summary:"角色保存"`
|
||||
Id int64 `json:"id"`
|
||||
Name string `json:"name" dc:"名称" v:"required#名称不能为空"`
|
||||
Code string `json:"code" dc:"编码"`
|
||||
@@ -41,14 +41,14 @@ type RoleSaveRes struct {
|
||||
}
|
||||
|
||||
type RoleGetReq struct {
|
||||
g.Meta `path:"/role/get/:id" method:"get" tags:"角色管理" summary:"角色获取"`
|
||||
g.Meta `path:"/role/get/:id" method:"get" perms:"admin:role:query" tags:"角色管理" summary:"角色获取"`
|
||||
Id int64 `json:"id" dc:"ID"`
|
||||
}
|
||||
|
||||
type RoleGetRes = entity.Role
|
||||
|
||||
type RoleDeleteReq struct {
|
||||
g.Meta `path:"/role/delete/:ids" method:"post" tags:"角色管理" summary:"角色删除"`
|
||||
g.Meta `path:"/role/delete/:ids" method:"post" perms:"admin:role:delete" tags:"角色管理" summary:"角色删除"`
|
||||
Ids string `json:"ids" dc:"删除id列表"`
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
input2 "gmanager/internal/admin/model/input"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
input2 "gmanager/internal/admin/model/input"
|
||||
)
|
||||
|
||||
type UserListReq struct {
|
||||
g.Meta `path:"/user/list" method:"post" tags:"用户管理" summary:"用户列表"`
|
||||
g.Meta `path:"/user/list" method:"post" perms:"admin:user:query" tags:"用户管理" summary:"用户列表"`
|
||||
Keywords string `json:"keywords" dc:"用户/手机号/昵称名称"`
|
||||
DeptId int64 `json:"deptId" dc:"部门"`
|
||||
Status int `json:"code" dc:"用户状态"`
|
||||
@@ -22,7 +23,7 @@ type UserListRes struct {
|
||||
}
|
||||
|
||||
type UserSaveReq struct {
|
||||
g.Meta `path:"/user/save/:id" method:"post" tags:"用户管理" summary:"用户保存"`
|
||||
g.Meta `path:"/user/save/:id" method:"post" perms:"admin:user:save" tags:"用户管理" summary:"用户保存"`
|
||||
Id int64 `json:"id"`
|
||||
DeptId int64 `json:"deptId" dc:"部门id" v:"required#部门不能为空"`
|
||||
UserName string `json:"userName" dc:"用户名称" v:"required#用户名称不能为空"`
|
||||
@@ -44,14 +45,14 @@ type UserSaveRes struct {
|
||||
}
|
||||
|
||||
type UserGetReq struct {
|
||||
g.Meta `path:"/user/get/:id" method:"get" tags:"用户管理" summary:"用户获取"`
|
||||
g.Meta `path:"/user/get/:id" method:"get" perms:"admin:user:query" tags:"用户管理" summary:"用户获取"`
|
||||
Id int64 `json:"id" dc:"ID"`
|
||||
}
|
||||
|
||||
type UserGetRes = input2.User
|
||||
|
||||
type UserDeleteReq struct {
|
||||
g.Meta `path:"/user/delete/:ids" method:"post" tags:"用户管理" summary:"用户删除"`
|
||||
g.Meta `path:"/user/delete/:ids" method:"post" perms:"admin:user:delete" tags:"用户管理" summary:"用户删除"`
|
||||
Ids string `json:"ids" dc:"删除id列表"`
|
||||
}
|
||||
|
||||
@@ -59,7 +60,7 @@ type UserDeleteRes struct {
|
||||
}
|
||||
|
||||
type UserPasswordResetReq struct {
|
||||
g.Meta `path:"/user/password/reset/:id" method:"post" tags:"用户管理" summary:"用户密码重置"`
|
||||
g.Meta `path:"/user/password/reset/:id" method:"post" perms:"admin:user:reset-password" tags:"用户管理" summary:"用户密码重置"`
|
||||
Id int64 `json:"id"`
|
||||
Password string `json:"password" dc:"密码" v:"required#密码不能为空"`
|
||||
}
|
||||
@@ -86,7 +87,7 @@ type UserMenusReq struct {
|
||||
type UserMenusRes = []*input2.UserMenu
|
||||
|
||||
type UserExportReq struct {
|
||||
g.Meta `path:"/user/export" method:"get" tags:"用户管理" summary:"用户数据导出"`
|
||||
g.Meta `path:"/user/export" method:"get" perms:"admin:user:export" tags:"用户管理" summary:"用户数据导出"`
|
||||
Keywords string `json:"keywords" dc:"用户/手机号/昵称名称"`
|
||||
DeptId int64 `json:"deptId" dc:"部门"`
|
||||
Status int `json:"code" dc:"用户状态"`
|
||||
@@ -99,19 +100,19 @@ type UserExportRes struct {
|
||||
}
|
||||
|
||||
type UserImportReq struct {
|
||||
g.Meta `path:"/user/import" method:"post" tags:"用户管理" summary:"批量导入用户"`
|
||||
File *ghttp.UploadFile `json:"file" type:"file" dc:"分片文件"`
|
||||
g.Meta `path:"/user/import" method:"post" perms:"admin:user:import" tags:"用户管理" summary:"批量导入用户"`
|
||||
File *ghttp.UploadFile `json:"file" type:"file" dc:"文件"`
|
||||
}
|
||||
|
||||
type UserImportRes struct {
|
||||
Code int `json:"code" dc:"状态码"` // 状态码
|
||||
InvalidCount int `json:"invalidCount" dc:"手机号"` // 无效数据条数
|
||||
ValidCount int `json:"validCount" dc:"手机号"` // 有效数据条数
|
||||
MessageList []string `json:"messageList" dc:"手机号"` // 错误信息
|
||||
Code int `json:"code" dc:"状态码"`
|
||||
InvalidCount int `json:"invalidCount" dc:"无效数据条数"`
|
||||
ValidCount int `json:"validCount" dc:"有效数据条数"`
|
||||
MessageList []string `json:"messageList" dc:"错误信息"`
|
||||
}
|
||||
|
||||
type UserTemplateReq struct {
|
||||
g.Meta `path:"/user/template" method:"get" tags:"用户管理" summary:"批量创建用户模版下载"`
|
||||
g.Meta `path:"/user/template" method:"get" perms:"admin:user:import" tags:"用户管理" summary:"批量创建用户模版下载"`
|
||||
}
|
||||
|
||||
type UserTemplateRes struct {
|
||||
|
||||
69
server/api/admin/v1/user_profile.go
Normal file
69
server/api/admin/v1/user_profile.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"gmanager/internal/admin/model/input"
|
||||
)
|
||||
|
||||
type UserProfileReq struct {
|
||||
g.Meta `path:"/user/profile" method:"get" tags:"个人中心" summary:"个人中心信息获取"`
|
||||
}
|
||||
|
||||
type UserProfileRes = input.UserProfile
|
||||
|
||||
type UserSaveProfileReq struct {
|
||||
g.Meta `path:"/user/saveProfile" method:"post" tags:"个人中心" summary:"个人中心信息保存"`
|
||||
Id int64 `json:"id"`
|
||||
NickName string `json:"nickName" dc:"昵称"`
|
||||
Mobile string `json:"mobile" dc:"手机号"`
|
||||
Email string `json:"email" dc:"邮箱"`
|
||||
Gender int `json:"gender" dc:"性别"`
|
||||
Address string `json:"address" dc:"地址"`
|
||||
Avatar string `json:"avatar" dc:"头像地址"`
|
||||
}
|
||||
|
||||
type UserSaveProfileRes struct {
|
||||
}
|
||||
|
||||
type UserChangePasswordReq struct {
|
||||
g.Meta `path:"/user/changePassword" method:"post" tags:"个人中心" summary:"个人中心修改密码"`
|
||||
OldPassword string `json:"oldPassword" dc:"原密码" v:"required#原密码不能为空"`
|
||||
NewPassword string `json:"newPassword" dc:"新密码" v:"required#新密码不能为空"`
|
||||
}
|
||||
|
||||
type UserChangePasswordRes struct {
|
||||
}
|
||||
|
||||
type UserSendMobileCodeReq struct {
|
||||
g.Meta `path:"/user/sendMobileCode" method:"post" tags:"个人中心" summary:"个人中心发送手机验证码"`
|
||||
Mobile string `json:"mobile" dc:"手机号" v:"required#手机号不能为空"`
|
||||
}
|
||||
|
||||
type UserSendMobileCodeRes struct {
|
||||
}
|
||||
|
||||
type UserSaveMobileReq struct {
|
||||
g.Meta `path:"/user/saveMobile" method:"post" tags:"个人中心" summary:"个人中心修改手机号"`
|
||||
Mobile string `json:"mobile" dc:"手机号" v:"required#手机号不能为空"`
|
||||
Code string `json:"code" dc:"验证码" v:"required#验证码不能为空"`
|
||||
}
|
||||
|
||||
type UserSaveMobileRes struct {
|
||||
}
|
||||
|
||||
type UserSendEmailCodeReq struct {
|
||||
g.Meta `path:"/user/sendEmailCode" method:"post" tags:"个人中心" summary:"个人中心发送Email验证码"`
|
||||
Email string `json:"mobile" dc:"邮箱" v:"required#邮箱不能为空"`
|
||||
}
|
||||
|
||||
type UserSendEmailCodeRes struct {
|
||||
}
|
||||
|
||||
type UserSaveEmailReq struct {
|
||||
g.Meta `path:"/user/saveEmail" method:"post" tags:"个人中心" summary:"个人中心修改Email"`
|
||||
Email string `json:"mobile" dc:"邮箱" v:"required#邮箱不能为空"`
|
||||
Code string `json:"code" dc:"验证码" v:"required#验证码不能为空"`
|
||||
}
|
||||
|
||||
type UserSaveEmailRes struct {
|
||||
}
|
||||
17
server/api/common/v1/upload.go
Normal file
17
server/api/common/v1/upload.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
// UploadFileReq 上传文件
|
||||
type UploadFileReq struct {
|
||||
g.Meta `path:"/upload/file" tags:"附件" method:"post" summary:"上传附件"`
|
||||
File *ghttp.UploadFile `json:"file" type:"file" dc:"文件"`
|
||||
}
|
||||
|
||||
type UploadFileRes struct {
|
||||
Url string `json:"url" dc:"URL"`
|
||||
Name string `json:"name" dc:"名称"`
|
||||
}
|
||||
@@ -5,7 +5,7 @@ go 1.23.0
|
||||
toolchain go1.24.0
|
||||
|
||||
require (
|
||||
github.com/goflyfox/gtoken/v2 v2.0.0
|
||||
github.com/goflyfox/gtoken/v2 v2.0.1
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.0
|
||||
github.com/gogf/gf/v2 v2.9.0
|
||||
github.com/mojocn/base64Captcha v1.3.8
|
||||
|
||||
@@ -6,9 +6,20 @@ gfcli:
|
||||
dao:
|
||||
- link: "mysql:root:123456@tcp(127.0.0.1:3306)/gmanager"
|
||||
descriptionTag: true
|
||||
path: "./internal/admin"
|
||||
removePrefix: "sys_"
|
||||
tables: "sys_dept,sys_config,sys_log,sys_menu,sys_role,sys_role_menu,sys_user,sys_user_role,sys_user_role_casbin"
|
||||
|
||||
build:
|
||||
name: "gmanager" # 编译后的可执行文件名称
|
||||
arch: "amd64" # 不填默认当前系统架构,可选:386,amd64,arm,all
|
||||
system: "linux" # 不填默认当前系统平台,可选:linux,darwin,windows,all
|
||||
packSrc: "resource" # 将resource目录打包进可执行文件,静态资源无需单独部署
|
||||
packDst: "internal/packed/packed.go" # 打包后生成的Go文件路径,一般使用相对路径指定到本项目目录中
|
||||
version: ""
|
||||
output: "./temp/gmanager" # 可执行文件生成路径
|
||||
extra: ""
|
||||
|
||||
docker:
|
||||
build: "-a amd64 -s linux -p temp -ew"
|
||||
tagPrefixes:
|
||||
|
||||
@@ -37,5 +37,8 @@ const (
|
||||
|
||||
// 缓存 cache
|
||||
const (
|
||||
CacheConfig = "config"
|
||||
CacheData = "data"
|
||||
CacheConfig = "config" // 配置缓存
|
||||
CacheUserPerm = "userPerm_%d" // 用户按钮权限缓存
|
||||
CacheUserMenu = "userMenu_%d" // 用户菜单权限缓存
|
||||
)
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
v1 "gmanager/api/admin/v1"
|
||||
"gmanager/internal/admin/consts"
|
||||
"gmanager/internal/admin/logic"
|
||||
"strings"
|
||||
)
|
||||
@@ -19,9 +18,6 @@ func (c *dept) List(ctx context.Context, req *v1.DeptListReq) (res *v1.DeptListR
|
||||
}
|
||||
|
||||
func (c *dept) Options(ctx context.Context, req *v1.DeptOptionsReq) (res *v1.DeptOptionsRes, err error) {
|
||||
if req != nil && req.Enable == 0 {
|
||||
req.Enable = consts.EnableYes
|
||||
}
|
||||
res, err = logic.Dept.Options(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
v1 "gmanager/api/admin/v1"
|
||||
"gmanager/internal/admin/logic"
|
||||
@@ -46,13 +47,13 @@ func (c *user) PasswordReset(ctx context.Context, req *v1.UserPasswordResetReq)
|
||||
|
||||
// UserInfo 获取用户信息接口
|
||||
func (c *user) UserInfo(ctx context.Context, req *v1.UserInfoReq) (res *v1.UserInfoRes, err error) {
|
||||
res, err = logic.Login.UserInfo(ctx, req)
|
||||
res, err = logic.User.UserInfo(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// UserMenus 获取用户菜单接口
|
||||
func (c *user) UserMenus(ctx context.Context, req *v1.UserMenusReq) (res *v1.UserMenusRes, err error) {
|
||||
res, err = logic.Login.UserMenus(ctx, req)
|
||||
res, err = logic.User.UserMenus(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -70,3 +71,41 @@ func (c *user) Template(ctx context.Context, req *v1.UserTemplateReq) (res *v1.U
|
||||
err = logic.User.Template(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *user) Profile(ctx context.Context, req *v1.UserProfileReq) (res *v1.UserProfileRes, err error) {
|
||||
res, err = logic.User.Profile(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *user) SaveProfile(ctx context.Context, req *v1.UserSaveProfileReq) (res *v1.UserSaveProfileRes, err error) {
|
||||
err = logic.User.SaveProfile(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *user) ChangePassword(ctx context.Context, req *v1.UserChangePasswordReq) (res *v1.UserChangePasswordRes, err error) {
|
||||
if req.OldPassword == req.NewPassword {
|
||||
return nil, gerror.New("原密码和新密码一致")
|
||||
}
|
||||
err = logic.User.ChangePassword(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *user) SendMobileCode(ctx context.Context, req *v1.UserSendMobileCodeReq) (res *v1.UserSendMobileCodeRes, err error) {
|
||||
err = logic.User.SendMobileCode(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *user) SaveMobile(ctx context.Context, req *v1.UserSaveMobileReq) (res *v1.UserSaveMobileRes, err error) {
|
||||
err = logic.User.SaveMobile(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *user) SendEmailCode(ctx context.Context, req *v1.UserSendEmailCodeReq) (res *v1.UserSendEmailCodeRes, err error) {
|
||||
err = logic.User.SendEmailCode(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *user) SaveEmail(ctx context.Context, req *v1.UserSaveEmailReq) (res *v1.UserSaveEmailRes, err error) {
|
||||
err = logic.User.SaveEmail(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ func (s *log) SaveLog(ctx context.Context, input *input.LogData) error {
|
||||
logMeta.UpdateAt = gtime.Now()
|
||||
}
|
||||
if session != nil {
|
||||
operator = session.Username
|
||||
operator = session.UserName
|
||||
}
|
||||
if input.PkVal > 0 {
|
||||
logMeta.Id = input.PkVal
|
||||
|
||||
@@ -63,7 +63,8 @@ func (s *login) Login(ctx context.Context, req *v1.LoginReq) (res *v1.LoginRes,
|
||||
Id: model.Id,
|
||||
Uuid: model.Uuid,
|
||||
NickName: model.NickName,
|
||||
Username: model.UserName,
|
||||
UserName: model.UserName,
|
||||
UserType: model.UserType,
|
||||
}
|
||||
// 认证成功调用Generate生成Token
|
||||
res.AccessToken, err = gftoken.GToken.Generate(ctx, req.Username, sessionUser)
|
||||
@@ -113,39 +114,6 @@ func (s *login) Logout(ctx context.Context, req *v1.LogoutReq) (res *v1.LogoutRe
|
||||
return
|
||||
}
|
||||
|
||||
// GetTree 菜单树形菜单
|
||||
func (s *login) GetTree(pid int64, list []*entity2.Menu) (tree []*input2.UserMenu) {
|
||||
tree = make([]*input2.UserMenu, 0, len(list))
|
||||
for _, v := range list {
|
||||
if v.ParentId == pid {
|
||||
name := v.RouteName
|
||||
if name == "" {
|
||||
name = v.RoutePath
|
||||
}
|
||||
t := &input2.UserMenu{
|
||||
Id: v.Id,
|
||||
Name: name,
|
||||
Component: v.Component,
|
||||
Path: v.RoutePath,
|
||||
Redirect: v.Redirect,
|
||||
Meta: input2.Meta{
|
||||
AlwaysShow: v.AlwaysShow == 1,
|
||||
Hidden: v.Enable != 1,
|
||||
Icon: v.Icon,
|
||||
KeepAlive: v.KeepAlive == 1,
|
||||
Title: v.Name,
|
||||
},
|
||||
}
|
||||
child := s.GetTree(v.Id, list)
|
||||
if len(child) > 0 {
|
||||
t.Children = child
|
||||
}
|
||||
tree = append(tree, t)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *login) CaptchaGet(ctx context.Context, req *v1.CaptchaReq) (res *v1.CaptchaRes, err error) {
|
||||
res = &v1.CaptchaRes{}
|
||||
res.CodeId, res.Img, err = captcha.Generate(ctx)
|
||||
|
||||
@@ -2,21 +2,25 @@ package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"gmanager/api/admin/v1"
|
||||
"gmanager/internal/admin/consts"
|
||||
"gmanager/internal/admin/dao"
|
||||
"gmanager/internal/admin/model/do"
|
||||
"gmanager/internal/admin/model/entity"
|
||||
"gmanager/internal/admin/model/input"
|
||||
"gmanager/internal/library/cache"
|
||||
"gmanager/internal/library/gftoken"
|
||||
|
||||
"github.com/gogf/gf/v2/crypto/gmd5"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
v1 "gmanager/api/admin/v1"
|
||||
"gmanager/internal/admin/consts"
|
||||
dao2 "gmanager/internal/admin/dao"
|
||||
"gmanager/internal/admin/model/do"
|
||||
entity2 "gmanager/internal/admin/model/entity"
|
||||
input2 "gmanager/internal/admin/model/input"
|
||||
"gmanager/internal/library/gftoken"
|
||||
)
|
||||
|
||||
// User 用户服务
|
||||
@@ -29,8 +33,8 @@ func (s *user) List(ctx context.Context, in *v1.UserListReq) (res *v1.UserListRe
|
||||
if in == nil {
|
||||
return
|
||||
}
|
||||
m := dao2.User.Ctx(ctx)
|
||||
columns := dao2.User.Columns()
|
||||
m := dao.User.Ctx(ctx)
|
||||
columns := dao.User.Columns()
|
||||
res = &v1.UserListRes{}
|
||||
|
||||
// where条件
|
||||
@@ -67,7 +71,7 @@ func (s *user) List(ctx context.Context, in *v1.UserListReq) (res *v1.UserListRe
|
||||
} else {
|
||||
m = m.Order("id desc")
|
||||
}
|
||||
var pageList []*input2.User
|
||||
var pageList []*input.User
|
||||
if err = m.Page(in.PageNum, in.PageSize).Scan(&pageList); err != nil {
|
||||
err = gerror.Wrap(err, "获取数据失败!")
|
||||
}
|
||||
@@ -87,11 +91,11 @@ func (s *user) List(ctx context.Context, in *v1.UserListReq) (res *v1.UserListRe
|
||||
|
||||
// Get 获取用户详情
|
||||
func (s *user) Get(ctx context.Context, id int64) (res *v1.UserGetRes, err error) {
|
||||
err = dao2.User.Ctx(ctx).Where(dao2.User.Columns().Id, id).Scan(&res)
|
||||
err = dao.User.Ctx(ctx).Where(dao.User.Columns().Id, id).Scan(&res)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
values, err := dao2.UserRole.Ctx(ctx).Fields(dao2.UserRole.Columns().RoleId).Where(dao2.UserRole.Columns().UserId, id).Array()
|
||||
values, err := dao.UserRole.Ctx(ctx).Fields(dao.UserRole.Columns().RoleId).Where(dao.UserRole.Columns().UserId, id).Array()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -107,8 +111,8 @@ func (s *user) Save(ctx context.Context, in *v1.UserSaveReq) error {
|
||||
return gerror.Wrap(err, "数据转换错误")
|
||||
}
|
||||
|
||||
m := dao2.User.Ctx(ctx)
|
||||
columns := dao2.User.Columns()
|
||||
m := dao.User.Ctx(ctx)
|
||||
columns := dao.User.Columns()
|
||||
|
||||
// 用户名唯一性校验
|
||||
nameCount, err := m.Where(columns.UserName, model.UserName).
|
||||
@@ -130,7 +134,7 @@ func (s *user) Save(ctx context.Context, in *v1.UserSaveReq) error {
|
||||
}
|
||||
_ = Log.Save(ctx, model, consts.UPDATE)
|
||||
// 删除历史角色
|
||||
_, err = dao2.UserRole.Ctx(ctx).Where(dao2.UserRole.Columns().UserId, model.Id).Delete()
|
||||
_, err = dao.UserRole.Ctx(ctx).Where(dao.UserRole.Columns().UserId, model.Id).Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -156,7 +160,7 @@ func (s *user) Save(ctx context.Context, in *v1.UserSaveReq) error {
|
||||
return err
|
||||
}
|
||||
model.Id = modelId
|
||||
_ = Log.SaveLog(ctx, &input2.LogData{
|
||||
_ = Log.SaveLog(ctx, &input.LogData{
|
||||
Model: model,
|
||||
OperType: consts.INSERT,
|
||||
OperRemark: "角色ID:" + gconv.String(in.RoleIds),
|
||||
@@ -167,24 +171,24 @@ func (s *user) Save(ctx context.Context, in *v1.UserSaveReq) error {
|
||||
userRoleList := g.List{}
|
||||
for _, roleId := range in.RoleIds {
|
||||
userRoleList = append(userRoleList,
|
||||
g.Map{dao2.UserRole.Columns().UserId: model.Id,
|
||||
dao2.UserRole.Columns().RoleId: roleId,
|
||||
g.Map{dao.UserRole.Columns().UserId: model.Id,
|
||||
dao.UserRole.Columns().RoleId: roleId,
|
||||
})
|
||||
}
|
||||
_, err = dao2.UserRole.Ctx(ctx).Insert(userRoleList)
|
||||
_, err = dao.UserRole.Ctx(ctx).Insert(userRoleList)
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete 删除用户
|
||||
func (s *user) Delete(ctx context.Context, ids []int) error {
|
||||
// 删除用户角色关联
|
||||
_, err := dao2.UserRole.Ctx(ctx).WhereIn(dao2.UserRole.Columns().UserId, ids).Delete()
|
||||
_, err := dao.UserRole.Ctx(ctx).WhereIn(dao.UserRole.Columns().UserId, ids).Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
_, err = dao2.User.Ctx(ctx).WhereIn(dao2.User.Columns().Id, ids).Delete()
|
||||
_, err = dao.User.Ctx(ctx).WhereIn(dao.User.Columns().Id, ids).Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -209,8 +213,8 @@ func (s *user) PasswordReset(ctx context.Context, in *v1.UserPasswordResetReq) e
|
||||
return err
|
||||
}
|
||||
userId := gftoken.GetSessionUser(ctx).Id
|
||||
columns := dao2.User.Columns()
|
||||
_, err = dao2.User.Ctx(ctx).Where(columns.Id, in.Id).Update(do.User{
|
||||
columns := dao.User.Columns()
|
||||
_, err = dao.User.Ctx(ctx).Where(columns.Id, in.Id).Update(do.User{
|
||||
UpdateId: userId,
|
||||
UpdateAt: gtime.Now(),
|
||||
Password: password,
|
||||
@@ -219,9 +223,9 @@ func (s *user) PasswordReset(ctx context.Context, in *v1.UserPasswordResetReq) e
|
||||
}
|
||||
|
||||
// UserInfo 获取用户信息接口
|
||||
func (s *login) UserInfo(ctx context.Context, req *v1.UserInfoReq) (res *v1.UserInfoRes, err error) {
|
||||
var model *entity2.User
|
||||
err = dao2.User.Ctx(ctx).Where(dao2.User.Columns().UserName, gftoken.GetUserKey(ctx)).Scan(&model)
|
||||
func (s *user) UserInfo(ctx context.Context, req *v1.UserInfoReq) (res *v1.UserInfoRes, err error) {
|
||||
var model *entity.User
|
||||
err = dao.User.Ctx(ctx).Where(dao.User.Columns().UserName, gftoken.GetUserKey(ctx)).Scan(&model)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -229,11 +233,11 @@ func (s *login) UserInfo(ctx context.Context, req *v1.UserInfoReq) (res *v1.User
|
||||
err = gerror.NewCode(gcode.CodeValidationFailed, "用户名信息获取失败!")
|
||||
return
|
||||
}
|
||||
perms, err := getUserPerms(ctx, model.Id, model.UserType)
|
||||
perms, err := getUserPerms(ctx, false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
roleNames, err := getUserRoleNames(ctx, model.Id, model.UserType)
|
||||
roleNames, err := getUserRoleNames(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -250,35 +254,65 @@ func (s *login) UserInfo(ctx context.Context, req *v1.UserInfoReq) (res *v1.User
|
||||
}
|
||||
|
||||
// UserMenus 获取用户菜单接口
|
||||
func (s *login) UserMenus(ctx context.Context, in *v1.UserMenusReq) (res *v1.UserMenusRes, err error) {
|
||||
func (s *user) UserMenus(ctx context.Context, in *v1.UserMenusReq) (res *v1.UserMenusRes, err error) {
|
||||
var (
|
||||
model *entity2.User
|
||||
menus []*entity2.Menu
|
||||
columns = dao2.Menu.Columns()
|
||||
menus []*entity.Menu
|
||||
)
|
||||
res = &v1.UserMenusRes{}
|
||||
m := dao2.Menu.Ctx(ctx).Where(columns.Enable, consts.EnableYes).WhereNot(columns.Type, consts.MenuTypeButton)
|
||||
// 获取当前用户
|
||||
err = dao2.User.Ctx(ctx).Where(dao2.User.Columns().UserName, gftoken.GetUserKey(ctx)).Scan(&model)
|
||||
menus, err = getUserMenus(ctx, false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if model.UserType == consts.UserTypeAdmin {
|
||||
if len(menus) > 0 {
|
||||
tree := s.GetTree(0, menus)
|
||||
res = &tree
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/** getUserMenus 获取用户菜单
|
||||
* @param cacheFlag 是否缓存
|
||||
*/
|
||||
func getUserMenus(ctx context.Context, cacheFlag bool) (menus []*entity.Menu, err error) {
|
||||
var (
|
||||
columns = dao.Menu.Columns()
|
||||
)
|
||||
sessionUser := gftoken.GetSessionUser(ctx)
|
||||
if sessionUser == nil {
|
||||
return
|
||||
}
|
||||
if cacheFlag { // 使用缓存
|
||||
cacheMap, err2 := cache.Instance().Get(ctx, fmt.Sprintf(consts.CacheUserMenu, sessionUser.Id))
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
if len(cacheMap) > 0 {
|
||||
err = gconv.Struct(cacheMap[consts.CacheData], &menus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(menus) > 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m := dao.Menu.Ctx(ctx).Where(columns.Enable, consts.EnableYes).WhereNot(columns.Type, consts.MenuTypeButton)
|
||||
if sessionUser.UserType == consts.UserTypeAdmin {
|
||||
m = m.OrderAsc(columns.Sort)
|
||||
if err = m.Scan(&menus); err != nil {
|
||||
err = gerror.Wrap(err, "获取数据失败!")
|
||||
}
|
||||
} else {
|
||||
roleIds, err2 := dao2.UserRole.Ctx(ctx).Fields(dao2.UserRole.Columns().RoleId).
|
||||
Where(dao2.UserRole.Columns().UserId, model.Id).Array()
|
||||
roleIds, err2 := dao.UserRole.Ctx(ctx).Fields(dao.UserRole.Columns().RoleId).
|
||||
Where(dao.UserRole.Columns().UserId, sessionUser.Id).Array()
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
if len(roleIds) == 0 {
|
||||
return
|
||||
}
|
||||
menuIds, err2 := dao2.RoleMenu.Ctx(ctx).Fields(dao2.RoleMenu.Columns().MenuId).
|
||||
WhereIn(dao2.RoleMenu.Columns().RoleId, gconv.SliceInt64(roleIds)).Array()
|
||||
menuIds, err2 := dao.RoleMenu.Ctx(ctx).Fields(dao.RoleMenu.Columns().MenuId).
|
||||
WhereIn(dao.RoleMenu.Columns().RoleId, gconv.SliceInt64(roleIds)).Array()
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
@@ -288,56 +322,114 @@ func (s *login) UserMenus(ctx context.Context, in *v1.UserMenusReq) (res *v1.Use
|
||||
return nil, err2
|
||||
}
|
||||
}
|
||||
|
||||
// 设置缓存
|
||||
if len(menus) > 0 {
|
||||
tree := s.GetTree(0, menus)
|
||||
res = &tree
|
||||
err = cache.Instance().Set(ctx, fmt.Sprintf(consts.CacheUserMenu, sessionUser.Id), g.Map{consts.CacheData: menus})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetTree 菜单树形菜单
|
||||
func (s *user) GetTree(pid int64, list []*entity.Menu) (tree []*input.UserMenu) {
|
||||
tree = make([]*input.UserMenu, 0, len(list))
|
||||
for _, v := range list {
|
||||
if v.ParentId == pid {
|
||||
name := v.RouteName
|
||||
if name == "" {
|
||||
name = v.RoutePath
|
||||
}
|
||||
t := &input.UserMenu{
|
||||
Id: v.Id,
|
||||
Name: name,
|
||||
Component: v.Component,
|
||||
Path: v.RoutePath,
|
||||
Redirect: v.Redirect,
|
||||
Meta: input.Meta{
|
||||
AlwaysShow: v.AlwaysShow == 1,
|
||||
Hidden: v.Enable != 1,
|
||||
Icon: v.Icon,
|
||||
KeepAlive: v.KeepAlive == 1,
|
||||
Title: v.Name,
|
||||
},
|
||||
}
|
||||
child := s.GetTree(v.Id, list)
|
||||
if len(child) > 0 {
|
||||
t.Children = child
|
||||
}
|
||||
tree = append(tree, t)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getUserRoleNames 获取用户对应角色名称
|
||||
func getUserRoleNames(ctx context.Context, userId int64, userType int) (res []string, err error) {
|
||||
if userType == consts.UserTypeAdmin {
|
||||
func getUserRoleNames(ctx context.Context) (res []string, err error) {
|
||||
sessionUser := gftoken.GetSessionUser(ctx)
|
||||
if sessionUser == nil {
|
||||
return
|
||||
}
|
||||
if sessionUser.UserType == consts.UserTypeAdmin {
|
||||
res = append(res, consts.RoleAdmin)
|
||||
return
|
||||
}
|
||||
|
||||
values, err := dao2.UserRole.Ctx(ctx).Fields(dao2.UserRole.Columns().RoleId).Where(dao2.UserRole.Columns().UserId, userId).Array()
|
||||
values, err := dao.UserRole.Ctx(ctx).Fields(dao.UserRole.Columns().RoleId).Where(dao.UserRole.Columns().UserId, sessionUser.Id).Array()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var roles []*entity2.Role
|
||||
err = dao2.Role.Ctx(ctx).WhereIn(dao2.Role.Columns().Id, gconv.SliceInt64(values)).Scan(&roles)
|
||||
var roles []*entity.Role
|
||||
err = dao.Role.Ctx(ctx).WhereIn(dao.Role.Columns().Id, gconv.SliceInt64(values)).Scan(&roles)
|
||||
for _, e := range roles {
|
||||
res = append(res, e.Code)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getUserPerms 获取用户对应按钮权限
|
||||
func getUserPerms(ctx context.Context, userId int64, userType int) (res []string, err error) {
|
||||
/** getUserPerms 获取用户对应按钮权限
|
||||
* @param cacheFlag 是否使用缓存
|
||||
*/
|
||||
func getUserPerms(ctx context.Context, cacheFlag bool) (res []string, err error) {
|
||||
sessionUser := gftoken.GetSessionUser(ctx)
|
||||
if sessionUser == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if cacheFlag { // 使用缓存
|
||||
cacheMap, err2 := cache.Instance().Get(ctx, fmt.Sprintf(consts.CacheUserPerm, sessionUser.Id))
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
if len(cacheMap) > 0 {
|
||||
res = gconv.SliceStr(cacheMap[consts.CacheData])
|
||||
if len(res) > 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 管理员权限
|
||||
var menus []*entity2.Menu
|
||||
columns := dao2.Menu.Columns()
|
||||
m := dao2.Menu.Ctx(ctx).Where(columns.Enable, consts.EnableYes).Where(columns.Type, consts.MenuTypeButton)
|
||||
if userType == consts.UserTypeAdmin {
|
||||
var menus []*entity.Menu
|
||||
columns := dao.Menu.Columns()
|
||||
m := dao.Menu.Ctx(ctx).Where(columns.Enable, consts.EnableYes).Where(columns.Type, consts.MenuTypeButton)
|
||||
if sessionUser.UserType == consts.UserTypeAdmin {
|
||||
// 管理员获取所有按钮权限
|
||||
if err = m.Scan(&menus); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
roleIds, err2 := dao2.UserRole.Ctx(ctx).Fields(dao2.UserRole.Columns().RoleId).
|
||||
Where(dao2.UserRole.Columns().UserId, userId).Array()
|
||||
roleIds, err2 := dao.UserRole.Ctx(ctx).Fields(dao.UserRole.Columns().RoleId).
|
||||
Where(dao.UserRole.Columns().UserId, sessionUser.Id).Array()
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
if len(roleIds) == 0 {
|
||||
return
|
||||
}
|
||||
menuIds, err2 := dao2.RoleMenu.Ctx(ctx).Fields(dao2.RoleMenu.Columns().MenuId).
|
||||
WhereIn(dao2.RoleMenu.Columns().RoleId, gconv.SliceInt64(roleIds)).Array()
|
||||
menuIds, err2 := dao.RoleMenu.Ctx(ctx).Fields(dao.RoleMenu.Columns().MenuId).
|
||||
WhereIn(dao.RoleMenu.Columns().RoleId, gconv.SliceInt64(roleIds)).Array()
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
@@ -356,5 +448,45 @@ func getUserPerms(ctx context.Context, userId int64, userType int) (res []string
|
||||
}
|
||||
}
|
||||
}
|
||||
// 设置缓存
|
||||
if len(res) > 0 {
|
||||
err = cache.Instance().Set(ctx, fmt.Sprintf(consts.CacheUserPerm, sessionUser.Id), g.Map{consts.CacheData: res})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *user) CheckPerm(ctx context.Context, perm string) bool {
|
||||
perms, err := getUserPerms(ctx, true)
|
||||
if err != nil {
|
||||
g.Log().Info(ctx, err)
|
||||
return false
|
||||
}
|
||||
for _, userPerm := range perms {
|
||||
if userPerm == perm {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CheckUrl 菜单校验
|
||||
// TODO 只有用户菜单权限,但是用户菜单以来组织机构和角色???
|
||||
func (s *user) CheckUrl(ctx context.Context, path string) bool {
|
||||
menus, err := getUserMenus(ctx, true)
|
||||
if err != nil {
|
||||
g.Log().Info(ctx, err)
|
||||
return false
|
||||
}
|
||||
for _, e := range menus {
|
||||
if e.Type != consts.MenuTypeMenu {
|
||||
continue
|
||||
}
|
||||
if gstr.HasPrefix(path, "/admin/"+e.RoutePath) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
119
server/internal/admin/logic/user_profile.go
Normal file
119
server/internal/admin/logic/user_profile.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/crypto/gmd5"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
v1 "gmanager/api/admin/v1"
|
||||
"gmanager/internal/admin/consts"
|
||||
dao2 "gmanager/internal/admin/dao"
|
||||
"gmanager/internal/admin/model/do"
|
||||
"gmanager/internal/library/gftoken"
|
||||
)
|
||||
|
||||
func (s *user) Profile(ctx context.Context, req *v1.UserProfileReq) (res *v1.UserProfileRes, err error) {
|
||||
model, err := s.Get(ctx, gftoken.GetUserId(ctx))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if model == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
deptMap, err := Dept.DeptMap(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
val, err := dao2.Role.Ctx(ctx).Fields("GROUP_CONCAT(name)").WhereIn(dao2.Role.Columns().Id, model.RoleIds).Value()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = &v1.UserProfileRes{
|
||||
Id: model.Id,
|
||||
UserName: model.UserName,
|
||||
NickName: model.NickName,
|
||||
Mobile: model.Mobile,
|
||||
Avatar: model.Avatar,
|
||||
Email: model.Email,
|
||||
Gender: model.Gender,
|
||||
CreateAt: model.CreateAt,
|
||||
DeptName: deptMap[model.DeptId].Name,
|
||||
RoleNames: val.String(),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *user) SaveProfile(ctx context.Context, in *v1.UserSaveProfileReq) error {
|
||||
userId := gftoken.GetUserId(ctx)
|
||||
var model do.User
|
||||
err := gconv.Struct(in, &model)
|
||||
if err != nil {
|
||||
return gerror.Wrap(err, "数据转换错误")
|
||||
}
|
||||
|
||||
model.Id = userId
|
||||
model.UpdateId = userId
|
||||
model.UpdateAt = gtime.Now()
|
||||
|
||||
_, err = dao2.User.Ctx(ctx).OmitEmpty().Where(dao2.User.Columns().Id, model.Id).Update(model)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = Log.Save(ctx, model, consts.UPDATE)
|
||||
return err
|
||||
}
|
||||
|
||||
// ChangePassword 修改密码
|
||||
func (s *user) ChangePassword(ctx context.Context, in *v1.UserChangePasswordReq) error {
|
||||
userId := gftoken.GetUserId(ctx)
|
||||
res, err := s.Get(ctx, userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res == nil {
|
||||
return nil
|
||||
}
|
||||
oldPassword, err := gmd5.Encrypt(in.OldPassword + res.Salt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oldPassword != res.Password {
|
||||
return gerror.New("原密码错误")
|
||||
}
|
||||
|
||||
password, err := gmd5.Encrypt(in.NewPassword + res.Salt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
columns := dao2.User.Columns()
|
||||
_, err = dao2.User.Ctx(ctx).Where(columns.Id, userId).Update(do.User{
|
||||
UpdateId: userId,
|
||||
UpdateAt: gtime.Now(),
|
||||
Password: password,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *user) SendMobileCode(ctx context.Context, req *v1.UserSendMobileCodeReq) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *user) SaveMobile(ctx context.Context, req *v1.UserSaveMobileReq) error {
|
||||
// 验证码校验
|
||||
userId := gftoken.GetUserId(ctx)
|
||||
return s.SaveProfile(ctx, &v1.UserSaveProfileReq{Id: userId, Mobile: req.Mobile})
|
||||
}
|
||||
|
||||
func (s *user) SendEmailCode(ctx context.Context, req *v1.UserSendEmailCodeReq) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *user) SaveEmail(ctx context.Context, req *v1.UserSaveEmailReq) error {
|
||||
// 验证码校验
|
||||
userId := gftoken.GetUserId(ctx)
|
||||
return s.SaveProfile(ctx, &v1.UserSaveProfileReq{Id: userId, Email: req.Email})
|
||||
}
|
||||
23
server/internal/admin/middleware/demo.go
Normal file
23
server/internal/admin/middleware/demo.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
// DemoNotice 演示环境提示
|
||||
func DemoNotice(r *ghttp.Request) {
|
||||
r.Middleware.Next()
|
||||
|
||||
var (
|
||||
msg string
|
||||
err = r.GetError()
|
||||
)
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
if gstr.Contains(msg, "denied to user") {
|
||||
r.SetError(gerror.New("演示环境,禁止操作"))
|
||||
}
|
||||
}
|
||||
}
|
||||
50
server/internal/admin/middleware/user_auth.go
Normal file
50
server/internal/admin/middleware/user_auth.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"gmanager/internal/admin/consts"
|
||||
"gmanager/internal/admin/logic"
|
||||
"gmanager/internal/library/gftoken"
|
||||
)
|
||||
|
||||
// UserPerm 权限校验
|
||||
func UserPerm(r *ghttp.Request) {
|
||||
// 不认证URL
|
||||
if gftoken.HasExcludePath(r) {
|
||||
r.Middleware.Next()
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
user := gftoken.GetSessionUser(ctx)
|
||||
// 用户登陆校验部分需要处理
|
||||
if user == nil {
|
||||
r.Middleware.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 管理员不校验
|
||||
if user.UserType == consts.UserTypeAdmin {
|
||||
r.Middleware.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 按钮权限校验
|
||||
perms := r.GetServeHandler().GetMetaTag("perms")
|
||||
if perms != "" { // 需要权限校验
|
||||
permArray := gstr.Split(perms, ",")
|
||||
for _, perm := range permArray {
|
||||
if !logic.User.CheckPerm(ctx, perm) {
|
||||
r.Response.WriteJson(ghttp.DefaultHandlerResponse{
|
||||
Code: gcode.CodeSecurityReason.Code(),
|
||||
Message: "按钮权限不足",
|
||||
})
|
||||
r.ExitAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.Middleware.Next()
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package input
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"gmanager/internal/admin/model/entity"
|
||||
)
|
||||
|
||||
@@ -14,3 +15,16 @@ type User struct {
|
||||
DeptName string `json:"deptName"`
|
||||
RoleIds []int64 `json:"roleIds"`
|
||||
}
|
||||
|
||||
type UserProfile struct {
|
||||
Id int64 `json:"id" description:"主键"`
|
||||
UserName string `json:"userName" description:"登录名/11111"`
|
||||
Mobile string `json:"mobile" description:"手机号"`
|
||||
Email string `json:"email" description:"email"`
|
||||
NickName string `json:"nickName" description:"昵称"`
|
||||
Gender int `json:"gender" description:"性别;0:保密,1:男,2:女"`
|
||||
Avatar string `json:"avatar" description:"头像地址"`
|
||||
CreateAt *gtime.Time `json:"createAt" description:"创建时间"`
|
||||
DeptName string `json:"deptName" description:"部门"`
|
||||
RoleNames string `json:"roleNames" description:"角色"`
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"gmanager/internal/admin/controller"
|
||||
"gmanager/internal/admin/middleware"
|
||||
common "gmanager/internal/common/controller"
|
||||
"gmanager/internal/library/cache"
|
||||
"gmanager/internal/library/gftoken"
|
||||
)
|
||||
@@ -31,13 +33,14 @@ var (
|
||||
//跨域处理,安全起见正式环境请注释该行
|
||||
group.Middleware(func(r *ghttp.Request) {
|
||||
r.Response.CORSDefault()
|
||||
g.Log().Info(ctx, r.GetServeHandler().GetMetaTag("perms"))
|
||||
r.Middleware.Next()
|
||||
})
|
||||
// gtoken认证
|
||||
group.Middleware(gftoken.MiddlewareAuth)
|
||||
group.Middleware(gftoken.MiddlewareAuth) // gtoken登陆认证
|
||||
group.Middleware(middleware.UserPerm) // 用户权限认证
|
||||
group.Middleware(ghttp.MiddlewareHandlerResponse)
|
||||
group.Middleware(middleware.DemoNotice) // 演示环境提示
|
||||
group.Bind(
|
||||
common.Upload,
|
||||
controller.Login,
|
||||
controller.Dept,
|
||||
controller.User,
|
||||
|
||||
5
server/internal/common/consts/consts.go
Normal file
5
server/internal/common/consts/consts.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package consts
|
||||
|
||||
const (
|
||||
StorageTypeLocal = 1
|
||||
)
|
||||
16
server/internal/common/controller/upload.go
Normal file
16
server/internal/common/controller/upload.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
v1 "gmanager/api/common/v1"
|
||||
"gmanager/internal/common/logic"
|
||||
)
|
||||
|
||||
type upload struct{}
|
||||
|
||||
var Upload = new(upload)
|
||||
|
||||
func (c *upload) Upload(ctx context.Context, req *v1.UploadFileReq) (res *v1.UploadFileRes, err error) {
|
||||
res, err = logic.Upload.Upload(ctx, req)
|
||||
return
|
||||
}
|
||||
50
server/internal/common/logic/storage_file.go
Normal file
50
server/internal/common/logic/storage_file.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"gmanager/internal/common/consts"
|
||||
"gmanager/internal/common/model/input"
|
||||
"gmanager/internal/common/service"
|
||||
)
|
||||
|
||||
type storageFile struct{}
|
||||
|
||||
func NewStorageFile() *storageFile {
|
||||
return &storageFile{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
service.RegisterStorage(NewStorageFile())
|
||||
}
|
||||
|
||||
func (s *storageFile) Upload(ctx context.Context, file *input.UploadFile) (res *input.UploadFileRes, err error) {
|
||||
// TODO 上传信息需要配置化
|
||||
var (
|
||||
attachmentPath = "attachment"
|
||||
nowDate = gtime.Date()
|
||||
storageType = consts.StorageTypeLocal
|
||||
)
|
||||
|
||||
fullDirPath := gfile.MainPkgPath() + "/resource/public/" + attachmentPath + "/" + nowDate
|
||||
fileName, err := file.File.Save(fullDirPath, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 不含静态文件夹的路径
|
||||
fullPath := "/" + attachmentPath + "/" + nowDate + "/" + fileName
|
||||
res = &input.UploadFileRes{
|
||||
Name: fileName,
|
||||
OriName: file.File.Filename,
|
||||
Type: storageType,
|
||||
Path: fullDirPath,
|
||||
FileUrl: "http://localhost:8000" + fullPath,
|
||||
Size: file.File.Size,
|
||||
Ext: gfile.Ext(fullPath),
|
||||
}
|
||||
g.Log().Info(ctx, "upload file", res)
|
||||
|
||||
return
|
||||
}
|
||||
31
server/internal/common/logic/upload.go
Normal file
31
server/internal/common/logic/upload.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
v1 "gmanager/api/common/v1"
|
||||
"gmanager/internal/common/model/input"
|
||||
"gmanager/internal/common/service"
|
||||
)
|
||||
|
||||
// Upload 上传服务
|
||||
var Upload = new(upload)
|
||||
|
||||
type upload struct{}
|
||||
|
||||
// Upload 上传文件
|
||||
func (s *upload) Upload(ctx context.Context, in *v1.UploadFileReq) (res *v1.UploadFileRes, err error) {
|
||||
uploadRes, err := service.Storage().Upload(ctx, &input.UploadFile{File: in.File})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if uploadRes == nil {
|
||||
err = gerror.New("上传失败")
|
||||
return
|
||||
}
|
||||
res = &v1.UploadFileRes{
|
||||
Name: uploadRes.Name,
|
||||
Url: uploadRes.FileUrl,
|
||||
}
|
||||
return
|
||||
}
|
||||
17
server/internal/common/model/input/upload.go
Normal file
17
server/internal/common/model/input/upload.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package input
|
||||
|
||||
import "github.com/gogf/gf/v2/net/ghttp"
|
||||
|
||||
type UploadFile struct {
|
||||
File *ghttp.UploadFile `json:"file" type:"file" dc:"文件"`
|
||||
}
|
||||
|
||||
type UploadFileRes struct {
|
||||
Name string `json:"name" description:"文件名"`
|
||||
OriName string `json:"oriName" description:"文件原始名"`
|
||||
Path string `json:"path" description:"本地路径"`
|
||||
FileUrl string `json:"fileUrl" description:"url"`
|
||||
Type int `json:"storageType" description:"存储类型"`
|
||||
Size int64 `json:"size" description:"文件大小"`
|
||||
Ext string `json:"ext" description:"扩展名"`
|
||||
}
|
||||
0
server/internal/common/service/.gitkeep
Normal file
0
server/internal/common/service/.gitkeep
Normal file
28
server/internal/common/service/storage.go
Normal file
28
server/internal/common/service/storage.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"gmanager/internal/common/model/input"
|
||||
)
|
||||
|
||||
type (
|
||||
IStorage interface {
|
||||
// Upload 上传文件
|
||||
Upload(ctx context.Context, file *input.UploadFile) (res *input.UploadFileRes, err error)
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
storage IStorage
|
||||
)
|
||||
|
||||
func Storage() IStorage {
|
||||
if storage == nil {
|
||||
panic("implement not found for interface IStorage, forgot register?")
|
||||
}
|
||||
return storage
|
||||
}
|
||||
|
||||
func RegisterStorage(i IStorage) {
|
||||
storage = i
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package bean
|
||||
|
||||
type SessionUser struct {
|
||||
Id int64 `form:"id" json:"id"` // 主键
|
||||
Uuid string `form:"uuid" json:"uuid"` // UUID
|
||||
Username string `form:"username" json:"username"` // 登录名/11111
|
||||
NickName string `form:"nickname" json:"nickname"` // 昵称
|
||||
Id int64 `json:"id"` // 主键
|
||||
Uuid string `json:"uuid"` // UUID
|
||||
UserName string `json:"userName"` // 登录名/11111
|
||||
NickName string `json:"nickName"` // 昵称
|
||||
UserType int `json:"userType"` // 类型
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
)
|
||||
|
||||
var GToken gtoken.Token
|
||||
var m gtoken.Middleware
|
||||
|
||||
func init() {
|
||||
options := >oken.Options{}
|
||||
@@ -21,17 +22,30 @@ func init() {
|
||||
}
|
||||
// 创建gtoken对象
|
||||
GToken = gtoken.NewDefaultToken(*options)
|
||||
m = gtoken.NewDefaultMiddleware(GToken,
|
||||
"/admin/login", "/admin/captcha/get")
|
||||
}
|
||||
|
||||
func MiddlewareAuth(r *ghttp.Request) {
|
||||
gtoken.NewDefaultMiddleware(GToken,
|
||||
"/admin/login", "/admin/captcha/get").Auth(r)
|
||||
m.Auth(r)
|
||||
}
|
||||
|
||||
func HasExcludePath(r *ghttp.Request) bool {
|
||||
return m.HasExcludePath(r)
|
||||
}
|
||||
|
||||
func GetUserKey(ctx context.Context) string {
|
||||
return g.RequestFromCtx(ctx).GetCtxVar(gtoken.KeyUserKey).String()
|
||||
}
|
||||
|
||||
func GetUserId(ctx context.Context) int64 {
|
||||
user := GetSessionUser(ctx)
|
||||
if user == nil {
|
||||
return 0
|
||||
}
|
||||
return user.Id
|
||||
}
|
||||
|
||||
func GetSessionUser(ctx context.Context) *bean.SessionUser {
|
||||
var user *bean.SessionUser
|
||||
data, err := GetData(ctx)
|
||||
@@ -48,7 +62,10 @@ func GetSessionUser(ctx context.Context) *bean.SessionUser {
|
||||
}
|
||||
|
||||
func GetData(ctx context.Context) (data any, err error) {
|
||||
userKey := g.RequestFromCtx(ctx).GetCtxVar(gtoken.KeyUserKey).String()
|
||||
_, data, err = GToken.Get(ctx, userKey)
|
||||
userKey := g.RequestFromCtx(ctx).GetCtxVar(gtoken.KeyUserKey)
|
||||
if userKey.IsNil() {
|
||||
return
|
||||
}
|
||||
_, data, err = GToken.Get(ctx, userKey.String())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ server:
|
||||
address: ":8000"
|
||||
openapiPath: "/api.json"
|
||||
swaggerPath: "/swagger"
|
||||
serverRoot: "resource/public" # 静态文件服务的目录根路径,配置时自动开启静态文件服务。
|
||||
# 日志基本配置
|
||||
# 此配置类似nginx,主要对请求日志的记录
|
||||
logPath: "./logs" # 日志文件存储目录路径,建议使用绝对路径。默认为空,表示关闭
|
||||
@@ -37,7 +36,7 @@ database:
|
||||
level: "all"
|
||||
stdout: true
|
||||
default:
|
||||
link: "mysql:uroot:Aa123456!#@tcp(127.0.0.1:3306)/gmanager"
|
||||
link: "mysql:uroot:Aa123456!#@tcp(127.0.0.1:3306)/gf_cms"
|
||||
debug: true
|
||||
|
||||
gToken:
|
||||
|
||||
Reference in New Issue
Block a user