lrj
18 小时以前 ae3349d2ff53767b5bc9cb30e1bf7e15f9e814ee
web/src/api/media.js
@@ -1,5 +1,7 @@
// 媒体查询 API
const GRAPHQL_ENDPOINT = 'http://localhost:8080/api/graphql';
import { graphqlRequest, API_CONFIG } from '../config/api.ts';
const GRAPHQL_ENDPOINT = API_CONFIG.GRAPHQL_ENDPOINT;
const MEDIAS_BY_TARGET_QUERY = `
  query MediasByTarget($targetType: Int!, $targetId: ID!) {
@@ -36,9 +38,17 @@
`;
export const getMediasByTarget = async (targetType, targetId) => {
  // 获取JWT token
  const { getToken } = await import('@/utils/auth');
  const token = getToken();
  const headers = { 'Content-Type': 'application/json' };
  if (token) {
    headers['Authorization'] = `Bearer ${token}`;
  }
  const res = await fetch(GRAPHQL_ENDPOINT, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    headers: headers,
    body: JSON.stringify({
      query: MEDIAS_BY_TARGET_QUERY,
      variables: { targetType, targetId }
@@ -52,9 +62,17 @@
};
export const saveMedia = async (input) => {
  // 获取JWT token
  const { getToken } = await import('@/utils/auth');
  const token = getToken();
  const headers = { 'Content-Type': 'application/json' };
  if (token) {
    headers['Authorization'] = `Bearer ${token}`;
  }
  const res = await fetch(GRAPHQL_ENDPOINT, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    headers: headers,
    body: JSON.stringify({
      query: SAVE_MEDIA_MUTATION,
      variables: { input }
@@ -68,46 +86,146 @@
};
export const deleteMedia = async (id) => {
  console.log('=== deleteMedia API调用 ===');
  console.log('要删除的媒体ID:', id);
  console.log('GraphQL查询:', DELETE_MEDIA_MUTATION);
  try {
    const variables = { id: parseInt(id) };
    // 发送GraphQL请求
    const result = await graphqlRequest(DELETE_MEDIA_MUTATION, variables);
    // 检查返回结果
    const deleteResult = result.data?.deleteMedia;
    return deleteResult;
  } catch (error) {
    throw new Error(`删除媒体失败: ${error.message}`);
  }
};
// 上传文件到服务器(带重试机制)
export const uploadFile = async (file, maxRetries = 3) => {
  const formData = new FormData();
  formData.append('file', file);
  
  // 获取JWT token
  const { getToken } = await import('@/utils/auth');
  const token = getToken();
  const headers = {};
  if (token) {
    headers['Authorization'] = `Bearer ${token}`;
  }
  let lastError;
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch('http://localhost:8080/api/upload/image', {
        method: 'POST',
        headers: headers,
        body: formData,
        // 添加超时设置
        signal: AbortSignal.timeout(30000) // 30秒超时
      });
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }
      const result = await response.json();
      if (!result.success) {
        throw new Error(result.error || '上传失败');
      }
      return result;
    } catch (error) {
      lastError = error;
      console.warn(`文件上传第${attempt}次尝试失败:`, error.message);
      // 如果是最后一次尝试,或者是非网络错误,直接抛出
      if (attempt === maxRetries ||
          (!error.message.includes('Failed to fetch') &&
           !error.message.includes('ERR_CONNECTION_RESET') &&
           !error.message.includes('ERR_NETWORK'))) {
        break;
      }
      // 等待一段时间后重试(指数退避)
      const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  // 提供更友好的错误信息
  if (lastError.message.includes('Failed to fetch') ||
      lastError.message.includes('ERR_CONNECTION_RESET') ||
      lastError.message.includes('ERR_NETWORK')) {
    throw new Error('网络连接失败,请检查网络连接或稍后重试');
  } else if (lastError.message.includes('timeout')) {
    throw new Error('上传超时,请检查网络连接或稍后重试');
  } else {
    throw new Error(`上传失败: ${lastError.message}`);
  }
};
// 上传视频文件并自动生成缩略图
export const uploadVideoWithThumbnail = async (videoFile) => {
  const { extractVideoFrame, generateThumbnailFileName } = await import('@/utils/video.js');
  try {
    // 1. 上传视频文件
    const videoUploadResult = await uploadFile(videoFile);
    // 2. 提取视频第一帧
    const thumbnailBlob = await extractVideoFrame(videoFile);
    // 3. 创建缩略图文件对象
    const thumbnailFileName = generateThumbnailFileName(videoFile.name);
    const thumbnailFile = new File([thumbnailBlob], thumbnailFileName, {
      type: 'image/jpeg'
    });
    // 4. 上传缩略图
    const thumbnailUploadResult = await uploadFile(thumbnailFile);
    // 5. 返回包含视频和缩略图信息的结果
    return {
      video: videoUploadResult,
      thumbnail: thumbnailUploadResult,
      success: true
    };
  } catch (error) {
    console.error('视频处理失败:', error);
    throw new Error(`视频处理失败: ${error.message}`);
  }
};
// 新版保存媒体(SaveMediaV2):使用字符串 targetType 与 MediaSaveInput
// 注意:目前后端仅支持 targetType: "player"(学员头像 -> 6)与 "activity_player"(报名资料 -> 5)。
// 字段命名与旧版不同:fileName 替代 name;可选 thumbPath;mediaType: 1图片、2视频、3音频、4文档。
const SAVE_MEDIA_V2_MUTATION = `
  mutation SaveMediaV2($input: MediaSaveInput!) {
    saveMediaV2(input: $input) {
      success
      message
      mediaId
    }
  }
`;
// 统一的 V2 保存接口(返回 { success, message, mediaId }),示例:
// await saveMediaV2({ targetType: 'player', targetId: 123, path: 'avatar/xxx.jpg', fileName: 'avatar.jpg', fileExt: 'jpg', fileSize: 2048, mediaType: 1 })
export const saveMediaV2 = async (input) => {
  const res = await fetch(GRAPHQL_ENDPOINT, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      query: DELETE_MEDIA_MUTATION,
      variables: { id: id.toString() }
      query: SAVE_MEDIA_V2_MUTATION,
      variables: { input }
    })
  });
  const result = await res.json();
  console.log('GraphQL响应:', result);
  console.log('deleteMedia结果:', result.data?.deleteMedia);
  if (result.errors) {
    console.error('GraphQL错误:', result.errors);
    throw new Error(result.errors[0].message);
  }
  const deleteResult = result.data.deleteMedia;
  console.log('返回的删除结果:', deleteResult, '类型:', typeof deleteResult);
  return deleteResult;
};
// 上传文件到服务器
export const uploadFile = async (file) => {
  const formData = new FormData();
  formData.append('file', file);
  const response = await fetch('http://localhost:8080/api/upload/image', {
    method: 'POST',
    body: formData
  });
  const result = await response.json();
  if (!result.success) {
    throw new Error(result.error || '上传失败');
  }
  return result;
  return result.data.saveMediaV2;
};