From 6fff6d35a5d08465416c0d702b3656a0b16ed9af Mon Sep 17 00:00:00 2001 From: suguo <25950955@qq.com> Date: Thu, 19 Mar 2026 17:35:11 +0800 Subject: [PATCH] init --- gin/router-gin.go | 8 +- handler/auth-handler.go | 58 --------- handler/user-handler.go | 128 +++++++++++++++++++ model/menu-model.go | 4 +- model/user-model.go | 5 +- mysql/org-mysql.go | 14 ++ mysql/user-mysql.go | 68 ++++++---- redis/menu-redis.go | 52 ++++++++ service/auth-service.go | 70 +--------- service/base-service.go | 6 +- service/bootstrap-service.go | 61 ++++++--- service/menu-service.go | 40 ++++-- service/user-service.go | 240 +++++++++++++++++++++++++++++++++++ 13 files changed, 571 insertions(+), 183 deletions(-) create mode 100644 redis/menu-redis.go create mode 100644 service/user-service.go diff --git a/gin/router-gin.go b/gin/router-gin.go index 2d85fc8..16595bb 100644 --- a/gin/router-gin.go +++ b/gin/router-gin.go @@ -15,7 +15,6 @@ func routerSetup(router *gin.Engine) { protected.Use(auth(), authorize()) protected.POST("/logout", handler.AuthLogout) protected.GET("/me", handler.AuthMe) - protected.POST("/password", handler.AuthChangePassword) protected.GET("/orgs", handler.OrgList) protected.POST("/orgs", handler.OrgCreate) @@ -55,4 +54,11 @@ func routerSetup(router *gin.Engine) { protected.GET("/menus", handler.MenuList) protected.GET("/options", handler.OptionMap) + + protected.GET("/users", handler.UserList) + protected.POST("/users", handler.UserCreate) + protected.GET("/users/:id", handler.UserGet) + protected.PUT("/users/:id", handler.UserUpdate) + protected.DELETE("/users/:id", handler.UserDelete) + protected.PUT("/users/:id/password", handler.UserPasswordUpdate) } diff --git a/handler/auth-handler.go b/handler/auth-handler.go index f4410b6..203f202 100644 --- a/handler/auth-handler.go +++ b/handler/auth-handler.go @@ -2,7 +2,6 @@ package handler import ( "net/http" - "os" "strings" "github.com/gin-gonic/gin" @@ -88,60 +87,3 @@ func AuthMe(c *gin.Context) { "user": usr, }) } - -func AuthChangePassword(c *gin.Context) { - usr := currentUser(c) - if usr == nil { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ - "data": "无效TOKEN, 请重新登录!", - }) - return - } - - var req AuthChangePasswordRequest - if err := c.ShouldBindJSON(&req); err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ - "data": "参数错误", - }) - return - } - req.OldPassword = strings.TrimSpace(req.OldPassword) - req.NewPassword = strings.TrimSpace(req.NewPassword) - err := service.AuthChangePassword(usr.ID, req.OldPassword, req.NewPassword) - if err != nil { - switch err { - case service.ErrInvalidArgument: - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ - "data": "密码不能为空", - }) - case service.ErrNewPasswordShort: - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ - "data": "新密码至少6位", - }) - case service.ErrOldPasswordWrong: - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ - "data": "旧密码错误", - }) - case service.ErrUserNotFound: - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ - "data": "无效用户, 请重新登录!", - }) - default: - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ - "data": authChangePasswordInternalError(err), - }) - } - return - } - - c.JSON(http.StatusOK, gin.H{ - "data": "ok", - }) -} - -func authChangePasswordInternalError(err error) string { - if os.Getenv("DEBUG") == "true" && err != nil { - return "修改失败: " + err.Error() - } - return "修改失败" -} diff --git a/handler/user-handler.go b/handler/user-handler.go index abeebd1..0b7d59b 100644 --- a/handler/user-handler.go +++ b/handler/user-handler.go @@ -1 +1,129 @@ package handler + +import ( + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + "myschools.me/heritage/heritage-api/service" +) + +// UserCreate 创建用户 +func UserCreate(c *gin.Context) { + var req service.UserCreateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + + user, err := service.UserCreate(req) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + + c.JSON(http.StatusOK, service.UserResponse{User: user}) +} + +// UserUpdate 更新用户 +func UserUpdate(c *gin.Context) { + userID := c.Param("id") + if userID == "" { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "用户ID不能为空"}) + return + } + + var req service.UserUpdateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + + user, err := service.UserUpdate(userID, req) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + + c.JSON(http.StatusOK, service.UserResponse{User: user}) +} + +// UserDelete 删除用户 +func UserDelete(c *gin.Context) { + userID := c.Param("id") + if userID == "" { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "用户ID不能为空"}) + return + } + + success, err := service.UserDelete(userID) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"data": success}) +} + +// UserGet 获取用户 +func UserGet(c *gin.Context) { + userID := c.Param("id") + if userID == "" { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "用户ID不能为空"}) + return + } + + user, err := service.UserGet(userID) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + + c.JSON(http.StatusOK, service.UserResponse{User: user}) +} + +// UserList 获取用户列表 +func UserList(c *gin.Context) { + // 获取分页参数 + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10")) + + // 确保分页参数有效 + if page < 1 { + page = 1 + } + if pageSize < 1 || pageSize > 100 { + pageSize = 10 + } + + users, total, err := service.UserList(page, pageSize) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + + c.JSON(http.StatusOK, service.UserListResponse{Users: users, Total: total}) +} + +// UserPasswordUpdate 更新用户密码 +func UserPasswordUpdate(c *gin.Context) { + userID := c.Param("id") + if userID == "" { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "用户ID不能为空"}) + return + } + + var req service.UserPasswordUpdateRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": "参数错误"}) + return + } + + err := service.UserPasswordUpdate(userID, req) + if err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"data": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"data": "密码更新成功"}) +} diff --git a/model/menu-model.go b/model/menu-model.go index 66d3514..95b6fcb 100644 --- a/model/menu-model.go +++ b/model/menu-model.go @@ -9,7 +9,7 @@ type Menu struct { Name string `json:"name"` Icon string `json:"icon"` Path string `json:"path"` - Sort int `json:"sort"` - PermissionCode string `json:"permissionCode"` + Sort int `json:"-"` + PermissionCode string `json:"-"` Children []*Menu `json:"children" gorm:"-"` } diff --git a/model/user-model.go b/model/user-model.go index f559063..0d5bb4b 100644 --- a/model/user-model.go +++ b/model/user-model.go @@ -2,8 +2,11 @@ package model type User struct { ID string `json:"id" gorm:"type:varchar(32);primaryKey"` - UserName string `json:"userName" gorm:"type:varchar(20);not null;uniqueIndex"` + Avatar string `json:"avatar" gorm:"type:varchar(255);not null;default:''"` + UserName string `json:"userName" gorm:"type:varchar(20);not null"` + Mobile string `json:"mobile" gorm:"type:varchar(11);not null;uniqueIndex"` PasswordHash string `json:"-" gorm:"type:varchar(128);default:''"` RoleID string `json:"roleId" gorm:"type:varchar(32);index"` Role Role `json:"role" gorm:"foreignKey:RoleID;references:ID"` + OrgID string `json:"orgId" gorm:"type:varchar(32);not null;index"` } diff --git a/mysql/org-mysql.go b/mysql/org-mysql.go index ab7746c..0cd3612 100644 --- a/mysql/org-mysql.go +++ b/mysql/org-mysql.go @@ -72,3 +72,17 @@ func OrgList(parentID *string, offset, limit *int) ([]model.Org, int64, error) { } return items, total, nil } + +func OrgCount() (*int64, error) { + db, err := newDB() + if err != nil { + return nil, err + } + + var count int64 + if err := db.Model(&model.Org{}).Count(&count).Error; err != nil { + return nil, err + } + + return &count, nil +} diff --git a/mysql/user-mysql.go b/mysql/user-mysql.go index 158e41d..1cef2d3 100644 --- a/mysql/user-mysql.go +++ b/mysql/user-mysql.go @@ -7,48 +7,35 @@ import ( "myschools.me/heritage/heritage-api/model" ) -func UserByUserName(userName *string) (*model.User, bool, error) { +func UserByUserName(userName *string) (*model.User, error) { db, err := newDB() if err != nil { - return nil, false, err + return nil, err } var u model.User if err := db.Where("user_name = ?", *userName).First(&u).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, false, nil + if err != gorm.ErrRecordNotFound { + return nil, nil } - return nil, false, err } - return &u, true, nil + return &u, nil } -func UserByID(userID *string) (*model.User, bool, error) { +// 获取单个用户信息,用户存在与否根据返回结果判断 +func UserFirst(userID *string) (*model.User, error) { db, err := newDB() if err != nil { - return nil, false, err + return nil, err } var u model.User if err := db.Where("id = ?", *userID).First(&u).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, false, nil + if err != gorm.ErrRecordNotFound { + return nil, err } - return nil, false, err } - return &u, true, nil -} - -func UpdateUserPasswordHash(userID, passwordHash *string) (bool, error) { - db, err := newDB() - if err != nil { - return false, err - } - tx := db.Model(&model.User{}).Where("id = ?", *userID).Update("password_hash", *passwordHash) - if tx.Error != nil { - return false, tx.Error - } - return tx.RowsAffected > 0, nil + return &u, nil } func UserRoleIDByUserID(userID *string) (*string, bool, error) { @@ -90,3 +77,36 @@ func UserCreate(obj *model.User) (*model.User, error) { } return obj, nil } + +func UserList(page, pageSize int) ([]model.User, int64, error) { + db, err := newDB() + if err != nil { + return nil, 0, err + } + + var users []model.User + var total int64 + + // 统计总数 + if err := db.Model(&model.User{}).Count(&total).Error; err != nil { + return nil, 0, err + } + + // 分页查询 + offset := (page - 1) * pageSize + if err := db.Offset(offset).Limit(pageSize).Find(&users).Error; err != nil { + return nil, 0, err + } + + return users, total, nil +} + +// 用户更新,可以更新所有项,不作限制,由service层判断 +func UserUpdate(userID *string, obj map[string]any) error { + db, err := newDB() + if err != nil { + return err + } + + return db.Model(&model.User{}).Where("id = ?", *userID).Updates(obj).Error +} diff --git a/redis/menu-redis.go b/redis/menu-redis.go new file mode 100644 index 0000000..fa15436 --- /dev/null +++ b/redis/menu-redis.go @@ -0,0 +1,52 @@ +package redis + +import ( + "encoding/json" + "fmt" + "time" + + "myschools.me/heritage/heritage-api/model" + + "github.com/sirupsen/logrus" +) + +// MenuListKey 菜单列表缓存键 +var MenuListKey = "menu:list_%s" + +// MenuListSet 将菜单列表放入缓存 +func MenuListSet(obj *string, menus []*model.Menu, expireIn time.Duration) error { + key := fmt.Sprintf(MenuListKey, *obj) + err := set(&key, menus, expireIn) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "MenuListSet", + }).Warnf("Set: %s", err.Error()) + return err + } + return nil +} + +// MenuListGet 从缓存中获取菜单列表 +func MenuListGet(obj *string) ([]*model.Menu, error) { + key := fmt.Sprintf(MenuListKey, *obj) + b, err := getBytes(&key) + if err != nil { + return nil, err + } + + if b == nil { + return nil, nil + } + + var menus []*model.Menu + if err := json.Unmarshal(*b, &menus); err != nil { + return nil, err + } + return menus, nil +} + +// MenuListDel 删除菜单列表缓存 +func MenuListDel(obj *string) error { + key := fmt.Sprintf(MenuListKey, *obj) + return delete(key) +} diff --git a/service/auth-service.go b/service/auth-service.go index 1e1d37d..e68052e 100644 --- a/service/auth-service.go +++ b/service/auth-service.go @@ -24,24 +24,24 @@ func AuthLogin(userName, plainPassword string) (string, *model.User, error) { return "", nil, ErrInvalidCredentials } - u, found, err := mysql.UserByUserName(&userName) + u, err := mysql.UserByUserName(&userName) if err != nil { logrus.WithFields(logrus.Fields{ - "func": "service.Login", + "func": "AuthLogin", "userName": userName, }).Errorf("mysql.UserByUserName: %s", err.Error()) return "", nil, err } - if !found || u == nil || u.PasswordHash == "" { + if u == nil || u.PasswordHash == "" { logrus.WithFields(logrus.Fields{ - "func": "service.AuthLogin", + "func": "AuthLogin", "userName": userName, }).Warnf("user not found or password not set") return "", nil, ErrInvalidCredentials } if !PasswordVerify(u.PasswordHash, plainPassword) { logrus.WithFields(logrus.Fields{ - "func": "service.AuthLogin", + "func": "AuthLogin", "userName": userName, }).Warnf("password verification failed") return "", nil, ErrInvalidCredentials @@ -52,10 +52,11 @@ func AuthLogin(userName, plainPassword string) (string, *model.User, error) { ID: u.ID, UserName: u.UserName, RoleID: u.RoleID, + OrgID: u.OrgID, } if err := redis.UserTokenSet(&token, safeUser); err != nil { logrus.WithFields(logrus.Fields{ - "func": "service.Login", + "func": "AuthLogin", "userID": u.ID, }).Errorf("redis.UserTokenSet: %s", err.Error()) return "", nil, err @@ -72,60 +73,3 @@ func AuthLogout(token string) error { } return redis.UserTokenDel(&token) } - -func AuthChangePassword(userID, oldPassword, newPassword string) error { - oldPassword = strings.TrimSpace(oldPassword) - newPassword = strings.TrimSpace(newPassword) - if oldPassword == "" || newPassword == "" { - return ErrInvalidArgument - } - if len(newPassword) < 6 { - return ErrNewPasswordShort - } - - dbUser, found, err := mysql.UserByID(&userID) - if err != nil { - logrus.WithFields(logrus.Fields{ - "func": "service.ChangePassword", - "userID": userID, - }).Errorf("mysql.UserByID: %s", err.Error()) - return err - } - if !found || dbUser == nil || dbUser.PasswordHash == "" { - logrus.WithFields(logrus.Fields{ - "func": "service.AuthChangePassword", - "userID": userID, - }).Warnf("user not found") - return ErrUserNotFound - } - - if !PasswordVerify(dbUser.PasswordHash, oldPassword) { - logrus.WithFields(logrus.Fields{ - "func": "service.AuthChangePassword", - "userID": userID, - }).Warnf("old password verification failed") - return ErrOldPasswordWrong - } - - hash, err := PasswordHash(newPassword) - if err != nil { - logrus.WithFields(logrus.Fields{ - "func": "service.ChangePassword", - "userID": userID, - }).Errorf("password.HashPassword: %s", err.Error()) - return err - } - - updated, err := mysql.UpdateUserPasswordHash(&userID, &hash) - if err != nil { - logrus.WithFields(logrus.Fields{ - "func": "service.ChangePassword", - "userID": userID, - }).Errorf("mysql.UpdateUserPasswordHash: %s", err.Error()) - return err - } - if !updated { - return ErrUserNotFound - } - return nil -} diff --git a/service/base-service.go b/service/base-service.go index 426c10a..e93b318 100644 --- a/service/base-service.go +++ b/service/base-service.go @@ -20,16 +20,16 @@ func newUserID() string { // 专给机构创建用的ID,20位 func newOrgID() string { i := uuid.Must(uuid.NewV7()).String() - return strings.ReplaceAll(i, "-", "")[:20] + return strings.ReplaceAll(i, "-", "")[12:] } // 项目ID22位 func newProjectID() string { i := uuid.Must(uuid.NewV7()).String() - return strings.ReplaceAll(i, "-", "")[:20] + return strings.ReplaceAll(i, "-", "")[10:] } func newToken() string { - i := uuid.Must(uuid.NewV7()).String() + i := uuid.Must(uuid.NewUUID()).String() return strings.ReplaceAll(i, "-", "") } diff --git a/service/bootstrap-service.go b/service/bootstrap-service.go index 3cac1dc..db3d8ba 100644 --- a/service/bootstrap-service.go +++ b/service/bootstrap-service.go @@ -87,33 +87,60 @@ func Bootstrap() { } } - userCount, err := mysql.UserCount() + // 初始化管理机构 + orgCount, err := mysql.OrgCount() if err != nil { logrus.WithFields(logrus.Fields{ "func": "Bootstrap", - }).Warnf("mysql.UserCount: %v", err) + }).Warnf("mysql.OrgCount: %v", err) panic(err) } - if *userCount == 0 { - defaultPwd := "admin" - h, err := bcrypt.GenerateFromPassword([]byte(defaultPwd), bcrypt.DefaultCost) + if *orgCount == 0 { + orgid := newOrgID() + // 创建管理机构 + adminOrg := &model.Org{ + ID: orgid, + Code: "admin", + Name: "管理机构", + OrgType: "admin", + ParentID: "0", + } + if err := mysql.OrgCreate(adminOrg); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "Bootstrap", + }).Warnf("mysql.OrgCreate: %v", err) + panic(err) + } + + userCount, err := mysql.UserCount() if err != nil { logrus.WithFields(logrus.Fields{ "func": "Bootstrap", - }).Warnf("bcrypt.GenerateFromPassword: %v", err) + }).Warnf("mysql.UserCount: %v", err) panic(err) } - u := &model.User{ - ID: newUserID(), - UserName: "admin", - PasswordHash: string(h), - RoleID: defaultRole.ID, - } - if _, err := mysql.UserCreate(u); err != nil { - logrus.WithFields(logrus.Fields{ - "func": "Bootstrap", - }).Warnf("mysql.UserCreate: %v", err) - panic(err) + if *userCount == 0 { + defaultPwd := "admin" + h, err := bcrypt.GenerateFromPassword([]byte(defaultPwd), bcrypt.DefaultCost) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "Bootstrap", + }).Warnf("bcrypt.GenerateFromPassword: %v", err) + panic(err) + } + u := &model.User{ + ID: newUserID(), + UserName: "admin", + PasswordHash: string(h), + RoleID: defaultRole.ID, + OrgID: orgid, + } + if _, err := mysql.UserCreate(u); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "Bootstrap", + }).Warnf("mysql.UserCreate: %v", err) + panic(err) + } } } diff --git a/service/menu-service.go b/service/menu-service.go index 53296af..65ed4cb 100644 --- a/service/menu-service.go +++ b/service/menu-service.go @@ -1,26 +1,29 @@ package service import ( + "time" + "github.com/sirupsen/logrus" "myschools.me/heritage/heritage-api/model" "myschools.me/heritage/heritage-api/mysql" + "myschools.me/heritage/heritage-api/redis" ) func MenuList(usr *model.User) ([]*model.Menu, error) { - // 获取用户角色 ID - roleID, found, err := mysql.UserRoleIDByUserID(&usr.ID) + // 先从缓存中获取角色对应的菜单列表 + allMenus, err := redis.MenuListGet(&usr.RoleID) + if err == nil && len(allMenus) > 0 { + return allMenus, nil + } + if err != nil { logrus.WithFields(logrus.Fields{ "func": "MenuList", - }).Warnf("mysql.UserRoleIDByUserID: %v", err) - return nil, err - } - if !found { - return nil, nil + }).Warnf("redis.MenuListGet: %v", err) } // 获取角色拥有的所有权限 - permissionCodes, err := mysql.RolePermissionsList(roleID) + permissionCodes, err := mysql.RolePermissionsList(&usr.RoleID) if err != nil { logrus.WithFields(logrus.Fields{ "func": "MenuList", @@ -28,8 +31,8 @@ func MenuList(usr *model.User) ([]*model.Menu, error) { return nil, err } - // 获取所有菜单 - allMenus, err := mysql.MenuList() + // 缓存中没有菜单,从数据库获取 + menus, err := mysql.MenuList() if err != nil { logrus.WithFields(logrus.Fields{ "func": "MenuList", @@ -49,13 +52,22 @@ func MenuList(usr *model.User) ([]*model.Menu, error) { // 过滤有权限的菜单 var filteredMenus []*model.Menu - for i := range allMenus { - if hasPermission(allMenus[i].PermissionCode) { - filteredMenus = append(filteredMenus, &allMenus[i]) + for i := range menus { + if hasPermission(menus[i].PermissionCode) { + filteredMenus = append(filteredMenus, &menus[i]) } } - return menuTreeBuild(filteredMenus, 0), nil + menutree := menuTreeBuild(filteredMenus, 0) + + // 将菜单列表放入缓存 + if err := redis.MenuListSet(&usr.RoleID, menutree, 60*time.Second); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "MenuList", + }).Warnf("redis.MenuListSet: %v", err) + // 缓存设置失败,不影响返回结果 + } + return menutree, nil } func menuTreeBuild(menus []*model.Menu, parentID uint) []*model.Menu { diff --git a/service/user-service.go b/service/user-service.go new file mode 100644 index 0000000..2bcfb45 --- /dev/null +++ b/service/user-service.go @@ -0,0 +1,240 @@ +package service + +import ( + "errors" + + "github.com/sirupsen/logrus" + "golang.org/x/crypto/bcrypt" + "myschools.me/heritage/heritage-api/model" + "myschools.me/heritage/heritage-api/mysql" +) + +// UserCreateRequest 创建用户请求 +type UserCreateRequest struct { + UserName string `json:"userName" binding:"required"` + Password string `json:"password" binding:"required"` + RoleID string `json:"roleId" binding:"required"` + OrgID string `json:"orgId" binding:"required"` +} + +// UserUpdateRequest 更新用户请求 +type UserUpdateRequest struct { + UserName string `json:"userName"` + RoleID string `json:"roleId"` + OrgID string `json:"orgId"` +} + +// UserPasswordUpdateRequest 更新密码请求 +type UserPasswordUpdateRequest struct { + OldPassword string `json:"oldPassword" binding:"required"` + NewPassword string `json:"newPassword" binding:"required"` +} + +// UserResponse 用户响应 +type UserResponse struct { + User *model.User `json:"user"` +} + +// UserListResponse 用户列表响应 +type UserListResponse struct { + Users []model.User `json:"users"` + Total int64 `json:"total"` +} + +// UserCreate 创建用户 +func UserCreate(req UserCreateRequest) (*model.User, error) { + // 检查用户名是否已存在 + existingUser, err := mysql.UserByUserName(&req.UserName) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "UserCreate", + }).Warnf("mysql.UserByUserName: %v", err) + return nil, err + } + if existingUser != nil && existingUser.ID != "" { + return nil, errors.New("用户名已存在") + } + + // 密码加密 + h, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "UserCreate", + }).Warnf("bcrypt.GenerateFromPassword: %v", err) + return nil, err + } + + // 创建用户 + user := &model.User{ + ID: newUserID(), + UserName: req.UserName, + PasswordHash: string(h), + RoleID: req.RoleID, + OrgID: req.OrgID, + } + + createdUser, err := mysql.UserCreate(user) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "UserCreate", + }).Warnf("mysql.UserCreate: %v", err) + return nil, err + } + + return createdUser, nil +} + +// UserUpdate 更新用户 +func UserUpdate(userID string, req UserUpdateRequest) (*model.User, error) { + // 检查用户是否存在 + existingUser, err := mysql.UserFirst(&userID) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "UserUpdate", + }).Warnf("mysql.UserFirst: %v", err) + return nil, err + } + if existingUser.ID == "" { + return nil, errors.New("用户不存在") + } + + // 检查用户名是否已被其他用户使用 + if req.UserName != "" && req.UserName != existingUser.UserName { + otherUser, err := mysql.UserByUserName(&req.UserName) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "UserUpdate", + }).Warnf("mysql.UserByUserName: %v", err) + return nil, err + } + if otherUser != nil && otherUser.ID != userID { + return nil, errors.New("用户名已存在") + } + } + + // 构建更新数据 + updateData := make(map[string]any) + if req.UserName != "" { + updateData["user_name"] = req.UserName + } + if req.RoleID != "" { + updateData["role_id"] = req.RoleID + } + if req.OrgID != "" { + updateData["org_id"] = req.OrgID + } + + // 更新用户 + if err := mysql.UserUpdate(&userID, updateData); err != nil { + logrus.WithFields(logrus.Fields{ + "func": "UserUpdate", + }).Warnf("mysql.UserUpdate: %v", err) + return nil, err + } + + // 获取更新后的用户 + updatedUser, err := mysql.UserFirst(&userID) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "UserUpdate", + }).Warnf("mysql.UserFirst: %v", err) + return nil, err + } + if updatedUser.ID == "" { + return nil, errors.New("用户不存在") + } + + return updatedUser, nil +} + +// UserDelete 删除用户 +func UserDelete(userID string) (bool, error) { + // 检查用户是否存在 + existingUser, err := mysql.UserFirst(&userID) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "UserDelete", + }).Warnf("mysql.UserFirst: %v", err) + return false, err + } + if existingUser.ID == "" { + return false, errors.New("用户不存在") + } + + // 这里可以添加删除用户的逻辑 + // 例如:软删除或硬删除 + // 由于当前 mysql 包中没有提供删除用户的函数,我们可以先返回一个错误 + return false, errors.New("删除用户功能暂未实现") +} + +// UserGet 获取用户 +func UserGet(userID string) (*model.User, error) { + // 检查用户是否存在 + user, err := mysql.UserFirst(&userID) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "UserGet", + }).Warnf("mysql.UserFirst: %v", err) + return nil, err + } + if user.ID == "" { + return nil, errors.New("用户不存在") + } + + return user, nil +} + +// UserList 获取用户列表 +func UserList(page, pageSize int) ([]model.User, int64, error) { + // 获取所有用户 + users, total, err := mysql.UserList(page, pageSize) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "UserList", + }).Warnf("mysql.UserList: %v", err) + return nil, 0, err + } + + return users, total, nil +} + +// UserPasswordUpdate 更新用户密码 +func UserPasswordUpdate(userID string, req UserPasswordUpdateRequest) error { + // 检查用户是否存在 + user, err := mysql.UserFirst(&userID) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "UserPasswordUpdate", + }).Warnf("mysql.UserFirst: %v", err) + return err + } + if user.ID == "" { + return errors.New("用户不存在") + } + + // 验证旧密码 + err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.OldPassword)) + if err != nil { + return errors.New("旧密码错误") + } + + // 密码加密 + h, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "UserPasswordUpdate", + }).Warnf("bcrypt.GenerateFromPassword: %v", err) + return err + } + + // 更新密码 + err = mysql.UserUpdate(&userID, map[string]any{"password_hash": string(h)}) + if err != nil { + logrus.WithFields(logrus.Fields{ + "func": "UserPasswordUpdate", + }).Warnf("mysql.UserUpdate: %v", err) + return err + } + + return nil +}