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