package handlers import ( "net/http" "strconv" "ezops/internal/api/middleware" "ezops/internal/config" "ezops/internal/models" "github.com/gin-gonic/gin" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) // UserHandler 用户处理器 type UserHandler struct { db *gorm.DB cfg *config.Config } // NewUserHandler 创建用户处理器 func NewUserHandler(db *gorm.DB, cfg *config.Config) *UserHandler { return &UserHandler{db: db, cfg: cfg} } // Login 用户登录 // @Summary 用户登录 // @Description 用户登录获取JWT令牌 // @Tags 用户 // @Accept json // @Produce json // @Param request body models.LoginRequest true "登录请求" // @Success 200 {object} models.Response{data=models.LoginResponse} // @Failure 400 {object} models.Response // @Failure 401 {object} models.Response // @Router /auth/login [post] func (h *UserHandler) Login(c *gin.Context) { var req models.LoginRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, models.Response{ Code: 400, Message: "请求参数错误: " + err.Error(), }) return } var user models.User err := h.db.Where("username = ?", req.Username).First(&user).Error if err == nil { // 本地用户存在 if user.Status != 1 { c.JSON(http.StatusUnauthorized, models.Response{ Code: 401, Message: "账号已被禁用", }) return } // 先尝试本地密码验证 passOk := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)) == nil if !passOk { // 本地密码失败 — LDAP 用户尝试 LDAP 认证 if user.AuthSource == "ldap" || user.Password == "-" { ldapInfo, ldapErr := tryLDAPAuth(h.db, req.Username, req.Password) if ldapErr != nil { c.JSON(http.StatusUnauthorized, models.Response{ Code: 401, Message: "用户名或密码错误", }) return } // LDAP OK — 更新本地信息 if ldapInfo != nil { updates := map[string]interface{}{} if ldapInfo.DisplayName != "" && ldapInfo.DisplayName != user.RealName { updates["real_name"] = ldapInfo.DisplayName } if ldapInfo.Email != "" && ldapInfo.Email != user.Email { updates["email"] = ldapInfo.Email } if len(updates) > 0 { h.db.Model(&user).Updates(updates) } } } else { c.JSON(http.StatusUnauthorized, models.Response{ Code: 401, Message: "用户名或密码错误", }) return } } } else if err == gorm.ErrRecordNotFound { // 本地用户不存在 — 尝试 LDAP 认证 ldapInfo, ldapErr := tryLDAPAuth(h.db, req.Username, req.Password) if ldapErr != nil { c.JSON(http.StatusUnauthorized, models.Response{ Code: 401, Message: "用户名或密码错误", }) return } // LDAP 认证成功,自动创建本地用户 newUser := models.User{ Username: ldapInfo.Username, Password: "-", RealName: ldapInfo.DisplayName, Email: ldapInfo.Email, Status: 1, AuthSource: "ldap", } if newUser.RealName == "" { newUser.RealName = newUser.Username } if err := h.db.Create(&newUser).Error; err != nil { c.JSON(http.StatusInternalServerError, models.Response{ Code: 500, Message: "创建LDAP用户失败: " + err.Error(), }) return } user = newUser } else { c.JSON(http.StatusInternalServerError, models.Response{ Code: 500, Message: "数据库错误", }) return } // 生成JWT令牌 token, err := middleware.GenerateToken(&user, &h.cfg.JWT) if err != nil { c.JSON(http.StatusInternalServerError, models.Response{ Code: 500, Message: "生成令牌失败", }) return } // 更新最后登录时间 now := h.db.NowFunc() h.db.Model(&user).Update("last_login", &now) c.JSON(http.StatusOK, models.Response{ Code: 200, Message: "登录成功", Data: models.LoginResponse{ Token: token, ExpiresIn: h.cfg.JWT.ExpireHour * 3600, User: &user, }, }) } // Register 用户注册 // @Summary 用户注册 // @Description 注册新用户 // @Tags 用户 // @Accept json // @Produce json // @Param request body models.CreateUserRequest true "注册请求" // @Success 200 {object} models.Response{data=models.User} // @Failure 400 {object} models.Response // @Router /auth/register [post] func (h *UserHandler) Register(c *gin.Context) { var req models.CreateUserRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, models.Response{ Code: 400, Message: "请求参数错误: " + err.Error(), }) return } // 检查用户名是否已存在 var existingUser models.User if err := h.db.Where("username = ?", req.Username).First(&existingUser).Error; err == nil { c.JSON(http.StatusBadRequest, models.Response{ Code: 400, Message: "用户名已存在", }) return } // 加密密码 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) if err != nil { c.JSON(http.StatusInternalServerError, models.Response{ Code: 500, Message: "密码加密失败", }) return } user := models.User{ Username: req.Username, Password: string(hashedPassword), Email: req.Email, Phone: req.Phone, RealName: req.RealName, Status: 1, } if err := h.db.Create(&user).Error; err != nil { c.JSON(http.StatusInternalServerError, models.Response{ Code: 500, Message: "创建用户失败", }) return } c.JSON(http.StatusOK, models.Response{ Code: 200, Message: "注册成功", Data: user, }) } // GetProfile 获取当前用户信息 // @Summary 获取当前用户信息 // @Description 获取当前登录用户的信息 // @Tags 用户 // @Produce json // @Security Bearer // @Success 200 {object} models.Response{data=models.User} // @Router /user/profile [get] func (h *UserHandler) GetProfile(c *gin.Context) { userID := middleware.GetUserID(c) var user models.User if err := h.db.Preload("Roles").First(&user, userID).Error; err != nil { c.JSON(http.StatusNotFound, models.Response{ Code: 404, Message: "用户不存在", }) return } c.JSON(http.StatusOK, models.Response{ Code: 200, Message: "success", Data: user, }) } // UpdateProfile 更新当前用户信息 // @Summary 更新当前用户信息 // @Description 更新当前登录用户的信息 // @Tags 用户 // @Accept json // @Produce json // @Security Bearer // @Param request body models.UpdateUserRequest true "更新请求" // @Success 200 {object} models.Response{data=models.User} // @Router /user/profile [put] func (h *UserHandler) UpdateProfile(c *gin.Context) { userID := middleware.GetUserID(c) var req models.UpdateUserRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, models.Response{ Code: 400, Message: "请求参数错误: " + err.Error(), }) return } updates := map[string]interface{}{} if req.Email != "" { updates["email"] = req.Email } if req.Phone != "" { updates["phone"] = req.Phone } if req.RealName != "" { updates["real_name"] = req.RealName } if err := h.db.Model(&models.User{}).Where("id = ?", userID).Updates(updates).Error; err != nil { c.JSON(http.StatusInternalServerError, models.Response{ Code: 500, Message: "更新失败", }) return } var user models.User h.db.First(&user, userID) c.JSON(http.StatusOK, models.Response{ Code: 200, Message: "更新成功", Data: user, }) } // ChangePassword 修改密码 // @Summary 修改密码 // @Description 修改当前用户密码 // @Tags 用户 // @Accept json // @Produce json // @Security Bearer // @Param request body models.ChangePasswordRequest true "修改密码请求" // @Success 200 {object} models.Response // @Router /user/password [put] func (h *UserHandler) ChangePassword(c *gin.Context) { userID := middleware.GetUserID(c) var req models.ChangePasswordRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, models.Response{ Code: 400, Message: "请求参数错误: " + err.Error(), }) return } var user models.User if err := h.db.First(&user, userID).Error; err != nil { c.JSON(http.StatusNotFound, models.Response{ Code: 404, Message: "用户不存在", }) return } if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.OldPassword)); err != nil { c.JSON(http.StatusBadRequest, models.Response{ Code: 400, Message: "原密码错误", }) return } hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost) if err != nil { c.JSON(http.StatusInternalServerError, models.Response{ Code: 500, Message: "密码加密失败", }) return } if err := h.db.Model(&user).Update("password", string(hashedPassword)).Error; err != nil { c.JSON(http.StatusInternalServerError, models.Response{ Code: 500, Message: "修改密码失败", }) return } c.JSON(http.StatusOK, models.Response{ Code: 200, Message: "密码修改成功", }) } // ListUsers 获取用户列表 // @Summary 获取用户列表 // @Description 获取所有用户列表(管理员) // @Tags 用户 // @Produce json // @Security Bearer // @Param page query int false "页码" // @Param page_size query int false "每页数量" // @Param keyword query string false "搜索关键词" // @Success 200 {object} models.Response{data=models.ListResponse} // @Router /users [get] func (h *UserHandler) ListUsers(c *gin.Context) { var query models.PageQuery if err := c.ShouldBindQuery(&query); err != nil { c.JSON(http.StatusBadRequest, models.Response{ Code: 400, Message: "请求参数错误", }) return } db := h.db.Model(&models.User{}) if query.Keyword != "" { db = db.Where("username LIKE ? OR email LIKE ? OR real_name LIKE ?", "%"+query.Keyword+"%", "%"+query.Keyword+"%", "%"+query.Keyword+"%") } var total int64 db.Count(&total) var users []models.User db.Preload("Roles").Offset(query.GetOffset()).Limit(query.GetLimit()).Find(&users) c.JSON(http.StatusOK, models.Response{ Code: 200, Message: "success", Data: models.ListResponse{ List: users, Total: total, }, }) } // GetUser 获取用户详情 // @Summary 获取用户详情 // @Description 获取指定用户的详细信息 // @Tags 用户 // @Produce json // @Security Bearer // @Param id path int true "用户ID" // @Success 200 {object} models.Response{data=models.User} // @Router /users/{id} [get] func (h *UserHandler) GetUser(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var user models.User if err := h.db.Preload("Roles").First(&user, id).Error; err != nil { c.JSON(http.StatusNotFound, models.Response{ Code: 404, Message: "用户不存在", }) return } c.JSON(http.StatusOK, models.Response{ Code: 200, Message: "success", Data: user, }) } // CreateUser 创建用户 // @Summary 创建用户 // @Description 创建新用户(管理员) // @Tags 用户 // @Accept json // @Produce json // @Security Bearer // @Param request body models.CreateUserRequest true "创建请求" // @Success 200 {object} models.Response{data=models.User} // @Router /users [post] func (h *UserHandler) CreateUser(c *gin.Context) { var req models.CreateUserRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, models.Response{ Code: 400, Message: "请求参数错误: " + err.Error(), }) return } // 检查用户名是否已存在 var existingUser models.User if err := h.db.Where("username = ?", req.Username).First(&existingUser).Error; err == nil { c.JSON(http.StatusBadRequest, models.Response{ Code: 400, Message: "用户名已存在", }) return } // 加密密码 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) if err != nil { c.JSON(http.StatusInternalServerError, models.Response{ Code: 500, Message: "密码加密失败", }) return } user := models.User{ Username: req.Username, Password: string(hashedPassword), Email: req.Email, Phone: req.Phone, RealName: req.RealName, Status: 1, } if err := h.db.Create(&user).Error; err != nil { c.JSON(http.StatusInternalServerError, models.Response{ Code: 500, Message: "创建用户失败", }) return } // 关联角色 if len(req.RoleIDs) > 0 { var roles []models.Role h.db.Find(&roles, req.RoleIDs) h.db.Model(&user).Association("Roles").Append(roles) } c.JSON(http.StatusOK, models.Response{ Code: 200, Message: "创建成功", Data: user, }) } // UpdateUser 更新用户 // @Summary 更新用户 // @Description 更新指定用户信息(管理员) // @Tags 用户 // @Accept json // @Produce json // @Security Bearer // @Param id path int true "用户ID" // @Param request body models.UpdateUserRequest true "更新请求" // @Success 200 {object} models.Response{data=models.User} // @Router /users/{id} [put] func (h *UserHandler) UpdateUser(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) var req models.UpdateUserRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, models.Response{ Code: 400, Message: "请求参数错误: " + err.Error(), }) return } var user models.User if err := h.db.First(&user, id).Error; err != nil { c.JSON(http.StatusNotFound, models.Response{ Code: 404, Message: "用户不存在", }) return } updates := map[string]interface{}{} if req.Email != "" { updates["email"] = req.Email } if req.Phone != "" { updates["phone"] = req.Phone } if req.RealName != "" { updates["real_name"] = req.RealName } if req.Status != nil { updates["status"] = *req.Status } if err := h.db.Model(&user).Updates(updates).Error; err != nil { c.JSON(http.StatusInternalServerError, models.Response{ Code: 500, Message: "更新失败", }) return } // 更新角色 if len(req.RoleIDs) > 0 { var roles []models.Role h.db.Find(&roles, req.RoleIDs) h.db.Model(&user).Association("Roles").Replace(roles) } h.db.Preload("Roles").First(&user, id) c.JSON(http.StatusOK, models.Response{ Code: 200, Message: "更新成功", Data: user, }) } // DeleteUser 删除用户 // @Summary 删除用户 // @Description 删除指定用户(管理员) // @Tags 用户 // @Produce json // @Security Bearer // @Param id path int true "用户ID" // @Success 200 {object} models.Response // @Router /users/{id} [delete] func (h *UserHandler) DeleteUser(c *gin.Context) { id, _ := strconv.Atoi(c.Param("id")) if err := h.db.Delete(&models.User{}, id).Error; err != nil { c.JSON(http.StatusInternalServerError, models.Response{ Code: 500, Message: "删除失败", }) return } c.JSON(http.StatusOK, models.Response{ Code: 200, Message: "删除成功", }) }