init
This commit is contained in:
parent
3f5d062ee8
commit
6fff6d35a5
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 "修改失败"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": "密码更新成功"})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:"-"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, "-", "")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
Loading…
Reference in New Issue