添加登录内容,尚未完善,先创建分支

This commit is contained in:
2025-07-14 12:31:43 +08:00
parent e3109423d4
commit fff474f46b
25 changed files with 2693 additions and 1102 deletions
+96 -112
View File
@@ -1,4 +1,4 @@
import { postgrestGet, postgrestDelete, postgrestPut, type PostgrestParams } from '../postgrest-client';
import { postgrestGet, postgrestDelete, postgrestPut, postgrestPost } from '../postgrest-client';
import { getDocumentTypes } from '../document-types/document-types';
import { formatDate } from '../../utils';
@@ -169,6 +169,29 @@ async function convertToUIDocument(doc: Document): Promise<DocumentUI> {
};
}
/**
* 后端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 搜索参数
@@ -180,124 +203,85 @@ export async function getDocuments(searchParams: DocumentSearchParams = {}): Pro
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<string, string>
};
// 添加筛选条件
const filter: Record<string, string> = {};
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) {
// 处理"待审核"状态 - 特殊处理 audit_status = 0 的情况,同时包含 null 值
if (searchParams.auditStatus === '0') {
filter['or'] = `(audit_status.eq.0,audit_status.is.null)`;
} else {
filter['audit_status'] = `eq.${searchParams.auditStatus}`;
}
}
if (searchParams.fileStatus) {
filter['status'] = `eq.${searchParams.fileStatus}`;
}
// 处理日期范围
if (searchParams.dateFrom) {
// 添加当天开始时间 00:00:00
filter['updated_at'] = `gte.${searchParams.dateFrom + ' 00:00:00'}`;
}
if (searchParams.dateTo) {
// 如果有开始日期,使用and条件;否则直接设置结束日期
const dateToKey = searchParams.dateFrom ? 'and' : 'updated_at';
// 添加当天结束时间 23:59:59
if (dateToKey === 'and') {
delete filter['updated_at'];
// 使用OR操作符连接两个条件
filter[dateToKey] = `(updated_at.gte.${searchParams.dateFrom+' 00:00:00'},updated_at.lte.${searchParams.dateTo+' 23:59:59'})`;
} else {
filter['updated_at'] = `lte.${searchParams.dateTo+' 23:59:59'}`;
}
}
// 根据 reviewType 添加过滤条件
if (searchParams.reviewType) {
// 如果已经有文档类型过滤,则不再添加 reviewType 的过滤
if (!searchParams.documentType) {
if (searchParams.reviewType === 'contract') {
// 如果是合同类型,只显示 type_id=1 的文档
filter['type_id'] = 'eq.1';
} else if (searchParams.reviewType === 'record') {
// 如果是卷宗类型,只显示 type_id=2 或 type_id=3 的文档
filter['type_id'] = 'in.(2,3)';
}
}
}
// console.log('filter-----', filter);
params.filter = filter;
// 发送请求
const response = await postgrestGet<Document[]>('documents', params);
if (response.error) {
return { error: response.error, status: response.status };
}
// 提取数据
const extractedData = extractApiData<Document[]>(response.data);
if (!extractedData) {
return { error: '获取文档数据失败', status: 500 };
}
// console.log('extractedData---1--',extractedData[0]);
// 准备RPC调用参数
const {
page = 1,
pageSize = 10,
name,
documentNumber,
documentType,
auditStatus,
fileStatus,
dateFrom,
dateTo,
reviewType
} = searchParams;
// 转换为UI格式
const documents = await Promise.all(extractedData.map(convertToUIDocument));
// console.log('documentsItem',documents)
// 获取总数
let totalCount = 0;
const responseWithHeaders = response as {
data: unknown;
headers?: Record<string, string>
};
if (responseWithHeaders.headers) {
const rangeHeader = responseWithHeaders.headers['content-range'];
if (rangeHeader) {
const total = rangeHeader.split('/')[1];
if (total !== '*') {
totalCount = parseInt(total, 10);
}
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];
}
}
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,
};
// 并行执行获取数据和获取总数的请求
const [documentsResponse, countResponse] = await Promise.all([
postgrestPost<DocumentFromSQL[], unknown>('rpc/get_documents_with_filters', { ...rpcParams, page, page_size: pageSize }),
postgrestPost<number, unknown>('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 || documents.length
total: totalCount
}
};
} catch (error) {
+205 -30
View File
@@ -70,6 +70,19 @@ export interface Document {
audit_status?: number;
}
// 合同结构比较表接口
export interface ContractStructureComparison {
id: number;
template_contract_name: string;
file_size: number;
status: DocumentStatus;
created_at: string;
document_id?: number;
template_contract_path?: string;
ocr_results?: Record<string, unknown>;
comparison_results?: Record<string, unknown>;
}
// 文件上传响应接口
export interface FileUploadResponse {
success: boolean;
@@ -118,6 +131,7 @@ export async function uploadFileToBinary(file: File): Promise<ArrayBuffer> {
* @param documentNumber 文档编号(可选)
* @param remark 备注信息(可选)
* @param isTestDocument 是否为测试文档
* @param documentId 关联的文档ID(用于合同附件上传)
* @returns 上传结果
*/
export async function uploadDocumentToServer(
@@ -128,7 +142,9 @@ export async function uploadDocumentToServer(
priority: string,
documentNumber?: string | null,
remark?: string | null,
isTestDocument: boolean = false
isTestDocument: boolean = false,
documentId?: number | null,
isReupload: boolean = false
): Promise<{data: FileUploadResponse; error?: never} | {data?: never; error: string; status?: number}> {
try {
// console.log('【调试】开始上传文档:', { fileName, fileSize: binaryData.byteLength });
@@ -147,22 +163,25 @@ export async function uploadDocumentToServer(
evaluation_level: priority,
document_number: documentNumber || null,
remark: remark || null,
is_test_document: isTestDocument
is_test_document: isTestDocument,
document_id: documentId || null,
is_reupload: isReupload
};
// 添加JSON字符串到FormData
formData.append('upload_info', JSON.stringify(uploadInfo));
// console.log('【调试】FormData准备完成:', JSON.stringify(uploadInfo));
// console.log('【调试】准备发送请求到服务器:', UPLOAD_URL);
// 根据是否有documentId决定使用哪个接口
const uploadEndpoint = documentId ? '/upload_contract_template' : '/upload';
const uploadUrl = UPLOAD_URL + uploadEndpoint;
// console.log('【调试】准备发送请求到服务器:', uploadUrl);
// 发送请求
// const response = await fetch(`${API_BASE_URL}/admin/documents/upload`, {
try {
// console.log('【调试】开始fetch请求...');
const response = await fetch(UPLOAD_URL, {
// const response = await fetch('http://172.16.0.55:8000/admin/documents/upload', {
// const response = await fetch('http://172.16.0.119:8000/admin/documents/upload', {
const response = await fetch(uploadUrl, {
method: 'POST',
headers: {
'X-File-Name': encodeURIComponent(fileName)
@@ -230,6 +249,126 @@ export async function getTodayDocuments(reviewType?: string): Promise<{data: Doc
const today = dayjs().startOf('day').format('YYYY-MM-DD');
// console.log('查询当天文档,日期范围:', today);
// 如果是合同类型,需要合并查询documents表和contract_structure_comparison表
if (reviewType === 'contract') {
try {
// 查询documents表中的合同数据
const documentsParams: PostgrestParams = {
select: `
id,
name,
type_id,
file_size,
status,
created_at,
document_number,
path,
storage_type,
is_test_document,
evaluation_level,
ocr_result,
extracted_results,
sumary,
remark,
audit_status
`,
order: 'created_at.desc',
filter: {
'created_at': `gte.${today}`,
'type_id': 'eq.1'
}
};
// 查询contract_structure_comparison表中的数据
// const comparisonParams: PostgrestParams = {
// select: `
// id,
// template_contract_name,
// file_size,
// status,
// created_at,
// document_id,
// template_contract_path,
// ocr_results,
// comparison_results
// `,
// order: 'created_at.desc',
// filter: {
// 'created_at': `gte.${today}`
// }
// };
// 并行查询两个表
// const [documentsResponse, comparisonResponse] = await Promise.all([
// postgrestGet<Document[]>('documents', documentsParams),
// postgrestGet<ContractStructureComparison[]>('contract_structure_comparison', comparisonParams)
// ]);
const documentsResponse = await postgrestGet<Document[]>('documents', documentsParams);
// console.log('documents表响应:', documentsResponse);
// console.log('contract_structure_comparison表响应:', comparisonResponse);
// if (documentsResponse.error && comparisonResponse.error) {
// console.error('两个表查询都失败:', documentsResponse.error, comparisonResponse.error);
// return { error: documentsResponse.error || comparisonResponse.error, status: documentsResponse.status || comparisonResponse.status };
// }
if (documentsResponse.error) {
console.error('documents表查询失败:', documentsResponse.error);
return { error: documentsResponse.error, status: documentsResponse.status };
}
// 提取documents表数据
let documentsData: Document[] = [];
if (!documentsResponse.error && documentsResponse.data) {
const extractedDocuments = extractApiData<Document[]>(documentsResponse.data);
if (extractedDocuments) {
documentsData = extractedDocuments;
}
}
// 提取contract_structure_comparison表数据并转换为Document格式
// let comparisonData: Document[] = [];
// if (!comparisonResponse.error && comparisonResponse.data) {
// const extractedComparison = extractApiData<ContractStructureComparison[]>(comparisonResponse.data);
// if (extractedComparison) {
// // 将ContractStructureComparison转换为Document格式
// console.log('extractedComparison:', extractedComparison);
// comparisonData = extractedComparison.map(item => ({
// id: item.id,
// name: item.template_contract_name || `合同结构比较记录_${item.id}`,
// type_id: 1, // 合同结构比较默认为合同类型
// file_size: item.file_size || 0,
// status: item.status,
// created_at: item.created_at,
// document_id: item.document_id,
// template_contract_path: item.template_contract_path,
// ocr_results: item.ocr_results,
// comparison_results: item.comparison_results
// }));
// }
// }
// 合并两个数据源
// const allData = [...documentsData, ...comparisonData];
const allData = [...documentsData];
// 按created_at降序排序
allData.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
// console.log('合并后的数据:', allData);
return { data: allData };
} catch (contractError) {
console.error('合同类型查询失败:', contractError);
return {
error: contractError instanceof Error ? contractError.message : '合同类型查询失败',
status: 500
};
}
}
// 非合同类型的原有逻辑
const params: PostgrestParams = {
select: `
id,
@@ -256,14 +395,7 @@ export async function getTodayDocuments(reviewType?: string): Promise<{data: Doc
};
// 根据reviewType添加过滤条件
if (reviewType === 'contract') {
// 如果是合同类型,只显示type_id=1的文档
if (params.filter) {
params.filter['type_id'] = 'eq.1';
} else {
params.filter = { 'type_id': 'eq.1' };
}
} else if (reviewType === 'record') {
if (reviewType === 'record') {
// 如果是卷宗类型,只显示type_id=2或type_id=3的文档
if (params.filter) {
params.filter['type_id'] = 'in.(2,3)';
@@ -352,33 +484,76 @@ export async function getDocumentTypes(reviewType?: string): Promise<{data: Docu
/**
* 获取指定文档的状态
* @param documentIds 文档ID列表
* @param attachmentIds 合同附件ID列表(可选)
* @returns 文档状态列表
*/
export async function getDocumentsStatus(documentIds: number[]): Promise<{data: Document[]; error?: never} | {data?: never; error: string; status?: number}> {
export async function getDocumentsStatus(
documentIds: number[],
attachmentIds?: number[]
): Promise<{data: Document[]; error?: never} | {data?: never; error: string; status?: number}> {
try {
if (!documentIds || documentIds.length === 0) {
if ((!documentIds || documentIds.length === 0) && (!attachmentIds || attachmentIds.length === 0)) {
return { data: [] };
}
const params: PostgrestParams = {
select: 'id, status',
filter: {
'id': `in.(${documentIds.join(',')})`
// 查询主文档状态
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let documentsResponse: any = { data: [], error: undefined, status: undefined };
if (documentIds && documentIds.length > 0) {
const documentsParams: PostgrestParams = {
select: 'id, status',
filter: {
'id': `in.(${documentIds.join(',')})`
}
};
documentsResponse = await postgrestGet<Document[]>('documents', documentsParams);
}
// 查询合同附件状态
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let attachmentResponse: any = { data: [], error: undefined, status: undefined };
if (attachmentIds && attachmentIds.length > 0) {
const attachmentParams: PostgrestParams = {
select: 'id, status',
filter: {
'id': `in.(${attachmentIds.join(',')})`
}
};
attachmentResponse = await postgrestGet<ContractStructureComparison[]>('contract_structure_comparison', attachmentParams);
}
if (documentsResponse.error && attachmentResponse.error) {
return { error: documentsResponse.error || attachmentResponse.error, status: documentsResponse.status || attachmentResponse.status };
}
let allData: Document[] = [];
// 处理主文档数据
if (!documentsResponse.error && documentsResponse.data) {
const extractedDocuments = extractApiData<Document[]>(documentsResponse.data);
if (extractedDocuments) {
allData = [...allData, ...extractedDocuments];
}
};
const response = await postgrestGet<Document[]>('documents', params);
if (response.error) {
return { error: response.error, status: response.status };
}
const extractedData = extractApiData<Document[]>(response.data);
if (!extractedData) {
return { error: '获取数据失败', status: 500 };
// 处理合同附件数据
if (!attachmentResponse.error && attachmentResponse.data) {
const extractedAttachments = extractApiData<ContractStructureComparison[]>(attachmentResponse.data);
if (extractedAttachments) {
// 将ContractStructureComparison转换为Document格式
const convertedAttachments: Document[] = extractedAttachments.map(item => ({
id: item.id,
name: item.template_contract_name || `合同结构比较记录_${item.id}`,
type_id: 1,
file_size: item.file_size || 0,
status: item.status,
created_at: item.created_at
}));
allData = [...allData, ...convertedAttachments];
}
}
return { data: extractedData };
return { data: allData };
} catch (error) {
console.error('获取文档状态失败:', error);
return {