Files
kami_backend/internal/logic/sys_user/sys_user.go
2024-07-14 20:26:33 +08:00

605 lines
18 KiB
Go

package sysUser
import (
"context"
"fmt"
v1 "kami/api/sys_user_login/v1"
"kami/internal/dao"
"kami/internal/errHandler"
"kami/internal/model"
"kami/internal/model/do"
"kami/internal/model/entity"
"kami/internal/service"
"kami/utility/config"
"kami/utility/token"
"kami/utility/utils"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/container/gset"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/glog"
"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/mssola/user_agent"
)
func init() {
service.RegisterSysUser(New())
}
type sSysUser struct {
casBinUserPrefix string // CasBin 用户id前缀
}
func New() *sSysUser {
return &sSysUser{
casBinUserPrefix: "u_",
}
}
func (s *sSysUser) GetCasBinUserPrefix() string {
return s.casBinUserPrefix
}
func (s *sSysUser) NotCheckAuthAdminIds(ctx context.Context) *gset.Set {
ids := g.Cfg().MustGet(ctx, "systemV1.notCheckAuthAdminIds")
if !g.IsNil(ids) {
return gset.NewFrom(ids)
}
return gset.New()
}
func (s *sSysUser) GetAdminUserByUsernamePassword(ctx context.Context, req *model.UserLoginInput) (user *model.LoginUserOutput, err error) {
user, err = s.GetUserByUsername(ctx, req.Username)
if user == nil || err != nil {
err = gerror.NewCode(gcode.CodeNotAuthorized, "账户名错误")
return
}
// 验证密码
if utils.EncryptPassword(req.Password, user.UserSalt) != user.UserPassword {
err = gerror.NewCode(gcode.CodeNotAuthorized, "密码错误")
return
}
////账号状态
//if user.UserStatus == 0 {
// liberr.ErrIsNil(ctx, gerror.New("账号已被冻结"))
//}
return
}
// GetUserByUsername 通过用户名获取用户信息
func (s *sSysUser) GetUserByUsername(ctx context.Context, userName string) (sysUser *model.LoginUserOutput, err error) {
sysUser = &model.LoginUserOutput{}
err = dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1()).Fields(sysUser).
Where(dao.V1SysUser.Columns().Username, userName).
Scan(sysUser)
err = utils.HandleNoRowsError(err)
return
}
// GetUserById 通过用户名获取用户信息
func (s *sSysUser) GetUserById(ctx context.Context, id string) (sysUser *model.LoginUserOutput, err error) {
sysUser = &model.LoginUserOutput{}
err = dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1()).Fields(sysUser).WherePri(id).Scan(sysUser)
return
}
// LoginLog 记录登录日志
func (s *sSysUser) LoginLog(ctx context.Context, params *model.LoginLogParams) {
ua := user_agent.New(params.UserAgent)
browser, _ := ua.Browser()
_, err := dao.V1SysUserLoginLog.Ctx(ctx).DB(config.GetDatabaseV1()).Insert(&do.V1SysUserLoginLog{
LoginName: params.Username,
IpAddr: params.Ip,
// LoginLocation: utils.GetCityByIp(params.Ip),
Browser: browser,
Os: ua.OS(),
Status: params.Status,
Message: params.Msg,
LoginTime: gtime.Now(),
Module: params.Module,
})
if err != nil {
glog.Error(ctx, err)
}
}
//func (s *sSysUser) UpdateLoginInfo(ctx context.Context, id uint64, ip string) (err error) {
// err = g.Try(ctx, func(ctx context.Context) {
// _, err = dao.V1SysUser.Ctx(ctx).WherePri(id).Unscoped().Update(g.Map{
// dao.V1SysUser.Columns().LastLoginIp: ip,
// dao.V1SysUser.Columns().LastLoginTime: gtime.Now(),
// })
// liberr.ErrIsNil(ctx, err, "更新用户登录信息失败")
// })
// return
//}
// GetAdminRules 获取用户菜单数据
func (s *sSysUser) GetAdminRules(ctx context.Context, userId string) (menuList []*model.UserMenus, permissions []string, err error) {
isSuperAdmin := false
// 获取无需验证权限的用户id
s.NotCheckAuthAdminIds(ctx).Iterator(func(v interface{}) bool {
if v == userId {
isSuperAdmin = true
return false
}
return true
})
// 获取用户菜单数据
allRoles, err := service.SysRole().GetRoleList(ctx)
if err != nil {
return
}
roles, err := s.GetAdminRole(ctx, userId, allRoles)
if err != nil {
return
}
name := make([]string, len(roles))
roleIds := make([]uint, len(roles))
for k, v := range roles {
name[k] = v.Name
roleIds[k] = v.Id
}
// 获取菜单信息
if isSuperAdmin {
// 超管获取所有菜单
permissions = []string{"*/*/*"}
menuList, err = s.GetAllMenus(ctx)
if err != nil {
return
}
} else {
menuList, err = s.GetAdminMenusByRoleIds(ctx, roleIds)
if err != nil {
return
}
permissions, err = s.GetPermissions(ctx, roleIds)
if err != nil {
return
}
}
return
}
// GetAdminRole 获取用户角色
func (s *sSysUser) GetAdminRole(ctx context.Context, userId string, allRoleList []*entity.V1SysRole) (roles []*entity.V1SysRole, err error) {
var roleIds []uint
roleIds, err = s.GetAdminRoleIds(ctx, userId)
if err != nil {
return
}
roles = make([]*entity.V1SysRole, 0, len(allRoleList))
for _, v := range allRoleList {
for _, id := range roleIds {
if id == v.Id {
roles = append(roles, v)
}
}
if len(roles) == len(roleIds) {
break
}
}
return
}
// GetAdminRoleIds 获取用户角色ids
func (s *sSysUser) GetAdminRoleIds(ctx context.Context, userId string) (roleIds []uint, err error) {
casbinService := service.Casbin()
enforcer, err := casbinService.CasbinEnforcer(ctx)
if err != nil {
return
}
// 查询关联角色规则
groupPolicy, err := enforcer.GetFilteredGroupingPolicy(0, fmt.Sprintf("%s%d", s.casBinUserPrefix, userId))
if err != nil {
return
}
if len(groupPolicy) > 0 {
roleIds = make([]uint, len(groupPolicy))
// 得到角色id的切片
for k, v := range groupPolicy {
roleIds[k] = gconv.Uint(v[1])
}
}
return
}
func (s *sSysUser) GetAllMenus(ctx context.Context) (menus []*model.UserMenus, err error) {
// 获取所有开启的菜单
var allMenus []*model.SysAuthRuleInfoOutput
allMenus, err = service.SysAuthRule().GetIsMenuList(ctx)
if err != nil {
return
}
menus = make([]*model.UserMenus, len(allMenus))
for k, v := range allMenus {
var menu *model.UserMenu
menu = s.setMenuData(menu, v)
menus[k] = &model.UserMenus{UserMenu: menu}
}
menus = s.GetMenusTree(menus, 0)
return
}
func (s *sSysUser) GetAdminMenusByRoleIds(ctx context.Context, roleIds []uint) (menus []*model.UserMenus, err error) {
// 获取角色对应的菜单id
casbinService := service.Casbin()
enforcer, err := casbinService.CasbinEnforcer(ctx)
if err != nil {
return
}
menuIds := map[int64]int64{}
for _, roleId := range roleIds {
// 查询当前权限
gp, err2 := enforcer.GetFilteredPolicy(0, gconv.String(roleId))
if err2 != nil {
return menus, err2
}
for _, p := range gp {
mid := gconv.Int64(p[1])
menuIds[mid] = mid
}
}
// 获取所有开启的菜单
allMenus, err := service.SysAuthRule().GetIsMenuList(ctx)
if err != nil {
return
}
menus = make([]*model.UserMenus, 0, len(allMenus))
for _, v := range allMenus {
if _, ok := menuIds[gconv.Int64(v.Id)]; gstr.Equal(v.Condition, "nocheck") || ok {
var roleMenu *model.UserMenu
roleMenu = s.setMenuData(roleMenu, v)
menus = append(menus, &model.UserMenus{UserMenu: roleMenu})
}
}
menus = s.GetMenusTree(menus, 0)
return
}
func (s *sSysUser) GetMenusTree(menus []*model.UserMenus, pid uint) []*model.UserMenus {
returnList := make([]*model.UserMenus, 0, len(menus))
for _, menu := range menus {
if menu.Pid == pid {
menu.Children = s.GetMenusTree(menus, menu.Id)
returnList = append(returnList, menu)
}
}
return returnList
}
func (s *sSysUser) setMenuData(menu *model.UserMenu, entity *model.SysAuthRuleInfoOutput) *model.UserMenu {
menu = &model.UserMenu{
UserMenu: v1.UserMenu{
Id: entity.Id,
Pid: entity.Pid,
Name: gstr.CaseCamelLower(gstr.Replace(entity.Name, "/", "_")),
Component: entity.Component,
Path: entity.Path,
MenuMeta: &v1.MenuMeta{
Icon: entity.Icon,
Title: entity.Title,
IsLink: "",
IsHide: entity.IsHide == 1,
IsKeepAlive: entity.IsCached == 1,
IsAffix: entity.IsAffix == 1,
IsIframe: entity.IsIframe == 1,
},
},
}
if menu.MenuMeta.IsIframe || entity.IsLink == 1 {
menu.MenuMeta.IsLink = entity.LinkUrl
}
return menu
}
func (s *sSysUser) GetPermissions(ctx context.Context, roleIds []uint) (userButtons []string, err error) {
// 获取角色对应的菜单id
casbinService := service.Casbin()
enforcer, err := casbinService.CasbinEnforcer(ctx)
if err != nil {
return
}
menuIds := map[int64]int64{}
for _, roleId := range roleIds {
// 查询当前权限
gp, err2 := enforcer.GetFilteredPolicy(0, gconv.String(roleId))
if err2 != nil {
return userButtons, err2
}
for _, p := range gp {
mid := gconv.Int64(p[1])
menuIds[mid] = mid
}
}
// 获取所有开启的按钮
allButtons, err := service.SysAuthRule().GetIsButtonList(ctx)
if err != nil {
return
}
userButtons = make([]string, 0, len(allButtons))
for _, button := range allButtons {
if _, ok := menuIds[gconv.Int64(button.Id)]; gstr.Equal(button.Condition, "nocheck") || ok {
userButtons = append(userButtons, button.Name)
}
}
return
}
// List 用户列表
func (s *sSysUser) List(ctx context.Context, req *model.UserSearchInput) (total int, userList []*entity.V1SysUser, err error) {
m := dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1())
total = 0
if req.Username != "" {
m = m.WhereLike(dao.V1SysUser.Columns().Username, utils.OrmLike(req.Username))
}
if req.Status != "" {
m = m.Where(dao.V1SysUser.Columns().UserStatus, gconv.Int(req.Status))
}
err = m.FieldsEx(dao.V1SysUser.Columns().UserPassword, dao.V1SysUser.Columns().UserSalt).
Page(req.Current, req.PageSize).OrderDesc(dao.V1SysUser.Columns().CreatedAt).
ScanAndCount(&userList, &total, true)
return
}
// ListWithPayment 查询用户数据,带钱包信息
func (s *sSysUser) ListWithPayment(ctx context.Context, req *model.UserSearchInput) (total int, userList []*model.UserSearchWithPaymentOutput, err error) {
m := dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1())
total = 0
if req.Username != "" {
m = m.WhereLike(dao.V1SysUser.Columns().Username, utils.OrmLike(req.Username))
}
if req.Status != "" {
m = m.Where(dao.V1SysUser.Columns().UserStatus, gconv.Int(req.Status))
}
err = m.FieldsEx(dao.V1SysUser.Columns().UserPassword, dao.V1SysUser.Columns().UserSalt).WithAll().
Page(req.Current, req.PageSize).OrderDesc(dao.V1SysUser.Columns().CreatedAt).
ScanAndCount(&userList, &total, true)
return
}
func (s *sSysUser) Add(ctx context.Context, req *model.UserAddInput) (err error) {
isExists, err := s.UserNameOrMobileExists(ctx, req.Username)
if err != nil {
return
}
if isExists {
err = gerror.NewCode(gcode.CodeOperationFailed, "当前用户存在")
return
}
userSalt := grand.S(10)
err = config.GetDatabaseV1().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
userId := utils.GenerateRandomUUID()
_, err2 := dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1()).TX(tx).Save(do.V1SysUser{
Id: userId,
Username: req.Username,
UserPassword: utils.EncryptPassword(req.Password, userSalt),
UserSalt: userSalt,
UserStatus: 1,
IsAdmin: req.IsAdmin,
})
if err2 != nil {
return err2
}
err2 = service.SysUserPayment().Add(ctx, model.SysUserPaymentInput{
UserId: userId,
}, tx)
return err2
})
return
}
func (s *sSysUser) Edit(ctx context.Context, req *model.UserEditInput) (err error) {
isExists, err := s.UserNameOrMobileExists(ctx, "", req.ID)
if err != nil {
return
}
if !isExists {
err = gerror.NewCode(gcode.CodeNotFound, "用户不存在")
}
userSalt := grand.S(10)
req.Password = utils.EncryptPassword(req.Password, userSalt)
err = config.GetDatabaseV1().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
_, err = dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1()).TX(tx).WherePri(req.ID).
Update(do.V1SysUser{
UserPassword: req.Password,
UserSalt: userSalt,
})
// if err != nil {
// return err
// }
// 设置用户所属角色信息
// err = s.EditUserRole(ctx, req.RoleIds, req.UserId)
// if err != nil {
// return err
// }
return err
})
return
}
// AddUserRole 添加用户角色信息
func (s *sSysUser) addUserRole(ctx context.Context, roleIds []int64, userId int64) (err error) {
casbinService := service.Casbin()
enforcer, err := casbinService.CasbinEnforcer(ctx)
if err != nil {
return
}
for _, v := range roleIds {
_, err = enforcer.AddGroupingPolicy(fmt.Sprintf("%s%d", s.casBinUserPrefix, userId), gconv.String(v))
if err != nil {
return
}
}
return
}
// EditUserRole 修改用户角色信息
func (s *sSysUser) EditUserRole(ctx context.Context, roleIds []int64, userId int64) (err error) {
casbinService := service.Casbin()
enforcer, err := casbinService.CasbinEnforcer(ctx)
if err != nil {
return
}
// 删除用户旧角色信息
_, _ = enforcer.RemoveFilteredGroupingPolicy(0, fmt.Sprintf("%s%d", s.casBinUserPrefix, userId))
for _, v := range roleIds {
_, err = enforcer.AddGroupingPolicy(fmt.Sprintf("%s%d", s.casBinUserPrefix, userId), gconv.String(v))
if err != nil {
return
}
}
return
}
func (s *sSysUser) UserNameOrMobileExists(ctx context.Context, userName string, id ...string) (isExist bool, err error) {
user := entity.V1SysUser{}
isExist = false
m := dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1())
if userName == "" && len(id) == 0 {
return
}
if len(id) > 0 {
m = m.Where(dao.V1SysUser.Columns().Id, id[0])
}
if userName != "" {
m = m.Where(dao.V1SysUser.Columns().Username, userName)
}
err = m.Limit(1).Scan(&user)
err = utils.HandleNoRowsError(err)
if err != nil {
return
}
if user.Id != "" {
isExist = true
}
return
}
// GetEditUser 获取编辑用户信息
func (s *sSysUser) GetEditUser(ctx context.Context, id string) (res *model.UserGetEditOutput, err error) {
res = new(model.UserGetEditOutput)
// 获取用户信息
res.User, err = s.GetUserInfoById(ctx, id)
if err != nil {
return
}
// 获取已选择的角色信息
res.CheckedRoleIds, err = s.GetAdminRoleIds(ctx, id)
return
}
// GetUserInfoById 通过Id获取用户信息
func (s *sSysUser) GetUserInfoById(ctx context.Context, id string, withPwd ...bool) (user *entity.V1SysUser, err error) {
if len(withPwd) > 0 && withPwd[0] {
// 用户用户信息
err = dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1()).Where(dao.V1SysUser.Columns().Id, id).Scan(&user)
} else {
// 用户用户信息
err = dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1()).Where(dao.V1SysUser.Columns().Id, id).
FieldsEx(dao.V1SysUser.Columns().UserPassword, dao.V1SysUser.Columns().UserSalt).Scan(&user)
}
return
}
// ChangePwd ResetUserPwd 重置用户密码
func (s *sSysUser) ChangePwd(ctx context.Context, input *model.UserChangePwdInput) (err error) {
salt := grand.S(10)
password := utils.EncryptPassword(input.NewPassword, salt)
_, err = dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1()).WherePri(input.UserId).Update(do.V1SysUser{
UserSalt: salt,
UserPassword: password,
})
return
}
func (s *sSysUser) ChangeUserStatus(ctx context.Context, input *model.UserStatusInput) (err error) {
_, err = dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1()).WherePri(input.ID).Update(do.V1SysUser{UserStatus: input.UserStatus})
return
}
// DeleteById 删除用户
func (s *sSysUser) DeleteById(ctx context.Context, id string) (err error) {
err = config.GetDatabaseV1().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
_, err = dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1()).TX(tx).Where(dao.V1SysUser.Columns().Id, id).Delete()
return nil
})
return
}
// Delete 删除用户
func (s *sSysUser) Delete(ctx context.Context, ids []string) (err error) {
err = config.GetDatabaseV1().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
_, err = dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1()).TX(tx).WhereIn(dao.V1SysUser.Columns().Id, ids).Delete()
if err != nil {
return err
}
casbinService := service.Casbin()
// 删除对应权限
enforcer, err2 := casbinService.CasbinEnforcer(ctx)
if err2 != nil {
return err2
}
for _, v := range ids {
_, _ = enforcer.RemoveFilteredGroupingPolicy(0, fmt.Sprintf("%s%d", s.casBinUserPrefix, v))
}
return nil
})
return
}
// GetUsers 通过用户ids查询多个用户信息
func (s *sSysUser) GetUsers(ctx context.Context, ids []int) (users []*model.SysUserSimpleOutput, err error) {
if len(ids) == 0 {
return
}
idsSet := gset.NewIntSetFrom(ids).Slice()
err = dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1()).WhereIn(dao.V1SysUser.Columns().Id, idsSet).
OrderAsc(dao.V1SysUser.Columns().Id).Scan(&users)
return
}
// GetUsersAll 获取所有用户
func (s *sSysUser) GetUsersAll(ctx context.Context) (users []*model.SysUserSimpleOutput, err error) {
err = dao.V1SysUser.Ctx(ctx).DB(config.GetDatabaseV1()).OrderAsc(dao.V1SysUser.Columns().Id).
Scan(&users)
err = utils.HandleNoRowsError(err)
return
}
func (s *sSysUser) GetUserIdFromToken(ctx context.Context) (needAuth bool, user *entity.V1SysUser, err error) {
tokenFrom := g.RequestFromCtx(ctx).Request.Header.Get("tokenFrom")
needAuth = false
if tokenFrom == "" {
err = gerror.NewCode(errHandler.ErrTokenExpired, "token失效")
return
}
if tokenFrom == "iframe" {
return
}
needAuth = true
tokenStr := token.GetTokenFromCtx(ctx)
if tokenStr == "" {
err = gerror.NewCode(errHandler.ErrTokenExpired, "token失效")
return
}
userToken, err := token.ParseUserToken(ctx, tokenStr)
if err != nil {
return
}
userService := service.SysUser()
user, err2 := userService.GetUserInfoById(ctx, userToken.UserID, true)
if err2 != nil || user == nil {
err = errHandler.WrapError(ctx, errHandler.ErrTokenExpired, err, "token失效或用户不存在")
return
}
return
}