fix: 修复Ant Design bodyStyle废弃警告和合集拖拽排序功能

- 将所有bodyStyle替换为styles.body,消除Ant Design废弃警告
- 修复合集拖拽排序功能,解决'Collection not found'错误
- 优化前端状态管理,确保currentProject和projects数组同步
- 添加调试脚本和文档,便于问题排查
- 修复数据库clip_ids映射问题,支持UUID和数字ID转换
This commit is contained in:
Kris Ka
2025-08-26 06:45:19 +08:00
parent aeb98ab2b9
commit ffaf91a1f8
17 changed files with 1159 additions and 91 deletions
+17 -4
View File
@@ -391,6 +391,15 @@ class CollectionService(BaseService[Collection, CollectionCreate, CollectionUpda
实际的clip UUID列表
"""
try:
# 检查是否已经是UUID格式(UUID是36个字符,包含4个连字符)
def is_uuid_format(clip_id: str) -> bool:
return len(clip_id) == 36 and clip_id.count('-') == 4
# 如果所有ID都已经是UUID格式,直接返回
if all(is_uuid_format(clip_id) for clip_id in numeric_clip_ids):
logger.info("所有clip_ids已经是UUID格式,跳过映射")
return numeric_clip_ids
# 获取项目中的所有clips,按创建时间排序
clips = self.db.query(Clip).filter(Clip.project_id == project_id).order_by(Clip.created_at).all()
@@ -401,11 +410,15 @@ class CollectionService(BaseService[Collection, CollectionCreate, CollectionUpda
# 映射clip_ids
mapped_clip_ids = []
for numeric_id in numeric_clip_ids:
if numeric_id in id_mapping:
mapped_clip_ids.append(id_mapping[numeric_id])
for clip_id in numeric_clip_ids:
if is_uuid_format(clip_id):
# 如果已经是UUID格式,直接使用
mapped_clip_ids.append(clip_id)
elif clip_id in id_mapping:
# 如果是数字ID,进行映射
mapped_clip_ids.append(id_mapping[clip_id])
else:
logger.warning(f"无法找到数字ID {numeric_id} 对应的clip")
logger.warning(f"无法找到数字ID {clip_id} 对应的clip")
return mapped_clip_ids
+173
View File
@@ -0,0 +1,173 @@
# 前端拖拽排序调试指南
## 问题确认
通过后端诊断,我们确认了以下几点:
- ✅ 后端API完全正常工作
- ✅ 所有API端点响应正确
- ✅ 数据库数据正确
- ❌ 前端没有发出API请求(后端日志中无记录)
## 问题定位
**问题出现在前端!** 前端的拖拽排序功能没有正确触发API调用。
## 前端调试步骤
### 1. 打开浏览器开发者工具
1. 在浏览器中打开项目详情页面
2. 按 `F12` 或右键 → "检查元素" 打开开发者工具
3. 切换到 **Network** 标签页
4. 确保记录网络请求(红色录制按钮应该是激活状态)
### 2. 检查拖拽功能是否触发
1. 尝试拖拽合集中的片段
2. 观察 **Network** 标签页是否有新的API请求出现
3. 如果没有API请求,说明拖拽事件没有正确处理
### 3. 检查JavaScript错误
1. 切换到 **Console** 标签页
2. 尝试拖拽排序操作
3. 查看是否有红色的错误信息
4. 记录错误信息以便进一步诊断
### 4. 检查组件是否正确渲染
1. 在 **Elements** 标签页中检查合集组件
2. 确认拖拽相关的事件处理器是否正确绑定
3. 查看组件的props和state是否正确
## 可能的问题原因
### 1. 拖拽库问题
**症状**: 拖拽操作无效果,没有视觉反馈
**检查**:
- 确认是否使用了拖拽库(如react-dnd, @dnd-kit等)
- 检查拖拽库的版本兼容性
- 查看拖拽库的配置是否正确
### 2. 事件处理器未绑定
**症状**: 可以拖拽但没有触发回调
**检查**:
- 检查 `onReorderClips` 等回调函数是否正确传递
- 确认组件的props是否正确接收
### 3. 状态管理问题
**症状**: 拖拽后状态没有更新
**检查**:
- 检查store中的 `reorderCollectionClips` 方法是否被调用
- 在方法开头添加 `console.log` 确认执行
### 4. API调用被拦截
**症状**: 前端代码执行但API请求没发出
**检查**:
- 检查axios配置
- 查看请求拦截器是否有问题
- 确认网络连接正常
## 具体调试代码
### 在CollectionPreviewModal.tsx中添加调试日志
```typescript
const handleReorderClips = async (newClipIds: string[]) => {
console.log('🔄 handleReorderClips called with:', newClipIds)
try {
console.log('📤 Calling onReorderClips...')
await onReorderClips?.(collection.id, newClipIds)
console.log('✅ onReorderClips completed successfully')
message.success('合集顺序已更新')
} catch (error) {
console.error('❌ onReorderClips failed:', error)
message.error('更新合集顺序失败')
}
}
```
### 在useProjectStore.ts中添加调试日志
```typescript
reorderCollectionClips: async (projectId: string, collectionId: string, newClipIds: string[]) => {
console.log('🎯 reorderCollectionClips called:', { projectId, collectionId, newClipIds })
// ... 现有代码 ...
try {
console.log('📤 Calling projectApi.reorderCollectionClips...')
await projectApi.reorderCollectionClips(projectId, collectionId, newClipIds)
console.log('✅ API call successful')
} catch (error) {
console.error('❌ API call failed:', error)
// ... 错误处理 ...
}
}
```
### 在api.ts中添加调试日志
```typescript
reorderCollectionClips: (projectId: string, collectionId: string, clipIds: string[]): Promise<Collection> => {
console.log('🌐 API call: reorderCollectionClips', { projectId, collectionId, clipIds })
const url = `/projects/${projectId}/collections/${collectionId}/reorder`
console.log('📡 Request URL:', url)
console.log('📦 Request data:', clipIds)
return api.patch(url, clipIds)
}
```
## 快速测试方法
在浏览器控制台中直接测试API调用:
```javascript
// 1. 测试store方法
window.useProjectStore.getState().reorderCollectionClips(
'86f9aa12-2f35-4618-b265-74b3d9a4cf2d',
'5e5dafc8-f29a-4705-8e87-b2bb06f2a5de',
['3d0bb0b6-dd8d-4105-9219-b1bce74c7b4a', '678a8c4b-16ac-4893-a8d9-1b28c3bb4c81']
)
// 2. 直接测试API调用
fetch('http://localhost:8000/api/v1/projects/86f9aa12-2f35-4618-b265-74b3d9a4cf2d/collections/5e5dafc8-f29a-4705-8e87-b2bb06f2a5de/reorder', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(['678a8c4b-16ac-4893-a8d9-1b28c3bb4c81', '3d0bb0b6-dd8d-4105-9219-b1bce74c7b4a'])
}).then(r => r.json()).then(console.log)
```
## 预期结果
如果一切正常,你应该看到:
1. **Network标签页**: 出现对 `/projects/.../collections/.../reorder` 的PATCH请求
2. **Console标签页**: 看到相关的调试日志输出
3. **Response**: 收到 `{"message": "Collection clips reordered successfully", "clip_ids": [...]}`
4. **UI更新**: 合集中片段的顺序立即更新
## 下一步行动
1. 按照上述步骤进行调试
2. 记录发现的错误信息
3. 根据错误信息定位具体问题
4. 修复前端代码中的问题
## 联系支持
如果按照此指南仍无法解决问题,请提供:
- 浏览器控制台的错误信息
- Network标签页的请求记录截图
- 具体的操作步骤描述
+5 -1
View File
@@ -157,7 +157,11 @@ const ClipCard: React.FC<ClipCardProps> = ({
overflow: 'hidden',
cursor: 'pointer'
}}
bodyStyle={{ padding: 0 }}
styles={{
body: {
padding: 0,
},
}}
cover={
<div
style={{
+6 -4
View File
@@ -80,10 +80,12 @@ const ClipDetailModal: React.FC<ClipDetailModalProps> = ({
centered
destroyOnClose
style={{ top: 20 }}
bodyStyle={{
padding: 0,
background: 'rgba(26, 26, 46, 0.95)',
borderRadius: '12px'
styles={{
body: {
padding: 0,
background: 'rgba(26, 26, 46, 0.95)',
borderRadius: '12px'
}
}}
>
<div style={{ padding: '24px' }}>
@@ -51,9 +51,11 @@ const CollectionCardMini: React.FC<CollectionCardMiniProps> = ({
background: 'linear-gradient(135deg, #1f1f1f 0%, #2a2a2a 100%)',
position: 'relative'
}}
bodyStyle={{
padding: '16px',
height: '100%'
styles={{
body: {
padding: '16px',
height: '100%'
}
}}
onClick={() => onView(collection)}
onMouseEnter={() => setIsHovered(true)}
@@ -228,7 +228,7 @@ const CollectionPreviewModal: React.FC<CollectionPreviewModalProps> = ({
footer={null}
width="90vw"
style={{ top: 20 }}
bodyStyle={{ padding: 0, height: '90vh' }}
styles={{ body: { padding: 0, height: '90vh' } }}
className="collection-preview-modal"
closable={false}
maskClosable={false}
@@ -258,7 +258,7 @@ const CollectionPreviewModal: React.FC<CollectionPreviewModalProps> = ({
footer={null}
width="90vw"
style={{ top: 20 }}
bodyStyle={{ padding: 0, height: '90vh' }}
styles={{ body: { padding: 0, height: '90vh' } }}
className="collection-preview-modal"
closable={false}
maskClosable={false}
+8 -6
View File
@@ -266,12 +266,14 @@ const ProjectCard: React.FC<ProjectCardProps> = ({ project, onDelete, onRetry, o
e.currentTarget.style.transform = 'translateY(0)'
e.currentTarget.style.boxShadow = '0 4px 20px rgba(0, 0, 0, 0.3)'
}}
bodyStyle={{
padding: '12px',
background: 'transparent',
height: 'calc(100% - 120px)',
display: 'flex',
flexDirection: 'column'
styles={{
body: {
padding: '12px',
background: 'transparent',
height: 'calc(100% - 120px)',
display: 'flex',
flexDirection: 'column'
}
}}
cover={
<div
+7
View File
@@ -85,6 +85,13 @@ const ProjectDetailPage: React.FC = () => {
console.log('🎯 Final project with data:', projectWithData)
setCurrentProject(projectWithData)
// 同时更新projects数组,确保Store中的数据同步
const { projects } = useProjectStore.getState()
const updatedProjects = projects.map(p =>
p.id === id ? projectWithData : p
)
useProjectStore.setState({ projects: updatedProjects })
} catch (error) {
console.error('Failed to load clips/collections:', error)
// 即使clips/collections加载失败,也设置项目基本信息
+38 -4
View File
@@ -277,10 +277,33 @@ export const useProjectStore = create<ProjectStore>((set, get) => ({
// 获取原始状态
const state = get()
const originalProject = state.projects.find(p => p.id === projectId)
const originalCollection = originalProject?.collections?.find(c => c.id === collectionId)
console.log('Current state projects:', state.projects.map(p => ({ id: p.id, collectionsCount: p.collections?.length || 0 })))
console.log('Current state currentProject:', state.currentProject ? { id: state.currentProject.id, collectionsCount: state.currentProject.collections?.length || 0 } : null)
// 优先从currentProject中查找,如果找不到再从projects数组中查找
let originalProject = state.currentProject?.id === projectId ? state.currentProject : null
let originalCollection = originalProject?.collections?.find(c => c.id === collectionId)
// 如果currentProject中没有找到,尝试从projects数组中查找
if (!originalCollection) {
const projectFromArray = state.projects.find(p => p.id === projectId)
if (projectFromArray) {
originalProject = projectFromArray
originalCollection = originalProject.collections?.find(c => c.id === collectionId)
}
}
console.log('Found project:', originalProject ? { id: originalProject.id, collectionsCount: originalProject.collections?.length || 0 } : null)
if (originalProject?.collections) {
console.log('Project collections:', originalProject.collections.map(c => ({ id: c.id, title: c.collection_title })))
}
console.log('Found collection:', originalCollection ? { id: originalCollection.id, title: originalCollection.collection_title } : null)
if (!originalCollection) {
console.error('Collection not found in store. Available collections:',
originalProject?.collections?.map(c => c.id) || [])
throw new Error('Collection not found')
}
@@ -345,8 +368,19 @@ export const useProjectStore = create<ProjectStore>((set, get) => ({
// 获取原始状态
const state = get()
const originalProject = state.projects.find(p => p.id === projectId)
const originalCollection = originalProject?.collections?.find(c => c.id === collectionId)
// 优先从currentProject中查找,如果找不到再从projects数组中查找
let originalProject = state.currentProject?.id === projectId ? state.currentProject : null
let originalCollection = originalProject?.collections?.find(c => c.id === collectionId)
// 如果currentProject中没有找到,尝试从projects数组中查找
if (!originalCollection) {
const projectFromArray = state.projects.find(p => p.id === projectId)
if (projectFromArray) {
originalProject = projectFromArray
originalCollection = originalProject.collections?.find(c => c.id === collectionId)
}
}
if (!originalCollection) {
throw new Error('Collection not found')
+126
View File
@@ -0,0 +1,126 @@
#!/usr/bin/env python3
"""
专门诊断前端拖拽排序问题
"""
import requests
import json
import time
def debug_frontend_issue():
"""诊断前端拖拽排序问题"""
# 项目ID和合集ID
project_id = "86f9aa12-2f35-4618-b265-74b3d9a4cf2d"
collection_id = "5e5dafc8-f29a-4705-8e87-b2bb06f2a5de"
print("🔍 专门诊断前端拖拽排序问题")
print("=" * 60)
# 1. 检查后端服务状态
print("1️⃣ 检查后端服务状态...")
try:
response = requests.get("http://localhost:8000/api/v1/")
print(f"✅ 后端服务正常运行")
except Exception as e:
print(f"❌ 后端服务异常: {e}")
return
# 2. 检查当前数据状态
print("\n2️⃣ 检查当前数据状态...")
try:
response = requests.get(f"http://localhost:8000/api/v1/collections/?project_id={project_id}")
if response.status_code == 200:
collections = response.json()['items']
target_collection = next((c for c in collections if c['id'] == collection_id), None)
if target_collection:
current_clip_ids = target_collection['clip_ids']
print(f"✅ 目标合集: {target_collection['name']}")
print(f"📋 当前clip_ids: {current_clip_ids}")
print(f"📊 片段数量: {len(current_clip_ids)}")
else:
print("❌ 未找到目标合集")
return
else:
print(f"❌ 获取collections失败: {response.status_code}")
return
except Exception as e:
print(f"❌ 获取数据异常: {e}")
return
# 3. 监控拖拽排序调用
print("\n3️⃣ 监控拖拽排序调用...")
print("🔔 请在前端进行拖拽排序操作,然后按回车键继续...")
input()
# 检查最近的日志
print("📋 检查后端日志中的拖拽排序记录...")
try:
with open('/Users/zhoukk/autoclip/backend.log', 'r') as f:
lines = f.readlines()
recent_lines = lines[-50:] # 获取最近50行
reorder_logs = []
for line in recent_lines:
if 'reorder' in line.lower() or '排序' in line:
reorder_logs.append(line.strip())
if reorder_logs:
print("📋 找到相关日志:")
for log in reorder_logs[-10:]: # 显示最近10条
print(f" {log}")
else:
print("❌ 未找到拖拽排序相关的日志")
except Exception as e:
print(f"❌ 读取日志失败: {e}")
# 4. 再次检查数据状态
print("\n4️⃣ 再次检查数据状态...")
try:
response = requests.get(f"http://localhost:8000/api/v1/collections/?project_id={project_id}")
if response.status_code == 200:
collections = response.json()['items']
target_collection = next((c for c in collections if c['id'] == collection_id), None)
if target_collection:
updated_clip_ids = target_collection['clip_ids']
print(f"📋 更新后的clip_ids: {updated_clip_ids}")
if updated_clip_ids != current_clip_ids:
print("✅ 数据已更新!")
else:
print("❌ 数据未更新")
else:
print("❌ 未找到目标合集")
else:
print(f"❌ 获取collections失败: {response.status_code}")
except Exception as e:
print(f"❌ 获取数据异常: {e}")
# 5. 提供调试建议
print("\n5️⃣ 调试建议...")
print("📋 如果拖拽排序仍然失败,请检查以下几点:")
print(" 1. 打开浏览器开发者工具的Network标签")
print(" 2. 进行拖拽排序操作")
print(" 3. 查看是否有API调用发出")
print(" 4. 检查API调用的状态码和响应")
print(" 5. 查看Console标签是否有JavaScript错误")
print("\n6️⃣ 常见问题排查:")
print(" ❓ 问题1: 前端没有发出API请求")
print(" 💡 解决: 检查前端代码中的事件处理是否正确绑定")
print()
print(" ❓ 问题2: API请求被拦截或失败")
print(" 💡 解决: 检查CORS设置,确认后端服务可访问")
print()
print(" ❓ 问题3: 前端状态更新失败")
print(" 💡 解决: 检查store的状态管理逻辑")
print()
print(" ❓ 问题4: 错误处理逻辑问题")
print(" 💡 解决: 检查try-catch块和错误消息显示")
print("\n" + "=" * 60)
print("🎯 诊断完成!")
if __name__ == "__main__":
debug_frontend_issue()
+179
View File
@@ -0,0 +1,179 @@
#!/usr/bin/env python3
"""
调试前端拖拽排序问题
"""
import requests
import json
def debug_frontend_reorder():
"""调试前端拖拽排序问题"""
# 项目ID和合集ID
project_id = "86f9aa12-2f35-4618-b265-74b3d9a4cf2d"
collection_id = "5e5dafc8-f29a-4705-8e87-b2bb06f2a5de"
print("🔍 调试前端拖拽排序问题")
print("=" * 50)
# 1. 检查后端API是否正常工作
print("1️⃣ 检查后端API...")
try:
response = requests.get(f"http://localhost:8000/api/v1/collections/?project_id={project_id}")
if response.status_code == 200:
collections = response.json()['items']
target_collection = next((c for c in collections if c['id'] == collection_id), None)
if target_collection:
initial_clip_ids = target_collection['clip_ids']
print(f"✅ 后端API正常,合集: {target_collection['name']}")
print(f"📋 当前clip_ids: {initial_clip_ids}")
else:
print("❌ 未找到目标合集")
return
else:
print(f"❌ 后端API异常: {response.status_code}")
return
except Exception as e:
print(f"❌ 后端API异常: {e}")
return
# 2. 测试不同的API调用方式
print("\n2️⃣ 测试不同的API调用方式...")
# 方式1: 使用正确的API端点
print("\n🔄 方式1: PATCH /projects/{project_id}/collections/{collection_id}/reorder")
new_clip_ids_1 = [initial_clip_ids[1], initial_clip_ids[0]] + initial_clip_ids[2:]
try:
response = requests.patch(
f"http://localhost:8000/api/v1/projects/{project_id}/collections/{collection_id}/reorder",
json=new_clip_ids_1,
headers={"Content-Type": "application/json"}
)
print(f"📥 响应状态: {response.status_code}")
if response.status_code == 200:
result = response.json()
print(f"✅ 方式1成功: {result.get('clip_ids', [])}")
else:
print(f"❌ 方式1失败: {response.text}")
except Exception as e:
print(f"❌ 方式1异常: {e}")
# 方式2: 使用错误的API端点(前端可能使用的)
print("\n🔄 方式2: PATCH /collections/{collection_id}/reorder")
new_clip_ids_2 = [initial_clip_ids[0], initial_clip_ids[1]] + initial_clip_ids[2:]
try:
response = requests.patch(
f"http://localhost:8000/api/v1/collections/{collection_id}/reorder",
json=new_clip_ids_2,
headers={"Content-Type": "application/json"}
)
print(f"📥 响应状态: {response.status_code}")
if response.status_code == 200:
result = response.json()
print(f"✅ 方式2成功: {result.get('clip_ids', [])}")
else:
print(f"❌ 方式2失败: {response.text}")
except Exception as e:
print(f"❌ 方式2异常: {e}")
# 方式3: 使用PUT方式更新metadata
print("\n🔄 方式3: PUT /collections/{collection_id} (metadata格式)")
new_clip_ids_3 = [initial_clip_ids[1], initial_clip_ids[0]] + initial_clip_ids[2:]
try:
update_data = {
"metadata": {
"clip_ids": new_clip_ids_3
}
}
response = requests.put(
f"http://localhost:8000/api/v1/collections/{collection_id}",
json=update_data,
headers={"Content-Type": "application/json"}
)
print(f"📥 响应状态: {response.status_code}")
if response.status_code == 200:
result = response.json()
print(f"✅ 方式3成功: {result.get('clip_ids', [])}")
else:
print(f"❌ 方式3失败: {response.text}")
except Exception as e:
print(f"❌ 方式3异常: {e}")
# 3. 检查前端可能的问题
print("\n3️⃣ 检查前端可能的问题...")
# 检查网络连接
print("\n🌐 检查网络连接...")
try:
response = requests.get("http://localhost:8000/api/v1/")
print(f"✅ 网络连接正常: {response.status_code}")
except Exception as e:
print(f"❌ 网络连接异常: {e}")
# 检查CORS
print("\n🔒 检查CORS...")
try:
response = requests.options(
f"http://localhost:8000/api/v1/projects/{project_id}/collections/{collection_id}/reorder",
headers={"Origin": "http://localhost:3000"}
)
print(f"✅ CORS检查: {response.status_code}")
print(f"📋 CORS头: {dict(response.headers)}")
except Exception as e:
print(f"❌ CORS检查异常: {e}")
# 4. 模拟前端的完整错误流程
print("\n4️⃣ 模拟前端的完整错误流程...")
# 模拟前端可能遇到的错误
print("\n🔄 模拟错误1: 无效的collection_id")
try:
response = requests.patch(
f"http://localhost:8000/api/v1/projects/{project_id}/collections/invalid-id/reorder",
json=initial_clip_ids,
headers={"Content-Type": "application/json"}
)
print(f"📥 错误1响应: {response.status_code}")
if response.status_code == 404:
print("✅ 错误处理正常")
else:
print(f"❌ 错误处理异常: {response.text}")
except Exception as e:
print(f"❌ 错误1异常: {e}")
print("\n🔄 模拟错误2: 无效的请求体")
try:
response = requests.patch(
f"http://localhost:8000/api/v1/projects/{project_id}/collections/{collection_id}/reorder",
json={"invalid": "data"},
headers={"Content-Type": "application/json"}
)
print(f"📥 错误2响应: {response.status_code}")
print(f"📋 错误2内容: {response.text}")
except Exception as e:
print(f"❌ 错误2异常: {e}")
print("\n🔄 模拟错误3: 空的clip_ids")
try:
response = requests.patch(
f"http://localhost:8000/api/v1/projects/{project_id}/collections/{collection_id}/reorder",
json=[],
headers={"Content-Type": "application/json"}
)
print(f"📥 错误3响应: {response.status_code}")
print(f"📋 错误3内容: {response.text}")
except Exception as e:
print(f"❌ 错误3异常: {e}")
print("\n" + "=" * 50)
print("🎯 调试完成!")
print("\n📋 可能的问题:")
print(" 1. 前端使用了错误的API端点")
print(" 2. 前端发送了错误的请求格式")
print(" 3. 网络连接问题")
print(" 4. CORS问题")
print(" 5. 前端错误处理逻辑问题")
if __name__ == "__main__":
debug_frontend_reorder()
+1
View File
@@ -157,3 +157,4 @@ if __name__ == "__main__":
print("\n🎉 测试完成!")
+164
View File
@@ -0,0 +1,164 @@
#!/usr/bin/env python3
"""
测试完整的前端拖拽排序流程
"""
import requests
import json
def test_complete_frontend_flow():
"""测试完整的前端拖拽排序流程"""
# 项目ID和合集ID
project_id = "86f9aa12-2f35-4618-b265-74b3d9a4cf2d"
collection_id = "5e5dafc8-f29a-4705-8e87-b2bb06f2a5de"
print("🎯 测试完整的前端拖拽排序流程")
print("=" * 60)
# 1. 模拟前端获取项目数据
print("1️⃣ 模拟前端获取项目数据...")
try:
# 获取项目信息
response = requests.get(f"http://localhost:8000/api/v1/projects/{project_id}")
if response.status_code == 200:
project = response.json()
print(f"✅ 项目: {project['name']}")
print(f"📊 状态: {project['status']}")
else:
print(f"❌ 获取项目失败: {response.status_code}")
return
except Exception as e:
print(f"❌ 获取项目异常: {e}")
return
# 2. 模拟前端获取clips数据
print("\n2️⃣ 模拟前端获取clips数据...")
try:
response = requests.get(f"http://localhost:8000/api/v1/clips/?project_id={project_id}")
if response.status_code == 200:
clips = response.json()['items']
print(f"✅ 获取到 {len(clips)} 个clips")
for i, clip in enumerate(clips[:3]): # 只显示前3个
print(f" {i+1}. {clip['title'][:30]}...")
else:
print(f"❌ 获取clips失败: {response.status_code}")
return
except Exception as e:
print(f"❌ 获取clips异常: {e}")
return
# 3. 模拟前端获取collections数据
print("\n3️⃣ 模拟前端获取collections数据...")
try:
response = requests.get(f"http://localhost:8000/api/v1/collections/?project_id={project_id}")
if response.status_code == 200:
collections = response.json()['items']
print(f"✅ 获取到 {len(collections)} 个collections")
target_collection = None
for collection in collections:
print(f" 📚 {collection['name']}: {len(collection['clip_ids'])} 个片段")
if collection['id'] == collection_id:
target_collection = collection
if not target_collection:
print("❌ 未找到目标合集")
return
initial_clip_ids = target_collection['clip_ids']
print(f"🎯 目标合集: {target_collection['name']}")
print(f"📋 初始clip_ids: {initial_clip_ids}")
else:
print(f"❌ 获取collections失败: {response.status_code}")
return
except Exception as e:
print(f"❌ 获取collections异常: {e}")
return
# 4. 模拟前端拖拽排序操作
print("\n4️⃣ 模拟前端拖拽排序操作...")
if len(initial_clip_ids) >= 2:
# 模拟拖拽:将第一个元素拖到第二个位置
new_clip_ids = [initial_clip_ids[1], initial_clip_ids[0]] + initial_clip_ids[2:]
print(f"🔄 拖拽操作: 将第1个元素移到第2个位置")
print(f"📋 新顺序: {new_clip_ids}")
try:
# 模拟前端的API调用
print("📤 发送API请求...")
response = requests.patch(
f"http://localhost:8000/api/v1/projects/{project_id}/collections/{collection_id}/reorder",
json=new_clip_ids,
headers={"Content-Type": "application/json"}
)
print(f"📥 响应状态: {response.status_code}")
if response.status_code == 200:
result = response.json()
print(f"✅ API调用成功")
print(f"📝 响应消息: {result.get('message', '')}")
print(f"📋 返回的clip_ids: {result.get('clip_ids', [])}")
else:
print(f"❌ API调用失败: {response.text}")
return
except Exception as e:
print(f"❌ API调用异常: {e}")
return
else:
print("❌ 片段数量不足,无法测试拖拽")
return
# 5. 模拟前端验证更新结果
print("\n5️⃣ 模拟前端验证更新结果...")
try:
response = requests.get(f"http://localhost:8000/api/v1/collections/?project_id={project_id}")
if response.status_code == 200:
collections = response.json()['items']
target_collection = next((c for c in collections if c['id'] == collection_id), None)
if target_collection:
updated_clip_ids = target_collection['clip_ids']
print(f"📋 验证结果: {updated_clip_ids}")
if updated_clip_ids == new_clip_ids:
print("✅ 拖拽排序成功!数据已正确更新")
else:
print("❌ 拖拽排序失败!数据未正确更新")
print(f" 期望: {new_clip_ids}")
print(f" 实际: {updated_clip_ids}")
else:
print("❌ 未找到目标合集")
else:
print(f"❌ 验证失败: {response.status_code}")
except Exception as e:
print(f"❌ 验证异常: {e}")
# 6. 模拟前端恢复原始状态
print("\n6️⃣ 模拟前端恢复原始状态...")
try:
response = requests.patch(
f"http://localhost:8000/api/v1/projects/{project_id}/collections/{collection_id}/reorder",
json=initial_clip_ids,
headers={"Content-Type": "application/json"}
)
if response.status_code == 200:
print("✅ 状态恢复成功")
else:
print(f"❌ 状态恢复失败: {response.text}")
except Exception as e:
print(f"❌ 状态恢复异常: {e}")
print("\n" + "=" * 60)
print("🎉 完整的前端拖拽排序流程测试完成!")
print("\n📋 测试总结:")
print(" ✅ 项目数据获取正常")
print(" ✅ Clips数据获取正常")
print(" ✅ Collections数据获取正常")
print(" ✅ 拖拽排序API调用正常")
print(" ✅ 数据更新验证正常")
print(" ✅ 状态恢复正常")
if __name__ == "__main__":
test_complete_frontend_flow()
+180
View File
@@ -0,0 +1,180 @@
#!/usr/bin/env python3
"""
模拟前端实际的拖拽排序调用
"""
import requests
import json
def test_frontend_actual_call():
"""模拟前端实际的拖拽排序调用"""
# 项目ID和合集ID
project_id = "86f9aa12-2f35-4618-b265-74b3d9a4cf2d"
collection_id = "5e5dafc8-f29a-4705-8e87-b2bb06f2a5de"
print("🎯 模拟前端实际的拖拽排序调用")
print("=" * 50)
# 1. 模拟前端获取初始数据
print("1️⃣ 模拟前端获取初始数据...")
try:
# 获取collections数据
response = requests.get(f"http://localhost:8000/api/v1/collections/?project_id={project_id}")
if response.status_code == 200:
collections = response.json()['items']
target_collection = next((c for c in collections if c['id'] == collection_id), None)
if target_collection:
initial_clip_ids = target_collection['clip_ids']
print(f"✅ 获取到合集: {target_collection['name']}")
print(f"📋 初始clip_ids: {initial_clip_ids}")
else:
print("❌ 未找到目标合集")
return
else:
print(f"❌ 获取collections失败: {response.status_code}")
return
except Exception as e:
print(f"❌ 获取数据异常: {e}")
return
# 2. 模拟前端拖拽操作(交换前两个元素)
print("\n2️⃣ 模拟前端拖拽操作...")
if len(initial_clip_ids) >= 2:
# 模拟拖拽:将第一个元素拖到第二个位置
new_clip_ids = [initial_clip_ids[1], initial_clip_ids[0]] + initial_clip_ids[2:]
print(f"🔄 拖拽操作: 将第1个元素移到第2个位置")
print(f"📋 新顺序: {new_clip_ids}")
# 3. 模拟前端store的reorderCollectionClips调用
print("\n3️⃣ 模拟前端store的reorderCollectionClips调用...")
print(f"📤 调用: projectApi.reorderCollectionClips('{project_id}', '{collection_id}', {new_clip_ids})")
try:
# 模拟前端的API调用
response = requests.patch(
f"http://localhost:8000/api/v1/projects/{project_id}/collections/{collection_id}/reorder",
json=new_clip_ids,
headers={
"Content-Type": "application/json",
"Accept": "application/json"
}
)
print(f"📥 响应状态: {response.status_code}")
print(f"📋 响应头: {dict(response.headers)}")
if response.status_code == 200:
result = response.json()
print(f"✅ API调用成功")
print(f"📝 响应消息: {result.get('message', '')}")
print(f"📋 返回的clip_ids: {result.get('clip_ids', [])}")
else:
print(f"❌ API调用失败")
print(f"📋 错误响应: {response.text}")
return
except Exception as e:
print(f"❌ API调用异常: {e}")
return
else:
print("❌ 片段数量不足,无法测试拖拽")
return
# 4. 模拟前端验证更新结果
print("\n4️⃣ 模拟前端验证更新结果...")
try:
response = requests.get(f"http://localhost:8000/api/v1/collections/?project_id={project_id}")
if response.status_code == 200:
collections = response.json()['items']
target_collection = next((c for c in collections if c['id'] == collection_id), None)
if target_collection:
updated_clip_ids = target_collection['clip_ids']
print(f"📋 验证结果: {updated_clip_ids}")
if updated_clip_ids == new_clip_ids:
print("✅ 拖拽排序成功!数据已正确更新")
else:
print("❌ 拖拽排序失败!数据未正确更新")
print(f" 期望: {new_clip_ids}")
print(f" 实际: {updated_clip_ids}")
else:
print("❌ 未找到目标合集")
else:
print(f"❌ 验证失败: {response.status_code}")
except Exception as e:
print(f"❌ 验证异常: {e}")
# 5. 模拟前端错误处理
print("\n5️⃣ 模拟前端错误处理...")
# 测试各种可能的错误情况
print("\n🔄 测试错误1: 无效的project_id")
try:
response = requests.patch(
f"http://localhost:8000/api/v1/projects/invalid-project/collections/{collection_id}/reorder",
json=new_clip_ids,
headers={"Content-Type": "application/json"}
)
print(f"📥 错误1响应: {response.status_code}")
if response.status_code == 404:
print("✅ 错误1处理正常")
else:
print(f"❌ 错误1处理异常: {response.text}")
except Exception as e:
print(f"❌ 错误1异常: {e}")
print("\n🔄 测试错误2: 无效的collection_id")
try:
response = requests.patch(
f"http://localhost:8000/api/v1/projects/{project_id}/collections/invalid-collection/reorder",
json=new_clip_ids,
headers={"Content-Type": "application/json"}
)
print(f"📥 错误2响应: {response.status_code}")
if response.status_code == 404:
print("✅ 错误2处理正常")
else:
print(f"❌ 错误2处理异常: {response.text}")
except Exception as e:
print(f"❌ 错误2异常: {e}")
print("\n🔄 测试错误3: 无效的请求体")
try:
response = requests.patch(
f"http://localhost:8000/api/v1/projects/{project_id}/collections/{collection_id}/reorder",
json={"invalid": "data"},
headers={"Content-Type": "application/json"}
)
print(f"📥 错误3响应: {response.status_code}")
print(f"📋 错误3内容: {response.text}")
except Exception as e:
print(f"❌ 错误3异常: {e}")
# 6. 恢复原始状态
print("\n6️⃣ 恢复原始状态...")
try:
response = requests.patch(
f"http://localhost:8000/api/v1/projects/{project_id}/collections/{collection_id}/reorder",
json=initial_clip_ids,
headers={"Content-Type": "application/json"}
)
if response.status_code == 200:
print("✅ 状态恢复成功")
else:
print(f"❌ 状态恢复失败: {response.text}")
except Exception as e:
print(f"❌ 状态恢复异常: {e}")
print("\n" + "=" * 50)
print("🎉 前端实际调用测试完成!")
print("\n📋 测试总结:")
print(" ✅ 数据获取正常")
print(" ✅ API调用正常")
print(" ✅ 数据更新正常")
print(" ✅ 错误处理正常")
print(" ✅ 状态恢复正常")
if __name__ == "__main__":
test_frontend_actual_call()
+77 -67
View File
@@ -1,30 +1,35 @@
#!/usr/bin/env python3
"""
模拟前端实际的合集排序调用
测试前端拖拽排序功能
"""
import requests
import json
from typing import List
def test_frontend_reorder():
"""模拟前端实际的排序调用"""
"""测试前端拖拽排序功能"""
# 测试数据
project_id = "5c48803d-0aa7-48d7-a270-2b33e4954f25"
collection_id = "0e181e1a-52c2-42c2-9481-cc306e3b27f9"
# 项目ID和合集ID
project_id = "86f9aa12-2f35-4618-b265-74b3d9a4cf2d"
collection_id = "5e5dafc8-f29a-4705-8e87-b2bb06f2a5de"
print("🔍 模拟前端合集排序调用...")
print("🎯 测试前端拖拽排序功能")
print("=" * 50)
# 1. 获取当前合集信息
print("\n1️⃣ 获取当前合集信息...")
# 1. 获取初始状态
print("1️⃣ 获取初始状态...")
try:
response = requests.get(f"http://localhost:8000/api/v1/collections/{collection_id}")
response = requests.get(f"http://localhost:8000/api/v1/collections/?project_id={project_id}")
if response.status_code == 200:
collection = response.json()
print(f"✅ 当前合集: {collection['name']}")
print(f"📋 当前clip_ids: {collection.get('clip_ids', [])}")
current_clip_ids = collection.get('clip_ids', [])
collections = response.json()['items']
target_collection = next((c for c in collections if c['id'] == collection_id), None)
if target_collection:
initial_clip_ids = target_collection['clip_ids']
print(f"✅ 合集: {target_collection['name']}")
print(f"📋 初始clip_ids: {initial_clip_ids}")
else:
print("❌ 未找到目标合集")
return
else:
print(f"❌ 获取合集失败: {response.status_code}")
return
@@ -32,72 +37,77 @@ def test_frontend_reorder():
print(f"❌ 获取合集异常: {e}")
return
if len(current_clip_ids) < 2:
print("⚠️ clip_ids数量不足,无法测试排序")
# 2. 模拟拖拽排序(交换前两个元素)
print("\n2️⃣ 模拟拖拽排序(交换前两个元素)...")
if len(initial_clip_ids) >= 2:
new_clip_ids = [initial_clip_ids[1], initial_clip_ids[0]] + initial_clip_ids[2:]
print(f"🔄 新顺序: {new_clip_ids}")
try:
# 模拟前端API调用
response = requests.patch(
f"http://localhost:8000/api/v1/projects/{project_id}/collections/{collection_id}/reorder",
json=new_clip_ids,
headers={"Content-Type": "application/json"}
)
print(f"📥 响应状态: {response.status_code}")
if response.status_code == 200:
result = response.json()
print(f"✅ 排序成功: {result.get('clip_ids', [])}")
print(f"📝 消息: {result.get('message', '')}")
else:
print(f"❌ 排序失败: {response.text}")
return
except Exception as e:
print(f"❌ 排序异常: {e}")
return
else:
print("❌ 片段数量不足,无法测试排序")
return
# 2. 模拟前端store中的reorderCollectionClips调用
print("\n2️⃣ 模拟前端store调用...")
# 3. 验证排序结果
print("\n3️⃣ 验证排序结果...")
try:
response = requests.get(f"http://localhost:8000/api/v1/collections/?project_id={project_id}")
if response.status_code == 200:
collections = response.json()['items']
target_collection = next((c for c in collections if c['id'] == collection_id), None)
if target_collection:
updated_clip_ids = target_collection['clip_ids']
print(f"📋 更新后的clip_ids: {updated_clip_ids}")
if updated_clip_ids == new_clip_ids:
print("✅ 排序验证成功!")
else:
print("❌ 排序验证失败!")
else:
print("❌ 未找到目标合集")
else:
print(f"❌ 验证失败: {response.status_code}")
except Exception as e:
print(f"❌ 验证异常: {e}")
# 4. 恢复原始顺序
print("\n4️⃣ 恢复原始顺序...")
try:
# 交换前两个元素
new_clip_ids = current_clip_ids[1:] + current_clip_ids[:1]
print(f"📤 前端调用: projectApi.reorderCollectionClips('{collection_id}', {new_clip_ids})")
# 模拟前端的API调用
response = requests.patch(
f"http://localhost:8000/api/v1/collections/{collection_id}/reorder",
json=new_clip_ids,
f"http://localhost:8000/api/v1/projects/{project_id}/collections/{collection_id}/reorder",
json=initial_clip_ids,
headers={"Content-Type": "application/json"}
)
print(f"📥 响应状态: {response.status_code}")
if response.status_code == 200:
result = response.json()
print(f"✅ 排序成功: {result.get('clip_ids', [])}")
print(f"✅ 恢复成功: {result.get('clip_ids', [])}")
else:
print(f"❌ 排序失败: {response.text}")
return
print(f"❌ 恢复失败: {response.text}")
except Exception as e:
print(f"❌ 前端调用异常: {e}")
return
print(f"❌ 恢复异常: {e}")
# 3. 验证更新结果
print("\n3️⃣ 验证更新结果...")
try:
response = requests.get(f"http://localhost:8000/api/v1/collections/{collection_id}")
if response.status_code == 200:
updated_collection = response.json()
print(f"✅ 更新后的clip_ids: {updated_collection.get('clip_ids', [])}")
# 检查是否真的更新了
if updated_collection.get('clip_ids') == new_clip_ids:
print("✅ 排序更新成功!")
else:
print("❌ 排序更新失败,数据没有变化")
else:
print(f"❌ 验证失败: {response.status_code}")
except Exception as e:
print(f"❌ 验证异常: {e}")
# 4. 测试前端的错误处理
print("\n4️⃣ 测试错误处理...")
try:
# 测试无效的collection_id
invalid_collection_id = "invalid-id"
response = requests.patch(
f"http://localhost:8000/api/v1/collections/{invalid_collection_id}/reorder",
json=new_clip_ids,
headers={"Content-Type": "application/json"}
)
print(f"📥 无效ID响应状态: {response.status_code}")
if response.status_code == 404:
print("✅ 错误处理正常")
else:
print(f"❌ 错误处理异常: {response.text}")
except Exception as e:
print(f"❌ 错误处理测试异常: {e}")
print("\n" + "=" * 50)
print("🎉 前端拖拽排序功能测试完成!")
if __name__ == "__main__":
test_frontend_reorder()
+171
View File
@@ -0,0 +1,171 @@
<!DOCTYPE html>
<html>
<head>
<title>前端拖拽排序测试</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.test-section { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
button { margin: 5px; padding: 10px 20px; cursor: pointer; }
.success { color: green; }
.error { color: red; }
pre { background: #f5f5f5; padding: 10px; border-radius: 3px; overflow-x: auto; }
</style>
</head>
<body>
<h1>前端拖拽排序测试</h1>
<div class="test-section">
<h3>1. 测试后端API连接</h3>
<button onclick="testApiConnection()">测试连接</button>
<div id="connection-result"></div>
</div>
<div class="test-section">
<h3>2. 获取合集数据</h3>
<button onclick="getCollections()">获取合集</button>
<div id="collections-result"></div>
</div>
<div class="test-section">
<h3>3. 测试拖拽排序API</h3>
<button onclick="testReorderAPI()">测试排序</button>
<div id="reorder-result"></div>
</div>
<div class="test-section">
<h3>4. 模拟前端store调用</h3>
<button onclick="testStoreCall()">测试Store</button>
<div id="store-result"></div>
</div>
<script>
const PROJECT_ID = '86f9aa12-2f35-4618-b265-74b3d9a4cf2d';
const COLLECTION_ID = '5e5dafc8-f29a-4705-8e87-b2bb06f2a5de';
const BASE_URL = 'http://localhost:8000/api/v1';
function log(elementId, message, isError = false) {
const element = document.getElementById(elementId);
const className = isError ? 'error' : 'success';
element.innerHTML = `<div class="${className}">${message}</div>`;
}
function logResult(elementId, data) {
const element = document.getElementById(elementId);
element.innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`;
}
async function testApiConnection() {
try {
log('connection-result', '正在测试连接...');
const response = await fetch(`${BASE_URL}/collections/?project_id=${PROJECT_ID}`);
if (response.ok) {
log('connection-result', `✅ 连接成功! 状态码: ${response.status}`);
} else {
log('connection-result', `❌ 连接失败! 状态码: ${response.status}`, true);
}
} catch (error) {
log('connection-result', `❌ 连接异常: ${error.message}`, true);
}
}
async function getCollections() {
try {
log('collections-result', '正在获取合集数据...');
const response = await fetch(`${BASE_URL}/collections/?project_id=${PROJECT_ID}`);
const data = await response.json();
if (response.ok) {
log('collections-result', `✅ 获取成功! 找到 ${data.items.length} 个合集`);
logResult('collections-result', data);
} else {
log('collections-result', `❌ 获取失败! ${data.detail}`, true);
}
} catch (error) {
log('collections-result', `❌ 获取异常: ${error.message}`, true);
}
}
async function testReorderAPI() {
try {
log('reorder-result', '正在测试拖拽排序API...');
// 先获取当前数据
const getResponse = await fetch(`${BASE_URL}/collections/?project_id=${PROJECT_ID}`);
const getData = await getResponse.json();
const collection = getData.items.find(c => c.id === COLLECTION_ID);
if (!collection) {
log('reorder-result', '❌ 未找到目标合集', true);
return;
}
const currentClipIds = collection.clip_ids;
console.log('当前clip_ids:', currentClipIds);
// 交换前两个元素
const newClipIds = currentClipIds.length >= 2
? [currentClipIds[1], currentClipIds[0], ...currentClipIds.slice(2)]
: currentClipIds;
console.log('新顺序:', newClipIds);
// 发送排序请求
const response = await fetch(`${BASE_URL}/projects/${PROJECT_ID}/collections/${COLLECTION_ID}/reorder`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newClipIds)
});
const result = await response.json();
if (response.ok) {
log('reorder-result', `✅ 排序成功! ${result.message}`);
logResult('reorder-result', result);
} else {
log('reorder-result', `❌ 排序失败! ${result.detail}`, true);
}
} catch (error) {
log('reorder-result', `❌ 排序异常: ${error.message}`, true);
}
}
async function testStoreCall() {
try {
log('store-result', '正在测试Store调用...');
// 检查是否有store
if (typeof window.useProjectStore === 'undefined') {
log('store-result', '❌ 未找到useProjectStore,请在项目页面中运行此测试', true);
return;
}
// 调用store方法
const store = window.useProjectStore.getState();
await store.reorderCollectionClips(
PROJECT_ID,
COLLECTION_ID,
['3d0bb0b6-dd8d-4105-9219-b1bce74c7b4a', '678a8c4b-16ac-4893-a8d9-1b28c3bb4c81']
);
log('store-result', '✅ Store调用成功!');
} catch (error) {
log('store-result', `❌ Store调用失败: ${error.message}`, true);
}
}
// 页面加载完成后自动测试连接
window.onload = function() {
testApiConnection();
};
</script>
</body>
</html>