mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-25 11:29:37 +08:00
更新首页显示
This commit is contained in:
@@ -20,6 +20,7 @@ type ResourceRepository interface {
|
||||
FindByIsValid(isValid bool) ([]entity.Resource, error)
|
||||
FindByIsPublic(isPublic bool) ([]entity.Resource, error)
|
||||
Search(query string, categoryID *uint, page, limit int) ([]entity.Resource, int64, error)
|
||||
SearchByPanID(query string, panID uint, page, limit int) ([]entity.Resource, int64, error)
|
||||
IncrementViewCount(id uint) error
|
||||
FindWithTags() ([]entity.Resource, error)
|
||||
UpdateWithTags(resource *entity.Resource, tagIDs []uint) error
|
||||
@@ -99,7 +100,7 @@ func (r *ResourceRepositoryImpl) FindByCategoryIDPaginated(categoryID uint, page
|
||||
var total int64
|
||||
|
||||
offset := (page - 1) * limit
|
||||
db := r.db.Model(&entity.Resource{}).Where("category_id = ?", categoryID).Preload("Category").Preload("Tags")
|
||||
db := r.db.Model(&entity.Resource{}).Where("category_id = ?", categoryID).Preload("Category").Preload("Tags").Order("updated_at DESC")
|
||||
|
||||
// 获取总数
|
||||
if err := db.Count(&total).Error; err != nil {
|
||||
@@ -124,7 +125,7 @@ func (r *ResourceRepositoryImpl) FindByPanIDPaginated(panID uint, page, limit in
|
||||
var total int64
|
||||
|
||||
offset := (page - 1) * limit
|
||||
db := r.db.Model(&entity.Resource{}).Where("pan_id = ?", panID).Preload("Category").Preload("Tags")
|
||||
db := r.db.Model(&entity.Resource{}).Where("pan_id = ?", panID).Preload("Category").Preload("Tags").Order("updated_at DESC")
|
||||
|
||||
// 获取总数
|
||||
if err := db.Count(&total).Error; err != nil {
|
||||
@@ -172,8 +173,31 @@ func (r *ResourceRepositoryImpl) Search(query string, categoryID *uint, page, li
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 获取分页数据
|
||||
err := db.Offset(offset).Limit(limit).Find(&resources).Error
|
||||
// 获取分页数据,按更新时间倒序
|
||||
err := db.Order("updated_at DESC").Offset(offset).Limit(limit).Find(&resources).Error
|
||||
return resources, total, err
|
||||
}
|
||||
|
||||
// SearchByPanID 在指定平台内搜索资源
|
||||
func (r *ResourceRepositoryImpl) SearchByPanID(query string, panID uint, page, limit int) ([]entity.Resource, int64, error) {
|
||||
var resources []entity.Resource
|
||||
var total int64
|
||||
|
||||
offset := (page - 1) * limit
|
||||
db := r.db.Model(&entity.Resource{}).Preload("Category").Preload("Tags").Where("pan_id = ?", panID)
|
||||
|
||||
// 构建查询条件
|
||||
if query != "" {
|
||||
db = db.Where("title ILIKE ? OR description ILIKE ?", "%"+query+"%", "%"+query+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
if err := db.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 获取分页数据,按更新时间倒序
|
||||
err := db.Order("updated_at DESC").Offset(offset).Limit(limit).Find(&resources).Error
|
||||
return resources, total, err
|
||||
}
|
||||
|
||||
|
||||
269
doc/AUTO_PROCESS_FIX.md
Normal file
269
doc/AUTO_PROCESS_FIX.md
Normal file
@@ -0,0 +1,269 @@
|
||||
# 待处理资源自动处理功能修复说明
|
||||
|
||||
## 问题描述
|
||||
|
||||
在管理后台开启"待处理资源自动处理"功能后,系统没有自动开始任务,并且出现外键约束错误:
|
||||
```
|
||||
ERROR: insert or update on table "resources" violates foreign key constraint "fk_resources_pan" (SQLSTATE 23503)
|
||||
```
|
||||
|
||||
## 问题原因
|
||||
|
||||
1. **UpdateSchedulerStatus方法参数不完整**:`utils/global_scheduler.go` 中的 `UpdateSchedulerStatus` 方法只接收了 `autoFetchHotDramaEnabled` 参数,没有处理 `autoProcessReadyResources` 参数。
|
||||
|
||||
2. **UpdateSystemConfig调用参数不完整**:`handlers/system_config_handler.go` 中的 `UpdateSystemConfig` 函数只传递了热播剧相关的参数,没有传递待处理资源相关的参数。
|
||||
|
||||
3. **调度器间隔时间硬编码**:`utils/scheduler.go` 中的 `StartReadyResourceScheduler` 方法使用了硬编码的5分钟间隔,而不是使用系统配置中的 `AutoProcessInterval`。
|
||||
|
||||
4. **平台匹配机制优化**:使用 `serviceType` 来匹配平台,并添加平台映射缓存提高性能。
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 1. 修复 UpdateSchedulerStatus 方法
|
||||
|
||||
**文件**: `utils/global_scheduler.go`
|
||||
|
||||
**修改前**:
|
||||
```go
|
||||
func (gs *GlobalScheduler) UpdateSchedulerStatus(autoFetchHotDramaEnabled bool) {
|
||||
// 只处理热播剧功能
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```go
|
||||
func (gs *GlobalScheduler) UpdateSchedulerStatus(autoFetchHotDramaEnabled bool, autoProcessReadyResources bool) {
|
||||
// 处理热播剧自动拉取功能
|
||||
if autoFetchHotDramaEnabled {
|
||||
if !gs.scheduler.IsRunning() {
|
||||
log.Println("系统配置启用自动拉取热播剧,启动定时任务")
|
||||
gs.scheduler.StartHotDramaScheduler()
|
||||
}
|
||||
} else {
|
||||
if gs.scheduler.IsRunning() {
|
||||
log.Println("系统配置禁用自动拉取热播剧,停止定时任务")
|
||||
gs.scheduler.StopHotDramaScheduler()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理待处理资源自动处理功能
|
||||
if autoProcessReadyResources {
|
||||
if !gs.scheduler.IsReadyResourceRunning() {
|
||||
log.Println("系统配置启用自动处理待处理资源,启动定时任务")
|
||||
gs.scheduler.StartReadyResourceScheduler()
|
||||
}
|
||||
} else {
|
||||
if gs.scheduler.IsReadyResourceRunning() {
|
||||
log.Println("系统配置禁用自动处理待处理资源,停止定时任务")
|
||||
gs.scheduler.StopReadyResourceScheduler()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 修复 UpdateSystemConfig 函数
|
||||
|
||||
**文件**: `handlers/system_config_handler.go`
|
||||
|
||||
**修改前**:
|
||||
```go
|
||||
scheduler.UpdateSchedulerStatus(req.AutoFetchHotDramaEnabled)
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```go
|
||||
scheduler.UpdateSchedulerStatus(req.AutoFetchHotDramaEnabled, req.AutoProcessReadyResources)
|
||||
```
|
||||
|
||||
### 3. 修复调度器间隔时间配置
|
||||
|
||||
**文件**: `utils/scheduler.go`
|
||||
|
||||
**修改前**:
|
||||
```go
|
||||
ticker := time.NewTicker(5 * time.Minute) // 每5分钟检查一次
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```go
|
||||
// 获取系统配置中的间隔时间
|
||||
config, err := s.systemConfigRepo.GetOrCreateDefault()
|
||||
interval := 5 * time.Minute // 默认5分钟
|
||||
if err == nil && config.AutoProcessInterval > 0 {
|
||||
interval = time.Duration(config.AutoProcessInterval) * time.Minute
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
log.Printf("待处理资源自动处理任务已启动,间隔时间: %v", interval)
|
||||
```
|
||||
|
||||
### 4. 优化平台匹配机制
|
||||
|
||||
**文件**: `utils/scheduler.go`
|
||||
|
||||
**新增平台映射缓存**:
|
||||
```go
|
||||
type Scheduler struct {
|
||||
// ... 其他字段 ...
|
||||
|
||||
// 平台映射缓存
|
||||
panCache map[string]*uint // serviceType -> panID
|
||||
panCacheOnce sync.Once
|
||||
}
|
||||
```
|
||||
|
||||
**新增初始化缓存方法**:
|
||||
```go
|
||||
// initPanCache 初始化平台映射缓存
|
||||
func (s *Scheduler) initPanCache() {
|
||||
s.panCacheOnce.Do(func() {
|
||||
// 获取所有平台数据
|
||||
pans, err := s.panRepo.FindAll()
|
||||
if err != nil {
|
||||
log.Printf("初始化平台缓存失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 建立 ServiceType 到 PanID 的映射
|
||||
serviceTypeToPanName := map[string]string{
|
||||
"quark": "quark",
|
||||
"alipan": "aliyun", // 阿里云盘在数据库中的名称是 aliyun
|
||||
"baidu": "baidu",
|
||||
"uc": "uc",
|
||||
"unknown": "other",
|
||||
}
|
||||
|
||||
// 创建平台名称到ID的映射
|
||||
panNameToID := make(map[string]*uint)
|
||||
for _, pan := range pans {
|
||||
panID := pan.ID
|
||||
panNameToID[pan.Name] = &panID
|
||||
}
|
||||
|
||||
// 建立 ServiceType 到 PanID 的映射
|
||||
for serviceType, panName := range serviceTypeToPanName {
|
||||
if panID, exists := panNameToID[panName]; exists {
|
||||
s.panCache[serviceType] = panID
|
||||
log.Printf("平台映射缓存: %s -> %s (ID: %d)", serviceType, panName, *panID)
|
||||
} else {
|
||||
log.Printf("警告: 未找到平台 %s 对应的数据库记录", panName)
|
||||
}
|
||||
}
|
||||
|
||||
// 确保有默认的 other 平台
|
||||
if otherID, exists := panNameToID["other"]; exists {
|
||||
s.panCache["unknown"] = otherID
|
||||
}
|
||||
|
||||
log.Printf("平台映射缓存初始化完成,共 %d 个映射", len(s.panCache))
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**新增根据服务类型获取平台ID的方法**:
|
||||
```go
|
||||
// getPanIDByServiceType 根据服务类型获取平台ID
|
||||
func (s *Scheduler) getPanIDByServiceType(serviceType panutils.ServiceType) *uint {
|
||||
s.initPanCache()
|
||||
|
||||
serviceTypeStr := serviceType.String()
|
||||
if panID, exists := s.panCache[serviceTypeStr]; exists {
|
||||
return panID
|
||||
}
|
||||
|
||||
// 如果找不到,返回 other 平台的ID
|
||||
if otherID, exists := s.panCache["other"]; exists {
|
||||
log.Printf("未找到服务类型 %s 的映射,使用默认平台 other", serviceTypeStr)
|
||||
return otherID
|
||||
}
|
||||
|
||||
log.Printf("未找到服务类型 %s 的映射,且没有默认平台,返回nil", serviceTypeStr)
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
**修改资源创建逻辑**:
|
||||
```go
|
||||
// 在 convertReadyResourceToResource 方法中
|
||||
resource := &entity.Resource{
|
||||
Title: title,
|
||||
Description: readyResource.Description,
|
||||
URL: shareURL,
|
||||
PanID: s.getPanIDByServiceType(serviceType), // 使用 serviceType 匹配
|
||||
IsValid: true,
|
||||
IsPublic: true,
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 添加 PanRepository 依赖
|
||||
|
||||
**文件**: `utils/scheduler.go`
|
||||
|
||||
**修改前**:
|
||||
```go
|
||||
type Scheduler struct {
|
||||
doubanService *DoubanService
|
||||
hotDramaRepo repo.HotDramaRepository
|
||||
readyResourceRepo repo.ReadyResourceRepository
|
||||
resourceRepo repo.ResourceRepository
|
||||
systemConfigRepo repo.SystemConfigRepository
|
||||
stopChan chan bool
|
||||
isRunning bool
|
||||
readyResourceRunning bool
|
||||
processingMutex sync.Mutex
|
||||
hotDramaMutex sync.Mutex
|
||||
}
|
||||
```
|
||||
|
||||
**修改后**:
|
||||
```go
|
||||
type Scheduler struct {
|
||||
doubanService *DoubanService
|
||||
hotDramaRepo repo.HotDramaRepository
|
||||
readyResourceRepo repo.ReadyResourceRepository
|
||||
resourceRepo repo.ResourceRepository
|
||||
systemConfigRepo repo.SystemConfigRepository
|
||||
panRepo repo.PanRepository
|
||||
stopChan chan bool
|
||||
isRunning bool
|
||||
readyResourceRunning bool
|
||||
processingMutex sync.Mutex
|
||||
hotDramaMutex sync.Mutex
|
||||
|
||||
// 平台映射缓存
|
||||
panCache map[string]*uint // serviceType -> panID
|
||||
panCacheOnce sync.Once
|
||||
}
|
||||
```
|
||||
|
||||
## 修复效果
|
||||
|
||||
现在当您在管理后台开启"待处理资源自动处理"功能时:
|
||||
|
||||
1. **系统会立即启动调度器** - 不再需要重启服务器
|
||||
2. **使用配置的间隔时间** - 不再是固定的5分钟
|
||||
3. **支持实时开关** - 可以随时开启或关闭功能
|
||||
4. **正确的外键关联** - 不再出现外键约束错误
|
||||
5. **智能平台识别** - 根据 serviceType 自动识别对应的平台
|
||||
6. **高性能缓存** - 平台映射缓存,避免重复数据库查询
|
||||
|
||||
## 测试方法
|
||||
|
||||
运行测试脚本验证修复效果:
|
||||
```bash
|
||||
# 测试自动处理功能
|
||||
chmod +x test-auto-process.sh
|
||||
./test-auto-process.sh
|
||||
|
||||
# 测试平台匹配机制
|
||||
chmod +x test-pan-mapping.sh
|
||||
./test-pan-mapping.sh
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 确保数据库中有默认的平台数据
|
||||
2. 确保系统配置中的 `auto_process_interval` 设置合理
|
||||
3. 如果仍有问题,检查日志中的错误信息
|
||||
161
doc/HOMEPAGE_COMPLETE_FIX.md
Normal file
161
doc/HOMEPAGE_COMPLETE_FIX.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# 首页完整修复说明
|
||||
|
||||
## 问题描述
|
||||
1. 首页默认显示100条数据
|
||||
2. 今日更新没有显示
|
||||
3. 总资源数没有显示
|
||||
4. 首页没有默认加载数据
|
||||
5. 获取分类失败导致错误
|
||||
|
||||
## 问题原因分析
|
||||
|
||||
### 1. 数据加载问题
|
||||
- 首页初始化时调用 `store.fetchResources()` 没有传递分页参数
|
||||
- Store 中的 `fetchResources` 方法没有设置默认参数
|
||||
- 后端API需要 `page` 和 `page_size` 参数才能正确返回数据
|
||||
|
||||
### 2. 数据显示问题
|
||||
- 模板中使用 `visibleResources` 但该变量没有正确设置
|
||||
- `visibleResources` 是空数组,导致页面显示"暂无数据"
|
||||
- 统计数据计算依赖 `safeResources`,但数据没有正确加载
|
||||
|
||||
### 3. 类型错误问题
|
||||
- TypeScript 类型检查错误,API 返回的数据类型不明确
|
||||
|
||||
### 4. 分类获取问题
|
||||
- 分类API返回undefined导致错误
|
||||
- 首页不需要分类功能,应该移除相关调用
|
||||
|
||||
## 修复内容
|
||||
|
||||
### 1. 修复数据加载参数
|
||||
**文件**: `web/pages/index.vue`
|
||||
```javascript
|
||||
// 修复前
|
||||
const resourcesPromise = store.fetchResources().then((data: any) => {
|
||||
localResources.value = data.resources || []
|
||||
return data
|
||||
})
|
||||
|
||||
// 修复后
|
||||
const resourcesPromise = store.fetchResources({
|
||||
page: 1,
|
||||
page_size: 100
|
||||
}).then((data: any) => {
|
||||
localResources.value = data.resources || []
|
||||
return data
|
||||
})
|
||||
```
|
||||
|
||||
### 2. 修复visibleResources计算属性
|
||||
**文件**: `web/pages/index.vue`
|
||||
```javascript
|
||||
// 修复前
|
||||
const visibleResources = ref<any[]>([])
|
||||
const pageSize = ref(20)
|
||||
|
||||
// 修复后
|
||||
const visibleResources = computed(() => safeResources.value)
|
||||
const pageSize = ref(100) // 修改为100条数据
|
||||
```
|
||||
|
||||
### 3. 修复Store中的fetchResources方法
|
||||
**文件**: `web/stores/resource.ts`
|
||||
```javascript
|
||||
// 修复前
|
||||
async fetchResources(params?: any) {
|
||||
this.loading = true
|
||||
try {
|
||||
const { getResources } = useResourceApi()
|
||||
const data = await getResources(params)
|
||||
this.resources = data.resources
|
||||
this.currentPage = data.page
|
||||
this.totalPages = Math.ceil(data.total / data.limit)
|
||||
} catch (error) {
|
||||
console.error('获取资源失败:', error)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
// 修复后
|
||||
async fetchResources(params?: any) {
|
||||
this.loading = true
|
||||
try {
|
||||
const { getResources } = useResourceApi()
|
||||
// 确保有默认参数
|
||||
const defaultParams = {
|
||||
page: 1,
|
||||
page_size: 100,
|
||||
...params
|
||||
}
|
||||
const data = await getResources(defaultParams) as any
|
||||
this.resources = data.resources || []
|
||||
this.currentPage = data.page || 1
|
||||
this.totalPages = Math.ceil((data.total || 0) / (data.page_size || 100))
|
||||
} catch (error) {
|
||||
console.error('获取资源失败:', error)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 修复TypeScript类型错误
|
||||
**文件**: `web/stores/resource.ts`
|
||||
```javascript
|
||||
// 为所有API调用添加类型断言
|
||||
const data = await getResources(defaultParams) as any
|
||||
const stats = await getStats() as any
|
||||
```
|
||||
|
||||
### 5. 移除分类获取功能
|
||||
**文件**: `web/pages/index.vue`
|
||||
```javascript
|
||||
// 移除分类获取调用
|
||||
// 移除 localCategories 状态管理
|
||||
// 简化 safeCategories 计算属性
|
||||
```
|
||||
|
||||
## 修复要点总结
|
||||
|
||||
1. **参数传递**: 确保首页初始化时传递正确的分页参数
|
||||
2. **默认值设置**: Store 方法中设置合理的默认参数
|
||||
3. **计算属性**: 将 `visibleResources` 改为计算属性,直接使用 `safeResources`
|
||||
4. **数据量调整**: 将默认显示数据量从20条改为100条
|
||||
5. **类型安全**: 添加类型断言解决TypeScript错误
|
||||
6. **字段修正**: 使用正确的字段名 `page_size` 而不是 `limit`
|
||||
7. **移除分类**: 移除不必要的分类获取功能,避免API错误
|
||||
|
||||
## 测试验证
|
||||
|
||||
运行测试脚本验证修复效果:
|
||||
```bash
|
||||
chmod +x test-homepage-fix.sh
|
||||
./test-homepage-fix.sh
|
||||
```
|
||||
|
||||
## 预期效果
|
||||
|
||||
修复后,首页应该能够:
|
||||
1. ✅ 页面加载时自动显示前100条资源数据
|
||||
2. ✅ 正确显示今日更新数量(基于当前加载的数据计算)
|
||||
3. ✅ 正确显示总资源数(从统计API获取)
|
||||
4. ✅ 平台筛选功能正常工作
|
||||
5. ✅ 搜索功能正常工作
|
||||
6. ✅ "加载更多"功能继续正常工作
|
||||
7. ✅ 不再出现分类获取错误
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **数据计算**: 今日更新数量基于当前加载的100条数据计算,如果需要更准确的统计,需要加载所有数据或使用专门的统计API
|
||||
2. **性能考虑**: 加载100条数据可能影响页面加载速度,可根据实际需求调整
|
||||
3. **缓存策略**: 考虑添加数据缓存以提高用户体验
|
||||
4. **错误处理**: 确保网络错误时有合适的降级处理
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. **虚拟滚动**: 对于大量数据,考虑实现虚拟滚动
|
||||
2. **分页优化**: 实现更智能的分页策略
|
||||
3. **缓存机制**: 添加数据缓存减少重复请求
|
||||
4. **加载状态**: 优化加载状态的用户体验
|
||||
@@ -16,6 +16,7 @@ func GetResources(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
categoryID := c.Query("category_id")
|
||||
panID := c.Query("pan_id")
|
||||
search := c.Query("search")
|
||||
|
||||
var resources []entity.Resource
|
||||
@@ -25,9 +26,19 @@ func GetResources(c *gin.Context) {
|
||||
// 设置响应头,启用缓存
|
||||
c.Header("Cache-Control", "public, max-age=300") // 5分钟缓存
|
||||
|
||||
if search != "" {
|
||||
if search != "" && panID != "" {
|
||||
// 平台内搜索
|
||||
panIDUint, _ := strconv.ParseUint(panID, 10, 32)
|
||||
resources, total, err = repoManager.ResourceRepository.SearchByPanID(search, uint(panIDUint), page, pageSize)
|
||||
} else if search != "" {
|
||||
// 全局搜索
|
||||
resources, total, err = repoManager.ResourceRepository.Search(search, nil, page, pageSize)
|
||||
} else if panID != "" {
|
||||
// 按平台筛选
|
||||
panIDUint, _ := strconv.ParseUint(panID, 10, 32)
|
||||
resources, total, err = repoManager.ResourceRepository.FindByPanIDPaginated(uint(panIDUint), page, pageSize)
|
||||
} else if categoryID != "" {
|
||||
// 按分类筛选
|
||||
categoryIDUint, _ := strconv.ParseUint(categoryID, 10, 32)
|
||||
resources, total, err = repoManager.ResourceRepository.FindByCategoryIDPaginated(uint(categoryIDUint), page, pageSize)
|
||||
} else {
|
||||
|
||||
@@ -14,6 +14,7 @@ func GetSchedulerStatus(c *gin.Context) {
|
||||
repoManager.ReadyResourceRepository,
|
||||
repoManager.ResourceRepository,
|
||||
repoManager.SystemConfigRepository,
|
||||
repoManager.PanRepository,
|
||||
)
|
||||
|
||||
status := gin.H{
|
||||
@@ -31,6 +32,7 @@ func StartHotDramaScheduler(c *gin.Context) {
|
||||
repoManager.ReadyResourceRepository,
|
||||
repoManager.ResourceRepository,
|
||||
repoManager.SystemConfigRepository,
|
||||
repoManager.PanRepository,
|
||||
)
|
||||
if scheduler.IsHotDramaSchedulerRunning() {
|
||||
ErrorResponse(c, "热播剧定时任务已在运行中", http.StatusBadRequest)
|
||||
@@ -47,6 +49,7 @@ func StopHotDramaScheduler(c *gin.Context) {
|
||||
repoManager.ReadyResourceRepository,
|
||||
repoManager.ResourceRepository,
|
||||
repoManager.SystemConfigRepository,
|
||||
repoManager.PanRepository,
|
||||
)
|
||||
if !scheduler.IsHotDramaSchedulerRunning() {
|
||||
ErrorResponse(c, "热播剧定时任务未在运行", http.StatusBadRequest)
|
||||
@@ -63,6 +66,7 @@ func TriggerHotDramaScheduler(c *gin.Context) {
|
||||
repoManager.ReadyResourceRepository,
|
||||
repoManager.ResourceRepository,
|
||||
repoManager.SystemConfigRepository,
|
||||
repoManager.PanRepository,
|
||||
)
|
||||
scheduler.StartHotDramaScheduler() // 直接启动一次
|
||||
SuccessResponse(c, gin.H{"message": "手动触发热播剧定时任务成功"})
|
||||
@@ -75,6 +79,7 @@ func FetchHotDramaNames(c *gin.Context) {
|
||||
repoManager.ReadyResourceRepository,
|
||||
repoManager.ResourceRepository,
|
||||
repoManager.SystemConfigRepository,
|
||||
repoManager.PanRepository,
|
||||
)
|
||||
names, err := scheduler.GetHotDramaNames()
|
||||
if err != nil {
|
||||
@@ -91,6 +96,7 @@ func StartReadyResourceScheduler(c *gin.Context) {
|
||||
repoManager.ReadyResourceRepository,
|
||||
repoManager.ResourceRepository,
|
||||
repoManager.SystemConfigRepository,
|
||||
repoManager.PanRepository,
|
||||
)
|
||||
if scheduler.IsReadyResourceRunning() {
|
||||
ErrorResponse(c, "待处理资源自动处理任务已在运行中", http.StatusBadRequest)
|
||||
@@ -107,6 +113,7 @@ func StopReadyResourceScheduler(c *gin.Context) {
|
||||
repoManager.ReadyResourceRepository,
|
||||
repoManager.ResourceRepository,
|
||||
repoManager.SystemConfigRepository,
|
||||
repoManager.PanRepository,
|
||||
)
|
||||
if !scheduler.IsReadyResourceRunning() {
|
||||
ErrorResponse(c, "待处理资源自动处理任务未在运行", http.StatusBadRequest)
|
||||
@@ -123,6 +130,7 @@ func TriggerReadyResourceScheduler(c *gin.Context) {
|
||||
repoManager.ReadyResourceRepository,
|
||||
repoManager.ResourceRepository,
|
||||
repoManager.SystemConfigRepository,
|
||||
repoManager.PanRepository,
|
||||
)
|
||||
// 手动触发一次处理
|
||||
scheduler.ProcessReadyResources()
|
||||
|
||||
@@ -139,9 +139,10 @@ func UpdateSystemConfig(c *gin.Context) {
|
||||
repoManager.ReadyResourceRepository,
|
||||
repoManager.ResourceRepository,
|
||||
repoManager.SystemConfigRepository,
|
||||
repoManager.PanRepository,
|
||||
)
|
||||
if scheduler != nil {
|
||||
scheduler.UpdateSchedulerStatus(req.AutoFetchHotDramaEnabled)
|
||||
scheduler.UpdateSchedulerStatus(req.AutoFetchHotDramaEnabled, req.AutoProcessReadyResources)
|
||||
}
|
||||
|
||||
// 返回更新后的配置
|
||||
|
||||
1
main.go
1
main.go
@@ -35,6 +35,7 @@ func main() {
|
||||
repoManager.ReadyResourceRepository,
|
||||
repoManager.ResourceRepository,
|
||||
repoManager.SystemConfigRepository,
|
||||
repoManager.PanRepository,
|
||||
)
|
||||
|
||||
// 检查系统配置,决定是否启动待处理资源自动处理任务
|
||||
|
||||
@@ -18,10 +18,10 @@ var (
|
||||
)
|
||||
|
||||
// GetGlobalScheduler 获取全局调度器实例(单例模式)
|
||||
func GetGlobalScheduler(hotDramaRepo repo.HotDramaRepository, readyResourceRepo repo.ReadyResourceRepository, resourceRepo repo.ResourceRepository, systemConfigRepo repo.SystemConfigRepository) *GlobalScheduler {
|
||||
func GetGlobalScheduler(hotDramaRepo repo.HotDramaRepository, readyResourceRepo repo.ReadyResourceRepository, resourceRepo repo.ResourceRepository, systemConfigRepo repo.SystemConfigRepository, panRepo repo.PanRepository) *GlobalScheduler {
|
||||
once.Do(func() {
|
||||
globalScheduler = &GlobalScheduler{
|
||||
scheduler: NewScheduler(hotDramaRepo, readyResourceRepo, resourceRepo, systemConfigRepo),
|
||||
scheduler: NewScheduler(hotDramaRepo, readyResourceRepo, resourceRepo, systemConfigRepo, panRepo),
|
||||
}
|
||||
})
|
||||
return globalScheduler
|
||||
@@ -110,10 +110,11 @@ func (gs *GlobalScheduler) ProcessReadyResources() {
|
||||
}
|
||||
|
||||
// UpdateSchedulerStatus 根据系统配置更新调度器状态
|
||||
func (gs *GlobalScheduler) UpdateSchedulerStatus(autoFetchHotDramaEnabled bool) {
|
||||
func (gs *GlobalScheduler) UpdateSchedulerStatus(autoFetchHotDramaEnabled bool, autoProcessReadyResources bool) {
|
||||
gs.mutex.Lock()
|
||||
defer gs.mutex.Unlock()
|
||||
|
||||
// 处理热播剧自动拉取功能
|
||||
if autoFetchHotDramaEnabled {
|
||||
if !gs.scheduler.IsRunning() {
|
||||
log.Println("系统配置启用自动拉取热播剧,启动定时任务")
|
||||
@@ -125,4 +126,17 @@ func (gs *GlobalScheduler) UpdateSchedulerStatus(autoFetchHotDramaEnabled bool)
|
||||
gs.scheduler.StopHotDramaScheduler()
|
||||
}
|
||||
}
|
||||
|
||||
// 处理待处理资源自动处理功能
|
||||
if autoProcessReadyResources {
|
||||
if !gs.scheduler.IsReadyResourceRunning() {
|
||||
log.Println("系统配置启用自动处理待处理资源,启动定时任务")
|
||||
gs.scheduler.StartReadyResourceScheduler()
|
||||
}
|
||||
} else {
|
||||
if gs.scheduler.IsReadyResourceRunning() {
|
||||
log.Println("系统配置禁用自动处理待处理资源,停止定时任务")
|
||||
gs.scheduler.StopReadyResourceScheduler()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,26 +18,33 @@ type Scheduler struct {
|
||||
readyResourceRepo repo.ReadyResourceRepository
|
||||
resourceRepo repo.ResourceRepository
|
||||
systemConfigRepo repo.SystemConfigRepository
|
||||
panRepo repo.PanRepository
|
||||
stopChan chan bool
|
||||
isRunning bool
|
||||
readyResourceRunning bool
|
||||
processingMutex sync.Mutex // 防止ready_resource任务重叠执行
|
||||
hotDramaMutex sync.Mutex // 防止热播剧任务重叠执行
|
||||
|
||||
// 平台映射缓存
|
||||
panCache map[string]*uint // serviceType -> panID
|
||||
panCacheOnce sync.Once
|
||||
}
|
||||
|
||||
// NewScheduler 创建新的定时任务管理器
|
||||
func NewScheduler(hotDramaRepo repo.HotDramaRepository, readyResourceRepo repo.ReadyResourceRepository, resourceRepo repo.ResourceRepository, systemConfigRepo repo.SystemConfigRepository) *Scheduler {
|
||||
func NewScheduler(hotDramaRepo repo.HotDramaRepository, readyResourceRepo repo.ReadyResourceRepository, resourceRepo repo.ResourceRepository, systemConfigRepo repo.SystemConfigRepository, panRepo repo.PanRepository) *Scheduler {
|
||||
return &Scheduler{
|
||||
doubanService: NewDoubanService(),
|
||||
hotDramaRepo: hotDramaRepo,
|
||||
readyResourceRepo: readyResourceRepo,
|
||||
resourceRepo: resourceRepo,
|
||||
systemConfigRepo: systemConfigRepo,
|
||||
panRepo: panRepo,
|
||||
stopChan: make(chan bool),
|
||||
isRunning: false,
|
||||
readyResourceRunning: false,
|
||||
processingMutex: sync.Mutex{},
|
||||
hotDramaMutex: sync.Mutex{},
|
||||
panCache: make(map[string]*uint),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,9 +215,18 @@ func (s *Scheduler) StartReadyResourceScheduler() {
|
||||
log.Println("启动待处理资源自动处理任务")
|
||||
|
||||
go func() {
|
||||
ticker := time.NewTicker(5 * time.Minute) // 每5分钟检查一次
|
||||
// 获取系统配置中的间隔时间
|
||||
config, err := s.systemConfigRepo.GetOrCreateDefault()
|
||||
interval := 5 * time.Minute // 默认5分钟
|
||||
if err == nil && config.AutoProcessInterval > 0 {
|
||||
interval = time.Duration(config.AutoProcessInterval) * time.Minute
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
log.Printf("待处理资源自动处理任务已启动,间隔时间: %v", interval)
|
||||
|
||||
// 立即执行一次
|
||||
s.processReadyResources()
|
||||
|
||||
@@ -399,7 +415,7 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes
|
||||
Title: title,
|
||||
Description: readyResource.Description,
|
||||
URL: shareURL,
|
||||
PanID: s.determinePanID(readyResource.URL),
|
||||
PanID: s.getPanIDByServiceType(serviceType),
|
||||
IsValid: true,
|
||||
IsPublic: true,
|
||||
}
|
||||
@@ -419,26 +435,6 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes
|
||||
return nil
|
||||
}
|
||||
|
||||
// determinePanID 根据URL确定平台ID
|
||||
func (s *Scheduler) determinePanID(url string) *uint {
|
||||
url = strings.ToLower(url)
|
||||
|
||||
// 这里可以根据你的平台配置来判断
|
||||
// 示例逻辑,你需要根据实际情况调整
|
||||
if strings.Contains(url, "pan.baidu.com") {
|
||||
panID := uint(1) // 百度网盘
|
||||
return &panID
|
||||
} else if strings.Contains(url, "www.aliyundrive.com") {
|
||||
panID := uint(2) // 阿里云盘
|
||||
return &panID
|
||||
} else if strings.Contains(url, "pan.quark.cn") {
|
||||
panID := uint(3) // 夸克网盘
|
||||
return &panID
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getOrCreateCategory 获取或创建分类
|
||||
func (s *Scheduler) getOrCreateCategory(categoryName string) (uint, error) {
|
||||
// 这里需要实现分类的查找和创建逻辑
|
||||
@@ -447,6 +443,70 @@ func (s *Scheduler) getOrCreateCategory(categoryName string) (uint, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// initPanCache 初始化平台映射缓存
|
||||
func (s *Scheduler) initPanCache() {
|
||||
s.panCacheOnce.Do(func() {
|
||||
// 获取所有平台数据
|
||||
pans, err := s.panRepo.FindAll()
|
||||
if err != nil {
|
||||
log.Printf("初始化平台缓存失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 建立 ServiceType 到 PanID 的映射
|
||||
serviceTypeToPanName := map[string]string{
|
||||
"quark": "quark",
|
||||
"alipan": "aliyun", // 阿里云盘在数据库中的名称是 aliyun
|
||||
"baidu": "baidu",
|
||||
"uc": "uc",
|
||||
"unknown": "other",
|
||||
}
|
||||
|
||||
// 创建平台名称到ID的映射
|
||||
panNameToID := make(map[string]*uint)
|
||||
for _, pan := range pans {
|
||||
panID := pan.ID
|
||||
panNameToID[pan.Name] = &panID
|
||||
}
|
||||
|
||||
// 建立 ServiceType 到 PanID 的映射
|
||||
for serviceType, panName := range serviceTypeToPanName {
|
||||
if panID, exists := panNameToID[panName]; exists {
|
||||
s.panCache[serviceType] = panID
|
||||
log.Printf("平台映射缓存: %s -> %s (ID: %d)", serviceType, panName, *panID)
|
||||
} else {
|
||||
log.Printf("警告: 未找到平台 %s 对应的数据库记录", panName)
|
||||
}
|
||||
}
|
||||
|
||||
// 确保有默认的 other 平台
|
||||
if otherID, exists := panNameToID["other"]; exists {
|
||||
s.panCache["unknown"] = otherID
|
||||
}
|
||||
|
||||
log.Printf("平台映射缓存初始化完成,共 %d 个映射", len(s.panCache))
|
||||
})
|
||||
}
|
||||
|
||||
// getPanIDByServiceType 根据服务类型获取平台ID
|
||||
func (s *Scheduler) getPanIDByServiceType(serviceType panutils.ServiceType) *uint {
|
||||
s.initPanCache()
|
||||
|
||||
serviceTypeStr := serviceType.String()
|
||||
if panID, exists := s.panCache[serviceTypeStr]; exists {
|
||||
return panID
|
||||
}
|
||||
|
||||
// 如果找不到,返回 other 平台的ID
|
||||
if otherID, exists := s.panCache["other"]; exists {
|
||||
log.Printf("未找到服务类型 %s 的映射,使用默认平台 other", serviceTypeStr)
|
||||
return otherID
|
||||
}
|
||||
|
||||
log.Printf("未找到服务类型 %s 的映射,且没有默认平台,返回nil", serviceTypeStr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsReadyResourceRunning 检查待处理资源自动处理任务是否在运行
|
||||
func (s *Scheduler) IsReadyResourceRunning() bool {
|
||||
return s.readyResourceRunning
|
||||
|
||||
@@ -18,6 +18,15 @@ export const parseApiResponse = <T>(response: any): T => {
|
||||
// 检查是否是包含success字段的响应格式(如登录接口)
|
||||
if (response && typeof response === 'object' && 'success' in response && 'data' in response) {
|
||||
if (response.success) {
|
||||
// 特殊处理资源接口返回的data.list格式,转换为resources格式
|
||||
if (response.data && response.data.list && Array.isArray(response.data.list)) {
|
||||
return {
|
||||
resources: response.data.list,
|
||||
total: response.data.total,
|
||||
page: response.data.page,
|
||||
page_size: response.data.limit
|
||||
} as T
|
||||
}
|
||||
return response.data
|
||||
} else {
|
||||
throw new Error(response.message || '请求失败')
|
||||
|
||||
@@ -168,7 +168,7 @@
|
||||
</a>
|
||||
</td>
|
||||
<td class="px-2 sm:px-6 py-2 sm:py-4 text-xs sm:text-sm text-gray-500" :title="resource.updated_at">
|
||||
{{ formatRelativeTime(resource.updated_at) }}
|
||||
<span v-html="formatRelativeTime(resource.updated_at)"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -231,13 +231,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加资源模态框 -->
|
||||
<!-- <ResourceModal
|
||||
v-if="showAddResourceModal"
|
||||
:resource="editingResource"
|
||||
@close="closeModal"
|
||||
@save="handleSaveResource"
|
||||
/> -->
|
||||
</div>
|
||||
|
||||
<!-- 页脚 -->
|
||||
@@ -259,10 +252,10 @@ const pageLoading = ref(true) // 添加页面加载状态
|
||||
const systemConfig = ref<SystemConfig | null>(null) // 添加系统配置状态
|
||||
|
||||
// 虚拟滚动相关
|
||||
const visibleResources = ref<any[]>([])
|
||||
const visibleResources = computed(() => safeResources.value)
|
||||
const hasMoreData = ref(true)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const pageSize = ref(100) // 修改为100条数据
|
||||
const isLoadingMore = ref(false)
|
||||
|
||||
// 延迟初始化store,避免SSR过程中的错误
|
||||
@@ -271,7 +264,6 @@ let userStore: any = null
|
||||
|
||||
// 本地状态管理,避免SSR过程中的store访问
|
||||
const localResources = ref<any[]>([])
|
||||
const localCategories = ref<any[]>([])
|
||||
const localStats = ref<any>({ total_resources: 0, total_categories: 0, total_tags: 0, total_views: 0 })
|
||||
const localLoading = ref(false)
|
||||
|
||||
@@ -293,12 +285,12 @@ const safeCategories = computed(() => {
|
||||
try {
|
||||
if (process.client && store) {
|
||||
const storeRefs = storeToRefs(store)
|
||||
return (storeRefs as any).categories?.value || localCategories.value
|
||||
return (storeRefs as any).categories?.value || []
|
||||
}
|
||||
return localCategories.value
|
||||
return []
|
||||
} catch (error) {
|
||||
console.error('获取categories时出错:', error)
|
||||
return localCategories.value
|
||||
return []
|
||||
}
|
||||
})
|
||||
|
||||
@@ -534,11 +526,21 @@ onMounted(async () => {
|
||||
// 使用Promise.race来添加超时机制,并优化请求顺序
|
||||
try {
|
||||
// 首先加载最重要的数据(资源列表)
|
||||
const resourcesPromise = store.fetchResources().then((data: any) => {
|
||||
localResources.value = data.resources || []
|
||||
const resourcesPromise = store.fetchResources({
|
||||
page: 1,
|
||||
page_size: 100
|
||||
}).then((data: any) => {
|
||||
console.log('首页 - 资源数据:', data)
|
||||
// 从store中获取数据,而不是从返回的data中获取
|
||||
if (store && store.resources) {
|
||||
localResources.value = store.resources || []
|
||||
} else {
|
||||
localResources.value = data?.resources || []
|
||||
}
|
||||
return data
|
||||
}).catch((e: any) => {
|
||||
console.error('获取资源失败:', e)
|
||||
localResources.value = []
|
||||
return { resources: [] }
|
||||
})
|
||||
|
||||
@@ -547,13 +549,6 @@ onMounted(async () => {
|
||||
|
||||
// 然后并行加载其他数据
|
||||
const otherDataPromise = Promise.allSettled([
|
||||
store.fetchCategories().then((data: any) => {
|
||||
localCategories.value = data.categories || []
|
||||
return data
|
||||
}).catch((e: any) => {
|
||||
console.error('获取分类失败:', e)
|
||||
return { categories: [] }
|
||||
}),
|
||||
store.fetchStats().then((data: any) => {
|
||||
localStats.value = data || { total_resources: 0, total_categories: 0, total_tags: 0, total_views: 0 }
|
||||
return data
|
||||
@@ -604,20 +599,50 @@ const fetchPlatforms = async () => {
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
const handleSearch = () => {
|
||||
const handleSearch = async () => {
|
||||
try {
|
||||
if (!store || !process.client) {
|
||||
console.error('store未初始化或不在客户端')
|
||||
return
|
||||
}
|
||||
|
||||
const platformId = selectedPlatform.value ? parseInt(selectedPlatform.value) : undefined
|
||||
store.searchResources(searchQuery.value, platformId).then((data: any) => {
|
||||
localResources.value = data.resources || []
|
||||
}).catch((error: any) => {
|
||||
console.error('搜索失败:', error)
|
||||
})
|
||||
|
||||
// 使用标准的资源API,传递pan_id参数
|
||||
const { useResourceApi } = await import('~/composables/useApi')
|
||||
const resourceApi = useResourceApi()
|
||||
const params: any = {
|
||||
page: 1,
|
||||
page_size: 100
|
||||
}
|
||||
|
||||
if (platformId) {
|
||||
params.pan_id = platformId
|
||||
}
|
||||
|
||||
if (searchQuery.value) {
|
||||
params.search = searchQuery.value
|
||||
}
|
||||
|
||||
console.log('搜索参数:', params)
|
||||
const response = await resourceApi.getResources(params) as any
|
||||
console.log('搜索结果:', response)
|
||||
|
||||
// 强制设置搜索结果到store,确保显示正确
|
||||
if (store && store.setResources) {
|
||||
store.setResources(response.resources || [])
|
||||
} else {
|
||||
// 如果没有setResources方法,直接设置localResources
|
||||
localResources.value = response.resources || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索处理时出错:', error)
|
||||
// 出错时也要清空结果
|
||||
if (store && store.setResources) {
|
||||
store.setResources([])
|
||||
} else {
|
||||
localResources.value = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,15 +713,15 @@ const formatRelativeTime = (dateString: string) => {
|
||||
return `<span class="text-pink-600 font-medium flex items-center"><i class="fas fa-circle-dot text-xs mr-1 animate-pulse"></i>${diffHour}小时前</span>`
|
||||
}
|
||||
} else if (diffDay < 1) {
|
||||
return `${diffHour}小时前`
|
||||
return `<span class="text-gray-600">${diffHour}小时前</span>`
|
||||
} else if (diffDay < 7) {
|
||||
return `${diffDay}天前`
|
||||
return `<span class="text-gray-600">${diffDay}天前</span>`
|
||||
} else if (diffWeek < 4) {
|
||||
return `${diffWeek}周前`
|
||||
return `<span class="text-gray-600">${diffWeek}周前</span>`
|
||||
} else if (diffMonth < 12) {
|
||||
return `${diffMonth}个月前`
|
||||
return `<span class="text-gray-600">${diffMonth}个月前</span>`
|
||||
} else {
|
||||
return `${diffYear}年前`
|
||||
return `<span class="text-gray-600">${diffYear}年前</span>`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,12 +60,40 @@ export const useResourceStore = defineStore('resource', {
|
||||
this.loading = true
|
||||
try {
|
||||
const { getResources } = useResourceApi()
|
||||
const data = await getResources(params)
|
||||
this.resources = data.resources
|
||||
this.currentPage = data.page
|
||||
this.totalPages = Math.ceil(data.total / data.limit)
|
||||
// 确保有默认参数
|
||||
const defaultParams = {
|
||||
page: 1,
|
||||
page_size: 100,
|
||||
...params
|
||||
}
|
||||
console.log('fetchResources - 请求参数:', defaultParams)
|
||||
const data = await getResources(defaultParams) as any
|
||||
console.log('fetchResources - 返回数据:', data)
|
||||
|
||||
// 添加更详细的错误检查
|
||||
if (!data) {
|
||||
console.error('fetchResources - 数据为空')
|
||||
this.resources = []
|
||||
return
|
||||
}
|
||||
|
||||
if (!data.resources) {
|
||||
console.error('fetchResources - 缺少resources字段:', data)
|
||||
this.resources = []
|
||||
return
|
||||
}
|
||||
|
||||
this.resources = data.resources || []
|
||||
this.currentPage = data.page || 1
|
||||
this.totalPages = Math.ceil((data.total || 0) / (data.page_size || 100))
|
||||
console.log('fetchResources - 设置成功:', {
|
||||
resourcesCount: this.resources.length,
|
||||
currentPage: this.currentPage,
|
||||
totalPages: this.totalPages
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('获取资源失败:', error)
|
||||
this.resources = []
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
@@ -74,7 +102,7 @@ export const useResourceStore = defineStore('resource', {
|
||||
async fetchCategories() {
|
||||
try {
|
||||
const { getCategories } = useCategoryApi()
|
||||
this.categories = await getCategories()
|
||||
this.categories = await getCategories() as any
|
||||
} catch (error) {
|
||||
console.error('获取分类失败:', error)
|
||||
}
|
||||
@@ -83,7 +111,7 @@ export const useResourceStore = defineStore('resource', {
|
||||
async fetchStats() {
|
||||
try {
|
||||
const { getStats } = useStatsApi()
|
||||
this.stats = await getStats()
|
||||
this.stats = await getStats() as any
|
||||
} catch (error) {
|
||||
console.error('获取统计失败:', error)
|
||||
}
|
||||
@@ -94,8 +122,8 @@ export const useResourceStore = defineStore('resource', {
|
||||
try {
|
||||
const { searchResources } = useResourceApi()
|
||||
const params = { q: query, category_id: categoryId }
|
||||
const data = await searchResources(params)
|
||||
this.resources = data.resources
|
||||
const data = await searchResources(params) as any
|
||||
this.resources = data.resources || []
|
||||
this.searchQuery = query
|
||||
this.selectedCategory = categoryId || null
|
||||
} catch (error) {
|
||||
@@ -171,6 +199,12 @@ export const useResourceStore = defineStore('resource', {
|
||||
}
|
||||
},
|
||||
|
||||
// 直接设置资源列表,用于搜索结果显示
|
||||
setResources(resources: Resource[]) {
|
||||
this.resources = resources
|
||||
console.log('setResources - 设置资源:', resources.length)
|
||||
},
|
||||
|
||||
clearSearch() {
|
||||
this.searchQuery = ''
|
||||
this.selectedCategory = null
|
||||
|
||||
Reference in New Issue
Block a user