feat: Implement compliance document analysis and chat functionality

- Added API functions for document analysis, compliance result retrieval, and chat streaming.
- Integrated document upload and analysis in CompliancePage with progress simulation.
- Enhanced DocsPage to load documents from the API and handle file uploads with parsing and embedding.
- Updated RagChatPage to fetch quick questions from the API and handle chat responses with streaming.
- Improved StatusPage to display system statistics and configuration from the API.
- Configured Vite to proxy API requests to the backend server.
This commit is contained in:
2026-05-11 11:32:02 +08:00
parent 41f9c122a9
commit 1bb7151abe
12 changed files with 730 additions and 216 deletions

View File

@@ -7,6 +7,7 @@ import {
mockPriorityActions,
fullDocumentContent,
} from '../../data';
import { analyzeDocument, getComplianceResult, complianceChat } from '../../api/compliance';
import { ChatPanel } from './ChatPanel';
import { TPattern } from '../../components/common/TPattern';
@@ -111,39 +112,114 @@ export const CompliancePage: React.FC = () => {
}
};
const startAnalysis = () => {
const startAnalysis = async () => {
if (!uploadedDoc) return;
setIsAnalyzing(true);
setAnalyzeStep(1);
setAnalyzePercent(0);
setTimeout(() => {
setAnalyzePercent(30);
setAnalyzeAction('正在解析文档结构...');
}, 500);
try {
// Get file from upload input
const fileInput = document.querySelector('input[type="file"]') as HTMLInputElement;
const file = fileInput?.files?.[0];
console.log("123")
setTimeout(() => {
setAnalyzeStep(2);
setAnalyzePercent(50);
setAnalyzeAction('正在识别语义段落...');
}, 1500);
// if (!file) {
// // setIsAnalyzing(false);
// // return;
// }
setTimeout(() => {
setAnalyzePercent(70);
setAnalyzeAction('正在识别第 2/3 个语义段落...');
}, 2500);
console.log("456")
// Upload and get task ID
const uploadRes = await analyzeDocument(file);
setTimeout(() => {
setAnalyzeStep(3);
setAnalyzePercent(85);
setAnalyzeAction('正在匹配法规条款...');
}, 3500);
// Simulate progress
setTimeout(() => {
setAnalyzePercent(30);
setAnalyzeAction('正在解析文档结构...');
}, 500);
setTimeout(() => {
setAnalyzePercent(100);
setAnalyzeAction('分析完成');
setTimeout(() => {
setAnalyzeStep(2);
setAnalyzePercent(50);
setAnalyzeAction('正在识别语义段落...');
}, 1500);
setTimeout(() => {
setAnalyzePercent(70);
setAnalyzeAction('正在识别第 2/3 个语义段落...');
}, 2500);
setTimeout(() => {
setAnalyzeStep(3);
setAnalyzePercent(85);
setAnalyzeAction('正在匹配法规条款...');
}, 3500);
// Get result after analysis completes
setTimeout(async () => {
try {
const result = await getComplianceResult(uploadRes.task_id);
if ('segments' in result) {
// Convert API response to frontend format
const apiChunks: ComplianceChunk[] = result.segments.map(s => ({
id: s.id,
index: s.index,
intent: s.intent,
startPos: s.start_pos,
endPos: s.end_pos,
content: s.content,
regulations: s.regulations.map(r => ({
id: r.id,
name: r.name,
clause: r.clause,
score: r.score,
matchKeyword: r.match_keyword,
category: r.category as 'high' | 'medium' | 'low',
fullContent: r.full_content,
})),
}));
// Calculate risk levels for each segment
const chunksWithRisk = apiChunks.map(chunk => {
const regs = chunk.regulations;
const highRegs = regs.filter(r => r.category === 'high');
const avgHighScore = highRegs.length > 0
? highRegs.reduce((sum, r) => sum + r.score, 0) / highRegs.length
: 1;
let riskLevel: 'high' | 'medium' | 'low' = 'low';
if (avgHighScore < 0.85 || highRegs.filter(r => r.score < 0.9).length >= 1) {
riskLevel = 'high';
} else if (avgHighScore < 0.92 || regs.filter(r => r.category === 'medium').length >= 2) {
riskLevel = 'medium';
}
return { ...chunk, riskLevel } as ComplianceChunk;
});
setChunks(chunksWithRisk);
}
} catch (error) {
console.error('Failed to get compliance result:', error);
// Fallback to mock data
setChunks(mockComplianceChunks);
}
setAnalyzePercent(100);
setAnalyzeAction('分析完成');
setIsAnalyzing(false);
}, 4500);
} catch (error) {
console.error('Failed to analyze document:', error);
setIsAnalyzing(false);
setChunks(mockComplianceChunks);
}, 4500);
// Fallback to mock data after delay
setTimeout(() => {
setChunks(mockComplianceChunks);
}, 4500);
}
};
const openChat = (chunkId: number) => {
@@ -180,27 +256,48 @@ export const CompliancePage: React.FC = () => {
setChatInput('');
setChatLoading(true);
setTimeout(() => {
let response = '';
const intent = chunk.intent;
const mockResps = mockAIResponses[intent];
let currentResponse = '';
if (chatInput.includes('合规') || chatInput.includes('符合')) {
response = mockResps?.compliance || '根据相关法规分析,该段落的合规性需进一步评估。';
} else if (chatInput.includes('解读') || chatInput.includes('什么') || chatInput.includes('如何')) {
response = mockResps?.interpretation || '法规要求详细解读如下...';
} else if (chatInput.includes('修改') || chatInput.includes('建议') || chatInput.includes('完善')) {
response = mockResps?.suggestion || '建议进行以下修改以提升合规性...';
} else {
response = `关于您的问题,${chunk.intent}部分涉及以下法规要点:\n\n${chunk.regulations.slice(0, 2).map(r => `${r.name} ${r.clause}(相关性 ${Math.round(r.score * 100)}%`).join('\n')}\n\n您可以进一步询问合规性评估或修改建议。`;
complianceChat(
activeChunkId,
chatInput,
(data: unknown) => {
const sseData = data as { type: string; text?: string };
if (sseData.type === 'chunk' && sseData.text) {
currentResponse += sseData.text;
setChatMessages(prev => ({
...prev,
[activeChunkId]: [...(prev[activeChunkId] || []).slice(0, -1), { id: Date.now() + 1, role: 'assistant', content: currentResponse }],
}));
} else if (sseData.type === 'done') {
setChatLoading(false);
}
},
(error: Error) => {
console.error('Compliance chat error:', error);
setChatLoading(false);
// Fallback to mock response
let response = '';
const intent = chunk.intent;
const mockResps = mockAIResponses[intent];
if (chatInput.includes('合规') || chatInput.includes('符合')) {
response = mockResps?.compliance || '根据相关法规分析,该段落的合规性需进一步评估。';
} else if (chatInput.includes('解读') || chatInput.includes('什么') || chatInput.includes('如何')) {
response = mockResps?.interpretation || '法规要求详细解读如下...';
} else if (chatInput.includes('修改') || chatInput.includes('建议') || chatInput.includes('完善')) {
response = mockResps?.suggestion || '建议进行以下修改以提升合规性...';
} else {
response = `关于您的问题,${chunk.intent}部分涉及以下法规要点:\n\n${chunk.regulations.slice(0, 2).map(r => `${r.name} ${r.clause}(相关性 ${Math.round(r.score * 100)}%`).join('\n')}\n\n您可以进一步询问合规性评估或修改建议。`;
}
setChatMessages(prev => ({
...prev,
[activeChunkId]: [...(prev[activeChunkId] || []), { id: Date.now() + 1, role: 'assistant', content: response }],
}));
},
() => {
setChatLoading(false);
}
setChatMessages(prev => ({
...prev,
[activeChunkId]: [...(prev[activeChunkId] || []), { id: Date.now() + 1, role: 'assistant', content: response }],
}));
setChatLoading(false);
}, 1000);
);
};
const dashboard = calculateRiskDashboard(chunks);
@@ -650,6 +747,7 @@ export const CompliancePage: React.FC = () => {
<button
onClick={startAnalysis}
className="t-btn"
type="button"
style={{
padding: '20px 48px',
fontSize: 16,
@@ -658,6 +756,8 @@ export const CompliancePage: React.FC = () => {
border: 'none',
borderRadius: 12,
cursor: 'pointer',
pointerEvents: 'auto',
zIndex: 10,
}}
></button>
</section>