/*
* @desc:用户处理
* @company:云南奇讯科技有限公司
* @Author: yixiaohu<yxh669@qq.com>
* @Date:   2022/9/23 15:08
 */

package sysUser

import (
	"context"
	"fmt"
	"github.com/gogf/gf/v2/container/garray"
	"github.com/gogf/gf/v2/encoding/gurl"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/tiger1103/gfast/v3/library/libWebsocket"
	"reflect"

	"github.com/gogf/gf/v2/container/gset"
	"github.com/gogf/gf/v2/database/gdb"
	"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/mssola/user_agent"
	"github.com/tiger1103/gfast/v3/api/v1/system"
	commonService "github.com/tiger1103/gfast/v3/internal/app/common/service"
	"github.com/tiger1103/gfast/v3/internal/app/system/consts"
	"github.com/tiger1103/gfast/v3/internal/app/system/dao"
	"github.com/tiger1103/gfast/v3/internal/app/system/model"
	"github.com/tiger1103/gfast/v3/internal/app/system/model/do"
	"github.com/tiger1103/gfast/v3/internal/app/system/model/entity"
	"github.com/tiger1103/gfast/v3/internal/app/system/service"
	"github.com/tiger1103/gfast/v3/library/libUtils"
	"github.com/tiger1103/gfast/v3/library/liberr"
)

func init() {
	service.RegisterSysUser(New())
}

type sSysUser struct {
	casBinUserPrefix string //CasBin 用户id前缀
}

func New() service.ISysUser {
	return &sSysUser{
		casBinUserPrefix: "u_",
	}
}

func (s *sSysUser) GetCasBinUserPrefix() string {
	return s.casBinUserPrefix
}

// IsSupperAdmin 判断用户是否超管
func (s *sSysUser) IsSupperAdmin(ctx context.Context, userId uint64) bool {
	superAdminIds := s.NotCheckAuthAdminIds(ctx)
	if superAdminIds.Contains(userId) {
		return true
	}
	return false
}

func (s *sSysUser) NotCheckAuthAdminIds(ctx context.Context) *gset.Set {
	ids := g.Cfg().MustGet(ctx, "system.notCheckAuthAdminIds").Uint64s()
	if !g.IsNil(ids) {
		return gset.NewFrom(ids)
	}
	return gset.New()
}

func (s *sSysUser) GetAdminUserByUsernamePassword(ctx context.Context, req *system.UserLoginReq) (user *model.LoginUserRes, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		user, err = s.GetUserByUsername(ctx, req.Username)
		liberr.ErrIsNil(ctx, err)
		liberr.ValueIsNil(user, "账号密码错误")
		//验证密码
		if libUtils.EncryptPassword(req.Password, user.UserSalt) != user.UserPassword {
			liberr.ErrIsNil(ctx, gerror.New("账号密码错误"))
		}
		//账号状态
		if user.UserStatus == 0 {
			liberr.ErrIsNil(ctx, gerror.New("账号已被冻结"))
		}
	})
	return
}

// GetUserByUsername 通过用户名获取用户信息
func (s *sSysUser) GetUserByUsername(ctx context.Context, userName string) (user *model.LoginUserRes, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		user = &model.LoginUserRes{}
		err = dao.SysUser.Ctx(ctx).Fields(user).Where(dao.SysUser.Columns().UserName, userName).Scan(user)
		liberr.ErrIsNil(ctx, err, "账号密码错误")
	})
	return
}

func (s *sSysUser) GetUserByPhone(ctx context.Context, phone string) (user *model.LoginUserRes, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		err = dao.SysUser.Ctx(ctx).Fields(user).Where(dao.SysUser.Columns().Mobile, phone).
			Scan(&user)
		liberr.ErrIsNil(ctx, err, "登录失败，用户信息不存在")
	})
	return
}

// GetUserById 通过用户名获取用户信息
func (s *sSysUser) GetUserById(ctx context.Context, id uint64) (user *model.LoginUserRes, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		user = &model.LoginUserRes{}
		err = dao.SysUser.Ctx(ctx).Fields(user).WherePri(id).Scan(user)
		liberr.ErrIsNil(ctx, err, "获取用户信息失败")
	})
	return
}

// LoginLog 记录登录日志
func (s *sSysUser) LoginLog(ctx context.Context, params *model.LoginLogParams) {
	ua := user_agent.New(params.UserAgent)
	browser, _ := ua.Browser()
	loginData := &do.SysLoginLog{
		LoginName:     params.Username,
		Ipaddr:        params.Ip,
		LoginLocation: libUtils.GetCityByIp(params.Ip),
		Browser:       browser,
		Os:            ua.OS(),
		Status:        params.Status,
		Msg:           params.Msg,
		LoginTime:     gtime.Now(),
		Module:        params.Module,
	}
	_, err := dao.SysLoginLog.Ctx(ctx).Insert(loginData)
	if err != nil {
		g.Log().Error(ctx, err)
	}
}

func (s *sSysUser) UpdateLoginInfo(ctx context.Context, id uint64, ip string, openId ...string) (err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		data := g.Map{
			dao.SysUser.Columns().LastLoginIp:   ip,
			dao.SysUser.Columns().LastLoginTime: gtime.Now(),
		}
		if len(openId) > 0 && openId[0] != "" {
			data[dao.SysUser.Columns().OpenId] = openId[0]
		}
		_, err = dao.SysUser.Ctx(ctx).WherePri(id).Unscoped().Update(data)
		liberr.ErrIsNil(ctx, err, "更新用户登录信息失败")
	})
	return
}

