/** * 交叉评查详情页面 * * 功能概述: * - 显示文档交叉评查结果和详细信息 * - 支持查看文档内容及评查点高亮标记 * - 提供评查点列表,分为通过、警告和错误三种类型 * - 支持评查点处理,如一键替换、人工审核等功能 * - 支持导出评查报告和下载原文件 * * 组件结构: * - FileInfo: 显示文件基本信息和操作按钮 * - FilePreview: 文档预览组件,显示文档内容及高亮问题 * - ReviewPointsList: 评查点列表组件,显示所有评查结果 * * 数据流转: * 1. 页面加载时从API获取评查详情数据 * 2. 根据评查点ID关联文档中的高亮区域 * 3. 点击评查点时在文档中定位对应位置 * 4. 处理评查点时更新状态并反馈到UI * * @author 中国烟草AI合同及卷宗审核系统开发团队 */ import { type MetaFunction, type LoaderFunctionArgs, type ActionFunctionArgs } from "@remix-run/node"; import React, { useState, useEffect, useCallback, useRef, useMemo } from "react"; import { useNavigate, useLoaderData } from "@remix-run/react"; import crossCheckingStyles from "~/styles/cross-checking-result.css?url"; import { getReviewPoints, updateReviewResult} from "~/api/evaluation_points/reviews"; import { toastService } from "~/components/ui/Toast"; import { confirmReviewResults, checkProposalVotes, findIsProposer } from "~/api/cross-checking/cross-file-result"; // 导入交叉评查详情页面组件 import { FileInfo, FilePreview, ReviewPointsList } from "~/components/cross-checking"; // 从ReviewPointsList组件中导入ReviewPoint类型 import { type ReviewPoint } from '~/components/cross-checking'; import { messageService } from "~/components/ui/MessageModal"; import { loadingBarService } from "~/components/ui/LoadingBar"; import { Breadcrumb } from "~/components/layout/Breadcrumb"; // 定义评分提案数据接口(与reviews.ts中保持一致) interface ScoringProposal { id: string | number; evaluation_result_id: string | number; proposer_id: string | number; proposed_score: number; reason: string; status: string; created_at: string; updated_at: string; document_id: string | number; } /** * 文件信息组件 * 显示文件名称、状态信息以及操作按钮(下载原文件、导出评查报告、确认评查结果) */ // 格式化文件大小 const formatFileSize = (bytes: number) => { if (bytes === 0) return "0 Bytes"; const k = 1024; const sizes = ["Bytes", "KB", "MB", "GB", "TB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; }; // 定义统计数据类型 interface Statistics { total: number; success: number; warning: number; error: number; score: number; } // 定义文件信息类型 interface FileInfo { fileName: string; contractNumber: string; fileSize: string; fileFormat: string; pageCount: number; uploadTime: string; uploadUser: string; auditStatus: number; fileType: string; // 文件类型(1:合同,2:卷宗等) } // 定义合同信息类型 interface ContractInfo { contractType: string; signDate: string; parties: { partyA: string; partyB: string; }; amount: string; period: string; } // 定义评查信息类型 interface ReviewInfo { reviewTime: string; reviewModel: string; ruleGroup: string; result: string; issueCount: number; } // 定义文档内容类型 interface FileContent { title: string; contractNumber: string; parties: { partyA: { name: string; address: string; representative: string; phone: string; }; partyB: { name: string; address: string; representative: string; phone: string; }; }; sections: { title: string; content: string; }[]; } // 定义分析项类型 interface AnalysisItem { title: string; content: string; description: string; } // 定义分析数据类型 interface AnalysisData { riskAlerts: AnalysisItem[]; suggestions: AnalysisItem[]; summary: string; } // 定义评查数据类型 interface ReviewData { fileInfo: FileInfo; contractInfo: ContractInfo; reviewInfo: ReviewInfo; statistics: Statistics; fileContent: FileContent; reviewPoints: ReviewPoint[]; aiAnalysis: AnalysisData; } export const meta: MetaFunction = () => { return [ { title: "交叉评查详情 - 中国烟草AI合同及卷宗审核系统" }, { name: "description", content: "查看文档交叉评查结果,处理问题点,确认评查结果" } ]; }; export function links() { return [{ rel: "stylesheet", href: crossCheckingStyles }]; } export const handle = { hideBreadcrumb: true }; export async function loader({ request }: LoaderFunctionArgs) { try { const url = new URL(request.url); const id = url.searchParams.get('id') || undefined; const taskId = url.searchParams.get('tId') || undefined; const taskName = url.searchParams.get('tName') || undefined; const previousRoute = url.searchParams.get('previousRoute') || ''; // console.log("id-------",id); if (!id) { return Response.json({ result: false, message: '文件ID不能为空' }); } if (!taskId) { return Response.json({ result: false, message: '任务ID不能为空' }); } // 获取用户会话信息 const { getUserSession } = await import("~/api/login/auth.server"); const { userInfo, frontendJWT } = await getUserSession(request); // 获取评查点数据,传递request对象 const reviewData = await getReviewPoints(id, request); // 获取当前登录用户是否是发起人 const isProposer = await findIsProposer(taskId, userInfo?.user_id, frontendJWT); // console.log("documentData-------",JSON.stringify(documentData.data,null,2)); // console.log("reviewData-------",JSON.stringify('data' in reviewData ? reviewData.data : '',null,2)); // console.log("reviewData-------",JSON.stringify(reviewData,null,2)); if ('error' in reviewData && reviewData.error) { console.error("获取评查点数据错误:", reviewData.error); return Response.json({ result: false, message: reviewData.error }); } // 确保reviewData有效且具有预期的属性 if ('document' in reviewData && 'data' in reviewData && 'reviewInfo' in reviewData && 'stats' in reviewData) { // console.log("reviewData-------",JSON.stringify(reviewData.data)); return Response.json({ previousRoute: previousRoute, document: reviewData.document, reviewPoints: reviewData.data, reviewInfo: reviewData.reviewInfo, statistics: reviewData.stats, comparison_document: reviewData.comparison_document, scoring_proposals: reviewData.scoring_proposals || [], userInfo: userInfo, jwtToken: frontendJWT, // 传递JWT token isProposer: isProposer, taskId: taskId, // 传递任务ID taskName: taskName // 传递任务名称 }); } else { console.error("返回的评查数据格式不正确",JSON.stringify(reviewData,null,2)); return Response.json({ result: false, message: '返回的评查数据格式不正确' }); } } catch (error) { console.error('获取评查数据失败:', error); return Response.json({ result: false, message: '获取评查数据失败' }); } } // 添加 action 函数处理需要用户认证的操作 export async function action({ request }: ActionFunctionArgs) { const formData = await request.formData(); const intent = formData.get("intent") as string; try { // 获取用户会话信息 const { getUserSession } = await import("~/api/login/auth.server"); const { userInfo, frontendJWT } = await getUserSession(request); if (intent === "updateReviewResult") { const reviewPointResultId = formData.get("reviewPointResultId") as string; const editAuditStatusId = formData.get("editAuditStatusId") as string; const result = formData.get("result") as string; const message = formData.get("message") as string; const response = await updateReviewResult(reviewPointResultId, editAuditStatusId, result, message, request); if (response.error) { return Response.json({ success: false, error: response.error }, { status: response.status || 500 }); } return Response.json({ success: true, data: response.data }); } if (intent === "getCrossCheckingOpinions") { const { getCrossCheckingOpinions } = await import("~/api/cross-checking/cross-file-result"); const documentId = formData.get("documentId") as string; const page = parseInt(formData.get("page") as string || "1", 10); const pageSize = parseInt(formData.get("pageSize") as string || "10", 10); const userId = userInfo?.user_id; const response = await getCrossCheckingOpinions(documentId, page, pageSize, userId, frontendJWT); if (response.error) { return Response.json({ success: false, error: response.error }, { status: response.status || 500 }); } return Response.json({ success: true, data: response.data }); } if (intent === "confirmReviewResults") { // 检查文档下提案是否存在未投票用户,首先先打开一个模态框,提示用户是否确认完成评查,如果用户点击确认,则调用confirmReviewResults接口,如果用户点击取消,则关闭模态框 // 模态框内的数据需要根据checkProposalVotes返回回来的数据进行显示,如果存在未投票用户,则提示用户存在未投票用户,如果不存在未投票用户,则提示用户完成评查 } } catch (error) { console.error('Action处理失败:', error); return Response.json({ success: false, error: error instanceof Error ? error.message : '操作失败' }, { status: 500 }); } } export default function CrossCheckingResult() { console.log('[组件] CrossCheckingResult 渲染'); const navigate = useNavigate(); const loaderData = useLoaderData(); const { document, reviewPoints, statistics, reviewInfo, scoring_proposals, jwtToken, userInfo, isProposer, taskId, taskName } = loaderData; const [isLoading, setIsLoading] = useState(false); // 已经通过loader加载了数据,不需要再显示加载状态 const [reviewData, setReviewData] = useState(null); const [activeReviewPointResultId, setActiveReviewPointResultId] = useState(null); const [targetPage, setTargetPage] = useState(undefined); const [localScoringProposals, setLocalScoringProposals] = useState(scoring_proposals || []); // 本地状态管理scoringProposals // 使用ref来跟踪loading状态,避免不必要的重新渲染 const isProcessingRef = useRef(false); // 添加组件挂载/卸载日志 useEffect(() => { console.log('[组件] CrossCheckingResult 挂载'); return () => { console.log('[组件] CrossCheckingResult 卸载'); }; }, []); // 同步外部scoring_proposals到本地状态 useEffect(() => { setLocalScoringProposals(scoring_proposals || []); }, [scoring_proposals]); // 处理意见提交成功的回调 const handleOpinionSubmitted = useCallback((newProposal: ScoringProposal) => { setLocalScoringProposals(prev => [...prev, newProposal]); }, []); // loader 数据加载出错 useEffect(()=>{ loadingBarService.hide(); if(Object.keys(loaderData).find(key => key === 'result') && !loaderData.result){ messageService.show({ title: '错误', message: loaderData.message, type: 'error', confirmText: '确定', cancelText: '', onConfirm: () => { navigate(-1); } }) } },[loaderData, navigate]); // 模拟获取评查数据 useEffect(() => { if (!document) return; // 构建文件信息对象 const fileInfo = { fileName: document.name || "未知文件名", path: document.path || "未知路径", contractNumber: document.documentNumber || "未知编号", fileSize: document.size ? formatFileSize(document.size) : "未知大小", // 文件格式类型 fileFormat: document.fileType ? document.fileType.toUpperCase() : "未知格式", pageCount: document.pageCount || 0, uploadTime: document.uploadTime || "未知时间", uploadUser: document.uploadUser || "未知用户", auditStatus: document.auditStatus || 0, legalBasis: document.legalBasis || {}, // 文件类型(1:合同,2:卷宗。。。) fileType: document.type || "" }; // 创建包含真实文档数据的评查数据对象 const reviewDataObj: ReviewData = { // 使用真实文件信息 fileInfo: fileInfo, // 其他字段暂时使用默认值 contractInfo: getMockReviewData().contractInfo, reviewInfo: reviewInfo, statistics: statistics, fileContent: getMockReviewData().fileContent, reviewPoints: reviewPoints, aiAnalysis: getMockReviewData().aiAnalysis, }; setReviewData(reviewDataObj); setIsLoading(false); }, [document, reviewPoints, statistics, reviewInfo]); const handleReviewPointSelect = useCallback((reviewPointId: string, page?: number) => { // 如果点击的是相同的评查点,但有page参数,先重置targetPage以确保useEffect能够触发 if (reviewPointId === activeReviewPointResultId && page) { setTargetPage(undefined); // 使用setTimeout确保状态更新后再设置新的targetPage setTimeout(() => { setActiveReviewPointResultId(reviewPointId); setTargetPage(page); }, 0); } else { // 正常设置activeReviewPointId和targetPage setActiveReviewPointResultId(reviewPointId); setTargetPage(page); } }, [activeReviewPointResultId]); // 处理评审点状态变更 const handleReviewPointStatusChange = async (reviewPointResultId: string, editAuditStatusId: string | number, newStatus: string, message: string) => { // 将字符串的布尔值转换为布尔类型 let boolResult = 'review'; if(newStatus !== 'review'){ boolResult = newStatus === 'true' ? 'true' : 'false'; } try { // 使用 fetch 调用 action const formData = new FormData(); formData.append("intent", "updateReviewResult"); formData.append("reviewPointResultId", reviewPointResultId); formData.append("editAuditStatusId", editAuditStatusId.toString()); formData.append("result", boolResult); formData.append("message", message); const response = await fetch(window.location.pathname, { method: "POST", body: formData, }); const result = await response.json(); if (!result.success) { console.error('更新评查结果失败:', result.error); toastService.error(`更新评查结果失败: ${result.error}`); return; } // console.log('评查点状态更新成功:', { // id: reviewPointResultId, // result: boolResult, // message: message // }); // 更新本地状态 if (reviewData) { // 找到要更新的评查点和它的原始状态 const reviewPointToUpdate = reviewData.reviewPoints.find(point => point.id === reviewPointResultId); const oldStatus = reviewPointToUpdate?.status || ''; const wasSuccess = reviewPointToUpdate?.result === true; const newIsSuccess = newStatus === 'true'; // 更新评查点 const updatedReviewPoints = reviewData.reviewPoints.map(point => point.id === reviewPointResultId ? { ...point, result: newStatus === 'true' ? true : (newStatus === 'false' ? false : point.result), editAuditStatus: boolResult === 'review' ? 0 : 1, title: boolResult === 'review' ? point.title : message, editAuditStatusMessage: boolResult === 'review' ? point.editAuditStatusMessage : message } : point ); // 更新统计数据 const updatedStatistics = { ...reviewData.statistics }; // 只处理结果实际变化的情况,即从通过变为不通过,或从不通过变为通过 if (newStatus !== 'review' && wasSuccess !== newIsSuccess) { if (newIsSuccess) { // 从不通过变为通过 updatedStatistics.success += 1; // 减少对应的错误或警告数量 if (oldStatus === 'warning') { updatedStatistics.warning = Math.max(0, updatedStatistics.warning - 1); } else if (oldStatus === 'error') { updatedStatistics.error = Math.max(0, updatedStatistics.error - 1); } } else { // 从通过变为不通过 updatedStatistics.success = Math.max(0, updatedStatistics.success - 1); // 增加对应的错误或警告数量 if (oldStatus === 'warning') { updatedStatistics.warning += 1; } else if (oldStatus === 'error') { updatedStatistics.error += 1; } } } // 更新 UI 状态 setReviewData({ ...reviewData, reviewPoints: updatedReviewPoints, statistics: updatedStatistics }); // 显示成功消息 if(document && document.id && newStatus !== 'review'){ toastService.success('评查点状态已更新'); } // console.log("newReviewPoints",updatedReviewPoints); // 如果是review操作才调用API刷新 // if (document && document.id && newStatus === 'review') { // await refreshReviewData(document.id.toString()); // } } } catch (error) { console.error('更新评查结果出错:', error); toastService.error('更新评查结果失败,请稍后重试'); } }; /** * 处理确认评查结果 * 1. 检查未投票提案 * 2. 根据结果弹出确认模态框 * 3. 用户确认后更新文档状态并跳转 */ const handleConfirmResults = useCallback(async (event?: React.MouseEvent) => { console.log('[完成评查] 开始处理'); // 首先阻止默认行为和事件冒泡,防止页面刷新 if (event) { event.preventDefault(); event.stopPropagation(); } if (!document || !document.id) { toastService.error('文档数据不完整,无法确认评查结果'); return; } // 使用ref防止重复点击,避免触发状态更新 if (isProcessingRef.current) { console.log('[完成评查] 正在处理中,跳过'); return; } try { console.log('[完成评查] 标记为处理中'); isProcessingRef.current = true; // 1. 先检查未投票(不触发loading状态更新,避免重新渲染) console.log('[完成评查] 开始检查未投票提案'); const checkRes = await checkProposalVotes(document.id, jwtToken); console.log("[完成评查] 检查结果:", checkRes); if (checkRes.error) { toastService.error(checkRes.error); isProcessingRef.current = false; return; } // 2. 解析返回数据,定义明确的类型 interface CheckProposalResponse { success: boolean; message: string; data: { pending_proposals: Array<{ evaluation_point_name: string; pending_voters_num: number; }>; }; } const responseData = checkRes.data as CheckProposalResponse; const pendingProposals = responseData?.data?.pending_proposals || []; // console.log("pendingProposals", pendingProposals); // 3. 构建模态框消息 let modalMessage: string = ''; if (Array.isArray(pendingProposals) && pendingProposals.length > 0) { modalMessage = pendingProposals.map((item) => `评查名称为:${item.evaluation_point_name} 还剩余 ${item.pending_voters_num}人未投票。` ).join('\n'); } else { modalMessage = '是否完成评查?'; } // 4. 重置处理状态标记,准备显示模态框(不触发状态更新) console.log('[完成评查] 重置处理标记,准备显示模态框'); isProcessingRef.current = false; // 5. 弹出模态框 console.log('[完成评查] 显示确认模态框'); messageService.show({ title: '提示', message: modalMessage, type: 'warning', confirmText: '确认', cancelText: '取消', onConfirm: async () => { // 用户点击确认后才开始处理,此时才显示loading console.log('[完成评查] 用户点击确认,开始更新状态'); setIsLoading(true); try { const res = await confirmReviewResults(document.id, jwtToken); if (res.error) { toastService.error(res.error); setIsLoading(false); return; } toastService.success('评查结果已确认,文档审核状态已更新'); // 跳转到交叉评查列表页,并带上任务信息以自动打开模态框 const params = new URLSearchParams({ openModal: 'true', taskId: taskId || '', taskName: taskName || '任务详情' }); navigate(`/cross-checking?${params.toString()}`); } catch (error) { console.error('确认评查结果失败:', error); toastService.error('确认评查结果失败,请重试'); setIsLoading(false); } }, // onCancel: () => { // // 用户取消时不需要做任何处理 // console.log('[完成评查] 用户取消了确认操作'); // } }); } catch (error) { isProcessingRef.current = false; toastService.error(`确认评查结果失败: ${error instanceof Error ? error.message : '未知错误'}`); } }, [document, jwtToken, navigate]); // 构建自定义面包屑项 - 使用 useMemo 缓存 const breadcrumbItems = useMemo(() => { const items = [ { title: "交叉评查详情", to: `/cross-checking/result?id=${document?.id}` } ]; // 添加前置路由 if (loaderData.previousRoute) { if (loaderData.previousRoute === 'crossChecking') { items.unshift({ title: "交叉评查", to: "/cross-checking" }); } } return items; }, [document?.id, loaderData.previousRoute]); return (
{isLoading ? (
加载中...
) : reviewData && ( <> {/* 自定义面包屑 */}
{/* 在面包屑右侧显示精简版的FileInfo */}
{reviewData.fileInfo.fileName}
{/* 合同编号:{reviewData.fileInfo.contractNumber} */} { reviewData.fileInfo.fileType != "1" ? "卷宗" : "合同" } 编号:{reviewData.fileInfo.contractNumber} {reviewData.fileInfo.fileSize && ( | {reviewData.fileInfo.fileSize} | {reviewData.fileInfo.fileFormat} | {reviewData.fileInfo.pageCount}页  )} {reviewData.fileInfo.uploadTime && (
| 上传时间:{reviewData.fileInfo.uploadTime} {/* | 上传用户:{reviewData.fileInfo.uploadUser} */}
)}
{/* 完成评查按钮 */} {isProposer && ( )}
{/* 文件信息和操作按钮 */} {/* */} {/* 交叉评查结果内容 */}
{/* 左侧:文件预览 */}
{/* 右侧:评查结果 */}
)}
); } // 模拟评查数据 function getMockReviewData(): ReviewData { return { fileInfo: { fileName: "烟草产品销售合同(2023版).docx", contractNumber: "XS-2023-1025-001", fileSize: "5.2MB", fileFormat: "DOCX", pageCount: 5, uploadTime: "2023-10-25 14:30:45", uploadUser: "张三", auditStatus: 0, fileType: "1" }, contractInfo: { contractType: "销售合同", signDate: "2023年10月20日", parties: { partyA: "XX烟草公司", partyB: "YY贸易有限公司" }, amount: "¥ 1,580,000.00", period: "2023年11月1日至2024年10月31日" }, reviewInfo: { reviewTime: "2023-10-25 14:35:12", reviewModel: "DeepSeek", ruleGroup: "合同标准规则组", result: "warning", issueCount: 9 }, statistics: { total: 15, success: 6, warning: 7, error: 2, score: 75 }, fileContent: { title: "烟草产品销售合同", contractNumber: "XS-2023-1025-001", parties: { partyA: { name: "XX烟草公司", address: "XX省XX市XX区XX路XX号", representative: "张XX", phone: "123-4567-8901" }, partyB: { name: "YY贸易有限公司", address: "XX省XX市XX区YY路YY号", representative: "李YY", phone: "123-4567-8902" } }, sections: [ { title: "总则", content: "1.1 本合同适用于甲乙双方之间的烟草制品买卖事宜。\n1.2 双方应本着平等互利、诚实信用的原则履行本合同。" }, { title: "合同标的物", content: "2.1 产品名称:烟草制品\n2.2 规格型号:如附件所列\n2.3 数量:5000箱\n2.4 质量要求:符合国家标准GB/T XXXXX-XXXX" }, { title: "交货与付款", content: "3.1 交货时间:自合同签订之日起30日内。\n3.2 乙方应在收到货物之日起5个工作日内支付合同款项,甲方应在收到乙方全部付款后开具增值税专用发票,乙方应在收到发票后支付剩余款项。\n3.3 交货地点:乙方指定的仓库。\n3.4 运输方式:陆运,运费由甲方承担。" }, { title: "合同文本", content: "本合同一式两份,甲乙双方各执一份,具有同等法律效力。" } ] }, reviewPoints: [ { id: "1", pointName: "付款条款", title: "付款条件描述不明确", groupName: "付款条款清晰性", // location: "交货与付款条款", status: "error", editAuditStatus: 0, content: { 'anjia':{ page: 1, value: { text: "乙方应在收到货物之日起5个工作日内支付合同款项,甲方应在收到乙方全部付款后开具增值税专用发票,乙方应在收到发票后支付剩余款项。" } }, 'yijia':{ page: 1, value: { text: "乙方应在收到货物之日起5个工作日内支付合同款项,甲方应在收到乙方全部付款后开具增值税专用发票,乙方应在收到发票后支付剩余款项。" } } }, suggestion: "乙方应在收到货物验收合格之日起5个工作日内支付合同总额的70%,甲方收到该部分款项后3个工作日内向乙方开具等额增值税专用发票;乙方应在收到发票之日起5个工作日内支付剩余30%款项。", position: { section: "交货与付款", index: 2 }, result: false }, { id: "2", pointName: "违约责任", title: "违约责任条款缺失", groupName: "合同权利义务对等性", status: "warning", editAuditStatus: 0, content: { 'clause': { page: 1, value: { text: "如合同发生纠纷,双方应协商解决。" } } }, suggestion: "如合同发生纠纷,双方应友好协商解决;协商不成的,任何一方均有权向甲方所在地人民法院提起诉讼。任何一方未能履行本合同约定义务,应向守约方支付合同总金额的10%作为违约金;给对方造成损失的,还应赔偿由此产生的全部损失。", position: { section: "争议解决", index: 0 }, result: false }, { id: "3", pointName: "签章审核", title: "签章不完整", groupName: "合同签署规范性", status: "warning", editAuditStatus: 0, content: { 'signature': { page: 5, value: { text: "乙方(盖章):YY贸易有限公司\n代表人签字:李YY\n日期:2023年10月20日" } } }, suggestion: "需要联系甲方补充公章", needsHumanReview: true, humanReviewNote: "需要联系甲方补充公章", position: { section: "签章", index: 0 }, result: false }, { id: "9", pointName: "交货方式", title: "交货方式描述模糊", groupName: "履行条款明确性", status: "success", editAuditStatus: 0, content: { 'delivery': { page: 3, value: { text: "3.4 运输方式:陆运,运费由甲方承担。" } } }, suggestion: "建议补充具体的运输方式和时间", needsHumanReview: true, humanReviewNote: "经核实,该交货方式虽然描述不够详细,但符合行业惯例且双方已经多次合作,不会造成实际履行障碍。", humanReviewBy: "王法务", humanReviewTime: "2023-11-05 14:30:22", position: { section: "交货与付款", index: 4 }, result: true }, { id: "10", pointName: "法律适用", title: "法律适用条款缺失", groupName: "争议解决条款完整性", status: "error", editAuditStatus: 0, content: { 'missing': { page: 0, value: { text: "" } } }, suggestion: "第十三条 法律适用\n本合同的订立、效力、解释、履行及争议的解决均适用中华人民共和国法律。因本合同引起的或与本合同有关的任何争议,双方应友好协商解决。协商不成的,提交甲方所在地人民法院诉讼解决。", position: { section: "缺失", index: 0 }, result: false } ], aiAnalysis: { riskAlerts: [ { title: "风险提示", content: "本合同缺少违约责任条款,可能导致权责不明。", description: "根据《中华人民共和国民法典》第五百七十七条规定,建议增加违约责任条款,明确双方违约责任及赔偿方式。" }, { title: "完整性检查", content: "本合同缺少法律适用条款。", description: "根据行业惯例,销售合同应明确约定适用法律和纠纷解决方式,以避免后续争议解决时的不确定性。" } ], suggestions: [ { title: "优化建议", content: "建议完善付款条件描述。", description: "目前合同中关于付款条件的描述存在歧义,可能导致付款时间和条件不明确。建议按系统修改建议优化。" } ], summary: "本合同基本结构完整,主体内容清晰,但存在多处条款描述不完善的问题,主要体现在支付条件、违约责任、不可抗力、保密条款、合同终止条件等方面。这些问题虽不影响合同的基本合规性,但可能在合同履行过程中引发争议和纠纷。同时,合同签章不完整,也影响了合同的法律效力。建议对上述问题进行修改完善后再行签署。" } }; }