From ae4161c822b5d41ad964d56ded54d4070aabfd1d Mon Sep 17 00:00:00 2001 From: yuance <182865460@qq.com> Date: Wed, 10 Dec 2025 16:49:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AE=A1=E8=AE=A1=E5=86=85?= =?UTF-8?q?=E5=AE=B97-=E9=87=8D=E5=A4=A7=E6=8A=95=E8=B5=84=E6=83=85?= =?UTF-8?q?=E5=86=B5=E5=AE=A1=E8=AE=A1=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/constants/AuditContent7Constants.java | 147 +++++++ .../controller/AuditContent7Controller.java | 301 +++++++++++++++ .../InvestmentSituationExportEntity.java | 31 ++ .../AuditContent7InvestmentService.java | 23 ++ .../AuditContent7InvestmentServiceImpl.java | 358 ++++++++++++++++++ 5 files changed, 860 insertions(+) create mode 100644 src/main/java/com/gxwebsoft/ai/constants/AuditContent7Constants.java create mode 100644 src/main/java/com/gxwebsoft/ai/controller/AuditContent7Controller.java create mode 100644 src/main/java/com/gxwebsoft/ai/dto/export/InvestmentSituationExportEntity.java create mode 100644 src/main/java/com/gxwebsoft/ai/service/AuditContent7InvestmentService.java create mode 100644 src/main/java/com/gxwebsoft/ai/service/impl/AuditContent7InvestmentServiceImpl.java diff --git a/src/main/java/com/gxwebsoft/ai/constants/AuditContent7Constants.java b/src/main/java/com/gxwebsoft/ai/constants/AuditContent7Constants.java new file mode 100644 index 0000000..4de0b84 --- /dev/null +++ b/src/main/java/com/gxwebsoft/ai/constants/AuditContent7Constants.java @@ -0,0 +1,147 @@ +package com.gxwebsoft.ai.constants; + +import java.util.HashMap; +import java.util.Map; +import java.util.Arrays; +import java.util.List; + +/** + * 重大投资情况审计常量类 + */ +public class AuditContent7Constants { + + // 审计类别定义(按审计内容7.txt要求) + public static final String CATEGORY_MAJOR_INVESTMENT = "重大对外投资审计"; + public static final String CATEGORY_MAJOR_PROJECT = "重大工程建设审计"; + public static final String CATEGORY_MAJOR_CAPITAL = "重大资本运作审计"; + public static final String CATEGORY_MAJOR_ASSET_DISPOSAL = "重大资产处置审计"; + public static final String CATEGORY_MAJOR_PROCUREMENT = "重大物资(服务)采购审计"; + public static final String CATEGORY_MAJOR_GUARANTEE = "重大担保借款审计"; + + // 审计类别描述 + public static final Map CATEGORY_DESCRIPTIONS = new HashMap<>(); + static { + CATEGORY_DESCRIPTIONS.put(CATEGORY_MAJOR_INVESTMENT, + "核查重大对外投资决策的合规性和民主性、审批手续、可行性研究、效益情况、会计处理、操作过程等"); + CATEGORY_DESCRIPTIONS.put(CATEGORY_MAJOR_PROJECT, + "工程建设项目决策程序、审批手续、安全质量、环保要求、效益情况等"); + CATEGORY_DESCRIPTIONS.put(CATEGORY_MAJOR_CAPITAL, + "重大资本运作事项决策权限、审批程序、交易对价、资金来源、效益效果等"); + CATEGORY_DESCRIPTIONS.put(CATEGORY_MAJOR_ASSET_DISPOSAL, + "重大资产处置决策程序、审批手续、交易价格、操作过程等"); + CATEGORY_DESCRIPTIONS.put(CATEGORY_MAJOR_PROCUREMENT, + "重大采购项目可行性、决策程序、采购过程、价格公允性、效益效果等"); + CATEGORY_DESCRIPTIONS.put(CATEGORY_MAJOR_GUARANTEE, + "重大担保借款决策程序、尽职调查、风险控制、后续跟踪等"); + } + + // 审计方法指令集(从审计内容7.txt提取) + public static final Map AUDIT_INSTRUCTIONS = new HashMap<>(); + static { + AUDIT_INSTRUCTIONS.put("GENERAL_RULE", + "审计方法及步骤:\n" + + "(1)收集全部企业的资产负债表和往来明细表,审计企业\"未分配利润\"的情况,对应查看其往来明细表,分析审计企业债务的偿还能力和债券的回收机率。\n" + + "(2)收集对其他单位进行投资的项目的收益报表及往来情况,审计投资的效益以及往来款项的回收机率。\n" + + "(3)对还在继续运营的特殊情况,审计企业的收益能力,分析继续运行的必要性、效益性。"); + + AUDIT_INSTRUCTIONS.put("DECISION_SYSTEM_CHECK", + "1、\"决策制度健全性验证\"指令:\n" + + "①制度存在性审查:根据关键字,检索被审计单位所有制度,是否有重大对外投资决策的制度规定\n" + + "②纵向合规性比对:将被审计单位的决策制度与国家、自治区、上级单位比较\n" + + "③横向一致性检查:与本单位其他制度比较,检查决策额度、表决比例、否决机制等多个维度的协同性\n"); + + AUDIT_INSTRUCTIONS.put("DECISION_PROCEDURE_CHECK", + "2、\"是否履行相应的决策程序\"指令:\n" + + "①决策程序完备性审查:根据被审计单位制度确定该项目是否属于重大投资\n" + + "②议事程序符合性审查:权限降级决策、参会人员范围、表决比例、材料替代违规、特别事项处理\n" + + "③民主决策真实性分析:发言均衡性检查、反对意见留存、表态顺序异常、决策逆转追踪\n" + + "④时序异常检查:立项时间、首次决策会日期、正式批准日、资金支付时间\n"); + } + + // 审计内容模板(从审计内容7.txt提取的具体审计点) + public static final Map> AUDIT_CONTENT_TEMPLATES = new HashMap<>(); + static { + // 重大对外投资审计内容 + AUDIT_CONTENT_TEMPLATES.put(CATEGORY_MAJOR_INVESTMENT, Arrays.asList( + "核查重大对外投资决策的合规性和民主性。企业是否履行相应的决策程序或相关决策制度是否建立健全,是否经过相应级别的领导班子集体讨论决策,有无简化决策程序或在多数人表示反对意见下仍强行通过决策,即领导个人说了算的情况。", + "核查重大对外投资审批手续。核查被审计企业与履行出资人职责的机构是否有明确的授权范围,对照国资委的授权清单,查看对外投资事项是否属于国资监管部门核准或备案事项,对外投资事项是否属于国家禁止或控制的投资范围。", + "核查重大对外投资可行性研究和尽职调查情况。是否经过可行性研究,相关论证是否充分,是否经过多方案的比较优选和科学论证,是否按规定开展尽职调查。", + "核査对外投资项目的效益情况。梳理企业被审计年度对外投资项目的效益情况,与可行性研究的预期效益相比较,是否存在明显效益低下、严重亏损、计提大额减值等情况。", + "查看长期投资、短期投资、银行存款等会计科目的明细账,确定重大对外投资项目的入账价值,审阅账面价值是否符合投资合同和决策所确定的投资额,重大差异是否有合理原因。审阅资金支付是否经过投资决策、合规审核后支付,是否存在超前支付等情况。", + "查看相关费用类科目或长期投资、短期投资和银行存款等会计科目的明细账,分析有关中介费等费用支岀情况,核实是否存在大额的、可疑的、超合同支付的中间费用;关注溢价收购的交易价格是否合理,核查溢价收购产生的大额商誉,是否根据被收购企业未来业绩实际情况,制定相应的或有对价条款,从而调整交易对价、降低收购不确定性。", + "审核\"长期投资\"等会计科目,分析投资事项的运营效益,是否存在重大亏损;调取被投资项目(或企业)的运营情况、会计报表等,分析企业账面是否如实反映项目盈亏。", + "重大对外投资到期收回、出售或转让的,核实取得的对价是否及时入账;调取被投资企业投资分红账务资料,核实分红派息资金是否按时收回并入账。", + "审核决策程序。详细查阅党委(组)会、股东会、董事会、监事会、总经理办公会和专题小组会等会议记录和会议纪要。查看相关决策程序是否合规,有无严格落实重大事项决策经过党组织前置研究的规定,是否经相应领导级别会议讨论研究;查看决策内容是否民主科学,仔细查阅参会人员的发言,有无存在主持会议的领导人员在多数人持不同意见甚至反对意见的情况下仍强行通过决议的情况,即领导人员\"说了算\"的情况。", + "审核审批过程。按照深化国有企业改革精神和改革国有资本授权经营体制的要求,梳理履行出资人职责的机构对被审计企业的授权范围,核查关注的重大对外投资事项是否需要国资监管部门核准或备案,有无领导人员擅自决策、超权限决策的情况;对照企业投资负面清单,审核对外投资事项是否属于国家禁止或控制的投资范围,核查是否取得行业主管部门审批,相关审批手续是否完备。", + "审核调查论证。查阅对外投资事项从立项到决策实施整个过程的论证材料,如可行性研究报告、财务咨询报告、法律意见书等。应关注以下三个方面:一是立项背景,重大对外投资项目是否符合国家经济结构调整方向,如是否符合供给侧结构性改革和产业发展方向;二是企业发展战略,重大对外投资事项是否符合企业主业经营范围,是否符合企业中长期发展规划;三是预期效益,对比可行性研究报告、财务咨询报告中关于项目预期的经营效益、投资规模、筹资与还款计划、预计资金回收期等内容,判断有无盲目投资、超偿还能力筹措资金等情况。", + "审核操作过程。审查重大对外投资合同内容是否与企业集体决策内容相符,重大对外投资事项是否属于未公开的关联交易事项,相关交易价格是否经过专业机构评估,交易价格是否客观公允,会计师事务所、资产评估机构是否违反有关规定出具虚假报告,或领导人员违规指定交易方,先确定交易价格再进行资产评估,或领导人员擅自更改交易价格等。", + "审核效益效果。核査重大对外投资企业是否正常经营,有无存在被投资企业效益持续低下,严重亏损甚至已经资不抵债,效益远低于预期;投资参股后是否积极行使股东权利,履行股东义务,有无因擅自扩大投资规模导致严重亏损,人为提高交易价格向特定关系人输送利益等违规问题。" + )); + + // 重大工程建设审计内容(类似方式添加其他类别) + AUDIT_CONTENT_TEMPLATES.put(CATEGORY_MAJOR_PROJECT, Arrays.asList( + "工程建设项目是否经企业相应领导层级集体讨论研究,有无领导人员擅自决策,特别是领导班子多数人持不同意见的情况下仍强行通过决策的情况。", + "相关立项审批手续是否完善,根据国资国企授权经营体制改革的有关要求,重大工程建设项目是否符合企业投资项目负面清单范围,有无应报未报、未批先建、违规建设楼堂馆所等。", + "工程建设项目有无出现安全和质量事故,工程设备是否符合污染防治有关要求,相关工程废物、废渣、废水等是否得到有效收集和处理,工程建设是否造成环境污染,环保设施是否有效运营。", + "工程建设项目竣工投产后的效益情况,是否存在竣工投入使用后长期闲置、计提大额减值、效益远低于预期产生巨额亏损等。", + "审核决策程序。对选定的重大工程建设项目,应详细查看其会议纪要和会议记录,审阅管理制度、签报、审批文件等资料。查看相关决策事项是否履行相应决策程序,如是否经党组织前置研究和相应层级领导班子集体讨论研究,决策内容是否符合民主要求,有无被审计领导人员在多数人反对的情况下强行通过决策,即领导人员\"说了算\"的情况。", + "审核审批过程。根据深化国有企业改革精神和改革国有资本授权经营体制的要求,核查重大工程建设项目是否属于企业自主决策事项,是否需要国资监管部门核准或者备案,若属于其他行业主管部门审批事项,企业还应获得如发展改革委、住建部门、环保部门等主管部门的相关审批手续,有无未批先建,化整为零规避审批。", + "审核调查论证。查阅项目从立项到决策实施整个过程的论证材料,如可行性研究报告、财务咨询报告、法律意见书等。可行性研究报告的内容是否完整、真实;建设目标、建设规模是否符合企业发展需要,规划是否符合行业发展前景和产业政策。", + "审核操作过程。重大工程建设项目是否严格按照集体决策的方案开展,工程的建设质量、环保标准是否符合要求,是否有定期的质量检查,有无发生重大安全质量事故、发生重大环保污染问题被有关部门处罚,或者工程建设进度严重滞后、无法按期竣工等问题。", + "审核效益效果。重大工程建设项目的效益,主要是两个方面:一是效益是否达到预期。与可行性研究报告、财务咨询报告等比较,对比项目投产后是否达到预期效益;与市场上同类型项目效益对比,有无存在效益明显低于同类型项目的情况。二是工程建设项目的运营情况,是否存在项目竣工投产后运行未达预期、长期闲置,或因工程建设项目与市场需求严豆偏离计提重大减值,导致大额亏损等问题。" + )); + + // 其他类别类似添加... + } + + // 关键词权重(用于知识检索排序) + public static final Map KEYWORD_WEIGHTS = new HashMap<>(); + static { + KEYWORD_WEIGHTS.put("重大投资", 10); + KEYWORD_WEIGHTS.put("对外投资", 9); + KEYWORD_WEIGHTS.put("工程建设", 9); + KEYWORD_WEIGHTS.put("资本运作", 9); + KEYWORD_WEIGHTS.put("资产处置", 9); + KEYWORD_WEIGHTS.put("物资采购", 9); + KEYWORD_WEIGHTS.put("担保借款", 9); + KEYWORD_WEIGHTS.put("决策程序", 8); + KEYWORD_WEIGHTS.put("审批手续", 8); + KEYWORD_WEIGHTS.put("可行性研究", 8); + KEYWORD_WEIGHTS.put("效益情况", 8); + KEYWORD_WEIGHTS.put("会议纪要", 7); + KEYWORD_WEIGHTS.put("会议记录", 7); + KEYWORD_WEIGHTS.put("可行性报告", 7); + KEYWORD_WEIGHTS.put("尽职调查", 7); + KEYWORD_WEIGHTS.put("关联交易", 7); + KEYWORD_WEIGHTS.put("价格公允", 7); + } + + // 文件类型关键词(用于工作底稿索引) + public static final Map> FILE_TYPE_KEYWORDS = new HashMap<>(); + static { + FILE_TYPE_KEYWORDS.put("会议材料", Arrays.asList("会议通知", "签到表", "会议记录", "会议纪要", "会议决议", "会议录像")); + FILE_TYPE_KEYWORDS.put("审批文件", Arrays.asList("立项批复", "审批文件", "核准文件", "备案文件", "红头文件")); + FILE_TYPE_KEYWORDS.put("论证材料", Arrays.asList("可行性研究报告", "财务咨询报告", "法律意见书", "尽职调查报告", "专家论证意见")); + FILE_TYPE_KEYWORDS.put("合同协议", Arrays.asList("投资合同", "合作协议", "担保合同", "借款合同", "采购合同", "转让协议")); + FILE_TYPE_KEYWORDS.put("财务资料", Arrays.asList("资产负债表", "利润表", "现金流量表", "往来明细表", "会计凭证", "银行流水")); + FILE_TYPE_KEYWORDS.put("评估报告", Arrays.asList("资产评估报告", "审计报告", "验资报告", "价值评估报告")); + } + + private AuditContent7Constants() { + // 防止实例化 + } + + /** + * 获取所有审计类别(按顺序) + */ + public static List getAllCategories() { + return Arrays.asList( + CATEGORY_MAJOR_INVESTMENT, + CATEGORY_MAJOR_PROJECT, + CATEGORY_MAJOR_CAPITAL, + CATEGORY_MAJOR_ASSET_DISPOSAL, + CATEGORY_MAJOR_PROCUREMENT, + CATEGORY_MAJOR_GUARANTEE + ); + } +} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/ai/controller/AuditContent7Controller.java b/src/main/java/com/gxwebsoft/ai/controller/AuditContent7Controller.java new file mode 100644 index 0000000..a5d0186 --- /dev/null +++ b/src/main/java/com/gxwebsoft/ai/controller/AuditContent7Controller.java @@ -0,0 +1,301 @@ +package com.gxwebsoft.ai.controller; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.gxwebsoft.ai.dto.AuditContentRequest; +import com.gxwebsoft.ai.dto.export.InvestmentSituationExportEntity; +import com.gxwebsoft.ai.entity.AiCloudDoc; +import com.gxwebsoft.ai.entity.AiCloudFile; +import com.gxwebsoft.ai.service.AiHistoryService; +import com.gxwebsoft.ai.utils.ExcelExportTool; +import com.gxwebsoft.common.core.web.ApiResult; +import com.gxwebsoft.common.core.web.BaseController; +import com.gxwebsoft.common.system.entity.User; +import com.gxwebsoft.pwl.entity.PwlProjectLibrary; +import com.gxwebsoft.pwl.service.PwlProjectLibraryService; +import com.gxwebsoft.ai.service.AiCloudDocService; +import com.gxwebsoft.ai.service.AiCloudFileService; +import com.gxwebsoft.ai.service.AuditContent7InvestmentService; +import com.gxwebsoft.ai.service.KnowledgeBaseService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.digest.DigestUtil; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 审计内容7控制器 - 重大投资情况审计 + */ +@Slf4j +@Tag(name = "审计内容7-重大投资情况") +@RestController +@RequestMapping("/api/ai/auditContent7") +public class AuditContent7Controller extends BaseController { + + @Autowired + private AuditContent7InvestmentService auditContent7InvestmentService; + + @Autowired + private AiCloudDocService aiCloudDocService; + + @Autowired + private AiCloudFileService aiCloudFileService; + + @Autowired + private KnowledgeBaseService knowledgeBaseService; + + @Autowired + private PwlProjectLibraryService pwlProjectLibraryService; + + @Autowired + private AiHistoryService aiHistoryService; + + // 历史记录有效期(分钟) + private static final int HISTORY_EXPIRE_MINUTES = 10; + + /** + * 生成重大投资情况审计表数据 + */ + @Operation(summary = "生成重大投资情况审计表") + @PostMapping("/generateInvestmentSituationTable") + public ApiResult generateInvestmentSituationTable(@RequestBody AuditContentRequest request, HttpServletRequest servletRequest) { + return generateTableData(request, servletRequest.getRequestURI(), + (params) -> auditContent7InvestmentService.generateInvestmentSituationTableData( + params.knowledgeBaseId, params.libraryKbIds, params.projectLibrary, + params.username, params.history, params.suggestion + )); + } + + /** + * 通用的表格数据生成方法 + */ + private ApiResult generateTableData(AuditContentRequest request, String interfaceName, Function generateFunction) { + final User loginUser = getLoginUser(); + String requestHistory = request.getHistory(); + request.setHistory(""); + + // 检查历史记录 + String requestHash = generateRequestHash(request, interfaceName); + var history = aiHistoryService.getValidHistory(requestHash, interfaceName, HISTORY_EXPIRE_MINUTES); + if (history != null) { + log.info("返回历史数据,请求哈希: {}", requestHash); + return success(JSONObject.parseObject(history.getResponseData())); + } + request.setHistory(requestHistory); + + String kbIdTmp = ""; + String libraryKbIds = ""; + + try { + // 创建临时知识库(如果需要) + if (hasUploadedFiles(request)) { + kbIdTmp = createTempKnowledgeBase(request); + } + + // 查询项目库信息 + libraryKbIds = getLibraryKbIds(request.getLibraryIds()); + + // 生成数据 + String knowledgeBaseId = getKnowledgeBaseId(kbIdTmp, request.getKbIds()); + GenerateParams params = new GenerateParams(knowledgeBaseId, libraryKbIds, request.getProjectLibrary(), loginUser.getUsername(), request.getHistory(), request.getSuggestion()); + + JSONObject result = generateFunction.apply(params); + + if(result.getBoolean("success")) { + // 保存到历史记录 + saveToHistory(request, interfaceName, requestHash, result, loginUser); + } + + return success(result); + } catch (Exception e) { + log.error("生成表格数据失败,接口: {}", interfaceName, e); + return fail("生成表格数据失败: " + e.getMessage()); + } finally { + cleanupTempKnowledgeBase(kbIdTmp); + } + } + + /** + * 生成请求哈希 + */ + private String generateRequestHash(AuditContentRequest request, String interfaceName) { + String requestJson = JSONObject.toJSONString(request); + return DigestUtil.md5Hex(interfaceName + ":" + requestJson); + } + + /** + * 保存到历史记录 + */ + private void saveToHistory(AuditContentRequest request, String interfaceName, String requestHash, JSONObject result, User loginUser) { + try { + aiHistoryService.saveHistory(requestHash, interfaceName, JSONObject.toJSONString(request), result.toJSONString(), loginUser.getUserId(), loginUser.getUsername(), loginUser.getTenantId()); + } catch (Exception e) { + log.warn("保存历史记录失败", e); + } + } + + /** + * 检查是否有上传的文件 + */ + private boolean hasUploadedFiles(AuditContentRequest request) { + return !request.getDocList().isEmpty() || !request.getFileList().isEmpty(); + } + + /** + * 获取知识库ID + */ + private String getKnowledgeBaseId(String tempKbId, String requestKbIds) { + return StrUtil.isNotBlank(tempKbId) ? tempKbId : requestKbIds; + } + + /** + * 获取项目库KB IDs + */ + private String getLibraryKbIds(String libraryIds) { + if (StrUtil.isBlank(libraryIds)) { + return ""; + } + List idList = StrUtil.split(libraryIds, ','); + List ret = pwlProjectLibraryService.list(new LambdaQueryWrapper().in(PwlProjectLibrary::getId, idList)); + return ret.stream().map(PwlProjectLibrary::getKbId).filter(StrUtil::isNotBlank).collect(Collectors.joining(",")); + } + + /** + * 创建临时知识库并提交文档 + */ + private String createTempKnowledgeBase(AuditContentRequest request) { + String kbIdTmp = knowledgeBaseService.createKnowledgeBaseTemp(); + // 收集文档ID + Set docIds = request.getDocList().stream().flatMap(docId -> aiCloudDocService.getSelfAndChildren(docId).stream()).map(AiCloudDoc::getId).collect(Collectors.toSet()); + // 查询相关文件 + List fileList = getRelatedFiles(docIds, request.getFileList()); + // 提取文件ID并提交到知识库 + Set kbFileIds = fileList.stream().map(AiCloudFile::getFileId).collect(Collectors.toSet()); + if (!kbFileIds.isEmpty()) { + knowledgeBaseService.submitDocuments(kbIdTmp, new ArrayList<>(kbFileIds)); + } + return kbIdTmp; + } + + /** + * 获取相关文件列表 + */ + private List getRelatedFiles(Set docIds, List fileList) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() + .in(!docIds.isEmpty(), AiCloudFile::getDocId, docIds) + .or(!fileList.isEmpty()) + .in(!fileList.isEmpty(), AiCloudFile::getId, fileList); + return aiCloudFileService.list(queryWrapper); + } + + /** + * 清理临时知识库 + */ + private void cleanupTempKnowledgeBase(String kbId) { + if (StrUtil.isNotBlank(kbId)) { + try { + knowledgeBaseService.deleteIndex(kbId); + } catch (Exception e) { + log.warn("删除临时知识库失败: {}", kbId, e); + } + } + } + + /** + * 导出重大投资情况审计表到Excel + */ + @Operation(summary = "导出重大投资情况审计表到Excel") + @PostMapping("/exportInvestmentSituationTable") + public void exportInvestmentSituationTable(@RequestBody Map request, HttpServletResponse response) { + exportToExcel(request, response, "重大投资情况审计表", + this::convertToInvestmentSituationEntityList, InvestmentSituationExportEntity.class); + } + + /** + * 通用的Excel导出方法 + */ + private void exportToExcel(Map request, HttpServletResponse response, + String sheetName, Function>, List> converter, + Class entityClass) { + List> dataList = (List>) request.get("data"); + String companyName = (String) request.get("companyName"); + String auditTime = (String) request.get("auditTime"); + + List exportData = converter.apply(dataList); + + String fileName = sheetName + "_" + (companyName != null ? companyName : "未知公司"); + String title = companyName != null ? companyName + " - " + sheetName : sheetName; + + if (auditTime != null) { + title += "(审计时间:" + auditTime + ")"; + } + + ExcelExportTool.exportExcel(exportData, entityClass, fileName, sheetName, title, response); + } + + /** + * 参数包装类 + */ + private static class GenerateParams { + final String knowledgeBaseId; + final String libraryKbIds; + final String projectLibrary; + final String username; + final String history; + final String suggestion; + + GenerateParams(String knowledgeBaseId, String libraryKbIds, String projectLibrary, String username, String history, String suggestion) { + this.knowledgeBaseId = knowledgeBaseId; + this.libraryKbIds = libraryKbIds; + this.projectLibrary = projectLibrary; + this.username = username; + this.history = history; + this.suggestion = suggestion; + } + } + + // ========== 数据转换方法 ========== + + private List convertToInvestmentSituationEntityList(List> originalData) { + return originalData.stream().map(this::convertToInvestmentSituationEntity).collect(Collectors.toList()); + } + + private InvestmentSituationExportEntity convertToInvestmentSituationEntity(Map item) { + InvestmentSituationExportEntity entity = new InvestmentSituationExportEntity(); + entity.setCategory(getStringValue(item, "category")); + entity.setAuditContent(getStringValue(item, "auditContent")); + entity.setCheckEvidence(getStringValue(item, "checkEvidence")); + entity.setTestResult(getStringValue(item, "testResult")); + entity.setWorkPaperIndex(formatWorkPaperIndex(item.get("workPaperIndex"))); + entity.setFileIndex(formatWorkPaperIndex(item.get("fileIndex"))); + return entity; + } + + private String getStringValue(Map map, String key) { + Object value = map.get(key); + return value != null ? value.toString() : ""; + } + + private String formatWorkPaperIndex(Object workPaperIndex) { + if (workPaperIndex == null) { + return ""; + } + + if (workPaperIndex instanceof List) { + List list = (List) workPaperIndex; + return String.join(", ", list.stream() + .map(Object::toString) + .collect(Collectors.toList())); + } + + return workPaperIndex.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/ai/dto/export/InvestmentSituationExportEntity.java b/src/main/java/com/gxwebsoft/ai/dto/export/InvestmentSituationExportEntity.java new file mode 100644 index 0000000..f79c70c --- /dev/null +++ b/src/main/java/com/gxwebsoft/ai/dto/export/InvestmentSituationExportEntity.java @@ -0,0 +1,31 @@ +package com.gxwebsoft.ai.dto.export; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import cn.afterturn.easypoi.excel.annotation.ExcelTarget; +import lombok.Data; + +/** + * 重大投资情况审计表导出实体 + */ +@Data +@ExcelTarget("InvestmentSituationExportEntity") +public class InvestmentSituationExportEntity { + + @Excel(name = "审计类别", orderNum = "1", width = 20) + private String category; + + @Excel(name = "审计内容", orderNum = "2", width = 60) + private String auditContent; + + @Excel(name = "检查的证据及测试内容", orderNum = "3", width = 80) + private String checkEvidence; + + @Excel(name = "测试结果", orderNum = "4", width = 15) + private String testResult; + + @Excel(name = "工作底稿索引", orderNum = "5", width = 40) + private String workPaperIndex; + + @Excel(name = "文件索引", orderNum = "6", width = 40) + private String fileIndex; +} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/ai/service/AuditContent7InvestmentService.java b/src/main/java/com/gxwebsoft/ai/service/AuditContent7InvestmentService.java new file mode 100644 index 0000000..0d4401a --- /dev/null +++ b/src/main/java/com/gxwebsoft/ai/service/AuditContent7InvestmentService.java @@ -0,0 +1,23 @@ +package com.gxwebsoft.ai.service; + +import com.alibaba.fastjson.JSONObject; + +/** + * 审计内容7-重大投资情况服务接口 + */ +public interface AuditContent7InvestmentService { + + /** + * 生成重大投资情况审计表数据 + * @param kbIds 知识库ID + * @param libraryKbIds 项目库知识库ID + * @param projectLibrary 审计案例库ID + * @param userName 用户名 + * @param history 历史记录 + * @param suggestion 用户建议 + * @return 生成的表格数据 + */ + JSONObject generateInvestmentSituationTableData(String kbIds, String libraryKbIds, + String projectLibrary, String userName, + String history, String suggestion); +} \ No newline at end of file diff --git a/src/main/java/com/gxwebsoft/ai/service/impl/AuditContent7InvestmentServiceImpl.java b/src/main/java/com/gxwebsoft/ai/service/impl/AuditContent7InvestmentServiceImpl.java new file mode 100644 index 0000000..a42eb7b --- /dev/null +++ b/src/main/java/com/gxwebsoft/ai/service/impl/AuditContent7InvestmentServiceImpl.java @@ -0,0 +1,358 @@ +package com.gxwebsoft.ai.service.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.gxwebsoft.ai.constants.AuditContent7Constants; +import com.gxwebsoft.ai.service.AuditContent7InvestmentService; +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 AuditContent7InvestmentServiceImpl extends AbstractAuditContentService implements AuditContent7InvestmentService { + + // Dify工作流配置 + private static final String DIFY_WORKFLOW_TOKEN = "Bearer app-rNhgcQkNr2JJP9lePT3ICzgW"; + + // 审计类别顺序 + private static final List CATEGORY_ORDER = AuditContent7Constants.getAllCategories(); + + @Override + public JSONObject generateInvestmentSituationTableData(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> futures = processCategoriesAsync( + CATEGORY_ORDER, + category -> generateCategoryDataAsync(category, kbIds, libraryKbIds, projectLibrary, userName, history, suggestion) + ); + + // 等待所有异步任务完成 + CompletableFuture.allOf(futures.values().toArray(new CompletableFuture[0])).join(); + + // 合并所有分类的结果 + JSONArray allData = mergeCategoryResults(CATEGORY_ORDER, futures); + + log.info("重大投资情况审计表生成成功 - 记录数: {}, 处理时间: {}ms", allData.size(), (System.currentTimeMillis() - startTime)); + + return buildSuccessResponse(allData, startTime, "investment_situation_audit"); + + } catch (Exception e) { + log.error("生成重大投资情况审计表失败", e); + return buildErrorResponse("生成重大投资情况审计表失败: " + e.getMessage()); + } + } + + /** + * 异步生成单个审计类别的数据 + */ + @Async + public CompletableFuture generateCategoryDataAsync(String category, String kbIds, + String libraryKbIds, String projectLibrary, + String userName, String history, String suggestion) { + return CompletableFuture.supplyAsync(() -> { + try { + log.info("开始生成审计类别 {} 的数据", category); + + // 1. 为当前审计类别召回相关知识 + Map> knowledgeSources = retrieveKnowledgeForCategory( + category, kbIds, libraryKbIds, projectLibrary + ); + + // 2. 构建完整的知识上下文 + String knowledgeContext = buildCompleteKnowledgeContext( + category, knowledgeSources, history, suggestion + ); + + // 3. 调用Dify工作流生成数据 + JSONObject requestBody = buildWorkflowRequest(knowledgeContext, userName); + JSONArray categoryData = callWorkflow(DIFY_WORKFLOW_URL, DIFY_WORKFLOW_TOKEN, requestBody, "重大投资情况-" + category); + + log.info("审计类别 {} 数据生成完成,生成 {} 条记录", category, categoryData.size()); + return categoryData; + + } catch (Exception e) { + log.error("生成审计类别 {} 数据失败", category, e); + return new JSONArray(); + } + }); + } + + /** + * 为单个审计类别检索相关知识 + */ + private Map> retrieveKnowledgeForCategory(String category, String kbIds, + String libraryKbIds, String projectLibrary) { + Map> knowledgeSources = new HashMap<>(); + knowledgeSources.put("enterprise", new ArrayList<>()); + knowledgeSources.put("regulation", new ArrayList<>()); + knowledgeSources.put("auditCase", new ArrayList<>()); + knowledgeSources.put("financial", new ArrayList<>()); // 财务数据单独处理 + + // 构建当前审计类别的查询词 + List categoryQueries = buildCategoryQueries(category); + + // 企业单位库检索 + if (StrUtil.isNotBlank(kbIds)) { + Arrays.stream(kbIds.split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .forEach(kbId -> { + knowledgeSources.get("enterprise") + .addAll(queryKnowledgeBase(kbId, categoryQueries, 120)); + // 单独查询财务相关文档 + knowledgeSources.get("financial") + .addAll(queryKnowledgeBase(kbId, + Arrays.asList("资产负债表", "利润表", "现金流量表", "会计科目", "银行流水"), 80)); + }); + } + + // 公共法律法规库检索 + if (StrUtil.isNotBlank(libraryKbIds)) { + Arrays.stream(libraryKbIds.split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .forEach(libId -> knowledgeSources.get("regulation") + .addAll(queryKnowledgeBase(libId, categoryQueries, 100))); + } + + // 审计案例库检索 + if (StrUtil.isNotBlank(projectLibrary)) { + knowledgeSources.get("auditCase").addAll( + queryKnowledgeBase(projectLibrary, categoryQueries, 80)); + } + + // 智能去重和排序(按相关性) + knowledgeSources.forEach((key, list) -> { + List processed = list.stream() + .distinct() + .sorted(this::investmentRelevanceComparator) + .limit(getLimitBySourceType(key)) + .collect(Collectors.toList()); + knowledgeSources.put(key, processed); + }); + + log.debug("审计类别 {} 知识检索完成 - 企业: {}条, 法规: {}条, 案例: {}条, 财务: {}条", + category, + knowledgeSources.get("enterprise").size(), + knowledgeSources.get("regulation").size(), + knowledgeSources.get("auditCase").size(), + knowledgeSources.get("financial").size()); + + return knowledgeSources; + } + + /** + * 构建审计类别特定的查询词 + */ + private List buildCategoryQueries(String category) { + switch (category) { + case AuditContent7Constants.CATEGORY_MAJOR_INVESTMENT: + return Arrays.asList("对外投资 决策程序 审批手续 可行性研究 投资效益", + "长期投资 短期投资 投资合同 投资回报"); + case AuditContent7Constants.CATEGORY_MAJOR_PROJECT: + return Arrays.asList("工程建设 项目审批 安全质量 环保要求 工程效益", + "工程项目 工程管理 竣工验收"); + case AuditContent7Constants.CATEGORY_MAJOR_CAPITAL: + return Arrays.asList("资本运作 并购重组 交易对价 资金来源", + "股权交易 资产评估 资本运营"); + case AuditContent7Constants.CATEGORY_MAJOR_ASSET_DISPOSAL: + return Arrays.asList("资产处置 资产评估 交易价格 产权交易", + "资产转让 资产拍卖 资产划转"); + case AuditContent7Constants.CATEGORY_MAJOR_PROCUREMENT: + return Arrays.asList("物资采购 服务采购 招标投标 采购价格", + "采购合同 供应商 采购管理"); + case AuditContent7Constants.CATEGORY_MAJOR_GUARANTEE: + return Arrays.asList("担保借款 担保合同 借款合同 风险控制", + "对外担保 借款审批 抵押质押"); + default: + return Arrays.asList(category); + } + } + + /** + * 构建完整的知识上下文 + */ + private String buildCompleteKnowledgeContext(String category, Map> knowledgeSources, + String history, String suggestion) { + StringBuilder context = new StringBuilder(); + + // 1. 审计要求说明 + context.append("## 重大投资情况审计要求 - ").append(category).append("\n"); + context.append("请基于以下知识生成").append(category).append("相关的审计表格数据:\n\n"); + context.append("**审计目标:**\n"); + context.append("- 核查重大投资决策的合规性和民主性\n"); + context.append("- 检查审批手续的完备性\n"); + context.append("- 评估可行性研究和尽职调查的充分性\n"); + context.append("- 核査投资项目的效益情况\n"); + context.append("- 识别决策程序、审批过程、操作过程中的风险和问题\n\n"); + + // 2. 审计方法(从常量中获取) + context.append("## 审计方法及步骤\n"); + context.append(AuditContent7Constants.AUDIT_INSTRUCTIONS.get("GENERAL_RULE")).append("\n\n"); + + // 3. 审计指令(决策程序检查) + context.append("## 审计检查指令\n"); + context.append(AuditContent7Constants.AUDIT_INSTRUCTIONS.get("DECISION_SYSTEM_CHECK")).append("\n"); + context.append(AuditContent7Constants.AUDIT_INSTRUCTIONS.get("DECISION_PROCEDURE_CHECK")).append("\n\n"); + + // 4. 数据格式要求 + context.append("## 数据格式要求\n"); + context.append("需要生成").append(category).append("分类的数据,尽可能生成多个实例:\n"); + context.append("- ").append(category).append(":").append(AuditContent7Constants.CATEGORY_DESCRIPTIONS.get(category)).append("\n"); + context.append("\n"); + + context.append("每条记录应包含5个核心字段:\n"); + context.append("- **category**:固定为\"").append(category).append("\"\n"); + context.append("- **auditContent**:具体的审计内容,从以下模板中选择或根据知识库生成\n"); + context.append("- **checkEvidence**:详细的检查证据描述,必须包含:\n"); + context.append(" ①查阅的具体文件名称和内容\n"); + context.append(" ②检查过程和方法\n"); + context.append(" ③与制度要求的差异点\n"); + context.append(" ④明确的总结性结论(问题、风险、合规状况)\n"); + context.append("- **testResult**:测试结果(通过/不通过/待检查),严格判断:\n"); + context.append(" • 通过:证据充分且完全符合要求\n"); + context.append(" • 不通过:制度不一致、执行不到位、证据不充分\n"); + context.append(" • 待检查:无法确定,需要进一步检查\n"); + context.append("- **workPaperIndex**:相关《工作底稿索引》,必须是实际存在的完整文件名,确保能在文件夹中搜索到\n"); + context.append("- **fileIndex**:相关《文件索引》,关联的支持文件\n\n"); + + // 5. 审计内容模板(提供参考) + context.append("## 审计内容模板(供参考)\n"); + List templates = AuditContent7Constants.AUDIT_CONTENT_TEMPLATES.get(category); + if (templates != null && !templates.isEmpty()) { + for (int i = 0; i < Math.min(templates.size(), 5); i++) { + context.append(i + 1).append(". ").append(templates.get(i)).append("\n"); + } + } + context.append("(请根据知识库内容,从不同角度生成多个审计检查点)\n\n"); + + // 6. 文件类型参考(用于工作底稿索引) + context.append("## 相关文件类型参考\n"); + AuditContent7Constants.FILE_TYPE_KEYWORDS.forEach((type, keywords) -> { + context.append("- ").append(type).append(":").append(String.join("、", keywords)).append("\n"); + }); + context.append("\n"); + + // 7. 历史内容 + if (StrUtil.isNotBlank(history)) { + context.append("## 历史生成内容\n"); + context.append("以下是之前生成的内容,请基于此进行优化:\n"); + context.append(history).append("\n\n"); + } + + // 8. 用户建议 + if (StrUtil.isNotBlank(suggestion)) { + context.append("## 用户优化建议\n"); + context.append("请根据以下建议对生成内容进行调整:\n"); + context.append(suggestion).append("\n\n"); + } + + // 9. 企业单位知识 + if (!knowledgeSources.get("enterprise").isEmpty()) { + context.append("## 企业单位制度知识\n"); + knowledgeSources.get("enterprise").forEach(knowledge -> + context.append(knowledge).append("\n")); + context.append("\n"); + } + + // 10. 法律法规知识 + if (!knowledgeSources.get("regulation").isEmpty()) { + context.append("## 法律法规知识\n"); + knowledgeSources.get("regulation").forEach(knowledge -> + context.append(knowledge).append("\n")); + context.append("\n"); + } + + // 11. 审计案例知识 + if (!knowledgeSources.get("auditCase").isEmpty()) { + context.append("## 审计案例知识\n"); + knowledgeSources.get("auditCase").forEach(knowledge -> + context.append(knowledge).append("\n")); + context.append("\n"); + } + + // 12. 财务数据知识 + if (!knowledgeSources.get("financial").isEmpty()) { + context.append("## 财务数据知识\n"); + context.append("(重点关注:资产负债表、利润表、会计科目、银行流水等)\n"); + knowledgeSources.get("financial").forEach(knowledge -> + context.append(knowledge).append("\n")); + } + + return context.toString(); + } + + /** + * 重大投资情况相关性比较器 + */ + private int investmentRelevanceComparator(String reg1, String reg2) { + int score1 = calculateInvestmentRelevanceScore(reg1); + int score2 = calculateInvestmentRelevanceScore(reg2); + return Integer.compare(score2, score1); // 降序排列 + } + + /** + * 计算重大投资情况相关性分数 + */ + private int calculateInvestmentRelevanceScore(String content) { + return AuditContent7Constants.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; + case "financial": return 60; + default: return 50; + } + } + + /** + * 重写callWorkflow方法,适配重大投资情况工作流的返回格式 + */ + @Override + protected JSONArray callWorkflow(String url, String token, JSONObject requestBody, String workflowName) { + try { + log.info("调用{}工作流,请求体长度: {}", workflowName, requestBody.toString().length()); + + // 调用父类方法获取工作流响应 + JSONArray result = super.callWorkflow(url, token, requestBody, workflowName); + + // 如果父类方法返回结果为空,则尝试按重大投资情况特有的格式解析 + if (result == null || result.isEmpty()) { + // 这里可以根据需要添加特定的解析逻辑 + log.warn("{}工作流返回结果为空,使用默认空数组", workflowName); + return new JSONArray(); + } + + log.info("成功获取{}工作流返回数据,记录数: {}", workflowName, result.size()); + return result; + + } catch (Exception e) { + log.error("调用{}工作流失败", workflowName, e); + throw new RuntimeException("调用" + workflowName + "工作流失败: " + e.getMessage(), e); + } + } + +} \ No newline at end of file