// GetAdminRules 获取用户菜单数据
func (s *sSysUser) GetAdminRules(ctx context.Context, userId uint64) (menuList []*model.UserMenus, permissions []string, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		//是否超管
		isSuperAdmin := s.IsSupperAdmin(ctx, userId)
		//获取用户菜单数据
		allRoles, err := service.SysRole().GetRoleList(ctx)
		liberr.ErrIsNil(ctx, err)
		roles, err := s.GetAdminRole(ctx, userId, allRoles)
		liberr.ErrIsNil(ctx, err)
		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)
			liberr.ErrIsNil(ctx, err)
		} else {
			menuList, err = s.GetAdminMenusByRoleIds(ctx, roleIds)
			liberr.ErrIsNil(ctx, err)
			permissions, err = s.GetPermissions(ctx, roleIds)
			liberr.ErrIsNil(ctx, err)
		}
	})
	return
}

// GetAdminRole 获取用户角色
func (s *sSysUser) GetAdminRole(ctx context.Context, userId uint64, allRoleList []*entity.SysRole) (roles []*entity.SysRole, err error) {
	var roleIds []uint
	roleIds, err = s.GetAdminRoleIds(ctx, userId)
	if err != nil {
		return
	}
	roles = make([]*entity.SysRole, 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 uint64, includeChildren ...bool) (roleIds []uint, err error) {
	enforcer, e := commonService.CasbinEnforcer(ctx)
	if e != nil {
		err = e
		return
	}
	//查询关联角色规则
	groupPolicy := enforcer.GetFilteredGroupingPolicy(0, fmt.Sprintf("%s%d", s.casBinUserPrefix, userId))
	if len(groupPolicy) > 0 {
		roleIds = make([]uint, len(groupPolicy))
		//得到角色id的切片
		for k, v := range groupPolicy {
			roleIds[k] = gconv.Uint(v[1])
		}
	}
	if len(includeChildren) > 0 && includeChildren[0] {
		//获取子级
		var allRoles []*entity.SysRole
		allRoles, err = service.SysRole().GetRoleList(ctx)
		if err != nil {
			return
		}
		childrenIds := make([]uint, 0, 100)
		for _, roleId := range roleIds {
			childrenIds = append(childrenIds, service.SysRole().FindSonIdsByParentId(allRoles, roleId)...)
		}
		//合并去重
		roleIds = append(roleIds, childrenIds...)
	}
	roleIds = libUtils.SliceUnique(roleIds)
	return
}

func (s *sSysUser) GetAllMenus(ctx context.Context) (menus []*model.UserMenus, err error) {
	//获取所有开启的菜单
	var allMenus []*model.SysAuthRuleInfoRes
	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) GetAdminMenusIdsByRoleIds(ctx context.Context, roleIds []uint) (menuIds *garray.Array, err error) {
	//获取角色对应的菜单id
	menuIds = garray.New()
	err = g.Try(ctx, func(ctx context.Context) {
		if s.IsSupperAdmin(ctx, service.Context().GetUserId(ctx)) {
			var menus []*model.SysAuthRuleInfoRes
			menus, err = service.SysAuthRule().GetMenuList(ctx)
			liberr.ErrIsNil(ctx, err)
			for _, m := range menus {
				menuIds.Append(m.Id)
			}
			return
		}
		enforcer, e := commonService.CasbinEnforcer(ctx)
		liberr.ErrIsNil(ctx, e)
		for _, roleId := range roleIds {
			//查询当前权限
			gp := enforcer.GetFilteredPolicy(0, gconv.String(roleId))
			for _, p := range gp {
				menuIds.Append(gconv.Uint(p[1]))
			}
		}
	})
	return
}

func (s *sSysUser) GetAdminMenusByRoleIds(ctx context.Context, roleIds []uint) (menus []*model.UserMenus, err error) {
	//获取角色对应的菜单id
	err = g.Try(ctx, func(ctx context.Context) {
		var (
			menuArr    *garray.Array
			menuIds    = map[uint]uint{}
			menuNameId = map[string]*model.SysAuthRuleInfoRes{}
		)
		menuArr, err = s.GetAdminMenusIdsByRoleIds(ctx, roleIds)
		liberr.ErrIsNil(ctx, err)
		menuArr.Iterator(func(k int, v interface{}) bool {
			mp := gconv.Uint(v)
			menuIds[mp] = mp
			return true
		})
		//获取所有开启的菜单
		allMenus, err := service.SysAuthRule().GetIsMenuList(ctx)
		liberr.ErrIsNil(ctx, err)
		menus = make([]*model.UserMenus, 0, len(allMenus))
		for _, v := range allMenus {
			urlMap, _ := gurl.ParseURL(v.Name, -1)
			if query, ok := urlMap["query"]; ok && query != "" {
				menuNameId[urlMap["path"]+"#query"] = v
			}
		}
		for _, v := range allMenus {
			var roleMenu *model.UserMenu
			if _, ok := menuIds[v.Id]; gstr.Equal(v.Condition, "nocheck") || ok {
				roleMenu = s.setMenuData(roleMenu, v)
				menus = append(menus, &model.UserMenus{UserMenu: roleMenu})
			}
			if info, ok := menuNameId[v.Name+"#query"]; ok && roleMenu != nil {
				roleMenu = s.setMenuData(roleMenu, info)
				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.SysAuthRuleInfoRes) *model.UserMenu {
	menu = &model.UserMenu{
		Id:        entity.Id,
		Pid:       entity.Pid,
		Name:      gstr.CaseCamelLower(gstr.Replace(entity.Name, "/", "_")),
		Component: entity.Component,
		Path:      entity.Path,
		MenuMeta: &model.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) {
	err = g.Try(ctx, func(ctx context.Context) {
		//获取角色对应的菜单id
		enforcer, err := commonService.CasbinEnforcer(ctx)
		liberr.ErrIsNil(ctx, err)
		menuIds := map[int64]int64{}
		for _, roleId := range roleIds {
			//查询当前权限
			gp := enforcer.GetFilteredPolicy(0, gconv.String(roleId))
			for _, p := range gp {
				mid := gconv.Int64(p[1])
				menuIds[mid] = mid
			}
		}
		//获取所有开启的按钮
		allButtons, err := service.SysAuthRule().GetIsButtonList(ctx)
		liberr.ErrIsNil(ctx, err)
		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
}

func fieldsEx() []string {
	s := dao.SysUser.Columns()
	return []string{
		s.Id,
		s.UserName,
		s.Mobile,
		s.UserNickname,
		s.Birthday,
		s.UserStatus,
		s.UserEmail,
		s.Sex,
		s.Avatar,
		s.DeptId,
		s.Remark,
		s.IsAdmin,
		s.Address,
		s.Describe,
		s.LastLoginIp,
		s.LastLoginTime,
		s.CreatedAt,
		s.UpdatedAt,
		s.DeletedAt,
	}
}

func inSlice(field string, excludes []string) bool {
	for _, exclude := range excludes {
		if exclude == field {
			return true
		}
	}
	return false
}

// 根据用户ID 获取用户
func (s *sSysUser) GetByIdsUser(ctx context.Context, req *system.UserByIdsReq) (total interface{}, userList []*entity.SysUser, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		m := dao.SysUser.Ctx(ctx)
		if req.Ids != nil {
			m = m.Where("id in (?)", req.Ids)
		}
		//dao.SysUserPost.Columns().UserId+" in (?)", ids
		if req.PageSize == 0 {
			req.PageSize = consts.PageSize
		}
		if req.PageNum == 0 {
			req.PageNum = 1
		}
		total, err = m.Count()
		liberr.ErrIsNil(ctx, err, "获取用户数据失败")

		// 排除password字段
		m = m.Fields(fieldsEx())

		err = m.Page(req.PageNum, req.PageSize).Order("id asc").Scan(&userList)
		liberr.ErrIsNil(ctx, err, "获取用户列表失败")
	})
	return
}

// List 用户列表
func (s *sSysUser) List(ctx context.Context, req *system.UserSearchReq) (total interface{}, userList []*entity.SysUser, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		m := dao.SysUser.Ctx(ctx)
		if req.KeyWords != "" {
			keyWords := "%" + req.KeyWords + "%"
			m = m.Where("user_name like ? or  user_nickname like ?", keyWords, keyWords)
		}
		if req.DeptId != "" {
			deptIds, e := s.getSearchDeptIds(ctx, gconv.Uint64(req.DeptId))
			liberr.ErrIsNil(ctx, e)
			m = m.Where("dept_id in (?)", deptIds)
		}
		if req.Status != "" {
			m = m.Where("user_status", gconv.Int(req.Status))
		}
		if req.Mobile != "" {
			m = m.Where("mobile like ?", "%"+req.Mobile+"%")
		}
		if len(req.DateRange) > 0 {
			m = m.Where("created_at >=? AND created_at <=?", req.DateRange[0], req.DateRange[1])
		}

		if req.RoleId > 0 {
			m = m.As("a").LeftJoin("casbin_rule", "b", "b.v0 = CONCAT('u_',a.id )")
			m = m.Where("v1 = ? and SUBSTR(v0,1,2) = 'u_'", req.RoleId)
		}
		if req.PageSize == 0 {
			req.PageSize = consts.PageSize
		}
		if req.PageNum == 0 {
			req.PageNum = 1
		}
		total, err = m.Count()
		liberr.ErrIsNil(ctx, err, "获取用户数据失败")

		// 排除password字段
		if req.RoleId > 0 {
			m = m.Fields(fieldsEx())
		} else {
			m = m.FieldsEx(dao.SysUser.Columns().UserPassword, dao.SysUser.Columns().UserSalt)
		}

		err = m.Page(req.PageNum, req.PageSize).Order("id asc").Scan(&userList)
		liberr.ErrIsNil(ctx, err, "获取用户列表失败")
	})
	return
}

func (s *sSysUser) GetUsersByRoleId(ctx context.Context, roleId uint) (users []*model.SysUserRoleDeptRes, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		m := dao.SysUser.Ctx(ctx)
		m = m.As("a").LeftJoin("casbin_rule", "b", "b.v0 = CONCAT('u_',a.id )").
			Where("v1 = ? and SUBSTR(v0,1,2) = 'u_'", roleId)
		err = m.Order("id asc").Scan(&users)
		liberr.ErrIsNil(ctx, err, "获取用户数据失败")
	})
	return
}

// GetUserSelector 获取用户选择器数据
func (s *sSysUser) GetUserSelector(ctx context.Context, req *system.UserSelectorReq) (total interface{}, userList []*model.SysUserSimpleRes, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		m := dao.SysUser.Ctx(ctx)
		if req.KeyWords != "" {
			keyWords := "%" + req.KeyWords + "%"
			m = m.Where("user_name like ? or  user_nickname like ?", keyWords, keyWords)
		}
		if req.DeptId != "" {
			deptIds, e := s.getSearchDeptIds(ctx, gconv.Uint64(req.DeptId))
			liberr.ErrIsNil(ctx, e)
			m = m.Where("dept_id in (?)", deptIds)
		}
		if req.Status != "" {
			m = m.Where("user_status", gconv.Int(req.Status))
		}
		if req.Mobile != "" {
			m = m.Where("mobile like ?", "%"+req.Mobile+"%")
		}
		if len(req.DateRange) > 0 {
			m = m.Where("created_at >=? AND created_at <=?", req.DateRange[0], req.DateRange[1])
		}

		if req.PageSize == 0 {
			req.PageSize = consts.PageSize
		}
		if req.PageNum == 0 {
			req.PageNum = 1
		}
		total, err = m.Count()
		liberr.ErrIsNil(ctx, err, "获取用户数据失败")

		// 排除password字段
		if req.RoleId > 0 {
			m = m.Fields(fieldsEx())
		} else {
			m = m.FieldsEx(dao.SysUser.Columns().UserPassword, dao.SysUser.Columns().UserSalt)
		}

		err = m.Page(req.PageNum, req.PageSize).Order("id asc").Scan(&userList)
		liberr.ErrIsNil(ctx, err, "获取用户列表失败")
	})
	return
}

