feat(user): backend add user and change user password

This commit is contained in:
LinkinStar 2022-12-07 10:26:02 +08:00
parent 149751153c
commit 42756c0742
8 changed files with 204 additions and 62 deletions

View File

@ -116,14 +116,14 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
roleRepo := role.NewRoleRepo(dataData)
roleService := role2.NewRoleService(roleRepo)
userRoleRelService := role2.NewUserRoleRelService(userRoleRelRepo, roleService)
userService := service.NewUserService(userRepo, userActiveActivityRepo, emailService, authService, serviceConf, siteInfoCommonService, userRoleRelService)
userCommon := usercommon.NewUserCommon(userRepo)
userService := service.NewUserService(userRepo, userActiveActivityRepo, emailService, authService, serviceConf, siteInfoCommonService, userRoleRelService, userCommon)
captchaRepo := captcha.NewCaptchaRepo(dataData)
captchaService := action.NewCaptchaService(captchaRepo)
uploaderService := uploader.NewUploaderService(serviceConf, siteInfoCommonService)
userController := controller.NewUserController(authService, userService, captchaService, emailService, uploaderService)
commentRepo := comment.NewCommentRepo(dataData, uniqueIDRepo)
commentCommonRepo := comment.NewCommentCommonRepo(dataData, uniqueIDRepo)
userCommon := usercommon.NewUserCommon(userRepo)
answerRepo := answer.NewAnswerRepo(dataData, uniqueIDRepo, userRankRepo, activityRepo)
questionRepo := question.NewQuestionRepo(dataData, uniqueIDRepo)
tagCommonRepo := tag_common.NewTagCommonRepo(dataData, uniqueIDRepo)
@ -180,7 +180,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
reportBackyardService := report_backyard.NewReportBackyardService(reportRepo, userCommon, commonRepo, answerRepo, questionRepo, commentCommonRepo, reportHandle, configRepo)
controller_backyardReportController := controller_backyard.NewReportController(reportBackyardService)
userBackyardRepo := user.NewUserBackyardRepo(dataData, authRepo)
userBackyardService := user_backyard.NewUserBackyardService(userBackyardRepo, userRoleRelService, authService)
userBackyardService := user_backyard.NewUserBackyardService(userBackyardRepo, userRoleRelService, authService, userCommon)
userBackyardController := controller_backyard.NewUserBackyardController(userBackyardService)
reasonRepo := reason.NewReasonRepo(configRepo)
reasonService := reason2.NewReasonService(reasonRepo)

View File

