新增生成决策支出表
This commit is contained in:
@@ -0,0 +1,69 @@
|
|||||||
|
package com.gxwebsoft.ai.constants;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支出情况表常量类
|
||||||
|
*/
|
||||||
|
public class AuditContent1ExpenseConstants {
|
||||||
|
|
||||||
|
// 支出类型分类
|
||||||
|
public static final String EXPENSE_TYPE_RECEPTION = "公务接待";
|
||||||
|
public static final String EXPENSE_TYPE_OVERSEAS = "出国";
|
||||||
|
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 Map<String, String> EXPENSE_DESCRIPTIONS = new HashMap<>();
|
||||||
|
static {
|
||||||
|
EXPENSE_DESCRIPTIONS.put(EXPENSE_TYPE_RECEPTION, "公务接待费用支出情况");
|
||||||
|
EXPENSE_DESCRIPTIONS.put(EXPENSE_TYPE_OVERSEAS, "出国(出境)费用支出情况");
|
||||||
|
EXPENSE_DESCRIPTIONS.put(EXPENSE_TYPE_VEHICLE, "公车运行维护费用支出情况");
|
||||||
|
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:相关文件索引";
|
||||||
|
|
||||||
|
// 关键词权重
|
||||||
|
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("财务报表", 9);
|
||||||
|
KEYWORD_WEIGHTS.put("年度报告", 8);
|
||||||
|
KEYWORD_WEIGHTS.put("预算执行", 8);
|
||||||
|
KEYWORD_WEIGHTS.put("财务分析", 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据来源关键词
|
||||||
|
public static final String[] DATA_SOURCE_KEYWORDS = {
|
||||||
|
"决算报表", "预算报表", "财务报告", "年度报告",
|
||||||
|
"预算执行情况", "经费使用情况", "支出明细", "费用分析"
|
||||||
|
};
|
||||||
|
|
||||||
|
private AuditContent1ExpenseConstants() {
|
||||||
|
// 防止实例化
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.gxwebsoft.ai.dto.AuditContentRequest;
|
import com.gxwebsoft.ai.dto.AuditContentRequest;
|
||||||
import com.gxwebsoft.ai.dto.export.EightRegExportEntity;
|
import com.gxwebsoft.ai.dto.export.EightRegExportEntity;
|
||||||
|
import com.gxwebsoft.ai.dto.export.ExpenseExportEntity;
|
||||||
import com.gxwebsoft.ai.dto.export.LeaderListExportEntity;
|
import com.gxwebsoft.ai.dto.export.LeaderListExportEntity;
|
||||||
import com.gxwebsoft.ai.entity.AiCloudDoc;
|
import com.gxwebsoft.ai.entity.AiCloudDoc;
|
||||||
import com.gxwebsoft.ai.entity.AiCloudFile;
|
import com.gxwebsoft.ai.entity.AiCloudFile;
|
||||||
@@ -17,6 +18,7 @@ import com.gxwebsoft.pwl.service.PwlProjectLibraryService;
|
|||||||
import com.gxwebsoft.ai.service.AiCloudDocService;
|
import com.gxwebsoft.ai.service.AiCloudDocService;
|
||||||
import com.gxwebsoft.ai.service.AiCloudFileService;
|
import com.gxwebsoft.ai.service.AiCloudFileService;
|
||||||
import com.gxwebsoft.ai.service.AuditContent1EightRegService;
|
import com.gxwebsoft.ai.service.AuditContent1EightRegService;
|
||||||
|
import com.gxwebsoft.ai.service.AuditContent1ExpenseService;
|
||||||
import com.gxwebsoft.ai.service.AuditContent1LeaderListService;
|
import com.gxwebsoft.ai.service.AuditContent1LeaderListService;
|
||||||
import com.gxwebsoft.ai.service.KnowledgeBaseService;
|
import com.gxwebsoft.ai.service.KnowledgeBaseService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@@ -45,6 +47,9 @@ public class AuditContent1Controller extends BaseController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private AuditContent1LeaderListService auditContent1LeaderListService;
|
private AuditContent1LeaderListService auditContent1LeaderListService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuditContent1ExpenseService auditContent1ExpenseService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AuditContent1EightRegService auditContent1EightRegService;
|
private AuditContent1EightRegService auditContent1EightRegService;
|
||||||
|
|
||||||
@@ -79,6 +84,19 @@ public class AuditContent1Controller extends BaseController {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成支出情况表数据
|
||||||
|
*/
|
||||||
|
@Operation(summary = "生成支出情况表")
|
||||||
|
@PostMapping("/generateExpenseTable")
|
||||||
|
public ApiResult<?> generateExpenseTable(@RequestBody AuditContentRequest request, HttpServletRequest servletRequest) {
|
||||||
|
return generateTableData(request, servletRequest.getRequestURI(),
|
||||||
|
(params) -> auditContent1ExpenseService.generateExpenseTableData(
|
||||||
|
params.knowledgeBaseId, params.libraryKbIds, params.projectLibrary,
|
||||||
|
params.username, params.history, params.suggestion
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成八项规定对比分析表数据
|
* 生成八项规定对比分析表数据
|
||||||
*/
|
*/
|
||||||
@@ -339,4 +357,35 @@ public class AuditContent1Controller extends BaseController {
|
|||||||
entity.setWorkPaperIndex(formatWorkPaperIndex(item.get("workPaperIndex")));
|
entity.setWorkPaperIndex(formatWorkPaperIndex(item.get("workPaperIndex")));
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出支出情况表到Excel
|
||||||
|
*/
|
||||||
|
@Operation(summary = "导出支出情况表到Excel")
|
||||||
|
@PostMapping("/exportExpenseTable")
|
||||||
|
public void exportExpenseTable(@RequestBody Map<String, Object> request, HttpServletResponse response) {
|
||||||
|
exportToExcel(request, response, "支出情况表",
|
||||||
|
this::convertToExpenseEntityList, ExpenseExportEntity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 数据转换方法 ==========
|
||||||
|
|
||||||
|
private List<ExpenseExportEntity> convertToExpenseEntityList(List<Map<String, Object>> originalData) {
|
||||||
|
return originalData.stream().map(this::convertToExpenseEntity).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpenseExportEntity convertToExpenseEntity(Map<String, Object> item) {
|
||||||
|
ExpenseExportEntity entity = new ExpenseExportEntity();
|
||||||
|
entity.setExpenseType(getStringValue(item, "expenseType"));
|
||||||
|
entity.setYear(getStringValue(item, "year"));
|
||||||
|
entity.setFinalStatementAmount(getStringValue(item, "finalStatementAmount"));
|
||||||
|
entity.setInitialBudgetAmount(getStringValue(item, "initialBudgetAmount"));
|
||||||
|
entity.setChangePercentage(getStringValue(item, "changePercentage"));
|
||||||
|
entity.setBudgetRatio(getStringValue(item, "budgetRatio"));
|
||||||
|
entity.setRemark(getStringValue(item, "remark"));
|
||||||
|
entity.setDataSource(getStringValue(item, "dataSource"));
|
||||||
|
entity.setWorkPaperIndex(formatWorkPaperIndex(item.get("workPaperIndex")));
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package com.gxwebsoft.ai.dto.export;
|
||||||
|
|
||||||
|
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支出情况表导出实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ExpenseExportEntity {
|
||||||
|
|
||||||
|
@Excel(name = "支出类型", orderNum = "0", width = 12)
|
||||||
|
private String expenseType;
|
||||||
|
|
||||||
|
@Excel(name = "年份", orderNum = "1", width = 8)
|
||||||
|
private String year;
|
||||||
|
|
||||||
|
@Excel(name = "决算报表数(元)", orderNum = "2", width = 15)
|
||||||
|
private String finalStatementAmount;
|
||||||
|
|
||||||
|
@Excel(name = "年初预算数(元)", orderNum = "3", width = 15)
|
||||||
|
private String initialBudgetAmount;
|
||||||
|
|
||||||
|
@Excel(name = "增减情况(%)", orderNum = "4", width = 12)
|
||||||
|
private String changePercentage;
|
||||||
|
|
||||||
|
@Excel(name = "占年初预算比例(%)", orderNum = "5", width = 12)
|
||||||
|
private String budgetRatio;
|
||||||
|
|
||||||
|
@Excel(name = "备注", orderNum = "6", width = 20)
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@Excel(name = "数据来源", orderNum = "7", width = 20)
|
||||||
|
private String dataSource;
|
||||||
|
|
||||||
|
@Excel(name = "工作底稿索引", orderNum = "8", width = 25)
|
||||||
|
private String workPaperIndex;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.gxwebsoft.ai.service;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支出情况表服务接口
|
||||||
|
*/
|
||||||
|
public interface AuditContent1ExpenseService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成支出情况表数据
|
||||||
|
* @param kbIds 知识库ID
|
||||||
|
* @param libraryKbIds 项目库KB IDs
|
||||||
|
* @param projectLibrary 项目库ID
|
||||||
|
* @param userName 用户名
|
||||||
|
* @param history 历史记录
|
||||||
|
* @param suggestion 建议
|
||||||
|
* @return JSON格式的结果
|
||||||
|
*/
|
||||||
|
JSONObject generateExpenseTableData(String kbIds, String libraryKbIds, String projectLibrary, String userName, String history, String suggestion);
|
||||||
|
}
|
||||||
@@ -0,0 +1,412 @@
|
|||||||
|
package com.gxwebsoft.ai.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.gxwebsoft.ai.constants.AuditContent1ExpenseConstants;
|
||||||
|
import com.gxwebsoft.ai.service.AuditContent1ExpenseService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class AuditContent1ExpenseServiceImpl extends AbstractAuditContentService implements AuditContent1ExpenseService {
|
||||||
|
|
||||||
|
// 工作流配置
|
||||||
|
private static final String DIFY_WORKFLOW_TOKEN = "Bearer app-xwWxGF6hERgdeKB5OPpj9Tq8";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject generateExpenseTableData(String kbIds, String libraryKbIds, String projectLibrary,
|
||||||
|
String userName, String history, String suggestion) {
|
||||||
|
log.info("开始生成支出情况表数据 - 用户: {}, kbIds: {}, libraryIds: {}, projectLibrary: {}",
|
||||||
|
userName, kbIds, libraryKbIds, projectLibrary);
|
||||||
|
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 检索相关知识
|
||||||
|
Map<String, List<String>> knowledgeSources = retrieveKnowledgeForExpense(kbIds, libraryKbIds, projectLibrary);
|
||||||
|
|
||||||
|
// 2. 构建完整的知识上下文
|
||||||
|
String knowledgeContext = buildCompleteKnowledgeContext(knowledgeSources, history, suggestion);
|
||||||
|
|
||||||
|
// 3. 调用工作流生成数据
|
||||||
|
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));
|
||||||
|
|
||||||
|
return buildSuccessResponse(expenseData, startTime, "expense_audit");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("生成支出情况表失败", e);
|
||||||
|
return buildErrorResponse("生成支出情况表失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证和增强支出情况表数据
|
||||||
|
*/
|
||||||
|
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<>();
|
||||||
|
knowledgeSources.put("financial", new ArrayList<>());
|
||||||
|
knowledgeSources.put("regulation", new ArrayList<>());
|
||||||
|
knowledgeSources.put("auditCase", new ArrayList<>());
|
||||||
|
|
||||||
|
// 构建更全面的查询词
|
||||||
|
List<String> queries = buildEnhancedExpenseQueries();
|
||||||
|
|
||||||
|
// 财务数据库检索
|
||||||
|
if (StrUtil.isNotBlank(kbIds)) {
|
||||||
|
Arrays.stream(kbIds.split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.filter(StrUtil::isNotBlank)
|
||||||
|
.forEach(kbId -> {
|
||||||
|
List<String> financialKnowledge = queryKnowledgeBase(kbId, queries, 200);
|
||||||
|
knowledgeSources.get("financial").addAll(financialKnowledge);
|
||||||
|
log.debug("财务知识库 {} 检索到 {} 条相关知识", kbId, financialKnowledge.size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 公共法律法规库检索
|
||||||
|
if (StrUtil.isNotBlank(libraryKbIds)) {
|
||||||
|
Arrays.stream(libraryKbIds.split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.filter(StrUtil::isNotBlank)
|
||||||
|
.forEach(libId -> {
|
||||||
|
List<String> regulationKnowledge = queryKnowledgeBase(libId, queries, 100);
|
||||||
|
knowledgeSources.get("regulation").addAll(regulationKnowledge);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 审计案例库检索
|
||||||
|
if (StrUtil.isNotBlank(projectLibrary)) {
|
||||||
|
List<String> auditCaseKnowledge = queryKnowledgeBase(projectLibrary, queries, 50);
|
||||||
|
knowledgeSources.get("auditCase").addAll(auditCaseKnowledge);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 智能去重和排序
|
||||||
|
knowledgeSources.forEach((key, list) -> {
|
||||||
|
List<String> processed = list.stream()
|
||||||
|
.distinct()
|
||||||
|
.sorted(this::expenseComparator)
|
||||||
|
.limit(getLimitBySourceType(key))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
knowledgeSources.put(key, processed);
|
||||||
|
});
|
||||||
|
|
||||||
|
log.info("支出情况表知识检索完成 - 财务: {}条, 法规: {}条, 案例: {}条",
|
||||||
|
knowledgeSources.get("financial").size(),
|
||||||
|
knowledgeSources.get("regulation").size(),
|
||||||
|
knowledgeSources.get("auditCase").size());
|
||||||
|
|
||||||
|
return knowledgeSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建增强的支出情况表查询词
|
||||||
|
*/
|
||||||
|
private List<String> buildEnhancedExpenseQueries() {
|
||||||
|
return Arrays.asList(
|
||||||
|
"决算报表 预算报表 财务报表 年度报告",
|
||||||
|
"公务接待费 接待费用 招待费用 餐饮费用",
|
||||||
|
"出国费用 出境费用 国际差旅 境外考察",
|
||||||
|
"公车运行维护 车辆费用 汽车费用 交通费用",
|
||||||
|
"会议费 培训费 会务费 培训费用",
|
||||||
|
"八项规定 三公经费 公务支出",
|
||||||
|
"预算执行 预算调整 超预算 预算控制",
|
||||||
|
"2020年 2021年 2022年 2023年",
|
||||||
|
"支出明细 费用明细 开支情况",
|
||||||
|
"财务分析 费用分析 支出分析"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建完整的知识上下文 - 优化提示
|
||||||
|
*/
|
||||||
|
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");
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
|
||||||
|
// 3. 历史内容
|
||||||
|
if (StrUtil.isNotBlank(history)) {
|
||||||
|
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. 财务知识
|
||||||
|
if (!knowledgeSources.get("financial").isEmpty()) {
|
||||||
|
context.append("## 财务知识\n");
|
||||||
|
context.append("这是财务相关的报表和文件信息,请仔细分析并提取所有支出数据:\n");
|
||||||
|
knowledgeSources.get("financial").forEach(knowledge ->
|
||||||
|
context.append(knowledge).append("\n"));
|
||||||
|
context.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 法律法规知识
|
||||||
|
if (!knowledgeSources.get("regulation").isEmpty()) {
|
||||||
|
context.append("## 法律法规知识\n");
|
||||||
|
knowledgeSources.get("regulation").forEach(knowledge ->
|
||||||
|
context.append(knowledge).append("\n"));
|
||||||
|
context.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 审计案例知识
|
||||||
|
if (!knowledgeSources.get("auditCase").isEmpty()) {
|
||||||
|
context.append("## 审计案例知识\n");
|
||||||
|
knowledgeSources.get("auditCase").forEach(knowledge ->
|
||||||
|
context.append(knowledge).append("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("构建的知识上下文长度: {}", context.length());
|
||||||
|
return context.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支出情况表相关性比较器
|
||||||
|
*/
|
||||||
|
private int expenseComparator(String content1, String content2) {
|
||||||
|
int score1 = calculateExpenseRelevanceScore(content1);
|
||||||
|
int score2 = calculateExpenseRelevanceScore(content2);
|
||||||
|
return Integer.compare(score2, score1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算支出情况表相关性分数
|
||||||
|
*/
|
||||||
|
private int calculateExpenseRelevanceScore(String content) {
|
||||||
|
return AuditContent1ExpenseConstants.KEYWORD_WEIGHTS.entrySet().stream()
|
||||||
|
.filter(entry -> content.contains(entry.getKey()))
|
||||||
|
.mapToInt(Map.Entry::getValue)
|
||||||
|
.sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getLimitBySourceType(String sourceType) {
|
||||||
|
switch (sourceType) {
|
||||||
|
case "financial": return 150;
|
||||||
|
case "regulation": return 80;
|
||||||
|
case "auditCase": return 40;
|
||||||
|
default: return 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user