// GetUsersRoleDept 获取多个用户角色 部门信息
func (s *sSysUser) GetUsersRoleDept(ctx context.Context, userList []*entity.SysUser) (users []*model.SysUserRoleDeptRes, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		allRoles, e := service.SysRole().GetRoleList(ctx)
		liberr.ErrIsNil(ctx, e)
		depts, e := service.SysDept().GetFromCache(ctx)
		liberr.ErrIsNil(ctx, e)
		users = make([]*model.SysUserRoleDeptRes, len(userList))
		for k, u := range userList {
			var dept *entity.SysDept
			users[k] = &model.SysUserRoleDeptRes{
				SysUser: u,
			}
			for _, d := range depts {
				if u.DeptId == uint64(d.DeptId) {
					dept = d
				}
			}
			users[k].Dept = dept
			roles, e := s.GetAdminRole(ctx, u.Id, allRoles)
			liberr.ErrIsNil(ctx, e)
			for _, r := range roles {
				users[k].RoleInfo = append(users[k].RoleInfo, &model.SysUserRoleInfoRes{RoleId: r.Id, Name: r.Name})
			}
		}
	})
	return
}

func (s *sSysUser) getSearchDeptIds(ctx context.Context, deptId uint64) (deptIds []uint64, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		deptAll, e := service.SysDept().GetFromCache(ctx)
		liberr.ErrIsNil(ctx, e)
		deptWithChildren := service.SysDept().FindSonByParentId(deptAll, deptId)
		deptIds = make([]uint64, len(deptWithChildren))
		for k, v := range deptWithChildren {
			deptIds[k] = v.DeptId
		}
		deptIds = append(deptIds, deptId)
	})
	return
}

