update: 更新页面,修复添加资源的问题

This commit is contained in:
ctwj
2025-07-27 01:45:04 +08:00
parent 312ecb041a
commit cf3376eb31
7 changed files with 196 additions and 39 deletions

View File

@@ -334,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 = ? or save_url ", url, url)
query := r.db.Model(&entity.Resource{}).Where("url = ? OR save_url = ?", url, url)
// 如果有排除ID则排除该记录用于更新时排除自己
if len(excludeID) > 0 {

View File

@@ -428,6 +428,34 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes
return nil
} else {
// 获取夸克网盘账号的 cookie
accounts, err := s.cksRepo.FindByPanID(*s.getPanIDByServiceType(serviceType))
if err != nil {
Error("获取夸克网盘账号失败: %v", err)
return err
}
if len(accounts) == 0 {
Error("没有可用的夸克网盘账号")
return fmt.Errorf("没有可用的夸克网盘账号")
}
// 选择第一个有效的账号
var selectedAccount *entity.Cks
for _, account := range accounts {
if account.IsValid {
selectedAccount = &account
break
}
}
if selectedAccount == nil {
Error("没有有效的夸克网盘账号")
return fmt.Errorf("没有有效的夸克网盘账号")
}
Debug("使用夸克网盘账号: %d, Cookie: %s", selectedAccount.ID, selectedAccount.Ck[:20]+"...")
// 准备配置
config := &panutils.PanConfig{
URL: readyResource.URL,
@@ -436,6 +464,7 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes
ExpiredType: 1, // 永久分享
AdFid: "",
Stoken: "",
Cookie: selectedAccount.Ck, // 添加 cookie
}
// 通过工厂获取对应的网盘服务单例
@@ -457,14 +486,22 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes
return nil
}
// 如果获取到了标题,更新资源标题
if result.Title != "" {
resource.Title = result.Title
}
}
// 处理标签
tagIDs, err := s.handleTags(readyResource.Tags)
if err != nil || tagIDs == nil {
if err != nil {
Error("处理标签失败: %v", err)
return err
}
// 如果没有标签tagIDs 可能为 nil这是正常的
if tagIDs == nil {
tagIDs = []uint{} // 初始化为空数组
}
// 处理分类
categoryID, err := s.resolveCategory(readyResource.Category, tagIDs)
if err != nil {
@@ -481,12 +518,16 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes
return err
}
// 插入 resource_tags 关联
if len(tagIDs) > 0 {
for _, tagID := range tagIDs {
err := s.resourceRepo.CreateResourceTag(resource.ID, tagID)
if err != nil {
Error("插入资源标签关联失败: %v", err)
}
}
} else {
Debug("没有标签,跳过插入资源标签关联")
}
return nil
}
@@ -889,44 +930,80 @@ func splitTags(tagStr string) []string {
// 处理标签返回所有标签ID
func (s *Scheduler) handleTags(tagStr string) ([]uint, error) {
if tagStr == "" {
return nil, nil
Debug("标签字符串为空,返回空数组")
return []uint{}, nil // 返回空数组而不是 nil
}
Debug("开始处理标签字符串: %s", tagStr)
tagNames := splitTags(tagStr)
Debug("分割后的标签名称: %v", tagNames)
var tagIDs []uint
for _, name := range tagNames {
name = strings.TrimSpace(name)
if name == "" {
Debug("跳过空标签名称")
continue
}
Debug("查找标签: %s", name)
tag, err := s.tagRepo.FindByName(name)
if err != nil || tag == nil {
if err != nil {
Debug("标签 %s 不存在,创建新标签", name)
// 不存在则新建
tag = &entity.Tag{Name: name}
err = s.tagRepo.Create(tag)
if err != nil {
return nil, err
Error("创建标签 %s 失败: %v", name, err)
return nil, fmt.Errorf("创建标签 %s 失败: %v", name, err)
}
Debug("成功创建标签: %s (ID: %d)", name, tag.ID)
} else {
Debug("找到已存在的标签: %s (ID: %d)", name, tag.ID)
}
tagIDs = append(tagIDs, tag.ID)
}
Debug("处理完成标签ID列表: %v", tagIDs)
return tagIDs, nil
}
// 分类处理逻辑
func (s *Scheduler) resolveCategory(categoryName string, tagIDs []uint) (*uint, error) {
Debug("开始处理分类,分类名称: %s, 标签ID列表: %v", categoryName, tagIDs)
if categoryName != "" {
Debug("查找分类: %s", categoryName)
cat, err := s.categoryRepo.FindByName(categoryName)
if err == nil && cat != nil {
if err != nil {
Debug("分类 %s 不存在: %v", categoryName, err)
} else if cat != nil {
Debug("找到分类: %s (ID: %d)", categoryName, cat.ID)
return &cat.ID, nil
}
}
// 没有分类,尝试用标签反查
if len(tagIDs) == 0 {
Debug("没有标签,无法通过标签反查分类")
return nil, nil
}
Debug("尝试通过标签反查分类")
for _, tagID := range tagIDs {
Debug("查找标签ID: %d", tagID)
tag, err := s.tagRepo.GetByID(tagID)
if err == nil && tag != nil && tag.CategoryID != nil {
if err != nil {
Debug("查找标签ID %d 失败: %v", tagID, err)
continue
}
if tag != nil && tag.CategoryID != nil {
Debug("通过标签 %s (ID: %d) 找到分类ID: %d", tag.Name, tagID, *tag.CategoryID)
return tag.CategoryID, nil
}
}
Debug("未找到分类返回nil")
return nil, nil
}

View File

@@ -39,7 +39,7 @@
required
></textarea>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
支持百度网盘阿里云盘夸克网盘等链接
支持百度网盘阿里云盘夸克网盘等链接每行一个链接
</p>
</div>
@@ -205,7 +205,7 @@ const clearForm = () => {
newTag.value = ''
}
// 单个添加提交
// 单个添加提交 - 更新为使用批量添加方法
const handleSubmit = async () => {
loading.value = true
try {
@@ -214,23 +214,23 @@ const handleSubmit = async () => {
// 多行链接处理
const urls = form.value.url.split(/\r?\n/).map(l => l.trim()).filter(Boolean)
// 为每个URL创建一个资源
for (const url of urls) {
// 使用批量添加方法,将多个URL作为一个资源的多个链接
const resourceData = {
title: form.value.title, // 标题必填
description: form.value.description || undefined, // 添加描述
url: url,
resources: [{
title: form.value.title || undefined, // 后端期望 *string 类型
description: form.value.description || '',
url: urls, // 现在 url 是一个数组
category: form.value.category || '',
tags: form.value.tags.join(','), // 转换为逗号分隔的字符串
img: form.value.img || '',
source: form.value.source || '手动添加',
extra: form.value.extra || '',
}]
}
await readyResourceApi.createReadyResource(resourceData)
}
const response = await readyResourceApi.batchCreateReadyResources(resourceData)
emit('success', `成功添加 ${urls.length}资源到待处理列表`)
emit('success', `成功添加资源,包含 ${urls.length}链接`)
clearForm()
} catch (e: any) {
emit('error', e.message || '添加失败')

View File

@@ -18,6 +18,11 @@ export const parseApiResponse = <T>(response: any): T => {
}
}
// 检查是否是包含items字段的响应格式如分类接口
if (response && typeof response === 'object' && 'items' in response) {
return response
}
// 检查是否是包含success字段的响应格式如登录接口
if (response && typeof response === 'object' && 'success' in response && 'data' in response) {
if (response.success) {
@@ -66,7 +71,7 @@ export const useAuthApi = () => {
}
export const useCategoryApi = () => {
const getCategories = () => useApiFetch('/categories').then(parseApiResponse)
const getCategories = (params?: any) => useApiFetch('/categories', { params }).then(parseApiResponse)
const createCategory = (data: any) => useApiFetch('/categories', { method: 'POST', body: data }).then(parseApiResponse)
const updateCategory = (id: number, data: any) => useApiFetch(`/categories/${id}`, { method: 'PUT', body: data }).then(parseApiResponse)
const deleteCategory = (id: number) => useApiFetch(`/categories/${id}`, { method: 'DELETE' }).then(parseApiResponse)
@@ -93,7 +98,7 @@ export const useCksApi = () => {
}
export const useTagApi = () => {
const getTags = () => useApiFetch('/tags').then(parseApiResponse)
const getTags = (params?: any) => useApiFetch('/tags', { params }).then(parseApiResponse)
const getTagsByCategory = (categoryId: number, params?: any) => useApiFetch(`/categories/${categoryId}/tags`, { params }).then(parseApiResponse)
const getTag = (id: number) => useApiFetch(`/tags/${id}`).then(parseApiResponse)
const createTag = (data: any) => useApiFetch('/tags', { method: 'POST', body: data }).then(parseApiResponse)

View File

@@ -260,12 +260,34 @@ const fetchCategories = async () => {
page_size: pageSize.value,
search: searchQuery.value
}
console.log('获取分类列表参数:', params)
const response = await categoryApi.getCategories(params)
categories.value = Array.isArray(response) ? response : []
console.log('分类接口响应:', response)
// 适配后端API响应格式
if (response && response.items) {
console.log('使用 items 格式:', response.items)
categories.value = response.items
totalCount.value = response.total || 0
totalPages.value = Math.ceil(totalCount.value / pageSize.value)
} else if (Array.isArray(response)) {
console.log('使用数组格式:', response)
// 兼容旧格式
categories.value = response
totalCount.value = response.length
totalPages.value = 1
} else {
console.log('使用默认格式:', response)
categories.value = []
totalCount.value = 0
totalPages.value = 1
}
console.log('最终分类数据:', categories.value)
} catch (error) {
console.error('获取分类列表失败:', error)
categories.value = []
totalCount.value = 0
totalPages.value = 1
} finally {
loading.value = false
}

View File

@@ -267,6 +267,9 @@
</template>
<script setup lang="ts">
// API 导入
import { useTagApi, useCategoryApi } from '~/composables/useApi'
// 设置页面布局
definePageMeta({
layout: 'admin',
@@ -277,6 +280,13 @@ const router = useRouter()
const userStore = useUserStore()
const config = useRuntimeConfig()
const tagApi = useTagApi()
const categoryApi = useCategoryApi()
// 调试信息
console.log('tagApi:', tagApi)
console.log('categoryApi:', categoryApi)
// 页面状态
const pageLoading = ref(true)
const loading = ref(false)
@@ -332,10 +342,25 @@ const checkAuth = () => {
// 获取分类列表
const fetchCategories = async () => {
try {
console.log('获取分类列表...')
const response = await categoryApi.getCategories()
categories.value = Array.isArray(response) ? response : []
console.log('分类接口响应:', response)
// 适配后端API响应格式
if (response && response.items) {
console.log('使用 items 格式:', response.items)
categories.value = response.items
} else if (Array.isArray(response)) {
console.log('使用数组格式:', response)
categories.value = response
} else {
console.log('使用默认格式:', response)
categories.value = []
}
console.log('最终分类数据:', categories.value)
} catch (error) {
console.error('获取分类列表失败:', error)
categories.value = []
}
}
@@ -348,17 +373,39 @@ const fetchTags = async () => {
page_size: pageSize.value,
search: searchQuery.value
}
console.log('获取标签列表参数:', params)
let response: any
if (selectedCategory.value) {
response = await tagApi.getTagsByCategory(selectedCategory.value, params)
response = await tagApi.getTagsByCategory(parseInt(selectedCategory.value), params)
} else {
response = await tagApi.getTags(params)
}
tags.value = response.items || []
console.log('标签接口响应:', response)
// 适配后端API响应格式
if (response && response.items) {
console.log('使用 items 格式:', response.items)
tags.value = response.items
totalCount.value = response.total || 0
totalPages.value = Math.ceil(totalCount.value / pageSize.value)
} else if (Array.isArray(response)) {
console.log('使用数组格式:', response)
tags.value = response
totalCount.value = response.length
totalPages.value = 1
} else {
console.log('使用默认格式:', response)
tags.value = []
totalCount.value = 0
totalPages.value = 1
}
console.log('最终标签数据:', tags.value)
} catch (error) {
console.error('获取标签列表失败:', error)
tags.value = []
totalCount.value = 0
totalPages.value = 1
} finally {
loading.value = false
}
@@ -481,15 +528,25 @@ const handleLogout = () => {
// 页面加载
onMounted(async () => {
try {
console.log('页面开始加载...')
checkAuth()
console.log('认证检查完成')
console.log('开始获取分类列表...')
await fetchCategories()
console.log('分类列表获取完成')
console.log('开始获取标签列表...')
await fetchTags()
console.log('标签列表获取完成')
// 检查URL参数如果action=add则自动打开新增弹窗
const route = useRoute()
if (route.query.action === 'add') {
showAddModal.value = true
}
console.log('页面加载完成')
} catch (error) {
console.error('标签管理页面初始化失败:', error)
} finally {

View File

@@ -5,12 +5,8 @@
<div class="text-center">
<h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">管理员登录</h1>
<p class="mt-2 text-sm text-gray-600">请输入管理员账号密码</p>
<div class="mt-3 p-3 bg-blue-50 rounded-lg">
<p class="text-xs text-blue-700">
<i class="fas fa-info-circle mr-1"></i>
默认管理员账户admin / password
</p>
</div>
<!-- <div class="mt-3 p-3 bg-blue-50 rounded-lg">
</div> -->
</div>
<form @submit.prevent="handleLogin" class="space-y-4">