新增审计内容8-内部控制测试接口
This commit is contained in:
@@ -0,0 +1,326 @@
|
||||
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. 如果测试步骤中有多个小点(如(1)、(2)、(3)),必须为每个小点生成独立记录\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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user