// 过滤用户可操作的角色
func (s *sSysUser) filterRoleIds(ctx context.Context, roleIds []uint, userId uint64, includeChildren ...bool) (newRoleIds []uint, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		var (
			accessRoleList []uint
			roleList       []*entity.SysRole
		)
		accessRoleList, err = service.SysUser().GetAdminRoleIds(ctx, userId, includeChildren...)
		liberr.ErrIsNil(ctx, err)
		roleList, err = service.SysRole().GetRoleList(ctx)
		liberr.ErrIsNil(ctx, err)
		//自己创建的角色可以被授权
		for _, v := range roleList {
			if v.CreatedBy == userId {
				accessRoleList = append(accessRoleList, v.Id)
			}
		}
		for _, r := range roleIds {
			for _, a := range accessRoleList {
				if r == a {
					newRoleIds = append(newRoleIds, r)
					break
				}
			}
		}
	})
	return
}

func (s *sSysUser) Add(ctx context.Context, req *system.UserAddReq) (err error) {
	err = s.UserNameOrMobileExists(ctx, req.UserName, req.Mobile)
	if err != nil {
		return
	}
	req.UserSalt = grand.S(10)
	req.Password = libUtils.EncryptPassword(req.Password, req.UserSalt)
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		err = g.Try(ctx, func(ctx context.Context) {
			userId, e := dao.SysUser.Ctx(ctx).TX(tx).InsertAndGetId(do.SysUser{
				UserName:     req.UserName,
				Mobile:       req.Mobile,
				UserNickname: req.NickName,
				UserPassword: req.Password,
				UserSalt:     req.UserSalt,
				UserStatus:   req.Status,
				UserEmail:    req.Email,
				Sex:          req.Sex,
				DeptId:       req.DeptId,
				Remark:       req.Remark,
				IsAdmin:      req.IsAdmin,
			})
			liberr.ErrIsNil(ctx, e, "添加用户失败")
			//不是超管过滤提交角色数据
			if !service.SysUser().IsSupperAdmin(ctx, service.Context().GetUserId(ctx)) {
				req.RoleIds, err = s.filterRoleIds(ctx, req.RoleIds, service.Context().GetUserId(ctx))
				liberr.ErrIsNil(ctx, err)
			}
			e = s.addUserRole(ctx, req.RoleIds, userId)
			liberr.ErrIsNil(ctx, e, "设置用户权限失败")
			e = s.AddUserPost(ctx, tx, req.PostIds, userId)
			liberr.ErrIsNil(ctx, e)
		})
		return err
	})
	return
}

