Files
java-10561/src/main/java/com/gxwebsoft/ai/service/impl/AuditContent8InternalControlServiceImpl.java

326 lines
16 KiB
Java
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.

package com.gxwebsoft.ai.service.impl;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.gxwebsoft.ai.constants.AuditContent8InternalControlConstants;
import com.gxwebsoft.ai.service.AuditContent8InternalControlService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import cn.hutool.core.util.StrUtil;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Slf4j
@Service
public class AuditContent8InternalControlServiceImpl extends AbstractAuditContentService implements AuditContent8InternalControlService {
// 工作流配置
private static final String DIFY_WORKFLOW_TOKEN = "Bearer app-R1GN6zKkrfBYVMtwz8c6pyRM";
// 审计测试方法
private static final List<String> TEST_METHODS = Arrays.asList(
"符合性测试", "穿行测试", "抽样测试", "观察询问", "文档查阅"
);
@Override
public JSONObject generateInternalControlTableData(String kbIds, String libraryKbIds, String projectLibrary, String userName, String history, String suggestion) {
log.info("开始生成单位层面财务管理内部控制测试表数据 - 用户: {}, kbIds: {}, libraryIds: {}, projectLibrary: {}", userName, kbIds, libraryKbIds, projectLibrary);
long startTime = System.currentTimeMillis();
try {
// 异步并行处理每个控制环节
Map<String, CompletableFuture<JSONArray>> futures = processCategoriesAsync(
Arrays.asList(AuditContent8InternalControlConstants.CONTROL_LINKS),
controlLink -> generateControlLinkDataAsync(controlLink, kbIds, libraryKbIds, projectLibrary, userName, history, suggestion)
);
// 等待所有异步任务完成
CompletableFuture.allOf(futures.values().toArray(new CompletableFuture[0])).join();
// 合并所有分类的结果
JSONArray allData = mergeCategoryResults(Arrays.asList(AuditContent8InternalControlConstants.CONTROL_LINKS), futures);
log.info("单位层面财务管理内部控制测试表生成成功 - 记录数: {}, 处理时间: {}ms", allData.size(), (System.currentTimeMillis() - startTime));
return buildSuccessResponse(allData, startTime, "internal_control_audit");
} catch (Exception e) {
log.error("生成单位层面财务管理内部控制测试表失败", e);
return buildErrorResponse("生成单位层面财务管理内部控制测试表失败: " + e.getMessage());
}
}
/**
* 异步生成单个控制环节的数据
*/
@Async
public CompletableFuture<JSONArray> generateControlLinkDataAsync(String controlLink, String kbIds, String libraryKbIds, String projectLibrary, String userName, String history, String suggestion) {
return CompletableFuture.supplyAsync(() -> {
try {
log.info("开始生成控制环节 {} 的数据", controlLink);
// 1. 为当前控制环节召回相关知识
Map<String, List<String>> knowledgeSources = retrieveKnowledgeForControlLink(
controlLink, kbIds, libraryKbIds, projectLibrary
);
// 2. 构建完整的知识上下文
String knowledgeContext = buildCompleteKnowledgeContext(
controlLink, knowledgeSources, history, suggestion
);
// 3. 调用工作流生成数据
JSONObject requestBody = buildWorkflowRequest(knowledgeContext, userName);
JSONArray controlLinkData = callWorkflow(DIFY_WORKFLOW_URL, DIFY_WORKFLOW_TOKEN, requestBody, "内部控制-" + controlLink);
log.info("控制环节 {} 数据生成完成,生成 {} 条记录", controlLink, controlLinkData.size());
return controlLinkData;
} catch (Exception e) {
log.error("生成控制环节 {} 数据失败", controlLink, e);
return new JSONArray();
}
});
}
/**
* 为单个控制环节检索相关知识
*/
private Map<String, List<String>> retrieveKnowledgeForControlLink(String controlLink, String kbIds, String libraryKbIds, String projectLibrary) {
Map<String, List<String>> knowledgeSources = new HashMap<>();
knowledgeSources.put("enterprise", new ArrayList<>());
knowledgeSources.put("regulation", new ArrayList<>());
knowledgeSources.put("auditCase", new ArrayList<>());
// 构建当前控制环节的查询词
List<String> controlQueries = buildControlLinkQueries(controlLink);
// 企业单位库检索
if (StrUtil.isNotBlank(kbIds)) {
Arrays.stream(kbIds.split(","))
.map(String::trim)
.filter(StrUtil::isNotBlank)
.forEach(kbId -> knowledgeSources.get("enterprise")
.addAll(queryKnowledgeBase(kbId, controlQueries, 120)));
}
// 公共法律法规库检索
if (StrUtil.isNotBlank(libraryKbIds)) {
Arrays.stream(libraryKbIds.split(","))
.map(String::trim)
.filter(StrUtil::isNotBlank)
.forEach(libId -> knowledgeSources.get("regulation")
.addAll(queryKnowledgeBase(libId, controlQueries, 100)));
}
// 审计案例库检索
if (StrUtil.isNotBlank(projectLibrary)) {
knowledgeSources.get("auditCase").addAll(
queryKnowledgeBase(projectLibrary, controlQueries, 80));
}
// 智能去重和排序
knowledgeSources.forEach((key, list) -> {
List<String> processed = list.stream()
.distinct()
.sorted(this::internalControlComparator)
.limit(getLimitBySourceType(key))
.collect(Collectors.toList());
knowledgeSources.put(key, processed);
});
log.debug("控制环节 {} 知识检索完成 - 企业: {}条, 法规: {}条, 案例: {}条",
controlLink,
knowledgeSources.get("enterprise").size(),
knowledgeSources.get("regulation").size(),
knowledgeSources.get("auditCase").size());
return knowledgeSources;
}
/**
* 构建控制环节特定的查询词
*/
private List<String> buildControlLinkQueries(String controlLink) {
// 根据控制环节构建相关查询词
List<String> queries = new ArrayList<>();
// 通用查询词
queries.add(controlLink);
queries.add("内部控制 " + controlLink);
queries.add("财务管理 " + controlLink);
// 根据特定控制环节添加相关关键词
if (controlLink.contains("岗位")) {
queries.add("岗位职责 不相容职务分离");
queries.add("岗位设置 职责划分");
}
if (controlLink.contains("授权审批")) {
queries.add("授权审批流程 审批权限");
queries.add("财务管理授权");
}
if (controlLink.contains("重大事项")) {
queries.add("重大事项决策 集体决策");
queries.add("三重一大 财务管理");
}
if (controlLink.contains("会计")) {
queries.add("会计核算 会计档案");
queries.add("会计凭证 会计账簿");
}
if (controlLink.contains("信息系统")) {
queries.add("财务信息系统 数据备份");
queries.add("信息安全 用户管理");
}
if (controlLink.contains("风险")) {
queries.add("财务风险管理 风险识别");
queries.add("风险评估 风险应对");
}
if (controlLink.contains("内部审计")) {
queries.add("内审部门 审计监督");
queries.add("审计检查 审计报告");
}
if (controlLink.contains("反舞弊")) {
queries.add("财务舞弊 举报投诉");
queries.add("舞弊防范 举报保护");
}
return queries;
}
/**
* 构建完整的知识上下文
*/
private String buildCompleteKnowledgeContext(String controlLink, Map<String, List<String>> knowledgeSources, String history, String suggestion) {
StringBuilder context = new StringBuilder();
// 1. 单位层面财务管理内部控制测试要求
context.append("## 单位层面财务管理内部控制测试要求 - ").append(controlLink).append("\n");
context.append("请基于以下知识生成").append(controlLink).append("相关的单位层面财务管理内部控制测试数据:\n\n");
context.append("1. 控制环节:").append(controlLink).append("\n");
context.append("2. 控制要求:").append(AuditContent8InternalControlConstants.CONTROL_REQUIREMENTS.get(controlLink)).append("\n");
context.append("3. 控制活动描述:").append(AuditContent8InternalControlConstants.CONTROL_ACTIVITIES.get(controlLink)).append("\n");
context.append("4. 控制职责岗位:").append(AuditContent8InternalControlConstants.CONTROL_POSITIONS.get(controlLink)).append("\n");
context.append("5. 测试步骤:\n");
AuditContent8InternalControlConstants.TEST_STEPS.get(controlLink).forEach(step ->
context.append(" ").append(step).append("\n"));
context.append("\n");
context.append("## 审计测试要求\n");
context.append("1. 基于测试步骤逐项检查,评估内部控制的有效性\n");
context.append("2. 检查证据必须具体到查阅的文件名称、内容、检查过程和发现的问题\n");
context.append("3. 测试结果必须基于充分证据严格判断:\n");
context.append(" - 通过:所有测试步骤均得到有效执行且证据充分\n");
context.append(" - 不通过:任一测试步骤未执行、执行不到位或证据不足\n");
context.append("4. 工作底稿索引必须填写实际存在的完整文件名\n");
context.append("5. 重点关注货币资金管理、财务报销、资产管理、物资采购等关键环节\n\n");
// 2. 数据格式要求
context.append("## 数据格式要求\n");
context.append("需要生成").append(controlLink).append("控制环节的内部控制测试数据:\n\n");
context.append("**重要:每个测试步骤必须单独生成一条记录**\n\n");
context.append("每条记录应包含以下字段:\n");
context.append("- controlLink控制环节固定值不要修改\n");
context.append("- controlRequirement控制要求固定值不要修改\n");
context.append("- controlActivity控制活动描述固定值不要修改\n");
context.append("- controlPosition控制职责岗位固定值不要修改\n");
context.append("- testSteps测试步骤**只包含当前步骤内容**,如\"1是否有制度规定\"\n");
context.append("- checkEvidence检查证据针对当前测试步骤的详细检查证据\n");
context.append("- testResult测试结果基于当前测试步骤的检查证据严格判断只有证据充分且完全符合要求才能判定为\"通过\",否则必须判定为\"不通过\"\n");
context.append("- workPaperIndex工作底稿索引实际存在的完整文件名确保能在文件夹中搜索到\n\n");
context.append("**生成规则:**\n");
context.append("1. **每个测试步骤单独生成一条完整记录**\n");
context.append("2. 如果测试步骤中有多个小点123必须为每个小点生成独立记录\n");
context.append("3. 每条记录的检查证据必须专门针对该测试步骤\n");
context.append("4. 每条记录的测试结果必须基于该步骤的检查证据独立判断\n");
context.append("5. 控制环节、控制要求、控制活动描述、控制职责岗位字段在所有相关记录中保持相同\n\n");
context.append("**注意:**\n");
context.append("1. 检查证据必须详细描述:针对当前测试步骤查阅了哪些制度文件、检查了哪些业务凭证、访谈了哪些人员、发现了什么问题\n");
context.append("2. 测试结果判定必须严格,对于制度不健全、执行不到位、证据不充分的情况必须判定为\"不通过\"\n");
context.append("3. 工作底稿索引必须准确对应实际审计工作底稿文件名称,不能使用章节标题\n");
context.append("4. 审计检查可采用:符合性测试、穿行测试、抽样测试、观察询问、文档查阅等方法\n");
context.append("5. 重点关注内部控制制度的建立健全性和执行的有效性\n\n");
// 3. 审计测试方法参考
context.append("## 审计测试方法参考\n");
context.append("审计时可采用的测试方法:\n");
TEST_METHODS.forEach(method -> context.append("- ").append(method).append("\n"));
context.append("\n");
// 4. 历史内容
if (StrUtil.isNotBlank(history)) {
context.append("## 历史生成内容\n");
context.append("以下是之前生成的内容,请基于此进行优化:\n");
context.append(history).append("\n\n");
}
// 5. 用户建议
if (StrUtil.isNotBlank(suggestion)) {
context.append("## 用户优化建议\n");
context.append("请根据以下建议对生成内容进行调整:\n");
context.append(suggestion).append("\n\n");
}
// 6. 企业单位知识
if (!knowledgeSources.get("enterprise").isEmpty()) {
context.append("## 企业单位知识\n");
knowledgeSources.get("enterprise").forEach(knowledge ->
context.append(knowledge).append("\n"));
context.append("\n");
}
// 7. 法律法规知识
if (!knowledgeSources.get("regulation").isEmpty()) {
context.append("## 法律法规知识\n");
knowledgeSources.get("regulation").forEach(knowledge ->
context.append(knowledge).append("\n"));
context.append("\n");
}
// 8. 审计案例知识
if (!knowledgeSources.get("auditCase").isEmpty()) {
context.append("## 审计案例知识\n");
knowledgeSources.get("auditCase").forEach(knowledge ->
context.append(knowledge).append("\n"));
}
return context.toString();
}
/**
* 内部控制相关性比较器
*/
private int internalControlComparator(String reg1, String reg2) {
int score1 = calculateInternalControlRelevanceScore(reg1);
int score2 = calculateInternalControlRelevanceScore(reg2);
return Integer.compare(score2, score1);
}
/**
* 计算内部控制相关性分数
*/
private int calculateInternalControlRelevanceScore(String content) {
return AuditContent8InternalControlConstants.KEYWORD_WEIGHTS.entrySet().stream()
.filter(entry -> content.contains(entry.getKey()))
.mapToInt(Map.Entry::getValue)
.sum();
}
private int getLimitBySourceType(String sourceType) {
switch (sourceType) {
case "enterprise": return 120;
case "regulation": return 100;
case "auditCase": return 80;
default: return 50;
}
}
}