package cn.huge.module.ai.service; import cn.huge.base.common.exception.ServiceException; import cn.huge.base.common.utils.GuavaCacheUtils; import cn.huge.base.common.utils.HttpClientUtils; import cn.huge.base.common.utils.ObjectUtils; import cn.huge.base.common.utils.ReturnSucUtils; import cn.huge.module.ai.domain.dto.DyhRepeatCaseDTO; import cn.huge.module.ai.domain.po.AiCaseRisk; import cn.huge.module.ai.domain.vo.AiRepeatVo; import cn.huge.module.ai.domain.vo.AiRiskVo; import cn.huge.module.cases.domain.po.CaseInfo; import cn.huge.module.cases.domain.po.CaseInfoUnfold; import cn.huge.module.cases.domain.po.CasePerson; import cn.huge.module.cases.service.CaseInfoService; import cn.huge.module.cases.service.CaseInfoUnfoldService; import cn.huge.module.cases.service.CasePersonService; import cn.huge.module.client.api.impl.KnowledgeCaseClientImpl; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpEntity; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.IOException; import java.util.*; import static cn.huge.module.constant.IsRiskConsts.IS_RISK_STATUS_1; @Slf4j @Service @Transactional(rollbackFor = Exception.class) public class AiCaseClassService { @Value("${ai.url}") private String aiUrl; @Value("${dify.url}") private String difyUrl; @Value("${dify.getCaseRiskApi}") private String getCaseRiskApi; @Value("${dify.user}") private String user; @Autowired private CaseInfoUnfoldService caseInfoUnfoldService; @Autowired private CaseInfoService caseInfoService; @Autowired private CasePersonService casePersonService; @Autowired private KnowledgeCaseClientImpl knowledgeCaseClient; /** * 发送 http post 请求,参数以原生字符串进行提交,因为ai请求时间过长,在此另外实现一个请求方法 * @param url 请求地址 * @param stringJson json字符串数据 * @param headers 请求头 * @param encode 编码 * @return * @throws Exception */ public static String httpPostRaw(String url, String stringJson, Map headers, String encode) throws Exception{ if(encode == null){ encode = "utf-8"; } CloseableHttpClient closeableHttpClient = HttpClients.createDefault(); //设置请求和传输超时时间,这里将时间设置为五分钟 RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(300000).setConnectTimeout(300000).build(); HttpPost httpost = new HttpPost(url); httpost.setConfig(requestConfig); //设置header httpost.setHeader("Content-type", "application/json"); if (ObjectUtils.isNotEmpty(headers)) { for (Map.Entry entry : headers.entrySet()) { httpost.setHeader(entry.getKey(),entry.getValue()); } } //组织请求参数 StringEntity stringEntity = new StringEntity(stringJson, encode); httpost.setEntity(stringEntity); String content = null; CloseableHttpResponse httpResponse = null; try { //响应信息 httpResponse = closeableHttpClient.execute(httpost); HttpEntity entity = httpResponse.getEntity(); content = EntityUtils.toString(entity, encode); } catch (Exception e) { e.printStackTrace(); throw new Exception("网络连接异常,请稍候重试!", e); }finally{ try { httpResponse.close(); } catch (IOException e) { e.printStackTrace(); } } try { //关闭连接、释放资源 closeableHttpClient.close(); } catch (IOException e) { e.printStackTrace(); throw new Exception("网络连接异常,请稍候重试!", e); } return content; } /** * 请求ai判断案件是否为风险、重点案件并保存标签到数据库 * * @param aiRiskVo 包含案件描述、请求和案件ID的请求类 * @return 如果案件被判定为风险或重点案件,则返回true,否则返回false */ public boolean getRiskResult(AiRiskVo aiRiskVo){ try{ if (aiRiskVo == null || aiRiskVo.getCaseText() == null || aiRiskVo.getCaseId() == null) { log.warn("输入参数为空,aiRiskVo: {}", aiRiskVo); return false; } boolean isRisk = false; // 从terms中获取案件描述、请求和案件ID String caseText = aiRiskVo.getCaseText(); String caseId = aiRiskVo.getCaseId(); // 准备请求参数 Map params = new HashMap<>(); params.put("caseText", caseText); params.put("caseId",caseId); // 发起HTTP请求到AI接口获取风险判断结果 String response = HttpClientUtils.httpPostForm(aiUrl + "/getRiskResult", params, new HashMap<>(), "utf-8"); JSONObject object = JSONObject.parseObject(response); // log.info("案件风险判断结果:"+s); int code = object.getIntValue("code"); if (code == 200) { // 获取 data 字段中的内容,true或false isRisk = object.getBooleanValue("data"); // log.info("案件风险判断结果:"+isRisk); } else { log.error("API返回异常,code: {}, response: {}", code, response); } if (isRisk){ // 如果案件被判定为风险案件,更新数据库中的风险标签 CaseInfoUnfold caseInfoUnfold = new CaseInfoUnfold(); caseInfoUnfold.setIsRisk(IS_RISK_STATUS_1); Map caseInfoUnfoldTerms = new HashMap<>(); caseInfoUnfoldTerms.put("id", caseId); // 更新案件风险标签 caseInfoUnfoldService.updateCaseInfoUnfoldTerms(caseInfoUnfold,caseInfoUnfoldTerms); } // 返回案件是否为风险或重点案件的结果 return isRisk; } catch (JSONException e) { log.error("[AiCaseClassService.getRiskResult] JSON解析失败,异常信息:{}", e.getMessage(), e); throw new ServiceException("AiCaseClassService.getRiskResult.JSON_PARSE_FAILED", e); }catch (Exception e){ // 异常处理,记录日志并抛出自定义异常 log.error("[AiCaseClassService.getRiskResult]调用失败,异常信息:"+e, e); throw new ServiceException("AiCaseClassService.getRiskResult", e); } } /** * 获取重复案件的结果 * 通过HTTP请求将案件文本发送到AI接口检查是否为重复来访案件 * * @param aiRepeatVo 包含案件信息的请求类,包括案件描述、请求、案件ID和当事人信息 * @return 返回一个包含重复来访案件信息的列表 */ public List getRepeatResult(AiRepeatVo aiRepeatVo){ try{ List caseInfos = new ArrayList<>(); // 申请方当事人-证件号码(可能包含多个,用逗号分隔) String plaintiffsCertiNosStr = aiRepeatVo.getPlaintiffsCertiNo(); List plaintiffsCertiNos = Arrays.asList(plaintiffsCertiNosStr.split(",")); // 被申请方当事人-证件号码(可能包含多个,用逗号分隔) String defendantsCertiNosStr = aiRepeatVo.getDefendantsCertiNo(); List defendantsCertiNos = Arrays.asList(defendantsCertiNosStr.split(",")); // 准备请求参数 String caseText = aiRepeatVo.getCaseDes() + "\n" + aiRepeatVo.getCaseClaim(); Map params = new HashMap<>(); params.put("caseText", caseText); try { // 发起HTTP请求到AI接口获取风险判断结果 String s = HttpClientUtils.httpPostForm(aiUrl + "/checkIsRepeat", params, new HashMap<>(), "utf-8"); JSONObject object = JSONObject.parseObject(s); int code = object.getIntValue("code"); List repeatCaseIds; if (code == 200) { // 获取 data 字段中的内容,一个列表,每个元素是一个案件ID repeatCaseIds = object.getJSONArray("data").toJavaList(String.class); } else { // 异常处理,记录日志并抛出自定义异常 log.error("[AiCaseClassService.getRiskResult]HTTP请求到AI接口获取风险判断结果失败,响应码: {}", code); throw new ServiceException("AiCaseClassService.getRiskResult"); } if (repeatCaseIds.isEmpty()) { log.info("[AiCaseClassService.getRiskResult]无重复案件信息"); return caseInfos; } // 查询对应案件的信息 for (String repeatCaseId : repeatCaseIds){ DyhRepeatCaseDTO caseInfo = buildCaseInfo(repeatCaseId, plaintiffsCertiNos, defendantsCertiNos); if (caseInfo != null) { // 添加案件信息到列表中 caseInfos.add(caseInfo); } } } catch (Exception e) { log.error("[AiCaseClassService.getRiskResult]HTTP请求或JSON解析失败,异常信息:" + e.getMessage(), e); throw new ServiceException("AiCaseClassService.getRiskResult", e); } return caseInfos; } catch (Exception e){ // 异常处理,记录日志并抛出自定义异常 log.error("[AiCaseClassService.getRiskResult]调用失败,异常信息:"+e, e); throw new ServiceException("AiCaseClassService.getRiskResult", e); } } /** * 构建案件信息对象 * 该方法通过案件ID获取案件详细信息,并根据人员类型设置原告和被告的证件号 * 同时,它会检查给定的原告和被告证件号列表,以确定是否存在相似的申请方或被申请方 * * @param repeatCaseId 案件ID * @param plaintiffsCertiNos 原告证件号列表 * @param defendantsCertiNos 被告证件号列表 * @return 返回填充了案件信息和重复分析列表的DyhRepeatCaseDTO对象 */ private DyhRepeatCaseDTO buildCaseInfo(String repeatCaseId, List plaintiffsCertiNos, List defendantsCertiNos) { // 初始化案件信息对象 DyhRepeatCaseDTO dyhRepeatCaseDTO = new DyhRepeatCaseDTO(); try { CaseInfoUnfold caseInfoUnfold = caseInfoUnfoldService.getById(repeatCaseId); CaseInfo caseInfo = caseInfoService.getById(repeatCaseId); if (caseInfo == null || caseInfoUnfold == null) { log.warn("案件信息不存在,caseId: {}", repeatCaseId); return null; } // 从案件详细信息中复制属性 BeanUtils.copyProperties(caseInfoUnfold, dyhRepeatCaseDTO); // 从案件基本信息中复制属性 BeanUtils.copyProperties(caseInfo, dyhRepeatCaseDTO); // 获取案件涉及的所有人员信息 List casePersons = casePersonService.listByCaseId(repeatCaseId); for (CasePerson casePerson : casePersons) { // 为证件号为空的人员设置默认值 if (Objects.equals(casePerson.getCertiNo(), null)) { casePerson.setCertiNo("-"); } // 根据人员类型设置案件信息中的原告或被告证件号 if ("15_020008-1".equals(casePerson.getPerType())) { dyhRepeatCaseDTO.setPlaintiffsCertiNo(casePerson.getCertiNo()); } else if ("15_020008-2".equals(casePerson.getPerType())) { dyhRepeatCaseDTO.setDefendantsCertiNo(casePerson.getCertiNo()); } } // 初始化重复分析列表 dyhRepeatCaseDTO.setRepeatAnalyseList(new ArrayList<>()); // 检查原告证件号是否匹配 if (!plaintiffsCertiNos.isEmpty() && plaintiffsCertiNos.contains(dyhRepeatCaseDTO.getPlaintiffsCertiNo())) { dyhRepeatCaseDTO.getRepeatAnalyseList().add("申请方当事人相似"); } // 检查被告证件号是否匹配 if (!defendantsCertiNos.isEmpty() && defendantsCertiNos.contains(dyhRepeatCaseDTO.getDefendantsCertiNo())) { dyhRepeatCaseDTO.getRepeatAnalyseList().add("被申请方当事人相似"); } // 添加事项概况相似到分析列表 dyhRepeatCaseDTO.getRepeatAnalyseList().add("事项概况相似"); // 返回填充好的案件信息对象 return dyhRepeatCaseDTO; } catch (Exception e) { log.error("构建案件信息对象失败,异常信息:" + e.getMessage(), e); throw new ServiceException("AiCaseClassService.buildCaseInfo", e); } } /** * 向量化案件到向量数据库 * * @param aiRepeatVo 包含案件描述、请求和案件ID的请求类 * @return 如果案件被判定为风险或重点案件,则返回true,否则返回false */ public boolean embeddingTextToMilvus(AiRepeatVo aiRepeatVo){ try{ if (aiRepeatVo == null || aiRepeatVo.getCaseDes() == null || aiRepeatVo.getCaseClaim() == null || aiRepeatVo.getCaseId() == null) { log.warn("输入参数为空,aiRepeatVo: {}", aiRepeatVo); return false; } boolean toMilvus = false; // 从terms中获取案件描述、请求和案件ID String caseText = aiRepeatVo.getCaseDes() + "\n" + aiRepeatVo.getCaseClaim(); String caseId = aiRepeatVo.getCaseId(); // 准备请求参数 Map params = new HashMap<>(); params.put("caseId", caseId); params.put("originalText", caseText); // 发起HTTP请求到AI接口 String response = HttpClientUtils.httpPostForm(aiUrl + "/embeddingTextToMilvus", params, new HashMap<>(), "utf-8"); JSONObject object = JSONObject.parseObject(response); int code = object.getIntValue("code"); if (code == 200) { // 获取 data 字段中的内容,true或false // object.getBooleanValue("data"); toMilvus = true; // log.info("案件风险判断结果:"+isRisk); } else { log.error("API返回异常,code: {}, response: {}", code, response); } return toMilvus; } catch (JSONException e) { log.error("[AiCaseClassService.getRiskResult] JSON解析失败,异常信息:{}", e.getMessage(), e); throw new ServiceException("AiCaseClassService.getRiskResult.JSON_PARSE_FAILED", e); }catch (Exception e){ // 异常处理,记录日志并抛出自定义异常 log.error("[AiCaseClassService.getRiskResult]调用失败,异常信息:"+e, e); throw new ServiceException("AiCaseClassService.getRiskResult", e); } } /** * 获取案件风险评估结果 * 处理流程: * 1. 优先从缓存获取评估结果 * 2. 检查数据库是否存在历史评估记录 * 3. 如无历史记录,则请求AI服务进行评估 * 4. 评估完成后更新案件风险等级 * * @param aiRiskVo 包含案件描述和ID的请求对象 * @return ReturnSucUtils封装的评估结果 */ public Object getCaseRisk(AiRiskVo aiRiskVo) { String cacheKey = "getCaseRisk" + aiRiskVo.getCaseId(); try { // 先从缓存获取 Object cacheAi = GuavaCacheUtils.getCacheAi(cacheKey); if (ObjectUtils.isNotEmpty(cacheAi)) { return ReturnSucUtils.getRepInfo("请求成功", cacheAi); } // 参数校验 if (!validateRiskRequest(aiRiskVo)) { return ReturnSucUtils.getRepInfo("参数错误", null); } AiCaseRisk risk = new AiCaseRisk(); risk.setCaseId(aiRiskVo.getCaseId()); // 检查数据库历史记录 AiCaseRisk riskResult = getExistingRiskAssessment(risk); if (riskResult != null) { return processRiskResult(riskResult, cacheKey); } // 请求AI服务并处理结果 return processAiAssessment(aiRiskVo, cacheKey, risk); } catch (Exception e) { log.error("获取案件风险评估失败: {}", e.getMessage(), e); return ReturnSucUtils.getRepInfo("风险评估异常", null); } } /** * 验证风险评估请求参数 */ private boolean validateRiskRequest(AiRiskVo aiRiskVo) { if (aiRiskVo == null || aiRiskVo.getCaseText() == null || aiRiskVo.getCaseId() == null) { log.warn("输入参数为空,aiRiskVo: {}", aiRiskVo); return false; } return true; } /** * 获取已存在的风险评估记录 * @param risk 案件ID * @return 最新的风险评估记录,如果不存在则返回null */ private AiCaseRisk getExistingRiskAssessment(AiCaseRisk risk) { List existingRisks = knowledgeCaseClient.checkRisks(risk); return !existingRisks.isEmpty() ? existingRisks.get(0) : null; } /** * 处理风险评估结果 * @param riskResult 风险评估结果 * @param cacheKey 缓存键 * @return 处理后的响应对象 */ private Object processRiskResult(AiCaseRisk riskResult, String cacheKey) { checkAndUpdateRiskLevel(riskResult, riskResult.getCaseId()); GuavaCacheUtils.putCacheAi(cacheKey, riskResult); return ReturnSucUtils.getRepInfo("请求成功", riskResult); } /** * 处理AI评估请求及其结果 * @param aiRiskVo 请求参数 * @param cacheKey 缓存键 * @return 处理后的响应对象 */ private Object processAiAssessment(AiRiskVo aiRiskVo, String cacheKey,AiCaseRisk risk) { JSONObject responseObj = requestAiRiskAssessment(aiRiskVo); if (responseObj != null) { AiCaseRisk latestRisk = getExistingRiskAssessment(risk); if (latestRisk != null) { return processRiskResult(latestRisk, cacheKey); } else { log.warn("AI评估成功但未获取到风险评估记录, caseId: {}", aiRiskVo.getCaseId()); } } return "风险评估失败"; } /** * 检查并更新案件风险等级 * @param riskResult 风险评估结果 * @param caseId 案件ID */ private void checkAndUpdateRiskLevel(AiCaseRisk riskResult, String caseId) { if (riskResult != null && riskResult.getIsRiskAi() != null) { try { int riskLevel = Integer.parseInt(riskResult.getIsRiskAi()); if (riskLevel > 3) { CaseInfoUnfold caseInfoUnfold = new CaseInfoUnfold(); caseInfoUnfold.setIsRisk(IS_RISK_STATUS_1); Map caseInfoUnfoldTerms = new HashMap<>(); caseInfoUnfoldTerms.put("id", caseId); caseInfoUnfoldService.updateCaseInfoUnfoldTerms(caseInfoUnfold, caseInfoUnfoldTerms); } } catch (NumberFormatException e) { log.error("风险等级解析失败: {}", riskResult.getIsRiskAi()); } } } /** * 请求AI风险评估服务 * @param aiRiskVo 风险评估请求参数 * @return AI服务响应结果 */ private JSONObject requestAiRiskAssessment(AiRiskVo aiRiskVo) { try { // 构建请求参数 Map requestBody = new HashMap<>(); Map inputs = new HashMap<>(); inputs.put("caseText", aiRiskVo.getCaseText()); inputs.put("caseId", aiRiskVo.getCaseId()); requestBody.put("inputs", inputs); requestBody.put("response_mode", "blocking"); requestBody.put("user", user); // 设置请求头 Map headers = new HashMap<>(); headers.put("Authorization", "Bearer " + getCaseRiskApi); // 发送请求到Dify服务 String response = httpPostRaw(difyUrl + "/workflows/run", JSONObject.toJSONString(requestBody), headers, "utf-8"); return JSONObject.parseObject(response); } catch (Exception e) { log.error("请求AI风险评估服务失败: {}", e.getMessage(), e); return null; } } }