func (s *sSysUser) Edit(ctx context.Context, req *system.UserEditReq) (err error) {
	err = s.UserNameOrMobileExists(ctx, "", req.Mobile, req.UserId)
	if err != nil {
		return
	}
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		err = g.Try(ctx, func(ctx context.Context) {
			_, err = dao.SysUser.Ctx(ctx).TX(tx).WherePri(req.UserId).Update(do.SysUser{
				Mobile:       req.Mobile,
				UserNickname: req.NickName,
				UserStatus:   req.Status,
				UserEmail:    req.Email,
				Sex:          req.Sex,
				DeptId:       req.DeptId,
				Remark:       req.Remark,
				IsAdmin:      req.IsAdmin,
			})
			liberr.ErrIsNil(ctx, err, "修改用户信息失败")
			if !service.SysUser().IsSupperAdmin(ctx, service.Context().GetUserId(ctx)) {
				req.RoleIds, err = s.filterRoleIds(ctx, req.RoleIds, service.Context().GetUserId(ctx))
				liberr.ErrIsNil(ctx, err)
			}
			//设置用户所属角色信息
			err = s.EditUserRole(ctx, req.RoleIds, req.UserId)
			liberr.ErrIsNil(ctx, err, "设置用户权限失败")
			err = s.AddUserPost(ctx, tx, req.PostIds, req.UserId)
			liberr.ErrIsNil(ctx, err)
			//通知用户更新token
			libWebsocket.SendToUser(gconv.Uint64(req.UserId), &libWebsocket.WResponse{
				Event: consts.WebsocketTypeTokenUpdated,
				Data:  nil,
			})
		})
		return err
	})
	return
}

// AddUserPost 添加用户岗位信息
func (s *sSysUser) AddUserPost(ctx context.Context, tx gdb.TX, postIds []int64, userId int64) (err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		//删除旧岗位信息
		_, err = dao.SysUserPost.Ctx(ctx).TX(tx).Where(dao.SysUserPost.Columns().UserId, userId).Delete()
		liberr.ErrIsNil(ctx, err, "设置用户岗位失败")
		if len(postIds) == 0 {
			return
		}
		//添加用户岗位信息
		data := g.List{}
		for _, v := range postIds {
			data = append(data, g.Map{
				dao.SysUserPost.Columns().UserId: userId,
				dao.SysUserPost.Columns().PostId: v,
			})
		}
		_, err = dao.SysUserPost.Ctx(ctx).TX(tx).Data(data).Insert()
		liberr.ErrIsNil(ctx, err, "设置用户岗位失败")
	})
	return
}

// AddUserRole 添加用户角色信息
func (s *sSysUser) addUserRole(ctx context.Context, roleIds []uint, userId int64) (err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		enforcer, e := commonService.CasbinEnforcer(ctx)
		liberr.ErrIsNil(ctx, e)
		for _, v := range roleIds {
			_, e = enforcer.AddGroupingPolicy(fmt.Sprintf("%s%d", s.casBinUserPrefix, userId), gconv.String(v))
			liberr.ErrIsNil(ctx, e)
		}
	})
	return
}

// EditUserRole 修改用户角色信息
func (s *sSysUser) EditUserRole(ctx context.Context, roleIds []uint, userId int64) (err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		enforcer, e := commonService.CasbinEnforcer(ctx)
		liberr.ErrIsNil(ctx, e)

		//删除用户旧角色信息
		_, err = enforcer.RemoveFilteredGroupingPolicy(0, fmt.Sprintf("%s%d", s.casBinUserPrefix, userId))
		liberr.ErrIsNil(ctx, err)
		for _, v := range roleIds {
			_, err = enforcer.AddGroupingPolicy(fmt.Sprintf("%s%d", s.casBinUserPrefix, userId), gconv.String(v))
			liberr.ErrIsNil(ctx, err)
		}
	})
	return
}

// SetUserRole 设置用户角色
func (s *sSysUser) SetUserRole(ctx context.Context, roleId uint, userIds []uint64) (err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		enforcer, e := commonService.CasbinEnforcer(ctx)
		liberr.ErrIsNil(ctx, e)

		//删除用户旧角色信息
		_, err = enforcer.RemoveFilteredGroupingPolicy(1, fmt.Sprintf("%d", roleId))
		liberr.ErrIsNil(ctx, err)
		for _, v := range userIds {
			_, err = enforcer.AddGroupingPolicy(fmt.Sprintf("%s%d", s.casBinUserPrefix, v), gconv.String(roleId))
			liberr.ErrIsNil(ctx, err)
			//通知用户更新token
			libWebsocket.SendToUser(v, &libWebsocket.WResponse{
				Event: consts.WebsocketTypeTokenUpdated,
				Data:  nil,
			})
		}
	})
	return
}

func (s *sSysUser) UserNameOrMobileExists(ctx context.Context, userName, mobile string, id ...int64) error {
	user := (*entity.SysUser)(nil)
	err := g.Try(ctx, func(ctx context.Context) {
		m := dao.SysUser.Ctx(ctx)
		if len(id) > 0 {
			m = m.Where(dao.SysUser.Columns().Id+" != ", id)
		}
		m = m.Where(fmt.Sprintf("%s='%s' OR %s='%s'",
			dao.SysUser.Columns().UserName,
			userName,
			dao.SysUser.Columns().Mobile,
			mobile))
		err := m.Limit(1).Scan(&user)
		liberr.ErrIsNil(ctx, err, "获取用户信息失败")
		if user == nil {
			return
		}
		if user.UserName == userName {
			liberr.ErrIsNil(ctx, gerror.New("用户名已存在"))
		}
		if user.Mobile == mobile {
			liberr.ErrIsNil(ctx, gerror.New("手机号已存在"))
		}
	})
	return err
}

// GetEditUser 获取编辑用户信息
func (s *sSysUser) GetEditUser(ctx context.Context, id uint64) (res *system.UserGetEditRes, err error) {
	res = new(system.UserGetEditRes)
	err = g.Try(ctx, func(ctx context.Context) {
		//获取用户信息
		res.User, err = s.GetUserInfoById(ctx, id)
		liberr.ErrIsNil(ctx, err)
		//获取已选择的角色信息
		res.CheckedRoleIds, err = s.GetAdminRoleIds(ctx, id)
		liberr.ErrIsNil(ctx, err)
		res.CheckedPosts, err = s.GetUserPostIds(ctx, id)
		liberr.ErrIsNil(ctx, err)
	})
	return
}

