diff --git a/src/main/java/com/gxwebsoft/ai/controller/AuditReportController2.java b/src/main/java/com/gxwebsoft/ai/controller/AuditReportController2.java new file mode 100644 index 0000000..b670b2c --- /dev/null +++ b/src/main/java/com/gxwebsoft/ai/controller/AuditReportController2.java @@ -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(); + } +} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/ai/factory/KnowledgeBaseClientFactory.java b/src/main/java/com/gxwebsoft/ai/factory/KnowledgeBaseClientFactory.java index 5c4beeb..cdba0f9 100644 --- a/src/main/java/com/gxwebsoft/ai/factory/KnowledgeBaseClientFactory.java +++ b/src/main/java/com/gxwebsoft/ai/factory/KnowledgeBaseClientFactory.java @@ -11,13 +11,19 @@ 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; } } \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/ai/service/AuditReportService.java b/src/main/java/com/gxwebsoft/ai/service/AuditReportService.java new file mode 100644 index 0000000..40efa9c --- /dev/null +++ b/src/main/java/com/gxwebsoft/ai/service/AuditReportService.java @@ -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); +} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/ai/service/impl/AuditReportServiceImpl.java b/src/main/java/com/gxwebsoft/ai/service/impl/AuditReportServiceImpl.java new file mode 100644 index 0000000..a68f864 --- /dev/null +++ b/src/main/java/com/gxwebsoft/ai/service/impl/AuditReportServiceImpl.java @@ -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 idList = StrUtil.split(libraryIds, ','); + List ret = pwlProjectLibraryService.list(new LambdaQueryWrapper().in(PwlProjectLibrary::getId, idList)); + String libraryKbIds = ret.stream().map(PwlProjectLibrary::getKbId).collect(Collectors.joining(",")); + + JSONObject result = new JSONObject(); + + // 1. 从知识库召回相关规定 + List 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 retrieveRegulations(String auditContent, String kbId, String libraryKbIds) { + List 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 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 analysisEvidence = retrieveEvidence(question.toJSONString(), analysisLib); + List 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 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 analysisEvidence, + List 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 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 analysisEvidence, List 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 queryKnowledgeBase(String kbId, String query, int topK) { + Set 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); + } +} \ No newline at end of file