update: 完善自动处理逻辑

This commit is contained in:
ctwj
2025-07-25 22:24:08 +08:00
parent 2582920e2c
commit f0e5c93a48
9 changed files with 187 additions and 75 deletions

View File

@@ -76,10 +76,4 @@
1. 在提交代码时使用规范的提交信息2. 在Pull Request中描述您的更改
3. 遵循项目的贡献指南
---
## 链接
- [项目主页](https://github.com/your-username/l9pan)
- [问题反馈](https://github.com/your-username/l9pan/issues)
- [讨论区](https://github.com/your-username/l9
---

View File

@@ -17,19 +17,16 @@ var DB *gorm.DB
// InitDB 初始化数据库连接
func InitDB() error {
host := os.Getenv("DB_HOST")
fmt.Printf("DB_HOST=%s\n", host)
if host == "" {
host = "localhost"
}
port := os.Getenv("DB_PORT")
fmt.Printf("DB_HOST=%s\n", port)
if port == "" {
port = "5432"
}
user := os.Getenv("DB_USER")
fmt.Printf("DB_HOST=%s\n", user)
if user == "" {
user = "postgres"
}

View File

@@ -33,6 +33,7 @@ type ResourceRepository interface {
FindExists(url string, excludeID ...uint) (bool, error)
BatchFindByURLs(urls []string) ([]entity.Resource, error)
GetResourcesForTransfer(panID uint, sinceTime time.Time) ([]*entity.Resource, error)
CreateResourceTag(resourceID, tagID uint) error
}
// ResourceRepositoryImpl Resource的Repository实现
@@ -333,7 +334,7 @@ func (r *ResourceRepositoryImpl) InvalidateCache() error {
// FindExists 检查是否存在相同URL的资源
func (r *ResourceRepositoryImpl) FindExists(url string, excludeID ...uint) (bool, error) {
var count int64
query := r.db.Model(&entity.Resource{}).Where("url = ?", url)
query := r.db.Model(&entity.Resource{}).Where("url = ? or save_url ", url, url)
// 如果有排除ID则排除该记录用于更新时排除自己
if len(excludeID) > 0 {
@@ -369,3 +370,12 @@ func (r *ResourceRepositoryImpl) GetResourcesForTransfer(panID uint, sinceTime t
}
return resources, nil
}
// CreateResourceTag 创建资源与标签的关联
func (r *ResourceRepositoryImpl) CreateResourceTag(resourceID, tagID uint) error {
resourceTag := &entity.ResourceTag{
ResourceID: resourceID,
TagID: tagID,
}
return r.GetDB().Create(resourceTag).Error
}

View File

@@ -18,6 +18,7 @@ type TagRepository interface {
FindWithPagination(page, pageSize int) ([]entity.Tag, int64, error)
Search(query string, page, pageSize int) ([]entity.Tag, int64, error)
UpdateWithNulls(tag *entity.Tag) error
GetByID(id uint) (*entity.Tag, error)
}
// TagRepositoryImpl Tag的Repository实现
@@ -144,3 +145,13 @@ func (r *TagRepositoryImpl) UpdateWithNulls(tag *entity.Tag) error {
// 使用Select方法明确指定要更新的字段包括null值
return r.db.Model(tag).Select("name", "description", "category_id", "updated_at").Updates(tag).Error
}
// GetByID 通过ID查找标签
func (r *TagRepositoryImpl) GetByID(id uint) (*entity.Tag, error) {
var tag entity.Tag
err := r.db.First(&tag, id).Error
if err != nil {
return nil, err
}
return &tag, nil
}

View File

@@ -16,6 +16,8 @@ func GetSchedulerStatus(c *gin.Context) {
repoManager.SystemConfigRepository,
repoManager.PanRepository,
repoManager.CksRepository,
repoManager.TagRepository,
repoManager.CategoryRepository,
)
status := gin.H{
@@ -36,6 +38,8 @@ func StartHotDramaScheduler(c *gin.Context) {
repoManager.SystemConfigRepository,
repoManager.PanRepository,
repoManager.CksRepository,
repoManager.TagRepository,
repoManager.CategoryRepository,
)
if scheduler.IsHotDramaSchedulerRunning() {
ErrorResponse(c, "热播剧定时任务已在运行中", http.StatusBadRequest)
@@ -54,6 +58,8 @@ func StopHotDramaScheduler(c *gin.Context) {
repoManager.SystemConfigRepository,
repoManager.PanRepository,
repoManager.CksRepository,
repoManager.TagRepository,
repoManager.CategoryRepository,
)
if !scheduler.IsHotDramaSchedulerRunning() {
ErrorResponse(c, "热播剧定时任务未在运行", http.StatusBadRequest)
@@ -72,6 +78,8 @@ func TriggerHotDramaScheduler(c *gin.Context) {
repoManager.SystemConfigRepository,
repoManager.PanRepository,
repoManager.CksRepository,
repoManager.TagRepository,
repoManager.CategoryRepository,
)
scheduler.StartHotDramaScheduler() // 直接启动一次
SuccessResponse(c, gin.H{"message": "手动触发热播剧定时任务成功"})
@@ -86,6 +94,8 @@ func FetchHotDramaNames(c *gin.Context) {
repoManager.SystemConfigRepository,
repoManager.PanRepository,
repoManager.CksRepository,
repoManager.TagRepository,
repoManager.CategoryRepository,
)
names, err := scheduler.GetHotDramaNames()
if err != nil {
@@ -104,6 +114,8 @@ func StartReadyResourceScheduler(c *gin.Context) {
repoManager.SystemConfigRepository,
repoManager.PanRepository,
repoManager.CksRepository,
repoManager.TagRepository,
repoManager.CategoryRepository,
)
if scheduler.IsReadyResourceRunning() {
ErrorResponse(c, "待处理资源自动处理任务已在运行中", http.StatusBadRequest)
@@ -122,6 +134,8 @@ func StopReadyResourceScheduler(c *gin.Context) {
repoManager.SystemConfigRepository,
repoManager.PanRepository,
repoManager.CksRepository,
repoManager.TagRepository,
repoManager.CategoryRepository,
)
if !scheduler.IsReadyResourceRunning() {
ErrorResponse(c, "待处理资源自动处理任务未在运行", http.StatusBadRequest)
@@ -140,6 +154,8 @@ func TriggerReadyResourceScheduler(c *gin.Context) {
repoManager.SystemConfigRepository,
repoManager.PanRepository,
repoManager.CksRepository,
repoManager.TagRepository,
repoManager.CategoryRepository,
)
// 手动触发一次处理
scheduler.ProcessReadyResources()
@@ -155,6 +171,8 @@ func StartAutoTransferScheduler(c *gin.Context) {
repoManager.SystemConfigRepository,
repoManager.PanRepository,
repoManager.CksRepository,
repoManager.TagRepository,
repoManager.CategoryRepository,
)
if scheduler.IsAutoTransferRunning() {
ErrorResponse(c, "自动转存定时任务已在运行中", http.StatusBadRequest)
@@ -173,6 +191,8 @@ func StopAutoTransferScheduler(c *gin.Context) {
repoManager.SystemConfigRepository,
repoManager.PanRepository,
repoManager.CksRepository,
repoManager.TagRepository,
repoManager.CategoryRepository,
)
if !scheduler.IsAutoTransferRunning() {
ErrorResponse(c, "自动转存定时任务未在运行", http.StatusBadRequest)
@@ -191,6 +211,8 @@ func TriggerAutoTransferScheduler(c *gin.Context) {
repoManager.SystemConfigRepository,
repoManager.PanRepository,
repoManager.CksRepository,
repoManager.TagRepository,
repoManager.CategoryRepository,
)
// 手动触发一次处理
scheduler.ProcessAutoTransfer()

View File

@@ -164,6 +164,8 @@ func UpdateSystemConfig(c *gin.Context) {
repoManager.SystemConfigRepository,
repoManager.PanRepository,
repoManager.CksRepository,
repoManager.TagRepository,
repoManager.CategoryRepository,
)
if scheduler != nil {
scheduler.UpdateSchedulerStatusWithAutoTransfer(req.AutoFetchHotDramaEnabled, req.AutoProcessReadyResources, req.AutoTransferEnabled)

View File

@@ -44,6 +44,8 @@ func main() {
repoManager.SystemConfigRepository,
repoManager.PanRepository,
repoManager.CksRepository,
repoManager.TagRepository,
repoManager.CategoryRepository,
)
// 检查系统配置,决定是否启动各种自动任务

View File

@@ -18,10 +18,10 @@ var (
)
// GetGlobalScheduler 获取全局调度器实例(单例模式)
func GetGlobalScheduler(hotDramaRepo repo.HotDramaRepository, readyResourceRepo repo.ReadyResourceRepository, resourceRepo repo.ResourceRepository, systemConfigRepo repo.SystemConfigRepository, panRepo repo.PanRepository, cksRepo repo.CksRepository) *GlobalScheduler {
func GetGlobalScheduler(hotDramaRepo repo.HotDramaRepository, readyResourceRepo repo.ReadyResourceRepository, resourceRepo repo.ResourceRepository, systemConfigRepo repo.SystemConfigRepository, panRepo repo.PanRepository, cksRepo repo.CksRepository, tagRepo repo.TagRepository, categoryRepo repo.CategoryRepository) *GlobalScheduler {
once.Do(func() {
globalScheduler = &GlobalScheduler{
scheduler: NewScheduler(hotDramaRepo, readyResourceRepo, resourceRepo, systemConfigRepo, panRepo, cksRepo),
scheduler: NewScheduler(hotDramaRepo, readyResourceRepo, resourceRepo, systemConfigRepo, panRepo, cksRepo, tagRepo, categoryRepo),
}
})
return globalScheduler

View File

@@ -16,13 +16,16 @@ import (
// Scheduler 定时任务管理器
type Scheduler struct {
doubanService *DoubanService
hotDramaRepo repo.HotDramaRepository
readyResourceRepo repo.ReadyResourceRepository
resourceRepo repo.ResourceRepository
systemConfigRepo repo.SystemConfigRepository
panRepo repo.PanRepository
cksRepo repo.CksRepository
doubanService *DoubanService
hotDramaRepo repo.HotDramaRepository
readyResourceRepo repo.ReadyResourceRepository
resourceRepo repo.ResourceRepository
systemConfigRepo repo.SystemConfigRepository
panRepo repo.PanRepository
cksRepo repo.CksRepository
// 新增
tagRepo repo.TagRepository
categoryRepo repo.CategoryRepository
stopChan chan bool
isRunning bool
readyResourceRunning bool
@@ -37,7 +40,7 @@ type Scheduler struct {
}
// NewScheduler 创建新的定时任务管理器
func NewScheduler(hotDramaRepo repo.HotDramaRepository, readyResourceRepo repo.ReadyResourceRepository, resourceRepo repo.ResourceRepository, systemConfigRepo repo.SystemConfigRepository, panRepo repo.PanRepository, cksRepo repo.CksRepository) *Scheduler {
func NewScheduler(hotDramaRepo repo.HotDramaRepository, readyResourceRepo repo.ReadyResourceRepository, resourceRepo repo.ResourceRepository, systemConfigRepo repo.SystemConfigRepository, panRepo repo.PanRepository, cksRepo repo.CksRepository, tagRepo repo.TagRepository, categoryRepo repo.CategoryRepository) *Scheduler {
return &Scheduler{
doubanService: NewDoubanService(),
hotDramaRepo: hotDramaRepo,
@@ -46,6 +49,8 @@ func NewScheduler(hotDramaRepo repo.HotDramaRepository, readyResourceRepo repo.R
systemConfigRepo: systemConfigRepo,
panRepo: panRepo,
cksRepo: cksRepo,
tagRepo: tagRepo,
categoryRepo: categoryRepo,
stopChan: make(chan bool),
isRunning: false,
readyResourceRunning: false,
@@ -401,6 +406,17 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes
Debug("检测到服务类型: %s, 分享ID: %s", serviceType.String(), shareID)
resource := &entity.Resource{
Title: derefString(readyResource.Title),
Description: readyResource.Description,
URL: readyResource.URL,
Cover: readyResource.Img,
IsValid: true,
IsPublic: true,
Key: readyResource.Key,
PanID: s.getPanIDByServiceType(serviceType),
}
// 不是夸克,直接保存,
if serviceType != panutils.Quark {
// 检测是否有效
@@ -410,67 +426,67 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes
return nil
}
// 入库
}
// 准备配置
config := &panutils.PanConfig{
URL: readyResource.URL,
Code: "", // 可以从readyResource中获取
IsType: 1, // 转存并分享后的资源信息 0 转存后分享, 1 只获取基本信息
ExpiredType: 1, // 永久分享
AdFid: "",
Stoken: "",
}
// 通过工厂获取对应的网盘服务单例
panService, err := factory.CreatePanService(readyResource.URL, config)
if err != nil {
Error("获取网盘服务失败: %v", err)
return err
}
// 统一处理:尝试转存获取标题
result, err := panService.Transfer(shareID)
if err != nil {
Error("网盘信息获取失败: %v", err)
return err
}
if !result.Success {
Error("网盘信息获取失败: %s", result.Message)
return nil
}
// 提取转存结果
if resultData, ok := result.Data.(map[string]interface{}); ok {
title := resultData["title"].(string)
shareURL := resultData["shareUrl"].(string)
// fid := resultData["fid"].(string) // 暂时未使用
// 创建资源记录
resource := &entity.Resource{
Title: title,
Description: readyResource.Description,
URL: shareURL,
PanID: s.getPanIDByServiceType(serviceType),
IsValid: true,
IsPublic: true,
Key: readyResource.Key,
} else {
// 准备配置
config := &panutils.PanConfig{
URL: readyResource.URL,
Code: "", // 可以从readyResource中获取
IsType: 1, // 转存并分享后的资源信息 0 转存后分享, 1 只获取基本信息
ExpiredType: 1, // 永久分享
AdFid: "",
Stoken: "",
}
// 如果有分类信息,尝试查找或创建分类
if readyResource.Category != "" {
categoryID, err := s.getOrCreateCategory(readyResource.Category)
if err == nil {
resource.CategoryID = &categoryID
}
// 通过工厂获取对应的网盘服务单例
panService, err := factory.CreatePanService(readyResource.URL, config)
if err != nil {
Error("获取网盘服务失败: %v", err)
return err
}
// 统一处理:尝试转存获取标题
result, err := panService.Transfer(shareID)
if err != nil {
Error("网盘信息获取失败: %v", err)
return err
}
if !result.Success {
Error("网盘信息获取失败: %s", result.Message)
return nil
}
return s.resourceRepo.Create(resource)
}
Error("转存结果格式异常")
// 处理标签
tagIDs, err := s.handleTags(readyResource.Tags)
if err != nil || tagIDs == nil {
Error("处理标签失败: %v", err)
return err
}
// 处理分类
categoryID, err := s.resolveCategory(readyResource.Category, tagIDs)
if err != nil {
Error("处理分类失败: %v", err)
return err
}
if categoryID != nil {
resource.CategoryID = categoryID
}
// 保存资源
err = s.resourceRepo.Create(resource)
if err != nil {
Error("资源保存失败: %v", err)
return err
}
// 插入 resource_tags 关联
for _, tagID := range tagIDs {
err := s.resourceRepo.CreateResourceTag(resource.ID, tagID)
if err != nil {
Error("插入资源标签关联失败: %v", err)
}
}
return nil
}
@@ -863,3 +879,61 @@ func (s *Scheduler) calculateAccountScore(account *entity.Cks) int64 {
return score
}
// 分割标签,支持中英文逗号
func splitTags(tagStr string) []string {
tagStr = strings.ReplaceAll(tagStr, "", ",")
return strings.Split(tagStr, ",")
}
// 处理标签返回所有标签ID
func (s *Scheduler) handleTags(tagStr string) ([]uint, error) {
if tagStr == "" {
return nil, nil
}
tagNames := splitTags(tagStr)
var tagIDs []uint
for _, name := range tagNames {
name = strings.TrimSpace(name)
if name == "" {
continue
}
tag, err := s.tagRepo.FindByName(name)
if err != nil || tag == nil {
// 不存在则新建
tag = &entity.Tag{Name: name}
err = s.tagRepo.Create(tag)
if err != nil {
return nil, err
}
}
tagIDs = append(tagIDs, tag.ID)
}
return tagIDs, nil
}
// 分类处理逻辑
func (s *Scheduler) resolveCategory(categoryName string, tagIDs []uint) (*uint, error) {
if categoryName != "" {
cat, err := s.categoryRepo.FindByName(categoryName)
if err == nil && cat != nil {
return &cat.ID, nil
}
}
// 没有分类,尝试用标签反查
for _, tagID := range tagIDs {
tag, err := s.tagRepo.GetByID(tagID)
if err == nil && tag != nil && tag.CategoryID != nil {
return tag.CategoryID, nil
}
}
return nil, nil
}
// 工具函数解引用string指针
func derefString(s *string) string {
if s == nil {
return ""
}
return *s
}