// GetUserInfoById 通过Id获取用户信息
func (s *sSysUser) GetUserInfoById(ctx context.Context, id uint64, withPwd ...bool) (user *entity.SysUser, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		if len(withPwd) > 0 && withPwd[0] {
			//用户用户信息
			err = dao.SysUser.Ctx(ctx).Where(dao.SysUser.Columns().Id, id).Scan(&user)
		} else {
			//用户用户信息
			err = dao.SysUser.Ctx(ctx).Where(dao.SysUser.Columns().Id, id).
				FieldsEx(dao.SysUser.Columns().UserPassword, dao.SysUser.Columns().UserSalt).Scan(&user)
		}
		liberr.ErrIsNil(ctx, err, "获取用户数据失败")
	})
	return
}

// GetUserPostIds 获取用户岗位
func (s *sSysUser) GetUserPostIds(ctx context.Context, userId uint64) (postIds []int64, err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		var list []*entity.SysUserPost
		err = dao.SysUserPost.Ctx(ctx).Where(dao.SysUserPost.Columns().UserId, userId).Scan(&list)
		liberr.ErrIsNil(ctx, err, "获取用户岗位信息失败")
		postIds = make([]int64, 0)
		for _, entity := range list {
			postIds = append(postIds, entity.PostId)
		}
	})
	return
}

// ResetUserPwd 重置用户密码
func (s *sSysUser) ResetUserPwd(ctx context.Context, req *system.UserResetPwdReq) (err error) {
	salt := grand.S(10)
	password := libUtils.EncryptPassword(req.Password, salt)
	err = g.Try(ctx, func(ctx context.Context) {
		_, err = dao.SysUser.Ctx(ctx).WherePri(req.Id).Update(g.Map{
			dao.SysUser.Columns().UserSalt:     salt,
			dao.SysUser.Columns().UserPassword: password,
		})
		liberr.ErrIsNil(ctx, err, "重置用户密码失败")
	})
	return
}

func (s *sSysUser) ChangeUserStatus(ctx context.Context, req *system.UserStatusReq) (err error) {
	err = g.Try(ctx, func(ctx context.Context) {
		_, err = dao.SysUser.Ctx(ctx).WherePri(req.Id).Update(do.SysUser{UserStatus: req.UserStatus})
		liberr.ErrIsNil(ctx, err, "设置用户状态失败")
	})
	return
}

// Delete 删除用户
func (s *sSysUser) Delete(ctx context.Context, ids []int) (err error) {
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		err = g.Try(ctx, func(ctx context.Context) {
			_, err = dao.SysUser.Ctx(ctx).TX(tx).Where(dao.SysUser.Columns().Id+" in(?)", ids).Delete()
			liberr.ErrIsNil(ctx, err, "删除用户失败")
			//删除对应权限
			enforcer, e := commonService.CasbinEnforcer(ctx)
			liberr.ErrIsNil(ctx, e)
			for _, v := range ids {
				enforcer.RemoveFilteredGroupingPolicy(0, fmt.Sprintf("%s%d", s.casBinUserPrefix, v))
			}
			//删除用户对应的岗位
			_, err = dao.SysUserPost.Ctx(ctx).TX(tx).Delete(dao.SysUserPost.Columns().UserId+" in (?)", ids)
			liberr.ErrIsNil(ctx, err, "删除用户的岗位失败")
		})
		return err
	})
	return
}

// GetUsers 通过用户ids查询多个用户信息
func (s *sSysUser) GetUsers(ctx context.Context, ids []int) (users []*model.SysUserSimpleRes, err error) {
	if len(ids) == 0 {
		return
	}
	idsSet := gset.NewIntSetFrom(ids).Slice()
	err = g.Try(ctx, func(ctx context.Context) {
		err = dao.SysUser.Ctx(ctx).Where(dao.SysUser.Columns().Id+" in(?)", idsSet).
			Order(dao.SysUser.Columns().Id + " ASC").Scan(&users)
	})
	return
}

// GetDataWhere 获取数据权限判断条件
// Deprecated : 此方法已废弃，请使用更简单的GetAuthWhere方法或GetAuthDeptWhere方法
func (s *sSysUser) GetDataWhere(ctx context.Context, userInfo *model.ContextUser, entityData interface{}, menuId uint) (where g.Map, err error) {
	whereJustMe := g.Map{} //本人数据权限
	t := reflect.TypeOf(entityData)
	err = g.Try(ctx, func(ctx context.Context) {
		for i := 0; i < t.Elem().NumField(); i++ {
			if t.Elem().Field(i).Name == "CreatedBy" {
				//若存在用户id的字段，则生成判断数据权限的条件
				//1、获取当前用户所属角色Ids
				var (
					roleIds   []uint
					scope     []*model.ScopeAuthData
					deptIdArr = gset.New()
					allScope  = false
				)
				roleIds, err = s.GetAdminRoleIds(ctx, userInfo.Id)
				liberr.ErrIsNil(ctx, err)
				scope, err = service.SysRole().GetRoleMenuScope(ctx, roleIds, menuId)
				liberr.ErrIsNil(ctx, err)
				if scope == nil {
					//角色未设置数据权限，默认仅本人数据权限
					whereJustMe = g.Map{"user.id": userInfo.Id}
				} else {
					//2获取角色对应数据权限
					for _, sv := range scope {
						switch sv.DataScope {
						case 1: //全部数据权限
							allScope = true
							goto endLoop
						case 2: //自定数据权限
							deptIdArr.Add(gconv.Interfaces(sv.DeptIds)...)
						case 3: //本部门数据权限
							deptIdArr.Add(gconv.Int64(userInfo.DeptId))
						case 4: //本部门及以下数据权限
							deptIdArr.Add(gconv.Int64(userInfo.DeptId))
							//获取正常状态部门数据
							deptList := ([]*entity.SysDept)(nil)
							deptList, err = service.SysDept().GetList(ctx, &system.DeptSearchReq{Status: "1"})
							liberr.ErrIsNil(ctx, err)
							var dList g.List
							for _, d := range deptList {
								m := g.Map{
									"id":    d.DeptId,
									"pid":   d.ParentId,
									"label": d.DeptName,
								}
								dList = append(dList, m)
							}
							l := libUtils.FindSonByParentId(dList, userInfo.DeptId, "pid", "id")
							for _, li := range l {
								deptIdArr.Add(gconv.Int64(li["id"]))
							}
						case 5: //仅本人数据权限
							whereJustMe = g.Map{"user.id": userInfo.Id}
						}
					}
				}
			endLoop:
				if !allScope && deptIdArr.Size() > 0 {
					where = g.Map{"user.dept_id": deptIdArr.Slice()}
				} else if !allScope && len(whereJustMe) > 0 {
					where = whereJustMe
				}
				break
			}
		}
	})
	return
}

