package com.smartor.service.impl; import com.alibaba.fastjson2.JSON; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.ruoyi.common.core.redis.RedisCache; import com.ruoyi.common.exception.base.BaseException; import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.DtoConversionUtils; import com.ruoyi.common.utils.RSAPublicKeyExample; import com.smartor.config.PhoneUtils; import com.smartor.domain.*; import com.smartor.mapper.*; import com.smartor.service.IIvrTaskTemplateScriptService; import com.smartor.service.IIvrTaskTemplateService; import com.smartor.service.IServiceSubtaskService; import com.smartor.service.IServiceTaskService; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; 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.text.SimpleDateFormat; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import static cn.hutool.poi.excel.sax.AttributeName.r; import static cn.hutool.poi.excel.sax.AttributeName.s; /** * 单一任务(随访)Service业务层处理 * * @author ruoyi * @date 2024-02-02 */ @Slf4j @Service public class ServiceSubtaskServiceImpl implements IServiceSubtaskService { @Autowired private ServiceSubtaskMapper serviceSubtaskMapper; @Autowired private ServiceSubtaskDetailMapper serviceSubtaskDetailMapper; @Autowired private IServiceTaskService serviceTaskService; @Autowired private IvrTaskTemplateTargetoptionMapper serviceTaskScriptTargetoptionMapper; @Autowired private IvrTaskVisitResultMapper serviceTaskVisitResultMapper; @Autowired private RedisCache redisCache; @Autowired private IvrLibaExtemplatescriptMapper ivrLibaExtemplatescriptMapper; @Autowired private IIvrTaskTemplateService ivrTaskTemplateService; @Autowired private IIvrTaskTemplateScriptService iIvrTaskTemplateScriptService; @Value("${pri_key}") private String pri_key; /** * 查询单一任务(随访) * * @param id 单一任务(随访)主键 * @return 单一任务(随访) */ @Override public ServiceSubtask selectServiceSubtaskById(Long id) { return serviceSubtaskMapper.selectServiceSubtaskById(id); } /** * 查询单一任务(随访)列表 * * @param ServiceSubtask 单一任务(随访) * @return 单一任务(随访) */ @Override public List selectServiceSubtaskList(ServiceSubtask ServiceSubtask) { return serviceSubtaskMapper.selectServiceSubtaskList(ServiceSubtask); } @Override public ServiceTaskVO queryTaskByCondition(ServiceSubtask ServiceSubtask) { //定义患者与单一任务关联表集合 List patTaskRelevances = new ArrayList<>(); List list = selectServiceSubtaskList(ServiceSubtask); if (CollectionUtils.isEmpty(list) || list.size() == 0) { return new ServiceTaskVO(); } ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(ServiceSubtask.getTaskid()); //将查出来的数据倒入ServiceSubtaskVO中 ServiceTaskVO serviceTaskVO = DtoConversionUtils.sourceToTarget(serviceTask, ServiceTaskVO.class); serviceTaskVO.setShowDate(serviceTask.getShowDate()); serviceTaskVO.setShowTimeMorn(serviceTask.getShowTimeMorn()); serviceTaskVO.setShowTimeNoon(serviceTask.getShowTimeNoon()); serviceTaskVO.setShowTimeNight(serviceTask.getShowTimeNight()); serviceTaskVO.setPreachform(serviceTask.getPreachform()); String sendTimeSlot = serviceTask.getSendTimeSlot(); ObjectMapper objectMapper = new ObjectMapper(); try { //获取到发送时间的集合 if (com.ruoyi.common.utils.StringUtils.isNotEmpty(sendTimeSlot)) { List taskSendTimeVOList = objectMapper.readValue(sendTimeSlot, List.class); serviceTaskVO.setSendTimeslot(taskSendTimeVOList); serviceTaskVO.setSendType(serviceTask.getSendType()); } //文本变量参数 if (com.ruoyi.common.utils.StringUtils.isNotEmpty(serviceTask.getTextParam())) { Map> textParam = objectMapper.readValue(serviceTask.getTextParam(), Map.class); serviceTaskVO.setTextParam(textParam); } } catch (JsonProcessingException e) { e.printStackTrace(); } for (ServiceSubtask serviceSubtask1 : list) { PatTaskRelevance patTaskRelevance = new PatTaskRelevance(); if (!serviceSubtask1.getHospType().equals("2")) { log.info("随访查询不为出院,{}", serviceSubtask1.getHospType()); //获取到患者信息,并放入到集合中 patTaskRelevance.setName(serviceSubtask1.getSendname()); patTaskRelevance.setAge(serviceSubtask1.getAge()); patTaskRelevance.setSfzh(serviceSubtask1.getSfzh()); patTaskRelevance.setPhone(serviceSubtask1.getPhone()); patTaskRelevance.setAddr(serviceSubtask1.getAddr()); patTaskRelevance.setDiagname(serviceSubtask1.getDiagname()); patTaskRelevance.setPatid(serviceSubtask1.getPatid()); patTaskRelevance.setSendStatus(serviceSubtask1.getSendstate()); patTaskRelevances.add(patTaskRelevance); } if (serviceSubtask1.getHospType().equals("2")) { log.info("随访查询为出院,{}", serviceSubtask1.getHospType()); patTaskRelevance.setName(serviceSubtask1.getSendname()); patTaskRelevance.setAge(serviceSubtask1.getAge()); patTaskRelevance.setSfzh(serviceSubtask1.getSfzh()); patTaskRelevance.setPhone(serviceSubtask1.getPhone()); patTaskRelevance.setAddr(serviceSubtask1.getAddr()); patTaskRelevance.setDeptName(serviceSubtask1.getDeptname()); patTaskRelevance.setBedNo(serviceSubtask1.getBedNo()); patTaskRelevance.setDiagname(serviceSubtask1.getDiagname()); patTaskRelevance.setPatid(serviceSubtask1.getPatid()); patTaskRelevance.setSendStatus(serviceSubtask1.getSendstate()); patTaskRelevances.add(patTaskRelevance); } } serviceTaskVO.setPatTaskRelevances(patTaskRelevances); return serviceTaskVO; } @Override public List patItem(ServiceSubtask serviceSubtask) { return this.selectServiceSubtaskList(serviceSubtask); } /** * 新增单一任务(随访) * * @param serviceSubtask 单一任务(随访) * @return 结果 */ @Override public int insertServiceSubtask(ServiceSubtask serviceSubtask) { serviceSubtask.setCreateTime(DateUtils.getNowDate()); return serviceSubtaskMapper.insertServiceSubtask(serviceSubtask); } /** * 修改单一任务(随访) * * @param serviceSubtask 单一任务(随访) * @return 结果 */ @Override public Boolean updateServiceSubtask(ServiceSubtask serviceSubtask) { serviceSubtask.setUpdateTime(DateUtils.getNowDate()); return serviceSubtaskMapper.updateServiceSubtask(serviceSubtask); } /** * 批量删除单一任务(随访) * * @return 结果 */ @Override public int deleteServiceSubtaskByIds(Long[] ids) { Integer i = 0; for (Long id : ids) { i = serviceSubtaskMapper.deleteServiceSubtaskById(id); } return i; } /** * 单一任务 * * @return 结果 */ @Transactional(rollbackFor = Exception.class) @Override public int insertOrUpdateTask(ServiceTaskVO serviceTaskVO) { if (ObjectUtils.isEmpty(serviceTaskVO)) { log.info("任务入参为空,请检查入参"); throw new BaseException("任务入参为空,请检查入参"); } Integer integer = 1; ServiceTask serviceTask = DtoConversionUtils.sourceToTarget(serviceTaskVO, ServiceTask.class); serviceTask.setTextParam(JSON.toJSONString(serviceTaskVO.getTextParam())); if (serviceTaskVO.getIsoperation() != null && serviceTaskVO.getIsoperation() == 1) { //往任务表中,新增任务 if (ObjectUtils.isNotEmpty(serviceTaskVO.getSendTimeslot())) serviceTask.setSendTimeSlot(JSON.toJSONString(serviceTaskVO.getSendTimeslot())); if (serviceTask.getSendState() == null) serviceTask.setSendState(1); serviceTask.setLibtemplateid(serviceTaskVO.getLibtemplateid().toString()); serviceTask.setTemplateid(serviceTaskVO.getTemplateid()); serviceTaskService.insertServiceTask(serviceTask); //将任务信息放到服务表中 ServiceSubtask serviceSubtask = DtoConversionUtils.sourceToTarget(serviceTaskVO, ServiceSubtask.class); serviceSubtask.setTaskid(serviceTask.getTaskid().longValue()); serviceSubtask.setTemplatename(serviceTaskVO.getTemplatename()); //新增 if (CollectionUtils.isNotEmpty(serviceTaskVO.getPatTaskRelevances())) { for (PatTaskRelevance patTaskRelevance : serviceTaskVO.getPatTaskRelevances()) { //将任务信息新增到随访服务表中 serviceSubtask.setSendname(patTaskRelevance.getName()); serviceSubtask.setAge(patTaskRelevance.getAge()); serviceSubtask.setSfzh(patTaskRelevance.getSfzh()); serviceSubtask.setType(serviceTaskVO.getHospType()); serviceSubtask.setHospType(patTaskRelevance.getHospType()); serviceSubtask.setOpenid(patTaskRelevance.getOpenid()); serviceSubtaskMapper.insertServiceSubtask(serviceSubtask); integer = serviceSubtask.getId().intValue(); } } } else if (serviceTaskVO.getIsoperation() != null && serviceTaskVO.getIsoperation() == 2) { //任务修改 if (ObjectUtils.isNotEmpty(serviceTaskVO.getSendTimeslot())) serviceTask.setSendTimeSlot(JSON.toJSONString(serviceTaskVO.getSendTimeslot())); //修改操作,需要将stopState状态+1 ServiceTask serviceTask1 = serviceTaskService.selectServiceTaskByTaskid(serviceTask.getTaskid()); serviceTask.setStopState(serviceTask1.getStopState() + 1); serviceTask.setTemplateid(serviceTaskVO.getLibtemplateid()); serviceTaskService.updateServiceTask(serviceTask); if (CollectionUtils.isNotEmpty(serviceTaskVO.getPatTaskRelevances())) { for (PatTaskRelevance patTaskRelevance : serviceTaskVO.getPatTaskRelevances()) { ServiceSubtask serviceSubtask = DtoConversionUtils.sourceToTarget(serviceTaskVO, ServiceSubtask.class); serviceSubtask.setSendname(patTaskRelevance.getName()); serviceSubtask.setAddr(patTaskRelevance.getAddr()); serviceSubtask.setPatid(patTaskRelevance.getPatid()); serviceSubtask.setOpenid(patTaskRelevance.getOpenid()); serviceSubtask.setType(serviceTaskVO.getHospType()); serviceSubtask.setCreateTime(DateUtils.getNowDate()); serviceSubtask.setHospType(patTaskRelevance.getHospType()); serviceSubtask.setTextParam(new Gson().toJson(serviceTaskVO.getTextParam())); if (patTaskRelevance.getIsoperation() != null) { if (patTaskRelevance.getIsoperation() == 2) serviceSubtaskMapper.updateServiceSubtaskByCondition(serviceSubtask); if (patTaskRelevance.getIsoperation() == 1) { serviceSubtask.setSendstate(1L); serviceSubtaskMapper.insertServiceSubtask(serviceSubtask); } if (patTaskRelevance.getIsoperation() == 3) // 通过taskid和patid去删除该条数据 serviceSubtaskMapper.deleteServiceSubtaskByCondition(serviceTaskVO.getTaskid(), patTaskRelevance.getPatid()); } integer = serviceSubtask.getTaskid().intValue(); } } } return integer; } @Override public void phoneCallBack(PhoneCallBackVO phoneCallBackVO) { phoneCallBackVO.setTextResult(phoneCallBackVO.getTextResult().substring(0, phoneCallBackVO.getTextResult().length() - 1)); SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss"); //获取数据 Boolean aBoolean = redisCache.hasKey(phoneCallBackVO.getUuid()); if (!aBoolean) { throw new BaseException("该uuid不存在"); } Integer hangupValue = redisCache.getCacheObject(phoneCallBackVO.getUuid() + "hangup"); PhoneUtils phoneUtils = new PhoneUtils(); if (hangupValue != null && hangupValue == 1) { String date = simpleDateFormat1.format(new Date()); log.info("电话要挂断了: {}", date); //hangupValue == 1 随访结束,直接可以挂电话 phoneUtils.hangup("", "", "", "", "", "", "", phoneCallBackVO.getUuid()); return; } Map map = redisCache.getCacheObject(phoneCallBackVO.getUuid()); ServiceSubtask serviceSubtask = (ServiceSubtask) map.get("ServiceSubtask"); List IvrTaskTemplateScriptVOs = (List) map.get("IvrTaskTemplateScriptVO"); //将uuid更新到数据库中 serviceSubtask.setSenduuid(phoneCallBackVO.getUuid()); serviceSubtaskMapper.updateServiceSubtask(serviceSubtask); //获取模板信息 IvrTaskTemplateVO ivrTaskTemplateVO = redisCache.getCacheObject(phoneCallBackVO.getUuid() + "IvrTaskTemplateVO"); //语音识别结果上报接口: 3 Integer noVoice = redisCache.getCacheObject(phoneCallBackVO.getUuid() + "noVoice"); QuestionMessage returnQues = redisCache.getCacheObject(phoneCallBackVO.getUuid() + "returnQues"); //将传回的结果放到回复对象中 returnQues.setContent(phoneCallBackVO.getTextResult()); IvrTaskTemplateScriptVO nowQuestion = returnQues.getNowQuestion(); if (StringUtils.isEmpty(returnQues.getContent())) { //无回话 //判断noVoice是否已经到了最大值 if (noVoice == ivrTaskTemplateVO.getNoVoiceNum().intValue()) { //已经问了对应的遍数,就判断是否还有下一题 if (nowQuestion.getTargetid() == IvrTaskTemplateScriptVOs.size()) { phoneUtils.ttsPlayback(ivrTaskTemplateVO.getRevisitAfter(), phoneCallBackVO.getUuid()); return; } else { //有下一题 String date = simpleDateFormat1.format(new Date()); log.info("去调用tts合成和播放接口: {},uuid为:{}", date, phoneCallBackVO.getUuid()); phoneUtils.ttsPlayback(nowQuestion.getScriptContent(), phoneCallBackVO.getUuid()); } } else { redisCache.setCacheObject(phoneCallBackVO.getUuid() + "noVoice", noVoice + 1, 120, TimeUnit.MINUTES); //调用ivrLibaTemplateScriptVO中的slienceText(静默话术) String slienceText = nowQuestion.getSlienceText(); //静默话术 + 问题, 去调用“tts合成和播放”接口 String date = simpleDateFormat1.format(new Date()); log.info("静默话术 + 问题,去调用tts合成和播放接口: {},uuid为:{}", date, phoneCallBackVO.getUuid()); phoneUtils.ttsPlayback(slienceText + nowQuestion.getScriptContent(), phoneCallBackVO.getUuid()); return; } } else { //isppd用来记录是否匹配到 Boolean isppd = false; //有回话,对回答的问题,进行正则匹配(这里只针对选择题,其它题型不行) for (int j = 0; j < nowQuestion.getIvrTaskScriptTargetoptionList().size(); j++) { //包含 Matcher matcher = null; if (StringUtils.isNotEmpty(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex())) { Pattern pattern = Pattern.compile(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex()); matcher = pattern.matcher(phoneCallBackVO.getTextResult()); } //不包含 Matcher matcher2 = null; if (StringUtils.isNotEmpty(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex2())) { Pattern pattern2 = Pattern.compile(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex2()); matcher2 = pattern2.matcher(phoneCallBackVO.getTextResult()); } } if (isppd != true) { //没有匹配到 Integer mateNum = redisCache.getCacheObject(phoneCallBackVO.getUuid() + "mateNum"); if (mateNum == null) mateNum = 0; //无匹配次数去判断是否到最大询问次数,并且所有的选项都匹配完了 if (mateNum == ivrTaskTemplateVO.getMateNum().intValue()) { //如果下一题为空.则新的数据返回,并加上感谢语 if (nowQuestion.getTargetid() < IvrTaskTemplateScriptVOs.size()) { QuestionMessage questionMessage = new QuestionMessage(); IvrTaskTemplateScriptVO nextQuestion = getNextQuestion(IvrTaskTemplateScriptVOs, nowQuestion); questionMessage.setQuestionList(IvrTaskTemplateScriptVOs); questionMessage.setNowQuestion(nextQuestion); String date = simpleDateFormat1.format(new Date()); log.info("如果下一题为空.则新的数据返回,并加上感谢语: {},uuid为:{}", date, phoneCallBackVO.getUuid()); phoneUtils.ttsPlayback(nextQuestion.getScriptContent(), phoneCallBackVO.getUuid()); return; } else { //就可以挂断电话了 String date = simpleDateFormat1.format(new Date()); log.info("就可以挂断电话了------: {},uuid为:{}", date, phoneCallBackVO.getUuid()); phoneUtils.ttsPlayback(ivrTaskTemplateVO.getRevisitAfter(), phoneCallBackVO.getUuid()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } phoneUtils.hangup("", "", ivrTaskTemplateVO.getRevisitAfter(), "", "", "", "", phoneCallBackVO.getUuid()); return; } } else if (mateNum < ivrTaskTemplateVO.getMateNum().intValue()) { //没有问到规定次数 mateNum = mateNum + 1; redisCache.setCacheObject(phoneCallBackVO.getUuid() + "mateNum", mateNum, 120, TimeUnit.MINUTES); } } //选项匹配完成后,需要再去通过库再进行匹配一次 String extemplateID = ivrTaskTemplateVO.getSubmoduleID(); String[] split = extemplateID.split(","); List list = Arrays.asList(split); List list1 = new ArrayList<>(); if (StringUtils.isNotEmpty(extemplateID)) { for (String str : list) { list1.add(Long.valueOf(str)); } List ivrLibaExtemplatescripts = ivrLibaExtemplatescriptMapper.queryIvrLibaExtemplatescriptList(list1); for (IvrLibaExtemplatescript ivrLibaExtemplatescript : ivrLibaExtemplatescripts) { if (StringUtils.isNotEmpty(ivrLibaExtemplatescript.getSelfRegex())) { Pattern pattern = Pattern.compile(ivrLibaExtemplatescript.getSelfRegex()); pattern.matcher(returnQues.getContent()); } if (StringUtils.isNotEmpty(ivrLibaExtemplatescript.getSelfRegex2())) { Pattern pattern2 = Pattern.compile(ivrLibaExtemplatescript.getSelfRegex2()); pattern2.matcher(returnQues.getContent()); } log.info("++++++++++++++++++++++++++通用库是否为空:selfRegex : {} , selfRegex2 : {}", ivrLibaExtemplatescript.getSelfRegex(), ivrLibaExtemplatescript.getSelfRegex2()); break; } String date = simpleDateFormat1.format(new Date()); log.info("最后的信息回复-: {},uuid为:{}", date, phoneCallBackVO.getUuid()); phoneUtils.ttsPlayback(nowQuestion.getScriptContent(), phoneCallBackVO.getUuid()); } } } /** * 电话ASR通话回调(雨绮) * * @param phoneCallReqYQVO */ @Override public PhoneCallBackYQVO phoneCallBackYQ(PhoneCallReqYQVO phoneCallReqYQVO) { PhoneCallBackYQVO phoneCallBackYQVO = new PhoneCallBackYQVO(); //channel_create 通道创建的时候,可以执行一些其它操作,譬如发个短信之类的; 我们的业务可以不用管 PlayEventCallback 这个是播放语音的,暂时用不到 End_time()= -1或null表示当前的asrtext不是一句完整的话 if (phoneCallReqYQVO.getOperate().equals("channel_create") || phoneCallReqYQVO.getOperate().equals("PlayEventCallback") || phoneCallReqYQVO.getEnd_time() == null || phoneCallReqYQVO.getEnd_time() == -1) { return phoneCallBackYQVO; } //通过子任务ID获取到模板信息 ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(Long.valueOf(phoneCallReqYQVO.getTaskid())); IvrTaskTemplate ivrTaskTemplate = ivrTaskTemplateService.selectIvrTaskTemplateByID(serviceSubtask.getTemplateid()); //获取模板问题 IvrTaskTemplateScript ivrTaskTemplateScript = new IvrTaskTemplateScript(); ivrTaskTemplateScript.setTemplateID(serviceSubtask.getTemplateid()); List ivrTaskTemplateScripts = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptList(ivrTaskTemplateScript); //获取问题ID 和 序号 String scriptId = redisCache.getCacheObject(phoneCallReqYQVO.getTaskid().trim() + "-" + phoneCallReqYQVO.getPhone().trim()); log.error("scriptId是多少:{}", scriptId); //当前题的信息 IvrTaskTemplateScriptVO ivrTaskTemplateScriptVO = iIvrTaskTemplateScriptService.getTaskTempScriptInfoByid(Long.valueOf(scriptId)); //判断UUID是否存在 Boolean aBoolean = redisCache.hasKey(phoneCallReqYQVO.getUuid()); if (!aBoolean) { //给静默设置一个默认次数在redis中 redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "noVoice", 0, 120, TimeUnit.MINUTES); } if ("SilentCallback".equals(phoneCallReqYQVO.getOperate())) { //如果是静默回调 Integer num = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "noVoice"); //判断静默回调次数是否小与模板规定的次数 if (num < ivrTaskTemplate.getNoVoiceNum()) { //小与的话,就继续问患者 phoneCallBackYQVO.setType("text"); phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue()); String scriptContent = ivrTaskTemplateScriptVO.getScriptContent(); phoneCallBackYQVO.setValue(getObject(serviceSubtask, scriptContent)); //将静默次数加1 Integer noVoiceNum = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "noVoice"); redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "noVoice", noVoiceNum + 1, 120, TimeUnit.MINUTES); } else { log.error("静默次数达到,挂掉电话:{}", num); //大与等于的话,直接挂断 phoneCallBackYQVO.setType("hangup"); phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue()); phoneCallBackYQVO.setValue(ivrTaskTemplate.getRevisitAfter()); //将结果写到detail中 ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq(); List serviceSubtaskDetailList = new ArrayList<>(); serviceSubtaskDetailList.add(getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate)); serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList); saveQuestionAnswerPhone(serviceSubTaskDetailReq); redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone()); } return phoneCallBackYQVO; } else if ("AsrCallback".equals(phoneCallReqYQVO.getOperate())) { //如果是文本回调 //根据问题ID获取该问题的类型 if (ivrTaskTemplateScriptVO.getScriptType().equals("1")) { //是选择题 for (int j = 0; j < ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().size(); j++) { log.error("phoneCallReqYQVO.getAsrtext()的值为:{}", phoneCallReqYQVO.getAsrtext()); if (StringUtils.isEmpty(phoneCallReqYQVO.getAsrtext())) { continue; } //包含 Matcher matcher = null; if (StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getTargetregex())) { Pattern pattern = Pattern.compile(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getTargetregex()); matcher = pattern.matcher(phoneCallReqYQVO.getAsrtext()); } //不包含 Matcher matcher2 = null; if (StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getTargetregex2())) { Pattern pattern2 = Pattern.compile(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getTargetregex2()); matcher2 = pattern2.matcher(phoneCallReqYQVO.getAsrtext()); } if (StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getTargetregex2()) && matcher2.matches() && StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getTargetregex()) && matcher.matches() || StringUtils.isEmpty(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getTargetregex()) && StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getTargetregex2()) && matcher2.matches() || StringUtils.isEmpty(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getTargetregex2()) && StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getTargetregex()) && matcher.matches()) { //说明匹配正确了 //这里应该先判断类型,去再修改,设置IsUserOperation是单选题的改法 ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).setIsUserOperation(1); serviceTaskScriptTargetoptionMapper.updateIvrTaskTemplateTargetoption(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j)); //将患者的回签写进service_subtask_detail中 ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq(); List serviceSubtaskDetailList = new ArrayList<>(); ivrTaskTemplateScriptVO.setQuestionResult(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getOptiondesc()); serviceSubtaskDetailList.add(getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate)); serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList); saveQuestionAnswerPhone(serviceSubTaskDetailReq); // // //获取下一题 if (ivrTaskTemplateScriptVO.getBranchFlag().equals("1") || ivrTaskTemplateScriptVO.getBranchFlag().equals("0") && ivrTaskTemplateScriptVO.getNextScriptno() != 0) { Long nextQuestion = null; if (ivrTaskTemplateScriptVO.getBranchFlag().equals("1")) { nextQuestion = ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getNextQuestion(); } else { nextQuestion = ivrTaskTemplateScriptVO.getNextScriptno(); } for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) { if (script.getSort() == nextQuestion.intValue()) { phoneCallBackYQVO.setType("text"); phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue()); String scriptContent = script.getScriptContent(); phoneCallBackYQVO.setValue(getObject(serviceSubtask, scriptContent)); //将该患者的Redis中的题目ID,进行修改 redisCache.setCacheObject(phoneCallReqYQVO.getTaskid().trim() + "-" + phoneCallReqYQVO.getPhone().trim(), script.getId().toString(), 120, TimeUnit.MINUTES); //删除无响应 redisCache.deleteObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid()); } } } else if (ivrTaskTemplateScriptVO.getBranchFlag().equals("0")) { if (ivrTaskTemplateScriptVO.getNextScriptno() == 0 || ivrTaskTemplateScriptVO.getNextScriptno() == null) { phoneCallBackYQVO.setType("hangup"); phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue()); //设置结束语 phoneCallBackYQVO.setValue(ivrTaskTemplate.getRevisitAfter()); Long id = serviceSubtask.getId(); Map map = delRedisValue(null, id.toString()); redisCache.setCacheObject(map.get("cacheName"), map.get("val")); redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone()); redisCache.deleteObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid()); // return phoneCallBackYQVO; } } } else { continue; } } //都没有匹配到 if (StringUtils.isEmpty(phoneCallBackYQVO.getValue())) { Integer count = redisCache.getCacheObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid()); if (count != null && count >= ivrTaskTemplate.getMateNum()) { //如果count已经大于或等于没有匹配次数 if (ivrTaskTemplateScriptVO.getBranchFlag().equals("0") && ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getBranchFlag().equals("0") && ivrTaskTemplateScriptVO.getNextScriptno() == 0 || ivrTaskTemplateScriptVO.getBranchFlag().equals("1") && ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getBranchFlag().equals("1") && ivrTaskTemplateScriptVO.getNextScriptno() == 0) { //如果是最后一道题,或者没有下一题了,就直接挂机 phoneCallBackYQVO.setType("hangup"); phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue()); if (StringUtils.isNotEmpty(phoneCallBackYQVO.getValue())) phoneCallBackYQVO.setValue(phoneCallBackYQVO.getValue() + ivrTaskTemplate.getRevisitAfter()); else phoneCallBackYQVO.setValue(ivrTaskTemplate.getRevisitAfter()); //去redis中,把该子任务ID删除 Long id = serviceSubtask.getId(); Map map = delRedisValue(null, id.toString()); redisCache.setCacheObject(map.get("cacheName"), map.get("val")); redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone()); // return phoneCallBackYQVO; } else { //根据ivrTaskTemplateScriptVO.getNextScriptno()获取下一题进行提问 for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) { if (script.getSort() == ivrTaskTemplateScriptVO.getNextScriptno().intValue()) { phoneCallBackYQVO.setType("text"); phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue()); String scriptContent = script.getScriptContent(); phoneCallBackYQVO.setValue(getObject(serviceSubtask, scriptContent)); //将该患者的Redis中的题目ID,进行修改 redisCache.setCacheObject(phoneCallReqYQVO.getTaskid().trim() + "-" + phoneCallReqYQVO.getPhone().trim(), script.getId().toString(), 120, TimeUnit.MINUTES); } } } } else { if (count == null) redisCache.setCacheObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid(), 1, 120, TimeUnit.MINUTES); else redisCache.setCacheObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid(), count + 1, 120, TimeUnit.MINUTES); phoneCallBackYQVO.setType("text"); phoneCallBackYQVO.setValue(ivrTaskTemplateScriptVO.getNoMatchText() + getObject(serviceSubtask, ivrTaskTemplateScriptVO.getScriptContent())); } } } else { //不是选择题,直接记录答案,将结果写到detail中 ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq(); List serviceSubtaskDetailList = new ArrayList<>(); serviceSubtaskDetailList.add(getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate)); serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList); saveQuestionAnswerPhone(serviceSubTaskDetailReq); //如果选项分支为1的话,则需要根据问题上的nextScriptno进行跳转 //问答题没有跳转 if (ivrTaskTemplateScriptVO.getNextScriptno() != null || ivrTaskTemplateScriptVO.getNextScriptno() != 0) { for (IvrTaskTemplateScript ivrTaskTemplateScript1 : ivrTaskTemplateScripts) { if (ivrTaskTemplateScriptVO.getNextScriptno().intValue() == ivrTaskTemplateScript1.getSort()) { phoneCallBackYQVO.setType("text"); phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue()); String scriptContent = ivrTaskTemplateScript1.getScriptContent(); phoneCallBackYQVO.setValue(getObject(serviceSubtask, scriptContent)); redisCache.deleteObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid()); } } } else if (ivrTaskTemplateScriptVO.getNextScriptno() == null) { //没有下一题了,就结束了 phoneCallBackYQVO.setType("hangup"); phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue()); phoneCallBackYQVO.setValue(ivrTaskTemplate.getRevisitAfter()); //去redis中,把该子任务ID删除 Long id = serviceSubtask.getId(); redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone()); log.error("电话挂断4"); } //选项匹配完成后,需要再去通过库再进行匹配一次 String extemplateID = ivrTaskTemplate.getSubmoduleID(); String[] split = extemplateID.split(","); List list = Arrays.asList(split); List list1 = new ArrayList<>(); if (StringUtils.isNotEmpty(extemplateID)) { for (String str : list) { list1.add(Long.valueOf(str)); } List ivrLibaExtemplatescripts = ivrLibaExtemplatescriptMapper.queryIvrLibaExtemplatescriptList(list1); for (IvrLibaExtemplatescript ivrLibaExtemplatescript : ivrLibaExtemplatescripts) { Matcher matcher = null; if (StringUtils.isNotEmpty(ivrLibaExtemplatescript.getSelfRegex())) { Pattern pattern = Pattern.compile(ivrLibaExtemplatescript.getSelfRegex()); matcher = pattern.matcher(phoneCallReqYQVO.getAsrtext()); } Matcher matcher2 = null; if (StringUtils.isNotEmpty(ivrLibaExtemplatescript.getSelfRegex2())) { Pattern pattern2 = Pattern.compile(ivrLibaExtemplatescript.getSelfRegex2()); matcher2 = pattern2.matcher(phoneCallReqYQVO.getAsrtext()); } if (StringUtils.isNotEmpty(ivrLibaExtemplatescript.getSelfRegex()) && matcher.matches() && StringUtils.isNotEmpty(ivrLibaExtemplatescript.getSelfRegex2()) && matcher2.matches() || StringUtils.isEmpty(ivrLibaExtemplatescript.getSelfRegex()) && StringUtils.isNotEmpty(ivrLibaExtemplatescript.getSelfRegex2()) && matcher2.matches() || StringUtils.isEmpty(ivrLibaExtemplatescript.getSelfRegex2()) && StringUtils.isNotEmpty(ivrLibaExtemplatescript.getSelfRegex()) && matcher.matches()) { //将通用库匹配的,放到返回值中 phoneCallBackYQVO.setValue(phoneCallBackYQVO.getValue() + ivrLibaExtemplatescript.getSwitchText()); } break; } } } } return phoneCallBackYQVO; } private String getObject(ServiceSubtask serviceSubtask, String scriptContent) { Map> param = getParam(serviceSubtask.getTaskid()); for (Map map : param.values()) { for (String key : map.keySet()) { scriptContent = scriptContent.replace(key, StringUtils.isNotEmpty(map.get(key)) ? map.get(key) : ""); } } scriptContent.replace("${name}", StringUtils.isNotEmpty(serviceSubtask.getName()) ? serviceSubtask.getName() : ""); scriptContent.replace("${dzz}", StringUtils.isNotEmpty(serviceSubtask.getPlaceOfResidence()) ? serviceSubtask.getPlaceOfResidence() : ""); scriptContent.replace("${phone}", StringUtils.isNotEmpty(serviceSubtask.getTelcode()) ? serviceSubtask.getTelcode() : ""); return scriptContent; } /** * 获取任务里的通配符 * * @param taskId * @return */ private Map> getParam(Long taskId) { ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(taskId); ObjectMapper objectMapper = new ObjectMapper(); Map> serviceTaskMap = null; try { serviceTaskMap = objectMapper.readValue(serviceTask.getTextParam(), Map.class); } catch (JsonProcessingException e) { e.printStackTrace(); } return serviceTaskMap; } private Map delRedisValue(String cache, String id) { id = "," + id + ","; String val = ""; String cacheName = ""; Map map = new HashMap<>(); if (StringUtils.isNotEmpty(cache)) { val = redisCache.getCacheObject(cache); if (!StringUtils.isEmpty(val)) { if (val.contains(id)) { val = val.replaceAll(id, ""); } } map.put("val", val); map.put("cacheName", cache); return map; } for (int i = 0; i < 6; i++) { val = redisCache.getCacheObject("cache-0" + i); if (!StringUtils.isEmpty(val)) { if (val.contains(id)) { val = val.replaceAll(id, ""); map.put("val", val); map.put("cacheName", "cache-0" + i); } } } return map; } /** * 雨绮任务拉取 * * @return */ @Override public List taskPull() { //pullTaskVOList用于数据返回 List pullTaskVOList = new ArrayList<>(); String value0 = redisCache.getCacheObject("cache-0"); // cache-0为立即发起的,其它的先推迟 if (!StringUtils.isEmpty(value0)) { pullTaskVOList = getPullTaskList(value0, "cache-0"); //将cache-0的数据,转移不对劲cache-00中 String cache00 = redisCache.getCacheObject("cache-0"); if (!StringUtils.isEmpty(cache00)) redisCache.setCacheObject("cache-0", cache00 + "," + value0); else redisCache.setCacheObject("cache-0", value0); redisCache.deleteObject("cache-0"); } else { for (int i = 1; i < 6; i++) { //取出从cache-1 到 cache-5的第一个子任务信息 String value = redisCache.getCacheObject("cache-" + i); if (StringUtils.isEmpty(value)) continue; List pullTaskVOList2 = getPullTaskList(value, "cache-" + i); if (CollectionUtils.isNotEmpty(pullTaskVOList2) && pullTaskVOList2.size() > 0) { pullTaskVOList.addAll(pullTaskVOList2); } redisCache.deleteObject("cache-" + i); } } return pullTaskVOList; } private List getPullTaskList(String subIds, String cacheName) { //pullTaskVOList用于数据返回 List pullTaskVOList = new ArrayList<>(); //newValue0用于保存没有处理的子 任务 String newValue0 = ""; //根据,获取子任务的ID String[] split = subIds.split(",,"); for (int i = 0; i < split.length; i++) { if (cacheName.equals("cache-0") && i < 5 || !cacheName.equals("cache-0") && i < 1) { PullTaskVO pullTaskVO = new PullTaskVO(); String subId = split[i].trim().replace(",", ""); ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(Long.valueOf(subId)); IvrTaskTemplate ivrTaskTemplate = ivrTaskTemplateService.selectIvrTaskTemplateByID(serviceSubtask.getTemplateid()); //通过任务模板中的"第一次问题编号"获取第一个问题; IvrTaskTemplateScript ivrTaskTemplateScript = null; IvrTaskTemplateScript ivrTaskTemplateScript1 = new IvrTaskTemplateScript(); ivrTaskTemplateScript1.setTemplateID(ivrTaskTemplate.getId()); List ivrTaskTemplateScripts = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptList(ivrTaskTemplateScript1); aa: for (IvrTaskTemplateScript ivrTaskTemplateScript2 : ivrTaskTemplateScripts) { if (ivrTaskTemplate.getFirstQuestionNum() == Long.valueOf(ivrTaskTemplateScript2.getSort())) { ivrTaskTemplateScript = ivrTaskTemplateScript2; break aa; } } //如果ivrTaskTemplateScript为空,也就没有往下执行的必要了 if (ObjectUtils.isEmpty(ivrTaskTemplateScript)) return null; //获取通配符匹配过后的问题 String scrContent = getObject(serviceSubtask, ivrTaskTemplateScript.getScriptContent()); String kcb = ivrTaskTemplate.getRevisitBefore() + "," + scrContent; //封装返回数据 //taskId = 子任务ID + 问题ID +问题序号 pullTaskVO.setTaskid(subId); pullTaskVO.setAppkey("ZurNHpaQLq6P55YS"); pullTaskVO.setSections(LocalTime.now().format(DateTimeFormatter.ofPattern("hh:mm")) + "-" + LocalTime.now().plusMinutes(1).format(DateTimeFormatter.ofPattern("hh:mm"))); pullTaskVO.setPhones(serviceSubtask.getPhone()); pullTaskVO.setPrologue(kcb); pullTaskVO.setDisplayNo("85129866"); pullTaskVOList.add(pullTaskVO); redisCache.setCacheObject(subId.trim() + "-" + serviceSubtask.getPhone().trim(), ivrTaskTemplateScript.getId().toString()); } else { if (StringUtils.isEmpty(newValue0)) { newValue0 = "," + split[i].trim() + ","; } else { newValue0 = newValue0 + "," + split[i].trim() + ","; } redisCache.setCacheObject(cacheName, newValue0); } } return pullTaskVOList; } @Override public Integer saveQuestionAnswerPhone(ServiceSubTaskDetailReq serviceSubTaskDetailReq) { int i = 0; if (StringUtils.isNotEmpty(serviceSubTaskDetailReq.getParam1())) { RSAPublicKeyExample rsaPublicKeyExample = new RSAPublicKeyExample(); Long tid = Long.valueOf(rsaPublicKeyExample.decryptedData(serviceSubTaskDetailReq.getParam1(), pri_key)); Long pid = Long.valueOf(rsaPublicKeyExample.decryptedData(serviceSubTaskDetailReq.getParam2(), pri_key)); ServiceSubtask ivrTaskSingle = new ServiceSubtask(); ivrTaskSingle.setTaskid(tid); ivrTaskSingle.setPatid(pid); List selectServiceSubtaskList = serviceSubtaskMapper.selectServiceSubtaskList(ivrTaskSingle); if (CollectionUtils.isEmpty(selectServiceSubtaskList) || selectServiceSubtaskList.size() == 0) { log.error("报错了,selectServiceSubtaskList数据为空了:{}", ivrTaskSingle); return 0; } //随访 for (ServiceSubtaskDetail serviceSubtaskDetail : serviceSubTaskDetailReq.getServiceSubtaskDetailList()) { serviceSubtaskDetail.setSubId(selectServiceSubtaskList.get(0).getId()); serviceSubtaskDetail.setId(UUID.randomUUID().toString()); serviceSubtaskDetail.setCreateTime(new Date()); i = serviceSubtaskDetailMapper.insertServiceSubtaskDetail(serviceSubtaskDetail); } } else { for (ServiceSubtaskDetail serviceSubtaskDetail : serviceSubTaskDetailReq.getServiceSubtaskDetailList()) { serviceSubtaskDetail.setCreateTime(new Date()); i = serviceSubtaskDetailMapper.insertServiceSubtaskDetail(serviceSubtaskDetail); } } return i; } private IvrTaskTemplateScriptVO getNextQuestion(List IvrTaskTemplateScriptVOList, IvrTaskTemplateScriptVO IvrTaskTemplateScriptVO) { for (int j = 0; j < IvrTaskTemplateScriptVOList.size(); j++) { if (IvrTaskTemplateScriptVOList.get(j).getTargetid() == IvrTaskTemplateScriptVO.getTargetid() + 1) { // 对该条templateScriptVO进行处理 return IvrTaskTemplateScriptVOList.get(j); } } return null; } private ServiceSubtaskDetail getServiceSubtaskDetail(PhoneCallReqYQVO phoneCallReqYQVO, IvrTaskTemplateScriptVO ivrTaskTemplateScriptVO, ServiceSubtask serviceSubtask, IvrTaskTemplate ivrTaskTemplate) { ServiceSubtaskDetail serviceSubtaskDetail = new ServiceSubtaskDetail(); serviceSubtaskDetail.setSubId(Long.valueOf(phoneCallReqYQVO.getTaskid())); serviceSubtaskDetail.setUuid(phoneCallReqYQVO.getUuid()); serviceSubtaskDetail.setPhone(phoneCallReqYQVO.getPhone()); serviceSubtaskDetail.setOperate(serviceSubtask.getCreateBy()); serviceSubtaskDetail.setDisplayno(phoneCallReqYQVO.getPhone()); serviceSubtaskDetail.setAssigntime(System.currentTimeMillis()); serviceSubtaskDetail.setStarttime(System.currentTimeMillis()); serviceSubtaskDetail.setAnswertime(System.currentTimeMillis()); serviceSubtaskDetail.setAsrtext("无应答"); if (StringUtils.isNotEmpty(phoneCallReqYQVO.getAsrtext())) serviceSubtaskDetail.setAsrtext(phoneCallReqYQVO.getAsrtext()); serviceSubtaskDetail.setBeginTime(System.currentTimeMillis()); serviceSubtaskDetail.setEndTime(System.currentTimeMillis()); serviceSubtaskDetail.setSentEnd(1L); serviceSubtaskDetail.setTemplateid(ivrTaskTemplate.getId().toString()); serviceSubtaskDetail.setTemplatequestionnum(ivrTaskTemplateScriptVO.getId()); serviceSubtaskDetail.setQuestiontext(ivrTaskTemplateScriptVO.getScriptContent()); serviceSubtaskDetail.setCategoryname(ivrTaskTemplateScriptVO.getScriptType()); serviceSubtaskDetail.setTargetoptions(ivrTaskTemplateScriptVO.getTargetOptions()); int i = 1; for (IvrTaskTemplateTargetoption ivrTaskTemplateTargetoption : ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList()) { if (ivrTaskTemplateTargetoption.getIsUserOperation() == 1) { serviceSubtaskDetail.setMatchedtext(ivrTaskTemplateTargetoption.getTargetvalue()); } serviceSubtaskDetail.setTargetvalue(StringUtils.isEmpty(serviceSubtaskDetail.getTargetvalue()) ? i + ivrTaskTemplateTargetoption.getTargetvalue() : serviceSubtaskDetail.getTargetvalue() + " " + (i + 1) + ivrTaskTemplateTargetoption.getTargetvalue()); } serviceSubtaskDetail.setAddtime(new Date()); serviceSubtaskDetail.setIsupload(0L); serviceSubtaskDetail.setUploadTime(new Date()); serviceSubtaskDetail.setDelFlag("0"); serviceSubtaskDetail.setValueType(ivrTaskTemplateScriptVO.getScriptType()); return serviceSubtaskDetail; } }