package handlers import ( "errors" "net/http" "strconv" "strings" "photography-backend/internal/model/entity" "photography-backend/internal/model/dto" "photography-backend/internal/service" "github.com/gin-gonic/gin" "go.uber.org/zap" ) type PhotoHandler struct { photoService *service.PhotoService logger *zap.Logger } func NewPhotoHandler(photoService *service.PhotoService, logger *zap.Logger) *PhotoHandler { return &PhotoHandler{ photoService: photoService, logger: logger, } } // GetPhotos 获取照片列表 // @Summary 获取照片列表 // @Description 获取照片列表,支持分页、搜索、过滤 // @Tags photos // @Accept json // @Produce json // @Param page query int false "页码" // @Param limit query int false "每页数量" // @Param search query string false "搜索关键词" // @Param status query string false "状态筛选" // @Param category_id query int false "分类ID" // @Param tags query string false "标签列表(逗号分隔)" // @Param start_date query string false "开始日期" // @Param end_date query string false "结束日期" // @Param sort_by query string false "排序字段" // @Param sort_order query string false "排序方向" // @Success 200 {object} service.PhotoListResponse // @Failure 400 {object} response.Error // @Failure 500 {object} response.Error // @Router /photos [get] func (h *PhotoHandler) GetPhotos(c *gin.Context) { var params service.PhotoListParams // 解析查询参数 if err := c.ShouldBindQuery(¶ms); err != nil { h.logger.Error("Failed to bind query params", zap.Error(err)) c.JSON(http.StatusBadRequest, response.Error{ Error: "Invalid query parameters", Message: err.Error(), }) return } // 解析标签参数 if tagsStr := c.Query("tags"); tagsStr != "" { params.Tags = strings.Split(tagsStr, ",") } // 调用服务层 result, err := h.photoService.GetPhotos(c.Request.Context(), params) if err != nil { h.logger.Error("Failed to get photos", zap.Error(err)) c.JSON(http.StatusInternalServerError, response.Error{ Error: "Failed to get photos", Message: err.Error(), }) return } c.JSON(http.StatusOK, result) } // GetPhoto 获取照片详情 // @Summary 获取照片详情 // @Description 根据ID获取照片详情 // @Tags photos // @Accept json // @Produce json // @Param id path int true "照片ID" // @Success 200 {object} models.Photo // @Failure 400 {object} response.Error // @Failure 404 {object} response.Error // @Failure 500 {object} response.Error // @Router /photos/{id} [get] func (h *PhotoHandler) GetPhoto(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, response.Error{ Error: "Invalid photo ID", Message: "Photo ID must be a valid number", }) return } photo, err := h.photoService.GetPhotoByID(c.Request.Context(), uint(id)) if err != nil { if err.Error() == "photo not found" { c.JSON(http.StatusNotFound, response.Error{ Error: "Photo not found", Message: "The requested photo does not exist", }) return } h.logger.Error("Failed to get photo", zap.Error(err)) c.JSON(http.StatusInternalServerError, response.Error{ Error: "Failed to get photo", Message: err.Error(), }) return } c.JSON(http.StatusOK, photo) } // CreatePhoto 创建照片 // @Summary 创建照片 // @Description 创建新的照片记录 // @Tags photos // @Accept json // @Produce json // @Param photo body models.CreatePhotoRequest true "照片信息" // @Success 201 {object} models.Photo // @Failure 400 {object} response.Error // @Failure 500 {object} response.Error // @Router /photos [post] func (h *PhotoHandler) CreatePhoto(c *gin.Context) { var req models.CreatePhotoRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Failed to bind JSON", zap.Error(err)) c.JSON(http.StatusBadRequest, response.Error{ Error: "Invalid request body", Message: err.Error(), }) return } // 验证请求数据 if err := h.validateCreatePhotoRequest(&req); err != nil { c.JSON(http.StatusBadRequest, response.Error{ Error: "Invalid request data", Message: err.Error(), }) return } photo, err := h.photoService.CreatePhoto(c.Request.Context(), &req) if err != nil { h.logger.Error("Failed to create photo", zap.Error(err)) c.JSON(http.StatusInternalServerError, response.Error{ Error: "Failed to create photo", Message: err.Error(), }) return } c.JSON(http.StatusCreated, photo) } // UpdatePhoto 更新照片 // @Summary 更新照片 // @Description 更新照片信息 // @Tags photos // @Accept json // @Produce json // @Param id path int true "照片ID" // @Param photo body models.UpdatePhotoRequest true "照片信息" // @Success 200 {object} models.Photo // @Failure 400 {object} response.Error // @Failure 404 {object} response.Error // @Failure 500 {object} response.Error // @Router /photos/{id} [put] func (h *PhotoHandler) UpdatePhoto(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, response.Error{ Error: "Invalid photo ID", Message: "Photo ID must be a valid number", }) return } var req models.UpdatePhotoRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Failed to bind JSON", zap.Error(err)) c.JSON(http.StatusBadRequest, response.Error{ Error: "Invalid request body", Message: err.Error(), }) return } photo, err := h.photoService.UpdatePhoto(c.Request.Context(), uint(id), &req) if err != nil { if err.Error() == "photo not found" { c.JSON(http.StatusNotFound, response.Error{ Error: "Photo not found", Message: "The requested photo does not exist", }) return } h.logger.Error("Failed to update photo", zap.Error(err)) c.JSON(http.StatusInternalServerError, response.Error{ Error: "Failed to update photo", Message: err.Error(), }) return } c.JSON(http.StatusOK, photo) } // DeletePhoto 删除照片 // @Summary 删除照片 // @Description 删除照片 // @Tags photos // @Accept json // @Produce json // @Param id path int true "照片ID" // @Success 204 "No Content" // @Failure 400 {object} response.Error // @Failure 404 {object} response.Error // @Failure 500 {object} response.Error // @Router /photos/{id} [delete] func (h *PhotoHandler) DeletePhoto(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, response.Error{ Error: "Invalid photo ID", Message: "Photo ID must be a valid number", }) return } err = h.photoService.DeletePhoto(c.Request.Context(), uint(id)) if err != nil { if err.Error() == "photo not found" { c.JSON(http.StatusNotFound, response.Error{ Error: "Photo not found", Message: "The requested photo does not exist", }) return } h.logger.Error("Failed to delete photo", zap.Error(err)) c.JSON(http.StatusInternalServerError, response.Error{ Error: "Failed to delete photo", Message: err.Error(), }) return } c.Status(http.StatusNoContent) } // UploadPhoto 上传照片 // @Summary 上传照片 // @Description 上传照片文件并创建记录 // @Tags photos // @Accept multipart/form-data // @Produce json // @Param file formData file true "照片文件" // @Param title formData string false "标题" // @Param description formData string false "描述" // @Param status formData string false "状态" // @Param category_ids formData string false "分类ID列表(逗号分隔)" // @Param tag_ids formData string false "标签ID列表(逗号分隔)" // @Success 201 {object} models.Photo // @Failure 400 {object} response.Error // @Failure 500 {object} response.Error // @Router /photos/upload [post] func (h *PhotoHandler) UploadPhoto(c *gin.Context) { // 获取上传的文件 file, header, err := c.Request.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, response.Error{ Error: "No file uploaded", Message: "Please select a file to upload", }) return } defer file.Close() // 构建创建请求 req := &models.CreatePhotoRequest{ Title: c.PostForm("title"), Description: c.PostForm("description"), Status: c.PostForm("status"), } // 如果未指定状态,默认为草稿 if req.Status == "" { req.Status = "draft" } // 解析分类ID if categoryIDsStr := c.PostForm("category_ids"); categoryIDsStr != "" { categoryIDStrs := strings.Split(categoryIDsStr, ",") for _, idStr := range categoryIDStrs { if id, err := strconv.ParseUint(strings.TrimSpace(idStr), 10, 32); err == nil { req.CategoryIDs = append(req.CategoryIDs, uint(id)) } } } // 解析标签ID if tagIDsStr := c.PostForm("tag_ids"); tagIDsStr != "" { tagIDStrs := strings.Split(tagIDsStr, ",") for _, idStr := range tagIDStrs { if id, err := strconv.ParseUint(strings.TrimSpace(idStr), 10, 32); err == nil { req.TagIDs = append(req.TagIDs, uint(id)) } } } // 上传照片 photo, err := h.photoService.UploadPhoto(c.Request.Context(), file, header, req) if err != nil { h.logger.Error("Failed to upload photo", zap.Error(err)) c.JSON(http.StatusInternalServerError, response.Error{ Error: "Failed to upload photo", Message: err.Error(), }) return } c.JSON(http.StatusCreated, photo) } // BatchUpdatePhotos 批量更新照片 // @Summary 批量更新照片 // @Description 批量更新照片信息 // @Tags photos // @Accept json // @Produce json // @Param request body models.BatchUpdatePhotosRequest true "批量更新请求" // @Success 200 {object} models.SuccessResponse // @Failure 400 {object} response.Error // @Failure 500 {object} response.Error // @Router /photos/batch/update [post] func (h *PhotoHandler) BatchUpdatePhotos(c *gin.Context) { var req models.BatchUpdatePhotosRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Failed to bind JSON", zap.Error(err)) c.JSON(http.StatusBadRequest, response.Error{ Error: "Invalid request body", Message: err.Error(), }) return } if len(req.IDs) == 0 { c.JSON(http.StatusBadRequest, response.Error{ Error: "Invalid request", Message: "No photo IDs provided", }) return } err := h.photoService.BatchUpdatePhotos(c.Request.Context(), req.IDs, &req) if err != nil { h.logger.Error("Failed to batch update photos", zap.Error(err)) c.JSON(http.StatusInternalServerError, response.Error{ Error: "Failed to batch update photos", Message: err.Error(), }) return } c.JSON(http.StatusOK, models.SuccessResponse{ Message: "Photos updated successfully", }) } // BatchDeletePhotos 批量删除照片 // @Summary 批量删除照片 // @Description 批量删除照片 // @Tags photos // @Accept json // @Produce json // @Param request body models.BatchDeleteRequest true "批量删除请求" // @Success 200 {object} models.SuccessResponse // @Failure 400 {object} response.Error // @Failure 500 {object} response.Error // @Router /photos/batch/delete [post] func (h *PhotoHandler) BatchDeletePhotos(c *gin.Context) { var req models.BatchDeleteRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Failed to bind JSON", zap.Error(err)) c.JSON(http.StatusBadRequest, response.Error{ Error: "Invalid request body", Message: err.Error(), }) return } if len(req.IDs) == 0 { c.JSON(http.StatusBadRequest, response.Error{ Error: "Invalid request", Message: "No photo IDs provided", }) return } err := h.photoService.BatchDeletePhotos(c.Request.Context(), req.IDs) if err != nil { h.logger.Error("Failed to batch delete photos", zap.Error(err)) c.JSON(http.StatusInternalServerError, response.Error{ Error: "Failed to batch delete photos", Message: err.Error(), }) return } c.JSON(http.StatusOK, models.SuccessResponse{ Message: "Photos deleted successfully", }) } // GetPhotoStats 获取照片统计信息 // @Summary 获取照片统计信息 // @Description 获取照片统计信息 // @Tags photos // @Accept json // @Produce json // @Success 200 {object} models.PhotoStats // @Failure 500 {object} response.Error // @Router /photos/stats [get] func (h *PhotoHandler) GetPhotoStats(c *gin.Context) { stats, err := h.photoService.GetPhotoStats(c.Request.Context()) if err != nil { h.logger.Error("Failed to get photo stats", zap.Error(err)) c.JSON(http.StatusInternalServerError, response.Error{ Error: "Failed to get photo stats", Message: err.Error(), }) return } c.JSON(http.StatusOK, stats) } // validateCreatePhotoRequest 验证创建照片请求 func (h *PhotoHandler) validateCreatePhotoRequest(req *models.CreatePhotoRequest) error { if req.Title == "" { return errors.New("title is required") } if req.Status == "" { req.Status = "draft" } // 验证状态值 validStatuses := []string{"draft", "published", "archived", "processing"} isValidStatus := false for _, status := range validStatuses { if req.Status == status { isValidStatus = true break } } if !isValidStatus { return errors.New("invalid status value") } return nil }