// GetAuthWhere 获取数据权限判断条件-按创建人id获取(created_by),数据权限会跟随创建人转移
func (s *sSysUser) GetAuthWhere(ctx context.Context, m *gdb.Model, userInfo *model.ContextUser, field ...string) *gdb.Model {
	var (
		//当前请求api接口对应的菜单
		url    = gstr.TrimLeft(ghttp.RequestFromCtx(ctx).Request.URL.Path, "/")
		menuId uint
		err    error
		nm     *gdb.Model
	)
	//获取菜单ID
	menuId, err = service.SysAuthRule().GetIdByName(ctx, url)
	if err != nil {
		g.Log().Error(ctx, err)
		return m
	}
	nm, err = s.GetAuthDataWhere(ctx, m, userInfo, menuId, field...)
	if err != nil {
		g.Log().Error(ctx, err)
		return m
	}
	return nm
}

// GetAuthDataWhere 获取数据权限判断条件-按创建用户
func (s *sSysUser) GetAuthDataWhere(ctx context.Context, m *gdb.Model, userInfo *model.ContextUser, menuId uint, field ...string) (nm *gdb.Model, err error) {
	whereJustMe := g.Map{} //本人数据权限
	createdUserField := "created_by"
	//表别名
	tableAlias := ""
	if len(field) > 0 && field[0] != "" {
		tableAlias = field[0]
	}
	if len(field) > 1 && field[1] != "" {
		createdUserField = field[1]
	}

	if tableAlias != "" {
		createdUserField = tableAlias + "." + createdUserField
	}
	err = g.Try(ctx, func(ctx context.Context) {
		//若存在用户id的字段，则生成判断数据权限的条件
		//1、获取当前用户所属角色Ids
		var (
			roleIds   []uint
			scope     []*model.ScopeAuthData
			deptIdArr = gset.New()
		)
		roleIds, err = s.GetAdminRoleIds(ctx, userInfo.Id)
		liberr.ErrIsNil(ctx, err)
		scope, err = service.SysRole().GetRoleMenuScope(ctx, roleIds, menuId)
		liberr.ErrIsNil(ctx, err)
		if scope == nil {
			//角色未设置数据权限，默认仅本人数据权限
			whereJustMe = g.Map{createdUserField: userInfo.Id}
		} else {
			//2获取角色对应数据权限
			for _, sv := range scope {
				switch sv.DataScope {
				case 1: //全部数据权限
					goto endLoop
				case 2: //自定数据权限
					deptIdArr.Add(gconv.Interfaces(sv.DeptIds)...)
				case 3: //本部门数据权限
					deptIdArr.Add(gconv.Int64(userInfo.DeptId))
				case 4: //本部门及以下数据权限
					deptIdArr.Add(gconv.Int64(userInfo.DeptId))
					//获取正常状态部门数据
					deptList := ([]*entity.SysDept)(nil)
					deptList, err = service.SysDept().GetList(ctx, &system.DeptSearchReq{Status: "1"})
					liberr.ErrIsNil(ctx, err)
					var dList g.List
					for _, d := range deptList {
						m := g.Map{
							"id":    d.DeptId,
							"pid":   d.ParentId,
							"label": d.DeptName,
						}
						dList = append(dList, m)
					}
					l := libUtils.FindSonByParentId(dList, userInfo.DeptId, "pid", "id")
					for _, li := range l {
						deptIdArr.Add(gconv.Int64(li["id"]))
					}
				case 5: //仅本人数据权限
					whereJustMe = g.Map{createdUserField: userInfo.Id}
				}
			}
		}
	endLoop:
		if deptIdArr.Size() > 0 {
			nm = m.WhereIn(createdUserField, dao.SysUser.Ctx(ctx).Fields(dao.SysUser.Columns().Id).
				WhereIn(dao.SysUser.Columns().DeptId, deptIdArr.Slice()))
		} else if len(whereJustMe) > 0 {
			nm = m.Where(whereJustMe)
		} else {
			nm = m
		}
	})
	return
}

