package handler import ( "carrotskin/internal/container" "carrotskin/internal/model" "carrotskin/internal/types" "strconv" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // TextureHandler 材质处理器(依赖注入版本) type TextureHandler struct { container *container.Container logger *zap.Logger } // NewTextureHandler 创建TextureHandler实例 func NewTextureHandler(c *container.Container) *TextureHandler { return &TextureHandler{ container: c, logger: c.Logger, } } // Get 获取材质详情 // @Summary 获取材质详情 // @Description 获取指定ID的材质详细信息 // @Tags texture // @Accept json // @Produce json // @Param id path int true "材质ID" // @Success 200 {object} model.Response{data=types.TextureInfo} "获取成功" // @Failure 400 {object} model.ErrorResponse "参数错误" // @Failure 404 {object} model.ErrorResponse "材质不存在" // @Router /api/v1/texture/{id} [get] func (h *TextureHandler) Get(c *gin.Context) { id, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { RespondBadRequest(c, "无效的材质ID", err) return } texture, err := h.container.TextureService.GetByID(c.Request.Context(), id) if err != nil { RespondNotFound(c, err.Error()) return } RespondSuccess(c, TextureToTextureInfo(texture)) } // Search 搜索材质 // @Summary 搜索材质 // @Description 搜索材质列表,支持关键词、类型、公开性筛选和分页 // @Tags texture // @Accept json // @Produce json // @Param keyword query string false "关键词" // @Param type query string false "材质类型 (SKIN/CAPE)" // @Param public_only query boolean false "仅显示公开材质" // @Param page query int false "页码" default(1) // @Param page_size query int false "每页数量" default(20) // @Success 200 {object} model.Response{data=map[string]interface{}} "获取成功" // @Failure 500 {object} model.ErrorResponse "服务器错误" // @Router /api/v1/texture [get] func (h *TextureHandler) Search(c *gin.Context) { keyword := c.Query("keyword") textureTypeStr := c.Query("type") publicOnly := c.Query("public_only") == "true" page := parseIntWithDefault(c.DefaultQuery("page", "1"), 1) pageSize := parseIntWithDefault(c.DefaultQuery("page_size", "20"), 20) var textureType model.TextureType switch textureTypeStr { case "SKIN": textureType = model.TextureTypeSkin case "CAPE": textureType = model.TextureTypeCape } textures, total, err := h.container.TextureService.Search(c.Request.Context(), keyword, textureType, publicOnly, page, pageSize) if err != nil { h.logger.Error("搜索材质失败", zap.String("keyword", keyword), zap.Error(err)) RespondServerError(c, "搜索材质失败", err) return } // 返回格式: // { // "code": 200, // "message": "操作成功", // "data": { // "list": [...], // "total": 1, // "page": 1, // "per_page": 5 // } // } RespondSuccess(c, gin.H{ "list": TexturesToTextureInfos(textures), "total": total, "page": page, "per_page": pageSize, }) } // Update 更新材质 // @Summary 更新材质 // @Description 更新材质信息(名称、描述、公开性) // @Tags texture // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "材质ID" // @Param request body types.UpdateTextureRequest true "更新信息" // @Success 200 {object} model.Response{data=types.TextureInfo} "更新成功" // @Failure 400 {object} model.ErrorResponse "参数错误" // @Failure 403 {object} model.ErrorResponse "无权操作" // @Router /api/v1/texture/{id} [put] func (h *TextureHandler) Update(c *gin.Context) { userID, ok := GetUserIDFromContext(c) if !ok { return } textureID, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { RespondBadRequest(c, "无效的材质ID", err) return } var req types.UpdateTextureRequest if err := c.ShouldBindJSON(&req); err != nil { RespondBadRequest(c, "请求参数错误", err) return } texture, err := h.container.TextureService.Update(c.Request.Context(), textureID, userID, req.Name, req.Description, req.IsPublic) if err != nil { h.logger.Error("更新材质失败", zap.Int64("user_id", userID), zap.Int64("texture_id", textureID), zap.Error(err), ) RespondForbidden(c, err.Error()) return } RespondSuccess(c, TextureToTextureInfo(texture)) } // Delete 删除材质 // @Summary 删除材质 // @Description 删除指定ID的材质 // @Tags texture // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "材质ID" // @Success 200 {object} model.Response "删除成功" // @Failure 400 {object} model.ErrorResponse "参数错误" // @Failure 403 {object} model.ErrorResponse "无权操作" // @Router /api/v1/texture/{id} [delete] func (h *TextureHandler) Delete(c *gin.Context) { userID, ok := GetUserIDFromContext(c) if !ok { return } textureID, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { RespondBadRequest(c, "无效的材质ID", err) return } if err := h.container.TextureService.Delete(c.Request.Context(), textureID, userID); err != nil { h.logger.Error("删除材质失败", zap.Int64("user_id", userID), zap.Int64("texture_id", textureID), zap.Error(err), ) RespondForbidden(c, err.Error()) return } RespondSuccess(c, nil) } // ToggleFavorite 切换收藏状态 // @Summary 切换收藏状态 // @Description 收藏或取消收藏指定材质 // @Tags texture // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "材质ID" // @Success 200 {object} model.Response{data=map[string]bool} "操作成功" // @Failure 400 {object} model.ErrorResponse "参数错误" // @Router /api/v1/texture/{id} [post] func (h *TextureHandler) ToggleFavorite(c *gin.Context) { userID, ok := GetUserIDFromContext(c) if !ok { return } textureID, err := strconv.ParseInt(c.Param("id"), 10, 64) if err != nil { RespondBadRequest(c, "无效的材质ID", err) return } isFavorited, err := h.container.TextureService.ToggleFavorite(c.Request.Context(), userID, textureID) if err != nil { h.logger.Error("切换收藏状态失败", zap.Int64("user_id", userID), zap.Int64("texture_id", textureID), zap.Error(err), ) RespondBadRequest(c, err.Error(), nil) return } RespondSuccess(c, map[string]bool{"is_favorited": isFavorited}) } // GetUserTextures 获取用户上传的材质列表 // @Summary 获取我的材质 // @Description 获取当前登录用户上传的材质列表 // @Tags texture // @Accept json // @Produce json // @Security BearerAuth // @Param page query int false "页码" default(1) // @Param page_size query int false "每页数量" default(20) // @Success 200 {object} model.Response{data=map[string]interface{}} "获取成功" // @Failure 500 {object} model.ErrorResponse "服务器错误" // @Router /api/v1/texture/my [get] func (h *TextureHandler) GetUserTextures(c *gin.Context) { userID, ok := GetUserIDFromContext(c) if !ok { return } page := parseIntWithDefault(c.DefaultQuery("page", "1"), 1) pageSize := parseIntWithDefault(c.DefaultQuery("page_size", "20"), 20) textures, total, err := h.container.TextureService.GetByUserID(c.Request.Context(), userID, page, pageSize) if err != nil { h.logger.Error("获取用户材质列表失败", zap.Int64("user_id", userID), zap.Error(err)) RespondServerError(c, "获取材质列表失败", err) return } RespondSuccess(c, gin.H{ "list": TexturesToTextureInfos(textures), "total": total, "page": page, "per_page": pageSize, }) } // GetUserFavorites 获取用户收藏的材质列表 // @Summary 获取我的收藏 // @Description 获取当前登录用户收藏的材质列表 // @Tags texture // @Accept json // @Produce json // @Security BearerAuth // @Param page query int false "页码" default(1) // @Param page_size query int false "每页数量" default(20) // @Success 200 {object} model.Response{data=map[string]interface{}} "获取成功" // @Failure 500 {object} model.ErrorResponse "服务器错误" // @Router /api/v1/texture/favorites [get] func (h *TextureHandler) GetUserFavorites(c *gin.Context) { userID, ok := GetUserIDFromContext(c) if !ok { return } page := parseIntWithDefault(c.DefaultQuery("page", "1"), 1) pageSize := parseIntWithDefault(c.DefaultQuery("page_size", "20"), 20) textures, total, err := h.container.TextureService.GetUserFavorites(c.Request.Context(), userID, page, pageSize) if err != nil { h.logger.Error("获取用户收藏列表失败", zap.Int64("user_id", userID), zap.Error(err)) RespondServerError(c, "获取收藏列表失败", err) return } RespondSuccess(c, gin.H{ "list": TexturesToTextureInfos(textures), "total": total, "page": page, "per_page": pageSize, }) } // Upload 直接上传材质文件 // @Summary 上传材质 // @Description 上传图片文件创建新材质 // @Tags texture // @Accept multipart/form-data // @Produce json // @Security BearerAuth // @Param file formData file true "材质文件 (PNG)" // @Param name formData string true "材质名称" // @Param description formData string false "材质描述" // @Param type formData string false "材质类型 (SKIN/CAPE)" default(SKIN) // @Param is_public formData boolean false "是否公开" default(false) // @Param is_slim formData boolean false "是否为纤细模型 (仅SKIN有效)" default(false) // @Success 200 {object} model.Response{data=types.TextureInfo} "上传成功" // @Failure 400 {object} model.ErrorResponse "参数错误" // @Router /api/v1/texture/upload [post] func (h *TextureHandler) Upload(c *gin.Context) { userID, ok := GetUserIDFromContext(c) if !ok { return } // 解析multipart表单 if err := c.Request.ParseMultipartForm(32 << 20); err != nil { // 32MB RespondBadRequest(c, "解析表单失败", err) return } // 获取文件 file, err := c.FormFile("file") if err != nil { RespondBadRequest(c, "获取文件失败", err) return } // 读取文件内容 src, err := file.Open() if err != nil { RespondBadRequest(c, "打开文件失败", err) return } defer src.Close() fileData := make([]byte, file.Size) if _, err := src.Read(fileData); err != nil { RespondBadRequest(c, "读取文件失败", err) return } // 获取表单字段 name := c.PostForm("name") if name == "" { RespondBadRequest(c, "名称不能为空", nil) return } description := c.PostForm("description") textureType := c.PostForm("type") if textureType == "" { textureType = "SKIN" // 默认值 } isPublic := c.PostForm("is_public") == "true" isSlim := c.PostForm("is_slim") == "true" // 检查上传限制 maxTextures := h.container.UserService.GetMaxTexturesPerUser() if err := h.container.TextureService.CheckUploadLimit(c.Request.Context(), userID, maxTextures); err != nil { RespondBadRequest(c, err.Error(), nil) return } // 调用服务上传 texture, err := h.container.TextureService.UploadTexture( c.Request.Context(), userID, name, description, textureType, fileData, file.Filename, isPublic, isSlim, ) if err != nil { h.logger.Error("上传材质失败", zap.Int64("user_id", userID), zap.String("file_name", file.Filename), zap.Error(err), ) RespondBadRequest(c, err.Error(), nil) return } RespondSuccess(c, TextureToTextureInfo(texture)) }