diff --git a/server/api/resource.go b/server/api/resource.go index def7229..94d8ee9 100644 --- a/server/api/resource.go +++ b/server/api/resource.go @@ -400,6 +400,108 @@ func handleGetLowResolutionCharacters(c fiber.Ctx) error { }) } +func handleGetLowResolutionResourceImages(c fiber.Ctx) error { + pageStr := c.Query("page") + if pageStr == "" { + pageStr = "1" + } + page, err := strconv.Atoi(pageStr) + if err != nil { + return model.NewRequestError("Invalid page number") + } + + // 支持自定义页面大小,默认50,最大1000 + pageSizeStr := c.Query("page_size") + if pageSizeStr == "" { + pageSizeStr = "50" + } + pageSize, err := strconv.Atoi(pageSizeStr) + if err != nil { + return model.NewRequestError("Invalid page_size parameter") + } + if pageSize > 1000 { + pageSize = 1000 // 限制最大页面大小 + } + if pageSize < 1 { + pageSize = 1 + } + + maxWidthStr := c.Query("max_width") + if maxWidthStr == "" { + maxWidthStr = "800" // 默认最大宽度800px + } + maxWidth, err := strconv.Atoi(maxWidthStr) + if err != nil { + return model.NewRequestError("Invalid max_width parameter") + } + + maxHeightStr := c.Query("max_height") + if maxHeightStr == "" { + maxHeightStr = "800" // 默认最大高度800px + } + maxHeight, err := strconv.Atoi(maxHeightStr) + if err != nil { + return model.NewRequestError("Invalid max_height parameter") + } + + images, totalPages, err := service.GetLowResolutionResourceImages(page, pageSize, maxWidth, maxHeight) + if err != nil { + return err + } + + if images == nil { + images = []model.LowResResourceImageView{} + } + + return c.Status(fiber.StatusOK).JSON(model.PageResponse[model.LowResResourceImageView]{ + Success: true, + Data: images, + TotalPages: totalPages, + Message: "Low resolution resource images retrieved successfully", + }) +} + +func handleUpdateResourceImage(c fiber.Ctx) error { + resourceIdStr := c.Params("resourceId") + oldImageIdStr := c.Params("oldImageId") + if resourceIdStr == "" || oldImageIdStr == "" { + return model.NewRequestError("Resource ID and Old Image ID are required") + } + resourceId, err := strconv.Atoi(resourceIdStr) + if err != nil { + return model.NewRequestError("Invalid resource ID") + } + oldImageId, err := strconv.Atoi(oldImageIdStr) + if err != nil { + return model.NewRequestError("Invalid old image ID") + } + + var params struct { + NewImageID uint `json:"new_image_id"` + } + body := c.Body() + err = json.Unmarshal(body, ¶ms) + if err != nil { + return model.NewRequestError("Invalid request body") + } + + uid, ok := c.Locals("uid").(uint) + if !ok { + return model.NewUnAuthorizedError("You must be logged in to update a resource image") + } + + err = service.UpdateResourceImage(uid, uint(resourceId), uint(oldImageId), params.NewImageID) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK).JSON(model.Response[any]{ + Success: true, + Data: nil, + Message: "Resource image updated successfully", + }) +} + func AddResourceRoutes(api fiber.Router) { resource := api.Group("/resource") { @@ -410,11 +512,13 @@ func AddResourceRoutes(api fiber.Router) { resource.Get("/pinned", handleGetPinnedResources) resource.Get("/vndb/characters", handleGetCharactersFromVndb) resource.Get("/characters/low-resolution", handleGetLowResolutionCharacters) + resource.Get("/images/low-resolution", handleGetLowResolutionResourceImages) resource.Get("/:id", handleGetResource) resource.Delete("/:id", handleDeleteResource) resource.Get("/tag/:tag", handleListResourcesWithTag) resource.Get("/user/:username", handleGetResourcesWithUser) resource.Post("/:id", handleUpdateResource) resource.Put("/:resourceId/character/:characterId/image", handleUpdateCharacterImage) + resource.Put("/:resourceId/image/:oldImageId", handleUpdateResourceImage) } } diff --git a/server/dao/resource.go b/server/dao/resource.go index 01e8059..100f758 100644 --- a/server/dao/resource.go +++ b/server/dao/resource.go @@ -2,6 +2,7 @@ package dao import ( "errors" + "fmt" "math/rand" "nysoure/server/model" "sync" @@ -593,3 +594,81 @@ func GetLowResolutionCharactersCount(maxWidth, maxHeight int) (int64, error) { return count, nil } + +// GetLowResolutionResourceImages 获取低清晰度的资源图片 +// maxWidth和maxHeight定义了低清晰度的阈值 +func GetLowResolutionResourceImages(maxWidth, maxHeight int, limit int, offset int) ([]model.LowResResourceImageView, error) { + var results []model.LowResResourceImageView + + query := ` + SELECT DISTINCT + r.id as resource_id, + r.title as title, + i.id as image_id, + i.width as image_width, + i.height as image_height + FROM resources r + INNER JOIN resource_images ri ON r.id = ri.resource_id + INNER JOIN images i ON ri.image_id = i.id + WHERE (i.width <= ? OR i.height <= ?) + ORDER BY r.id, i.id + LIMIT ? OFFSET ? + ` + + err := db.Raw(query, maxWidth, maxHeight, limit, offset).Scan(&results).Error + if err != nil { + return nil, err + } + + return results, nil +} + +// GetLowResolutionResourceImagesCount 获取低清晰度资源图片的总数 +func GetLowResolutionResourceImagesCount(maxWidth, maxHeight int) (int64, error) { + var count int64 + + query := ` + SELECT COUNT(DISTINCT ri.resource_id, ri.image_id) + FROM resources r + INNER JOIN resource_images ri ON r.id = ri.resource_id + INNER JOIN images i ON ri.image_id = i.id + WHERE (i.width <= ? OR i.height <= ?) + ` + + err := db.Raw(query, maxWidth, maxHeight).Scan(&count).Error + if err != nil { + return 0, err + } + + return count, nil +} + +// UpdateResourceImage 更新资源中特定的图片ID +func UpdateResourceImage(resourceID, oldImageID, newImageID uint) error { + return db.Transaction(func(tx *gorm.DB) error { + // 首先检查关联是否存在 + var exists bool + err := tx.Raw("SELECT EXISTS(SELECT 1 FROM resource_images WHERE resource_id = ? AND image_id = ?)", + resourceID, oldImageID).Scan(&exists).Error + if err != nil { + return err + } + + if !exists { + return fmt.Errorf("resource %d does not have image %d", resourceID, oldImageID) + } + + // 更新resource_images表中的image_id + result := tx.Exec("UPDATE resource_images SET image_id = ? WHERE resource_id = ? AND image_id = ?", + newImageID, resourceID, oldImageID) + if result.Error != nil { + return result.Error + } + + if result.RowsAffected == 0 { + return fmt.Errorf("no resource image association updated") + } + + return nil + }) +} diff --git a/server/model/resource.go b/server/model/resource.go index 88bed9e..18eee27 100644 --- a/server/model/resource.go +++ b/server/model/resource.go @@ -60,6 +60,14 @@ type ResourceDetailView struct { Characters []CharacterView `json:"characters"` } +type LowResResourceImageView struct { + ResourceID uint `json:"resource_id"` + Title string `json:"title"` + ImageID uint `json:"image_id"` + ImageWidth int `json:"image_width"` + ImageHeight int `json:"image_height"` +} + func (r *Resource) ToView() ResourceView { tags := make([]TagView, len(r.Tags)) for i, tag := range r.Tags { diff --git a/server/service/resource.go b/server/service/resource.go index fe5398a..e14bb95 100644 --- a/server/service/resource.go +++ b/server/service/resource.go @@ -832,3 +832,51 @@ func GetLowResolutionCharacters(page int, pageSize int, maxWidth, maxHeight int) return characters, totalPages, nil } + +// GetLowResolutionResourceImages 获取低清晰度的资源图片 +func GetLowResolutionResourceImages(page int, pageSize int, maxWidth, maxHeight int) ([]model.LowResResourceImageView, int, error) { + if page <= 0 { + page = 1 + } + if pageSize <= 0 { + pageSize = 50 // 默认每页50个图片 + } + if pageSize > 1000 { + pageSize = 1000 // 限制最大页面大小 + } + + offset := (page - 1) * pageSize + + // 获取资源图片列表 + images, err := dao.GetLowResolutionResourceImages(maxWidth, maxHeight, pageSize, offset) + if err != nil { + return nil, 0, err + } + + // 获取总数 + totalCount, err := dao.GetLowResolutionResourceImagesCount(maxWidth, maxHeight) + if err != nil { + return nil, 0, err + } + + totalPages := int((totalCount + int64(pageSize) - 1) / int64(pageSize)) + + return images, totalPages, nil +} + +// UpdateResourceImage 更新资源图片 +func UpdateResourceImage(uid, resourceID, oldImageID, newImageID uint) error { + // 首先检查用户权限 - 确保用户是资源的所有者或管理员 + resource, err := dao.GetResourceByID(resourceID) + if err != nil { + return err + } + + if resource.UserID != uid { + // 可以在这里添加管理员权限检查 + return model.NewUnAuthorizedError("You don't have permission to update this resource") + } + + // 更新资源图片 + return dao.UpdateResourceImage(resourceID, oldImageID, newImageID) +}