// GetAuthDeptWhere 获取部门数据权限判断条件-按部门id获取(dept_id),数据权限依赖部门，不会随创建人转移
func (s *sSysUser) GetAuthDeptWhere(ctx context.Context, m *gdb.Model, userInfo *model.ContextUser, field ...string) *gdb.Model {
	var (
		//当前请求api接口对应的菜单
		url    = gstr.TrimLeft(ghttp.RequestFromCtx(ctx).Request.URL.Path, "/")
		menuId uint
		err    error
		nm     *gdb.Model
	)
	//获取菜单ID
	menuId, err = service.SysAuthRule().GetIdByName(ctx, url)
	if err != nil {
		g.Log().Error(ctx, err)
		return m
	}
	nm, err = s.GetAuthDeptDataWhere(ctx, m, userInfo, menuId, field...)
	if err != nil {
		g.Log().Error(ctx, err)
		return m
	}
	return nm
}

// GetAuthDeptDataWhere 获取数据权限判断条件-按部门
func (s *sSysUser) GetAuthDeptDataWhere(ctx context.Context, m *gdb.Model, userInfo *model.ContextUser, menuId uint, field ...string) (nm *gdb.Model, err error) {
	whereJustMe := g.Map{} //本人数据权限
	deptField := "dept_id"
	createdUserField := "created_by"
	//表别名
	tableAlias := ""
	if len(field) > 0 && field[0] != "" {
		tableAlias = field[0]
	}
	if len(field) > 1 && field[1] != "" {
		deptField = field[1]
	}

	if len(field) > 2 && field[2] != "" {
		createdUserField = field[2]
	}

	if tableAlias != "" {
		deptField = tableAlias + "." + deptField
		createdUserField = tableAlias + "." + createdUserField
	}
	err = g.Try(ctx, func(ctx context.Context) {
		//若存在用户id的字段，则生成判断数据权限的条件
		//1、获取当前用户所属角色Ids
		var (
			roleIds   []uint
			scope     []*model.ScopeAuthData
			deptIdArr = gset.New()
		)
		roleIds, err = s.GetAdminRoleIds(ctx, userInfo.Id)
		liberr.ErrIsNil(ctx, err)
		scope, err = service.SysRole().GetRoleMenuScope(ctx, roleIds, menuId)
		liberr.ErrIsNil(ctx, err)
		if scope == nil {
			//角色未设置数据权限，默认仅本人数据权限
			whereJustMe = g.Map{deptField: userInfo.DeptId, createdUserField: userInfo.Id}
		} else {
			//2获取角色对应数据权限
			for _, sv := range scope {
				switch sv.DataScope {
				case 1: //全部数据权限
					goto endLoop
				case 2: //自定数据权限
					deptIdArr.Add(gconv.Interfaces(sv.DeptIds)...)
				case 3: //本部门数据权限
					deptIdArr.Add(gconv.Int64(userInfo.DeptId))
				case 4: //本部门及以下数据权限
					deptIdArr.Add(gconv.Int64(userInfo.DeptId))
					//获取正常状态部门数据
					deptList := ([]*entity.SysDept)(nil)
					deptList, err = service.SysDept().GetList(ctx, &system.DeptSearchReq{Status: "1"})
					liberr.ErrIsNil(ctx, err)
					var dList g.List
					for _, d := range deptList {
						m := g.Map{
							"id":    d.DeptId,
							"pid":   d.ParentId,
							"label": d.DeptName,
						}
						dList = append(dList, m)
					}
					l := libUtils.FindSonByParentId(dList, userInfo.DeptId, "pid", "id")
					for _, li := range l {
						deptIdArr.Add(gconv.Int64(li["id"]))
					}
				case 5: //仅本人数据权限
					whereJustMe = g.Map{deptField: userInfo.Id, createdUserField: userInfo.Id}
				}
			}
		}
	endLoop:
		if deptIdArr.Size() > 0 {
			nm = m.WhereIn(deptField, deptIdArr.Slice())
		} else if len(whereJustMe) > 0 {
			nm = m.Where(whereJustMe)
		} else {
			nm = m
		}
	})
	return
}

// HasAccessByDataWhere 判断用户是否有数据权限
func (s *sSysUser) HasAccessByDataWhere(ctx context.Context, where g.Map, uid interface{}) bool {
	err := g.Try(ctx, func(ctx context.Context) {
		rec, err := dao.SysUser.Ctx(ctx).As("user").
			Fields("user.id").
			Where("user.id", uid).Where(where).One()
		liberr.ErrIsNil(ctx, err)
		if rec.IsEmpty() {
			liberr.ErrIsNil(ctx, gerror.New("没有数据"))
		}
	})
	return err == nil
}

// AccessRule 判断用户是否有某一菜单规则权限
func (s *sSysUser) AccessRule(ctx context.Context, userId uint64, rule string) bool {
	//获取无需验证权限的用户id
	tagSuperAdmin := s.IsSupperAdmin(ctx, userId)
	if tagSuperAdmin {
		return true
	}
	menuList, err := service.SysAuthRule().GetMenuList(ctx)
	if err != nil {
		g.Log().Error(ctx, err)
		return false
	}
	var menu *model.SysAuthRuleInfoRes
	for _, m := range menuList {
		ms := gstr.SubStr(m.Name, 0, gstr.Pos(m.Name, "?"))
		if m.Name == rule || ms == rule {
			menu = m
			break
		}
	}
	// 不存在的规则直接false
	if menu == nil {
		return false
	}
	enforcer, err := commonService.CasbinEnforcer(ctx)
	if err != nil {
		g.Log().Error(ctx, err)
		return false
	}
	hasAccess, err := enforcer.Enforce(fmt.Sprintf("%s%d", service.SysUser().GetCasBinUserPrefix(), userId), gconv.String(menu.Id), "All")
	if err != nil {
		g.Log().Error(ctx, err)
		return false
	}
	return hasAccess
}
