import { postgrestGet, postgrestDelete, postgrestPut, postgrestPost } from '../postgrest-client'; import { getDocumentTypes } from '../document-types/document-types'; import { formatDate } from '../../utils'; /** * 从不同格式的 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; } /** * 查询参数 */ export interface DocumentSearchParams { name?: string; documentNumber?: string; documentType?: string; status?: string; auditStatus?: string; fileStatus?: string; dateFrom?: string; dateTo?: string; page?: number; pageSize?: number; reviewType?: string; userId?: string; // 添加用户ID筛选 } /** * 数据库文档结构 */ 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, Failed, Evaluationing, Processed issues: number | null; uploadTime: string; fileType: string; path: string; isTest: boolean; updatedAt?: string; pageCount?: number; ocrResult?: unknown; } /** * 获取文件扩展名 * @param filename 文件名 * @returns 文件扩展名 */ function getFileExtension(filename: string): string { const parts = filename.split('.'); return parts.length > 1 ? parts.pop()?.toLowerCase() || '' : ''; } /** * 获取评查结果 * @param id 评查结果ID * @returns 评查结果 */ async function getEvaluationResults(id: number) { const response = await postgrestGet<[]>('evaluation_results', { filter: { 'document_id': `eq.${id}` } }); if (response.error) { return { error: response.error, status: response.status }; } const evaluationResult = extractApiData<[]>(response.data); return evaluationResult; } /** * 将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()); const evaluationResult = await getEvaluationResults(doc.id); let issues = 0; interface EvaluationResultItem { evaluated_results?: { result?: string; [key: string]: unknown; }; [key: string]: unknown; } if (evaluationResult && Array.isArray(evaluationResult)) { evaluationResult.forEach((result: EvaluationResultItem) => { if(result && result.evaluated_results && !result.evaluated_results.result){ issues++; } }); } 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: issues, // 使用计算得到的issues 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, ocrResult: doc.ocr_result }; } /** * 后端SQL函数返回的文档结构 */ interface DocumentFromSQL { id: number; name: string; document_number: string; type_id: number; type_name: string; file_size: number; audit_status: number; status: string; false_count: number; updated_at: string; path: string; is_test_document: boolean; ocr_result: { __meta?: { page_count?: number; } }; } /** * 获取文档列表 * @param searchParams 搜索参数 * @returns 文档列表和总数 */ export async function getDocuments(searchParams: DocumentSearchParams = {}): Promise<{ data?: { documents: DocumentUI[], total: number }; error?: string; status?: number; }> { try { // 准备RPC调用参数 const { page = 1, pageSize = 10, name, documentNumber, documentType, auditStatus, fileStatus, dateFrom, dateTo, reviewType, userId } = searchParams; let documentTypes: number[] | undefined; if (documentType) { documentTypes = [parseInt(documentType, 10)]; } else if (reviewType) { if (reviewType === 'contract') { documentTypes = [1]; } else if (reviewType === 'record') { documentTypes = [2, 3]; } } // 确保userId必须存在,如果不存在则抛出错误 if (!userId) { return { error: '用户身份验证失败,无法获取文档列表', status: 401 }; } const rpcParams = { search_name: name, search_document_number: documentNumber, search_document_types: documentTypes, search_audit_status: auditStatus !== undefined ? parseInt(auditStatus, 10) : undefined, search_file_status: fileStatus, search_date_from: dateFrom, search_date_to: dateTo, search_user_id: parseInt(userId, 10), // 强制要求传递用户ID }; // 并行执行获取数据和获取总数的请求 const [documentsResponse, countResponse] = await Promise.all([ postgrestPost('rpc/get_documents_with_filters', { ...rpcParams, page, page_size: pageSize }), postgrestPost('rpc/count_documents_with_filters', rpcParams) ]); // 处理获取文档列表的错误 if (documentsResponse.error || !documentsResponse.data) { return { error: documentsResponse.error || '获取文档数据失败', status: documentsResponse.status || 500 }; } // 处理获取总数的错误 if (countResponse.error || typeof countResponse.data !== 'number') { // 如果计数失败,可以继续返回数据,但总数可能不准 console.error('获取文档总数失败:', countResponse.error); } // console.log('countResponse.data', countResponse.data); const totalCount = typeof countResponse.data === 'number' ? countResponse.data : 0; // 将SQL返回的数据转换为UI格式 const documents: DocumentUI[] = documentsResponse.data.map((doc: DocumentFromSQL) => ({ id: doc.id, name: doc.name, documentNumber: doc.document_number, type: doc.type_id.toString(), typeName: doc.type_name || '未知类型', size: doc.file_size, auditStatus: doc.audit_status ?? 0, fileStatus: doc.status || '', issues: doc.false_count ?? null, 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, ocrResult: doc.ocr_result })); return { data: { documents, total: totalCount } }; } catch (error) { console.error('获取文档列表失败:', error); return { error: error instanceof Error ? error.message : '获取文档列表失败', status: 500 }; } } /** * 删除文档 * @param id 文档ID * @param userId 用户ID * @returns 删除结果 */ export async function deleteDocument(id: string, userId: string): Promise<{ success?: boolean; error?: string; status?: number; }> { try { if (!id) { return { error: '文档ID不能为空', status: 400 }; } if (!userId) { return { error: '用户身份验证失败', status: 401 }; } const response = await postgrestDelete( 'documents', { filter: { 'id': `eq.${id}`, 'user_id': `eq.${userId}` // 确保只能删除自己的文档 } } ); 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, userId: string): Promise<{ data?: DocumentUI; error?: string; status?: number; }> { try { if (!id) { return { error: '文档ID不能为空', status: 400 }; } if (!userId) { return { error: '用户身份验证失败', status: 401 }; } const response = await postgrestGet( 'documents', { filter: { 'id': `eq.${id}`, 'user_id': `eq.${userId}` }, 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 }, userId: string): Promise<{ data?: DocumentUI; error?: string; status?: number; }> { try { if (!id) { return { error: '文档ID不能为空', status: 400 }; } if (!userId) { return { error: '用户身份验证失败', status: 401 }; } // 准备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), user_id: parseInt(userId) // 确保只能更新自己的文档 } ); if (response.error) { console.error('更新文档API错误:', response.error); return { error: response.error, status: response.status }; } // 获取更新后的完整文档数据 const updatedResponse = await getDocument(id, userId); return updatedResponse; } catch (error) { console.error('更新文档信息失败:', error); return { error: error instanceof Error ? error.message : '更新文档信息失败', status: 500 }; } }