瀏覽代碼

增加 api sandbox 路由

yourname 5 月之前
父節點
當前提交
818df4f794
共有 2 個文件被更改,包括 177 次插入0 次删除
  1. 171 0
      docs/convert.api.md
  2. 6 0
      src/client/member/routes.tsx

+ 171 - 0
docs/convert.api.md

@@ -0,0 +1,171 @@
+```ts
+import type { ActionFunctionArgs } from '@remix-run/node';
+import type { ConvertRequest, ConvertResponse, Template } from '@shared/types/mod.ts';
+import { ERROR_CODES, EXCEL_TEMPLATE_TABLE } from '@shared/types/mod.ts';
+import { excelParser } from '~/lib/ExcelParser.server.ts';
+import { getApiClient } from '~/lib/ApiClient.server.ts';
+
+/**
+ * 公开的Excel转JSON API接口 
+ * POST /api/v1/convert
+ * 
+ * 需要在请求头中提供有效的API密钥:
+ * - X-API-Key: 'YOUR_API_KEY'
+ */
+export const action = async ({ request }: ActionFunctionArgs) => {
+  console.log('[ExcelToJSON API] 收到转换请求');
+  
+  // 只支持POST请求
+  if (request.method !== 'POST') {
+    console.log('[ExcelToJSON API] 错误: 不支持的请求方法', request.method);
+    return Response.json({
+      success: false,
+      code: ERROR_CODES.VALIDATION_ERROR,
+      message: '不支持的请求方法,仅支持POST'
+    }, { status: 405 });
+  }
+
+  // 验证API密钥
+  const apiKey = request.headers.get('X-API-Key');
+  if (!apiKey) {
+    console.log('[ExcelToJSON API] 错误: 缺少API密钥');
+    return Response.json({
+      success: false,
+      code: ERROR_CODES.AUTH_ERROR,
+      message: '缺少API密钥,请在请求头中提供X-API-Key'
+    }, { status: 401 });
+  }
+
+  // TODO: 在实际生产环境中,应该从数据库或环境变量中获取并验证API密钥
+  const validApiKey = process.env.API_KEY || 'excel2json-api-key';
+  if (apiKey !== validApiKey) {
+    console.log('[ExcelToJSON API] 错误: API密钥无效', apiKey);
+    return Response.json({
+      success: false,
+      code: ERROR_CODES.AUTH_ERROR,
+      message: 'API密钥无效'
+    }, { status: 401 });
+  }
+
+  try {
+    // 解析请求数据
+    const requestData = await request.json() as ConvertRequest;
+    console.log('[ExcelToJSON API] 请求数据:', {
+      templateId: requestData.templateId,
+      hasConfig: !!requestData.config,
+      inputType: requestData.input?.substring(0, 20) + '...' // 只记录输入类型的前20个字符
+    });
+    
+    // 验证输入参数
+    if (!requestData.input) {
+      console.log('[ExcelToJSON API] 错误: 缺少input参数');
+      return Response.json({
+        success: false,
+        code: ERROR_CODES.VALIDATION_ERROR,
+        message: '请提供input参数'
+      }, { status: 400 });
+    }
+
+    // 获取API客户端
+    console.log('[ExcelToJSON API] 获取API客户端');
+    const apiClient = await getApiClient();
+    
+    // 解析配置
+    let templateConfig;
+    
+    // 如果提供了templateId,从数据库获取模板配置
+    if (requestData.templateId) {
+      console.log('[ExcelToJSON API] 正在获取模板,ID:', requestData.templateId);
+      try {
+        const template = await apiClient.database.table(EXCEL_TEMPLATE_TABLE)
+          .where('id', requestData.templateId)
+          .where('is_deleted', 0)
+          .first() as Template;
+        
+        if (!template) {
+          console.log('[ExcelToJSON API] 错误: 模板不存在', requestData.templateId);
+          return Response.json({
+            success: false,
+            code: ERROR_CODES.NOT_FOUND,
+            message: '模板不存在'
+          }, { status: 404 });
+        }
+        
+        templateConfig = template.template_config;
+        console.log('[ExcelToJSON API] 成功获取模板配置,工作表数:', templateConfig.sheets?.length);
+        
+      } catch (error) {
+        console.error('[ExcelToJSON API] 获取模板失败:', error);
+        return Response.json({
+          success: false,
+          code: ERROR_CODES.SERVER_ERROR,
+          message: '获取模板失败'
+        }, { status: 500 });
+      }
+    } 
+    // 如果提供了自定义配置,使用自定义配置
+    else if (requestData.config) {
+      templateConfig = requestData.config;
+      console.log('[ExcelToJSON API] 使用自定义配置,工作表数:', templateConfig.sheets?.length);
+    } 
+    // 如果没有提供模板ID或自定义配置,返回错误
+    else {
+      console.log('[ExcelToJSON API] 错误: 缺少templateId或config参数');
+      return Response.json({
+        success: false,
+        code: ERROR_CODES.VALIDATION_ERROR,
+        message: '请提供templateId或config参数'
+      }, { status: 400 });
+    }
+
+    // 将输入转换为ArrayBuffer
+    let buffer: ArrayBuffer;
+    
+    try {
+      console.log('[ExcelToJSON API] 正在获取文件数据');
+      buffer = await excelParser.getBufferFromUrlOrBase64(requestData.input);
+      console.log('[ExcelToJSON API] 成功获取文件数据,大小:', buffer.byteLength, '字节');
+    } catch (error) {
+      console.error('[ExcelToJSON API] 获取文件数据失败:', error);
+      return Response.json({
+        success: false,
+        code: ERROR_CODES.VALIDATION_ERROR,
+        message: '获取文件数据失败,请确保提供了有效的URL或Base64编码'
+      }, { status: 400 });
+    }
+    
+    // 处理Excel文件
+    console.log('[ExcelToJSON API] 开始解析Excel文件');
+    const result = await excelParser.parseExcelBuffer(buffer, templateConfig.sheets);
+    console.log('[ExcelToJSON API] Excel解析完成,总表格数:', result.totalTables, '警告数:', result.warnings.length);
+    
+    // 构造响应
+    const response: ConvertResponse = {
+      success: true,
+      data: result.exportData,
+      warnings: result.warnings,
+      availableFields: result.availableFieldsBySheet,
+      totalTables: result.totalTables
+    };
+    
+    // 记录API调用日志(可选)
+    // await apiClient.database.table('api_logs').insert({
+    //   api_key: apiKey,
+    //   endpoint: '/api/v1/convert',
+    //   created_at: new Date()
+    // });
+    
+    console.log('[ExcelToJSON API] 转换成功,响应数据大小:', 
+      JSON.stringify(response).length, '字节');
+    return Response.json(response);
+    
+  } catch (error) {
+    console.error('[ExcelToJSON API] 转换失败:', error);
+    return Response.json({
+      success: false,
+      code: ERROR_CODES.SERVER_ERROR,
+      message: `转换失败: ${error instanceof Error ? error.message : '未知错误'}`
+    }, { status: 500 });
+  }
+}; 
+```

+ 6 - 0
src/client/member/routes.tsx

@@ -9,6 +9,7 @@ import { LoginPage } from './pages/Login';
 import MemberHome from './pages/Home';
 import TemplateList from './pages/TemplateList';
 import TemplateEdit from './pages/TemplateEdit';
+import { ApiTester } from './components/ApiTester';
 
 export const router = createBrowserRouter([
   {
@@ -51,6 +52,11 @@ export const router = createBrowserRouter([
         element: <TemplateEdit />,
         errorElement: <ErrorPage />
       },
+      {
+        path: 'api-sandbox',
+        element: <ApiTester />,
+        errorElement: <ErrorPage />
+      },
       {
         path: '*',
         element: <NotFoundPage />,