import { postgrestGet, postgrestDelete, postgrestPut, postgrestPost } from '../postgrest-client'; import { getDocumentTypes } from '../document-types/document-types'; import { formatDate } from '../../utils'; import { API_BASE_URL } from '~/config/api-config'; import type { DocumentType } from './files-upload'; /** * 从不同格式的 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 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; remark?: string; updatedAt?: string; pageCount?: number; ocrResult?: unknown; // 结果统计字段 pass_count?: number | null; // 通过数量 warning_count?: number | null; // 警告数量 error_count?: number | null; // 错误数量 manual_count?: number | null; // 人工审核数量 // 消息详情字段 warning_messages?: string[]; // 警告消息列表 error_messages?: string[]; // 错误消息列表 manual_messages?: string[]; // 人工审核消息列表 // 版本管理相关字段 historyCount?: number; // 历史版本数量(不含当前版本) previousIssues?: number | null; // 上一个版本的问题数量 previous_pass_count?: number | null; // 上一版本通过数量 previous_warning_count?: number | null; // 上一版本警告数量 previous_error_count?: number | null; // 上一版本错误数量 previous_manual_count?: number | null; // 上一版本人工数量 isExpanded?: boolean; // 是否展开历史版本(前端状态) historyVersions?: DocumentVersionUI[]; // 历史版本列表 } /** * 文档历史版本结构 */ export interface DocumentVersionUI { id: number; name: string; documentNumber: string; type: string; typeName: string; size: number; auditStatus: number; fileStatus: string; issues: number | null; issuesDiff?: number; // 与上一个版本的问题数量差异(绝对值) issuesDiffType?: 'increase' | 'decrease' | 'same'; // 差异类型 uploadTime: string; fileType: string; path: string; isTest: boolean; updatedAt?: string; pageCount?: number; ocrResult?: unknown; versionNumber?: number; // 版本号(v2, v3, v4...) // 结果统计字段 pass_count: number | null; // 通过数量 warning_count: number | null; // 警告数量 error_count: number | null; // 错误数量 manual_count: number | null; // 人工审核数量 previous_pass_count?: number | null; // 上一版本通过数量 previous_warning_count?: number | null; // 上一版本警告数量 previous_error_count?: number | null; // 上一版本错误数量 previous_manual_count?: number | null; // 上一版本人工数量 } /** * 获取文件扩展名 * @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, frontendJWT?: string) { const response = await postgrestGet<[]>('/api/postgrest/proxy/evaluation_results', { filter: { 'document_id': `eq.${id}` }, token: frontendJWT }); if (response.error) { return { error: response.error, status: response.status }; } const evaluationResult = extractApiData<[]>(response.data); return evaluationResult; } /** * 将API文档转换为UI文档 */ async function convertToUIDocument(doc: Document, frontendJWT?: string): Promise { // 获取文档类型信息 const typeResponse = await getDocumentTypes(undefined, frontendJWT); const documentTypes = typeResponse.data?.types || []; const docType = documentTypes.find(type => type.id.toString() === doc.type_id.toString()); const evaluationResult = await getEvaluationResults(doc.id, frontendJWT); 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, remark: doc.remark, 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 id 文档ID * @param userId 用户ID * @param token JWT token (可选) * @returns 删除结果 */ export async function deleteDocument(id: string, userId: string, token?: 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( '/api/postgrest/proxy/documents', { filter: { 'id': `eq.${id}`, 'user_id': `eq.${userId}` // 确保只能删除自己的文档 }, token } ); 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, frontendJWT?: 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( '/api/postgrest/proxy/documents', { filter: { 'id': `eq.${id}`, 'user_id': `eq.${userId}` }, limit: 1, token: frontendJWT } ); 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], frontendJWT); return { data: documentUI }; } catch (error) { console.error('获取文档详情失败:', error); return { error: error instanceof Error ? error.message : '获取文档详情失败', status: 500 }; } } /** * 获取单个文档详情 * @param id 文档ID * @returns 文档详情 */ // export async function getDocumentWithNoUserId(id: string, frontendJWT?: string): Promise<{ // data?: DocumentUI; // error?: string; // status?: number; // }> { // try { // if (!id) { // return { error: '文档ID不能为空', status: 400 }; // } // // console.log("get单个文档id", id) // const response = await postgrestGet( // '/api/postgrest/proxy/documents', // { // filter: { // 'id': `eq.${id}`, // }, // limit: 1, // token: frontendJWT // } // ); // if (response.error) { // return { error: response.error, status: response.status }; // } // // console.log("respose", response) // const extractedData = extractApiData(response.data); // if (!extractedData || extractedData.length === 0) { // return { error: '文档不存在', status: 404 }; // } // // console.log('extractedData', extractedData); // const documentUI = await convertToUIDocument(extractedData[0], frontendJWT); // return { data: documentUI }; // } catch (error) { // console.error('获取文档详情失败:', error); // return { // error: error instanceof Error ? error.message : '获取文档详情失败', // status: 500 // }; // } // } /** * 获取文档类型列表(按IDs过滤版本) * @param ids 文档类型ID数组(必填) * @param frontendJWT JWT token(可选) * @returns 文档类型列表 */ export async function getDocumentTypesByIds(ids: number[], frontendJWT?: string): Promise<{ data?: { types: DocumentType[], total: number }; error?: string; status?: number; }> { try { if (!ids || ids.length === 0) { return { data: { types: [], total: 0 } }; } const response = await postgrestGet( '/api/postgrest/proxy/document_types', { filter: { 'id': `in.(${ids.join(',')})` }, token: frontendJWT }); if (response.error) { return { error: response.error, status: response.status }; } const extractedData = extractApiData(response.data); if (!extractedData) { return { error: '获取文档类型列表失败', status: 500 }; } return { data: { types: extractedData, total: extractedData.length } }; } 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, frontendJWT?: 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>( '/api/postgrest/proxy/documents', apiDocument, { id: parseInt(id), user_id: parseInt(userId) // 确保只能更新自己的文档 }, frontendJWT ); if (response.error) { console.error('更新文档API错误:', response.error); return { error: response.error, status: response.status }; } // 获取更新后的完整文档数据 const updatedResponse = await getDocument(id, userId, frontendJWT); return updatedResponse; } catch (error) { console.error('更新文档信息失败:', error); return { error: error instanceof Error ? error.message : '更新文档信息失败', status: 500 }; } } /** * 获取文档列表(使用新的后端API) * @param searchParams 搜索参数 * @returns 文档列表和总数 */ export async function getDocumentsListFromAPI(searchParams: { page?: number; pageSize?: number; name?: string; documentNumber?: string; documentTypeIds?: number[]; // 文档类型ID数组 auditStatus?: string; fileStatus?: string; dateFrom?: string; dateTo?: string; token: string; // JWT token (必填) }): Promise<{ data?: { documents: DocumentUI[], total: number, page: number, totalPages: number }; error?: string; status?: number; }> { try { const { page = 1, pageSize = 10, name, documentNumber, documentTypeIds, auditStatus, fileStatus, dateFrom, dateTo, token } = searchParams; // 构建查询参数 const params: Record = { page, page_size: pageSize }; // 添加可选参数 if (name) params.name = name; if (documentNumber) params.document_number = documentNumber; if (auditStatus) params.audit_status = parseInt(auditStatus, 10); if (fileStatus) params.status = fileStatus; if (dateFrom) params.start_time = dateFrom; if (dateTo) params.end_time = dateTo; // 处理文档类型ID数组 - 转换为逗号分隔的字符串 if (documentTypeIds && documentTypeIds.length > 0) { params.type_id = documentTypeIds.join(','); } // console.log('📤 [getDocumentsListFromAPI] 请求参数:', params); // 调用后端API const axios = await import('axios').then(m => m.default); const response = await axios.get(`${API_BASE_URL}/admin/versions/documents-list`, { params, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }); const data = response.data; const backendDocuments = data.documents || []; const totalCount = data.total || 0; const totalPages = data.total_pages || 0; // console.log(`📥 [getDocumentsListFromAPI] 获取到 ${backendDocuments.length} 个文档,总数: ${totalCount}`); // 转换后端数据为前端 DocumentUI 格式 const convertedDocuments: DocumentUI[] = backendDocuments.map((doc: any) => { // 转换历史版本数据 const historyVersions: DocumentVersionUI[] = (doc.history_versions || []).map((hv: any, index: number) => { // 计算与下一个版本(更早的版本)的问题数量差异 let issuesDiff: number | undefined; let issuesDiffType: 'increase' | 'decrease' | 'same' | undefined; if (index < doc.history_versions.length - 1) { const olderDoc = doc.history_versions[index + 1]; if (hv.issue_count != null && olderDoc.issue_count != null) { const diff = hv.issue_count - olderDoc.issue_count; issuesDiff = Math.abs(diff); if (diff > 0) { issuesDiffType = 'increase'; } else if (diff < 0) { issuesDiffType = 'decrease'; } else { issuesDiffType = 'same'; } } } // 获取前一个版本的统计数据(如果存在) const prevVersion = index < doc.history_versions.length - 1 ? doc.history_versions[index + 1] : null; return { id: hv.id, name: hv.name || doc.name, documentNumber: hv.document_number || doc.document_number || '', type: hv.type_id?.toString() || doc.type_id?.toString() || '', typeName: hv.type_name || doc.type_name || '未知类型', size: hv.file_size || 0, auditStatus: hv.audit_status ?? 0, fileStatus: hv.status || 'Processed', issues: hv.issue_count ?? null, issuesDiff, issuesDiffType, uploadTime: formatDate(hv.created_at), fileType: getFileExtension(hv.name || doc.name), path: hv.path || '', isTest: hv.is_test_document || false, updatedAt: formatDate(hv.updated_at || hv.created_at), pageCount: hv.ocr_result?.__meta?.page_count || 0, ocrResult: hv.ocr_result, versionNumber: hv.version_number, // 结果统计字段 pass_count: hv.pass_count ?? null, warning_count: hv.warning_count ?? null, error_count: hv.error_count ?? null, manual_count: hv.manual_count ?? null, // 前一版本统计(用于差异对比) previous_pass_count: prevVersion?.pass_count ?? null, previous_warning_count: prevVersion?.warning_count ?? null, previous_error_count: prevVersion?.error_count ?? null, previous_manual_count: prevVersion?.manual_count ?? null }; }); return { id: doc.id, name: doc.name, documentNumber: doc.document_number || '', type: doc.type_id?.toString() || '', typeName: doc.type_name || '未知类型', size: doc.file_size || 0, auditStatus: doc.audit_status ?? 0, fileStatus: doc.status || '', issues: doc.issue_count ?? null, uploadTime: formatDate(doc.upload_time || doc.created_at), fileType: getFileExtension(doc.name), path: doc.path || '', isTest: doc.is_test_document || false, updatedAt: formatDate(doc.updated_at || doc.created_at), pageCount: doc.ocr_result?.__meta?.page_count || 0, ocrResult: doc.ocr_result, // 结果统计字段 pass_count: doc.pass_count ?? null, warning_count: doc.warning_count ?? null, error_count: doc.error_count ?? null, manual_count: doc.manual_count ?? null, // 消息详情字段 warning_messages: doc.warning_messages || [], error_messages: doc.error_messages || [], manual_messages: doc.manual_messages || [], // 版本管理字段 historyCount: (doc.total_versions || 1) - 1, // 总版本数 - 1 = 历史版本数 previousIssues: doc.history_versions?.[0]?.issue_count ?? null, previous_pass_count: doc.history_versions?.[0]?.pass_count ?? null, previous_warning_count: doc.history_versions?.[0]?.warning_count ?? null, previous_error_count: doc.history_versions?.[0]?.error_count ?? null, previous_manual_count: doc.history_versions?.[0]?.manual_count ?? null, historyVersions: historyVersions.length > 0 ? historyVersions : undefined }; }); return { data: { documents: convertedDocuments, total: totalCount, page: data.page || page, totalPages } }; } catch (error) { console.error('❌ [getDocumentsListFromAPI] 获取文档列表失败:', error); // 处理axios错误 if (error && typeof error === 'object' && 'response' in error) { const axiosError = error as { response?: { data?: any; status?: number; statusText?: string } }; return { error: axiosError.response?.data?.message || axiosError.response?.statusText || '获取文档列表失败', status: axiosError.response?.status || 500 }; } return { error: error instanceof Error ? error.message : '获取文档列表失败', status: 500 }; } }