新增用户管理和个人信息功能,支持CRUD操作和密码修改
This commit is contained in:
@@ -66,6 +66,50 @@ func Login(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
type changePasswordRequest struct {
|
||||
OldPassword string `json:"old_password" binding:"required"`
|
||||
NewPassword string `json:"new_password" binding:"required"`
|
||||
}
|
||||
|
||||
func ChangePassword(c *gin.Context) {
|
||||
var req changePasswordRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "请输入原密码和新密码"})
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.NewPassword) < 6 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "新密码长度不能少于6位"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := c.Get("user_id")
|
||||
|
||||
var user model.User
|
||||
if err := db.DB.First(&user, userID).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.OldPassword)); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "原密码错误"})
|
||||
return
|
||||
}
|
||||
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "密码加密失败"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := db.DB.Model(&user).Update("password", string(hash)).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "密码修改失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "密码修改成功"})
|
||||
}
|
||||
|
||||
func Me(c *gin.Context) {
|
||||
userID, _ := c.Get("user_id")
|
||||
username, _ := c.Get("username")
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"lmvpn/internal/db"
|
||||
"lmvpn/internal/model"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type createUserRequest struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Role string `json:"role"`
|
||||
Status *int `json:"status"`
|
||||
}
|
||||
|
||||
type updateUserRequest struct {
|
||||
Status *int `json:"status"`
|
||||
Role string `json:"role"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type userResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Role string `json:"role"`
|
||||
Status int `json:"status"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
func formatUser(u *model.User) userResponse {
|
||||
return userResponse{
|
||||
ID: u.ID,
|
||||
Username: u.Username,
|
||||
Role: u.Role,
|
||||
Status: u.Status,
|
||||
CreatedAt: u.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
UpdatedAt: u.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
}
|
||||
|
||||
func ListUsers(c *gin.Context) {
|
||||
var users []model.User
|
||||
db.DB.Order("id asc").Find(&users)
|
||||
|
||||
result := make([]userResponse, len(users))
|
||||
for i, u := range users {
|
||||
result[i] = formatUser(&u)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"users": result})
|
||||
}
|
||||
|
||||
func CreateUser(c *gin.Context) {
|
||||
var req createUserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
var exist model.User
|
||||
if err := db.DB.Where("username = ?", req.Username).First(&exist).Error; err == nil {
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "用户名已存在"})
|
||||
return
|
||||
}
|
||||
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "密码加密失败"})
|
||||
return
|
||||
}
|
||||
|
||||
user := model.User{
|
||||
Username: req.Username,
|
||||
Password: string(hash),
|
||||
Role: "user",
|
||||
Status: 1,
|
||||
}
|
||||
if req.Role != "" {
|
||||
user.Role = req.Role
|
||||
}
|
||||
if req.Status != nil {
|
||||
user.Status = *req.Status
|
||||
}
|
||||
|
||||
if err := db.DB.Create(&user).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "创建用户失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, formatUser(&user))
|
||||
}
|
||||
|
||||
func UpdateUser(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
currentUserID, _ := c.Get("user_id")
|
||||
|
||||
var user model.User
|
||||
if err := db.DB.First(&user, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
var req updateUserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
updates := map[string]interface{}{}
|
||||
|
||||
if req.Status != nil {
|
||||
updates["status"] = *req.Status
|
||||
}
|
||||
|
||||
if req.Role != "" {
|
||||
if user.ID == currentUserID.(uint) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "不能修改自己的角色"})
|
||||
return
|
||||
}
|
||||
if req.Role != "admin" && user.Role == "admin" {
|
||||
var adminCount int64
|
||||
db.DB.Model(&model.User{}).Where("role = ?", "admin").Count(&adminCount)
|
||||
if adminCount <= 1 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "系统至少需要保留一个管理员"})
|
||||
return
|
||||
}
|
||||
}
|
||||
updates["role"] = req.Role
|
||||
}
|
||||
|
||||
if req.Password != "" {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "密码加密失败"})
|
||||
return
|
||||
}
|
||||
updates["password"] = string(hash)
|
||||
}
|
||||
|
||||
if len(updates) == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "没有需要更新的字段"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := db.DB.Model(&user).Updates(updates).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "更新用户失败"})
|
||||
return
|
||||
}
|
||||
|
||||
db.DB.First(&user, id)
|
||||
c.JSON(http.StatusOK, formatUser(&user))
|
||||
}
|
||||
|
||||
func DeleteUser(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
currentUserID, _ := c.Get("user_id")
|
||||
|
||||
if uint(id) == currentUserID.(uint) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "不能删除自己"})
|
||||
return
|
||||
}
|
||||
|
||||
var user model.User
|
||||
if err := db.DB.First(&user, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
if user.Role == "admin" {
|
||||
var adminCount int64
|
||||
db.DB.Model(&model.User{}).Where("role = ?", "admin").Count(&adminCount)
|
||||
if adminCount <= 1 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "系统至少需要保留一个管理员"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := db.DB.Delete(&user).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "删除用户失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "删除成功"})
|
||||
}
|
||||
@@ -51,6 +51,18 @@ func ParseToken(tokenStr string) (*Claims, error) {
|
||||
return nil, jwt.ErrSignatureInvalid
|
||||
}
|
||||
|
||||
func AdminMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
role, _ := c.Get("role")
|
||||
if role != "admin" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "无权限访问"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func AuthMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
|
||||
@@ -20,6 +20,16 @@ func Setup(r *gin.Engine) {
|
||||
auth.Use(middleware.AuthMiddleware())
|
||||
{
|
||||
auth.GET("/me", handler.Me)
|
||||
auth.PUT("/me/password", handler.ChangePassword)
|
||||
}
|
||||
|
||||
admin := r.Group("/api/admin")
|
||||
admin.Use(middleware.AuthMiddleware(), middleware.AdminMiddleware())
|
||||
{
|
||||
admin.GET("/users", handler.ListUsers)
|
||||
admin.POST("/users", handler.CreateUser)
|
||||
admin.PUT("/users/:id", handler.UpdateUser)
|
||||
admin.DELETE("/users/:id", handler.DeleteUser)
|
||||
}
|
||||
|
||||
fs := http.FileServer(http.Dir("./dist"))
|
||||
|
||||
Reference in New Issue
Block a user