Files
leaudit-platform-frontend/app/routes/reviews.tsx
T

876 lines
32 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 评查详情页面
*
* 功能概述:
* - 显示文档评查结果和详细信息
* - 支持查看文档内容及评查点高亮标记
* - 提供评查点列表,分为通过、警告和错误三种类型
* - 支持评查点处理,如一键替换、人工审核等功能
* - 支持导出评查报告和下载原文件
*
* 组件结构:
* - FileInfo: 显示文件基本信息和操作按钮
* - ReviewTabs: 页面选项卡,包括评查结果、AI智能分析和文件信息
* - FilePreview: 文档预览组件,显示文档内容及高亮问题
* - ReviewPointsList: 评查点列表组件,显示所有评查结果
* - AIAnalysis: AI智能分析结果,提供综合评价
* - FileDetails: 文件详情信息
*
* 数据流转:
* 1. 页面加载时从API获取评查详情数据
* 2. 根据评查点ID关联文档中的高亮区域
* 3. 点击评查点时在文档中定位对应位置
* 4. 处理评查点时更新状态并反馈到UI
*
* @author 中国烟草AI合同及卷宗审核系统开发团队
*/
import { type MetaFunction, type LoaderFunctionArgs } from "@remix-run/node";
import { useState, useEffect } from "react";
import { useNavigate, useLoaderData } from "@remix-run/react";
import reviewsStyles from "~/styles/reviews.css?url";
import { getReviewPoints, updateReviewResult, confirmReviewResults } from "~/api/evaluation_points/reviews";
import { toastService } from "~/components/ui/Toast";
// 导入评查详情页面组件
import {
FileInfo,
ReviewTabs,
FilePreview,
ReviewPointsList,
AIAnalysis,
FileDetails
} from "~/components/reviews";
// 从ReviewPointsList组件中导入ReviewPoint类型
import { type ReviewPoint } from '~/components/reviews';
import { messageService } from "~/components/ui/MessageModal";
import { loadingBarService } from "~/components/ui/LoadingBar";
import { Breadcrumb } from "~/components/layout/Breadcrumb";
/**
* 文件信息组件
* 显示文件名称、状态信息以及操作按钮(下载原文件、导出评查报告、确认评查结果)
*/
// 格式化文件大小
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: reviewsStyles }];
}
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 previousRoute = url.searchParams.get('previousRoute') || '';
// console.log("id-------",id);
if (!id) {
return Response.json({ result: false, message: '文件ID不能为空' });
}
// 获取评查点数据
const reviewData = await getReviewPoints(id);
// console.log("documentData-------",JSON.stringify(documentData.data,null,2));
// console.log("reviewData-------",JSON.stringify('data' in reviewData ? reviewData.data : '',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
});
} 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: '获取评查数据失败' });
}
}
export default function ReviewDetails() {
const navigate = useNavigate();
const loaderData = useLoaderData<typeof loader>();
const { document, reviewPoints, statistics, reviewInfo } = loaderData;
const [isLoading, setIsLoading] = useState(false); // 已经通过loader加载了数据,不需要再显示加载状态
const [activeTab, setActiveTab] = useState<string>('preview'); // 'preview', 'analysis', 'fileinfo'
const [reviewData, setReviewData] = useState<ReviewData | null>(null);
const [activeReviewPointResultId, setActiveReviewPointResultId] = useState<string | null>(null);
const [targetPage, setTargetPage] = useState<number | undefined>(undefined);
// 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 handleTabChange = (tabKey: string) => {
setActiveTab(tabKey);
};
const handleReviewPointSelect = (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);
}
};
// 刷新评审数据
// async function refreshReviewData(documentId: string) {
// // 设置加载状态
// setIsLoading(true);
// try {
// // 获取最新的评审数据
// const response = await getReviewPoints(documentId);
// if ('error' in response && response.error) {
// console.error('刷新评审数据失败:', response.error);
// toastService.error(`刷新评审数据失败: ${response.error}`);
// return;
// }
// // 确保response有效且具有预期的属性
// if ('data' in response && 'stats' in response && 'reviewInfo' in response) {
// const reviewPointsData = response.data || [];
// const statisticsData = response.stats || { total: 0, success: 0, warning: 0, error: 0, score: 0 };
// const reviewInfoData = response.reviewInfo || {
// reviewTime: '',
// reviewModel: '',
// ruleGroup: '',
// result: '',
// issueCount: 0
// };
// // 更新评审数据和统计信息
// setReviewData(prevData => {
// if (!prevData) {
// // 如果prevData为null,创建一个新的ReviewData对象
// return {
// fileInfo: {
// fileName: document?.name || "",
// 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
// },
// contractInfo: getMockReviewData().contractInfo,
// reviewInfo: reviewInfoData as ReviewInfo,
// statistics: statisticsData as Statistics,
// fileContent: getMockReviewData().fileContent,
// reviewPoints: reviewPointsData as unknown as ReviewPoint[],
// aiAnalysis: getMockReviewData().aiAnalysis
// };
// }
// // 处理prevData非null的情况
// return {
// ...prevData,
// reviewPoints: reviewPointsData as unknown as ReviewPoint[],
// statistics: statisticsData as Statistics,
// reviewInfo: reviewInfoData as ReviewInfo
// };
// });
// toastService.success('评审数据已更新');
// } else {
// console.error('返回的数据格式不正确');
// toastService.error('刷新评审数据失败: 返回的数据格式不正确');
// }
// } catch (error) {
// console.error('刷新评审数据失败:', error);
// toastService.error(`刷新评审数据失败: ${error instanceof Error ? error.message : '未知错误'}`);
// } finally {
// setIsLoading(false);
// }
// }
// 处理评审点状态变更
const handleReviewPointStatusChange = async (reviewPointResultId: string, editAuditStatusId: string | number, newStatus: string, message: string) => {
// 将字符串的布尔值转换为布尔类型
let boolResult = 'review';
if(newStatus !== 'review'){
boolResult = newStatus === 'true' ? 'true' : 'false';
}
try {
// 调用 API 更新评查结果
const response = await updateReviewResult(reviewPointResultId, editAuditStatusId, boolResult, message);
if (response.error) {
console.error('更新评查结果失败:', response.error);
toastService.error(`更新评查结果失败: ${response.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: 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('更新评查结果失败,请稍后重试');
}
};
const handleConfirmResults = async () => {
if (!document || !document.id) {
toastService.error('文档数据不完整,无法确认评查结果');
return;
}
try {
// 显示加载状态
setIsLoading(true);
// 调用API确认评查结果
const response = await confirmReviewResults(document.id.toString());
if (response.error) {
console.error('确认评查结果失败:', response.error);
toastService.error(`确认评查结果失败: ${response.error}`);
return;
}
// 显示成功消息
toastService.success('评查结果已确认,文档审核状态已更新');
// 导航到文档列表页
navigate('/documents');
} catch (error) {
console.error('确认评查结果出错:', error);
toastService.error(`确认评查结果失败: ${error instanceof Error ? error.message : '未知错误'}`);
} finally {
setIsLoading(false);
}
};
// 构建自定义面包屑项
const getBreadcrumbItems = () => {
const items = [
{ title: "评查详情", to: `/reviews?id=${document?.id}` }
];
// 添加前置路由
if (loaderData.previousRoute) {
if (loaderData.previousRoute === 'filesUpload') {
items.unshift({ title: "文件上传", to: "/files/upload" });
} else if (loaderData.previousRoute === 'documents') {
items.unshift({ title: "文档列表", to: "/documents" });
} else if (loaderData.previousRoute === 'rulesFiles') {
items.unshift({ title: "评查文件列表", to: "/rules-files" });
}
}
return items;
};
return (
<div className="review-container">
{isLoading ? (
<div className="flex justify-center items-center p-12">
<div className="loading-spinner"></div>
<span className="ml-3">...</span>
</div>
) : reviewData && (
<>
{/* 自定义面包屑 */}
<div className="flex justify-between items-center mb-2">
<Breadcrumb
items={getBreadcrumbItems()}
className="items-center flex !mb-0"
/>
{/* 在面包屑右侧显示精简版的FileInfo */}
<div className=" ml-10 text-left flex-1 flex flex-row flex-wrap">
<span className="mr-2 text-xl font-medium">
{reviewData.fileInfo.fileName}
</span>
<div className="text-xs text-gray-500 flex items-center">
{/* 合同编号:{reviewData.fileInfo.contractNumber} */}
{ reviewData.fileInfo.fileType != "1" ? "卷宗" : "合同" }
{reviewData.fileInfo.contractNumber}
{reviewData.fileInfo.fileSize && (
<span className="text-xs text-gray-500 ml-2">
| {reviewData.fileInfo.fileSize} | {reviewData.fileInfo.fileFormat} | {reviewData.fileInfo.pageCount}&nbsp;
</span>
)}
{reviewData.fileInfo.uploadTime && (
<div className="text-xs text-gray-500">
| {reviewData.fileInfo.uploadTime}
{/* | 上传用户:{reviewData.fileInfo.uploadUser} */}
</div>
)}
</div>
</div>
</div>
{/* <div className="text-xs text-gray-500 flex items-center mb-1">
合同编号:{reviewData.fileInfo.contractNumber}
{reviewData.fileInfo.fileSize && (
<span className="text-xs text-gray-500 ml-2">
| {reviewData.fileInfo.fileSize} | {reviewData.fileInfo.fileFormat} | {reviewData.fileInfo.pageCount}页&nbsp;
</span>
)}
{reviewData.fileInfo.uploadTime && (
<div className="text-xs text-gray-500">
| 上传时间:{reviewData.fileInfo.uploadTime} | 上传用户:{reviewData.fileInfo.uploadUser}
</div>
)}
</div> */}
{/* 文件信息和操作按钮 */}
{/* <FileInfo
fileInfo={{
...reviewData.fileInfo,
previousRoute: loaderData.previousRoute
}}
onConfirmResults={handleConfirmResults}
/> */}
{/* 选项卡 */}
<ReviewTabs
activeTab={activeTab}
onTabChange={handleTabChange}
fileInfo={{
previousRoute: loaderData.previousRoute,
path: document?.path,
auditStatus: document?.auditStatus,
type: document?.type
}}
onConfirmResults={handleConfirmResults}
>
{/* 评查结果选项卡内容 */}
{activeTab === 'preview' && (
<div className="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:space-x-4">
{/* 左侧:文件预览 */}
<div className="w-full lg:w-[65%]">
<FilePreview
fileContent={document}
reviewPoints={reviewData.reviewPoints}
activeReviewPointResultId={activeReviewPointResultId}
targetPage={targetPage}
/>
</div>
{/* 右侧:评查结果 */}
<div className="w-full lg:w-[35%]">
<ReviewPointsList
reviewPoints={reviewData.reviewPoints}
statistics={reviewData.statistics}
activeReviewPointResultId={activeReviewPointResultId}
onReviewPointSelect={handleReviewPointSelect}
onStatusChange={handleReviewPointStatusChange}
/>
</div>
</div>
)}
{/* 结构比对选项卡内容 */}
{activeTab === 'filecompare' && (
<div className="flex flex-col lg:flex-row space-y-4 lg:space-y-0 lg:space-x-4">
{/* 左侧:原文件预览 */}
<div className="w-full lg:w-[38%]">
<FilePreview
fileContent={document}
reviewPoints={reviewData.reviewPoints}
activeReviewPointResultId={activeReviewPointResultId}
targetPage={targetPage}
/>
</div>
{/* 中间:附件文件预览 */}
<div className="w-full lg:w-[38%]">
<FilePreview
fileContent={document}
reviewPoints={[]}
activeReviewPointResultId={activeReviewPointResultId}
targetPage={targetPage}
isStructuredView={true}
/>
</div>
{/* 右侧:评查结果 */}
<div className="w-full lg:w-[24%]">
<ReviewPointsList
reviewPoints={reviewData.reviewPoints}
statistics={reviewData.statistics}
activeReviewPointResultId={activeReviewPointResultId}
onReviewPointSelect={handleReviewPointSelect}
onStatusChange={handleReviewPointStatusChange}
/>
</div>
</div>
)}
{/* AI智能分析选项卡内容 */}
{activeTab === 'analysis' && (
<AIAnalysis
analysisData={reviewData.aiAnalysis}
score={reviewData.statistics.score}
onConfirmResults={handleConfirmResults}
/>
)}
{/* 文件信息选项卡内容 */}
{activeTab === 'fileinfo' && (
<FileDetails
fileInfo={reviewData.fileInfo}
contractInfo={reviewData.contractInfo}
reviewInfo={reviewData.reviewInfo}
/>
)}
</ReviewTabs>
</>
)}
</div>
);
}
// 模拟评查数据
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: "本合同基本结构完整,主体内容清晰,但存在多处条款描述不完善的问题,主要体现在支付条件、违约责任、不可抗力、保密条款、合同终止条件等方面。这些问题虽不影响合同的基本合规性,但可能在合同履行过程中引发争议和纠纷。同时,合同签章不完整,也影响了合同的法律效力。建议对上述问题进行修改完善后再行签署。"
}
};
}