package user_controller import ( ginI18n "github.com/gin-contrib/i18n" "github.com/gin-gonic/gin" "github.com/yitter/idgenerator-go/idgen" "gorm.io/gorm" "reflect" "schisandra-cloud-album/common/constant" "schisandra-cloud-album/common/enum" "schisandra-cloud-album/common/randomname" "schisandra-cloud-album/common/redis" "schisandra-cloud-album/common/result" "schisandra-cloud-album/global" "schisandra-cloud-album/model" "schisandra-cloud-album/utils" "strconv" "time" ) // GetUserList // @Summary 获取所有用户列表 // @Tags 用户模块 // @Success 200 {string} json // @Router /controller/auth/user/List [get] func (UserController) GetUserList(c *gin.Context) { userList := userService.GetUserListService() result.OkWithData(userList, c) } // QueryUserByUsername // @Summary 根据用户名查询用户 // @Tags 用户模块 // @Param username query string true "用户名" // @Success 200 {string} json // @Router /controller/auth/user/query_by_username [get] func (UserController) QueryUserByUsername(c *gin.Context) { username := c.Query("username") user := userService.QueryUserByUsernameService(username) if reflect.DeepEqual(user, model.ScaAuthUser{}) { result.FailWithMessage(ginI18n.MustGetMessage(c, "NotFoundUser"), c) return } result.OkWithData(user, c) } // QueryUserByUuid // @Summary 根据uuid查询用户 // @Tags 用户模块 // @Param uid query string true "用户uid" // @Success 200 {string} json // @Router /controller/auth/user/query_by_uid [get] func (UserController) QueryUserByUuid(c *gin.Context) { uid := c.Query("uid") user := userService.QueryUserByUuidService(&uid) if user.ID == 0 { result.FailWithMessage(ginI18n.MustGetMessage(c, "NotFoundUser"), c) return } result.OkWithData(user, c) } // DeleteUser 删除用户 // @Summary 删除用户 // @Tags 用户模块 // @Param uid query string true "用户uid" // @Success 200 {string} json // @Router /controller/auth/user/delete [delete] func (UserController) DeleteUser(c *gin.Context) { uid := c.Query("uid") err := userService.DeleteUserService(uid) if err != nil { result.FailWithMessage(ginI18n.MustGetMessage(c, "DeletedFailed"), c) return } result.OkWithMessage(ginI18n.MustGetMessage(c, "DeletedSuccess"), c) } // QueryUserByPhone 根据手机号查询用户 // @Summary 根据手机号查询用户 // @Tags 用户模块 // @Param phone query string true "手机号" // @Success 200 {string} json // @Router /controller/auth/user/query_by_phone [get] func (UserController) QueryUserByPhone(c *gin.Context) { phone := c.Query("phone") user := userService.QueryUserByPhoneService(phone) if user.ID == 0 { result.FailWithMessage(ginI18n.MustGetMessage(c, "NotFoundUser"), c) return } result.OkWithData(user, c) } // AccountLogin 账号登录 // @Summary 账号登录 // @Tags 用户模块 // @Param user body AccountLoginRequest true "用户信息" // @Success 200 {string} json // @Router /controller/user/login [post] func (UserController) AccountLogin(c *gin.Context) { accountLoginRequest := AccountLoginRequest{} err := c.ShouldBindJSON(&accountLoginRequest) if err != nil { result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c) return } rotateData := utils.CheckRotateData(accountLoginRequest.Angle, accountLoginRequest.Key) if !rotateData { result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaVerifyError"), c) return } account := accountLoginRequest.Account password := accountLoginRequest.Password var user model.ScaAuthUser if utils.IsPhone(account) { user = userService.QueryUserByPhoneService(account) } else if utils.IsEmail(account) { user = userService.QueryUserByEmailService(account) } else if utils.IsUsername(account) { user = userService.QueryUserByUsernameService(account) } else { result.FailWithMessage(ginI18n.MustGetMessage(c, "AccountErrorFormat"), c) return } if user.ID == 0 { result.FailWithMessage(ginI18n.MustGetMessage(c, "NotFoundUser"), c) return } if !utils.Verify(*user.Password, password) { result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c) return } handelUserLogin(user, accountLoginRequest.AutoLogin, c) } // PhoneLogin 手机号登录/注册 // @Summary 手机号登录/注册 // @Tags 用户模块 // @Param user body PhoneLoginRequest true "用户信息" // @Success 200 {string} json // @Router /controller/user/phone_login [post] func (UserController) PhoneLogin(c *gin.Context) { request := PhoneLoginRequest{} err := c.ShouldBind(&request) if err != nil { result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c) return } phone := request.Phone captcha := request.Captcha autoLogin := request.AutoLogin if !utils.IsPhone(phone) { result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneErrorFormat"), c) return } userChan := make(chan model.ScaAuthUser) go func() { user := userService.QueryUserByPhoneService(phone) userChan <- user }() user := <-userChan close(userChan) if user.ID == 0 { // 未注册 codeChan := make(chan *string) go func() { code := redis.Get(constant.UserLoginSmsRedisKey + phone).Val() codeChan <- &code }() code := <-codeChan close(codeChan) if code == nil { result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c) return } uid := idgen.NextId() uidStr := strconv.FormatInt(uid, 10) avatar, err := utils.GenerateAvatar(uidStr) if err != nil { global.LOG.Errorln(err) return } name := randomname.GenerateName() createUser := model.ScaAuthUser{ UID: &uidStr, Phone: &phone, Avatar: &avatar, Nickname: &name, Gender: &enum.Male, } errChan := make(chan error) go func() { err := global.DB.Transaction(func(tx *gorm.DB) error { addUser, err := userService.AddUserService(createUser) if err != nil { return err } _, err = global.Casbin.AddRoleForUser(uidStr, enum.User) if err != nil { return err } handelUserLogin(*addUser, autoLogin, c) return nil }) errChan <- err }() err = <-errChan close(errChan) if err != nil { result.FailWithMessage(ginI18n.MustGetMessage(c, "RegisterUserError"), c) return } } else { codeChan := make(chan string) go func() { code := redis.Get(constant.UserLoginSmsRedisKey + phone).Val() codeChan <- code }() code := <-codeChan close(codeChan) if code == "" { result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c) return } if captcha != code { result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c) return } handelUserLogin(user, autoLogin, c) } } // RefreshHandler 刷新token // @Summary 刷新token // @Tags 用户模块 // @Param refresh_token query string true "刷新token" // @Success 200 {string} json // @Router /controller/token/refresh [post] func (UserController) RefreshHandler(c *gin.Context) { request := RefreshTokenRequest{} if err := c.ShouldBindJSON(&request); err != nil { result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c) return } refreshToken := request.RefreshToken parseRefreshToken, isUpd, err := utils.ParseRefreshToken(refreshToken) if err != nil || !isUpd { global.LOG.Errorln(err) result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c) return } accessTokenString, err := utils.GenerateAccessToken(utils.AccessJWTPayload{UserID: parseRefreshToken.UserID}) if err != nil { result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c) return } tokenKey := constant.UserLoginTokenRedisKey + *parseRefreshToken.UserID token, err := redis.Get(tokenKey).Result() if err != nil || token == "" { global.LOG.Errorln(err) result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c) return } data := ResponseData{ AccessToken: accessTokenString, RefreshToken: refreshToken, UID: parseRefreshToken.UserID, } if err := redis.Set(tokenKey, data, time.Hour*24*7).Err(); err != nil { result.FailWithMessage(ginI18n.MustGetMessage(c, "LoginExpired"), c) return } result.OkWithData(data, c) } // ResetPassword 重置密码 // @Summary 重置密码 // @Tags 用户模块 // @Param user body ResetPasswordRequest true "用户信息" // @Success 200 {string} json // @Router /controller/user/reset_password [post] func (UserController) ResetPassword(c *gin.Context) { var resetPasswordRequest ResetPasswordRequest if err := c.ShouldBindJSON(&resetPasswordRequest); err != nil { result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c) return } phone := resetPasswordRequest.Phone captcha := resetPasswordRequest.Captcha password := resetPasswordRequest.Password repassword := resetPasswordRequest.Repassword if !utils.IsPhone(phone) { result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneError"), c) return } if password != repassword { result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordNotSame"), c) return } if !utils.IsPassword(password) { result.FailWithMessage(ginI18n.MustGetMessage(c, "PasswordError"), c) return } // 使用事务确保验证码检查和密码更新的原子性 tx := global.DB.Begin() defer func() { if r := recover(); r != nil { tx.Rollback() } }() if err := tx.Error; err != nil { result.FailWithMessage(ginI18n.MustGetMessage(c, "DatabaseError"), c) return } code := redis.Get(constant.UserLoginSmsRedisKey + phone).Val() if code == "" { result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaExpired"), c) return } if captcha != code { result.FailWithMessage(ginI18n.MustGetMessage(c, "CaptchaError"), c) return } // 验证码检查通过后立即删除或标记为已使用 if err := redis.Del(constant.UserLoginSmsRedisKey + phone).Err(); err != nil { tx.Rollback() result.FailWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordError"), c) return } user := userService.QueryUserByPhoneService(phone) if reflect.DeepEqual(user, model.ScaAuthUser{}) { result.FailWithMessage(ginI18n.MustGetMessage(c, "PhoneNotRegister"), c) return } encrypt, err := utils.Encrypt(password) if err != nil { result.FailWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordError")+": "+err.Error(), c) return } if err := userService.UpdateUserService(phone, encrypt); err != nil { tx.Rollback() result.FailWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordError"), c) return } tx.Commit() result.OkWithMessage(ginI18n.MustGetMessage(c, "ResetPasswordSuccess"), c) } // Logout 退出登录 // @Summary 退出登录 // @Tags 用户模块 // @Success 200 {string} json // @Router /controller/auth/user/logout [post] func (UserController) Logout(c *gin.Context) { userId := c.Query("user_id") if userId == "" { global.LOG.Errorln("userId is empty") result.FailWithMessage(ginI18n.MustGetMessage(c, "ParamsError"), c) return } tokenKey := constant.UserLoginTokenRedisKey + userId del := redis.Del(tokenKey) if del.Err() != nil { global.LOG.Errorln(del.Err()) result.FailWithMessage(ginI18n.MustGetMessage(c, "LogoutFailed"), c) return } ip := utils.GetClientIP(c) key := constant.UserLoginClientRedisKey + ip del = redis.Del(key) if del.Err() != nil { global.LOG.Errorln(del.Err()) result.FailWithMessage(ginI18n.MustGetMessage(c, "LogoutFailed"), c) return } result.OkWithMessage(ginI18n.MustGetMessage(c, "LogoutSuccess"), c) }