Compare commits
23 Commits
db53938d88
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c91ec620e | |||
| 6fc21fd142 | |||
| 58c4d5a540 | |||
| ddc1fa2dbd | |||
| b0d32c05d6 | |||
| bfbc7ba24b | |||
| 32f48732f4 | |||
| a4aa1ef479 | |||
| 2a64e5117b | |||
| bdf4cde39f | |||
| 9689ce9e23 | |||
| d04305c325 | |||
| ae1e150d1c | |||
| 378d37f590 | |||
| 9629c7234d | |||
| 86d9f4892c | |||
| 6c5b2057a7 | |||
| 45a88a94ea | |||
| a284472782 | |||
| 696df82963 | |||
| f407deb7ad | |||
| d2fe476b1a | |||
| 02bdd456f4 |
@@ -8,5 +8,9 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "ai.template")
|
||||
public class TemplateConfig {
|
||||
|
||||
private String wordTemplatePath;
|
||||
|
||||
private String evidenceTemplatePath;
|
||||
|
||||
}
|
||||
@@ -192,7 +192,8 @@ public class AuditContent10PartyConductConstants {
|
||||
obj.put("detailCategory", detailCategory);
|
||||
obj.put("content", content);
|
||||
obj.put("executionStatus", "");
|
||||
obj.put("workPaperIndex", "[\"实际存在的完整文件名1\", \"实际存在的完整文件名2\", \"...\"]");
|
||||
obj.put("workPaperIndex", "[\"实际存在的完整文件名1||FileUrl1\", \"实际存在的完整文件名2||FileUrl2\", \"...\"]");
|
||||
// obj.put("workPaperIndex", "[\"实际存在的完整FileId1\", \"实际存在的完整FileId2\", \"...\"]");
|
||||
FIXED_ORDER_EXAMPLE_DATA.add(obj);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,35 @@ public class AuditContent1EightRegConstants {
|
||||
"简化机场迎送和接待工作。驻外使领馆和其他驻外机构一律不得向代表团赠送礼品,外方所赠礼品应严格按国家有关规定处理。");
|
||||
}
|
||||
|
||||
|
||||
// 审计建议内容 - 新增的审计建议
|
||||
public static final Map<String, String> AUDIT_SUGGESTIONS = new HashMap<>();
|
||||
static {
|
||||
AUDIT_SUGGESTIONS.put(CATEGORY_RESEARCH,
|
||||
"如审查后认定:确为缺少系统性规范和调研记录。建议:\n" +
|
||||
"1.补足制度短板,系统性规范公司层面调研工作管理要求;\n" +
|
||||
"2.强化过程管理与材料归档,确保调研活动可视、可查、可评,切实发挥服务决策、解决问题的实效。");
|
||||
|
||||
AUDIT_SUGGESTIONS.put(CATEGORY_MEETINGS,
|
||||
"如审查确认被审计单位存在未严格遵循《中央八项规定》中关于'精简会议活动'的规定,可提出审计建议:\n" +
|
||||
"建议减少不必要的会议数量,提高会议质量和效率,避免因频繁开会而造成人力资源浪费。在召开会议前,应明确会议的具体目的和议题,避免无目标、无计划的会议。");
|
||||
|
||||
AUDIT_SUGGESTIONS.put(CATEGORY_VISITS,
|
||||
"如审查确认被审计单位存在未严格遵循《中央八项规定》中关于'轻车简从''不得扰民'的要求,可提出审计建议:\n" +
|
||||
"切实规范领导人员公务出行管理,严格贯彻落实'轻车简从''不得扰民'等要求。明确规定考察线路须事前报备、严格控制随员及车辆规模、严禁超标接待与礼仪迎送、严禁使用警车等非必要车辆疏导清场。责立健全常态化监管与问责机制,确保有关规定执行到位,对违规行为严肃追责。");
|
||||
|
||||
AUDIT_SUGGESTIONS.put(CATEGORY_GUARD_WORK,
|
||||
"如审查确认被审计单位存在未严格遵循《中央八项规定》中关于'轻车简从''不得扰民'的要求,可提出审计建议:\n" +
|
||||
"切实规范领导人员公务出行管理,严格贯彻落实'轻车简从''不得扰民'等要求。明确规定考察线路须事前报备、严格控制随员及车辆规模、严禁超标接待与礼仪迎送、严禁使用警车等非必要车辆疏导清场。责立健全常态化监管与问责机制,确保有关规定执行到位,对违规行为严肃追责。");
|
||||
|
||||
AUDIT_SUGGESTIONS.put(CATEGORY_ECONOMY,
|
||||
"如审查后认定:确为会议活动经费超支。可提出审计建议:\n" +
|
||||
"强化会议活动经费预算控制,严查支出明细,杜绝超标准住宿餐饮、高消费活动、豪华布置及纪念品发放。\n\n" +
|
||||
"其他建议:\n" +
|
||||
"1.各直属企业定期对内部控制情况开展实质性评价,杜绝形式主义。\n" +
|
||||
"2.上级公司强化对下属企业的监督与检查,保障相关制度有效执行。");
|
||||
}
|
||||
|
||||
// 关键词权重
|
||||
public static final Map<String, Integer> KEYWORD_WEIGHTS = new HashMap<>();
|
||||
static {
|
||||
|
||||
@@ -14,8 +14,9 @@ public class AuditContent1ExpenseConstants {
|
||||
public static final String EXPENSE_TYPE_VEHICLE = "公车运行维护";
|
||||
public static final String EXPENSE_TYPE_MEETING = "会议培训费";
|
||||
|
||||
// 年度范围
|
||||
public static final int[] YEAR_RANGE = {2020, 2021, 2022, 2023};
|
||||
// 年度范围 - 根据知识库动态确定,这里提供默认起始年份
|
||||
public static final int DEFAULT_START_YEAR = 2020;
|
||||
public static final int DEFAULT_END_YEAR = 2023;
|
||||
|
||||
// 分类描述
|
||||
public static final Map<String, String> EXPENSE_DESCRIPTIONS = new HashMap<>();
|
||||
@@ -26,43 +27,79 @@ public class AuditContent1ExpenseConstants {
|
||||
EXPENSE_DESCRIPTIONS.put(EXPENSE_TYPE_MEETING, "会议、培训费用支出情况");
|
||||
}
|
||||
|
||||
// 数据格式要求
|
||||
// 数据格式要求 - 简化版
|
||||
public static final String DATA_FORMAT_REQUIREMENT =
|
||||
"每条记录应包含以下字段:\n" +
|
||||
"- expenseType:支出类型(公务接待/出国/公车运行维护/会议培训费)\n" +
|
||||
"- year:年份(2020-2023)\n" +
|
||||
"- finalStatementAmount:决算报表数(单位:元)\n" +
|
||||
"- initialBudgetAmount:年初预算数(单位:元)\n" +
|
||||
"- changePercentage:增减情况(%)\n" +
|
||||
"- budgetRatio:占年初预算比例(%)\n" +
|
||||
"- remark:备注信息\n" +
|
||||
"- dataSource:数据来源文件\n" +
|
||||
"- workPaperIndex:相关文件索引";
|
||||
"请严格按照以下JSON格式生成数据,每类支出每个年度至少1条记录,总共至少16条记录:\n" +
|
||||
"[\n" +
|
||||
" {\n" +
|
||||
" \"expenseType\": \"公务接待/出国/公车运行维护/会议培训费\",\n" +
|
||||
" \"year\": \"具体年份(根据知识库中的连续四个完整年度)\",\n" +
|
||||
" \"finalStatementAmount\": \"决算报表数\",\n" +
|
||||
" \"initialBudgetAmount\": \"年初预算数\",\n" +
|
||||
" \"changePercentage\": \"增减百分比\",\n" +
|
||||
" \"budgetRatio\": \"占年初预算比例\",\n" +
|
||||
" \"remark\": \"备注信息\",\n" +
|
||||
" \"dataSource\": \"数据来源文件\",\n" +
|
||||
// " \"workPaperIndex\": [\"FileId1\", \"FileId2\"]\n" +
|
||||
" \"workPaperIndex\": [\"实际存在的完整文件名1||FileUrl1\", \"实际存在的完整文件名2||FileUrl2\"]\n" +
|
||||
" }\n" +
|
||||
"]";
|
||||
|
||||
// 关键词权重
|
||||
// 关键词权重 - 增强工程类排除
|
||||
public static final Map<String, Integer> KEYWORD_WEIGHTS = new HashMap<>();
|
||||
static {
|
||||
KEYWORD_WEIGHTS.put("决算", 10);
|
||||
KEYWORD_WEIGHTS.put("预算", 10);
|
||||
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("费用", 7);
|
||||
// 高优先级:直接支出关键词
|
||||
KEYWORD_WEIGHTS.put("公务接待", 15);
|
||||
KEYWORD_WEIGHTS.put("出国", 15);
|
||||
KEYWORD_WEIGHTS.put("公车运行维护", 15);
|
||||
KEYWORD_WEIGHTS.put("会议培训费", 15);
|
||||
KEYWORD_WEIGHTS.put("三公经费", 14);
|
||||
KEYWORD_WEIGHTS.put("接待费", 13);
|
||||
KEYWORD_WEIGHTS.put("差旅费", 12);
|
||||
KEYWORD_WEIGHTS.put("车辆费", 12);
|
||||
KEYWORD_WEIGHTS.put("会务费", 12);
|
||||
KEYWORD_WEIGHTS.put("培训费", 12);
|
||||
|
||||
// 中优先级:财务文档
|
||||
KEYWORD_WEIGHTS.put("决算报表", 10);
|
||||
KEYWORD_WEIGHTS.put("预算报表", 10);
|
||||
KEYWORD_WEIGHTS.put("财务报表", 9);
|
||||
KEYWORD_WEIGHTS.put("年度报告", 8);
|
||||
KEYWORD_WEIGHTS.put("部门决算", 9);
|
||||
KEYWORD_WEIGHTS.put("部门预算", 9);
|
||||
KEYWORD_WEIGHTS.put("预算执行", 8);
|
||||
KEYWORD_WEIGHTS.put("财务分析", 7);
|
||||
|
||||
// 年份关键词(中等优先级,用于识别年度)
|
||||
KEYWORD_WEIGHTS.put("年度", 7);
|
||||
KEYWORD_WEIGHTS.put("年报表", 7);
|
||||
KEYWORD_WEIGHTS.put("年度决算", 8);
|
||||
KEYWORD_WEIGHTS.put("年度预算", 8);
|
||||
|
||||
// 低优先级:通用财务
|
||||
KEYWORD_WEIGHTS.put("支出", 7);
|
||||
KEYWORD_WEIGHTS.put("费用", 6);
|
||||
KEYWORD_WEIGHTS.put("年度报告", 6);
|
||||
|
||||
// 排除工程类(负权重)
|
||||
KEYWORD_WEIGHTS.put("工程造价", -20);
|
||||
KEYWORD_WEIGHTS.put("概预算", -20);
|
||||
KEYWORD_WEIGHTS.put("工程款", -15);
|
||||
KEYWORD_WEIGHTS.put("施工", -15);
|
||||
KEYWORD_WEIGHTS.put("项目投资", -15);
|
||||
KEYWORD_WEIGHTS.put("基建", -15);
|
||||
}
|
||||
|
||||
// 数据来源关键词
|
||||
public static final String[] DATA_SOURCE_KEYWORDS = {
|
||||
"决算报表", "预算报表", "财务报告", "年度报告",
|
||||
"预算执行情况", "经费使用情况", "支出明细", "费用分析"
|
||||
// 需要排除的工程类关键词
|
||||
public static final String[] ENGINEERING_EXCLUDE_KEYWORDS = {
|
||||
"工程造价", "概预算", "工程概算", "工程预算", "工程结算",
|
||||
"施工合同", "工程款", "项目投资", "基建", "工程项目"
|
||||
};
|
||||
|
||||
// 年份正则表达式,用于从知识库中提取年份
|
||||
public static final String YEAR_PATTERN = "20\\d{2}";
|
||||
|
||||
// 最近几年范围(用于查询构建)
|
||||
public static final int RECENT_YEARS_COUNT = 4;
|
||||
|
||||
private AuditContent1ExpenseConstants() {
|
||||
// 防止实例化
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ public class AuditContent1LeaderListConstants {
|
||||
"- tenurePeriod:任职期间(格式:YYYY.MM-YYYY.MM)\n" +
|
||||
"- mainResponsibilities:主要工作责任\n" +
|
||||
"- remark:备注信息\n" +
|
||||
"- workPaperIndex:相关文件索引";
|
||||
// "- workPaperIndex:[相关文件FileId]";
|
||||
"- workPaperIndex:[\"实际存在的完整文件名1||FileUrl1\"]";
|
||||
|
||||
// 关键词权重
|
||||
public static final Map<String, Integer> KEYWORD_WEIGHTS = new HashMap<>();
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package com.gxwebsoft.ai.constants;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 审计内容2-单位发展战略执行常量类
|
||||
@@ -52,7 +49,22 @@ public class AuditContent2StrategyConstants {
|
||||
CATEGORY_DEVELOPMENT_CONCEPT.put(CATEGORY_PERFORMANCE_ASSESSMENT, "创新");
|
||||
}
|
||||
|
||||
// 完整的审计框架(原Excel内容)- 保持不变
|
||||
// 分类查询关键词
|
||||
public static final Map<String, List<String>> CATEGORY_QUERIES = new HashMap<>();
|
||||
static {
|
||||
CATEGORY_QUERIES.put(CATEGORY_GOVERNANCE_REFORM,
|
||||
Arrays.asList("法人治理 薪酬改革 国有资产管理", "改革创新 治理结构 董事会"));
|
||||
CATEGORY_QUERIES.put(CATEGORY_DEVELOPMENT_STRATEGY,
|
||||
Arrays.asList("混合所有制 剥离办社会职能 改革措施", "协调发展 改革方案 政策落实"));
|
||||
CATEGORY_QUERIES.put(CATEGORY_RISK_PREVENTION,
|
||||
Arrays.asList("发展规划 金融风险 债务风险", "绿色发展 风险防控 资产负债率"));
|
||||
CATEGORY_QUERIES.put(CATEGORY_SOCIAL_ENVIRONMENTAL,
|
||||
Arrays.asList("精准扶贫 污染防治 环境保护", "开放共享 社会责任 定点扶贫"));
|
||||
CATEGORY_QUERIES.put(CATEGORY_PERFORMANCE_ASSESSMENT,
|
||||
Arrays.asList("考核指标 经营业绩 目标责任", "创新发展 数据核实 完成情况"));
|
||||
}
|
||||
|
||||
// 完整的审计框架(原Excel内容)
|
||||
public static final String AUDIT_FRAMEWORK =
|
||||
"审计内容框架:\n" +
|
||||
"1. 公司法人治理结构建立健全情况。是否贯彻落实将党组织研究讨论作为董事会、经理层决策重大问题前置程序的要求;是否加强董事会建设、有效落实董事会职权;各治理主体是否职责明确、落实到位。\n" +
|
||||
@@ -69,44 +81,39 @@ public class AuditContent2StrategyConstants {
|
||||
"12. 对照国资委、财政部、工业和信息化部等与企业签订的年度经营业绩责任书、任期经营业绩责任书、中央单位定点扶贫工作责任书等,梳理企业承担的考核内容和指标。\n" +
|
||||
"13. 对于可以量化的审计事项,利用大数据分析方法,系统收集和整理各类考核指标信息,建立企业考核指标数据库,对照企业提供的财务或相关统计报表,检查企业完成情况。检查相关指标的原始数据、计算方法、计算过程,核实其真实性和准确性,对于依靠人工填报、设置调整系数、与以往年度相差较大或与考核值相差较小的数据,重点审计真实性、完整性、准确性。\n";
|
||||
|
||||
// 审计目标说明 - 优化版,突出五大发展理念
|
||||
// 审计目标说明
|
||||
public static final String AUDIT_OBJECTIVE =
|
||||
"审计目标:检查被审计领导干部任职期间在" +
|
||||
"创新、协调、绿色、开放、共享五大发展理念指引下," +
|
||||
"合法合规制定本部门发展规划和发展思路," +
|
||||
"推动规划和政策措施实施的时间表、路线图及其执行效果。";
|
||||
|
||||
// 分类与审计框架片段的映射(重新调整,更加均衡)
|
||||
// 分类与审计框架片段的映射
|
||||
public static final Map<String, String> CATEGORY_AUDIT_FRAMEWORK_FRAGMENTS = new HashMap<>();
|
||||
static {
|
||||
// 法人治理与改革创新 - 对应框架的1-3点(创新理念)
|
||||
CATEGORY_AUDIT_FRAMEWORK_FRAGMENTS.put(CATEGORY_GOVERNANCE_REFORM,
|
||||
"审计内容框架(法人治理与改革创新相关):\n" +
|
||||
"1. 公司法人治理结构建立健全情况。是否贯彻落实将党组织研究讨论作为董事会、经理层决策重大问题前置程序的要求;是否加强董事会建设、有效落实董事会职权;各治理主体是否职责明确、落实到位。\n" +
|
||||
"2. 薪酬制度改革情况。薪酬分配差异化改革过程中是否存在进展不平衡、政策不配套、任务不协调等问题;有无超工资总额列支工资性支出;企业领导人员在核定的年度薪酬之外领取报酬,是否严格规范履职待遇、业务支出,有无将公款用于个人支出等问题。\n" +
|
||||
"3. 完善国有资产管理体制方面。有关部门是否建立健全监管权力清单和责任清单,是否存在越位、缺位、不到位等问题;国有资本投资、运营公司改革试点工作的情况,有无要求方案部署不够明确、资本授权及经营资质受限、相关企业未制定时间表路线图、未与兼并重组等改革试点任务协同推进、资本运营偏离改革方向、战略性前瞻性产业投资不增反降、党政机关和事业单位所属企业的国有资本纳入经营性国有资产集中统一监管体系推进迟缓、国有资本经营预算管理不到位等问题。\n");
|
||||
|
||||
// 发展规划与协调推进 - 对应框架的4-6点(协调理念)
|
||||
CATEGORY_AUDIT_FRAMEWORK_FRAGMENTS.put(CATEGORY_DEVELOPMENT_STRATEGY,
|
||||
"审计内容框架(发展规划与协调推进相关):\n" +
|
||||
"4. 发展混合所有制经济方面。是否存在\"混而不改\",改革流于形式、效果不佳的问题;清产核资、评估定价、转让交易、登记确权等相关政策、审批和实施程序是否合法合规,在国有资产和产权转让过程中是否存在国有资产流失等问题;员工持股试点推进过程中存在的政策性障碍。此外,还要从国有企业改革工作总休上关注,混合所有制改革作为国有企业改革的重要突破口的作用发挥情况。\n" +
|
||||
"5. 剥离办社会职能和解决历史遗留问题方面。国有企业有无制定时间表路线图,是否按时限完成国有企业职工家属区\"三供一业\"分离移交,剥离国有企业办医疗、教育等公共服务机构,对国有企业退休人员实行社会化管理,推进厂办大集体改革等问题,遇到什么困难、需要什么政策和多少资金支持,等等;相关部门的配套政策是否建立健全。\n" +
|
||||
"6. 国有企业改革措施是否制定?制定的措施是否符合党中央、国务院、省市的相关文件规定和精神。\n");
|
||||
|
||||
// 风险防控与绿色发展 - 对应框架的7-9点(绿色理念)
|
||||
CATEGORY_AUDIT_FRAMEWORK_FRAGMENTS.put(CATEGORY_RISK_PREVENTION,
|
||||
"审计内容框架(风险防控与绿色发展相关):\n" +
|
||||
"7. 企业年度工作报告中关于改革措施的落实情况,进度是否符合预期,是否违反整体改革措施计划。规划执行情况材料是否合规、完整。企业境外资产、投资管理办法和内控制度,及企业向国资委等有关部门报送的境外资产产权登记情况和境外资产财务报表数据等,是否合规、完整、符合企业实际情况。企业制定的公司发展规划,包括总规划和专项规划(如主业、国际化、金融、财务、人力资源、建设、生产、营销、科技、信息化、安全应急、企业文化等),是否符合企业整体实际情况,是否符合党中央、国务院、省市的相关文件规定和精神。公司发展规划滚动调整情况是否符合实际情况,是否为了减轻企业目标故意调整。公司发展规划中提及的制定依据,是否符合国家有关部门制定的行业产业发展规划等。\n" +
|
||||
"8. 金融业务风险。关注金融业务服务实体经济和主业发展情况,有无脱实向虚、通过金融产品将资金违规投向房地产、地方政府融资平台、产能过剩等限制或禁止领域的问题;关注企业开展境外金融衍生品情况,有无违规开展业务造成重大损失或损失风险等;关注银行、证券、保险、信托等金融子企业的资产管理规模及经营情况,风险管控机制是否健全,业务运营是否合规,有无信贷资金、金融资产造成重大损失的问题,有无金融产品逾期或违规展期、不良资产比率较高、风险与收益不匹配、本金偿付风险或违约风险大等问题;关注企业债券发行、委托理财、对外担保等金融业务开展中存在的突出问题,是否造成重大损失或损失风险等。\n" +
|
||||
"9. 债务风险。关注企业落实降杠杆减负债政策情况,是否存在企业资产负债率居高不下、超过警戒线或管控线甚至资不抵债,有无建立健全企业债务风险防控机制,发挥资产负债约束机制作用,债务结构是否合理,有无违规对不符合国家产业政策的企业实施债转股,有无违规通过\"名股实债\"等方式变相举债或形成重大隐性债务,以及虚假去杠杆等问题。\n");
|
||||
|
||||
// 社会责任与开放共享 - 对应框架的10-11点(开放共享理念)
|
||||
CATEGORY_AUDIT_FRAMEWORK_FRAGMENTS.put(CATEGORY_SOCIAL_ENVIRONMENTAL,
|
||||
"审计内容框架(社会责任与开放共享相关):\n" +
|
||||
"10. 精准扶贫政策落实情况。关注企业在精准扶贫相关政策落实、项目安排、资金使用等方面的推进情况,是否落实企业定点扶贫工作的任务要求,有无出台具体帮扶措施,履行帮扶责任定点扶贫的目标对象、工作举措、资金使用等是否精准;有无通过发展产业、对接市场、安置就业等多种方式开展精准扶贫行动;以中央企业贫困地区产业投资基金、贫困地区产业发展基金等为代表的央企产业扶贫基金出资额是否及时足额缴纳、有无大量资金闲置和项目运营效果不佳等问题。\n" +
|
||||
"11. 污染防治工作推进情况。企业贯彻落实生态环境保护和环境污染防治相关政策措施情况,重点是打蹴蓝天保卫战、打好碧水保卫战和推进净土保卫战等相关措施的落实情况。是否存在非法占地、违规改变土地使用条件、倒卖土地、土地闲置的问题;企业是否完成国家节能减排任务目标,有无不顾生态环境盲目决策和建设项目,造成重大环境污染和资源损毁等问题;有无违规偷排、漏排、超排废渣废液废气,瞒报、漏报检测数据,有无违规堆放、未按规定处理工业危险废弃物、危险化学品等问题;对所属企业发生的破坏生态环境情况,是否存在追责问责不到位等问题。企业是否建立能源消耗及污染物排放统计台账;是否按时定期将本企业节能减排汇总报表和总结分析报告报送有关部门,有无漏报、迟报、不按要求报送;重点类、关注类企业是否在总结分析报告中开展与同行业节能减排技术指标的对标和分析。\n");
|
||||
|
||||
// 绩效考核与创新发展 - 对应框架的12-13点(创新理念)
|
||||
CATEGORY_AUDIT_FRAMEWORK_FRAGMENTS.put(CATEGORY_PERFORMANCE_ASSESSMENT,
|
||||
"审计内容框架(绩效考核与创新发展相关):\n" +
|
||||
"12. 对照国资委、财政部、工业和信息化部等与企业签订的年度经营业绩责任书、任期经营业绩责任书、中央单位定点扶贫工作责任书等,梳理企业承担的考核内容和指标。\n" +
|
||||
@@ -139,9 +146,12 @@ public class AuditContent2StrategyConstants {
|
||||
"3. 检查证据要真实具体,有文件依据\n" +
|
||||
"4. 测试结果判定要严格,有充分依据\n" +
|
||||
"5. 工作底稿索引要准确对应实际文件\n" +
|
||||
"6. 注重评估五大发展理念贯彻情况";
|
||||
"6. 注重评估五大发展理念贯彻情况\n" +
|
||||
"7. 必须深入分析实际执行情况(结果),重点检查:\n" +
|
||||
" (1) 是否有会议纪要等材料证明决策按照党组织委员会→公司领导班子→董事会逐级落实;\n" +
|
||||
" (2) 是否有相应材料对执行内容进行说明和证明,展示具体的执行过程和执行效果。";
|
||||
|
||||
// 关键词权重 - 根据新分类优化
|
||||
// 关键词权重
|
||||
public static final Map<String, Integer> KEYWORD_WEIGHTS = new HashMap<>();
|
||||
static {
|
||||
// 创新类关键词
|
||||
@@ -174,4 +184,11 @@ public class AuditContent2StrategyConstants {
|
||||
KEYWORD_WEIGHTS.put("审计证据", 6);
|
||||
}
|
||||
|
||||
// 各知识源检索限制
|
||||
public static final Map<String, Integer> SOURCE_LIMITS = new HashMap<>();
|
||||
static {
|
||||
SOURCE_LIMITS.put("enterprise", 120);
|
||||
SOURCE_LIMITS.put("regulation", 60);
|
||||
SOURCE_LIMITS.put("auditCase", 40);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ public class AuditContent4TargetConstants {
|
||||
"4. 检查审计目标责任的完成情况\n\n" +
|
||||
"审计方法及步骤:\n" +
|
||||
"1. 审阅上级主管部门下达或单位自定的目标责任制,对照完成情况,检查目标责任制的落实效果\n" +
|
||||
"2. 主要以地矿局下达的目标任务为衡量标准,地矿局没有下达目标任务的,以单位自定的目标任务为衡量标准\n" +
|
||||
"2. 主要以单位自定的目标任务为衡量标准\n" +
|
||||
"3. 对已完成任务的部分,检查是否真实完成\n" +
|
||||
"4. 对未完成任务的部分,检查未完成的原因\n\n" +
|
||||
"特别说明:\n" +
|
||||
@@ -67,7 +67,6 @@ public class AuditContent4TargetConstants {
|
||||
KEYWORD_WEIGHTS.put("未完成", 7);
|
||||
KEYWORD_WEIGHTS.put("已完成", 7);
|
||||
KEYWORD_WEIGHTS.put("上级主管", 7);
|
||||
KEYWORD_WEIGHTS.put("地矿局", 6);
|
||||
}
|
||||
|
||||
// 审计要点提示
|
||||
@@ -91,7 +90,8 @@ public class AuditContent4TargetConstants {
|
||||
"7. selfCompletion: 自定完成情况\n" +
|
||||
"8. selfReason: 自定未完成原因\n" +
|
||||
"9. remark: 备注\n" +
|
||||
"10. workPaperIndex: 工作底稿索引";
|
||||
// "10. workPaperIndex: [相关文件FileId]";
|
||||
"10. workPaperIndex: [相关文件的完整文件名||FileUrl]";
|
||||
|
||||
// 获取分类的简要审计框架概述
|
||||
public static String getBriefAuditFrameworkForCategory(String category) {
|
||||
@@ -139,7 +139,8 @@ public class AuditContent4TargetConstants {
|
||||
" \"selfCompletion\": \"自定目标完成情况(已完成/部分完成/未完成)\",\n" +
|
||||
" \"selfReason\": \"自定目标未完成原因(如已完成,填'无未完成原因')\",\n" +
|
||||
" \"remark\": \"备注\",\n" +
|
||||
" \"workPaperIndex\": [\"实际存在的完整文件名1\", \"实际存在的完整文件名2\", ...]\n" +
|
||||
// " \"workPaperIndex\": [\"实际存在的完整FileId1\", \"实际存在的完整FileId2\", ...]\n" +
|
||||
" \"workPaperIndex\": [\"实际存在的完整文件名1||FileUrl1\", \"实际存在的完整文件名2||FileUrl2\", ...]\n" +
|
||||
" }\n" +
|
||||
"]\n\n" +
|
||||
"重要说明:\n" +
|
||||
|
||||
@@ -176,7 +176,8 @@ public class AuditContent5BudgetExecutionConstants {
|
||||
" \"additionalBudgetAmount\": \"追加预算数\",\n" +
|
||||
" \"actualAppropriation\": \"实际拨款数\",\n" +
|
||||
" \"indicatorBalance\": \"指标结余\",\n" +
|
||||
" \"workPaperIndex\": [\"实际存在的完整文件名1\", \"实际存在的完整文件名2\", ...]\n" +
|
||||
// " \"workPaperIndex\": [\"实际存在的完整FileId1\", \"实际存在的完整FileId2\", ...]\n" +
|
||||
" \"workPaperIndex\": [\"实际存在的完整文件名1||FileUrl1\", \"实际存在的完整文件名2||FileUrl2\", ...]\n" +
|
||||
" }\n" +
|
||||
"]\n\n" +
|
||||
"**重要说明:**\n" +
|
||||
|
||||
@@ -186,7 +186,8 @@ public class AuditContent5BudgetManageConstants {
|
||||
" \"governmentProcurement\": \"政府采购\",\n" +
|
||||
" \"payableToUnit\": \"应拨单位款\",\n" +
|
||||
" \"other\": \"其他(可简要说明审计发现)\",\n" +
|
||||
" \"workPaperIndex\": [\"实际存在的完整文件名1\", \"实际存在的完整文件名2\", ...]\n" +
|
||||
// " \"workPaperIndex\": [\"实际存在的完整FileId1\", \"实际存在的完整FileId2\", ...]\n" +
|
||||
" \"workPaperIndex\": [\"实际存在的完整文件名1||FileUrl1\", \"实际存在的完整文件名2||FileUrl2\", ...]\n" +
|
||||
" }\n" +
|
||||
"]\n\n" +
|
||||
"**重要说明:**\n" +
|
||||
|
||||
@@ -103,7 +103,8 @@ public class AuditContent6StateAssetsConstants {
|
||||
" \"approvalDoc\": \"国有资产出租的审批文件(如未出租填'未出租')\",\n" +
|
||||
" \"inBudget\": \"是否纳入预算(是/否)\",\n" +
|
||||
" \"remark\": \"备注(包含资产状态、使用情况、审计发现等)\",\n" +
|
||||
" \"workPaperIndex\": [\"实际存在的完整文件名1\", \"实际存在的完整文件名2\", ...]\n" +
|
||||
// " \"workPaperIndex\": [\"实际存在的完整FileId1\", \"实际存在的完整FileId2\", ...]\n" +
|
||||
" \"workPaperIndex\": [\"实际存在的完整文件名1||FileUrl1\", \"实际存在的完整文件名2||FileUrl2\", ...]\n" +
|
||||
" }\n" +
|
||||
"]\n\n" +
|
||||
"重要说明:\n" +
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
package com.gxwebsoft.ai.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gxwebsoft.ai.dto.AuditEvidenceRequest;
|
||||
import com.gxwebsoft.ai.service.AuditEvidenceService;
|
||||
import com.gxwebsoft.common.core.web.ApiResult;
|
||||
import com.gxwebsoft.common.core.web.BaseController;
|
||||
import com.gxwebsoft.common.system.entity.User;
|
||||
|
||||
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.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.poi.openxml4j.util.ZipSecureFile;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFDocument;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFRun;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFTable;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
|
||||
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.gxwebsoft.ai.config.TemplateConfig;
|
||||
import cn.afterturn.easypoi.word.WordExportUtil;
|
||||
|
||||
/**
|
||||
* 审计取证单控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@Tag(name = "审计取证单")
|
||||
@RestController
|
||||
@RequestMapping("/api/ai/auditEvidence")
|
||||
public class AuditEvidenceController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private AuditEvidenceService auditEvidenceService;
|
||||
|
||||
@Autowired
|
||||
private TemplateConfig templateConfig;
|
||||
|
||||
/**
|
||||
* 生成审计取证单
|
||||
*/
|
||||
@Operation(summary = "生成审计取证单")
|
||||
@PostMapping("/generate")
|
||||
public ApiResult<?> generateAuditEvidence(@RequestBody AuditEvidenceRequest request) {
|
||||
try {
|
||||
final User loginUser = getLoginUser();
|
||||
request.setUserName(loginUser.getUsername());
|
||||
|
||||
log.info("接收到审计取证单生成请求 - 用户: {}, 项目: {}", request.getUserName(), request.getProjectName());
|
||||
JSONObject result = auditEvidenceService.generateAuditEvidence(request);
|
||||
if (Boolean.TRUE.equals(result.getBoolean("success"))) {
|
||||
return success(result);
|
||||
} else {
|
||||
return fail(result.getString("error") != null ? result.getString("error") : "生成审计取证单失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("生成审计取证单异常", e);
|
||||
return fail("生成审计取证单异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载审计取证单Word文档
|
||||
*/
|
||||
@Operation(summary = "下载审计取证单Word文档")
|
||||
@PostMapping("/download")
|
||||
public void downloadAuditEvidence(@RequestBody AuditEvidenceRequest request, HttpServletResponse response) {
|
||||
double originalMinInflateRatio = ZipSecureFile.getMinInflateRatio();
|
||||
|
||||
try {
|
||||
ZipSecureFile.setMinInflateRatio(0.001);
|
||||
|
||||
// 准备模板数据 - 将取证单字段映射到Word模板
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
// map.put("caseIndex", request.getCaseIndex() != null ? request.getCaseIndex() : "");
|
||||
map.put("caseIndex", request.getCaseIndex() != null ? String.format("%-50s", request.getCaseIndex()) : String.format("%-50s", ""));
|
||||
map.put("pageIndex", request.getPageIndex() != null ? request.getPageIndex() : "1");
|
||||
map.put("pageTotal", request.getPageTotal() != null ? request.getPageTotal() : "1");
|
||||
map.put("projectName", request.getProjectName() != null ? request.getProjectName() : "");
|
||||
map.put("auditedTarget", request.getAuditedTarget() != null ? request.getAuditedTarget() : "");
|
||||
map.put("auditMatter", request.getAuditMatter() != null ? request.getAuditMatter() : "");
|
||||
map.put("summaryTitle", request.getSummaryTitle() != null ? request.getSummaryTitle() : "");
|
||||
map.put("auditRecord", request.getAuditRecord() != null ? request.getAuditRecord() : "");
|
||||
map.put("auditFinding", request.getAuditFinding() != null ? request.getAuditFinding() : "");
|
||||
map.put("evidenceBasis", request.getEvidenceBasis() != null ? request.getEvidenceBasis() : "");
|
||||
map.put("handling", request.getHandling() != null ? request.getHandling() : "");
|
||||
map.put("attachment", request.getAttachment() != null ? request.getAttachment() : "");
|
||||
map.put("auditors", request.getAuditors() != null ? request.getAuditors() : "");
|
||||
map.put("compileDate", request.getCompileDate() != null ? request.getCompileDate() : "");
|
||||
map.put("providerOpinion", request.getProviderOpinion() != null ? request.getProviderOpinion() : "");
|
||||
map.put("providerDate", request.getProviderDate() != null ? request.getProviderDate() : "");
|
||||
map.put("attachmentPages", request.getAttachmentPages() != null ? request.getAttachmentPages() : "");
|
||||
map.put("feedbackDeadline", request.getFeedbackDeadline() != null ? request.getFeedbackDeadline() : "");
|
||||
|
||||
// 获取登录用户信息
|
||||
final User loginUser = getLoginUser();
|
||||
if (request.getAuditors() == null || request.getAuditors().isEmpty()) {
|
||||
map.put("auditors", loginUser.getUsername());
|
||||
}
|
||||
|
||||
// 使用 Easypoi 的 Word 模板功能生成取证单
|
||||
XWPFDocument document = WordExportUtil.exportWord07(
|
||||
templateConfig.getEvidenceTemplatePath(), // 需要配置取证单模板路径
|
||||
map
|
||||
);
|
||||
|
||||
// 处理换行,确保 \n 转为硬回车(新段落)
|
||||
processParagraphs(document);
|
||||
|
||||
// 设置响应头
|
||||
String fileName = "审计取证单_" + (request.getProjectName() != null ? request.getProjectName() : "取证单") + ".docx";
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=" + java.net.URLEncoder.encode(fileName, "UTF-8"));
|
||||
|
||||
// 输出文档
|
||||
try (OutputStream out = response.getOutputStream()) {
|
||||
document.write(out);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("生成审计取证单Word文档失败", e);
|
||||
throw new RuntimeException("生成审计取证单失败", e);
|
||||
} finally {
|
||||
ZipSecureFile.setMinInflateRatio(originalMinInflateRatio);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理段落换行(增强版,同时处理表格单元格)
|
||||
*/
|
||||
private void processParagraphs(XWPFDocument document) {
|
||||
// 1. 处理普通段落
|
||||
List<XWPFParagraph> originalParas = new ArrayList<>(document.getParagraphs());
|
||||
|
||||
for (XWPFParagraph para : originalParas) {
|
||||
String text = para.getText();
|
||||
if (text == null || !text.contains("\n")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] parts = text.replace("\r\n", "\n").replace("\r", "\n").split("\n");
|
||||
|
||||
// 在原段落位置之前插入新段落
|
||||
int pos = document.getPosOfParagraph(para);
|
||||
|
||||
// 修正:按正序插入(从前往后)
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
// 创建新段落
|
||||
XWPFParagraph newPara = document.insertNewParagraph(para.getCTP().newCursor());
|
||||
|
||||
// 复制样式
|
||||
newPara.getCTP().setPPr(para.getCTP().getPPr());
|
||||
|
||||
XWPFRun newRun = newPara.createRun();
|
||||
newRun.setText(parts[i].trim());
|
||||
|
||||
if (!para.getRuns().isEmpty()) {
|
||||
newRun.getCTR().setRPr(para.getRuns().get(0).getCTR().getRPr());
|
||||
}
|
||||
}
|
||||
|
||||
// 删除原段落
|
||||
document.removeBodyElement(pos + parts.length);
|
||||
}
|
||||
|
||||
// 2. 处理表格单元格中的段落
|
||||
List<XWPFTable> tables = document.getTables();
|
||||
for (XWPFTable table : tables) {
|
||||
for (XWPFTableRow row : table.getRows()) {
|
||||
for (XWPFTableCell cell : row.getTableCells()) {
|
||||
List<XWPFParagraph> cellParagraphs = cell.getParagraphs();
|
||||
for (XWPFParagraph para : cellParagraphs) {
|
||||
String text = para.getText();
|
||||
if (text == null || !text.contains("\n")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 清空原段落内容
|
||||
for (int i = para.getRuns().size() - 1; i >= 0; i--) {
|
||||
para.removeRun(i);
|
||||
}
|
||||
|
||||
// 分割文本并按行添加
|
||||
String[] lines = text.replace("\r\n", "\n").replace("\r", "\n").split("\n");
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
XWPFRun run = para.createRun();
|
||||
run.setText(lines[i].trim());
|
||||
|
||||
// 如果不是最后一行,添加换行
|
||||
if (i < lines.length - 1) {
|
||||
run.addBreak();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gxwebsoft.ai.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.gxwebsoft.ai.dto.AuditContentRequest;
|
||||
@@ -88,6 +89,8 @@ public abstract class BaseAuditContentController extends BaseController {
|
||||
JSONObject result = generateFunction.apply(params);
|
||||
|
||||
if(result.getBoolean("success")) {
|
||||
// 转换workPaperIndex,原数据:["文件名1"+"||"+"下载地址1","文件名2"+"||"+"下载地址2"],转换后:["文件ID1"+"||"+"文件名1"+"||"+"下载地址1","文件ID2"+"||"+"文件名2"+"||"+"下载地址2"]
|
||||
convertWorkPaperFileInfo(result);
|
||||
// 保存到历史记录
|
||||
saveToHistory(request, interfaceName, requestHash, result, loginUser);
|
||||
}
|
||||
@@ -101,6 +104,78 @@ public abstract class BaseAuditContentController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
private void convertWorkPaperFileInfo(JSONObject result) {
|
||||
JSONArray data = result.getJSONArray("data");
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
JSONObject obj = data.getJSONObject(i);
|
||||
JSONArray workPaperIndexFiles = obj.getJSONArray("workPaperIndex");
|
||||
if (workPaperIndexFiles != null && !workPaperIndexFiles.isEmpty()) {
|
||||
for (int j = 0; j < workPaperIndexFiles.size(); j++) {
|
||||
String files = workPaperIndexFiles.getString(j);
|
||||
workPaperIndexFiles.set(j, "FileId||" + files);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void convertWorkPaperFileInfo2(JSONObject result) {
|
||||
JSONArray data = result.getJSONArray("data");
|
||||
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
JSONObject obj = data.getJSONObject(i);
|
||||
JSONArray workPaperIndexFileIds = obj.getJSONArray("workPaperIndex");
|
||||
|
||||
// 先获取并转换 workPaperIndex 字段
|
||||
if (workPaperIndexFileIds != null && !workPaperIndexFileIds.isEmpty()) {
|
||||
// 先查询所有文件
|
||||
List<AiCloudFile> aiCloudFiles = aiCloudFileService.list(new LambdaQueryWrapper<AiCloudFile>().in(AiCloudFile::getFileId, workPaperIndexFileIds));
|
||||
|
||||
// 创建文件ID到文件的映射,便于查找
|
||||
Map<String, AiCloudFile> fileMap = aiCloudFiles.stream().collect(Collectors.toMap(AiCloudFile::getFileId, file -> file));
|
||||
|
||||
// 创建转换后的数组(保持原顺序)
|
||||
JSONArray transformedArray = new JSONArray();
|
||||
for (Object fileIdObj : workPaperIndexFileIds) {
|
||||
String fileId = (String) fileIdObj;
|
||||
AiCloudFile file = fileMap.get(fileId);
|
||||
if (file != null) {
|
||||
String transformed = file.getFileId() + "||" + file.getFileName() + "||" + file.getFileUrl();
|
||||
transformedArray.add(transformed);
|
||||
} else {
|
||||
transformedArray.add(fileIdObj);
|
||||
}
|
||||
}
|
||||
|
||||
// 将转换后的数据塞回原字段
|
||||
obj.put("workPaperIndex", transformedArray);
|
||||
|
||||
// 使用 workPaperIndexFileIds 中的每个 fileId 替换其他字段中的四种格式
|
||||
for (String key : obj.keySet()) {
|
||||
if ("workPaperIndex".equals(key)) {
|
||||
continue; // 跳过 workPaperIndex 字段
|
||||
}
|
||||
Object value = obj.get(key);
|
||||
if (value instanceof String) {
|
||||
String text = (String) value;
|
||||
for (Object fileIdObj : workPaperIndexFileIds) {
|
||||
String fileId = (String) fileIdObj;
|
||||
text = text.replace("【FileId:" + fileId + "】", "")
|
||||
.replace("(FileId:" + fileId + ")", "")
|
||||
.replace("(FileId:" + fileId + ")", "")
|
||||
.replace("FileId:"+fileId, "")
|
||||
.replace("【" + fileId + "】", "")
|
||||
.replace("(" + fileId + ")", "")
|
||||
.replace("(" + fileId + ")", "")
|
||||
.replace(fileId, "");;
|
||||
}
|
||||
obj.put(key, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成请求哈希
|
||||
*/
|
||||
|
||||
@@ -93,8 +93,9 @@ public class KnowledgeBaseController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
boolean result = knowledgeBaseService.uploadDocuments(kbId, files);
|
||||
|
||||
List<String> fileIds = knowledgeBaseService.uploadDocuments(kbId, files);
|
||||
boolean result = knowledgeBaseService.submitDocuments(kbId, fileIds);
|
||||
|
||||
if (result) {
|
||||
return success("成功上传 " + files.length + " 个文件");
|
||||
} else {
|
||||
|
||||
@@ -35,6 +35,11 @@ public class AuditContentRequest {
|
||||
*/
|
||||
private String history;
|
||||
|
||||
/**
|
||||
* 知识库关键词
|
||||
*/
|
||||
private String keywords;
|
||||
|
||||
/**
|
||||
* 优化建议
|
||||
*/
|
||||
|
||||
35
src/main/java/com/gxwebsoft/ai/dto/AuditEvidenceRequest.java
Normal file
35
src/main/java/com/gxwebsoft/ai/dto/AuditEvidenceRequest.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package com.gxwebsoft.ai.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AuditEvidenceRequest {
|
||||
// 基础信息
|
||||
private String caseIndex; // 案引号
|
||||
private String projectName; // 项目名称
|
||||
private String auditedTarget; // 被审计单位或个人
|
||||
private String auditMatter; // 审计事项
|
||||
private String summaryTitle; // 标题
|
||||
private String auditRecord; // 审计记录
|
||||
private String auditFinding; // 审计发现
|
||||
private String evidenceBasis; // 定性依据
|
||||
private String handling; // 处理
|
||||
private String suggestion; // 建议
|
||||
private String attachment; // 附件
|
||||
private String auditors; // 审计人员
|
||||
private String compileDate; // 编制日期
|
||||
private String providerOpinion; // 证据提供单位意见
|
||||
private String providerDate; // 证据提供日期
|
||||
private String attachmentPages; // 附件页数
|
||||
private String feedbackDeadline; // 反馈期限
|
||||
|
||||
// 导出取证单使用
|
||||
private String pageIndex;
|
||||
private String pageTotal;
|
||||
|
||||
// 历史内容(用于工作流生成)
|
||||
private String history;
|
||||
|
||||
// 用户信息
|
||||
private String userName;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.gxwebsoft.ai.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gxwebsoft.ai.dto.AuditEvidenceRequest;
|
||||
|
||||
public interface AuditEvidenceService {
|
||||
/**
|
||||
* 生成审计取证单
|
||||
*/
|
||||
JSONObject generateAuditEvidence(AuditEvidenceRequest request);
|
||||
}
|
||||
@@ -58,15 +58,15 @@ public interface KnowledgeBaseService {
|
||||
/**
|
||||
* 上传知识库文档
|
||||
*/
|
||||
boolean uploadDocuments(String kbId, MultipartFile[] files);
|
||||
List<String> uploadDocuments(String kbId, MultipartFile[] files);
|
||||
|
||||
/**
|
||||
* 知识库追加导入已解析的文档
|
||||
*/
|
||||
void submitDocuments(String kbId, String fileId);
|
||||
boolean submitDocuments(String kbId, String fileId);
|
||||
|
||||
/**
|
||||
* 知识库追加导入已解析的文档
|
||||
*/
|
||||
void submitDocuments(String kbId, List<String> fileIds);
|
||||
boolean submitDocuments(String kbId, List<String> fileIds);
|
||||
}
|
||||
@@ -5,13 +5,17 @@ import com.aliyun.bailian20231229.models.RetrieveResponse;
|
||||
import com.aliyun.bailian20231229.models.RetrieveResponseBody;
|
||||
import com.aliyun.bailian20231229.models.RetrieveResponseBody.RetrieveResponseBodyData;
|
||||
import com.aliyun.bailian20231229.models.RetrieveResponseBody.RetrieveResponseBodyDataNodes;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gxwebsoft.ai.config.KnowledgeBaseConfig;
|
||||
import com.gxwebsoft.ai.entity.AiCloudFile;
|
||||
import com.gxwebsoft.ai.factory.KnowledgeBaseClientFactory;
|
||||
import com.gxwebsoft.ai.util.KnowledgeBaseUtil;
|
||||
import com.gxwebsoft.ai.service.AiCloudFileService;
|
||||
import com.gxwebsoft.ai.util.AiCloudKnowledgeBaseUtil;
|
||||
import com.gxwebsoft.common.core.context.TenantContext;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
@@ -21,6 +25,9 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public abstract class AbstractAuditContentService {
|
||||
@@ -31,6 +38,9 @@ public abstract class AbstractAuditContentService {
|
||||
@Autowired
|
||||
protected KnowledgeBaseConfig config;
|
||||
|
||||
@Autowired
|
||||
protected AiCloudFileService aiCloudFileService;
|
||||
|
||||
protected static final String DIFY_WORKFLOW_URL = "http://1.14.159.185:8180/v1/workflows/run";
|
||||
|
||||
// 用于同步的锁对象池
|
||||
@@ -42,7 +52,7 @@ public abstract class AbstractAuditContentService {
|
||||
protected JSONArray callWorkflow(String url, String token, JSONObject requestBody, String workflowName) {
|
||||
try {
|
||||
log.info("调用{}工作流,请求体长度: {}", workflowName, requestBody.toString().length());
|
||||
|
||||
|
||||
String result = HttpUtil.createPost(url)
|
||||
.header("Authorization", token)
|
||||
.header("Content-Type", "application/json")
|
||||
@@ -50,34 +60,95 @@ public abstract class AbstractAuditContentService {
|
||||
.timeout(10 * 60 * 1000)
|
||||
.execute()
|
||||
.body();
|
||||
|
||||
|
||||
log.info("{}工作流返回结果长度: {}", workflowName, result.length());
|
||||
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
JsonNode rootNode = objectMapper.readTree(result);
|
||||
|
||||
|
||||
String outputText = rootNode.path("data")
|
||||
.path("outputs")
|
||||
.path("result")
|
||||
.asText();
|
||||
|
||||
.path("outputs")
|
||||
.path("result")
|
||||
.asText();
|
||||
|
||||
if (StrUtil.isBlank(outputText)) {
|
||||
log.warn("{}工作流返回结果为空", workflowName);
|
||||
log.warn("{}工作流返回 result 为空", workflowName);
|
||||
return new JSONArray();
|
||||
}
|
||||
|
||||
JsonNode arrayNode = objectMapper.readTree(outputText);
|
||||
JSONArray jsonArray = JSONArray.parseArray(arrayNode.toString());
|
||||
|
||||
|
||||
// ★ NEW:先从 ```json``` 中提取
|
||||
String jsonText = extractJsonFromMarkdown(outputText);
|
||||
|
||||
// ★ NEW:兜底提取
|
||||
if (StrUtil.isBlank(jsonText)) {
|
||||
jsonText = extractFirstJson(outputText);
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(jsonText)) {
|
||||
log.error("{} 工作流返回内容无法解析为 JSON,原始内容:{}", workflowName, outputText);
|
||||
throw new RuntimeException("Dify 返回内容中未找到有效 JSON");
|
||||
}
|
||||
|
||||
JSONArray jsonArray = JSONArray.parseArray(jsonText);
|
||||
|
||||
log.info("成功解析{}工作流返回数据,记录数: {}", workflowName, jsonArray.size());
|
||||
return jsonArray;
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("调用{}工作流失败", workflowName, e);
|
||||
throw new RuntimeException("调用" + workflowName + "工作流失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 ```json 代码块中提取 JSON
|
||||
*/
|
||||
private static String extractJsonFromMarkdown(String text) {
|
||||
if (StrUtil.isBlank(text)) {
|
||||
return null;
|
||||
}
|
||||
Pattern pattern = Pattern.compile("```json\\s*(.*?)\\s*```", Pattern.DOTALL);
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1).trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 兜底:从混合文本中提取第一个完整 JSON({} 或 [])
|
||||
*/
|
||||
private static String extractFirstJson(String text) {
|
||||
if (StrUtil.isBlank(text)) return null;
|
||||
|
||||
int start = -1;
|
||||
char open = 0, close = 0;
|
||||
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if (c == '{' || c == '[') {
|
||||
start = i;
|
||||
open = c;
|
||||
close = (c == '{') ? '}' : ']';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (start == -1) return null;
|
||||
|
||||
int count = 0;
|
||||
for (int i = start; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if (c == open) count++;
|
||||
if (c == close) count--;
|
||||
if (count == 0) {
|
||||
return text.substring(start, i + 1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 构建工作流请求通用方法
|
||||
*/
|
||||
@@ -100,63 +171,152 @@ public abstract class AbstractAuditContentService {
|
||||
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询知识库通用方法
|
||||
* 同一个kbId,全局线程情况下,只支持一个,不支持异步多并发,并按队列顺序执行
|
||||
*/
|
||||
protected List<String> queryKnowledgeBase(String kbId, List<String> queries, int topK) {
|
||||
Set<String> results = new LinkedHashSet<>();
|
||||
String workspaceId = config.getWorkspaceId();
|
||||
|
||||
// 获取或创建该kbId对应的锁对象
|
||||
Object lock = kbLocks.computeIfAbsent(kbId, k -> new Object());
|
||||
|
||||
// 使用synchronized确保同一个kbId的查询串行执行
|
||||
synchronized (lock) {
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
for (String query : queries) {
|
||||
try {
|
||||
RetrieveResponse resp = KnowledgeBaseUtil.retrieveIndex(client, workspaceId, kbId, query);
|
||||
|
||||
Optional.ofNullable(resp)
|
||||
.map(RetrieveResponse::getBody)
|
||||
.map(RetrieveResponseBody::getData)
|
||||
.map(RetrieveResponseBodyData::getNodes)
|
||||
.orElse(Collections.emptyList())
|
||||
.stream()
|
||||
.limit(topK)
|
||||
.forEach(node -> processKnowledgeNode(node, results));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("查询知识库失败 - kbId: {}, query: {}", kbId, query, e);
|
||||
}
|
||||
// 1. 收集所有节点和文档ID
|
||||
List<RetrieveResponseBodyDataNodes> allNodes = collectKnowledgeNodes(kbId, queries, topK);
|
||||
if (allNodes.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 2. 批量查询文件URL
|
||||
Map<String, String> fileUrlMap = TenantContext.callIgnoreTenant(() -> batchQueryFileUrls(allNodes));
|
||||
|
||||
// 3. 处理节点生成结果
|
||||
return processNodesToResults(allNodes, fileUrlMap);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("创建知识库客户端失败", e);
|
||||
log.error("查询知识库失败 - kbId: {}", kbId, e);
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(results);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理知识库节点通用方法
|
||||
* 收集知识库节点
|
||||
*/
|
||||
protected void processKnowledgeNode(RetrieveResponseBodyDataNodes node, Set<String> results) {
|
||||
private List<RetrieveResponseBodyDataNodes> collectKnowledgeNodes(String kbId, List<String> queries, int topK) {
|
||||
List<RetrieveResponseBodyDataNodes> allNodes = new ArrayList<>();
|
||||
String workspaceId = config.getWorkspaceId();
|
||||
try {
|
||||
String text = node.getText();
|
||||
if (StrUtil.isBlank(text) || text.length() < 10) {
|
||||
return;
|
||||
Client client = clientFactory.createClient();
|
||||
for (String query : queries) {
|
||||
try {
|
||||
RetrieveResponse resp = AiCloudKnowledgeBaseUtil.retrieveIndex(client, workspaceId, kbId, query);
|
||||
List<RetrieveResponseBodyDataNodes> nodes = Optional.ofNullable(resp)
|
||||
.map(RetrieveResponse::getBody)
|
||||
.map(RetrieveResponseBody::getData)
|
||||
.map(RetrieveResponseBodyData::getNodes)
|
||||
.orElse(Collections.emptyList())
|
||||
.stream()
|
||||
.limit(topK)
|
||||
.collect(Collectors.toList());
|
||||
allNodes.addAll(nodes);
|
||||
} catch (Exception e) {
|
||||
log.warn("查询知识库失败 - kbId: {}, query: {}", kbId, query, e);
|
||||
}
|
||||
}
|
||||
|
||||
String docName = extractDocumentName(node);
|
||||
String formattedText = String.format("《%s》%s", docName, text);
|
||||
results.add(formattedText);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("处理知识库节点失败", e);
|
||||
log.error("创建知识库客户端失败", e);
|
||||
}
|
||||
return allNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量查询文件URL
|
||||
*/
|
||||
protected Map<String, String> batchQueryFileUrls(List<RetrieveResponseBodyDataNodes> nodes) {
|
||||
// 收集所有文档ID
|
||||
Set<String> docIds = nodes.stream().map(this::extractDocumentId).filter(StrUtil::isNotBlank).collect(Collectors.toSet());
|
||||
if (docIds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
try {
|
||||
// 批量查询
|
||||
List<AiCloudFile> files = aiCloudFileService.list(new LambdaQueryWrapper<AiCloudFile>().in(AiCloudFile::getFileId, docIds));
|
||||
// 构建映射表
|
||||
return files.stream()
|
||||
.filter(file -> file.getFileUrl() != null)
|
||||
.collect(Collectors.toMap(
|
||||
AiCloudFile::getFileId,
|
||||
AiCloudFile::getFileUrl
|
||||
));
|
||||
} catch (Exception e) {
|
||||
log.error("批量查询文件信息失败", e);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理节点生成结果
|
||||
*/
|
||||
private List<String> processNodesToResults(List<RetrieveResponseBodyDataNodes> nodes, Map<String, String> fileUrlMap) {
|
||||
Set<String> results = new LinkedHashSet<>();
|
||||
|
||||
// 构建可用文件列表
|
||||
StringBuilder fileListBuilder = new StringBuilder();
|
||||
fileListBuilder.append("\n\n**可用真实文件列表(仅限以下文件,不得虚构其他文件名):**\n");
|
||||
|
||||
Set<String> processedFiles = new HashSet<>(); // 避免重复
|
||||
|
||||
for (RetrieveResponseBodyDataNodes node : nodes) {
|
||||
try {
|
||||
// 检查文本有效性
|
||||
String text = node.getText();
|
||||
if (StrUtil.isBlank(text) || text.length() < 10) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取文档信息
|
||||
String docName = extractDocumentName(node);
|
||||
String docId = extractDocumentId(node);
|
||||
String fileUrl = fileUrlMap.get(docId);
|
||||
if (StrUtil.isBlank(fileUrl)) {
|
||||
fileUrl = extractDocumentUrl(node);
|
||||
}
|
||||
if (StrUtil.isBlank(fileUrl)) {
|
||||
fileUrl = "无链接";
|
||||
}
|
||||
|
||||
// 添加文件名到列表(去重)
|
||||
if (StrUtil.isNotBlank(docName) && !processedFiles.contains(docName)) {
|
||||
processedFiles.add(docName);
|
||||
fileListBuilder.append("- 《").append(docName).append("》");
|
||||
if (!"无链接".equals(fileUrl)) {
|
||||
fileListBuilder.append(" || ").append(fileUrl);
|
||||
}
|
||||
fileListBuilder.append("\n");
|
||||
}
|
||||
|
||||
// 格式化为知识库内容
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("document_name", docName);
|
||||
json.put("file_url", fileUrl);
|
||||
json.put("content", text);
|
||||
String formattedText = json.toJSONString();
|
||||
results.add(formattedText);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warn("处理知识库节点失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 添加文件列表到结果
|
||||
fileListBuilder.append("\n**重要约束:**\n");
|
||||
fileListBuilder.append("1. 只能使用上述列表中的文件,不得虚构其他文件名\n");
|
||||
fileListBuilder.append("2. 如果某项检查未涉及上述文件,则 workPaperIndex 填写\"[]\"\n");
|
||||
fileListBuilder.append("3. 所有文件必须来自提供的知识库内容\n");
|
||||
|
||||
results.add(fileListBuilder.toString());
|
||||
|
||||
return new ArrayList<>(results);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,6 +338,44 @@ public abstract class AbstractAuditContentService {
|
||||
return "相关文档";
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取文档Id通用方法
|
||||
*/
|
||||
protected String extractDocumentId(RetrieveResponseBodyDataNodes node) {
|
||||
try {
|
||||
Object metadataObj = node.getMetadata();
|
||||
if (metadataObj instanceof Map) {
|
||||
Map<?, ?> metadata = (Map<?, ?>) metadataObj;
|
||||
Object docIdObj = metadata.get("doc_id");
|
||||
if (docIdObj != null) {
|
||||
return docIdObj.toString();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("提取文档名称失败", e);
|
||||
}
|
||||
return "相关文档";
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取文档url通用方法
|
||||
*/
|
||||
protected String extractDocumentUrl(RetrieveResponseBodyDataNodes node) {
|
||||
try {
|
||||
Object metadataObj = node.getMetadata();
|
||||
if (metadataObj instanceof Map) {
|
||||
Map<?, ?> metadata = (Map<?, ?>) metadataObj;
|
||||
Object docIdObj = metadata.get("doc_url");
|
||||
if (docIdObj != null) {
|
||||
return docIdObj.toString();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("提取文档名称失败", e);
|
||||
}
|
||||
return "相关文档";
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建成功响应通用方法
|
||||
*/
|
||||
|
||||
@@ -6,8 +6,11 @@ import com.gxwebsoft.ai.config.KnowledgeBaseConfig;
|
||||
import com.gxwebsoft.ai.factory.KnowledgeBaseClientFactory;
|
||||
import com.gxwebsoft.ai.util.AiCloudDataCenterUtil;
|
||||
import com.gxwebsoft.ai.util.AiCloudKnowledgeBaseUtil;
|
||||
import com.gxwebsoft.common.system.controller.FileController;
|
||||
import com.gxwebsoft.common.system.entity.FileRecord;
|
||||
import com.gxwebsoft.common.system.entity.User;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
@@ -51,11 +54,15 @@ public class AiCloudFileServiceImpl extends ServiceImpl<AiCloudFileMapper, AiClo
|
||||
private AiCloudDocService aiCloudDocService;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private OaCompanyService oaCompanyService;
|
||||
|
||||
@Resource
|
||||
private KnowledgeBaseService knowledgeBaseService;
|
||||
|
||||
@Resource
|
||||
private FileController fileController;
|
||||
|
||||
@Override
|
||||
public PageResult<AiCloudFile> pageRel(AiCloudFileParam param) {
|
||||
PageParam<AiCloudFile, AiCloudFileParam> page = new PageParam<>(param);
|
||||
@@ -92,6 +99,8 @@ public class AiCloudFileServiceImpl extends ServiceImpl<AiCloudFileMapper, AiClo
|
||||
AddFileResponse addFileResponse = AiCloudDataCenterUtil.uploadFile(client, workspaceId, categoryId, file);
|
||||
String fileId = addFileResponse.getBody().getData().getFileId();
|
||||
|
||||
FileRecord fileRecord = fileController.upload(file, loginUser.getTenantId());
|
||||
|
||||
// 保存文件信息到数据库
|
||||
AiCloudFile aiCloudFile = new AiCloudFile();
|
||||
aiCloudFile.setDocId(docId);
|
||||
@@ -100,6 +109,7 @@ public class AiCloudFileServiceImpl extends ServiceImpl<AiCloudFileMapper, AiClo
|
||||
aiCloudFile.setFileType(getFileExtension(file.getOriginalFilename()));
|
||||
aiCloudFile.setFileExt(getFileExtension(file.getOriginalFilename()));
|
||||
aiCloudFile.setFileId(fileId);
|
||||
aiCloudFile.setFileUrl(fileRecord.getDownloadUrl());
|
||||
aiCloudFile.setUploadTime(LocalDateTime.now());
|
||||
aiCloudFile.setWorkspaceId(workspaceId);
|
||||
|
||||
|
||||
@@ -248,7 +248,8 @@ public class AuditContent1EightRegServiceImpl extends AbstractAuditContentServic
|
||||
context.append("- content:审计内容(具体中共中央八项规定/具体中央八项规定实施细则)\n");
|
||||
context.append("- testContent:审计检查的证据,需详细描述查阅过程、查阅的具体文件和内容\n");
|
||||
context.append("- result:审计测试的结果(通过/不通过),严格判断,从严掌握通过标准\n");
|
||||
context.append("- workPaperIndex:相关《参考文件名》,必须是实际存在的完整文件名,不能使用附表标题,确保能在文件夹中搜索到\n");
|
||||
// context.append("- workPaperIndex:相关《参考文件FileId》,必须是实际存在的完整文件FileId,不能使用附表标题,确保能在文件夹中搜索到\n");
|
||||
context.append("- workPaperIndex:相关[\"实际存在的完整文件名||FileUrl\"],必须是实际存在的完整文件名,不能使用附表标题,确保能在文件夹中搜索到\n");
|
||||
context.append("\n注意:\n");
|
||||
context.append("1. 请根据知识库内容尽可能全面地生成所有相关制度规定和检查点\n");
|
||||
context.append("2. 工作底稿索引必须准确对应实际文件名称,避免使用附表或章节标题\n");
|
||||
@@ -260,6 +261,14 @@ public class AuditContent1EightRegServiceImpl extends AbstractAuditContentServic
|
||||
context.append("### 政策内容\n").append(AuditContent1EightRegConstants.POLICY_CONTENTS.get(category)).append("\n\n");
|
||||
context.append("### 实施细则\n").append(AuditContent1EightRegConstants.IMPLEMENTATION_DETAILS.get(category)).append("\n\n");
|
||||
|
||||
// 4. 审计建议 - 新增部分
|
||||
String auditSuggestion = AuditContent1EightRegConstants.AUDIT_SUGGESTIONS.get(category);
|
||||
if (StrUtil.isNotBlank(auditSuggestion)) {
|
||||
context.append("## 审计建议\n");
|
||||
context.append("如发现被审计单位在").append(category).append("方面存在不符合中央八项规定的情况,可参考以下审计建议:\n\n");
|
||||
context.append(auditSuggestion).append("\n\n");
|
||||
}
|
||||
|
||||
// 4. 历史内容
|
||||
if (StrUtil.isNotBlank(history)) {
|
||||
context.append("## 历史生成内容\n");
|
||||
|
||||
@@ -28,7 +28,7 @@ public class AuditContent1ExpenseServiceImpl extends AbstractAuditContentService
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
// 1. 检索相关知识
|
||||
// 1. 检索相关知识(优化查询策略,避免工程类报表)
|
||||
Map<String, List<String>> knowledgeSources = retrieveKnowledgeForExpense(kbIds, libraryKbIds, projectLibrary);
|
||||
|
||||
// 2. 构建完整的知识上下文
|
||||
@@ -38,14 +38,24 @@ public class AuditContent1ExpenseServiceImpl extends AbstractAuditContentService
|
||||
JSONObject requestBody = buildWorkflowRequest(knowledgeContext, userName);
|
||||
JSONArray expenseData = callWorkflow(DIFY_WORKFLOW_URL, DIFY_WORKFLOW_TOKEN, requestBody, "支出情况表");
|
||||
|
||||
// 4. 数据验证和补充
|
||||
expenseData = validateAndEnhanceExpenseData(expenseData);
|
||||
|
||||
log.info("支出情况表生成成功 - 记录数: {}, 处理时间: {}ms",
|
||||
expenseData.size(), (System.currentTimeMillis() - startTime));
|
||||
// 4. 按 expenseType 和 year 排序
|
||||
if (expenseData != null && !expenseData.isEmpty()) {
|
||||
expenseData.sort((o1, o2) -> {
|
||||
JSONObject obj1 = (JSONObject) o1;
|
||||
JSONObject obj2 = (JSONObject) o2;
|
||||
// 先按 expenseType 排序
|
||||
int typeCompare = obj1.getString("expenseType").compareTo(obj2.getString("expenseType"));
|
||||
if (typeCompare != 0) {
|
||||
return typeCompare;
|
||||
}
|
||||
// 再按 year 排序
|
||||
return obj1.getString("year").compareTo(obj2.getString("year"));
|
||||
});
|
||||
}
|
||||
|
||||
// 5. 不进行数据验证和补充,直接返回大模型输出
|
||||
log.info("支出情况表生成成功 - 记录数: {}, 处理时间: {}ms", expenseData.size(), (System.currentTimeMillis() - startTime));
|
||||
return buildSuccessResponse(expenseData, startTime, "expense_audit");
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("生成支出情况表失败", e);
|
||||
return buildErrorResponse("生成支出情况表失败: " + e.getMessage());
|
||||
@@ -53,190 +63,7 @@ public class AuditContent1ExpenseServiceImpl extends AbstractAuditContentService
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证和增强支出情况表数据
|
||||
*/
|
||||
private JSONArray validateAndEnhanceExpenseData(JSONArray originalData) {
|
||||
if (originalData == null || originalData.isEmpty()) {
|
||||
log.warn("原始数据为空,返回空数组");
|
||||
return new JSONArray();
|
||||
}
|
||||
|
||||
JSONArray enhancedData = new JSONArray();
|
||||
|
||||
for (int i = 0; i < originalData.size(); i++) {
|
||||
JSONObject item = originalData.getJSONObject(i);
|
||||
if (item != null) {
|
||||
// 确保所有必需字段都存在
|
||||
enhanceExpenseData(item);
|
||||
enhancedData.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
// 确保数据完整性(至少16条记录)
|
||||
if (enhancedData.size() < 16) {
|
||||
enhancedData = supplementMissingExpenseData(enhancedData);
|
||||
}
|
||||
|
||||
log.info("数据增强完成 - 原始记录数: {}, 增强后记录数: {}", originalData.size(), enhancedData.size());
|
||||
return enhancedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增强单个支出数据
|
||||
*/
|
||||
private void enhanceExpenseData(JSONObject expenseData) {
|
||||
// 确保支出类型正确
|
||||
String expenseType = expenseData.getString("expenseType");
|
||||
if (StrUtil.isBlank(expenseType) || !isValidExpenseType(expenseType)) {
|
||||
expenseData.put("expenseType", AuditContent1ExpenseConstants.EXPENSE_TYPE_RECEPTION);
|
||||
}
|
||||
|
||||
// 确保年份在2020-2023范围内
|
||||
String year = expenseData.getString("year");
|
||||
if (StrUtil.isBlank(year) || !isValidYear(year)) {
|
||||
expenseData.put("year", "2023");
|
||||
}
|
||||
|
||||
// 确保金额格式正确
|
||||
if (StrUtil.isBlank(expenseData.getString("finalStatementAmount"))) {
|
||||
expenseData.put("finalStatementAmount", "0.00");
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(expenseData.getString("initialBudgetAmount"))) {
|
||||
expenseData.put("initialBudgetAmount", "0.00");
|
||||
}
|
||||
|
||||
// 计算缺失的百分比字段
|
||||
calculateMissingPercentages(expenseData);
|
||||
|
||||
// 确保数据来源
|
||||
if (StrUtil.isBlank(expenseData.getString("dataSource"))) {
|
||||
String yearStr = expenseData.getString("year");
|
||||
expenseData.put("dataSource", yearStr + "年度部门决算报表");
|
||||
}
|
||||
|
||||
// 确保工作底稿索引是数组格式
|
||||
if (!expenseData.containsKey("workPaperIndex") || expenseData.get("workPaperIndex") == null) {
|
||||
JSONArray workPaperIndex = new JSONArray();
|
||||
workPaperIndex.add("《" + expenseData.getString("year") + "年度部门决算报表》");
|
||||
workPaperIndex.add("《" + expenseData.getString("year") + "年预算执行情况分析》");
|
||||
expenseData.put("workPaperIndex", workPaperIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 补充缺失的支出数据
|
||||
*/
|
||||
private JSONArray supplementMissingExpenseData(JSONArray existingData) {
|
||||
JSONArray supplementedData = new JSONArray();
|
||||
supplementedData.addAll(existingData);
|
||||
|
||||
// 获取已存在的数据组合
|
||||
Set<String> existingCombinations = new HashSet<>();
|
||||
for (int i = 0; i < existingData.size(); i++) {
|
||||
JSONObject item = existingData.getJSONObject(i);
|
||||
String key = item.getString("expenseType") + "_" + item.getString("year");
|
||||
existingCombinations.add(key);
|
||||
}
|
||||
|
||||
// 补充缺失的数据
|
||||
String[] expenseTypes = {
|
||||
AuditContent1ExpenseConstants.EXPENSE_TYPE_RECEPTION,
|
||||
AuditContent1ExpenseConstants.EXPENSE_TYPE_OVERSEAS,
|
||||
AuditContent1ExpenseConstants.EXPENSE_TYPE_VEHICLE,
|
||||
AuditContent1ExpenseConstants.EXPENSE_TYPE_MEETING
|
||||
};
|
||||
|
||||
String[] years = {"2020", "2021", "2022", "2023"};
|
||||
|
||||
for (String expenseType : expenseTypes) {
|
||||
for (String year : years) {
|
||||
String key = expenseType + "_" + year;
|
||||
if (!existingCombinations.contains(key)) {
|
||||
JSONObject newItem = createDefaultExpenseRecord(expenseType, year);
|
||||
supplementedData.add(newItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return supplementedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建默认的支出记录
|
||||
*/
|
||||
private JSONObject createDefaultExpenseRecord(String expenseType, String year) {
|
||||
JSONObject record = new JSONObject();
|
||||
record.put("expenseType", expenseType);
|
||||
record.put("year", year);
|
||||
record.put("finalStatementAmount", "0.00");
|
||||
record.put("initialBudgetAmount", "0.00");
|
||||
record.put("changePercentage", "0.0%");
|
||||
record.put("budgetRatio", "0.0%");
|
||||
record.put("remark", "根据年度财务报告数据生成");
|
||||
record.put("dataSource", year + "年度部门决算报表");
|
||||
|
||||
JSONArray workPaperIndex = new JSONArray();
|
||||
workPaperIndex.add("《" + year + "年度部门决算报表》");
|
||||
workPaperIndex.add("《" + year + "年预算执行情况分析》");
|
||||
record.put("workPaperIndex", workPaperIndex);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算缺失的百分比字段
|
||||
*/
|
||||
private void calculateMissingPercentages(JSONObject expenseData) {
|
||||
try {
|
||||
String finalAmountStr = expenseData.getString("finalStatementAmount").replace(",", "");
|
||||
String budgetAmountStr = expenseData.getString("initialBudgetAmount").replace(",", "");
|
||||
|
||||
double finalAmount = Double.parseDouble(finalAmountStr);
|
||||
double budgetAmount = Double.parseDouble(budgetAmountStr);
|
||||
|
||||
// 计算增减百分比
|
||||
if (budgetAmount > 0) {
|
||||
double changePercentage = ((finalAmount - budgetAmount) / budgetAmount) * 100;
|
||||
if (!expenseData.containsKey("changePercentage") || StrUtil.isBlank(expenseData.getString("changePercentage"))) {
|
||||
expenseData.put("changePercentage", String.format("%.1f%%", changePercentage));
|
||||
}
|
||||
|
||||
// 计算预算占比
|
||||
double budgetRatio = (finalAmount / budgetAmount) * 100;
|
||||
if (!expenseData.containsKey("budgetRatio") || StrUtil.isBlank(expenseData.getString("budgetRatio"))) {
|
||||
expenseData.put("budgetRatio", String.format("%.1f%%", budgetRatio));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("计算百分比失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查支出类型是否有效
|
||||
*/
|
||||
private boolean isValidExpenseType(String expenseType) {
|
||||
return expenseType.equals(AuditContent1ExpenseConstants.EXPENSE_TYPE_RECEPTION) ||
|
||||
expenseType.equals(AuditContent1ExpenseConstants.EXPENSE_TYPE_OVERSEAS) ||
|
||||
expenseType.equals(AuditContent1ExpenseConstants.EXPENSE_TYPE_VEHICLE) ||
|
||||
expenseType.equals(AuditContent1ExpenseConstants.EXPENSE_TYPE_MEETING);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查年份是否有效
|
||||
*/
|
||||
private boolean isValidYear(String year) {
|
||||
for (int validYear : AuditContent1ExpenseConstants.YEAR_RANGE) {
|
||||
if (year.equals(String.valueOf(validYear))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检索支出情况表相关知识 - 优化查询策略
|
||||
* 检索支出情况表相关知识 - 优化查询策略,排除工程类报表
|
||||
*/
|
||||
private Map<String, List<String>> retrieveKnowledgeForExpense(String kbIds, String libraryKbIds, String projectLibrary) {
|
||||
Map<String, List<String>> knowledgeSources = new HashMap<>();
|
||||
@@ -244,8 +71,8 @@ public class AuditContent1ExpenseServiceImpl extends AbstractAuditContentService
|
||||
knowledgeSources.put("regulation", new ArrayList<>());
|
||||
knowledgeSources.put("auditCase", new ArrayList<>());
|
||||
|
||||
// 构建更全面的查询词
|
||||
List<String> queries = buildEnhancedExpenseQueries();
|
||||
// 构建更精确的查询词,专门针对四类支出
|
||||
List<String> queries = buildPreciseExpenseQueries();
|
||||
|
||||
// 财务数据库检索
|
||||
if (StrUtil.isNotBlank(kbIds)) {
|
||||
@@ -254,6 +81,10 @@ public class AuditContent1ExpenseServiceImpl extends AbstractAuditContentService
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.forEach(kbId -> {
|
||||
List<String> financialKnowledge = queryKnowledgeBase(kbId, queries, 200);
|
||||
|
||||
// 过滤掉工程类报表内容
|
||||
financialKnowledge = filterOutEngineeringReports(financialKnowledge);
|
||||
|
||||
knowledgeSources.get("financial").addAll(financialKnowledge);
|
||||
log.debug("财务知识库 {} 检索到 {} 条相关知识", kbId, financialKnowledge.size());
|
||||
});
|
||||
@@ -265,21 +96,22 @@ public class AuditContent1ExpenseServiceImpl extends AbstractAuditContentService
|
||||
.map(String::trim)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.forEach(libId -> {
|
||||
List<String> regulationKnowledge = queryKnowledgeBase(libId, queries, 100);
|
||||
List<String> regulationKnowledge = queryKnowledgeBase(libId, queries, 50);
|
||||
knowledgeSources.get("regulation").addAll(regulationKnowledge);
|
||||
});
|
||||
}
|
||||
|
||||
// 审计案例库检索
|
||||
if (StrUtil.isNotBlank(projectLibrary)) {
|
||||
List<String> auditCaseKnowledge = queryKnowledgeBase(projectLibrary, queries, 50);
|
||||
List<String> auditCaseKnowledge = queryKnowledgeBase(projectLibrary, queries, 30);
|
||||
knowledgeSources.get("auditCase").addAll(auditCaseKnowledge);
|
||||
}
|
||||
|
||||
// 智能去重和排序
|
||||
// 智能去重、排序和过滤
|
||||
knowledgeSources.forEach((key, list) -> {
|
||||
List<String> processed = list.stream()
|
||||
.distinct()
|
||||
.filter(this::isRelevantExpenseContent) // 过滤相关性
|
||||
.sorted(this::expenseComparator)
|
||||
.limit(getLimitBySourceType(key))
|
||||
.collect(Collectors.toList());
|
||||
@@ -295,69 +127,115 @@ public class AuditContent1ExpenseServiceImpl extends AbstractAuditContentService
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建增强的支出情况表查询词
|
||||
* 构建精确的支出情况表查询词
|
||||
*/
|
||||
private List<String> buildEnhancedExpenseQueries() {
|
||||
private List<String> buildPreciseExpenseQueries() {
|
||||
return Arrays.asList(
|
||||
"决算报表 预算报表 财务报表 年度报告",
|
||||
"公务接待费 接待费用 招待费用 餐饮费用",
|
||||
"出国费用 出境费用 国际差旅 境外考察",
|
||||
"公车运行维护 车辆费用 汽车费用 交通费用",
|
||||
"会议费 培训费 会务费 培训费用",
|
||||
"八项规定 三公经费 公务支出",
|
||||
"预算执行 预算调整 超预算 预算控制",
|
||||
"2020年 2021年 2022年 2023年",
|
||||
"支出明细 费用明细 开支情况",
|
||||
"财务分析 费用分析 支出分析"
|
||||
// 直接针对四类支出的查询
|
||||
"公务接待 接待费 招待费 餐饮费 公务用餐",
|
||||
"出国 出境 国际差旅 境外考察 出国费用",
|
||||
"公车运行维护 车辆费 汽车费 交通费 公务用车",
|
||||
"会议培训费 会议费 培训费 会务费 培训支出",
|
||||
|
||||
// 财务文档查询(排除工程类)
|
||||
"决算报表 公务接待 出国 公车 会议培训",
|
||||
"预算报表 三公经费 支出明细",
|
||||
"部门决算 公务支出 费用统计",
|
||||
"部门预算 经费预算 支出预算",
|
||||
|
||||
// 通用年度查询,不指定具体年份,让知识库返回所有年份数据
|
||||
"年度 公务接待 出国 公车 会议培训",
|
||||
"决算 公务接待 出国 公车 会议培训",
|
||||
"预算 公务接待 出国 公车 会议培训",
|
||||
"三公经费 支出 决算 预算"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建完整的知识上下文 - 优化提示
|
||||
* 过滤掉工程类报表内容
|
||||
*/
|
||||
private List<String> filterOutEngineeringReports(List<String> knowledgeList) {
|
||||
return knowledgeList.stream()
|
||||
.filter(content -> {
|
||||
// 检查是否包含工程类关键词
|
||||
for (String excludeKeyword : AuditContent1ExpenseConstants.ENGINEERING_EXCLUDE_KEYWORDS) {
|
||||
if (content.contains(excludeKeyword)) {
|
||||
log.debug("过滤工程类内容: {}", content.substring(0, Math.min(50, content.length())));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查内容是否与支出相关
|
||||
*/
|
||||
private boolean isRelevantExpenseContent(String content) {
|
||||
String lowerContent = content.toLowerCase();
|
||||
|
||||
// 必须包含至少一个支出类型关键词
|
||||
boolean hasExpenseType = lowerContent.contains("公务接待") ||
|
||||
lowerContent.contains("接待费") ||
|
||||
lowerContent.contains("出国") ||
|
||||
lowerContent.contains("出境") ||
|
||||
lowerContent.contains("公车") ||
|
||||
lowerContent.contains("车辆费") ||
|
||||
lowerContent.contains("会议") ||
|
||||
lowerContent.contains("培训") ||
|
||||
lowerContent.contains("三公经费");
|
||||
|
||||
// 排除工程类内容
|
||||
boolean isEngineering = false;
|
||||
for (String excludeKeyword : AuditContent1ExpenseConstants.ENGINEERING_EXCLUDE_KEYWORDS) {
|
||||
if (content.contains(excludeKeyword)) {
|
||||
isEngineering = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hasExpenseType && !isEngineering;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建完整的知识上下文 - 简化版本,只传递知识内容
|
||||
*/
|
||||
private String buildCompleteKnowledgeContext(Map<String, List<String>> knowledgeSources, String history, String suggestion) {
|
||||
StringBuilder context = new StringBuilder();
|
||||
|
||||
// 1. 支出情况表分析要求 - 增强要求
|
||||
context.append("## 支出情况表分析要求\n");
|
||||
context.append("请基于以下知识生成完整详细的公务接待/出国/公车运行维护/会议培训费等支出情况表数据:\n\n");
|
||||
context.append("1. 必须生成完整的支出情况表数据,覆盖2020-2023四个年度\n");
|
||||
context.append("2. 重点关注四类支出:公务接待、出国(出境)、公车运行维护、会议培训费\n");
|
||||
context.append("3. 从财务知识库中提取所有实际的支出数据,要求全面完整\n");
|
||||
context.append("4. 确保信息的准确性和完整性,包括决算数、预算数、增减情况和占比情况\n");
|
||||
context.append("5. 工作底稿索引必须准确对应实际文件名称,可以包含多个相关文件\n");
|
||||
context.append("6. 要求生成尽可能多、尽可能完整的支出记录,覆盖所有年份和支出类型\n\n");
|
||||
// 1. 审计关注要点(简化版)
|
||||
context.append("## 审计关注要点\n");
|
||||
context.append("请基于以下知识生成完整的公务接待/出国/公车运行维护/会议培训费等支出情况表数据。\n");
|
||||
context.append("重点关注以下审计要点:\n");
|
||||
context.append("1. 检查公务接待、出国、公车、会议培训四类支出的预算执行情况\n");
|
||||
context.append("2. 对比年初预算数和决算报表数,分析增减情况\n");
|
||||
context.append("3. 关注是否存在未经预算批准变相公款消费的情况\n");
|
||||
context.append("4. 检查报销手续是否完善,是否存在超支使用\n\n");
|
||||
|
||||
// 2. 数据格式要求
|
||||
context.append("## 数据格式要求\n");
|
||||
context.append("需要生成完整详细的支出情况表数据:\n\n");
|
||||
context.append(AuditContent1ExpenseConstants.DATA_FORMAT_REQUIREMENT);
|
||||
context.append("\n\n重要要求:\n");
|
||||
context.append("1. 必须根据知识库内容生成尽可能多、尽可能完整的支出信息\n");
|
||||
context.append("2. 工作底稿索引必须准确对应实际文件名称,避免使用附表或章节标题\n");
|
||||
context.append("3. 金额单位为元,保持两位小数,可以使用逗号分隔千位\n");
|
||||
context.append("4. 百分比计算要准确,增减情况正数表示增长,负数表示减少\n");
|
||||
context.append("5. 数据来源要具体,如:2023年度部门决算报表\n");
|
||||
context.append("6. 备注信息可以包含超支原因、特殊情况说明等\n\n");
|
||||
// 2. 特别提醒
|
||||
context.append("## 特别提醒\n");
|
||||
context.append("1. 必须准确识别支出类型:只提取\"公务接待\"、\"出国\"、\"公车运行维护\"、\"会议培训费\"四类支出\n");
|
||||
context.append("2. 不要将《工程造价和概(预)算执行情况表》等工程类报表误识别为\"三公经费\"报表\n");
|
||||
// context.append("3. 工作底稿索引必须使用实际存在的完整文件名称作为FileId\n\n");
|
||||
context.append("3. 工作底稿索引必须使用实际存在的完整文件名||FileUrl\n\n");
|
||||
|
||||
// 3. 历史内容
|
||||
if (StrUtil.isNotBlank(history)) {
|
||||
context.append("## 历史生成内容\n");
|
||||
context.append("以下是之前生成的内容,请基于此进行优化和补充:\n");
|
||||
context.append("## 历史生成内容参考\n");
|
||||
context.append(history).append("\n\n");
|
||||
}
|
||||
|
||||
// 4. 用户建议
|
||||
if (StrUtil.isNotBlank(suggestion)) {
|
||||
context.append("## 用户优化建议\n");
|
||||
context.append("请根据以下建议对生成内容进行调整:\n");
|
||||
context.append(suggestion).append("\n\n");
|
||||
}
|
||||
|
||||
// 5. 财务知识
|
||||
// 5. 财务知识(核心内容)
|
||||
if (!knowledgeSources.get("financial").isEmpty()) {
|
||||
context.append("## 财务知识\n");
|
||||
context.append("这是财务相关的报表和文件信息,请仔细分析并提取所有支出数据:\n");
|
||||
context.append("## 财务知识库内容\n");
|
||||
context.append("以下是财务相关的报表和文件信息,请仔细分析并提取所有支出数据:\n\n");
|
||||
knowledgeSources.get("financial").forEach(knowledge ->
|
||||
context.append(knowledge).append("\n"));
|
||||
context.append("\n");
|
||||
@@ -365,7 +243,7 @@ public class AuditContent1ExpenseServiceImpl extends AbstractAuditContentService
|
||||
|
||||
// 6. 法律法规知识
|
||||
if (!knowledgeSources.get("regulation").isEmpty()) {
|
||||
context.append("## 法律法规知识\n");
|
||||
context.append("## 法律法规参考\n");
|
||||
knowledgeSources.get("regulation").forEach(knowledge ->
|
||||
context.append(knowledge).append("\n"));
|
||||
context.append("\n");
|
||||
@@ -373,7 +251,7 @@ public class AuditContent1ExpenseServiceImpl extends AbstractAuditContentService
|
||||
|
||||
// 7. 审计案例知识
|
||||
if (!knowledgeSources.get("auditCase").isEmpty()) {
|
||||
context.append("## 审计案例知识\n");
|
||||
context.append("## 审计案例参考\n");
|
||||
knowledgeSources.get("auditCase").forEach(knowledge ->
|
||||
context.append(knowledge).append("\n"));
|
||||
}
|
||||
@@ -403,10 +281,10 @@ public class AuditContent1ExpenseServiceImpl extends AbstractAuditContentService
|
||||
|
||||
private int getLimitBySourceType(String sourceType) {
|
||||
switch (sourceType) {
|
||||
case "financial": return 150;
|
||||
case "regulation": return 80;
|
||||
case "auditCase": return 40;
|
||||
default: return 100;
|
||||
case "financial": return 100; // 减少数量,提高质量
|
||||
case "regulation": return 50;
|
||||
case "auditCase": return 30;
|
||||
default: return 50;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ public class AuditContent1LeaderListServiceImpl extends AbstractAuditContentServ
|
||||
JSONArray leaderListData = callWorkflow(DIFY_WORKFLOW_URL, DIFY_WORKFLOW_TOKEN, requestBody, "领导班子名单");
|
||||
|
||||
// 4. 数据验证和补充
|
||||
leaderListData = validateAndEnhanceLeaderListData(leaderListData);
|
||||
// leaderListData = validateAndEnhanceLeaderListData(leaderListData);
|
||||
|
||||
log.info("领导班子名单生成成功 - 记录数: {}, 处理时间: {}ms",
|
||||
leaderListData.size(), (System.currentTimeMillis() - startTime));
|
||||
@@ -117,13 +117,6 @@ public class AuditContent1LeaderListServiceImpl extends AbstractAuditContentServ
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 确保工作底稿索引是数组格式
|
||||
if (!leaderData.containsKey("workPaperIndex") || leaderData.get("workPaperIndex") == null) {
|
||||
JSONArray workPaperIndex = new JSONArray();
|
||||
workPaperIndex.add("《千汇发〔2025〕6号千汇公司关于领导班子成员工作分工的通知》");
|
||||
leaderData.put("workPaperIndex", workPaperIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,15 +21,6 @@ public class AuditContent2StrategyServiceImpl extends AbstractAuditContentServic
|
||||
// 工作流配置
|
||||
private static final String DIFY_WORKFLOW_TOKEN = "Bearer app-3cPaXHIPFPS6lIfMGV67NOu0";
|
||||
|
||||
// 分类定义 - 调整为5个分类
|
||||
private static final List<String> CATEGORY_ORDER = Arrays.asList(
|
||||
AuditContent2StrategyConstants.CATEGORY_GOVERNANCE_REFORM,
|
||||
AuditContent2StrategyConstants.CATEGORY_DEVELOPMENT_STRATEGY,
|
||||
AuditContent2StrategyConstants.CATEGORY_RISK_PREVENTION,
|
||||
AuditContent2StrategyConstants.CATEGORY_SOCIAL_ENVIRONMENTAL,
|
||||
AuditContent2StrategyConstants.CATEGORY_PERFORMANCE_ASSESSMENT
|
||||
);
|
||||
|
||||
@Override
|
||||
public JSONObject generateStrategyAuditTableData(String kbIds, String libraryKbIds, String projectLibrary,
|
||||
String userName, String history, String suggestion) {
|
||||
@@ -41,7 +32,7 @@ public class AuditContent2StrategyServiceImpl extends AbstractAuditContentServic
|
||||
try {
|
||||
// 异步并行处理每个分类
|
||||
Map<String, CompletableFuture<JSONArray>> futures = processCategoriesAsync(
|
||||
CATEGORY_ORDER,
|
||||
AuditContent2StrategyConstants.CATEGORY_ORDER,
|
||||
category -> generateCategoryDataAsync(category, kbIds, libraryKbIds, projectLibrary, userName, history, suggestion)
|
||||
);
|
||||
|
||||
@@ -49,7 +40,7 @@ public class AuditContent2StrategyServiceImpl extends AbstractAuditContentServic
|
||||
CompletableFuture.allOf(futures.values().toArray(new CompletableFuture[0])).join();
|
||||
|
||||
// 合并所有分类的结果
|
||||
JSONArray allData = mergeCategoryResults(CATEGORY_ORDER, futures);
|
||||
JSONArray allData = mergeCategoryResults(AuditContent2StrategyConstants.CATEGORY_ORDER, futures);
|
||||
|
||||
log.info("单位发展战略执行审计表生成成功 - 记录数: {}, 处理时间: {}ms", allData.size(), (System.currentTimeMillis() - startTime));
|
||||
|
||||
@@ -104,8 +95,10 @@ public class AuditContent2StrategyServiceImpl extends AbstractAuditContentServic
|
||||
knowledgeSources.put("regulation", new ArrayList<>());
|
||||
knowledgeSources.put("auditCase", new ArrayList<>());
|
||||
|
||||
// 构建当前分类的查询词
|
||||
List<String> categoryQueries = buildCategoryQueries(category);
|
||||
// 从常量类获取当前分类的查询词
|
||||
List<String> categoryQueries = AuditContent2StrategyConstants.CATEGORY_QUERIES.getOrDefault(
|
||||
category, Arrays.asList(category + " 审计 检查")
|
||||
);
|
||||
|
||||
// 企业单位库检索 - 这是主要考察内容
|
||||
if (StrUtil.isNotBlank(kbIds)) {
|
||||
@@ -113,7 +106,7 @@ public class AuditContent2StrategyServiceImpl extends AbstractAuditContentServic
|
||||
.map(String::trim)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.forEach(kbId -> knowledgeSources.get("enterprise")
|
||||
.addAll(queryKnowledgeBase(kbId, categoryQueries, 150))); // 增加检索数量
|
||||
.addAll(queryKnowledgeBase(kbId, categoryQueries, 150)));
|
||||
}
|
||||
|
||||
// 公共法律法规库检索
|
||||
@@ -135,8 +128,7 @@ public class AuditContent2StrategyServiceImpl extends AbstractAuditContentServic
|
||||
knowledgeSources.forEach((key, list) -> {
|
||||
List<String> processed = list.stream()
|
||||
.distinct()
|
||||
.sorted(this::strategyAuditComparator)
|
||||
.limit(getLimitBySourceType(key))
|
||||
.limit(AuditContent2StrategyConstants.SOURCE_LIMITS.getOrDefault(key, 50))
|
||||
.collect(Collectors.toList());
|
||||
knowledgeSources.put(key, processed);
|
||||
});
|
||||
@@ -150,26 +142,6 @@ public class AuditContent2StrategyServiceImpl extends AbstractAuditContentServic
|
||||
return knowledgeSources;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建分类特定的查询词
|
||||
*/
|
||||
private List<String> buildCategoryQueries(String category) {
|
||||
switch (category) {
|
||||
case AuditContent2StrategyConstants.CATEGORY_GOVERNANCE_REFORM:
|
||||
return Arrays.asList("法人治理 薪酬改革 国有资产管理", "改革创新 治理结构 董事会");
|
||||
case AuditContent2StrategyConstants.CATEGORY_DEVELOPMENT_STRATEGY:
|
||||
return Arrays.asList("混合所有制 剥离办社会职能 改革措施", "协调发展 改革方案 政策落实");
|
||||
case AuditContent2StrategyConstants.CATEGORY_RISK_PREVENTION:
|
||||
return Arrays.asList("发展规划 金融风险 债务风险", "绿色发展 风险防控 资产负债率");
|
||||
case AuditContent2StrategyConstants.CATEGORY_SOCIAL_ENVIRONMENTAL:
|
||||
return Arrays.asList("精准扶贫 污染防治 环境保护", "开放共享 社会责任 定点扶贫");
|
||||
case AuditContent2StrategyConstants.CATEGORY_PERFORMANCE_ASSESSMENT:
|
||||
return Arrays.asList("考核指标 经营业绩 目标责任", "创新发展 数据核实 完成情况");
|
||||
default:
|
||||
return Arrays.asList(category + " 审计 检查");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建完整的知识上下文
|
||||
*/
|
||||
@@ -179,47 +151,60 @@ public class AuditContent2StrategyServiceImpl extends AbstractAuditContentServic
|
||||
|
||||
// 1. 核心审计任务
|
||||
context.append("## 核心审计任务\n");
|
||||
context.append("为以下审计分类生成具体的审计表内容:\n");
|
||||
context.append("分类:").append(category).append("\n");
|
||||
context.append("描述:").append(AuditContent2StrategyConstants.CATEGORY_DESCRIPTIONS.get(category)).append("\n");
|
||||
context.append(AuditContent2StrategyConstants.AUDIT_OBJECTIVE).append("\n\n");
|
||||
context.append("本次重点审计分类:").append(category).append("\n");
|
||||
context.append("对应发展理念:").append(AuditContent2StrategyConstants.CATEGORY_DEVELOPMENT_CONCEPT.get(category)).append("\n");
|
||||
context.append("分类描述:").append(AuditContent2StrategyConstants.CATEGORY_DESCRIPTIONS.get(category)).append("\n");
|
||||
context.append("审计重点:").append(AuditContent2StrategyConstants.getBriefAuditFrameworkForCategory(category)).append("\n\n");
|
||||
|
||||
// 2. 审计框架(只传入当前分类相关的部分)
|
||||
context.append("## 审计框架(审计规则)\n");
|
||||
context.append("以下审计框架定义了审计范围和要点,请基于此框架开展工作:\n");
|
||||
// 2. 执行结果分析要求
|
||||
context.append("## 执行结果分析要求(必须遵循)\n");
|
||||
context.append("**必须深入分析实际执行结果,重点关注贯彻落实的证据:**\n");
|
||||
context.append("**检查要求(满足以下任一条件即可):**\n");
|
||||
context.append("1. **会议纪要路径**:检查是否有会议纪要证明按照\"党组织委员会→公司领导班子→董事会\"逐级开会落实;\n");
|
||||
context.append(" *或者*\n");
|
||||
context.append("2. **其他落实证据**:检查是否有其他材料(签批文件、任务分解、执行报告等)证明相关审计内容已从上往下逐级贯彻落实。\n");
|
||||
context.append("\n");
|
||||
context.append("**在检查证据中必须明确指出:**\n");
|
||||
context.append("①是否存在有效的贯彻落实证据(会议纪要或其他材料);\n");
|
||||
context.append("②执行过程是否有充分材料支撑;\n");
|
||||
context.append("③执行结果是否达到预期。\n\n");
|
||||
|
||||
// 获取当前分类相关的审计框架片段
|
||||
// 3. 审计框架
|
||||
context.append("## 审计框架(审计规则)\n");
|
||||
String categoryFramework = AuditContent2StrategyConstants.CATEGORY_AUDIT_FRAMEWORK_FRAGMENTS.get(category);
|
||||
if (categoryFramework != null) {
|
||||
context.append(categoryFramework).append("\n");
|
||||
} else {
|
||||
// 如果找不到对应分类,则使用完整的框架(保底)
|
||||
log.warn("未找到分类 {} 对应的审计框架片段,使用完整框架", category);
|
||||
context.append(AuditContent2StrategyConstants.AUDIT_FRAMEWORK).append("\n");
|
||||
}
|
||||
|
||||
// 3. 企业单位知识(主要考察内容)
|
||||
// 4. 企业单位知识(主要考察内容)
|
||||
if (!knowledgeSources.get("enterprise").isEmpty()) {
|
||||
context.append("## 企业单位知识(具体考察内容)\n");
|
||||
context.append("以下是企业单位的实际资料,请基于审计框架,结合这些具体内容生成审计记录:\n\n");
|
||||
context.append("基于审计框架,结合以下企业实际资料生成审计记录:\n\n");
|
||||
knowledgeSources.get("enterprise").forEach(knowledge ->
|
||||
context.append("• ").append(knowledge).append("\n"));
|
||||
context.append("\n");
|
||||
} else {
|
||||
context.append("## 企业单位知识\n");
|
||||
context.append("未检索到相关企业资料,请基于审计框架生成审计内容。\n\n");
|
||||
}
|
||||
|
||||
// 4. 审计工作原则
|
||||
// 5. 审计工作原则
|
||||
context.append("## 审计工作原则\n");
|
||||
context.append(AuditContent2StrategyConstants.AUDIT_PRINCIPLES).append("\n\n");
|
||||
|
||||
// 5. 数据格式要求(精简版)
|
||||
// 6. 数据格式要求
|
||||
context.append("## 数据格式要求\n");
|
||||
context.append("生成JSON数组,每个元素包含4个字段:\n");
|
||||
context.append("1. auditContent: 具体审计内容(基于框架,结合企业实际)\n");
|
||||
context.append("2. checkEvidence: 检查证据(引用企业具体文件)\n");
|
||||
context.append("2. checkEvidence: 检查证据(引用企业具体文件,必须包含执行结果分析)\n");
|
||||
context.append("3. testResult: 测试结果(通过/不通过)(基于充分证据严格判断)\n");
|
||||
context.append("4. workPaperIndex: 工作底稿索引(具体文件名)\n\n");
|
||||
// context.append("4. workPaperIndex: 工作底稿索引(具体文件FileId)\n\n");
|
||||
context.append("4. workPaperIndex: 工作底稿索引(具体文件完整文件名||FileUrl)\n\n");
|
||||
|
||||
// 6. 法规和案例参考
|
||||
// 7. 法规和案例参考(如果有)
|
||||
if (!knowledgeSources.get("regulation").isEmpty()) {
|
||||
context.append("## 法律法规参考\n");
|
||||
knowledgeSources.get("regulation").forEach(knowledge ->
|
||||
@@ -234,13 +219,13 @@ public class AuditContent2StrategyServiceImpl extends AbstractAuditContentServic
|
||||
context.append("\n");
|
||||
}
|
||||
|
||||
// 7. 历史内容(如果有)
|
||||
// 8. 历史内容(如果有)
|
||||
if (StrUtil.isNotBlank(history)) {
|
||||
context.append("## 历史生成内容\n");
|
||||
context.append(history).append("\n\n");
|
||||
}
|
||||
|
||||
// 8. 用户建议(如果有)
|
||||
// 9. 用户建议(如果有)
|
||||
if (StrUtil.isNotBlank(suggestion)) {
|
||||
context.append("## 用户建议\n");
|
||||
context.append(suggestion).append("\n");
|
||||
@@ -251,41 +236,4 @@ public class AuditContent2StrategyServiceImpl extends AbstractAuditContentServic
|
||||
|
||||
return context.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发展战略审计相关性比较器
|
||||
*/
|
||||
private int strategyAuditComparator(String reg1, String reg2) {
|
||||
int score1 = calculateStrategyAuditRelevanceScore(reg1);
|
||||
int score2 = calculateStrategyAuditRelevanceScore(reg2);
|
||||
return Integer.compare(score2, score1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算发展战略审计相关性分数
|
||||
*/
|
||||
private int calculateStrategyAuditRelevanceScore(String content) {
|
||||
return AuditContent2StrategyConstants.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 60; // 减少法规数量
|
||||
case "auditCase": return 40; // 减少案例数量
|
||||
default: return 50;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前分类相关的审计框架内容
|
||||
* 这是一个辅助方法,方便其他地方调用
|
||||
*/
|
||||
private String getAuditFrameworkForCategory(String category) {
|
||||
String framework = AuditContent2StrategyConstants.CATEGORY_AUDIT_FRAMEWORK_FRAGMENTS.get(category);
|
||||
return framework != null ? framework : AuditContent2StrategyConstants.AUDIT_FRAMEWORK;
|
||||
}
|
||||
}
|
||||
@@ -194,14 +194,19 @@ public class AuditContent3TripleServiceImpl extends AbstractAuditContentService
|
||||
context.append("- companyFormulation:公司层面的三重一大相关制度规定及执行情况,需包含详细分析过程和查阅依据\n");
|
||||
context.append("- checkEvidence:审计检查的证据,需详细描述查阅过程、查阅的具体文件和内容\n");
|
||||
context.append("- testResult:审计测试的结果(通过/不通过),严格判断,从严掌握通过标准\n");
|
||||
context.append("- workPaperIndex:相关《参考文件名》,必须是实际存在的完整文件名,不能使用附表标题,确保能在文件夹中搜索到\n");
|
||||
// context.append("- workPaperIndex:相关《参考文件名FileId》,必须是实际存在的完整文件FileId,不能使用附表标题,确保能在文件夹中搜索到\n");
|
||||
context.append("- workPaperIndex:相关[\"实际存在的完整文件名||FileUrl\"],必须是实际存在的完整文件名,不能使用附表标题,确保能在文件夹中搜索到\n");
|
||||
context.append("\n注意:\n");
|
||||
context.append("1. 请根据知识库内容尽可能全面地生成所有相关制度规定和检查点\n");
|
||||
context.append("2. 公司执行情况分析需包含:查阅了哪些文件、发现了什么内容、与制度的差异点、分析判断过程\n");
|
||||
context.append("3. 工作底稿索引必须准确对应实际文件名称,避免使用附表或章节标题\n");
|
||||
context.append("4. 测试结果判定需严格,对于制度不一致、执行不到位、证据不充分的情况必须判定为不通过\n");
|
||||
context.append("5. 审计检查的证据主要按公司制度为主,政策内容和集团制度为次要\n");
|
||||
context.append("6. 审计检查的证据增加判断公司制度是否与政策内容和集团制度相符\n\n");
|
||||
context.append("6. 审计检查的证据增加判断公司制度是否与政策内容和集团制度相符\n");
|
||||
context.append("7. 历史问题整改分析:需核查历史问题是否已整改,结合最新时间材料(如最新会议纪要)分析当前是否存在相同问题\n");
|
||||
context.append("8. 项目上会核查:涉及三重一大的项目必须核查是否按规定上会,检查有无会议纪要作为证据\n");
|
||||
context.append("9. 制度权限关系:明确分析公司制度与集团制度的关联关系,权限设置必须遵循公司权限≤集团权限的原则\n");
|
||||
context.append("10. 层级关系识别:注意识别文件中的上级单位信息,分析制度执行是否符合层级管理要求\n\n");
|
||||
|
||||
// 3. 参考数据(从常量类中获取)
|
||||
context.append("## 参考数据\n");
|
||||
|
||||
@@ -119,7 +119,7 @@ public class AuditContent4TargetServiceImpl extends AbstractAuditContentService
|
||||
"目标责任 目标责任制 目标任务",
|
||||
"下达文件 计划文件 考核办法",
|
||||
"完成情况 未完成原因 考核结果",
|
||||
"地矿局 上级主管 主管部门",
|
||||
"上级主管 主管部门",
|
||||
"年度计划 工作目标 考核指标",
|
||||
"责任书 责任状 目标考核",
|
||||
"经营目标 工作计划 绩效考核",
|
||||
@@ -177,7 +177,8 @@ public class AuditContent4TargetServiceImpl extends AbstractAuditContentService
|
||||
// 9. 特别提醒(新增关键优化点)
|
||||
context.append("## 特别提醒\n");
|
||||
context.append("1. 审计证据必须包含具体的查阅过程:查阅了哪些文件、文件中发现了什么内容\n");
|
||||
context.append("2. workPaperIndex必须填写实际存在的完整文件名,确保能在文件夹中搜索到\n");
|
||||
// context.append("2. workPaperIndex必须填写实际存在的完整文件FileId,确保能在文件夹中搜索到\n");
|
||||
context.append("2. workPaperIndex必须填写实际存在的完整文件名||FileUrl,确保能在文件夹中搜索到\n");
|
||||
context.append("3. 完成情况判定必须基于充分证据:只有证据充分且完全符合要求才能判定为已完成\n");
|
||||
context.append("4. 对于执行不到位、效果不佳、证据不充分的情况必须在备注中说明\n");
|
||||
context.append("5. 尽可能识别知识库中所有相关年度,生成对应的审计记录\n");
|
||||
@@ -315,13 +316,13 @@ public class AuditContent4TargetServiceImpl extends AbstractAuditContentService
|
||||
}
|
||||
|
||||
// 确保工作底稿索引是数组格式
|
||||
if (!item.containsKey("workPaperIndex") || !(item.get("workPaperIndex") instanceof JSONArray)) {
|
||||
JSONArray workPaperIndex = new JSONArray();
|
||||
String year = item.getString("year");
|
||||
workPaperIndex.add(year + "年度目标责任书");
|
||||
workPaperIndex.add(year + "年度工作总结报告");
|
||||
item.put("workPaperIndex", workPaperIndex);
|
||||
}
|
||||
// if (!item.containsKey("workPaperIndex") || !(item.get("workPaperIndex") instanceof JSONArray)) {
|
||||
// JSONArray workPaperIndex = new JSONArray();
|
||||
// String year = item.getString("year");
|
||||
// workPaperIndex.add(year + "年度目标责任书");
|
||||
// workPaperIndex.add(year + "年度工作总结报告");
|
||||
// item.put("workPaperIndex", workPaperIndex);
|
||||
// }
|
||||
|
||||
processedData.add(item);
|
||||
}
|
||||
|
||||
@@ -237,7 +237,8 @@ public class AuditContent5BudgetExecutionServiceImpl extends AbstractAuditConten
|
||||
context.append("1. 必须全面分析预算执行全过程,包括执行进度、执行效果、执行合规性\n");
|
||||
context.append("2. 重点关注预算执行率、资金到位率、预算执行偏差等关键指标\n");
|
||||
context.append("3. 金额字段应填写具体数值,如\"1,000,000.00\",不能填写简单的\"有\"或\"无\"\n");
|
||||
context.append("4. workPaperIndex必须填写实际存在的完整文件名\n");
|
||||
// context.append("4. workPaperIndex必须填写实际存在的完整文件FileId\n");
|
||||
context.append("4. workPaperIndex必须填写实际存在的完整文件名||FileUrl\n");
|
||||
context.append("5. 对于无数据的字段,可填写\"-\"或留空,但不能填写\"无\"\n");
|
||||
context.append("6. 基于预算执行全流程进行审计分析,包括月度、季度、年度执行情况\n");
|
||||
context.append("7. 重点关注预算执行偏差原因分析和整改措施\n");
|
||||
|
||||
@@ -237,7 +237,8 @@ public class AuditContent5BudgetManageServiceImpl extends AbstractAuditContentSe
|
||||
context.append("1. 必须全面识别知识库中所有预算科目,包括:基本支出、项目支出、人员经费、公用经费等\n");
|
||||
context.append("2. 每个独立的预算科目都要生成独立的审计记录,不限制数量,尽可能多地生成\n");
|
||||
context.append("3. 金额字段应填写具体数值,如\"1,000,000.00\",不能填写简单的\"有\"或\"无\"\n");
|
||||
context.append("4. workPaperIndex必须填写实际存在的完整文件名\n");
|
||||
// context.append("4. workPaperIndex必须填写实际存在的完整文件FileId\n");
|
||||
context.append("4. workPaperIndex必须填写实际存在的完整文件名||FileUrl\n");
|
||||
context.append("5. 对于无数据的字段,可填写\"-\"或留空,但不能填写\"无\"\n");
|
||||
context.append("6. 基于预算编制、调整、执行的全流程进行审计分析\n");
|
||||
context.append("7. 重点关注预算调整的合规性和预算执行的真实性\n\n");
|
||||
|
||||
@@ -192,7 +192,8 @@ public class AuditContent6StateAssetsServiceImpl extends AbstractAuditContentSer
|
||||
context.append("1. 必须全面识别知识库中所有国有资产,包括:房屋、土地、车辆、机械设备、办公设备、电子设备、家具、无形资产等\n");
|
||||
context.append("2. 每个独立的资产都要生成独立的审计记录,不限制数量,尽可能多地生成\n");
|
||||
context.append("3. 即使资产信息不完整,也要基于现有信息生成审计记录\n");
|
||||
context.append("4. workPaperIndex必须填写实际存在的完整文件名\n");
|
||||
// context.append("4. workPaperIndex必须填写实际存在的完整文件FileId\n");
|
||||
context.append("4. workPaperIndex必须填写实际存在的完整文件名||FileUrl\n");
|
||||
context.append("5. 对于未出租资产,承租方、合同金额等字段填写\"未出租\"\n");
|
||||
context.append("6. 备注中应详细说明资产状况、使用情况、合规性评价\n");
|
||||
context.append("7. 不能填写简单的\"无\",要提供有意义的描述\n\n");
|
||||
@@ -320,12 +321,12 @@ public class AuditContent6StateAssetsServiceImpl extends AbstractAuditContentSer
|
||||
}
|
||||
|
||||
// 确保工作底稿索引是数组格式
|
||||
if (!item.containsKey("workPaperIndex") || !(item.get("workPaperIndex") instanceof JSONArray)) {
|
||||
JSONArray workPaperIndex = new JSONArray();
|
||||
String assetName = item.getString("assetName");
|
||||
workPaperIndex.add(assetName + "资产登记台账");
|
||||
item.put("workPaperIndex", workPaperIndex);
|
||||
}
|
||||
// if (!item.containsKey("workPaperIndex") || !(item.get("workPaperIndex") instanceof JSONArray)) {
|
||||
// JSONArray workPaperIndex = new JSONArray();
|
||||
// String assetName = item.getString("assetName");
|
||||
// workPaperIndex.add(assetName + "资产登记台账");
|
||||
// item.put("workPaperIndex", workPaperIndex);
|
||||
// }
|
||||
|
||||
processedData.add(item);
|
||||
}
|
||||
|
||||
@@ -227,7 +227,8 @@ public class AuditContent7InvestmentServiceImpl extends AbstractAuditContentServ
|
||||
context.append(" • 通过:证据充分且完全符合要求\n");
|
||||
context.append(" • 不通过:制度不一致、执行不到位、证据不充分\n");
|
||||
context.append(" • 待检查:无法确定,需要进一步检查\n");
|
||||
context.append("- **workPaperIndex**:相关《工作底稿索引》,必须是实际存在的完整文件名,确保能在文件夹中搜索到\n");
|
||||
// context.append("- **workPaperIndex**:相关《工作底稿索引FileId》,必须是实际存在的完整文件FileId,确保能在文件夹中搜索到\n");
|
||||
context.append("- **workPaperIndex**:相关[\"实际存在的完整文件名||FileUrl\"],必须是实际存在的完整文件名,不能使用附表标题,确保能在文件夹中搜索到\n");
|
||||
context.append("- **fileIndex**:相关《文件索引》,关联的支持文件\n\n");
|
||||
|
||||
// 5. 审计内容模板(提供参考)
|
||||
|
||||
@@ -233,7 +233,8 @@ public class AuditContent8InternalControlServiceImpl extends AbstractAuditConten
|
||||
context.append("- testSteps(测试步骤):**只包含当前步骤内容**,如\"(1)是否有制度规定\"\n");
|
||||
context.append("- checkEvidence(检查证据):针对当前测试步骤的详细检查证据\n");
|
||||
context.append("- testResult(测试结果):基于当前测试步骤的检查证据严格判断,只有证据充分且完全符合要求才能判定为\"通过\",否则必须判定为\"不通过\"\n");
|
||||
context.append("- workPaperIndex(工作底稿索引):实际存在的完整文件名,确保能在文件夹中搜索到\n\n");
|
||||
// context.append("- workPaperIndex(工作底稿索引FileId):实际存在的完整文件FileId,确保能在文件夹中搜索到\n\n");
|
||||
context.append("- workPaperIndex(工作底稿索引文件名):实际存在的完整文件名||FileUrl,确保能在文件夹中搜索到\n\n");
|
||||
|
||||
context.append("**生成规则:**\n");
|
||||
context.append("1. **每个测试步骤单独生成一条完整记录**\n");
|
||||
|
||||
@@ -18,6 +18,9 @@ import java.util.stream.Collectors;
|
||||
public class AuditContent9PersonnelServiceImpl extends AbstractAuditContentService implements AuditContent9PersonnelService {
|
||||
|
||||
private static final String DIFY_WORKFLOW_TOKEN = "Bearer app-fmGhYITVQVAHaY3GJYonIGPA";
|
||||
|
||||
private static final String DIFY_WORKFLOW_TOKEN_EXT = "Bearer app-olBqOrzi1IQCWyDqthME6SYG";
|
||||
|
||||
|
||||
@Override
|
||||
public JSONObject generatePersonnelTableData(String kbIds, String libraryKbIds, String projectLibrary,
|
||||
@@ -150,12 +153,12 @@ public class AuditContent9PersonnelServiceImpl extends AbstractAuditContentServi
|
||||
);
|
||||
}
|
||||
|
||||
// 去重和排序
|
||||
// 去重
|
||||
knowledgeSources.forEach((key, list) -> {
|
||||
List<String> processed = list.stream()
|
||||
.distinct()
|
||||
.sorted(this::keywordRelevanceComparator)
|
||||
.limit(getLimitBySourceType(key))
|
||||
// .sorted(this::keywordRelevanceComparator)
|
||||
// .limit(getLimitBySourceType(key))
|
||||
.collect(Collectors.toList());
|
||||
knowledgeSources.put(key, processed);
|
||||
});
|
||||
@@ -179,6 +182,14 @@ public class AuditContent9PersonnelServiceImpl extends AbstractAuditContentServi
|
||||
keywords.addAll(Arrays.asList("劳务外包", "外包合同", "假外包真派遣"));
|
||||
} else if (auditContent.contains("人员经费")) {
|
||||
keywords.addAll(Arrays.asList("工资总额", "福利费", "劳务费"));
|
||||
keywords.addAll(Arrays.asList(
|
||||
"工资总额", "福利费", "劳务费",
|
||||
"补贴", "慰问", "福利",
|
||||
"节日", "生日", "体检",
|
||||
"食堂", "交通补贴", "通讯补贴",
|
||||
"误餐补贴", "职工福利", "员工福利",
|
||||
"防暑降温", "取暖费", "劳保用品"
|
||||
));
|
||||
} else if (auditContent.contains("借用人员")) {
|
||||
keywords.addAll(Arrays.asList("借调", "借用人员", "借调程序"));
|
||||
} else if (auditContent.contains("绩效管理")) {
|
||||
@@ -206,19 +217,29 @@ public class AuditContent9PersonnelServiceImpl extends AbstractAuditContentServi
|
||||
String userName, String history, String suggestion) {
|
||||
|
||||
try {
|
||||
// 构建上下文
|
||||
String context = buildSubItemContext(auditContent, subContent, knowledgeSources,
|
||||
history, suggestion);
|
||||
|
||||
// 调用Dify工作流 - 使用正确的请求体格式
|
||||
JSONArray ext = new JSONArray();
|
||||
String updatedHistory = history; // 使用新变量避免修改入参
|
||||
// 检查是否是福利费列支范围检查子项
|
||||
String extSubContent = AuditContent9PersonnelConstants.AUDIT_SUB_CONTENTS.get(AuditContent9PersonnelConstants.AUDIT_CONTENTS[2]).get(1).getAuditTarget();
|
||||
if (subContent.getAuditTarget().equals(extSubContent)) {
|
||||
// 生成扩展数据(福利费明细清单)
|
||||
String contextExt = buildSubItemContextExt(auditContent, subContent, knowledgeSources, history, suggestion);
|
||||
JSONObject requestBodyExt = buildWorkflowRequest(contextExt, userName);
|
||||
ext = callWorkflow(DIFY_WORKFLOW_URL, DIFY_WORKFLOW_TOKEN_EXT, requestBodyExt, auditContent + "-" + subContent.getAuditTarget());
|
||||
// 更新历史内容(不影响原始入参)
|
||||
if (StrUtil.isNotBlank(history)) {
|
||||
updatedHistory = history + "\n《福利费超范围支出明细清单》:" + ext.toJSONString();
|
||||
}
|
||||
}
|
||||
|
||||
// 构建主上下文
|
||||
String context = buildSubItemContext(auditContent, subContent, knowledgeSources, updatedHistory, suggestion);
|
||||
// 调用Dify工作流
|
||||
JSONObject requestBody = buildWorkflowRequest(context, userName);
|
||||
|
||||
JSONArray result = callWorkflow(DIFY_WORKFLOW_URL, DIFY_WORKFLOW_TOKEN,
|
||||
requestBody, auditContent + "-" + subContent.getAuditTarget());
|
||||
JSONArray result = callWorkflow(DIFY_WORKFLOW_URL, DIFY_WORKFLOW_TOKEN, requestBody, auditContent + "-" + subContent.getAuditTarget());
|
||||
|
||||
// 处理返回结果
|
||||
return processSubItemResult(result, auditContent, subContent);
|
||||
|
||||
return processSubItemResult(result, auditContent, subContent, ext);
|
||||
} catch (Exception e) {
|
||||
log.error("生成子项数据失败: {} - {}", auditContent, subContent.getAuditTarget(), e);
|
||||
return new JSONArray();
|
||||
@@ -251,7 +272,15 @@ public class AuditContent9PersonnelServiceImpl extends AbstractAuditContentServi
|
||||
context.append("## 重要要求\n");
|
||||
context.append("1. 必须使用具体单位名称,禁止使用'XX单位'等模糊词汇\n");
|
||||
context.append("2. 审计记录必须具体,包含文件名称、数据、人员等详细信息\n");
|
||||
context.append("3. 重点关注问题发现,提供具体证据和建议\n\n");
|
||||
context.append("3. 重点关注问题发现,提供具体证据和建议\n");
|
||||
context.append("4. **如果在上传资料中找不到相应证据/凭证,不要直接判定违反规定,应说明'未找到相关材料,无法判定'**\n");
|
||||
context.append("5. **除了审计证据中列出的资料清单,还需主动查找上传材料中其他涉及审计内容和目标的材料**\n");
|
||||
context.append("6. **合同与主体公司不相关时,不应判定在主体公司责任范围内,需明确区分责任主体**\n\n");
|
||||
|
||||
context.append("## 审计判断原则\n");
|
||||
context.append("1. **证据不足原则**:当缺乏关键证据时,不做出违规判定\n");
|
||||
context.append("2. **主动查找原则**:不局限于给定清单,主动识别所有相关材料\n");
|
||||
context.append("3. **责任主体原则**:明确区分合同主体,不扩大责任范围\n\n");
|
||||
|
||||
context.append("## 返回格式\n");
|
||||
context.append("返回JSON数组,每条记录包含以下字段:\n");
|
||||
@@ -259,15 +288,33 @@ public class AuditContent9PersonnelServiceImpl extends AbstractAuditContentServi
|
||||
context.append("- auditTarget:固定为'").append(subContent.getAuditTarget()).append("'\n");
|
||||
context.append("- auditEvidence:固定为'").append(subContent.getAuditEvidence()).append("'\n");
|
||||
context.append("- generationResult:审计发现和结论\n");
|
||||
context.append("- workPaperIndex:工作底稿索引,具体的文件名数组\n\n");
|
||||
// context.append("- workPaperIndex:工作底稿索引,具体的文件FileId数组\n\n");
|
||||
context.append("- workPaperIndex:工作底稿索引,具体的文件[\"实际存在的完整文件名1||FileUrl1\", \"实际存在的完整文件名2||FileUrl2\", ...]数组\n\n");
|
||||
|
||||
context.append("## generationResult格式\n");
|
||||
context.append("标题:在审计期间,[具体单位名称]存在[具体问题]\n\n");
|
||||
context.append("审计记录:\n经核查[具体文件],发现:[具体事实]\n\n");
|
||||
context.append("审计发现:\n上述行为构成[问题性质],违反了[相关规定]\n\n");
|
||||
context.append("定性依据:\n① [法规1];② [法规2]\n\n");
|
||||
context.append("处理建议:\n1. [建议1];2. [建议2]\n\n");
|
||||
context.append("附件:\n- [文件1]\n- [文件2]\n\n");
|
||||
context.append("标题:在审计期间,[具体单位名称]存在[具体问题](如有充分证据)\n");
|
||||
context.append("或:在审计期间,[具体单位名称]未提供充分证据证明[审计事项]\n\n");
|
||||
|
||||
context.append("审计记录:\n");
|
||||
context.append("1. 核查的具体文件和内容\n");
|
||||
context.append("2. **注明是否找到审计证据清单中要求的材料**\n");
|
||||
context.append("3. **注明是否发现其他相关材料**\n");
|
||||
context.append("4. **注明合同主体是否与审计单位一致**\n\n");
|
||||
|
||||
context.append("审计发现:\n");
|
||||
context.append("1. 如有充分证据:上述行为构成[问题性质],违反了[相关规定]\n");
|
||||
context.append("2. 如证据不足:未找到相关材料,无法判定是否合规\n\n");
|
||||
|
||||
context.append("定性依据:\n");
|
||||
context.append("① [法规1];② [法规2]\n");
|
||||
context.append("**如无违规,可不填写定性依据**\n\n");
|
||||
|
||||
context.append("处理建议:\n");
|
||||
context.append("1. [建议1];2. [建议2]\n\n");
|
||||
|
||||
context.append("附件:\n");
|
||||
context.append("- [实际查阅的文件1]\n");
|
||||
context.append("- [实际查阅的文件2]\n\n");
|
||||
|
||||
// 添加历史内容
|
||||
if (StrUtil.isNotBlank(history)) {
|
||||
@@ -303,11 +350,160 @@ public class AuditContent9PersonnelServiceImpl extends AbstractAuditContentServi
|
||||
return context.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建福利费超范围支出明细清单的上下文
|
||||
*/
|
||||
private String buildSubItemContextExt(String auditContent,
|
||||
AuditContent9PersonnelConstants.AuditSubContent subContent,
|
||||
Map<String, List<String>> knowledgeSources,
|
||||
String history, String suggestion) {
|
||||
|
||||
StringBuilder context = new StringBuilder();
|
||||
|
||||
context.append("## 审计任务\n");
|
||||
context.append("生成福利费支出明细清单数据\n\n");
|
||||
|
||||
context.append("## 审计要求\n");
|
||||
context.append("1. 审计内容:").append(auditContent).append("\n");
|
||||
context.append("2. 审计目标:").append(subContent.getAuditTarget()).append("\n");
|
||||
context.append("3. 审计证据:").append(subContent.getAuditEvidence()).append("\n");
|
||||
context.append("4. 审计方法:").append(subContent.getAiTestContent()).append("\n");
|
||||
context.append("5. 制度依据:").append(subContent.getRegulationBasis()).append("\n\n");
|
||||
|
||||
context.append("## 生成结果要求\n");
|
||||
context.append("生成福利费支出明细清单,包括所有福利费支出的详细记录\n\n");
|
||||
|
||||
context.append("## 重要要求\n");
|
||||
context.append("1. **积极查找知识库中所有福利费相关支出记录**,包括但不限于:\n");
|
||||
context.append(" - 福利费支出凭证、报销单\n");
|
||||
context.append(" - 工资表中的福利费项目\n");
|
||||
context.append(" - 补贴发放记录、福利发放记录\n");
|
||||
context.append(" - 节日慰问、职工福利相关支出\n");
|
||||
context.append(" - 职工食堂、交通补贴、通讯补贴等\n");
|
||||
context.append("2. **即使信息不完整也要尽可能记录**:\n");
|
||||
context.append(" - 如果凭证号不明确,可以使用'未知'或根据内容推断\n");
|
||||
context.append(" - 如果日期不明确,可以合理推断或使用'审计期间'\n");
|
||||
context.append(" - 如果金额不明确,可以根据上下文合理估计\n");
|
||||
context.append("3. **允许合理推断**:\n");
|
||||
context.append(" - 根据支出用途推断款项性质\n");
|
||||
context.append(" - 根据文件内容推断归属部门/人员\n");
|
||||
context.append(" - 根据上下文补充缺失信息\n");
|
||||
context.append("4. **优先使用实际信息**,只有在确实无法获取时才允许推断\n\n");
|
||||
|
||||
context.append("## 审计判断原则\n");
|
||||
context.append("1. **积极查找原则**:尽可能查找所有可能的福利费相关支出\n");
|
||||
context.append("2. **信息补充原则**:对不完整信息进行合理补充\n");
|
||||
context.append("3. **分类识别原则**:识别以下类型的福利费支出:\n");
|
||||
context.append(" - 职工福利发放(节日福利、生日福利等)\n");
|
||||
context.append(" - 职工补贴(交通、通讯、餐饮补贴等)\n");
|
||||
context.append(" - 职工活动支出(文体活动、旅游活动等)\n");
|
||||
context.append(" - 职工慰问支出(生病慰问、困难补助等)\n");
|
||||
context.append(" - 其他福利性支出\n");
|
||||
context.append("4. **合规性初步判断**:\n");
|
||||
context.append(" - 对于明显超出常规福利费范围的项目进行标注\n");
|
||||
context.append(" - 对于可能违规的支出进行初步分析\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");
|
||||
|
||||
context.append("## 返回格式\n");
|
||||
context.append("返回JSON数组,每条记录必须包含以下字段,严格按照字段名和格式:\n");
|
||||
context.append("- **index**:序号(数字,从1开始自动递增)\n");
|
||||
context.append("- **voucher**:凭证号(字符串,优先使用实际凭证号,如无法获取可用'未知'或合理推断)\n");
|
||||
context.append("- **expenditureDate**:支出日期(字符串,格式:YYYY-MM-DD,如无法获取可用'审计期间')\n");
|
||||
context.append("- **usage**:用途(字符串,详细说明支出用途)\n");
|
||||
context.append("- **payee**:收款方(字符串,完整收款方名称,如无法获取可用'相关单位/个人')\n");
|
||||
context.append("- **amount**:金额(数字,单位:元,保留两位小数,如无法获取可用0.00)\n");
|
||||
context.append("- **belongTo**:归属部门/人员(字符串,明确责任主体,如无法获取可用'相关部门')\n");
|
||||
context.append("- **natureDesc**:款项性质说明(字符串,详细说明款项性质和目的)\n");
|
||||
context.append("- **violationDesc**:违规说明(字符串,如果不明确是否违规,可留空或写'待进一步核实')\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");
|
||||
context.append("6. **其他福利**:防暑降温费、取暖费、劳保用品等\n\n");
|
||||
|
||||
context.append("## 如何从知识库中提取信息\n");
|
||||
context.append("1. **搜索关键词**:福利费、补贴、慰问、福利、节日、生日、体检、活动、食堂、交通、通讯、餐饮\n");
|
||||
context.append("2. **文件类型**:财务凭证、报销单、工资表、发放记录、会议纪要、审批单\n");
|
||||
context.append("3. **提取策略**:\n");
|
||||
context.append(" - 从财务凭证中提取凭证号、金额、日期\n");
|
||||
context.append(" - 从报销单中提取用途、收款方\n");
|
||||
context.append(" - 从审批记录中提取归属部门\n");
|
||||
context.append(" - 综合分析文件内容推断款项性质\n\n");
|
||||
|
||||
context.append("## 特殊处理说明\n");
|
||||
context.append("1. **如果知识库中没有直接福利费凭证**:\n");
|
||||
context.append(" - 可以查找相关会议纪要中提到的福利费支出\n");
|
||||
context.append(" - 可以查找工资表中的福利费项目\n");
|
||||
context.append(" - 可以查找报销单中的福利相关支出\n");
|
||||
context.append("2. **信息补充方法**:\n");
|
||||
context.append(" - 通过文件名推断凭证号\n");
|
||||
context.append(" - 通过文件创建日期推断支出日期\n");
|
||||
context.append(" - 通过上下文推断款项用途\n");
|
||||
context.append("3. **生成多条记录**:\n");
|
||||
context.append(" - 一条福利费支出凭证可以生成一条记录\n");
|
||||
context.append(" - 一份工资表中的福利费项目可以生成多条记录\n");
|
||||
context.append(" - 一份会议纪要中的多项福利费决定可以生成多条记录\n\n");
|
||||
|
||||
// 添加历史内容
|
||||
if (StrUtil.isNotBlank(history)) {
|
||||
context.append("## 历史内容\n");
|
||||
context.append(history).append("\n\n");
|
||||
}
|
||||
|
||||
// 添加用户建议
|
||||
if (StrUtil.isNotBlank(suggestion)) {
|
||||
context.append("## 用户建议\n");
|
||||
context.append(suggestion).append("\n\n");
|
||||
}
|
||||
|
||||
// 添加相关知识
|
||||
if (!knowledgeSources.get("enterprise").isEmpty()) {
|
||||
context.append("## 企业财务信息\n");
|
||||
context.append("请仔细查找以下内容中的福利费相关信息:\n");
|
||||
knowledgeSources.get("enterprise").forEach(info -> {
|
||||
context.append(info).append("\n");
|
||||
});
|
||||
}
|
||||
|
||||
// if (!knowledgeSources.get("regulation").isEmpty()) {
|
||||
// context.append("## 法规信息\n");
|
||||
// context.append("参考以下福利费管理规定:\n");
|
||||
// knowledgeSources.get("regulation").forEach(info -> {
|
||||
// context.append(info).append("\n");
|
||||
// });
|
||||
// }
|
||||
|
||||
// if (!knowledgeSources.get("auditCase").isEmpty()) {
|
||||
// context.append("## 审计案例信息\n");
|
||||
// context.append("参考以下福利费审计案例:\n");
|
||||
// knowledgeSources.get("auditCase").forEach(info -> {
|
||||
// context.append(info).append("\n");
|
||||
// });
|
||||
// }
|
||||
|
||||
context.append("\n## 最终要求\n");
|
||||
context.append("请基于以上信息和知识库内容,尽可能多地生成福利费支出明细清单数据。\n");
|
||||
context.append("要求尽可能真实、完整,即使信息不完整也要尽量生成记录。\n");
|
||||
context.append("如果知识库中没有任何福利费相关信息,请返回空数组。\n");
|
||||
context.append("如果有福利费相关信息,请至少生成3-5条记录,尽可能多地提取信息。\n");
|
||||
|
||||
return context.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理子项返回结果
|
||||
*/
|
||||
private JSONArray processSubItemResult(JSONArray result, String auditContent,
|
||||
AuditContent9PersonnelConstants.AuditSubContent subContent) {
|
||||
AuditContent9PersonnelConstants.AuditSubContent subContent, JSONArray ext) {
|
||||
|
||||
if (result == null || result.isEmpty()) {
|
||||
JSONArray defaultResult = new JSONArray();
|
||||
@@ -320,41 +516,48 @@ public class AuditContent9PersonnelServiceImpl extends AbstractAuditContentServi
|
||||
defaultResult.add(defaultItem);
|
||||
return defaultResult;
|
||||
}
|
||||
|
||||
|
||||
// 检查是否是福利费列支范围检查子项
|
||||
String extSubContent = AuditContent9PersonnelConstants.AUDIT_SUB_CONTENTS.get(AuditContent9PersonnelConstants.AUDIT_CONTENTS[2]).get(1).getAuditTarget();
|
||||
if (ext.size() > 0 && subContent.getAuditTarget().equals(extSubContent)) {
|
||||
// 将扩展数据添加到结果中
|
||||
JSONObject firstItem = result.getJSONObject(0);
|
||||
firstItem.put("ext", ext);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关键词相关性比较器
|
||||
*/
|
||||
private int keywordRelevanceComparator(String content1, String content2) {
|
||||
int score1 = calculateKeywordScore(content1);
|
||||
int score2 = calculateKeywordScore(content2);
|
||||
return Integer.compare(score2, score1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算关键词得分
|
||||
*/
|
||||
private int calculateKeywordScore(String content) {
|
||||
int score = 0;
|
||||
for (Map.Entry<String, Integer> entry : AuditContent9PersonnelConstants.KEYWORD_WEIGHTS.entrySet()) {
|
||||
if (content.contains(entry.getKey())) {
|
||||
score += entry.getValue();
|
||||
}
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取不同来源的限制条数
|
||||
*/
|
||||
private int getLimitBySourceType(String sourceType) {
|
||||
switch (sourceType) {
|
||||
case "enterprise": return 80;
|
||||
case "regulation": return 60;
|
||||
case "auditCase": return 40;
|
||||
default: return 30;
|
||||
}
|
||||
}
|
||||
// /**
|
||||
// * 关键词相关性比较器
|
||||
// */
|
||||
// private int keywordRelevanceComparator(String content1, String content2) {
|
||||
// int score1 = calculateKeywordScore(content1);
|
||||
// int score2 = calculateKeywordScore(content2);
|
||||
// return Integer.compare(score2, score1);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 计算关键词得分
|
||||
// */
|
||||
// private int calculateKeywordScore(String content) {
|
||||
// int score = 0;
|
||||
// for (Map.Entry<String, Integer> entry : AuditContent9PersonnelConstants.KEYWORD_WEIGHTS.entrySet()) {
|
||||
// if (content.contains(entry.getKey())) {
|
||||
// score += entry.getValue();
|
||||
// }
|
||||
// }
|
||||
// return score;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取不同来源的限制条数
|
||||
// */
|
||||
// private int getLimitBySourceType(String sourceType) {
|
||||
// switch (sourceType) {
|
||||
// case "enterprise": return 80;
|
||||
// case "regulation": return 60;
|
||||
// case "auditCase": return 40;
|
||||
// default: return 30;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package com.gxwebsoft.ai.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gxwebsoft.ai.dto.AuditEvidenceRequest;
|
||||
import com.gxwebsoft.ai.service.AuditEvidenceService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AuditEvidenceServiceImpl implements AuditEvidenceService {
|
||||
|
||||
// Dify工作流配置
|
||||
private static final String DIFY_WORKFLOW_URL = "http://1.14.159.185:8180/v1/workflows/run";
|
||||
private static final String DIFY_WORKFLOW_TOKEN = "Bearer app-DEhvF537rQfy6MvH9KeBKUVj";
|
||||
|
||||
@Override
|
||||
public JSONObject generateAuditEvidence(AuditEvidenceRequest request) {
|
||||
log.info("开始生成审计取证单 - 用户: {}, 项目: {}", request.getUserName(), request.getProjectName());
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
// 1. 构建完整上下文
|
||||
String knowledgeContext = buildCompleteContext(request);
|
||||
|
||||
// 2. 构建工作流请求
|
||||
JSONObject requestBody = buildWorkflowRequest(knowledgeContext, request.getUserName());
|
||||
|
||||
// 3. 调用Dify工作流
|
||||
JSONObject result = callWorkflow(requestBody, "审计取证单");
|
||||
|
||||
// 4. 添加处理时间等信息
|
||||
result.put("success", true);
|
||||
result.put("processing_time", (System.currentTimeMillis() - startTime) + "ms");
|
||||
result.put("generated_time", new Date().toString());
|
||||
|
||||
log.info("审计取证单生成成功 - 处理时间: {}ms", (System.currentTimeMillis() - startTime));
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("生成审计取证单失败", e);
|
||||
return buildErrorResponse("生成审计取证单失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建完整上下文
|
||||
*/
|
||||
private String buildCompleteContext(AuditEvidenceRequest request) {
|
||||
StringBuilder context = new StringBuilder();
|
||||
|
||||
// 1. 审计取证单生成要求
|
||||
context.append("## 审计取证单生成要求\n");
|
||||
context.append("请基于以下信息生成一份完整的审计取证单。审计取证单是审计工作中的重要文书,用于记录审计发现的问题、事实依据、处理建议等。\n\n");
|
||||
|
||||
// 2. 输出格式要求
|
||||
context.append("## 输出格式要求\n");
|
||||
context.append("请严格按照以下JSON格式输出,不要包含任何额外文本:\n");
|
||||
context.append("{\n");
|
||||
// context.append(" \"caseIndex\": \"案件编号,如审计-[2024]-001\",\n");
|
||||
// context.append(" \"projectName\": \"具体项目名称\",\n");
|
||||
context.append(" \"auditedTarget\": \"被审计单位或个人\",\n");
|
||||
context.append(" \"auditMatter\": \"审计事项描述\",\n");
|
||||
context.append(" \"summaryTitle\": \"核心问题标题\",\n");
|
||||
context.append(" \"auditRecord\": \"客观的审计核查事实记录,包括时间、地点、主体、行为、数据等\",\n");
|
||||
context.append(" \"auditFinding\": \"审计发现的具体问题、性质及影响\",\n");
|
||||
context.append(" \"evidenceBasis\": \"定性依据,引用法规、制度或合同条款\",\n");
|
||||
context.append(" \"handling\": \"拟采取的处理措施\",\n");
|
||||
context.append(" \"suggestion\": \"改进或整改建议\",\n");
|
||||
context.append(" \"attachment\": \"列示随附的证明材料(只出现材料中文名称,不要出现FileId,不要出现网站链接)\",\n");
|
||||
context.append(" \"auditors\": \"审计人员姓名\",\n");
|
||||
context.append(" \"compileDate\": \"编制日期,格式:YYYY-MM-DD\",\n");
|
||||
context.append(" \"providerOpinion\": \"证据提供单位或个人意见\",\n");
|
||||
context.append(" \"providerDate\": \"证据提供日期,格式:YYYY-MM-DD\",\n");
|
||||
context.append(" \"attachmentPages\": \"附件页数,如:2\",\n");
|
||||
context.append(" \"feedbackDeadline\": \"反馈期限,如:5个工作日内\"\n");
|
||||
context.append("}\n");
|
||||
|
||||
// 3. 字段内容要求
|
||||
context.append("## 字段内容具体要求\n");
|
||||
context.append("1. **审计记录**:客观记录审计核查的具体事实,包括时间、地点、主体、行为、数据等,避免主观评价。\n");
|
||||
context.append("2. **审计发现**:基于审计记录提出具体问题性质,先事实后定性,避免夸大。\n");
|
||||
context.append("3. **定性依据**:必须引用具体的法规、制度或合同条款,格式如:根据《XX法》第XX条规定...\n");
|
||||
context.append("4. **处理措施**:针对发现的问题提出具体的处理措施,要有可操作性。\n");
|
||||
context.append("5. **建议**:针对问题根源提出改进或整改建议,要具体可行。\n");
|
||||
context.append("6. **日期**:所有日期必须使用YYYY-MM-DD格式。\n\n");
|
||||
|
||||
// 4. 用户提供的基础信息
|
||||
if (StrUtil.isNotBlank(request.getProjectName()) ||
|
||||
StrUtil.isNotBlank(request.getAuditedTarget()) ||
|
||||
StrUtil.isNotBlank(request.getAuditMatter())) {
|
||||
|
||||
context.append("## 用户提供的基础信息\n");
|
||||
|
||||
if (StrUtil.isNotBlank(request.getProjectName())) {
|
||||
context.append("- 项目名称:").append(request.getProjectName()).append("\n");
|
||||
}
|
||||
if (StrUtil.isNotBlank(request.getAuditedTarget())) {
|
||||
context.append("- 被审计单位/个人:").append(request.getAuditedTarget()).append("\n");
|
||||
}
|
||||
if (StrUtil.isNotBlank(request.getAuditMatter())) {
|
||||
context.append("- 审计事项:").append(request.getAuditMatter()).append("\n");
|
||||
}
|
||||
if (StrUtil.isNotBlank(request.getSummaryTitle())) {
|
||||
context.append("- 标题:").append(request.getSummaryTitle()).append("\n");
|
||||
}
|
||||
context.append("\n");
|
||||
}
|
||||
|
||||
// 5. 历史内容(如果存在)
|
||||
if (StrUtil.isNotBlank(request.getHistory())) {
|
||||
context.append("## 历史内容参考\n");
|
||||
context.append("以下是与本次审计相关的历史内容,请参考但不完全照搬:\n");
|
||||
context.append(request.getHistory()).append("\n\n");
|
||||
}
|
||||
|
||||
// 6. 额外要求
|
||||
context.append("## 额外要求\n");
|
||||
context.append("1. 内容必须真实、准确、完整,符合审计文书规范\n");
|
||||
context.append("2. 语言表述要严谨、客观、规范\n");
|
||||
context.append("3. 各字段内容要相互衔接,逻辑清晰\n");
|
||||
context.append("4. 如果某些信息不明确,请根据审计常规进行合理补充\n");
|
||||
|
||||
log.debug("构建的上下文长度: {}", context.length());
|
||||
return context.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建工作流请求
|
||||
*/
|
||||
private JSONObject buildWorkflowRequest(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);
|
||||
requestBody.put("timeout", 300); // 5分钟超时
|
||||
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用工作流
|
||||
*/
|
||||
private JSONObject callWorkflow(JSONObject requestBody, String workflowName) {
|
||||
try {
|
||||
log.info("调用{}工作流,请求体长度: {}", workflowName, requestBody.toString().length());
|
||||
|
||||
String result = HttpUtil.createPost(DIFY_WORKFLOW_URL)
|
||||
.header("Authorization", DIFY_WORKFLOW_TOKEN)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(requestBody.toString())
|
||||
.timeout(5 * 60 * 1000) // 5分钟
|
||||
.execute()
|
||||
.body();
|
||||
|
||||
log.info("{}工作流返回结果长度: {}", workflowName, result.length());
|
||||
|
||||
JSONObject jsonResponse = JSONObject.parseObject(result);
|
||||
|
||||
// 提取输出文本
|
||||
String outputText = jsonResponse.getJSONObject("data")
|
||||
.getJSONObject("outputs")
|
||||
.getString("result");
|
||||
|
||||
if (StrUtil.isBlank(outputText)) {
|
||||
log.warn("{}工作流返回结果为空", workflowName);
|
||||
return new JSONObject();
|
||||
}
|
||||
|
||||
// 解析为JSON对象
|
||||
JSONObject auditEvidence = JSONObject.parseObject(outputText);
|
||||
|
||||
log.info("成功解析{}工作流返回数据", workflowName);
|
||||
return auditEvidence;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("调用{}工作流失败", workflowName, e);
|
||||
throw new RuntimeException("调用" + workflowName + "工作流失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建错误响应
|
||||
*/
|
||||
private JSONObject buildErrorResponse(String errorMessage) {
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("success", false);
|
||||
result.put("error", errorMessage);
|
||||
result.put("timestamp", System.currentTimeMillis());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ 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.ai.util.AiCloudKnowledgeBaseUtil;
|
||||
import com.gxwebsoft.pwl.entity.PwlProjectLibrary;
|
||||
import com.gxwebsoft.pwl.service.PwlProjectLibraryService;
|
||||
|
||||
@@ -319,7 +319,7 @@ public class AuditReportServiceImpl implements AuditReportService {
|
||||
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
RetrieveResponse resp = KnowledgeBaseUtil.retrieveIndex(client, workspaceId, kbId, query);
|
||||
RetrieveResponse resp = AiCloudKnowledgeBaseUtil.retrieveIndex(client, workspaceId, kbId, query);
|
||||
|
||||
if (resp.getBody() != null && resp.getBody().getData() != null
|
||||
&& resp.getBody().getData().getNodes() != null) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.gxwebsoft.ai.service.impl;
|
||||
|
||||
import com.aliyun.bailian20231229.Client;
|
||||
import com.aliyun.bailian20231229.models.AddFileResponse;
|
||||
import com.aliyun.bailian20231229.models.CreateIndexResponse;
|
||||
import com.aliyun.bailian20231229.models.DeleteIndexDocumentResponse;
|
||||
import com.aliyun.bailian20231229.models.DeleteIndexResponse;
|
||||
@@ -13,9 +14,8 @@ import com.gxwebsoft.ai.constants.KnowledgeBaseConstants;
|
||||
import com.gxwebsoft.ai.dto.KnowledgeBaseRequest;
|
||||
import com.gxwebsoft.ai.factory.KnowledgeBaseClientFactory;
|
||||
import com.gxwebsoft.ai.service.KnowledgeBaseService;
|
||||
import com.gxwebsoft.ai.util.AiCloudDataCenterUtil;
|
||||
import com.gxwebsoft.ai.util.AiCloudKnowledgeBaseUtil;
|
||||
import com.gxwebsoft.ai.util.KnowledgeBaseUploader;
|
||||
import com.gxwebsoft.ai.util.KnowledgeBaseUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
@@ -24,6 +24,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
@@ -56,7 +57,7 @@ public class KnowledgeBaseServiceImpl implements KnowledgeBaseService {
|
||||
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
RetrieveResponse resp = KnowledgeBaseUtil.retrieveIndex(client, workspaceId, indexId, searchQuery);
|
||||
RetrieveResponse resp = AiCloudKnowledgeBaseUtil.retrieveIndex(client, workspaceId, indexId, searchQuery);
|
||||
for (RetrieveResponseBodyDataNodes node : resp.getBody().getData().getNodes()) {
|
||||
result.add(node.getText());
|
||||
if (result.size() >= searchTopK) {
|
||||
@@ -79,7 +80,7 @@ public class KnowledgeBaseServiceImpl implements KnowledgeBaseService {
|
||||
}
|
||||
|
||||
Client client = clientFactory.createClient();
|
||||
CreateIndexResponse indexResponse = KnowledgeBaseUtil.createIndex(client, workspaceId, companyCode, companyName);
|
||||
CreateIndexResponse indexResponse = AiCloudKnowledgeBaseUtil.createIndex(client, workspaceId, companyCode, companyName);
|
||||
return indexResponse.getBody().getData().getId();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("创建知识库失败: " + e.getMessage(), e);
|
||||
@@ -97,7 +98,7 @@ public class KnowledgeBaseServiceImpl implements KnowledgeBaseService {
|
||||
String workspaceId = config.getWorkspaceId();
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
ListIndicesResponse indicesResponse = KnowledgeBaseUtil.listIndices(client, workspaceId);
|
||||
ListIndicesResponse indicesResponse = AiCloudKnowledgeBaseUtil.listIndices(client, workspaceId);
|
||||
|
||||
return indicesResponse.getBody().getData().getIndices().stream()
|
||||
.anyMatch(index -> companyCode.equals(index.getName()));
|
||||
@@ -111,7 +112,7 @@ public class KnowledgeBaseServiceImpl implements KnowledgeBaseService {
|
||||
String workspaceId = config.getWorkspaceId();
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
ListIndicesResponse indicesResponse = KnowledgeBaseUtil.listIndices(client, workspaceId);
|
||||
ListIndicesResponse indicesResponse = AiCloudKnowledgeBaseUtil.listIndices(client, workspaceId);
|
||||
|
||||
return indicesResponse.getBody().getData().getIndices().stream()
|
||||
.filter(index -> companyCode.equals(index.getName()))
|
||||
@@ -129,7 +130,7 @@ public class KnowledgeBaseServiceImpl implements KnowledgeBaseService {
|
||||
String workspaceId = config.getWorkspaceId();
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
ListIndexDocumentsResponse indexDocumentsResponse = KnowledgeBaseUtil.listIndexDocuments(client, workspaceId, kbId, pageSize, pageNumber);
|
||||
ListIndexDocumentsResponse indexDocumentsResponse = AiCloudKnowledgeBaseUtil.listIndexDocuments(client, workspaceId, kbId, pageSize, pageNumber);
|
||||
ret.put("data", indexDocumentsResponse.getBody().getData().getDocuments());
|
||||
ret.put("total", indexDocumentsResponse.getBody().getData().getTotalCount());
|
||||
} catch (Exception e) {
|
||||
@@ -143,7 +144,7 @@ public class KnowledgeBaseServiceImpl implements KnowledgeBaseService {
|
||||
String workspaceId = config.getWorkspaceId();
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
DeleteIndexResponse indexDocumentResponse = KnowledgeBaseUtil.deleteIndex(client, workspaceId, kbId);
|
||||
DeleteIndexResponse indexDocumentResponse = AiCloudKnowledgeBaseUtil.deleteIndex(client, workspaceId, kbId);
|
||||
return indexDocumentResponse.getBody().getSuccess();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("删除知识库失败: " + e.getMessage(), e);
|
||||
@@ -156,7 +157,10 @@ public class KnowledgeBaseServiceImpl implements KnowledgeBaseService {
|
||||
List<String> ids = StrUtil.splitTrim(fileIds, ",");
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
DeleteIndexDocumentResponse indexDocumentResponse = KnowledgeBaseUtil.deleteIndexDocument(client, workspaceId, kbId, ids);
|
||||
DeleteIndexDocumentResponse indexDocumentResponse = AiCloudKnowledgeBaseUtil.deleteIndexDocument(client, workspaceId, kbId, ids);
|
||||
for(String id : ids) {
|
||||
AiCloudKnowledgeBaseUtil.deleteAppDocument(client, workspaceId, id);
|
||||
}
|
||||
return indexDocumentResponse.getBody().getSuccess();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("删除知识库下的文档失败: " + e.getMessage(), e);
|
||||
@@ -164,17 +168,24 @@ public class KnowledgeBaseServiceImpl implements KnowledgeBaseService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean uploadDocuments(String kbId, MultipartFile[] files) {
|
||||
public List<String> uploadDocuments(String kbId, MultipartFile[] files) {
|
||||
String workspaceId = config.getWorkspaceId();
|
||||
int count = files.length;
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
List<String> fileIds = KnowledgeBaseUploader.uploadDocuments(client, workspaceId, kbId, files);
|
||||
//上传切片完成后删除原文档(释放云空间)
|
||||
for(String fileId : fileIds) {
|
||||
KnowledgeBaseUtil.deleteAppDocument(client, workspaceId, fileId);
|
||||
|
||||
List<String> fileIds = new ArrayList<>();
|
||||
for(MultipartFile file : files) {
|
||||
AddFileResponse addFileResponse = AiCloudDataCenterUtil.uploadFile(client, workspaceId, "cate_28190570c20043d692897701d4547401_10377381", file);
|
||||
String fileId = addFileResponse.getBody().getData().getFileId();
|
||||
fileIds.add(fileId);
|
||||
}
|
||||
return !fileIds.isEmpty() && fileIds.size() == count;
|
||||
// List<String> fileIds = AiCloudKnowledgeBaseUtil.uploadDocuments(client, workspaceId, kbId, files);
|
||||
//上传切片完成后删除原文档(释放云空间)
|
||||
// for(String fileId : fileIds) {
|
||||
// AiCloudKnowledgeBaseUtil.deleteAppDocument(client, workspaceId, fileId);
|
||||
// }
|
||||
return fileIds;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("上传文档到知识库失败: " + e.getMessage(), e);
|
||||
}
|
||||
@@ -182,25 +193,28 @@ public class KnowledgeBaseServiceImpl implements KnowledgeBaseService {
|
||||
|
||||
@Async
|
||||
@Override
|
||||
public void submitDocuments(String kbId, String fileId) {
|
||||
public boolean submitDocuments(String kbId, String fileId) {
|
||||
String workspaceId = config.getWorkspaceId();
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
AiCloudKnowledgeBaseUtil.submitIndexAddDocumentsJob(client, workspaceId, kbId, fileId);
|
||||
boolean result = AiCloudKnowledgeBaseUtil.submitIndexAddDocumentsJob(client, workspaceId, kbId, fileId).getBody().getSuccess();
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("添加文档到知识库失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submitDocuments(String kbId, List<String> fileIds) {
|
||||
public boolean submitDocuments(String kbId, List<String> fileIds) {
|
||||
String workspaceId = config.getWorkspaceId();
|
||||
boolean result = true;
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
AiCloudKnowledgeBaseUtil.submitIndexAddDocumentsJob(client, workspaceId, kbId, fileIds);
|
||||
result = result && AiCloudKnowledgeBaseUtil.submitIndexAddDocumentsJob(client, workspaceId, kbId, fileIds).getBody().getSuccess();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("添加文档到知识库失败: " + e.getMessage(), e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
@@ -261,6 +261,11 @@ public class AiCloudDataCenterUtil {
|
||||
|
||||
String fileId = addFileResponse.getBody().getData().getFileId();
|
||||
waitForFileParsing(client, workspaceId, fileId);
|
||||
|
||||
// fileId的UUID部分做为标签更新 file_df12ed21b7384353bd75868444c516ae_10377381 -> df12ed21b7384353bd75868444c516ae
|
||||
String tag = StrUtil.subBetween(fileId, "_", "_").substring(0, 32);
|
||||
updateFileTag(client, workspaceId, fileId, Arrays.asList(tag));
|
||||
|
||||
return addFileResponse;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package com.gxwebsoft.ai.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.aliyun.bailian20231229.models.CreateIndexResponse;
|
||||
import com.aliyun.bailian20231229.models.DeleteFileResponse;
|
||||
import com.aliyun.bailian20231229.models.DeleteIndexDocumentResponse;
|
||||
@@ -37,7 +39,36 @@ public class AiCloudKnowledgeBaseUtil {
|
||||
RetrieveRequest retrieveRequest = new RetrieveRequest();
|
||||
retrieveRequest.setIndexId(indexId);
|
||||
retrieveRequest.setQuery(query);
|
||||
retrieveRequest.setDenseSimilarityTopK(null);
|
||||
retrieveRequest.setDenseSimilarityTopK(100);
|
||||
retrieveRequest.setSparseSimilarityTopK(100);
|
||||
retrieveRequest.setEnableReranking(false);//开启耗费巨量token
|
||||
RuntimeOptions runtime = new RuntimeOptions();
|
||||
return client.retrieveWithOptions(workspaceId, retrieveRequest, null, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在指定的知识库中检索信息。
|
||||
*
|
||||
* @param client 客户端对象(bailian20231229Client)
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param indexId 知识库ID
|
||||
* @param query 检索查询语句
|
||||
* @param filesIds 指定文件
|
||||
* @return 阿里云百炼服务的响应
|
||||
*/
|
||||
public static RetrieveResponse retrieveIndex(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, String query, List<String> filesIds) throws Exception {
|
||||
RetrieveRequest retrieveRequest = new RetrieveRequest();
|
||||
retrieveRequest.setIndexId(indexId);
|
||||
retrieveRequest.setQuery(query);
|
||||
retrieveRequest.setDenseSimilarityTopK(100);
|
||||
retrieveRequest.setSparseSimilarityTopK(100);
|
||||
retrieveRequest.setEnableReranking(false);//开启耗费巨量token
|
||||
List<Map<String, String>> searchFilters = new ArrayList<>();
|
||||
Map<String, String> searchFiltersTags = new HashMap<>();
|
||||
searchFiltersTags.put("tags", JSON.toJSONString(filesIds));
|
||||
searchFilters.add(searchFiltersTags);
|
||||
retrieveRequest.setSearchFilters(searchFilters);
|
||||
// retrieveRequest.setRerankMinScore(null);
|
||||
RuntimeOptions runtime = new RuntimeOptions();
|
||||
return client.retrieveWithOptions(workspaceId, retrieveRequest, null, runtime);
|
||||
}
|
||||
|
||||
@@ -1,384 +1,384 @@
|
||||
package com.gxwebsoft.ai.util;
|
||||
|
||||
import com.aliyun.bailian20231229.models.*;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 创建知识库
|
||||
* @author GIIT-YC
|
||||
*
|
||||
*/
|
||||
public class KnowledgeBaseCreate {
|
||||
|
||||
String ALIBABA_CLOUD_ACCESS_KEY_ID = "LTAI5tD5YRKuxWz6Eg7qrM4P";
|
||||
String ALIBABA_CLOUD_ACCESS_KEY_SECRET = "bO8TBDXflOwbtSKimPpG8XrJnyzgTk";
|
||||
String WORKSPACE_ID = "llm-4pf5auwewoz34zqu";
|
||||
|
||||
/**
|
||||
* 检查并提示设置必要的环境变量。
|
||||
*
|
||||
* @return true 如果所有必需的环境变量都已设置,否则 false
|
||||
*/
|
||||
public static boolean checkEnvironmentVariables() {
|
||||
Map<String, String> requiredVars = new HashMap<>();
|
||||
requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_ID", "阿里云访问密钥ID");
|
||||
requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "阿里云访问密钥密码");
|
||||
requiredVars.put("WORKSPACE_ID", "阿里云百炼业务空间ID");
|
||||
|
||||
List<String> missingVars = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : requiredVars.entrySet()) {
|
||||
String value = System.getenv(entry.getKey());
|
||||
if (value == null || value.isEmpty()) {
|
||||
missingVars.add(entry.getKey());
|
||||
System.out.println("错误:请设置 " + entry.getKey() + " 环境变量 (" + entry.getValue() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
return missingVars.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算文档的MD5值。
|
||||
*
|
||||
* @param filePath 文档本地路径
|
||||
* @return 文档的MD5值
|
||||
* @throws Exception 如果计算过程中发生错误
|
||||
*/
|
||||
public static String calculateMD5(String filePath) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
try (FileInputStream fis = new FileInputStream(filePath)) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
md.update(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : md.digest()) {
|
||||
sb.append(String.format("%02x", b & 0xff));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文档大小(以字节为单位)。
|
||||
*
|
||||
* @param filePath 文档本地路径
|
||||
* @return 文档大小(以字节为单位)
|
||||
*/
|
||||
public static String getFileSize(String filePath) {
|
||||
File file = new File(filePath);
|
||||
long fileSize = file.length();
|
||||
return String.valueOf(fileSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化客户端(Client)。
|
||||
*
|
||||
* @return 配置好的客户端对象
|
||||
*/
|
||||
public static com.aliyun.bailian20231229.Client createClient() throws Exception {
|
||||
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
|
||||
.setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
|
||||
.setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
|
||||
// 下方接入地址以公有云的公网接入地址为例,可按需更换接入地址。
|
||||
config.endpoint = "bailian.cn-beijing.aliyuncs.com";
|
||||
return new com.aliyun.bailian20231229.Client(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请文档上传租约。
|
||||
*
|
||||
* @param client 客户端对象
|
||||
* @param categoryId 类目ID
|
||||
* @param fileName 文档名称
|
||||
* @param fileMd5 文档的MD5值
|
||||
* @param fileSize 文档大小(以字节为单位)
|
||||
* @param workspaceId 业务空间ID
|
||||
* @return 阿里云百炼服务的响应对象
|
||||
*/
|
||||
public static ApplyFileUploadLeaseResponse applyLease(com.aliyun.bailian20231229.Client client, String categoryId,
|
||||
String fileName, String fileMd5, String fileSize, String workspaceId) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.bailian20231229.models.ApplyFileUploadLeaseRequest applyFileUploadLeaseRequest = new com.aliyun.bailian20231229.models.ApplyFileUploadLeaseRequest();
|
||||
applyFileUploadLeaseRequest.setFileName(fileName);
|
||||
applyFileUploadLeaseRequest.setMd5(fileMd5);
|
||||
applyFileUploadLeaseRequest.setSizeInBytes(fileSize);
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
ApplyFileUploadLeaseResponse applyFileUploadLeaseResponse = null;
|
||||
applyFileUploadLeaseResponse = client.applyFileUploadLeaseWithOptions(categoryId, workspaceId,
|
||||
applyFileUploadLeaseRequest, headers, runtime);
|
||||
return applyFileUploadLeaseResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文档到临时存储。
|
||||
*
|
||||
* @param preSignedUrl 上传租约中的 URL
|
||||
* @param headers 上传请求的头部
|
||||
* @param filePath 文档本地路径
|
||||
* @throws Exception 如果上传过程中发生错误
|
||||
*/
|
||||
public static void uploadFile(String preSignedUrl, Map<String, String> headers, String filePath) throws Exception {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists() || !file.isFile()) {
|
||||
throw new IllegalArgumentException("文件不存在或不是普通文件: " + filePath);
|
||||
}
|
||||
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
URL url = new URL(preSignedUrl);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("PUT");
|
||||
conn.setDoOutput(true);
|
||||
|
||||
// 设置上传请求头
|
||||
conn.setRequestProperty("X-bailian-extra", headers.get("X-bailian-extra"));
|
||||
conn.setRequestProperty("Content-Type", headers.get("Content-Type"));
|
||||
|
||||
// 分块读取并上传文档
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
conn.getOutputStream().write(buffer, 0, bytesRead);
|
||||
}
|
||||
|
||||
int responseCode = conn.getResponseCode();
|
||||
if (responseCode != 200) {
|
||||
throw new RuntimeException("上传失败: " + responseCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文档添加到类目中。
|
||||
*
|
||||
* @param client 客户端对象
|
||||
* @param leaseId 租约ID
|
||||
* @param parser 用于文档的解析器
|
||||
* @param categoryId 类目ID
|
||||
* @param workspaceId 业务空间ID
|
||||
* @return 阿里云百炼服务的响应对象
|
||||
*/
|
||||
public static AddFileResponse addFile(com.aliyun.bailian20231229.Client client, String leaseId, String parser,
|
||||
String categoryId, String workspaceId) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.bailian20231229.models.AddFileRequest addFileRequest = new com.aliyun.bailian20231229.models.AddFileRequest();
|
||||
addFileRequest.setLeaseId(leaseId);
|
||||
addFileRequest.setParser(parser);
|
||||
addFileRequest.setCategoryId(categoryId);
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
return client.addFileWithOptions(workspaceId, addFileRequest, headers, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询文档的基本信息。
|
||||
*
|
||||
* @param client 客户端对象
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param fileId 文档ID
|
||||
* @return 阿里云百炼服务的响应对象
|
||||
*/
|
||||
public static DescribeFileResponse describeFile(com.aliyun.bailian20231229.Client client, String workspaceId,
|
||||
String fileId) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
return client.describeFileWithOptions(workspaceId, fileId, headers, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在阿里云百炼服务中创建知识库(初始化)。
|
||||
*
|
||||
* @param client 客户端对象
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param fileId 文档ID
|
||||
* @param name 知识库名称
|
||||
* @param structureType 知识库的数据类型
|
||||
* @param sourceType 应用数据的数据类型,支持类目类型和文档类型
|
||||
* @param sinkType 知识库的向量存储类型
|
||||
* @return 阿里云百炼服务的响应对象
|
||||
*/
|
||||
public static CreateIndexResponse createIndex(com.aliyun.bailian20231229.Client client, String workspaceId,
|
||||
String fileId, String name, String structureType, String sourceType, String sinkType) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.bailian20231229.models.CreateIndexRequest createIndexRequest = new com.aliyun.bailian20231229.models.CreateIndexRequest();
|
||||
createIndexRequest.setStructureType(structureType);
|
||||
createIndexRequest.setName(name);
|
||||
createIndexRequest.setSourceType(sourceType);
|
||||
createIndexRequest.setSinkType(sinkType);
|
||||
createIndexRequest.setDocumentIds(Collections.singletonList(fileId));
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
return client.createIndexWithOptions(workspaceId, createIndexRequest, headers, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向阿里云百炼服务提交索引任务。
|
||||
*
|
||||
* @param client 客户端对象
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param indexId 知识库ID
|
||||
* @return 阿里云百炼服务的响应对象
|
||||
*/
|
||||
public static SubmitIndexJobResponse submitIndex(com.aliyun.bailian20231229.Client client, String workspaceId,
|
||||
String indexId) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.bailian20231229.models.SubmitIndexJobRequest submitIndexJobRequest = new com.aliyun.bailian20231229.models.SubmitIndexJobRequest();
|
||||
submitIndexJobRequest.setIndexId(indexId);
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
return client.submitIndexJobWithOptions(workspaceId, submitIndexJobRequest, headers, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询索引任务状态。
|
||||
*
|
||||
* @param client 客户端对象
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param jobId 任务ID
|
||||
* @param indexId 知识库ID
|
||||
* @return 阿里云百炼服务的响应对象
|
||||
*/
|
||||
public static GetIndexJobStatusResponse getIndexJobStatus(com.aliyun.bailian20231229.Client client,
|
||||
String workspaceId, String jobId, String indexId) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.bailian20231229.models.GetIndexJobStatusRequest getIndexJobStatusRequest = new com.aliyun.bailian20231229.models.GetIndexJobStatusRequest();
|
||||
getIndexJobStatusRequest.setIndexId(indexId);
|
||||
getIndexJobStatusRequest.setJobId(jobId);
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
GetIndexJobStatusResponse getIndexJobStatusResponse = null;
|
||||
getIndexJobStatusResponse = client.getIndexJobStatusWithOptions(workspaceId, getIndexJobStatusRequest, headers,
|
||||
runtime);
|
||||
return getIndexJobStatusResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用阿里云百炼服务创建知识库。
|
||||
*
|
||||
* @param filePath 文档本地路径
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param name 知识库名称
|
||||
* @return 如果成功,返回知识库ID;否则返回 null
|
||||
*/
|
||||
public static String createKnowledgeBase(String filePath, String workspaceId, String name) {
|
||||
// 设置默认值
|
||||
String categoryId = "default";
|
||||
String parser = "DASHSCOPE_DOCMIND";
|
||||
String sourceType = "DATA_CENTER_FILE";
|
||||
String structureType = "unstructured";
|
||||
String sinkType = "DEFAULT";
|
||||
try {
|
||||
// 步骤1:初始化客户端(Client)
|
||||
System.out.println("步骤1:初始化Client");
|
||||
com.aliyun.bailian20231229.Client client = createClient();
|
||||
|
||||
// 步骤2:准备文档信息
|
||||
System.out.println("步骤2:准备文档信息");
|
||||
String fileName = new File(filePath).getName();
|
||||
String fileMd5 = calculateMD5(filePath);
|
||||
String fileSize = getFileSize(filePath);
|
||||
|
||||
// 步骤3:申请上传租约
|
||||
System.out.println("步骤3:向阿里云百炼申请上传租约");
|
||||
ApplyFileUploadLeaseResponse leaseResponse = applyLease(client, categoryId, fileName, fileMd5, fileSize,
|
||||
workspaceId);
|
||||
String leaseId = leaseResponse.getBody().getData().getFileUploadLeaseId();
|
||||
String uploadUrl = leaseResponse.getBody().getData().getParam().getUrl();
|
||||
Object uploadHeaders = leaseResponse.getBody().getData().getParam().getHeaders();
|
||||
|
||||
// 步骤4:上传文档
|
||||
System.out.println("步骤4:上传文档到阿里云百炼");
|
||||
// 请自行安装jackson-databind
|
||||
// 将上一步的uploadHeaders转换为Map(Key-Value形式)
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
Map<String, String> uploadHeadersMap = (Map<String, String>) mapper
|
||||
.readValue(mapper.writeValueAsString(uploadHeaders), Map.class);
|
||||
uploadFile(uploadUrl, uploadHeadersMap, filePath);
|
||||
|
||||
// 步骤5:将文档添加到服务器
|
||||
System.out.println("步骤5:将文档添加到阿里云百炼服务器");
|
||||
AddFileResponse addResponse = addFile(client, leaseId, parser, categoryId, workspaceId);
|
||||
String fileId = addResponse.getBody().getData().getFileId();
|
||||
|
||||
// 步骤6:检查文档状态
|
||||
System.out.println("步骤6:检查阿里云百炼中的文档状态");
|
||||
while (true) {
|
||||
DescribeFileResponse describeResponse = describeFile(client, workspaceId, fileId);
|
||||
String status = describeResponse.getBody().getData().getStatus();
|
||||
System.out.println("当前文档状态:" + status);
|
||||
|
||||
if (status.equals("INIT")) {
|
||||
System.out.println("文档待解析,请稍候...");
|
||||
} else if (status.equals("PARSING")) {
|
||||
System.out.println("文档解析中,请稍候...");
|
||||
} else if (status.equals("PARSE_SUCCESS")) {
|
||||
System.out.println("文档解析完成!");
|
||||
break;
|
||||
} else {
|
||||
System.out.println("未知的文档状态:" + status + ",请联系技术支持。");
|
||||
return null;
|
||||
}
|
||||
TimeUnit.SECONDS.sleep(5);
|
||||
}
|
||||
|
||||
// 步骤7:初始化知识库
|
||||
System.out.println("步骤7:在阿里云百炼中创建知识库");
|
||||
CreateIndexResponse indexResponse = createIndex(client, workspaceId, fileId, name, structureType,
|
||||
sourceType, sinkType);
|
||||
String indexId = indexResponse.getBody().getData().getId();
|
||||
|
||||
// 步骤8:提交索引任务
|
||||
System.out.println("步骤8:向阿里云百炼提交索引任务");
|
||||
SubmitIndexJobResponse submitResponse = submitIndex(client, workspaceId, indexId);
|
||||
String jobId = submitResponse.getBody().getData().getId();
|
||||
|
||||
// 步骤9:获取索引任务状态
|
||||
System.out.println("步骤9:获取阿里云百炼索引任务状态");
|
||||
while (true) {
|
||||
GetIndexJobStatusResponse getStatusResponse = getIndexJobStatus(client, workspaceId, jobId, indexId);
|
||||
String status = getStatusResponse.getBody().getData().getStatus();
|
||||
System.out.println("当前索引任务状态:" + status);
|
||||
|
||||
if (status.equals("COMPLETED")) {
|
||||
break;
|
||||
}
|
||||
TimeUnit.SECONDS.sleep(5);
|
||||
}
|
||||
|
||||
System.out.println("阿里云百炼知识库创建成功!");
|
||||
return indexId;
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("发生错误:" + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数。
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
if (!checkEnvironmentVariables()) {
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.print("请输入您需要上传文档的实际本地路径(以Linux为例:/xxx/xxx/阿里云百炼系列手机产品介绍.docx):");
|
||||
String filePath = scanner.nextLine();
|
||||
|
||||
System.out.print("请为您的知识库输入一个名称:");
|
||||
String kbName = scanner.nextLine();
|
||||
|
||||
String workspaceId = System.getenv("WORKSPACE_ID");
|
||||
String result = createKnowledgeBase(filePath, workspaceId, kbName);
|
||||
if (result != null) {
|
||||
System.out.println("知识库ID: " + result);
|
||||
}
|
||||
}
|
||||
}
|
||||
//package com.gxwebsoft.ai.util;
|
||||
//
|
||||
//import com.aliyun.bailian20231229.models.*;
|
||||
//import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
//
|
||||
//import java.io.File;
|
||||
//import java.io.FileInputStream;
|
||||
//import java.net.HttpURLConnection;
|
||||
//import java.net.URL;
|
||||
//import java.security.MessageDigest;
|
||||
//import java.util.*;
|
||||
//import java.util.concurrent.TimeUnit;
|
||||
//
|
||||
///**
|
||||
// * 创建知识库
|
||||
// * @author GIIT-YC
|
||||
// *
|
||||
// */
|
||||
//public class KnowledgeBaseCreate {
|
||||
//
|
||||
// String ALIBABA_CLOUD_ACCESS_KEY_ID = "LTAI5tD5YRKuxWz6Eg7qrM4P";
|
||||
// String ALIBABA_CLOUD_ACCESS_KEY_SECRET = "bO8TBDXflOwbtSKimPpG8XrJnyzgTk";
|
||||
// String WORKSPACE_ID = "llm-4pf5auwewoz34zqu";
|
||||
//
|
||||
// /**
|
||||
// * 检查并提示设置必要的环境变量。
|
||||
// *
|
||||
// * @return true 如果所有必需的环境变量都已设置,否则 false
|
||||
// */
|
||||
// public static boolean checkEnvironmentVariables() {
|
||||
// Map<String, String> requiredVars = new HashMap<>();
|
||||
// requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_ID", "阿里云访问密钥ID");
|
||||
// requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "阿里云访问密钥密码");
|
||||
// requiredVars.put("WORKSPACE_ID", "阿里云百炼业务空间ID");
|
||||
//
|
||||
// List<String> missingVars = new ArrayList<>();
|
||||
// for (Map.Entry<String, String> entry : requiredVars.entrySet()) {
|
||||
// String value = System.getenv(entry.getKey());
|
||||
// if (value == null || value.isEmpty()) {
|
||||
// missingVars.add(entry.getKey());
|
||||
// System.out.println("错误:请设置 " + entry.getKey() + " 环境变量 (" + entry.getValue() + ")");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return missingVars.isEmpty();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 计算文档的MD5值。
|
||||
// *
|
||||
// * @param filePath 文档本地路径
|
||||
// * @return 文档的MD5值
|
||||
// * @throws Exception 如果计算过程中发生错误
|
||||
// */
|
||||
// public static String calculateMD5(String filePath) throws Exception {
|
||||
// MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
// try (FileInputStream fis = new FileInputStream(filePath)) {
|
||||
// byte[] buffer = new byte[4096];
|
||||
// int bytesRead;
|
||||
// while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
// md.update(buffer, 0, bytesRead);
|
||||
// }
|
||||
// }
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
// for (byte b : md.digest()) {
|
||||
// sb.append(String.format("%02x", b & 0xff));
|
||||
// }
|
||||
// return sb.toString();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取文档大小(以字节为单位)。
|
||||
// *
|
||||
// * @param filePath 文档本地路径
|
||||
// * @return 文档大小(以字节为单位)
|
||||
// */
|
||||
// public static String getFileSize(String filePath) {
|
||||
// File file = new File(filePath);
|
||||
// long fileSize = file.length();
|
||||
// return String.valueOf(fileSize);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 初始化客户端(Client)。
|
||||
// *
|
||||
// * @return 配置好的客户端对象
|
||||
// */
|
||||
// public static com.aliyun.bailian20231229.Client createClient() throws Exception {
|
||||
// com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
|
||||
// .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
|
||||
// .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
|
||||
// // 下方接入地址以公有云的公网接入地址为例,可按需更换接入地址。
|
||||
// config.endpoint = "bailian.cn-beijing.aliyuncs.com";
|
||||
// return new com.aliyun.bailian20231229.Client(config);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 申请文档上传租约。
|
||||
// *
|
||||
// * @param client 客户端对象
|
||||
// * @param categoryId 类目ID
|
||||
// * @param fileName 文档名称
|
||||
// * @param fileMd5 文档的MD5值
|
||||
// * @param fileSize 文档大小(以字节为单位)
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @return 阿里云百炼服务的响应对象
|
||||
// */
|
||||
// public static ApplyFileUploadLeaseResponse applyLease(com.aliyun.bailian20231229.Client client, String categoryId,
|
||||
// String fileName, String fileMd5, String fileSize, String workspaceId) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.bailian20231229.models.ApplyFileUploadLeaseRequest applyFileUploadLeaseRequest = new com.aliyun.bailian20231229.models.ApplyFileUploadLeaseRequest();
|
||||
// applyFileUploadLeaseRequest.setFileName(fileName);
|
||||
// applyFileUploadLeaseRequest.setMd5(fileMd5);
|
||||
// applyFileUploadLeaseRequest.setSizeInBytes(fileSize);
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// ApplyFileUploadLeaseResponse applyFileUploadLeaseResponse = null;
|
||||
// applyFileUploadLeaseResponse = client.applyFileUploadLeaseWithOptions(categoryId, workspaceId,
|
||||
// applyFileUploadLeaseRequest, headers, runtime);
|
||||
// return applyFileUploadLeaseResponse;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 上传文档到临时存储。
|
||||
// *
|
||||
// * @param preSignedUrl 上传租约中的 URL
|
||||
// * @param headers 上传请求的头部
|
||||
// * @param filePath 文档本地路径
|
||||
// * @throws Exception 如果上传过程中发生错误
|
||||
// */
|
||||
// public static void uploadFile(String preSignedUrl, Map<String, String> headers, String filePath) throws Exception {
|
||||
// File file = new File(filePath);
|
||||
// if (!file.exists() || !file.isFile()) {
|
||||
// throw new IllegalArgumentException("文件不存在或不是普通文件: " + filePath);
|
||||
// }
|
||||
//
|
||||
// try (FileInputStream fis = new FileInputStream(file)) {
|
||||
// URL url = new URL(preSignedUrl);
|
||||
// HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
// conn.setRequestMethod("PUT");
|
||||
// conn.setDoOutput(true);
|
||||
//
|
||||
// // 设置上传请求头
|
||||
// conn.setRequestProperty("X-bailian-extra", headers.get("X-bailian-extra"));
|
||||
// conn.setRequestProperty("Content-Type", headers.get("Content-Type"));
|
||||
//
|
||||
// // 分块读取并上传文档
|
||||
// byte[] buffer = new byte[4096];
|
||||
// int bytesRead;
|
||||
// while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
// conn.getOutputStream().write(buffer, 0, bytesRead);
|
||||
// }
|
||||
//
|
||||
// int responseCode = conn.getResponseCode();
|
||||
// if (responseCode != 200) {
|
||||
// throw new RuntimeException("上传失败: " + responseCode);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 将文档添加到类目中。
|
||||
// *
|
||||
// * @param client 客户端对象
|
||||
// * @param leaseId 租约ID
|
||||
// * @param parser 用于文档的解析器
|
||||
// * @param categoryId 类目ID
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @return 阿里云百炼服务的响应对象
|
||||
// */
|
||||
// public static AddFileResponse addFile(com.aliyun.bailian20231229.Client client, String leaseId, String parser,
|
||||
// String categoryId, String workspaceId) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.bailian20231229.models.AddFileRequest addFileRequest = new com.aliyun.bailian20231229.models.AddFileRequest();
|
||||
// addFileRequest.setLeaseId(leaseId);
|
||||
// addFileRequest.setParser(parser);
|
||||
// addFileRequest.setCategoryId(categoryId);
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// return client.addFileWithOptions(workspaceId, addFileRequest, headers, runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 查询文档的基本信息。
|
||||
// *
|
||||
// * @param client 客户端对象
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param fileId 文档ID
|
||||
// * @return 阿里云百炼服务的响应对象
|
||||
// */
|
||||
// public static DescribeFileResponse describeFile(com.aliyun.bailian20231229.Client client, String workspaceId,
|
||||
// String fileId) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// return client.describeFileWithOptions(workspaceId, fileId, headers, runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 在阿里云百炼服务中创建知识库(初始化)。
|
||||
// *
|
||||
// * @param client 客户端对象
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param fileId 文档ID
|
||||
// * @param name 知识库名称
|
||||
// * @param structureType 知识库的数据类型
|
||||
// * @param sourceType 应用数据的数据类型,支持类目类型和文档类型
|
||||
// * @param sinkType 知识库的向量存储类型
|
||||
// * @return 阿里云百炼服务的响应对象
|
||||
// */
|
||||
// public static CreateIndexResponse createIndex(com.aliyun.bailian20231229.Client client, String workspaceId,
|
||||
// String fileId, String name, String structureType, String sourceType, String sinkType) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.bailian20231229.models.CreateIndexRequest createIndexRequest = new com.aliyun.bailian20231229.models.CreateIndexRequest();
|
||||
// createIndexRequest.setStructureType(structureType);
|
||||
// createIndexRequest.setName(name);
|
||||
// createIndexRequest.setSourceType(sourceType);
|
||||
// createIndexRequest.setSinkType(sinkType);
|
||||
// createIndexRequest.setDocumentIds(Collections.singletonList(fileId));
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// return client.createIndexWithOptions(workspaceId, createIndexRequest, headers, runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 向阿里云百炼服务提交索引任务。
|
||||
// *
|
||||
// * @param client 客户端对象
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param indexId 知识库ID
|
||||
// * @return 阿里云百炼服务的响应对象
|
||||
// */
|
||||
// public static SubmitIndexJobResponse submitIndex(com.aliyun.bailian20231229.Client client, String workspaceId,
|
||||
// String indexId) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.bailian20231229.models.SubmitIndexJobRequest submitIndexJobRequest = new com.aliyun.bailian20231229.models.SubmitIndexJobRequest();
|
||||
// submitIndexJobRequest.setIndexId(indexId);
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// return client.submitIndexJobWithOptions(workspaceId, submitIndexJobRequest, headers, runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 查询索引任务状态。
|
||||
// *
|
||||
// * @param client 客户端对象
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param jobId 任务ID
|
||||
// * @param indexId 知识库ID
|
||||
// * @return 阿里云百炼服务的响应对象
|
||||
// */
|
||||
// public static GetIndexJobStatusResponse getIndexJobStatus(com.aliyun.bailian20231229.Client client,
|
||||
// String workspaceId, String jobId, String indexId) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.bailian20231229.models.GetIndexJobStatusRequest getIndexJobStatusRequest = new com.aliyun.bailian20231229.models.GetIndexJobStatusRequest();
|
||||
// getIndexJobStatusRequest.setIndexId(indexId);
|
||||
// getIndexJobStatusRequest.setJobId(jobId);
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// GetIndexJobStatusResponse getIndexJobStatusResponse = null;
|
||||
// getIndexJobStatusResponse = client.getIndexJobStatusWithOptions(workspaceId, getIndexJobStatusRequest, headers,
|
||||
// runtime);
|
||||
// return getIndexJobStatusResponse;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 使用阿里云百炼服务创建知识库。
|
||||
// *
|
||||
// * @param filePath 文档本地路径
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param name 知识库名称
|
||||
// * @return 如果成功,返回知识库ID;否则返回 null
|
||||
// */
|
||||
// public static String createKnowledgeBase(String filePath, String workspaceId, String name) {
|
||||
// // 设置默认值
|
||||
// String categoryId = "default";
|
||||
// String parser = "DASHSCOPE_DOCMIND";
|
||||
// String sourceType = "DATA_CENTER_FILE";
|
||||
// String structureType = "unstructured";
|
||||
// String sinkType = "DEFAULT";
|
||||
// try {
|
||||
// // 步骤1:初始化客户端(Client)
|
||||
// System.out.println("步骤1:初始化Client");
|
||||
// com.aliyun.bailian20231229.Client client = createClient();
|
||||
//
|
||||
// // 步骤2:准备文档信息
|
||||
// System.out.println("步骤2:准备文档信息");
|
||||
// String fileName = new File(filePath).getName();
|
||||
// String fileMd5 = calculateMD5(filePath);
|
||||
// String fileSize = getFileSize(filePath);
|
||||
//
|
||||
// // 步骤3:申请上传租约
|
||||
// System.out.println("步骤3:向阿里云百炼申请上传租约");
|
||||
// ApplyFileUploadLeaseResponse leaseResponse = applyLease(client, categoryId, fileName, fileMd5, fileSize,
|
||||
// workspaceId);
|
||||
// String leaseId = leaseResponse.getBody().getData().getFileUploadLeaseId();
|
||||
// String uploadUrl = leaseResponse.getBody().getData().getParam().getUrl();
|
||||
// Object uploadHeaders = leaseResponse.getBody().getData().getParam().getHeaders();
|
||||
//
|
||||
// // 步骤4:上传文档
|
||||
// System.out.println("步骤4:上传文档到阿里云百炼");
|
||||
// // 请自行安装jackson-databind
|
||||
// // 将上一步的uploadHeaders转换为Map(Key-Value形式)
|
||||
// ObjectMapper mapper = new ObjectMapper();
|
||||
// Map<String, String> uploadHeadersMap = (Map<String, String>) mapper
|
||||
// .readValue(mapper.writeValueAsString(uploadHeaders), Map.class);
|
||||
// uploadFile(uploadUrl, uploadHeadersMap, filePath);
|
||||
//
|
||||
// // 步骤5:将文档添加到服务器
|
||||
// System.out.println("步骤5:将文档添加到阿里云百炼服务器");
|
||||
// AddFileResponse addResponse = addFile(client, leaseId, parser, categoryId, workspaceId);
|
||||
// String fileId = addResponse.getBody().getData().getFileId();
|
||||
//
|
||||
// // 步骤6:检查文档状态
|
||||
// System.out.println("步骤6:检查阿里云百炼中的文档状态");
|
||||
// while (true) {
|
||||
// DescribeFileResponse describeResponse = describeFile(client, workspaceId, fileId);
|
||||
// String status = describeResponse.getBody().getData().getStatus();
|
||||
// System.out.println("当前文档状态:" + status);
|
||||
//
|
||||
// if (status.equals("INIT")) {
|
||||
// System.out.println("文档待解析,请稍候...");
|
||||
// } else if (status.equals("PARSING")) {
|
||||
// System.out.println("文档解析中,请稍候...");
|
||||
// } else if (status.equals("PARSE_SUCCESS")) {
|
||||
// System.out.println("文档解析完成!");
|
||||
// break;
|
||||
// } else {
|
||||
// System.out.println("未知的文档状态:" + status + ",请联系技术支持。");
|
||||
// return null;
|
||||
// }
|
||||
// TimeUnit.SECONDS.sleep(5);
|
||||
// }
|
||||
//
|
||||
// // 步骤7:初始化知识库
|
||||
// System.out.println("步骤7:在阿里云百炼中创建知识库");
|
||||
// CreateIndexResponse indexResponse = createIndex(client, workspaceId, fileId, name, structureType,
|
||||
// sourceType, sinkType);
|
||||
// String indexId = indexResponse.getBody().getData().getId();
|
||||
//
|
||||
// // 步骤8:提交索引任务
|
||||
// System.out.println("步骤8:向阿里云百炼提交索引任务");
|
||||
// SubmitIndexJobResponse submitResponse = submitIndex(client, workspaceId, indexId);
|
||||
// String jobId = submitResponse.getBody().getData().getId();
|
||||
//
|
||||
// // 步骤9:获取索引任务状态
|
||||
// System.out.println("步骤9:获取阿里云百炼索引任务状态");
|
||||
// while (true) {
|
||||
// GetIndexJobStatusResponse getStatusResponse = getIndexJobStatus(client, workspaceId, jobId, indexId);
|
||||
// String status = getStatusResponse.getBody().getData().getStatus();
|
||||
// System.out.println("当前索引任务状态:" + status);
|
||||
//
|
||||
// if (status.equals("COMPLETED")) {
|
||||
// break;
|
||||
// }
|
||||
// TimeUnit.SECONDS.sleep(5);
|
||||
// }
|
||||
//
|
||||
// System.out.println("阿里云百炼知识库创建成功!");
|
||||
// return indexId;
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// System.out.println("发生错误:" + e.getMessage());
|
||||
// e.printStackTrace();
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 主函数。
|
||||
// */
|
||||
// public static void main(String[] args) {
|
||||
// Scanner scanner = new Scanner(System.in);
|
||||
// if (!checkEnvironmentVariables()) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// System.out.print("请输入您需要上传文档的实际本地路径(以Linux为例:/xxx/xxx/阿里云百炼系列手机产品介绍.docx):");
|
||||
// String filePath = scanner.nextLine();
|
||||
//
|
||||
// System.out.print("请为您的知识库输入一个名称:");
|
||||
// String kbName = scanner.nextLine();
|
||||
//
|
||||
// String workspaceId = System.getenv("WORKSPACE_ID");
|
||||
// String result = createKnowledgeBase(filePath, workspaceId, kbName);
|
||||
// if (result != null) {
|
||||
// System.out.println("知识库ID: " + result);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -1,145 +1,145 @@
|
||||
package com.gxwebsoft.ai.util;
|
||||
|
||||
import com.aliyun.bailian20231229.models.DeleteIndexResponse;
|
||||
import com.aliyun.bailian20231229.models.ListIndicesResponse;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 管理知识库
|
||||
* @author GIIT-YC
|
||||
*
|
||||
*/
|
||||
public class KnowledgeBaseManage {
|
||||
|
||||
String ALIBABA_CLOUD_ACCESS_KEY_ID = "LTAI5tD5YRKuxWz6Eg7qrM4P";
|
||||
String ALIBABA_CLOUD_ACCESS_KEY_SECRET = "bO8TBDXflOwbtSKimPpG8XrJnyzgTk";
|
||||
String WORKSPACE_ID = "llm-4pf5auwewoz34zqu";
|
||||
|
||||
/**
|
||||
* 检查并提示设置必要的环境变量。
|
||||
*
|
||||
* @return true 如果所有必需的环境变量都已设置,否则 false
|
||||
*/
|
||||
public static boolean checkEnvironmentVariables() {
|
||||
Map<String, String> requiredVars = new HashMap<>();
|
||||
requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_ID", "阿里云访问密钥ID");
|
||||
requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "阿里云访问密钥密码");
|
||||
requiredVars.put("WORKSPACE_ID", "阿里云百炼业务空间ID");
|
||||
|
||||
List<String> missingVars = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : requiredVars.entrySet()) {
|
||||
String value = System.getenv(entry.getKey());
|
||||
if (value == null || value.isEmpty()) {
|
||||
missingVars.add(entry.getKey());
|
||||
System.out.println("错误:请设置 " + entry.getKey() + " 环境变量 (" + entry.getValue() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
return missingVars.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建并配置客户端(Client)
|
||||
*
|
||||
* @return 配置好的客户端(Client)
|
||||
*/
|
||||
public static com.aliyun.bailian20231229.Client createClient() throws Exception {
|
||||
com.aliyun.credentials.Client credential = new com.aliyun.credentials.Client();
|
||||
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
|
||||
.setCredential(credential);
|
||||
// 下方接入地址以公有云的公网接入地址为例,可按需更换接入地址。
|
||||
config.endpoint = "bailian.cn-beijing.aliyuncs.com";
|
||||
return new com.aliyun.bailian20231229.Client(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定业务空间下一个或多个知识库的详细信息
|
||||
*
|
||||
* @param client 客户端(Client)
|
||||
* @param workspaceId 业务空间ID
|
||||
* @return 阿里云百炼服务的响应
|
||||
*/
|
||||
public static ListIndicesResponse listIndices(com.aliyun.bailian20231229.Client client, String workspaceId)
|
||||
throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.bailian20231229.models.ListIndicesRequest listIndicesRequest = new com.aliyun.bailian20231229.models.ListIndicesRequest();
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
return client.listIndicesWithOptions(workspaceId, listIndicesRequest, headers, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 永久性删除指定的知识库
|
||||
*
|
||||
* @param client 客户端(Client)
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param indexId 知识库ID
|
||||
* @return 阿里云百炼服务的响应
|
||||
*/
|
||||
public static DeleteIndexResponse deleteIndex(com.aliyun.bailian20231229.Client client, String workspaceId,
|
||||
String indexId) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.bailian20231229.models.DeleteIndexRequest deleteIndexRequest = new com.aliyun.bailian20231229.models.DeleteIndexRequest();
|
||||
deleteIndexRequest.setIndexId(indexId);
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
return client.deleteIndexWithOptions(workspaceId, deleteIndexRequest, headers, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
if (!checkEnvironmentVariables()) {
|
||||
System.out.println("环境变量校验未通过。");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
System.out.print("请选择要执行的操作:\n1. 查看知识库\n2. 删除知识库\n请输入选项(1或2):");
|
||||
String startOption = scanner.nextLine();
|
||||
com.aliyun.bailian20231229.Client client = createClient();
|
||||
if (startOption.equals("1")) {
|
||||
// 查看知识库
|
||||
System.out.println("\n执行查看知识库");
|
||||
String workspaceId = System.getenv("WORKSPACE_ID");
|
||||
ListIndicesResponse response = listIndices(client, workspaceId);
|
||||
// 请自行安装jackson-databind。将响应转换为 JSON 字符串
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String result = mapper.writeValueAsString(response.getBody().getData());
|
||||
System.out.println(result);
|
||||
} else if (startOption.equals("2")) {
|
||||
System.out.println("\n执行删除知识库");
|
||||
String workspaceId = System.getenv("WORKSPACE_ID");
|
||||
System.out.print("请输入知识库ID:"); // 即 CreateIndex 接口返回的 Data.Id,您也可以在阿里云百炼控制台的知识库页面获取。
|
||||
String indexId = scanner.nextLine();
|
||||
// 删除前二次确认
|
||||
boolean confirm = false;
|
||||
while (!confirm) {
|
||||
System.out.print("您确定要永久性删除该知识库 " + indexId + " 吗?(y/n): ");
|
||||
String input = scanner.nextLine().trim().toLowerCase();
|
||||
if (input.equals("y")) {
|
||||
confirm = true;
|
||||
} else if (input.equals("n")) {
|
||||
System.out.println("已取消删除操作。");
|
||||
return;
|
||||
} else {
|
||||
System.out.println("无效输入,请输入 y 或 n。");
|
||||
}
|
||||
}
|
||||
DeleteIndexResponse resp = deleteIndex(client, workspaceId, indexId);
|
||||
if (resp.getBody().getStatus().equals("200")) {
|
||||
System.out.println("知识库" + indexId + "删除成功!");
|
||||
} else {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
System.out.println("发生错误:" + mapper.writeValueAsString(resp.getBody()));
|
||||
}
|
||||
} else {
|
||||
System.out.println("无效的选项,程序退出。");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("发生错误:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
//package com.gxwebsoft.ai.util;
|
||||
//
|
||||
//import com.aliyun.bailian20231229.models.DeleteIndexResponse;
|
||||
//import com.aliyun.bailian20231229.models.ListIndicesResponse;
|
||||
//import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
//
|
||||
//import java.util.*;
|
||||
//
|
||||
///**
|
||||
// * 管理知识库
|
||||
// * @author GIIT-YC
|
||||
// *
|
||||
// */
|
||||
//public class KnowledgeBaseManage {
|
||||
//
|
||||
// String ALIBABA_CLOUD_ACCESS_KEY_ID = "LTAI5tD5YRKuxWz6Eg7qrM4P";
|
||||
// String ALIBABA_CLOUD_ACCESS_KEY_SECRET = "bO8TBDXflOwbtSKimPpG8XrJnyzgTk";
|
||||
// String WORKSPACE_ID = "llm-4pf5auwewoz34zqu";
|
||||
//
|
||||
// /**
|
||||
// * 检查并提示设置必要的环境变量。
|
||||
// *
|
||||
// * @return true 如果所有必需的环境变量都已设置,否则 false
|
||||
// */
|
||||
// public static boolean checkEnvironmentVariables() {
|
||||
// Map<String, String> requiredVars = new HashMap<>();
|
||||
// requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_ID", "阿里云访问密钥ID");
|
||||
// requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "阿里云访问密钥密码");
|
||||
// requiredVars.put("WORKSPACE_ID", "阿里云百炼业务空间ID");
|
||||
//
|
||||
// List<String> missingVars = new ArrayList<>();
|
||||
// for (Map.Entry<String, String> entry : requiredVars.entrySet()) {
|
||||
// String value = System.getenv(entry.getKey());
|
||||
// if (value == null || value.isEmpty()) {
|
||||
// missingVars.add(entry.getKey());
|
||||
// System.out.println("错误:请设置 " + entry.getKey() + " 环境变量 (" + entry.getValue() + ")");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return missingVars.isEmpty();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 创建并配置客户端(Client)
|
||||
// *
|
||||
// * @return 配置好的客户端(Client)
|
||||
// */
|
||||
// public static com.aliyun.bailian20231229.Client createClient() throws Exception {
|
||||
// com.aliyun.credentials.Client credential = new com.aliyun.credentials.Client();
|
||||
// com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
|
||||
// .setCredential(credential);
|
||||
// // 下方接入地址以公有云的公网接入地址为例,可按需更换接入地址。
|
||||
// config.endpoint = "bailian.cn-beijing.aliyuncs.com";
|
||||
// return new com.aliyun.bailian20231229.Client(config);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取指定业务空间下一个或多个知识库的详细信息
|
||||
// *
|
||||
// * @param client 客户端(Client)
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @return 阿里云百炼服务的响应
|
||||
// */
|
||||
// public static ListIndicesResponse listIndices(com.aliyun.bailian20231229.Client client, String workspaceId)
|
||||
// throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.bailian20231229.models.ListIndicesRequest listIndicesRequest = new com.aliyun.bailian20231229.models.ListIndicesRequest();
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// return client.listIndicesWithOptions(workspaceId, listIndicesRequest, headers, runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 永久性删除指定的知识库
|
||||
// *
|
||||
// * @param client 客户端(Client)
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param indexId 知识库ID
|
||||
// * @return 阿里云百炼服务的响应
|
||||
// */
|
||||
// public static DeleteIndexResponse deleteIndex(com.aliyun.bailian20231229.Client client, String workspaceId,
|
||||
// String indexId) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.bailian20231229.models.DeleteIndexRequest deleteIndexRequest = new com.aliyun.bailian20231229.models.DeleteIndexRequest();
|
||||
// deleteIndexRequest.setIndexId(indexId);
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// return client.deleteIndexWithOptions(workspaceId, deleteIndexRequest, headers, runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 主函数
|
||||
// */
|
||||
// public static void main(String[] args) {
|
||||
// if (!checkEnvironmentVariables()) {
|
||||
// System.out.println("环境变量校验未通过。");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// try {
|
||||
// Scanner scanner = new Scanner(System.in);
|
||||
// System.out.print("请选择要执行的操作:\n1. 查看知识库\n2. 删除知识库\n请输入选项(1或2):");
|
||||
// String startOption = scanner.nextLine();
|
||||
// com.aliyun.bailian20231229.Client client = createClient();
|
||||
// if (startOption.equals("1")) {
|
||||
// // 查看知识库
|
||||
// System.out.println("\n执行查看知识库");
|
||||
// String workspaceId = System.getenv("WORKSPACE_ID");
|
||||
// ListIndicesResponse response = listIndices(client, workspaceId);
|
||||
// // 请自行安装jackson-databind。将响应转换为 JSON 字符串
|
||||
// ObjectMapper mapper = new ObjectMapper();
|
||||
// String result = mapper.writeValueAsString(response.getBody().getData());
|
||||
// System.out.println(result);
|
||||
// } else if (startOption.equals("2")) {
|
||||
// System.out.println("\n执行删除知识库");
|
||||
// String workspaceId = System.getenv("WORKSPACE_ID");
|
||||
// System.out.print("请输入知识库ID:"); // 即 CreateIndex 接口返回的 Data.Id,您也可以在阿里云百炼控制台的知识库页面获取。
|
||||
// String indexId = scanner.nextLine();
|
||||
// // 删除前二次确认
|
||||
// boolean confirm = false;
|
||||
// while (!confirm) {
|
||||
// System.out.print("您确定要永久性删除该知识库 " + indexId + " 吗?(y/n): ");
|
||||
// String input = scanner.nextLine().trim().toLowerCase();
|
||||
// if (input.equals("y")) {
|
||||
// confirm = true;
|
||||
// } else if (input.equals("n")) {
|
||||
// System.out.println("已取消删除操作。");
|
||||
// return;
|
||||
// } else {
|
||||
// System.out.println("无效输入,请输入 y 或 n。");
|
||||
// }
|
||||
// }
|
||||
// DeleteIndexResponse resp = deleteIndex(client, workspaceId, indexId);
|
||||
// if (resp.getBody().getStatus().equals("200")) {
|
||||
// System.out.println("知识库" + indexId + "删除成功!");
|
||||
// } else {
|
||||
// ObjectMapper mapper = new ObjectMapper();
|
||||
// System.out.println("发生错误:" + mapper.writeValueAsString(resp.getBody()));
|
||||
// }
|
||||
// } else {
|
||||
// System.out.println("无效的选项,程序退出。");
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// System.out.println("发生错误:" + e.getMessage());
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -1,110 +1,110 @@
|
||||
package com.gxwebsoft.ai.util;
|
||||
|
||||
import com.aliyun.bailian20231229.models.RetrieveRequest;
|
||||
import com.aliyun.bailian20231229.models.RetrieveResponse;
|
||||
import com.aliyun.teautil.models.RuntimeOptions;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 检索知识库
|
||||
* @author GIIT-YC
|
||||
*
|
||||
*/
|
||||
public class KnowledgeBaseRetrieve {
|
||||
|
||||
static String ALIBABA_CLOUD_ACCESS_KEY_ID = "LTAI5tD5YRKuxWz6Eg7qrM4P";
|
||||
static String ALIBABA_CLOUD_ACCESS_KEY_SECRET = "bO8TBDXflOwbtSKimPpG8XrJnyzgTk";
|
||||
static String WORKSPACE_ID = "llm-4pf5auwewoz34zqu";
|
||||
|
||||
|
||||
/**
|
||||
* 检查并提示设置必要的环境变量。
|
||||
*
|
||||
* @return true 如果所有必需的环境变量都已设置,否则 false
|
||||
*/
|
||||
public static boolean checkEnvironmentVariables() {
|
||||
Map<String, String> requiredVars = new HashMap<>();
|
||||
requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_ID", "阿里云访问密钥ID");
|
||||
requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "阿里云访问密钥密码");
|
||||
requiredVars.put("WORKSPACE_ID", "阿里云百炼业务空间ID");
|
||||
|
||||
List<String> missingVars = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : requiredVars.entrySet()) {
|
||||
String value = System.getenv(entry.getKey());
|
||||
if (value == null || value.isEmpty()) {
|
||||
missingVars.add(entry.getKey());
|
||||
System.out.println("错误:请设置 " + entry.getKey() + " 环境变量 (" + entry.getValue() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
return missingVars.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化客户端(Client)。
|
||||
*
|
||||
* @return 配置好的客户端对象
|
||||
*/
|
||||
public static com.aliyun.bailian20231229.Client createClient() throws Exception {
|
||||
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
|
||||
.setAccessKeyId(ALIBABA_CLOUD_ACCESS_KEY_ID)
|
||||
.setAccessKeySecret(ALIBABA_CLOUD_ACCESS_KEY_SECRET);
|
||||
// 下方接入地址以公有云的公网接入地址为例,可按需更换接入地址。
|
||||
config.endpoint = "bailian.cn-beijing.aliyuncs.com";
|
||||
return new com.aliyun.bailian20231229.Client(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在指定的知识库中检索信息。
|
||||
*
|
||||
* @param client 客户端对象(bailian20231229Client)
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param indexId 知识库ID
|
||||
* @param query 检索查询语句
|
||||
* @return 阿里云百炼服务的响应
|
||||
*/
|
||||
public static RetrieveResponse retrieveIndex(com.aliyun.bailian20231229.Client client, String workspaceId,
|
||||
String indexId, String query) throws Exception {
|
||||
RetrieveRequest retrieveRequest = new RetrieveRequest();
|
||||
retrieveRequest.setIndexId(indexId);
|
||||
retrieveRequest.setQuery(query);
|
||||
retrieveRequest.setDenseSimilarityTopK(null);
|
||||
RuntimeOptions runtime = new RuntimeOptions();
|
||||
return client.retrieveWithOptions(workspaceId, retrieveRequest, null, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用阿里云百炼服务检索知识库。
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// if (!checkEnvironmentVariables()) {
|
||||
// System.out.println("环境变量校验未通过。");
|
||||
// return;
|
||||
//package com.gxwebsoft.ai.util;
|
||||
//
|
||||
//import com.aliyun.bailian20231229.models.RetrieveRequest;
|
||||
//import com.aliyun.bailian20231229.models.RetrieveResponse;
|
||||
//import com.aliyun.teautil.models.RuntimeOptions;
|
||||
//import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
//
|
||||
//import java.util.*;
|
||||
//
|
||||
///**
|
||||
// * 检索知识库
|
||||
// * @author GIIT-YC
|
||||
// *
|
||||
// */
|
||||
//public class KnowledgeBaseRetrieve {
|
||||
//
|
||||
// static String ALIBABA_CLOUD_ACCESS_KEY_ID = "LTAI5tD5YRKuxWz6Eg7qrM4P";
|
||||
// static String ALIBABA_CLOUD_ACCESS_KEY_SECRET = "bO8TBDXflOwbtSKimPpG8XrJnyzgTk";
|
||||
// static String WORKSPACE_ID = "llm-4pf5auwewoz34zqu";
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * 检查并提示设置必要的环境变量。
|
||||
// *
|
||||
// * @return true 如果所有必需的环境变量都已设置,否则 false
|
||||
// */
|
||||
// public static boolean checkEnvironmentVariables() {
|
||||
// Map<String, String> requiredVars = new HashMap<>();
|
||||
// requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_ID", "阿里云访问密钥ID");
|
||||
// requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "阿里云访问密钥密码");
|
||||
// requiredVars.put("WORKSPACE_ID", "阿里云百炼业务空间ID");
|
||||
//
|
||||
// List<String> missingVars = new ArrayList<>();
|
||||
// for (Map.Entry<String, String> entry : requiredVars.entrySet()) {
|
||||
// String value = System.getenv(entry.getKey());
|
||||
// if (value == null || value.isEmpty()) {
|
||||
// missingVars.add(entry.getKey());
|
||||
// System.out.println("错误:请设置 " + entry.getKey() + " 环境变量 (" + entry.getValue() + ")");
|
||||
// }
|
||||
// }
|
||||
|
||||
try {
|
||||
// 步骤1:初始化客户端(Client)
|
||||
System.out.println("步骤1:创建Client");
|
||||
com.aliyun.bailian20231229.Client client = createClient();
|
||||
|
||||
// 步骤2:检索知识库
|
||||
System.out.println("步骤2:检索知识库");
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
System.out.print("请输入知识库ID:"); // 即 CreateIndex 接口返回的 Data.Id,您也可以在阿里云百炼控制台的知识库页面获取。
|
||||
String indexId = scanner.nextLine();
|
||||
System.out.print("请输入检索query:");
|
||||
String query = scanner.nextLine();
|
||||
String workspaceId = WORKSPACE_ID;
|
||||
RetrieveResponse resp = retrieveIndex(client, workspaceId, indexId, query);
|
||||
|
||||
// 请自行安装jackson-databind。将响应体responsebody转换为 JSON 字符串
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String result = mapper.writeValueAsString(resp.getBody());
|
||||
System.out.println(result);
|
||||
} catch (Exception e) {
|
||||
System.out.println("发生错误:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// return missingVars.isEmpty();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 初始化客户端(Client)。
|
||||
// *
|
||||
// * @return 配置好的客户端对象
|
||||
// */
|
||||
// public static com.aliyun.bailian20231229.Client createClient() throws Exception {
|
||||
// com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
|
||||
// .setAccessKeyId(ALIBABA_CLOUD_ACCESS_KEY_ID)
|
||||
// .setAccessKeySecret(ALIBABA_CLOUD_ACCESS_KEY_SECRET);
|
||||
// // 下方接入地址以公有云的公网接入地址为例,可按需更换接入地址。
|
||||
// config.endpoint = "bailian.cn-beijing.aliyuncs.com";
|
||||
// return new com.aliyun.bailian20231229.Client(config);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 在指定的知识库中检索信息。
|
||||
// *
|
||||
// * @param client 客户端对象(bailian20231229Client)
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param indexId 知识库ID
|
||||
// * @param query 检索查询语句
|
||||
// * @return 阿里云百炼服务的响应
|
||||
// */
|
||||
// public static RetrieveResponse retrieveIndex(com.aliyun.bailian20231229.Client client, String workspaceId,
|
||||
// String indexId, String query) throws Exception {
|
||||
// RetrieveRequest retrieveRequest = new RetrieveRequest();
|
||||
// retrieveRequest.setIndexId(indexId);
|
||||
// retrieveRequest.setQuery(query);
|
||||
// retrieveRequest.setDenseSimilarityTopK(null);
|
||||
// RuntimeOptions runtime = new RuntimeOptions();
|
||||
// return client.retrieveWithOptions(workspaceId, retrieveRequest, null, runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 使用阿里云百炼服务检索知识库。
|
||||
// */
|
||||
// public static void main(String[] args) {
|
||||
//// if (!checkEnvironmentVariables()) {
|
||||
//// System.out.println("环境变量校验未通过。");
|
||||
//// return;
|
||||
//// }
|
||||
//
|
||||
// try {
|
||||
// // 步骤1:初始化客户端(Client)
|
||||
// System.out.println("步骤1:创建Client");
|
||||
// com.aliyun.bailian20231229.Client client = createClient();
|
||||
//
|
||||
// // 步骤2:检索知识库
|
||||
// System.out.println("步骤2:检索知识库");
|
||||
// Scanner scanner = new Scanner(System.in);
|
||||
// System.out.print("请输入知识库ID:"); // 即 CreateIndex 接口返回的 Data.Id,您也可以在阿里云百炼控制台的知识库页面获取。
|
||||
// String indexId = scanner.nextLine();
|
||||
// System.out.print("请输入检索query:");
|
||||
// String query = scanner.nextLine();
|
||||
// String workspaceId = WORKSPACE_ID;
|
||||
// RetrieveResponse resp = retrieveIndex(client, workspaceId, indexId, query);
|
||||
//
|
||||
// // 请自行安装jackson-databind。将响应体responsebody转换为 JSON 字符串
|
||||
// ObjectMapper mapper = new ObjectMapper();
|
||||
// String result = mapper.writeValueAsString(resp.getBody());
|
||||
// System.out.println(result);
|
||||
// } catch (Exception e) {
|
||||
// System.out.println("发生错误:" + e.getMessage());
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -1,384 +1,384 @@
|
||||
package com.gxwebsoft.ai.util;
|
||||
|
||||
import com.aliyun.bailian20231229.models.*;
|
||||
import com.aliyun.teautil.models.RuntimeOptions;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 更新知识库
|
||||
* @author GIIT-YC
|
||||
*
|
||||
*/
|
||||
public class KnowledgeBaseUpdate {
|
||||
|
||||
String ALIBABA_CLOUD_ACCESS_KEY_ID = "LTAI5tD5YRKuxWz6Eg7qrM4P";
|
||||
String ALIBABA_CLOUD_ACCESS_KEY_SECRET = "bO8TBDXflOwbtSKimPpG8XrJnyzgTk";
|
||||
String WORKSPACE_ID = "llm-4pf5auwewoz34zqu";
|
||||
|
||||
/**
|
||||
* 检查并提示设置必要的环境变量。
|
||||
*
|
||||
* @return true 如果所有必需的环境变量都已设置,否则 false
|
||||
*/
|
||||
public static boolean checkEnvironmentVariables() {
|
||||
Map<String, String> requiredVars = new HashMap<>();
|
||||
requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_ID", "阿里云访问密钥ID");
|
||||
requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "阿里云访问密钥密码");
|
||||
requiredVars.put("WORKSPACE_ID", "阿里云百炼业务空间ID");
|
||||
|
||||
List<String> missingVars = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : requiredVars.entrySet()) {
|
||||
String value = System.getenv(entry.getKey());
|
||||
if (value == null || value.isEmpty()) {
|
||||
missingVars.add(entry.getKey());
|
||||
System.out.println("错误:请设置 " + entry.getKey() + " 环境变量 (" + entry.getValue() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
return missingVars.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建并配置客户端(Client)
|
||||
*
|
||||
* @return 配置好的客户端(Client)
|
||||
*/
|
||||
public static com.aliyun.bailian20231229.Client createClient() throws Exception {
|
||||
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
|
||||
.setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
|
||||
.setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
|
||||
// 下方接入地址以公有云的公网接入地址为例,可按需更换接入地址。
|
||||
config.endpoint = "bailian.cn-beijing.aliyuncs.com";
|
||||
return new com.aliyun.bailian20231229.Client(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算文档的MD5值
|
||||
*
|
||||
* @param filePath 文档本地路径
|
||||
* @return 文档的MD5值
|
||||
*/
|
||||
public static String calculateMD5(String filePath) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
try (FileInputStream fis = new FileInputStream(filePath)) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
md.update(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : md.digest()) {
|
||||
sb.append(String.format("%02x", b & 0xff));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文档大小(以字节为单位)
|
||||
*
|
||||
* @param filePath 文档本地路径
|
||||
* @return 文档大小(以字节为单位)
|
||||
*/
|
||||
public static String getFileSize(String filePath) {
|
||||
File file = new File(filePath);
|
||||
long fileSize = file.length();
|
||||
return String.valueOf(fileSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请文档上传租约。
|
||||
*
|
||||
* @param client 客户端对象
|
||||
* @param categoryId 类目ID
|
||||
* @param fileName 文档名称
|
||||
* @param fileMd5 文档的MD5值
|
||||
* @param fileSize 文档大小(以字节为单位)
|
||||
* @param workspaceId 业务空间ID
|
||||
* @return 阿里云百炼服务的响应对象
|
||||
*/
|
||||
public static ApplyFileUploadLeaseResponse applyLease(com.aliyun.bailian20231229.Client client, String categoryId,
|
||||
String fileName, String fileMd5, String fileSize, String workspaceId) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.bailian20231229.models.ApplyFileUploadLeaseRequest applyFileUploadLeaseRequest = new com.aliyun.bailian20231229.models.ApplyFileUploadLeaseRequest();
|
||||
applyFileUploadLeaseRequest.setFileName(fileName);
|
||||
applyFileUploadLeaseRequest.setMd5(fileMd5);
|
||||
applyFileUploadLeaseRequest.setSizeInBytes(fileSize);
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
ApplyFileUploadLeaseResponse applyFileUploadLeaseResponse = null;
|
||||
applyFileUploadLeaseResponse = client.applyFileUploadLeaseWithOptions(categoryId, workspaceId,
|
||||
applyFileUploadLeaseRequest, headers, runtime);
|
||||
return applyFileUploadLeaseResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文档到临时存储。
|
||||
*
|
||||
* @param preSignedUrl 上传租约中的 URL
|
||||
* @param headers 上传请求的头部
|
||||
* @param filePath 文档本地路径
|
||||
* @throws Exception 如果上传过程中发生错误
|
||||
*/
|
||||
public static void uploadFile(String preSignedUrl, Map<String, String> headers, String filePath) throws Exception {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists() || !file.isFile()) {
|
||||
throw new IllegalArgumentException("文件不存在或不是普通文件: " + filePath);
|
||||
}
|
||||
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
URL url = new URL(preSignedUrl);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("PUT");
|
||||
conn.setDoOutput(true);
|
||||
|
||||
// 设置上传请求头
|
||||
conn.setRequestProperty("X-bailian-extra", headers.get("X-bailian-extra"));
|
||||
conn.setRequestProperty("Content-Type", headers.get("Content-Type"));
|
||||
|
||||
// 分块读取并上传文档
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
conn.getOutputStream().write(buffer, 0, bytesRead);
|
||||
}
|
||||
|
||||
int responseCode = conn.getResponseCode();
|
||||
if (responseCode != 200) {
|
||||
throw new RuntimeException("上传失败: " + responseCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文档添加到类目中。
|
||||
*
|
||||
* @param client 客户端对象
|
||||
* @param leaseId 租约ID
|
||||
* @param parser 用于文档的解析器
|
||||
* @param categoryId 类目ID
|
||||
* @param workspaceId 业务空间ID
|
||||
* @return 阿里云百炼服务的响应对象
|
||||
*/
|
||||
public static AddFileResponse addFile(com.aliyun.bailian20231229.Client client, String leaseId, String parser,
|
||||
String categoryId, String workspaceId) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.bailian20231229.models.AddFileRequest addFileRequest = new com.aliyun.bailian20231229.models.AddFileRequest();
|
||||
addFileRequest.setLeaseId(leaseId);
|
||||
addFileRequest.setParser(parser);
|
||||
addFileRequest.setCategoryId(categoryId);
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
return client.addFileWithOptions(workspaceId, addFileRequest, headers, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询文档的基本信息。
|
||||
*
|
||||
* @param client 客户端对象
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param fileId 文档ID
|
||||
* @return 阿里云百炼服务的响应对象
|
||||
*/
|
||||
public static DescribeFileResponse describeFile(com.aliyun.bailian20231229.Client client, String workspaceId,
|
||||
String fileId) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
return client.describeFileWithOptions(workspaceId, fileId, headers, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向一个非结构化知识库追加导入已解析的文档
|
||||
*
|
||||
* @param client 客户端(Client)
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param indexId 知识库ID
|
||||
* @param fileId 文档ID
|
||||
* @param sourceType 数据类型
|
||||
* @return 阿里云百炼服务的响应
|
||||
*/
|
||||
public static SubmitIndexAddDocumentsJobResponse submitIndexAddDocumentsJob(
|
||||
com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, String fileId,
|
||||
String sourceType) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
SubmitIndexAddDocumentsJobRequest submitIndexAddDocumentsJobRequest = new SubmitIndexAddDocumentsJobRequest();
|
||||
submitIndexAddDocumentsJobRequest.setIndexId(indexId);
|
||||
submitIndexAddDocumentsJobRequest.setDocumentIds(Collections.singletonList(fileId));
|
||||
submitIndexAddDocumentsJobRequest.setSourceType(sourceType);
|
||||
RuntimeOptions runtime = new RuntimeOptions();
|
||||
return client.submitIndexAddDocumentsJobWithOptions(workspaceId, submitIndexAddDocumentsJobRequest, headers,
|
||||
runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询索引任务状态。
|
||||
*
|
||||
* @param client 客户端对象
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param jobId 任务ID
|
||||
* @param indexId 知识库ID
|
||||
* @return 阿里云百炼服务的响应对象
|
||||
*/
|
||||
public static GetIndexJobStatusResponse getIndexJobStatus(com.aliyun.bailian20231229.Client client,
|
||||
String workspaceId, String jobId, String indexId) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.bailian20231229.models.GetIndexJobStatusRequest getIndexJobStatusRequest = new com.aliyun.bailian20231229.models.GetIndexJobStatusRequest();
|
||||
getIndexJobStatusRequest.setIndexId(indexId);
|
||||
getIndexJobStatusRequest.setJobId(jobId);
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
GetIndexJobStatusResponse getIndexJobStatusResponse = null;
|
||||
getIndexJobStatusResponse = client.getIndexJobStatusWithOptions(workspaceId, getIndexJobStatusRequest, headers,
|
||||
runtime);
|
||||
return getIndexJobStatusResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从指定的非结构化知识库中永久删除一个或多个文档
|
||||
*
|
||||
* @param client 客户端(Client)
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param indexId 知识库ID
|
||||
* @param fileId 文档ID
|
||||
* @return 阿里云百炼服务的响应
|
||||
*/
|
||||
public static DeleteIndexDocumentResponse deleteIndexDocument(com.aliyun.bailian20231229.Client client,
|
||||
String workspaceId, String indexId, String fileId) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
DeleteIndexDocumentRequest deleteIndexDocumentRequest = new DeleteIndexDocumentRequest();
|
||||
deleteIndexDocumentRequest.setIndexId(indexId);
|
||||
deleteIndexDocumentRequest.setDocumentIds(Collections.singletonList(fileId));
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
return client.deleteIndexDocumentWithOptions(workspaceId, deleteIndexDocumentRequest, headers, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用阿里云百炼服务更新知识库
|
||||
*
|
||||
* @param filePath 文档(更新后的)的实际本地路径
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param indexId 需要更新的知识库ID
|
||||
* @param oldFileId 需要更新的文档的FileID
|
||||
* @return 如果成功,返回知识库ID;否则返回 null
|
||||
*/
|
||||
public static String updateKnowledgeBase(String filePath, String workspaceId, String indexId, String oldFileId) {
|
||||
// 设置默认值
|
||||
String categoryId = "default";
|
||||
String parser = "DASHSCOPE_DOCMIND";
|
||||
String sourceType = "DATA_CENTER_FILE";
|
||||
try {
|
||||
// 步骤1:初始化客户端(Client)
|
||||
System.out.println("步骤1:创建Client");
|
||||
com.aliyun.bailian20231229.Client client = createClient();
|
||||
|
||||
// 步骤2:准备文档信息(更新后的文档)
|
||||
System.out.println("步骤2:准备文档信息");
|
||||
String fileName = Paths.get(filePath).getFileName().toString();
|
||||
String fileMd5 = calculateMD5(filePath);
|
||||
String fileSize = getFileSize(filePath);
|
||||
|
||||
// 步骤3:申请上传租约
|
||||
System.out.println("步骤3:向阿里云百炼申请上传租约");
|
||||
ApplyFileUploadLeaseResponse leaseResponse = applyLease(client, categoryId, fileName, fileMd5, fileSize,
|
||||
workspaceId);
|
||||
String leaseId = leaseResponse.getBody().getData().getFileUploadLeaseId();
|
||||
String uploadUrl = leaseResponse.getBody().getData().getParam().getUrl();
|
||||
Object uploadHeaders = leaseResponse.getBody().getData().getParam().getHeaders();
|
||||
|
||||
// 步骤4:上传文档到临时存储
|
||||
System.out.println("步骤4:上传文档到临时存储");
|
||||
// 请自行安装jackson-databind
|
||||
// 将上一步的uploadHeaders转换为Map(Key-Value形式)
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
Map<String, String> uploadHeadersMap = (Map<String, String>) mapper
|
||||
.readValue(mapper.writeValueAsString(uploadHeaders), Map.class);
|
||||
uploadFile(uploadUrl, uploadHeadersMap, filePath);
|
||||
|
||||
// 步骤5:添加文档到类目中
|
||||
System.out.println("步骤5:添加文档到类目中");
|
||||
AddFileResponse addResponse = addFile(client, leaseId, parser, categoryId, workspaceId);
|
||||
String fileId = addResponse.getBody().getData().getFileId();
|
||||
|
||||
// 步骤6:检查更新后的文档状态
|
||||
System.out.println("步骤6:检查阿里云百炼中的文档状态");
|
||||
while (true) {
|
||||
DescribeFileResponse describeResponse = describeFile(client, workspaceId, fileId);
|
||||
String status = describeResponse.getBody().getData().getStatus();
|
||||
System.out.println("当前文档状态:" + status);
|
||||
if ("INIT".equals(status)) {
|
||||
System.out.println("文档待解析,请稍候...");
|
||||
} else if ("PARSING".equals(status)) {
|
||||
System.out.println("文档解析中,请稍候...");
|
||||
} else if ("PARSE_SUCCESS".equals(status)) {
|
||||
System.out.println("文档解析完成!");
|
||||
break;
|
||||
} else {
|
||||
System.out.println("未知的文档状态:" + status + ",请联系技术支持。");
|
||||
return null;
|
||||
}
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
|
||||
// 步骤7:提交追加文档任务
|
||||
System.out.println("步骤7:提交追加文档任务");
|
||||
SubmitIndexAddDocumentsJobResponse indexAddResponse = submitIndexAddDocumentsJob(client, workspaceId,
|
||||
indexId, fileId, sourceType);
|
||||
String jobId = indexAddResponse.getBody().getData().getId();
|
||||
|
||||
// 步骤8:等待追加任务完成
|
||||
System.out.println("步骤8:等待追加任务完成");
|
||||
while (true) {
|
||||
GetIndexJobStatusResponse jobStatusResponse = getIndexJobStatus(client, workspaceId, jobId, indexId);
|
||||
String status = jobStatusResponse.getBody().getData().getStatus();
|
||||
System.out.println("当前索引任务状态:" + status);
|
||||
if ("COMPLETED".equals(status)) {
|
||||
break;
|
||||
}
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
|
||||
// 步骤9:删除旧文档
|
||||
System.out.println("步骤9:删除旧文档");
|
||||
deleteIndexDocument(client, workspaceId, indexId, oldFileId);
|
||||
|
||||
System.out.println("阿里云百炼知识库更新成功!");
|
||||
return indexId;
|
||||
} catch (Exception e) {
|
||||
System.out.println("发生错误:" + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主函数。
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
if (!checkEnvironmentVariables()) {
|
||||
System.out.println("环境变量校验未通过。");
|
||||
return;
|
||||
}
|
||||
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
System.out.print("请输入您需要上传文档(更新后的)的实际本地路径(以Linux为例:/xxx/xxx/阿里云百炼系列手机产品介绍.docx):");
|
||||
String filePath = scanner.nextLine();
|
||||
|
||||
System.out.print("请输入需要更新的知识库ID:"); // 即 CreateIndex 接口返回的 Data.Id,您也可以在阿里云百炼控制台的知识库页面获取。
|
||||
String indexId = scanner.nextLine(); // 即 AddFile 接口返回的 FileId。您也可以在阿里云百炼控制台的应用数据页面,单击文件名称旁的 ID 图标获取。
|
||||
|
||||
System.out.print("请输入需要更新的文档的 FileID:");
|
||||
String oldFileId = scanner.nextLine();
|
||||
|
||||
String workspaceId = System.getenv("WORKSPACE_ID");
|
||||
String result = updateKnowledgeBase(filePath, workspaceId, indexId, oldFileId);
|
||||
if (result != null) {
|
||||
System.out.println("知识库更新成功,返回知识库ID: " + result);
|
||||
} else {
|
||||
System.out.println("知识库更新失败。");
|
||||
}
|
||||
}
|
||||
}
|
||||
//package com.gxwebsoft.ai.util;
|
||||
//
|
||||
//import com.aliyun.bailian20231229.models.*;
|
||||
//import com.aliyun.teautil.models.RuntimeOptions;
|
||||
//import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
//
|
||||
//import java.io.File;
|
||||
//import java.io.FileInputStream;
|
||||
//import java.net.HttpURLConnection;
|
||||
//import java.net.URL;
|
||||
//import java.nio.file.Paths;
|
||||
//import java.security.MessageDigest;
|
||||
//import java.util.*;
|
||||
//
|
||||
///**
|
||||
// * 更新知识库
|
||||
// * @author GIIT-YC
|
||||
// *
|
||||
// */
|
||||
//public class KnowledgeBaseUpdate {
|
||||
//
|
||||
// String ALIBABA_CLOUD_ACCESS_KEY_ID = "LTAI5tD5YRKuxWz6Eg7qrM4P";
|
||||
// String ALIBABA_CLOUD_ACCESS_KEY_SECRET = "bO8TBDXflOwbtSKimPpG8XrJnyzgTk";
|
||||
// String WORKSPACE_ID = "llm-4pf5auwewoz34zqu";
|
||||
//
|
||||
// /**
|
||||
// * 检查并提示设置必要的环境变量。
|
||||
// *
|
||||
// * @return true 如果所有必需的环境变量都已设置,否则 false
|
||||
// */
|
||||
// public static boolean checkEnvironmentVariables() {
|
||||
// Map<String, String> requiredVars = new HashMap<>();
|
||||
// requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_ID", "阿里云访问密钥ID");
|
||||
// requiredVars.put("ALIBABA_CLOUD_ACCESS_KEY_SECRET", "阿里云访问密钥密码");
|
||||
// requiredVars.put("WORKSPACE_ID", "阿里云百炼业务空间ID");
|
||||
//
|
||||
// List<String> missingVars = new ArrayList<>();
|
||||
// for (Map.Entry<String, String> entry : requiredVars.entrySet()) {
|
||||
// String value = System.getenv(entry.getKey());
|
||||
// if (value == null || value.isEmpty()) {
|
||||
// missingVars.add(entry.getKey());
|
||||
// System.out.println("错误:请设置 " + entry.getKey() + " 环境变量 (" + entry.getValue() + ")");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return missingVars.isEmpty();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 创建并配置客户端(Client)
|
||||
// *
|
||||
// * @return 配置好的客户端(Client)
|
||||
// */
|
||||
// public static com.aliyun.bailian20231229.Client createClient() throws Exception {
|
||||
// com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
|
||||
// .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
|
||||
// .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
|
||||
// // 下方接入地址以公有云的公网接入地址为例,可按需更换接入地址。
|
||||
// config.endpoint = "bailian.cn-beijing.aliyuncs.com";
|
||||
// return new com.aliyun.bailian20231229.Client(config);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 计算文档的MD5值
|
||||
// *
|
||||
// * @param filePath 文档本地路径
|
||||
// * @return 文档的MD5值
|
||||
// */
|
||||
// public static String calculateMD5(String filePath) throws Exception {
|
||||
// MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
// try (FileInputStream fis = new FileInputStream(filePath)) {
|
||||
// byte[] buffer = new byte[4096];
|
||||
// int bytesRead;
|
||||
// while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
// md.update(buffer, 0, bytesRead);
|
||||
// }
|
||||
// }
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
// for (byte b : md.digest()) {
|
||||
// sb.append(String.format("%02x", b & 0xff));
|
||||
// }
|
||||
// return sb.toString();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取文档大小(以字节为单位)
|
||||
// *
|
||||
// * @param filePath 文档本地路径
|
||||
// * @return 文档大小(以字节为单位)
|
||||
// */
|
||||
// public static String getFileSize(String filePath) {
|
||||
// File file = new File(filePath);
|
||||
// long fileSize = file.length();
|
||||
// return String.valueOf(fileSize);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 申请文档上传租约。
|
||||
// *
|
||||
// * @param client 客户端对象
|
||||
// * @param categoryId 类目ID
|
||||
// * @param fileName 文档名称
|
||||
// * @param fileMd5 文档的MD5值
|
||||
// * @param fileSize 文档大小(以字节为单位)
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @return 阿里云百炼服务的响应对象
|
||||
// */
|
||||
// public static ApplyFileUploadLeaseResponse applyLease(com.aliyun.bailian20231229.Client client, String categoryId,
|
||||
// String fileName, String fileMd5, String fileSize, String workspaceId) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.bailian20231229.models.ApplyFileUploadLeaseRequest applyFileUploadLeaseRequest = new com.aliyun.bailian20231229.models.ApplyFileUploadLeaseRequest();
|
||||
// applyFileUploadLeaseRequest.setFileName(fileName);
|
||||
// applyFileUploadLeaseRequest.setMd5(fileMd5);
|
||||
// applyFileUploadLeaseRequest.setSizeInBytes(fileSize);
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// ApplyFileUploadLeaseResponse applyFileUploadLeaseResponse = null;
|
||||
// applyFileUploadLeaseResponse = client.applyFileUploadLeaseWithOptions(categoryId, workspaceId,
|
||||
// applyFileUploadLeaseRequest, headers, runtime);
|
||||
// return applyFileUploadLeaseResponse;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 上传文档到临时存储。
|
||||
// *
|
||||
// * @param preSignedUrl 上传租约中的 URL
|
||||
// * @param headers 上传请求的头部
|
||||
// * @param filePath 文档本地路径
|
||||
// * @throws Exception 如果上传过程中发生错误
|
||||
// */
|
||||
// public static void uploadFile(String preSignedUrl, Map<String, String> headers, String filePath) throws Exception {
|
||||
// File file = new File(filePath);
|
||||
// if (!file.exists() || !file.isFile()) {
|
||||
// throw new IllegalArgumentException("文件不存在或不是普通文件: " + filePath);
|
||||
// }
|
||||
//
|
||||
// try (FileInputStream fis = new FileInputStream(file)) {
|
||||
// URL url = new URL(preSignedUrl);
|
||||
// HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
// conn.setRequestMethod("PUT");
|
||||
// conn.setDoOutput(true);
|
||||
//
|
||||
// // 设置上传请求头
|
||||
// conn.setRequestProperty("X-bailian-extra", headers.get("X-bailian-extra"));
|
||||
// conn.setRequestProperty("Content-Type", headers.get("Content-Type"));
|
||||
//
|
||||
// // 分块读取并上传文档
|
||||
// byte[] buffer = new byte[4096];
|
||||
// int bytesRead;
|
||||
// while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
// conn.getOutputStream().write(buffer, 0, bytesRead);
|
||||
// }
|
||||
//
|
||||
// int responseCode = conn.getResponseCode();
|
||||
// if (responseCode != 200) {
|
||||
// throw new RuntimeException("上传失败: " + responseCode);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 将文档添加到类目中。
|
||||
// *
|
||||
// * @param client 客户端对象
|
||||
// * @param leaseId 租约ID
|
||||
// * @param parser 用于文档的解析器
|
||||
// * @param categoryId 类目ID
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @return 阿里云百炼服务的响应对象
|
||||
// */
|
||||
// public static AddFileResponse addFile(com.aliyun.bailian20231229.Client client, String leaseId, String parser,
|
||||
// String categoryId, String workspaceId) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.bailian20231229.models.AddFileRequest addFileRequest = new com.aliyun.bailian20231229.models.AddFileRequest();
|
||||
// addFileRequest.setLeaseId(leaseId);
|
||||
// addFileRequest.setParser(parser);
|
||||
// addFileRequest.setCategoryId(categoryId);
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// return client.addFileWithOptions(workspaceId, addFileRequest, headers, runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 查询文档的基本信息。
|
||||
// *
|
||||
// * @param client 客户端对象
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param fileId 文档ID
|
||||
// * @return 阿里云百炼服务的响应对象
|
||||
// */
|
||||
// public static DescribeFileResponse describeFile(com.aliyun.bailian20231229.Client client, String workspaceId,
|
||||
// String fileId) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// return client.describeFileWithOptions(workspaceId, fileId, headers, runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 向一个非结构化知识库追加导入已解析的文档
|
||||
// *
|
||||
// * @param client 客户端(Client)
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param indexId 知识库ID
|
||||
// * @param fileId 文档ID
|
||||
// * @param sourceType 数据类型
|
||||
// * @return 阿里云百炼服务的响应
|
||||
// */
|
||||
// public static SubmitIndexAddDocumentsJobResponse submitIndexAddDocumentsJob(
|
||||
// com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, String fileId,
|
||||
// String sourceType) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// SubmitIndexAddDocumentsJobRequest submitIndexAddDocumentsJobRequest = new SubmitIndexAddDocumentsJobRequest();
|
||||
// submitIndexAddDocumentsJobRequest.setIndexId(indexId);
|
||||
// submitIndexAddDocumentsJobRequest.setDocumentIds(Collections.singletonList(fileId));
|
||||
// submitIndexAddDocumentsJobRequest.setSourceType(sourceType);
|
||||
// RuntimeOptions runtime = new RuntimeOptions();
|
||||
// return client.submitIndexAddDocumentsJobWithOptions(workspaceId, submitIndexAddDocumentsJobRequest, headers,
|
||||
// runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 查询索引任务状态。
|
||||
// *
|
||||
// * @param client 客户端对象
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param jobId 任务ID
|
||||
// * @param indexId 知识库ID
|
||||
// * @return 阿里云百炼服务的响应对象
|
||||
// */
|
||||
// public static GetIndexJobStatusResponse getIndexJobStatus(com.aliyun.bailian20231229.Client client,
|
||||
// String workspaceId, String jobId, String indexId) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.bailian20231229.models.GetIndexJobStatusRequest getIndexJobStatusRequest = new com.aliyun.bailian20231229.models.GetIndexJobStatusRequest();
|
||||
// getIndexJobStatusRequest.setIndexId(indexId);
|
||||
// getIndexJobStatusRequest.setJobId(jobId);
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// GetIndexJobStatusResponse getIndexJobStatusResponse = null;
|
||||
// getIndexJobStatusResponse = client.getIndexJobStatusWithOptions(workspaceId, getIndexJobStatusRequest, headers,
|
||||
// runtime);
|
||||
// return getIndexJobStatusResponse;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 从指定的非结构化知识库中永久删除一个或多个文档
|
||||
// *
|
||||
// * @param client 客户端(Client)
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param indexId 知识库ID
|
||||
// * @param fileId 文档ID
|
||||
// * @return 阿里云百炼服务的响应
|
||||
// */
|
||||
// public static DeleteIndexDocumentResponse deleteIndexDocument(com.aliyun.bailian20231229.Client client,
|
||||
// String workspaceId, String indexId, String fileId) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// DeleteIndexDocumentRequest deleteIndexDocumentRequest = new DeleteIndexDocumentRequest();
|
||||
// deleteIndexDocumentRequest.setIndexId(indexId);
|
||||
// deleteIndexDocumentRequest.setDocumentIds(Collections.singletonList(fileId));
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// return client.deleteIndexDocumentWithOptions(workspaceId, deleteIndexDocumentRequest, headers, runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 使用阿里云百炼服务更新知识库
|
||||
// *
|
||||
// * @param filePath 文档(更新后的)的实际本地路径
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param indexId 需要更新的知识库ID
|
||||
// * @param oldFileId 需要更新的文档的FileID
|
||||
// * @return 如果成功,返回知识库ID;否则返回 null
|
||||
// */
|
||||
// public static String updateKnowledgeBase(String filePath, String workspaceId, String indexId, String oldFileId) {
|
||||
// // 设置默认值
|
||||
// String categoryId = "default";
|
||||
// String parser = "DASHSCOPE_DOCMIND";
|
||||
// String sourceType = "DATA_CENTER_FILE";
|
||||
// try {
|
||||
// // 步骤1:初始化客户端(Client)
|
||||
// System.out.println("步骤1:创建Client");
|
||||
// com.aliyun.bailian20231229.Client client = createClient();
|
||||
//
|
||||
// // 步骤2:准备文档信息(更新后的文档)
|
||||
// System.out.println("步骤2:准备文档信息");
|
||||
// String fileName = Paths.get(filePath).getFileName().toString();
|
||||
// String fileMd5 = calculateMD5(filePath);
|
||||
// String fileSize = getFileSize(filePath);
|
||||
//
|
||||
// // 步骤3:申请上传租约
|
||||
// System.out.println("步骤3:向阿里云百炼申请上传租约");
|
||||
// ApplyFileUploadLeaseResponse leaseResponse = applyLease(client, categoryId, fileName, fileMd5, fileSize,
|
||||
// workspaceId);
|
||||
// String leaseId = leaseResponse.getBody().getData().getFileUploadLeaseId();
|
||||
// String uploadUrl = leaseResponse.getBody().getData().getParam().getUrl();
|
||||
// Object uploadHeaders = leaseResponse.getBody().getData().getParam().getHeaders();
|
||||
//
|
||||
// // 步骤4:上传文档到临时存储
|
||||
// System.out.println("步骤4:上传文档到临时存储");
|
||||
// // 请自行安装jackson-databind
|
||||
// // 将上一步的uploadHeaders转换为Map(Key-Value形式)
|
||||
// ObjectMapper mapper = new ObjectMapper();
|
||||
// Map<String, String> uploadHeadersMap = (Map<String, String>) mapper
|
||||
// .readValue(mapper.writeValueAsString(uploadHeaders), Map.class);
|
||||
// uploadFile(uploadUrl, uploadHeadersMap, filePath);
|
||||
//
|
||||
// // 步骤5:添加文档到类目中
|
||||
// System.out.println("步骤5:添加文档到类目中");
|
||||
// AddFileResponse addResponse = addFile(client, leaseId, parser, categoryId, workspaceId);
|
||||
// String fileId = addResponse.getBody().getData().getFileId();
|
||||
//
|
||||
// // 步骤6:检查更新后的文档状态
|
||||
// System.out.println("步骤6:检查阿里云百炼中的文档状态");
|
||||
// while (true) {
|
||||
// DescribeFileResponse describeResponse = describeFile(client, workspaceId, fileId);
|
||||
// String status = describeResponse.getBody().getData().getStatus();
|
||||
// System.out.println("当前文档状态:" + status);
|
||||
// if ("INIT".equals(status)) {
|
||||
// System.out.println("文档待解析,请稍候...");
|
||||
// } else if ("PARSING".equals(status)) {
|
||||
// System.out.println("文档解析中,请稍候...");
|
||||
// } else if ("PARSE_SUCCESS".equals(status)) {
|
||||
// System.out.println("文档解析完成!");
|
||||
// break;
|
||||
// } else {
|
||||
// System.out.println("未知的文档状态:" + status + ",请联系技术支持。");
|
||||
// return null;
|
||||
// }
|
||||
// Thread.sleep(5000);
|
||||
// }
|
||||
//
|
||||
// // 步骤7:提交追加文档任务
|
||||
// System.out.println("步骤7:提交追加文档任务");
|
||||
// SubmitIndexAddDocumentsJobResponse indexAddResponse = submitIndexAddDocumentsJob(client, workspaceId,
|
||||
// indexId, fileId, sourceType);
|
||||
// String jobId = indexAddResponse.getBody().getData().getId();
|
||||
//
|
||||
// // 步骤8:等待追加任务完成
|
||||
// System.out.println("步骤8:等待追加任务完成");
|
||||
// while (true) {
|
||||
// GetIndexJobStatusResponse jobStatusResponse = getIndexJobStatus(client, workspaceId, jobId, indexId);
|
||||
// String status = jobStatusResponse.getBody().getData().getStatus();
|
||||
// System.out.println("当前索引任务状态:" + status);
|
||||
// if ("COMPLETED".equals(status)) {
|
||||
// break;
|
||||
// }
|
||||
// Thread.sleep(5000);
|
||||
// }
|
||||
//
|
||||
// // 步骤9:删除旧文档
|
||||
// System.out.println("步骤9:删除旧文档");
|
||||
// deleteIndexDocument(client, workspaceId, indexId, oldFileId);
|
||||
//
|
||||
// System.out.println("阿里云百炼知识库更新成功!");
|
||||
// return indexId;
|
||||
// } catch (Exception e) {
|
||||
// System.out.println("发生错误:" + e.getMessage());
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 主函数。
|
||||
// */
|
||||
// public static void main(String[] args) {
|
||||
// if (!checkEnvironmentVariables()) {
|
||||
// System.out.println("环境变量校验未通过。");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// Scanner scanner = new Scanner(System.in);
|
||||
// System.out.print("请输入您需要上传文档(更新后的)的实际本地路径(以Linux为例:/xxx/xxx/阿里云百炼系列手机产品介绍.docx):");
|
||||
// String filePath = scanner.nextLine();
|
||||
//
|
||||
// System.out.print("请输入需要更新的知识库ID:"); // 即 CreateIndex 接口返回的 Data.Id,您也可以在阿里云百炼控制台的知识库页面获取。
|
||||
// String indexId = scanner.nextLine(); // 即 AddFile 接口返回的 FileId。您也可以在阿里云百炼控制台的应用数据页面,单击文件名称旁的 ID 图标获取。
|
||||
//
|
||||
// System.out.print("请输入需要更新的文档的 FileID:");
|
||||
// String oldFileId = scanner.nextLine();
|
||||
//
|
||||
// String workspaceId = System.getenv("WORKSPACE_ID");
|
||||
// String result = updateKnowledgeBase(filePath, workspaceId, indexId, oldFileId);
|
||||
// if (result != null) {
|
||||
// System.out.println("知识库更新成功,返回知识库ID: " + result);
|
||||
// } else {
|
||||
// System.out.println("知识库更新失败。");
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -1,303 +1,303 @@
|
||||
package com.gxwebsoft.ai.util;
|
||||
|
||||
import com.aliyun.bailian20231229.Client;
|
||||
import com.aliyun.bailian20231229.models.*;
|
||||
import com.aliyun.teautil.models.RuntimeOptions;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.*;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* 知识库上传工具类
|
||||
* @author GIIT-YC
|
||||
*
|
||||
*/
|
||||
public class KnowledgeBaseUploader {
|
||||
|
||||
/**
|
||||
* 上传文档到知识库(直接处理MultipartFile)
|
||||
*
|
||||
* @param client 阿里云客户端
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param indexId 知识库ID
|
||||
* @param file 上传的文件
|
||||
* @return 新文档的FileID,失败返回null
|
||||
*/
|
||||
public static String uploadDocument(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, MultipartFile file) {
|
||||
try {
|
||||
// 准备文档信息
|
||||
String fileName = file.getOriginalFilename();
|
||||
String fileMd5 = calculateMD5(file.getInputStream());
|
||||
String fileSize = String.valueOf(file.getSize());
|
||||
|
||||
// 申请上传租约
|
||||
ApplyFileUploadLeaseRequest leaseRequest = new ApplyFileUploadLeaseRequest()
|
||||
.setFileName(fileName)
|
||||
.setMd5(fileMd5)
|
||||
.setSizeInBytes(fileSize);
|
||||
|
||||
ApplyFileUploadLeaseResponse leaseResponse = client.applyFileUploadLeaseWithOptions(
|
||||
"default", workspaceId, leaseRequest, new HashMap<>(), new RuntimeOptions());
|
||||
|
||||
String leaseId = leaseResponse.getBody().getData().getFileUploadLeaseId();
|
||||
String uploadUrl = leaseResponse.getBody().getData().getParam().getUrl();
|
||||
|
||||
// 上传文件
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
Map<String, String> headers = mapper.readValue(mapper.writeValueAsString(leaseResponse.getBody().getData().getParam().getHeaders()), Map.class);
|
||||
|
||||
uploadFile(uploadUrl, headers, file);
|
||||
|
||||
// 添加文件到类目
|
||||
AddFileRequest addRequest = new AddFileRequest()
|
||||
.setLeaseId(leaseId)
|
||||
.setParser("DASHSCOPE_DOCMIND")
|
||||
.setCategoryId("default");
|
||||
|
||||
AddFileResponse addResponse = client.addFileWithOptions(workspaceId, addRequest, new HashMap<>(), new RuntimeOptions());
|
||||
|
||||
String fileId = addResponse.getBody().getData().getFileId();
|
||||
|
||||
// 等待文件解析完成
|
||||
waitForFileParsing(client, workspaceId, fileId);
|
||||
|
||||
// 添加到知识库
|
||||
SubmitIndexAddDocumentsJobRequest indexRequest = new SubmitIndexAddDocumentsJobRequest()
|
||||
.setIndexId(indexId)
|
||||
.setDocumentIds(Collections.singletonList(fileId))
|
||||
.setSourceType("DATA_CENTER_FILE");
|
||||
|
||||
SubmitIndexAddDocumentsJobResponse indexResponse = client.submitIndexAddDocumentsJobWithOptions(workspaceId, indexRequest, new HashMap<>(), new RuntimeOptions());
|
||||
|
||||
// 等待索引完成
|
||||
waitForIndexJob(client, workspaceId, indexResponse.getBody().getData().getId(), indexId);
|
||||
|
||||
return fileId;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量上传文档到知识库
|
||||
*/
|
||||
public static List<String> uploadDocuments(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, MultipartFile[] files) {
|
||||
List<String> fileIds = new ArrayList<>();
|
||||
for (MultipartFile file : files) {
|
||||
String fileId = uploadDocument(client, workspaceId, indexId, file);
|
||||
if (fileId != null) {
|
||||
fileIds.add(fileId);
|
||||
}
|
||||
}
|
||||
return fileIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文档到知识库
|
||||
*
|
||||
* @param client 阿里云客户端
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param indexId 知识库ID
|
||||
* @param filePath 文档本地路径
|
||||
* @return 新文档的FileID,失败返回null
|
||||
*/
|
||||
public static String uploadDocument(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, String filePath) {
|
||||
try {
|
||||
// 准备文档信息
|
||||
String fileName = Paths.get(filePath).getFileName().toString();
|
||||
String fileMd5 = calculateMD5(filePath);
|
||||
String fileSize = String.valueOf(new File(filePath).length());
|
||||
|
||||
// 申请上传租约
|
||||
ApplyFileUploadLeaseRequest leaseRequest = new ApplyFileUploadLeaseRequest()
|
||||
.setFileName(fileName)
|
||||
.setMd5(fileMd5)
|
||||
.setSizeInBytes(fileSize);
|
||||
|
||||
ApplyFileUploadLeaseResponse leaseResponse = client.applyFileUploadLeaseWithOptions(
|
||||
"default", workspaceId, leaseRequest, new HashMap<>(), new RuntimeOptions());
|
||||
|
||||
String leaseId = leaseResponse.getBody().getData().getFileUploadLeaseId();
|
||||
String uploadUrl = leaseResponse.getBody().getData().getParam().getUrl();
|
||||
|
||||
// 上传文件
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
Map<String, String> headers = mapper.readValue(
|
||||
mapper.writeValueAsString(leaseResponse.getBody().getData().getParam().getHeaders()),
|
||||
Map.class);
|
||||
|
||||
uploadFile(uploadUrl, headers, filePath);
|
||||
|
||||
// 添加文件到类目
|
||||
AddFileRequest addRequest = new AddFileRequest()
|
||||
.setLeaseId(leaseId)
|
||||
.setParser("DASHSCOPE_DOCMIND")
|
||||
.setCategoryId("default");
|
||||
|
||||
AddFileResponse addResponse = client.addFileWithOptions(
|
||||
workspaceId, addRequest, new HashMap<>(), new RuntimeOptions());
|
||||
|
||||
String fileId = addResponse.getBody().getData().getFileId();
|
||||
|
||||
// 等待文件解析完成
|
||||
waitForFileParsing(client, workspaceId, fileId);
|
||||
|
||||
// 添加到知识库
|
||||
SubmitIndexAddDocumentsJobRequest indexRequest = new SubmitIndexAddDocumentsJobRequest()
|
||||
.setIndexId(indexId)
|
||||
.setDocumentIds(Collections.singletonList(fileId))
|
||||
.setSourceType("DATA_CENTER_FILE");
|
||||
|
||||
SubmitIndexAddDocumentsJobResponse indexResponse = client.submitIndexAddDocumentsJobWithOptions(
|
||||
workspaceId, indexRequest, new HashMap<>(), new RuntimeOptions());
|
||||
|
||||
// 等待索引完成
|
||||
waitForIndexJob(client, workspaceId, indexResponse.getBody().getData().getId(), indexId);
|
||||
|
||||
return fileId;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String calculateMD5(String filePath) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
try (FileInputStream fis = new FileInputStream(filePath)) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
md.update(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : md.digest()) {
|
||||
sb.append(String.format("%02x", b & 0xff));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void uploadFile(String preSignedUrl, Map<String, String> headers,
|
||||
String filePath) throws Exception {
|
||||
try (FileInputStream fis = new FileInputStream(filePath)) {
|
||||
HttpURLConnection conn = (HttpURLConnection) new URL(preSignedUrl).openConnection();
|
||||
conn.setRequestMethod("PUT");
|
||||
conn.setDoOutput(true);
|
||||
conn.setRequestProperty("X-bailian-extra", headers.get("X-bailian-extra"));
|
||||
conn.setRequestProperty("Content-Type", headers.get("Content-Type"));
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
conn.getOutputStream().write(buffer, 0, bytesRead);
|
||||
}
|
||||
|
||||
if (conn.getResponseCode() != 200) {
|
||||
throw new RuntimeException("上传失败: " + conn.getResponseCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void waitForFileParsing(com.aliyun.bailian20231229.Client client,
|
||||
String workspaceId, String fileId) throws Exception {
|
||||
while (true) {
|
||||
DescribeFileResponse response = client.describeFileWithOptions(
|
||||
workspaceId, fileId, new HashMap<>(), new RuntimeOptions());
|
||||
|
||||
String status = response.getBody().getData().getStatus();
|
||||
if ("PARSE_SUCCESS".equals(status)) break;
|
||||
if ("PARSE_FAILED".equals(status)) throw new RuntimeException("文档解析失败");
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
}
|
||||
|
||||
private static void waitForIndexJob(com.aliyun.bailian20231229.Client client,
|
||||
String workspaceId, String jobId, String indexId) throws Exception {
|
||||
while (true) {
|
||||
GetIndexJobStatusRequest request = new GetIndexJobStatusRequest()
|
||||
.setIndexId(indexId)
|
||||
.setJobId(jobId);
|
||||
|
||||
GetIndexJobStatusResponse response = client.getIndexJobStatusWithOptions(
|
||||
workspaceId, request, new HashMap<>(), new RuntimeOptions());
|
||||
|
||||
String status = response.getBody().getData().getStatus();
|
||||
if ("COMPLETED".equals(status)) break;
|
||||
if ("FAILED".equals(status)) throw new RuntimeException("索引任务失败");
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
}
|
||||
|
||||
private static String calculateMD5(InputStream inputStream) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
md.update(buffer, 0, bytesRead);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : md.digest()) {
|
||||
sb.append(String.format("%02x", b & 0xff));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void uploadFile(String preSignedUrl, Map<String, String> headers,
|
||||
MultipartFile file) throws Exception {
|
||||
HttpURLConnection conn = (HttpURLConnection) new URL(preSignedUrl).openConnection();
|
||||
conn.setRequestMethod("PUT");
|
||||
conn.setDoOutput(true);
|
||||
conn.setRequestProperty("X-bailian-extra", headers.get("X-bailian-extra"));
|
||||
conn.setRequestProperty("Content-Type", headers.get("Content-Type"));
|
||||
|
||||
try (InputStream inputStream = file.getInputStream()) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
conn.getOutputStream().write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
if (conn.getResponseCode() != 200) {
|
||||
throw new RuntimeException("上传失败: " + conn.getResponseCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化客户端(Client)。
|
||||
*
|
||||
* @return 配置好的客户端对象
|
||||
*/
|
||||
public static com.aliyun.bailian20231229.Client createClient(String accessKeyId, String accessKeySecret) throws Exception {
|
||||
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
|
||||
.setAccessKeyId(accessKeyId)
|
||||
.setAccessKeySecret(accessKeySecret);
|
||||
// 下方接入地址以公有云的公网接入地址为例,可按需更换接入地址。
|
||||
config.endpoint = "bailian.cn-beijing.aliyuncs.com";
|
||||
return new com.aliyun.bailian20231229.Client(config);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String ALIBABA_CLOUD_ACCESS_KEY_ID = "LTAI5tD5YRKuxWz6Eg7qrM4P";
|
||||
String ALIBABA_CLOUD_ACCESS_KEY_SECRET = "bO8TBDXflOwbtSKimPpG8XrJnyzgTk";
|
||||
String WORKSPACE_ID = "llm-4pf5auwewoz34zqu";
|
||||
String indexId = "b9pvwfqp3d";
|
||||
String filePath = "D:\\公司经济责任审计方案模板.docx";
|
||||
|
||||
Client client = createClient(ALIBABA_CLOUD_ACCESS_KEY_ID, ALIBABA_CLOUD_ACCESS_KEY_SECRET);
|
||||
|
||||
uploadDocument(client, WORKSPACE_ID, indexId, filePath);
|
||||
}
|
||||
}
|
||||
//package com.gxwebsoft.ai.util;
|
||||
//
|
||||
//import com.aliyun.bailian20231229.Client;
|
||||
//import com.aliyun.bailian20231229.models.*;
|
||||
//import com.aliyun.teautil.models.RuntimeOptions;
|
||||
//import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
//
|
||||
//import java.io.File;
|
||||
//import java.io.FileInputStream;
|
||||
//import java.io.InputStream;
|
||||
//import java.net.HttpURLConnection;
|
||||
//import java.net.URL;
|
||||
//import java.nio.file.Paths;
|
||||
//import java.security.MessageDigest;
|
||||
//import java.util.*;
|
||||
//
|
||||
//import org.springframework.web.multipart.MultipartFile;
|
||||
//
|
||||
///**
|
||||
// * 知识库上传工具类
|
||||
// * @author GIIT-YC
|
||||
// *
|
||||
// */
|
||||
//public class KnowledgeBaseUploader {
|
||||
//
|
||||
// /**
|
||||
// * 上传文档到知识库(直接处理MultipartFile)
|
||||
// *
|
||||
// * @param client 阿里云客户端
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param indexId 知识库ID
|
||||
// * @param file 上传的文件
|
||||
// * @return 新文档的FileID,失败返回null
|
||||
// */
|
||||
// public static String uploadDocument(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, MultipartFile file) {
|
||||
// try {
|
||||
// // 准备文档信息
|
||||
// String fileName = file.getOriginalFilename();
|
||||
// String fileMd5 = calculateMD5(file.getInputStream());
|
||||
// String fileSize = String.valueOf(file.getSize());
|
||||
//
|
||||
// // 申请上传租约
|
||||
// ApplyFileUploadLeaseRequest leaseRequest = new ApplyFileUploadLeaseRequest()
|
||||
// .setFileName(fileName)
|
||||
// .setMd5(fileMd5)
|
||||
// .setSizeInBytes(fileSize);
|
||||
//
|
||||
// ApplyFileUploadLeaseResponse leaseResponse = client.applyFileUploadLeaseWithOptions(
|
||||
// "default", workspaceId, leaseRequest, new HashMap<>(), new RuntimeOptions());
|
||||
//
|
||||
// String leaseId = leaseResponse.getBody().getData().getFileUploadLeaseId();
|
||||
// String uploadUrl = leaseResponse.getBody().getData().getParam().getUrl();
|
||||
//
|
||||
// // 上传文件
|
||||
// ObjectMapper mapper = new ObjectMapper();
|
||||
// Map<String, String> headers = mapper.readValue(mapper.writeValueAsString(leaseResponse.getBody().getData().getParam().getHeaders()), Map.class);
|
||||
//
|
||||
// uploadFile(uploadUrl, headers, file);
|
||||
//
|
||||
// // 添加文件到类目
|
||||
// AddFileRequest addRequest = new AddFileRequest()
|
||||
// .setLeaseId(leaseId)
|
||||
// .setParser("DASHSCOPE_DOCMIND")
|
||||
// .setCategoryId("default");
|
||||
//
|
||||
// AddFileResponse addResponse = client.addFileWithOptions(workspaceId, addRequest, new HashMap<>(), new RuntimeOptions());
|
||||
//
|
||||
// String fileId = addResponse.getBody().getData().getFileId();
|
||||
//
|
||||
// // 等待文件解析完成
|
||||
// waitForFileParsing(client, workspaceId, fileId);
|
||||
//
|
||||
// // 添加到知识库
|
||||
// SubmitIndexAddDocumentsJobRequest indexRequest = new SubmitIndexAddDocumentsJobRequest()
|
||||
// .setIndexId(indexId)
|
||||
// .setDocumentIds(Collections.singletonList(fileId))
|
||||
// .setSourceType("DATA_CENTER_FILE");
|
||||
//
|
||||
// SubmitIndexAddDocumentsJobResponse indexResponse = client.submitIndexAddDocumentsJobWithOptions(workspaceId, indexRequest, new HashMap<>(), new RuntimeOptions());
|
||||
//
|
||||
// // 等待索引完成
|
||||
// waitForIndexJob(client, workspaceId, indexResponse.getBody().getData().getId(), indexId);
|
||||
//
|
||||
// return fileId;
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 批量上传文档到知识库
|
||||
// */
|
||||
// public static List<String> uploadDocuments(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, MultipartFile[] files) {
|
||||
// List<String> fileIds = new ArrayList<>();
|
||||
// for (MultipartFile file : files) {
|
||||
// String fileId = uploadDocument(client, workspaceId, indexId, file);
|
||||
// if (fileId != null) {
|
||||
// fileIds.add(fileId);
|
||||
// }
|
||||
// }
|
||||
// return fileIds;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 上传文档到知识库
|
||||
// *
|
||||
// * @param client 阿里云客户端
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param indexId 知识库ID
|
||||
// * @param filePath 文档本地路径
|
||||
// * @return 新文档的FileID,失败返回null
|
||||
// */
|
||||
// public static String uploadDocument(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, String filePath) {
|
||||
// try {
|
||||
// // 准备文档信息
|
||||
// String fileName = Paths.get(filePath).getFileName().toString();
|
||||
// String fileMd5 = calculateMD5(filePath);
|
||||
// String fileSize = String.valueOf(new File(filePath).length());
|
||||
//
|
||||
// // 申请上传租约
|
||||
// ApplyFileUploadLeaseRequest leaseRequest = new ApplyFileUploadLeaseRequest()
|
||||
// .setFileName(fileName)
|
||||
// .setMd5(fileMd5)
|
||||
// .setSizeInBytes(fileSize);
|
||||
//
|
||||
// ApplyFileUploadLeaseResponse leaseResponse = client.applyFileUploadLeaseWithOptions(
|
||||
// "default", workspaceId, leaseRequest, new HashMap<>(), new RuntimeOptions());
|
||||
//
|
||||
// String leaseId = leaseResponse.getBody().getData().getFileUploadLeaseId();
|
||||
// String uploadUrl = leaseResponse.getBody().getData().getParam().getUrl();
|
||||
//
|
||||
// // 上传文件
|
||||
// ObjectMapper mapper = new ObjectMapper();
|
||||
// Map<String, String> headers = mapper.readValue(
|
||||
// mapper.writeValueAsString(leaseResponse.getBody().getData().getParam().getHeaders()),
|
||||
// Map.class);
|
||||
//
|
||||
// uploadFile(uploadUrl, headers, filePath);
|
||||
//
|
||||
// // 添加文件到类目
|
||||
// AddFileRequest addRequest = new AddFileRequest()
|
||||
// .setLeaseId(leaseId)
|
||||
// .setParser("DASHSCOPE_DOCMIND")
|
||||
// .setCategoryId("default");
|
||||
//
|
||||
// AddFileResponse addResponse = client.addFileWithOptions(
|
||||
// workspaceId, addRequest, new HashMap<>(), new RuntimeOptions());
|
||||
//
|
||||
// String fileId = addResponse.getBody().getData().getFileId();
|
||||
//
|
||||
// // 等待文件解析完成
|
||||
// waitForFileParsing(client, workspaceId, fileId);
|
||||
//
|
||||
// // 添加到知识库
|
||||
// SubmitIndexAddDocumentsJobRequest indexRequest = new SubmitIndexAddDocumentsJobRequest()
|
||||
// .setIndexId(indexId)
|
||||
// .setDocumentIds(Collections.singletonList(fileId))
|
||||
// .setSourceType("DATA_CENTER_FILE");
|
||||
//
|
||||
// SubmitIndexAddDocumentsJobResponse indexResponse = client.submitIndexAddDocumentsJobWithOptions(
|
||||
// workspaceId, indexRequest, new HashMap<>(), new RuntimeOptions());
|
||||
//
|
||||
// // 等待索引完成
|
||||
// waitForIndexJob(client, workspaceId, indexResponse.getBody().getData().getId(), indexId);
|
||||
//
|
||||
// return fileId;
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private static String calculateMD5(String filePath) throws Exception {
|
||||
// MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
// try (FileInputStream fis = new FileInputStream(filePath)) {
|
||||
// byte[] buffer = new byte[4096];
|
||||
// int bytesRead;
|
||||
// while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
// md.update(buffer, 0, bytesRead);
|
||||
// }
|
||||
// }
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
// for (byte b : md.digest()) {
|
||||
// sb.append(String.format("%02x", b & 0xff));
|
||||
// }
|
||||
// return sb.toString();
|
||||
// }
|
||||
//
|
||||
// private static void uploadFile(String preSignedUrl, Map<String, String> headers,
|
||||
// String filePath) throws Exception {
|
||||
// try (FileInputStream fis = new FileInputStream(filePath)) {
|
||||
// HttpURLConnection conn = (HttpURLConnection) new URL(preSignedUrl).openConnection();
|
||||
// conn.setRequestMethod("PUT");
|
||||
// conn.setDoOutput(true);
|
||||
// conn.setRequestProperty("X-bailian-extra", headers.get("X-bailian-extra"));
|
||||
// conn.setRequestProperty("Content-Type", headers.get("Content-Type"));
|
||||
//
|
||||
// byte[] buffer = new byte[4096];
|
||||
// int bytesRead;
|
||||
// while ((bytesRead = fis.read(buffer)) != -1) {
|
||||
// conn.getOutputStream().write(buffer, 0, bytesRead);
|
||||
// }
|
||||
//
|
||||
// if (conn.getResponseCode() != 200) {
|
||||
// throw new RuntimeException("上传失败: " + conn.getResponseCode());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private static void waitForFileParsing(com.aliyun.bailian20231229.Client client,
|
||||
// String workspaceId, String fileId) throws Exception {
|
||||
// while (true) {
|
||||
// DescribeFileResponse response = client.describeFileWithOptions(
|
||||
// workspaceId, fileId, new HashMap<>(), new RuntimeOptions());
|
||||
//
|
||||
// String status = response.getBody().getData().getStatus();
|
||||
// if ("PARSE_SUCCESS".equals(status)) break;
|
||||
// if ("PARSE_FAILED".equals(status)) throw new RuntimeException("文档解析失败");
|
||||
// Thread.sleep(5000);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private static void waitForIndexJob(com.aliyun.bailian20231229.Client client,
|
||||
// String workspaceId, String jobId, String indexId) throws Exception {
|
||||
// while (true) {
|
||||
// GetIndexJobStatusRequest request = new GetIndexJobStatusRequest()
|
||||
// .setIndexId(indexId)
|
||||
// .setJobId(jobId);
|
||||
//
|
||||
// GetIndexJobStatusResponse response = client.getIndexJobStatusWithOptions(
|
||||
// workspaceId, request, new HashMap<>(), new RuntimeOptions());
|
||||
//
|
||||
// String status = response.getBody().getData().getStatus();
|
||||
// if ("COMPLETED".equals(status)) break;
|
||||
// if ("FAILED".equals(status)) throw new RuntimeException("索引任务失败");
|
||||
// Thread.sleep(5000);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private static String calculateMD5(InputStream inputStream) throws Exception {
|
||||
// MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
// byte[] buffer = new byte[4096];
|
||||
// int bytesRead;
|
||||
// while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
// md.update(buffer, 0, bytesRead);
|
||||
// }
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
// for (byte b : md.digest()) {
|
||||
// sb.append(String.format("%02x", b & 0xff));
|
||||
// }
|
||||
// return sb.toString();
|
||||
// }
|
||||
//
|
||||
// private static void uploadFile(String preSignedUrl, Map<String, String> headers,
|
||||
// MultipartFile file) throws Exception {
|
||||
// HttpURLConnection conn = (HttpURLConnection) new URL(preSignedUrl).openConnection();
|
||||
// conn.setRequestMethod("PUT");
|
||||
// conn.setDoOutput(true);
|
||||
// conn.setRequestProperty("X-bailian-extra", headers.get("X-bailian-extra"));
|
||||
// conn.setRequestProperty("Content-Type", headers.get("Content-Type"));
|
||||
//
|
||||
// try (InputStream inputStream = file.getInputStream()) {
|
||||
// byte[] buffer = new byte[4096];
|
||||
// int bytesRead;
|
||||
// while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
// conn.getOutputStream().write(buffer, 0, bytesRead);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (conn.getResponseCode() != 200) {
|
||||
// throw new RuntimeException("上传失败: " + conn.getResponseCode());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 初始化客户端(Client)。
|
||||
// *
|
||||
// * @return 配置好的客户端对象
|
||||
// */
|
||||
// public static com.aliyun.bailian20231229.Client createClient(String accessKeyId, String accessKeySecret) throws Exception {
|
||||
// com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
|
||||
// .setAccessKeyId(accessKeyId)
|
||||
// .setAccessKeySecret(accessKeySecret);
|
||||
// // 下方接入地址以公有云的公网接入地址为例,可按需更换接入地址。
|
||||
// config.endpoint = "bailian.cn-beijing.aliyuncs.com";
|
||||
// return new com.aliyun.bailian20231229.Client(config);
|
||||
// }
|
||||
//
|
||||
// public static void main(String[] args) throws Exception {
|
||||
// String ALIBABA_CLOUD_ACCESS_KEY_ID = "LTAI5tD5YRKuxWz6Eg7qrM4P";
|
||||
// String ALIBABA_CLOUD_ACCESS_KEY_SECRET = "bO8TBDXflOwbtSKimPpG8XrJnyzgTk";
|
||||
// String WORKSPACE_ID = "llm-4pf5auwewoz34zqu";
|
||||
// String indexId = "b9pvwfqp3d";
|
||||
// String filePath = "D:\\公司经济责任审计方案模板.docx";
|
||||
//
|
||||
// Client client = createClient(ALIBABA_CLOUD_ACCESS_KEY_ID, ALIBABA_CLOUD_ACCESS_KEY_SECRET);
|
||||
//
|
||||
// uploadDocument(client, WORKSPACE_ID, indexId, filePath);
|
||||
// }
|
||||
//}
|
||||
@@ -1,136 +1,156 @@
|
||||
package com.gxwebsoft.ai.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.aliyun.bailian20231229.models.CreateIndexResponse;
|
||||
import com.aliyun.bailian20231229.models.DeleteFileResponse;
|
||||
import com.aliyun.bailian20231229.models.DeleteIndexDocumentResponse;
|
||||
import com.aliyun.bailian20231229.models.DeleteIndexResponse;
|
||||
import com.aliyun.bailian20231229.models.ListIndexDocumentsResponse;
|
||||
import com.aliyun.bailian20231229.models.ListIndicesResponse;
|
||||
import com.aliyun.bailian20231229.models.RetrieveRequest;
|
||||
import com.aliyun.bailian20231229.models.RetrieveResponse;
|
||||
import com.aliyun.teautil.models.RuntimeOptions;
|
||||
|
||||
/**
|
||||
* 知识库工具类
|
||||
* @author GIIT-YC
|
||||
*
|
||||
*/
|
||||
public class KnowledgeBaseUtil {
|
||||
|
||||
/**
|
||||
* 在指定的知识库中检索信息。
|
||||
*
|
||||
* @param client 客户端对象(bailian20231229Client)
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param indexId 知识库ID
|
||||
* @param query 检索查询语句
|
||||
* @return 阿里云百炼服务的响应
|
||||
*/
|
||||
public static RetrieveResponse retrieveIndex(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, String query) throws Exception {
|
||||
RetrieveRequest retrieveRequest = new RetrieveRequest();
|
||||
retrieveRequest.setIndexId(indexId);
|
||||
retrieveRequest.setQuery(query);
|
||||
retrieveRequest.setDenseSimilarityTopK(null);
|
||||
RuntimeOptions runtime = new RuntimeOptions();
|
||||
return client.retrieveWithOptions(workspaceId, retrieveRequest, null, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在阿里云百炼服务中创建知识库(初始化)。
|
||||
*
|
||||
* @param client 客户端对象
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param name 知识库名称
|
||||
* @param desc 知识库描述
|
||||
* @return 阿里云百炼服务的响应对象
|
||||
*/
|
||||
public static CreateIndexResponse createIndex(com.aliyun.bailian20231229.Client client, String workspaceId, String name, String desc) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.bailian20231229.models.CreateIndexRequest createIndexRequest = new com.aliyun.bailian20231229.models.CreateIndexRequest();
|
||||
createIndexRequest.setStructureType("unstructured");
|
||||
createIndexRequest.setName(name);
|
||||
createIndexRequest.setDescription(desc);
|
||||
createIndexRequest.setSinkType("DEFAULT");
|
||||
createIndexRequest.setEmbeddingModelName("text-embedding-v4");
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
return client.createIndexWithOptions(workspaceId, createIndexRequest, headers, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定业务空间下一个或多个知识库的详细信息
|
||||
*
|
||||
* @param client 客户端(Client)
|
||||
* @param workspaceId 业务空间ID
|
||||
* @return 阿里云百炼服务的响应
|
||||
*/
|
||||
public static ListIndicesResponse listIndices(com.aliyun.bailian20231229.Client client, String workspaceId) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.bailian20231229.models.ListIndicesRequest listIndicesRequest = new com.aliyun.bailian20231229.models.ListIndicesRequest();
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
return client.listIndicesWithOptions(workspaceId, listIndicesRequest, headers, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 永久性删除指定的知识库
|
||||
*
|
||||
* @param client 客户端(Client)
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param indexId 知识库ID
|
||||
* @return 阿里云百炼服务的响应
|
||||
*/
|
||||
public static DeleteIndexResponse deleteIndex(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId) throws Exception {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
com.aliyun.bailian20231229.models.DeleteIndexRequest deleteIndexRequest = new com.aliyun.bailian20231229.models.DeleteIndexRequest();
|
||||
deleteIndexRequest.setIndexId(indexId);
|
||||
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
return client.deleteIndexWithOptions(workspaceId, deleteIndexRequest, headers, runtime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询知识库下的文档列表
|
||||
*
|
||||
* @param client 客户端(Client)
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param indexId 知识库ID
|
||||
* @return 阿里云百炼服务的响应
|
||||
*/
|
||||
public static ListIndexDocumentsResponse listIndexDocuments(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, Integer pageSize, Integer pageNumber) throws Exception {
|
||||
com.aliyun.bailian20231229.models.ListIndexDocumentsRequest listIndexDocumentsRequest = new com.aliyun.bailian20231229.models.ListIndexDocumentsRequest();
|
||||
listIndexDocumentsRequest.setIndexId(indexId);
|
||||
listIndexDocumentsRequest.setPageSize(pageSize);
|
||||
listIndexDocumentsRequest.setPageNumber(pageNumber);
|
||||
return client.listIndexDocuments(workspaceId, listIndexDocumentsRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除知识库下的文档
|
||||
*
|
||||
* @param client 客户端(Client)
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param indexId 知识库ID
|
||||
* @param ids 删除文件ID列表
|
||||
* @return 阿里云百炼服务的响应
|
||||
*/
|
||||
public static DeleteIndexDocumentResponse deleteIndexDocument(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, List<String> ids) throws Exception {
|
||||
com.aliyun.bailian20231229.models.DeleteIndexDocumentRequest deleteIndexDocumentRequest = new com.aliyun.bailian20231229.models.DeleteIndexDocumentRequest();
|
||||
deleteIndexDocumentRequest.setIndexId(indexId);
|
||||
deleteIndexDocumentRequest.setDocumentIds(ids);
|
||||
return client.deleteIndexDocument(workspaceId, deleteIndexDocumentRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除阿里云应用数据文档
|
||||
*
|
||||
* @param client 客户端(Client)
|
||||
* @param workspaceId 业务空间ID
|
||||
* @param fileId 删除文件ID
|
||||
* @return 阿里云百炼服务的响应
|
||||
*/
|
||||
public static DeleteFileResponse deleteAppDocument(com.aliyun.bailian20231229.Client client, String workspaceId, String fileId) throws Exception {
|
||||
return client.deleteFile(fileId, workspaceId);
|
||||
}
|
||||
}
|
||||
//package com.gxwebsoft.ai.util;
|
||||
//
|
||||
//import java.util.ArrayList;
|
||||
//import java.util.HashMap;
|
||||
//import java.util.List;
|
||||
//import java.util.Map;
|
||||
//
|
||||
//import com.alibaba.fastjson.JSON;
|
||||
//import com.aliyun.bailian20231229.models.CreateIndexResponse;
|
||||
//import com.aliyun.bailian20231229.models.DeleteFileResponse;
|
||||
//import com.aliyun.bailian20231229.models.DeleteIndexDocumentResponse;
|
||||
//import com.aliyun.bailian20231229.models.DeleteIndexResponse;
|
||||
//import com.aliyun.bailian20231229.models.ListIndexDocumentsResponse;
|
||||
//import com.aliyun.bailian20231229.models.ListIndicesResponse;
|
||||
//import com.aliyun.bailian20231229.models.RetrieveRequest;
|
||||
//import com.aliyun.bailian20231229.models.RetrieveResponse;
|
||||
//import com.aliyun.teautil.models.RuntimeOptions;
|
||||
//
|
||||
///**
|
||||
// * 知识库工具类
|
||||
// * @author GIIT-YC
|
||||
// *
|
||||
// */
|
||||
//public class KnowledgeBaseUtil {
|
||||
//
|
||||
// /**
|
||||
// * 在指定的知识库中检索信息。
|
||||
// *
|
||||
// * @param client 客户端对象(bailian20231229Client)
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param indexId 知识库ID
|
||||
// * @param query 检索查询语句
|
||||
// * @return 阿里云百炼服务的响应
|
||||
// */
|
||||
// public static RetrieveResponse retrieveIndex(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, String query) throws Exception {
|
||||
// RetrieveRequest retrieveRequest = new RetrieveRequest();
|
||||
// retrieveRequest.setIndexId(indexId);
|
||||
// retrieveRequest.setQuery(query);
|
||||
// retrieveRequest.setDenseSimilarityTopK(100);
|
||||
// retrieveRequest.setSparseSimilarityTopK(100);
|
||||
// retrieveRequest.setEnableReranking(false);//开启耗费巨量token
|
||||
// RuntimeOptions runtime = new RuntimeOptions();
|
||||
// return client.retrieveWithOptions(workspaceId, retrieveRequest, null, runtime);
|
||||
// }
|
||||
//
|
||||
// public static RetrieveResponse retrieveIndex(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, String query, List<String> filesIds) throws Exception {
|
||||
// RetrieveRequest retrieveRequest = new RetrieveRequest();
|
||||
// retrieveRequest.setIndexId(indexId);
|
||||
// retrieveRequest.setQuery(query);
|
||||
// retrieveRequest.setDenseSimilarityTopK(100);
|
||||
// retrieveRequest.setSparseSimilarityTopK(100);
|
||||
// retrieveRequest.setEnableReranking(false);//开启耗费巨量token
|
||||
// List<Map<String, String>> searchFilters = new ArrayList<>();
|
||||
// Map<String, String> searchFiltersTags = new HashMap<>();
|
||||
// searchFiltersTags.put("tags", JSON.toJSONString(filesIds));
|
||||
// searchFilters.add(searchFiltersTags);
|
||||
// retrieveRequest.setSearchFilters(searchFilters);
|
||||
// RuntimeOptions runtime = new RuntimeOptions();
|
||||
// return client.retrieveWithOptions(workspaceId, retrieveRequest, null, runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 在阿里云百炼服务中创建知识库(初始化)。
|
||||
// *
|
||||
// * @param client 客户端对象
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param name 知识库名称
|
||||
// * @param desc 知识库描述
|
||||
// * @return 阿里云百炼服务的响应对象
|
||||
// */
|
||||
// public static CreateIndexResponse createIndex(com.aliyun.bailian20231229.Client client, String workspaceId, String name, String desc) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.bailian20231229.models.CreateIndexRequest createIndexRequest = new com.aliyun.bailian20231229.models.CreateIndexRequest();
|
||||
// createIndexRequest.setStructureType("unstructured");
|
||||
// createIndexRequest.setName(name);
|
||||
// createIndexRequest.setDescription(desc);
|
||||
// createIndexRequest.setSinkType("DEFAULT");
|
||||
// createIndexRequest.setEmbeddingModelName("text-embedding-v4");
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// return client.createIndexWithOptions(workspaceId, createIndexRequest, headers, runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取指定业务空间下一个或多个知识库的详细信息
|
||||
// *
|
||||
// * @param client 客户端(Client)
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @return 阿里云百炼服务的响应
|
||||
// */
|
||||
// public static ListIndicesResponse listIndices(com.aliyun.bailian20231229.Client client, String workspaceId) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.bailian20231229.models.ListIndicesRequest listIndicesRequest = new com.aliyun.bailian20231229.models.ListIndicesRequest();
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// return client.listIndicesWithOptions(workspaceId, listIndicesRequest, headers, runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 永久性删除指定的知识库
|
||||
// *
|
||||
// * @param client 客户端(Client)
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param indexId 知识库ID
|
||||
// * @return 阿里云百炼服务的响应
|
||||
// */
|
||||
// public static DeleteIndexResponse deleteIndex(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId) throws Exception {
|
||||
// Map<String, String> headers = new HashMap<>();
|
||||
// com.aliyun.bailian20231229.models.DeleteIndexRequest deleteIndexRequest = new com.aliyun.bailian20231229.models.DeleteIndexRequest();
|
||||
// deleteIndexRequest.setIndexId(indexId);
|
||||
// com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
|
||||
// return client.deleteIndexWithOptions(workspaceId, deleteIndexRequest, headers, runtime);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 查询知识库下的文档列表
|
||||
// *
|
||||
// * @param client 客户端(Client)
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param indexId 知识库ID
|
||||
// * @return 阿里云百炼服务的响应
|
||||
// */
|
||||
// public static ListIndexDocumentsResponse listIndexDocuments(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, Integer pageSize, Integer pageNumber) throws Exception {
|
||||
// com.aliyun.bailian20231229.models.ListIndexDocumentsRequest listIndexDocumentsRequest = new com.aliyun.bailian20231229.models.ListIndexDocumentsRequest();
|
||||
// listIndexDocumentsRequest.setIndexId(indexId);
|
||||
// listIndexDocumentsRequest.setPageSize(pageSize);
|
||||
// listIndexDocumentsRequest.setPageNumber(pageNumber);
|
||||
// return client.listIndexDocuments(workspaceId, listIndexDocumentsRequest);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 删除知识库下的文档
|
||||
// *
|
||||
// * @param client 客户端(Client)
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param indexId 知识库ID
|
||||
// * @param ids 删除文件ID列表
|
||||
// * @return 阿里云百炼服务的响应
|
||||
// */
|
||||
// public static DeleteIndexDocumentResponse deleteIndexDocument(com.aliyun.bailian20231229.Client client, String workspaceId, String indexId, List<String> ids) throws Exception {
|
||||
// com.aliyun.bailian20231229.models.DeleteIndexDocumentRequest deleteIndexDocumentRequest = new com.aliyun.bailian20231229.models.DeleteIndexDocumentRequest();
|
||||
// deleteIndexDocumentRequest.setIndexId(indexId);
|
||||
// deleteIndexDocumentRequest.setDocumentIds(ids);
|
||||
// return client.deleteIndexDocument(workspaceId, deleteIndexDocumentRequest);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 删除阿里云应用数据文档
|
||||
// *
|
||||
// * @param client 客户端(Client)
|
||||
// * @param workspaceId 业务空间ID
|
||||
// * @param fileId 删除文件ID
|
||||
// * @return 阿里云百炼服务的响应
|
||||
// */
|
||||
// public static DeleteFileResponse deleteAppDocument(com.aliyun.bailian20231229.Client client, String workspaceId, String fileId) throws Exception {
|
||||
// return client.deleteFile(fileId, workspaceId);
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.gxwebsoft.common.system.controller;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||
import com.gxwebsoft.common.core.config.ConfigProperties;
|
||||
@@ -316,4 +319,23 @@ public class FileController extends BaseController {
|
||||
return fail("修改失败");
|
||||
}
|
||||
|
||||
public FileRecord upload(MultipartFile file, Integer tenantId) {
|
||||
FileRecord fileRecord = new FileRecord();
|
||||
if (file == null || file.isEmpty()) {
|
||||
return fileRecord;
|
||||
}
|
||||
try {
|
||||
String requestURL = config.getServerUrl() + "/oss/upload";
|
||||
String response = HttpRequest.post(requestURL)
|
||||
.header("Tenantid", tenantId != null ? tenantId.toString() : getTenantId().toString())
|
||||
.form("file", file.getBytes(), file.getOriginalFilename())
|
||||
.execute()
|
||||
.body();
|
||||
JSONObject data = JSONObject.parseObject(response).getJSONObject("data");
|
||||
fileRecord = data != null ? BeanUtil.copyProperties(data, FileRecord.class) : fileRecord;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return fileRecord;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +112,10 @@ public class OaCompanyController extends BaseController {
|
||||
@Operation(summary = "删除企业信息")
|
||||
@DeleteMapping("/{id}")
|
||||
public ApiResult<?> remove(@PathVariable("id") Integer id) {
|
||||
//删云目录
|
||||
oaCompanyService.removeCompanyDocDirectories(id);
|
||||
//删云知识库
|
||||
oaCompanyService.removeCompanyKnowledgeBase(id);
|
||||
if (oaCompanyService.removeById(id)) {
|
||||
return success("删除成功");
|
||||
}
|
||||
|
||||
@@ -46,4 +46,19 @@ public interface OaCompanyService extends IService<OaCompany> {
|
||||
* @param userId 创建用户ID
|
||||
*/
|
||||
void initCompanyDocDirectories(OaCompany oaCompany, Integer userId);
|
||||
|
||||
/**
|
||||
* 清空企业目录
|
||||
*
|
||||
* @param oaCompany 企业Id
|
||||
*/
|
||||
boolean removeCompanyDocDirectories(Integer companyId);
|
||||
|
||||
/**
|
||||
* 清空企业知识库
|
||||
*
|
||||
* @param oaCompany 企业Id
|
||||
*/
|
||||
boolean removeCompanyKnowledgeBase(Integer companyId);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
package com.gxwebsoft.oa.service.impl;
|
||||
|
||||
import com.aliyun.bailian20231229.Client;
|
||||
import com.aliyun.bailian20231229.models.DeleteCategoryResponseBody;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.gxwebsoft.oa.mapper.OaCompanyMapper;
|
||||
import com.gxwebsoft.oa.service.OaCompanyService;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import com.gxwebsoft.oa.entity.OaCompany;
|
||||
import com.gxwebsoft.oa.param.OaCompanyParam;
|
||||
import com.gxwebsoft.ai.config.KnowledgeBaseConfig;
|
||||
import com.gxwebsoft.ai.entity.AiCloudDoc;
|
||||
import com.gxwebsoft.ai.entity.AiCloudFile;
|
||||
import com.gxwebsoft.ai.factory.KnowledgeBaseClientFactory;
|
||||
import com.gxwebsoft.ai.service.AiCloudDocService;
|
||||
import com.gxwebsoft.ai.service.AiCloudFileService;
|
||||
import com.gxwebsoft.ai.util.AiCloudDataCenterUtil;
|
||||
import com.gxwebsoft.ai.util.AiCloudKnowledgeBaseUtil;
|
||||
import com.gxwebsoft.common.core.web.PageParam;
|
||||
import com.gxwebsoft.common.core.web.PageResult;
|
||||
|
||||
@@ -21,6 +29,7 @@ import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 企业信息Service实现
|
||||
@@ -33,6 +42,9 @@ public class OaCompanyServiceImpl extends ServiceImpl<OaCompanyMapper, OaCompany
|
||||
|
||||
@Autowired
|
||||
private AiCloudDocService aiCloudDocService;
|
||||
|
||||
@Autowired
|
||||
private AiCloudFileService aiCloudFileService;
|
||||
|
||||
@Autowired
|
||||
private KnowledgeBaseConfig config;
|
||||
@@ -123,4 +135,58 @@ public class OaCompanyServiceImpl extends ServiceImpl<OaCompanyMapper, OaCompany
|
||||
// 批量保存子目录
|
||||
aiCloudDocService.saveBatch(directories);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeCompanyDocDirectories(Integer companyId) {
|
||||
boolean ret = true;
|
||||
String workspaceId = config.getWorkspaceId();
|
||||
if(companyId==null) {
|
||||
return ret;
|
||||
}
|
||||
//目录
|
||||
List<AiCloudDoc> docList = aiCloudDocService.list(new LambdaQueryWrapper<AiCloudDoc>().eq(AiCloudDoc::getCompanyId, companyId));
|
||||
List<Integer> docIds = docList.stream().map(AiCloudDoc::getId).collect(Collectors.toList());
|
||||
//先删目阿里云录下所有文件
|
||||
List<AiCloudFile> fileList = aiCloudFileService.list(new LambdaQueryWrapper<AiCloudFile>().in(AiCloudFile::getDocId, docIds));
|
||||
List<Integer> fileIds = fileList.stream().map(AiCloudFile::getId).collect(Collectors.toList());
|
||||
for(AiCloudFile file : fileList) {
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
AiCloudDataCenterUtil.deleteFile(client, workspaceId, file.getFileId());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
//再删阿里云目录
|
||||
List<String> categoryIds = docList.stream().map(AiCloudDoc::getCategoryId).filter(StrUtil::isNotBlank).distinct().collect(Collectors.toList());
|
||||
for(String categoryId : categoryIds) {
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
ret = AiCloudDataCenterUtil.deleteCategory(client, workspaceId, categoryId).getBody().getSuccess();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
aiCloudDocService.removeByIds(docIds);
|
||||
aiCloudFileService.removeByIds(fileIds);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeCompanyKnowledgeBase(Integer companyId) {
|
||||
boolean ret = true;
|
||||
String workspaceId = config.getWorkspaceId();
|
||||
if(companyId==null) {
|
||||
return ret;
|
||||
}
|
||||
//删阿里云知识库
|
||||
OaCompany oaCompany = baseMapper.selectById(companyId);
|
||||
try {
|
||||
Client client = clientFactory.createClient();
|
||||
ret = AiCloudKnowledgeBaseUtil.deleteIndex(client, workspaceId, oaCompany.getKbId()).getBody().getSuccess();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.gxwebsoft.pwl.controller;
|
||||
import cn.afterturn.easypoi.excel.ExcelImportUtil;
|
||||
import cn.afterturn.easypoi.excel.entity.ImportParams;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
@@ -135,7 +136,8 @@ public class PwlProjectController extends BaseController {
|
||||
//asLibCode = as00000000001
|
||||
String asLibCode = "as" + StrUtil.padPre(pwlProject.getId().toString(), 11, '0');
|
||||
// String asLibCode = "as"+pwlProject.getCode();
|
||||
String analysisKbId = knowledgeBaseService.createKnowledgeBase(asLibName, asLibCode);
|
||||
// String analysisKbId = knowledgeBaseService.createKnowledgeBase(asLibName, asLibCode);
|
||||
String analysisKbId = RandomUtil.randomString(10);
|
||||
pwlProject.setAnalysisLibrary(analysisKbId);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -152,7 +154,8 @@ public class PwlProjectController extends BaseController {
|
||||
//pjLibCode = pj00000000001
|
||||
String pjLibCode = "pj" + StrUtil.padPre(pwlProject.getId().toString(), 11, '0');
|
||||
// String pjLibCode = "pj"+pwlProject.getCode();
|
||||
String projectKbId = knowledgeBaseService.createKnowledgeBase(pjLibName, pjLibCode);
|
||||
// String projectKbId = knowledgeBaseService.createKnowledgeBase(pjLibName, pjLibCode);
|
||||
String projectKbId = RandomUtil.randomString(10);
|
||||
pwlProject.setProjectLibrary(projectKbId);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -41,6 +41,9 @@ public class PwlProject implements Serializable {
|
||||
@Schema(description = "项目标识")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "案引号")
|
||||
private String caseIndex;
|
||||
|
||||
@Schema(description = "上级id, 0是顶级")
|
||||
private Integer parentId;
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
<if test="param.code != null">
|
||||
AND a.code LIKE CONCAT('%', #{param.code}, '%')
|
||||
</if>
|
||||
<if test="param.caseIndex != null">
|
||||
AND a.case_index LIKE CONCAT('%', #{param.caseIndex}, '%')
|
||||
</if>
|
||||
<if test="param.parentId != null">
|
||||
AND a.parent_id = #{param.parentId}
|
||||
</if>
|
||||
|
||||
@@ -40,6 +40,9 @@ public class PwlProjectParam extends BaseParam {
|
||||
@Schema(description = "项目标识")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "案引号")
|
||||
private String caseIndex;
|
||||
|
||||
@Schema(description = "上级id, 0是顶级")
|
||||
@QueryField(type = QueryType.EQ)
|
||||
private Integer parentId;
|
||||
|
||||
@@ -76,4 +76,8 @@ ai:
|
||||
# Word 模板路径
|
||||
word-template-path: classpath:templates/audit_report_template.docx
|
||||
# 或者使用绝对路径
|
||||
# word-template-path: D:\\公司经济责任审计方案模板.docx
|
||||
# word-template-path: D:\\公司经济责任审计方案模板.docx
|
||||
# Word 取证单模板路径
|
||||
evidence-template-path: classpath:templates/audit_evidence_template.docx
|
||||
# Word 取证单绝对路径
|
||||
#evidence-template-path: D:\\audit_evidence_template.docx
|
||||
|
||||
@@ -82,4 +82,8 @@ ai:
|
||||
# Word 模板路径
|
||||
word-template-path: classpath:templates/audit_report_template.docx
|
||||
# 或者使用绝对路径
|
||||
# word-template-path: D:\\公司经济责任审计方案模板.docx
|
||||
# word-template-path: D:\\公司经济责任审计方案模板.docx
|
||||
# Word 取证单模板路径
|
||||
evidence-template-path: classpath:templates/audit_evidence_template.docx
|
||||
# Word 取证单绝对路径
|
||||
#evidence-template-path: D:\\audit_evidence_template.docx
|
||||
|
||||
BIN
src/main/resources/templates/audit_evidence_template.docx
Normal file
BIN
src/main/resources/templates/audit_evidence_template.docx
Normal file
Binary file not shown.
Reference in New Issue
Block a user