| | |
| | | import com.ruoyi.phone.domain.QuestionMessage; |
| | | import com.ruoyi.phone.service.IPhoneService; |
| | | import com.ruoyi.task.api.TaskSubtaskApi; |
| | | import com.ruoyi.task.api.spi.TaskFtpPort; |
| | | import com.ruoyi.task.api.spi.TaskPreachformPort; |
| | | import com.ruoyi.task.api.spi.TaskSysConfigPort; |
| | | import com.ruoyi.common.utils.FtpService; |
| | | import com.ruoyi.task.mapper.ServiceSubtaskPreachformMapper; |
| | | import com.ruoyi.task.domain.IvrTaskVisitResult; |
| | | import com.ruoyi.task.domain.ServiceSubTaskDetailReq; |
| | | import com.ruoyi.task.domain.ServiceSubtask; |
| | |
| | | import java.util.regex.Matcher; |
| | | import java.util.regex.Pattern; |
| | | import java.util.stream.Collectors; |
| | | import com.ruoyi.system.service.ISysConfigService; |
| | | |
| | | /** |
| | | * 电话业务Service实现(原 {@code ruoyi-task} 里 {@code ServiceSubtaskServiceImpl} 的电话部分)。 |
| | |
| | | * 该表归 ruoyi-inhospvisit,由 smartor 侧适配实现本端口。 |
| | | */ |
| | | @Autowired |
| | | private TaskPreachformPort taskPreachformPort; |
| | | private ServiceSubtaskPreachformMapper taskPreachformPort; |
| | | |
| | | /** |
| | | * 录音文件下载(雨绮通话记录回调里把当天录音从 FTP 拉回本地) |
| | | * <p>2026-11:{@code FtpService} 已从 smartor 下沉到 {@code ruoyi-common}, |
| | | * 原来的过渡端口 {@code TaskFtpPort} 随之删除,这里直接注入通用工具。 |
| | | */ |
| | | @Autowired |
| | | private TaskFtpPort taskFtpPort; |
| | | private FtpService ftpService; |
| | | |
| | | /** |
| | | * 参数配置读取({@code sys.voice.match.ai.url}:AI 选项匹配服务地址,按机构配置) |
| | | */ |
| | | @Autowired |
| | | private TaskSysConfigPort taskSysConfigPort; |
| | | private ISysConfigService sysConfigService; |
| | | |
| | | /** |
| | | * AI 选项匹配契约(ruoyi-common,实现在 ruoyi-task,即原 {@code matchOptionIndex}) |
| | |
| | | ServiceSubtask ss = new ServiceSubtask(); |
| | | ss.setId(serviceSubtask.getId()); |
| | | ss.setSendstate(6L); |
| | | //电话已答完(完成):状态与完成时间一起写,避免"已完成却无完成时间" |
| | | ss.setFinishtime(new Date()); |
| | | taskSubtaskApi.updateSubtask(ss); |
| | | String date = simpleDateFormat1.format(new Date()); |
| | | log.error("没有下一题了,就结束了: {},uuid为:{}", date, phoneCallBackVO.getUuid()); |
| | |
| | | ServiceSubtask ss = new ServiceSubtask(); |
| | | ss.setId(serviceSubtask.getId()); |
| | | ss.setSendstate(6L); |
| | | //电话已答完(完成):状态与完成时间一起写,避免"已完成却无完成时间" |
| | | ss.setFinishtime(new Date()); |
| | | taskSubtaskApi.updateSubtask(ss); |
| | | |
| | | String date = simpleDateFormat1.format(new Date()); |
| | |
| | | //去redis中,把该子任务ID删除 |
| | | Long id = serviceSubtask.getId(); |
| | | //先更新一下分数 |
| | | //⚠️ 静默挂机 = 电话没接通 / 患者一直没应答,属于"未通",不是"完成": |
| | | // ① 不写 finishtime —— 这条单子接下来要交给补偿任务(setFailPreachForm 置 5 或 3, |
| | | // 再由 compensateTask 捞起来继续发下一种方式 / 判超时);一旦写了完成时间, |
| | | // 后面被置成"超时(7)"时就会留下"超时却有完成时间"的脏数据, |
| | | // 及时率、完成率(ServiceSubtaskMapper#selectTimelyRateBatch 认 finishtime 非空)也会被虚高; |
| | | // ② 不整行回写 serviceSubtask —— 那会把上面刚写的 sendstate=5(电话静默)覆盖回查库时的旧值, |
| | | // 所以这里只回写 id + 分数 + 更新时间,状态交给下面的 setFailPreachForm 决定 |
| | | // (最后一种方式 → 5 发送失败;还有下一种 → 3 已发送,两者都会被补偿任务捞起来)。 |
| | | double score = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE"); |
| | | serviceSubtask.setScore(String.valueOf(score)); |
| | | serviceSubtask.setFinishtime(new Date()); |
| | | serviceSubtask.setUpdateTime(new Date()); |
| | | log.error("先更新一下分数,电话的serviceSubtask的值为:{}", serviceSubtask); |
| | | taskSubtaskApi.updateSubtask(serviceSubtask); |
| | | ServiceSubtask scoreUpdate = new ServiceSubtask(); |
| | | scoreUpdate.setId(id); |
| | | scoreUpdate.setScore(String.valueOf(score)); |
| | | scoreUpdate.setUpdateTime(new Date()); |
| | | log.error("先更新一下分数,电话的scoreUpdate的值为:{}", scoreUpdate); |
| | | taskSubtaskApi.updateSubtask(scoreUpdate); |
| | | //记录状态 |
| | | setFailPreachForm(serviceSubtask, "3", "静默次数达到" + num + "次", "4"); |
| | | |
| | |
| | | ss.setId(serviceSubtask.getId()); |
| | | ss.setSendstate(6L); |
| | | ss.setRemark("电话拨打已完成"); |
| | | //电话已答完(完成):状态与完成时间一起写,避免"已完成却无完成时间" |
| | | ss.setFinishtime(new Date()); |
| | | taskSubtaskApi.updateSubtask(ss); |
| | | //记录状态 |
| | | setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9"); |
| | |
| | | //用来标记,是否有匹配上的 |
| | | Integer flag = 0; |
| | | //1-AI识别 |
| | | String url = taskSysConfigPort.selectConfigValue("sys.voice.match.ai.url", phoneCallReqYQVO.getOrgid()); |
| | | String url = sysConfigService.selectConfigByKey("sys.voice.match.ai.url", phoneCallReqYQVO.getOrgid()); |
| | | |
| | | if (ObjectUtils.isNotEmpty(url)) { |
| | | PhoneCallBackYQVO aiMatchResult = AiMatch(url, phoneCallReqYQVO, phoneCallBackYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate, ivrTaskTemplateScripts, scriptId, flag, phoneCallReqYQVO.getUuid()); |
| | |
| | | ss.setId(serviceSubtask.getId()); |
| | | ss.setSendstate(6L); |
| | | ss.setRemark("电话拨打已完成"); |
| | | //电话已答完(完成):状态与完成时间一起写,避免"已完成却无完成时间" |
| | | ss.setFinishtime(new Date()); |
| | | taskSubtaskApi.updateSubtask(ss); |
| | | //记录状态 |
| | | setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9"); |
| | |
| | | taskSubtaskApi.updateSubtask(serviceSubtask); |
| | | setFailPreachForm(serviceSubtask, StringUtils.isNotEmpty(serviceSubtask.getCurrentPreachform()) ? serviceSubtask.getCurrentPreachform() : "3", "电话拨打已完成", "9"); |
| | | //下载文件 |
| | | taskFtpPort.downloadFolder("/" + LocalDate.now().toString(), profile + "/upload/vadio/voice/" + LocalDate.now().toString()); |
| | | ftpService.downloadFolder("/" + LocalDate.now().toString(), profile + "/upload/vadio/voice/" + LocalDate.now().toString()); |
| | | redisCache.deleteObject(phoneCallRecordVO.getTaskid() + "recordAccept-hungup"); |
| | | } else { |
| | | //患者拒绝随访 |
| | |
| | | serviceSubtask.setCurrentPreachform(preachform); |
| | | serviceSubtask.setSendstate(5L); |
| | | serviceSubtask.setRemark(remark + ";所有执行方式均未获取结果,任务失败"); |
| | | taskSubtaskApi.updateSubtask(serviceSubtask); |
| | | //只回写本次要改的列(不再整行回写,避免把库里的旧 finishtime 等一起带回来) |
| | | updateSubtaskState(serviceSubtask, 5L, preachform, serviceSubtask.getRemark(), null); |
| | | return true; |
| | | } else { |
| | | //加入目前补偿里的 remark |
| | | serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + "," + remark : remark); |
| | | taskSubtaskApi.updateSubtask(serviceSubtask); |
| | | updateSubtaskState(serviceSubtask, null, null, serviceSubtask.getRemark(), null); |
| | | } |
| | | } |
| | | |
| | |
| | | //如果等于9,则说明电话正常结束了 |
| | | if (failSendstate.equals("9")) serviceSubtask.setSendstate(6L); |
| | | serviceSubtask.setVisitTime(getNextVisitTime(serviceSubtask.getId(), serviceSubtask.getTaskid(), serviceSubtask.getVisitTime())); |
| | | taskSubtaskApi.updateSubtask(serviceSubtask); |
| | | updateSubtaskState(serviceSubtask, serviceSubtask.getSendstate(), preachform, null, serviceSubtask.getVisitTime()); |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * 只回写本次真正要改的列(替代"整行回写 serviceSubtask")。 |
| | | * <p> |
| | | * 为什么必须收窄:mapper 的 updateServiceSubtask 是<if test="xx != null">动态 SET, |
| | | * 整行回写会把库里已有的 finishtime / visit_time / score 等旧值原样再写一遍 —— 既可能覆盖 |
| | | * 并发写入,也是"状态 3/5/7 却带着完成时间"这类脏数据的搬运工。 |
| | | * <p> |
| | | * 完成时间口径:sendstate=6(完成)时补完成时间;3 已发送 / 5 发送失败 / 7 超时等非完成状态 |
| | | * 一律不写,Mapper 侧的"完成时间不变量"会顺手把旧值清掉。 |
| | | */ |
| | | private void updateSubtaskState(ServiceSubtask source, Long sendstate, String currentPreachform, |
| | | String remark, Date visitTime) { |
| | | if (source == null || source.getId() == null) { |
| | | return; |
| | | } |
| | | ServiceSubtask update = new ServiceSubtask(); |
| | | update.setId(source.getId()); |
| | | update.setSendstate(sendstate); |
| | | update.setCurrentPreachform(currentPreachform); |
| | | update.setRemark(remark); |
| | | update.setVisitTime(visitTime); |
| | | if (sendstate != null && sendstate == 6L) { |
| | | update.setFinishtime(source.getFinishtime() != null ? source.getFinishtime() : new Date()); |
| | | } |
| | | taskSubtaskApi.updateSubtask(update); |
| | | } |
| | | |
| | | private Date getNextVisitTime(Long subid, Long taskid, Date visitTime) { |
| | |
| | | public PhoneCallBackSHVO processResust(PhoneCallResultYQVO phoneCallResultYQVO) { |
| | | PhoneCallBackSHVO phoneCallBackSHVO = new PhoneCallBackSHVO(); |
| | | //通过scriptid,获取所有的结果 |
| | | String url = taskSysConfigPort.selectConfigValue("sys.voice.match.ai.url", phoneCallResultYQVO.getOrgid()); |
| | | String url = sysConfigService.selectConfigByKey("sys.voice.match.ai.url", phoneCallResultYQVO.getOrgid()); |
| | | ServiceSubtask serviceSubtask = taskSubtaskApi.getSubtask(phoneCallResultYQVO.getSubId()); |
| | | IvrTaskTemplate ivrTaskTemplate = ivrTaskTemplateService.selectIvrTaskTemplateByID(serviceSubtask.getTemplateid()); |
| | | IvrTaskTemplateScriptVO ivrTaskTemplateScriptVO = iIvrTaskTemplateScriptService.getTaskTempScriptInfoByid(Long.valueOf(phoneCallResultYQVO.getScriptId())); |
| | |
| | | ss.setId(serviceSubtask.getId()); |
| | | ss.setSendstate(6L); |
| | | ss.setRemark("电话拨打已完成"); |
| | | //电话已答完(完成):状态与完成时间一起写,避免"已完成却无完成时间" |
| | | ss.setFinishtime(new Date()); |
| | | taskSubtaskApi.updateSubtask(ss); |
| | | //记录状态 |
| | | setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9"); |