Files
urldb/task/expansion_processor.go
2025-10-28 11:07:00 +08:00

688 lines
24 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package task
import (
"context"
"encoding/json"
"fmt"
"math/rand"
"time"
pan "github.com/ctwj/urldb/common"
"github.com/ctwj/urldb/db/entity"
"github.com/ctwj/urldb/db/repo"
"github.com/ctwj/urldb/utils"
)
// ExpansionProcessor 扩容任务处理器
type ExpansionProcessor struct {
repoMgr *repo.RepositoryManager
}
// NewExpansionProcessor 创建扩容任务处理器
func NewExpansionProcessor(repoMgr *repo.RepositoryManager) *ExpansionProcessor {
return &ExpansionProcessor{
repoMgr: repoMgr,
}
}
// GetTaskType 获取任务类型
func (ep *ExpansionProcessor) GetTaskType() string {
return "expansion"
}
// ExpansionInput 扩容任务输入数据结构
type ExpansionInput struct {
PanAccountID uint `json:"pan_account_id"`
DataSource map[string]interface{} `json:"data_source,omitempty"`
}
// TransferredResource 转存成功的资源信息
type TransferredResource struct {
Title string `json:"title"`
URL string `json:"url"`
}
// ExpansionOutput 扩容任务输出数据结构
type ExpansionOutput struct {
Success bool `json:"success"`
Message string `json:"message"`
Error string `json:"error,omitempty"`
Time string `json:"time"`
TransferredResources []TransferredResource `json:"transferred_resources,omitempty"`
}
// Process 处理扩容任务项
func (ep *ExpansionProcessor) Process(ctx context.Context, taskID uint, item *entity.TaskItem) error {
startTime := utils.GetCurrentTime()
utils.InfoWithFields(map[string]interface{}{
"task_item_id": item.ID,
"task_id": taskID,
}, "开始处理扩容任务项: %d", item.ID)
// 解析输入数据
parseStart := utils.GetCurrentTime()
var input ExpansionInput
if err := json.Unmarshal([]byte(item.InputData), &input); err != nil {
parseDuration := time.Since(parseStart)
utils.ErrorWithFields(map[string]interface{}{
"error": err.Error(),
"duration_ms": parseDuration.Milliseconds(),
}, "解析输入数据失败: %v耗时: %v", err, parseDuration)
return fmt.Errorf("解析输入数据失败: %v", err)
}
parseDuration := time.Since(parseStart)
utils.DebugWithFields(map[string]interface{}{
"duration_ms": parseDuration.Milliseconds(),
}, "解析输入数据完成,耗时: %v", parseDuration)
// 验证输入数据
validateStart := utils.GetCurrentTime()
if err := ep.validateInput(&input); err != nil {
validateDuration := time.Since(validateStart)
utils.Error("输入数据验证失败: %v耗时: %v", err, validateDuration)
return fmt.Errorf("输入数据验证失败: %v", err)
}
validateDuration := time.Since(validateStart)
utils.Debug("输入数据验证完成,耗时: %v", validateDuration)
// 检查账号是否已经扩容过
checkExpansionStart := utils.GetCurrentTime()
exists, err := ep.checkExpansionExists(input.PanAccountID)
checkExpansionDuration := time.Since(checkExpansionStart)
if err != nil {
utils.Error("检查扩容记录失败: %v耗时: %v", err, checkExpansionDuration)
return fmt.Errorf("检查扩容记录失败: %v", err)
}
utils.Debug("检查扩容记录完成,耗时: %v", checkExpansionDuration)
if exists {
output := ExpansionOutput{
Success: false,
Message: "账号已扩容过",
Error: "每个账号只能扩容一次",
Time: utils.GetCurrentTimeString(),
}
outputJSON, _ := json.Marshal(output)
item.OutputData = string(outputJSON)
utils.Info("账号已扩容过,跳过扩容: 账号ID %d", input.PanAccountID)
return fmt.Errorf("账号已扩容过")
}
// 检查账号类型只支持quark账号
checkAccountTypeStart := utils.GetCurrentTime()
if err := ep.checkAccountType(input.PanAccountID); err != nil {
checkAccountTypeDuration := time.Since(checkAccountTypeStart)
output := ExpansionOutput{
Success: false,
Message: "账号类型不支持扩容",
Error: err.Error(),
Time: utils.GetCurrentTimeString(),
}
outputJSON, _ := json.Marshal(output)
item.OutputData = string(outputJSON)
utils.Error("账号类型不支持扩容: %v耗时: %v", err, checkAccountTypeDuration)
return err
}
checkAccountTypeDuration := time.Since(checkAccountTypeStart)
utils.Debug("检查账号类型完成,耗时: %v", checkAccountTypeDuration)
// 执行扩容操作(传入数据源)
expansionStart := utils.GetCurrentTime()
transferred, err := ep.performExpansion(ctx, input.PanAccountID, input.DataSource)
expansionDuration := time.Since(expansionStart)
if err != nil {
output := ExpansionOutput{
Success: false,
Message: "扩容失败",
Error: err.Error(),
Time: utils.GetCurrentTimeString(),
}
outputJSON, _ := json.Marshal(output)
item.OutputData = string(outputJSON)
utils.Error("扩容任务项处理失败: %d, 错误: %v总耗时: %v", item.ID, err, expansionDuration)
return fmt.Errorf("扩容失败: %v", err)
}
utils.Debug("扩容操作完成,耗时: %v", expansionDuration)
// 扩容成功
output := ExpansionOutput{
Success: true,
Message: "扩容成功",
Time: utils.GetCurrentTimeString(),
TransferredResources: transferred,
}
outputJSON, _ := json.Marshal(output)
item.OutputData = string(outputJSON)
elapsedTime := time.Since(startTime)
utils.InfoWithFields(map[string]interface{}{
"task_item_id": item.ID,
"account_id": input.PanAccountID,
"duration_ms": elapsedTime.Milliseconds(),
}, "扩容任务项处理完成: %d, 账号ID: %d, 总耗时: %v", item.ID, input.PanAccountID, elapsedTime)
return nil
}
// validateInput 验证输入数据
func (ep *ExpansionProcessor) validateInput(input *ExpansionInput) error {
startTime := utils.GetCurrentTime()
if input.PanAccountID == 0 {
utils.Error("账号ID验证失败账号ID不能为空耗时: %v", time.Since(startTime))
return fmt.Errorf("账号ID不能为空")
}
utils.Debug("输入数据验证完成,耗时: %v", time.Since(startTime))
return nil
}
// checkExpansionExists 检查账号是否已经扩容过
func (ep *ExpansionProcessor) checkExpansionExists(panAccountID uint) (bool, error) {
startTime := utils.GetCurrentTime()
// 查询所有expansion类型的任务
tasksStart := utils.GetCurrentTime()
tasks, _, err := ep.repoMgr.TaskRepository.GetList(1, 1000, "expansion", "completed")
tasksDuration := time.Since(tasksStart)
if err != nil {
utils.Error("获取扩容任务列表失败: %v耗时: %v", err, tasksDuration)
return false, fmt.Errorf("获取扩容任务列表失败: %v", err)
}
utils.Debug("获取扩容任务列表完成,找到 %d 个任务,耗时: %v", len(tasks), tasksDuration)
// 检查每个任务的配置中是否包含该账号ID
checkStart := utils.GetCurrentTime()
for _, task := range tasks {
if task.Config != "" {
var taskConfig map[string]interface{}
if err := json.Unmarshal([]byte(task.Config), &taskConfig); err == nil {
if configAccountID, ok := taskConfig["pan_account_id"].(float64); ok {
if uint(configAccountID) == panAccountID {
// 找到了该账号的扩容任务,检查任务状态
if task.Status == "completed" {
// 如果任务已完成,说明已经扩容过
checkDuration := time.Since(checkStart)
utils.Debug("检查扩容记录完成,账号已扩容,耗时: %v", checkDuration)
return true, nil
}
}
}
}
}
}
checkDuration := time.Since(checkStart)
utils.Debug("检查扩容记录完成,账号未扩容,耗时: %v", checkDuration)
totalDuration := time.Since(startTime)
utils.Debug("检查扩容记录完成,账号未扩容,总耗时: %v", totalDuration)
return false, nil
}
// checkAccountType 检查账号类型只支持quark账号
func (ep *ExpansionProcessor) checkAccountType(panAccountID uint) error {
startTime := utils.GetCurrentTime()
// 获取账号信息
accountStart := utils.GetCurrentTime()
cks, err := ep.repoMgr.CksRepository.FindByID(panAccountID)
accountDuration := time.Since(accountStart)
if err != nil {
utils.Error("获取账号信息失败: %v耗时: %v", err, accountDuration)
return fmt.Errorf("获取账号信息失败: %v", err)
}
utils.Debug("获取账号信息完成,耗时: %v", accountDuration)
// 检查是否为quark账号
serviceCheckStart := utils.GetCurrentTime()
if cks.ServiceType != "quark" {
serviceCheckDuration := time.Since(serviceCheckStart)
utils.Error("账号类型检查失败,当前账号类型: %s耗时: %v", cks.ServiceType, serviceCheckDuration)
return fmt.Errorf("只支持quark账号扩容当前账号类型: %s", cks.ServiceType)
}
serviceCheckDuration := time.Since(serviceCheckStart)
utils.Debug("账号类型检查完成为quark账号耗时: %v", serviceCheckDuration)
totalDuration := time.Since(startTime)
utils.Debug("账号类型检查完成,总耗时: %v", totalDuration)
return nil
}
// performExpansion 执行扩容操作
func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID uint, dataSource map[string]interface{}) ([]TransferredResource, error) {
rand.Seed(time.Now().UnixNano())
startTime := utils.GetCurrentTime()
utils.Info("执行扩容操作账号ID: %d, 数据源: %v", panAccountID, dataSource)
transferred := []TransferredResource{}
// 获取账号信息
accountStart := utils.GetCurrentTime()
account, err := ep.repoMgr.CksRepository.FindByID(panAccountID)
accountDuration := time.Since(accountStart)
if err != nil {
utils.Error("获取账号信息失败: %v耗时: %v", err, accountDuration)
return nil, fmt.Errorf("获取账号信息失败: %v", err)
}
utils.Debug("获取账号信息完成,耗时: %v", accountDuration)
// 创建网盘服务工厂
serviceStart := utils.GetCurrentTime()
factory := pan.NewPanFactory()
service, err := factory.CreatePanServiceByType(pan.Quark, &pan.PanConfig{
URL: "",
ExpiredType: 0,
IsType: 0,
Cookie: account.Ck,
})
serviceDuration := time.Since(serviceStart)
if err != nil {
utils.Error("创建网盘服务失败: %v耗时: %v", err, serviceDuration)
return nil, fmt.Errorf("创建网盘服务失败: %v", err)
}
service.SetCKSRepository(ep.repoMgr.CksRepository, *account)
utils.Debug("创建网盘服务完成,耗时: %v", serviceDuration)
// 定义扩容分类列表(按优先级排序)
categories := []string{
"情色", "喜剧", "动作", "科幻", "动画", "悬疑", "犯罪", "惊悚",
"冒险", "恐怖", "战争", "传记", "剧情", "爱情", "家庭", "儿童",
"音乐", "历史", "奇幻", "歌舞", "武侠", "灾难", "西部", "古装", "运动",
}
// 获取数据源类型
dataSourceType := "internal"
var thirdPartyURL string
if dataSource != nil {
if dsType, ok := dataSource["type"].(string); ok {
dataSourceType = dsType
if dsType == "third-party" {
if url, ok := dataSource["url"].(string); ok {
thirdPartyURL = url
}
}
}
}
utils.Info("使用数据源类型: %s", dataSourceType)
totalTransferred := 0
totalFailed := 0
// 逐个处理分类
for _, category := range categories {
utils.Info("开始处理分类: %s", category)
// 获取该分类的资源
resourcesStart := utils.GetCurrentTime()
resources, err := ep.getHotResources(category)
resourcesDuration := time.Since(resourcesStart)
if err != nil {
utils.Error("获取分类 %s 的资源失败: %v耗时: %v", category, err, resourcesDuration)
continue
}
utils.Debug("获取分类 %s 的资源完成,耗时: %v", category, resourcesDuration)
if len(resources) == 0 {
utils.Info("分类 %s 没有可用资源,跳过", category)
continue
}
utils.Info("分类 %s 获取到 %d 个资源", category, len(resources))
// 转存该分类的资源限制每个分类最多转存20个
maxPerCategory := 20
transferredCount := 0
for _, resource := range resources {
if transferredCount >= maxPerCategory {
break
}
// 检查是否还有存储空间
storageCheckStart := utils.GetCurrentTime()
hasSpace, err := ep.checkStorageSpace(service, &account.Ck)
storageCheckDuration := time.Since(storageCheckStart)
if err != nil {
utils.Error("检查存储空间失败: %v耗时: %v", err, storageCheckDuration)
return transferred, fmt.Errorf("检查存储空间失败: %v", err)
}
utils.Debug("检查存储空间完成,耗时: %v", storageCheckDuration)
if !hasSpace {
utils.Info("存储空间不足,停止扩容,但保存已转存的资源")
// 存储空间不足时,停止继续转存,但返回已转存的资源作为成功结果
break
}
// 获取资源 , dataSourceType, thirdPartyURL
resourceGetStart := utils.GetCurrentTime()
resource, err := ep.getResourcesByHot(resource, dataSourceType, thirdPartyURL, *account, service)
resourceGetDuration := time.Since(resourceGetStart)
if resource == nil || err != nil {
if resource != nil {
utils.Error("获取资源失败: %s, 错误: %v耗时: %v", resource.Title, err, resourceGetDuration)
} else {
utils.Error("获取资源失败, 错误: %v耗时: %v", err, resourceGetDuration)
}
totalFailed++
continue
}
utils.Debug("获取资源完成,耗时: %v", resourceGetDuration)
// 执行转存
transferStart := utils.GetCurrentTime()
saveURL, err := ep.transferResource(ctx, service, resource, *account)
transferDuration := time.Since(transferStart)
if err != nil {
utils.Error("转存资源失败: %s, 错误: %v耗时: %v", resource.Title, err, transferDuration)
totalFailed++
continue
}
utils.Debug("转存资源完成,耗时: %v", transferDuration)
// 随机休眠1-3秒避免请求过于频繁
sleepDuration := time.Duration(rand.Intn(3)+1) * time.Second
time.Sleep(sleepDuration)
// 保存转存结果到任务输出
transferred = append(transferred, TransferredResource{
Title: resource.Title,
URL: saveURL,
})
totalTransferred++
transferredCount++
utils.Info("成功转存资源: %s -> %s", resource.Title, saveURL)
// 每转存5个资源检查一次存储空间
if totalTransferred%5 == 0 {
utils.Info("已转存 %d 个资源,检查存储空间", totalTransferred)
}
}
utils.Info("分类 %s 处理完成,转存 %d 个资源", category, transferredCount)
}
elapsedTime := time.Since(startTime)
utils.Info("扩容完成,总共转存: %d 个资源,失败: %d 个资源,总耗时: %v", totalTransferred, totalFailed, elapsedTime)
return transferred, nil
}
// getResourcesForCategory 获取指定分类的资源
func (ep *ExpansionProcessor) getResourcesByHot(
resource *entity.HotDrama, dataSourceType,
thirdPartyURL string,
entity entity.Cks,
service pan.PanService,
) (*entity.Resource, error) {
startTime := utils.GetCurrentTime()
if dataSourceType == "third-party" && thirdPartyURL != "" {
// 从第三方API获取资源
thirdPartyStart := utils.GetCurrentTime()
result, err := ep.getResourcesFromThirdPartyAPI(resource, thirdPartyURL)
thirdPartyDuration := time.Since(thirdPartyStart)
utils.Debug("从第三方API获取资源完成耗时: %v", thirdPartyDuration)
return result, err
}
// 从内部数据库获取资源
internalStart := utils.GetCurrentTime()
result, err := ep.getResourcesFromInternalDB(resource, entity, service)
internalDuration := time.Since(internalStart)
utils.Debug("从内部数据库获取资源完成,耗时: %v", internalDuration)
totalDuration := time.Since(startTime)
utils.Debug("获取资源完成: %s总耗时: %v", resource.Title, totalDuration)
return result, err
}
// getResourcesFromInternalDB 根据 HotDrama 的title 获取数据库中资源,并且资源的类型和 account 的资源类型一致
func (ep *ExpansionProcessor) getResourcesFromInternalDB(HotDrama *entity.HotDrama, account entity.Cks, service pan.PanService) (*entity.Resource, error) {
startTime := utils.GetCurrentTime()
// 修改配置 isType = 1 只检测,不转存
configStart := utils.GetCurrentTime()
service.UpdateConfig(&pan.PanConfig{
URL: "",
ExpiredType: 0,
IsType: 1,
Cookie: account.Ck,
})
utils.Debug("更新服务配置完成,耗时: %v", time.Since(configStart))
panID := account.PanID
// 1. 搜索标题
searchStart := utils.GetCurrentTime()
params := map[string]interface{}{
"search": HotDrama.Title,
"pan_id": panID,
"is_valid": true,
"page": 1,
"page_size": 10,
}
resources, _, err := ep.repoMgr.ResourceRepository.SearchWithFilters(params)
searchDuration := time.Since(searchStart)
if err != nil {
utils.Error("搜索资源失败: %v耗时: %v", err, searchDuration)
return nil, fmt.Errorf("搜索资源失败: %v", err)
}
utils.Debug("搜索资源完成,找到 %d 个资源,耗时: %v", len(resources), searchDuration)
// 检查结果是否有效,通过服务验证
validateStart := utils.GetCurrentTime()
for _, res := range resources {
if res.IsValid && res.URL != "" {
// 使用服务验证资源是否可转存
shareID, _ := pan.ExtractShareId(res.URL)
if shareID != "" {
result, err := service.Transfer(shareID)
if err == nil && result != nil && result.Success {
validateDuration := time.Since(validateStart)
utils.Debug("验证资源成功: %s耗时: %v", res.Title, validateDuration)
return &res, nil
}
}
}
}
validateDuration := time.Since(validateStart)
utils.Debug("验证资源完成,未找到有效资源,耗时: %v", validateDuration)
totalDuration := time.Since(startTime)
utils.Debug("从内部数据库获取资源完成: %s总耗时: %v", HotDrama.Title, totalDuration)
// 3. 没有有效资源,返回错误信息
return nil, fmt.Errorf("未找到有效的资源")
}
// getResourcesFromInternalDB 从内部数据库获取资源
func (ep *ExpansionProcessor) getHotResources(category string) ([]*entity.HotDrama, error) {
startTime := utils.GetCurrentTime()
// 获取该分类下sub_type为"排行"的资源
rankedStart := utils.GetCurrentTime()
dramas, _, err := ep.repoMgr.HotDramaRepository.FindByCategoryAndSubType(category, "排行", 1, 20)
rankedDuration := time.Since(rankedStart)
if err != nil {
utils.Error("获取分类 %s 的排行资源失败: %v耗时: %v", category, err, rankedDuration)
return nil, fmt.Errorf("获取分类 %s 的资源失败: %v", category, err)
}
utils.Debug("获取分类 %s 的排行资源完成,找到 %d 个资源,耗时: %v", category, len(dramas), rankedDuration)
// 如果没有找到"排行"类型的资源,尝试获取该分类下的所有资源
if len(dramas) == 0 {
allStart := utils.GetCurrentTime()
dramas, _, err = ep.repoMgr.HotDramaRepository.FindByCategory(category, 1, 20)
allDuration := time.Since(allStart)
if err != nil {
utils.Error("获取分类 %s 的所有资源失败: %v耗时: %v", category, err, allDuration)
return nil, fmt.Errorf("获取分类 %s 的资源失败: %v", category, err)
}
utils.Debug("获取分类 %s 的所有资源完成,找到 %d 个资源,耗时: %v", category, len(dramas), allDuration)
}
// 转换为指针数组
convertStart := utils.GetCurrentTime()
result := make([]*entity.HotDrama, len(dramas))
for i := range dramas {
result[i] = &dramas[i]
}
convertDuration := time.Since(convertStart)
utils.Debug("转换资源数组完成,耗时: %v", convertDuration)
totalDuration := time.Since(startTime)
utils.Debug("获取热门资源完成: 分类 %s总数 %d总耗时: %v", category, len(result), totalDuration)
return result, nil
}
// getResourcesFromThirdPartyAPI 从第三方API获取资源
func (ep *ExpansionProcessor) getResourcesFromThirdPartyAPI(resource *entity.HotDrama, apiURL string) (*entity.Resource, error) {
// 构建API请求URL添加分类参数
// requestURL := fmt.Sprintf("%s?category=%s&limit=20", apiURL, resource)
// TODO 使用第三方API接口请求资源
return nil, nil
}
// checkStorageSpace 检查存储空间是否足够
func (ep *ExpansionProcessor) checkStorageSpace(service pan.PanService, ck *string) (bool, error) {
startTime := utils.GetCurrentTime()
userInfoStart := utils.GetCurrentTime()
userInfo, err := service.GetUserInfo(ck)
userInfoDuration := time.Since(userInfoStart)
if err != nil {
utils.Error("获取用户信息失败: %v耗时: %v", err, userInfoDuration)
// 如果无法获取用户信息,假设还有空间继续
return true, nil
}
utils.Debug("获取用户信息完成,耗时: %v", userInfoDuration)
// 检查是否还有足够的空间保留至少10GB空间
const reservedSpaceGB = 100
reservedSpaceBytes := int64(reservedSpaceGB * 1024 * 1024 * 1024)
if userInfo.TotalSpace-userInfo.UsedSpace <= reservedSpaceBytes {
utils.Info("存储空间不足,已使用: %d bytes总容量: %d bytes检查耗时: %v",
userInfo.UsedSpace, userInfo.TotalSpace, time.Since(startTime))
return false, nil
}
totalDuration := time.Since(startTime)
utils.Debug("存储空间检查完成,有足够空间,耗时: %v", totalDuration)
return true, nil
}
// transferResource 执行单个资源的转存
func (ep *ExpansionProcessor) transferResource(ctx context.Context, service pan.PanService, res *entity.Resource, account entity.Cks) (string, error) {
startTime := utils.GetCurrentTime()
// 修改配置 isType = 0 转存
configStart := utils.GetCurrentTime()
service.UpdateConfig(&pan.PanConfig{
URL: "",
ExpiredType: 0,
IsType: 0,
Cookie: account.Ck,
})
utils.Debug("更新服务配置完成,耗时: %v", time.Since(configStart))
// 如果没有URL跳过转存
if res.URL == "" {
utils.Error("资源 %s 没有有效的URL", res.URL)
return "", fmt.Errorf("资源 %s 没有有效的URL", res.URL)
}
// 提取分享ID
extractStart := utils.GetCurrentTime()
shareID, _ := pan.ExtractShareId(res.URL)
extractDuration := time.Since(extractStart)
if shareID == "" {
utils.Error("无法从URL %s 提取分享ID耗时: %v", res.URL, extractDuration)
return "", fmt.Errorf("无法从URL %s 提取分享ID", res.URL)
}
utils.Debug("提取分享ID完成: %s耗时: %v", shareID, extractDuration)
// 执行转存
transferStart := utils.GetCurrentTime()
result, err := service.Transfer(shareID)
transferDuration := time.Since(transferStart)
if err != nil {
utils.Error("转存失败: %v耗时: %v", err, transferDuration)
return "", fmt.Errorf("转存失败: %v", err)
}
if result == nil || !result.Success {
errorMsg := "转存失败"
if result != nil {
errorMsg = result.Message
}
utils.Error("转存结果失败: %s耗时: %v", errorMsg, time.Since(transferStart))
return "", fmt.Errorf("转存失败: %s", errorMsg)
}
// 提取转存链接
extractURLStart := utils.GetCurrentTime()
var saveURL string
if result.Data != nil {
if data, ok := result.Data.(map[string]interface{}); ok {
if v, ok := data["shareUrl"]; ok {
saveURL, _ = v.(string)
}
}
}
if saveURL == "" {
saveURL = result.ShareURL
}
if saveURL == "" {
extractURLDuration := time.Since(extractURLStart)
utils.Error("转存成功但未获取到分享链接,耗时: %v", extractURLDuration)
return "", fmt.Errorf("转存成功但未获取到分享链接")
}
totalDuration := time.Since(startTime)
utils.Debug("转存资源完成: %s -> %s总耗时: %v", res.Title, saveURL, totalDuration)
return saveURL, nil
}
// recordTransferredResource 记录转存成功的资源
// func (ep *ExpansionProcessor) recordTransferredResource(drama *entity.HotDrama, accountID uint, saveURL string) error {
// // 获取夸克网盘的平台ID
// panIDInt, err := ep.repoMgr.PanRepository.FindIdByServiceType("quark")
// if err != nil {
// utils.Error("获取夸克网盘平台ID失败: %v", err)
// return err
// }
// // 转换为uint
// panID := uint(panIDInt)
// // 创建资源记录
// resource := &entity.Resource{
// Title: drama.Title,
// URL: drama.PosterURL,
// SaveURL: saveURL,
// PanID: &panID,
// CreatedAt: time.Now(),
// UpdatedAt: time.Now(),
// IsValid: true,
// IsPublic: false, // 扩容资源默认不公开
// }
// // 保存到数据库
// err = ep.repoMgr.ResourceRepository.Create(resource)
// if err != nil {
// return fmt.Errorf("保存资源记录失败: %v", err)
// }
// utils.Info("成功记录转存资源: %s (ID: %d)", drama.Title, resource.ID)
// return nil
// }