diff --git a/src/main/java/com/gxwebsoft/ai/service/impl/AuditContent9PersonnelServiceImpl.java b/src/main/java/com/gxwebsoft/ai/service/impl/AuditContent9PersonnelServiceImpl.java index 20f253f..ef5c1e7 100644 --- a/src/main/java/com/gxwebsoft/ai/service/impl/AuditContent9PersonnelServiceImpl.java +++ b/src/main/java/com/gxwebsoft/ai/service/impl/AuditContent9PersonnelServiceImpl.java @@ -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, @@ -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(); @@ -329,11 +350,160 @@ public class AuditContent9PersonnelServiceImpl extends AbstractAuditContentServi return context.toString(); } + /** + * 构建福利费超范围支出明细清单的上下文 + */ + private String buildSubItemContextExt(String auditContent, + AuditContent9PersonnelConstants.AuditSubContent subContent, + Map> 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(); @@ -346,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 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 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; +// } +// } } \ No newline at end of file