审计核查功能初版
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
package com.gxwebsoft.ai.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.openxml4j.util.ZipSecureFile;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFDocument;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gxwebsoft.ai.service.AuditReportService;
|
||||
import com.gxwebsoft.common.core.web.ApiResult;
|
||||
import com.gxwebsoft.common.core.web.BaseController;
|
||||
import com.gxwebsoft.common.system.entity.User;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
/**
|
||||
* 审计报告控制器 - 重构版
|
||||
*/
|
||||
@Tag(name = "审计报告")
|
||||
@RestController
|
||||
@RequestMapping("/api/ai/auditReport2")
|
||||
public class AuditReportController2 extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private AuditReportService auditReportService;
|
||||
|
||||
/**
|
||||
* 生成审计核查报告 - 重构版
|
||||
*/
|
||||
@Operation(summary = "生成审计核查报告")
|
||||
@PostMapping("/generate")
|
||||
public ApiResult<?> generateAuditReport(
|
||||
@RequestPart("file") MultipartFile file,
|
||||
@RequestPart(value = "kbId", required = false) String kbId,
|
||||
@RequestPart(value = "libraryIds", required = false) String libraryIds,
|
||||
@RequestPart(value = "analysisLibrary", required = false) String analysisLibrary,
|
||||
@RequestPart(value = "projectLibrary", required = false) String projectLibrary) {
|
||||
|
||||
final User loginUser = getLoginUser();
|
||||
try {
|
||||
// 1. 解析文件五、六点内容
|
||||
String auditContent = extractAuditContent(file);
|
||||
if (StrUtil.isBlank(auditContent)) {
|
||||
return fail("未能提取到审计内容");
|
||||
}
|
||||
|
||||
// 2. 生成完整审计报告
|
||||
JSONObject auditReport = auditReportService.generateCompleteAuditReport(
|
||||
auditContent, file.getOriginalFilename(), kbId, libraryIds,
|
||||
analysisLibrary, projectLibrary, loginUser.getUsername()
|
||||
);
|
||||
|
||||
return success(auditReport);
|
||||
|
||||
} catch (Exception e) {
|
||||
return fail("生成审计报告失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取审计内容(五、六点)
|
||||
*/
|
||||
private String extractAuditContent(MultipartFile file) throws IOException {
|
||||
StringBuilder content = new StringBuilder();
|
||||
|
||||
// 保存原来的最小解压比率
|
||||
double originalMinInflateRatio = ZipSecureFile.getMinInflateRatio();
|
||||
// 设置新的最小解压比率
|
||||
ZipSecureFile.setMinInflateRatio(0.001);
|
||||
|
||||
try (InputStream is = file.getInputStream();
|
||||
XWPFDocument doc = new XWPFDocument(is)) {
|
||||
|
||||
boolean inSection5 = false;
|
||||
boolean inSection6 = false;
|
||||
|
||||
for (XWPFParagraph para : doc.getParagraphs()) {
|
||||
String text = para.getText();
|
||||
if (StrUtil.isBlank(text)) continue;
|
||||
|
||||
// 检测第五部分
|
||||
if (text.contains("五、") || text.contains("5、")) {
|
||||
inSection5 = true;
|
||||
inSection6 = false;
|
||||
content.append("【第五部分】\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检测第六部分
|
||||
if (text.contains("六、") || text.contains("6、")) {
|
||||
inSection5 = false;
|
||||
inSection6 = true;
|
||||
content.append("【第六部分】\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检测结束
|
||||
if ((text.contains("七、") || text.contains("7、")) && inSection6) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 收集内容
|
||||
if (inSection5 || inSection6) {
|
||||
content.append(text).append("\n");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// 恢复原来的最小解压比率
|
||||
ZipSecureFile.setMinInflateRatio(originalMinInflateRatio);
|
||||
}
|
||||
|
||||
return content.toString();
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,18 @@ public class KnowledgeBaseClientFactory {
|
||||
@Autowired
|
||||
private KnowledgeBaseConfig config;
|
||||
|
||||
private Client client;
|
||||
|
||||
public Client createClient() throws Exception {
|
||||
com.aliyun.teaopenapi.models.Config authConfig = new com.aliyun.teaopenapi.models.Config()
|
||||
.setAccessKeyId(config.getAccessKeyId())
|
||||
.setAccessKeySecret(config.getAccessKeySecret());
|
||||
// 下方接入地址以公有云的公网接入地址为例,可按需更换接入地址。
|
||||
authConfig.endpoint = "bailian.cn-beijing.aliyuncs.com";
|
||||
return new com.aliyun.bailian20231229.Client(authConfig);
|
||||
if(client == null) {
|
||||
com.aliyun.teaopenapi.models.Config authConfig = new com.aliyun.teaopenapi.models.Config()
|
||||
.setAccessKeyId(config.getAccessKeyId())
|
||||
.setAccessKeySecret(config.getAccessKeySecret());
|
||||
// 下方接入地址以公有云的公网接入地址为例,可按需更换接入地址。
|
||||
authConfig.endpoint = "bailian.cn-beijing.aliyuncs.com";
|
||||
client = new com.aliyun.bailian20231229.Client(authConfig);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.gxwebsoft.ai.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
public interface AuditReportService {
|
||||
|
||||
/**
|
||||
* 生成完整审计报告
|
||||
*/
|
||||
JSONObject generateCompleteAuditReport(String auditContent, String fileName,
|
||||
String kbId, String libraryIds,
|
||||
String analysisLibrary, String projectLibrary,
|
||||
String userName);
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
package com.gxwebsoft.ai.service.impl;
|
||||
|
||||
import com.aliyun.bailian20231229.Client;
|
||||
import com.aliyun.bailian20231229.models.RetrieveResponse;
|
||||
import com.aliyun.bailian20231229.models.RetrieveResponseBody.RetrieveResponseBodyDataNodes;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gxwebsoft.ai.config.KnowledgeBaseConfig;
|
||||
import com.gxwebsoft.ai.factory.KnowledgeBaseClientFactory;
|
||||
import com.gxwebsoft.ai.service.AuditReportService;
|
||||
import com.gxwebsoft.ai.util.KnowledgeBaseUtil;
|
||||
import com.gxwebsoft.pwl.entity.PwlProjectLibrary;
|
||||
import com.gxwebsoft.pwl.service.PwlProjectLibraryService;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class AuditReportServiceImpl implements AuditReportService {
|
||||
|
||||
@Autowired
|
||||
private KnowledgeBaseClientFactory clientFactory;
|
||||
|
||||
@Autowired
|
||||
private KnowledgeBaseConfig config;
|
||||
|
||||
@Autowired
|
||||
private PwlProjectLibraryService pwlProjectLibraryService;
|
||||
|
||||
// 工作流配置
|
||||
private static final String QUESTION_GENERATION_WORKFLOW_URL = "http://1.14.159.185:8180/v1/workflows/run";
|
||||
private static final String QUESTION_GENERATION_TOKEN = "Bearer app-vqbecZtTGmpGL5YhQzsy0Ctr";
|
||||
|
||||
private static final String QUESTION_ANALYSIS_WORKFLOW_URL = "http://1.14.159.185:8180/v1/workflows/run";
|
||||
private static final String QUESTION_ANALYSIS_TOKEN = "Bearer app-5CQWPxctsPape0HePWA3cP4Y";
|
||||
|
||||
@Override
|
||||
public JSONObject generateCompleteAuditReport(String auditContent, String fileName,
|
||||
String kbId, String libraryIds,
|
||||
String analysisLibrary, String projectLibrary,
|
||||
String userName) {
|
||||
|
||||
List<String> idList = StrUtil.split(libraryIds, ',');
|
||||
List<PwlProjectLibrary> ret = pwlProjectLibraryService.list(new LambdaQueryWrapper<PwlProjectLibrary>().in(PwlProjectLibrary::getId, idList));
|
||||
String libraryKbIds = ret.stream().map(PwlProjectLibrary::getKbId).collect(Collectors.joining(","));
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
|
||||
// 1. 从知识库召回相关规定
|
||||
List<String> regulations = retrieveRegulations(auditContent, kbId, libraryKbIds);
|
||||
|
||||
// 2. 生成审计问题列表
|
||||
JSONArray questionList = generateAuditQuestions(auditContent, regulations, userName);
|
||||
|
||||
// 3. 分析每个问题
|
||||
JSONArray findings = analyzeQuestions(questionList, analysisLibrary, projectLibrary, userName);
|
||||
|
||||
// 4. 构建返回结果
|
||||
result.put("source_file", fileName);
|
||||
result.put("audit_content", auditContent);
|
||||
result.put("total_questions", questionList.size());
|
||||
result.put("total_findings", findings.size());
|
||||
result.put("findings", findings);
|
||||
result.put("generated_time", new Date().toString());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从知识库召回相关规定
|
||||
*/
|
||||
private List<String> retrieveRegulations(String auditContent, String kbId, String libraryKbIds) {
|
||||
List<String> regulations = new ArrayList<>();
|
||||
String query = buildRegulationQuery(auditContent);
|
||||
|
||||
// 主知识库
|
||||
if (StrUtil.isNotBlank(kbId)) {
|
||||
regulations.addAll(queryKnowledgeBase(kbId, query, 10));
|
||||
}
|
||||
|
||||
// 多个库
|
||||
if (StrUtil.isNotBlank(libraryKbIds)) {
|
||||
for (String libId : libraryKbIds.split(",")) {
|
||||
if (StrUtil.isNotBlank(libId.trim())) {
|
||||
regulations.addAll(queryKnowledgeBase(libId.trim(), query, 5));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return regulations.stream().distinct().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成审计问题列表
|
||||
*/
|
||||
private JSONArray generateAuditQuestions(String auditContent, List<String> regulations, String userName) {
|
||||
// 构建知识上下文
|
||||
String knowledgeContext = buildKnowledgeContext(auditContent, regulations);
|
||||
|
||||
JSONObject requestBody = buildQuestionGenerationRequest(knowledgeContext, userName);
|
||||
|
||||
JSONArray response = callQuestionGenerationWorkflow(requestBody);
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析每个问题
|
||||
*/
|
||||
private JSONArray analyzeQuestions(JSONArray questions, String analysisLib, String projectLib, String userName) {
|
||||
JSONArray findings = new JSONArray();
|
||||
|
||||
for (int i = 0; i < questions.size(); i++) {
|
||||
JSONObject question = questions.getJSONObject(i);
|
||||
|
||||
// 从两个库召回证据
|
||||
List<String> analysisEvidence = retrieveEvidence(question.toJSONString(), analysisLib);
|
||||
List<String> projectEvidence = retrieveEvidence(question.toJSONString(), projectLib);
|
||||
|
||||
// 调用工作流分析
|
||||
JSONArray analysisResult = analyzeSingleQuestion(question, analysisEvidence, projectEvidence, userName);
|
||||
|
||||
if (analysisResult != null && !analysisResult.isEmpty()) {
|
||||
JSONObject finding = analysisResult.getJSONObject(0);
|
||||
// 添加索引
|
||||
finding.put("index", i + 1);
|
||||
findings.add(finding);
|
||||
}
|
||||
}
|
||||
|
||||
return findings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 召回证据
|
||||
*/
|
||||
private List<String> retrieveEvidence(String question, String libraryId) {
|
||||
if (StrUtil.isBlank(libraryId)) return new ArrayList<>();
|
||||
return new ArrayList<>(queryKnowledgeBase(libraryId, question, 5));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分析单个问题
|
||||
*/
|
||||
private JSONArray analyzeSingleQuestion(JSONObject question, List<String> analysisEvidence,
|
||||
List<String> projectEvidence, String userName) {
|
||||
|
||||
String evidenceContext = buildEvidenceContext(analysisEvidence, projectEvidence);
|
||||
String questionText = buildQuestionText(question);
|
||||
|
||||
JSONObject requestBody = buildQuestionAnalysisRequest(questionText, evidenceContext, userName);
|
||||
|
||||
return callQuestionAnalysisWorkflow(requestBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建问题文本
|
||||
*/
|
||||
private String buildQuestionText(JSONObject question) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("问题表述: ").append(question.getString("问题表述")).append("\n");
|
||||
sb.append("问题明细: ").append(question.getString("问题明细")).append("\n");
|
||||
sb.append("法规依据: ").append(question.getString("法规依据"));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建知识上下文
|
||||
*/
|
||||
private String buildKnowledgeContext(String auditContent, List<String> regulations) {
|
||||
StringBuilder context = new StringBuilder();
|
||||
context.append("审计内容:\n").append(auditContent).append("\n\n");
|
||||
if (!regulations.isEmpty()) {
|
||||
context.append("相关规定:\n");
|
||||
regulations.forEach(reg -> context.append("- ").append(reg).append("\n"));
|
||||
}
|
||||
return context.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建证据上下文
|
||||
*/
|
||||
private String buildEvidenceContext(List<String> analysisEvidence, List<String> projectEvidence) {
|
||||
StringBuilder context = new StringBuilder();
|
||||
|
||||
if (!analysisEvidence.isEmpty()) {
|
||||
context.append("分析库证据:\n");
|
||||
analysisEvidence.forEach(ev -> context.append("- ").append(ev).append("\n"));
|
||||
}
|
||||
|
||||
if (!projectEvidence.isEmpty()) {
|
||||
context.append("项目库证据:\n");
|
||||
projectEvidence.forEach(ev -> context.append("- ").append(ev).append("\n"));
|
||||
}
|
||||
|
||||
return context.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用问题生成工作流
|
||||
*/
|
||||
private JSONArray callQuestionGenerationWorkflow(JSONObject requestBody) {
|
||||
try {
|
||||
String result = HttpUtil.createPost(QUESTION_GENERATION_WORKFLOW_URL)
|
||||
.header("Authorization", QUESTION_GENERATION_TOKEN)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(requestBody.toString())
|
||||
.timeout(600000)
|
||||
.execute()
|
||||
.body();
|
||||
|
||||
JSONObject jsonResponse = JSONObject.parseObject(result);
|
||||
String outputText = jsonResponse.getJSONObject("data")
|
||||
.getJSONObject("outputs")
|
||||
.getString("result");
|
||||
|
||||
// 解析JSON数组
|
||||
return JSONArray.parseArray(outputText);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("调用问题生成工作流失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用问题分析工作流
|
||||
*/
|
||||
private JSONArray callQuestionAnalysisWorkflow(JSONObject requestBody) {
|
||||
try {
|
||||
String result = HttpUtil.createPost(QUESTION_ANALYSIS_WORKFLOW_URL)
|
||||
.header("Authorization", QUESTION_ANALYSIS_TOKEN)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(requestBody.toString())
|
||||
.timeout(600000)
|
||||
.execute()
|
||||
.body();
|
||||
|
||||
JSONObject jsonResponse = JSONObject.parseObject(result);
|
||||
String outputText = jsonResponse.getJSONObject("data")
|
||||
.getJSONObject("outputs")
|
||||
.getString("result");
|
||||
|
||||
// 解析JSON数组
|
||||
return JSONArray.parseArray(outputText);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("分析问题失败: " + e.getMessage());
|
||||
// 返回默认结构,避免整个流程中断
|
||||
return createDefaultFinding();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建默认的发现条目
|
||||
*/
|
||||
private JSONArray createDefaultFinding() {
|
||||
JSONObject finding = new JSONObject();
|
||||
finding.put("是否存在相关问题", "false");
|
||||
finding.put("问题表述", "");
|
||||
finding.put("问题明细", "");
|
||||
finding.put("法规依据", "");
|
||||
finding.put("涉及金额(万元)", "/");
|
||||
finding.put("具体责任界定", "/");
|
||||
finding.put("整改类型", "/");
|
||||
finding.put("整改要求", "/");
|
||||
finding.put("整改时限", "/");
|
||||
finding.put("审计建议", "/");
|
||||
|
||||
JSONArray array = new JSONArray();
|
||||
array.add(finding);
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建问题生成工作流请求
|
||||
*/
|
||||
private JSONObject buildQuestionGenerationRequest(String knowledge, String userName) {
|
||||
JSONObject requestBody = new JSONObject();
|
||||
JSONObject inputs = new JSONObject();
|
||||
|
||||
inputs.put("knowledge", knowledge);
|
||||
|
||||
requestBody.put("inputs", inputs);
|
||||
requestBody.put("response_mode", "blocking");
|
||||
requestBody.put("user", userName);
|
||||
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建问题分析工作流请求
|
||||
*/
|
||||
private JSONObject buildQuestionAnalysisRequest(String query, String knowledge, String userName) {
|
||||
JSONObject requestBody = new JSONObject();
|
||||
JSONObject inputs = new JSONObject();
|
||||
|
||||
inputs.put("query", query);
|
||||
inputs.put("knowledge", knowledge);
|
||||
|
||||
requestBody.put("inputs", inputs);
|
||||
requestBody.put("response_mode", "blocking");
|
||||
requestBody.put("user", userName);
|
||||
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询知识库
|
||||
*/
|
||||
private Set<String> queryKnowledgeBase(String kbId, String query, int topK) {
|
||||
Set<String> results = new LinkedHashSet<>();
|
||||
String workspaceId = config.getWorkspaceId();
|
||||
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
RetrieveResponse resp = KnowledgeBaseUtil.retrieveIndex(client, workspaceId, kbId, query);
|
||||
|
||||
if (resp.getBody() != null && resp.getBody().getData() != null
|
||||
&& resp.getBody().getData().getNodes() != null) {
|
||||
|
||||
for (RetrieveResponseBodyDataNodes node : resp.getBody().getData().getNodes()) {
|
||||
results.add(node.getText());
|
||||
if (results.size() >= topK) break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 记录日志但不中断流程
|
||||
System.err.println("查询知识库失败: " + e.getMessage());
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建法规查询
|
||||
*/
|
||||
private String buildRegulationQuery(String auditContent) {
|
||||
String[] keywords = {"制度", "办法", "规定", "流程", "标准", "规范", "要求"};
|
||||
return String.join(" ", keywords);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user