import { postgrestGet, postgrestDelete, postgrestPut, type PostgrestParams } from '../postgrest-client'; import dayjs from 'dayjs'; import { getDocumentTypes } from '../document-types/document-types'; /** * 从不同格式的 API 响应中提取数据 * @param responseData API 响应数据 * @returns 提取后的数据或 null */ function extractApiData(responseData: unknown): T | null { if (!responseData) return null; // 格式1: { code: number, msg: string, data: T } if (typeof responseData === 'object' && responseData !== null && 'code' in responseData && 'data' in responseData && (responseData as { data: unknown }).data) { return (responseData as { data: T }).data; } // 格式2: 直接是数据对象 return responseData as T; } /** * 格式化日期 * @param dateString 日期字符串 * @returns 格式化后的日期字符串 */ function formatDate(dateString: string): string { if (!dateString) return ''; try { return dayjs(dateString).format('YYYY-MM-DD HH:mm:ss'); } catch (error) { console.error('日期格式化失败:', error); return dateString; } } /** * 查询参数 */ export interface DocumentSearchParams { name?: string; documentNumber?: string; documentType?: string; status?: string; auditStatus?: string; fileStatus?: string; dateFrom?: string; dateTo?: string; page?: number; pageSize?: number; } /** * 数据库文档结构 */ export interface Document { id: number; user_id: number | null; type_id: number; name: string; document_number: string; path: string; storage_type: string; file_size: number; upload_time: string; is_test_document: boolean; evaluation_level: string; status: 'pass' | 'warning' | 'waiting' | 'processing' | 'fail'; file_status: 'Waiting' | 'Cutting' | 'Extractioning' | 'Evaluationing' | 'Processed'; audit_status: number; // -1: 不通过, 0: 待审核, 1: 通过, 2: 警告, 3: 审核中 ocr_result?: { __meta?: { page_count?: number; } }; extracted_results?: unknown; summary?: unknown; remark?: string; created_at: string; updated_at: string; } /** * 前端UI文档结构 */ export interface DocumentUI { id: number; name: string; documentNumber: string; type: string; typeName: string; size: number; auditStatus: number; // -1: 不通过, 0: 待审核, 1: 通过, 2: 警告, 3: 审核中 fileStatus: string; // Waiting, Cutting, Extractioning, Evaluationing, Processed issues: number | null; uploadTime: string; fileType: string; path: string; isTest: boolean; updatedAt?: string; pageCount?: number; } /** * 获取文件扩展名 * @param filename 文件名 * @returns 文件扩展名 */ function getFileExtension(filename: string): string { const parts = filename.split('.'); return parts.length > 1 ? parts.pop()?.toLowerCase() || '' : ''; } /** * 将API文档转换为UI文档 */ async function convertToUIDocument(doc: Document): Promise { // 获取文档类型信息 const typeResponse = await getDocumentTypes(); const documentTypes = typeResponse.data?.types || []; const docType = documentTypes.find(type => type.id.toString() === doc.type_id.toString()); return { id: doc.id, name: doc.name, documentNumber: doc.document_number, type: doc.type_id.toString(), typeName: docType?.name || '未知类型', size: doc.file_size, auditStatus: doc.audit_status || 0, fileStatus: doc.status || '', // 默认为'' issues: 0, // 固定为0 uploadTime: formatDate(doc.updated_at), fileType: getFileExtension(doc.name), path: doc.path, isTest: doc.is_test_document, updatedAt: formatDate(doc.updated_at), pageCount: doc.ocr_result?.__meta?.page_count || 0 }; } /** * 获取文档列表 * @param searchParams 搜索参数 * @returns 文档列表和总数 */ export async function getDocuments(searchParams: DocumentSearchParams = {}): Promise<{ data?: { documents: DocumentUI[], total: number }; error?: string; status?: number; }> { try { const page = searchParams.page || 1; const pageSize = searchParams.pageSize || 10; // 构建查询参数 const params: PostgrestParams = { select: '*', order: 'updated_at.desc', headers: { 'Prefer': 'count=exact' }, limit: pageSize, offset: (page - 1) * pageSize, filter: {} as Record }; // 添加筛选条件 const filter: Record = {}; if (searchParams.name) { filter['name'] = `ilike.%${searchParams.name}%`; } if (searchParams.documentNumber) { filter['document_number'] = `ilike.%${searchParams.documentNumber}%`; } if (searchParams.documentType) { filter['type_id'] = `eq.${searchParams.documentType}`; } if (searchParams.auditStatus) { filter['audit_status'] = `eq.${searchParams.auditStatus}`; } if (searchParams.fileStatus) { filter['status'] = `eq.${searchParams.fileStatus}`; } // 处理日期范围 if (searchParams.dateFrom) { // 添加当天开始时间 00:00:00 filter['created_at'] = `gte.'${dayjs(`${searchParams.dateFrom} 00:00:00`).format()}'`; } if (searchParams.dateTo) { // 如果有开始日期,使用and条件;否则直接设置结束日期 const dateToKey = searchParams.dateFrom ? 'and' : 'created_at'; const newDateFrom = dayjs(`${searchParams.dateFrom} 00:00:00`).format(); const newDateTo = dayjs(`${searchParams.dateTo} 23:59:59`).format(); // 添加当天结束时间 23:59:59 if (dateToKey === 'and') { delete filter['created_at']; // 使用OR操作符连接两个条件 filter[dateToKey] = `(created_at.gte.'${newDateFrom}',created_at.lte.'${newDateTo}')`; } else { filter['created_at'] = `lte.'${newDateTo}'`; } } // console.log('filter-----', filter); params.filter = filter; // 发送请求 const response = await postgrestGet('documents', params); if (response.error) { return { error: response.error, status: response.status }; } // 提取数据 const extractedData = extractApiData(response.data); if (!extractedData) { return { error: '获取文档数据失败', status: 500 }; } // 转换为UI格式 const documents = await Promise.all(extractedData.map(convertToUIDocument)); // console.log('documentsItem',documents) // 获取总数 let totalCount = 0; const responseWithHeaders = response as { data: unknown; headers?: Record }; if (responseWithHeaders.headers) { const rangeHeader = responseWithHeaders.headers['content-range']; if (rangeHeader) { const total = rangeHeader.split('/')[1]; if (total !== '*') { totalCount = parseInt(total, 10); } } } return { data: { documents, total: totalCount || documents.length } }; } catch (error) { console.error('获取文档列表失败:', error); return { error: error instanceof Error ? error.message : '获取文档列表失败', status: 500 }; } } /** * 删除文档 * @param id 文档ID * @returns 删除结果 */ export async function deleteDocument(id: string): Promise<{ success?: boolean; error?: string; status?: number; }> { try { if (!id) { return { error: '文档ID不能为空', status: 400 }; } const response = await postgrestDelete( 'documents', { filter: { 'id': `eq.${id}` } } ); if (response.error) { return { error: response.error, status: response.status }; } return { success: true }; } catch (error) { console.error('删除文档失败:', error); return { error: error instanceof Error ? error.message : '删除文档失败', status: 500 }; } } /** * 获取单个文档详情 * @param id 文档ID * @returns 文档详情 */ export async function getDocument(id: string): Promise<{ data?: DocumentUI; error?: string; status?: number; }> { try { if (!id) { return { error: '文档ID不能为空', status: 400 }; } const response = await postgrestGet( 'documents', { filter: { 'id': `eq.${id}` }, limit: 1 } ); if (response.error) { return { error: response.error, status: response.status }; } const extractedData = extractApiData(response.data); if (!extractedData || extractedData.length === 0) { return { error: '文档不存在', status: 404 }; } const documentUI = await convertToUIDocument(extractedData[0]); return { data: documentUI }; } catch (error) { console.error('获取文档详情失败:', error); return { error: error instanceof Error ? error.message : '获取文档详情失败', status: 500 }; } } /** * 获取文件下载链接 * @param filePath 文件路径 * @returns 下载链接 */ export async function getFileDownloadUrl(filePath: string): Promise<{ data?: { downloadUrl: string }; error?: string; status?: number; }> { try { if (!filePath) { return { error: '文件路径不能为空', status: 400 }; } // 这里应该调用获取文件下载链接的API // 假设后端有这样的端点:/api/files/generate-download-url?path=xxx // 实际项目中需要根据你的后端API调整 // 临时解决方案:返回Remix路由路径 // 这将通过Remix服务器代理对文件的访问 return { data: { downloadUrl: `/documents/download?path=${encodeURIComponent(filePath)}` } }; } catch (error) { console.error('获取文件下载链接失败:', error); return { error: error instanceof Error ? error.message : '获取文件下载链接失败', status: 500 }; } } /** * 更新文档信息 * @param id 文档ID * @param document 部分文档数据 * @returns 更新结果 */ export async function updateDocument(id: string, document: Partial & { remark?: string }): Promise<{ data?: DocumentUI; error?: string; status?: number; }> { try { if (!id) { return { error: '文档ID不能为空', status: 400 }; } // 准备API数据 - 将UI数据转换为API格式 const apiDocument: Partial = {}; if (document.documentNumber !== undefined) { apiDocument.document_number = document.documentNumber; } if (document.type !== undefined) { apiDocument.type_id = parseInt(document.type); } if (document.auditStatus !== undefined) { apiDocument.audit_status = document.auditStatus; } if (document.isTest !== undefined) { apiDocument.is_test_document = document.isTest; } if (document.remark !== undefined) { apiDocument.remark = document.remark; } console.log('更新文档API数据:', apiDocument); const response = await postgrestPut>( 'documents', apiDocument, { id: parseInt(id) } ); if (response.error) { console.error('更新文档API错误:', response.error); return { error: response.error, status: response.status }; } // 获取更新后的完整文档数据 const updatedResponse = await getDocument(id); return updatedResponse; } catch (error) { console.error('更新文档信息失败:', error); return { error: error instanceof Error ? error.message : '更新文档信息失败', status: 500 }; } }