package handler import ( "net/http" "strconv" "carrotskin/internal/container" "carrotskin/internal/model" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // AdminHandler 管理员处理器 type AdminHandler struct { container *container.Container } // NewAdminHandler 创建管理员处理器 func NewAdminHandler(c *container.Container) *AdminHandler { return &AdminHandler{container: c} } // SetUserRoleRequest 设置用户角色请求 type SetUserRoleRequest struct { UserID int64 `json:"user_id" binding:"required"` Role string `json:"role" binding:"required,oneof=user admin"` } // SetUserRole 设置用户角色 // @Summary 设置用户角色 // @Description 管理员设置指定用户的角色 // @Tags Admin // @Accept json // @Produce json // @Param request body SetUserRoleRequest true "设置角色请求" // @Success 200 {object} model.Response // @Failure 400 {object} model.Response // @Failure 403 {object} model.Response // @Security BearerAuth // @Router /admin/users/role [put] func (h *AdminHandler) SetUserRole(c *gin.Context) { var req SetUserRoleRequest if err := c.ShouldBindJSON(&req); err != nil { RespondBadRequest(c, "参数错误", err) return } // 获取当前操作者ID operatorID, _ := c.Get("user_id") // 不能修改自己的角色 if req.UserID == operatorID.(int64) { c.JSON(http.StatusBadRequest, model.NewErrorResponse( model.CodeBadRequest, "不能修改自己的角色", nil, )) return } // 检查目标用户是否存在 targetUser, err := h.container.UserRepo.FindByID(c.Request.Context(), req.UserID) if err != nil || targetUser == nil { c.JSON(http.StatusNotFound, model.NewErrorResponse( model.CodeNotFound, "用户不存在", nil, )) return } // 更新用户角色 err = h.container.UserRepo.UpdateFields(c.Request.Context(), req.UserID, map[string]interface{}{ "role": req.Role, }) if err != nil { RespondServerError(c, "更新用户角色失败", err) return } h.container.Logger.Info("管理员修改用户角色", zap.Int64("operator_id", operatorID.(int64)), zap.Int64("target_user_id", req.UserID), zap.String("new_role", req.Role), ) c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{ "message": "用户角色更新成功", "user_id": req.UserID, "role": req.Role, })) } // GetUserList 获取用户列表 // @Summary 获取用户列表 // @Description 管理员获取所有用户列表 // @Tags Admin // @Produce json // @Param page query int false "页码" default(1) // @Param page_size query int false "每页数量" default(20) // @Success 200 {object} model.Response // @Failure 403 {object} model.Response // @Security BearerAuth // @Router /admin/users [get] func (h *AdminHandler) GetUserList(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) if page < 1 { page = 1 } if pageSize < 1 || pageSize > 100 { pageSize = 20 } // 使用数据库直接查询用户列表 var users []model.User var total int64 db := h.container.DB db.Model(&model.User{}).Count(&total) db.Offset((page - 1) * pageSize).Limit(pageSize).Order("id DESC").Find(&users) // 构建响应(隐藏敏感信息) userList := make([]gin.H, len(users)) for i, u := range users { userList[i] = gin.H{ "id": u.ID, "username": u.Username, "email": u.Email, "avatar": u.Avatar, "role": u.Role, "status": u.Status, "points": u.Points, "last_login_at": u.LastLoginAt, "created_at": u.CreatedAt, } } c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{ "users": userList, "total": total, "page": page, "page_size": pageSize, })) } // GetUserDetail 获取用户详情 // @Summary 获取用户详情 // @Description 管理员获取指定用户的详细信息 // @Tags Admin // @Produce json // @Param id path int true "用户ID" // @Success 200 {object} model.Response // @Failure 404 {object} model.Response // @Security BearerAuth // @Router /admin/users/{id} [get] func (h *AdminHandler) GetUserDetail(c *gin.Context) { userID, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { RespondBadRequest(c, "无效的用户ID", err) return } user, err := h.container.UserRepo.FindByID(c.Request.Context(), userID) if err != nil || user == nil { c.JSON(http.StatusNotFound, model.NewErrorResponse( model.CodeNotFound, "用户不存在", nil, )) return } c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{ "id": user.ID, "username": user.Username, "email": user.Email, "avatar": user.Avatar, "role": user.Role, "status": user.Status, "points": user.Points, "properties": user.Properties, "last_login_at": user.LastLoginAt, "created_at": user.CreatedAt, "updated_at": user.UpdatedAt, })) } // SetUserStatusRequest 设置用户状态请求 type SetUserStatusRequest struct { UserID int64 `json:"user_id" binding:"required"` Status int16 `json:"status" binding:"required,oneof=1 0 -1"` // 1:正常, 0:禁用, -1:删除 } // SetUserStatus 设置用户状态 // @Summary 设置用户状态 // @Description 管理员设置用户状态(启用/禁用) // @Tags Admin // @Accept json // @Produce json // @Param request body SetUserStatusRequest true "设置状态请求" // @Success 200 {object} model.Response // @Failure 400 {object} model.Response // @Security BearerAuth // @Router /admin/users/status [put] func (h *AdminHandler) SetUserStatus(c *gin.Context) { var req SetUserStatusRequest if err := c.ShouldBindJSON(&req); err != nil { RespondBadRequest(c, "参数错误", err) return } operatorID, _ := c.Get("user_id") // 不能修改自己的状态 if req.UserID == operatorID.(int64) { c.JSON(http.StatusBadRequest, model.NewErrorResponse( model.CodeBadRequest, "不能修改自己的状态", nil, )) return } // 检查目标用户是否存在 targetUser, err := h.container.UserRepo.FindByID(c.Request.Context(), req.UserID) if err != nil || targetUser == nil { c.JSON(http.StatusNotFound, model.NewErrorResponse( model.CodeNotFound, "用户不存在", nil, )) return } // 更新用户状态 err = h.container.UserRepo.UpdateFields(c.Request.Context(), req.UserID, map[string]interface{}{ "status": req.Status, }) if err != nil { RespondServerError(c, "更新用户状态失败", err) return } statusText := map[int16]string{1: "正常", 0: "禁用", -1: "删除"}[req.Status] h.container.Logger.Info("管理员修改用户状态", zap.Int64("operator_id", operatorID.(int64)), zap.Int64("target_user_id", req.UserID), zap.Int16("new_status", req.Status), ) c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{ "message": "用户状态更新成功", "user_id": req.UserID, "status": req.Status, "status_text": statusText, })) } // DeleteTexture 管理员删除材质 // @Summary 管理员删除材质 // @Description 管理员可以删除任意材质(用于审核不当内容) // @Tags Admin // @Produce json // @Param id path int true "材质ID" // @Success 200 {object} model.Response // @Failure 404 {object} model.Response // @Security BearerAuth // @Router /admin/textures/{id} [delete] func (h *AdminHandler) DeleteTexture(c *gin.Context) { textureID, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { RespondBadRequest(c, "无效的材质ID", err) return } operatorID, _ := c.Get("user_id") // 检查材质是否存在 var texture model.Texture if err := h.container.DB.First(&texture, textureID).Error; err != nil { c.JSON(http.StatusNotFound, model.NewErrorResponse( model.CodeNotFound, "材质不存在", nil, )) return } // 删除材质 if err := h.container.DB.Delete(&texture).Error; err != nil { RespondServerError(c, "删除材质失败", err) return } h.container.Logger.Info("管理员删除材质", zap.Int64("operator_id", operatorID.(int64)), zap.Int64("texture_id", textureID), zap.Int64("uploader_id", texture.UploaderID), zap.String("texture_name", texture.Name), ) c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{ "message": "材质删除成功", "texture_id": textureID, })) } // GetTextureList 管理员获取材质列表 // @Summary 管理员获取材质列表 // @Description 管理员获取所有材质列表(用于审核) // @Tags Admin // @Produce json // @Param page query int false "页码" default(1) // @Param page_size query int false "每页数量" default(20) // @Success 200 {object} model.Response // @Security BearerAuth // @Router /admin/textures [get] func (h *AdminHandler) GetTextureList(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) if page < 1 { page = 1 } if pageSize < 1 || pageSize > 100 { pageSize = 20 } var textures []model.Texture var total int64 db := h.container.DB db.Model(&model.Texture{}).Count(&total) db.Preload("Uploader").Offset((page - 1) * pageSize).Limit(pageSize).Order("id DESC").Find(&textures) // 构建响应 textureList := make([]gin.H, len(textures)) for i, t := range textures { uploaderName := "" if t.Uploader != nil { uploaderName = t.Uploader.Username } textureList[i] = gin.H{ "id": t.ID, "name": t.Name, "type": t.Type, "hash": t.Hash, "uploader_id": t.UploaderID, "uploader_name": uploaderName, "is_public": t.IsPublic, "download_count": t.DownloadCount, "created_at": t.CreatedAt, } } c.JSON(http.StatusOK, model.NewSuccessResponse(gin.H{ "textures": textureList, "total": total, "page": page, "page_size": pageSize, })) }