You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
virtual-patient/virtual-patient-web/src/main/java/com/supervision/service/impl/AskServiceImpl.java

247 lines
12 KiB
Java

2 years ago
package com.supervision.service.impl;
import cn.hutool.core.codec.Base64Encoder;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.JSONUtil;
import com.supervision.domain.GlobalResult;
import com.supervision.exception.BusinessException;
1 year ago
import com.supervision.feign.AskQaSimilarityFeignClient;
import com.supervision.feign.RasaManageFeignClient;
import com.supervision.model.Process;
import com.supervision.model.*;
1 year ago
import com.supervision.pojo.qaSimilarity.QaSimilarityQuestion;
import com.supervision.pojo.qaSimilarity.QaSimilarityQuestionAnswer;
import com.supervision.pojo.vo.TalkResultResVO;
import com.supervision.pojo.vo.TalkVideoReqVO;
import com.supervision.service.*;
1 year ago
import com.supervision.util.AsrUtil;
import com.supervision.util.MinioUtil;
import com.supervision.util.UserUtil;
import com.supervision.vo.rasa.RasaTalkVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
2 years ago
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
2 years ago
@Slf4j
2 years ago
@Service
@RequiredArgsConstructor
2 years ago
public class AskServiceImpl implements AskService {
private final ProcessService processService;
private final AskTemplateQuestionLibraryService askTemplateQuestionLibraryService;
private final AskPatientAnswerService askPatientAnswerService;
private final ConfigPhysicalToolService configPhysicalToolService;
private final ConfigAncillaryItemService configAncillaryItemService;
private final FileResourceService fileResourceService;
private final RasaManageFeignClient rasaManageFeignClient;
1 year ago
private final AskQaSimilarityFeignClient askQaSimilarityFeignClient;
private final CommonDicService commonDicService;
@Override
public String receiveVoiceFile(MultipartFile file) {
if (file.getSize() <= 0) {
throw new BusinessException("语音内容为空");
}
// 获取音频对应的文字
String text = null;
try {
text = AsrUtil.asrTransformByBytes(file.getBytes());
} catch (Exception e) {
throw new BusinessException("获取语音失败");
}
if (StrUtil.isEmpty(text)) {
throw new BusinessException("语音内容为空");
}
return text;
}
private void saveQaRecord(String processId, String answerType, String answerId, String question, AskTemplateQuestionLibrary library, String resText) {
DiagnosisQaRecord record = new DiagnosisQaRecord();
record.setProcessId(processId);
record.setAnswerType(answerType);
record.setAnswerId(answerId);
if (ObjectUtil.isNotEmpty(library)) {
record.setQuestionLibraryId(library.getId());
}
record.setQuestion(question);
record.setAnswer(resText);
record.setCreateUserId(UserUtil.getUser().getId());
record.insert();
}
1 year ago
private void setActionRelation(TalkResultResVO talkResultResVO) {
Integer type = talkResultResVO.getType();
String actionId = talkResultResVO.getActionId();
1 year ago
if (StrUtil.isEmpty(actionId) || null == type) {
return;
}
// 体格检查
1 year ago
if (type.equals(2)) {
ConfigPhysicalTool configPhysicalTool = configPhysicalToolService.getById(actionId);
1 year ago
if (null != configPhysicalTool) {
talkResultResVO.setItemName(configPhysicalTool.getToolName());
talkResultResVO.setItemImage(configPhysicalTool.getIconBase64());
talkResultResVO.setRequireLocation(configPhysicalTool.getRequireLocation());
// 生成树的时候,没有ID字段,所以用名称生成md5作为ID
talkResultResVO.setActionType(new MD5().digestHex16(configPhysicalTool.getType()));
}
}
// 辅助检查
1 year ago
if (type.equals(3)) {
ConfigAncillaryItem configAncillaryItem = configAncillaryItemService.getById(actionId);
1 year ago
if (null != configAncillaryItem) {
talkResultResVO.setItemName(configAncillaryItem.getItemName());
// 生成树的时候,没有ID字段,所以用名称生成md5作为ID
talkResultResVO.setActionType(new MD5().digestHex16(configAncillaryItem.getType()));
}
}
}
@Override
public TalkResultResVO talkByVideo(TalkVideoReqVO talkReqVO) {
// 根据processId找到对应的病人
Process process = Optional.ofNullable(processService.getById(talkReqVO.getProcessId())).orElseThrow(() -> new BusinessException("未找到诊疗进程"));
1 year ago
String talkResult = talkQaSimilarity(talkReqVO.getText(), UserUtil.getUser().getId());
// 调用rasa获取文字内容
1 year ago
//String rasaResult = talkRasa(talkReqVO.getText(), UserUtil.getUser().getId());
log.info("调用对话的回复是:{}", talkResult);
TalkResultResVO talkResultResVO = new TalkResultResVO();
// 如果rasa没有识别出来,则返回默认值
1 year ago
if (StrUtil.isBlank(talkResult)) {
AskPatientAnswer medicalRecErrorAnswer = getMedicalRecErrorAnswer(process.getMedicalRecId());
talkResultResVO.setVideoBase64(getAnswerVideoBase64OrDefault(medicalRecErrorAnswer.getAnswerResourceId()));
talkResultResVO.setAnswerMessage(medicalRecErrorAnswer.getAnswer());
saveQaRecord(talkReqVO.getProcessId(), "default", null, talkReqVO.getText(), null, medicalRecErrorAnswer.getAnswer());
talkResultResVO.setType(1);
return talkResultResVO;
}
// 这里校验,rasa回复的结果是不是action
// 这里设置的模板,对于action的动作全部是用ancillary_ | tool_进行标记,详情看生成rasa的yml的代码:RasaServiceImpl.generateDomain
// ancillary_ | tool_
1 year ago
if (talkResult.startsWith("ancillary_") || talkResult.startsWith("tool_")) {
log.info("呼出语句:{}", talkResult);
List<String> actionList = StrUtil.split(talkResult, '_');
if (actionList.size() > 1) {
1 year ago
// 在这里设置为动作
talkResultResVO.setActionId(actionList.get(1));
talkResultResVO.setType("ancillary".equals(actionList.get(0)) ? 3 : 2);
setActionRelation(talkResultResVO);
return talkResultResVO;
}
} else {
1 year ago
AskTemplateQuestionLibrary library = askTemplateQuestionLibraryService.getById(talkResult);
if (ObjectUtil.isEmpty(library)) {
1 year ago
log.info("{}:未从问题库中找到,回答未识别语句", talkResult);
AskPatientAnswer medicalRecErrorAnswer = getMedicalRecErrorAnswer(process.getMedicalRecId());
talkResultResVO.setVideoBase64(getAnswerVideoBase64OrDefault(medicalRecErrorAnswer.getAnswerResourceId()));
talkResultResVO.setAnswerMessage(medicalRecErrorAnswer.getAnswer());
saveQaRecord(talkReqVO.getProcessId(), "default", null, talkReqVO.getText(), null, medicalRecErrorAnswer.getAnswer());
} else {
1 year ago
AskPatientAnswer askPatientAnswer = askPatientAnswerService.lambdaQuery().eq(AskPatientAnswer::getMedicalId, process.getMedicalRecId())
.eq(AskPatientAnswer::getLibraryQuestionId, library.getId()).last("limit 1").one();
1 year ago
if (ObjectUtil.isNotEmpty(askPatientAnswer) && StrUtil.isNotEmpty(askPatientAnswer.getAnswerResourceId())) {
String resText = askPatientAnswer.getAnswer();
1 year ago
log.info("{}:找到了病历配置的回答语句:{},回答内容:{}", talkResult, askPatientAnswer.getId(), resText);
talkResultResVO.setVideoBase64(getAnswerVideoBase64OrDefault(askPatientAnswer.getAnswerResourceId()));
talkResultResVO.setAnswerMessage(askPatientAnswer.getAnswer());
// 保存记录
saveQaRecord(talkReqVO.getProcessId(), "patient", askPatientAnswer.getId(), talkReqVO.getText(), library, resText);
1 year ago
} else {
log.info("{}:病历配置,从AskPatientAnswer中未找到回答结果,回复未识别到语句", talkResult);
AskPatientAnswer medicalRecErrorAnswer = getMedicalRecErrorAnswer(process.getMedicalRecId());
talkResultResVO.setVideoBase64(getAnswerVideoBase64OrDefault(medicalRecErrorAnswer.getAnswerResourceId()));
talkResultResVO.setAnswerMessage(medicalRecErrorAnswer.getAnswer());
saveQaRecord(talkReqVO.getProcessId(), "default", null, talkReqVO.getText(), library, medicalRecErrorAnswer.getAnswer());
}
}
}
talkResultResVO.setType(1);
return talkResultResVO;
}
public String talkRasa(String question, String sessionId) {
RasaTalkVo rasaTalkVo = new RasaTalkVo();
rasaTalkVo.setQuestion(question);
rasaTalkVo.setSessionId(sessionId);
// 默认为1
rasaTalkVo.setModelId("1");
try {
GlobalResult<List<String>> talk = rasaManageFeignClient.talk(rasaTalkVo);
log.info("调用rasa对话返回结果:{}", talk);
if (talk.getCode() != 200 || CollUtil.isEmpty(talk.getData())) {
return null;
}
return CollUtil.getFirst(talk.getData());
} catch (Exception e) {
log.error("talkRasa error ", e);
}
return null;
}
1 year ago
public String talkQaSimilarity(String question, String sessionId) {
log.info("开始调用talkQaSimilarity,问题:{}", question);
1 year ago
GlobalResult<List<QaSimilarityQuestionAnswer>> result = askQaSimilarityFeignClient.askQuestionSimilarityAnswer(new QaSimilarityQuestion(question));
log.info("调用talkQaSimilarity结束,问题:{},返回结果:{}", question, JSONUtil.toJsonStr(result));
1 year ago
if (200 != result.getCode()) {
throw new BusinessException("相似度匹配失败");
}
return CollUtil.getFirst(result.getData()).getMatchQuestionCode();
}
private AskPatientAnswer getMedicalRecErrorAnswer(String medicalRecId) {
//Optional.ofNullable(medicalRecErrorAnswer).orElseGet(() ->new AskPatientAnswer()).getAnswer()
Assert.notEmpty(medicalRecId, "病历id不能为空");
CommonDic commonDic = commonDicService.lambdaQuery().eq(CommonDic::getGroupCode, "AQT").eq(CommonDic::getCode, "system_error").one();
Assert.notNull(commonDic, "字典未配置系统内置异常识别场景");
AskTemplateQuestionLibrary askTemplateQuestionLibrary = askTemplateQuestionLibraryService.lambdaQuery().eq(AskTemplateQuestionLibrary::getDictId, commonDic.getId()).one();
Assert.notNull(askTemplateQuestionLibrary, "知识库未配置系统内置异常识别场景");
return askPatientAnswerService.lambdaQuery().eq(AskPatientAnswer::getMedicalId, medicalRecId).eq(AskPatientAnswer::getLibraryQuestionId, askTemplateQuestionLibrary.getId()).one();
}
/**
* base64
*
* @param fileResourceId id
* @return base64
*/
private String getAnswerVideoBase64OrDefault(String fileResourceId) {
FileResource fileResource = fileResourceService.getById(fileResourceId);
try (InputStream inputStream = MinioUtil.download(fileResource.getMinioId())) {
return Base64Encoder.encode(IoUtil.readBytes(inputStream));
} catch (Exception e) {
log.error("获取视频失败", e);
throw new BusinessException("未找到回复视频");
}
}
2 years ago
}