|
@@ -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 });
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+```
|