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<String, String> 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<String, String> 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<String, String> 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<String, Object> 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<DyhRepeatCaseDTO> getRepeatResult(AiRepeatVo aiRepeatVo){
|
try{
|
List<DyhRepeatCaseDTO> caseInfos = new ArrayList<>();
|
|
// 申请方当事人-证件号码(可能包含多个,用逗号分隔)
|
String plaintiffsCertiNosStr = aiRepeatVo.getPlaintiffsCertiNo();
|
List<String> plaintiffsCertiNos = Arrays.asList(plaintiffsCertiNosStr.split(","));
|
|
// 被申请方当事人-证件号码(可能包含多个,用逗号分隔)
|
String defendantsCertiNosStr = aiRepeatVo.getDefendantsCertiNo();
|
List<String> defendantsCertiNos = Arrays.asList(defendantsCertiNosStr.split(","));
|
|
// 准备请求参数
|
String caseText = aiRepeatVo.getCaseDes() + "\n" + aiRepeatVo.getCaseClaim();
|
Map<String, String> 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<String> 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<String> plaintiffsCertiNos, List<String> 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<CasePerson> 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<String, String> 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<AiCaseRisk> 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<String, Object> 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<String, Object> requestBody = new HashMap<>();
|
Map<String, Object> 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<String, String> 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;
|
}
|
}
|
}
|