From 78b147da47470d958d3ca9f0d485c8f9367e8a43 Mon Sep 17 00:00:00 2001 From: ctwj <908504609@qq.com> Date: Tue, 29 Jul 2025 01:29:53 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=83=A8=E5=88=86?= =?UTF-8?q?=E9=93=BE=E6=8E=A5=E6=A3=80=E6=B5=8B=E5=A4=B1=E8=B4=A5=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/pan_factory.go | 4 + db/converter/converter.go | 24 ++-- db/dto/ready_resource.go | 1 + db/dto/response.go | 1 + db/entity/ready_resource.go | 1 + db/repo/ready_resource_repository.go | 22 ++++ handlers/ready_resource_handler.go | 179 +++++++++++++++++++++++++++ main.go | 3 + utils/errors.go | 153 +++++++++++++++++++++++ utils/scheduler.go | 65 ++++++---- 10 files changed, 420 insertions(+), 33 deletions(-) create mode 100644 utils/errors.go diff --git a/common/pan_factory.go b/common/pan_factory.go index 4ac4c23..cb84a53 100644 --- a/common/pan_factory.go +++ b/common/pan_factory.go @@ -16,6 +16,7 @@ const ( BaiduPan UC NotFound + Xunlei ) // String 返回服务类型的字符串表示 @@ -29,6 +30,8 @@ func (s ServiceType) String() string { return "baidu" case UC: return "uc" + case Xunlei: + return "xunlei" default: return "unknown" } @@ -173,6 +176,7 @@ func ExtractServiceType(url string) ServiceType { "pan.baidu.com": BaiduPan, "drive.uc.cn": UC, "fast.uc.cn": UC, + "pan.xunlei.com": Xunlei, } for pattern, serviceType := range patterns { diff --git a/db/converter/converter.go b/db/converter/converter.go index 01cef39..b800403 100644 --- a/db/converter/converter.go +++ b/db/converter/converter.go @@ -171,17 +171,19 @@ func ToCksResponseList(cksList []entity.Cks) []dto.CksResponse { // ToReadyResourceResponse 将ReadyResource实体转换为ReadyResourceResponse func ToReadyResourceResponse(resource *entity.ReadyResource) dto.ReadyResourceResponse { return dto.ReadyResourceResponse{ - ID: resource.ID, - Title: resource.Title, - URL: resource.URL, - Category: resource.Category, - Tags: resource.Tags, - Img: resource.Img, - Source: resource.Source, - Extra: resource.Extra, - Key: resource.Key, - CreateTime: resource.CreateTime, - IP: resource.IP, + ID: resource.ID, + Title: resource.Title, + Description: resource.Description, + URL: resource.URL, + Category: resource.Category, + Tags: resource.Tags, + Img: resource.Img, + Source: resource.Source, + Extra: resource.Extra, + Key: resource.Key, + ErrorMsg: resource.ErrorMsg, + CreateTime: resource.CreateTime, + IP: resource.IP, } } diff --git a/db/dto/ready_resource.go b/db/dto/ready_resource.go index eaf4db4..0993ff7 100644 --- a/db/dto/ready_resource.go +++ b/db/dto/ready_resource.go @@ -10,6 +10,7 @@ type ReadyResourceRequest struct { Img string `json:"img" example:"https://example.com/image.jpg"` Source string `json:"source" example:"数据来源"` Extra string `json:"extra" example:"额外信息"` + ErrorMsg string `json:"error_msg" example:"错误信息"` } // BatchReadyResourceRequest 批量待处理资源请求 diff --git a/db/dto/response.go b/db/dto/response.go index 39913b3..8669d96 100644 --- a/db/dto/response.go +++ b/db/dto/response.go @@ -89,6 +89,7 @@ type ReadyResourceResponse struct { Source string `json:"source"` Extra string `json:"extra"` Key string `json:"key"` + ErrorMsg string `json:"error_msg"` CreateTime time.Time `json:"create_time"` IP *string `json:"ip"` } diff --git a/db/entity/ready_resource.go b/db/entity/ready_resource.go index f3c5aab..aa3a9c6 100644 --- a/db/entity/ready_resource.go +++ b/db/entity/ready_resource.go @@ -18,6 +18,7 @@ type ReadyResource struct { Source string `json:"source" gorm:"size:100;comment:数据来源"` Extra string `json:"extra" gorm:"type:text;comment:额外附加数据"` Key string `json:"key" gorm:"size:64;index;comment:资源组标识,相同key表示同一组资源"` + ErrorMsg string `json:"error_msg" gorm:"type:text;comment:处理失败时的错误信息"` CreateTime time.Time `json:"create_time" gorm:"default:CURRENT_TIMESTAMP"` IP *string `json:"ip" gorm:"size:45;comment:IP地址"` CreatedAt time.Time `json:"created_at"` diff --git a/db/repo/ready_resource_repository.go b/db/repo/ready_resource_repository.go index 5a30611..5b10322 100644 --- a/db/repo/ready_resource_repository.go +++ b/db/repo/ready_resource_repository.go @@ -21,6 +21,9 @@ type ReadyResourceRepository interface { FindAllWithinDays(days int) ([]entity.ReadyResource, error) BatchFindByURLs(urls []string) ([]entity.ReadyResource, error) GenerateUniqueKey() (string, error) + FindWithErrors() ([]entity.ReadyResource, error) + FindWithoutErrors() ([]entity.ReadyResource, error) + ClearErrorMsg(id uint) error } // ReadyResourceRepositoryImpl ReadyResource的Repository实现 @@ -113,3 +116,22 @@ func (r *ReadyResourceRepositoryImpl) GenerateUniqueKey() (string, error) { } return "", gorm.ErrInvalidData } + +// FindWithErrors 查找有错误信息的资源 +func (r *ReadyResourceRepositoryImpl) FindWithErrors() ([]entity.ReadyResource, error) { + var resources []entity.ReadyResource + err := r.db.Where("error_msg != '' AND error_msg IS NOT NULL").Find(&resources).Error + return resources, err +} + +// FindWithoutErrors 查找没有错误信息的资源 +func (r *ReadyResourceRepositoryImpl) FindWithoutErrors() ([]entity.ReadyResource, error) { + var resources []entity.ReadyResource + err := r.db.Where("error_msg = '' OR error_msg IS NULL").Find(&resources).Error + return resources, err +} + +// ClearErrorMsg 清除指定资源的错误信息 +func (r *ReadyResourceRepositoryImpl) ClearErrorMsg(id uint) error { + return r.db.Model(&entity.ReadyResource{}).Where("id = ?", id).Update("error_msg", "").Error +} diff --git a/handlers/ready_resource_handler.go b/handlers/ready_resource_handler.go index 9541874..bdad1be 100644 --- a/handlers/ready_resource_handler.go +++ b/handlers/ready_resource_handler.go @@ -286,3 +286,182 @@ func DeleteReadyResourcesByKey(c *gin.Context) { "message": "资源组删除成功", }) } + +// getRetryableErrorCount 统计可重试的错误数量 +func getRetryableErrorCount(resources []entity.ReadyResource) int { + count := 0 + + for _, resource := range resources { + if resource.ErrorMsg != "" { + errorMsg := strings.ToUpper(resource.ErrorMsg) + // 检查错误类型标记 + if strings.Contains(resource.ErrorMsg, "[NO_ACCOUNT]") || + strings.Contains(resource.ErrorMsg, "[NO_VALID_ACCOUNT]") || + strings.Contains(resource.ErrorMsg, "[TRANSFER_FAILED]") || + strings.Contains(resource.ErrorMsg, "[LINK_CHECK_FAILED]") { + count++ + } else if strings.Contains(errorMsg, "没有可用的网盘账号") || + strings.Contains(errorMsg, "没有有效的网盘账号") || + strings.Contains(errorMsg, "网盘信息获取失败") || + strings.Contains(errorMsg, "链接检查失败") { + count++ + } + } + } + return count +} + +// GetReadyResourcesWithErrors 获取有错误信息的待处理资源 +func GetReadyResourcesWithErrors(c *gin.Context) { + // 获取分页参数 + pageStr := c.DefaultQuery("page", "1") + pageSizeStr := c.DefaultQuery("page_size", "100") + + page, err := strconv.Atoi(pageStr) + if err != nil || page < 1 { + page = 1 + } + + pageSize, err := strconv.Atoi(pageSizeStr) + if err != nil || pageSize < 1 || pageSize > 1000 { + pageSize = 100 + } + + // 获取有错误的资源 + resources, err := repoManager.ReadyResourceRepository.FindWithErrors() + if err != nil { + ErrorResponse(c, err.Error(), http.StatusInternalServerError) + return + } + + responses := converter.ToReadyResourceResponseList(resources) + + // 统计错误类型 + errorTypeStats := make(map[string]int) + for _, resource := range resources { + if resource.ErrorMsg != "" { + // 尝试从错误信息中提取错误类型 + if len(resource.ErrorMsg) > 0 && resource.ErrorMsg[0] == '[' { + endIndex := strings.Index(resource.ErrorMsg, "]") + if endIndex > 0 { + errorType := resource.ErrorMsg[1:endIndex] + errorTypeStats[errorType]++ + } else { + errorTypeStats["UNKNOWN"]++ + } + } else { + // 如果没有错误类型标记,尝试从错误信息中推断 + errorMsg := strings.ToUpper(resource.ErrorMsg) + if strings.Contains(errorMsg, "不支持的链接") { + errorTypeStats["UNSUPPORTED_LINK"]++ + } else if strings.Contains(errorMsg, "链接无效") { + errorTypeStats["INVALID_LINK"]++ + } else if strings.Contains(errorMsg, "没有可用的网盘账号") { + errorTypeStats["NO_ACCOUNT"]++ + } else if strings.Contains(errorMsg, "没有有效的网盘账号") { + errorTypeStats["NO_VALID_ACCOUNT"]++ + } else if strings.Contains(errorMsg, "网盘信息获取失败") { + errorTypeStats["TRANSFER_FAILED"]++ + } else if strings.Contains(errorMsg, "创建网盘服务失败") { + errorTypeStats["SERVICE_CREATION_FAILED"]++ + } else if strings.Contains(errorMsg, "处理标签失败") { + errorTypeStats["TAG_PROCESSING_FAILED"]++ + } else if strings.Contains(errorMsg, "处理分类失败") { + errorTypeStats["CATEGORY_PROCESSING_FAILED"]++ + } else if strings.Contains(errorMsg, "资源保存失败") { + errorTypeStats["RESOURCE_SAVE_FAILED"]++ + } else if strings.Contains(errorMsg, "未找到对应的平台ID") { + errorTypeStats["PLATFORM_NOT_FOUND"]++ + } else if strings.Contains(errorMsg, "链接检查失败") { + errorTypeStats["LINK_CHECK_FAILED"]++ + } else { + errorTypeStats["UNKNOWN"]++ + } + } + } + } + + SuccessResponse(c, gin.H{ + "data": responses, + "page": page, + "page_size": pageSize, + "total": len(resources), + "count": len(resources), + "error_stats": errorTypeStats, + "retryable_count": getRetryableErrorCount(resources), + }) +} + +// ClearErrorMsg 清除指定资源的错误信息 +func ClearErrorMsg(c *gin.Context) { + idStr := c.Param("id") + id, err := strconv.ParseUint(idStr, 10, 32) + if err != nil { + ErrorResponse(c, "无效的ID", http.StatusBadRequest) + return + } + + err = repoManager.ReadyResourceRepository.ClearErrorMsg(uint(id)) + if err != nil { + ErrorResponse(c, err.Error(), http.StatusInternalServerError) + return + } + + SuccessResponse(c, gin.H{"message": "错误信息已清除"}) +} + +// RetryFailedResources 重试失败的资源 +func RetryFailedResources(c *gin.Context) { + // 获取有错误的资源 + resources, err := repoManager.ReadyResourceRepository.FindWithErrors() + if err != nil { + ErrorResponse(c, err.Error(), http.StatusInternalServerError) + return + } + + if len(resources) == 0 { + SuccessResponse(c, gin.H{ + "message": "没有需要重试的资源", + "count": 0, + }) + return + } + + // 只重试可重试的错误 + clearedCount := 0 + skippedCount := 0 + + for _, resource := range resources { + isRetryable := false + errorMsg := strings.ToUpper(resource.ErrorMsg) + + // 检查错误类型标记 + if strings.Contains(resource.ErrorMsg, "[NO_ACCOUNT]") || + strings.Contains(resource.ErrorMsg, "[NO_VALID_ACCOUNT]") || + strings.Contains(resource.ErrorMsg, "[TRANSFER_FAILED]") || + strings.Contains(resource.ErrorMsg, "[LINK_CHECK_FAILED]") { + isRetryable = true + } else if strings.Contains(errorMsg, "没有可用的网盘账号") || + strings.Contains(errorMsg, "没有有效的网盘账号") || + strings.Contains(errorMsg, "网盘信息获取失败") || + strings.Contains(errorMsg, "链接检查失败") { + isRetryable = true + } + + if isRetryable { + if err := repoManager.ReadyResourceRepository.ClearErrorMsg(resource.ID); err == nil { + clearedCount++ + } + } else { + skippedCount++ + } + } + + SuccessResponse(c, gin.H{ + "message": "已清除可重试资源的错误信息,资源将在下次调度时重新处理", + "total_count": len(resources), + "cleared_count": clearedCount, + "skipped_count": skippedCount, + "retryable_count": getRetryableErrorCount(resources), + }) +} diff --git a/main.go b/main.go index 054558b..dcc09c0 100644 --- a/main.go +++ b/main.go @@ -201,6 +201,9 @@ func main() { api.DELETE("/ready-resources", middleware.AuthMiddleware(), middleware.AdminMiddleware(), handlers.ClearReadyResources) api.GET("/ready-resources/key/:key", middleware.AuthMiddleware(), middleware.AdminMiddleware(), handlers.GetReadyResourcesByKey) api.DELETE("/ready-resources/key/:key", middleware.AuthMiddleware(), middleware.AdminMiddleware(), handlers.DeleteReadyResourcesByKey) + api.GET("/ready-resources/errors", middleware.AuthMiddleware(), middleware.AdminMiddleware(), handlers.GetReadyResourcesWithErrors) + api.POST("/ready-resources/:id/clear-error", middleware.AuthMiddleware(), middleware.AdminMiddleware(), handlers.ClearErrorMsg) + api.POST("/ready-resources/retry-failed", middleware.AuthMiddleware(), middleware.AdminMiddleware(), handlers.RetryFailedResources) // 用户管理(仅管理员) api.GET("/users", middleware.AuthMiddleware(), middleware.AdminMiddleware(), handlers.GetUsers) diff --git a/utils/errors.go b/utils/errors.go new file mode 100644 index 0000000..6a37cd9 --- /dev/null +++ b/utils/errors.go @@ -0,0 +1,153 @@ +package utils + +import "fmt" + +// ErrorType 错误类型枚举 +type ErrorType string + +const ( + // ErrorTypeUnsupportedLink 不支持的链接 + ErrorTypeUnsupportedLink ErrorType = "UNSUPPORTED_LINK" + // ErrorTypeInvalidLink 无效链接 + ErrorTypeInvalidLink ErrorType = "INVALID_LINK" + // ErrorTypeNoAccount 没有可用账号 + ErrorTypeNoAccount ErrorType = "NO_ACCOUNT" + // ErrorTypeNoValidAccount 没有有效账号 + ErrorTypeNoValidAccount ErrorType = "NO_VALID_ACCOUNT" + // ErrorTypeServiceCreation 服务创建失败 + ErrorTypeServiceCreation ErrorType = "SERVICE_CREATION_FAILED" + // ErrorTypeTransferFailed 转存失败 + ErrorTypeTransferFailed ErrorType = "TRANSFER_FAILED" + // ErrorTypeTagProcessing 标签处理失败 + ErrorTypeTagProcessing ErrorType = "TAG_PROCESSING_FAILED" + // ErrorTypeCategoryProcessing 分类处理失败 + ErrorTypeCategoryProcessing ErrorType = "CATEGORY_PROCESSING_FAILED" + // ErrorTypeResourceSave 资源保存失败 + ErrorTypeResourceSave ErrorType = "RESOURCE_SAVE_FAILED" + // ErrorTypePlatformNotFound 平台未找到 + ErrorTypePlatformNotFound ErrorType = "PLATFORM_NOT_FOUND" + // ErrorTypeLinkCheckFailed 链接检查失败 + ErrorTypeLinkCheckFailed ErrorType = "LINK_CHECK_FAILED" +) + +// ResourceError 资源处理错误 +type ResourceError struct { + Type ErrorType `json:"type"` + Message string `json:"message"` + URL string `json:"url,omitempty"` + Details string `json:"details,omitempty"` +} + +// Error 实现error接口 +func (e *ResourceError) Error() string { + if e.Details != "" { + return fmt.Sprintf("[%s] %s: %s", e.Type, e.Message, e.Details) + } + return fmt.Sprintf("[%s] %s", e.Type, e.Message) +} + +// NewResourceError 创建新的资源错误 +func NewResourceError(errorType ErrorType, message string, url string, details string) *ResourceError { + return &ResourceError{ + Type: errorType, + Message: message, + URL: url, + Details: details, + } +} + +// NewUnsupportedLinkError 创建不支持的链接错误 +func NewUnsupportedLinkError(url string) *ResourceError { + return NewResourceError(ErrorTypeUnsupportedLink, "不支持的链接地址", url, "") +} + +// NewInvalidLinkError 创建无效链接错误 +func NewInvalidLinkError(url string, details string) *ResourceError { + return NewResourceError(ErrorTypeInvalidLink, "链接无效", url, details) +} + +// NewNoAccountError 创建没有账号错误 +func NewNoAccountError(platform string) *ResourceError { + return NewResourceError(ErrorTypeNoAccount, "没有可用的网盘账号", "", fmt.Sprintf("平台: %s", platform)) +} + +// NewNoValidAccountError 创建没有有效账号错误 +func NewNoValidAccountError(platform string) *ResourceError { + return NewResourceError(ErrorTypeNoValidAccount, "没有有效的网盘账号", "", fmt.Sprintf("平台: %s", platform)) +} + +// NewServiceCreationError 创建服务创建失败错误 +func NewServiceCreationError(url string, details string) *ResourceError { + return NewResourceError(ErrorTypeServiceCreation, "创建网盘服务失败", url, details) +} + +// NewTransferFailedError 创建转存失败错误 +func NewTransferFailedError(url string, details string) *ResourceError { + return NewResourceError(ErrorTypeTransferFailed, "网盘信息获取失败", url, details) +} + +// NewTagProcessingError 创建标签处理失败错误 +func NewTagProcessingError(details string) *ResourceError { + return NewResourceError(ErrorTypeTagProcessing, "处理标签失败", "", details) +} + +// NewCategoryProcessingError 创建分类处理失败错误 +func NewCategoryProcessingError(details string) *ResourceError { + return NewResourceError(ErrorTypeCategoryProcessing, "处理分类失败", "", details) +} + +// NewResourceSaveError 创建资源保存失败错误 +func NewResourceSaveError(url string, details string) *ResourceError { + return NewResourceError(ErrorTypeResourceSave, "资源保存失败", url, details) +} + +// NewPlatformNotFoundError 创建平台未找到错误 +func NewPlatformNotFoundError(platform string) *ResourceError { + return NewResourceError(ErrorTypePlatformNotFound, "未找到对应的平台ID", "", fmt.Sprintf("平台: %s", platform)) +} + +// NewLinkCheckError 创建链接检查失败错误 +func NewLinkCheckError(url string, details string) *ResourceError { + return NewResourceError(ErrorTypeLinkCheckFailed, "链接检查失败", url, details) +} + +// IsResourceError 检查是否为资源错误 +func IsResourceError(err error) bool { + _, ok := err.(*ResourceError) + return ok +} + +// GetResourceError 获取资源错误 +func GetResourceError(err error) *ResourceError { + if resourceErr, ok := err.(*ResourceError); ok { + return resourceErr + } + return nil +} + +// GetErrorType 获取错误类型 +func GetErrorType(err error) ErrorType { + if resourceErr := GetResourceError(err); resourceErr != nil { + return resourceErr.Type + } + return "" +} + +// IsRetryableError 检查是否为可重试的错误 +func IsRetryableError(err error) bool { + errorType := GetErrorType(err) + switch errorType { + case ErrorTypeNoAccount, ErrorTypeNoValidAccount, ErrorTypeTransferFailed, ErrorTypeLinkCheckFailed: + return true + default: + return false + } +} + +// GetErrorSummary 获取错误摘要 +func GetErrorSummary(err error) string { + if resourceErr := GetResourceError(err); resourceErr != nil { + return fmt.Sprintf("%s: %s", resourceErr.Type, resourceErr.Message) + } + return err.Error() +} diff --git a/utils/scheduler.go b/utils/scheduler.go index 2c3bcda..9894f5e 100644 --- a/utils/scheduler.go +++ b/utils/scheduler.go @@ -352,8 +352,9 @@ func (s *Scheduler) processReadyResources() { return } - // 获取所有待处理资源 + // 获取所有没有错误的待处理资源 readyResources, err := s.readyResourceRepo.FindAll() + // readyResources, err := s.readyResourceRepo.FindWithoutErrors() if err != nil { Error("获取待处理资源失败: %v", err) return @@ -384,10 +385,21 @@ func (s *Scheduler) processReadyResources() { if err := s.convertReadyResourceToResource(readyResource, factory); err != nil { Error("处理资源失败 (ID: %d): %v", readyResource.ID, err) + + // 保存完整的错误信息 + readyResource.ErrorMsg = err.Error() + + if updateErr := s.readyResourceRepo.Update(&readyResource); updateErr != nil { + Error("更新错误信息失败 (ID: %d): %v", readyResource.ID, updateErr) + } else { + Info("已保存错误信息到资源 (ID: %d): %s", readyResource.ID, err.Error()) + } + } else { + // 处理成功,删除readyResource + s.readyResourceRepo.Delete(readyResource.ID) + processedCount++ + Info("成功处理资源: %s", readyResource.URL) } - s.readyResourceRepo.Delete(readyResource.ID) - processedCount++ - Info("成功处理资源: %s", readyResource.URL) } Info("待处理资源处理完成,共处理 %d 个资源", processedCount) @@ -401,7 +413,7 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes shareID, serviceType := panutils.ExtractShareId(readyResource.URL) if serviceType == panutils.NotFound { Warn("不支持的链接地址: %s", readyResource.URL) - return nil + return NewUnsupportedLinkError(readyResource.URL) } Debug("检测到服务类型: %s, 分享ID: %s", serviceType.String(), shareID) @@ -420,24 +432,32 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes // 不是夸克,直接保存, if serviceType != panutils.Quark { // 检测是否有效 - checkResult, _ := commonutils.CheckURL(readyResource.URL) + checkResult, err := commonutils.CheckURL(readyResource.URL) + if err != nil { + Error("链接检查失败: %v", err) + return NewLinkCheckError(readyResource.URL, err.Error()) + } if !checkResult.Status { Warn("链接无效: %s", readyResource.URL) - return nil + return NewInvalidLinkError(readyResource.URL, "链接状态检查失败") } - - return nil } else { // 获取夸克网盘账号的 cookie - accounts, err := s.cksRepo.FindByPanID(*s.getPanIDByServiceType(serviceType)) + panID := s.getPanIDByServiceType(serviceType) + if panID == nil { + Error("未找到对应的平台ID") + return NewPlatformNotFoundError(serviceType.String()) + } + + accounts, err := s.cksRepo.FindByPanID(*panID) if err != nil { Error("获取夸克网盘账号失败: %v", err) - return err + return NewServiceCreationError(readyResource.URL, fmt.Sprintf("获取网盘账号失败: %v", err)) } if len(accounts) == 0 { Error("没有可用的夸克网盘账号") - return fmt.Errorf("没有可用的夸克网盘账号") + return NewNoAccountError(serviceType.String()) } // 选择第一个有效的账号 @@ -451,7 +471,7 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes if selectedAccount == nil { Error("没有有效的夸克网盘账号") - return fmt.Errorf("没有有效的夸克网盘账号") + return NewNoValidAccountError(serviceType.String()) } Debug("使用夸克网盘账号: %d, Cookie: %s", selectedAccount.ID, selectedAccount.Ck[:20]+"...") @@ -471,32 +491,32 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes panService, err := factory.CreatePanService(readyResource.URL, config) if err != nil { Error("获取网盘服务失败: %v", err) - return err + return NewServiceCreationError(readyResource.URL, err.Error()) } // 统一处理:尝试转存获取标题 result, err := panService.Transfer(shareID) if err != nil { Error("网盘信息获取失败: %v", err) - return err + return NewTransferFailedError(readyResource.URL, err.Error()) } if !result.Success { Error("网盘信息获取失败: %s", result.Message) - return nil + return NewTransferFailedError(readyResource.URL, result.Message) } // 如果获取到了标题,更新资源标题 - if result.Title != "" { - resource.Title = result.Title - } + // if result.Title != "" { + // resource.Title = result.Title + // } } // 处理标签 tagIDs, err := s.handleTags(readyResource.Tags) if err != nil { Error("处理标签失败: %v", err) - return err + return NewTagProcessingError(err.Error()) } // 如果没有标签,tagIDs 可能为 nil,这是正常的 if tagIDs == nil { @@ -506,7 +526,7 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes categoryID, err := s.resolveCategory(readyResource.Category, tagIDs) if err != nil { Error("处理分类失败: %v", err) - return err + return NewCategoryProcessingError(err.Error()) } if categoryID != nil { resource.CategoryID = categoryID @@ -515,7 +535,7 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes err = s.resourceRepo.Create(resource) if err != nil { Error("资源保存失败: %v", err) - return err + return NewResourceSaveError(readyResource.URL, err.Error()) } // 插入 resource_tags 关联 if len(tagIDs) > 0 { @@ -523,6 +543,7 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes err := s.resourceRepo.CreateResourceTag(resource.ID, tagID) if err != nil { Error("插入资源标签关联失败: %v", err) + // 这里不返回错误,因为资源已经保存成功,标签关联失败不影响主要功能 } } } else {