package handler import ( "net/http" "strconv" "github.com/gin-gonic/gin" "github.com/user-management-system/internal/auth" "github.com/user-management-system/internal/domain" "github.com/user-management-system/internal/service" ) // UserHandler handles user management requests type UserHandler struct { userService *service.UserService } // NewUserHandler creates a new UserHandler func NewUserHandler(userService *service.UserService) *UserHandler { return &UserHandler{userService: userService} } // CreateUser 创建用户 // @Summary 创建用户 // @Description 创建新用户账号(仅管理员) // @Tags 用户管理 // @Accept json // @Produce json // @Security BearerAuth // @Param request body CreateUserRequest true "用户信息" // @Success 201 {object} Response{data=UserResponse} "用户创建成功" // @Failure 400 {object} Response "请求参数错误" // @Failure 401 {object} Response "未认证" // @Failure 403 {object} Response "无权限" // @Router /api/v1/users [post] func (h *UserHandler) CreateUser(c *gin.Context) { var req struct { Username string `json:"username" binding:"required"` Email string `json:"email"` Password string `json:"password"` Nickname string `json:"nickname"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } user := &domain.User{ Username: req.Username, Email: domain.StrPtr(req.Email), Nickname: req.Nickname, Status: domain.UserStatusActive, } if req.Password != "" { hashed, err := auth.HashPassword(req.Password) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to hash password"}) return } user.Password = hashed } if err := h.userService.Create(c.Request.Context(), user); err != nil { handleError(c, err) return } c.JSON(http.StatusCreated, gin.H{ "code": 0, "message": "success", "data": toUserResponse(user), }) } // ListUsers 获取用户列表 // @Summary 获取用户列表 // @Description 获取用户列表,支持游标分页和偏移分页 // @Tags 用户管理 // @Produce json // @Security BearerAuth // @Param cursor query string false "游标分页游标" // @Param size query int false "每页大小" // @Param offset query int false "偏移分页偏移量" // @Param limit query int false "每页大小" // @Success 200 {object} Response{data=UserListResponse} "用户列表" // @Router /api/v1/users [get] func (h *UserHandler) ListUsers(c *gin.Context) { cursor := c.Query("cursor") sizeStr := c.DefaultQuery("size", "") // Use cursor-based pagination when cursor is provided if cursor != "" || sizeStr != "" { var req service.ListCursorRequest if err := c.ShouldBindQuery(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } result, err := h.userService.ListCursor(c.Request.Context(), &req) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": result, }) return } // Fallback to legacy offset-based pagination offset, _ := strconv.ParseInt(c.DefaultQuery("offset", "0"), 10, 64) limit, _ := strconv.ParseInt(c.DefaultQuery("limit", "20"), 10, 64) users, total, err := h.userService.List(c.Request.Context(), int(offset), int(limit)) if err != nil { handleError(c, err) return } userResponses := make([]*UserResponse, len(users)) for i, u := range users { userResponses[i] = toUserResponse(u) } c.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": gin.H{ "users": userResponses, "total": total, "offset": offset, "limit": limit, }, }) } // GetUser 获取用户详情 // @Summary 获取用户详情 // @Description 根据ID获取用户详细信息 // @Tags 用户管理 // @Produce json // @Security BearerAuth // @Param id path int true "用户ID" // @Success 200 {object} Response{data=UserResponse} "用户信息" // @Failure 404 {object} Response "用户不存在" // @Router /api/v1/users/{id} [get] func (h *UserHandler) GetUser(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid user id"}) return } user, err := h.userService.GetByID(c.Request.Context(), id) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "success", "data": toUserResponse(user)}) } // UpdateUser 更新用户 // @Summary 更新用户信息 // @Description 更新用户的基本信息(仅管理员或本人) // @Tags 用户管理 // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "用户ID" // @Param request body UpdateUserRequest true "更新信息" // @Success 200 {object} Response{data=UserResponse} "更新成功" // @Failure 400 {object} Response "请求参数错误" // @Failure 403 {object} Response "无权限" // @Failure 404 {object} Response "用户不存在" // @Router /api/v1/users/{id} [put] func (h *UserHandler) UpdateUser(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid user id"}) return } var req struct { Email *string `json:"email"` Nickname *string `json:"nickname"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return } user, err := h.userService.GetByID(c.Request.Context(), id) if err != nil { handleError(c, err) return } if req.Email != nil { user.Email = req.Email } if req.Nickname != nil { user.Nickname = *req.Nickname } if err := h.userService.Update(c.Request.Context(), user); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "success", "data": toUserResponse(user)}) } // DeleteUser 删除用户 // @Summary 删除用户 // @Description 删除用户账号(仅管理员) // @Tags 用户管理 // @Produce json // @Security BearerAuth // @Param id path int true "用户ID" // @Success 200 {object} Response "删除成功" // @Failure 403 {object} Response "无权限" // @Failure 404 {object} Response "用户不存在" // @Router /api/v1/users/{id} [delete] func (h *UserHandler) DeleteUser(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid user id"}) return } if err := h.userService.Delete(c.Request.Context(), id); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "success"}) } // UpdatePassword 修改密码 // @Summary 修改用户密码 // @Description 修改用户密码(仅管理员或本人) // @Tags 用户管理 // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "用户ID" // @Param request body UpdatePasswordRequest true "密码信息" // @Success 200 {object} Response "密码修改成功" // @Failure 400 {object} Response "请求参数错误" // @Failure 403 {object} Response "无权限" // @Failure 404 {object} Response "用户不存在" // @Router /api/v1/users/{id}/password [put] func (h *UserHandler) UpdatePassword(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid user id"}) return } var req struct { OldPassword string `json:"old_password" binding:"required"` NewPassword string `json:"new_password" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return } if err := h.userService.ChangePassword(c.Request.Context(), id, req.OldPassword, req.NewPassword); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "密码修改成功"}) } // UpdateUserStatus 更新用户状态 // @Summary 更新用户状态 // @Description 更新用户账号状态(active/inactive/locked/disabled)(仅管理员) // @Tags 用户管理 // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "用户ID" // @Param request body UpdateStatusRequest true "状态信息" // @Success 200 {object} Response "状态更新成功" // @Failure 400 {object} Response "无效的状态值" // @Failure 403 {object} Response "无权限" // @Failure 404 {object} Response "用户不存在" // @Router /api/v1/users/{id}/status [put] func (h *UserHandler) UpdateUserStatus(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid user id"}) return } var req struct { Status string `json:"status" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return } var status domain.UserStatus switch req.Status { case "active", "1": status = domain.UserStatusActive case "inactive", "0": status = domain.UserStatusInactive case "locked", "2": status = domain.UserStatusLocked case "disabled", "3": status = domain.UserStatusDisabled default: c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid status"}) return } if err := h.userService.UpdateStatus(c.Request.Context(), id, status); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "success"}) } // GetUserRoles 获取用户角色 // @Summary 获取用户角色列表 // @Description 获取指定用户的角色列表(仅本人或管理员) // @Tags 用户管理 // @Produce json // @Security BearerAuth // @Param id path int true "用户ID" // @Success 200 {object} Response{data=[]domain.Role} "角色列表" // @Failure 403 {object} Response "无权限" // @Failure 404 {object} Response "用户不存在" // @Router /api/v1/users/{id}/roles [get] func (h *UserHandler) GetUserRoles(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid user id"}) return } // Authorization: only self or admin can view user roles currentUserID := c.GetInt64("user_id") isAdmin := false if roles, ok := c.Get("user_roles"); ok { for _, role := range roles.([]*domain.Role) { if role.Code == "admin" { isAdmin = true break } } } if currentUserID != id && !isAdmin { c.JSON(http.StatusForbidden, gin.H{"code": 403, "message": "permission denied"}) return } roles, err := h.userService.GetUserRoles(c.Request.Context(), id) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": roles, }) } // AssignRoles 分配用户角色 // @Summary 分配用户角色 // @Description 为用户分配角色(替换现有角色)(仅管理员) // @Tags 用户管理 // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "用户ID" // @Param request body AssignRolesRequest true "角色ID列表" // @Success 200 {object} Response "角色分配成功" // @Failure 400 {object} Response "请求参数错误" // @Failure 403 {object} Response "无权限" // @Failure 404 {object} Response "用户不存在" // @Router /api/v1/users/{id}/roles [post] func (h *UserHandler) AssignRoles(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid user id"}) return } var req struct { RoleIDs []int64 `json:"role_ids" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return } if err := h.userService.AssignRoles(c.Request.Context(), id, req.RoleIDs); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "角色分配成功"}) } // BatchUpdateStatus 批量更新用户状态 // @Summary 批量更新用户状态 // @Description 批量更新多个用户的状态(仅管理员) // @Tags 用户管理 // @Accept json // @Produce json // @Security BearerAuth // @Param request body service.BatchUpdateStatusRequest true "批量更新请求" // @Success 200 {object} Response "批量更新成功" // @Failure 400 {object} Response "请求参数错误" // @Failure 403 {object} Response "无权限" // @Router /api/v1/users/batch/status [put] func (h *UserHandler) BatchUpdateStatus(c *gin.Context) { var req service.BatchUpdateStatusRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return } count, err := h.userService.BatchUpdateStatus(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "批量更新失败"}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "更新成功", "data": gin.H{"count": count}}) } // BatchDelete 批量删除用户 // @Summary 批量删除用户 // @Description 批量删除多个用户(仅管理员) // @Tags 用户管理 // @Accept json // @Produce json // @Security BearerAuth // @Param request body service.BatchDeleteRequest true "批量删除请求" // @Success 200 {object} Response "批量删除成功" // @Failure 400 {object} Response "请求参数错误" // @Failure 403 {object} Response "无权限" // @Router /api/v1/users/batch [delete] func (h *UserHandler) BatchDelete(c *gin.Context) { var req service.BatchDeleteRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return } count, err := h.userService.BatchDelete(c.Request.Context(), &req) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "批量删除失败"}) return } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "删除成功", "data": gin.H{"count": count}}) } // ListAdmins 获取管理员列表 // @Summary 获取管理员列表 // @Description 获取所有管理员用户列表(仅管理员) // @Tags 用户管理 // @Produce json // @Security BearerAuth // @Success 200 {object} Response{data=[]UserResponse} "管理员列表" // @Failure 403 {object} Response "无权限" // @Router /api/v1/users/admins [get] func (h *UserHandler) ListAdmins(c *gin.Context) { admins, err := h.userService.ListAdmins(c.Request.Context()) if err != nil { handleError(c, err) return } adminResponses := make([]*UserResponse, len(admins)) for i, u := range admins { adminResponses[i] = toUserResponse(u) } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "success", "data": adminResponses}) } // CreateAdmin 创建管理员 // @Summary 创建管理员 // @Description 创建新管理员账号(仅管理员) // @Tags 用户管理 // @Accept json // @Produce json // @Security BearerAuth // @Param request body CreateAdminRequest true "管理员信息" // @Success 201 {object} Response{data=UserResponse} "管理员创建成功" // @Failure 400 {object} Response "请求参数错误" // @Failure 403 {object} Response "无权限" // @Router /api/v1/users/admins [post] func (h *UserHandler) CreateAdmin(c *gin.Context) { var req struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` Email string `json:"email"` Nickname string `json:"nickname"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()}) return } adminReq := &service.CreateAdminRequest{ Username: req.Username, Password: req.Password, Email: req.Email, Nickname: req.Nickname, } admin, err := h.userService.CreateAdmin(c.Request.Context(), adminReq) if err != nil { handleError(c, err) return } c.JSON(http.StatusCreated, gin.H{"code": 0, "message": "管理员创建成功", "data": toUserResponse(admin)}) } // DeleteAdmin 删除管理员 // @Summary 删除管理员 // @Description 删除管理员角色(最后管理员保护、自删保护)(仅管理员) // @Tags 用户管理 // @Produce json // @Security BearerAuth // @Param id path int true "用户ID" // @Success 200 {object} Response "管理员已移除" // @Failure 400 {object} Response "无效的用户ID" // @Failure 403 {object} Response "无权限" // @Failure 409 {object} Response "无法删除(最后管理员或自删)" // @Router /api/v1/users/admins/{id} [delete] func (h *UserHandler) DeleteAdmin(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid user id"}) return } currentUserID := c.GetInt64("user_id") if err := h.userService.DeleteAdmin(c.Request.Context(), id, currentUserID); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"code": 0, "message": "管理员已移除"}) } type UserResponse struct { ID int64 `json:"id"` Username string `json:"username"` Email string `json:"email,omitempty"` Nickname string `json:"nickname,omitempty"` Status string `json:"status"` } func toUserResponse(u *domain.User) *UserResponse { email := "" if u.Email != nil { email = *u.Email } return &UserResponse{ ID: u.ID, Username: u.Username, Email: email, Nickname: u.Nickname, Status: strconv.FormatInt(int64(u.Status), 10), } }