package service import ( "errors" "fmt" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/entity" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/request" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/bean/vo/response" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/client" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/conf" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/common/tools" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/pkg/beagle/resp" "gitlab.wodcloud.com/smart-operation/so-operation-api/src/util" "strconv" dysmsapi "github.com/aliyun/alibaba-cloud-sdk-go/services/dysmsapi" "image/color" "strings" "time" "github.com/go-redis/redis" "github.com/mojocn/base64Captcha" uuid "github.com/satori/go.uuid" "github.com/spf13/cast" "go.uber.org/zap" "k8s.io/apimachinery/pkg/util/json" ) type UserSvc struct { SystemAccount string PassWord string } // 获取用户信息(登录前) func (u *UserSvc) GetUserInfo(phone string) (userInfo entity.SystemUserInfo, err error) { db, err := client.GetDbClient() if err != nil { return userInfo, resp.DbConnectError.ErrorDetail(err) } // 查询用户 userModel := db.Table("system_user") userModel.Join("LEFT", []string{"system_organization", "so"}, "so.organization_id = system_user.organization_id") userModel.Select("system_user.*,so.name as org_name") if phone == "" { userModel.Where("system_account = ?", u.SystemAccount) } else { userModel.Where("system_user.phone = ?", phone) } _, err = userModel.Where("system_user.state = 1").Get(&userInfo) if err != nil { return userInfo, resp.DbSelectError.ErrorDetail(err) } return userInfo, nil } func (u *UserSvc) GetCurUser(token string) (m entity.SystemUserInfo, err error) { rcon, err := client.GetRedisClient() if err != nil { return m, resp.RedisConnectError.WithError(err) } result, err := rcon.Get(token) if err != nil { if err.Error() == "redis: nil" { err = errors.New("登录超时") return m, resp.FAIL.WithError(err) } else { conf.Logger.Error("获取token失败", zap.Error(err)) return m, resp.FAIL.WithError(err) } } if result == "" { err = errors.New("登录超时") return m, resp.FAIL.WithError(err) } err = json.Unmarshal([]byte(result), &m) if err != nil { return m, resp.FAIL.WithError(err) } m.Password = "" op := SystemOptionsSvc{} config, err := op.GetSystemOptions() if config.SessionValidity > 0 { expireTime := time.Duration(config.SessionValidity) _, _ = rcon.Expire(token, expireTime*time.Minute) } return } func (u *UserSvc) UserLogout(cookie string) (msg string, err error) { // 清理redis redisCli, err := client.GetRedisClient() if err != nil { return "", resp.FAIL.WithError(err) } err = redisCli.Del(cookie) if err != nil { msg = "登出失败" return msg, resp.FAIL.WithError(err) } return "登出成功", nil } func (u *UserSvc) GetCaptcha(width, height int) (captcha response.Captcha, err error) { param := tools.ConfigJsonBody{ CaptchaType: "string", DriverString: &base64Captcha.DriverString{ Height: height, Width: width, NoiseCount: 0, ShowLineOptions: 2, Length: 4, Source: "124567890", BgColor: &color.RGBA{255, 255, 255, 1}, //Fonts: []string{"3Dumb.ttf", "ApothecaryFont.ttf", "Comismsh.ttf", "DENNEthree-dee.ttf", "DeborahFancyDress.ttf", // "Flim-Flam.ttf", "RitaSmith.ttf", "actionj.ttf", "chromohv.ttf"}, Fonts: []string{"DENNEthree-dee.ttf"}, }, } captcha.Id, captcha.Captcha, err = tools.GenerateCaptcha(param) return } func (u *UserSvc) VerifyCaptcha(id, value string) (err error) { param := tools.ConfigJsonBody{} param.Id = id param.VerifyValue = strings.ToLower(value) right := tools.Verify(param) if !right { err = errors.New("验证码不正确") conf.Logger.Error("验证码不正确", zap.Error(err)) } return } // 用户锁定(此逻辑包含锁定规则以及锁定key) func (u *UserSvc) UserLock(userId int, pwd string, config *entity.SystemPreferenceConfig) (err error) { r, err := client.GetRedisClient() if err != nil { return resp.RedisConnectError.ErrorDetail(err) } startLockKey := fmt.Sprintf("%s-%v", client.RULEKEY, userId) lockSystemKey := fmt.Sprintf("%s-%v", client.LOCKKEY, userId) errorNum, err := r.Get(startLockKey) if err != nil && err != redis.Nil { conf.Logger.Error("获取密码插入次数失败", zap.Error(err)) return resp.RedisExecError.ErrorDetail(err) } else if errorNum == "" { if err := r.Set(startLockKey, 0, time.Duration(config.LoginLimitTime)*time.Hour); err != nil { conf.Logger.Error("密码插入次数插入redis失败", zap.Error(err)) return resp.RedisExecError.ErrorDetail(err) } } else if cast.ToInt(errorNum) >= config.LoginPwdError { // 错误上限, 自动锁定 has, err := r.Exist(lockSystemKey) if err != nil { return err } if has == 1 { // 获取锁定规则过期时间 ttl, err := r.Ttl(startLockKey) if err != nil { conf.Logger.Error("获取锁定规则过期时间失败", zap.Error(err)) return resp.RedisExecError.ErrorDetail(err) } return resp.FAIL.ErrorDetail(fmt.Errorf("您已经%s次输错密码,账号已锁定,请在%s分后重试!", errorNum, ttl/60)) } // 如果不存在过期时间key,则代表锁定时间已过期,删除锁定规则,重新进行新的锁定规则 err = r.Del(startLockKey) if err != nil { conf.Logger.Error("删除锁定规则文件失败", zap.Error(err)) return resp.RedisExecError.ErrorDetail(err) } } // 判断是否输错密码 incr, err := r.Incr(startLockKey) if err != nil { conf.Logger.Error("写入错误次数失败", zap.Error(err)) return err } conf.Logger.Info("当前错误次数为", zap.Int64("incr", incr)) if int(incr) >= config.LoginPwdError { err = r.Set(lockSystemKey, 0, time.Duration(config.LoginLockTime)*time.Minute) if err != nil { return err } } return fmt.Errorf("请正确输入密码,连续%d次输错密码,账号将被锁定,还可重试%d次", incr, config.LoginPwdError-cast.ToInt(incr)) } func (u *UserSvc) UserUnLock(userId int) error { startLockKey := fmt.Sprintf("%s-%v", client.RULEKEY, userId) r, err := client.GetRedisClient() if err != nil { return resp.RedisConnectError.ErrorDetail(err) } // 密码正确 删除 锁定文件 if err := r.Del(startLockKey); err != nil { conf.Logger.Error("删除锁定文件错误", zap.Error(err)) return err } return nil } // 登录 func (u *UserSvc) LoginV2(userInfo entity.SystemUserInfo) (last_login_time, msg string, uuidStr string, err error) { redisCli, err := client.GetRedisClient() if err != nil { return "", "", "", resp.RedisConnectError.ErrorDetail(err) } // TODO 用户数据、登录时间存入redis last_login_time, uuidStr, err = u.SaveUserInfo(userInfo, redisCli) if err != nil { conf.Logger.Error("保存用户数据失败", zap.Error(err)) return "", "", "", resp.RedisExecError.ErrorDetail(err) } msg = "登录成功" conf.Logger.Info("登录成功", zap.String("msg", msg)) go func() { // 最后心跳时间 finalHeartBeatMap := map[string]interface{}{ "final_heartbeat_unix": time.Now().UnixNano() / 1e6, "final_heartbeat_flag": false, } finalHeartBeatByte, _ := json.Marshal(finalHeartBeatMap) redisCli.HSet(conf.FinalHeartBeatUnixKey, strconv.Itoa(userInfo.Id), string(finalHeartBeatByte)) }() return } // 保存用户信息 func (u *UserSvc) SaveUserInfo(userInfo entity.SystemUserInfo, r client.Redis) (last_login, uuidStr string, err error) { uu := uuid.NewV4() uuidStr = uu.String() // 存入redis b, err := json.Marshal(userInfo) op := SystemOptionsSvc{} config, err := op.GetSystemOptions() expireTime := time.Duration(config.SessionValidity) if err != nil { return "", "", err } err = r.Set(uuidStr, string(b), time.Minute*expireTime) if err != nil { conf.Logger.Error("保存用户数据失败", zap.Error(err)) return "", "", err } // 登录时间计录在map loginMap := make(map[string]string, 0) cont, _ := r.Get("LOGIN-TIME") if cont != "" { err = json.Unmarshal([]byte(cont), &loginMap) if err != nil { return "", "", err } } now := time.Now().Format(conf.LocalDateTimeFormat) if _, ok := loginMap[u.SystemAccount]; ok { last_login = loginMap[u.SystemAccount] } else { last_login = now } loginMap[u.SystemAccount] = now a, err := json.Marshal(loginMap) if err != nil { return "", "", err } err = r.Set("LOGIN-TIME", string(a), -1) if err != nil { conf.Logger.Error("登录失败", zap.Error(err)) return "", "", err } // 保存用户id以及token信息 err = r.LPush(strconv.Itoa(userInfo.Id), uuidStr) if err != nil { conf.Logger.Error("保存用户信息失败", zap.Error(err)) return "", "", err } return last_login, uuidStr, nil } // 获取短信验证码 func (u *UserSvc) GetSmsVerifyCode(phone string) (data interface{}, err error) { smsClient, err := dysmsapi.NewClientWithAccessKey("cn-hangzhou", conf.Options.SmsAccessKeyId, conf.Options.SmsAccessSecret) if err != nil { conf.Logger.Error("dysmsapi client error", zap.Error(err)) return nil, resp.FAIL.ErrorDetail(err) } code := util.Rand6() params := map[string]interface{}{"code": code} templateParam, err := json.Marshal(params) if err != nil { conf.Logger.Error("序列化模板失败!", zap.Error(err)) return nil, resp.FAIL.ErrorDetail(err) } request := dysmsapi.CreateSendSmsRequest() request.Scheme = "https" request.PhoneNumbers = phone request.TemplateCode = conf.Options.SmsTemplateLogin request.SignName = conf.Options.SmsSignName request.TemplateParam = string(templateParam) req, err := smsClient.SendSms(request) if err != nil { return nil, resp.FAIL.ErrorDetail(err) } data, err = u.SetCodeToRedis(phone, code) if err != nil { conf.Logger.Error("set to cache is error!", zap.Error(err)) return nil, resp.FAIL.ErrorDetail(err) } fmt.Printf("response is %#v\n", req) return data, nil } // 验证码保存 func (u *UserSvc) SetCodeToRedis(phone, code string) (data interface{}, err error) { rcon, err := client.GetRedisClient() if err != nil { return nil, resp.RedisConnectError.ErrorDetail(err) } err = rcon.Set(phone, code, time.Minute*5) if err != nil { return nil, resp.DbInsertError.ErrorDetail(err) } str, err := rcon.Get(phone) if err != nil { return nil, resp.RedisExecError.ErrorDetail(err) } return str, nil } // 校验验证码是否过期 func (u *UserSvc) VerifyLoginCodeExpire(phone, code string, redisCli client.Redis) error { str, err := redisCli.Get(phone) if err != nil { conf.Logger.Error("code不存在!") } if str != code { return errors.New("验证码无效!") } return nil } func (u *UserSvc) VerifyPhoneCode(phone string) (exist int64, err error) { rcon, err := client.GetRedisClient() if err != nil { return 0, err } exist, err = rcon.Exist(phone) if err != nil { return 0, err } return exist, nil } // 手机号登录 func (u *UserSvc) UserPhoneLogin(req request.SendPhoneMsg, userInfo entity.SystemUserInfo) (last_login_time, msg string, uuidStr string, err error) { redisCli, err := client.GetRedisClient() if err != nil { return "", "", "", resp.RedisConnectError.ErrorDetail(err) } // TODO 用户数据、登录时间存入redis last_login_time, uuidStr, err = u.SaveUserInfo(userInfo, redisCli) if err != nil { conf.Logger.Error("保存用户数据失败", zap.Error(err)) return "", "", "", resp.RedisExecError.ErrorDetail(err) } msg = "登录成功" conf.Logger.Info("登录成功", zap.String("msg", msg)) // 登录成功,清除验证码 go func() { // 最后心跳时间 finalHeartBeatMap := map[string]interface{}{ "final_heartbeat_unix": time.Now().UnixNano() / 1e6, "final_heartbeat_flag": false, } finalHeartBeatByte, _ := json.Marshal(finalHeartBeatMap) redisCli.HSet(conf.FinalHeartBeatUnixKey, strconv.Itoa(userInfo.Id), string(finalHeartBeatByte)) redisCli.Del(req.Phone) }() return } // 根据手机号获取账户 func (u *UserSvc) PhoneToAccount(phone string) (data string, err error) { db, err := client.GetDbClient() if err != nil { err = resp.DbConnectError.ErrorDetail(err) return } systemUser := entity.SystemUser{} _, err = db.Table("system_user").Select("system_account").Where("phone = ?", phone).Get(&systemUser) if err != nil { conf.Logger.Error("获取账号信息失败", zap.Error(err)) return "", resp.DbSelectError.ErrorDetail(err) } return systemUser.SystemAccount, nil } func (u *UserSvc) ForgetPwdCheck(req request.ForgetPwdCheck) (err error) { rcon, err := client.GetRedisClient() if err != nil { return resp.RedisConnectError.ErrorDetail(err) } str, err := rcon.Get(req.Phone) if err != nil { return resp.RedisExecError.ErrorDetail(err) } if str != req.Code { return resp.FAIL.ErrorDetail(errors.New("验证码不正确")) } return } func (u *UserSvc) UpdateAccountPwd(req request.AccountForgetPasswordReq) (err error) { db, err := client.GetDbClient() if err != nil { err = resp.DbConnectError.ErrorDetail(err) return } oldSystemUser := entity.SystemUser{} has, err := db.Cols("password", "id").Where("phone = ?", req.Phone).Get(&oldSystemUser) if err != nil || !has { conf.Logger.Error("查询系统账户详情失败", zap.Error(err)) err = resp.DbSelectError.ErrorDetail(errors.New("查询系统账户详情失败")) return } password, err := SolvePassword(oldSystemUser.Id, req.Password) if err != nil { return } SystemUser := entity.SystemUser{} SystemUser.Password = password SystemUser.PwdForceStatus = 0 SystemUser.PwdUpdatedTime = time.Now() _, err = db.Where("id =?", oldSystemUser.Id).Where("is_deleted = 0").Cols("password", "pwd_force_status", "pwd_updated_time").Update(&SystemUser) if err != nil { conf.Logger.Error("修改系统账户失败", zap.Error(err)) err = resp.DbUpdateError.ErrorDetail(errors.New("修改系统账户失败")) return } return }