forked from 77media/video-flow
500 lines
15 KiB
TypeScript
500 lines
15 KiB
TypeScript
/**
|
||
* 视频编辑功能API接口
|
||
*/
|
||
|
||
import { post, get, put, del } from '@/api/request';
|
||
import { ApiResponse } from '@/api/common';
|
||
import {
|
||
EditPoint,
|
||
CreateEditPointRequest,
|
||
UpdateEditPointRequest,
|
||
DeleteEditPointRequest,
|
||
EditPointsResponse,
|
||
EditPointStatus
|
||
} from './types';
|
||
import { getVideoEditApiConfig, getApiEndpoint, debugLog, errorHandlingConfig } from './config';
|
||
|
||
// Mock数据存储(使用localStorage持久化)
|
||
let mockEditPoints: EditPoint[] = [];
|
||
let mockIdCounter = 1;
|
||
|
||
// 初始化Mock数据
|
||
function initializeMockData() {
|
||
try {
|
||
const stored = localStorage.getItem('video-edit-mock-data');
|
||
if (stored) {
|
||
const data = JSON.parse(stored);
|
||
mockEditPoints = data.editPoints || [];
|
||
mockIdCounter = data.idCounter || 1;
|
||
debugLog('Mock数据加载成功', { count: mockEditPoints.length, nextId: mockIdCounter });
|
||
}
|
||
} catch (error) {
|
||
debugLog('Mock数据加载失败,使用默认数据', error);
|
||
mockEditPoints = [];
|
||
mockIdCounter = 1;
|
||
}
|
||
}
|
||
|
||
// 保存Mock数据
|
||
function saveMockData() {
|
||
try {
|
||
const data = {
|
||
editPoints: mockEditPoints,
|
||
idCounter: mockIdCounter,
|
||
timestamp: new Date().toISOString()
|
||
};
|
||
localStorage.setItem('video-edit-mock-data', JSON.stringify(data));
|
||
debugLog('Mock数据保存成功', { count: mockEditPoints.length });
|
||
} catch (error) {
|
||
debugLog('Mock数据保存失败', error);
|
||
}
|
||
}
|
||
|
||
// 初始化数据
|
||
initializeMockData();
|
||
|
||
/**
|
||
* Mock API函数
|
||
*/
|
||
function createMockEditPoint(request: CreateEditPointRequest): EditPoint {
|
||
const editPoint: EditPoint = {
|
||
id: `edit-point-${mockIdCounter++}`,
|
||
videoId: request.videoId,
|
||
projectId: request.projectId,
|
||
userId: 1, // Mock用户ID
|
||
position: request.position,
|
||
timestamp: request.timestamp,
|
||
description: request.description || '',
|
||
status: EditPointStatus.PENDING,
|
||
createdAt: new Date().toISOString(),
|
||
updatedAt: new Date().toISOString(),
|
||
showInput: true, // 新创建的编辑点应该显示输入框
|
||
connectionDirection: 'auto'
|
||
};
|
||
|
||
mockEditPoints.push(editPoint);
|
||
saveMockData(); // 持久化保存
|
||
debugLog('Mock编辑点创建', editPoint);
|
||
return editPoint;
|
||
}
|
||
|
||
/**
|
||
* 创建编辑点
|
||
*/
|
||
export async function createEditPoint(request: CreateEditPointRequest): Promise<EditPoint | null> {
|
||
try {
|
||
const config = getVideoEditApiConfig();
|
||
debugLog('创建编辑点请求', request);
|
||
|
||
// 使用Mock API
|
||
if (config.useMockApi) {
|
||
debugLog('使用Mock API创建编辑点');
|
||
await new Promise(resolve => setTimeout(resolve, 500)); // 模拟网络延迟
|
||
const editPoint = createMockEditPoint(request);
|
||
debugLog('Mock编辑点创建成功', editPoint);
|
||
return editPoint;
|
||
}
|
||
|
||
// 获取API端点
|
||
const apiUrl = getApiEndpoint('/edit-points');
|
||
|
||
const response = await post<ApiResponse<EditPoint>>(apiUrl, {
|
||
video_id: request.videoId,
|
||
project_id: request.projectId,
|
||
position_x: request.position.x,
|
||
position_y: request.position.y,
|
||
timestamp: request.timestamp,
|
||
description: request.description || '',
|
||
status: EditPointStatus.PENDING
|
||
});
|
||
|
||
if (response.successful && response.data) {
|
||
// 转换API响应格式为前端数据格式
|
||
const editPoint: EditPoint = {
|
||
id: response.data.id,
|
||
videoId: response.data.videoId,
|
||
projectId: response.data.projectId,
|
||
userId: response.data.userId,
|
||
position: {
|
||
x: response.data.position.x,
|
||
y: response.data.position.y
|
||
},
|
||
timestamp: response.data.timestamp,
|
||
description: response.data.description,
|
||
status: response.data.status,
|
||
createdAt: response.data.createdAt,
|
||
updatedAt: response.data.updatedAt,
|
||
showInput: false,
|
||
connectionDirection: 'auto'
|
||
};
|
||
|
||
console.log('编辑点创建成功:', editPoint);
|
||
return editPoint;
|
||
} else {
|
||
console.error('创建编辑点失败:', response.message);
|
||
return null;
|
||
}
|
||
} catch (error) {
|
||
console.error('创建编辑点API调用失败:', error);
|
||
// 如果真实API失败,回退到Mock
|
||
if (!USE_MOCK_API) {
|
||
console.log('🔄 API失败,回退到Mock模式');
|
||
const editPoint = createMockEditPoint(request);
|
||
return editPoint;
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新编辑点
|
||
*/
|
||
export async function updateEditPoint(request: UpdateEditPointRequest): Promise<boolean> {
|
||
try {
|
||
console.log('更新编辑点请求:', request);
|
||
|
||
const config = getVideoEditApiConfig();
|
||
|
||
// 使用Mock API
|
||
if (config.useMockApi) {
|
||
debugLog('使用Mock API更新编辑点');
|
||
await new Promise(resolve => setTimeout(resolve, 300)); // 模拟网络延迟
|
||
|
||
const editPointIndex = mockEditPoints.findIndex(point => point.id === request.id);
|
||
if (editPointIndex !== -1) {
|
||
const editPoint = mockEditPoints[editPointIndex];
|
||
|
||
if (request.description !== undefined) {
|
||
editPoint.description = request.description;
|
||
}
|
||
|
||
if (request.status !== undefined) {
|
||
editPoint.status = request.status;
|
||
}
|
||
|
||
if (request.position !== undefined) {
|
||
editPoint.position = request.position;
|
||
}
|
||
|
||
editPoint.updatedAt = new Date().toISOString();
|
||
saveMockData(); // 持久化保存
|
||
debugLog('Mock编辑点更新成功', editPoint);
|
||
return true;
|
||
} else {
|
||
debugLog('Mock编辑点不存在', request.id);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
const updateData: any = {};
|
||
|
||
if (request.description !== undefined) {
|
||
updateData.description = request.description;
|
||
}
|
||
|
||
if (request.status !== undefined) {
|
||
updateData.status = request.status;
|
||
}
|
||
|
||
if (request.position !== undefined) {
|
||
updateData.position_x = request.position.x;
|
||
updateData.position_y = request.position.y;
|
||
}
|
||
|
||
// 选择API端点
|
||
const apiUrl = USE_LOCAL_API ? `/api/video-edit/edit-points/${request.id}` : `/video-edit/edit-points/${request.id}`;
|
||
|
||
const response = await put<ApiResponse<EditPoint>>(apiUrl, updateData);
|
||
|
||
if (response.successful) {
|
||
console.log('编辑点更新成功');
|
||
return true;
|
||
} else {
|
||
console.error('更新编辑点失败:', response.message);
|
||
return false;
|
||
}
|
||
} catch (error) {
|
||
console.error('更新编辑点API调用失败:', error);
|
||
// 如果真实API失败,回退到Mock
|
||
if (!USE_MOCK_API) {
|
||
console.log('🔄 API失败,回退到Mock模式');
|
||
const editPointIndex = mockEditPoints.findIndex(point => point.id === request.id);
|
||
if (editPointIndex !== -1) {
|
||
const editPoint = mockEditPoints[editPointIndex];
|
||
if (request.description !== undefined) editPoint.description = request.description;
|
||
if (request.status !== undefined) editPoint.status = request.status;
|
||
if (request.position !== undefined) editPoint.position = request.position;
|
||
editPoint.updatedAt = new Date().toISOString();
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 删除编辑点
|
||
*/
|
||
export async function deleteEditPoint(request: DeleteEditPointRequest): Promise<boolean> {
|
||
try {
|
||
console.log('删除编辑点请求:', request);
|
||
|
||
// 使用Mock API
|
||
if (USE_MOCK_API) {
|
||
console.log('🔧 使用Mock API删除编辑点');
|
||
await new Promise(resolve => setTimeout(resolve, 200)); // 模拟网络延迟
|
||
|
||
const editPointIndex = mockEditPoints.findIndex(point => point.id === request.id);
|
||
if (editPointIndex !== -1) {
|
||
mockEditPoints.splice(editPointIndex, 1);
|
||
console.log('✅ Mock编辑点删除成功');
|
||
return true;
|
||
} else {
|
||
console.error('❌ Mock编辑点不存在:', request.id);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 选择API端点
|
||
const apiUrl = USE_LOCAL_API ? `/api/video-edit/edit-points/${request.id}` : `/video-edit/edit-points/${request.id}`;
|
||
|
||
const response = await del<ApiResponse<void>>(apiUrl);
|
||
|
||
if (response.successful) {
|
||
console.log('编辑点删除成功');
|
||
return true;
|
||
} else {
|
||
console.error('删除编辑点失败:', response.message);
|
||
return false;
|
||
}
|
||
} catch (error) {
|
||
console.error('删除编辑点API调用失败:', error);
|
||
// 如果真实API失败,回退到Mock
|
||
if (!USE_MOCK_API) {
|
||
console.log('🔄 API失败,回退到Mock模式');
|
||
const editPointIndex = mockEditPoints.findIndex(point => point.id === request.id);
|
||
if (editPointIndex !== -1) {
|
||
mockEditPoints.splice(editPointIndex, 1);
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取编辑点列表
|
||
*/
|
||
export async function getEditPoints(params: {
|
||
videoId: string;
|
||
projectId: string;
|
||
offset?: number;
|
||
limit?: number;
|
||
}): Promise<EditPointsResponse | null> {
|
||
try {
|
||
console.log('获取编辑点列表请求:', params);
|
||
|
||
// 使用Mock API
|
||
if (USE_MOCK_API) {
|
||
console.log('🔧 使用Mock API获取编辑点列表');
|
||
await new Promise(resolve => setTimeout(resolve, 300)); // 模拟网络延迟
|
||
|
||
// 过滤匹配的编辑点
|
||
const filteredPoints = mockEditPoints.filter(point =>
|
||
point.videoId === params.videoId && point.projectId === params.projectId
|
||
);
|
||
|
||
const offset = params.offset || 0;
|
||
const limit = params.limit || 50;
|
||
const paginatedPoints = filteredPoints.slice(offset, offset + limit);
|
||
|
||
const result: EditPointsResponse = {
|
||
editPoints: paginatedPoints,
|
||
totalCount: filteredPoints.length,
|
||
hasMore: offset + limit < filteredPoints.length
|
||
};
|
||
|
||
console.log('✅ Mock编辑点列表获取成功:', result);
|
||
return result;
|
||
}
|
||
|
||
const queryParams = new URLSearchParams({
|
||
video_id: params.videoId,
|
||
project_id: params.projectId,
|
||
offset: (params.offset || 0).toString(),
|
||
limit: (params.limit || 50).toString()
|
||
});
|
||
|
||
// 选择API端点
|
||
const apiUrl = USE_LOCAL_API ? `/api/video-edit/edit-points?${queryParams}` : `/video-edit/edit-points?${queryParams}`;
|
||
|
||
const response = await get<ApiResponse<{
|
||
edit_points: any[];
|
||
total_count: number;
|
||
has_more: boolean;
|
||
}>>(apiUrl);
|
||
|
||
if (response.successful && response.data) {
|
||
// 转换API响应格式为前端数据格式
|
||
const editPoints: EditPoint[] = response.data.edit_points.map((item: any) => ({
|
||
id: item.id,
|
||
videoId: item.video_id,
|
||
projectId: item.project_id,
|
||
userId: item.user_id,
|
||
position: {
|
||
x: item.position_x,
|
||
y: item.position_y
|
||
},
|
||
timestamp: item.timestamp,
|
||
description: item.description,
|
||
status: item.status,
|
||
createdAt: item.created_at,
|
||
updatedAt: item.updated_at,
|
||
showInput: false,
|
||
connectionDirection: 'auto'
|
||
}));
|
||
|
||
const result: EditPointsResponse = {
|
||
editPoints,
|
||
totalCount: response.data.total_count,
|
||
hasMore: response.data.has_more
|
||
};
|
||
|
||
console.log('编辑点列表获取成功:', result);
|
||
return result;
|
||
} else {
|
||
console.error('获取编辑点列表失败:', response.message);
|
||
return null;
|
||
}
|
||
} catch (error) {
|
||
console.error('获取编辑点列表API调用失败:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 批量删除编辑点
|
||
*/
|
||
export async function batchDeleteEditPoints(editPointIds: string[]): Promise<boolean> {
|
||
try {
|
||
console.log('批量删除编辑点请求:', editPointIds);
|
||
|
||
const response = await post<ApiResponse<void>>('/video-edit/edit-points/batch-delete', {
|
||
edit_point_ids: editPointIds
|
||
});
|
||
|
||
if (response.successful) {
|
||
console.log('批量删除编辑点成功');
|
||
return true;
|
||
} else {
|
||
console.error('批量删除编辑点失败:', response.message);
|
||
return false;
|
||
}
|
||
} catch (error) {
|
||
console.error('批量删除编辑点API调用失败:', error);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取编辑点统计信息
|
||
*/
|
||
export async function getEditPointStats(params: {
|
||
videoId?: string;
|
||
projectId: string;
|
||
userId?: number;
|
||
}): Promise<{
|
||
totalCount: number;
|
||
statusCounts: Record<EditPointStatus, number>;
|
||
recentActivity: EditPoint[];
|
||
} | null> {
|
||
try {
|
||
console.log('获取编辑点统计请求:', params);
|
||
|
||
const queryParams = new URLSearchParams({
|
||
project_id: params.projectId
|
||
});
|
||
|
||
if (params.videoId) {
|
||
queryParams.append('video_id', params.videoId);
|
||
}
|
||
|
||
if (params.userId) {
|
||
queryParams.append('user_id', params.userId.toString());
|
||
}
|
||
|
||
const response = await get<ApiResponse<{
|
||
total_count: number;
|
||
status_counts: Record<string, number>;
|
||
recent_activity: any[];
|
||
}>>(`/video-edit/edit-points/stats?${queryParams}`);
|
||
|
||
if (response.successful && response.data) {
|
||
// 转换状态计数格式
|
||
const statusCounts: Record<EditPointStatus, number> = {
|
||
[EditPointStatus.PENDING]: response.data.status_counts.pending || 0,
|
||
[EditPointStatus.EDITED]: response.data.status_counts.edited || 0,
|
||
[EditPointStatus.PROCESSING]: response.data.status_counts.processing || 0,
|
||
[EditPointStatus.COMPLETED]: response.data.status_counts.completed || 0,
|
||
[EditPointStatus.FAILED]: response.data.status_counts.failed || 0
|
||
};
|
||
|
||
// 转换最近活动数据
|
||
const recentActivity: EditPoint[] = response.data.recent_activity.map((item: any) => ({
|
||
id: item.id,
|
||
videoId: item.video_id,
|
||
projectId: item.project_id,
|
||
userId: item.user_id,
|
||
position: {
|
||
x: item.position_x,
|
||
y: item.position_y
|
||
},
|
||
timestamp: item.timestamp,
|
||
description: item.description,
|
||
status: item.status,
|
||
createdAt: item.created_at,
|
||
updatedAt: item.updated_at,
|
||
showInput: false,
|
||
connectionDirection: 'auto'
|
||
}));
|
||
|
||
const result = {
|
||
totalCount: response.data.total_count,
|
||
statusCounts,
|
||
recentActivity
|
||
};
|
||
|
||
console.log('编辑点统计获取成功:', result);
|
||
return result;
|
||
} else {
|
||
console.error('获取编辑点统计失败:', response.message);
|
||
return null;
|
||
}
|
||
} catch (error) {
|
||
console.error('获取编辑点统计API调用失败:', error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 提交编辑请求到处理队列
|
||
*/
|
||
export async function submitEditRequest(editPointId: string): Promise<boolean> {
|
||
try {
|
||
console.log('提交编辑请求:', editPointId);
|
||
|
||
const response = await post<ApiResponse<void>>(`/video-edit/edit-points/${editPointId}/submit`, {});
|
||
|
||
if (response.successful) {
|
||
console.log('编辑请求提交成功');
|
||
return true;
|
||
} else {
|
||
console.error('提交编辑请求失败:', response.message);
|
||
return false;
|
||
}
|
||
} catch (error) {
|
||
console.error('提交编辑请求API调用失败:', error);
|
||
return false;
|
||
}
|
||
}
|