@ -60,6 +60,50 @@ func (uc *UserBackyardController) UpdateUserRole(ctx *gin.Context) {
handler.HandleResponse(ctx, err, nil)
}
// AddUser add user
// @Summary add user
// @Description add user
// @Security ApiKeyAuth
// @Tags admin
// @Accept json
// @Produce json
// @Param data body schema.AddUserReq true "user"
// @Success 200 {object} handler.RespBody
// @Router /answer/admin/api/user [post]
func (uc *UserBackyardController) AddUser(ctx *gin.Context) {
req := &schema.AddUserReq{}
if handler.BindAndCheck(ctx, req) {
return
}
req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx)
err := uc.userService.AddUser(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
// UpdateUserPassword update user password
// @Summary update user password
// @Description update user password
// @Security ApiKeyAuth
// @Tags admin
// @Accept json
// @Produce json
// @Param data body schema.UpdateUserPasswordReq true "user"
// @Success 200 {object} handler.RespBody
// @Router /answer/admin/api/user/password [put]
func (uc *UserBackyardController) UpdateUserPassword(ctx *gin.Context) {
req := &schema.UpdateUserPasswordReq{}
if handler.BindAndCheck(ctx, req) {
return
}
req.LoginUserID = middleware.GetLoginUserIDFromContext(ctx)
err := uc.userService.UpdateUserPassword(ctx, req)
handler.HandleResponse(ctx, err, nil)
}
// GetUserPage get user page
// @Summary get user page
// @Description get user page

View File

@ -61,6 +61,24 @@ func (ur *userBackyardRepo) UpdateUserStatus(ctx context.Context, userID string,
return
}
// AddUser add user
func (ur *userBackyardRepo) AddUser(ctx context.Context, user *entity.User) (err error) {
_, err = ur.data.DB.Insert(user)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
// UpdateUserPassword update user password
func (ur *userBackyardRepo) UpdateUserPassword(ctx context.Context, userID string, password string) (err error) {
_, err = ur.data.DB.ID(userID).Update(&entity.User{Pass: password})
if err != nil {
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
// GetUserInfo get user info
func (ur *userBackyardRepo) GetUserInfo(ctx context.Context, userID string) (user *entity.User, exist bool, err error) {
user = &entity.User{}

View File

@ -234,6 +234,8 @@ func (a *AnswerAPIRouter) RegisterAnswerCmsAPIRouter(r *gin.RouterGroup) {
r.GET("/users/page", a.backyardUserController.GetUserPage)
r.PUT("/user/status", a.backyardUserController.UpdateUserStatus)
r.PUT("/user/role", a.backyardUserController.UpdateUserRole)
r.POST("/user", a.backyardUserController.AddUser)
r.PUT("/user/password", a.backyardUserController.UpdateUserPassword)
// reason
r.GET("/reasons", a.reasonController.Reasons)

View File

@ -84,3 +84,18 @@ type UpdateUserRoleReq struct {
// login user id
LoginUserID string `json:"-"`
}
// AddUserReq add user request
type AddUserReq struct {
DisplayName string `validate:"required,gt=4,lte=30" json:"display_name"`
Email string `validate:"required,email,gt=0,lte=500" json:"email"`
Password string `validate:"required,gte=8,lte=32" json:"password"`
LoginUserID string `json:"-"`
}
// UpdateUserPasswordReq update user password request
type UpdateUserPasswordReq struct {
UserID string `validate:"required" json:"user_id"`
Password string `validate:"required,gte=8,lte=32" json:"password"`
LoginUserID string `json:"-"`
}

View File

@ -14,9 +14,11 @@ import (
"github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/internal/service/auth"
"github.com/answerdev/answer/internal/service/role"
usercommon "github.com/answerdev/answer/internal/service/user_common"
"github.com/jinzhu/copier"
"github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/log"
"golang.org/x/crypto/bcrypt"
)
// UserBackyardRepo user repository
@ -25,6 +27,8 @@ type UserBackyardRepo interface {
GetUserInfo(ctx context.Context, userID string) (user *entity.User, exist bool, err error)
GetUserPage(ctx context.Context, page, pageSize int, user *entity.User,
usernameOrDisplayName string, isStaff bool) (users []*entity.User, total int64, err error)
AddUser(ctx context.Context, user *entity.User) (err error)
UpdateUserPassword(ctx context.Context, userID string, password string) (err error)
}
// UserBackyardService user service
@ -32,6 +36,7 @@ type UserBackyardService struct {
userRepo UserBackyardRepo
userRoleRelService *role.UserRoleRelService
authService *auth.AuthService
userCommonService *usercommon.UserCommon
}
// NewUserBackyardService new user backyard service
@ -39,11 +44,13 @@ func NewUserBackyardService(
userRepo UserBackyardRepo,
userRoleRelService *role.UserRoleRelService,
authService *auth.AuthService,
userCommonService *usercommon.UserCommon,
) *UserBackyardService {
return &UserBackyardService{
userRepo: userRepo,
userRoleRelService: userRoleRelService,
authService: authService,
userCommonService: userCommonService,
}
}
@ -94,6 +101,57 @@ func (us *UserBackyardService) UpdateUserRole(ctx context.Context, req *schema.U
return
}
// AddUser add user
func (us *UserBackyardService) AddUser(ctx context.Context, req *schema.AddUserReq) (err error) {
hashPwd, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
return err
}
userInfo := &entity.User{}
userInfo.EMail = req.Email
userInfo.DisplayName = req.DisplayName
userInfo.Pass = string(hashPwd)
userInfo.Username, err = us.userCommonService.MakeUsername(ctx, userInfo.DisplayName)
if err != nil {
return err
}
userInfo.MailStatus = entity.EmailStatusAvailable
userInfo.Status = entity.UserStatusAvailable
userInfo.Rank = 1
err = us.userRepo.AddUser(ctx, userInfo)
if err != nil {
return err
}
return
}
// UpdateUserPassword update user password
func (us *UserBackyardService) UpdateUserPassword(ctx context.Context, req *schema.UpdateUserPasswordReq) (err error) {
userInfo, exist, err := us.userRepo.GetUserInfo(ctx, req.UserID)
if err != nil {
return err
}
if !exist {
return errors.BadRequest(reason.UserNotFound)
}
hashPwd, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
return err
}
err = us.userRepo.UpdateUserPassword(ctx, userInfo.ID, string(hashPwd))
if err != nil {
return err
}
// logout this user
us.authService.RemoveAllUserTokens(ctx, req.UserID)
return
}
// GetUserInfo get user one
func (us *UserBackyardService) GetUserInfo(ctx context.Context, userID string) (resp *schema.GetUserInfoResp, err error) {
user, exist, err := us.userRepo.GetUserInfo(ctx, userID)

View File

@ -2,9 +2,17 @@ package usercommon
import (
"context"
"encoding/hex"
"math/rand"
"regexp"
"strings"
"github.com/Chain-Zhang/pinyin"
"github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/pkg/checker"
"github.com/segmentfault/pacman/errors"
)
type UserRepo interface {
@ -94,3 +102,41 @@ func (us *UserCommon) FormatUserBasicInfo(ctx context.Context, userInfo *entity.
}
return userBasicInfo
}
// MakeUsername
// Generate a unique Username based on the displayName
func (us *UserCommon) 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 "", errors.BadRequest(reason.UsernameInvalid)
} 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 !has {
break
}
bytes := make([]byte, 2)
_, _ = rand.Read(bytes)
suffix = hex.EncodeToString(bytes)
}
return username + suffix, nil
}

View File

@ -2,14 +2,9 @@ package service
import (
"context"
"encoding/hex"
"encoding/json"
"fmt"
"math/rand"
"regexp"
"strings"
"github.com/Chain-Zhang/pinyin"
"github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/base/reason"
"github.com/answerdev/answer/internal/base/translator"
@ -23,7 +18,6 @@ import (
"github.com/answerdev/answer/internal/service/service_config"
"github.com/answerdev/answer/internal/service/siteinfo_common"
usercommon "github.com/answerdev/answer/internal/service/user_common"
"github.com/answerdev/answer/pkg/checker"
"github.com/google/uuid"
"github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/log"
@ -34,13 +28,14 @@ import (
// UserService user service
type UserService struct {
userRepo usercommon.UserRepo
userActivity activity.UserActiveActivityRepo
serviceConfig *service_config.ServiceConfig
emailService *export.EmailService
authService *auth.AuthService
siteInfoService *siteinfo_common.SiteInfoCommonService
userRoleService *role.UserRoleRelService
userCommonService *usercommon.UserCommon
userRepo usercommon.UserRepo
userActivity activity.UserActiveActivityRepo
serviceConfig *service_config.ServiceConfig
emailService *export.EmailService
authService *auth.AuthService
siteInfoService *siteinfo_common.SiteInfoCommonService
userRoleService *role.UserRoleRelService
}
func NewUserService(userRepo usercommon.UserRepo,
@ -50,15 +45,17 @@ func NewUserService(userRepo usercommon.UserRepo,
serviceConfig *service_config.ServiceConfig,
siteInfoService *siteinfo_common.SiteInfoCommonService,
userRoleService *role.UserRoleRelService,
userCommonService *usercommon.UserCommon,
) *UserService {
return &UserService{
userRepo: userRepo,
userActivity: userActivity,
emailService: emailService,
serviceConfig: serviceConfig,
authService: authService,
siteInfoService: siteInfoService,
userRoleService: userRoleService,
userCommonService: userCommonService,
userRepo: userRepo,
userActivity: userActivity,
emailService: emailService,
serviceConfig: serviceConfig,
authService: authService,
siteInfoService: siteInfoService,
userRoleService: userRoleService,
}
}
@ -307,7 +304,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.userCommonService.MakeUsername(ctx, registerUserInfo.Name)
if err != nil {
return nil, err
}
@ -456,44 +453,6 @@ func (us *UserService) UserVerifyEmail(ctx context.Context, req *schema.UserVeri
return resp, nil
}
// 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 !has {
break
}
bytes := make([]byte, 2)
_, _ = rand.Read(bytes)
suffix = hex.EncodeToString(bytes)
}
return username + suffix, nil
}
// verifyPassword
// Compare whether the password is correct
func (us *UserService) verifyPassword(ctx context.Context, LoginPass, UserPass string) bool {