mirror of https://gitee.com/answerdev/answer.git
feat: support update username
This commit is contained in:
parent
22251849d9
commit
c39e115cde
|
@ -74,8 +74,10 @@ error:
|
|||
other: "user not found"
|
||||
suspended:
|
||||
other: "user is suspended"
|
||||
|
||||
|
||||
username_invalid:
|
||||
other: "username is invalid"
|
||||
username_duplicate:
|
||||
other: "username is already in use"
|
||||
|
||||
report:
|
||||
spam:
|
||||
|
|
|
@ -55,7 +55,7 @@ func BindAndCheck(ctx *gin.Context, data interface{}) bool {
|
|||
|
||||
errField, err := validator.GetValidatorByLang(lang.Abbr()).Check(data)
|
||||
if err != nil {
|
||||
HandleResponse(ctx, myErrors.New(http.StatusBadRequest, reason.RequestFormatError).WithMsg(err.Error()), errField)
|
||||
HandleResponse(ctx, err, errField)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -24,6 +24,8 @@ const (
|
|||
DisallowVoteYourSelf = "error.object.disallow_vote_your_self"
|
||||
CaptchaVerificationFailed = "error.object.captcha_verification_failed"
|
||||
UserNotFound = "error.user.not_found"
|
||||
UsernameInvalid = "error.user.username_invalid"
|
||||
UsernameDuplicate = "error.user.username_duplicate"
|
||||
EmailDuplicate = "error.email.duplicate"
|
||||
EmailVerifyUrlExpired = "error.email.verify_url_expired"
|
||||
EmailNeedToBeVerified = "error.email.need_to_be_verified"
|
||||
|
|
|
@ -11,7 +11,9 @@ import (
|
|||
"github.com/go-playground/validator/v10"
|
||||
"github.com/go-playground/validator/v10/translations/en"
|
||||
"github.com/go-playground/validator/v10/translations/zh"
|
||||
"github.com/segmentfault/answer/internal/base/reason"
|
||||
"github.com/segmentfault/answer/internal/base/translator"
|
||||
myErrors "github.com/segmentfault/pacman/errors"
|
||||
"github.com/segmentfault/pacman/i18n"
|
||||
)
|
||||
|
||||
|
@ -98,7 +100,7 @@ func (m *MyValidator) Check(value interface{}) (errField *ErrorField, err error)
|
|||
Key: translator.GlobalTrans.Tr(m.Lang, fieldError.Field()),
|
||||
Value: fieldError.Translate(m.Tran),
|
||||
}
|
||||
return errField, errors.New(fieldError.Translate(m.Tran))
|
||||
return errField, myErrors.BadRequest(reason.RequestFormatError).WithMsg(fieldError.Translate(m.Tran))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -206,11 +206,11 @@ func (uc *UserController) UserLogout(ctx *gin.Context) {
|
|||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body schema.UserRegister true "UserRegister"
|
||||
// @Param data body schema.UserRegisterReq true "UserRegisterReq"
|
||||
// @Success 200 {object} handler.RespBody{data=schema.GetUserResp}
|
||||
// @Router /answer/api/v1/user/register/email [post]
|
||||
func (uc *UserController) UserRegisterByEmail(ctx *gin.Context) {
|
||||
req := &schema.UserRegister{}
|
||||
req := &schema.UserRegisterReq{}
|
||||
if handler.BindAndCheck(ctx, req) {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ func (ur *userRepo) UpdateEmail(ctx context.Context, userID, email string) (err
|
|||
// UpdateInfo update user info
|
||||
func (ur *userRepo) UpdateInfo(ctx context.Context, userInfo *entity.User) (err error) {
|
||||
_, err = ur.data.DB.Where("id = ?", userInfo.ID).
|
||||
Cols("display_name", "avatar", "bio", "bio_html", "website", "location").Update(userInfo)
|
||||
Cols("username", "display_name", "avatar", "bio", "bio_html", "website", "location").Update(userInfo)
|
||||
if err != nil {
|
||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
|
|
|
@ -2,11 +2,14 @@ package schema
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"regexp"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/segmentfault/answer/internal/base/reason"
|
||||
"github.com/segmentfault/answer/internal/base/validator"
|
||||
"github.com/segmentfault/answer/internal/entity"
|
||||
"github.com/segmentfault/answer/pkg/checker"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
)
|
||||
|
||||
// UserVerifyEmailReq user verify email request
|
||||
|
@ -179,10 +182,10 @@ type UserEmailLogin struct {
|
|||
CaptchaCode string `json:"captcha_code" ` // captcha_code
|
||||
}
|
||||
|
||||
// Register
|
||||
type UserRegister struct {
|
||||
// UserRegisterReq user register request
|
||||
type UserRegisterReq struct {
|
||||
// name
|
||||
Name string `validate:"required,gt=5,lte=50" json:"name"`
|
||||
Name string `validate:"required,gt=4,lte=30" json:"name"`
|
||||
// email
|
||||
Email string `validate:"required,email,gt=0,lte=500" json:"e_mail" `
|
||||
// password
|
||||
|
@ -190,7 +193,7 @@ type UserRegister struct {
|
|||
IP string `json:"-" `
|
||||
}
|
||||
|
||||
func (u *UserRegister) Check() (errField *validator.ErrorField, err error) {
|
||||
func (u *UserRegisterReq) Check() (errField *validator.ErrorField, err error) {
|
||||
// TODO i18n
|
||||
err = checker.PassWordCheck(8, 32, 0, u.Pass)
|
||||
if err != nil {
|
||||
|
@ -224,6 +227,8 @@ func (u *UserModifyPassWordRequest) Check() (errField *validator.ErrorField, err
|
|||
type UpdateInfoRequest struct {
|
||||
// display_name
|
||||
DisplayName string `validate:"required,gt=0,lte=30" json:"display_name"`
|
||||
// username
|
||||
Username string `validate:"omitempty,gt=0,lte=30" json:"username"`
|
||||
// avatar
|
||||
Avatar string `validate:"omitempty,gt=0,lte=500" json:"avatar"`
|
||||
// bio
|
||||
|
@ -238,6 +243,21 @@ type UpdateInfoRequest struct {
|
|||
UserId string `json:"-" `
|
||||
}
|
||||
|
||||
func (u *UpdateInfoRequest) Check() (errField *validator.ErrorField, err error) {
|
||||
if len(u.Username) > 0 {
|
||||
re := regexp.MustCompile(`^[a-z0-9._-]{4,30}$`)
|
||||
match := re.MatchString(u.Username)
|
||||
if !match {
|
||||
err = errors.BadRequest(reason.UsernameInvalid)
|
||||
return &validator.ErrorField{
|
||||
Key: "username",
|
||||
Value: err.Error(),
|
||||
}, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type UserRetrievePassWordRequest struct {
|
||||
Email string `validate:"required,email,gt=0,lte=500" json:"e_mail" ` // e_mail
|
||||
CaptchaID string `json:"captcha_id" ` // captcha_id
|
||||
|
|
|
@ -2,7 +2,9 @@ package service
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
|
@ -17,7 +19,6 @@ import (
|
|||
"github.com/segmentfault/answer/internal/service/service_config"
|
||||
usercommon "github.com/segmentfault/answer/internal/service/user_common"
|
||||
"github.com/segmentfault/answer/pkg/checker"
|
||||
"github.com/segmentfault/answer/pkg/uid"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
@ -233,20 +234,30 @@ func (us *UserService) UserModifyPassWord(ctx context.Context, request *schema.U
|
|||
return nil
|
||||
}
|
||||
|
||||
// UpdateInfo
|
||||
func (us *UserService) UpdateInfo(ctx context.Context, request *schema.UpdateInfoRequest) error {
|
||||
userinfo := entity.User{}
|
||||
userinfo.ID = request.UserId
|
||||
userinfo.Avatar = request.Avatar
|
||||
userinfo.DisplayName = request.DisplayName
|
||||
userinfo.Bio = request.Bio
|
||||
userinfo.BioHtml = request.BioHtml
|
||||
userinfo.Location = request.Location
|
||||
userinfo.Website = request.Website
|
||||
err := us.userRepo.UpdateInfo(ctx, &userinfo)
|
||||
// UpdateInfo update user info
|
||||
func (us *UserService) UpdateInfo(ctx context.Context, req *schema.UpdateInfoRequest) (err error) {
|
||||
if len(req.Username) > 0 {
|
||||
userInfo, exist, err := us.userRepo.GetByUsername(ctx, req.Username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exist && userInfo.ID != req.UserId {
|
||||
return errors.BadRequest(reason.UsernameDuplicate)
|
||||
}
|
||||
}
|
||||
|
||||
userInfo := entity.User{}
|
||||
userInfo.ID = req.UserId
|
||||
userInfo.Avatar = req.Avatar
|
||||
userInfo.DisplayName = req.DisplayName
|
||||
userInfo.Bio = req.Bio
|
||||
userInfo.BioHtml = req.BioHtml
|
||||
userInfo.Location = req.Location
|
||||
userInfo.Website = req.Website
|
||||
userInfo.Username = req.Username
|
||||
if err := us.userRepo.UpdateInfo(ctx, &userInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -259,7 +270,7 @@ func (us *UserService) UserEmailHas(ctx context.Context, email string) (bool, er
|
|||
}
|
||||
|
||||
// UserRegisterByEmail user register
|
||||
func (us *UserService) UserRegisterByEmail(ctx context.Context, registerUserInfo *schema.UserRegister) (
|
||||
func (us *UserService) UserRegisterByEmail(ctx context.Context, registerUserInfo *schema.UserRegisterReq) (
|
||||
resp *schema.GetUserResp, err error) {
|
||||
_, has, err := us.userRepo.GetByEmail(ctx, registerUserInfo.Email)
|
||||
if err != nil {
|
||||
|
@ -276,7 +287,7 @@ func (us *UserService) UserRegisterByEmail(ctx context.Context, registerUserInfo
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userInfo.Username, err = us.makeUserName(ctx, registerUserInfo.Name)
|
||||
userInfo.Username, err = us.makeUsername(ctx, registerUserInfo.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -408,58 +419,42 @@ func (us *UserService) UserVerifyEmail(ctx context.Context, req *schema.UserVeri
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
// makeUserName
|
||||
// Generate a unique Username based on the NickName
|
||||
// todo Waiting to be realized
|
||||
func (us *UserService) makeUserName(ctx context.Context, userName string) (string, error) {
|
||||
userName = us.formatUserName(ctx, userName)
|
||||
_, has, err := us.userRepo.GetByUsername(ctx, userName)
|
||||
// makeUsername
|
||||
// Generate a unique Username based on the displayName
|
||||
func (us *UserService) makeUsername(ctx context.Context, displayName string) (username string, err error) {
|
||||
// Chinese processing
|
||||
if has := checker.IsChinese(displayName); has {
|
||||
str, err := pinyin.New(displayName).Split("").Mode(pinyin.WithoutTone).Convert()
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
displayName = str
|
||||
}
|
||||
}
|
||||
|
||||
username = strings.ReplaceAll(displayName, " ", "_")
|
||||
username = strings.ToLower(username)
|
||||
suffix := ""
|
||||
|
||||
re := regexp.MustCompile(`^[a-z0-9._-]{4,30}$`)
|
||||
match := re.MatchString(username)
|
||||
if !match {
|
||||
return "", errors.BadRequest(reason.UsernameInvalid)
|
||||
}
|
||||
|
||||
for {
|
||||
_, has, err := us.userRepo.GetByUsername(ctx, username+suffix)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
//If the user name is duplicated, it is generated recursively from the new one.
|
||||
if has {
|
||||
userName = uid.IDStr()
|
||||
return us.makeUserName(ctx, userName)
|
||||
if !has {
|
||||
break
|
||||
}
|
||||
return userName, nil
|
||||
}
|
||||
|
||||
// formatUserName
|
||||
// Generate a Username through a nickname
|
||||
func (us *UserService) formatUserName(ctx context.Context, Name string) string {
|
||||
formatName, pass := us.CheckUserName(ctx, Name)
|
||||
if !pass {
|
||||
//todo 重新给用户 生成随机 username
|
||||
return uid.IDStr()
|
||||
bytes := make([]byte, 2)
|
||||
_, _ = rand.Read(bytes)
|
||||
suffix = hex.EncodeToString(bytes)
|
||||
}
|
||||
return formatName
|
||||
}
|
||||
|
||||
func (us *UserService) CheckUserName(ctx context.Context, name string) (string, bool) {
|
||||
name = strings.Replace(name, " ", "_", -1)
|
||||
name = strings.ToLower(name)
|
||||
//Chinese processing
|
||||
has := checker.IsChinese(name)
|
||||
if has {
|
||||
str, err := pinyin.New(name).Split("").Mode(pinyin.WithoutTone).Convert()
|
||||
if err != nil {
|
||||
log.Error("pinyin Error", err)
|
||||
return "", false
|
||||
} else {
|
||||
name = str
|
||||
}
|
||||
}
|
||||
//Format filtering
|
||||
re, err := regexp.Compile(`^[a-z0-9._-]{4,20}$`)
|
||||
if err != nil {
|
||||
log.Error("regexp.Compile Error", err, "name", name)
|
||||
}
|
||||
match := re.MatchString(name)
|
||||
if !match {
|
||||
return "", false
|
||||
}
|
||||
return name, true
|
||||
return username + suffix, nil
|
||||
}
|
||||
|
||||
// verifyPassword
|
||||
|
|
Loading…
Reference in New Issue