package service import ( "encoding/base64" "gadmin/config" "gadmin/internal/admin/consts" "gadmin/internal/admin/forms" "gadmin/internal/gorm/model" "gadmin/internal/gorm/query" "gadmin/utility/serializer" "gadmin/utility/token" jsoniter "github.com/json-iterator/go" "time" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "golang.org/x/crypto/bcrypt" ) // User 管理员服务 var User = NewsUser() type LoginStats struct { FailNum int BanTs int64 } type sUser struct { BanIps map[string]*LoginStats } func NewsUser() *sUser { user := new(sUser) user.BanIps = make(map[string]*LoginStats) return user } // 一次登录,清空错误次数 func (s *sUser) AddSucessNum(ip string) { delete(s.BanIps, ip) } // 增加错误登录次数,超过10次,不再进行密码验证 func (s *sUser) AddFailNum(ip string) { stats, ok := s.BanIps[ip] if !ok { stats = new(LoginStats) s.BanIps[ip] = stats } if stats.BanTs > 0 { return } if stats.FailNum > 10 { curTs := time.Now().Unix() stats.BanTs = curTs // 清理错误登录 for k, v := range s.BanIps { if v.BanTs+24*60*60 < curTs { delete(s.BanIps, k) } } return } stats.FailNum += 1 } // 检查是否允许登录 func (s *sUser) CheckLoginAllow(ip string) bool { stats, ok := s.BanIps[ip] if !ok { return true } cur := time.Now().Unix() if stats.BanTs+1*60*60 > cur { return false } stats.BanTs = 0 stats.FailNum = 0 return true } // GetUser 用ID获取用户 func (s *sUser) GetUser(ID interface{}) (u *model.AdminUser, err error) { result := config.DB.First(&u, ID) return u, result.Error } // SetPassword 设置密码 func (s *sUser) SetPassword(password string) (string, error) { bytes, err := bcrypt.GenerateFromPassword([]byte(password), consts.PassWordCost) if err != nil { return "", err } return string(bytes), nil } // CheckPassword 校验密码 func (s *sUser) CheckPasswordRight(passwordDigest, password string) bool { err := bcrypt.CompareHashAndPassword([]byte(passwordDigest), []byte(password)) return err == nil } func (s *sUser) Login(req forms.UserLoginReq, ip string) serializer.Response { var ( u model.AdminUser ) if ok := s.CheckLoginAllow(ip); !ok { return serializer.ParamErr("账号已被禁止登录", nil) } if err := config.AdminDB.Where("user_name = ?", req.UserName).First(&u).Error; err != nil { s.AddFailNum(ip) logrus.Warnf("sUser %v Login req error %v.", req.UserName, ip) return serializer.ParamErr("账号错误或不存在,请检查", nil) } //p, _ := s.SetPassword(req.Password) //fmt.Println("pass:", p) if u.Status != 1 { return serializer.ParamErr("账号已被禁用", nil) } if s.CheckPasswordRight(u.PasswordDigest, req.Password) == false { s.AddFailNum(ip) logrus.Warnf("sUser %v Login req error. ip: %v.", req.UserName, ip) return serializer.ParamErr("密码不正确,如忘记了密码请联系管理员!", nil) } s.AddSucessNum(ip) //if os.Getenv("GIN_MODE") == "release" && u.UserName == "mojun" { // return serializer.ParamErr("该账号只允许在测试服登录!", nil) //} t := token.GenerateTokenUsingUUID() // 记录登录token key := config.GetUserTokenKey(u.ID) config.TokenRedis.HSet(key, t, time.Now().Unix()) config.TokenRedis.Expire(key, config.TokenExpireTime) tokenKey := config.GetTokenKey(t) user := &token.UserClaims{ ID: u.ID, UserName: u.UserName, RoleId: int64(u.RoleID), Avatar: u.Avatar, Nickname: u.Nickname, AccessToken: t, } userStr, err := jsoniter.MarshalToString(user) if err != nil { return serializer.Err(1, "", err) } config.TokenRedis.Set(tokenKey, userStr, config.TokenExpireTime) return serializer.Suc(forms.UserLoginRes{ ID: u.ID, UserName: u.UserName, Nickname: u.Nickname, Status: u.Status, Avatar: u.Avatar, Token: base64.URLEncoding.EncodeToString([]byte(t)), }) //t, err := token.GenerateToken(&token.UserClaims{ // ID: u.ID, // UserName: u.UserName, // RoleId: int64(u.RoleID), // Avatar: u.Avatar, // Nickname: u.Nickname, //}) //if err != nil { // return serializer.ParamErr(err.Error(), nil) //} //return serializer.Suc(forms.UserLoginRes{ // ID: u.ID, // UserName: u.UserName, // Nickname: u.Nickname, // Status: u.Status, // Avatar: u.Avatar, // Token: t, //}) } func (s *sUser) List(ctx *gin.Context, req forms.AdminUserListReq) serializer.Response { var ( q = query.Use(config.AdminDB).AdminUser r = query.Use(config.AdminDB).AdminRole m = q.WithContext(ctx) offset int64 = 0 models forms.UserAccountListRes lists []*forms.AdminUserListModel ) m = m.Order(q.ID.Desc()) req.Page, req.PerPage, offset = forms.CalculatePage(req.Page, req.PerPage) count, err := m.Count() if err != nil { return serializer.Err(consts.CodeParamErr, "查询出错 count", err) } if count > 0 { if err = m.Limit(int(req.PerPage)).Offset(int(offset)).Scan(&lists); err != nil { return serializer.Err(consts.CodeParamErr, "查询出错 lists", err) } } for _, v := range lists { role, err := r.WithContext(ctx).Where(r.ID.Eq(v.RoleID)).First() v.RoleName = "未绑定角色" if role != nil && err == nil { v.RoleName = role.Name } } models.List = lists models.Page = req.Page models.PerPage = req.PerPage models.PageCount = (count + req.PerPage - 1) / req.PerPage return serializer.Suc(models) } func (s *sUser) Edit(ctx *gin.Context, req forms.AdminUserEditReq) serializer.Response { q := query.Use(config.AdminDB).AdminUser logrus.Warnf("sUser Edit req:%+v", req) if req.RoleID <= 0 { return serializer.ParamErr("角色不能为空", nil) } if req.UserName == "" { return serializer.ParamErr("用户名不能为空", nil) } // 修改 if req.ID > 0 { update := &model.AdminUser{ UserName: req.UserName, RoleID: int32(req.RoleID), Nickname: req.Nickname, Status: req.Status, UpdatedAt: time.Now(), } _, err := q.WithContext(ctx).Where(q.ID.Eq(req.ID)).Updates(update) if err != nil { return serializer.Err(consts.CodeParamErr, "更新出错", err) } // 维护redis token config.TokenRedis.Del(config.GetUserTokenKey(req.ID)) return serializer.Suc(nil) } if req.Password == "" { return serializer.ParamErr("密码不能为空", nil) } // 加密密码 password, err := s.SetPassword(req.Password) if err != nil { return serializer.Err( consts.CodeEncryptError, "密码加密失败", err, ) } // 新增 create := &model.AdminUser{ UserName: req.UserName, RoleID: int32(req.RoleID), Nickname: req.Nickname, PasswordDigest: password, Status: req.Status, UpdatedAt: time.Now(), CreatedAt: time.Now(), } if err = query.Use(config.AdminDB).AdminUser.WithContext(ctx).Create(create); err != nil { return serializer.DBErr(err.Error(), err) } return serializer.Suc(nil) } func (s *sUser) ResetPassword(ctx *gin.Context, req forms.AdminUserResetPasswordReq) serializer.Response { q := query.Use(config.AdminDB).AdminUser logrus.Warnf("sUser ResetPassword req:%+v", req) if req.ID <= 0 { return serializer.ParamErr("用于ID不能为空", nil) } if req.Password == "" { return serializer.ParamErr("密码不能为空", nil) } // 加密密码 password, err := s.SetPassword(req.Password) if err != nil { return serializer.Err( consts.CodeEncryptError, "密码加密失败", err, ) } update := &model.AdminUser{ PasswordDigest: password, UpdatedAt: time.Now(), } _, err = q.WithContext(ctx).Where(q.ID.Eq(req.ID)).Updates(update) if err != nil { return serializer.Err(consts.CodeParamErr, "更新出错", err) } // 维护redis token config.TokenRedis.Del(config.GetUserTokenKey(req.ID)) return serializer.Suc(nil) } func (s *sUser) UpdatePassword(ctx *gin.Context, req forms.AdminUserUpdatePasswordReq) serializer.Response { q := query.Use(config.AdminDB).AdminUser logrus.Warnf("sUser UpdatePassword req:%+v", req) userId := token.GetUID(ctx) if userId <= 0 { return serializer.ParamErr("用户信息获取失败", nil) } models, err := q.WithContext(ctx).Where(q.ID.Eq(userId)).First() if err != nil { return serializer.ParamErr(err.Error(), err) } if models == nil { return serializer.ParamErr("用户不存在", nil) } if !s.CheckPasswordRight(models.PasswordDigest, req.OldPassword) { return serializer.ParamErr("老密码不正确", nil) } // 加密密码 password, err := s.SetPassword(req.NewPassword) if err != nil { return serializer.Err( consts.CodeEncryptError, "密码加密失败", err, ) } update := &model.AdminUser{ PasswordDigest: password, UpdatedAt: time.Now(), } _, err = q.WithContext(ctx).Where(q.ID.Eq(userId)).Updates(update) if err != nil { return serializer.Err(consts.CodeParamErr, "更新出错", err) } // 修改token状态 config.TokenRedis.Del(config.GetUserTokenKey(userId)) return serializer.Suc(nil) } func (s *sUser) GetUserInfo(c *gin.Context) (*model.AdminUser, error) { q := query.Use(config.AdminDB).AdminUser userId := token.GetUID(c) if userId <= 0 { c.JSON(200, serializer.Err(consts.CodeNoPermission, "用户信息获取失败", nil)) c.Abort() } return q.WithContext(c).Where(q.ID.Eq(userId)).First() } func (s *sUser) GetUserRoleId(c *gin.Context) int64 { info, err := s.GetUserInfo(c) if err != nil { return 0 } if info == nil { return 0 } return int64(info.RoleID) } func (s *sUser) GetUserByUnionID(unionID string) (*model.AdminUser, error) { q := query.Use(config.AdminDB).AdminUser return q.Where(q.FeishuUnionID.Eq(unionID)).First() } func (s *sUser) GetUserRolePermission(c *gin.Context) (bool, error) { // 只有超管拥有角色管理权限 roleId, _ := c.Get("admin_role_id") return config.IsSuperRole(roleId.(int64)), nil //res, ok := c.Get("user") //if !ok { // c.JSON(200, serializer.CheckLogin()) // c.Abort() //} //user := res.(*token.UserClaims) //if user.RoleId == 1 { // 超管拥有权限 // return true, nil //} //// 查询玩家是否拥有权限管理页面的入口 //pageQ := query.Use(config.AdminDB).AdminMenu //pageIds := make([]int32, 0) //err := pageQ.WithContext(c).Where(pageQ.Path.Eq("/permission")).Pluck(pageQ.ID, &pageIds) //if err != nil { // logrus.WithField("from", "AdminMenu Pluck").Error(err) // return false, err //} //rmq := query.Use(config.AdminDB).AdminRoleMenu //count, err := rmq.WithContext(c).Where(rmq.RoleID.Eq(int32(user.RoleId)), rmq.PageID.In(pageIds...)).Count() //if err != nil { // logrus.WithField("from", "AdminRoleMenu Count").Error(err) // return false, err //} //if count > 0 { // return true, nil //} else { // return false, nil //} }