From cd18170ee63995cf589a95778b7069d1163b8159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Fri, 12 Aug 2022 13:20:23 +0800 Subject: [PATCH] =?UTF-8?q?feat(user):=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E9=87=8D=E8=AE=BE=E5=AF=86=E7=A0=81=EF=BC=8C?= =?UTF-8?q?=E5=BE=85=E6=B5=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cache/repository.go | 31 +++++++++++++++++++++ controller/user.go | 52 ++++++++++++++++++++++++++++++++++ repository/abstract.go | 23 --------------- repository/user.go | 28 +++++++++++++++++++ service/user.go | 63 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 23 deletions(-) create mode 100644 cache/repository.go diff --git a/cache/repository.go b/cache/repository.go new file mode 100644 index 0000000..90d72b6 --- /dev/null +++ b/cache/repository.go @@ -0,0 +1,31 @@ +package cache + +import "time" + +func CacheData[T interface{}](instance T, category string, key ...string) error { + var keys = make([]string, 0) + keys = append(keys, category) + keys = append(keys, key...) + cacheKey := CacheKey("cache", keys...) + if exists, _ := Exists(cacheKey); exists { + Delete(cacheKey) + } + return Cache(cacheKey, &instance, 5*time.Minute) +} + +func RetreiveData[T interface{}](category string, key ...string) (*T, error) { + var keys = make([]string, 0) + keys = append(keys, category) + keys = append(keys, key...) + return Retreive[T](CacheKey("cache", keys...)) +} + +func AbolishCacheData(category string, key ...string) { + var keys = make([]string, 0) + keys = append(keys, category) + keys = append(keys, key...) + cacheKey := CacheKey("cache", keys...) + if exists, _ := Exists(cacheKey); exists { + Delete(cacheKey) + } +} diff --git a/controller/user.go b/controller/user.go index 8cf121d..000a8cb 100644 --- a/controller/user.go +++ b/controller/user.go @@ -25,6 +25,8 @@ func InitializeUserController(router *gin.Engine) { } UserController.Router.POST("/login", UserController.Login) UserController.Router.DELETE("/logout", security.MustAuthenticated, UserController.Logout) + UserController.Router.DELETE("/password/:uid", security.OPSAuthorize, UserController.InvalidUserPassword) + UserController.Router.PUT("/password", UserController.ResetUserPassword) } type LoginFormData struct { @@ -77,3 +79,53 @@ func (_UserController) Logout(c *gin.Context) { } result.Success("用户已成功登出系统。") } + +func (_UserController) InvalidUserPassword(c *gin.Context) { + result := response.NewResult(c) + targetUserId := c.Param("uid") + verifyCode, err := service.UserService.InvalidUserPassword(targetUserId) + if errors.Is(err, &exceptions.NotFoundError{}) { + result.NotFound("未找到指定用户。") + return + } + if errors.Is(err, &exceptions.UnsuccessfulOperationError{}) { + result.Error(500, "未能成功更新用户的密码。") + return + } + if err != nil { + result.Error(500, err.Error()) + return + } + result.QuickJson(http.StatusOK, http.StatusAccepted, "用户密码已经失效", gin.H{"verify": verifyCode}) +} + +type ResetPasswordFormData struct { + VerifyCode string `json:"verifyCode"` + Username string `json:"uname"` + NewPassword string `json:"newPass"` +} + +func (_UserController) ResetUserPassword(c *gin.Context) { + result := response.NewResult(c) + resetForm := new(ResetPasswordFormData) + c.BindJSON(resetForm) + verified, err := service.UserService.VerifyUserPassword(resetForm.Username, resetForm.VerifyCode) + if !verified { + result.Error(http.StatusUnauthorized, "验证码不正确。") + return + } + if err != nil { + result.Error(http.StatusInternalServerError, err.Error()) + return + } + completed, err := service.UserService.ResetUserPassword(resetForm.Username, resetForm.NewPassword) + if err != nil { + result.Error(http.StatusInternalServerError, err.Error()) + return + } + if completed { + result.Success("用户凭据已更新。") + return + } + result.Error(http.StatusNotAcceptable, "用户凭据未能成功更新。") +} diff --git a/repository/abstract.go b/repository/abstract.go index 62760ba..50d137c 100644 --- a/repository/abstract.go +++ b/repository/abstract.go @@ -1,10 +1,5 @@ package repository -import ( - "electricity_bill_calc/cache" - "time" -) - func _postProcessSingle[T interface{}](instance *T, has bool, err error) (*T, error) { if err != nil { return nil, err @@ -26,21 +21,3 @@ func _postProcessList[T interface{}](instance []*T, has bool, err error) ([]*T, return nil, nil } } - -func cacheData[T interface{}](instance T, category string, key ...string) error { - var keys = make([]string, 0) - keys = append(keys, category) - keys = append(keys, key...) - cacheKey := cache.CacheKey("cache", keys...) - if exists, _ := cache.Exists(cacheKey); exists { - cache.Delete(cacheKey) - } - return cache.Cache(cacheKey, &instance, 5*time.Minute) -} - -func retreiveData[T interface{}](category string, key ...string) (*T, error) { - var keys = make([]string, 0) - keys = append(keys, category) - keys = append(keys, key...) - return cache.Retreive[T](cache.CacheKey("cache", keys...)) -} diff --git a/repository/user.go b/repository/user.go index 43ae712..0e01a67 100644 --- a/repository/user.go +++ b/repository/user.go @@ -1,6 +1,7 @@ package repository import ( + "electricity_bill_calc/cache" "electricity_bill_calc/global" "electricity_bill_calc/model" ) @@ -10,13 +11,40 @@ type _UserRepository struct{} var UserRepo _UserRepository func (_UserRepository) FindUserByUsername(username string) (*model.User, error) { + cachedUser, _ := cache.RetreiveData[model.User]("user", username) + if cachedUser != nil { + return cachedUser, nil + } user := new(model.User) has, err := global.DBConn.Where("username=?", username).Get(user) + if has { + cache.CacheData(user, "user", username) + } return _postProcessSingle(user, has, err) } func (_UserRepository) RetreiveUserDetail(uid string) (*model.UserDetail, error) { + cachedUser, _ := cache.RetreiveData[model.UserDetail]("user", uid) + if cachedUser != nil { + return cachedUser, nil + } user := new(model.UserDetail) has, err := global.DBConn.Where("id=?", uid).Get(user) + if has { + cache.CacheData(user, "user_detail", uid) + } + return _postProcessSingle(user, has, err) +} + +func (_UserRepository) FindUserByID(uid string) (*model.User, error) { + cachedUser, _ := cache.RetreiveData[model.User]("user", uid) + if cachedUser != nil { + return cachedUser, nil + } + user := new(model.User) + has, err := global.DBConn.ID(uid).Get(user) + if has { + cache.CacheData(user, "user", uid) + } return _postProcessSingle(user, has, err) } diff --git a/service/user.go b/service/user.go index ac8ed2e..5a0d438 100644 --- a/service/user.go +++ b/service/user.go @@ -5,8 +5,10 @@ import ( "electricity_bill_calc/cache" "electricity_bill_calc/config" "electricity_bill_calc/exceptions" + "electricity_bill_calc/global" "electricity_bill_calc/model" "electricity_bill_calc/repository" + "electricity_bill_calc/utils" "fmt" "time" @@ -92,3 +94,64 @@ func (_UserService) ProcessManagementUserLogin(username, password string) (*mode cache.CacheSession(session) return session, nil } + +func (_UserService) InvalidUserPassword(uid string) (string, error) { + user, err := repository.UserRepo.FindUserByID(uid) + if user == nil && err != nil { + return "", exceptions.NewNotFoundError("指定的用户不存在。") + } + verifyCode := utils.RandStr(10) + hash := sha512.New512_256() + hash.Write([]byte(verifyCode)) + user.Password = string(hash.Sum(nil)) + user.ResetNeeded = true + affected, err := global.DBConn.ID(uid).Cols("password", "reset_needed").Update(user) + if err != nil { + return "", err + } + if affected > 0 { + // ! 同一个用户在缓存中有两个键。 + cache.AbolishCacheData("user", user.Id) + cache.AbolishCacheData("user", user.Username) + return verifyCode, nil + } else { + return "", exceptions.NewUnsuccessfulOperationError() + } +} + +func (_UserService) VerifyUserPassword(username, verifyCode string) (bool, error) { + user, err := repository.UserRepo.FindUserByUsername(username) + if user == nil || err != nil { + return false, exceptions.NewNotFoundError("指定的用户不存在。") + } + hash := sha512.New512_256() + hash.Write([]byte(verifyCode)) + hashedVerifyCode := string(hash.Sum(nil)) + if hashedVerifyCode != user.Password { + return false, nil + } else { + return true, nil + } +} + +func (_UserService) ResetUserPassword(username, password string) (bool, error) { + user, err := repository.UserRepo.FindUserByUsername(username) + if user == nil || err != nil { + return false, exceptions.NewNotFoundError("指定的用户不存在。") + } + hash := sha512.New512_256() + hash.Write([]byte(password)) + user.Password = string(hash.Sum(nil)) + user.ResetNeeded = false + affected, err := global.DBConn.ID(user.Id).Cols("password", "reset_needed").Update(user) + if err != nil { + return false, err + } + if affected > 0 { + cache.AbolishCacheData("user", user.Id) + cache.AbolishCacheData("user", user.Username) + return true, nil + } else { + return false, nil + } +}